From 47ffcdb93d6e2ac1f9d497e43e0213c98d129254 Mon Sep 17 00:00:00 2001
From: Dave Parks <davep@lindenlab.com>
Date: Tue, 6 Apr 2010 16:24:08 -0500
Subject: [PATCH] Rigged attachments (almost works).

---
 indra/llmath/llvolume.cpp           |  22 ++++--
 indra/llrender/llglslshader.cpp     |  14 +++-
 indra/llrender/llglslshader.h       |   3 +-
 indra/llrender/llshadermgr.cpp      |   8 ++
 indra/newview/lldrawpoolavatar.cpp  |  26 ++++++-
 indra/newview/lldrawpoolavatar.h    |   2 +
 indra/newview/llface.cpp            |  18 ++++-
 indra/newview/llface.h              |   4 +-
 indra/newview/llspatialpartition.h  |   7 ++
 indra/newview/llviewershadermgr.cpp |  25 +++++++
 indra/newview/llviewershadermgr.h   |   3 +
 indra/newview/llvoavatar.cpp        | 109 ++++++++++++++++++++++++++++
 indra/newview/llvoavatar.h          |   1 +
 indra/newview/llvovolume.cpp        |  20 +++++
 14 files changed, 247 insertions(+), 15 deletions(-)

diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp
index c563af592f1..fdd48b9e9ed 100644
--- a/indra/llmath/llvolume.cpp
+++ b/indra/llmath/llvolume.cpp
@@ -2013,22 +2013,23 @@ bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size)
 			if (mdl[i].has("Weights"))
 			{
 				face.mWeights.resize(num_verts);
+
 				LLSD::Binary weights = mdl[i]["Weights"];
 
-				LLSD::Binary::iterator iter = weights.begin();
+				U32 idx = 0;
 
 				U32 cur_vertex = 0;
-				while (iter != weights.end())
+				while (idx < weights.size() && cur_vertex < num_verts)
 				{
-					const S32 END_INFLUENCES = 0xFF;
-					U8 joint = *(iter++);
+					const U8 END_INFLUENCES = 0xFF;
+					U8 joint = weights[idx++];
 
 					U32 cur_influence = 0;
 					while (joint != END_INFLUENCES)
 					{
-						U16 influence = *(iter++);
+						U16 influence = weights[idx++];
 						influence = influence << 8;
-						influence |= *(iter++);
+						influence |= weights[idx++];
 
 						F32 w = llmin((F32) influence / 65535.f, 0.99999f);
 						face.mWeights[cur_vertex].mV[cur_influence++] = (F32) joint + w;
@@ -2039,13 +2040,18 @@ bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size)
 						}
 						else
 						{
-							joint = *(iter++);
+							joint = weights[idx++];
 						}
 					}
 
 					cur_vertex++;
-					iter++;
 				}
+
+				if (cur_vertex != num_verts || idx != weights.size())
+				{
+					llwarns << "Vertex weight count does not match vertex count!" << llendl;
+				}
+					
 			}
 
 			LLVector3 min_pos;
diff --git a/indra/llrender/llglslshader.cpp b/indra/llrender/llglslshader.cpp
index ca92cb6580f..949057df049 100644
--- a/indra/llrender/llglslshader.cpp
+++ b/indra/llrender/llglslshader.cpp
@@ -61,7 +61,7 @@ BOOL shouldChange(const LLVector4& v1, const LLVector4& v2)
 
 LLShaderFeatures::LLShaderFeatures()
 : calculatesLighting(false), isShiny(false), isFullbright(false), hasWaterFog(false),
-hasTransport(false), hasSkinning(false), hasAtmospherics(false), isSpecular(false),
+hasTransport(false), hasSkinning(false), hasObjectSkinning(false), hasAtmospherics(false), isSpecular(false),
 hasGamma(false), hasLighting(false), calculatesAtmospherics(false)
 {
 }
@@ -717,6 +717,18 @@ GLint LLGLSLShader::getUniformLocation(const string& uniform)
 	return -1;
 }
 
+GLint LLGLSLShader::getAttribLocation(U32 attrib)
+{
+	if (attrib < mAttribute.size())
+	{
+		return mAttribute[attrib];
+	}
+	else
+	{
+		return -1;
+	}
+}
+
 void LLGLSLShader::uniform1i(const string& uniform, GLint v)
 {
 	GLint location = getUniformLocation(uniform);
diff --git a/indra/llrender/llglslshader.h b/indra/llrender/llglslshader.h
index 166d4af04ca..dc493ba1629 100644
--- a/indra/llrender/llglslshader.h
+++ b/indra/llrender/llglslshader.h
@@ -48,6 +48,7 @@ class LLShaderFeatures
 	bool hasWaterFog; // implies no gamma
 	bool hasTransport; // implies no lighting (it's possible to have neither though)
 	bool hasSkinning;	
+	bool hasObjectSkinning;
 	bool hasAtmospherics;
 	bool hasGamma;
 
@@ -109,7 +110,7 @@ class LLGLSLShader
 	void vertexAttrib4fv(U32 index, GLfloat* v);
 	
 	GLint getUniformLocation(const std::string& uniform);
-	
+	GLint getAttribLocation(U32 attrib);
 	GLint mapUniformTextureChannel(GLint location, GLenum type);
 	
 
diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp
index 1286e91e49c..23b76351ebb 100644
--- a/indra/llrender/llshadermgr.cpp
+++ b/indra/llrender/llshadermgr.cpp
@@ -152,6 +152,14 @@ BOOL LLShaderMgr::attachShaderFeatures(LLGLSLShader * shader)
 			return FALSE;
 		}
 	}
+
+	if (features->hasObjectSkinning)
+	{
+		if (!shader->attachObject("avatar/objectSkinV.glsl"))
+		{
+			return FALSE;
+		}
+	}
 	
 	///////////////////////////////////////
 	// Attach Fragment Shader Features Next
diff --git a/indra/newview/lldrawpoolavatar.cpp b/indra/newview/lldrawpoolavatar.cpp
index 012e41383fb..9311a5f60ec 100644
--- a/indra/newview/lldrawpoolavatar.cpp
+++ b/indra/newview/lldrawpoolavatar.cpp
@@ -320,7 +320,7 @@ void LLDrawPoolAvatar::renderShadow(S32 pass)
 
 S32 LLDrawPoolAvatar::getNumPasses()
 {
-	return LLPipeline::sImpostorRender ? 1 : 3;
+	return LLPipeline::sImpostorRender ? 1 : 4;
 }
 
 void LLDrawPoolAvatar::render(S32 pass)
@@ -357,6 +357,8 @@ void LLDrawPoolAvatar::beginRenderPass(S32 pass)
 		break;
 	case 2:
 		beginSkinned();
+	case 3:
+		beginRigged();
 		break;
 	}
 }
@@ -381,6 +383,10 @@ void LLDrawPoolAvatar::endRenderPass(S32 pass)
 		break;
 	case 2:
 		endSkinned();
+		break;
+	case 3:
+		endRigged();
+		break;
 	}
 }
 
@@ -566,6 +572,18 @@ void LLDrawPoolAvatar::endSkinned()
 	gGL.getTexUnit(0)->activate();
 }
 
+void LLDrawPoolAvatar::beginRigged()
+{
+	gSkinnedObjectSimpleProgram.bind();
+	LLVertexBuffer::sWeight4Loc = gSkinnedObjectSimpleProgram.getAttribLocation(LLViewerShaderMgr::OBJECT_WEIGHT);
+}
+
+void LLDrawPoolAvatar::endRigged()
+{
+	gSkinnedObjectSimpleProgram.unbind();
+	LLVertexBuffer::sWeight4Loc = -1;
+}
+
 void LLDrawPoolAvatar::beginDeferredSkinned()
 {
 	sShaderLevel = mVertexShaderLevel;
@@ -711,6 +729,12 @@ void LLDrawPoolAvatar::renderAvatars(LLVOAvatar* single_avatar, S32 pass)
 		avatarp->renderRigid();
 		return;
 	}
+
+	if (pass == 3)
+	{
+		avatarp->renderSkinnedAttachments();
+		return;
+	}
 	
 	if (sShaderLevel > 0)
 	{
diff --git a/indra/newview/lldrawpoolavatar.h b/indra/newview/lldrawpoolavatar.h
index b9479436199..c43aa9b1e30 100644
--- a/indra/newview/lldrawpoolavatar.h
+++ b/indra/newview/lldrawpoolavatar.h
@@ -90,10 +90,12 @@ class LLDrawPoolAvatar : public LLFacePool
 	void beginRigid();
 	void beginFootShadow();
 	void beginSkinned();
+	void beginRigged();
 		
 	void endRigid();
 	void endFootShadow();
 	void endSkinned();
+	void endRigged();
 
 	void beginDeferredImpostor();
 	void beginDeferredRigid();
diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp
index 53330e4d985..bc3e04db183 100644
--- a/indra/newview/llface.cpp
+++ b/indra/newview/llface.cpp
@@ -888,7 +888,8 @@ static LLFastTimer::DeclareTimer FTM_FACE_GET_GEOM("Face Geom");
 BOOL LLFace::getGeometryVolume(const LLVolume& volume,
 							   const S32 &f,
 								const LLMatrix4& mat_vert, const LLMatrix3& mat_normal,
-								const U16 &index_offset)
+								const U16 &index_offset,
+								bool force_rebuild)
 {
 	LLFastTimer t(FTM_FACE_GET_GEOM);
 	const LLVolumeFace &vf = volume.getVolumeFace(f);
@@ -925,8 +926,9 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
 	LLStrider<LLColor4U> colors;
 	LLStrider<LLVector3> binormals;
 	LLStrider<U16> indicesp;
+	LLStrider<LLVector4> weights;
 
-	BOOL full_rebuild = mDrawablep->isState(LLDrawable::REBUILD_VOLUME);
+	BOOL full_rebuild = force_rebuild || mDrawablep->isState(LLDrawable::REBUILD_VOLUME);
 	
 	BOOL global_volume = mDrawablep->getVOVolume()->isVolumeGlobal();
 	LLVector3 scale;
@@ -944,6 +946,7 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
 	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_weights = rebuild_pos && mVertexBuffer->hasDataType(LLVertexBuffer::TYPE_WEIGHT4);
 
 	const LLTextureEntry *tep = mVObjp->getTE(f);
 	U8  bump_code = tep ? tep->getBumpmap() : 0;
@@ -960,7 +963,11 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
 	{
 		mVertexBuffer->getBinormalStrider(binormals, mGeomIndex);
 	}
-
+	if (rebuild_weights)
+	{
+		mVertexBuffer->getWeight4Strider(weights, mGeomIndex);
+	}
+	
 	F32 tcoord_xoffset = 0.f ;
 	F32 tcoord_yoffset = 0.f ;
 	F32 tcoord_xscale = 1.f ;
@@ -1338,6 +1345,11 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,
 			*binormals++ = binormal;
 		}
 		
+		if (rebuild_weights)
+		{
+			*weights++ = vf.mWeights[i];
+		}
+
 		if (rebuild_color)
 		{
 			*colors++ = color;		
diff --git a/indra/newview/llface.h b/indra/newview/llface.h
index 67dd97e6f7a..06ec043c760 100644
--- a/indra/newview/llface.h
+++ b/indra/newview/llface.h
@@ -73,6 +73,7 @@ class LLFace
 		HUD_RENDER		= 0x0008,
 		USE_FACE_COLOR	= 0x0010,
 		TEXTURE_ANIM	= 0x0020, 
+		RIGGED			= 0x0040,
 	};
 
 	static void initClass();
@@ -145,7 +146,8 @@ class LLFace
 	BOOL getGeometryVolume(const LLVolume& volume,
 						const S32 &f,
 						const LLMatrix4& mat_vert, const LLMatrix3& mat_normal,
-						const U16 &index_offset);
+						const U16 &index_offset,
+						bool force_rebuild = false);
 
 	// For avatar
 	U16			 getGeometryAvatar(
diff --git a/indra/newview/llspatialpartition.h b/indra/newview/llspatialpartition.h
index d74216de2d1..b5e59673749 100644
--- a/indra/newview/llspatialpartition.h
+++ b/indra/newview/llspatialpartition.h
@@ -615,6 +615,13 @@ class LLCloudPartition : public LLParticlePartition
 class LLVolumeGeometryManager: public LLGeometryManager
 {
  public:
+	typedef enum
+	{
+		NONE = 0,
+		BATCH_SORT,
+		DISTANCE_SORT
+	} eSortType;
+
 	virtual ~LLVolumeGeometryManager() { }
 	virtual void rebuildGeom(LLSpatialGroup* group);
 	virtual void rebuildMesh(LLSpatialGroup* group);
diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp
index a0d0b9d4904..8a68dd6ea70 100644
--- a/indra/newview/llviewershadermgr.cpp
+++ b/indra/newview/llviewershadermgr.cpp
@@ -77,6 +77,9 @@ LLGLSLShader		gObjectFullbrightShinyProgram;
 LLGLSLShader		gObjectShinyProgram;
 LLGLSLShader		gObjectShinyWaterProgram;
 
+//object hardware skinning shaders
+LLGLSLShader		gSkinnedObjectSimpleProgram;
+
 //environment shaders
 LLGLSLShader		gTerrainProgram;
 LLGLSLShader		gTerrainWaterProgram;
@@ -148,6 +151,7 @@ LLViewerShaderMgr::LLViewerShaderMgr() :
 	mShaderList.push_back(&gObjectSimpleProgram);
 	mShaderList.push_back(&gObjectFullbrightProgram);
 	mShaderList.push_back(&gObjectFullbrightShinyProgram);
+	mShaderList.push_back(&gSkinnedObjectSimpleProgram);
 	mShaderList.push_back(&gTerrainProgram);
 	mShaderList.push_back(&gTerrainWaterProgram);
 	mShaderList.push_back(&gObjectSimpleWaterProgram);
@@ -195,6 +199,7 @@ void LLViewerShaderMgr::initAttribsAndUniforms(void)
 		mReservedAttribs.push_back("materialColor");
 		mReservedAttribs.push_back("specularColor");
 		mReservedAttribs.push_back("binormal");
+		mReservedAttribs.push_back("object_weight");
 
 		mAvatarAttribs.reserve(5);
 		mAvatarAttribs.push_back("weight");
@@ -548,6 +553,9 @@ void LLViewerShaderMgr::unloadShaders()
 	gObjectShinyProgram.unload();
 	gObjectFullbrightShinyProgram.unload();
 	gObjectShinyWaterProgram.unload();
+
+	gSkinnedObjectSimpleProgram.unload();
+
 	gWaterProgram.unload();
 	gUnderWaterProgram.unload();
 	gTerrainProgram.unload();
@@ -625,6 +633,7 @@ BOOL LLViewerShaderMgr::loadBasicShaders()
 	shaders.push_back( make_pair( "lighting/lightSpecularV.glsl",			mVertexShaderLevel[SHADER_LIGHTING] ) );
 	shaders.push_back( make_pair( "windlight/atmosphericsV.glsl",			mVertexShaderLevel[SHADER_WINDLIGHT] ) );
 	shaders.push_back( make_pair( "avatar/avatarSkinV.glsl",				1 ) );
+	shaders.push_back( make_pair( "avatar/objectSkinV.glsl",				1 ) );
 
 	// We no longer have to bind the shaders to global glhandles, they are automatically added to a map now.
 	for (U32 i = 0; i < shaders.size(); i++)
@@ -1214,6 +1223,7 @@ BOOL LLViewerShaderMgr::loadShadersObject()
 		gObjectSimpleWaterProgram.unload();
 		gObjectFullbrightProgram.unload();
 		gObjectFullbrightWaterProgram.unload();
+		gSkinnedObjectSimpleProgram.unload();
 		return FALSE;
 	}
 
@@ -1323,6 +1333,21 @@ BOOL LLViewerShaderMgr::loadShadersObject()
 		success = gObjectFullbrightShinyProgram.createShader(NULL, &mShinyUniforms);
 	}
 
+	if (success)
+	{
+		gSkinnedObjectSimpleProgram.mName = "Skinned Simple Shader";
+		gSkinnedObjectSimpleProgram.mFeatures.calculatesLighting = true;
+		gSkinnedObjectSimpleProgram.mFeatures.calculatesAtmospherics = true;
+		gSkinnedObjectSimpleProgram.mFeatures.hasGamma = true;
+		gSkinnedObjectSimpleProgram.mFeatures.hasAtmospherics = true;
+		gSkinnedObjectSimpleProgram.mFeatures.hasLighting = true;
+		gSkinnedObjectSimpleProgram.mFeatures.hasObjectSkinning = true;
+		gSkinnedObjectSimpleProgram.mShaderFiles.clear();
+		gSkinnedObjectSimpleProgram.mShaderFiles.push_back(make_pair("objects/simpleSkinnedV.glsl", GL_VERTEX_SHADER_ARB));
+		gSkinnedObjectSimpleProgram.mShaderFiles.push_back(make_pair("objects/simpleF.glsl", GL_FRAGMENT_SHADER_ARB));
+		gSkinnedObjectSimpleProgram.mShaderLevel = mVertexShaderLevel[SHADER_OBJECT];
+		success = gSkinnedObjectSimpleProgram.createShader(NULL, NULL);
+	}
 
 	if( !success )
 	{
diff --git a/indra/newview/llviewershadermgr.h b/indra/newview/llviewershadermgr.h
index ac2b4624e02..83a650cdbc7 100644
--- a/indra/newview/llviewershadermgr.h
+++ b/indra/newview/llviewershadermgr.h
@@ -82,6 +82,7 @@ class LLViewerShaderMgr: public LLShaderMgr
 		MATERIAL_COLOR = 0,
 		SPECULAR_COLOR,
 		BINORMAL,
+		OBJECT_WEIGHT,
 		END_RESERVED_ATTRIBS
 	} eGLSLReservedAttribs;
 	
@@ -313,6 +314,8 @@ extern LLGLSLShader			gObjectFullbrightShinyProgram;
 extern LLGLSLShader			gObjectShinyProgram;
 extern LLGLSLShader			gObjectShinyWaterProgram;
 
+extern LLGLSLShader			gSkinnedObjectSimpleProgram;
+
 //environment shaders
 extern LLGLSLShader			gTerrainProgram;
 extern LLGLSLShader			gTerrainWaterProgram;
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index c400e8510e6..535440692fd 100644
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -64,6 +64,7 @@
 #include "llkeyframefallmotion.h"
 #include "llkeyframestandmotion.h"
 #include "llkeyframewalkmotion.h"
+#include "llmeshrepository.h"
 #include "llmutelist.h"
 #include "llmoveview.h"
 #include "llquantize.h"
@@ -79,6 +80,7 @@
 #include "llviewermenu.h"
 #include "llviewerobjectlist.h"
 #include "llviewerparcelmgr.h"
+#include "llviewershadermgr.h"
 #include "llviewerstats.h"
 #include "llvoavatarself.h"
 #include "llvovolume.h"
@@ -3644,6 +3646,113 @@ bool LLVOAvatar::shouldAlphaMask()
 
 }
 
+U32 LLVOAvatar::renderSkinnedAttachments()
+{
+	U32 num_indices = 0;
+	
+	const U32 data_mask =	LLVertexBuffer::MAP_VERTEX | 
+							LLVertexBuffer::MAP_NORMAL | 
+							LLVertexBuffer::MAP_TEXCOORD0 | 
+							LLVertexBuffer::MAP_WEIGHT4;
+
+	for (attachment_map_t::const_iterator iter = mAttachmentPoints.begin(); 
+		 iter != mAttachmentPoints.end();
+		 ++iter)
+	{
+		LLViewerJointAttachment* attachment = iter->second;
+		for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin();
+			 attachment_iter != attachment->mAttachedObjects.end();
+			 ++attachment_iter)
+		{
+			const LLViewerObject* attached_object = (*attachment_iter);
+			if (attached_object && !attached_object->isHUDAttachment())
+			{
+				const LLDrawable* drawable = attached_object->mDrawable;
+				if (drawable)
+				{
+					for (S32 i = 0; i < drawable->getNumFaces(); ++i)
+					{
+						LLFace* face = drawable->getFace(i);
+						if (face->isState(LLFace::RIGGED))
+						{
+							LLVolume* volume = attached_object->getVolume();
+							const LLVolumeFace& vol_face = volume->getVolumeFace(i);
+
+							const LLMeshSkinInfo* skin = NULL;
+							LLVertexBuffer* buff = face->mVertexBuffer;
+
+							if (!buff || 
+								!buff->hasDataType(LLVertexBuffer::TYPE_WEIGHT4) ||
+								buff->getRequestedVerts() != vol_face.mVertices.size())
+							{
+								face->mVertexBuffer = NULL;
+								face->mLastVertexBuffer = NULL;
+								buff = NULL;
+
+								LLUUID mesh_id = volume->getParams().getSculptID();
+								if (mesh_id.notNull())
+								{
+									skin = gMeshRepo.getSkinInfo(mesh_id);
+									if (skin)
+									{
+										face->mVertexBuffer = new LLVertexBuffer(data_mask, 0);
+										face->mVertexBuffer->allocateBuffer(vol_face.mVertices.size(), vol_face.mIndices.size(), true);
+
+										face->setGeomIndex(0);
+										face->setIndicesIndex(0);
+										
+										U16 offset = 0;
+										
+										LLMatrix4 mat_vert = skin->mBindShapeMatrix;
+										LLMatrix3 mat_normal;
+
+										face->getGeometryVolume(*volume, i, mat_vert, mat_normal, offset, true);
+										buff = face->mVertexBuffer;
+									}
+								}
+							}								
+							
+							if (buff)
+							{
+								if (skin)
+								{
+									LLMatrix4 mat[64];
+
+									for (U32 i = 0; i < skin->mJointNames.size(); ++i)
+									{
+										LLJoint* joint = getJoint(skin->mJointNames[i]);
+										if (joint)
+										{
+											mat[i] = skin->mInvBindMatrix[i];
+											mat[i] *= joint->getWorldMatrix();
+										}
+									}
+									
+									gSkinnedObjectSimpleProgram.uniformMatrix4fv("matrixPalette", 
+										skin->mJointNames.size(),
+										FALSE,
+										(GLfloat*) mat[0].mMatrix);
+
+									buff->setBuffer(data_mask);
+
+									U16 start = face->getGeomStart();
+									U16 end = start + face->getGeomCount();
+									S32 offset = face->getIndicesStart();
+									U32 count = face->getIndicesCount();
+
+									buff->drawRange(LLRender::TRIANGLES, start, end, count, offset);
+								}
+							}
+						}
+					}
+				}
+			}
+		}
+	}
+
+	return num_indices;
+}
+
 //-----------------------------------------------------------------------------
 // renderSkinned()
 //-----------------------------------------------------------------------------
diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h
index d5485413f40..b0535a4a265 100644
--- a/indra/newview/llvoavatar.h
+++ b/indra/newview/llvoavatar.h
@@ -339,6 +339,7 @@ class LLVOAvatar :
 	U32 		renderImpostor(LLColor4U color = LLColor4U(255,255,255,255), S32 diffuse_channel = 0);
 	U32 		renderRigid();
 	U32 		renderSkinned(EAvatarRenderPass pass);
+	U32			renderSkinnedAttachments();
 	U32 		renderTransparent(BOOL first_pass);
 	void 		renderCollisionVolumes();
 	static void	deleteCachedImages(bool clearAll=true);
diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp
index bc83e11fd27..56fb42bb890 100644
--- a/indra/newview/llvovolume.cpp
+++ b/indra/newview/llvovolume.cpp
@@ -3496,6 +3496,10 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
 
 		drawablep->clearState(LLDrawable::HAS_ALPHA);
 
+		bool rigged = vobj->isAttachment() && 
+					vobj->isMesh() && 
+					gMeshRepo.getSkinInfo(vobj->getVolume()->getParams().getSculptID());
+
 		//for each face
 		for (S32 i = 0; i < drawablep->getNumFaces(); i++)
 		{
@@ -3503,6 +3507,22 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
 			drawablep->updateFaceSize(i);
 			LLFace* facep = drawablep->getFace(i);
 
+			if (rigged) 
+			{
+				if (!facep->isState(LLFace::RIGGED))
+				{
+					facep->mVertexBuffer = NULL;
+					facep->mLastVertexBuffer = NULL;
+					facep->setState(LLFace::RIGGED);
+				}
+
+				continue;
+			}
+			else
+			{
+				facep->clearState(LLFace::RIGGED);
+			}
+
 			if (cur_total > max_total || facep->getIndicesCount() <= 0 || facep->getGeomCount() <= 0)
 			{
 				facep->mVertexBuffer = NULL;
-- 
GitLab