3658 lines
90 KiB
C++
3658 lines
90 KiB
C++
/**
|
|
* @file llspatialpartition.cpp
|
|
* @brief LLSpatialGroup class implementation and supporting functions
|
|
*
|
|
* $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 "llspatialpartition.h"
|
|
|
|
#include "llappviewer.h"
|
|
#include "lltexturecache.h"
|
|
#include "lltexturefetch.h"
|
|
#include "llimageworker.h"
|
|
#include "llviewerwindow.h"
|
|
#include "llviewerobjectlist.h"
|
|
#include "llvovolume.h"
|
|
#include "llvolume.h"
|
|
#include "llvolumeoctree.h"
|
|
#include "llviewercamera.h"
|
|
#include "llface.h"
|
|
#include "llfloaterinspect.h"
|
|
#include "llfloatertools.h"
|
|
#include "llviewercontrol.h"
|
|
#include "llviewerregion.h"
|
|
#include "llcamera.h"
|
|
#include "pipeline.h"
|
|
#include "llmeshrepository.h"
|
|
#include "llrender.h"
|
|
#include "lloctree.h"
|
|
#include "llphysicsshapebuilderutil.h"
|
|
#include "llvoavatar.h"
|
|
#include "llvolumemgr.h"
|
|
#include "llglslshader.h"
|
|
#include "llviewershadermgr.h"
|
|
#include "llcontrolavatar.h"
|
|
|
|
static LLTrace::BlockTimerStatHandle FTM_FRUSTUM_CULL("Frustum Culling");
|
|
static LLTrace::BlockTimerStatHandle FTM_CULL_REBOUND("Cull Rebound");
|
|
|
|
extern bool gShiftFrame;
|
|
|
|
static U32 sZombieGroups = 0;
|
|
U32 LLSpatialGroup::sNodeCount = 0;
|
|
|
|
U32 gOctreeMaxCapacity;
|
|
U32 gOctreeReserveCapacity;
|
|
|
|
BOOL LLSpatialGroup::sNoDelete = FALSE;
|
|
|
|
static F32 sLastMaxTexPriority = 1.f;
|
|
static F32 sCurMaxTexPriority = 1.f;
|
|
|
|
BOOL LLSpatialPartition::sTeleportRequested = FALSE;
|
|
|
|
//BOOL LLSpatialPartition::sFreezeState = FALSE;
|
|
|
|
//static counter for frame to switch LOD on
|
|
|
|
void sg_assert(BOOL expr)
|
|
{
|
|
#if LL_OCTREE_PARANOIA_CHECK
|
|
if (!expr)
|
|
{
|
|
LL_ERRS() << "Octree invalid!" << LL_ENDL;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
//returns:
|
|
// 0 if sphere and AABB are not intersecting
|
|
// 1 if they are
|
|
// 2 if AABB is entirely inside sphere
|
|
|
|
S32 LLSphereAABB(const LLVector3& center, const LLVector3& size, const LLVector3& pos, const F32 &rad)
|
|
{
|
|
S32 ret = 2;
|
|
|
|
LLVector3 min = center - size;
|
|
LLVector3 max = center + size;
|
|
for (U32 i = 0; i < 3; i++)
|
|
{
|
|
if (min.mV[i] > pos.mV[i] + rad ||
|
|
max.mV[i] < pos.mV[i] - rad)
|
|
{ //totally outside
|
|
return 0;
|
|
}
|
|
|
|
if (min.mV[i] < pos.mV[i] - rad ||
|
|
max.mV[i] > pos.mV[i] + rad)
|
|
{ //intersecting
|
|
ret = 1;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
LLSpatialGroup::~LLSpatialGroup()
|
|
{
|
|
/*if (sNoDelete)
|
|
{
|
|
LL_ERRS() << "Illegal deletion of LLSpatialGroup!" << LL_ENDL;
|
|
}*/
|
|
|
|
if (gDebugGL)
|
|
{
|
|
gPipeline.checkReferences(this);
|
|
}
|
|
if (hasState((LLSpatialGroup::eSpatialState)DEAD))
|
|
{
|
|
sZombieGroups--;
|
|
}
|
|
|
|
sNodeCount--;
|
|
|
|
clearDrawMap();
|
|
}
|
|
|
|
void LLSpatialGroup::clearDrawMap()
|
|
{
|
|
mDrawMap.clear();
|
|
}
|
|
|
|
BOOL LLSpatialGroup::isHUDGroup()
|
|
{
|
|
return getSpatialPartition() && getSpatialPartition()->isHUDPartition() ;
|
|
}
|
|
|
|
void LLSpatialGroup::validate()
|
|
{
|
|
ll_assert_aligned(this,64);
|
|
#if LL_OCTREE_PARANOIA_CHECK
|
|
|
|
sg_assert(!isState(DIRTY));
|
|
sg_assert(!isDead());
|
|
|
|
LLVector4a myMin;
|
|
myMin.setSub(mBounds[0], mBounds[1]);
|
|
LLVector4a myMax;
|
|
myMax.setAdd(mBounds[0], mBounds[1]);
|
|
|
|
validateDrawMap();
|
|
|
|
for (element_iter i = getDataBegin(); i != getDataEnd(); ++i)
|
|
{
|
|
LLDrawable* drawable = *i;
|
|
sg_assert(drawable->getSpatialGroup() == this);
|
|
if (drawable->getSpatialBridge())
|
|
{
|
|
sg_assert(drawable->getSpatialBridge() == getSpatialPartition()->asBridge());
|
|
}
|
|
|
|
/*if (drawable->isSpatialBridge())
|
|
{
|
|
LLSpatialPartition* part = drawable->asPartition();
|
|
if (!part)
|
|
{
|
|
LL_ERRS() << "Drawable reports it is a spatial bridge but not a partition." << LL_ENDL;
|
|
}
|
|
LLSpatialGroup* group = (LLSpatialGroup*) part->mOctree->getListener(0);
|
|
group->validate();
|
|
}*/
|
|
}
|
|
|
|
for (U32 i = 0; i < mOctreeNode->getChildCount(); ++i)
|
|
{
|
|
LLSpatialGroup* group = (LLSpatialGroup*) mOctreeNode->getChild(i)->getListener(0);
|
|
|
|
group->validate();
|
|
|
|
//ensure all children are enclosed in this node
|
|
LLVector4a center = group->mBounds[0];
|
|
LLVector4a size = group->mBounds[1];
|
|
|
|
LLVector4a min;
|
|
min.setSub(center, size);
|
|
LLVector4a max;
|
|
max.setAdd(center, size);
|
|
|
|
for (U32 j = 0; j < 3; j++)
|
|
{
|
|
sg_assert(min[j] >= myMin[j]-0.02f);
|
|
sg_assert(max[j] <= myMax[j]+0.02f);
|
|
}
|
|
}
|
|
|
|
#endif
|
|
}
|
|
|
|
void LLSpatialGroup::validateDrawMap()
|
|
{
|
|
#if LL_OCTREE_PARANOIA_CHECK
|
|
for (draw_map_t::iterator i = mDrawMap.begin(); i != mDrawMap.end(); ++i)
|
|
{
|
|
LLSpatialGroup::drawmap_elem_t& draw_vec = i->second;
|
|
for (drawmap_elem_t::iterator j = draw_vec.begin(); j != draw_vec.end(); ++j)
|
|
{
|
|
LLDrawInfo& params = **j;
|
|
|
|
params.validate();
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
BOOL LLSpatialGroup::updateInGroup(LLDrawable *drawablep, BOOL immediate)
|
|
{
|
|
drawablep->updateSpatialExtents();
|
|
|
|
OctreeNode* parent = mOctreeNode->getOctParent();
|
|
|
|
if (mOctreeNode->isInside(drawablep->getPositionGroup()) &&
|
|
(mOctreeNode->contains(drawablep->getEntry()) ||
|
|
(drawablep->getBinRadius() > mOctreeNode->getSize()[0] &&
|
|
parent && parent->getElementCount() >= gOctreeMaxCapacity)))
|
|
{
|
|
unbound();
|
|
setState((LLSpatialGroup::eSpatialState)OBJECT_DIRTY);
|
|
//setState(GEOM_DIRTY);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOL LLSpatialGroup::addObject(LLDrawable *drawablep)
|
|
{
|
|
if(!drawablep)
|
|
{
|
|
return FALSE;
|
|
}
|
|
{
|
|
drawablep->setGroup(this);
|
|
setState(LLSpatialGroup::eSpatialState(OBJECT_DIRTY | GEOM_DIRTY));
|
|
setOcclusionState(LLSpatialGroup::DISCARD_QUERY, LLSpatialGroup::STATE_MODE_ALL_CAMERAS);
|
|
gPipeline.markRebuild(this, TRUE);
|
|
if (drawablep->isSpatialBridge())
|
|
{
|
|
mBridgeList.push_back((LLSpatialBridge*) drawablep);
|
|
}
|
|
if (drawablep->getRadius() > 1.f)
|
|
{
|
|
setState(IMAGE_DIRTY);
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void LLSpatialGroup::rebuildGeom()
|
|
{
|
|
if (!isDead())
|
|
{
|
|
getSpatialPartition()->rebuildGeom(this);
|
|
|
|
if (hasState(LLSpatialGroup::MESH_DIRTY))
|
|
{
|
|
gPipeline.markMeshDirty(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
void LLSpatialGroup::rebuildMesh()
|
|
{
|
|
if (!isDead())
|
|
{
|
|
getSpatialPartition()->rebuildMesh(this);
|
|
}
|
|
}
|
|
|
|
static LLTrace::BlockTimerStatHandle FTM_REBUILD_VBO("VBO Rebuilt");
|
|
static LLTrace::BlockTimerStatHandle FTM_ADD_GEOMETRY_COUNT("Add Geometry");
|
|
static LLTrace::BlockTimerStatHandle FTM_CREATE_VB("Create VB");
|
|
static LLTrace::BlockTimerStatHandle FTM_GET_GEOMETRY("Get Geometry");
|
|
|
|
void LLSpatialPartition::rebuildGeom(LLSpatialGroup* group)
|
|
{
|
|
if (group->isDead() || !group->hasState(LLSpatialGroup::GEOM_DIRTY))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!LLPipeline::sSkipUpdate && group->changeLOD())
|
|
{
|
|
group->mLastUpdateDistance = group->mDistance;
|
|
group->mLastUpdateViewAngle = group->mViewAngle;
|
|
}
|
|
|
|
LL_RECORD_BLOCK_TIME(FTM_REBUILD_VBO);
|
|
|
|
group->clearDrawMap();
|
|
|
|
//get geometry count
|
|
U32 index_count = 0;
|
|
U32 vertex_count = 0;
|
|
|
|
{
|
|
LL_RECORD_BLOCK_TIME(FTM_ADD_GEOMETRY_COUNT);
|
|
addGeometryCount(group, vertex_count, index_count);
|
|
}
|
|
|
|
if (vertex_count > 0 && index_count > 0)
|
|
{ //create vertex buffer containing volume geometry for this node
|
|
{
|
|
LL_RECORD_BLOCK_TIME(FTM_CREATE_VB);
|
|
group->mBuilt = 1.f;
|
|
if (group->mVertexBuffer.isNull() ||
|
|
!group->mVertexBuffer->isWriteable() ||
|
|
(group->mBufferUsage != group->mVertexBuffer->getUsage() && LLVertexBuffer::sEnableVBOs))
|
|
{
|
|
group->mVertexBuffer = createVertexBuffer(mVertexDataMask, group->mBufferUsage);
|
|
group->mVertexBuffer->allocateBuffer(vertex_count, index_count, true);
|
|
stop_glerror();
|
|
}
|
|
else
|
|
{
|
|
group->mVertexBuffer->resizeBuffer(vertex_count, index_count);
|
|
stop_glerror();
|
|
}
|
|
}
|
|
|
|
{
|
|
LL_RECORD_BLOCK_TIME(FTM_GET_GEOMETRY);
|
|
getGeometry(group);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
group->mVertexBuffer = NULL;
|
|
group->mBufferVec.clear();
|
|
}
|
|
|
|
group->mLastUpdateTime = gFrameTimeSeconds;
|
|
group->clearState(LLSpatialGroup::GEOM_DIRTY);
|
|
}
|
|
|
|
|
|
void LLSpatialPartition::rebuildMesh(LLSpatialGroup* group)
|
|
{
|
|
|
|
}
|
|
|
|
LLSpatialGroup* LLSpatialGroup::getParent()
|
|
{
|
|
return (LLSpatialGroup*)LLViewerOctreeGroup::getParent();
|
|
}
|
|
|
|
BOOL LLSpatialGroup::removeObject(LLDrawable *drawablep, BOOL from_octree)
|
|
{
|
|
if(!drawablep)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
unbound();
|
|
if (mOctreeNode && !from_octree)
|
|
{
|
|
drawablep->setGroup(NULL);
|
|
}
|
|
else
|
|
{
|
|
drawablep->setGroup(NULL);
|
|
setState(GEOM_DIRTY);
|
|
gPipeline.markRebuild(this, TRUE);
|
|
|
|
if (drawablep->isSpatialBridge())
|
|
{
|
|
for (bridge_list_t::iterator i = mBridgeList.begin(); i != mBridgeList.end(); ++i)
|
|
{
|
|
if (*i == drawablep)
|
|
{
|
|
mBridgeList.erase(i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (getElementCount() == 0)
|
|
{ //delete draw map on last element removal since a rebuild might never happen
|
|
clearDrawMap();
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
void LLSpatialGroup::shift(const LLVector4a &offset)
|
|
{
|
|
LLVector4a t = mOctreeNode->getCenter();
|
|
t.add(offset);
|
|
mOctreeNode->setCenter(t);
|
|
mOctreeNode->updateMinMax();
|
|
mBounds[0].add(offset);
|
|
mExtents[0].add(offset);
|
|
mExtents[1].add(offset);
|
|
mObjectBounds[0].add(offset);
|
|
mObjectExtents[0].add(offset);
|
|
mObjectExtents[1].add(offset);
|
|
|
|
if (!getSpatialPartition()->mRenderByGroup &&
|
|
getSpatialPartition()->mPartitionType != LLViewerRegion::PARTITION_TREE &&
|
|
getSpatialPartition()->mPartitionType != LLViewerRegion::PARTITION_TERRAIN &&
|
|
getSpatialPartition()->mPartitionType != LLViewerRegion::PARTITION_ATTACHMENT &&
|
|
getSpatialPartition()->mPartitionType != LLViewerRegion::PARTITION_BRIDGE)
|
|
{
|
|
setState(GEOM_DIRTY);
|
|
gPipeline.markRebuild(this, TRUE);
|
|
}
|
|
}
|
|
|
|
class LLSpatialSetState : public OctreeTraveler
|
|
{
|
|
public:
|
|
LLSpatialGroup::eSpatialState mState;
|
|
LLSpatialSetState(LLSpatialGroup::eSpatialState state) : mState(state) { }
|
|
virtual void visit(const OctreeNode* branch) { ((LLSpatialGroup*) branch->getListener(0))->setState(mState); }
|
|
};
|
|
|
|
class LLSpatialSetStateDiff : public LLSpatialSetState
|
|
{
|
|
public:
|
|
LLSpatialSetStateDiff(LLSpatialGroup::eSpatialState state) : LLSpatialSetState(state) { }
|
|
|
|
virtual void traverse(const OctreeNode* n)
|
|
{
|
|
LLSpatialGroup* group = (LLSpatialGroup*) n->getListener(0);
|
|
|
|
if (!group->hasState(mState))
|
|
{
|
|
OctreeTraveler::traverse(n);
|
|
}
|
|
}
|
|
};
|
|
|
|
void LLSpatialGroup::setState(eSpatialState state, S32 mode)
|
|
{
|
|
llassert(state <= LLSpatialGroup::STATE_MASK);
|
|
|
|
if (mode > STATE_MODE_SINGLE)
|
|
{
|
|
if (mode == STATE_MODE_DIFF)
|
|
{
|
|
LLSpatialSetStateDiff setter(state);
|
|
setter.traverse(mOctreeNode);
|
|
}
|
|
else
|
|
{
|
|
LLSpatialSetState setter(state);
|
|
setter.traverse(mOctreeNode);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mState |= state;
|
|
}
|
|
}
|
|
|
|
class LLSpatialClearState : public OctreeTraveler
|
|
{
|
|
public:
|
|
LLSpatialGroup::eSpatialState mState;
|
|
LLSpatialClearState(LLSpatialGroup::eSpatialState state) : mState(state) { }
|
|
virtual void visit(const OctreeNode* branch) { ((LLSpatialGroup*) branch->getListener(0))->clearState(mState); }
|
|
};
|
|
|
|
class LLSpatialClearStateDiff : public LLSpatialClearState
|
|
{
|
|
public:
|
|
LLSpatialClearStateDiff(LLSpatialGroup::eSpatialState state) : LLSpatialClearState(state) { }
|
|
|
|
virtual void traverse(const OctreeNode* n)
|
|
{
|
|
LLSpatialGroup* group = (LLSpatialGroup*) n->getListener(0);
|
|
|
|
if (group->hasState(mState))
|
|
{
|
|
OctreeTraveler::traverse(n);
|
|
}
|
|
}
|
|
};
|
|
|
|
void LLSpatialGroup::clearState(eSpatialState state, S32 mode)
|
|
{
|
|
llassert(state <= LLSpatialGroup::STATE_MASK);
|
|
|
|
if (mode > STATE_MODE_SINGLE)
|
|
{
|
|
if (mode == STATE_MODE_DIFF)
|
|
{
|
|
LLSpatialClearStateDiff clearer(state);
|
|
clearer.traverse(mOctreeNode);
|
|
}
|
|
else
|
|
{
|
|
LLSpatialClearState clearer(state);
|
|
clearer.traverse(mOctreeNode);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mState &= ~state;
|
|
}
|
|
}
|
|
|
|
//======================================
|
|
// Octree Listener Implementation
|
|
//======================================
|
|
|
|
LLSpatialGroup::LLSpatialGroup(OctreeNode* node, LLSpatialPartition* part) : LLOcclusionCullingGroup(node, part),
|
|
mObjectBoxSize(1.f),
|
|
mGeometryBytes(0),
|
|
mSurfaceArea(0.f),
|
|
mBuilt(0.f),
|
|
mVertexBuffer(NULL),
|
|
mBufferUsage(part->mBufferUsage),
|
|
mDistance(0.f),
|
|
mDepth(0.f),
|
|
mLastUpdateDistance(-1.f),
|
|
mLastUpdateTime(gFrameTimeSeconds)
|
|
{
|
|
ll_assert_aligned(this,16);
|
|
|
|
sNodeCount++;
|
|
|
|
mViewAngle.splat(0.f);
|
|
mLastUpdateViewAngle.splat(-1.f);
|
|
|
|
sg_assert(mOctreeNode->getListenerCount() == 0);
|
|
setState(LLSpatialGroup::eSpatialState(SG_INITIAL_STATE_MASK));
|
|
gPipeline.markRebuild(this, TRUE);
|
|
|
|
mRadius = 1;
|
|
mPixelArea = 1024.f;
|
|
}
|
|
|
|
void LLSpatialGroup::updateDistance(LLCamera &camera)
|
|
{
|
|
if (LLViewerCamera::sCurCameraID != LLViewerCamera::CAMERA_WORLD)
|
|
{
|
|
LL_WARNS() << "Attempted to update distance for camera other than world camera!" << LL_ENDL;
|
|
return;
|
|
}
|
|
|
|
if (gShiftFrame)
|
|
{
|
|
return;
|
|
}
|
|
|
|
#if !LL_RELEASE_FOR_DOWNLOAD
|
|
if (hasState(LLSpatialGroup::eSpatialState(OBJECT_DIRTY)))
|
|
{
|
|
LL_ERRS() << "Spatial group dirty on distance update." << LL_ENDL;
|
|
}
|
|
#endif
|
|
if (!isEmpty())
|
|
{
|
|
mRadius = getSpatialPartition()->mRenderByGroup ? mObjectBounds[1].getLength3().getF32() :
|
|
(F32) mOctreeNode->getSize().getLength3().getF32();
|
|
mDistance = getSpatialPartition()->calcDistance(this, camera);
|
|
mPixelArea = getSpatialPartition()->calcPixelArea(this, camera);
|
|
}
|
|
}
|
|
|
|
F32 LLSpatialPartition::calcDistance(LLSpatialGroup* group, LLCamera& camera)
|
|
{
|
|
LLVector4a eye;
|
|
LLVector4a origin;
|
|
origin.load3(camera.getOrigin().mV);
|
|
|
|
eye.setSub(group->mObjectBounds[0], origin);
|
|
|
|
F32 dist = 0.f;
|
|
|
|
if (group->mDrawMap.find(LLRenderPass::PASS_ALPHA) != group->mDrawMap.end())
|
|
{
|
|
LLVector4a v = eye;
|
|
|
|
dist = eye.getLength3().getF32();
|
|
eye.normalize3fast();
|
|
|
|
if (!group->hasState(LLSpatialGroup::ALPHA_DIRTY))
|
|
{
|
|
if (!group->getSpatialPartition()->isBridge())
|
|
{
|
|
LLVector4a view_angle = eye;
|
|
|
|
LLVector4a diff;
|
|
diff.setSub(view_angle, group->mLastUpdateViewAngle);
|
|
|
|
if (diff.getLength3().getF32() > 0.64f)
|
|
{
|
|
group->mViewAngle = view_angle;
|
|
group->mLastUpdateViewAngle = view_angle;
|
|
//for occasional alpha sorting within the group
|
|
//NOTE: If there is a trivial way to detect that alpha sorting here would not change the render order,
|
|
//not setting this node to dirty would be a very good thing
|
|
group->setState(LLSpatialGroup::ALPHA_DIRTY);
|
|
gPipeline.markRebuild(group, FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
//calculate depth of node for alpha sorting
|
|
|
|
LLVector3 at = camera.getAtAxis();
|
|
|
|
LLVector4a ata;
|
|
ata.load3(at.mV);
|
|
|
|
LLVector4a t = ata;
|
|
//front of bounding box
|
|
t.mul(0.25f);
|
|
t.mul(group->mObjectBounds[1]);
|
|
v.sub(t);
|
|
|
|
group->mDepth = v.dot3(ata).getF32();
|
|
}
|
|
else
|
|
{
|
|
dist = eye.getLength3().getF32();
|
|
}
|
|
|
|
if (dist < 16.f)
|
|
{
|
|
dist /= 16.f;
|
|
dist *= dist;
|
|
dist *= 16.f;
|
|
}
|
|
|
|
return dist;
|
|
}
|
|
|
|
F32 LLSpatialPartition::calcPixelArea(LLSpatialGroup* group, LLCamera& camera)
|
|
{
|
|
return LLPipeline::calcPixelArea(group->mObjectBounds[0], group->mObjectBounds[1], camera);
|
|
}
|
|
|
|
F32 LLSpatialGroup::getUpdateUrgency() const
|
|
{
|
|
if (!isVisible())
|
|
{
|
|
return 0.f;
|
|
}
|
|
else
|
|
{
|
|
//return (gFrameTimeSeconds - mLastUpdateTime+4.f)/mDistance;
|
|
F32 time = gFrameTimeSeconds-mLastUpdateTime+4.f;
|
|
return time + (mObjectBounds[1].dot3(mObjectBounds[1]).getF32()+1.f)/mDistance;
|
|
}
|
|
}
|
|
|
|
BOOL LLSpatialGroup::changeLOD()
|
|
{
|
|
if (hasState(LLSpatialGroup::eSpatialState(ALPHA_DIRTY | OBJECT_DIRTY)))
|
|
{ ///a rebuild is going to happen, update distance and LoD
|
|
return TRUE;
|
|
}
|
|
|
|
if (getSpatialPartition()->mSlopRatio > 0.f)
|
|
{
|
|
F32 ratio = (mDistance - mLastUpdateDistance)/(llmax(mLastUpdateDistance, mRadius));
|
|
|
|
if (fabsf(ratio) >= getSpatialPartition()->mSlopRatio)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
if (mDistance > mRadius*2.f)
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (needsUpdate())
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void LLSpatialGroup::handleInsertion(const TreeNode* node, LLViewerOctreeEntry* entry)
|
|
{
|
|
addObject((LLDrawable*)entry->getDrawable());
|
|
unbound();
|
|
setState(LLSpatialGroup::eSpatialState(OBJECT_DIRTY));
|
|
}
|
|
|
|
void LLSpatialGroup::handleRemoval(const TreeNode* node, LLViewerOctreeEntry* entry)
|
|
{
|
|
removeObject((LLDrawable*)entry->getDrawable(), TRUE);
|
|
LLViewerOctreeGroup::handleRemoval(node, entry);
|
|
}
|
|
|
|
void LLSpatialGroup::handleDestruction(const TreeNode* node)
|
|
{
|
|
if(isDead())
|
|
{
|
|
return;
|
|
}
|
|
setState(LLSpatialGroup::eSpatialState(DEAD));
|
|
|
|
for (element_iter i = getDataBegin(); i != getDataEnd(); ++i)
|
|
{
|
|
LLViewerOctreeEntry* entry = *i;
|
|
|
|
if (entry->getGroup() == this)
|
|
{
|
|
if(entry->hasDrawable())
|
|
{
|
|
((LLDrawable*)entry->getDrawable())->setGroup(NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
clearDrawMap();
|
|
mVertexBuffer = NULL;
|
|
mBufferVec.clear();
|
|
sZombieGroups++;
|
|
mOctreeNode = NULL;
|
|
}
|
|
|
|
void LLSpatialGroup::handleChildAddition(const OctreeNode* parent, OctreeNode* child)
|
|
{
|
|
if (child->getListenerCount() == 0)
|
|
{
|
|
new LLSpatialGroup(child, getSpatialPartition());
|
|
}
|
|
else
|
|
{
|
|
OCT_ERRS << "LLSpatialGroup redundancy detected." << LL_ENDL;
|
|
}
|
|
|
|
unbound();
|
|
|
|
assert_states_valid(this);
|
|
}
|
|
|
|
void LLSpatialGroup::destroyGL(bool keep_occlusion)
|
|
{
|
|
setState(LLSpatialGroup::GEOM_DIRTY | LLSpatialGroup::IMAGE_DIRTY);
|
|
|
|
if (!keep_occlusion)
|
|
{ //going to need a rebuild
|
|
gPipeline.markRebuild(this, TRUE);
|
|
}
|
|
|
|
mLastUpdateTime = gFrameTimeSeconds;
|
|
mVertexBuffer = NULL;
|
|
mBufferVec.clear();
|
|
|
|
clearDrawMap();
|
|
|
|
if (!keep_occlusion)
|
|
{
|
|
releaseOcclusionQueryObjectNames();
|
|
}
|
|
|
|
|
|
OctreeGuard guard(mOctreeNode);
|
|
for (LLSpatialGroup::element_iter i = getDataBegin(); i != getDataEnd(); ++i)
|
|
{
|
|
LLDrawable* drawable = (LLDrawable*)(*i)->getDrawable();
|
|
if(!drawable)
|
|
{
|
|
continue;
|
|
}
|
|
for (S32 j = 0; j < drawable->getNumFaces(); j++)
|
|
{
|
|
LLFace* facep = drawable->getFace(j);
|
|
if (facep)
|
|
{
|
|
facep->clearVertexBuffer();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//==============================================
|
|
|
|
LLSpatialPartition::LLSpatialPartition(U32 data_mask, BOOL render_by_group, U32 buffer_usage, LLViewerRegion* regionp)
|
|
: mRenderByGroup(render_by_group), mBridge(NULL)
|
|
{
|
|
mRegionp = regionp;
|
|
mPartitionType = LLViewerRegion::PARTITION_NONE;
|
|
mVertexDataMask = data_mask;
|
|
mBufferUsage = buffer_usage;
|
|
mDepthMask = FALSE;
|
|
mSlopRatio = 0.25f;
|
|
mInfiniteFarClip = FALSE;
|
|
|
|
new LLSpatialGroup(mOctree, this);
|
|
}
|
|
|
|
|
|
LLSpatialPartition::~LLSpatialPartition()
|
|
{
|
|
}
|
|
|
|
|
|
LLSpatialGroup *LLSpatialPartition::put(LLDrawable *drawablep, BOOL was_visible)
|
|
{
|
|
drawablep->updateSpatialExtents();
|
|
|
|
//keep drawable from being garbage collected
|
|
LLPointer<LLDrawable> ptr = drawablep;
|
|
|
|
if(!drawablep->getGroup())
|
|
{
|
|
assert_octree_valid(mOctree);
|
|
mOctree->insert(drawablep->getEntry());
|
|
assert_octree_valid(mOctree);
|
|
}
|
|
|
|
LLSpatialGroup* group = drawablep->getSpatialGroup();
|
|
llassert(group != NULL);
|
|
|
|
if (group && was_visible && group->isOcclusionState(LLSpatialGroup::QUERY_PENDING))
|
|
{
|
|
group->setOcclusionState(LLSpatialGroup::DISCARD_QUERY, LLSpatialGroup::STATE_MODE_ALL_CAMERAS);
|
|
}
|
|
|
|
return group;
|
|
}
|
|
|
|
BOOL LLSpatialPartition::remove(LLDrawable *drawablep, LLSpatialGroup *curp)
|
|
{
|
|
if (!curp->removeObject(drawablep))
|
|
{
|
|
OCT_ERRS << "Failed to remove drawable from octree!" << LL_ENDL;
|
|
}
|
|
else
|
|
{
|
|
drawablep->setGroup(NULL);
|
|
}
|
|
|
|
assert_octree_valid(mOctree);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void LLSpatialPartition::move(LLDrawable *drawablep, LLSpatialGroup *curp, BOOL immediate)
|
|
{
|
|
// sanity check submitted by open source user bushing Spatula
|
|
// who was seeing crashing here. (See VWR-424 reported by Bunny Mayne)
|
|
if (!drawablep)
|
|
{
|
|
OCT_ERRS << "LLSpatialPartition::move was passed a bad drawable." << LL_ENDL;
|
|
return;
|
|
}
|
|
|
|
BOOL was_visible = curp ? curp->isVisible() : FALSE;
|
|
|
|
if (curp && curp->getSpatialPartition() != this)
|
|
{
|
|
//keep drawable from being garbage collected
|
|
LLPointer<LLDrawable> ptr = drawablep;
|
|
if (curp->getSpatialPartition()->remove(drawablep, curp))
|
|
{
|
|
put(drawablep, was_visible);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
OCT_ERRS << "Drawable lost between spatial partitions on outbound transition." << LL_ENDL;
|
|
}
|
|
}
|
|
|
|
if (curp && curp->updateInGroup(drawablep, immediate))
|
|
{
|
|
// Already updated, don't need to do anything
|
|
assert_octree_valid(mOctree);
|
|
return;
|
|
}
|
|
|
|
//keep drawable from being garbage collected
|
|
LLPointer<LLDrawable> ptr = drawablep;
|
|
if (curp && !remove(drawablep, curp))
|
|
{
|
|
OCT_ERRS << "Move couldn't find existing spatial group!" << LL_ENDL;
|
|
}
|
|
|
|
put(drawablep, was_visible);
|
|
}
|
|
|
|
class LLSpatialShift : public OctreeTraveler
|
|
{
|
|
public:
|
|
const LLVector4a& mOffset;
|
|
|
|
LLSpatialShift(const LLVector4a& offset) : mOffset(offset) { }
|
|
virtual void visit(const OctreeNode* branch)
|
|
{
|
|
((LLSpatialGroup*) branch->getListener(0))->shift(mOffset);
|
|
}
|
|
};
|
|
|
|
void LLSpatialPartition::shift(const LLVector4a &offset)
|
|
{ //shift octree node bounding boxes by offset
|
|
LLSpatialShift shifter(offset);
|
|
shifter.traverse(mOctree);
|
|
}
|
|
|
|
class LLOctreeCull : public LLViewerOctreeCull
|
|
{
|
|
public:
|
|
LLOctreeCull(LLCamera* camera) : LLViewerOctreeCull(camera) {}
|
|
|
|
virtual bool earlyFail(LLViewerOctreeGroup* base_group)
|
|
{
|
|
LLSpatialGroup* group = (LLSpatialGroup*)base_group;
|
|
group->checkOcclusion();
|
|
|
|
if (group->getOctreeNode()->getParent() && //never occlusion cull the root node
|
|
LLPipeline::sUseOcclusion && //ignore occlusion if disabled
|
|
group->isOcclusionState(LLSpatialGroup::OCCLUDED))
|
|
{
|
|
gPipeline.markOccluder(group);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
virtual S32 frustumCheck(const LLViewerOctreeGroup* group)
|
|
{
|
|
S32 res = AABBInFrustumNoFarClipGroupBounds(group);
|
|
if (res != 0)
|
|
{
|
|
res = llmin(res, AABBSphereIntersectGroupExtents(group));
|
|
}
|
|
return res;
|
|
}
|
|
|
|
virtual S32 frustumCheckObjects(const LLViewerOctreeGroup* group)
|
|
{
|
|
S32 res = AABBInFrustumNoFarClipObjectBounds(group);
|
|
if (res != 0)
|
|
{
|
|
res = llmin(res, AABBSphereIntersectObjectExtents(group));
|
|
}
|
|
return res;
|
|
}
|
|
|
|
virtual void processGroup(LLViewerOctreeGroup* base_group)
|
|
{
|
|
LLSpatialGroup* group = (LLSpatialGroup*)base_group;
|
|
if (group->needsUpdate() ||
|
|
group->getVisible(LLViewerCamera::sCurCameraID) < LLDrawable::getCurrentFrame() - 1)
|
|
{
|
|
group->doOcclusion(mCamera);
|
|
}
|
|
gPipeline.markNotCulled(group, *mCamera);
|
|
}
|
|
};
|
|
|
|
class LLOctreeCullNoFarClip : public LLOctreeCull
|
|
{
|
|
public:
|
|
LLOctreeCullNoFarClip(LLCamera* camera)
|
|
: LLOctreeCull(camera) { }
|
|
|
|
virtual S32 frustumCheck(const LLViewerOctreeGroup* group)
|
|
{
|
|
return AABBInFrustumNoFarClipGroupBounds(group);
|
|
}
|
|
|
|
virtual S32 frustumCheckObjects(const LLViewerOctreeGroup* group)
|
|
{
|
|
S32 res = AABBInFrustumNoFarClipObjectBounds(group);
|
|
return res;
|
|
}
|
|
};
|
|
|
|
class LLOctreeCullShadow : public LLOctreeCull
|
|
{
|
|
public:
|
|
LLOctreeCullShadow(LLCamera* camera)
|
|
: LLOctreeCull(camera) { }
|
|
|
|
virtual S32 frustumCheck(const LLViewerOctreeGroup* group)
|
|
{
|
|
return AABBInFrustumGroupBounds(group);
|
|
}
|
|
|
|
virtual S32 frustumCheckObjects(const LLViewerOctreeGroup* group)
|
|
{
|
|
return AABBInFrustumObjectBounds(group);
|
|
}
|
|
};
|
|
|
|
class LLOctreeCullVisExtents: public LLOctreeCullShadow
|
|
{
|
|
public:
|
|
LLOctreeCullVisExtents(LLCamera* camera, LLVector4a& min, LLVector4a& max)
|
|
: LLOctreeCullShadow(camera), mMin(min), mMax(max), mEmpty(TRUE) { }
|
|
|
|
virtual bool earlyFail(LLViewerOctreeGroup* base_group)
|
|
{
|
|
LLSpatialGroup* group = (LLSpatialGroup*)base_group;
|
|
|
|
if (group->getOctreeNode()->getParent() && //never occlusion cull the root node
|
|
LLPipeline::sUseOcclusion && //ignore occlusion if disabled
|
|
group->isOcclusionState(LLSpatialGroup::OCCLUDED))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
virtual void traverse(const OctreeNode* n)
|
|
{
|
|
LLSpatialGroup* group = (LLSpatialGroup*) n->getListener(0);
|
|
|
|
if (earlyFail(group))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ((mRes && group->hasState(LLSpatialGroup::eSpatialState(LLSpatialGroup::SKIP_FRUSTUM_CHECK))) ||
|
|
mRes == 2)
|
|
{ //don't need to do frustum check
|
|
OctreeTraveler::traverse(n);
|
|
}
|
|
else
|
|
{
|
|
mRes = frustumCheck(group);
|
|
|
|
if (mRes)
|
|
{ //at least partially in, run on down
|
|
OctreeTraveler::traverse(n);
|
|
}
|
|
|
|
mRes = 0;
|
|
}
|
|
}
|
|
|
|
virtual void processGroup(LLViewerOctreeGroup* base_group)
|
|
{
|
|
LLSpatialGroup* group = (LLSpatialGroup*)base_group;
|
|
|
|
llassert(!group->hasState(LLSpatialGroup::eSpatialState(LLSpatialGroup::DIRTY)) && !group->isEmpty());
|
|
|
|
if (mRes < 2)
|
|
{
|
|
if (AABBInFrustumObjectBounds(group) > 0)
|
|
{
|
|
mEmpty = FALSE;
|
|
const LLVector4a* exts = group->getObjectExtents();
|
|
update_min_max(mMin, mMax, exts[0]);
|
|
update_min_max(mMin, mMax, exts[1]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mEmpty = FALSE;
|
|
const LLVector4a* exts = group->getExtents();
|
|
update_min_max(mMin, mMax, exts[0]);
|
|
update_min_max(mMin, mMax, exts[1]);
|
|
}
|
|
}
|
|
|
|
BOOL mEmpty;
|
|
LLVector4a& mMin;
|
|
LLVector4a& mMax;
|
|
};
|
|
|
|
class LLOctreeCullDetectVisible: public LLOctreeCullShadow
|
|
{
|
|
public:
|
|
LLOctreeCullDetectVisible(LLCamera* camera)
|
|
: LLOctreeCullShadow(camera), mResult(FALSE) { }
|
|
|
|
virtual bool earlyFail(LLViewerOctreeGroup* base_group)
|
|
{
|
|
LLSpatialGroup* group = (LLSpatialGroup*)base_group;
|
|
|
|
if (mResult || //already found a node, don't check any more
|
|
(group->getOctreeNode()->getParent() && //never occlusion cull the root node
|
|
LLPipeline::sUseOcclusion && //ignore occlusion if disabled
|
|
group->isOcclusionState(LLSpatialGroup::OCCLUDED)))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
virtual void processGroup(LLViewerOctreeGroup* base_group)
|
|
{
|
|
if (base_group->isVisible())
|
|
{
|
|
mResult = TRUE;
|
|
}
|
|
}
|
|
|
|
BOOL mResult;
|
|
};
|
|
|
|
class LLOctreeSelect : public LLOctreeCull
|
|
{
|
|
public:
|
|
LLOctreeSelect(LLCamera* camera, std::vector<LLDrawable*>* results)
|
|
: LLOctreeCull(camera), mResults(results) { }
|
|
|
|
virtual bool earlyFail(LLViewerOctreeGroup* group) { return false; }
|
|
virtual void preprocess(LLViewerOctreeGroup* group) { }
|
|
|
|
virtual void processGroup(LLViewerOctreeGroup* base_group)
|
|
{
|
|
LLSpatialGroup* group = (LLSpatialGroup*)base_group;
|
|
OctreeNode* branch = group->getOctreeNode();
|
|
|
|
for (OctreeNode::const_element_iter i = branch->getDataBegin(); i != branch->getDataEnd(); ++i)
|
|
{
|
|
LLDrawable* drawable = (LLDrawable*)(*i)->getDrawable();
|
|
if(!drawable)
|
|
{
|
|
continue;
|
|
}
|
|
if (!drawable->isDead())
|
|
{
|
|
if (drawable->isSpatialBridge())
|
|
{
|
|
drawable->setVisible(*mCamera, mResults, TRUE);
|
|
}
|
|
else
|
|
{
|
|
mResults->push_back(drawable);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
std::vector<LLDrawable*>* mResults;
|
|
};
|
|
|
|
void drawBox(const LLVector3& c, const LLVector3& r)
|
|
{
|
|
LLVertexBuffer::unbind();
|
|
|
|
gGL.begin(LLRender::TRIANGLE_STRIP);
|
|
//left front
|
|
gGL.vertex3fv((c+r.scaledVec(LLVector3(-1,1,-1))).mV);
|
|
gGL.vertex3fv((c+r.scaledVec(LLVector3(-1,1,1))).mV);
|
|
//right front
|
|
gGL.vertex3fv((c+r.scaledVec(LLVector3(1,1,-1))).mV);
|
|
gGL.vertex3fv((c+r.scaledVec(LLVector3(1,1,1))).mV);
|
|
//right back
|
|
gGL.vertex3fv((c+r.scaledVec(LLVector3(1,-1,-1))).mV);
|
|
gGL.vertex3fv((c+r.scaledVec(LLVector3(1,-1,1))).mV);
|
|
//left back
|
|
gGL.vertex3fv((c+r.scaledVec(LLVector3(-1,-1,-1))).mV);
|
|
gGL.vertex3fv((c+r.scaledVec(LLVector3(-1,-1,1))).mV);
|
|
//left front
|
|
gGL.vertex3fv((c+r.scaledVec(LLVector3(-1,1,-1))).mV);
|
|
gGL.vertex3fv((c+r.scaledVec(LLVector3(-1,1,1))).mV);
|
|
gGL.end();
|
|
|
|
//bottom
|
|
gGL.begin(LLRender::TRIANGLE_STRIP);
|
|
gGL.vertex3fv((c+r.scaledVec(LLVector3(1,1,-1))).mV);
|
|
gGL.vertex3fv((c+r.scaledVec(LLVector3(1,-1,-1))).mV);
|
|
gGL.vertex3fv((c+r.scaledVec(LLVector3(-1,1,-1))).mV);
|
|
gGL.vertex3fv((c+r.scaledVec(LLVector3(-1,-1,-1))).mV);
|
|
gGL.end();
|
|
|
|
//top
|
|
gGL.begin(LLRender::TRIANGLE_STRIP);
|
|
gGL.vertex3fv((c+r.scaledVec(LLVector3(1,1,1))).mV);
|
|
gGL.vertex3fv((c+r.scaledVec(LLVector3(-1,1,1))).mV);
|
|
gGL.vertex3fv((c+r.scaledVec(LLVector3(1,-1,1))).mV);
|
|
gGL.vertex3fv((c+r.scaledVec(LLVector3(-1,-1,1))).mV);
|
|
gGL.end();
|
|
}
|
|
|
|
void drawBox(const LLVector4a& c, const LLVector4a& r)
|
|
{
|
|
drawBox(reinterpret_cast<const LLVector3&>(c), reinterpret_cast<const LLVector3&>(r));
|
|
}
|
|
|
|
void drawBoxOutline(const LLVector3& pos, const LLVector3& size)
|
|
{
|
|
LLVector3 v1 = size.scaledVec(LLVector3( 1, 1,1));
|
|
LLVector3 v2 = size.scaledVec(LLVector3(-1, 1,1));
|
|
LLVector3 v3 = size.scaledVec(LLVector3(-1,-1,1));
|
|
LLVector3 v4 = size.scaledVec(LLVector3( 1,-1,1));
|
|
|
|
gGL.begin(LLRender::LINES);
|
|
|
|
//top
|
|
gGL.vertex3fv((pos+v1).mV);
|
|
gGL.vertex3fv((pos+v2).mV);
|
|
gGL.vertex3fv((pos+v2).mV);
|
|
gGL.vertex3fv((pos+v3).mV);
|
|
gGL.vertex3fv((pos+v3).mV);
|
|
gGL.vertex3fv((pos+v4).mV);
|
|
gGL.vertex3fv((pos+v4).mV);
|
|
gGL.vertex3fv((pos+v1).mV);
|
|
|
|
//bottom
|
|
gGL.vertex3fv((pos-v1).mV);
|
|
gGL.vertex3fv((pos-v2).mV);
|
|
gGL.vertex3fv((pos-v2).mV);
|
|
gGL.vertex3fv((pos-v3).mV);
|
|
gGL.vertex3fv((pos-v3).mV);
|
|
gGL.vertex3fv((pos-v4).mV);
|
|
gGL.vertex3fv((pos-v4).mV);
|
|
gGL.vertex3fv((pos-v1).mV);
|
|
|
|
//right
|
|
gGL.vertex3fv((pos+v1).mV);
|
|
gGL.vertex3fv((pos-v3).mV);
|
|
|
|
gGL.vertex3fv((pos+v4).mV);
|
|
gGL.vertex3fv((pos-v2).mV);
|
|
|
|
//left
|
|
gGL.vertex3fv((pos+v2).mV);
|
|
gGL.vertex3fv((pos-v4).mV);
|
|
|
|
gGL.vertex3fv((pos+v3).mV);
|
|
gGL.vertex3fv((pos-v1).mV);
|
|
|
|
gGL.end();
|
|
}
|
|
|
|
void drawBoxOutline(const LLVector4a& pos, const LLVector4a& size)
|
|
{
|
|
drawBoxOutline(reinterpret_cast<const LLVector3&>(pos), reinterpret_cast<const LLVector3&>(size));
|
|
}
|
|
|
|
class LLOctreeDirty : public OctreeTraveler
|
|
{
|
|
public:
|
|
LLOctreeDirty(bool no_rebuild = false) : mNoRebuild(no_rebuild){}
|
|
|
|
virtual void visit(const OctreeNode* state)
|
|
{
|
|
LLSpatialGroup* group = (LLSpatialGroup*) state->getListener(0);
|
|
group->destroyGL();
|
|
|
|
{OctreeGuard guard(group->getOctreeNode());
|
|
for (LLSpatialGroup::element_iter i = group->getDataBegin(); i != group->getDataEnd(); ++i)
|
|
{
|
|
LLDrawable* drawable = (LLDrawable*)(*i)->getDrawable();
|
|
if(!drawable)
|
|
{
|
|
continue;
|
|
}
|
|
if (!mNoRebuild && drawable->getVObj().notNull() && !group->getSpatialPartition()->mRenderByGroup)
|
|
{
|
|
gPipeline.markRebuild(drawable, LLDrawable::REBUILD_ALL, TRUE);
|
|
}
|
|
}
|
|
|
|
for (LLSpatialGroup::bridge_list_t::iterator i = group->mBridgeList.begin(); i != group->mBridgeList.end(); ++i)
|
|
{
|
|
LLSpatialBridge* bridge = *i;
|
|
traverse(bridge->mOctree);
|
|
}}
|
|
}
|
|
|
|
private:
|
|
BOOL mNoRebuild;
|
|
};
|
|
|
|
void LLSpatialPartition::restoreGL()
|
|
{
|
|
}
|
|
|
|
void LLSpatialPartition::resetVertexBuffers()
|
|
{
|
|
LLOctreeDirty dirty(sTeleportRequested);
|
|
dirty.traverse(mOctree);
|
|
}
|
|
|
|
BOOL LLSpatialPartition::getVisibleExtents(LLCamera& camera, LLVector3& visMin, LLVector3& visMax)
|
|
{
|
|
LLVector4a visMina, visMaxa;
|
|
visMina.load3(visMin.mV);
|
|
visMaxa.load3(visMax.mV);
|
|
|
|
{
|
|
LL_RECORD_BLOCK_TIME(FTM_CULL_REBOUND);
|
|
LLSpatialGroup* group = (LLSpatialGroup*) mOctree->getListener(0);
|
|
group->rebound();
|
|
}
|
|
|
|
LLOctreeCullVisExtents vis(&camera, visMina, visMaxa);
|
|
vis.traverse(mOctree);
|
|
|
|
visMin.set(visMina.getF32ptr());
|
|
visMax.set(visMaxa.getF32ptr());
|
|
return vis.mEmpty;
|
|
}
|
|
|
|
BOOL LLSpatialPartition::visibleObjectsInFrustum(LLCamera& camera)
|
|
{
|
|
LLOctreeCullDetectVisible vis(&camera);
|
|
vis.traverse(mOctree);
|
|
return vis.mResult;
|
|
}
|
|
|
|
S32 LLSpatialPartition::cull(LLCamera &camera, std::vector<LLDrawable *>* results)
|
|
{
|
|
#if LL_OCTREE_PARANOIA_CHECK
|
|
((LLSpatialGroup*)mOctree->getListener(0))->checkStates();
|
|
#endif
|
|
{
|
|
//BOOL temp = sFreezeState;
|
|
//sFreezeState = FALSE;
|
|
LL_RECORD_BLOCK_TIME(FTM_CULL_REBOUND);
|
|
LLSpatialGroup* group = (LLSpatialGroup*) mOctree->getListener(0);
|
|
group->rebound();
|
|
//sFreezeState = temp;
|
|
}
|
|
|
|
#if LL_OCTREE_PARANOIA_CHECK
|
|
((LLSpatialGroup*)mOctree->getListener(0))->validate();
|
|
#endif
|
|
|
|
LLOctreeSelect selecter(&camera, results);
|
|
selecter.traverse(mOctree);
|
|
|
|
return 0;
|
|
}
|
|
S32 LLSpatialPartition::cull(LLCamera &camera, bool do_occlusion)
|
|
{
|
|
#if LL_OCTREE_PARANOIA_CHECK
|
|
((LLSpatialGroup*)mOctree->getListener(0))->checkStates();
|
|
#endif
|
|
{
|
|
LL_RECORD_BLOCK_TIME(FTM_CULL_REBOUND);
|
|
LLSpatialGroup* group = (LLSpatialGroup*) mOctree->getListener(0);
|
|
group->rebound();
|
|
}
|
|
|
|
#if LL_OCTREE_PARANOIA_CHECK
|
|
((LLSpatialGroup*)mOctree->getListener(0))->validate();
|
|
#endif
|
|
|
|
if (LLPipeline::sShadowRender)
|
|
{
|
|
LL_RECORD_BLOCK_TIME(FTM_FRUSTUM_CULL);
|
|
LLOctreeCullShadow culler(&camera);
|
|
culler.traverse(mOctree);
|
|
}
|
|
else if (mInfiniteFarClip || !LLPipeline::sUseFarClip)
|
|
{
|
|
LL_RECORD_BLOCK_TIME(FTM_FRUSTUM_CULL);
|
|
LLOctreeCullNoFarClip culler(&camera);
|
|
culler.traverse(mOctree);
|
|
}
|
|
else
|
|
{
|
|
LL_RECORD_BLOCK_TIME(FTM_FRUSTUM_CULL);
|
|
LLOctreeCull culler(&camera);
|
|
culler.traverse(mOctree);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void pushVerts(LLDrawInfo* params, U32 mask)
|
|
{
|
|
LLRenderPass::applyModelMatrix(*params);
|
|
params->mVertexBuffer->setBuffer(mask);
|
|
params->mVertexBuffer->drawRange(params->mParticle ? LLRender::POINTS : LLRender::TRIANGLES,
|
|
params->mStart, params->mEnd, params->mCount, params->mOffset);
|
|
}
|
|
|
|
void pushVerts(LLSpatialGroup* group, U32 mask)
|
|
{
|
|
LLDrawInfo* params = NULL;
|
|
|
|
for (LLSpatialGroup::draw_map_t::iterator i = group->mDrawMap.begin(); i != group->mDrawMap.end(); ++i)
|
|
{
|
|
for (LLSpatialGroup::drawmap_elem_t::iterator j = i->second.begin(); j != i->second.end(); ++j)
|
|
{
|
|
params = *j;
|
|
pushVerts(params, mask);
|
|
}
|
|
}
|
|
}
|
|
|
|
void pushVerts(LLFace* face, U32 mask)
|
|
{
|
|
if (face)
|
|
{
|
|
llassert(face->verify());
|
|
|
|
LLVertexBuffer* buffer = face->getVertexBuffer();
|
|
|
|
if (buffer && (face->getGeomCount() >= 3))
|
|
{
|
|
buffer->setBuffer(mask);
|
|
U16 start = face->getGeomStart();
|
|
U16 end = start + face->getGeomCount()-1;
|
|
U32 count = face->getIndicesCount();
|
|
U16 offset = face->getIndicesStart();
|
|
buffer->drawRange(LLRender::TRIANGLES, start, end, count, offset);
|
|
}
|
|
}
|
|
}
|
|
|
|
void pushVerts(LLDrawable* drawable, U32 mask)
|
|
{
|
|
for (S32 i = 0; i < drawable->getNumFaces(); ++i)
|
|
{
|
|
pushVerts(drawable->getFace(i), mask);
|
|
}
|
|
}
|
|
|
|
void pushVerts(LLVolume* volume)
|
|
{
|
|
LLVertexBuffer::unbind();
|
|
for (S32 i = 0; i < volume->getNumVolumeFaces(); ++i)
|
|
{
|
|
const LLVolumeFace& face = volume->getVolumeFace(i);
|
|
LLVertexBuffer::drawElements(LLRender::TRIANGLES, face.mNumVertices, face.mPositions, NULL, face.mNumIndices, face.mIndices);
|
|
}
|
|
}
|
|
|
|
void pushBufferVerts(LLVertexBuffer* buffer, U32 mask)
|
|
{
|
|
if (buffer)
|
|
{
|
|
buffer->setBuffer(mask);
|
|
buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0);
|
|
}
|
|
}
|
|
|
|
void pushBufferVerts(LLSpatialGroup* group, U32 mask, bool push_alpha = true)
|
|
{
|
|
if (group->getSpatialPartition()->mRenderByGroup)
|
|
{
|
|
if (!group->mDrawMap.empty() && !group->mDrawMap.begin()->second.empty())
|
|
{
|
|
LLDrawInfo* params = *(group->mDrawMap.begin()->second.begin());
|
|
LLRenderPass::applyModelMatrix(*params);
|
|
|
|
if (push_alpha)
|
|
{
|
|
pushBufferVerts(group->mVertexBuffer, mask);
|
|
}
|
|
|
|
for (LLSpatialGroup::buffer_vec_t::iterator i = group->mBufferVec.begin(); i != group->mBufferVec.end(); ++i)
|
|
{
|
|
for (LLSpatialGroup::buffer_texture_vec_t::iterator j = i->second.begin(); j != i->second.end(); ++j)
|
|
{
|
|
for (LLSpatialGroup::buffer_list_t::iterator k = j->second.begin(); k != j->second.end(); ++k)
|
|
{
|
|
pushBufferVerts(*k, mask);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const LLVector4a* bounds = group->getBounds();
|
|
drawBox(bounds[0], bounds[1]);
|
|
}
|
|
}
|
|
|
|
void pushVertsColorCoded(LLSpatialGroup* group, U32 mask)
|
|
{
|
|
LLDrawInfo* params = NULL;
|
|
|
|
static LLColor4 colors[] = {
|
|
LLColor4::green,
|
|
LLColor4::green1,
|
|
LLColor4::green2,
|
|
LLColor4::green3,
|
|
LLColor4::green4,
|
|
LLColor4::green5,
|
|
LLColor4::green6
|
|
};
|
|
|
|
static const U32 col_count = LL_ARRAY_SIZE(colors);
|
|
|
|
U32 col = 0;
|
|
|
|
for (LLSpatialGroup::draw_map_t::iterator i = group->mDrawMap.begin(); i != group->mDrawMap.end(); ++i)
|
|
{
|
|
for (LLSpatialGroup::drawmap_elem_t::iterator j = i->second.begin(); j != i->second.end(); ++j)
|
|
{
|
|
params = *j;
|
|
LLRenderPass::applyModelMatrix(*params);
|
|
gGL.diffuseColor4f(colors[col].mV[0], colors[col].mV[1], colors[col].mV[2], 0.5f);
|
|
params->mVertexBuffer->setBuffer(mask);
|
|
params->mVertexBuffer->drawRange(params->mParticle ? LLRender::POINTS : LLRender::TRIANGLES,
|
|
params->mStart, params->mEnd, params->mCount, params->mOffset);
|
|
col = (col+1)%col_count;
|
|
}
|
|
}
|
|
}
|
|
|
|
void renderOctree(LLSpatialGroup* group)
|
|
{
|
|
//render solid object bounding box, color
|
|
//coded by buffer usage and activity
|
|
LLGLDepthTest depth(GL_TRUE, GL_FALSE);
|
|
gGL.setSceneBlendType(LLRender::BT_ADD_WITH_ALPHA);
|
|
LLVector4 col;
|
|
if (group->mBuilt > 0.f)
|
|
{
|
|
group->mBuilt -= 2.f * gFrameIntervalSeconds;
|
|
if (group->mBufferUsage == GL_STATIC_DRAW_ARB)
|
|
{
|
|
col.setVec(1.0f, 0, 0, group->mBuilt*0.5f);
|
|
}
|
|
else
|
|
{
|
|
col.setVec(0.1f,0.1f,1,0.1f);
|
|
//col.setVec(1.0f, 1.0f, 0, sinf(group->mBuilt*3.14159f)*0.5f);
|
|
}
|
|
|
|
if (group->mBufferUsage != GL_STATIC_DRAW_ARB)
|
|
{
|
|
LLGLDepthTest gl_depth(FALSE, FALSE);
|
|
gGL.setPolygonMode(LLRender::PF_FRONT_AND_BACK, LLRender::PM_LINE);
|
|
|
|
gGL.diffuseColor4f(1,0,0,group->mBuilt);
|
|
gGL.setLineWidth(5.f);
|
|
|
|
const LLVector4a* bounds = group->getObjectBounds();
|
|
drawBoxOutline(bounds[0], bounds[1]);
|
|
gGL.setLineWidth(1.f);
|
|
|
|
OctreeGuard guard(group->getOctreeNode());
|
|
for (LLSpatialGroup::element_iter i = group->getDataBegin(); i != group->getDataEnd(); ++i)
|
|
{
|
|
LLDrawable* drawable = (LLDrawable*)(*i)->getDrawable();
|
|
if(!drawable)
|
|
{
|
|
continue;
|
|
}
|
|
if (!group->getSpatialPartition()->isBridge())
|
|
{
|
|
gGL.pushMatrix();
|
|
LLVector3 trans = drawable->getRegion()->getOriginAgent();
|
|
gGL.translatef(trans.mV[0], trans.mV[1], trans.mV[2]);
|
|
}
|
|
|
|
for (S32 j = 0; j < drawable->getNumFaces(); j++)
|
|
{
|
|
LLFace* face = drawable->getFace(j);
|
|
if (face && face->getVertexBuffer())
|
|
{
|
|
if (gFrameTimeSeconds - face->mLastUpdateTime < 0.5f)
|
|
{
|
|
gGL.diffuseColor4f(0, 1, 0, group->mBuilt);
|
|
}
|
|
else if (gFrameTimeSeconds - face->mLastMoveTime < 0.5f)
|
|
{
|
|
gGL.diffuseColor4f(1, 0, 0, group->mBuilt);
|
|
}
|
|
else
|
|
{
|
|
continue;
|
|
}
|
|
|
|
face->getVertexBuffer()->setBuffer(LLVertexBuffer::MAP_VERTEX);
|
|
//drawBox((face->mExtents[0] + face->mExtents[1])*0.5f,
|
|
// (face->mExtents[1]-face->mExtents[0])*0.5f);
|
|
face->getVertexBuffer()->draw(LLRender::TRIANGLES, face->getIndicesCount(), face->getIndicesStart());
|
|
}
|
|
}
|
|
|
|
if (!group->getSpatialPartition()->isBridge())
|
|
{
|
|
gGL.popMatrix();
|
|
}
|
|
}
|
|
gGL.setPolygonMode(LLRender::PF_FRONT_AND_BACK, LLRender::PM_FILL);
|
|
gGL.diffuseColor4f(1,1,1,1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (group->mBufferUsage == GL_STATIC_DRAW_ARB && !group->isEmpty()
|
|
&& group->getSpatialPartition()->mRenderByGroup)
|
|
{
|
|
col.setVec(0.8f, 0.4f, 0.1f, 0.1f);
|
|
}
|
|
else
|
|
{
|
|
col.setVec(0.1f, 0.1f, 1.f, 0.1f);
|
|
}
|
|
}
|
|
|
|
gGL.diffuseColor4fv(col.mV);
|
|
LLVector4a fudge;
|
|
fudge.splat(0.001f);
|
|
const LLVector4a* bounds = group->getObjectBounds();
|
|
LLVector4a size = bounds[1];
|
|
size.mul(1.01f);
|
|
size.add(fudge);
|
|
|
|
//{
|
|
// LLGLDepthTest depth(GL_TRUE, GL_FALSE);
|
|
// drawBox(group->mObjectBounds[0], fudge);
|
|
//}
|
|
|
|
gGL.setSceneBlendType(LLRender::BT_ALPHA);
|
|
|
|
//if (group->mBuilt <= 0.f)
|
|
{
|
|
//draw opaque outline
|
|
//gGL.diffuseColor4f(col.mV[0], col.mV[1], col.mV[2], 1.f);
|
|
//drawBoxOutline(group->mObjectBounds[0], group->mObjectBounds[1]);
|
|
|
|
gGL.diffuseColor4f(0,1,1,1);
|
|
|
|
const LLVector4a* bounds = group->getBounds();
|
|
drawBoxOutline(bounds[0], bounds[1]);
|
|
|
|
//draw bounding box for draw info
|
|
/*if (group->mSpatialPartition->mRenderByGroup)
|
|
{
|
|
gGL.diffuseColor4f(1.0f, 0.75f, 0.25f, 0.6f);
|
|
for (LLSpatialGroup::draw_map_t::iterator i = group->mDrawMap.begin(); i != group->mDrawMap.end(); ++i)
|
|
{
|
|
for (LLSpatialGroup::drawmap_elem_t::iterator j = i->second.begin(); j != i->second.end(); ++j)
|
|
{
|
|
LLDrawInfo* draw_info = *j;
|
|
LLVector4a center;
|
|
center.setAdd(draw_info->mExtents[1], draw_info->mExtents[0]);
|
|
center.mul(0.5f);
|
|
LLVector4a size;
|
|
size.setSub(draw_info->mExtents[1], draw_info->mExtents[0]);
|
|
size.mul(0.5f);
|
|
drawBoxOutline(center, size);
|
|
}
|
|
}
|
|
}*/
|
|
}
|
|
|
|
// OctreeNode* node = group->mOctreeNode;
|
|
// gGL.color4f(0,1,0,1);
|
|
// drawBoxOutline(LLVector3(node->getCenter()), LLVector3(node->getSize()));
|
|
}
|
|
|
|
void renderVisibility(LLSpatialGroup* group, LLCamera* camera)
|
|
{
|
|
LLGLEnable<GL_BLEND> blend;
|
|
gGL.setSceneBlendType(LLRender::BT_ALPHA);
|
|
LLGLEnable<GL_CULL_FACE> cull;
|
|
gGL.setPolygonMode(LLRender::PF_FRONT_AND_BACK, LLRender::PM_LINE);
|
|
|
|
BOOL render_objects = (!LLPipeline::sUseOcclusion || !group->isOcclusionState(LLSpatialGroup::OCCLUDED)) && group->isVisible() &&
|
|
!group->isEmpty();
|
|
|
|
if (render_objects)
|
|
{
|
|
LLGLDepthTest depth_under(GL_TRUE, GL_FALSE, GL_GREATER);
|
|
gGL.diffuseColor4f(0, 0.5f, 0, 0.5f);
|
|
pushBufferVerts(group, LLVertexBuffer::MAP_VERTEX);
|
|
}
|
|
|
|
{
|
|
LLGLDepthTest depth_over(GL_TRUE, GL_FALSE, GL_LEQUAL);
|
|
|
|
if (render_objects)
|
|
{
|
|
gGL.diffuseColor4f(0.f, 0.5f, 0.f,1.f);
|
|
pushBufferVerts(group, LLVertexBuffer::MAP_VERTEX);
|
|
}
|
|
|
|
gGL.setPolygonMode(LLRender::PF_FRONT_AND_BACK, LLRender::PM_FILL);
|
|
|
|
if (render_objects)
|
|
{
|
|
gGL.diffuseColor4f(0.f, 0.75f, 0.f,0.5f);
|
|
pushBufferVerts(group, LLVertexBuffer::MAP_VERTEX);
|
|
}
|
|
/*else if (camera && group->mOcclusionVerts)
|
|
{
|
|
LLVertexBuffer::unbind();
|
|
glVertexPointer(3, GL_FLOAT, 0, group->mOcclusionVerts);
|
|
|
|
gGL.diffuseColor4f(1.0f, 0.f, 0.f, 0.5f);
|
|
glDrawRangeElements(GL_TRIANGLE_FAN, 0, 7, 8, GL_UNSIGNED_BYTE, get_box_fan_indices(camera, group->mBounds[0]));
|
|
gGL.setPolygonMode(LLRender::PF_FRONT_AND_BACK, LLRender::PM_LINE);
|
|
|
|
gGL.diffuseColor4f(1.0f, 1.f, 1.f, 1.0f);
|
|
glDrawRangeElements(GL_TRIANGLE_FAN, 0, 7, 8, GL_UNSIGNED_BYTE, get_box_fan_indices(camera, group->mBounds[0]));
|
|
gGL.setPolygonMode(LLRender::PF_FRONT_AND_BACK, LLRender::PM_FILL);
|
|
}*/
|
|
}
|
|
}
|
|
|
|
void renderCrossHairs(LLVector3 position, F32 size, LLColor4 color)
|
|
{
|
|
gGL.diffuseColor4fv(color.mV);
|
|
gGL.begin(LLRender::LINES);
|
|
{
|
|
gGL.vertex3fv((position - LLVector3(size, 0.f, 0.f)).mV);
|
|
gGL.vertex3fv((position + LLVector3(size, 0.f, 0.f)).mV);
|
|
gGL.vertex3fv((position - LLVector3(0.f, size, 0.f)).mV);
|
|
gGL.vertex3fv((position + LLVector3(0.f, size, 0.f)).mV);
|
|
gGL.vertex3fv((position - LLVector3(0.f, 0.f, size)).mV);
|
|
gGL.vertex3fv((position + LLVector3(0.f, 0.f, size)).mV);
|
|
}
|
|
gGL.end();
|
|
}
|
|
|
|
void renderUpdateType(LLDrawable* drawablep)
|
|
{
|
|
LLViewerObject* vobj = drawablep->getVObj();
|
|
if (!vobj || OUT_UNKNOWN == vobj->getLastUpdateType())
|
|
{
|
|
return;
|
|
}
|
|
LLGLEnable<GL_BLEND> blend;
|
|
switch (vobj->getLastUpdateType())
|
|
{
|
|
case OUT_FULL:
|
|
gGL.diffuseColor4f(0,1,0,0.5f);
|
|
break;
|
|
case OUT_TERSE_IMPROVED:
|
|
gGL.diffuseColor4f(0,1,1,0.5f);
|
|
break;
|
|
case OUT_FULL_COMPRESSED:
|
|
if (vobj->getLastUpdateCached())
|
|
{
|
|
gGL.diffuseColor4f(1,0,0,0.5f);
|
|
}
|
|
else
|
|
{
|
|
gGL.diffuseColor4f(1,1,0,0.5f);
|
|
}
|
|
break;
|
|
case OUT_FULL_CACHED:
|
|
gGL.diffuseColor4f(0,0,1,0.5f);
|
|
break;
|
|
default:
|
|
LL_WARNS() << "Unknown update_type " << vobj->getLastUpdateType() << LL_ENDL;
|
|
break;
|
|
};
|
|
S32 num_faces = drawablep->getNumFaces();
|
|
if (num_faces)
|
|
{
|
|
for (S32 i = 0; i < num_faces; ++i)
|
|
{
|
|
pushVerts(drawablep->getFace(i), LLVertexBuffer::MAP_VERTEX);
|
|
}
|
|
}
|
|
}
|
|
|
|
void renderComplexityDisplay(LLDrawable* drawablep)
|
|
{
|
|
LLViewerObject* vobj = drawablep->getVObj();
|
|
if (!vobj)
|
|
{
|
|
return;
|
|
}
|
|
|
|
LLVOVolume *voVol = dynamic_cast<LLVOVolume*>(vobj);
|
|
|
|
if (!voVol)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!voVol->isRoot())
|
|
{
|
|
return;
|
|
}
|
|
|
|
LLVOVolume::texture_cost_t textures;
|
|
F32 cost = (F32) voVol->getRenderCost(textures);
|
|
|
|
// add any child volumes
|
|
LLViewerObject::const_child_list_t children = voVol->getChildren();
|
|
for (LLViewerObject::const_child_list_t::const_iterator iter = children.begin(); iter != children.end(); ++iter)
|
|
{
|
|
const LLViewerObject *child = *iter;
|
|
const LLVOVolume *child_volume = dynamic_cast<const LLVOVolume*>(child);
|
|
if (child_volume)
|
|
{
|
|
cost += child_volume->getRenderCost(textures);
|
|
}
|
|
}
|
|
|
|
// add texture cost
|
|
for (LLVOVolume::texture_cost_t::iterator iter = textures.begin(); iter != textures.end(); ++iter)
|
|
{
|
|
// add the cost of each individual texture in the linkset
|
|
cost += iter->second;
|
|
}
|
|
|
|
F32 cost_max = (F32) LLVOVolume::getRenderComplexityMax();
|
|
|
|
|
|
|
|
// allow user to set a static color scale
|
|
if (gSavedSettings.getS32("RenderComplexityStaticMax") > 0)
|
|
{
|
|
cost_max = gSavedSettings.getS32("RenderComplexityStaticMax");
|
|
}
|
|
|
|
F32 cost_ratio = cost / cost_max;
|
|
|
|
// cap cost ratio at 1.0f in case cost_max is at a low threshold
|
|
cost_ratio = cost_ratio > 1.0f ? 1.0f : cost_ratio;
|
|
|
|
LLGLEnable<GL_BLEND> blend;
|
|
|
|
LLColor4 color;
|
|
const LLColor4 color_min = gSavedSettings.getColor4("RenderComplexityColorMin");
|
|
const LLColor4 color_mid = gSavedSettings.getColor4("RenderComplexityColorMid");
|
|
const LLColor4 color_max = gSavedSettings.getColor4("RenderComplexityColorMax");
|
|
|
|
if (cost_ratio < 0.5f)
|
|
{
|
|
color = color_min * (1 - cost_ratio * 2) + color_mid * (cost_ratio * 2);
|
|
}
|
|
else
|
|
{
|
|
color = color_mid * (1 - (cost_ratio - 0.5) * 2) + color_max * ((cost_ratio - 0.5) * 2);
|
|
}
|
|
|
|
LLSD color_val = color.getValue();
|
|
|
|
// don't highlight objects below the threshold
|
|
if (cost > gSavedSettings.getS32("RenderComplexityThreshold"))
|
|
{
|
|
gGL.diffuseColor4f(color[0],color[1],color[2],0.5f);
|
|
|
|
|
|
S32 num_faces = drawablep->getNumFaces();
|
|
if (num_faces)
|
|
{
|
|
for (S32 i = 0; i < num_faces; ++i)
|
|
{
|
|
pushVerts(drawablep->getFace(i), LLVertexBuffer::MAP_VERTEX);
|
|
}
|
|
}
|
|
LLViewerObject::const_child_list_t children = voVol->getChildren();
|
|
for (LLViewerObject::const_child_list_t::const_iterator iter = children.begin(); iter != children.end(); ++iter)
|
|
{
|
|
const LLViewerObject *child = *iter;
|
|
if (child)
|
|
{
|
|
num_faces = child->getNumFaces();
|
|
if (num_faces)
|
|
{
|
|
for (S32 i = 0; i < num_faces; ++i)
|
|
{
|
|
pushVerts(child->mDrawable->getFace(i), LLVertexBuffer::MAP_VERTEX);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
voVol->setDebugText(llformat("%4.0f", cost));
|
|
}
|
|
|
|
void renderBoundingBox(LLDrawable* drawable, BOOL set_color = TRUE)
|
|
{
|
|
if (set_color)
|
|
{
|
|
if (drawable->isSpatialBridge())
|
|
{
|
|
gGL.diffuseColor4f(1,0.5f,0,1);
|
|
}
|
|
else if (drawable->getVOVolume())
|
|
{
|
|
if (drawable->isRoot())
|
|
{
|
|
gGL.diffuseColor4f(1,1,0,1);
|
|
}
|
|
else
|
|
{
|
|
gGL.diffuseColor4f(0,1,0,1);
|
|
}
|
|
}
|
|
else if (drawable->getVObj())
|
|
{
|
|
switch (drawable->getVObj()->getPCode())
|
|
{
|
|
case LLViewerObject::LL_VO_SURFACE_PATCH:
|
|
gGL.diffuseColor4f(0,1,1,1);
|
|
break;
|
|
case LLViewerObject::LL_VO_CLOUDS:
|
|
gGL.diffuseColor4f(0.5f,0.5f,0.5f,1.0f);
|
|
break;
|
|
case LLViewerObject::LL_VO_PART_GROUP:
|
|
case LLViewerObject::LL_VO_HUD_PART_GROUP:
|
|
gGL.diffuseColor4f(0,0,1,1);
|
|
break;
|
|
case LLViewerObject::LL_VO_VOID_WATER:
|
|
case LLViewerObject::LL_VO_WATER:
|
|
gGL.diffuseColor4f(0,0.5f,1,1);
|
|
break;
|
|
case LL_PCODE_LEGACY_TREE:
|
|
gGL.diffuseColor4f(0,0.5f,0,1);
|
|
break;
|
|
default:
|
|
LLControlAvatar *cav = dynamic_cast<LLControlAvatar*>(drawable->getVObj()->asAvatar());
|
|
if (cav)
|
|
{
|
|
bool has_pos_constraint = (cav->mPositionConstraintFixup != LLVector3());
|
|
bool has_scale_constraint = (cav->mScaleConstraintFixup != 1.0f);
|
|
if (has_pos_constraint || has_scale_constraint)
|
|
{
|
|
gGL.diffuseColor4f(1,0,0,1);
|
|
}
|
|
else
|
|
{
|
|
gGL.diffuseColor4f(0,1,0.5,1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
gGL.diffuseColor4f(1,0,1,1); // magenta
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
gGL.diffuseColor4f(1,0,0,1);
|
|
}
|
|
}
|
|
|
|
const LLVector4a* ext;
|
|
LLVector4a pos, size;
|
|
|
|
if (drawable->getVOVolume())
|
|
{
|
|
//render face bounding boxes
|
|
for (S32 i = 0; i < drawable->getNumFaces(); i++)
|
|
{
|
|
LLFace* facep = drawable->getFace(i);
|
|
if (facep)
|
|
{
|
|
ext = facep->mExtents;
|
|
|
|
pos.setAdd(ext[0], ext[1]);
|
|
pos.mul(0.5f);
|
|
size.setSub(ext[1], ext[0]);
|
|
size.mul(0.5f);
|
|
|
|
drawBoxOutline(pos,size);
|
|
}
|
|
}
|
|
}
|
|
|
|
//render drawable bounding box
|
|
ext = drawable->getSpatialExtents();
|
|
|
|
pos.setAdd(ext[0], ext[1]);
|
|
pos.mul(0.5f);
|
|
size.setSub(ext[1], ext[0]);
|
|
size.mul(0.5f);
|
|
|
|
LLViewerObject* vobj = drawable->getVObj();
|
|
if (vobj && vobj->onActiveList())
|
|
{
|
|
gGL.setLineWidth(llmax(4.f*sinf(gFrameTimeSeconds*2.f)+1.f, 1.f));
|
|
//glLineWidth(4.f*(sinf(gFrameTimeSeconds*2.f)*0.25f+0.75f));
|
|
stop_glerror();
|
|
drawBoxOutline(pos,size);
|
|
gGL.setLineWidth(1.f);
|
|
}
|
|
else
|
|
{
|
|
drawBoxOutline(pos,size);
|
|
}
|
|
}
|
|
|
|
void renderNormals(LLDrawable* drawablep)
|
|
{
|
|
LLVertexBuffer::unbind();
|
|
|
|
LLVOVolume* vol = drawablep->getVOVolume();
|
|
if (vol)
|
|
{
|
|
LLVolume* volume = vol->getVolume();
|
|
gGL.pushMatrix();
|
|
gGL.multMatrix(vol->getRelativeXform());
|
|
|
|
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
|
|
|
|
LLVector4a scale(gSavedSettings.getF32("RenderDebugNormalScale"));
|
|
|
|
for (S32 i = 0; i < volume->getNumVolumeFaces(); ++i)
|
|
{
|
|
const LLVolumeFace& face = volume->getVolumeFace(i);
|
|
|
|
for (S32 j = 0; j < face.mNumVertices; ++j)
|
|
{
|
|
gGL.begin(LLRender::LINES);
|
|
LLVector4a n,p;
|
|
|
|
n.setMul(face.mNormals[j], scale);
|
|
p.setAdd(face.mPositions[j], n);
|
|
|
|
gGL.diffuseColor4f(1,1,1,1);
|
|
gGL.vertex3fv(face.mPositions[j].getF32ptr());
|
|
gGL.vertex3fv(p.getF32ptr());
|
|
|
|
if (face.mTangents)
|
|
{
|
|
n.setMul(face.mTangents[j], scale);
|
|
p.setAdd(face.mPositions[j], n);
|
|
|
|
gGL.diffuseColor4f(0,1,1,1);
|
|
gGL.vertex3fv(face.mPositions[j].getF32ptr());
|
|
gGL.vertex3fv(p.getF32ptr());
|
|
}
|
|
gGL.end();
|
|
}
|
|
}
|
|
|
|
gGL.popMatrix();
|
|
}
|
|
}
|
|
|
|
S32 get_physics_detail(const LLVolumeParams& volume_params, const LLVector3& scale)
|
|
{
|
|
const S32 DEFAULT_DETAIL = 1;
|
|
const F32 LARGE_THRESHOLD = 5.f;
|
|
const F32 MEGA_THRESHOLD = 25.f;
|
|
|
|
S32 detail = DEFAULT_DETAIL;
|
|
F32 avg_scale = (scale[0]+scale[1]+scale[2])/3.f;
|
|
|
|
if (avg_scale > LARGE_THRESHOLD)
|
|
{
|
|
detail += 1;
|
|
if (avg_scale > MEGA_THRESHOLD)
|
|
{
|
|
detail += 1;
|
|
}
|
|
}
|
|
|
|
return detail;
|
|
}
|
|
|
|
void renderMeshBaseHull(LLVOVolume* volume, U32 data_mask, LLColor4& color, LLColor4& line_color)
|
|
{
|
|
LLUUID mesh_id = volume->getVolume()->getParams().getSculptID();
|
|
LLModel::Decomposition* decomp = gMeshRepo.getDecomposition(mesh_id);
|
|
|
|
const LLVector3 center(0,0,0);
|
|
const LLVector3 size(0.25f,0.25f,0.25f);
|
|
|
|
if (decomp)
|
|
{
|
|
if (!decomp->mBaseHullMesh.empty())
|
|
{
|
|
gGL.diffuseColor4fv(color.mV);
|
|
LLVertexBuffer::drawArrays(LLRender::TRIANGLES, decomp->mBaseHullMesh.mPositions, decomp->mBaseHullMesh.mNormals);
|
|
}
|
|
else
|
|
{
|
|
gMeshRepo.buildPhysicsMesh(*decomp);
|
|
gGL.diffuseColor4f(0,1,1,1);
|
|
drawBoxOutline(center, size);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
gGL.diffuseColor3f(1,0,1);
|
|
drawBoxOutline(center, size);
|
|
}
|
|
}
|
|
|
|
void render_hull(LLModel::PhysicsMesh& mesh, const LLColor4& color, const LLColor4& line_color)
|
|
{
|
|
if(mesh.mPositions.empty() || mesh.mNormals.empty())
|
|
return;
|
|
gGL.diffuseColor4fv(color.mV);
|
|
LLVertexBuffer::drawArrays(LLRender::TRIANGLES, mesh.mPositions, mesh.mNormals);
|
|
LLGLEnable<GL_POLYGON_OFFSET_LINE> offset;
|
|
gGL.setPolygonMode(LLRender::PF_FRONT_AND_BACK, LLRender::PM_LINE);
|
|
gGL.setPolygonOffset(3.f, 3.f);
|
|
gGL.setLineWidth(3.f);
|
|
gGL.diffuseColor4fv(line_color.mV);
|
|
LLVertexBuffer::drawArrays(LLRender::TRIANGLES, mesh.mPositions, mesh.mNormals);
|
|
gGL.setLineWidth(1.f);
|
|
gGL.setPolygonMode(LLRender::PF_FRONT_AND_BACK, LLRender::PM_FILL);
|
|
}
|
|
|
|
void renderPhysicsShape(LLDrawable* drawable, LLVOVolume* volume)
|
|
{
|
|
U8 physics_type = volume->getPhysicsShapeType();
|
|
|
|
if (physics_type == LLViewerObject::PHYSICS_SHAPE_NONE || volume->isFlexible())
|
|
{
|
|
return;
|
|
}
|
|
|
|
//not allowed to return at this point without rendering *something*
|
|
|
|
F32 threshold = gSavedSettings.getF32("ObjectCostHighThreshold");
|
|
F32 cost = volume->getObjectCost();
|
|
|
|
LLColor4 low = gSavedSettings.getColor4("ObjectCostLowColor");
|
|
LLColor4 mid = gSavedSettings.getColor4("ObjectCostMidColor");
|
|
LLColor4 high = gSavedSettings.getColor4("ObjectCostHighColor");
|
|
|
|
F32 normalizedCost = 1.f - exp( -(cost / threshold) );
|
|
|
|
LLColor4 color;
|
|
if ( normalizedCost <= 0.5f )
|
|
{
|
|
color = lerp( low, mid, 2.f * normalizedCost );
|
|
}
|
|
else
|
|
{
|
|
color = lerp( mid, high, 2.f * ( normalizedCost - 0.5f ) );
|
|
}
|
|
|
|
LLColor4 line_color = color*0.5f;
|
|
|
|
U32 data_mask = LLVertexBuffer::MAP_VERTEX;
|
|
|
|
LLVolumeParams volume_params = volume->getVolume()->getParams();
|
|
|
|
LLPhysicsVolumeParams physics_params(volume_params,
|
|
physics_type == LLViewerObject::PHYSICS_SHAPE_CONVEX_HULL);
|
|
|
|
LLPhysicsShapeBuilderUtil::PhysicsShapeSpecification physics_spec;
|
|
LLPhysicsShapeBuilderUtil::determinePhysicsShape(physics_params, volume->getScale(), physics_spec);
|
|
|
|
U32 type = physics_spec.getType();
|
|
|
|
LLVector3 center(0,0,0);
|
|
LLVector3 size(0.25f,0.25f,0.25f);
|
|
|
|
gGL.pushMatrix();
|
|
gGL.multMatrix(volume->getRelativeXform());
|
|
|
|
if (type == LLPhysicsShapeBuilderUtil::PhysicsShapeSpecification::USER_MESH)
|
|
{
|
|
//Skip. no
|
|
LLUUID mesh_id = volume->getVolume()->getParams().getSculptID();
|
|
LLModel::Decomposition* decomp = gMeshRepo.getDecomposition(mesh_id);
|
|
|
|
if (decomp)
|
|
{ //render a physics based mesh
|
|
|
|
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
|
|
|
|
if (!decomp->mHull.empty())
|
|
{ //decomposition exists, use that
|
|
|
|
if (decomp->mMesh.empty())
|
|
{
|
|
gMeshRepo.buildPhysicsMesh(*decomp);
|
|
}
|
|
|
|
for (U32 i = 0; i < decomp->mMesh.size(); ++i)
|
|
{
|
|
render_hull(decomp->mMesh[i], color, line_color);
|
|
}
|
|
}
|
|
else if (!decomp->mPhysicsShapeMesh.empty())
|
|
{
|
|
//decomp has physics mesh, render that mesh
|
|
gGL.diffuseColor4fv(color.mV);
|
|
LLVertexBuffer::drawArrays(LLRender::TRIANGLES, decomp->mPhysicsShapeMesh.mPositions, decomp->mPhysicsShapeMesh.mNormals);
|
|
|
|
gGL.setPolygonMode(LLRender::PF_FRONT_AND_BACK, LLRender::PM_LINE);
|
|
gGL.diffuseColor4fv(line_color.mV);
|
|
LLVertexBuffer::drawArrays(LLRender::TRIANGLES, decomp->mPhysicsShapeMesh.mPositions, decomp->mPhysicsShapeMesh.mNormals);
|
|
gGL.setPolygonMode(LLRender::PF_FRONT_AND_BACK, LLRender::PM_FILL);
|
|
}
|
|
else
|
|
{ //no mesh or decomposition, render base hull
|
|
renderMeshBaseHull(volume, data_mask, color, line_color);
|
|
|
|
if (decomp->mPhysicsShapeMesh.empty())
|
|
{
|
|
//attempt to fetch physics shape mesh if available
|
|
gMeshRepo.fetchPhysicsShape(mesh_id);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
gGL.diffuseColor3f(1,1,0);
|
|
drawBoxOutline(center, size);
|
|
}
|
|
}
|
|
else if (type == LLPhysicsShapeBuilderUtil::PhysicsShapeSpecification::USER_CONVEX ||
|
|
type == LLPhysicsShapeBuilderUtil::PhysicsShapeSpecification::PRIM_CONVEX)
|
|
{
|
|
if (volume->isMesh())
|
|
{
|
|
renderMeshBaseHull(volume, data_mask, color, line_color);
|
|
}
|
|
/*else
|
|
{
|
|
LLVolumeParams volume_params = volume->getVolume()->getParams();
|
|
S32 detail = get_physics_detail(volume_params, volume->getScale());
|
|
LLVolume* phys_volume = LLPrimitive::getVolumeManager()->refVolume(volume_params, detail);
|
|
|
|
if (!phys_volume->mHullPoints)
|
|
{ //build convex hull
|
|
std::vector<LLVector3> pos;
|
|
std::vector<U16> index;
|
|
|
|
S32 index_offset = 0;
|
|
|
|
for (S32 i = 0; i < phys_volume->getNumVolumeFaces(); ++i)
|
|
{
|
|
const LLVolumeFace& face = phys_volume->getVolumeFace(i);
|
|
if (index_offset + face.mNumVertices > 65535)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
for (S32 j = 0; j < face.mNumVertices; ++j)
|
|
{
|
|
pos.push_back(LLVector3(face.mPositions[j].getF32ptr()));
|
|
}
|
|
|
|
for (S32 j = 0; j < face.mNumIndices; ++j)
|
|
{
|
|
index.push_back(face.mIndices[j]+index_offset);
|
|
}
|
|
|
|
index_offset += face.mNumVertices;
|
|
}
|
|
|
|
if (!pos.empty() && !index.empty())
|
|
{
|
|
LLCDMeshData mesh;
|
|
mesh.mIndexBase = &index[0];
|
|
mesh.mVertexBase = pos[0].mV;
|
|
mesh.mNumVertices = pos.size();
|
|
mesh.mVertexStrideBytes = 12;
|
|
mesh.mIndexStrideBytes = 6;
|
|
mesh.mIndexType = LLCDMeshData::INT_16;
|
|
|
|
mesh.mNumTriangles = index.size()/3;
|
|
|
|
LLCDMeshData res;
|
|
|
|
LLConvexDecomposition::getInstance()->generateSingleHullMeshFromMesh( &mesh, &res );
|
|
|
|
//copy res into phys_volume
|
|
phys_volume->mHullPoints = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*res.mNumVertices);
|
|
phys_volume->mNumHullPoints = res.mNumVertices;
|
|
|
|
S32 idx_size = (res.mNumTriangles*3*2+0xF) & ~0xF;
|
|
phys_volume->mHullIndices = (U16*) ll_aligned_malloc_16(idx_size);
|
|
phys_volume->mNumHullIndices = res.mNumTriangles*3;
|
|
|
|
const F32* v = res.mVertexBase;
|
|
|
|
for (S32 i = 0; i < res.mNumVertices; ++i)
|
|
{
|
|
F32* p = (F32*) ((U8*)v+i*res.mVertexStrideBytes);
|
|
phys_volume->mHullPoints[i].load3(p);
|
|
}
|
|
|
|
if (res.mIndexType == LLCDMeshData::INT_16)
|
|
{
|
|
for (S32 i = 0; i < res.mNumTriangles; ++i)
|
|
{
|
|
U16* idx = (U16*) (((U8*)res.mIndexBase)+i*res.mIndexStrideBytes);
|
|
|
|
phys_volume->mHullIndices[i*3+0] = idx[0];
|
|
phys_volume->mHullIndices[i*3+1] = idx[1];
|
|
phys_volume->mHullIndices[i*3+2] = idx[2];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (S32 i = 0; i < res.mNumTriangles; ++i)
|
|
{
|
|
U32* idx = (U32*) (((U8*)res.mIndexBase)+i*res.mIndexStrideBytes);
|
|
|
|
phys_volume->mHullIndices[i*3+0] = (U16) idx[0];
|
|
phys_volume->mHullIndices[i*3+1] = (U16) idx[1];
|
|
phys_volume->mHullIndices[i*3+2] = (U16) idx[2];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (phys_volume->mHullPoints)
|
|
{
|
|
//render hull
|
|
|
|
gGL.setPolygonMode(LLRender::PF_FRONT_AND_BACK, LLRender::PM_LINE);
|
|
|
|
gGL.diffuseColor4fv(line_color.mV);
|
|
LLVertexBuffer::unbind();
|
|
|
|
llassert(!LLGLSLShader::sNoFixedFunction || LLGLSLShader::sCurBoundShader != 0);
|
|
|
|
LLVertexBuffer::drawElements(LLRender::TRIANGLES, phys_volume->mNumHullPoints phys_volume->mHullPoints, NULL, phys_volume->mNumHullIndices, phys_volume->mHullIndices);
|
|
|
|
gGL.diffuseColor4fv(color.mV);
|
|
gGL.setPolygonMode(LLRender::PF_FRONT_AND_BACK, LLRender::PM_FILL);
|
|
LLVertexBuffer::drawElements(LLRender::TRIANGLES, phys_volume->mNumHullPoints phys_volume->mHullPoints, NULL, phys_volume->mNumHullIndices, phys_volume->mHullIndices);
|
|
|
|
}
|
|
else
|
|
{
|
|
gGL.diffuseColor4f(1,0,1,1);
|
|
drawBoxOutline(center, size);
|
|
}
|
|
|
|
LLPrimitive::sVolumeManager->unrefVolume(phys_volume);
|
|
}*/
|
|
}
|
|
else if (type == LLPhysicsShapeBuilderUtil::PhysicsShapeSpecification::BOX)
|
|
{
|
|
LLVector3 center = physics_spec.getCenter();
|
|
LLVector3 scale = physics_spec.getScale();
|
|
LLVector3 vscale = volume->getScale()*2.f;
|
|
scale.set(scale[0]/vscale[0], scale[1]/vscale[1], scale[2]/vscale[2]);
|
|
|
|
gGL.diffuseColor4fv(color.mV);
|
|
drawBox(center, scale);
|
|
}
|
|
else if (type == LLPhysicsShapeBuilderUtil::PhysicsShapeSpecification::SPHERE)
|
|
{
|
|
/*LLVolumeParams volume_params;
|
|
volume_params.setType( LL_PCODE_PROFILE_CIRCLE_HALF, LL_PCODE_PATH_CIRCLE );
|
|
volume_params.setBeginAndEndS( 0.f, 1.f );
|
|
volume_params.setBeginAndEndT( 0.f, 1.f );
|
|
volume_params.setRatio ( 1, 1 );
|
|
volume_params.setShear ( 0, 0 );
|
|
LLVolume* sphere = LLPrimitive::sVolumeManager->refVolume(volume_params, 3);
|
|
|
|
gGL.diffuseColor4fv(color.mV);
|
|
pushVerts(sphere);
|
|
LLPrimitive::sVolumeManager->unrefVolume(sphere);*/
|
|
}
|
|
else if (type == LLPhysicsShapeBuilderUtil::PhysicsShapeSpecification::CYLINDER)
|
|
{
|
|
LLVolumeParams volume_params;
|
|
volume_params.setType( LL_PCODE_PROFILE_CIRCLE, LL_PCODE_PATH_LINE );
|
|
volume_params.setBeginAndEndS( 0.f, 1.f );
|
|
volume_params.setBeginAndEndT( 0.f, 1.f );
|
|
volume_params.setRatio ( 1, 1 );
|
|
volume_params.setShear ( 0, 0 );
|
|
LLVolume* cylinder = LLPrimitive::getVolumeManager()->refVolume(volume_params, 3);
|
|
|
|
gGL.diffuseColor4fv(color.mV);
|
|
pushVerts(cylinder);
|
|
LLPrimitive::getVolumeManager()->unrefVolume(cylinder);
|
|
}
|
|
else if (type == LLPhysicsShapeBuilderUtil::PhysicsShapeSpecification::PRIM_MESH)
|
|
{
|
|
LLVolumeParams volume_params = volume->getVolume()->getParams();
|
|
S32 detail = get_physics_detail(volume_params, volume->getScale());
|
|
|
|
LLVolume* phys_volume = LLPrimitive::getVolumeManager()->refVolume(volume_params, detail);
|
|
gGL.setPolygonMode(LLRender::PF_FRONT_AND_BACK, LLRender::PM_LINE);
|
|
|
|
gGL.diffuseColor4fv(line_color.mV);
|
|
pushVerts(phys_volume);
|
|
|
|
gGL.diffuseColor4fv(color.mV);
|
|
gGL.setPolygonMode(LLRender::PF_FRONT_AND_BACK, LLRender::PM_FILL);
|
|
pushVerts(phys_volume);
|
|
LLPrimitive::getVolumeManager()->unrefVolume(phys_volume);
|
|
}
|
|
else if (type == LLPhysicsShapeBuilderUtil::PhysicsShapeSpecification::PRIM_CONVEX)
|
|
{
|
|
LLVolumeParams volume_params = volume->getVolume()->getParams();
|
|
S32 detail = get_physics_detail(volume_params, volume->getScale());
|
|
|
|
LLVolume* phys_volume = LLPrimitive::getVolumeManager()->refVolume(volume_params, detail);
|
|
|
|
if (phys_volume->mHullPoints && phys_volume->mHullIndices)
|
|
{
|
|
gGL.setPolygonMode(LLRender::PF_FRONT_AND_BACK, LLRender::PM_LINE);
|
|
gGL.diffuseColor4fv(line_color.mV);
|
|
LLVertexBuffer::drawElements(LLRender::TRIANGLES, phys_volume->mNumHullPoints, phys_volume->mHullPoints, NULL, phys_volume->mNumHullIndices, phys_volume->mHullIndices);
|
|
|
|
gGL.setPolygonMode(LLRender::PF_FRONT_AND_BACK, LLRender::PM_FILL);
|
|
gGL.diffuseColor4fv(color.mV);
|
|
LLVertexBuffer::drawElements(LLRender::TRIANGLES, phys_volume->mNumHullPoints, phys_volume->mHullPoints, NULL, phys_volume->mNumHullIndices, phys_volume->mHullIndices);
|
|
}
|
|
else
|
|
{
|
|
gGL.diffuseColor3f(1,0,1);
|
|
drawBoxOutline(center, size);
|
|
gMeshRepo.buildHull(volume_params, detail);
|
|
}
|
|
LLPrimitive::getVolumeManager()->unrefVolume(phys_volume);
|
|
}
|
|
else if (type == LLPhysicsShapeBuilderUtil::PhysicsShapeSpecification::SCULPT)
|
|
{
|
|
//TODO: implement sculpted prim physics display
|
|
}
|
|
else
|
|
{
|
|
LL_ERRS() << "Unhandled type" << LL_ENDL;
|
|
}
|
|
|
|
gGL.popMatrix();
|
|
}
|
|
|
|
void renderPhysicsShapes(LLSpatialGroup* group)
|
|
{
|
|
OctreeGuard guard(group->getOctreeNode());
|
|
for (OctreeNode::const_element_iter i = group->getDataBegin(); i != group->getDataEnd(); ++i)
|
|
{
|
|
LLDrawable* drawable = (LLDrawable*)(*i)->getDrawable();
|
|
if (!drawable)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (drawable->isSpatialBridge())
|
|
{
|
|
LLSpatialBridge* bridge = drawable->asPartition()->asBridge();
|
|
|
|
if (bridge)
|
|
{
|
|
gGL.pushMatrix();
|
|
gGL.multMatrix(bridge->mDrawable->getRenderMatrix());
|
|
bridge->renderPhysicsShapes();
|
|
gGL.popMatrix();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LLVOVolume* volume = drawable->getVOVolume();
|
|
if (volume && !volume->isAttachment() && volume->getPhysicsShapeType() != LLViewerObject::PHYSICS_SHAPE_NONE )
|
|
{
|
|
if (!group->getSpatialPartition()->isBridge())
|
|
{
|
|
gGL.pushMatrix();
|
|
LLVector3 trans = drawable->getRegion()->getOriginAgent();
|
|
gGL.translatef(trans.mV[0], trans.mV[1], trans.mV[2]);
|
|
renderPhysicsShape(drawable, volume);
|
|
gGL.popMatrix();
|
|
}
|
|
else
|
|
{
|
|
renderPhysicsShape(drawable, volume);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LLViewerObject* object = drawable->getVObj();
|
|
if (object && object->getPCode() == LLViewerObject::LL_VO_SURFACE_PATCH)
|
|
{
|
|
gGL.pushMatrix();
|
|
gGL.multMatrix(object->getRegion()->mRenderMatrix);
|
|
//push face vertices for terrain
|
|
for (S32 i = 0; i < drawable->getNumFaces(); ++i)
|
|
{
|
|
LLFace* face = drawable->getFace(i);
|
|
if (face)
|
|
{
|
|
LLVertexBuffer* buff = face->getVertexBuffer();
|
|
if (buff)
|
|
{
|
|
gGL.setPolygonMode(LLRender::PF_FRONT_AND_BACK, LLRender::PM_LINE);
|
|
|
|
buff->setBuffer(LLVertexBuffer::MAP_VERTEX);
|
|
gGL.diffuseColor3f(0.2f, 0.5f, 0.3f);
|
|
buff->draw(LLRender::TRIANGLES, buff->getNumIndices(), 0);
|
|
|
|
gGL.diffuseColor3f(0.2f, 1.f, 0.3f);
|
|
gGL.setPolygonMode(LLRender::PF_FRONT_AND_BACK, LLRender::PM_FILL);
|
|
buff->draw(LLRender::TRIANGLES, buff->getNumIndices(), 0);
|
|
}
|
|
}
|
|
}
|
|
gGL.popMatrix();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void renderTexturePriority(LLDrawable* drawable)
|
|
{
|
|
for (int face=0; face<drawable->getNumFaces(); ++face)
|
|
{
|
|
LLFace *facep = drawable->getFace(face);
|
|
|
|
LLVector4 cold(0,0,0.25f);
|
|
LLVector4 hot(1,0.25f,0.25f);
|
|
|
|
LLVector4 boost_cold(0,0,0,0);
|
|
LLVector4 boost_hot(0,1,0,1);
|
|
|
|
LLGLDisable<GL_BLEND> blend;
|
|
|
|
//LLViewerTexture* imagep = facep->getTexture();
|
|
//if (imagep)
|
|
if (facep)
|
|
{
|
|
|
|
//F32 vsize = imagep->mMaxVirtualSize;
|
|
F32 vsize = facep->getPixelArea();
|
|
|
|
if (vsize > sCurMaxTexPriority)
|
|
{
|
|
sCurMaxTexPriority = vsize;
|
|
}
|
|
|
|
F32 t = vsize/sLastMaxTexPriority;
|
|
|
|
LLVector4 col = lerp(cold, hot, t);
|
|
gGL.diffuseColor4fv(col.mV);
|
|
}
|
|
//else
|
|
//{
|
|
// gGL.diffuseColor4f(1,0,1,1);
|
|
//}
|
|
|
|
LLVector4a center;
|
|
center.setAdd(facep->mExtents[1],facep->mExtents[0]);
|
|
center.mul(0.5f);
|
|
LLVector4a size;
|
|
size.setSub(facep->mExtents[1],facep->mExtents[0]);
|
|
size.mul(0.5f);
|
|
size.add(LLVector4a(0.01f));
|
|
drawBox(center, size);
|
|
|
|
/*S32 boost = imagep->getBoostLevel();
|
|
if (boost>LLGLTexture::BOOST_NONE)
|
|
{
|
|
F32 t = (F32) boost / (F32) (LLGLTexture::BOOST_MAX_LEVEL-1);
|
|
LLVector4 col = lerp(boost_cold, boost_hot, t);
|
|
LLGLEnable blend_on(GL_BLEND);
|
|
gGL.blendFunc(GL_SRC_ALPHA, GL_ONE);
|
|
gGL.diffuseColor4fv(col.mV);
|
|
drawBox(center, size);
|
|
gGL.blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
}*/
|
|
}
|
|
}
|
|
|
|
void renderPoints(LLDrawable* drawablep)
|
|
{
|
|
LLGLDepthTest depth(GL_FALSE, GL_FALSE);
|
|
if (drawablep->getNumFaces())
|
|
{
|
|
gGL.begin(LLRender::POINTS);
|
|
gGL.diffuseColor3f(1,1,1);
|
|
for (S32 i = 0; i < drawablep->getNumFaces(); i++)
|
|
{
|
|
LLFace * face = drawablep->getFace(i);
|
|
if (face)
|
|
{
|
|
gGL.vertex3fv(face->mCenterLocal.mV);
|
|
}
|
|
}
|
|
gGL.end();
|
|
}
|
|
}
|
|
|
|
void renderTextureAnim(LLDrawInfo* params)
|
|
{
|
|
if (!params->mTextureMatrix)
|
|
{
|
|
return;
|
|
}
|
|
|
|
LLGLEnable<GL_BLEND> blend;
|
|
gGL.diffuseColor4f(1,1,0,0.5f);
|
|
pushVerts(params, LLVertexBuffer::MAP_VERTEX);
|
|
}
|
|
|
|
void renderBatchSize(LLDrawInfo* params)
|
|
{
|
|
LLGLEnable<GL_POLYGON_OFFSET_FILL> offset;
|
|
gGL.setPolygonOffset(-1.f, 1.f);
|
|
gGL.diffuseColor4ubv((GLubyte*) &(params->mDebugColor));
|
|
pushVerts(params, LLVertexBuffer::MAP_VERTEX);
|
|
}
|
|
|
|
void renderShadowFrusta(LLDrawInfo* params)
|
|
{
|
|
LLGLEnable<GL_BLEND> blend;
|
|
gGL.setSceneBlendType(LLRender::BT_ADD);
|
|
|
|
LLVector4a center;
|
|
center.setAdd(params->mExtents[1], params->mExtents[0]);
|
|
center.mul(0.5f);
|
|
LLVector4a size;
|
|
size.setSub(params->mExtents[1],params->mExtents[0]);
|
|
size.mul(0.5f);
|
|
|
|
if (gPipeline.mShadowCamera[4].AABBInFrustum(center, size))
|
|
{
|
|
gGL.diffuseColor3f(1,0,0);
|
|
pushVerts(params, LLVertexBuffer::MAP_VERTEX);
|
|
}
|
|
if (gPipeline.mShadowCamera[5].AABBInFrustum(center, size))
|
|
{
|
|
gGL.diffuseColor3f(0,1,0);
|
|
pushVerts(params, LLVertexBuffer::MAP_VERTEX);
|
|
}
|
|
if (gPipeline.mShadowCamera[6].AABBInFrustum(center, size))
|
|
{
|
|
gGL.diffuseColor3f(0,0,1);
|
|
pushVerts(params, LLVertexBuffer::MAP_VERTEX);
|
|
}
|
|
if (gPipeline.mShadowCamera[7].AABBInFrustum(center, size))
|
|
{
|
|
gGL.diffuseColor3f(1,0,1);
|
|
pushVerts(params, LLVertexBuffer::MAP_VERTEX);
|
|
}
|
|
|
|
gGL.setSceneBlendType(LLRender::BT_ALPHA);
|
|
}
|
|
|
|
|
|
void renderLights(LLDrawable* drawablep)
|
|
{
|
|
if (!drawablep->isLight())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (drawablep->getNumFaces())
|
|
{
|
|
LLGLEnable<GL_BLEND> blend;
|
|
gGL.diffuseColor4f(0,1,1,0.5f);
|
|
|
|
for (S32 i = 0; i < drawablep->getNumFaces(); i++)
|
|
{
|
|
LLFace * face = drawablep->getFace(i);
|
|
if (face)
|
|
{
|
|
pushVerts(face, LLVertexBuffer::MAP_VERTEX);
|
|
}
|
|
}
|
|
|
|
const LLVector4a* ext = drawablep->getSpatialExtents();
|
|
|
|
LLVector4a pos;
|
|
pos.setAdd(ext[0], ext[1]);
|
|
pos.mul(0.5f);
|
|
LLVector4a size;
|
|
size.setSub(ext[1], ext[0]);
|
|
size.mul(0.5f);
|
|
|
|
{
|
|
//LLGLDepthTest depth(GL_FALSE, GL_TRUE);
|
|
LLGLDepthTest depth(GL_TRUE, GL_TRUE, GL_ALWAYS);
|
|
gGL.diffuseColor4f(1,1,1,1);
|
|
drawBoxOutline(pos, size);
|
|
}
|
|
|
|
gGL.diffuseColor4f(1,1,0,1);
|
|
F32 rad = drawablep->getVOVolume()->getLightRadius();
|
|
drawBoxOutline(pos, LLVector4a(rad));
|
|
}
|
|
}
|
|
|
|
LL_ALIGN_PREFIX(16)
|
|
class LLRenderOctreeRaycast : public LLOctreeTriangleRayIntersect
|
|
{
|
|
public:
|
|
|
|
|
|
LLRenderOctreeRaycast(const LLVector4a& start, const LLVector4a& dir, F32* closest_t)
|
|
: LLOctreeTriangleRayIntersect(start, dir, NULL, closest_t, NULL, NULL, NULL, NULL)
|
|
{
|
|
|
|
}
|
|
|
|
void visit(const LLOctreeNode<LLVolumeTriangle>* branch)
|
|
{
|
|
LLVolumeOctreeListener* vl = (LLVolumeOctreeListener*) branch->getListener(0);
|
|
|
|
LLVector3 center, size;
|
|
|
|
if (branch->isEmpty())
|
|
{
|
|
gGL.diffuseColor3f(1.f,0.2f,0.f);
|
|
center.set(branch->getCenter().getF32ptr());
|
|
size.set(branch->getSize().getF32ptr());
|
|
}
|
|
else
|
|
{
|
|
gGL.diffuseColor3f(0.75f, 1.f, 0.f);
|
|
center.set(vl->mBounds[0].getF32ptr());
|
|
size.set(vl->mBounds[1].getF32ptr());
|
|
}
|
|
|
|
drawBoxOutline(center, size);
|
|
|
|
for (U32 i = 0; i < 2; i++)
|
|
{
|
|
LLGLDepthTest depth(GL_TRUE, GL_FALSE, i == 1 ? GL_LEQUAL : GL_GREATER);
|
|
|
|
if (i == 1)
|
|
{
|
|
gGL.diffuseColor4f(0,1,1,0.5f);
|
|
}
|
|
else
|
|
{
|
|
gGL.diffuseColor4f(0,0.5f,0.5f, 0.25f);
|
|
drawBoxOutline(center, size);
|
|
}
|
|
|
|
if (i == 1)
|
|
{
|
|
gGL.setLineWidth(3.f);
|
|
}
|
|
|
|
gGL.begin(LLRender::TRIANGLES);
|
|
for (LLOctreeNode<LLVolumeTriangle>::const_element_iter iter = branch->getDataBegin();
|
|
iter != branch->getDataEnd();
|
|
++iter)
|
|
{
|
|
const LLVolumeTriangle* tri = *iter;
|
|
|
|
gGL.vertex3fv(tri->mV[0]->getF32ptr());
|
|
gGL.vertex3fv(tri->mV[1]->getF32ptr());
|
|
gGL.vertex3fv(tri->mV[2]->getF32ptr());
|
|
}
|
|
gGL.end();
|
|
|
|
if (i == 1)
|
|
{
|
|
gGL.setLineWidth(1.f);
|
|
}
|
|
}
|
|
}
|
|
} LL_ALIGN_POSTFIX(16);
|
|
|
|
void renderRaycast(LLDrawable* drawablep)
|
|
{
|
|
if (drawablep->getNumFaces())
|
|
{
|
|
LLGLEnable<GL_BLEND> blend;
|
|
gGL.diffuseColor4f(0,1,1,0.5f);
|
|
|
|
if (drawablep->getVOVolume())
|
|
{
|
|
//gGL.setPolygonMode(LLRender::PF_FRONT_AND_BACK, LLRender::PM_LINE);
|
|
//pushVerts(drawablep->getFace(gDebugRaycastFaceHit), LLVertexBuffer::MAP_VERTEX);
|
|
//gGL.setPolygonMode(LLRender::PF_FRONT_AND_BACK, LLRender::PM_FILL);
|
|
|
|
LLVOVolume* vobj = drawablep->getVOVolume();
|
|
LLVolume* volume = vobj->getVolume();
|
|
|
|
bool transform = true;
|
|
if (drawablep->isState(LLDrawable::RIGGED))
|
|
{
|
|
volume = vobj->getRiggedVolume();
|
|
transform = false;
|
|
}
|
|
|
|
if (volume)
|
|
{
|
|
LLVector3 trans = drawablep->getRegion()->getOriginAgent();
|
|
|
|
for (S32 i = 0; i < volume->getNumVolumeFaces(); ++i)
|
|
{
|
|
const LLVolumeFace& face = volume->getVolumeFace(i);
|
|
|
|
gGL.pushMatrix();
|
|
gGL.translatef(trans.mV[0], trans.mV[1], trans.mV[2]);
|
|
gGL.multMatrix(vobj->getRelativeXform());
|
|
|
|
LLVector4a start, end;
|
|
if (transform)
|
|
{
|
|
LLVector3 v_start(gDebugRaycastStart.getF32ptr());
|
|
LLVector3 v_end(gDebugRaycastEnd.getF32ptr());
|
|
|
|
v_start = vobj->agentPositionToVolume(v_start);
|
|
v_end = vobj->agentPositionToVolume(v_end);
|
|
|
|
start.load3(v_start.mV);
|
|
end.load3(v_end.mV);
|
|
}
|
|
else
|
|
{
|
|
start = gDebugRaycastStart;
|
|
end = gDebugRaycastEnd;
|
|
}
|
|
|
|
LLVector4a dir;
|
|
dir.setSub(end, start);
|
|
|
|
gGL.flush();
|
|
gGL.setPolygonMode(LLRender::PF_FRONT_AND_BACK, LLRender::PM_LINE);
|
|
|
|
{
|
|
//render face positions
|
|
gGL.diffuseColor4f(0,1,1,0.5f);
|
|
LLVertexBuffer::drawElements(LLRender::TRIANGLES, face.mNumVertices, face.mPositions, NULL, face.mNumIndices, face.mIndices);
|
|
}
|
|
|
|
if (!volume->isUnique())
|
|
{
|
|
F32 t = 1.f;
|
|
|
|
if (!face.mOctree)
|
|
{
|
|
((LLVolumeFace*) &face)->createOctree();
|
|
}
|
|
|
|
LLRenderOctreeRaycast render(start, dir, &t);
|
|
|
|
render.traverse(face.mOctree);
|
|
}
|
|
|
|
gGL.popMatrix();
|
|
gGL.setPolygonMode(LLRender::PF_FRONT_AND_BACK, LLRender::PM_FILL);
|
|
}
|
|
}
|
|
}
|
|
else if (drawablep->isAvatar())
|
|
{
|
|
if (drawablep->getVObj() == gDebugRaycastObject)
|
|
{
|
|
LLGLDepthTest depth(GL_FALSE);
|
|
LLVOAvatar* av = (LLVOAvatar*) drawablep->getVObj().get();
|
|
av->renderCollisionVolumes();
|
|
}
|
|
}
|
|
|
|
if (drawablep->getVObj() == gDebugRaycastObject)
|
|
{
|
|
// draw intersection point
|
|
gGL.pushMatrix();
|
|
gGL.loadMatrix(gGLModelView);
|
|
LLVector3 translate(gDebugRaycastIntersection.getF32ptr());
|
|
gGL.translatef(translate.mV[0], translate.mV[1], translate.mV[2]);
|
|
LLCoordFrame orient;
|
|
LLVector4a debug_binormal;
|
|
|
|
debug_binormal.setCross3(gDebugRaycastNormal, gDebugRaycastTangent);
|
|
debug_binormal.mul(gDebugRaycastTangent.getF32ptr()[3]);
|
|
|
|
LLVector3 normal(gDebugRaycastNormal.getF32ptr());
|
|
LLVector3 binormal(debug_binormal.getF32ptr());
|
|
|
|
//LLCoordFrame isn't vectorized, for now.
|
|
orient.lookDir(normal, binormal);
|
|
LLMatrix4 rotation;
|
|
orient.getRotMatrixToParent(rotation);
|
|
LLMatrix4a rotationa;
|
|
rotationa.loadu((F32*)rotation.mMatrix);
|
|
gGL.multMatrix(rotationa);
|
|
|
|
gGL.diffuseColor4f(1,0,0,0.5f);
|
|
drawBox(LLVector3(0, 0, 0), LLVector3(0.1f, 0.022f, 0.022f));
|
|
gGL.diffuseColor4f(0,1,0,0.5f);
|
|
drawBox(LLVector3(0, 0, 0), LLVector3(0.021f, 0.1f, 0.021f));
|
|
gGL.diffuseColor4f(0,0,1,0.5f);
|
|
drawBox(LLVector3(0, 0, 0), LLVector3(0.02f, 0.02f, 0.1f));
|
|
gGL.popMatrix();
|
|
|
|
// draw bounding box of prim
|
|
const LLVector4a* ext = drawablep->getSpatialExtents();
|
|
|
|
LLVector4a pos;
|
|
pos.setAdd(ext[0], ext[1]);
|
|
pos.mul(0.5f);
|
|
LLVector4a size;
|
|
size.setSub(ext[1], ext[0]);
|
|
size.mul(0.5f);
|
|
|
|
//LLGLDepthTest depth(GL_FALSE, GL_TRUE);
|
|
LLGLDepthTest depth(GL_TRUE, GL_TRUE, GL_ALWAYS);
|
|
gGL.diffuseColor4f(0,0.5f,0.5f,1);
|
|
drawBoxOutline(pos, size);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void renderAvatarCollisionVolumes(LLVOAvatar* avatar)
|
|
{
|
|
avatar->renderCollisionVolumes();
|
|
}
|
|
|
|
void renderAvatarBones(LLVOAvatar* avatar)
|
|
{
|
|
avatar->renderBones();
|
|
}
|
|
void renderAgentTarget(LLVOAvatar* avatar)
|
|
{
|
|
// render these for self only (why, i don't know)
|
|
if (avatar->isSelf())
|
|
{
|
|
renderCrossHairs(avatar->getPositionAgent(), 0.2f, LLColor4(1, 0, 0, 0.8f));
|
|
renderCrossHairs(avatar->mDrawable->getPositionAgent(), 0.2f, LLColor4(1, 0, 0, 0.8f));
|
|
renderCrossHairs(avatar->mRoot->getWorldPosition(), 0.2f, LLColor4(1, 1, 1, 0.8f));
|
|
renderCrossHairs(avatar->mPelvisp->getWorldPosition(), 0.2f, LLColor4(0, 0, 1, 0.8f));
|
|
}
|
|
}
|
|
|
|
|
|
class LLOctreeRenderNonOccluded : public OctreeTraveler
|
|
{
|
|
public:
|
|
LLCamera* mCamera;
|
|
LLOctreeRenderNonOccluded(LLCamera* camera): mCamera(camera) {}
|
|
|
|
virtual void traverse(const OctreeNode* node)
|
|
{
|
|
LLSpatialGroup* group = (LLSpatialGroup*) node->getListener(0);
|
|
|
|
const LLVector4a* bounds = group->getBounds();
|
|
if (!mCamera || mCamera->AABBInFrustumNoFarClip(bounds[0], bounds[1]))
|
|
{
|
|
node->accept(this);
|
|
stop_glerror();
|
|
|
|
for (U32 i = 0; i < node->getChildCount(); i++)
|
|
{
|
|
traverse(node->getChild(i));
|
|
stop_glerror();
|
|
}
|
|
|
|
//draw tight fit bounding boxes for spatial group
|
|
if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_OCTREE))
|
|
{
|
|
group->rebuildGeom();
|
|
group->rebuildMesh();
|
|
|
|
renderOctree(group);
|
|
stop_glerror();
|
|
}
|
|
|
|
//render visibility wireframe
|
|
if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_OCCLUSION))
|
|
{
|
|
group->rebuildGeom();
|
|
group->rebuildMesh();
|
|
|
|
gGL.flush();
|
|
gGL.pushMatrix();
|
|
gGLLastMatrix = NULL;
|
|
gGL.loadMatrix(gGLModelView);
|
|
renderVisibility(group, mCamera);
|
|
stop_glerror();
|
|
gGLLastMatrix = NULL;
|
|
gGL.popMatrix();
|
|
gGL.diffuseColor4f(1,1,1,1);
|
|
}
|
|
}
|
|
}
|
|
|
|
virtual void visit(const OctreeNode* branch)
|
|
{
|
|
LLSpatialGroup* group = (LLSpatialGroup*) branch->getListener(0);
|
|
const LLVector4a* bounds = group->getBounds();
|
|
if (group->hasState(LLSpatialGroup::GEOM_DIRTY) || (mCamera && !mCamera->AABBInFrustumNoFarClip(bounds[0], bounds[1])))
|
|
{
|
|
return;
|
|
}
|
|
|
|
LLGLDisable<GL_STENCIL_TEST> stencil;
|
|
|
|
group->rebuildGeom();
|
|
group->rebuildMesh();
|
|
|
|
if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_BBOXES))
|
|
{
|
|
if (!group->isEmpty())
|
|
{
|
|
gGL.diffuseColor3f(0,0,1);
|
|
const LLVector4a* obj_bounds = group->getObjectBounds();
|
|
drawBoxOutline(obj_bounds[0], obj_bounds[1]);
|
|
}
|
|
}
|
|
|
|
{OctreeGuard guard(branch);
|
|
for (OctreeNode::const_element_iter i = branch->getDataBegin(); i != branch->getDataEnd(); ++i)
|
|
{
|
|
LLDrawable* drawable = (LLDrawable*)(*i)->getDrawable();
|
|
if(!drawable)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_BBOXES))
|
|
{
|
|
renderBoundingBox(drawable);
|
|
}
|
|
|
|
if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_NORMALS))
|
|
{
|
|
renderNormals(drawable);
|
|
}
|
|
|
|
if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_BUILD_QUEUE))
|
|
{
|
|
if (drawable->isState(LLDrawable::IN_REBUILD_Q2))
|
|
{
|
|
gGL.diffuseColor4f(0.6f, 0.6f, 0.1f, 1.f);
|
|
const LLVector4a* ext = drawable->getSpatialExtents();
|
|
LLVector4a center;
|
|
center.setAdd(ext[0], ext[1]);
|
|
center.mul(0.5f);
|
|
LLVector4a size;
|
|
size.setSub(ext[1], ext[0]);
|
|
size.mul(0.5f);
|
|
drawBoxOutline(center, size);
|
|
}
|
|
}
|
|
|
|
if (drawable->getVOVolume() && gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_PRIORITY))
|
|
{
|
|
renderTexturePriority(drawable);
|
|
}
|
|
|
|
if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_POINTS))
|
|
{
|
|
renderPoints(drawable);
|
|
}
|
|
|
|
if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_LIGHTS))
|
|
{
|
|
renderLights(drawable);
|
|
}
|
|
|
|
if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_RAYCAST))
|
|
{
|
|
renderRaycast(drawable);
|
|
}
|
|
if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_UPDATE_TYPE))
|
|
{
|
|
renderUpdateType(drawable);
|
|
}
|
|
if(gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_RENDER_COMPLEXITY))
|
|
{
|
|
renderComplexityDisplay(drawable);
|
|
}
|
|
|
|
LLVOAvatar* avatar = dynamic_cast<LLVOAvatar*>(drawable->getVObj().get());
|
|
|
|
if (avatar && gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_AVATAR_VOLUME))
|
|
{
|
|
renderAvatarCollisionVolumes(avatar);
|
|
}
|
|
|
|
if (avatar && gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_AVATAR_JOINTS))
|
|
{
|
|
renderAvatarBones(avatar);
|
|
}
|
|
|
|
if (avatar && gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_AGENT_TARGET))
|
|
{
|
|
renderAgentTarget(avatar);
|
|
}
|
|
|
|
if (gDebugGL)
|
|
{
|
|
for (U32 i = 0; i < (U32)drawable->getNumFaces(); ++i)
|
|
{
|
|
LLFace* facep = drawable->getFace(i);
|
|
if (facep)
|
|
{
|
|
U8 index = facep->getTextureIndex();
|
|
if (facep->mDrawInfo)
|
|
{
|
|
if (index < 255)
|
|
{
|
|
if (facep->mDrawInfo->mTextureList.size() <= index)
|
|
{
|
|
LL_ERRS() << "Face texture index out of bounds." << LL_ENDL;
|
|
}
|
|
else if (facep->mDrawInfo->mTextureList[index] != facep->getTexture())
|
|
{
|
|
LL_ERRS() << "Face texture index incorrect." << LL_ENDL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}}
|
|
|
|
for (LLSpatialGroup::draw_map_t::iterator i = group->mDrawMap.begin(); i != group->mDrawMap.end(); ++i)
|
|
{
|
|
LLSpatialGroup::drawmap_elem_t& draw_vec = i->second;
|
|
for (LLSpatialGroup::drawmap_elem_t::iterator j = draw_vec.begin(); j != draw_vec.end(); ++j)
|
|
{
|
|
LLDrawInfo* draw_info = *j;
|
|
if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_ANIM))
|
|
{
|
|
renderTextureAnim(draw_info);
|
|
}
|
|
if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_BATCH_SIZE))
|
|
{
|
|
renderBatchSize(draw_info);
|
|
}
|
|
if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_SHADOW_FRUSTA))
|
|
{
|
|
renderShadowFrusta(draw_info);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
class LLOctreeRenderPhysicsShapes : public OctreeTraveler
|
|
{
|
|
public:
|
|
LLCamera* mCamera;
|
|
LLOctreeRenderPhysicsShapes(LLCamera* camera): mCamera(camera) {}
|
|
|
|
virtual void traverse(const OctreeNode* node)
|
|
{
|
|
LLSpatialGroup* group = (LLSpatialGroup*) node->getListener(0);
|
|
|
|
const LLVector4a* bounds = group->getBounds();
|
|
if (!mCamera || mCamera->AABBInFrustumNoFarClip(bounds[0], bounds[1]))
|
|
{
|
|
node->accept(this);
|
|
stop_glerror();
|
|
|
|
for (U32 i = 0; i < node->getChildCount(); i++)
|
|
{
|
|
traverse(node->getChild(i));
|
|
stop_glerror();
|
|
}
|
|
|
|
group->rebuildGeom();
|
|
group->rebuildMesh();
|
|
|
|
renderPhysicsShapes(group);
|
|
}
|
|
}
|
|
|
|
virtual void visit(const OctreeNode* branch)
|
|
{
|
|
|
|
}
|
|
};
|
|
|
|
class LLOctreePushBBoxVerts : public OctreeTraveler
|
|
{
|
|
public:
|
|
LLCamera* mCamera;
|
|
LLOctreePushBBoxVerts(LLCamera* camera): mCamera(camera) {}
|
|
|
|
virtual void traverse(const OctreeNode* node)
|
|
{
|
|
LLSpatialGroup* group = (LLSpatialGroup*) node->getListener(0);
|
|
|
|
const LLVector4a* bounds = group->getBounds();
|
|
if (!mCamera || mCamera->AABBInFrustum(bounds[0], bounds[1]))
|
|
{
|
|
node->accept(this);
|
|
|
|
for (U32 i = 0; i < node->getChildCount(); i++)
|
|
{
|
|
traverse(node->getChild(i));
|
|
}
|
|
}
|
|
}
|
|
|
|
virtual void visit(const OctreeNode* branch)
|
|
{
|
|
LLSpatialGroup* group = (LLSpatialGroup*) branch->getListener(0);
|
|
|
|
const LLVector4a* bounds = group->getBounds();
|
|
if (group->hasState(LLSpatialGroup::GEOM_DIRTY) || (mCamera && !mCamera->AABBInFrustumNoFarClip(bounds[0], bounds[1])))
|
|
{
|
|
return;
|
|
}
|
|
|
|
OctreeGuard guard(branch);
|
|
for (OctreeNode::const_element_iter i = branch->getDataBegin(); i != branch->getDataEnd(); ++i)
|
|
{
|
|
LLDrawable* drawable = (LLDrawable*)(*i)->getDrawable();
|
|
if(!drawable)
|
|
{
|
|
continue;
|
|
}
|
|
renderBoundingBox(drawable, FALSE);
|
|
}
|
|
}
|
|
};
|
|
|
|
void LLSpatialPartition::renderIntersectingBBoxes(LLCamera* camera)
|
|
{
|
|
LLOctreePushBBoxVerts pusher(camera);
|
|
pusher.traverse(mOctree);
|
|
}
|
|
|
|
class LLOctreeStateCheck : public OctreeTraveler
|
|
{
|
|
public:
|
|
U32 mInheritedMask[LLViewerCamera::NUM_CAMERAS];
|
|
|
|
LLOctreeStateCheck()
|
|
{
|
|
for (U32 i = 0; i < LLViewerCamera::NUM_CAMERAS; i++)
|
|
{
|
|
mInheritedMask[i] = 0;
|
|
}
|
|
}
|
|
|
|
virtual void traverse(const OctreeNode* node)
|
|
{
|
|
LLSpatialGroup* group = (LLSpatialGroup*) node->getListener(0);
|
|
|
|
node->accept(this);
|
|
|
|
|
|
U32 temp[LLViewerCamera::NUM_CAMERAS];
|
|
|
|
for (U32 i = 0; i < LLViewerCamera::NUM_CAMERAS; i++)
|
|
{
|
|
temp[i] = mInheritedMask[i];
|
|
mInheritedMask[i] |= group->mOcclusionState[i] & LLSpatialGroup::OCCLUDED;
|
|
}
|
|
|
|
for (U32 i = 0; i < node->getChildCount(); i++)
|
|
{
|
|
traverse(node->getChild(i));
|
|
}
|
|
|
|
for (U32 i = 0; i < LLViewerCamera::NUM_CAMERAS; i++)
|
|
{
|
|
mInheritedMask[i] = temp[i];
|
|
}
|
|
}
|
|
|
|
|
|
virtual void visit(const OctreeNode* state)
|
|
{
|
|
LLSpatialGroup* group = (LLSpatialGroup*) state->getListener(0);
|
|
|
|
for (U32 i = 0; i < LLViewerCamera::NUM_CAMERAS; i++)
|
|
{
|
|
if (mInheritedMask[i] && !(group->mOcclusionState[i] & mInheritedMask[i]))
|
|
{
|
|
LL_ERRS() << "Spatial group failed inherited mask test." << LL_ENDL;
|
|
}
|
|
}
|
|
|
|
if (group->hasState(LLSpatialGroup::eSpatialState(LLSpatialGroup::DIRTY)))
|
|
{
|
|
assert_parent_state(group, LLSpatialGroup::eSpatialState(LLSpatialGroup::DIRTY));
|
|
}
|
|
}
|
|
|
|
void assert_parent_state(LLSpatialGroup* group, LLSpatialGroup::eSpatialState state)
|
|
{
|
|
LLSpatialGroup* parent = group->getParent();
|
|
while (parent)
|
|
{
|
|
if (!parent->hasState(state))
|
|
{
|
|
LL_ERRS() << "Spatial group failed parent state check." << LL_ENDL;
|
|
}
|
|
parent = parent->getParent();
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
void LLSpatialPartition::renderPhysicsShapes()
|
|
{
|
|
LLSpatialBridge* bridge = asBridge();
|
|
LLCamera* camera = LLViewerCamera::getInstance();
|
|
|
|
if (bridge)
|
|
{
|
|
camera = NULL;
|
|
}
|
|
|
|
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
|
|
gGL.setLineWidth(3.f);
|
|
LLOctreeRenderPhysicsShapes render_physics(camera);
|
|
render_physics.traverse(mOctree);
|
|
gGL.setLineWidth(1.f);
|
|
}
|
|
|
|
void LLSpatialPartition::renderDebug()
|
|
{
|
|
if (!gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_OCTREE |
|
|
LLPipeline::RENDER_DEBUG_OCCLUSION |
|
|
LLPipeline::RENDER_DEBUG_LIGHTS |
|
|
LLPipeline::RENDER_DEBUG_BATCH_SIZE |
|
|
LLPipeline::RENDER_DEBUG_UPDATE_TYPE |
|
|
LLPipeline::RENDER_DEBUG_BBOXES |
|
|
LLPipeline::RENDER_DEBUG_NORMALS |
|
|
LLPipeline::RENDER_DEBUG_POINTS |
|
|
LLPipeline::RENDER_DEBUG_TEXTURE_PRIORITY |
|
|
LLPipeline::RENDER_DEBUG_TEXTURE_ANIM |
|
|
LLPipeline::RENDER_DEBUG_RAYCAST |
|
|
LLPipeline::RENDER_DEBUG_AVATAR_VOLUME |
|
|
LLPipeline::RENDER_DEBUG_AVATAR_JOINTS |
|
|
LLPipeline::RENDER_DEBUG_AGENT_TARGET |
|
|
//LLPipeline::RENDER_DEBUG_BUILD_QUEUE |
|
|
LLPipeline::RENDER_DEBUG_SHADOW_FRUSTA |
|
|
LLPipeline::RENDER_DEBUG_RENDER_COMPLEXITY))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (LLGLSLShader::sNoFixedFunction)
|
|
{
|
|
gDebugProgram.bind();
|
|
}
|
|
|
|
if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_PRIORITY))
|
|
{
|
|
//sLastMaxTexPriority = lerp(sLastMaxTexPriority, sCurMaxTexPriority, gFrameIntervalSeconds);
|
|
sLastMaxTexPriority = (F32) LLViewerCamera::getInstance()->getScreenPixelArea();
|
|
sCurMaxTexPriority = 0.f;
|
|
}
|
|
|
|
LLGLDisable<GL_CULL_FACE> cullface;
|
|
LLGLEnable<GL_BLEND> blend;
|
|
gGL.setSceneBlendType(LLRender::BT_ALPHA);
|
|
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
|
|
LLGLState<GL_LIGHTING> light_state;
|
|
gPipeline.disableLights(light_state);
|
|
|
|
LLSpatialBridge* bridge = asBridge();
|
|
LLCamera* camera = LLViewerCamera::getInstance();
|
|
|
|
if (bridge)
|
|
{
|
|
camera = NULL;
|
|
}
|
|
|
|
LLOctreeStateCheck checker;
|
|
checker.traverse(mOctree);
|
|
|
|
LLOctreeRenderNonOccluded render_debug(camera);
|
|
render_debug.traverse(mOctree);
|
|
|
|
if (LLGLSLShader::sNoFixedFunction)
|
|
{
|
|
gDebugProgram.unbind();
|
|
}
|
|
}
|
|
|
|
void LLSpatialGroup::drawObjectBox(LLColor4 col)
|
|
{
|
|
gGL.diffuseColor4fv(col.mV);
|
|
LLVector4a size;
|
|
size = mObjectBounds[1];
|
|
size.mul(1.01f);
|
|
size.add(LLVector4a(0.001f));
|
|
drawBox(mObjectBounds[0], size);
|
|
}
|
|
|
|
bool LLSpatialPartition::isHUDPartition()
|
|
{
|
|
return mPartitionType == LLViewerRegion::PARTITION_HUD ;
|
|
}
|
|
|
|
BOOL LLSpatialPartition::isVisible(const LLVector3& v)
|
|
{
|
|
if (!LLViewerCamera::getInstance()->sphereInFrustum(v, 4.0f))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
LL_ALIGN_PREFIX(16)
|
|
class LLOctreeIntersect : public LLOctreeTraveler<LLViewerOctreeEntry>
|
|
{
|
|
public:
|
|
LL_ALIGN_16(LLVector4a mStart);
|
|
LL_ALIGN_16(LLVector4a mEnd);
|
|
|
|
S32 *mFaceHit;
|
|
LLVector4a *mIntersection;
|
|
LLVector2 *mTexCoord;
|
|
LLVector4a *mNormal;
|
|
LLVector4a *mTangent;
|
|
LLDrawable* mHit;
|
|
BOOL mPickTransparent;
|
|
|
|
LLOctreeIntersect(const LLVector4a& start, const LLVector4a& end, BOOL pick_transparent,
|
|
S32* face_hit, LLVector4a* intersection, LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent)
|
|
: mStart(start),
|
|
mEnd(end),
|
|
mFaceHit(face_hit),
|
|
mIntersection(intersection),
|
|
mTexCoord(tex_coord),
|
|
mNormal(normal),
|
|
mTangent(tangent),
|
|
mHit(NULL),
|
|
mPickTransparent(pick_transparent)
|
|
{
|
|
}
|
|
|
|
virtual void visit(const OctreeNode* branch)
|
|
{
|
|
OctreeGuard guard(branch);
|
|
for (OctreeNode::const_element_iter i = branch->getDataBegin(); i != branch->getDataEnd(); ++i)
|
|
{
|
|
check(*i);
|
|
}
|
|
}
|
|
|
|
virtual LLDrawable* check(const OctreeNode* node)
|
|
{
|
|
node->accept(this);
|
|
|
|
OctreeGuard guard(node);
|
|
for (U32 i = 0; i < node->getChildCount(); i++)
|
|
{
|
|
const OctreeNode* child = node->getChild(i);
|
|
LLVector3 res;
|
|
|
|
LLSpatialGroup* group = (LLSpatialGroup*) child->getListener(0);
|
|
|
|
LLVector4a size;
|
|
LLVector4a center;
|
|
|
|
const LLVector4a* bounds = group->getBounds();
|
|
size = bounds[1];
|
|
center = bounds[0];
|
|
|
|
LLVector4a local_start = mStart;
|
|
LLVector4a local_end = mEnd;
|
|
|
|
if (group->getSpatialPartition()->isBridge())
|
|
{
|
|
LLMatrix4a local_matrix = group->getSpatialPartition()->asBridge()->mDrawable->getRenderMatrix();
|
|
local_matrix.invert();
|
|
|
|
local_matrix.affineTransform(mStart, local_start);
|
|
local_matrix.affineTransform(mEnd, local_end);
|
|
}
|
|
|
|
if (LLLineSegmentBoxIntersect(local_start, local_end, center, size))
|
|
{
|
|
check(child);
|
|
}
|
|
}
|
|
|
|
return mHit;
|
|
}
|
|
|
|
virtual bool check(LLViewerOctreeEntry* entry)
|
|
{
|
|
LLDrawable* drawable = (LLDrawable*)entry->getDrawable();
|
|
|
|
if (!drawable || !gPipeline.hasRenderType(drawable->getRenderType()) || !drawable->isVisible())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (drawable->isSpatialBridge())
|
|
{
|
|
LLSpatialPartition *part = drawable->asPartition();
|
|
LLSpatialBridge* bridge = part->asBridge();
|
|
if (bridge && gPipeline.hasRenderType(bridge->mDrawableType))
|
|
{
|
|
check(part->mOctree);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LLViewerObject* vobj = drawable->getVObj();
|
|
|
|
if (vobj)
|
|
{
|
|
LLVector4a intersection;
|
|
bool skip_check = false;
|
|
if (vobj->isAvatar())
|
|
{
|
|
LLVOAvatar* avatar = (LLVOAvatar*) vobj;
|
|
if (gFloaterTools->getVisible() || LLFloaterInspect::findInstance())
|
|
{
|
|
LLViewerObject* hit = avatar->lineSegmentIntersectRiggedAttachments(mStart, mEnd, -1, mPickTransparent, mFaceHit, &intersection, mTexCoord, mNormal, mTangent);
|
|
if (hit)
|
|
{
|
|
mEnd = intersection;
|
|
if (mIntersection)
|
|
{
|
|
*mIntersection = intersection;
|
|
}
|
|
|
|
mHit = hit->mDrawable;
|
|
skip_check = true;
|
|
}
|
|
|
|
}
|
|
else if (avatar->isLangolier())
|
|
{
|
|
skip_check = true;
|
|
}
|
|
}
|
|
|
|
if (!skip_check && vobj->lineSegmentIntersect(mStart, mEnd, -1, mPickTransparent, mFaceHit, &intersection, mTexCoord, mNormal, mTangent))
|
|
{
|
|
mEnd = intersection; // shorten ray so we only find CLOSER hits
|
|
if (mIntersection)
|
|
{
|
|
*mIntersection = intersection;
|
|
}
|
|
|
|
mHit = vobj->mDrawable;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
} LL_ALIGN_POSTFIX(16);
|
|
|
|
LLDrawable* LLSpatialPartition::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end,
|
|
BOOL pick_transparent,
|
|
S32* face_hit, // return the face hit
|
|
LLVector4a* intersection, // return the intersection point
|
|
LLVector2* tex_coord, // return the texture coordinates of the intersection point
|
|
LLVector4a* normal, // return the surface normal at the intersection point
|
|
LLVector4a* tangent // return the surface tangent at the intersection point
|
|
)
|
|
|
|
{
|
|
LLOctreeIntersect intersect(start, end, pick_transparent, face_hit, intersection, tex_coord, normal, tangent);
|
|
LLDrawable* drawable = intersect.check(mOctree);
|
|
|
|
return drawable;
|
|
}
|
|
|
|
LLDrawInfo::LLDrawInfo(U16 start, U16 end, U32 count, U32 offset,
|
|
LLViewerTexture* texture, LLVertexBuffer* buffer,
|
|
BOOL fullbright, U8 bump, BOOL particle, F32 part_size)
|
|
:
|
|
mVertexBuffer(buffer),
|
|
mTexture(texture),
|
|
mTextureMatrix(NULL),
|
|
mModelMatrix(NULL),
|
|
mStart(start),
|
|
mEnd(end),
|
|
mCount(count),
|
|
mOffset(offset),
|
|
mFullbright(fullbright),
|
|
mBump(bump),
|
|
mShiny(0),
|
|
mParticle(particle),
|
|
mPartSize(part_size),
|
|
mVSize(0.f),
|
|
mGroup(NULL),
|
|
mFace(NULL),
|
|
mDistance(0.f),
|
|
mDrawMode(LLRender::TRIANGLES),
|
|
mMaterial(NULL),
|
|
mShaderMask(0),
|
|
mSpecColor(1.0f, 1.0f, 1.0f, 0.5f),
|
|
mBlendFuncSrc(LLRender::BF_SOURCE_ALPHA),
|
|
mBlendFuncDst(LLRender::BF_ONE_MINUS_SOURCE_ALPHA),
|
|
mHasGlow(FALSE),
|
|
mEnvIntensity(0.0f),
|
|
mAlphaMaskCutoff(0.5f),
|
|
mDiffuseAlphaMode(0)
|
|
{
|
|
//mVertexBuffer->validateRange(mStart, mEnd, mCount, mOffset);
|
|
|
|
mDebugColor = (rand() << 16) + rand();
|
|
}
|
|
|
|
LLDrawInfo::~LLDrawInfo()
|
|
{
|
|
/*if (LLSpatialGroup::sNoDelete)
|
|
{
|
|
LL_ERRS() << "LLDrawInfo deleted illegally!" << LL_ENDL;
|
|
}*/
|
|
|
|
if (mFace)
|
|
{
|
|
mFace->setDrawInfo(NULL);
|
|
}
|
|
|
|
if (gDebugGL)
|
|
{
|
|
gPipeline.checkReferences(this);
|
|
}
|
|
}
|
|
|
|
void LLDrawInfo::validate()
|
|
{
|
|
mVertexBuffer->validateRange(mStart, mEnd, mCount, mOffset);
|
|
}
|
|
|
|
LLVertexBuffer* LLGeometryManager::createVertexBuffer(U32 type_mask, U32 usage)
|
|
{
|
|
return new LLVertexBuffer(type_mask, usage);
|
|
}
|
|
|
|
|
|
void LLCullResult::clear()
|
|
{
|
|
mVisibleGroups.clear();
|
|
mAlphaGroups.clear();
|
|
mOcclusionGroups.clear();
|
|
mDrawableGroups.clear();
|
|
mVisibleList.clear();
|
|
mVisibleBridge.clear();
|
|
|
|
for (U32 i = 0; i < LLRenderPass::NUM_RENDER_TYPES; i++)
|
|
{
|
|
mRenderMap[i].clear();
|
|
}
|
|
}
|
|
|
|
void LLCullResult::assertDrawMapsEmpty()
|
|
{
|
|
for (U32 i = 0; i < LLRenderPass::NUM_RENDER_TYPES; i++)
|
|
{
|
|
if (hasRenderMap(i))
|
|
{
|
|
LL_ERRS() << "Stale LLDrawInfo's in LLCullResult!" << LL_ENDL;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|