diff --git a/indra/llmath/llrect.h b/indra/llmath/llrect.h
index 17fa981c7c2399e280e0959e0182d2f93b9a48ed..21ca4189ff714c17f2b2489c49e111ebcd4a054b 100644
--- a/indra/llmath/llrect.h
+++ b/indra/llmath/llrect.h
@@ -183,10 +183,11 @@ template <class Type> class LLRectBase
 
 	LLRectBase& setCenterAndSize(Type x, Type y, Type width, Type height)
 	{
+		// width and height could be odd, so favor top, right with extra pixel
 		mLeft = x - width/2;
-		mTop = y + height/2;
-		mRight = x + width/2;
 		mBottom = y - height/2;
+		mTop = mBottom + height;
+		mRight = mLeft + width;
 		return *this;
 	}
 
diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp
index 3b01140e3a639e4795fe9112e6b025fe8f91b0ee..242b0809ebbb62f2bafb7700214446aaea64dce9 100644
--- a/indra/llmath/llvolume.cpp
+++ b/indra/llmath/llvolume.cpp
@@ -104,46 +104,128 @@ BOOL check_same_clock_dir( const LLVector3& pt1, const LLVector3& pt2, const LLV
 	}
 } 
 
-// intersect test between triangle pt1,pt2,pt3 and line from linept to linept+vect
-//returns TRUE if intersecting and moves linept to the point of intersection
-BOOL LLTriangleLineSegmentIntersect( const LLVector3& pt1, const LLVector3& pt2, const LLVector3& pt3, LLVector3& linept, const LLVector3& vect)
+BOOL LLLineSegmentBoxIntersect(const LLVector3& start, const LLVector3& end, const LLVector3& center, const LLVector3& size)
 {
-	LLVector3 V1 = pt2-pt1;
-	LLVector3 V2 = pt3-pt2;
+	float fAWdU[3];
+	LLVector3 dir;
+	LLVector3 diff;
+
+	for (U32 i = 0; i < 3; i++)
+	{
+		dir.mV[i] = 0.5f * (end.mV[i] - start.mV[i]);
+		diff.mV[i] = (0.5f * (end.mV[i] + start.mV[i])) - center.mV[i];
+		fAWdU[i] = fabsf(dir.mV[i]);
+		if(fabsf(diff.mV[i])>size.mV[i] + fAWdU[i]) return false;
+	}
+
+	float f;
+	f = dir.mV[1] * diff.mV[2] - dir.mV[2] * diff.mV[1];    if(fabsf(f)>size.mV[1]*fAWdU[2] + size.mV[2]*fAWdU[1])  return false;
+	f = dir.mV[2] * diff.mV[0] - dir.mV[0] * diff.mV[2];    if(fabsf(f)>size.mV[0]*fAWdU[2] + size.mV[2]*fAWdU[0])  return false;
+	f = dir.mV[0] * diff.mV[1] - dir.mV[1] * diff.mV[0];    if(fabsf(f)>size.mV[0]*fAWdU[1] + size.mV[1]*fAWdU[0])  return false;
 	
-	LLVector3 norm = V1 % V2;
+	return true;
+}
+
+
+// intersect test between triangle vert0, vert1, vert2 and a ray from orig in direction dir.
+// returns TRUE if intersecting and returns barycentric coordinates in intersection_a, intersection_b,
+// and returns the intersection point along dir in intersection_t.
+
+// Moller-Trumbore algorithm
+BOOL LLTriangleRayIntersect(const LLVector3& vert0, const LLVector3& vert1, const LLVector3& vert2, const LLVector3& orig, const LLVector3& dir,
+							F32* intersection_a, F32* intersection_b, F32* intersection_t, BOOL two_sided)
+{
+	F32 u, v, t;
+	
+	/* find vectors for two edges sharing vert0 */
+	LLVector3 edge1 = vert1 - vert0;
+	
+	LLVector3 edge2 = vert2 - vert0;;
+
+	/* begin calculating determinant - also used to calculate U parameter */
+	LLVector3 pvec = dir % edge2;
 	
-	F32 dotprod = norm * vect;
+	/* if determinant is near zero, ray lies in plane of triangle */
+	F32 det = edge1 * pvec;
 
-	if(dotprod < 0)
+	if (!two_sided)
 	{
-		//Find point of intersect to triangle plane.
-		//find t to intersect point
-		F32 t = -(norm * (linept-pt1))/dotprod;
+		if (det < F_APPROXIMATELY_ZERO)
+		{
+			return FALSE;
+		}
+
+		/* calculate distance from vert0 to ray origin */
+		LLVector3 tvec = orig - vert0;
 
-		// if ds is neg line started past triangle so can't hit triangle.
-		if (t > 0) 
+		/* calculate U parameter and test bounds */
+		u = tvec * pvec;	
+
+		if (u < 0.f || u > det)
 		{
 			return FALSE;
 		}
 	
-		LLVector3 pt_int = linept + (vect*t);
+		/* prepare to test V parameter */
+		LLVector3 qvec = tvec % edge1;
 		
-		if(check_same_clock_dir(pt1, pt2, pt_int, norm))
+		/* calculate V parameter and test bounds */
+		v = dir * qvec;
+		if (v < 0.f || u + v > det)
 		{
-			if(check_same_clock_dir(pt2, pt3, pt_int, norm))
+			return FALSE;
+		}
+
+		/* calculate t, scale parameters, ray intersects triangle */
+		t = edge2 * qvec;
+		F32 inv_det = 1.0 / det;
+		t *= inv_det;
+		u *= inv_det;
+		v *= inv_det;
+	}
+	
+	else // two sided
 			{
-				if(check_same_clock_dir(pt3, pt1, pt_int, norm))
+		if (det > -F_APPROXIMATELY_ZERO && det < F_APPROXIMATELY_ZERO)
 				{
-					// answer in pt_int is insde triangle
-					linept.setVec(pt_int);
-					return TRUE;
+			return FALSE;
 				}
+		F32 inv_det = 1.0 / det;
+
+		/* calculate distance from vert0 to ray origin */
+		LLVector3 tvec = orig - vert0;
+		
+		/* calculate U parameter and test bounds */
+		u = (tvec * pvec) * inv_det;
+		if (u < 0.f || u > 1.f)
+		{
+			return FALSE;
 			}
+
+		/* prepare to test V parameter */
+		LLVector3 qvec = tvec - edge1;
+		
+		/* calculate V parameter and test bounds */
+		v = (dir * qvec) * inv_det;
+		
+		if (v < 0.f || u + v > 1.f)
+		{
+			return FALSE;
 		}
+
+		/* calculate t, ray intersects triangle */
+		t = (edge2 * qvec) * inv_det;
 	}
 	
-	return FALSE;
+	if (intersection_a != NULL)
+		*intersection_a = u;
+	if (intersection_b != NULL)
+		*intersection_b = v;
+	if (intersection_t != NULL)
+		*intersection_t = t;
+	
+	
+	return TRUE;
 } 
 
 
@@ -3405,47 +3487,99 @@ void LLVolume::generateSilhouetteVertices(std::vector<LLVector3> &vertices,
 	}
 }
 
-S32 LLVolume::lineSegmentIntersect(const LLVector3& start, LLVector3& end) const
+S32 LLVolume::lineSegmentIntersect(const LLVector3& start, const LLVector3& end, 
+								   S32 face,
+								   LLVector3* intersection,LLVector2* tex_coord, LLVector3* normal, LLVector3* bi_normal)
 {
-	S32 ret = -1;
+	S32 hit_face = -1;
+	
+	S32 start_face;
+	S32 end_face;
 	
-	LLVector3 vec = end - start;
+	if (face == -1) // ALL_SIDES
+	{
+		start_face = 0;
+		end_face = getNumFaces() - 1;
+	}
+	else
+	{
+		start_face = face;
+		end_face = face;
+	}
+
+	LLVector3 dir = end - start;
+
+	F32 closest_t = 2.f; // must be larger than 1
 	
-	for (S32 i = 0; i < getNumFaces(); i++)
+	for (S32 i = start_face; i <= end_face; i++)
 	{
-		const LLVolumeFace& face = getVolumeFace(i);
+		LLVolumeFace face = getVolumeFace((U32)i);
+
+		LLVector3 box_center = (face.mExtents[0] + face.mExtents[1]) / 2.f;
+		LLVector3 box_size   = face.mExtents[1] - face.mExtents[0];
+
+        if (LLLineSegmentBoxIntersect(start, end, box_center, box_size))
+		{
+			if (bi_normal != NULL) // if the caller wants binormals, we may need to generate them
+			{
+				genBinormals(i);
+			}
+			
+			for (U32 tri = 0; tri < face.mIndices.size()/3; tri++) 
+			{
+				S32 index1 = face.mIndices[tri*3+0];
+				S32 index2 = face.mIndices[tri*3+1];
+				S32 index3 = face.mIndices[tri*3+2];
 
-		for (U32 j = 0; j < face.mIndices.size()/3; j++) 
+				F32 a, b, t;
+			
+				if (LLTriangleRayIntersect(face.mVertices[index1].mPosition,
+										   face.mVertices[index2].mPosition,
+										   face.mVertices[index3].mPosition,
+										   start, dir, &a, &b, &t, FALSE))
+				{
+					if ((t >= 0.f) &&      // if hit is after start
+						(t <= 1.f) &&      // and before end
+						(t < closest_t))   // and this hit is closer
 		{
-			//approximate normal
-			S32 v1 = face.mIndices[j*3+0];
-			S32 v2 = face.mIndices[j*3+1];
-			S32 v3 = face.mIndices[j*3+2];
+						closest_t = t;
+						hit_face = i;
 
-			LLVector3 norm = (face.mVertices[v2].mPosition - face.mVertices[v1].mPosition) % 
-				(face.mVertices[v3].mPosition - face.mVertices[v2].mPosition);
+						if (intersection != NULL)
+						{
+							*intersection = start + dir * closest_t;
+						}
 			
-			if (norm.magVecSquared() >= 0.00000001f) 
+						if (tex_coord != NULL)
 			{
-				//get view vector
-				//LLVector3 view = (start-face.mVertices[v1].mPosition);
-				//if (view * norm < 0.0f)
+							*tex_coord = ((1.f - a - b)  * face.mVertices[index1].mTexCoord +
+										  a              * face.mVertices[index2].mTexCoord +
+										  b              * face.mVertices[index3].mTexCoord);
+
+						}
+
+						if (normal != NULL)
 				{
-					if (LLTriangleLineSegmentIntersect( face.mVertices[v1].mPosition,
-														face.mVertices[v2].mPosition,
-														face.mVertices[v3].mPosition,
-														end,
-														vec))
+							*normal    = ((1.f - a - b)  * face.mVertices[index1].mNormal + 
+										  a              * face.mVertices[index2].mNormal +
+										  b              * face.mVertices[index3].mNormal);
+						}
+
+						if (bi_normal != NULL)
 					{
-						vec = end-start;
-						ret = (S32) i;
+							*bi_normal = ((1.f - a - b)  * face.mVertices[index1].mBinormal + 
+										  a              * face.mVertices[index2].mBinormal +
+										  b              * face.mVertices[index3].mBinormal);
+						}
+
 					}
 				}
 			}
 		}		
 	}
 	
-	return ret;
+	
+	return hit_face;
 }
 
 class LLVertexIndexPair
diff --git a/indra/llmath/llvolume.h b/indra/llmath/llvolume.h
index c395ed737878886ea819ea8bc44e03f556343f7e..0444a028253f826f6552426705795827133850c5 100644
--- a/indra/llmath/llvolume.h
+++ b/indra/llmath/llvolume.h
@@ -906,7 +906,13 @@ class LLVolume : public LLRefCount
 	//get the face index of the face that intersects with the given line segment at the point 
 	//closest to start.  Moves end to the point of intersection.  Returns -1 if no intersection.
 	//Line segment must be in volume space.
-	S32 lineSegmentIntersect(const LLVector3& start, LLVector3& end) const;
+	S32 lineSegmentIntersect(const LLVector3& start, const LLVector3& end,
+							 S32 face = -1,                          // which face to check, -1 = ALL_SIDES
+							 LLVector3* intersection = NULL,         // return the intersection point
+							 LLVector2* tex_coord = NULL,            // return the texture coordinates of the intersection point
+							 LLVector3* normal = NULL,               // return the surface normal at the intersection point
+							 LLVector3* bi_normal = NULL             // return the surface bi-normal at the intersection point
+		);
 	
 	// The following cleans up vertices and triangles,
 	// getting rid of degenerate triangles and duplicate vertices,
@@ -967,4 +973,10 @@ LLVector3 calc_binormal_from_triangle(
 		const LLVector3& pos2,
 		const LLVector2& tex2);
 
+BOOL LLLineSegmentBoxIntersect(const LLVector3& start, const LLVector3& end, const LLVector3& center, const LLVector3& size);
+BOOL LLTriangleRayIntersect(const LLVector3& vert0, const LLVector3& vert1, const LLVector3& vert2, const LLVector3& orig, const LLVector3& dir,
+							F32* intersection_a, F32* intersection_b, F32* intersection_t, BOOL two_sided);
+	
+	
+
 #endif
diff --git a/indra/llmath/v2math.cpp b/indra/llmath/v2math.cpp
index 70330485750faec493a08cee99d333d5b6989c93..f9e62cbd538c39abb47e11027fe4ab5cf372adeb 100644
--- a/indra/llmath/v2math.cpp
+++ b/indra/llmath/v2math.cpp
@@ -33,6 +33,7 @@
 
 //#include "vmath.h"
 #include "v2math.h"
+#include "v3math.h"
 #include "v4math.h"
 #include "m4math.h"
 #include "m3math.h"
diff --git a/indra/llmath/v2math.h b/indra/llmath/v2math.h
index 5f46655a0721a13995ac17ac87e74329413eaae5..c896b80977f1642ece972655d4607f8cf9bf5a20 100644
--- a/indra/llmath/v2math.h
+++ b/indra/llmath/v2math.h
@@ -33,6 +33,7 @@
 #define LL_V2MATH_H
 
 #include "llmath.h"
+#include "v3math.h"
 
 class LLVector4;
 class LLMatrix3;
@@ -49,9 +50,10 @@ class LLVector2
 
 		static LLVector2 zero;
 
-		LLVector2();							// Initializes LLVector2 to (0, 0)
-		LLVector2(F32 x, F32 y);			    // Initializes LLVector2 to (x. y)
-		LLVector2(const F32 *vec);				// Initializes LLVector2 to (vec[0]. vec[1])
+		LLVector2();							  // Initializes LLVector2 to (0, 0)
+		LLVector2(F32 x, F32 y);			      // Initializes LLVector2 to (x. y)
+		LLVector2(const F32 *vec);				  // Initializes LLVector2 to (vec[0]. vec[1])
+        explicit LLVector2(const LLVector3 &vec); // Initializes LLVector2 to (vec[0]. vec[1])
 		
 		// Clears LLVector2 to (0, 0).  DEPRECATED - prefer zeroVec.
 		void	clear();
@@ -137,6 +139,12 @@ inline LLVector2::LLVector2(const F32 *vec)
 	mV[VY] = vec[VY];
 }
 
+inline LLVector2::LLVector2(const LLVector3 &vec)
+{
+	mV[VX] = vec.mV[VX];
+	mV[VY] = vec.mV[VY];
+}
+
 
 // Clear and Assignment Functions
 
diff --git a/indra/llmath/v3math.cpp b/indra/llmath/v3math.cpp
index 166761e55083521d1ee6e6c345d730edac4cec3c..12916b625387be877b1c8839217f09bed2ba2c6b 100644
--- a/indra/llmath/v3math.cpp
+++ b/indra/llmath/v3math.cpp
@@ -34,6 +34,7 @@
 #include "v3math.h"
 
 //#include "vmath.h"
+#include "v2math.h"
 #include "v4math.h"
 #include "m4math.h"
 #include "m3math.h"
@@ -270,6 +271,13 @@ const LLVector3&	LLVector3::setVec(const LLVector4 &vec)
 	return (*this);
 }
 
+LLVector3::LLVector3(const LLVector2 &vec)
+{
+	mV[VX] = (F32)vec.mV[VX];
+	mV[VY] = (F32)vec.mV[VY];
+	mV[VZ] = 0;
+}
+
 LLVector3::LLVector3(const LLVector3d &vec)
 {
 	mV[VX] = (F32)vec.mdV[VX];
diff --git a/indra/llmath/v3math.h b/indra/llmath/v3math.h
index 051d5376b7ca1953165d923eba8ed91a611b8e53..42ca1f88b23755dc56aaa20a7f93e836f7dd3fe4 100644
--- a/indra/llmath/v3math.h
+++ b/indra/llmath/v3math.h
@@ -36,6 +36,7 @@
 #include "llmath.h"
 
 #include "llsd.h"
+class LLVector2;
 class LLVector4;
 class LLMatrix3;
 class LLVector3d;
@@ -62,6 +63,7 @@ class LLVector3
 		inline LLVector3();							// Initializes LLVector3 to (0, 0, 0)
 		inline LLVector3(const F32 x, const F32 y, const F32 z);			// Initializes LLVector3 to (x. y, z)
 		inline explicit LLVector3(const F32 *vec);				// Initializes LLVector3 to (vec[0]. vec[1], vec[2])
+		explicit LLVector3(const LLVector2 &vec);				// Initializes LLVector3 to (vec[0]. vec[1], 0)
 		explicit LLVector3(const LLVector3d &vec);				// Initializes LLVector3 to (vec[0]. vec[1], vec[2])
 		explicit LLVector3(const LLVector4 &vec);				// Initializes LLVector4 to (vec[0]. vec[1], vec[2])
 		LLVector3(const LLSD& sd);
diff --git a/indra/llmessage/lltemplatemessagereader.cpp b/indra/llmessage/lltemplatemessagereader.cpp
index b1395b0b703f0d273303959841888ad5e664d870..b5186ad539736000b0a5f7828350b18c287ef69f 100644
--- a/indra/llmessage/lltemplatemessagereader.cpp
+++ b/indra/llmessage/lltemplatemessagereader.cpp
@@ -574,7 +574,9 @@ BOOL LLTemplateMessageReader::decodeData(const U8* buffer, const LLHost& sender
 			// repeat number is a single byte
 			if (decode_pos >= mReceiveSize)
 			{
-				logRanOffEndOfPacket(sender, decode_pos, 1);
+				// commented out - hetgrid says that missing variable blocks
+				// at end of message are legal
+				// logRanOffEndOfPacket(sender, decode_pos, 1);
 
 				// default to 0 repeats
 				repeat_number = 0;
diff --git a/indra/llmessage/message_prehash.cpp b/indra/llmessage/message_prehash.cpp
index 0d537e05754e8d4db048e0966b55c5065814da1d..dd034904d3efef3fee7ef62f166d8e34fd727ffc 100644
--- a/indra/llmessage/message_prehash.cpp
+++ b/indra/llmessage/message_prehash.cpp
@@ -1371,4 +1371,7 @@ char* _PREHASH_OwnerMask = LLMessageStringTable::getInstance()->getString("Owner
 char* _PREHASH_TransferInventoryAck = LLMessageStringTable::getInstance()->getString("TransferInventoryAck");
 char* _PREHASH_RegionDenyAgeUnverified = LLMessageStringTable::getInstance()->getString("RegionDenyAgeUnverified");
 char* _PREHASH_AgeVerificationBlock = LLMessageStringTable::getInstance()->getString("AgeVerificationBlock");
+char* _PREHASH_UCoord = LLMessageStringTable::getInstance()->getString("UCoord");
+char* _PREHASH_VCoord = LLMessageStringTable::getInstance()->getString("VCoord");
+char* _PREHASH_FaceIndex = LLMessageStringTable::getInstance()->getString("FaceIndex");
 
diff --git a/indra/llmessage/message_prehash.h b/indra/llmessage/message_prehash.h
index 287296d1eb59385d5689d1c3e76c0fea6eb73e26..9dfb3992ca079744a9c401d0ec4d69136e5fcf0b 100644
--- a/indra/llmessage/message_prehash.h
+++ b/indra/llmessage/message_prehash.h
@@ -1371,6 +1371,8 @@ extern char * _PREHASH_OwnerMask;
 extern char * _PREHASH_TransferInventoryAck;
 extern char * _PREHASH_RegionDenyAgeUnverified;
 extern char * _PREHASH_AgeVerificationBlock;
-
+extern char * _PREHASH_UCoord;
+extern char * _PREHASH_VCoord;
+extern char * _PREHASH_FaceIndex;
 
 #endif
diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp
index c20bfe6ac0c66ea1f264523c1e81675a4fdacd3d..c1da6c93fe8ce4df6d705499ee357d49ea2b5403 100644
--- a/indra/llui/llview.cpp
+++ b/indra/llui/llview.cpp
@@ -63,7 +63,6 @@ BOOL	LLView::sDebugKeys = FALSE;
 S32		LLView::sDepth = 0;
 BOOL	LLView::sDebugMouseHandling = FALSE;
 std::string LLView::sMouseHandlerMessage;
-S32	LLView::sSelectID = GL_NAME_UI_RESERVED;
 BOOL	LLView::sEditingUI = FALSE;
 BOOL	LLView::sForceReshape = FALSE;
 LLView*	LLView::sEditingUIView = NULL;
diff --git a/indra/llwindow/llwindowmacosx.cpp b/indra/llwindow/llwindowmacosx.cpp
index 65f0a2b7e270b31ba86ba1ed2c1cd5eb4fcaa720..df3fb2e2403dfa4cd58517057bffca8e2561384e 100644
--- a/indra/llwindow/llwindowmacosx.cpp
+++ b/indra/llwindow/llwindowmacosx.cpp
@@ -1411,6 +1411,11 @@ BOOL LLWindowMacOSX::setCursorPosition(const LLCoordWindow position)
 	// Under certain circumstances, this will trigger us to decouple the cursor.
 	adjustCursorDecouple(true);
 
+	// trigger mouse move callback
+	LLCoordGL gl_pos;
+	convertCoords(position, &gl_pos);
+	mCallbacks->handleMouseMove(this, gl_pos, (MASK)0);
+
 	return result;
 }
 
diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp
index 4a81ddd1fd2c7da1d0f7d617aa006babbfa81855..526a6769a0c7666648cf58550ff183b35925e795 100644
--- a/indra/llwindow/llwindowwin32.cpp
+++ b/indra/llwindow/llwindowwin32.cpp
@@ -1387,6 +1387,10 @@ BOOL LLWindowWin32::setCursorPosition(const LLCoordWindow position)
 		return FALSE;
 	}
 
+	LLCoordGL gl_pos;
+	convertCoords(position, &gl_pos);
+	mCallbacks->handleMouseMove(this, gl_pos, (MASK)0);
+	
 	return SetCursorPos(screen_pos.mX, screen_pos.mY);
 }
 
diff --git a/indra/lscript/lscript_library/lscript_library.cpp b/indra/lscript/lscript_library/lscript_library.cpp
index 0b357c9182b157eb41ffb2183ab2492027be1025..b99bd64f2afa2da6a2768bebf6ddad5e1f40d8d9 100644
--- a/indra/lscript/lscript_library/lscript_library.cpp
+++ b/indra/lscript/lscript_library/lscript_library.cpp
@@ -442,6 +442,14 @@ void LLScriptLibrary::init()
 	addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetRegionAgentCount", "i", NULL, "int llGetRegionAgentCount()\nreturns the number of agents in a region"));
 	addFunction(new LLScriptLibraryFunction(10.f, 1.f, dummy_func, "llTextBox", NULL, "ksi", "llTextBox(key avatar, string message, integer chat_channel\nShows a dialog box on the avatar's screen with the message.\nA text box asks for input, and if entered the text is chatted on chat_channel."));
 	addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetAgentLanguage", "s", "k", "string llGetAgentLanguage(key id)\nGets the agents preferred language.."));
+	addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llDetectedTouchUV", "v", "i", "vector llDetectedTouchUV(integer number)\nreturns the u and v coordinates in the first two components of a vector, for a triggered touch event"));
+	addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llDetectedTouchFace", "i", "i", "integer llDetectedTouchFace(integer number)\nreturns the index of the face on the object for a triggered touch event"));
+	addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llDetectedTouchPos", "v", "i", "vector llDetectedTouchPos(integer number)\nreturns the position touched for a triggered touch event"));
+	addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llDetectedTouchNormal", "v", "i", "vector llDetectedTouchNormal(integer number)\nreturns the surface normal for a triggered touch event"));
+	addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llDetectedTouchBinormal", "v", "i", "vector llDetectedTouchBinormal(integer number)\nreturns the surface binormal for a triggered touch event"));
+	addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llDetectedTouchST", "v", "i", "vector llDetectedTouchST(integer number)\nreturns the s and t coordinates in the first two components of a vector, for a triggered touch event"));
+
+
 
 	// energy, sleep, dummy_func, name, return type, parameters, help text, gods-only
 
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 8ca74f6d0f24076214b97303e28808186c0f4c3e..4c47cb45b2c87bd0e5439e008762484ebb38ccab 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -5201,6 +5201,28 @@
       <key>Value</key>
       <string />
     </map>
+    <key>PerFrameHoverPick</key>
+    <map>
+      <key>Comment</key>
+      <string>Detect the object under the mouse continually</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>Boolean</string>
+      <key>Value</key>
+      <real>1</real>
+    </map>
+    <key>PerFrameHoverPickCount</key>
+    <map>
+      <key>Comment</key>
+      <string>Detect the object under the mouse every n frames</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>S32</string>
+      <key>Value</key>
+      <integer>1</integer>
+    </map>
     <key>PermissionsCautionEnabled</key>
     <map>
       <key>Comment</key>
diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp
index 7fb9e57507bcd54fd59217ff704b753245c01172..cffd4410c380b2adcbc1cd64e4e130a3e94f1c89 100644
--- a/indra/newview/llagent.cpp
+++ b/indra/newview/llagent.cpp
@@ -133,7 +133,6 @@
 // end Ventrella
 
 extern LLMenuBarGL* gMenuBarView;
-extern U8 gLastPickAlpha;
 
 //drone wandering constants
 const F32 MAX_WANDER_TIME = 20.f;						// seconds
@@ -330,6 +329,8 @@ LLAgent::LLAgent()
 	mCameraZoomFraction(1.f),			// deprecated
 	mThirdPersonHeadOffset(0.f, 0.f, 1.f),
 	mSitCameraEnabled(FALSE),
+	mHUDTargetZoom(1.f),
+	mHUDCurZoom(1.f),
 	mFocusOnAvatar(TRUE),
 	mFocusGlobal(),
 	mFocusTargetGlobal(),
@@ -530,10 +531,7 @@ void LLAgent::resetView(BOOL reset_camera)
 		setFocusOnAvatar(TRUE, ANIMATE);
 	}
 
-	if (mAvatarObject.notNull())
-	{
-		mAvatarObject->mHUDTargetZoom = 1.f;
-	}
+	mHUDTargetZoom = 1.f;
 }
 
 // Handle any actions that need to be performed when the main app gains focus
@@ -1298,7 +1296,7 @@ LLQuaternion LLAgent::getQuat() const
 //-----------------------------------------------------------------------------
 // calcFocusOffset()
 //-----------------------------------------------------------------------------
-LLVector3d LLAgent::calcFocusOffset(LLViewerObject *object, S32 x, S32 y)
+LLVector3 LLAgent::calcFocusOffset(LLViewerObject *object, S32 x, S32 y)
 {
 	// calculate offset based on view direction
 	BOOL is_avatar = object->isAvatar();
@@ -1457,10 +1455,10 @@ LLVector3d LLAgent::calcFocusOffset(LLViewerObject *object, S32 x, S32 y)
 		
 		obj_rel = lerp(focus_delta, obj_rel, bias);
 		
-		return LLVector3d(obj_rel);
+		return LLVector3(obj_rel);
 	}
 
-	return LLVector3d(focus_delta.mV[VX], focus_delta.mV[VY], focus_delta.mV[VZ]);
+	return LLVector3(focus_delta.mV[VX], focus_delta.mV[VY], focus_delta.mV[VZ]);
 }
 
 //-----------------------------------------------------------------------------
@@ -1650,7 +1648,7 @@ F32 LLAgent::getCameraZoomFraction()
 	if (selection->getObjectCount() && selection->getSelectType() == SELECT_TYPE_HUD)
 	{
 		// already [0,1]
-		return mAvatarObject->mHUDTargetZoom;
+		return mHUDTargetZoom;
 	}
 	else if (mFocusOnAvatar && cameraThirdPerson())
 	{
@@ -1698,7 +1696,7 @@ void LLAgent::setCameraZoomFraction(F32 fraction)
 
 	if (selection->getObjectCount() && selection->getSelectType() == SELECT_TYPE_HUD)
 	{
-		mAvatarObject->mHUDTargetZoom = fraction;
+		mHUDTargetZoom = fraction;
 	}
 	else if (mFocusOnAvatar && cameraThirdPerson())
 	{
@@ -1808,7 +1806,7 @@ void LLAgent::cameraZoomIn(const F32 fraction)
 	if (selection->getObjectCount() && selection->getSelectType() == SELECT_TYPE_HUD)
 	{
 		// just update hud zoom level
-		mAvatarObject->mHUDTargetZoom /= fraction;
+		mHUDTargetZoom /= fraction;
 		return;
 	}
 
@@ -3757,7 +3755,7 @@ LLVector3d LLAgent::calcCameraPositionTargetGlobal(BOOL *hit_limit)
 
 					lag_interp *= u;
 
-					if (gViewerWindow->getLeftMouseDown() && gLastHitObjectID == mAvatarObject->getID())
+					if (gViewerWindow->getLeftMouseDown() && gViewerWindow->getLastPick().mObjectID == mAvatarObject->getID())
 					{
 						// disable camera lag when using mouse-directed steering
 						target_lag.clearVec();
@@ -4285,6 +4283,12 @@ void LLAgent::setFocusObject(LLViewerObject* object)
 //-----------------------------------------------------------------------------
 // setFocusGlobal()
 //-----------------------------------------------------------------------------
+void LLAgent::setFocusGlobal(const LLPickInfo& pick)
+{
+	setFocusGlobal(pick.mPosGlobal, pick.mObjectID);
+}
+
+
 void LLAgent::setFocusGlobal(const LLVector3d& focus, const LLUUID &object_id)
 {
 	setFocusObject(gObjectList.findObject(object_id));
diff --git a/indra/newview/llagent.h b/indra/newview/llagent.h
index 45006fef8d76dc7874d34d2360a0a89b3439cfa3..ea9138fdd1510bd104748fc5cd14276f37a7b555 100644
--- a/indra/newview/llagent.h
+++ b/indra/newview/llagent.h
@@ -95,6 +95,7 @@ class LLMessageSystem;
 class LLPermissions;
 class LLHost;
 class LLFriendObserver;
+class LLPickInfo;
 
 struct LLGroupData
 {
@@ -191,6 +192,7 @@ class LLAgent : public LLObservable
 	void			changeCameraToFollow(BOOL animate = TRUE);
 	//end Ventrella
 
+	void			setFocusGlobal(const LLPickInfo& pick);
 	void			setFocusGlobal(const LLVector3d &focus, const LLUUID &object_id = LLUUID::null);
 	void			setFocusOnAvatar(BOOL focus, BOOL animate);
 	void			setCameraPosAndFocusGlobal(const LLVector3d& pos, const LLVector3d& focus, const LLUUID &object_id);
@@ -374,7 +376,7 @@ class LLAgent : public LLObservable
 	void			sendAnimationRequests(LLDynamicArray<LLUUID> &anim_ids, EAnimRequest request);
 	void			sendAnimationRequest(const LLUUID &anim_id, EAnimRequest request);
 
-	LLVector3d		calcFocusOffset(LLViewerObject *object, S32 x, S32 y);
+	LLVector3		calcFocusOffset(LLViewerObject *object, S32 x, S32 y);
 	BOOL			calcCameraMinDistance(F32 &obj_min_distance);
 
 	void			startCameraAnimation();
@@ -711,6 +713,9 @@ class LLAgent : public LLObservable
 
 	LLDynamicArray<LLGroupData> mGroups;
 
+	F32				mHUDTargetZoom;	// target zoom level for HUD objects (used when editing)
+	F32				mHUDCurZoom;	// current animated zoom level for HUD objects
+
 	BOOL			mInitialized;
 
 	static BOOL		sDebugDisplayTarget;
@@ -786,6 +791,7 @@ class LLAgent : public LLObservable
 	LLVector3		mSitCameraFocus;				// root relative camera target when sitting
 	LLVector3d      mCameraSmoothingLastPositionGlobal;    
 	LLVector3d      mCameraSmoothingLastPositionAgent;    
+
 	
 	//Ventrella
 	LLVector3		mCameraUpVector;				// camera's up direction in world coordinates (determines the 'roll' of the view)
diff --git a/indra/newview/lldrawpoolavatar.cpp b/indra/newview/lldrawpoolavatar.cpp
index 422c0dc9a870f2b6dfdd8eae49abbd366d9b9fab..6d81edd7ca00f3cddff0994b8610865cd4144281 100644
--- a/indra/newview/lldrawpoolavatar.cpp
+++ b/indra/newview/lldrawpoolavatar.cpp
@@ -53,8 +53,6 @@ static U32 sBufferUsage = GL_STREAM_DRAW_ARB;
 static U32 sShaderLevel = 0;
 static LLGLSLShader* sVertexProgram = NULL;
 
-extern BOOL gUseGLPick;
-
 F32 CLOTHING_GRAVITY_EFFECT = 0.7f;
 F32 CLOTHING_ACCEL_FORCE_FACTOR = 0.2f;
 const S32 NUM_TEST_AVATARS = 30;
@@ -566,11 +564,6 @@ void LLDrawPoolAvatar::renderAvatars(LLVOAvatar* single_avatar, S32 pass)
 //-----------------------------------------------------------------------------
 void LLDrawPoolAvatar::renderForSelect()
 {
-	if (gUseGLPick)
-	{
-		return;
-	}
-	
 	if (!gRenderAvatar)
 	{
 		return;
@@ -618,7 +611,7 @@ void LLDrawPoolAvatar::renderForSelect()
 
 	glColor4ubv(color.mV);
 
-	if ((sShaderLevel > 0) && !gUseGLPick)  // for hardware blending
+	if (sShaderLevel > 0)  // for hardware blending
 	{
 		sRenderingSkinned = TRUE;
 		sVertexProgram->bind();
@@ -628,7 +621,7 @@ void LLDrawPoolAvatar::renderForSelect()
 	avatarp->renderSkinned(AVATAR_RENDER_PASS_SINGLE);
 
 	// if we're in software-blending, remember to set the fence _after_ we draw so we wait till this rendering is done
-	if ((sShaderLevel > 0) && !gUseGLPick)
+	if (sShaderLevel > 0)
 	{
 		sRenderingSkinned = FALSE;
 		sVertexProgram->unbind();
diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp
index b2f5caa57d06a3d1ff0d086fec67fe5eca6b57e5..16668f81e2b9d7e25243734a9694e9a95e1c34a4 100644
--- a/indra/newview/llface.cpp
+++ b/indra/newview/llface.cpp
@@ -54,8 +54,6 @@
 
 #define LL_MAX_INDICES_COUNT 1000000
 
-extern BOOL gPickFaces;
-
 BOOL LLFace::sSafeRenderSelect = TRUE; // FALSE
 
 #define DOTVEC(a,b) (a.mV[0]*b.mV[0] + a.mV[1]*b.mV[1] + a.mV[2]*b.mV[2])
@@ -73,14 +71,15 @@ The resulting texture coordinate <u,v> is:
 	u = 2(B dot P)
 	v = 2(T dot P)
 */
-void planarProjection(LLVector2 &tc, const LLVolumeFace::VertexData &vd, const LLVector3 &mCenter, const LLVector3& vec)
+void planarProjection(LLVector2 &tc, const LLVector3& normal,
+					  const LLVector3 &mCenter, const LLVector3& vec)
 {	//DONE!
 	LLVector3 binormal;
-	float d = vd.mNormal * LLVector3(1,0,0);
+	float d = normal * LLVector3(1,0,0);
 	if (d >= 0.5f || d <= -0.5f)
 	{
 		binormal = LLVector3(0,1,0);
-		if (vd.mNormal.mV[0] < 0)
+		if (normal.mV[0] < 0)
 		{
 			binormal = -binormal;
 		}
@@ -88,18 +87,19 @@ void planarProjection(LLVector2 &tc, const LLVolumeFace::VertexData &vd, const L
 	else
 	{
         binormal = LLVector3(1,0,0);
-		if (vd.mNormal.mV[1] > 0)
+		if (normal.mV[1] > 0)
 		{
 			binormal = -binormal;
 		}
 	}
-	LLVector3 tangent = binormal % vd.mNormal;
+	LLVector3 tangent = binormal % normal;
 
 	tc.mV[1] = -((tangent*vec)*2 - 0.5f);
 	tc.mV[0] = 1.0f+((binormal*vec)*2 - 0.5f);
 }
 
-void sphericalProjection(LLVector2 &tc, const LLVolumeFace::VertexData &vd, const LLVector3 &mCenter, const LLVector3& vec)
+void sphericalProjection(LLVector2 &tc, const LLVector3& normal,
+						 const LLVector3 &mCenter, const LLVector3& vec)
 {	//BROKEN
 	/*tc.mV[0] = acosf(vd.mNormal * LLVector3(1,0,0))/3.14159f;
 	
@@ -110,7 +110,7 @@ void sphericalProjection(LLVector2 &tc, const LLVolumeFace::VertexData &vd, cons
 	}*/
 }
 
-void cylindricalProjection(LLVector2 &tc, const LLVolumeFace::VertexData &vd, const LLVector3 &mCenter, const LLVector3& vec)
+void cylindricalProjection(LLVector2 &tc, const LLVector3& normal, const LLVector3 &mCenter, const LLVector3& vec)
 {	//BROKEN
 	/*LLVector3 binormal;
 	float d = vd.mNormal * LLVector3(1,0,0);
@@ -374,7 +374,7 @@ void LLFace::renderForSelect(U32 data_mask)
 #if !LL_RELEASE_FOR_DOWNLOAD
 		LLGLState::checkClientArrays(data_mask);
 #endif
-		if (gPickFaces && mTEOffset != -1)
+		if (mTEOffset != -1)
 		{
 			// mask off high 4 bits (16 total possible faces)
 			color.mV[0] &= 0x0f;
@@ -419,12 +419,11 @@ void LLFace::renderSelected(LLImageGL *imagep, const LLColor4& color)
 
 	if (mGeomCount > 0 && mIndicesCount > 0)
 	{
-		LLGLSPipelineAlpha gls_pipeline_alpha;
-		glColor4fv(color.mV);
+		gGL.color4fv(color.mV);
 
 		LLViewerImage::bindTexture(imagep);
 	
-		glPushMatrix();
+		gGL.pushMatrix();
 		if (mDrawablep->isActive())
 		{
 			glMultMatrixf((GLfloat*)mDrawablep->getRenderMatrix().mMatrix);
@@ -434,137 +433,49 @@ void LLFace::renderSelected(LLImageGL *imagep, const LLColor4& color)
 			glMultMatrixf((GLfloat*)mDrawablep->getRegion()->mRenderMatrix.mMatrix);
 		}
 
-		mVertexBuffer->setBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD);
+		mVertexBuffer->setBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD);
 #if !LL_RELEASE_FOR_DOWNLOAD
-		LLGLState::checkClientArrays(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD);
+		LLGLState::checkClientArrays(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD);
 #endif
 		mVertexBuffer->draw(LLVertexBuffer::TRIANGLES, mIndicesCount, mIndicesIndex);
 				
-		glPopMatrix();
+		gGL.popMatrix();
 	}
 }
 
-void LLFace::renderSelectedUV(const S32 offset, const S32 count)
+
+/* removed in lieu of raycast uv detection
+void LLFace::renderSelectedUV()
 {
-#if 0
 	LLViewerImage* red_blue_imagep = gImageList.getImageFromFile("uv_test1.j2c", TRUE, TRUE);
 	LLViewerImage* green_imagep = gImageList.getImageFromFile("uv_test2.tga", TRUE, TRUE);
 
-	LLGLSObjectSelect object_select;
-	LLGLEnable blend(GL_BLEND);
-	
-	if (!mDrawPoolp || !getIndicesCount() || getIndicesStart() < 0)
-	{
-		return;
-	}
-	for (S32 pass = 0; pass < 2; pass++)
-	{
-		static F32 bias = 0.f;
-		static F32 factor = -10.f;
-		if (mGeomCount > 0)
-		{
-			gGL.color4fv(LLColor4::white.mV);
-
-			if (pass == 0)
-			{
-				LLViewerImage::bindTexture(red_blue_imagep);
-				red_blue_imagep->setMipFilterNearest (TRUE, TRUE);
-			}
-			else // pass == 1
-			{
-				gGL.blendFunc(GL_ONE, GL_ONE);
-				LLViewerImage::bindTexture(green_imagep);
-				glMatrixMode(GL_TEXTURE);
-				glPushMatrix();
-				glScalef(256.f, 256.f, 1.f);
-				green_imagep->setMipFilterNearest (TRUE, TRUE);
-			}
-
-
-			if (!isState(GLOBAL))
-			{
-				// Apply the proper transform for non-global objects.
-				glMatrixMode(GL_MODELVIEW);
-				glPushMatrix();
-				glMultMatrixf((float*)getRenderMatrix().mMatrix);
-			}
-
-			glEnable(GL_POLYGON_OFFSET_FILL);
-			glPolygonOffset(factor, bias);
-			if (sSafeRenderSelect)
-			{
-				gGL.begin(LLVertexBuffer::TRIANGLES);
-				if (count)
-				{
-					for (S32 i = offset; i < offset + count; i++)
-					{
-						LLVector2 tc = mDrawPoolp->getTexCoord(mDrawPoolp->getIndex(getIndicesStart() + i), 0);
-						gGL.texCoord2fv(tc.mV);
-						LLVector3 vertex = mDrawPoolp->getVertex(mDrawPoolp->getIndex(getIndicesStart() + i));
-						gGL.vertex3fv(vertex.mV);
-					}
-				}
-				else
-				{
-					for (U32 i = 0; i < getIndicesCount(); i++)
-					{
-						LLVector2 tc = mDrawPoolp->getTexCoord(mDrawPoolp->getIndex(getIndicesStart() + i), 0);
-						gGL.texCoord2fv(tc.mV);
-						LLVector3 vertex = mDrawPoolp->getVertex(mDrawPoolp->getIndex(getIndicesStart() + i));
-						gGL.vertex3fv(vertex.mV);
-					}
-				}
-				gGL.end();
-			}
-			else
-			{
-				llassert(mGeomIndex >= 0);
-				if (count)
-				{
-					if (mIndicesCount > 0)
-					{
-						glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_SHORT, getRawIndices() + offset); 
-					}
-					else
-					{
-						llerrs << "Rendering non-indexed volume face!" << llendl;
-						glDrawArrays(mPrimType, mGeomIndex, mGeomCount); 
-					}
-				}
-				else
-				{
-					if (mIndicesCount > 0)
-					{
-						glDrawElements(GL_TRIANGLES, mIndicesCount, GL_UNSIGNED_SHORT, getRawIndices()); 
-					}
-					else
-					{
-						glDrawArrays(GL_TRIANGLES, mGeomIndex, mGeomCount); 
-					}
-				}
-			}
+	LLGLSUVSelect object_select;
 
-			glDisable(GL_POLYGON_OFFSET_FILL);
-			if (!isState(GLOBAL))
-			{
-				// Restore the tranform for non-global objects
-				glPopMatrix();
-			}
-			if (pass == 1)
-			{
-				glMatrixMode(GL_TEXTURE);
-				glPopMatrix();
-				glMatrixMode(GL_MODELVIEW);
-				gGL.blendFunc(GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA);
-			}
-		}
-	}
+	// use red/blue gradient to get coarse UV coordinates
+	renderSelected(red_blue_imagep, LLColor4::white);
 	
-	//restore blend func
-	gGL.blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-#endif
+	static F32 bias = 0.f;
+	static F32 factor = -10.f;
+	glPolygonOffset(factor, bias);
+
+	// add green dither pattern on top of red/blue gradient
+	gGL.blendFunc(LLRender::BF_ONE, LLRender::BF_ONE);
+	glMatrixMode(GL_TEXTURE);
+	glPushMatrix();
+	// make green pattern repeat once per texel in red/blue texture
+	glScalef(256.f, 256.f, 1.f);
+	glMatrixMode(GL_MODELVIEW);
+
+	renderSelected(green_imagep, LLColor4::white);
+
+	glMatrixMode(GL_TEXTURE);
+	glPopMatrix();
+	glMatrixMode(GL_MODELVIEW);
+	gGL.blendFunc(LLRender::BF_SOURCE_ALPHA, LLRender::BF_ONE_MINUS_SOURCE_ALPHA);
 }
 
+*/
 
 void LLFace::printDebugInfo() const
 {
@@ -760,6 +671,69 @@ BOOL LLFace::genVolumeBBoxes(const LLVolume &volume, S32 f,
 	return TRUE;
 }
 
+
+
+// convert surface coordinates to texture coordinates, based on
+// the values in the texture entry.  probably should be
+// integrated with getGeometryVolume() for its texture coordinate
+// generation - but i'll leave that to someone more familiar
+// with the implications.
+LLVector2 LLFace::surfaceToTexture(LLVector2 surface_coord, LLVector3 position, LLVector3 normal)
+{
+	LLVector2 tc = surface_coord;
+	
+	const LLTextureEntry *tep = getTextureEntry();
+
+	if (tep == NULL)
+	{
+		// can't do much without the texture entry
+		return surface_coord;
+	}
+
+	// see if we have a non-default mapping
+    U8 texgen = getTextureEntry()->getTexGen();
+	if (texgen != LLTextureEntry::TEX_GEN_DEFAULT)
+	{
+		LLVector3 center = mDrawablep->getVOVolume()->getVolume()->getVolumeFace(mTEOffset).mCenter;
+		
+		LLVector3 scale = (mDrawablep->getVOVolume()->isVolumeGlobal()) ? LLVector3(1,1,1) : mVObjp->getScale();
+		LLVector3 vec = position;
+		vec.scaleVec(scale);
+
+		switch (texgen)
+		{
+		case LLTextureEntry::TEX_GEN_PLANAR:
+			planarProjection(tc, normal, center, vec);
+			break;
+		case LLTextureEntry::TEX_GEN_SPHERICAL:
+			sphericalProjection(tc, normal, center, vec);
+			break;
+		case LLTextureEntry::TEX_GEN_CYLINDRICAL:
+			cylindricalProjection(tc, normal, center, vec);
+			break;
+		default:
+			break;
+		}		
+	}
+
+	if (mTextureMatrix)	// if we have a texture matrix, use it
+	{
+		LLVector3 tc3(tc);
+		tc3 = tc3 * *mTextureMatrix;
+		tc = LLVector2(tc3);
+	}
+	
+	else // otherwise use the texture entry parameters
+	{
+		xform(tc, cos(tep->getRotation()), sin(tep->getRotation()),
+			  tep->mOffsetS, tep->mOffsetT, tep->mScaleS, tep->mScaleT);
+	}
+
+	
+	return tc;
+}
+
+
 BOOL LLFace::getGeometryVolume(const LLVolume& volume,
 							   const S32 &f,
 								const LLMatrix4& mat_vert, const LLMatrix3& mat_normal,
@@ -1039,13 +1013,13 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
 				switch (texgen)
 				{
 					case LLTextureEntry::TEX_GEN_PLANAR:
-						planarProjection(tc, vf.mVertices[i], vf.mCenter, vec);
+						planarProjection(tc, vf.mVertices[i].mNormal, vf.mCenter, vec);
 						break;
 					case LLTextureEntry::TEX_GEN_SPHERICAL:
-						sphericalProjection(tc, vf.mVertices[i], vf.mCenter, vec);
+						sphericalProjection(tc, vf.mVertices[i].mNormal, vf.mCenter, vec);
 						break;
 					case LLTextureEntry::TEX_GEN_CYLINDRICAL:
-						cylindricalProjection(tc, vf.mVertices[i], vf.mCenter, vec);
+						cylindricalProjection(tc, vf.mVertices[i].mNormal, vf.mCenter, vec);
 						break;
 					default:
 						break;
diff --git a/indra/newview/llface.h b/indra/newview/llface.h
index 0391cda66fc0a0dd9e2a6c2e64fc3d4a9dfb2eec..9e7a33eb7b59ac2c8ca9aa0cb9d9c536fe26111b 100644
--- a/indra/newview/llface.h
+++ b/indra/newview/llface.h
@@ -91,6 +91,7 @@ class LLFace
 	LLXformMatrix*	getXform()			const	{ return mXform; }
 	BOOL			hasGeometry()		const	{ return mGeomCount > 0; }
 	LLVector3		getPositionAgent()	const;
+	LLVector2       surfaceToTexture(LLVector2 surface_coord, LLVector3 position, LLVector3 normal);
 	
 	U32				getState()			const	{ return mState; }
 	void			setState(U32 state)			{ mState |= state; }
@@ -165,7 +166,7 @@ class LLFace
 	void		update();
 
 	void		updateCenterAgent(); // Update center when xform has changed.
-	void		renderSelectedUV(const S32 offset = 0, const S32 count = 0);
+	void		renderSelectedUV();
 
 	void		renderForSelect(U32 data_mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD);
 	void		renderSelected(LLImageGL *image, const LLColor4 &color);
diff --git a/indra/newview/llhudobject.h b/indra/newview/llhudobject.h
index 11d9f43fcc9a8b06e6a5c86518ad7ab2b4b3cb2a..12b2eced9b4145c399f12b2c66a29ffb644415b9 100644
--- a/indra/newview/llhudobject.h
+++ b/indra/newview/llhudobject.h
@@ -67,6 +67,8 @@ class LLHUDObject : public LLRefCount
 
 	U8 getType() const { return mType; }
 
+	LLVector3d getPositionGlobal() const { return mPositionGlobal; }
+
 	static LLHUDObject *addHUDObject(const U8 type);
 	static LLHUDEffect *addHUDEffect(const U8 type);
 	static void updateAll();
diff --git a/indra/newview/llmanip.cpp b/indra/newview/llmanip.cpp
index 5e09e69e2bf35de1095fe572938de2e43a62cc0c..891dd69f84da6992661cdd5fd684d82c4683f60e 100644
--- a/indra/newview/llmanip.cpp
+++ b/indra/newview/llmanip.cpp
@@ -96,7 +96,8 @@ LLManip::LLManip( const std::string& name, LLToolComposite* composite )
 	:
 	LLTool( name, composite ),
 	mInSnapRegime(FALSE),
-	mHighlightedPart(LL_NO_PART)
+	mHighlightedPart(LL_NO_PART),
+	mManipPart(LL_NO_PART)
 {
 }
 
@@ -177,7 +178,7 @@ F32 LLManip::getSubdivisionLevel(const LLVector3 &reference_point, const LLVecto
 	LLVector3 cam_to_reference;
 	if (mObjectSelection->getSelectType() == SELECT_TYPE_HUD)
 	{
-		cam_to_reference = LLVector3(1.f / gAgent.getAvatarObject()->mHUDCurZoom, 0.f, 0.f);
+		cam_to_reference = LLVector3(1.f / gAgent.mHUDCurZoom, 0.f, 0.f);
 	}
 	else
 	{
@@ -199,6 +200,8 @@ void LLManip::handleSelect()
 
 void LLManip::handleDeselect()
 {
+	mHighlightedPart = LL_NO_PART;
+	mManipPart = LL_NO_PART;
 	mObjectSelection = NULL;
 }
 
@@ -260,8 +263,8 @@ BOOL LLManip::getMousePointOnPlaneGlobal(LLVector3d& point, S32 x, S32 y, LLVect
 	if (mObjectSelection->getSelectType() == SELECT_TYPE_HUD)
 	{
 		BOOL result = FALSE;
-		F32 mouse_x = ((F32)x / gViewerWindow->getWindowWidth() - 0.5f) * LLViewerCamera::getInstance()->getAspect() / gAgent.getAvatarObject()->mHUDCurZoom;
-		F32 mouse_y = ((F32)y / gViewerWindow->getWindowHeight() - 0.5f) / gAgent.getAvatarObject()->mHUDCurZoom;
+		F32 mouse_x = ((F32)x / gViewerWindow->getWindowWidth() - 0.5f) * LLViewerCamera::getInstance()->getAspect() / gAgent.mHUDCurZoom;
+		F32 mouse_y = ((F32)y / gViewerWindow->getWindowHeight() - 0.5f) / gAgent.mHUDCurZoom;
 
 		LLVector3 origin_agent = gAgent.getPosAgentFromGlobal(origin);
 		LLVector3 mouse_pos = LLVector3(0.f, -mouse_x, mouse_y);
@@ -299,8 +302,8 @@ BOOL LLManip::nearestPointOnLineFromMouse( S32 x, S32 y, const LLVector3& b1, co
 
 	if (mObjectSelection->getSelectType() == SELECT_TYPE_HUD)
 	{
-		F32 mouse_x = (((F32)x / gViewerWindow->getWindowWidth()) - 0.5f) * LLViewerCamera::getInstance()->getAspect() / gAgent.getAvatarObject()->mHUDCurZoom;
-		F32 mouse_y = (((F32)y / gViewerWindow->getWindowHeight()) - 0.5f) / gAgent.getAvatarObject()->mHUDCurZoom;
+		F32 mouse_x = (((F32)x / gViewerWindow->getWindowWidth()) - 0.5f) * LLViewerCamera::getInstance()->getAspect() / gAgent.mHUDCurZoom;
+		F32 mouse_y = (((F32)y / gViewerWindow->getWindowHeight()) - 0.5f) / gAgent.mHUDCurZoom;
 		a1 = LLVector3(llmin(b1.mV[VX] - 0.1f, b2.mV[VX] - 0.1f, 0.f), -mouse_x, mouse_y);
 		a2 = a1 + LLVector3(1.f, 0.f, 0.f);
 	}
@@ -484,7 +487,7 @@ void LLManip::renderTickText(const LLVector3& pos, const std::string& text, cons
 	LLVector3 render_pos = pos;
 	if (hud_selection)
 	{
-		F32 zoom_amt = gAgent.getAvatarObject()->mHUDCurZoom;
+		F32 zoom_amt = gAgent.mHUDCurZoom;
 		F32 inv_zoom_amt = 1.f / zoom_amt;
 		// scale text back up to counter-act zoom level
 		render_pos = pos * zoom_amt;
@@ -542,7 +545,7 @@ void LLManip::renderTickValue(const LLVector3& pos, F32 value, const std::string
 	LLVector3 render_pos = pos;
 	if (hud_selection)
 	{
-		F32 zoom_amt = gAgent.getAvatarObject()->mHUDCurZoom;
+		F32 zoom_amt = gAgent.mHUDCurZoom;
 		F32 inv_zoom_amt = 1.f / zoom_amt;
 		// scale text back up to counter-act zoom level
 		render_pos = pos * zoom_amt;
diff --git a/indra/newview/llmanip.h b/indra/newview/llmanip.h
index 1313f77c5b1fc85b04bab66a2184a7b812ba1d87..43d6eaadc812651e3573e6ab71c8e5471cbd29fa 100644
--- a/indra/newview/llmanip.h
+++ b/indra/newview/llmanip.h
@@ -155,6 +155,7 @@ class LLManip : public LLTool
 	BOOL				mInSnapRegime;
 	LLSafeHandle<LLObjectSelection> mObjectSelection;
 	EManipPart			mHighlightedPart;
+	EManipPart			mManipPart;
 
 	static F32			sHelpTextVisibleTime;
 	static F32			sHelpTextFadeTime;
diff --git a/indra/newview/llmaniprotate.cpp b/indra/newview/llmaniprotate.cpp
index e98ded2a879dae8aa6f2810600a76e6e2ea17e06..ca0812c8a041e4c17ccf5688e2efc6e92a890b88 100644
--- a/indra/newview/llmaniprotate.cpp
+++ b/indra/newview/llmaniprotate.cpp
@@ -98,7 +98,6 @@ LLManipRotate::LLManipRotate( LLToolComposite* composite )
 	mCenterToCamMag(0.f),
 	mCenterToProfilePlane(),
 	mCenterToProfilePlaneMag(0.f),
-	mManipPart( LL_NO_PART ),
 	mSendUpdateOnMouseUp( FALSE ),
 	mSmoothRotate( FALSE ),
 	mCamEdgeOn(FALSE),
@@ -113,13 +112,6 @@ void LLManipRotate::handleSelect()
 	LLManip::handleSelect();
 }
 
-void LLManipRotate::handleDeselect()
-{
-	mHighlightedPart = LL_NO_PART;
-	mManipPart = LL_NO_PART;
-	LLManip::handleDeselect();
-}
-
 void LLManipRotate::render()
 {
 	LLGLSUIDefault gls_ui;
@@ -144,7 +136,7 @@ void LLManipRotate::render()
 	glPushMatrix();
 	if (mObjectSelection->getSelectType() == SELECT_TYPE_HUD)
 	{
-		F32 zoom = gAgent.getAvatarObject()->mHUDCurZoom;
+		F32 zoom = gAgent.mHUDCurZoom;
 		glScalef(zoom, zoom, zoom);
 	}
 
@@ -363,8 +355,7 @@ BOOL LLManipRotate::handleMouseDown(S32 x, S32 y, MASK mask)
 	LLViewerObject* first_object = mObjectSelection->getFirstMoveableObject(TRUE);
 	if( first_object )
 	{
-		LLViewerObject* hit_obj = gViewerWindow->lastObjectHit();
-		if( hit_obj && mHighlightedPart != LL_NO_PART )
+		if( mHighlightedPart != LL_NO_PART )
 		{
 			handled = handleMouseDownOnPart( x, y, mask );
 		}
@@ -1126,18 +1117,18 @@ BOOL LLManipRotate::updateVisiblity()
 	LLVector3 center = gAgent.getPosAgentFromGlobal( mRotationCenter );
 	if (mObjectSelection->getSelectType() == SELECT_TYPE_HUD)
 	{
-		mCenterToCam = LLVector3(-1.f / gAgent.getAvatarObject()->mHUDCurZoom, 0.f, 0.f);
+		mCenterToCam = LLVector3(-1.f / gAgent.mHUDCurZoom, 0.f, 0.f);
 		mCenterToCamNorm = mCenterToCam;
 		mCenterToCamMag = mCenterToCamNorm.normVec();
 
 		mRadiusMeters = RADIUS_PIXELS / (F32) LLViewerCamera::getInstance()->getViewHeightInPixels();
-		mRadiusMeters /= gAgent.getAvatarObject()->mHUDCurZoom;
+		mRadiusMeters /= gAgent.mHUDCurZoom;
 
 		mCenterToProfilePlaneMag = mRadiusMeters * mRadiusMeters / mCenterToCamMag;
 		mCenterToProfilePlane = -mCenterToProfilePlaneMag * mCenterToCamNorm;
 
-		mCenterScreen.set((S32)((0.5f - mRotationCenter.mdV[VY]) / gAgent.getAvatarObject()->mHUDCurZoom * gViewerWindow->getWindowWidth()),
-							(S32)((mRotationCenter.mdV[VZ] + 0.5f) / gAgent.getAvatarObject()->mHUDCurZoom * gViewerWindow->getWindowHeight()));
+		mCenterScreen.set((S32)((0.5f - mRotationCenter.mdV[VY]) / gAgent.mHUDCurZoom * gViewerWindow->getWindowWidth()),
+							(S32)((mRotationCenter.mdV[VZ] + 0.5f) / gAgent.mHUDCurZoom * gViewerWindow->getWindowHeight()));
 		visible = TRUE;
 	}
 	else
@@ -1653,8 +1644,8 @@ void LLManipRotate::mouseToRay( S32 x, S32 y, LLVector3* ray_pt, LLVector3* ray_
 {
 	if (LLSelectMgr::getInstance()->getSelection()->getSelectType() == SELECT_TYPE_HUD)
 	{
-		F32 mouse_x = (((F32)x / gViewerWindow->getWindowWidth()) - 0.5f) / gAgent.getAvatarObject()->mHUDCurZoom;
-		F32 mouse_y = ((((F32)y) / gViewerWindow->getWindowHeight()) - 0.5f) / gAgent.getAvatarObject()->mHUDCurZoom;
+		F32 mouse_x = (((F32)x / gViewerWindow->getWindowWidth()) - 0.5f) / gAgent.mHUDCurZoom;
+		F32 mouse_y = ((((F32)y) / gViewerWindow->getWindowHeight()) - 0.5f) / gAgent.mHUDCurZoom;
 
 		*ray_pt = LLVector3(-1.f, -mouse_x, mouse_y);
 		*ray_dir = LLVector3(1.f, 0.f, 0.f);
diff --git a/indra/newview/llmaniprotate.h b/indra/newview/llmaniprotate.h
index 460bd3fcbab580905fbb134a1b97f5b369eed92c..f5c815d53773f3936152e0f2c2af781572ef3aef 100644
--- a/indra/newview/llmaniprotate.h
+++ b/indra/newview/llmaniprotate.h
@@ -64,7 +64,6 @@ class LLManipRotate : public LLManip
 	virtual void	render();
 
 	virtual void	handleSelect();
-	virtual void	handleDeselect();
 
 	virtual BOOL	handleMouseDownOnPart(S32 x, S32 y, MASK mask);
 	virtual void	highlightManipulators(S32 x, S32 y);
@@ -109,8 +108,6 @@ class LLManipRotate : public LLManip
 	LLVector3			mCenterToProfilePlane;
 	F32					mCenterToProfilePlaneMag;
 
-	EManipPart			mManipPart;
-
 	BOOL				mSendUpdateOnMouseUp;
 
 	BOOL				mSmoothRotate;
diff --git a/indra/newview/llmanipscale.cpp b/indra/newview/llmanipscale.cpp
index 57c901e9bcdb22fb67006af9eb2025b4c6fe0717..5859d4c75ff6309478a9be9b4403e77e6f4a31e0 100644
--- a/indra/newview/llmanipscale.cpp
+++ b/indra/newview/llmanipscale.cpp
@@ -166,19 +166,11 @@ void LLManipScale::handleSelect()
 	LLManip::handleSelect();
 }
 
-void LLManipScale::handleDeselect()
-{
-	mHighlightedPart = LL_NO_PART;
-	mManipPart = LL_NO_PART;
-	LLManip::handleDeselect();
-}
-
 LLManipScale::LLManipScale( LLToolComposite* composite )
 	: 
 	LLManip( std::string("Scale"), composite ),
 	mBoxHandleSize( 1.f ),
 	mScaledBoxHandleSize( 1.f ),
-	mManipPart( LL_NO_PART ),
 	mLastMouseX( -1 ),
 	mLastMouseY( -1 ),
 	mSendUpdateOnMouseUp( FALSE ),
@@ -216,7 +208,7 @@ void LLManipScale::render()
 		glPushMatrix();
 		if (mObjectSelection->getSelectType() == SELECT_TYPE_HUD)
 		{
-			F32 zoom = gAgent.getAvatarObject()->mHUDCurZoom;
+			F32 zoom = gAgent.mHUDCurZoom;
 			glScalef(zoom, zoom, zoom);
 		}
 
@@ -233,7 +225,7 @@ void LLManipScale::render()
 		if (mObjectSelection->getSelectType() == SELECT_TYPE_HUD)
 		{
 			mBoxHandleSize = BOX_HANDLE_BASE_SIZE * BOX_HANDLE_BASE_FACTOR / (F32) LLViewerCamera::getInstance()->getViewHeightInPixels();
-			mBoxHandleSize /= gAgent.getAvatarObject()->mHUDCurZoom;
+			mBoxHandleSize /= gAgent.mHUDCurZoom;
 		}
 		else
 		{
@@ -316,9 +308,7 @@ BOOL LLManipScale::handleMouseDown(S32 x, S32 y, MASK mask)
 {
 	BOOL	handled = FALSE;
 
-	LLViewerObject* hit_obj = gViewerWindow->lastObjectHit();
-	if( hit_obj ||  
-		(mHighlightedPart != LL_NO_PART) )
+	if(mHighlightedPart != LL_NO_PART)
 	{
 		handled = handleMouseDownOnPart( x, y, mask );
 	}
@@ -446,7 +436,7 @@ void LLManipScale::highlightManipulators(S32 x, S32 y)
 			LLMatrix4 cfr(OGL_TO_CFR_ROTATION);
 			transform *= cfr;
 			LLMatrix4 window_scale;
-			F32 zoom_level = 2.f * gAgent.getAvatarObject()->mHUDCurZoom;
+			F32 zoom_level = 2.f * gAgent.mHUDCurZoom;
 			window_scale.initAll(LLVector3(zoom_level / LLViewerCamera::getInstance()->getAspect(), zoom_level, 0.f),
 				LLQuaternion::DEFAULT,
 				LLVector3::zero);
@@ -1367,7 +1357,7 @@ void LLManipScale::updateSnapGuides(const LLBBox& bbox)
 
 	if(mObjectSelection->getSelectType() == SELECT_TYPE_HUD)
 	{
-		mSnapRegimeOffset = SNAP_GUIDE_SCREEN_OFFSET / gAgent.getAvatarObject()->mHUDCurZoom;
+		mSnapRegimeOffset = SNAP_GUIDE_SCREEN_OFFSET / gAgent.mHUDCurZoom;
 
 	}
 	else
@@ -1380,7 +1370,7 @@ void LLManipScale::updateSnapGuides(const LLBBox& bbox)
 	if (mObjectSelection->getSelectType() == SELECT_TYPE_HUD)
 	{
 		cam_at_axis.setVec(1.f, 0.f, 0.f);
-		snap_guide_length = SNAP_GUIDE_SCREEN_LENGTH / gAgent.getAvatarObject()->mHUDCurZoom;
+		snap_guide_length = SNAP_GUIDE_SCREEN_LENGTH / gAgent.mHUDCurZoom;
 	}
 	else
 	{
diff --git a/indra/newview/llmanipscale.h b/indra/newview/llmanipscale.h
index c02845e3586a87e1a181702445d32bbb28759ef6..432be2cd3e50be2df2992335de0279199291da92 100644
--- a/indra/newview/llmanipscale.h
+++ b/indra/newview/llmanipscale.h
@@ -76,7 +76,6 @@ class LLManipScale : public LLManip
 	virtual BOOL	handleHover( S32 x, S32 y, MASK mask );
 	virtual void	render();
 	virtual void	handleSelect();
-	virtual void	handleDeselect();
 
 	virtual BOOL	handleMouseDownOnPart(S32 x, S32 y, MASK mask);
 	virtual void	highlightManipulators(S32 x, S32 y);	// decided which manipulator, if any, should be highlighted by mouse hover
@@ -140,7 +139,6 @@ class LLManipScale : public LLManip
 
 	F32				mBoxHandleSize;		// The size of the handles at the corners of the bounding box
 	F32				mScaledBoxHandleSize; // handle size after scaling for selection feedback
-	EManipPart		mManipPart;
 	LLVector3d		mDragStartPointGlobal;
 	LLVector3d		mDragStartCenterGlobal;	// The center of the bounding box of all selected objects at time of drag start
 	LLVector3d		mDragPointGlobal;
diff --git a/indra/newview/llmaniptranslate.cpp b/indra/newview/llmaniptranslate.cpp
index f4f3a535de67066fd4fa3a9235d3585c170b4562..7785b5a07894d0272c096fee52683b510b9a4997 100644
--- a/indra/newview/llmaniptranslate.cpp
+++ b/indra/newview/llmaniptranslate.cpp
@@ -113,7 +113,6 @@ LLManipTranslate::LLManipTranslate( LLToolComposite* composite )
 	mConeSize(0),
 	mArrowLengthMeters(0.f),
 	mPlaneManipOffsetMeters(0.f),
-	mManipPart(LL_NO_PART),
 	mUpdateTimer(),
 	mSnapOffsetMeters(0.f),
 	mArrowScales(1.f, 1.f, 1.f),
@@ -257,21 +256,12 @@ void LLManipTranslate::handleSelect()
 	LLManip::handleSelect();
 }
 
-void LLManipTranslate::handleDeselect()
-{
-	mHighlightedPart = LL_NO_PART;
-	mManipPart = LL_NO_PART;
-	LLManip::handleDeselect();
-}
-
 BOOL LLManipTranslate::handleMouseDown(S32 x, S32 y, MASK mask)
 {
 	BOOL	handled = FALSE;
 
 	// didn't click in any UI object, so must have clicked in the world
-	LLViewerObject* hit_obj = gViewerWindow->lastObjectHit();
-	if( hit_obj && 
-		(mHighlightedPart == LL_X_ARROW ||
+	if( (mHighlightedPart == LL_X_ARROW ||
 		 mHighlightedPart == LL_Y_ARROW ||
 		 mHighlightedPart == LL_Z_ARROW ||
 		 mHighlightedPart == LL_YZ_PLANE ||
@@ -818,7 +808,7 @@ void LLManipTranslate::highlightManipulators(S32 x, S32 y)
 		LLMatrix4 cfr(OGL_TO_CFR_ROTATION);
 		transform *= cfr;
 		LLMatrix4 window_scale;
-		F32 zoom_level = 2.f * gAgent.getAvatarObject()->mHUDCurZoom;
+		F32 zoom_level = 2.f * gAgent.mHUDCurZoom;
 		window_scale.initAll(LLVector3(zoom_level / LLViewerCamera::getInstance()->getAspect(), zoom_level, 0.f),
 			LLQuaternion::DEFAULT,
 			LLVector3::zero);
@@ -1056,7 +1046,7 @@ void LLManipTranslate::render()
 	gGL.pushMatrix();
 	if (mObjectSelection->getSelectType() == SELECT_TYPE_HUD)
 	{
-		F32 zoom = gAgent.getAvatarObject()->mHUDCurZoom;
+		F32 zoom = gAgent.mHUDCurZoom;
 		glScalef(zoom, zoom, zoom);
 	}
 	{
@@ -1220,7 +1210,7 @@ void LLManipTranslate::renderSnapGuides()
 
 		if (mObjectSelection->getSelectType() == SELECT_TYPE_HUD)
 		{
-			guide_size_meters = 1.f / gAgent.getAvatarObject()->mHUDCurZoom;
+			guide_size_meters = 1.f / gAgent.mHUDCurZoom;
 			mSnapOffsetMeters = mArrowLengthMeters * 1.5f;
 		}
 		else
@@ -1803,7 +1793,7 @@ void LLManipTranslate::renderTranslationHandles()
 	if (mObjectSelection->getSelectType() == SELECT_TYPE_HUD)
 	{
 		mArrowLengthMeters = mAxisArrowLength / gViewerWindow->getWindowHeight();
-		mArrowLengthMeters /= gAgent.getAvatarObject()->mHUDCurZoom;
+		mArrowLengthMeters /= gAgent.mHUDCurZoom;
 	}
 	else
 	{
diff --git a/indra/newview/llmaniptranslate.h b/indra/newview/llmaniptranslate.h
index c9f98e9c7d500fe517ee476aba224c663765666a..8c17dd3d510f52cf50f7c2d058428d44c85eb8cb 100644
--- a/indra/newview/llmaniptranslate.h
+++ b/indra/newview/llmaniptranslate.h
@@ -61,7 +61,6 @@ class LLManipTranslate : public LLManip
 	virtual BOOL	handleHover(S32 x, S32 y, MASK mask);
 	virtual void	render();
 	virtual void	handleSelect();
-	virtual void	handleDeselect();
 
 	virtual void	highlightManipulators(S32 x, S32 y);
 	virtual BOOL	handleMouseDownOnPart(S32 x, S32 y, MASK mask);
@@ -110,7 +109,6 @@ class LLManipTranslate : public LLManip
 	F32			mArrowLengthMeters;		// meters
 	F32			mGridSizeMeters;
 	F32			mPlaneManipOffsetMeters;
-	EManipPart	mManipPart;
 	LLVector3	mManipNormal;
 	LLVector3d	mDragCursorStartGlobal;
 	LLVector3d	mDragSelectionStartGlobal;
diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp
index 8ebaeb67585a762098233861e4764a132b64eaca..2f11dad010f23d0f79901a73e5069fd479affad0 100644
--- a/indra/newview/llselectmgr.cpp
+++ b/indra/newview/llselectmgr.cpp
@@ -96,9 +96,6 @@ const S32 MAX_ACTION_QUEUE_SIZE = 20;
 const S32 MAX_SILS_PER_FRAME = 50;
 const S32 MAX_OBJECTS_PER_PACKET = 254;
 
-extern LLUUID			gLastHitObjectID;
-extern LLVector3d		gLastHitObjectOffset;
-
 //
 // Globals
 //
@@ -4833,7 +4830,7 @@ void LLSelectMgr::renderSilhouettes(BOOL for_hud)
 	{
 		LLBBox hud_bbox = avatar->getHUDBBox();
 
-		F32 cur_zoom = avatar->mHUDCurZoom;
+		F32 cur_zoom = gAgent.mHUDCurZoom;
 
 		// set up transform to encompass bounding box of HUD
 		glMatrixMode(GL_PROJECTION);
@@ -5280,7 +5277,7 @@ void LLSelectNode::renderOneSilhouette(const LLColor4 &color)
 		F32 silhouette_thickness;
 		if (is_hud_object && gAgent.getAvatarObject())
 		{
-			silhouette_thickness = LLSelectMgr::sHighlightThickness / gAgent.getAvatarObject()->mHUDCurZoom;
+			silhouette_thickness = LLSelectMgr::sHighlightThickness / gAgent.mHUDCurZoom;
 		}
 		else
 		{
@@ -5479,8 +5476,8 @@ void LLSelectMgr::updateSelectionCenter()
 		if (mSelectedObjects->mSelectType != SELECT_TYPE_HUD && gAgent.getAvatarObject())
 		{
 			// reset hud ZOOM
-			gAgent.getAvatarObject()->mHUDTargetZoom = 1.f;
-			gAgent.getAvatarObject()->mHUDCurZoom = 1.f;
+			gAgent.mHUDTargetZoom = 1.f;
+			gAgent.mHUDCurZoom = 1.f;
 		}
 
 		mShowSelection = FALSE;
@@ -5564,11 +5561,12 @@ void LLSelectMgr::updatePointAt()
 		if (mSelectedObjects->getObjectCount())
 		{					
 			LLVector3 select_offset;
-			LLViewerObject *click_object = gObjectList.findObject(gLastHitObjectID);
+			const LLPickInfo& pick = gViewerWindow->getLastPick();
+			LLViewerObject *click_object = pick.getObject();
 			if (click_object && click_object->isSelected())
 			{
 				// clicked on another object in our selection group, use that as target
-				select_offset.setVec(gLastHitObjectOffset);
+				select_offset.setVec(pick.mObjectOffset);
 				select_offset.rotVec(~click_object->getRenderRotation());
 		
 				gAgent.setPointAt(POINTAT_TARGET_SELECT, click_object, select_offset);
@@ -5766,29 +5764,20 @@ BOOL LLSelectMgr::setForceSelection(BOOL force)
 
 void LLSelectMgr::resetAgentHUDZoom()
 {
-	if (gAgent.getAvatarObject())
-	{
-		gAgent.getAvatarObject()->mHUDTargetZoom = 1.f;
-		gAgent.getAvatarObject()->mHUDCurZoom = 1.f;
-	}
+	gAgent.mHUDTargetZoom = 1.f;
+	gAgent.mHUDCurZoom = 1.f;
 }
 
 void LLSelectMgr::getAgentHUDZoom(F32 &target_zoom, F32 &current_zoom) const
 {
-	if (gAgent.getAvatarObject())
-	{
-		target_zoom = gAgent.getAvatarObject()->mHUDTargetZoom;
-		current_zoom = gAgent.getAvatarObject()->mHUDCurZoom;
-	}
+	target_zoom = gAgent.mHUDTargetZoom;
+	current_zoom = gAgent.mHUDCurZoom;
 }
 
 void LLSelectMgr::setAgentHUDZoom(F32 target_zoom, F32 current_zoom)
 {
-	if (gAgent.getAvatarObject())
-	{
-		gAgent.getAvatarObject()->mHUDTargetZoom = target_zoom;
-		gAgent.getAvatarObject()->mHUDCurZoom = current_zoom;
-	}
+	gAgent.mHUDTargetZoom = target_zoom;
+	gAgent.mHUDCurZoom = current_zoom;
 }
 
 LLObjectSelection::LLObjectSelection() : 
diff --git a/indra/newview/llspatialpartition.cpp b/indra/newview/llspatialpartition.cpp
index a62b1ac56a3d494b95f00733969ae98ec4e435c5..e9b09599e2fd055c79d123949cf39120ca773a1b 100644
--- a/indra/newview/llspatialpartition.cpp
+++ b/indra/newview/llspatialpartition.cpp
@@ -36,6 +36,7 @@
 #include "llviewerwindow.h"
 #include "llviewerobjectlist.h"
 #include "llvovolume.h"
+#include "llvolume.h"
 #include "llviewercamera.h"
 #include "llface.h"
 #include "llviewercontrol.h"
@@ -243,28 +244,6 @@ void LLSpatialGroup::buildOcclusion()
 
 BOOL earlyFail(LLCamera* camera, LLSpatialGroup* group);
 
-BOOL LLLineSegmentAABB(const LLVector3& start, const LLVector3& end, const LLVector3& center, const LLVector3& size)
-{
-	float fAWdU[3];
-	LLVector3 dir;
-	LLVector3 diff;
-	
-	for (U32 i = 0; i < 3; i++)
-	{
-		dir.mV[i] = 0.5f * (end.mV[i] - start.mV[i]);
-		diff.mV[i] = (0.5f * (end.mV[i] + start.mV[i])) - center.mV[i];
-		fAWdU[i] = fabsf(dir.mV[i]);
-		if(fabsf(diff.mV[i])>size.mV[i] + fAWdU[i])	return false;
-	}
-
-	float f;
-	f = dir.mV[1] * diff.mV[2] - dir.mV[2] * diff.mV[1];	if(fabsf(f)>size.mV[1]*fAWdU[2] + size.mV[2]*fAWdU[1])	return false;
-	f = dir.mV[2] * diff.mV[0] - dir.mV[0] * diff.mV[2];	if(fabsf(f)>size.mV[0]*fAWdU[2] + size.mV[2]*fAWdU[0])	return false;
-	f = dir.mV[0] * diff.mV[1] - dir.mV[1] * diff.mV[0];	if(fabsf(f)>size.mV[0]*fAWdU[1] + size.mV[1]*fAWdU[0])	return false;
-
-	return true;
-}
-
 //returns:
 //	0 if sphere and AABB are not intersecting 
 //	1 if they are
@@ -2305,6 +2284,56 @@ void renderLights(LLDrawable* drawablep)
 	}
 }
 
+
+void renderRaycast(LLDrawable* drawablep)
+{
+	if (drawablep->getVObj() != gDebugRaycastObject)
+	{
+		return;
+	}
+	
+	if (drawablep->getNumFaces())
+	{
+		LLGLEnable blend(GL_BLEND);
+		gGL.color4f(0,1,1,0.5f);
+
+		for (S32 i = 0; i < drawablep->getNumFaces(); i++)
+		{
+			pushVerts(drawablep->getFace(i), LLVertexBuffer::MAP_VERTEX);
+		}
+
+		// draw intersection point
+		glPushMatrix();
+		glLoadMatrixd(gGLModelView);
+		LLVector3 translate = gDebugRaycastIntersection;
+		glTranslatef(translate.mV[0], translate.mV[1], translate.mV[2]);
+		LLCoordFrame orient;
+		orient.lookDir(gDebugRaycastNormal, gDebugRaycastBinormal);
+		LLMatrix4 rotation;
+		orient.getRotMatrixToParent(rotation);
+		glMultMatrixf((float*)rotation.mMatrix);
+		
+		gGL.color4f(1,0,0,0.5f);
+		drawBox(LLVector3(0, 0, 0), LLVector3(0.1f, 0.022f, 0.022f));
+		gGL.color4f(0,1,0,0.5f);
+		drawBox(LLVector3(0, 0, 0), LLVector3(0.021f, 0.1f, 0.021f));
+		gGL.color4f(0,0,1,0.5f);
+		drawBox(LLVector3(0, 0, 0), LLVector3(0.02f, 0.02f, 0.1f));
+		glPopMatrix();
+
+		// draw bounding box of prim
+		const LLVector3* ext = drawablep->getSpatialExtents();
+
+		LLVector3 pos = (ext[0] + ext[1]) * 0.5f;
+		LLVector3 size = (ext[1] - ext[0]) * 0.5f;
+
+		LLGLDepthTest depth(GL_FALSE, GL_TRUE);
+		gGL.color4f(0,0.5f,0.5f,1);
+		drawBoxOutline(pos, size);
+
+	}
+}
+
 class LLOctreeRenderNonOccluded : public LLOctreeTraveler<LLDrawable>
 {
 public:
@@ -2381,6 +2410,11 @@ class LLOctreeRenderNonOccluded : public LLOctreeTraveler<LLDrawable>
 			{
 				renderLights(drawable);
 			}
+
+			if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_RAYCAST))
+			{
+				renderRaycast(drawable);
+			}
 		}
 		
 		for (LLSpatialGroup::draw_map_t::iterator i = group->mDrawMap.begin(); i != group->mDrawMap.end(); ++i)
@@ -2411,7 +2445,8 @@ void LLSpatialPartition::renderDebug()
 									  LLPipeline::RENDER_DEBUG_BBOXES |
 									  LLPipeline::RENDER_DEBUG_POINTS |
 									  LLPipeline::RENDER_DEBUG_TEXTURE_PRIORITY |
-									  LLPipeline::RENDER_DEBUG_TEXTURE_ANIM))
+									  LLPipeline::RENDER_DEBUG_TEXTURE_ANIM |
+									  LLPipeline::RENDER_DEBUG_RAYCAST))
 	{
 		return;
 	}
@@ -2457,17 +2492,37 @@ BOOL LLSpatialPartition::isVisible(const LLVector3& v)
 	return TRUE;
 }
 
-class LLOctreePick : public LLSpatialGroup::OctreeTraveler
+class LLOctreeIntersect : public LLSpatialGroup::OctreeTraveler
 {
 public:
 	LLVector3 mStart;
 	LLVector3 mEnd;
-	LLDrawable* mRet;
+	S32       *mFaceHit;
+	LLVector3 *mIntersection;
+	LLVector2 *mTexCoord;
+	LLVector3 *mNormal;
+	LLVector3 *mBinormal;
+	LLDrawable* mHit;
 	
-	LLOctreePick(LLVector3 start, LLVector3 end)
-	: mStart(start), mEnd(end)
+	LLOctreeIntersect(LLVector3 start, LLVector3 end,
+					  S32* face_hit, LLVector3* intersection, LLVector2* tex_coord, LLVector3* normal, LLVector3* binormal)
+		: mStart(start),
+		  mEnd(end),
+		  mFaceHit(face_hit),
+		  mIntersection(intersection),
+		  mTexCoord(tex_coord),
+		  mNormal(normal),
+		  mBinormal(binormal),
+		  mHit(NULL)
 	{
-		mRet = NULL;
+	}
+	
+	virtual void visit(const LLSpatialGroup::OctreeNode* branch) 
+	{	
+		for (LLSpatialGroup::OctreeNode::const_element_iter i = branch->getData().begin(); i != branch->getData().end(); ++i)
+	{
+			check(*i);
+		}
 	}
 
 	virtual LLDrawable* check(const LLSpatialGroup::OctreeNode* node)
@@ -2487,41 +2542,73 @@ class LLOctreePick : public LLSpatialGroup::OctreeTraveler
 			size = group->mBounds[1];
 			center = group->mBounds[0];
 			
-			if (LLLineSegmentAABB(mStart, mEnd, center, size))
+			LLVector3 local_start = mStart;
+			LLVector3 local_end   = mEnd;
+
+			if (group->mSpatialPartition->isBridge())
+			{
+				LLMatrix4 local_matrix = group->mSpatialPartition->asBridge()->mDrawable->getRenderMatrix();
+				local_matrix.invert();
+				
+				local_start = mStart * local_matrix;
+				local_end   = mEnd   * local_matrix;
+			}
+
+			if (LLLineSegmentBoxIntersect(local_start, local_end, center, size))
 			{
 				check(child);
 			}
 		}	
 
-		return mRet;
+		return mHit;
 	}
 
-	virtual void visit(const LLSpatialGroup::OctreeNode* branch) 
+	virtual bool check(LLDrawable* drawable)
 	{	
-		for (LLSpatialGroup::OctreeNode::const_element_iter i = branch->getData().begin(); i != branch->getData().end(); ++i)
+		if (drawable->isSpatialBridge())
 		{
-			check(*i);
-		}
+			LLSpatialPartition *part = drawable->asPartition();
+
+			check(part->mOctree);
 	}
 
-	virtual bool check(LLDrawable* drawable)
+		else
 	{
 		LLViewerObject* vobj = drawable->getVObj();
-		if (vobj->lineSegmentIntersect(mStart, mEnd))
+
+			if (vobj)
+			{
+				LLVector3 intersection;
+				if (vobj->lineSegmentIntersect(mStart, mEnd, -1, mFaceHit, &intersection, mTexCoord, mNormal, mBinormal))
+				{
+					mEnd = intersection;  // shorten ray so we only find CLOSER hits
+					if (mIntersection)
 		{
-			mRet = vobj->mDrawable;
+						*mIntersection = intersection;
+					}
+					
+					mHit = vobj->mDrawable;
+				}
+			}
 		}
 		
 		return false;
 	}
 };
 
-LLDrawable*	LLSpatialPartition::pickDrawable(const LLVector3& start, const LLVector3& end, LLVector3& collision)
+LLDrawable* LLSpatialPartition::lineSegmentIntersect(const LLVector3& start, const LLVector3& end,
+													 S32* face_hit,                   // return the face hit
+													 LLVector3* intersection,         // return the intersection point
+													 LLVector2* tex_coord,            // return the texture coordinates of the intersection point
+													 LLVector3* normal,               // return the surface normal at the intersection point
+													 LLVector3* bi_normal             // return the surface bi-normal at the intersection point
+	)
+
 {
-	LLOctreePick pick(start, end);
-	LLDrawable* ret = pick.check(mOctree);
-	collision.setVec(pick.mEnd);
-	return ret;
+	LLOctreeIntersect intersect(start, end, face_hit, intersection, tex_coord, normal, bi_normal);
+	LLDrawable* drawable = intersect.check(mOctree);
+
+	return drawable;
 }
 
 LLDrawInfo::LLDrawInfo(U16 start, U16 end, U32 count, U32 offset, 
diff --git a/indra/newview/llspatialpartition.h b/indra/newview/llspatialpartition.h
index 7e872915b9ac271191b8ff61bb3aeec852880a4f..d9a81d8b9e1553b5a8241c67aa6b917dd60e2564 100644
--- a/indra/newview/llspatialpartition.h
+++ b/indra/newview/llspatialpartition.h
@@ -300,7 +300,14 @@ class LLSpatialPartition: public LLGeometryManager
 	LLSpatialGroup *put(LLDrawable *drawablep, BOOL was_visible = FALSE);
 	BOOL remove(LLDrawable *drawablep, LLSpatialGroup *curp);
 	
-	LLDrawable*	pickDrawable(const LLVector3& start, const LLVector3& end, LLVector3& collision);
+	LLDrawable* lineSegmentIntersect(const LLVector3& start, const LLVector3& end,
+									 S32* face_hit,                          // return the face hit
+									 LLVector3* intersection = NULL,         // return the intersection point
+									 LLVector2* tex_coord = NULL,            // return the texture coordinates of the intersection point
+									 LLVector3* normal = NULL,               // return the surface normal at the intersection point
+									 LLVector3* bi_normal = NULL             // return the surface bi-normal at the intersection point
+		);
+	
 	
 	// If the drawable moves, move it here.
 	virtual void move(LLDrawable *drawablep, LLSpatialGroup *curp, BOOL immediate = FALSE);
diff --git a/indra/newview/lltexturectrl.cpp b/indra/newview/lltexturectrl.cpp
index c522bd069745403d3e0a7e302ceda6dad1406ffa..83b5cdbb66c26e7fe9281797b5e3e6d897e86627 100644
--- a/indra/newview/lltexturectrl.cpp
+++ b/indra/newview/lltexturectrl.cpp
@@ -1452,13 +1452,15 @@ LLToolTexEyedropper::~LLToolTexEyedropper()
 
 BOOL LLToolTexEyedropper::handleMouseDown(S32 x, S32 y, MASK mask)
 {
-	LLViewerObject* hit_obj	= gViewerWindow->lastObjectHit();
+	// this will affect framerate on mouse down
+	const LLPickInfo& pick = gViewerWindow->pickImmediate(x, y, FALSE);
+	LLViewerObject* hit_obj	= pick.getObject();
 	if (hit_obj && 
 		!hit_obj->isAvatar())
 	{
-		if( (0 <= gLastHitObjectFace) && (gLastHitObjectFace < hit_obj->getNumTEs()) )
+		if( (0 <= pick.mObjectFace) && (pick.mObjectFace < hit_obj->getNumTEs()) )
 		{
-			LLViewerImage* image = hit_obj->getTEImage( gLastHitObjectFace );
+			LLViewerImage* image = hit_obj->getTEImage( pick.mObjectFace );
 			if( image )
 			{
 				if( mCallback )
diff --git a/indra/newview/lltoolcomp.cpp b/indra/newview/lltoolcomp.cpp
index 4efc6f74d45fc83c95e213ce74c7e5fa5e687c48..9fd8044cca539f75aa92b6870066b87988dbaeeb 100644
--- a/indra/newview/lltoolcomp.cpp
+++ b/indra/newview/lltoolcomp.cpp
@@ -145,18 +145,18 @@ LLToolCompInspect::~LLToolCompInspect()
 BOOL LLToolCompInspect::handleMouseDown(S32 x, S32 y, MASK mask)
 {
 	mMouseDown = TRUE;
-	gViewerWindow->hitObjectOrLandGlobalAsync(x, y, mask, pickCallback);
+	gViewerWindow->pickAsync(x, y, mask, pickCallback);
 	return TRUE;
 }
 
-void LLToolCompInspect::pickCallback(S32 x, S32 y, MASK mask)
+void LLToolCompInspect::pickCallback(const LLPickInfo& pick_info)
 {
-	LLViewerObject* hit_obj = gViewerWindow->lastObjectHit();
+	LLViewerObject* hit_obj = pick_info.getObject();
 
 	if (!LLToolCompInspect::getInstance()->mMouseDown)
 	{
 		// fast click on object, but mouse is already up...just do select
-		LLToolCompInspect::getInstance()->mSelectRect->handleObjectSelection(hit_obj, mask, gSavedSettings.getBOOL("EditLinkedParts"), FALSE);
+		LLToolCompInspect::getInstance()->mSelectRect->handleObjectSelection(pick_info, gSavedSettings.getBOOL("EditLinkedParts"), FALSE);
 		return;
 	}
 
@@ -167,13 +167,13 @@ void LLToolCompInspect::pickCallback(S32 x, S32 y, MASK mask)
 			LLEditMenuHandler::gEditMenuHandler = LLSelectMgr::getInstance();
 		}
 		LLToolCompInspect::getInstance()->setCurrentTool( LLToolCompInspect::getInstance()->mSelectRect );
-		LLToolCompInspect::getInstance()->mSelectRect->handleMouseDown( x, y, mask );
+		LLToolCompInspect::getInstance()->mSelectRect->handlePick( pick_info );
 
 	}
 	else
 	{
 		LLToolCompInspect::getInstance()->setCurrentTool( LLToolCompInspect::getInstance()->mSelectRect );
-		LLToolCompInspect::getInstance()->mSelectRect->handleMouseDown( x, y, mask);
+		LLToolCompInspect::getInstance()->mSelectRect->handlePick( pick_info );
 	}
 }
 
@@ -218,19 +218,19 @@ BOOL LLToolCompTranslate::handleHover(S32 x, S32 y, MASK mask)
 BOOL LLToolCompTranslate::handleMouseDown(S32 x, S32 y, MASK mask)
 {
 	mMouseDown = TRUE;
-	gViewerWindow->hitObjectOrLandGlobalAsync(x, y, mask, pickCallback, TRUE);
+	gViewerWindow->pickAsync(x, y, mask, pickCallback, TRUE);
 	return TRUE;
 }
 
-void LLToolCompTranslate::pickCallback(S32 x, S32 y, MASK mask)
+void LLToolCompTranslate::pickCallback(const LLPickInfo& pick_info)
 {
-	LLViewerObject* hit_obj = gViewerWindow->lastObjectHit();
+	LLViewerObject* hit_obj = pick_info.getObject();
 
-	LLToolCompTranslate::getInstance()->mManip->highlightManipulators(x, y);
+	LLToolCompTranslate::getInstance()->mManip->highlightManipulators(pick_info.mMousePt.mX, pick_info.mMousePt.mY);
 	if (!LLToolCompTranslate::getInstance()->mMouseDown)
 	{
 		// fast click on object, but mouse is already up...just do select
-		LLToolCompTranslate::getInstance()->mSelectRect->handleObjectSelection(hit_obj, mask, gSavedSettings.getBOOL("EditLinkedParts"), FALSE);
+		LLToolCompTranslate::getInstance()->mSelectRect->handleObjectSelection(pick_info, gSavedSettings.getBOOL("EditLinkedParts"), FALSE);
 		return;
 	}
 
@@ -246,12 +246,12 @@ void LLToolCompTranslate::pickCallback(S32 x, S32 y, MASK mask)
 		if(	LLManip::LL_NO_PART != LLToolCompTranslate::getInstance()->mManip->getHighlightedPart() && can_move)
 		{
 			LLToolCompTranslate::getInstance()->setCurrentTool( LLToolCompTranslate::getInstance()->mManip );
-			LLToolCompTranslate::getInstance()->mManip->handleMouseDownOnPart( x, y, mask );
+			LLToolCompTranslate::getInstance()->mManip->handleMouseDownOnPart( pick_info.mMousePt.mX, pick_info.mMousePt.mY, pick_info.mKeyMask );
 		}
 		else
 		{
 			LLToolCompTranslate::getInstance()->setCurrentTool( LLToolCompTranslate::getInstance()->mSelectRect );
-			LLToolCompTranslate::getInstance()->mSelectRect->handleMouseDown( x, y, mask );
+			LLToolCompTranslate::getInstance()->mSelectRect->handlePick( pick_info );
 
 			// *TODO: add toggle to trigger old click-drag functionality
 			// LLToolCompTranslate::getInstance()->mManip->handleMouseDownOnPart( XY_part, x, y, mask);
@@ -260,7 +260,7 @@ void LLToolCompTranslate::pickCallback(S32 x, S32 y, MASK mask)
 	else
 	{
 		LLToolCompTranslate::getInstance()->setCurrentTool( LLToolCompTranslate::getInstance()->mSelectRect );
-		LLToolCompTranslate::getInstance()->mSelectRect->handleMouseDown( x, y, mask);
+		LLToolCompTranslate::getInstance()->mSelectRect->handlePick( pick_info );
 	}
 }
 
@@ -342,19 +342,19 @@ BOOL LLToolCompScale::handleHover(S32 x, S32 y, MASK mask)
 BOOL LLToolCompScale::handleMouseDown(S32 x, S32 y, MASK mask)
 {
 	mMouseDown = TRUE;
-	gViewerWindow->hitObjectOrLandGlobalAsync(x, y, mask, pickCallback);
+	gViewerWindow->pickAsync(x, y, mask, pickCallback);
 	return TRUE;
 }
 
-void LLToolCompScale::pickCallback(S32 x, S32 y, MASK mask)
+void LLToolCompScale::pickCallback(const LLPickInfo& pick_info)
 {
-	LLViewerObject* hit_obj = gViewerWindow->lastObjectHit();
+	LLViewerObject* hit_obj = pick_info.getObject();
 
-	LLToolCompScale::getInstance()->mManip->highlightManipulators(x, y);
+	LLToolCompScale::getInstance()->mManip->highlightManipulators(pick_info.mMousePt.mX, pick_info.mMousePt.mY);
 	if (!LLToolCompScale::getInstance()->mMouseDown)
 	{
 		// fast click on object, but mouse is already up...just do select
-		LLToolCompScale::getInstance()->mSelectRect->handleObjectSelection(hit_obj, mask, gSavedSettings.getBOOL("EditLinkedParts"), FALSE);
+		LLToolCompScale::getInstance()->mSelectRect->handleObjectSelection(pick_info, gSavedSettings.getBOOL("EditLinkedParts"), FALSE);
 
 		return;
 	}
@@ -368,18 +368,18 @@ void LLToolCompScale::pickCallback(S32 x, S32 y, MASK mask)
 		if(	LLManip::LL_NO_PART != LLToolCompScale::getInstance()->mManip->getHighlightedPart() )
 		{
 			LLToolCompScale::getInstance()->setCurrentTool( LLToolCompScale::getInstance()->mManip );
-			LLToolCompScale::getInstance()->mManip->handleMouseDownOnPart( x, y, mask );
+			LLToolCompScale::getInstance()->mManip->handleMouseDownOnPart( pick_info.mMousePt.mX, pick_info.mMousePt.mY, pick_info.mKeyMask );
 		}
 		else
 		{
 			LLToolCompScale::getInstance()->setCurrentTool( LLToolCompScale::getInstance()->mSelectRect );
-			LLToolCompScale::getInstance()->mSelectRect->handleMouseDown( x, y, mask );
+			LLToolCompScale::getInstance()->mSelectRect->handlePick( pick_info );
 		}
 	}
 	else
 	{
 		LLToolCompScale::getInstance()->setCurrentTool( LLToolCompScale::getInstance()->mSelectRect );
-		LLToolCompScale::getInstance()->mCur->handleMouseDown( x, y, mask );
+		LLToolCompScale::getInstance()->mSelectRect->handlePick( pick_info );
 	}
 }
 
@@ -457,15 +457,15 @@ BOOL LLToolCompCreate::handleMouseDown(S32 x, S32 y, MASK mask)
 	BOOL handled = FALSE;
 	mMouseDown = TRUE;
 
-	if ( !(mask == MASK_SHIFT) && !(mask == MASK_CONTROL) )
+	if ( (mask == MASK_SHIFT) || (mask == MASK_CONTROL) )
 	{
-		setCurrentTool( mPlacer );
-		handled = mPlacer->placeObject( x, y, mask );
+		gViewerWindow->pickAsync(x, y, mask, pickCallback);
+		handled = TRUE;
 	}
 	else
 	{
-		gViewerWindow->hitObjectOrLandGlobalAsync(x, y, mask, pickCallback);
-		handled = TRUE;
+		setCurrentTool( mPlacer );
+		handled = mPlacer->placeObject( x, y, mask );
 	}
 	
 	mObjectPlacedOnMouseDown = TRUE;
@@ -473,15 +473,15 @@ BOOL LLToolCompCreate::handleMouseDown(S32 x, S32 y, MASK mask)
 	return TRUE;
 }
 
-void LLToolCompCreate::pickCallback(S32 x, S32 y, MASK mask)
+void LLToolCompCreate::pickCallback(const LLPickInfo& pick_info)
 {
 	// *NOTE: We mask off shift and control, so you cannot
 	// multi-select multiple objects with the create tool.
-	mask = (mask & ~MASK_SHIFT);
+	MASK mask = (pick_info.mKeyMask & ~MASK_SHIFT);
 	mask = (mask & ~MASK_CONTROL);
 
 	LLToolCompCreate::getInstance()->setCurrentTool( LLToolCompCreate::getInstance()->mSelectRect );
-	LLToolCompCreate::getInstance()->mSelectRect->handleMouseDown( x, y, mask);
+	LLToolCompCreate::getInstance()->mSelectRect->handlePick( pick_info );
 }
 
 BOOL LLToolCompCreate::handleDoubleClick(S32 x, S32 y, MASK mask)
@@ -543,19 +543,19 @@ BOOL LLToolCompRotate::handleHover(S32 x, S32 y, MASK mask)
 BOOL LLToolCompRotate::handleMouseDown(S32 x, S32 y, MASK mask)
 {
 	mMouseDown = TRUE;
-	gViewerWindow->hitObjectOrLandGlobalAsync(x, y, mask, pickCallback);
+	gViewerWindow->pickAsync(x, y, mask, pickCallback);
 	return TRUE;
 }
 
-void LLToolCompRotate::pickCallback(S32 x, S32 y, MASK mask)
+void LLToolCompRotate::pickCallback(const LLPickInfo& pick_info)
 {
-	LLViewerObject* hit_obj = gViewerWindow->lastObjectHit();
+	LLViewerObject* hit_obj = pick_info.getObject();
 
-	LLToolCompRotate::getInstance()->mManip->highlightManipulators(x, y);
+	LLToolCompRotate::getInstance()->mManip->highlightManipulators(pick_info.mMousePt.mX, pick_info.mMousePt.mY);
 	if (!LLToolCompRotate::getInstance()->mMouseDown)
 	{
 		// fast click on object, but mouse is already up...just do select
-		LLToolCompRotate::getInstance()->mSelectRect->handleObjectSelection(hit_obj, mask, gSavedSettings.getBOOL("EditLinkedParts"), FALSE);
+		LLToolCompRotate::getInstance()->mSelectRect->handleObjectSelection(pick_info, gSavedSettings.getBOOL("EditLinkedParts"), FALSE);
 		return;
 	}
 	
@@ -568,18 +568,18 @@ void LLToolCompRotate::pickCallback(S32 x, S32 y, MASK mask)
 		if(	LLManip::LL_NO_PART != LLToolCompRotate::getInstance()->mManip->getHighlightedPart() )
 		{
 			LLToolCompRotate::getInstance()->setCurrentTool( LLToolCompRotate::getInstance()->mManip );
-			LLToolCompRotate::getInstance()->mManip->handleMouseDownOnPart( x, y, mask );
+			LLToolCompRotate::getInstance()->mManip->handleMouseDownOnPart( pick_info.mMousePt.mX, pick_info.mMousePt.mY, pick_info.mKeyMask );
 		}
 		else
 		{
 			LLToolCompRotate::getInstance()->setCurrentTool( LLToolCompRotate::getInstance()->mSelectRect );
-			LLToolCompRotate::getInstance()->mSelectRect->handleMouseDown( x, y, mask );
+			LLToolCompRotate::getInstance()->mSelectRect->handlePick( pick_info );
 		}
 	}
 	else
 	{
 		LLToolCompRotate::getInstance()->setCurrentTool( LLToolCompRotate::getInstance()->mSelectRect );
-		LLToolCompRotate::getInstance()->mCur->handleMouseDown( x, y, mask );
+		LLToolCompRotate::getInstance()->mSelectRect->handlePick( pick_info );
 	}
 }
 
diff --git a/indra/newview/lltoolcomp.h b/indra/newview/lltoolcomp.h
index 8926cd34d21dbc778b5f4d7ba133156c22b69dca..5547a6d15b01e4607df07016adb728938b6579f9 100644
--- a/indra/newview/lltoolcomp.h
+++ b/indra/newview/lltoolcomp.h
@@ -37,6 +37,7 @@
 class LLManip;
 class LLToolSelectRect;
 class LLToolPlacer;
+class LLPickInfo;
 
 class LLView;
 class LLTextBox;
@@ -114,7 +115,7 @@ class LLToolCompInspect : public LLToolComposite, public LLSingleton<LLToolCompI
     virtual BOOL		handleMouseDown(S32 x, S32 y, MASK mask);
     virtual BOOL		handleDoubleClick(S32 x, S32 y, MASK mask);
 
-	static void pickCallback(S32 x, S32 y, MASK mask);
+	static void pickCallback(const LLPickInfo& pick_info);
 };
 
 //-----------------------------------------------------------------------
@@ -135,7 +136,7 @@ class LLToolCompTranslate : public LLToolComposite, public LLSingleton<LLToolCom
 
 	virtual LLTool*		getOverrideTool(MASK mask);
 
-	static void pickCallback(S32 x, S32 y, MASK mask);
+	static void pickCallback(const LLPickInfo& pick_info);
 };
 
 //-----------------------------------------------------------------------
@@ -156,7 +157,7 @@ class LLToolCompScale : public LLToolComposite, public LLSingleton<LLToolCompSca
 
 	virtual LLTool*		getOverrideTool(MASK mask);
 	
-	static void pickCallback(S32 x, S32 y, MASK mask);
+	static void pickCallback(const LLPickInfo& pick_info);
 };
 
 
@@ -178,7 +179,7 @@ class LLToolCompRotate : public LLToolComposite, public LLSingleton<LLToolCompRo
 
 	virtual LLTool*		getOverrideTool(MASK mask);
 
-	static void pickCallback(S32 x, S32 y, MASK mask);
+	static void pickCallback(const LLPickInfo& pick_info);
 
 protected:
 };
@@ -197,7 +198,7 @@ class LLToolCompCreate : public LLToolComposite, public LLSingleton<LLToolCompCr
     virtual BOOL		handleDoubleClick(S32 x, S32 y, MASK mask);
 	virtual BOOL		handleMouseUp(S32 x, S32 y, MASK mask);
 	
-	static void pickCallback(S32 x, S32 y, MASK mask);
+	static void pickCallback(const LLPickInfo& pick_info);
 protected:
 	LLToolPlacer*		mPlacer;
 	BOOL				mObjectPlacedOnMouseDown;
diff --git a/indra/newview/lltooldraganddrop.cpp b/indra/newview/lltooldraganddrop.cpp
index 940f5ba62d92449118ca5f945ee98fdf5fbe7dd9..109a11755bbfe87fead3cdd08090c4a1122d2cce 100644
--- a/indra/newview/lltooldraganddrop.cpp
+++ b/indra/newview/lltooldraganddrop.cpp
@@ -988,32 +988,37 @@ void LLToolDragAndDrop::dragOrDrop3D( S32 x, S32 y, MASK mask, BOOL drop, EAccep
 	mDrop = drop;
 	if (mDrop)
 	{
-		gPickFaces = TRUE;
 		// don't allow drag and drop onto transparent objects
-		gViewerWindow->hitObjectOrLandGlobalImmediate(x, y, pickCallback, FALSE);
+		pickCallback(gViewerWindow->pickImmediate(x, y, FALSE));
 	}
 	else
 	{
-		// Don't pick faces during hover.  Nothing currently requires per-face
-		// data.
 		// don't allow drag and drop onto transparent objects
-		gViewerWindow->hitObjectOrLandGlobalAsync(x, y, mask, pickCallback, FALSE);
+		gViewerWindow->pickAsync(x, y, mask, pickCallback, FALSE);
 	}
 
 	*acceptance = mLastAccept;
 }
 
-void LLToolDragAndDrop::pickCallback(S32 x, S32 y, MASK mask)
+void LLToolDragAndDrop::pickCallback(const LLPickInfo& pick_info)
 {
 	EDropTarget target = DT_NONE;
 	S32	hit_face = -1;
 
-	LLViewerObject* hit_obj = gViewerWindow->lastNonFloraObjectHit();
+	LLViewerObject* hit_obj = pick_info.getObject();
 	LLSelectMgr::getInstance()->unhighlightAll();
 
 	// Treat attachments as part of the avatar they are attached to.
 	if (hit_obj)
 	{
+		// don't allow drag and drop on grass, trees, etc.
+		if(pick_info.mPickType == LLPickInfo::PICK_FLORA)
+		{
+			LLToolDragAndDrop::getInstance()->mCursor = UI_CURSOR_NO;
+			gViewerWindow->getWindow()->setCursor( LLToolDragAndDrop::getInstance()->mCursor );
+			return;
+		}
+
 		if(hit_obj->isAttachment() && !hit_obj->isHUDAttachment())
 		{
 			LLVOAvatar* avatar = LLVOAvatar::findAvatarFromAttachment( hit_obj );
@@ -1044,12 +1049,12 @@ void LLToolDragAndDrop::pickCallback(S32 x, S32 y, MASK mask)
 		else
 		{
 			target = DT_OBJECT;
-			hit_face = gLastHitNonFloraObjectFace;
+			hit_face = pick_info.mObjectFace;
 			// if any item being dragged will be applied to the object under our cursor
 			// highlight that object
 			for (S32 i = 0; i < (S32)LLToolDragAndDrop::getInstance()->mCargoIDs.size(); i++)
 			{
-				if (LLToolDragAndDrop::getInstance()->mCargoTypes[i] != DAD_OBJECT || (mask & MASK_CONTROL))
+				if (LLToolDragAndDrop::getInstance()->mCargoTypes[i] != DAD_OBJECT || (pick_info.mKeyMask & MASK_CONTROL))
 				{
 					LLSelectMgr::getInstance()->highlightObjectAndFamily(hit_obj);
 					break;
@@ -1057,7 +1062,7 @@ void LLToolDragAndDrop::pickCallback(S32 x, S32 y, MASK mask)
 			}
 		}
 	}
-	else if(gLastHitLand)
+	else if(pick_info.mPickType == LLPickInfo::PICK_LAND)
 	{
 		target = DT_LAND;
 		hit_face = -1;
@@ -1073,7 +1078,7 @@ void LLToolDragAndDrop::pickCallback(S32 x, S32 y, MASK mask)
 			(U32)LLToolDragAndDrop::getInstance()->mLastAccept,
 			(U32)callMemberFunction((*LLToolDragAndDrop::getInstance()), 
 				LLToolDragAndDrop::getInstance()->sDragAndDrop3d[LLToolDragAndDrop::getInstance()->mCargoTypes[LLToolDragAndDrop::getInstance()->mCurItemIndex]][target])
-				(hit_obj, hit_face, mask, FALSE));
+				(hit_obj, hit_face, pick_info.mKeyMask, FALSE));
 	}
 
 	if (LLToolDragAndDrop::getInstance()->mDrop && (U32)LLToolDragAndDrop::getInstance()->mLastAccept >= ACCEPT_YES_COPY_SINGLE)
@@ -1095,7 +1100,7 @@ void LLToolDragAndDrop::pickCallback(S32 x, S32 y, MASK mask)
 			// Call the right implementation function
 			(U32)callMemberFunction((*LLToolDragAndDrop::getInstance()), 
 				LLToolDragAndDrop::getInstance()->sDragAndDrop3d[LLToolDragAndDrop::getInstance()->mCargoTypes[LLToolDragAndDrop::getInstance()->mCurItemIndex]][target])
-				(hit_obj, hit_face, mask, TRUE);
+				(hit_obj, hit_face, pick_info.mKeyMask, TRUE);
 		}
 	}
 
@@ -1142,7 +1147,7 @@ void LLToolDragAndDrop::pickCallback(S32 x, S32 y, MASK mask)
 		llassert( FALSE );
 	}
 
-	LLToolDragAndDrop::getInstance()->mLastHitPos = gLastHitPosGlobal + gLastHitObjectOffset;
+	LLToolDragAndDrop::getInstance()->mLastHitPos = pick_info.mPosGlobal;
 	LLToolDragAndDrop::getInstance()->mLastCameraPos = gAgent.getCameraPositionGlobal();
 
 	gViewerWindow->getWindow()->setCursor( LLToolDragAndDrop::getInstance()->mCursor );
diff --git a/indra/newview/lltooldraganddrop.h b/indra/newview/lltooldraganddrop.h
index 8f6d1713fa68ab19247c230547800ed597bf0a31..8e2788a1739033c97a6c5112021578a9e6ff3600 100644
--- a/indra/newview/lltooldraganddrop.h
+++ b/indra/newview/lltooldraganddrop.h
@@ -45,6 +45,7 @@
 class LLToolDragAndDrop;
 class LLViewerRegion;
 class LLVOAvatar;
+class LLPickInfo;
 
 class LLToolDragAndDrop : public LLTool, public LLSingleton<LLToolDragAndDrop>
 {
@@ -106,7 +107,7 @@ class LLToolDragAndDrop : public LLTool, public LLSingleton<LLToolDragAndDrop>
 					EAcceptance* acceptance);
 	void dragOrDrop3D(S32 x, S32 y, MASK mask, BOOL drop,
 					  EAcceptance* acceptance);
-	static void pickCallback(S32 x, S32 y, MASK mask);
+	static void pickCallback(const LLPickInfo& pick_info);
 
 protected:
 
diff --git a/indra/newview/lltoolface.cpp b/indra/newview/lltoolface.cpp
index 324891318288e6f5a66243f3666eaad204065579..138fc9960687ff16d8fd3596f88f4f1a9bc34df9 100644
--- a/indra/newview/lltoolface.cpp
+++ b/indra/newview/lltoolface.cpp
@@ -82,17 +82,16 @@ BOOL LLToolFace::handleDoubleClick(S32 x, S32 y, MASK mask)
 
 BOOL LLToolFace::handleMouseDown(S32 x, S32 y, MASK mask)
 {
-	gPickFaces = TRUE;
-	gViewerWindow->hitObjectOrLandGlobalAsync(x, y, mask, pickCallback);
+	gViewerWindow->pickAsync(x, y, mask, pickCallback);
 	return TRUE;
 }
 
-void LLToolFace::pickCallback(S32 x, S32 y, MASK mask)
+void LLToolFace::pickCallback(const LLPickInfo& pick_info)
 {
-	LLViewerObject* hit_obj	= gViewerWindow->lastObjectHit();
+	LLViewerObject* hit_obj	= pick_info.getObject();
 	if (hit_obj)
 	{
-		S32 hit_face = gLastHitObjectFace;
+		S32 hit_face = pick_info.mObjectFace;
 		
 		if (hit_obj->isAvatar())
 		{
@@ -102,7 +101,7 @@ void LLToolFace::pickCallback(S32 x, S32 y, MASK mask)
 
 		// ...clicked on a world object, try to pick the appropriate face
 
-		if (mask & MASK_SHIFT)
+		if (pick_info.mKeyMask & MASK_SHIFT)
 		{
 			// If object not selected, need to inform sim
 			if ( !hit_obj->isSelected() )
@@ -133,7 +132,7 @@ void LLToolFace::pickCallback(S32 x, S32 y, MASK mask)
 	}
 	else
 	{
-		if (!(mask == MASK_SHIFT))
+		if (!(pick_info.mKeyMask == MASK_SHIFT))
 		{
 			LLSelectMgr::getInstance()->deselectAll();
 		}
diff --git a/indra/newview/lltoolface.h b/indra/newview/lltoolface.h
index f573bfa9c50e4fea05b2182edb14c190815a812a..113dcc9fbd814e0c76ee805750c24a5da3866130 100644
--- a/indra/newview/lltoolface.h
+++ b/indra/newview/lltoolface.h
@@ -35,6 +35,7 @@
 #include "lltool.h"
 
 class LLViewerObject;
+class LLPickInfo;
 
 class LLToolFace
 :	public LLTool, public LLSingleton<LLToolFace>
@@ -49,7 +50,7 @@ class LLToolFace
 	virtual void	handleDeselect();
 	virtual void	render();			// draw face highlights
 
-	static void pickCallback(S32 x, S32 y, MASK mask);
+	static void pickCallback(const LLPickInfo& pick_info);
 };
 
 #endif
diff --git a/indra/newview/lltoolfocus.cpp b/indra/newview/lltoolfocus.cpp
index b46d25583a68701e3581fac0067a72067020ae17..3fdf24bbc367687ae61c111ed598293d33f9c2d2 100644
--- a/indra/newview/lltoolfocus.cpp
+++ b/indra/newview/lltoolfocus.cpp
@@ -126,28 +126,28 @@ BOOL LLToolCamera::handleMouseDown(S32 x, S32 y, MASK mask)
 
 	gViewerWindow->hideCursor();
 
-	gViewerWindow->hitObjectOrLandGlobalAsync(x, y, mask, pickCallback);
+	gViewerWindow->pickAsync(x, y, mask, pickCallback);
 	// don't steal focus from UI
 	return FALSE;
 }
 
-void LLToolCamera::pickCallback(S32 x, S32 y, MASK mask)
+void LLToolCamera::pickCallback(const LLPickInfo& pick_info)
 {
 	if (!LLToolCamera::getInstance()->hasMouseCapture())
 	{
 		return;
 	}
 
-	LLToolCamera::getInstance()->mMouseDownX = x;
-	LLToolCamera::getInstance()->mMouseDownY = y;
+	LLToolCamera::getInstance()->mMouseDownX = pick_info.mMousePt.mX;
+	LLToolCamera::getInstance()->mMouseDownY = pick_info.mMousePt.mY;
 
 	gViewerWindow->moveCursorToCenter();
 
 	// Potentially recenter if click outside rectangle
-	LLViewerObject* hit_obj = gViewerWindow->lastObjectHit();
+	LLViewerObject* hit_obj = pick_info.getObject();
 
 	// Check for hit the sky, or some other invalid point
-	if (!hit_obj && gLastHitPosGlobal.isExactlyZero())
+	if (!hit_obj && pick_info.mPosGlobal.isExactlyZero())
 	{
 		LLToolCamera::getInstance()->mValidClickPoint = FALSE;
 		return;
@@ -195,29 +195,27 @@ void LLToolCamera::pickCallback(S32 x, S32 y, MASK mask)
 		}
 	}
 	//RN: check to see if this is mouse-driving as opposed to ALT-zoom or Focus tool
-	else if (mask & MASK_ALT || 
+	else if (pick_info.mKeyMask & MASK_ALT || 
 			(LLToolMgr::getInstance()->getCurrentTool()->getName() == "Camera")) 
 	{
-		LLViewerObject* hit_obj = gViewerWindow->lastObjectHit();
+		LLViewerObject* hit_obj = pick_info.getObject();
 		if (hit_obj)
 		{
 			// ...clicked on a world object, so focus at its position
-			// Use "gLastHitPosGlobal" because it's correct for avatar heads,
-			// not pelvis.
 			if (!hit_obj->isHUDAttachment())
 			{
 				gAgent.setFocusOnAvatar(FALSE, ANIMATE);
-				gAgent.setFocusGlobal( gLastHitObjectOffset + gLastHitPosGlobal, gLastHitObjectID);
+				gAgent.setFocusGlobal(pick_info);
 			}
 		}
-		else if (!gLastHitPosGlobal.isExactlyZero())
+		else if (!pick_info.mPosGlobal.isExactlyZero())
 		{
 			// Hit the ground
 			gAgent.setFocusOnAvatar(FALSE, ANIMATE);
-			gAgent.setFocusGlobal( gLastHitPosGlobal, gLastHitObjectID);
+			gAgent.setFocusGlobal(pick_info);
 		}
 
-		if (!(mask & MASK_ALT) &&
+		if (!(pick_info.mKeyMask & MASK_ALT) &&
 			gAgent.cameraThirdPerson() &&
 			gViewerWindow->getLeftMouseDown() && 
 			!gSavedSettings.getBOOL("FreezeTime") &&
@@ -238,7 +236,7 @@ void LLToolCamera::pickCallback(S32 x, S32 y, MASK mask)
 		LLVector3d cam_pos = gAgent.getCameraPositionGlobal();
 		cam_pos -= LLVector3d(LLViewerCamera::getInstance()->getLeftAxis() * gAgent.calcCustomizeAvatarUIOffset( cam_pos ));
 
-		gAgent.setCameraPosAndFocusGlobal( cam_pos, gLastHitObjectOffset + gLastHitPosGlobal, gLastHitObjectID);
+		gAgent.setCameraPosAndFocusGlobal( cam_pos, pick_info.mPosGlobal, pick_info.mObjectID);
 	}
 }
 
diff --git a/indra/newview/lltoolfocus.h b/indra/newview/lltoolfocus.h
index 2ed456b188171bc42d4d332a275fe48ba9f942fc..c8c748a1b013de99ef6f32367f06a05d09ea931e 100644
--- a/indra/newview/lltoolfocus.h
+++ b/indra/newview/lltoolfocus.h
@@ -34,6 +34,8 @@
 
 #include "lltool.h"
 
+class LLPickInfo;
+
 class LLToolCamera
 :	public LLTool, public LLSingleton<LLToolCamera>
 {
@@ -52,7 +54,7 @@ class LLToolCamera
 
 	virtual LLTool*	getOverrideTool(MASK mask) { return NULL; }
 
-	static void pickCallback(S32 x, S32 y, MASK mask);
+	static void pickCallback(const LLPickInfo& pick_info);
 	BOOL mouseSteerMode() { return mMouseSteering; }
 
 protected:
diff --git a/indra/newview/lltoolgrab.cpp b/indra/newview/lltoolgrab.cpp
index a41e9381f69f8bd4e9e23fe25e2b6501cda69be6..13037c6a20b36b3587abe6fb026eac7fd792e604 100644
--- a/indra/newview/lltoolgrab.cpp
+++ b/indra/newview/lltoolgrab.cpp
@@ -78,16 +78,6 @@ LLToolGrab::LLToolGrab( LLToolComposite* composite )
 :	LLTool( std::string("Grab"), composite ),
 	mMode( GRAB_INACTIVE ),
 	mVerticalDragging( FALSE ),
-	mHitLand(FALSE),
-	mHitObjectID(),
-	mGrabObject( NULL ),
-	mLastMouseX(0),
-	mLastMouseY(0),
-	mMouseDownX( -1 ),
-	mMouseDownY( -1 ),
-	mMouseMask(0),
-	mAccumDeltaX(0),
-	mAccumDeltaY(0),
 	mHasMoved( FALSE ),
 	mOutsideSlop(FALSE),
 	mDeselectedThisClick(FALSE),
@@ -138,24 +128,23 @@ BOOL LLToolGrab::handleMouseDown(S32 x, S32 y, MASK mask)
 		llinfos << "LLToolGrab handleMouseDown" << llendl;
 	}
 
-	mHitLand = FALSE;
-
 	// call the base class to propogate info to sim
 	LLTool::handleMouseDown(x, y, mask);
 	
 	if (!gAgent.leftButtonGrabbed())
 	{
 		// can grab transparent objects (how touch event propagates, scripters rely on this)
-		gViewerWindow->hitObjectOrLandGlobalAsync(x, y, mask, pickCallback, TRUE);
+		gViewerWindow->pickAsync(x, y, mask, pickCallback, TRUE);
 	}
 	return TRUE;
 }
 
-void LLToolGrab::pickCallback(S32 x, S32 y, MASK mask)
+void LLToolGrab::pickCallback(const LLPickInfo& pick_info)
 {
-	LLViewerObject	*objectp = gObjectList.findObject( gLastHitObjectID );
+	LLToolGrab::getInstance()->mGrabPick = pick_info;
+	LLViewerObject	*objectp = pick_info.getObject();
 
-	BOOL extend_select = (mask & MASK_SHIFT);
+	BOOL extend_select = (pick_info.mKeyMask & MASK_SHIFT);
 
 	if (!extend_select && !LLSelectMgr::getInstance()->getSelection()->isEmpty())
 	{
@@ -172,23 +161,22 @@ void LLToolGrab::pickCallback(S32 x, S32 y, MASK mask)
 	{
 		LLToolGrab::getInstance()->setMouseCapture(TRUE);
 		LLToolGrab::getInstance()->mMode = GRAB_NOOBJECT;
-		LLToolGrab::getInstance()->mHitObjectID.setNull();
+		LLToolGrab::getInstance()->mGrabPick.mObjectID.setNull();
 	}
 	else
 	{
-		LLToolGrab::getInstance()->handleObjectHit(objectp, x, y, mask);
+		LLToolGrab::getInstance()->handleObjectHit(LLToolGrab::getInstance()->mGrabPick);
 	}
 }
 
-BOOL LLToolGrab::handleObjectHit(LLViewerObject *objectp, S32 x, S32 y, MASK mask)
+BOOL LLToolGrab::handleObjectHit(const LLPickInfo& info)
 {
-	mMouseDownX = x;
-	mMouseDownY = y;
-	mMouseMask = mask;
+	mGrabPick = info;
+	LLViewerObject* objectp = mGrabPick.getObject();
 
 	if (gDebugClicks)
 	{
-		llinfos << "LLToolGrab handleObjectHit " << mMouseDownX << "," << mMouseDownY << llendl;
+		llinfos << "LLToolGrab handleObjectHit " << info.mMousePt.mX << "," << info.mMousePt.mY << llendl;
 	}
 
 	if (NULL == objectp) // unexpected
@@ -209,8 +197,6 @@ BOOL LLToolGrab::handleObjectHit(LLViewerObject *objectp, S32 x, S32 y, MASK mas
 
 	setMouseCapture( TRUE );
 
-	mHitObjectID = objectp->getID();
-
 	// Grabs always start from the root
 	// objectp = (LLViewerObject *)objectp->getRoot();
 
@@ -230,13 +216,13 @@ BOOL LLToolGrab::handleObjectHit(LLViewerObject *objectp, S32 x, S32 y, MASK mas
 		if (gAgent.cameraMouselook() && !script_touch)
 		{
 			mMode = GRAB_LOCKED;
+			gViewerWindow->hideCursor();
+			gViewerWindow->moveCursorToCenter();
 		}
 		else
 		{
 			mMode = GRAB_NONPHYSICAL;
 		}
-		gViewerWindow->hideCursor();
-		gViewerWindow->moveCursorToCenter();
 		// Don't bail out here, go on and grab so buttons can get
 		// their "touched" event.
 	}
@@ -261,20 +247,18 @@ BOOL LLToolGrab::handleObjectHit(LLViewerObject *objectp, S32 x, S32 y, MASK mas
 
 	// Always send "touched" message
 
+	mLastMouseX = gViewerWindow->getCurrentMouseX();
+	mLastMouseY = gViewerWindow->getCurrentMouseY();
 	mAccumDeltaX = 0;
 	mAccumDeltaY = 0;
 	mHasMoved = FALSE;
 	mOutsideSlop = FALSE;
 
-	mGrabObject = objectp;
+	mVerticalDragging = (info.mKeyMask == MASK_VERTICAL) || gGrabBtnVertical;
 
-	mGrabOffset.clearVec();
+	startGrab();
 
-	mVerticalDragging = (mask == MASK_VERTICAL) || gGrabBtnVertical;
-
-	startGrab(x, y);
-
-	if ((mask == MASK_SPIN) || gGrabBtnSpin)
+	if ((info.mKeyMask == MASK_SPIN) || gGrabBtnSpin)
 	{
 		startSpin();
 	}
@@ -282,10 +266,10 @@ BOOL LLToolGrab::handleObjectHit(LLViewerObject *objectp, S32 x, S32 y, MASK mas
 	LLSelectMgr::getInstance()->updateSelectionCenter();		// update selection beam
 
 	// update point at
-	LLViewerObject *edit_object = gObjectList.findObject(mHitObjectID);
-	if (edit_object)
+	LLViewerObject *edit_object = info.getObject();
+	if (edit_object && info.mPickType != LLPickInfo::PICK_FLORA)
 	{
-		LLVector3 local_edit_point = gAgent.getPosAgentFromGlobal(gLastHitNonFloraPosGlobal);
+		LLVector3 local_edit_point = gAgent.getPosAgentFromGlobal(info.mPosGlobal);
 		local_edit_point -= edit_object->getPositionAgent();
 		local_edit_point = local_edit_point * ~edit_object->getRenderRotation();
 		gAgent.setPointAt(POINTAT_TARGET_GRAB, edit_object, local_edit_point );
@@ -307,10 +291,15 @@ BOOL LLToolGrab::handleObjectHit(LLViewerObject *objectp, S32 x, S32 y, MASK mas
 
 void LLToolGrab::startSpin()
 {
+	LLViewerObject* objectp = mGrabPick.getObject();
+	if (!objectp)
+	{
+		return;
+	}
 	mSpinGrabbing = TRUE;
 
 	// Was saveSelectedObjectTransform()
-	LLViewerObject *root = (LLViewerObject *)mGrabObject->getRoot();
+	LLViewerObject *root = (LLViewerObject *)objectp->getRoot();
 	mSpinRotation = root->getRotation();
 
 	LLMessageSystem *msg = gMessageSystem;
@@ -319,8 +308,8 @@ void LLToolGrab::startSpin()
 	msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
 	msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
 	msg->nextBlockFast(_PREHASH_ObjectData);
-	msg->addUUIDFast(_PREHASH_ObjectID, mGrabObject->getID() );
-	msg->sendMessage( mGrabObject->getRegion()->getHost() );
+	msg->addUUIDFast(_PREHASH_ObjectID, mGrabPick.mObjectID );
+	msg->sendMessage( objectp->getRegion()->getHost() );
 }
 
 
@@ -328,6 +317,12 @@ void LLToolGrab::stopSpin()
 {
 	mSpinGrabbing = FALSE;
 
+	LLViewerObject* objectp = mGrabPick.getObject();
+	if (!objectp)
+	{
+		return;
+	}
+
 	LLMessageSystem *msg = gMessageSystem;
 	switch(mMode)
 	{
@@ -339,8 +334,8 @@ void LLToolGrab::stopSpin()
 		msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
 		msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
 		msg->nextBlockFast(_PREHASH_ObjectData);
-		msg->addUUIDFast(_PREHASH_ObjectID, mGrabObject->getID() );
-		msg->sendMessage( mGrabObject->getRegion()->getHost() );
+		msg->addUUIDFast(_PREHASH_ObjectID, objectp->getID() );
+		msg->sendMessage( objectp->getRegion()->getHost() );
 		break;
 
 	case GRAB_NOOBJECT:
@@ -352,11 +347,17 @@ void LLToolGrab::stopSpin()
 }
 
 
-void LLToolGrab::startGrab(S32 x, S32 y)
+void LLToolGrab::startGrab()
 {
 	// Compute grab_offset in the OBJECT's root's coordinate frame
 	// (sometimes root == object)
-	LLViewerObject *root = (LLViewerObject *)mGrabObject->getRoot();
+	LLViewerObject* objectp = mGrabPick.getObject();
+	if (!objectp)
+	{
+		return;
+	}
+
+	LLViewerObject *root = (LLViewerObject *)objectp->getRoot();
 
 	// drag from center
 	LLVector3d grab_start_global = root->getPositionGlobal();
@@ -365,7 +366,7 @@ void LLToolGrab::startGrab(S32 x, S32 y)
 	// JC - This code looks wonky, but I believe it does the right thing.
 	// Otherwise, when you grab a linked object set, it "pops" on the start
 	// of the drag.
-	LLVector3d grab_offsetd = root->getPositionGlobal() - mGrabObject->getPositionGlobal();
+	LLVector3d grab_offsetd = root->getPositionGlobal() - objectp->getPositionGlobal();
 
 	LLVector3 grab_offset;
 	grab_offset.setVec(grab_offsetd);
@@ -384,9 +385,16 @@ void LLToolGrab::startGrab(S32 x, S32 y)
 	msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
 	msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
 	msg->nextBlockFast(_PREHASH_ObjectData);
-	msg->addU32Fast(_PREHASH_LocalID, mGrabObject->mLocalID);
+	msg->addU32Fast(_PREHASH_LocalID, objectp->mLocalID);
 	msg->addVector3Fast(_PREHASH_GrabOffset, grab_offset );
-	msg->sendMessage( mGrabObject->getRegion()->getHost());
+	msg->nextBlock("SurfaceInfo");
+	msg->addVector3("UVCoord", LLVector3(mGrabPick.mUVCoords));
+	msg->addVector3("STCoord", LLVector3(mGrabPick.mSTCoords));
+	msg->addS32Fast(_PREHASH_FaceIndex, mGrabPick.mObjectFace);
+	msg->addVector3("Position", mGrabPick.mIntersection);
+	msg->addVector3("Normal", mGrabPick.mNormal);
+	msg->addVector3("Binormal", mGrabPick.mBinormal);
+	msg->sendMessage( objectp->getRegion()->getHost());
 
 	mGrabOffsetFromCenterInitial = grab_offset;
 	mGrabHiddenOffsetFromCamera = mDragStartFromCamera;
@@ -397,9 +405,6 @@ void LLToolGrab::startGrab(S32 x, S32 y)
 
 BOOL LLToolGrab::handleHover(S32 x, S32 y, MASK mask)
 {
-	mLastMouseX = x;
-	mLastMouseY = y;
-
 	if (!gViewerWindow->getLeftMouseDown())
 	{
 		gViewerWindow->setCursor(UI_CURSOR_TOOLGRAB);
@@ -411,9 +416,12 @@ BOOL LLToolGrab::handleHover(S32 x, S32 y, MASK mask)
 	switch( mMode )
 	{
 	case GRAB_ACTIVE_CENTER:
-	case GRAB_NONPHYSICAL:
 		handleHoverActive( x, y, mask );	// cursor hidden
 		break;
+		
+	case GRAB_NONPHYSICAL:
+		handleHoverNonPhysical(x, y, mask);
+		break;
 
 	case GRAB_INACTIVE:
 		handleHoverInactive( x, y, mask );  // cursor set here
@@ -426,18 +434,24 @@ BOOL LLToolGrab::handleHover(S32 x, S32 y, MASK mask)
 
 	}
 
+	mLastMouseX = x;
+	mLastMouseY = y;
+	
 	return TRUE;
 }
 
+const F32 GRAB_SENSITIVITY_X = 0.0075f;
+const F32 GRAB_SENSITIVITY_Y = 0.0075f;
+
 
 
 		
 // Dragging.
 void LLToolGrab::handleHoverActive(S32 x, S32 y, MASK mask)
 {
-	llassert( hasMouseCapture() );
-	llassert( mGrabObject );
-	if (mGrabObject->isDead())
+	LLViewerObject* objectp = mGrabPick.getObject();
+	if (!objectp || !hasMouseCapture() ) return;
+	if (objectp->isDead())
 	{
 		// Bail out of drag because object has been killed
 		setMouseCapture(FALSE);
@@ -466,7 +480,7 @@ void LLToolGrab::handleHoverActive(S32 x, S32 y, MASK mask)
 		// ...switch to horizontal dragging
 		mVerticalDragging = FALSE;
 
-		mDragStartPointGlobal = gViewerWindow->clickPointInWorldGlobal(x, y, mGrabObject);
+		mDragStartPointGlobal = gViewerWindow->clickPointInWorldGlobal(x, y, objectp);
 		mDragStartFromCamera = mDragStartPointGlobal - gAgent.getCameraPositionGlobal();
 	}
 	else if (!mVerticalDragging && (mask == MASK_VERTICAL) )
@@ -474,16 +488,13 @@ void LLToolGrab::handleHoverActive(S32 x, S32 y, MASK mask)
 		// ...switch to vertical dragging
 		mVerticalDragging = TRUE;
 
-		mDragStartPointGlobal = gViewerWindow->clickPointInWorldGlobal(x, y, mGrabObject);
+		mDragStartPointGlobal = gViewerWindow->clickPointInWorldGlobal(x, y, objectp);
 		mDragStartFromCamera = mDragStartPointGlobal - gAgent.getCameraPositionGlobal();
 	}
 
 	const F32 RADIANS_PER_PIXEL_X = 0.01f;
 	const F32 RADIANS_PER_PIXEL_Y = 0.01f;
 
-	const F32 SENSITIVITY_X = 0.0075f;
-	const F32 SENSITIVITY_Y = 0.0075f;
-
 	S32 dx = x - (gViewerWindow->getWindowWidth() / 2);
 	S32 dy = y - (gViewerWindow->getWindowHeight() / 2);
 
@@ -525,9 +536,9 @@ void LLToolGrab::handleHoverActive(S32 x, S32 y, MASK mask)
 			msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
 			msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
 			msg->nextBlockFast(_PREHASH_ObjectData);
-			msg->addUUIDFast(_PREHASH_ObjectID, mGrabObject->getID() );
+			msg->addUUIDFast(_PREHASH_ObjectID, objectp->getID() );
 			msg->addQuatFast(_PREHASH_Rotation, mSpinRotation );
-			msg->sendMessage( mGrabObject->getRegion()->getHost() );
+			msg->sendMessage( objectp->getRegion()->getHost() );
 		}
 		else
 		{
@@ -555,8 +566,8 @@ void LLToolGrab::handleHoverActive(S32 x, S32 y, MASK mask)
 			}
 
 			mGrabHiddenOffsetFromCamera = mGrabHiddenOffsetFromCamera 
-				+ (x_part * (-dx * SENSITIVITY_X)) 
-				+ (y_part * ( dy * SENSITIVITY_Y));
+				+ (x_part * (-dx * GRAB_SENSITIVITY_X)) 
+				+ (y_part * ( dy * GRAB_SENSITIVITY_Y));
 
 
 			// Send the message to the viewer.
@@ -640,7 +651,7 @@ void LLToolGrab::handleHoverActive(S32 x, S32 y, MASK mask)
 				&& (grab_center_gl.mY > 24))
 			{
 				// Transmit update to simulator
-				LLVector3 grab_pos_region = mGrabObject->getRegion()->getPosRegionFromGlobal( grab_point_global );
+				LLVector3 grab_pos_region = objectp->getRegion()->getPosRegionFromGlobal( grab_point_global );
 
 				LLMessageSystem *msg = gMessageSystem;
 				msg->newMessageFast(_PREHASH_ObjectGrabUpdate);
@@ -648,11 +659,19 @@ void LLToolGrab::handleHoverActive(S32 x, S32 y, MASK mask)
 				msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
 				msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
 				msg->nextBlockFast(_PREHASH_ObjectData);
-				msg->addUUIDFast(_PREHASH_ObjectID, mGrabObject->getID() );
+				msg->addUUIDFast(_PREHASH_ObjectID, objectp->getID() );
 				msg->addVector3Fast(_PREHASH_GrabOffsetInitial, mGrabOffsetFromCenterInitial );
 				msg->addVector3Fast(_PREHASH_GrabPosition, grab_pos_region );
 				msg->addU32Fast(_PREHASH_TimeSinceLast, dt_milliseconds );
-				msg->sendMessage( mGrabObject->getRegion()->getHost() );
+				msg->nextBlock("SurfaceInfo");
+				msg->addVector3("UVCoord", LLVector3(mGrabPick.mUVCoords));
+				msg->addVector3("STCoord", LLVector3(mGrabPick.mSTCoords));
+				msg->addS32Fast(_PREHASH_FaceIndex, mGrabPick.mObjectFace);
+				msg->addVector3("Position", mGrabPick.mIntersection);
+				msg->addVector3("Normal", mGrabPick.mNormal);
+				msg->addVector3("Binormal", mGrabPick.mBinormal);
+
+				msg->sendMessage( objectp->getRegion()->getHost() );
 			}
 		}
 
@@ -666,8 +685,8 @@ void LLToolGrab::handleHoverActive(S32 x, S32 y, MASK mask)
 	if (mHasMoved)
 	{
 		if (!gAgent.cameraMouselook() && 
-			!mGrabObject->isHUDAttachment() && 
-			mGrabObject->getRoot() == gAgent.getAvatarObject()->getRoot())
+			!objectp->isHUDAttachment() && 
+			objectp->getRoot() == gAgent.getAvatarObject()->getRoot())
 		{
 			// force focus to point in space where we were looking previously
 			gAgent.setFocusGlobal(gAgent.calcFocusPositionTargetGlobal(), LLUUID::null);
@@ -686,6 +705,134 @@ void LLToolGrab::handleHoverActive(S32 x, S32 y, MASK mask)
 }
  
 
+void LLToolGrab::handleHoverNonPhysical(S32 x, S32 y, MASK mask)
+{
+	LLViewerObject* objectp = mGrabPick.getObject();
+	if (!objectp || !hasMouseCapture() ) return;
+	if (objectp->isDead())
+	{
+		// Bail out of drag because object has been killed
+		setMouseCapture(FALSE);
+		return;
+	}
+
+	LLPickInfo pick = mGrabPick;
+	pick.mMousePt = LLCoordGL(x, y);
+	pick.getSurfaceInfo();
+
+	// compute elapsed time
+	F32 dt = mGrabTimer.getElapsedTimeAndResetF32();
+	U32 dt_milliseconds = (U32) (1000.f * dt);
+
+	// i'm not a big fan of the following code - it's been culled from the physical grab case.
+	// ideally these two would be nicely integrated - but the code in that method is a serious
+	// mess of spaghetti.  so here we go:
+
+	LLVector3 grab_pos_region(0,0,0);
+	
+	const BOOL SUPPORT_LLDETECTED_GRAB = TRUE;
+	if (SUPPORT_LLDETECTED_GRAB)
+	{
+		//--------------------------------------------------
+		// Toggle vertical dragging
+		//--------------------------------------------------
+		if (mVerticalDragging && !(mask == MASK_VERTICAL) && !gGrabBtnVertical)
+		{
+			mVerticalDragging = FALSE;
+		}
+	
+		else if (!mVerticalDragging && (mask == MASK_VERTICAL) )
+		{
+			mVerticalDragging = TRUE;
+		}
+	
+		S32 dx = x - mLastMouseX;
+		S32 dy = y - mLastMouseY;
+
+		if (dx != 0 || dy != 0)
+		{
+			mAccumDeltaX += dx;
+			mAccumDeltaY += dy;
+		
+			S32 dist_sq = mAccumDeltaX * mAccumDeltaX + mAccumDeltaY * mAccumDeltaY;
+			if (dist_sq > SLOP_DIST_SQ)
+			{
+				mOutsideSlop = TRUE;
+			}
+
+			// mouse has moved 
+			mHasMoved = TRUE;
+
+			//------------------------------------------------------
+			// Handle grabbing
+			//------------------------------------------------------
+
+			LLVector3d x_part;
+			x_part.setVec(LLViewerCamera::getInstance()->getLeftAxis());
+			x_part.mdV[VZ] = 0.0;
+			x_part.normVec();
+
+			LLVector3d y_part;
+			if( mVerticalDragging )
+			{
+				y_part.setVec(LLViewerCamera::getInstance()->getUpAxis());
+				// y_part.setVec(0.f, 0.f, 1.f);
+			}
+			else
+			{
+				// drag toward camera
+				y_part = x_part % LLVector3d::z_axis;
+				y_part.mdV[VZ] = 0.0;
+				y_part.normVec();
+			}
+
+			mGrabHiddenOffsetFromCamera = mGrabHiddenOffsetFromCamera 
+				+ (x_part * (-dx * GRAB_SENSITIVITY_X)) 
+				+ (y_part * ( dy * GRAB_SENSITIVITY_Y));
+
+		}
+		
+		// need to return offset from mGrabStartPoint
+		LLVector3d grab_point_global = gAgent.getCameraPositionGlobal() + mGrabHiddenOffsetFromCamera;
+		grab_pos_region = objectp->getRegion()->getPosRegionFromGlobal( grab_point_global );
+	}
+
+	LLMessageSystem *msg = gMessageSystem;
+	msg->newMessageFast(_PREHASH_ObjectGrabUpdate);
+	msg->nextBlockFast(_PREHASH_AgentData);
+	msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+	msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+	msg->nextBlockFast(_PREHASH_ObjectData);
+	msg->addUUIDFast(_PREHASH_ObjectID, objectp->getID() );
+	msg->addVector3Fast(_PREHASH_GrabOffsetInitial, mGrabOffsetFromCenterInitial );
+	msg->addVector3Fast(_PREHASH_GrabPosition, grab_pos_region );
+	msg->addU32Fast(_PREHASH_TimeSinceLast, dt_milliseconds );
+	msg->nextBlock("SurfaceInfo");
+	msg->addVector3("UVCoord", LLVector3(pick.mUVCoords));
+	msg->addVector3("STCoord", LLVector3(pick.mSTCoords));
+	msg->addS32Fast(_PREHASH_FaceIndex, pick.mObjectFace);
+	msg->addVector3("Position", pick.mIntersection);
+	msg->addVector3("Normal", pick.mNormal);
+	msg->addVector3("Binormal", pick.mBinormal);
+
+	msg->sendMessage( objectp->getRegion()->getHost() );
+
+	// update point-at / look-at
+	if (pick.mObjectFace != -1) // if the intersection was on the surface of the obejct
+	{
+		LLVector3 local_edit_point = pick.mIntersection;
+		local_edit_point -= objectp->getPositionAgent();
+		local_edit_point = local_edit_point * ~objectp->getRenderRotation();
+		gAgent.setPointAt(POINTAT_TARGET_GRAB, objectp, local_edit_point );
+		gAgent.setLookAt(LOOKAT_TARGET_SELECT, objectp, local_edit_point );
+	}
+	
+	
+	
+	gViewerWindow->setCursor(UI_CURSOR_HAND);  
+}
+ 
+
 // Not dragging.  Just showing affordances
 void LLToolGrab::handleHoverInactive(S32 x, S32 y, MASK mask)
 {
@@ -726,7 +873,7 @@ void LLToolGrab::handleHoverFailed(S32 x, S32 y, MASK mask)
 	}
 	else
 	{
-		S32 dist_sq = (x-mMouseDownX) * (x-mMouseDownX) + (y-mMouseDownY) * (y-mMouseDownY);
+		S32 dist_sq = (x-mGrabPick.mMousePt.mX) * (x-mGrabPick.mMousePt.mX) + (y-mGrabPick.mMousePt.mY) * (y-mGrabPick.mMousePt.mY);
 		if( mOutsideSlop || dist_sq > SLOP_DIST_SQ )
 		{
 			mOutsideSlop = TRUE;
@@ -791,23 +938,27 @@ void LLToolGrab::stopEditing()
 
 void LLToolGrab::onMouseCaptureLost()
 {
+	LLViewerObject* objectp = mGrabPick.getObject();
+	if (!objectp)
+	{
+		gViewerWindow->showCursor();
+		return;
+	}
 	// First, fix cursor placement
 	if( !gAgent.cameraMouselook() 
-		&& (GRAB_ACTIVE_CENTER == mMode || GRAB_NONPHYSICAL == mMode))
+		&& (GRAB_ACTIVE_CENTER == mMode))
 	{
-		llassert( mGrabObject ); 
-
-		if (mGrabObject->isHUDAttachment())
+		if (objectp->isHUDAttachment())
 		{
 			// ...move cursor "naturally", as if it had moved when hidden
-			S32 x = mMouseDownX + mAccumDeltaX;
-			S32 y = mMouseDownY + mAccumDeltaY;
+			S32 x = mGrabPick.mMousePt.mX + mAccumDeltaX;
+			S32 y = mGrabPick.mMousePt.mY + mAccumDeltaY;
 			LLUI::setCursorPositionScreen(x, y);
 		}
 		else if (mHasMoved)
 		{
 			// ...move cursor back to the center of the object
-			LLVector3 grab_point_agent = mGrabObject->getRenderPosition();
+			LLVector3 grab_point_agent = objectp->getRenderPosition();
 
 			LLCoordGL gl_point;
 			if (LLViewerCamera::getInstance()->projectPosAgentToScreen(grab_point_agent, gl_point))
@@ -818,19 +969,21 @@ void LLToolGrab::onMouseCaptureLost()
 		else
 		{
 			// ...move cursor back to click position
-			LLUI::setCursorPositionScreen(mMouseDownX, mMouseDownY);
+			LLUI::setCursorPositionScreen(mGrabPick.mMousePt.mX, mGrabPick.mMousePt.mY);
 		}
 
 		gViewerWindow->showCursor();
 	}
 
 	stopGrab();
+	if (mSpinGrabbing)
 	stopSpin();
+	
 	mMode = GRAB_INACTIVE;
 
 	mHideBuildHighlight = FALSE;
 
-	mGrabObject = NULL;
+	mGrabPick.mObjectID.setNull();
 
 	LLSelectMgr::getInstance()->updateSelectionCenter();
 	gAgent.setPointAt(POINTAT_TARGET_CLEAR);
@@ -842,6 +995,24 @@ void LLToolGrab::onMouseCaptureLost()
 
 void LLToolGrab::stopGrab()
 {
+	LLViewerObject* objectp = mGrabPick.getObject();
+	if (!objectp)
+	{
+		return;
+	}
+
+	LLPickInfo pick = mGrabPick;
+
+	if (mMode == GRAB_NONPHYSICAL)
+	{
+		// for non-physical (touch) grabs,
+		// gather surface info for this degrab (mouse-up)
+		S32 x = gViewerWindow->getCurrentMouseX();
+		S32 y = gViewerWindow->getCurrentMouseY();
+		pick.mMousePt = LLCoordGL(x, y);
+		pick.getSurfaceInfo();
+	}
+
 	// Next, send messages to simulator
 	LLMessageSystem *msg = gMessageSystem;
 	switch(mMode)
@@ -854,11 +1025,18 @@ void LLToolGrab::stopGrab()
 		msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
 		msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
 		msg->nextBlockFast(_PREHASH_ObjectData);
-		msg->addU32Fast(_PREHASH_LocalID, mGrabObject->mLocalID);
-		msg->sendMessage(mGrabObject->getRegion()->getHost());
+		msg->addU32Fast(_PREHASH_LocalID, objectp->mLocalID);
+		msg->nextBlock("SurfaceInfo");
+		msg->addVector3("UVCoord", LLVector3(mGrabPick.mUVCoords));
+		msg->addVector3("STCoord", LLVector3(mGrabPick.mSTCoords));
+		msg->addS32Fast(_PREHASH_FaceIndex, pick.mObjectFace);
+		msg->addVector3("Position", pick.mIntersection);
+		msg->addVector3("Normal", pick.mNormal);
+		msg->addVector3("Binormal", pick.mBinormal);
+
+		msg->sendMessage(objectp->getRegion()->getHost());
 
 		mVerticalDragging = FALSE;
-		mGrabOffset.clearVec();
 		break;
 
 	case GRAB_NOOBJECT:
@@ -880,14 +1058,12 @@ void LLToolGrab::render()
 
 BOOL LLToolGrab::isEditing()
 {
-	// Can't just compare to null directly due to "smart" pointer.
-	LLViewerObject *obj = mGrabObject;
-	return (obj != NULL);
+	return (mGrabPick.getObject().notNull());
 }
 
 LLViewerObject* LLToolGrab::getEditingObject()
 {
-	return mGrabObject;
+	return mGrabPick.getObject();
 }
 
 
diff --git a/indra/newview/lltoolgrab.h b/indra/newview/lltoolgrab.h
index d744c495a58d40ca0e44af6fd89be3294fc14d31..696ce0742a24a9d8c5f95f1efc80bb7d2122b58b 100644
--- a/indra/newview/lltoolgrab.h
+++ b/indra/newview/lltoolgrab.h
@@ -37,10 +37,12 @@
 #include "llquaternion.h"
 #include "llmemory.h"
 #include "lluuid.h"
+#include "llviewerwindow.h" // for LLPickInfo
 
 class LLView;
 class LLTextBox;
 class LLViewerObject;
+class LLPickInfo;
 
 class LLToolGrab : public LLTool, public LLSingleton<LLToolGrab>
 {
@@ -69,15 +71,15 @@ class LLToolGrab : public LLTool, public LLSingleton<LLToolGrab>
 	LLVector3		getGrabOffset(S32 x, S32 y);		// HACK
 
 	// Capture the mouse and start grabbing.
-	BOOL			handleObjectHit(LLViewerObject *objectp, S32 x, S32 y, MASK mask);
+	BOOL			handleObjectHit(const LLPickInfo& info);
 
 	// Certain grabs should not highlight the "Build" toolbar button
 	BOOL getHideBuildHighlight() { return mHideBuildHighlight; }
 
-	static void		pickCallback(S32 x, S32 y, MASK mask);
+	static void		pickCallback(const LLPickInfo& pick_info);
 private:
 	LLVector3d		getGrabPointGlobal();
-	void			startGrab(S32 x, S32 y);
+	void			startGrab();
 	void			stopGrab();
 
 	void			startSpin();
@@ -85,6 +87,7 @@ class LLToolGrab : public LLTool, public LLSingleton<LLToolGrab>
 
 	void			handleHoverSpin(S32 x, S32 y, MASK mask);
 	void			handleHoverActive(S32 x, S32 y, MASK mask);
+	void			handleHoverNonPhysical(S32 x, S32 y, MASK mask);
 	void			handleHoverInactive(S32 x, S32 y, MASK mask);
 	void			handleHoverFailed(S32 x, S32 y, MASK mask);
 
@@ -96,29 +99,26 @@ class LLToolGrab : public LLTool, public LLSingleton<LLToolGrab>
 	BOOL			mVerticalDragging;
 
 	BOOL			mHitLand;
-	LLUUID			mHitObjectID;				// if hit something, its ID
 
-	LLPointer<LLViewerObject>	mGrabObject;					// the object currently being grabbed
 	LLTimer			mGrabTimer;						// send simulator time between hover movements
 
 	LLVector3		mGrabOffsetFromCenterInitial;	// meters from CG of object
-	LLVector3		mGrabOffset;					// how far cursor currently is from grab start point, meters
 	LLVector3d		mGrabHiddenOffsetFromCamera;	// in cursor hidden drag, how far is grab offset from camera
 
 	LLVector3d		mDragStartPointGlobal;				// projected into world
 	LLVector3d		mDragStartFromCamera;			// drag start relative to camera
 
+	LLPickInfo		mGrabPick;
+
 	S32				mLastMouseX;
 	S32				mLastMouseY;
-	S32				mMouseDownX;
-	S32				mMouseDownY;
-	MASK			mMouseMask;
 	S32				mAccumDeltaX;	// since cursor hidden, how far have you moved?
 	S32				mAccumDeltaY;
 	BOOL			mHasMoved;		// has mouse moved off center at all?
 	BOOL			mOutsideSlop;	// has mouse moved outside center 5 pixels?
 	BOOL			mDeselectedThisClick;
 
+
 	BOOL			mSpinGrabbing;
 	LLQuaternion	mSpinRotation;
 
@@ -131,3 +131,4 @@ extern LLTool* gGrabTransientTool;
 
 #endif  // LL_TOOLGRAB_H
 
+
diff --git a/indra/newview/lltoolindividual.cpp b/indra/newview/lltoolindividual.cpp
index 3fa96cd84a7c497ec05b224e61012ae89ab0df21..0e0524daa64f8a8d8fb9d5d7d654bfdd99aeb272 100644
--- a/indra/newview/lltoolindividual.cpp
+++ b/indra/newview/lltoolindividual.cpp
@@ -72,13 +72,13 @@ LLToolIndividual::~LLToolIndividual()
 
 BOOL LLToolIndividual::handleMouseDown(S32 x, S32 y, MASK mask)
 {
-	gViewerWindow->hitObjectOrLandGlobalAsync(x, y, mask, pickCallback);
+	gViewerWindow->pickAsync(x, y, mask, pickCallback);
 	return TRUE;
 }
 
-void LLToolIndividual::pickCallback(S32 x, S32 y, MASK mask)
+void LLToolIndividual::pickCallback(const LLPickInfo& pick_info)
 {
-	LLViewerObject* obj = gViewerWindow->lastObjectHit();
+	LLViewerObject* obj = pick_info.getObject();
 	LLSelectMgr::getInstance()->deselectAll();
 	if(obj)
 	{
diff --git a/indra/newview/lltoolindividual.h b/indra/newview/lltoolindividual.h
index 48a2365363d2366b93b8dce3c34d12b6a88efa9e..913e4ff2b8e0d1b334bfdb4b145f7f5d5c131a83 100644
--- a/indra/newview/lltoolindividual.h
+++ b/indra/newview/lltoolindividual.h
@@ -34,6 +34,8 @@
 
 #include "lltool.h"
 
+class LLPickInfo;
+
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // Class lltoolindividual
 //
@@ -52,7 +54,7 @@ class LLToolIndividual : public LLTool, public LLSingleton<LLToolIndividual>
 	//virtual void handleDeselect();
 	//virtual void render();
 
-	static void pickCallback(S32 x, S32 y, MASK mask);
+	static void pickCallback(const LLPickInfo& pick_info);
 
 protected:
 
diff --git a/indra/newview/lltoolobjpicker.cpp b/indra/newview/lltoolobjpicker.cpp
index 67e0db962f8c0529974ade06d1c0466964f6638d..ee9c24f2699fd9a5e47e1d13e80a834591faaee3 100644
--- a/indra/newview/lltoolobjpicker.cpp
+++ b/indra/newview/lltoolobjpicker.cpp
@@ -69,7 +69,7 @@ BOOL LLToolObjPicker::handleMouseDown(S32 x, S32 y, MASK mask)
 	if (! handled)
 	{
 		// didn't click in any UI object, so must have clicked in the world
-		gViewerWindow->hitObjectOrLandGlobalAsync(x, y, mask, pickCallback);
+		gViewerWindow->pickAsync(x, y, mask, pickCallback);
 		handled = TRUE;
 	}
 	else
@@ -90,16 +90,10 @@ BOOL LLToolObjPicker::handleMouseDown(S32 x, S32 y, MASK mask)
 	return handled;
 }
 
-void LLToolObjPicker::pickCallback(S32 x, S32 y, MASK mask)
+void LLToolObjPicker::pickCallback(const LLPickInfo& pick_info)
 {
-	// You must hit the body for this tool to think you hit the object.
-	LLViewerObject*	objectp = NULL;
-	objectp = gObjectList.findObject( gLastHitObjectID );
-	if (objectp)
-	{
-		LLToolObjPicker::getInstance()->mHitObjectID = objectp->mID;
-		LLToolObjPicker::getInstance()->mPicked = TRUE;
-	}
+	LLToolObjPicker::getInstance()->mHitObjectID = pick_info.mObjectID;
+	LLToolObjPicker::getInstance()->mPicked = pick_info.mObjectID.notNull();
 }
 
 
@@ -181,3 +175,4 @@ void LLToolObjPicker::handleDeselect()
 }
 
 
+
diff --git a/indra/newview/lltoolobjpicker.h b/indra/newview/lltoolobjpicker.h
index 794d035a83f25859e835fecf82139d1c7bc2d168..8d0c87c8ae1972e6ed6bfc6d8a900a707e8b7ea2 100644
--- a/indra/newview/lltoolobjpicker.h
+++ b/indra/newview/lltoolobjpicker.h
@@ -36,6 +36,8 @@
 #include "v3math.h"
 #include "lluuid.h"
 
+class LLPickInfo;
+
 class LLToolObjPicker : public LLTool, public LLSingleton<LLToolObjPicker>
 {
 public:
@@ -54,7 +56,7 @@ class LLToolObjPicker : public LLTool, public LLSingleton<LLToolObjPicker>
 
 	LLUUID				getObjectID() const { return mHitObjectID; }
 
-	static void			pickCallback(S32 x, S32 y, MASK mask);
+	static void			pickCallback(const LLPickInfo& pick_info);
 
 protected:
 	BOOL				mPicked;
diff --git a/indra/newview/lltoolpie.cpp b/indra/newview/lltoolpie.cpp
index b6bcf9544df98a8f3d9ca4ea4ef0ca748a9fc383..b444cb8ce620df400b31cced14153a04d6d8fb87 100644
--- a/indra/newview/lltoolpie.cpp
+++ b/indra/newview/lltoolpie.cpp
@@ -84,50 +84,48 @@ LLToolPie::LLToolPie()
 :	LLTool(std::string("Select")),
 	mPieMouseButtonDown( FALSE ),
 	mGrabMouseButtonDown( FALSE ),
-	mHitLand( FALSE ),
-	mHitObjectID(),
 	mMouseOutsideSlop( FALSE )
 { }
 
 
 BOOL LLToolPie::handleMouseDown(S32 x, S32 y, MASK mask)
 {
-	gPickFaces = TRUE;
 	//left mouse down always picks transparent
-	gViewerWindow->hitObjectOrLandGlobalAsync(x, y, mask, leftMouseCallback, 
-											  TRUE, TRUE);
+	gViewerWindow->pickAsync(x, y, mask, leftMouseCallback, TRUE, TRUE);
 	mGrabMouseButtonDown = TRUE;
 	return TRUE;
 }
 
 // static
-void LLToolPie::leftMouseCallback(S32 x, S32 y, MASK mask)
+void LLToolPie::leftMouseCallback(const LLPickInfo& pick_info)
 {
-	LLToolPie::getInstance()->pickAndShowMenu(x, y, mask, FALSE);
+	LLToolPie::getInstance()->mPick = pick_info;
+	LLToolPie::getInstance()->pickAndShowMenu(FALSE);
 }
 
 BOOL LLToolPie::handleRightMouseDown(S32 x, S32 y, MASK mask)
 {
-	// Pick faces in case they select "Copy Texture" and need that info.
-	gPickFaces = TRUE;
 	// don't pick transparent so users can't "pay" transparent objects
-	gViewerWindow->hitObjectOrLandGlobalAsync(x, y, mask, rightMouseCallback,
-											  FALSE, TRUE);
+	gViewerWindow->pickAsync(x, y, mask, rightMouseCallback, FALSE);
 	mPieMouseButtonDown = TRUE; 
 	// don't steal focus from UI
 	return FALSE;
 }
 
 // static
-void LLToolPie::rightMouseCallback(S32 x, S32 y, MASK mask)
+void LLToolPie::rightMouseCallback(const LLPickInfo& pick_info)
 {
-	LLToolPie::getInstance()->pickAndShowMenu(x, y, mask, TRUE);
+	LLToolPie::getInstance()->mPick = pick_info;
+	LLToolPie::getInstance()->pickAndShowMenu(TRUE);
 }
 
 // True if you selected an object.
-BOOL LLToolPie::pickAndShowMenu(S32 x, S32 y, MASK mask, BOOL always_show)
+BOOL LLToolPie::pickAndShowMenu(BOOL always_show)
 {
-	if (!always_show && gLastHitParcelWall)
+	S32 x = mPick.mMousePt.mX;
+	S32 y = mPick.mMousePt.mY;
+	MASK mask = mPick.mKeyMask;
+	if (!always_show && mPick.mPickType == LLPickInfo::PICK_PARCEL_WALL)
 	{
 		LLParcel* parcel = LLViewerParcelMgr::getInstance()->getCollisionParcel();
 		if (parcel)
@@ -151,25 +149,18 @@ BOOL LLToolPie::pickAndShowMenu(S32 x, S32 y, MASK mask, BOOL always_show)
 	}
 
 	// didn't click in any UI object, so must have clicked in the world
-	LLViewerObject *object = gViewerWindow->lastObjectHit();
+	LLViewerObject *object = mPick.getObject();
 	LLViewerObject *parent = NULL;
 
-	mHitLand = !object && !gLastHitPosGlobal.isExactlyZero();
-	if (!mHitLand)
+	if (mPick.mPickType != LLPickInfo::PICK_LAND)
 	{
 		LLViewerParcelMgr::getInstance()->deselectLand();
 	}
 	
 	if (object)
 	{
-		mHitObjectID = object->mID;
-
 		parent = object->getRootEdit();
 	}
-	else
-	{
-		mHitObjectID.setNull();
-	}
 
 	BOOL touchable = (object && object->flagHandleTouch()) 
 					 || (parent && parent->flagHandleTouch());
@@ -206,19 +197,19 @@ BOOL LLToolPie::pickAndShowMenu(S32 x, S32 y, MASK mask, BOOL always_show)
 			{
 				// pay event goes to object actually clicked on
 				sClickActionObject = object;
-				sLeftClickSelection = LLToolSelect::handleObjectSelection(object, MASK_NONE, FALSE, TRUE);
+				sLeftClickSelection = LLToolSelect::handleObjectSelection(mPick, FALSE, TRUE);
 				return TRUE;
 			}
 			break;
 		case CLICK_ACTION_BUY:
 			sClickActionObject = parent;
-			sLeftClickSelection = LLToolSelect::handleObjectSelection(parent, MASK_NONE, FALSE, TRUE);
+			sLeftClickSelection = LLToolSelect::handleObjectSelection(mPick, FALSE, TRUE, TRUE);
 			return TRUE;
 		case CLICK_ACTION_OPEN:
 			if (parent && parent->allowOpen())
 			{
 				sClickActionObject = parent;
-				sLeftClickSelection = LLToolSelect::handleObjectSelection(parent, MASK_NONE, FALSE, TRUE);
+				sLeftClickSelection = LLToolSelect::handleObjectSelection(mPick, FALSE, TRUE, TRUE);
 			}
 			return TRUE;
 		case CLICK_ACTION_PLAY:
@@ -239,12 +230,13 @@ BOOL LLToolPie::pickAndShowMenu(S32 x, S32 y, MASK mask, BOOL always_show)
 	{
 		gGrabTransientTool = this;
 		LLToolMgr::getInstance()->getCurrentToolset()->selectTool( LLToolGrab::getInstance() );
-		return LLToolGrab::getInstance()->handleObjectHit( object, x, y, mask);
+		return LLToolGrab::getInstance()->handleObjectHit( mPick );
 	}
 	
-	if (!object && gLastHitHUDIcon && gLastHitHUDIcon->getSourceObject())
+	LLHUDIcon* last_hit_hud_icon = mPick.mHUDIcon;
+	if (!object && last_hit_hud_icon && last_hit_hud_icon->getSourceObject())
 	{
-		LLFloaterScriptDebug::show(gLastHitHUDIcon->getSourceObject()->getID());
+		LLFloaterScriptDebug::show(last_hit_hud_icon->getSourceObject()->getID());
 	}
 
 	// If left-click never selects or spawns a menu
@@ -273,7 +265,7 @@ BOOL LLToolPie::pickAndShowMenu(S32 x, S32 y, MASK mask, BOOL always_show)
 			LLToolMgr::getInstance()->setTransientTool(LLToolCamera::getInstance());
 			gViewerWindow->hideCursor();
 			LLToolCamera::getInstance()->setMouseCapture(TRUE);
-			LLToolCamera::getInstance()->pickCallback(gViewerWindow->getCurrentMouseX(), gViewerWindow->getCurrentMouseY(), mask);
+			LLToolCamera::getInstance()->pickCallback(mPick);
 			gAgent.setFocusOnAvatar(TRUE, TRUE);
 
 			return TRUE;
@@ -292,22 +284,22 @@ BOOL LLToolPie::pickAndShowMenu(S32 x, S32 y, MASK mask, BOOL always_show)
 	}
 
 	// Can't ignore children here.
-	LLToolSelect::handleObjectSelection(object, mask, FALSE, TRUE);
+	LLToolSelect::handleObjectSelection(mPick, FALSE, TRUE);
 
 	// Spawn pie menu
-	if (mHitLand)
+	if (mPick.mPickType == LLPickInfo::PICK_LAND)
 	{
-		LLParcelSelectionHandle selection = LLViewerParcelMgr::getInstance()->selectParcelAt( gLastHitPosGlobal );
+		LLParcelSelectionHandle selection = LLViewerParcelMgr::getInstance()->selectParcelAt( mPick.mPosGlobal );
 		gMenuHolder->setParcelSelection(selection);
 		gPieLand->show(x, y, mPieMouseButtonDown);
 
 		// VEFFECT: ShowPie
 		LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_SPHERE, TRUE);
-		effectp->setPositionGlobal(gLastHitPosGlobal);
+		effectp->setPositionGlobal(mPick.mPosGlobal);
 		effectp->setColor(LLColor4U(gAgent.getEffectColor()));
 		effectp->setDuration(0.25f);
 	}
-	else if (mHitObjectID == gAgent.getID() )
+	else if (mPick.mObjectID == gAgent.getID() )
 	{
 		if(!gPieSelf) 
 		{
@@ -377,7 +369,7 @@ BOOL LLToolPie::pickAndShowMenu(S32 x, S32 y, MASK mask, BOOL always_show)
 			// Don't show when you click on someone else, it freaks them
 			// out.
 			LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_SPHERE, TRUE);
-			effectp->setPositionGlobal(gLastHitPosGlobal);
+			effectp->setPositionGlobal(mPick.mPosGlobal);
 			effectp->setColor(LLColor4U(gAgent.getEffectColor()));
 			effectp->setDuration(0.25f);
 		}
@@ -544,7 +536,7 @@ BOOL LLToolPie::handleHover(S32 x, S32 y, MASK mask)
 	LLViewerObject *parent = NULL;
 	if (gHoverView)
 	{
-		object = gHoverView->getLastHoverObject();
+		object = gViewerWindow->getHoverPick().getObject();
 	}
 
 	if (object)
@@ -581,7 +573,7 @@ BOOL LLToolPie::handleHover(S32 x, S32 y, MASK mask)
 
 BOOL LLToolPie::handleMouseUp(S32 x, S32 y, MASK mask)
 {
-	LLViewerObject* obj = gViewerWindow->lastObjectHit();
+	LLViewerObject* obj = mPick.getObject();
 	U8 click_action = final_click_action(obj);
 	if (click_action != CLICK_ACTION_NONE)
 	{
@@ -626,18 +618,18 @@ BOOL LLToolPie::handleDoubleClick(S32 x, S32 y, MASK mask)
 
 	if (gSavedSettings.getBOOL("DoubleClickAutoPilot"))
 	{
-		if (gLastHitLand
-			&& !gLastHitPosGlobal.isExactlyZero())
+		if (mPick.mPickType == LLPickInfo::PICK_LAND
+			&& !mPick.mPosGlobal.isExactlyZero())
 		{
 			handle_go_to();
 			return TRUE;
 		}
-		else if (gLastHitObjectID.notNull()
-				 && !gLastHitPosGlobal.isExactlyZero())
+		else if (mPick.mObjectID.notNull()
+				 && !mPick.mPosGlobal.isExactlyZero())
 		{
 			// Hit an object
 			// HACK: Call the last hit position the point we hit on the object
-			gLastHitPosGlobal += gLastHitObjectOffset;
+			//gLastHitPosGlobal += gLastHitObjectOffset;
 			handle_go_to();
 			return TRUE;
 		}
@@ -649,7 +641,7 @@ BOOL LLToolPie::handleDoubleClick(S32 x, S32 y, MASK mask)
 	objects gets you into trouble.
 
 	// If double-click on object or land, go there.
-	LLViewerObject *object = gViewerWindow->lastObjectHit();
+	LLViewerObject *object = gViewerWindow->getLastPick().getObject();
 	if (object)
 	{
 		if (object->isAvatar())
@@ -756,10 +748,11 @@ static void handle_click_action_open_media(LLPointer<LLViewerObject> objectp)
 	if (objectp.isNull()) return;
 
 	// did we hit a valid face on the object?
-	if( gLastHitObjectFace < 0 || gLastHitObjectFace >= objectp->getNumTEs() ) return;
+	S32 face = LLToolPie::getInstance()->getPick().mObjectFace;
+	if( face < 0 || face >= objectp->getNumTEs() ) return;
 		
 	// is media playing on this face?
-	if (!LLViewerMedia::isActiveMediaTexture(objectp->getTE(gLastHitObjectFace)->getID()))
+	if (!LLViewerMedia::isActiveMediaTexture(objectp->getTE(face)->getID()))
 	{
 		handle_click_action_play();
 		return;
diff --git a/indra/newview/lltoolpie.h b/indra/newview/lltoolpie.h
index 4963940840a10b11d3f68ba07520b1982debb186..a8103f23a04f09c2863b8e6fcbe69ffa2c2f1a8d 100644
--- a/indra/newview/lltoolpie.h
+++ b/indra/newview/lltoolpie.h
@@ -34,6 +34,7 @@
 
 #include "lltool.h"
 #include "lluuid.h"
+#include "llviewerwindow.h" // for LLPickInfo
 
 class LLViewerObject;
 class LLObjectSelection;
@@ -58,23 +59,25 @@ class LLToolPie : public LLTool, public LLSingleton<LLToolPie>
 	virtual void		handleDeselect();
 	virtual LLTool*		getOverrideTool(MASK mask);
 
-	static void			leftMouseCallback(S32 x, S32 y, MASK mask);
-	static void			rightMouseCallback(S32 x, S32 y, MASK mask);
+	LLPickInfo&			getPick() { return mPick; }
+
+	static void			leftMouseCallback(const LLPickInfo& pick_info);
+	static void			rightMouseCallback(const LLPickInfo& pick_info);
 
 	static void			selectionPropertiesReceived();
 
+
 protected:
 	BOOL outsideSlop(S32 x, S32 y, S32 start_x, S32 start_y);
-	BOOL pickAndShowMenu(S32 x, S32 y, MASK mask, BOOL edit_menu);
+	BOOL pickAndShowMenu(BOOL edit_menu);
 	BOOL useClickAction(BOOL always_show, MASK mask, LLViewerObject* object,
 						LLViewerObject* parent);
 
 protected:
 	BOOL				mPieMouseButtonDown;
 	BOOL				mGrabMouseButtonDown;
-	BOOL				mHitLand;
-	LLUUID				mHitObjectID;
 	BOOL				mMouseOutsideSlop;				// for this drag, has mouse moved outside slop region
+	LLPickInfo			mPick;
 	static LLPointer<LLViewerObject> sClickActionObject;
 	static U8				sClickAction;
 	static LLSafeHandle<LLObjectSelection> sLeftClickSelection;
diff --git a/indra/newview/lltoolpipette.cpp b/indra/newview/lltoolpipette.cpp
index a565e4361b5c01d2c1b63e2a6b7caea39a2afc81..9456cd4472b76494386c37f8550bf10ee0a40fb0 100644
--- a/indra/newview/lltoolpipette.cpp
+++ b/indra/newview/lltoolpipette.cpp
@@ -67,9 +67,8 @@ BOOL LLToolPipette::handleMouseDown(S32 x, S32 y, MASK mask)
 {
 	mSuccess = TRUE;
 	mTooltipMsg.clear();
-	gPickFaces = TRUE;
 	setMouseCapture(TRUE);
-	gViewerWindow->hitObjectOrLandGlobalAsync(x, y, mask, pickCallback);
+	gViewerWindow->pickAsync(x, y, mask, pickCallback);
 	return TRUE;
 }
 
@@ -88,8 +87,7 @@ BOOL LLToolPipette::handleHover(S32 x, S32 y, MASK mask)
 	gViewerWindow->setCursor(mSuccess ? UI_CURSOR_PIPETTE : UI_CURSOR_NO);
 	if (hasMouseCapture()) // mouse button is down
 	{
-		gPickFaces = TRUE;
-		gViewerWindow->hitObjectOrLandGlobalAsync(x, y, mask, pickCallback);
+		gViewerWindow->pickAsync(x, y, mask, pickCallback);
 		return TRUE;
 	}
 	return FALSE;
@@ -107,19 +105,19 @@ BOOL LLToolPipette::handleToolTip(S32 x, S32 y, std::string& msg, LLRect *sticky
 	return TRUE;
 }
 
-void LLToolPipette::pickCallback(S32 x, S32 y, MASK mask)
+void LLToolPipette::pickCallback(const LLPickInfo& pick_info)
 {
-	LLViewerObject* hit_obj	= gViewerWindow->lastObjectHit();
+	LLViewerObject* hit_obj	= pick_info.getObject();
 	LLSelectMgr::getInstance()->unhighlightAll();
 
 	// if we clicked on a face of a valid prim, save off texture entry data
 	if (hit_obj && 
 		hit_obj->getPCode() == LL_PCODE_VOLUME &&
-		gLastHitObjectFace != -1)
+		pick_info.mObjectFace != -1)
 	{
 		//TODO: this should highlight the selected face only
 		LLSelectMgr::getInstance()->highlightObjectOnly(hit_obj);
-		LLToolPipette::getInstance()->mTextureEntry = *hit_obj->getTE(gLastHitObjectFace);
+		LLToolPipette::getInstance()->mTextureEntry = *hit_obj->getTE(pick_info.mObjectFace);
 		if (LLToolPipette::getInstance()->mSelectCallback)
 		{
 			LLToolPipette::getInstance()->mSelectCallback(LLToolPipette::getInstance()->mTextureEntry, LLToolPipette::getInstance()->mUserData);
diff --git a/indra/newview/lltoolpipette.h b/indra/newview/lltoolpipette.h
index b88a6eebc87c775de10922be09766a46a212aa8c..5d5b27f9648e15f5a05bc6347c53c2353275aec4 100644
--- a/indra/newview/lltoolpipette.h
+++ b/indra/newview/lltoolpipette.h
@@ -41,6 +41,7 @@
 #include "lltextureentry.h"
 
 class LLViewerObject;
+class LLPickInfo;
 
 class LLToolPipette
 :	public LLTool, public LLSingleton<LLToolPipette>
@@ -58,7 +59,7 @@ class LLToolPipette
 	void setSelectCallback(select_callback callback, void* user_data);
 	void setResult(BOOL success, const std::string& msg);
 
-	static void pickCallback(S32 x, S32 y, MASK mask);
+	static void pickCallback(const LLPickInfo& pick_info);
 
 protected:
 	LLTextureEntry	mTextureEntry;
diff --git a/indra/newview/lltoolplacer.cpp b/indra/newview/lltoolplacer.cpp
index 7d4d0397cca114852e2d9a9d912ccb7dd7e5026c..8806d1465aa45f4a89e9147407e8d61e7013a554 100644
--- a/indra/newview/lltoolplacer.cpp
+++ b/indra/newview/lltoolplacer.cpp
@@ -81,14 +81,22 @@ BOOL LLToolPlacer::raycastForNewObjPos( S32 x, S32 y, LLViewerObject** hit_obj,
 
 	// Viewer-side pick to find the right sim to create the object on.  
 	// First find the surface the object will be created on.
-	gViewerWindow->hitObjectOrLandGlobalImmediate(x, y, NULL, FALSE);
+	LLPickInfo pick = gViewerWindow->pickImmediate(x, y, FALSE);
 	
 	// Note: use the frontmost non-flora version because (a) plants usually have lots of alpha and (b) pants' Havok
 	// representations (if any) are NOT the same as their viewer representation.
-	*hit_obj = gObjectList.findObject( gLastHitNonFloraObjectID );
-	*hit_face = gLastHitNonFloraObjectFace;
-	*b_hit_land = !(*hit_obj) && !gLastHitNonFloraPosGlobal.isExactlyZero();
-	LLVector3d land_pos_global = gLastHitNonFloraPosGlobal;
+	if (pick.mPickType == LLPickInfo::PICK_FLORA)
+	{
+		*hit_obj = NULL;
+		*hit_face = -1;
+	}
+	else
+	{
+		*hit_obj = pick.getObject();
+		*hit_face = pick.mObjectFace;
+	}
+	*b_hit_land = !(*hit_obj) && !pick.mPosGlobal.isExactlyZero();
+	LLVector3d land_pos_global = pick.mPosGlobal;
 
 	// Make sure there's a surface to place the new object on.
 	BOOL bypass_sim_raycast = FALSE;
diff --git a/indra/newview/lltoolselect.cpp b/indra/newview/lltoolselect.cpp
index 8f84013a266fecbf3d2070a1ec6cc31a7aaf7af9..e74cd5892420c3c98fba57ef1dbe80d3fd01f1e8 100644
--- a/indra/newview/lltoolselect.cpp
+++ b/indra/newview/lltoolselect.cpp
@@ -64,39 +64,24 @@ LLToolSelect::LLToolSelect( LLToolComposite* composite )
 // True if you selected an object.
 BOOL LLToolSelect::handleMouseDown(S32 x, S32 y, MASK mask)
 {
-	BOOL handled = FALSE;
-
-	// didn't click in any UI object, so must have clicked in the world
-	LLViewerObject*	object = NULL;
-
-	// You must hit the body for this tool to think you hit the object.
-	object = gObjectList.findObject( gLastHitObjectID );
-
-	if (object)
-	{
-		mSelectObjectID = object->getID();
-		handled = TRUE;
-	}
-	else
-	{
-		mSelectObjectID.setNull();
-	}
+	// do immediate pick query
+	mPick = gViewerWindow->pickImmediate(x, y, TRUE);
 
 	// Pass mousedown to agent
 	LLTool::handleMouseDown(x, y, mask);
 
-	return handled;
+	return mPick.getObject().notNull();
 }
 
-BOOL LLToolSelect::handleDoubleClick(S32 x, S32 y, MASK mask)
-{
-	//RN: double click to toggle individual/linked picking???
-	return LLTool::handleDoubleClick(x, y, mask);
-}
 
 // static
-LLSafeHandle<LLObjectSelection> LLToolSelect::handleObjectSelection(LLViewerObject *object, MASK mask, BOOL ignore_group, BOOL temp_select)
+LLObjectSelectionHandle LLToolSelect::handleObjectSelection(const LLPickInfo& pick, BOOL ignore_group, BOOL temp_select, BOOL select_root)
 {
+	LLViewerObject* object = pick.getObject();
+	if (select_root)
+	{
+		object = object->getRootEdit();
+	}
 	BOOL select_owned = gSavedSettings.getBOOL("SelectOwnedOnly");
 	BOOL select_movable = gSavedSettings.getBOOL("SelectMovableOnly");
 	
@@ -108,14 +93,16 @@ LLSafeHandle<LLObjectSelection> LLToolSelect::handleObjectSelection(LLViewerObje
 		LLSelectMgr::getInstance()->setForceSelection(TRUE);
 	}
 
-	BOOL extend_select = (mask == MASK_SHIFT) || (mask == MASK_CONTROL);
+	BOOL extend_select = (pick.mKeyMask == MASK_SHIFT) || (pick.mKeyMask == MASK_CONTROL);
 
 	// If no object, check for icon, then just deselect
 	if (!object)
 	{
-		if (gLastHitHUDIcon && gLastHitHUDIcon->getSourceObject())
+		LLHUDIcon* last_hit_hud_icon = pick.mHUDIcon;
+
+		if (last_hit_hud_icon && last_hit_hud_icon->getSourceObject())
 		{
-			LLFloaterScriptDebug::show(gLastHitHUDIcon->getSourceObject()->getID());
+			LLFloaterScriptDebug::show(last_hit_hud_icon->getSourceObject()->getID());
 		}
 		else if (!extend_select)
 		{
@@ -240,8 +227,7 @@ BOOL LLToolSelect::handleMouseUp(S32 x, S32 y, MASK mask)
 {
 	mIgnoreGroup = gSavedSettings.getBOOL("EditLinkedParts");
 
-	LLViewerObject* object = gObjectList.findObject(mSelectObjectID);
-	LLToolSelect::handleObjectSelection(object, mask, mIgnoreGroup, FALSE);
+	handleObjectSelection(mPick, mIgnoreGroup, FALSE);
 
 	return LLTool::handleMouseUp(x, y, mask);
 }
@@ -275,3 +261,4 @@ void LLToolSelect::onMouseCaptureLost()
 
 
 
+
diff --git a/indra/newview/lltoolselect.h b/indra/newview/lltoolselect.h
index c3d10424b1a3ede3a71aff53747d91deafec3d9b..f6359863a87a91eb66c138ad8b86e3493a4b9ccf 100644
--- a/indra/newview/lltoolselect.h
+++ b/indra/newview/lltoolselect.h
@@ -35,6 +35,7 @@
 #include "lltool.h"
 #include "v3math.h"
 #include "lluuid.h"
+#include "llviewerwindow.h" // for LLPickInfo
 
 class LLObjectSelection;
 
@@ -45,11 +46,10 @@ class LLToolSelect : public LLTool, public LLSingleton<LLToolSelect>
 
 	virtual BOOL		handleMouseDown(S32 x, S32 y, MASK mask);
 	virtual BOOL		handleMouseUp(S32 x, S32 y, MASK mask);
-	virtual BOOL		handleDoubleClick(S32 x, S32 y, MASK mask);
 
 	virtual void		stopEditing();
 
-	static LLSafeHandle<LLObjectSelection>	handleObjectSelection(LLViewerObject *object, MASK mask, BOOL ignore_group, BOOL temp_select);
+	static LLSafeHandle<LLObjectSelection>	handleObjectSelection(const LLPickInfo& pick, BOOL ignore_group, BOOL temp_select, BOOL select_root = FALSE);
 
 	virtual void		onMouseCaptureLost();
 	virtual void		handleDeselect();
@@ -57,6 +57,7 @@ class LLToolSelect : public LLTool, public LLSingleton<LLToolSelect>
 protected:
 	BOOL				mIgnoreGroup;
 	LLUUID				mSelectObjectID;
+	LLPickInfo			mPick;
 };
 
 
diff --git a/indra/newview/lltoolselectrect.cpp b/indra/newview/lltoolselectrect.cpp
index 67d9e2da657fdf4016f152fbecf74a131ba72c33..0d403bbb287f8cf060aa0e986619bf835446baec 100644
--- a/indra/newview/lltoolselectrect.cpp
+++ b/indra/newview/lltoolselectrect.cpp
@@ -78,18 +78,26 @@ void dialog_refresh_all(void);
 
 BOOL LLToolSelectRect::handleMouseDown(S32 x, S32 y, MASK mask)
 {
+	handlePick(gViewerWindow->pickImmediate(x, y, TRUE));
+
+	LLTool::handleMouseDown(x, y, mask);
+
+	return mPick.getObject().notNull();
+}
+
+void LLToolSelectRect::handlePick(const LLPickInfo& pick)
+{
+	mPick = pick;
+
 	// start dragging rectangle
 	setMouseCapture( TRUE );
 
-	mDragStartX = x;
-	mDragStartY = y;
-	mDragEndX = x;
-	mDragEndY = y;
+	mDragStartX = pick.mMousePt.mX;
+	mDragStartY = pick.mMousePt.mY;
+	mDragEndX = pick.mMousePt.mX;
+	mDragEndY = pick.mMousePt.mY;
 
 	mMouseOutsideSlop = FALSE;
-
-	LLToolSelect::handleMouseDown(x, y, mask);
-	return TRUE;
 }
 
 
diff --git a/indra/newview/lltoolselectrect.h b/indra/newview/lltoolselectrect.h
index d45f0a433c08ff191f11252379545c39b0382550..63b8d26e242c904c5a60c3be4c62f248b313c9e6 100644
--- a/indra/newview/lltoolselectrect.h
+++ b/indra/newview/lltoolselectrect.h
@@ -46,6 +46,8 @@ class LLToolSelectRect
 	virtual BOOL	handleHover(S32 x, S32 y, MASK mask);
 	virtual void	draw();							// draw the select rectangle
 
+	void handlePick(const LLPickInfo& pick);
+
 protected:
 	void			handleRectangleSelection(S32 x, S32 y, MASK mask);	// true if you selected one
 	BOOL			outsideSlop(S32 x, S32 y, S32 start_x, S32 start_y);
diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp
index b2cf873a2192bbbe43348b6c7595911875ebade4..0e83ba1123b113083bb31ad83a20822015cdb711 100644
--- a/indra/newview/llviewerdisplay.cpp
+++ b/indra/newview/llviewerdisplay.cpp
@@ -798,7 +798,12 @@ void render_hud_attachments()
 	glh::matrix4f current_proj = glh_get_current_projection();
 	glh::matrix4f current_mod = glh_get_current_modelview();
 
-	if (LLPipeline::sShowHUDAttachments && !gDisconnected && setup_hud_matrices(FALSE))
+	// clamp target zoom level to reasonable values
+	gAgent.mHUDTargetZoom = llclamp(gAgent.mHUDTargetZoom, 0.1f, 1.f);
+	// smoothly interpolate current zoom level
+	gAgent.mHUDCurZoom = lerp(gAgent.mHUDCurZoom, gAgent.mHUDTargetZoom, LLCriticalDamp::getInterpolant(0.03f));
+
+	if (LLPipeline::sShowHUDAttachments && !gDisconnected && setup_hud_matrices())
 	{
 		LLCamera hud_cam = *LLViewerCamera::getInstance();
 		LLVector3 origin = hud_cam.getOrigin();
@@ -856,52 +861,53 @@ void render_hud_attachments()
 	glh_set_current_modelview(current_mod);
 }
 
-BOOL setup_hud_matrices(BOOL for_select)
+BOOL setup_hud_matrices()
+{
+	LLRect whole_screen = gViewerWindow->getVirtualWindowRect();
+
+	// apply camera zoom transform (for high res screenshots)
+	F32 zoom_factor = LLViewerCamera::getInstance()->getZoomFactor();
+	S16 sub_region = LLViewerCamera::getInstance()->getZoomSubRegion();
+	if (zoom_factor > 1.f)
+	{
+		S32 num_horizontal_tiles = llceil(zoom_factor);
+		S32 tile_width = llround((F32)gViewerWindow->getWindowWidth() / zoom_factor);
+		S32 tile_height = llround((F32)gViewerWindow->getWindowHeight() / zoom_factor);
+		int tile_y = sub_region / num_horizontal_tiles;
+		int tile_x = sub_region - (tile_y * num_horizontal_tiles);
+		glh::matrix4f mat;
+
+		whole_screen.setLeftTopAndSize(tile_x * tile_width, gViewerWindow->getWindowHeight() - (tile_y * tile_height), tile_width, tile_height);
+	}
+
+	return setup_hud_matrices(whole_screen);
+}
+
+BOOL setup_hud_matrices(const LLRect& screen_region)
 {
 	LLVOAvatar* my_avatarp = gAgent.getAvatarObject();
 	if (my_avatarp && my_avatarp->hasHUDAttachment())
 	{
-		if (!for_select)
-		{
-			// clamp target zoom level to reasonable values
-			my_avatarp->mHUDTargetZoom = llclamp(my_avatarp->mHUDTargetZoom, 0.1f, 1.f);
-			// smoothly interpolate current zoom level
-			my_avatarp->mHUDCurZoom = lerp(my_avatarp->mHUDCurZoom, my_avatarp->mHUDTargetZoom, LLCriticalDamp::getInterpolant(0.03f));
-		}
-
-		F32 zoom_level = my_avatarp->mHUDCurZoom;
-		// clear z buffer and set up transform for hud
-		if (!for_select)
-		{
-			//glClear(GL_DEPTH_BUFFER_BIT);
-		}
+		F32 zoom_level = gAgent.mHUDCurZoom;
 		LLBBox hud_bbox = my_avatarp->getHUDBBox();
 
-		
-		// set up transform to encompass bounding box of HUD
+		// set up transform to keep HUD objects in front of camera
 		glMatrixMode(GL_PROJECTION);
 		F32 hud_depth = llmax(1.f, hud_bbox.getExtentLocal().mV[VX] * 1.1f);
-		if (for_select)
-		{
-			//RN: reset viewport to window extents so ortho screen is calculated with proper reference frame
-			gViewerWindow->setupViewport();
-		}
 		glh::matrix4f proj = gl_ortho(-0.5f * LLViewerCamera::getInstance()->getAspect(), 0.5f * LLViewerCamera::getInstance()->getAspect(), -0.5f, 0.5f, 0.f, hud_depth);
 		proj.element(2,2) = -0.01f;
 
-		// apply camera zoom transform (for high res screenshots)
-		F32 zoom_factor = LLViewerCamera::getInstance()->getZoomFactor();
-		S16 sub_region = LLViewerCamera::getInstance()->getZoomSubRegion();
-		if (zoom_factor > 1.f)
-		{
-			float offset = zoom_factor - 1.f;
-			int pos_y = sub_region / llceil(zoom_factor);
-			int pos_x = sub_region - (pos_y*llceil(zoom_factor));
-			glh::matrix4f mat;
-			mat.set_scale(glh::vec3f(zoom_factor, zoom_factor, 1.f));
-			mat.set_translate(glh::vec3f(LLViewerCamera::getInstance()->getAspect() * 0.5f * (offset - (F32)pos_x * 2.f), 0.5f * (offset - (F32)pos_y * 2.f), 0.f));
-			proj *= mat;
-		}
+		F32 aspect_ratio = LLViewerCamera::getInstance()->getAspect();
+
+		glh::matrix4f mat;
+		F32 scale_x = (F32)gViewerWindow->getWindowWidth() / (F32)screen_region.getWidth();
+		F32 scale_y = (F32)gViewerWindow->getWindowHeight() / (F32)screen_region.getHeight();
+		mat.set_scale(glh::vec3f(scale_x, scale_y, 1.f));
+		mat.set_translate(
+			glh::vec3f(clamp_rescale((F32)screen_region.getCenterX(), 0.f, (F32)gViewerWindow->getWindowWidth(), 0.5f * scale_x * aspect_ratio, -0.5f * scale_x * aspect_ratio),
+						clamp_rescale((F32)screen_region.getCenterY(), 0.f, (F32)gViewerWindow->getWindowHeight(), 0.5f * scale_y, -0.5f * scale_y),
+						0.f));
+		proj *= mat;
 
 		glLoadMatrixf(proj.m);
 		glh_set_current_projection(proj);
@@ -909,9 +915,8 @@ BOOL setup_hud_matrices(BOOL for_select)
 		glMatrixMode(GL_MODELVIEW);
 		glh::matrix4f model((GLfloat*) OGL_TO_CFR_ROTATION);
 		
-		glh::matrix4f mat;
-		mat.set_translate(glh::vec3f(-hud_bbox.getCenterLocal().mV[VX] + (hud_depth * 0.5f), 0.f, 0.f));
 		mat.set_scale(glh::vec3f(zoom_level, zoom_level, zoom_level));
+		mat.set_translate(glh::vec3f(-hud_bbox.getCenterLocal().mV[VX] + (hud_depth * 0.5f), 0.f, 0.f));
 
 		model *= mat;
 		glLoadMatrixf(model.m);
@@ -1127,14 +1132,14 @@ void render_ui_2d()
 	gGL.getTexUnit(0)->setTextureBlendType(LLTexUnit::TB_MULT);
 
 	// render outline for HUD
-	if (gAgent.getAvatarObject() && gAgent.getAvatarObject()->mHUDCurZoom < 0.98f)
+	if (gAgent.getAvatarObject() && gAgent.mHUDCurZoom < 0.98f)
 	{
 		glPushMatrix();
 		S32 half_width = (gViewerWindow->getWindowWidth() / 2);
 		S32 half_height = (gViewerWindow->getWindowHeight() / 2);
 		glScalef(LLUI::sGLScaleFactor.mV[0], LLUI::sGLScaleFactor.mV[1], 1.f);
 		glTranslatef((F32)half_width, (F32)half_height, 0.f);
-		F32 zoom = gAgent.getAvatarObject()->mHUDCurZoom;
+		F32 zoom = gAgent.mHUDCurZoom;
 		glScalef(zoom,zoom,1.f);
 		gGL.color4fv(LLColor4::white.mV);
 		gl_rect_2d(-half_width, half_height, half_width, -half_height, FALSE);
diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp
index 1586f8dc75971982ca1bd859b0fae77dff63d8c9..3f57b26fec7c8de66699a4db238e27d465bb3755 100644
--- a/indra/newview/llviewermedia.cpp
+++ b/indra/newview/llviewermedia.cpp
@@ -694,5 +694,3 @@ void LLViewerMedia::setMimeType(std::string mime_type)
 {
 	sViewerMediaImpl.mMimeType = mime_type;
 }
-
-
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index 347ddac7977dbe75134fd87ca532aeeeb7e78d1d..a095c9e159113db4a32b3f7e98c8113e56f479b8 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -1359,6 +1359,9 @@ void init_debug_rendering_menu(LLMenuGL* menu)
 	sub_menu->append(new LLMenuItemCheckGL("Glow",&LLPipeline::toggleRenderDebug, NULL,
 													&LLPipeline::toggleRenderDebugControl,
 													(void*)LLPipeline::RENDER_DEBUG_GLOW));
+	sub_menu->append(new LLMenuItemCheckGL("Raycasting",	&LLPipeline::toggleRenderDebug, NULL,
+													&LLPipeline::toggleRenderDebugControl,
+													(void*)LLPipeline::RENDER_DEBUG_RAYCAST));
 	
 	sub_menu->append(new LLMenuItemCheckGL("Show Depth Buffer",
 										   &menu_toggle_control,
@@ -1628,7 +1631,11 @@ class LLObjectReportAbuse : public view_listener_t
 {
 	bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
 	{
-		LLFloaterReporter::showFromObject(gLastHitObjectID);
+		LLViewerObject* objectp = LLSelectMgr::getInstance()->getSelection()->getFirstObject();
+		if (objectp)
+		{
+			LLFloaterReporter::showFromObject(objectp->getID());
+		}
 		return true;
 	}
 };
@@ -1638,7 +1645,7 @@ class LLObjectEnableReportAbuse : public view_listener_t
 {
 	bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
 	{
-		bool new_value = !gLastHitObjectID.isNull();
+		bool new_value = LLSelectMgr::getInstance()->getSelection()->getObjectCount() != 0;
 		gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
 		return true;
 	}
@@ -1648,7 +1655,7 @@ class LLObjectTouch : public view_listener_t
 {
 	bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
 	{
-		LLViewerObject* object = gObjectList.findObject(gLastHitObjectID);
+		LLViewerObject* object = LLSelectMgr::getInstance()->getSelection()->getFirstObject();
 		if (!object) return true;
 
 		LLMessageSystem	*msg = gMessageSystem;
@@ -1683,7 +1690,7 @@ class LLObjectEnableTouch : public view_listener_t
 {
 	bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
 	{
-		LLViewerObject* obj = gObjectList.findObject(gLastHitObjectID);
+		LLViewerObject* obj = LLSelectMgr::getInstance()->getSelection()->getFirstObject();
 		bool new_value = obj && obj->flagHandleTouch();
 		gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
 
@@ -1717,7 +1724,7 @@ void label_touch(std::string& label, void*)
 
 bool handle_object_open()
 {
-	LLViewerObject* obj = gObjectList.findObject(gLastHitObjectID);
+	LLViewerObject* obj = LLSelectMgr::getInstance()->getSelection()->getFirstObject();
 	if(!obj) return true;
 
 	LLFloaterOpenObject::show();
@@ -1738,7 +1745,7 @@ class LLObjectEnableOpen : public view_listener_t
 	{
 		// Look for contents in root object, which is all the LLFloaterOpenObject
 		// understands.
-		LLViewerObject* obj = gObjectList.findObject(gLastHitObjectID);
+		LLViewerObject* obj = LLSelectMgr::getInstance()->getSelection()->getFirstObject();
 		bool new_value = (obj != NULL);
 		if (new_value)
 		{
@@ -1838,14 +1845,14 @@ class LLObjectBuild : public view_listener_t
 		{
 			// zoom in if we're looking at the avatar
 			gAgent.setFocusOnAvatar(FALSE, ANIMATE);
-			gAgent.setFocusGlobal(gLastHitPosGlobal + gLastHitObjectOffset, gLastHitObjectID);
+			gAgent.setFocusGlobal(LLToolPie::getInstance()->getPick());
 			gAgent.cameraZoomIn(0.666f);
 			gAgent.cameraOrbitOver( 30.f * DEG_TO_RAD );
 			gViewerWindow->moveCursorToCenter();
 		}
 		else if ( gSavedSettings.getBOOL("EditCameraMovement") )
 		{
-			gAgent.setFocusGlobal(gLastHitPosGlobal + gLastHitObjectOffset, gLastHitObjectID);
+			gAgent.setFocusGlobal(LLToolPie::getInstance()->getPick());
 			gViewerWindow->moveCursorToCenter();
 		}
 
@@ -1878,13 +1885,17 @@ class LLObjectEdit : public view_listener_t
 			else
 			{
 				gAgent.setFocusOnAvatar(FALSE, ANIMATE);
+				LLViewerObject* selected_objectp = selection->getFirstRootObject();
+				if (selected_objectp)
+				{
 				// zoom in on object center instead of where we clicked, as we need to see the manipulator handles
-				gAgent.setFocusGlobal(gLastHitPosGlobal /*+ gLastHitObjectOffset*/, gLastHitObjectID);
+					gAgent.setFocusGlobal(selected_objectp->getPositionGlobal(), selected_objectp->getID());
 				gAgent.cameraZoomIn(0.666f);
 				gAgent.cameraOrbitOver( 30.f * DEG_TO_RAD );
 				gViewerWindow->moveCursorToCenter();
 			}
 		}
+		}
 
 		gFloaterTools->open();		/* Flawfinder: ignore */
 	
@@ -1923,7 +1934,7 @@ class LLLandBuild : public view_listener_t
 		{
 			// zoom in if we're looking at the avatar
 			gAgent.setFocusOnAvatar(FALSE, ANIMATE);
-			gAgent.setFocusGlobal(gLastHitPosGlobal + gLastHitObjectOffset, gLastHitObjectID);
+			gAgent.setFocusGlobal(LLToolPie::getInstance()->getPick());
 			gAgent.cameraZoomIn(0.666f);
 			gAgent.cameraOrbitOver( 30.f * DEG_TO_RAD );
 			gViewerWindow->moveCursorToCenter();
@@ -1931,7 +1942,7 @@ class LLLandBuild : public view_listener_t
 		else if ( gSavedSettings.getBOOL("EditCameraMovement")  )
 		{
 			// otherwise just move focus
-			gAgent.setFocusGlobal(gLastHitPosGlobal + gLastHitObjectOffset, gLastHitObjectID);
+			gAgent.setFocusGlobal(LLToolPie::getInstance()->getPick());
 			gViewerWindow->moveCursorToCenter();
 		}
 
@@ -2049,15 +2060,19 @@ BOOL enable_has_attachments(void*)
 //---------------------------------------------------------------------------
 void handle_follow(void *userdata)
 {
-	// follow a given avatar, ID in gLastHitObjectID
-	gAgent.startFollowPilot(gLastHitObjectID);
+	// follow a given avatar by ID
+	LLViewerObject* objectp = LLSelectMgr::getInstance()->getSelection()->getFirstObject();
+	if (objectp)
+	{
+		gAgent.startFollowPilot(objectp->getID());
+	}
 }
 
 class LLObjectEnableMute : public view_listener_t
 {
 	bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
 	{
-		LLViewerObject* object = gViewerWindow->lastObjectHit();
+		LLViewerObject* object = LLSelectMgr::getInstance()->getSelection()->getFirstObject();
 		bool new_value = (object != NULL);
 		if (new_value)
 		{
@@ -2080,7 +2095,7 @@ class LLObjectMute : public view_listener_t
 {
 	bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
 	{
-		LLViewerObject* object = gViewerWindow->lastObjectHit();
+		LLViewerObject* object = LLSelectMgr::getInstance()->getSelection()->getFirstObject();
 		if (!object) return true;
 		
 		LLUUID id;
@@ -2136,11 +2151,12 @@ bool handle_go_to()
 	// JAMESDEBUG try simulator autopilot
 	std::vector<std::string> strings;
 	std::string val;
-	val = llformat("%g", gLastHitPosGlobal.mdV[VX]);
+	LLVector3d pos = LLToolPie::getInstance()->getPick().mPosGlobal;
+	val = llformat("%g", pos.mdV[VX]);
 	strings.push_back(val);
-	val = llformat("%g", gLastHitPosGlobal.mdV[VY]);
+	val = llformat("%g", pos.mdV[VY]);
 	strings.push_back(val);
-	val = llformat("%g", gLastHitPosGlobal.mdV[VZ]);
+	val = llformat("%g", pos.mdV[VZ]);
 	strings.push_back(val);
 	send_generic_message("autopilot", strings);
 
@@ -2209,7 +2225,7 @@ class LLAvatarFreeze : public view_listener_t
 {
 	bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
 	{
-		LLVOAvatar* avatar = find_avatar_from_object( gViewerWindow->lastObjectHit() );
+		LLVOAvatar* avatar = find_avatar_from_object( LLSelectMgr::getInstance()->getSelection()->getFirstObject() );
 		if( avatar )
 		{
 			LLUUID* avatar_id = new LLUUID( avatar->getID() );
@@ -2259,7 +2275,7 @@ class LLAvatarDebug : public view_listener_t
 {
 	bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
 	{
-		LLVOAvatar* avatar = find_avatar_from_object( gViewerWindow->lastObjectHit() );
+		LLVOAvatar* avatar = find_avatar_from_object( LLSelectMgr::getInstance()->getSelection()->getFirstObject() );
 		if( avatar )
 		{
 			avatar->dumpLocalTextures();
@@ -2311,7 +2327,7 @@ class LLAvatarEject : public view_listener_t
 {
 	bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
 	{
-		LLVOAvatar* avatar = find_avatar_from_object( gViewerWindow->lastObjectHit() );
+		LLVOAvatar* avatar = find_avatar_from_object( LLSelectMgr::getInstance()->getSelection()->getFirstObject() );
 		if( avatar )
 		{
 			LLUUID* avatar_id = new LLUUID( avatar->getID() );
@@ -2341,7 +2357,7 @@ class LLAvatarEnableFreezeEject : public view_listener_t
 {
 	bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
 	{
-		LLVOAvatar* avatar = find_avatar_from_object( gViewerWindow->lastObjectHit() );
+		LLVOAvatar* avatar = find_avatar_from_object( LLSelectMgr::getInstance()->getSelection()->getFirstObject() );
 		bool new_value = (avatar != NULL);
 
 		if (new_value)
@@ -2366,7 +2382,7 @@ class LLAvatarGiveCard : public view_listener_t
 	bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
 	{
 		llinfos << "handle_give_card()" << llendl;
-		LLViewerObject* dest = gViewerWindow->lastObjectHit();
+		LLViewerObject* dest = LLSelectMgr::getInstance()->getSelection()->getFirstObject();
 		if(dest && dest->isAvatar())
 		{
 			bool found_name = false;
@@ -2605,9 +2621,9 @@ void handle_dump_region_object_cache(void*)
 
 void handle_dump_focus(void *)
 {
-	LLView *view = gFocusMgr.getKeyboardFocus();
-	std::string name = view ? view->getName() : "(none)";
-	llinfos << "Keyboard focus " << name << llendl;
+	LLUICtrl *ctrl = dynamic_cast<LLUICtrl*>(gFocusMgr.getKeyboardFocus());
+
+	llinfos << "Keyboard focus " << (ctrl ? ctrl->getName() : "(none)") << llendl;
 }
 
 class LLSelfStandUp : public view_listener_t
@@ -2823,7 +2839,7 @@ class LLAvatarEnableAddFriend : public view_listener_t
 {
 	bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
 	{
-		LLVOAvatar* avatar = find_avatar_from_object(gViewerWindow->lastObjectHit());
+		LLVOAvatar* avatar = find_avatar_from_object(LLSelectMgr::getInstance()->getSelection()->getFirstObject());
 		bool new_value = avatar && !is_agent_friend(avatar->getID());
 		gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
 		return true;
@@ -2870,10 +2886,12 @@ class LLEditEnableCustomizeAvatar : public view_listener_t
 	}
 };
 
+// only works on pie menu
 bool handle_sit_or_stand()
 {
-	LLViewerObject *object = gObjectList.findObject(gLastHitNonFloraObjectID);	
-	if (!object)
+	LLPickInfo pick = LLToolPie::getInstance()->getPick();
+	LLViewerObject *object = pick.getObject();;
+	if (!object || pick.mPickType == LLPickInfo::PICK_FLORA)
 	{
 		return true;
 	}
@@ -2888,17 +2906,13 @@ bool handle_sit_or_stand()
 
 	if (object && object->getPCode() == LL_PCODE_VOLUME)
 	{
-		LLVector3d offset_double = gViewerWindow->lastNonFloraObjectHitOffset();
-		LLVector3 offset_single;
-		offset_single.setVec(offset_double);
-		
 		gMessageSystem->newMessageFast(_PREHASH_AgentRequestSit);
 		gMessageSystem->nextBlockFast(_PREHASH_AgentData);
 		gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
 		gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
 		gMessageSystem->nextBlockFast(_PREHASH_TargetObject);
 		gMessageSystem->addUUIDFast(_PREHASH_TargetID, object->mID);
-		gMessageSystem->addVector3Fast(_PREHASH_Offset, offset_single);
+		gMessageSystem->addVector3Fast(_PREHASH_Offset, pick.mObjectOffset);
 
 		object->getRegion()->sendReliableMessage();
 	}
@@ -2932,7 +2946,7 @@ class LLLandSit : public view_listener_t
 		gAgent.setControlFlags(AGENT_CONTROL_STAND_UP);
 		LLViewerParcelMgr::getInstance()->deselectLand();
 
-		LLVector3d posGlobal = gLastHitPosGlobal;
+		LLVector3d posGlobal = LLToolPie::getInstance()->getPick().mPosGlobal;
 		
 		LLQuaternion target_rot;
 		if (gAgent.getAvatarObject())
@@ -5028,7 +5042,7 @@ class LLAvatarInviteToGroup : public view_listener_t
 {
 	bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
 	{
-		LLVOAvatar* avatar = find_avatar_from_object( gViewerWindow->lastObjectHit() );
+		LLVOAvatar* avatar = find_avatar_from_object( LLSelectMgr::getInstance()->getSelection()->getFirstObject() );
 		if(avatar)
 		{
 			invite_to_group(avatar->getID());
@@ -5041,7 +5055,7 @@ class LLAvatarAddFriend : public view_listener_t
 {
 	bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
 	{
-		LLVOAvatar* avatar = find_avatar_from_object( gViewerWindow->lastObjectHit() );
+		LLVOAvatar* avatar = find_avatar_from_object( LLSelectMgr::getInstance()->getSelection()->getFirstObject() );
 		if(avatar && !is_agent_friend(avatar->getID()))
 		{
 			request_friendship(avatar->getID());
@@ -5114,11 +5128,11 @@ class LLEnablePayObject : public view_listener_t
 {
 	bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
 	{
-		LLVOAvatar* avatar = find_avatar_from_object(gViewerWindow->lastObjectHit());
+		LLVOAvatar* avatar = find_avatar_from_object(LLSelectMgr::getInstance()->getSelection()->getFirstObject());
 		bool new_value = (avatar != NULL);
 		if (!new_value)
 		{
-			LLViewerObject* object = gViewerWindow->lastObjectHit();
+			LLViewerObject* object = LLSelectMgr::getInstance()->getSelection()->getFirstObject();
 			if( object )
 			{
 				LLViewerObject *parent = (LLViewerObject *)object->getParent();
@@ -5138,8 +5152,9 @@ class LLObjectEnableSitOrStand : public view_listener_t
 	bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
 	{
 		bool new_value = false;
-		LLViewerObject* dest_object = NULL;
-		if((dest_object = gObjectList.findObject(gLastHitObjectID)))
+		LLViewerObject* dest_object = LLSelectMgr::getInstance()->getSelection()->getFirstObject();
+
+		if(dest_object)
 		{
 			if(dest_object->getPCode() == LL_PCODE_VOLUME)
 			{
@@ -5502,7 +5517,11 @@ class LLShowAgentProfile : public view_listener_t
 		}
 		else if (userdata.asString() == "hit object")
 		{
-			agent_id = gLastHitObjectID;
+			LLViewerObject* objectp = LLSelectMgr::getInstance()->getSelection()->getFirstObject();
+			if (objectp)
+			{
+				agent_id = objectp->getID();
+			}
 		}
 		else
 		{
@@ -5538,12 +5557,12 @@ void handle_focus(void *)
 	{
 		// zoom in if we're looking at the avatar
 		gAgent.setFocusOnAvatar(FALSE, ANIMATE);
-		gAgent.setFocusGlobal(gLastHitPosGlobal + gLastHitObjectOffset, gLastHitObjectID);
+		gAgent.setFocusGlobal(LLToolPie::getInstance()->getPick());
 		gAgent.cameraZoomIn(0.666f);
 	}
 	else
 	{
-		gAgent.setFocusGlobal(gLastHitPosGlobal + gLastHitObjectOffset, gLastHitObjectID);
+		gAgent.setFocusGlobal(LLToolPie::getInstance()->getPick());
 	}
 
 	gViewerWindow->moveCursorToCenter();
@@ -5561,19 +5580,19 @@ class LLLandEdit : public view_listener_t
 		{
 			// zoom in if we're looking at the avatar
 			gAgent.setFocusOnAvatar(FALSE, ANIMATE);
-			gAgent.setFocusGlobal(gLastHitPosGlobal + gLastHitObjectOffset, gLastHitObjectID);
+			gAgent.setFocusGlobal(LLToolPie::getInstance()->getPick());
 
 			gAgent.cameraOrbitOver( F_PI * 0.25f );
 			gViewerWindow->moveCursorToCenter();
 		}
 		else if ( gSavedSettings.getBOOL("EditCameraMovement") )
 		{
-			gAgent.setFocusGlobal(gLastHitPosGlobal + gLastHitObjectOffset, gLastHitObjectID);
+			gAgent.setFocusGlobal(LLToolPie::getInstance()->getPick());
 			gViewerWindow->moveCursorToCenter();
 		}
 
 
-		LLViewerParcelMgr::getInstance()->selectParcelAt( gLastHitPosGlobal );
+		LLViewerParcelMgr::getInstance()->selectParcelAt( LLToolPie::getInstance()->getPick().mPosGlobal );
 
 		gFloaterTools->showMore(TRUE);
 		gFloaterView->bringToFront( gFloaterTools );
@@ -5611,13 +5630,13 @@ void handle_move(void*)
 	{
 		// zoom in if we're looking at the avatar
 		gAgent.setFocusOnAvatar(FALSE, ANIMATE);
-		gAgent.setFocusGlobal(gLastHitPosGlobal + gLastHitObjectOffset, gLastHitObjectID);
+		gAgent.setFocusGlobal(LLToolPie::getInstance()->getPick());
 
 		gAgent.cameraZoomIn(0.666f);
 	}
 	else
 	{
-		gAgent.setFocusGlobal(gLastHitPosGlobal + gLastHitObjectOffset, gLastHitObjectID);
+		gAgent.setFocusGlobal(LLToolPie::getInstance()->getPick());
 	}
 
 	gViewerWindow->moveCursorToCenter();
@@ -5719,7 +5738,7 @@ class LLAttachmentDrop : public view_listener_t
 	{
 		// Called when the user clicked on an object attached to them
 		// and selected "Drop".
-		LLViewerObject *object = gViewerWindow->lastObjectHit();
+		LLViewerObject *object = LLSelectMgr::getInstance()->getSelection()->getFirstObject();
 		if (!object)
 		{
 			llwarns << "handle_drop_attachment() - no object to drop" << llendl;
@@ -5819,7 +5838,7 @@ class LLAttachmentDetach : public view_listener_t
 	{
 		// Called when the user clicked on an object attached to them
 		// and selected "Detach".
-		LLViewerObject *object = gViewerWindow->lastObjectHit();
+		LLViewerObject *object = LLSelectMgr::getInstance()->getSelection()->getFirstObject();
 		if (!object)
 		{
 			llwarns << "handle_detach() - no object to detach" << llendl;
@@ -5899,7 +5918,7 @@ class LLAttachmentEnableDrop : public view_listener_t
 		// in your inventory.  Therefore, we disable the drop option until the
 		// item is in your inventory
 
-		LLViewerObject*              object         = gViewerWindow->lastObjectHit();
+		LLViewerObject*              object         = LLSelectMgr::getInstance()->getSelection()->getFirstObject();
 		LLViewerJointAttachment*     attachment_pt  = NULL;
 		LLInventoryItem*             item           = NULL;
 
@@ -5941,7 +5960,7 @@ class LLAttachmentEnableDrop : public view_listener_t
 
 BOOL enable_detach(void*)
 {
-	LLViewerObject* object = gViewerWindow->lastObjectHit();
+	LLViewerObject* object = LLSelectMgr::getInstance()->getSelection()->getFirstObject();
 	if (!object) return FALSE;
 	if (!object->isAttachment()) return FALSE;
 
@@ -6045,7 +6064,7 @@ class LLAvatarSendIM : public view_listener_t
 {
 	bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
 	{
-		LLVOAvatar* avatar = find_avatar_from_object( gViewerWindow->lastObjectHit() );
+		LLVOAvatar* avatar = find_avatar_from_object( LLSelectMgr::getInstance()->getSelection()->getFirstObject() );
 		if(avatar)
 		{
 			std::string name("IM");
@@ -6821,7 +6840,11 @@ void handle_dump_avatar_local_textures(void*)
 
 void handle_debug_avatar_textures(void*)
 {
-	LLFloaterAvatarTextures::show(gLastHitObjectID);
+	LLViewerObject* objectp = LLSelectMgr::getInstance()->getSelection()->getFirstObject();
+	if (objectp)
+	{
+		LLFloaterAvatarTextures::show(objectp->getID());
+	}
 }
 
 void handle_grab_texture(void* data)
diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp
index 19025b34a4d19ac40e70a7855f00b090934a9dbc..1aa7d2c3ffde2a0d0c628cd37d2a8d55a3bf7b5b 100644
--- a/indra/newview/llviewerobject.cpp
+++ b/indra/newview/llviewerobject.cpp
@@ -3039,7 +3039,7 @@ void LLViewerObject::updatePositionCaches() const
 
 const LLVector3d LLViewerObject::getPositionGlobal() const
 {
-	LLVector3d position_global = mRegionp->getPosGlobalFromRegion(getPositionRegion());;
+	LLVector3d position_global = mRegionp->getPosGlobalFromRegion(getPositionRegion());
 
 	if (isAttachment())
 	{
@@ -3365,6 +3365,19 @@ LLViewerObject* LLViewerObject::getRootEdit() const
 	return (LLViewerObject*)root;
 }
 
+
+BOOL LLViewerObject::lineSegmentIntersect(const LLVector3& start, const LLVector3& end,
+										  S32 face,
+										  S32* face_hit,
+										  LLVector3* intersection,
+										  LLVector2* tex_coord,
+										  LLVector3* normal,
+										  LLVector3* bi_normal)
+{
+	return false;
+}
+
+
 U8 LLViewerObject::getMediaType() const
 {
 	if (mMedia)
@@ -4836,11 +4849,6 @@ BOOL LLViewerObject::setFlags(U32 flags, BOOL state)
 	return setit;
 }
 
-BOOL LLViewerObject::lineSegmentIntersect(const LLVector3& start, LLVector3& end) const
-{
-	return FALSE;
-}
-
 void LLViewerObject::applyAngularVelocity(F32 dt)
 {
 	//do target omega here
diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h
index 0f0fa62ea1bb8615a1cdadb0e9a67cf8b83b87d4..a143589ee92fd4ab9cccea4adf24bcdf66163c71 100644
--- a/indra/newview/llviewerobject.h
+++ b/indra/newview/llviewerobject.h
@@ -242,9 +242,15 @@ class LLViewerObject : public LLPrimitive, public LLRefCount
 	
 
 	//detect if given line segment (in agent space) intersects with this viewer object.
-	//returns TRUE if intersection detected and moves end to the point of intersection
-	//closest to start.
-	virtual BOOL lineSegmentIntersect(const LLVector3& start, LLVector3& end) const;
+	//returns TRUE if intersection detected and returns information about intersection
+	virtual BOOL lineSegmentIntersect(const LLVector3& start, const LLVector3& end,
+									  S32 face = -1,                          // which face to check, -1 = ALL_SIDES
+									  S32* face_hit = NULL,                   // which face was hit
+									  LLVector3* intersection = NULL,         // return the intersection point
+									  LLVector2* tex_coord = NULL,            // return the texture coordinates of the intersection point
+									  LLVector3* normal = NULL,               // return the surface normal at the intersection point
+									  LLVector3* bi_normal = NULL             // return the surface bi-normal at the intersection point
+		);
 	
 	virtual const LLVector3d getPositionGlobal() const;
 	virtual const LLVector3 &getPositionRegion() const;
diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp
index 8cd295b8a828aefc5e32e6c4c99b555360be56de..a37120451e3b92ba4867e2e5e6bb30bc99b3aa94 100644
--- a/indra/newview/llviewerobjectlist.cpp
+++ b/indra/newview/llviewerobjectlist.cpp
@@ -1078,14 +1078,14 @@ void LLViewerObjectList::renderObjectBounds(const LLVector3 &center)
 {
 }
 
-
-U32 LLViewerObjectList::renderObjectsForSelect(LLCamera &camera, BOOL pick_parcel_wall, BOOL keep_pick_list)
+void LLViewerObjectList::renderObjectsForSelect(LLCamera &camera, const LLRect& screen_rect, BOOL pick_parcel_wall, BOOL render_transparent)
 {
-	gRenderForSelect = TRUE;
+	generatePickList(camera);
+	renderPickList(screen_rect, pick_parcel_wall, render_transparent);
+}
 
-	//	LLTimer pick_timer;
-	if (!keep_pick_list)
-	{
+void LLViewerObjectList::generatePickList(LLCamera &camera)
+{
 		LLViewerObject *objectp;
 		S32 i;
 		// Reset all of the GL names to zero.
@@ -1199,11 +1199,14 @@ U32 LLViewerObjectList::renderObjectsForSelect(LLCamera &camera, BOOL pick_parce
 			}
 
 			LLHUDIcon::generatePickIDs(i * step, step);
-		
-			// At this point, we should only have live drawables/viewer objects
-			gPipeline.renderForSelect(mSelectPickList);
-		}
 	}
+}
+
+void LLViewerObjectList::renderPickList(const LLRect& screen_rect, BOOL pick_parcel_wall, BOOL render_transparent)
+{
+	gRenderForSelect = TRUE;
+		
+	gPipeline.renderForSelect(mSelectPickList, render_transparent, screen_rect);
 
 	//
 	// Render pass for selected objects
@@ -1220,7 +1223,6 @@ U32 LLViewerObjectList::renderObjectsForSelect(LLCamera &camera, BOOL pick_parce
 
 	//llinfos << "Rendered " << count << " for select" << llendl;
 	//llinfos << "Took " << pick_timer.getElapsedTimeF32()*1000.f << "ms to pick" << llendl;
-	return 0;
 }
 
 LLViewerObject *LLViewerObjectList::getSelectedObject(const U32 object_id)
@@ -1526,3 +1528,4 @@ bool LLViewerObjectList::OrphanInfo::operator!=(const OrphanInfo &rhs) const
 	return !operator==(rhs);
 }
 
+
diff --git a/indra/newview/llviewerobjectlist.h b/indra/newview/llviewerobjectlist.h
index b9b6ff243ff55e3b4c06d9f3f375398b9581af52..a5285930b299881b296696d27bf8a3e83f899f06 100644
--- a/indra/newview/llviewerobjectlist.h
+++ b/indra/newview/llviewerobjectlist.h
@@ -108,7 +108,10 @@ class LLViewerObjectList
 	void updateAvatarVisibility();
 
 	// Selection related stuff
-	U32 renderObjectsForSelect(LLCamera &camera, BOOL pick_parcel_wall = FALSE, BOOL keep_pick_list = FALSE);
+	void renderObjectsForSelect(LLCamera &camera, const LLRect& screen_rect, BOOL pick_parcel_wall = FALSE, BOOL render_transparent = TRUE);
+	void generatePickList(LLCamera &camera);
+	void renderPickList(const LLRect& screen_rect, BOOL pick_parcel_wall, BOOL render_transparent);
+
 	LLViewerObject *getSelectedObject(const U32 object_id);
 
 	inline S32 getNumObjects() { return mObjects.count(); }
diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
index 0296aee8cabf4c53c4d84f30b60b0ea7a86fe7de..4c575ff1392b98b8e72ae78cc2dde58ee5fa21e1 100644
--- a/indra/newview/llviewerwindow.cpp
+++ b/indra/newview/llviewerwindow.cpp
@@ -203,32 +203,8 @@ extern S32 gJamesInt;
 LLViewerWindow	*gViewerWindow = NULL;
 LLVelocityBar	*gVelocityBar = NULL;
 
-LLVector3d		gLastHitPosGlobal;
-LLVector3d		gLastHitObjectOffset;
-LLUUID			gLastHitObjectID;
-S32				gLastHitObjectFace = -1;
-BOOL			gLastHitLand = FALSE;
-F32				gLastHitUCoord;
-F32				gLastHitVCoord;
-
-
-LLVector3d		gLastHitNonFloraPosGlobal;
-LLVector3d		gLastHitNonFloraObjectOffset;
-LLUUID			gLastHitNonFloraObjectID;
-S32				gLastHitNonFloraObjectFace = -1;
-BOOL			gLastHitParcelWall = FALSE;
-
-S32				gLastHitUIElement = 0;
-LLHUDIcon*		gLastHitHUDIcon = NULL;
 
 BOOL			gDebugSelect = FALSE;
-U8				gLastPickAlpha = 255;
-BOOL			gUseGLPick = FALSE;
-
-// On the next pick pass (whenever that happens)
-// should we try to pick individual faces?
-// Cleared to FALSE every time a pick happens.
-BOOL			gPickFaces = FALSE;
 
 LLFrameTimer	gMouseIdleTimer;
 LLFrameTimer	gAwayTimer;
@@ -239,6 +215,11 @@ BOOL			gShowOverlayTitle = FALSE;
 BOOL			gPickTransparent = TRUE;
 
 BOOL			gDebugFastUIRender = FALSE;
+LLViewerObject*  gDebugRaycastObject = NULL;
+LLVector3       gDebugRaycastIntersection;
+LLVector2       gDebugRaycastTexCoord;
+LLVector3       gDebugRaycastNormal;
+LLVector3       gDebugRaycastBinormal;
 
 // HUD display lines in lower right
 BOOL				gDisplayWindInfo = FALSE;
@@ -256,9 +237,6 @@ const F32 MIN_AFK_TIME = 2.f; // minimum time after setting away state before co
 const F32 MAX_FAST_FRAME_TIME = 0.5f;
 const F32 FAST_FRAME_INCREMENT = 0.1f;
 
-const S32 PICK_HALF_WIDTH = 5;
-const S32 PICK_DIAMETER = 2 * PICK_HALF_WIDTH+1;
-
 const F32 MIN_DISPLAY_SCALE = 0.85f;
 
 const S32 CONSOLE_BOTTOM_PAD = 40;
@@ -1509,8 +1487,8 @@ LLViewerWindow::LLViewerWindow(
 	mToolStored( NULL ),
 	mSuppressToolbox( FALSE ),
 	mHideCursorPermanent( FALSE ),
-	mPickPending(FALSE),
-	mIgnoreActivate( FALSE )
+	mIgnoreActivate( FALSE ),
+	mHoverPick()
 {
 	// Default to application directory.
 	LLViewerWindow::sSnapshotBaseName = "Snapshot";
@@ -1618,8 +1596,6 @@ LLViewerWindow::LLViewerWindow(
 	mCurrentMousePoint.mX = getWindowWidth() / 2;
 	mCurrentMousePoint.mY = getWindowHeight() / 2;
 
-	mPickBuffer = new U8[PICK_DIAMETER * PICK_DIAMETER * 4];
-
 	gShowOverlayTitle = gSavedSettings.getBOOL("ShowOverlayTitle");
 	mOverlayTitle = gSavedSettings.getString("OverlayTitle");
 	// Can't have spaces in settings.ini strings, so use underscores instead and convert them.
@@ -2032,9 +2008,6 @@ LLViewerWindow::~LLViewerWindow()
 
 	LLViewerImage::cleanupClass();
 	
-	delete[] mPickBuffer;
-	mPickBuffer = NULL;
-
 	llinfos << "Cleaning up select manager" << llendl;
 	LLSelectMgr::getInstance()->cleanup();
 
@@ -2733,6 +2706,10 @@ BOOL LLViewerWindow::handlePerFrameHover()
 
 	LLView::sMouseHandlerMessage.clear();
 
+	S32 x = mCurrentMousePoint.mX;
+	S32 y = mCurrentMousePoint.mY;
+	MASK mask = gKeyboard->currentMask(TRUE);
+
 	//RN: fix for asynchronous notification of mouse leaving window not working
 	LLCoordWindow mouse_pos;
 	mWindow->getCursorPosition(&mouse_pos);
@@ -2748,6 +2725,7 @@ BOOL LLViewerWindow::handlePerFrameHover()
 		mMouseInWindow = TRUE;
 	}
 
+
 	S32 dx = lltrunc((F32) (mCurrentMousePoint.mX - mLastMousePoint.mX) * LLUI::sGLScaleFactor.mV[VX]);
 	S32 dy = lltrunc((F32) (mCurrentMousePoint.mY - mLastMousePoint.mY) * LLUI::sGLScaleFactor.mV[VY]);
 
@@ -2778,10 +2756,6 @@ BOOL LLViewerWindow::handlePerFrameHover()
 		return TRUE;
 	}
 
-	S32 x = mCurrentMousePoint.mX;
-	S32 y = mCurrentMousePoint.mY;
-	MASK mask = gKeyboard->currentMask(TRUE);
-
 	// clean up current focus
 	LLUICtrl* cur_focus = gFocusMgr.getKeyboardFocus();
 	if (cur_focus)
@@ -3123,10 +3097,41 @@ BOOL LLViewerWindow::handlePerFrameHover()
 		LLSelectMgr::getInstance()->deselectUnused();
 	}
 
+	if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_RAYCAST))
+	{
+		gDebugRaycastObject = cursorIntersect(-1, -1, 512.f, NULL, -1,
+											  NULL,
+											  &gDebugRaycastIntersection,
+											  &gDebugRaycastTexCoord,
+											  &gDebugRaycastNormal,
+											  &gDebugRaycastBinormal);
+	}
+
+	static U16 frame_counter = 0;
+	static S32 previous_x = -1;
+	static S32 previous_y = -1;
+	
+	if (((previous_x != x) || (previous_y != y)) ||
+		((gSavedSettings.getBOOL("PerFrameHoverPick"))
+		 && ((frame_counter % gSavedSettings.getS32("PerFrameHoverPickCount")) == 0)))
+		{
+			pickAsync(getCurrentMouseX(), getCurrentMouseY(), mask, hoverPickCallback, TRUE);
+		}
+	frame_counter++;
+	previous_x = x;
+	previous_y = y;
+	
 	return handled;
 }
 
 
+/* static */
+void LLViewerWindow::hoverPickCallback(const LLPickInfo& pick_info)
+{
+	gViewerWindow->mHoverPick = pick_info;
+}
+	
+
 void LLViewerWindow::saveLastMouse(const LLCoordGL &point)
 {
 	// Store last mouse location.
@@ -3220,7 +3225,7 @@ void LLViewerWindow::renderSelections( BOOL for_gl_pick, BOOL pick_parcel_walls,
 			glPushMatrix();
 			if (selection->getSelectType() == SELECT_TYPE_HUD)
 			{
-				F32 zoom = gAgent.getAvatarObject()->mHUDCurZoom;
+				F32 zoom = gAgent.mHUDCurZoom;
 				glScalef(zoom, zoom, zoom);
 			}
 
@@ -3372,19 +3377,14 @@ BOOL LLViewerWindow::clickPointOnSurfaceGlobal(const S32 x, const S32 y, LLViewe
 	return intersect;
 }
 
-void LLViewerWindow::hitObjectOrLandGlobalAsync(S32 x, S32 y_from_bot, MASK mask, void (*callback)(S32 x, S32 y, MASK mask), BOOL pick_transparent, BOOL pick_parcel_walls)
+void LLViewerWindow::pickAsync(S32 x, S32 y_from_bot, MASK mask, void (*callback)(const LLPickInfo& info), BOOL pick_transparent, BOOL get_surface_info)
 {
 	if (gNoRender)
 	{
 		return;
 	}
 	
-	glClear(GL_DEPTH_BUFFER_BIT);
-	gDepthDirty = TRUE;
-
-	S32 scaled_x = llround((F32)x * mDisplayScale.mV[VX]);
-	S32 scaled_y = llround((F32)y_from_bot * mDisplayScale.mV[VY]);
-
+	// push back pick info object
 	BOOL in_build_mode = gFloaterTools && gFloaterTools->getVisible();
 	if (in_build_mode || LLDrawPoolAlpha::sShowDebugAlpha)
 	{
@@ -3392,29 +3392,44 @@ void LLViewerWindow::hitObjectOrLandGlobalAsync(S32 x, S32 y_from_bot, MASK mask
 		// "Show Debug Alpha" means no object actually transparent
 		pick_transparent = TRUE;
 	}
-	gPickTransparent = pick_transparent;
 
-	gUseGLPick = FALSE;
-	mPickCallback = callback;
+	// center initial pick frame buffer region under mouse cursor
+	// since that area is guaranteed to be onscreen and hence a valid
+	// part of the framebuffer
+	if (mPicks.empty())
+	{
+		mPickScreenRegion.setCenterAndSize(x, y_from_bot, PICK_DIAMETER, PICK_DIAMETER);
 
-	// Default to not hitting anything
-	gLastHitPosGlobal.zeroVec();
-	gLastHitObjectOffset.zeroVec();
-	gLastHitObjectID.setNull();
-	gLastHitObjectFace = -1;
+		if (mPickScreenRegion.mLeft < 0) mPickScreenRegion.translate(-mPickScreenRegion.mLeft, 0);
+		if (mPickScreenRegion.mBottom < 0) mPickScreenRegion.translate(0, -mPickScreenRegion.mBottom);
+		if (mPickScreenRegion.mRight > mWindowRect.getWidth() ) mPickScreenRegion.translate(mWindowRect.getWidth() - mPickScreenRegion.mRight, 0);
+		if (mPickScreenRegion.mTop > mWindowRect.getHeight() ) mPickScreenRegion.translate(0, mWindowRect.getHeight() - mPickScreenRegion.mTop);
+	}
 
-	gLastHitNonFloraPosGlobal.zeroVec();
-	gLastHitNonFloraObjectOffset.zeroVec();
-	gLastHitNonFloraObjectID.setNull();
-	gLastHitNonFloraObjectFace = -1;
+	// set frame buffer region for picking results
+	// stack multiple picks left to right
+	LLRect screen_region = mPickScreenRegion;
+	screen_region.translate(mPicks.size() * PICK_DIAMETER, 0);
 
-	gLastHitParcelWall = FALSE;
+	LLPickInfo pick(LLCoordGL(x, y_from_bot), screen_region, mask, pick_transparent, get_surface_info, callback);
 
+	schedulePick(pick);
+}
+
+void LLViewerWindow::schedulePick(LLPickInfo& pick_info)
+{
+	llassert_always(pick_info.mScreenRegion.notNull());
+	mPicks.push_back(pick_info);
+	
+	S32 scaled_x = llround((F32)pick_info.mMousePt.mX * mDisplayScale.mV[VX]);
+	S32 scaled_y = llround((F32)pick_info.mMousePt.mY * mDisplayScale.mV[VY]);
+
+	// Default to not hitting anything
 	LLCamera pick_camera;
 	pick_camera.setOrigin(LLViewerCamera::getInstance()->getOrigin());
 	pick_camera.setOriginAndLookAt(LLViewerCamera::getInstance()->getOrigin(),
 								   LLViewerCamera::getInstance()->getUpAxis(),
-								   LLViewerCamera::getInstance()->getOrigin() + mouseDirectionGlobal(x, y_from_bot));
+								   LLViewerCamera::getInstance()->getOrigin() + mouseDirectionGlobal(pick_info.mMousePt.mX, pick_info.mMousePt.mY));
 	pick_camera.setView(0.5f*DEG_TO_RAD);
 	pick_camera.setNear(LLViewerCamera::getInstance()->getNear());
 	pick_camera.setFar(LLViewerCamera::getInstance()->getFar());
@@ -3431,117 +3446,38 @@ void LLViewerWindow::hitObjectOrLandGlobalAsync(S32 x, S32 y_from_bot, MASK mask
 	glPushMatrix();
 	glLoadIdentity();
 
-	// build perspective transform and picking viewport
-	// Perform pick on a PICK_DIAMETER x PICK_DIAMETER pixel region around cursor point.
-	// Don't limit the select distance for this pick.
-	LLViewerCamera::getInstance()->setPerspective(FOR_SELECTION, scaled_x - (PICK_HALF_WIDTH + 2), scaled_y - (PICK_HALF_WIDTH + 2), PICK_DIAMETER + 4, PICK_DIAMETER + 4, FALSE);
-	// make viewport big enough to handle antialiased frame buffers
-	gGLViewport[0] = scaled_x - (PICK_HALF_WIDTH + 2);
-	gGLViewport[1] = scaled_y - (PICK_HALF_WIDTH + 2);
-	gGLViewport[2] = PICK_DIAMETER + 4;
-	gGLViewport[3] = PICK_DIAMETER + 4;
-	glViewport(gGLViewport[0], gGLViewport[1], gGLViewport[2], gGLViewport[3]);
-	LLViewerCamera::updateFrustumPlanes(pick_camera);
-	stop_glerror();
-
+	// clear work area
+	{
+		LLGLState scissor_state(GL_SCISSOR_TEST);
+		scissor_state.enable();
+		glScissor(pick_info.mScreenRegion.mLeft, pick_info.mScreenRegion.mBottom, pick_info.mScreenRegion.getWidth(), pick_info.mScreenRegion.getHeight());
 	glClearColor(0.f, 0.f, 0.f, 0.f);
 	glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
 	//glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
-
-	// Draw the objects so the user can select them.
-	// The starting ID is 1, since land is zero.
-	gObjectList.renderObjectsForSelect(pick_camera, pick_parcel_walls);
-
-	stop_glerror();
-
-	// restore drawing state
-	glMatrixMode(GL_PROJECTION);
-	glPopMatrix();
-	glMatrixMode(GL_MODELVIEW);
-	glPopMatrix();
-
-	setupViewport();
-
-	mPickPoint.set(x, y_from_bot);
-	mPickOffset.set(0, 0);
-	mPickMask = mask;
-	mPickPending = TRUE;
-
-	// delay further event processing until we receive results of pick
-	mWindow->delayInputProcessing();
-}
-
-void LLViewerWindow::hitUIElementImmediate(S32 x, S32 y, void (*callback)(S32 x, S32 y, MASK mask))
-{
-	// Performs the GL UI pick.
-	// Stores its results in global, gLastHitUIElement
-	if (gNoRender)
-	{
-		return;
-	}
-	
-	hitUIElementAsync(x, y, gKeyboard->currentMask(TRUE), NULL);
-	performPick();
-	if (callback)
-	{
-		callback(x, y, gKeyboard->currentMask(TRUE));
-	}
-}
-
-//RN: this currently doesn't do anything
-void LLViewerWindow::hitUIElementAsync(S32 x, S32 y_from_bot, MASK mask, void (*callback)(S32 x, S32 y, MASK mask))
-{
-	if (gNoRender)
-	{
-		return;
 	}
-
-// 	F32 delta_time = gAlphaFadeTimer.getElapsedTimeAndResetF32();
-
-	gUseGLPick = FALSE;
-	mPickCallback = callback;
-
-	// Default to not hitting anything
-	gLastHitUIElement = 0;
-
-	LLCamera pick_camera;
-	pick_camera.setOrigin(LLViewerCamera::getInstance()->getOrigin());
-	pick_camera.setOriginAndLookAt(LLViewerCamera::getInstance()->getOrigin(),
-								   LLViewerCamera::getInstance()->getUpAxis(),
-								   LLViewerCamera::getInstance()->getOrigin() + mouseDirectionGlobal(x, y_from_bot));
-	pick_camera.setView(0.5f*DEG_TO_RAD);
-	pick_camera.setNear(LLViewerCamera::getInstance()->getNear());
-	pick_camera.setFar(LLViewerCamera::getInstance()->getFar());
-	pick_camera.setAspect(1.f);
-
-	// save our drawing state
-	glMatrixMode(GL_MODELVIEW);
-	glPushMatrix();
-	glLoadIdentity();
 	
-	glMatrixMode(GL_PROJECTION);
-	glPushMatrix();
-	glLoadIdentity();
-
-	// build orthogonal transform and picking viewport
+	// build perspective transform and picking viewport
 	// Perform pick on a PICK_DIAMETER x PICK_DIAMETER pixel region around cursor point.
 	// Don't limit the select distance for this pick.
-	setup2DRender();
-	const LLVector2& display_scale = getDisplayScale();
-	glScalef(display_scale.mV[VX], display_scale.mV[VY], 1.f);
+	LLViewerCamera::getInstance()->setPerspective(FOR_SELECTION, scaled_x - PICK_HALF_WIDTH, scaled_y - PICK_HALF_WIDTH, PICK_DIAMETER, PICK_DIAMETER, FALSE);
 
-	gGL.getTexUnit(0)->setTextureBlendType(LLTexUnit::TB_MULT);
+	// render for object picking
 
 	// make viewport big enough to handle antialiased frame buffers
-	glViewport(x - (PICK_HALF_WIDTH + 2), y_from_bot - (PICK_HALF_WIDTH + 2), PICK_DIAMETER + 4, PICK_DIAMETER + 4);
-	stop_glerror();
+	gGLViewport[0] = pick_info.mScreenRegion.mLeft;
+	gGLViewport[1] = pick_info.mScreenRegion.mBottom;
+	gGLViewport[2] = pick_info.mScreenRegion.getWidth();
+	gGLViewport[3] = pick_info.mScreenRegion.getHeight();
 
-	glClearColor(0.f, 0.f, 0.f, 0.f);
-	glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+	glViewport(gGLViewport[0], gGLViewport[1], gGLViewport[2], gGLViewport[3]);
+	LLViewerCamera::updateFrustumPlanes(pick_camera);
+	stop_glerror();
 
 	// Draw the objects so the user can select them.
 	// The starting ID is 1, since land is zero.
-	//drawForSelect();
+	LLRect pick_region;
+	pick_region.setOriginAndSize(scaled_x - PICK_HALF_WIDTH, scaled_y - PICK_HALF_WIDTH, PICK_DIAMETER, PICK_DIAMETER);
+	gObjectList.renderObjectsForSelect(pick_camera, pick_region, FALSE, pick_info.mPickTransparent);
 
 	stop_glerror();
 
@@ -3551,309 +3487,121 @@ void LLViewerWindow::hitUIElementAsync(S32 x, S32 y_from_bot, MASK mask, void (*
 	glMatrixMode(GL_MODELVIEW);
 	glPopMatrix();
 
+	setup3DRender();
+	setup2DRender();
 	setupViewport();
 
-	mPickPoint.set(x, y_from_bot);
-	mPickOffset.set(0, 0);
-	mPickMask = mask;
-	mPickPending = TRUE;
+	// delay further event processing until we receive results of pick
+	mWindow->delayInputProcessing();
 }
 
+
 void LLViewerWindow::performPick()
 {
-	if (gNoRender || !mPickPending)
+	if (gNoRender)
 	{
 		return;
 	}
 
-	mPickPending = FALSE;
-	U32	te_offset = NO_FACE;
-	
-	// find pick region that is fully onscreen
-	LLCoordGL scaled_pick_point = mPickPoint;
-	scaled_pick_point.mX = llclamp(llround((F32)mPickPoint.mX * mDisplayScale.mV[VX]), PICK_HALF_WIDTH, getWindowDisplayWidth() - PICK_HALF_WIDTH);
-	scaled_pick_point.mY = llclamp(llround((F32)mPickPoint.mY * mDisplayScale.mV[VY]), PICK_HALF_WIDTH, getWindowDisplayHeight() - PICK_HALF_WIDTH);
-
-	glReadPixels(scaled_pick_point.mX - PICK_HALF_WIDTH, scaled_pick_point.mY - PICK_HALF_WIDTH, PICK_DIAMETER, PICK_DIAMETER, GL_RGBA, GL_UNSIGNED_BYTE, mPickBuffer);
-
-	S32 pixel_index = PICK_HALF_WIDTH * PICK_DIAMETER + PICK_HALF_WIDTH;
-	S32 name = (U32)mPickBuffer[(pixel_index * 4) + 0] << 16 | (U32)mPickBuffer[(pixel_index * 4) + 1] << 8 | (U32)mPickBuffer[(pixel_index * 4) + 2];
-	gLastPickAlpha = mPickBuffer[(pixel_index * 4) + 3];
-
-	if (name >= (S32)GL_NAME_UI_RESERVED && name < (S32)GL_NAME_INDEX_OFFSET)
-	{
-		// hit a UI element
-		gLastHitUIElement = name;
-		if (mPickCallback)
-		{
-			mPickCallback(mPickPoint.mX, mPickPoint.mY, mPickMask);
-		}
-	}
-
-	//imdebug("rgba rbga=bbba b=8 w=%d h=%d %p", PICK_DIAMETER, PICK_DIAMETER, mPickBuffer);
-
-	S32 x_offset = mPickPoint.mX - llround((F32)scaled_pick_point.mX / mDisplayScale.mV[VX]);
-	S32 y_offset = mPickPoint.mY - llround((F32)scaled_pick_point.mY / mDisplayScale.mV[VY]);
-
-	
-	// we hit nothing, scan surrounding pixels for something useful
-	if (!name)
+	if (!mPicks.empty())
 	{
-		S32 closest_distance = 10000;
-		//S32 closest_pick_name = 0;
-		for (S32 col = 0; col < PICK_DIAMETER; col++)
+		std::vector<LLPickInfo>::iterator pick_it;
+		for (pick_it = mPicks.begin(); pick_it != mPicks.end(); ++pick_it)
 		{
-			for (S32 row = 0; row < PICK_DIAMETER; row++)
-			{
-				S32 distance_squared = (llabs(col - x_offset - PICK_HALF_WIDTH) * llabs(col - x_offset - PICK_HALF_WIDTH)) + (llabs(row - y_offset - PICK_HALF_WIDTH) * llabs(row - y_offset - PICK_HALF_WIDTH));
-				pixel_index = row * PICK_DIAMETER + col;
-				S32 test_name = (U32)mPickBuffer[(pixel_index * 4) + 0] << 16 | (U32)mPickBuffer[(pixel_index * 4) + 1] << 8 | (U32)mPickBuffer[(pixel_index * 4) + 2];
-				gLastPickAlpha = mPickBuffer[(pixel_index * 4) + 3];
-				if (test_name && distance_squared < closest_distance)
-				{
-					closest_distance = distance_squared;
-					name = test_name;
-					gLastPickAlpha = mPickBuffer[(pixel_index * 4) + 3];
-					mPickOffset.mX = col - PICK_HALF_WIDTH;
-					mPickOffset.mY = row - PICK_HALF_WIDTH;
-				}
-			}
+			pick_it->fetchResults();
 		}
-	}
 
-	if (name)
-	{
-		mPickPoint.mX += llround((F32)mPickOffset.mX * mDisplayScale.mV[VX]);
-		mPickPoint.mY += llround((F32)mPickOffset.mY * mDisplayScale.mV[VY]);
-	}
-
-	if (gPickFaces)
-	{
-		te_offset = ((U32)name >> 20);
-		name &= 0x000fffff;
-		// don't clear gPickFaces, as we still need to check for UV coordinates
+		mLastPick = mPicks.back();
+		mPicks.clear();
 	}
 
-	LLViewerObject	*objectp = NULL;
-
-	// Frontmost non-foreground object that isn't trees or grass
-	LLViewerObject* nonflora_objectp = NULL;
-	S32 nonflora_name = -1;
-	S32 nonflora_te_offset = NO_FACE;
-
-	if (name == (S32)GL_NAME_PARCEL_WALL)
-	{
-		gLastHitParcelWall = TRUE;
-	}
-
-	gLastHitHUDIcon = NULL;
-
-	objectp = gObjectList.getSelectedObject(name);
-	if (objectp)
-	{
-		LLViewerObject* parent = (LLViewerObject*)(objectp->getParent());
-		if (NULL == parent) {
-			// if you are the parent
-			parent = objectp;
-		}
-		if (objectp->mbCanSelect)
-		{
-			te_offset = (te_offset == 16) ? NO_FACE : te_offset;
-
-			// If the hit object isn't a plant, store it as the frontmost non-flora object.
-			LLPCode pcode = objectp->getPCode();
-			if( (LL_PCODE_LEGACY_GRASS != pcode) &&
-				(LL_PCODE_LEGACY_TREE != pcode) &&
-				(LL_PCODE_TREE_NEW != pcode))
-			{
-				nonflora_objectp = objectp;
-				nonflora_name = name;
-				nonflora_te_offset = te_offset;
-			}
-		}
-		else
-		{
-			//llinfos << "Hit object you can't select" << llendl;
-		}
-	}
-	else
-	{
-		// was this name referring to a hud icon?
-		gLastHitHUDIcon = LLHUDIcon::handlePick(name);
-	}
-
-	analyzeHit( 
-		mPickPoint.mX, mPickPoint.mY, objectp, te_offset,
-		&gLastHitObjectID, &gLastHitObjectFace, &gLastHitPosGlobal, &gLastHitLand, &gLastHitUCoord, &gLastHitVCoord );
-
-	if (objectp && !gLastHitObjectID.isNull())
-	{
-		gLastHitObjectOffset = gAgent.calcFocusOffset(objectp, mPickPoint.mX, mPickPoint.mY);
-	}
+}
 
-	if( objectp == nonflora_objectp )
-	{
-		gLastHitNonFloraObjectID	= gLastHitObjectID;
-		gLastHitNonFloraObjectFace	= gLastHitObjectFace;
-		gLastHitNonFloraPosGlobal	= gLastHitPosGlobal;
-		gLastHitNonFloraObjectOffset= gLastHitObjectOffset;
-	}
-	else
+// Performs the GL object/land pick.
+LLPickInfo LLViewerWindow::pickImmediate(S32 x, S32 y_from_bot,  BOOL pick_transparent)
+{
+	if (gNoRender)
 	{
-		analyzeHit(  mPickPoint.mX, mPickPoint.mY, nonflora_objectp, nonflora_te_offset,
-			&gLastHitNonFloraObjectID, &gLastHitNonFloraObjectFace, &gLastHitNonFloraPosGlobal,
-			&gLastHitLand, &gLastHitUCoord, &gLastHitVCoord);
-
-		if( nonflora_objectp )
-		{
-			gLastHitNonFloraObjectOffset = gAgent.calcFocusOffset(nonflora_objectp, mPickPoint.mX, mPickPoint.mY);
-		}
+		return LLPickInfo();
 	}
 
-	if (mPickCallback)
-	{
-		mPickCallback(mPickPoint.mX, mPickPoint.mY, mPickMask);
-	}
+	pickAsync(x, y_from_bot, gKeyboard->currentMask(TRUE), NULL, pick_transparent);
+	// assume that pickAsync put the results in the back of the mPicks list
+	mLastPick = mPicks.back();
+	mLastPick.fetchResults();
+	mPicks.pop_back();
 
-	gPickFaces = FALSE;
+	return mLastPick;
 }
 
-// Performs the GL object/land pick.
-// Stores its results in globals, gHit*
-void LLViewerWindow::hitObjectOrLandGlobalImmediate(S32 x, S32 y_from_bot, void (*callback)(S32 x, S32 y, MASK mask), BOOL pick_transparent)
+LLViewerObject* LLViewerWindow::cursorIntersect(S32 mouse_x, S32 mouse_y, F32 depth,
+												LLViewerObject *this_object,
+												S32 this_face,
+												S32* face_hit,
+												LLVector3 *intersection,
+												LLVector2 *uv,
+												LLVector3 *normal,
+												LLVector3 *binormal)
 {
-	if (gNoRender)
-	{
-		return;
-	}
-	
-	hitObjectOrLandGlobalAsync(x, y_from_bot, gKeyboard->currentMask(TRUE), NULL, pick_transparent);
-	performPick();
-	if (callback)
+	S32 x = mouse_x;
+	S32 y = mouse_y;
+
+	if ((mouse_x == -1) && (mouse_y == -1)) // use current mouse position
 	{
-		callback(x, y_from_bot, gKeyboard->currentMask(TRUE));
+		x = getCurrentMouseX();
+		y = getCurrentMouseY();
 	}
-}
 
-LLViewerObject* LLViewerWindow::getObjectUnderCursor(const F32 depth)
-{
-	S32 x = getCurrentMouseX();
-	S32 y = getCurrentMouseY();
+	// HUD coordinates of mouse
+	LLVector3 mouse_point_hud = mousePointHUD(x, y);
+	LLVector3 mouse_hud_start = mouse_point_hud - LLVector3(depth, 0, 0);
+	LLVector3 mouse_hud_end   = mouse_point_hud + LLVector3(depth, 0, 0);
 	
+	// world coordinates of mouse
 	LLVector3		mouse_direction_global = mouseDirectionGlobal(x,y);
-	LLVector3		camera_pos_global = LLViewerCamera::getInstance()->getOrigin();
-	LLVector3		pick_end = camera_pos_global + mouse_direction_global * depth;
-	LLVector3		collision_point;
-	return gPipeline.pickObject(camera_pos_global, pick_end, collision_point);
-}
-
-void LLViewerWindow::analyzeHit(
-	S32				x,				// input
-	S32				y_from_bot,		// input
-	LLViewerObject* objectp,		// input
-	U32				te_offset,		// input
-	LLUUID*			hit_object_id_p,// output
-	S32*			hit_face_p,		// output
-	LLVector3d*		hit_pos_p,		// output
-	BOOL*			hit_land,		// output
-	F32*			hit_u_coord,	// output
-	F32*			hit_v_coord)	// output
-{
-	// Clean up inputs
-	S32 face = -1;
-	
-	if (te_offset != NO_FACE ) 
-	{
-		face = te_offset;
-	}
+	LLVector3 mouse_point_global = LLViewerCamera::getInstance()->getOrigin();
+	LLVector3 mouse_world_start = mouse_point_global;
+	LLVector3 mouse_world_end   = mouse_point_global + mouse_direction_global * depth;
 
-	*hit_land = FALSE;
+	
+	LLViewerObject* found = NULL;
 
-	if (objectp)
+	if (this_object)  // check only this object
 	{
-		if( objectp->getPCode() == LLViewerObject::LL_VO_SURFACE_PATCH )
+		if (this_object->isHUDAttachment()) // is a HUD object?
 		{
-			// Hit land
-			*hit_land = TRUE;
-
-			// put global position into land_pos
-			LLVector3d land_pos;
-			if (mousePointOnLandGlobal(x, y_from_bot, &land_pos))
+			if (this_object->lineSegmentIntersect(mouse_hud_start, mouse_hud_end, this_face,
+												  face_hit, intersection, uv, normal, binormal))
 			{
-				*hit_object_id_p = LLUUID::null;
-				*hit_face_p = -1;
-
-				// Fudge the land focus a little bit above ground.
-				*hit_pos_p = land_pos + LLVector3d(0.f, 0.f, 0.1f);
-				//llinfos << "DEBUG Hit Land " << *hit_pos_p  << llendl;
-				return;
+				found = this_object;
 			}
-			else
-			{
-				//llinfos << "Hit land but couldn't find position" << llendl;
-				// Fall through to "Didn't hit anything"
 			}
-		}
-		else
+		
+		else // is a world object
 		{
-			*hit_object_id_p = objectp->mID;
-			*hit_face_p = face;
-
-			// Hit an object
-			if (objectp->isAvatar())
+			if (this_object->lineSegmentIntersect(mouse_world_start, mouse_world_end, this_face,
+												  face_hit, intersection, uv, normal, binormal))
 			{
-				*hit_pos_p = gAgent.getPosGlobalFromAgent(((LLVOAvatar*)objectp)->mPelvisp->getWorldPosition());
+				found = this_object;
 			}
-			else if (objectp->mDrawable.notNull())
-			{
-				*hit_pos_p = gAgent.getPosGlobalFromAgent(objectp->getRenderPosition());
 			}
-			else
-			{
-				// regular object
-				*hit_pos_p = objectp->getPositionGlobal();
 			}
 
-			if (gPickFaces && face > -1 &&
-				objectp->mDrawable.notNull() && objectp->getPCode() == LL_PCODE_VOLUME &&
-				face < objectp->mDrawable->getNumFaces())
+	else // check ALL objects
 			{
-				// render red-blue gradient to get 1/256 precision
-				// then render green grid to get final 1/4096 precision
-				S32 scaled_x = llround((F32)x * mDisplayScale.mV[VX]);
-				S32 scaled_y = llround((F32)y_from_bot * mDisplayScale.mV[VY]);
-				const S32 UV_PICK_WIDTH = 41;
-				const S32 UV_PICK_HALF_WIDTH = (UV_PICK_WIDTH - 1) / 2;
-				U8 uv_pick_buffer[UV_PICK_WIDTH * UV_PICK_WIDTH * 4];
-				S32 pick_face = face;
-				LLFace* facep = objectp->mDrawable->getFace(pick_face);
-				LLViewerCamera::getInstance()->setPerspective(FOR_SELECTION, scaled_x - UV_PICK_HALF_WIDTH, scaled_y - UV_PICK_HALF_WIDTH, UV_PICK_WIDTH, UV_PICK_WIDTH, FALSE);
-				glViewport(scaled_x - UV_PICK_HALF_WIDTH, scaled_y - UV_PICK_HALF_WIDTH, UV_PICK_WIDTH, UV_PICK_WIDTH);
-				gPipeline.renderFaceForUVSelect(facep);
-
-				glReadPixels(scaled_x - UV_PICK_HALF_WIDTH, scaled_y - UV_PICK_HALF_WIDTH, UV_PICK_WIDTH, UV_PICK_WIDTH, GL_RGBA, GL_UNSIGNED_BYTE, uv_pick_buffer);
-				U8* center_pixel = &uv_pick_buffer[4 * ((UV_PICK_WIDTH * UV_PICK_HALF_WIDTH) + UV_PICK_HALF_WIDTH + 1)];
-				*hit_u_coord = (F32)((center_pixel[VGREEN] & 0xf) + (16.f * center_pixel[VRED])) / 4095.f;
-				*hit_v_coord = (F32)((center_pixel[VGREEN] >> 4) + (16.f * center_pixel[VBLUE])) / 4095.f;
-			}
-			else
+		found = gPipeline.lineSegmentIntersectInHUD(mouse_hud_start, mouse_hud_end,
+													face_hit, intersection, uv, normal, binormal);
+
+		if (!found) // if not found in HUD, look in world:
+
 			{
-				*hit_u_coord = 0.f;
-				*hit_v_coord = 0.f;
+			found = gPipeline.lineSegmentIntersectInWorld(mouse_world_start, mouse_world_end,
+														  face_hit, intersection, uv, normal, binormal);
 			}
 
-			//llinfos << "DEBUG Hit Object " << *hit_pos_p << llendl;
-			return;
-		}
 	}
 
-	// Didn't hit anything.
-	*hit_object_id_p = LLUUID::null;
-	*hit_face_p = -1;
-	*hit_pos_p = LLVector3d::zero;
-	*hit_u_coord = 0.f;
-	*hit_v_coord = 0.f;
-	//llinfos << "DEBUG Hit Nothing " << llendl;
+	return found;
 }
 
 // Returns unit vector relative to camera
@@ -3884,6 +3632,18 @@ LLVector3 LLViewerWindow::mouseDirectionGlobal(const S32 x, const S32 y) const
 	return mouse_vector;
 }
 
+LLVector3 LLViewerWindow::mousePointHUD(const S32 x, const S32 y) const
+{
+	// find screen resolution
+	S32			height = getWindowHeight();
+	S32			width = getWindowWidth();
+
+	// remap with uniform scale (1/height) so that top is -0.5, bottom is +0.5
+	F32 hud_x = -((F32)x - (F32)width/2.f)  / height;
+	F32 hud_y = ((F32)y - (F32)height/2.f) / height;
+
+	return LLVector3(0.f, hud_x, hud_y);
+}
 
 // Returns unit vector relative to camera in camera space
 // indicating direction of point on screen x,y
@@ -4249,8 +4009,7 @@ BOOL LLViewerWindow::thumbnailSnapshot(LLImageRaw *raw, S32 preview_width, S32 p
 	LLHUDText::setDisplayText(FALSE) ;
 	if (type == SNAPSHOT_TYPE_OBJECT_ID)
 	{
-		gPickTransparent = FALSE;
-		gObjectList.renderObjectsForSelect(*LLViewerCamera::getInstance(), FALSE, FALSE);
+		gObjectList.renderPickList(gViewerWindow->getVirtualWindowRect(), FALSE, FALSE);
 	}
 	else
 	{
@@ -4452,6 +4211,8 @@ BOOL LLViewerWindow::rawSnapshot(LLImageRaw *raw, S32 image_width, S32 image_hei
 	F32 depth_conversion_factor_1 = (LLViewerCamera::getInstance()->getFar() + LLViewerCamera::getInstance()->getNear()) / (2.f * LLViewerCamera::getInstance()->getFar() * LLViewerCamera::getInstance()->getNear());
 	F32 depth_conversion_factor_2 = (LLViewerCamera::getInstance()->getFar() - LLViewerCamera::getInstance()->getNear()) / (2.f * LLViewerCamera::getInstance()->getFar() * LLViewerCamera::getInstance()->getNear());
 
+	gObjectList.generatePickList(*LLViewerCamera::getInstance());
+
 	for (int subimage_y = 0; subimage_y < scale_factor; ++subimage_y)
 	{
 		S32 subimage_y_offset = llclamp(buffer_y_offset - (subimage_y * window_height), 0, window_height);;
@@ -4472,9 +4233,7 @@ BOOL LLViewerWindow::rawSnapshot(LLImageRaw *raw, S32 image_width, S32 image_hei
 				LLViewerCamera::getInstance()->setZoomParameters(scale_factor, subimage_x+(subimage_y*llceil(scale_factor)));
 				setup3DRender();
 				setupViewport();
-				BOOL first_time_through = (subimage_x + subimage_y == 0);
-				gPickTransparent = FALSE;
-				gObjectList.renderObjectsForSelect(*LLViewerCamera::getInstance(), FALSE, !first_time_through);
+				gObjectList.renderPickList(gViewerWindow->getVirtualWindowRect(), FALSE, FALSE);
 			}
 			else
 			{
@@ -4723,27 +4482,6 @@ void LLViewerWindow::setup2DRender()
 	gl_state_for_2d(mWindowRect.getWidth(), mWindowRect.getHeight());
 }
 
-// Could cache the pointer from the last hitObjectOrLand here.
-LLViewerObject *LLViewerWindow::lastObjectHit()
-{
-	return gObjectList.findObject( gLastHitObjectID );
-}
-
-const LLVector3d& LLViewerWindow::lastObjectHitOffset()
-{
-	return gLastHitObjectOffset;
-}
-
-// Could cache the pointer from the last hitObjectOrLand here.
-LLViewerObject *LLViewerWindow::lastNonFloraObjectHit()
-{
-	return gObjectList.findObject( gLastHitNonFloraObjectID );
-}
-
-const LLVector3d& LLViewerWindow::lastNonFloraObjectHitOffset()
-{
-	return gLastHitNonFloraObjectOffset;
-}
 
 
 void LLViewerWindow::setShowProgress(const BOOL show)
@@ -5166,46 +4904,7 @@ F32 LLViewerWindow::getDisplayAspectRatio() const
 
 void LLViewerWindow::drawPickBuffer() const
 {
-	if (mPickBuffer)
-	{
-		gGL.color4f(1,1,1,1);
-		gGL.pushMatrix();
-		LLGLDisable no_blend(GL_BLEND);
-		LLGLDisable no_alpha_test(GL_ALPHA_TEST);
-		LLGLSNoTexture no_texture;
-		glPixelZoom(10.f, 10.f);
-		glRasterPos2f(((F32)mPickPoint.mX * mDisplayScale.mV[VX] + 10.f), 
-			((F32)mPickPoint.mY * mDisplayScale.mV[VY] + 10.f));
-		glDrawPixels(PICK_DIAMETER, PICK_DIAMETER, GL_RGBA, GL_UNSIGNED_BYTE, mPickBuffer);
-		glPixelZoom(1.f, 1.f);
-		gGL.color4fv(LLColor4::white.mV);
-		gl_rect_2d(llround((F32)mPickPoint.mX * mDisplayScale.mV[VX] - (F32)(PICK_HALF_WIDTH)), 
-			llround((F32)mPickPoint.mY * mDisplayScale.mV[VY] + (F32)(PICK_HALF_WIDTH)),
-			llround((F32)mPickPoint.mX * mDisplayScale.mV[VX] + (F32)(PICK_HALF_WIDTH)),
-			llround((F32)mPickPoint.mY * mDisplayScale.mV[VY] - (F32)(PICK_HALF_WIDTH)),
-			FALSE);
-		gl_line_2d(llround((F32)mPickPoint.mX * mDisplayScale.mV[VX] - (F32)(PICK_HALF_WIDTH)), 
-			llround((F32)mPickPoint.mY * mDisplayScale.mV[VY] + (F32)(PICK_HALF_WIDTH)),
-			llround((F32)mPickPoint.mX * mDisplayScale.mV[VX] + 10.f), 
-			llround((F32)mPickPoint.mY * mDisplayScale.mV[VY] + (F32)(PICK_DIAMETER) * 10.f + 10.f));
-		gl_line_2d(llround((F32)mPickPoint.mX * mDisplayScale.mV[VX] + (F32)(PICK_HALF_WIDTH)),
-			llround((F32)mPickPoint.mY * mDisplayScale.mV[VY] - (F32)(PICK_HALF_WIDTH)),
-			llround((F32)mPickPoint.mX * mDisplayScale.mV[VX] + (F32)(PICK_DIAMETER) * 10.f + 10.f), 
-			llround((F32)mPickPoint.mY * mDisplayScale.mV[VY] + 10.f));
-		gGL.translatef(10.f, 10.f, 0.f);
-		gl_rect_2d(llround((F32)mPickPoint.mX * mDisplayScale.mV[VX]), 
-			llround((F32)mPickPoint.mY * mDisplayScale.mV[VY] + (F32)(PICK_DIAMETER) * 10.f),
-			llround((F32)mPickPoint.mX * mDisplayScale.mV[VX] + (F32)(PICK_DIAMETER) * 10.f),
-			llround((F32)mPickPoint.mY * mDisplayScale.mV[VY]),
-			FALSE);
-		gl_rect_2d(llround((F32)mPickPoint.mX * mDisplayScale.mV[VX] + (F32)(PICK_HALF_WIDTH + mPickOffset.mX)* 10.f), 
-			llround((F32)mPickPoint.mY * mDisplayScale.mV[VY] + (F32)(PICK_HALF_WIDTH + mPickOffset.mY + 1) * 10.f),
-			llround((F32)mPickPoint.mX * mDisplayScale.mV[VX] + (F32)(PICK_HALF_WIDTH + mPickOffset.mX + 1) * 10.f),
-			llround((F32)mPickPoint.mY * mDisplayScale.mV[VY] + (F32)(PICK_HALF_WIDTH  + mPickOffset.mY) * 10.f),
-			FALSE);
-		gGL.popMatrix();
-		gGL.flush();
-	}
+	mHoverPick.drawPickBuffer();
 }
 
 void LLViewerWindow::calcDisplayScale()
@@ -5394,3 +5093,342 @@ void* LLBottomPanel::createToolBar(void* data)
 	gToolBar = new LLToolBar();
 	return gToolBar;
 }
+
+//
+// LLPickInfo
+//
+LLPickInfo::LLPickInfo()
+	: mKeyMask(MASK_NONE),
+	  mPickCallback(NULL),
+	  mPickType(PICK_INVALID),
+	  mWantSurfaceInfo(FALSE),
+	  mObjectFace(-1),
+	  mUVCoords(-1.f, -1.f),
+	  mSTCoords(-1.f, -1.f),
+	  mXYCoords(-1, -1),
+	  mIntersection(),
+	  mNormal(),
+	  mBinormal(),
+	  mHUDIcon(NULL),
+	  mPickTransparent(FALSE)
+{
+}
+
+LLPickInfo::LLPickInfo(const LLCoordGL& mouse_pos, 
+					   const LLRect& screen_region,
+						MASK keyboard_mask, 
+						BOOL pick_transparent,
+						BOOL pick_uv_coords,
+						void (*pick_callback)(const LLPickInfo& pick_info))
+	: mMousePt(mouse_pos),
+	  mScreenRegion(screen_region),
+	  mKeyMask(keyboard_mask),
+	  mPickCallback(pick_callback),
+	  mPickType(PICK_INVALID),
+	  mWantSurfaceInfo(pick_uv_coords),
+	  mObjectFace(-1),
+	  mUVCoords(-1.f, -1.f),
+	  mSTCoords(-1.f, -1.f),
+	  mXYCoords(-1, -1),
+	  mNormal(),
+	  mBinormal(),
+	  mHUDIcon(NULL),
+	  mPickTransparent(pick_transparent)
+{
+}
+
+LLPickInfo::~LLPickInfo()
+{
+}
+
+void LLPickInfo::fetchResults()
+{
+	// read back colors and depth values from buffer
+	glReadPixels(mScreenRegion.mLeft, mScreenRegion.mBottom, mScreenRegion.getWidth(), mScreenRegion.getHeight(), GL_RGBA, GL_UNSIGNED_BYTE, mPickBuffer);
+	glReadPixels(mScreenRegion.mLeft, mScreenRegion.mBottom, mScreenRegion.getWidth(), mScreenRegion.getHeight(), GL_DEPTH_COMPONENT, GL_FLOAT, mPickDepthBuffer );
+
+	// find pick region that is fully onscreen
+	LLCoordGL scaled_pick_point;;
+	scaled_pick_point.mX = llclamp(llround((F32)mMousePt.mX * gViewerWindow->getDisplayScale().mV[VX]), PICK_HALF_WIDTH, gViewerWindow->getWindowDisplayWidth() - PICK_HALF_WIDTH);
+	scaled_pick_point.mY = llclamp(llround((F32)mMousePt.mY * gViewerWindow->getDisplayScale().mV[VY]), PICK_HALF_WIDTH, gViewerWindow->getWindowDisplayHeight() - PICK_HALF_WIDTH);
+	S32 pixel_index = PICK_HALF_WIDTH * PICK_DIAMETER + PICK_HALF_WIDTH;
+	S32 pick_id = (U32)mPickBuffer[(pixel_index * 4) + 0] << 16 | (U32)mPickBuffer[(pixel_index * 4) + 1] << 8 | (U32)mPickBuffer[(pixel_index * 4) + 2];
+	F32 depth = mPickDepthBuffer[pixel_index];
+
+	S32 x_offset = mMousePt.mX - llround((F32)scaled_pick_point.mX / gViewerWindow->getDisplayScale().mV[VX]);
+	S32 y_offset = mMousePt.mY - llround((F32)scaled_pick_point.mY / gViewerWindow->getDisplayScale().mV[VY]);
+
+	mPickPt = mMousePt;
+
+	// we hit nothing, scan surrounding pixels for something useful
+	if (!pick_id)
+	{
+		S32 closest_distance = 10000;
+		//S32 closest_pick_name = 0;
+		for (S32 col = 0; col < PICK_DIAMETER; col++)
+		{
+			for (S32 row = 0; row < PICK_DIAMETER; row++)
+			{
+				S32 distance_squared = (llabs(col - x_offset - PICK_HALF_WIDTH) * llabs(col - x_offset - PICK_HALF_WIDTH)) + (llabs(row - y_offset - PICK_HALF_WIDTH) * llabs(row - y_offset - PICK_HALF_WIDTH));
+				pixel_index = row * PICK_DIAMETER + col;
+				S32 test_name = (U32)mPickBuffer[(pixel_index * 4) + 0] << 16 | (U32)mPickBuffer[(pixel_index * 4) + 1] << 8 | (U32)mPickBuffer[(pixel_index * 4) + 2];
+				if (test_name && distance_squared < closest_distance)
+				{
+					closest_distance = distance_squared;
+					pick_id = test_name;
+					depth = mPickDepthBuffer[pixel_index];
+					mPickPt.mX = mMousePt.mX + (col - PICK_HALF_WIDTH);
+					mPickPt.mY = mMousePt.mY + (row - PICK_HALF_WIDTH);
+				}
+			}
+		}
+	}
+
+	U32 te_offset = ((U32)pick_id >> 20);
+	pick_id &= 0x000fffff;
+
+	//unproject relative clicked coordinate from window coordinate using GL
+	GLint viewport[4];
+	GLdouble modelview[16];
+	GLdouble projection[16];
+	GLfloat winX, winY;
+	GLdouble posX, posY, posZ;
+
+	LLViewerObject* objectp = gObjectList.getSelectedObject(pick_id);
+
+	if (pick_id == (S32)GL_NAME_PARCEL_WALL)
+	{
+		mPickType = PICK_PARCEL_WALL;
+	}
+	else if (objectp)
+	{
+		if( objectp->getPCode() == LLViewerObject::LL_VO_SURFACE_PATCH )
+		{
+			// Hit land
+			mPickType = PICK_LAND;
+			mObjectID.setNull(); // land has no id
+
+			// put global position into land_pos
+			LLVector3d land_pos;
+			if (gViewerWindow->mousePointOnLandGlobal(mPickPt.mX, mPickPt.mY, &land_pos))
+			{
+				// Fudge the land focus a little bit above ground.
+				mPosGlobal = land_pos + LLVector3d::z_axis * 0.1f;
+			}
+		}
+		else
+		{
+			if(isFlora(objectp))
+			{
+				mPickType = PICK_FLORA;
+			}
+			else
+			{
+				mPickType = PICK_OBJECT;
+			}
+			mObjectOffset = gAgent.calcFocusOffset(objectp, mPickPt.mX, mPickPt.mY);
+			mObjectID = objectp->mID;
+			mObjectFace = (te_offset == NO_FACE) ? -1 : (S32)te_offset;
+
+			glh::matrix4f newModel((F32*)LLViewerCamera::getInstance()->getModelview().mMatrix);
+
+			for(U32 i = 0; i < 16; ++i)
+			{
+				modelview[i] = newModel.m[i];
+				projection[i] = LLViewerCamera::getInstance()->getProjection().mMatrix[i/4][i%4];
+			}
+			glGetIntegerv( GL_VIEWPORT, viewport );
+
+			winX = ((F32)mPickPt.mX) * gViewerWindow->getDisplayScale().mV[VX];
+			winY = ((F32)mPickPt.mY) * gViewerWindow->getDisplayScale().mV[VY];
+
+			gluUnProject( winX, winY, depth, modelview, projection, viewport, &posX, &posY, &posZ);
+
+			mPosGlobal = gAgent.getPosGlobalFromAgent(LLVector3(posX, posY, posZ));
+			
+			if (mWantSurfaceInfo)
+			{
+				getSurfaceInfo();
+			}
+		}
+	}
+	else
+	{
+		// was this name referring to a hud icon?
+		mHUDIcon = LLHUDIcon::handlePick(pick_id);
+		if (mHUDIcon)
+		{
+			mPickType = PICK_ICON;
+			mPosGlobal = mHUDIcon->getPositionGlobal();
+		}
+	}
+
+	if (mPickCallback)
+	{
+		mPickCallback(*this);
+	}
+}
+
+LLPointer<LLViewerObject> LLPickInfo::getObject() const
+{
+	return gObjectList.findObject( mObjectID );
+}
+
+void LLPickInfo::updateXYCoords()
+{
+	const LLTextureEntry* tep = getObject()->getTE(mObjectFace);
+	LLPointer<LLViewerImage> imagep = gImageList.getImage(tep->getID());
+	if(mUVCoords.mV[VX] >= 0.f && mUVCoords.mV[VY] >= 0.f && imagep.notNull())
+	{
+		LLCoordGL coords;
+		
+		coords.mX = llround(mUVCoords.mV[VX] * (F32)imagep->getWidth());
+		coords.mY = llround(mUVCoords.mV[VY] * (F32)imagep->getHeight());
+
+		gViewerWindow->getWindow()->convertCoords(coords, &mXYCoords);
+	}
+}
+
+void LLPickInfo::drawPickBuffer() const
+{
+	if (mPickBuffer)
+	{
+		gGL.pushMatrix();
+		LLGLDisable no_blend(GL_BLEND);
+		LLGLDisable no_alpha_test(GL_ALPHA_TEST);
+		LLGLSNoTexture no_texture;
+		glPixelZoom(10.f, 10.f);
+		LLVector2 display_scale = gViewerWindow->getDisplayScale();
+		glRasterPos2f(((F32)mMousePt.mX * display_scale.mV[VX] + 10.f), 
+			((F32)mMousePt.mY * display_scale.mV[VY] + 10.f));
+		glDrawPixels(PICK_DIAMETER, PICK_DIAMETER, GL_RGBA, GL_UNSIGNED_BYTE, mPickBuffer);
+		glPixelZoom(1.f, 1.f);
+		gGL.color4fv(LLColor4::white.mV);
+		gl_rect_2d(llround((F32)mMousePt.mX * display_scale.mV[VX] - (F32)(PICK_HALF_WIDTH)), 
+			llround((F32)mMousePt.mY * display_scale.mV[VY] + (F32)(PICK_HALF_WIDTH)),
+			llround((F32)mMousePt.mX * display_scale.mV[VX] + (F32)(PICK_HALF_WIDTH)),
+			llround((F32)mMousePt.mY * display_scale.mV[VY] - (F32)(PICK_HALF_WIDTH)),
+			FALSE);
+		gl_line_2d(llround((F32)mMousePt.mX * display_scale.mV[VX] - (F32)(PICK_HALF_WIDTH)), 
+			llround((F32)mMousePt.mY * display_scale.mV[VY] + (F32)(PICK_HALF_WIDTH)),
+			llround((F32)mMousePt.mX * display_scale.mV[VX] + 10.f), 
+			llround((F32)mMousePt.mY * display_scale.mV[VY] + (F32)(PICK_DIAMETER) * 10.f + 10.f));
+		gl_line_2d(llround((F32)mMousePt.mX * display_scale.mV[VX] + (F32)(PICK_HALF_WIDTH)),
+			llround((F32)mMousePt.mY * display_scale.mV[VY] - (F32)(PICK_HALF_WIDTH)),
+			llround((F32)mMousePt.mX * display_scale.mV[VX] + (F32)(PICK_DIAMETER) * 10.f + 10.f), 
+			llround((F32)mMousePt.mY * display_scale.mV[VY] + 10.f));
+		gGL.translatef(10.f, 10.f, 0.f);
+		gl_rect_2d(llround((F32)mPickPt.mX * display_scale.mV[VX]), 
+			llround((F32)mPickPt.mY * display_scale.mV[VY] + (F32)(PICK_DIAMETER) * 10.f),
+			llround((F32)mPickPt.mX * display_scale.mV[VX] + (F32)(PICK_DIAMETER) * 10.f),
+			llround((F32)mPickPt.mY * display_scale.mV[VY]),
+			FALSE);
+		gl_rect_2d(llround((F32)mPickPt.mX * display_scale.mV[VX]), 
+			llround((F32)mPickPt.mY * display_scale.mV[VY] + 10.f),
+			llround((F32)mPickPt.mX * display_scale.mV[VX] + 10.f),
+			llround((F32)mPickPt.mY * display_scale.mV[VY]),
+			FALSE);
+		gGL.popMatrix();
+	}
+}
+
+void LLPickInfo::getSurfaceInfo()
+{
+	// set values to uninitialized - this is what we return if no intersection is found
+	mObjectFace   = -1;
+	mUVCoords     = LLVector2(-1, -1);
+	mSTCoords     = LLVector2(-1, -1);
+	mXYCoords	  = LLCoordScreen(-1, -1);
+	mIntersection = LLVector3(0,0,0);
+	mNormal       = LLVector3(0,0,0);
+	mBinormal     = LLVector3(0,0,0);
+	
+	LLViewerObject* objectp = getObject();
+
+	if (objectp)
+	{
+		if (gViewerWindow->cursorIntersect(llround((F32)mMousePt.mX), llround((F32)mMousePt.mY), 1024.f,
+										   objectp, -1,
+										   &mObjectFace,
+										   &mIntersection,
+										   &mSTCoords,
+										   &mNormal,
+										   &mBinormal))
+		{
+			// if we succeeded with the intersect above, compute the texture coordinates:
+
+			if (objectp->mDrawable.notNull())
+			{
+				LLFace* facep = objectp->mDrawable->getFace(mObjectFace);
+
+				mUVCoords = facep->surfaceToTexture(mSTCoords, mIntersection, mNormal);
+			}
+
+			// and XY coords:
+			updateXYCoords();
+			
+		}
+	}
+}
+
+
+/* code to get UV via a special UV render - removed in lieu of raycast method
+LLVector2 LLPickInfo::pickUV()
+{
+	LLVector2 result(-1.f, -1.f);
+
+	LLViewerObject* objectp = getObject();
+	if (!objectp)
+	{
+		return result;
+	}
+
+	if (mObjectFace > -1 &&
+		objectp->mDrawable.notNull() && objectp->getPCode() == LL_PCODE_VOLUME &&
+		mObjectFace < objectp->mDrawable->getNumFaces())
+	{
+		S32 scaled_x = llround((F32)mPickPt.mX * gViewerWindow->getDisplayScale().mV[VX]);
+		S32 scaled_y = llround((F32)mPickPt.mY * gViewerWindow->getDisplayScale().mV[VY]);
+		const S32 UV_PICK_WIDTH = 5;
+		const S32 UV_PICK_HALF_WIDTH = (UV_PICK_WIDTH - 1) / 2;
+		U8 uv_pick_buffer[UV_PICK_WIDTH * UV_PICK_WIDTH * 4];
+		LLFace* facep = objectp->mDrawable->getFace(mObjectFace);
+		if (facep)
+		{
+			LLGLState scissor_state(GL_SCISSOR_TEST);
+			scissor_state.enable();
+			LLViewerCamera::getInstance()->setPerspective(FOR_SELECTION, scaled_x - UV_PICK_HALF_WIDTH, scaled_y - UV_PICK_HALF_WIDTH, UV_PICK_WIDTH, UV_PICK_WIDTH, FALSE);
+			//glViewport(scaled_x - UV_PICK_HALF_WIDTH, scaled_y - UV_PICK_HALF_WIDTH, UV_PICK_WIDTH, UV_PICK_WIDTH);
+			glScissor(scaled_x - UV_PICK_HALF_WIDTH, scaled_y - UV_PICK_HALF_WIDTH, UV_PICK_WIDTH, UV_PICK_WIDTH);
+
+			glClear(GL_DEPTH_BUFFER_BIT);
+
+			facep->renderSelectedUV();
+
+			glReadPixels(scaled_x - UV_PICK_HALF_WIDTH, scaled_y - UV_PICK_HALF_WIDTH, UV_PICK_WIDTH, UV_PICK_WIDTH, GL_RGBA, GL_UNSIGNED_BYTE, uv_pick_buffer);
+			U8* center_pixel = &uv_pick_buffer[4 * ((UV_PICK_WIDTH * UV_PICK_HALF_WIDTH) + UV_PICK_HALF_WIDTH + 1)];
+
+			result.mV[VX] = (F32)((center_pixel[VGREEN] & 0xf) + (16.f * center_pixel[VRED])) / 4095.f;
+			result.mV[VY] = (F32)((center_pixel[VGREEN] >> 4) + (16.f * center_pixel[VBLUE])) / 4095.f;
+		}
+	}
+
+	return result;
+} */
+
+
+//static 
+bool LLPickInfo::isFlora(LLViewerObject* object)
+{
+	if (!object) return false;
+
+	LLPCode pcode = object->getPCode();
+
+	if( (LL_PCODE_LEGACY_GRASS == pcode) 
+		|| (LL_PCODE_LEGACY_TREE == pcode) 
+		|| (LL_PCODE_TREE_NEW == pcode))
+	{
+		return true;
+	}
+	return false;
+}
diff --git a/indra/newview/llviewerwindow.h b/indra/newview/llviewerwindow.h
index 5c0eae61be61c06c8ff6c07554379ed8ecb48bd2..c7d02cb720b0f25cc8a0bb95a08a67a8849bcf8d 100644
--- a/indra/newview/llviewerwindow.h
+++ b/indra/newview/llviewerwindow.h
@@ -60,6 +60,70 @@ class LLTextBox;
 class LLImageRaw;
 class LLHUDIcon;
 
+#define PICK_HALF_WIDTH 5
+#define PICK_DIAMETER (2 * PICK_HALF_WIDTH + 1)
+
+class LLPickInfo
+{
+public:
+	LLPickInfo();
+	LLPickInfo(const LLCoordGL& mouse_pos, 
+		const LLRect& screen_region,
+		MASK keyboard_mask, 
+		BOOL pick_transparent, 
+		BOOL pick_surface_info,
+		void (*pick_callback)(const LLPickInfo& pick_info));
+	~LLPickInfo();
+
+	void fetchResults();
+	LLPointer<LLViewerObject> getObject() const;
+	LLUUID getObjectID() const { return mObjectID; }
+	void drawPickBuffer() const;
+
+	static bool isFlora(LLViewerObject* object);
+
+	typedef enum e_pick_type
+	{
+		PICK_OBJECT,
+		PICK_FLORA,
+		PICK_LAND,
+		PICK_ICON,
+		PICK_PARCEL_WALL,
+		PICK_INVALID
+	} EPickType;
+
+public:
+	LLCoordGL		mMousePt;
+	MASK			mKeyMask;
+	void			(*mPickCallback)(const LLPickInfo& pick_info);
+
+	EPickType		mPickType;
+	LLCoordGL		mPickPt;
+	LLVector3d		mPosGlobal;
+	LLVector3		mObjectOffset;
+	LLUUID			mObjectID;
+	S32				mObjectFace;
+	LLHUDIcon*		mHUDIcon;
+	LLVector3       mIntersection;
+	LLVector2		mUVCoords;
+	LLVector2       mSTCoords;
+	LLCoordScreen	mXYCoords;
+	LLVector3		mNormal;
+	LLVector3		mBinormal;
+	BOOL			mPickTransparent;
+	LLRect			mScreenRegion;
+	void		    getSurfaceInfo();
+
+private:
+	void			updateXYCoords();
+
+	BOOL			mWantSurfaceInfo;   // do we populate mUVCoord, mNormal, mBinormal?
+	U8				mPickBuffer[PICK_DIAMETER * PICK_DIAMETER * 4];
+	F32				mPickDepthBuffer[PICK_DIAMETER * PICK_DIAMETER];
+	BOOL			mPickParcelWall;
+
+};
+
 #define MAX_IMAGE_SIZE 6144 //6 * 1024, max snapshot image size 6144 * 6144
 
 class LLViewerWindow : public LLWindowCallbacks
@@ -143,6 +207,9 @@ class LLViewerWindow : public LLWindowCallbacks
 	BOOL			getLeftMouseDown()	const	{ return mLeftMouseDown; }
 	BOOL			getRightMouseDown()	const	{ return mRightMouseDown; }
 
+	const LLPickInfo&	getLastPick() const { return mLastPick; }
+	const LLPickInfo&	getHoverPick() const { return mHoverPick; }
+
 	LLUICtrl*		getTopCtrl() const;
 	BOOL			hasTopCtrl(LLView* view) const;
 
@@ -150,10 +217,10 @@ class LLViewerWindow : public LLWindowCallbacks
 	void			setup3DRender();
 	void			setup2DRender();
 
-	BOOL			isPickPending()				{ return mPickPending; }
-
 	LLVector3		mouseDirectionGlobal(const S32 x, const S32 y) const;
 	LLVector3		mouseDirectionCamera(const S32 x, const S32 y) const;
+	LLVector3       mousePointHUD(const S32 x, const S32 y) const;
+		
 
 	// Is window of our application frontmost?
 	BOOL			getActive() const			{ return mActive; }
@@ -244,20 +311,27 @@ class LLViewerWindow : public LLWindowCallbacks
 	void			renderSelections( BOOL for_gl_pick, BOOL pick_parcel_walls, BOOL for_hud );
 	void			performPick();
 
-	void			hitObjectOrLandGlobalAsync(S32 x, S32 y_from_bot, MASK mask, void (*callback)(S32 x, S32 y, MASK mask), BOOL pick_transparent = FALSE, BOOL pick_parcel_walls = FALSE);
-	void			hitObjectOrLandGlobalImmediate(S32 x, S32 y, void (*callback)(S32 x, S32 y, MASK mask), BOOL pick_transparent);
+	void			pickAsync(S32 x, S32 y_from_bot, MASK mask, void (*callback)(const LLPickInfo& pick_info),
+							  BOOL pick_transparent = FALSE, BOOL get_surface_info = FALSE);
+	LLPickInfo		pickImmediate(S32 x, S32 y, BOOL pick_transparent);
+	static void     hoverPickCallback(const LLPickInfo& pick_info);
+	
+	LLViewerObject* cursorIntersect(S32 mouse_x = -1, S32 mouse_y = -1, F32 depth = 512.f,
+									LLViewerObject *this_object = NULL,
+									S32 this_face = -1,
+									S32* face_hit = NULL,
+									LLVector3 *intersection = NULL,
+									LLVector2 *uv = NULL,
+									LLVector3 *normal = NULL,
+									LLVector3 *binormal = NULL);
 	
-	void			hitUIElementAsync(S32 x, S32 y_from_bot, MASK mask, void (*callback)(S32 x, S32 y, MASK mask));
-	void			hitUIElementImmediate(S32 x, S32 y, void (*callback)(S32 x, S32 y, MASK mask));
-
-	LLViewerObject*	getObjectUnderCursor(const F32 depth = 16.0f);
 	
 	// Returns a pointer to the last object hit
-	LLViewerObject	*lastObjectHit();
-	LLViewerObject  *lastNonFloraObjectHit();
+	//LLViewerObject	*getObject();
+	//LLViewerObject  *lastNonFloraObjectHit();
 
-	const LLVector3d& lastObjectHitOffset();
-	const LLVector3d& lastNonFloraObjectHitOffset();
+	//const LLVector3d& getObjectOffset();
+	//const LLVector3d& lastNonFloraObjectHitOffset();
 
 	// mousePointOnLand() returns true if found point
 	BOOL			mousePointOnLandGlobal(const S32 x, const S32 y, LLVector3d *land_pos_global);
@@ -306,19 +380,7 @@ class LLViewerWindow : public LLWindowCallbacks
 	void			stopGL(BOOL save_state = TRUE);
 	void			restoreGL(const std::string& progress_message = LLStringUtil::null);
 	void			initFonts(F32 zoom_factor = 1.f);
-	
-	void			analyzeHit(
-						S32				x,				// input
-						S32				y_from_bot,		// input
-						LLViewerObject* objectp,		// input
-						U32				te_offset,		// input
-						LLUUID*			hit_object_id_p,// output
-						S32*			hit_face_p,		// output
-						LLVector3d*		hit_pos_p,		// output
-						BOOL*			hit_land,		// output
-						F32*			hit_u_coord,	// output
-						F32*			hit_v_coord);	// output
-
+	void			schedulePick(LLPickInfo& pick_info);
 	
 public:
 	LLWindow*		mWindow;						// graphical window object
@@ -354,16 +416,14 @@ class LLViewerWindow : public LLWindowCallbacks
 	BOOL			mSuppressToolbox;	// sometimes hide the toolbox, despite
 										// having a camera tool selected
 	BOOL			mHideCursorPermanent;	// true during drags, mouselook
-	LLCoordGL		mPickPoint;
-	LLCoordGL		mPickOffset;
-	MASK			mPickMask;
-	BOOL			mPickPending;
-	void			(*mPickCallback)(S32 x, S32 y, MASK mask);
+	LLPickInfo		mLastPick;
+	LLPickInfo		mHoverPick;
+	std::vector<LLPickInfo> mPicks;
+	LLRect			mPickScreenRegion; // area of frame buffer for rendering pick frames (generally follows mouse to avoid going offscreen)
 
 	std::string		mOverlayTitle;		// Used for special titles such as "Second Life - Special E3 2003 Beta"
 
 	BOOL			mIgnoreActivate;
-	U8*				mPickBuffer;
 
 	std::string		mInitAlert;			// Window / GL initialization requires an alert
 	
@@ -398,9 +458,7 @@ void toggle_first_person();
 void toggle_build(void*);
 void reset_viewer_state_on_sim(void);
 void update_saved_window_size(const std::string& control,S32 delta_width, S32 delta_height);
-//
-// Constants
-//
+
 
 
 //
@@ -414,28 +472,15 @@ extern LLFrameTimer		gMouseIdleTimer;		// how long has it been since the mouse l
 extern LLFrameTimer		gAwayTimer;				// tracks time before setting the avatar away state to true
 extern LLFrameTimer		gAwayTriggerTimer;		// how long the avatar has been away
 
-extern LLVector3d		gLastHitPosGlobal;
-extern LLVector3d		gLastHitObjectOffset;
-extern LLUUID			gLastHitObjectID;
-extern S32				gLastHitObjectFace;
-extern BOOL				gLastHitLand;
-extern F32				gLastHitUCoord;
-extern F32				gLastHitVCoord;
-
-
-extern LLVector3d		gLastHitNonFloraPosGlobal;
-extern LLVector3d		gLastHitNonFloraObjectOffset;
-extern LLUUID			gLastHitNonFloraObjectID;
-extern S32				gLastHitNonFloraObjectFace;
-
-extern S32				gLastHitUIElement;
-extern LLHUDIcon*		gLastHitHUDIcon;
-extern BOOL				gLastHitParcelWall;
 extern BOOL				gDebugSelect;
-extern BOOL				gPickFaces;
-extern BOOL				gPickTransparent;
 
 extern BOOL				gDebugFastUIRender;
+extern LLViewerObject*  gDebugRaycastObject;
+extern LLVector3        gDebugRaycastIntersection;
+extern LLVector2        gDebugRaycastTexCoord;
+extern LLVector3        gDebugRaycastNormal;
+extern LLVector3        gDebugRaycastBinormal;
+
 extern S32 CHAT_BAR_HEIGHT; 
 
 extern BOOL			gDisplayCameraPos;
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index 287f06375720a4b863cc99a564eb4c98c2327710..47090d5c65cc4d39ca0672500c0a4406f91a9458 100644
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -622,8 +622,6 @@ LLVOAvatar::LLVOAvatar(
 	LLViewerRegion* regionp)
 	:
 	LLViewerObject(id, pcode, regionp),
-	mHUDTargetZoom(1.f),
-	mHUDCurZoom(1.f),
 	mLastHeadBakedID( IMG_DEFAULT_AVATAR ),
 	mLastUpperBodyBakedID( IMG_DEFAULT_AVATAR ),
 	mLastLowerBodyBakedID( IMG_DEFAULT_AVATAR ),
@@ -3250,7 +3248,8 @@ void LLVOAvatar::idleUpdateTractorBeam()
 			}
 			else
 			{
-				mBeam->setPositionGlobal(gLastHitNonFloraPosGlobal + gLastHitNonFloraObjectOffset);
+				const LLPickInfo& pick = gViewerWindow->getLastPick();
+				mBeam->setPositionGlobal(pick.mPosGlobal);
 			}
 
 		}
diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h
index 54fd7d370f7751cc86dddb6aab0ced5590c10c0c..ff846c8d833ce490ad8f84e56c8e6790569dd5b8 100644
--- a/indra/newview/llvoavatar.h
+++ b/indra/newview/llvoavatar.h
@@ -636,8 +636,6 @@ class LLVOAvatar :
 	// special purpose joint for HUD attachments
 	//--------------------------------------------------------------------
 	LLViewerJoint *mScreenp;
-	F32				mHUDTargetZoom;
-	F32				mHUDCurZoom;
 
 	//--------------------------------------------------------------------
 	// mesh objects for skinned avatar
diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp
index b84f8d8e4b4d57a4967de60efe554c4dc239a11f..1e1f8be859bbffc319c3980c74c07f156b0e1656 100644
--- a/indra/newview/llvovolume.cpp
+++ b/indra/newview/llvovolume.cpp
@@ -1893,26 +1893,42 @@ LLVector3 LLVOVolume::agentPositionToVolume(const LLVector3& pos) const
 
 LLVector3 LLVOVolume::agentDirectionToVolume(const LLVector3& dir) const
 {
-	return dir * ~getRenderRotation();
+	LLVector3 ret = dir * ~getRenderRotation();
+	
+	LLVector3 objScale = isVolumeGlobal() ? LLVector3(1,1,1) : getScale();
+	ret.scaleVec(objScale);
+
+	return ret;
 }
 
 LLVector3 LLVOVolume::volumePositionToAgent(const LLVector3& dir) const
 {
 	LLVector3 ret = dir;
-	ret.scaleVec(getScale());
+	LLVector3 objScale = isVolumeGlobal() ? LLVector3(1,1,1) : getScale();
+	ret.scaleVec(objScale);
 	ret = ret * getRenderRotation();
 	ret += getRenderPosition();
 	
 	return ret;
 }
 
-BOOL LLVOVolume::lineSegmentIntersect(const LLVector3& start, LLVector3& end) const
+LLVector3 LLVOVolume::volumeDirectionToAgent(const LLVector3& dir) const
 {
-	return FALSE;
+	LLVector3 ret = dir;
+	LLVector3 objScale = isVolumeGlobal() ? LLVector3(1,1,1) : getScale();
+	LLVector3 invObjScale(1.f / objScale.mV[VX], 1.f / objScale.mV[VY], 1.f / objScale.mV[VZ]);
+	ret.scaleVec(invObjScale);
+	ret = ret * getRenderRotation();
+
+	return ret;
+}
+
+
+BOOL LLVOVolume::lineSegmentIntersect(const LLVector3& start, const LLVector3& end, S32 face, S32 *face_hitp,
+									  LLVector3* intersection,LLVector2* tex_coord, LLVector3* normal, LLVector3* bi_normal)
 	
-#if 0 // needs to be rewritten to use face extents instead of volume bounds
+{
 	LLVolume* volume = getVolume();
-	BOOL ret = FALSE;
 	if (volume)
 	{	
 		LLVector3 v_start, v_end, v_dir;
@@ -1920,17 +1936,38 @@ BOOL LLVOVolume::lineSegmentIntersect(const LLVector3& start, LLVector3& end) co
 		v_start = agentPositionToVolume(start);
 		v_end = agentPositionToVolume(end);
 		
-		if (LLLineSegmentAABB(v_start, v_end, volume->mBounds[0], volume->mBounds[1]))
+		S32 face_hit = volume->lineSegmentIntersect(v_start, v_end, face,
+													intersection, tex_coord, normal, bi_normal);
+		if (face_hit >= 0)
 		{
-			if (volume->lineSegmentIntersect(v_start, v_end) >= 0)
+			if (face_hitp != NULL)
+			{
+				*face_hitp = face_hit;
+			}
+			
+			if (intersection != NULL)
+			{
+				*intersection = volumePositionToAgent(*intersection);  // must map back to agent space
+			}
+
+			if (normal != NULL)
+			{
+				*normal = volumeDirectionToAgent(*normal);
+				(*normal).normVec();
+			}
+
+			if (bi_normal != NULL)
 			{
-				end = volumePositionToAgent(v_end);
-				ret = TRUE;
+				*bi_normal = volumeDirectionToAgent(*bi_normal);
+				(*bi_normal).normVec();
 			}
+
+			
+			return TRUE;
 		}
 	}
-	return ret;
-#endif
+	
+	return FALSE;
 }
 
 U32 LLVOVolume::getPartitionType() const
diff --git a/indra/newview/llvovolume.h b/indra/newview/llvovolume.h
index d7b72f7a18a79eb04503ff2e6d31ae171f2bdb6e..262d4ecc8d6a0a55722eb0a2ccdbada39935be18 100644
--- a/indra/newview/llvovolume.h
+++ b/indra/newview/llvovolume.h
@@ -111,10 +111,20 @@ class LLVOVolume : public LLViewerObject
 	const LLMatrix3&	getRelativeXformInvTrans() const		{ return mRelativeXformInvTrans; }
 	/*virtual*/	const LLMatrix4	getRenderMatrix() const;
 
-	/*virtual*/ BOOL	lineSegmentIntersect(const LLVector3& start, LLVector3& end) const;
+
+	/*virtual*/ BOOL lineSegmentIntersect(const LLVector3& start, const LLVector3& end, 
+										  S32 face = -1,                        // which face to check, -1 = ALL_SIDES
+										  S32* face_hit = NULL,                 // which face was hit
+										  LLVector3* intersection = NULL,       // return the intersection point
+										  LLVector2* tex_coord = NULL,          // return the texture coordinates of the intersection point
+										  LLVector3* normal = NULL,             // return the surface normal at the intersection point
+										  LLVector3* bi_normal = NULL           // return the surface bi-normal at the intersection point
+		);
+	
 				LLVector3 agentPositionToVolume(const LLVector3& pos) const;
 				LLVector3 agentDirectionToVolume(const LLVector3& dir) const;
 				LLVector3 volumePositionToAgent(const LLVector3& dir) const;
+				LLVector3 volumeDirectionToAgent(const LLVector3& dir) const;
 
 				
 				BOOL	getVolumeChanged() const				{ return mVolumeChanged; }
diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp
index f9a18bf1925e1901abd0b54ced845cea7e3a5c6c..c24d1b882a715ab535fd0653ca741885ce9fd85f 100644
--- a/indra/newview/pipeline.cpp
+++ b/indra/newview/pipeline.cpp
@@ -2188,6 +2188,7 @@ void LLPipeline::renderHighlights()
 
 	// Draw 3D UI elements here (before we clear the Z buffer in POOL_HUD)
 	// Render highlighted faces.
+	LLGLSPipelineAlpha gls_pipeline_alpha;
 	LLColor4 color(1.f, 1.f, 1.f, 0.5f);
 	LLGLEnable color_mat(GL_COLOR_MATERIAL);
 	disableLights();
@@ -2341,7 +2342,7 @@ void LLPipeline::renderGeom(LLCamera& camera, BOOL forceVBOUpdate)
 
 	if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_PICKING))
 	{
-		gObjectList.renderObjectsForSelect(camera);
+		gObjectList.renderObjectsForSelect(camera, gViewerWindow->getVirtualWindowRect());
 	}
 	else if (gSavedSettings.getBOOL("RenderDeferred"))
 	{
@@ -2591,7 +2592,7 @@ void LLPipeline::renderDebug()
 	gGL.flush();
 }
 
-void LLPipeline::renderForSelect(std::set<LLViewerObject*>& objects)
+void LLPipeline::renderForSelect(std::set<LLViewerObject*>& objects, BOOL render_transparent, const LLRect& screen_rect)
 {
 	assertInitialized();
 
@@ -2644,7 +2645,7 @@ void LLPipeline::renderForSelect(std::set<LLViewerObject*>& objects)
 	}	
 
 	LLGLEnable alpha_test(GL_ALPHA_TEST);
-	if (gPickTransparent)
+	if (render_transparent)
 	{
 		gGL.setAlphaRejectSettings(LLRender::CF_GREATER_EQUAL, 0.f);
 	}
@@ -2689,14 +2690,7 @@ void LLPipeline::renderForSelect(std::set<LLViewerObject*>& objects)
 		glh::matrix4f save_proj(glh_get_current_projection());
 		glh::matrix4f save_model(glh_get_current_modelview());
 
-		U32 viewport[4];
-
-		for (U32 i = 0; i < 4; i++)
-		{
-			viewport[i] = gGLViewport[i];
-		}
-		
-		setup_hud_matrices(TRUE);
+		setup_hud_matrices(screen_rect);
 		for (LLVOAvatar::attachment_map_t::iterator iter = avatarp->mAttachmentPoints.begin(); 
 			 iter != avatarp->mAttachmentPoints.end(); )
 		{
@@ -2748,11 +2742,6 @@ void LLPipeline::renderForSelect(std::set<LLViewerObject*>& objects)
 		glh_set_current_modelview(save_model);
 
 	
-		for (U32 i = 0; i < 4; i++)
-		{
-			gGLViewport[i] = viewport[i];
-		}
-		glViewport(gGLViewport[0], gGLViewport[1], gGLViewport[2], gGLViewport[3]);
 	}
 
 	gGL.getTexUnit(0)->setTextureBlendType(LLTexUnit::TB_MULT);
@@ -2762,11 +2751,6 @@ void LLPipeline::renderForSelect(std::set<LLViewerObject*>& objects)
 	gGL.setColorMask(true, true);
 }
 
-void LLPipeline::renderFaceForUVSelect(LLFace* facep)
-{
-	if (facep) facep->renderSelectedUV();
-}
-
 void LLPipeline::rebuildPools()
 {
 	LLMemType mt(LLMemType::MTYPE_PIPELINE);
@@ -3959,7 +3943,13 @@ BOOL LLPipeline::getProcessBeacons(void* data)
 	return sRenderProcessBeacons;
 }
 
-LLViewerObject* LLPipeline::pickObject(const LLVector3 &start, const LLVector3 &end, LLVector3 &collision)
+LLViewerObject* LLPipeline::lineSegmentIntersectInWorld(const LLVector3& start, const LLVector3& end,
+														S32* face_hit,
+														LLVector3* intersection,         // return the intersection point
+														LLVector2* tex_coord,            // return the texture coordinates of the intersection point
+														LLVector3* normal,               // return the surface normal at the intersection point
+														LLVector3* bi_normal             // return the surface bi-normal at the intersection point
+	)
 {
 	LLDrawable* drawable = NULL;
 
@@ -3967,10 +3957,45 @@ LLViewerObject* LLPipeline::pickObject(const LLVector3 &start, const LLVector3 &
 			iter != LLWorld::getInstance()->getRegionList().end(); ++iter)
 	{
 		LLViewerRegion* region = *iter;
-		LLSpatialPartition* part = region->getSpatialPartition(LLViewerRegion::PARTITION_VOLUME);
+
+		for (U32 j = 0; j < LLViewerRegion::NUM_PARTITIONS; j++)
+		{
+			if ((j == LLViewerRegion::PARTITION_VOLUME) || (j == LLViewerRegion::PARTITION_BRIDGE))  // only check these partitions for now
+			{
+				LLSpatialPartition* part = region->getSpatialPartition(j);
+				if (part)
+				{
+					LLDrawable* hit = part->lineSegmentIntersect(start, end, face_hit, intersection, tex_coord, normal, bi_normal);
+					if (hit)
+					{
+						drawable = hit;
+					}
+				}
+			}
+		}
+	}
+	return drawable ? drawable->getVObj().get() : NULL;
+}
+
+LLViewerObject* LLPipeline::lineSegmentIntersectInHUD(const LLVector3& start, const LLVector3& end,
+													  S32* face_hit,
+													  LLVector3* intersection,         // return the intersection point
+													  LLVector2* tex_coord,            // return the texture coordinates of the intersection point
+													  LLVector3* normal,               // return the surface normal at the intersection point
+													  LLVector3* bi_normal             // return the surface bi-normal at the intersection point
+	)
+{
+	LLDrawable* drawable = NULL;
+
+	for (LLWorld::region_list_t::iterator iter = LLWorld::getInstance()->getRegionList().begin(); 
+			iter != LLWorld::getInstance()->getRegionList().end(); ++iter)
+	{
+		LLViewerRegion* region = *iter;
+
+		LLSpatialPartition* part = region->getSpatialPartition(LLViewerRegion::PARTITION_HUD);
 		if (part)
 		{
-			LLDrawable* hit = part->pickDrawable(start, end, collision);
+			LLDrawable* hit = part->lineSegmentIntersect(start, end, face_hit, intersection, tex_coord, normal, bi_normal);
 			if (hit)
 			{
 				drawable = hit;
diff --git a/indra/newview/pipeline.h b/indra/newview/pipeline.h
index e98be79120d4c6d185a901b55ce6f05eb804135e..ac2c32fedd3f676e6da002d2dc5ea3e8326a7282 100644
--- a/indra/newview/pipeline.h
+++ b/indra/newview/pipeline.h
@@ -65,8 +65,8 @@ typedef enum e_avatar_skinning_method
 
 BOOL compute_min_max(LLMatrix4& box, LLVector2& min, LLVector2& max); // Shouldn't be defined here!
 bool LLRayAABB(const LLVector3 &center, const LLVector3 &size, const LLVector3& origin, const LLVector3& dir, LLVector3 &coord, F32 epsilon = 0);
-BOOL LLLineSegmentAABB(const LLVector3& start, const LLVector3& end, const LLVector3& center, const LLVector3& size);
-BOOL setup_hud_matrices(BOOL for_select);
+BOOL setup_hud_matrices(); // use whole screen to render hud
+BOOL setup_hud_matrices(const LLRect& screen_region); // specify portion of screen (in pixels) to render hud attachments from (for picking)
 glh::matrix4f glh_copy_matrix(GLdouble* src);
 glh::matrix4f glh_get_current_modelview();
 void glh_set_current_modelview(const glh::matrix4f& mat);
@@ -129,8 +129,21 @@ class LLPipeline
 	void        markTextured(LLDrawable *drawablep);
 	void        markRebuild(LLDrawable *drawablep, LLDrawable::EDrawableFlags flag = LLDrawable::REBUILD_ALL, BOOL priority = FALSE);
 		
-	//get the object between start and end that's closest to start.  Return the point of collision in collision.
-	LLViewerObject* pickObject(const LLVector3 &start, const LLVector3 &end, LLVector3 &collision);
+	//get the object between start and end that's closest to start.
+	LLViewerObject* lineSegmentIntersectInWorld(const LLVector3& start, const LLVector3& end,
+												S32* face_hit,                          // return the face hit
+												LLVector3* intersection = NULL,         // return the intersection point
+												LLVector2* tex_coord = NULL,            // return the texture coordinates of the intersection point
+												LLVector3* normal = NULL,               // return the surface normal at the intersection point
+												LLVector3* bi_normal = NULL             // return the surface bi-normal at the intersection point  
+		);
+	LLViewerObject* lineSegmentIntersectInHUD(const LLVector3& start, const LLVector3& end,
+											  S32* face_hit,                          // return the face hit
+											  LLVector3* intersection = NULL,         // return the intersection point
+											  LLVector2* tex_coord = NULL,            // return the texture coordinates of the intersection point
+											  LLVector3* normal = NULL,               // return the surface normal at the intersection point
+											  LLVector3* bi_normal = NULL             // return the surface bi-normal at the intersection point
+		);
 
 	// Something about these textures has changed.  Dirty them.
 	void        dirtyPoolObjectTextures(const std::set<LLViewerImage*>& textures);
@@ -185,8 +198,7 @@ class LLPipeline
 	void renderHighlights();
 	void renderDebug();
 
-	void renderForSelect(std::set<LLViewerObject*>& objects);
-	void renderFaceForUVSelect(LLFace* facep);
+	void renderForSelect(std::set<LLViewerObject*>& objects, BOOL render_transparent, const LLRect& screen_rect);
 	void rebuildPools(); // Rebuild pools
 
 	void findReferences(LLDrawable *drawablep);	// Find the lists which have references to this object
@@ -329,7 +341,8 @@ class LLPipeline
 		RENDER_DEBUG_TEXTURE_ANIM		= 0x080000,
 		RENDER_DEBUG_LIGHTS				= 0x100000,
 		RENDER_DEBUG_BATCH_SIZE			= 0x200000,
-		RENDER_DEBUG_SHAME				= 0x400000,
+		RENDER_DEBUG_RAYCAST            = 0x400000,
+		RENDER_DEBUG_SHAME				= 0x800000
 	};
 
 public:
diff --git a/scripts/messages/message_template.msg b/scripts/messages/message_template.msg
index 776128323954dd7f4583ac0bc8714cb2243820af..6f49ef25b5ea9199d8304639f1306180d504d8b1 100644
--- a/scripts/messages/message_template.msg
+++ b/scripts/messages/message_template.msg
@@ -2436,6 +2436,15 @@ version 2.0
 		{	LocalID				U32  }
 		{	GrabOffset			LLVector3 }
 	}
+	{
+		SurfaceInfo     Variable
+		{   UVCoord     LLVector3 }
+		{   STCoord     LLVector3 }
+       	{   FaceIndex   S32 }
+       	{   Position    LLVector3 }
+       	{   Normal      LLVector3 }
+       	{   Binormal    LLVector3 }
+	}
 }
 
 
@@ -2457,6 +2466,16 @@ version 2.0
 		{	GrabPosition		LLVector3	}	// LLVector3, region local
 		{	TimeSinceLast		U32	}
 	}
+	{
+		SurfaceInfo     Variable
+		{   UVCoord     LLVector3 }
+		{   STCoord     LLVector3 }
+       	{   FaceIndex   S32 }
+       	{   Position    LLVector3 }
+       	{   Normal      LLVector3 }
+       	{   Binormal    LLVector3 }
+	}
+
 }
 
 
@@ -2472,6 +2491,15 @@ version 2.0
 		ObjectData			Single
 		{	LocalID				U32  }
 	}
+	{
+		SurfaceInfo     Variable
+		{   UVCoord     LLVector3 }
+		{   STCoord     LLVector3 }
+       	{   FaceIndex   S32 }
+       	{   Position    LLVector3 }
+       	{   Normal      LLVector3 }
+       	{   Binormal    LLVector3 }
+	}
 }