diff options
author | James Cook <james@lindenlab.com> | 2007-01-02 08:33:20 +0000 |
---|---|---|
committer | James Cook <james@lindenlab.com> | 2007-01-02 08:33:20 +0000 |
commit | 420b91db29485df39fd6e724e782c449158811cb (patch) | |
tree | b471a94563af914d3ed3edd3e856d21cb1b69945 /indra/newview/llviewerpartsim.cpp |
Print done when done.
Diffstat (limited to 'indra/newview/llviewerpartsim.cpp')
-rw-r--r-- | indra/newview/llviewerpartsim.cpp | 626 |
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 ¢er_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--; + } + } +} |