1024 lines
24 KiB
C++
1024 lines
24 KiB
C++
/**
|
|
* @file llvopartgroup.cpp
|
|
* @brief Group of particle systems
|
|
*
|
|
* $LicenseInfo:firstyear=2001&license=viewergpl$
|
|
*
|
|
* Copyright (c) 2001-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 "llvopartgroup.h"
|
|
|
|
#include "lldrawpoolalpha.h"
|
|
|
|
#include "llfasttimer.h"
|
|
#include "message.h"
|
|
#include "v2math.h"
|
|
|
|
#include "llagentcamera.h"
|
|
#include "lldrawable.h"
|
|
#include "llface.h"
|
|
#include "llsky.h"
|
|
#include "llviewercamera.h"
|
|
#include "llviewerpartsim.h"
|
|
#include "llviewerregion.h"
|
|
#include "pipeline.h"
|
|
#include "llspatialpartition.h"
|
|
#include "llglslshader.h"
|
|
|
|
const F32 MAX_PART_LIFETIME = 120.f;
|
|
|
|
extern U64 gFrameTime;
|
|
|
|
LLPointer<LLVertexBuffer> LLVOPartGroup::sVB = NULL;
|
|
S32 LLVOPartGroup::sVBSlotFree[];
|
|
S32* LLVOPartGroup::sVBSlotCursor = NULL;
|
|
//S32 LLVOPartGroup::sVBSlotCursor = 0;
|
|
|
|
void LLVOPartGroup::initClass()
|
|
{
|
|
for (S32 i = 0; i < LL_MAX_PARTICLE_COUNT; ++i)
|
|
{
|
|
sVBSlotFree[i] = i;
|
|
}
|
|
|
|
sVBSlotCursor = sVBSlotFree;
|
|
}
|
|
|
|
//static
|
|
void LLVOPartGroup::restoreGL()
|
|
{
|
|
|
|
//TODO: optimize out binormal mask here. Specular and normal coords as well.
|
|
sVB = new LLVertexBuffer(VERTEX_DATA_MASK | LLVertexBuffer::MAP_TANGENT | LLVertexBuffer::MAP_TEXCOORD1 | LLVertexBuffer::MAP_TEXCOORD2, GL_STREAM_DRAW_ARB);
|
|
U32 count = LL_MAX_PARTICLE_COUNT;
|
|
sVB->allocateBuffer(count*4, count*6, true);
|
|
|
|
//indices and texcoords are always the same, set once
|
|
LLStrider<U16> indicesp;
|
|
|
|
LLStrider<LLVector4a> verticesp;
|
|
|
|
sVB->getIndexStrider(indicesp);
|
|
sVB->getVertexStrider(verticesp);
|
|
|
|
LLVector4a v;
|
|
v.set(0,0,0,0);
|
|
|
|
|
|
U16 vert_offset = 0;
|
|
|
|
for (U32 i = 0; i < LL_MAX_PARTICLE_COUNT; i++)
|
|
{
|
|
*indicesp++ = vert_offset + 0;
|
|
*indicesp++ = vert_offset + 1;
|
|
*indicesp++ = vert_offset + 2;
|
|
|
|
*indicesp++ = vert_offset + 1;
|
|
*indicesp++ = vert_offset + 3;
|
|
*indicesp++ = vert_offset + 2;
|
|
|
|
*verticesp++ = v;
|
|
|
|
vert_offset += 4;
|
|
}
|
|
|
|
LLStrider<LLVector2> texcoordsp;
|
|
sVB->getTexCoord0Strider(texcoordsp);
|
|
|
|
for (U32 i = 0; i < LL_MAX_PARTICLE_COUNT; i++)
|
|
{
|
|
*texcoordsp++ = LLVector2(0.f, 1.f);
|
|
*texcoordsp++ = LLVector2(0.f, 0.f);
|
|
*texcoordsp++ = LLVector2(1.f, 1.f);
|
|
*texcoordsp++ = LLVector2(1.f, 0.f);
|
|
}
|
|
|
|
sVB->flush();
|
|
|
|
}
|
|
|
|
//static
|
|
void LLVOPartGroup::destroyGL()
|
|
{
|
|
sVB = NULL;
|
|
}
|
|
|
|
//static
|
|
S32 LLVOPartGroup::findAvailableVBSlot()
|
|
{
|
|
if (sVBSlotCursor >= sVBSlotFree+LL_MAX_PARTICLE_COUNT)
|
|
{ //no more available slots
|
|
return -1;
|
|
}
|
|
|
|
S32 ret = *sVBSlotCursor;
|
|
sVBSlotCursor++;
|
|
|
|
return ret;
|
|
|
|
//return sVBSlotCursor++;
|
|
}
|
|
|
|
bool ll_is_part_idx_allocated(S32 idx, S32* start, S32* end)
|
|
{
|
|
while (start < end)
|
|
{
|
|
if (*start == idx)
|
|
{ //not allocated (in free list)
|
|
return false;
|
|
}
|
|
++start;
|
|
}
|
|
|
|
//allocated (not in free list)
|
|
return true;
|
|
//return false;
|
|
}
|
|
|
|
//static
|
|
void LLVOPartGroup::freeVBSlot(S32 idx)
|
|
{
|
|
llassert(idx < LL_MAX_PARTICLE_COUNT && idx >= 0);
|
|
llassert(sVBSlotCursor > sVBSlotFree);
|
|
llassert(ll_is_part_idx_allocated(idx, sVBSlotCursor, sVBSlotFree+LL_MAX_PARTICLE_COUNT));
|
|
|
|
if (sVBSlotCursor > sVBSlotFree)
|
|
{
|
|
sVBSlotCursor--;
|
|
*sVBSlotCursor = idx;
|
|
}
|
|
}
|
|
|
|
LLVOPartGroup::LLVOPartGroup(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp)
|
|
: LLAlphaObject(id, pcode, regionp),
|
|
mViewerPartGroupp(NULL)
|
|
{
|
|
setNumTEs(1);
|
|
setTETexture(0, LLUUID::null);
|
|
mbCanSelect = FALSE; // users can't select particle systems
|
|
}
|
|
|
|
|
|
LLVOPartGroup::~LLVOPartGroup()
|
|
{
|
|
}
|
|
|
|
BOOL LLVOPartGroup::isActive() const
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
F32 LLVOPartGroup::getBinRadius()
|
|
{
|
|
return mViewerPartGroupp->getBoxSide();
|
|
}
|
|
|
|
void LLVOPartGroup::updateSpatialExtents(LLVector4a& newMin, LLVector4a& newMax)
|
|
{
|
|
const LLVector3& pos_agent = getPositionAgent();
|
|
|
|
LLVector4a scale;
|
|
LLVector4a p;
|
|
|
|
p.load3(pos_agent.mV);
|
|
|
|
scale.splat(mScale.mV[0]+mViewerPartGroupp->getBoxSide()*0.5f);
|
|
|
|
newMin.setSub(p, scale);
|
|
newMax.setAdd(p,scale);
|
|
|
|
llassert(newMin.isFinite3());
|
|
llassert(newMax.isFinite3());
|
|
|
|
llassert(p.isFinite3());
|
|
mDrawable->setPositionGroup(p);
|
|
}
|
|
|
|
void LLVOPartGroup::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
|
|
{
|
|
}
|
|
|
|
void LLVOPartGroup::setPixelAreaAndAngle(LLAgent &agent)
|
|
{
|
|
// mPixelArea is calculated during render
|
|
F32 mid_scale = getMidScale();
|
|
F32 range = (getRenderPosition()-LLViewerCamera::getInstance()->getOrigin()).length();
|
|
|
|
if (range < 0.001f || isHUDAttachment()) // range == zero
|
|
{
|
|
mAppAngle = 180.f;
|
|
}
|
|
else
|
|
{
|
|
mAppAngle = (F32) atan2( mid_scale, range) * RAD_TO_DEG;
|
|
}
|
|
}
|
|
|
|
void LLVOPartGroup::updateTextures()
|
|
{
|
|
// Texture stats for particles 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);
|
|
return mDrawable;
|
|
}
|
|
|
|
const F32 MAX_PARTICLE_AREA_SCALE = 0.02f; // some tuned constant, limits on how much particle area to draw
|
|
|
|
LLUUID LLVOPartGroup::getPartOwner(S32 idx)
|
|
{
|
|
LLUUID ret = LLUUID::null;
|
|
|
|
if (idx < (S32) mViewerPartGroupp->mParticles.size())
|
|
{
|
|
ret = mViewerPartGroupp->mParticles[idx]->mPartSourcep->getOwnerUUID();
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
LLUUID LLVOPartGroup::getPartSource(S32 idx)
|
|
{
|
|
LLUUID ret = LLUUID::null;
|
|
|
|
if (idx < (S32) mViewerPartGroupp->mParticles.size())
|
|
{
|
|
LLViewerPart* part = mViewerPartGroupp->mParticles[idx];
|
|
if (part && part->mPartSourcep.notNull() &&
|
|
part->mPartSourcep->mSourceObjectp.notNull())
|
|
{
|
|
LLViewerObject* source = part->mPartSourcep->mSourceObjectp;
|
|
ret = source->getID();
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
F32 LLVOPartGroup::getPartSize(S32 idx)
|
|
{
|
|
if (idx < (S32) mViewerPartGroupp->mParticles.size())
|
|
{
|
|
return mViewerPartGroupp->mParticles[idx]->mScale.mV[0];
|
|
}
|
|
|
|
return 0.f;
|
|
}
|
|
|
|
void LLVOPartGroup::getBlendFunc(S32 idx, U32& src, U32& dst)
|
|
{
|
|
if (idx < (S32) mViewerPartGroupp->mParticles.size())
|
|
{
|
|
LLViewerPart* part = mViewerPartGroupp->mParticles[idx];
|
|
src = part->mBlendFuncSource;
|
|
dst = part->mBlendFuncDest;
|
|
}
|
|
}
|
|
|
|
LLVector3 LLVOPartGroup::getCameraPosition() const
|
|
{
|
|
return gAgentCamera.getCameraPositionAgent();
|
|
}
|
|
|
|
static LLFastTimer::DeclareTimer FTM_UPDATE_PARTICLES("Update Particles");
|
|
BOOL LLVOPartGroup::updateGeometry(LLDrawable *drawable)
|
|
{
|
|
LLFastTimer ftm(FTM_UPDATE_PARTICLES);
|
|
|
|
dirtySpatialGroup();
|
|
|
|
S32 num_parts = mViewerPartGroupp->getCount();
|
|
LLFace *facep;
|
|
LLSpatialGroup* group = drawable->getSpatialGroup();
|
|
if (!group && num_parts)
|
|
{
|
|
drawable->movePartition();
|
|
group = drawable->getSpatialGroup();
|
|
}
|
|
|
|
if (group && group->isVisible())
|
|
{
|
|
dirtySpatialGroup(TRUE);
|
|
}
|
|
|
|
if (!num_parts)
|
|
{
|
|
if (group && drawable->getNumFaces())
|
|
{
|
|
group->setState(LLSpatialGroup::GEOM_DIRTY);
|
|
}
|
|
drawable->setNumFaces(0, NULL, getTEImage(0));
|
|
return TRUE;
|
|
}
|
|
|
|
if (!(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_PARTICLES)))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
if (num_parts > drawable->getNumFaces())
|
|
{
|
|
drawable->setNumFacesFast(num_parts+num_parts/4, NULL, getTEImage(0));
|
|
}
|
|
|
|
F32 tot_area = 0;
|
|
|
|
F32 max_area = LLViewerPartSim::getMaxPartCount() * MAX_PARTICLE_AREA_SCALE;
|
|
F32 pixel_meter_ratio = LLViewerCamera::getInstance()->getPixelMeterRatio();
|
|
pixel_meter_ratio *= pixel_meter_ratio;
|
|
|
|
LLViewerPartSim::checkParticleCount(mViewerPartGroupp->mParticles.size()) ;
|
|
|
|
S32 count=0;
|
|
mDepth = 0.f;
|
|
S32 i = 0 ;
|
|
LLVector3 camera_agent = getCameraPosition();
|
|
|
|
F32 max_scale = 0.f;
|
|
|
|
|
|
for (i = 0 ; i < (S32)mViewerPartGroupp->mParticles.size(); i++)
|
|
{
|
|
const LLViewerPart *part = mViewerPartGroupp->mParticles[i];
|
|
|
|
|
|
//remember the largest particle
|
|
max_scale = llmax(max_scale, part->mScale.mV[0], part->mScale.mV[1]);
|
|
|
|
if (part->mFlags & LLPartData::LL_PART_RIBBON_MASK)
|
|
{ //include ribbon segment length in scale
|
|
const LLVector3* pos_agent = NULL;
|
|
if (part->mParent)
|
|
{
|
|
pos_agent = &(part->mParent->mPosAgent);
|
|
}
|
|
else if (part->mPartSourcep.notNull())
|
|
{
|
|
pos_agent = &(part->mPartSourcep->mPosAgent);
|
|
}
|
|
|
|
if (pos_agent)
|
|
{
|
|
F32 dist = (*pos_agent-part->mPosAgent).length();
|
|
|
|
max_scale = llmax(max_scale, dist);
|
|
}
|
|
}
|
|
|
|
LLVector3 part_pos_agent(part->mPosAgent);
|
|
LLVector3 at(part_pos_agent - camera_agent);
|
|
|
|
|
|
F32 camera_dist_squared = at.lengthSquared();
|
|
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;
|
|
|
|
llassert(std::isfinite(inv_camera_dist_squared));
|
|
llassert(!std::isnan(inv_camera_dist_squared));
|
|
|
|
F32 area = part->mScale.mV[0] * part->mScale.mV[1] * inv_camera_dist_squared;
|
|
tot_area = llmax(tot_area, area);
|
|
|
|
if (tot_area > max_area)
|
|
{
|
|
break;
|
|
}
|
|
|
|
count++;
|
|
|
|
facep = drawable->getFace(i);
|
|
if (!facep)
|
|
{
|
|
LL_WARNS() << "No face found for index " << i << "!" << LL_ENDL;
|
|
continue;
|
|
}
|
|
|
|
facep->setTEOffset(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->setViewerObject(this);
|
|
|
|
if (part->mFlags & LLPartData::LL_PART_EMISSIVE_MASK)
|
|
{
|
|
facep->setState(LLFace::FULLBRIGHT);
|
|
}
|
|
else
|
|
{
|
|
facep->clearState(LLFace::FULLBRIGHT);
|
|
}
|
|
|
|
facep->mCenterLocal = part->mPosAgent;
|
|
facep->setFaceColor(part->mColor);
|
|
facep->setTexture(part->mImagep);
|
|
|
|
//check if this particle texture is replaced by a parcel media texture.
|
|
if(part->mImagep.notNull() && part->mImagep->hasParcelMedia())
|
|
{
|
|
part->mImagep->getParcelMedia()->addMediaToFace(facep) ;
|
|
}
|
|
|
|
mPixelArea = tot_area * pixel_meter_ratio;
|
|
const F32 area_scale = 10.f; // scale area to increase priority a bit
|
|
facep->setVirtualSize(mPixelArea*area_scale);
|
|
}
|
|
for (i = count; i < drawable->getNumFaces(); i++)
|
|
{
|
|
LLFace* facep = drawable->getFace(i);
|
|
if (!facep)
|
|
{
|
|
LL_WARNS() << "No face found for index " << i << "!" << LL_ENDL;
|
|
continue;
|
|
}
|
|
facep->setTEOffset(i);
|
|
facep->setSize(0, 0);
|
|
}
|
|
|
|
//record max scale (used to stretch bounding box for visibility culling)
|
|
|
|
mScale.set(max_scale, max_scale, max_scale);
|
|
|
|
mDrawable->movePartition();
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL LLVOPartGroup::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end,
|
|
S32 face,
|
|
BOOL pick_transparent,
|
|
S32* face_hit,
|
|
LLVector4a* intersection,
|
|
LLVector2* tex_coord,
|
|
LLVector4a* normal,
|
|
LLVector4a* bi_normal)
|
|
{
|
|
LLVector4a dir;
|
|
dir.setSub(end, start);
|
|
|
|
F32 closest_t = 2.f;
|
|
BOOL ret = FALSE;
|
|
|
|
for (U32 idx = 0; idx < mViewerPartGroupp->mParticles.size(); ++idx)
|
|
{
|
|
const LLViewerPart &part = *((LLViewerPart*) (mViewerPartGroupp->mParticles[idx]));
|
|
|
|
LLVector4a v[4];
|
|
LLStrider<LLVector4a> verticesp;
|
|
verticesp = v;
|
|
|
|
getGeometry(part, verticesp);
|
|
|
|
F32 a,b,t;
|
|
if (LLTriangleRayIntersect(v[0], v[1], v[2], start, dir, a,b,t) ||
|
|
LLTriangleRayIntersect(v[1], v[3], v[2], start, dir, a,b,t))
|
|
{
|
|
if (t >= 0.f &&
|
|
t <= 1.f &&
|
|
t < closest_t)
|
|
{
|
|
ret = TRUE;
|
|
closest_t = t;
|
|
if (face_hit)
|
|
{
|
|
*face_hit = idx;
|
|
}
|
|
|
|
if (intersection)
|
|
{
|
|
LLVector4a intersect = dir;
|
|
intersect.mul(closest_t);
|
|
intersection->setAdd(intersect, start);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void LLVOPartGroup::getGeometry(const LLViewerPart& part,
|
|
LLStrider<LLVector4a>& verticesp)
|
|
{
|
|
if (part.mFlags & LLPartData::LL_PART_RIBBON_MASK)
|
|
{
|
|
LLVector4a axis, pos, paxis, ppos;
|
|
F32 scale, pscale;
|
|
|
|
pos.load3(part.mPosAgent.mV);
|
|
axis.load3(part.mAxis.mV);
|
|
scale = part.mScale.mV[0];
|
|
|
|
if (part.mParent)
|
|
{
|
|
ppos.load3(part.mParent->mPosAgent.mV);
|
|
paxis.load3(part.mParent->mAxis.mV);
|
|
pscale = part.mParent->mScale.mV[0];
|
|
}
|
|
else
|
|
{ //use source object as position
|
|
|
|
if (part.mPartSourcep->mSourceObjectp.notNull())
|
|
{
|
|
LLVector3 v = LLVector3(0,0,1);
|
|
v *= part.mPartSourcep->mSourceObjectp->getRenderRotation();
|
|
paxis.load3(v.mV);
|
|
ppos.load3(part.mPartSourcep->mPosAgent.mV);
|
|
pscale = part.mStartScale.mV[0];
|
|
}
|
|
else
|
|
{ //no source object, no parent, nothing to draw
|
|
ppos = pos;
|
|
pscale = scale;
|
|
paxis = axis;
|
|
}
|
|
}
|
|
|
|
LLVector4a p0, p1, p2, p3;
|
|
|
|
scale *= 0.5f;
|
|
pscale *= 0.5f;
|
|
|
|
axis.mul(scale);
|
|
paxis.mul(pscale);
|
|
|
|
p0.setAdd(pos, axis);
|
|
p1.setSub(pos,axis);
|
|
p2.setAdd(ppos, paxis);
|
|
p3.setSub(ppos, paxis);
|
|
|
|
(*verticesp++) = p2;
|
|
(*verticesp++) = p3;
|
|
(*verticesp++) = p0;
|
|
(*verticesp++) = p1;
|
|
}
|
|
else
|
|
{
|
|
LLVector4a part_pos_agent;
|
|
part_pos_agent.load3(part.mPosAgent.mV);
|
|
LLVector4a camera_agent;
|
|
camera_agent.load3(getCameraPosition().mV);
|
|
LLVector4a at;
|
|
at.setSub(part_pos_agent, camera_agent);
|
|
LLVector4a up(0, 0, 1);
|
|
LLVector4a right;
|
|
|
|
right.setCross3(at, up);
|
|
right.normalize3fast();
|
|
up.setCross3(right, at);
|
|
up.normalize3fast();
|
|
|
|
if (part.mFlags & LLPartData::LL_PART_FOLLOW_VELOCITY_MASK)
|
|
{
|
|
LLVector4a normvel;
|
|
normvel.load3(part.mVelocity.mV);
|
|
normvel.normalize3fast();
|
|
LLVector2 up_fracs;
|
|
up_fracs.mV[0] = normvel.dot3(right).getF32();
|
|
up_fracs.mV[1] = normvel.dot3(up).getF32();
|
|
up_fracs.normalize();
|
|
LLVector4a new_up;
|
|
LLVector4a new_right;
|
|
|
|
//new_up = up_fracs.mV[0] * right + up_fracs.mV[1]*up;
|
|
LLVector4a t = right;
|
|
t.mul(up_fracs.mV[0]);
|
|
new_up = up;
|
|
new_up.mul(up_fracs.mV[1]);
|
|
new_up.add(t);
|
|
|
|
//new_right = up_fracs.mV[1] * right - up_fracs.mV[0]*up;
|
|
t = right;
|
|
t.mul(up_fracs.mV[1]);
|
|
new_right = up;
|
|
new_right.mul(up_fracs.mV[0]);
|
|
t.sub(new_right);
|
|
|
|
up = new_up;
|
|
right = t;
|
|
up.normalize3fast();
|
|
right.normalize3fast();
|
|
}
|
|
|
|
right.mul(0.5f*part.mScale.mV[0]);
|
|
up.mul(0.5f*part.mScale.mV[1]);
|
|
|
|
|
|
//HACK -- the verticesp->mV[3] = 0.f here are to set the texture index to 0 (particles don't use texture batching, maybe they should)
|
|
// this works because there is actually a 4th float stored after the vertex position which is used as a texture index
|
|
// also, somebody please VECTORIZE THIS
|
|
|
|
LLVector4a ppapu;
|
|
LLVector4a ppamu;
|
|
|
|
ppapu.setAdd(part_pos_agent, up);
|
|
ppamu.setSub(part_pos_agent, up);
|
|
|
|
verticesp->setSub(ppapu, right);
|
|
(*verticesp++).getF32ptr()[3] = 0.f;
|
|
verticesp->setSub(ppamu, right);
|
|
(*verticesp++).getF32ptr()[3] = 0.f;
|
|
verticesp->setAdd(ppapu, right);
|
|
(*verticesp++).getF32ptr()[3] = 0.f;
|
|
verticesp->setAdd(ppamu, right);
|
|
(*verticesp++).getF32ptr()[3] = 0.f;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void LLVOPartGroup::getGeometry(S32 idx,
|
|
LLStrider<LLVector4a>& verticesp,
|
|
LLStrider<LLVector3>& normalsp,
|
|
LLStrider<LLVector2>& texcoordsp,
|
|
LLStrider<LLColor4U>& colorsp,
|
|
LLStrider<LLColor4U>& emissivep,
|
|
LLStrider<U16>& indicesp)
|
|
{
|
|
if (idx >= (S32) mViewerPartGroupp->mParticles.size())
|
|
{
|
|
return;
|
|
}
|
|
|
|
const LLViewerPart &part = *((LLViewerPart*) (mViewerPartGroupp->mParticles[idx]));
|
|
|
|
getGeometry(part, verticesp);
|
|
|
|
LLColor4U pcolor;
|
|
LLColor4U color = part.mColor;
|
|
|
|
LLColor4U pglow;
|
|
|
|
if (part.mFlags & LLPartData::LL_PART_RIBBON_MASK)
|
|
{ //make sure color blends properly
|
|
if (part.mParent)
|
|
{
|
|
pglow = part.mParent->mGlow;
|
|
pcolor = part.mParent->mColor;
|
|
}
|
|
else
|
|
{
|
|
pglow = LLColor4U(0, 0, 0, (U8) ll_round(255.f*part.mStartGlow));
|
|
pcolor = part.mStartColor;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pglow = part.mGlow;
|
|
pcolor = color;
|
|
}
|
|
|
|
*colorsp++ = pcolor;
|
|
*colorsp++ = pcolor;
|
|
*colorsp++ = color;
|
|
*colorsp++ = color;
|
|
|
|
//Only add emissive attributes if glowing (doing it for all particles is INCREDIBLY inefficient as it leads to a second, slower, render pass.)
|
|
if (LLGLSLShader::sNoFixedFunction && (pglow.mV[3] > 0 || part.mGlow.mV[3] > 0))
|
|
{ //only write glow if it is not zero
|
|
*emissivep++ = pglow;
|
|
*emissivep++ = pglow;
|
|
*emissivep++ = part.mGlow;
|
|
*emissivep++ = part.mGlow;
|
|
}
|
|
|
|
|
|
if (!(part.mFlags & LLPartData::LL_PART_EMISSIVE_MASK))
|
|
{ //not fullbright, needs normal
|
|
LLVector3 normal = -LLViewerCamera::getInstance()->getXAxis();
|
|
*normalsp++ = normal;
|
|
*normalsp++ = normal;
|
|
*normalsp++ = normal;
|
|
*normalsp++ = normal;
|
|
}
|
|
}
|
|
U32 LLVOPartGroup::getPartitionType() const
|
|
{
|
|
return LLViewerRegion::PARTITION_PARTICLE;
|
|
}
|
|
|
|
LLParticlePartition::LLParticlePartition()
|
|
: LLSpatialPartition(LLDrawPoolAlpha::VERTEX_DATA_MASK | LLVertexBuffer::MAP_TEXTURE_INDEX, TRUE, GL_STREAM_DRAW_ARB)
|
|
{
|
|
mRenderPass = LLRenderPass::PASS_ALPHA;
|
|
mDrawableType = LLPipeline::RENDER_TYPE_PARTICLES;
|
|
mPartitionType = LLViewerRegion::PARTITION_PARTICLE;
|
|
mSlopRatio = 0.f;
|
|
mLODPeriod = 1;
|
|
}
|
|
|
|
LLHUDParticlePartition::LLHUDParticlePartition() :
|
|
LLParticlePartition()
|
|
{
|
|
mDrawableType = LLPipeline::RENDER_TYPE_HUD_PARTICLES;
|
|
mPartitionType = LLViewerRegion::PARTITION_HUD_PARTICLE;
|
|
}
|
|
|
|
static LLFastTimer::DeclareTimer FTM_REBUILD_PARTICLE_VBO("Particle VBO");
|
|
|
|
void LLParticlePartition::rebuildGeom(LLSpatialGroup* group)
|
|
{
|
|
if (group->isDead() || !group->hasState(LLSpatialGroup::GEOM_DIRTY))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (group->changeLOD())
|
|
{
|
|
group->mLastUpdateDistance = group->mDistance;
|
|
group->mLastUpdateViewAngle = group->mViewAngle;
|
|
}
|
|
|
|
LLFastTimer ftm(FTM_REBUILD_PARTICLE_VBO);
|
|
|
|
group->clearDrawMap();
|
|
|
|
//get geometry count
|
|
U32 index_count = 0;
|
|
U32 vertex_count = 0;
|
|
|
|
addGeometryCount(group, vertex_count, index_count);
|
|
|
|
|
|
if (vertex_count > 0 && index_count > 0)
|
|
{
|
|
group->mBuilt = 1.f;
|
|
//use one vertex buffer for all groups
|
|
group->mVertexBuffer = LLVOPartGroup::sVB;
|
|
getGeometry(group);
|
|
}
|
|
else
|
|
{
|
|
group->mVertexBuffer = NULL;
|
|
group->mBufferMap.clear();
|
|
}
|
|
|
|
group->mLastUpdateTime = gFrameTimeSeconds;
|
|
group->clearState(LLSpatialGroup::GEOM_DIRTY);
|
|
}
|
|
|
|
void LLParticlePartition::addGeometryCount(LLSpatialGroup* group, U32& vertex_count, U32& index_count)
|
|
{
|
|
group->mBufferUsage = mBufferUsage;
|
|
|
|
mFaceList.clear();
|
|
|
|
LLViewerCamera* camera = LLViewerCamera::getInstance();
|
|
OctreeGuard guard(group->getOctreeNode());
|
|
for (LLSpatialGroup::element_iter i = group->getDataBegin(); i != group->getDataEnd(); ++i)
|
|
{
|
|
LLDrawable* drawablep = (LLDrawable*)(*i)->getDrawable();
|
|
|
|
if (drawablep->isDead())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
LLAlphaObject* obj = (LLAlphaObject*) drawablep->getVObj().get();
|
|
obj->mDepth = 0.f;
|
|
|
|
U32 count = 0;
|
|
for (S32 j = 0; j < drawablep->getNumFaces(); ++j)
|
|
{
|
|
drawablep->updateFaceSize(j);
|
|
|
|
LLFace* facep = drawablep->getFace(j);
|
|
if ( !facep || !facep->hasGeometry())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
vertex_count += facep->getGeomCount();
|
|
index_count += facep->getIndicesCount();
|
|
|
|
count++;
|
|
facep->mDistance = (facep->mCenterLocal - camera->getOrigin()) * camera->getAtAxis();
|
|
obj->mDepth += facep->mDistance;
|
|
|
|
mFaceList.push_back(facep);
|
|
llassert(facep->getIndicesCount() < 65536);
|
|
}
|
|
|
|
obj->mDepth /= count;
|
|
}
|
|
}
|
|
|
|
|
|
static LLFastTimer::DeclareTimer FTM_REBUILD_PARTICLE_GEOM("Particle Geom");
|
|
|
|
void LLParticlePartition::getGeometry(LLSpatialGroup* group)
|
|
{
|
|
LLFastTimer ftm(FTM_REBUILD_PARTICLE_GEOM);
|
|
|
|
std::sort(mFaceList.begin(), mFaceList.end(), LLFace::CompareDistanceGreater());
|
|
|
|
U32 index_count = 0;
|
|
U32 vertex_count = 0;
|
|
|
|
group->clearDrawMap();
|
|
|
|
LLVertexBuffer* buffer = group->mVertexBuffer;
|
|
|
|
LLStrider<U16> indicesp;
|
|
LLStrider<LLVector4a> verticesp;
|
|
LLStrider<LLVector3> normalsp;
|
|
LLStrider<LLVector2> texcoordsp;
|
|
LLStrider<LLColor4U> colorsp;
|
|
LLStrider<LLColor4U> emissivep;
|
|
|
|
buffer->getVertexStrider(verticesp);
|
|
buffer->getNormalStrider(normalsp);
|
|
buffer->getColorStrider(colorsp);
|
|
buffer->getEmissiveStrider(emissivep);
|
|
|
|
|
|
LLSpatialGroup::drawmap_elem_t& draw_vec = group->mDrawMap[mRenderPass];
|
|
|
|
for (std::vector<LLFace*>::iterator i = mFaceList.begin(); i != mFaceList.end(); ++i)
|
|
{
|
|
LLFace* facep = *i;
|
|
LLAlphaObject* object = (LLAlphaObject*) facep->getViewerObject();
|
|
|
|
if (!facep->isState(LLFace::PARTICLE))
|
|
{ //set the indices of this face
|
|
S32 idx = LLVOPartGroup::findAvailableVBSlot();
|
|
if (idx >= 0)
|
|
{
|
|
facep->setGeomIndex(idx*4);
|
|
facep->setIndicesIndex(idx*6);
|
|
facep->setVertexBuffer(LLVOPartGroup::sVB);
|
|
facep->setPoolType(LLDrawPool::POOL_ALPHA);
|
|
facep->setState(LLFace::PARTICLE);
|
|
}
|
|
else
|
|
{
|
|
continue; //out of space in particle buffer
|
|
}
|
|
}
|
|
|
|
S32 geom_idx = (S32) facep->getGeomIndex();
|
|
|
|
LLStrider<U16> cur_idx = indicesp + facep->getIndicesStart();
|
|
LLStrider<LLVector4a> cur_vert = verticesp + geom_idx;
|
|
LLStrider<LLVector3> cur_norm = normalsp + geom_idx;
|
|
LLStrider<LLVector2> cur_tc = texcoordsp + geom_idx;
|
|
LLStrider<LLColor4U> cur_col = colorsp + geom_idx;
|
|
LLStrider<LLColor4U> cur_glow = emissivep + geom_idx;
|
|
|
|
LLColor4U* start_glow = cur_glow.get();
|
|
|
|
object->getGeometry(facep->getTEOffset(), cur_vert, cur_norm, cur_tc, cur_col, cur_glow, cur_idx);
|
|
|
|
BOOL has_glow = FALSE;
|
|
|
|
if (cur_glow.get() != start_glow)
|
|
{
|
|
has_glow = TRUE;
|
|
}
|
|
|
|
llassert(facep->getGeomCount() == 4);
|
|
llassert(facep->getIndicesCount() == 6);
|
|
|
|
|
|
vertex_count += facep->getGeomCount();
|
|
index_count += facep->getIndicesCount();
|
|
|
|
S32 idx = draw_vec.size()-1;
|
|
|
|
BOOL fullbright = facep->isState(LLFace::FULLBRIGHT);
|
|
F32 vsize = facep->getVirtualSize();
|
|
|
|
bool batched = false;
|
|
|
|
U32 bf_src = LLRender::BF_SOURCE_ALPHA;
|
|
U32 bf_dst = LLRender::BF_ONE_MINUS_SOURCE_ALPHA;
|
|
|
|
object->getBlendFunc(facep->getTEOffset(), bf_src, bf_dst);
|
|
|
|
|
|
if (idx >= 0)
|
|
{
|
|
LLDrawInfo* info = draw_vec[idx];
|
|
|
|
if (info->mTexture == facep->getTexture() &&
|
|
info->mHasGlow == has_glow &&
|
|
info->mFullbright == fullbright &&
|
|
info->mBlendFuncDst == bf_dst &&
|
|
info->mBlendFuncSrc == bf_src)
|
|
{
|
|
if (draw_vec[idx]->mEnd == facep->getGeomIndex()-1)
|
|
{
|
|
batched = true;
|
|
info->mCount += facep->getIndicesCount();
|
|
info->mEnd += facep->getGeomCount();
|
|
info->mVSize = llmax(draw_vec[idx]->mVSize, vsize);
|
|
}
|
|
else if (draw_vec[idx]->mStart == facep->getGeomIndex()+facep->getGeomCount()+1)
|
|
{
|
|
batched = true;
|
|
info->mCount += facep->getIndicesCount();
|
|
info->mStart -= facep->getGeomCount();
|
|
info->mOffset = facep->getIndicesStart();
|
|
info->mVSize = llmax(draw_vec[idx]->mVSize, vsize);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (!batched)
|
|
{
|
|
U32 start = facep->getGeomIndex();
|
|
U32 end = start + facep->getGeomCount()-1;
|
|
U32 offset = facep->getIndicesStart();
|
|
U32 count = facep->getIndicesCount();
|
|
LLDrawInfo* info = new LLDrawInfo(start,end,count,offset,facep->getTexture(),
|
|
//facep->getTexture(),
|
|
buffer, fullbright);
|
|
const LLVector4a* bounds = group->getBounds();
|
|
info->mExtents[0] = bounds[0];
|
|
info->mExtents[1] = bounds[1];
|
|
info->mVSize = vsize;
|
|
info->mBlendFuncDst = bf_dst;
|
|
info->mBlendFuncSrc = bf_src;
|
|
info->mHasGlow = has_glow;
|
|
info->mParticle = TRUE;
|
|
draw_vec.push_back(info);
|
|
//for alpha sorting
|
|
facep->setDrawInfo(info);
|
|
}
|
|
|
|
if(facep->getGeomCount() > 0)
|
|
{
|
|
buffer->validateRange(facep->getGeomIndex(), facep->getGeomIndex() + facep->getGeomCount() - 1, facep->getIndicesCount(), facep->getIndicesStart());
|
|
}
|
|
}
|
|
|
|
mFaceList.clear();
|
|
}
|
|
|
|
F32 LLParticlePartition::calcPixelArea(LLSpatialGroup* group, LLCamera& camera)
|
|
{
|
|
return 1024.f;
|
|
}
|
|
|
|
U32 LLVOHUDPartGroup::getPartitionType() const
|
|
{
|
|
return LLViewerRegion::PARTITION_HUD_PARTICLE;
|
|
}
|
|
|
|
LLDrawable* LLVOHUDPartGroup::createDrawable(LLPipeline *pipeline)
|
|
{
|
|
pipeline->allocDrawable(this);
|
|
mDrawable->setLit(FALSE);
|
|
mDrawable->setRenderType(LLPipeline::RENDER_TYPE_HUD_PARTICLES);
|
|
return mDrawable;
|
|
}
|
|
|
|
LLVector3 LLVOHUDPartGroup::getCameraPosition() const
|
|
{
|
|
return LLVector3(-1,0,0);
|
|
}
|
|
|