diff options
author | Ansariel <ansariel.hiller@phoenixviewer.com> | 2024-05-22 19:04:52 +0200 |
---|---|---|
committer | Ansariel <ansariel.hiller@phoenixviewer.com> | 2024-05-22 19:04:52 +0200 |
commit | 1b67dd855c41f5a0cda7ec2a68d98071986ca703 (patch) | |
tree | ab243607f74f78200787bba5b9b88f07ef1b966f /indra/newview/llviewerpartsim.cpp | |
parent | 6d6eabca44d08d5b97bfe3e941d2b9687c2246ea (diff) | |
parent | e1623bb276f83a43ce7a197e388720c05bdefe61 (diff) |
Merge remote-tracking branch 'origin/main' into DRTVWR-600-maint-A
# Conflicts:
# autobuild.xml
# indra/cmake/CMakeLists.txt
# indra/cmake/GoogleMock.cmake
# indra/llaudio/llaudioengine_fmodstudio.cpp
# indra/llaudio/llaudioengine_fmodstudio.h
# indra/llaudio/lllistener_fmodstudio.cpp
# indra/llaudio/lllistener_fmodstudio.h
# indra/llaudio/llstreamingaudio_fmodstudio.cpp
# indra/llaudio/llstreamingaudio_fmodstudio.h
# indra/llcharacter/llmultigesture.cpp
# indra/llcharacter/llmultigesture.h
# indra/llimage/llimage.cpp
# indra/llimage/llimagepng.cpp
# indra/llimage/llimageworker.cpp
# indra/llimage/tests/llimageworker_test.cpp
# indra/llmessage/tests/llmockhttpclient.h
# indra/llprimitive/llgltfmaterial.h
# indra/llrender/llfontfreetype.cpp
# indra/llui/llcombobox.cpp
# indra/llui/llfolderview.cpp
# indra/llui/llfolderviewmodel.h
# indra/llui/lllineeditor.cpp
# indra/llui/lllineeditor.h
# indra/llui/lltextbase.cpp
# indra/llui/lltextbase.h
# indra/llui/lltexteditor.cpp
# indra/llui/lltextvalidate.cpp
# indra/llui/lltextvalidate.h
# indra/llui/lluictrl.h
# indra/llui/llview.cpp
# indra/llwindow/llwindowmacosx.cpp
# indra/newview/app_settings/settings.xml
# indra/newview/llappearancemgr.cpp
# indra/newview/llappearancemgr.h
# indra/newview/llavatarpropertiesprocessor.cpp
# indra/newview/llavatarpropertiesprocessor.h
# indra/newview/llbreadcrumbview.cpp
# indra/newview/llbreadcrumbview.h
# indra/newview/llbreastmotion.cpp
# indra/newview/llbreastmotion.h
# indra/newview/llconversationmodel.h
# indra/newview/lldensityctrl.cpp
# indra/newview/lldensityctrl.h
# indra/newview/llface.inl
# indra/newview/llfloatereditsky.cpp
# indra/newview/llfloatereditwater.cpp
# indra/newview/llfloateremojipicker.h
# indra/newview/llfloaterimsessiontab.cpp
# indra/newview/llfloaterprofiletexture.cpp
# indra/newview/llfloaterprofiletexture.h
# indra/newview/llgesturemgr.cpp
# indra/newview/llgesturemgr.h
# indra/newview/llimpanel.cpp
# indra/newview/llimpanel.h
# indra/newview/llinventorybridge.cpp
# indra/newview/llinventorybridge.h
# indra/newview/llinventoryclipboard.cpp
# indra/newview/llinventoryclipboard.h
# indra/newview/llinventoryfunctions.cpp
# indra/newview/llinventoryfunctions.h
# indra/newview/llinventorygallery.cpp
# indra/newview/lllistbrowser.cpp
# indra/newview/lllistbrowser.h
# indra/newview/llpanelobjectinventory.cpp
# indra/newview/llpanelprofile.cpp
# indra/newview/llpanelprofile.h
# indra/newview/llpreviewgesture.cpp
# indra/newview/llsavedsettingsglue.cpp
# indra/newview/llsavedsettingsglue.h
# indra/newview/lltooldraganddrop.cpp
# indra/newview/llurllineeditorctrl.cpp
# indra/newview/llvectorperfoptions.cpp
# indra/newview/llvectorperfoptions.h
# indra/newview/llviewerparceloverlay.cpp
# indra/newview/llviewertexlayer.cpp
# indra/newview/llviewertexturelist.cpp
# indra/newview/macmain.h
# indra/test/test.cpp
Diffstat (limited to 'indra/newview/llviewerpartsim.cpp')
-rw-r--r-- | indra/newview/llviewerpartsim.cpp | 1792 |
1 files changed, 896 insertions, 896 deletions
diff --git a/indra/newview/llviewerpartsim.cpp b/indra/newview/llviewerpartsim.cpp index 386a44b0d9..932ab7a61d 100644 --- a/indra/newview/llviewerpartsim.cpp +++ b/indra/newview/llviewerpartsim.cpp @@ -1,896 +1,896 @@ -/** - * @file llviewerpartsim.cpp - * @brief LLViewerPart class implementation - * - * $LicenseInfo:firstyear=2003&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" - -#include "llviewerpartsim.h" - -#include "llviewercontrol.h" - -#include "llagent.h" -#include "llviewercamera.h" -#include "llviewerobjectlist.h" -#include "llviewerpartsource.h" -#include "llviewerregion.h" -#include "llvopartgroup.h" -#include "llworld.h" -#include "llmutelist.h" -#include "pipeline.h" -#include "llspatialpartition.h" -#include "llvoavatarself.h" -#include "llvovolume.h" - -const F32 PART_SIM_BOX_SIDE = 16.f; - -//static -S32 LLViewerPartSim::sMaxParticleCount = 0; -S32 LLViewerPartSim::sParticleCount = 0; -S32 LLViewerPartSim::sParticleCount2 = 0; -// 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; - -//static -const S32 LLViewerPartSim::MAX_PART_COUNT = 8192; -const F32 LLViewerPartSim::PART_THROTTLE_THRESHOLD = 0.9f; -const F32 LLViewerPartSim::PART_ADAPT_RATE_MULT = 2.0f; - -//static -const F32 LLViewerPartSim::PART_THROTTLE_RESCALE = PART_THROTTLE_THRESHOLD / (1.0f-PART_THROTTLE_THRESHOLD); -const F32 LLViewerPartSim::PART_ADAPT_RATE_MULT_RECIP = 1.0f/PART_ADAPT_RATE_MULT; - - -U32 LLViewerPart::sNextPartID = 1; - -F32 calc_desired_size(LLViewerCamera* camera, LLVector3 pos, LLVector2 scale) -{ - F32 desired_size = (pos - camera->getOrigin()).magVec(); - desired_size /= 4; - return llclamp(desired_size, scale.magVec()*0.5f, PART_SIM_BOX_SIDE*2); -} - -LLViewerPart::LLViewerPart() : - mPartID(0), - mLastUpdateTime(0.f), - mSkipOffset(0.f), - mVPCallback(NULL), - mImagep(NULL) -{ - mPartSourcep = NULL; - mParent = NULL; - mChild = NULL; - ++LLViewerPartSim::sParticleCount2 ; -} - -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; - - --LLViewerPartSim::sParticleCount2 ; -} - -void LLViewerPart::init(LLPointer<LLViewerPartSource> sourcep, LLViewerTexture *imagep, LLVPCallback cb) -{ - mPartID = LLViewerPart::sNextPartID; - LLViewerPart::sNextPartID++; - mFlags = 0x00f; - mLastUpdateTime = 0.f; - mMaxAge = 10.f; - mSkipOffset = 0.0f; - - mVPCallback = cb; - mPartSourcep = sourcep; - - mImagep = imagep; -} - - -///////////////////////////// -// -// LLViewerPartGroup implementation -// -// - - -LLViewerPartGroup::LLViewerPartGroup(const LLVector3 ¢er_agent, const F32 box_side, bool hud) - : mHud(hud) -{ - mVOPartGroupp = NULL; - mUniformParticles = true; - - mRegionp = LLWorld::getInstance()->getRegionFromPosAgent(center_agent); - llassert_always(center_agent.isFinite()); - - if (!mRegionp) - { - //LL_WARNS() << "No region at position, using agent region!" << LL_ENDL; - mRegionp = gAgent.getRegion(); - } - mCenterAgent = center_agent; - mBoxRadius = F_SQRT3*box_side*0.5f; - - 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()); - - mBoxSide = box_side; - - F32 scale = box_side * 0.5f; - - mVOPartGroupp->setScale(LLVector3(scale,scale,scale)); - - //gPipeline.addObject(mVOPartGroupp); - gPipeline.createObject(mVOPartGroupp); - - LLSpatialGroup* group = mVOPartGroupp->mDrawable->getSpatialGroup(); - - if (group != NULL) - { - 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; - } - - mSkippedTime = 0.f; - - static U32 id_seed = 0; - mID = ++id_seed; -} - -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, const 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 (part->mFlags & LLPartData::LL_PART_HUD && !mHud) - { - return false; - } - - bool uniform_part = part->mScale.mV[0] == part->mScale.mV[1] && - !(part->mFlags & LLPartData::LL_PART_FOLLOW_VELOCITY_MASK); - - if (!posInGroup(part->mPosAgent, desired_size) || - (mUniformParticles && !uniform_part) || - (!mUniformParticles && uniform_part)) - { - return false; - } - - gPipeline.markRebuild(mVOPartGroupp->mDrawable, LLDrawable::REBUILD_ALL); - - mParticles.push_back(part); - part->mSkipOffset=mSkippedTime; - LLViewerPartSim::incPartCount(1); - return true; -} - - -void LLViewerPartGroup::updateParticles(const F32 lastdt) -{ - F32 dt; - - LLVector3 gravity(0.f, 0.f, GRAVITY); - - LLViewerPartSim::checkParticleCount(mParticles.size()); - - LLViewerCamera* camera = LLViewerCamera::getInstance(); - LLViewerRegion *regionp = getRegion(); - S32 end = (S32) mParticles.size(); - for (S32 i = 0 ; i < (S32)mParticles.size();) - { - LLVector3 a(0.f, 0.f, 0.f); - 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*regionp->mWind.getVelocity(regionp->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.setVec(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.setVec(part->mStartScale); - part->mScale *= 1.f - frac; - part->mScale += frac*part->mEndScale; - } - - // Do glow interpolation - part->mGlow.mV[3] = (U8) ll_round(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)) - { - mParticles[i] = mParticles.back() ; - mParticles.pop_back() ; - delete part ; - } - else - { - F32 desired_size = calc_desired_size(camera, part->mPosAgent, part->mScale); - if (!posInGroup(part->mPosAgent, desired_size)) - { - // Transfer particles between groups - LLViewerPartSim::getInstance()->put(part) ; - 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, LLDrawable::REBUILD_ALL); - } - LLViewerPartSim::decPartCount(removed); - } - - // Kill the viewer object if this particle group is empty - if (mParticles.empty()) - { - gObjectList.killObject(mVOPartGroupp); - mVOPartGroupp = NULL; - } - - LLViewerPartSim::checkParticleCount() ; -} - - -void LLViewerPartGroup::shift(const LLVector3 &offset) -{ - mCenterAgent += offset; - mMinObjPos += offset; - mMaxObjPos += offset; - - for (S32 i = 0 ; i < (S32)mParticles.size(); i++) - { - mParticles[i]->mPosAgent += offset; - } -} - -void LLViewerPartGroup::removeParticlesByID(const U32 source_id) -{ - for (S32 i = 0; i < (S32)mParticles.size(); i++) - { - if(mParticles[i]->mPartSourcep->getID() == source_id) - { - mParticles[i]->mFlags = LLViewerPart::LL_PART_DEAD_MASK; - } - } -} - -////////////////////////////////// -// -// LLViewerPartSim implementation -// -// - -//static -void LLViewerPartSim::checkParticleCount(U32 size) -{ - if(LLViewerPartSim::sParticleCount2 != LLViewerPartSim::sParticleCount) - { - LL_ERRS() << "sParticleCount: " << LLViewerPartSim::sParticleCount << " ; sParticleCount2: " << LLViewerPartSim::sParticleCount2 << LL_ENDL ; - } - - if(size > (U32)LLViewerPartSim::sParticleCount2) - { - LL_ERRS() << "curren particle size: " << LLViewerPartSim::sParticleCount2 << " array size: " << size << LL_ENDL ; - } -} - -LLViewerPartSim::LLViewerPartSim() -{ - sMaxParticleCount = llmin(gSavedSettings.getS32("RenderMaxPartCount"), LL_MAX_PARTICLE_COUNT); - static U32 id_seed = 0; - mID = ++id_seed; -} - -//enable/disable particle system -void LLViewerPartSim::enable(bool enabled) -{ - if(!enabled && sMaxParticleCount > 0) - { - sMaxParticleCount = 0; //disable - } - else if(enabled && sMaxParticleCount < 1) - { - sMaxParticleCount = llmin(gSavedSettings.getS32("RenderMaxPartCount"), LL_MAX_PARTICLE_COUNT); - } - - return; -} - -void LLViewerPartSim::destroyClass() -{ - S32 i; - S32 count; - - // Kill all of the groups (and particles) - count = (S32) mViewerPartGroups.size(); - for (i = 0; i < count; i++) - { - delete mViewerPartGroups[i]; - } - mViewerPartGroups.clear(); - - // Kill all of the sources - mViewerPartSources.clear(); -} - -//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 don't add more if the viewer is really slow - const F32 MIN_FRAME_RATE_FOR_NEW_PARTICLES = 4.f; - if (gFPSClamped < MIN_FRAME_RATE_FOR_NEW_PARTICLES) - { - return false; - } - - return true; -} - -void LLViewerPartSim::addPart(LLViewerPart* part) -{ - if (sParticleCount < MAX_PART_COUNT) - { - put(part); - } - else - { - //delete the particle if can not add it in - delete part ; - part = NULL ; - } -} - - -LLViewerPartGroup *LLViewerPartSim::put(LLViewerPart* part) -{ - const F32 MAX_MAG = 1000000.f*1000000.f; // 1 million - LLViewerPartGroup *return_group = NULL ; - if (part->mPosAgent.magVecSquared() > MAX_MAG || !part->mPosAgent.isFinite()) - { -#if 0 && !LL_RELEASE_FOR_DOWNLOAD - LL_WARNS() << "LLViewerPartSim::put Part out of range!" << LL_ENDL; - LL_WARNS() << part->mPosAgent << LL_ENDL; -#endif - } - else - { - LLViewerCamera* camera = LLViewerCamera::getInstance(); - F32 desired_size = calc_desired_size(camera, part->mPosAgent, part->mScale); - - S32 count = (S32) mViewerPartGroups.size(); - for (S32 i = 0; 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 didn't 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)) - { - LL_WARNS() << "LLViewerPartSim::put - Particle didn't go into its box!" << LL_ENDL; - LL_INFOS() << groupp->getCenterAgent() << LL_ENDL; - LL_INFOS() << part->mPosAgent << LL_ENDL; - mViewerPartGroups.pop_back() ; - delete groupp; - groupp = NULL ; - } - return_group = groupp; - } - } - - if(!return_group) //failed to insert the particle - { - delete part ; - part = NULL ; - } - - return return_group ; -} - -LLViewerPartGroup *LLViewerPartSim::createViewerPartGroup(const LLVector3 &pos_agent, const 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) -{ - S32 i; - S32 count; - - count = (S32) mViewerPartSources.size(); - for (i = 0; i < count; i++) - { - mViewerPartSources[i]->mPosAgent += offset; - mViewerPartSources[i]->mTargetPosAgent += offset; - mViewerPartSources[i]->mLastUpdatePosAgent += offset; - } - - count = (S32) mViewerPartGroups.size(); - for (i = 0; i < count; i++) - { - mViewerPartGroups[i]->shift(offset); - } -} - -static LLTrace::BlockTimerStatHandle FTM_SIMULATE_PARTICLES("Simulate Particles"); - -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_RECORD_BLOCK_TIME(FTM_SIMULATE_PARTICLES); - - // Start at a random particle system so the same - // particle system doesn't always get first pick at the - // particles. Theoretically we'd want to do this in distance - // order or something, but sorting particle sources will be a big - // pain. - S32 i; - S32 count = (S32) mViewerPartSources.size(); - S32 start = (S32)ll_frand((F32)count); - S32 dir = 1; - S32 deldir = 0; - if (ll_frand() > 0.5f) - { - dir = -1; - deldir = -1; - } - - S32 num_updates = 0; - for (i = start; num_updates < count;) - { - if (i >= count) - { - i = 0; - } - if (i < 0) - { - i = count - 1; - } - - if (!mViewerPartSources[i]->isDead()) - { - bool upd = true; - LLViewerObject* vobj = mViewerPartSources[i]->mSourceObjectp; - - if (vobj && vobj->isAvatar() && ((LLVOAvatar*)vobj)->isInMuteList()) - { - upd = false; - } - - if(vobj && vobj->isOwnerInMuteList(mViewerPartSources[i]->getOwnerUUID())) - { - upd = false; - } - - if (upd && vobj && (vobj->getPCode() == LL_PCODE_VOLUME)) - { - if(vobj->getAvatar() && vobj->getAvatar()->isTooComplex() && vobj->getAvatar()->isTooSlow()) - { - upd = false; - } - - LLVOVolume* vvo = (LLVOVolume *)vobj; - if (!LLPipeline::sRenderAttachedParticles && vvo && vvo->isAttachment()) - { - upd = false; - } - } - - if (upd) - { - mViewerPartSources[i]->update(dt); - } - } - - if (mViewerPartSources[i]->isDead()) - { - mViewerPartSources.erase(mViewerPartSources.begin() + i); - count--; - i+=deldir; - } - else - { - i += dir; - } - num_updates++; - } - - count = (S32) mViewerPartGroups.size(); - for (i = 0; i < count; i++) - { - LLViewerObject* vobj = mViewerPartGroups[i]->mVOPartGroupp; - - S32 visirate = 1; - if (vobj && !vobj->isDead() && vobj->mDrawable && !vobj->mDrawable->isDead()) - { - LLSpatialGroup* group = vobj->mDrawable->getSpatialGroup(); - if (group && !group->isVisible()) // && !group->isState(LLSpatialGroup::OBJECT_DIRTY)) - { - visirate = 8; - } - } - - if ((LLDrawable::getCurrentFrame()+mViewerPartGroups[i]->mID)%visirate == 0) - { - if (vobj && !vobj->isDead()) - { - gPipeline.markRebuild(vobj->mDrawable, LLDrawable::REBUILD_ALL); - } - mViewerPartGroups[i]->updateParticles(dt * visirate); - mViewerPartGroups[i]->mSkippedTime=0.0f; - if (!mViewerPartGroups[i]->getCount()) - { - delete mViewerPartGroups[i]; - mViewerPartGroups.erase(mViewerPartGroups.begin() + i); - i--; - count--; - } - } - else - { - mViewerPartGroups[i]->mSkippedTime+=dt; - } - - } - if (LLDrawable::getCurrentFrame()%16==0) - { - if (sParticleCount > sMaxParticleCount * 0.875f - && sParticleAdaptiveRate < 2.0f) - { - sParticleAdaptiveRate *= PART_ADAPT_RATE_MULT; - } - else - { - if (sParticleCount < sMaxParticleCount * 0.5f - && sParticleAdaptiveRate > 0.03125f) - { - sParticleAdaptiveRate *= PART_ADAPT_RATE_MULT_RECIP; - } - } - } - - updatePartBurstRate() ; - - //LL_INFOS() << "Particles: " << sParticleCount << " Adaptive Rate: " << sParticleAdaptiveRate << LL_ENDL; -} - -void LLViewerPartSim::updatePartBurstRate() -{ - if (!(LLDrawable::getCurrentFrame() & 0xf)) - { - if (sParticleCount >= MAX_PART_COUNT) //set rate to zero - { - sParticleBurstRate = 0.0f ; - } - else if(sParticleCount > 0) - { - if(sParticleBurstRate > 0.0000001f) - { - F32 total_particles = sParticleCount / sParticleBurstRate ; //estimated - F32 new_rate = llclamp(0.9f * sMaxParticleCount / total_particles, 0.0f, 1.0f) ; - F32 delta_rate_threshold = llmin(0.1f * llmax(new_rate, sParticleBurstRate), 0.1f) ; - F32 delta_rate = llclamp(new_rate - sParticleBurstRate, -1.0f * delta_rate_threshold, delta_rate_threshold) ; - - sParticleBurstRate = llclamp(sParticleBurstRate + 0.5f * delta_rate, 0.0f, 1.0f) ; - } - else - { - sParticleBurstRate += 0.0000001f ; - } - } - else - { - sParticleBurstRate += 0.00125f ; - } - } -} - -void LLViewerPartSim::addPartSource(LLPointer<LLViewerPartSource> sourcep) -{ - if (!sourcep) - { - LL_WARNS() << "Null part source!" << LL_ENDL; - return; - } - sourcep->setStart() ; - mViewerPartSources.push_back(sourcep); -} - -void LLViewerPartSim::removeLastCreatedSource() -{ - mViewerPartSources.pop_back(); -} - -void LLViewerPartSim::cleanupRegion(LLViewerRegion *regionp) -{ - for (group_list_t::iterator i = mViewerPartGroups.begin(); i != mViewerPartGroups.end(); ) - { - group_list_t::iterator iter = i++; - - if ((*iter)->getRegion() == regionp) - { - delete *iter; - i = mViewerPartGroups.erase(iter); - } - } -} - -void LLViewerPartSim::clearParticlesByID(const U32 system_id) -{ - for (group_list_t::iterator g = mViewerPartGroups.begin(); g != mViewerPartGroups.end(); ++g) - { - (*g)->removeParticlesByID(system_id); - } - for (source_list_t::iterator i = mViewerPartSources.begin(); i != mViewerPartSources.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(); iter != mViewerPartSources.end(); ++iter) - { - if ((*iter)->getOwnerUUID() == task_id) - { - clearParticlesByID((*iter)->getID()); - } - } -} +/**
+ * @file llviewerpartsim.cpp
+ * @brief LLViewerPart class implementation
+ *
+ * $LicenseInfo:firstyear=2003&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llviewerpartsim.h"
+
+#include "llviewercontrol.h"
+
+#include "llagent.h"
+#include "llviewercamera.h"
+#include "llviewerobjectlist.h"
+#include "llviewerpartsource.h"
+#include "llviewerregion.h"
+#include "llvopartgroup.h"
+#include "llworld.h"
+#include "llmutelist.h"
+#include "pipeline.h"
+#include "llspatialpartition.h"
+#include "llvoavatarself.h"
+#include "llvovolume.h"
+
+const F32 PART_SIM_BOX_SIDE = 16.f;
+
+//static
+S32 LLViewerPartSim::sMaxParticleCount = 0;
+S32 LLViewerPartSim::sParticleCount = 0;
+S32 LLViewerPartSim::sParticleCount2 = 0;
+// 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;
+
+//static
+const S32 LLViewerPartSim::MAX_PART_COUNT = 8192;
+const F32 LLViewerPartSim::PART_THROTTLE_THRESHOLD = 0.9f;
+const F32 LLViewerPartSim::PART_ADAPT_RATE_MULT = 2.0f;
+
+//static
+const F32 LLViewerPartSim::PART_THROTTLE_RESCALE = PART_THROTTLE_THRESHOLD / (1.0f-PART_THROTTLE_THRESHOLD);
+const F32 LLViewerPartSim::PART_ADAPT_RATE_MULT_RECIP = 1.0f/PART_ADAPT_RATE_MULT;
+
+
+U32 LLViewerPart::sNextPartID = 1;
+
+F32 calc_desired_size(LLViewerCamera* camera, LLVector3 pos, LLVector2 scale)
+{
+ F32 desired_size = (pos - camera->getOrigin()).magVec();
+ desired_size /= 4;
+ return llclamp(desired_size, scale.magVec()*0.5f, PART_SIM_BOX_SIDE*2);
+}
+
+LLViewerPart::LLViewerPart() :
+ mPartID(0),
+ mLastUpdateTime(0.f),
+ mSkipOffset(0.f),
+ mVPCallback(NULL),
+ mImagep(NULL)
+{
+ mPartSourcep = NULL;
+ mParent = NULL;
+ mChild = NULL;
+ ++LLViewerPartSim::sParticleCount2 ;
+}
+
+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;
+
+ --LLViewerPartSim::sParticleCount2 ;
+}
+
+void LLViewerPart::init(LLPointer<LLViewerPartSource> sourcep, LLViewerTexture *imagep, LLVPCallback cb)
+{
+ mPartID = LLViewerPart::sNextPartID;
+ LLViewerPart::sNextPartID++;
+ mFlags = 0x00f;
+ mLastUpdateTime = 0.f;
+ mMaxAge = 10.f;
+ mSkipOffset = 0.0f;
+
+ mVPCallback = cb;
+ mPartSourcep = sourcep;
+
+ mImagep = imagep;
+}
+
+
+/////////////////////////////
+//
+// LLViewerPartGroup implementation
+//
+//
+
+
+LLViewerPartGroup::LLViewerPartGroup(const LLVector3 ¢er_agent, const F32 box_side, bool hud)
+ : mHud(hud)
+{
+ mVOPartGroupp = NULL;
+ mUniformParticles = true;
+
+ mRegionp = LLWorld::getInstance()->getRegionFromPosAgent(center_agent);
+ llassert_always(center_agent.isFinite());
+
+ if (!mRegionp)
+ {
+ //LL_WARNS() << "No region at position, using agent region!" << LL_ENDL;
+ mRegionp = gAgent.getRegion();
+ }
+ mCenterAgent = center_agent;
+ mBoxRadius = F_SQRT3*box_side*0.5f;
+
+ 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());
+
+ mBoxSide = box_side;
+
+ F32 scale = box_side * 0.5f;
+
+ mVOPartGroupp->setScale(LLVector3(scale,scale,scale));
+
+ //gPipeline.addObject(mVOPartGroupp);
+ gPipeline.createObject(mVOPartGroupp);
+
+ LLSpatialGroup* group = mVOPartGroupp->mDrawable->getSpatialGroup();
+
+ if (group != NULL)
+ {
+ 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;
+ }
+
+ mSkippedTime = 0.f;
+
+ static U32 id_seed = 0;
+ mID = ++id_seed;
+}
+
+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, const 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 (part->mFlags & LLPartData::LL_PART_HUD && !mHud)
+ {
+ return false;
+ }
+
+ bool uniform_part = part->mScale.mV[0] == part->mScale.mV[1] &&
+ !(part->mFlags & LLPartData::LL_PART_FOLLOW_VELOCITY_MASK);
+
+ if (!posInGroup(part->mPosAgent, desired_size) ||
+ (mUniformParticles && !uniform_part) ||
+ (!mUniformParticles && uniform_part))
+ {
+ return false;
+ }
+
+ gPipeline.markRebuild(mVOPartGroupp->mDrawable, LLDrawable::REBUILD_ALL);
+
+ mParticles.push_back(part);
+ part->mSkipOffset=mSkippedTime;
+ LLViewerPartSim::incPartCount(1);
+ return true;
+}
+
+
+void LLViewerPartGroup::updateParticles(const F32 lastdt)
+{
+ F32 dt;
+
+ LLVector3 gravity(0.f, 0.f, GRAVITY);
+
+ LLViewerPartSim::checkParticleCount(mParticles.size());
+
+ LLViewerCamera* camera = LLViewerCamera::getInstance();
+ LLViewerRegion *regionp = getRegion();
+ S32 end = (S32) mParticles.size();
+ for (S32 i = 0 ; i < (S32)mParticles.size();)
+ {
+ LLVector3 a(0.f, 0.f, 0.f);
+ 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*regionp->mWind.getVelocity(regionp->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.setVec(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.setVec(part->mStartScale);
+ part->mScale *= 1.f - frac;
+ part->mScale += frac*part->mEndScale;
+ }
+
+ // Do glow interpolation
+ part->mGlow.mV[3] = (U8) ll_round(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))
+ {
+ mParticles[i] = mParticles.back() ;
+ mParticles.pop_back() ;
+ delete part ;
+ }
+ else
+ {
+ F32 desired_size = calc_desired_size(camera, part->mPosAgent, part->mScale);
+ if (!posInGroup(part->mPosAgent, desired_size))
+ {
+ // Transfer particles between groups
+ LLViewerPartSim::getInstance()->put(part) ;
+ 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, LLDrawable::REBUILD_ALL);
+ }
+ LLViewerPartSim::decPartCount(removed);
+ }
+
+ // Kill the viewer object if this particle group is empty
+ if (mParticles.empty())
+ {
+ gObjectList.killObject(mVOPartGroupp);
+ mVOPartGroupp = NULL;
+ }
+
+ LLViewerPartSim::checkParticleCount() ;
+}
+
+
+void LLViewerPartGroup::shift(const LLVector3 &offset)
+{
+ mCenterAgent += offset;
+ mMinObjPos += offset;
+ mMaxObjPos += offset;
+
+ for (S32 i = 0 ; i < (S32)mParticles.size(); i++)
+ {
+ mParticles[i]->mPosAgent += offset;
+ }
+}
+
+void LLViewerPartGroup::removeParticlesByID(const U32 source_id)
+{
+ for (S32 i = 0; i < (S32)mParticles.size(); i++)
+ {
+ if(mParticles[i]->mPartSourcep->getID() == source_id)
+ {
+ mParticles[i]->mFlags = LLViewerPart::LL_PART_DEAD_MASK;
+ }
+ }
+}
+
+//////////////////////////////////
+//
+// LLViewerPartSim implementation
+//
+//
+
+//static
+void LLViewerPartSim::checkParticleCount(U32 size)
+{
+ if(LLViewerPartSim::sParticleCount2 != LLViewerPartSim::sParticleCount)
+ {
+ LL_ERRS() << "sParticleCount: " << LLViewerPartSim::sParticleCount << " ; sParticleCount2: " << LLViewerPartSim::sParticleCount2 << LL_ENDL ;
+ }
+
+ if(size > (U32)LLViewerPartSim::sParticleCount2)
+ {
+ LL_ERRS() << "curren particle size: " << LLViewerPartSim::sParticleCount2 << " array size: " << size << LL_ENDL ;
+ }
+}
+
+LLViewerPartSim::LLViewerPartSim()
+{
+ sMaxParticleCount = llmin(gSavedSettings.getS32("RenderMaxPartCount"), LL_MAX_PARTICLE_COUNT);
+ static U32 id_seed = 0;
+ mID = ++id_seed;
+}
+
+//enable/disable particle system
+void LLViewerPartSim::enable(bool enabled)
+{
+ if(!enabled && sMaxParticleCount > 0)
+ {
+ sMaxParticleCount = 0; //disable
+ }
+ else if(enabled && sMaxParticleCount < 1)
+ {
+ sMaxParticleCount = llmin(gSavedSettings.getS32("RenderMaxPartCount"), LL_MAX_PARTICLE_COUNT);
+ }
+
+ return;
+}
+
+void LLViewerPartSim::destroyClass()
+{
+ S32 i;
+ S32 count;
+
+ // Kill all of the groups (and particles)
+ count = (S32) mViewerPartGroups.size();
+ for (i = 0; i < count; i++)
+ {
+ delete mViewerPartGroups[i];
+ }
+ mViewerPartGroups.clear();
+
+ // Kill all of the sources
+ mViewerPartSources.clear();
+}
+
+//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 don't add more if the viewer is really slow
+ const F32 MIN_FRAME_RATE_FOR_NEW_PARTICLES = 4.f;
+ if (gFPSClamped < MIN_FRAME_RATE_FOR_NEW_PARTICLES)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+void LLViewerPartSim::addPart(LLViewerPart* part)
+{
+ if (sParticleCount < MAX_PART_COUNT)
+ {
+ put(part);
+ }
+ else
+ {
+ //delete the particle if can not add it in
+ delete part ;
+ part = NULL ;
+ }
+}
+
+
+LLViewerPartGroup *LLViewerPartSim::put(LLViewerPart* part)
+{
+ const F32 MAX_MAG = 1000000.f*1000000.f; // 1 million
+ LLViewerPartGroup *return_group = NULL ;
+ if (part->mPosAgent.magVecSquared() > MAX_MAG || !part->mPosAgent.isFinite())
+ {
+#if 0 && !LL_RELEASE_FOR_DOWNLOAD
+ LL_WARNS() << "LLViewerPartSim::put Part out of range!" << LL_ENDL;
+ LL_WARNS() << part->mPosAgent << LL_ENDL;
+#endif
+ }
+ else
+ {
+ LLViewerCamera* camera = LLViewerCamera::getInstance();
+ F32 desired_size = calc_desired_size(camera, part->mPosAgent, part->mScale);
+
+ S32 count = (S32) mViewerPartGroups.size();
+ for (S32 i = 0; 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 didn't 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))
+ {
+ LL_WARNS() << "LLViewerPartSim::put - Particle didn't go into its box!" << LL_ENDL;
+ LL_INFOS() << groupp->getCenterAgent() << LL_ENDL;
+ LL_INFOS() << part->mPosAgent << LL_ENDL;
+ mViewerPartGroups.pop_back() ;
+ delete groupp;
+ groupp = NULL ;
+ }
+ return_group = groupp;
+ }
+ }
+
+ if(!return_group) //failed to insert the particle
+ {
+ delete part ;
+ part = NULL ;
+ }
+
+ return return_group ;
+}
+
+LLViewerPartGroup *LLViewerPartSim::createViewerPartGroup(const LLVector3 &pos_agent, const 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)
+{
+ S32 i;
+ S32 count;
+
+ count = (S32) mViewerPartSources.size();
+ for (i = 0; i < count; i++)
+ {
+ mViewerPartSources[i]->mPosAgent += offset;
+ mViewerPartSources[i]->mTargetPosAgent += offset;
+ mViewerPartSources[i]->mLastUpdatePosAgent += offset;
+ }
+
+ count = (S32) mViewerPartGroups.size();
+ for (i = 0; i < count; i++)
+ {
+ mViewerPartGroups[i]->shift(offset);
+ }
+}
+
+static LLTrace::BlockTimerStatHandle FTM_SIMULATE_PARTICLES("Simulate Particles");
+
+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_RECORD_BLOCK_TIME(FTM_SIMULATE_PARTICLES);
+
+ // Start at a random particle system so the same
+ // particle system doesn't always get first pick at the
+ // particles. Theoretically we'd want to do this in distance
+ // order or something, but sorting particle sources will be a big
+ // pain.
+ S32 i;
+ S32 count = (S32) mViewerPartSources.size();
+ S32 start = (S32)ll_frand((F32)count);
+ S32 dir = 1;
+ S32 deldir = 0;
+ if (ll_frand() > 0.5f)
+ {
+ dir = -1;
+ deldir = -1;
+ }
+
+ S32 num_updates = 0;
+ for (i = start; num_updates < count;)
+ {
+ if (i >= count)
+ {
+ i = 0;
+ }
+ if (i < 0)
+ {
+ i = count - 1;
+ }
+
+ if (!mViewerPartSources[i]->isDead())
+ {
+ bool upd = true;
+ LLViewerObject* vobj = mViewerPartSources[i]->mSourceObjectp;
+
+ if (vobj && vobj->isAvatar() && ((LLVOAvatar*)vobj)->isInMuteList())
+ {
+ upd = false;
+ }
+
+ if(vobj && vobj->isOwnerInMuteList(mViewerPartSources[i]->getOwnerUUID()))
+ {
+ upd = false;
+ }
+
+ if (upd && vobj && (vobj->getPCode() == LL_PCODE_VOLUME))
+ {
+ if(vobj->getAvatar() && vobj->getAvatar()->isTooComplex() && vobj->getAvatar()->isTooSlow())
+ {
+ upd = false;
+ }
+
+ LLVOVolume* vvo = (LLVOVolume *)vobj;
+ if (!LLPipeline::sRenderAttachedParticles && vvo && vvo->isAttachment())
+ {
+ upd = false;
+ }
+ }
+
+ if (upd)
+ {
+ mViewerPartSources[i]->update(dt);
+ }
+ }
+
+ if (mViewerPartSources[i]->isDead())
+ {
+ mViewerPartSources.erase(mViewerPartSources.begin() + i);
+ count--;
+ i+=deldir;
+ }
+ else
+ {
+ i += dir;
+ }
+ num_updates++;
+ }
+
+ count = (S32) mViewerPartGroups.size();
+ for (i = 0; i < count; i++)
+ {
+ LLViewerObject* vobj = mViewerPartGroups[i]->mVOPartGroupp;
+
+ S32 visirate = 1;
+ if (vobj && !vobj->isDead() && vobj->mDrawable && !vobj->mDrawable->isDead())
+ {
+ LLSpatialGroup* group = vobj->mDrawable->getSpatialGroup();
+ if (group && !group->isVisible()) // && !group->isState(LLSpatialGroup::OBJECT_DIRTY))
+ {
+ visirate = 8;
+ }
+ }
+
+ if ((LLDrawable::getCurrentFrame()+mViewerPartGroups[i]->mID)%visirate == 0)
+ {
+ if (vobj && !vobj->isDead())
+ {
+ gPipeline.markRebuild(vobj->mDrawable, LLDrawable::REBUILD_ALL);
+ }
+ mViewerPartGroups[i]->updateParticles(dt * visirate);
+ mViewerPartGroups[i]->mSkippedTime=0.0f;
+ if (!mViewerPartGroups[i]->getCount())
+ {
+ delete mViewerPartGroups[i];
+ mViewerPartGroups.erase(mViewerPartGroups.begin() + i);
+ i--;
+ count--;
+ }
+ }
+ else
+ {
+ mViewerPartGroups[i]->mSkippedTime+=dt;
+ }
+
+ }
+ if (LLDrawable::getCurrentFrame()%16==0)
+ {
+ if (sParticleCount > sMaxParticleCount * 0.875f
+ && sParticleAdaptiveRate < 2.0f)
+ {
+ sParticleAdaptiveRate *= PART_ADAPT_RATE_MULT;
+ }
+ else
+ {
+ if (sParticleCount < sMaxParticleCount * 0.5f
+ && sParticleAdaptiveRate > 0.03125f)
+ {
+ sParticleAdaptiveRate *= PART_ADAPT_RATE_MULT_RECIP;
+ }
+ }
+ }
+
+ updatePartBurstRate() ;
+
+ //LL_INFOS() << "Particles: " << sParticleCount << " Adaptive Rate: " << sParticleAdaptiveRate << LL_ENDL;
+}
+
+void LLViewerPartSim::updatePartBurstRate()
+{
+ if (!(LLDrawable::getCurrentFrame() & 0xf))
+ {
+ if (sParticleCount >= MAX_PART_COUNT) //set rate to zero
+ {
+ sParticleBurstRate = 0.0f ;
+ }
+ else if(sParticleCount > 0)
+ {
+ if(sParticleBurstRate > 0.0000001f)
+ {
+ F32 total_particles = sParticleCount / sParticleBurstRate ; //estimated
+ F32 new_rate = llclamp(0.9f * sMaxParticleCount / total_particles, 0.0f, 1.0f) ;
+ F32 delta_rate_threshold = llmin(0.1f * llmax(new_rate, sParticleBurstRate), 0.1f) ;
+ F32 delta_rate = llclamp(new_rate - sParticleBurstRate, -1.0f * delta_rate_threshold, delta_rate_threshold) ;
+
+ sParticleBurstRate = llclamp(sParticleBurstRate + 0.5f * delta_rate, 0.0f, 1.0f) ;
+ }
+ else
+ {
+ sParticleBurstRate += 0.0000001f ;
+ }
+ }
+ else
+ {
+ sParticleBurstRate += 0.00125f ;
+ }
+ }
+}
+
+void LLViewerPartSim::addPartSource(LLPointer<LLViewerPartSource> sourcep)
+{
+ if (!sourcep)
+ {
+ LL_WARNS() << "Null part source!" << LL_ENDL;
+ return;
+ }
+ sourcep->setStart() ;
+ mViewerPartSources.push_back(sourcep);
+}
+
+void LLViewerPartSim::removeLastCreatedSource()
+{
+ mViewerPartSources.pop_back();
+}
+
+void LLViewerPartSim::cleanupRegion(LLViewerRegion *regionp)
+{
+ for (group_list_t::iterator i = mViewerPartGroups.begin(); i != mViewerPartGroups.end(); )
+ {
+ group_list_t::iterator iter = i++;
+
+ if ((*iter)->getRegion() == regionp)
+ {
+ delete *iter;
+ i = mViewerPartGroups.erase(iter);
+ }
+ }
+}
+
+void LLViewerPartSim::clearParticlesByID(const U32 system_id)
+{
+ for (group_list_t::iterator g = mViewerPartGroups.begin(); g != mViewerPartGroups.end(); ++g)
+ {
+ (*g)->removeParticlesByID(system_id);
+ }
+ for (source_list_t::iterator i = mViewerPartSources.begin(); i != mViewerPartSources.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(); iter != mViewerPartSources.end(); ++iter)
+ {
+ if ((*iter)->getOwnerUUID() == task_id)
+ {
+ clearParticlesByID((*iter)->getID());
+ }
+ }
+}
|