/** * @file llvopartgroup.cpp * @brief Group of particle systems * * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc. * $License$ */ #include "llviewerprecompiledheaders.h" #include "llvopartgroup.h" #include "llfasttimer.h" #include "message.h" #include "v2math.h" #include "llagent.h" #include "lldrawable.h" #include "llface.h" #include "llsky.h" #include "llviewercamera.h" #include "llviewerpartsim.h" #include "llviewerregion.h" #include "pipeline.h" const F32 MAX_PART_LIFETIME = 120.f; extern U64 gFrameTime; LLVOPartGroup::LLVOPartGroup(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp) : LLViewerObject(id, pcode, regionp), mViewerPartGroupp(NULL) { setNumTEs(1); setTETexture(0, LLUUID::null); mbCanSelect = FALSE; // users can't select particle systems mDebugColor = LLColor4(frand(1.f), frand(1.f), frand(1.f), 1.f); } LLVOPartGroup::~LLVOPartGroup() { } BOOL LLVOPartGroup::isActive() const { return TRUE; } BOOL LLVOPartGroup::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) { if (gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_PARTICLES)) { gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE); } // Particle groups don't need any of default idleUpdate velocity interpolation stuff. //LLViewerObject::idleUpdate(agent, world, time); return TRUE; } void LLVOPartGroup::setPixelAreaAndAngle(LLAgent &agent) { // mPixelArea is calculated during render LLVector3 viewer_pos_agent = agent.getCameraPositionAgent(); LLVector3 pos_agent = getRenderPosition(); F32 dx = viewer_pos_agent.mV[VX] - pos_agent.mV[VX]; F32 dy = viewer_pos_agent.mV[VY] - pos_agent.mV[VY]; F32 dz = viewer_pos_agent.mV[VZ] - pos_agent.mV[VZ]; F32 mid_scale = getMidScale(); F32 range = sqrt(dx*dx + dy*dy + dz*dz); if (range < 0.001f || isHUDAttachment()) // range == zero { mAppAngle = 180.f; } else { mAppAngle = (F32) atan2( mid_scale, range) * RAD_TO_DEG; } } void LLVOPartGroup::updateTextures(LLAgent &agent) { // Texture stats for particles will need to be updated in a different way... } LLDrawable* LLVOPartGroup::createDrawable(LLPipeline *pipeline) { pipeline->allocDrawable(this); mDrawable->setLit(FALSE); mDrawable->setRenderType(LLPipeline::RENDER_TYPE_PARTICLES); LLDrawPool *pool = gPipeline.getPool(LLDrawPool::POOL_ALPHA); mDrawable->setNumFaces(mViewerPartGroupp->getCount(), pool, getTEImage(0)); return mDrawable; } const F32 MAX_PARTICLE_AREA_SCALE = 0.02f; // some tuned constant, limits on how much particle area to draw BOOL LLVOPartGroup::updateGeometry(LLDrawable *drawable) { LLFastTimer t(LLFastTimer::FTM_UPDATE_PARTICLES); LLVector3 at; LLVector3 up; LLVector3 right; LLVector3 position_agent; LLVector3 camera_agent = gAgent.getCameraPositionAgent(); LLVector2 uvs[4]; uvs[0].setVec(0.f, 1.f); uvs[1].setVec(0.f, 0.f); uvs[2].setVec(1.f, 1.f); uvs[3].setVec(1.f, 0.f); LLDrawPool *drawpool = gPipeline.getPool(LLDrawPool::POOL_ALPHA); S32 num_parts = mViewerPartGroupp->getCount(); LLFace *facep; if (!num_parts) { drawable->setNumFaces(0, drawpool, getTEImage(0)); LLPipeline::sCompiles++; return TRUE; } if (!(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_PARTICLES))) { return TRUE; } // drawable->setNumFaces(num_parts, drawpool, getTEImage(0)); drawable->setNumFacesFast(num_parts, drawpool, getTEImage(0)); LLVector3 normal(-gCamera->getXAxis()); // make point agent face camera LLStrider verticesp; LLStrider normalsp; LLStrider texCoordsp; U32 *indicesp = 0; S32 vert_offset; F32 tot_area = 0; F32 max_area = LLViewerPartSim::getMaxPartCount() * MAX_PARTICLE_AREA_SCALE; S32 count=0; for (S32 i = 0; i < num_parts; i++) { const LLViewerPart &part = mViewerPartGroupp->mParticles[i]; LLVector3 part_pos_agent(part.mPosAgent); at = part_pos_agent - camera_agent; //F32 invcamdist = 1.0f / at.magVec(); //area += (part.mScale.mV[0]*invcamdist)*(part.mScale.mV[1]*invcamdist); F32 camera_dist_squared = at.magVecSquared(); F32 inv_camera_dist_squared; if (camera_dist_squared > 1.f) inv_camera_dist_squared = 1.f / camera_dist_squared; else inv_camera_dist_squared = 1.f; F32 area = part.mScale.mV[0] * part.mScale.mV[1] * inv_camera_dist_squared; tot_area += area; if (tot_area > max_area) { break; } count++; facep = drawable->getFace(i); const F32 NEAR_PART_DIST_SQ = 5.f*5.f; // Only discard particles > 5 m from the camera const F32 MIN_PART_AREA = .005f*.005f; // only less than 5 mm x 5 mm at 1 m from camera if (camera_dist_squared > NEAR_PART_DIST_SQ && area < MIN_PART_AREA) { facep->setSize(0, 0); continue; } facep->setSize(4, 6); facep->mCenterAgent = part_pos_agent; vert_offset = facep->getGeometry(verticesp,normalsp,texCoordsp, indicesp); if (-1 == vert_offset) { return TRUE; } right = at % LLVector3(0.f, 0.f, 1.f); right.normVec(); up = right % at; up.normVec(); if (part.mFlags & LLPartData::LL_PART_FOLLOW_VELOCITY_MASK) { LLVector3 normvel = part.mVelocity; normvel.normVec(); LLVector2 up_fracs; up_fracs.mV[0] = normvel*right; up_fracs.mV[1] = normvel*up; up_fracs.normVec(); LLVector3 new_up; LLVector3 new_right; new_up = up_fracs.mV[0] * right + up_fracs.mV[1]*up; new_right = up_fracs.mV[1] * right - up_fracs.mV[0]*up; up = new_up; right = new_right; up.normVec(); right.normVec(); } right *= 0.5f*part.mScale.mV[0]; up *= 0.5f*part.mScale.mV[1]; *verticesp++ = part_pos_agent + up - right; *verticesp++ = part_pos_agent - up - right; *verticesp++ = part_pos_agent + up + right; *verticesp++ = part_pos_agent - up + right; *texCoordsp++ = uvs[0]; *texCoordsp++ = uvs[1]; *texCoordsp++ = uvs[2]; *texCoordsp++ = uvs[3]; *normalsp++ = normal; *normalsp++ = normal; *normalsp++ = normal; *normalsp++ = normal; if (part.mFlags & LLPartData::LL_PART_EMISSIVE_MASK) { facep->setState(LLFace::FULLBRIGHT); } else { facep->clearState(LLFace::FULLBRIGHT); } facep->setFaceColor(part.mColor); *indicesp++ = vert_offset + 0; *indicesp++ = vert_offset + 1; *indicesp++ = vert_offset + 2; *indicesp++ = vert_offset + 1; *indicesp++ = vert_offset + 3; *indicesp++ = vert_offset + 2; } for (S32 j = count; j < drawable->getNumFaces(); j++) { drawable->getFace(j)->setSize(0,0); } mPixelArea = tot_area * gCamera->getPixelMeterRatio() * gCamera->getPixelMeterRatio(); const F32 area_scale = 10.f; // scale area to increase priority a bit getTEImage(0)->addTextureStats(mPixelArea * area_scale); LLPipeline::sCompiles++; return TRUE; }