12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070 |
- /**
- * @file llviewerpartsim.cpp
- * @brief LLViewerPart class implementation
- *
- * $LicenseInfo:firstyear=2003&license=viewergpl$
- *
- * Copyright (c) 2003-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
- #include "llviewerprecompiledheaders.h"
- #include <utility>
- #include "llviewerpartsim.h"
- #include "llfasttimer.h"
- #include "llagent.h"
- #include "llappviewer.h" // gFPSClamped
- #include "llpipeline.h"
- //MK
- #include "mkrlinterface.h"
- //mk
- #include "llspatialpartition.h"
- #include "llviewercamera.h"
- #include "llviewercontrol.h"
- #include "llviewerobjectlist.h"
- #include "llviewerpartsource.h"
- #include "llviewerregion.h"
- #include "llvoavatar.h"
- #include "llvopartgroup.h"
- #include "llvovolume.h"
- #include "llworld.h"
- constexpr F32 PART_SIM_BOX_SIDE = 16.f;
- // Global
- LLViewerPartSim gViewerPartSim;
- //static
- U32 LLViewerPart::sNextPartID = 1;
- S32 LLViewerPartSim::sMaxParticleCount = 0;
- S32 LLViewerPartSim::sParticleCount = 0;
- #if LL_DEBUG
- S32 LLViewerPartSim::sParticleCount2 = 0;
- #endif
- // This controls how greedy individual particle burst sources are allowed to
- // be, and adapts according to how near the particle-count limit we are.
- F32 LLViewerPartSim::sParticleAdaptiveRate = 0.0625f;
- F32 LLViewerPartSim::sParticleBurstRate = 0.5f;
- F32 calc_desired_size(LLVector3 pos, LLVector2 scale)
- {
- F32 desired_size = (pos - gViewerCamera.getOrigin()).length() * 0.25f;
- return llclamp(desired_size, scale.length() * 0.5f,
- PART_SIM_BOX_SIDE * 2.f);
- }
- ///////////////////////////////////////////////////////////////////////////////
- // LLViewerPart class
- ///////////////////////////////////////////////////////////////////////////////
- LLViewerPart::LLViewerPart()
- : mPartID(0),
- mLastUpdateTime(0.f),
- mSkipOffset(0.f),
- mVPCallback(NULL),
- mParent(NULL),
- mChild(NULL)
- {
- #if LL_DEBUG
- ++LLViewerPartSim::sParticleCount2;
- #endif
- }
- LLViewerPart::~LLViewerPart()
- {
- if (mPartSourcep.notNull() && mPartSourcep->mLastPart == this)
- {
- mPartSourcep->mLastPart = NULL;
- }
- // Patch up holes in the ribbon
- if (mParent)
- {
- llassert(mParent->mChild == this);
- mParent->mChild = mChild;
- }
- if (mChild)
- {
- llassert (mChild->mParent == this);
- mChild->mParent = mParent;
- }
- mPartSourcep = NULL;
- #if LL_DEBUG
- --LLViewerPartSim::sParticleCount2;
- #endif
- }
- void LLViewerPart::init(LLPointer<LLViewerPartSource> sourcep,
- LLViewerTexture* imagep, LLVPCallback cb)
- {
- mPartID = sNextPartID++;
- mFlags = 0x00f;
- mLastUpdateTime = mSkipOffset = 0.f;
- mMaxAge = 10.f;
- mVPCallback = cb;
- mPartSourcep = std::move(sourcep);
- mImagep = imagep;
- if (mImagep.notNull())
- {
- mImagep->setBoostLevel(LLGLTexture::BOOST_SUPER_HIGH);
- // Do not allow to discard the texture: fast changing particle systems
- // often see their cycling textures de-rez if we do. *TODO: only apply
- // to fast changing particle systems ? HB
- LLViewerFetchedTexture* texp = mImagep->asFetched();
- if (texp)
- {
- texp->setMinDiscardLevel(1);
- }
- mImagep->dontDiscard();
- #if !LL_IMPLICIT_SETNODELETE
- // Also set NO_DELETE since the changing textures might otherwise get
- // removed from memory. HB
- mImagep->setNoDelete();
- #endif
- }
- }
- ///////////////////////////////////////////////////////////////////////////////
- // LLViewerPartGroup class
- ///////////////////////////////////////////////////////////////////////////////
- LLViewerPartGroup::LLViewerPartGroup(const LLVector3& center_agent,
- F32 box_side, bool hud)
- : mVOPartGroupp(NULL),
- mHud(hud),
- mUniformParticles(true),
- mCenterAgent(center_agent),
- mBoxSide(box_side),
- mBoxRadius(F_SQRT3 * 0.5f * box_side),
- mSkippedTime(0.f)
- {
- static U32 id_seed = 0;
- mID = ++id_seed;
- llassert_always(center_agent.isFinite());
- mRegionp = gWorld.getRegionFromPosAgent(center_agent);
- if (!mRegionp)
- {
- LL_DEBUGS("Particles") << "No region at position, using agent region"
- << LL_ENDL;
- mRegionp = gAgent.getRegion();
- }
- if (mHud)
- {
- mVOPartGroupp =
- (LLVOPartGroup*)gObjectList.createObjectViewer(LLViewerObject::LL_VO_HUD_PART_GROUP,
- getRegion());
- }
- else
- {
- mVOPartGroupp =
- (LLVOPartGroup*)gObjectList.createObjectViewer(LLViewerObject::LL_VO_PART_GROUP,
- getRegion());
- }
- mVOPartGroupp->setViewerPartGroup(this);
- mVOPartGroupp->setPositionAgent(getCenterAgent());
- F32 scale = box_side * 0.5f;
- mVOPartGroupp->setScale(LLVector3(scale, scale, scale));
- gPipeline.createObject(mVOPartGroupp);
- LLSpatialGroup* group = mVOPartGroupp->mDrawable->getSpatialGroup();
- if (group)
- {
- LLVector3 center(group->getOctreeNode()->getCenter().getF32ptr());
- LLVector3 size(group->getOctreeNode()->getSize().getF32ptr());
- size += LLVector3(0.01f, 0.01f, 0.01f);
- mMinObjPos = center - size;
- mMaxObjPos = center + size;
- }
- else
- {
- // Not sure what else to set the obj bounds to when the drawable has no
- // spatial group.
- LLVector3 extents(mBoxRadius, mBoxRadius, mBoxRadius);
- mMinObjPos = center_agent - extents;
- mMaxObjPos = center_agent + extents;
- }
- }
- LLViewerPartGroup::~LLViewerPartGroup()
- {
- cleanup();
- S32 count = (S32)mParticles.size();
- for (S32 i = 0; i < count; ++i)
- {
- delete mParticles[i];
- }
- mParticles.clear();
- LLViewerPartSim::decPartCount(count);
- }
- void LLViewerPartGroup::cleanup()
- {
- if (mVOPartGroupp)
- {
- if (!mVOPartGroupp->isDead())
- {
- gObjectList.killObject(mVOPartGroupp);
- }
- mVOPartGroupp = NULL;
- }
- }
- bool LLViewerPartGroup::posInGroup(const LLVector3& pos, F32 desired_size)
- {
- if (pos.mV[VX] < mMinObjPos.mV[VX] || pos.mV[VY] < mMinObjPos.mV[VY] ||
- pos.mV[VZ] < mMinObjPos.mV[VZ])
- {
- return false;
- }
- if (pos.mV[VX] > mMaxObjPos.mV[VX] || pos.mV[VY] > mMaxObjPos.mV[VY] ||
- pos.mV[VZ] > mMaxObjPos.mV[VZ])
- {
- return false;
- }
- if (desired_size > 0 &&
- (desired_size < mBoxRadius * 0.5f || desired_size > mBoxRadius * 2.f))
- {
- return false;
- }
- return true;
- }
- bool LLViewerPartGroup::addPart(LLViewerPart* part, F32 desired_size)
- {
- if (!mHud && (part->mFlags & LLPartData::LL_PART_HUD))
- {
- return false;
- }
- bool uniform_part = part->mScale.mV[0] == part->mScale.mV[1] &&
- !(part->mFlags &
- LLPartData::LL_PART_FOLLOW_VELOCITY_MASK);
- if (mUniformParticles != uniform_part ||
- !posInGroup(part->mPosAgent, desired_size))
- {
- return false;
- }
- gPipeline.markRebuild(mVOPartGroupp->mDrawable);
- mParticles.push_back(part);
- part->mSkipOffset = mSkippedTime;
- LLViewerPartSim::incPartCount(1);
- return true;
- }
- void LLViewerPartGroup::updateParticles(F32 lastdt)
- {
- F32 dt;
- LLVector3 gravity(0.f, 0.f, GRAVITY);
- #if LL_DEBUG
- LLViewerPartSim::checkParticleCount(mParticles.size());
- #endif
- S32 end = (S32)mParticles.size();
- for (S32 i = 0, count = mParticles.size(); i < count; )
- {
- LLVector3 a;
- LLViewerPart* part = mParticles[i];
- dt = lastdt + mSkippedTime - part->mSkipOffset;
- part->mSkipOffset = 0.f;
- // Update current time
- const F32 cur_time = part->mLastUpdateTime + dt;
- const F32 frac = cur_time / part->mMaxAge;
- // "Drift" the object based on the source object
- if (part->mFlags & LLPartData::LL_PART_FOLLOW_SRC_MASK)
- {
- part->mPosAgent = part->mPartSourcep->mPosAgent;
- part->mPosAgent += part->mPosOffset;
- }
- // Do a custom callback if we have one...
- if (part->mVPCallback)
- {
- (*part->mVPCallback)(*part, dt);
- }
- if (part->mFlags & LLPartData::LL_PART_WIND_MASK)
- {
- part->mVelocity *= 1.f - 0.1f * dt;
- part->mVelocity += 0.1f * dt *
- mRegionp->mWind.getVelocity(mRegionp->getPosRegionFromAgent(part->mPosAgent));
- }
- // Now do interpolation towards a target
- if (part->mFlags & LLPartData::LL_PART_TARGET_POS_MASK)
- {
- F32 remaining = part->mMaxAge - part->mLastUpdateTime;
- F32 step = dt / remaining;
- step = llclamp(step, 0.f, 0.1f);
- step *= 5.f;
- // we want a velocity that will result in reaching the target in the
- // Interpolate towards the target.
- LLVector3 delta_pos = part->mPartSourcep->mTargetPosAgent -
- part->mPosAgent;
- delta_pos /= remaining;
- part->mVelocity *= 1.f - step;
- part->mVelocity += step * delta_pos;
- }
- if (part->mFlags & LLPartData::LL_PART_TARGET_LINEAR_MASK)
- {
- LLVector3 delta_pos = part->mPartSourcep->mTargetPosAgent -
- part->mPartSourcep->mPosAgent;
- part->mPosAgent = part->mPartSourcep->mPosAgent;
- part->mPosAgent += frac * delta_pos;
- part->mVelocity = delta_pos;
- }
- else
- {
- // Do velocity interpolation
- part->mPosAgent += dt * part->mVelocity;
- part->mPosAgent += 0.5f * dt * dt * part->mAccel;
- part->mVelocity += part->mAccel * dt;
- }
- // Do a bounce test
- if (part->mFlags & LLPartData::LL_PART_BOUNCE_MASK)
- {
- // Need to do point vs. plane check...
- // For now, just check relative to object height...
- F32 dz = part->mPosAgent.mV[VZ] -
- part->mPartSourcep->mPosAgent.mV[VZ];
- if (dz < 0)
- {
- part->mPosAgent.mV[VZ] += -2.f * dz;
- part->mVelocity.mV[VZ] *= -0.75f;
- }
- }
- // Reset the offset from the source position
- if (part->mFlags & LLPartData::LL_PART_FOLLOW_SRC_MASK)
- {
- part->mPosOffset = part->mPosAgent;
- part->mPosOffset -= part->mPartSourcep->mPosAgent;
- }
- // Do color interpolation
- if (part->mFlags & LLPartData::LL_PART_INTERP_COLOR_MASK)
- {
- part->mColor.set(part->mStartColor);
- // Note: LLColor4's v%k means multiply-alpha-only,
- // LLColor4's v*k means multiply-rgb-only
- part->mColor *= 1.f - frac; // rgb*k
- part->mColor %= 1.f - frac; // alpha*k
- part->mColor += frac % (frac * part->mEndColor); // rgb,alpha
- }
- // Do scale interpolation
- if (part->mFlags & LLPartData::LL_PART_INTERP_SCALE_MASK)
- {
- part->mScale.set(part->mStartScale);
- part->mScale *= 1.f - frac;
- part->mScale += frac * part->mEndScale;
- }
- // Do glow interpolation
- part->mGlow.mV[3] = (U8)ll_roundp(lerp(part->mStartGlow,
- part->mEndGlow, frac) * 255.f);
- // Set the last update time to now.
- part->mLastUpdateTime = cur_time;
- // Kill dead particles (either flagged dead, or too old)
- if (part->mLastUpdateTime > part->mMaxAge ||
- LLViewerPart::LL_PART_DEAD_MASK == part->mFlags)
- {
- delete part;
- if (i < --count)
- {
- mParticles[i] = mParticles.back();
- }
- mParticles.pop_back();
- }
- else
- {
- // Increment the active particles count for the source
- part->mPartSourcep->incPartCount();
- F32 desired_size = calc_desired_size(part->mPosAgent,
- part->mScale);
- if (!posInGroup(part->mPosAgent, desired_size))
- {
- // Transfer particles between groups
- gViewerPartSim.put(part);
- if (i < --count)
- {
- mParticles[i] = mParticles.back();
- }
- mParticles.pop_back();
- }
- else
- {
- ++i;
- }
- }
- }
- S32 removed = end - (S32)mParticles.size();
- if (removed > 0)
- {
- // We removed one or more particles, so flag this group for update
- if (mVOPartGroupp.notNull())
- {
- gPipeline.markRebuild(mVOPartGroupp->mDrawable);
- }
- LLViewerPartSim::decPartCount(removed);
- }
- // Kill the viewer object if this particle group is empty
- if (mParticles.empty())
- {
- gObjectList.killObject(mVOPartGroupp);
- mVOPartGroupp = NULL;
- }
- #if LL_DEBUG
- LLViewerPartSim::checkParticleCount(mParticles.size());
- #endif
- }
- void LLViewerPartGroup::shift(const LLVector3& offset)
- {
- mCenterAgent += offset;
- mMinObjPos += offset;
- mMaxObjPos += offset;
- for (S32 i = 0, count = mParticles.size(); i < count; ++i)
- {
- mParticles[i]->mPosAgent += offset;
- }
- }
- void LLViewerPartGroup::removeParticlesByID(U32 source_id)
- {
- for (S32 i = 0, count = mParticles.size(); i < count; ++i)
- {
- if (mParticles[i]->mPartSourcep->getID() == source_id)
- {
- mParticles[i]->mFlags = LLViewerPart::LL_PART_DEAD_MASK;
- }
- }
- }
- ///////////////////////////////////////////////////////////////////////////////
- // LLViewerPartSim class
- ///////////////////////////////////////////////////////////////////////////////
- void LLViewerPartSim::initClass()
- {
- setMaxPartCount(gSavedSettings.getS32("RenderMaxPartCount"));
- }
- void LLViewerPartSim::cleanupClass()
- {
- // Kill all of the groups (and particles)
- llinfos << "Destroying all particle groups..." << llendl;
- for (S32 i = 0, count = mViewerPartGroups.size(); i < count; ++i)
- {
- delete mViewerPartGroups[i];
- }
- mViewerPartGroups.clear();
- // Kill all of the sources
- llinfos << "Destroying all particle sources..." << llendl;
- mViewerPartSources.clear();
- llinfos << "Particles destroyed." << llendl;
- }
- //static
- void LLViewerPartSim::setMaxPartCount(S32 max)
- {
- sMaxParticleCount = llclamp(max, 2, 8192);
- }
- #if LL_DEBUG
- //static
- void LLViewerPartSim::checkParticleCount(U32 size)
- {
- if (sParticleCount2 != sParticleCount)
- {
- llerrs << "sParticleCount: " << sParticleCount
- << " - sParticleCount2: " << sParticleCount2 << llendl;
- }
- if (size > (U32)sParticleCount2)
- {
- llerrs << "Current particle size: " << sParticleCount2
- << " - array size: " << size << llendl;
- }
- }
- #endif
- //static
- bool LLViewerPartSim::shouldAddPart()
- {
- if (sParticleCount >= MAX_PART_COUNT)
- {
- return false;
- }
- if (sParticleCount > PART_THROTTLE_THRESHOLD * sMaxParticleCount)
- {
- F32 frac = (F32)sParticleCount / (F32)sMaxParticleCount;
- frac -= PART_THROTTLE_THRESHOLD;
- frac *= PART_THROTTLE_RESCALE;
- if (ll_frand() < frac)
- {
- // Skip...
- return false;
- }
- }
- // Check frame rate, and do not add more if the viewer is really slow
- constexpr F32 MIN_FRAME_RATE_FOR_NEW_PARTICLES = 5.f;
- return gFPSClamped >= MIN_FRAME_RATE_FOR_NEW_PARTICLES;
- }
- void LLViewerPartSim::addPart(LLViewerPart* part)
- {
- if (sParticleCount < MAX_PART_COUNT)
- {
- put(part);
- }
- else
- {
- // Delete the particle if we cannot add it in
- delete part;
- part = NULL;
- }
- }
- LLViewerPartGroup* LLViewerPartSim::put(LLViewerPart* part)
- {
- constexpr F32 MAX_MAG = 1000000.f * 1000000.f;
- LLViewerPartGroup* return_group = NULL;
- if (part->mPosAgent.lengthSquared() > MAX_MAG ||
- !part->mPosAgent.isFinite())
- {
- LL_DEBUGS("Particles") << "Particle out of range ! Position: "
- << part->mPosAgent << LL_ENDL;
- }
- else
- {
- F32 desired_size = calc_desired_size(part->mPosAgent, part->mScale);
- for (S32 i = 0, count = mViewerPartGroups.size(); i < count; ++i)
- {
- if (mViewerPartGroups[i]->addPart(part, desired_size))
- {
- // We found a spatial group that we fit into, add us and exit
- return_group = mViewerPartGroups[i];
- break;
- }
- }
- // Hmm, we did not fit in any of the existing spatial groups
- // Create a new one...
- if (!return_group)
- {
- llassert_always(part->mPosAgent.isFinite());
- LLViewerPartGroup* groupp =
- createViewerPartGroup(part->mPosAgent, desired_size,
- part->mFlags & LLPartData::LL_PART_HUD);
- groupp->mUniformParticles =
- part->mScale.mV[0] == part->mScale.mV[1] &&
- !(part->mFlags & LLPartData::LL_PART_FOLLOW_VELOCITY_MASK);
- if (!groupp->addPart(part))
- {
- llwarns << "Particle did not go into its box ! Particle group center: "
- << groupp->getCenterAgent() << " - mPosAgent = "
- << part->mPosAgent << llendl;
- mViewerPartGroups.pop_back();
- delete groupp;
- groupp = NULL;
- }
- return_group = groupp;
- }
- }
- if (!return_group) // Failed to insert the particle
- {
- delete part;
- }
- return return_group;
- }
- LLViewerPartGroup* LLViewerPartSim::createViewerPartGroup(const LLVector3& pos_agent,
- F32 desired_size,
- bool hud)
- {
- // Find a box that has a center position divisible by PART_SIM_BOX_SIDE
- // that encompasses pos_agent
- LLViewerPartGroup* groupp = new LLViewerPartGroup(pos_agent, desired_size,
- hud);
- mViewerPartGroups.push_back(groupp);
- return groupp;
- }
- void LLViewerPartSim::shift(const LLVector3& offset)
- {
- for (S32 i = 0, count = mViewerPartSources.size(); i < count; ++i)
- {
- mViewerPartSources[i]->mPosAgent += offset;
- mViewerPartSources[i]->mTargetPosAgent += offset;
- mViewerPartSources[i]->mLastUpdatePosAgent += offset;
- }
- for (S32 i = 0, count = mViewerPartGroups.size(); i < count; ++i)
- {
- mViewerPartGroups[i]->shift(offset);
- }
- }
- void LLViewerPartSim::updateSimulation()
- {
- static LLFrameTimer update_timer;
- const F32 dt = llmin(update_timer.getElapsedTimeAndResetF32(), 0.1f);
- if (!gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_PARTICLES))
- {
- return;
- }
- LL_FAST_TIMER(FTM_SIMULATE_PARTICLES);
- //MK
- // ref_joint will also be used as a flag for restricted vision in the loop
- // (when NULL, there is no restriction in force).
- LLJoint* ref_joint = NULL;
- LLVector3 joint_pos;
- if (gRLenabled && gRLInterface.mVisionRestricted)
- {
- ref_joint = gRLInterface.getCamDistDrawFromJoint();
- joint_pos = ref_joint->getWorldPosition();
- }
- //mk
- // Note: to avoid starvation of the particles allotment by high particle
- // count sources, the sources are updated in growing order of active
- // (rezzed) particles. The sorting is done at the end of this method. HB
- for (S32 i = 0, count = mViewerPartSources.size(); i < count; )
- {
- LLViewerPartSource* psrc = mViewerPartSources[i].get();
- if (psrc && !psrc->isDead())
- {
- bool upd = true;
- LLViewerObject* vobj = psrc->mSourceObjectp;
- bool is_volume = vobj && vobj->getPCode() == LL_PCODE_VOLUME;
- if (is_volume && !LLPipeline::sRenderAttachedParticles &&
- ((LLVOVolume*)vobj)->isAttachment())
- {
- upd = false;
- }
- if (upd && vobj && vobj->isAvatar() &&
- ((LLVOAvatar*)vobj)->isInMuteList())
- {
- upd = false;
- }
- if (upd && is_volume)
- {
- LLVOAvatar* avatar = vobj->getAvatar();
- if (avatar && avatar->isInMuteList())
- {
- upd = false;
- }
- }
- //MK
- // If our vision is obscured enough, particles in world and worn by
- // other avatars may give away their position (because of a
- // rendering issue) => hide them if their source object is too far.
- if (upd && is_volume && ref_joint)
- {
- LLVector3 offset = ((LLVOVolume*)vobj)->getPositionRegion() -
- joint_pos;
- if (offset.length() > gRLInterface.mCamDistDrawMax)
- {
- upd = false;
- }
- }
- //mk
- if (upd)
- {
- psrc->update(dt);
- }
- else
- {
- // Pretend the source is too far away (used in sorting)
- psrc->mDistFromCamera = 1024.f;
- }
- // Increment the updates count for this source
- psrc->incPartUpdates();
- }
- if (!psrc || psrc->isDead())
- {
- if (i < --count)
- {
- mViewerPartSources[i] = mViewerPartSources.back();
- }
- mViewerPartSources.pop_back();
- }
- else
- {
- ++i;
- }
- }
- S32 current_frame = LLViewerOctreeEntryData::getCurrentFrame();
- for (S32 i = 0, count = mViewerPartGroups.size(); i < count; ++i)
- {
- LLViewerPartGroup* pgroup = mViewerPartGroups[i];
- if (!pgroup) continue; // Paranoia !
- LLViewerObject* vobj = pgroup->mVOPartGroupp;
- LLDrawable* drawable = NULL;
- if (vobj && !vobj->isDead())
- {
- drawable = vobj->mDrawable;
- if (drawable && drawable->isDead())
- {
- drawable = NULL;
- }
- }
- S32 visirate = 1;
- if (drawable)
- {
- LLSpatialGroup* group = drawable->getSpatialGroup();
- if (group && !group->isVisible() /* &&
- !group->isState(LLSpatialGroup::OBJECT_DIRTY)*/)
- {
- if (!vobj || vobj->getPCode() != LL_PCODE_VOLUME ||
- !(LLPipeline::sRenderAttachedParticles &&
- ((LLVOVolume*)vobj)->isAttachment()))
- {
- visirate = 8;
- if (vobj)
- {
- LL_DEBUGS("Particles") << "Object " << vobj->getID()
- << " gets its particles refresh sparsed because its group is not visible."
- << LL_ENDL;
- }
- }
- }
- }
- if ((current_frame + pgroup->mID) % visirate == 0)
- {
- if (drawable)
- {
- gPipeline.markRebuild(drawable);
- }
- pgroup->updateParticles(dt * visirate);
- pgroup->mSkippedTime = 0.f;
- if (!pgroup->getCount())
- {
- delete pgroup;
- mViewerPartGroups[i--] = mViewerPartGroups.back();
- mViewerPartGroups.pop_back();
- --count;
- }
- }
- else
- {
- pgroup->mSkippedTime += dt;
- }
- }
- if (current_frame % 16 == 0)
- {
- if (sParticleCount > sMaxParticleCount * 0.875f &&
- sParticleAdaptiveRate < 2.f)
- {
- sParticleAdaptiveRate *= PART_ADAPT_RATE_MULT;
- }
- else
- {
- if (sParticleCount < sMaxParticleCount * 0.5f &&
- sParticleAdaptiveRate > 0.03125f)
- {
- sParticleAdaptiveRate *= PART_ADAPT_RATE_MULT_RECIP;
- }
- }
- #if 0 // Way too spammy...
- LL_DEBUGS("Particles") << "Particles: " << sParticleCount
- << " Adaptive Rate: " << sParticleAdaptiveRate
- << LL_ENDL;
- #endif
- }
- updatePartBurstRate();
- // Let's now sort the particle sources following the average number of
- // active (rezzed) particles they got per update, so that the sources
- // with less particles are updated first on next updateSimulation() run.
- // We also take into account the distance to the camera in our priorizing.
- // We use a std::map with a long integer as the key to perform a quick
- // sorting, by using the averaged particles count multiplied by a distance
- // factor as the MSLW of the key with a counter as the LSLW of the key, and
- // the source object address as the map data. We then std::move the smart
- // pointers to the sources from that map back into the sources vector. HB
- {
- LL_FAST_TIMER(FTM_SIM_PART_SORT);
- // Using an union to avoid costly bit shifting...
- union key_value
- {
- U64 key;
- U32 word[2];
- } kv;
- #if LL_BIG_ENDIAN
- # define LSW 1
- # define MSW 0
- #else
- # define LSW 0
- # define MSW 1
- #endif
- constexpr F32 ONE32TH = 1.f / 32.f;
- U32 count = (U32)mViewerPartSources.size();
- llassert(count < 1 << 24);
- typedef std::map<U64, LLPointer<LLViewerPartSource> > sorting_map_t;
- sorting_map_t sources;
- for (U32 i = 0; i < count; ++i)
- {
- LLViewerPartSource* psrc = mViewerPartSources[i].get();
- // Compute the key we will sort the particles with
- U64 dist_ratio = psrc->mDistFromCamera * ONE32TH;
- if (dist_ratio == 0)
- {
- dist_ratio = 1;
- }
- kv.word[LSW] = i;
- kv.word[MSW] = psrc->getAveragePartCount() * dist_ratio;
- LL_DEBUGS("Particles") << "Particle source #" << i << " "
- << psrc->getID();
- LLViewerObject* vobj = psrc->mSourceObjectp;
- if (vobj)
- {
- LL_CONT << " on object " << vobj->getID();
- if (vobj && vobj->getPCode() == LL_PCODE_VOLUME)
- {
- LLVOVolume* vvo = (LLVOVolume*)vobj;
- if (vvo && vvo->isAttachment())
- {
- LL_CONT << " (attachment)";
- }
- }
- }
- LL_CONT << " got an average particles count * distance factor of: "
- << kv.word[MSW] << " - Resulting key: 0x" << std::hex
- << kv.key << std::dec << LL_ENDL;
- // Do this last in the loop since mViewerPartSources[i] gets
- // invalidated (NULLed) by the move constructor of LLPointer.
- sources[kv.key] = std::move(mViewerPartSources[i]);
- }
- llassert_always(sources.size() == count);
- sorting_map_t::iterator it = sources.begin();
- for (U32 i = 0; i < count; ++i, ++it)
- {
- mViewerPartSources[i] = std::move(it->second);
- }
- LL_DEBUGS("Particles") << "Sorted particles sources:";
- for (U32 i = 0; i < count; ++i, ++it)
- {
- LL_CONT << "\n #" << i + 1 << ", source "
- << mViewerPartSources[i]->getID();
- }
- LL_CONT << LL_ENDL;
- }
- }
- void LLViewerPartSim::updatePartBurstRate()
- {
- if (LLViewerOctreeEntryData::getCurrentFrame() & 0xf)
- {
- return;
- }
- if (sParticleCount >= MAX_PART_COUNT)
- {
- // Set rate to zero if above max particles count
- sParticleBurstRate = 0.f;
- return;
- }
- if (sParticleCount <= 0)
- {
- sParticleBurstRate += 0.00125f;
- return;
- }
- if (sParticleBurstRate <= 0.0000001f)
- {
- sParticleBurstRate += 0.0000001f;
- return;
- }
- F32 total_particles = sParticleCount / sParticleBurstRate; // Estimated
- F32 new_rate = llmin(0.9f * sMaxParticleCount / total_particles, 1.f);
- F32 delta_rate_threshold =
- llmin(0.1f * llmax(new_rate, sParticleBurstRate), 0.1f);
- F32 delta_rate = llclamp(new_rate - sParticleBurstRate,
- -delta_rate_threshold, delta_rate_threshold);
- sParticleBurstRate = llclamp(sParticleBurstRate + 0.5f * delta_rate,
- 0.f, 1.f);
- }
- void LLViewerPartSim::addPartSource(LLPointer<LLViewerPartSource> sourcep)
- {
- if (sourcep.isNull())
- {
- llwarns << "Null particle source !" << llendl;
- return;
- }
- sourcep->setStart();
- // Do this last since sourcep gets invalidated (NULLed) by the move
- // constructor of LLPointer. HB
- mViewerPartSources.emplace_back(std::move(sourcep));
- }
- void LLViewerPartSim::removeLastCreatedSource()
- {
- mViewerPartSources.pop_back();
- }
- void LLViewerPartSim::cleanupRegion(LLViewerRegion* regionp)
- {
- for (U32 i = 0, count = mViewerPartGroups.size(); i < count; )
- {
- if (mViewerPartGroups[i]->getRegion() == regionp)
- {
- delete mViewerPartGroups[i];
- if (i < --count)
- {
- mViewerPartGroups[i] = mViewerPartGroups.back();
- }
- mViewerPartGroups.pop_back();
- }
- else
- {
- ++i;
- }
- }
- }
- void LLViewerPartSim::clearParticlesByID(U32 system_id)
- {
- for (group_list_t::iterator g = mViewerPartGroups.begin(),
- end = mViewerPartGroups.end();
- g != end; ++g)
- {
- (*g)->removeParticlesByID(system_id);
- }
- for (source_list_t::iterator i = mViewerPartSources.begin(),
- end = mViewerPartSources.end();
- i != end; ++i)
- {
- if ((*i)->getID() == system_id)
- {
- (*i)->setDead();
- break;
- }
- }
- }
- void LLViewerPartSim::clearParticlesByOwnerID(const LLUUID& task_id)
- {
- for (source_list_t::iterator iter = mViewerPartSources.begin(),
- end = mViewerPartSources.end();
- iter != end; ++iter)
- {
- if ((*iter)->getOwnerUUID() == task_id)
- {
- clearParticlesByID((*iter)->getID());
- }
- }
- }
- void LLViewerPartSim::clearParticlesByRootObjectID(const LLUUID& object_id)
- {
- LLViewerObject* objectp = gObjectList.findObject(object_id);
- if (!objectp)
- {
- llwarns << "Tried to clear particles for non-existent object "
- << object_id << llendl;
- }
- else if (objectp->isAvatar())
- {
- clearParticlesByOwnerID(object_id);
- }
- else
- {
- if (objectp->getPartSource())
- {
- clearParticlesByID(objectp->getPartSource()->getID());
- }
- for (LLViewerObject::child_list_t::const_iterator
- it = objectp->getChildren().begin(),
- end = objectp->getChildren().end();
- it != end; ++it)
- {
- LLViewerObject* childp = *it;
- if (childp && childp->getPartSource())
- {
- clearParticlesByID(childp->getPartSource()->getID());
- }
- }
- }
- }
|