PageRenderTime 403ms CodeModel.GetById 14ms app.highlight 355ms RepoModel.GetById 2ms app.codeStats 1ms

/indra/newview/llvovolume.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 2586 lines | 2053 code | 357 blank | 176 comment | 423 complexity | 72a4750d007eb3fabf818833a5f8f90a MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1/** 
   2 * @file llvovolume.cpp
   3 * @brief LLVOVolume class implementation
   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// A "volume" is a box, cylinder, sphere, or other primitive shape.
  28
  29#include "llviewerprecompiledheaders.h"
  30
  31#include "llvovolume.h"
  32
  33#include <sstream>
  34
  35#include "llviewercontrol.h"
  36#include "lldir.h"
  37#include "llflexibleobject.h"
  38#include "llfloatertools.h"
  39#include "llmaterialtable.h"
  40#include "llprimitive.h"
  41#include "llvolume.h"
  42#include "llvolumeoctree.h"
  43#include "llvolumemgr.h"
  44#include "llvolumemessage.h"
  45#include "material_codes.h"
  46#include "message.h"
  47#include "llpluginclassmedia.h" // for code in the mediaEvent handler
  48#include "object_flags.h"
  49#include "llagentconstants.h"
  50#include "lldrawable.h"
  51#include "lldrawpoolavatar.h"
  52#include "lldrawpoolbump.h"
  53#include "llface.h"
  54#include "llspatialpartition.h"
  55#include "llhudmanager.h"
  56#include "llflexibleobject.h"
  57#include "llsky.h"
  58#include "lltexturefetch.h"
  59#include "llvector4a.h"
  60#include "llviewercamera.h"
  61#include "llviewertexturelist.h"
  62#include "llviewerobjectlist.h"
  63#include "llviewerregion.h"
  64#include "llviewertextureanim.h"
  65#include "llworld.h"
  66#include "llselectmgr.h"
  67#include "pipeline.h"
  68#include "llsdutil.h"
  69#include "llmatrix4a.h"
  70#include "llmediaentry.h"
  71#include "llmediadataclient.h"
  72#include "llmeshrepository.h"
  73#include "llagent.h"
  74#include "llviewermediafocus.h"
  75#include "lldatapacker.h"
  76#include "llviewershadermgr.h"
  77#include "llvoavatar.h"
  78#include "llvocache.h"
  79
  80const S32 MIN_QUIET_FRAMES_COALESCE = 30;
  81const F32 FORCE_SIMPLE_RENDER_AREA = 512.f;
  82const F32 FORCE_CULL_AREA = 8.f;
  83const F32 MAX_LOD_DISTANCE = 24.f;
  84
  85
  86BOOL gAnimateTextures = TRUE;
  87//extern BOOL gHideSelectedObjects;
  88
  89F32 LLVOVolume::sLODFactor = 1.f;
  90F32	LLVOVolume::sLODSlopDistanceFactor = 0.5f; //Changing this to zero, effectively disables the LOD transition slop 
  91F32 LLVOVolume::sDistanceFactor = 1.0f;
  92S32 LLVOVolume::sNumLODChanges = 0;
  93S32 LLVOVolume::mRenderComplexity_last = 0;
  94S32 LLVOVolume::mRenderComplexity_current = 0;
  95LLPointer<LLObjectMediaDataClient> LLVOVolume::sObjectMediaClient = NULL;
  96LLPointer<LLObjectMediaNavigateClient> LLVOVolume::sObjectMediaNavigateClient = NULL;
  97
  98static LLFastTimer::DeclareTimer FTM_GEN_TRIANGLES("Generate Triangles");
  99static LLFastTimer::DeclareTimer FTM_GEN_VOLUME("Generate Volumes");
 100static LLFastTimer::DeclareTimer FTM_VOLUME_TEXTURES("Volume Textures");
 101
 102// Implementation class of LLMediaDataClientObject.  See llmediadataclient.h
 103class LLMediaDataClientObjectImpl : public LLMediaDataClientObject
 104{
 105public:
 106	LLMediaDataClientObjectImpl(LLVOVolume *obj, bool isNew) : mObject(obj), mNew(isNew) 
 107	{
 108		mObject->addMDCImpl();
 109	}
 110	~LLMediaDataClientObjectImpl()
 111	{
 112		mObject->removeMDCImpl();
 113	}
 114	
 115	virtual U8 getMediaDataCount() const 
 116		{ return mObject->getNumTEs(); }
 117
 118	virtual LLSD getMediaDataLLSD(U8 index) const 
 119		{
 120			LLSD result;
 121			LLTextureEntry *te = mObject->getTE(index); 
 122			if (NULL != te)
 123			{
 124				llassert((te->getMediaData() != NULL) == te->hasMedia());
 125				if (te->getMediaData() != NULL)
 126				{
 127					result = te->getMediaData()->asLLSD();
 128					// XXX HACK: workaround bug in asLLSD() where whitelist is not set properly
 129					// See DEV-41949
 130					if (!result.has(LLMediaEntry::WHITELIST_KEY))
 131					{
 132						result[LLMediaEntry::WHITELIST_KEY] = LLSD::emptyArray();
 133					}
 134				}
 135			}
 136			return result;
 137		}
 138	virtual bool isCurrentMediaUrl(U8 index, const std::string &url) const
 139		{
 140			LLTextureEntry *te = mObject->getTE(index); 
 141			if (te)
 142			{
 143				if (te->getMediaData())
 144				{
 145					return (te->getMediaData()->getCurrentURL() == url);
 146				}
 147			}
 148			return url.empty();
 149		}
 150
 151	virtual LLUUID getID() const
 152		{ return mObject->getID(); }
 153
 154	virtual void mediaNavigateBounceBack(U8 index)
 155		{ mObject->mediaNavigateBounceBack(index); }
 156	
 157	virtual bool hasMedia() const
 158		{ return mObject->hasMedia(); }
 159	
 160	virtual void updateObjectMediaData(LLSD const &data, const std::string &version_string) 
 161		{ mObject->updateObjectMediaData(data, version_string); }
 162	
 163	virtual F64 getMediaInterest() const 
 164		{ 
 165			F64 interest = mObject->getTotalMediaInterest();
 166			if (interest < (F64)0.0)
 167			{
 168				// media interest not valid yet, try pixel area
 169				interest = mObject->getPixelArea();
 170				// HACK: force recalculation of pixel area if interest is the "magic default" of 1024.
 171				if (interest == 1024.f)
 172				{
 173					const_cast<LLVOVolume*>(static_cast<LLVOVolume*>(mObject))->setPixelAreaAndAngle(gAgent);
 174					interest = mObject->getPixelArea();
 175				}
 176			}
 177			return interest; 
 178		}
 179	
 180	virtual bool isInterestingEnough() const
 181		{
 182			return LLViewerMedia::isInterestingEnough(mObject, getMediaInterest());
 183		}
 184
 185	virtual std::string getCapabilityUrl(const std::string &name) const
 186		{ return mObject->getRegion()->getCapability(name); }
 187	
 188	virtual bool isDead() const
 189		{ return mObject->isDead(); }
 190	
 191	virtual U32 getMediaVersion() const
 192		{ return LLTextureEntry::getVersionFromMediaVersionString(mObject->getMediaURL()); }
 193	
 194	virtual bool isNew() const
 195		{ return mNew; }
 196
 197private:
 198	LLPointer<LLVOVolume> mObject;
 199	bool mNew;
 200};
 201
 202
 203LLVOVolume::LLVOVolume(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp)
 204	: LLViewerObject(id, pcode, regionp),
 205	  mVolumeImpl(NULL)
 206{
 207	mTexAnimMode = 0;
 208	mRelativeXform.setIdentity();
 209	mRelativeXformInvTrans.setIdentity();
 210
 211	mFaceMappingChanged = FALSE;
 212	mLOD = MIN_LOD;
 213	mTextureAnimp = NULL;
 214	mVolumeChanged = FALSE;
 215	mVObjRadius = LLVector3(1,1,0.5f).length();
 216	mNumFaces = 0;
 217	mLODChanged = FALSE;
 218	mSculptChanged = FALSE;
 219	mSpotLightPriority = 0.f;
 220
 221	mMediaImplList.resize(getNumTEs());
 222	mLastFetchedMediaVersion = -1;
 223	mIndexInTex = 0;
 224	mMDCImplCount = 0;
 225}
 226
 227LLVOVolume::~LLVOVolume()
 228{
 229	delete mTextureAnimp;
 230	mTextureAnimp = NULL;
 231	delete mVolumeImpl;
 232	mVolumeImpl = NULL;
 233
 234	if(!mMediaImplList.empty())
 235	{
 236		for(U32 i = 0 ; i < mMediaImplList.size() ; i++)
 237		{
 238			if(mMediaImplList[i].notNull())
 239			{
 240				mMediaImplList[i]->removeObject(this) ;
 241			}
 242		}
 243	}
 244}
 245
 246void LLVOVolume::markDead()
 247{
 248	if (!mDead)
 249	{
 250		if(getMDCImplCount() > 0)
 251		{
 252			LLMediaDataClientObject::ptr_t obj = new LLMediaDataClientObjectImpl(const_cast<LLVOVolume*>(this), false);
 253			if (sObjectMediaClient) sObjectMediaClient->removeFromQueue(obj);
 254			if (sObjectMediaNavigateClient) sObjectMediaNavigateClient->removeFromQueue(obj);
 255		}
 256		
 257		// Detach all media impls from this object
 258		for(U32 i = 0 ; i < mMediaImplList.size() ; i++)
 259		{
 260			removeMediaImpl(i);
 261		}
 262
 263		if (mSculptTexture.notNull())
 264		{
 265			mSculptTexture->removeVolume(this);
 266		}
 267	}
 268	
 269	LLViewerObject::markDead();
 270}
 271
 272
 273// static
 274void LLVOVolume::initClass()
 275{
 276	// gSavedSettings better be around
 277	if (gSavedSettings.getBOOL("PrimMediaMasterEnabled"))
 278	{
 279		const F32 queue_timer_delay = gSavedSettings.getF32("PrimMediaRequestQueueDelay");
 280		const F32 retry_timer_delay = gSavedSettings.getF32("PrimMediaRetryTimerDelay");
 281		const U32 max_retries = gSavedSettings.getU32("PrimMediaMaxRetries");
 282		const U32 max_sorted_queue_size = gSavedSettings.getU32("PrimMediaMaxSortedQueueSize");
 283		const U32 max_round_robin_queue_size = gSavedSettings.getU32("PrimMediaMaxRoundRobinQueueSize");
 284		sObjectMediaClient = new LLObjectMediaDataClient(queue_timer_delay, retry_timer_delay, max_retries, 
 285														 max_sorted_queue_size, max_round_robin_queue_size);
 286		sObjectMediaNavigateClient = new LLObjectMediaNavigateClient(queue_timer_delay, retry_timer_delay, 
 287																	 max_retries, max_sorted_queue_size, max_round_robin_queue_size);
 288	}
 289}
 290
 291// static
 292void LLVOVolume::cleanupClass()
 293{
 294    sObjectMediaClient = NULL;
 295    sObjectMediaNavigateClient = NULL;
 296}
 297
 298U32 LLVOVolume::processUpdateMessage(LLMessageSystem *mesgsys,
 299										  void **user_data,
 300										  U32 block_num, EObjectUpdateType update_type,
 301										  LLDataPacker *dp)
 302{
 303	LLColor4U color;
 304	const S32 teDirtyBits = (TEM_CHANGE_TEXTURE|TEM_CHANGE_COLOR|TEM_CHANGE_MEDIA);
 305
 306	// Do base class updates...
 307	U32 retval = LLViewerObject::processUpdateMessage(mesgsys, user_data, block_num, update_type, dp);
 308
 309	LLUUID sculpt_id;
 310	U8 sculpt_type = 0;
 311	if (isSculpted())
 312	{
 313		LLSculptParams *sculpt_params = (LLSculptParams *)getParameterEntry(LLNetworkData::PARAMS_SCULPT);
 314		sculpt_id = sculpt_params->getSculptTexture();
 315		sculpt_type = sculpt_params->getSculptType();
 316	}
 317
 318	if (!dp)
 319	{
 320		if (update_type == OUT_FULL)
 321		{
 322			////////////////////////////////
 323			//
 324			// Unpack texture animation data
 325			//
 326			//
 327
 328			if (mesgsys->getSizeFast(_PREHASH_ObjectData, block_num, _PREHASH_TextureAnim))
 329			{
 330				if (!mTextureAnimp)
 331				{
 332					mTextureAnimp = new LLViewerTextureAnim();
 333				}
 334				else
 335				{
 336					if (!(mTextureAnimp->mMode & LLTextureAnim::SMOOTH))
 337					{
 338						mTextureAnimp->reset();
 339					}
 340				}
 341				mTexAnimMode = 0;
 342				mTextureAnimp->unpackTAMessage(mesgsys, block_num);
 343			}
 344			else
 345			{
 346				if (mTextureAnimp)
 347				{
 348					delete mTextureAnimp;
 349					mTextureAnimp = NULL;
 350					gPipeline.markTextured(mDrawable);
 351					mFaceMappingChanged = TRUE;
 352					mTexAnimMode = 0;
 353				}
 354			}
 355
 356			// Unpack volume data
 357			LLVolumeParams volume_params;
 358			LLVolumeMessage::unpackVolumeParams(&volume_params, mesgsys, _PREHASH_ObjectData, block_num);
 359			volume_params.setSculptID(sculpt_id, sculpt_type);
 360
 361			if (setVolume(volume_params, 0))
 362			{
 363				markForUpdate(TRUE);
 364			}
 365		}
 366
 367		// Sigh, this needs to be done AFTER the volume is set as well, otherwise bad stuff happens...
 368		////////////////////////////
 369		//
 370		// Unpack texture entry data
 371		//
 372
 373		S32 result = unpackTEMessage(mesgsys, _PREHASH_ObjectData, block_num);
 374		if (result & teDirtyBits)
 375		{
 376			updateTEData();
 377		}
 378		if (result & TEM_CHANGE_MEDIA)
 379		{
 380			retval |= MEDIA_FLAGS_CHANGED;
 381		}
 382	}
 383	else
 384	{
 385		// CORY TO DO: Figure out how to get the value here
 386		if (update_type != OUT_TERSE_IMPROVED)
 387		{
 388			LLVolumeParams volume_params;
 389			BOOL res = LLVolumeMessage::unpackVolumeParams(&volume_params, *dp);
 390			if (!res)
 391			{
 392				llwarns << "Bogus volume parameters in object " << getID() << llendl;
 393				llwarns << getRegion()->getOriginGlobal() << llendl;
 394			}
 395
 396			volume_params.setSculptID(sculpt_id, sculpt_type);
 397
 398			if (setVolume(volume_params, 0))
 399			{
 400				markForUpdate(TRUE);
 401			}
 402			S32 res2 = unpackTEMessage(*dp);
 403			if (TEM_INVALID == res2)
 404			{
 405				// There's something bogus in the data that we're unpacking.
 406				dp->dumpBufferToLog();
 407				llwarns << "Flushing cache files" << llendl;
 408
 409				if(LLVOCache::hasInstance() && getRegion())
 410				{
 411					LLVOCache::getInstance()->removeEntry(getRegion()->getHandle()) ;
 412				}
 413				
 414				llwarns << "Bogus TE data in " << getID() << llendl;
 415			}
 416			else 
 417			{
 418				if (res2 & teDirtyBits) 
 419				{
 420					updateTEData();
 421				}
 422				if (res2 & TEM_CHANGE_MEDIA)
 423				{
 424					retval |= MEDIA_FLAGS_CHANGED;
 425				}
 426			}
 427
 428			U32 value = dp->getPassFlags();
 429
 430			if (value & 0x40)
 431			{
 432				if (!mTextureAnimp)
 433				{
 434					mTextureAnimp = new LLViewerTextureAnim();
 435				}
 436				else
 437				{
 438					if (!(mTextureAnimp->mMode & LLTextureAnim::SMOOTH))
 439					{
 440						mTextureAnimp->reset();
 441					}
 442				}
 443				mTexAnimMode = 0;
 444				mTextureAnimp->unpackTAMessage(*dp);
 445			}
 446			else if (mTextureAnimp)
 447			{
 448				delete mTextureAnimp;
 449				mTextureAnimp = NULL;
 450				gPipeline.markTextured(mDrawable);
 451				mFaceMappingChanged = TRUE;
 452				mTexAnimMode = 0;
 453			}
 454		}
 455		else
 456		{
 457			S32 texture_length = mesgsys->getSizeFast(_PREHASH_ObjectData, block_num, _PREHASH_TextureEntry);
 458			if (texture_length)
 459			{
 460				U8							tdpbuffer[1024];
 461				LLDataPackerBinaryBuffer	tdp(tdpbuffer, 1024);
 462				mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_TextureEntry, tdpbuffer, 0, block_num);
 463				S32 result = unpackTEMessage(tdp);
 464				if (result & teDirtyBits)
 465				{
 466					updateTEData();
 467				}
 468				if (result & TEM_CHANGE_MEDIA)
 469				{
 470					retval |= MEDIA_FLAGS_CHANGED;
 471				}
 472			}
 473		}
 474	}
 475	if (retval & (MEDIA_URL_REMOVED | MEDIA_URL_ADDED | MEDIA_URL_UPDATED | MEDIA_FLAGS_CHANGED)) 
 476	{
 477		// If only the media URL changed, and it isn't a media version URL,
 478		// ignore it
 479		if ( ! ( retval & (MEDIA_URL_ADDED | MEDIA_URL_UPDATED) &&
 480				 mMedia && ! mMedia->mMediaURL.empty() &&
 481				 ! LLTextureEntry::isMediaVersionString(mMedia->mMediaURL) ) )
 482		{
 483			// If the media changed at all, request new media data
 484			LL_DEBUGS("MediaOnAPrim") << "Media update: " << getID() << ": retval=" << retval << " Media URL: " <<
 485                ((mMedia) ?  mMedia->mMediaURL : std::string("")) << LL_ENDL;
 486			requestMediaDataUpdate(retval & MEDIA_FLAGS_CHANGED);
 487		}
 488        else {
 489            LL_INFOS("MediaOnAPrim") << "Ignoring media update for: " << getID() << " Media URL: " <<
 490                ((mMedia) ?  mMedia->mMediaURL : std::string("")) << LL_ENDL;
 491        }
 492	}
 493	// ...and clean up any media impls
 494	cleanUpMediaImpls();
 495
 496	return retval;
 497}
 498
 499
 500void LLVOVolume::animateTextures()
 501{
 502	F32 off_s = 0.f, off_t = 0.f, scale_s = 1.f, scale_t = 1.f, rot = 0.f;
 503	S32 result = mTextureAnimp->animateTextures(off_s, off_t, scale_s, scale_t, rot);
 504	
 505	if (result)
 506	{
 507		if (!mTexAnimMode)
 508		{
 509			mFaceMappingChanged = TRUE;
 510			gPipeline.markTextured(mDrawable);
 511		}
 512		mTexAnimMode = result | mTextureAnimp->mMode;
 513				
 514		S32 start=0, end=mDrawable->getNumFaces()-1;
 515		if (mTextureAnimp->mFace >= 0 && mTextureAnimp->mFace <= end)
 516		{
 517			start = end = mTextureAnimp->mFace;
 518		}
 519		
 520		for (S32 i = start; i <= end; i++)
 521		{
 522			LLFace* facep = mDrawable->getFace(i);
 523			if(facep->getVirtualSize() <= MIN_TEX_ANIM_SIZE && facep->mTextureMatrix) continue;
 524
 525			const LLTextureEntry* te = facep->getTextureEntry();
 526			
 527			if (!te)
 528			{
 529				continue;
 530			}
 531		
 532			if (!(result & LLViewerTextureAnim::ROTATE))
 533			{
 534				te->getRotation(&rot);
 535			}
 536			if (!(result & LLViewerTextureAnim::TRANSLATE))
 537			{
 538				te->getOffset(&off_s,&off_t);
 539			}			
 540			if (!(result & LLViewerTextureAnim::SCALE))
 541			{
 542				te->getScale(&scale_s, &scale_t);
 543			}
 544
 545			if (!facep->mTextureMatrix)
 546			{
 547				facep->mTextureMatrix = new LLMatrix4();
 548			}
 549
 550			LLMatrix4& tex_mat = *facep->mTextureMatrix;
 551			tex_mat.setIdentity();
 552			LLVector3 trans ;
 553
 554			if(facep->isAtlasInUse())
 555			{
 556				//
 557				//if use atlas for animated texture
 558				//apply the following transform to the animation matrix.
 559				//
 560
 561				F32 tcoord_xoffset = 0.f ;
 562				F32 tcoord_yoffset = 0.f ;
 563				F32 tcoord_xscale = 1.f ;
 564				F32 tcoord_yscale = 1.f ;			
 565				if(facep->isAtlasInUse())
 566				{
 567					const LLVector2* tmp = facep->getTexCoordOffset() ;
 568					tcoord_xoffset = tmp->mV[0] ; 
 569					tcoord_yoffset = tmp->mV[1] ;
 570
 571					tmp = facep->getTexCoordScale() ;
 572					tcoord_xscale = tmp->mV[0] ; 
 573					tcoord_yscale = tmp->mV[1] ;	
 574				}
 575				trans.set(LLVector3(tcoord_xoffset + tcoord_xscale * (off_s+0.5f), tcoord_yoffset + tcoord_yscale * (off_t+0.5f), 0.f));
 576
 577				tex_mat.translate(LLVector3(-(tcoord_xoffset + tcoord_xscale * 0.5f), -(tcoord_yoffset + tcoord_yscale * 0.5f), 0.f));
 578			}
 579			else	//non atlas
 580			{
 581				trans.set(LLVector3(off_s+0.5f, off_t+0.5f, 0.f));			
 582				tex_mat.translate(LLVector3(-0.5f, -0.5f, 0.f));
 583			}
 584
 585			LLVector3 scale(scale_s, scale_t, 1.f);			
 586			LLQuaternion quat;
 587			quat.setQuat(rot, 0, 0, -1.f);
 588		
 589			tex_mat.rotate(quat);				
 590
 591			LLMatrix4 mat;
 592			mat.initAll(scale, LLQuaternion(), LLVector3());
 593			tex_mat *= mat;
 594		
 595			tex_mat.translate(trans);
 596		}
 597	}
 598	else
 599	{
 600		if (mTexAnimMode && mTextureAnimp->mRate == 0)
 601		{
 602			U8 start, count;
 603
 604			if (mTextureAnimp->mFace == -1)
 605			{
 606				start = 0;
 607				count = getNumTEs();
 608			}
 609			else
 610			{
 611				start = (U8) mTextureAnimp->mFace;
 612				count = 1;
 613			}
 614
 615			for (S32 i = start; i < start + count; i++)
 616			{
 617				if (mTexAnimMode & LLViewerTextureAnim::TRANSLATE)
 618				{
 619					setTEOffset(i, mTextureAnimp->mOffS, mTextureAnimp->mOffT);				
 620				}
 621				if (mTexAnimMode & LLViewerTextureAnim::SCALE)
 622				{
 623					setTEScale(i, mTextureAnimp->mScaleS, mTextureAnimp->mScaleT);	
 624				}
 625				if (mTexAnimMode & LLViewerTextureAnim::ROTATE)
 626				{
 627					setTERotation(i, mTextureAnimp->mRot);
 628				}
 629			}
 630
 631			gPipeline.markTextured(mDrawable);
 632			mFaceMappingChanged = TRUE;
 633			mTexAnimMode = 0;
 634		}
 635	}
 636}
 637BOOL LLVOVolume::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
 638{
 639	LLViewerObject::idleUpdate(agent, world, time);
 640
 641	static LLFastTimer::DeclareTimer ftm("Volume");
 642	LLFastTimer t(ftm);
 643
 644	if (mDead || mDrawable.isNull())
 645	{
 646		return TRUE;
 647	}
 648	
 649	///////////////////////
 650	//
 651	// Do texture animation stuff
 652	//
 653
 654	if (mTextureAnimp && gAnimateTextures)
 655	{
 656		animateTextures();
 657	}
 658
 659	// Dispatch to implementation
 660	if (mVolumeImpl)
 661	{
 662		mVolumeImpl->doIdleUpdate(agent, world, time);
 663	}
 664
 665	const S32 MAX_ACTIVE_OBJECT_QUIET_FRAMES = 40;
 666
 667	if (mDrawable->isActive())
 668	{
 669		if (mDrawable->isRoot() && 
 670			mDrawable->mQuietCount++ > MAX_ACTIVE_OBJECT_QUIET_FRAMES && 
 671			(!mDrawable->getParent() || !mDrawable->getParent()->isActive()))
 672		{
 673			mDrawable->makeStatic();
 674		}
 675	}
 676
 677	return TRUE;
 678}
 679
 680void LLVOVolume::updateTextures()
 681{
 682	const F32 TEXTURE_AREA_REFRESH_TIME = 5.f; // seconds
 683	if (mTextureUpdateTimer.getElapsedTimeF32() > TEXTURE_AREA_REFRESH_TIME)
 684	{
 685		updateTextureVirtualSize();		
 686	}
 687}
 688
 689BOOL LLVOVolume::isVisible() const 
 690{
 691	if(mDrawable.notNull() && mDrawable->isVisible())
 692	{
 693		return TRUE ;
 694	}
 695
 696	if(isAttachment())
 697	{
 698		LLViewerObject* objp = (LLViewerObject*)getParent() ;
 699		while(objp && !objp->isAvatar())
 700		{
 701			objp = (LLViewerObject*)objp->getParent() ;
 702		}
 703
 704		return objp && objp->mDrawable.notNull() && objp->mDrawable->isVisible() ;
 705	}
 706
 707	return FALSE ;
 708}
 709
 710void LLVOVolume::updateTextureVirtualSize(bool forced)
 711{
 712	LLFastTimer ftm(FTM_VOLUME_TEXTURES);
 713	// Update the pixel area of all faces
 714
 715	if(!forced)
 716	{
 717		if(!isVisible())
 718		{
 719			return ;
 720		}
 721
 722		if (!gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_SIMPLE))
 723		{
 724			return;
 725		}
 726	}
 727
 728	static LLCachedControl<bool> dont_load_textures(gSavedSettings,"TextureDisable");
 729		
 730	if (dont_load_textures || LLAppViewer::getTextureFetch()->mDebugPause) // || !mDrawable->isVisible())
 731	{
 732		return;
 733	}
 734
 735	mTextureUpdateTimer.reset();
 736	
 737	F32 old_area = mPixelArea;
 738	mPixelArea = 0.f;
 739
 740	const S32 num_faces = mDrawable->getNumFaces();
 741	F32 min_vsize=999999999.f, max_vsize=0.f;
 742	LLViewerCamera* camera = LLViewerCamera::getInstance();
 743	for (S32 i = 0; i < num_faces; i++)
 744	{
 745		LLFace* face = mDrawable->getFace(i);
 746		const LLTextureEntry *te = face->getTextureEntry();
 747		LLViewerTexture *imagep = face->getTexture();
 748		if (!imagep || !te ||			
 749			face->mExtents[0].equals3(face->mExtents[1]))
 750		{
 751			continue;
 752		}
 753		
 754		F32 vsize;
 755		F32 old_size = face->getVirtualSize();
 756
 757		if (isHUDAttachment())
 758		{
 759			F32 area = (F32) camera->getScreenPixelArea();
 760			vsize = area;
 761			imagep->setBoostLevel(LLViewerTexture::BOOST_HUD);
 762 			face->setPixelArea(area); // treat as full screen
 763			face->setVirtualSize(vsize);
 764		}
 765		else
 766		{
 767			vsize = face->getTextureVirtualSize();
 768		}
 769
 770		mPixelArea = llmax(mPixelArea, face->getPixelArea());		
 771
 772		if (face->mTextureMatrix != NULL)
 773		{
 774			if ((vsize < MIN_TEX_ANIM_SIZE && old_size > MIN_TEX_ANIM_SIZE) ||
 775				(vsize > MIN_TEX_ANIM_SIZE && old_size < MIN_TEX_ANIM_SIZE))
 776			{
 777				gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_TCOORD, FALSE);
 778			}
 779		}
 780				
 781		if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_AREA))
 782		{
 783			if (vsize < min_vsize) min_vsize = vsize;
 784			if (vsize > max_vsize) max_vsize = vsize;
 785		}
 786		else if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_PRIORITY))
 787		{
 788			LLViewerFetchedTexture* img = LLViewerTextureManager::staticCastToFetchedTexture(imagep) ;
 789			if(img)
 790			{
 791				F32 pri = img->getDecodePriority();
 792				pri = llmax(pri, 0.0f);
 793				if (pri < min_vsize) min_vsize = pri;
 794				if (pri > max_vsize) max_vsize = pri;
 795			}
 796		}
 797		else if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_FACE_AREA))
 798		{
 799			F32 pri = mPixelArea;
 800			if (pri < min_vsize) min_vsize = pri;
 801			if (pri > max_vsize) max_vsize = pri;
 802		}	
 803	}
 804	
 805	if (isSculpted())
 806	{
 807		LLSculptParams *sculpt_params = (LLSculptParams *)getParameterEntry(LLNetworkData::PARAMS_SCULPT);
 808		LLUUID id =  sculpt_params->getSculptTexture();
 809		
 810		updateSculptTexture();
 811		
 812		
 813
 814		if (mSculptTexture.notNull())
 815		{
 816			mSculptTexture->setBoostLevel(llmax((S32)mSculptTexture->getBoostLevel(),
 817												(S32)LLViewerTexture::BOOST_SCULPTED));
 818			mSculptTexture->setForSculpt() ;
 819			
 820			if(!mSculptTexture->isCachedRawImageReady())
 821			{
 822				S32 lod = llmin(mLOD, 3);
 823				F32 lodf = ((F32)(lod + 1.0f)/4.f);
 824				F32 tex_size = lodf * LLViewerTexture::sMaxSculptRez ;
 825				mSculptTexture->addTextureStats(2.f * tex_size * tex_size, FALSE);
 826			
 827				//if the sculpty very close to the view point, load first
 828				{				
 829					LLVector3 lookAt = getPositionAgent() - camera->getOrigin();
 830					F32 dist = lookAt.normVec() ;
 831					F32 cos_angle_to_view_dir = lookAt * camera->getXAxis() ;				
 832					mSculptTexture->setAdditionalDecodePriority(0.8f * LLFace::calcImportanceToCamera(cos_angle_to_view_dir, dist)) ;
 833				}
 834			}
 835	
 836			S32 texture_discard = mSculptTexture->getDiscardLevel(); //try to match the texture
 837			S32 current_discard = getVolume() ? getVolume()->getSculptLevel() : -2 ;
 838
 839			if (texture_discard >= 0 && //texture has some data available
 840				(texture_discard < current_discard || //texture has more data than last rebuild
 841				current_discard < 0)) //no previous rebuild
 842			{
 843				gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, FALSE);
 844				mSculptChanged = TRUE;
 845			}
 846
 847			if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_SCULPTED))
 848			{
 849				setDebugText(llformat("T%d C%d V%d\n%dx%d",
 850										  texture_discard, current_discard, getVolume()->getSculptLevel(),
 851										  mSculptTexture->getHeight(), mSculptTexture->getWidth()));
 852			}
 853		}
 854
 855	}
 856
 857	if (getLightTextureID().notNull())
 858	{
 859		LLLightImageParams* params = (LLLightImageParams*) getParameterEntry(LLNetworkData::PARAMS_LIGHT_IMAGE);
 860		LLUUID id = params->getLightTexture();
 861		mLightTexture = LLViewerTextureManager::getFetchedTexture(id);
 862		if (mLightTexture.notNull())
 863		{
 864			F32 rad = getLightRadius();
 865			mLightTexture->addTextureStats(gPipeline.calcPixelArea(getPositionAgent(), 
 866																	LLVector3(rad,rad,rad),
 867																	*camera));
 868		}	
 869	}
 870	
 871	if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_AREA))
 872	{
 873		setDebugText(llformat("%.0f:%.0f", (F32) sqrt(min_vsize),(F32) sqrt(max_vsize)));
 874	}
 875 	else if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_PRIORITY))
 876 	{
 877 		setDebugText(llformat("%.0f:%.0f", (F32) sqrt(min_vsize),(F32) sqrt(max_vsize)));
 878 	}
 879	else if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_FACE_AREA))
 880	{
 881		setDebugText(llformat("%.0f:%.0f", (F32) sqrt(min_vsize),(F32) sqrt(max_vsize)));
 882	}
 883
 884	if (mPixelArea == 0)
 885	{ //flexi phasing issues make this happen
 886		mPixelArea = old_area;
 887	}
 888}
 889
 890BOOL LLVOVolume::isActive() const
 891{
 892	return !mStatic || mTextureAnimp || (mVolumeImpl && mVolumeImpl->isActive()) || 
 893		(mDrawable.notNull() && mDrawable->isActive());
 894}
 895
 896BOOL LLVOVolume::setMaterial(const U8 material)
 897{
 898	BOOL res = LLViewerObject::setMaterial(material);
 899	
 900	return res;
 901}
 902
 903void LLVOVolume::setTexture(const S32 face)
 904{
 905	llassert(face < getNumTEs());
 906	gGL.getTexUnit(0)->bind(getTEImage(face));
 907}
 908
 909void LLVOVolume::setScale(const LLVector3 &scale, BOOL damped)
 910{
 911	if (scale != getScale())
 912	{
 913		// store local radius
 914		LLViewerObject::setScale(scale);
 915
 916		if (mVolumeImpl)
 917		{
 918			mVolumeImpl->onSetScale(scale, damped);
 919		}
 920		
 921		updateRadius();
 922
 923		//since drawable transforms do not include scale, changing volume scale
 924		//requires an immediate rebuild of volume verts.
 925		gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_POSITION, TRUE);
 926	}
 927}
 928
 929LLFace* LLVOVolume::addFace(S32 f)
 930{
 931	const LLTextureEntry* te = getTE(f);
 932	LLViewerTexture* imagep = getTEImage(f);
 933	return mDrawable->addFace(te, imagep);
 934}
 935
 936LLDrawable *LLVOVolume::createDrawable(LLPipeline *pipeline)
 937{
 938	pipeline->allocDrawable(this);
 939		
 940	mDrawable->setRenderType(LLPipeline::RENDER_TYPE_VOLUME);
 941
 942	S32 max_tes_to_set = getNumTEs();
 943	for (S32 i = 0; i < max_tes_to_set; i++)
 944	{
 945		addFace(i);
 946	}
 947	mNumFaces = max_tes_to_set;
 948
 949	if (isAttachment())
 950	{
 951		mDrawable->makeActive();
 952	}
 953
 954	if (getIsLight())
 955	{
 956		// Add it to the pipeline mLightSet
 957		gPipeline.setLight(mDrawable, TRUE);
 958	}
 959	
 960	updateRadius();
 961	bool force_update = true; // avoid non-alpha mDistance update being optimized away
 962	mDrawable->updateDistance(*LLViewerCamera::getInstance(), force_update);
 963
 964	return mDrawable;
 965}
 966
 967BOOL LLVOVolume::setVolume(const LLVolumeParams &params_in, const S32 detail, bool unique_volume)
 968{
 969	LLVolumeParams volume_params = params_in;
 970
 971	S32 last_lod = mVolumep.notNull() ? LLVolumeLODGroup::getVolumeDetailFromScale(mVolumep->getDetail()) : -1;
 972	S32 lod = mLOD;
 973
 974	BOOL is404 = FALSE;
 975	
 976	if (isSculpted())
 977	{
 978		// if it's a mesh
 979		if ((volume_params.getSculptType() & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_MESH)
 980		{ //meshes might not have all LODs, get the force detail to best existing LOD
 981			LLUUID mesh_id = volume_params.getSculptID();
 982
 983			lod = gMeshRepo.getActualMeshLOD(volume_params, lod);
 984			if (lod == -1)
 985			{
 986				is404 = TRUE;
 987				lod = 0;
 988			}
 989		}
 990	}
 991
 992	// Check if we need to change implementations
 993	bool is_flexible = (volume_params.getPathParams().getCurveType() == LL_PCODE_PATH_FLEXIBLE);
 994	if (is_flexible)
 995	{
 996		setParameterEntryInUse(LLNetworkData::PARAMS_FLEXIBLE, TRUE, false);
 997		if (!mVolumeImpl)
 998		{
 999			LLFlexibleObjectData* data = (LLFlexibleObjectData*)getParameterEntry(LLNetworkData::PARAMS_FLEXIBLE);
1000			mVolumeImpl = new LLVolumeImplFlexible(this, data);
1001		}
1002	}
1003	else
1004	{
1005		// Mark the parameter not in use
1006		setParameterEntryInUse(LLNetworkData::PARAMS_FLEXIBLE, FALSE, false);
1007		if (mVolumeImpl)
1008		{
1009			delete mVolumeImpl;
1010			mVolumeImpl = NULL;
1011			if (mDrawable.notNull())
1012			{
1013				// Undo the damage we did to this matrix
1014				mDrawable->updateXform(FALSE);
1015			}
1016		}
1017	}
1018	
1019	if (is404)
1020	{
1021		setIcon(LLViewerTextureManager::getFetchedTextureFromFile("icons/Inv_Mesh.png", TRUE, LLViewerTexture::BOOST_UI));
1022		//render prim proxy when mesh loading attempts give up
1023		volume_params.setSculptID(LLUUID::null, LL_SCULPT_TYPE_NONE);
1024
1025	}
1026
1027	if ((LLPrimitive::setVolume(volume_params, lod, (mVolumeImpl && mVolumeImpl->isVolumeUnique()))) || mSculptChanged)
1028	{
1029		mFaceMappingChanged = TRUE;
1030		
1031		if (mVolumeImpl)
1032		{
1033			mVolumeImpl->onSetVolume(volume_params, mLOD);
1034		}
1035	
1036		updateSculptTexture();
1037
1038		if (isSculpted())
1039		{
1040			updateSculptTexture();
1041			// if it's a mesh
1042			if ((volume_params.getSculptType() & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_MESH)
1043			{
1044				if (!getVolume()->isMeshAssetLoaded())
1045				{ 
1046					//load request not yet issued, request pipeline load this mesh
1047					LLUUID asset_id = volume_params.getSculptID();
1048					S32 available_lod = gMeshRepo.loadMesh(this, volume_params, lod, last_lod);
1049					if (available_lod != lod)
1050					{
1051						LLPrimitive::setVolume(volume_params, available_lod);
1052					}
1053				}
1054				
1055			}
1056			else // otherwise is sculptie
1057			{
1058				if (mSculptTexture.notNull())
1059				{
1060					sculpt();
1061				}
1062			}
1063		}
1064
1065		return TRUE;
1066	}
1067
1068	return FALSE;
1069}
1070
1071void LLVOVolume::updateSculptTexture()
1072{
1073	LLPointer<LLViewerFetchedTexture> old_sculpt = mSculptTexture;
1074
1075	if (isSculpted() && !isMesh())
1076	{
1077		LLSculptParams *sculpt_params = (LLSculptParams *)getParameterEntry(LLNetworkData::PARAMS_SCULPT);
1078		LLUUID id =  sculpt_params->getSculptTexture();
1079		if (id.notNull())
1080		{
1081			mSculptTexture = LLViewerTextureManager::getFetchedTexture(id, TRUE, LLViewerTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE);
1082		}
1083	}
1084	else
1085	{
1086		mSculptTexture = NULL;
1087	}
1088
1089	if (mSculptTexture != old_sculpt)
1090	{
1091		if (old_sculpt.notNull())
1092		{
1093			old_sculpt->removeVolume(this);
1094		}
1095		if (mSculptTexture.notNull())
1096		{
1097			mSculptTexture->addVolume(this);
1098		}
1099	}
1100	
1101}
1102
1103void LLVOVolume::notifyMeshLoaded()
1104{ 
1105	mSculptChanged = TRUE;
1106	gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_GEOMETRY, TRUE);
1107}
1108
1109// sculpt replaces generate() for sculpted surfaces
1110void LLVOVolume::sculpt()
1111{	
1112	if (mSculptTexture.notNull())
1113	{				
1114		U16 sculpt_height = 0;
1115		U16 sculpt_width = 0;
1116		S8 sculpt_components = 0;
1117		const U8* sculpt_data = NULL;
1118	
1119		S32 discard_level = mSculptTexture->getDiscardLevel() ;
1120		LLImageRaw* raw_image = mSculptTexture->getCachedRawImage() ;
1121		
1122		S32 max_discard = mSculptTexture->getMaxDiscardLevel();
1123		if (discard_level > max_discard)
1124			discard_level = max_discard;    // clamp to the best we can do
1125
1126		S32 current_discard = getVolume()->getSculptLevel() ;
1127		if(current_discard < -2)
1128		{
1129			llwarns << "WARNING!!: Current discard of sculpty at " << current_discard 
1130				<< " is less than -2." << llendl;
1131			
1132			// corrupted volume... don't update the sculpty
1133			return;
1134		}
1135		else if (current_discard > MAX_DISCARD_LEVEL)
1136		{
1137			llwarns << "WARNING!!: Current discard of sculpty at " << current_discard 
1138				<< " is more than than allowed max of " << MAX_DISCARD_LEVEL << llendl;
1139			
1140			// corrupted volume... don't update the sculpty			
1141			return;
1142		}
1143
1144		if (current_discard == discard_level)  // no work to do here
1145			return;
1146		
1147		if(!raw_image)
1148		{
1149			llassert(discard_level < 0) ;
1150
1151			sculpt_width = 0;
1152			sculpt_height = 0;
1153			sculpt_data = NULL ;
1154
1155			if(LLViewerTextureManager::sTesterp)
1156			{
1157				LLViewerTextureManager::sTesterp->updateGrayTextureBinding();
1158			}
1159		}
1160		else
1161		{					
1162			sculpt_height = raw_image->getHeight();
1163			sculpt_width = raw_image->getWidth();
1164			sculpt_components = raw_image->getComponents();		
1165					   
1166			sculpt_data = raw_image->getData();
1167
1168			if(LLViewerTextureManager::sTesterp)
1169			{
1170				mSculptTexture->updateBindStatsForTester() ;
1171			}
1172		}
1173		getVolume()->sculpt(sculpt_width, sculpt_height, sculpt_components, sculpt_data, discard_level);
1174
1175		//notify rebuild any other VOVolumes that reference this sculpty volume
1176		for (S32 i = 0; i < mSculptTexture->getNumVolumes(); ++i)
1177		{
1178			LLVOVolume* volume = (*(mSculptTexture->getVolumeList()))[i];
1179			if (volume != this && volume->getVolume() == getVolume())
1180			{
1181				gPipeline.markRebuild(volume->mDrawable, LLDrawable::REBUILD_GEOMETRY, FALSE);
1182			}
1183		}
1184	}
1185}
1186
1187S32	LLVOVolume::computeLODDetail(F32 distance, F32 radius)
1188{
1189	S32	cur_detail;
1190	if (LLPipeline::sDynamicLOD)
1191	{
1192		// We've got LOD in the profile, and in the twist.  Use radius.
1193		F32 tan_angle = (LLVOVolume::sLODFactor*radius)/distance;
1194		cur_detail = LLVolumeLODGroup::getDetailFromTan(llround(tan_angle, 0.01f));
1195	}
1196	else
1197	{
1198		cur_detail = llclamp((S32) (sqrtf(radius)*LLVOVolume::sLODFactor*4.f), 0, 3);		
1199	}
1200	return cur_detail;
1201}
1202
1203BOOL LLVOVolume::calcLOD()
1204{
1205	if (mDrawable.isNull())
1206	{
1207		return FALSE;
1208	}
1209
1210	S32 cur_detail = 0;
1211	
1212	F32 radius;
1213	F32 distance;
1214
1215	if (mDrawable->isState(LLDrawable::RIGGED))
1216	{
1217		LLVOAvatar* avatar = getAvatar(); 
1218		distance = avatar->mDrawable->mDistanceWRTCamera;
1219		radius = avatar->getBinRadius();
1220	}
1221	else
1222	{
1223		distance = mDrawable->mDistanceWRTCamera;
1224		radius = getVolume()->mLODScaleBias.scaledVec(getScale()).length();
1225	}
1226	
1227	//hold onto unmodified distance for debugging
1228	//F32 debug_distance = distance;
1229	
1230	distance *= sDistanceFactor;
1231
1232	F32 rampDist = LLVOVolume::sLODFactor * 2;
1233	
1234	if (distance < rampDist)
1235	{
1236		// Boost LOD when you're REALLY close
1237		distance *= 1.0f/rampDist;
1238		distance *= distance;
1239		distance *= rampDist;
1240	}
1241	
1242	// DON'T Compensate for field of view changing on FOV zoom.
1243	distance *= F_PI/3.f;
1244
1245	cur_detail = computeLODDetail(llround(distance, 0.01f), 
1246									llround(radius, 0.01f));
1247
1248
1249	if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_LOD_INFO))
1250	{
1251		//setDebugText(llformat("%.2f:%.2f, %d", debug_distance, radius, cur_detail));
1252
1253		setDebugText(llformat("%d", mDrawable->getFace(0)->getTextureIndex()));
1254	}
1255
1256	if (cur_detail != mLOD)
1257	{
1258		mAppAngle = llround((F32) atan2( mDrawable->getRadius(), mDrawable->mDistanceWRTCamera) * RAD_TO_DEG, 0.01f);
1259		mLOD = cur_detail;		
1260		return TRUE;
1261	}
1262	else
1263	{
1264		return FALSE;
1265	}
1266}
1267
1268BOOL LLVOVolume::updateLOD()
1269{
1270	if (mDrawable.isNull())
1271	{
1272		return FALSE;
1273	}
1274	
1275	BOOL lod_changed = calcLOD();
1276
1277	if (lod_changed)
1278	{
1279		gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, FALSE);
1280		mLODChanged = TRUE;
1281	}
1282	else
1283	{
1284		F32 new_radius = getBinRadius();
1285		F32 old_radius = mDrawable->getBinRadius();
1286		if (new_radius < old_radius * 0.9f || new_radius > old_radius*1.1f)
1287		{
1288			gPipeline.markPartitionMove(mDrawable);
1289		}
1290	}
1291
1292	lod_changed = lod_changed || LLViewerObject::updateLOD();
1293	
1294	return lod_changed;
1295}
1296
1297BOOL LLVOVolume::setDrawableParent(LLDrawable* parentp)
1298{
1299	if (!LLViewerObject::setDrawableParent(parentp))
1300	{
1301		// no change in drawable parent
1302		return FALSE;
1303	}
1304
1305	if (!mDrawable->isRoot())
1306	{
1307		// rebuild vertices in parent relative space
1308		gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);
1309
1310		if (mDrawable->isActive() && !parentp->isActive())
1311		{
1312			parentp->makeActive();
1313		}
1314		else if (mDrawable->isStatic() && parentp->isActive())
1315		{
1316			mDrawable->makeActive();
1317		}
1318	}
1319	
1320	return TRUE;
1321}
1322
1323void LLVOVolume::updateFaceFlags()
1324{
1325	for (S32 i = 0; i < getVolume()->getNumFaces(); i++)
1326	{
1327		LLFace *face = mDrawable->getFace(i);
1328		if (!face)
1329		{
1330			return;
1331		}
1332
1333		BOOL fullbright = getTE(i)->getFullbright();
1334		face->clearState(LLFace::FULLBRIGHT | LLFace::HUD_RENDER | LLFace::LIGHT);
1335
1336		if (fullbright || (mMaterial == LL_MCODE_LIGHT))
1337		{
1338			face->setState(LLFace::FULLBRIGHT);
1339		}
1340		if (mDrawable->isLight())
1341		{
1342			face->setState(LLFace::LIGHT);
1343		}
1344		if (isHUDAttachment())
1345		{
1346			face->setState(LLFace::HUD_RENDER);
1347		}
1348	}
1349}
1350
1351BOOL LLVOVolume::setParent(LLViewerObject* parent)
1352{
1353	BOOL ret = FALSE ;
1354	if (parent != getParent())
1355	{
1356		ret = LLViewerObject::setParent(parent);
1357		if (ret && mDrawable)
1358		{
1359			gPipeline.markMoved(mDrawable);
1360			gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);
1361		}
1362	}
1363
1364	return ret ;
1365}
1366
1367// NOTE: regenFaces() MUST be followed by genTriangles()!
1368void LLVOVolume::regenFaces()
1369{
1370	// remove existing faces
1371	BOOL count_changed = mNumFaces != getNumTEs();
1372	
1373	if (count_changed)
1374	{
1375		deleteFaces();		
1376		// add new faces
1377		mNumFaces = getNumTEs();
1378	}
1379		
1380	for (S32 i = 0; i < mNumFaces; i++)
1381	{
1382		LLFace* facep = count_changed ? addFace(i) : mDrawable->getFace(i);
1383		facep->setTEOffset(i);
1384		facep->setTexture(getTEImage(i));
1385		facep->setViewerObject(this);
1386		
1387		// If the face had media on it, this will have broken the link between the LLViewerMediaTexture and the face.
1388		// Re-establish the link.
1389		if((int)mMediaImplList.size() > i)
1390		{
1391			if(mMediaImplList[i])
1392			{
1393				LLViewerMediaTexture* media_tex = LLViewerTextureManager::findMediaTexture(mMediaImplList[i]->getMediaTextureID()) ;
1394				if(media_tex)
1395				{
1396					media_tex->addMediaToFace(facep) ;
1397				}
1398			}
1399		}
1400	}
1401	
1402	if (!count_changed)
1403	{
1404		updateFaceFlags();
1405	}
1406}
1407
1408BOOL LLVOVolume::genBBoxes(BOOL force_global)
1409{
1410	BOOL res = TRUE;
1411
1412	LLVector4a min,max;
1413
1414	min.clear();
1415	max.clear();
1416
1417	BOOL rebuild = mDrawable->isState(LLDrawable::REBUILD_VOLUME | LLDrawable::REBUILD_POSITION | LLDrawable::REBUILD_RIGGED);
1418
1419//	bool rigged = false;
1420	LLVolume* volume = mRiggedVolume;
1421	if (!volume)
1422	{
1423		volume = getVolume();
1424	}
1425
1426	for (S32 i = 0; i < getVolume()->getNumVolumeFaces(); i++)
1427	{
1428		LLFace *face = mDrawable->getFace(i);
1429		if (!face)
1430		{
1431			continue;
1432		}
1433		res &= face->genVolumeBBoxes(*volume, i,
1434										mRelativeXform, mRelativeXformInvTrans,
1435										(mVolumeImpl && mVolumeImpl->isVolumeGlobal()) || force_global);
1436		
1437		if (rebuild)
1438		{
1439			if (i == 0)
1440			{
1441				min = face->mExtents[0];
1442				max = face->mExtents[1];
1443			}
1444			else
1445			{
1446				min.setMin(min, face->mExtents[0]);
1447				max.setMax(max, face->mExtents[1]);
1448			}
1449		}
1450	}
1451	
1452	if (rebuild)
1453	{
1454		mDrawable->setSpatialExtents(min,max);
1455		min.add(max);
1456		min.mul(0.5f);
1457		mDrawable->setPositionGroup(min);	
1458	}
1459
1460	updateRadius();
1461	mDrawable->movePartition();
1462			
1463	return res;
1464}
1465
1466void LLVOVolume::preRebuild()
1467{
1468	if (mVolumeImpl != NULL)
1469	{
1470		mVolumeImpl->preRebuild();
1471	}
1472}
1473
1474void LLVOVolume::updateRelativeXform()
1475{
1476	if (mVolumeImpl)
1477	{
1478		mVolumeImpl->updateRelativeXform();
1479		return;
1480	}
1481	
1482	LLDrawable* drawable = mDrawable;
1483	
1484	if (drawable->isState(LLDrawable::RIGGED) && mRiggedVolume.notNull())
1485	{ //rigged volume (which is in agent space) is used for generating bounding boxes etc
1486	  //inverse of render matrix should go to partition space
1487		mRelativeXform = getRenderMatrix();
1488
1489		F32* dst = (F32*) mRelativeXformInvTrans.mMatrix;
1490		F32* src = (F32*) mRelativeXform.mMatrix;
1491		dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2];
1492		dst[3] = src[4]; dst[4] = src[5]; dst[5] = src[6];
1493		dst[6] = src[8]; dst[7] = src[9]; dst[8] = src[10];
1494		
1495		mRelativeXform.invert();
1496		mRelativeXformInvTrans.transpose();
1497	}
1498	else if (drawable->isActive())
1499	{				
1500		// setup relative transforms
1501		LLQuaternion delta_rot;
1502		LLVector3 delta_pos, delta_scale;
1503		
1504		//matrix from local space to parent relative/global space
1505		delta_rot = drawable->isSpatialRoot() ? LLQuaternion() : mDrawable->getRotation();
1506		delta_pos = drawable->isSpatialRoot() ? LLVector3(0,0,0) : mDrawable->getPosition();
1507		delta_scale = mDrawable->getScale();
1508
1509		// Vertex transform (4x4)
1510		LLVector3 x_axis = LLVector3(delta_scale.mV[VX], 0.f, 0.f) * delta_rot;
1511		LLVector3 y_axis = LLVector3(0.f, delta_scale.mV[VY], 0.f) * delta_rot;
1512		LLVector3 z_axis = LLVector3(0.f, 0.f, delta_scale.mV[VZ]) * delta_rot;
1513
1514		mRelativeXform.initRows(LLVector4(x_axis, 0.f),
1515								LLVector4(y_axis, 0.f),
1516								LLVector4(z_axis, 0.f),
1517								LLVector4(delta_pos, 1.f));
1518
1519		
1520		// compute inverse transpose for normals
1521		// mRelativeXformInvTrans.setRows(x_axis, y_axis, z_axis);
1522		// mRelativeXformInvTrans.invert(); 
1523		// mRelativeXformInvTrans.setRows(x_axis, y_axis, z_axis);
1524		// grumble - invert is NOT a matrix invert, so we do it by hand:
1525
1526		LLMatrix3 rot_inverse = LLMatrix3(~delta_rot);
1527
1528		LLMatrix3 scale_inverse;
1529		scale_inverse.setRows(LLVector3(1.0, 0.0, 0.0) / delta_scale.mV[VX],
1530							  LLVector3(0.0, 1.0, 0.0) / delta_scale.mV[VY],
1531							  LLVector3(0.0, 0.0, 1.0) / delta_scale.mV[VZ]);
1532							   
1533		
1534		mRelativeXformInvTrans = rot_inverse * scale_inverse;
1535
1536		mRelativeXformInvTrans.transpose();
1537	}
1538	else
1539	{
1540		LLVector3 pos = getPosition();
1541		LLVector3 scale = getScale();
1542		LLQuaternion rot = getRotation();
1543	
1544		if (mParent)
1545		{
1546			pos *= mParent->getRotation();
1547			pos += mParent->getPosition();
1548			rot *= mParent->getRotation();
1549		}
1550		
1551		//LLViewerRegion* region = getRegion();
1552		//pos += region->getOriginAgent();
1553		
1554		LLVector3 x_axis = LLVector3(scale.mV[VX], 0.f, 0.f) * rot;
1555		LLVector3 y_axis = LLVector3(0.f, scale.mV[VY], 0.f) * rot;
1556		LLVector3 z_axis = LLVector3(0.f, 0.f, scale.mV[VZ]) * rot;
1557
1558		mRelativeXform.initRows(LLVector4(x_axis, 0.f),
1559								LLVector4(y_axis, 0.f),
1560								LLVector4(z_axis, 0.f),
1561								LLVector4(pos, 1.f));
1562
1563		// compute inverse transpose for normals
1564		LLMatrix3 rot_inverse = LLMatrix3(~rot);
1565
1566		LLMatrix3 scale_inverse;
1567		scale_inverse.setRows(LLVector3(1.0, 0.0, 0.0) / scale.mV[VX],
1568							  LLVector3(0.0, 1.0, 0.0) / scale.mV[VY],
1569							  LLVector3(0.0, 0.0, 1.0) / scale.mV[VZ]);
1570							   
1571		
1572		mRelativeXformInvTrans = rot_inverse * scale_inverse;
1573
1574		mRelativeXformInvTrans.transpose();
1575	}
1576}
1577
1578static LLFastTimer::DeclareTimer FTM_GEN_FLEX("Generate Flexies");
1579static LLFastTimer::DeclareTimer FTM_UPDATE_PRIMITIVES("Update Primitives");
1580static LLFastTimer::DeclareTimer FTM_UPDATE_RIGGED_VOLUME("Update Rigged");
1581
1582BOOL LLVOVolume::updateGeometry(LLDrawable *drawable)
1583{
1584	LLFastTimer t(FTM_UPDATE_PRIMITIVES);
1585	
1586	if (mDrawable->isState(LLDrawable::REBUILD_RIGGED))
1587	{
1588		{
1589			LLFastTimer t(FTM_UPDATE_RIGGED_VOLUME);
1590			updateRiggedVolume();
1591		}
1592		genBBoxes(FALSE);
1593		mDrawable->clearState(LLDrawable::REBUILD_RIGGED);
1594	}
1595
1596	if (mVolumeImpl != NULL)
1597	{
1598		BOOL res;
1599		{
1600			LLFastTimer t(FTM_GEN_FLEX);
1601			res = mVolumeImpl->doUpdateGeometry(drawable);
1602		}
1603		updateFaceFlags();
1604		return res;
1605	}
1606	
1607	dirtySpatialGroup(drawable->isState(LLDrawable::IN_REBUILD_Q1));
1608
1609	BOOL compiled = FALSE;
1610			
1611	updateRelativeXform();
1612	
1613	if (mDrawable.isNull()) // Not sure why this is happening, but it is...
1614	{
1615		return TRUE; // No update to complete
1616	}
1617
1618	if (mVolumeChanged || mFaceMappingChanged )
1619	{
1620		compiled = TRUE;
1621
1622		if (mVolumeChanged)
1623		{
1624			LLFastTimer ftm(FTM_GEN_VOLUME);
1625			LLVolumeParams volume_params = getVolume()->getParams();
1626			setVolume(volume_params, 0);
1627			drawable->setState(LLDrawable::REBUILD_VOLUME);
1628		}
1629
1630		{
1631			LLFastTimer t(FTM_GEN_TRIANGLES);
1632			regenFaces();
1633			genBBoxes(FALSE);
1634		}
1635	}
1636	else if ((mLODChanged) || (mSculptChanged))
1637	{
1638		LLVolume *old_volumep, *new_volumep;
1639		F32 old_lod, new_lod;
1640		S32 old_num_faces, new_num_faces ;
1641
1642		old_volumep = getVolume();
1643		old_lod = old_volumep->getDetail();
1644		old_num_faces = old_volumep->getNumFaces() ;
1645		old_volumep = NULL ;
1646
1647		{
1648			LLFastTimer ftm(FTM_GEN_VOLUME);
1649			LLVolumeParams volume_params = getVolume()->getParams();
1650			setVolume(volume_params, 0);
1651		}
1652
1653		new_volumep = getVolume();
1654		new_lod = new_volumep->getDetail();
1655		new_num_faces = new_volumep->getNumFaces() ;
1656		new_volumep = NULL ;
1657
1658		if ((new_lod != old_lod) || mSculptChanged)
1659		{
1660			compiled = TRUE;
1661			sNumLODChanges += new_num_faces ;
1662	
1663			if((S32)getNumTEs() != getVolume()->getNumFaces())
1664			{
1665				setNumTEs(getVolume()->getNumFaces()); //mesh loading may change number of faces.
1666			}
1667
1668			drawable->setState(LLDrawable::REBUILD_VOLUME); // for face->genVolumeTriangles()
1669
1670			{
1671				LLFastTimer t(FTM_GEN_TRIANGLES);
1672				if (new_num_faces != old_num_faces || mNumFaces != (S32)getNumTEs())
1673				{
1674					regenFaces();
1675				}
1676				genBBoxes(FALSE);
1677
1678				if (mSculptChanged)
1679				{ //changes in sculpt maps can thrash an object bounding box without 
1680				  //triggering a spatial group bounding box update -- force spatial group
1681				  //to update bounding boxes
1682					LLSpatialGroup* group = mDrawable->getSpatialGroup();
1683					if (group)
1684					{
1685						group->unbound();
1686					}
1687				}
1688			}
1689		}
1690	}
1691	// it has its own drawable (it's moved) or it has changed UVs or it has changed xforms from global<->local
1692	else
1693	{
1694		compiled = TRUE;
1695		// All it did was move or we changed the texture coordinate offset
1696		LLFastTimer t(FTM_GEN_TRIANGLES);
1697		genBBoxes(FALSE);
1698	}
1699
1700	// Update face flags
1701	updateFaceFlags();
1702	
1703	if(compiled)
1704	{
1705		LLPipeline::sCompiles++;
1706	}
1707		
1708	mVolumeChanged = FALSE;
1709	mLODChanged = FALSE;
1710	mSculptChanged = FALSE;
1711	mFaceMappingChanged = FALSE;
1712	
1713	return LLViewerObject::updateGeometry(drawable);
1714}
1715
1716void LLVOVolume::updateFaceSize(S32 idx)
1717{
1718	LLFace* facep = mDrawable->getFace(idx);
1719	if (idx >= getVolume()->getNumVolumeFaces())
1720	{
1721		facep->setSize(0,0, true);
1722	}
1723	else
1724	{
1725		const LLVolumeFace& vol_face = getVolume()->getVolumeFace(idx);
1726		facep->setSize(vol_face.mNumVertices, vol_face.mNumIndices, 
1727						true); // <--- volume faces should be padded for 16-byte alignment
1728		
1729	}
1730}
1731
1732BOOL LLVOVolume::isRootEdit() const
1733{
1734	if (mParent && !((LLViewerObject*)mParent)->isAvatar())
1735	{
1736		return FALSE;
1737	}
1738	return TRUE;
1739}
1740
1741//virtual
1742void LLVOVolume::setNumTEs(const U8 num_tes)
1743{
1744	const U8 old_num_tes = getNumTEs() ;
1745	
1746	if(old_num_tes && old_num_tes < num_tes) //new faces added
1747	{
1748		LLViewerObject::setNumTEs(num_tes) ;
1749
1750		if(mMediaImplList.size() >= old_num_tes && mMediaImplList[old_num_tes -1].notNull())//duplicate the last media textures if exists.
1751		{
1752			mMediaImplList.resize(num_tes) ;
1753			const LLTextureEntry* te = getTE(old_num_tes - 1) ;
1754			for(U8 i = old_num_tes; i < num_tes ; i++)
1755			{
1756				setTE(i, *te) ;
1757				mMediaImplList[i] = mMediaImplList[old_num_tes -1] ;
1758			}
1759			mMediaImplList[old_num_tes -1]->setUpdated(TRUE) ;
1760		}
1761	}
1762	else if(old_num_tes > num_tes && mMediaImplList.size() > num_tes) //old faces removed
1763	{
1764		U8 end = mMediaImplList.size() ;
1765		for(U8 i = num_tes; i < end ; i++)
1766		{
1767			removeMediaImpl(i) ;				
1768		}
1769		mMediaImplList.resize(num_tes) ;
1770
1771		LLViewerObject::setNumTEs(num_tes) ;
1772	}
1773	else
1774	{
1775		LLViewerObject::setNumTEs(num_tes) ;
1776	}
1777
1778	return ;
1779}
1780
1781void LLVOVolume::setTEImage(const U8 te, LLViewerTexture *imagep)
1782{
1783	BOOL changed = (mTEImages[te] != imagep);
1784	LLViewerObject::setTEImage(te, imagep);
1785	if (changed)
1786	{
1787		gPipeline.markTextured(mDrawable);
1788		mFaceMappingChanged = TRUE;
1789	}
1790}
1791
1792S32 LLVOVolume::setTETexture(const U8 te, const LLUUID &uuid)
1793{
1794	S32 res = LLViewerObject::setTETexture(te, uuid);
1795	if (res)
1796	{
1797		gPipeline.markTextured(mDrawable);
1798		mFaceMappingChanged = TRUE;
1799	}
1800	return res;
1801}
1802
1803S32 LLVOVolume::setTEColor(const U8 te, const LLColor3& color)
1804{
1805	return setTEColor(te, LLColor4(color));
1806}
1807
1808S32 LLVOVolume::setTEColor(const U8 te, const LLColor4& color)
1809{
1810	S32 retval = 0;
1811	const LLTextureEntry *tep = getTE(te);
1812	if (!tep)
1813	{
1814		llwarns << "No texture entry for te " << (S32)te << ", object " << mID << llendl;
1815	}
1816	else if (color != tep->getColor())
1817	{
1818		if (color.mV[3] != tep->getColor().mV[3])
1819		{
1820			gPipeline.markTextured(mDrawable);
1821		}
1822		retval = LLPrimitive::setTEColor(te, color);
1823		if (mDrawable.notNull() && retval)
1824		{
1825			// These should only happen on updates which are not the initial update.
1826			mDrawable->setState(LLDrawable::REBUILD_COLOR);
1827			dirtyMesh();
1828		}
1829	}
1830
1831	return  retval;
1832}
1833
1834S32 LLVOVolume::setTEBumpmap(const U8 te, const U8 bumpmap)
1835{
1836	S32 res = LLViewerObject::setTEBumpmap(te, bumpmap);
1837	if (res)
1838	{
1839		gPipeline.markTextured(mDrawable);
1840		mFaceMappingChanged = TRUE;
1841	}
1842	return  res;
1843}
1844
1845S32 LLVOVolume::setTETexGen(const U8 te, const U8 texgen)
1846{
1847	S32 res = LLViewerObject::setTETexGen(te, texgen);
1848	if (res)
1849	{
1850		gPipeline.markTextured(mDrawable);
1851		mFaceMappingChanged = TRUE;
1852	}
1853	return  res;
1854}
1855
1856S32 LLVOVolume::setTEMediaTexGen(const U8 te, const U8 media)
1857{
1858	S32 res = LLViewerObject::setTEMediaTexGen(te, media);
1859	if (res)
1860	{
1861		gPipeline.markTextured(mDrawable);
1862		mFaceMappingChanged = TRUE;
1863	}
1864	return  res;
1865}
1866
1867S32 LLVOVolume::setTEShiny(const U8 te, const U8 shiny)
1868{
1869	S32 res = LLViewerObject::setTEShiny(te, shiny);
1870	if (res)
1871	{
1872		gPipeline.markTextured(mDrawable);
1873		mFaceMappingChanged = TRUE;
1874	}
1875	return  res;
1876}
1877
1878S32 LLVOVolume::setTEFullbright(const U8 te, const U8 fullbright)
1879{
1880	S32 res = LLViewerObject::setTEFullbright(te, fullbright);
1881	if (res)
1882	{
1883		gPipeline.markTextured(mDrawable);
1884		mFaceMappingChanged = TRUE;
1885	}
1886	return  res;
1887}
1888
1889S32 LLVOVolume::setTEBumpShinyFullbright(const U8 te, const U8 bump)
1890{
1891	S32 res = LLViewerObject::setTEBumpShinyFullbright(te, bump);
1892	if (res)
1893	{
1894		gPipeline.markTextured(mDrawable);
1895		mFaceMappingChanged = TRUE;
1896	}
1897	return res;
1898}
1899
1900S32 LLVOVolume::setTEMediaFlags(const U8 te, const U8 media_flags)
1901{
1902	S32 res = LLViewerObject::setTEMediaFlags(te, media_flags);
1903	if (res)
1904	{
1905		gPipeline.markTextured(mDrawable);
1906		mFaceMappingChanged = TRUE;
1907	}
1908	return  res;
1909}
1910
1911S32 LLVOVolume::setTEGlow(const U8 te, const F32 glow)
1912{
1913	S32 res = LLViewerObject::setTEGlow(te, glow);
1914	if (res)
1915	{
1916		gPipeline.markTextured(mDrawable);
1917		mFaceMappingChanged = TRUE;
1918	}
1919	return  res;
1920}
1921
1922S32 LLVOVolume::setTEScale(const U8 te, const F32 s, const F32 t)
1923{
1924	S32 res = LLViewerObject::setTEScale(te, s, t);
1925	if (res)
1926	{
1927		gPipeline.markTextured(mDrawable);
1928		mFaceMappingChanged = TRUE;
1929	}
1930	return res;
1931}
1932
1933S32 LLVOVolume::setTEScaleS(const U8 te, const F32 s)
1934{
1935	S32 res = LLViewerObject::setTEScaleS(te, s);
1936	if (res)
1937	{
1938		gPipeline.markTextured(mDrawable);
1939		mFaceMappingChanged = TRUE;
1940	}
1941	return res;
1942}
1943
1944S32 LLVOVolume::setTEScaleT(const U8 te, const F32 t)
1945{
1946	S32 res = LLViewerObject::setTEScaleT(te, t);
1947	if (res)
1948	{
1949		gPipeline.markTextured(mDrawable);
1950		mFaceMappingChanged = TRUE;
1951	}
1952	return res;
1953}
1954
1955void LLVOVolume::updateTEData()
1956{
1957	/*if (mDrawable.notNull())
1958	{
1959		mFaceMappingChanged = TRUE;
1960		gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_MATERIAL, TRUE);
1961	}*/
1962}
1963
1964bool LLVOVolume::hasMedia() const
1965{
1966	bool result = false;
1967	const U8 numTEs = getNumTEs();
1968	for (U8 i = 0; i < numTEs; i++)
1969	{
1970		const LLTextureEntry* te = getTE(i);
1971		if(te->hasMedia())
1972		{
1973			result = true;
1974			break;
1975		}
1976	}
1977	return result;
1978}
1979
1980LLVector3 LLVOVolume::getApproximateFaceNormal(U8 face_id)
1981{
1982	LLVolume* volume = getVolume();
1983	LLVector4a result;
1984	result.clear();
1985
1986	LLVector3 ret;
1987
1988	if (volume && face_id < volume->getNumVolumeFaces())
1989	{
1990		const LLVolumeFace& face = volume->getVolumeFace(face_id);
1991		for (S32 i = 0; i < (S32)face.mNumVertices; ++i)
1992		{
1993			result.add(face.mNormals[i]);
1994		}
1995
1996		LLVector3 ret(result.getF32ptr());
1997		ret = volumeDirectionToAgent(ret);
1998		ret.normVec();
1999	}
2000	
2001	return ret;
2002}
2003
2004void LLVOVolume::requestMediaDataUpdate(bool isNew)
2005{
2006    if (sObjectMediaClient)
2007		sObjectMediaClient->fetchMedia(new LLMediaDataClientObjectImpl(this, isNew));
2008}
2009
2010bool LLVOVolume::isMediaDataBeingFetched() const
2011{
2012	// I know what I'm doing by const_casting this away: this is just 
2013	// a wrapper class that is only going to do a lookup.
2014	return (sObjectMediaClient) ? sObjectMediaClient->isInQueue(new LLMediaDataClientObjectImpl(const_cast<LLVOVolume*>(this), false)) : false;
2015}
2016
2017void LLVOVolume::cleanUpMediaImpls()
2018{
2019	// Iterate through our TEs and remove any Impls that are no longer used
2020	const U8 numTEs = getNumTEs();
2021	for (U8 i = 0; i < numTEs; i++)
2022	{
2023		const LLTextureEntry* te = getTE(i);
2024		if( ! te->hasMedia())
2025		{
2026			// Delete the media IMPL!
2027			removeMediaImpl(i) ;
2028		}
2029	}
2030}
2031
2032void LLVOVolume::updateObjectMediaData(const LLSD &media_data_array, const std::string &media_version)
2033{
2034	// media_data_array is an array of media entry maps
2035	// media_version is the version string in the response.
2036	U32 fetched_versi

Large files files are truncated, but you can click here to view the full file