PageRenderTime 1530ms CodeModel.GetById 161ms app.highlight 1123ms RepoModel.GetById 154ms app.codeStats 2ms

/indra/newview/llspatialpartition.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 2720 lines | 2118 code | 432 blank | 170 comment | 317 complexity | 905d06b0391037b1a5379e498573db63 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1/** 
   2 * @file llspatialpartition.cpp
   3 * @brief LLSpatialGroup class implementation and supporting functions
   4 *
   5 * $LicenseInfo:firstyear=2003&license=viewerlgpl$
   6 * Second Life Viewer Source Code
   7 * Copyright (C) 2010, Linden Research, Inc.
   8 * 
   9 * This library is free software; you can redistribute it and/or
  10 * modify it under the terms of the GNU Lesser General Public
  11 * License as published by the Free Software Foundation;
  12 * version 2.1 of the License only.
  13 * 
  14 * This library is distributed in the hope that it will be useful,
  15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  17 * Lesser General Public License for more details.
  18 * 
  19 * You should have received a copy of the GNU Lesser General Public
  20 * License along with this library; if not, write to the Free Software
  21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  22 * 
  23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
  24 * $/LicenseInfo$
  25 */
  26
  27#include "llviewerprecompiledheaders.h"
  28
  29#include "llspatialpartition.h"
  30
  31#include "llappviewer.h"
  32#include "lltexturecache.h"
  33#include "lltexturefetch.h"
  34#include "llimageworker.h"
  35#include "llviewerwindow.h"
  36#include "llviewerobjectlist.h"
  37#include "llvovolume.h"
  38#include "llvolume.h"
  39#include "llvolumeoctree.h"
  40#include "llviewercamera.h"
  41#include "llface.h"
  42#include "llfloatertools.h"
  43#include "llviewercontrol.h"
  44#include "llviewerregion.h"
  45#include "llcamera.h"
  46#include "pipeline.h"
  47#include "llmeshrepository.h"
  48#include "llrender.h"
  49#include "lloctree.h"
  50#include "llphysicsshapebuilderutil.h"
  51#include "llvoavatar.h"
  52#include "llvolumemgr.h"
  53#include "lltextureatlas.h"
  54#include "llglslshader.h"
  55#include "llviewershadermgr.h"
  56
  57static LLFastTimer::DeclareTimer FTM_FRUSTUM_CULL("Frustum Culling");
  58static LLFastTimer::DeclareTimer FTM_CULL_REBOUND("Cull Rebound");
  59
  60const F32 SG_OCCLUSION_FUDGE = 0.25f;
  61#define SG_DISCARD_TOLERANCE 0.01f
  62
  63#if LL_OCTREE_PARANOIA_CHECK
  64#define assert_octree_valid(x) x->validate()
  65#define assert_states_valid(x) ((LLSpatialGroup*) x->mSpatialPartition->mOctree->getListener(0))->checkStates()
  66#else
  67#define assert_octree_valid(x)
  68#define assert_states_valid(x)
  69#endif
  70
  71
  72static U32 sZombieGroups = 0;
  73U32 LLSpatialGroup::sNodeCount = 0;
  74
  75#define LL_TRACK_PENDING_OCCLUSION_QUERIES 0
  76
  77std::set<GLuint> LLSpatialGroup::sPendingQueries;
  78
  79U32 gOctreeMaxCapacity;
  80
  81BOOL LLSpatialGroup::sNoDelete = FALSE;
  82
  83static F32 sLastMaxTexPriority = 1.f;
  84static F32 sCurMaxTexPriority = 1.f;
  85
  86class LLOcclusionQueryPool : public LLGLNamePool
  87{
  88protected:
  89	virtual GLuint allocateName()
  90	{
  91		GLuint name;
  92		glGenQueriesARB(1, &name);
  93		return name;
  94	}
  95
  96	virtual void releaseName(GLuint name)
  97	{
  98#if LL_TRACK_PENDING_OCCLUSION_QUERIES
  99		LLSpatialGroup::sPendingQueries.erase(name);
 100#endif
 101		glDeleteQueriesARB(1, &name);
 102	}
 103};
 104
 105static LLOcclusionQueryPool sQueryPool;
 106
 107//static counter for frame to switch LOD on
 108
 109void sg_assert(BOOL expr)
 110{
 111#if LL_OCTREE_PARANOIA_CHECK
 112	if (!expr)
 113	{
 114		llerrs << "Octree invalid!" << llendl;
 115	}
 116#endif
 117}
 118
 119S32 AABBSphereIntersect(const LLVector3& min, const LLVector3& max, const LLVector3 &origin, const F32 &rad)
 120{
 121	return AABBSphereIntersectR2(min, max, origin, rad*rad);
 122}
 123
 124S32 AABBSphereIntersectR2(const LLVector3& min, const LLVector3& max, const LLVector3 &origin, const F32 &r)
 125{
 126	F32 d = 0.f;
 127	F32 t;
 128	
 129	if ((min-origin).magVecSquared() < r &&
 130		(max-origin).magVecSquared() < r)
 131	{
 132		return 2;
 133	}
 134
 135	for (U32 i = 0; i < 3; i++)
 136	{
 137		if (origin.mV[i] < min.mV[i])
 138		{
 139			t = min.mV[i] - origin.mV[i];
 140			d += t*t;
 141		}
 142		else if (origin.mV[i] > max.mV[i])
 143		{
 144			t = origin.mV[i] - max.mV[i];
 145			d += t*t;
 146		}
 147
 148		if (d > r)
 149		{
 150			return 0;
 151		}
 152	}
 153
 154	return 1;
 155}
 156
 157
 158S32 AABBSphereIntersect(const LLVector4a& min, const LLVector4a& max, const LLVector3 &origin, const F32 &rad)
 159{
 160	return AABBSphereIntersectR2(min, max, origin, rad*rad);
 161}
 162
 163S32 AABBSphereIntersectR2(const LLVector4a& min, const LLVector4a& max, const LLVector3 &origin, const F32 &r)
 164{
 165	F32 d = 0.f;
 166	F32 t;
 167	
 168	LLVector4a origina;
 169	origina.load3(origin.mV);
 170
 171	LLVector4a v;
 172	v.setSub(min, origina);
 173	
 174	if (v.dot3(v) < r)
 175	{
 176		v.setSub(max, origina);
 177		if (v.dot3(v) < r)
 178		{
 179			return 2;
 180		}
 181	}
 182
 183
 184	for (U32 i = 0; i < 3; i++)
 185	{
 186		if (origin.mV[i] < min[i])
 187		{
 188			t = min[i] - origin.mV[i];
 189			d += t*t;
 190		}
 191		else if (origin.mV[i] > max[i])
 192		{
 193			t = origin.mV[i] - max[i];
 194			d += t*t;
 195		}
 196
 197		if (d > r)
 198		{
 199			return 0;
 200		}
 201	}
 202
 203	return 1;
 204}
 205
 206
 207typedef enum
 208{
 209	b000 = 0x00,
 210	b001 = 0x01,
 211	b010 = 0x02,
 212	b011 = 0x03,
 213	b100 = 0x04,
 214	b101 = 0x05,
 215	b110 = 0x06,
 216	b111 = 0x07,
 217} eLoveTheBits;
 218
 219//contact Runitai Linden for a copy of the SL object used to write this table
 220//basically, you give the table a bitmask of the look-at vector to a node and it
 221//gives you a triangle fan index array
 222static U16 sOcclusionIndices[] =
 223{
 224	 //000
 225		b111, b110, b010, b011, b001, b101, b100, b110,
 226	 //001 
 227		b011, b010, b000, b001, b101, b111, b110, b010,
 228	 //010
 229		b101, b100, b110, b111, b011, b001, b000, b100,
 230	 //011 
 231		b001, b000, b100, b101, b111, b011, b010, b000,
 232	 //100 
 233		b110, b000, b010, b011, b111, b101, b100, b000,
 234	 //101 
 235		b010, b100, b000, b001, b011, b111, b110, b100,
 236	 //110
 237		b100, b010, b110, b111, b101, b001, b000, b010,
 238	 //111
 239		b000, b110, b100, b101, b001, b011, b010, b110,
 240};
 241
 242U32 get_box_fan_indices(LLCamera* camera, const LLVector4a& center)
 243{
 244	LLVector4a origin;
 245	origin.load3(camera->getOrigin().mV);
 246
 247	S32 cypher = center.greaterThan(origin).getGatheredBits() & 0x7;
 248	
 249	return cypher*8;
 250}
 251
 252U8* get_box_fan_indices_ptr(LLCamera* camera, const LLVector4a& center)
 253{
 254	LLVector4a origin;
 255	origin.load3(camera->getOrigin().mV);
 256
 257	S32 cypher = center.greaterThan(origin).getGatheredBits() & 0x7;
 258	
 259	return (U8*) (sOcclusionIndices+cypher*8);
 260}
 261
 262
 263static LLFastTimer::DeclareTimer FTM_BUILD_OCCLUSION("Build Occlusion");
 264
 265void LLSpatialGroup::buildOcclusion()
 266{
 267	if (mOcclusionVerts.isNull())
 268	{
 269		mOcclusionVerts = new LLVertexBuffer(LLVertexBuffer::MAP_VERTEX, 
 270			LLVertexBuffer::sUseStreamDraw ? mBufferUsage : 0); //if GL has a hard time with VBOs, don't use them for occlusion culling.
 271		mOcclusionVerts->allocateBuffer(8, 64, true);
 272	
 273		LLStrider<U16> idx;
 274		mOcclusionVerts->getIndexStrider(idx);
 275		for (U32 i = 0; i < 64; i++)
 276		{
 277			*idx++ = sOcclusionIndices[i];
 278		}
 279	}
 280
 281	LLVector4a fudge;
 282	fudge.splat(SG_OCCLUSION_FUDGE);
 283
 284	LLVector4a r;
 285	r.setAdd(mBounds[1], fudge);
 286
 287	LLStrider<LLVector3> pos;
 288	
 289	{
 290		LLFastTimer t(FTM_BUILD_OCCLUSION);
 291		mOcclusionVerts->getVertexStrider(pos);
 292	}
 293
 294	{
 295		LLVector4a* v = (LLVector4a*) pos.get();
 296
 297		const LLVector4a& c = mBounds[0];
 298		const LLVector4a& s = r;
 299		
 300		static const LLVector4a octant[] =
 301		{
 302			LLVector4a(-1.f, -1.f, -1.f),
 303			LLVector4a(-1.f, -1.f, 1.f),
 304			LLVector4a(-1.f, 1.f, -1.f),
 305			LLVector4a(-1.f, 1.f, 1.f),
 306
 307			LLVector4a(1.f, -1.f, -1.f),
 308			LLVector4a(1.f, -1.f, 1.f),
 309			LLVector4a(1.f, 1.f, -1.f),
 310			LLVector4a(1.f, 1.f, 1.f),
 311		};
 312
 313		//vertex positions are encoded so the 3 bits of their vertex index 
 314		//correspond to their axis facing, with bit position 3,2,1 matching
 315		//axis facing x,y,z, bit set meaning positive facing, bit clear 
 316		//meaning negative facing
 317		
 318		for (S32 i = 0; i < 8; ++i)
 319		{
 320			LLVector4a p;
 321			p.setMul(s, octant[i]);
 322			p.add(c);
 323			v[i] = p;
 324		}
 325	}
 326	
 327	{
 328		mOcclusionVerts->flush();
 329		LLVertexBuffer::unbind();
 330	}
 331
 332	clearState(LLSpatialGroup::OCCLUSION_DIRTY);
 333}
 334
 335
 336BOOL earlyFail(LLCamera* camera, LLSpatialGroup* group);
 337
 338//returns:
 339//	0 if sphere and AABB are not intersecting 
 340//	1 if they are
 341//	2 if AABB is entirely inside sphere
 342
 343S32 LLSphereAABB(const LLVector3& center, const LLVector3& size, const LLVector3& pos, const F32 &rad)
 344{
 345	S32 ret = 2;
 346
 347	LLVector3 min = center - size;
 348	LLVector3 max = center + size;
 349	for (U32 i = 0; i < 3; i++)
 350	{
 351		if (min.mV[i] > pos.mV[i] + rad ||
 352			max.mV[i] < pos.mV[i] - rad)
 353		{	//totally outside
 354			return 0;
 355		}
 356		
 357		if (min.mV[i] < pos.mV[i] - rad ||
 358			max.mV[i] > pos.mV[i] + rad)
 359		{	//intersecting
 360			ret = 1;
 361		}
 362	}
 363
 364	return ret;
 365}
 366
 367LLSpatialGroup::~LLSpatialGroup()
 368{
 369	/*if (sNoDelete)
 370	{
 371		llerrs << "Illegal deletion of LLSpatialGroup!" << llendl;
 372	}*/
 373
 374	if (gDebugGL)
 375	{
 376		gPipeline.checkReferences(this);
 377	}
 378
 379	if (isState(DEAD))
 380	{
 381		sZombieGroups--;
 382	}
 383	
 384	sNodeCount--;
 385
 386	if (gGLManager.mHasOcclusionQuery)
 387	{
 388		for (U32 i = 0; i < LLViewerCamera::NUM_CAMERAS; ++i)
 389		{
 390			if (mOcclusionQuery[i])
 391			{
 392				sQueryPool.release(mOcclusionQuery[i]);
 393			}
 394		}
 395	}
 396
 397	mOcclusionVerts = NULL;
 398
 399	LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
 400	clearDrawMap();
 401	clearAtlasList() ;
 402}
 403
 404BOOL LLSpatialGroup::hasAtlas(LLTextureAtlas* atlasp)
 405{
 406	S8 type = atlasp->getComponents() - 1 ;
 407	for(std::list<LLTextureAtlas*>::iterator iter = mAtlasList[type].begin(); iter != mAtlasList[type].end() ; ++iter)
 408	{
 409		if(atlasp == *iter)
 410		{
 411			return TRUE ;
 412		}
 413	}
 414	return FALSE ;
 415}
 416
 417void LLSpatialGroup::addAtlas(LLTextureAtlas* atlasp, S8 recursive_level) 
 418{		
 419	if(!hasAtlas(atlasp))
 420	{
 421		mAtlasList[atlasp->getComponents() - 1].push_back(atlasp) ;
 422		atlasp->addSpatialGroup(this) ;
 423	}
 424	
 425	--recursive_level;
 426	if(recursive_level)//levels propagating up.
 427	{
 428		LLSpatialGroup* parent = getParent() ;
 429		if(parent)
 430		{
 431			parent->addAtlas(atlasp, recursive_level) ;
 432		}
 433	}	
 434}
 435
 436void LLSpatialGroup::removeAtlas(LLTextureAtlas* atlasp, BOOL remove_group, S8 recursive_level) 
 437{
 438	mAtlasList[atlasp->getComponents() - 1].remove(atlasp) ;
 439	if(remove_group)
 440	{
 441		atlasp->removeSpatialGroup(this) ;
 442	}
 443
 444	--recursive_level;
 445	if(recursive_level)//levels propagating up.
 446	{
 447		LLSpatialGroup* parent = getParent() ;
 448		if(parent)
 449		{
 450			parent->removeAtlas(atlasp, recursive_level) ;
 451		}
 452	}	
 453}
 454
 455void LLSpatialGroup::clearAtlasList() 
 456{
 457	std::list<LLTextureAtlas*>::iterator iter ;
 458	for(S8 i = 0 ; i < 4 ; i++)
 459	{
 460		if(mAtlasList[i].size() > 0)
 461		{
 462			for(iter = mAtlasList[i].begin(); iter != mAtlasList[i].end() ; ++iter)
 463			{
 464				((LLTextureAtlas*)*iter)->removeSpatialGroup(this) ;			
 465			}
 466			mAtlasList[i].clear() ;
 467		}
 468	}
 469}
 470
 471LLTextureAtlas* LLSpatialGroup::getAtlas(S8 ncomponents, S8 to_be_reserved, S8 recursive_level)
 472{
 473	S8 type = ncomponents - 1 ;
 474	if(mAtlasList[type].size() > 0)
 475	{
 476		for(std::list<LLTextureAtlas*>::iterator iter = mAtlasList[type].begin(); iter != mAtlasList[type].end() ; ++iter)
 477		{
 478			if(!((LLTextureAtlas*)*iter)->isFull(to_be_reserved))
 479			{
 480				return *iter ;
 481			}
 482		}
 483	}
 484
 485	--recursive_level;
 486	if(recursive_level)
 487	{
 488		LLSpatialGroup* parent = getParent() ;
 489		if(parent)
 490		{
 491			return parent->getAtlas(ncomponents, to_be_reserved, recursive_level) ;
 492		}
 493	}
 494	return NULL ;
 495}
 496
 497void LLSpatialGroup::setCurUpdatingSlot(LLTextureAtlasSlot* slotp) 
 498{ 
 499	mCurUpdatingSlotp = slotp;
 500
 501	//if(!hasAtlas(mCurUpdatingSlotp->getAtlas()))
 502	//{
 503	//	addAtlas(mCurUpdatingSlotp->getAtlas()) ;
 504	//}
 505}
 506
 507LLTextureAtlasSlot* LLSpatialGroup::getCurUpdatingSlot(LLViewerTexture* imagep, S8 recursive_level) 
 508{ 
 509	if(gFrameCount && mCurUpdatingTime == gFrameCount && mCurUpdatingTexture == imagep)
 510	{
 511		return mCurUpdatingSlotp ;
 512	}
 513
 514	//--recursive_level ;
 515	//if(recursive_level)
 516	//{
 517	//	LLSpatialGroup* parent = getParent() ;
 518	//	if(parent)
 519	//	{
 520	//		return parent->getCurUpdatingSlot(imagep, recursive_level) ;
 521	//	}
 522	//}
 523	return NULL ;
 524}
 525
 526void LLSpatialGroup::clearDrawMap()
 527{
 528	mDrawMap.clear();
 529}
 530
 531BOOL LLSpatialGroup::isHUDGroup() 
 532{
 533	return mSpatialPartition && mSpatialPartition->isHUDPartition() ; 
 534}
 535
 536BOOL LLSpatialGroup::isRecentlyVisible() const
 537{
 538	return (LLDrawable::getCurrentFrame() - mVisible[LLViewerCamera::sCurCameraID]) < LLDrawable::getMinVisFrameRange() ;
 539}
 540
 541BOOL LLSpatialGroup::isVisible() const
 542{
 543	return mVisible[LLViewerCamera::sCurCameraID] >= LLDrawable::getCurrentFrame() ? TRUE : FALSE;
 544}
 545
 546void LLSpatialGroup::setVisible()
 547{
 548	mVisible[LLViewerCamera::sCurCameraID] = LLDrawable::getCurrentFrame();
 549}
 550
 551void LLSpatialGroup::validate()
 552{
 553#if LL_OCTREE_PARANOIA_CHECK
 554
 555	sg_assert(!isState(DIRTY));
 556	sg_assert(!isDead());
 557
 558	LLVector4a myMin;
 559	myMin.setSub(mBounds[0], mBounds[1]);
 560	LLVector4a myMax;
 561	myMax.setAdd(mBounds[0], mBounds[1]);
 562
 563	validateDrawMap();
 564
 565	for (element_iter i = getData().begin(); i != getData().end(); ++i)
 566	{
 567		LLDrawable* drawable = *i;
 568		sg_assert(drawable->getSpatialGroup() == this);
 569		if (drawable->getSpatialBridge())
 570		{
 571			sg_assert(drawable->getSpatialBridge() == mSpatialPartition->asBridge());
 572		}
 573
 574		/*if (drawable->isSpatialBridge())
 575		{
 576			LLSpatialPartition* part = drawable->asPartition();
 577			if (!part)
 578			{
 579				llerrs << "Drawable reports it is a spatial bridge but not a partition." << llendl;
 580			}
 581			LLSpatialGroup* group = (LLSpatialGroup*) part->mOctree->getListener(0);
 582			group->validate();
 583		}*/
 584	}
 585
 586	for (U32 i = 0; i < mOctreeNode->getChildCount(); ++i)
 587	{
 588		LLSpatialGroup* group = (LLSpatialGroup*) mOctreeNode->getChild(i)->getListener(0);
 589
 590		group->validate();
 591		
 592		//ensure all children are enclosed in this node
 593		LLVector4a center = group->mBounds[0];
 594		LLVector4a size = group->mBounds[1];
 595		
 596		LLVector4a min;
 597		min.setSub(center, size);
 598		LLVector4a max;
 599		max.setAdd(center, size);
 600		
 601		for (U32 j = 0; j < 3; j++)
 602		{
 603			sg_assert(min[j] >= myMin[j]-0.02f);
 604			sg_assert(max[j] <= myMax[j]+0.02f);
 605		}
 606	}
 607
 608#endif
 609}
 610
 611void LLSpatialGroup::checkStates()
 612{
 613#if LL_OCTREE_PARANOIA_CHECK
 614	//LLOctreeStateCheck checker;
 615	//checker.traverse(mOctreeNode);
 616#endif
 617}
 618
 619void LLSpatialGroup::validateDrawMap()
 620{
 621#if LL_OCTREE_PARANOIA_CHECK
 622	for (draw_map_t::iterator i = mDrawMap.begin(); i != mDrawMap.end(); ++i)
 623	{
 624		LLSpatialGroup::drawmap_elem_t& draw_vec = i->second;
 625		for (drawmap_elem_t::iterator j = draw_vec.begin(); j != draw_vec.end(); ++j)
 626		{
 627			LLDrawInfo& params = **j;
 628		
 629			params.validate();
 630		}
 631	}
 632#endif
 633}
 634
 635BOOL LLSpatialGroup::updateInGroup(LLDrawable *drawablep, BOOL immediate)
 636{
 637	LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
 638		
 639	drawablep->updateSpatialExtents();
 640
 641	OctreeNode* parent = mOctreeNode->getOctParent();
 642	
 643	if (mOctreeNode->isInside(drawablep->getPositionGroup()) && 
 644		(mOctreeNode->contains(drawablep) ||
 645		 (drawablep->getBinRadius() > mOctreeNode->getSize()[0] &&
 646				parent && parent->getElementCount() >= gOctreeMaxCapacity)))
 647	{
 648		unbound();
 649		setState(OBJECT_DIRTY);
 650		//setState(GEOM_DIRTY);
 651		return TRUE;
 652	}
 653		
 654	return FALSE;
 655}
 656
 657
 658BOOL LLSpatialGroup::addObject(LLDrawable *drawablep, BOOL add_all, BOOL from_octree)
 659{
 660	LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
 661	if (!from_octree)
 662	{
 663		mOctreeNode->insert(drawablep);
 664	}
 665	else
 666	{
 667		drawablep->setSpatialGroup(this);
 668		setState(OBJECT_DIRTY | GEOM_DIRTY);
 669		setOcclusionState(LLSpatialGroup::DISCARD_QUERY, LLSpatialGroup::STATE_MODE_ALL_CAMERAS);
 670		gPipeline.markRebuild(this, TRUE);
 671		if (drawablep->isSpatialBridge())
 672		{
 673			mBridgeList.push_back((LLSpatialBridge*) drawablep);
 674		}
 675		if (drawablep->getRadius() > 1.f)
 676		{
 677			setState(IMAGE_DIRTY);
 678		}
 679	}
 680
 681	return TRUE;
 682}
 683
 684void LLSpatialGroup::rebuildGeom()
 685{
 686	LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
 687	if (!isDead())
 688	{
 689		mSpatialPartition->rebuildGeom(this);
 690	}
 691}
 692
 693void LLSpatialGroup::rebuildMesh()
 694{
 695	if (!isDead())
 696	{
 697		mSpatialPartition->rebuildMesh(this);
 698	}
 699}
 700
 701static LLFastTimer::DeclareTimer FTM_REBUILD_VBO("VBO Rebuilt");
 702
 703void LLSpatialPartition::rebuildGeom(LLSpatialGroup* group)
 704{
 705	if (group->isDead() || !group->isState(LLSpatialGroup::GEOM_DIRTY))
 706	{
 707		return;
 708	}
 709
 710	if (group->changeLOD())
 711	{
 712		group->mLastUpdateDistance = group->mDistance;
 713		group->mLastUpdateViewAngle = group->mViewAngle;
 714	}
 715	
 716	LLFastTimer ftm(FTM_REBUILD_VBO);	
 717
 718	group->clearDrawMap();
 719	
 720	//get geometry count
 721	U32 index_count = 0;
 722	U32 vertex_count = 0;
 723	
 724	addGeometryCount(group, vertex_count, index_count);
 725
 726	if (vertex_count > 0 && index_count > 0)
 727	{ //create vertex buffer containing volume geometry for this node
 728		group->mBuilt = 1.f;
 729		if (group->mVertexBuffer.isNull() || (group->mBufferUsage != group->mVertexBuffer->getUsage() && LLVertexBuffer::sEnableVBOs))
 730		{
 731			group->mVertexBuffer = createVertexBuffer(mVertexDataMask, group->mBufferUsage);
 732			group->mVertexBuffer->allocateBuffer(vertex_count, index_count, true);
 733			stop_glerror();
 734		}
 735		else
 736		{
 737			group->mVertexBuffer->resizeBuffer(vertex_count, index_count);
 738			stop_glerror();
 739		}
 740		
 741		getGeometry(group);
 742	}
 743	else
 744	{
 745		group->mVertexBuffer = NULL;
 746		group->mBufferMap.clear();
 747	}
 748
 749	group->mLastUpdateTime = gFrameTimeSeconds;
 750	group->clearState(LLSpatialGroup::GEOM_DIRTY);
 751}
 752
 753
 754void LLSpatialPartition::rebuildMesh(LLSpatialGroup* group)
 755{
 756
 757}
 758
 759BOOL LLSpatialGroup::boundObjects(BOOL empty, LLVector4a& minOut, LLVector4a& maxOut)
 760{	
 761	const OctreeNode* node = mOctreeNode;
 762
 763	if (node->getData().empty())
 764	{	//don't do anything if there are no objects
 765		if (empty && mOctreeNode->getParent())
 766		{	//only root is allowed to be empty
 767			OCT_ERRS << "Empty leaf found in octree." << llendl;
 768		}
 769		return FALSE;
 770	}
 771
 772	LLVector4a& newMin = mObjectExtents[0];
 773	LLVector4a& newMax = mObjectExtents[1];
 774	
 775	if (isState(OBJECT_DIRTY))
 776	{ //calculate new bounding box
 777		clearState(OBJECT_DIRTY);
 778
 779		//initialize bounding box to first element
 780		OctreeNode::const_element_iter i = node->getData().begin();
 781		LLDrawable* drawablep = *i;
 782		const LLVector4a* minMax = drawablep->getSpatialExtents();
 783
 784		newMin = minMax[0];
 785		newMax = minMax[1];
 786
 787		for (++i; i != node->getData().end(); ++i)
 788		{
 789			drawablep = *i;
 790			minMax = drawablep->getSpatialExtents();
 791			
 792			update_min_max(newMin, newMax, minMax[0]);
 793			update_min_max(newMin, newMax, minMax[1]);
 794
 795			//bin up the object
 796			/*for (U32 i = 0; i < 3; i++)
 797			{
 798				if (minMax[0].mV[i] < newMin.mV[i])
 799				{
 800					newMin.mV[i] = minMax[0].mV[i];
 801				}
 802				if (minMax[1].mV[i] > newMax.mV[i])
 803				{
 804					newMax.mV[i] = minMax[1].mV[i];
 805				}
 806			}*/
 807		}
 808		
 809		mObjectBounds[0].setAdd(newMin, newMax);
 810		mObjectBounds[0].mul(0.5f);
 811		mObjectBounds[1].setSub(newMax, newMin);
 812		mObjectBounds[1].mul(0.5f);
 813	}
 814	
 815	if (empty)
 816	{
 817		minOut = newMin;
 818		maxOut = newMax;
 819	}
 820	else
 821	{
 822		minOut.setMin(minOut, newMin);
 823		maxOut.setMax(maxOut, newMax);
 824	}
 825		
 826	return TRUE;
 827}
 828
 829void LLSpatialGroup::unbound()
 830{
 831	if (isState(DIRTY))
 832	{
 833		return;
 834	}
 835
 836	setState(DIRTY);
 837	
 838	//all the parent nodes need to rebound this child
 839	if (mOctreeNode)
 840	{
 841		OctreeNode* parent = (OctreeNode*) mOctreeNode->getParent();
 842		while (parent != NULL)
 843		{
 844			LLSpatialGroup* group = (LLSpatialGroup*) parent->getListener(0);
 845			if (group->isState(DIRTY))
 846			{
 847				return;
 848			}
 849			
 850			group->setState(DIRTY);
 851			parent = (OctreeNode*) parent->getParent();
 852		}
 853	}
 854}
 855
 856LLSpatialGroup* LLSpatialGroup::getParent()
 857{
 858	if (isDead())
 859	{
 860		return NULL;
 861	}
 862
 863	if(!mOctreeNode)
 864	{
 865		return NULL;
 866	}
 867	OctreeNode* parent = mOctreeNode->getOctParent();
 868
 869	if (parent)
 870	{
 871		return (LLSpatialGroup*) parent->getListener(0);
 872	}
 873
 874	return NULL;
 875}
 876
 877BOOL LLSpatialGroup::removeObject(LLDrawable *drawablep, BOOL from_octree)
 878{
 879	LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
 880	unbound();
 881	if (mOctreeNode && !from_octree)
 882	{
 883		if (!mOctreeNode->remove(drawablep))
 884		{
 885			OCT_ERRS << "Could not remove drawable from spatial group" << llendl;
 886		}
 887	}
 888	else
 889	{
 890		drawablep->setSpatialGroup(NULL);
 891		setState(GEOM_DIRTY);
 892		gPipeline.markRebuild(this, TRUE);
 893
 894		if (drawablep->isSpatialBridge())
 895		{
 896			for (bridge_list_t::iterator i = mBridgeList.begin(); i != mBridgeList.end(); ++i)
 897			{
 898				if (*i == drawablep)
 899				{
 900					mBridgeList.erase(i);
 901					break;
 902				}
 903			}
 904		}
 905
 906		if (getElementCount() == 0)
 907		{ //delete draw map on last element removal since a rebuild might never happen
 908			clearDrawMap();
 909		}
 910	}
 911	return TRUE;
 912}
 913
 914void LLSpatialGroup::shift(const LLVector4a &offset)
 915{
 916	LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
 917	LLVector4a t = mOctreeNode->getCenter();
 918	t.add(offset);	
 919	mOctreeNode->setCenter(t);
 920	mOctreeNode->updateMinMax();
 921	mBounds[0].add(offset);
 922	mExtents[0].add(offset);
 923	mExtents[1].add(offset);
 924	mObjectBounds[0].add(offset);
 925	mObjectExtents[0].add(offset);
 926	mObjectExtents[1].add(offset);
 927
 928	//if (!mSpatialPartition->mRenderByGroup)
 929	{
 930		setState(GEOM_DIRTY);
 931		gPipeline.markRebuild(this, TRUE);
 932	}
 933
 934	if (mOcclusionVerts.notNull())
 935	{
 936		setState(OCCLUSION_DIRTY);
 937	}
 938}
 939
 940class LLSpatialSetState : public LLSpatialGroup::OctreeTraveler
 941{
 942public:
 943	U32 mState;
 944	LLSpatialSetState(U32 state) : mState(state) { }
 945	virtual void visit(const LLSpatialGroup::OctreeNode* branch) { ((LLSpatialGroup*) branch->getListener(0))->setState(mState); }	
 946};
 947
 948class LLSpatialSetStateDiff : public LLSpatialSetState
 949{
 950public:
 951	LLSpatialSetStateDiff(U32 state) : LLSpatialSetState(state) { }
 952
 953	virtual void traverse(const LLSpatialGroup::OctreeNode* n)
 954	{
 955		LLSpatialGroup* group = (LLSpatialGroup*) n->getListener(0);
 956		
 957		if (!group->isState(mState))
 958		{
 959			LLSpatialGroup::OctreeTraveler::traverse(n);
 960		}
 961	}
 962};
 963
 964void LLSpatialGroup::setState(U32 state) 
 965{ 
 966	mState |= state; 
 967	
 968	llassert(state <= LLSpatialGroup::STATE_MASK);
 969}	
 970
 971void LLSpatialGroup::setState(U32 state, S32 mode) 
 972{
 973	LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
 974
 975	llassert(state <= LLSpatialGroup::STATE_MASK);
 976	
 977	if (mode > STATE_MODE_SINGLE)
 978	{
 979		if (mode == STATE_MODE_DIFF)
 980		{
 981			LLSpatialSetStateDiff setter(state);
 982			setter.traverse(mOctreeNode);
 983		}
 984		else
 985		{
 986			LLSpatialSetState setter(state);
 987			setter.traverse(mOctreeNode);
 988		}
 989	}
 990	else
 991	{
 992		mState |= state;
 993	}
 994}
 995
 996class LLSpatialClearState : public LLSpatialGroup::OctreeTraveler
 997{
 998public:
 999	U32 mState;
1000	LLSpatialClearState(U32 state) : mState(state) { }
1001	virtual void visit(const LLSpatialGroup::OctreeNode* branch) { ((LLSpatialGroup*) branch->getListener(0))->clearState(mState); }
1002};
1003
1004class LLSpatialClearStateDiff : public LLSpatialClearState
1005{
1006public:
1007	LLSpatialClearStateDiff(U32 state) : LLSpatialClearState(state) { }
1008
1009	virtual void traverse(const LLSpatialGroup::OctreeNode* n)
1010	{
1011		LLSpatialGroup* group = (LLSpatialGroup*) n->getListener(0);
1012		
1013		if (group->isState(mState))
1014		{
1015			LLSpatialGroup::OctreeTraveler::traverse(n);
1016		}
1017	}
1018};
1019
1020void LLSpatialGroup::clearState(U32 state)
1021{
1022	llassert(state <= LLSpatialGroup::STATE_MASK);
1023
1024	mState &= ~state; 
1025}
1026
1027void LLSpatialGroup::clearState(U32 state, S32 mode)
1028{
1029	llassert(state <= LLSpatialGroup::STATE_MASK);
1030
1031	LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
1032	
1033	if (mode > STATE_MODE_SINGLE)
1034	{
1035		if (mode == STATE_MODE_DIFF)
1036		{
1037			LLSpatialClearStateDiff clearer(state);
1038			clearer.traverse(mOctreeNode);
1039		}
1040		else
1041		{
1042			LLSpatialClearState clearer(state);
1043			clearer.traverse(mOctreeNode);
1044		}
1045	}
1046	else
1047	{
1048		mState &= ~state;
1049	}
1050}
1051
1052BOOL LLSpatialGroup::isState(U32 state) const
1053{ 
1054	llassert(state <= LLSpatialGroup::STATE_MASK);
1055
1056	return mState & state ? TRUE : FALSE; 
1057}
1058
1059//=====================================
1060//		Occlusion State Set/Clear
1061//=====================================
1062class LLSpatialSetOcclusionState : public LLSpatialGroup::OctreeTraveler
1063{
1064public:
1065	U32 mState;
1066	LLSpatialSetOcclusionState(U32 state) : mState(state) { }
1067	virtual void visit(const LLSpatialGroup::OctreeNode* branch) { ((LLSpatialGroup*) branch->getListener(0))->setOcclusionState(mState); }	
1068};
1069
1070class LLSpatialSetOcclusionStateDiff : public LLSpatialSetOcclusionState
1071{
1072public:
1073	LLSpatialSetOcclusionStateDiff(U32 state) : LLSpatialSetOcclusionState(state) { }
1074
1075	virtual void traverse(const LLSpatialGroup::OctreeNode* n)
1076	{
1077		LLSpatialGroup* group = (LLSpatialGroup*) n->getListener(0);
1078		
1079		if (!group->isOcclusionState(mState))
1080		{
1081			LLSpatialGroup::OctreeTraveler::traverse(n);
1082		}
1083	}
1084};
1085
1086
1087void LLSpatialGroup::setOcclusionState(U32 state, S32 mode) 
1088{
1089	LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
1090	
1091	if (mode > STATE_MODE_SINGLE)
1092	{
1093		if (mode == STATE_MODE_DIFF)
1094		{
1095			LLSpatialSetOcclusionStateDiff setter(state);
1096			setter.traverse(mOctreeNode);
1097		}
1098		else if (mode == STATE_MODE_BRANCH)
1099		{
1100			LLSpatialSetOcclusionState setter(state);
1101			setter.traverse(mOctreeNode);
1102		}
1103		else
1104		{
1105			for (U32 i = 0; i < LLViewerCamera::NUM_CAMERAS; i++)
1106			{
1107				mOcclusionState[i] |= state;
1108
1109				if ((state & DISCARD_QUERY) && mOcclusionQuery[i])
1110				{
1111					sQueryPool.release(mOcclusionQuery[i]);
1112					mOcclusionQuery[i] = 0;
1113				}
1114			}
1115		}
1116	}
1117	else
1118	{
1119		mOcclusionState[LLViewerCamera::sCurCameraID] |= state;
1120		if ((state & DISCARD_QUERY) && mOcclusionQuery[LLViewerCamera::sCurCameraID])
1121		{
1122			sQueryPool.release(mOcclusionQuery[LLViewerCamera::sCurCameraID]);
1123			mOcclusionQuery[LLViewerCamera::sCurCameraID] = 0;
1124		}
1125	}
1126}
1127
1128class LLSpatialClearOcclusionState : public LLSpatialGroup::OctreeTraveler
1129{
1130public:
1131	U32 mState;
1132	
1133	LLSpatialClearOcclusionState(U32 state) : mState(state) { }
1134	virtual void visit(const LLSpatialGroup::OctreeNode* branch) { ((LLSpatialGroup*) branch->getListener(0))->clearOcclusionState(mState); }
1135};
1136
1137class LLSpatialClearOcclusionStateDiff : public LLSpatialClearOcclusionState
1138{
1139public:
1140	LLSpatialClearOcclusionStateDiff(U32 state) : LLSpatialClearOcclusionState(state) { }
1141
1142	virtual void traverse(const LLSpatialGroup::OctreeNode* n)
1143	{
1144		LLSpatialGroup* group = (LLSpatialGroup*) n->getListener(0);
1145		
1146		if (group->isOcclusionState(mState))
1147		{
1148			LLSpatialGroup::OctreeTraveler::traverse(n);
1149		}
1150	}
1151};
1152
1153void LLSpatialGroup::clearOcclusionState(U32 state, S32 mode)
1154{
1155	LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
1156	
1157	if (mode > STATE_MODE_SINGLE)
1158	{
1159		if (mode == STATE_MODE_DIFF)
1160		{
1161			LLSpatialClearOcclusionStateDiff clearer(state);
1162			clearer.traverse(mOctreeNode);
1163		}
1164		else if (mode == STATE_MODE_BRANCH)
1165		{
1166			LLSpatialClearOcclusionState clearer(state);
1167			clearer.traverse(mOctreeNode);
1168		}
1169		else
1170		{
1171			for (U32 i = 0; i < LLViewerCamera::NUM_CAMERAS; i++)
1172			{
1173				mOcclusionState[i] &= ~state;
1174			}
1175		}
1176	}
1177	else
1178	{
1179		mOcclusionState[LLViewerCamera::sCurCameraID] &= ~state;
1180	}
1181}
1182//======================================
1183//		Octree Listener Implementation
1184//======================================
1185
1186LLSpatialGroup::LLSpatialGroup(OctreeNode* node, LLSpatialPartition* part) :
1187	mState(0),
1188	mBuilt(0.f),
1189	mOctreeNode(node),
1190	mSpatialPartition(part),
1191	mVertexBuffer(NULL), 
1192	mBufferUsage(part->mBufferUsage),
1193	mDistance(0.f),
1194	mDepth(0.f),
1195	mLastUpdateDistance(-1.f), 
1196	mLastUpdateTime(gFrameTimeSeconds),
1197	mAtlasList(4),
1198	mCurUpdatingTime(0),
1199	mCurUpdatingSlotp(NULL),
1200	mCurUpdatingTexture (NULL)
1201{
1202	sNodeCount++;
1203	LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
1204
1205	mViewAngle.splat(0.f);
1206	mLastUpdateViewAngle.splat(-1.f);
1207	mExtents[0] = mExtents[1] = mObjectBounds[0] = mObjectBounds[0] = mObjectBounds[1] = 
1208		mObjectExtents[0] = mObjectExtents[1] = mViewAngle;
1209
1210	sg_assert(mOctreeNode->getListenerCount() == 0);
1211	mOctreeNode->addListener(this);
1212	setState(SG_INITIAL_STATE_MASK);
1213	gPipeline.markRebuild(this, TRUE);
1214
1215	mBounds[0] = node->getCenter();
1216	mBounds[1] = node->getSize();
1217
1218	part->mLODSeed = (part->mLODSeed+1)%part->mLODPeriod;
1219	mLODHash = part->mLODSeed;
1220
1221	OctreeNode* oct_parent = node->getOctParent();
1222
1223	LLSpatialGroup* parent = oct_parent ? (LLSpatialGroup*) oct_parent->getListener(0) : NULL;
1224
1225	for (U32 i = 0; i < LLViewerCamera::NUM_CAMERAS; i++)
1226	{
1227		mOcclusionQuery[i] = 0;
1228		mOcclusionIssued[i] = 0;
1229		mOcclusionState[i] = parent ? SG_STATE_INHERIT_MASK & parent->mOcclusionState[i] : 0;
1230		mVisible[i] = 0;
1231	}
1232
1233	mOcclusionVerts = NULL;
1234
1235	mRadius = 1;
1236	mPixelArea = 1024.f;
1237}
1238
1239void LLSpatialGroup::updateDistance(LLCamera &camera)
1240{
1241	if (LLViewerCamera::sCurCameraID != LLViewerCamera::CAMERA_WORLD)
1242	{
1243		llwarns << "Attempted to update distance for camera other than world camera!" << llendl;
1244		return;
1245	}
1246
1247#if !LL_RELEASE_FOR_DOWNLOAD
1248	if (isState(LLSpatialGroup::OBJECT_DIRTY))
1249	{
1250		llerrs << "Spatial group dirty on distance update." << llendl;
1251	}
1252#endif
1253	if (!getData().empty())
1254	{
1255		mRadius = mSpatialPartition->mRenderByGroup ? mObjectBounds[1].getLength3().getF32() :
1256						(F32) mOctreeNode->getSize().getLength3().getF32();
1257		mDistance = mSpatialPartition->calcDistance(this, camera);
1258		mPixelArea = mSpatialPartition->calcPixelArea(this, camera);
1259	}
1260}
1261
1262F32 LLSpatialPartition::calcDistance(LLSpatialGroup* group, LLCamera& camera)
1263{
1264	LLVector4a eye;
1265	LLVector4a origin;
1266	origin.load3(camera.getOrigin().mV);
1267
1268	eye.setSub(group->mObjectBounds[0], origin);
1269
1270	F32 dist = 0.f;
1271
1272	if (group->mDrawMap.find(LLRenderPass::PASS_ALPHA) != group->mDrawMap.end())
1273	{
1274		LLVector4a v = eye;
1275
1276		dist = eye.getLength3().getF32();
1277		eye.normalize3fast();
1278
1279		if (!group->isState(LLSpatialGroup::ALPHA_DIRTY))
1280		{
1281			if (!group->mSpatialPartition->isBridge())
1282			{
1283				LLVector4a view_angle = eye;
1284
1285				LLVector4a diff;
1286				diff.setSub(view_angle, group->mLastUpdateViewAngle);
1287
1288				if (diff.getLength3().getF32() > 0.64f)
1289				{
1290					group->mViewAngle = view_angle;
1291					group->mLastUpdateViewAngle = view_angle;
1292					//for occasional alpha sorting within the group
1293					//NOTE: If there is a trivial way to detect that alpha sorting here would not change the render order,
1294					//not setting this node to dirty would be a very good thing
1295					group->setState(LLSpatialGroup::ALPHA_DIRTY);
1296					gPipeline.markRebuild(group, FALSE);
1297				}
1298			}
1299		}
1300
1301		//calculate depth of node for alpha sorting
1302
1303		LLVector3 at = camera.getAtAxis();
1304
1305		LLVector4a ata;
1306		ata.load3(at.mV);
1307
1308		LLVector4a t = ata;
1309		//front of bounding box
1310		t.mul(0.25f);
1311		t.mul(group->mObjectBounds[1]);
1312		v.sub(t);
1313		
1314		group->mDepth = v.dot3(ata).getF32();
1315	}
1316	else
1317	{
1318		dist = eye.getLength3().getF32();
1319	}
1320
1321	if (dist < 16.f)
1322	{
1323		dist /= 16.f;
1324		dist *= dist;
1325		dist *= 16.f;
1326	}
1327
1328	return dist;
1329}
1330
1331F32 LLSpatialPartition::calcPixelArea(LLSpatialGroup* group, LLCamera& camera)
1332{
1333	return LLPipeline::calcPixelArea(group->mObjectBounds[0], group->mObjectBounds[1], camera);
1334}
1335
1336F32 LLSpatialGroup::getUpdateUrgency() const
1337{
1338	if (!isVisible())
1339	{
1340		return 0.f;
1341	}
1342	else
1343	{
1344		F32 time = gFrameTimeSeconds-mLastUpdateTime+4.f;
1345		return time + (mObjectBounds[1].dot3(mObjectBounds[1]).getF32()+1.f)/mDistance;
1346	}
1347}
1348
1349BOOL LLSpatialGroup::needsUpdate()
1350{
1351	return (LLDrawable::getCurrentFrame()%mSpatialPartition->mLODPeriod == mLODHash) ? TRUE : FALSE;
1352}
1353
1354BOOL LLSpatialGroup::changeLOD()
1355{
1356	if (isState(ALPHA_DIRTY | OBJECT_DIRTY))
1357	{ ///a rebuild is going to happen, update distance and LoD
1358		return TRUE;
1359	}
1360
1361	if (mSpatialPartition->mSlopRatio > 0.f)
1362	{
1363		F32 ratio = (mDistance - mLastUpdateDistance)/(llmax(mLastUpdateDistance, mRadius));
1364
1365		if (fabsf(ratio) >= mSpatialPartition->mSlopRatio)
1366		{
1367			return TRUE;
1368		}
1369
1370		if (mDistance > mRadius*2.f)
1371		{
1372			return FALSE;
1373		}
1374	}
1375	
1376	if (needsUpdate())
1377	{
1378		return TRUE;
1379	}
1380	
1381	return FALSE;
1382}
1383
1384void LLSpatialGroup::handleInsertion(const TreeNode* node, LLDrawable* drawablep)
1385{
1386	LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
1387	addObject(drawablep, FALSE, TRUE);
1388	unbound();
1389	setState(OBJECT_DIRTY);
1390}
1391
1392void LLSpatialGroup::handleRemoval(const TreeNode* node, LLDrawable* drawable)
1393{
1394	LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
1395	removeObject(drawable, TRUE);
1396	setState(OBJECT_DIRTY);
1397}
1398
1399void LLSpatialGroup::handleDestruction(const TreeNode* node)
1400{
1401	LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
1402	setState(DEAD);
1403	
1404	for (element_iter i = getData().begin(); i != getData().end(); ++i)
1405	{
1406		LLDrawable* drawable = *i;
1407		if (drawable->getSpatialGroup() == this)
1408		{
1409			drawable->setSpatialGroup(NULL);
1410		}
1411	}
1412	
1413	clearDrawMap();
1414	mVertexBuffer = NULL;
1415	mBufferMap.clear();
1416	sZombieGroups++;
1417	mOctreeNode = NULL;
1418}
1419
1420void LLSpatialGroup::handleStateChange(const TreeNode* node)
1421{
1422	//drop bounding box upon state change
1423	if (mOctreeNode != node)
1424	{
1425		mOctreeNode = (OctreeNode*) node;
1426	}
1427	unbound();
1428}
1429
1430void LLSpatialGroup::handleChildAddition(const OctreeNode* parent, OctreeNode* child) 
1431{
1432	LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
1433	if (child->getListenerCount() == 0)
1434	{
1435		new LLSpatialGroup(child, mSpatialPartition);
1436	}
1437	else
1438	{
1439		OCT_ERRS << "LLSpatialGroup redundancy detected." << llendl;
1440	}
1441
1442	unbound();
1443
1444	assert_states_valid(this);
1445}
1446
1447void LLSpatialGroup::handleChildRemoval(const OctreeNode* parent, const OctreeNode* child)
1448{
1449	unbound();
1450}
1451
1452void LLSpatialGroup::destroyGL() 
1453{
1454	setState(LLSpatialGroup::GEOM_DIRTY | LLSpatialGroup::IMAGE_DIRTY);
1455	gPipeline.markRebuild(this, TRUE);
1456
1457	mLastUpdateTime = gFrameTimeSeconds;
1458	mVertexBuffer = NULL;
1459	mBufferMap.clear();
1460
1461	clearDrawMap();
1462
1463	for (U32 i = 0; i < LLViewerCamera::NUM_CAMERAS; i++)
1464	{
1465		if (mOcclusionQuery[i])
1466		{
1467			sQueryPool.release(mOcclusionQuery[i]);
1468			mOcclusionQuery[i] = 0;
1469		}
1470	}
1471
1472	mOcclusionVerts = NULL;
1473
1474	for (LLSpatialGroup::element_iter i = getData().begin(); i != getData().end(); ++i)
1475	{
1476		LLDrawable* drawable = *i;
1477		for (S32 j = 0; j < drawable->getNumFaces(); j++)
1478		{
1479			LLFace* facep = drawable->getFace(j);
1480			facep->clearVertexBuffer();
1481		}
1482	}
1483}
1484
1485BOOL LLSpatialGroup::rebound()
1486{
1487	if (!isState(DIRTY))
1488	{	//return TRUE if we're not empty
1489		return TRUE;
1490	}
1491	
1492	if (mOctreeNode->getChildCount() == 1 && mOctreeNode->getElementCount() == 0)
1493	{
1494		LLSpatialGroup* group = (LLSpatialGroup*) mOctreeNode->getChild(0)->getListener(0);
1495		group->rebound();
1496		
1497		//copy single child's bounding box
1498		mBounds[0] = group->mBounds[0];
1499		mBounds[1] = group->mBounds[1];
1500		mExtents[0] = group->mExtents[0];
1501		mExtents[1] = group->mExtents[1];
1502		
1503		group->setState(SKIP_FRUSTUM_CHECK);
1504	}
1505	else if (mOctreeNode->isLeaf())
1506	{ //copy object bounding box if this is a leaf
1507		boundObjects(TRUE, mExtents[0], mExtents[1]);
1508		mBounds[0] = mObjectBounds[0];
1509		mBounds[1] = mObjectBounds[1];
1510	}
1511	else
1512	{
1513		LLVector4a& newMin = mExtents[0];
1514		LLVector4a& newMax = mExtents[1];
1515		LLSpatialGroup* group = (LLSpatialGroup*) mOctreeNode->getChild(0)->getListener(0);
1516		group->clearState(SKIP_FRUSTUM_CHECK);
1517		group->rebound();
1518		//initialize to first child
1519		newMin = group->mExtents[0];
1520		newMax = group->mExtents[1];
1521
1522		//first, rebound children
1523		for (U32 i = 1; i < mOctreeNode->getChildCount(); i++)
1524		{
1525			group = (LLSpatialGroup*) mOctreeNode->getChild(i)->getListener(0);
1526			group->clearState(SKIP_FRUSTUM_CHECK);
1527			group->rebound();
1528			const LLVector4a& max = group->mExtents[1];
1529			const LLVector4a& min = group->mExtents[0];
1530
1531			newMax.setMax(newMax, max);
1532			newMin.setMin(newMin, min);
1533		}
1534
1535		boundObjects(FALSE, newMin, newMax);
1536		
1537		mBounds[0].setAdd(newMin, newMax);
1538		mBounds[0].mul(0.5f);
1539		mBounds[1].setSub(newMax, newMin);
1540		mBounds[1].mul(0.5f);
1541	}
1542	
1543	setState(OCCLUSION_DIRTY);
1544	
1545	clearState(DIRTY);
1546
1547	return TRUE;
1548}
1549
1550static LLFastTimer::DeclareTimer FTM_OCCLUSION_READBACK("Readback Occlusion");
1551static LLFastTimer::DeclareTimer FTM_OCCLUSION_WAIT("Wait");
1552
1553void LLSpatialGroup::checkOcclusion()
1554{
1555	if (LLPipeline::sUseOcclusion > 1)
1556	{
1557		LLFastTimer t(FTM_OCCLUSION_READBACK);
1558		LLSpatialGroup* parent = getParent();
1559		if (parent && parent->isOcclusionState(LLSpatialGroup::OCCLUDED))
1560		{	//if the parent has been marked as occluded, the child is implicitly occluded
1561			clearOcclusionState(QUERY_PENDING | DISCARD_QUERY);
1562		}
1563		else if (isOcclusionState(QUERY_PENDING))
1564		{	//otherwise, if a query is pending, read it back
1565
1566			GLuint available = 0;
1567			if (mOcclusionQuery[LLViewerCamera::sCurCameraID])
1568			{
1569				glGetQueryObjectuivARB(mOcclusionQuery[LLViewerCamera::sCurCameraID], GL_QUERY_RESULT_AVAILABLE_ARB, &available);
1570
1571				if (mOcclusionIssued[LLViewerCamera::sCurCameraID] < gFrameCount)
1572				{ //query was issued last frame, wait until it's available
1573					S32 max_loop = 1024;
1574					LLFastTimer t(FTM_OCCLUSION_WAIT);
1575					while (!available && max_loop-- > 0)
1576					{
1577						F32 max_time = llmin(gFrameIntervalSeconds*10.f, 1.f);
1578						//do some usefu work while we wait
1579						LLAppViewer::getTextureCache()->update(max_time); // unpauses the texture cache thread
1580						LLAppViewer::getImageDecodeThread()->update(max_time); // unpauses the image thread
1581						LLAppViewer::getTextureFetch()->update(max_time); // unpauses the texture fetch thread
1582						
1583						glGetQueryObjectuivARB(mOcclusionQuery[LLViewerCamera::sCurCameraID], GL_QUERY_RESULT_AVAILABLE_ARB, &available);
1584					}
1585				}
1586			}
1587			else
1588			{
1589				available = 1;
1590			}
1591
1592			if (available)
1593			{ //result is available, read it back, otherwise wait until next frame
1594				GLuint res = 1;
1595				if (!isOcclusionState(DISCARD_QUERY) && mOcclusionQuery[LLViewerCamera::sCurCameraID])
1596				{
1597					glGetQueryObjectuivARB(mOcclusionQuery[LLViewerCamera::sCurCameraID], GL_QUERY_RESULT_ARB, &res);	
1598#if LL_TRACK_PENDING_OCCLUSION_QUERIES
1599					sPendingQueries.erase(mOcclusionQuery[LLViewerCamera::sCurCameraID]);
1600#endif
1601				}
1602				else if (mOcclusionQuery[LLViewerCamera::sCurCameraID])
1603				{ //delete the query to avoid holding onto hundreds of pending queries
1604					sQueryPool.release(mOcclusionQuery[LLViewerCamera::sCurCameraID]);
1605					mOcclusionQuery[LLViewerCamera::sCurCameraID] = 0;
1606				}
1607				
1608				if (isOcclusionState(DISCARD_QUERY))
1609				{
1610					res = 2;
1611				}
1612
1613				if (res > 0)
1614				{
1615					assert_states_valid(this);
1616					clearOcclusionState(LLSpatialGroup::OCCLUDED, LLSpatialGroup::STATE_MODE_DIFF);
1617					assert_states_valid(this);
1618				}
1619				else
1620				{
1621					assert_states_valid(this);
1622					setOcclusionState(LLSpatialGroup::OCCLUDED, LLSpatialGroup::STATE_MODE_DIFF);
1623					assert_states_valid(this);
1624				}
1625
1626				clearOcclusionState(QUERY_PENDING | DISCARD_QUERY);
1627			}
1628		}
1629		else if (mSpatialPartition->isOcclusionEnabled() && isOcclusionState(LLSpatialGroup::OCCLUDED))
1630		{	//check occlusion has been issued for occluded node that has not had a query issued
1631			assert_states_valid(this);
1632			clearOcclusionState(LLSpatialGroup::OCCLUDED, LLSpatialGroup::STATE_MODE_DIFF);
1633			assert_states_valid(this);
1634		}
1635	}
1636}
1637
1638static LLFastTimer::DeclareTimer FTM_PUSH_OCCLUSION_VERTS("Push Occlusion");
1639static LLFastTimer::DeclareTimer FTM_SET_OCCLUSION_STATE("Occlusion State");
1640static LLFastTimer::DeclareTimer FTM_OCCLUSION_EARLY_FAIL("Occlusion Early Fail");
1641static LLFastTimer::DeclareTimer FTM_OCCLUSION_ALLOCATE("Allocate");
1642static LLFastTimer::DeclareTimer FTM_OCCLUSION_BUILD("Build");
1643static LLFastTimer::DeclareTimer FTM_OCCLUSION_BEGIN_QUERY("Begin Query");
1644static LLFastTimer::DeclareTimer FTM_OCCLUSION_END_QUERY("End Query");
1645static LLFastTimer::DeclareTimer FTM_OCCLUSION_SET_BUFFER("Set Buffer");
1646static LLFastTimer::DeclareTimer FTM_OCCLUSION_DRAW_WATER("Draw Water");
1647static LLFastTimer::DeclareTimer FTM_OCCLUSION_DRAW("Draw");
1648
1649
1650
1651void LLSpatialGroup::doOcclusion(LLCamera* camera)
1652{
1653	if (mSpatialPartition->isOcclusionEnabled() && LLPipeline::sUseOcclusion > 1)
1654	{
1655		// Don't cull hole/edge water, unless we have the GL_ARB_depth_clamp extension
1656		if (earlyFail(camera, this))
1657		{
1658			LLFastTimer t(FTM_OCCLUSION_EARLY_FAIL);
1659			setOcclusionState(LLSpatialGroup::DISCARD_QUERY);
1660			assert_states_valid(this);
1661			clearOcclusionState(LLSpatialGroup::OCCLUDED, LLSpatialGroup::STATE_MODE_DIFF);
1662			assert_states_valid(this);
1663		}
1664		else
1665		{
1666			if (!isOcclusionState(QUERY_PENDING) || isOcclusionState(DISCARD_QUERY))
1667			{
1668				{ //no query pending, or previous query to be discarded
1669					LLFastTimer t(FTM_RENDER_OCCLUSION);
1670
1671					if (!mOcclusionQuery[LLViewerCamera::sCurCameraID])
1672					{
1673						LLFastTimer t(FTM_OCCLUSION_ALLOCATE);
1674						mOcclusionQuery[LLViewerCamera::sCurCameraID] = sQueryPool.allocate();
1675					}
1676
1677					if (mOcclusionVerts.isNull() || isState(LLSpatialGroup::OCCLUSION_DIRTY))
1678					{
1679						LLFastTimer t(FTM_OCCLUSION_BUILD);
1680						buildOcclusion();
1681					}
1682					
1683					// Depth clamp all water to avoid it being culled as a result of being
1684					// behind the far clip plane, and in the case of edge water to avoid
1685					// it being culled while still visible.
1686					bool const use_depth_clamp = gGLManager.mHasDepthClamp &&
1687												(mSpatialPartition->mDrawableType == LLDrawPool::POOL_WATER ||						
1688												mSpatialPartition->mDrawableType == LLDrawPool::POOL_VOIDWATER);
1689
1690					LLGLEnable clamp(use_depth_clamp ? GL_DEPTH_CLAMP : 0);				
1691						
1692#if !LL_DARWIN					
1693					U32 mode = gGLManager.mHasOcclusionQuery2 ? GL_ANY_SAMPLES_PASSED : GL_SAMPLES_PASSED_ARB;
1694#else
1695					U32 mode = GL_SAMPLES_PASSED_ARB;
1696#endif
1697					
1698#if LL_TRACK_PENDING_OCCLUSION_QUERIES
1699					sPendingQueries.insert(mOcclusionQuery[LLViewerCamera::sCurCameraID]);
1700#endif
1701
1702					{
1703						LLFastTimer t(FTM_PUSH_OCCLUSION_VERTS);
1704						
1705						//store which frame this query was issued on
1706						mOcclusionIssued[LLViewerCamera::sCurCameraID] = gFrameCount;
1707
1708						{
1709							LLFastTimer t(FTM_OCCLUSION_BEGIN_QUERY);
1710							glBeginQueryARB(mode, mOcclusionQuery[LLViewerCamera::sCurCameraID]);					
1711						}
1712					
1713						{
1714							LLFastTimer t(FTM_OCCLUSION_SET_BUFFER);
1715							mOcclusionVerts->setBuffer(LLVertexBuffer::MAP_VERTEX);
1716						}
1717
1718						if (!use_depth_clamp && mSpatialPartition->mDrawableType == LLDrawPool::POOL_VOIDWATER)
1719						{
1720							LLFastTimer t(FTM_OCCLUSION_DRAW_WATER);
1721
1722							LLGLSquashToFarClip squash(glh_get_current_projection(), 1);
1723							if (camera->getOrigin().isExactlyZero())
1724							{ //origin is invalid, draw entire box
1725								mOcclusionVerts->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, 0);
1726								mOcclusionVerts->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, b111*8);				
1727							}
1728							else
1729							{
1730								mOcclusionVerts->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, get_box_fan_indices(camera, mBounds[0]));
1731							}
1732						}
1733						else
1734						{
1735							LLFastTimer t(FTM_OCCLUSION_DRAW);
1736							if (camera->getOrigin().isExactlyZero())
1737							{ //origin is invalid, draw entire box
1738								mOcclusionVerts->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, 0);
1739								mOcclusionVerts->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, b111*8);				
1740							}
1741							else
1742							{
1743								mOcclusionVerts->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, get_box_fan_indices(camera, mBounds[0]));
1744							}
1745						}
1746
1747
1748						{
1749							LLFastTimer t(FTM_OCCLUSION_END_QUERY);
1750							glEndQueryARB(mode);
1751						}
1752					}
1753				}
1754
1755				{
1756					LLFastTimer t(FTM_SET_OCCLUSION_STATE);
1757					setOcclusionState(LLSpatialGroup::QUERY_PENDING);
1758					clearOcclusionState(LLSpatialGroup::DISCARD_QUERY);
1759				}
1760			}
1761		}
1762	}
1763}
1764
1765//==============================================
1766
1767LLSpatialPartition::LLSpatialPartition(U32 data_mask, BOOL render_by_group, U32 buffer_usage)
1768: mRenderByGroup(render_by_group)
1769{
1770	LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
1771	mOcclusionEnabled = TRUE;
1772	mDrawableType = 0;
1773	mPartitionType = LLViewerRegion::PARTITION_NONE;
1774	mLODSeed = 0;
1775	mLODPeriod = 1;
1776	mVertexDataMask = data_mask;
1777	mBufferUsage = buffer_usage;
1778	mDepthMask = FALSE;
1779	mSlopRatio = 0.25f;
1780	mInfiniteFarClip = FALSE;
1781
1782	LLVector4a center, size;
1783	center.splat(0.f);
1784	size.splat(1.f);
1785
1786	mOctree = new LLSpatialGroup::OctreeRoot(center,size,
1787											NULL);
1788	new LLSpatialGroup(mOctree, this);
1789}
1790
1791
1792LLSpatialPartition::~LLSpatialPartition()
1793{
1794	LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
1795	
1796	delete mOctree;
1797	mOctree = NULL;
1798}
1799
1800
1801LLSpatialGroup *LLSpatialPartition::put(LLDrawable *drawablep, BOOL was_visible)
1802{
1803	LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
1804		
1805	drawablep->updateSpatialExtents();
1806
1807	//keep drawable from being garbage collected
1808	LLPointer<LLDrawable> ptr = drawablep;
1809		
1810	assert_octree_valid(mOctree);
1811	mOctree->insert(drawablep);
1812	assert_octree_valid(mOctree);
1813	
1814	LLSpatialGroup* group = drawablep->getSpatialGroup();
1815
1816	if (group && was_visible && group->isOcclusionState(LLSpatialGroup::QUERY_PENDING))
1817	{
1818		group->setOcclusionState(LLSpatialGroup::DISCARD_QUERY, LLSpatialGroup::STATE_MODE_ALL_CAMERAS);
1819	}
1820
1821	return group;
1822}
1823
1824BOOL LLSpatialPartition::remove(LLDrawable *drawablep, LLSpatialGroup *curp)
1825{
1826	LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
1827	
1828	drawablep->setSpatialGroup(NULL);
1829
1830	if (!curp->removeObject(drawablep))
1831	{
1832		OCT_ERRS << "Failed to remove drawable from octree!" << llendl;
1833	}
1834
1835	assert_octree_valid(mOctree);
1836	
1837	return TRUE;
1838}
1839
1840void LLSpatialPartition::move(LLDrawable *drawablep, LLSpatialGroup *curp, BOOL immediate)
1841{
1842	LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
1843		
1844	// sanity check submitted by open source user bushing Spatula
1845	// who was seeing crashing here. (See VWR-424 reported by Bunny Mayne)
1846	if (!drawablep)
1847	{
1848		OCT_ERRS << "LLSpatialPartition::move was passed a bad drawable." << llendl;
1849		return;
1850	}
1851		
1852	BOOL was_visible = curp ? curp->isVisible() : FALSE;
1853
1854	if (curp && curp->mSpatialPartition != this)
1855	{
1856		//keep drawable from being garbage collected
1857		LLPointer<LLDrawable> ptr = drawablep;
1858		if (curp->mSpatialPartition->remove(drawablep, curp))
1859		{
1860			put(drawablep, was_visible);
1861			return;
1862		}
1863		else
1864		{
1865			OCT_ERRS << "Drawable lost between spatial partitions on outbound transition." << llendl;
1866		}
1867	}
1868		
1869	if (curp && curp->updateInGroup(drawablep, immediate))
1870	{
1871		// Already updated, don't need to do anything
1872		assert_octree_valid(mOctree);
1873		return;
1874	}
1875
1876	//keep drawable from being garbage collected
1877	LLPointer<LLDrawable> ptr = drawablep;
1878	if (curp && !remove(drawablep, curp))
1879	{
1880		OCT_ERRS << "Move couldn't find existing spatial group!" << llendl;
1881	}
1882
1883	put(drawablep, was_visible);
1884}
1885
1886class LLSpatialShift : public LLSpatialGroup::OctreeTraveler
1887{
1888public:
1889	const LLVector4a& mOffset;
1890
1891	LLSpatialShift(const LLVector4a& offset) : mOffset(offset) { }
1892	virtual void visit(const LLSpatialGroup::OctreeNode* branch) 
1893	{ 
1894		((LLSpatialGroup*) branch->getListener(0))->shift(mOffset); 
1895	}
1896};
1897
1898void LLSpatialPartition::shift(const LLVector4a &offset)
1899{ //shift octree node bounding boxes by offset
1900	LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
1901	LLSpatialShift shifter(offset);
1902	shifter.traverse(mOctree);
1903}
1904
1905class LLOctreeCull : public LLSpatialGroup::OctreeTraveler
1906{
1907public:
1908	LLOctreeCull(LLCamera* camera)
1909		: mCamera(camera), mRes(0) { }
1910
1911	virtual bool earlyFail(LLSpatialGroup* group)
1912	{
1913		group->checkOcclusion();
1914
1915		if (group->mOctreeNode->getParent() &&	//never occlusion cull the root node
1916		  	LLPipeline::sUseOcclusion &&			//ignore occlusion if disabled
1917			group->isOcclusionState(LLSpatialGroup::OCCLUDED))
1918		{
1919			gPipeline.markOccluder(group);
1920			return true;
1921		}
1922		
1923		return false;
1924	}
1925	
1926	virtual void traverse(const LLSpatialGroup::OctreeNode* n)
1927	{
1928		LLSpatialGroup* group = (LLSpatialGroup*) n->getListener(0);
1929
1930		if (earlyFail(group))
1931		{
1932			return;
1933		}
1934		
1935		if (mRes == 2 || 
1936			(mRes && group->isState(LLSpatialGroup::SKIP_FRUSTUM_CHECK)))
1937		{	//fully in, just add everything
1938			LLSpatialGroup::OctreeTraveler::traverse(n);
1939		}
1940		else
1941		{
1942			mRes = frustumCheck(group);
1943				
1944			if (mRes)
1945			{ //at least partially in, run on down
1946				LLSpatialGroup::OctreeTraveler::traverse(n);
1947			}
1948
1949			mRes = 0;
1950		}
1951	}
1952	
1953	virtual S32 frustumCheck(const LLSpatialGroup* group)
1954	{
1955		S32 res = mCamera->AABBInFrustumNoFarClip(group->mBounds[0], group->mBounds[1]);
1956		if (res != 0)
1957		{
1958			res = llmin(res, AABBSphereIntersect(group->mExtents[0], group->mExtents[1], mCamera->getOrigin(), mCamera->mFrustumCornerDist));
1959		}
1960		return res;
1961	}
1962
1963	virtual S32 frustumCheckObjects(const LLSpatialGroup* group)
1964	{
1965		S32 res = mCamera->AABBInFrustumNoFarClip(group->mObjectBounds[0], group->mObjectBounds[1]);
1966		if (res != 0)
1967		{
1968			res = llmin(res, AABBSphereIntersect(group->mObjectExtents[0], group->mObjectExtents[1], mCamera->getOrigin(), mCamera->mFrustumCornerDist));
1969		}
1970		return res;
1971	}
1972
1973	virtual bool checkObjects(const LLSpatialGroup::OctreeNode* branch, const LLSpatialGroup* group)
1974	{
1975		if (branch->getElementCount() == 0) //no elements
1976		{
1977			return false;
1978		}
1979		else if (branch->getChildCount() == 0) //leaf state, already checked tightest bounding box
1980		{
1981			return true;
1982		}
1983		else if (mRes == 1 && !frustumCheckObjects(group)) //no objects in frustum
1984		{
1985			return false;
1986		}
1987		
1988		return true;
1989	}
1990
1991	virtual void preprocess(LLSpatialGroup* group)
1992	{
1993		
1994	}
1995	
1996	virtual void processGroup(LLSpatialGroup* group)
1997	{
1998		if (group->needsUpdate() ||
1999			group->mVisible[LLViewerCamera::sCurCameraID] < LLDrawable::getCurrentFrame() - 1)
2000		{
2001			group->doOcclusion(mCamera);
2002		}
2003		gPipeline.markNotCulled(group, *mCamera);
2004	}
2005	
2006	virtual void visit(const LLSpatialGroup::OctreeNode* branch) 
2007	{	
2008		LLSpatialGroup* group = (LLSpatialGroup*) branch->getListener(0);
2009
2010		preprocess(group);
2011		
2012		if (checkObjects(branch, group))
2013		{
2014			processGroup(group);
2015		}
2016	}
2017
2018	LLCamera *mCamera;
2019	S32 mRes;
2020};
2021
2022class LLOctreeCullNoFarClip : public LLOctreeCull
2023{
2024public: 
2025	LLOctreeCullNoFarClip(LLCamera* camera) 
2026		: LLOctreeCull(camera) { }
2027
2028	virtual S32 frustumCheck(const LLSpatialGroup* group)
2029	{
2030		return mCamera->AABBInFrustumNoFarClip(group->mBounds[0], group->mBounds[1]);
2031	}
2032
2033	virtual S32 frustumCheckObjects(const LLSpatialGroup* group)
2034	{
2035		S32 res = mCamera->AABBInFrustumNoFarClip(group->mObjectBounds[0], group->mObjectBounds[1]);
2036		return res;
2037	}
2038};
2039
2040class LLOctreeCullShadow : public LLOctreeCull
2041{
2042public:
2043	LLOctreeCullShadow(LLCamera* camera)
2044		: LLOctreeCull(camera) { }
2045
2046	virtual S32 frustumCheck(const LLSpatialGroup* group)
2047	{
2048		return mCamera->AABBInFrustum(group->mBounds[0], group->mBounds[1]);
2049	}
2050
2051	virtual S32 frustumCheckObjects(const LLSpatialGroup* group)
2052	{
2053		return mCamera->AABBInFrustum(group->mObjectBounds[0], group->mObjectBounds[1]);
2054	}
2055};
2056
2057class LLOctreeCullVisExtents: public LLOctreeCullShadow
2058{
2059public:
2060	LLOctreeCullVisExtents(LLCamera* camera, LLVector4a& min, LLVector4a& max)
2061		: LLOctreeCullShadow(camera), mMin(min), mMax(max), mEmpty(TRUE) { }
2062
2063	virtual bool earlyFail(LLSpatialGroup* group)
2064	{
2065		if (group->mOctreeNode->getParent() &&	//never occlusion cull the root node
2066			LLPipeline::sUseOcclusion &&			//ignore occlusion if disabled
2067			group->isOcclusionState(LLSpatialGroup::OCCLUDED))
2068		{
2069			return true;
2070		}
2071		
2072		return false;
2073	}
2074
2075	virtual void traverse(const LLSpatialGroup::OctreeNode* n)
2076	{
2077		LLSpatialGroup* group = (LLSpatialGroup*) n->getListener(0);
2078
2079		if (earlyFail(group))
2080		{
2081			return;
2082		}
2083		
2084		if ((mRes && group->isState(LLSpatialGroup::SKIP_FRUSTUM_CHECK)) ||
2085			mRes == 2)
2086		{	//don't need to do frustum check
2087			LLSpatialGroup::OctreeTraveler::traverse(n);
2088		}
2089		else
2090		{  
2091			mRes = frustumCheck(group);
2092				
2093			if (mRes)
2094			{ //at least partially in, run on down
2095				LLSpatialGroup::OctreeTraveler::traverse(n);
2096			}
2097
2098			mRes = 0;
2099		}
2100	}
2101
2102	virtual void processGroup(LLSpatialGroup* group)
2103	{
2104		llassert(!group->isState(LLSpatialGroup::DIRTY) && !group->getData().empty())
2105		
2106		if (mRes < 2)
2107		{
2108			if (mCamera->AABBInFru

Large files files are truncated, but you can click here to view the full file