1508 lines
38 KiB
C++
1508 lines
38 KiB
C++
/**
|
|
* @file llvieweroctree.cpp
|
|
* @brief LLViewerOctreeGroup class implementation and supporting functions
|
|
*
|
|
* $LicenseInfo:firstyear=2003&license=viewerlgpl$
|
|
* Second Life Viewer Source Code
|
|
* Copyright (C) 2010, Linden Research, Inc.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation;
|
|
* version 2.1 of the License only.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
|
* $/LicenseInfo$
|
|
*/
|
|
|
|
#include "llviewerprecompiledheaders.h"
|
|
#include "llvieweroctree.h"
|
|
#include "llviewerregion.h"
|
|
#include "pipeline.h"
|
|
#include "llviewercontrol.h"
|
|
#include "llappviewer.h"
|
|
#include "llglslshader.h"
|
|
#include "llviewershadermgr.h"
|
|
#include "lltexturecache.h"
|
|
#include "llimageworker.h"
|
|
#include "lltexturefetch.h"
|
|
|
|
//-----------------------------------------------------------------------------------
|
|
//static variables definitions
|
|
//-----------------------------------------------------------------------------------
|
|
U32 LLViewerOctreeEntryData::sCurVisible = 10; //reserve the low numbers for special use.
|
|
|
|
//-----------------------------------------------------------------------------------
|
|
//some global functions definitions
|
|
//-----------------------------------------------------------------------------------
|
|
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;
|
|
}
|
|
|
|
|
|
#define LL_TRACK_PENDING_OCCLUSION_QUERIES 0
|
|
|
|
const F32 SG_OCCLUSION_FUDGE = 0.25f;
|
|
#define SG_DISCARD_TOLERANCE 0.01f
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------
|
|
//class LLViewerOctreeEntry definitions
|
|
//-----------------------------------------------------------------------------------
|
|
LLViewerOctreeEntry::LLViewerOctreeEntry() :
|
|
mGroup(NULL),
|
|
mBinRadius(0.f),
|
|
mBinIndex(-1),
|
|
mVisible(0)
|
|
{
|
|
mPositionGroup.clear();
|
|
mExtents[0].clear();
|
|
mExtents[1].clear();
|
|
|
|
for(S32 i = 0; i < NUM_DATA_TYPE; i++)
|
|
{
|
|
mData[i] = NULL;
|
|
}
|
|
}
|
|
|
|
LLViewerOctreeEntry::~LLViewerOctreeEntry()
|
|
{
|
|
llassert(!mGroup);
|
|
}
|
|
|
|
void LLViewerOctreeEntry::addData(LLViewerOctreeEntryData* data)
|
|
{
|
|
//llassert(mData[data->getDataType()] == NULL);
|
|
llassert(data != NULL);
|
|
|
|
mData[data->getDataType()] = data;
|
|
}
|
|
|
|
void LLViewerOctreeEntry::removeData(LLViewerOctreeEntryData* data)
|
|
{
|
|
//llassert(data->getDataType() != LLVOCACHEENTRY); //can not remove VOCache entry
|
|
|
|
if(!mData[data->getDataType()])
|
|
{
|
|
return;
|
|
}
|
|
if(mData[data->getDataType()] != data)
|
|
{
|
|
return;
|
|
}
|
|
|
|
mData[data->getDataType()] = NULL;
|
|
|
|
if(mGroup != NULL && !mData[LLDRAWABLE])
|
|
{
|
|
LLViewerOctreeGroup* group = mGroup;
|
|
mGroup = NULL;
|
|
group->removeFromGroup(data);
|
|
|
|
llassert(mBinIndex == -1);
|
|
}
|
|
}
|
|
|
|
//called by group handleDestruction() ONLY when group is destroyed by octree.
|
|
void LLViewerOctreeEntry::nullGroup()
|
|
{
|
|
mGroup = NULL;
|
|
}
|
|
|
|
void LLViewerOctreeEntry::setGroup(LLViewerOctreeGroup* group)
|
|
{
|
|
if(mGroup == group)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(mGroup)
|
|
{
|
|
LLViewerOctreeGroup* old_group = mGroup;
|
|
mGroup = NULL;
|
|
old_group->removeFromGroup(this);
|
|
|
|
llassert(mBinIndex == -1);
|
|
}
|
|
|
|
mGroup = group;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------
|
|
//class LLViewerOctreeEntryData definitions
|
|
//-----------------------------------------------------------------------------------
|
|
LLViewerOctreeEntryData::~LLViewerOctreeEntryData()
|
|
{
|
|
if(mEntry)
|
|
{
|
|
mEntry->removeData(this);
|
|
}
|
|
}
|
|
|
|
LLViewerOctreeEntryData::LLViewerOctreeEntryData(LLViewerOctreeEntry::eEntryDataType_t data_type)
|
|
: mDataType(data_type),
|
|
mEntry(NULL)
|
|
{
|
|
}
|
|
|
|
//virtual
|
|
void LLViewerOctreeEntryData::setOctreeEntry(LLViewerOctreeEntry* entry)
|
|
{
|
|
llassert_always(mEntry.isNull());
|
|
|
|
if(mEntry.notNull())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(!entry)
|
|
{
|
|
mEntry = new LLViewerOctreeEntry();
|
|
}
|
|
else
|
|
{
|
|
mEntry = entry;
|
|
}
|
|
mEntry->addData(this);
|
|
}
|
|
|
|
void LLViewerOctreeEntryData::removeOctreeEntry()
|
|
{
|
|
if(mEntry)
|
|
{
|
|
mEntry->removeData(this);
|
|
mEntry = NULL;
|
|
}
|
|
}
|
|
|
|
void LLViewerOctreeEntryData::setSpatialExtents(const LLVector3& min, const LLVector3& max)
|
|
{
|
|
mEntry->mExtents[0].load3(min.mV);
|
|
mEntry->mExtents[1].load3(max.mV);
|
|
}
|
|
|
|
void LLViewerOctreeEntryData::setSpatialExtents(const LLVector4a& min, const LLVector4a& max)
|
|
{
|
|
mEntry->mExtents[0] = min;
|
|
mEntry->mExtents[1] = max;
|
|
}
|
|
|
|
void LLViewerOctreeEntryData::setPositionGroup(const LLVector4a& pos)
|
|
{
|
|
mEntry->mPositionGroup = pos;
|
|
}
|
|
|
|
const LLVector4a* LLViewerOctreeEntryData::getSpatialExtents() const
|
|
{
|
|
return mEntry->getSpatialExtents();
|
|
}
|
|
//virtual
|
|
void LLViewerOctreeEntryData::setGroup(LLViewerOctreeGroup* group)
|
|
{
|
|
mEntry->setGroup(group);
|
|
}
|
|
|
|
void LLViewerOctreeEntryData::shift(const LLVector4a &shift_vector)
|
|
{
|
|
mEntry->mExtents[0].add(shift_vector);
|
|
mEntry->mExtents[1].add(shift_vector);
|
|
mEntry->mPositionGroup.add(shift_vector);
|
|
}
|
|
|
|
LLViewerOctreeGroup* LLViewerOctreeEntryData::getGroup()const
|
|
{
|
|
return mEntry.notNull() ? mEntry->mGroup : NULL;
|
|
}
|
|
|
|
const LLVector4a& LLViewerOctreeEntryData::getPositionGroup() const
|
|
{
|
|
return mEntry->getPositionGroup();
|
|
}
|
|
|
|
//virtual
|
|
bool LLViewerOctreeEntryData::isVisible() const
|
|
{
|
|
if(mEntry)
|
|
{
|
|
return mEntry->mVisible == sCurVisible;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//virtual
|
|
bool LLViewerOctreeEntryData::isRecentlyVisible() const
|
|
{
|
|
if(!mEntry)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(isVisible())
|
|
{
|
|
return true;
|
|
}
|
|
if(getGroup() && getGroup()->isRecentlyVisible())
|
|
{
|
|
setVisible();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void LLViewerOctreeEntryData::setVisible() const
|
|
{
|
|
if(mEntry)
|
|
{
|
|
mEntry->mVisible = sCurVisible;
|
|
}
|
|
}
|
|
|
|
void LLViewerOctreeEntryData::resetVisible() const
|
|
{
|
|
if(mEntry)
|
|
{
|
|
mEntry->mVisible = 0;
|
|
}
|
|
}
|
|
//-----------------------------------------------------------------------------------
|
|
//class LLViewerOctreeGroup definitions
|
|
//-----------------------------------------------------------------------------------
|
|
|
|
LLViewerOctreeGroup::~LLViewerOctreeGroup()
|
|
{
|
|
//empty here
|
|
}
|
|
|
|
LLViewerOctreeGroup::LLViewerOctreeGroup(OctreeNode* node) :
|
|
mOctreeNode(node),
|
|
mAnyVisible(0),
|
|
mState(CLEAN)
|
|
{
|
|
LLVector4a tmp;
|
|
tmp.splat(0.f);
|
|
mExtents[0] = mExtents[1] = mObjectBounds[0] = mObjectBounds[0] = mObjectBounds[1] =
|
|
mObjectExtents[0] = mObjectExtents[1] = tmp;
|
|
|
|
mBounds[0] = node->getCenter();
|
|
mBounds[1] = node->getSize();
|
|
|
|
mOctreeNode->addListener(this);
|
|
}
|
|
|
|
bool LLViewerOctreeGroup::hasElement(LLViewerOctreeEntryData* data)
|
|
{
|
|
if(!data->getEntry())
|
|
{
|
|
return false;
|
|
}
|
|
return std::find(getDataBegin(), getDataEnd(), data->getEntry()) != getDataEnd();
|
|
}
|
|
bool LLViewerOctreeGroup::removeFromGroup(LLViewerOctreeEntryData* data)
|
|
{
|
|
return removeFromGroup(data->getEntry());
|
|
}
|
|
|
|
bool LLViewerOctreeGroup::removeFromGroup(LLViewerOctreeEntry* entry)
|
|
{
|
|
llassert(entry != NULL);
|
|
llassert(!entry->getGroup());
|
|
|
|
if(isDead()) //group is about to be destroyed, not need to double delete the entry.
|
|
{
|
|
entry->setBinIndex(-1);
|
|
return true;
|
|
}
|
|
|
|
unbound();
|
|
setState(OBJECT_DIRTY);
|
|
|
|
if (mOctreeNode)
|
|
{
|
|
if (!mOctreeNode->remove(entry)) //this could cause *this* pointer to be destroyed, so no more function calls after this.
|
|
{
|
|
OCT_ERRS << "Could not remove LLVOCacheEntry from LLVOCacheOctreeGroup" << LL_ENDL;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//virtual
|
|
void LLViewerOctreeGroup::unbound()
|
|
{
|
|
if (isDirty())
|
|
{
|
|
return;
|
|
}
|
|
|
|
setState(DIRTY);
|
|
|
|
//all the parent nodes need to rebound this child
|
|
if (mOctreeNode)
|
|
{
|
|
OctreeNode* parent = (OctreeNode*) mOctreeNode->getParent();
|
|
while (parent != NULL)
|
|
{
|
|
LLViewerOctreeGroup* group = (LLViewerOctreeGroup*) parent->getListener(0);
|
|
if (!group || group->isDirty())
|
|
{
|
|
return;
|
|
}
|
|
|
|
group->setState(DIRTY);
|
|
parent = (OctreeNode*) parent->getParent();
|
|
}
|
|
}
|
|
}
|
|
|
|
//virtual
|
|
void LLViewerOctreeGroup::rebound()
|
|
{
|
|
if (!isDirty())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (mOctreeNode->getChildCount() == 1 && mOctreeNode->getElementCount() == 0)
|
|
{
|
|
LLViewerOctreeGroup* group = (LLViewerOctreeGroup*) 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];
|
|
LLViewerOctreeGroup* group = (LLViewerOctreeGroup*) 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 = (LLViewerOctreeGroup*) 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;
|
|
}
|
|
|
|
//virtual
|
|
void LLViewerOctreeGroup::handleInsertion(const TreeNode* node, LLViewerOctreeEntry* obj)
|
|
{
|
|
obj->setGroup(this);
|
|
unbound();
|
|
setState(OBJECT_DIRTY);
|
|
}
|
|
|
|
//virtual
|
|
void LLViewerOctreeGroup::handleRemoval(const TreeNode* node, LLViewerOctreeEntry* obj)
|
|
{
|
|
unbound();
|
|
setState(OBJECT_DIRTY);
|
|
|
|
obj->setGroup(NULL); //this could cause *this* pointer to be destroyed. So no more function calls after this.
|
|
}
|
|
|
|
//virtual
|
|
void LLViewerOctreeGroup::handleDestruction(const TreeNode* node)
|
|
{
|
|
for (OctreeNode::element_iter i = mOctreeNode->getDataBegin(); i != mOctreeNode->getDataEnd(); ++i)
|
|
{
|
|
LLViewerOctreeEntry* obj = *i;
|
|
if (obj && obj->getGroup() == this)
|
|
{
|
|
obj->nullGroup();
|
|
//obj->setGroup(NULL);
|
|
}
|
|
}
|
|
mOctreeNode = NULL;
|
|
}
|
|
|
|
//virtual
|
|
void LLViewerOctreeGroup::handleStateChange(const TreeNode* node)
|
|
{
|
|
//drop bounding box upon state change
|
|
if (mOctreeNode != node)
|
|
{
|
|
mOctreeNode = (OctreeNode*) node;
|
|
}
|
|
unbound();
|
|
}
|
|
|
|
//virtual
|
|
void LLViewerOctreeGroup::handleChildAddition(const OctreeNode* parent, OctreeNode* child)
|
|
{
|
|
if (child->getListenerCount() == 0)
|
|
{
|
|
new LLViewerOctreeGroup(child);
|
|
}
|
|
else
|
|
{
|
|
OCT_ERRS << "LLViewerOctreeGroup redundancy detected." << LL_ENDL;
|
|
}
|
|
|
|
unbound();
|
|
|
|
((LLViewerOctreeGroup*)child->getListener(0))->unbound();
|
|
}
|
|
|
|
//virtual
|
|
void LLViewerOctreeGroup::handleChildRemoval(const OctreeNode* parent, const OctreeNode* child)
|
|
{
|
|
unbound();
|
|
}
|
|
|
|
LLViewerOctreeGroup* LLViewerOctreeGroup::getParent()
|
|
{
|
|
if (isDead())
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if(!mOctreeNode)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
OctreeNode* parent = mOctreeNode->getOctParent();
|
|
|
|
if (parent)
|
|
{
|
|
return (LLViewerOctreeGroup*) parent->getListener(0);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
//virtual
|
|
bool LLViewerOctreeGroup::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." << LL_ENDL;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
LLVector4a& newMin = mObjectExtents[0];
|
|
LLVector4a& newMax = mObjectExtents[1];
|
|
|
|
if (hasState(OBJECT_DIRTY))
|
|
{ //calculate new bounding box
|
|
clearState(OBJECT_DIRTY);
|
|
|
|
//initialize bounding box to first element
|
|
OctreeNode::const_element_iter i = node->getDataBegin();
|
|
LLViewerOctreeEntry* entry = *i;
|
|
const LLVector4a* minMax = entry->getSpatialExtents();
|
|
|
|
newMin = minMax[0];
|
|
newMax = minMax[1];
|
|
|
|
for (++i; i != node->getDataEnd(); ++i)
|
|
{
|
|
entry = *i;
|
|
minMax = entry->getSpatialExtents();
|
|
|
|
update_min_max(newMin, newMax, minMax[0]);
|
|
update_min_max(newMin, newMax, minMax[1]);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
//virtual
|
|
BOOL LLViewerOctreeGroup::isVisible() const
|
|
{
|
|
return mVisible[LLViewerCamera::sCurCameraID] >= LLViewerOctreeEntryData::getCurrentFrame() ? TRUE : FALSE;
|
|
}
|
|
|
|
//virtual
|
|
BOOL LLViewerOctreeGroup::isRecentlyVisible() const
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
void LLViewerOctreeGroup::setVisible()
|
|
{
|
|
mVisible[LLViewerCamera::sCurCameraID] = LLViewerOctreeEntryData::getCurrentFrame();
|
|
|
|
if(LLViewerCamera::sCurCameraID < LLViewerCamera::CAMERA_WATER0)
|
|
{
|
|
mAnyVisible = LLViewerOctreeEntryData::getCurrentFrame();
|
|
}
|
|
}
|
|
|
|
void LLViewerOctreeGroup::checkStates()
|
|
{
|
|
#if LL_OCTREE_PARANOIA_CHECK
|
|
LLOctreeStateCheck checker;
|
|
checker.traverse(mOctreeNode);
|
|
#endif
|
|
}
|
|
|
|
//-------------------------------------------------------------------------------------------
|
|
//occulsion culling functions and classes
|
|
//-------------------------------------------------------------------------------------------
|
|
std::set<U32> LLOcclusionCullingGroup::sPendingQueries;
|
|
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
|
|
LLOcclusionCullingGroup::sPendingQueries.erase(name);
|
|
#endif
|
|
glDeleteQueriesARB(1, &name);
|
|
}
|
|
};
|
|
|
|
static LLOcclusionQueryPool sQueryPool;
|
|
U32 LLOcclusionCullingGroup::getNewOcclusionQueryObjectName()
|
|
{
|
|
return sQueryPool.allocate();
|
|
}
|
|
|
|
void LLOcclusionCullingGroup::releaseOcclusionQueryObjectName(GLuint name)
|
|
{
|
|
sQueryPool.release(name);
|
|
}
|
|
|
|
//=====================================
|
|
// Occlusion State Set/Clear
|
|
//=====================================
|
|
class LLSpatialSetOcclusionState : public OctreeTraveler
|
|
{
|
|
public:
|
|
LLOcclusionCullingGroup::eOcclusionState mState;
|
|
LLSpatialSetOcclusionState(LLOcclusionCullingGroup::eOcclusionState state) : mState(state) { }
|
|
virtual void visit(const OctreeNode* branch)
|
|
{
|
|
LLOcclusionCullingGroup* group = (LLOcclusionCullingGroup*)branch->getListener(0);
|
|
if(group)
|
|
{
|
|
group->setOcclusionState(mState);
|
|
}
|
|
}
|
|
};
|
|
|
|
class LLSpatialSetOcclusionStateDiff : public LLSpatialSetOcclusionState
|
|
{
|
|
public:
|
|
LLSpatialSetOcclusionStateDiff(LLOcclusionCullingGroup::eOcclusionState state) : LLSpatialSetOcclusionState(state) { }
|
|
|
|
virtual void traverse(const OctreeNode* n)
|
|
{
|
|
LLOcclusionCullingGroup* group = (LLOcclusionCullingGroup*) n->getListener(0);
|
|
|
|
if (group && !group->isOcclusionState(mState))
|
|
{
|
|
OctreeTraveler::traverse(n);
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
LLOcclusionCullingGroup::LLOcclusionCullingGroup(OctreeNode* node, LLViewerOctreePartition* part) :
|
|
LLViewerOctreeGroup(node),
|
|
mSpatialPartition(part)
|
|
{
|
|
part->mLODSeed = (part->mLODSeed+1)%part->mLODPeriod;
|
|
mLODHash = part->mLODSeed;
|
|
|
|
OctreeNode* oct_parent = node->getOctParent();
|
|
LLOcclusionCullingGroup* parent = oct_parent ? (LLOcclusionCullingGroup*) 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;
|
|
}
|
|
}
|
|
|
|
LLOcclusionCullingGroup::~LLOcclusionCullingGroup()
|
|
{
|
|
releaseOcclusionQueryObjectNames();
|
|
}
|
|
|
|
BOOL LLOcclusionCullingGroup::needsUpdate()
|
|
{
|
|
return (LLDrawable::getCurrentFrame() % mSpatialPartition->mLODPeriod == mLODHash) ? TRUE : FALSE;
|
|
}
|
|
|
|
BOOL LLOcclusionCullingGroup::isRecentlyVisible() const
|
|
{
|
|
const S32 MIN_VIS_FRAME_RANGE = 2;
|
|
return (LLDrawable::getCurrentFrame() - mVisible[LLViewerCamera::sCurCameraID]) < MIN_VIS_FRAME_RANGE ;
|
|
}
|
|
|
|
BOOL LLOcclusionCullingGroup::isAnyRecentlyVisible() const
|
|
{
|
|
const S32 MIN_VIS_FRAME_RANGE = 2;
|
|
return (LLDrawable::getCurrentFrame() - mAnyVisible) < MIN_VIS_FRAME_RANGE ;
|
|
}
|
|
|
|
//virtual
|
|
void LLOcclusionCullingGroup::handleChildAddition(const OctreeNode* parent, OctreeNode* child)
|
|
{
|
|
if (child->getListenerCount() == 0)
|
|
{
|
|
new LLOcclusionCullingGroup(child, mSpatialPartition);
|
|
}
|
|
else
|
|
{
|
|
OCT_ERRS << "LLOcclusionCullingGroup redundancy detected." << LL_ENDL;
|
|
}
|
|
|
|
unbound();
|
|
|
|
((LLViewerOctreeGroup*)child->getListener(0))->unbound();
|
|
}
|
|
|
|
void LLOcclusionCullingGroup::releaseOcclusionQueryObjectNames()
|
|
{
|
|
if (gGLManager.mHasOcclusionQuery)
|
|
{
|
|
for (U32 i = 0; i < LLViewerCamera::NUM_CAMERAS; ++i)
|
|
{
|
|
if (mOcclusionQuery[i])
|
|
{
|
|
releaseOcclusionQueryObjectName(mOcclusionQuery[i]);
|
|
mOcclusionQuery[i] = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void LLOcclusionCullingGroup::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])
|
|
{
|
|
releaseOcclusionQueryObjectName(mOcclusionQuery[i]);
|
|
mOcclusionQuery[i] = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mOcclusionState[LLViewerCamera::sCurCameraID] |= state;
|
|
if ((state & DISCARD_QUERY) && mOcclusionQuery[LLViewerCamera::sCurCameraID])
|
|
{
|
|
releaseOcclusionQueryObjectName(mOcclusionQuery[LLViewerCamera::sCurCameraID]);
|
|
mOcclusionQuery[LLViewerCamera::sCurCameraID] = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
class LLSpatialClearOcclusionState : public OctreeTraveler
|
|
{
|
|
public:
|
|
LLOcclusionCullingGroup::eOcclusionState mState;
|
|
|
|
LLSpatialClearOcclusionState(LLOcclusionCullingGroup::eOcclusionState state) : mState(state) { }
|
|
virtual void visit(const OctreeNode* branch)
|
|
{
|
|
LLOcclusionCullingGroup* group = (LLOcclusionCullingGroup*) branch->getListener(0);
|
|
if(group)
|
|
{
|
|
group->clearOcclusionState(mState);
|
|
}
|
|
}
|
|
};
|
|
|
|
class LLSpatialClearOcclusionStateDiff : public LLSpatialClearOcclusionState
|
|
{
|
|
public:
|
|
LLSpatialClearOcclusionStateDiff(LLOcclusionCullingGroup::eOcclusionState state) : LLSpatialClearOcclusionState(state) { }
|
|
|
|
virtual void traverse(const OctreeNode* n)
|
|
{
|
|
LLOcclusionCullingGroup* group = (LLOcclusionCullingGroup*) n->getListener(0);
|
|
|
|
if (group && group->isOcclusionState(mState))
|
|
{
|
|
OctreeTraveler::traverse(n);
|
|
}
|
|
}
|
|
};
|
|
|
|
void LLOcclusionCullingGroup::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;
|
|
}
|
|
}
|
|
|
|
static LLTrace::BlockTimerStatHandle FTM_OCCLUSION_READBACK("Readback Occlusion");
|
|
static LLTrace::BlockTimerStatHandle FTM_OCCLUSION_WAIT("Occlusion Wait");
|
|
|
|
BOOL LLOcclusionCullingGroup::earlyFail(LLCamera* camera, const LLVector4a* bounds)
|
|
{
|
|
if (camera->getOrigin().isExactlyZero())
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
static LLCachedControl<F32> vel("SHOcclusionFudge",SG_OCCLUSION_FUDGE);
|
|
LLVector4a fudge(vel*2.f);
|
|
|
|
const LLVector4a& c = bounds[0];
|
|
static LLVector4a r;
|
|
r.setAdd(bounds[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;
|
|
}
|
|
|
|
U32 LLOcclusionCullingGroup::getLastOcclusionIssuedTime()
|
|
{
|
|
return mOcclusionIssued[LLViewerCamera::sCurCameraID];
|
|
}
|
|
|
|
void LLOcclusionCullingGroup::checkOcclusion()
|
|
{
|
|
if (LLPipeline::sUseOcclusion > 1)
|
|
{
|
|
LL_RECORD_BLOCK_TIME(FTM_OCCLUSION_READBACK);
|
|
LLOcclusionCullingGroup* parent = (LLOcclusionCullingGroup*)getParent();
|
|
if (parent && parent->isOcclusionState(LLOcclusionCullingGroup::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;
|
|
LL_RECORD_BLOCK_TIME(FTM_OCCLUSION_WAIT);
|
|
while (!available && max_loop-- > 0)
|
|
{
|
|
F32 max_time = llmin(gFrameIntervalSeconds.value()*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
|
|
releaseOcclusionQueryObjectName(mOcclusionQuery[LLViewerCamera::sCurCameraID]);
|
|
mOcclusionQuery[LLViewerCamera::sCurCameraID] = 0;
|
|
}
|
|
|
|
if (isOcclusionState(DISCARD_QUERY))
|
|
{
|
|
res = 2;
|
|
}
|
|
|
|
if (res > 0)
|
|
{
|
|
assert_states_valid(this);
|
|
clearOcclusionState(LLOcclusionCullingGroup::OCCLUDED, LLOcclusionCullingGroup::STATE_MODE_DIFF);
|
|
assert_states_valid(this);
|
|
}
|
|
else
|
|
{
|
|
assert_states_valid(this);
|
|
|
|
setOcclusionState(LLOcclusionCullingGroup::OCCLUDED, LLOcclusionCullingGroup::STATE_MODE_DIFF);
|
|
|
|
assert_states_valid(this);
|
|
}
|
|
|
|
clearOcclusionState(QUERY_PENDING | DISCARD_QUERY);
|
|
}
|
|
}
|
|
else if (mSpatialPartition->isOcclusionEnabled() && isOcclusionState(LLOcclusionCullingGroup::OCCLUDED))
|
|
{ //check occlusion has been issued for occluded node that has not had a query issued
|
|
assert_states_valid(this);
|
|
clearOcclusionState(LLOcclusionCullingGroup::OCCLUDED, LLOcclusionCullingGroup::STATE_MODE_DIFF);
|
|
assert_states_valid(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
static LLTrace::BlockTimerStatHandle FTM_PUSH_OCCLUSION_VERTS("Push Occlusion");
|
|
static LLTrace::BlockTimerStatHandle FTM_SET_OCCLUSION_STATE("Occlusion State");
|
|
static LLTrace::BlockTimerStatHandle FTM_OCCLUSION_EARLY_FAIL("Occlusion Early Fail");
|
|
static LLTrace::BlockTimerStatHandle FTM_OCCLUSION_ALLOCATE("Allocate");
|
|
static LLTrace::BlockTimerStatHandle FTM_OCCLUSION_BUILD("Build");
|
|
static LLTrace::BlockTimerStatHandle FTM_OCCLUSION_BEGIN_QUERY("Begin Query");
|
|
static LLTrace::BlockTimerStatHandle FTM_OCCLUSION_END_QUERY("End Query");
|
|
static LLTrace::BlockTimerStatHandle FTM_OCCLUSION_SET_BUFFER("Set Buffer");
|
|
static LLTrace::BlockTimerStatHandle FTM_OCCLUSION_DRAW_WATER("Draw Water");
|
|
static LLTrace::BlockTimerStatHandle FTM_OCCLUSION_DRAW("Draw");
|
|
|
|
void LLOcclusionCullingGroup::doOcclusion(LLCamera* camera, const LLVector4a* shift)
|
|
{
|
|
LLGLDisable<GL_STENCIL_TEST> stencil;
|
|
if (mSpatialPartition->isOcclusionEnabled() && LLPipeline::sUseOcclusion > 1)
|
|
{
|
|
//move mBounds to the agent space if necessary
|
|
LLVector4a bounds[2];
|
|
bounds[0] = mBounds[0];
|
|
bounds[1] = mBounds[1];
|
|
if(shift != NULL)
|
|
{
|
|
bounds[0].add(*shift);
|
|
}
|
|
|
|
// Don't cull hole/edge water, unless we have the GL_ARB_depth_clamp extension
|
|
if (earlyFail(camera, bounds))
|
|
{
|
|
LL_RECORD_BLOCK_TIME(FTM_OCCLUSION_EARLY_FAIL);
|
|
setOcclusionState(LLOcclusionCullingGroup::DISCARD_QUERY);
|
|
assert_states_valid(this);
|
|
clearOcclusionState(LLOcclusionCullingGroup::OCCLUDED, LLOcclusionCullingGroup::STATE_MODE_DIFF);
|
|
assert_states_valid(this);
|
|
}
|
|
else
|
|
{
|
|
if (!isOcclusionState(QUERY_PENDING) || isOcclusionState(DISCARD_QUERY))
|
|
{
|
|
{ //no query pending, or previous query to be discarded
|
|
LL_RECORD_BLOCK_TIME(FTM_RENDER_OCCLUSION);
|
|
|
|
if (!mOcclusionQuery[LLViewerCamera::sCurCameraID])
|
|
{
|
|
LL_RECORD_BLOCK_TIME(FTM_OCCLUSION_ALLOCATE);
|
|
mOcclusionQuery[LLViewerCamera::sCurCameraID] = getNewOcclusionQueryObjectName();
|
|
}
|
|
|
|
// 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<GL_DEPTH_CLAMP> clamp(use_depth_clamp);
|
|
|
|
#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
|
|
|
|
{
|
|
LL_RECORD_BLOCK_TIME(FTM_PUSH_OCCLUSION_VERTS);
|
|
|
|
//store which frame this query was issued on
|
|
mOcclusionIssued[LLViewerCamera::sCurCameraID] = gFrameCount;
|
|
|
|
{
|
|
LL_RECORD_BLOCK_TIME(FTM_OCCLUSION_BEGIN_QUERY);
|
|
glBeginQueryARB(mode, mOcclusionQuery[LLViewerCamera::sCurCameraID]);
|
|
}
|
|
|
|
LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr;
|
|
llassert(shader);
|
|
|
|
shader->uniform3fv(LLShaderMgr::BOX_CENTER, 1, bounds[0].getF32ptr());
|
|
//static LLVector4a fudge(SG_OCCLUSION_FUDGE);
|
|
static LLCachedControl<F32> vel("SHOcclusionFudge",SG_OCCLUSION_FUDGE);
|
|
LLVector4a fudge(SG_OCCLUSION_FUDGE);
|
|
static LLVector4a fudged_bounds;
|
|
fudged_bounds.setAdd(fudge, bounds[1]);
|
|
shader->uniform3fv(LLShaderMgr::BOX_SIZE, 1, fudged_bounds.getF32ptr());
|
|
|
|
if (!use_depth_clamp && mSpatialPartition->mDrawableType == LLDrawPool::POOL_VOIDWATER)
|
|
{
|
|
LL_RECORD_BLOCK_TIME(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, bounds[0]));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LL_RECORD_BLOCK_TIME(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, bounds[0]));
|
|
}
|
|
}
|
|
|
|
|
|
{
|
|
LL_RECORD_BLOCK_TIME(FTM_OCCLUSION_END_QUERY);
|
|
glEndQueryARB(mode);
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
LL_RECORD_BLOCK_TIME(FTM_SET_OCCLUSION_STATE);
|
|
setOcclusionState(LLOcclusionCullingGroup::QUERY_PENDING);
|
|
clearOcclusionState(LLOcclusionCullingGroup::DISCARD_QUERY);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//-------------------------------------------------------------------------------------------
|
|
//end of occulsion culling functions and classes
|
|
//-------------------------------------------------------------------------------------------
|
|
|
|
//-----------------------------------------------------------------------------------
|
|
//class LLViewerOctreePartition definitions
|
|
//-----------------------------------------------------------------------------------
|
|
LLViewerOctreePartition::LLViewerOctreePartition() :
|
|
mRegionp(NULL),
|
|
mOcclusionEnabled(TRUE),
|
|
mDrawableType(0),
|
|
mLODSeed(0),
|
|
mLODPeriod(1)
|
|
{
|
|
LLVector4a center, size;
|
|
center.splat(0.f);
|
|
size.splat(1.f);
|
|
|
|
mOctree = new OctreeRoot(center,size, NULL);
|
|
}
|
|
|
|
LLViewerOctreePartition::~LLViewerOctreePartition()
|
|
{
|
|
delete mOctree;
|
|
mOctree = NULL;
|
|
}
|
|
|
|
BOOL LLViewerOctreePartition::isOcclusionEnabled()
|
|
{
|
|
return mOcclusionEnabled || LLPipeline::sUseOcclusion > 2;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------
|
|
//class LLViewerOctreeCull definitions
|
|
//-----------------------------------------------------------------------------------
|
|
|
|
//virtual
|
|
bool LLViewerOctreeCull::earlyFail(LLViewerOctreeGroup* group)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//virtual
|
|
void LLViewerOctreeCull::traverse(const OctreeNode* n)
|
|
{
|
|
LLViewerOctreeGroup* group = (LLViewerOctreeGroup*) n->getListener(0);
|
|
|
|
if (earlyFail(group))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (mRes == 2 ||
|
|
(mRes && group->hasState(LLViewerOctreeGroup::SKIP_FRUSTUM_CHECK)))
|
|
{ //fully in, just add everything
|
|
OctreeTraveler::traverse(n);
|
|
}
|
|
else
|
|
{
|
|
mRes = frustumCheck(group);
|
|
|
|
if (mRes)
|
|
{ //at least partially in, run on down
|
|
OctreeTraveler::traverse(n);
|
|
}
|
|
|
|
mRes = 0;
|
|
}
|
|
}
|
|
|
|
//------------------------------------------
|
|
//agent space group culling
|
|
S32 LLViewerOctreeCull::AABBInFrustumNoFarClipGroupBounds(const LLViewerOctreeGroup* group)
|
|
{
|
|
return mCamera->AABBInFrustumNoFarClip(group->mBounds[0], group->mBounds[1]);
|
|
}
|
|
|
|
S32 LLViewerOctreeCull::AABBSphereIntersectGroupExtents(const LLViewerOctreeGroup* group)
|
|
{
|
|
return AABBSphereIntersect(group->mExtents[0], group->mExtents[1], mCamera->getOrigin(), mCamera->mFrustumCornerDist);
|
|
}
|
|
|
|
S32 LLViewerOctreeCull::AABBInFrustumGroupBounds(const LLViewerOctreeGroup* group)
|
|
{
|
|
return mCamera->AABBInFrustum(group->mBounds[0], group->mBounds[1]);
|
|
}
|
|
//------------------------------------------
|
|
|
|
//------------------------------------------
|
|
//agent space object set culling
|
|
S32 LLViewerOctreeCull::AABBInFrustumNoFarClipObjectBounds(const LLViewerOctreeGroup* group)
|
|
{
|
|
return mCamera->AABBInFrustumNoFarClip(group->mObjectBounds[0], group->mObjectBounds[1]);
|
|
}
|
|
|
|
S32 LLViewerOctreeCull::AABBSphereIntersectObjectExtents(const LLViewerOctreeGroup* group)
|
|
{
|
|
return AABBSphereIntersect(group->mObjectExtents[0], group->mObjectExtents[1], mCamera->getOrigin(), mCamera->mFrustumCornerDist);
|
|
}
|
|
|
|
S32 LLViewerOctreeCull::AABBInFrustumObjectBounds(const LLViewerOctreeGroup* group)
|
|
{
|
|
return mCamera->AABBInFrustum(group->mObjectBounds[0], group->mObjectBounds[1]);
|
|
}
|
|
//------------------------------------------
|
|
|
|
//------------------------------------------
|
|
//local regional space group culling
|
|
S32 LLViewerOctreeCull::AABBInRegionFrustumNoFarClipGroupBounds(const LLViewerOctreeGroup* group)
|
|
{
|
|
return mCamera->AABBInRegionFrustumNoFarClip(group->mBounds[0], group->mBounds[1]);
|
|
}
|
|
|
|
S32 LLViewerOctreeCull::AABBInRegionFrustumGroupBounds(const LLViewerOctreeGroup* group)
|
|
{
|
|
return mCamera->AABBInRegionFrustum(group->mBounds[0], group->mBounds[1]);
|
|
}
|
|
|
|
S32 LLViewerOctreeCull::AABBRegionSphereIntersectGroupExtents(const LLViewerOctreeGroup* group, const LLVector3& shift)
|
|
{
|
|
return AABBSphereIntersect(group->mExtents[0], group->mExtents[1], mCamera->getOrigin() - shift, mCamera->mFrustumCornerDist);
|
|
}
|
|
//------------------------------------------
|
|
|
|
//------------------------------------------
|
|
//local regional space object culling
|
|
S32 LLViewerOctreeCull::AABBInRegionFrustumObjectBounds(const LLViewerOctreeGroup* group)
|
|
{
|
|
return mCamera->AABBInRegionFrustum(group->mObjectBounds[0], group->mObjectBounds[1]);
|
|
}
|
|
|
|
S32 LLViewerOctreeCull::AABBInRegionFrustumNoFarClipObjectBounds(const LLViewerOctreeGroup* group)
|
|
{
|
|
return mCamera->AABBInRegionFrustumNoFarClip(group->mObjectBounds[0], group->mObjectBounds[1]);
|
|
}
|
|
|
|
S32 LLViewerOctreeCull::AABBRegionSphereIntersectObjectExtents(const LLViewerOctreeGroup* group, const LLVector3& shift)
|
|
{
|
|
return AABBSphereIntersect(group->mObjectExtents[0], group->mObjectExtents[1], mCamera->getOrigin() - shift, mCamera->mFrustumCornerDist);
|
|
}
|
|
//------------------------------------------
|
|
//check if the objects projection large enough
|
|
|
|
bool LLViewerOctreeCull::checkProjectionArea(const LLVector4a& center, const LLVector4a& size, const LLVector3& shift, F32 pixel_threshold, F32 near_radius)
|
|
{
|
|
LLVector3 local_orig = mCamera->getOrigin() - shift;
|
|
LLVector4a origin;
|
|
origin.load3(local_orig.mV);
|
|
|
|
LLVector4a lookAt;
|
|
lookAt.setSub(center, origin);
|
|
F32 distance = lookAt.getLength3().getF32();
|
|
if(distance <= near_radius)
|
|
{
|
|
return true; //always load close-by objects
|
|
}
|
|
|
|
// treat object as if it were near_radius meters closer than it actually was.
|
|
// this allows us to get some temporal coherence on visibility...objects that can be reached quickly will tend to be visible
|
|
distance -= near_radius;
|
|
|
|
F32 squared_rad = size.dot3(size).getF32();
|
|
return squared_rad / distance > pixel_threshold;
|
|
}
|
|
|
|
//virtual
|
|
bool LLViewerOctreeCull::checkObjects(const OctreeNode* branch, const LLViewerOctreeGroup* 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 LLViewerOctreeCull::preprocess(LLViewerOctreeGroup* group)
|
|
{
|
|
}
|
|
|
|
//virtual
|
|
void LLViewerOctreeCull::processGroup(LLViewerOctreeGroup* group)
|
|
{
|
|
}
|
|
|
|
//virtual
|
|
void LLViewerOctreeCull::visit(const OctreeNode* branch)
|
|
{
|
|
LLViewerOctreeGroup* group = (LLViewerOctreeGroup*) branch->getListener(0);
|
|
|
|
preprocess(group);
|
|
|
|
if (checkObjects(branch, group))
|
|
{
|
|
processGroup(group);
|
|
}
|
|
}
|
|
|