diff -r 6b887df85f95 indra/llmath/m4math.cpp
--- a/indra/llmath/m4math.cpp	Mon Oct 31 16:08:55 2011 -0700
+++ b/indra/llmath/m4math.cpp	Sat Dec 31 21:15:11 2011 -0600
@@ -274,6 +274,68 @@
 	return *this;
 }
 
+
+// does an actual inversion
+const LLMatrix4& LLMatrix4::invert_real()
+{
+	F32* m = &mMatrix[0][0];
+	
+	F32 inv[16];
+	F32 det;
+	
+	inv[0] =   m[5]*m[10]*m[15] - m[5]*m[11]*m[14] - m[9]*m[6]*m[15]
+	+ m[9]*m[7]*m[14] + m[13]*m[6]*m[11] - m[13]*m[7]*m[10];
+	inv[4] =  -m[4]*m[10]*m[15] + m[4]*m[11]*m[14] + m[8]*m[6]*m[15]
+	- m[8]*m[7]*m[14] - m[12]*m[6]*m[11] + m[12]*m[7]*m[10];
+	inv[8] =   m[4]*m[9]*m[15] - m[4]*m[11]*m[13] - m[8]*m[5]*m[15]
+	+ m[8]*m[7]*m[13] + m[12]*m[5]*m[11] - m[12]*m[7]*m[9];
+	inv[12] = -m[4]*m[9]*m[14] + m[4]*m[10]*m[13] + m[8]*m[5]*m[14]
+	- m[8]*m[6]*m[13] - m[12]*m[5]*m[10] + m[12]*m[6]*m[9];
+	inv[1] =  -m[1]*m[10]*m[15] + m[1]*m[11]*m[14] + m[9]*m[2]*m[15]
+	- m[9]*m[3]*m[14] - m[13]*m[2]*m[11] + m[13]*m[3]*m[10];
+	inv[5] =   m[0]*m[10]*m[15] - m[0]*m[11]*m[14] - m[8]*m[2]*m[15]
+	+ m[8]*m[3]*m[14] + m[12]*m[2]*m[11] - m[12]*m[3]*m[10];
+	inv[9] =  -m[0]*m[9]*m[15] + m[0]*m[11]*m[13] + m[8]*m[1]*m[15]
+	- m[8]*m[3]*m[13] - m[12]*m[1]*m[11] + m[12]*m[3]*m[9];
+	inv[13] =  m[0]*m[9]*m[14] - m[0]*m[10]*m[13] - m[8]*m[1]*m[14]
+	+ m[8]*m[2]*m[13] + m[12]*m[1]*m[10] - m[12]*m[2]*m[9];
+	inv[2] =   m[1]*m[6]*m[15] - m[1]*m[7]*m[14] - m[5]*m[2]*m[15]
+	+ m[5]*m[3]*m[14] + m[13]*m[2]*m[7] - m[13]*m[3]*m[6];
+	inv[6] =  -m[0]*m[6]*m[15] + m[0]*m[7]*m[14] + m[4]*m[2]*m[15]
+	- m[4]*m[3]*m[14] - m[12]*m[2]*m[7] + m[12]*m[3]*m[6];
+	inv[10] =  m[0]*m[5]*m[15] - m[0]*m[7]*m[13] - m[4]*m[1]*m[15]
+	+ m[4]*m[3]*m[13] + m[12]*m[1]*m[7] - m[12]*m[3]*m[5];
+	inv[14] = -m[0]*m[5]*m[14] + m[0]*m[6]*m[13] + m[4]*m[1]*m[14]
+	- m[4]*m[2]*m[13] - m[12]*m[1]*m[6] + m[12]*m[2]*m[5];
+	inv[3] =  -m[1]*m[6]*m[11] + m[1]*m[7]*m[10] + m[5]*m[2]*m[11]
+	- m[5]*m[3]*m[10] - m[9]*m[2]*m[7] + m[9]*m[3]*m[6];
+	inv[7] =   m[0]*m[6]*m[11] - m[0]*m[7]*m[10] - m[4]*m[2]*m[11]
+	+ m[4]*m[3]*m[10] + m[8]*m[2]*m[7] - m[8]*m[3]*m[6];
+	inv[11] = -m[0]*m[5]*m[11] + m[0]*m[7]*m[9] + m[4]*m[1]*m[11]
+	- m[4]*m[3]*m[9] - m[8]*m[1]*m[7] + m[8]*m[3]*m[5];
+	inv[15] =  m[0]*m[5]*m[10] - m[0]*m[6]*m[9] - m[4]*m[1]*m[10]
+	+ m[4]*m[2]*m[9] + m[8]*m[1]*m[6] - m[8]*m[2]*m[5];
+	
+	det = m[0]*inv[0] + m[1]*inv[4] + m[2]*inv[8] + m[3]*inv[12];
+	
+	if (det == 0)
+	{
+		setZero();
+	}
+	else 
+	{
+		det = 1.0 / det;
+		
+		for (int i = 0; i < 16; i++)
+			m[i] = inv[i] * det;
+		
+	}
+
+	
+	return *this;
+
+}
+
 LLVector4 LLMatrix4::getFwdRow4() const
 {
 	return LLVector4(mMatrix[VX][VX], mMatrix[VX][VY], mMatrix[VX][VZ], mMatrix[VX][VW]);
diff -r 6b887df85f95 indra/llmath/m4math.h
--- a/indra/llmath/m4math.h	Mon Oct 31 16:08:55 2011 -0700
+++ b/indra/llmath/m4math.h	Sat Dec 31 21:15:11 2011 -0600
@@ -202,7 +202,9 @@
 	//
 
 	const LLMatrix4& transpose();						// Transpose LLMatrix4
-	const LLMatrix4& invert();						// Invert LLMatrix4
+	const LLMatrix4& invert();						// Invert LLMatrix4 
+	const LLMatrix4& invert_real();					// Invert LLMatrix4 - works for all matrices
+
 
 	// Rotate existing matrix
 	// These are really, really, inefficient as implemented! - djs
diff -r 6b887df85f95 indra/newview/lldrawpoolavatar.cpp
--- a/indra/newview/lldrawpoolavatar.cpp	Mon Oct 31 16:08:55 2011 -0700
+++ b/indra/newview/lldrawpoolavatar.cpp	Sat Dec 31 21:15:11 2011 -0600
@@ -1291,7 +1291,9 @@
 	}
 }
 
-void LLDrawPoolAvatar::updateRiggedFaceVertexBuffer(LLVOAvatar* avatar, LLFace* face, const LLMeshSkinInfo* skin, LLVolume* volume, const LLVolumeFace& vol_face)
+void LLDrawPoolAvatar::updateRiggedFaceVertexBuffer(LLVOAvatar* avatar, LLFace* face, 
+													const LLMeshSkinInfo* skin, LLVolume* volume, 
+													const LLVolumeFace& vol_face, LLVOVolume* vobj)
 {
 	LLVector4a* weight = vol_face.mWeights;
 	if (!weight)
@@ -1344,9 +1346,12 @@
 		  m.m[4], m.m[5], m.m[6],
 		  m.m[8], m.m[9], m.m[10] };
 
-		LLMatrix3 mat_normal(mat3);				
+		LLMatrix3 mat_normal(mat3);		
+		
+		LLDeformedVolume* deformed_volume = vobj->getDeformedVolume();
+		deformed_volume->deform(volume, avatar, skin, face->getTEOffset());
 
-		face->getGeometryVolume(*volume, face->getTEOffset(), mat_vert, mat_normal, offset, true);
+		face->getGeometryVolume(*deformed_volume, face->getTEOffset(), mat_vert, mat_normal, offset, true);
 	}
 
 	if (sShaderLevel <= 0 && face->mLastSkinTime < avatar->getLastSkinTime())
@@ -1424,6 +1429,10 @@
 
 			if (norm)
 			{
+				// normals are not transformed by the same math as points.
+				// you need the inverse transpose.
+				// these matrices are non-uniformly scaled (by a lot) so this
+				// math is wrong.  (i think.)  -qarl
 				LLVector4a& n = vol_face.mNormals[j];
 				bind_shape_matrix.rotate(n, t);
 				final_mat.rotate(t, dst);
@@ -1486,7 +1495,7 @@
 		stop_glerror();
 
 		const LLVolumeFace& vol_face = volume->getVolumeFace(te);
-		updateRiggedFaceVertexBuffer(avatar, face, skin, volume, vol_face);
+		updateRiggedFaceVertexBuffer(avatar, face, skin, volume, vol_face, vobj);
 		
 		stop_glerror();
 
diff -r 6b887df85f95 indra/newview/lldrawpoolavatar.h
--- a/indra/newview/lldrawpoolavatar.h	Mon Oct 31 16:08:55 2011 -0700
+++ b/indra/newview/lldrawpoolavatar.h	Sat Dec 31 21:15:11 2011 -0600
@@ -35,6 +35,7 @@
 class LLMeshSkinInfo;
 class LLVolume;
 class LLVolumeFace;
+class LLVOVolume;
 
 
 class LLDrawPoolAvatar : public LLFacePool
@@ -133,7 +134,8 @@
 									  LLFace* facep, 
 									  const LLMeshSkinInfo* skin, 
 									  LLVolume* volume,
-									  const LLVolumeFace& vol_face);
+									  const LLVolumeFace& vol_face,
+									  LLVOVolume* vobj);
 
 	void renderRigged(LLVOAvatar* avatar, U32 type, bool glow = false);
 	void renderRiggedSimple(LLVOAvatar* avatar);
diff -r 6b887df85f95 indra/newview/llpolymesh.h
--- a/indra/newview/llpolymesh.h	Mon Oct 31 16:08:55 2011 -0700
+++ b/indra/newview/llpolymesh.h	Sat Dec 31 21:15:11 2011 -0600
@@ -234,6 +234,12 @@
 		return mBinormals; 
 	}
 
+	// Get base mesh coords
+	const LLVector3 *getBaseCoords() const{
+		llassert(mSharedData);
+		return mSharedData->mBaseCoords;
+	}
+	
 	// Get base mesh normals
 	const LLVector3 *getBaseNormals() const{
 		llassert(mSharedData);
diff -r 6b887df85f95 indra/newview/llvoavatar.cpp
--- a/indra/newview/llvoavatar.cpp	Mon Oct 31 16:08:55 2011 -0700
+++ b/indra/newview/llvoavatar.cpp	Sat Dec 31 21:15:11 2011 -0600
@@ -5187,6 +5187,10 @@
 	return mMeshLOD[MESH_ID_UPPER_BODY]->mMeshParts[0]->getMesh();
 }
 
+LLPolyMesh* LLVOAvatar::getMesh(S32 which)
+{
+	return mMeshLOD[which]->mMeshParts[0]->getMesh();
+}
 
 //-----------------------------------------------------------------------------
 // LLVOAvatar::getPosGlobalFromAgent()
@@ -5715,9 +5719,10 @@
 		mLastSkeletonSerialNum = mSkeletonSerialNum;
 		mRoot.updateWorldMatrixChildren();
 	}
-
+	
 	dirtyMesh();
 	updateHeadOffset();
+	rebuildRiggedAttachments();
 }
 
 //-----------------------------------------------------------------------------
diff -r 6b887df85f95 indra/newview/llvoavatar.h
--- a/indra/newview/llvoavatar.h	Mon Oct 31 16:08:55 2011 -0700
+++ b/indra/newview/llvoavatar.h	Sat Dec 31 21:15:11 2011 -0600
@@ -192,6 +192,8 @@
 	virtual F32				getPixelArea() const;
 	virtual LLPolyMesh*		getHeadMesh();
 	virtual LLPolyMesh*		getUpperBodyMesh();
+	virtual LLPolyMesh*     getMesh(S32 which);
+
 	virtual LLVector3d		getPosGlobalFromAgent(const LLVector3 &position);
 	virtual LLVector3		getPosAgentFromGlobal(const LLVector3d &position);
 	virtual void			updateVisualParams();
diff -r 6b887df85f95 indra/newview/llvovolume.cpp
--- a/indra/newview/llvovolume.cpp	Mon Oct 31 16:08:55 2011 -0700
+++ b/indra/newview/llvovolume.cpp	Sat Dec 31 21:15:11 2011 -0600
@@ -3811,7 +3811,7 @@
 				LLVector4a& v = vol_face.mPositions[j];
 				LLVector4a t;
 				LLVector4a dst;
-				bind_shape_matrix.affineTransform(v, t);
+				bind_shape_matrix.affineTransform(v, t);  // shouldn't this matrix be premultiplied into the matrix palette?  this is expensive.
 				final_mat.affineTransform(t, dst);
 				pos[j] = dst;
 			}
@@ -3848,6 +3848,220 @@
 	}
 }
 
+
+LLDeformedVolume* LLVOVolume::getDeformedVolume()
+{
+	if (!mDeformedVolume)
+	{
+		// doesn't exist yet - make it
+		mDeformedVolume = new LLDeformedVolume;
+	}
+	
+	return mDeformedVolume;
+}
+
+static LLFastTimer::DeclareTimer FTM_SKIN_RIGGED_DEFORM("Deform");
+
+
+LLDeformedVolume::deform_table_t& LLDeformedVolume::getDeformTable(LLVolume* source, LLVOAvatar* avatar, const LLMeshSkinInfo* skin, S32 face)
+{
+	LLDeformTableCacheIndex cache_index(source->getParams().getSculptID(), face, source->getVolumeFace(face).mNumVertices);
+	
+	deform_cache_t::iterator i = mDeformCache.find(cache_index);
+	
+	if (i != mDeformCache.end())
+	{
+		return i->second;
+	}
+	
+	
+	// doesn't exist in cache, must create
+
+	
+	// tables are fairly small, we keep them around in case the user is switching between meshes for poorman's animation
+	U32 MAX_CACHE_SIZE = 20;  
+	
+	if (mDeformCache.size() > MAX_CACHE_SIZE)
+	{
+		// delete element at random
+		U32 random = ll_rand(MAX_CACHE_SIZE-1);
+		
+		deform_cache_t::iterator iterator = mDeformCache.begin();
+		
+		for (int i = 0; i < random; i++)
+		{
+			iterator++;
+		}
+		
+		mDeformCache.erase(iterator);
+	}
+	
+	
+	deform_table_t& deform_table = mDeformCache[cache_index];
+	
+	// table is a mapping from verts on the volume to verts on the avatar mesh.
+	// currently we take closest point - but may want to modify this is favor
+	// verts with similar skin weights and/or normals (so verts on the left leg
+	// don't move with the right leg by accident.)
+	
+	const LLVolumeFace& source_face = source->getVolumeFace(face);
+	
+
+	
+	// build mapping
+	deform_table.resize(source_face.mNumVertices);
+	
+	// maps volume space to object (avatar) space
+	LLMatrix4a bind_shape_matrix_a;
+	bind_shape_matrix_a.loadu(skin->mBindShapeMatrix);  
+	// skeleton scaling factor - lots of assumptions in this, but necessary for efficiency
+	F32 scale = powf(skin->mInvBindMatrix[0].determinant(), 1.0f / 3.0f);
+	
+	// these are the meshes we follow
+	S32 MORPH_MESHES[] = { LLVOAvatarDefines::MESH_ID_HEAD, LLVOAvatarDefines::MESH_ID_UPPER_BODY, LLVOAvatarDefines::MESH_ID_LOWER_BODY };
+	
+	// for each vertex in the volume, find the closest vertex on the avatar mesh(es)	
+	for (S32 i = 0; i < source_face.mNumVertices; i++)
+	{
+		LLVector4a source_position = source_face.mPositions[i];
+		LLVector4a source_position_transformed;
+		bind_shape_matrix_a.affineTransform(source_position, source_position_transformed);
+		source_position_transformed.mul(scale);
+		
+		F32 closest_distance;
+		S32 closest_vertex;
+		S32 closest_mesh;
+		
+		BOOL first_time = TRUE;
+
+		for (S32 k = 0; k < sizeof(MORPH_MESHES)/sizeof(S32); k++)
+		{
+			S32 mesh_id = MORPH_MESHES[k];
+			
+			LLPolyMesh* avatar_mesh = avatar->getMesh(mesh_id);
+			
+			const LLVector3* base_positions = avatar_mesh->getBaseCoords();
+			
+			for (S32 j = 0; j < avatar_mesh->getNumVertices(); j++)
+			{
+				LLVector4a base_position;
+				base_position.load3(base_positions[j].mV);
+				
+				LLVector4a delta;
+				delta.setSub(source_position_transformed, base_position);
+				
+				F32 distance = delta.getLength3().getF32();
+				
+				if (first_time ||  // first time through loop is always closest
+					(distance < closest_distance))
+				{
+					closest_distance = distance;
+					closest_vertex = j;
+					closest_mesh = mesh_id;
+					first_time = FALSE;
+				}
+			}
+		}
+		// create entry for found vertex
+		deform_table[i].mVertex = closest_vertex;
+		deform_table[i].mMesh = (U8)closest_mesh;	
+	}
+	
+	
+	return deform_table;
+}
+
+
+
+void LLDeformedVolume::deform(LLVolume* source, LLVOAvatar* avatar, const LLMeshSkinInfo* skin, S32 face)
+{
+	LLFastTimer t(FTM_SKIN_RIGGED_DEFORM);
+	
+	bool copy = false;
+	if (source->getNumVolumeFaces() != getNumVolumeFaces())
+	{ 
+		copy = true;
+	}
+	
+	for (S32 i = 0; i < source->getNumVolumeFaces() && !copy; ++i)
+	{
+		const LLVolumeFace& src_face = source->getVolumeFace(i);
+		const LLVolumeFace& dst_face = getVolumeFace(i);
+		
+		if (src_face.mNumIndices != dst_face.mNumIndices ||
+			src_face.mNumVertices != dst_face.mNumVertices)
+		{
+			copy = true;
+		}
+	}
+	
+	if (copy)
+	{
+		copyVolumeFaces(source);	
+	}
+	
+	const LLVolumeFace& source_face = source->getVolumeFace(face);
+	const LLVolumeFace& destination_face = getVolumeFace(face);
+	
+	
+	// transforms volume space to avatar space
+	LLMatrix4a bind_shape_matrix_a;
+	bind_shape_matrix_a.loadu(skin->mBindShapeMatrix);
+	
+	// transforms back again
+	LLMatrix4 bind_shape_matrix_inverse = skin->mBindShapeMatrix;
+	bind_shape_matrix_inverse.invert_real();
+	LLMatrix4a bind_shape_matrix_inverse_a;
+	bind_shape_matrix_inverse_a.loadu(bind_shape_matrix_inverse);
+	
+	// skeleton scaling factor - lots of assumptions in this, but necessary for efficiency
+	F32 scale = powf(skin->mInvBindMatrix[0].determinant(), 1.0f / 3.0f);
+
+
+	// get the deform table
+	LLDeformTableCacheIndex cache_index(source->getParams().getSculptID(), face, source->getVolumeFace(face).mNumVertices);
+	deform_table_t& deform_table = getDeformTable(source, avatar, skin, face);
+
+	for (S32 i = 0; i < source_face.mNumVertices; i++)
+	{
+		U16 closest_vertex = deform_table[i].mVertex;
+		S32 closest_mesh = deform_table[i].mMesh;
+		
+		// find the current mesh
+		LLPolyMesh* avatar_mesh = avatar->getMesh(closest_mesh);
+		// and get positions for both original and morphed vertices
+		const LLVector3* base_positions = avatar_mesh->getBaseCoords();
+		const LLVector4* morph_positions = avatar_mesh->getCoords();
+
+
+		// find the movement of this vertex due to morphing
+		LLVector4a base_position;
+		base_position.load3(base_positions[closest_vertex].mV);
+		LLVector4a morph_position;
+		morph_position.load3(morph_positions[closest_vertex].mV);
+		LLVector4a morph_delta;
+		morph_delta.setSub(morph_position, base_position);
+
+		// source position mapped to avatar space
+		LLVector4a source_position = source_face.mPositions[i];
+		LLVector4a source_position_transformed;
+		bind_shape_matrix_a.affineTransform(source_position, source_position_transformed);
+		source_position_transformed.mul(scale);
+
+		// modify by movement delta
+		LLVector4a destination_position_transformed;
+		destination_position_transformed.setAdd(source_position_transformed, morph_delta);
+		
+		// map back to volume space
+		destination_position_transformed.mul(1.0f / scale);
+		LLVector4a destination_position;
+		bind_shape_matrix_inverse_a.affineTransform(destination_position_transformed, destination_position);
+		
+		destination_face.mPositions[i] = destination_position;
+	}
+	
+}
+
 U32 LLVOVolume::getPartitionType() const
 {
 	if (isHUDAttachment())
diff -r 6b887df85f95 indra/newview/llvovolume.h
--- a/indra/newview/llvovolume.h	Mon Oct 31 16:08:55 2011 -0700
+++ b/indra/newview/llvovolume.h	Sat Dec 31 21:15:11 2011 -0600
@@ -62,6 +62,68 @@
 	void update(const LLMeshSkinInfo* skin, LLVOAvatar* avatar, const LLVolume* src_volume);
 };
 
+
+
+
+
+class LLDeformedVolume : public LLVolume
+{
+public:
+	LLDeformedVolume()
+	: LLVolume(LLVolumeParams(), 0.f)
+	{
+	}
+	
+	// used as a key for the deform table cache
+	class LLDeformTableCacheIndex
+	{
+	public:
+		LLDeformTableCacheIndex(const LLUUID& id, S32 face, S32 vertex_count) :
+		mID(id), mFace(face), mVertexCount(vertex_count) {}
+		
+		LLUUID mID;
+		S32 mFace;
+		S32 mVertexCount;
+		
+		bool operator<(const LLDeformTableCacheIndex& lhs) const
+		{
+			if (mID < lhs.mID)
+				return TRUE;
+			if (mID > lhs.mID)
+				return FALSE;
+			if (mFace < lhs.mFace)
+				return TRUE;
+			if (mFace > lhs.mFace)
+				return FALSE;
+			if (mVertexCount < lhs.mVertexCount)
+				return TRUE;
+			
+			return FALSE;
+		}
+	};
+	
+	
+	// these are entries in the deformer table.
+	// they map vertices on the volume mesh to
+	// vertices on the avatar mesh(es).
+	struct LLDeformMap
+	{
+		U8 mMesh;
+		U16 mVertex;
+	};
+	
+	typedef std::vector<struct LLDeformMap> deform_table_t;
+	typedef std::map<LLDeformTableCacheIndex, deform_table_t > deform_cache_t;
+	deform_cache_t mDeformCache;
+	
+	void deform(LLVolume* source, LLVOAvatar* avatar, const LLMeshSkinInfo* skin, S32 face);
+	
+private:
+	deform_table_t& getDeformTable(LLVolume* source, LLVOAvatar* avatar, const LLMeshSkinInfo* skin, S32 face);
+};
+
+
+
 // Base class for implementations of the volume - Primitive, Flexible Object, etc.
 class LLVolumeInterface
 {
@@ -315,6 +377,9 @@
 
 	//clear out rigged volume and revert back to non-rigged state for picking/LOD/distance updates
 	void clearRiggedVolume();
+	
+	// deformed volume (rigged attachments follow avatar morph shape changes
+	LLDeformedVolume* getDeformedVolume();
 
 protected:
 	S32	computeLODDetail(F32	distance, F32 radius);
@@ -359,6 +424,7 @@
 	S32 mMDCImplCount;
 
 	LLPointer<LLRiggedVolume> mRiggedVolume;
+	LLPointer<LLDeformedVolume> mDeformedVolume;
 
 	// statics
 public:
