4527 lines
110 KiB
C++
4527 lines
110 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"
|
|
|
|
static LLFastTimer::DeclareTimer FTM_FRUSTUM_CULL("Frustum Culling");
|
|
static LLFastTimer::DeclareTimer FTM_CULL_REBOUND("Cull Rebound");
|
|
|
|
const F32 SG_OCCLUSION_FUDGE = 0.25f;
|
|
#define SG_DISCARD_TOLERANCE 0.01f
|
|
|
|
#if LL_OCTREE_PARANOIA_CHECK
|
|
#define assert_octree_valid(x) x->validate()
|
|
#define assert_states_valid(x) ((LLSpatialGroup*) x->mSpatialPartition->mOctree->getListener(0))->checkStates()
|
|
#else
|
|
#define assert_octree_valid(x)
|
|
#define assert_states_valid(x)
|
|
#endif
|
|
|
|
extern bool gShiftFrame;
|
|
|
|
static U32 sZombieGroups = 0;
|
|
U32 LLSpatialGroup::sNodeCount = 0;
|
|
|
|
#define LL_TRACK_PENDING_OCCLUSION_QUERIES 0
|
|
|
|
std::set<GLuint> LLSpatialGroup::sPendingQueries;
|
|
|
|
U32 gOctreeMaxCapacity;
|
|
U32 gOctreeReserveCapacity;
|
|
|
|
BOOL LLSpatialGroup::sNoDelete = FALSE;
|
|
|
|
static F32 sLastMaxTexPriority = 1.f;
|
|
static F32 sCurMaxTexPriority = 1.f;
|
|
|
|
class LLOcclusionQueryPool : public LLGLNamePool
|
|
{
|
|
public:
|
|
LLOcclusionQueryPool()
|
|
{
|
|
}
|
|
|
|
protected:
|
|
|
|
virtual GLuint allocateName()
|
|
{
|
|
GLuint ret = 0;
|
|
|
|
glGenQueriesARB(1, &ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
virtual void releaseName(GLuint name)
|
|
{
|
|
#if LL_TRACK_PENDING_OCCLUSION_QUERIES
|
|
LLSpatialGroup::sPendingQueries.erase(name);
|
|
#endif
|
|
glDeleteQueriesARB(1, &name);
|
|
}
|
|
};
|
|
|
|
static LLOcclusionQueryPool sQueryPool;
|
|
|
|
//BOOL LLSpatialPartition::sFreezeState = FALSE;
|
|
|
|
//static counter for frame to switch LOD on
|
|
|
|
void sg_assert(BOOL expr)
|
|
{
|
|
#if LL_OCTREE_PARANOIA_CHECK
|
|
if (!expr)
|
|
{
|
|
llerrs << "Octree invalid!" << llendl;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
S32 AABBSphereIntersect(const LLVector3& min, const LLVector3& max, const LLVector3 &origin, const F32 &rad)
|
|
{
|
|
return AABBSphereIntersectR2(min, max, origin, rad*rad);
|
|
}
|
|
|
|
S32 AABBSphereIntersectR2(const LLVector3& min, const LLVector3& max, const LLVector3 &origin, const F32 &r)
|
|
{
|
|
F32 d = 0.f;
|
|
F32 t;
|
|
|
|
if ((min-origin).magVecSquared() < r &&
|
|
(max-origin).magVecSquared() < r)
|
|
{
|
|
return 2;
|
|
}
|
|
|
|
for (U32 i = 0; i < 3; i++)
|
|
{
|
|
if (origin.mV[i] < min.mV[i])
|
|
{
|
|
t = min.mV[i] - origin.mV[i];
|
|
d += t*t;
|
|
}
|
|
else if (origin.mV[i] > max.mV[i])
|
|
{
|
|
t = origin.mV[i] - max.mV[i];
|
|
d += t*t;
|
|
}
|
|
|
|
if (d > r)
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
S32 AABBSphereIntersect(const LLVector4a& min, const LLVector4a& max, const LLVector3 &origin, const F32 &rad)
|
|
{
|
|
return AABBSphereIntersectR2(min, max, origin, rad*rad);
|
|
}
|
|
|
|
S32 AABBSphereIntersectR2(const LLVector4a& min, const LLVector4a& max, const LLVector3 &origin, const F32 &r)
|
|
{
|
|
F32 d = 0.f;
|
|
F32 t;
|
|
|
|
LLVector4a origina;
|
|
origina.load3(origin.mV);
|
|
|
|
LLVector4a v;
|
|
v.setSub(min, origina);
|
|
|
|
if (v.dot3(v) < r)
|
|
{
|
|
v.setSub(max, origina);
|
|
if (v.dot3(v) < r)
|
|
{
|
|
return 2;
|
|
}
|
|
}
|
|
|
|
|
|
for (U32 i = 0; i < 3; i++)
|
|
{
|
|
if (origin.mV[i] < min[i])
|
|
{
|
|
t = min[i] - origin.mV[i];
|
|
d += t*t;
|
|
}
|
|
else if (origin.mV[i] > max[i])
|
|
{
|
|
t = origin.mV[i] - max[i];
|
|
d += t*t;
|
|
}
|
|
|
|
if (d > r)
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
typedef enum
|
|
{
|
|
b000 = 0x00,
|
|
b001 = 0x01,
|
|
b010 = 0x02,
|
|
b011 = 0x03,
|
|
b100 = 0x04,
|
|
b101 = 0x05,
|
|
b110 = 0x06,
|
|
b111 = 0x07,
|
|
} eLoveTheBits;
|
|
|
|
//contact Runitai Linden for a copy of the SL object used to write this table
|
|
//basically, you give the table a bitmask of the look-at vector to a node and it
|
|
//gives you a triangle fan index array
|
|
static U16 sOcclusionIndices[] =
|
|
{
|
|
//000
|
|
b111, b110, b010, b011, b001, b101, b100, b110,
|
|
//001
|
|
b011, b010, b000, b001, b101, b111, b110, b010,
|
|
//010
|
|
b101, b100, b110, b111, b011, b001, b000, b100,
|
|
//011
|
|
b001, b000, b100, b101, b111, b011, b010, b000,
|
|
//100
|
|
b110, b000, b010, b011, b111, b101, b100, b000,
|
|
//101
|
|
b010, b100, b000, b001, b011, b111, b110, b100,
|
|
//110
|
|
b100, b010, b110, b111, b101, b001, b000, b010,
|
|
//111
|
|
b000, b110, b100, b101, b001, b011, b010, b110,
|
|
};
|
|
|
|
U32 get_box_fan_indices(LLCamera* camera, const LLVector4a& center)
|
|
{
|
|
LLVector4a origin;
|
|
origin.load3(camera->getOrigin().mV);
|
|
|
|
S32 cypher = center.greaterThan(origin).getGatheredBits() & 0x7;
|
|
|
|
return cypher*8;
|
|
}
|
|
|
|
U8* get_box_fan_indices_ptr(LLCamera* camera, const LLVector4a& center)
|
|
{
|
|
LLVector4a origin;
|
|
origin.load3(camera->getOrigin().mV);
|
|
|
|
S32 cypher = center.greaterThan(origin).getGatheredBits() & 0x7;
|
|
|
|
return (U8*) (sOcclusionIndices+cypher*8);
|
|
}
|
|
|
|
//create a vertex buffer for efficiently rendering cubes
|
|
LLVertexBuffer* ll_create_cube_vb(U32 type_mask, U32 usage)
|
|
{
|
|
LLVertexBuffer* ret = new LLVertexBuffer(type_mask, usage);
|
|
|
|
ret->allocateBuffer(8, 64, true);
|
|
|
|
LLStrider<LLVector3> pos;
|
|
LLStrider<U16> idx;
|
|
|
|
ret->getVertexStrider(pos);
|
|
ret->getIndexStrider(idx);
|
|
|
|
pos[0] = LLVector3(-1,-1,-1);
|
|
pos[1] = LLVector3(-1,-1, 1);
|
|
pos[2] = LLVector3(-1, 1,-1);
|
|
pos[3] = LLVector3(-1, 1, 1);
|
|
pos[4] = LLVector3( 1,-1,-1);
|
|
pos[5] = LLVector3( 1,-1, 1);
|
|
pos[6] = LLVector3( 1, 1,-1);
|
|
pos[7] = LLVector3( 1, 1, 1);
|
|
|
|
for (U32 i = 0; i < 64; i++)
|
|
{
|
|
idx[i] = sOcclusionIndices[i];
|
|
}
|
|
|
|
ret->flush();
|
|
|
|
return ret;
|
|
}
|
|
|
|
static LLFastTimer::DeclareTimer FTM_BUILD_OCCLUSION("Build Occlusion");
|
|
|
|
BOOL earlyFail(LLCamera* camera, LLSpatialGroup* group);
|
|
|
|
//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)
|
|
{
|
|
llerrs << "Illegal deletion of LLSpatialGroup!" << llendl;
|
|
}*/
|
|
|
|
if (gDebugGL)
|
|
{
|
|
gPipeline.checkReferences(this);
|
|
}
|
|
if (isState(DEAD))
|
|
{
|
|
sZombieGroups--;
|
|
}
|
|
|
|
sNodeCount--;
|
|
|
|
if (gGLManager.mHasOcclusionQuery)
|
|
{
|
|
for (U32 i = 0; i < LLViewerCamera::NUM_CAMERAS; ++i)
|
|
{
|
|
if (mOcclusionQuery[i])
|
|
{
|
|
sQueryPool.release(mOcclusionQuery[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
clearDrawMap();
|
|
}
|
|
|
|
void LLSpatialGroup::clearDrawMap()
|
|
{
|
|
mDrawMap.clear();
|
|
}
|
|
|
|
BOOL LLSpatialGroup::isHUDGroup()
|
|
{
|
|
return mSpatialPartition && mSpatialPartition->isHUDPartition() ;
|
|
}
|
|
|
|
BOOL LLSpatialGroup::isRecentlyVisible() const
|
|
{
|
|
return (LLDrawable::getCurrentFrame() - mVisible[LLViewerCamera::sCurCameraID]) < LLDrawable::getMinVisFrameRange() ;
|
|
}
|
|
|
|
BOOL LLSpatialGroup::isVisible() const
|
|
{
|
|
return mVisible[LLViewerCamera::sCurCameraID] >= LLDrawable::getCurrentFrame() ? TRUE : FALSE;
|
|
}
|
|
|
|
void LLSpatialGroup::setVisible()
|
|
{
|
|
mVisible[LLViewerCamera::sCurCameraID] = LLDrawable::getCurrentFrame();
|
|
}
|
|
|
|
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() == mSpatialPartition->asBridge());
|
|
}
|
|
|
|
/*if (drawable->isSpatialBridge())
|
|
{
|
|
LLSpatialPartition* part = drawable->asPartition();
|
|
if (!part)
|
|
{
|
|
llerrs << "Drawable reports it is a spatial bridge but not a partition." << llendl;
|
|
}
|
|
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::checkStates()
|
|
{
|
|
#if LL_OCTREE_PARANOIA_CHECK
|
|
LLOctreeStateCheck checker;
|
|
checker.traverse(mOctreeNode);
|
|
#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) ||
|
|
(drawablep->getBinRadius() > mOctreeNode->getSize()[0] &&
|
|
parent && parent->getElementCount() >= gOctreeMaxCapacity)))
|
|
{
|
|
unbound();
|
|
setState(OBJECT_DIRTY);
|
|
//setState(GEOM_DIRTY);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOL LLSpatialGroup::addObject(LLDrawable *drawablep, BOOL add_all, BOOL from_octree)
|
|
{
|
|
if (!from_octree)
|
|
{
|
|
mOctreeNode->insert(drawablep);
|
|
}
|
|
else
|
|
{
|
|
drawablep->setSpatialGroup(this);
|
|
setState(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())
|
|
{
|
|
mSpatialPartition->rebuildGeom(this);
|
|
|
|
if (isState(LLSpatialGroup::MESH_DIRTY))
|
|
{
|
|
gPipeline.markMeshDirty(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
void LLSpatialGroup::rebuildMesh()
|
|
{
|
|
if (!isDead())
|
|
{
|
|
mSpatialPartition->rebuildMesh(this);
|
|
}
|
|
}
|
|
|
|
static LLFastTimer::DeclareTimer FTM_REBUILD_VBO("VBO Rebuilt");
|
|
static LLFastTimer::DeclareTimer FTM_ADD_GEOMETRY_COUNT("Add Geometry");
|
|
static LLFastTimer::DeclareTimer FTM_CREATE_VB("Create VB");
|
|
static LLFastTimer::DeclareTimer FTM_GET_GEOMETRY("Get Geometry");
|
|
|
|
void LLSpatialPartition::rebuildGeom(LLSpatialGroup* group)
|
|
{
|
|
if (group->isDead() || !group->isState(LLSpatialGroup::GEOM_DIRTY))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!LLPipeline::sSkipUpdate && group->changeLOD())
|
|
{
|
|
group->mLastUpdateDistance = group->mDistance;
|
|
group->mLastUpdateViewAngle = group->mViewAngle;
|
|
}
|
|
|
|
LLFastTimer ftm(FTM_REBUILD_VBO);
|
|
|
|
group->clearDrawMap();
|
|
|
|
//get geometry count
|
|
U32 index_count = 0;
|
|
U32 vertex_count = 0;
|
|
|
|
{
|
|
LLFastTimer t(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
|
|
{
|
|
LLFastTimer t(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();
|
|
}
|
|
}
|
|
|
|
{
|
|
LLFastTimer t(FTM_GET_GEOMETRY);
|
|
getGeometry(group);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
group->mVertexBuffer = NULL;
|
|
group->mBufferMap.clear();
|
|
}
|
|
|
|
group->mLastUpdateTime = gFrameTimeSeconds;
|
|
group->clearState(LLSpatialGroup::GEOM_DIRTY);
|
|
}
|
|
|
|
void LLSpatialPartition::rebuildMesh(LLSpatialGroup* group)
|
|
{
|
|
|
|
}
|
|
|
|
BOOL LLSpatialGroup::boundObjects(BOOL empty, LLVector4a& minOut, LLVector4a& maxOut)
|
|
{
|
|
const OctreeNode* node = mOctreeNode;
|
|
|
|
if (node->isEmpty())
|
|
{ //don't do anything if there are no objects
|
|
if (empty && mOctreeNode->getParent())
|
|
{ //only root is allowed to be empty
|
|
OCT_ERRS << "Empty leaf found in octree." << llendl;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
LLVector4a& newMin = mObjectExtents[0];
|
|
LLVector4a& newMax = mObjectExtents[1];
|
|
|
|
if (isState(OBJECT_DIRTY))
|
|
{ //calculate new bounding box
|
|
clearState(OBJECT_DIRTY);
|
|
|
|
//initialize bounding box to first element
|
|
OctreeNode::const_element_iter i = node->getDataBegin();
|
|
LLDrawable* drawablep = *i;
|
|
const LLVector4a* minMax = drawablep->getSpatialExtents();
|
|
|
|
newMin = minMax[0];
|
|
newMax = minMax[1];
|
|
|
|
for (++i; i != node->getDataEnd(); ++i)
|
|
{
|
|
drawablep = *i;
|
|
minMax = drawablep->getSpatialExtents();
|
|
|
|
update_min_max(newMin, newMax, minMax[0]);
|
|
update_min_max(newMin, newMax, minMax[1]);
|
|
|
|
//bin up the object
|
|
/*for (U32 i = 0; i < 3; i++)
|
|
{
|
|
if (minMax[0].mV[i] < newMin.mV[i])
|
|
{
|
|
newMin.mV[i] = minMax[0].mV[i];
|
|
}
|
|
if (minMax[1].mV[i] > newMax.mV[i])
|
|
{
|
|
newMax.mV[i] = minMax[1].mV[i];
|
|
}
|
|
}*/
|
|
}
|
|
|
|
mObjectBounds[0].setAdd(newMin, newMax);
|
|
mObjectBounds[0].mul(0.5f);
|
|
mObjectBounds[1].setSub(newMax, newMin);
|
|
mObjectBounds[1].mul(0.5f);
|
|
}
|
|
|
|
if (empty)
|
|
{
|
|
minOut = newMin;
|
|
maxOut = newMax;
|
|
}
|
|
else
|
|
{
|
|
minOut.setMin(minOut, newMin);
|
|
maxOut.setMax(maxOut, newMax);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void LLSpatialGroup::unbound()
|
|
{
|
|
if (isState(DIRTY))
|
|
{
|
|
return;
|
|
}
|
|
|
|
setState(DIRTY);
|
|
|
|
//all the parent nodes need to rebound this child
|
|
if (mOctreeNode)
|
|
{
|
|
OctreeNode* parent = (OctreeNode*) mOctreeNode->getParent();
|
|
while (parent != NULL)
|
|
{
|
|
LLSpatialGroup* group = (LLSpatialGroup*) parent->getListener(0);
|
|
if (group->isState(DIRTY))
|
|
{
|
|
return;
|
|
}
|
|
|
|
group->setState(DIRTY);
|
|
parent = (OctreeNode*) parent->getParent();
|
|
}
|
|
}
|
|
}
|
|
|
|
LLSpatialGroup* LLSpatialGroup::getParent()
|
|
{
|
|
if (isDead())
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if(!mOctreeNode)
|
|
{
|
|
return NULL;
|
|
}
|
|
OctreeNode* parent = mOctreeNode->getOctParent();
|
|
|
|
if (parent)
|
|
{
|
|
return (LLSpatialGroup*) parent->getListener(0);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
BOOL LLSpatialGroup::removeObject(LLDrawable *drawablep, BOOL from_octree)
|
|
{
|
|
unbound();
|
|
if (mOctreeNode && !from_octree)
|
|
{
|
|
if (!mOctreeNode->remove(drawablep))
|
|
{
|
|
OCT_ERRS << "Could not remove drawable from spatial group" << llendl;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
drawablep->setSpatialGroup(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 (!mSpatialPartition->mRenderByGroup &&
|
|
mSpatialPartition->mPartitionType != LLViewerRegion::PARTITION_TREE &&
|
|
mSpatialPartition->mPartitionType != LLViewerRegion::PARTITION_TERRAIN &&
|
|
mSpatialPartition->mPartitionType != LLViewerRegion::PARTITION_BRIDGE)
|
|
{
|
|
setState(GEOM_DIRTY);
|
|
gPipeline.markRebuild(this, TRUE);
|
|
}
|
|
}
|
|
|
|
class LLSpatialSetState : public LLSpatialGroup::OctreeTraveler
|
|
{
|
|
public:
|
|
LLSpatialGroup::eSpatialState mState;
|
|
LLSpatialSetState(LLSpatialGroup::eSpatialState state) : mState(state) { }
|
|
virtual void visit(const LLSpatialGroup::OctreeNode* branch) { ((LLSpatialGroup*) branch->getListener(0))->setState(mState); }
|
|
};
|
|
|
|
class LLSpatialSetStateDiff : public LLSpatialSetState
|
|
{
|
|
public:
|
|
LLSpatialSetStateDiff(LLSpatialGroup::eSpatialState state) : LLSpatialSetState(state) { }
|
|
|
|
virtual void traverse(const LLSpatialGroup::OctreeNode* n)
|
|
{
|
|
LLSpatialGroup* group = (LLSpatialGroup*) n->getListener(0);
|
|
|
|
if (!group->isState(mState))
|
|
{
|
|
LLSpatialGroup::OctreeTraveler::traverse(n);
|
|
}
|
|
}
|
|
};
|
|
|
|
void LLSpatialGroup::setState(eSpatialState state)
|
|
{
|
|
// if (LLSpatialPartition::sFreezeState)
|
|
// return;
|
|
mState |= state;
|
|
|
|
llassert(state <= LLSpatialGroup::STATE_MASK);
|
|
}
|
|
|
|
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 LLSpatialGroup::OctreeTraveler
|
|
{
|
|
public:
|
|
LLSpatialGroup::eSpatialState mState;
|
|
LLSpatialClearState(LLSpatialGroup::eSpatialState state) : mState(state) { }
|
|
virtual void visit(const LLSpatialGroup::OctreeNode* branch) { ((LLSpatialGroup*) branch->getListener(0))->clearState(mState); }
|
|
};
|
|
|
|
class LLSpatialClearStateDiff : public LLSpatialClearState
|
|
{
|
|
public:
|
|
LLSpatialClearStateDiff(LLSpatialGroup::eSpatialState state) : LLSpatialClearState(state) { }
|
|
|
|
virtual void traverse(const LLSpatialGroup::OctreeNode* n)
|
|
{
|
|
LLSpatialGroup* group = (LLSpatialGroup*) n->getListener(0);
|
|
|
|
if (group->isState(mState))
|
|
{
|
|
LLSpatialGroup::OctreeTraveler::traverse(n);
|
|
}
|
|
}
|
|
};
|
|
|
|
void LLSpatialGroup::clearState(eSpatialState state)
|
|
{
|
|
llassert(state <= LLSpatialGroup::STATE_MASK);
|
|
|
|
mState &= ~state;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
BOOL LLSpatialGroup::isState(eSpatialState state) const
|
|
{
|
|
llassert(state <= LLSpatialGroup::STATE_MASK);
|
|
|
|
return mState & state ? TRUE : FALSE;
|
|
}
|
|
|
|
//=====================================
|
|
// Occlusion State Set/Clear
|
|
//=====================================
|
|
class LLSpatialSetOcclusionState : public LLSpatialGroup::OctreeTraveler
|
|
{
|
|
public:
|
|
LLSpatialGroup::eOcclusionState mState;
|
|
LLSpatialSetOcclusionState(LLSpatialGroup::eOcclusionState state) : mState(state) { }
|
|
virtual void visit(const LLSpatialGroup::OctreeNode* branch) { ((LLSpatialGroup*) branch->getListener(0))->setOcclusionState(mState); }
|
|
};
|
|
|
|
class LLSpatialSetOcclusionStateDiff : public LLSpatialSetOcclusionState
|
|
{
|
|
public:
|
|
LLSpatialSetOcclusionStateDiff(LLSpatialGroup::eOcclusionState state) : LLSpatialSetOcclusionState(state) { }
|
|
|
|
virtual void traverse(const LLSpatialGroup::OctreeNode* n)
|
|
{
|
|
LLSpatialGroup* group = (LLSpatialGroup*) n->getListener(0);
|
|
|
|
if (!group->isOcclusionState(mState))
|
|
{
|
|
LLSpatialGroup::OctreeTraveler::traverse(n);
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
void LLSpatialGroup::setOcclusionState(eOcclusionState state, S32 mode)
|
|
{
|
|
if (mode > STATE_MODE_SINGLE)
|
|
{
|
|
if (mode == STATE_MODE_DIFF)
|
|
{
|
|
LLSpatialSetOcclusionStateDiff setter(state);
|
|
setter.traverse(mOctreeNode);
|
|
}
|
|
else if (mode == STATE_MODE_BRANCH)
|
|
{
|
|
LLSpatialSetOcclusionState setter(state);
|
|
setter.traverse(mOctreeNode);
|
|
}
|
|
else
|
|
{
|
|
for (U32 i = 0; i < LLViewerCamera::NUM_CAMERAS; i++)
|
|
{
|
|
mOcclusionState[i] |= state;
|
|
|
|
if ((state & DISCARD_QUERY) && mOcclusionQuery[i])
|
|
{
|
|
sQueryPool.release(mOcclusionQuery[i]);
|
|
mOcclusionQuery[i] = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mOcclusionState[LLViewerCamera::sCurCameraID] |= state;
|
|
if ((state & DISCARD_QUERY) && mOcclusionQuery[LLViewerCamera::sCurCameraID])
|
|
{
|
|
sQueryPool.release(mOcclusionQuery[LLViewerCamera::sCurCameraID]);
|
|
mOcclusionQuery[LLViewerCamera::sCurCameraID] = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
class LLSpatialClearOcclusionState : public LLSpatialGroup::OctreeTraveler
|
|
{
|
|
public:
|
|
LLSpatialGroup::eOcclusionState mState;
|
|
|
|
LLSpatialClearOcclusionState(LLSpatialGroup::eOcclusionState state) : mState(state) { }
|
|
virtual void visit(const LLSpatialGroup::OctreeNode* branch) { ((LLSpatialGroup*) branch->getListener(0))->clearOcclusionState(mState); }
|
|
};
|
|
|
|
class LLSpatialClearOcclusionStateDiff : public LLSpatialClearOcclusionState
|
|
{
|
|
public:
|
|
LLSpatialClearOcclusionStateDiff(LLSpatialGroup::eOcclusionState state) : LLSpatialClearOcclusionState(state) { }
|
|
|
|
virtual void traverse(const LLSpatialGroup::OctreeNode* n)
|
|
{
|
|
LLSpatialGroup* group = (LLSpatialGroup*) n->getListener(0);
|
|
|
|
if (group->isOcclusionState(mState))
|
|
{
|
|
LLSpatialGroup::OctreeTraveler::traverse(n);
|
|
}
|
|
}
|
|
};
|
|
|
|
void LLSpatialGroup::clearOcclusionState(eOcclusionState state, S32 mode)
|
|
{
|
|
if (mode > STATE_MODE_SINGLE)
|
|
{
|
|
if (mode == STATE_MODE_DIFF)
|
|
{
|
|
LLSpatialClearOcclusionStateDiff clearer(state);
|
|
clearer.traverse(mOctreeNode);
|
|
}
|
|
else if (mode == STATE_MODE_BRANCH)
|
|
{
|
|
LLSpatialClearOcclusionState clearer(state);
|
|
clearer.traverse(mOctreeNode);
|
|
}
|
|
else
|
|
{
|
|
for (U32 i = 0; i < LLViewerCamera::NUM_CAMERAS; i++)
|
|
{
|
|
mOcclusionState[i] &= ~state;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mOcclusionState[LLViewerCamera::sCurCameraID] &= ~state;
|
|
}
|
|
}
|
|
//======================================
|
|
// Octree Listener Implementation
|
|
//======================================
|
|
|
|
LLSpatialGroup::LLSpatialGroup(OctreeNode* node, LLSpatialPartition* part) :
|
|
mObjectBoxSize(1.f),
|
|
mState(0),
|
|
mGeometryBytes(0),
|
|
mSurfaceArea(0.f),
|
|
mBuilt(0.f),
|
|
mOctreeNode(node),
|
|
mSpatialPartition(part),
|
|
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);
|
|
mExtents[0] = mExtents[1] = mObjectBounds[0] = mObjectBounds[1] =
|
|
mObjectExtents[0] = mObjectExtents[1] = mViewAngle;
|
|
|
|
sg_assert(mOctreeNode->getListenerCount() == 0);
|
|
mOctreeNode->addListener(this);
|
|
setState(SG_INITIAL_STATE_MASK);
|
|
gPipeline.markRebuild(this, TRUE);
|
|
|
|
mBounds[0] = node->getCenter();
|
|
mBounds[1] = node->getSize();
|
|
|
|
part->mLODSeed = (part->mLODSeed+1)%part->mLODPeriod;
|
|
mLODHash = part->mLODSeed;
|
|
|
|
OctreeNode* oct_parent = node->getOctParent();
|
|
|
|
LLSpatialGroup* parent = oct_parent ? (LLSpatialGroup*) oct_parent->getListener(0) : NULL;
|
|
|
|
for (U32 i = 0; i < LLViewerCamera::NUM_CAMERAS; i++)
|
|
{
|
|
mOcclusionQuery[i] = 0;
|
|
mOcclusionIssued[i] = 0;
|
|
mOcclusionState[i] = parent ? SG_STATE_INHERIT_MASK & parent->mOcclusionState[i] : 0;
|
|
mVisible[i] = 0;
|
|
}
|
|
|
|
mRadius = 1;
|
|
mPixelArea = 1024.f;
|
|
}
|
|
|
|
void LLSpatialGroup::updateDistance(LLCamera &camera)
|
|
{
|
|
if (LLViewerCamera::sCurCameraID != LLViewerCamera::CAMERA_WORLD)
|
|
{
|
|
llwarns << "Attempted to update distance for camera other than world camera!" << llendl;
|
|
return;
|
|
}
|
|
|
|
if (gShiftFrame)
|
|
{
|
|
return;
|
|
}
|
|
|
|
#if !LL_RELEASE_FOR_DOWNLOAD
|
|
if (isState(LLSpatialGroup::OBJECT_DIRTY))
|
|
{
|
|
llerrs << "Spatial group dirty on distance update." << llendl;
|
|
}
|
|
#endif
|
|
if (!isEmpty() /*&& !LLSpatialPartition::sFreezeState*/)
|
|
{
|
|
mRadius = mSpatialPartition->mRenderByGroup ? mObjectBounds[1].getLength3().getF32() :
|
|
(F32) mOctreeNode->getSize().getLength3().getF32();
|
|
mDistance = mSpatialPartition->calcDistance(this, camera);
|
|
mPixelArea = mSpatialPartition->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->isState(LLSpatialGroup::ALPHA_DIRTY))
|
|
{
|
|
if (!group->mSpatialPartition->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::needsUpdate()
|
|
{
|
|
return (LLDrawable::getCurrentFrame()%mSpatialPartition->mLODPeriod == mLODHash) ? TRUE : FALSE;
|
|
}
|
|
|
|
BOOL LLSpatialGroup::changeLOD()
|
|
{
|
|
if (isState(ALPHA_DIRTY | OBJECT_DIRTY))
|
|
{ ///a rebuild is going to happen, update distance and LoD
|
|
return TRUE;
|
|
}
|
|
|
|
if (mSpatialPartition->mSlopRatio > 0.f)
|
|
{
|
|
F32 ratio = (mDistance - mLastUpdateDistance)/(llmax(mLastUpdateDistance, mRadius));
|
|
|
|
if (fabsf(ratio) >= mSpatialPartition->mSlopRatio)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
if (mDistance > mRadius*2.f)
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (needsUpdate())
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void LLSpatialGroup::handleInsertion(const TreeNode* node, LLDrawable* drawablep)
|
|
{
|
|
addObject(drawablep, FALSE, TRUE);
|
|
unbound();
|
|
setState(OBJECT_DIRTY);
|
|
}
|
|
|
|
void LLSpatialGroup::handleRemoval(const TreeNode* node, LLDrawable* drawable)
|
|
{
|
|
removeObject(drawable, TRUE);
|
|
setState(OBJECT_DIRTY);
|
|
}
|
|
|
|
void LLSpatialGroup::handleDestruction(const TreeNode* node)
|
|
{
|
|
setState(DEAD);
|
|
|
|
{OctreeGuard guard(mOctreeNode);
|
|
for (element_iter i = getDataBegin(); i != getDataEnd(); ++i)
|
|
{
|
|
LLDrawable* drawable = *i;
|
|
if (drawable->getSpatialGroup() == this)
|
|
{
|
|
drawable->setSpatialGroup(NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
//clean up avatar attachment stats
|
|
LLSpatialBridge* bridge = mSpatialPartition->asBridge();
|
|
if (bridge)
|
|
{
|
|
if (bridge->mAvatar.notNull())
|
|
{
|
|
bridge->mAvatar->mAttachmentGeometryBytes -= mGeometryBytes;
|
|
bridge->mAvatar->mAttachmentSurfaceArea -= mSurfaceArea;
|
|
}
|
|
}
|
|
|
|
clearDrawMap();
|
|
mVertexBuffer = NULL;
|
|
mBufferMap.clear();
|
|
sZombieGroups++;
|
|
mOctreeNode = NULL;
|
|
}
|
|
|
|
void LLSpatialGroup::handleStateChange(const TreeNode* node)
|
|
{
|
|
//drop bounding box upon state change
|
|
if (mOctreeNode != node)
|
|
{
|
|
mOctreeNode = (OctreeNode*) node;
|
|
}
|
|
unbound();
|
|
}
|
|
|
|
void LLSpatialGroup::handleChildAddition(const OctreeNode* parent, OctreeNode* child)
|
|
{
|
|
if (child->getListenerCount() == 0)
|
|
{
|
|
new LLSpatialGroup(child, mSpatialPartition);
|
|
}
|
|
else
|
|
{
|
|
OCT_ERRS << "LLSpatialGroup redundancy detected." << llendl;
|
|
}
|
|
|
|
unbound();
|
|
|
|
assert_states_valid(this);
|
|
}
|
|
|
|
void LLSpatialGroup::handleChildRemoval(const OctreeNode* parent, const OctreeNode* child)
|
|
{
|
|
unbound();
|
|
}
|
|
|
|
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;
|
|
mBufferMap.clear();
|
|
|
|
clearDrawMap();
|
|
|
|
if (!keep_occlusion)
|
|
{
|
|
for (U32 i = 0; i < LLViewerCamera::NUM_CAMERAS; i++)
|
|
{
|
|
if (mOcclusionQuery[i])
|
|
{
|
|
sQueryPool.release(mOcclusionQuery[i]);
|
|
mOcclusionQuery[i] = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
OctreeGuard guard(mOctreeNode);
|
|
for (LLSpatialGroup::element_iter i = getDataBegin(); i != getDataEnd(); ++i)
|
|
{
|
|
LLDrawable* drawable = *i;
|
|
for (S32 j = 0; j < drawable->getNumFaces(); j++)
|
|
{
|
|
LLFace* facep = drawable->getFace(j);
|
|
if (facep)
|
|
{
|
|
facep->clearVertexBuffer();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOL LLSpatialGroup::rebound()
|
|
{
|
|
if (!isState(DIRTY))
|
|
{ //return TRUE if we're not empty
|
|
return TRUE;
|
|
}
|
|
|
|
if (mOctreeNode->getChildCount() == 1 && mOctreeNode->getElementCount() == 0)
|
|
{
|
|
LLSpatialGroup* group = (LLSpatialGroup*) mOctreeNode->getChild(0)->getListener(0);
|
|
group->rebound();
|
|
|
|
//copy single child's bounding box
|
|
mBounds[0] = group->mBounds[0];
|
|
mBounds[1] = group->mBounds[1];
|
|
mExtents[0] = group->mExtents[0];
|
|
mExtents[1] = group->mExtents[1];
|
|
|
|
group->setState(SKIP_FRUSTUM_CHECK);
|
|
}
|
|
else if (mOctreeNode->isLeaf())
|
|
{ //copy object bounding box if this is a leaf
|
|
boundObjects(TRUE, mExtents[0], mExtents[1]);
|
|
mBounds[0] = mObjectBounds[0];
|
|
mBounds[1] = mObjectBounds[1];
|
|
}
|
|
else
|
|
{
|
|
LLVector4a& newMin = mExtents[0];
|
|
LLVector4a& newMax = mExtents[1];
|
|
LLSpatialGroup* group = (LLSpatialGroup*) mOctreeNode->getChild(0)->getListener(0);
|
|
group->clearState(SKIP_FRUSTUM_CHECK);
|
|
group->rebound();
|
|
//initialize to first child
|
|
newMin = group->mExtents[0];
|
|
newMax = group->mExtents[1];
|
|
|
|
//first, rebound children
|
|
for (U32 i = 1; i < mOctreeNode->getChildCount(); i++)
|
|
{
|
|
group = (LLSpatialGroup*) mOctreeNode->getChild(i)->getListener(0);
|
|
group->clearState(SKIP_FRUSTUM_CHECK);
|
|
group->rebound();
|
|
const LLVector4a& max = group->mExtents[1];
|
|
const LLVector4a& min = group->mExtents[0];
|
|
|
|
newMax.setMax(newMax, max);
|
|
newMin.setMin(newMin, min);
|
|
}
|
|
|
|
boundObjects(FALSE, newMin, newMax);
|
|
|
|
mBounds[0].setAdd(newMin, newMax);
|
|
mBounds[0].mul(0.5f);
|
|
mBounds[1].setSub(newMax, newMin);
|
|
mBounds[1].mul(0.5f);
|
|
}
|
|
|
|
clearState(DIRTY);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static LLFastTimer::DeclareTimer FTM_OCCLUSION_READBACK("Readback Occlusion");
|
|
static LLFastTimer::DeclareTimer FTM_OCCLUSION_WAIT("Occlusion Wait");
|
|
|
|
void LLSpatialGroup::checkOcclusion()
|
|
{
|
|
if (LLPipeline::sUseOcclusion > 1)
|
|
{
|
|
LLFastTimer t(FTM_OCCLUSION_READBACK);
|
|
LLSpatialGroup* parent = getParent();
|
|
if (parent && parent->isOcclusionState(LLSpatialGroup::OCCLUDED))
|
|
{ //if the parent has been marked as occluded, the child is implicitly occluded
|
|
clearOcclusionState(QUERY_PENDING | DISCARD_QUERY);
|
|
}
|
|
else if (isOcclusionState(QUERY_PENDING))
|
|
{ //otherwise, if a query is pending, read it back
|
|
|
|
GLuint available = 0;
|
|
if (mOcclusionQuery[LLViewerCamera::sCurCameraID])
|
|
{
|
|
glGetQueryObjectuivARB(mOcclusionQuery[LLViewerCamera::sCurCameraID], GL_QUERY_RESULT_AVAILABLE_ARB, &available);
|
|
|
|
static LLCachedControl<bool> wait_for_query("RenderSynchronousOcclusion", true);
|
|
|
|
if (wait_for_query && mOcclusionIssued[LLViewerCamera::sCurCameraID] < gFrameCount)
|
|
{ //query was issued last frame, wait until it's available
|
|
S32 max_loop = 1024;
|
|
LLFastTimer t(FTM_OCCLUSION_WAIT);
|
|
while (!available && max_loop-- > 0)
|
|
{
|
|
F32 max_time = llmin(gFrameIntervalSeconds*10.f, 1.f);
|
|
//do some usefu work while we wait
|
|
LLAppViewer::getTextureCache()->update(max_time); // unpauses the texture cache thread
|
|
LLAppViewer::getImageDecodeThread()->update(max_time); // unpauses the image thread
|
|
LLAppViewer::getTextureFetch()->update(max_time); // unpauses the texture fetch thread
|
|
|
|
glGetQueryObjectuivARB(mOcclusionQuery[LLViewerCamera::sCurCameraID], GL_QUERY_RESULT_AVAILABLE_ARB, &available);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
available = 1;
|
|
}
|
|
|
|
if (available)
|
|
{ //result is available, read it back, otherwise wait until next frame
|
|
GLuint res = 1;
|
|
if (!isOcclusionState(DISCARD_QUERY) && mOcclusionQuery[LLViewerCamera::sCurCameraID])
|
|
{
|
|
glGetQueryObjectuivARB(mOcclusionQuery[LLViewerCamera::sCurCameraID], GL_QUERY_RESULT_ARB, &res);
|
|
#if LL_TRACK_PENDING_OCCLUSION_QUERIES
|
|
sPendingQueries.erase(mOcclusionQuery[LLViewerCamera::sCurCameraID]);
|
|
#endif
|
|
}
|
|
else if (mOcclusionQuery[LLViewerCamera::sCurCameraID])
|
|
{ //delete the query to avoid holding onto hundreds of pending queries
|
|
sQueryPool.release(mOcclusionQuery[LLViewerCamera::sCurCameraID]);
|
|
mOcclusionQuery[LLViewerCamera::sCurCameraID] = 0;
|
|
}
|
|
|
|
if (isOcclusionState(DISCARD_QUERY))
|
|
{
|
|
res = 2;
|
|
}
|
|
|
|
if (res > 0)
|
|
{
|
|
assert_states_valid(this);
|
|
clearOcclusionState(LLSpatialGroup::OCCLUDED, LLSpatialGroup::STATE_MODE_DIFF);
|
|
assert_states_valid(this);
|
|
}
|
|
else
|
|
{
|
|
assert_states_valid(this);
|
|
setOcclusionState(LLSpatialGroup::OCCLUDED, LLSpatialGroup::STATE_MODE_DIFF);
|
|
assert_states_valid(this);
|
|
}
|
|
|
|
clearOcclusionState(QUERY_PENDING | DISCARD_QUERY);
|
|
}
|
|
}
|
|
else if (mSpatialPartition->isOcclusionEnabled() && isOcclusionState(LLSpatialGroup::OCCLUDED))
|
|
{ //check occlusion has been issued for occluded node that has not had a query issued
|
|
assert_states_valid(this);
|
|
clearOcclusionState(LLSpatialGroup::OCCLUDED, LLSpatialGroup::STATE_MODE_DIFF);
|
|
assert_states_valid(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
static LLFastTimer::DeclareTimer FTM_PUSH_OCCLUSION_VERTS("Push Occlusion");
|
|
static LLFastTimer::DeclareTimer FTM_SET_OCCLUSION_STATE("Occlusion State");
|
|
static LLFastTimer::DeclareTimer FTM_OCCLUSION_EARLY_FAIL("Occlusion Early Fail");
|
|
static LLFastTimer::DeclareTimer FTM_OCCLUSION_ALLOCATE("Allocate");
|
|
static LLFastTimer::DeclareTimer FTM_OCCLUSION_BUILD("Build");
|
|
static LLFastTimer::DeclareTimer FTM_OCCLUSION_BEGIN_QUERY("Begin Query");
|
|
static LLFastTimer::DeclareTimer FTM_OCCLUSION_END_QUERY("End Query");
|
|
static LLFastTimer::DeclareTimer FTM_OCCLUSION_SET_BUFFER("Set Buffer");
|
|
static LLFastTimer::DeclareTimer FTM_OCCLUSION_DRAW_WATER("Draw Water");
|
|
static LLFastTimer::DeclareTimer FTM_OCCLUSION_DRAW("Draw");
|
|
|
|
|
|
|
|
void LLSpatialGroup::doOcclusion(LLCamera* camera)
|
|
{
|
|
LLGLDisable stencil(GL_STENCIL_TEST);
|
|
if (mSpatialPartition->isOcclusionEnabled() && LLPipeline::sUseOcclusion > 1)
|
|
{
|
|
//static const LLCachedControl<BOOL> render_water_void_culling("RenderWaterVoidCulling", TRUE);
|
|
// Don't cull hole/edge water, unless RenderWaterVoidCulling is set and we have the GL_ARB_depth_clamp extension.
|
|
//if ((mSpatialPartition->mDrawableType == LLDrawPool::POOL_VOIDWATER && !gGLManager.mHasDepthClamp) ||
|
|
// earlyFail(camera, this))
|
|
if (earlyFail(camera, this)) //Returns true if camera is inside this spatial group.
|
|
{
|
|
LLFastTimer t(FTM_OCCLUSION_EARLY_FAIL);
|
|
setOcclusionState(LLSpatialGroup::DISCARD_QUERY);
|
|
assert_states_valid(this);
|
|
clearOcclusionState(LLSpatialGroup::OCCLUDED, LLSpatialGroup::STATE_MODE_DIFF);
|
|
assert_states_valid(this);
|
|
}
|
|
else
|
|
{
|
|
if (!isOcclusionState(QUERY_PENDING) || isOcclusionState(DISCARD_QUERY))
|
|
{
|
|
{ //no query pending, or previous query to be discarded
|
|
LLFastTimer t(FTM_RENDER_OCCLUSION);
|
|
|
|
if (!mOcclusionQuery[LLViewerCamera::sCurCameraID])
|
|
{
|
|
LLFastTimer t(FTM_OCCLUSION_ALLOCATE);
|
|
mOcclusionQuery[LLViewerCamera::sCurCameraID] = sQueryPool.allocate();
|
|
}
|
|
|
|
// Depth clamp all water to avoid it being culled as a result of being
|
|
// behind the far clip plane, and in the case of edge water to avoid
|
|
// it being culled while still visible.
|
|
bool const use_depth_clamp = gGLManager.mHasDepthClamp &&
|
|
(mSpatialPartition->mDrawableType == LLDrawPool::POOL_WATER ||
|
|
mSpatialPartition->mDrawableType == LLDrawPool::POOL_VOIDWATER);
|
|
|
|
LLGLEnable clamp(use_depth_clamp ? GL_DEPTH_CLAMP : 0);
|
|
|
|
#if !LL_DARWIN
|
|
U32 mode = gGLManager.mHasOcclusionQuery2 ? GL_ANY_SAMPLES_PASSED : GL_SAMPLES_PASSED_ARB;
|
|
#else
|
|
U32 mode = GL_SAMPLES_PASSED_ARB;
|
|
#endif
|
|
|
|
#if LL_TRACK_PENDING_OCCLUSION_QUERIES
|
|
sPendingQueries.insert(mOcclusionQuery[LLViewerCamera::sCurCameraID]);
|
|
#endif
|
|
|
|
{
|
|
LLFastTimer t(FTM_PUSH_OCCLUSION_VERTS);
|
|
|
|
//store which frame this query was issued on
|
|
mOcclusionIssued[LLViewerCamera::sCurCameraID] = gFrameCount;
|
|
|
|
{
|
|
LLFastTimer t(FTM_OCCLUSION_BEGIN_QUERY);
|
|
glBeginQueryARB(mode, mOcclusionQuery[LLViewerCamera::sCurCameraID]);
|
|
}
|
|
|
|
LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr;
|
|
llassert(shader);
|
|
|
|
shader->uniform3fv(LLShaderMgr::BOX_CENTER, 1, mBounds[0].getF32ptr());
|
|
//static LLVector4a fudge(SG_OCCLUSION_FUDGE);
|
|
static LLCachedControl<F32> vel("SHOcclusionFudge",SG_OCCLUSION_FUDGE);
|
|
LLVector4a fudge(SG_OCCLUSION_FUDGE);
|
|
static LLVector4a bounds;
|
|
bounds.setAdd(fudge,mBounds[1]);
|
|
shader->uniform3fv(LLShaderMgr::BOX_SIZE, 1, bounds.getF32ptr());
|
|
|
|
if (!use_depth_clamp && mSpatialPartition->mDrawableType == LLDrawPool::POOL_VOIDWATER)
|
|
{
|
|
LLFastTimer t(FTM_OCCLUSION_DRAW_WATER);
|
|
|
|
LLGLSquashToFarClip squash(glh_get_current_projection(), 1);
|
|
if (camera->getOrigin().isExactlyZero())
|
|
{ //origin is invalid, draw entire box
|
|
gPipeline.mCubeVB->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, 0);
|
|
gPipeline.mCubeVB->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, b111*8);
|
|
}
|
|
else
|
|
{
|
|
gPipeline.mCubeVB->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, get_box_fan_indices(camera, mBounds[0]));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LLFastTimer t(FTM_OCCLUSION_DRAW);
|
|
if (camera->getOrigin().isExactlyZero())
|
|
{ //origin is invalid, draw entire box
|
|
gPipeline.mCubeVB->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, 0);
|
|
gPipeline.mCubeVB->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, b111*8);
|
|
}
|
|
else
|
|
{
|
|
gPipeline.mCubeVB->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, get_box_fan_indices(camera, mBounds[0]));
|
|
}
|
|
}
|
|
|
|
|
|
{
|
|
LLFastTimer t(FTM_OCCLUSION_END_QUERY);
|
|
glEndQueryARB(mode);
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
LLFastTimer t(FTM_SET_OCCLUSION_STATE);
|
|
setOcclusionState(LLSpatialGroup::QUERY_PENDING);
|
|
clearOcclusionState(LLSpatialGroup::DISCARD_QUERY);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//==============================================
|
|
|
|
LLSpatialPartition::LLSpatialPartition(U32 data_mask, BOOL render_by_group, U32 buffer_usage)
|
|
: mRenderByGroup(render_by_group), mBridge(NULL)
|
|
{
|
|
mOcclusionEnabled = TRUE;
|
|
mDrawableType = 0;
|
|
mPartitionType = LLViewerRegion::PARTITION_NONE;
|
|
mLODSeed = 0;
|
|
mLODPeriod = 1;
|
|
mVertexDataMask = data_mask;
|
|
mBufferUsage = buffer_usage;
|
|
mDepthMask = FALSE;
|
|
mSlopRatio = 0.25f;
|
|
mInfiniteFarClip = FALSE;
|
|
|
|
LLVector4a center, size;
|
|
center.splat(0.f);
|
|
size.splat(1.f);
|
|
|
|
mOctree = new LLSpatialGroup::OctreeRoot(center,size,
|
|
NULL);
|
|
new LLSpatialGroup(mOctree, this);
|
|
}
|
|
|
|
|
|
LLSpatialPartition::~LLSpatialPartition()
|
|
{
|
|
delete mOctree;
|
|
mOctree = NULL;
|
|
}
|
|
|
|
|
|
LLSpatialGroup *LLSpatialPartition::put(LLDrawable *drawablep, BOOL was_visible)
|
|
{
|
|
drawablep->updateSpatialExtents();
|
|
|
|
//keep drawable from being garbage collected
|
|
LLPointer<LLDrawable> ptr = drawablep;
|
|
|
|
assert_octree_valid(mOctree);
|
|
mOctree->insert(drawablep);
|
|
assert_octree_valid(mOctree);
|
|
|
|
LLSpatialGroup* group = drawablep->getSpatialGroup();
|
|
|
|
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!" << llendl;
|
|
}
|
|
else
|
|
{
|
|
drawablep->setSpatialGroup(NULL);
|
|
}
|
|
|
|
drawablep->setSpatialGroup(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." << llendl;
|
|
return;
|
|
}
|
|
|
|
BOOL was_visible = curp ? curp->isVisible() : FALSE;
|
|
|
|
if (curp && curp->mSpatialPartition != this)
|
|
{
|
|
//keep drawable from being garbage collected
|
|
LLPointer<LLDrawable> ptr = drawablep;
|
|
if (curp->mSpatialPartition->remove(drawablep, curp))
|
|
{
|
|
put(drawablep, was_visible);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
OCT_ERRS << "Drawable lost between spatial partitions on outbound transition." << llendl;
|
|
}
|
|
}
|
|
|
|
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!" << llendl;
|
|
}
|
|
|
|
put(drawablep, was_visible);
|
|
}
|
|
|
|
class LLSpatialShift : public LLSpatialGroup::OctreeTraveler
|
|
{
|
|
public:
|
|
const LLVector4a& mOffset;
|
|
|
|
LLSpatialShift(const LLVector4a& offset) : mOffset(offset) { }
|
|
virtual void visit(const LLSpatialGroup::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 LLSpatialGroup::OctreeTraveler
|
|
{
|
|
public:
|
|
LLOctreeCull(LLCamera* camera)
|
|
: mCamera(camera), mRes(0) { }
|
|
|
|
virtual bool earlyFail(LLSpatialGroup* group)
|
|
{
|
|
group->checkOcclusion();
|
|
|
|
if (group->mOctreeNode->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 void traverse(const LLSpatialGroup::OctreeNode* n)
|
|
{
|
|
LLSpatialGroup* group = (LLSpatialGroup*) n->getListener(0);
|
|
|
|
if (earlyFail(group))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (mRes == 2 ||
|
|
(mRes && group->isState(LLSpatialGroup::SKIP_FRUSTUM_CHECK)))
|
|
{ //fully in, just add everything
|
|
LLSpatialGroup::OctreeTraveler::traverse(n);
|
|
}
|
|
else
|
|
{
|
|
mRes = frustumCheck(group);
|
|
|
|
if (mRes)
|
|
{ //at least partially in, run on down
|
|
LLSpatialGroup::OctreeTraveler::traverse(n);
|
|
}
|
|
|
|
mRes = 0;
|
|
}
|
|
}
|
|
|
|
virtual S32 frustumCheck(const LLSpatialGroup* group)
|
|
{
|
|
S32 res = mCamera->AABBInFrustumNoFarClip(group->mBounds[0], group->mBounds[1]);
|
|
if (res != 0)
|
|
{
|
|
res = llmin(res, AABBSphereIntersect(group->mExtents[0], group->mExtents[1], mCamera->getOrigin(), mCamera->mFrustumCornerDist));
|
|
}
|
|
return res;
|
|
}
|
|
|
|
virtual S32 frustumCheckObjects(const LLSpatialGroup* group)
|
|
{
|
|
S32 res = mCamera->AABBInFrustumNoFarClip(group->mObjectBounds[0], group->mObjectBounds[1]);
|
|
if (res != 0)
|
|
{
|
|
res = llmin(res, AABBSphereIntersect(group->mObjectExtents[0], group->mObjectExtents[1], mCamera->getOrigin(), mCamera->mFrustumCornerDist));
|
|
}
|
|
return res;
|
|
}
|
|
|
|
virtual bool checkObjects(const LLSpatialGroup::OctreeNode* branch, const LLSpatialGroup* group)
|
|
{
|
|
if (branch->getElementCount() == 0) //no elements
|
|
{
|
|
return false;
|
|
}
|
|
else if (branch->getChildCount() == 0) //leaf state, already checked tightest bounding box
|
|
{
|
|
return true;
|
|
}
|
|
else if (mRes == 1 && !frustumCheckObjects(group)) //no objects in frustum
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
virtual void preprocess(LLSpatialGroup* group)
|
|
{
|
|
|
|
}
|
|
|
|
virtual void processGroup(LLSpatialGroup* group)
|
|
{
|
|
if (group->needsUpdate() ||
|
|
group->mVisible[LLViewerCamera::sCurCameraID] < LLDrawable::getCurrentFrame() - 1)
|
|
{
|
|
group->doOcclusion(mCamera);
|
|
}
|
|
gPipeline.markNotCulled(group, *mCamera);
|
|
}
|
|
|
|
virtual void visit(const LLSpatialGroup::OctreeNode* branch)
|
|
{
|
|
LLSpatialGroup* group = (LLSpatialGroup*) branch->getListener(0);
|
|
|
|
preprocess(group);
|
|
|
|
if (checkObjects(branch, group))
|
|
{
|
|
processGroup(group);
|
|
}
|
|
}
|
|
|
|
LLCamera *mCamera;
|
|
S32 mRes;
|
|
};
|
|
|
|
class LLOctreeCullNoFarClip : public LLOctreeCull
|
|
{
|
|
public:
|
|
LLOctreeCullNoFarClip(LLCamera* camera)
|
|
: LLOctreeCull(camera) { }
|
|
|
|
virtual S32 frustumCheck(const LLSpatialGroup* group)
|
|
{
|
|
return mCamera->AABBInFrustumNoFarClip(group->mBounds[0], group->mBounds[1]);
|
|
}
|
|
|
|
virtual S32 frustumCheckObjects(const LLSpatialGroup* group)
|
|
{
|
|
S32 res = mCamera->AABBInFrustumNoFarClip(group->mObjectBounds[0], group->mObjectBounds[1]);
|
|
return res;
|
|
}
|
|
};
|
|
|
|
class LLOctreeCullShadow : public LLOctreeCull
|
|
{
|
|
public:
|
|
LLOctreeCullShadow(LLCamera* camera)
|
|
: LLOctreeCull(camera) { }
|
|
|
|
virtual S32 frustumCheck(const LLSpatialGroup* group)
|
|
{
|
|
return mCamera->AABBInFrustum(group->mBounds[0], group->mBounds[1]);
|
|
}
|
|
|
|
virtual S32 frustumCheckObjects(const LLSpatialGroup* group)
|
|
{
|
|
return mCamera->AABBInFrustum(group->mObjectBounds[0], group->mObjectBounds[1]);
|
|
}
|
|
};
|
|
|
|
class LLOctreeCullVisExtents: public LLOctreeCullShadow
|
|
{
|
|
public:
|
|
LLOctreeCullVisExtents(LLCamera* camera, LLVector4a& min, LLVector4a& max)
|
|
: LLOctreeCullShadow(camera), mMin(min), mMax(max), mEmpty(TRUE) { }
|
|
|
|
virtual bool earlyFail(LLSpatialGroup* group)
|
|
{
|
|
if (group->mOctreeNode->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 LLSpatialGroup::OctreeNode* n)
|
|
{
|
|
LLSpatialGroup* group = (LLSpatialGroup*) n->getListener(0);
|
|
|
|
if (earlyFail(group))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ((mRes && group->isState(LLSpatialGroup::SKIP_FRUSTUM_CHECK)) ||
|
|
mRes == 2)
|
|
{ //don't need to do frustum check
|
|
LLSpatialGroup::OctreeTraveler::traverse(n);
|
|
}
|
|
else
|
|
{
|
|
mRes = frustumCheck(group);
|
|
|
|
if (mRes)
|
|
{ //at least partially in, run on down
|
|
LLSpatialGroup::OctreeTraveler::traverse(n);
|
|
}
|
|
|
|
mRes = 0;
|
|
}
|
|
}
|
|
|
|
virtual void processGroup(LLSpatialGroup* group)
|
|
{
|
|
llassert(!group->isState(LLSpatialGroup::DIRTY) && !group->isEmpty());
|
|
|
|
if (mRes < 2)
|
|
{
|
|
if (mCamera->AABBInFrustum(group->mObjectBounds[0], group->mObjectBounds[1]) > 0)
|
|
{
|
|
mEmpty = FALSE;
|
|
update_min_max(mMin, mMax, group->mObjectExtents[0]);
|
|
update_min_max(mMin, mMax, group->mObjectExtents[1]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mEmpty = FALSE;
|
|
update_min_max(mMin, mMax, group->mExtents[0]);
|
|
update_min_max(mMin, mMax, group->mExtents[1]);
|
|
}
|
|
}
|
|
|
|
BOOL mEmpty;
|
|
LLVector4a& mMin;
|
|
LLVector4a& mMax;
|
|
};
|
|
|
|
class LLOctreeCullDetectVisible: public LLOctreeCullShadow
|
|
{
|
|
public:
|
|
LLOctreeCullDetectVisible(LLCamera* camera)
|
|
: LLOctreeCullShadow(camera), mResult(FALSE) { }
|
|
|
|
virtual bool earlyFail(LLSpatialGroup* group)
|
|
{
|
|
if (mResult || //already found a node, don't check any more
|
|
(group->mOctreeNode->getParent() && //never occlusion cull the root node
|
|
LLPipeline::sUseOcclusion && //ignore occlusion if disabled
|
|
group->isOcclusionState(LLSpatialGroup::OCCLUDED)))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
virtual void processGroup(LLSpatialGroup* group)
|
|
{
|
|
if (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(LLSpatialGroup* group) { return false; }
|
|
virtual void preprocess(LLSpatialGroup* group) { }
|
|
|
|
virtual void processGroup(LLSpatialGroup* group)
|
|
{
|
|
LLSpatialGroup::OctreeNode* branch = group->mOctreeNode;
|
|
|
|
OctreeGuard guard(branch);
|
|
for (LLSpatialGroup::OctreeNode::const_element_iter i = branch->getDataBegin(); i != branch->getDataEnd(); ++i)
|
|
{
|
|
LLDrawable* drawable = *i;
|
|
|
|
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 LLOctreeTraveler<LLDrawable>
|
|
{
|
|
public:
|
|
virtual void visit(const LLOctreeNode<LLDrawable>* state)
|
|
{
|
|
LLSpatialGroup* group = (LLSpatialGroup*) state->getListener(0);
|
|
group->destroyGL();
|
|
|
|
{OctreeGuard guard(group->mOctreeNode);
|
|
for (LLSpatialGroup::element_iter i = group->getDataBegin(); i != group->getDataEnd(); ++i)
|
|
{
|
|
LLDrawable* drawable = *i;
|
|
if (drawable->getVObj().notNull() && !group->mSpatialPartition->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);
|
|
}
|
|
}
|
|
};
|
|
|
|
void LLSpatialPartition::restoreGL()
|
|
{
|
|
}
|
|
|
|
void LLSpatialPartition::resetVertexBuffers()
|
|
{
|
|
LLOctreeDirty dirty;
|
|
dirty.traverse(mOctree);
|
|
}
|
|
|
|
BOOL LLSpatialPartition::isOcclusionEnabled()
|
|
{
|
|
return mOcclusionEnabled || LLPipeline::sUseOcclusion > 2;
|
|
}
|
|
|
|
BOOL LLSpatialPartition::getVisibleExtents(LLCamera& camera, LLVector3& visMin, LLVector3& visMax)
|
|
{
|
|
LLVector4a visMina, visMaxa;
|
|
visMina.load3(visMin.mV);
|
|
visMaxa.load3(visMax.mV);
|
|
{
|
|
LLFastTimer ftm(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, BOOL for_select)
|
|
{
|
|
#if LL_OCTREE_PARANOIA_CHECK
|
|
((LLSpatialGroup*)mOctree->getListener(0))->checkStates();
|
|
#endif
|
|
{
|
|
//BOOL temp = sFreezeState;
|
|
//sFreezeState = FALSE;
|
|
LLFastTimer ftm(FTM_CULL_REBOUND);
|
|
LLSpatialGroup* group = (LLSpatialGroup*) mOctree->getListener(0);
|
|
group->rebound();
|
|
//sFreezeState = temp;
|
|
}
|
|
|
|
#if LL_OCTREE_PARANOIA_CHECK
|
|
((LLSpatialGroup*)mOctree->getListener(0))->validate();
|
|
#endif
|
|
|
|
|
|
if (for_select)
|
|
{
|
|
LLOctreeSelect selecter(&camera, results);
|
|
selecter.traverse(mOctree);
|
|
}
|
|
else if (LLPipeline::sShadowRender)
|
|
{
|
|
LLFastTimer ftm(FTM_FRUSTUM_CULL);
|
|
LLOctreeCullShadow culler(&camera);
|
|
culler.traverse(mOctree);
|
|
}
|
|
else if (mInfiniteFarClip || !LLPipeline::sUseFarClip)
|
|
{
|
|
LLFastTimer ftm(FTM_FRUSTUM_CULL);
|
|
LLOctreeCullNoFarClip culler(&camera);
|
|
culler.traverse(mOctree);
|
|
}
|
|
else
|
|
{
|
|
LLFastTimer ftm(FTM_FRUSTUM_CULL);
|
|
LLOctreeCull culler(&camera);
|
|
culler.traverse(mOctree);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
BOOL earlyFail(LLCamera* camera, LLSpatialGroup* group)
|
|
{
|
|
if (camera->getOrigin().isExactlyZero())
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
static LLCachedControl<F32> vel("SHOcclusionFudge",SG_OCCLUSION_FUDGE);
|
|
LLVector4a fudge(vel*2.f);
|
|
|
|
const LLVector4a& c = group->mBounds[0];
|
|
static LLVector4a r;
|
|
r.setAdd(group->mBounds[1], fudge);
|
|
|
|
/*if (r.magVecSquared() > 1024.0*1024.0)
|
|
{
|
|
return TRUE;
|
|
}*/
|
|
|
|
LLVector4a e;
|
|
e.load3(camera->getOrigin().mV);
|
|
|
|
LLVector4a min;
|
|
min.setSub(c,r);
|
|
LLVector4a max;
|
|
max.setAdd(c,r);
|
|
|
|
S32 lt = e.lessThan(min).getGatheredBits() & 0x7;
|
|
if (lt)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
S32 gt = e.greaterThan(max).getGatheredBits() & 0x7;
|
|
if (gt)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
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.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->mSpatialPartition->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_map_t::iterator i = group->mBufferMap.begin(); i != group->mBufferMap.end(); ++i)
|
|
{
|
|
for (LLSpatialGroup::buffer_texture_map_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
|
|
{
|
|
drawBox(group->mBounds[0], group->mBounds[1]);
|
|
}
|
|
}
|
|
|
|
void pushVertsColorCoded(LLSpatialGroup* group, U32 mask)
|
|
{
|
|
LLDrawInfo* params = NULL;
|
|
|
|
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);
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
|
|
|
gGL.diffuseColor4f(1,0,0,group->mBuilt);
|
|
gGL.flush();
|
|
glLineWidth(5.f);
|
|
drawBoxOutline(group->mObjectBounds[0], group->mObjectBounds[1]);
|
|
gGL.flush();
|
|
glLineWidth(1.f);
|
|
gGL.flush();
|
|
OctreeGuard guard(group->mOctreeNode);
|
|
for (LLSpatialGroup::element_iter i = group->getDataBegin(); i != group->getDataEnd(); ++i)
|
|
{
|
|
LLDrawable* drawable = *i;
|
|
if (!group->mSpatialPartition->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->mSpatialPartition->isBridge())
|
|
{
|
|
gGL.popMatrix();
|
|
}
|
|
}
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
|
gGL.diffuseColor4f(1,1,1,1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (group->mBufferUsage == GL_STATIC_DRAW_ARB && !group->isEmpty()
|
|
&& group->mSpatialPartition->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);
|
|
LLVector4a size = group->mObjectBounds[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);
|
|
drawBoxOutline(group->mBounds[0],group->mBounds[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);
|
|
}
|
|
}
|
|
}*/
|
|
}
|
|
|
|
// LLSpatialGroup::OctreeNode* node = group->mOctreeNode;
|
|
// gGL.color4f(0,1,0,1);
|
|
// drawBoxOutline(LLVector3(node->getCenter()), LLVector3(node->getSize()));
|
|
}
|
|
|
|
void renderVisibility(LLSpatialGroup* group, LLCamera* camera)
|
|
{
|
|
LLGLEnable blend(GL_BLEND);
|
|
gGL.setSceneBlendType(LLRender::BT_ALPHA);
|
|
LLGLEnable cull(GL_CULL_FACE);
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_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);
|
|
}
|
|
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_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]));
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_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]));
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_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 blend(GL_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:
|
|
llwarns << "Unknown update_type " << vobj->getLastUpdateType() << llendl;
|
|
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 blend(GL_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:
|
|
gGL.diffuseColor4f(1,0,1,1);
|
|
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.flush();
|
|
glLineWidth(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.flush();
|
|
glLineWidth(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((F32*) vol->getRelativeXform().mMatrix);
|
|
|
|
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 offset(GL_POLYGON_OFFSET_LINE);
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
|
glPolygonOffset(3.f, 3.f);
|
|
glLineWidth(3.f);
|
|
gGL.diffuseColor4fv(line_color.mV);
|
|
LLVertexBuffer::drawArrays(LLRender::TRIANGLES, mesh.mPositions, mesh.mNormals);
|
|
glLineWidth(1.f);
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_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((F32*) volume->getRelativeXform().mMatrix);
|
|
|
|
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);
|
|
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
|
gGL.diffuseColor4fv(line_color.mV);
|
|
LLVertexBuffer::drawArrays(LLRender::TRIANGLES, decomp->mPhysicsShapeMesh.mPositions, decomp->mPhysicsShapeMesh.mNormals);
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_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
|
|
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
|
|
|
gGL.diffuseColor4fv(line_color.mV);
|
|
LLVertexBuffer::unbind();
|
|
|
|
llassert(!LLGLSLShader::sNoFixedFunction || LLGLSLShader::sCurBoundShader != 0);
|
|
|
|
LLVertexBuffer::drawElements(LLRender::TRIANGLES, phys_volume->mHullPoints, NULL, phys_volume->mNumHullIndices, phys_volume->mHullIndices);
|
|
|
|
gGL.diffuseColor4fv(color.mV);
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
|
LLVertexBuffer::drawElements(LLRender::TRIANGLES, 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);
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
|
|
|
gGL.diffuseColor4fv(line_color.mV);
|
|
pushVerts(phys_volume);
|
|
|
|
gGL.diffuseColor4fv(color.mV);
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_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)
|
|
{
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
|
gGL.diffuseColor4fv(line_color.mV);
|
|
LLVertexBuffer::drawElements(LLRender::TRIANGLES, phys_volume->mHullPoints, NULL, phys_volume->mNumHullIndices, phys_volume->mHullIndices);
|
|
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
|
gGL.diffuseColor4fv(color.mV);
|
|
LLVertexBuffer::drawElements(LLRender::TRIANGLES, 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
|
|
{
|
|
llerrs << "Unhandled type" << llendl;
|
|
}
|
|
|
|
gGL.popMatrix();
|
|
}
|
|
|
|
void renderPhysicsShapes(LLSpatialGroup* group)
|
|
{
|
|
OctreeGuard guard(group->mOctreeNode);
|
|
for (LLSpatialGroup::OctreeNode::const_element_iter i = group->getDataBegin(); i != group->getDataEnd(); ++i)
|
|
{
|
|
LLDrawable* drawable = *i;
|
|
LLVOVolume* volume = drawable->getVOVolume();
|
|
if (volume && !volume->isAttachment() && volume->getPhysicsShapeType() != LLViewerObject::PHYSICS_SHAPE_NONE )
|
|
{
|
|
if (!group->mSpatialPartition->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((F32*) object->getRegion()->mRenderMatrix.mMatrix);
|
|
//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)
|
|
{
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_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);
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_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 blend(GL_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 blend(GL_BLEND);
|
|
gGL.diffuseColor4f(1,1,0,0.5f);
|
|
pushVerts(params, LLVertexBuffer::MAP_VERTEX);
|
|
}
|
|
|
|
void renderBatchSize(LLDrawInfo* params)
|
|
{
|
|
LLGLEnable offset(GL_POLYGON_OFFSET_FILL);
|
|
glPolygonOffset(-1.f, 1.f);
|
|
gGL.diffuseColor4ubv((GLubyte*) &(params->mDebugColor));
|
|
pushVerts(params, LLVertexBuffer::MAP_VERTEX);
|
|
}
|
|
|
|
void renderShadowFrusta(LLDrawInfo* params)
|
|
{
|
|
LLGLEnable blend(GL_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 blend(GL_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));
|
|
}
|
|
}
|
|
|
|
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.flush();
|
|
glLineWidth(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.flush();
|
|
glLineWidth(1.f);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
void renderRaycast(LLDrawable* drawablep)
|
|
{
|
|
if (drawablep->getNumFaces())
|
|
{
|
|
LLGLEnable blend(GL_BLEND);
|
|
gGL.diffuseColor4f(0,1,1,0.5f);
|
|
|
|
if (drawablep->getVOVolume())
|
|
{
|
|
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
|
//pushVerts(drawablep->getFace(gDebugRaycastFaceHit), LLVertexBuffer::MAP_VERTEX);
|
|
//glPolygonMode(GL_FRONT_AND_BACK, GL_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((F32*) vobj->getRelativeXform().mMatrix);
|
|
|
|
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();
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
|
|
|
{
|
|
//render face positions
|
|
gGL.diffuseColor4f(0,1,1,0.5f);
|
|
LLVertexBuffer::drawElements(LLRender::TRIANGLES, 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();
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_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.getF32ptr());
|
|
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());
|
|
|
|
orient.lookDir(normal, binormal);
|
|
LLMatrix4 rotation;
|
|
orient.getRotMatrixToParent(rotation);
|
|
gGL.multMatrix((float*)rotation.mMatrix);
|
|
|
|
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 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 LLOctreeTraveler<LLDrawable>
|
|
{
|
|
public:
|
|
LLCamera* mCamera;
|
|
LLOctreeRenderNonOccluded(LLCamera* camera): mCamera(camera) {}
|
|
|
|
virtual void traverse(const LLSpatialGroup::OctreeNode* node)
|
|
{
|
|
LLSpatialGroup* group = (LLSpatialGroup*) node->getListener(0);
|
|
|
|
if (!mCamera || mCamera->AABBInFrustumNoFarClip(group->mBounds[0], group->mBounds[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.getF32ptr());
|
|
renderVisibility(group, mCamera);
|
|
stop_glerror();
|
|
gGLLastMatrix = NULL;
|
|
gGL.popMatrix();
|
|
gGL.diffuseColor4f(1,1,1,1);
|
|
}
|
|
}
|
|
}
|
|
|
|
virtual void visit(const LLSpatialGroup::OctreeNode* branch)
|
|
{
|
|
LLSpatialGroup* group = (LLSpatialGroup*) branch->getListener(0);
|
|
|
|
if (group->isState(LLSpatialGroup::GEOM_DIRTY) || (mCamera && !mCamera->AABBInFrustumNoFarClip(group->mBounds[0], group->mBounds[1])))
|
|
{
|
|
return;
|
|
}
|
|
|
|
LLGLDisable stencil(GL_STENCIL_TEST);
|
|
|
|
group->rebuildGeom();
|
|
group->rebuildMesh();
|
|
|
|
if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_BBOXES))
|
|
{
|
|
if (!group->isEmpty())
|
|
{
|
|
gGL.diffuseColor3f(0,0,1);
|
|
drawBoxOutline(group->mObjectBounds[0],
|
|
group->mObjectBounds[1]);
|
|
}
|
|
}
|
|
|
|
{OctreeGuard guard(branch);
|
|
for (LLSpatialGroup::OctreeNode::const_element_iter i = branch->getDataBegin(); i != branch->getDataEnd(); ++i)
|
|
{
|
|
LLDrawable* drawable = *i;
|
|
|
|
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))
|
|
{
|
|
avatar->renderJoints();
|
|
}
|
|
|
|
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)
|
|
{
|
|
llerrs << "Face texture index out of bounds." << llendl;
|
|
}
|
|
else if (facep->mDrawInfo->mTextureList[index] != facep->getTexture())
|
|
{
|
|
llerrs << "Face texture index incorrect." << llendl;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}}
|
|
|
|
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 LLOctreeTraveler<LLDrawable>
|
|
{
|
|
public:
|
|
LLCamera* mCamera;
|
|
LLOctreeRenderPhysicsShapes(LLCamera* camera): mCamera(camera) {}
|
|
|
|
virtual void traverse(const LLSpatialGroup::OctreeNode* node)
|
|
{
|
|
LLSpatialGroup* group = (LLSpatialGroup*) node->getListener(0);
|
|
|
|
if (!mCamera || mCamera->AABBInFrustumNoFarClip(group->mBounds[0], group->mBounds[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 LLSpatialGroup::OctreeNode* branch)
|
|
{
|
|
|
|
}
|
|
};
|
|
|
|
class LLOctreePushBBoxVerts : public LLOctreeTraveler<LLDrawable>
|
|
{
|
|
public:
|
|
LLCamera* mCamera;
|
|
LLOctreePushBBoxVerts(LLCamera* camera): mCamera(camera) {}
|
|
|
|
virtual void traverse(const LLSpatialGroup::OctreeNode* node)
|
|
{
|
|
LLSpatialGroup* group = (LLSpatialGroup*) node->getListener(0);
|
|
|
|
if (!mCamera || mCamera->AABBInFrustum(group->mBounds[0], group->mBounds[1]))
|
|
{
|
|
node->accept(this);
|
|
|
|
for (U32 i = 0; i < node->getChildCount(); i++)
|
|
{
|
|
traverse(node->getChild(i));
|
|
}
|
|
}
|
|
}
|
|
|
|
virtual void visit(const LLSpatialGroup::OctreeNode* branch)
|
|
{
|
|
LLSpatialGroup* group = (LLSpatialGroup*) branch->getListener(0);
|
|
|
|
if (group->isState(LLSpatialGroup::GEOM_DIRTY) || (mCamera && !mCamera->AABBInFrustumNoFarClip(group->mBounds[0], group->mBounds[1])))
|
|
{
|
|
return;
|
|
}
|
|
|
|
OctreeGuard guard(branch);
|
|
for (LLSpatialGroup::OctreeNode::const_element_iter i = branch->getDataBegin(); i != branch->getDataEnd(); ++i)
|
|
{
|
|
LLDrawable* drawable = *i;
|
|
|
|
renderBoundingBox(drawable, FALSE);
|
|
}
|
|
}
|
|
};
|
|
|
|
void LLSpatialPartition::renderIntersectingBBoxes(LLCamera* camera)
|
|
{
|
|
LLOctreePushBBoxVerts pusher(camera);
|
|
pusher.traverse(mOctree);
|
|
}
|
|
|
|
class LLOctreeStateCheck : public LLOctreeTraveler<LLDrawable>
|
|
{
|
|
public:
|
|
U32 mInheritedMask[LLViewerCamera::NUM_CAMERAS];
|
|
|
|
LLOctreeStateCheck()
|
|
{
|
|
for (U32 i = 0; i < LLViewerCamera::NUM_CAMERAS; i++)
|
|
{
|
|
mInheritedMask[i] = 0;
|
|
}
|
|
}
|
|
|
|
virtual void traverse(const LLSpatialGroup::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 LLOctreeNode<LLDrawable>* state)
|
|
{
|
|
LLSpatialGroup* group = (LLSpatialGroup*) state->getListener(0);
|
|
|
|
for (U32 i = 0; i < LLViewerCamera::NUM_CAMERAS; i++)
|
|
{
|
|
if (mInheritedMask[i] && !(group->mOcclusionState[i] & mInheritedMask[i]))
|
|
{
|
|
llerrs << "Spatial group failed inherited mask test." << llendl;
|
|
}
|
|
}
|
|
|
|
if (group->isState(LLSpatialGroup::DIRTY))
|
|
{
|
|
assert_parent_state(group, LLSpatialGroup::DIRTY);
|
|
}
|
|
}
|
|
|
|
void assert_parent_state(LLSpatialGroup* group, LLSpatialGroup::eSpatialState state)
|
|
{
|
|
LLSpatialGroup* parent = group->getParent();
|
|
while (parent)
|
|
{
|
|
if (!parent->isState(state))
|
|
{
|
|
llerrs << "Spatial group failed parent state check." << llendl;
|
|
}
|
|
parent = parent->getParent();
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
void LLSpatialPartition::renderPhysicsShapes()
|
|
{
|
|
LLSpatialBridge* bridge = asBridge();
|
|
LLCamera* camera = LLViewerCamera::getInstance();
|
|
|
|
if (bridge)
|
|
{
|
|
camera = NULL;
|
|
}
|
|
|
|
gGL.flush();
|
|
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
|
|
glLineWidth(3.f);
|
|
LLOctreeRenderPhysicsShapes render_physics(camera);
|
|
render_physics.traverse(mOctree);
|
|
gGL.flush();
|
|
glLineWidth(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 cullface(GL_CULL_FACE);
|
|
LLGLEnable blend(GL_BLEND);
|
|
gGL.setSceneBlendType(LLRender::BT_ALPHA);
|
|
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
|
|
gPipeline.disableLights();
|
|
|
|
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 LLSpatialGroup::OctreeTraveler
|
|
{
|
|
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 LLSpatialGroup::OctreeNode* branch)
|
|
{
|
|
OctreeGuard guard(branch);
|
|
for (LLSpatialGroup::OctreeNode::const_element_iter i = branch->getDataBegin(); i != branch->getDataEnd(); ++i)
|
|
{
|
|
check(*i);
|
|
}
|
|
}
|
|
|
|
virtual LLDrawable* check(const LLSpatialGroup::OctreeNode* node)
|
|
{
|
|
node->accept(this);
|
|
|
|
OctreeGuard guard(node);
|
|
for (U32 i = 0; i < node->getChildCount(); i++)
|
|
{
|
|
const LLSpatialGroup::OctreeNode* child = node->getChild(i);
|
|
LLVector3 res;
|
|
|
|
LLSpatialGroup* group = (LLSpatialGroup*) child->getListener(0);
|
|
|
|
LLVector4a size;
|
|
LLVector4a center;
|
|
|
|
size = group->mBounds[1];
|
|
center = group->mBounds[0];
|
|
|
|
LLVector4a local_start = mStart;
|
|
LLVector4a local_end = mEnd;
|
|
|
|
if (group->mSpatialPartition->isBridge())
|
|
{
|
|
LLMatrix4 local_matrix = group->mSpatialPartition->asBridge()->mDrawable->getRenderMatrix();
|
|
local_matrix.invert();
|
|
|
|
LLMatrix4a local_matrix4a;
|
|
local_matrix4a.loadu(local_matrix);
|
|
|
|
local_matrix4a.affineTransform(mStart, local_start);
|
|
local_matrix4a.affineTransform(mEnd, local_end);
|
|
}
|
|
|
|
if (LLLineSegmentBoxIntersect(local_start, local_end, center, size))
|
|
{
|
|
check(child);
|
|
}
|
|
}
|
|
|
|
return mHit;
|
|
}
|
|
|
|
virtual bool check(LLDrawable* drawable)
|
|
{
|
|
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::instanceExists())
|
|
{
|
|
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),
|
|
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)
|
|
{
|
|
llerrs << "LLDrawInfo deleted illegally!" << llendl;
|
|
}*/
|
|
|
|
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))
|
|
{
|
|
llerrs << "Stale LLDrawInfo's in LLCullResult!" << llendl;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|