PageRenderTime 100ms CodeModel.GetById 18ms app.highlight 76ms RepoModel.GetById 1ms app.codeStats 1ms

/indra/newview/llpolymorph.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 718 lines | 499 code | 98 blank | 121 comment | 83 complexity | 812a435101013222ce79e19bb74f87e6 MD5 | raw file
  1/** 
  2 * @file llpolymorph.cpp
  3 * @brief Implementation of LLPolyMesh class
  4 *
  5 * $LicenseInfo:firstyear=2001&license=viewerlgpl$
  6 * Second Life Viewer Source Code
  7 * Copyright (C) 2010, Linden Research, Inc.
  8 * 
  9 * This library is free software; you can redistribute it and/or
 10 * modify it under the terms of the GNU Lesser General Public
 11 * License as published by the Free Software Foundation;
 12 * version 2.1 of the License only.
 13 * 
 14 * This library is distributed in the hope that it will be useful,
 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 17 * Lesser General Public License for more details.
 18 * 
 19 * You should have received a copy of the GNU Lesser General Public
 20 * License along with this library; if not, write to the Free Software
 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 22 * 
 23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 24 * $/LicenseInfo$
 25 */
 26
 27//-----------------------------------------------------------------------------
 28// Header Files
 29//-----------------------------------------------------------------------------
 30#include "llviewerprecompiledheaders.h"
 31
 32#include "llpolymorph.h"
 33#include "llvoavatar.h"
 34#include "llwearable.h"
 35#include "llxmltree.h"
 36#include "llendianswizzle.h"
 37
 38//#include "../tools/imdebug/imdebug.h"
 39
 40const F32 NORMAL_SOFTEN_FACTOR = 0.65f;
 41
 42//-----------------------------------------------------------------------------
 43// LLPolyMorphData()
 44//-----------------------------------------------------------------------------
 45LLPolyMorphData::LLPolyMorphData(const std::string& morph_name)
 46	: mName(morph_name)
 47{
 48	mNumIndices = 0;
 49	mCurrentIndex = 0;
 50	mTotalDistortion = 0.f;
 51	mAvgDistortion.zeroVec();
 52	mMaxDistortion = 0.f;
 53	mVertexIndices = NULL;
 54	mCoords = NULL;
 55	mNormals = NULL;
 56	mBinormals = NULL;
 57	mTexCoords = NULL;
 58
 59	mMesh = NULL;
 60}
 61
 62LLPolyMorphData::LLPolyMorphData(const LLPolyMorphData &rhs) :
 63	mName(rhs.mName),
 64	mNumIndices(rhs.mNumIndices),
 65	mTotalDistortion(rhs.mTotalDistortion),
 66	mAvgDistortion(rhs.mAvgDistortion),
 67	mMaxDistortion(rhs.mMaxDistortion),
 68	mVertexIndices(NULL),
 69	mCoords(NULL),
 70	mNormals(NULL),
 71	mBinormals(NULL),
 72	mTexCoords(NULL)
 73{
 74	const S32 numVertices = mNumIndices;
 75
 76	mCoords = new LLVector3[numVertices];
 77	mNormals = new LLVector3[numVertices];
 78	mBinormals = new LLVector3[numVertices];
 79	mTexCoords = new LLVector2[numVertices];
 80	mVertexIndices = new U32[numVertices];
 81	
 82	for (S32 v=0; v < numVertices; v++)
 83	{
 84		mCoords[v] = rhs.mCoords[v];
 85		mNormals[v] = rhs.mNormals[v];
 86		mBinormals[v] = rhs.mBinormals[v];
 87		mTexCoords[v] = rhs.mTexCoords[v];
 88		mVertexIndices[v] = rhs.mVertexIndices[v];
 89	}
 90}
 91
 92
 93//-----------------------------------------------------------------------------
 94// ~LLPolyMorphData()
 95//-----------------------------------------------------------------------------
 96LLPolyMorphData::~LLPolyMorphData()
 97{
 98	delete [] mVertexIndices;
 99	delete [] mCoords;
100	delete [] mNormals;
101	delete [] mBinormals;
102	delete [] mTexCoords;
103}
104
105//-----------------------------------------------------------------------------
106// loadBinary()
107//-----------------------------------------------------------------------------
108BOOL LLPolyMorphData::loadBinary(LLFILE *fp, LLPolyMeshSharedData *mesh)
109{
110	S32 numVertices;
111	S32 numRead;
112
113	numRead = fread(&numVertices, sizeof(S32), 1, fp);
114	llendianswizzle(&numVertices, sizeof(S32), 1);
115	if (numRead != 1)
116	{
117		llwarns << "Can't read number of morph target vertices" << llendl;
118		return FALSE;
119	}
120
121	//-------------------------------------------------------------------------
122	// allocate vertices
123	//-------------------------------------------------------------------------
124	mCoords = new LLVector3[numVertices];
125	mNormals = new LLVector3[numVertices];
126	mBinormals = new LLVector3[numVertices];
127	mTexCoords = new LLVector2[numVertices];
128	// Actually, we are allocating more space than we need for the skiplist
129	mVertexIndices = new U32[numVertices];
130	mNumIndices = 0;
131	mTotalDistortion = 0.f;
132	mMaxDistortion = 0.f;
133	mAvgDistortion.zeroVec();
134	mMesh = mesh;
135
136	//-------------------------------------------------------------------------
137	// read vertices
138	//-------------------------------------------------------------------------
139	for(S32 v = 0; v < numVertices; v++)
140	{
141		numRead = fread(&mVertexIndices[v], sizeof(U32), 1, fp);
142		llendianswizzle(&mVertexIndices[v], sizeof(U32), 1);
143		if (numRead != 1)
144		{
145			llwarns << "Can't read morph target vertex number" << llendl;
146			return FALSE;
147		}
148
149		if (mVertexIndices[v] > 10000)
150		{
151			llerrs << "Bad morph index: " << mVertexIndices[v] << llendl;
152		}
153
154
155		numRead = fread(&mCoords[v].mV, sizeof(F32), 3, fp);
156		llendianswizzle(&mCoords[v].mV, sizeof(F32), 3);
157		if (numRead != 3)
158		{
159			llwarns << "Can't read morph target vertex coordinates" << llendl;
160			return FALSE;
161		}
162
163		F32 magnitude = mCoords[v].magVec();
164		
165		mTotalDistortion += magnitude;
166		mAvgDistortion.mV[VX] += fabs(mCoords[v].mV[VX]);
167		mAvgDistortion.mV[VY] += fabs(mCoords[v].mV[VY]);
168		mAvgDistortion.mV[VZ] += fabs(mCoords[v].mV[VZ]);
169		
170		if (magnitude > mMaxDistortion)
171		{
172			mMaxDistortion = magnitude;
173		}
174
175		numRead = fread(&mNormals[v].mV, sizeof(F32), 3, fp);
176		llendianswizzle(&mNormals[v].mV, sizeof(F32), 3);
177		if (numRead != 3)
178		{
179			llwarns << "Can't read morph target normal" << llendl;
180			return FALSE;
181		}
182
183		numRead = fread(&mBinormals[v].mV, sizeof(F32), 3, fp);
184		llendianswizzle(&mBinormals[v].mV, sizeof(F32), 3);
185		if (numRead != 3)
186		{
187			llwarns << "Can't read morph target binormal" << llendl;
188			return FALSE;
189		}
190
191
192		numRead = fread(&mTexCoords[v].mV, sizeof(F32), 2, fp);
193		llendianswizzle(&mTexCoords[v].mV, sizeof(F32), 2);
194		if (numRead != 2)
195		{
196			llwarns << "Can't read morph target uv" << llendl;
197			return FALSE;
198		}
199
200		mNumIndices++;
201	}
202
203	mAvgDistortion = mAvgDistortion * (1.f/(F32)mNumIndices);
204	mAvgDistortion.normVec();
205
206	return TRUE;
207}
208
209//-----------------------------------------------------------------------------
210// LLPolyMorphTargetInfo()
211//-----------------------------------------------------------------------------
212LLPolyMorphTargetInfo::LLPolyMorphTargetInfo()
213	: mIsClothingMorph(FALSE)
214{
215}
216
217BOOL LLPolyMorphTargetInfo::parseXml(LLXmlTreeNode* node)
218{
219	llassert( node->hasName( "param" ) && node->getChildByName( "param_morph" ) );
220
221	if (!LLViewerVisualParamInfo::parseXml(node))
222		return FALSE;
223
224	// Get mixed-case name
225	static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name");
226	if( !node->getFastAttributeString( name_string, mMorphName ) )
227	{
228		llwarns << "Avatar file: <param> is missing name attribute" << llendl;
229		return FALSE;  // Continue, ignoring this tag
230	}
231
232	static LLStdStringHandle clothing_morph_string = LLXmlTree::addAttributeString("clothing_morph");
233	node->getFastAttributeBOOL(clothing_morph_string, mIsClothingMorph);
234
235	LLXmlTreeNode *paramNode = node->getChildByName("param_morph");
236
237        if (NULL == paramNode)
238        {
239                llwarns << "Failed to getChildByName(\"param_morph\")"
240                        << llendl;
241                return FALSE;
242        }
243
244	for (LLXmlTreeNode* child_node = paramNode->getFirstChild();
245		 child_node;
246		 child_node = paramNode->getNextChild())
247	{
248		static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name");
249		if (child_node->hasName("volume_morph"))
250		{
251			std::string volume_name;
252			if (child_node->getFastAttributeString(name_string, volume_name))
253			{
254				LLVector3 scale;
255				static LLStdStringHandle scale_string = LLXmlTree::addAttributeString("scale");
256				child_node->getFastAttributeVector3(scale_string, scale);
257				
258				LLVector3 pos;
259				static LLStdStringHandle pos_string = LLXmlTree::addAttributeString("pos");
260				child_node->getFastAttributeVector3(pos_string, pos);
261
262				mVolumeInfoList.push_back(LLPolyVolumeMorphInfo(volume_name,scale,pos));
263			}
264		}
265	}
266	
267	return TRUE;
268}
269
270//-----------------------------------------------------------------------------
271// LLPolyMorphTarget()
272//-----------------------------------------------------------------------------
273LLPolyMorphTarget::LLPolyMorphTarget(LLPolyMesh *poly_mesh)
274	: mMorphData(NULL), mMesh(poly_mesh),
275	  mVertMask(NULL),
276	  mLastSex(SEX_FEMALE),
277	  mNumMorphMasksPending(0)
278{
279}
280
281//-----------------------------------------------------------------------------
282// ~LLPolyMorphTarget()
283//-----------------------------------------------------------------------------
284LLPolyMorphTarget::~LLPolyMorphTarget()
285{
286	if (mVertMask)
287	{
288		delete mVertMask;
289	}
290}
291
292//-----------------------------------------------------------------------------
293// setInfo()
294//-----------------------------------------------------------------------------
295BOOL LLPolyMorphTarget::setInfo(LLPolyMorphTargetInfo* info)
296{
297	llassert(mInfo == NULL);
298	if (info->mID < 0)
299		return FALSE;
300	mInfo = info;
301	mID = info->mID;
302	setWeight(getDefaultWeight(), FALSE );
303
304	LLVOAvatar* avatarp = mMesh->getAvatar();
305	LLPolyMorphTargetInfo::volume_info_list_t::iterator iter;
306	for (iter = getInfo()->mVolumeInfoList.begin(); iter != getInfo()->mVolumeInfoList.end(); iter++)
307	{
308		LLPolyVolumeMorphInfo *volume_info = &(*iter);
309		for (S32 i = 0; i < avatarp->mNumCollisionVolumes; i++)
310		{
311			if (avatarp->mCollisionVolumes[i].getName() == volume_info->mName)
312			{
313				mVolumeMorphs.push_back(LLPolyVolumeMorph(&avatarp->mCollisionVolumes[i],
314														  volume_info->mScale,
315														  volume_info->mPos));
316				break;
317			}
318		}
319	}
320
321	std::string morph_param_name = getInfo()->mMorphName;
322	
323	mMorphData = mMesh->getMorphData(morph_param_name);
324	if (!mMorphData)
325	{
326		const std::string driven_tag = "_Driven";
327		U32 pos = morph_param_name.find(driven_tag);
328		if (pos > 0)
329		{
330			morph_param_name = morph_param_name.substr(0,pos);
331			mMorphData = mMesh->getMorphData(morph_param_name);
332		}
333	}
334	if (!mMorphData)
335	{
336		llwarns << "No morph target named " << morph_param_name << " found in mesh." << llendl;
337		return FALSE;  // Continue, ignoring this tag
338	}
339	return TRUE;
340}
341
342/*virtual*/ LLViewerVisualParam* LLPolyMorphTarget::cloneParam(LLWearable* wearable) const
343{
344	LLPolyMorphTarget *new_param = new LLPolyMorphTarget(mMesh);
345	*new_param = *this;
346	return new_param;
347}
348
349#if 0 // obsolete
350//-----------------------------------------------------------------------------
351// parseData()
352//-----------------------------------------------------------------------------
353BOOL LLPolyMorphTarget::parseData(LLXmlTreeNode* node)
354{
355	LLPolyMorphTargetInfo* info = new LLPolyMorphTargetInfo;
356
357	info->parseXml(node);
358	if (!setInfo(info))
359	{
360		delete info;
361		return FALSE;
362	}
363	return TRUE;
364}
365#endif
366
367//-----------------------------------------------------------------------------
368// getVertexDistortion()
369//-----------------------------------------------------------------------------
370LLVector3 LLPolyMorphTarget::getVertexDistortion(S32 requested_index, LLPolyMesh *mesh)
371{
372	if (!mMorphData || mMesh != mesh) return LLVector3::zero;
373
374	for(U32 index = 0; index < mMorphData->mNumIndices; index++)
375	{
376		if (mMorphData->mVertexIndices[index] == (U32)requested_index)
377		{
378			return mMorphData->mCoords[index];
379		}
380	}
381
382	return LLVector3::zero;
383}
384
385//-----------------------------------------------------------------------------
386// getFirstDistortion()
387//-----------------------------------------------------------------------------
388const LLVector3 *LLPolyMorphTarget::getFirstDistortion(U32 *index, LLPolyMesh **poly_mesh)
389{
390	if (!mMorphData) return &LLVector3::zero;
391
392	LLVector3* resultVec;
393	mMorphData->mCurrentIndex = 0;
394	if (mMorphData->mNumIndices)
395	{
396		resultVec = &mMorphData->mCoords[mMorphData->mCurrentIndex];
397		if (index != NULL)
398		{
399			*index = mMorphData->mVertexIndices[mMorphData->mCurrentIndex];
400		}
401		if (poly_mesh != NULL)
402		{
403			*poly_mesh = mMesh;
404		}
405
406		return resultVec;
407	}
408	return NULL;
409}
410
411//-----------------------------------------------------------------------------
412// getNextDistortion()
413//-----------------------------------------------------------------------------
414const LLVector3 *LLPolyMorphTarget::getNextDistortion(U32 *index, LLPolyMesh **poly_mesh)
415{
416	if (!mMorphData) return &LLVector3::zero;
417
418	LLVector3* resultVec;
419	mMorphData->mCurrentIndex++;
420	if (mMorphData->mCurrentIndex < mMorphData->mNumIndices)
421	{
422		resultVec = &mMorphData->mCoords[mMorphData->mCurrentIndex];
423		if (index != NULL)
424		{
425			*index = mMorphData->mVertexIndices[mMorphData->mCurrentIndex];
426		}
427		if (poly_mesh != NULL)
428		{
429			*poly_mesh = mMesh;
430		}
431		return resultVec;
432	}
433	return NULL;
434}
435
436//-----------------------------------------------------------------------------
437// getTotalDistortion()
438//-----------------------------------------------------------------------------
439F32	LLPolyMorphTarget::getTotalDistortion() 
440{ 
441	if (mMorphData) 
442	{
443		return mMorphData->mTotalDistortion; 
444	}
445	else 
446	{
447		return 0.f;
448	}
449}
450
451//-----------------------------------------------------------------------------
452// getAvgDistortion()
453//-----------------------------------------------------------------------------
454const LLVector3& LLPolyMorphTarget::getAvgDistortion()	
455{
456	if (mMorphData) 
457	{
458		return mMorphData->mAvgDistortion; 
459	}
460	else 
461	{
462		return LLVector3::zero;
463	}
464}
465
466//-----------------------------------------------------------------------------
467// getMaxDistortion()
468//-----------------------------------------------------------------------------
469F32	LLPolyMorphTarget::getMaxDistortion() 
470{
471	if (mMorphData) 
472	{
473		return mMorphData->mMaxDistortion; 
474	}
475	else
476	{
477		return 0.f;
478	}
479}
480
481//-----------------------------------------------------------------------------
482// apply()
483//-----------------------------------------------------------------------------
484void LLPolyMorphTarget::apply( ESex avatar_sex )
485{
486	if (!mMorphData || mNumMorphMasksPending > 0)
487	{
488		return;
489	}
490
491	mLastSex = avatar_sex;
492
493	// Check for NaN condition (NaN is detected if a variable doesn't equal itself.
494	if (mCurWeight != mCurWeight)
495	{
496		mCurWeight = 0.0;
497	}
498	if (mLastWeight != mLastWeight)
499	{
500		mLastWeight = mCurWeight+.001;
501	}
502
503	// perform differential update of morph
504	F32 delta_weight = ( getSex() & avatar_sex ) ? (mCurWeight - mLastWeight) : (getDefaultWeight() - mLastWeight);
505	// store last weight
506	mLastWeight += delta_weight;
507
508	if (delta_weight != 0.f)
509	{
510		llassert(!mMesh->isLOD());
511		LLVector4 *coords = mMesh->getWritableCoords();
512
513		LLVector3 *scaled_normals = mMesh->getScaledNormals();
514		LLVector4 *normals = mMesh->getWritableNormals();
515
516		LLVector3 *scaled_binormals = mMesh->getScaledBinormals();
517		LLVector3 *binormals = mMesh->getWritableBinormals();
518
519		LLVector4 *clothing_weights = mMesh->getWritableClothingWeights();
520		LLVector2 *tex_coords = mMesh->getWritableTexCoords();
521
522		F32 *maskWeightArray = (mVertMask) ? mVertMask->getMorphMaskWeights() : NULL;
523
524		for(U32 vert_index_morph = 0; vert_index_morph < mMorphData->mNumIndices; vert_index_morph++)
525		{
526			S32 vert_index_mesh = mMorphData->mVertexIndices[vert_index_morph];
527
528			F32 maskWeight = 1.f;
529			if (maskWeightArray)
530			{
531				maskWeight = maskWeightArray[vert_index_morph];
532			}
533
534			coords[vert_index_mesh] += LLVector4(mMorphData->mCoords[vert_index_morph] * delta_weight * maskWeight);
535
536			if (getInfo()->mIsClothingMorph && clothing_weights)
537			{
538				LLVector3 clothing_offset = mMorphData->mCoords[vert_index_morph] * delta_weight * maskWeight;
539				LLVector4* clothing_weight = &clothing_weights[vert_index_mesh];
540				clothing_weight->mV[VX] += clothing_offset.mV[VX];
541				clothing_weight->mV[VY] += clothing_offset.mV[VY];
542				clothing_weight->mV[VZ] += clothing_offset.mV[VZ];
543				clothing_weight->mV[VW] = maskWeight;
544			}
545
546			// calculate new normals based on half angles
547			scaled_normals[vert_index_mesh] += mMorphData->mNormals[vert_index_morph] * delta_weight * maskWeight * NORMAL_SOFTEN_FACTOR;
548			LLVector3 normalized_normal = scaled_normals[vert_index_mesh];
549			normalized_normal.normVec();
550			normals[vert_index_mesh] = LLVector4(normalized_normal);
551
552			// calculate new binormals
553			scaled_binormals[vert_index_mesh] += mMorphData->mBinormals[vert_index_morph] * delta_weight * maskWeight * NORMAL_SOFTEN_FACTOR;
554			LLVector3 tangent = scaled_binormals[vert_index_mesh] % normalized_normal;
555			LLVector3 normalized_binormal = normalized_normal % tangent; 
556			normalized_binormal.normVec();
557			binormals[vert_index_mesh] = normalized_binormal;
558
559			tex_coords[vert_index_mesh] += mMorphData->mTexCoords[vert_index_morph] * delta_weight * maskWeight;
560		}
561
562		// now apply volume changes
563		for( volume_list_t::iterator iter = mVolumeMorphs.begin(); iter != mVolumeMorphs.end(); iter++ )
564		{
565			LLPolyVolumeMorph* volume_morph = &(*iter);
566			LLVector3 scale_delta = volume_morph->mScale * delta_weight;
567			LLVector3 pos_delta = volume_morph->mPos * delta_weight;
568			
569			volume_morph->mVolume->setScale(volume_morph->mVolume->getScale() + scale_delta);
570			volume_morph->mVolume->setPosition(volume_morph->mVolume->getPosition() + pos_delta);
571		}
572	}
573
574	if (mNext)
575	{
576		mNext->apply(avatar_sex);
577	}
578}
579
580//-----------------------------------------------------------------------------
581// applyMask()
582//-----------------------------------------------------------------------------
583void	LLPolyMorphTarget::applyMask(U8 *maskTextureData, S32 width, S32 height, S32 num_components, BOOL invert)
584{
585	LLVector4 *clothing_weights = getInfo()->mIsClothingMorph ? mMesh->getWritableClothingWeights() : NULL;
586
587	if (!mVertMask)
588	{
589		mVertMask = new LLPolyVertexMask(mMorphData);
590		mNumMorphMasksPending--;
591	}
592	else
593	{
594		// remove effect of previous mask
595		F32 *maskWeights = (mVertMask) ? mVertMask->getMorphMaskWeights() : NULL;
596
597		if (maskWeights)
598		{
599			LLVector4 *coords = mMesh->getWritableCoords();
600			LLVector3 *scaled_normals = mMesh->getScaledNormals();
601			LLVector3 *scaled_binormals = mMesh->getScaledBinormals();
602			LLVector2 *tex_coords = mMesh->getWritableTexCoords();
603
604			for(U32 vert = 0; vert < mMorphData->mNumIndices; vert++)
605			{
606				F32 lastMaskWeight = mLastWeight * maskWeights[vert];
607				S32 out_vert = mMorphData->mVertexIndices[vert];
608
609				// remove effect of existing masked morph
610				coords[out_vert] -= LLVector4(mMorphData->mCoords[vert]) * lastMaskWeight;
611				scaled_normals[out_vert] -= mMorphData->mNormals[vert] * lastMaskWeight * NORMAL_SOFTEN_FACTOR;
612				scaled_binormals[out_vert] -= mMorphData->mBinormals[vert] * lastMaskWeight * NORMAL_SOFTEN_FACTOR;
613				tex_coords[out_vert] -= mMorphData->mTexCoords[vert] * lastMaskWeight;
614
615				if (clothing_weights)
616				{
617					LLVector3 clothing_offset = mMorphData->mCoords[vert] * lastMaskWeight;
618					LLVector4* clothing_weight = &clothing_weights[out_vert];
619					clothing_weight->mV[VX] -= clothing_offset.mV[VX];
620					clothing_weight->mV[VY] -= clothing_offset.mV[VY];
621					clothing_weight->mV[VZ] -= clothing_offset.mV[VZ];
622				}
623			}
624		}
625	}
626
627	// set last weight to 0, since we've removed the effect of this morph
628	mLastWeight = 0.f;
629
630	mVertMask->generateMask(maskTextureData, width, height, num_components, invert, clothing_weights);
631
632	apply(mLastSex);
633}
634
635
636//-----------------------------------------------------------------------------
637// LLPolyVertexMask()
638//-----------------------------------------------------------------------------
639LLPolyVertexMask::LLPolyVertexMask(LLPolyMorphData* morph_data)
640{
641	mWeights = new F32[morph_data->mNumIndices];
642	mMorphData = morph_data;
643	mWeightsGenerated = FALSE;
644}
645
646//-----------------------------------------------------------------------------
647// ~LLPolyVertexMask()
648//-----------------------------------------------------------------------------
649LLPolyVertexMask::~LLPolyVertexMask()
650{
651	delete[] mWeights;
652}
653
654//-----------------------------------------------------------------------------
655// generateMask()
656//-----------------------------------------------------------------------------
657void LLPolyVertexMask::generateMask(U8 *maskTextureData, S32 width, S32 height, S32 num_components, BOOL invert, LLVector4 *clothing_weights)
658{
659// RN debug output that uses Image Debugger (http://www.cs.unc.edu/~baxter/projects/imdebug/)
660//	BOOL debugImg = FALSE; 
661//	if (debugImg)
662//	{
663//		if (invert)
664//		{
665//			imdebug("lum rbga=rgba b=8 w=%d h=%d *-1 %p", width, height, maskTextureData);
666//		}
667//		else
668//		{
669//			imdebug("lum rbga=rgba b=8 w=%d h=%d %p", width, height, maskTextureData);
670//		}
671//	}
672	for (U32 index = 0; index < mMorphData->mNumIndices; index++)
673	{
674		S32 vertIndex = mMorphData->mVertexIndices[index];
675		const S32 *sharedVertIndex = mMorphData->mMesh->getSharedVert(vertIndex);
676		LLVector2 uvCoords;
677
678		if (sharedVertIndex)
679		{
680			uvCoords = mMorphData->mMesh->getUVs(*sharedVertIndex);
681		}
682		else
683		{
684			uvCoords = mMorphData->mMesh->getUVs(vertIndex);
685		}
686		U32 s = llclamp((U32)(uvCoords.mV[VX] * (F32)(width - 1)), (U32)0, (U32)width - 1);
687		U32 t = llclamp((U32)(uvCoords.mV[VY] * (F32)(height - 1)), (U32)0, (U32)height - 1);
688		
689		mWeights[index] = ((F32) maskTextureData[((t * width + s) * num_components) + (num_components - 1)]) / 255.f;
690		
691		if (invert) 
692		{
693			mWeights[index] = 1.f - mWeights[index];
694		}
695
696		// now apply step function
697		// mWeights[index] = mWeights[index] > 0.95f ? 1.f : 0.f;
698
699		if (clothing_weights)
700		{
701			clothing_weights[vertIndex].mV[VW] = mWeights[index];
702		}
703	}
704	mWeightsGenerated = TRUE;
705}
706
707//-----------------------------------------------------------------------------
708// getMaskForMorphIndex()
709//-----------------------------------------------------------------------------
710F32* LLPolyVertexMask::getMorphMaskWeights()
711{
712	if (!mWeightsGenerated)
713	{
714		return NULL;
715	}
716	
717	return mWeights;
718}