summaryrefslogtreecommitdiff
path: root/indra/newview/llviewerpartsim.cpp
diff options
context:
space:
mode:
authorJames Cook <james@lindenlab.com>2007-01-02 08:33:20 +0000
committerJames Cook <james@lindenlab.com>2007-01-02 08:33:20 +0000
commit420b91db29485df39fd6e724e782c449158811cb (patch)
treeb471a94563af914d3ed3edd3e856d21cb1b69945 /indra/newview/llviewerpartsim.cpp
Print done when done.
Diffstat (limited to 'indra/newview/llviewerpartsim.cpp')
-rw-r--r--indra/newview/llviewerpartsim.cpp626
1 files changed, 626 insertions, 0 deletions
diff --git a/indra/newview/llviewerpartsim.cpp b/indra/newview/llviewerpartsim.cpp
new file mode 100644
index 0000000000..b67062e0a9
--- /dev/null
+++ b/indra/newview/llviewerpartsim.cpp
@@ -0,0 +1,626 @@
+/**
+ * @file llviewerpartsim.cpp
+ * @brief LLViewerPart class implementation
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llviewerpartsim.h"
+
+#include "llviewercontrol.h"
+
+#include "llagent.h"
+#include "llviewerobjectlist.h"
+#include "llviewerpartsource.h"
+#include "llviewerregion.h"
+#include "llvopartgroup.h"
+#include "llworld.h"
+#include "pipeline.h"
+
+const S32 MAX_PART_COUNT = 4096;
+
+const F32 PART_SIM_BOX_SIDE = 32.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;
+
+
+U32 LLViewerPart::sNextPartID = 1;
+
+LLViewerPart::LLViewerPart()
+{
+ mPartSourcep = NULL;
+}
+
+LLViewerPart::~LLViewerPart()
+{
+ mPartSourcep = NULL;
+}
+
+LLViewerPart &LLViewerPart::operator=(const LLViewerPart &part)
+{
+ mPartID = part.mPartID;
+ mFlags = part.mFlags;
+ mMaxAge = part.mMaxAge;
+
+ mStartColor = part.mStartColor;
+ mEndColor = part.mEndColor;
+ mStartScale = part.mStartScale;
+ mEndScale = part.mEndScale;
+
+ mPosOffset = part.mPosOffset;
+ mParameter = part.mParameter;
+
+ mLastUpdateTime = part.mLastUpdateTime;
+ mVPCallback = part.mVPCallback;
+ mPartSourcep = part.mPartSourcep;
+
+ mImagep = part.mImagep;
+ mPosAgent = part.mPosAgent;
+ mVelocity = part.mVelocity;
+ mAccel = part.mAccel;
+ mColor = part.mColor;
+ mScale = part.mScale;
+
+
+ return *this;
+}
+
+void LLViewerPart::init(LLViewerPartSource *sourcep, LLViewerImage *imagep, LLVPCallback cb)
+{
+ mPartID = LLViewerPart::sNextPartID;
+ LLViewerPart::sNextPartID++;
+ mFlags = 0x00f;
+ mLastUpdateTime = 0.f;
+ mMaxAge = 10.f;
+
+ mVPCallback = cb;
+ mPartSourcep = sourcep;
+
+ mImagep = imagep;
+}
+
+
+/////////////////////////////
+//
+// LLViewerPartGroup implementation
+//
+//
+
+
+LLViewerPartGroup::LLViewerPartGroup(const LLVector3 &center_agent, const F32 box_side)
+{
+ mVOPartGroupp = NULL;
+ mRegionp = gWorldPointer->getRegionFromPosAgent(center_agent);
+ if (!mRegionp)
+ {
+ //llwarns << "No region at position, using agent region!" << llendl;
+ mRegionp = gAgent.getRegion();
+ }
+ mCenterAgent = center_agent;
+ mBoxRadius = F_SQRT3*box_side*0.5f;
+
+ LLVector3 rad_vec(box_side*0.5f, box_side*0.5f, box_side*0.5f);
+ rad_vec += LLVector3(0.001f, 0.001f, 0.001f);
+ mMinObjPos = mCenterAgent - rad_vec;
+ mMaxObjPos = mCenterAgent + rad_vec;
+}
+
+LLViewerPartGroup::~LLViewerPartGroup()
+{
+ cleanup();
+ S32 count = mParticles.count();
+ S32 i;
+
+ for (i = 0; i < count; i++)
+ {
+ mParticles[i].mPartSourcep = NULL;
+ }
+ mParticles.reset();
+ LLViewerPartSim::decPartCount(count);
+}
+
+void LLViewerPartGroup::cleanup()
+{
+ if (mVOPartGroupp)
+ {
+ if (!mVOPartGroupp->isDead())
+ {
+ gObjectList.killObject(mVOPartGroupp);
+ }
+ mVOPartGroupp = NULL;
+ }
+}
+
+BOOL LLViewerPartGroup::posInGroup(const LLVector3 &pos)
+{
+ 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;
+ }
+
+ return TRUE;
+}
+
+
+BOOL LLViewerPartGroup::addPart(LLViewerPart &part)
+{
+ if (!posInGroup(part.mPosAgent) ||
+ (mVOPartGroupp.notNull() && (part.mImagep != mVOPartGroupp->getTEImage(0))))
+ {
+ return FALSE;
+ }
+
+ if (!mVOPartGroupp)
+ {
+ mVOPartGroupp = (LLVOPartGroup *)gObjectList.createObjectViewer(LLViewerObject::LL_VO_PART_GROUP, getRegion());
+ mVOPartGroupp->setViewerPartGroup(this);
+ mVOPartGroupp->setPositionAgent(getCenterAgent());
+ mVOPartGroupp->setScale(LLVector3(PART_SIM_BOX_SIDE, PART_SIM_BOX_SIDE, PART_SIM_BOX_SIDE));
+ mVOPartGroupp->setTEImage(0, part.mImagep);
+ gPipeline.addObject(mVOPartGroupp);
+ }
+
+ mParticles.put(part);
+ LLViewerPartSim::incPartCount(1);
+ return TRUE;
+}
+
+
+void LLViewerPartGroup::removePart(const S32 part_num)
+{
+ // Remove the entry for the particle we just deleted.
+ LLPointer<LLViewerPartSource> ps = mParticles[mParticles.count() - 1].mPartSourcep;
+
+ mParticles[mParticles.count() - 1].mPartSourcep = NULL;
+ mParticles.remove(part_num);
+ if (part_num < mParticles.count())
+ {
+ mParticles[part_num].mPartSourcep = ps;
+ }
+
+ LLViewerPartSim::decPartCount(1);
+}
+
+
+void LLViewerPartGroup::updateParticles(const F32 dt)
+{
+ S32 i, count;
+
+
+ LLVector3 gravity(0.f, 0.f, -9.8f);
+
+ LLViewerRegion *regionp = getRegion();
+ count = mParticles.count();
+ for (i = 0; i < count; i++)
+ {
+ LLVector3 a(0.f, 0.f, 0.f);
+ LLViewerPart &part = mParticles[i];
+
+ // 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;
+ //part.mPosAgent *= 1.f - to_target_frac;
+ //part.mPosAgent += to_target_frac*part.mPartSourcep->mTargetPosAgent;
+ }
+
+
+ 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);
+ part.mColor *= 1.f - frac;
+ part.mColor.mV[3] *= (1.f - frac)*part.mStartColor.mV[3];
+ part.mColor += frac*part.mEndColor;
+ part.mColor.mV[3] += frac*part.mEndColor.mV[3];
+ }
+
+ // 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;
+ }
+
+ // 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))
+ {
+ removePart(i);
+ i--;
+ count--;
+ }
+ else if (!posInGroup(part.mPosAgent))
+ {
+ // Transfer particles between groups
+ gWorldPointer->mPartSim.put(part);
+ removePart(i);
+ i--;
+ count--;
+ }
+ }
+
+ // Kill the viewer object if this particle group is empty
+ if (!mParticles.count())
+ {
+ gObjectList.killObject(mVOPartGroupp);
+ mVOPartGroupp = NULL;
+ }
+}
+
+
+void LLViewerPartGroup::shift(const LLVector3 &offset)
+{
+ mCenterAgent += offset;
+ mMinObjPos += offset;
+ mMaxObjPos += offset;
+
+ S32 count = mParticles.count();
+ S32 i;
+ for (i = 0; i < count; i++)
+ {
+ mParticles[i].mPosAgent += offset;
+ }
+}
+
+
+//////////////////////////////////
+//
+// LLViewerPartSim implementation
+//
+//
+
+
+LLViewerPartSim::LLViewerPartSim()
+{
+ sMaxParticleCount = gSavedSettings.getS32("RenderMaxPartCount");
+}
+
+
+LLViewerPartSim::~LLViewerPartSim()
+{
+ S32 i;
+ S32 count;
+
+ // Kill all of the groups (and particles)
+ count = mViewerPartGroups.count();
+ for (i = 0; i < count; i++)
+ {
+ delete mViewerPartGroups[i];
+ }
+ mViewerPartGroups.reset();
+
+ // Kill all of the sources
+ count = mViewerPartSources.count();
+ for (i = 0; i < count; i++)
+ {
+ mViewerPartSources[i] = NULL;
+ }
+ mViewerPartSources.reset();
+}
+
+BOOL LLViewerPartSim::shouldAddPart()
+{
+ if (sParticleCount > 0.75f*sMaxParticleCount)
+ {
+
+ F32 frac = (F32)sParticleCount/(F32)sMaxParticleCount;
+ frac -= 0.75;
+ frac *= 3.f;
+ if (frand(1.f) < frac)
+ {
+ // Skip...
+ return FALSE;
+ }
+ }
+ if (sParticleCount >= MAX_PART_COUNT)
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+void LLViewerPartSim::addPart(LLViewerPart &part)
+{
+ if (sParticleCount < MAX_PART_COUNT)
+ {
+ put(part);
+ }
+}
+
+LLViewerPartGroup *LLViewerPartSim::put(LLViewerPart &part)
+{
+ const F32 MAX_MAG = 1000000.f*1000000.f; // 1 million
+ if (part.mPosAgent.magVecSquared() > MAX_MAG)
+ {
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ llwarns << "LLViewerPartSim::put Part out of range!" << llendl;
+ llwarns << part.mPosAgent << llendl;
+#endif
+ return NULL;
+ }
+
+ S32 i;
+ S32 count;
+
+ count = mViewerPartGroups.count();
+ for (i = 0; i < count; i++)
+ {
+ if (mViewerPartGroups[i]->addPart(part))
+ {
+ // We found a spatial group that we fit into, add us and exit
+ return mViewerPartGroups[i];
+ }
+ }
+
+ // Hmm, we didn't fit in any of the existing spatial groups
+ // Create a new one...
+ LLViewerPartGroup *groupp = createViewerPartGroup(part.mPosAgent);
+ if (!groupp->addPart(part))
+ {
+ llwarns << "LLViewerPartSim::put - Particle didn't go into its box!" << llendl;
+ llinfos << groupp->getCenterAgent() << llendl;
+ llinfos << part.mPosAgent << llendl;
+ return NULL;
+ }
+ return groupp;
+}
+
+LLViewerPartGroup *LLViewerPartSim::createViewerPartGroup(const LLVector3 &pos_agent)
+{
+ F32 x_origin = ((S32)(pos_agent.mV[VX]/PART_SIM_BOX_SIDE))*PART_SIM_BOX_SIDE;
+ if (x_origin > pos_agent.mV[VX])
+ {
+ x_origin -= PART_SIM_BOX_SIDE;
+ }
+
+ F32 y_origin = ((S32)(pos_agent.mV[VY]/PART_SIM_BOX_SIDE))*PART_SIM_BOX_SIDE;
+ if (y_origin > pos_agent.mV[VY])
+ {
+ y_origin -= PART_SIM_BOX_SIDE;
+ }
+
+ F32 z_origin = ((S32)(pos_agent.mV[VZ]/PART_SIM_BOX_SIDE))*PART_SIM_BOX_SIDE;
+ if (z_origin > pos_agent.mV[VZ])
+ {
+ z_origin -= PART_SIM_BOX_SIDE;
+ }
+
+ LLVector3 group_center(x_origin + PART_SIM_BOX_OFFSET,
+ y_origin + PART_SIM_BOX_OFFSET,
+ z_origin + PART_SIM_BOX_OFFSET);
+
+ LLViewerPartGroup *groupp = new LLViewerPartGroup(group_center, PART_SIM_BOX_SIDE);
+ mViewerPartGroups.put(groupp);
+ return groupp;
+}
+
+
+void LLViewerPartSim::shift(const LLVector3 &offset)
+{
+ S32 i;
+ S32 count;
+
+ count = mViewerPartSources.count();
+ for (i = 0; i < count; i++)
+ {
+ mViewerPartSources[i]->mPosAgent += offset;
+ mViewerPartSources[i]->mTargetPosAgent += offset;
+ mViewerPartSources[i]->mLastUpdatePosAgent += offset;
+ }
+
+ count = mViewerPartGroups.count();
+ for (i = 0; i < count; i++)
+ {
+ mViewerPartGroups[i]->shift(offset);
+ }
+}
+
+
+void LLViewerPartSim::updateSimulation()
+{
+ LLMemType mt(LLMemType::MTYPE_PARTICLES);
+
+ static LLFrameTimer update_timer;
+
+ const F32 dt = update_timer.getElapsedTimeAndResetF32();
+
+ if (!(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_PARTICLES)))
+ {
+ return;
+ }
+
+ // 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 = mViewerPartSources.count();
+ S32 start = (S32)frand((F32)count);
+ S32 dir = 1;
+ if (frand(1.0) > 0.5f)
+ {
+ dir = -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())
+ {
+ mViewerPartSources[i]->update(dt);
+ }
+
+ if (mViewerPartSources[i]->isDead())
+ {
+ mViewerPartSources.remove(i);
+ count--;
+ }
+ else
+ {
+ i += dir;
+ }
+ num_updates++;
+ }
+
+
+ count = mViewerPartGroups.count();
+ for (i = 0; i < count; i++)
+ {
+ mViewerPartGroups[i]->updateParticles(dt);
+ if (!mViewerPartGroups[i]->getCount())
+ {
+ delete mViewerPartGroups[i];
+ mViewerPartGroups.remove(i);
+ i--;
+ count--;
+ }
+ }
+ //llinfos << "Particles: " << sParticleCount << llendl;
+}
+
+
+void LLViewerPartSim::addPartSource(LLViewerPartSource *sourcep)
+{
+ if (!sourcep)
+ {
+ llwarns << "Null part source!" << llendl;
+ return;
+ }
+ mViewerPartSources.put(sourcep);
+}
+
+
+void LLViewerPartSim::cleanupRegion(LLViewerRegion *regionp)
+{
+ S32 i, count;
+ count = mViewerPartGroups.count();
+ for (i = 0; i < count; i++)
+ {
+ if (mViewerPartGroups[i]->getRegion() == regionp)
+ {
+ delete mViewerPartGroups[i];
+ mViewerPartGroups.remove(i);
+ i--;
+ count--;
+ }
+ }
+}
+
+void LLViewerPartSim::cleanMutedParticles(const LLUUID& task_id)
+{
+ S32 i;
+ S32 count = mViewerPartSources.count();
+ for (i = 0; i < count; ++i)
+ {
+ if (mViewerPartSources[i]->getOwnerUUID() == task_id)
+ {
+ mViewerPartSources.remove(i);
+ i--;
+ count--;
+ }
+ }
+}