PageRenderTime 128ms CodeModel.GetById 46ms app.highlight 75ms RepoModel.GetById 1ms app.codeStats 1ms

/indra/newview/llviewerpartsim.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 861 lines | 657 code | 126 blank | 78 comment | 113 complexity | 2bced8a9d42519346dc8d83dc1935d5a MD5 | raw file
  1/** 
  2 * @file llviewerpartsim.cpp
  3 * @brief LLViewerPart class implementation
  4 *
  5 * $LicenseInfo:firstyear=2003&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#include "llviewerprecompiledheaders.h"
 28
 29#include "llviewerpartsim.h"
 30
 31#include "llviewercontrol.h"
 32
 33#include "llagent.h"
 34#include "llviewercamera.h"
 35#include "llviewerobjectlist.h"
 36#include "llviewerpartsource.h"
 37#include "llviewerregion.h"
 38#include "llvopartgroup.h"
 39#include "llworld.h"
 40#include "pipeline.h"
 41#include "llspatialpartition.h"
 42#include "llvovolume.h"
 43
 44const F32 PART_SIM_BOX_SIDE = 16.f;
 45const F32 PART_SIM_BOX_OFFSET = 0.5f*PART_SIM_BOX_SIDE;
 46const F32 PART_SIM_BOX_RAD = 0.5f*F_SQRT3*PART_SIM_BOX_SIDE;
 47
 48//static
 49S32 LLViewerPartSim::sMaxParticleCount = 0;
 50S32 LLViewerPartSim::sParticleCount = 0;
 51S32 LLViewerPartSim::sParticleCount2 = 0;
 52// This controls how greedy individual particle burst sources are allowed to be, and adapts according to how near the particle-count limit we are.
 53F32 LLViewerPartSim::sParticleAdaptiveRate = 0.0625f;
 54F32 LLViewerPartSim::sParticleBurstRate = 0.5f;
 55
 56//static
 57const S32 LLViewerPartSim::MAX_PART_COUNT = 8192;
 58const F32 LLViewerPartSim::PART_THROTTLE_THRESHOLD = 0.9f;
 59const F32 LLViewerPartSim::PART_ADAPT_RATE_MULT = 2.0f;
 60
 61//static
 62const F32 LLViewerPartSim::PART_THROTTLE_RESCALE = PART_THROTTLE_THRESHOLD / (1.0f-PART_THROTTLE_THRESHOLD);
 63const F32 LLViewerPartSim::PART_ADAPT_RATE_MULT_RECIP = 1.0f/PART_ADAPT_RATE_MULT;
 64
 65
 66U32 LLViewerPart::sNextPartID = 1;
 67
 68F32 calc_desired_size(LLViewerCamera* camera, LLVector3 pos, LLVector2 scale)
 69{
 70	F32 desired_size = (pos - camera->getOrigin()).magVec();
 71	desired_size /= 4;
 72	return llclamp(desired_size, scale.magVec()*0.5f, PART_SIM_BOX_SIDE*2);
 73}
 74
 75LLViewerPart::LLViewerPart() :
 76	mPartID(0),
 77	mLastUpdateTime(0.f),
 78	mSkipOffset(0.f),
 79	mVPCallback(NULL),
 80	mImagep(NULL)
 81{
 82	LLMemType mt(LLMemType::MTYPE_PARTICLES);
 83	mPartSourcep = NULL;
 84
 85	++LLViewerPartSim::sParticleCount2 ;
 86}
 87
 88LLViewerPart::~LLViewerPart()
 89{
 90	LLMemType mt(LLMemType::MTYPE_PARTICLES);
 91	mPartSourcep = NULL;
 92
 93	--LLViewerPartSim::sParticleCount2 ;
 94}
 95
 96void LLViewerPart::init(LLPointer<LLViewerPartSource> sourcep, LLViewerTexture *imagep, LLVPCallback cb)
 97{
 98	LLMemType mt(LLMemType::MTYPE_PARTICLES);
 99	mPartID = LLViewerPart::sNextPartID;
100	LLViewerPart::sNextPartID++;
101	mFlags = 0x00f;
102	mLastUpdateTime = 0.f;
103	mMaxAge = 10.f;
104	mSkipOffset = 0.0f;
105
106	mVPCallback = cb;
107	mPartSourcep = sourcep;
108
109	mImagep = imagep;
110}
111
112
113/////////////////////////////
114//
115// LLViewerPartGroup implementation
116//
117//
118
119
120LLViewerPartGroup::LLViewerPartGroup(const LLVector3 &center_agent, const F32 box_side, bool hud)
121 : mHud(hud)
122{
123	LLMemType mt(LLMemType::MTYPE_PARTICLES);
124	mVOPartGroupp = NULL;
125	mUniformParticles = TRUE;
126
127	mRegionp = LLWorld::getInstance()->getRegionFromPosAgent(center_agent);
128	llassert_always(center_agent.isFinite());
129	
130	if (!mRegionp)
131	{
132		//llwarns << "No region at position, using agent region!" << llendl;
133		mRegionp = gAgent.getRegion();
134	}
135	mCenterAgent = center_agent;
136	mBoxRadius = F_SQRT3*box_side*0.5f;
137
138	if (mHud)
139	{
140		mVOPartGroupp = (LLVOPartGroup *)gObjectList.createObjectViewer(LLViewerObject::LL_VO_HUD_PART_GROUP, getRegion());
141	}
142	else
143	{
144	mVOPartGroupp = (LLVOPartGroup *)gObjectList.createObjectViewer(LLViewerObject::LL_VO_PART_GROUP, getRegion());
145	}
146	mVOPartGroupp->setViewerPartGroup(this);
147	mVOPartGroupp->setPositionAgent(getCenterAgent());
148	F32 scale = box_side * 0.5f;
149	mVOPartGroupp->setScale(LLVector3(scale,scale,scale));
150	
151	//gPipeline.addObject(mVOPartGroupp);
152	gPipeline.createObject(mVOPartGroupp);
153
154	LLSpatialGroup* group = mVOPartGroupp->mDrawable->getSpatialGroup();
155
156	if (group != NULL)
157	{
158		LLVector3 center(group->mOctreeNode->getCenter().getF32ptr());
159		LLVector3 size(group->mOctreeNode->getSize().getF32ptr());
160		size += LLVector3(0.01f, 0.01f, 0.01f);
161		mMinObjPos = center - size;
162		mMaxObjPos = center + size;
163	}
164	else 
165	{
166		// Not sure what else to set the obj bounds to when the drawable has no spatial group.
167		LLVector3 extents(mBoxRadius, mBoxRadius, mBoxRadius);
168		mMinObjPos = center_agent - extents;
169		mMaxObjPos = center_agent + extents;
170	}
171
172	mSkippedTime = 0.f;
173
174	static U32 id_seed = 0;
175	mID = ++id_seed;
176}
177
178LLViewerPartGroup::~LLViewerPartGroup()
179{
180	LLMemType mt(LLMemType::MTYPE_PARTICLES);
181	cleanup();
182	
183	S32 count = (S32) mParticles.size();
184	for(S32 i = 0 ; i < count ; i++)
185	{
186		delete mParticles[i] ;
187	}
188	mParticles.clear();
189	
190	LLViewerPartSim::decPartCount(count);
191}
192
193void LLViewerPartGroup::cleanup()
194{
195	LLMemType mt(LLMemType::MTYPE_PARTICLES);
196	if (mVOPartGroupp)
197	{
198		if (!mVOPartGroupp->isDead())
199		{
200			gObjectList.killObject(mVOPartGroupp);
201		}
202		mVOPartGroupp = NULL;
203	}
204}
205
206BOOL LLViewerPartGroup::posInGroup(const LLVector3 &pos, const F32 desired_size)
207{
208	LLMemType mt(LLMemType::MTYPE_PARTICLES);
209	if ((pos.mV[VX] < mMinObjPos.mV[VX])
210		|| (pos.mV[VY] < mMinObjPos.mV[VY])
211		|| (pos.mV[VZ] < mMinObjPos.mV[VZ]))
212	{
213		return FALSE;
214	}
215
216	if ((pos.mV[VX] > mMaxObjPos.mV[VX])
217		|| (pos.mV[VY] > mMaxObjPos.mV[VY])
218		|| (pos.mV[VZ] > mMaxObjPos.mV[VZ]))
219	{
220		return FALSE;
221	}
222
223	if (desired_size > 0 && 
224		(desired_size < mBoxRadius*0.5f ||
225		desired_size > mBoxRadius*2.f))
226	{
227		return FALSE;
228	}
229
230	return TRUE;
231}
232
233
234BOOL LLViewerPartGroup::addPart(LLViewerPart* part, F32 desired_size)
235{
236	LLMemType mt(LLMemType::MTYPE_PARTICLES);
237
238	if (part->mFlags & LLPartData::LL_PART_HUD && !mHud)
239	{
240		return FALSE;
241	}
242
243	BOOL uniform_part = part->mScale.mV[0] == part->mScale.mV[1] && 
244					!(part->mFlags & LLPartData::LL_PART_FOLLOW_VELOCITY_MASK);
245
246	if (!posInGroup(part->mPosAgent, desired_size) ||
247		(mUniformParticles && !uniform_part) ||
248		(!mUniformParticles && uniform_part))
249	{
250		return FALSE;
251	}
252
253	gPipeline.markRebuild(mVOPartGroupp->mDrawable, LLDrawable::REBUILD_ALL, TRUE);
254	
255	mParticles.push_back(part);
256	part->mSkipOffset=mSkippedTime;
257	LLViewerPartSim::incPartCount(1);
258	return TRUE;
259}
260
261
262void LLViewerPartGroup::updateParticles(const F32 lastdt)
263{
264	LLMemType mt(LLMemType::MTYPE_PARTICLES);
265	F32 dt;
266	
267	LLVector3 gravity(0.f, 0.f, GRAVITY);
268
269	LLViewerPartSim::checkParticleCount(mParticles.size());
270
271	LLViewerCamera* camera = LLViewerCamera::getInstance();
272	LLViewerRegion *regionp = getRegion();
273	S32 end = (S32) mParticles.size();
274	for (S32 i = 0 ; i < (S32)mParticles.size();)
275	{
276		LLVector3 a(0.f, 0.f, 0.f);
277		LLViewerPart* part = mParticles[i] ;
278
279		dt = lastdt + mSkippedTime - part->mSkipOffset;
280		part->mSkipOffset = 0.f;
281
282		// Update current time
283		const F32 cur_time = part->mLastUpdateTime + dt;
284		const F32 frac = cur_time / part->mMaxAge;
285
286		// "Drift" the object based on the source object
287		if (part->mFlags & LLPartData::LL_PART_FOLLOW_SRC_MASK)
288		{
289			part->mPosAgent = part->mPartSourcep->mPosAgent;
290			part->mPosAgent += part->mPosOffset;
291		}
292
293		// Do a custom callback if we have one...
294		if (part->mVPCallback)
295		{
296			(*part->mVPCallback)(*part, dt);
297		}
298
299		if (part->mFlags & LLPartData::LL_PART_WIND_MASK)
300		{
301			LLVector3 tempVel(part->mVelocity);
302			part->mVelocity *= 1.f - 0.1f*dt;
303			part->mVelocity += 0.1f*dt*regionp->mWind.getVelocity(regionp->getPosRegionFromAgent(part->mPosAgent));
304		}
305
306		// Now do interpolation towards a target
307		if (part->mFlags & LLPartData::LL_PART_TARGET_POS_MASK)
308		{
309			F32 remaining = part->mMaxAge - part->mLastUpdateTime;
310			F32 step = dt / remaining;
311
312			step = llclamp(step, 0.f, 0.1f);
313			step *= 5.f;
314			// we want a velocity that will result in reaching the target in the 
315			// Interpolate towards the target.
316			LLVector3 delta_pos = part->mPartSourcep->mTargetPosAgent - part->mPosAgent;
317
318			delta_pos /= remaining;
319
320			part->mVelocity *= (1.f - step);
321			part->mVelocity += step*delta_pos;
322		}
323
324
325		if (part->mFlags & LLPartData::LL_PART_TARGET_LINEAR_MASK)
326		{
327			LLVector3 delta_pos = part->mPartSourcep->mTargetPosAgent - part->mPartSourcep->mPosAgent;			
328			part->mPosAgent = part->mPartSourcep->mPosAgent;
329			part->mPosAgent += frac*delta_pos;
330			part->mVelocity = delta_pos;
331		}
332		else
333		{
334			// Do velocity interpolation
335			part->mPosAgent += dt*part->mVelocity;
336			part->mPosAgent += 0.5f*dt*dt*part->mAccel;
337			part->mVelocity += part->mAccel*dt;
338		}
339
340		// Do a bounce test
341		if (part->mFlags & LLPartData::LL_PART_BOUNCE_MASK)
342		{
343			// Need to do point vs. plane check...
344			// For now, just check relative to object height...
345			F32 dz = part->mPosAgent.mV[VZ] - part->mPartSourcep->mPosAgent.mV[VZ];
346			if (dz < 0)
347			{
348				part->mPosAgent.mV[VZ] += -2.f*dz;
349				part->mVelocity.mV[VZ] *= -0.75f;
350			}
351		}
352
353
354		// Reset the offset from the source position
355		if (part->mFlags & LLPartData::LL_PART_FOLLOW_SRC_MASK)
356		{
357			part->mPosOffset = part->mPosAgent;
358			part->mPosOffset -= part->mPartSourcep->mPosAgent;
359		}
360
361		// Do color interpolation
362		if (part->mFlags & LLPartData::LL_PART_INTERP_COLOR_MASK)
363		{
364			part->mColor.setVec(part->mStartColor);
365			// note: LLColor4's v%k means multiply-alpha-only,
366			//       LLColor4's v*k means multiply-rgb-only
367			part->mColor *= 1.f - frac; // rgb*k
368			part->mColor %= 1.f - frac; // alpha*k
369			part->mColor += frac%(frac*part->mEndColor); // rgb,alpha
370		}
371
372		// Do scale interpolation
373		if (part->mFlags & LLPartData::LL_PART_INTERP_SCALE_MASK)
374		{
375			part->mScale.setVec(part->mStartScale);
376			part->mScale *= 1.f - frac;
377			part->mScale += frac*part->mEndScale;
378		}
379
380		// Set the last update time to now.
381		part->mLastUpdateTime = cur_time;
382
383
384		// Kill dead particles (either flagged dead, or too old)
385		if ((part->mLastUpdateTime > part->mMaxAge) || (LLViewerPart::LL_PART_DEAD_MASK == part->mFlags))
386		{
387			mParticles[i] = mParticles.back() ;
388			mParticles.pop_back() ;
389			delete part ;
390		}
391		else 
392		{
393			F32 desired_size = calc_desired_size(camera, part->mPosAgent, part->mScale);
394			if (!posInGroup(part->mPosAgent, desired_size))
395			{
396				// Transfer particles between groups
397				LLViewerPartSim::getInstance()->put(part) ;
398				mParticles[i] = mParticles.back() ;
399				mParticles.pop_back() ;
400			}
401			else
402			{
403				i++ ;
404			}
405		}
406	}
407
408	S32 removed = end - (S32)mParticles.size();
409	if (removed > 0)
410	{
411		// we removed one or more particles, so flag this group for update
412		if (mVOPartGroupp.notNull())
413		{
414			gPipeline.markRebuild(mVOPartGroupp->mDrawable, LLDrawable::REBUILD_ALL, TRUE);
415		}
416		LLViewerPartSim::decPartCount(removed);
417	}
418	
419	// Kill the viewer object if this particle group is empty
420	if (mParticles.empty())
421	{
422		gObjectList.killObject(mVOPartGroupp);
423		mVOPartGroupp = NULL;
424	}
425
426	LLViewerPartSim::checkParticleCount() ;
427}
428
429
430void LLViewerPartGroup::shift(const LLVector3 &offset)
431{
432	LLMemType mt(LLMemType::MTYPE_PARTICLES);
433	mCenterAgent += offset;
434	mMinObjPos += offset;
435	mMaxObjPos += offset;
436
437	for (S32 i = 0 ; i < (S32)mParticles.size(); i++)
438	{
439		mParticles[i]->mPosAgent += offset;
440	}
441}
442
443void LLViewerPartGroup::removeParticlesByID(const U32 source_id)
444{
445	LLMemType mt(LLMemType::MTYPE_PARTICLES);
446
447	for (S32 i = 0; i < (S32)mParticles.size(); i++)
448	{
449		if(mParticles[i]->mPartSourcep->getID() == source_id)
450		{
451			mParticles[i]->mFlags = LLViewerPart::LL_PART_DEAD_MASK;
452		}		
453	}
454}
455
456//////////////////////////////////
457//
458// LLViewerPartSim implementation
459//
460//
461
462//static
463void LLViewerPartSim::checkParticleCount(U32 size)
464{
465	if(LLViewerPartSim::sParticleCount2 != LLViewerPartSim::sParticleCount)
466	{
467		llerrs << "sParticleCount: " << LLViewerPartSim::sParticleCount << " ; sParticleCount2: " << LLViewerPartSim::sParticleCount2 << llendl ;
468	}
469
470	if(size > (U32)LLViewerPartSim::sParticleCount2)
471	{
472		llerrs << "curren particle size: " << LLViewerPartSim::sParticleCount2 << " array size: " << size << llendl ;
473	}
474}
475
476LLViewerPartSim::LLViewerPartSim()
477{
478	LLMemType mt(LLMemType::MTYPE_PARTICLES);
479	sMaxParticleCount = gSavedSettings.getS32("RenderMaxPartCount");
480	static U32 id_seed = 0;
481	mID = ++id_seed;
482}
483
484
485void LLViewerPartSim::destroyClass()
486{
487	LLMemType mt(LLMemType::MTYPE_PARTICLES);
488	S32 i;
489	S32 count;
490
491	// Kill all of the groups (and particles)
492	count = (S32) mViewerPartGroups.size();
493	for (i = 0; i < count; i++)
494	{
495		delete mViewerPartGroups[i];
496	}
497	mViewerPartGroups.clear();
498
499	// Kill all of the sources 
500	mViewerPartSources.clear();
501}
502
503BOOL LLViewerPartSim::shouldAddPart()
504{
505	LLMemType mt(LLMemType::MTYPE_PARTICLES);
506	if (sParticleCount > PART_THROTTLE_THRESHOLD*sMaxParticleCount)
507	{
508
509		F32 frac = (F32)sParticleCount/(F32)sMaxParticleCount;
510		frac -= PART_THROTTLE_THRESHOLD;
511		frac *= PART_THROTTLE_RESCALE;
512		if (ll_frand() < frac)
513		{
514			// Skip...
515			return FALSE;
516		}
517	}
518	if (sParticleCount >= MAX_PART_COUNT)
519	{
520		return FALSE;
521	}
522
523	return TRUE;
524}
525
526void LLViewerPartSim::addPart(LLViewerPart* part)
527{
528	LLMemType mt(LLMemType::MTYPE_PARTICLES);
529	if (sParticleCount < MAX_PART_COUNT)
530	{
531		put(part);
532	}
533	else
534	{
535		//delete the particle if can not add it in
536		delete part ;
537		part = NULL ;
538	}
539}
540
541
542LLViewerPartGroup *LLViewerPartSim::put(LLViewerPart* part)
543{
544	LLMemType mt(LLMemType::MTYPE_PARTICLES);
545	const F32 MAX_MAG = 1000000.f*1000000.f; // 1 million
546	LLViewerPartGroup *return_group = NULL ;
547	if (part->mPosAgent.magVecSquared() > MAX_MAG || !part->mPosAgent.isFinite())
548	{
549#if 0 && !LL_RELEASE_FOR_DOWNLOAD
550		llwarns << "LLViewerPartSim::put Part out of range!" << llendl;
551		llwarns << part->mPosAgent << llendl;
552#endif
553	}
554	else
555	{	
556		LLViewerCamera* camera = LLViewerCamera::getInstance();
557		F32 desired_size = calc_desired_size(camera, part->mPosAgent, part->mScale);
558
559		S32 count = (S32) mViewerPartGroups.size();
560		for (S32 i = 0; i < count; i++)
561		{
562			if (mViewerPartGroups[i]->addPart(part, desired_size))
563			{
564				// We found a spatial group that we fit into, add us and exit
565				return_group = mViewerPartGroups[i];
566				break ;
567			}
568		}
569
570		// Hmm, we didn't fit in any of the existing spatial groups
571		// Create a new one...
572		if(!return_group)
573		{
574			llassert_always(part->mPosAgent.isFinite());
575			LLViewerPartGroup *groupp = createViewerPartGroup(part->mPosAgent, desired_size, part->mFlags & LLPartData::LL_PART_HUD);
576			groupp->mUniformParticles = (part->mScale.mV[0] == part->mScale.mV[1] && 
577									!(part->mFlags & LLPartData::LL_PART_FOLLOW_VELOCITY_MASK));
578			if (!groupp->addPart(part))
579			{
580				llwarns << "LLViewerPartSim::put - Particle didn't go into its box!" << llendl;
581				llinfos << groupp->getCenterAgent() << llendl;
582				llinfos << part->mPosAgent << llendl;
583				mViewerPartGroups.pop_back() ;
584				delete groupp;
585				groupp = NULL ;
586			}
587			return_group = groupp;
588		}
589	}
590
591	if(!return_group) //failed to insert the particle
592	{
593		delete part ;
594		part = NULL ;
595	}
596
597	return return_group ;
598}
599
600LLViewerPartGroup *LLViewerPartSim::createViewerPartGroup(const LLVector3 &pos_agent, const F32 desired_size, bool hud)
601{
602	LLMemType mt(LLMemType::MTYPE_PARTICLES);
603	//find a box that has a center position divisible by PART_SIM_BOX_SIDE that encompasses
604	//pos_agent
605	LLViewerPartGroup *groupp = new LLViewerPartGroup(pos_agent, desired_size, hud);
606	mViewerPartGroups.push_back(groupp);
607	return groupp;
608}
609
610
611void LLViewerPartSim::shift(const LLVector3 &offset)
612{
613	S32 i;
614	S32 count;
615
616	count = (S32) mViewerPartSources.size();
617	for (i = 0; i < count; i++)
618	{
619		mViewerPartSources[i]->mPosAgent += offset;
620		mViewerPartSources[i]->mTargetPosAgent += offset;
621		mViewerPartSources[i]->mLastUpdatePosAgent += offset;
622	}
623
624	count = (S32) mViewerPartGroups.size();
625	for (i = 0; i < count; i++)
626	{
627		mViewerPartGroups[i]->shift(offset);
628	}
629}
630
631static LLFastTimer::DeclareTimer FTM_SIMULATE_PARTICLES("Simulate Particles");
632
633void LLViewerPartSim::updateSimulation()
634{
635	LLMemType mt(LLMemType::MTYPE_PARTICLES);
636	
637	static LLFrameTimer update_timer;
638
639	const F32 dt = llmin(update_timer.getElapsedTimeAndResetF32(), 0.1f);
640
641 	if (!(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_PARTICLES)))
642	{
643		return;
644	}
645
646	LLFastTimer ftm(FTM_SIMULATE_PARTICLES);
647
648	// Start at a random particle system so the same
649	// particle system doesn't always get first pick at the
650	// particles.  Theoretically we'd want to do this in distance
651	// order or something, but sorting particle sources will be a big
652	// pain.
653	S32 i;
654	S32 count = (S32) mViewerPartSources.size();
655	S32 start = (S32)ll_frand((F32)count);
656	S32 dir = 1;
657	S32 deldir = 0;
658	if (ll_frand() > 0.5f)
659	{
660		dir = -1;
661		deldir = -1;
662	}
663
664	S32 num_updates = 0;
665	for (i = start; num_updates < count;)
666	{
667		if (i >= count)
668		{
669			i = 0;
670		}
671		if (i < 0)
672		{
673			i = count - 1;
674		}
675
676		if (!mViewerPartSources[i]->isDead())
677		{
678			BOOL upd = TRUE;
679			if (!LLPipeline::sRenderAttachedParticles)
680			{
681				LLViewerObject* vobj = mViewerPartSources[i]->mSourceObjectp;
682				if (vobj && (vobj->getPCode() == LL_PCODE_VOLUME))
683				{
684					LLVOVolume* vvo = (LLVOVolume *)vobj;
685					if (vvo && vvo->isAttachment())
686					{
687						upd = FALSE;
688					}
689				}
690			}
691
692			if (upd) 
693			{
694				mViewerPartSources[i]->update(dt);
695			}
696		}
697
698		if (mViewerPartSources[i]->isDead())
699		{
700			mViewerPartSources.erase(mViewerPartSources.begin() + i);
701			count--;
702			i+=deldir;
703		}
704		else
705        {
706			 i += dir;
707        }
708		num_updates++;
709	}
710
711	count = (S32) mViewerPartGroups.size();
712	for (i = 0; i < count; i++)
713	{
714		LLViewerObject* vobj = mViewerPartGroups[i]->mVOPartGroupp;
715
716		S32 visirate = 1;
717		if (vobj)
718		{
719			LLSpatialGroup* group = vobj->mDrawable->getSpatialGroup();
720			if (group && !group->isVisible()) // && !group->isState(LLSpatialGroup::OBJECT_DIRTY))
721			{
722				visirate = 8;
723			}
724		}
725
726		if ((LLDrawable::getCurrentFrame()+mViewerPartGroups[i]->mID)%visirate == 0)
727		{
728			if (vobj)
729			{
730				gPipeline.markRebuild(vobj->mDrawable, LLDrawable::REBUILD_ALL, TRUE);
731			}
732			mViewerPartGroups[i]->updateParticles(dt * visirate);
733			mViewerPartGroups[i]->mSkippedTime=0.0f;
734			if (!mViewerPartGroups[i]->getCount())
735			{
736				delete mViewerPartGroups[i];
737				mViewerPartGroups.erase(mViewerPartGroups.begin() + i);
738				i--;
739				count--;
740			}
741		}
742		else
743		{	
744			mViewerPartGroups[i]->mSkippedTime+=dt;
745		}
746
747	}
748	if (LLDrawable::getCurrentFrame()%16==0)
749	{
750		if (sParticleCount > sMaxParticleCount * 0.875f
751		    && sParticleAdaptiveRate < 2.0f)
752		{
753			sParticleAdaptiveRate *= PART_ADAPT_RATE_MULT;
754		}
755		else
756		{
757			if (sParticleCount < sMaxParticleCount * 0.5f
758			    && sParticleAdaptiveRate > 0.03125f)
759			{
760				sParticleAdaptiveRate *= PART_ADAPT_RATE_MULT_RECIP;
761			}
762		}
763	}
764
765	updatePartBurstRate() ;
766
767	//llinfos << "Particles: " << sParticleCount << " Adaptive Rate: " << sParticleAdaptiveRate << llendl;
768}
769
770void LLViewerPartSim::updatePartBurstRate()
771{
772	if (!(LLDrawable::getCurrentFrame() & 0xf))
773	{
774		if (sParticleCount >= MAX_PART_COUNT) //set rate to zero
775		{
776			sParticleBurstRate = 0.0f ;
777		}
778		else if(sParticleCount > 0)
779		{
780			if(sParticleBurstRate > 0.0000001f)
781			{				
782				F32 total_particles = sParticleCount / sParticleBurstRate ; //estimated
783				F32 new_rate = llclamp(0.9f * sMaxParticleCount / total_particles, 0.0f, 1.0f) ;
784				F32 delta_rate_threshold = llmin(0.1f * llmax(new_rate, sParticleBurstRate), 0.1f) ;
785				F32 delta_rate = llclamp(new_rate - sParticleBurstRate, -1.0f * delta_rate_threshold, delta_rate_threshold) ;
786
787				sParticleBurstRate = llclamp(sParticleBurstRate + 0.5f * delta_rate, 0.0f, 1.0f) ;
788			}
789			else
790			{
791				sParticleBurstRate += 0.0000001f ;
792			}
793		}
794		else
795		{
796			sParticleBurstRate += 0.00125f ;
797		}
798	}
799}
800
801void LLViewerPartSim::addPartSource(LLPointer<LLViewerPartSource> sourcep)
802{
803	LLMemType mt(LLMemType::MTYPE_PARTICLES);
804	if (!sourcep)
805	{
806		llwarns << "Null part source!" << llendl;
807		return;
808	}
809	sourcep->setStart() ;
810	mViewerPartSources.push_back(sourcep);
811}
812
813void LLViewerPartSim::removeLastCreatedSource()
814{
815	mViewerPartSources.pop_back();
816}
817
818void LLViewerPartSim::cleanupRegion(LLViewerRegion *regionp)
819{
820	LLMemType mt(LLMemType::MTYPE_PARTICLES);
821	for (group_list_t::iterator i = mViewerPartGroups.begin(); i != mViewerPartGroups.end(); )
822	{
823		group_list_t::iterator iter = i++;
824
825		if ((*iter)->getRegion() == regionp)
826		{
827			delete *iter;
828			i = mViewerPartGroups.erase(iter);			
829		}
830	}
831}
832
833void LLViewerPartSim::clearParticlesByID(const U32 system_id)
834{
835	LLMemType mt(LLMemType::MTYPE_PARTICLES);
836	for (group_list_t::iterator g = mViewerPartGroups.begin(); g != mViewerPartGroups.end(); ++g)
837	{
838		(*g)->removeParticlesByID(system_id);
839	}
840	for (source_list_t::iterator i = mViewerPartSources.begin(); i != mViewerPartSources.end(); ++i)
841	{
842		if ((*i)->getID() == system_id)
843		{
844			(*i)->setDead();	
845			break;
846		}
847	}
848	
849}
850
851void LLViewerPartSim::clearParticlesByOwnerID(const LLUUID& task_id)
852{
853	LLMemType mt(LLMemType::MTYPE_PARTICLES);
854	for (source_list_t::iterator iter = mViewerPartSources.begin(); iter != mViewerPartSources.end(); ++iter)
855	{
856		if ((*iter)->getOwnerUUID() == task_id)
857		{
858			clearParticlesByID((*iter)->getID());
859		}
860	}
861}