From c3f14b915c38a4978745f12f1f816572cce4b5a0 Mon Sep 17 00:00:00 2001
From: Dave Parks <davep@lindenlab.com>
Date: Mon, 3 Jun 2013 12:50:48 -0500
Subject: [PATCH] NORSPEC-229 Fix for bad binormals on mirrored surfaces (use
 tangent calculator instead of binormal calculator, convert binormal centric
 code to tangent centric)

---
 doc/contributions.txt                         |   1 +
 indra/llmath/llvolume.cpp                     | 327 ++++++++++--------
 indra/llmath/llvolume.h                       |  36 +-
 indra/llmath/llvolumeoctree.cpp               |  66 ++--
 indra/llmath/llvolumeoctree.h                 |   8 +-
 indra/llrender/llshadermgr.cpp                |   2 +-
 indra/llrender/llvertexbuffer.cpp             |  38 +-
 indra/llrender/llvertexbuffer.h               |   7 +-
 .../shaders/class1/deferred/bumpSkinnedV.glsl |   6 +-
 .../shaders/class1/deferred/bumpV.glsl        |   6 +-
 .../shaders/class1/deferred/materialF.glsl    |   2 +-
 .../shaders/class1/deferred/materialV.glsl    |  11 +-
 .../shaders/class1/transform/binormalV.glsl   |   6 +-
 indra/newview/lldrawpoolalpha.cpp             |   2 +-
 indra/newview/lldrawpoolavatar.h              |   6 +-
 indra/newview/lldrawpoolbump.cpp              |   2 +-
 indra/newview/lldrawpoolmaterials.h           |   2 +-
 indra/newview/llface.cpp                      | 147 +++-----
 indra/newview/llface.h                        |   2 +-
 indra/newview/llhudicon.cpp                   |  21 +-
 indra/newview/llhudicon.h                     |   4 +-
 indra/newview/llhudnametag.cpp                |  18 +-
 indra/newview/llhudnametag.h                  |   2 +-
 indra/newview/llspatialpartition.cpp          |  90 ++---
 indra/newview/llspatialpartition.h            |  10 +-
 indra/newview/llviewerobject.cpp              |  16 +-
 indra/newview/llviewerobject.h                |  10 +-
 indra/newview/llviewershadermgr.cpp           |  18 +-
 indra/newview/llviewershadermgr.h             |   2 +-
 indra/newview/llviewerwindow.cpp              | 134 ++++---
 indra/newview/llviewerwindow.h                |  23 +-
 indra/newview/llvoavatar.cpp                  |  37 +-
 indra/newview/llvoavatar.h                    |  16 +-
 indra/newview/llvograss.cpp                   |  28 +-
 indra/newview/llvograss.h                     |   8 +-
 indra/newview/llvopartgroup.cpp               |   2 +-
 indra/newview/llvosurfacepatch.cpp            |  20 +-
 indra/newview/llvosurfacepatch.h              |   8 +-
 indra/newview/llvotree.cpp                    |  13 +-
 indra/newview/llvotree.h                      |   8 +-
 indra/newview/llvovolume.cpp                  |  86 +++--
 indra/newview/llvovolume.h                    |   8 +-
 indra/newview/pipeline.cpp                    |  52 +--
 indra/newview/pipeline.h                      |  16 +-
 44 files changed, 718 insertions(+), 609 deletions(-)

diff --git a/doc/contributions.txt b/doc/contributions.txt
index b942bc4c19..66ccb404a8 100755
--- a/doc/contributions.txt
+++ b/doc/contributions.txt
@@ -404,6 +404,7 @@ Ganymedes Costagravas
 Geenz Spad
 	STORM-1823
 	STORM-1900
+	NORSPEC-229
 Gene Frostbite
 GeneJ Composer
 Geneko Nemeth
diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp
index 3f06e6b99e..bf03c971cd 100755
--- a/indra/llmath/llvolume.cpp
+++ b/indra/llmath/llvolume.cpp
@@ -2079,9 +2079,9 @@ void LLVolume::regen()
 	createVolumeFaces();
 }
 
-void LLVolume::genBinormals(S32 face)
+void LLVolume::genTangents(S32 face)
 {
-	mVolumeFaces[face].createBinormals();
+	mVolumeFaces[face].createTangents();
 }
 
 LLVolume::~LLVolume()
@@ -4392,7 +4392,7 @@ void LLVolume::generateSilhouetteVertices(std::vector<LLVector3> &vertices,
 				segments.push_back(vertices.size());
 #if DEBUG_SILHOUETTE_BINORMALS
 				vertices.push_back(face.mVertices[j].getPosition());
-				vertices.push_back(face.mVertices[j].getPosition() + face.mVertices[j].mBinormal*0.1f);
+				vertices.push_back(face.mVertices[j].getPosition() + face.mVertices[j].mTangent*0.1f);
 				normals.push_back(LLVector3(0,0,1));
 				normals.push_back(LLVector3(0,0,1));
 				segments.push_back(vertices.size());
@@ -4508,22 +4508,9 @@ void LLVolume::generateSilhouetteVertices(std::vector<LLVector3> &vertices,
 	}
 }
 
-S32 LLVolume::lineSegmentIntersect(const LLVector3& start, const LLVector3& end, 
-								   S32 face,
-								   LLVector3* intersection,LLVector2* tex_coord, LLVector3* normal, LLVector3* bi_normal)
-{
-	LLVector4a starta, enda;
-	starta.load3(start.mV);
-	enda.load3(end.mV);
-
-	return lineSegmentIntersect(starta, enda, face, intersection, tex_coord, normal, bi_normal);
-
-}
-
-
 S32 LLVolume::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, 
 								   S32 face,
-								   LLVector3* intersection,LLVector2* tex_coord, LLVector3* normal, LLVector3* bi_normal)
+								   LLVector4a* intersection,LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent_out)
 {
 	S32 hit_face = -1;
 	
@@ -4561,9 +4548,9 @@ S32 LLVolume::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& en
 
         if (LLLineSegmentBoxIntersect(start, end, box_center, box_size))
 		{
-			if (bi_normal != NULL) // if the caller wants binormals, we may need to generate them
+			if (tangent_out != NULL) // if the caller wants tangents, we may need to generate them
 			{
-				genBinormals(i);
+				genTangents(i);
 			}
 
 			if (isUnique())
@@ -4597,7 +4584,7 @@ S32 LLVolume::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& en
 								LLVector4a intersect = dir;
 								intersect.mul(closest_t);
 								intersect.add(start);
-								intersection->set(intersect.getF32ptr());
+								*intersection = intersect;
 							}
 
 
@@ -4612,19 +4599,42 @@ S32 LLVolume::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& en
 
 							if (normal!= NULL)
 							{
-								LLVector4* norm = (LLVector4*) face.mNormals;
-
-								*normal		= ((1.f - a - b)  * LLVector3(norm[idx0]) + 
-									a              * LLVector3(norm[idx1]) +
-									b              * LLVector3(norm[idx2]));
+								LLVector4a* norm = face.mNormals;
+								
+								LLVector4a n1,n2,n3;
+								n1 = norm[idx0];
+								n1.mul(1.f-a-b);
+								
+								n2 = norm[idx1];
+								n2.mul(a);
+								
+								n3 = norm[idx2];
+								n3.mul(b);
+
+								n1.add(n2);
+								n1.add(n3);
+								
+								*normal		= n1; 
 							}
 
-							if (bi_normal != NULL)
+							if (tangent_out != NULL)
 							{
-								LLVector4* binormal = (LLVector4*) face.mBinormals;
-								*bi_normal = ((1.f - a - b)  * LLVector3(binormal[idx0]) + 
-										a              * LLVector3(binormal[idx1]) +
-										b              * LLVector3(binormal[idx2]));
+								LLVector4a* tangents = face.mTangents;
+								
+								LLVector4a t1,t2,t3;
+								t1 = tangents[idx0];
+								t1.mul(1.f-a-b);
+								
+								t2 = tangents[idx1];
+								t2.mul(a);
+								
+								t3 = tangents[idx2];
+								t3.mul(b);
+
+								t1.add(t2);
+								t1.add(t3);
+								
+								*tangent_out = t1; 
 							}
 						}
 					}
@@ -4637,7 +4647,7 @@ S32 LLVolume::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& en
 					face.createOctree();
 				}
 			
-				LLOctreeTriangleRayIntersect intersect(start, dir, &face, &closest_t, intersection, tex_coord, normal, bi_normal);
+				LLOctreeTriangleRayIntersect intersect(start, dir, &face, &closest_t, intersection, tex_coord, normal, tangent_out);
 				intersect.traverse(face.mOctree);
 				if (intersect.mHitFace)
 				{
@@ -5183,7 +5193,7 @@ LLVolumeFace::LLVolumeFace() :
 	mNumIndices(0),
 	mPositions(NULL),
 	mNormals(NULL),
-	mBinormals(NULL),
+	mTangents(NULL),
 	mTexCoords(NULL),
 	mIndices(NULL),
 	mWeights(NULL),
@@ -5206,7 +5216,7 @@ LLVolumeFace::LLVolumeFace(const LLVolumeFace& src)
 	mNumIndices(0),
 	mPositions(NULL),
 	mNormals(NULL),
-	mBinormals(NULL),
+	mTangents(NULL),
 	mTexCoords(NULL),
 	mIndices(NULL),
 	mWeights(NULL),
@@ -5264,15 +5274,15 @@ LLVolumeFace& LLVolumeFace::operator=(const LLVolumeFace& src)
 		}
 
 
-		if (src.mBinormals)
+		if (src.mTangents)
 		{
-			allocateBinormals(src.mNumVertices);
-			LLVector4a::memcpyNonAliased16((F32*) mBinormals, (F32*) src.mBinormals, vert_size);
+			allocateTangents(src.mNumVertices);
+			LLVector4a::memcpyNonAliased16((F32*) mTangents, (F32*) src.mTangents, vert_size);
 		}
 		else
 		{
-			ll_aligned_free_16(mBinormals);
-			mBinormals = NULL;
+			ll_aligned_free_16(mTangents);
+			mTangents = NULL;
 		}
 
 		if (src.mWeights)
@@ -5316,8 +5326,8 @@ void LLVolumeFace::freeData()
 	mTexCoords = NULL;
 	ll_aligned_free_16(mIndices);
 	mIndices = NULL;
-	ll_aligned_free_16(mBinormals);
-	mBinormals = NULL;
+	ll_aligned_free_16(mTangents);
+	mTangents = NULL;
 	ll_aligned_free_16(mWeights);
 	mWeights = NULL;
 
@@ -5897,7 +5907,7 @@ void LLVolumeFace::cacheOptimize()
 	}
 
 	LLVector4a* binorm = NULL;
-	if (mBinormals)
+	if (mTangents)
 	{
 		binorm = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*num_verts);
 	}
@@ -5922,9 +5932,9 @@ void LLVolumeFace::cacheOptimize()
 			{
 				wght[cur_idx] = mWeights[idx];
 			}
-			if (mBinormals)
+			if (mTangents)
 			{
-				binorm[cur_idx] = mBinormals[idx];
+				binorm[cur_idx] = mTangents[idx];
 			}
 
 			cur_idx++;
@@ -5940,13 +5950,13 @@ void LLVolumeFace::cacheOptimize()
 	ll_aligned_free_16(mNormals);
 	ll_aligned_free_16(mTexCoords);
 	ll_aligned_free_16(mWeights);
-	ll_aligned_free_16(mBinormals);
+	ll_aligned_free_16(mTangents);
 
 	mPositions = pos;
 	mNormals = norm;
 	mTexCoords = tc;
 	mWeights = wght;
-	mBinormals = binorm;
+	mTangents = binorm;
 
 	//std::string result = llformat("ACMR pre/post: %.3f/%.3f  --  %d triangles %d breaks", pre_acmr, post_acmr, mNumIndices/3, breaks);
 	//llinfos << result << llendl;
@@ -6027,7 +6037,7 @@ void LLVolumeFace::swapData(LLVolumeFace& rhs)
 {
 	llswap(rhs.mPositions, mPositions);
 	llswap(rhs.mNormals, mNormals);
-	llswap(rhs.mBinormals, mBinormals);
+	llswap(rhs.mTangents, mTangents);
 	llswap(rhs.mTexCoords, mTexCoords);
 	llswap(rhs.mIndices,mIndices);
 	llswap(rhs.mNumVertices, mNumVertices);
@@ -6116,22 +6126,11 @@ BOOL LLVolumeFace::createUnCutCubeCap(LLVolume* volume, BOOL partial_build)
 			corners[2].mTexCoord=swap;
 		}
 
-		LLVector4a binormal;
-		
-		calc_binormal_from_triangle( binormal,
-			corners[0].getPosition(), corners[0].mTexCoord,
-			corners[1].getPosition(), corners[1].mTexCoord,
-			corners[2].getPosition(), corners[2].mTexCoord);
-		
-		binormal.normalize3fast();
-
 		S32 size = (grid_size+1)*(grid_size+1);
 		resizeVertices(size);
-		allocateBinormals(size);
-
+		
 		LLVector4a* pos = (LLVector4a*) mPositions;
 		LLVector4a* norm = (LLVector4a*) mNormals;
-		LLVector4a* binorm = (LLVector4a*) mBinormals;
 		LLVector2* tc = (LLVector2*) mTexCoords;
 
 		for(int gx = 0;gx<grid_size+1;gx++)
@@ -6150,8 +6149,7 @@ BOOL LLVolumeFace::createUnCutCubeCap(LLVolume* volume, BOOL partial_build)
 				*pos++ = newVert.getPosition();
 				*norm++ = baseVert.getNormal();
 				*tc++ = newVert.mTexCoord;
-				*binorm++ = binormal;
-
+				
 				if (gx == 0 && gy == 0)
 				{
 					min = newVert.getPosition();
@@ -6227,8 +6225,7 @@ BOOL LLVolumeFace::createCap(LLVolume* volume, BOOL partial_build)
 	if (!(mTypeMask & HOLLOW_MASK) && !(mTypeMask & OPEN_MASK))
 	{
 		resizeVertices(num_vertices+1);
-		allocateBinormals(num_vertices+1);	
-
+		
 		if (!partial_build)
 		{
 			resizeIndices(num_indices+3);
@@ -6237,8 +6234,7 @@ BOOL LLVolumeFace::createCap(LLVolume* volume, BOOL partial_build)
 	else
 	{
 		resizeVertices(num_vertices);
-		allocateBinormals(num_vertices);
-
+		
 		if (!partial_build)
 		{
 			resizeIndices(num_indices);
@@ -6272,8 +6268,7 @@ BOOL LLVolumeFace::createCap(LLVolume* volume, BOOL partial_build)
 	LLVector2* tc = (LLVector2*) mTexCoords;
 	LLVector4a* pos = (LLVector4a*) mPositions;
 	LLVector4a* norm = (LLVector4a*) mNormals;
-	LLVector4a* binorm = (LLVector4a*) mBinormals;
-
+	
 	// Copy the vertices into the array
 	for (S32 i = 0; i < num_vertices; i++)
 	{
@@ -6309,13 +6304,6 @@ BOOL LLVolumeFace::createCap(LLVolume* volume, BOOL partial_build)
 
 	cuv = (min_uv + max_uv)*0.5f;
 
-	LLVector4a binormal;
-	calc_binormal_from_triangle(binormal,
-		*mCenter, cuv,
-		pos[0], tc[0],
-		pos[1], tc[1]);
-	binormal.normalize3fast();
-
 	LLVector4a normal;
 	LLVector4a d0, d1;
 	
@@ -6347,7 +6335,6 @@ BOOL LLVolumeFace::createCap(LLVolume* volume, BOOL partial_build)
 		
 	for (S32 i = 0; i < num_vertices; i++)
 	{
-		binorm[i].load4a(binormal.getF32ptr());
 		norm[i].load4a(normal.getF32ptr());
 	}
 
@@ -6589,59 +6576,65 @@ BOOL LLVolumeFace::createCap(LLVolume* volume, BOOL partial_build)
 	return TRUE;
 }
 
-void LLVolumeFace::createBinormals()
+void CalculateTangentArray(U32 vertexCount, const LLVector4a *vertex, const LLVector4a *normal,
+        const LLVector2 *texcoord, U32 triangleCount, const U16* index_array, LLVector4a *tangent);
+
+void LLVolumeFace::createTangents()
 {
-	if (!mBinormals)
+	if (!mTangents)
 	{
-		allocateBinormals(mNumVertices);
+		allocateTangents(mNumVertices);
 
-		//generate binormals
-		LLVector4a* pos = mPositions;
-		LLVector2* tc = (LLVector2*) mTexCoords;
-		LLVector4a* binorm = (LLVector4a*) mBinormals;
+		//generate tangents
+		//LLVector4a* pos = mPositions;
+		//LLVector2* tc = (LLVector2*) mTexCoords;
+		LLVector4a* binorm = (LLVector4a*) mTangents;
 
-		LLVector4a* end = mBinormals+mNumVertices;
+		LLVector4a* end = mTangents+mNumVertices;
 		while (binorm < end)
 		{
 			(*binorm++).clear();
 		}
 
-		binorm = mBinormals;
+		binorm = mTangents;
+
+		CalculateTangentArray(mNumVertices, mPositions, mNormals, mTexCoords, mNumIndices/3, mIndices, mTangents);
 
-		for (U32 i = 0; i < mNumIndices/3; i++) 
+		/*for (U32 i = 0; i < mNumIndices/3; i++) 
 		{	//for each triangle
 			const U16& i0 = mIndices[i*3+0];
 			const U16& i1 = mIndices[i*3+1];
 			const U16& i2 = mIndices[i*3+2];
 						
-			//calculate binormal
-			LLVector4a binormal;
-			calc_binormal_from_triangle(binormal,
+			//calculate tangent
+			LLVector4a tangent;
+			calc_tangent_from_triangle(tangent,
 										pos[i0], tc[i0],
 										pos[i1], tc[i1],
 										pos[i2], tc[i2]);
 
 
 			//add triangle normal to vertices
-			binorm[i0].add(binormal);
-			binorm[i1].add(binormal);
-			binorm[i2].add(binormal);
+			binorm[i0].add(tangent);
+			binorm[i1].add(tangent);
+			binorm[i2].add(tangent);
 
 			//even out quad contributions
 			if (i % 2 == 0) 
 			{
-				binorm[i2].add(binormal);
+				binorm[i2].add(tangent);
 			}
 			else 
 			{
-				binorm[i1].add(binormal);
+				binorm[i1].add(tangent);
 			}
-		}
+		}*/
+
 
-		//normalize binormals
+		//normalize tangents
 		for (U32 i = 0; i < mNumVertices; i++) 
 		{
-			binorm[i].normalize3fast();
+			//binorm[i].normalize3fast();
 			//bump map/planar projection code requires normals to be normalized
 			mNormals[i].normalize3fast();
 		}
@@ -6652,10 +6645,10 @@ void LLVolumeFace::resizeVertices(S32 num_verts)
 {
 	ll_aligned_free_16(mPositions);
 	ll_aligned_free_16(mNormals);
-	ll_aligned_free_16(mBinormals);
+	ll_aligned_free_16(mTangents);
 	ll_aligned_free_16(mTexCoords);
 
-	mBinormals = NULL;
+	mTangents = NULL;
 
 	if (num_verts)
 	{
@@ -6705,9 +6698,9 @@ void LLVolumeFace::pushVertex(const LLVector4a& pos, const LLVector4a& norm, con
 	ll_assert_aligned(mTexCoords,16);
 	
 
-	//just clear binormals
-	ll_aligned_free_16(mBinormals);
-	mBinormals = NULL;
+	//just clear tangents
+	ll_aligned_free_16(mTangents);
+	mTangents = NULL;
 
 	mPositions[mNumVertices] = pos;
 	mNormals[mNumVertices] = norm;
@@ -6716,10 +6709,10 @@ void LLVolumeFace::pushVertex(const LLVector4a& pos, const LLVector4a& norm, con
 	mNumVertices++;	
 }
 
-void LLVolumeFace::allocateBinormals(S32 num_verts)
+void LLVolumeFace::allocateTangents(S32 num_verts)
 {
-	ll_aligned_free_16(mBinormals);
-	mBinormals = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*num_verts);
+	ll_aligned_free_16(mTangents);
+	mTangents = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*num_verts);
 }
 
 void LLVolumeFace::allocateWeights(S32 num_verts)
@@ -7231,53 +7224,87 @@ BOOL LLVolumeFace::createSide(LLVolume* volume, BOOL partial_build)
 	return TRUE;
 }
 
-// Finds binormal based on three vertices with texture coordinates.
-// Fills in dummy values if the triangle has degenerate texture coordinates.
-void calc_binormal_from_triangle(LLVector4a& binormal,
-
-	const LLVector4a& pos0,
-	const LLVector2& tex0,
-	const LLVector4a& pos1,
-	const LLVector2& tex1,
-	const LLVector4a& pos2,
-	const LLVector2& tex2)
-{
-	LLVector4a rx0( pos0[VX], tex0.mV[VX], tex0.mV[VY] );
-	LLVector4a rx1( pos1[VX], tex1.mV[VX], tex1.mV[VY] );
-	LLVector4a rx2( pos2[VX], tex2.mV[VX], tex2.mV[VY] );
-	
-	LLVector4a ry0( pos0[VY], tex0.mV[VX], tex0.mV[VY] );
-	LLVector4a ry1( pos1[VY], tex1.mV[VX], tex1.mV[VY] );
-	LLVector4a ry2( pos2[VY], tex2.mV[VX], tex2.mV[VY] );
-
-	LLVector4a rz0( pos0[VZ], tex0.mV[VX], tex0.mV[VY] );
-	LLVector4a rz1( pos1[VZ], tex1.mV[VX], tex1.mV[VY] );
-	LLVector4a rz2( pos2[VZ], tex2.mV[VX], tex2.mV[VY] );
-	
-	LLVector4a lhs, rhs;
-
-	LLVector4a r0; 
-	lhs.setSub(rx0, rx1); rhs.setSub(rx0, rx2);
-	r0.setCross3(lhs, rhs);
+//adapted from Lengyel, Eric. “Computing Tangent Space Basis Vectors for an Arbitrary Mesh”. Terathon Software 3D Graphics Library, 2001. http://www.terathon.com/code/tangent.html
+void CalculateTangentArray(U32 vertexCount, const LLVector4a *vertex, const LLVector4a *normal,
+        const LLVector2 *texcoord, U32 triangleCount, const U16* index_array, LLVector4a *tangent)
+{
+    //LLVector4a *tan1 = new LLVector4a[vertexCount * 2];
+	LLVector4a* tan1 = (LLVector4a*) ll_aligned_malloc_16(vertexCount*2*sizeof(LLVector4a));
+
+    LLVector4a* tan2 = tan1 + vertexCount;
+
+	memset(tan1, 0, vertexCount*2*sizeof(LLVector4a));
+        
+    for (U32 a = 0; a < triangleCount; a++)
+    {
+        U32 i1 = *index_array++;
+        U32 i2 = *index_array++;
+        U32 i3 = *index_array++;
+        
+        const LLVector4a& v1 = vertex[i1];
+        const LLVector4a& v2 = vertex[i2];
+        const LLVector4a& v3 = vertex[i3];
+        
+        const LLVector2& w1 = texcoord[i1];
+        const LLVector2& w2 = texcoord[i2];
+        const LLVector2& w3 = texcoord[i3];
+        
+		const F32* v1ptr = v1.getF32ptr();
+		const F32* v2ptr = v2.getF32ptr();
+		const F32* v3ptr = v3.getF32ptr();
 		
-	LLVector4a r1;
-	lhs.setSub(ry0, ry1); rhs.setSub(ry0, ry2);
-	r1.setCross3(lhs, rhs);
-
-	LLVector4a r2;
-	lhs.setSub(rz0, rz1); rhs.setSub(rz0, rz2);
-	r2.setCross3(lhs, rhs);
+        float x1 = v2ptr[0] - v1ptr[0];
+        float x2 = v3ptr[0] - v1ptr[0];
+        float y1 = v2ptr[1] - v1ptr[1];
+        float y2 = v3ptr[1] - v1ptr[1];
+        float z1 = v2ptr[2] - v1ptr[2];
+        float z2 = v3ptr[2] - v1ptr[2];
+        
+        float s1 = w2.mV[0] - w1.mV[0];
+        float s2 = w3.mV[0] - w1.mV[0];
+        float t1 = w2.mV[1] - w1.mV[1];
+        float t2 = w3.mV[1] - w1.mV[1];
+        
+        float r = 1.0F / (s1 * t2 - s2 * t1);
+        LLVector4a sdir((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r,
+                (t2 * z1 - t1 * z2) * r);
+        LLVector4a tdir((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r,
+                (s1 * z2 - s2 * z1) * r);
+        
+        tan1[i1].add(sdir);
+        tan1[i2].add(sdir);
+        tan1[i3].add(sdir);
+        
+        tan2[i1].add(tdir);
+        tan2[i2].add(tdir);
+        tan2[i3].add(tdir);
+    }
+    
+    for (U32 a = 0; a < vertexCount; a++)
+    {
+        LLVector4a n = normal[a];
+        const LLVector4a& t = tan1[a];
+        
+		LLVector4a ncrosst;
+		ncrosst.setCross3(n,t);
+
+        // Gram-Schmidt orthogonalize
+        n.mul(n.dot3(t).getF32());
+
+		LLVector4a tsubn;
+		tsubn.setSub(t,n);
+
+		tsubn.normalize3fast();
+		
+        // Calculate handedness
+		F32 handedness = ncrosst.dot3(tan2[a]).getF32() < 0.f ? -1.f : 1.f;
+		
+		tsubn.getF32ptr()[3] = handedness;
 
-	if( r0[VX] && r1[VX] && r2[VX] )
-	{
-		binormal.set(
-				-r0[VZ] / r0[VX],
-				-r1[VZ] / r1[VX],
-				-r2[VZ] / r2[VX]);
-		// binormal.normVec();
-	}
-	else
-	{
-		binormal.set( 0, 1 , 0 );
-	}
+        tangent[a] = tsubn;
+    }
+    
+	ll_aligned_free_16(tan1);
 }
+
+
diff --git a/indra/llmath/llvolume.h b/indra/llmath/llvolume.h
index c845556557..164b8d6652 100755
--- a/indra/llmath/llvolume.h
+++ b/indra/llmath/llvolume.h
@@ -844,12 +844,12 @@ private:
 public:
 
 	BOOL create(LLVolume* volume, BOOL partial_build = FALSE);
-	void createBinormals();
+	void createTangents();
 	
 	void appendFace(const LLVolumeFace& face, LLMatrix4& transform, LLMatrix4& normal_tranform);
 
 	void resizeVertices(S32 num_verts);
-	void allocateBinormals(S32 num_verts);
+	void allocateTangents(S32 num_verts);
 	void allocateWeights(S32 num_verts);
 	void resizeIndices(S32 num_indices);
 	void fillFromLegacyData(std::vector<LLVolumeFace::VertexData>& v, std::vector<U16>& idx);
@@ -916,7 +916,7 @@ public:
 
 	LLVector4a* mPositions;
 	LLVector4a* mNormals;
-	LLVector4a* mBinormals;
+	LLVector4a* mTangents;
 	LLVector2*  mTexCoords;
 	U16* mIndices;
 
@@ -980,7 +980,7 @@ public:
 	void setDirty() { mPathp->setDirty(); mProfilep->setDirty(); }
 
 	void regen();
-	void genBinormals(S32 face);
+	void genTangents(S32 face);
 
 	BOOL isConvex() const;
 	BOOL isCap(S32 face);
@@ -1008,21 +1008,14 @@ public:
 	//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, const LLVector3& end,
+	S32 lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end,
 							 S32 face = -1,                          // which face to check, -1 = ALL_SIDES
-							 LLVector3* intersection = NULL,         // return the intersection point
+							 LLVector4a* 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
+							 LLVector4a* normal = NULL,               // return the surface normal at the intersection point
+							 LLVector4a* tangent = NULL             // return the surface tangent at the intersection point
 		);
 
-	S32 lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, 
-								   S32 face = 1,
-								   LLVector3* intersection = NULL,
-								   LLVector2* tex_coord = NULL,
-								   LLVector3* normal = NULL,
-								   LLVector3* bi_normal = NULL);
-	
 	LLFaceID generateFaceMask();
 
 	BOOL isFaceMaskValid(LLFaceID face_mask);
@@ -1081,21 +1074,12 @@ public:
 
 std::ostream& operator<<(std::ostream &s, const LLVolumeParams &volume_params);
 
-void calc_binormal_from_triangle(
-		LLVector4a& binormal,
-		const LLVector4a& pos0,
-		const LLVector2& tex0,
-		const LLVector4a& pos1,
-		const LLVector2& tex1,
-		const LLVector4a& pos2,
-		const LLVector2& tex2);
-
 BOOL LLLineSegmentBoxIntersect(const F32* start, const F32* end, const F32* center, const F32* size);
 BOOL LLLineSegmentBoxIntersect(const LLVector3& start, const LLVector3& end, const LLVector3& center, const LLVector3& size);
 BOOL LLLineSegmentBoxIntersect(const LLVector4a& start, const LLVector4a& end, const LLVector4a& center, const LLVector4a& 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);
+//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);
 
 BOOL LLTriangleRayIntersect(const LLVector4a& vert0, const LLVector4a& vert1, const LLVector4a& vert2, const LLVector4a& orig, const LLVector4a& dir,
 							F32& intersection_a, F32& intersection_b, F32& intersection_t);
diff --git a/indra/llmath/llvolumeoctree.cpp b/indra/llmath/llvolumeoctree.cpp
index cc83cb7235..0728b49c1f 100755
--- a/indra/llmath/llvolumeoctree.cpp
+++ b/indra/llmath/llvolumeoctree.cpp
@@ -94,14 +94,14 @@ void LLVolumeOctreeListener::handleChildAddition(const LLOctreeNode<LLVolumeTria
 
 LLOctreeTriangleRayIntersect::LLOctreeTriangleRayIntersect(const LLVector4a& start, const LLVector4a& dir, 
 							   const LLVolumeFace* face, F32* closest_t,
-							   LLVector3* intersection,LLVector2* tex_coord, LLVector3* normal, LLVector3* bi_normal)
+							   LLVector4a* intersection,LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent)
    : mFace(face),
      mStart(start),
 	 mDir(dir),
 	 mIntersection(intersection),
 	 mTexCoord(tex_coord),
 	 mNormal(normal),
-	 mBinormal(bi_normal),
+	 mTangent(tangent),
 	 mClosestT(closest_t),
 	 mHitFace(false)
 {
@@ -112,13 +112,7 @@ void LLOctreeTriangleRayIntersect::traverse(const LLOctreeNode<LLVolumeTriangle>
 {
 	LLVolumeOctreeListener* vl = (LLVolumeOctreeListener*) node->getListener(0);
 
-	/*const F32* start = mStart.getF32();
-	const F32* end = mEnd.getF32();
-	const F32* center = vl->mBounds[0].getF32();
-	const F32* size = vl->mBounds[1].getF32();*/
-
-	//if (LLLineSegmentBoxIntersect(mStart, mEnd, vl->mBounds[0], vl->mBounds[1]))
-	if (LLLineSegmentBoxIntersect(mStart.getF32ptr(), mEnd.getF32ptr(), vl->mBounds[0].getF32ptr(), vl->mBounds[1].getF32ptr()))
+	if (LLLineSegmentBoxIntersect(mStart, mEnd, vl->mBounds[0], vl->mBounds[1]))
 	{
 		node->accept(this);
 		for (S32 i = 0; i < node->getChildCount(); ++i)
@@ -152,34 +146,60 @@ void LLOctreeTriangleRayIntersect::visit(const LLOctreeNode<LLVolumeTriangle>* n
 					LLVector4a intersect = mDir;
 					intersect.mul(*mClosestT);
 					intersect.add(mStart);
-					mIntersection->set(intersect.getF32ptr());
+					*mIntersection = intersect;
 				}
 
+				U32 idx0 = tri->mIndex[0];
+				U32 idx1 = tri->mIndex[1];
+				U32 idx2 = tri->mIndex[2];
 
 				if (mTexCoord != NULL)
 				{
 					LLVector2* tc = (LLVector2*) mFace->mTexCoords;
-					*mTexCoord = ((1.f - a - b)  * tc[tri->mIndex[0]] +
-						a              * tc[tri->mIndex[1]] +
-						b              * tc[tri->mIndex[2]]);
+					*mTexCoord = ((1.f - a - b)  * tc[idx0] +
+						a              * tc[idx1] +
+						b              * tc[idx2]);
 
 				}
 
 				if (mNormal != NULL)
 				{
-					LLVector4* norm = (LLVector4*) mFace->mNormals;
-
-					*mNormal    = ((1.f - a - b)  * LLVector3(norm[tri->mIndex[0]]) + 
-						a              * LLVector3(norm[tri->mIndex[1]]) +
-						b              * LLVector3(norm[tri->mIndex[2]]));
+					LLVector4a* norm = mFace->mNormals;
+								
+					LLVector4a n1,n2,n3;
+					n1 = norm[idx0];
+					n1.mul(1.f-a-b);
+								
+					n2 = norm[idx1];
+					n2.mul(a);
+								
+					n3 = norm[idx2];
+					n3.mul(b);
+
+					n1.add(n2);
+					n1.add(n3);
+								
+					*mNormal		= n1; 
 				}
 
-				if (mBinormal != NULL)
+				if (mTangent != NULL)
 				{
-					LLVector4* binormal = (LLVector4*) mFace->mBinormals;
-					*mBinormal = ((1.f - a - b)  * LLVector3(binormal[tri->mIndex[0]]) + 
-							a              * LLVector3(binormal[tri->mIndex[1]]) +
-							b              * LLVector3(binormal[tri->mIndex[2]]));
+					LLVector4a* tangents = mFace->mTangents;
+								
+					LLVector4a t1,t2,t3;
+					t1 = tangents[idx0];
+					t1.mul(1.f-a-b);
+								
+					t2 = tangents[idx1];
+					t2.mul(a);
+								
+					t3 = tangents[idx2];
+					t3.mul(b);
+
+					t1.add(t2);
+					t1.add(t3);
+								
+					*mTangent = t1; 
 				}
 			}
 		}
diff --git a/indra/llmath/llvolumeoctree.h b/indra/llmath/llvolumeoctree.h
index 9ae34a0c4e..80d6ced36d 100755
--- a/indra/llmath/llvolumeoctree.h
+++ b/indra/llmath/llvolumeoctree.h
@@ -137,16 +137,16 @@ public:
 	LLVector4a mStart;
 	LLVector4a mDir;
 	LLVector4a mEnd;
-	LLVector3* mIntersection;
+	LLVector4a* mIntersection;
 	LLVector2* mTexCoord;
-	LLVector3* mNormal;
-	LLVector3* mBinormal;
+	LLVector4a* mNormal;
+	LLVector4a* mTangent;
 	F32* mClosestT;
 	bool mHitFace;
 
 	LLOctreeTriangleRayIntersect(const LLVector4a& start, const LLVector4a& dir, 
 								   const LLVolumeFace* face, F32* closest_t,
-								   LLVector3* intersection,LLVector2* tex_coord, LLVector3* normal, LLVector3* bi_normal);
+								   LLVector4a* intersection,LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent);
 
 	void traverse(const LLOctreeNode<LLVolumeTriangle>* node);
 
diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp
index 37b9c0e0e0..fea4ee2819 100755
--- a/indra/llrender/llshadermgr.cpp
+++ b/indra/llrender/llshadermgr.cpp
@@ -977,7 +977,7 @@ void LLShaderMgr::initAttribsAndUniforms()
 	mReservedAttribs.push_back("texcoord3");
 	mReservedAttribs.push_back("diffuse_color");
 	mReservedAttribs.push_back("emissive");
-	mReservedAttribs.push_back("binormal");
+	mReservedAttribs.push_back("tangent");
 	mReservedAttribs.push_back("weight");
 	mReservedAttribs.push_back("weight4");
 	mReservedAttribs.push_back("clothing");
diff --git a/indra/llrender/llvertexbuffer.cpp b/indra/llrender/llvertexbuffer.cpp
index 08f2d30aa4..01541026b1 100755
--- a/indra/llrender/llvertexbuffer.cpp
+++ b/indra/llrender/llvertexbuffer.cpp
@@ -342,7 +342,7 @@ S32 LLVertexBuffer::sTypeSize[LLVertexBuffer::TYPE_MAX] =
 	sizeof(LLVector2), // TYPE_TEXCOORD3,
 	sizeof(LLColor4U), // TYPE_COLOR,
 	sizeof(LLColor4U), // TYPE_EMISSIVE, only alpha is used currently
-	sizeof(LLVector4), // TYPE_BINORMAL,
+	sizeof(LLVector4), // TYPE_TANGENT,
 	sizeof(F32),	   // TYPE_WEIGHT,
 	sizeof(LLVector4), // TYPE_WEIGHT4,
 	sizeof(LLVector4), // TYPE_CLOTHWEIGHT,
@@ -359,7 +359,7 @@ static std::string vb_type_name[] =
 	"TYPE_TEXCOORD3",
 	"TYPE_COLOR",
 	"TYPE_EMISSIVE",
-	"TYPE_BINORMAL",
+	"TYPE_TANGENT",
 	"TYPE_WEIGHT",
 	"TYPE_WEIGHT4",
 	"TYPE_CLOTHWEIGHT",
@@ -542,16 +542,16 @@ void LLVertexBuffer::setupClientArrays(U32 data_mask)
 				}
 			}
 
-			if (sLastMask & MAP_BINORMAL)
+			if (sLastMask & MAP_TANGENT)
 			{
-				if (!(data_mask & MAP_BINORMAL))
+				if (!(data_mask & MAP_TANGENT))
 				{
 					glClientActiveTextureARB(GL_TEXTURE2_ARB);
 					glDisableClientState(GL_TEXTURE_COORD_ARRAY);
 					glClientActiveTextureARB(GL_TEXTURE0_ARB);
 				}
 			}
-			else if (data_mask & MAP_BINORMAL)
+			else if (data_mask & MAP_TANGENT)
 			{
 				glClientActiveTextureARB(GL_TEXTURE2_ARB);
 				glEnableClientState(GL_TEXTURE_COORD_ARRAY);
@@ -1354,7 +1354,7 @@ void LLVertexBuffer::setupVertexArray()
 		2, //TYPE_TEXCOORD3,
 		4, //TYPE_COLOR,
 		4, //TYPE_EMISSIVE,
-		3, //TYPE_BINORMAL,
+		4, //TYPE_TANGENT,
 		1, //TYPE_WEIGHT,
 		4, //TYPE_WEIGHT4,
 		4, //TYPE_CLOTHWEIGHT,
@@ -1371,7 +1371,7 @@ void LLVertexBuffer::setupVertexArray()
 		GL_FLOAT, //TYPE_TEXCOORD3,
 		GL_UNSIGNED_BYTE, //TYPE_COLOR,
 		GL_UNSIGNED_BYTE, //TYPE_EMISSIVE,
-		GL_FLOAT,   //TYPE_BINORMAL,
+		GL_FLOAT,   //TYPE_TANGENT,
 		GL_FLOAT, //TYPE_WEIGHT,
 		GL_FLOAT, //TYPE_WEIGHT4,
 		GL_FLOAT, //TYPE_CLOTHWEIGHT,
@@ -1388,7 +1388,7 @@ void LLVertexBuffer::setupVertexArray()
 		false, //TYPE_TEXCOORD3,
 		false, //TYPE_COLOR,
 		false, //TYPE_EMISSIVE,
-		false, //TYPE_BINORMAL,
+		false, //TYPE_TANGENT,
 		false, //TYPE_WEIGHT,
 		false, //TYPE_WEIGHT4,
 		false, //TYPE_CLOTHWEIGHT,
@@ -1405,7 +1405,7 @@ void LLVertexBuffer::setupVertexArray()
 		GL_FALSE, //TYPE_TEXCOORD3,
 		GL_TRUE, //TYPE_COLOR,
 		GL_TRUE, //TYPE_EMISSIVE,
-		GL_FALSE,   //TYPE_BINORMAL,
+		GL_FALSE,   //TYPE_TANGENT,
 		GL_FALSE, //TYPE_WEIGHT,
 		GL_FALSE, //TYPE_WEIGHT4,
 		GL_FALSE, //TYPE_CLOTHWEIGHT,
@@ -2078,9 +2078,13 @@ bool LLVertexBuffer::getNormalStrider(LLStrider<LLVector3>& strider, S32 index,
 {
 	return VertexBufferStrider<LLVector3,TYPE_NORMAL>::get(*this, strider, index, count, map_range);
 }
-bool LLVertexBuffer::getBinormalStrider(LLStrider<LLVector3>& strider, S32 index, S32 count, bool map_range)
+bool LLVertexBuffer::getTangentStrider(LLStrider<LLVector3>& strider, S32 index, S32 count, bool map_range)
 {
-	return VertexBufferStrider<LLVector3,TYPE_BINORMAL>::get(*this, strider, index, count, map_range);
+	return VertexBufferStrider<LLVector3,TYPE_TANGENT>::get(*this, strider, index, count, map_range);
+}
+bool LLVertexBuffer::getTangentStrider(LLStrider<LLVector4a>& strider, S32 index, S32 count, bool map_range)
+{
+	return VertexBufferStrider<LLVector4a,TYPE_TANGENT>::get(*this, strider, index, count, map_range);
 }
 bool LLVertexBuffer::getColorStrider(LLStrider<LLColor4U>& strider, S32 index, S32 count, bool map_range)
 {
@@ -2390,11 +2394,11 @@ void LLVertexBuffer::setupVertexBuffer(U32 data_mask)
 			void* ptr = (void*)(base + mOffsets[TYPE_TEXCOORD1]);
 			glVertexAttribPointerARB(loc,2,GL_FLOAT, GL_FALSE, LLVertexBuffer::sTypeSize[TYPE_TEXCOORD1], ptr);
 		}
-		if (data_mask & MAP_BINORMAL)
+		if (data_mask & MAP_TANGENT)
 		{
-			S32 loc = TYPE_BINORMAL;
-			void* ptr = (void*)(base + mOffsets[TYPE_BINORMAL]);
-			glVertexAttribPointerARB(loc, 3,GL_FLOAT, GL_FALSE, LLVertexBuffer::sTypeSize[TYPE_BINORMAL], ptr);
+			S32 loc = TYPE_TANGENT;
+			void* ptr = (void*)(base + mOffsets[TYPE_TANGENT]);
+			glVertexAttribPointerARB(loc, 4,GL_FLOAT, GL_FALSE, LLVertexBuffer::sTypeSize[TYPE_TANGENT], ptr);
 		}
 		if (data_mask & MAP_TEXCOORD0)
 		{
@@ -2472,10 +2476,10 @@ void LLVertexBuffer::setupVertexBuffer(U32 data_mask)
 			glTexCoordPointer(2,GL_FLOAT, LLVertexBuffer::sTypeSize[TYPE_TEXCOORD1], (void*)(base + mOffsets[TYPE_TEXCOORD1]));
 			glClientActiveTextureARB(GL_TEXTURE0_ARB);
 		}
-		if (data_mask & MAP_BINORMAL)
+		if (data_mask & MAP_TANGENT)
 		{
 			glClientActiveTextureARB(GL_TEXTURE2_ARB);
-			glTexCoordPointer(3,GL_FLOAT, LLVertexBuffer::sTypeSize[TYPE_BINORMAL], (void*)(base + mOffsets[TYPE_BINORMAL]));
+			glTexCoordPointer(4,GL_FLOAT, LLVertexBuffer::sTypeSize[TYPE_TANGENT], (void*)(base + mOffsets[TYPE_TANGENT]));
 			glClientActiveTextureARB(GL_TEXTURE0_ARB);
 		}
 		if (data_mask & MAP_TEXCOORD0)
diff --git a/indra/llrender/llvertexbuffer.h b/indra/llrender/llvertexbuffer.h
index 1be9b79e84..04806c1d8c 100755
--- a/indra/llrender/llvertexbuffer.h
+++ b/indra/llrender/llvertexbuffer.h
@@ -174,7 +174,7 @@ public:
 		TYPE_TEXCOORD3,
 		TYPE_COLOR,
 		TYPE_EMISSIVE,
-		TYPE_BINORMAL,
+		TYPE_TANGENT,
 		TYPE_WEIGHT,
 		TYPE_WEIGHT4,
 		TYPE_CLOTHWEIGHT,
@@ -192,7 +192,7 @@ public:
 		MAP_COLOR = (1<<TYPE_COLOR),
 		MAP_EMISSIVE = (1<<TYPE_EMISSIVE),
 		// These use VertexAttribPointer and should possibly be made generic
-		MAP_BINORMAL = (1<<TYPE_BINORMAL),
+		MAP_TANGENT = (1<<TYPE_TANGENT),
 		MAP_WEIGHT = (1<<TYPE_WEIGHT),
 		MAP_WEIGHT4 = (1<<TYPE_WEIGHT4),
 		MAP_CLOTHWEIGHT = (1<<TYPE_CLOTHWEIGHT),
@@ -252,7 +252,8 @@ public:
 	bool getTexCoord1Strider(LLStrider<LLVector2>& strider, S32 index=0, S32 count = -1, bool map_range = false);
 	bool getTexCoord2Strider(LLStrider<LLVector2>& strider, S32 index=0, S32 count = -1, bool map_range = false);
 	bool getNormalStrider(LLStrider<LLVector3>& strider, S32 index=0, S32 count = -1, bool map_range = false);
-	bool getBinormalStrider(LLStrider<LLVector3>& strider, S32 index=0, S32 count = -1, bool map_range = false);
+	bool getTangentStrider(LLStrider<LLVector3>& strider, S32 index=0, S32 count = -1, bool map_range = false);
+	bool getTangentStrider(LLStrider<LLVector4a>& strider, S32 index=0, S32 count = -1, bool map_range = false);
 	bool getColorStrider(LLStrider<LLColor4U>& strider, S32 index=0, S32 count = -1, bool map_range = false);
 	bool getTextureIndexStrider(LLStrider<LLColor4U>& strider, S32 index=0, S32 count = -1, bool map_range = false);
 	bool getEmissiveStrider(LLStrider<LLColor4U>& strider, S32 index=0, S32 count = -1, bool map_range = false);
diff --git a/indra/newview/app_settings/shaders/class1/deferred/bumpSkinnedV.glsl b/indra/newview/app_settings/shaders/class1/deferred/bumpSkinnedV.glsl
index 8ba75010a2..10144f3e16 100755
--- a/indra/newview/app_settings/shaders/class1/deferred/bumpSkinnedV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/bumpSkinnedV.glsl
@@ -30,7 +30,7 @@ ATTRIBUTE vec3 position;
 ATTRIBUTE vec4 diffuse_color;
 ATTRIBUTE vec3 normal;
 ATTRIBUTE vec2 texcoord0;
-ATTRIBUTE vec3 binormal;
+ATTRIBUTE vec4 tangent;
 
 VARYING vec3 vary_mat0;
 VARYING vec3 vary_mat1;
@@ -52,8 +52,8 @@ void main()
 	
 	
 	vec3 n = normalize((mat * vec4(normal.xyz+position.xyz, 1.0)).xyz-pos.xyz);
-	vec3 b = normalize((mat * vec4(binormal.xyz+position.xyz, 1.0)).xyz-pos.xyz);
-	vec3 t = cross(b, n);
+	vec3 t = normalize((mat * vec4(tangent.xyz+position.xyz, 1.0)).xyz-pos.xyz);
+	vec3 b = cross(n, t) * tangent.w;
 	
 	vary_mat0 = vec3(t.x, b.x, n.x);
 	vary_mat1 = vec3(t.y, b.y, n.y);
diff --git a/indra/newview/app_settings/shaders/class1/deferred/bumpV.glsl b/indra/newview/app_settings/shaders/class1/deferred/bumpV.glsl
index c8d38bb8f7..9f9749394e 100755
--- a/indra/newview/app_settings/shaders/class1/deferred/bumpV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/bumpV.glsl
@@ -31,7 +31,7 @@ ATTRIBUTE vec3 position;
 ATTRIBUTE vec4 diffuse_color;
 ATTRIBUTE vec3 normal;
 ATTRIBUTE vec2 texcoord0;
-ATTRIBUTE vec3 binormal;
+ATTRIBUTE vec4 tangent;
 
 VARYING vec3 vary_mat0;
 VARYING vec3 vary_mat1;
@@ -46,8 +46,8 @@ void main()
 	vary_texcoord0 = (texture_matrix0 * vec4(texcoord0,0,1)).xy;
 	
 	vec3 n = normalize(normal_matrix * normal);
-	vec3 b = normalize(normal_matrix * binormal);
-	vec3 t = cross(b, n);
+	vec3 t = normalize(normal_matrix * tangent.xyz);
+	vec3 b = cross(n, t) * tangent.w;
 	
 	vary_mat0 = vec3(t.x, b.x, n.x);
 	vary_mat1 = vec3(t.y, b.y, n.y);
diff --git a/indra/newview/app_settings/shaders/class1/deferred/materialF.glsl b/indra/newview/app_settings/shaders/class1/deferred/materialF.glsl
index 17aa0e32a7..df8e7a6c0b 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/materialF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/materialF.glsl
@@ -495,7 +495,7 @@ void main()
 	//final_color.rgb *= 1 - spec.a * env_intensity;
 	//final_specular.rgb *= specular_color.rgb;
 	
-	vec4 final_normal = vec4(encode_normal(normalize(tnorm)), spec.a * env_intensity, 0.0);
+	vec4 final_normal = vec4(encode_normal(normalize(tnorm)), env_intensity, 0.0);
 	final_specular.a = specular_color.a * norm.a;
 #else
 	vec4 final_normal = vec4(encode_normal(normalize(tnorm)), env_intensity, 0.0);
diff --git a/indra/newview/app_settings/shaders/class1/deferred/materialV.glsl b/indra/newview/app_settings/shaders/class1/deferred/materialV.glsl
index 0638dcfa55..b25032866b 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/materialV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/materialV.glsl
@@ -56,7 +56,7 @@ ATTRIBUTE vec2 texcoord0;
 
 
 #if HAS_NORMAL_MAP
-ATTRIBUTE vec3 binormal;
+ATTRIBUTE vec4 tangent;
 ATTRIBUTE vec2 texcoord1;
 
 VARYING vec3 vary_mat0;
@@ -110,8 +110,8 @@ void main()
 #if HAS_SKIN
 	vec3 n = normalize((mat*vec4(normal.xyz+position.xyz,1.0)).xyz-pos.xyz);
 #if HAS_NORMAL_MAP
-	vec3 b = normalize((mat*vec4(binormal.xyz+position.xyz,1.0)).xyz-pos.xyz);
-	vec3 t = cross(b, n);
+	vec3 t = normalize((mat*vec4(tangent.xyz+position.xyz,1.0)).xyz-pos.xyz);
+	vec3 b = cross(n, t)*tangent.w;
 	
 	vary_mat0 = vec3(t.x, b.x, n.x);
 	vary_mat1 = vec3(t.y, b.y, n.y);
@@ -122,8 +122,9 @@ vary_normal  = n;
 #else //HAS_SKIN
 	vec3 n = normalize(normal_matrix * normal);
 #if HAS_NORMAL_MAP
-	vec3 b = normalize(normal_matrix * binormal);
-	vec3 t = cross(b, n);
+	vec3 t = normalize(normal_matrix * tangent.xyz);
+	vec3 b = cross(n,t)*tangent.w;
+	//vec3 t = cross(b,n) * binormal.w;
 	
 	vary_mat0 = vec3(t.x, b.x, n.x);
 	vary_mat1 = vec3(t.y, b.y, n.y);
diff --git a/indra/newview/app_settings/shaders/class1/transform/binormalV.glsl b/indra/newview/app_settings/shaders/class1/transform/binormalV.glsl
index 44f1aa34a0..449d8d8b4e 100755
--- a/indra/newview/app_settings/shaders/class1/transform/binormalV.glsl
+++ b/indra/newview/app_settings/shaders/class1/transform/binormalV.glsl
@@ -25,12 +25,12 @@
 
 uniform mat3 normal_matrix;
 
-ATTRIBUTE vec3 binormal;
+ATTRIBUTE vec4 tangent;
 
-VARYING vec4 binormal_out;
+VARYING vec4 tangent_out;
 
 void main()
 {
-	binormal_out = vec4(normal_matrix * binormal, 0.0);
+	tangent_out = vec4(normal_matrix * tangent.xyz), tangent.w);
 }
 
diff --git a/indra/newview/lldrawpoolalpha.cpp b/indra/newview/lldrawpoolalpha.cpp
index 5ed8bbca50..c832e1401d 100755
--- a/indra/newview/lldrawpoolalpha.cpp
+++ b/indra/newview/lldrawpoolalpha.cpp
@@ -239,7 +239,7 @@ void LLDrawPoolAlpha::render(S32 pass)
 
 	if (mVertexShaderLevel > 0)
 	{
-		renderAlpha(getVertexDataMask() | LLVertexBuffer::MAP_TEXTURE_INDEX | LLVertexBuffer::MAP_BINORMAL | LLVertexBuffer::MAP_TEXCOORD1 | LLVertexBuffer::MAP_TEXCOORD2);
+		renderAlpha(getVertexDataMask() | LLVertexBuffer::MAP_TEXTURE_INDEX | LLVertexBuffer::MAP_TANGENT | LLVertexBuffer::MAP_TEXCOORD1 | LLVertexBuffer::MAP_TEXCOORD2);
 	}
 	else
 	{
diff --git a/indra/newview/lldrawpoolavatar.h b/indra/newview/lldrawpoolavatar.h
index bb9126c162..7d0368a945 100755
--- a/indra/newview/lldrawpoolavatar.h
+++ b/indra/newview/lldrawpoolavatar.h
@@ -207,7 +207,7 @@ public:
 		RIGGED_NORMMAP_VMASK =
 						LLVertexBuffer::MAP_VERTEX | 
 						LLVertexBuffer::MAP_NORMAL | 
-						LLVertexBuffer::MAP_BINORMAL | 
+						LLVertexBuffer::MAP_TANGENT | 
 						LLVertexBuffer::MAP_TEXCOORD0 |
 						LLVertexBuffer::MAP_TEXCOORD1 |
 						LLVertexBuffer::MAP_COLOR |
@@ -218,7 +218,7 @@ public:
 		RIGGED_NORMSPEC_VMASK =
 						LLVertexBuffer::MAP_VERTEX | 
 						LLVertexBuffer::MAP_NORMAL | 
-						LLVertexBuffer::MAP_BINORMAL | 
+						LLVertexBuffer::MAP_TANGENT | 
 						LLVertexBuffer::MAP_TEXCOORD0 |
 						LLVertexBuffer::MAP_TEXCOORD1 |
 						LLVertexBuffer::MAP_TEXCOORD2 |
@@ -247,7 +247,7 @@ public:
 		RIGGED_DEFERRED_BUMP_MASK = LLVertexBuffer::MAP_VERTEX | 
 							 LLVertexBuffer::MAP_NORMAL | 
 							 LLVertexBuffer::MAP_TEXCOORD0 |
-							 LLVertexBuffer::MAP_BINORMAL |
+							 LLVertexBuffer::MAP_TANGENT |
 							 LLVertexBuffer::MAP_COLOR |
 							 LLVertexBuffer::MAP_WEIGHT4,
 		RIGGED_DEFERRED_SIMPLE_MASK = LLVertexBuffer::MAP_VERTEX | 
diff --git a/indra/newview/lldrawpoolbump.cpp b/indra/newview/lldrawpoolbump.cpp
index 29076de54b..7152a34094 100755
--- a/indra/newview/lldrawpoolbump.cpp
+++ b/indra/newview/lldrawpoolbump.cpp
@@ -856,7 +856,7 @@ void LLDrawPoolBump::renderDeferred(S32 pass)
 	LLCullResult::drawinfo_iterator begin = gPipeline.beginRenderMap(type);
 	LLCullResult::drawinfo_iterator end = gPipeline.endRenderMap(type);
 
-	U32 mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0 | LLVertexBuffer::MAP_BINORMAL | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_COLOR;
+	U32 mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0 | LLVertexBuffer::MAP_TANGENT | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_COLOR;
 	
 	for (LLCullResult::drawinfo_iterator i = begin; i != end; ++i)	
 	{
diff --git a/indra/newview/lldrawpoolmaterials.h b/indra/newview/lldrawpoolmaterials.h
index cfbd13f335..eae1aba87c 100644
--- a/indra/newview/lldrawpoolmaterials.h
+++ b/indra/newview/lldrawpoolmaterials.h
@@ -52,7 +52,7 @@ public:
 		LLVertexBuffer::MAP_TEXCOORD1 |
 		LLVertexBuffer::MAP_TEXCOORD2 |
 		LLVertexBuffer::MAP_COLOR |
-		LLVertexBuffer::MAP_BINORMAL
+		LLVertexBuffer::MAP_TANGENT
 	};
 	
 	/*virtual*/ U32 getVertexDataMask() { return VERTEX_DATA_MASK; }
diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp
index 63919630fc..46d6a1a97f 100755
--- a/indra/newview/llface.cpp
+++ b/indra/newview/llface.cpp
@@ -106,41 +106,6 @@ void planarProjection(LLVector2 &tc, const LLVector4a& normal,
 	tc.mV[0] = 1.0f+((binormal.dot3(vec).getF32())*2 - 0.5f);
 }
 
-void sphericalProjection(LLVector2 &tc, const LLVector4a& normal,
-						 const LLVector4a &mCenter, const LLVector4a& vec)
-{	//BROKEN
-	/*tc.mV[0] = acosf(vd.mNormal * LLVector3(1,0,0))/3.14159f;
-	
-	tc.mV[1] = acosf(vd.mNormal * LLVector3(0,0,1))/6.284f;
-	if (vd.mNormal.mV[1] > 0)
-	{
-		tc.mV[1] = 1.0f-tc.mV[1];
-	}*/
-}
-
-void cylindricalProjection(LLVector2 &tc, const LLVector4a& normal, const LLVector4a &mCenter, const LLVector4a& vec)
-{	//BROKEN
-	/*LLVector3 binormal;
-	float d = vd.mNormal * LLVector3(1,0,0);
-	if (d >= 0.5f || d <= -0.5f)
-	{
-		binormal = LLVector3(0,1,0);
-	}
-	else{
-		binormal = LLVector3(1,0,0);
-	}
-	LLVector3 tangent = binormal % vd.mNormal;
-
-	tc.mV[1] = -((tangent*vec)*2 - 0.5f);
-
-	tc.mV[0] = acosf(vd.mNormal * LLVector3(1,0,0))/6.284f;
-
-	if (vd.mNormal.mV[1] < 0)
-	{
-		tc.mV[0] = 1.0f-tc.mV[0];
-	}*/
-}
-
 ////////////////////
 //
 // LLFace implementation
@@ -937,7 +902,7 @@ BOOL LLFace::genVolumeBBoxes(const LLVolume &volume, S32 f,
 // 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 LLFace::surfaceToTexture(LLVector2 surface_coord, const LLVector4a& position, const LLVector4a& normal)
 {
 	LLVector2 tc = surface_coord;
 	
@@ -957,7 +922,9 @@ LLVector2 LLFace::surfaceToTexture(LLVector2 surface_coord, LLVector3 position,
 		LLVector4a& center = *(mDrawablep->getVOVolume()->getVolume()->getVolumeFace(mTEOffset).mCenter);
 		
 		LLVector4a volume_position;
-		volume_position.load3(mDrawablep->getVOVolume()->agentPositionToVolume(position).mV);
+		LLVector3 v_position(position.getF32ptr());
+
+		volume_position.load3(mDrawablep->getVOVolume()->agentPositionToVolume(v_position).mV);
 		
 		if (!mDrawablep->getVOVolume()->isVolumeGlobal())
 		{
@@ -967,23 +934,14 @@ LLVector2 LLFace::surfaceToTexture(LLVector2 surface_coord, LLVector3 position,
 		}
 		
 		LLVector4a volume_normal;
-		volume_normal.load3(mDrawablep->getVOVolume()->agentDirectionToVolume(normal).mV);
+		LLVector3 v_normal(normal.getF32ptr());
+		volume_normal.load3(mDrawablep->getVOVolume()->agentDirectionToVolume(v_normal).mV);
 		volume_normal.normalize3fast();
 		
-		switch (texgen)
+		if (texgen == LLTextureEntry::TEX_GEN_PLANAR)
 		{
-		case LLTextureEntry::TEX_GEN_PLANAR:
 			planarProjection(tc, volume_normal, center, volume_position);
-			break;
-		case LLTextureEntry::TEX_GEN_SPHERICAL:
-			sphericalProjection(tc, volume_normal, center, volume_position);
-			break;
-		case LLTextureEntry::TEX_GEN_CYLINDRICAL:
-			cylindricalProjection(tc, volume_normal, center, volume_position);
-			break;
-		default:
-			break;
-		}		
+		}
 	}
 
 	if (mTextureMatrix)	// if we have a texture matrix, use it
@@ -1010,7 +968,12 @@ void LLFace::getPlanarProjectedParams(LLQuaternion* face_rot, LLVector3* face_po
 	const LLMatrix4& vol_mat = getWorldMatrix();
 	const LLVolumeFace& vf = getViewerObject()->getVolume()->getVolumeFace(mTEOffset);
 	const LLVector4a& normal4a = vf.mNormals[0];
-	const LLVector4a& binormal4a = vf.mBinormals[0];
+	const LLVector4a& tangent = vf.mTangents[0];
+
+	LLVector4a binormal4a;
+	binormal4a.setCross3(normal4a, tangent);
+	binormal4a.mul(tangent.getF32ptr()[3]);
+
 	LLVector2 projected_binormal;
 	planarProjection(projected_binormal, normal4a, *vf.mCenter, binormal4a);
 	projected_binormal -= LLVector2(0.5f, 0.5f); // this normally happens in xform()
@@ -1138,7 +1101,7 @@ void LLFace::cacheFaceInVRAM(const LLVolumeFace& vf)
 {
 	LLFastTimer t(FTM_FACE_GEOM_VOLUME);
 	U32 mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0 |
-				LLVertexBuffer::MAP_BINORMAL | LLVertexBuffer::MAP_NORMAL;
+				LLVertexBuffer::MAP_TANGENT | LLVertexBuffer::MAP_NORMAL;
 	
 	if (vf.mWeights)
 	{
@@ -1151,11 +1114,11 @@ void LLFace::cacheFaceInVRAM(const LLVolumeFace& vf)
 	buff->allocateBuffer(vf.mNumVertices, 0, true);
 
 	LLStrider<LLVector4a> f_vert;
-	LLStrider<LLVector3> f_binorm;
+	LLStrider<LLVector4a> f_tangent;
 	LLStrider<LLVector3> f_norm;
 	LLStrider<LLVector2> f_tc;
 
-	buff->getBinormalStrider(f_binorm);
+	buff->getTangentStrider(f_tangent);
 	buff->getVertexStrider(f_vert);
 	buff->getNormalStrider(f_norm);
 	buff->getTexCoord0Strider(f_tc);
@@ -1163,7 +1126,7 @@ void LLFace::cacheFaceInVRAM(const LLVolumeFace& vf)
 	for (U32 i = 0; i < vf.mNumVertices; ++i)
 	{
 		*f_vert++ = vf.mPositions[i];
-		(*f_binorm++).set(vf.mBinormals[i].getF32ptr());
+		*f_tangent++ = vf.mTangents[i];
 		*f_tc++ = vf.mTexCoords[i];
 		(*f_norm++).set(vf.mNormals[i].getF32ptr());
 	}
@@ -1205,7 +1168,7 @@ static LLFastTimer::DeclareTimer FTM_FACE_GEOM_TEXTURE("Texture");
 static LLFastTimer::DeclareTimer FTM_FACE_GEOM_COLOR("Color");
 static LLFastTimer::DeclareTimer FTM_FACE_GEOM_EMISSIVE("Emissive");
 static LLFastTimer::DeclareTimer FTM_FACE_GEOM_WEIGHTS("Weights");
-static LLFastTimer::DeclareTimer FTM_FACE_GEOM_BINORMAL("Binormal");
+static LLFastTimer::DeclareTimer FTM_FACE_GEOM_TANGENT("Binormal");
 static LLFastTimer::DeclareTimer FTM_FACE_GEOM_INDEX("Index");
 static LLFastTimer::DeclareTimer FTM_FACE_GEOM_INDEX_TAIL("Tail");
 static LLFastTimer::DeclareTimer FTM_FACE_POSITION_STORE("Pos");
@@ -1271,7 +1234,7 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
 	LLStrider<LLVector2> tex_coords2;
 	LLStrider<LLVector3> norm;
 	LLStrider<LLColor4U> colors;
-	LLStrider<LLVector3> binorm;
+	LLStrider<LLVector3> tangent;
 	LLStrider<U16> indicesp;
 	LLStrider<LLVector4> wght;
 
@@ -1293,7 +1256,7 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
 	bool rebuild_emissive = rebuild_color && mVertexBuffer->hasDataType(LLVertexBuffer::TYPE_EMISSIVE);
 	bool rebuild_tcoord = full_rebuild || mDrawablep->isState(LLDrawable::REBUILD_TCOORD);
 	bool rebuild_normal = rebuild_pos && mVertexBuffer->hasDataType(LLVertexBuffer::TYPE_NORMAL);
-	bool rebuild_binormal = rebuild_pos && mVertexBuffer->hasDataType(LLVertexBuffer::TYPE_BINORMAL);
+	bool rebuild_tangent = rebuild_pos && mVertexBuffer->hasDataType(LLVertexBuffer::TYPE_TANGENT);
 	bool rebuild_weights = rebuild_pos && mVertexBuffer->hasDataType(LLVertexBuffer::TYPE_WEIGHT4);
 
 	const LLTextureEntry *tep = mVObjp->getTE(f);
@@ -1441,7 +1404,7 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
 
 		if (vf.mVertexBuffer.isNull() || buff->getNumVerts() != vf.mNumVertices)
 		{
-			mVObjp->getVolume()->genBinormals(f);
+			mVObjp->getVolume()->genTangents(f);
 			LLFace::cacheFaceInVRAM(vf);
 			buff = (LLVertexBuffer*) vf.mVertexBuffer.get();
 		}		
@@ -1526,15 +1489,15 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
 			glEndTransformFeedback();
 		}
 
-		if (rebuild_binormal)
+		if (rebuild_tangent)
 		{
-			LLFastTimer t(FTM_FACE_GEOM_BINORMAL);
-			gTransformBinormalProgram.bind();
+			LLFastTimer t(FTM_FACE_GEOM_TANGENT);
+			gTransformTangentProgram.bind();
 			
-			mVertexBuffer->bindForFeedback(0, LLVertexBuffer::TYPE_BINORMAL, mGeomIndex, mGeomCount);
+			mVertexBuffer->bindForFeedback(0, LLVertexBuffer::TYPE_TANGENT, mGeomIndex, mGeomCount);
 						
 			glBeginTransformFeedback(GL_POINTS);
-			buff->setBuffer(LLVertexBuffer::MAP_BINORMAL);
+			buff->setBuffer(LLVertexBuffer::MAP_TANGENT);
 			push_for_transform(buff, vf.mNumVertices, mGeomCount);
 			glEndTransformFeedback();
 		}
@@ -1596,7 +1559,7 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
 		
 			if (bump_code)
 			{
-				mVObjp->getVolume()->genBinormals(f);
+				mVObjp->getVolume()->genTangents(f);
 				F32 offset_multiple; 
 				switch( bump_code )
 				{
@@ -1645,7 +1608,7 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
 			U8 texgen = getTextureEntry()->getTexGen();
 			if (rebuild_tcoord && texgen != LLTextureEntry::TEX_GEN_DEFAULT)
 			{ //planar texgen needs binormals
-				mVObjp->getVolume()->genBinormals(f);
+				mVObjp->getVolume()->genTangents(f);
 			}
 
 			U8 tex_mode = 0;
@@ -1887,20 +1850,10 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
 				
 							vec.mul(scalea);
 
-							switch (texgen)
+							if (texgen == LLTextureEntry::TEX_GEN_PLANAR)
 							{
-								case LLTextureEntry::TEX_GEN_PLANAR:
-									planarProjection(tc, norm, center, vec);
-									break;
-								case LLTextureEntry::TEX_GEN_SPHERICAL:
-									sphericalProjection(tc, norm, center, vec);
-									break;
-								case LLTextureEntry::TEX_GEN_CYLINDRICAL:
-									cylindricalProjection(tc, norm, center, vec);
-									break;
-								default:
-									break;
-							}		
+								planarProjection(tc, norm, center, vec);
+							}
 						}
 
 						if (tex_mode && mTextureMatrix)
@@ -1934,11 +1887,14 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
 		
 					for (S32 i = 0; i < num_vertices; i++)
 					{
-						LLVector4a tangent;
-						tangent.setCross3(vf.mBinormals[i], vf.mNormals[i]);
+						LLVector4a tangent = vf.mTangents[i];
 
+						LLVector4a binorm;
+						binorm.setCross3(vf.mNormals[i], tangent);
+						binorm.mul(tangent.getF32ptr()[3]);
+						
 						LLMatrix4a tangent_to_object;
-						tangent_to_object.setRows(tangent, vf.mBinormals[i], vf.mNormals[i]);
+						tangent_to_object.setRows(tangent, binorm, vf.mNormals[i]);
 						LLVector4a t;
 						tangent_to_object.rotate(binormal_dir, t);
 						LLVector4a binormal;
@@ -2056,21 +2012,28 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
 			}
 		}
 		
-		if (rebuild_binormal)
+		if (rebuild_tangent)
 		{
-			LLFastTimer t(FTM_FACE_GEOM_BINORMAL);
-			mVertexBuffer->getBinormalStrider(binorm, mGeomIndex, mGeomCount, map_range);
-			F32* binormals = (F32*) binorm.get();
+			LLFastTimer t(FTM_FACE_GEOM_TANGENT);
+			mVertexBuffer->getTangentStrider(tangent, mGeomIndex, mGeomCount, map_range);
+			F32* tangents = (F32*) tangent.get();
 			
-			mVObjp->getVolume()->genBinormals(f);
+			mVObjp->getVolume()->genTangents(f);
 			
+			LLVector4Logical mask;
+			mask.clear();
+			mask.setElement<3>();
+
 			for (S32 i = 0; i < num_vertices; i++)
 			{
-				LLVector4a binormal;
-				mat_normal.rotate(vf.mBinormals[i], binormal);
-				binormal.normalize3fast();
-				binormal.store4a(binormals);
-				binormals += 4;
+				LLVector4a tangent_out;
+				mat_normal.rotate(vf.mTangents[i], tangent_out);
+				tangent_out.normalize3fast();
+				
+				tangent_out.setSelectWithMask(mask, vf.mTangents[i], tangent_out);
+				tangent_out.store4a(tangents);
+				
+				tangents += 4;
 			}
 
 			if (map_range)
diff --git a/indra/newview/llface.h b/indra/newview/llface.h
index 453d2c23d4..0687544d53 100755
--- a/indra/newview/llface.h
+++ b/indra/newview/llface.h
@@ -118,7 +118,7 @@ public:
 	LLXformMatrix*	getXform()			const	{ return mXform; }
 	BOOL			hasGeometry()		const	{ return mGeomCount > 0; }
 	LLVector3		getPositionAgent()	const;
-	LLVector2       surfaceToTexture(LLVector2 surface_coord, LLVector3 position, LLVector3 normal);
+	LLVector2       surfaceToTexture(LLVector2 surface_coord, const LLVector4a& position, const LLVector4a& normal);
 	void 			getPlanarProjectedParams(LLQuaternion* face_rot, LLVector3* face_pos, F32* scale) const;
 	bool			calcAlignedPlanarTE(const LLFace* align_to, LLVector2* st_offset,
 										LLVector2* st_scale, F32* st_rot) const;
diff --git a/indra/newview/llhudicon.cpp b/indra/newview/llhudicon.cpp
index 7e1025c41b..825c2b31be 100755
--- a/indra/newview/llhudicon.cpp
+++ b/indra/newview/llhudicon.cpp
@@ -202,7 +202,7 @@ void LLHUDIcon::render()
 	renderIcon(FALSE);
 }
 
-BOOL LLHUDIcon::lineSegmentIntersect(const LLVector3& start, const LLVector3& end, LLVector3* intersection)
+BOOL LLHUDIcon::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, LLVector4a* intersection)
 {
 	if (mHidden)
 		return FALSE;
@@ -275,23 +275,18 @@ BOOL LLHUDIcon::lineSegmentIntersect(const LLVector3& start, const LLVector3& en
 	LLVector4a upper_right;
 	upper_right.setAdd(lower_right, y_scalea);
 
-	LLVector4a enda;
-	enda.load3(end.mV);
-	LLVector4a starta;
-	starta.load3(start.mV);
 	LLVector4a dir;
-	dir.setSub(enda, starta);
+	dir.setSub(end, start);
 
 	F32 a,b,t;
 
-	if (LLTriangleRayIntersect(upper_right, upper_left, lower_right, starta, dir, a,b,t) ||
-		LLTriangleRayIntersect(upper_left, lower_left, lower_right, starta, dir, a,b,t))
+	if (LLTriangleRayIntersect(upper_right, upper_left, lower_right, start, dir, a,b,t) ||
+		LLTriangleRayIntersect(upper_left, lower_left, lower_right, start, dir, a,b,t))
 	{
 		if (intersection)
 		{
 			dir.mul(t);
-			starta.add(dir);
-			*intersection = LLVector3(starta.getF32ptr());
+			intersection->setAdd(start, dir);
 		}
 		return TRUE;
 	}
@@ -331,12 +326,12 @@ LLHUDIcon* LLHUDIcon::handlePick(S32 pick_id)
 }
 
 //static
-LLHUDIcon* LLHUDIcon::lineSegmentIntersectAll(const LLVector3& start, const LLVector3& end, LLVector3* intersection)
+LLHUDIcon* LLHUDIcon::lineSegmentIntersectAll(const LLVector4a& start, const LLVector4a& end, LLVector4a* intersection)
 {
 	icon_instance_t::iterator icon_it;
 
-	LLVector3 local_end = end;
-	LLVector3 position;
+	LLVector4a local_end = end;
+	LLVector4a position;
 
 	LLHUDIcon* ret = NULL;
 	for(icon_it = sIconInstances.begin(); icon_it != sIconInstances.end(); ++icon_it)
diff --git a/indra/newview/llhudicon.h b/indra/newview/llhudicon.h
index 644daa0299..557252ab0b 100755
--- a/indra/newview/llhudicon.h
+++ b/indra/newview/llhudicon.h
@@ -62,7 +62,7 @@ public:
 
 	static S32 generatePickIDs(S32 start_id, S32 step_size);
 	static LLHUDIcon* handlePick(S32 pick_id);
-	static LLHUDIcon* lineSegmentIntersectAll(const LLVector3& start, const LLVector3& end, LLVector3* intersection);
+	static LLHUDIcon* lineSegmentIntersectAll(const LLVector4a& start, const LLVector4a& end, LLVector4a* intersection);
 
 	static void updateAll();
 	static void cleanupDeadIcons();
@@ -73,7 +73,7 @@ public:
 	BOOL getHidden() const { return mHidden; }
 	void setHidden( BOOL hide ) { mHidden = hide; }
 
-	BOOL lineSegmentIntersect(const LLVector3& start, const LLVector3& end, LLVector3* intersection);
+	BOOL lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, LLVector4a* intersection);
 
 protected:
 	LLHUDIcon(const U8 type);
diff --git a/indra/newview/llhudnametag.cpp b/indra/newview/llhudnametag.cpp
index 3336097955..c3b49f739a 100755
--- a/indra/newview/llhudnametag.cpp
+++ b/indra/newview/llhudnametag.cpp
@@ -116,7 +116,7 @@ LLHUDNameTag::~LLHUDNameTag()
 }
 
 
-BOOL LLHUDNameTag::lineSegmentIntersect(const LLVector3& start, const LLVector3& end, LLVector3& intersection, BOOL debug_render)
+BOOL LLHUDNameTag::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, LLVector4a& intersection, BOOL debug_render)
 {
 	if (!mVisible || mHidden)
 	{
@@ -199,15 +199,23 @@ BOOL LLHUDNameTag::lineSegmentIntersect(const LLVector3& start, const LLVector3&
 		bg_pos + height_vec,
 	};
 
-	LLVector3 dir = end-start;
+	LLVector4a dir;
+	dir.setSub(end,start);
 	F32 a, b, t;
 
-	if (LLTriangleRayIntersect(v[0], v[1], v[2], start, dir, a, b, t, FALSE) ||
-		LLTriangleRayIntersect(v[2], v[3], v[0], start, dir, a, b, t, FALSE) )
+	LLVector4a v0,v1,v2,v3;
+	v0.load3(v[0].mV);
+	v1.load3(v[1].mV);
+	v2.load3(v[2].mV);
+	v3.load3(v[3].mV);
+
+	if (LLTriangleRayIntersect(v0, v1, v2, start, dir, a, b, t) ||
+		LLTriangleRayIntersect(v2, v3, v0, start, dir, a, b, t) )
 	{
 		if (t <= 1.f)
 		{
-			intersection = start + dir*t;
+			dir.mul(t);
+			intersection.setAdd(start, dir);
 			return TRUE;
 		}
 	}
diff --git a/indra/newview/llhudnametag.h b/indra/newview/llhudnametag.h
index 72647d5b26..38a4f18415 100755
--- a/indra/newview/llhudnametag.h
+++ b/indra/newview/llhudnametag.h
@@ -124,7 +124,7 @@ public:
 	void setHidden( BOOL hide ) { mHidden = hide; }
 	void shift(const LLVector3& offset);
 
-	BOOL lineSegmentIntersect(const LLVector3& start, const LLVector3& end, LLVector3& intersection, BOOL debug_render = FALSE);
+	BOOL lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, LLVector4a& intersection, BOOL debug_render = FALSE);
 
 	static void shiftAll(const LLVector3& offset);
 	static void addPickable(std::set<LLViewerObject*> &pick_list);
diff --git a/indra/newview/llspatialpartition.cpp b/indra/newview/llspatialpartition.cpp
index 252f129133..78401020a6 100755
--- a/indra/newview/llspatialpartition.cpp
+++ b/indra/newview/llspatialpartition.cpp
@@ -3039,9 +3039,9 @@ void renderNormals(LLDrawable* drawablep)
 				gGL.vertex3fv(face.mPositions[j].getF32ptr());
 				gGL.vertex3fv(p.getF32ptr());
 				
-				if (face.mBinormals)
+				if (face.mTangents)
 				{
-					n.setMul(face.mBinormals[j], scale);
+					n.setMul(face.mTangents[j], scale);
 					p.setAdd(face.mPositions[j], n);
 				
 					gGL.diffuseColor4f(0,1,1,1);
@@ -3888,11 +3888,17 @@ void renderRaycast(LLDrawable* drawablep)
 					gGL.translatef(trans.mV[0], trans.mV[1], trans.mV[2]);					
 					gGL.multMatrix((F32*) vobj->getRelativeXform().mMatrix);
 
-					LLVector3 start, end;
+					LLVector4a start, end;
 					if (transform)
 					{
-						start = vobj->agentPositionToVolume(gDebugRaycastStart);
-						end = vobj->agentPositionToVolume(gDebugRaycastEnd);
+						LLVector3 v_start(gDebugRaycastStart.getF32ptr());
+						LLVector3 v_end(gDebugRaycastEnd.getF32ptr());
+
+						v_start = vobj->agentPositionToVolume(v_start);
+						v_end = vobj->agentPositionToVolume(v_end);
+
+						start.load3(v_start.mV);
+						end.load3(v_end.mV);
 					}
 					else
 					{
@@ -3900,11 +3906,8 @@ void renderRaycast(LLDrawable* drawablep)
 						end = gDebugRaycastEnd;
 					}
 
-					LLVector4a starta, enda;
-					starta.load3(start.mV);
-					enda.load3(end.mV);
 					LLVector4a dir;
-					dir.setSub(enda, starta);
+					dir.setSub(end, start);
 
 					gGL.flush();
 					glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);				
@@ -3927,7 +3930,7 @@ void renderRaycast(LLDrawable* drawablep)
 							((LLVolumeFace*) &face)->createOctree(); 
 						}
 
-						LLRenderOctreeRaycast render(starta, dir, &t);
+						LLRenderOctreeRaycast render(start, dir, &t);
 					
 						render.traverse(face.mOctree);
 					}
@@ -3952,10 +3955,18 @@ void renderRaycast(LLDrawable* drawablep)
 			// draw intersection point
 			gGL.pushMatrix();
 			gGL.loadMatrix(gGLModelView);
-			LLVector3 translate = gDebugRaycastIntersection;
+			LLVector3 translate(gDebugRaycastIntersection.getF32ptr());
 			gGL.translatef(translate.mV[0], translate.mV[1], translate.mV[2]);
 			LLCoordFrame orient;
-			orient.lookDir(gDebugRaycastNormal, gDebugRaycastBinormal);
+			LLVector4a debug_binormal;
+			
+			debug_binormal.setCross3(gDebugRaycastNormal, gDebugRaycastTangent);
+			debug_binormal.mul(gDebugRaycastTangent.getF32ptr()[3]);
+
+			LLVector3 normal(gDebugRaycastNormal.getF32ptr());
+			LLVector3 binormal(debug_binormal.getF32ptr());
+						
+			orient.lookDir(normal, binormal);
 			LLMatrix4 rotation;
 			orient.getRotMatrixToParent(rotation);
 			gGL.multMatrix((float*)rotation.mMatrix);
@@ -4457,28 +4468,30 @@ BOOL LLSpatialPartition::isVisible(const LLVector3& v)
 	return TRUE;
 }
 
+LL_ALIGN_PREFIX(16)
 class LLOctreeIntersect : public LLSpatialGroup::OctreeTraveler
 {
 public:
-	LLVector3 mStart;
-	LLVector3 mEnd;
+	LL_ALIGN_16(LLVector4a mStart);
+	LL_ALIGN_16(LLVector4a mEnd);
+
 	S32       *mFaceHit;
-	LLVector3 *mIntersection;
+	LLVector4a *mIntersection;
 	LLVector2 *mTexCoord;
-	LLVector3 *mNormal;
-	LLVector3 *mBinormal;
+	LLVector4a *mNormal;
+	LLVector4a *mTangent;
 	LLDrawable* mHit;
 	BOOL mPickTransparent;
 
-	LLOctreeIntersect(LLVector3 start, LLVector3 end, BOOL pick_transparent,
-					  S32* face_hit, LLVector3* intersection, LLVector2* tex_coord, LLVector3* normal, LLVector3* binormal)
+	LLOctreeIntersect(const LLVector4a& start, const LLVector4a& end, BOOL pick_transparent,
+					  S32* face_hit, LLVector4a* intersection, LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent)
 		: mStart(start),
 		  mEnd(end),
 		  mFaceHit(face_hit),
 		  mIntersection(intersection),
 		  mTexCoord(tex_coord),
 		  mNormal(normal),
-		  mBinormal(binormal),
+		  mTangent(tangent),
 		  mHit(NULL),
 		  mPickTransparent(pick_transparent)
 	{
@@ -4509,23 +4522,22 @@ public:
 			size = group->mBounds[1];
 			center = group->mBounds[0];
 			
-			LLVector3 local_start = mStart;
-			LLVector3 local_end   = mEnd;
+			LLVector4a local_start = mStart;
+			LLVector4a 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;
-			}
 
-			LLVector4a start, end;
-			start.load3(local_start.mV);
-			end.load3(local_end.mV);
+				LLMatrix4a local_matrix4a;
+				local_matrix4a.loadu(local_matrix);
 
-			if (LLLineSegmentBoxIntersect(start, end, center, size))
+				local_matrix4a.affineTransform(mStart, local_start);
+				local_matrix4a.affineTransform(mEnd, local_end);
+			}
+
+			if (LLLineSegmentBoxIntersect(local_start, local_end, center, size))
 			{
 				check(child);
 			}
@@ -4556,14 +4568,14 @@ public:
 
 			if (vobj)
 			{
-				LLVector3 intersection;
+				LLVector4a intersection;
 				bool skip_check = false;
 				if (vobj->isAvatar())
 				{
 					LLVOAvatar* avatar = (LLVOAvatar*) vobj;
 					if (avatar->isSelf() && LLFloater::isVisible(gFloaterTools))
 					{
-						LLViewerObject* hit = avatar->lineSegmentIntersectRiggedAttachments(mStart, mEnd, -1, mPickTransparent, mFaceHit, &intersection, mTexCoord, mNormal, mBinormal);
+						LLViewerObject* hit = avatar->lineSegmentIntersectRiggedAttachments(mStart, mEnd, -1, mPickTransparent, mFaceHit, &intersection, mTexCoord, mNormal, mTangent);
 						if (hit)
 						{
 							mEnd = intersection;
@@ -4579,7 +4591,7 @@ public:
 					}
 				}
 
-				if (!skip_check && vobj->lineSegmentIntersect(mStart, mEnd, -1, mPickTransparent, mFaceHit, &intersection, mTexCoord, mNormal, mBinormal))
+				if (!skip_check && vobj->lineSegmentIntersect(mStart, mEnd, -1, mPickTransparent, mFaceHit, &intersection, mTexCoord, mNormal, mTangent))
 				{
 					mEnd = intersection;  // shorten ray so we only find CLOSER hits
 					if (mIntersection)
@@ -4594,19 +4606,19 @@ public:
 				
 		return false;
 	}
-};
+} LL_ALIGN_POSTFIX(16);
 
-LLDrawable* LLSpatialPartition::lineSegmentIntersect(const LLVector3& start, const LLVector3& end,
+LLDrawable* LLSpatialPartition::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end,
 													 BOOL pick_transparent,													
 													 S32* face_hit,                   // return the face hit
-													 LLVector3* intersection,         // return the intersection point
+													 LLVector4a* 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
+													 LLVector4a* normal,               // return the surface normal at the intersection point
+													 LLVector4a* tangent			// return the surface tangent at the intersection point
 	)
 
 {
-	LLOctreeIntersect intersect(start, end, pick_transparent, face_hit, intersection, tex_coord, normal, bi_normal);
+	LLOctreeIntersect intersect(start, end, pick_transparent, face_hit, intersection, tex_coord, normal, tangent);
 	LLDrawable* drawable = intersect.check(mOctree);
 
 	return drawable;
diff --git a/indra/newview/llspatialpartition.h b/indra/newview/llspatialpartition.h
index 8ccc3efd66..9732be90af 100755
--- a/indra/newview/llspatialpartition.h
+++ b/indra/newview/llspatialpartition.h
@@ -204,7 +204,7 @@ public:
 	};
 };
 
-LL_ALIGN_PREFIX(16)
+LL_ALIGN_PREFIX(64)
 class LLSpatialGroup : public LLOctreeListener<LLDrawable>
 {
 	friend class LLSpatialPartition;
@@ -490,13 +490,13 @@ public:
 	LLSpatialGroup *put(LLDrawable *drawablep, BOOL was_visible = FALSE);
 	BOOL remove(LLDrawable *drawablep, LLSpatialGroup *curp);
 	
-	LLDrawable* lineSegmentIntersect(const LLVector3& start, const LLVector3& end,
+	LLDrawable* lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end,
 									 BOOL pick_transparent, 
 									 S32* face_hit,                          // return the face hit
-									 LLVector3* intersection = NULL,         // return the intersection point
+									 LLVector4a* 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
+									 LLVector4a* normal = NULL,               // return the surface normal at the intersection point
+									 LLVector4a* tangent = NULL             // return the surface tangent at the intersection point
 		);
 	
 	
diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp
index 4e233d479a..198b13ee06 100755
--- a/indra/newview/llviewerobject.cpp
+++ b/indra/newview/llviewerobject.cpp
@@ -3821,19 +3821,19 @@ LLViewerObject* LLViewerObject::getRootEdit() const
 }
 
 
-BOOL LLViewerObject::lineSegmentIntersect(const LLVector3& start, const LLVector3& end,
+BOOL LLViewerObject::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end,
 										  S32 face,
 										  BOOL pick_transparent,
 										  S32* face_hit,
-										  LLVector3* intersection,
+										  LLVector4a* intersection,
 										  LLVector2* tex_coord,
-										  LLVector3* normal,
-										  LLVector3* bi_normal)
+										  LLVector4a* normal,
+										  LLVector4a* tangent)
 {
 	return false;
 }
 
-BOOL LLViewerObject::lineSegmentBoundingBox(const LLVector3& start, const LLVector3& end)
+BOOL LLViewerObject::lineSegmentBoundingBox(const LLVector4a& start, const LLVector4a& end)
 {
 	if (mDrawable.isNull() || mDrawable->isDead())
 	{
@@ -3850,11 +3850,7 @@ BOOL LLViewerObject::lineSegmentBoundingBox(const LLVector3& start, const LLVect
 	size.setSub(ext[1], ext[0]);
 	size.mul(0.5f);
 
-	LLVector4a starta, enda;
-	starta.load3(start.mV);
-	enda.load3(end.mV);
-
-	return LLLineSegmentBoxIntersect(starta, enda, center, size);
+	return LLLineSegmentBoxIntersect(start, end, center, size);
 }
 
 U8 LLViewerObject::getMediaType() const
diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h
index bcb74a8d1f..932846c211 100755
--- a/indra/newview/llviewerobject.h
+++ b/indra/newview/llviewerobject.h
@@ -258,17 +258,17 @@ public:
 
 	//detect if given line segment (in agent space) intersects with this viewer object.
 	//returns TRUE if intersection detected and returns information about intersection
-	virtual BOOL lineSegmentIntersect(const LLVector3& start, const LLVector3& end,
+	virtual BOOL lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end,
 									  S32 face = -1,                          // which face to check, -1 = ALL_SIDES
 									  BOOL pick_transparent = FALSE,
 									  S32* face_hit = NULL,                   // which face was hit
-									  LLVector3* intersection = NULL,         // return the intersection point
+									  LLVector4a* 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
+									  LLVector4a* normal = NULL,               // return the surface normal at the intersection point
+									  LLVector4a* tangent = NULL             // return the surface tangent at the intersection point
 		);
 	
-	virtual BOOL lineSegmentBoundingBox(const LLVector3& start, const LLVector3& end);
+	virtual BOOL lineSegmentBoundingBox(const LLVector4a& start, const LLVector4a& end);
 
 	virtual const LLVector3d getPositionGlobal() const;
 	virtual const LLVector3 &getPositionRegion() const;
diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp
index 5f08e8ca2e..636214290d 100755
--- a/indra/newview/llviewershadermgr.cpp
+++ b/indra/newview/llviewershadermgr.cpp
@@ -68,7 +68,7 @@ LLGLSLShader			gTransformPositionProgram;
 LLGLSLShader			gTransformTexCoordProgram;
 LLGLSLShader			gTransformNormalProgram;
 LLGLSLShader			gTransformColorProgram;
-LLGLSLShader			gTransformBinormalProgram;
+LLGLSLShader			gTransformTangentProgram;
 
 //utility shaders
 LLGLSLShader	gOcclusionProgram;
@@ -789,7 +789,7 @@ void LLViewerShaderMgr::unloadShaders()
 	gTransformTexCoordProgram.unload();
 	gTransformNormalProgram.unload();
 	gTransformColorProgram.unload();
-	gTransformBinormalProgram.unload();
+	gTransformTangentProgram.unload();
 
 	mVertexShaderLevel[SHADER_LIGHTING] = 0;
 	mVertexShaderLevel[SHADER_OBJECT] = 0;
@@ -3043,7 +3043,7 @@ BOOL LLViewerShaderMgr::loadTransformShaders()
 		gTransformTexCoordProgram.unload();
 		gTransformNormalProgram.unload();
 		gTransformColorProgram.unload();
-		gTransformBinormalProgram.unload();
+		gTransformTangentProgram.unload();
 		return TRUE;
 	}
 
@@ -3106,16 +3106,16 @@ BOOL LLViewerShaderMgr::loadTransformShaders()
 
 	if (success)
 	{
-		gTransformBinormalProgram.mName = "Binormal Transform Shader";
-		gTransformBinormalProgram.mShaderFiles.clear();
-		gTransformBinormalProgram.mShaderFiles.push_back(make_pair("transform/binormalV.glsl", GL_VERTEX_SHADER_ARB));
-		gTransformBinormalProgram.mShaderLevel = mVertexShaderLevel[SHADER_TRANSFORM];
+		gTransformTangentProgram.mName = "Binormal Transform Shader";
+		gTransformTangentProgram.mShaderFiles.clear();
+		gTransformTangentProgram.mShaderFiles.push_back(make_pair("transform/binormalV.glsl", GL_VERTEX_SHADER_ARB));
+		gTransformTangentProgram.mShaderLevel = mVertexShaderLevel[SHADER_TRANSFORM];
 
 		const char* varyings[] = {
-			"binormal_out",
+			"tangent_out",
 		};
 	
-		success = gTransformBinormalProgram.createShader(NULL, NULL, 1, varyings);
+		success = gTransformTangentProgram.createShader(NULL, NULL, 1, varyings);
 	}
 
 	
diff --git a/indra/newview/llviewershadermgr.h b/indra/newview/llviewershadermgr.h
index 702a92c69c..8c7de05062 100755
--- a/indra/newview/llviewershadermgr.h
+++ b/indra/newview/llviewershadermgr.h
@@ -217,7 +217,7 @@ extern LLGLSLShader			gTransformPositionProgram;
 extern LLGLSLShader			gTransformTexCoordProgram;
 extern LLGLSLShader			gTransformNormalProgram;
 extern LLGLSLShader			gTransformColorProgram;
-extern LLGLSLShader			gTransformBinormalProgram;
+extern LLGLSLShader			gTransformTangentProgram;
 
 
 
diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
index fe4d5b3e4d..65a906d3c0 100755
--- a/indra/newview/llviewerwindow.cpp
+++ b/indra/newview/llviewerwindow.cpp
@@ -229,13 +229,13 @@ LLFrameTimer	gAwayTriggerTimer;
 BOOL			gShowOverlayTitle = FALSE;
 
 LLViewerObject*  gDebugRaycastObject = NULL;
-LLVector3       gDebugRaycastIntersection;
-LLVector2       gDebugRaycastTexCoord;
-LLVector3       gDebugRaycastNormal;
-LLVector3       gDebugRaycastBinormal;
-S32				gDebugRaycastFaceHit;
-LLVector3		gDebugRaycastStart;
-LLVector3		gDebugRaycastEnd;
+LLVector4a       gDebugRaycastIntersection;
+LLVector2        gDebugRaycastTexCoord;
+LLVector4a       gDebugRaycastNormal;
+LLVector4a       gDebugRaycastTangent;
+S32				 gDebugRaycastFaceHit;
+LLVector4a		 gDebugRaycastStart;
+LLVector4a		 gDebugRaycastEnd;
 
 // HUD display lines in lower right
 BOOL				gDisplayWindInfo = FALSE;
@@ -2841,7 +2841,7 @@ void LLViewerWindow::updateUI()
 											  &gDebugRaycastIntersection,
 											  &gDebugRaycastTexCoord,
 											  &gDebugRaycastNormal,
-											  &gDebugRaycastBinormal,
+											  &gDebugRaycastTangent,
 											  &gDebugRaycastStart,
 											  &gDebugRaycastEnd);
 	}
@@ -3739,7 +3739,7 @@ LLPickInfo LLViewerWindow::pickImmediate(S32 x, S32 y_from_bot,  BOOL pick_trans
 }
 
 LLHUDIcon* LLViewerWindow::cursorIntersectIcon(S32 mouse_x, S32 mouse_y, F32 depth,
-										   LLVector3* intersection)
+										   LLVector4a* intersection)
 {
 	S32 x = mouse_x;
 	S32 y = mouse_y;
@@ -3751,14 +3751,17 @@ LLHUDIcon* LLViewerWindow::cursorIntersectIcon(S32 mouse_x, S32 mouse_y, F32 dep
 	}
 
 	// world coordinates of mouse
+	// VECTORIZE THIS
 	LLVector3 mouse_direction_global = mouseDirectionGlobal(x,y);
 	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;
 
-	return LLHUDIcon::lineSegmentIntersectAll(mouse_world_start, mouse_world_end, intersection);
-
+	LLVector4a start, end;
+	start.load3(mouse_world_start.mV);
+	end.load3(mouse_world_end.mV);
 	
+	return LLHUDIcon::lineSegmentIntersectAll(start, end, intersection);
 }
 
 LLViewerObject* LLViewerWindow::cursorIntersect(S32 mouse_x, S32 mouse_y, F32 depth,
@@ -3766,12 +3769,12 @@ LLViewerObject* LLViewerWindow::cursorIntersect(S32 mouse_x, S32 mouse_y, F32 de
 												S32 this_face,
 												BOOL pick_transparent,
 												S32* face_hit,
-												LLVector3 *intersection,
+												LLVector4a *intersection,
 												LLVector2 *uv,
-												LLVector3 *normal,
-												LLVector3 *binormal,
-												LLVector3* start,
-												LLVector3* end)
+												LLVector4a *normal,
+												LLVector4a *tangent,
+												LLVector4a* start,
+												LLVector4a* end)
 {
 	S32 x = mouse_x;
 	S32 y = mouse_y;
@@ -3806,17 +3809,27 @@ LLViewerObject* LLViewerWindow::cursorIntersect(S32 mouse_x, S32 mouse_y, F32 de
 	if (!LLViewerJoystick::getInstance()->getOverrideCamera())
 	{ //always set raycast intersection to mouse_world_end unless
 		//flycam is on (for DoF effect)
-		gDebugRaycastIntersection = mouse_world_end;
+		gDebugRaycastIntersection.load3(mouse_world_end.mV);
 	}
 
+	LLVector4a mw_start;
+	mw_start.load3(mouse_world_start.mV);
+	LLVector4a mw_end;
+	mw_end.load3(mouse_world_end.mV);
+
+	LLVector4a mh_start;
+	mh_start.load3(mouse_hud_start.mV);
+	LLVector4a mh_end;
+	mh_end.load3(mouse_hud_end.mV);
+
 	if (start)
 	{
-		*start = mouse_world_start;
+		*start = mw_start;
 	}
 
 	if (end)
 	{
-		*end = mouse_world_end;
+		*end = mw_end;
 	}
 
 	LLViewerObject* found = NULL;
@@ -3825,16 +3838,16 @@ LLViewerObject* LLViewerWindow::cursorIntersect(S32 mouse_x, S32 mouse_y, F32 de
 	{
 		if (this_object->isHUDAttachment()) // is a HUD object?
 		{
-			if (this_object->lineSegmentIntersect(mouse_hud_start, mouse_hud_end, this_face, pick_transparent,
-												  face_hit, intersection, uv, normal, binormal))
+			if (this_object->lineSegmentIntersect(mh_start, mh_end, this_face, pick_transparent,
+												  face_hit, intersection, uv, normal, tangent))
 			{
 				found = this_object;
 			}
 		}
 		else // is a world object
 		{
-			if (this_object->lineSegmentIntersect(mouse_world_start, mouse_world_end, this_face, pick_transparent,
-												  face_hit, intersection, uv, normal, binormal))
+			if (this_object->lineSegmentIntersect(mw_start, mw_end, this_face, pick_transparent,
+												  face_hit, intersection, uv, normal, tangent))
 			{
 				found = this_object;
 			}
@@ -3842,20 +3855,20 @@ LLViewerObject* LLViewerWindow::cursorIntersect(S32 mouse_x, S32 mouse_y, F32 de
 	}
 	else // check ALL objects
 	{
-		found = gPipeline.lineSegmentIntersectInHUD(mouse_hud_start, mouse_hud_end, pick_transparent,
-													face_hit, intersection, uv, normal, binormal);
+		found = gPipeline.lineSegmentIntersectInHUD(mh_start, mh_end, pick_transparent,
+													face_hit, intersection, uv, normal, tangent);
 
 		if (!found) // if not found in HUD, look in world:
 		{
-			found = gPipeline.lineSegmentIntersectInWorld(mouse_world_start, mouse_world_end, pick_transparent,
-														  face_hit, intersection, uv, normal, binormal);
+			found = gPipeline.lineSegmentIntersectInWorld(mw_start, mw_end, pick_transparent,
+														  face_hit, intersection, uv, normal, tangent);
 			if (found && !pick_transparent)
 			{
 				gDebugRaycastIntersection = *intersection;
 			}
 		}
 	}
-
+		
 	return found;
 }
 
@@ -5112,6 +5125,7 @@ LLPickInfo::LLPickInfo()
 	  mXYCoords(-1, -1),
 	  mIntersection(),
 	  mNormal(),
+	  mTangent(),
 	  mBinormal(),
 	  mHUDIcon(NULL),
 	  mPickTransparent(FALSE)
@@ -5133,6 +5147,7 @@ LLPickInfo::LLPickInfo(const LLCoordGL& mouse_pos,
 	  mSTCoords(-1.f, -1.f),
 	  mXYCoords(-1, -1),
 	  mNormal(),
+	  mTangent(),
 	  mBinormal(),
 	  mHUDIcon(NULL),
 	  mPickTransparent(pick_transparent)
@@ -5143,19 +5158,26 @@ void LLPickInfo::fetchResults()
 {
 
 	S32 face_hit = -1;
-	LLVector3 intersection, normal, binormal;
+	LLVector4a intersection, normal;
+	LLVector4a tangent;
+
 	LLVector2 uv;
 
 	LLHUDIcon* hit_icon = gViewerWindow->cursorIntersectIcon(mMousePt.mX, mMousePt.mY, 512.f, &intersection);
 	
+	LLVector4a origin;
+	origin.load3(LLViewerCamera::getInstance()->getOrigin().mV);
 	F32 icon_dist = 0.f;
 	if (hit_icon)
 	{
-		icon_dist = (LLViewerCamera::getInstance()->getOrigin()-intersection).magVec();
+		LLVector4a delta;
+		delta.setSub(intersection, origin);
+		icon_dist = delta.getLength3().getF32();
 	}
+
 	LLViewerObject* hit_object = gViewerWindow->cursorIntersect(mMousePt.mX, mMousePt.mY, 512.f,
 									NULL, -1, mPickTransparent, &face_hit,
-									&intersection, &uv, &normal, &binormal);
+									&intersection, &uv, &normal, &tangent);
 	
 	mPickPt = mMousePt;
 
@@ -5165,9 +5187,13 @@ void LLPickInfo::fetchResults()
 	
 	LLViewerObject* objectp = hit_object;
 
+
+	LLVector4a delta;
+	delta.setSub(origin, intersection);
+
 	if (hit_icon && 
 		(!objectp || 
-		icon_dist < (LLViewerCamera::getInstance()->getOrigin()-intersection).magVec()))
+		icon_dist < delta.getLength3().getF32()))
 	{
 		// was this name referring to a hud icon?
 		mHUDIcon = hit_icon;
@@ -5204,11 +5230,16 @@ void LLPickInfo::fetchResults()
 			{
 				mPickType = PICK_OBJECT;
 			}
-			mObjectOffset = gAgentCamera.calcFocusOffset(objectp, intersection, mPickPt.mX, mPickPt.mY);
+
+			LLVector3 v_intersection(intersection.getF32ptr());
+
+			mObjectOffset = gAgentCamera.calcFocusOffset(objectp, v_intersection, mPickPt.mX, mPickPt.mY);
 			mObjectID = objectp->mID;
 			mObjectFace = (te_offset == NO_FACE) ? -1 : (S32)te_offset;
 
-			mPosGlobal = gAgent.getPosGlobalFromAgent(intersection);
+			
+
+			mPosGlobal = gAgent.getPosGlobalFromAgent(v_intersection);
 			
 			if (mWantSurfaceInfo)
 			{
@@ -5252,7 +5283,16 @@ void LLPickInfo::getSurfaceInfo()
 	mIntersection = LLVector3(0,0,0);
 	mNormal       = LLVector3(0,0,0);
 	mBinormal     = LLVector3(0,0,0);
+	mTangent	  = LLVector4(0,0,0,0);
 	
+	LLVector4a tangent;
+	LLVector4a intersection;
+	LLVector4a normal;
+
+	tangent.clear();
+	normal.clear();
+	intersection.clear();
+
 	LLViewerObject* objectp = getObject();
 
 	if (objectp)
@@ -5260,10 +5300,10 @@ void LLPickInfo::getSurfaceInfo()
 		if (gViewerWindow->cursorIntersect(llround((F32)mMousePt.mX), llround((F32)mMousePt.mY), 1024.f,
 										   objectp, -1, mPickTransparent,
 										   &mObjectFace,
-										   &mIntersection,
+										   &intersection,
 										   &mSTCoords,
-										   &mNormal,
-										   &mBinormal))
+										   &normal,
+										   &tangent))
 		{
 			// if we succeeded with the intersect above, compute the texture coordinates:
 
@@ -5272,10 +5312,26 @@ void LLPickInfo::getSurfaceInfo()
 				LLFace* facep = objectp->mDrawable->getFace(mObjectFace);
 				if (facep)
 				{
-				mUVCoords = facep->surfaceToTexture(mSTCoords, mIntersection, mNormal);
-			}
+					mUVCoords = facep->surfaceToTexture(mSTCoords, intersection, normal);
+				}
 			}
 
+			mIntersection.set(intersection.getF32ptr());
+			mNormal.set(normal.getF32ptr());
+			mTangent.set(tangent.getF32ptr());
+
+			//extrapoloate binormal from normal and tangent
+			
+			LLVector4a binormal;
+			binormal.setCross3(normal, tangent);
+			binormal.mul(tangent.getF32ptr()[3]);
+
+			mBinormal.set(binormal.getF32ptr());
+
+			mBinormal.normalize();
+			mNormal.normalize();
+			mTangent.normalize();
+
 			// and XY coords:
 			updateXYCoords();
 			
diff --git a/indra/newview/llviewerwindow.h b/indra/newview/llviewerwindow.h
index b33488fd78..89f6e3bc26 100755
--- a/indra/newview/llviewerwindow.h
+++ b/indra/newview/llviewerwindow.h
@@ -115,6 +115,7 @@ public:
 	LLVector2       mSTCoords;
 	LLCoordScreen	mXYCoords;
 	LLVector3		mNormal;
+	LLVector4		mTangent;
 	LLVector3		mBinormal;
 	BOOL			mPickTransparent;
 	void		    getSurfaceInfo();
@@ -357,19 +358,19 @@ public:
 	void			pickAsync(S32 x, S32 y_from_bot, MASK mask, void (*callback)(const LLPickInfo& pick_info), BOOL pick_transparent = FALSE);
 	LLPickInfo		pickImmediate(S32 x, S32 y, BOOL pick_transparent);
 	LLHUDIcon* cursorIntersectIcon(S32 mouse_x, S32 mouse_y, F32 depth,
-										   LLVector3* intersection);
+										   LLVector4a* intersection);
 
 	LLViewerObject* cursorIntersect(S32 mouse_x = -1, S32 mouse_y = -1, F32 depth = 512.f,
 									LLViewerObject *this_object = NULL,
 									S32 this_face = -1,
 									BOOL pick_transparent = FALSE,
 									S32* face_hit = NULL,
-									LLVector3 *intersection = NULL,
+									LLVector4a *intersection = NULL,
 									LLVector2 *uv = NULL,
-									LLVector3 *normal = NULL,
-									LLVector3 *binormal = NULL,
-									LLVector3* start = NULL,
-									LLVector3* end = NULL);
+									LLVector4a *normal = NULL,
+									LLVector4a *tangent = NULL,
+									LLVector4a* start = NULL,
+									LLVector4a* end = NULL);
 	
 	
 	// Returns a pointer to the last object hit
@@ -499,13 +500,13 @@ extern LLFrameTimer		gAwayTimer;				// tracks time before setting the avatar awa
 extern LLFrameTimer		gAwayTriggerTimer;		// how long the avatar has been away
 
 extern LLViewerObject*  gDebugRaycastObject;
-extern LLVector3        gDebugRaycastIntersection;
+extern LLVector4a       gDebugRaycastIntersection;
 extern LLVector2        gDebugRaycastTexCoord;
-extern LLVector3        gDebugRaycastNormal;
-extern LLVector3        gDebugRaycastBinormal;
+extern LLVector4a       gDebugRaycastNormal;
+extern LLVector4a       gDebugRaycastTangent;
 extern S32				gDebugRaycastFaceHit;
-extern LLVector3		gDebugRaycastStart;
-extern LLVector3		gDebugRaycastEnd;
+extern LLVector4a		gDebugRaycastStart;
+extern LLVector4a		gDebugRaycastEnd;
 
 extern BOOL			gDisplayCameraPos;
 extern BOOL			gDisplayWindInfo;
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index 373a59ad6b..1a050800b4 100755
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -1388,19 +1388,20 @@ void LLVOAvatar::renderCollisionVolumes()
 
 	if (mNameText.notNull())
 	{
-		LLVector3 unused;
-		mNameText->lineSegmentIntersect(LLVector3(0,0,0), LLVector3(0,0,1), unused, TRUE);
+		LLVector4a unused;
+	
+		mNameText->lineSegmentIntersect(unused, unused, unused, TRUE);
 	}
 }
 
-BOOL LLVOAvatar::lineSegmentIntersect(const LLVector3& start, const LLVector3& end,
+BOOL LLVOAvatar::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end,
 									  S32 face,
 									  BOOL pick_transparent,
 									  S32* face_hit,
-									  LLVector3* intersection,
+									  LLVector4a* intersection,
 									  LLVector2* tex_coord,
-									  LLVector3* normal,
-									  LLVector3* bi_normal)
+									  LLVector4a* normal,
+									  LLVector4a* tangent)
 {
 	if ((isSelf() && !gAgent.needsRenderAvatar()) || !LLPipeline::sPickAvatar)
 	{
@@ -1417,8 +1418,8 @@ BOOL LLVOAvatar::lineSegmentIntersect(const LLVector3& start, const LLVector3& e
 			glh::matrix4f inverse = mat.inverse();
 			glh::matrix4f norm_mat = inverse.transpose();
 
-			glh::vec3f p1(start.mV);
-			glh::vec3f p2(end.mV);
+			glh::vec3f p1(start.getF32ptr());
+			glh::vec3f p2(end.getF32ptr());
 
 			inverse.mult_matrix_vec(p1);
 			inverse.mult_matrix_vec(p2);
@@ -1437,12 +1438,12 @@ BOOL LLVOAvatar::lineSegmentIntersect(const LLVector3& start, const LLVector3& e
 
 				if (intersection)
 				{
-					*intersection = LLVector3(res_pos.v);
+					intersection->load3(res_pos.v);
 				}
 
 				if (normal)
 				{
-					*normal = LLVector3(res_norm.v);
+					normal->load3(res_norm.v);
 				}
 
 				return TRUE;
@@ -1478,7 +1479,7 @@ BOOL LLVOAvatar::lineSegmentIntersect(const LLVector3& start, const LLVector3& e
 
 	
 	
-	LLVector3 position;
+	LLVector4a position;
 	if (mNameText.notNull() && mNameText->lineSegmentIntersect(start, end, position))
 	{
 		if (intersection)
@@ -1492,14 +1493,14 @@ BOOL LLVOAvatar::lineSegmentIntersect(const LLVector3& start, const LLVector3& e
 	return FALSE;
 }
 
-LLViewerObject* LLVOAvatar::lineSegmentIntersectRiggedAttachments(const LLVector3& start, const LLVector3& end,
+LLViewerObject* LLVOAvatar::lineSegmentIntersectRiggedAttachments(const LLVector4a& start, const LLVector4a& end,
 									  S32 face,
 									  BOOL pick_transparent,
 									  S32* face_hit,
-									  LLVector3* intersection,
+									  LLVector4a* intersection,
 									  LLVector2* tex_coord,
-									  LLVector3* normal,
-									  LLVector3* bi_normal)
+									  LLVector4a* normal,
+									  LLVector4a* tangent)
 {
 	if (isSelf() && !gAgent.needsRenderAvatar())
 	{
@@ -1510,8 +1511,8 @@ LLViewerObject* LLVOAvatar::lineSegmentIntersectRiggedAttachments(const LLVector
 
 	if (lineSegmentBoundingBox(start, end))
 	{
-		LLVector3 local_end = end;
-		LLVector3 local_intersection;
+		LLVector4a local_end = end;
+		LLVector4a local_intersection;
 
 		for (attachment_map_t::iterator iter = mAttachmentPoints.begin(); 
 			iter != mAttachmentPoints.end();
@@ -1525,7 +1526,7 @@ LLViewerObject* LLVOAvatar::lineSegmentIntersectRiggedAttachments(const LLVector
 			{
 				LLViewerObject* attached_object = (*attachment_iter);
 					
-				if (attached_object->lineSegmentIntersect(start, local_end, face, pick_transparent, face_hit, &local_intersection, tex_coord, normal, bi_normal))
+				if (attached_object->lineSegmentIntersect(start, local_end, face, pick_transparent, face_hit, &local_intersection, tex_coord, normal, tangent))
 				{
 					local_end = local_intersection;
 					if (intersection)
diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h
index e7f249be69..b05eed344b 100755
--- a/indra/newview/llvoavatar.h
+++ b/indra/newview/llvoavatar.h
@@ -162,22 +162,22 @@ public:
 	/*virtual*/ void   	 	 	updateRegion(LLViewerRegion *regionp);
 	/*virtual*/ void   	 	 	updateSpatialExtents(LLVector4a& newMin, LLVector4a &newMax);
 	/*virtual*/ void   	 	 	getSpatialExtents(LLVector4a& newMin, LLVector4a& newMax);
-	/*virtual*/ BOOL   	 	 	lineSegmentIntersect(const LLVector3& start, const LLVector3& end,
+	/*virtual*/ BOOL   	 	 	lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end,
 												 S32 face = -1,                    // which face to check, -1 = ALL_SIDES
 												 BOOL pick_transparent = FALSE,
 												 S32* face_hit = NULL,             // which face was hit
-												 LLVector3* intersection = NULL,   // return the intersection point
+												 LLVector4a* 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*	lineSegmentIntersectRiggedAttachments(const LLVector3& start, const LLVector3& end,
+												 LLVector4a* normal = NULL,         // return the surface normal at the intersection point
+												 LLVector4a* tangent = NULL);     // return the surface tangent at the intersection point
+	LLViewerObject*	lineSegmentIntersectRiggedAttachments(const LLVector4a& start, const LLVector4a& end,
 												 S32 face = -1,                    // which face to check, -1 = ALL_SIDES
 												 BOOL pick_transparent = FALSE,
 												 S32* face_hit = NULL,             // which face was hit
-												 LLVector3* intersection = NULL,   // return the intersection point
+												 LLVector4a* 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
+												 LLVector4a* normal = NULL,         // return the surface normal at the intersection point
+												 LLVector4a* tangent = NULL);     // return the surface tangent at the intersection point
 
 	//--------------------------------------------------------------------
 	// LLCharacter interface and related
diff --git a/indra/newview/llvograss.cpp b/indra/newview/llvograss.cpp
index 6a25b765cf..cab5c4bc1d 100755
--- a/indra/newview/llvograss.cpp
+++ b/indra/newview/llvograss.cpp
@@ -764,8 +764,8 @@ void LLVOGrass::updateDrawable(BOOL force_damped)
 }
 
 // virtual 
-BOOL LLVOGrass::lineSegmentIntersect(const LLVector3& start, const LLVector3& end, S32 face, BOOL pick_transparent, S32 *face_hitp,
-									  LLVector3* intersection,LLVector2* tex_coord, LLVector3* normal, LLVector3* bi_normal)
+BOOL LLVOGrass::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, S32 face, BOOL pick_transparent, S32 *face_hitp,
+									  LLVector4a* intersection,LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent)
 	
 {
 	BOOL ret = FALSE;
@@ -776,7 +776,8 @@ BOOL LLVOGrass::lineSegmentIntersect(const LLVector3& start, const LLVector3& en
 		return FALSE;
 	}
 
-	LLVector3 dir = end-start;
+	LLVector4a dir;
+	dir.setSub(end, start);
 
 	mPatch = mRegionp->getLand().resolvePatchRegion(getPositionRegion());
 	
@@ -844,23 +845,31 @@ BOOL LLVOGrass::lineSegmentIntersect(const LLVector3& start, const LLVector3& en
 
 		U32 idx0 = 0,idx1 = 0,idx2 = 0;
 
-		if (LLTriangleRayIntersect(v[0], v[1], v[2], start, dir, a, b, t, FALSE))
+		LLVector4a v0a,v1a,v2a,v3a;
+
+		v0a.load3(v[0].mV);
+		v1a.load3(v[1].mV);
+		v2a.load3(v[2].mV);
+		v3a.load3(v[3].mV);
+
+		
+		if (LLTriangleRayIntersect(v0a, v1a, v2a, start, dir, a, b, t))
 		{
 			hit = TRUE;
 			idx0 = 0; idx1 = 1; idx2 = 2;
 		}
-		else if (LLTriangleRayIntersect(v[1], v[3], v[2], start, dir, a, b, t, FALSE))
+		else if (LLTriangleRayIntersect(v1a, v3a, v2a, start, dir, a, b, t))
 		{
 			hit = TRUE;
 			idx0 = 1; idx1 = 3; idx2 = 2;
 		}
-		else if (LLTriangleRayIntersect(v[2], v[1], v[0], start, dir, a, b, t, FALSE))
+		else if (LLTriangleRayIntersect(v2a, v1a, v0a, start, dir, a, b, t))
 		{
 			normal1 = -normal1;
 			hit = TRUE;
 			idx0 = 2; idx1 = 1; idx2 = 0;
 		}
-		else if (LLTriangleRayIntersect(v[2], v[3], v[1], start, dir, a, b, t, FALSE))
+		else if (LLTriangleRayIntersect(v2a, v3a, v1a, start, dir, a, b, t))
 		{
 			normal1 = -normal1;
 			hit = TRUE;
@@ -883,7 +892,8 @@ BOOL LLVOGrass::lineSegmentIntersect(const LLVector3& start, const LLVector3& en
 					closest_t = t;
 					if (intersection != NULL)
 					{
-						*intersection = start+dir*closest_t;
+						dir.mul(closest_t);
+						intersection->setAdd(start, dir);
 					}
 
 					if (tex_coord != NULL)
@@ -893,7 +903,7 @@ BOOL LLVOGrass::lineSegmentIntersect(const LLVector3& start, const LLVector3& en
 
 					if (normal != NULL)
 					{
-						*normal    = normal1;
+						normal->load3(normal1.mV);
 					}
 					ret = TRUE;
 				}
diff --git a/indra/newview/llvograss.h b/indra/newview/llvograss.h
index b9835b8802..122806766d 100755
--- a/indra/newview/llvograss.h
+++ b/indra/newview/llvograss.h
@@ -75,14 +75,14 @@ public:
 	/*virtual*/ BOOL    isActive() const; // Whether this object needs to do an idleUpdate.
 	/*virtual*/ void idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time);
 
-	/*virtual*/ BOOL lineSegmentIntersect(const LLVector3& start, const LLVector3& end, 
+	/*virtual*/ BOOL lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, 
 										  S32 face = -1,                        // which face to check, -1 = ALL_SIDES
 										  BOOL pick_transparent = FALSE,
 										  S32* face_hit = NULL,                 // which face was hit
-										  LLVector3* intersection = NULL,       // return the intersection point
+										  LLVector4a* 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
+										  LLVector4a* normal = NULL,             // return the surface normal at the intersection point
+										  LLVector4a* tangent = NULL           // return the surface tangent at the intersection point
 		);
 
 	static S32 sMaxGrassSpecies;
diff --git a/indra/newview/llvopartgroup.cpp b/indra/newview/llvopartgroup.cpp
index 65711d2339..6a7f26bdb5 100755
--- a/indra/newview/llvopartgroup.cpp
+++ b/indra/newview/llvopartgroup.cpp
@@ -67,7 +67,7 @@ void LLVOPartGroup::restoreGL()
 {
 
 	//TODO: optimize out binormal mask here.  Specular and normal coords as well.
-	sVB = new LLVertexBuffer(VERTEX_DATA_MASK | LLVertexBuffer::MAP_BINORMAL | LLVertexBuffer::MAP_TEXCOORD1 | LLVertexBuffer::MAP_TEXCOORD2, GL_STREAM_DRAW_ARB);
+	sVB = new LLVertexBuffer(VERTEX_DATA_MASK | LLVertexBuffer::MAP_TANGENT | LLVertexBuffer::MAP_TEXCOORD1 | LLVertexBuffer::MAP_TEXCOORD2, GL_STREAM_DRAW_ARB);
 	U32 count = LL_MAX_PARTICLE_COUNT;
 	sVB->allocateBuffer(count*4, count*6, true);
 
diff --git a/indra/newview/llvosurfacepatch.cpp b/indra/newview/llvosurfacepatch.cpp
index de15f0ef43..9a5c5831ca 100755
--- a/indra/newview/llvosurfacepatch.cpp
+++ b/indra/newview/llvosurfacepatch.cpp
@@ -97,10 +97,10 @@ public:
 			glTexCoordPointer(2,GL_FLOAT, LLVertexBuffer::sTypeSize[TYPE_TEXCOORD1], (void*)(base + mOffsets[TYPE_TEXCOORD1]));
 			glClientActiveTextureARB(GL_TEXTURE0_ARB);
 		}
-		if (data_mask & MAP_BINORMAL)
+		if (data_mask & MAP_TANGENT)
 		{
 			glClientActiveTextureARB(GL_TEXTURE2_ARB);
-			glTexCoordPointer(3,GL_FLOAT, LLVertexBuffer::sTypeSize[TYPE_BINORMAL], (void*)(base + mOffsets[TYPE_BINORMAL]));
+			glTexCoordPointer(3,GL_FLOAT, LLVertexBuffer::sTypeSize[TYPE_TANGENT], (void*)(base + mOffsets[TYPE_TANGENT]));
 			glClientActiveTextureARB(GL_TEXTURE0_ARB);
 		}
 		if (data_mask & MAP_TEXCOORD0)
@@ -936,8 +936,8 @@ void LLVOSurfacePatch::getGeomSizesEast(const S32 stride, const S32 east_stride,
 	}
 }
 
-BOOL LLVOSurfacePatch::lineSegmentIntersect(const LLVector3& start, const LLVector3& end, S32 face, BOOL pick_transparent, S32 *face_hitp,
-									  LLVector3* intersection,LLVector2* tex_coord, LLVector3* normal, LLVector3* bi_normal)
+BOOL LLVOSurfacePatch::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, S32 face, BOOL pick_transparent, S32 *face_hitp,
+									  LLVector4a* intersection,LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent)
 	
 {
 
@@ -946,7 +946,9 @@ BOOL LLVOSurfacePatch::lineSegmentIntersect(const LLVector3& start, const LLVect
 		return FALSE;
 	}
 
-	LLVector3 delta = end-start;
+	LLVector4a da;
+	da.setSub(end, start);
+	LLVector3 delta(da.getF32ptr());
 		
 	LLVector3 pdelta = delta;
 	pdelta.mV[2] = 0;
@@ -955,7 +957,9 @@ BOOL LLVOSurfacePatch::lineSegmentIntersect(const LLVector3& start, const LLVect
 	
 	F32 tdelta = 1.f/plength;
 
-	LLVector3 origin = start - mRegionp->getOriginAgent();
+	LLVector3 v_start(start.getF32ptr());
+
+	LLVector3 origin = v_start - mRegionp->getOriginAgent();
 
 	if (mRegionp->getLandHeightRegion(origin) > origin.mV[2])
 	{
@@ -1010,12 +1014,12 @@ BOOL LLVOSurfacePatch::lineSegmentIntersect(const LLVector3& start, const LLVect
 					{
 						sample.mV[2] = mRegionp->getLandHeightRegion(sample);
 					}
-					*intersection = sample + mRegionp->getOriginAgent();
+					intersection->load3((sample + mRegionp->getOriginAgent()).mV);
 				}
 
 				if (normal)
 				{
-					*normal = mRegionp->getLand().resolveNormalGlobal(mRegionp->getPosGlobalFromRegion(sample));
+					normal->load3((mRegionp->getLand().resolveNormalGlobal(mRegionp->getPosGlobalFromRegion(sample))).mV);
 				}
 
 				return TRUE;
diff --git a/indra/newview/llvosurfacepatch.h b/indra/newview/llvosurfacepatch.h
index a15878368e..7b53219be8 100755
--- a/indra/newview/llvosurfacepatch.h
+++ b/indra/newview/llvosurfacepatch.h
@@ -79,14 +79,14 @@ public:
 	void dirtyPatch();
 	void dirtyGeom();
 
-	/*virtual*/ BOOL lineSegmentIntersect(const LLVector3& start, const LLVector3& end, 
+	/*virtual*/ BOOL lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, 
 										  S32 face = -1,                        // which face to check, -1 = ALL_SIDES
 										  BOOL pick_transparent = FALSE,
 										  S32* face_hit = NULL,                 // which face was hit
-										  LLVector3* intersection = NULL,       // return the intersection point
+										  LLVector4a* 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
+										  LLVector4a* normal = NULL,             // return the surface normal at the intersection point
+										  LLVector4a* tangent = NULL           // return the surface tangent at the intersection point
 		);
 
 	BOOL			mDirtiedPatch;
diff --git a/indra/newview/llvotree.cpp b/indra/newview/llvotree.cpp
index a1b36e3c8d..cd12cd9552 100755
--- a/indra/newview/llvotree.cpp
+++ b/indra/newview/llvotree.cpp
@@ -1112,8 +1112,8 @@ void LLVOTree::updateSpatialExtents(LLVector4a& newMin, LLVector4a& newMax)
 	mDrawable->setPositionGroup(pos);
 }
 
-BOOL LLVOTree::lineSegmentIntersect(const LLVector3& start, const LLVector3& end, S32 face, BOOL pick_transparent, S32 *face_hitp,
-									  LLVector3* intersection,LLVector2* tex_coord, LLVector3* normal, LLVector3* bi_normal)
+BOOL LLVOTree::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, S32 face, BOOL pick_transparent, S32 *face_hitp,
+									  LLVector4a* intersection,LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent)
 	
 {
 
@@ -1142,16 +1142,19 @@ BOOL LLVOTree::lineSegmentIntersect(const LLVector3& start, const LLVector3& end
 
 	LLVector3 pos, norm;
 		
-	if (linesegment_tetrahedron(start, end, center, size, quat, pos, norm))
+	LLVector3 start3(start.getF32ptr());
+	LLVector3 end3(end.getF32ptr());
+
+	if (linesegment_tetrahedron(start3, end3, center, size, quat, pos, norm))
 	{
 		if (intersection)
 		{
-			*intersection = pos;
+			intersection->load3(pos.mV);
 		}
 
 		if (normal)
 		{
-			*normal = norm;
+			normal->load3(norm.mV);
 		}
 		return TRUE;
 	}
diff --git a/indra/newview/llvotree.h b/indra/newview/llvotree.h
index 2a7eb21238..2ecb0303a1 100755
--- a/indra/newview/llvotree.h
+++ b/indra/newview/llvotree.h
@@ -106,14 +106,14 @@ public:
 								 F32 branches, 
 								 F32 alpha); 
 
-	 /*virtual*/ BOOL lineSegmentIntersect(const LLVector3& start, const LLVector3& end, 
+	 /*virtual*/ BOOL lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, 
 										  S32 face = -1,                        // which face to check, -1 = ALL_SIDES
 										  BOOL pick_transparent = FALSE,
 										  S32* face_hit = NULL,                 // which face was hit
-										  LLVector3* intersection = NULL,       // return the intersection point
+										  LLVector4a* 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
+										  LLVector4a* normal = NULL,             // return the surface normal at the intersection point
+										  LLVector4a* tangent = NULL           // return the surface tangent at the intersection point
 		);
 
 	static S32 sMaxTreeSpecies;
diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp
index b107f43e4c..677d79d601 100755
--- a/indra/newview/llvovolume.cpp
+++ b/indra/newview/llvovolume.cpp
@@ -1046,7 +1046,7 @@ BOOL LLVOVolume::setVolume(const LLVolumeParams &params_in, const S32 detail, bo
 				{ //already cached
 					break;
 				}
-				volume->genBinormals(i);
+				volume->genTangents(i);
 				LLFace::cacheFaceInVRAM(face);
 			}
 		}
@@ -3612,8 +3612,8 @@ LLVector3 LLVOVolume::volumeDirectionToAgent(const LLVector3& dir) const
 }
 
 
-BOOL LLVOVolume::lineSegmentIntersect(const LLVector3& start, const LLVector3& end, S32 face, BOOL pick_transparent, S32 *face_hitp,
-									  LLVector3* intersection,LLVector2* tex_coord, LLVector3* normal, LLVector3* bi_normal)
+BOOL LLVOVolume::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, S32 face, BOOL pick_transparent, S32 *face_hitp,
+									  LLVector4a* intersection,LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent)
 	
 {
 	if (!mbCanSelect 
@@ -3645,23 +3645,25 @@ BOOL LLVOVolume::lineSegmentIntersect(const LLVector3& start, const LLVector3& e
 	
 	if (volume)
 	{	
-		LLVector3 v_start, v_end, v_dir;
-	
+		LLVector4a local_start = start;
+		LLVector4a local_end = end;
+		
 		if (transform)
 		{
-			v_start = agentPositionToVolume(start);
-			v_end = agentPositionToVolume(end);
-		}
-		else
-		{
-			v_start = start;
-			v_end = end;
-		}
+			LLVector3 v_start(start.getF32ptr());
+			LLVector3 v_end(end.getF32ptr());
 		
-		LLVector3 p;
-		LLVector3 n;
+			v_start = agentPositionToVolume(v_start);
+			v_end = agentPositionToVolume(v_end);
+
+			local_start.load3(v_start.mV);
+			local_end.load3(v_end.mV);
+		}
+				
+		LLVector4a p;
+		LLVector4a n;
 		LLVector2 tc;
-		LLVector3 bn;
+		LLVector4a tn;
 
 		if (intersection != NULL)
 		{
@@ -3678,9 +3680,9 @@ BOOL LLVOVolume::lineSegmentIntersect(const LLVector3& start, const LLVector3& e
 			n = *normal;
 		}
 
-		if (bi_normal != NULL)
+		if (tangent != NULL)
 		{
-			bn = *bi_normal;
+			tn = *tangent;
 		}
 
 		S32 face_hit = -1;
@@ -3706,8 +3708,8 @@ BOOL LLVOVolume::lineSegmentIntersect(const LLVector3& start, const LLVector3& e
 				continue;
 			}
 
-			face_hit = volume->lineSegmentIntersect(v_start, v_end, i,
-													&p, &tc, &n, &bn);
+			face_hit = volume->lineSegmentIntersect(local_start, local_end, i,
+													&p, &tc, &n, &tn);
 			
 			if (face_hit >= 0 && mDrawable->getNumFaces() > face_hit)
 			{
@@ -3716,7 +3718,7 @@ BOOL LLVOVolume::lineSegmentIntersect(const LLVector3& start, const LLVector3& e
 				if (face &&
 					(pick_transparent || !face->getTexture() || !face->getTexture()->hasGLTexture() || face->getTexture()->getMask(face->surfaceToTexture(tc, p, n))))
 				{
-					v_end = p;
+					local_end = p;
 					if (face_hitp != NULL)
 					{
 						*face_hitp = face_hit;
@@ -3726,7 +3728,9 @@ BOOL LLVOVolume::lineSegmentIntersect(const LLVector3& start, const LLVector3& e
 					{
 						if (transform)
 						{
-							*intersection = volumePositionToAgent(p);  // must map back to agent space
+							LLVector3 v_p(p.getF32ptr());
+
+							intersection->load3(volumePositionToAgent(v_p).mV);  // must map back to agent space
 						}
 						else
 						{
@@ -3738,27 +3742,37 @@ BOOL LLVOVolume::lineSegmentIntersect(const LLVector3& start, const LLVector3& e
 					{
 						if (transform)
 						{
-							*normal = volumeDirectionToAgent(n);
+							LLVector3 v_n(n.getF32ptr());
+							normal->load3(volumeDirectionToAgent(v_n).mV);
 						}
 						else
 						{
 							*normal = n;
 						}
 
-						(*normal).normVec();
+						(*normal).normalize3fast();
 					}
 
-					if (bi_normal != NULL)
+					if (tangent != NULL)
 					{
 						if (transform)
 						{
-							*bi_normal = volumeDirectionToAgent(bn);
+							LLVector3 v_tn(tn.getF32ptr());
+
+							LLVector4a trans_tangent;
+							trans_tangent.load3(volumeDirectionToAgent(v_tn).mV);
+
+							LLVector4Logical mask;
+							mask.clear();
+							mask.setElement<3>();
+
+							tangent->setSelectWithMask(mask, tn, trans_tangent);
 						}
 						else
 						{
-							*bi_normal = bn;
+							*tangent = tn;
 						}
-						(*bi_normal).normVec();
+						(*tangent).normalize3fast();
 					}
 
 					if (tex_coord != NULL)
@@ -4038,7 +4052,7 @@ static LLFastTimer::DeclareTimer FTM_REGISTER_FACE("Register Face");
 void LLVolumeGeometryManager::registerFace(LLSpatialGroup* group, LLFace* facep, U32 type)
 {
 	LLFastTimer t(FTM_REGISTER_FACE);
-	if (type == LLRenderPass::PASS_ALPHA && facep->getTextureEntry()->getMaterialParams().notNull() && !facep->getVertexBuffer()->hasDataType(LLVertexBuffer::TYPE_BINORMAL))
+	if (type == LLRenderPass::PASS_ALPHA && facep->getTextureEntry()->getMaterialParams().notNull() && !facep->getVertexBuffer()->hasDataType(LLVertexBuffer::TYPE_TANGENT))
 	{
 		LL_WARNS("RenderMaterials") << "Oh no! No binormals for this alpha blended face!" << LL_ENDL;
 	}
@@ -4747,11 +4761,11 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
 								if (mat->getNormalID().notNull())
 								{
 									if (mat->getSpecularID().notNull())
-									{ //has normal and specular maps (needs texcoord1, texcoord2, and binormal)
+									{ //has normal and specular maps (needs texcoord1, texcoord2, and tangent)
 										normspec_faces.push_back(facep);
 									}
 									else
-									{ //has normal map (needs texcoord1 and binormal)
+									{ //has normal map (needs texcoord1 and tangent)
 										norm_faces.push_back(facep);
 									}
 								}
@@ -4765,7 +4779,7 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
 								}									
 							}
 							else if (te->getBumpmap())
-							{ //needs normal + binormal
+							{ //needs normal + tangent
 								bump_faces.push_back(facep);
 							}
 							else if (te->getShiny() || !te->getFullbright())
@@ -4781,7 +4795,7 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
 						else
 						{
 							if (te->getBumpmap() && LLPipeline::sRenderBump)
-							{ //needs normal + binormal
+							{ //needs normal + tangent
 								bump_faces.push_back(facep);
 							}
 							else if ((te->getShiny() && LLPipeline::sRenderBump) ||
@@ -4822,7 +4836,7 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
 	U32 bump_mask = LLVertexBuffer::MAP_TEXCOORD0 | LLVertexBuffer::MAP_TEXCOORD1 | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_COLOR;
 	U32 fullbright_mask = LLVertexBuffer::MAP_TEXCOORD0 | LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_COLOR;
 
-	U32 norm_mask = simple_mask | LLVertexBuffer::MAP_TEXCOORD1 | LLVertexBuffer::MAP_BINORMAL;
+	U32 norm_mask = simple_mask | LLVertexBuffer::MAP_TEXCOORD1 | LLVertexBuffer::MAP_TANGENT;
 	U32 normspec_mask = norm_mask | LLVertexBuffer::MAP_TEXCOORD2;
 	U32 spec_mask = simple_mask | LLVertexBuffer::MAP_TEXCOORD2;
 
@@ -4841,9 +4855,9 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
 
 	if (batch_textures)
 	{
-		bump_mask = bump_mask | LLVertexBuffer::MAP_BINORMAL;
+		bump_mask = bump_mask | LLVertexBuffer::MAP_TANGENT;
 		simple_mask = simple_mask | LLVertexBuffer::MAP_TEXTURE_INDEX;
-		alpha_mask = alpha_mask | LLVertexBuffer::MAP_TEXTURE_INDEX | LLVertexBuffer::MAP_BINORMAL | LLVertexBuffer::MAP_TEXCOORD1 | LLVertexBuffer::MAP_TEXCOORD2;
+		alpha_mask = alpha_mask | LLVertexBuffer::MAP_TEXTURE_INDEX | LLVertexBuffer::MAP_TANGENT | LLVertexBuffer::MAP_TEXCOORD1 | LLVertexBuffer::MAP_TEXCOORD2;
 		fullbright_mask = fullbright_mask | LLVertexBuffer::MAP_TEXTURE_INDEX;
 	}
 
diff --git a/indra/newview/llvovolume.h b/indra/newview/llvovolume.h
index 928ff7f66b..7503f8c5aa 100755
--- a/indra/newview/llvovolume.h
+++ b/indra/newview/llvovolume.h
@@ -137,14 +137,14 @@ public:
 
 	/*virtual*/ U32		getTriangleCount(S32* vcount = NULL) const;
 	/*virtual*/ U32		getHighLODTriangleCount();
-	/*virtual*/ BOOL lineSegmentIntersect(const LLVector3& start, const LLVector3& end, 
+	/*virtual*/ BOOL lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, 
 										  S32 face = -1,                        // which face to check, -1 = ALL_SIDES
 										  BOOL pick_transparent = FALSE,
 										  S32* face_hit = NULL,                 // which face was hit
-										  LLVector3* intersection = NULL,       // return the intersection point
+										  LLVector4a* 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
+										  LLVector4a* normal = NULL,             // return the surface normal at the intersection point
+										  LLVector4a* tangent = NULL           // return the surface tangent at the intersection point
 		);
 	
 				LLVector3 agentPositionToVolume(const LLVector3& pos) const;
diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp
index 6dc89742ec..cbcf3165c4 100755
--- a/indra/newview/pipeline.cpp
+++ b/indra/newview/pipeline.cpp
@@ -6886,20 +6886,20 @@ void LLPipeline::setRenderHighlightTextureChannel(LLRender::eTexIndex channel)
 	sRenderHighlightTextureChannel = channel;
 }
 
-LLViewerObject* LLPipeline::lineSegmentIntersectInWorld(const LLVector3& start, const LLVector3& end,
+LLViewerObject* LLPipeline::lineSegmentIntersectInWorld(const LLVector4a& start, const LLVector4a& end,
 														BOOL pick_transparent,												
 														S32* face_hit,
-														LLVector3* intersection,         // return the intersection point
+														LLVector4a* 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
+														LLVector4a* normal,               // return the surface normal at the intersection point
+														LLVector4a* tangent             // return the surface tangent at the intersection point
 	)
 {
 	LLDrawable* drawable = NULL;
 
-	LLVector3 local_end = end;
+	LLVector4a local_end = end;
 
-	LLVector3 position;
+	LLVector4a position;
 
 	sPickAvatar = FALSE; //LLToolMgr::getInstance()->inBuildMode() ? FALSE : TRUE;
 	
@@ -6919,7 +6919,7 @@ LLViewerObject* LLPipeline::lineSegmentIntersectInWorld(const LLVector3& start,
 				LLSpatialPartition* part = region->getSpatialPartition(j);
 				if (part && hasRenderType(part->mDrawableType))
 				{
-					LLDrawable* hit = part->lineSegmentIntersect(start, local_end, pick_transparent, face_hit, &position, tex_coord, normal, bi_normal);
+					LLDrawable* hit = part->lineSegmentIntersect(start, local_end, pick_transparent, face_hit, &position, tex_coord, normal, tangent);
 					if (hit)
 					{
 						drawable = hit;
@@ -6934,8 +6934,8 @@ LLViewerObject* LLPipeline::lineSegmentIntersectInWorld(const LLVector3& start,
 	{
 		//save hit info in case we need to restore
 		//due to attachment override
-		LLVector3 local_normal;
-		LLVector3 local_binormal;
+		LLVector4a local_normal;
+		LLVector4a local_tangent;
 		LLVector2 local_texcoord;
 		S32 local_face_hit = -1;
 
@@ -6947,9 +6947,9 @@ LLViewerObject* LLPipeline::lineSegmentIntersectInWorld(const LLVector3& start,
 		{
 			local_texcoord = *tex_coord;
 		}
-		if (bi_normal)
+		if (tangent)
 		{
-			local_binormal = *bi_normal;
+			local_tangent = *tangent;
 		}
 		if (normal)
 		{
@@ -6968,12 +6968,15 @@ LLViewerObject* LLPipeline::lineSegmentIntersectInWorld(const LLVector3& start,
 			LLSpatialPartition* part = region->getSpatialPartition(LLViewerRegion::PARTITION_BRIDGE);
 			if (part && hasRenderType(part->mDrawableType))
 			{
-				LLDrawable* hit = part->lineSegmentIntersect(start, local_end, pick_transparent, face_hit, &position, tex_coord, normal, bi_normal);
+				LLDrawable* hit = part->lineSegmentIntersect(start, local_end, pick_transparent, face_hit, &position, tex_coord, normal, tangent);
 				if (hit)
 				{
+					LLVector4a delta;
+					delta.setSub(position, local_end);
+
 					if (!drawable || 
 						!drawable->getVObj()->isAttachment() ||
-						(position-local_end).magVec() > ATTACHMENT_OVERRIDE_DIST)
+						delta.getLength3().getF32() > ATTACHMENT_OVERRIDE_DIST)
 					{ //avatar overrides if previously hit drawable is not an attachment or 
 					  //attachment is far enough away from detected intersection
 						drawable = hit;
@@ -6991,9 +6994,9 @@ LLViewerObject* LLPipeline::lineSegmentIntersectInWorld(const LLVector3& start,
 						{
 							*tex_coord = local_texcoord;
 						}
-						if (bi_normal)
+						if (tangent)
 						{
-							*bi_normal = local_binormal;
+							*tangent = local_tangent;
 						}
 						if (normal)
 						{
@@ -7027,13 +7030,13 @@ LLViewerObject* LLPipeline::lineSegmentIntersectInWorld(const LLVector3& start,
 	return drawable ? drawable->getVObj().get() : NULL;
 }
 
-LLViewerObject* LLPipeline::lineSegmentIntersectInHUD(const LLVector3& start, const LLVector3& end,
+LLViewerObject* LLPipeline::lineSegmentIntersectInHUD(const LLVector4a& start, const LLVector4a& end,
 													  BOOL pick_transparent,													
 													  S32* face_hit,
-													  LLVector3* intersection,         // return the intersection point
+													  LLVector4a* 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
+													  LLVector4a* normal,               // return the surface normal at the intersection point
+													  LLVector4a* tangent				// return the surface tangent at the intersection point
 	)
 {
 	LLDrawable* drawable = NULL;
@@ -7053,7 +7056,7 @@ LLViewerObject* LLPipeline::lineSegmentIntersectInHUD(const LLVector3& start, co
 		LLSpatialPartition* part = region->getSpatialPartition(LLViewerRegion::PARTITION_HUD);
 		if (part)
 		{
-			LLDrawable* hit = part->lineSegmentIntersect(start, end, pick_transparent, face_hit, intersection, tex_coord, normal, bi_normal);
+			LLDrawable* hit = part->lineSegmentIntersect(start, end, pick_transparent, face_hit, intersection, tex_coord, normal, tangent);
 			if (hit)
 			{
 				drawable = hit;
@@ -7513,13 +7516,18 @@ void LLPipeline::renderBloom(BOOL for_snapshot, F32 zoom_factor, int subfield)
 			{
 				if (LLViewerJoystick::getInstance()->getOverrideCamera())
 				{ //focus on point under cursor
-					focus_point = gDebugRaycastIntersection;
+					focus_point.set(gDebugRaycastIntersection.getF32ptr());
 				}
 				else if (gAgentCamera.cameraMouselook())
 				{ //focus on point under mouselook crosshairs
+					LLVector4a result;
+					result.clear();
+
 					gViewerWindow->cursorIntersect(-1, -1, 512.f, NULL, -1, FALSE,
 													NULL,
-													&focus_point);
+													&result);
+
+					focus_point.set(result.getF32ptr());
 				}
 				else
 				{
diff --git a/indra/newview/pipeline.h b/indra/newview/pipeline.h
index a8362953b4..2c023a6f70 100755
--- a/indra/newview/pipeline.h
+++ b/indra/newview/pipeline.h
@@ -188,21 +188,21 @@ public:
 	void		markMeshDirty(LLSpatialGroup* group);
 
 	//get the object between start and end that's closest to start.
-	LLViewerObject* lineSegmentIntersectInWorld(const LLVector3& start, const LLVector3& end,
+	LLViewerObject* lineSegmentIntersectInWorld(const LLVector4a& start, const LLVector4a& end,
 												BOOL pick_transparent,
 												S32* face_hit,                          // return the face hit
-												LLVector3* intersection = NULL,         // return the intersection point
+												LLVector4a* 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  
+												LLVector4a* normal = NULL,               // return the surface normal at the intersection point
+												LLVector4a* tangent = NULL             // return the surface tangent at the intersection point  
 		);
-	LLViewerObject* lineSegmentIntersectInHUD(const LLVector3& start, const LLVector3& end,
+	LLViewerObject* lineSegmentIntersectInHUD(const LLVector4a& start, const LLVector4a& end,
 											  BOOL pick_transparent,
 											  S32* face_hit,                          // return the face hit
-											  LLVector3* intersection = NULL,         // return the intersection point
+											  LLVector4a* 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
+											  LLVector4a* normal = NULL,               // return the surface normal at the intersection point
+											  LLVector4a* tangent = NULL             // return the surface tangent at the intersection point
 		);
 
 	// Something about these textures has changed.  Dirty them.
-- 
GitLab