diff --git a/indra/llcharacter/llstatemachine.h b/indra/llcharacter/llstatemachine.h
index c19a926dbb8afd35db80055f100ae5a8e2cc88bf..5ae1668a11fa2138bb3d65d22aefb70aac81fba1 100644
--- a/indra/llcharacter/llstatemachine.h
+++ b/indra/llcharacter/llstatemachine.h
@@ -54,7 +54,7 @@ class LLFSMTransition : public LLUniqueID
 {
 public:
 	LLFSMTransition() : LLUniqueID(){};
-	virtual std::string getName(){ return "unnamed"; }
+	virtual std::string getName()const { return "unnamed"; }
 };
 
 class LLFSMState : public LLUniqueID
@@ -64,7 +64,7 @@ class LLFSMState : public LLUniqueID
 	virtual void onEntry(void *){};
 	virtual void onExit(void *){};
 	virtual void execute(void *){};
-	virtual std::string getName(){ return "unnamed"; }
+	virtual std::string getName() const { return "unnamed"; }
 };
 
 class LLStateDiagram
diff --git a/indra/llcommon/indra_constants.h b/indra/llcommon/indra_constants.h
index 89b276e3b3055d701d6e17daa6d2869f0c7cfe25..a0b129363929a571f9b578a8871f1f2689eab964 100644
--- a/indra/llcommon/indra_constants.h
+++ b/indra/llcommon/indra_constants.h
@@ -38,9 +38,10 @@
 // At 45 Hz collisions seem stable and objects seem
 // to settle down at a reasonable rate.
 // JC 3/18/2003
-const F32 HAVOK_TIMESTEP = 1.f / 45.f;
+const F32 PHYSICS_TIMESTEP = 1.f / 45.f;
 
 const F32 COLLISION_TOLERANCE = 0.1f;
+const F32 HALF_COLLISION_TOLERANCE = COLLISION_TOLERANCE * 0.5f;
 
 // Time constants
 const U32 HOURS_PER_LINDEN_DAY		= 4;	
@@ -53,6 +54,8 @@ const F32 REGION_WIDTH_METERS = 256.f;
 const S32 REGION_WIDTH_UNITS = 256;
 const U32 REGION_WIDTH_U32 = 256;
 
+const F32 REGION_HEIGHT_METERS = 4096.f;
+
 // Bits for simulator performance query flags
 enum LAND_STAT_FLAGS
 {
@@ -87,7 +90,7 @@ const 	F32 	MAX_AGENT_HEIGHT		= 2.65f - 2.0f * COLLISION_TOLERANCE;
 
 // For linked sets
 const S32 MAX_CHILDREN_PER_TASK = 255;
-const S32 MAX_CHILDREN_PER_PHYSICAL_TASK = 31;
+const S32 MAX_CHILDREN_PER_PHYSICAL_TASK = 32;
 
 const S32 MAX_JOINTS_PER_OBJECT = 1;	// limiting to 1 until Havok 2.x
 
diff --git a/indra/llcommon/llagentconstants.h b/indra/llcommon/llagentconstants.h
index 2989d6d7d4e318e8306d3ed5b33a24db5ab73a59..f630af6530f8a5b6dea3c663a48f4218f5c50ba5 100644
--- a/indra/llcommon/llagentconstants.h
+++ b/indra/llcommon/llagentconstants.h
@@ -67,43 +67,43 @@ const U32 CONTROL_ML_LBUTTON_DOWN_INDEX		= 30;
 const U32 CONTROL_ML_LBUTTON_UP_INDEX		= 31;
 const U32 TOTAL_CONTROLS					= 32;
 
-const U32 AGENT_CONTROL_AT_POS              = 0x1 << CONTROL_AT_POS_INDEX;
-const U32 AGENT_CONTROL_AT_NEG              = 0x1 << CONTROL_AT_NEG_INDEX;
-const U32 AGENT_CONTROL_LEFT_POS            = 0x1 << CONTROL_LEFT_POS_INDEX;
-const U32 AGENT_CONTROL_LEFT_NEG            = 0x1 << CONTROL_LEFT_NEG_INDEX;
-const U32 AGENT_CONTROL_UP_POS              = 0x1 << CONTROL_UP_POS_INDEX;
-const U32 AGENT_CONTROL_UP_NEG              = 0x1 << CONTROL_UP_NEG_INDEX;
-const U32 AGENT_CONTROL_PITCH_POS           = 0x1 << CONTROL_PITCH_POS_INDEX;
-const U32 AGENT_CONTROL_PITCH_NEG           = 0x1 << CONTROL_PITCH_NEG_INDEX;
-const U32 AGENT_CONTROL_YAW_POS             = 0x1 << CONTROL_YAW_POS_INDEX;
-const U32 AGENT_CONTROL_YAW_NEG             = 0x1 << CONTROL_YAW_NEG_INDEX;
-
-const U32 AGENT_CONTROL_FAST_AT             = 0x1 << CONTROL_FAST_AT_INDEX;
-const U32 AGENT_CONTROL_FAST_LEFT           = 0x1 << CONTROL_FAST_LEFT_INDEX;
-const U32 AGENT_CONTROL_FAST_UP             = 0x1 << CONTROL_FAST_UP_INDEX;
-
-const U32 AGENT_CONTROL_FLY					= 0x1 << CONTROL_FLY_INDEX;
-const U32 AGENT_CONTROL_STOP				= 0x1 << CONTROL_STOP_INDEX;
-const U32 AGENT_CONTROL_FINISH_ANIM			= 0x1 << CONTROL_FINISH_ANIM_INDEX;
-const U32 AGENT_CONTROL_STAND_UP			= 0x1 << CONTROL_STAND_UP_INDEX;
-const U32 AGENT_CONTROL_SIT_ON_GROUND		= 0x1 << CONTROL_SIT_ON_GROUND_INDEX;
-const U32 AGENT_CONTROL_MOUSELOOK			= 0x1 << CONTROL_MOUSELOOK_INDEX;
-
-const U32 AGENT_CONTROL_NUDGE_AT_POS        = 0x1 << CONTROL_NUDGE_AT_POS_INDEX;
-const U32 AGENT_CONTROL_NUDGE_AT_NEG        = 0x1 << CONTROL_NUDGE_AT_NEG_INDEX;
-const U32 AGENT_CONTROL_NUDGE_LEFT_POS      = 0x1 << CONTROL_NUDGE_LEFT_POS_INDEX;
-const U32 AGENT_CONTROL_NUDGE_LEFT_NEG      = 0x1 << CONTROL_NUDGE_LEFT_NEG_INDEX;
-const U32 AGENT_CONTROL_NUDGE_UP_POS        = 0x1 << CONTROL_NUDGE_UP_POS_INDEX;
-const U32 AGENT_CONTROL_NUDGE_UP_NEG        = 0x1 << CONTROL_NUDGE_UP_NEG_INDEX;
-const U32 AGENT_CONTROL_TURN_LEFT	        = 0x1 << CONTROL_TURN_LEFT_INDEX;
-const U32 AGENT_CONTROL_TURN_RIGHT	        = 0x1 << CONTROL_TURN_RIGHT_INDEX;
-
-const U32 AGENT_CONTROL_AWAY				= 0x1 << CONTROL_AWAY_INDEX;
-
-const U32 AGENT_CONTROL_LBUTTON_DOWN		= 0x1 << CONTROL_LBUTTON_DOWN_INDEX;
-const U32 AGENT_CONTROL_LBUTTON_UP			= 0x1 << CONTROL_LBUTTON_UP_INDEX;
-const U32 AGENT_CONTROL_ML_LBUTTON_DOWN		= 0x1 << CONTROL_ML_LBUTTON_DOWN_INDEX;
-const U32 AGENT_CONTROL_ML_LBUTTON_UP		= ((U32)0x1) << CONTROL_ML_LBUTTON_UP_INDEX;
+const U32 AGENT_CONTROL_AT_POS              = 0x1 << CONTROL_AT_POS_INDEX;			// 0x00000001
+const U32 AGENT_CONTROL_AT_NEG              = 0x1 << CONTROL_AT_NEG_INDEX;			// 0x00000002
+const U32 AGENT_CONTROL_LEFT_POS            = 0x1 << CONTROL_LEFT_POS_INDEX;		// 0x00000004
+const U32 AGENT_CONTROL_LEFT_NEG            = 0x1 << CONTROL_LEFT_NEG_INDEX;		// 0x00000008
+const U32 AGENT_CONTROL_UP_POS              = 0x1 << CONTROL_UP_POS_INDEX;			// 0x00000010
+const U32 AGENT_CONTROL_UP_NEG              = 0x1 << CONTROL_UP_NEG_INDEX;			// 0x00000020
+const U32 AGENT_CONTROL_PITCH_POS           = 0x1 << CONTROL_PITCH_POS_INDEX;		// 0x00000040
+const U32 AGENT_CONTROL_PITCH_NEG           = 0x1 << CONTROL_PITCH_NEG_INDEX;		// 0x00000080
+const U32 AGENT_CONTROL_YAW_POS             = 0x1 << CONTROL_YAW_POS_INDEX;			// 0x00000100
+const U32 AGENT_CONTROL_YAW_NEG             = 0x1 << CONTROL_YAW_NEG_INDEX;			// 0x00000200
+
+const U32 AGENT_CONTROL_FAST_AT             = 0x1 << CONTROL_FAST_AT_INDEX;			// 0x00000400
+const U32 AGENT_CONTROL_FAST_LEFT           = 0x1 << CONTROL_FAST_LEFT_INDEX;		// 0x00000800
+const U32 AGENT_CONTROL_FAST_UP             = 0x1 << CONTROL_FAST_UP_INDEX;			// 0x00001000
+
+const U32 AGENT_CONTROL_FLY					= 0x1 << CONTROL_FLY_INDEX;				// 0x00002000
+const U32 AGENT_CONTROL_STOP				= 0x1 << CONTROL_STOP_INDEX;			// 0x00004000
+const U32 AGENT_CONTROL_FINISH_ANIM			= 0x1 << CONTROL_FINISH_ANIM_INDEX;		// 0x00008000
+const U32 AGENT_CONTROL_STAND_UP			= 0x1 << CONTROL_STAND_UP_INDEX;		// 0x00010000
+const U32 AGENT_CONTROL_SIT_ON_GROUND		= 0x1 << CONTROL_SIT_ON_GROUND_INDEX;	// 0x00020000
+const U32 AGENT_CONTROL_MOUSELOOK			= 0x1 << CONTROL_MOUSELOOK_INDEX;		// 0x00040000
+
+const U32 AGENT_CONTROL_NUDGE_AT_POS        = 0x1 << CONTROL_NUDGE_AT_POS_INDEX;	// 0x00080000
+const U32 AGENT_CONTROL_NUDGE_AT_NEG        = 0x1 << CONTROL_NUDGE_AT_NEG_INDEX;	// 0x00100000
+const U32 AGENT_CONTROL_NUDGE_LEFT_POS      = 0x1 << CONTROL_NUDGE_LEFT_POS_INDEX;	// 0x00200000
+const U32 AGENT_CONTROL_NUDGE_LEFT_NEG      = 0x1 << CONTROL_NUDGE_LEFT_NEG_INDEX;	// 0x00400000
+const U32 AGENT_CONTROL_NUDGE_UP_POS        = 0x1 << CONTROL_NUDGE_UP_POS_INDEX;	// 0x00800000
+const U32 AGENT_CONTROL_NUDGE_UP_NEG        = 0x1 << CONTROL_NUDGE_UP_NEG_INDEX;	// 0x01000000
+const U32 AGENT_CONTROL_TURN_LEFT	        = 0x1 << CONTROL_TURN_LEFT_INDEX;		// 0x02000000
+const U32 AGENT_CONTROL_TURN_RIGHT	        = 0x1 << CONTROL_TURN_RIGHT_INDEX;		// 0x04000000
+
+const U32 AGENT_CONTROL_AWAY				= 0x1 << CONTROL_AWAY_INDEX;			// 0x08000000
+
+const U32 AGENT_CONTROL_LBUTTON_DOWN		= 0x1 << CONTROL_LBUTTON_DOWN_INDEX;	// 0x10000000
+const U32 AGENT_CONTROL_LBUTTON_UP			= 0x1 << CONTROL_LBUTTON_UP_INDEX;		// 0x20000000
+const U32 AGENT_CONTROL_ML_LBUTTON_DOWN		= 0x1 << CONTROL_ML_LBUTTON_DOWN_INDEX;	// 0x40000000
+const U32 AGENT_CONTROL_ML_LBUTTON_UP		= ((U32)0x1) << CONTROL_ML_LBUTTON_UP_INDEX;	// 0x80000000
 
 const U32 AGENT_CONTROL_AT 		= AGENT_CONTROL_AT_POS 
 								  | AGENT_CONTROL_AT_NEG 
diff --git a/indra/llcommon/llapr.cpp b/indra/llcommon/llapr.cpp
index 3512f2fb17bec5ca072f55901901a0bb5be4d8bd..5e6dfd975e584d4a039e9f500fadbb0171cc7c42 100644
--- a/indra/llcommon/llapr.cpp
+++ b/indra/llcommon/llapr.cpp
@@ -116,8 +116,10 @@ void LLScopedLock::unlock()
 bool ll_apr_warn_status(apr_status_t status)
 {
 	if(APR_SUCCESS == status) return false;
+#ifndef LL_WINDOWS
 	char buf[MAX_STRING];	/* Flawfinder: ignore */
 	llwarns << "APR: " << apr_strerror(status, buf, MAX_STRING) << llendl;
+#endif
 	return true;
 }
 
diff --git a/indra/llcommon/lldefs.h b/indra/llcommon/lldefs.h
index 96b2ab169b8b9fcd06efc25cd73465d9edbcfea9..34bde66678476e3b00de0ea2a8b8f78e79e32369 100644
--- a/indra/llcommon/lldefs.h
+++ b/indra/llcommon/lldefs.h
@@ -218,7 +218,15 @@ inline LLDATATYPE llmin(const LLDATATYPE& d1, const LLDATATYPE& d2, const LLDATA
 template <class LLDATATYPE> 
 inline LLDATATYPE llclamp(const LLDATATYPE& a, const LLDATATYPE& minval, const LLDATATYPE& maxval)
 {
-	return llmin(llmax(a, minval), maxval);
+	if ( a < minval )
+	{
+		return minval;
+	}
+	else if ( a > maxval )
+	{
+		return maxval;
+	}
+	return a;
 }
 
 template <class LLDATATYPE> 
@@ -234,3 +242,4 @@ inline LLDATATYPE llclampb(const LLDATATYPE& a)
 }
 
 #endif // LL_LLDEFS_H
+
diff --git a/indra/llcommon/llframetimer.cpp b/indra/llcommon/llframetimer.cpp
index 23d8775824aad0c5c6412429ffc81aa7ec4154a5..b74151bc2a443a39ba0cbd58ef73f64cdaa1ae04 100644
--- a/indra/llcommon/llframetimer.cpp
+++ b/indra/llcommon/llframetimer.cpp
@@ -140,3 +140,18 @@ F32 LLFrameTimer::getFrameDeltaTimeF32()
 {
 	return (F32)(U64_to_F64(sFrameDeltaTime) * USEC_TO_SEC_F64); 
 }
+
+
+//	static 
+// Return seconds since the current frame started
+F32  LLFrameTimer::getCurrentFrameTime()
+{
+	U64 frame_time = totalTime() - sTotalTime;
+	return (F32)(U64_to_F64(frame_time) * USEC_TO_SEC_F64); 
+}
+
+// Glue code to avoid full class .h file #includes
+F32  getCurrentFrameTime()
+{
+	return (F32)(LLFrameTimer::getCurrentFrameTime());
+}
diff --git a/indra/llcommon/llframetimer.h b/indra/llcommon/llframetimer.h
index 9d55fd1a0b4d217c1baa25de52630e83da8c42dc..2998560ab9fe43b391c6fb707b8db556971c21cc 100644
--- a/indra/llcommon/llframetimer.h
+++ b/indra/llcommon/llframetimer.h
@@ -74,6 +74,9 @@ class LLFrameTimer
 
 	static F32	getFrameDeltaTimeF32();
 
+	// Return seconds since the current frame started
+	static F32  getCurrentFrameTime();
+
 	// MANIPULATORS
 	void start();
 	void stop();
@@ -144,4 +147,7 @@ class LLFrameTimer
 	BOOL mStarted;
 };
 
+// Glue code for Havok (or anything else that doesn't want the full .h files)
+extern F32  getCurrentFrameTime();
+
 #endif  // LL_LLFRAMETIMER_H
diff --git a/indra/llcommon/llpreprocessor.h b/indra/llcommon/llpreprocessor.h
index ac06b431c1a91dc9f56ae2016ad45f98b212d3d5..454a820ce5b8ec83192e94745b076b057f146554 100644
--- a/indra/llcommon/llpreprocessor.h
+++ b/indra/llcommon/llpreprocessor.h
@@ -79,9 +79,16 @@
 
 #endif
 
+
 // Deal with the differeneces on Windows
 #if LL_MSVC
-#define snprintf safe_snprintf		/* Flawfinder: ignore */
+namespace snprintf_hack
+{
+	int snprintf(char *str, size_t size, const char *format, ...);
+}
+
+// #define snprintf safe_snprintf		/* Flawfinder: ignore */
+using snprintf_hack::snprintf;
 #endif	// LL_MSVC
 
 // Static linking with apr on windows needs to be declared.
diff --git a/indra/llcommon/llptrskiplist.h b/indra/llcommon/llptrskiplist.h
index 81c8ca3ef3ad0b908d5ad1160dfc9807762e2824..b03faf57f1e4d6310c88270eb5c793d1f3ad58e4 100644
--- a/indra/llcommon/llptrskiplist.h
+++ b/indra/llcommon/llptrskiplist.h
@@ -34,6 +34,7 @@
 
 #include "llerror.h"
 //#include "vmath.h"
+#include "llrand.h"
 
 /////////////////////////////////////////////
 //
diff --git a/indra/llcommon/llskiplist.h b/indra/llcommon/llskiplist.h
index 314043ebacf9e48cd5ca3141ace58480b05e56a4..17e0baddb7f40e2d43340f97b8df872a56def2d7 100644
--- a/indra/llcommon/llskiplist.h
+++ b/indra/llcommon/llskiplist.h
@@ -31,6 +31,7 @@
 #ifndef LL_LLSKIPLIST_H
 #define LL_LLSKIPLIST_H
 
+#include "llrand.h"
 #include "llrand.h"
 
 // NOTA BENE: Insert first needs to be < NOT <=
diff --git a/indra/llcommon/llstatenums.h b/indra/llcommon/llstatenums.h
index e9876f8061489869b41b9ff066792fe268149689..a82996b8527a130f0020e9c28be746dc46951539 100644
--- a/indra/llcommon/llstatenums.h
+++ b/indra/llcommon/llstatenums.h
@@ -57,7 +57,13 @@ enum
 	LL_SIM_STAT_VIRTUAL_SIZE_KB,
 	LL_SIM_STAT_RESIDENT_SIZE_KB,
 	LL_SIM_STAT_PENDING_LOCAL_UPLOADS,
-	LL_SIM_STAT_TOTAL_UNACKED_BYTES
+	LL_SIM_STAT_TOTAL_UNACKED_BYTES,
+	LL_SIM_STAT_PHYSICS_PINNED_TASKS,
+	LL_SIM_STAT_PHYSICS_LOD_TASKS,
+	LL_SIM_STAT_SIMPHYSICSSTEPMS,
+	LL_SIM_STAT_SIMPHYSICSSHAPEMS,
+	LL_SIM_STAT_SIMPHYSICSOTHERMS,
+	LL_SIM_STAT_SIMPHYSICSMEMORY
 };
 
 #endif
diff --git a/indra/llcommon/llstl.h b/indra/llcommon/llstl.h
index b692daefd8de765793dd9805159bd68e4d2e6b96..988e706091b4f45add6674c311481e4ad02ea8a2 100644
--- a/indra/llcommon/llstl.h
+++ b/indra/llcommon/llstl.h
@@ -33,6 +33,9 @@
 #define LL_LLSTL_H
 
 #include <functional>
+#include <algorithm>
+#include <map>
+#include <vector>
 #include <set>
 #include <deque>
 
diff --git a/indra/llcommon/llstring.cpp b/indra/llcommon/llstring.cpp
index a688bc1c6fd5edbe687390c09ea48cef9bba9d55..59d71a8e8e74b90c24935b36c67e058a04c599a7 100644
--- a/indra/llcommon/llstring.cpp
+++ b/indra/llcommon/llstring.cpp
@@ -804,16 +804,19 @@ std::string utf8str_removeCRLF(const std::string& utf8str)
 
 #if LL_WINDOWS
 // documentation moved to header. Phoenix 2007-11-27
-int safe_snprintf(char *str, size_t size, const char *format, ...)
+namespace snprintf_hack
 {
-	va_list args;
-	va_start(args, format);
-
-	int num_written = _vsnprintf(str, size, format, args); /* Flawfinder: ignore */
-	va_end(args);
-	
-	str[size-1] = '\0'; // always null terminate
-	return num_written;
+	int snprintf(char *str, size_t size, const char *format, ...)
+	{
+		va_list args;
+		va_start(args, format);
+
+		int num_written = _vsnprintf(str, size, format, args); /* Flawfinder: ignore */
+		va_end(args);
+		
+		str[size-1] = '\0'; // always null terminate
+		return num_written;
+	}
 }
 
 std::string ll_convert_wide_to_string(const wchar_t* in)
diff --git a/indra/llcommon/llversionserver.h b/indra/llcommon/llversionserver.h
index b87d054b3b85f645bddb24eca0c6b8c86764880c..5da3c01096bd80ced55a2893541e4e9d66638106 100644
--- a/indra/llcommon/llversionserver.h
+++ b/indra/llcommon/llversionserver.h
@@ -33,9 +33,9 @@
 #define LL_LLVERSIONSERVER_H
 
 const S32 LL_VERSION_MAJOR = 1;
-const S32 LL_VERSION_MINOR = 19;
-const S32 LL_VERSION_PATCH = 2;
-const S32 LL_VERSION_BUILD = 83236;
+const S32 LL_VERSION_MINOR = 20;
+const S32 LL_VERSION_PATCH = 0;
+const S32 LL_VERSION_BUILD = 83892;
 
 const char * const LL_CHANNEL = "Second Life Server";
 
diff --git a/indra/llmath/llline.cpp b/indra/llmath/llline.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b62631072b3a2ae56843522be3d422428e72eb7f
--- /dev/null
+++ b/indra/llmath/llline.cpp
@@ -0,0 +1,176 @@
+/** 
+ * @file llline.cpp
+ * @author Andrew Meadows
+ * @brief Simple line class that can compute nearest approach between two lines
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llline.h"
+#include "llrand.h"
+
+const F32 SOME_SMALL_NUMBER = 1.0e-5f;
+const F32 SOME_VERY_SMALL_NUMBER = 1.0e-8f;
+
+LLLine::LLLine()
+:	mPoint(0.f, 0.f, 0.f),
+	mDirection(1.f, 0.f, 0.f)
+{ }
+
+LLLine::LLLine( const LLVector3& first_point, const LLVector3& second_point )
+{
+	setPoints(first_point, second_point);
+}
+
+void LLLine::setPoints( const LLVector3& first_point, const LLVector3& second_point )
+{
+	mPoint = first_point;
+	mDirection = second_point - first_point;
+	mDirection.normalize();
+}
+
+void LLLine::setPointDirection( const LLVector3& first_point, const LLVector3& second_point )
+{
+	setPoints(first_point, first_point + second_point);
+}
+
+bool LLLine::intersects( const LLVector3& point, F32 radius ) const
+{
+	LLVector3 other_direction = point - mPoint;
+	LLVector3 nearest_point = mPoint + mDirection * (other_direction * mDirection);
+	F32 nearest_approach = (nearest_point - point).length();
+	return (nearest_approach <= radius);
+}
+
+// returns the point on this line that is closest to some_point
+LLVector3 LLLine::nearestApproach( const LLVector3& some_point ) const
+{
+	return (mPoint + mDirection * ((some_point - mPoint) * mDirection));
+}
+
+// the accuracy of this method sucks when you give it two nearly
+// parallel lines, so you should probably check for parallelism
+// before you call this
+// 
+// returns the point on this line that is closest to other_line
+LLVector3 LLLine::nearestApproach( const LLLine& other_line ) const
+{
+	LLVector3 between_points = other_line.mPoint - mPoint;
+	F32 dir_dot_dir = mDirection * other_line.mDirection;
+	F32 one_minus_dir_dot_dir = 1.0f - fabs(dir_dot_dir);
+	if ( one_minus_dir_dot_dir < SOME_VERY_SMALL_NUMBER )
+	{
+#ifdef LL_DEBUG
+		llwarns << "LLLine::nearestApproach() was given two very "
+			<< "nearly parallel lines dir1 = " << mDirection 
+			<< " dir2 = " << other_line.mDirection << " with 1-dot_product = " 
+			<< one_minus_dir_dot_dir << llendl;
+#endif
+		// the lines are approximately parallel
+		// We shouldn't fall in here because this check should have been made
+		// BEFORE this function was called.  We dare not continue with the 
+		// computations for fear of division by zero, but we have to return 
+		// something so we return a bogus point -- caller beware.
+		return 0.5f * (mPoint + other_line.mPoint);
+	}
+
+	F32 odir_dot_bp = other_line.mDirection * between_points;
+
+	F32 numerator = 0;
+	F32 denominator = 0;
+	for (S32 i=0; i<3; i++)
+	{
+		F32 factor = dir_dot_dir * other_line.mDirection.mV[i] - mDirection.mV[i];
+		numerator += ( between_points.mV[i] - odir_dot_bp * other_line.mDirection.mV[i] ) * factor;
+		denominator -= factor * factor;
+	}
+
+	F32 length_to_nearest_approach = numerator / denominator;
+
+	return mPoint + length_to_nearest_approach * mDirection;
+}
+
+std::ostream& operator<<( std::ostream& output_stream, const LLLine& line )
+{
+	output_stream << "{point=" << line.mPoint << "," << "dir=" << line.mDirection << "}";
+	return output_stream;
+}
+
+
+F32 ALMOST_PARALLEL = 0.99f;
+F32 TOO_SMALL_FOR_DIVISION = 0.0001f;
+
+// returns 'true' if this line intersects the plane
+// on success stores the intersection point in 'result'
+bool LLLine::intersectsPlane( LLVector3& result, const LLLine& plane ) const
+{
+	// p = P + l * d     equation for a line
+	// 
+	// N * p = D         equation for a point
+	//
+	// N * (P + l * d) = D
+	// N*P + l * (N*d) = D
+	// l * (N*d) = D - N*P
+	// l =  ( D - N*P ) / ( N*d )
+	//
+
+	F32 dot = plane.mDirection * mDirection;
+	if (fabs(dot) < TOO_SMALL_FOR_DIVISION)
+	{
+		return false;
+	}
+
+	F32 plane_dot = plane.mDirection * plane.mPoint;
+	F32 length = ( plane_dot - (plane.mDirection * mPoint) ) / dot;
+	result = mPoint + length * mDirection;
+	return true;
+}
+
+//static 
+// returns 'true' if planes intersect, and stores the result 
+// the second and third arguments are treated as planes
+// where mPoint is on the plane and mDirection is the normal
+// result.mPoint will be the intersection line's closest approach 
+// to first_plane.mPoint
+bool LLLine::getIntersectionBetweenTwoPlanes( LLLine& result, const LLLine& first_plane, const LLLine& second_plane )
+{
+	// TODO -- if we ever get some generic matrix solving code in our libs
+	// then we should just use that, since this problem is really just
+	// linear algebra.
+
+	F32 dot = fabs(first_plane.mDirection * second_plane.mDirection);
+	if (dot > ALMOST_PARALLEL)
+	{
+		// the planes are nearly parallel
+		return false;
+	}
+
+	LLVector3 direction = first_plane.mDirection % second_plane.mDirection;
+	direction.normalize();
+
+	LLVector3 first_intersection;
+	{
+		LLLine intersection_line(first_plane);
+		intersection_line.mDirection = direction % first_plane.mDirection;
+		intersection_line.mDirection.normalize();
+		intersection_line.intersectsPlane(first_intersection, second_plane);
+	}
+
+	/*
+	LLVector3 second_intersection;
+	{
+		LLLine intersection_line(second_plane);
+		intersection_line.mDirection = direction % second_plane.mDirection;
+		intersection_line.mDirection.normalize();
+		intersection_line.intersectsPlane(second_intersection, first_plane);
+	}
+	*/
+
+	result.mPoint = first_intersection;
+	result.mDirection = direction;
+
+	return true;
+}
+
+
diff --git a/indra/llmath/llline.h b/indra/llmath/llline.h
new file mode 100644
index 0000000000000000000000000000000000000000..cdae3fc1fe708d05a633ecd7132270e6df9fd697
--- /dev/null
+++ b/indra/llmath/llline.h
@@ -0,0 +1,62 @@
+// llline.h
+/**
+ * @file llline.cpp
+ * @author Andrew Meadows
+ * @brief Simple line for computing nearest approach between two infinite lines
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LINE_H
+#define LL_LINE_H
+
+#include <iostream>
+#include "stdtypes.h"
+#include "v3math.h"
+
+const F32 DEFAULT_INTERSECTION_ERROR = 0.000001f;
+
+class LLLine
+{
+public:
+	LLLine();
+	LLLine( const LLVector3& first_point, const LLVector3& second_point );
+	virtual ~LLLine() {};
+
+	void setPointDirection( const LLVector3& first_point, const LLVector3& second_point );
+	void setPoints( const LLVector3& first_point, const LLVector3& second_point );
+
+	bool intersects( const LLVector3& point, F32 radius = DEFAULT_INTERSECTION_ERROR ) const;
+
+	// returns the point on this line that is closest to some_point
+	LLVector3 nearestApproach( const LLVector3& some_point ) const;
+
+	// returns the point on this line that is closest to other_line
+	LLVector3 nearestApproach( const LLLine& other_line ) const;
+
+	friend std::ostream& operator<<( std::ostream& output_stream, const LLLine& line );
+
+	// returns 'true' if this line intersects the plane
+	// on success stores the intersection point in 'result'
+	bool intersectsPlane( LLVector3& result, const LLLine& plane ) const;
+
+	// returns 'true' if planes intersect, and stores the result 
+	// the second and third arguments are treated as planes
+	// where mPoint is on the plane and mDirection is the normal
+	// result.mPoint will be the intersection line's closest approach 
+	// to first_plane.mPoint
+	static bool getIntersectionBetweenTwoPlanes( LLLine& result, const LLLine& first_plane, const LLLine& second_plane );
+
+	const LLVector3& getPoint() const { return mPoint; }
+	const LLVector3& getDirection() const { return mDirection; }
+
+protected:
+	// these are protected because some code assumes that the normal is 
+	// always correct and properly normalized.
+	LLVector3 mPoint;
+	LLVector3 mDirection;
+};
+
+
+#endif
diff --git a/indra/llmath/llmath.h b/indra/llmath/llmath.h
index 6df241d3ab94c0233041c457815d016f41169b9b..5dfddff4eb62cf76d5117757ef740e4cc389dd86 100644
--- a/indra/llmath/llmath.h
+++ b/indra/llmath/llmath.h
@@ -32,8 +32,14 @@
 #ifndef LLMATH_H
 #define LLMATH_H
 
+#include <cmath>
+//#include <math.h>
+//#include <stdlib.h>
+#include "lldefs.h"
+
 // work around for Windows & older gcc non-standard function names.
 #if LL_WINDOWS
+#include <float.h>
 #define llisnan(val)	_isnan(val)
 #define llfinite(val)	_finite(val)
 #elif (LL_LINUX && __GNUC__ <= 2)
@@ -99,6 +105,12 @@ inline BOOL is_approx_equal(F32 x, F32 y)
 	return (abs((S32) ((U32&)x - (U32&)y) ) < COMPARE_MANTISSA_UP_TO_BIT);
 }
 
+inline BOOL is_approx_equal(F64 x, F64 y)
+{
+	const S64 COMPARE_MANTISSA_UP_TO_BIT = 0x02;
+	return (abs((S32) ((U64&)x - (U64&)y) ) < COMPARE_MANTISSA_UP_TO_BIT);
+}
+
 inline BOOL is_approx_equal_fraction(F32 x, F32 y, U32 frac_bits)
 {
 	BOOL ret = TRUE;
@@ -120,6 +132,27 @@ inline BOOL is_approx_equal_fraction(F32 x, F32 y, U32 frac_bits)
 	return ret;
 }
 
+inline BOOL is_approx_equal_fraction(F64 x, F64 y, U32 frac_bits)
+{
+	BOOL ret = TRUE;
+	F64 diff = (F64) fabs(x - y);
+
+	S32 diffInt = (S32) diff;
+	S32 diffFracTolerance = (S32) ((diff - (F64) diffInt) * (1 << frac_bits));
+	
+	// if integer portion is not equal, not enough bits were used for packing
+	// so error out since either the use case is not correct OR there is
+	// an issue with pack/unpack. should fail in either case.
+	// for decimal portion, make sure that the delta is no more than 1
+	// based on the number of bits used for packing decimal portion.
+	if (diffInt != 0 || diffFracTolerance > 1)
+	{
+		ret = FALSE;
+	}
+
+	return ret;
+}
+
 inline S32 llabs(const S32 a)
 {
 	return S32(labs(a));
diff --git a/indra/llmath/llquaternion.cpp b/indra/llmath/llquaternion.cpp
index 34c1fd1762c87ebbb6a158be6fc8ca8fd324e960..c3e84e366d7b6db69e334bc0a04e9754388f0c6e 100644
--- a/indra/llmath/llquaternion.cpp
+++ b/indra/llmath/llquaternion.cpp
@@ -51,19 +51,19 @@ const LLQuaternion LLQuaternion::DEFAULT;
 LLQuaternion::LLQuaternion(const LLMatrix4 &mat)
 {
 	*this = mat.quaternion();
-	normQuat();
+	normalize();
 }
 
 LLQuaternion::LLQuaternion(const LLMatrix3 &mat)
 {
 	*this = mat.quaternion();
-	normQuat();
+	normalize();
 }
 
 LLQuaternion::LLQuaternion(F32 angle, const LLVector4 &vec)
 {
 	LLVector3 v(vec.mV[VX], vec.mV[VY], vec.mV[VZ]);
-	v.normVec();
+	v.normalize();
 
 	F32 c, s;
 	c = cosf(angle*0.5f);
@@ -73,13 +73,13 @@ LLQuaternion::LLQuaternion(F32 angle, const LLVector4 &vec)
 	mQ[VY] = v.mV[VY] * s;
 	mQ[VZ] = v.mV[VZ] * s;
 	mQ[VW] = c;
-	normQuat();
+	normalize();
 }
 
 LLQuaternion::LLQuaternion(F32 angle, const LLVector3 &vec)
 {
 	LLVector3 v(vec);
-	v.normVec();
+	v.normalize();
 
 	F32 c, s;
 	c = cosf(angle*0.5f);
@@ -89,7 +89,7 @@ LLQuaternion::LLQuaternion(F32 angle, const LLVector3 &vec)
 	mQ[VY] = v.mV[VY] * s;
 	mQ[VZ] = v.mV[VZ] * s;
 	mQ[VW] = c;
-	normQuat();
+	normalize();
 }
 
 LLQuaternion::LLQuaternion(const LLVector3 &x_axis,
@@ -99,7 +99,7 @@ LLQuaternion::LLQuaternion(const LLVector3 &x_axis,
 	LLMatrix3 mat;
 	mat.setRows(x_axis, y_axis, z_axis);
 	*this = mat.quaternion();
-	normQuat();
+	normalize();
 }
 
 // Quatizations
@@ -138,10 +138,93 @@ void	LLQuaternion::quantize8(F32 lower, F32 upper)
 
 // Set LLQuaternion routines
 
+const LLQuaternion&	LLQuaternion::setAngleAxis(F32 angle, F32 x, F32 y, F32 z)
+{
+	LLVector3 vec(x, y, z);
+	vec.normalize();
+
+	angle *= 0.5f;
+	F32 c, s;
+	c = cosf(angle);
+	s = sinf(angle);
+
+	mQ[VX] = vec.mV[VX]*s;
+	mQ[VY] = vec.mV[VY]*s;
+	mQ[VZ] = vec.mV[VZ]*s;
+	mQ[VW] = c;
+
+	normalize();
+	return (*this);
+}
+
+const LLQuaternion&	LLQuaternion::setAngleAxis(F32 angle, const LLVector3 &vec)
+{
+	LLVector3 v(vec);
+	v.normalize();
+
+	angle *= 0.5f;
+	F32 c, s;
+	c = cosf(angle);
+	s = sinf(angle);
+
+	mQ[VX] = v.mV[VX]*s;
+	mQ[VY] = v.mV[VY]*s;
+	mQ[VZ] = v.mV[VZ]*s;
+	mQ[VW] = c;
+
+	normalize();
+	return (*this);
+}
+
+const LLQuaternion&	LLQuaternion::setAngleAxis(F32 angle, const LLVector4 &vec)
+{
+	LLVector3 v(vec.mV[VX], vec.mV[VY], vec.mV[VZ]);
+	v.normalize();
+
+	F32 c, s;
+	c = cosf(angle*0.5f);
+	s = sinf(angle*0.5f);
+
+	mQ[VX] = v.mV[VX]*s;
+	mQ[VY] = v.mV[VY]*s;
+	mQ[VZ] = v.mV[VZ]*s;
+	mQ[VW] = c;
+
+	normalize();
+	return (*this);
+}
+
+const LLQuaternion&	LLQuaternion::setEulerAngles(F32 roll, F32 pitch, F32 yaw)
+{
+	LLMatrix3 rot_mat(roll, pitch, yaw);
+	rot_mat.orthogonalize();
+	*this = rot_mat.quaternion();
+		
+	normalize();
+	return (*this);
+}
+
+// deprecated
+const LLQuaternion&	LLQuaternion::set(const LLMatrix3 &mat)
+{
+	*this = mat.quaternion();
+	normalize();
+	return (*this);
+}
+
+// deprecated
+const LLQuaternion&	LLQuaternion::set(const LLMatrix4 &mat)
+{
+	*this = mat.quaternion();
+	normalize();
+	return (*this);
+}
+
+// deprecated
 const LLQuaternion&	LLQuaternion::setQuat(F32 angle, F32 x, F32 y, F32 z)
 {
 	LLVector3 vec(x, y, z);
-	vec.normVec();
+	vec.normalize();
 
 	angle *= 0.5f;
 	F32 c, s;
@@ -153,14 +236,15 @@ const LLQuaternion&	LLQuaternion::setQuat(F32 angle, F32 x, F32 y, F32 z)
 	mQ[VZ] = vec.mV[VZ]*s;
 	mQ[VW] = c;
 
-	normQuat();
+	normalize();
 	return (*this);
 }
 
+// deprecated
 const LLQuaternion&	LLQuaternion::setQuat(F32 angle, const LLVector3 &vec)
 {
 	LLVector3 v(vec);
-	v.normVec();
+	v.normalize();
 
 	angle *= 0.5f;
 	F32 c, s;
@@ -172,14 +256,14 @@ const LLQuaternion&	LLQuaternion::setQuat(F32 angle, const LLVector3 &vec)
 	mQ[VZ] = v.mV[VZ]*s;
 	mQ[VW] = c;
 
-	normQuat();
+	normalize();
 	return (*this);
 }
 
 const LLQuaternion&	LLQuaternion::setQuat(F32 angle, const LLVector4 &vec)
 {
 	LLVector3 v(vec.mV[VX], vec.mV[VY], vec.mV[VZ]);
-	v.normVec();
+	v.normalize();
 
 	F32 c, s;
 	c = cosf(angle*0.5f);
@@ -190,7 +274,7 @@ const LLQuaternion&	LLQuaternion::setQuat(F32 angle, const LLVector4 &vec)
 	mQ[VZ] = v.mV[VZ]*s;
 	mQ[VW] = c;
 
-	normQuat();
+	normalize();
 	return (*this);
 }
 
@@ -200,7 +284,21 @@ const LLQuaternion&	LLQuaternion::setQuat(F32 roll, F32 pitch, F32 yaw)
 	rot_mat.orthogonalize();
 	*this = rot_mat.quaternion();
 		
-	normQuat();
+	normalize();
+	return (*this);
+}
+
+const LLQuaternion&	LLQuaternion::setQuat(const LLMatrix3 &mat)
+{
+	*this = mat.quaternion();
+	normalize();
+	return (*this);
+}
+
+const LLQuaternion&	LLQuaternion::setQuat(const LLMatrix4 &mat)
+{
+	*this = mat.quaternion();
+	normalize();
 	return (*this);
 //#if 1
 //	// NOTE: LLQuaternion's are actually inverted with respect to
@@ -337,8 +435,8 @@ void LLQuaternion::shortestArc(const LLVector3 &a, const LLVector3 &b)
 
 	// Make sure neither vector is zero length.  Also normalize
 	// the vectors while we are at it.
-	F32 vec_a_mag = vec_a.normVec();
-	F32 vec_b_mag = vec_b.normVec();
+	F32 vec_a_mag = vec_a.normalize();
+	F32 vec_b_mag = vec_b.normalize();
 	if (vec_a_mag < F_APPROXIMATELY_ZERO ||
 		vec_b_mag < F_APPROXIMATELY_ZERO)
 	{
@@ -370,7 +468,7 @@ void LLQuaternion::shortestArc(const LLVector3 &a, const LLVector3 &b)
 		ortho_axis -= proj;
 		
 		// Turn this into an orthonormal axis.
-		F32 ortho_length = ortho_axis.normVec();
+		F32 ortho_length = ortho_axis.normalize();
 		// If the axis' length is 0, then our guess at an orthogonal axis
 		// was wrong (a is parallel to the x-axis).
 		if (ortho_length < F_APPROXIMATELY_ZERO)
@@ -391,7 +489,7 @@ void LLQuaternion::shortestArc(const LLVector3 &a, const LLVector3 &b)
 		// Return the rotation between these vectors.
 		F32 theta = (F32)acos(cos_theta);
 
-		setQuat(theta, axis);
+		setAngleAxis(theta, axis);
 	}
 }
 
@@ -516,7 +614,7 @@ LLQuaternion lerp(F32 t, const LLQuaternion &p, const LLQuaternion &q)
 {
 	LLQuaternion r;
 	r = t * (q - p) + p;
-	r.normQuat();
+	r.normalize();
 	return r;
 }
 #endif
@@ -529,7 +627,7 @@ LLQuaternion lerp(F32 t, const LLQuaternion &q)
 	r.mQ[VY] = t * q.mQ[VY];
 	r.mQ[VZ] = t * q.mQ[VZ];
 	r.mQ[VW] = t * (q.mQ[VZ] - 1.f) + 1.f;
-	r.normQuat();
+	r.normalize();
 	return r;
 }
 
@@ -544,7 +642,7 @@ LLQuaternion lerp(F32 t, const LLQuaternion &p, const LLQuaternion &q)
 	r.mQ[VY] = t * q.mQ[VY] + (inv_t * p.mQ[VY]);
 	r.mQ[VZ] = t * q.mQ[VZ] + (inv_t * p.mQ[VZ]);
 	r.mQ[VW] = t * q.mQ[VW] + (inv_t * p.mQ[VW]);
-	r.normQuat();
+	r.normalize();
 	return r;
 }
 
@@ -640,8 +738,8 @@ LLQuaternion slerp(F32 t, const LLQuaternion &q)
         // when c < 0.0 then theta > PI/2 
         // since quat and -quat are the same rotation we invert one of  
         // p or q to reduce unecessary spins
-        // A equivalent way to do it is to convert acos(c) as if it had been negative,
-        // and to negate stp 
+        // A equivalent way to do it is to convert acos(c) as if it had 
+		// been negative, and to negate stp 
         angle   = (F32) acos(-c); 
         stp     = -(F32) sin(angle * (1.f - t));
         stq     = (F32) sin(angle * t);
@@ -742,20 +840,6 @@ LLQuaternion::Order StringToOrder( const char *str )
 	return LLQuaternion::XYZ;
 }
 
-const LLQuaternion&	LLQuaternion::setQuat(const LLMatrix3 &mat)
-{
-	*this = mat.quaternion();
-	normQuat();
-	return (*this);
-}
-
-const LLQuaternion&	LLQuaternion::setQuat(const LLMatrix4 &mat)
-{
-	*this = mat.quaternion();
-	normQuat();
-	return (*this);
-}
-
 void LLQuaternion::getAngleAxis(F32* angle, LLVector3 &vec) const
 {
 	F32 cos_a = mQ[VW];
@@ -769,10 +853,28 @@ void LLQuaternion::getAngleAxis(F32* angle, LLVector3 &vec) const
 	else
 		sin_a = 1.f/sin_a;
 
-    *angle = 2.0f * (F32) acos( cos_a );
-    vec.mV[VX] = mQ[VX] * sin_a;
-    vec.mV[VY] = mQ[VY] * sin_a;
-    vec.mV[VZ] = mQ[VZ] * sin_a;
+    F32 temp_angle = 2.0f * (F32) acos( cos_a );
+	if (temp_angle > F_PI)
+	{
+		// The (angle,axis) pair should never have angles outside [PI, -PI]
+		// since we want the _shortest_ (angle,axis) solution.
+		// Since acos is defined for [0, PI], and we multiply by 2.0, we
+		// can push the angle outside the acceptible range.
+		// When this happens we set the angle to the other portion of a 
+		// full 2PI rotation, and negate the axis, which reverses the 
+		// direction of the rotation (by the right-hand rule).
+		*angle = 2.f * F_PI - temp_angle;
+    	vec.mV[VX] = - mQ[VX] * sin_a;
+    	vec.mV[VY] = - mQ[VY] * sin_a;
+    	vec.mV[VZ] = - mQ[VZ] * sin_a;
+	}
+	else
+	{
+		*angle = temp_angle;
+    	vec.mV[VX] = mQ[VX] * sin_a;
+    	vec.mV[VY] = mQ[VY] * sin_a;
+    	vec.mV[VZ] = mQ[VZ] * sin_a;
+	}
 }
 
 
@@ -846,7 +948,7 @@ BOOL LLQuaternion::parseQuat(const char* buf, LLQuaternion* value)
 	S32 count = sscanf( buf, "%f %f %f %f", quat.mQ + 0, quat.mQ + 1, quat.mQ + 2, quat.mQ + 3 );
 	if( 4 == count )
 	{
-		value->setQuat( quat );
+		value->set( quat );
 		return TRUE;
 	}
 
diff --git a/indra/llmath/llquaternion.h b/indra/llmath/llquaternion.h
index 01ddae94cb530b7b119c753edc96c270e442a56f..a088d70674d2556e1ded8746f97fc11bf7b8ec3c 100644
--- a/indra/llmath/llquaternion.h
+++ b/indra/llmath/llquaternion.h
@@ -57,10 +57,10 @@ class LLQuaternion
 	LLQuaternion();									// Initializes Quaternion to (0,0,0,1)
 	explicit LLQuaternion(const LLMatrix4 &mat);				// Initializes Quaternion from Matrix4
 	explicit LLQuaternion(const LLMatrix3 &mat);				// Initializes Quaternion from Matrix3
-	LLQuaternion(F32 x, F32 y, F32 z, F32 w);		// Initializes Quaternion to normQuat(x, y, z, w)
+	LLQuaternion(F32 x, F32 y, F32 z, F32 w);		// Initializes Quaternion to normalize(x, y, z, w)
 	LLQuaternion(F32 angle, const LLVector4 &vec);	// Initializes Quaternion to axis_angle2quat(angle, vec)
 	LLQuaternion(F32 angle, const LLVector3 &vec);	// Initializes Quaternion to axis_angle2quat(angle, vec)
-	LLQuaternion(const F32 *q);						// Initializes Quaternion to normQuat(x, y, z, w)
+	LLQuaternion(const F32 *q);						// Initializes Quaternion to normalize(x, y, z, w)
 	LLQuaternion(const LLVector3 &x_axis,
 				 const LLVector3 &y_axis,
 				 const LLVector3 &z_axis);			// Initializes Quaternion from Matrix3 = [x_axis ; y_axis ; z_axis]
@@ -71,15 +71,27 @@ class LLQuaternion
 	void quantize16(F32 lower, F32 upper);					// changes the vector to reflect quatization
 	void quantize8(F32 lower, F32 upper);							// changes the vector to reflect quatization
 	void loadIdentity();											// Loads the quaternion that represents the identity rotation
-	const LLQuaternion&	setQuatInit(F32 x, F32 y, F32 z, F32 w);	// Sets Quaternion to normQuat(x, y, z, w)
-	const LLQuaternion&	setQuat(const LLQuaternion &quat);			// Copies Quaternion
-	const LLQuaternion&	setQuat(const F32 *q);						// Sets Quaternion to normQuat(quat[VX], quat[VY], quat[VZ], quat[VW])
-	const LLQuaternion&	setQuat(const LLMatrix3 &mat);				// Sets Quaternion to mat2quat(mat)
-	const LLQuaternion&	setQuat(const LLMatrix4 &mat);				// Sets Quaternion to mat2quat(mat)
-	const LLQuaternion&	setQuat(F32 angle, F32 x, F32 y, F32 z);	// Sets Quaternion to axis_angle2quat(angle, x, y, z)
-	const LLQuaternion&	setQuat(F32 angle, const LLVector3 &vec);	// Sets Quaternion to axis_angle2quat(angle, vec)
-	const LLQuaternion&	setQuat(F32 angle, const LLVector4 &vec);	// Sets Quaternion to axis_angle2quat(angle, vec)
-	const LLQuaternion&	setQuat(F32 roll, F32 pitch, F32 yaw);		// Sets Quaternion to euler2quat(pitch, yaw, roll)
+
+	const LLQuaternion&	set(F32 x, F32 y, F32 z, F32 w);		// Sets Quaternion to normalize(x, y, z, w)
+	const LLQuaternion&	set(const LLQuaternion &quat);			// Copies Quaternion
+	const LLQuaternion&	set(const F32 *q);						// Sets Quaternion to normalize(quat[VX], quat[VY], quat[VZ], quat[VW])
+	const LLQuaternion&	set(const LLMatrix3 &mat);				// Sets Quaternion to mat2quat(mat)
+	const LLQuaternion&	set(const LLMatrix4 &mat);				// Sets Quaternion to mat2quat(mat)
+
+	const LLQuaternion&	setAngleAxis(F32 angle, F32 x, F32 y, F32 z);	// Sets Quaternion to axis_angle2quat(angle, x, y, z)
+	const LLQuaternion&	setAngleAxis(F32 angle, const LLVector3 &vec);	// Sets Quaternion to axis_angle2quat(angle, vec)
+	const LLQuaternion&	setAngleAxis(F32 angle, const LLVector4 &vec);	// Sets Quaternion to axis_angle2quat(angle, vec)
+	const LLQuaternion&	setEulerAngles(F32 roll, F32 pitch, F32 yaw);	// Sets Quaternion to euler2quat(pitch, yaw, roll)
+
+	const LLQuaternion&	setQuatInit(F32 x, F32 y, F32 z, F32 w);	// deprecated
+	const LLQuaternion&	setQuat(const LLQuaternion &quat);			// deprecated
+	const LLQuaternion&	setQuat(const F32 *q);						// deprecated
+	const LLQuaternion&	setQuat(const LLMatrix3 &mat);				// deprecated
+	const LLQuaternion&	setQuat(const LLMatrix4 &mat);				// deprecated
+	const LLQuaternion&	setQuat(F32 angle, F32 x, F32 y, F32 z);	// deprecated
+	const LLQuaternion&	setQuat(F32 angle, const LLVector3 &vec);	// deprecated
+	const LLQuaternion&	setQuat(F32 angle, const LLVector4 &vec);	// deprecated
+	const LLQuaternion&	setQuat(F32 roll, F32 pitch, F32 yaw);		// deprecated
 
 	LLMatrix4	getMatrix4(void) const;							// Returns the Matrix4 equivalent of Quaternion
 	LLMatrix3	getMatrix3(void) const;							// Returns the Matrix3 equivalent of Quaternion
@@ -87,11 +99,16 @@ class LLQuaternion
 	void		getAngleAxis(F32* angle, LLVector3 &vec) const;
 	void		getEulerAngles(F32 *roll, F32* pitch, F32 *yaw) const;
 
-	F32				normQuat();					// Normalizes Quaternion and returns magnitude
-	const LLQuaternion&	conjQuat(void);				// Conjugates Quaternion and returns result
+	F32	normalize();	// Normalizes Quaternion and returns magnitude
+	F32	normQuat();		// deprecated
+
+	const LLQuaternion&	conjugate(void);	// Conjugates Quaternion and returns result
+	const LLQuaternion&	conjQuat(void);		// deprecated
 
 	// Other useful methods
-	const LLQuaternion&	transQuat();											// Transpose
+	const LLQuaternion&	transpose();		// transpose (same as conjugate)
+	const LLQuaternion&	transQuat();		// deprecated
+
 	void			shortestArc(const LLVector3 &a, const LLVector3 &b);	// shortest rotation from a to b
 	const LLQuaternion& constrain(F32 radians);						// constrains rotation to a cone angle specified in radians
 
@@ -189,7 +206,7 @@ inline LLQuaternion::LLQuaternion(F32 x, F32 y, F32 z, F32 w)
 	mQ[VS] = w;
 
 	//RN: don't normalize this case as its used mainly for temporaries during calculations
-	//normQuat();
+	//normalize();
 	/*
 	F32 mag = sqrtf(mQ[VX]*mQ[VX] + mQ[VY]*mQ[VY] + mQ[VZ]*mQ[VZ] + mQ[VS]*mQ[VS]);
 	mag -= 1.f;
@@ -205,7 +222,7 @@ inline LLQuaternion::LLQuaternion(const F32 *q)
 	mQ[VZ] = q[VZ];
 	mQ[VS] = q[VW];
 
-	normQuat();
+	normalize();
 	/*
 	F32 mag = sqrtf(mQ[VX]*mQ[VX] + mQ[VY]*mQ[VY] + mQ[VZ]*mQ[VZ] + mQ[VS]*mQ[VS]);
 	mag -= 1.f;
@@ -224,33 +241,67 @@ inline void LLQuaternion::loadIdentity()
 }
 
 
+inline const LLQuaternion&	LLQuaternion::set(F32 x, F32 y, F32 z, F32 w)
+{
+	mQ[VX] = x;
+	mQ[VY] = y;
+	mQ[VZ] = z;
+	mQ[VS] = w;
+	normalize();
+	return (*this);
+}
+
+inline const LLQuaternion&	LLQuaternion::set(const LLQuaternion &quat)
+{
+	mQ[VX] = quat.mQ[VX];
+	mQ[VY] = quat.mQ[VY];
+	mQ[VZ] = quat.mQ[VZ];
+	mQ[VW] = quat.mQ[VW];
+	normalize();
+	return (*this);
+}
+
+inline const LLQuaternion&	LLQuaternion::set(const F32 *q)
+{
+	mQ[VX] = q[VX];
+	mQ[VY] = q[VY];
+	mQ[VZ] = q[VZ];
+	mQ[VS] = q[VW];
+	normalize();
+	return (*this);
+}
+
+
+// deprecated
 inline const LLQuaternion&	LLQuaternion::setQuatInit(F32 x, F32 y, F32 z, F32 w)
 {
 	mQ[VX] = x;
 	mQ[VY] = y;
 	mQ[VZ] = z;
 	mQ[VS] = w;
-	normQuat();
+	normalize();
 	return (*this);
 }
 
+// deprecated
 inline const LLQuaternion&	LLQuaternion::setQuat(const LLQuaternion &quat)
 {
 	mQ[VX] = quat.mQ[VX];
 	mQ[VY] = quat.mQ[VY];
 	mQ[VZ] = quat.mQ[VZ];
 	mQ[VW] = quat.mQ[VW];
-	normQuat();
+	normalize();
 	return (*this);
 }
 
+// deprecated
 inline const LLQuaternion&	LLQuaternion::setQuat(const F32 *q)
 {
 	mQ[VX] = q[VX];
 	mQ[VY] = q[VY];
 	mQ[VZ] = q[VZ];
 	mQ[VS] = q[VW];
-	normQuat();
+	normalize();
 	return (*this);
 }
 
@@ -270,10 +321,36 @@ inline void LLQuaternion::getAngleAxis(F32* angle, F32* x, F32* y, F32* z) const
 	else
 		sin_a = 1.f/sin_a;
 
-    *angle = 2.0f * (F32) acos( cos_a );
-    *x = mQ[VX] * sin_a;
-    *y = mQ[VY] * sin_a;
-    *z = mQ[VZ] * sin_a;
+    F32 temp_angle = 2.0f * (F32) acos( cos_a );
+	if (temp_angle > F_PI)
+	{
+		// The (angle,axis) pair should never have angles outside [PI, -PI]
+		// since we want the _shortest_ (angle,axis) solution.
+		// Since acos is defined for [0, PI], and we multiply by 2.0, we
+		// can push the angle outside the acceptible range.
+		// When this happens we set the angle to the other portion of a 
+		// full 2PI rotation, and negate the axis, which reverses the 
+		// direction of the rotation (by the right-hand rule).
+		*angle = 2.f * F_PI - temp_angle;
+    	*x = - mQ[VX] * sin_a;
+    	*y = - mQ[VY] * sin_a;
+    	*z = - mQ[VZ] * sin_a;
+	}
+	else
+	{
+		*angle = temp_angle;
+    	*x = mQ[VX] * sin_a;
+    	*y = mQ[VY] * sin_a;
+    	*z = mQ[VZ] * sin_a;
+	}
+}
+
+inline const LLQuaternion& LLQuaternion::conjugate()
+{
+	mQ[VX] *= -1.f;
+	mQ[VY] *= -1.f;
+	mQ[VZ] *= -1.f;
+	return (*this);
 }
 
 inline const LLQuaternion& LLQuaternion::conjQuat()
@@ -285,12 +362,21 @@ inline const LLQuaternion& LLQuaternion::conjQuat()
 }
 
 // Transpose
+inline const LLQuaternion& LLQuaternion::transpose()
+{
+	mQ[VX] *= -1.f;
+	mQ[VY] *= -1.f;
+	mQ[VZ] *= -1.f;
+	return (*this);
+}
+
+// deprecated
 inline const LLQuaternion& LLQuaternion::transQuat()
 {
-	mQ[VX] = -mQ[VX];
-	mQ[VY] = -mQ[VY];
-	mQ[VZ] = -mQ[VZ];
-	return *this;
+	mQ[VX] *= -1.f;
+	mQ[VY] *= -1.f;
+	mQ[VZ] *= -1.f;
+	return (*this);
 }
 
 
@@ -382,6 +468,30 @@ inline const LLQuaternion&	operator*=(LLQuaternion &a, const LLQuaternion &b)
 	return a;
 }
 
+inline F32	LLQuaternion::normalize()
+{
+	F32 mag = sqrtf(mQ[VX]*mQ[VX] + mQ[VY]*mQ[VY] + mQ[VZ]*mQ[VZ] + mQ[VS]*mQ[VS]);
+
+	if (mag > FP_MAG_THRESHOLD)
+	{
+		F32 oomag = 1.f/mag;
+		mQ[VX] *= oomag;
+		mQ[VY] *= oomag;
+		mQ[VZ] *= oomag;
+		mQ[VS] *= oomag;
+	}
+	else
+	{
+		mQ[VX] = 0.f;
+		mQ[VY] = 0.f;
+		mQ[VZ] = 0.f;
+		mQ[VS] = 1.f;
+	}
+
+	return mag;
+}
+
+// deprecated
 inline F32	LLQuaternion::normQuat()
 {
 	F32 mag = sqrtf(mQ[VX]*mQ[VX] + mQ[VY]*mQ[VY] + mQ[VZ]*mQ[VZ] + mQ[VS]*mQ[VS]);
diff --git a/indra/llmath/llsphere.cpp b/indra/llmath/llsphere.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3428dc14872dfb3c2d1676ccca4d42d3b4caa8ca
--- /dev/null
+++ b/indra/llmath/llsphere.cpp
@@ -0,0 +1,351 @@
+/** 
+ * @file llsphere.cpp
+ * @author Andrew Meadows
+ * @brief Simple line class that can compute nearest approach between two lines
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llsphere.h"
+
+LLSphere::LLSphere()
+:	mCenter(0.f, 0.f, 0.f),
+	mRadius(0.f)
+{ }
+
+LLSphere::LLSphere( const LLVector3& center, F32 radius)
+{
+	set(center, radius);
+}
+
+void LLSphere::set( const LLVector3& center, F32 radius )
+{
+	mCenter = center;
+	setRadius(radius);
+}
+
+void LLSphere::setCenter( const LLVector3& center)
+{
+	mCenter = center;
+}
+
+void LLSphere::setRadius( F32 radius)
+{
+	if (radius < 0.f)
+	{
+		radius = -radius;
+	}
+	mRadius = radius;
+}
+	
+const LLVector3& LLSphere::getCenter() const
+{
+	return mCenter;
+}
+
+F32 LLSphere::getRadius() const
+{
+	return mRadius;
+}
+
+// returns 'TRUE' if this sphere completely contains other_sphere
+BOOL LLSphere::contains(const LLSphere& other_sphere) const
+{
+	F32 separation = (mCenter - other_sphere.mCenter).length();
+	return (mRadius >= separation + other_sphere.mRadius) ? TRUE : FALSE;
+}
+
+// returns 'TRUE' if this sphere completely contains other_sphere
+BOOL LLSphere::overlaps(const LLSphere& other_sphere) const
+{
+	F32 separation = (mCenter - other_sphere.mCenter).length();
+	return (separation <= mRadius + other_sphere.mRadius) ? TRUE : FALSE;
+}
+
+// returns overlap
+// negative overlap is closest approach
+F32 LLSphere::getOverlap(const LLSphere& other_sphere) const
+{
+	// separation is distance from other_sphere's edge and this center
+	return (mCenter - other_sphere.mCenter).length() - mRadius - other_sphere.mRadius;
+}
+
+bool LLSphere::operator==(const LLSphere& rhs) const
+{
+	// TODO? -- use approximate equality for centers?
+	return (mRadius == rhs.mRadius
+			&& mCenter == rhs.mCenter);
+}
+
+std::ostream& operator<<( std::ostream& output_stream, const LLSphere& sphere)
+{
+	output_stream << "{center=" << sphere.mCenter << "," << "radius=" << sphere.mRadius << "}";
+	return output_stream;
+}
+
+// static 
+// removes any spheres that are contained in others
+void LLSphere::collapse(std::vector<LLSphere>& sphere_list)
+{
+	std::vector<LLSphere>::iterator first_itr = sphere_list.begin();
+	while (first_itr != sphere_list.end())
+	{
+		bool delete_from_front = false;
+
+		std::vector<LLSphere>::iterator second_itr = first_itr;
+		++second_itr;
+		while (second_itr != sphere_list.end())
+		{
+			if (second_itr->contains(*first_itr))
+			{
+				delete_from_front = true;
+				break;
+			}
+			else if (first_itr->contains(*second_itr))
+			{
+				sphere_list.erase(second_itr++);
+			}
+			else
+			{
+				++second_itr;
+			}
+		}
+
+		if (delete_from_front)
+		{
+			sphere_list.erase(first_itr++);
+		}
+		else
+		{
+			++first_itr;
+		}
+	}
+}
+
+// static
+// returns the bounding sphere that contains both spheres
+LLSphere LLSphere::getBoundingSphere(const LLSphere& first_sphere, const LLSphere& second_sphere)
+{
+	LLVector3 direction = second_sphere.mCenter - first_sphere.mCenter;
+
+	// HACK -- it is possible to get enough floating point error in the 
+	// other getBoundingSphere() method that we have to add some slop
+	// at the end.  Unfortunately, this breaks the link-order invarience
+	// for the linkability tests... unless we also apply the same slop
+	// here.
+	F32 half_milimeter = 0.0005f;
+
+	F32 distance = direction.length();
+	if (0.f == distance)
+	{
+		direction.setVec(1.f, 0.f, 0.f);
+	}
+	else
+	{
+		direction.normVec();
+	}
+	// the 'edge' is measured from the first_sphere's center
+	F32 max_edge = 0.f;
+	F32 min_edge = 0.f;
+
+	max_edge = llmax(max_edge + first_sphere.getRadius(), max_edge + distance + second_sphere.getRadius() + half_milimeter);
+	min_edge = llmin(min_edge - first_sphere.getRadius(), min_edge + distance - second_sphere.getRadius() - half_milimeter);
+	F32 radius = 0.5f * (max_edge - min_edge);
+	LLVector3 center = first_sphere.mCenter + (0.5f * (max_edge + min_edge)) * direction;
+	return LLSphere(center, radius);
+}
+
+// static
+// returns the bounding sphere that contains an arbitrary set of spheres
+LLSphere LLSphere::getBoundingSphere(const std::vector<LLSphere>& sphere_list)
+{
+	// this algorithm can get relatively inaccurate when the sphere 
+	// collection is 'small' (contained within a bounding sphere of about 
+	// 2 meters or less)
+	// TODO -- improve the accuracy for small collections of spheres
+
+	LLSphere bounding_sphere( LLVector3(0.f, 0.f, 0.f), 0.f );
+	S32 sphere_count = sphere_list.size();
+	if (1 == sphere_count)
+	{
+		// trivial case -- single sphere
+		std::vector<LLSphere>::const_iterator sphere_itr = sphere_list.begin();
+		bounding_sphere = *sphere_itr;
+	}
+	else if (2 == sphere_count)
+	{
+		// trivial case -- two spheres
+		std::vector<LLSphere>::const_iterator first_sphere = sphere_list.begin();
+		std::vector<LLSphere>::const_iterator second_sphere = first_sphere;
+		++second_sphere;
+		bounding_sphere = LLSphere::getBoundingSphere(*first_sphere, *second_sphere);
+	}
+	else if (sphere_count > 0)
+	{
+		// non-trivial case -- we will approximate the solution
+		//
+		// NOTE -- there is a fancy/fast way to do this for large 
+		// numbers of arbirary N-dimensional spheres -- you can look it
+		// up on the net.  We're dealing with 3D spheres at collection
+		// sizes of 256 spheres or smaller, so we just use this
+		// brute force method.
+
+		// TODO -- perhaps would be worthwile to test for the solution where
+		// the largest spanning radius just happens to work.  That is, where
+		// there are really two spheres that determine the bounding sphere,
+		// and all others are contained therein.
+
+		// compute the AABB
+		std::vector<LLSphere>::const_iterator first_itr = sphere_list.begin();
+		LLVector3 max_corner = first_itr->getCenter() + first_itr->getRadius() * LLVector3(1.f, 1.f, 1.f);
+		LLVector3 min_corner = first_itr->getCenter() - first_itr->getRadius() * LLVector3(1.f, 1.f, 1.f);
+		{
+			std::vector<LLSphere>::const_iterator sphere_itr = sphere_list.begin();
+			for (++sphere_itr; sphere_itr != sphere_list.end(); ++sphere_itr)
+			{
+				LLVector3 center = sphere_itr->getCenter();
+				F32 radius = sphere_itr->getRadius();
+				for (S32 i=0; i<3; ++i)
+				{
+					if (center.mV[i] + radius > max_corner.mV[i])
+					{
+						max_corner.mV[i] = center.mV[i] + radius;
+					}
+					if (center.mV[i] - radius < min_corner.mV[i])
+					{
+						min_corner.mV[i] = center.mV[i] - radius;
+					}
+				}
+			}
+		}
+
+		// get the starting center and radius from the AABB
+		LLVector3 diagonal = max_corner - min_corner;
+		F32 bounding_radius = 0.5f * diagonal.length();
+		LLVector3 bounding_center = 0.5f * (max_corner + min_corner);
+
+		// compute the starting step-size
+		F32 minimum_radius = 0.5f * llmin(diagonal.mV[VX], llmin(diagonal.mV[VY], diagonal.mV[VZ]));
+		F32 step_length = bounding_radius - minimum_radius;
+		S32 step_count = 0;
+		S32 max_step_count = 12;
+		F32 half_milimeter = 0.0005f;
+
+		// wander the center around in search of tighter solutions
+		S32 last_dx = 2;	// 2 is out of bounds --> no match
+		S32 last_dy = 2;
+		S32 last_dz = 2;
+
+		while (step_length > half_milimeter
+				&& step_count < max_step_count)
+		{
+			// the algorithm for testing the maximum radius could be expensive enough
+			// that it makes sense to NOT duplicate testing when possible, so we keep
+			// track of where we last tested, and only test the new points
+
+			S32 best_dx = 0;
+			S32 best_dy = 0;
+			S32 best_dz = 0;
+
+			// sample near the center of the box
+			bool found_better_center = false;
+			for (S32 dx = -1; dx < 2; ++dx)
+			{
+				for (S32 dy = -1; dy < 2; ++dy)
+				{
+					for (S32 dz = -1; dz < 2; ++dz)
+					{
+						if (dx == 0 && dy == 0 && dz == 0)
+						{
+							continue;
+						}
+
+						// count the number of indecies that match the last_*'s
+						S32 match_count = 0;
+						if (last_dx == dx) ++match_count;
+						if (last_dy == dy) ++match_count;
+						if (last_dz == dz) ++match_count;
+						if (match_count == 2)
+						{
+							// we've already tested this point
+							continue;
+						}
+
+						LLVector3 center = bounding_center;
+						center.mV[VX] += (F32) dx * step_length;
+						center.mV[VY] += (F32) dy * step_length;
+						center.mV[VZ] += (F32) dz * step_length;
+
+						// compute the radius of the bounding sphere
+						F32 max_radius = 0.f;
+						std::vector<LLSphere>::const_iterator sphere_itr;
+						for (sphere_itr = sphere_list.begin(); sphere_itr != sphere_list.end(); ++sphere_itr)
+						{
+							F32 radius = (sphere_itr->getCenter() - center).length() + sphere_itr->getRadius();
+							if (radius > max_radius)
+							{
+								max_radius = radius;
+							}
+						}
+						if (max_radius < bounding_radius)
+						{
+							best_dx = dx;
+							best_dy = dy;
+							best_dz = dz;
+							bounding_center = center;
+							bounding_radius = max_radius;
+							found_better_center = true;
+						}
+					}
+				}
+			}
+			if (found_better_center)
+			{
+				// remember where we came from so we can avoid retesting
+				last_dx = -best_dx;
+				last_dy = -best_dy;
+				last_dz = -best_dz;
+			}
+			else
+			{
+				// reduce the step size
+				step_length *= 0.5f;
+				//++step_count;
+				// reset the last_*'s
+				last_dx = 2;	// 2 is out of bounds --> no match
+				last_dy = 2;
+				last_dz = 2;
+			}
+		}
+
+		// HACK -- it is possible to get enough floating point error for the
+		// bounding sphere to too small on the order of 10e-6, but we only need
+		// it to be accurate to within about half a millimeter
+		bounding_radius += half_milimeter;
+
+		// this algorithm can get relatively inaccurate when the sphere 
+		// collection is 'small' (contained within a bounding sphere of about 
+		// 2 meters or less)
+		// TODO -- fix this
+		/* debug code
+		{
+			std::vector<LLSphere>::const_iterator sphere_itr;
+			for (sphere_itr = sphere_list.begin(); sphere_itr != sphere_list.end(); ++sphere_itr)
+			{
+				F32 radius = (sphere_itr->getCenter() - bounding_center).length() + sphere_itr->getRadius();
+				if (radius + 0.1f > bounding_radius)
+				{
+					std::cout << " rad = " << radius << "  bounding - rad = " << (bounding_radius - radius) << std::endl;
+				}
+			}
+			std::cout << "\n" << std::endl;
+		}
+		*/ 
+
+		bounding_sphere.set(bounding_center, bounding_radius);
+	}
+	return bounding_sphere;
+}
+
+
diff --git a/indra/llmath/llsphere.h b/indra/llmath/llsphere.h
new file mode 100644
index 0000000000000000000000000000000000000000..709406eb5eaa87de0b772386ebc17fc47db5015a
--- /dev/null
+++ b/indra/llmath/llsphere.h
@@ -0,0 +1,59 @@
+// llsphere.h
+/**
+ * @file llsphere.cpp
+ * @author Andrew Meadows
+ * @brief Simple sphere implementation for basic geometric operations
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_SPHERE_H
+#define LL_SPHERE_H
+
+#include "stdtypes.h"
+#include "v3math.h"
+#include <iostream>
+#include <vector>
+
+class LLSphere
+{
+public:
+	LLSphere();
+	LLSphere( const LLVector3& center, F32 radius );
+
+	void set( const LLVector3& center, F32 radius );
+	void setCenter( const LLVector3& center );
+	void setRadius( F32 radius );
+
+	const LLVector3& getCenter() const;
+	F32 getRadius() const;
+
+	// returns TRUE if this sphere completely contains other_sphere
+	BOOL contains(const LLSphere& other_sphere) const;
+
+	// returns TRUE if this sphere overlaps other_sphere
+	BOOL overlaps(const LLSphere& other_sphere) const;
+
+	// returns overlap distance
+	// negative overlap is closest approach
+	F32 getOverlap(const LLSphere& other_sphere) const;
+
+	// removes any spheres that are contained in others
+	static void collapse(std::vector<LLSphere>& sphere_list);
+
+	// returns minimum sphere bounding sphere for a set of spheres
+	static LLSphere getBoundingSphere(const LLSphere& first_sphere, const LLSphere& second_sphere);
+	static LLSphere getBoundingSphere(const std::vector<LLSphere>& sphere_list);
+
+	bool operator==(const LLSphere& rhs) const;
+
+	friend std::ostream& operator<<( std::ostream& output_stream, const LLSphere& line );
+
+protected:
+	LLVector3 mCenter;
+	F32 mRadius;
+};
+
+
+#endif
diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp
index 0c711cabcd9f72480ef51d0df3887ce6a3d0317e..43b42bf182868dbe3de1051cbe43421a78d92112 100644
--- a/indra/llmath/llvolume.cpp
+++ b/indra/llmath/llvolume.cpp
@@ -2510,12 +2510,19 @@ bool LLVolumeParams::validate(U8 prof_curve, F32 prof_begin, F32 prof_end, F32 h
 	return true;
 }
 
-#define MAX_INDEX 10000
 S32 *LLVolume::getTriangleIndices(U32 &num_indices) const
 {
-	S32 index[MAX_INDEX];
+	S32 expected_num_triangle_indices = getNumTriangleIndices();
+	if (expected_num_triangle_indices > MAX_VOLUME_TRIANGLE_INDICES)
+	{
+		// we don't allow LLVolumes with this many vertices
+		llwarns << "Couldn't allocate triangle indices" << llendl;
+		num_indices = 0;
+		return NULL;
+	}
+
+	S32* index = new S32[expected_num_triangle_indices];
 	S32 count = 0;
-	S32 *indices = NULL;
 
 	// Let's do this totally diffently, as we don't care about faces...
 	// Counter-clockwise triangles are forward facing...
@@ -2529,6 +2536,9 @@ S32 *LLVolume::getTriangleIndices(U32 &num_indices) const
 	size_s_out = getProfile().getTotalOut();
 	size_t = getPath().mPath.size();
 
+	// NOTE -- if the construction of the triangles below ever changes
+	// then getNumTriangleIndices() method may also have to be updated.
+
 	if (open)		/* Flawfinder: ignore */
 	{
 		if (hollow)
@@ -2536,9 +2546,6 @@ S32 *LLVolume::getTriangleIndices(U32 &num_indices) const
 			// Open hollow -- much like the closed solid, except we 
 			// we need to stitch up the gap between s=0 and s=size_s-1
 
-			if ( (size_t - 1) * (((size_s -1) * 6) + 6) >= MAX_INDEX)
-				goto noindices;
-
 			for (t = 0; t < size_t - 1; t++)
 			{
 				// The outer face, first cut, and inner face
@@ -2652,8 +2659,6 @@ S32 *LLVolume::getTriangleIndices(U32 &num_indices) const
 
 					if (use_tri1a2)
 					{
-						if (count + 3 >= MAX_INDEX)
-							goto noindices;
 						index[count++] = pt1 + i;
 						index[count++] = pt1 + 1 + i;
 						index[count++] = pt2 + i;
@@ -2661,8 +2666,6 @@ S32 *LLVolume::getTriangleIndices(U32 &num_indices) const
 					}
 					else
 					{
-						if (count + 3 >= MAX_INDEX)
-							goto noindices;
 						index[count++] = pt1 + i;
 						index[count++] = pt2 - 1 + i;
 						index[count++] = pt2 + i;
@@ -2753,8 +2756,6 @@ S32 *LLVolume::getTriangleIndices(U32 &num_indices) const
 
 					if (use_tri1a2)
 					{
-						if (count + 3 >= MAX_INDEX)
-							goto noindices;
 						index[count++] = pt1;
 						index[count++] = pt2;
 						index[count++] = pt1 + 1;
@@ -2762,8 +2763,6 @@ S32 *LLVolume::getTriangleIndices(U32 &num_indices) const
 					}
 					else
 					{
-						if (count + 3 >= MAX_INDEX)
-							goto noindices;
 						index[count++] = pt1;
 						index[count++] = pt2;
 						index[count++] = pt2 - 1;
@@ -2776,9 +2775,6 @@ S32 *LLVolume::getTriangleIndices(U32 &num_indices) const
 		{
 			// Open solid
 
-			if ( (size_t - 1) * (((size_s -1) * 6) + 6) >= MAX_INDEX)
-				goto noindices;
-
 			for (t = 0; t < size_t - 1; t++)
 			{
 				// Outer face + 1 cut face
@@ -2808,8 +2804,6 @@ S32 *LLVolume::getTriangleIndices(U32 &num_indices) const
 			// Do the top and bottom caps, if necessary
 			if (path_open)
 			{
-				if ( count + (size_s - 2) * 3 >= MAX_INDEX)
-					goto noindices;
 				for (s = 0; s < size_s - 2; s++)
 				{
 					index[count++] = s+1;
@@ -2819,8 +2813,6 @@ S32 *LLVolume::getTriangleIndices(U32 &num_indices) const
 
 				// We've got a top cap
 				S32 offset = (size_t - 1)*size_s;
-				if ( count + (size_s - 2) * 3 >= MAX_INDEX)
-					goto noindices;
 				for (s = 0; s < size_s - 2; s++)
 				{
 					// Inverted ordering from bottom cap.
@@ -2836,8 +2828,6 @@ S32 *LLVolume::getTriangleIndices(U32 &num_indices) const
 		// Closed hollow
 		// Outer face
 		
-		if ( (size_t - 1) * (size_s_out - 1) * 6 >= MAX_INDEX)
-			goto noindices;
 		for (t = 0; t < size_t - 1; t++)
 		{
 			for (s = 0; s < size_s_out - 1; s++)
@@ -2856,8 +2846,6 @@ S32 *LLVolume::getTriangleIndices(U32 &num_indices) const
 
 		// Inner face
 		// Invert facing from outer face
-		if ( count + (size_t - 1) * ((size_s - 1) - size_s_out) * 6 >= MAX_INDEX)
-			goto noindices;
 		for (t = 0; t < size_t - 1; t++)
 		{
 			for (s = size_s_out; s < size_s - 1; s++)
@@ -2962,8 +2950,6 @@ S32 *LLVolume::getTriangleIndices(U32 &num_indices) const
 
 				if (use_tri1a2)
 				{
-					if (count + 3 >= MAX_INDEX)
-						goto noindices;
 					index[count++] = pt1 + i;
 					index[count++] = pt1 + 1 + i;
 					index[count++] = pt2 + i;
@@ -2971,8 +2957,6 @@ S32 *LLVolume::getTriangleIndices(U32 &num_indices) const
 				}
 				else
 				{
-					if (count + 3 >= MAX_INDEX)
-						goto noindices;
 					index[count++] = pt1 + i;
 					index[count++] = pt2 - 1 + i;
 					index[count++] = pt2 + i;
@@ -3063,8 +3047,6 @@ S32 *LLVolume::getTriangleIndices(U32 &num_indices) const
 
 				if (use_tri1a2)
 				{
-					if (count + 3 >= MAX_INDEX)
-						goto noindices;
 					index[count++] = pt1;
 					index[count++] = pt2;
 					index[count++] = pt1 + 1;
@@ -3072,8 +3054,6 @@ S32 *LLVolume::getTriangleIndices(U32 &num_indices) const
 				}
 				else
 				{
-					if (count + 3 >= MAX_INDEX)
-						goto noindices;
 					index[count++] = pt1;
 					index[count++] = pt2;
 					index[count++] = pt2 - 1;
@@ -3085,8 +3065,6 @@ S32 *LLVolume::getTriangleIndices(U32 &num_indices) const
 	else
 	{
 		// Closed solid.  Easy case.
-		if ( (size_t - 1) * (size_s - 1) * 6 > MAX_INDEX)
-			goto noindices;
 		for (t = 0; t < size_t - 1; t++)
 		{
 			for (s = 0; s < size_s - 1; s++)
@@ -3108,8 +3086,6 @@ S32 *LLVolume::getTriangleIndices(U32 &num_indices) const
 		if (path_open)
 		{
 			// bottom cap
-			if ( count + (size_s - 2 - 1) * 3 >= MAX_INDEX)
-				goto noindices;
 			for (s = 1; s < size_s - 2; s++)
 			{
 				index[count++] = s+1;
@@ -3119,8 +3095,6 @@ S32 *LLVolume::getTriangleIndices(U32 &num_indices) const
 
 			// top cap
 			S32 offset = (size_t - 1)*size_s;
-			if ( count + (size_s - 2 - 1) * 3 >= MAX_INDEX)
-				goto noindices;
 			for (s = 1; s < size_s - 2; s++)
 			{
 				// Inverted ordering from bottom cap.
@@ -3131,7 +3105,18 @@ S32 *LLVolume::getTriangleIndices(U32 &num_indices) const
 		}
 	}
 
+#ifdef LL_DEBUG
+	// assert that we computed the correct number of indices
+	if (count != expected_num_triangle_indices )
+	{
+		llerrs << "bad index count prediciton:"
+			<< "  expected=" << expected_num_triangle_indices 
+			<< " actual=" << count << llendl;
+	}
+#endif
+
 #if 0
+	// verify that each index does not point beyond the size of the mesh
 	S32 num_vertices = mMesh.size();
 	for (i = 0; i < count; i+=3)
 	{
@@ -3142,17 +3127,65 @@ S32 *LLVolume::getTriangleIndices(U32 &num_indices) const
 	}
 #endif
 
-	indices = new S32[count];
-noindices:
-	if (!indices)
+	num_indices = count;
+	return index;
+}
+
+S32 LLVolume::getNumTriangleIndices() const
+{
+	BOOL profile_open = getProfile().isOpen();
+	BOOL hollow = getProfile().isHollow();
+	BOOL path_open = getPath().isOpen();
+
+	S32 size_s, size_s_out, size_t;
+	size_s = getProfile().getTotal();
+	size_s_out = getProfile().getTotalOut();
+	size_t = getPath().mPath.size();
+
+	S32 count = 0;
+	if (profile_open)		/* Flawfinder: ignore */
 	{
-		llwarns << "Couldn't allocate triangle indices" << llendl;
-		num_indices = 0;
-		return NULL;
+		if (hollow)
+		{
+			// Open hollow -- much like the closed solid, except we 
+			// we need to stitch up the gap between s=0 and s=size_s-1
+			count = (size_t - 1) * (((size_s -1) * 6) + 6);
+		}
+		else
+		{
+			count = (size_t - 1) * (((size_s -1) * 6) + 6); 
+		}
 	}
-	num_indices = count;
-	memcpy(indices, index, count * sizeof(S32));		/* Flawfinder: ignore */
-	return indices;
+	else if (hollow)
+	{
+		// Closed hollow
+		// Outer face
+		count = (size_t - 1) * (size_s_out - 1) * 6;
+
+		// Inner face
+		count += (size_t - 1) * ((size_s - 1) - size_s_out) * 6;
+	}
+	else
+	{
+		// Closed solid.  Easy case.
+		count = (size_t - 1) * (size_s - 1) * 6;
+	}
+
+	if (path_open)
+	{
+		S32 cap_triangle_count = size_s - 3;
+		if ( profile_open
+			|| hollow )
+		{
+			cap_triangle_count = size_s - 2;
+		}
+		if ( cap_triangle_count > 0 )
+		{
+			// top and bottom caps
+			count += cap_triangle_count * 2 * 3;
+		}
+	}
+	return count;
 }
 
 //-----------------------------------------------------------------------------
@@ -3483,7 +3516,7 @@ struct lessTriangle
 
 BOOL equalTriangle(const S32 *a, const S32 *b)
 {
-	if ((*a == *b) && (*(a+1) == *(b+1)) && ((*a+2) == (*b+2)))
+	if ((*a == *b) && (*(a+1) == *(b+1)) && (*(a+2) == *(b+2)))
 	{
 		return TRUE;
 	}
@@ -3499,6 +3532,21 @@ BOOL LLVolume::cleanupTriangleData( const S32 num_input_vertices,
 									S32 &num_output_triangles,
 									S32 **output_triangles)
 {
+	/* Testing: avoid any cleanup
+	num_output_vertices = num_input_vertices;
+	num_output_triangles = num_input_triangles;
+
+	*output_vertices = new LLVector3[num_input_vertices];
+	for (S32 i = 0; i < num_input_vertices; i++)
+	{
+		(*output_vertices)[i] = input_vertices[i].mPos;
+	}
+
+	*output_triangles = new S32[num_input_triangles*3];
+	memcpy(*output_triangles, input_triangles, 3*num_input_triangles*sizeof(S32));		// Flawfinder: ignore
+	return TRUE;
+	*/
+
 	// Here's how we do this:
 	// Create a structure which contains the original vertex index and the
 	// LLVector3 data.
@@ -3549,7 +3597,7 @@ BOOL LLVolume::cleanupTriangleData( const S32 num_input_vertices,
 		}
 		else
 		{
-			//llinfos << "Removed duplicate vertex " << pairp->mVertex << llendl;
+			//llinfos << "Removed duplicate vertex " << pairp->mVertex << ", distance magVecSquared() is " << (pairp->mVertex - prev_pairp->mVertex).magVecSquared() << llendl;
 		}
 		vertex_mapping[pairp->mIndex] = new_num_vertices - 1;
 	}
@@ -3561,50 +3609,54 @@ BOOL LLVolume::cleanupTriangleData( const S32 num_input_vertices,
 
 	for (i = 0; i < num_input_triangles; i++)
 	{
-		//llinfos << "Checking triangle " << input_triangles[i*3] << ":" << input_triangles[i*3+1] << ":" << input_triangles[i*3+2] << llendl;
-		input_triangles[i*3] = vertex_mapping[input_triangles[i*3]];
-		input_triangles[i*3+1] = vertex_mapping[input_triangles[i*3+1]];
-		input_triangles[i*3+2] = vertex_mapping[input_triangles[i*3+2]];
+		S32 v1 = i*3;
+		S32 v2 = i*3 + 1;
+		S32 v3 = i*3 + 2;
+
+		//llinfos << "Checking triangle " << input_triangles[v1] << ":" << input_triangles[v2] << ":" << input_triangles[v3] << llendl;
+		input_triangles[v1] = vertex_mapping[input_triangles[v1]];
+		input_triangles[v2] = vertex_mapping[input_triangles[v2]];
+		input_triangles[v3] = vertex_mapping[input_triangles[v3]];
 
-		if ((input_triangles[i*3] == input_triangles[i*3+1])
-			|| (input_triangles[i*3] == input_triangles[i*3+2])
-			|| (input_triangles[i*3+1] == input_triangles[i*3+2]))
+		if ((input_triangles[v1] == input_triangles[v2])
+			|| (input_triangles[v1] == input_triangles[v3])
+			|| (input_triangles[v2] == input_triangles[v3]))
 		{
-			//llinfos << "Removing degenerate triangle " << input_triangles[i*3] << ":" << input_triangles[i*3+1] << ":" << input_triangles[i*3+2] << llendl;
+			//llinfos << "Removing degenerate triangle " << input_triangles[v1] << ":" << input_triangles[v2] << ":" << input_triangles[v3] << llendl;
 			// Degenerate triangle, skip
 			continue;
 		}
 
-		if (input_triangles[i*3] < input_triangles[i*3+1])
+		if (input_triangles[v1] < input_triangles[v2])
 		{
-			if (input_triangles[i*3] < input_triangles[i*3+2])
+			if (input_triangles[v1] < input_triangles[v3])
 			{
 				// (0 < 1) && (0 < 2)
-				new_triangles[new_num_triangles*3] = input_triangles[i*3];
-				new_triangles[new_num_triangles*3+1] = input_triangles[i*3+1];
-				new_triangles[new_num_triangles*3+2] = input_triangles[i*3+2];
+				new_triangles[new_num_triangles*3] = input_triangles[v1];
+				new_triangles[new_num_triangles*3+1] = input_triangles[v2];
+				new_triangles[new_num_triangles*3+2] = input_triangles[v3];
 			}
 			else
 			{
 				// (0 < 1) && (2 < 0)
-				new_triangles[new_num_triangles*3] = input_triangles[i*3+2];
-				new_triangles[new_num_triangles*3+1] = input_triangles[i*3];
-				new_triangles[new_num_triangles*3+2] = input_triangles[i*3+1];
+				new_triangles[new_num_triangles*3] = input_triangles[v3];
+				new_triangles[new_num_triangles*3+1] = input_triangles[v1];
+				new_triangles[new_num_triangles*3+2] = input_triangles[v2];
 			}
 		}
-		else if (input_triangles[i*3+1] < input_triangles[i*3+2])
+		else if (input_triangles[v2] < input_triangles[v3])
 		{
 			// (1 < 0) && (1 < 2)
-			new_triangles[new_num_triangles*3] = input_triangles[i*3+1];
-			new_triangles[new_num_triangles*3+1] = input_triangles[i*3+2];
-			new_triangles[new_num_triangles*3+2] = input_triangles[i*3];
+			new_triangles[new_num_triangles*3] = input_triangles[v2];
+			new_triangles[new_num_triangles*3+1] = input_triangles[v3];
+			new_triangles[new_num_triangles*3+2] = input_triangles[v1];
 		}
 		else
 		{
 			// (1 < 0) && (2 < 1)
-			new_triangles[new_num_triangles*3] = input_triangles[i*3+2];
-			new_triangles[new_num_triangles*3+1] = input_triangles[i*3];
-			new_triangles[new_num_triangles*3+2] = input_triangles[i*3+1];
+			new_triangles[new_num_triangles*3] = input_triangles[v3];
+			new_triangles[new_num_triangles*3+1] = input_triangles[v1];
+			new_triangles[new_num_triangles*3+2] = input_triangles[v2];
 		}
 		new_num_triangles++;
 	}
@@ -3845,23 +3897,44 @@ void LLVolumeParams::reduceT(F32 begin, F32 end)
 	mPathParams.setEnd(a + end * (b - a));
 }
 
+const F32 MIN_CONCAVE_PROFILE_WEDGE = 0.125f;	// 1/8 unity
+const F32 MIN_CONCAVE_PATH_WEDGE = 0.111111f;	// 1/9 unity
+
+// returns TRUE if the shape can be approximated with a convex shape 
+// for collison purposes
 BOOL LLVolumeParams::isConvex() const
 {
-	// The logic for determining convexity is a little convoluted.
+	F32 path_length = mPathParams.getEnd() - mPathParams.getBegin();
 	 
-	// Do we need to take getTwistBegin into account?  DK 08/12/04
-	if (   mProfileParams.getHollow() != 0.0f 
-		|| mPathParams.getTwist() != mPathParams.getTwistBegin() )
+	if ( mPathParams.getTwist() != mPathParams.getTwistBegin()
+		&& path_length > MIN_CONCAVE_PATH_WEDGE )
 	{
-		// hollow or twist gaurantees concavity
+		// twist along a "not too short" path is concave
 		return FALSE;
 	}
 
 	F32 profile_length = mProfileParams.getEnd() - mProfileParams.getBegin();
-	BOOL concave_profile = (profile_length < 1.0f) && (profile_length > 0.5f);
-	if (concave_profile)
+	F32 hollow = mProfileParams.getHollow();
+	BOOL same_hole = hollow == 0.f 
+					 || (mProfileParams.getCurveType() & LL_PCODE_HOLE_MASK) == LL_PCODE_HOLE_SAME;
+
+	F32 min_profile_wedge = MIN_CONCAVE_PROFILE_WEDGE;
+	U8 profile_type = mProfileParams.getCurveType() & LL_PCODE_PROFILE_MASK;
+	if ( LL_PCODE_PROFILE_CIRCLE_HALF == profile_type )
 	{
-		// concave profile
+		// it is a sphere and spheres get twice the minimum profile wedge
+		min_profile_wedge = 2.f * MIN_CONCAVE_PROFILE_WEDGE;
+	}
+
+	BOOL convex_profile = ( ( profile_length == 1.f
+						     || profile_length <= 0.5f )
+						   && hollow == 0.f )						// trivially convex
+						  || ( profile_length <= min_profile_wedge
+							  && same_hole );						// effectvely convex (even when hollow)
+
+	if (!convex_profile)
+	{
+		// profile is concave
 		return FALSE;
 	}
 
@@ -3872,7 +3945,6 @@ BOOL LLVolumeParams::isConvex() const
 		return TRUE;
 	}
 
-	F32 path_length = mPathParams.getEnd() - mPathParams.getBegin();
 	BOOL concave_path = (path_length < 1.0f) && (path_length > 0.5f);
 	if (concave_path)
 	{
@@ -3880,17 +3952,43 @@ BOOL LLVolumeParams::isConvex() const
 	}
 
 	// we're left with spheres, toroids and tubes
-	// only the spheres can be convex
-	U8 profile_type = mProfileParams.getCurveType() & LL_PCODE_PROFILE_MASK;
 	if ( LL_PCODE_PROFILE_CIRCLE_HALF == profile_type )
 	{
+		// at this stage all spheres must be convex
 		return TRUE;
 	}
 
 	// it's a toroid or tube		
+	if ( path_length <= MIN_CONCAVE_PATH_WEDGE )
+	{
+		// effectively convex
+		return TRUE;
+	}
+
 	return FALSE;
 }
 
+// debug
+void LLVolumeParams::setCube()
+{
+	mProfileParams.setCurveType(LL_PCODE_PROFILE_SQUARE);
+	mProfileParams.setBegin(0.f);
+	mProfileParams.setEnd(1.f);
+	mProfileParams.setHollow(0.f);
+
+	mPathParams.setBegin(0.f);
+	mPathParams.setEnd(1.f);
+	mPathParams.setScale(1.f, 1.f);
+	mPathParams.setShear(0.f, 0.f);
+	mPathParams.setCurveType(LL_PCODE_PATH_LINE);
+	mPathParams.setTwistBegin(0.f);
+	mPathParams.setTwistEnd(0.f);
+	mPathParams.setRadiusOffset(0.f);
+	mPathParams.setTaper(0.f, 0.f);
+	mPathParams.setRevolutions(0.f);
+	mPathParams.setSkew(0.f);
+}
+
 LLFaceID LLVolume::generateFaceMask()
 {
 	LLFaceID new_mask = 0x0000;
diff --git a/indra/llmath/llvolume.h b/indra/llmath/llvolume.h
index 9af02d26292e950b6c74877ad732c88662a6c681..a1eba9de389e13089117c82ed0031578b87a1626 100644
--- a/indra/llmath/llvolume.h
+++ b/indra/llmath/llvolume.h
@@ -72,6 +72,8 @@ const F32 TAPER_QUANTA  = 0.01f;
 const F32 REV_QUANTA    = 0.015f;
 const F32 HOLLOW_QUANTA = 0.00002f;
 
+const S32 MAX_VOLUME_TRIANGLE_INDICES = 10000;
+
 //============================================================================
 
 // useful masks
@@ -187,10 +189,10 @@ class LLProfileParams
 public:
 	LLProfileParams()
 	{
-		mBegin     = 0;
-		mEnd       = 1;
-		mHollow    = 0;
 		mCurveType = LL_PCODE_PROFILE_SQUARE;
+		mBegin     = 0.f;
+		mEnd       = 1.f;
+		mHollow    = 0.f;
 	}
 
 	LLProfileParams(U8 curve, F32 begin, F32 end, F32 hollow)
@@ -307,17 +309,17 @@ class LLPathParams
 public:
 	LLPathParams()
 	{
-		mBegin     = 0;
-		mEnd       = 1;
-		mScale.setVec(1,1);
-		mShear.setVec(0,0);
+		mBegin     = 0.f;
+		mEnd       = 1.f;
+		mScale.setVec(1.f,1.f);
+		mShear.setVec(0.f,0.f);
 		mCurveType = LL_PCODE_PATH_LINE;
-		mTwistBegin		= 0;
-		mTwistEnd     	= 0;
-		mRadiusOffset	= 0;
-		mTaper.setVec(0,0);
-		mRevolutions	= 1;
-		mSkew			= 0;
+		mTwistBegin		= 0.f;
+		mTwistEnd     	= 0.f;
+		mRadiusOffset	= 0.f;
+		mTaper.setVec(0.f,0.f);
+		mRevolutions	= 1.f;
+		mSkew			= 0.f;
 	}
 
 	LLPathParams(U8 curve, F32 begin, F32 end, F32 scx, F32 scy, F32 shx, F32 shy, F32 twistend, F32 twistbegin, F32 radiusoffset, F32 tx, F32 ty, F32 revolutions, F32 skew)
@@ -627,6 +629,9 @@ class LLVolumeParams
 	
 	friend std::ostream& operator<<(std::ostream &s, const LLVolumeParams &volume_params);
 
+	// debug helper functions
+	void setCube();
+
 protected:
 	LLProfileParams mProfileParams;
 	LLPathParams	mPathParams;
@@ -869,6 +874,10 @@ class LLVolume : public LLRefCount
 	S32 getSculptLevel() const                              { return mSculptLevel; }
 	
 	S32 *getTriangleIndices(U32 &num_indices) const;
+
+	// returns number of triangle indeces required for path/profile mesh
+	S32 getNumTriangleIndices() const;
+
 	void generateSilhouetteVertices(std::vector<LLVector3> &vertices, std::vector<LLVector3> &normals, std::vector<S32> &segments, const LLVector3& view_vec,
 											  const LLMatrix4& mat,
 										  const LLMatrix3& norm_mat);
diff --git a/indra/llmath/llvolumemgr.cpp b/indra/llmath/llvolumemgr.cpp
index 1a448ed8e0cd0a488534c816a7d38c2b2e6bdebe..f3a6b7d157aef24ba9ed6ba801f8ca9a28582537 100644
--- a/indra/llmath/llvolumemgr.cpp
+++ b/indra/llmath/llvolumemgr.cpp
@@ -36,7 +36,7 @@
 
 //#define DEBUG_VOLUME
 
-LLVolumeMgr* gVolumeMgr = 0;
+//LLVolumeMgr* gVolumeMgr = 0;
 
 const F32 BASE_THRESHOLD = 0.03f;
 
@@ -49,37 +49,23 @@ F32 LLVolumeLODGroup::mDetailThresholds[NUM_LODS] = {BASE_THRESHOLD,
 //static
 F32 LLVolumeLODGroup::mDetailScales[NUM_LODS] = {1.f, 1.5f, 2.5f, 4.f};
 
-//============================================================================
-//static
-void LLVolumeMgr::initClass()
-{
-	gVolumeMgr = new LLVolumeMgr();
-}
-
-//static
-BOOL LLVolumeMgr::cleanupClass()
-{
-	BOOL res = FALSE;
-	if (gVolumeMgr) {
-		res = gVolumeMgr->cleanup();
-		delete gVolumeMgr;
-		gVolumeMgr = 0;
-	}
-	return res;
-}
 
 //============================================================================
 
 LLVolumeMgr::LLVolumeMgr()
+:	mDataMutex(NULL)
 {
-	mDataMutex = new LLMutex(gAPRPoolp);
-//	mNumVolumes = 0;
+	// the LLMutex magic interferes with easy unit testing,
+	// so you now must manually call useMutex() to use it
+	//mDataMutex = new LLMutex(gAPRPoolp);
 }
 
 LLVolumeMgr::~LLVolumeMgr()
 {
 	cleanup();
+
 	delete mDataMutex;
+	mDataMutex = NULL;
 }
 
 BOOL LLVolumeMgr::cleanup()
@@ -90,7 +76,10 @@ BOOL LLVolumeMgr::cleanup()
 	}
 	#endif
 	BOOL no_refs = TRUE;
-	mDataMutex->lock();
+	if (mDataMutex)
+	{
+		mDataMutex->lock();
+	}
 	for (volume_lod_group_map_t::iterator iter = mVolumeLODGroups.begin(),
 			 end = mVolumeLODGroups.end();
 		 iter != end; iter++)
@@ -106,29 +95,37 @@ BOOL LLVolumeMgr::cleanup()
  		volgroupp->unref();// this );
 	}
 	mVolumeLODGroups.clear();
-	mDataMutex->unlock();
+	if (mDataMutex)
+	{
+		mDataMutex->unlock();
+	}
 	return no_refs;
 }
 
+// whatever calls getVolume() never owns the LLVolume* and
+// cannot keep references for long since it may be deleted
+// later.  For best results hold it in an LLPointer<LLVolume>.
 LLVolume *LLVolumeMgr::getVolume(const LLVolumeParams &volume_params, const S32 detail)
 {
 	LLVolumeLODGroup* volgroupp;
-	mDataMutex->lock();
+	if (mDataMutex)
+	{
+		mDataMutex->lock();
+	}
 	volume_lod_group_map_t::iterator iter = mVolumeLODGroups.find(&volume_params);
 	if( iter == mVolumeLODGroups.end() )
 	{
-		volgroupp = new LLVolumeLODGroup(volume_params);
-		const LLVolumeParams* params = &(volgroupp->getParams());
-		mVolumeLODGroups[params] = volgroupp;
- 		volgroupp->ref(); // initial reference
+		volgroupp = createNewGroup(volume_params);
 	}
 	else
 	{
 		volgroupp = iter->second;
 	}
-	volgroupp->ref();// this );
-	mDataMutex->unlock();
-	//	mNumVolumes++;
+	volgroupp->ref();
+	if (mDataMutex)
+	{
+		mDataMutex->unlock();
+	}
 	#ifdef DEBUG_VOLUME
 	{
 		lldebugs << "LLVolumeMgr::getVolume() " << (*this) << llendl;
@@ -137,6 +134,27 @@ LLVolume *LLVolumeMgr::getVolume(const LLVolumeParams &volume_params, const S32
 	return volgroupp->getLOD(detail);
 }
 
+// virtual
+LLVolumeLODGroup* LLVolumeMgr::getGroup( const LLVolumeParams& volume_params ) const
+{
+	LLVolumeLODGroup* volgroupp = NULL;
+	if (mDataMutex)
+	{
+		mDataMutex->lock();
+	}
+	volume_lod_group_map_t::const_iterator iter = mVolumeLODGroups.find(&volume_params);
+	if( iter != mVolumeLODGroups.end() )
+	{
+		volgroupp = iter->second;
+	}
+	if (mDataMutex)
+	{
+		mDataMutex->unlock();
+	}
+	return volgroupp;
+}
+
+// virtual
 void LLVolumeMgr::cleanupVolume(LLVolume *volumep)
 {
 	if (volumep->isUnique())
@@ -145,12 +163,18 @@ void LLVolumeMgr::cleanupVolume(LLVolume *volumep)
 		return;
 	}
 	LLVolumeParams* params = (LLVolumeParams*) &(volumep->getParams());
-	mDataMutex->lock();
+	if (mDataMutex)
+	{
+		mDataMutex->lock();
+	}
 	volume_lod_group_map_t::iterator iter = mVolumeLODGroups.find(params);
 	if( iter == mVolumeLODGroups.end() )
 	{
 		llerrs << "Warning! Tried to cleanup unknown volume type! " << *params << llendl;
-		mDataMutex->unlock();
+		if (mDataMutex)
+		{
+			mDataMutex->unlock();
+		}
 		return;
 	}
 	else
@@ -164,9 +188,11 @@ void LLVolumeMgr::cleanupVolume(LLVolume *volumep)
 			mVolumeLODGroups.erase(params);
 			volgroupp->unref();// this );
 		}
-		//	mNumVolumes--;
 	}
-	mDataMutex->unlock();
+	if (mDataMutex)
+	{
+		mDataMutex->unlock();
+	}
 
 	#ifdef DEBUG_VOLUME
 	{
@@ -175,10 +201,43 @@ void LLVolumeMgr::cleanupVolume(LLVolume *volumep)
 	#endif
 }
 
+#ifdef DEBUG_VOLUME
+S32 LLVolumeMgr::getTotalRefCount() const
+{
+	S32 total_ref_count = 0;
+	for ( volume_lod_group_map_t::const_iterator iter = mVolumeLODGroups.begin(),
+			 end = mVolumeLODGroups.end();
+		 iter != end; iter++)
+	{
+		total_ref_count += iter->second->getTotalVolumeRefCount();
+	}
+	return total_ref_count;
+}
+
+S32 LLVolumeMgr::getGroupCount() const
+{
+	return mVolumeLODGroups.size();
+}
+#endif
+
+// protected
+LLVolumeLODGroup* LLVolumeMgr::createNewGroup(const LLVolumeParams& volume_params)
+{
+	LLVolumeLODGroup* group = new LLVolumeLODGroup(volume_params);
+	const LLVolumeParams* params = &(group->getParams());
+	mVolumeLODGroups[params] = group;
+ 	group->ref(); // initial reference
+	return group;
+}
+
+// virtual
 void LLVolumeMgr::dump()
 {
 	F32 avg = 0.f;
-	mDataMutex->lock();
+	if (mDataMutex)
+	{
+		mDataMutex->lock();
+	}
 	for (volume_lod_group_map_t::iterator iter = mVolumeLODGroups.begin(),
 			 end = mVolumeLODGroups.end();
 		 iter != end; iter++)
@@ -188,16 +247,30 @@ void LLVolumeMgr::dump()
 	}
 	int count = (int)mVolumeLODGroups.size();
 	avg = count ? avg / (F32)count : 0.0f;
-	mDataMutex->unlock();
+	if (mDataMutex)
+	{
+		mDataMutex->unlock();
+	}
 	llinfos << "Average usage of LODs " << avg << llendl;
 }
 
+void LLVolumeMgr::useMutex()
+{ 
+	if (!mDataMutex)
+	{
+		mDataMutex = new LLMutex(gAPRPoolp);
+	}
+}
+
 std::ostream& operator<<(std::ostream& s, const LLVolumeMgr& volume_mgr)
 {
 	s << "{ numLODgroups=" << volume_mgr.mVolumeLODGroups.size() << ", ";
 
 	S32 total_refs = 0;
-	volume_mgr.mDataMutex->lock();
+	if (volume_mgr.mDataMutex)
+	{
+		volume_mgr.mDataMutex->lock();
+	}
 
 	LLVolumeMgr::volume_lod_group_map_iter iter = volume_mgr.mVolumeLODGroups.begin();
 	LLVolumeMgr::volume_lod_group_map_iter end  = volume_mgr.mVolumeLODGroups.end();
@@ -208,7 +281,10 @@ std::ostream& operator<<(std::ostream& s, const LLVolumeMgr& volume_mgr)
 		s << ", " << (*volgroupp);
 	}
 
-	volume_mgr.mDataMutex->unlock();
+	if (volume_mgr.mDataMutex)
+	{
+		volume_mgr.mDataMutex->unlock();
+	}
 
 	s << ", total_refs=" << total_refs << " }";
 	return s;
@@ -222,15 +298,39 @@ LLVolumeLODGroup::LLVolumeLODGroup(const LLVolumeParams &params)
 	for (i = 0; i < NUM_LODS; i++)
 	{
 		mLODRefs[i] = 0;
-		mVolumeLODs[i] = NULL;
+		// no need to initialize mVolumeLODs, they are smart pointers
+		//mVolumeLODs[i] = NULL;
 		mAccessCount[i] = 0;
 	}
 }
 
+#ifdef DEBUG_VOLUME
+S32 LLVolumeLODGroup::getTotalVolumeRefCount() const
+{
+	S32 total_ref_count = 0;
+	for (S32 i = 0; i < NUM_LODS; i++)
+	{
+		total_ref_count += mLODRefs[i];
+	}
+	return total_ref_count;
+}
+#endif
+
+// protected
 LLVolumeLODGroup::~LLVolumeLODGroup()
 {
+	destroy();
 }
 
+// protected
+void LLVolumeLODGroup::destroy()
+{
+	for (S32 i = 0; i < NUM_LODS; i++)
+	{
+		// remember that mVolumeLODs are smart pointers!
+		mVolumeLODs[i] = NULL;
+	}
+}
 
 LLVolume * LLVolumeLODGroup::getLOD(const S32 detail)
 {
@@ -242,7 +342,7 @@ LLVolume * LLVolumeLODGroup::getLOD(const S32 detail)
 		mVolumeLODs[detail] = new LLVolume(mParams, mDetailScales[detail]);
 	}
 	mLODRefs[detail]++;
-	return mVolumeLODs[detail];
+	return mVolumeLODs[detail].get();
 }
 
 BOOL LLVolumeLODGroup::derefLOD(LLVolume *volumep)
diff --git a/indra/llmath/llvolumemgr.h b/indra/llmath/llvolumemgr.h
index f3d4b5ee6b6237978f92465bcd6d8719e4448223..c28ffce6311e50506edcaaf9ec847114d907eda5 100644
--- a/indra/llmath/llvolumemgr.h
+++ b/indra/llmath/llvolumemgr.h
@@ -43,9 +43,6 @@ class LLVolumeLODGroup;
 
 class LLVolumeLODGroup : public LLThreadSafeRefCount
 {
-protected:
-	~LLVolumeLODGroup();
-	
 public:
 	enum
 	{
@@ -60,11 +57,19 @@ class LLVolumeLODGroup : public LLThreadSafeRefCount
 	static F32 getVolumeScaleFromDetail(const S32 detail);
 
 	LLVolume *getLOD(const S32 detail);
-	const LLVolumeParams &getParams() const { return mParams; };
+	const LLVolumeParams& getParams() const { return mParams; };
 
 	F32	dump();
 	friend std::ostream& operator<<(std::ostream& s, const LLVolumeLODGroup& volgroup);
 
+#ifdef DEBUG_VOLUME
+	S32 getTotalVolumeRefCount() const;
+#endif
+
+protected:
+	virtual ~LLVolumeLODGroup();
+	void destroy();
+	
 protected:
 	LLVolumeParams mParams;
 
@@ -77,30 +82,50 @@ class LLVolumeLODGroup : public LLThreadSafeRefCount
 
 class LLVolumeMgr
 {
-public:
-	static void initClass();
-	static BOOL cleanupClass();
+//public:
+//	static void initClass();
+//	static BOOL cleanupClass();
 	
 public:
 	LLVolumeMgr();
-	~LLVolumeMgr();
+	virtual ~LLVolumeMgr();
 	BOOL cleanup();			// Cleanup all volumes being managed, returns TRUE if no dangling references
+
+	virtual LLVolumeLODGroup* getGroup( const LLVolumeParams& volume_params ) const;
+
+	// whatever calls getVolume() never owns the LLVolume* and
+	// cannot keep references for long since it may be deleted
+	// later.  For best results hold it in an LLPointer<LLVolume>.
 	LLVolume *getVolume(const LLVolumeParams &volume_params, const S32 detail);
+
 	void cleanupVolume(LLVolume *volumep);
 
 	void dump();
+
+	// manually call this for mutex magic
+	void useMutex();
+
+#ifdef DEBUG_VOLUME
+	S32 getTotalRefCount() const;
+	S32 getGroupCount() const;
+#endif
 	friend std::ostream& operator<<(std::ostream& s, const LLVolumeMgr& volume_mgr);
 
+protected:
+	virtual LLVolumeLODGroup* createNewGroup(const LLVolumeParams& volume_params);
+
 protected:
 	typedef std::map<const LLVolumeParams*, LLVolumeLODGroup*, LLVolumeParams::compare> volume_lod_group_map_t;
 	typedef volume_lod_group_map_t::const_iterator volume_lod_group_map_iter;
 	volume_lod_group_map_t mVolumeLODGroups;
 
 	LLMutex* mDataMutex;
-	
-//	S32 mNumVolumes;
+
+	// We need to be able to disable threadsafe checks to prevent 
+	// some unit_tests from blocking on failure
+	bool mThreadSafe;
 };
 
-extern LLVolumeMgr* gVolumeMgr;
+//extern LLVolumeMgr* gVolumeMgr;
 
 #endif // LL_LLVOLUMEMGR_H
diff --git a/indra/llmath/m3math.cpp b/indra/llmath/m3math.cpp
index d4f99cb8c9fd60f6658e30938e3f10c920048747..184b87c000e954df549107b7d21521ff12072437 100644
--- a/indra/llmath/m3math.cpp
+++ b/indra/llmath/m3math.cpp
@@ -136,7 +136,7 @@ void LLMatrix3::getEulerAngles(F32 *roll, F32 *pitch, F32 *yaw) const
 
 // Clear and Assignment Functions
 
-const LLMatrix3&	LLMatrix3::identity()
+const LLMatrix3&	LLMatrix3::setIdentity()
 {
 	mMatrix[0][0] = 1.f;
 	mMatrix[0][1] = 0.f;
@@ -152,7 +152,23 @@ const LLMatrix3&	LLMatrix3::identity()
 	return (*this);
 }
 
-const LLMatrix3&	LLMatrix3::zero()
+const LLMatrix3&	LLMatrix3::clear()
+{
+	mMatrix[0][0] = 0.f;
+	mMatrix[0][1] = 0.f;
+	mMatrix[0][2] = 0.f;
+
+	mMatrix[1][0] = 0.f;
+	mMatrix[1][1] = 0.f;
+	mMatrix[1][2] = 0.f;
+
+	mMatrix[2][0] = 0.f;
+	mMatrix[2][1] = 0.f;
+	mMatrix[2][2] = 0.f;
+	return (*this);
+}
+
+const LLMatrix3&	LLMatrix3::setZero()
 {
 	mMatrix[0][0] = 0.f;
 	mMatrix[0][1] = 0.f;
@@ -190,15 +206,26 @@ F32		LLMatrix3::determinant() const
 		  	mMatrix[0][2] * (mMatrix[1][0] * mMatrix[2][1] - mMatrix[1][1] * mMatrix[2][0]); 
 }
 
-// This is identical to the transMat3() method because we assume a rotation matrix
-const LLMatrix3&	LLMatrix3::invert()
+// inverts this matrix
+void LLMatrix3::invert()
 {
-	// transpose the matrix
-	F32 temp;
-	temp = mMatrix[VX][VY]; mMatrix[VX][VY] = mMatrix[VY][VX]; mMatrix[VY][VX] = temp;
-	temp = mMatrix[VX][VZ]; mMatrix[VX][VZ] = mMatrix[VZ][VX]; mMatrix[VZ][VX] = temp;
-	temp = mMatrix[VY][VZ]; mMatrix[VY][VZ] = mMatrix[VZ][VY]; mMatrix[VZ][VY] = temp;
-	return *this;
+	// fails silently if determinant is zero too small
+	F32 det = determinant();
+	const F32 VERY_SMALL_DETERMINANT = 0.000001f;
+	if (fabs(det) > VERY_SMALL_DETERMINANT)
+	{
+		// invertiable
+		LLMatrix3 t(*this);
+		mMatrix[VX][VX] = ( t.mMatrix[VY][VY] * t.mMatrix[VZ][VZ] - t.mMatrix[VY][VZ] * t.mMatrix[VZ][VY] ) / det;
+		mMatrix[VY][VX] = ( t.mMatrix[VY][VZ] * t.mMatrix[VZ][VX] - t.mMatrix[VY][VX] * t.mMatrix[VZ][VZ] ) / det;
+		mMatrix[VZ][VX] = ( t.mMatrix[VY][VX] * t.mMatrix[VZ][VY] - t.mMatrix[VY][VY] * t.mMatrix[VZ][VX] ) / det;
+		mMatrix[VX][VY] = ( t.mMatrix[VZ][VY] * t.mMatrix[VX][VZ] - t.mMatrix[VZ][VZ] * t.mMatrix[VX][VY] ) / det;
+		mMatrix[VY][VY] = ( t.mMatrix[VZ][VZ] * t.mMatrix[VX][VX] - t.mMatrix[VZ][VX] * t.mMatrix[VX][VZ] ) / det;
+		mMatrix[VZ][VY] = ( t.mMatrix[VZ][VX] * t.mMatrix[VX][VY] - t.mMatrix[VZ][VY] * t.mMatrix[VX][VX] ) / det;
+		mMatrix[VX][VZ] = ( t.mMatrix[VX][VY] * t.mMatrix[VY][VZ] - t.mMatrix[VX][VZ] * t.mMatrix[VY][VY] ) / det;
+		mMatrix[VY][VZ] = ( t.mMatrix[VX][VZ] * t.mMatrix[VY][VX] - t.mMatrix[VX][VX] * t.mMatrix[VY][VZ] ) / det;
+		mMatrix[VZ][VZ] = ( t.mMatrix[VX][VX] * t.mMatrix[VY][VY] - t.mMatrix[VX][VY] * t.mMatrix[VY][VX] ) / det;
+	}
 }
 
 // does not assume a rotation matrix, and does not divide by determinant, assuming results will be renormalized
@@ -351,6 +378,27 @@ const LLMatrix3&	LLMatrix3::setRows(const LLVector3 &fwd, const LLVector3 &left,
 	return *this;
 }
 
+const LLMatrix3& LLMatrix3::setRow( U32 rowIndex, const LLVector3& row )
+{
+	llassert( rowIndex >= 0 && rowIndex < NUM_VALUES_IN_MAT3 );
+
+	mMatrix[rowIndex][0] = row[0];
+	mMatrix[rowIndex][1] = row[1];
+	mMatrix[rowIndex][2] = row[2];
+
+	return *this;
+}
+
+const LLMatrix3& LLMatrix3::setCol( U32 colIndex, const LLVector3& col )
+{
+	llassert( colIndex >= 0 && colIndex < NUM_VALUES_IN_MAT3 );
+
+	mMatrix[0][colIndex] = col[0];
+	mMatrix[1][colIndex] = col[1];
+	mMatrix[2][colIndex] = col[2];
+
+	return *this;
+}
 		
 // Rotate exisitng mMatrix
 const LLMatrix3&	LLMatrix3::rotate(const F32 angle, const F32 x, const F32 y, const F32 z)
@@ -384,6 +432,16 @@ const LLMatrix3&	LLMatrix3::rotate(const LLQuaternion &q)
 	return *this;
 }
 
+void LLMatrix3::add(const LLMatrix3& other_matrix)
+{
+	for (S32 i = 0; i < 3; ++i)
+	{
+		for (S32 j = 0; j < 3; ++j)
+		{
+			mMatrix[i][j] += other_matrix.mMatrix[i][j];
+		}
+	}
+}
 
 LLVector3	LLMatrix3::getFwdRow() const
 {
@@ -536,6 +594,19 @@ const LLMatrix3& operator*=(LLMatrix3 &a, const LLMatrix3 &b)
 	return a;
 }
 
+const LLMatrix3& operator*=(LLMatrix3 &a, F32 scalar )
+{
+	for( U32 i = 0; i < NUM_VALUES_IN_MAT3; ++i )
+	{
+		for( U32 j = 0; j < NUM_VALUES_IN_MAT3; ++j )
+		{
+			a.mMatrix[i][j] *= scalar;
+		}
+	}
+
+	return a;
+}
+
 std::ostream& operator<<(std::ostream& s, const LLMatrix3 &a) 
 {
 	s << "{ " 
diff --git a/indra/llmath/m3math.h b/indra/llmath/m3math.h
index d2848aaf5a43b06bbf3163aff4bd380dd3939e1a..5f37456f51061b91049ef4ed36ff1e5e4c72f052 100644
--- a/indra/llmath/m3math.h
+++ b/indra/llmath/m3math.h
@@ -33,6 +33,7 @@
 #define LL_M3MATH_H
 
 #include "llerror.h"
+#include "stdtypes.h"
 
 class LLVector4;
 class LLVector3;
@@ -76,8 +77,9 @@ class LLMatrix3
 		//
 
 		// various useful matrix functions
-		const LLMatrix3& identity();				// Load identity matrix
-		const LLMatrix3& zero();					// Clears Matrix to zero
+		const LLMatrix3& setIdentity();				// Load identity matrix
+		const LLMatrix3& clear();					// Clears Matrix to zero
+		const LLMatrix3& setZero();					// Clears Matrix to zero
 
 		///////////////////////////
 		//
@@ -91,6 +93,9 @@ class LLMatrix3
 		const LLMatrix3& setRot(const LLQuaternion &q);			// Transform matrix by Euler angles and translating by pos
 
 		const LLMatrix3& setRows(const LLVector3 &x_axis, const LLVector3 &y_axis, const LLVector3 &z_axis);
+		const LLMatrix3& setRow( U32 rowIndex, const LLVector3& row );
+		const LLMatrix3& setCol( U32 colIndex, const LLVector3& col );
+
 		
 		///////////////////////////
 		//
@@ -103,29 +108,31 @@ class LLMatrix3
 		LLVector3 getFwdRow() const;
 		LLVector3 getLeftRow() const;
 		LLVector3 getUpRow() const;
-		F32	 determinant() const;						// Return determinant
+		F32	 determinant() const;			// Return determinant
 
 
 		///////////////////////////
 		//
 		// Operations on an existing matrix
 		//
-		const LLMatrix3& transpose();					// Transpose MAT4
-		const LLMatrix3& invert();					// Invert MAT4
-		const LLMatrix3& orthogonalize();						// Orthogonalizes X, then Y, then Z
-		const LLMatrix3& adjointTranspose();		// returns transpose of matrix adjoint, for multiplying normals
+		const LLMatrix3& transpose();		// Transpose MAT4
+		const LLMatrix3& orthogonalize();	// Orthogonalizes X, then Y, then Z
+		void invert();			// Invert MAT4
+		const LLMatrix3& adjointTranspose();// returns transpose of matrix adjoint, for multiplying normals
 
 		
 		// Rotate existing matrix  
 		// Note: the two lines below are equivalent:
 		//	foo.rotate(bar) 
 		//	foo = foo * bar
-		// That is, foo.rotMat3(bar) multiplies foo by bar FROM THE RIGHT
+		// That is, foo.rotate(bar) multiplies foo by bar FROM THE RIGHT
 		const LLMatrix3& rotate(const F32 angle, const F32 x, const F32 y, const F32 z); 	// Rotate matrix by rotating angle radians about (x, y, z)
 		const LLMatrix3& rotate(const F32 angle, const LLVector3 &vec);						// Rotate matrix by rotating angle radians about vec
 		const LLMatrix3& rotate(const F32 roll, const F32 pitch, const F32 yaw); 			// Rotate matrix by roll (about x), pitch (about y), and yaw (about z)
 		const LLMatrix3& rotate(const LLQuaternion &q);			// Transform matrix by Euler angles and translating by pos
 
+		void add(const LLMatrix3& other_matrix);	// add other_matrix to this one
+
 // This operator is misleading as to operation direction
 //		friend LLVector3 operator*(const LLMatrix3 &a, const LLVector3 &b);			// Apply rotation a to vector b
 
@@ -137,6 +144,7 @@ class LLMatrix3
 		friend bool operator!=(const LLMatrix3 &a, const LLMatrix3 &b);				// Return a != b
 
 		friend const LLMatrix3& operator*=(LLMatrix3 &a, const LLMatrix3 &b);				// Return a * b
+		friend const LLMatrix3& operator*=(LLMatrix3 &a, F32 scalar );						// Return a * scalar
 
 		friend std::ostream&	 operator<<(std::ostream& s, const LLMatrix3 &a);	// Stream a
 };
diff --git a/indra/llmath/m4math.cpp b/indra/llmath/m4math.cpp
index 4e7cf847dc605999e6af7cca267e6a05067fff74..836b3178d5165a788f52b0529c2754cf4e5eca99 100644
--- a/indra/llmath/m4math.cpp
+++ b/indra/llmath/m4math.cpp
@@ -163,7 +163,7 @@ LLMatrix4::~LLMatrix4(void)
 
 // Clear and Assignment Functions
 
-const LLMatrix4& LLMatrix4::zero()
+const LLMatrix4& LLMatrix4::setZero()
 {
 	mMatrix[0][0] = 0.f;
 	mMatrix[0][1] = 0.f;
diff --git a/indra/llmath/m4math.h b/indra/llmath/m4math.h
index 4958777b296520b5fd852a89cb0b014f11250b92..7eacbf654241e2b225180799db942898804e2cd6 100644
--- a/indra/llmath/m4math.h
+++ b/indra/llmath/m4math.h
@@ -132,8 +132,8 @@ class LLMatrix4
 				  const LLVector4 &row3);
 
 	// various useful matrix functions
-	const LLMatrix4& identity();					// Load identity matrix
-	const LLMatrix4& zero();						// Clears matrix to all zeros.
+	const LLMatrix4& setIdentity();					// Load identity matrix
+	const LLMatrix4& setZero();						// Clears matrix to all zeros.
 
 	const LLMatrix4& initRotation(const F32 angle, const F32 x, const F32 y, const F32 z);	// Calculate rotation matrix by rotating angle radians about (x, y, z)
 	const LLMatrix4& initRotation(const F32 angle, const LLVector4 &axis);	// Calculate rotation matrix for rotating angle radians about vec
@@ -243,10 +243,10 @@ class LLMatrix4
 
 inline LLMatrix4::LLMatrix4()
 {
-	identity();
+	setIdentity();
 }
 
-inline const LLMatrix4&	LLMatrix4::identity()
+inline const LLMatrix4&	LLMatrix4::setIdentity()
 {
 	mMatrix[0][0] = 1.f;
 	mMatrix[0][1] = 0.f;
diff --git a/indra/llmath/v2math.h b/indra/llmath/v2math.h
index f2450b1fd382f568e02148cffbbf4cce6b297d54..5f46655a0721a13995ac17ac87e74329413eaae5 100644
--- a/indra/llmath/v2math.h
+++ b/indra/llmath/v2math.h
@@ -54,18 +54,26 @@ class LLVector2
 		LLVector2(const F32 *vec);				// Initializes LLVector2 to (vec[0]. vec[1])
 		
 		// Clears LLVector2 to (0, 0).  DEPRECATED - prefer zeroVec.
-		void	clearVec();
+		void	clear();
+		void	setZero();
+		void	clearVec();	// deprecated
+		void	zeroVec();	// deprecated
 
-		// Zero LLVector2 to (0, 0)
-		void	zeroVec();
+		void	set(F32 x, F32 y);	        // Sets LLVector2 to (x, y)
+		void	set(const LLVector2 &vec);	// Sets LLVector2 to vec
+		void	set(const F32 *vec);			// Sets LLVector2 to vec
 
-		void	setVec(F32 x, F32 y);	        // Sets LLVector2 to (x, y)
-		void	setVec(const LLVector2 &vec);	// Sets LLVector2 to vec
-		void	setVec(const F32 *vec);			// Sets LLVector2 to vec
+		void	setVec(F32 x, F32 y);	        // deprecated
+		void	setVec(const LLVector2 &vec);	// deprecated
+		void	setVec(const F32 *vec);			// deprecated
 
-		F32		magVec() const;				// Returns magnitude of LLVector2
-		F32		magVecSquared() const;		// Returns magnitude squared of LLVector2
-		F32		normVec();					// Normalizes and returns the magnitude of LLVector2
+		F32		length() const;				// Returns magnitude of LLVector2
+		F32		lengthSquared() const;		// Returns magnitude squared of LLVector2
+		F32		normalize();					// Normalizes and returns the magnitude of LLVector2
+
+		F32		magVec() const;				// deprecated
+		F32		magVecSquared() const;		// deprecated
+		F32		normVec();					// deprecated
 
 		BOOL		abs();						// sets all values to absolute value of original value (first octant), returns TRUE if changed
 
@@ -132,30 +140,66 @@ inline LLVector2::LLVector2(const F32 *vec)
 
 // Clear and Assignment Functions
 
+inline void	LLVector2::clear(void)
+{
+	mV[VX] = 0.f;
+	mV[VY] = 0.f;
+}
+
+inline void	LLVector2::setZero(void)
+{
+	mV[VX] = 0.f;
+	mV[VY] = 0.f;
+}
+
+// deprecated
 inline void	LLVector2::clearVec(void)
 {
 	mV[VX] = 0.f;
 	mV[VY] = 0.f;
 }
 
+// deprecated
 inline void	LLVector2::zeroVec(void)
 {
 	mV[VX] = 0.f;
 	mV[VY] = 0.f;
 }
 
+inline void	LLVector2::set(F32 x, F32 y)
+{
+	mV[VX] = x;
+	mV[VY] = y;
+}
+
+inline void	LLVector2::set(const LLVector2 &vec)
+{
+	mV[VX] = vec.mV[VX];
+	mV[VY] = vec.mV[VY];
+}
+
+inline void	LLVector2::set(const F32 *vec)
+{
+	mV[VX] = vec[VX];
+	mV[VY] = vec[VY];
+}
+
+
+// deprecated
 inline void	LLVector2::setVec(F32 x, F32 y)
 {
 	mV[VX] = x;
 	mV[VY] = y;
 }
 
+// deprecated
 inline void	LLVector2::setVec(const LLVector2 &vec)
 {
 	mV[VX] = vec.mV[VX];
 	mV[VY] = vec.mV[VY];
 }
 
+// deprecated
 inline void	LLVector2::setVec(const F32 *vec)
 {
 	mV[VX] = vec[VX];
@@ -164,16 +208,49 @@ inline void	LLVector2::setVec(const F32 *vec)
 
 // LLVector2 Magnitude and Normalization Functions
 
+inline F32 LLVector2::length(void) const
+{
+	return fsqrtf(mV[0]*mV[0] + mV[1]*mV[1]);
+}
+
+inline F32 LLVector2::lengthSquared(void) const
+{
+	return mV[0]*mV[0] + mV[1]*mV[1];
+}
+
+inline F32		LLVector2::normalize(void)
+{
+	F32 mag = fsqrtf(mV[0]*mV[0] + mV[1]*mV[1]);
+	F32 oomag;
+
+	if (mag > FP_MAG_THRESHOLD)
+	{
+		oomag = 1.f/mag;
+		mV[0] *= oomag;
+		mV[1] *= oomag;
+	}
+	else
+	{
+		mV[0] = 0.f;
+		mV[1] = 0.f;
+		mag = 0;
+	}
+	return (mag);
+}
+
+// deprecated
 inline F32		LLVector2::magVec(void) const
 {
 	return fsqrtf(mV[0]*mV[0] + mV[1]*mV[1]);
 }
 
+// deprecated
 inline F32		LLVector2::magVecSquared(void) const
 {
 	return mV[0]*mV[0] + mV[1]*mV[1];
 }
 
+// deprecated
 inline F32		LLVector2::normVec(void)
 {
 	F32 mag = fsqrtf(mV[0]*mV[0] + mV[1]*mV[1]);
diff --git a/indra/llmath/v3dmath.h b/indra/llmath/v3dmath.h
index ac3f06c453d0d4516146e0e68feccc3f2969d9c3..667c335f5139fe70765742ffdeeb84724dcfded3 100644
--- a/indra/llmath/v3dmath.h
+++ b/indra/llmath/v3dmath.h
@@ -83,8 +83,9 @@ class LLVector3d
 		BOOL		clamp(const F64 min, const F64 max);		// Clamps all values to (min,max), returns TRUE if data changed
 		BOOL		abs();						// sets all values to absolute value of original value (first octant), returns TRUE if changed
 
-		inline const LLVector3d&	clearVec();						// Clears LLVector3d to (0, 0, 0, 1)
-		inline const LLVector3d&	zeroVec();						// Zero LLVector3d to (0, 0, 0, 0)
+		inline const LLVector3d&	clearVec();		// Clears LLVector3d to (0, 0, 0, 1)
+		inline const LLVector3d&	setZero();		// Zero LLVector3d to (0, 0, 0, 0)
+		inline const LLVector3d&	zeroVec();		// deprecated
 		inline const LLVector3d&	setVec(const F64 x, const F64 y, const F64 z);	// Sets LLVector3d to (x, y, z, 1)
 		inline const LLVector3d&	setVec(const LLVector3d &vec);	// Sets LLVector3d to vec
 		inline const LLVector3d&	setVec(const F64 *vec);			// Sets LLVector3d to vec
@@ -198,6 +199,14 @@ inline const LLVector3d&	LLVector3d::clearVec(void)
 	return (*this);
 }
 
+inline const LLVector3d&	LLVector3d::setZero(void)
+{
+	mdV[0] = 0.f;
+	mdV[1] = 0.f;
+	mdV[2] = 0.f;
+	return (*this);
+}
+
 inline const LLVector3d&	LLVector3d::zeroVec(void)
 {
 	mdV[0] = 0.f;
diff --git a/indra/llmath/v3math.cpp b/indra/llmath/v3math.cpp
index 5ffd1dd428b92d24332ec571c17e3efaf9e34c98..f1fe1a780ef705b1eff8c1d1c6e4ef8ae8a8de89 100644
--- a/indra/llmath/v3math.cpp
+++ b/indra/llmath/v3math.cpp
@@ -172,6 +172,22 @@ LLVector3			LLVector3::scaledVec(const LLVector3& vec) const
 	return ret;
 }
 
+const LLVector3&	LLVector3::set(const LLVector3d &vec)
+{
+	mV[0] = (F32)vec.mdV[0];
+	mV[1] = (F32)vec.mdV[1];
+	mV[2] = (F32)vec.mdV[2];
+	return (*this);
+}
+
+const LLVector3&	LLVector3::set(const LLVector4 &vec)
+{
+	mV[0] = vec.mV[0];
+	mV[1] = vec.mV[1];
+	mV[2] = vec.mV[2];
+	return (*this);
+}
+
 const LLVector3&	LLVector3::setVec(const LLVector3d &vec)
 {
 	mV[0] = (F32)vec.mdV[0];
diff --git a/indra/llmath/v3math.h b/indra/llmath/v3math.h
index e18b20ddd0e8378222401ca15a6b2b2538b01632..03c780a1f4427bd63d146f52ead5fc4b7a223840 100644
--- a/indra/llmath/v3math.h
+++ b/indra/llmath/v3math.h
@@ -81,18 +81,33 @@ class LLVector3
 
 		BOOL		abs();						// sets all values to absolute value of original value (first octant), returns TRUE if changed
 		
-		inline void	clearVec();						// Clears LLVector3 to (0, 0, 0, 1)
-		inline void	zeroVec();						// Zero LLVector3 to (0, 0, 0, 0)
-		inline void	setVec(F32 x, F32 y, F32 z);	// Sets LLVector3 to (x, y, z, 1)
-		inline void	setVec(const LLVector3 &vec);	// Sets LLVector3 to vec
-		inline void	setVec(const F32 *vec);			// Sets LLVector3 to vec
+		inline void	clear();						// Clears LLVector3 to (0, 0, 0)
+		inline void	setZero();						// Clears LLVector3 to (0, 0, 0)
+		inline void	clearVec();						// deprecated
+		inline void	zeroVec();						// deprecated
 
-		const LLVector3& setVec(const LLVector4 &vec);
-		const LLVector3& setVec(const LLVector3d &vec);	// Sets LLVector3 to vec
+		inline void	set(F32 x, F32 y, F32 z);		// Sets LLVector3 to (x, y, z, 1)
+		inline void	set(const LLVector3 &vec);		// Sets LLVector3 to vec
+		inline void	set(const F32 *vec);			// Sets LLVector3 to vec
+		const LLVector3& set(const LLVector4 &vec);
+		const LLVector3& set(const LLVector3d &vec);// Sets LLVector3 to vec
 
-		F32		magVec() const;				// Returns magnitude of LLVector3
-		F32		magVecSquared() const;		// Returns magnitude squared of LLVector3
-		inline F32		normVec();					// Normalizes and returns the magnitude of LLVector3
+		inline void	setVec(F32 x, F32 y, F32 z);	// deprecated
+		inline void	setVec(const LLVector3 &vec);	// deprecated
+		inline void	setVec(const F32 *vec);			// deprecated
+
+		const LLVector3& setVec(const LLVector4 &vec);  // deprecated
+		const LLVector3& setVec(const LLVector3d &vec);	// deprecated
+
+		F32		length() const;			// Returns magnitude of LLVector3
+		F32		lengthSquared() const;	// Returns magnitude squared of LLVector3
+		F32		magVec() const;			// deprecated
+		F32		magVecSquared() const;	// deprecated
+
+		inline F32		normalize();	// Normalizes and returns the magnitude of LLVector3
+		inline F32		normVec();		// deprecated
+
+		inline BOOL inRange( F32 min, F32 max ) const; // Returns true if all values of the vector are between min and max
 
 		const LLVector3&	rotVec(F32 angle, const LLVector3 &vec);	// Rotates about vec by angle radians
 		const LLVector3&	rotVec(F32 angle, F32 x, F32 y, F32 z);		// Rotates about x,y,z by angle radians
@@ -188,6 +203,20 @@ inline BOOL LLVector3::isFinite() const
 
 // Clear and Assignment Functions
 
+inline void	LLVector3::clear(void)
+{
+	mV[0] = 0.f;
+	mV[1] = 0.f;
+	mV[2] = 0.f;
+}
+
+inline void	LLVector3::setZero(void)
+{
+	mV[0] = 0.f;
+	mV[1] = 0.f;
+	mV[2] = 0.f;
+}
+
 inline void	LLVector3::clearVec(void)
 {
 	mV[0] = 0.f;
@@ -202,6 +231,28 @@ inline void	LLVector3::zeroVec(void)
 	mV[2] = 0.f;
 }
 
+inline void	LLVector3::set(F32 x, F32 y, F32 z)
+{
+	mV[VX] = x;
+	mV[VY] = y;
+	mV[VZ] = z;
+}
+
+inline void	LLVector3::set(const LLVector3 &vec)
+{
+	mV[0] = vec.mV[0];
+	mV[1] = vec.mV[1];
+	mV[2] = vec.mV[2];
+}
+
+inline void	LLVector3::set(const F32 *vec)
+{
+	mV[0] = vec[0];
+	mV[1] = vec[1];
+	mV[2] = vec[2];
+}
+
+// deprecated
 inline void	LLVector3::setVec(F32 x, F32 y, F32 z)
 {
 	mV[VX] = x;
@@ -209,6 +260,7 @@ inline void	LLVector3::setVec(F32 x, F32 y, F32 z)
 	mV[VZ] = z;
 }
 
+// deprecated
 inline void	LLVector3::setVec(const LLVector3 &vec)
 {
 	mV[0] = vec.mV[0];
@@ -216,6 +268,7 @@ inline void	LLVector3::setVec(const LLVector3 &vec)
 	mV[2] = vec.mV[2];
 }
 
+// deprecated
 inline void	LLVector3::setVec(const F32 *vec)
 {
 	mV[0] = vec[0];
@@ -223,6 +276,29 @@ inline void	LLVector3::setVec(const F32 *vec)
 	mV[2] = vec[2];
 }
 
+inline F32 LLVector3::normalize(void)
+{
+	F32 mag = fsqrtf(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]);
+	F32 oomag;
+
+	if (mag > FP_MAG_THRESHOLD)
+	{
+		oomag = 1.f/mag;
+		mV[0] *= oomag;
+		mV[1] *= oomag;
+		mV[2] *= oomag;
+	}
+	else
+	{
+		mV[0] = 0.f;
+		mV[1] = 0.f;
+		mV[2] = 0.f;
+		mag = 0;
+	}
+	return (mag);
+}
+
+// deprecated
 inline F32 LLVector3::normVec(void)
 {
 	F32 mag = fsqrtf(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]);
@@ -247,6 +323,16 @@ inline F32 LLVector3::normVec(void)
 
 // LLVector3 Magnitude and Normalization Functions
 
+inline F32	LLVector3::length(void) const
+{
+	return fsqrtf(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]);
+}
+
+inline F32	LLVector3::lengthSquared(void) const
+{
+	return mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2];
+}
+
 inline F32	LLVector3::magVec(void) const
 {
 	return fsqrtf(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]);
@@ -257,6 +343,13 @@ inline F32	LLVector3::magVecSquared(void) const
 	return mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2];
 }
 
+inline BOOL LLVector3::inRange( F32 min, F32 max ) const
+{
+	return mV[0] >= min && mV[0] <= max &&
+		   mV[1] >= min && mV[1] <= max &&
+		   mV[2] >= min && mV[2] <= max;		
+}
+
 inline LLVector3 operator+(const LLVector3 &a, const LLVector3 &b)
 {
 	LLVector3 c(a);
@@ -397,7 +490,7 @@ inline F32	dist_vec_squared2D(const LLVector3 &a, const LLVector3 &b)
 inline LLVector3 projected_vec(const LLVector3 &a, const LLVector3 &b)
 {
 	LLVector3 project_axis = b;
-	project_axis.normVec();
+	project_axis.normalize();
 	return project_axis * (a * project_axis);
 }
 
@@ -438,8 +531,8 @@ inline F32 angle_between(const LLVector3& a, const LLVector3& b)
 {
 	LLVector3 an = a;
 	LLVector3 bn = b;
-	an.normVec();
-	bn.normVec();
+	an.normalize();
+	bn.normalize();
 	F32 cosine = an * bn;
 	F32 angle = (cosine >= 1.0f) ? 0.0f :
 				(cosine <= -1.0f) ? F_PI :
@@ -451,8 +544,8 @@ inline BOOL are_parallel(const LLVector3 &a, const LLVector3 &b, F32 epsilon)
 {
 	LLVector3 an = a;
 	LLVector3 bn = b;
-	an.normVec();
-	bn.normVec();
+	an.normalize();
+	bn.normalize();
 	F32 dot = an * bn;
 	if ( (1.0f - fabs(dot)) < epsilon)
 	{
diff --git a/indra/llmath/v4math.cpp b/indra/llmath/v4math.cpp
index b753778ba19a3cf55285f535aa5e6d85b890859e..9da4b501d66d341bb9ea2b6381eb65e0d66da05b 100644
--- a/indra/llmath/v4math.cpp
+++ b/indra/llmath/v4math.cpp
@@ -113,8 +113,8 @@ F32 angle_between( const LLVector4& a, const LLVector4& b )
 {
 	LLVector4 an = a;
 	LLVector4 bn = b;
-	an.normVec();
-	bn.normVec();
+	an.normalize();
+	bn.normalize();
 	F32 cosine = an * bn;
 	F32 angle = (cosine >= 1.0f) ? 0.0f :
 		(cosine <= -1.0f) ? F_PI :
@@ -126,8 +126,8 @@ BOOL are_parallel(const LLVector4 &a, const LLVector4 &b, F32 epsilon)
 {
 	LLVector4 an = a;
 	LLVector4 bn = b;
-	an.normVec();
-	bn.normVec();
+	an.normalize();
+	bn.normalize();
 	F32 dot = an * bn;
 	if ( (1.0f - fabs(dot)) < epsilon)
 		return TRUE;
diff --git a/indra/llmath/v4math.h b/indra/llmath/v4math.h
index 34b5f9e33c17c9b4c23beffb9eb5dd9f5cdc6198..9f71d3452a027735e9fcb581e72d3f4c8d4c9493 100644
--- a/indra/llmath/v4math.h
+++ b/indra/llmath/v4math.h
@@ -68,17 +68,29 @@ class LLVector4
 
 		inline BOOL isFinite() const;									// checks to see if all values of LLVector3 are finite
 
-		inline void	clearVec();							// Clears LLVector4 to (0, 0, 0, 1)
-		inline void	zeroVec();							// zero LLVector4 to (0, 0, 0, 0)
-		inline void	setVec(F32 x, F32 y, F32 z);		// Sets LLVector4 to (x, y, z, 1)
-		inline void	setVec(F32 x, F32 y, F32 z, F32 w);	// Sets LLVector4 to (x, y, z, w)
-		inline void	setVec(const LLVector4 &vec);		// Sets LLVector4 to vec
-		inline void	setVec(const LLVector3 &vec, F32 w = 1.f); // Sets LLVector4 to LLVector3 vec
-		inline void	setVec(const F32 *vec);				// Sets LLVector4 to vec
-
-		F32			magVec() const;				// Returns magnitude of LLVector4
-		F32			magVecSquared() const;		// Returns magnitude squared of LLVector4
-		F32			normVec();					// Normalizes and returns the magnitude of LLVector4
+		inline void	clear();		// Clears LLVector4 to (0, 0, 0, 1)
+		inline void	clearVec();		// deprecated
+		inline void	zeroVec();		// deprecated
+
+		inline void	set(F32 x, F32 y, F32 z);			// Sets LLVector4 to (x, y, z, 1)
+		inline void	set(F32 x, F32 y, F32 z, F32 w);	// Sets LLVector4 to (x, y, z, w)
+		inline void	set(const LLVector4 &vec);			// Sets LLVector4 to vec
+		inline void	set(const LLVector3 &vec, F32 w = 1.f); // Sets LLVector4 to LLVector3 vec
+		inline void	set(const F32 *vec);				// Sets LLVector4 to vec
+
+		inline void	setVec(F32 x, F32 y, F32 z);		// deprecated
+		inline void	setVec(F32 x, F32 y, F32 z, F32 w);	// deprecated
+		inline void	setVec(const LLVector4 &vec);		// deprecated
+		inline void	setVec(const LLVector3 &vec, F32 w = 1.f); // deprecated
+		inline void	setVec(const F32 *vec);				// deprecated
+
+		F32	length() const;				// Returns magnitude of LLVector4
+		F32	lengthSquared() const;		// Returns magnitude squared of LLVector4
+		F32	normalize();				// Normalizes and returns the magnitude of LLVector4
+
+		F32			magVec() const;				// deprecated
+		F32			magVecSquared() const;		// deprecated
+		F32			normVec();					// deprecated
 
 		// Sets all values to absolute value of their original values
 		// Returns TRUE if data changed
@@ -192,6 +204,15 @@ inline BOOL LLVector4::isFinite() const
 
 // Clear and Assignment Functions
 
+inline void	LLVector4::clear(void)
+{
+	mV[VX] = 0.f;
+	mV[VY] = 0.f;
+	mV[VZ] = 0.f;
+	mV[VW] = 1.f;
+}
+
+// deprecated
 inline void	LLVector4::clearVec(void)
 {
 	mV[VX] = 0.f;
@@ -200,6 +221,7 @@ inline void	LLVector4::clearVec(void)
 	mV[VW] = 1.f;
 }
 
+// deprecated
 inline void	LLVector4::zeroVec(void)
 {
 	mV[VX] = 0.f;
@@ -208,6 +230,48 @@ inline void	LLVector4::zeroVec(void)
 	mV[VW] = 0.f;
 }
 
+inline void	LLVector4::set(F32 x, F32 y, F32 z)
+{
+	mV[VX] = x;
+	mV[VY] = y;
+	mV[VZ] = z;
+	mV[VW] = 1.f;
+}
+
+inline void	LLVector4::set(F32 x, F32 y, F32 z, F32 w)
+{
+	mV[VX] = x;
+	mV[VY] = y;
+	mV[VZ] = z;
+	mV[VW] = w;
+}
+
+inline void	LLVector4::set(const LLVector4 &vec)
+{
+	mV[VX] = vec.mV[VX];
+	mV[VY] = vec.mV[VY];
+	mV[VZ] = vec.mV[VZ];
+	mV[VW] = vec.mV[VW];
+}
+
+inline void	LLVector4::set(const LLVector3 &vec, F32 w)
+{
+	mV[VX] = vec.mV[VX];
+	mV[VY] = vec.mV[VY];
+	mV[VZ] = vec.mV[VZ];
+	mV[VW] = w;
+}
+
+inline void	LLVector4::set(const F32 *vec)
+{
+	mV[VX] = vec[VX];
+	mV[VY] = vec[VY];
+	mV[VZ] = vec[VZ];
+	mV[VW] = vec[VW];
+}
+
+
+// deprecated
 inline void	LLVector4::setVec(F32 x, F32 y, F32 z)
 {
 	mV[VX] = x;
@@ -216,6 +280,7 @@ inline void	LLVector4::setVec(F32 x, F32 y, F32 z)
 	mV[VW] = 1.f;
 }
 
+// deprecated
 inline void	LLVector4::setVec(F32 x, F32 y, F32 z, F32 w)
 {
 	mV[VX] = x;
@@ -224,6 +289,7 @@ inline void	LLVector4::setVec(F32 x, F32 y, F32 z, F32 w)
 	mV[VW] = w;
 }
 
+// deprecated
 inline void	LLVector4::setVec(const LLVector4 &vec)
 {
 	mV[VX] = vec.mV[VX];
@@ -232,6 +298,7 @@ inline void	LLVector4::setVec(const LLVector4 &vec)
 	mV[VW] = vec.mV[VW];
 }
 
+// deprecated
 inline void	LLVector4::setVec(const LLVector3 &vec, F32 w)
 {
 	mV[VX] = vec.mV[VX];
@@ -240,6 +307,7 @@ inline void	LLVector4::setVec(const LLVector3 &vec, F32 w)
 	mV[VW] = w;
 }
 
+// deprecated
 inline void	LLVector4::setVec(const F32 *vec)
 {
 	mV[VX] = vec[VX];
@@ -250,6 +318,16 @@ inline void	LLVector4::setVec(const F32 *vec)
 
 // LLVector4 Magnitude and Normalization Functions
 
+inline F32		LLVector4::length(void) const
+{
+	return fsqrtf(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]);
+}
+
+inline F32		LLVector4::lengthSquared(void) const
+{
+	return mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ];
+}
+
 inline F32		LLVector4::magVec(void) const
 {
 	return fsqrtf(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]);
@@ -364,13 +442,13 @@ inline LLVector4 operator-(const LLVector4 &a)
 inline F32	dist_vec(const LLVector4 &a, const LLVector4 &b)
 {
 	LLVector4 vec = a - b;
-	return (vec.magVec());
+	return (vec.length());
 }
 
 inline F32	dist_vec_squared(const LLVector4 &a, const LLVector4 &b)
 {
 	LLVector4 vec = a - b;
-	return (vec.magVecSquared());
+	return (vec.lengthSquared());
 }
 
 inline LLVector4 lerp(const LLVector4 &a, const LLVector4 &b, F32 u)
@@ -382,6 +460,29 @@ inline LLVector4 lerp(const LLVector4 &a, const LLVector4 &b, F32 u)
 		a.mV[VW] + (b.mV[VW] - a.mV[VW]) * u);
 }
 
+inline F32		LLVector4::normalize(void)
+{
+	F32 mag = fsqrtf(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]);
+	F32 oomag;
+
+	if (mag > FP_MAG_THRESHOLD)
+	{
+		oomag = 1.f/mag;
+		mV[VX] *= oomag;
+		mV[VY] *= oomag;
+		mV[VZ] *= oomag;
+	}
+	else
+	{
+		mV[0] = 0.f;
+		mV[1] = 0.f;
+		mV[2] = 0.f;
+		mag = 0;
+	}
+	return (mag);
+}
+
+// deprecated
 inline F32		LLVector4::normVec(void)
 {
 	F32 mag = fsqrtf(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]);
diff --git a/indra/llmath/xform.h b/indra/llmath/xform.h
index 9a5c99140efc8baf5698d60d11e142316ae7fe48..feca7908206d273db113936b56499582e0958de4 100644
--- a/indra/llmath/xform.h
+++ b/indra/llmath/xform.h
@@ -35,10 +35,12 @@
 #include "m4math.h"
 #include "llquaternion.h"
 
-const F32 MAX_OBJECT_Z 		= 768.f;
+const F32 MAX_OBJECT_Z 		= 4096.f; // should match REGION_HEIGHT_METERS, Pre-havok4: 768.f
 const F32 MIN_OBJECT_Z 		= -256.f;
-const F32 MIN_OBJECT_SCALE 	= 0.01f;
-const F32 MAX_OBJECT_SCALE 	= 10.f;
+const F32 DEFAULT_MAX_PRIM_SCALE = 10.f;
+const F32 MIN_PRIM_SCALE = 0.01f;
+const F32 MAX_PRIM_SCALE = 65536.f;	// something very high but not near FLT_MAX
+
 
 class LLXform
 {
@@ -138,7 +140,7 @@ class LLXformMatrix : public LLXform
 
 	void init()
 	{
-		mWorldMatrix.identity();
+		mWorldMatrix.setIdentity();
 		mMin.clearVec();
 		mMax.clearVec();
 
diff --git a/indra/llmessage/llinstantmessage.h b/indra/llmessage/llinstantmessage.h
index abb1651fafab7a6fab83f9456e6054c53baf1686..9645d4a763cf73d9d5b26b15808f8f831515074c 100644
--- a/indra/llmessage/llinstantmessage.h
+++ b/indra/llmessage/llinstantmessage.h
@@ -177,7 +177,7 @@ enum EGodlikeRequest
 	GOD_WANTS_NOTHING,
 
 	// for requesting physics information about an object
-	GOD_WANTS_HAVOK_INFO,
+	GOD_WANTS_PHYSICS_INFO,
 	
 	// two unused requests that can be appropriated for debug 
 	// purposes (no viewer recompile necessary)
diff --git a/indra/llmessage/lliohttpserver.cpp b/indra/llmessage/lliohttpserver.cpp
index 625dbb68b93be61abfd0ce024da80ad01a5a7cca..ec5cb93d694649a566cdccffa323b19fdaa569d9 100644
--- a/indra/llmessage/lliohttpserver.cpp
+++ b/indra/llmessage/lliohttpserver.cpp
@@ -203,7 +203,9 @@ LLIOPipe::EStatus LLHTTPPipe::process_impl(
 		}
 
 		// Log all HTTP transactions.
-		llinfos << verb << " " << context[CONTEXT_REQUEST]["path"].asString()
+		// TODO: Add a way to log these to their own file instead of indra.log
+		// It is just too spammy to be in indra.log.
+		lldebugs << verb << " " << context[CONTEXT_REQUEST]["path"].asString()
 			<< " " << mStatusCode << " " <<  mStatusMessage << " " << delta
 			<< "s" << llendl;
 
@@ -723,8 +725,8 @@ LLIOPipe::EStatus LLHTTPResponder::process_impl(
 		const LLHTTPNode* node = mRootNode.traverse(mPath, context);
 		if(node)
 		{
- 			lldebugs << "LLHTTPResponder::process_impl found node for "
-				<< mAbsPathAndQuery << llendl;
+ 			//llinfos << "LLHTTPResponder::process_impl found node for "
+			//	<< mAbsPathAndQuery << llendl;
 
   			// Copy everything after mLast read to the out.
 			LLBufferArray::segment_iterator_t seg_iter;
diff --git a/indra/llmessage/llregionflags.h b/indra/llmessage/llregionflags.h
index 65d76bba2851eef6dc360953b6759cf9c7d0551f..aa9964d46b6e2d2b7fed2db850aee33352c43d44 100644
--- a/indra/llmessage/llregionflags.h
+++ b/indra/llmessage/llregionflags.h
@@ -140,7 +140,7 @@ const U32 ESTATE_MAINLAND = 1;
 const U32 ESTATE_ORIENTATION = 2;
 const U32 ESTATE_INTERNAL = 3;
 const U32 ESTATE_SHOWCASE = 4;
-const U32 ESTATE_KIDGRID = 5;
+const U32 ESTATE_TEEN = 5;
 const U32 ESTATE_LAST_LINDEN = 5; // last linden owned/managed estate
 
 // for EstateOwnerRequest, setaccess message
diff --git a/indra/llprimitive/llmaterialtable.cpp b/indra/llprimitive/llmaterialtable.cpp
index 40cf97099c843d85bc855f311ccfa0d6c2e7ed36..3eea03e0b9a5a5a781bef390f39b93ba1ddd17e4 100644
--- a/indra/llprimitive/llmaterialtable.cpp
+++ b/indra/llprimitive/llmaterialtable.cpp
@@ -36,9 +36,58 @@
 #include "material_codes.h"
 #include "sound_ids.h"
 #include "imageids.h"
+#include <llphysics/llphysicsversion.h>
 
 LLMaterialTable LLMaterialTable::basic(1);
 
+/* 
+	Old Havok 1 constants
+
+// these are the approximately correct friction values for various materials
+// however Havok1's friction dynamics are not very correct, so the effective
+// friction coefficients that result from these numbers are approximately
+// 25-50% too low, more incorrect for the lower values.
+F32 const LLMaterialTable::FRICTION_MIN 	= 0.2f; 	
+F32 const LLMaterialTable::FRICTION_GLASS 	= 0.2f; 	// borosilicate glass
+F32 const LLMaterialTable::FRICTION_LIGHT 	= 0.2f; 	//
+F32 const LLMaterialTable::FRICTION_METAL 	= 0.3f; 	// steel
+F32 const LLMaterialTable::FRICTION_PLASTIC	= 0.4f; 	// HDPE
+F32 const LLMaterialTable::FRICTION_WOOD 	= 0.6f; 	// southern pine
+F32 const LLMaterialTable::FRICTION_FLESH 	= 0.60f; 	// saltwater
+F32 const LLMaterialTable::FRICTION_LAND 	= 0.78f; 	// dirt
+F32 const LLMaterialTable::FRICTION_STONE 	= 0.8f; 	// concrete
+F32 const LLMaterialTable::FRICTION_RUBBER 	= 0.9f; 	//
+F32 const LLMaterialTable::FRICTION_MAX 	= 0.95f; 	//
+*/
+
+#if LL_CURRENT_HAVOK_VERSION == LL_HAVOK_VERSION_460
+// Havok4 has more correct friction dynamics, however here we have to use
+// the "incorrect" equivalents for the legacy Havok1 behavior
+F32 const LLMaterialTable::FRICTION_MIN 	= 0.15f; 	
+F32 const LLMaterialTable::FRICTION_GLASS 	= 0.13f; 	// borosilicate glass
+F32 const LLMaterialTable::FRICTION_LIGHT 	= 0.14f; 	//
+F32 const LLMaterialTable::FRICTION_METAL 	= 0.22f; 	// steel
+F32 const LLMaterialTable::FRICTION_PLASTIC	= 0.3f; 	// HDPE
+F32 const LLMaterialTable::FRICTION_WOOD 	= 0.44f; 	// southern pine
+F32 const LLMaterialTable::FRICTION_FLESH 	= 0.46f; 	// saltwater
+F32 const LLMaterialTable::FRICTION_LAND 	= 0.58f; 	// dirt
+F32 const LLMaterialTable::FRICTION_STONE 	= 0.6f; 	// concrete
+F32 const LLMaterialTable::FRICTION_RUBBER 	= 0.67f; 	//
+F32 const LLMaterialTable::FRICTION_MAX 	= 0.71f; 	//
+#endif
+
+F32 const LLMaterialTable::RESTITUTION_MIN 		= 0.02f; 	
+F32 const LLMaterialTable::RESTITUTION_LAND 	= LLMaterialTable::RESTITUTION_MIN;
+F32 const LLMaterialTable::RESTITUTION_FLESH 	= 0.2f; 	// saltwater
+F32 const LLMaterialTable::RESTITUTION_STONE 	= 0.4f; 	// concrete
+F32 const LLMaterialTable::RESTITUTION_METAL 	= 0.4f; 	// steel
+F32 const LLMaterialTable::RESTITUTION_WOOD 	= 0.5f; 	// southern pine
+F32 const LLMaterialTable::RESTITUTION_GLASS 	= 0.7f; 	// borosilicate glass
+F32 const LLMaterialTable::RESTITUTION_PLASTIC	= 0.7f; 	// HDPE
+F32 const LLMaterialTable::RESTITUTION_LIGHT 	= 0.7f; 	//
+F32 const LLMaterialTable::RESTITUTION_RUBBER 	= 0.9f; 	//
+F32 const LLMaterialTable::RESTITUTION_MAX		= 0.95f;
+
 F32 const LLMaterialTable::DEFAULT_FRICTION = 0.5f;
 F32 const LLMaterialTable::DEFAULT_RESTITUTION = 0.4f;
 
diff --git a/indra/llprimitive/llmaterialtable.h b/indra/llprimitive/llmaterialtable.h
index 46b6f070d90a04015da5bfc276f41115fd9b4c98..e84e75c677b78fae102ed5e334c049e9430fa951 100644
--- a/indra/llprimitive/llmaterialtable.h
+++ b/indra/llprimitive/llmaterialtable.h
@@ -33,11 +33,36 @@
 #define LL_LLMATERIALTABLE_H
 
 #include "lluuid.h"
-#include "linked_lists.h"
 #include "llstring.h"
 
+#include <list>
+
 const U32 LLMATERIAL_INFO_NAME_LENGTH = 256;
 
+// We've moved toward more reasonable mass values for the Havok4 engine.
+// The LEGACY_DEFAULT_OBJECT_DENSITY is used to maintain support for
+// legacy scripts code (llGetMass()) and script energy consumption.
+const F32 DEFAULT_OBJECT_DENSITY = 1000.0f;			// per m^3
+const F32 LEGACY_DEFAULT_OBJECT_DENSITY = 10.0f;
+
+// Avatars density depends on the collision shape used.  The approximate
+// legacy volumes of avatars are:
+// Body_Length Body_Width Body_Fat Leg_Length  Volume(m^3)
+// -------------------------------------------------------
+//    min     |   min    |  min   |    min    |   0.123   |
+//    max     |   max    |  max   |    max    |   0.208   |
+//
+// Either the avatar shape must be tweaked to match those volumes
+// or the DEFAULT_AVATAR_DENSITY must be adjusted to achieve the 
+// legacy mass.
+//
+// The current density appears to be low because the mass and
+// inertia are computed as if the avatar were a cylinder which
+// has more volume than the actual collision shape of the avatar.
+// See the physics engine mass properties code for more info.
+const F32 DEFAULT_AVATAR_DENSITY = 445.3f;		// was 444.24f;
+
+
 class LLMaterialInfo
 {
 public:
@@ -84,9 +109,33 @@ class LLMaterialInfo
 class LLMaterialTable
 {
 public:
+	static const F32 FRICTION_MIN;
+	static const F32 FRICTION_GLASS;
+	static const F32 FRICTION_LIGHT;
+	static const F32 FRICTION_METAL;
+	static const F32 FRICTION_PLASTIC;
+	static const F32 FRICTION_WOOD;
+	static const F32 FRICTION_LAND;
+	static const F32 FRICTION_STONE;
+	static const F32 FRICTION_FLESH;
+	static const F32 FRICTION_RUBBER;
+	static const F32 FRICTION_MAX;
+
+	static const F32 RESTITUTION_MIN;
+	static const F32 RESTITUTION_LAND;
+	static const F32 RESTITUTION_FLESH;
+	static const F32 RESTITUTION_STONE;
+	static const F32 RESTITUTION_METAL;
+	static const F32 RESTITUTION_WOOD;
+	static const F32 RESTITUTION_GLASS;
+	static const F32 RESTITUTION_PLASTIC;
+	static const F32 RESTITUTION_LIGHT;
+	static const F32 RESTITUTION_RUBBER;
+	static const F32 RESTITUTION_MAX;
+
 	typedef std::list<LLMaterialInfo*> info_list_t;
 	info_list_t mMaterialInfoList;
-	
+
 	LLUUID *mCollisionSoundMatrix;
 	LLUUID *mSlidingSoundMatrix;
 	LLUUID *mRollingSoundMatrix;
@@ -117,8 +166,8 @@ class LLMaterialTable
 	char*  getName(U8 mcode);
 
 	F32 getDensity(U8 mcode);						        // kg/m^3, 0 if not found
-	F32 getFriction(U8 mcode);						        // havok values
-	F32 getRestitution(U8 mcode);						    // havok values
+	F32 getFriction(U8 mcode);						        // physics values
+	F32 getRestitution(U8 mcode);						    // physics values
 	F32 getHPMod(U8 mcode);
 	F32 getDamageMod(U8 mcode);
 	F32 getEPMod(U8 mcode);
diff --git a/indra/llprimitive/llprimitive.cpp b/indra/llprimitive/llprimitive.cpp
index 77bca8f803173ed378678e3851fa7d3463799f6a..cc676f73f18bb694d7d0e345f3fee6fd1ed91fc8 100644
--- a/indra/llprimitive/llprimitive.cpp
+++ b/indra/llprimitive/llprimitive.cpp
@@ -113,9 +113,38 @@ const BOOL FLEXIBLE_OBJECT_DEFAULT_RENDERING_COLLISION_SPHERE = FALSE;
 
 const char *SCULPT_DEFAULT_TEXTURE = "be293869-d0d9-0a69-5989-ad27f1946fd4"; // old inverted texture: "7595d345-a24c-e7ef-f0bd-78793792133e";
 
+//static 
+// LEGACY: by default we use the LLVolumeMgr::gVolumeMgr global
+// TODO -- eliminate this global from the codebase!
+LLVolumeMgr* LLPrimitive::sVolumeManager = NULL;
+
+// static
+void LLPrimitive::setVolumeManager( LLVolumeMgr* volume_manager )
+{
+	if ( !volume_manager || sVolumeManager )
+	{
+		llerrs << "Unable to set LLPrimitive::sVolumeManager to NULL" << llendl;
+	}
+	sVolumeManager = volume_manager;
+}
+
+// static
+bool LLPrimitive::cleanupVolumeManager()
+{
+	BOOL res = FALSE;
+	if (sVolumeManager) 
+	{
+		res = sVolumeManager->cleanup();
+		delete sVolumeManager;
+		sVolumeManager = NULL;
+	}
+	return res;
+}
+
 
 //===============================================================
 LLPrimitive::LLPrimitive()
+:	mMiscFlags(0)
 {
 	mPrimitiveCode = 0;
 
@@ -149,7 +178,7 @@ LLPrimitive::~LLPrimitive()
 	// Cleanup handled by volume manager
 	if (mVolumep)
 	{
-		gVolumeMgr->cleanupVolume(mVolumep);
+		sVolumeManager->cleanupVolume(mVolumep);
 	}
 	mVolumep = NULL;
 }
@@ -162,7 +191,7 @@ LLPrimitive *LLPrimitive::createPrimitive(LLPCode p_code)
 	
 	if (retval)
 	{
-		retval->init(p_code);
+		retval->init_primitive(p_code);
 	}
 	else
 	{
@@ -173,7 +202,7 @@ LLPrimitive *LLPrimitive::createPrimitive(LLPCode p_code)
 }
 
 //===============================================================
-void LLPrimitive::init(LLPCode p_code)
+void LLPrimitive::init_primitive(LLPCode p_code)
 {
 	if (mNumTEs)
 	{
@@ -533,6 +562,8 @@ S32 LLPrimitive::setTEGlow(const U8 te, const F32 glow)
 
 LLPCode LLPrimitive::legacyToPCode(const U8 legacy)
 {
+	// TODO: Should this default to something valid?
+	// Maybe volume?
 	LLPCode pcode = 0;
 
 	switch (legacy)
@@ -621,7 +652,7 @@ LLPCode LLPrimitive::legacyToPCode(const U8 legacy)
 		pcode = LL_PCODE_TREE_NEW;
 		break;
 	default:
-		llwarns << "Unknown legacy code " << legacy << "!" << llendl;
+		llwarns << "Unknown legacy code " << legacy << " [" << (S32)legacy << "]!" << llendl;
 	}
 
 	return pcode;
@@ -904,10 +935,10 @@ BOOL LLPrimitive::setVolume(const LLVolumeParams &volume_params, const S32 detai
 			}
 		}
 
-		volumep = gVolumeMgr->getVolume(volume_params, detail);
+		volumep = sVolumeManager->getVolume(volume_params, detail);
 		if (volumep == mVolumep)
 		{
-			gVolumeMgr->cleanupVolume( volumep );  // gVolumeMgr->getVolume() creates a reference, but we don't need a second one.
+			sVolumeManager->cleanupVolume( volumep );  // LLVolumeMgr::getVolume() creates a reference, but we don't need a second one.
 			return TRUE;
 		}
 	}
@@ -950,7 +981,7 @@ BOOL LLPrimitive::setVolume(const LLVolumeParams &volume_params, const S32 detai
 
 
 	// build the new object
-	gVolumeMgr->cleanupVolume(mVolumep);
+	sVolumeManager->cleanupVolume(mVolumep);
 	mVolumep = volumep;
 	
 	U32 new_face_mask = mVolumep->mFaceMask;
diff --git a/indra/llprimitive/llprimitive.h b/indra/llprimitive/llprimitive.h
index eef58341e7639cffbc89f1e4e4f5fecc1e9def6c..2b738f8d298a3ff94d3935c2de57b735703cc299 100644
--- a/indra/llprimitive/llprimitive.h
+++ b/indra/llprimitive/llprimitive.h
@@ -48,6 +48,7 @@ class LLColor4;
 class LLColor3;
 class LLTextureEntry;
 class LLDataPacker;
+class LLVolumeMgr;
 
 enum LLGeomType // NOTE: same vals as GL Ids
 {
@@ -269,11 +270,32 @@ class LLSculptParams : public LLNetworkData
 class LLPrimitive : public LLXform
 {
 public:
+
+	// HACK for removing LLPrimitive's dependency on gVolumeMgr global.
+	// If a different LLVolumeManager is instantiated and set early enough
+	// then the LLPrimitive class will use it instead of gVolumeMgr.
+	static LLVolumeMgr* getVolumeManager() { return sVolumeManager; }
+	static void setVolumeManager( LLVolumeMgr* volume_manager);
+	static bool cleanupVolumeManager();
+
+	// these flags influence how the RigidBody representation is built
+	static const U32 PRIM_FLAG_PHANTOM 				= 0x1 << 0;
+	static const U32 PRIM_FLAG_VOLUME_DETECT 		= 0x1 << 1;
+	static const U32 PRIM_FLAG_DYNAMIC 				= 0x1 << 2;
+	static const U32 PRIM_FLAG_AVATAR 				= 0x1 << 3;
+	static const U32 PRIM_FLAG_SCULPT 				= 0x1 << 4;
+	// not used yet, but soon
+	static const U32 PRIM_FLAG_COLLISION_CALLBACK 	= 0x1 << 5;
+	static const U32 PRIM_FLAG_CONVEX 				= 0x1 << 6;
+	static const U32 PRIM_FLAG_DEFAULT_VOLUME		= 0x1 << 7;
+	static const U32 PRIM_FLAG_SITTING				= 0x1 << 8;
+	static const U32 PRIM_FLAG_SITTING_ON_GROUND	= 0x1 << 9;		// Set along with PRIM_FLAG_SITTING
+
 	LLPrimitive();
 	virtual ~LLPrimitive();
 
 	static LLPrimitive *createPrimitive(LLPCode p_code);
-	void init(LLPCode p_code);
+	void init_primitive(LLPCode p_code);
 
 	void setPCode(const LLPCode pcode);
 	const LLVolume *getVolumeConst() const { return mVolumep; }		// HACK for Windoze confusion about ostream operator in LLVolume
@@ -369,8 +391,15 @@ class LLPrimitive : public LLXform
 
 	void setTextureList(LLTextureEntry *listp);
 
-	inline BOOL			isAvatar() const;
-	
+	inline BOOL	isAvatar() const;
+	inline BOOL	isSittingAvatar() const;
+	inline BOOL	isSittingAvatarOnGround() const;
+
+	void setFlags(U32 flags) { mMiscFlags = flags; }
+	void addFlags(U32 flags) { mMiscFlags |= flags; }
+	void removeFlags(U32 flags) { mMiscFlags &= ~flags; }
+	U32 getFlags() const { return mMiscFlags; }
+
 	static const char *pCodeToString(const LLPCode pcode);
 	static LLPCode legacyToPCode(const U8 legacy);
 	static U8 pCodeToLegacy(const LLPCode pcode);
@@ -388,11 +417,28 @@ class LLPrimitive : public LLXform
 	LLTextureEntry		*mTextureList;		// list of texture GUIDs, scales, offsets
 	U8					mMaterial;			// Material code
 	U8					mNumTEs;			// # of faces on the primitve	
+	U32 				mMiscFlags;			// home for misc bools
+
+	static LLVolumeMgr* sVolumeManager;
 };
 
 inline BOOL LLPrimitive::isAvatar() const
 {
-	return mPrimitiveCode == LL_PCODE_LEGACY_AVATAR;
+	return ( LL_PCODE_LEGACY_AVATAR == mPrimitiveCode ) ? TRUE : FALSE;
+}
+
+inline BOOL LLPrimitive::isSittingAvatar() const
+{
+	// this is only used server-side
+	return ( LL_PCODE_LEGACY_AVATAR == mPrimitiveCode 
+			 &&	 ((getFlags() & (PRIM_FLAG_SITTING | PRIM_FLAG_SITTING_ON_GROUND)) != 0) ) ? TRUE : FALSE;
+}
+
+inline BOOL LLPrimitive::isSittingAvatarOnGround() const
+{
+	// this is only used server-side
+	return ( LL_PCODE_LEGACY_AVATAR == mPrimitiveCode 
+			 &&	 ((getFlags() & PRIM_FLAG_SITTING_ON_GROUND) != 0) ) ? TRUE : FALSE;
 }
 
 // static
diff --git a/indra/llprimitive/llprimlinkinfo.h b/indra/llprimitive/llprimlinkinfo.h
new file mode 100644
index 0000000000000000000000000000000000000000..139617f969128e9f54e974852db694944f76f572
--- /dev/null
+++ b/indra/llprimitive/llprimlinkinfo.h
@@ -0,0 +1,375 @@
+/** 
+ * @file llprimlinkinfo.h
+ * @author andrew@lindenlab.com
+ * @brief A template for determining which prims in a set are linkable
+ *
+ * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+
+#ifndef LL_PRIM_LINK_INFO_H
+#define LL_PRIM_LINK_INFO_H
+
+// system includes
+#include <iostream>
+#include <map>
+#include <list>
+#include <vector>
+
+// common includes
+#include "stdtypes.h"
+#include "v3math.h"
+#include "llquaternion.h"
+#include "llsphere.h"
+
+
+const F32 MAX_OBJECT_SPAN = 54.f;		// max distance from outside edge of an object to the farthest edge
+const F32 OBJECT_SPAN_BONUS = 2.f;		// infinitesimally small prims can always link up to this distance
+const S32 MAX_PRIMS_PER_OBJECT = 255;
+
+
+template < typename DATA_TYPE >
+class LLPrimLinkInfo
+{
+public:
+	LLPrimLinkInfo();
+	LLPrimLinkInfo( DATA_TYPE data, const LLSphere& sphere );
+	~LLPrimLinkInfo();
+
+	void set( DATA_TYPE data, const LLSphere& sphere );
+	void append( DATA_TYPE data, const LLSphere& sphere );
+	void getData( std::list< DATA_TYPE >& data_list ) const;
+	F32 getDiameter() const;
+	LLVector3 getCenter() const;
+
+	// returns 'true' if this info can link with other_info
+	bool canLink( const LLPrimLinkInfo< DATA_TYPE >& other_info );
+
+	S32 getPrimCount() const { return mDataMap.size(); }
+
+	void mergeLinkableSet( typename std::list< LLPrimLinkInfo < DATA_TYPE > >& unlinked );
+
+	void transform(const LLVector3& position, const LLQuaternion& rotation);
+
+private:
+	// returns number of merges made
+	S32 merge(LLPrimLinkInfo< DATA_TYPE >& other_info);
+
+	// returns number of collapses made
+	static S32 collapse(typename std::list< LLPrimLinkInfo < DATA_TYPE > >& unlinked );
+
+	void computeBoundingSphere();
+
+	// Internal utility to encapsulate the link rules
+	F32 get_max_linkable_span(const LLSphere& first, const LLSphere& second);
+	F32 get_span(const LLSphere& first, const LLSphere& second);
+
+private:
+	std::map< DATA_TYPE, LLSphere > mDataMap;
+	LLSphere mBoundingSphere;
+};
+
+
+
+template < typename DATA_TYPE >
+LLPrimLinkInfo< DATA_TYPE >::LLPrimLinkInfo()
+:	mBoundingSphere( LLVector3(0.f, 0.f, 0.f), 0.f )
+{
+}
+
+template < typename DATA_TYPE >
+LLPrimLinkInfo< DATA_TYPE >::LLPrimLinkInfo( DATA_TYPE data, const LLSphere& sphere)
+:	mBoundingSphere(sphere)
+{
+	mDataMap[data] = sphere;
+}
+
+template < typename DATA_TYPE >
+LLPrimLinkInfo< DATA_TYPE >::~LLPrimLinkInfo()
+{
+	mDataMap.clear();
+}
+
+template < typename DATA_TYPE >
+void LLPrimLinkInfo< DATA_TYPE>::set( DATA_TYPE data, const LLSphere& sphere )
+{
+	if (!mDataMap.empty())
+	{
+		mDataMap.clear();
+	}
+	mDataMap[data] = sphere;
+	mBoundingSphere = sphere;
+}
+
+template < typename DATA_TYPE >
+void LLPrimLinkInfo< DATA_TYPE>::append( DATA_TYPE data, const LLSphere& sphere )
+{
+	mDataMap[data] = sphere;
+	if (!mBoundingSphere.contains(sphere))
+	{
+		computeBoundingSphere();
+	}
+}
+
+template < typename DATA_TYPE >
+void LLPrimLinkInfo< DATA_TYPE >::getData( std::list< DATA_TYPE >& data_list) const
+{
+	typename std::map< DATA_TYPE, LLSphere >::const_iterator map_itr;
+	for (map_itr = mDataMap.begin(); map_itr != mDataMap.end(); ++map_itr)
+	{
+		data_list.push_back(map_itr->first);
+	}
+}
+
+template < typename DATA_TYPE >
+F32 LLPrimLinkInfo< DATA_TYPE >::getDiameter() const
+{ 
+	return 2.f * mBoundingSphere.getRadius();
+}
+
+template < typename DATA_TYPE >
+LLVector3 LLPrimLinkInfo< DATA_TYPE >::getCenter() const
+{ 
+	return mBoundingSphere.getCenter(); 
+}
+
+template < typename DATA_TYPE >
+F32 LLPrimLinkInfo< DATA_TYPE >::get_max_linkable_span(const LLSphere& first, const LLSphere& second)
+{
+	F32 max_span = 3.f * (first.getRadius() + second.getRadius()) + OBJECT_SPAN_BONUS;
+	if (max_span > MAX_OBJECT_SPAN)
+	{
+		max_span = MAX_OBJECT_SPAN;
+	}
+
+	return max_span;
+}
+
+template < typename DATA_TYPE >
+F32 LLPrimLinkInfo< DATA_TYPE >::get_span(const LLSphere& first, const LLSphere& second)
+{
+	F32 span = (first.getCenter() - second.getCenter()).length()
+				+ first.getRadius() + second.getRadius();
+	return span;
+}
+
+// static
+// returns 'true' if this info can link with any part of other_info
+template < typename DATA_TYPE >
+bool LLPrimLinkInfo< DATA_TYPE >::canLink(const LLPrimLinkInfo& other_info)
+{
+	F32 max_span = get_max_linkable_span(mBoundingSphere, other_info.mBoundingSphere);
+
+	F32 span = get_span(mBoundingSphere, other_info.mBoundingSphere);
+	
+	if (span <= max_span)
+	{
+		// The entire other_info fits inside the max span.
+		return TRUE;
+	}
+	else if (span > max_span + 2.f * other_info.mBoundingSphere.getRadius())
+	{
+		// there is no way any piece of other_info could link with this one
+		return FALSE;
+	}
+
+	// there may be a piece of other_info that is linkable
+	typename std::map< DATA_TYPE, LLSphere >::const_iterator map_itr;
+	for (map_itr = other_info.mDataMap.begin(); map_itr != other_info.mDataMap.end(); ++map_itr)
+	{
+		const LLSphere& other_sphere = (*map_itr).second;
+		max_span = get_max_linkable_span(mBoundingSphere, other_sphere);
+
+		span = get_span(mBoundingSphere, other_sphere);
+
+		if (span <= max_span)
+		{
+			// found one piece that is linkable
+			return TRUE;
+		}
+	}
+	return FALSE;
+}
+
+// merges elements of 'unlinked' 
+// returns number of links made (NOT final prim count, NOR linked prim count)
+// and removes any linkable infos from 'unlinked' 
+template < typename DATA_TYPE >
+void LLPrimLinkInfo< DATA_TYPE >::mergeLinkableSet(std::list< LLPrimLinkInfo< DATA_TYPE > > & unlinked)
+{
+	bool linked_something = true;
+	while (linked_something)
+	{
+		linked_something = false;
+
+		typename std::list< LLPrimLinkInfo< DATA_TYPE > >::iterator other_itr = unlinked.begin();
+		while ( other_itr != unlinked.end()
+			   && getPrimCount() < MAX_PRIMS_PER_OBJECT )
+		{
+			S32 merge_count = merge(*other_itr);
+			if (merge_count > 0)
+			{
+				linked_something = true;
+			}
+			if (0 == (*other_itr).getPrimCount())
+			{
+				unlinked.erase(other_itr++);
+			}
+			else
+			{
+				++other_itr;
+			}
+		}
+		if (!linked_something
+			&& unlinked.size() > 1)
+		{
+			S32 collapse_count = collapse(unlinked);
+			if (collapse_count > 0)
+			{
+				linked_something = true;
+			}
+		}
+	}
+}
+
+// transforms all of the spheres into a new reference frame
+template < typename DATA_TYPE >
+void LLPrimLinkInfo< DATA_TYPE >::transform(const LLVector3& position, const LLQuaternion& rotation)
+{
+	typename std::map< DATA_TYPE, LLSphere >::iterator map_itr;
+	for (map_itr = mDataMap.begin(); map_itr != mDataMap.end(); ++map_itr)
+	{
+		(*map_itr).second.setCenter((*map_itr).second.getCenter() * rotation + position);
+	}
+	mBoundingSphere.setCenter(mBoundingSphere.getCenter() * rotation + position);
+}
+
+// private
+// returns number of links made
+template < typename DATA_TYPE >
+S32 LLPrimLinkInfo< DATA_TYPE >::merge(LLPrimLinkInfo& other_info)
+{
+	S32 link_count = 0;
+
+//	F32 other_radius = other_info.mBoundingSphere.getRadius();
+//	other_info.computeBoundingSphere();
+//	if ( other_radius != other_info.mBoundingSphere.getRadius() )
+//	{
+//		llinfos << "Other bounding sphere changed!!" << llendl;
+//	}
+
+//	F32 this_radius = mBoundingSphere.getRadius();
+//	computeBoundingSphere();
+//	if ( this_radius != mBoundingSphere.getRadius() )
+//	{
+//		llinfos << "This bounding sphere changed!!" << llendl;
+//	}
+
+
+	F32 max_span = get_max_linkable_span(mBoundingSphere, other_info.mBoundingSphere);
+
+	//  F32 center_dist = (mBoundingSphere.getCenter() - other_info.mBoundingSphere.getCenter()).length();
+	//	llinfos << "objects are " << center_dist << "m apart" << llendl;
+	F32 span = get_span(mBoundingSphere, other_info.mBoundingSphere);
+
+	F32 span_limit = max_span + (2.f * other_info.mBoundingSphere.getRadius());
+	if (span > span_limit)
+	{
+		// there is no way any piece of other_info could link with this one
+		// llinfos << "span too large:  " << span << " vs. " << span_limit << llendl;
+		return 0;
+	}
+
+	bool completely_linkable = (span <= max_span) ? true : false;
+
+	typename std::map< DATA_TYPE, LLSphere >::iterator map_itr = other_info.mDataMap.begin();
+	while (map_itr != other_info.mDataMap.end()
+			&& getPrimCount() < MAX_PRIMS_PER_OBJECT )
+	{
+		DATA_TYPE other_data = (*map_itr).first;
+		LLSphere& other_sphere = (*map_itr).second;
+
+		if (!completely_linkable)
+		{
+			max_span = get_max_linkable_span(mBoundingSphere, other_sphere);
+	
+			F32 span = get_span(mBoundingSphere, other_sphere);
+
+			if (span > max_span)
+			{
+				++map_itr;
+				continue;
+			}
+		}
+
+		mDataMap[other_data] = other_sphere;
+		++link_count;
+
+		if (!mBoundingSphere.contains(other_sphere) )
+		{
+			computeBoundingSphere();
+		}
+
+		// remove from the other info
+		other_info.mDataMap.erase(map_itr++);
+	}
+
+	if (link_count > 0 && other_info.getPrimCount() > 0)
+	{
+		other_info.computeBoundingSphere();
+	}
+	return link_count;
+}
+
+// links any linkable elements of unlinked
+template < typename DATA_TYPE >
+S32 LLPrimLinkInfo< DATA_TYPE >::collapse(std::list< LLPrimLinkInfo< DATA_TYPE > > & unlinked)
+{ 
+	S32 link_count = 0;
+	bool linked_something = true;
+	while (linked_something)
+	{
+		linked_something = false;
+
+		typename std::list< LLPrimLinkInfo< DATA_TYPE > >::iterator this_itr = unlinked.begin();
+		typename std::list< LLPrimLinkInfo< DATA_TYPE > >::iterator other_itr = this_itr;
+		++other_itr;
+		while ( other_itr != unlinked.end() )
+			   
+		{
+			S32 merge_count = (*this_itr).merge(*other_itr);
+			if (merge_count > 0)
+			{
+				linked_something = true;
+				link_count += merge_count;
+			}
+			if (0 == (*other_itr).getPrimCount())
+			{
+				unlinked.erase(other_itr++);
+			}
+			else
+			{
+				++other_itr;
+			}
+		}
+	}
+	return link_count;
+}
+
+
+template < typename DATA_TYPE >
+void LLPrimLinkInfo< DATA_TYPE >::computeBoundingSphere()
+{ 
+	std::vector< LLSphere > sphere_list;
+	typename std::map< DATA_TYPE, LLSphere >::const_iterator map_itr;
+	for (map_itr = mDataMap.begin(); map_itr != mDataMap.end(); ++map_itr)
+	{
+		sphere_list.push_back(map_itr->second);
+	}
+	mBoundingSphere = LLSphere::getBoundingSphere(sphere_list);
+}
+
+
+#endif
+
diff --git a/indra/newview/app_settings/keywords.ini b/indra/newview/app_settings/keywords.ini
index 961e86c6cb30697ba433e13e460b3a1435a099b2..9a8b1f7537bc8efcfe3571c32d4855755ce0daaa 100644
--- a/indra/newview/app_settings/keywords.ini
+++ b/indra/newview/app_settings/keywords.ini
@@ -339,7 +339,7 @@ PRIM_FLEXIBLE		Sets primitive flexibility to TRUE or FALSE
 PRIM_POINT_LIGHT	Sets light emission to TRUE or FALSE
 PRIM_TEMP_ON_REZ	Sets temporay on rez to TRUE or FALSE
 PRIM_PHANTOM		Sets phantom to TRUE or FALSE
-PRIM_CAST_SHADOWS	Enables or disables shadow casting for the primitive
+PRIM_CAST_SHADOWS	DEPRECATED. Takes 1 parameter, an integer, but has no effect when set and always returns 0 if used in llGetPrimitiveParams.
 PRIM_POSITION		Sets primitive position to a vector position
 PRIM_SIZE			Sets primitive size to a vector size
 PRIM_ROTATION		Sets primitive rotation
diff --git a/indra/newview/linux_tools/handle_secondlifeprotocol.sh b/indra/newview/linux_tools/handle_secondlifeprotocol.sh
old mode 100755
new mode 100644
diff --git a/indra/newview/linux_tools/register_secondlifeprotocol.sh b/indra/newview/linux_tools/register_secondlifeprotocol.sh
old mode 100755
new mode 100644
diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp
index d00dfef478c4774b953945531b2b802251c24b63..2f5589a96611963185a3b47d5bdd45f0ac24a82d 100644
--- a/indra/newview/llagent.cpp
+++ b/indra/newview/llagent.cpp
@@ -92,7 +92,7 @@
 #include "llquantize.h"
 #include "llselectmgr.h"
 #include "llsky.h"
-#include "llsphere.h"
+#include "llrendersphere.h"
 #include "llstatusbar.h"
 #include "llimview.h"
 #include "lltool.h"
@@ -1907,7 +1907,7 @@ void LLAgent::cameraOrbitIn(const F32 meters)
 
 		if( CAMERA_MODE_CUSTOMIZE_AVATAR == getCameraMode() )
 		{
-			llclamp( new_distance, APPEARANCE_MIN_ZOOM, APPEARANCE_MAX_ZOOM );
+			new_distance = llclamp( new_distance, APPEARANCE_MIN_ZOOM, APPEARANCE_MAX_ZOOM );
 		}
 
 		// Compute new camera offset
@@ -6891,7 +6891,7 @@ void LLAgent::sendAgentSetAppearance()
 	msg->addUUIDFast(_PREHASH_AgentID, getID());
 	msg->addUUIDFast(_PREHASH_SessionID, getSessionID());
 
-	// correct for the collisiton tolerance (to make it look like the 
+	// correct for the collision tolerance (to make it look like the 
 	// agent is actually walking on the ground/object)
 	// NOTE -- when we start correcting all of the other Havok geometry 
 	// to compensate for the COLLISION_TOLERANCE ugliness we will have 
diff --git a/indra/newview/llagent.h b/indra/newview/llagent.h
index 50830a75f0b85c933e28d9d13a83e3140fc7044d..1a7d23928895de5002cc4584f08dc7f342e4861f 100644
--- a/indra/newview/llagent.h
+++ b/indra/newview/llagent.h
@@ -181,7 +181,7 @@ class LLAgent : public LLObservable
 	void			updateCamera();			// call once per frame to update camera location/orientation
 	void			resetCamera();						// slam camera into its default position
 	void			setupSitCamera();
-	void			setCameraCollidePlane(LLVector4 &plane) { mCameraCollidePlane = plane; }
+	void			setCameraCollidePlane(const LLVector4 &plane) { mCameraCollidePlane = plane; }
 
 	void			changeCameraToDefault();
 	void			changeCameraToMouselook(BOOL animate = TRUE);
@@ -428,7 +428,7 @@ class LLAgent : public LLObservable
 
 	U32 			getControlFlags(); 
 	void 			setControlFlags(U32 mask); 			// performs bitwise mControlFlags |= mask
-	void 			clearControlFlags(U32 mask); 			// performs bitwise mControlFlags &= mask
+	void 			clearControlFlags(U32 mask); 			// performs bitwise mControlFlags &= ~mask
 	BOOL			controlFlagsDirty() const;
 	void			enableControlFlagReset();
 	void 			resetControlFlags();
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index 502160716eb6e680b1ed5e2da3fabed3723065a6..fc092e5cba89c0529e290f46e94ab2ea31a8b79c 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -32,6 +32,7 @@
 
 #include "llviewerprecompiledheaders.h"
 #include "llappviewer.h"
+#include "llprimitive.h"
 
 #include "llversionviewer.h"
 #include "llfeaturemanager.h"
@@ -1207,10 +1208,12 @@ bool LLAppViewer::cleanup()
 	gLcdScreen = NULL;
 #endif
 
-	if (!gVolumeMgr->cleanup())
+	LLVolumeMgr* volume_manager = LLPrimitive::getVolumeManager();
+	if (!volume_manager->cleanup())
 	{
 		llwarns << "Remaining references in the volume manager!" << llendflush;
 	}
+	LLPrimitive::cleanupVolumeManager();
 
 	LLViewerParcelMgr::cleanupGlobals();
 
@@ -1219,7 +1222,8 @@ bool LLAppViewer::cleanup()
  	//end_messaging_system();
 
 	LLFollowCamMgr::cleanupClass();
-	LLVolumeMgr::cleanupClass();
+	//LLVolumeMgr::cleanupClass();
+	LLPrimitive::cleanupVolumeManager();
 	LLWorldMapView::cleanupClass();
 	LLUI::cleanupClass();
 	
@@ -1766,7 +1770,10 @@ bool LLAppViewer::initConfiguration()
 	LLSplashScreen::show();
 	LLSplashScreen::update(splash_msg.str().c_str());
 
-	LLVolumeMgr::initClass();
+	//LLVolumeMgr::initClass();
+	LLVolumeMgr* volume_manager = new LLVolumeMgr();
+	volume_manager->useMutex();	// LLApp and LLMutex magic must be manually enabled
+	LLPrimitive::setVolumeManager(volume_manager);
 
 	// Note: this is where we used to initialize LLFeatureManager::getInstance()->.
 
diff --git a/indra/newview/llflexibleobject.cpp b/indra/newview/llflexibleobject.cpp
index b700faecccec46c13d35ba3c74316297a79b99cf..c18dc069a47c74fe438822e969e34911900e60cc 100644
--- a/indra/newview/llflexibleobject.cpp
+++ b/indra/newview/llflexibleobject.cpp
@@ -36,7 +36,7 @@
 #include "llface.h"
 #include "llflexibleobject.h"
 #include "llglheaders.h"
-#include "llsphere.h"
+#include "llrendersphere.h"
 #include "llviewerobject.h"
 #include "llimagegl.h"
 #include "llagent.h"
diff --git a/indra/newview/llglsandbox.cpp b/indra/newview/llglsandbox.cpp
index a29136214c6c88821ffef7512d08ec6b9e58ad17..493b69f99e15a0edab392d2da1954f91fdec00df 100644
--- a/indra/newview/llglsandbox.cpp
+++ b/indra/newview/llglsandbox.cpp
@@ -53,7 +53,7 @@
 #include "lltoolmgr.h"
 #include "llselectmgr.h"
 #include "llhudmanager.h"
-#include "llsphere.h"
+#include "llrendersphere.h"
 #include "llviewerobjectlist.h"
 #include "lltoolselectrect.h"
 #include "llviewerwindow.h"
diff --git a/indra/newview/llhudeffect.cpp b/indra/newview/llhudeffect.cpp
index 1385141bd0b8ec77ccb1d11e0b6a08bc96e8bbaa..83fdb66fe5c88e869b5fe41de3d65f8da9a297b5 100644
--- a/indra/newview/llhudeffect.cpp
+++ b/indra/newview/llhudeffect.cpp
@@ -36,7 +36,7 @@
 #include "message.h"
 #include "llgl.h"
 #include "llagent.h"
-#include "llsphere.h"
+#include "llrendersphere.h"
 #include "llimagegl.h"
 
 #include "llviewerobjectlist.h"
diff --git a/indra/newview/llhudeffectbeam.cpp b/indra/newview/llhudeffectbeam.cpp
index 613a234ba5f0c34dc4a155f9d86b234cd04ed137..9fae0e3387290715ac709b1b90fd6a18ac77db89 100644
--- a/indra/newview/llhudeffectbeam.cpp
+++ b/indra/newview/llhudeffectbeam.cpp
@@ -43,7 +43,7 @@
 #include "llglheaders.h"
 #include "llhudrender.h"
 #include "llimagegl.h"
-#include "llsphere.h"
+#include "llrendersphere.h"
 #include "llviewercamera.h"
 #include "llvoavatar.h"
 #include "llviewercontrol.h"
diff --git a/indra/newview/llhudeffectlookat.cpp b/indra/newview/llhudeffectlookat.cpp
index 920a1caaf365f77185305d51a5be7aafa1893846..613f310b0899831fa5b10d5206cf84c226856793 100644
--- a/indra/newview/llhudeffectlookat.cpp
+++ b/indra/newview/llhudeffectlookat.cpp
@@ -40,7 +40,7 @@
 #include "llvoavatar.h"
 #include "lldrawable.h"
 #include "llviewerobjectlist.h"
-#include "llsphere.h"
+#include "llrendersphere.h"
 #include "llselectmgr.h"
 #include "llglheaders.h"
 
diff --git a/indra/newview/llmanipscale.cpp b/indra/newview/llmanipscale.cpp
index cd9cd83968982ee10fc43dfc30b7e94f942ccd1c..a2d6909b282f9afa5b9d25d359e6e3271df8a33f 100644
--- a/indra/newview/llmanipscale.cpp
+++ b/indra/newview/llmanipscale.cpp
@@ -962,8 +962,8 @@ void LLManipScale::dragCorner( S32 x, S32 y )
 		mInSnapRegime = FALSE;
 	}
 
-	F32 max_scale_factor = MAX_OBJECT_SCALE / MIN_OBJECT_SCALE;
-	F32 min_scale_factor = MIN_OBJECT_SCALE / MAX_OBJECT_SCALE;
+	F32 max_scale_factor = DEFAULT_MAX_PRIM_SCALE / MIN_PRIM_SCALE;
+	F32 min_scale_factor = MIN_PRIM_SCALE / DEFAULT_MAX_PRIM_SCALE;
 
 	// find max and min scale factors that will make biggest object hit max absolute scale and smallest object hit min absolute scale
 	for (LLObjectSelection::iterator iter = mObjectSelection->begin();
@@ -975,10 +975,10 @@ void LLManipScale::dragCorner( S32 x, S32 y )
 		{
 			const LLVector3& scale = selectNode->mSavedScale;
 
-			F32 cur_max_scale_factor = llmin( MAX_OBJECT_SCALE / scale.mV[VX], MAX_OBJECT_SCALE / scale.mV[VY], MAX_OBJECT_SCALE / scale.mV[VZ] );
+			F32 cur_max_scale_factor = llmin( DEFAULT_MAX_PRIM_SCALE / scale.mV[VX], DEFAULT_MAX_PRIM_SCALE / scale.mV[VY], DEFAULT_MAX_PRIM_SCALE / scale.mV[VZ] );
 			max_scale_factor = llmin( max_scale_factor, cur_max_scale_factor );
 
-			F32 cur_min_scale_factor = llmax( MIN_OBJECT_SCALE / scale.mV[VX], MIN_OBJECT_SCALE / scale.mV[VY], MIN_OBJECT_SCALE / scale.mV[VZ] );
+			F32 cur_min_scale_factor = llmax( MIN_PRIM_SCALE / scale.mV[VX], MIN_PRIM_SCALE / scale.mV[VY], MIN_PRIM_SCALE / scale.mV[VZ] );
 			min_scale_factor = llmax( min_scale_factor, cur_min_scale_factor );
 		}
 	}
@@ -1270,7 +1270,7 @@ void LLManipScale::stretchFace( const LLVector3& drag_start_agent, const LLVecto
 
 			F32 denom = axis * dir_local;
 			F32 desired_delta_size	= is_approx_zero(denom) ? 0.f : (delta_local_mag / denom);  // in meters
-			F32 desired_scale		= llclamp(selectNode->mSavedScale.mV[axis_index] + desired_delta_size, MIN_OBJECT_SCALE, MAX_OBJECT_SCALE);
+			F32 desired_scale		= llclamp(selectNode->mSavedScale.mV[axis_index] + desired_delta_size, MIN_PRIM_SCALE, DEFAULT_MAX_PRIM_SCALE);
 			// propagate scale constraint back to position offset
 			desired_delta_size		= desired_scale - selectNode->mSavedScale.mV[axis_index]; // propagate constraint back to position
 
@@ -1968,7 +1968,7 @@ F32		LLManipScale::partToMaxScale( S32 part, const LLBBox &bbox ) const
 			max_extent = bbox_extents.mV[i];
 		}
 	}
-	max_scale_factor = bbox_extents.magVec() * MAX_OBJECT_SCALE / max_extent;
+	max_scale_factor = bbox_extents.magVec() * DEFAULT_MAX_PRIM_SCALE / max_extent;
 
 	if (getUniform())
 	{
@@ -1983,7 +1983,7 @@ F32		LLManipScale::partToMinScale( S32 part, const LLBBox &bbox ) const
 {
 	LLVector3 bbox_extents = unitVectorToLocalBBoxExtent( partToUnitVector( part ), bbox );
 	bbox_extents.abs();
-	F32 min_extent = MAX_OBJECT_SCALE;
+	F32 min_extent = DEFAULT_MAX_PRIM_SCALE;
 	for (U32 i = VX; i <= VZ; i++)
 	{
 		if (bbox_extents.mV[i] > 0.f && bbox_extents.mV[i] < min_extent)
@@ -1991,7 +1991,7 @@ F32		LLManipScale::partToMinScale( S32 part, const LLBBox &bbox ) const
 			min_extent = bbox_extents.mV[i];
 		}
 	}
-	F32 min_scale_factor = bbox_extents.magVec() * MIN_OBJECT_SCALE / min_extent;
+	F32 min_scale_factor = bbox_extents.magVec() * MIN_PRIM_SCALE / min_extent;
 
 	if (getUniform())
 	{
diff --git a/indra/newview/llmaniptranslate.cpp b/indra/newview/llmaniptranslate.cpp
index 9856b47830499e7f018abaf49a700f267c3eb92e..c55f9f806a20197b9ca113f970f76caad4ae317c 100644
--- a/indra/newview/llmaniptranslate.cpp
+++ b/indra/newview/llmaniptranslate.cpp
@@ -53,7 +53,7 @@
 #include "llhudrender.h"
 #include "llresmgr.h"
 #include "llselectmgr.h"
-#include "llsphere.h"
+#include "llrendersphere.h"
 #include "llstatusbar.h"
 #include "lltoolmgr.h"
 #include "llviewercamera.h"
diff --git a/indra/newview/llpanelobject.cpp b/indra/newview/llpanelobject.cpp
index 83f72160e1c3185d85a27aae31f084a090811935..d6ac5908dcee0ca1186a2918e534094ff54c6e36 100644
--- a/indra/newview/llpanelobject.cpp
+++ b/indra/newview/llpanelobject.cpp
@@ -1666,15 +1666,8 @@ void LLPanelObject::sendPosition()
 				mObject->setPositionEdit(newpos);
 			}
 			LLSelectMgr::getInstance()->sendMultipleUpdate(UPD_POSITION);
-			//mRootObject->sendPositionUpdate();
 
 			LLSelectMgr::getInstance()->updateSelectionCenter();
-
-//			llinfos << "position sent" << llendl;
-		}
-		else
-		{
-//			llinfos << "position not changed" << llendl;
 		}
 	}
 	else
diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
index 331d13d85dee58ab41b3edb9af9383280857b0f2..fb692d257b5776246bb244ef234e59a5e91cb6a0 100644
--- a/indra/newview/llstartup.cpp
+++ b/indra/newview/llstartup.cpp
@@ -3263,6 +3263,41 @@ void init_stat_view()
 	stat_barp->mDisplayBar = FALSE;
 	stat_barp->mDisplayMean = FALSE;
 
+	LLStatView *phys_details_viewp;
+	phys_details_viewp = new LLStatView("phys detail view", "Physics Details", "", rect);
+	sim_statviewp->addChildAtEnd(phys_details_viewp);
+
+	stat_barp = phys_details_viewp->addStat("Pinned Objects", &(LLViewerStats::getInstance()->mPhysicsPinnedTasks));
+	stat_barp->mPrecision = 0;
+	stat_barp->mMinBar = 0.f;
+	stat_barp->mMaxBar = 500.f;
+	stat_barp->mTickSpacing = 10.f;
+	stat_barp->mLabelSpacing = 40.f;
+	stat_barp->mPerSec = FALSE;
+	stat_barp->mDisplayBar = FALSE;
+	stat_barp->mDisplayMean = FALSE;
+
+	stat_barp = phys_details_viewp->addStat("Low LOD Objects", &(LLViewerStats::getInstance()->mPhysicsLODTasks));
+	stat_barp->mPrecision = 0;
+	stat_barp->mMinBar = 0.f;
+	stat_barp->mMaxBar = 500.f;
+	stat_barp->mTickSpacing = 10.f;
+	stat_barp->mLabelSpacing = 40.f;
+	stat_barp->mPerSec = FALSE;
+	stat_barp->mDisplayBar = FALSE;
+	stat_barp->mDisplayMean = FALSE;
+
+	stat_barp = phys_details_viewp->addStat("Memory Allocated", &(LLViewerStats::getInstance()->mPhysicsMemoryAllocated));
+	stat_barp->setUnitLabel(" MB");
+	stat_barp->mPrecision = 0;
+	stat_barp->mMinBar = 0.f;
+	stat_barp->mMaxBar = 1024.f;
+	stat_barp->mTickSpacing = 128.f;
+	stat_barp->mLabelSpacing = 256.f;
+	stat_barp->mPerSec = FALSE;
+	stat_barp->mDisplayBar = FALSE;
+	stat_barp->mDisplayMean = FALSE;
+
 	stat_barp = sim_statviewp->addStat("Agent Updates/Sec", &(LLViewerStats::getInstance()->mSimAgentUPS));
 	stat_barp->mPrecision = 1;
 	stat_barp->mMinBar = 0.f;
@@ -3424,6 +3459,44 @@ void init_stat_view()
 	stat_barp->mDisplayBar = FALSE;
 	stat_barp->mDisplayMean = FALSE;
 
+	LLStatView *physics_time_viewp;
+	physics_time_viewp = new LLStatView("physics perf view", "Physics Details (ms)", "", rect);
+	sim_time_viewp->addChildAtEnd(physics_time_viewp);
+	{
+		stat_barp = physics_time_viewp->addStat("Physics Step", &(LLViewerStats::getInstance()->mSimSimPhysicsStepMsec));
+		stat_barp->setUnitLabel("ms");
+		stat_barp->mPrecision = 1;
+		stat_barp->mMinBar = 0.f;
+		stat_barp->mMaxBar = 40.f;
+		stat_barp->mTickSpacing = 10.f;
+		stat_barp->mLabelSpacing = 20.f;
+		stat_barp->mPerSec = FALSE;
+		stat_barp->mDisplayBar = FALSE;
+		stat_barp->mDisplayMean = FALSE;
+
+		stat_barp = physics_time_viewp->addStat("Update Shapes", &(LLViewerStats::getInstance()->mSimSimPhysicsShapeUpdateMsec));
+		stat_barp->setUnitLabel("ms");
+		stat_barp->mPrecision = 1;
+		stat_barp->mMinBar = 0.f;
+		stat_barp->mMaxBar = 40.f;
+		stat_barp->mTickSpacing = 10.f;
+		stat_barp->mLabelSpacing = 20.f;
+		stat_barp->mPerSec = FALSE;
+		stat_barp->mDisplayBar = FALSE;
+		stat_barp->mDisplayMean = FALSE;
+
+		stat_barp = physics_time_viewp->addStat("Other", &(LLViewerStats::getInstance()->mSimSimPhysicsOtherMsec));
+		stat_barp->setUnitLabel("ms");
+		stat_barp->mPrecision = 1;
+		stat_barp->mMinBar = 0.f;
+		stat_barp->mMaxBar = 40.f;
+		stat_barp->mTickSpacing = 10.f;
+		stat_barp->mLabelSpacing = 20.f;
+		stat_barp->mPerSec = FALSE;
+		stat_barp->mDisplayBar = FALSE;
+		stat_barp->mDisplayMean = FALSE;
+	}
+
 	stat_barp = sim_time_viewp->addStat("Sim Time (Other)", &(LLViewerStats::getInstance()->mSimSimOtherMsec));
 	stat_barp->setUnitLabel("ms");
 	stat_barp->mPrecision = 1;
diff --git a/indra/newview/llviewercamera.cpp b/indra/newview/llviewercamera.cpp
index 517a02b4ad4b84f3367c3472c88892efbc1597ca..8657f59ccbeb7f512a10a4043be91d4b177b2c5e 100644
--- a/indra/newview/llviewercamera.cpp
+++ b/indra/newview/llviewercamera.cpp
@@ -168,7 +168,7 @@ void LLViewerCamera::calcProjection(const F32 far_distance) const
 
 	f = 1/tan(fov_y*0.5f);
 
-	mProjectionMatrix.zero();
+	mProjectionMatrix.setZero();
 	mProjectionMatrix.mMatrix[0][0] = f/aspect;
 	mProjectionMatrix.mMatrix[1][1] = f;
 	mProjectionMatrix.mMatrix[2][2] = (z_far + z_near)/(z_near - z_far);
diff --git a/indra/newview/llviewerjoint.cpp b/indra/newview/llviewerjoint.cpp
index 04de6bed92dd7f5b19dfb9e8208d4a152309e360..b1cad86a610b0db4b7f0a2922796c37742af0909 100644
--- a/indra/newview/llviewerjoint.cpp
+++ b/indra/newview/llviewerjoint.cpp
@@ -40,7 +40,7 @@
 #include "llglimmediate.h"
 #include "llmath.h"
 #include "llglheaders.h"
-#include "llsphere.h"
+#include "llrendersphere.h"
 #include "llvoavatar.h"
 #include "pipeline.h"
 
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index 4d7ef5e2fd00d162f18dc3a9715965fcdceba0c7..4234d4862c9c584f9bebd6b5ea29072d089f5dac 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -1193,7 +1193,6 @@ void init_debug_ui_menu(LLMenuGL* menu)
 	menu->append(new LLMenuItemCallGL( "Dump SelectMgr", &dump_select_mgr));
 	menu->append(new LLMenuItemCallGL( "Dump Inventory", &dump_inventory));
 	menu->append(new LLMenuItemCallGL( "Dump Focus Holder", &handle_dump_focus, NULL, NULL, 'F', MASK_ALT | MASK_CONTROL));
-	menu->append(new LLMenuItemCallGL( "Dump VolumeMgr",	&dump_volume_mgr, NULL, NULL));
 	menu->append(new LLMenuItemCallGL( "Print Selected Object Info",	&print_object_info, NULL, NULL, 'P', MASK_CONTROL|MASK_SHIFT ));
 	menu->append(new LLMenuItemCallGL( "Print Agent Info",			&print_agent_nvpairs, NULL, NULL, 'P', MASK_SHIFT ));
 	menu->append(new LLMenuItemCallGL( "Texture Memory Stats",  &output_statistics, NULL, NULL, 'M', MASK_SHIFT | MASK_ALT | MASK_CONTROL));
@@ -5219,11 +5218,6 @@ void dump_select_mgr(void*)
 	LLSelectMgr::getInstance()->dump();
 }
 
-void dump_volume_mgr(void*)
-{
-	gVolumeMgr->dump();
-}
-
 void dump_inventory(void*)
 {
 	gInventory.dumpInventory();
diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp
index 938034a00941c34340a6e3ff685d49074c1f1c20..439063e4399f5623be70d4dc6c763e45a664118b 100644
--- a/indra/newview/llviewermessage.cpp
+++ b/indra/newview/llviewermessage.cpp
@@ -3490,6 +3490,24 @@ void process_sim_stats(LLMessageSystem *msg, void **user_data)
 		case LL_SIM_STAT_TOTAL_UNACKED_BYTES:
 			LLViewerStats::getInstance()->mSimTotalUnackedBytes.addValue(stat_value / 1024.f);
 			break;
+		case LL_SIM_STAT_PHYSICS_PINNED_TASKS:
+			LLViewerStats::getInstance()->mPhysicsPinnedTasks.addValue(stat_value);
+			break;
+		case LL_SIM_STAT_PHYSICS_LOD_TASKS:
+			LLViewerStats::getInstance()->mPhysicsLODTasks.addValue(stat_value);
+			break;
+		case LL_SIM_STAT_SIMPHYSICSSTEPMS:
+			LLViewerStats::getInstance()->mSimSimPhysicsStepMsec.addValue(stat_value);
+			break;
+		case LL_SIM_STAT_SIMPHYSICSSHAPEMS:
+			LLViewerStats::getInstance()->mSimSimPhysicsShapeUpdateMsec.addValue(stat_value);
+			break;
+		case LL_SIM_STAT_SIMPHYSICSOTHERMS:
+			LLViewerStats::getInstance()->mSimSimPhysicsOtherMsec.addValue(stat_value);
+			break;
+		case LL_SIM_STAT_SIMPHYSICSMEMORY:
+			LLViewerStats::getInstance()->mPhysicsMemoryAllocated.addValue(stat_value);
+			break;
 		default:
 // 			llwarns << "Unknown stat id" << stat_id << llendl;
 		  break;
diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp
index d32eb6414c88bd31441564e2e043546c92c71bea..30668172f1e6aedbdd2a55e6546fe66716a8f6c4 100644
--- a/indra/newview/llviewerobject.cpp
+++ b/indra/newview/llviewerobject.cpp
@@ -67,7 +67,7 @@
 #include "llfollowcam.h"
 #include "llnetmap.h"
 #include "llselectmgr.h"
-#include "llsphere.h"
+#include "llrendersphere.h"
 #include "lltooldraganddrop.h"
 #include "llviewercamera.h"
 #include "llviewerimagelist.h"
@@ -202,7 +202,7 @@ LLViewerObject::LLViewerObject(const LLUUID &id, const LLPCode pcode, LLViewerRe
 {
 	llassert(mRegionp);
 
-	LLPrimitive::init(pcode);
+	LLPrimitive::init_primitive(pcode);
 
 	// CP: added 12/2/2005 - this was being initialised to 0, not the current frame time
 	mLastInterpUpdateSecs = LLFrameTimer::getElapsedSeconds();
@@ -723,6 +723,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys,
 	LLVector3 new_vel;
 	LLVector3 new_acc;
 	LLVector3 new_angv;
+	LLVector3 old_angv = getAngularVelocity();
 	LLQuaternion new_rot;
 	LLVector3 new_scale = getScale();
 
@@ -1857,7 +1858,8 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys,
 		}
 	}
 
-	if (new_rot != mLastRot)
+	if (new_rot != mLastRot
+		|| new_angv != old_angv)
 	{
 		mLastRot = new_rot;
 		setChanged(ROTATED | SILHOUETTE);
@@ -1974,7 +1976,7 @@ BOOL LLViewerObject::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
 		F32 dt_raw = (F32)(time - mLastInterpUpdateSecs);
 		F32 dt = mTimeDilation * dt_raw;
 
-		if (!mUserSelected && !mJointInfo)
+		if (!mJointInfo)
 		{
 			applyAngularVelocity(dt);
 		}
@@ -2060,9 +2062,9 @@ BOOL LLViewerObject::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
 		else
 		{
 			// linear motion
-			// HAVOK_TIMESTEP is used below to correct for the fact that the velocity in object
+			// PHYSICS_TIMESTEP is used below to correct for the fact that the velocity in object
 			// updates represents the average velocity of the last timestep, rather than the final velocity.
-			// the time dilation above should guarrantee that dt is never less than HAVOK_TIMESTEP, theoretically
+			// the time dilation above should guarantee that dt is never less than PHYSICS_TIMESTEP, theoretically
 			// 
 			// There is a problem here if dt is negative. . .
 
@@ -2074,7 +2076,7 @@ BOOL LLViewerObject::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
 			
 			if (!(accel.isExactlyZero() && vel.isExactlyZero()))
 			{
-				LLVector3 pos 	= (vel + (0.5f * (dt-HAVOK_TIMESTEP)) * accel) * dt;	
+				LLVector3 pos 	= (vel + (0.5f * (dt-PHYSICS_TIMESTEP)) * accel) * dt;	
 			
 				// region local  
 				setPositionRegion(pos + getPositionRegion());
@@ -3526,36 +3528,21 @@ void LLViewerObject::sendRotationUpdate() const
 	gMessageSystem->sendReliable( regionp->getHost() );
 }
 
-// formerly send_object_position_global
-void LLViewerObject::sendPositionUpdate() const
-{
-	gMessageSystem->newMessageFast(_PREHASH_ObjectPosition);
-	gMessageSystem->nextBlockFast(_PREHASH_AgentData);
-	gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
-	gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
-	gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
-	gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID,	mLocalID );
-	gMessageSystem->addVector3Fast(_PREHASH_Position, getPositionRegion());
-	LLViewerRegion* regionp = getRegion();
-	gMessageSystem->sendReliable(regionp->getHost());
-}
-
-
-//formerly send_object_scale
-void LLViewerObject::sendScaleUpdate()
-{
-	gMessageSystem->newMessageFast(_PREHASH_ObjectScale);
-	gMessageSystem->nextBlockFast(_PREHASH_AgentData);
-	gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
-	gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
-	gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
-	gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID,	mLocalID );
-	gMessageSystem->addVector3Fast(_PREHASH_Scale,	(getScale()));
-
-	LLViewerRegion *regionp = getRegion();
-	gMessageSystem->sendReliable(regionp->getHost() );
-}
-
+/* Obsolete, we use MultipleObjectUpdate instead
+//// formerly send_object_position_global
+//void LLViewerObject::sendPositionUpdate() const
+//{
+//	gMessageSystem->newMessageFast(_PREHASH_ObjectPosition);
+//	gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+//	gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+//	gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+//	gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+//	gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID,	mLocalID );
+//	gMessageSystem->addVector3Fast(_PREHASH_Position, getPositionRegion());
+//	LLViewerRegion* regionp = getRegion();
+//	gMessageSystem->sendReliable(regionp->getHost());
+//}
+*/
 
 //formerly send_object_shape(LLViewerObject *object)
 void LLViewerObject::sendShapeUpdate()
diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h
index 06cf2b2266f871378d7d7a2723e02cd706604830..20616b32d6f078f992b34ccf30a59cd3934c8d62 100644
--- a/indra/newview/llviewerobject.h
+++ b/indra/newview/llviewerobject.h
@@ -268,7 +268,6 @@ class LLViewerObject : public LLPrimitive, public LLRefCount
 	void setPositionAgent(const LLVector3 &pos_agent, BOOL damped = FALSE);
 	void setPositionParent(const LLVector3 &pos_parent, BOOL damped = FALSE);
 	void setPositionAbsoluteGlobal( const LLVector3d &pos_global, BOOL damped = FALSE );
-	void sendPositionUpdate() const;
 
 	virtual const LLMatrix4& getWorldMatrix(LLXformMatrix* xform) const		{ return xform->getWorldMatrix(); }
 
@@ -303,7 +302,6 @@ class LLViewerObject : public LLPrimitive, public LLRefCount
 	void sendTEUpdate() const;			// Sends packed representation of all texture entry information
 	
 	virtual void setScale(const LLVector3 &scale, BOOL damped = FALSE);
-	void sendScaleUpdate();
 
 	void sendShapeUpdate();
 
diff --git a/indra/newview/llviewerpartsim.cpp b/indra/newview/llviewerpartsim.cpp
index 6cfef5b18fb98642ea21179209b825da3278e56a..ccf7a5d1d7f35f1dd10a7c74c62082c109941886 100644
--- a/indra/newview/llviewerpartsim.cpp
+++ b/indra/newview/llviewerpartsim.cpp
@@ -243,7 +243,7 @@ void LLViewerPartGroup::updateParticles(const F32 lastdt)
 	S32 i;
 	F32 dt;
 	
-	LLVector3 gravity(0.f, 0.f, -9.8f);
+	LLVector3 gravity(0.f, 0.f, GRAVITY);
 
 	LLViewerRegion *regionp = getRegion();
 	S32 end = (S32) mParticles.size();
diff --git a/indra/newview/llviewerstats.h b/indra/newview/llviewerstats.h
index 819438832dd7a12905eba17debba86164338228d..bd16e61149a617b8ac0105353c87e15eea578b25 100644
--- a/indra/newview/llviewerstats.h
+++ b/indra/newview/llviewerstats.h
@@ -69,6 +69,11 @@ class LLViewerStats : public LLSingleton<LLViewerStats>
 	LLStat mSimNetMsec;
 	LLStat mSimSimOtherMsec;
 	LLStat mSimSimPhysicsMsec;
+
+	LLStat mSimSimPhysicsStepMsec;
+	LLStat mSimSimPhysicsShapeUpdateMsec;
+	LLStat mSimSimPhysicsOtherMsec;
+
 	LLStat mSimAgentMsec;
 	LLStat mSimImagesMsec;
 	LLStat mSimScriptMsec;
@@ -86,6 +91,9 @@ class LLViewerStats : public LLSingleton<LLViewerStats>
 	LLStat mSimPendingLocalUploads;
 	LLStat mSimTotalUnackedBytes;
 
+	LLStat mPhysicsPinnedTasks;
+	LLStat mPhysicsLODTasks;
+	LLStat mPhysicsMemoryAllocated;
 	/*
 	LLStat mSimCPUUsageStat;
 	LLStat mSimMemTotalStat;
diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
index 5c3128b8e0f8ef99a6096c52231203810f68e208..697aea8582ca1bb9acf7b64b00ef35c3afb98d0e 100644
--- a/indra/newview/llviewerwindow.cpp
+++ b/indra/newview/llviewerwindow.cpp
@@ -131,7 +131,7 @@
 #include "llresmgr.h"
 #include "llrootview.h"
 #include "llselectmgr.h"
-#include "llsphere.h"
+#include "llrendersphere.h"
 #include "llstartup.h"
 #include "llstatusbar.h"
 #include "llstatview.h"
diff --git a/indra/newview/llvotextbubble.cpp b/indra/newview/llvotextbubble.cpp
index 775b1ec61d74d41f9dc7829b25d10fbd52eb9c18..b48a5a989ccb7fd2dba80c7ea4044de04bbf916d 100644
--- a/indra/newview/llvotextbubble.cpp
+++ b/indra/newview/llvotextbubble.cpp
@@ -36,7 +36,7 @@
 #include "imageids.h"
 #include "llviewercontrol.h"
 #include "llprimitive.h"
-#include "llsphere.h"
+#include "llrendersphere.h"
 
 #include "llagent.h"
 #include "llbox.h"
diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp
index a1b3c32e0148b04f899a829922ccc9ed6042cb9b..2b8cf93b2d9ef87fef56b7d7d9dba060ac947319 100644
--- a/indra/newview/llvovolume.cpp
+++ b/indra/newview/llvovolume.cpp
@@ -83,8 +83,8 @@ LLVOVolume::LLVOVolume(const LLUUID &id, const LLPCode pcode, LLViewerRegion *re
 	  mVolumeImpl(NULL)
 {
 	mTexAnimMode = 0;
-	mRelativeXform.identity();
-	mRelativeXformInvTrans.identity();
+	mRelativeXform.setIdentity();
+	mRelativeXformInvTrans.setIdentity();
 
 	mLOD = MIN_LOD;
 	mTextureAnimp = NULL;
@@ -326,7 +326,7 @@ void LLVOVolume::animateTextures()
 			}
 
 			LLMatrix4& tex_mat = *facep->mTextureMatrix;
-			tex_mat.identity();
+			tex_mat.setIdentity();
 			tex_mat.translate(LLVector3(-0.5f, -0.5f, 0.f));
 			tex_mat.rotate(quat);				
 
diff --git a/indra/newview/llvowater.cpp b/indra/newview/llvowater.cpp
index 8755a5ae4a880ab6f10493cf2be77109a7d51970..3cc834d32325f00cad614c9d5024bde5eb7ab48a 100644
--- a/indra/newview/llvowater.cpp
+++ b/indra/newview/llvowater.cpp
@@ -67,7 +67,6 @@ const U32 N_RES_HALF	= (N_RES >> 1);
 const U32 WIDTH			= (N_RES * WAVE_STEP); //128.f //64		// width of wave tile, in meters
 const F32 WAVE_STEP_INV	= (1. / WAVE_STEP);
 
-const F32 g				= 9.81f;          // gravitational constant (m/s^2)
 
 LLVOWater::LLVOWater(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp)
 :	LLStaticViewerObject(id, LL_VO_WATER, regionp)
diff --git a/indra/newview/llworld.cpp b/indra/newview/llworld.cpp
index 923b45fc5a2b31a4af24a727868bdad5c99a6a64..ca947bed977171f689dc1310018e884e180a07da 100644
--- a/indra/newview/llworld.cpp
+++ b/indra/newview/llworld.cpp
@@ -527,6 +527,11 @@ F32 LLWorld::resolveStepHeightGlobal(const LLVOAvatar* avatarp, const LLVector3d
 			intersection.mdV[VZ] -= norm_dist_from_plane * segment_length;
 			intersection_normal = foot_plane_normal;
 		}
+		else
+		{
+			intersection = land_intersection;
+			intersection_normal = resolveLandNormalGlobal(land_intersection);
+		}
 	}
 
 	return normalized_land_distance;
diff --git a/indra/newview/llworld.h b/indra/newview/llworld.h
index cb6102268eb797314bd05c50dc7dda48d915764f..9dd90480b629e7a18f5af1191029683218cf1264 100644
--- a/indra/newview/llworld.h
+++ b/indra/newview/llworld.h
@@ -113,7 +113,7 @@ class LLWorld : public LLSingleton<LLWorld>
 	// region X and Y size in meters
 	F32						getRegionWidthInMeters() const	{ return mWidthInMeters; }
 	F32						getRegionMinHeight() const		{ return -mWidthInMeters; }
-	F32						getRegionMaxHeight() const		{ return 3.f*mWidthInMeters; }
+	F32						getRegionMaxHeight() const		{ return MAX_OBJECT_Z; }
 
 	void					updateRegions(F32 max_update_time);
 	void					updateVisibilities();
diff --git a/indra/newview/macview_Prefix.h b/indra/newview/macview_Prefix.h
index ac19cd924e997eaf358c8cf67b265693f571fdd0..b6dcc1d127e8f8c5c5c2f36deb4a2b730f99fe5c 100644
--- a/indra/newview/macview_Prefix.h
+++ b/indra/newview/macview_Prefix.h
@@ -82,7 +82,7 @@
 #include "llmoveview.h"
 #include "llselectmgr.h"
 #include "llsky.h"
-#include "llsphere.h"
+#include "llrendersphere.h"
 #include "llstatusbar.h"
 #include "lltalkview.h"
 #include "lltool.h"
diff --git a/indra/test/io.cpp b/indra/test/io.cpp
index c322522ce34ad9f9b8f9f9334abc7987e039431f..3de1e8edef01b45a05f4011dadcd9fa19b99eb7a 100644
--- a/indra/test/io.cpp
+++ b/indra/test/io.cpp
@@ -1151,7 +1151,7 @@ namespace tut
 		chain.push_back(LLIOPipe::ptr_t(new LLPipeStringInjector("hi")));
 		chain.push_back(LLIOPipe::ptr_t(new LLIOSocketWriter(client)));
 		chain.push_back(LLIOPipe::ptr_t(new LLIONull));
-		mPump->addChain(chain, 0.2);
+		mPump->addChain(chain, 0.2f);
 		chain.clear();
 
 		// pump for a bit and make sure all 3 chains are running
diff --git a/indra/test/lltut.h b/indra/test/lltut.h
index 1827624dbf50da1d0735637bc56e607e199b9195..375d55818221e2260294c7c7523e83a785457014 100644
--- a/indra/test/lltut.h
+++ b/indra/test/lltut.h
@@ -44,6 +44,16 @@ class LLSD;
 
 namespace tut
 {
+	inline void ensure_approximately_equals(const char* msg, F64 actual, F64 expected, U32 frac_bits)
+	{
+		if(!is_approx_equal_fraction(actual, expected, frac_bits))
+		{
+			std::stringstream ss;
+			ss << (msg?msg:"") << (msg?": ":"") << "not equal actual: " << actual << " expected: " << expected;
+			throw tut::failure(ss.str().c_str());
+		}
+	}
+
 	inline void ensure_approximately_equals(const char* msg, F32 actual, F32 expected, U32 frac_bits)
 	{
 		if(!is_approx_equal_fraction(actual, expected, frac_bits))
diff --git a/indra/test/prim_linkability_tut.cpp b/indra/test/prim_linkability_tut.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d2236a5cff5b55ea02e7bdbbe70afe257c47e1f2
--- /dev/null
+++ b/indra/test/prim_linkability_tut.cpp
@@ -0,0 +1,477 @@
+/** 
+ * @file linkability.cpp
+ * @author andrew@lindenlab.com
+ * @date 2007-04-23
+ * @brief Tests for the LLPrimLinkInfo template which computes the linkability of prims
+ *
+ * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "lltut.h"
+#include "llprimlinkinfo.h"
+#include "llrand.h"
+
+
+// helper function
+void randomize_sphere(LLSphere& sphere, F32 center_range, F32 radius_range)
+{
+	F32 radius = ll_frand(2.f * radius_range) - radius_range;
+	LLVector3 center;
+	for (S32 i=0; i<3; ++i)
+	{
+		center.mV[i] = ll_frand(2.f * center_range) - center_range;
+	}
+	sphere.setRadius(radius);
+	sphere.setCenter(center);
+}
+
+// helper function.  Same as above with a min and max radius.
+void randomize_sphere(LLSphere& sphere, F32 center_range, F32 minimum_radius, F32 maximum_radius)
+{
+	F32 radius = ll_frand(maximum_radius - minimum_radius) + minimum_radius; 
+	LLVector3 center;
+	for (S32 i=0; i<3; ++i)
+	{
+		center.mV[i] = ll_frand(2.f * center_range) - center_range;
+	}
+	sphere.setRadius(radius);
+	sphere.setCenter(center);
+}
+
+// helper function
+bool random_sort( const LLPrimLinkInfo< S32 >&, const LLPrimLinkInfo< S32 >& b)
+{
+	return (ll_rand(64) < 32);
+}
+
+namespace tut
+{
+	struct linkable_data
+	{
+		LLPrimLinkInfo<S32> info;
+	};
+
+	typedef test_group<linkable_data> linkable_test;
+	typedef linkable_test::object linkable_object;
+	tut::linkable_test wtf("prim linkability");
+
+	template<> template<>
+	void linkable_object::test<1>()
+	{
+		// Here we test the boundary of the LLPrimLinkInfo::canLink() method 
+		// between semi-random middle-sized objects.
+
+		S32 number_of_tests = 100;
+		for (S32 test = 0; test < number_of_tests; ++test)
+		{
+			// compute the radii that would provide the above max link distance
+			F32 first_radius = 0.f;
+			F32 second_radius = 0.f;
+			
+			// compute a random center for the first sphere
+			// compute some random max link distance
+			F32 max_link_span = ll_frand(MAX_OBJECT_SPAN);
+			if (max_link_span < OBJECT_SPAN_BONUS)
+			{
+				max_link_span += OBJECT_SPAN_BONUS;
+			}
+			LLVector3 first_center(
+					ll_frand(2.f * max_link_span) - max_link_span, 
+					ll_frand(2.f * max_link_span) - max_link_span, 
+					ll_frand(2.f * max_link_span) - max_link_span);
+
+			// put the second sphere at the right distance from the origin
+			// such that it is within the max_link_distance of the first
+			LLVector3 direction(ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f);
+			direction.normalize();
+			F32 half_milimeter = 0.0005f;
+			LLVector3 second_center;
+
+			// max_span = 3 * (first_radius + second_radius) + OBJECT_SPAN_BONUS
+			// make sure they link at short distances
+			{
+				second_center = first_center + (OBJECT_SPAN_BONUS - half_milimeter) * direction;
+				LLPrimLinkInfo<S32> first_info(0, LLSphere(first_center, first_radius) );
+				LLPrimLinkInfo<S32> second_info(1, LLSphere(second_center, second_radius) );
+				ensure("these nearby objects should link", first_info.canLink(second_info) );
+			}
+
+			// make sure they fail to link if we move them apart just a little bit
+			{
+				second_center = first_center + (OBJECT_SPAN_BONUS + half_milimeter) * direction;
+				LLPrimLinkInfo<S32> first_info(0, LLSphere(first_center, first_radius) );
+				LLPrimLinkInfo<S32> second_info(1, LLSphere(second_center, second_radius) );
+				ensure("these nearby objects should NOT link", !first_info.canLink(second_info) );
+			}
+
+			// make sure the objects link or not at medium distances
+			{
+				first_radius = 0.3f * ll_frand(max_link_span - OBJECT_SPAN_BONUS);
+
+				// This is the exact second radius that will link at exactly our random max_link_distance
+				second_radius = ((max_link_span - OBJECT_SPAN_BONUS) / 3.f) - first_radius;
+				second_center = first_center + (max_link_span - first_radius - second_radius - half_milimeter) * direction;
+
+				LLPrimLinkInfo<S32> first_info(0, LLSphere(first_center, first_radius) );
+				LLPrimLinkInfo<S32> second_info(1, LLSphere(second_center, second_radius) );
+
+				ensure("these objects should link", first_info.canLink(second_info) );
+			}
+
+			// make sure they fail to link if we move them apart just a little bit
+			{
+				// move the second sphere such that it is a little too far from the first
+				second_center += (2.f * half_milimeter) * direction;
+				LLPrimLinkInfo<S32> first_info(0, LLSphere(first_center, first_radius) );
+				LLPrimLinkInfo<S32> second_info(1, LLSphere(second_center, second_radius) );
+				
+				ensure("these objects should NOT link", !first_info.canLink(second_info) );
+			}
+
+			// make sure things don't link at far distances
+			{
+				second_center = first_center + (MAX_OBJECT_SPAN + 2.f * half_milimeter) * direction;
+				second_radius = 0.3f * MAX_OBJECT_SPAN;
+				LLPrimLinkInfo<S32> first_info(0, LLSphere(first_center, first_radius) );
+				LLPrimLinkInfo<S32> second_info(1, LLSphere(second_center, second_radius) );
+				ensure("these objects should NOT link", !first_info.canLink(second_info) );
+			}
+			
+		}
+	}
+
+	template<> template<>
+	void linkable_object::test<2>()
+	{
+
+		// Consider a row of eight spheres in a row, each 10m in diameter and centered
+		// at 10m intervals:  01234567.
+
+		F32 radius = 5.f;
+		F32 spacing = 10.f;
+
+		LLVector3 line_direction(ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f);
+		line_direction.normalize();
+
+		LLVector3 first_center(ll_frand(2.f * spacing) -spacing, ll_frand(2.f * spacing) - spacing, ll_frand(2.f * spacing) - spacing);
+
+		LLPrimLinkInfo<S32> infos[8];
+
+		for (S32 index = 0; index < 8; ++index)
+		{
+			LLVector3 center = first_center + ((F32)(index) * spacing) * line_direction;
+			infos[index].set(index, LLSphere(center, radius));
+		}
+
+		// Max span for 2 spheres of 5m radius is 3 * (5 + 5) + 1 = 31m
+		// spheres 0&2 have a 30m span (from outside edge to outside edge) and should link
+		{
+			LLPrimLinkInfo<S32> root_info = infos[0];
+			std::list< LLPrimLinkInfo<S32> > info_list;
+			info_list.push_back(infos[2]);
+			root_info.mergeLinkableSet(info_list);
+			S32 prim_count = root_info.getPrimCount();
+			ensure_equals("0&2 prim count should be 2", prim_count, 2);
+			ensure_equals("0&2 unlinkable list should have length 0", (S32) info_list.size(), 0);
+		}
+
+		
+		// spheres 0&3 have a 40 meter span and should NOT link outright
+		{
+			LLPrimLinkInfo<S32> root_info = infos[0];
+			std::list< LLPrimLinkInfo<S32> > info_list;
+			info_list.push_back(infos[3]);
+			root_info.mergeLinkableSet(info_list);
+			S32 prim_count = root_info.getPrimCount();
+			ensure_equals("0&4 prim count should be 1", prim_count, 1);
+			ensure_equals("0&4 unlinkable list should have length 1", (S32) info_list.size(), 1);
+		}
+
+		
+		// spheres 0-4 should link no matter what order : 01234
+		// Total span = 50m, 012 link with a r=15.5 giving max span of 3 * (15.5 + 5) + 1 = 62.5, but the cap is 54m
+		{
+			LLPrimLinkInfo<S32> root_info = infos[0];
+			std::list< LLPrimLinkInfo<S32> > info_list;
+			for (S32 index = 1; index < 5; ++index)
+			{
+				info_list.push_back(infos[index]);
+			}
+			root_info.mergeLinkableSet(info_list);
+			S32 prim_count = root_info.getPrimCount();
+			ensure_equals("01234 prim count should be 5", prim_count, 5);
+			ensure_equals("01234 unlinkable list should have length 0", (S32) info_list.size(), 0);
+		}
+
+		
+		// spheres 0-5 should link no matter what order : 04321
+		{
+			LLPrimLinkInfo<S32> root_info = infos[0];
+			std::list< LLPrimLinkInfo<S32> > info_list;
+			for (S32 index = 4; index > 0; --index)
+			{
+				info_list.push_back(infos[index]);
+			}
+			root_info.mergeLinkableSet(info_list);
+			S32 prim_count = root_info.getPrimCount();
+			ensure_equals("04321 prim count should be 5", prim_count, 5);
+			ensure_equals("04321 unlinkable list should have length 0", (S32) info_list.size(), 0);
+		}
+
+		// spheres 0-4 should link no matter what order : 01423
+		{
+			LLPrimLinkInfo<S32> root_info = infos[0];
+			std::list< LLPrimLinkInfo<S32> > info_list;
+			info_list.push_back(infos[1]);
+			info_list.push_back(infos[4]);
+			info_list.push_back(infos[2]);
+			info_list.push_back(infos[3]);
+			root_info.mergeLinkableSet(info_list);
+			S32 prim_count = root_info.getPrimCount();
+			ensure_equals("01423 prim count should be 5", prim_count, 5);
+			ensure_equals("01423 unlinkable list should have length 0", (S32) info_list.size(), 0);
+		}
+
+		// spheres 0-5 should NOT fully link, only 0-4 
+		{
+			LLPrimLinkInfo<S32> root_info = infos[0];
+			std::list< LLPrimLinkInfo<S32> > info_list;
+			for (S32 index = 1; index < 6; ++index)
+			{
+				info_list.push_back(infos[index]);
+			}
+			root_info.mergeLinkableSet(info_list);
+			S32 prim_count = root_info.getPrimCount();
+			ensure_equals("012345 prim count should be 5", prim_count, 5);
+			ensure_equals("012345 unlinkable list should have length 1", (S32) info_list.size(), 1);
+			std::list< LLPrimLinkInfo<S32> >::iterator info_itr = info_list.begin();
+			if (info_itr != info_list.end())
+			{
+				// examine the contents of the unlinked info
+				std::list<S32> unlinked_indecies;
+				info_itr->getData(unlinked_indecies);
+				// make sure there is only one index in the unlinked_info
+				ensure_equals("012345 unlinkable index count should be 1", (S32) unlinked_indecies.size(), 1);
+				// make sure its value is 6
+				std::list<S32>::iterator unlinked_index_itr = unlinked_indecies.begin();
+				S32 unlinkable_index = *unlinked_index_itr;
+				ensure_equals("012345 unlinkable index should be 5", (S32) unlinkable_index, 5);
+			}
+		}
+		
+		// spheres 0-7 should NOT fully link, only 0-5 
+		{
+			LLPrimLinkInfo<S32> root_info = infos[0];
+			std::list< LLPrimLinkInfo<S32> > info_list;
+			for (S32 index = 1; index < 8; ++index)
+			{
+				info_list.push_back(infos[index]);
+			}
+			root_info.mergeLinkableSet(info_list);
+			S32 prim_count = root_info.getPrimCount();
+			ensure_equals("01234567 prim count should be 5", prim_count, 5);
+			// Should be 1 linkinfo on unlinkable that has 2 prims
+			ensure_equals("01234567 unlinkable list should have length 1", (S32) info_list.size(), 1);
+			std::list< LLPrimLinkInfo<S32> >::iterator info_itr = info_list.begin();
+			if (info_itr != info_list.end())
+			{
+				// make sure there is only one index in the unlinked_info
+				std::list<S32> unlinked_indecies;
+				info_itr->getData(unlinked_indecies);
+				ensure_equals("0123456 unlinkable index count should be 3", (S32) unlinked_indecies.size(), 3);
+
+				// make sure its values are 6 and 7
+				std::list<S32>::iterator unlinked_index_itr = unlinked_indecies.begin();
+				S32 unlinkable_index = *unlinked_index_itr;
+				ensure_equals("0123456 first unlinkable index should be 5", (S32) unlinkable_index, 5);
+				++unlinked_index_itr;
+				unlinkable_index = *unlinked_index_itr;
+				ensure_equals("0123456 second unlinkable index should be 6", (S32) unlinkable_index, 6);
+				++unlinked_index_itr;
+				unlinkable_index = *unlinked_index_itr;
+				ensure_equals("0123456 third unlinkable index should be 7", (S32) unlinkable_index, 7);
+
+			}
+		}
+	}
+
+	template<> template<>
+	void linkable_object::test<3>()
+	{
+		// Here we test the link results between an LLPrimLinkInfo and a set of
+		// randomized LLPrimLinkInfos where the expected results are known.
+		S32 number_of_tests = 5;
+		for (S32 test = 0; test < number_of_tests; ++test)
+		{
+			// the radii are known
+			F32 first_radius = 1.f;
+			F32 second_radius = 2.f;
+			F32 third_radius = 3.f;
+
+			// compute the distances
+			F32 half_milimeter = 0.0005f;
+			F32 max_first_second_span = 3.f * (first_radius + second_radius) + OBJECT_SPAN_BONUS;
+			F32 linkable_distance = max_first_second_span - first_radius - second_radius - half_milimeter;
+
+			F32 max_full_span = 3.f * (0.5f * max_first_second_span + third_radius) + OBJECT_SPAN_BONUS;
+			F32 unlinkable_distance = max_full_span - 0.5f * linkable_distance - third_radius + half_milimeter;
+
+			// compute some random directions
+			LLVector3 first_direction(ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f);
+			first_direction.normalize();
+			LLVector3 second_direction(ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f);
+			second_direction.normalize();
+			LLVector3 third_direction(ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f);
+			third_direction.normalize();
+	
+			// compute the centers
+			LLVector3 first_center = ll_frand(10.f) * first_direction;
+			LLVector3 second_center = first_center + ll_frand(linkable_distance) * second_direction;
+			LLVector3 first_join_center = 0.5f * (first_center + second_center);
+			LLVector3 third_center = first_join_center + unlinkable_distance * third_direction;
+
+			// make sure the second info links and the third does not
+			{
+				// initialize the infos
+				S32 index = 0;
+				LLPrimLinkInfo<S32> first_info(index++, LLSphere(first_center, first_radius));
+				LLPrimLinkInfo<S32> second_info(index++, LLSphere(second_center, second_radius));
+				LLPrimLinkInfo<S32> third_info(index++, LLSphere(third_center, third_radius));
+	
+				// put the second and third infos in a list
+				std::list< LLPrimLinkInfo<S32> > info_list;
+				info_list.push_back(second_info);
+				info_list.push_back(third_info); 
+	
+				// merge the list with the first_info
+				first_info.mergeLinkableSet(info_list);
+				S32 prim_count = first_info.getPrimCount();
+
+				ensure_equals("prim count should be 2", prim_count, 2);
+				ensure_equals("unlinkable list should have length 1", (S32) info_list.size(), 1);
+			}
+
+			// reverse the order and make sure we get the same results
+			{
+				// initialize the infos
+				S32 index = 0;
+				LLPrimLinkInfo<S32> first_info(index++, LLSphere(first_center, first_radius));
+				LLPrimLinkInfo<S32> second_info(index++, LLSphere(second_center, second_radius));
+				LLPrimLinkInfo<S32> third_info(index++, LLSphere(third_center, third_radius));
+	
+				// build the list in the reverse order
+				std::list< LLPrimLinkInfo<S32> > info_list;
+				info_list.push_back(third_info); 
+				info_list.push_back(second_info);
+	
+				// merge the list with the first_info
+				first_info.mergeLinkableSet(info_list);
+				S32 prim_count = first_info.getPrimCount();
+
+				ensure_equals("prim count should be 2", prim_count, 2);
+				ensure_equals("unlinkable list should have length 1", (S32) info_list.size(), 1);
+			}
+		}
+	}
+
+	template<> template<>
+	void linkable_object::test<4>()
+	{
+		// Here we test whether linkability is invarient under permutations
+		// of link order.  To do this we generate a bunch of random spheres
+		// and then try to link them in different ways.
+		//
+		// NOTE: the linkability will only be invarient if there is only one
+		// linkable solution.  Multiple solutions will exist if the set of 
+		// candidates are larger than the maximum linkable distance, or more 
+		// numerous than a single linked object can contain.  This is easily 
+		// understood by considering a very large set of link candidates, 
+		// and first linking preferentially to the left until linking fails, 
+		// then doing the same to the right -- the final solutions will differ.
+		// Hence for this test we must generate candidate sets that lie within 
+		// the linkability envelope of a single object.
+		//
+		// NOTE: a random set of objects will tend to either be totally linkable
+		// or totally not.  That is, the random orientations that 
+
+		F32 root_center_range = 0.f;
+		F32 min_prim_radius = 0.1f;
+		F32 max_prim_radius = 2.f;
+		
+		// Linkability is min(MAX_OBJECT_SPAN,3 *( R1 + R2 ) + BONUS)
+		// 3 * (min_prim_radius + min_prim_radius) + OBJECT_SPAN_BONUS = 6 * min_prim_radius + OBJECT_SPAN_BONUS;
+		// Use .45 instead of .5 to gaurantee objects are within the minimum span.
+		F32 child_center_range = 0.45f * ( (6*min_prim_radius) + OBJECT_SPAN_BONUS );
+
+		S32 number_of_tests = 100;
+		S32 number_of_spheres = 10;
+		S32 number_of_scrambles = 10;
+		S32 number_of_random_bubble_sorts = 10;
+
+		for (S32 test = 0; test < number_of_tests; ++test)
+		{
+			LLSphere sphere;
+			S32 sphere_index = 0;
+
+			// build the root piece
+			randomize_sphere(sphere, root_center_range, min_prim_radius, max_prim_radius);
+			info.set( sphere_index++, sphere );
+
+			// build the unlinked pieces
+			std::list< LLPrimLinkInfo<S32> > info_list;
+			for (; sphere_index < number_of_spheres; ++sphere_index)
+			{
+				randomize_sphere(sphere, child_center_range, min_prim_radius, max_prim_radius);
+				LLPrimLinkInfo<S32> child_info( sphere_index, sphere );
+				info_list.push_back(child_info);
+			}
+
+			// declare the variables used to store the results
+			std::list<S32> first_linked_list;
+
+			{
+				// the link attempt will modify our original info's, so we
+				// have to make copies of the originals for testing
+				LLPrimLinkInfo<S32> test_info( 0, LLSphere(info.getCenter(), 0.5f * info.getDiameter()) );
+				std::list< LLPrimLinkInfo<S32> > test_list;
+				test_list.assign(info_list.begin(), info_list.end());
+
+				// try to link
+				test_info.mergeLinkableSet(test_list);
+	
+				ensure("All prims should link, but did not.",test_list.empty());
+
+				// store the results 
+				test_info.getData(first_linked_list);
+				first_linked_list.sort();
+			}
+
+			// try to link the spheres in various random orders
+			for (S32 scramble = 0; scramble < number_of_scrambles; ++scramble)
+			{
+				LLPrimLinkInfo<S32> test_info(0, LLSphere(info.getCenter(), 0.5f * info.getDiameter()) );
+
+				// scramble the order of the info_list
+				std::list< LLPrimLinkInfo<S32> > test_list;
+				test_list.assign(info_list.begin(), info_list.end());
+				for (S32 i = 0; i < number_of_random_bubble_sorts; i++)
+				{
+					test_list.sort(random_sort);
+				}
+
+				// try to link
+				test_info.mergeLinkableSet(test_list);
+	
+				// get the results 
+				std::list<S32> linked_list;
+				test_info.getData(linked_list);
+				linked_list.sort();
+
+				ensure_equals("linked set size should be order independent",linked_list.size(),first_linked_list.size());
+			}
+		}
+	}
+}
+
diff --git a/scripts/messages/message_template.msg b/scripts/messages/message_template.msg
index b9c694bbbf14e892029dfd35b0fb88eab52ca7f8..c5588f430172b5b10043ecc9c6e313c47e9cf7bc 100644
--- a/scripts/messages/message_template.msg
+++ b/scripts/messages/message_template.msg
@@ -1955,10 +1955,19 @@ version 2.0
 }
 
 
-// ObjectPosition
-// viewer -> simulator
+// DEPRECATED: ObjectPosition
+// == Old Behavior ==
+// Set the position on objects
+//
+// == Reason for deprecation ==
+// Unused code path was removed in the move to Havok4
+// Object position, scale and rotation messages were already unified
+// to MultipleObjectUpdate and this message was unused cruft.
+//
+// == New Location ==
+// MultipleObjectUpdate can be used instead.
 {
-	ObjectPosition Medium 4 NotTrusted Zerocoded
+	ObjectPosition Medium 4 NotTrusted Zerocoded Deprecated
 	{
 		AgentData		Single
 		{	AgentID		LLUUID	}
@@ -1972,10 +1981,19 @@ version 2.0
 }
 
 
-// ObjectScale
-// viewer -> simulator
+// DEPRECATED: ObjectScale
+// == Old Behavior ==
+// Set the scale on objects
+//
+// == Reason for deprecation ==
+// Unused code path was removed in the move to Havok4
+// Object position, scale and rotation messages were already unified
+// to MultipleObjectUpdate and this message was unused cruft.
+//
+// == New Location ==
+// MultipleObjectUpdate can be used instead.
 {
-	ObjectScale Low 92 NotTrusted Zerocoded
+	ObjectScale Low 92 NotTrusted Zerocoded Deprecated
 	{
 		AgentData		Single
 		{	AgentID		LLUUID	}