891 lines
22 KiB
C++
891 lines
22 KiB
C++
/**
|
|
* @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 "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 "pipeline.h"
|
|
#include "llspatialpartition.h"
|
|
#include "llvovolume.h"
|
|
|
|
const F32 PART_SIM_BOX_SIDE = 16.f;
|
|
const F32 PART_SIM_BOX_OFFSET = 0.5f*PART_SIM_BOX_SIDE;
|
|
const F32 PART_SIM_BOX_RAD = 0.5f*F_SQRT3*PART_SIM_BOX_SIDE;
|
|
|
|
//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)
|
|
{
|
|
//llwarns << "No region at position, using agent region!" << llendl;
|
|
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->mOctreeNode->getCenter().getF32ptr());
|
|
LLVector3 size(group->mOctreeNode->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, TRUE);
|
|
|
|
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)
|
|
{
|
|
LLVector3 tempVel(part->mVelocity);
|
|
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) llround(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, TRUE);
|
|
}
|
|
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)
|
|
{
|
|
llerrs << "sParticleCount: " << LLViewerPartSim::sParticleCount << " ; sParticleCount2: " << LLViewerPartSim::sParticleCount2 << llendl ;
|
|
}
|
|
|
|
if(size > (U32)LLViewerPartSim::sParticleCount2)
|
|
{
|
|
llerrs << "curren particle size: " << LLViewerPartSim::sParticleCount2 << " array size: " << size << llendl ;
|
|
}
|
|
}
|
|
|
|
LLViewerPartSim::LLViewerPartSim()
|
|
{
|
|
sMaxParticleCount = llmin(gSavedSettings.getS32("RenderMaxPartCount"), LL_MAX_PARTICLE_COUNT);
|
|
static U32 id_seed = 0;
|
|
mID = ++id_seed;
|
|
}
|
|
|
|
|
|
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
|
|
llwarns << "LLViewerPartSim::put Part out of range!" << llendl;
|
|
llwarns << part->mPosAgent << llendl;
|
|
#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))
|
|
{
|
|
llwarns << "LLViewerPartSim::put - Particle didn't go into its box!" << llendl;
|
|
llinfos << groupp->getCenterAgent() << llendl;
|
|
llinfos << part->mPosAgent << llendl;
|
|
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 LLFastTimer::DeclareTimer FTM_SIMULATE_PARTICLES("Simulate Particles");
|
|
|
|
void LLViewerPartSim::updateSimulation()
|
|
{
|
|
static LLFrameTimer update_timer;
|
|
|
|
//reset VBO cursor
|
|
|
|
const F32 dt = llmin(update_timer.getElapsedTimeAndResetF32(), 0.1f);
|
|
|
|
if (!(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_PARTICLES)))
|
|
{
|
|
return;
|
|
}
|
|
|
|
LLFastTimer ftm(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;
|
|
//if (!LLPipeline::sRenderAttachedParticles)
|
|
{
|
|
LLViewerObject* vobj = mViewerPartSources[i]->mSourceObjectp;
|
|
if (vobj && (vobj->getPCode() == LL_PCODE_VOLUME))
|
|
{
|
|
LLVOVolume* vvo = (LLVOVolume *)vobj;
|
|
if (vvo && vvo->isAttachment())
|
|
{
|
|
if (!LLPipeline::sRenderAttachedParticles)
|
|
upd = FALSE;
|
|
else
|
|
{
|
|
LLVOAvatar *avatar = vvo->getAvatar();
|
|
if(avatar && avatar->isVisuallyMuted())
|
|
upd = FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (upd)
|
|
{
|
|
mViewerPartSources[i]->update(dt);
|
|
}
|
|
}
|
|
|
|
if (mViewerPartSources[i]->isDead())
|
|
{
|
|
mViewerPartSources[i] = mViewerPartSources.back();
|
|
mViewerPartSources.pop_back();
|
|
//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->mDrawable.notNull())
|
|
{
|
|
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->mDrawable.notNull())
|
|
{
|
|
gPipeline.markRebuild(vobj->mDrawable, LLDrawable::REBUILD_ALL, TRUE);
|
|
}
|
|
mViewerPartGroups[i]->updateParticles(dt * visirate);
|
|
mViewerPartGroups[i]->mSkippedTime=0.0f;
|
|
if (!mViewerPartGroups[i]->getCount())
|
|
{
|
|
delete mViewerPartGroups[i];
|
|
mViewerPartGroups[i] = mViewerPartGroups.back();
|
|
mViewerPartGroups.pop_back();
|
|
//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() ;
|
|
|
|
//llinfos << "Particles: " << sParticleCount << " Adaptive Rate: " << sParticleAdaptiveRate << llendl;
|
|
}
|
|
|
|
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)
|
|
{
|
|
llwarns << "Null part source!" << llendl;
|
|
return;
|
|
}
|
|
sourcep->setStart() ;
|
|
mViewerPartSources.push_back(sourcep);
|
|
}
|
|
|
|
void LLViewerPartSim::removeLastCreatedSource()
|
|
{
|
|
mViewerPartSources.pop_back();
|
|
}
|
|
|
|
void LLViewerPartSim::cleanupRegion(LLViewerRegion *regionp)
|
|
{
|
|
group_list_t& vec = mViewerPartGroups;
|
|
for (group_list_t::size_type i = 0;i<vec.size();++i)
|
|
{
|
|
if (vec[i]->getRegion() == regionp)
|
|
{
|
|
delete vec[i];
|
|
vec[i--] = vec.back();
|
|
vec.pop_back();
|
|
//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());
|
|
}
|
|
}
|
|
}
|