diff --git a/doc/LICENSE-logos.txt b/doc/LICENSE-logos.txt
index 53b417cab675e2f06f6cdf5302cc81d76e42085d..0ca7dfd4916eafe03c3a8ae1e747ced82406a49d 100644
--- a/doc/LICENSE-logos.txt
+++ b/doc/LICENSE-logos.txt
@@ -6,7 +6,12 @@ Research, Inc.  Other trademarks include (but are not limited to): the
 names Linden and Linden Research, as well as the Linden Lab Hexagon
 Design and the Second Life Hand Design logos.
 
-Use of logos and trademarks are subject to the Linden Lab trademark
-policy, available at:
-
+The copyrightable materials in this file that are owned by Linden Lab
+are "Work" licensed under Creative Commons license Attribution-Share
+Alike 2.5 (summarized at
+http://creativecommons.org/licenses/by-sa/2.5/ , full text of license
+at http://creativecommons.org/licenses/by-sa/2.5/legalcode); provided
+however that all trademarks of Linden Lab are subject to the Linden
+Lab trademark policy, available at
 http://secondlife.com/corporate/trademark/
+
diff --git a/indra/llaudio/llaudiodecodemgr.cpp b/indra/llaudio/llaudiodecodemgr.cpp
index a2a594f2eb07a499c4bb9ddeac6395780e5ea743..9d8d91a58e59bd695dfb09326e11dc48eada10c5 100644
--- a/indra/llaudio/llaudiodecodemgr.cpp
+++ b/indra/llaudio/llaudiodecodemgr.cpp
@@ -33,9 +33,25 @@ LLAudioDecodeMgr *gAudioDecodeMgrp = NULL;
 
 static const S32 WAV_HEADER_SIZE = 44;
 
-class LLVorbisDecodeState
+
+//////////////////////////////////////////////////////////////////////////////
+
+
+class LLVorbisDecodeState : public LLRefCount
 {
 public:
+	class WriteResponder : public LLLFSThread::Responder
+	{
+	public:
+		WriteResponder(LLVorbisDecodeState* decoder) : mDecoder(decoder) {}
+		~WriteResponder() {}
+		void completed(S32 bytes)
+		{
+			mDecoder->ioComplete(bytes);
+		}
+		LLPointer<LLVorbisDecodeState> mDecoder;
+	};
+	
 	LLVorbisDecodeState(const LLUUID &uuid, const LLString &out_filename);
 	virtual ~LLVorbisDecodeState();
 
@@ -45,12 +61,14 @@ public:
 
 	void flushBadFile();
 
+	void ioComplete(S32 bytes)			{ mBytesRead = bytes; }
 	BOOL isValid() const				{ return mValid; }
 	BOOL isDone() const					{ return mDone; }
 	const LLUUID &getUUID() const		{ return mUUID; }
 protected:
 	BOOL mValid;
 	BOOL mDone;
+	LLAtomicS32 mBytesRead;
 	LLUUID mUUID;
 
 	std::vector<U8> mWAVBuffer;
@@ -64,172 +82,6 @@ protected:
 	S32 mCurrentSection;
 };
 
-void LLVorbisDecodeState::flushBadFile()
-{
-	if (mInFilep)
-	{
-		llwarns << "Flushing bad vorbis file from VFS for " << mUUID << llendl;
-		mInFilep->remove();
-	}
-}
-
-
-LLAudioDecodeMgr::LLAudioDecodeMgr()
-{
-	mCurrentDecodep = NULL;
-}
-
-LLAudioDecodeMgr::~LLAudioDecodeMgr()
-{
-	delete mCurrentDecodep;
-	mCurrentDecodep = NULL;
-}
-
-
-void LLAudioDecodeMgr::processQueue(const F32 num_secs)
-{
-	LLUUID uuid;
-
-	LLTimer decode_timer;
-
-	BOOL done = FALSE;
-	while (!done)
-	{
-		if (mCurrentDecodep)
-		{
-			BOOL res;
-
-			// Decode in a loop until we're done or have run out of time.
-			while(!(res = mCurrentDecodep->decodeSection()) && (decode_timer.getElapsedTimeF32() < num_secs))
-			{
-				// decodeSection does all of the work above
-			}
-
-			if (mCurrentDecodep->isDone() && !mCurrentDecodep->isValid())
-			{
-				// We had an error when decoding, abort.
-				llwarns << mCurrentDecodep->getUUID() << " has invalid vorbis data, aborting decode" << llendl;
-				mCurrentDecodep->flushBadFile();
-				LLAudioData *adp = gAudiop->getAudioData(mCurrentDecodep->getUUID());
-				adp->setHasValidData(FALSE);
-				delete mCurrentDecodep;
-				mCurrentDecodep = NULL;
-				done = TRUE;
-			}
-
-			if (!res)
-			{
-				// We've used up out time slice, bail...
-				done = TRUE;
-			}
-			else if (mCurrentDecodep)
-			{
-				if (mCurrentDecodep->finishDecode())
-				{
-					// We finished!
-					if (mCurrentDecodep->isValid() && mCurrentDecodep->isDone())
-					{
-						LLAudioData *adp = gAudiop->getAudioData(mCurrentDecodep->getUUID());
-						adp->setHasDecodedData(TRUE);
-						adp->setHasValidData(TRUE);
-
-						// At this point, we could see if anyone needs this sound immediately, but
-						// I'm not sure that there's a reason to - we need to poll all of the playing
-						// sounds anyway.
-						//llinfos << "Finished the vorbis decode, now what?" << llendl;
-					}
-					else
-					{
-						llinfos << "Vorbis decode failed!!!" << llendl;
-					}
-					delete mCurrentDecodep;
-					mCurrentDecodep = NULL;
-				}
-				done = TRUE; // done for now
-			}
-		}
-
-		if (!done)
-		{
-			if (!mDecodeQueue.getLength())
-			{
-				// Nothing else on the queue.
-				done = TRUE;
-			}
-			else
-			{
-				LLUUID uuid;
-				mDecodeQueue.pop(uuid);
-				if (gAudiop->hasDecodedFile(uuid))
-				{
-					// This file has already been decoded, don't decode it again.
-					continue;
-				}
-
-				lldebugs << "Decoding " << uuid << " from audio queue!" << llendl;
-
-				char uuid_str[64];			/*Flawfinder: ignore*/
-				char d_path[LL_MAX_PATH];	/*Flawfinder: ignore*/
-
-				LLTimer timer;
-				timer.reset();
-
-				uuid.toString(uuid_str);
-				snprintf(d_path, LL_MAX_PATH, "%s.dsf", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_str).c_str()); /*Flawfinder: ignore*/
-
-				mCurrentDecodep = new LLVorbisDecodeState(uuid, d_path);
-				if (!mCurrentDecodep->initDecode())
-				{
-					delete mCurrentDecodep;
-					mCurrentDecodep = NULL;
-				}
-			}
-		}
-	}
-}
-
-
-BOOL LLAudioDecodeMgr::addDecodeRequest(const LLUUID &uuid)
-{
-	if (gAudiop->hasDecodedFile(uuid))
-	{
-		// Already have a decoded version, don't need to decode it.
-		return TRUE;
-	}
-
-	if (gAssetStorage->hasLocalAsset(uuid, LLAssetType::AT_SOUND))
-	{
-		// Just put it on the decode queue.
-		gAudioDecodeMgrp->mDecodeQueue.push(uuid);
-		return TRUE;
-	}
-
-	return FALSE;
-}
-
-
-S32 LLAudioDecodeMgr::getRequestCount()
-{
-	/*
-	S32 count = 0;
-	if (mCurrentTransfer.notNull())
-	{
-		count++;
-	}
-
-	count += mRequestQueue.getLength();
-	return count;
-	*/
-	return 0;
-}
-
-
-
-
-
-
-
-
 size_t vfs_read(void *ptr, size_t size, size_t nmemb, void *datasource)
 {
 	LLVFile *file = (LLVFile *)datasource;
@@ -284,26 +136,21 @@ int vfs_seek(void *datasource, ogg_int64_t offset, int whence)
 int vfs_close (void *datasource)
 {
 	LLVFile *file = (LLVFile *)datasource;
-
 	delete file;
-
 	return 0;
 }
 
 long vfs_tell (void *datasource)
 {
 	LLVFile *file = (LLVFile *)datasource;
-
 	return file->tell();
 }
 
-
-
-
 LLVorbisDecodeState::LLVorbisDecodeState(const LLUUID &uuid, const LLString &out_filename)
 {
 	mDone = FALSE;
 	mValid = FALSE;
+	mBytesRead = -1;
 	mUUID = uuid;
 	mInFilep = NULL;
 	mCurrentSection = 0;
@@ -575,32 +422,27 @@ BOOL LLVorbisDecodeState::finishDecode()
 			return TRUE; // we've finished
 		}
 #if !defined(USE_WAV_VFILE)
-		mFileHandle = LLLFSThread::sLocal->write(mOutFilename, &mWAVBuffer[0], 0, data_length);
+		mBytesRead = -1;
+		mFileHandle = LLLFSThread::sLocal->write(mOutFilename, &mWAVBuffer[0], 0, data_length,
+												 new WriteResponder(this));
 #endif
 	}
 
 	if (mFileHandle != LLLFSThread::nullHandle())
 	{
-		LLLFSThread::status_t s = LLLFSThread::sLocal->getRequestStatus(mFileHandle);
-		if (s != LLLFSThread::STATUS_COMPLETE)
-		{
-			if (s != LLLFSThread::STATUS_QUEUED && s != LLLFSThread::STATUS_INPROGRESS)
-			{
-				llerrs << "Bad file status in LLVorbisDecodeState::finishDecode: " << s << llendl;
-			}
-			return FALSE; // not finished
-		}
-		else
+		if (mBytesRead >= 0)
 		{
-			LLLFSThread::Request* req = (LLLFSThread::Request*)LLLFSThread::sLocal->getRequest(mFileHandle);
-			if (req->getBytesRead() == 0) //!= req->getBytes() // should be safe, but needs testing
+			if (mBytesRead == 0)
 			{
 				llwarns << "Unable to write file in LLVorbisDecodeState::finishDecode" << llendl;
 				mValid = FALSE;
 				return TRUE; // we've finished
 			}
 		}
-		LLLFSThread::sLocal->completeRequest(mFileHandle);
+		else
+		{
+			return FALSE; // not done
+		}
 	}
 	
 	mDone = TRUE;
@@ -614,3 +456,165 @@ BOOL LLVorbisDecodeState::finishDecode()
 
 	return TRUE;
 }
+
+void LLVorbisDecodeState::flushBadFile()
+{
+	if (mInFilep)
+	{
+		llwarns << "Flushing bad vorbis file from VFS for " << mUUID << llendl;
+		mInFilep->remove();
+	}
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+class LLAudioDecodeMgr::Impl
+{
+	friend class LLAudioDecodeMgr;
+public:
+	Impl() {};
+	~Impl() {};
+
+	void processQueue(const F32 num_secs = 0.005);
+
+protected:
+	LLLinkedQueue<LLUUID> mDecodeQueue;
+	LLPointer<LLVorbisDecodeState> mCurrentDecodep;
+};
+
+
+void LLAudioDecodeMgr::Impl::processQueue(const F32 num_secs)
+{
+	LLUUID uuid;
+
+	LLTimer decode_timer;
+
+	BOOL done = FALSE;
+	while (!done)
+	{
+		if (mCurrentDecodep)
+		{
+			BOOL res;
+
+			// Decode in a loop until we're done or have run out of time.
+			while(!(res = mCurrentDecodep->decodeSection()) && (decode_timer.getElapsedTimeF32() < num_secs))
+			{
+				// decodeSection does all of the work above
+			}
+
+			if (mCurrentDecodep->isDone() && !mCurrentDecodep->isValid())
+			{
+				// We had an error when decoding, abort.
+				llwarns << mCurrentDecodep->getUUID() << " has invalid vorbis data, aborting decode" << llendl;
+				mCurrentDecodep->flushBadFile();
+				LLAudioData *adp = gAudiop->getAudioData(mCurrentDecodep->getUUID());
+				adp->setHasValidData(FALSE);
+				mCurrentDecodep = NULL;
+				done = TRUE;
+			}
+
+			if (!res)
+			{
+				// We've used up out time slice, bail...
+				done = TRUE;
+			}
+			else if (mCurrentDecodep)
+			{
+				if (mCurrentDecodep->finishDecode())
+				{
+					// We finished!
+					if (mCurrentDecodep->isValid() && mCurrentDecodep->isDone())
+					{
+						LLAudioData *adp = gAudiop->getAudioData(mCurrentDecodep->getUUID());
+						adp->setHasDecodedData(TRUE);
+						adp->setHasValidData(TRUE);
+
+						// At this point, we could see if anyone needs this sound immediately, but
+						// I'm not sure that there's a reason to - we need to poll all of the playing
+						// sounds anyway.
+						//llinfos << "Finished the vorbis decode, now what?" << llendl;
+					}
+					else
+					{
+						llinfos << "Vorbis decode failed!!!" << llendl;
+					}
+					mCurrentDecodep = NULL;
+				}
+				done = TRUE; // done for now
+			}
+		}
+
+		if (!done)
+		{
+			if (!mDecodeQueue.getLength())
+			{
+				// Nothing else on the queue.
+				done = TRUE;
+			}
+			else
+			{
+				LLUUID uuid;
+				mDecodeQueue.pop(uuid);
+				if (gAudiop->hasDecodedFile(uuid))
+				{
+					// This file has already been decoded, don't decode it again.
+					continue;
+				}
+
+				lldebugs << "Decoding " << uuid << " from audio queue!" << llendl;
+
+				char uuid_str[64];			/*Flawfinder: ignore*/
+				char d_path[LL_MAX_PATH];	/*Flawfinder: ignore*/
+
+				LLTimer timer;
+				timer.reset();
+
+				uuid.toString(uuid_str);
+				snprintf(d_path, LL_MAX_PATH, "%s.dsf", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_str).c_str()); /*Flawfinder: ignore*/
+
+				mCurrentDecodep = new LLVorbisDecodeState(uuid, d_path);
+				if (!mCurrentDecodep->initDecode())
+				{
+					mCurrentDecodep = NULL;
+				}
+			}
+		}
+	}
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+LLAudioDecodeMgr::LLAudioDecodeMgr()
+{
+	mImpl = new Impl;
+}
+
+LLAudioDecodeMgr::~LLAudioDecodeMgr()
+{
+	delete mImpl;
+}
+
+void LLAudioDecodeMgr::processQueue(const F32 num_secs)
+{
+	mImpl->processQueue(num_secs);
+}
+
+BOOL LLAudioDecodeMgr::addDecodeRequest(const LLUUID &uuid)
+{
+	if (gAudiop->hasDecodedFile(uuid))
+	{
+		// Already have a decoded version, don't need to decode it.
+		return TRUE;
+	}
+
+	if (gAssetStorage->hasLocalAsset(uuid, LLAssetType::AT_SOUND))
+	{
+		// Just put it on the decode queue.
+		mImpl->mDecodeQueue.push(uuid);
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+
diff --git a/indra/llaudio/llaudiodecodemgr.h b/indra/llaudio/llaudiodecodemgr.h
index ea1358b8e9e1543799b1e9e32addb92487adf135..a72d63a228f431927843490008ab57890c2bb02e 100644
--- a/indra/llaudio/llaudiodecodemgr.h
+++ b/indra/llaudio/llaudiodecodemgr.h
@@ -19,7 +19,6 @@
 class LLVFS;
 class LLVorbisDecodeState;
 
-
 class LLAudioDecodeMgr
 {
 public:
@@ -27,19 +26,12 @@ public:
 	~LLAudioDecodeMgr();
 
 	void processQueue(const F32 num_secs = 0.005);
-
-	LLLinkedQueue<LLUUID> mDecodeQueue;
-
-	LLVorbisDecodeState *mCurrentDecodep;
-
 	BOOL addDecodeRequest(const LLUUID &uuid);
 	void addAudioRequest(const LLUUID &uuid);
-
-	S32 getRequestCount();
 	
 protected:
-	LLLinkedQueue<LLUUID> mDownloadQueue;
-	LLFrameTimer mCurrentTransferAge;
+	class Impl;
+	Impl* mImpl;
 };
 
 extern LLAudioDecodeMgr *gAudioDecodeMgrp;
diff --git a/indra/llcharacter/llcharacter.cpp b/indra/llcharacter/llcharacter.cpp
index ecca9a2526d1b7c034b3e398ebe6fa5f585ec913..583f01cf91316cd8cccb15f123ae7c8a8710d8b7 100644
--- a/indra/llcharacter/llcharacter.cpp
+++ b/indra/llcharacter/llcharacter.cpp
@@ -19,13 +19,7 @@
 
 LLStringTable LLCharacter::sVisualParamNames(1024);
 
-// helper functions
-BOOL larger_screen_area( LLCharacter* data_new, LLCharacter* data_tested )
-{
-	return data_new->getPixelArea() > data_tested->getPixelArea();
-}
-
-LLLinkedList< LLCharacter > LLCharacter::sInstances( larger_screen_area );
+std::vector< LLCharacter* > LLCharacter::sInstances;
 
 
 //-----------------------------------------------------------------------------
@@ -40,7 +34,7 @@ LLCharacter::LLCharacter()
 	mSkeletonSerialNum( 0 )
 {
 	mMotionController.setCharacter( this );
-	sInstances.addData(this);
+	sInstances.push_back(this);
 	mPauseRequest = new LLPauseRequestHandle();
 }
 
@@ -57,7 +51,11 @@ LLCharacter::~LLCharacter()
 	{
 		delete param;
 	}
-	sInstances.removeData(this);
+	std::vector<LLCharacter*>::iterator iter = std::find(sInstances.begin(), sInstances.end(), this);
+	if (iter != sInstances.end())
+	{
+		sInstances.erase(iter);
+	}
 }
 
 
@@ -66,7 +64,7 @@ LLCharacter::~LLCharacter()
 //-----------------------------------------------------------------------------
 LLJoint *LLCharacter::getJoint( const std::string &name )
 {
-	LLJoint *joint = NULL;
+	LLJoint* joint = NULL;
 
 	LLJoint *root = getRootJoint();
 	if (root)
@@ -183,7 +181,7 @@ void LLCharacter::flushAllMotions()
 //-----------------------------------------------------------------------------
 // dumpCharacter()
 //-----------------------------------------------------------------------------
-void LLCharacter::dumpCharacter( LLJoint *joint )
+void LLCharacter::dumpCharacter( LLJoint* joint )
 {
 	// handle top level entry into recursion
 	if (joint == NULL)
@@ -198,11 +196,11 @@ void LLCharacter::dumpCharacter( LLJoint *joint )
 	llinfos << "DEBUG: " << joint->getName() << " (" << (joint->getParent()?joint->getParent()->getName():std::string("ROOT")) << ")" << llendl;
 
 	// recurse
-	for (	LLJoint *j = joint->mChildren.getFirstData();
-			j != NULL;
-			j = joint->mChildren.getNextData() )
+	for (LLJoint::child_list_t::iterator iter = joint->mChildren.begin();
+		 iter != joint->mChildren.end(); ++iter)
 	{
-		dumpCharacter(j);
+		LLJoint* child_joint = *iter;
+		dumpCharacter(child_joint);
 	}
 }
 
diff --git a/indra/llcharacter/llcharacter.h b/indra/llcharacter/llcharacter.h
index f2f106f3606ba42911679de87535dc8227273ec7..0ed22f81f7066732ea54017f0c6db479ccb7b0f5 100644
--- a/indra/llcharacter/llcharacter.h
+++ b/indra/llcharacter/llcharacter.h
@@ -21,6 +21,7 @@
 #include "linked_lists.h"
 #include "string_table.h"
 #include "llmemory.h"
+#include "llthread.h"
 
 class LLPolyMesh;
 
@@ -90,7 +91,7 @@ public:
 	virtual F32 getTimeDilation() = 0;
 
 	// gets current pixel area of this character
-	virtual F32 getPixelArea() = 0;
+	virtual F32 getPixelArea() const = 0;
 
 	// gets the head mesh of the character
 	virtual LLPolyMesh*	getHeadMesh() = 0;
@@ -223,7 +224,7 @@ public:
 	U32				getSkeletonSerialNum() const		{ return mSkeletonSerialNum; }
 	void			setSkeletonSerialNum( U32 num )	{ mSkeletonSerialNum = num; }
 
-	static LLLinkedList< LLCharacter > sInstances;
+	static std::vector< LLCharacter* > sInstances;
 
 protected:
 	LLMotionController	mMotionController;
diff --git a/indra/llcharacter/lljoint.cpp b/indra/llcharacter/lljoint.cpp
index 3924c06adc75145649345f81cf89b1b5d5819172..3797b06aa16d5b5866b9077cd85948f8753001ba 100644
--- a/indra/llcharacter/lljoint.cpp
+++ b/indra/llcharacter/lljoint.cpp
@@ -50,8 +50,9 @@ LLJoint::LLJoint(const std::string &name, LLJoint *parent)
 
 	setName(name);
 	if (parent)
+	{
 		parent->addChild( this );
-
+	}
 	touch();
 }
 
@@ -61,6 +62,10 @@ LLJoint::LLJoint(const std::string &name, LLJoint *parent)
 //-----------------------------------------------------------------------------
 LLJoint::~LLJoint()
 {
+	if (mParent)
+	{
+		mParent->removeChild( this );
+	}
 	removeAllChildren();
 }
 
@@ -72,7 +77,9 @@ void LLJoint::setup(const std::string &name, LLJoint *parent)
 {
 	setName(name);
 	if (parent)
+	{
 		parent->addChild( this );
+	}
 }
 
 //-----------------------------------------------------------------------------
@@ -90,11 +97,11 @@ void LLJoint::touch(U32 flags)
 		{
 			child_flags |= POSITION_DIRTY;
 		}
-		
-		for (	LLJoint *joint = mChildren.getFirstData();
-				joint != NULL;
-				joint = mChildren.getNextData() )
+
+		for (child_list_t::iterator iter = mChildren.begin();
+			 iter != mChildren.end(); ++iter)
 		{
+			LLJoint* joint = *iter;
 			joint->touch(child_flags);
 		}
 	}
@@ -121,13 +128,15 @@ LLJoint *LLJoint::findJoint( const std::string &name )
 	if (name == getName())
 		return this;
 
-	for (	LLJoint *j = mChildren.getFirstData();
-			j != NULL;
-			j = mChildren.getNextData() )
+	for (child_list_t::iterator iter = mChildren.begin();
+		 iter != mChildren.end(); ++iter)
 	{
-		LLJoint *found = j->findJoint(name);
+		LLJoint* joint = *iter;
+		LLJoint *found = joint->findJoint(name);
 		if (found)
+		{
 			return found;
+		}
 	}
 
 	return NULL;	
@@ -137,12 +146,12 @@ LLJoint *LLJoint::findJoint( const std::string &name )
 //--------------------------------------------------------------------
 // addChild()
 //--------------------------------------------------------------------
-void LLJoint::addChild(LLJoint *joint)
+void LLJoint::addChild(LLJoint* joint)
 {
 	if (joint->mParent)
 		joint->mParent->removeChild(joint);
 
-	mChildren.addDataAtEnd(joint);
+	mChildren.push_back(joint);
 	joint->mXform.setParent(&mXform);
 	joint->mParent = this;	
 	joint->touch();
@@ -152,9 +161,13 @@ void LLJoint::addChild(LLJoint *joint)
 //--------------------------------------------------------------------
 // removeChild()
 //--------------------------------------------------------------------
-void LLJoint::removeChild(LLJoint *joint)
+void LLJoint::removeChild(LLJoint* joint)
 {
-	this->mChildren.removeData(joint);
+	child_list_t::iterator iter = std::find(mChildren.begin(), mChildren.end(), joint);
+	if (iter != mChildren.end())
+	{
+		this->mChildren.erase(iter);
+	}
 	joint->mXform.setParent(NULL);
 	joint->mParent = NULL;
 	joint->touch();
@@ -166,11 +179,15 @@ void LLJoint::removeChild(LLJoint *joint)
 //--------------------------------------------------------------------
 void LLJoint::removeAllChildren()
 {
-	for (	LLJoint *joint = mChildren.getFirstData();
-			joint != NULL;
-			joint = mChildren.getNextData() )
+	for (child_list_t::iterator iter = mChildren.begin();
+		 iter != mChildren.end();)
 	{
-		removeChild(joint);
+		child_list_t::iterator curiter = iter++;
+		LLJoint* joint = *curiter;
+		mChildren.erase(curiter);
+		joint->mXform.setParent(NULL);
+		joint->mParent = NULL;
+		joint->touch();
 	}
 }
 
@@ -189,8 +206,11 @@ const LLVector3& LLJoint::getPosition()
 //--------------------------------------------------------------------
 void LLJoint::setPosition( const LLVector3& pos )
 {
-	mXform.setPosition(pos);
-	touch(MATRIX_DIRTY | POSITION_DIRTY);
+//	if (mXform.getPosition() != pos)
+	{
+		mXform.setPosition(pos);
+		touch(MATRIX_DIRTY | POSITION_DIRTY);
+	}
 }
 
 
@@ -257,8 +277,11 @@ void LLJoint::setRotation( const LLQuaternion& rot )
 {
 	if (rot.isFinite())
 	{
-		mXform.setRotation(rot);
-		touch(MATRIX_DIRTY | ROTATION_DIRTY);
+	//	if (mXform.getRotation() != rot)
+		{
+			mXform.setRotation(rot);
+			touch(MATRIX_DIRTY | ROTATION_DIRTY);
+		}
 	}
 }
 
@@ -320,8 +343,12 @@ const LLVector3& LLJoint::getScale()
 //--------------------------------------------------------------------
 void LLJoint::setScale( const LLVector3& scale )
 {
-	mXform.setScale(scale);
-	touch();
+//	if (mXform.getScale() != scale)
+	{
+		mXform.setScale(scale);
+		touch();
+	}
+
 }
 
 
@@ -393,14 +420,18 @@ void LLJoint::updateWorldPRSParent()
 // updateWorldMatrixChildren()
 //-----------------------------------------------------------------------------
 void LLJoint::updateWorldMatrixChildren()
-{
+{	
+	if (!this->mUpdateXform) return;
+
 	if (mDirtyFlags & MATRIX_DIRTY)
 	{
 		updateWorldMatrix();
 	}
-	for (LLJoint *child = mChildren.getFirstData(); child; child = mChildren.getNextData())
+	for (child_list_t::iterator iter = mChildren.begin();
+		 iter != mChildren.end(); ++iter)
 	{
-		child->updateWorldMatrixChildren();
+		LLJoint* joint = *iter;
+		joint->updateWorldMatrixChildren();
 	}
 }
 
@@ -475,8 +506,10 @@ void LLJoint::clampRotation(LLQuaternion old_rot, LLQuaternion new_rot)
 {
 	LLVector3 main_axis(1.f, 0.f, 0.f);
 
-	for (LLJoint* joint = mChildren.getFirstData(); joint; joint = mChildren.getNextData())
+	for (child_list_t::iterator iter = mChildren.begin();
+		 iter != mChildren.end(); ++iter)
 	{
+		LLJoint* joint = *iter;
 		if (joint->isAnimatable())
 		{
 			main_axis = joint->getPosition();
diff --git a/indra/llcharacter/lljoint.h b/indra/llcharacter/lljoint.h
index 2fc86e87df37d67bc5b7a02899d7062bf00f9732..6399d0a429a7c70afb09a13271890bba9a0c74ac 100644
--- a/indra/llcharacter/lljoint.h
+++ b/indra/llcharacter/lljoint.h
@@ -76,7 +76,8 @@ public:
 	LLDynamicArray<LLVector3> mConstraintSilhouette;
 
 	// child joints
-	LLLinkedList<LLJoint> mChildren;
+	typedef std::list<LLJoint*> child_list_t;
+	child_list_t mChildren;
 
 	// debug statics
 	static S32		sNumTouches;
diff --git a/indra/llcharacter/llkeyframemotion.cpp b/indra/llcharacter/llkeyframemotion.cpp
index bfa4b637e1761755f0950ded09269a9f044aa37b..ea77126aca174568f1b5e91e79cefb6b44ad081f 100644
--- a/indra/llcharacter/llkeyframemotion.cpp
+++ b/indra/llcharacter/llkeyframemotion.cpp
@@ -1892,28 +1892,26 @@ void LLKeyframeMotion::onLoadComplete(LLVFS *vfs,
 									   void* user_data, S32 status)
 {
 	LLUUID* id = (LLUUID*)user_data;
+		
+	std::vector<LLCharacter* >::iterator char_iter = LLCharacter::sInstances.begin();
 
-	LLCharacter* character = NULL;
+	while(char_iter != LLCharacter::sInstances.end() &&
+			(*char_iter)->getID() != *id)
+	{
+		++char_iter;
+	}
 	
-	for(character = LLCharacter::sInstances.getFirstData();
-		character;
-		character = LLCharacter::sInstances.getNextData())
-		{
-			if (character->getID() == *id)
-			{
-				break;
-			}
-		}
-
 	delete id;
 
-	if (!character)
+	if (char_iter == LLCharacter::sInstances.end())
 	{
 		return;
 	}
 
+	LLCharacter* character = *char_iter;
+
 	// create an instance of this motion (it may or may not already exist)
-	LLKeyframeMotion* motionp = (LLKeyframeMotion*)character->createMotion(asset_uuid);
+	LLKeyframeMotion* motionp = (LLKeyframeMotion*) character->createMotion(asset_uuid);
 
 	if (0 == status && motionp)
 	{
diff --git a/indra/llcommon/imageids.h b/indra/llcommon/imageids.h
index 8147183bec7dd1228af17aba5dcd565568105251..9f57ec84c28c74d15cc87e72ef83070dfa424ad0 100644
--- a/indra/llcommon/imageids.h
+++ b/indra/llcommon/imageids.h
@@ -31,7 +31,7 @@
 const LLUUID IMG_CLEAR			("11ee27f5-43c0-414e-afd5-d7f5688c351f");  // VIEWER
 const LLUUID IMG_SMOKE			("b4ba225c-373f-446d-9f7e-6cb7b5cf9b3d");  // VIEWER
 
-const LLUUID IMG_DEFAULT		("b4ba225c-373f-446d-9f7e-6cb7b5cf9b3d");  // VIEWER
+const LLUUID IMG_DEFAULT		("d2114404-dd59-4a4d-8e6c-49359e91bbf0");  // VIEWER
 
 //const LLUUID IMG_SAND			("0ff70ead-4562-45f9-9e8a-52b1a3286868"); // VIEWER 1.5k
 //const LLUUID IMG_GRASS			("5ab48dd5-05d0-4f1a-ace6-efd4e2fb3508"); // VIEWER 1.2k
diff --git a/indra/llcommon/llapr.cpp b/indra/llcommon/llapr.cpp
index a7fc6a40a7145946c57c702a8248159336f8e5bc..bf6a4d1b2177fa9140f9e4086d79f9790f4969ea 100644
--- a/indra/llcommon/llapr.cpp
+++ b/indra/llcommon/llapr.cpp
@@ -103,11 +103,13 @@ void ll_apr_assert_status(apr_status_t status)
 	llassert(ll_apr_warn_status(status) == false);
 }
 
-apr_file_t* ll_apr_file_open(const LLString& filename, apr_int32_t flags, S32* sizep)
+// File I/O
+apr_file_t* ll_apr_file_open(const LLString& filename, apr_int32_t flags, S32* sizep, apr_pool_t* pool)
 {
 	apr_file_t* apr_file;
 	apr_status_t s;
-	s = apr_file_open(&apr_file, filename.c_str(), flags, APR_OS_DEFAULT, gAPRPoolp);
+	if (pool == NULL) pool = gAPRPoolp;
+	s = apr_file_open(&apr_file, filename.c_str(), flags, APR_OS_DEFAULT, pool);
 	if (s != APR_SUCCESS)
 	{
 		if (sizep)
@@ -123,6 +125,7 @@ apr_file_t* ll_apr_file_open(const LLString& filename, apr_int32_t flags, S32* s
 		apr_off_t offset = 0;
 		if (apr_file_seek(apr_file, APR_END, &offset) == APR_SUCCESS)
 		{
+			llassert_always(offset <= 0x7fffffff);
 			file_size = (S32)offset;
 			offset = 0;
 			apr_file_seek(apr_file, APR_SET, &offset);
@@ -132,6 +135,18 @@ apr_file_t* ll_apr_file_open(const LLString& filename, apr_int32_t flags, S32* s
 
 	return apr_file;
 }
+apr_file_t* ll_apr_file_open(const LLString& filename, apr_int32_t flags, S32* sizep)
+{
+	return ll_apr_file_open(filename, flags, sizep, NULL);
+}
+apr_file_t* ll_apr_file_open(const LLString& filename, apr_int32_t flags, apr_pool_t* pool)
+{
+	return ll_apr_file_open(filename, flags, NULL, pool);
+}
+apr_file_t* ll_apr_file_open(const LLString& filename, apr_int32_t flags)
+{
+	return ll_apr_file_open(filename, flags, NULL, NULL);
+}
 
 S32 ll_apr_file_read(apr_file_t* apr_file, void *buf, S32 nbytes)
 {
@@ -143,10 +158,37 @@ S32 ll_apr_file_read(apr_file_t* apr_file, void *buf, S32 nbytes)
 	}
 	else
 	{
+		llassert_always(sz <= 0x7fffffff);
 		return (S32)sz;
 	}
 }
 
+S32 ll_apr_file_read_ex(const LLString& filename, apr_pool_t* pool, void *buf, S32 offset, S32 nbytes)
+{
+	if (pool == NULL) pool = gAPRPoolp;
+	apr_file_t* filep = ll_apr_file_open(filename, APR_READ|APR_BINARY, pool);
+	if (!filep)
+	{
+		return 0;
+	}
+	S32 off;
+	if (offset < 0)
+		off = ll_apr_file_seek(filep, APR_END, 0);
+	else
+		off = ll_apr_file_seek(filep, APR_SET, offset);
+	S32 bytes_read;
+	if (off < 0)
+	{
+		bytes_read = 0;
+	}
+	else
+	{
+		bytes_read = ll_apr_file_read(filep, buf, nbytes );
+	}
+	apr_file_close(filep);
+
+	return bytes_read;
+}
 
 S32 ll_apr_file_write(apr_file_t* apr_file, const void *buf, S32 nbytes)
 {
@@ -158,28 +200,73 @@ S32 ll_apr_file_write(apr_file_t* apr_file, const void *buf, S32 nbytes)
 	}
 	else
 	{
+		llassert_always(sz <= 0x7fffffff);
 		return (S32)sz;
 	}
 }
 
+S32 ll_apr_file_write_ex(const LLString& filename, apr_pool_t* pool, void *buf, S32 offset, S32 nbytes)
+{
+	if (pool == NULL) pool = gAPRPoolp;
+	apr_int32_t flags = APR_CREATE|APR_WRITE|APR_BINARY;
+	if (offset < 0)
+	{
+		flags |= APR_APPEND;
+		offset = 0;
+	}
+	apr_file_t* filep = ll_apr_file_open(filename, flags, pool);
+	if (!filep)
+	{
+		return 0;
+	}
+	if (offset > 0)
+	{
+		offset = ll_apr_file_seek(filep, APR_SET, offset);
+	}
+	S32 bytes_written;
+	if (offset < 0)
+	{
+		bytes_written = 0;
+	}
+	else
+	{
+		bytes_written = ll_apr_file_write(filep, buf, nbytes );
+	}
+	apr_file_close(filep);
+
+	return bytes_written;
+}
+
 S32 ll_apr_file_seek(apr_file_t* apr_file, apr_seek_where_t where, S32 offset)
 {
-	apr_off_t apr_offset = offset;
-	apr_status_t s = apr_file_seek(apr_file, where, &apr_offset);
+	apr_status_t s;
+	apr_off_t apr_offset;
+	if (offset >= 0)
+	{
+		apr_offset = (apr_off_t)offset;
+		s = apr_file_seek(apr_file, where, &apr_offset);
+	}
+	else
+	{
+		apr_offset = 0;
+		s = apr_file_seek(apr_file, APR_END, &apr_offset);
+	}
 	if (s != APR_SUCCESS)
 	{
 		return -1;
 	}
 	else
 	{
+		llassert_always(apr_offset <= 0x7fffffff);
 		return (S32)apr_offset;
 	}
 }
 
-bool ll_apr_file_remove(const LLString& filename)
+bool ll_apr_file_remove(const LLString& filename, apr_pool_t* pool)
 {
 	apr_status_t s;
-	s = apr_file_remove(filename.c_str(), gAPRPoolp);
+	if (pool == NULL) pool = gAPRPoolp;
+	s = apr_file_remove(filename.c_str(), pool);
 	if (s != APR_SUCCESS)
 	{
 		llwarns << "ll_apr_file_remove failed on file: " << filename << llendl;
@@ -188,10 +275,11 @@ bool ll_apr_file_remove(const LLString& filename)
 	return true;
 }
 
-bool ll_apr_file_rename(const LLString& filename, const LLString& newname)
+bool ll_apr_file_rename(const LLString& filename, const LLString& newname, apr_pool_t* pool)
 {
 	apr_status_t s;
-	s = apr_file_rename(filename.c_str(), newname.c_str(), gAPRPoolp);
+	if (pool == NULL) pool = gAPRPoolp;
+	s = apr_file_rename(filename.c_str(), newname.c_str(), pool);
 	if (s != APR_SUCCESS)
 	{
 		llwarns << "ll_apr_file_rename failed on file: " << filename << llendl;
@@ -199,3 +287,73 @@ bool ll_apr_file_rename(const LLString& filename, const LLString& newname)
 	}
 	return true;
 }
+
+bool ll_apr_file_exists(const LLString& filename, apr_pool_t* pool)
+{
+	apr_file_t* apr_file;
+	apr_status_t s;
+	if (pool == NULL) pool = gAPRPoolp;
+	s = apr_file_open(&apr_file, filename.c_str(), APR_READ, APR_OS_DEFAULT, pool);
+	if (s != APR_SUCCESS || !apr_file)
+	{
+		return false;
+	}
+	else
+	{
+		apr_file_close(apr_file);
+		return true;
+	}
+}
+
+S32 ll_apr_file_size(const LLString& filename, apr_pool_t* pool)
+{
+	apr_file_t* apr_file;
+	apr_finfo_t info;
+	apr_status_t s;
+	if (pool == NULL) pool = gAPRPoolp;
+	s = apr_file_open(&apr_file, filename.c_str(), APR_READ, APR_OS_DEFAULT, pool);
+	if (s != APR_SUCCESS || !apr_file)
+	{
+		return 0;
+	}
+	else
+	{
+		apr_status_t s = apr_file_info_get(&info, APR_FINFO_SIZE, apr_file);
+		apr_file_close(apr_file);
+		if (s == APR_SUCCESS)
+		{
+			return (S32)info.size;
+		}
+		else
+		{
+			return 0;
+		}
+	}
+}
+
+bool ll_apr_dir_make(const LLString& dirname, apr_pool_t* pool)
+{
+	apr_status_t s;
+	if (pool == NULL) pool = gAPRPoolp;
+	s = apr_dir_make(dirname.c_str(), APR_FPROT_OS_DEFAULT, pool);
+	if (s != APR_SUCCESS)
+	{
+		llwarns << "ll_apr_file_remove failed on file: " << dirname << llendl;
+		return false;
+	}
+	return true;
+}
+
+bool ll_apr_dir_remove(const LLString& dirname, apr_pool_t* pool)
+{
+	apr_status_t s;
+	if (pool == NULL) pool = gAPRPoolp;
+	s = apr_file_remove(dirname.c_str(), pool);
+	if (s != APR_SUCCESS)
+	{
+		llwarns << "ll_apr_file_remove failed on file: " << dirname << llendl;
+		return false;
+	}
+	return true;
+}
+
diff --git a/indra/llcommon/llapr.h b/indra/llcommon/llapr.h
index 1e9e944eefb2116360d0c00ab0b89ea7371c0196..e89912279ec88d6e526da71d2d8a10cc52176836 100644
--- a/indra/llcommon/llapr.h
+++ b/indra/llcommon/llapr.h
@@ -106,14 +106,24 @@ typedef LLAtomic32<S32> LLAtomicS32;
 #define LL_APR_WB (APR_CREATE|APR_TRUNCATE|APR_WRITE|APR_BINARY) // "wb"
 #define LL_APR_RPB (APR_READ|APR_WRITE|APR_BINARY) // "r+b"
 #define LL_APR_WPB (APR_CREATE|APR_TRUNCATE|APR_READ|APR_WRITE|APR_BINARY) // "w+b"
-apr_file_t* ll_apr_file_open(const LLString& filename, apr_int32_t flags, S32* sizep = NULL);
+apr_file_t* ll_apr_file_open(const LLString& filename, apr_int32_t flags, S32* sizep, apr_pool_t* pool);
+apr_file_t* ll_apr_file_open(const LLString& filename, apr_int32_t flags, S32* sizep);
+apr_file_t* ll_apr_file_open(const LLString& filename, apr_int32_t flags, apr_pool_t* pool);
+apr_file_t* ll_apr_file_open(const LLString& filename, apr_int32_t flags);
 // Returns actual offset, -1 if seek fails
 S32 ll_apr_file_seek(apr_file_t* apr_file, apr_seek_where_t where, S32 offset);
-// Returns bytes read/written, 0 if read/write fails
+// Returns bytes read/written, 0 if read/write fails:
 S32 ll_apr_file_read(apr_file_t* apr_file, void* buf, S32 nbytes);
+S32 ll_apr_file_read_ex(const LLString& filename, apr_pool_t* pool, void *buf, S32 offset, S32 nbytes);
 S32 ll_apr_file_write(apr_file_t* apr_file, const void* buf, S32 nbytes);
-bool ll_apr_file_remove(const LLString& filename);
-bool ll_apr_file_rename(const LLString& filename, const LLString& newname);
+S32 ll_apr_file_write_ex(const LLString& filename, apr_pool_t* pool, void *buf, S32 offset, S32 nbytes);
+// returns false if failure:
+bool ll_apr_file_remove(const LLString& filename, apr_pool_t* pool = NULL);
+bool ll_apr_file_rename(const LLString& filename, const LLString& newname, apr_pool_t* pool = NULL);
+bool ll_apr_file_exists(const LLString& filename, apr_pool_t* pool = NULL);
+S32 ll_apr_file_size(const LLString& filename, apr_pool_t* pool = NULL);
+bool ll_apr_dir_make(const LLString& dirname, apr_pool_t* pool = NULL);
+bool ll_apr_dir_remove(const LLString& dirname, apr_pool_t* pool = NULL);
 
 /**
  * @brief Function which approprately logs error or remains quiet on
diff --git a/indra/llcommon/llcommon.cpp b/indra/llcommon/llcommon.cpp
index ff810abfa9efafb3387a4d76ad25a2f76a0073a5..52fcaa3c193115d30554cfaab9425a729b9c2def 100644
--- a/indra/llcommon/llcommon.cpp
+++ b/indra/llcommon/llcommon.cpp
@@ -8,6 +8,7 @@
 #include "linden_common.h"
 
 #include "llcommon.h"
+#include "llthread.h"
 
 //static
 BOOL LLCommon::sAprInitialized = FALSE;
diff --git a/indra/llcommon/llcommon.h b/indra/llcommon/llcommon.h
index c37d479ba9da7a7df53c1e99780086dd632ce160..e7fb5b84e4732bafed25b3477ab7f9df65d797ee 100644
--- a/indra/llcommon/llcommon.h
+++ b/indra/llcommon/llcommon.h
@@ -12,7 +12,6 @@
 #include "llapr.h"
 // #include "llframecallbackmanager.h"
 #include "lltimer.h"
-#include "llworkerthread.h"
 #include "llfile.h"
 
 class LLCommon
diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp
index 1ed25a0d176b749e31bd1d8289b5e56d83f3c9fc..bbff363f54c593b7dfda870e311fe881c1a1dc2a 100644
--- a/indra/llcommon/llerror.cpp
+++ b/indra/llcommon/llerror.cpp
@@ -12,6 +12,7 @@
 #include "llerror.h"
 #include "llerrorcontrol.h"
 
+#include "llapp.h"
 #include "llapr.h"
 extern apr_thread_mutex_t *gLogMutexp;
 #include "llfile.h"
diff --git a/indra/llcommon/llevent.h b/indra/llcommon/llevent.h
index 283f40f923dd67934355d51a43010fa005503499..a3f6b0e942702932b09b8de8afa7e9b1036d7af3 100644
--- a/indra/llcommon/llevent.h
+++ b/indra/llcommon/llevent.h
@@ -12,6 +12,7 @@
 
 #include "llsd.h"
 #include "llmemory.h"
+#include "llthread.h"
 
 class LLEventListener;
 class LLEvent;
diff --git a/indra/llcommon/llfasttimer.h b/indra/llcommon/llfasttimer.h
index 2fde728eaf244003b371789795b2e02cdcf18d95..1a7e60656cf5ec2baae01a2dab5cf469a2eebddd 100644
--- a/indra/llcommon/llfasttimer.h
+++ b/indra/llcommon/llfasttimer.h
@@ -37,13 +37,22 @@ public:
 		FTM_UPDATE_TERRAIN,
 		FTM_UPDATE_PRIMITIVES,
 		FTM_UPDATE_PARTICLES,
+		FTM_SIMULATE_PARTICLES,
 		FTM_UPDATE_SKY,
 		FTM_UPDATE_TEXTURES,
+		FTM_UPDATE_WATER,
+		FTM_UPDATE_CLOUDS,
+		FTM_UPDATE_GRASS,
+		FTM_UPDATE_TREE,
+		FTM_UPDATE_AVATAR,
 		
 		// common render components
 		FTM_RENDER_GEOMETRY,
 		 FTM_RENDER_TERRAIN,
 		 FTM_RENDER_SIMPLE,
+		 FTM_RENDER_FULLBRIGHT,
+		 FTM_RENDER_GRASS,
+		 FTM_RENDER_INVISIBLE,
 		 FTM_RENDER_SHINY,
 		 FTM_RENDER_BUMP,
 		 FTM_RENDER_TREES,
@@ -62,6 +71,20 @@ public:
 		FTM_MESSAGES,
 		FTM_REBUILD,
 		FTM_STATESORT,
+		FTM_STATESORT_DRAWABLE,
+		FTM_STATESORT_POSTSORT,
+		FTM_REBUILD_VBO,
+		FTM_REBUILD_VOLUME_VB,
+		FTM_REBUILD_BRIDGE_VB,
+		FTM_REBUILD_HUD_VB,
+		FTM_REBUILD_TERRAIN_VB,
+		FTM_REBUILD_WATER_VB,
+		FTM_REBUILD_TREE_VB,
+		FTM_REBUILD_PARTICLE_VB,
+		FTM_REBUILD_CLOUD_VB,
+		FTM_REBUILD_GRASS_VB,
+		FTM_REBUILD_NONE_VB,
+		FTM_REBUILD_OCCLUSION_VB,
 		FTM_POOLS,
 		FTM_POOLRENDER,
 		FTM_IDLE_CB,
@@ -71,6 +94,7 @@ public:
 		FTM_UPDATE_LIGHTS,
 		FTM_CULL,
 		FTM_CULL_REBOUND,
+		FTM_FRUSTUM_CULL,
 		FTM_GEO_UPDATE,
 		FTM_GEO_RESERVE,
 		FTM_GEO_LIGHT,
@@ -97,6 +121,7 @@ public:
 		FTM_IMAGE_UPDATE,
 		FTM_IMAGE_CREATE,
 		FTM_IMAGE_DECODE,
+		FTM_IMAGE_MARK_DIRTY,
 		FTM_PIPELINE,
 		FTM_VFILE_WAIT,
 		FTM_FLEXIBLE_UPDATE,
diff --git a/indra/llcommon/llfile.cpp b/indra/llcommon/llfile.cpp
index 4acd94f9431d3157ff965dc911e7e097f22bc209..f43e57f46792732c9bb6df746b43c63756a4d0e6 100644
--- a/indra/llcommon/llfile.cpp
+++ b/indra/llcommon/llfile.cpp
@@ -28,6 +28,19 @@ int	LLFile::mkdir(const	char* dirname, int perms)
 #endif
 }
 
+// static
+int	LLFile::rmdir(const	char* dirname)
+{
+#if LL_WINDOWS	
+	// permissions are ignored on Windows
+	std::string utf8dirname = dirname;
+	llutf16string utf16dirname = utf8str_to_utf16str(utf8dirname);
+	return _wrmdir(utf16dirname.c_str());
+#else
+	return ::rmdir(dirname);
+#endif
+}
+
 // static
 LLFILE*	LLFile::fopen(const	char* filename, const char* mode)	/* Flawfinder: ignore */
 {
diff --git a/indra/llcommon/llfile.h b/indra/llcommon/llfile.h
index 2899f51a600ea07cb497ce2ddd70d4f624e4c4bd..605918cd29d2b65334b1392caba7e3ba73a07108 100644
--- a/indra/llcommon/llfile.h
+++ b/indra/llcommon/llfile.h
@@ -50,6 +50,7 @@ public:
 	// be overridden by the user's umask.  It is ignored on Windows.
 	static	int		mkdir(const char* filename, int perms = 0700);
 
+	static	int		rmdir(const char* filename);
 	static	int		remove(const char* filename);
 	static	int		rename(const char* filename,const char*	newname);
 	static	int		stat(const char*	filename,llstat*	file_status);
diff --git a/indra/llcommon/llmemory.cpp b/indra/llcommon/llmemory.cpp
index 0a21c3a2a720c16f46274632dad0031a4b83c9c0..1063de5a8cb4e7548054a1c1aa5c879c6392e874 100644
--- a/indra/llcommon/llmemory.cpp
+++ b/indra/llcommon/llmemory.cpp
@@ -9,6 +9,7 @@
 #include "linden_common.h"
 
 #include "llmemory.h"
+#include "llmemtype.h"
 
 // not defining nullfunc will currently crash when trying to use a LLHandle
 template< typename _Ty >
@@ -244,43 +245,6 @@ void operator delete[] (void *p)
 
 //----------------------------------------------------------------------------
 
-//static
-LLMutex* LLThreadSafeRefCount::sMutex = 0;
-
-//static
-void LLThreadSafeRefCount::initClass()
-{
-	if (!sMutex)
-	{
-		sMutex = new LLMutex(0);
-	}
-}
-
-//static
-void LLThreadSafeRefCount::cleanupClass()
-{
-	delete sMutex;
-	sMutex = NULL;
-}
-	
-
-//----------------------------------------------------------------------------
-
-LLThreadSafeRefCount::LLThreadSafeRefCount() :
-	mRef(0)
-{
-}
-
-LLThreadSafeRefCount::~LLThreadSafeRefCount()
-{ 
-	if (mRef != 0)
-	{
-		llerrs << "deleting non-zero reference" << llendl;
-	}
-}
-
-//----------------------------------------------------------------------------
-
 LLRefCount::LLRefCount() :
 	mRef(0)
 {
diff --git a/indra/llcommon/llmemory.h b/indra/llcommon/llmemory.h
index 962a4aa5d505941e526262ac0b6feb4ffc78f88d..d543d023ffc52ac7fc6dbd3bf2adb3cfe47a66b1 100644
--- a/indra/llcommon/llmemory.h
+++ b/indra/llcommon/llmemory.h
@@ -12,8 +12,6 @@
 #include <cstdlib>
 
 #include "llerror.h"
-#include "llthread.h"
-#include "llmemtype.h"
 
 extern S32 gTotalDAlloc;
 extern S32 gTotalDAUse;
@@ -42,53 +40,7 @@ private:
 //   LLPointer<LLFoo> x = new LLFoo; // constructor does not do anything interesting
 //   x->instantiate(); // does stuff like place x into an update queue
 
-class LLThreadSafeRefCount
-{
-public:
-	static void initClass(); // creates sMutex
-	static void cleanupClass(); // destroys sMutex
-	
-private:
-	static LLMutex* sMutex;
-
-private:
-	LLThreadSafeRefCount(const LLThreadSafeRefCount&); // not implemented
-	LLThreadSafeRefCount&operator=(const LLThreadSafeRefCount&); // not implemented
-
-protected:
-	virtual ~LLThreadSafeRefCount(); // use unref()
-	
-public:
-	LLThreadSafeRefCount();
-	
-	void ref()
-	{
-		if (sMutex) sMutex->lock();
-		mRef++; 
-		if (sMutex) sMutex->unlock();
-	} 
-
-	S32 unref()
-	{
-		llassert(mRef >= 1);
-		if (sMutex) sMutex->lock();
-		S32 res = --mRef;
-		if (sMutex) sMutex->unlock();
-		if (0 == res) 
-		{
-			delete this; 
-			res = 0;
-		}
-		return res;
-	}	
-	S32 getNumRefs() const
-	{
-		return mRef;
-	}
-
-private: 
-	S32	mRef; 
-};
+// see llthread.h for LLThreadSafeRefCount
 
 //----------------------------------------------------------------------------
 
@@ -132,6 +84,7 @@ private:
 
 //----------------------------------------------------------------------------
 
+// Note: relies on Type having ref() and unref() methods
 template <class Type> class LLPointer
 {
 public:
diff --git a/indra/llcommon/llmemtype.h b/indra/llcommon/llmemtype.h
index 17afaa6a8a18cd1862859332886570464e5b0edc..53f7f66285e0170b978e32d1083836def9a8afd6 100644
--- a/indra/llcommon/llmemtype.h
+++ b/indra/llcommon/llmemtype.h
@@ -52,6 +52,7 @@ public:
 		
 		MTYPE_DRAWABLE,
 		MTYPE_OBJECT,
+		MTYPE_VERTEX_DATA,
 		MTYPE_SPACE_PARTITION,
 		MTYPE_PIPELINE,
 		MTYPE_AVATAR,
diff --git a/indra/llcommon/llqueuedthread.cpp b/indra/llcommon/llqueuedthread.cpp
index bc41927d38ccb43b73d882b797affb4802f12cce..e6ac9ac11d299ad6ac62cf3490eb76c99e5d9228 100644
--- a/indra/llcommon/llqueuedthread.cpp
+++ b/indra/llcommon/llqueuedthread.cpp
@@ -12,10 +12,9 @@
 //============================================================================
 
 // MAIN THREAD
-LLQueuedThread::LLQueuedThread(const std::string& name, bool threaded, bool runalways) :
+LLQueuedThread::LLQueuedThread(const std::string& name, bool threaded) :
 	LLThread(name),
 	mThreaded(threaded),
-	mRunAlways(runalways),
 	mIdleThread(TRUE),
 	mNextHandle(0)
 {
@@ -27,6 +26,12 @@ LLQueuedThread::LLQueuedThread(const std::string& name, bool threaded, bool runa
 
 // MAIN THREAD
 LLQueuedThread::~LLQueuedThread()
+{
+	shutdown();
+	// ~LLThread() will be called here
+}
+
+void LLQueuedThread::shutdown()
 {
 	setQuitting();
 
@@ -54,61 +59,69 @@ LLQueuedThread::~LLQueuedThread()
 	}
 
 	QueuedRequest* req;
+	S32 active_count = 0;
 	while ( (req = (QueuedRequest*)mRequestHash.pop_element()) )
 	{
+		if (req->getStatus() == STATUS_QUEUED || req->getStatus() == STATUS_INPROGRESS)
+		{
+			++active_count;
+		}
 		req->deleteRequest();
 	}
-	
-	// ~LLThread() will be called here
+	if (active_count)
+	{
+		llwarns << "~LLQueuedThread() called with active requests: " << active_count << llendl;
+	}
 }
 
 //----------------------------------------------------------------------------
 
 // MAIN THREAD
-void LLQueuedThread::update(U32 ms_elapsed)
+// virtual
+S32 LLQueuedThread::update(U32 max_time_ms)
 {
-	updateQueue(0);
+	return updateQueue(max_time_ms);
 }
 
-void LLQueuedThread::updateQueue(S32 inc)
+S32 LLQueuedThread::updateQueue(U32 max_time_ms)
 {
-	// If mRunAlways == TRUE, unpause the thread whenever we put something into the queue.
-	// If mRunAlways == FALSE, we only unpause the thread when updateQueue() is called from the main loop (i.e. between rendered frames)
-	
-	if (inc == 0) // Frame Update
+	F64 max_time = (F64)max_time_ms * .001;
+	LLTimer timer;
+	S32 pending = 1;
+
+	// Frame Update
+	if (mThreaded)
 	{
-		if (mThreaded)
-		{
-			unpause();
-			wake(); // Wake the thread up if necessary.
-		}
-		else
+		pending = getPending();
+		unpause();
+	}
+	else
+	{
+		while (pending > 0)
 		{
-			while (processNextRequest() > 0)
-				;
+			pending = processNextRequest();
+			if (max_time && timer.getElapsedTimeF64() > max_time)
+				break;
 		}
 	}
-	else
+	return pending;
+}
+
+void LLQueuedThread::incQueue()
+{
+	// Something has been added to the queue
+	if (!isPaused())
 	{
-		// Something has been added to the queue
-		if (mRunAlways)
+		if (mThreaded)
 		{
-			if (mThreaded)
-			{
-				wake(); // Wake the thread up if necessary.
-			}
-			else
-			{
-				while(processNextRequest() > 0)
-					;
-			}
+			wake(); // Wake the thread up if necessary.
 		}
 	}
 }
 
 //virtual
 // May be called from any thread
-S32 LLQueuedThread::getPending(bool child_thread)
+S32 LLQueuedThread::getPending()
 {
 	S32 res;
 	lockData();
@@ -122,7 +135,7 @@ void LLQueuedThread::waitOnPending()
 {
 	while(1)
 	{
-		updateQueue(0);
+		update(0);
 
 		if (mIdleThread)
 		{
@@ -181,7 +194,7 @@ bool LLQueuedThread::addRequest(QueuedRequest* req)
 #endif
 	unlockData();
 
-	updateQueue(1);
+	incQueue();
 
 	return true;
 }
@@ -195,7 +208,7 @@ bool LLQueuedThread::waitForResult(LLQueuedThread::handle_t handle, bool auto_co
 	bool done = false;
 	while(!done)
 	{
-		updateQueue(0); // unpauses
+		update(0); // unpauses
 		lockData();
 		QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle);
 		if (!req)
@@ -253,51 +266,47 @@ LLQueuedThread::status_t LLQueuedThread::getRequestStatus(handle_t handle)
 	return res;
 }
 
-LLQueuedThread::status_t LLQueuedThread::abortRequest(handle_t handle, U32 flags)
+void LLQueuedThread::abortRequest(handle_t handle, bool autocomplete)
 {
-	status_t res = STATUS_EXPIRED;
 	lockData();
 	QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle);
 	if (req)
 	{
-		res = req->abortRequest(flags);
-		if ((flags & AUTO_COMPLETE) && (res == STATUS_COMPLETE))
-		{
-			mRequestHash.erase(handle);
-			req->deleteRequest();
-// 			check();
-		}
-#if _DEBUG
-// 		llinfos << llformat("LLQueuedThread::Aborted req [%08d]",handle) << llendl;
-#endif
+		req->setFlags(FLAG_ABORT | (autocomplete ? FLAG_AUTO_COMPLETE : 0));
 	}
 	unlockData();
-	return res;
 }
 
 // MAIN thread
-LLQueuedThread::status_t LLQueuedThread::setFlags(handle_t handle, U32 flags)
+void LLQueuedThread::setFlags(handle_t handle, U32 flags)
 {
-	status_t res = STATUS_EXPIRED;
 	lockData();
 	QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle);
 	if (req)
 	{
-		res = req->setFlags(flags);
+		req->setFlags(flags);
 	}
 	unlockData();
-	return res;
 }
 
 void LLQueuedThread::setPriority(handle_t handle, U32 priority)
 {
 	lockData();
 	QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle);
-	if (req && (req->getStatus() == STATUS_QUEUED))
+	if (req)
 	{
-		llverify(mRequestQueue.erase(req) == 1);
-		req->setPriority(priority);
-		mRequestQueue.insert(req);
+		if(req->getStatus() == STATUS_INPROGRESS)
+		{
+			// not in list
+			req->setPriority(priority);
+		}
+		else if(req->getStatus() == STATUS_QUEUED)
+		{
+			// remove from list then re-insert
+			llverify(mRequestQueue.erase(req) == 1);
+			req->setPriority(priority);
+			mRequestQueue.insert(req);
+		}
 	}
 	unlockData();
 }
@@ -309,9 +318,10 @@ bool LLQueuedThread::completeRequest(handle_t handle)
 	QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle);
 	if (req)
 	{
-		llassert(req->getStatus() != STATUS_QUEUED && req->getStatus() != STATUS_ABORT);
+		llassert_always(req->getStatus() != STATUS_QUEUED);
+		llassert_always(req->getStatus() != STATUS_INPROGRESS);
 #if _DEBUG
-//  		llinfos << llformat("LLQueuedThread::Completed req [%08d]",handle) << llendl;
+// 		llinfos << llformat("LLQueuedThread::Completed req [%08d]",handle) << llendl;
 #endif
 		mRequestHash.erase(handle);
 		req->deleteRequest();
@@ -345,28 +355,34 @@ bool LLQueuedThread::check()
 //============================================================================
 // Runs on its OWN thread
 
-int LLQueuedThread::processNextRequest()
+S32 LLQueuedThread::processNextRequest()
 {
-	QueuedRequest *req = 0;
+	QueuedRequest *req;
 	// Get next request from pool
 	lockData();
 	while(1)
 	{
-		if (!mRequestQueue.empty())
+		req = NULL;
+		if (mRequestQueue.empty())
 		{
-			req = *mRequestQueue.begin();
-			mRequestQueue.erase(mRequestQueue.begin());
+			break;
 		}
-		if (req && req->getStatus() == STATUS_ABORT)
+		req = *mRequestQueue.begin();
+		mRequestQueue.erase(mRequestQueue.begin());
+		if ((req->getFlags() & FLAG_ABORT) || (mStatus == QUITTING))
 		{
 			req->setStatus(STATUS_ABORTED);
-			req = 0;
-		}
-		else
-		{
-			llassert (!req || req->getStatus() == STATUS_QUEUED)
-			break;
+			req->finishRequest(false);
+			if (req->getFlags() & FLAG_AUTO_COMPLETE)
+			{
+				mRequestHash.erase(req);
+				req->deleteRequest();
+// 				check();
+			}
+			continue;
 		}
+		llassert_always(req->getStatus() == STATUS_QUEUED);
+		break;
 	}
 	if (req)
 	{
@@ -374,22 +390,22 @@ int LLQueuedThread::processNextRequest()
 	}
 	unlockData();
 
-	// This is the only place we will cal req->setStatus() after
+	// This is the only place we will call req->setStatus() after
 	// it has initially been seet to STATUS_QUEUED, so it is
 	// safe to access req.
 	if (req)
 	{
 		// process request
-		bool complete = processRequest(req);
+		bool complete = req->processRequest();
 
 		if (complete)
 		{
 			lockData();
 			req->setStatus(STATUS_COMPLETE);
-			req->finishRequest();
-			if (req->getFlags() & AUTO_COMPLETE)
+			req->finishRequest(true);
+			if (req->getFlags() & FLAG_AUTO_COMPLETE)
 			{
-				llverify(mRequestHash.erase(req))
+				mRequestHash.erase(req);
 				req->deleteRequest();
 // 				check();
 			}
@@ -400,12 +416,18 @@ int LLQueuedThread::processNextRequest()
 			lockData();
 			req->setStatus(STATUS_QUEUED);
 			mRequestQueue.insert(req);
+			U32 priority = req->getPriority();
 			unlockData();
+			if (priority < PRIORITY_NORMAL)
+			{
+				ms_sleep(1); // sleep the thread a little
+			}
 		}
 	}
 
-	int res;
-	if (getPending(true) == 0)
+	S32 res;
+	S32 pending = getPending();
+	if (pending == 0)
 	{
 		if (isQuitting())
 		{
@@ -418,7 +440,7 @@ int LLQueuedThread::processNextRequest()
 	}
 	else
 	{
-		res = 1;
+		res = pending;
 	}
 	return res;
 }
@@ -426,13 +448,14 @@ int LLQueuedThread::processNextRequest()
 bool LLQueuedThread::runCondition()
 {
 	// mRunCondition must be locked here
-	return (mRequestQueue.empty() && mIdleThread) ? FALSE : TRUE;
+	if (mRequestQueue.empty() && mIdleThread)
+		return false;
+	else
+		return true;
 }
 
 void LLQueuedThread::run()
 {
-	llinfos << "QUEUED THREAD STARTING" << llendl;
-
 	while (1)
 	{
 		// this will block on the condition until runCondition() returns true, the thread is unpaused, or the thread leaves the RUNNING state.
@@ -455,6 +478,8 @@ void LLQueuedThread::run()
 		{
 			break;
 		}
+
+		//LLThread::yield(); // thread should yield after each request		
 	}
 
 	llinfos << "QUEUED THREAD " << mName << " EXITING." << llendl;
@@ -472,20 +497,18 @@ LLQueuedThread::QueuedRequest::QueuedRequest(LLQueuedThread::handle_t handle, U3
 
 LLQueuedThread::QueuedRequest::~QueuedRequest()
 {
-	if (mStatus != STATUS_DELETE)
-	{
-		llerrs << "Attemt to directly delete a  LLQueuedThread::QueuedRequest; use deleteRequest()" << llendl;
-	}
+	llassert_always(mStatus == STATUS_DELETE);
 }
 
 //virtual
-void LLQueuedThread::QueuedRequest::finishRequest()
+void LLQueuedThread::QueuedRequest::finishRequest(bool completed)
 {
 }
 
 //virtual
 void LLQueuedThread::QueuedRequest::deleteRequest()
 {
+	llassert_always(mStatus != STATUS_INPROGRESS);
 	setStatus(STATUS_DELETE);
 	delete this;
 }
diff --git a/indra/llcommon/llqueuedthread.h b/indra/llcommon/llqueuedthread.h
index beff473f520625ec901466bdda0a39df6dc5c2c6..8b6a658333ea73516ec0638262959d49ff53fd5c 100644
--- a/indra/llcommon/llqueuedthread.h
+++ b/indra/llcommon/llqueuedthread.h
@@ -33,7 +33,8 @@ public:
 		PRIORITY_HIGH =      0x30000000,
 		PRIORITY_NORMAL =    0x20000000,
 		PRIORITY_LOW =       0x10000000,
-		PRIORITY_LOWBITS =   0x0FFFFFFF
+		PRIORITY_LOWBITS =   0x0FFFFFFF,
+		PRIORITY_HIGHBITS =  0x70000000
 	};
 	enum status_t {
 		STATUS_EXPIRED = -1,
@@ -41,13 +42,13 @@ public:
 		STATUS_QUEUED = 1,
 		STATUS_INPROGRESS = 2,
 		STATUS_COMPLETE = 3,
-		STATUS_ABORT = 4,
-		STATUS_ABORTED = 5,
-		STATUS_DELETE = 6
+		STATUS_ABORTED = 4,
+		STATUS_DELETE = 5
 	};
 	enum flags_t {
-		AUTO_COMPLETE = 1,
-		AUTO_DELETE = 2 // child-class dependent
+		FLAG_AUTO_COMPLETE = 1,
+		FLAG_AUTO_DELETE = 2, // child-class dependent
+		FLAG_ABORT = 4
 	};
 
 	typedef U32 handle_t;
@@ -60,7 +61,7 @@ public:
 		friend class LLQueuedThread;
 		
 	protected:
-		~QueuedRequest(); // use deleteRequest()
+		virtual ~QueuedRequest(); // use deleteRequest()
 		
 	public:
 		QueuedRequest(handle_t handle, U32 priority, U32 flags = 0);
@@ -92,26 +93,14 @@ public:
 			mStatus = newstatus;
 			return oldstatus;
 		}
-		status_t abortRequest(U32 flags)
+		void setFlags(U32 flags)
 		{
 			// NOTE: flags are |'d
-			if (mStatus == STATUS_QUEUED)
-			{
-				setStatus(STATUS_ABORT);
-			}
 			mFlags |= flags;
-			status_t status = mStatus;
-			return status;
-		}
-		status_t setFlags(U32 flags)
-		{
-			// NOTE: flags are |'d
-			mFlags |= flags;
-			status_t status = mStatus;
-			return status;
 		}
 		
-		virtual void finishRequest(); // Always called when after has been processed
+		virtual bool processRequest() = 0; // Return true when request has completed
+		virtual void finishRequest(bool completed); // Always called from thread after request has completed or aborted
 		virtual void deleteRequest(); // Only method to delete a request
 
 		void setPriority(U32 pri)
@@ -141,9 +130,10 @@ public:
 	static handle_t nullHandle() { return handle_t(0); }
 	
 public:
-	LLQueuedThread(const std::string& name, bool threaded = TRUE, bool runalways = TRUE);
+	LLQueuedThread(const std::string& name, bool threaded = true);
 	virtual ~LLQueuedThread();	
-
+	virtual void shutdown();
+	
 private:
 	// No copy constructor or copy assignment
 	LLQueuedThread(const LLQueuedThread&);
@@ -155,26 +145,25 @@ private:
 protected:
 	handle_t generateHandle();
 	bool addRequest(QueuedRequest* req);
-	int  processNextRequest(void);
+	S32  processNextRequest(void);
+	void incQueue();
 
-	virtual bool processRequest(QueuedRequest* req) = 0;
-	
 public:
 	bool waitForResult(handle_t handle, bool auto_complete = true);
 
-	void update(U32 ms_elapsed);
-	void updateQueue(S32 inc);
+	virtual S32 update(U32 max_time_ms);
+	S32 updateQueue(U32 max_time_ms);
+	
 	void waitOnPending();
 	void printQueueStats();
 
-	S32 getPending(bool child_thread = false);
+	S32 getPending();
 	bool getThreaded() { return mThreaded ? true : false; }
-	bool getRunAlways() { return mRunAlways ? true : false; }
 
 	// Request accessors
 	status_t getRequestStatus(handle_t handle);
-	status_t abortRequest(handle_t handle, U32 flags = 0);
-	status_t setFlags(handle_t handle, U32 flags);
+	void abortRequest(handle_t handle, bool autocomplete);
+	void setFlags(handle_t handle, U32 flags);
 	void setPriority(handle_t handle, U32 priority);
 	bool completeRequest(handle_t handle);
 	// This is public for support classes like LLWorkerThread,
@@ -186,7 +175,6 @@ public:
 	
 protected:
 	BOOL mThreaded;  // if false, run on main thread and do updates during update()
-	BOOL mRunAlways; // if false, only wake the threads when updateClass() is called
 	LLAtomic32<BOOL> mIdleThread; // request queue is empty (or we are quitting) and the thread is idle
 	
 	typedef std::set<QueuedRequest*, queued_request_less> request_queue_t;
diff --git a/indra/llcommon/llsecondlifeurls.h b/indra/llcommon/llsecondlifeurls.h
index b07c18eb722327fdf2eaa896f210988aef9f40ce..cbea60034fb9f034c6c311d7f6aebb4a6b100f63 100644
--- a/indra/llcommon/llsecondlifeurls.h
+++ b/indra/llcommon/llsecondlifeurls.h
@@ -31,18 +31,9 @@ extern const char UPGRADE_TO_PREMIUM_URL[];
 // How to get DirectX 9
 extern const char DIRECTX_9_URL[];
 
-// On AMD with bad AGP controller
-extern const char AMD_AGP_URL[];
-
 // Out of date VIA chipset
 extern const char VIA_URL[];
 
-// Out of date intel chipset driver
-extern const char INTEL_CHIPSET_URL[];
-
-// Out of date SiS chipset driver
-extern const char SIS_CHIPSET_URL[];
-
 // Linden Blogs page
 extern const char BLOGS_URL[];
 
diff --git a/indra/llcommon/llstrider.h b/indra/llcommon/llstrider.h
index 0688e43940bf8c26a56577615ddac4f9e107e25f..c5fbf1282a41de95d9c1014b418c5f8c2ee4b64e 100644
--- a/indra/llcommon/llstrider.h
+++ b/indra/llcommon/llstrider.h
@@ -32,6 +32,7 @@ public:
 	Object* operator->()           { return mObjectp; }
 	Object& operator *()           { return *mObjectp; }
 	Object* operator ++(int)       { Object* old = mObjectp; mBytep += mSkip; return old; }
+	Object* operator +=(int i)     { mBytep += mSkip*i; return mObjectp; }
 	Object& operator[](U32 index)  { return *(Object*)(mBytep + (mSkip * index)); }
 };
 
diff --git a/indra/llcommon/llthread.cpp b/indra/llcommon/llthread.cpp
index bd2dd7c8f56e5a944a8f111e6de9bf659564b3c1..a92d553148ab6f5377348671b704e2a20794be11 100644
--- a/indra/llcommon/llthread.cpp
+++ b/indra/llcommon/llthread.cpp
@@ -80,6 +80,11 @@ LLThread::LLThread(const std::string& name, apr_pool_t *poolp) :
 
 
 LLThread::~LLThread()
+{
+	shutdown();
+}
+
+void LLThread::shutdown()
 {
 	// Warning!  If you somehow call the thread destructor from itself,
 	// the thread will die in an unclean fashion!
@@ -186,18 +191,6 @@ void LLThread::checkPause()
 
 //============================================================================
 
-bool LLThread::isQuitting() const
-{
-	return (QUITTING == mStatus);
-}
-
-
-bool LLThread::isStopped() const
-{
-	return (STOPPED == mStatus);
-}
-
-
 void LLThread::setQuitting()
 {
 	mRunCondition->lock();
@@ -328,3 +321,49 @@ void LLCondition::broadcast()
 	apr_thread_cond_broadcast(mAPRCondp);
 }
 
+//============================================================================
+
+//----------------------------------------------------------------------------
+
+//static
+LLMutex* LLThreadSafeRefCount::sMutex = 0;
+
+//static
+void LLThreadSafeRefCount::initClass()
+{
+	if (!sMutex)
+	{
+		sMutex = new LLMutex(0);
+	}
+}
+
+//static
+void LLThreadSafeRefCount::cleanupClass()
+{
+	delete sMutex;
+	sMutex = NULL;
+}
+	
+
+//----------------------------------------------------------------------------
+
+LLThreadSafeRefCount::LLThreadSafeRefCount() :
+	mRef(0)
+{
+}
+
+LLThreadSafeRefCount::~LLThreadSafeRefCount()
+{ 
+	if (mRef != 0)
+	{
+		llerrs << "deleting non-zero reference" << llendl;
+	}
+}
+
+//============================================================================
+
+LLResponder::~LLResponder()
+{
+}
+
+//============================================================================
diff --git a/indra/llcommon/llthread.h b/indra/llcommon/llthread.h
index f6f6bd210aa50ae363f44d9f4aa024441978fc80..ce5daa938cc1f752dd03753cd9a5ee8f32754ad2 100644
--- a/indra/llcommon/llthread.h
+++ b/indra/llcommon/llthread.h
@@ -11,6 +11,7 @@
 
 #include "llapr.h"
 #include "llapp.h"
+#include "llmemory.h"
 
 #include "apr-1/apr_thread_cond.h"
 
@@ -30,19 +31,20 @@ public:
 
 	LLThread(const std::string& name, apr_pool_t *poolp = NULL);
 	virtual ~LLThread(); // Warning!  You almost NEVER want to destroy a thread unless it's in the STOPPED state.
-		
+	virtual void shutdown(); // stops the thread
+	
 	static void yield(); // Static because it can be called by the main thread, which doesn't have an LLThread data structure.
 
 
-	bool isQuitting() const;
-	bool isStopped() const;
+	bool isQuitting() const { return (QUITTING == mStatus); }
+	bool isStopped() const { return (STOPPED == mStatus); }
 	
 	// PAUSE / RESUME functionality. See source code for important usage notes.
 public:
 	// Called from MAIN THREAD.
 	void pause();
 	void unpause();
-	bool isPaused() { return mPaused ? true : false; }
+	bool isPaused() { return isStopped() || mPaused == TRUE; }
 	
 	// Cause the thread to wake up and check its condition
 	void wake();
@@ -60,7 +62,7 @@ public:
 	
 private:
 	BOOL				mPaused;
-
+	
 	// static function passed to APR thread creation routine
 	static void *APR_THREAD_FUNC staticRun(apr_thread_t *apr_threadp, void *datap);
 
@@ -159,6 +161,69 @@ void LLThread::unlockData()
 }
 
 
+//============================================================================
+
+// see llmemory.h for LLPointer<> definition
+
+class LLThreadSafeRefCount
+{
+public:
+	static void initClass(); // creates sMutex
+	static void cleanupClass(); // destroys sMutex
+	
+private:
+	static LLMutex* sMutex;
+
+private:
+	LLThreadSafeRefCount(const LLThreadSafeRefCount&); // not implemented
+	LLThreadSafeRefCount&operator=(const LLThreadSafeRefCount&); // not implemented
+
+protected:
+	virtual ~LLThreadSafeRefCount(); // use unref()
+	
+public:
+	LLThreadSafeRefCount();
+	
+	void ref()
+	{
+		if (sMutex) sMutex->lock();
+		mRef++; 
+		if (sMutex) sMutex->unlock();
+	} 
+
+	S32 unref()
+	{
+		llassert(mRef >= 1);
+		if (sMutex) sMutex->lock();
+		S32 res = --mRef;
+		if (sMutex) sMutex->unlock();
+		if (0 == res) 
+		{
+			delete this; 
+			res = 0;
+		}
+		return res;
+	}	
+	S32 getNumRefs() const
+	{
+		return mRef;
+	}
+
+private: 
+	S32	mRef; 
+};
+
+//============================================================================
+
+// Simple responder for self destructing callbacks
+// Pure virtual class
+class LLResponder : public LLThreadSafeRefCount
+{
+public:
+	virtual ~LLResponder();
+	virtual void completed(bool success) = 0;
+};
+
 //============================================================================
 
 #endif // LL_LLTHREAD_H
diff --git a/indra/llcommon/llworkerthread.cpp b/indra/llcommon/llworkerthread.cpp
index a9370c8f6dde7c92b52ba03be6c112cf0a20151a..31f5c1cfcd31d20b7aa3c307e6aca647ad5674fb 100644
--- a/indra/llcommon/llworkerthread.cpp
+++ b/indra/llcommon/llworkerthread.cpp
@@ -13,99 +13,87 @@
 #include "llframecallbackmanager.h"
 #endif
 
-//============================================================================
-
-/*static*/ LLWorkerThread* LLWorkerThread::sLocal = NULL;
-/*static*/ std::set<LLWorkerThread*> LLWorkerThread::sThreadList;
-
 //============================================================================
 // Run on MAIN thread
 
-//static
-void LLWorkerThread::initClass(bool local_is_threaded, bool local_run_always)
+LLWorkerThread::LLWorkerThread(const std::string& name, bool threaded) :
+	LLQueuedThread(name, threaded),
+	mWorkerAPRPoolp(NULL)
 {
-	if (!sLocal)
-	{
-		sLocal = new LLWorkerThread(local_is_threaded, local_run_always);
-	}
+	apr_pool_create(&mWorkerAPRPoolp, NULL);
+	mDeleteMutex = new LLMutex(getAPRPool());
 }
 
-//static
-void LLWorkerThread::cleanupClass()
+LLWorkerThread::~LLWorkerThread()
 {
-	if (sLocal)
+	// Delete any workers in the delete queue (should be safe - had better be!)
+	if (!mDeleteList.empty())
 	{
-		while (sLocal->getPending())
-		{
-			sLocal->update(0);
-		}
-		delete sLocal;
-		sLocal = NULL;
-		llassert(sThreadList.size() == 0);
+		llwarns << "Worker Thread: " << mName << " destroyed with " << mDeleteList.size()
+				<< " entries in delete list." << llendl;
 	}
-}
 
-//static
-S32 LLWorkerThread::updateClass(U32 ms_elapsed)
-{
-	for (std::set<LLWorkerThread*>::iterator iter = sThreadList.begin(); iter != sThreadList.end(); iter++)
-	{
-		(*iter)->update(ms_elapsed);
-	}
-	return getAllPending();
+	delete mDeleteMutex;
+	
+	// ~LLQueuedThread() will be called here
 }
 
-//static
-S32 LLWorkerThread::getAllPending()
+// virtual
+S32 LLWorkerThread::update(U32 max_time_ms)
 {
-	S32 res = 0;
-	for (std::set<LLWorkerThread*>::iterator iter = sThreadList.begin(); iter != sThreadList.end(); iter++)
+	S32 res = LLQueuedThread::update(max_time_ms);
+	// Delete scheduled workers
+	std::vector<LLWorkerClass*> delete_list;
+	std::vector<LLWorkerClass*> abort_list;
+	mDeleteMutex->lock();
+	for (delete_list_t::iterator iter = mDeleteList.begin();
+		 iter != mDeleteList.end(); )
 	{
-		res += (*iter)->getPending();
+		delete_list_t::iterator curiter = iter++;
+		LLWorkerClass* worker = *curiter;
+		if (worker->deleteOK())
+		{
+			if (worker->getFlags(LLWorkerClass::WCF_WORK_FINISHED))
+			{
+				delete_list.push_back(worker);
+				mDeleteList.erase(curiter);
+			}
+			else if (!worker->getFlags(LLWorkerClass::WCF_ABORT_REQUESTED))
+			{
+				abort_list.push_back(worker);
+			}
+		}
 	}
-	return res;
-}
-
-//static
-void LLWorkerThread::pauseAll()
-{
-	for (std::set<LLWorkerThread*>::iterator iter = sThreadList.begin(); iter != sThreadList.end(); iter++)
+	mDeleteMutex->unlock();	
+	// abort and delete after releasing mutex
+	for (std::vector<LLWorkerClass*>::iterator iter = abort_list.begin();
+		 iter != abort_list.end(); ++iter)
 	{
-		(*iter)->pause();
+		(*iter)->abortWork(false);
 	}
-}
-
-//static
-void LLWorkerThread::waitOnAllPending()
-{
-	for (std::set<LLWorkerThread*>::iterator iter = sThreadList.begin(); iter != sThreadList.end(); iter++)
+	for (std::vector<LLWorkerClass*>::iterator iter = delete_list.begin();
+		 iter != delete_list.end(); ++iter)
 	{
-		(*iter)->waitOnPending();
+		LLWorkerClass* worker = *iter;
+		if (worker->mRequestHandle)
+		{
+			// Finished but not completed
+			completeRequest(worker->mRequestHandle);
+			worker->mRequestHandle = LLWorkerThread::nullHandle();
+			worker->clearFlags(LLWorkerClass::WCF_HAVE_WORK);
+		}
+		delete *iter;
 	}
+	return res;
 }
 
 //----------------------------------------------------------------------------
 
-LLWorkerThread::LLWorkerThread(bool threaded, bool runalways) :
-	LLQueuedThread("Worker", threaded, runalways)
-{
-	sThreadList.insert(this);
-}
-
-LLWorkerThread::~LLWorkerThread()
-{
-	llverify(sThreadList.erase(this) == 1);
-	// ~LLQueuedThread() will be called here
-}
-
-//----------------------------------------------------------------------------
-
-
-LLWorkerThread::handle_t LLWorkerThread::add(LLWorkerClass* workerclass, S32 param, U32 priority)
+LLWorkerThread::handle_t LLWorkerThread::addWorkRequest(LLWorkerClass* workerclass, S32 param, U32 priority)
 {
 	handle_t handle = generateHandle();
 	
-	Request* req = new Request(handle, priority, workerclass, param);
+	WorkRequest* req = new WorkRequest(handle, priority, workerclass, param);
 
 	bool res = addRequest(req);
 	if (!res)
@@ -118,63 +106,80 @@ LLWorkerThread::handle_t LLWorkerThread::add(LLWorkerClass* workerclass, S32 par
 	return handle;
 }
 
-//============================================================================
-// Runs on its OWN thread
-
-bool LLWorkerThread::processRequest(QueuedRequest* qreq)
+void LLWorkerThread::deleteWorker(LLWorkerClass* workerclass)
 {
-	Request *req = (Request*)qreq;
-
-	req->getWorkerClass()->setWorking(true);
-
-	bool complete = req->getWorkerClass()->doWork(req->getParam());
-
-	req->getWorkerClass()->setWorking(false);
-
-	LLThread::yield(); // worker thread should yield after each request
-	
-	return complete;
+	mDeleteMutex->lock();
+	mDeleteList.push_back(workerclass);
+	mDeleteMutex->unlock();
 }
 
 //============================================================================
+// Runs on its OWN thread
 
-LLWorkerThread::Request::Request(handle_t handle, U32 priority, LLWorkerClass* workerclass, S32 param) :
+LLWorkerThread::WorkRequest::WorkRequest(handle_t handle, U32 priority, LLWorkerClass* workerclass, S32 param) :
 	LLQueuedThread::QueuedRequest(handle, priority),
 	mWorkerClass(workerclass),
 	mParam(param)
 {
 }
 
-void LLWorkerThread::Request::deleteRequest()
+LLWorkerThread::WorkRequest::~WorkRequest()
+{
+}
+
+// virtual (required for access by LLWorkerThread)
+void LLWorkerThread::WorkRequest::deleteRequest()
 {
 	LLQueuedThread::QueuedRequest::deleteRequest();
 }	
 
+// virtual
+bool LLWorkerThread::WorkRequest::processRequest()
+{
+	LLWorkerClass* workerclass = getWorkerClass();
+	workerclass->setWorking(true);
+	bool complete = workerclass->doWork(getParam());
+	workerclass->setWorking(false);
+	return complete;
+}
+
+// virtual
+void LLWorkerThread::WorkRequest::finishRequest(bool completed)
+{
+	LLWorkerClass* workerclass = getWorkerClass();
+	workerclass->finishWork(getParam(), completed);
+	U32 flags = LLWorkerClass::WCF_WORK_FINISHED | (completed ? 0 : LLWorkerClass::WCF_WORK_ABORTED);
+	workerclass->setFlags(flags);
+}
+
 //============================================================================
 // LLWorkerClass:: operates in main thread
 
 LLWorkerClass::LLWorkerClass(LLWorkerThread* workerthread, const std::string& name)
 	: mWorkerThread(workerthread),
 	  mWorkerClassName(name),
-	  mWorkHandle(LLWorkerThread::nullHandle()),
+	  mRequestHandle(LLWorkerThread::nullHandle()),
+	  mMutex(workerthread->getWorkerAPRPool()),
 	  mWorkFlags(0)
 {
 	if (!mWorkerThread)
 	{
-		mWorkerThread = LLWorkerThread::sLocal;
+		llerrs << "LLWorkerClass() called with NULL workerthread: " << name << llendl;
 	}
 }
+
 LLWorkerClass::~LLWorkerClass()
 {
-	if (mWorkHandle != LLWorkerThread::nullHandle())
+	llassert_always(!(mWorkFlags & WCF_WORKING));
+	llassert_always(mWorkFlags & WCF_DELETE_REQUESTED);
+	if (mRequestHandle != LLWorkerThread::nullHandle())
 	{
-		LLWorkerThread::Request* workreq = (LLWorkerThread::Request*)mWorkerThread->getRequest(mWorkHandle);
+		LLWorkerThread::WorkRequest* workreq = (LLWorkerThread::WorkRequest*)mWorkerThread->getRequest(mRequestHandle);
 		if (!workreq)
 		{
 			llerrs << "LLWorkerClass destroyed with stale work handle" << llendl;
 		}
-		if (workreq->getStatus() != LLWorkerThread::STATUS_ABORT &&
-			workreq->getStatus() != LLWorkerThread::STATUS_ABORTED &&
+		if (workreq->getStatus() != LLWorkerThread::STATUS_ABORTED &&
 			workreq->getStatus() != LLWorkerThread::STATUS_COMPLETE)
 		{
 			llerrs << "LLWorkerClass destroyed with active worker! Worker Status: " << workreq->getStatus() << llendl;
@@ -184,21 +189,58 @@ LLWorkerClass::~LLWorkerClass()
 
 void LLWorkerClass::setWorkerThread(LLWorkerThread* workerthread)
 {
-	if (mWorkHandle != LLWorkerThread::nullHandle())
+	mMutex.lock();
+	if (mRequestHandle != LLWorkerThread::nullHandle())
 	{
 		llerrs << "LLWorkerClass attempt to change WorkerThread with active worker!" << llendl;
 	}
 	mWorkerThread = workerthread;
+	mMutex.unlock();
+}
+
+//----------------------------------------------------------------------------
+
+//virtual
+void LLWorkerClass::finishWork(S32 param, bool success)
+{
+}
+
+//virtual
+bool LLWorkerClass::deleteOK()
+{
+	return true; // default always OK
+}
+
+//----------------------------------------------------------------------------
+
+// Called from worker thread
+void LLWorkerClass::setWorking(bool working)
+{
+	mMutex.lock();
+	if (working)
+	{
+		llassert_always(!(mWorkFlags & WCF_WORKING));
+		setFlags(WCF_WORKING);
+	}
+	else
+	{
+		llassert_always((mWorkFlags & WCF_WORKING));
+		clearFlags(WCF_WORKING);
+	}
+	mMutex.unlock();
 }
 
 //----------------------------------------------------------------------------
 
 bool LLWorkerClass::yield()
 {
-	llassert(mWorkFlags & WCF_WORKING);
 	LLThread::yield();
 	mWorkerThread->checkPause();
-	return (getFlags() & WCF_ABORT_REQUESTED) ? true : false;
+	bool res;
+	mMutex.lock();
+	res = (getFlags() & WCF_ABORT_REQUESTED) ? true : false;
+	mMutex.unlock();
+	return res;
 }
 
 //----------------------------------------------------------------------------
@@ -206,7 +248,9 @@ bool LLWorkerClass::yield()
 // calls startWork, adds doWork() to queue
 void LLWorkerClass::addWork(S32 param, U32 priority)
 {
-	if (mWorkHandle != LLWorkerThread::nullHandle())
+	mMutex.lock();
+	llassert_always(!(mWorkFlags & (WCF_WORKING|WCF_HAVE_WORK)));
+	if (mRequestHandle != LLWorkerThread::nullHandle())
 	{
 		llerrs << "LLWorkerClass attempt to add work with active worker!" << llendl;
 	}
@@ -214,70 +258,93 @@ void LLWorkerClass::addWork(S32 param, U32 priority)
 // 	llinfos << "addWork: " << mWorkerClassName << " Param: " << param << llendl;
 #endif
 	startWork(param);
-	mWorkHandle = mWorkerThread->add(this, param, priority);
+	clearFlags(WCF_WORK_FINISHED|WCF_WORK_ABORTED);
+	setFlags(WCF_HAVE_WORK);
+	mRequestHandle = mWorkerThread->addWorkRequest(this, param, priority);
+	mMutex.unlock();
 }
 
-void LLWorkerClass::abortWork()
+void LLWorkerClass::abortWork(bool autocomplete)
 {
+	mMutex.lock();
 #if _DEBUG
-// 	LLWorkerThread::Request* workreq = mWorkerThread->getRequest(mWorkHandle);
+// 	LLWorkerThread::WorkRequest* workreq = mWorkerThread->getRequest(mRequestHandle);
 // 	if (workreq)
 // 		llinfos << "abortWork: " << mWorkerClassName << " Param: " << workreq->getParam() << llendl;
 #endif
-	mWorkerThread->abortRequest(mWorkHandle);
-	setFlags(WCF_ABORT_REQUESTED);
+	if (mRequestHandle != LLWorkerThread::nullHandle())
+	{
+		mWorkerThread->abortRequest(mRequestHandle, autocomplete);
+		mWorkerThread->setPriority(mRequestHandle, LLQueuedThread::PRIORITY_IMMEDIATE);
+		setFlags(WCF_ABORT_REQUESTED);
+	}
+	mMutex.unlock();
 }
 
 // if doWork is complete or aborted, call endWork() and return true
-bool LLWorkerClass::checkWork()
+bool LLWorkerClass::checkWork(bool aborting)
 {
+	LLMutexLock lock(&mMutex);
 	bool complete = false, abort = false;
-	LLWorkerThread::Request* workreq = (LLWorkerThread::Request*)mWorkerThread->getRequest(mWorkHandle);
-	llassert(workreq);
-	if (getFlags(WCF_ABORT_REQUESTED) || workreq->getStatus() == LLWorkerThread::STATUS_ABORTED)
+	if (mRequestHandle != LLWorkerThread::nullHandle())
 	{
-		complete = true;
-		abort = true;
+		LLWorkerThread::WorkRequest* workreq = (LLWorkerThread::WorkRequest*)mWorkerThread->getRequest(mRequestHandle);
+		llassert_always(workreq);
+		LLQueuedThread::status_t status = workreq->getStatus();
+		if (status == LLWorkerThread::STATUS_ABORTED)
+		{
+			complete = true;
+			abort = true;
+		}
+		else if (status == LLWorkerThread::STATUS_COMPLETE)
+		{
+			complete = true;
+		}
+		else
+		{
+			llassert_always(!aborting || (workreq->getFlags() & LLQueuedThread::FLAG_ABORT));
+		}
+		if (complete)
+		{
+			llassert_always(!(getFlags(WCF_WORKING)));
+			endWork(workreq->getParam(), abort);
+			mWorkerThread->completeRequest(mRequestHandle);
+			mRequestHandle = LLWorkerThread::nullHandle();
+			clearFlags(WCF_HAVE_WORK);
+		}
 	}
-	else if (workreq->getStatus() == LLWorkerThread::STATUS_COMPLETE)
+	else
 	{
 		complete = true;
 	}
-	if (complete)
-	{
-#if _DEBUG
-// 		llinfos << "endWork: " << mWorkerClassName << " Param: " << workreq->getParam() << llendl;
-#endif
-		endWork(workreq->getParam(), abort);
-		mWorkerThread->completeRequest(mWorkHandle);
-		mWorkHandle = LLWorkerThread::nullHandle();
-	}
 	return complete;
 }
 
-void LLWorkerClass::killWork()
+void LLWorkerClass::scheduleDelete()
 {
-	if (haveWork())
+	bool do_delete = false;
+	mMutex.lock();
+	if (!(getFlags(WCF_DELETE_REQUESTED)))
 	{
-		abortWork();
-		bool paused = mWorkerThread->isPaused();
-		while (!checkWork())
-		{
-			mWorkerThread->updateQueue(0);
-		}
-		if (paused)
-		{
-			mWorkerThread->pause();
-		}
+		setFlags(WCF_DELETE_REQUESTED);
+		do_delete = true;
+	}
+	mMutex.unlock();
+	if (do_delete)
+	{
+		mWorkerThread->deleteWorker(this);
 	}
 }
 
 void LLWorkerClass::setPriority(U32 priority)
 {
-	if (haveWork())
+	mMutex.lock();
+	if (mRequestHandle != LLWorkerThread::nullHandle())
 	{
-		mWorkerThread->setPriority(mWorkHandle, priority);
+		mRequestPriority = priority;
+		mWorkerThread->setPriority(mRequestHandle, priority);
 	}
+	mMutex.unlock();
 }
 
 //============================================================================
diff --git a/indra/llcommon/llworkerthread.h b/indra/llcommon/llworkerthread.h
index bf5887e797dbed7d9a10ebdbf792a5e909b95aa9..d466c35786a9a14e3ffbf030501bbc045029f2ac 100644
--- a/indra/llcommon/llworkerthread.h
+++ b/indra/llcommon/llworkerthread.h
@@ -28,13 +28,13 @@ class LLWorkerClass;
 class LLWorkerThread : public LLQueuedThread
 {
 public:
-	class Request : public LLQueuedThread::QueuedRequest
+	class WorkRequest : public LLQueuedThread::QueuedRequest
 	{
 	protected:
-		~Request() {}; // use deleteRequest()
+		virtual ~WorkRequest(); // use deleteRequest()
 		
 	public:
-		Request(handle_t handle, U32 priority, LLWorkerClass* workerclass, S32 param);
+		WorkRequest(handle_t handle, U32 priority, LLWorkerClass* workerclass, S32 param);
 
 		S32 getParam()
 		{
@@ -45,6 +45,8 @@ public:
 			return mWorkerClass;
 		}
 
+		/*virtual*/ bool processRequest();
+		/*virtual*/ void finishRequest(bool completed);
 		/*virtual*/ void deleteRequest();
 		
 	private:
@@ -52,26 +54,24 @@ public:
 		S32 mParam;
 	};
 
-public:
-	LLWorkerThread(bool threaded = true, bool runalways = true);
-	~LLWorkerThread();	
-
-protected:
-	/*virtual*/ bool processRequest(QueuedRequest* req);
+private:
+	typedef std::list<LLWorkerClass*> delete_list_t;
+	delete_list_t mDeleteList;
+	LLMutex* mDeleteMutex;
+	apr_pool_t* mWorkerAPRPoolp;
 	
 public:
-	handle_t add(LLWorkerClass* workerclass, S32 param, U32 priority = PRIORITY_NORMAL);
-
-	static void initClass(bool local_is_threaded = true, bool local_run_always = true); // Setup sLocal
-	static S32 updateClass(U32 ms_elapsed);
-	static S32 getAllPending();
-	static void pauseAll();
-	static void waitOnAllPending();
-	static void cleanupClass();		// Delete sLocal
+	LLWorkerThread(const std::string& name, bool threaded = true);
+	~LLWorkerThread();
 
-public:
-	static LLWorkerThread* sLocal;		// Default worker thread
-	static std::set<LLWorkerThread*> sThreadList; // array of threads (includes sLocal)
+	apr_pool_t* getWorkerAPRPool() { return mWorkerAPRPoolp; }
+	
+	/*virtual*/ S32 update(U32 max_time_ms);
+	
+	handle_t addWorkRequest(LLWorkerClass* workerclass, S32 param, U32 priority = PRIORITY_NORMAL);
+	
+	void deleteWorker(LLWorkerClass* workerclass); // schedule for deletion
+	S32 getNumDeletes() { return mDeleteList.size(); } // debug
 };
 
 //============================================================================
@@ -93,11 +93,17 @@ public:
 
 class LLWorkerClass
 {
+	friend class LLWorkerThread;
+	friend class LLWorkerThread::WorkRequest;
 public:
 	typedef LLWorkerThread::handle_t handle_t;
 	enum FLAGS
 	{
-		WCF_WORKING = 0x01,
+		WCF_HAVE_WORK = 0x01,
+		WCF_WORKING = 0x02,
+		WCF_WORK_FINISHED = 0x10,
+		WCF_WORK_ABORTED = 0x20,
+		WCF_DELETE_REQUESTED = 0x40,
 		WCF_ABORT_REQUESTED = 0x80
 	};
 	
@@ -106,17 +112,29 @@ public:
 	virtual ~LLWorkerClass();
 
 	// pure virtual, called from WORKER THREAD, returns TRUE if done
-	virtual bool doWork(S32 param)=0; // Called from LLWorkerThread::processRequest()
-
-	// called from WORKER THREAD
-	void setWorking(bool working) { working ? setFlags(WCF_WORKING) : clearFlags(WCF_WORKING); }
+	virtual bool doWork(S32 param)=0; // Called from WorkRequest::processRequest()
+	// virtual, called from finishRequest() after completed or aborted
+	virtual void finishWork(S32 param, bool completed); // called from finishRequest() (WORK THREAD)
+	// virtual, returns true if safe to delete the worker
+	virtual bool deleteOK(); // called from update() (WORK THREAD)
 	
+	// schedlueDelete(): schedules deletion once aborted or completed
+	void scheduleDelete();
+	
+	bool haveWork() { return getFlags(WCF_HAVE_WORK); } // may still be true if aborted
 	bool isWorking() { return getFlags(WCF_WORKING); }
 	bool wasAborted() { return getFlags(WCF_ABORT_REQUESTED); }
+
+	// setPriority(): changes the priority of a request
+	void setPriority(U32 priority);
+	U32  getPriority() { return mRequestPriority; }
 		
 	const std::string& getName() const { return mWorkerClassName; }
 
 protected:
+	// called from WORKER THREAD
+	void setWorking(bool working);
+	
 	// Call from doWork only to avoid eating up cpu time.
 	// Returns true if work has been aborted
 	// yields the current thread and calls mWorkerThread->checkPause()
@@ -128,20 +146,11 @@ protected:
 	void addWork(S32 param, U32 priority = LLWorkerThread::PRIORITY_NORMAL);
 
 	// abortWork(): requests that work be aborted
-	void abortWork();
+	void abortWork(bool autocomplete);
 	
 	// checkWork(): if doWork is complete or aborted, call endWork() and return true
-	bool checkWork();
+	bool checkWork(bool aborting = false);
 
-	// haveWork(): return true if mWorkHandle != null
-	bool haveWork() { return mWorkHandle != LLWorkerThread::nullHandle(); }
-
-	// killWork(): aborts work and waits for the abort to process
-	void killWork();
-
-	// setPriority(): changes the priority of a request
-	void setPriority(U32 priority);
-	
 private:
 	void setFlags(U32 flags) { mWorkFlags = mWorkFlags | flags; }
 	void clearFlags(U32 flags) { mWorkFlags = mWorkFlags & ~flags; }
@@ -156,9 +165,11 @@ private:
 protected:
 	LLWorkerThread* mWorkerThread;
 	std::string mWorkerClassName;
-	handle_t mWorkHandle;
+	handle_t mRequestHandle;
+	U32 mRequestPriority; // last priority set
 
 private:
+	LLMutex mMutex;
 	LLAtomicU32 mWorkFlags;
 };
 
diff --git a/indra/llimage/llimage.cpp b/indra/llimage/llimage.cpp
index dc864aaf53539230ac147efbea2c8c77465359a1..9b37cdade5140c99c24272798c7787110956dd8a 100644
--- a/indra/llimage/llimage.cpp
+++ b/indra/llimage/llimage.cpp
@@ -17,7 +17,7 @@
 #include "llmath.h"
 #include "stdtypes.h"
 #include "v4coloru.h"
-#include "llmemory.h"
+#include "llmemtype.h"
 
 #include "llimage.h"
 #include "llimagebmp.h"
@@ -114,7 +114,7 @@ U8* LLImageBase::allocateData(S32 size)
 			llerrs << llformat("LLImageBase::allocateData called with bad dimentions: %dx%dx%d",mWidth,mHeight,mComponents) << llendl;
 		}
 	}
-	else if ((size <= 0 || size > 4096*4096*16) && sSizeOverride == FALSE)
+	else if (size <= 0 || (size > 4096*4096*16 && sSizeOverride == FALSE))
 	{
 		llerrs << "LLImageBase::allocateData: bad size: " << size << llendl;
 	}
@@ -197,7 +197,8 @@ LLImageRaw::LLImageRaw(U8 *data, U16 width, U16 height, S8 components)
 	: LLImageBase()
 {
 	mMemType = LLMemType::MTYPE_IMAGERAW;
-	copyData(data, width, height, components);
+	allocateDataSize(width, height, components);
+	memcpy(getData(), data, width*height*components);
 	++sRawImageCount;
 }
 
@@ -239,20 +240,6 @@ void LLImageRaw::deleteData()
 	LLImageBase::deleteData();
 }
 
-BOOL LLImageRaw::copyData(U8 *data, U16 width, U16 height, S8 components)
-{
-	if (!resize(width, height, components))
-	{
-		return FALSE;
-	}
-	if (getData() == NULL || data == NULL)
-	{
-		return FALSE;
-	}
-	memcpy(getData(), data, width*height*components);	/* Flawfinder: ignore */
-	return TRUE;
-}
-
 BOOL LLImageRaw::resize(U16 width, U16 height, S8 components)
 {
 	if ((getWidth() == width) && (getHeight() == height) && (getComponents() == components))
@@ -1243,25 +1230,8 @@ bool LLImageRaw::createFromFile(const LLString &filename, bool j2c_lowest_mip_on
 //static
 S32 LLImageFormatted::sGlobalFormattedMemory = 0;
 
-//static
-LLWorkerThread* LLImageFormatted::sWorkerThread = NULL;
-
-//static
-void LLImageFormatted::initClass(bool threaded, bool run_always)
-{
-	sWorkerThread = new LLWorkerThread(threaded, run_always);
-}
-
-//static
-void LLImageFormatted::cleanupClass()
-{
-	delete sWorkerThread;
-	sWorkerThread = NULL;
-}
-
-
 LLImageFormatted::LLImageFormatted(S8 codec)
-	: LLImageBase(), LLWorkerClass(sWorkerThread, "ImageFormatted"),
+	: LLImageBase(),
 	  mCodec(codec),
 	  mDecoding(0),
 	  mDecoded(0),
@@ -1276,64 +1246,14 @@ LLImageFormatted::~LLImageFormatted()
 	// NOTE: ~LLimageBase() call to deleteData() calls LLImageBase::deleteData()
 	//        NOT LLImageFormatted::deleteData()
 	deleteData();
-	releaseDecodedData();
-}
-
-//----------------------------------------------------------------------------
-
-//virtual
-void LLImageFormatted::startWork(S32 param)
-{
-	if (mDecoding) llerrs << "WTF?" << llendl;
-}
-
-bool LLImageFormatted::doWork(S32 param)
-{
-	if (!(isWorking())) llerrs << "WTF?" << llendl;
-	llassert(mDecodedImage.notNull());
-	if (param == 0)
-	{
-		// Decode primary channels
-		mDecoded = decode(mDecodedImage, .001f); // 1ms
-	}
-	else
-	{
-		// Decode aux channel
-		mDecoded = decode(mDecodedImage, .001f, param, param); // 1ms
-	}
-	if (mDecoded)
-	{
-		return true;
-	}
-	else
-	{
-		return false;
-	}
-}
-
-void LLImageFormatted::endWork(S32 param, bool aborted)
-{
-	if (mDecoding) llerrs << "WTF?" << llendl;
-	if (!mDecoded) llerrs << "WTF?" << llendl;
 }
 
 //----------------------------------------------------------------------------
 
 // static
-LLImageFormatted* LLImageFormatted::createFromExtension(const LLString& instring)
+LLImageFormatted* LLImageFormatted::createFromType(S8 codec)
 {
-	LLString exten;
-	size_t dotidx = instring.rfind('.');
-	if (dotidx != LLString::npos)
-	{
-		exten = instring.substr(dotidx+1);
-	}
-	else
-	{
-		exten = instring;
-	}
-	S8 codec = get_codec(exten);
-	LLPointer<LLImageFormatted> image;
+	LLImageFormatted* image;
 	switch(codec)
 	{
 	  case IMG_CODEC_BMP:
@@ -1354,10 +1274,28 @@ LLImageFormatted* LLImageFormatted::createFromExtension(const LLString& instring
 		image = new LLImageDXT();
 		break;
 	  default:
+		image = NULL;
 		break;
 	}
 	return image;
 }
+
+// static
+LLImageFormatted* LLImageFormatted::createFromExtension(const LLString& instring)
+{
+	LLString exten;
+	size_t dotidx = instring.rfind('.');
+	if (dotidx != LLString::npos)
+	{
+		exten = instring.substr(dotidx+1);
+	}
+	else
+	{
+		exten = instring;
+	}
+	S8 codec = get_codec(exten);
+	return createFromType(codec);
+}
 //----------------------------------------------------------------------------
 
 // virtual
@@ -1374,15 +1312,6 @@ void LLImageFormatted::dump()
 
 //----------------------------------------------------------------------------
 
-void LLImageFormatted::readHeader(U8* data, S32 size)
-{
-	if (size <= 0)
-	{
-		size = calcHeaderSize();
-	}
-	copyData(data, size); // calls updateData()
-}
-
 S32 LLImageFormatted::calcDataSize(S32 discard_level)
 {
 	if (discard_level < 0)
@@ -1426,82 +1355,6 @@ BOOL LLImageFormatted::decode(LLImageRaw* raw_image,F32  decode_time, S32 first_
 	return decode( raw_image, decode_time );  // Loads first 4 channels by default.
 } 
 
-// virtual
-BOOL LLImageFormatted::requestDecodedData(LLPointer<LLImageRaw>& raw, S32 discard, F32 decode_time)
-{
-	llassert(getData() && getDataSize());
-	// For most codecs, only mDiscardLevel data is available. (see LLImageDXT for exception)
-	if (discard >= 0 && discard != mDiscardLevel)
-	{
-		llerrs << "Request for invalid discard level" << llendl;
-	}
-	if (haveWork())
-	{
-		checkWork();
-	}
-	if (!mDecoded)
-	{
-		if (!haveWork())
-		{
-			llassert(!mDecoding);
-			mDecodedImage = new LLImageRaw(getWidth(), getHeight(), getComponents());
-			addWork(0);
-		}
-		return FALSE;
-	}
-	else
-	{
-		llassert(mDecodedImage.notNull());
-		llassert(!mDecoding);
-		raw = mDecodedImage;
-		return TRUE;
-	}
-}
-
-BOOL LLImageFormatted::requestDecodedAuxData(LLPointer<LLImageRaw>& raw, S32 channel, 
-											 S32 discard, F32 decode_time)
-{
-	llassert(getData() && getDataSize());
-	// For most codecs, only mDiscardLevel data is available. (see LLImageDXT for exception)
-	if (discard >= 0 && discard != mDiscardLevel)
-	{
-		llerrs << "Request for invalid discard level" << llendl;
-	}
-	if (haveWork())
-	{
-		checkWork();
-	}
-	if (!mDecoded)
-	{
-		if (!haveWork())
-		{
-			llassert(!mDecoding);
-			mDecodedImage = new LLImageRaw(getWidth(), getHeight(), 1);
-			addWork(channel);
-		}
-		return FALSE;
-	}
-	else
-	{
-		llassert(mDecodedImage.notNull());
-		llassert(!mDecoding);
-		raw = mDecodedImage;
-		return TRUE;
-	}
-}
-
-
-// virtual
-void LLImageFormatted::releaseDecodedData()
-{
-	if (mDecoded || mDecoding)
-	{
-		mDecodedImage = NULL; // deletes image
-		mDecoded = FALSE;
-		mDecoding = FALSE;
-	}
-}
-
 //----------------------------------------------------------------------------
 
 // virtual
@@ -1549,52 +1402,42 @@ void LLImageFormatted::sanityCheck()
 
 BOOL LLImageFormatted::copyData(U8 *data, S32 size)
 {
-	if (data && data != getData())
+	if ( (data && data != getData()) || (size != getDataSize()) )
 	{
 		deleteData();
 		allocateData(size);
 		memcpy(getData(), data, size);	/* Flawfinder: ignore */
 	}
-	updateData(); // virtual
-	
 	return TRUE;
 }
 
-BOOL LLImageFormatted::appendData(U8 *data, S32 size)
+// LLImageFormatted becomes the owner of data
+void LLImageFormatted::setData(U8 *data, S32 size)
 {
-	LLMemType mt1((LLMemType::EMemType)mMemType);
-	S32 old_size = getDataSize();
-	U8* old_data = getData();
-	S32 new_size = old_size + size;
-	U8* new_data = new U8[new_size];
-	if (!new_data)
-	{
-		llerrs << "Out of memory in LLImageFormatted::appendData(U8 *data, S32 size)" << llendl;
-		return FALSE;
-	}
-	// resize the image
-	setDataAndSize(new_data, new_size);
-	// copy the old data and delete it
-	memcpy(new_data, old_data, old_size);	/* Flawfinder: ignore */
-	delete old_data;
-	// if we have new data, copy it and call updateData()
-	if (data)
+	if (data && data != getData())
 	{
-		memcpy(new_data + old_size, data, size);	/* Flawfinder: ignore */
-		updateData(); // virtual
+		deleteData();
+		setDataAndSize(data, size); // Access private LLImageBase members
+		sGlobalFormattedMemory += getDataSize();
 	}
-	return TRUE;
 }
 
-BOOL LLImageFormatted::setData(U8 *data, S32 size)
+void LLImageFormatted::appendData(U8 *data, S32 size)
 {
-	if (data && data != getData())
+	if (data)
 	{
-		deleteData();
-		setDataAndSize(data, size); // Access private LLImageBase members
-		sGlobalFormattedMemory += getDataSize();
+		if (!getData())
+		{
+			setData(data, size);
+		}
+		else 
+		{
+			S32 cursize = getDataSize();
+			S32 newsize = cursize + size;
+			reallocateData(newsize);
+			memcpy(getData() + cursize, data, size);
+		}
 	}
-	return updateData(); // virtual
 }
 
 //----------------------------------------------------------------------------
@@ -1667,8 +1510,6 @@ S8 LLImageFormatted::getCodec() const
 
 //============================================================================
 
-//----------------------------------------------------------------------------
-
 static void avg4_colors4(const U8* a, const U8* b, const U8* c, const U8* d, U8* dst)
 {
 	dst[0] = (U8)(((U32)(a[0]) + b[0] + c[0] + d[0])>>2);
@@ -1794,3 +1635,5 @@ F32 LLImageBase::calc_download_priority(F32 virtual_size, F32 visible_pixels, S3
 
 	return w_priority;
 }
+
+//============================================================================
diff --git a/indra/llimage/llimage.h b/indra/llimage/llimage.h
index 13ea916654a91baf3dc7a4d3c0d7422d803772ca..0e007c2c864763c05cd80be00b5d6a3e20f2036c 100644
--- a/indra/llimage/llimage.h
+++ b/indra/llimage/llimage.h
@@ -13,7 +13,7 @@
 #include "lluuid.h"
 #include "llstring.h"
 #include "llmemory.h"
-#include "llworkerthread.h"
+#include "llthread.h"
 
 const S32 MIN_IMAGE_MIP =  2; // 4x4, only used for expand/contract power of 2
 const S32 MAX_IMAGE_MIP = 11; // 2048x2048
@@ -135,8 +135,6 @@ public:
 	/*virtual*/ U8* allocateData(S32 size = -1);
 	/*virtual*/ U8* reallocateData(S32 size);
 	
-	BOOL copyData(U8 *data, U16 width, U16 height, S8 components);
-
 	BOOL resize(U16 width, U16 height, S8 components);
 
 	U8 * getSubImage(U32 x_pos, U32 y_pos, U32 width, U32 height) const;
@@ -206,11 +204,10 @@ public:
 
 // Compressed representation of image.
 // Subclass from this class for the different representations (J2C, bmp)
-class LLImageFormatted : public LLImageBase, public LLWorkerClass
+class LLImageFormatted : public LLImageBase
 {
 public:
-	static void initClass(bool threaded = true, bool run_always = true);
-	static void cleanupClass();
+	static LLImageFormatted* createFromType(S8 codec);
 	static LLImageFormatted* createFromExtension(const LLString& instring);	
 
 protected:
@@ -228,22 +225,11 @@ public:
 	/*virtual*/ void dump();
 	/*virtual*/ void sanityCheck();
 
-	// LLWorkerThread
-public:
-	// called from WORKER THREAD, returns TRUE if done
-	/*virtual*/ bool doWork(S32 param);
-private:
-	// called from MAIN THREAD
-	/*virtual*/ void startWork(S32 param); // called from addWork()
-	/*virtual*/ void endWork(S32 param, bool aborted); // called from doWork()
-
 	// New methods
 public:
 	// calcHeaderSize() returns the maximum size of header;
 	//   0 indicates we don't know have a header and have to lead the entire file
 	virtual S32 calcHeaderSize() { return 0; };
-	// readHeader() reads size bytes into mData, and sets width/height/ncomponents
-	virtual void readHeader(U8* data, S32 size);
 	// calcDataSize() returns how many bytes to read to load discard_level (including header)
 	virtual S32 calcDataSize(S32 discard_level);
 	// calcDiscardLevelBytes() returns the smallest valid discard level based on the number of input bytes
@@ -253,27 +239,16 @@ public:
 	
 	BOOL load(const LLString& filename);
 	BOOL save(const LLString& filename);
-// 	BOOL save(LLVFS *vfs, const LLUUID &uuid, const LLAssetType::EType type);
-//    Depricated to remove VFS dependency (see .cpp for replacement):
 
 	virtual BOOL updateData() = 0; // pure virtual
-	BOOL copyData(U8 *data, S32 size); // calls updateData()
- 	BOOL setData(U8 *data, S32 size); // calls updateData()
-	BOOL appendData(U8 *data, S32 size); // use if some data (e.g header) is already loaded, calls updateData()
+ 	void setData(U8 *data, S32 size);
+ 	void appendData(U8 *data, S32 size);
 
 	// Loads first 4 channels.
 	virtual BOOL decode(LLImageRaw* raw_image, F32 decode_time=0.0) = 0;  
 	// Subclasses that can handle more than 4 channels should override this function.
 	virtual BOOL decode(LLImageRaw* raw_image, F32 decode_time, S32 first_channel, S32 max_channel);
 
-	// Decode methods to return a pointer to raw data for purposes of passing to
-	// opengl or such. This class tracks the decoded data and keeps it alive until
-	// destroyed or releaseDecodedData() is called.
-	virtual BOOL requestDecodedData(LLPointer<LLImageRaw>& raw, S32 discard = -1, F32 decode_time=0.0);
-	virtual BOOL requestDecodedAuxData(LLPointer<LLImageRaw>& raw, S32 channel, 
-									   S32 discard = -1, F32 decode_time=0.0);
-	virtual void releaseDecodedData();
-
 	virtual BOOL encode(const LLImageRaw* raw_image, F32 encode_time=0.0) = 0;
 
 	S8 getCodec() const;
@@ -282,17 +257,17 @@ public:
 	void setDiscardLevel(S8 discard_level) { mDiscardLevel = discard_level; }
 	S8 getDiscardLevel() const { return mDiscardLevel; }
 
+protected:
+	BOOL copyData(U8 *data, S32 size); // calls updateData()
+	
 protected:
 	S8 mCodec;
 	S8 mDecoding;
 	S8 mDecoded;
 	S8 mDiscardLevel;
 
-	LLPointer<LLImageRaw> mDecodedImage;
-	
 public:
 	static S32 sGlobalFormattedMemory;
-	static LLWorkerThread* sWorkerThread;
 };
 
 #endif
diff --git a/indra/llimage/llimagedxt.cpp b/indra/llimage/llimagedxt.cpp
index dfb5b957d34fa7e2b5745492cb38d327945dcc8f..1b6ce6527457595108061c8387ac2b0235a8bb71 100644
--- a/indra/llimage/llimagedxt.cpp
+++ b/indra/llimage/llimagedxt.cpp
@@ -265,8 +265,7 @@ BOOL LLImageDXT::decode(LLImageRaw* raw_image, F32 time)
 	return TRUE;
 }
 
-// virtual
-BOOL LLImageDXT::requestDecodedData(LLPointer<LLImageRaw>& raw, S32 discard, F32 decode_time)
+BOOL LLImageDXT::getMipData(LLPointer<LLImageRaw>& raw, S32 discard)
 {
 	if (discard < 0)
 	{
@@ -283,11 +282,6 @@ BOOL LLImageDXT::requestDecodedData(LLPointer<LLImageRaw>& raw, S32 discard, F32
 	return TRUE;
 }
 
-void LLImageDXT::releaseDecodedData()
-{
-	// nothing to do
-}
-
 BOOL LLImageDXT::encode(const LLImageRaw* raw_image, F32 time, bool explicit_mips)
 {
 	llassert_always(raw_image);
@@ -426,6 +420,7 @@ bool LLImageDXT::convertToDXR()
 	dxtfile_header_t* header = (dxtfile_header_t*)newdata;
 	header->pixel_fmt.fourcc = getFourCC(newformat);
 	setData(newdata, total_bytes);
+	updateData();
 	return true;
 }
 
diff --git a/indra/llimage/llimagedxt.h b/indra/llimage/llimagedxt.h
index 88d28a2958bd1fb7eaf3a7a6821600ed632affef..e286d03e1efab545f534938a0370547595af1d9a 100644
--- a/indra/llimage/llimagedxt.h
+++ b/indra/llimage/llimagedxt.h
@@ -82,12 +82,11 @@ public:
 				BOOL encode(const LLImageRaw* raw_image, F32 time, bool explicit_mips);
 	/*virtual*/ BOOL encode(const LLImageRaw* raw_image, F32 time=0.0);
 
-	/*virtual*/ BOOL requestDecodedData(LLPointer<LLImageRaw>& raw, S32 discard=-1, F32 decode_time=0.0);
-	/*virtual*/ void releaseDecodedData();
-	
 	/*virtual*/ S32 calcHeaderSize();
 	/*virtual*/ S32 calcDataSize(S32 discard_level = 0);
 
+	BOOL getMipData(LLPointer<LLImageRaw>& raw, S32 discard=-1);
+	
 	void setFormat();
 	S32 getMipOffset(S32 discard);
 	
diff --git a/indra/llimage/llimagej2c.cpp b/indra/llimage/llimagej2c.cpp
index 32a8c60facbbb6127d040f4b2f13ce6865ecbd35..398e7c9e32e4da9ae124faf906ac729bf7347fd2 100644
--- a/indra/llimage/llimagej2c.cpp
+++ b/indra/llimage/llimagej2c.cpp
@@ -11,7 +11,7 @@
 
 #include "lldir.h"
 #include "llimagej2c.h"
-#include "llmemory.h"
+#include "llmemtype.h"
 
 typedef LLImageJ2CImpl* (*CreateLLImageJ2CFunction)();
 typedef void (*DestroyLLImageJ2CFunction)(LLImageJ2CImpl*);
@@ -224,7 +224,22 @@ BOOL LLImageJ2C::decode(LLImageRaw *raw_imagep, F32 decode_time, S32 first_chann
 	// Update the raw discard level
 	updateRawDiscardLevel();
 
-	return mImpl->decodeImpl(*this, *raw_imagep, decode_time, first_channel, max_channel_count);
+	mDecoding = TRUE;
+	BOOL res = mImpl->decodeImpl(*this, *raw_imagep, decode_time, first_channel, max_channel_count);
+	if (res)
+	{
+		if (!mDecoding)
+		{
+			// Failed
+			raw_imagep->deleteData();
+		}
+		else
+		{
+			mDecoding = FALSE;
+		}
+		return TRUE; // done
+	}
+	return FALSE;
 }
 
 
@@ -334,7 +349,7 @@ BOOL LLImageJ2C::loadAndValidate(const LLString &filename)
 	U8 *data = new U8[file_size];
 	apr_size_t bytes_read = file_size;
 	apr_status_t s = apr_file_read(apr_file, data, &bytes_read); // modifies bytes_read	
-	if (s != APR_SUCCESS || bytes_read != file_size)
+	if (s != APR_SUCCESS || (S32)bytes_read != file_size)
 	{
 		delete[] data;
 		setLastError("Unable to read entire file");
@@ -349,9 +364,9 @@ BOOL LLImageJ2C::loadAndValidate(const LLString &filename)
 BOOL LLImageJ2C::validate(U8 *data, U32 file_size)
 {
 	LLMemType mt1((LLMemType::EMemType)mMemType);
-	// Taken from setData()
 
-	BOOL res = LLImageFormatted::setData(data, file_size);
+	setData(data, file_size);
+	BOOL res = updateData();
 	if ( !res )
 	{
 		return FALSE;
@@ -367,10 +382,9 @@ BOOL LLImageJ2C::validate(U8 *data, U32 file_size)
 	return mImpl->getMetadata(*this);
 }
 
-void LLImageJ2C::setDecodingDone(BOOL complete)
+void LLImageJ2C::decodeFailed()
 {
 	mDecoding = FALSE;
-	mDecoded = complete;
 }
 
 void LLImageJ2C::updateRawDiscardLevel()
diff --git a/indra/llimage/llimagej2c.h b/indra/llimage/llimagej2c.h
index 4a3b017a55dda363f833feb239e68cd30ebd7b46..1181ae2bb8aaae6258daa5e21b5a0b88c8fcc99d 100644
--- a/indra/llimage/llimagej2c.h
+++ b/indra/llimage/llimagej2c.h
@@ -53,7 +53,7 @@ protected:
 	friend class LLImageJ2CImpl;
 	friend class LLImageJ2COJ;
 	friend class LLImageJ2CKDU;
-	void setDecodingDone(BOOL complete = TRUE);
+	void decodeFailed();
 	void updateRawDiscardLevel();
 
 	S32 mMaxBytes; // Maximum number of bytes of data to use...
diff --git a/indra/llimage/llimageworker.cpp b/indra/llimage/llimageworker.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b44bf19227fed58ec0ebb1ed37cb60ce92f07cd3
--- /dev/null
+++ b/indra/llimage/llimageworker.cpp
@@ -0,0 +1,165 @@
+/** 
+ * @file llimage.cpp
+ * @brief Base class for images.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llimageworker.h"
+#include "llimagedxt.h"
+
+//----------------------------------------------------------------------------
+
+//static
+LLWorkerThread* LLImageWorker::sWorkerThread = NULL;
+S32 LLImageWorker::sCount = 0;
+
+//static
+void LLImageWorker::initClass(LLWorkerThread* workerthread)
+{
+	sWorkerThread = workerthread;
+}
+
+//static
+void LLImageWorker::cleanupClass()
+{
+}
+
+//----------------------------------------------------------------------------
+
+LLImageWorker::LLImageWorker(LLImageFormatted* image, U32 priority, S32 discard, LLResponder* responder)
+	: LLWorkerClass(sWorkerThread, "Image"),
+	  mFormattedImage(image),
+	  mDecodedType(-1),
+	  mDiscardLevel(discard),
+	  mPriority(priority),
+	  mResponder(responder)
+{
+	++sCount;
+}
+
+LLImageWorker::~LLImageWorker()
+{
+	mDecodedImage = NULL;
+	mFormattedImage = NULL;
+	--sCount;
+}
+
+//----------------------------------------------------------------------------
+
+//virtual, main thread
+void LLImageWorker::startWork(S32 param)
+{
+	llassert_always(mDecodedImage.isNull());
+	mDecodedType = -1;
+}
+
+bool LLImageWorker::doWork(S32 param)
+{
+	bool decoded = false;
+	if(mDecodedImage.isNull())
+	{
+		if (!mFormattedImage->updateData())
+		{
+			mDecodedType = -2; // failed
+			return true;
+		}
+		if (mDiscardLevel >= 0)
+		{
+			mFormattedImage->setDiscardLevel(mDiscardLevel);
+		}
+		if (!(mFormattedImage->getWidth() * mFormattedImage->getHeight() * mFormattedImage->getComponents()))
+		{
+			decoded = true; // failed
+		}
+		else
+		{
+			S32 nc = param ? 1 : mFormattedImage->getComponents();
+			mDecodedImage = new LLImageRaw(mFormattedImage->getWidth(),
+										   mFormattedImage->getHeight(),
+										   nc);
+		}
+	}
+	if (!decoded)
+	{
+		if (param == 0)
+		{
+			// Decode primary channels
+			decoded = mFormattedImage->decode(mDecodedImage, .1f); // 1ms
+		}
+		else
+		{
+			// Decode aux channel
+			decoded = mFormattedImage->decode(mDecodedImage, .1f, param, param); // 1ms
+		}
+	}
+	if (decoded)
+	{
+		// Call the callback immediately; endWork doesn't get called until ckeckWork
+		if (mResponder.notNull())
+		{
+			bool success = (!wasAborted() && mDecodedImage.notNull() && mDecodedImage->getDataSize() != 0);
+			mResponder->completed(success);
+		}
+	}
+	return decoded;
+}
+
+void LLImageWorker::endWork(S32 param, bool aborted)
+{
+	if (mDecodedType != -2)
+	{
+		mDecodedType = aborted ? -2 : param;
+	}
+}
+
+//----------------------------------------------------------------------------
+
+
+BOOL LLImageWorker::requestDecodedAuxData(LLPointer<LLImageRaw>& raw, S32 channel, S32 discard)
+{
+	// For most codecs, only mDiscardLevel data is available.
+	//  (see LLImageDXT for exception)
+	if (discard >= 0 && discard != mFormattedImage->getDiscardLevel())
+	{
+		llerrs << "Request for invalid discard level" << llendl;
+	}
+	checkWork();
+	if (mDecodedType == -2)
+	{
+		return TRUE; // aborted, done
+	}
+	if (mDecodedType != channel)
+	{
+		if (!haveWork())
+		{
+			addWork(channel, mPriority);
+		}
+		return FALSE;
+	}
+	else
+	{
+		llassert_always(!haveWork());
+		llassert_always(mDecodedType == channel);
+		raw = mDecodedImage; // smart pointer acquires ownership of data
+		mDecodedImage = NULL;
+		return TRUE;
+	}
+}
+
+BOOL LLImageWorker::requestDecodedData(LLPointer<LLImageRaw>& raw, S32 discard)
+{
+	if (mFormattedImage->getCodec() == IMG_CODEC_DXT)
+	{
+		// special case
+		LLImageDXT* imagedxt = (LLImageDXT*)((LLImageFormatted*)mFormattedImage);
+		return imagedxt->getMipData(raw, discard);
+	}
+	else
+	{
+		return requestDecodedAuxData(raw, 0, discard);
+	}
+}
diff --git a/indra/llimage/llimageworker.h b/indra/llimage/llimageworker.h
new file mode 100644
index 0000000000000000000000000000000000000000..cdd30417cefe357d0028bfe49bc81014cc236ccf
--- /dev/null
+++ b/indra/llimage/llimageworker.h
@@ -0,0 +1,57 @@
+/** 
+ * @file llimageworker.h
+ * @brief Object for managing images and their textures.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLIMAGEWORKER_H
+#define LL_LLIMAGEWORKER_H
+
+#include "llimage.h"
+#include "llworkerthread.h"
+
+class LLImageWorker : public LLWorkerClass
+{
+public:
+	static void initClass(LLWorkerThread* workerthread);
+	static void cleanupClass();
+	static LLWorkerThread* getWorkerThread() { return sWorkerThread; }
+
+	// LLWorkerThread
+public:
+	LLImageWorker(LLImageFormatted* image, U32 priority, S32 discard, LLResponder* responder = NULL);
+	~LLImageWorker();
+
+	// called from WORKER THREAD, returns TRUE if done
+	/*virtual*/ bool doWork(S32 param);
+	
+	BOOL requestDecodedData(LLPointer<LLImageRaw>& raw, S32 discard = -1);
+	BOOL requestDecodedAuxData(LLPointer<LLImageRaw>& raw, S32 channel, S32 discard = -1);
+	void releaseDecodedData();
+	void cancelDecode();
+
+private:
+	// called from MAIN THREAD
+	/*virtual*/ void startWork(S32 param); // called from addWork()
+	/*virtual*/ void endWork(S32 param, bool aborted); // called from doWork()
+
+protected:
+	LLPointer<LLImageFormatted> mFormattedImage;
+	LLPointer<LLImageRaw> mDecodedImage;
+	S32 mDecodedType;
+	S32 mDiscardLevel;
+
+private:
+	U32 mPriority;
+	LLPointer<LLResponder> mResponder;
+	
+protected:
+	static LLWorkerThread* sWorkerThread;
+
+public:
+	static S32 sCount;
+};
+
+#endif
diff --git a/indra/llimagej2coj/llimagej2coj.cpp b/indra/llimagej2coj/llimagej2coj.cpp
index 823d2a8dd49dfc6f6b0cafb8f0f26f7dd87bdf49..a6c7c623f3d48ce545e575fb76c671fa6d9aedff 100644
--- a/indra/llimagej2coj/llimagej2coj.cpp
+++ b/indra/llimagej2coj/llimagej2coj.cpp
@@ -151,7 +151,6 @@ BOOL LLImageJ2COJ::decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 decod
 	/* free image data structure */
 	opj_image_destroy(image);
 
-	base.setDecodingDone();
 	return TRUE;
 }
 
@@ -262,6 +261,7 @@ BOOL LLImageJ2COJ::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, con
 	codestream_length = cio_tell(cio);
 
 	base.copyData(cio->buffer, codestream_length);
+	base.updateData(); // set width, height
 
 	/* close and free the byte stream */
 	opj_cio_close(cio);
diff --git a/indra/llinventory/llinventory.h b/indra/llinventory/llinventory.h
index 9f750c46b25ca6a9486ed2b04510d5156170e509..8a6fdfd0adca14fe00b6806daca393d7e9a50e07 100644
--- a/indra/llinventory/llinventory.h
+++ b/indra/llinventory/llinventory.h
@@ -13,6 +13,7 @@
 
 #include "llassetstorage.h"
 #include "lldarray.h"
+#include "llmemtype.h"
 #include "llpermissions.h"
 #include "llsaleinfo.h"
 #include "llsd.h"
diff --git a/indra/llmath/lloctree.h b/indra/llmath/lloctree.h
index bea21e22c645e091dc0370ccc797b87e59e6f0a0..e5b2a5f3126efdff680f030568c3b4a237eaf653 100644
--- a/indra/llmath/lloctree.h
+++ b/indra/llmath/lloctree.h
@@ -21,6 +21,7 @@
 #endif
 
 #define LL_OCTREE_PARANOIA_CHECK 0
+#define LL_OCTREE_MAX_CAPACITY 256
 
 template <class T> class LLOctreeState;
 template <class T> class LLOctreeNode;
@@ -88,7 +89,8 @@ public:
 	virtual void removeByAddress(T* data)				{ getOctState()->removeByAddress(data); }
 	virtual bool hasLeafState()	const					{ return getOctState()->isLeaf(); }
 	virtual void destroy()								{ getOctState()->destroy(); }
-	virtual oct_node* getNodeAt(T* data)				{ return getOctState()->getNodeAt(data); }
+	virtual oct_node* getNodeAt(T* data)				{ return getNodeAt(data->getPositionGroup(), data->getBinRadius()); }
+	virtual oct_node* getNodeAt(const LLVector3d& pos, const F64& rad) { return getOctState()->getNodeAt(pos, rad); }
 	virtual U8 getOctant() const						{ return mOctant; }
 	virtual void setOctant(U8 octant)					{ mOctant = octant; }
 	virtual const oct_state* getOctState() const		{ return (const oct_state*) BaseType::mState; }
@@ -117,9 +119,14 @@ public:
 		return ret;
 	}
 	
+	virtual bool isInside(const LLVector3d& pos, const F64& rad) const
+	{
+		return rad <= mSize.mdV[0]*2.0 && isInside(pos); 
+	}
+
 	virtual bool isInside(T* data) const			
 	{ 
-		return data->getBinRadius() <= mSize.mdV[0]*2.0 && isInside(data->getPositionGroup()); 
+		return isInside(data->getPositionGroup(), data->getBinRadius());
 	}
 
 	virtual bool isInside(const LLVector3d& pos) const
@@ -154,6 +161,11 @@ public:
 	}
 
 	bool contains(T* xform)
+	{
+		return contains(xform->getBinRadius());
+	}
+
+	bool contains(F64 radius)
 	{
 		if (mParent == NULL)
 		{	//root node contains nothing
@@ -162,7 +174,6 @@ public:
 
 		F64 size = mSize.mdV[0];
 		F64 p_size = size * 2.0;
-		F64 radius = xform->getBinRadius();
 
 		return (radius <= 0.001 && size <= 0.001) ||
 				(radius <= p_size && radius > size);
@@ -247,11 +258,15 @@ public:
 	oct_node* getOctNode()									{ return (oct_node*) BaseType::getNode(); }
 
 	virtual oct_node* getNodeAt(T* data)
+	{
+		return getNodeAt(data->getPositionGroup(), data->getBinRadius());
+	}
+
+	virtual oct_node* getNodeAt(const LLVector3d& pos, const F64& rad)
 	{ 
-		const LLVector3d& pos = data->getPositionGroup();
 		LLOctreeNode<T>* node = getOctNode();
 
-		if (node->isInside(data))
+		if (node->isInside(pos, rad))
 		{		
 			//do a quick search by octant
 			U8 octant = node->getOctant(pos.mdV);
@@ -261,7 +276,7 @@ public:
 			//at the appropriate octant or is smaller than the object.  
 			//by definition, that node is the smallest node that contains 
 			// the data
-			while (keep_going && node->getSize().mdV[0] >= data->getBinRadius())
+			while (keep_going && node->getSize().mdV[0] >= rad)
 			{	
 				keep_going = FALSE;
 				for (U32 i = 0; i < node->getChildCount() && !keep_going; i++)
@@ -275,9 +290,9 @@ public:
 				}
 			}
 		}
-		else if (!node->contains(data) && node->getParent())
+		else if (!node->contains(rad) && node->getParent())
 		{ //if we got here, data does not exist in this node
-			return ((LLOctreeNode<T>*) node->getParent())->getNodeAt(data);
+			return ((LLOctreeNode<T>*) node->getParent())->getNodeAt(pos, rad);
 		}
 
 		return node;
@@ -291,22 +306,15 @@ public:
 			return false;
 		}
 		LLOctreeNode<T>* node = getOctNode();
+		LLOctreeNode<T>* parent = node->getOctParent();
 
-		if (data->getBinRadius() <= node->getSize().mdV[0])
-		{
-			oct_node* dest = getNodeAt(data);
-
-			if (dest != node)
-			{
-				dest->insert(data);
-				return false;
-			}
-		}
-
-		//no kid found, is it even here?
-		if (node->isInside(data))
+		//is it here?
+		if (node->isInside(data->getPositionGroup()))
 		{
-			if (node->contains(data))
+			if (getElementCount() < LL_OCTREE_MAX_CAPACITY &&
+				(node->contains(data->getBinRadius()) ||
+				(data->getBinRadius() > node->getSize().mdV[0] &&
+				parent && parent->getElementCount() >= LL_OCTREE_MAX_CAPACITY))) 
 			{ //it belongs here
 				if (data == NULL)
 				{
@@ -327,7 +335,19 @@ public:
 				return true;
 			}
 			else
-			{ 		
+			{ 	
+				//find a child to give it to
+				oct_node* child = NULL;
+				for (U32 i = 0; i < getChildCount(); i++)
+				{
+					child = getChild(i);
+					if (child->isInside(data->getPositionGroup()))
+					{
+						child->insert(data);
+						return false;
+					}
+				}
+				
 				//it's here, but no kids are in the right place, make a new kid
 				LLVector3d center(node->getCenter());
 				LLVector3d size(node->getSize()*0.5);
@@ -359,7 +379,7 @@ public:
 
 				//make the new kid
 				LLOctreeState<T>* newstate = new LLOctreeState<T>();
-				oct_node* child = new LLOctreeNode<T>(center, size, newstate, node);
+				child = new LLOctreeNode<T>(center, size, newstate, node);
 				addChild(child);
 
 				child->insert(data);
@@ -367,8 +387,15 @@ public:
 		}
 		else 
 		{
-			//it's not in here, give it to the parent
-			node->getOctParent()->insert(data);
+			//it's not in here, give it to the root
+			LLOctreeNode<T>* parent = node->getOctParent();
+			while (parent)
+			{
+				node = parent;
+				parent = node->getOctParent();
+			}
+
+			node->insert(data);
 		}
 
 		return false;
diff --git a/indra/llmath/lltreenode.h b/indra/llmath/lltreenode.h
index dd0c73c00cddcd8a15e40103c936e1db1653aaaa..cdc52cf90ac3a346593835fdece12a6fc07dd4fc 100644
--- a/indra/llmath/lltreenode.h
+++ b/indra/llmath/lltreenode.h
@@ -22,7 +22,6 @@ class LLTreeState
 public:
 	LLTreeState(LLTreeNode<T>* node)				{ setNode(node); }
 	virtual ~LLTreeState() { };
-	
 	virtual bool insert(T* data) = 0;
 	virtual bool remove(T* data) = 0;
 	virtual void setNode(LLTreeNode<T>* node);
@@ -35,7 +34,7 @@ private:
 };
 
 template <class T>
-class LLTreeListener
+class LLTreeListener: public LLRefCount
 {
 public:
 	virtual ~LLTreeListener() { };
@@ -75,7 +74,7 @@ protected:
 	
 	LLTreeState<T>* mState;
 public:
-	std::vector<LLTreeListener<T>*> mListeners;
+	std::vector<LLPointer<LLTreeListener<T> > > mListeners;
 };
 
 template <class T>
diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp
index 75e4042f077c5385d8885e0fd46e55b258ba2be1..5354de783cf5013ab5de96e1ed94ba4a99d4e7fb 100644
--- a/indra/llmath/llvolume.cpp
+++ b/indra/llmath/llvolume.cpp
@@ -1765,9 +1765,6 @@ void LLVolume::createVolumeFaces()
 			mVolumeFaces[i].create();
 		}
 	}
-
-	mBounds[1] = LLVector3(0,0,0);
-	mBounds[0] = LLVector3(512,512,512);
 }
 
 
@@ -1812,21 +1809,28 @@ void LLVolumeParams::copyParams(const LLVolumeParams &params)
 	mPathParams.copyParams(params.mPathParams);
 }
 
+// Less restricitve approx 0 for volumes
+const F32 APPROXIMATELY_ZERO = 0.001f;
+bool approx_zero( F32 f, F32 tolerance = APPROXIMATELY_ZERO)
+{
+	return (f >= -tolerance) && (f <= tolerance);
+}
+
 // return true if in range (or nearly so)
-static bool limit_range(F32& v, F32 min, F32 max)
+static bool limit_range(F32& v, F32 min, F32 max, F32 tolerance = APPROXIMATELY_ZERO)
 {
 	F32 min_delta = v - min;
 	if (min_delta < 0.f)
 	{
 		v = min;
-		if (!is_approx_zero(min_delta))
+		if (!approx_zero(min_delta, tolerance))
 			return false;
 	}
 	F32 max_delta = max - v;
 	if (max_delta < 0.f)
 	{
 		v = max;
-		if (!is_approx_zero(max_delta))
+		if (!approx_zero(max_delta, tolerance))
 			return false;
 	}
 	return true;
@@ -1841,9 +1845,10 @@ bool LLVolumeParams::setBeginAndEndS(const F32 b, const F32 e)
 	valid &= limit_range(begin, 0.f, 1.f - MIN_CUT_DELTA);
 
 	F32 end = e;
+	if (end >= .0149f && end < MIN_CUT_DELTA) end = MIN_CUT_DELTA; // eliminate warning for common rounding error
 	valid &= limit_range(end, MIN_CUT_DELTA, 1.f);
 
-	valid &= limit_range(begin, 0.f, end - MIN_CUT_DELTA);
+	valid &= limit_range(begin, 0.f, end - MIN_CUT_DELTA, .01f);
 
 	// Now set them.
 	mProfileParams.setBegin(begin);
@@ -1863,7 +1868,7 @@ bool LLVolumeParams::setBeginAndEndT(const F32 b, const F32 e)
 	F32 end = e;
 	valid &= limit_range(end, MIN_CUT_DELTA, 1.f);
 
-	valid &= limit_range(begin, 0.f, end - MIN_CUT_DELTA);
+	valid &= limit_range(begin, 0.f, end - MIN_CUT_DELTA, .01f);
 
 	// Now set them.
 	mPathParams.setBegin(begin);
@@ -2020,7 +2025,7 @@ bool LLVolumeParams::setRadiusOffset(const F32 offset)
 		{
 			radius_offset = max_radius_mag;
 		}
-		valid = is_approx_zero(delta);
+		valid = approx_zero(delta, .1f);
 	}
 
 	mPathParams.setRadiusOffset(radius_offset);
@@ -2054,7 +2059,7 @@ bool LLVolumeParams::setSkew(const F32 skew_value)
 		{
 			skew = min_skew_mag;
 		}
-		valid = is_approx_zero(delta);
+		valid = approx_zero(delta);
 	}
 
 	mPathParams.setSkew(skew);
@@ -2980,10 +2985,15 @@ void LLVolume::generateSilhouetteVertices(std::vector<LLVector3> &vertices,
 						S32 v2 = face.mIndices[j*3+((k+1)%3)];
 						
 						vertices.push_back(face.mVertices[v1].mPosition*mat);
-						normals.push_back(face.mVertices[v1].mNormal*norm_mat);
+						LLVector3 norm1 = face.mVertices[v1].mNormal * norm_mat;
+						norm1.normVec();
+						normals.push_back(norm1);
 
 						vertices.push_back(face.mVertices[v2].mPosition*mat);
-						normals.push_back(face.mVertices[v2].mNormal*norm_mat);
+						LLVector3 norm2 = face.mVertices[v2].mNormal * norm_mat;
+						norm2.normVec();
+						normals.push_back(norm2);
+
 						segments.push_back(vertices.size());
 					}
 				}		
@@ -3747,6 +3757,9 @@ BOOL LLVolumeFace::createUnCutCubeCap()
 	num_vertices = (grid_size+1)*(grid_size+1);
 	num_indices = quad_count * 4;
 
+	LLVector3& min = mExtents[0];
+	LLVector3& max = mExtents[1];
+
 	S32 offset = 0;
 	if (mTypeMask & TOP_MASK)
 		offset = (max_t-1) * max_s;
@@ -3786,32 +3799,6 @@ BOOL LLVolumeFace::createUnCutCubeCap()
 	}
 
 	S32	vtop = mVertices.size();
-//	S32	itop = mIndices.size();
-///	vector_append(mVertices,4);
-//	vector_append(mIndices,4);
-//	LLVector3 new_pt = lerp(pt1, pt2, t_fraction);
-#if 0
-	for(int t=0;t<4;t++){
-		VertexData vd;
-		vd.mPosition = corners[t].mPosition;
-		vd.mNormal = 
-			((corners[(t+1)%4].mPosition-corners[t].mPosition)%
-			 (corners[(t+2)%4].mPosition-corners[(t+1)%4].mPosition));
-		vd.mNormal.normVec();
-		
-		if (mTypeMask & TOP_MASK)
-			vd.mNormal *= -1.0f;
-		vd.mBinormal = vd.mNormal;
-		vd.mTexCoord = corners[t].mTexCoord;
-		mVertices.push_back(vd);
-	}
-	int idxs[] = {0,1,2,2,3,0};
-	if (mTypeMask & TOP_MASK){
-		for(int i=0;i<6;i++)mIndices.push_back(vtop+idxs[i]);
-	}else{
-		for(int i=5;i>=0;i--)mIndices.push_back(vtop+idxs[i]);
-	}
-#else
 	for(int gx = 0;gx<grid_size+1;gx++){
 		for(int gy = 0;gy<grid_size+1;gy++){
 			VertexData newVert;
@@ -3823,8 +3810,20 @@ BOOL LLVolumeFace::createUnCutCubeCap()
 				(F32)gx/(F32)grid_size,
 				(F32)gy/(F32)grid_size);
 			mVertices.push_back(newVert);
+
+			if (gx == 0 && gy == 0)
+			{
+				min = max = newVert.mPosition;
+			}
+			else
+			{
+				update_min_max(min,max,newVert.mPosition);
+			}
 		}
 	}
+	
+	mCenter = (min + max) * 0.5f;
+
 	int idxs[] = {0,1,(grid_size+1)+1,(grid_size+1)+1,(grid_size+1),0};
 	for(int gx = 0;gx<grid_size;gx++){
 		for(int gy = 0;gy<grid_size;gy++){
@@ -3835,7 +3834,7 @@ BOOL LLVolumeFace::createUnCutCubeCap()
 			}
 		}
 	}
-#endif
+	
 	return TRUE;
 }
 
@@ -3882,12 +3881,15 @@ BOOL LLVolumeFace::createCap()
 	// Figure out the normal, assume all caps are flat faces.
 	// Cross product to get normals.
 	
-	LLVector2 cuv = LLVector2(0,0);
-	
+	LLVector2 cuv;
+	LLVector2 min_uv, max_uv;
+
+	LLVector3& min = mExtents[0];
+	LLVector3& max = mExtents[1];
+
 	// Copy the vertices into the array
 	for (i = 0; i < num_vertices; i++)
 	{
-
 		if (mTypeMask & TOP_MASK)
 		{
 			mVertices[i].mTexCoord.mV[0] = profile[i].mV[0]+0.5f;
@@ -3900,17 +3902,22 @@ BOOL LLVolumeFace::createCap()
 			mVertices[i].mTexCoord.mV[1] = 0.5f - profile[i].mV[1];
 		}
 
-		if(i){
-			//Dont include the first point of the profile in the average
-			cuv += mVertices[i].mTexCoord;
-			mCenter += mVertices[i].mPosition = mesh[i + offset].mPos;
+		mVertices[i].mPosition = mesh[i + offset].mPos;
+		
+		if (i == 0)
+		{
+			min = max = mVertices[i].mPosition;
+			min_uv = max_uv = mVertices[i].mTexCoord;
+		}
+		else
+		{
+			update_min_max(min,max, mVertices[i].mPosition);
+			update_min_max(min_uv, max_uv, mVertices[i].mTexCoord);
 		}
-		else mVertices[i].mPosition = mesh[i + offset].mPos;
-		//mVertices[i].mNormal = normal;
 	}
 
-	mCenter /= (F32)(num_vertices-1);
-	cuv /= (F32)(num_vertices-1);
+	mCenter = (min+max)*0.5f;
+	cuv = (min_uv + max_uv)*0.5f;
 
 	LLVector3 binormal = calc_binormal_from_triangle( 
 		mCenter, cuv,
@@ -4215,13 +4222,11 @@ BOOL LLVolumeFace::createCap()
 	return TRUE;
 }
 
-
 BOOL LLVolumeFace::createSide()
 {
 	BOOL flat = mTypeMask & FLAT_MASK;
 	S32 num_vertices, num_indices;
 
-
 	const std::vector<LLVolume::Point>& mesh = mVolumep->getMesh();
 	const std::vector<LLVector3>& profile = mVolumep->getProfile().mProfile;
 	const std::vector<LLPath::PathPt>& path_data = mVolumep->getPath().mPath;
@@ -4237,6 +4242,9 @@ BOOL LLVolumeFace::createSide()
 	vector_append(mIndices,num_indices);
 	vector_append(mEdge, num_indices);
 
+	LLVector3& face_min = mExtents[0];
+	LLVector3& face_max = mExtents[1];
+
 	mCenter.clearVec();
 
 	S32 begin_stex = llfloor( profile[mBeginS].mV[2] );
@@ -4284,17 +4292,26 @@ BOOL LLVolumeFace::createSide()
 				i = mBeginS + s + max_s*t;
 			}
 
-			mCenter += mVertices[cur_vertex].mPosition = mesh[i].mPos;
+			mVertices[cur_vertex].mPosition = mesh[i].mPos;
 			mVertices[cur_vertex].mTexCoord = LLVector2(ss,tt);
 		
 			mVertices[cur_vertex].mNormal = LLVector3(0,0,0);
 			mVertices[cur_vertex].mBinormal = LLVector3(0,0,0);
 			
+			if (cur_vertex == 0)
+			{
+				face_min = face_max = mesh[i].mPos;
+			}
+			else
+			{
+				update_min_max(face_min, face_max, mesh[i].mPos);
+			}
+
 			cur_vertex++;
 
 			if ((mTypeMask & INNER_MASK) && (mTypeMask & FLAT_MASK) && mNumS > 2 && s > 0)
 			{
-				mCenter += mVertices[cur_vertex].mPosition = mesh[i].mPos;
+				mVertices[cur_vertex].mPosition = mesh[i].mPos;
 				mVertices[cur_vertex].mTexCoord = LLVector2(ss,tt);
 			
 				mVertices[cur_vertex].mNormal = LLVector3(0,0,0);
@@ -4316,15 +4333,19 @@ BOOL LLVolumeFace::createSide()
 
 			i = mBeginS + s + max_s*t;
 			ss = profile[mBeginS + s].mV[2] - begin_stex;
-			mCenter += mVertices[cur_vertex].mPosition = mesh[i].mPos;
+			mVertices[cur_vertex].mPosition = mesh[i].mPos;
 			mVertices[cur_vertex].mTexCoord = LLVector2(ss,tt);
 		
 			mVertices[cur_vertex].mNormal = LLVector3(0,0,0);
 			mVertices[cur_vertex].mBinormal = LLVector3(0,0,0);
+
+			update_min_max(face_min,face_max,mesh[i].mPos);
+
 			cur_vertex++;
 		}
 	}
-	mCenter /= (F32)num_vertices;
+	
+	mCenter = (face_min + face_max) * 0.5f;
 
 	S32 cur_index = 0;
 	S32 cur_edge = 0;
@@ -4448,32 +4469,14 @@ BOOL LLVolumeFace::createSide()
 		}
 	}
 
-	//this loop would LOVE OpenMP
-	LLVector3 min = mVolumep->mBounds[0] - mVolumep->mBounds[1];
-	LLVector3 max = mVolumep->mBounds[0] + mVolumep->mBounds[1];
-
-	if (min == max && min == LLVector3(512,512,512))
+	//normalize normals and binormals here so the meshes that reference
+	//this volume data don't have to
+	for (U32 i = 0; i < mVertices.size(); i++) 
 	{
-		min = max = mVertices[0].mPosition;
-	}
-
-	for (U32 i = 0; i < mVertices.size(); i++) {
 		mVertices[i].mNormal.normVec();
 		mVertices[i].mBinormal.normVec();
-
-		for (U32 j = 0; j < 3; j++) {
-			if (mVertices[i].mPosition.mV[j] > max.mV[j]) {
-				max.mV[j] = mVertices[i].mPosition.mV[j];
-			}
-			if (mVertices[i].mPosition.mV[j] < min.mV[j]) {
-				min.mV[j] = mVertices[i].mPosition.mV[j];
-			}
-		}
 	}
 
-	mVolumep->mBounds[0] = (min + max) * 0.5f; //center
-	mVolumep->mBounds[1] = (max - min) * 0.5f; //half-height
-
 	return TRUE;
 }
 
@@ -4572,7 +4575,7 @@ LLVector3 calc_binormal_from_triangle(
 				-r0.mV[VZ] / r0.mV[VX],
 				-r1.mV[VZ] / r1.mV[VX],
 				-r2.mV[VZ] / r2.mV[VX]);
-		//binormal.normVec();
+		// binormal.normVec();
 		return binormal;
 	}
 	else
diff --git a/indra/llmath/llvolume.h b/indra/llmath/llvolume.h
index cf9ae00f21c4689274e0803fc1cf0819fec66a36..97aeeb9589f150c5b5949528deaa183a6a9b222f 100644
--- a/indra/llmath/llvolume.h
+++ b/indra/llmath/llvolume.h
@@ -756,6 +756,8 @@ public:
 	S32 mNumS;
 	S32 mNumT;
 
+	LLVector3 mExtents[2]; //minimum and maximum point of face
+
 	std::vector<VertexData> mVertices;
 	std::vector<S32>	mIndices;
 	std::vector<S32>	mEdge;
@@ -849,7 +851,6 @@ public:
 	const LLVolumeFace &getVolumeFace(const S32 f) const {return mVolumeFaces[f];} // DO NOT DELETE VOLUME WHILE USING THIS REFERENCE, OR HOLD A POINTER TO THIS VOLUMEFACE
 
 	U32					mFaceMask;			// bit array of which faces exist in this volume
-	LLVector3			mBounds[2];			// bounding box (center, half-height)
 	LLVector3			mLODScaleBias;		// vector for biasing LOD based on scale
 
 protected:
diff --git a/indra/llmath/m3math.cpp b/indra/llmath/m3math.cpp
index 523acb4dcfe4230ea0299f08a2c150025e7fa8b2..6483f2adbe76b255ea134f73aa048505ab145383 100644
--- a/indra/llmath/m3math.cpp
+++ b/indra/llmath/m3math.cpp
@@ -33,14 +33,14 @@
 
 LLMatrix3::LLMatrix3(const LLQuaternion &q)
 {
-	*this = q.getMatrix3();
+	setRot(q);
 }
 
 
 LLMatrix3::LLMatrix3(const F32 angle, const LLVector3 &vec)
 {
 	LLQuaternion	quat(angle, vec);
-	*this = setRot(quat);
+	setRot(quat);
 }
 
 LLMatrix3::LLMatrix3(const F32 angle, const LLVector3d &vec)
@@ -48,60 +48,25 @@ LLMatrix3::LLMatrix3(const F32 angle, const LLVector3d &vec)
 	LLVector3 vec_f;
 	vec_f.setVec(vec);
 	LLQuaternion	quat(angle, vec_f);
-	*this = setRot(quat);
+	setRot(quat);
 }
 
 LLMatrix3::LLMatrix3(const F32 angle, const LLVector4 &vec)
 {
 	LLQuaternion	quat(angle, vec);
-	*this = setRot(quat);
+	setRot(quat);
 }
 
 LLMatrix3::LLMatrix3(const F32 angle, const F32 x, const F32 y, const F32 z)
 {
 	LLVector3 vec(x, y, z);
 	LLQuaternion	quat(angle, vec);
-	*this = setRot(quat);
+	setRot(quat);
 }
 
 LLMatrix3::LLMatrix3(const F32 roll, const F32 pitch, const F32 yaw)
 {
-	// Rotates RH about x-axis by 'roll'  then
-	// rotates RH about the old y-axis by 'pitch' then
-	// rotates RH about the original z-axis by 'yaw'.
-	//                .
-	//               /|\ yaw axis
-	//                |     __.
-	//   ._        ___|      /| pitch axis
-	//  _||\       \\ |-.   /
-	//  \|| \_______\_|__\_/_______
-	//   | _ _   o o o_o_o_o o   /_\_  ________\ roll axis
-	//   //  /_______/    /__________>         /   
-	//  /_,-'       //   /
-	//             /__,-'
-
-	F32		cx, sx, cy, sy, cz, sz;
-	F32		cxsy, sxsy;
-
-    cx = (F32)cos(roll); //A
-    sx = (F32)sin(roll); //B
-    cy = (F32)cos(pitch); //C
-    sy = (F32)sin(pitch); //D
-    cz = (F32)cos(yaw); //E
-    sz = (F32)sin(yaw); //F
-
-    cxsy = cx * sy; //AD
-    sxsy = sx * sy; //BD 
-
-    mMatrix[0][0] =  cy * cz;
-    mMatrix[1][0] = -cy * sz;
-    mMatrix[2][0] = sy;
-    mMatrix[0][1] = sxsy * cz + cx * sz;
-    mMatrix[1][1] = -sxsy * sz + cx * cz;
-    mMatrix[2][1] = -sx * cy;
-    mMatrix[0][2] =  -cxsy * cz + sx * sz;
-    mMatrix[1][2] =  cxsy * sz + sx * cz;
-    mMatrix[2][2] =  cx * cy;
+	setRot(roll,pitch,yaw);
 }
 
 // From Matrix and Quaternion FAQ
@@ -288,34 +253,64 @@ LLQuaternion	LLMatrix3::quaternion() const
 // These functions take Rotation arguments
 const LLMatrix3&	LLMatrix3::setRot(const F32 angle, const F32 x, const F32 y, const F32 z)
 {
-	LLMatrix3	mat(angle, x, y, z);
-	*this = mat;
+	setRot(LLQuaternion(angle,x,y,z));
 	return *this;
 }
 
 const LLMatrix3&	LLMatrix3::setRot(const F32 angle, const LLVector3 &vec)
 {
-	LLMatrix3	mat(angle, vec);
-	*this = mat;
+	setRot(LLQuaternion(angle, vec));
 	return *this;
 }
 
 const LLMatrix3&	LLMatrix3::setRot(const F32 roll, const F32 pitch, const F32 yaw)
 {
-	LLMatrix3	mat(roll, pitch, yaw); 
-	*this = mat;
+	// Rotates RH about x-axis by 'roll'  then
+	// rotates RH about the old y-axis by 'pitch' then
+	// rotates RH about the original z-axis by 'yaw'.
+	//                .
+	//               /|\ yaw axis
+	//                |     __.
+	//   ._        ___|      /| pitch axis
+	//  _||\       \\ |-.   /
+	//  \|| \_______\_|__\_/_______
+	//   | _ _   o o o_o_o_o o   /_\_  ________\ roll axis
+	//   //  /_______/    /__________>         /   
+	//  /_,-'       //   /
+	//             /__,-'
+
+	F32		cx, sx, cy, sy, cz, sz;
+	F32		cxsy, sxsy;
+
+    cx = (F32)cos(roll); //A
+    sx = (F32)sin(roll); //B
+    cy = (F32)cos(pitch); //C
+    sy = (F32)sin(pitch); //D
+    cz = (F32)cos(yaw); //E
+    sz = (F32)sin(yaw); //F
+
+    cxsy = cx * sy; //AD
+    sxsy = sx * sy; //BD 
+
+    mMatrix[0][0] =  cy * cz;
+    mMatrix[1][0] = -cy * sz;
+    mMatrix[2][0] = sy;
+    mMatrix[0][1] = sxsy * cz + cx * sz;
+    mMatrix[1][1] = -sxsy * sz + cx * cz;
+    mMatrix[2][1] = -sx * cy;
+    mMatrix[0][2] =  -cxsy * cz + sx * sz;
+    mMatrix[1][2] =  cxsy * sz + sx * cz;
+    mMatrix[2][2] =  cx * cy;
 	return *this;
 }
 
 
 const LLMatrix3&	LLMatrix3::setRot(const LLQuaternion &q)
 {
-	LLMatrix3	mat(q);
-	*this = mat;
+	*this = q.getMatrix3();
 	return *this;
 }
 
-
 const LLMatrix3&	LLMatrix3::setRows(const LLVector3 &fwd, const LLVector3 &left, const LLVector3 &up)
 {
 	mMatrix[0][0] = fwd.mV[0];
diff --git a/indra/llmath/m3math.h b/indra/llmath/m3math.h
index ac93ed86fb5a2805ef9a5781d63c4ffdd710bdc5..c3798fca73f78d1211f973a2baad183996ecc41f 100644
--- a/indra/llmath/m3math.h
+++ b/indra/llmath/m3math.h
@@ -32,9 +32,6 @@ class LLQuaternion;
 
 
 static const U32 NUM_VALUES_IN_MAT3	= 3;
-#if LL_WINDOWS
-__declspec( align(16) )
-#endif
 class LLMatrix3
 {
 	public:
@@ -119,11 +116,7 @@ class LLMatrix3
 		friend const LLMatrix3& operator*=(LLMatrix3 &a, const LLMatrix3 &b);				// Return a * b
 
 		friend std::ostream&	 operator<<(std::ostream& s, const LLMatrix3 &a);	// Stream a
-}
-#if LL_DARWIN
-__attribute__ ((aligned (16)))
-#endif
-;
+};
 
 inline LLMatrix3::LLMatrix3(void)
 {
diff --git a/indra/llmath/m4math.h b/indra/llmath/m4math.h
index 4f26d875ff194f8ae82232f70d3b8d2ac5f30790..69463d7718acc686e143eccf6b6a60c67b1ccf66 100644
--- a/indra/llmath/m4math.h
+++ b/indra/llmath/m4math.h
@@ -73,9 +73,6 @@ class LLQuaternion;
 
 static const U32 NUM_VALUES_IN_MAT4 = 4;
 
-#if LL_WINDOWS
-__declspec(align(16))
-#endif
 class LLMatrix4
 {
 public:
@@ -218,11 +215,7 @@ public:
 	friend const LLMatrix4& operator*=(LLMatrix4 &a, const F32 &b);			// Return a * b
 
 	friend std::ostream&	 operator<<(std::ostream& s, const LLMatrix4 &a);	// Stream a
-}
-#if LL_DARWIN
-__attribute__ ((aligned (16)))
-#endif
-;
+};
 
 
 inline LLMatrix4::LLMatrix4()
diff --git a/indra/llmath/v2math.h b/indra/llmath/v2math.h
index c94333e2a9b7d50e9cd5a7c1d2fb82ba1ac0beca..863318551e4f2dba9aa13f94a5bfe73252485e8f 100644
--- a/indra/llmath/v2math.h
+++ b/indra/llmath/v2math.h
@@ -298,6 +298,21 @@ inline LLVector2 operator-(const LLVector2 &a)
 	return LLVector2( -a.mV[0], -a.mV[1] );
 }
 
+inline void update_min_max(LLVector2& min, LLVector2& max, const LLVector2& pos)
+{
+	for (U32 i = 0; i < 2; i++)
+	{
+		if (min.mV[i] > pos.mV[i])
+		{
+			min.mV[i] = pos.mV[i];
+		}
+		if (max.mV[i] < pos.mV[i])
+		{
+			max.mV[i] = pos.mV[i];
+		}
+	}
+}
+
 inline std::ostream& operator<<(std::ostream& s, const LLVector2 &a) 
 {
 	s << "{ " << a.mV[VX] << ", " << a.mV[VY] << " }";
diff --git a/indra/llmath/v3math.cpp b/indra/llmath/v3math.cpp
index 9e6084565ed3ed740b52fd431c530cbda9793429..c68e72bb971b8eb3de53630e09cf7537962a8a98 100644
--- a/indra/llmath/v3math.cpp
+++ b/indra/llmath/v3math.cpp
@@ -118,7 +118,7 @@ const LLVector3&	LLVector3::rotVec(F32 angle, const LLVector3 &vec)
 {
 	if ( !vec.isExactlyZero() && angle )
 	{
-		*this = *this * LLMatrix3(angle, vec);
+		*this = *this * LLQuaternion(angle, vec);
 	}
 	return *this;
 }
@@ -128,7 +128,7 @@ const LLVector3&	LLVector3::rotVec(F32 angle, F32 x, F32 y, F32 z)
 	LLVector3 vec(x, y, z);
 	if ( !vec.isExactlyZero() && angle )
 	{
-		*this = *this * LLMatrix3(angle, vec);
+		*this = *this * LLQuaternion(angle, vec);
 	}
 	return *this;
 }
diff --git a/indra/llmath/v3math.h b/indra/llmath/v3math.h
index 72372c07e97b25bee25e9d6e7fb02abb9afdf958..f787fa2ac171b78d6af8943f7a08df2e0f2c7379 100644
--- a/indra/llmath/v3math.h
+++ b/indra/llmath/v3math.h
@@ -396,6 +396,20 @@ inline BOOL	LLVector3::isNull() const
 	return FALSE;
 }
 
+inline void update_min_max(LLVector3& min, LLVector3& max, const LLVector3& pos)
+{
+	for (U32 i = 0; i < 3; i++)
+	{
+		if (min.mV[i] > pos.mV[i])
+		{
+			min.mV[i] = pos.mV[i];
+		}
+		if (max.mV[i] < pos.mV[i])
+		{
+			max.mV[i] = pos.mV[i];
+		}
+	}
+}
 
 inline F32 angle_between(const LLVector3& a, const LLVector3& b)
 {
diff --git a/indra/llmath/v4math.h b/indra/llmath/v4math.h
index abdc66e0b1828710b244d288abb77168a0a2a793..e9b8d5b71f71f64270fe556db47a9486ae4201e9 100644
--- a/indra/llmath/v4math.h
+++ b/indra/llmath/v4math.h
@@ -21,10 +21,6 @@ class LLQuaternion;
 
 static const U32 LENGTHOFVECTOR4 = 4;
 
-#if LL_WINDOWS
-__declspec( align(16) )
-#endif
-
 class LLVector4
 {
 	public:
@@ -95,12 +91,7 @@ class LLVector4
 		friend const LLVector4& operator/=(LLVector4 &a, F32 k);				// Return a divided by scaler k
 
 		friend LLVector4 operator-(const LLVector4 &a);					// Return vector -a
-}
-#if LL_DARWIN
-__attribute__ ((aligned (16)))
-#endif
-;
-
+};
 
 // Non-member functions 
 F32 angle_between(const LLVector4 &a, const LLVector4 &b);		// Returns angle (radians) between a and b
diff --git a/indra/llmessage/llcurl.cpp b/indra/llmessage/llcurl.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..886697ed4195ba05e6ed33c0826a11e7d97dfa0e
--- /dev/null
+++ b/indra/llmessage/llcurl.cpp
@@ -0,0 +1,343 @@
+/*
+ *  llcurl.cpp
+ *  MacTester
+ *
+ *  Created by Zero Linden on 10/15/06.
+ *  Copyright 2006 __MyCompanyName__. All rights reserved.
+ *
+ */
+
+#include "llcurl.h"
+
+#include <iomanip>
+
+#include "llsdserialize.h"
+
+//////////////////////////////////////////////////////////////////////////////
+/*
+	The trick to getting curl to do keep-alives is to reuse the
+	same easy handle for the requests.  It appears that curl
+	keeps a pool of connections alive for each easy handle, but
+	doesn't share them between easy handles.  Therefore it is
+	important to keep a pool of easy handles and reuse them,
+	rather than create and destroy them with each request.  This
+	code does this.
+
+	Furthermore, it would behoove us to keep track of which
+	hosts an easy handle was used for and pick an easy handle
+	that matches the next request.  This code does not current
+	do this.
+ */
+
+using namespace std;
+	
+LLCurl::Responder::Responder()
+	: mReferenceCount(0)
+{
+}
+LLCurl::Responder::~Responder()
+{
+}
+
+// virtual
+void LLCurl::Responder::error(U32 status, const std::stringstream& content)
+{
+	llinfos << "LLCurl::Responder::error " << status << ": " << content.str() << llendl;
+}
+
+// virtual
+void LLCurl::Responder::result(const std::stringstream& content)
+{
+}
+
+// virtual
+void LLCurl::Responder::completed(U32 status, const std::stringstream& content)
+{
+	if (200 <= status &&  status < 300)
+	{
+		result(content);
+	}
+	else
+	{
+		error(status, content);
+	}
+}
+
+
+namespace boost
+{
+	void intrusive_ptr_add_ref(LLCurl::Responder* p)
+	{
+		++p->mReferenceCount;
+	}
+	
+	void intrusive_ptr_release(LLCurl::Responder* p)
+	{
+		if(0 == --p->mReferenceCount)
+		{
+			delete p;
+		}
+	}
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+size_t
+curlOutputCallback(void* data, size_t size, size_t nmemb, void* user_data)
+{
+	stringstream& output = *(stringstream*)user_data;
+	
+	size_t n = size * nmemb;
+	output.write((const char*)data, n);
+	if (!((istream&)output).good()) {
+		std::cerr << "WHAT!?!?!? istream side bad" << std::endl;
+	}
+	if (!((ostream&)output).good()) {
+		std::cerr << "WHAT!?!?!? ostream side bad" << std::endl;
+	}
+
+	return n;
+}
+
+// Only used if request contained a body (post or put), Not currently implemented.
+// size_t
+// curlRequestCallback(void* data, size_t size, size_t nmemb, void* user_data)
+// {
+// 	stringstream& request = *(stringstream*)user_data;
+	
+// 	size_t n = size * nmemb;
+// 	request.read((char*)data, n);
+// 	return request.gcount();
+// }
+
+
+
+
+
+LLCurl::Easy::Easy()
+{
+	mHeaders = 0;
+	mHeaders = curl_slist_append(mHeaders, "Connection: keep-alive");
+	mHeaders = curl_slist_append(mHeaders, "Keep-alive: 300");
+	mHeaders = curl_slist_append(mHeaders, "Content-Type: application/xml");
+		// FIXME: shouldn't be there for GET/DELETE
+		// FIXME: should have ACCEPT headers
+		
+	mHandle = curl_easy_init();
+}
+
+LLCurl::Easy::~Easy()
+{
+	curl_easy_cleanup(mHandle);
+	curl_slist_free_all(mHeaders);
+}
+
+void
+LLCurl::Easy::get(const string& url, ResponderPtr responder)
+{
+	prep(url, responder);
+	curl_easy_setopt(mHandle, CURLOPT_HTTPGET, 1);
+}
+
+void
+LLCurl::Easy::getByteRange(const string& url, S32 offset, S32 length, ResponderPtr responder)
+{
+	mRange = llformat("Range: bytes=%d-%d", offset,offset+length-1);
+	mHeaders = curl_slist_append(mHeaders, mRange.c_str());
+	prep(url, responder);
+	curl_easy_setopt(mHandle, CURLOPT_HTTPGET, 1);
+}
+
+void
+LLCurl::Easy::perform()
+{
+	report(curl_easy_perform(mHandle));
+}
+
+void
+LLCurl::Easy::prep(const std::string& url, ResponderPtr responder)
+{
+#if !LL_DARWIN
+ 	curl_easy_reset(mHandle); // SJB: doesn't exisit on OSX 10.3.9
+#else
+	// SJB: equivalent? fast?
+ 	curl_easy_cleanup(mHandle);
+ 	mHandle = curl_easy_init();
+#endif
+	
+	curl_easy_setopt(mHandle, CURLOPT_PRIVATE, this);
+
+//	curl_easy_setopt(mHandle, CURLOPT_VERBOSE, 1); // usefull for debugging
+	curl_easy_setopt(mHandle, CURLOPT_NOSIGNAL, 1);
+	curl_easy_setopt(mHandle, CURLOPT_WRITEFUNCTION, &curlOutputCallback);
+	curl_easy_setopt(mHandle, CURLOPT_WRITEDATA, &mOutput);
+#if 1 // For debug
+	curl_easy_setopt(mHandle, CURLOPT_HEADERFUNCTION, &curlOutputCallback);
+	curl_easy_setopt(mHandle, CURLOPT_HEADERDATA, &mHeaderOutput);
+#endif
+	curl_easy_setopt(mHandle, CURLOPT_ERRORBUFFER, &mErrorBuffer);
+	curl_easy_setopt(mHandle, CURLOPT_ENCODING, "");
+	curl_easy_setopt(mHandle, CURLOPT_SSL_VERIFYPEER, false);
+	curl_easy_setopt(mHandle, CURLOPT_HTTPHEADER, mHeaders);
+
+	mOutput.str("");
+	if (!((istream&)mOutput).good()) {
+		std::cerr << "WHAT!?!?!? istream side bad" << std::endl;
+	}
+	if (!((ostream&)mOutput).good()) {
+		std::cerr << "WHAT!?!?!? ostream side bad" << std::endl;
+	}
+
+	mURL = url;
+	curl_easy_setopt(mHandle, CURLOPT_URL, mURL.c_str());
+
+	mResponder = responder;
+}
+
+void
+LLCurl::Easy::report(CURLcode code)
+{
+	if (!mResponder) return;
+	
+	long responseCode;
+	
+	if (code == CURLE_OK)
+	{
+		curl_easy_getinfo(mHandle, CURLINFO_RESPONSE_CODE, &responseCode);
+	}
+	else
+	{
+		responseCode = 499;
+	}
+	
+	mResponder->completed(responseCode, mOutput);
+	mResponder = NULL;
+}
+
+
+
+
+
+
+LLCurl::Multi::Multi()
+{
+	mHandle = curl_multi_init();
+}
+
+LLCurl::Multi::~Multi()
+{
+	// FIXME: should clean up excess handles in mFreeEasy
+	curl_multi_cleanup(mHandle);
+}
+
+
+void
+LLCurl::Multi::get(const std::string& url, ResponderPtr responder)
+{
+	LLCurl::Easy* easy = easyAlloc();
+	easy->get(url, responder);
+	curl_multi_add_handle(mHandle, easy->mHandle);
+}
+	
+void
+LLCurl::Multi::getByteRange(const std::string& url, S32 offset, S32 length, ResponderPtr responder)
+{
+	LLCurl::Easy* easy = easyAlloc();
+	easy->getByteRange(url, offset, length, responder);
+	curl_multi_add_handle(mHandle, easy->mHandle);
+}
+	
+void
+LLCurl::Multi::process()
+{
+	int count;
+	for (int call_count = 0; call_count < 5; call_count += 1)
+	{
+		if (CURLM_CALL_MULTI_PERFORM != curl_multi_perform(mHandle, &count))
+		{
+			break;
+		}
+	}
+		
+	CURLMsg* msg;
+	int msgs_in_queue;
+	while ((msg = curl_multi_info_read(mHandle, &msgs_in_queue)))
+	{
+		if (msg->msg != CURLMSG_DONE) continue;
+		Easy* easy = 0;
+		curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &easy);
+		if (!easy) continue;
+		easy->report(msg->data.result);
+		
+		curl_multi_remove_handle(mHandle, easy->mHandle);
+		easyFree(easy);
+	}
+}
+
+
+
+LLCurl::Easy*
+LLCurl::Multi::easyAlloc()
+{
+	Easy* easy = 0;
+	
+	if (mFreeEasy.empty())
+	{
+		easy = new Easy();
+	}
+	else
+	{
+		easy = mFreeEasy.back();
+		mFreeEasy.pop_back();
+	}
+
+	return easy;
+}
+
+void
+LLCurl::Multi::easyFree(Easy* easy)
+{
+	if (mFreeEasy.size() < 5)
+	{
+		mFreeEasy.push_back(easy);
+	}
+	else
+	{
+		delete easy;
+	}
+}
+
+
+
+namespace
+{
+	static LLCurl::Multi* sMainMulti = 0;
+	
+	LLCurl::Multi*
+	mainMulti()
+	{
+		if (!sMainMulti) {
+			sMainMulti = new LLCurl::Multi();
+		}
+		return sMainMulti;
+	}
+}
+
+void
+LLCurl::get(const std::string& url, ResponderPtr responder)
+{
+	mainMulti()->get(url, responder);
+}
+	
+void
+LLCurl::getByteRange(const std::string& url, S32 offset, S32 length, ResponderPtr responder)
+{
+	mainMulti()->getByteRange(url, offset, length, responder);
+}
+	
+void
+LLCurl::process()
+{
+	mainMulti()->process();
+}
+
diff --git a/indra/llmessage/llcurl.h b/indra/llmessage/llcurl.h
new file mode 100644
index 0000000000000000000000000000000000000000..4f1b5e60314fb3fc85f2e218145de1bf664e3f54
--- /dev/null
+++ b/indra/llmessage/llcurl.h
@@ -0,0 +1,118 @@
+/*
+ *  llcurl.h
+ *  MacTester
+ *
+ *  Created by Zero Linden on 10/15/06.
+ *  Copyright 2006 __MyCompanyName__. All rights reserved.
+ *
+ */
+ 
+#ifndef LL_LLCURL_H
+#define LL_LLCURL_H
+
+#include "linden_common.h"
+
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <boost/intrusive_ptr.hpp>
+#include <curl/curl.h>
+
+// #include "llhttpclient.h"
+
+class LLCurl
+{
+public:
+	class Multi;
+
+	class Responder
+	{
+	public:
+		Responder();
+		virtual ~Responder();
+
+		virtual void error(U32 status, const std::stringstream& content);	// called with bad status codes
+		
+		virtual void result(const std::stringstream& content);
+		
+		virtual void completed(U32 status, const std::stringstream& content);
+			/**< The default implemetnation calls
+				either:
+				* result(), or
+				* error() 
+			*/
+			
+	public: /* but not really -- don't touch this */
+		U32 mReferenceCount;
+	};
+	typedef boost::intrusive_ptr<Responder>	ResponderPtr;
+	
+	class Easy
+	{
+	public:
+		Easy();
+		~Easy();
+		
+		void get(const std::string& url, ResponderPtr);
+		void getByteRange(const std::string& url, S32 offset, S32 length, ResponderPtr);
+
+		void perform();
+
+	private:
+		void prep(const std::string& url, ResponderPtr);
+		void report(CURLcode);
+		
+		CURL*				mHandle;
+		struct curl_slist*	mHeaders;
+		
+		std::string			mURL;
+		std::string			mRange;
+		std::stringstream	mRequest;
+
+		std::stringstream	mOutput;
+		char				mErrorBuffer[CURL_ERROR_SIZE];
+
+		std::stringstream	mHeaderOutput; // Debug
+		
+		ResponderPtr		mResponder;
+
+		friend class Multi;
+	};
+
+
+	class Multi
+	{
+	public:
+		Multi();
+		~Multi();
+
+		void get(const std::string& url, ResponderPtr);
+		void getByteRange(const std::string& url, S32 offset, S32 length, ResponderPtr);
+
+		void process();
+		
+	private:
+		Easy* easyAlloc();
+		void easyFree(Easy*);
+		
+		CURLM* mHandle;
+		
+		typedef std::vector<Easy*>	EasyList;
+		EasyList mFreeEasy;
+	};
+
+
+	static void get(const std::string& url, ResponderPtr);
+	static void getByteRange(const std::string& url, S32 offset, S32 length, ResponderPtr responder);
+	
+	static void process();
+};
+
+namespace boost
+{
+	void intrusive_ptr_add_ref(LLCurl::Responder* p);
+	void intrusive_ptr_release(LLCurl::Responder* p);
+};
+
+#endif // LL_LLCURL_H
diff --git a/indra/llmessage/llpartdata.cpp b/indra/llmessage/llpartdata.cpp
index 4ce7bc1fcd42445248f8301716f250da34c0c105..6fc2af2cb22577eac2c65434169000c974fb463a 100644
--- a/indra/llmessage/llpartdata.cpp
+++ b/indra/llmessage/llpartdata.cpp
@@ -212,6 +212,20 @@ BOOL LLPartSysData::unpack(LLDataPacker &dp)
 	return TRUE;
 }
 
+std::ostream& operator<<(std::ostream& s, const LLPartSysData &data)
+{
+	s << "Flags: " << std::hex << data.mFlags;
+	s << " Pattern: " << std::hex << (U32) data.mPattern << "\n";
+	s << "Age: [" << data.mStartAge << ", " << data.mMaxAge << "]\n";
+	s << "Angle: [" << data.mInnerAngle << ", " << data.mOuterAngle << "]\n";
+	s << "Burst Rate: " << data.mBurstRate << "\n";
+	s << "Burst Radius: " << data.mBurstRadius << "\n";
+	s << "Burst Speed: [" << data.mBurstSpeedMin << ", " << data.mBurstSpeedMax << "]\n";
+	s << "Burst Part Count: " << std::hex << (U32) data.mBurstPartCount << "\n";
+	s << "Angular Velocity: " << data.mAngularVelocity << "\n";
+	s << "Accel: " << data.mPartAccel;
+	return s;
+}
 
 BOOL LLPartSysData::isNullPS(const S32 block_num)
 {
diff --git a/indra/llmessage/llpartdata.h b/indra/llmessage/llpartdata.h
index ba3bdaf9b6a4757b4f4eb9ccf61ca8e56267dc65..662d635a088919a33030084f13d6ca552327d99b 100644
--- a/indra/llmessage/llpartdata.h
+++ b/indra/llmessage/llpartdata.h
@@ -167,6 +167,9 @@ public:
 	// a combination of multiple parameters, we
 	// need to clamp it using a separate method instead of an accessor.
 	void clampSourceParticleRate();
+	
+	friend std::ostream&	 operator<<(std::ostream& s, const LLPartSysData &data);		// Stream a
+	
 public:
 	// Public because I'm lazy....
 
diff --git a/indra/llmessage/llpumpio.cpp b/indra/llmessage/llpumpio.cpp
index 5abc63fad0d128bc40dbb8f5eebf1e201895460b..320719072c49283e0a4a13edf57ed2fa593eadcd 100644
--- a/indra/llmessage/llpumpio.cpp
+++ b/indra/llmessage/llpumpio.cpp
@@ -868,7 +868,7 @@ void LLPumpIO::processChain(LLChainInfo& chain)
 			PUMP_DEBUG;
 			if(LLIOPipe::isError(status))
 			{
-				llinfos << "Pump generated pipe error: '"
+				llinfos << "Pump generated pipe err: '"
 #if LL_DEBUG_PIPE_TYPE_IN_PUMP
 						<< typeid(*((*it).mPipe)).name() << "':'"
 #endif
diff --git a/indra/llmessage/lltransfermanager.cpp b/indra/llmessage/lltransfermanager.cpp
index 4eea7418af478a74c7e89c8f507ec709ca17cc32..bddd79367a22af97e0b261e76f81c12518f4f210 100644
--- a/indra/llmessage/lltransfermanager.cpp
+++ b/indra/llmessage/lltransfermanager.cpp
@@ -335,7 +335,7 @@ void LLTransferManager::processTransferInfo(LLMessageSystem *msgp, void **)
 		{
 			// Perhaps this stuff should be inside a method in LLTransferPacket?
 			// I'm too lazy to do it now, though.
-			llinfos << "Playing back delayed packet " << packet_id << llendl;
+// 			llinfos << "Playing back delayed packet " << packet_id << llendl;
 			LLTransferPacket *packetp = ttp->mDelayedPacketMap[packet_id];
 
 			// This is somewhat inefficient, but avoids us having to duplicate
@@ -455,6 +455,8 @@ void LLTransferManager::processTransferPacket(LLMessageSystem *msgp, void **)
 			ttcp->deleteTransfer(ttp);
 			return;
 		}
+#if 0
+		// Spammy!
 		const S32 LL_TRANSFER_WARN_GAP = 10;
 		if(!ttp->gotInfo())
 		{
@@ -468,6 +470,7 @@ void LLTransferManager::processTransferPacket(LLMessageSystem *msgp, void **)
 				<< " from " << msgp->getSender() << ", got " << packet_id
 				<< " expecting " << ttp->getNextPacketID() << llendl;
 		}
+#endif
 		return;
 	}
 
@@ -508,7 +511,7 @@ void LLTransferManager::processTransferPacket(LLMessageSystem *msgp, void **)
 		{
 			// Perhaps this stuff should be inside a method in LLTransferPacket?
 			// I'm too lazy to do it now, though.
-			llinfos << "Playing back delayed packet " << packet_id << llendl;
+// 			llinfos << "Playing back delayed packet " << packet_id << llendl;
 			LLTransferPacket *packetp = ttp->mDelayedPacketMap[packet_id];
 
 			// This is somewhat inefficient, but avoids us having to duplicate
diff --git a/indra/llmessage/lltransfertargetvfile.cpp b/indra/llmessage/lltransfertargetvfile.cpp
index b8c138886a80ccf1433a05a813a85d7347d7f24a..6714185d3f6bc38fe15feb081a2d210c33647b70 100644
--- a/indra/llmessage/lltransfertargetvfile.cpp
+++ b/indra/llmessage/lltransfertargetvfile.cpp
@@ -14,34 +14,9 @@
 #include "llerror.h"
 #include "llvfile.h"
 
-//static
-std::list<LLTransferTargetParamsVFile*> LLTransferTargetVFile::sCallbackQueue;
-
 //static
 void LLTransferTargetVFile::updateQueue(bool shutdown)
 {
-	for(std::list<LLTransferTargetParamsVFile*>::iterator iter = sCallbackQueue.begin();
-		iter != sCallbackQueue.end(); )
-	{
-		std::list<LLTransferTargetParamsVFile*>::iterator curiter = iter++;
-		LLTransferTargetParamsVFile* params = *curiter;
-		LLVFSThread::status_t s = LLVFile::getVFSThread()->getRequestStatus(params->mHandle);
-		if (s == LLVFSThread::STATUS_COMPLETE || s == LLVFSThread::STATUS_EXPIRED)
-		{
-			params->mCompleteCallback(
-				params->mErrCode,
-				params->getAssetID(),
-				params->getAssetType(),
-				params->mUserDatap);
-			delete params;
-			iter = sCallbackQueue.erase(curiter);
-		}
-		else if (shutdown)
-		{
-			delete params;
-			iter = sCallbackQueue.erase(curiter);
-		}
-	}
 }
 
 
@@ -50,8 +25,7 @@ LLTransferTargetParamsVFile::LLTransferTargetParamsVFile() :
 	mAssetType(LLAssetType::AT_NONE),
 	mCompleteCallback(NULL),
 	mUserDatap(NULL),
-	mErrCode(0),
-	mHandle(LLVFSThread::nullHandle())
+	mErrCode(0)
 {
 }
 
@@ -166,7 +140,6 @@ void LLTransferTargetVFile::completionCallback(const LLTSCode status)
 		llwarns << "Aborting vfile transfer after asset storage shut down!" << llendl;
 		return;
 	}
-	LLVFSThread::handle_t handle = LLVFSThread::nullHandle();
 	
 	// Still need to gracefully handle error conditions.
 	S32 err_code = 0;
@@ -175,11 +148,11 @@ void LLTransferTargetVFile::completionCallback(const LLTSCode status)
 	  case LLTS_DONE:
 		if (!mNeedsCreate)
 		{
-			handle = LLVFile::getVFSThread()->rename(
-				gAssetStorage->mVFS,
-				mTempID, mParams.getAssetType(),
-				mParams.getAssetID(), mParams.getAssetType(),
-				LLVFSThread::AUTO_DELETE);
+			LLVFile file(gAssetStorage->mVFS, mTempID, mParams.getAssetType(), LLVFile::WRITE);
+			if (!file.rename(mParams.getAssetID(), mParams.getAssetType()))
+			{
+				llerrs << "LLTransferTargetVFile: rename failed" << llendl;
+			}
 		}
 		err_code = LL_ERR_NOERR;
 		lldebugs << "LLTransferTargetVFile::completionCallback for "
@@ -219,20 +192,9 @@ void LLTransferTargetVFile::completionCallback(const LLTSCode status)
 	}
 	if (mParams.mCompleteCallback)
 	{
-		if (handle != LLVFSThread::nullHandle())
-		{
-			LLTransferTargetParamsVFile* params = new LLTransferTargetParamsVFile(mParams);
-			params->mErrCode = err_code;
-			params->mHandle = handle;
-			sCallbackQueue.push_back(params);
-		}
-		else
-		{
-			mParams.mCompleteCallback(
-				err_code,
-				mParams.getAssetID(),
-				mParams.getAssetType(),
-				mParams.mUserDatap);
-		}
+		mParams.mCompleteCallback(err_code,
+								  mParams.getAssetID(),
+								  mParams.getAssetType(),
+								  mParams.mUserDatap);
 	}
 }
diff --git a/indra/llmessage/lltransfertargetvfile.h b/indra/llmessage/lltransfertargetvfile.h
index 57eaeca37848ff8bbd6195d04f677cfc4cd49f17..e9d5fa0c00650eda32c1ebdabbd597ea075ef2f4 100644
--- a/indra/llmessage/lltransfertargetvfile.h
+++ b/indra/llmessage/lltransfertargetvfile.h
@@ -71,8 +71,6 @@ protected:
 
 	BOOL mNeedsCreate;
 	LLUUID mTempID;
-
-	static std::list<LLTransferTargetParamsVFile*> sCallbackQueue;
 };
 
 #endif // LL_LLTRANSFERTARGETFILE_H
diff --git a/indra/llmessage/message.h b/indra/llmessage/message.h
index 48d950e377a0b0f8129cbbab2956cfcce602311f..3f9dfa08d6e0f6f3296c5b6a736807c67a49d6b6 100644
--- a/indra/llmessage/message.h
+++ b/indra/llmessage/message.h
@@ -421,6 +421,7 @@ public:
 	void	addStringFast( const char* varname, const std::string& s);				// typed, checks storage space
 	void	addString( const char* varname, const std::string& s);				// typed, checks storage space
 
+	TPACKETID getCurrentRecvPacketID() { return mCurrentRecvPacketID; }
 	S32 getCurrentSendTotal() const { return mCurrentSendTotal; }
 
 	// This method checks for current send total and returns true if
diff --git a/indra/llprimitive/llvolumemessage.cpp b/indra/llprimitive/llvolumemessage.cpp
index d2f1e12526975e7aec0ebd832288a2876f171b33..a43aa19cabee9eab8dd6ae568c9ef3bfc4a3cd93 100644
--- a/indra/llprimitive/llvolumemessage.cpp
+++ b/indra/llprimitive/llvolumemessage.cpp
@@ -429,41 +429,36 @@ bool LLVolumeMessage::unpackPathParams(LLPathParams* params, LLDataPacker &dp)
 // static
 bool LLVolumeMessage::constrainVolumeParams(LLVolumeParams& params)
 {
-	bool ok = true;
+	U32 bad = 0;
 
 	// This is called immediately after an unpack. feed the raw data
 	// through the checked setters to constraint it to a valid set of
 	// volume params.
-	ok &= params.setType(
-		params.getProfileParams().getCurveType(),
-		params.getPathParams().getCurveType());
-	ok &= params.setBeginAndEndS(
-		params.getProfileParams().getBegin(),
-		params.getProfileParams().getEnd());
-	ok &= params.setBeginAndEndT(
-		params.getPathParams().getBegin(),
-		params.getPathParams().getEnd());
-	ok &= params.setHollow(params.getProfileParams().getHollow());
-	ok &= params.setTwistBegin(params.getPathParams().getTwistBegin());
-	ok &= params.setTwistEnd(params.getPathParams().getTwistEnd());
-	ok &= params.setRatio(
-		params.getPathParams().getScaleX(),
-		params.getPathParams().getScaleY());
-	ok &= params.setShear(
-		params.getPathParams().getShearX(),
-		params.getPathParams().getShearY());
-	ok &= params.setTaper(
-		params.getPathParams().getTaperX(),
-		params.getPathParams().getTaperY());
-	ok &= params.setRevolutions(params.getPathParams().getRevolutions());
-	ok &= params.setRadiusOffset(params.getPathParams().getRadiusOffset());
-	ok &= params.setSkew(params.getPathParams().getSkew());
-	if(!ok)
+	bad |= params.setType(params.getProfileParams().getCurveType(),
+						 params.getPathParams().getCurveType()) ? 0 : 1;
+	bad |= params.setBeginAndEndS(params.getProfileParams().getBegin(),
+								  params.getProfileParams().getEnd()) ? 0 : 2;
+	bad |= params.setBeginAndEndT(params.getPathParams().getBegin(),
+								  params.getPathParams().getEnd()) ? 0 : 4;
+	bad |= params.setHollow(params.getProfileParams().getHollow()) ? 0 : 8;
+	bad |= params.setTwistBegin(params.getPathParams().getTwistBegin()) ? 0 : 0x10;
+	bad |= params.setTwistEnd(params.getPathParams().getTwistEnd()) ? 0 : 0x20;
+	bad |= params.setRatio(params.getPathParams().getScaleX(),
+						   params.getPathParams().getScaleY()) ? 0 : 0x40;
+	bad |= params.setShear(params.getPathParams().getShearX(),
+						   params.getPathParams().getShearY()) ? 0 : 0x80;
+	bad |= params.setTaper(params.getPathParams().getTaperX(),
+						   params.getPathParams().getTaperY()) ? 0 : 0x100;
+	bad |= params.setRevolutions(params.getPathParams().getRevolutions()) ? 0 : 0x200;
+	bad |= params.setRadiusOffset(params.getPathParams().getRadiusOffset()) ? 0 : 0x400;
+	bad |= params.setSkew(params.getPathParams().getSkew()) ? 0 : 0x800;
+	if(bad)
 	{
 		llwarns << "LLVolumeMessage::constrainVolumeParams() - "
-			<< "forced to constrain incoming volume params." << llendl;
+				<< "forced to constrain incoming volume params: "
+				<< llformat("0x%04x",bad) << llendl;
 	}
-	return ok;
+	return bad ? false : true;
 }
 
 bool LLVolumeMessage::packVolumeParams(const LLVolumeParams* params, LLMessageSystem *mesgsys)
diff --git a/indra/llrender/llfontgl.cpp b/indra/llrender/llfontgl.cpp
index f42122b0eeb14970d6f70844c6594fb5706a686c..1fc040c7d6130abf0105bd59cfb301e297b297a7 100644
--- a/indra/llrender/llfontgl.cpp
+++ b/indra/llrender/llfontgl.cpp
@@ -14,6 +14,7 @@
 #include "llfontgl.h"
 #include "llgl.h"
 #include "v4color.h"
+#include "llstl.h"
 
 const S32 BOLD_OFFSET = 1;
 
diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp
index b4edd3d365f12e27ef629600aff6996da13dd5a1..9c1178b9f7587d1f1ac2b7da6533d70fae5e689a 100644
--- a/indra/llrender/llimagegl.cpp
+++ b/indra/llrender/llimagegl.cpp
@@ -115,6 +115,15 @@ void LLImageGL::unbindTexture(S32 stage, LLGLenum bind_target)
 	sCurrentBoundTextures[stage] = 0;
 }
 
+// static (duplicated for speed and to avoid GL_TEXTURE_2D default argument which requires GL header dependency)
+void LLImageGL::unbindTexture(S32 stage)
+{
+	glActiveTextureARB(GL_TEXTURE0_ARB + stage);
+	glClientActiveTextureARB(GL_TEXTURE0_ARB + stage);
+	glBindTexture(GL_TEXTURE_2D, 0);
+	sCurrentBoundTextures[stage] = 0;
+}
+
 // static
 void LLImageGL::updateStats(F32 current_time)
 {
@@ -371,13 +380,8 @@ BOOL LLImageGL::bindTextureInternal(const S32 stage) const
 		llwarns << "Trying to bind a texture while GL is disabled!" << llendl;
 	}
 
-	stop_glerror();
-	
 	glActiveTextureARB(GL_TEXTURE0_ARB + stage);
-	//glClientActiveTextureARB(GL_TEXTURE0_ARB + stage);
-	
-	stop_glerror();
-
+		
 	if (sCurrentBoundTextures[stage] && sCurrentBoundTextures[stage] == mTexName)
 	{
 		// already set!
@@ -392,7 +396,6 @@ BOOL LLImageGL::bindTextureInternal(const S32 stage) const
 
 		glBindTexture(mBindTarget, mTexName);
 		sCurrentBoundTextures[stage] = mTexName;
-		stop_glerror();
 
 		if (mLastBindTime != sLastFrameTime)
 		{
@@ -631,6 +634,7 @@ void LLImageGL::setImage(const U8* data_in, BOOL data_hasmips)
 		}
 		mHasMipMaps = FALSE;
 	}
+	glFlush();
 	stop_glerror();
 }
 
@@ -645,6 +649,11 @@ BOOL LLImageGL::setSubImage(const U8* datap, S32 data_width, S32 data_height, S3
 		llwarns << "Setting subimage on image without GL texture" << llendl;
 		return FALSE;
 	}
+	if (datap == NULL)
+	{
+		llwarns << "Setting subimage on image with NULL datap" << llendl;
+		return FALSE;
+	}
 	
 	if (x_pos == 0 && y_pos == 0 && width == getWidth() && height == getHeight())
 	{
@@ -657,7 +666,9 @@ BOOL LLImageGL::setSubImage(const U8* datap, S32 data_width, S32 data_height, S3
 			dump();
 			llerrs << "setSubImage called with mipmapped image (not supported)" << llendl;
 		}
-		llassert(mCurrentDiscardLevel == 0);
+		llassert_always(mCurrentDiscardLevel == 0);
+		llassert_always(x_pos >= 0 && y_pos >= 0);
+		
 		if (((x_pos + width) > getWidth()) || 
 			(y_pos + height) > getHeight())
 		{
@@ -698,7 +709,8 @@ BOOL LLImageGL::setSubImage(const U8* datap, S32 data_width, S32 data_height, S3
 
 		datap += (y_pos * data_width + x_pos) * getComponents();
 		// Update the GL texture
-		llverify(bindTextureInternal(0));
+		BOOL res = bindTextureInternal(0);
+		if (!res) llerrs << "LLImageGL::setSubImage(): bindTexture failed" << llendl;
 		stop_glerror();
 
 		glTexSubImage2D(mTarget, 0, x_pos, y_pos, 
@@ -714,7 +726,7 @@ BOOL LLImageGL::setSubImage(const U8* datap, S32 data_width, S32 data_height, S3
 		glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
 		stop_glerror();
 	}
-	
+	glFlush();
 	return TRUE;
 }
 
diff --git a/indra/llrender/llimagegl.h b/indra/llrender/llimagegl.h
index 4f6a11c2d942b455dbf435ef5d8f7842530ea1f1..1586a837b414db65665c12c41d47024cd992fa80 100644
--- a/indra/llrender/llimagegl.h
+++ b/indra/llrender/llimagegl.h
@@ -17,7 +17,7 @@
 
 //============================================================================
 
-class LLImageGL : public LLThreadSafeRefCount
+class LLImageGL : public LLRefCount
 {
 public:
 	// Size calculation
@@ -29,6 +29,7 @@ public:
 	// Usually you want stage = 0 and bind_target = GL_TEXTURE_2D
 	static void bindExternalTexture( LLGLuint gl_name, S32 stage, LLGLenum bind_target);
 	static void unbindTexture(S32 stage, LLGLenum target);
+	static void unbindTexture(S32 stage); // Uses GL_TEXTURE_2D (not a default arg to avoid gl.h dependency)
 
 	// needs to be called every frame
 	static void updateStats(F32 current_time);
diff --git a/indra/llrender/llvertexbuffer.cpp b/indra/llrender/llvertexbuffer.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d6477d69ec2ed36ee944582bc657b4b45de472c0
--- /dev/null
+++ b/indra/llrender/llvertexbuffer.cpp
@@ -0,0 +1,918 @@
+#include "linden_common.h"
+
+#include "llvertexbuffer.h"
+// #include "llrender.h"
+#include "llglheaders.h"
+#include "llmemory.h"
+#include "llmemtype.h"
+
+//============================================================================
+
+//static
+S32 LLVertexBuffer::sCount = 0;
+S32 LLVertexBuffer::sGLCount = 0;
+BOOL LLVertexBuffer::sEnableVBOs = TRUE;
+S32 LLVertexBuffer::sGLRenderBuffer = 0;
+S32 LLVertexBuffer::sGLRenderIndices = 0;
+U32 LLVertexBuffer::sLastMask = 0;
+BOOL LLVertexBuffer::sVBOActive = FALSE;
+BOOL LLVertexBuffer::sIBOActive = FALSE;
+U32 LLVertexBuffer::sAllocatedBytes = 0;
+BOOL LLVertexBuffer::sRenderActive = FALSE;
+
+std::vector<U32> LLVertexBuffer::sDeleteList;
+LLVertexBuffer::buffer_list_t LLVertexBuffer::sLockedList;
+
+S32 LLVertexBuffer::sTypeOffsets[LLVertexBuffer::TYPE_MAX] =
+{
+	sizeof(LLVector3), // TYPE_VERTEX,
+	sizeof(LLVector3), // TYPE_NORMAL,
+	sizeof(LLVector2), // TYPE_TEXCOORD,
+	sizeof(LLVector2), // TYPE_TEXCOORD2,
+	sizeof(LLColor4U), // TYPE_COLOR,
+	sizeof(LLVector3), // TYPE_BINORMAL,
+	sizeof(F32),	   // TYPE_WEIGHT,
+	sizeof(LLVector4), // TYPE_CLOTHWEIGHT,
+};
+
+//static
+void LLVertexBuffer::initClass(bool use_vbo)
+{
+	sEnableVBOs = use_vbo;
+}
+
+//static 
+void LLVertexBuffer::unbind()
+{
+	if (sVBOActive)
+	{
+		glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
+		sVBOActive = FALSE;
+	}
+	if (sIBOActive)
+	{
+		glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
+		sIBOActive = FALSE;
+	}
+
+	sGLRenderBuffer = 0;
+	sGLRenderIndices = 0;
+}
+
+//static
+void LLVertexBuffer::cleanupClass()
+{
+	LLMemType mt(LLMemType::MTYPE_VERTEX_DATA);
+	sLockedList.clear();
+	startRender(); 
+	stopRender();
+	clientCopy(); // deletes GL buffers
+}
+
+//static, call before rendering VBOs
+void LLVertexBuffer::startRender()
+{		
+	LLMemType mt(LLMemType::MTYPE_VERTEX_DATA);
+	if (sEnableVBOs)
+	{
+		glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
+		glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
+		sVBOActive = FALSE;
+		sIBOActive = FALSE;
+	}
+	
+	sRenderActive = TRUE;
+	sGLRenderBuffer = 0;
+	sGLRenderIndices = 0;
+	sLastMask = 0;
+}
+
+void LLVertexBuffer::stopRender()
+{
+	sRenderActive = FALSE;
+}
+
+void LLVertexBuffer::clientCopy()
+{
+	if (!sDeleteList.empty())
+	{
+		size_t num = sDeleteList.size();
+		glDeleteBuffersARB(sDeleteList.size(), (GLuint*) &(sDeleteList[0]));
+		sDeleteList.clear();
+		sGLCount -= num;
+	}
+
+	if (sEnableVBOs)
+	{
+		LLTimer timer;
+		BOOL reset = TRUE;
+		buffer_list_t::iterator iter = sLockedList.begin();
+		while(iter != sLockedList.end())
+		{
+			LLVertexBuffer* buffer = *iter;
+			if (buffer->isLocked() && buffer->useVBOs())
+			{
+				buffer->setBuffer(0);
+			}
+			++iter;
+			if (reset)
+			{
+				reset = FALSE;
+				timer.reset(); //skip first copy (don't count pipeline stall)
+			}
+			else
+			{
+				if (timer.getElapsedTimeF64() > 0.005)
+				{
+					break;
+				}
+			}
+
+		}
+
+		sLockedList.erase(sLockedList.begin(), iter);
+	}
+}
+
+//----------------------------------------------------------------------------
+
+// For debugging
+struct VTNC /// Simple
+{
+	F32 v1,v2,v3;
+	F32 n1,n2,n3;
+	F32 t1,t2;
+	U32 c;
+};
+static VTNC dbg_vtnc;
+
+struct VTUNCB // Simple + Bump
+{
+	F32 v1,v2,v3;
+	F32 n1,n2,n3;
+	F32 t1,t2;
+	F32 u1,u2;
+	F32 b1,b2,b3;
+	U32 c;
+};
+static VTUNCB dbg_vtuncb;
+
+struct VTUNC // Surfacepatch
+{
+	F32 v1,v2,v3;
+	F32 n1,n2,n3;
+	F32 t1,t2;
+	F32 u1,u2;
+	U32 c;
+};
+static VTUNC dbg_vtunc;
+
+struct VTNW /// Avatar
+{
+	F32 v1,v2,v3;
+	F32 n1,n2,n3;
+	F32 t1,t2;
+	F32 w;
+};
+static VTNW dbg_vtnw;
+
+struct VTNPAD /// Avatar Output
+{
+	F32 v1,v2,v3,p1;
+	F32 n1,n2,n3,p2;
+	F32 t1,t2,p3,p4;
+};
+static VTNPAD dbg_vtnpad;
+
+//----------------------------------------------------------------------------
+
+LLVertexBuffer::LLVertexBuffer(U32 typemask, S32 usage) :
+	LLRefCount(),
+	mNumVerts(0), mNumIndices(0), mUsage(usage), mGLBuffer(0), mGLIndices(0), 
+	mMappedData(NULL),
+	mMappedIndexData(NULL), mLocked(FALSE),
+	mResized(FALSE), mEmpty(TRUE), mFinal(FALSE), mFilthy(FALSE)
+{
+	LLMemType mt(LLMemType::MTYPE_VERTEX_DATA);
+	if (!sEnableVBOs)
+	{
+		mUsage = GL_STREAM_DRAW_ARB;
+	}
+	
+	S32 stride = 0;
+	for (S32 i=0; i<TYPE_MAX; i++)
+	{
+		U32 mask = 1<<i;
+		if (typemask & mask)
+		{
+			mOffsets[i] = stride;
+			stride += sTypeOffsets[i];
+		}
+	}
+	mTypeMask = typemask;
+	mStride = stride;
+	sCount++;
+}
+
+// protected, use unref()
+//virtual
+LLVertexBuffer::~LLVertexBuffer()
+{
+	LLMemType mt(LLMemType::MTYPE_VERTEX_DATA);
+	destroyGLBuffer();
+	destroyGLIndices();
+	sCount--;
+	
+	if (mLocked)
+	{
+		//pull off of locked list
+		for (buffer_list_t::iterator i = sLockedList.begin(); i != sLockedList.end(); ++i)
+		{
+			if (*i == this)
+			{
+				sLockedList.erase(i);
+				break;
+			}
+		}
+	}
+};
+
+//----------------------------------------------------------------------------
+
+void LLVertexBuffer::createGLBuffer()
+{
+	LLMemType mt(LLMemType::MTYPE_VERTEX_DATA);
+
+	U32 size = getSize();
+	if (mGLBuffer)
+	{
+		destroyGLBuffer();
+	}
+
+	if (size == 0)
+	{
+		return;
+	}
+
+	mMappedData = new U8[size];
+	memset(mMappedData, 0, size);
+	mEmpty = TRUE;
+
+	if (useVBOs())
+	{
+		glGenBuffersARB(1, (GLuint*) &mGLBuffer);
+		mResized = TRUE;
+		sGLCount++;
+	}
+	else
+	{
+		static int gl_buffer_idx = 0;
+		mGLBuffer = ++gl_buffer_idx;
+	}
+}
+
+void LLVertexBuffer::createGLIndices()
+{
+	LLMemType mt(LLMemType::MTYPE_VERTEX_DATA);
+	U32 size = getIndicesSize();
+
+	if (mGLIndices)
+	{
+		destroyGLIndices();
+	}
+	
+	if (size == 0)
+	{
+		return;
+	}
+
+	mMappedIndexData = new U8[size];
+	memset(mMappedIndexData, 0, size);
+	mEmpty = TRUE;
+
+	if (useVBOs())
+	{
+		glGenBuffersARB(1, (GLuint*) &mGLIndices);
+		mResized = TRUE;
+		sGLCount++;
+	}
+	else
+	{
+		static int gl_buffer_idx = 0;
+		mGLIndices = ++gl_buffer_idx;
+	}
+}
+
+void LLVertexBuffer::destroyGLBuffer()
+{
+	LLMemType mt(LLMemType::MTYPE_VERTEX_DATA);
+	if (mGLBuffer)
+	{
+		if (useVBOs())
+		{
+			sDeleteList.push_back(mGLBuffer);
+		}
+		
+		delete [] mMappedData;
+		mMappedData = NULL;
+		mEmpty = TRUE;
+		sAllocatedBytes -= getSize();
+	}
+	
+	mGLBuffer = 0;
+}
+
+void LLVertexBuffer::destroyGLIndices()
+{
+	LLMemType mt(LLMemType::MTYPE_VERTEX_DATA);
+	if (mGLIndices)
+	{
+		if (useVBOs())
+		{
+			sDeleteList.push_back(mGLIndices);
+		}
+		
+		delete [] mMappedIndexData;
+		mMappedIndexData = NULL;
+		mEmpty = TRUE;
+		sAllocatedBytes -= getIndicesSize();
+	}
+
+	mGLIndices = 0;
+}
+
+void LLVertexBuffer::updateNumVerts(S32 nverts)
+{
+	LLMemType mt(LLMemType::MTYPE_VERTEX_DATA);
+	if (!mDynamicSize)
+	{
+		mNumVerts = nverts;
+	}
+	else if (mUsage == GL_STATIC_DRAW_ARB ||
+		nverts > mNumVerts ||
+		nverts < mNumVerts/2)
+	{
+		if (mUsage != GL_STATIC_DRAW_ARB)
+		{
+			nverts += nverts/4;
+		}
+
+		mNumVerts = nverts;
+	}
+}
+
+void LLVertexBuffer::updateNumIndices(S32 nindices)
+{
+	LLMemType mt(LLMemType::MTYPE_VERTEX_DATA);
+	if (!mDynamicSize)
+	{
+		mNumIndices = nindices;
+	}
+	else if (mUsage == GL_STATIC_DRAW_ARB ||
+		nindices > mNumIndices ||
+		nindices < mNumIndices/2)
+	{
+		if (mUsage != GL_STATIC_DRAW_ARB)
+		{
+			nindices += nindices/4;
+		}
+
+		mNumIndices = nindices;
+	}
+}
+
+void LLVertexBuffer::makeStatic()
+{
+	if (!sEnableVBOs)
+	{
+		return;
+	}
+	
+	if (sRenderActive)
+	{
+		llerrs << "Make static called during render." << llendl;
+	}
+	
+	if (mUsage != GL_STATIC_DRAW_ARB)
+	{
+		if (useVBOs())
+		{
+			if (mGLBuffer)
+			{
+				sDeleteList.push_back(mGLBuffer);
+			}
+			if (mGLIndices)
+			{
+				sDeleteList.push_back(mGLIndices);
+			}
+		}
+	
+		if (mGLBuffer)
+		{
+			sGLCount++;
+			glGenBuffersARB(1, (GLuint*) &mGLBuffer);
+		}
+		if (mGLIndices)
+		{
+			sGLCount++;
+			glGenBuffersARB(1, (GLuint*) &mGLIndices);
+		}
+			
+		mUsage = GL_STATIC_DRAW_ARB;
+		mResized = TRUE;
+
+		if (!mLocked)
+		{
+			mLocked = TRUE;
+			sLockedList.push_back(this);
+		}
+	}
+}
+
+void LLVertexBuffer::allocateBuffer(S32 nverts, S32 nindices, bool create)
+{
+	LLMemType mt(LLMemType::MTYPE_VERTEX_DATA);
+		
+	updateNumVerts(nverts);
+	updateNumIndices(nindices);
+	
+	if (mMappedData)
+	{
+		llerrs << "LLVertexBuffer::allocateBuffer() called redundantly." << llendl;
+	}
+	if (create && (nverts || nindices))
+	{
+		createGLBuffer();
+		createGLIndices();
+	}
+	
+	sAllocatedBytes += getSize() + getIndicesSize();
+}
+
+void LLVertexBuffer::resizeBuffer(S32 newnverts, S32 newnindices)
+{
+	LLMemType mt(LLMemType::MTYPE_VERTEX_DATA);
+	mDynamicSize = TRUE;
+	if (mUsage == GL_STATIC_DRAW_ARB)
+	{ //always delete/allocate static buffers on resize
+		destroyGLBuffer();
+		destroyGLIndices();
+		allocateBuffer(newnverts, newnindices, TRUE);
+		mFinal = FALSE;
+	}
+	else if (newnverts > mNumVerts || newnindices > mNumIndices ||
+			 newnverts < mNumVerts/2 || newnindices < mNumIndices/2)
+	{
+		sAllocatedBytes -= getSize() + getIndicesSize();
+		
+		S32 oldsize = getSize();
+		S32 old_index_size = getIndicesSize();
+
+		updateNumVerts(newnverts);		
+		updateNumIndices(newnindices);
+		
+		S32 newsize = getSize();
+		S32 new_index_size = getIndicesSize();
+
+		sAllocatedBytes += newsize + new_index_size;
+
+		if (newsize)
+		{
+			if (!mGLBuffer)
+			{ //no buffer exists, create a new one
+				createGLBuffer();
+			}
+			else
+			{
+				//delete old buffer, keep GL buffer for now
+				U8* old = mMappedData;
+				mMappedData = new U8[newsize];
+				if (old)
+				{	
+					memcpy(mMappedData, old, llmin(newsize, oldsize));
+					if (newsize > oldsize)
+					{
+						memset(mMappedData+oldsize, 0, newsize-oldsize);
+					}
+
+					delete [] old;
+				}
+				else
+				{
+					memset(mMappedData, 0, newsize);
+					mEmpty = TRUE;
+				}
+				mResized = TRUE;
+			}
+		}
+		else if (mGLBuffer)
+		{
+			destroyGLBuffer();
+		}
+		
+		if (new_index_size)
+		{
+			if (!mGLIndices)
+			{
+				createGLIndices();
+			}
+			else
+			{
+				//delete old buffer, keep GL buffer for now
+				U8* old = mMappedIndexData;
+				mMappedIndexData = new U8[new_index_size];
+				if (old)
+				{	
+					memcpy(mMappedIndexData, old, llmin(new_index_size, old_index_size));
+					if (new_index_size > old_index_size)
+					{
+						memset(mMappedIndexData+old_index_size, 0, new_index_size - old_index_size);
+					}
+					delete [] old;
+				}
+				else
+				{
+					memset(mMappedIndexData, 0, new_index_size);
+					mEmpty = TRUE;
+				}
+				mResized = TRUE;
+			}
+		}
+		else if (mGLIndices)
+		{
+			destroyGLIndices();
+		}
+	}
+}
+
+BOOL LLVertexBuffer::useVBOs() const
+{
+	//it's generally ineffective to use VBO for things that are streaming
+	//when we already have a client buffer around
+	if (mUsage == GL_STREAM_DRAW_ARB)
+	{
+		return FALSE;
+	}
+
+	return sEnableVBOs && (!sRenderActive || !mLocked);
+}
+
+//----------------------------------------------------------------------------
+
+// Map for data access
+U8* LLVertexBuffer::mapBuffer(S32 access)
+{
+	LLMemType mt(LLMemType::MTYPE_VERTEX_DATA);
+	if (sRenderActive)
+	{
+		llwarns << "Buffer mapped during render frame!" << llendl;
+	}
+	if (!mGLBuffer && !mGLIndices)
+	{
+		llerrs << "LLVertexBuffer::mapBuffer() called  before createGLBuffer" << llendl;
+	}
+	if (mFinal)
+	{
+		llerrs << "LLVertexBuffer::mapBuffer() called on a finalized buffer." << llendl;
+	}
+	if (!mMappedData && !mMappedIndexData)
+	{
+		llerrs << "LLVertexBuffer::mapBuffer() called on unallocated buffer." << llendl;
+	}
+	
+	if (!mLocked && useVBOs())
+	{
+		mLocked = TRUE;
+		sLockedList.push_back(this);
+	}
+	
+	return mMappedData;
+}
+
+void LLVertexBuffer::unmapBuffer()
+{
+	LLMemType mt(LLMemType::MTYPE_VERTEX_DATA);
+	if (mMappedData || mMappedIndexData)
+	{
+		if (useVBOs() && mLocked)
+		{
+			if (mGLBuffer)
+			{
+				if (mResized)
+				{
+					glBufferDataARB(GL_ARRAY_BUFFER_ARB, getSize(), mMappedData, mUsage);
+				}
+				else
+				{
+					if (mEmpty || mDirtyRegions.empty())
+					{
+						glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, 0, getSize(), mMappedData);
+					}
+					else
+					{
+						for (std::vector<DirtyRegion>::iterator i = mDirtyRegions.begin(); i != mDirtyRegions.end(); ++i)
+						{
+							DirtyRegion& region = *i;
+							glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, region.mIndex*mStride, region.mCount*mStride, mMappedData + region.mIndex*mStride);
+							glFlush();
+						}
+					}
+				}
+			}
+			
+			if (mGLIndices)
+			{
+				if (mResized)
+				{
+					glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, getIndicesSize(), mMappedIndexData, mUsage);
+				}
+				else
+				{
+					if (mEmpty || mDirtyRegions.empty())
+					{
+						glBufferSubDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0, getIndicesSize(), mMappedIndexData);
+					}
+					else
+					{
+						for (std::vector<DirtyRegion>::iterator i = mDirtyRegions.begin(); i != mDirtyRegions.end(); ++i)
+						{
+							DirtyRegion& region = *i;
+							glBufferSubDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, region.mIndicesIndex*sizeof(U32), 
+								region.mIndicesCount*sizeof(U32), mMappedIndexData + region.mIndicesIndex*sizeof(U32));
+							glFlush();
+						}
+					}
+				}
+			}
+
+			mDirtyRegions.clear();
+			mFilthy = FALSE;
+			mResized = FALSE;
+
+			if (mUsage == GL_STATIC_DRAW_ARB)
+			{ //static draw buffers can only be mapped a single time
+				//throw out client data (we won't be using it again)
+				delete [] mMappedData;
+				delete [] mMappedIndexData;
+				mMappedIndexData = NULL;
+				mMappedData = NULL;
+				mEmpty = TRUE;
+				mFinal = TRUE;
+			}
+			else
+			{
+				mEmpty = FALSE;
+			}
+			
+			mLocked = FALSE;
+			
+			glFlush();
+		}
+	}
+}
+
+//----------------------------------------------------------------------------
+
+template <class T,S32 type> struct VertexBufferStrider
+{
+	typedef LLStrider<T> strider_t;
+	static bool get(LLVertexBuffer& vbo, 
+					strider_t& strider, 
+					S32 index)
+	{
+		vbo.mapBuffer();
+		if (type == LLVertexBuffer::TYPE_INDEX)
+		{
+			S32 stride = sizeof(T);
+			strider = (T*)(vbo.getMappedIndices() + index*stride);
+			strider.setStride(0);
+			return TRUE;
+		}
+		else if (vbo.hasDataType(type))
+		{
+			S32 stride = vbo.getStride();
+			strider = (T*)(vbo.getMappedData() + vbo.getOffset(type) + index*stride);
+			strider.setStride(stride);
+			return TRUE;
+		}
+		else
+		{
+			llerrs << "VertexBufferStrider could not find valid vertex data." << llendl;
+		}
+		return FALSE;
+	}
+};
+
+
+bool LLVertexBuffer::getVertexStrider(LLStrider<LLVector3>& strider, S32 index)
+{
+	return VertexBufferStrider<LLVector3,TYPE_VERTEX>::get(*this, strider, index);
+}
+bool LLVertexBuffer::getIndexStrider(LLStrider<U32>& strider, S32 index)
+{
+	return VertexBufferStrider<U32,TYPE_INDEX>::get(*this, strider, index);
+}
+bool LLVertexBuffer::getTexCoordStrider(LLStrider<LLVector2>& strider, S32 index)
+{
+	return VertexBufferStrider<LLVector2,TYPE_TEXCOORD>::get(*this, strider, index);
+}
+bool LLVertexBuffer::getTexCoord2Strider(LLStrider<LLVector2>& strider, S32 index)
+{
+	return VertexBufferStrider<LLVector2,TYPE_TEXCOORD2>::get(*this, strider, index);
+}
+bool LLVertexBuffer::getNormalStrider(LLStrider<LLVector3>& strider, S32 index)
+{
+	return VertexBufferStrider<LLVector3,TYPE_NORMAL>::get(*this, strider, index);
+}
+bool LLVertexBuffer::getBinormalStrider(LLStrider<LLVector3>& strider, S32 index)
+{
+	return VertexBufferStrider<LLVector3,TYPE_BINORMAL>::get(*this, strider, index);
+}
+bool LLVertexBuffer::getColorStrider(LLStrider<LLColor4U>& strider, S32 index)
+{
+	return VertexBufferStrider<LLColor4U,TYPE_COLOR>::get(*this, strider, index);
+}
+bool LLVertexBuffer::getWeightStrider(LLStrider<F32>& strider, S32 index)
+{
+	return VertexBufferStrider<F32,TYPE_WEIGHT>::get(*this, strider, index);
+}
+bool LLVertexBuffer::getClothWeightStrider(LLStrider<LLVector4>& strider, S32 index)
+{
+	return VertexBufferStrider<LLVector4,TYPE_CLOTHWEIGHT>::get(*this, strider, index);
+}
+
+void LLVertexBuffer::setStride(S32 type, S32 new_stride)
+{
+	LLMemType mt(LLMemType::MTYPE_VERTEX_DATA);
+	if (mNumVerts)
+	{
+		llerrs << "LLVertexBuffer::setOffset called with mNumVerts = " << mNumVerts << llendl;
+	}
+	// This code assumes that setStride() will only be called once per VBO per type.
+	S32 delta = new_stride - sTypeOffsets[type];
+	for (S32 i=type+1; i<TYPE_MAX; i++)
+	{
+		if (mTypeMask & (1<<i))
+		{
+			mOffsets[i] += delta;
+		}
+	}
+	mStride += delta;
+}
+
+//----------------------------------------------------------------------------
+
+// Set for rendering
+void LLVertexBuffer::setBuffer(U32 data_mask)
+{
+	LLMemType mt(LLMemType::MTYPE_VERTEX_DATA);
+	//set up pointers if the data mask is different ...
+	BOOL setup = (sLastMask != data_mask);
+
+	if (useVBOs())
+	{
+		if (mGLBuffer && (mGLBuffer != sGLRenderBuffer || !sVBOActive))
+		{
+			glBindBufferARB(GL_ARRAY_BUFFER_ARB, mGLBuffer);
+			sVBOActive = TRUE;
+			setup = TRUE; // ... or the bound buffer changed
+		}
+		if (mGLIndices && (mGLIndices != sGLRenderIndices || !sIBOActive))
+		{
+			glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, mGLIndices);
+			sIBOActive = TRUE;
+		}
+		
+		unmapBuffer();
+	}
+	else
+	{
+		if (!mDirtyRegions.empty())
+		{
+			mFilthy = TRUE;
+			mDirtyRegions.clear();
+		}
+		
+		if (mGLBuffer)
+		{
+			if (sEnableVBOs && sVBOActive)
+			{
+				glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
+				sVBOActive = FALSE;
+				setup = TRUE; // ... or a VBO is deactivated
+			}
+			if (sGLRenderBuffer != mGLBuffer)
+			{
+				setup = TRUE; // ... or a client memory pointer changed
+			}
+		}
+		if (sEnableVBOs && mGLIndices && sIBOActive)
+		{
+			glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
+			sIBOActive = FALSE;
+		}
+	}
+	
+	if (mGLIndices)
+	{
+		sGLRenderIndices = mGLIndices;
+	}
+	if (mGLBuffer)
+	{
+		sGLRenderBuffer = mGLBuffer;
+		if (data_mask && setup)
+		{
+			if (!sRenderActive)
+			{
+				llwarns << "Vertex buffer set for rendering outside of render frame." << llendl;
+			}
+			setupVertexBuffer(data_mask); // subclass specific setup (virtual function)
+			sLastMask = data_mask;
+		}
+	}
+}
+
+// virtual (default)
+void LLVertexBuffer::setupVertexBuffer(U32 data_mask) const
+{
+	LLMemType mt(LLMemType::MTYPE_VERTEX_DATA);
+	stop_glerror();
+	U8* base = useVBOs() ? NULL : mMappedData;
+	S32 stride = mStride;
+
+	if ((data_mask & mTypeMask) != data_mask)
+	{
+		llerrs << "LLVertexBuffer::setupVertexBuffer missing required components for supplied data mask." << llendl;
+	}
+
+	if (data_mask & MAP_VERTEX)
+	{
+		glVertexPointer(3,GL_FLOAT, stride, (void*)(base + 0));
+	}
+	if (data_mask & MAP_NORMAL)
+	{
+		glNormalPointer(GL_FLOAT, stride, (void*)(base + mOffsets[TYPE_NORMAL]));
+	}
+	if (data_mask & MAP_TEXCOORD2)
+	{
+		glClientActiveTextureARB(GL_TEXTURE1_ARB);
+		glTexCoordPointer(2,GL_FLOAT, stride, (void*)(base + mOffsets[TYPE_TEXCOORD2]));
+	}
+	if (data_mask & MAP_TEXCOORD)
+	{
+		glClientActiveTextureARB(GL_TEXTURE0_ARB);
+		glTexCoordPointer(2,GL_FLOAT, stride, (void*)(base + mOffsets[TYPE_TEXCOORD]));
+	}
+	if (data_mask & MAP_COLOR)
+	{
+		glColorPointer(4, GL_UNSIGNED_BYTE, stride, (void*)(base + mOffsets[TYPE_COLOR]));
+	}
+	if (data_mask & MAP_BINORMAL)
+	{
+		glVertexAttribPointerARB(6, 3, GL_FLOAT, FALSE,  stride, (void*)(base + mOffsets[TYPE_BINORMAL]));
+	}
+	if (data_mask & MAP_WEIGHT)
+	{
+		glVertexAttribPointerARB(1, 1, GL_FLOAT, FALSE, stride, (void*)(base + mOffsets[TYPE_WEIGHT]));
+	}
+	if (data_mask & MAP_CLOTHWEIGHT)
+	{
+		glVertexAttribPointerARB(4, 4, GL_FLOAT, TRUE,  stride, (void*)(base + mOffsets[TYPE_CLOTHWEIGHT]));
+	}
+
+	llglassertok();
+}
+
+void LLVertexBuffer::markDirty(U32 vert_index, U32 vert_count, U32 indices_index, U32 indices_count)
+{
+	if (useVBOs() && !mFilthy)
+	{
+		if (!mDirtyRegions.empty())
+		{
+			DirtyRegion& region = *(mDirtyRegions.rbegin());
+			if (region.mIndex + region.mCount == vert_index &&
+				region.mIndicesIndex + region.mIndicesCount == indices_index)
+			{
+				region.mCount += vert_count;
+				region.mIndicesCount += indices_count;
+				return;
+			}
+		}
+
+		mDirtyRegions.push_back(DirtyRegion(vert_index,vert_count,indices_index,indices_count));
+	}
+}
+
+void LLVertexBuffer::markClean()
+{
+	if (!mResized && !mEmpty && !mFilthy)
+	{
+		buffer_list_t::reverse_iterator iter = sLockedList.rbegin();
+		if (*iter == this)
+		{
+			mLocked = FALSE;
+			sLockedList.pop_back();
+		}
+	}
+}
+
diff --git a/indra/llrender/llvertexbuffer.h b/indra/llrender/llvertexbuffer.h
new file mode 100644
index 0000000000000000000000000000000000000000..e672321e761e69db43f3650cd65b8d2edbd87447
--- /dev/null
+++ b/indra/llrender/llvertexbuffer.h
@@ -0,0 +1,179 @@
+#ifndef LL_LLVERTEXBUFFER_H
+#define LL_LLVERTEXBUFFER_H
+
+#include "llgl.h"
+#include "v2math.h"
+#include "v3math.h"
+#include "v4math.h"
+#include "v4coloru.h"
+#include "llstrider.h"
+#include "llmemory.h"
+#include <set>
+#include <vector>
+
+//============================================================================
+// NOTES
+// Threading:
+//  All constructors should take an 'create' paramater which should only be
+//  'true' when called from the main thread. Otherwise createGLBuffer() will
+//  be called as soon as getVertexPointer(), etc is called (which MUST ONLY be
+//  called from the main (i.e OpenGL) thread)
+
+//============================================================================
+// base class
+
+class LLVertexBuffer : public LLRefCount
+{
+public:
+	static void initClass(bool use_vbo);
+	static void cleanupClass();
+ 	static void startRender(); //between start and stop render, no client copies will occur
+	static void stopRender(); //any buffer not copied to GL will be rendered from client memory
+	static void clientCopy(); //copy data from client to GL
+	static void unbind(); //unbind any bound vertex buffer
+
+	enum {
+		TYPE_VERTEX,
+		TYPE_NORMAL,
+		TYPE_TEXCOORD,
+		TYPE_TEXCOORD2,
+		TYPE_COLOR,
+		// These use VertexAttribPointer and should possibly be made generic
+		TYPE_BINORMAL,
+		TYPE_WEIGHT,
+		TYPE_CLOTHWEIGHT,
+		TYPE_MAX,
+		TYPE_INDEX,
+	};
+	enum {
+		MAP_VERTEX = (1<<TYPE_VERTEX),
+		MAP_NORMAL = (1<<TYPE_NORMAL),
+		MAP_TEXCOORD = (1<<TYPE_TEXCOORD),
+		MAP_TEXCOORD2 = (1<<TYPE_TEXCOORD2),
+		MAP_COLOR = (1<<TYPE_COLOR),
+		// These use VertexAttribPointer and should possibly be made generic
+		MAP_BINORMAL = (1<<TYPE_BINORMAL),
+		MAP_WEIGHT = (1<<TYPE_WEIGHT),
+		MAP_CLOTHWEIGHT = (1<<TYPE_CLOTHWEIGHT),
+		MAP_DRAW = 0x2000, // Buffer is in draw (read-only) mode
+		MAP_MAPPED = 0x4000, // Indicates that buffer has been mapped, but not to any type of data
+		MAP_UNMAPPED = 0x8000 // Indicates that buffer has been logically un-mapped
+	};
+	
+protected:
+	virtual ~LLVertexBuffer(); // use unref()
+
+	virtual void setupVertexBuffer(U32 data_mask) const; // pure virtual, called from mapBuffer()
+
+	void	createGLBuffer();
+	void	createGLIndices();
+	void 	destroyGLBuffer();
+	void 	destroyGLIndices();
+	void	updateNumVerts(S32 nverts);
+	void	updateNumIndices(S32 nindices); 
+	virtual BOOL	useVBOs() const;
+	void	unmapBuffer();
+	
+public:
+	LLVertexBuffer(U32 typemask, S32 usage);
+	
+	// map for data access
+	U8*		mapBuffer(S32 access = -1);
+	// set for rendering
+	virtual void	setBuffer(U32 data_mask); 	// calls  setupVertexBuffer() if data_mask is not 0
+	// allocate buffer
+	void	allocateBuffer(S32 nverts, S32 nindices, bool create);
+	virtual void resizeBuffer(S32 newnverts, S32 newnindices);
+	void	makeStatic();
+	
+	// Only call each getVertexPointer, etc, once before calling unmapBuffer()
+	// call unmapBuffer() after calls to getXXXStrider() before any cals to setBuffer()
+	// example:
+	//   vb->getVertexBuffer(verts);
+	//   vb->getNormalStrider(norms);
+	//   setVertsNorms(verts, norms);
+	//   vb->unmapBuffer();
+	bool getVertexStrider(LLStrider<LLVector3>& strider, S32 index=0);
+	bool getIndexStrider(LLStrider<U32>& strider, S32 index=0);
+	bool getTexCoordStrider(LLStrider<LLVector2>& strider, S32 index=0);
+	bool getTexCoord2Strider(LLStrider<LLVector2>& strider, S32 index=0);
+	bool getNormalStrider(LLStrider<LLVector3>& strider, S32 index=0);
+	bool getBinormalStrider(LLStrider<LLVector3>& strider, S32 index=0);
+	bool getColorStrider(LLStrider<LLColor4U>& strider, S32 index=0);
+	bool getWeightStrider(LLStrider<F32>& strider, S32 index=0);
+	bool getClothWeightStrider(LLStrider<LLVector4>& strider, S32 index=0);
+	
+	BOOL isEmpty() const					{ return mEmpty; }
+	BOOL isLocked() const					{ return mLocked; }
+	S32 getNumVerts() const					{ return mNumVerts; }
+	S32 getNumIndices() const				{ return mNumIndices; }
+	U8* getIndicesPointer() const			{ return useVBOs() ? NULL : mMappedIndexData; }
+	U8* getVerticesPointer() const			{ return useVBOs() ? NULL : mMappedData; }
+	S32 getStride() const					{ return mStride; }
+	S32 getTypeMask() const					{ return mTypeMask; }
+	BOOL hasDataType(S32 type) const		{ return ((1 << type) & getTypeMask()) ? TRUE : FALSE; }
+	S32 getSize() const						{ return mNumVerts*mStride; }
+	S32 getIndicesSize() const				{ return mNumIndices * sizeof(U32); }
+	U8* getMappedData() const				{ return mMappedData; }
+	U8* getMappedIndices() const			{ return mMappedIndexData; }
+	S32 getOffset(S32 type) const			{ return mOffsets[type]; }
+	S32 getUsage() const					{ return mUsage; }
+
+	void setStride(S32 type, S32 new_stride);
+	
+	void markDirty(U32 vert_index, U32 vert_count, U32 indices_index, U32 indices_count);
+	void markClean();
+
+protected:	
+	S32		mNumVerts;		// Number of vertices
+	S32		mNumIndices;	// Number of indices
+	S32		mStride;
+	U32		mTypeMask;
+	S32		mUsage;			// GL usage
+	U32		mGLBuffer;		// GL VBO handle
+	U32		mGLIndices;		// GL IBO handle
+	U8*		mMappedData;	// pointer to currently mapped data (NULL if unmapped)
+	U8*		mMappedIndexData;	// pointer to currently mapped indices (NULL if unmapped)
+	BOOL	mLocked;			// if TRUE, buffer is being or has been written to in client memory
+	BOOL	mFinal;			// if TRUE, buffer can not be mapped again
+	BOOL	mFilthy;		// if TRUE, entire buffer must be copied (used to prevent redundant dirty flags)
+	BOOL	mEmpty;			// if TRUE, client buffer is empty (or NULL). Old values have been discarded.
+	S32		mOffsets[TYPE_MAX];
+	BOOL	mResized;		// if TRUE, client buffer has been resized and GL buffer has not
+	BOOL	mDynamicSize;	// if TRUE, buffer has been resized at least once (and should be padded)
+
+	class DirtyRegion
+	{
+	public:
+		U32 mIndex;
+		U32 mCount;
+		U32 mIndicesIndex;
+		U32 mIndicesCount;
+
+		DirtyRegion(U32 vi, U32 vc, U32 ii, U32 ic)
+			: mIndex(vi), mCount(vc), mIndicesIndex(ii), mIndicesCount(ic)
+		{ }	
+	};
+
+	std::vector<DirtyRegion> mDirtyRegions; //vector of dirty regions to rebuild
+
+public:
+	static BOOL sRenderActive;
+	static S32 sCount;
+	static S32 sGLCount;
+	static std::vector<U32> sDeleteList;
+	typedef std::list<LLVertexBuffer*> buffer_list_t;
+	static buffer_list_t sLockedList;
+	
+	static BOOL sEnableVBOs;
+	static S32 sTypeOffsets[TYPE_MAX];
+	static S32 sGLRenderBuffer;
+	static S32 sGLRenderIndices;
+	static BOOL sVBOActive;
+	static BOOL sIBOActive;
+	static U32 sLastMask;
+	static U32 sAllocatedBytes;
+};
+
+
+#endif // LL_LLVERTEXBUFFER_H
diff --git a/indra/llui/llcombobox.cpp b/indra/llui/llcombobox.cpp
index fc7a29887b4deea54fccd4c4d25e3c8c24bb2341..667985c699facd9e791f16305d15c6fb24b7ea52 100644
--- a/indra/llui/llcombobox.cpp
+++ b/indra/llui/llcombobox.cpp
@@ -43,6 +43,7 @@ LLComboBox::LLComboBox(	const LLString& name, const LLRect &rect, const LLString
 	mDrawButton(TRUE),
 	mTextEntry(NULL),
 	mArrowImage(NULL),
+	mArrowImageWidth(8),
 	mAllowTextEntry(FALSE),
 	mMaxChars(20),
 	mTextEntryTentative(TRUE),
@@ -99,6 +100,7 @@ LLComboBox::LLComboBox(	const LLString& name, const LLRect &rect, const LLString
 
 	LLUUID arrow_image_id( LLUI::sAssetsGroup->getString("combobox_arrow.tga") );
 	mArrowImage = LLUI::sImageProvider->getUIImageByID(arrow_image_id);
+	mArrowImageWidth = llmax(8,mArrowImage->getWidth()); // In case image hasn't loaded yet
 }
 
 
@@ -502,7 +504,7 @@ void LLComboBox::setButtonVisible(BOOL visible)
 		LLRect text_entry_rect(0, mRect.getHeight(), mRect.getWidth(), 0);
 		if (visible)
 		{
-			text_entry_rect.mRight -= mArrowImage->getWidth() + 2 * LLUI::sConfigGroup->getS32("DropShadowButton");
+			text_entry_rect.mRight -= mArrowImageWidth + 2 * LLUI::sConfigGroup->getS32("DropShadowButton");
 		}
 		//mTextEntry->setRect(text_entry_rect);
 		mTextEntry->reshape(text_entry_rect.getWidth(), text_entry_rect.getHeight(), TRUE);
@@ -525,7 +527,7 @@ void LLComboBox::draw()
 			// Paste the graphic on the right edge
 			if (!mArrowImage.isNull())
 			{
-				S32 left = mRect.getWidth() - mArrowImage->getWidth() - LLUI::sConfigGroup->getS32("DropShadowButton");
+				S32 left = mRect.getWidth() - mArrowImageWidth - LLUI::sConfigGroup->getS32("DropShadowButton");
 
 				gl_draw_image( left, 0, mArrowImage,
 					LLColor4::white);
@@ -825,7 +827,7 @@ void LLComboBox::setAllowTextEntry(BOOL allow, S32 max_chars, BOOL set_tentative
 	if (allow && !mAllowTextEntry)
 	{
 		S32 shadow_size = LLUI::sConfigGroup->getS32("DropShadowButton");
-		mButton->setRect(LLRect( mRect.getWidth() - mArrowImage->getWidth() - 2 * shadow_size,
+		mButton->setRect(LLRect( mRect.getWidth() - mArrowImageWidth - 2 * shadow_size,
 								rect.mTop, rect.mRight, rect.mBottom));
 		mButton->setTabStop(FALSE);
 
@@ -835,7 +837,7 @@ void LLComboBox::setAllowTextEntry(BOOL allow, S32 max_chars, BOOL set_tentative
 		if (!mTextEntry)
 		{
 			LLRect text_entry_rect(0, mRect.getHeight(), mRect.getWidth(), 0);
-			text_entry_rect.mRight -= mArrowImage->getWidth() + 2 * LLUI::sConfigGroup->getS32("DropShadowButton");
+			text_entry_rect.mRight -= mArrowImageWidth + 2 * LLUI::sConfigGroup->getS32("DropShadowButton");
 			mTextEntry = new LLLineEditor("combo_text_entry",
 										text_entry_rect,
 										"",
diff --git a/indra/llui/llcombobox.h b/indra/llui/llcombobox.h
index 1ec31ec1c0f1158a1a3eeedfba8645da54826ca8..faf99937c994e4939178212bb81329de6688b90e 100644
--- a/indra/llui/llcombobox.h
+++ b/indra/llui/llcombobox.h
@@ -167,8 +167,9 @@ protected:
 	BOOL				mDrawButton;
 	LLLineEditor*		mTextEntry;
 	LLPointer<LLImageGL>	mArrowImage;
+	S32					mArrowImageWidth;
 	BOOL				mAllowTextEntry;
-	S32				mMaxChars;
+	S32					mMaxChars;
 	BOOL				mTextEntryTentative;
 	void				(*mPrearrangeCallback)(LLUICtrl*,void*);
 	void				(*mTextEntryCallback)(LLLineEditor*, void*);
diff --git a/indra/llui/llpanel.h b/indra/llui/llpanel.h
index 59922994c15b6dca2790609d89a3977969eea213..fdf4a117d452413a9a1e396fd9187e57e9ce60cd 100644
--- a/indra/llui/llpanel.h
+++ b/indra/llui/llpanel.h
@@ -17,6 +17,7 @@
 #include "llviewborder.h"
 #include "v4color.h"
 #include <list>
+#include <queue>
 
 const S32 LLPANEL_BORDER_WIDTH = 1;
 const BOOL BORDER_YES = TRUE;
diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp
index c168bbe5a84e92f97e87347c40c66401eeb2a6e7..f2ae318dca9afb924de24d5c3aa7899dc2df6e4a 100644
--- a/indra/llui/llscrolllistctrl.cpp
+++ b/indra/llui/llscrolllistctrl.cpp
@@ -2720,7 +2720,7 @@ void LLScrollListCtrl::setFocus(BOOL b)
 	if (!getFirstSelected())
 	{
 		selectFirstItem();
-		onCommit();
+		//onCommit(); // SJB: selectFirstItem() will call onCommit() if appropriate
 	}
 	LLUICtrl::setFocus(b);
 }
diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp
index 710e333796be75db3f94b8395031182d7e5a3760..de34aabb1ff9b11b1d13ac9fd32e639492031c2c 100644
--- a/indra/llui/lltexteditor.cpp
+++ b/indra/llui/lltexteditor.cpp
@@ -3934,6 +3934,8 @@ LLXMLNodePtr LLTextEditor::getXML(bool save_children) const
 
 	node->createChild("word_wrap", TRUE)->setBoolValue(mWordWrap);
 
+	node->createChild("hide_scrollbar", TRUE)->setBoolValue(mHideScrollbarForShortDocs);
+
 	addColorXML(node, mCursorColor, "cursor_color", "TextCursorColor");
 	addColorXML(node, mFgColor, "text_color", "TextFgColor");
 	addColorXML(node, mReadOnlyFgColor, "text_readonly_color", "TextFgReadOnlyColor");
diff --git a/indra/llui/lluictrl.h b/indra/llui/lluictrl.h
index f58b7d6e16de4e0edfa944c2214ef894e03af1f1..14181512014b7eb57e701f180ecfc40ff3b6bad4 100644
--- a/indra/llui/lluictrl.h
+++ b/indra/llui/lluictrl.h
@@ -17,7 +17,6 @@
 //
 // Classes
 //
-class LLViewerImage;
 class LLFontGL;
 class LLButton;
 class LLTextBox;
diff --git a/indra/llui/lluictrlfactory.cpp b/indra/llui/lluictrlfactory.cpp
index 8ae9fd02843d667488f6f6539f93bbf35f88294c..21278455dacc9f1b1de59447345680eb64132497 100644
--- a/indra/llui/lluictrlfactory.cpp
+++ b/indra/llui/lluictrlfactory.cpp
@@ -191,12 +191,18 @@ LLUICtrlFactory::LLUICtrlFactory()
 	LLUICtrlCreator<LLMenuBarGL>::registerCreator(LL_MENU_BAR_GL_TAG, this);
 	LLUICtrlCreator<LLScrollingPanelList>::registerCreator(LL_SCROLLING_PANEL_LIST_TAG, this);
 
+	setupPaths();
 
+}
+
+void LLUICtrlFactory::setupPaths()
+{
 	LLString filename = gDirUtilp->getExpandedFilename(LL_PATH_SKINS, "paths.xml");
 
 	LLXMLNodePtr root;
 	BOOL success  = LLXMLNode::parseFile(filename, root, NULL);
-
+	mXUIPaths.clear();
+	
 	if (!success)
 	{
 		LLString slash = gDirUtilp->getDirDelimiter();
@@ -220,7 +226,7 @@ LLUICtrlFactory::LLUICtrlFactory()
 			path_val_ui.setArg("[Language]", language);
 			LLString fullpath = app_dir + path_val_ui.getString();
 
-			if (mXUIPaths.empty() || (find(mXUIPaths.begin(), mXUIPaths.end(), fullpath) == mXUIPaths.end()) )
+			if (std::find(mXUIPaths.begin(), mXUIPaths.end(), fullpath) == mXUIPaths.end())
 			{
 				mXUIPaths.push_back(app_dir + path_val_ui.getString());
 			}
diff --git a/indra/llui/lluictrlfactory.h b/indra/llui/lluictrlfactory.h
index b3bd5c90203888c281a838cadea886cb0601336a..cb6864bafdad40433e1801827ecc68579293a502 100644
--- a/indra/llui/lluictrlfactory.h
+++ b/indra/llui/lluictrlfactory.h
@@ -55,6 +55,8 @@ public:
 	// do not call!  needs to be public so run-time can clean up the singleton
 	virtual ~LLUICtrlFactory();
 
+	void setupPaths();
+	
 	void buildFloater(LLFloater* floaterp, const LLString &filename, 
 						const LLCallbackMap::map_t* factory_map = NULL, BOOL open = TRUE);
 
diff --git a/indra/llvfs/lldir.cpp b/indra/llvfs/lldir.cpp
index 9f8d6c08d556da900bf5751b26566be5678d0e30..d8d77e6a233b47ce953b25ea0f679d4b08da1c9f 100644
--- a/indra/llvfs/lldir.cpp
+++ b/indra/llvfs/lldir.cpp
@@ -183,6 +183,27 @@ const std::string &LLDir::getTempDir() const
 	return mTempDir;
 }
 
+const std::string  LLDir::getCacheDir(bool get_default) const
+{
+	if (mCacheDir.empty() || get_default)
+	{
+		std::string res;
+		if (getOSUserAppDir().empty())
+		{
+			res = "data";
+		}
+		else
+		{
+			res = getOSUserAppDir() + mDirDelimiter + "cache";
+		}
+		return res;
+	}
+	else
+	{
+		return mCacheDir;
+	}
+}
+
 const std::string &LLDir::getCAFile() const
 {
 	return mCAFile;
@@ -198,7 +219,12 @@ const std::string &LLDir::getSkinDir() const
 	return mSkinDir;
 }
 
-std::string LLDir::getExpandedFilename(ELLPath location, const std::string &filename) const
+std::string LLDir::getExpandedFilename(ELLPath location, const std::string& filename) const
+{
+	return getExpandedFilename(location, "", filename);
+}
+
+std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subdir, const std::string& in_filename) const
 {
 	std::string prefix;
 	switch (location)
@@ -230,16 +256,7 @@ std::string LLDir::getExpandedFilename(ELLPath location, const std::string &file
 		break;
 		
 	case LL_PATH_CACHE:
-		if (getOSUserAppDir().empty())
-		{
-			prefix = "data";
-		}
-		else
-		{
-			prefix = getOSUserAppDir();
-			prefix += mDirDelimiter;
-			prefix += "cache";
-		}
+	    prefix = getCacheDir();
 		break;
 		
 	case LL_PATH_USER_SETTINGS:
@@ -290,6 +307,16 @@ std::string LLDir::getExpandedFilename(ELLPath location, const std::string &file
 		llassert(0);
 	}
 
+	std::string filename = in_filename;
+	if (!subdir.empty())
+	{
+		filename = subdir + mDirDelimiter + in_filename;
+	}
+	else
+	{
+		filename = in_filename;
+	}
+	
 	std::string expanded_filename;
 	if (!filename.empty())
 	{
@@ -304,8 +331,7 @@ std::string LLDir::getExpandedFilename(ELLPath location, const std::string &file
 			expanded_filename = filename;
 		}
 	}
-	else
-	if (!prefix.empty())
+	else if (!prefix.empty())
 	{
 		// Directory only, no file name.
 		expanded_filename = prefix;
@@ -405,6 +431,30 @@ void LLDir::setSkinFolder(const std::string &skin_folder)
 	mSkinDir += skin_folder;
 }
 
+bool LLDir::setCacheDir(const std::string &path)
+{
+	if (path.empty() )
+	{
+		// reset to default
+		mCacheDir = "";
+		return true;
+	}
+	else
+	{
+		LLFile::mkdir(path.c_str());
+		std::string tempname = path + mDirDelimiter + "temp";
+		LLFILE* file = LLFile::fopen(tempname.c_str(),"wt");
+		if (file)
+		{
+			fclose(file);
+			LLFile::remove(tempname.c_str());
+			mCacheDir = path;
+			return true;
+		}
+		return false;
+	}
+}
+
 void LLDir::dumpCurrentDirectories()
 {
 	llinfos << "Current Directories:" << llendl;
diff --git a/indra/llvfs/lldir.h b/indra/llvfs/lldir.h
index 710dcd1ae34f4a0b40642362f68f2fa3d526aa76..5389378def017c5007b94c56ef5f2b57e2d7d473 100644
--- a/indra/llvfs/lldir.h
+++ b/indra/llvfs/lldir.h
@@ -60,12 +60,14 @@ class LLDir
 	const std::string &getChatLogsDir() const;	// Location of the chat logs dir.
 	const std::string &getPerAccountChatLogsDir() const;	// Location of the per account chat logs dir.
 	const std::string &getTempDir() const;			// Common temporary directory
+	const std::string  getCacheDir(bool get_default = false) const;	// Location of the cache.
 	const std::string &getCAFile() const;			// File containing TLS certificate authorities
 	const std::string &getDirDelimiter() const;	// directory separator for platform (ie. '\' or '/' or ':')
 	const std::string &getSkinDir() const;		// User-specified skin folder.
 
 	// Expanded filename
 	std::string getExpandedFilename(ELLPath location, const std::string &filename) const;
+	std::string getExpandedFilename(ELLPath location, const std::string &subdir, const std::string &filename) const;
 
 	// random filename in common temporary directory
 	std::string getTempFilename() const;
@@ -74,6 +76,7 @@ class LLDir
 	virtual void setPerAccountChatLogsDir(const std::string &first, const std::string &last);		// Set the per user chat log directory.
 	virtual void setLindenUserDir(const std::string &first, const std::string &last);		// Set the linden user dir to this user's dir
 	virtual void setSkinFolder(const std::string &skin_folder);
+	virtual bool setCacheDir(const std::string &path);
 
 	virtual void dumpCurrentDirectories();
 	
@@ -91,6 +94,7 @@ protected:
 	std::string mChatLogsDir;		 // Location for chat logs.
 	std::string mCAFile;				 // Location of the TLS certificate authority PEM file.
 	std::string mTempDir;
+	std::string mCacheDir;
 	std::string mDirDelimiter;
 	std::string mSkinDir;			// Location for u ser-specified skin info.
 };
diff --git a/indra/llvfs/lllfsthread.cpp b/indra/llvfs/lllfsthread.cpp
index 6af638fd12a59ac0a050d92b5037d25fb2baec3b..598de1d3707cd1430a9d8f135061739f2f0a7275 100644
--- a/indra/llvfs/lllfsthread.cpp
+++ b/indra/llvfs/lllfsthread.cpp
@@ -19,10 +19,10 @@
 //============================================================================
 // Run on MAIN thread
 //static
-void LLLFSThread::initClass(bool local_is_threaded, bool local_run_always)
+void LLLFSThread::initClass(bool local_is_threaded)
 {
 	llassert(sLocal == NULL);
-	sLocal = new LLLFSThread(local_is_threaded, local_run_always);
+	sLocal = new LLLFSThread(local_is_threaded);
 }
 
 //static
@@ -46,8 +46,9 @@ void LLLFSThread::cleanupClass()
 
 //----------------------------------------------------------------------------
 
-LLLFSThread::LLLFSThread(bool threaded, bool runalways) :
-	LLQueuedThread("LFS", threaded, runalways)
+LLLFSThread::LLLFSThread(bool threaded) :
+	LLQueuedThread("LFS", threaded),
+	mPriorityCounter(PRIORITY_LOWBITS)
 {
 }
 
@@ -59,250 +60,164 @@ LLLFSThread::~LLLFSThread()
 //----------------------------------------------------------------------------
 
 LLLFSThread::handle_t LLLFSThread::read(const LLString& filename,	/* Flawfinder: ignore */ 
-										U8* buffer, S32 offset, S32 numbytes, U32 priority, U32 flags)
+										U8* buffer, S32 offset, S32 numbytes,
+										Responder* responder, U32 priority)
 {
 	handle_t handle = generateHandle();
 
-	priority = llmax(priority, (U32)PRIORITY_LOW); // All reads are at least PRIORITY_LOW
-	Request* req = new Request(handle, priority, flags,
+	if (priority == 0) priority = PRIORITY_NORMAL | priorityCounter();
+	else if (priority < PRIORITY_LOW) priority |= PRIORITY_LOW; // All reads are at least PRIORITY_LOW
+
+	Request* req = new Request(this, handle, priority,
 							   FILE_READ, filename,
-							   buffer, offset, numbytes);
+							   buffer, offset, numbytes,
+							   responder);
 
 	bool res = addRequest(req);
 	if (!res)
 	{
 		llerrs << "LLLFSThread::read called after LLLFSThread::cleanupClass()" << llendl;
-		req->deleteRequest();
-		handle = nullHandle();
 	}
 
 	return handle;
 }
 
-S32 LLLFSThread::readImmediate(const LLString& filename,
-							   U8* buffer, S32 offset, S32 numbytes)
-{
-	handle_t handle = generateHandle();
-
-	Request* req = new Request(handle, PRIORITY_IMMEDIATE, 0,
-							   FILE_READ, filename,
-							   buffer, offset, numbytes);
-	
-	S32 res = addRequest(req) ? 1 : 0;
-	if (res == 0)
-	{
-		llerrs << "LLLFSThread::read called after LLLFSThread::cleanupClass()" << llendl;
-		req->deleteRequest();
-	}
-	else
-	{
-		llverify(waitForResult(handle, false) == true);
-		res = req->getBytesRead();
-		completeRequest(handle);
-	}
-	return res;
-}
-
 LLLFSThread::handle_t LLLFSThread::write(const LLString& filename,
-										 U8* buffer, S32 offset, S32 numbytes, U32 flags)
+										 U8* buffer, S32 offset, S32 numbytes,
+										 Responder* responder, U32 priority)
 {
 	handle_t handle = generateHandle();
 
-	Request* req = new Request(handle, 0, flags,
-							   FILE_WRITE, filename,
-							   buffer, offset, numbytes);
-
-	bool res = addRequest(req);
-	if (!res)
-	{
-		llerrs << "LLLFSThread::read called after LLLFSThread::cleanupClass()" << llendl;
-		req->deleteRequest();
-		handle = nullHandle();
-	}
+	if (priority == 0) priority = PRIORITY_LOW | priorityCounter();
 	
-	return handle;
-}
-
-S32 LLLFSThread::writeImmediate(const LLString& filename,
-								 U8* buffer, S32 offset, S32 numbytes)
-{
-	handle_t handle = generateHandle();
-
-	Request* req = new Request(handle, PRIORITY_IMMEDIATE, 0,
+	Request* req = new Request(this, handle, priority,
 							   FILE_WRITE, filename,
-							   buffer, offset, numbytes);
-
-	S32 res = addRequest(req) ? 1 : 0;
-	if (res == 0)
-	{
-		llerrs << "LLLFSThread::write called after LLLFSThread::cleanupClass()" << llendl;
-		req->deleteRequest();
-	}
-	else
-	{
-		llverify(waitForResult(handle, false) == true);
-		res = req->getBytesRead();
-		completeRequest(handle);
-	}
-	return res;
-}
-
-
-LLLFSThread::handle_t LLLFSThread::rename(const LLString& filename, const LLString& newname, U32 flags)
-{
-	handle_t handle = generateHandle();
-
-	LLString* new_name_str = new LLString(newname); // deleted with Request
-	Request* req = new Request(handle, 0, flags,
-							   FILE_RENAME, filename,
-							   (U8*)new_name_str, 0, 0);
-
-	bool res = addRequest(req);
-	if (!res)
-	{
-		llerrs << "LLLFSThread::rename called after LLLFSThread::cleanupClass()" << llendl;
-		req->deleteRequest();
-		handle = nullHandle();
-	}
-	
-	return handle;
-}
-
-LLLFSThread::handle_t LLLFSThread::remove(const LLString& filename, U32 flags)
-{
-	handle_t handle = generateHandle();
-
-	Request* req = new Request(handle, 0, flags,
-							   FILE_RENAME, filename,
-							   NULL, 0, 0);
+							   buffer, offset, numbytes,
+							   responder);
 
 	bool res = addRequest(req);
 	if (!res)
 	{
-		llerrs << "LLLFSThread::remove called after LLLFSThread::cleanupClass()" << llendl;
-		req->deleteRequest();
-		handle = nullHandle();
+		llerrs << "LLLFSThread::read called after LLLFSThread::cleanupClass()" << llendl;
 	}
 	
 	return handle;
 }
 
-//============================================================================
-// Runs on its OWN thread
-
-bool LLLFSThread::processRequest(QueuedRequest* qreq)
-{
-	Request *req = (Request*)qreq;
-
-	bool complete = req->processIO();
-
-	return complete;
-}
-
 //============================================================================
 
-LLLFSThread::Request::Request(handle_t handle, U32 priority, U32 flags,
+LLLFSThread::Request::Request(LLLFSThread* thread,
+							  handle_t handle, U32 priority,
 							  operation_t op, const LLString& filename,
-							  U8* buffer, S32 offset, S32 numbytes) :
-	QueuedRequest(handle, priority, flags),
+							  U8* buffer, S32 offset, S32 numbytes,
+							  Responder* responder) :
+	QueuedRequest(handle, priority, FLAG_AUTO_COMPLETE),
+	mThread(thread),
 	mOperation(op),
 	mFileName(filename),
 	mBuffer(buffer),
 	mOffset(offset),
 	mBytes(numbytes),
-	mBytesRead(0)
+	mBytesRead(0),
+	mResponder(responder)
 {
-	llassert(mBuffer);
-
-	if (numbytes <= 0 && mOperation != FILE_RENAME && mOperation != FILE_REMOVE)
+	if (numbytes <= 0)
 	{
 		llwarns << "LLLFSThread: Request with numbytes = " << numbytes << llendl;
 	}
 }
 
-void LLLFSThread::Request::finishRequest()
+LLLFSThread::Request::~Request()
 {
 }
 
+// virtual, called from own thread
+void LLLFSThread::Request::finishRequest(bool completed)
+{
+	if (mResponder.notNull())
+	{
+		mResponder->completed(completed ? mBytesRead : 0);
+		mResponder = NULL;
+	}
+}
+
 void LLLFSThread::Request::deleteRequest()
 {
-	if (getStatus() == STATUS_QUEUED || getStatus() == STATUS_ABORT)
+	if (getStatus() == STATUS_QUEUED)
 	{
 		llerrs << "Attempt to delete a queued LLLFSThread::Request!" << llendl;
 	}	
-	if (mOperation == FILE_WRITE)
-	{
-		if (mFlags & AUTO_DELETE)
-		{
-			delete mBuffer;
-		}
-	}
-	else if (mOperation == FILE_RENAME)
+	if (mResponder.notNull())
 	{
-		LLString* new_name = (LLString*)mBuffer;
-		delete new_name;
+		mResponder->completed(0);
+		mResponder = NULL;
 	}
 	LLQueuedThread::QueuedRequest::deleteRequest();
 }
 
-bool LLLFSThread::Request::processIO()
+bool LLLFSThread::Request::processRequest()
 {
 	bool complete = false;
 	if (mOperation ==  FILE_READ)
 	{
 		llassert(mOffset >= 0);
-		apr_file_t* filep = ll_apr_file_open(mFileName, LL_APR_RB);
+		apr_file_t* filep = ll_apr_file_open(mFileName, LL_APR_RB, mThread->mAPRPoolp);
 		if (!filep)
 		{
 			llwarns << "LLLFS: Unable to read file: " << mFileName << llendl;
 			mBytesRead = 0; // fail
 			return true;
 		}
+		S32 off;
 		if (mOffset < 0)
-			ll_apr_file_seek(filep, APR_END, 0);
+			off = ll_apr_file_seek(filep, APR_END, 0);
 		else
-			ll_apr_file_seek(filep, APR_SET, mOffset);
+			off = ll_apr_file_seek(filep, APR_SET, mOffset);
+		llassert_always(off >= 0);
 		mBytesRead = ll_apr_file_read(filep, mBuffer, mBytes );
 		apr_file_close(filep);
 		complete = true;
-		//llinfos << llformat("LLLFSThread::READ '%s': %d bytes",mFileName.c_str(),mBytesRead) << llendl;
+// 		llinfos << "LLLFSThread::READ:" << mFileName << " Bytes: " << mBytesRead << llendl;
 	}
 	else if (mOperation ==  FILE_WRITE)
 	{
-		apr_file_t* filep = ll_apr_file_open(mFileName, LL_APR_WB);
+		apr_int32_t flags = APR_CREATE|APR_WRITE|APR_BINARY;
+		if (mOffset < 0)
+			flags |= APR_APPEND;
+		apr_file_t* filep = ll_apr_file_open(mFileName, flags, mThread->mAPRPoolp);
 		if (!filep)
 		{
 			llwarns << "LLLFS: Unable to write file: " << mFileName << llendl;
 			mBytesRead = 0; // fail
 			return true;
 		}
-		if (mOffset < 0)
-			ll_apr_file_seek(filep, APR_END, 0);
-		else
-			ll_apr_file_seek(filep, APR_SET, mOffset);
+		if (mOffset >= 0)
+		{
+			S32 seek = ll_apr_file_seek(filep, APR_SET, mOffset);
+			if (seek < 0)
+			{
+				apr_file_close(filep);
+				llwarns << "LLLFS: Unable to write file (seek failed): " << mFileName << llendl;
+				mBytesRead = 0; // fail
+				return true;
+			}
+		}
 		mBytesRead = ll_apr_file_write(filep, mBuffer, mBytes );
 		complete = true;
 		apr_file_close(filep);
-		//llinfos << llformat("LLLFSThread::WRITE '%s': %d bytes",mFileName.c_str(),mBytesRead) << llendl;
-	}
-	else if (mOperation ==  FILE_RENAME)
-	{
-		LLString* new_name = (LLString*)mBuffer;
-		ll_apr_file_rename(mFileName, *new_name);
-		complete = true;
-		//llinfos << llformat("LLLFSThread::RENAME '%s': '%s'",mFileName.c_str(),new_name->c_str()) << llendl;
-	}
-	else if (mOperation ==  FILE_REMOVE)
-	{
-		ll_apr_file_remove(mFileName);
-		complete = true;
-		//llinfos << llformat("LLLFSThread::REMOVE '%s'",mFileName.c_str()) << llendl;
+// 		llinfos << "LLLFSThread::WRITE:" << mFileName << " Bytes: " << mBytesRead << "/" << mBytes << " Offset:" << mOffset << llendl;
 	}
 	else
 	{
-		llerrs << llformat("LLLFSThread::unknown operation: %d", mOperation) << llendl;
+		llerrs << "LLLFSThread::unknown operation: " << (S32)mOperation << llendl;
 	}
 	return complete;
 }
 
 //============================================================================
+
+LLLFSThread::Responder::~Responder()
+{
+}
+
+//============================================================================
diff --git a/indra/llvfs/lllfsthread.h b/indra/llvfs/lllfsthread.h
index 37a6e8bae5c6aee4eea9fda91dce7b4d60bb668b..74fd0ffd4d65eb36703db2ca2c8a910b3e151689 100644
--- a/indra/llvfs/lllfsthread.h
+++ b/indra/llvfs/lllfsthread.h
@@ -36,15 +36,24 @@ public:
 	//------------------------------------------------------------------------
 public:
 
+	class Responder : public LLThreadSafeRefCount
+	{
+	public:
+		virtual ~Responder();
+		virtual void completed(S32 bytes) = 0;
+	};
+
 	class Request : public QueuedRequest
 	{
 	protected:
-		~Request() {}; // use deleteRequest()
+		virtual ~Request(); // use deleteRequest()
 		
 	public:
-		Request(handle_t handle, U32 priority, U32 flags,
+		Request(LLLFSThread* thread,
+				handle_t handle, U32 priority, 
 				operation_t op, const LLString& filename,
-				U8* buffer, S32 offset, S32 numbytes);
+				U8* buffer, S32 offset, S32 numbytes,
+				Responder* responder);
 
 		S32 getBytes()
 		{
@@ -67,12 +76,12 @@ public:
 			return mFileName;
 		}
 		
-		/*virtual*/ void finishRequest();
+		/*virtual*/ bool processRequest();
+		/*virtual*/ void finishRequest(bool completed);
 		/*virtual*/ void deleteRequest();
-
-		bool processIO();
 		
 	private:
+		LLLFSThread* mThread;
 		operation_t mOperation;
 		
 		LLString mFileName;
@@ -80,35 +89,36 @@ public:
 		U8* mBuffer;	// dest for reads, source for writes, new UUID for rename
 		S32 mOffset;	// offset into file, -1 = append (WRITE only)
 		S32 mBytes;		// bytes to read from file, -1 = all
-		S32	mBytesRead;	// bytes read from file
+		S32 mBytesRead;	// bytes read from file
+
+		LLPointer<Responder> mResponder;
 	};
 
 	//------------------------------------------------------------------------
 public:
-	LLLFSThread(bool threaded = TRUE, bool runalways = TRUE);
+	LLLFSThread(bool threaded = TRUE);
 	~LLLFSThread();	
 
 	// Return a Request handle
 	handle_t read(const LLString& filename,	/* Flawfinder: ignore */ 
-				  U8* buffer, S32 offset, S32 numbytes, U32 pri=PRIORITY_NORMAL, U32 flags = 0);
+				  U8* buffer, S32 offset, S32 numbytes,
+				  Responder* responder, U32 pri=0);
 	handle_t write(const LLString& filename,
-				   U8* buffer, S32 offset, S32 numbytes, U32 flags = 0);
-	handle_t rename(const LLString& filename, const LLString& newname, U32 flags = 0);
-	handle_t remove(const LLString& filename, U32 flags = 0);
+				   U8* buffer, S32 offset, S32 numbytes,
+				   Responder* responder, U32 pri=0);
 	
-	// Return number of bytes read
-	S32 readImmediate(const LLString& filename,
-					  U8* buffer, S32 offset, S32 numbytes);
-	S32 writeImmediate(const LLString& filename,
-					   U8* buffer, S32 offset, S32 numbytes);
-
-	static void initClass(bool local_is_threaded = TRUE, bool run_always = TRUE); // Setup sLocal
+	// Misc
+	U32 priorityCounter() { return mPriorityCounter-- & PRIORITY_LOWBITS; } // Use to order IO operations
+	
+	// static initializers
+	static void initClass(bool local_is_threaded = TRUE); // Setup sLocal
 	static S32 updateClass(U32 ms_elapsed);
 	static void cleanupClass();		// Delete sLocal
 
-protected:
-	/*virtual*/ bool processRequest(QueuedRequest* req);
-
+	
+private:
+	U32 mPriorityCounter;
+	
 public:
 	static LLLFSThread* sLocal;		// Default local file thread
 };
diff --git a/indra/llvfs/llvfile.cpp b/indra/llvfs/llvfile.cpp
index a4612233d39c1c744ad84bd05489aa50678c8a25..ae45c8fe4214d756fabd468b4f4d09b2af49f39d 100644
--- a/indra/llvfs/llvfile.cpp
+++ b/indra/llvfs/llvfile.cpp
@@ -51,12 +51,12 @@ LLVFile::~LLVFile()
 		{
 			if (!(mMode & LLVFile::WRITE))
 			{
-				// 			llwarns << "Destroying LLVFile with pending async read/write, aborting..." << llendl;
-				sVFSThread->abortRequest(mHandle, LLVFSThread::AUTO_COMPLETE);
+				//llwarns << "Destroying LLVFile with pending async read/write, aborting..." << llendl;
+				sVFSThread->setFlags(mHandle, LLVFSThread::FLAG_AUTO_COMPLETE | LLVFSThread::FLAG_ABORT);
 			}
 			else // WRITE
 			{
-				sVFSThread->setFlags(mHandle, LLVFSThread::AUTO_COMPLETE);
+				sVFSThread->setFlags(mHandle, LLVFSThread::FLAG_AUTO_COMPLETE);
 			}
 		}
 	}
@@ -194,8 +194,8 @@ BOOL LLVFile::write(const U8 *buffer, S32 bytes)
 		S32 offset = -1;
 		mHandle = sVFSThread->write(mVFS, mFileID, mFileType,
 									writebuf, offset, bytes,
-									LLVFSThread::AUTO_COMPLETE | LLVFSThread::AUTO_DELETE);
-		mHandle = LLVFSThread::nullHandle(); // AUTO_COMPLETE means we don't track this
+									LLVFSThread::FLAG_AUTO_COMPLETE | LLVFSThread::FLAG_AUTO_DELETE);
+		mHandle = LLVFSThread::nullHandle(); // FLAG_AUTO_COMPLETE means we don't track this
 	}
 	else
 	{
@@ -304,7 +304,7 @@ BOOL LLVFile::setMaxSize(S32 size)
 			}
 			if (sVFSThread->isPaused())
 			{
-				sVFSThread->updateQueue(0);
+				sVFSThread->update(0);
 			}
 			ms_sleep(10);
 		}
@@ -408,7 +408,7 @@ void LLVFile::waitForLock(EVFSLock lock)
 	{
 		if (sVFSThread->isPaused())
 		{
-			sVFSThread->updateQueue(0);
+			sVFSThread->update(0);
 		}
 		ms_sleep(1);
 	}
diff --git a/indra/llvfs/llvfs.cpp b/indra/llvfs/llvfs.cpp
index 592f74dd025af686b03e5a34ae4eaa465c53144d..e4749041ee8b80294d38bc0b47607ae2dc4263a2 100644
--- a/indra/llvfs/llvfs.cpp
+++ b/indra/llvfs/llvfs.cpp
@@ -771,12 +771,17 @@ BOOL LLVFS::setMaxSize(const LLUUID &file_id, const LLAssetType::EType file_type
 	}
     
 	// round all sizes upward to KB increments
-	if (max_size & FILE_BLOCK_MASK)
+	// SJB: Need to not round for the new texture-pipeline code so we know the correct
+	//      max file size. Need to investigate the potential problems with this...
+	if (file_type != LLAssetType::AT_TEXTURE)
 	{
-		max_size += FILE_BLOCK_MASK;
-		max_size &= ~FILE_BLOCK_MASK;
-	}
-    
+		if (max_size & FILE_BLOCK_MASK)
+		{
+			max_size += FILE_BLOCK_MASK;
+			max_size &= ~FILE_BLOCK_MASK;
+		}
+    }
+	
 	if (block && block->mLength > 0)
 	{    
 		block->mAccessTime = (U32)time(NULL);
@@ -1998,7 +2003,7 @@ LLString get_extension(LLAssetType::EType type)
 	switch(type)
 	{
 	  case LLAssetType::AT_TEXTURE:
-		extension = ".jp2"; // ".j2c"; // IrfanView recognizes .jp2 -sjb
+		extension = ".j2c";
 		break;
 	  case LLAssetType::AT_SOUND:
 		extension = ".ogg";
diff --git a/indra/llvfs/llvfsthread.cpp b/indra/llvfs/llvfsthread.cpp
index 619c1b9bb39bb7d4310ddede15193f21f2d757b9..57cdb7626e9a566e6c7316dabd875d7569143ae0 100644
--- a/indra/llvfs/llvfsthread.cpp
+++ b/indra/llvfs/llvfsthread.cpp
@@ -20,10 +20,10 @@
 //============================================================================
 // Run on MAIN thread
 //static
-void LLVFSThread::initClass(bool local_is_threaded, bool local_run_always)
+void LLVFSThread::initClass(bool local_is_threaded)
 {
 	llassert(sLocal == NULL);
-	sLocal = new LLVFSThread(local_is_threaded, local_run_always);
+	sLocal = new LLVFSThread(local_is_threaded);
 }
 
 //static
@@ -47,8 +47,8 @@ void LLVFSThread::cleanupClass()
 
 //----------------------------------------------------------------------------
 
-LLVFSThread::LLVFSThread(bool threaded, bool runalways) :
-	LLQueuedThread("VFS", threaded, runalways)
+LLVFSThread::LLVFSThread(bool threaded) :
+	LLQueuedThread("VFS", threaded)
 {
 }
 
@@ -145,38 +145,26 @@ S32 LLVFSThread::writeImmediate(LLVFS* vfs, const LLUUID &file_id, const LLAsset
 }
 
 
-LLVFSThread::handle_t LLVFSThread::rename(LLVFS* vfs, const LLUUID &file_id, const LLAssetType::EType file_type,
-										  const LLUUID &new_id, const LLAssetType::EType new_type, U32 flags)
-{
-	handle_t handle = generateHandle();
+// LLVFSThread::handle_t LLVFSThread::rename(LLVFS* vfs, const LLUUID &file_id, const LLAssetType::EType file_type,
+// 										  const LLUUID &new_id, const LLAssetType::EType new_type, U32 flags)
+// {
+// 	handle_t handle = generateHandle();
 
-	LLUUID* new_idp = new LLUUID(new_id); // deleted with Request
-	// new_type is passed as "numbytes"
-	Request* req = new Request(handle, 0, flags, FILE_RENAME, vfs, file_id, file_type,
-							   (U8*)new_idp, 0, (S32)new_type);
+// 	LLUUID* new_idp = new LLUUID(new_id); // deleted with Request
+// 	// new_type is passed as "numbytes"
+// 	Request* req = new Request(handle, 0, flags, FILE_RENAME, vfs, file_id, file_type,
+// 							   (U8*)new_idp, 0, (S32)new_type);
 
-	bool res = addRequest(req);
-	if (!res)
-	{
-		llerrs << "LLVFSThread::read called after LLVFSThread::cleanupClass()" << llendl;
-		req->deleteRequest();
-		handle = nullHandle();
-	}
+// 	bool res = addRequest(req);
+// 	if (!res)
+// 	{
+// 		llerrs << "LLVFSThread::read called after LLVFSThread::cleanupClass()" << llendl;
+// 		req->deleteRequest();
+// 		handle = nullHandle();
+// 	}
 	
-	return handle;
-}
-
-//============================================================================
-// Runs on its OWN thread
-
-bool LLVFSThread::processRequest(QueuedRequest* qreq)
-{
-	Request *req = (Request*)qreq;
-
-	bool complete = req->processIO();
-
-	return complete;
-}
+// 	return handle;
+// }
 
 //============================================================================
 
@@ -223,7 +211,7 @@ LLVFSThread::Request::Request(handle_t handle, U32 priority, U32 flags,
 }
 
 // dec locks as soon as a request finishes
-void LLVFSThread::Request::finishRequest()
+void LLVFSThread::Request::finishRequest(bool completed)
 {
 	if (mOperation == FILE_WRITE)
 	{
@@ -241,13 +229,13 @@ void LLVFSThread::Request::finishRequest()
 
 void LLVFSThread::Request::deleteRequest()
 {
-	if (getStatus() == STATUS_QUEUED || getStatus() == STATUS_ABORT)
+	if (getStatus() == STATUS_QUEUED)
 	{
 		llerrs << "Attempt to delete a queued LLVFSThread::Request!" << llendl;
 	}	
 	if (mOperation == FILE_WRITE)
 	{
-		if (mFlags & AUTO_DELETE)
+		if (mFlags & FLAG_AUTO_DELETE)
 		{
 			delete [] mBuffer;
 		}
@@ -260,7 +248,7 @@ void LLVFSThread::Request::deleteRequest()
 	LLQueuedThread::QueuedRequest::deleteRequest();
 }
 
-bool LLVFSThread::Request::processIO()
+bool LLVFSThread::Request::processRequest()
 {
 	bool complete = false;
 	if (mOperation ==  FILE_READ)
@@ -283,7 +271,7 @@ bool LLVFSThread::Request::processIO()
 		mVFS->renameFile(mFileID, mFileType, *new_idp, new_type);
 		mFileID = *new_idp;
 		complete = true;
-		//llinfos << llformat("LLVFSThread::WRITE '%s': %d bytes arg:%d",getFilename(),mBytesRead) << llendl;
+		//llinfos << llformat("LLVFSThread::RENAME '%s': %d bytes arg:%d",getFilename(),mBytesRead) << llendl;
 	}
 	else
 	{
diff --git a/indra/llvfs/llvfsthread.h b/indra/llvfs/llvfsthread.h
index 68393388132e9e4822e452c7ce1269ae33b0bbdf..ea7c5123b009ca635b6e695f9d5c7fcf2d5bd309 100644
--- a/indra/llvfs/llvfsthread.h
+++ b/indra/llvfs/llvfsthread.h
@@ -69,10 +69,9 @@ public:
 			return std::string(tbuf);
 		}
 		
-		/*virtual*/ void finishRequest();
+		/*virtual*/ bool processRequest();
+		/*virtual*/ void finishRequest(bool completed);
 		/*virtual*/ void deleteRequest();
-
-		bool processIO();
 		
 	private:
 		operation_t mOperation;
@@ -90,10 +89,10 @@ public:
 	//------------------------------------------------------------------------
 public:
 	static std::string sDataPath;
-	static void setDataPath(const std::string& path) { sDataPath = path; }
+	static LLVFSThread* sLocal;		// Default worker thread
 	
 public:
-	LLVFSThread(bool threaded = TRUE, bool runalways = TRUE);
+	LLVFSThread(bool threaded = TRUE);
 	~LLVFSThread();	
 
 	// Return a Request handle
@@ -101,8 +100,9 @@ public:
 				  U8* buffer, S32 offset, S32 numbytes, U32 pri=PRIORITY_NORMAL, U32 flags = 0);
 	handle_t write(LLVFS* vfs, const LLUUID &file_id, const LLAssetType::EType file_type,
 				   U8* buffer, S32 offset, S32 numbytes, U32 flags);
-	handle_t rename(LLVFS* vfs, const LLUUID &file_id, const LLAssetType::EType file_type,
-					const LLUUID &new_id, const LLAssetType::EType new_type, U32 flags);
+	// SJB: rename seems to have issues, especially when threaded
+// 	handle_t rename(LLVFS* vfs, const LLUUID &file_id, const LLAssetType::EType file_type,
+// 					const LLUUID &new_id, const LLAssetType::EType new_type, U32 flags);
 	// Return number of bytes read
 	S32 readImmediate(LLVFS* vfs, const LLUUID &file_id, const LLAssetType::EType file_type,
 					  U8* buffer, S32 offset, S32 numbytes);
@@ -111,12 +111,11 @@ public:
 
 	/*virtual*/ bool processRequest(QueuedRequest* req);
 
-	static void initClass(bool local_is_threaded = TRUE, bool run_always = TRUE); // Setup sLocal
+public:
+	static void initClass(bool local_is_threaded = TRUE); // Setup sLocal
 	static S32 updateClass(U32 ms_elapsed);
 	static void cleanupClass();		// Delete sLocal
-
-public:
-	static LLVFSThread* sLocal;		// Default worker thread
+	static void setDataPath(const std::string& path) { sDataPath = path; }
 };
 
 //============================================================================
diff --git a/indra/llwindow/llwindow.h b/indra/llwindow/llwindow.h
index 442f4c9667d9e89b533f10998f135527f35a5953..bbe530c9069013d25083fd4bd30df4906313a140 100644
--- a/indra/llwindow/llwindow.h
+++ b/indra/llwindow/llwindow.h
@@ -298,6 +298,7 @@ public:
 //
 // helper funcs
 //
+extern BOOL gDebugWindowProc;
 
 // Protocols, like "http" and "https" we support in URLs
 extern const S32 gURLProtocolWhitelistCount;
diff --git a/indra/llwindow/llwindowmacosx.cpp b/indra/llwindow/llwindowmacosx.cpp
index a32013a5eedc87c52245349d09a5a9ade28f353f..c18e72b706d3af317b7f950f2c0e9509dfb4d843 100644
--- a/indra/llwindow/llwindowmacosx.cpp
+++ b/indra/llwindow/llwindowmacosx.cpp
@@ -11,6 +11,7 @@
 #include "linden_common.h"
 
 #include <Carbon/Carbon.h>
+#include <OpenGL/OpenGL.h>
 
 #include "llwindowmacosx.h"
 #include "llkeyboardmacosx.h"
@@ -719,6 +720,22 @@ BOOL LLWindowMacOSX::createContext(int x, int y, int width, int height, int bits
 	}
 	aglSetInteger(mContext, AGL_SWAP_INTERVAL, &frames_per_swap);  
 
+#if 0 // SJB: Got a compile error. Plus I don't want to test this along with everything else ; save it for later
+	//enable multi-threaded OpenGL
+	CGLError cgl_err;
+	CGLContextObj ctx = CGLGetCurrentContext();
+			
+	cgl_err =  CGLEnable( ctx, kCGLCEMPEngine);
+			
+	if (cgl_err != kCGLNoError )
+	{
+		 llinfos << "Multi-threaded OpenGL not available." << llendl;
+	}    
+	else
+	{
+		llinfos << "Multi-threaded OpenGL enabled." << llendl;
+	}
+#endif 		
 	// Don't need to get the current gamma, since there's a call that restores it to the system defaults.
 	return TRUE;
 }
diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp
index c79a39c513e257f2eec1056784202b3314e5222f..4efa173fc430088a6535487b77e6509793294dc5 100644
--- a/indra/llwindow/llwindowwin32.cpp
+++ b/indra/llwindow/llwindowwin32.cpp
@@ -55,61 +55,6 @@ void show_window_creation_error(const char* title)
 {
 	llwarns << title << llendl;
 	shell_open( "help/window_creation_error.html");
-	/*
-	OSMessageBox(
-	"Second Life is unable to run because it can't set up your display.\n"
-	"We need to be able to make a 32-bit color window at 1024x768, with\n"
-	"an 8 bit alpha channel.\n"
-	"\n"
-	"First, be sure your monitor is set to True Color (32-bit) in\n"
-	"Start -> Control Panels -> Display -> Settings.\n"
-	"\n"
-	"Otherwise, this may be due to video card driver issues.\n"
-	"Please make sure you have the latest video card drivers installed.\n"
-	"ATI drivers are available at http://www.ati.com/\n"
-	"nVidia drivers are available at http://www.nvidia.com/\n"
-	"\n"
-	"If you continue to receive this message, contact customer service.",
-	title,
-	OSMB_OK);
-	*/
-}
-
-BOOL check_for_card(const char* RENDERER, const char* bad_card)
-{
-	if(bad_card == NULL)
-	{
-		return FALSE;
-	}
-	if (!strnicmp(RENDERER, bad_card, strlen(bad_card)))	/* Flawfinder: ignore */
-	{
-		char buffer[1024];	/* Flawfinder: ignore */
-		snprintf(buffer, sizeof(buffer), /* Flawfinder: ignore */
-			"Your video card appears to be a %s, which Second Life does not support.\n"
-			"\n"
-			"Second Life requires a video card with 32 Mb of memory or more, as well as\n"
-			"multitexture support.  We explicitly support nVidia GeForce 2 or better, \n"
-			"and ATI Radeon 8500 or better.\n"
-			"\n"
-			"If you own a supported card and continue to receive this message, try \n"
-			"updating to the latest video card drivers. Otherwise look in the\n"
-			"secondlife.com support section or e-mail technical support\n"
-			"\n"
-			"You can try to run Second Life, but it will probably crash or run\n"
-			"very slowly.  Try anyway?",
-			bad_card);
-		S32 button = OSMessageBox(buffer, "Unsupported video card", OSMB_YESNO);
-		if (OSBTN_YES == button)
-		{
-			return FALSE;
-		}
-		else
-		{
-			return TRUE;
-		}
-	}
-
-	return FALSE;
 }
 
 //static
@@ -132,6 +77,7 @@ LLWindowWin32::LLWindowWin32(char *title, char *name, S32 x, S32 y, S32 width,
 							 BOOL ignore_pixel_depth)
 	: LLWindow(fullscreen, flags)
 {
+	S32 i = 0;
 	mIconResource = gIconResource;
 	mOverrideAspectRatio = 0.f;
 	mNativeAspectRatio = 0.f;
@@ -500,37 +446,6 @@ LLWindowWin32::LLWindowWin32(char *title, char *name, S32 x, S32 y, S32 width,
 			return;
 		}
 
-		// Check for some explicitly unsupported cards.
-		const char* RENDERER = (const char*) glGetString(GL_RENDERER);
-
-		const char* CARD_LIST[] = 
-		{	"RAGE 128",
-		"RIVA TNT2",
-		"Intel 810",
-		"3Dfx/Voodoo3",
-		"Radeon 7000",
-		"Radeon 7200",
-		"Radeon 7500",
-		"Radeon DDR",
-		"Radeon VE",
-		"GDI Generic" };
-		const S32 CARD_COUNT = sizeof(CARD_LIST)/sizeof(char*);
-
-		// Future candidates:
-		// ProSavage/Twister
-		// SuperSavage
-
-		S32 i;
-		for (i = 0; i < CARD_COUNT; i++)
-		{
-			if (check_for_card(RENDERER, CARD_LIST[i]))
-			{
-				close();
-				shell_open( "help/unsupported_card.html" );
-				return;
-			}
-		}
-
 		gGLManager.initWGL();
 
 		if (gGLManager.mHasWGLARBPixelFormat && (wglChoosePixelFormatARB != NULL))
diff --git a/indra/llwindow/llwindowwin32.h b/indra/llwindow/llwindowwin32.h
index 0b6fd5157a3613c91e4dca2396b7e9ae21e23721..ba944ca900065d43170ade275db5255e0412a932 100644
--- a/indra/llwindow/llwindowwin32.h
+++ b/indra/llwindow/llwindowwin32.h
@@ -182,6 +182,7 @@ private:
 };
 
 extern LLW32MsgCallback gAsyncMsgCallback;
+extern LPWSTR gIconResource;
 
 static void	handleMessage( const MSG& msg );
 
diff --git a/indra/llxml/llcontrol.cpp b/indra/llxml/llcontrol.cpp
index da6302b27c377fb6dfa558df578b65bf182a7c60..2af28f7d2c7661d8f11e9e22fe5fb1b70b4258c3 100644
--- a/indra/llxml/llcontrol.cpp
+++ b/indra/llxml/llcontrol.cpp
@@ -113,7 +113,8 @@ void LLControlGroup::cleanup()
 
 LLControlBase*	LLControlGroup::getControl(const LLString& name)
 {
-	return mNameTable[name];
+	ctrl_name_table_t::iterator iter = mNameTable.find(name);
+	return iter == mNameTable.end() ? NULL : (LLControlBase*)iter->second;
 }
 
 BOOL LLControlGroup::declareControl(const LLString& name, eControlType type, const LLSD initial_val, const LLString& comment, BOOL persist)
@@ -124,9 +125,11 @@ BOOL LLControlGroup::declareControl(const LLString& name, eControlType type, con
 		LLControl* control = new LLControl(name, type, initial_val, comment, persist);
 		mNameTable[name] = control;
 		return TRUE;
-	} else
+	}
+	else
 	{
 		llwarns << "LLControlGroup::declareControl: Control named " << name << " already exists." << llendl;
+		mNameTable.erase(name);
 		return FALSE;
 	}
 }
@@ -188,7 +191,7 @@ BOOL LLControlGroup::declareColor3(const LLString& name, const LLColor3 &initial
 
 LLSD LLControlGroup::registerListener(const LLString& name, LLSimpleListenerObservable *listener)
 {
-	LLControlBase *control = mNameTable[name];
+	LLControlBase *control = getControl(name);
 	if (control)
 	{
 		return control->registerListener(listener);
@@ -198,7 +201,7 @@ LLSD LLControlGroup::registerListener(const LLString& name, LLSimpleListenerObse
 
 BOOL LLControlGroup::getBOOL(const LLString& name)
 {
-	LLControlBase* control = mNameTable[name];
+	LLControlBase* control = getControl(name);
 	
 	if (control && control->isType(TYPE_BOOLEAN))
 		return control->get().asBoolean();
@@ -211,7 +214,7 @@ BOOL LLControlGroup::getBOOL(const LLString& name)
 
 S32 LLControlGroup::getS32(const LLString& name)
 {
-	LLControlBase* control = mNameTable[name];
+	LLControlBase* control = getControl(name);
 	
 	if (control && control->isType(TYPE_S32))
 		return control->get().asInteger();
@@ -224,7 +227,7 @@ S32 LLControlGroup::getS32(const LLString& name)
 
 U32 LLControlGroup::getU32(const LLString& name)
 {
-	LLControlBase* control = mNameTable[name];
+	LLControlBase* control = getControl(name);
 	
 	if (control && control->isType(TYPE_U32))		
 		return control->get().asInteger();
@@ -237,7 +240,7 @@ U32 LLControlGroup::getU32(const LLString& name)
 
 F32 LLControlGroup::getF32(const LLString& name)
 {
-	LLControlBase* control = mNameTable[name];
+	LLControlBase* control = getControl(name);
 	
 	if (control && control->isType(TYPE_F32))
 		return (F32) control->get().asReal();
@@ -250,7 +253,7 @@ F32 LLControlGroup::getF32(const LLString& name)
 
 LLString LLControlGroup::findString(const LLString& name)
 {
-	LLControlBase* control = mNameTable[name];
+	LLControlBase* control = getControl(name);
 	
 	if (control && control->isType(TYPE_STRING))
 		return control->get().asString();
@@ -259,7 +262,7 @@ LLString LLControlGroup::findString(const LLString& name)
 
 LLString LLControlGroup::getString(const LLString& name)
 {
-	LLControlBase* control = mNameTable[name];
+	LLControlBase* control = getControl(name);
 	
 	if (control && control->isType(TYPE_STRING))
 		return control->get().asString();
@@ -285,7 +288,7 @@ LLString LLControlGroup::getText(const LLString& name)
 
 LLVector3 LLControlGroup::getVector3(const LLString& name)
 {
-	LLControlBase* control = mNameTable[name];
+	LLControlBase* control = getControl(name);
 	
 	if (control && control->isType(TYPE_VEC3))
 		return control->get();
@@ -298,7 +301,7 @@ LLVector3 LLControlGroup::getVector3(const LLString& name)
 
 LLVector3d LLControlGroup::getVector3d(const LLString& name)
 {
-	LLControlBase* control = mNameTable[name];
+	LLControlBase* control = getControl(name);
 	
 	if (control && control->isType(TYPE_VEC3D))
 		return control->get();
@@ -311,7 +314,7 @@ LLVector3d LLControlGroup::getVector3d(const LLString& name)
 
 LLRect LLControlGroup::getRect(const LLString& name)
 {
-	LLControlBase* control = mNameTable[name];
+	LLControlBase* control = getControl(name);
 	
 	if (control && control->isType(TYPE_RECT))
 		return control->get();
@@ -357,7 +360,7 @@ LLColor4 LLControlGroup::getColor(const LLString& name)
 
 LLColor4U LLControlGroup::getColor4U(const LLString& name)
 {
-	LLControlBase* control = mNameTable[name];
+	LLControlBase* control = getControl(name);
 	
 	if (control && control->isType(TYPE_COL4U))
 		return control->get();
@@ -370,7 +373,7 @@ LLColor4U LLControlGroup::getColor4U(const LLString& name)
 
 LLColor4 LLControlGroup::getColor4(const LLString& name)
 {
-	LLControlBase* control = mNameTable[name];
+	LLControlBase* control = getControl(name);
 	
 	if (control && control->isType(TYPE_COL4))
 		return control->get();
@@ -383,7 +386,7 @@ LLColor4 LLControlGroup::getColor4(const LLString& name)
 
 LLColor3 LLControlGroup::getColor3(const LLString& name)
 {
-	LLControlBase* control = mNameTable[name];
+	LLControlBase* control = getControl(name);
 	
 	if (control && control->isType(TYPE_COL3))
 		return control->get();
@@ -396,9 +399,8 @@ LLColor3 LLControlGroup::getColor3(const LLString& name)
 
 BOOL LLControlGroup::controlExists(const LLString& name)
 {
-	void *control = mNameTable[name];
-
-	return (control != 0);
+	ctrl_name_table_t::iterator iter = mNameTable.find(name);
+	return iter != mNameTable.end();
 }
 
 //-------------------------------------------------------------------
@@ -407,7 +409,7 @@ BOOL LLControlGroup::controlExists(const LLString& name)
 
 void LLControlGroup::setBOOL(const LLString& name, BOOL val)
 {
-	LLControlBase* control = mNameTable[name];
+	LLControlBase* control = getControl(name);
 	
 	if (control && control->isType(TYPE_BOOLEAN))
 	{
@@ -422,7 +424,7 @@ void LLControlGroup::setBOOL(const LLString& name, BOOL val)
 
 void LLControlGroup::setS32(const LLString& name, S32 val)
 {
-	LLControlBase* control = mNameTable[name];
+	LLControlBase* control = getControl(name);
 	
 	if (control && control->isType(TYPE_S32))
 	{
@@ -437,7 +439,7 @@ void LLControlGroup::setS32(const LLString& name, S32 val)
 
 void LLControlGroup::setF32(const LLString& name, F32 val)
 {
-	LLControlBase* control = mNameTable[name];
+	LLControlBase* control = getControl(name);
 	
 	if (control && control->isType(TYPE_F32))
 	{
@@ -452,7 +454,7 @@ void LLControlGroup::setF32(const LLString& name, F32 val)
 
 void LLControlGroup::setU32(const LLString& name, U32 val)
 {
-	LLControlBase* control = mNameTable[name];
+	LLControlBase* control = getControl(name);
 	
 	if (control && control->isType(TYPE_U32))
 	{
@@ -467,7 +469,7 @@ void LLControlGroup::setU32(const LLString& name, U32 val)
 
 void LLControlGroup::setString(const LLString& name, const LLString &val)
 {
-	LLControlBase* control = mNameTable[name];
+	LLControlBase* control = getControl(name);
 	
 	if (control && control->isType(TYPE_STRING))
 	{
@@ -482,7 +484,7 @@ void LLControlGroup::setString(const LLString& name, const LLString &val)
 
 void LLControlGroup::setVector3(const LLString& name, const LLVector3 &val)
 {
-	LLControlBase* control = mNameTable[name];
+	LLControlBase* control = getControl(name);
 	
 	if (control && control->isType(TYPE_VEC3))
 	{
@@ -496,7 +498,7 @@ void LLControlGroup::setVector3(const LLString& name, const LLVector3 &val)
 
 void LLControlGroup::setVector3d(const LLString& name, const LLVector3d &val)
 {
-	LLControlBase* control = mNameTable[name];
+	LLControlBase* control = getControl(name);
 	
 	if (control && control->isType(TYPE_VEC3D))
 	{
@@ -510,7 +512,7 @@ void LLControlGroup::setVector3d(const LLString& name, const LLVector3d &val)
 
 void LLControlGroup::setRect(const LLString& name, const LLRect &val)
 {
-	LLControlBase* control = mNameTable[name];
+	LLControlBase* control = getControl(name);
 	
 	if (control && control->isType(TYPE_RECT))
 	{
@@ -524,7 +526,7 @@ void LLControlGroup::setRect(const LLString& name, const LLRect &val)
 
 void LLControlGroup::setColor4U(const LLString& name, const LLColor4U &val)
 {
-	LLControlBase* control = mNameTable[name];
+	LLControlBase* control = getControl(name);
 	
 	if (control && control->isType(TYPE_COL4U))
 	{
@@ -538,7 +540,7 @@ void LLControlGroup::setColor4U(const LLString& name, const LLColor4U &val)
 
 void LLControlGroup::setColor4(const LLString& name, const LLColor4 &val)
 {
-	LLControlBase* control = mNameTable[name];
+	LLControlBase* control = getControl(name);
 	
 	if (control && control->isType(TYPE_COL4))
 	{
@@ -557,7 +559,7 @@ void LLControlGroup::setValue(const LLString& name, const LLSD& val)
 		return;
 	}
 
-	LLControlBase* control = mNameTable[name];
+	LLControlBase* control = getControl(name);
 	
 	if (control)
 	{
@@ -839,7 +841,7 @@ U32 LLControlGroup::loadFromFile(const LLString& filename, BOOL require_declarat
 	{
 		name = child_nodep->getName();		
 		
-		BOOL declared = (mNameTable[name].notNull());
+		BOOL declared = controlExists(name);
 
 		if (require_declaration && !declared)
 		{
@@ -1026,8 +1028,7 @@ U32 LLControlGroup::saveToFile(const LLString& filename, BOOL nondefault_only)
 			break;
 		}
 
-		LLControlBase* control = (LLControlBase *)mNameTable[name];
-
+		LLControlBase* control = (LLControlBase *)iter->second;
 		if (!control)
 		{
 			llwarns << "Tried to save invalid control: " << name << llendl;
diff --git a/indra/llxml/llxmlnode.h b/indra/llxml/llxmlnode.h
index ca8c1d176f9c3c3bccd25cc74b08ba111531b394..ae16a9d800e97b2e7545b8be48e7dde6e69c4e8b 100644
--- a/indra/llxml/llxmlnode.h
+++ b/indra/llxml/llxmlnode.h
@@ -15,6 +15,7 @@
 
 #include "indra_constants.h"
 #include "llmemory.h"
+#include "llthread.h"
 #include "llstring.h"
 #include "llstringtable.h"
 
diff --git a/indra/newview/app_settings/shaders/class1/lighting/lightF.glsl b/indra/newview/app_settings/shaders/class1/lighting/lightF.glsl
index b2a6d67621a7d1b803045ae06700a1ea544c8186..c169fceb887cfb501a37fd0a4dc00a7617f82bf6 100644
--- a/indra/newview/app_settings/shaders/class1/lighting/lightF.glsl
+++ b/indra/newview/app_settings/shaders/class1/lighting/lightF.glsl
@@ -5,7 +5,7 @@ uniform sampler2D diffuseMap;
 void default_lighting() 
 {
 	vec4 color = gl_Color * texture2D(diffuseMap, gl_TexCoord[0].xy);
-	applyScatter(color.rgb);
+	//applyScatter(color.rgb);
 	gl_FragColor = color;
 }
 
diff --git a/indra/newview/app_settings/shaders/class1/objects/shinyF.glsl b/indra/newview/app_settings/shaders/class1/objects/shinyF.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..12c99a656732624271a1a6af512b81ac61531217
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/objects/shinyF.glsl
@@ -0,0 +1,13 @@
+void applyScatter(inout vec3 col);
+
+uniform samplerCube environmentMap;
+
+void main() 
+{
+	vec3 ref = textureCube(environmentMap, gl_TexCoord[0].xyz).rgb;
+			
+	applyScatter(ref);
+		
+	gl_FragColor.rgb = ref;
+	gl_FragColor.a = gl_Color.a;
+}
diff --git a/indra/newview/app_settings/shaders/class1/objects/shinyV.glsl b/indra/newview/app_settings/shaders/class1/objects/shinyV.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..621ff6b5b7b338a79f7b8f99148ec8d1c2429722
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/objects/shinyV.glsl
@@ -0,0 +1,27 @@
+void default_scatter(vec3 viewVec, vec3 lightDir);
+
+uniform vec4 origin;
+
+void main()
+{
+	//transform vertex
+	gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
+	
+	vec3 pos = (gl_ModelViewMatrix * gl_Vertex).xyz;
+	vec3 norm = normalize(gl_NormalMatrix * gl_Normal);
+	
+	gl_FrontColor = gl_Color;
+	
+	vec3 ref = reflect(pos, norm);
+	
+	vec3 d = pos - origin.xyz;
+	float dist = dot(normalize(d), ref);
+	vec3 e = d + (ref * max(origin.w-dist, 0.0));
+	
+	ref = e - origin.xyz;
+	
+	gl_TexCoord[0] = gl_TextureMatrix[0]*vec4(ref,1.0);
+	
+	default_scatter(pos.xyz, gl_LightSource[0].position.xyz);
+}
+
diff --git a/indra/newview/app_settings/shaders/class2/lighting/lightF.glsl b/indra/newview/app_settings/shaders/class2/lighting/lightF.glsl
index 0ef1129253bec73eb178ed44188bb18e7aa7a211..6f732ed731c760886395dff9f25aa726eda0fc09 100644
--- a/indra/newview/app_settings/shaders/class2/lighting/lightF.glsl
+++ b/indra/newview/app_settings/shaders/class2/lighting/lightF.glsl
@@ -5,7 +5,7 @@ uniform sampler2D diffuseMap;
 void default_lighting() 
 {
 	vec4 color = gl_Color * texture2D(diffuseMap, gl_TexCoord[0].xy);
-	applyScatter(color.rgb);
+	//applyScatter(color.rgb);
 	gl_FragColor = color;
 }
 
diff --git a/indra/newview/app_settings/viewerart.xml b/indra/newview/app_settings/viewerart.xml
index dbd22278a7f93fcea35ff7fbdb2ae6cde6c891f1..1ae2010002d5dd7415aee14bc8fd25df5fec4824 100644
--- a/indra/newview/app_settings/viewerart.xml
+++ b/indra/newview/app_settings/viewerart.xml
@@ -1,282 +1,4 @@
 <settings version = "101">
-	<closebox.tga value="47a8c844-cd2a-4b1a-be01-df8b1612fe5d"/>
-	<close_in_blue.tga value="e5821134-23c0-4bd0-af06-7fa95b9fb01a"/>
-	<tearoffbox.tga value="74e1a96f-4833-a24d-a1bb-1bce1468b0e7"/>
-	<tearoff_pressed.tga value="d2524c13-4ba6-af7c-e305-8ac6cc18d86a"/>
-	<resize_handle_bottom_right_blue.tga value="e3690e25-9690-4f6c-a745-e7dcd885285a"/>
-	<scrollbutton_up_out_blue.tga value="dad084d7-9a46-452a-b0ff-4b9f1cefdde9"/>
-	<scrollbutton_up_in_blue.tga value="a93abdf3-27b5-4e22-a8fa-c48216cd2e3a"/>
-	<scrollbutton_down_out_blue.tga value="b4ecdecf-5c8d-44e7-b882-17a77e88ed55"/>
-	<scrollbutton_down_in_blue.tga value="d2421bab-2eaf-4863-b8f6-5e4c52519247"/>
-	<scrollbutton_left_out_blue.tga value="43773e8d-49aa-48e0-80f3-a04715f4677a"/>
-	<scrollbutton_left_in_blue.tga value="ea137a32-6718-4d05-9c22-7d570d27b2cd"/>
-	<scrollbutton_right_out_blue.tga value="3d700d19-e708-465d-87f2-46c8c0ee7938"/>
-	<scrollbutton_right_in_blue.tga value="b749de64-e903-4c3c-ac0b-25fb6fa39cb5"/>
-	<spin_up_out_blue.tga value="56576e6e-6710-4e66-89f9-471b59122794"/>
-	<spin_up_in_blue.tga value="c8450082-96a0-4319-8090-d3ff900b4954"/>
-	<spin_down_out_blue.tga value="b6d240dd-5602-426f-b606-bbb49a30726d"/>
-	<spin_down_in_blue.tga value="a985ac71-052f-48e6-9c33-d931c813ac92"/>
-	<radio_active_false.tga value="7a1ba9b8-1047-4d1e-9cfc-bc478c80b63f"/>
-	<radio_active_true.tga value="52f09e07-5816-4052-953c-94c6c10479b7"/>
-	<radio_inactive_false.tga value="90688481-67ff-4af0-be69-4aa084bcad1e"/>
-	<radio_inactive_true.tga value="1975db39-aa29-4251-aea0-409ac09d414d"/>
-	<checkbox_enabled_false.tga value="05bb64ee-96fd-4243-b74e-f40a41bc53ba"/>
-	<checkbox_enabled_true.tga value="cf4a2ed7-1533-4686-9dde-df9a37ddca55"/>
-	<checkbox_disabled_false.tga value="7d94cb59-32a2-49bf-a516-9e5a2045f9d9"/>
-	<checkbox_disabled_true.tga value="c817c642-9abd-4236-9287-ae0513fe7d2b"/>
-	<tab_top_blue.tga value="1ed83f57-41cf-4052-a3b4-2e8bb78d8191"/>
-	<tab_top_selected_blue.tga value="16d032e8-817b-4368-8a4e-b7b947ae3889"/>
-	<tab_bottom_blue.tga value="bf0a8779-689b-48c3-bb9a-6af546366ef4"/>
-	<tab_bottom_selected_blue.tga value="c001d8fd-a869-4b6f-86a1-fdcb106df9c7"/>
-	<tab_left.tga value="1097dcb3-aef9-8152-f471-431d840ea89e"/>
-	<tab_left_selected.tga value="bea77041-5835-1661-f298-47e2d32b7a70"/>
-	<crosshairs.tga value="6e1a3980-bf2d-4274-8970-91e60d85fb52"/>
-	<move_backward_in.tga value="db11d956-5e7d-4aa5-b39d-7774d339fc5c"/>
-	<move_backward_out.tga value="3ae8bb18-ed97-4cd3-ae5c-d54bc8479fe7"/>
-	<move_down_in.tga value="b92a70b9-c841-4c94-b4b3-cee9eb460d48"/>
-	<move_down_out.tga value="b5abc9fa-9e62-4e03-bc33-82c4c1b6b689"/>
-	<move_forward_in.tga value="54197a61-f5d1-4c29-95d2-c071d08849cb"/>
-	<move_forward_out.tga value="a0eb4021-1b20-4a53-892d-8faa9265a6f5"/>
-	<move_left_in.tga value="724996f5-b956-46f6-9844-4fcfce1d5e83"/>
-	<move_left_out.tga value="82476321-0374-4c26-9567-521535ab4cd7"/>
-	<move_right_in.tga value="7eeb57d2-3f37-454d-a729-8b217b8be443"/>
-	<move_right_out.tga value="1fbe4e60-0607-44d1-a50a-032eff56ae75"/>
-	<move_turn_left_in.tga value="95463c78-aaa6-464d-892d-3a805b6bb7bf"/>
-	<move_turn_left_out.tga value="13a93910-6b44-45eb-ad3a-4d1324c59bac"/>
-	<move_turn_right_in.tga value="5e616d0d-4335-476f-9977-560bccd009da"/>
-	<move_turn_right_out.tga value="5a44fd04-f52b-4c30-8b00-4a31e27614bd"/>
-	<move_up_out.tga value="f887146d-829f-4e39-9211-cf872b78f97c"/>
-	<move_up_in.tga value="49b4b357-e430-4b56-b9e0-05b8759c3c82"/>
-	<cam_rotate_out.tga value="88745b46-da05-11d5-8ac0-0003477c4611"/>
-	<cam_rotate_in.tga value="70bf2262-3eed-4996-88ac-076907e8921d"/>
-	<cam_zoom_out.tga value="bb02e941-cb3b-4dd3-892a-6841b5de6e45"/>
-	<cam_zoom_plus_in.tga value="c7aefd32-ce13-4242-82cc-2631d44ff9d3"/>
-	<cam_zoom_minus_in.tga value="deed3f4b-93e9-4183-a3b0-a5a98a6de1bb"/>
-	<cam_tracking_out.tga value="95c4ea0e-e3c2-4904-b847-7d7676139ebb"/>
-	<cam_tracking_in.tga value="fe2fc73b-5a64-4a8e-aacc-46fa81faf96a"/>
-	<direction_arrow.tga value="586383e8-4d9b-4fba-9196-2b5938e79c2c"/>
-	<minimize.tga value="34c9398d-bb78-4643-9633-46a2fa3e9637"/>
-	<minimize_inactive.tga value="6e72abba-1378-437f-bf7a-f0c15f3e99a3"/>
-	<minimize_pressed.tga value="39801651-26cb-4926-af57-7af9352c273c"/>
-	<restore.tga value="111b39de-8928-4690-b7b2-e17d5c960277"/>
-	<restore_inactive.tga value="0eafa471-70af-4882-b8c1-40a310929744"/>
-	<restore_pressed.tga value="90a0ed5c-2e7b-4845-9958-a64a1b30f312"/>
-	<combobox_arrow.tga value="b31c1335-0e9c-4927-bc90-53277777d9c1"/>
-	<white.tga value="5748decc-f629-461c-9a36-a35a221fe21f"/>
-	<darkgray.tga value="267e26d3-e0e1-41b8-91b1-3b337102928d"/>
-	<lightgray.tga value="c520bf46-cc5d-412b-a60b-9f1bd245189f"/>
-	<eyes.tga value="6522e74d-1660-4e7f-b601-6f48c1659a77"/>
-	<hair.tga value="7ca39b4c-bd19-4699-aff7-f93fd03d3e7b"/>
-	<black.tga value="e2244626-f22f-4839-8123-1e7baddeb659"/>
-	<close_inactive_blue.tga value="779e4fa3-9b13-f74a-fba9-3886fe9c86ba"/>
-	<button_disabled_32x128.tga value="f8124d60-2875-c358-7847-2acb63e5400c"/>
-	<button_enabled_32x128.tga value="d8faf8cb-ee6e-b0b5-abd9-bde873ad3461"/>
-	<button_enabled_selected_32x128.tga value="1eddba75-b682-110a-104e-6cdcce616a25"/>
-	<button_anim_play_selected.tga value="119c37bb-24af-45fe-ae11-3a6bc3c85138"/>
-	<button_anim_pause_selected.tga value="ad65d67a-777b-fbfa-693d-4bdcfca2acca"/>
-	<button_anim_pause.tga value="db2d9c2d-0bbd-21e2-e83a-103ea2def7a8"/>
-	<button_anim_play.tga value="2a7f6738-5d82-2ff3-d419-30ed09cbb72b"/>
-	<button_anim_stop.tga value="e10c9e36-d9f6-c8b4-de96-557dccce9205"/>
-	<button_anim_stop_selected.tga value="b8c0e0aa-2771-439e-c919-d2f5dad69a1c"/>
-	<rounded_square.tga value="38ce8b3c-fb30-5c59-9926-bd643613f606"/>
-	<rounded_square_soft.tga value="4c95e6bc-fe77-9cb4-b58a-909848042c1e"/>
-	<badge_ok.tga value="211035a7-c313-378d-478c-e80bbd0fde63"/>
-	<badge_note.tga value="13f6e639-b3f9-28da-a1e6-e990a43052b6"/>
-	<badge_warn.tga value="0992d4bc-7af8-4a1f-f2e6-e6c4083b066e"/>
-	<badge_error.tga value="00c50485-8491-ab70-2ea8-43f26fd028e2"/>
-	<status_money.tga value="5863eb7a-1546-6501-533a-6061f73a36b7"/>
-	<status_health.tga value="4330e8ce-b39b-1eb8-c2ec-a97c0b3947b5"/>
-	<status_fly.tga value="0e058115-5b8f-c3d7-dcaa-9623d92885d1"/>
-	<status_build.tga value="175a6b75-45c9-c2c2-4765-bf37a3909b53"/>
-	<status_busy.tga value="beb0d821-6725-abdf-032d-1f70cdabde82"/>
-	<status_scripts.tga value="4cc1afcd-04dd-178f-e074-0f9dc730ab45"/>
-	<status_buy_currency.tga value="f43a535a-59ac-26e3-84bc-c786735fabe4"/>
-	<status_buy_currency_pressed.tga value="bfa5be70-37c7-8126-fecd-df55390954d5"/>
-	<status_buy_land.tga value="1a0edac5-3e50-fc9b-2752-70c1f69cb959"/>
-	<status_buy_land_pressed.tga value="257647b7-199f-99ff-8be9-f6753289a3aa"/>
-	<terrain_dirt.tga value="b8d3965a-ad78-bf43-699b-bff8eca6c975"/>
-	<terrain_grass.tga value="abb783e6-3e93-26c0-248a-247666855da3"/>
-	<terrain_mountain.tga value="179cdabd-398a-9b6b-1391-4dc333ba321f"/>
-	<terrain_rock.tga value="beb169c7-11ea-fff2-efe5-0f24dc881df2"/>
-	<terrain_dirt_detail.tga value="0bc58228-74a0-7e83-89bc-5c23464bcec5"/>
-	<terrain_grass_detail.tga value="63338ede-0037-c4fd-855b-015d77112fc8"/>
-	<terrain_mountain_detail.tga value="303cd381-8560-7579-23f1-f0a880799740"/>
-	<terrain_rock_detail.tga value="53a2f406-4895-1d13-d541-d2e3b86bc19c"/>
-	<square_btn_32x128.tga value="b28df901-6b8d-d31c-7903-4eb9676d4bfc"/>
-	<square_btn_selected_32x128.tga value="c48c9e95-191b-96d3-08b2-6e8ada58b651"/>
-	<tree_pine_1.tga value="0187babf-6c0d-5891-ebed-4ecab1426683"/>
-	<tree_oak.tga value="8a515889-eac9-fb55-8eba-d2dc09eb32c8"/>
-	<tree_tropical_1.tga value="5bc11cd6-2f40-071e-a8da-0903394204f9"/>
-	<tree_palm_1.tga value="ca4e8c27-473c-eb1c-2f5d-50ee3f07d85c"/>
-	<tree_dogwood.tga value="64367bd1-697e-b3e6-0b65-3f862a577366"/>
-	<tree_tropical_2.tga value="cdd9a9fc-6d0b-f90d-8416-c72b6019bca8"/>
-	<tree_palm_2.tga value="2d784476-d0db-9979-0cff-9408745a7cf3"/>
-	<tree_cypress_1.tga value="fb2ae204-3fd1-df33-594f-c9f882830e66"/>
-	<tree_cypress_2.tga value="30047cec-269d-408e-0c30-b2603b887268"/>
-	<tree_pine_2.tga value="d691a01c-13b7-578d-57c0-5caef0b4e7e1"/>
-	<tree_plumeria.tga value="6de37e4e-7029-61f5-54b8-f5e63f983f58"/>
-	<winter_tree_aspen.tga value="7c0cf89b-44b1-1ce2-dd74-07102a98ac2a"/>
-	<winter_tree_pine_1.tga value="10d2a01a-0818-84b9-4b96-c2eb63256519"/>
-	<winter_tree_pine_2.tga value="67931331-0c02-4876-1255-28770896c6a2"/>
-	<tree_eucalyptus.tga value="a6162133-724b-54df-a12f-51cd070ad6f3"/>
-	<tree_fern.tga value="8872f2b8-31db-42d8-580a-b3e4a91262de"/>
-	<tree_eelgrass.tga value="96b4de31-f4fa-337d-ec78-451e3609769e"/>
-	<tree_sea_sword.tga value="5894e2e7-ab8d-edfa-e61c-18cf16854ba3"/>
-	<tree_kelp_1.tga value="2caf1179-7861-6ff3-4b7d-46e17780bdfa"/>
-	<tree_kelp_2.tga value="2a4880b6-b7a3-690a-2049-bfbe38eafb9f"/>
-	<tree_beach_grass_1.tga value="18fb888b-e8f1-dce7-7da7-321d651ea6b0"/>
-	<tool_dozer.tga value="d2a0d4d4-54eb-4d16-be4b-4eae43845c74"/>
-	<tool_dozer_active.tga value="d4afdbbe-1550-4b7d-91de-95731f47e8e3"/>
-	<tool_land.tga value="86fe4df4-0ecb-4382-b9ae-475925a92388"/>
-	<tool_land_active.tga value="34e60587-0791-4a07-8918-f5995fcc22a3"/>
-	<tool_zoom.tga value="27eb8829-fe65-45ed-a49a-73aac42f4b38"/>
-	<tool_zoom_active.tga value="69445f58-5c8e-44e0-9d2e-47408bb43b39"/>
-	<tool_orbit.tga value="06964fe4-033f-448a-95c9-30dc41d1be8b"/>
-	<tool_orbit_active.tga value="ee4e07db-3f72-4098-bd4c-aef34515a7bc"/>
-	<tool_pan.tga value="a32aa302-0a15-48d2-b2b1-4d69f1161173"/>
-	<tool_pan_active.tga value="24d9ad33-0b42-4eb5-99a3-659d838bc5c0"/>
-	<inv_folder_texture.tga value="743f035b-a049-43f4-16c7-7ec8daa2c481"/>
-	<inv_folder_sound.tga value="e10cb910-1e71-da47-bd12-8c53f7793714"/>
-	<inv_folder_callingcard.tga value="a3735971-e2b2-d78a-580d-d265cd8f2484"/>
-	<inv_folder_landmark.tga value="9f921155-7c8c-e276-d5ec-03ac9340584d"/>
-	<inv_folder_script.tga value="baa5c310-6a6d-cc48-51eb-65196ba31d77"/>
-	<inv_folder_object.tga value="113e5133-fd0d-ee51-4a59-9d67ca10e8a7"/>
-	<inv_folder_notecard.tga value="a9e75d84-5073-9cb7-10a9-1ca68ef5c7ba"/>
-	<inv_folder_clothing.tga value="f1427d3d-b2e8-97c4-69ab-1f36d4c0e8f0"/>
-	<inv_folder_bodypart.tga value="1fe05580-1d2f-0345-b28b-52b6e3a20e5d"/>
-	<inv_folder_trash.tga value="88ad072e-ea0b-aabd-5ac0-b37862a6eb66"/>
-	<inv_folder_plain_closed.tga value="86f00960-c3e9-9680-145d-3beffd743e9c"/>
-	<inv_folder_plain_open.tga value="d15dc243-2d0b-47af-0ce1-ec376464bdc8"/>
-	<inv_folder_snapshot.tga value="6efe85e7-800f-1843-296c-a5b7adffe091"/>
-	<inv_folder_lostandfound.tga value="9a371a04-297d-bacf-0d16-5f49753efe1d"/>
-	<inv_folder_animation.tga value="4d59b3ee-f29d-b912-2bcc-9bb1f8a07ec6"/>
-	<inv_folder_gesture.tga value="4de9129a-9fc1-d759-d739-364293906ba2"/>
-	<inv_item_texture.tga value="19f452d7-4eee-9f46-76cc-5497d17f1dd9"/>
-	<inv_item_sound.tga value="eb414d69-c77d-d4e7-66e6-6c2e6f6c1976"/>
-	<inv_item_callingcard_online.tga value="672cc53e-8dc0-ba91-2a4e-574104cf071c"/>
-	<inv_item_callingcard_offline.tga value="d0afe86b-2489-7600-55b7-6abb0a63d9f9"/>
-	<inv_item_landmark.tga value="bf25a2a0-85da-7fa0-0993-e461768d0221"/>
-	<inv_item_landmark_visited.tga value="229fac85-5428-4ab7-adeb-eb8389e91092"/>
-	<inv_item_script.tga value="59a3df81-ed76-06c9-7264-6dada535e7a3"/>
-	<inv_item_clothing.tga value="34dfe476-8e26-0e3a-11cf-76cc4a7126ce"/>
-	<inv_item_object.tga value="0f0780a0-89c4-742a-ef28-26405a41cf85"/>
-	<inv_item_notecard.tga value="23ce8a2c-9ea2-d863-6572-806f0645b0c7"/>
-	<inv_item_bodypart.tga value="d2a5362d-5c55-57dd-a9e9-5c814d1ddc16"/>
-	<inv_item_attach.tga value="5bcae41e-aa5d-02f8-edf1-605ebdd875ab"/>
-	<inv_item_snapshot.tga value="3810d584-b092-7caa-57e0-010f192b9659"/>
-	<inv_item_eyes.tga value="eaa5fd96-5c25-06ef-2280-7ef20203e167"/>
-	<inv_item_gloves.tga value="117b11cb-c04e-5081-13da-1a8846070fd0"/>
-	<inv_item_hair.tga value="6bca3bf4-ed6d-d438-63a0-2a7066d03a0b"/>
-	<inv_item_jacket.tga value="8df59386-56e0-c811-0443-840da3acb3a5"/>
-	<inv_item_pants.tga value="a87a58ca-f857-63b1-0acf-072711ed1bdb"/>
-	<inv_item_shape.tga value="4463e433-4db5-79ef-c1b0-4821b03ddb07"/>
-	<inv_item_shirt.tga value="e2ffb62b-6abc-22d6-952d-764759b4d636"/>
-	<inv_item_shoes.tga value="cf384fa5-1edd-c37c-2134-283dd4fe3396"/>
-	<inv_item_skirt.tga value="0b43f826-2abc-2944-7d72-10777a51d19b"/>
-	<inv_item_socks.tga value="22137c6d-6ec5-6eee-9a2e-2d7a9e6cbcd4"/>
-	<inv_item_underpants.tga value="2f15dc09-4385-526c-aa5d-d9d516ec7d99"/>
-	<inv_item_undershirt.tga value="f72ab629-a3ab-de0c-35c0-5285e27478ce"/>
-	<inv_item_animation.tga value="b5cda0d6-d196-ce48-63db-d04323ef8931"/>
-	<inv_item_gesture.tga value="5579245d-d5bf-5f13-46b0-8624490de24c"/>
-	<pixiesmall.tga value="168e6813-096e-07ea-97ae-fd416826f627"/>
-	<legend.tga value="ca7609c6-6ec6-32d9-332e-0d8f437ef644"/>
-	<propertyline.tga value="e3548c46-8d5e-03da-fcab-4fc36ad818bb"/>
-	<startup_logo.tga value="66864f3c-e095-d9c8-058d-d6575e6ed1b8"/>
-	<grass_texture_1.tga value="79504bf5-c3ec-0763-6563-d843de66d0a1"/>
-	<grass_texture_2.tga value="6c4727b8-ac79-ba44-3b81-f9aa887b47eb"/>
-	<grass_texture_3.tga value="99bd60a2-3250-efc9-2e39-2fbcadefbecc"/>
-	<grass_texture_4.tga value="7a2b3a4a-53c2-53ac-5716-aac7d743c020"/>
-	<undergrowth_1.tga value="8f458549-173b-23ff-d4ff-bfaa5ea2371b"/>
-	<silhouette.tga value="da5d4079-7819-6b53-d2a4-dc9929381d7d"/>
-	<avatar_thumb_bkgrnd.tga value="3a7f4f0d-be14-ee78-29e3-fc8b0b2a68d3"/>
-	<missing_asset.tga value="32dfd1c8-7ff6-5909-d983-6d4adfb4255d"/>
-	<alpha_gradient.tga value="e97cf410-8e61-7005-ec06-629eba4cd1fb"/>
-	<alpha_gradient_2d.tga value="38b86f85-2575-52a9-a531-23108d8da837"/>
-	<alpha_noise.tga value="b9e1cf8a-9660-c020-0c69-18f1ea27268a"/>
-	<alpha_sizzle.tga value="e121e2fc-7573-740f-edfd-0d45a9ba486e"/>
-	<bump_woodgrain.tga value="058c75c0-a0d5-f2f8-43f3-e9699a89c2fc"/>
-	<bump_bark.tga value="6c9fa78a-1c69-2168-325b-3e03ffa348ce"/>
-	<bump_bricks.tga value="b8eed5f0-64b7-6e12-b67f-43fa8e773440"/>
-	<bump_checker.tga value="9deab416-9c63-78d6-d558-9a156f12044c"/>
-	<bump_concrete.tga value="db9d39ec-a896-c287-1ced-64566217021e"/>
-	<bump_crustytile.tga value="f2d7b6f6-4200-1e9a-fd5b-96459e950f94"/>
-	<bump_cutstone.tga value="d9258671-868f-7511-c321-7baef9e948a4"/>
-	<bump_discs.tga value="d21e44ca-ff1c-a96e-b2ef-c0753426b7d9"/>
-	<bump_gravel.tga value="4726f13e-bd07-f2fb-feb0-bfa2ac58ab61"/>
-	<bump_petridish.tga value="e569711a-27c2-aad4-9246-0c910239a179"/>
-	<bump_siding.tga value="073c9723-540c-5449-cdd4-0e87fdc159e3"/>
-	<bump_stonetile.tga value="ae874d1a-93ef-54fb-5fd3-eb0cb156afc0"/>
-	<bump_stucco.tga value="92e66e00-f56f-598a-7997-048aa64cde18"/>
-	<bump_suction.tga value="83b77fc6-10b4-63ec-4de7-f40629f238c5"/>
-	<bump_weave.tga value="735198cf-6ea0-2550-e222-21d3c6a341ae"/>
-	<icon_avatar_online.tga value="529ed15b-3d41-dcc1-79de-90bf21770b5b"/>
-	<icon_avatar_offline.tga value="34648c67-5bfb-5790-e05e-8bd6600fd087"/>
-	<icon_event.tga value="be235ae0-53cf-1d68-b3ae-cf375ed1fb58"/>
-	<icon_event_mature.tga value="cc090999-1b3e-2e97-7a38-c9f4afd10297"/>
-	<icon_group.tga value="04237108-a879-5a95-9b0c-b18fd09bc447"/>
-	<icon_place.tga value="ba0bac4e-815e-14e1-2895-5065b8c703b3"/>
-	<icon_top_pick.tga value="77ca91a2-4431-aeaf-6249-3dd99c7dd86d"/>
-	<icon_popular.tga value="bdd47da5-5b5b-c906-37ad-16aaa64f096f"/>
-	<icon_for_sale.tga value="f20728fd-1670-3771-2293-e0dd3f0bcaab"/>
-	<icon_auction.tga value="96abf5b1-335c-6b76-61e3-74ada07f3cb8"/>
-	<icon_land_for_landless.tga value="c421ddf2-b9d7-b373-503c-f4c423f37f1c"/>
-	<icon_day_cycle.tga value="5b30a285-f1e3-92b1-dcd3-0d07366ced3e"/>
-	<icon_lock.tga value="9beb8cdd-3dce-53c2-b28e-e1f3bc2ec0a4"/>
-	<noentrylines.tga value="5d3e196b-fd4d-ada7-e4c1-99f8e9f1cfbf"/>
-	<noentrypasslines.tga value="ac8f8627-6a30-8da8-d4bd-958668eea7a0"/>
-	<notify_tip_icon.tga value="74ba3584-58ea-9984-5b76-62d37942ab77"/>
-	<notify_box_icon.tga value="b2ef2d31-9714-a07b-6ca7-31638166364b"/>
-	<notify_next.tga value="07d0ea4c-af0c-aad1-dbbf-c24020ff2b80"/>
-	<map_avatar_you_8.tga value="02fbdc40-5e07-a6e1-228b-58e10f8335b7"/>
-	<map_avatar_8.tga value="0be58a91-8065-c02b-7a12-2cc14dddbc37"/>
-	<map_avatar_16.tga value="db0dadd5-026a-88cf-f5c1-523a0a2daa3e"/>
-	<map_telehub.tga value="bf1b2bb0-13b1-40ae-3354-b1b93761bdb4"/>
-	<map_infohub.tga value="85b1a79a-7f6c-9df3-4d6c-17b1a4efb55a"/>
-	<map_home.tga value="ae9b8f5f-03a1-2e71-db77-6eb27a1ba181"/>
-	<map_event.tga value="6008be5e-9267-2a3a-9798-e81b076c22ca"/>
-	<map_event_mature.tga value="f9cdba28-a227-d613-2f16-ce06209314ae"/>
-	<map_track_8.tga value="bfdc7bf6-e2ee-1754-f4df-cc25887714ad"/>
-	<map_track_16.tga value="a3878395-ef00-a0e6-ee9a-f45ed6b9ce59"/>
-	<object_cone.tga value="c2b8c90a-7dca-26e3-1a63-7aa4a0389cf9"/>
-	<object_cone_active.tga value="cf69c64b-f19e-e1f3-a586-42fef31a23be"/>
-	<object_cube.tga value="70c747ac-1de3-a8b9-514d-101753ca6ccb"/>
-	<object_cube_active.tga value="f9c5e213-1076-7a7d-7889-52388aad2c1a"/>
-	<object_cylinder.tga value="13e35d95-5f6c-9a91-1766-49dedf9b1267"/>
-	<object_cylinder_active.tga value="3f3e4932-8412-e2a7-cfe9-92caf5978b1b"/>
-	<object_grass.tga value="7ca8e672-920b-4653-3970-1abc91abef58"/>
-	<object_grass_active.tga value="d0fc7cc9-646a-6860-cf7c-1d9e58cd6dab"/>
-	<object_hemi_cone.tga value="69d5e60c-739a-40b1-b526-84072121e394"/>
-	<object_hemi_cone_active.tga value="2e0c5435-95bb-1c0d-5da1-42336fb1cfc0"/>
-	<object_hemi_cylinder.tga value="f4be3e06-24a8-f86e-acc7-7daefc0572b7"/>
-	<object_hemi_cylinder_active.tga value="67279486-cfc1-3633-de42-85db65db373c"/>
-	<object_hemi_sphere.tga value="b67251ab-1716-b9fb-f911-967ba3fe027b"/>
-	<object_hemi_sphere_active.tga value="6c489466-3058-6475-6b1b-e5fc1d49f1f3"/>
-	<object_pyramid.tga value="9dde8b56-2cc4-a932-b63a-38c3a83221ad"/>
-	<object_pyramid_active.tga value="e7217b1a-e3d8-e339-d28a-d7714d0b5bee"/>
-	<object_sphere.tga value="7fa122c0-b994-460e-8636-cdc451d67268"/>
-	<object_sphere_active.tga value="f2c3bcbc-2904-41a5-1c22-688f176fd1ee"/>
-	<object_tetrahedron.tga value="e17db404-9fc5-9534-1038-777c82b2771f"/>
-	<object_tetrahedron_active.tga value="2792ea3b-c052-85fe-d168-a62b2f4e9d7c"/>
-	<object_tree.tga value="710d1bec-fb33-28f1-e77e-ddbb5b51f5ed"/>
-	<object_tree_active.tga value="da4835c7-b12a-41dd-11db-dae452f040c2"/>
-	<object_prism.tga value="02935f3a-dcda-3b42-1874-da89d4c12870"/>
-	<object_prism_active.tga value="223aac97-bd2f-ec2e-ad45-5641b77c78f9"/>
-	<object_torus.tga value="19e1f4c9-6aa6-4414-981d-59a1343a6472"/>
-	<object_torus_active.tga value="ef2bca77-5004-4547-b00a-3b96e463f89f"/>
-	<object_tube.tga value="7ce81316-a478-480f-961c-435fcbdecaf0"/>
-	<object_tube_active.tga value="55c3e4d1-cfdc-48a8-af32-a34844b91832"/>
-	<object_ring_active.tga value="2c955a73-fa31-237b-a4a1-5c8ede3bae50"/>
-	<object_ring.tga value="a7610e41-4647-16d8-0e0e-85a1211c1596"/>
-	<container_animation.tga value="c4e657a1-4c86-0159-2da0-32ff948484e6"/>
-	<container_bodypart.tga value="770cb2df-758d-34d5-36c7-e3de06db5b5d"/>
-	<container_clothing.tga value="dd90406f-4c8f-a3f9-41df-d562f94f09e0"/>
-	<container_gesture.tga value="59cd31c0-2791-3c48-f740-f0a36c68653e"/>
-	<container_landmark.tga value="24c63386-04f7-ce6f-4ff2-dfb215d2e21f"/>
-	<container_many_things.tga value="849d3292-d9fa-7186-5465-dd7b5fc1ec48"/>
-	<container_object.tga value="ad887ae1-2bee-f2c9-6786-5599de3c95c4"/>
-	<container_script.tga value="b93bd494-c4bd-bcdf-4a59-35a9497d03f3"/>
-	<container_sound.tga value="5ddea031-cfa3-2776-43e3-c7146c1b4cd6"/>
-	<container_texture.tga value="b3f95caf-bd62-bef3-0ded-dea752920629"/>
 	<avatar_aim_l_bow.bvh value="46bb4359-de38-4ed8-6a22-f1f52fe8f506"/>
 	<avatar_aim_r_bazooka.bvh value="b5b4a67d-0aee-30d2-72cd-77b333e932ef"/>
 	<avatar_aim_r_handgun.bvh value="3147d815-6338-b932-f011-16b56d9ac18b"/>
@@ -391,114 +113,4 @@
 	<avatar_yes_happy.bvh value="b8c8b2a3-9008-1771-3bfc-90924955ab2d"/>
 	<avatar_yes_head.bvh value="15dd911d-be82-2856-26db-27659b142875"/>
 	<avatar_yoga_float.bvh value="42ecd00b-9947-a97c-400a-bbc9174c7aeb"/>
-	<fringe.tga value="8ac54e9d-ec09-d804-60ab-47404a9b4a36"/>
-	<foot_shadow.tga value="14e8a47d-1055-0a68-5d55-eafd9ad3da5b"/>
-	<img_smoke_poof.tga value="c734da52-f2ba-f0ba-d59e-15ea49f3d5e9"/>
-	<img_shot.tga value="173b05c7-53a9-4cf8-ce6b-5eec21c5c63f"/>
-	<folder_arrow.tga value="09a324a8-acc1-d9cd-2cbd-7465d90d3a98"/>
-	<color_swatch_alpha.tga value="f13db22f-c55c-8bdf-7b1c-221e56fde253"/>
-	<script_error.tga value="e5a0ec29-f59e-d29e-2c59-ed66c187c26c"/>
-	<status_script_debug.tga value="7775b5cc-93a5-6efd-0d9b-4e079afac217"/>
-	<water_normal.tga value="822ded49-9a6c-f61c-cb89-6df54f42cdf4"/>
-	<icon_groupnotice.tga value="21579c81-a85e-f11c-2d80-33a4c007d88c"/>
-	<icon_groupnoticeinventory.tga value="8fcca699-08e7-3d58-2f05-86c9d52bbe82"/>
-	<tab_background_lightgrey.tga value="c769e547-c307-43ca-2b6a-51cad6d1c527"/>
-	<tab_background_purple.tga value="0ae8a2e9-aff4-249c-fc4a-0f41f89847dd"/>
-	<tab_background_darkpurple.tga value="38ff4f7e-3078-a749-8302-d6cc94b404c4"/>
-	<smicon_warn.tga value="f47c17a3-8bfb-3c9f-22b8-77923de7eed9"/>
-	<uv_test1.tga value="f43b75f5-9aa5-18ec-d5d9-e6d1b8442613"/>
-	<uv_test2.tga value="300ce95f-3d3f-7c1a-3a22-3fc48f873fb9"/>
-	<eye_button_active.tga value="2b42b375-f9b4-788e-46c7-7ef38762d0ba"/>
-	<eye_button_inactive.tga value="be1b7225-98b5-eb2a-2c86-ddaae3328a6e"/>
-	<account_id_blue.tga value="6ab9179a-7308-58db-6c9d-893d3b52bece"/>
-	<account_id_orange.tga value="fbe89371-1251-4e77-d2d8-8eeccffe3ca8"/>
-	<account_id_green.tga value="3bf64d5a-38d3-b752-cf52-3d9f8fca353a"/>
-	<status_push.tga value="07d1f523-e327-4d10-20d6-8bc22a6e8f56"/>
-	<ff_visible_online.tga value="d609a41f-34c0-7aae-b2c6-2fc3ab26d916"/>
-	<ff_visible_map.tga value="20b52706-c1ab-414a-9dea-1cb788ad5689"/>
-	<ff_edit_mine.tga value="1baee0b9-4b89-39eb-8815-866d82300ab5"/>
-	<ff_edit_theirs.tga value="32e981cd-4700-da5a-7fc7-d573ec3742f4"/>
-	<inv_item_script_dangerous.tga value="0b502db8-6fcd-c442-ecfe-483a0dce875e"/>
-	<ff_visible_map_button.tga value="c1079bef-5cf9-90f3-6dcd-48989851c252"/>
-	<ff_visible_online_button.tga value="36749b47-93d6-2c5e-7ebd-d38d30311163"/>
-	<ff_edit_theirs_button.tga value="ca229f65-d7e0-133e-1bc2-674abc33f3d5"/>
-	<ff_edit_mine_button.tga value="57f05b46-63d8-c3d5-66d6-8b915746b956"/>
-	<ff_online_status_button.tga value="3b1b6a53-9c8c-568a-22c5-2a8f3e5286f5"/>
-	<oi_hud_cen_0_0.tga value="3c650257-9caf-7cad-b26c-84c9eca560f1"/>
-	<oi_hud_intro.tga value="7611fb3d-9ff2-abd3-d98f-805c1c87e757"/>
-	<oi_hud_underwater.tga value="cde61aea-83c2-3001-d598-6b348f7a8e0b"/>
-	<oi_hud_got_passport.tga value="1271838d-d777-b811-7c4c-2a00308bd80a"/>
-	<oi_hud_texture_off_edge.tga value="852be205-b1ea-6356-58c8-8c5ee5a841a6"/>
-	<oi_hud_texture_on_edge.tga value="ab11e6ff-a732-be70-67df-c43131274562"/>
-	<oi_hud_flyingabovewater.tga value="c9d150d6-2739-5f8b-cce6-3cf98242920a"/>
-	<oi_hud_walkingabovewater.tga value="78284eeb-05f3-ff25-11a0-3cc9dbb30f0c"/>
-	<oi_hud_landmark.tga value="6cd9c221-9d42-a283-256b-09a113a87271"/>
-	<oi_hud_cus_5_3.tga value="7c12f4fb-f502-26d1-a2f3-cdb6aff61663"/>
-	<oi_hud_cus_5_2.tga value="c52c9c94-adc0-0f4e-6658-ed33d6ea8829"/>
-	<oi_hud_cus_5_1.tga value="9f6d5d11-6ca9-608c-e8a6-b77989350292"/>
-	<oi_hud_cus_5_0.tga value="2000cff1-119f-2023-66c0-ac5630d2f96e"/>
-	<oi_hud_cus_4_5.tga value="f302a935-ccd1-e2f5-3a38-e185cc262f3a"/>
-	<oi_hud_cus_4_3.tga value="af8d5b3c-b40f-cea5-b0b2-440fbd84a11a"/>
-	<oi_hud_cus_4_2.tga value="11b26901-8207-12bc-5224-10a12ac4c651"/>
-	<oi_hud_cus_4_1.tga value="41baadb7-1b94-907e-9443-54e92bba77cd"/>
-	<oi_hud_cus_4_0.tga value="9d627f8e-092c-5d32-6c12-ef76ab81cedc"/>
-	<oi_hud_cus_3_4.tga value="b196486e-d0d2-4fd7-529a-c84b4495fc74"/>
-	<oi_hud_cus_3_2.tga value="0b81c4bb-de33-e493-7bcb-e7221d97e5e7"/>
-	<oi_hud_cus_3_1.tga value="436dab74-25ae-8b60-c648-50663b7faa1d"/>
-	<oi_hud_cus_3_0.tga value="6c1594de-1e66-273c-a2ab-8f0ffa8b4633"/>
-	<oi_hud_cus_2_4.tga value="bb31fe48-8566-eec0-e96b-64025f832b63"/>
-	<oi_hud_cus_2_2.tga value="c946959a-26ae-eb66-efa0-20154057789d"/>
-	<oi_hud_cus_2_1.tga value="c946959a-26ae-eb66-efa0-20154057789d"/>
-	<oi_hud_cus_2_0.tga value="d7833106-b4a8-7666-bde1-64886de289f9"/>
-	<oi_hud_cus_1_0.tga value="811ded22-5940-940c-4821-6fbbfb6611d6"/>
-	<oi_hud_cus_1_1.tga value="eda8513b-a343-5109-1fd6-f1c7ad89b703"/>
-	<oi_hud_cus_1_2.tga value="7a4ce18c-e715-34d4-dfee-704c270a8ac8"/>
-	<oi_hud_cus_1_4.tga value="d3771c15-ac03-b762-b992-d9fd2fedf38a"/>
-	<oi_hud_com_4_4.tga value="d9e1e90d-3cc3-6269-128e-67f7a2b32d26"/>
-	<oi_hud_com_4_2.tga value="0f649a26-6fdb-c73b-ffac-e50fc311d5ce"/>
-	<oi_hud_com_4_1.tga value="ae5b1ce6-a2d2-22d2-f532-6280b3bc6adb"/>
-	<oi_hud_com_4_0.tga value="12cda3a0-58c7-dfa8-7f9b-380e5bb8baf9"/>
-	<oi_hud_com_3_4.tga value="ff326257-0530-356a-e0f8-be535044e540"/>
-	<oi_hud_com_3_2.tga value="66740ddb-1d56-89f9-f0c9-ae5eb7bb9537"/>
-	<oi_hud_com_3_1.tga value="55d662f4-6a28-6388-7c75-af1c9fd33055"/>
-	<oi_hud_com_3_0.tga value="de9d318f-b69e-82f9-0c61-43b868c5ca6b"/>
-	<oi_hud_com_2_4.tga value="01d47e68-400a-d0e1-afb7-d6806d1d477e"/>
-	<oi_hud_com_2_0.tga value="09c98850-27d4-6a12-abae-4af4bba23b6b"/>
-	<oi_hud_com_1_3.tga value="5c2049b9-f797-6608-ca71-758f3716aa90"/>
-	<oi_hud_com_1_1.tga value="1116ff68-cdc4-1cfc-e137-30f8426afeda"/>
-	<oi_hud_com_1_0.tga value="bd847d31-f5af-95f7-2b9c-af47d8ba53bd"/>
-	<oi_hud_nav_4_5.tga value="66194280-b087-db94-35d9-41e8f7518515"/>
-	<oi_hud_nav_4_4.tga value="180c4241-e309-4c05-13ee-9080ab69498d"/>
-	<oi_hud_nav_4_3.tga value="e98a6ba6-99c6-fa15-84b6-9afadea6c467"/>
-	<oi_hud_nav_4_2.tga value="2e19f352-1893-59a9-949b-4d2cfd3a8222"/>
-	<oi_hud_nav_4_1.tga value="13a1675b-fb5a-19b3-b5a3-74b0a6765f7d"/>
-	<oi_hud_nav_4_0.tga value="e7526e8d-b085-b26c-b0ae-2708ec231401"/>
-	<oi_hud_nav_3_5.tga value="5e67b0d0-29a2-6a08-c85e-b12d59e53d6e"/>
-	<oi_hud_nav_3_4.tga value="2ed8fbc2-5c4d-53c2-b289-88baffceab1a"/>
-	<oi_hud_nav_3_3.tga value="e0a72f1a-282e-1c1a-2cb7-6423feb41759"/>
-	<oi_hud_nav_3_2.tga value="4bcebb23-da5e-47d9-eac1-e4453f762c8c"/>
-	<oi_hud_nav_3_1.tga value="6ac87575-330e-3a2d-3b80-a34e7b277e50"/>
-	<oi_hud_nav_3_0.tga value="f1451e8e-7310-9152-47d5-5d037c28fef3"/>
-	<oi_hud_nav_2_6.tga value="c60b42ff-ee60-98e4-e603-ca2470141d4b"/>
-	<oi_hud_nav_2_5.tga value="a02b5a1a-bbdb-5556-ae5b-a2e68494755a"/>
-	<oi_hud_nav_2_4.tga value="625535ab-8abf-b3e7-48fb-43f728b77c79"/>
-	<oi_hud_nav_2_3.tga value="00a609c3-5750-3b5a-3ce3-458bdf632203"/>
-	<oi_hud_nav_2_2.tga value="94903387-d37f-092c-e4d2-c190f68577b8"/>
-	<oi_hud_nav_2_1.tga value="ee0cd82c-6ce8-8e73-307b-6d0dc77b19e8"/>
-	<oi_hud_nav_2_0.tga value="3e10b379-ed2c-7424-1fe7-bef3558c7536"/>
-	<oi_hud_nav_1_4.tga value="bf8d0be8-2012-1664-3ea5-e69a71c206e9"/>
-	<oi_hud_nav_1_2.tga value="72100f87-18a7-fc4a-4793-de281e8b02cc"/>
-	<oi_hud_nav_1_1.tga value="b048faf3-60ce-c3a2-d034-36613449d377"/>
-	<oi_hud_nav_1_0.tga value="0ad45106-3b26-6448-0b90-feae8bd46c38"/>
-	<oi_hud_mov_4_5.tga value="7c4a45c2-37dd-312c-c6ab-20896dd0a5a6"/>
-	<oi_hud_mov_4_3.tga value="8a88da1c-3735-c71e-d48a-016df0798de4"/>
-	<oi_hud_mov_4_2.tga value="f55ae4d3-7d6a-e6ac-4cf7-03014ce14390"/>
-	<oi_hud_mov_4_1.tga value="1cc3fcf1-35c0-e222-27d2-6905cf5c4cee"/>
-	<oi_hud_mov_4_0.tga value="1ae592dc-46f4-616e-b7c6-0dff3e6f40e5"/>
-	<oi_hud_mov_3_4.tga value="831b39be-99fc-45bd-ba85-708f9dc93bfd"/>
-	<oi_hud_mov_3_2.tga value="9f7e7373-92a9-d66a-ad5a-afb55ca6ac1f"/>
-	<oi_hud_mov_3_1.tga value="ab37ed0d-7e66-1f77-3acf-b0fe4b74dbe8"/>
-	<oi_hud_mov_3_0.tga value="f5ff1f08-4c92-8606-1854-cc5b9d3e445c"/>
-	<oi_hud_mov_1_2.tga value="1e3abeed-e893-c44e-1f9d-5ecc76d21e5d"/>
-	<oi_hud_mov_1_0.tga value="e300fc95-aa94-8e31-c501-ce903cac8b7c"/>
-</settings>
\ No newline at end of file
+</settings>
diff --git a/indra/newview/featuretable.txt b/indra/newview/featuretable.txt
index 3675fc3618256213efca10f0ebb6fd5caaa9db07..59e816d61bf9bc136278b3c942c254baebb6cb7b 100644
--- a/indra/newview/featuretable.txt
+++ b/indra/newview/featuretable.txt
@@ -1,4 +1,4 @@
-version 9
+version 10
 
 // NOTE: This is mostly identical to featuretable_mac.txt with a few differences
 // Should be combined into one table
@@ -23,7 +23,7 @@ version 9
 // NOTE: All settings are set to the MIN of applied values, including 'all'!
 //
 list all
-RenderAGP				1	1
+RenderVBO			1	1
 RenderAniso			1	0
 RenderAvatarMode	1	2
 RenderAvatarVP		1	1
@@ -40,6 +40,7 @@ VertexShaderEnable	1	1
 //
 list Class0
 VertexShaderEnable	1	0
+RenderVBO			1	1
 RenderDistance		1	64
 RenderAvatarVP		1	0
 RenderAvatarMode	1	0
@@ -52,6 +53,7 @@ RenderRippleWater	1	0
 //
 list Class1
 VertexShaderEnable	1	0
+RenderVBO			1	1
 RenderDistance		1	96
 RenderAvatarVP		1	1
 RenderAvatarMode	1	0
@@ -99,7 +101,7 @@ RenderAvatarVP		0  0
 // "Default" setups for safe, low, medium, high
 //
 list safe
-RenderAGP				1	0
+RenderVBO			1	0
 RenderAniso			1	0
 RenderAvatarVP		0	0
 RenderLighting		1	0
@@ -108,7 +110,7 @@ RenderTerrainDetail 1	0
 
 
 list low
-RenderAGP				1	1
+RenderVBO			1	0
 RenderAniso			1	0
 RenderLighting		1	0
 
@@ -135,12 +137,17 @@ RenderObjectBump	0	0
 //
 // Graphics card based feature masks
 //
-list Brookdale
+list OpenGLPre15
+RenderVBO			1	0
+
+list Intel
+RenderVBO			1	0
 RenderAniso			1	0
 RenderLighting		1	0
 RenderTerrainDetail	1	0
 
 list GeForce2
+RenderVBO			1	1
 RenderAniso			1	0
 RenderLighting		1	0
 RenderParticleCount	1	2048
@@ -150,10 +157,7 @@ list GeForce3
 
 list ATI
 
-// Hacked to be paranoid "safe"
-// Disable AGP entirely, in Catalyst 4.3 it's at least 50% slower
 list Radeon8500
-RenderAGP				0	0
 RenderLighting		1	0
 RenderParticleCount	1	4096
 
@@ -162,9 +166,8 @@ list Radeon9700
 RenderParticleCount	1	4096
 
 // Hacked to be paranoid "safe"
-// Disable AGP entirely, in Catalyst 4.3 it's at least 50% slower
 list MobilityRadeon9000
-RenderLighting			1	0
+RenderLighting		1	0
 RenderParticleCount	1	4096
 
 list GeForceFX
diff --git a/indra/newview/featuretable_mac.txt b/indra/newview/featuretable_mac.txt
index 25228fc9994eca1bcf591609f2bd77eed95f9763..568a260034893e1d20787a8b12bbf3136c239797 100644
--- a/indra/newview/featuretable_mac.txt
+++ b/indra/newview/featuretable_mac.txt
@@ -24,7 +24,7 @@ version 9
 //
 // Mac specific: RenderAvatarVP not enabled at all
 list all
-RenderAGP				1	1
+RenderVBO			1	1
 RenderAniso			1	0
 RenderAvatarMode	1	2
 RenderAvatarVP		1	0
@@ -99,7 +99,7 @@ RenderAvatarVP		0  0
 // "Default" setups for safe, low, medium, high
 //
 list safe
-RenderAGP				1	0
+RenderVBO			1	0
 RenderAniso			1	0
 RenderAvatarVP		0	0
 RenderDistance		1	64
@@ -109,7 +109,7 @@ RenderTerrainDetail 1	0
 
 
 list low
-RenderAGP				1	1
+RenderVBO			1	0
 RenderAniso			1	0
 RenderDistance		1	96
 RenderLighting		1	0
@@ -135,6 +135,12 @@ list RAM256MB
 RenderDistance		1	96
 RenderObjectBump	0	0
 
+//
+// Graphics card based feature masks
+//
+list OpenGLPre15
+RenderVBO			1	0
+
 // nVidia settings
 list NVIDIA
 
@@ -146,6 +152,6 @@ RenderParticleCount	1	2048
 RenderTerrainDetail	1	0
 
 //
-// ATI and AGP now work okay.
+// ATI settings
 //
 list ATI
diff --git a/indra/newview/gpu_table.txt b/indra/newview/gpu_table.txt
index fae089486e15b677f43f2d9b80eaeb831a4ea729..92f9b446f87936084d4912b9e94fd8823814064c 100644
--- a/indra/newview/gpu_table.txt
+++ b/indra/newview/gpu_table.txt
@@ -17,11 +17,14 @@
 3Dfx							.*3Dfx.*								0
 3Dlabs							.*3Dlabs.*								0
 ATI All-in-Wonder PCI-E			.*ATI.*All-in-Wonder.*PCI-E.*			1
+ATI All-in-Wonder X800			.*ATI.*All-in-Wonder X8.*				2
 ATI All-in-Wonder X1800			.*ATI.*All-in-Wonder X18.*				3
 ATI All-in-Wonder X1900			.*ATI.*All-in-Wonder X19.*				3
-ATI ASUS X1300					.*ATI.*ASUS X13.*						3
-ATI ASUS X1600					.*ATI.*ASUS X16.*						3
-ATI Diamond X1300				.*ATI.*Diamond X13.*					3
+ATI ASUS X1xxx					.*ASUS X1.*			 					3
+ATI Mobility Radeon X1xxx		.*ATI.*Mobility.*X1.*					2
+ATI Radeon OpenGL				.*ATI.*Radeon OpenGL.* 					3
+ATI Diamond X1xxx				.*ATI.*Diamond.*X1.*					3
+ATI FireGL 5xxx					.*ATI.*FireGL V5.*						3
 ATI FireGL						.*ATI.*Fire.*GL.*						0
 ATI FireMV						.*ATI.*FireMV.*							0
 ATI Generic						.*ATI.*Generic.*						0
@@ -35,14 +38,19 @@ ATI Radeon 9600					.*ATI.*Radeon 96.*						1
 ATI Radeon 9700					.*ATI.*Radeon 97.*						1
 ATI Radeon 9800					.*ATI.*Radeon 98.*						1
 ATI Radeon X1300				.*ATI.*Radeon X13.*						3
+ATI Radeon X1400				.*ATI.*Radeon X14.*						3
+ATI Radeon X1500				.*ATI.*Radeon X15.*						3
 ATI Radeon X1600				.*ATI.*Radeon X16.*						3
+ATI Radeon X1700				.*ATI.*Radeon X17.*						3
 ATI Radeon X1800				.*ATI.*Radeon X18.*						3
 ATI Radeon X1900				.*ATI.*Radeon X19.*						3
 ATI Radeon X300					.*ATI.*Radeon X3.*						2
+ATI Radeon X400					.*ATI.*Radeon X4.*						2
 ATI Radeon X500					.*ATI.*Radeon X5.*						2
 ATI Radeon X600					.*ATI.*Radeon X6.*						2
 ATI Radeon X700					.*ATI.*Radeon X7.*						2
 ATI Radeon X800					.*ATI.*Radeon X8.*						2
+ATI Radeon X900					.*ATI.*Radeon X9.*						2
 ATI Radeon Xpress				.*ATI.*Radeon Xpress.*					1
 ATI Rage 128					.*ATI.*Rage 128.*						0
 Intel 830M						.*Intel.*830M							0
@@ -91,7 +99,6 @@ NVIDIA GeForce FX Go5600		.*NVIDIA.*GeForce FX Go56.*				1
 NVIDIA GeForce FX Go5700		.*NVIDIA.*GeForce FX Go57.*				1
 NVIDIA GeForce FX Go5800		.*NVIDIA.*GeForce FX Go58.*				1
 NVIDIA GeForce FX Go5900		.*NVIDIA.*GeForce FX Go59.*				1
-NVIDIA GeForce Go 6				.*GeForce Go 6.*						2
 NVIDIA GeForce Go 6100			.*NVIDIA.*GeForce Go 61.*				2
 NVIDIA GeForce Go 6200			.*NVIDIA.*GeForce Go 62.*				2
 NVIDIA GeForce Go 6500			.*NVIDIA.*GeForce Go 65.*				2
@@ -103,6 +110,7 @@ NVIDIA GeForce Go 7400			.*NVIDIA.*GeForce Go 74.*				3
 NVIDIA GeForce Go 7600			.*NVIDIA.*GeForce Go 76.*				3
 NVIDIA GeForce Go 7800			.*NVIDIA.*GeForce Go 78.*				3
 NVIDIA GeForce Go 7900			.*NVIDIA.*GeForce Go 79.*				3
+NVIDIA GeForce Go 6				.*GeForce Go 6.*						2
 NVIDIA GeForce PCX				.*GeForce PCX.*							1
 NVIDIA Generic					.*NVIDIA.*NV.*							0
 NVIDIA Generic					.*NVIDIA.*Unknown.*						0
diff --git a/indra/newview/linux_tools/wrapper.sh b/indra/newview/linux_tools/wrapper.sh
index fd571cacfd30b39952f7bed1c0889fe496bc9f2c..3fd59eb87c9b305148858e7a8c78d5ff8b0962f2 100755
--- a/indra/newview/linux_tools/wrapper.sh
+++ b/indra/newview/linux_tools/wrapper.sh
@@ -13,8 +13,9 @@
 #export LL_BAD_ALSA=x
 
 ## - Avoids the optional OpenGL extensions which have proven most problematic
-##   on some hardware.  Disabling this option may cause crashes and hangs on
-##   some unstable combinations of drivers and hardware.
+##   on some hardware.  Disabling this option may cause BETTER PERFORMANCE but
+##   may also cause CRASHES and hangs on some unstable combinations of drivers
+##   and hardware.
 export LL_GL_BASICEXT=x
 
 ## - Avoids *all* optional OpenGL extensions.  This is the safest and least-
diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp
index 8951d0ef330679fac5593a3b4b2279aa85127098..07daf89f1cd392dfefdc89a0bfc7937682dc6c97 100644
--- a/indra/newview/llagent.cpp
+++ b/indra/newview/llagent.cpp
@@ -103,7 +103,6 @@
 // end Ventrella
 
 extern LLMenuBarGL* gMenuBarView;
-extern F32 gMinObjectDistance;
 extern U8 gLastPickAlpha;
 extern F32 gFrameDTClamped;
 
@@ -3322,7 +3321,7 @@ void LLAgent::updateCamera()
 			attachment;
 			attachment = mAvatarObject->mAttachmentPoints.getNextData())
 		{
-			LLViewerObject *attached_object = attachment->getObject(0);
+			LLViewerObject *attached_object = attachment->getObject();
 			if (attached_object && !attached_object->isDead() && attached_object->mDrawable.notNull())
 			{
 				// clear any existing "early" movements of attachment
@@ -3432,21 +3431,26 @@ LLVector3d LLAgent::calcFocusPositionTargetGlobal()
 		{
 			LLDrawable* drawablep = mFocusObject->mDrawable;
 			
-			if (mTrackFocusObject && drawablep && drawablep->isActive())
+			if (mTrackFocusObject &&
+				drawablep && 
+				drawablep->isActive())
 			{
-				if (mFocusObject->isSelected())
+				if (!mFocusObject->isAvatar())
 				{
-					gPipeline.updateMoveNormalAsync(drawablep);
-				}
-				else
-				{
-					if (drawablep->isState(LLDrawable::MOVE_UNDAMPED))
+					if (mFocusObject->isSelected())
 					{
 						gPipeline.updateMoveNormalAsync(drawablep);
 					}
 					else
 					{
-						gPipeline.updateMoveDampedAsync(drawablep);
+						if (drawablep->isState(LLDrawable::MOVE_UNDAMPED))
+						{
+							gPipeline.updateMoveNormalAsync(drawablep);
+						}
+						else
+						{
+							gPipeline.updateMoveDampedAsync(drawablep);
+						}
 					}
 				}
 			}
@@ -3457,11 +3461,6 @@ LLVector3d LLAgent::calcFocusPositionTargetGlobal()
 			}
 			LLVector3 focus_agent = mFocusObject->getRenderPosition() + mFocusObjectOffset;
 			mFocusTargetGlobal.setVec(getPosGlobalFromAgent(focus_agent));
-			// *FIX: get camera pointat behavior working
-			//if (mTrackFocusObject)
-			//{
-			//	mCameraFocusOffset = gAgent.getPosGlobalFromAgent(gCamera->getOrigin()) - mFocusTargetGlobal;
-			//}
 		}
 		return mFocusTargetGlobal;
 	}
@@ -3826,8 +3825,6 @@ LLVector3d LLAgent::calcCameraPositionTargetGlobal(BOOL *hit_limit)
 	if (camera_position_global.mdV[VZ] < camera_land_height + camera_min_off_ground)
 	{
 		camera_position_global.mdV[VZ] = camera_land_height + camera_min_off_ground;
-
-		gMinObjectDistance = MIN_NEAR_PLANE;
 		isConstrained = TRUE;
 	}
 
@@ -6572,7 +6569,7 @@ void LLAgent::makeNewOutfit(
 			S32 attachment_pt = attachments_to_include[i];
 			LLViewerJointAttachment* attachment = mAvatarObject->mAttachmentPoints.getIfThere( attachment_pt );
 			if(!attachment) continue;
-			LLViewerObject* attached_object = attachment->getObject(0);
+			LLViewerObject* attached_object = attachment->getObject();
 			if(!attached_object) continue;
 			const LLUUID& item_id = attachment->getItemID();
 			if(item_id.isNull()) continue;
@@ -7195,7 +7192,7 @@ void LLAgent::userRemoveAllAttachments( void* userdata )
 		 attachment;
 		 attachment = avatarp->mAttachmentPoints.getNextData())
 	{
-		LLViewerObject* objectp = attachment->getObject(0);
+		LLViewerObject* objectp = attachment->getObject();
 		if (objectp)
 		{
 			gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
diff --git a/indra/newview/llcloud.cpp b/indra/newview/llcloud.cpp
index cdba49c40d9b70da87aef50d4e51c098c4aec0d0..4548adae9192693ad6997e0096a8bc2deccbdd09 100644
--- a/indra/newview/llcloud.cpp
+++ b/indra/newview/llcloud.cpp
@@ -97,16 +97,14 @@ void LLCloudGroup::updatePuffs(const F32 dt)
 		mVOCloudsp->setPositionRegion(mCenterRegion);
 		mVOCloudsp->setScale(LLVector3(256.f/CLOUD_GROUPS_PER_EDGE + CLOUD_PUFF_WIDTH,
 										 256.f/CLOUD_GROUPS_PER_EDGE + CLOUD_PUFF_WIDTH,
-										 CLOUD_HEIGHT_RANGE + CLOUD_PUFF_HEIGHT));
+										 CLOUD_HEIGHT_RANGE + CLOUD_PUFF_HEIGHT)*0.5f);
 		gPipeline.addObject(mVOCloudsp);
 	}
 
-	S32 i;
-
 	LLVector3 velocity;
 	LLVector3d vel_d;
 	// Update the positions of all of the clouds
-	for (i = 0; i < mCloudPuffs.count(); i++)
+	for (U32 i = 0; i < mCloudPuffs.size(); i++)
 	{
 		LLCloudPuff &puff = mCloudPuffs[i];
 		velocity = mCloudLayerp->getRegion()->mWind.getCloudVelocity(mCloudLayerp->getRegion()->getPosRegionFromGlobal(puff.mPositionGlobal));
@@ -121,8 +119,8 @@ void LLCloudGroup::updatePuffs(const F32 dt)
 
 void LLCloudGroup::updatePuffOwnership()
 {
-	S32 i = 0;
-	while (i < mCloudPuffs.count())
+	U32 i = 0;
+	while (i < mCloudPuffs.size())
 	{
 		if (mCloudPuffs[i].getLifeState() == LL_PUFF_DYING)
 		{
@@ -146,10 +144,11 @@ void LLCloudGroup::updatePuffOwnership()
 			continue;
 		}
 		//llinfos << "Puff handed off!" << llendl;
-		LLCloudPuff *puffp = new_cgp->mCloudPuffs.reserve_block(1);
-		puffp->mPositionGlobal = mCloudPuffs[i].mPositionGlobal;
-		puffp->mAlpha = mCloudPuffs[i].mAlpha;
-		mCloudPuffs.remove(i);
+		LLCloudPuff puff;
+		puff.mPositionGlobal = mCloudPuffs[i].mPositionGlobal;
+		puff.mAlpha = mCloudPuffs[i].mAlpha;
+		mCloudPuffs.erase(mCloudPuffs.begin() + i);
+		new_cgp->mCloudPuffs.push_back(puff);
 	}
 
 	//llinfos << "Puff count: " << LLCloudPuff::sPuffCount << llendl;
@@ -165,7 +164,7 @@ void LLCloudGroup::updatePuffCount()
 	S32 target_puff_count = llround(CLOUD_DENSITY * mDensity);
 	target_puff_count = llmax(0, target_puff_count);
 	target_puff_count = llmin(CLOUD_COUNT_MAX, target_puff_count);
-	S32 current_puff_count = mCloudPuffs.count();
+	S32 current_puff_count = (S32) mCloudPuffs.size();
 	// Create a new cloud if we need one
 	if (current_puff_count < target_puff_count)
 	{
@@ -186,7 +185,7 @@ void LLCloudGroup::updatePuffCount()
 
 	// Count the number of live puffs
 	S32 live_puff_count = 0;
-	for (i = 0; i < mCloudPuffs.count(); i++)
+	for (i = 0; i < (S32) mCloudPuffs.size(); i++)
 	{
 		if (mCloudPuffs[i].getLifeState() != LL_PUFF_DYING)
 		{
@@ -212,12 +211,12 @@ void LLCloudGroup::updatePuffCount()
 
 	// Remove fully dead puffs
 	i = 0;
-	while (i < mCloudPuffs.count())
+	while (i < (S32) mCloudPuffs.size())
 	{
 		if (mCloudPuffs[i].isDead())
 		{
 			//llinfos << "Removing dead puff!" << llendl;
-			mCloudPuffs.remove(i);
+			mCloudPuffs.erase(mCloudPuffs.begin() + i);
 			LLCloudPuff::sPuffCount--;
 		}
 		else
diff --git a/indra/newview/llcloud.h b/indra/newview/llcloud.h
index 06c2b5c9ff8978cd447b51c0fb514686e1b58132..3bb06c818bc2c7b043262e856ac58428e0871d9b 100644
--- a/indra/newview/llcloud.h
+++ b/indra/newview/llcloud.h
@@ -110,7 +110,7 @@ public:
 	BOOL inGroup(const LLCloudPuff &puff) const;
 
 	F32 getDensity() const							{ return mDensity; }
-	S32 getNumPuffs() const							{ return mCloudPuffs.count(); }
+	S32 getNumPuffs() const							{ return (S32) mCloudPuffs.size(); }
 	const LLCloudPuff &getPuff(const S32 i)			{ return mCloudPuffs[i]; }
 protected:
 	LLCloudLayer *mCloudLayerp;
@@ -118,7 +118,7 @@ protected:
 	F32 mDensity;
 	S32 mTargetPuffCount;
 
-	LLDynamicArray<LLCloudPuff> mCloudPuffs;
+	std::vector<LLCloudPuff> mCloudPuffs;
 	LLPointer<LLVOClouds> mVOCloudsp;
 };
 
diff --git a/indra/newview/llcolorswatch.h b/indra/newview/llcolorswatch.h
index 020f700dfb1074d87dc4d474b0b48bc2894917b8..999dce12967b1c4b250f196e97c80027799ca556 100644
--- a/indra/newview/llcolorswatch.h
+++ b/indra/newview/llcolorswatch.h
@@ -12,6 +12,7 @@
 #include "lluictrl.h"
 #include "v4color.h"
 #include "llfloater.h"
+#include "llviewerimage.h"
 
 //
 // Classes
diff --git a/indra/newview/llcylinder.cpp b/indra/newview/llcylinder.cpp
index 1774ff4ccfdca3da21cc7d5c2a1978fb2ec73abd..579fd61daafe081bba246ff4320cf6b50de585e0 100644
--- a/indra/newview/llcylinder.cpp
+++ b/indra/newview/llcylinder.cpp
@@ -15,7 +15,7 @@
 #include "llmath.h"
 #include "noise.h"
 #include "v3math.h"
-
+#include "llvertexbuffer.h"
 #include "llgl.h"
 #include "llglheaders.h"
 
@@ -24,6 +24,10 @@ LLCone		gCone;
 
 GLUquadricObj* gQuadObj = NULL;
 
+static const GLint SLICES[] = { 30, 20, 12, 6 };		// same as sphere slices
+static const GLint STACKS = 2;
+static const GLfloat RADIUS = 0.5f;
+	
 // draws a cylinder or cone
 // returns approximate number of triangles required
 U32 draw_cylinder_side(GLint slices, GLint stacks, GLfloat base_radius, GLfloat top_radius)
@@ -87,46 +91,27 @@ U32 draw_cylinder_cap(GLint slices, GLfloat base_radius, BOOL is_top)
 	return triangles;
 }
 
+void LLCylinder::drawSide(S32 detail)
+{
+	draw_cylinder_side(SLICES[detail], STACKS, RADIUS, RADIUS);
+}
 
-void LLCylinder::prerender()
+void LLCylinder::drawTop(S32 detail)
 {
-	GLint stacks = 2;
-	GLfloat radius = 0.5f;
-	GLint slices[CYLINDER_LEVELS_OF_DETAIL] = { 30, 20, 12, 6 };		// same as sphere slices
+	draw_cylinder_cap(SLICES[detail], RADIUS, TOP);
+}
 
-	for (S32 detail = 0; detail < CYLINDER_LEVELS_OF_DETAIL; detail++)
-	{
-		mTriangleCount[detail] = 0;
-
-		mDisplayListSide[detail] = glGenLists(1);
-		glNewList(mDisplayListSide[detail], GL_COMPILE);
-		mTriangleCount[detail] += draw_cylinder_side( slices[detail], stacks, radius, radius );
-		glEndList();
-
-		mDisplayListTop[detail] = glGenLists(1);
-		glNewList( mDisplayListTop[detail], GL_COMPILE);
-		mTriangleCount[detail] += draw_cylinder_cap( slices[detail], radius, TOP );
-		glEndList();
-
-		mDisplayListBottom[detail] = glGenLists(1);
-		glNewList( mDisplayListBottom[detail], GL_COMPILE);
-		mTriangleCount[detail] += draw_cylinder_cap( slices[detail], radius, BOTTOM );
-		glEndList();
-	}
+void LLCylinder::drawBottom(S32 detail)
+{
+	draw_cylinder_cap(SLICES[detail], RADIUS, BOTTOM);
 }
 
-void LLCylinder::cleanupGL()
+void LLCylinder::prerender()
 {
-	for (S32 detail = 0; detail < CYLINDER_LEVELS_OF_DETAIL; detail++)
-	{
-		glDeleteLists(mDisplayListSide[detail], 1);
-		mDisplayListSide[detail] = 0;
-		glDeleteLists(mDisplayListTop[detail], 1);
-		mDisplayListTop[detail] = 0;
-		glDeleteLists(mDisplayListBottom[detail], 1);
-		mDisplayListBottom[detail] = 0;
-	}
+}
 
+void LLCylinder::cleanupGL()
+{
 	if (gQuadObj)
 	{
 		gluDeleteQuadric(gQuadObj);
@@ -178,19 +163,21 @@ void LLCylinder::renderface(F32 pixel_area, S32 face)
 		return;
 	}
 
+	LLVertexBuffer::unbind();
+	
 	switch(face)
 	{
 	case 0:
 		glTranslatef(0.f, 0.f, -0.5f);
-		glCallList(mDisplayListSide[level_of_detail]);
+		drawSide(level_of_detail);
 		break;
 	case 1:
 		glTranslatef(0.0f, 0.f, 0.5f);
-		glCallList(mDisplayListTop[level_of_detail]);
+		drawTop(level_of_detail);
 		break;
 	case 2:
 		glTranslatef(0.0f, 0.f, -0.5f);
-		glCallList(mDisplayListBottom[level_of_detail]);
+		drawBottom(level_of_detail);
 		break;
 	default:
 		llerror("LLCylinder::renderface() fell out of switch", 0);
@@ -208,37 +195,10 @@ void LLCylinder::renderface(F32 pixel_area, S32 face)
 
 void LLCone::prerender()
 {
-	GLint stacks = 2;
-	GLfloat radius = 0.5f;
-	GLint slices[CONE_LEVELS_OF_DETAIL] = { 32, 18, 12, 6 };
-
-	for (S32 detail = 0; detail < CONE_LEVELS_OF_DETAIL; detail++)
-	{
-		mTriangleCount[detail] = 0;
-
-		mDisplayListSide[detail] = glGenLists(1);
-		glNewList(mDisplayListSide[detail], GL_COMPILE);
-		mTriangleCount[detail] += draw_cylinder_side( slices[detail], stacks, radius, 0.f );
-		glEndList();
-
-		mDisplayListBottom[detail] = glGenLists(1);
-		glNewList( mDisplayListBottom[detail], GL_COMPILE);
-		mTriangleCount[detail] += draw_cylinder_cap( slices[detail], radius, BOTTOM );
-		glEndList();
-	}
 }
 
 void LLCone::cleanupGL()
 {
-	for (S32 detail = 0; detail < CYLINDER_LEVELS_OF_DETAIL; detail++)
-	{
-		glDeleteLists(mDisplayListSide[detail], 1);
-		mDisplayListSide[detail] = 0;
-
-		glDeleteLists(mDisplayListBottom[detail], 1);
-		mDisplayListBottom[detail] = 0;
-	}
-
 	if (gQuadObj)
 	{
 		gluDeleteQuadric(gQuadObj);
@@ -246,6 +206,15 @@ void LLCone::cleanupGL()
 	}
 }
 
+void LLCone::drawSide(S32 detail)
+{
+	draw_cylinder_side( SLICES[detail], STACKS, RADIUS, 0.f );	
+}
+
+void LLCone::drawBottom(S32 detail)
+{
+	draw_cylinder_cap( SLICES[detail], RADIUS, BOTTOM );
+}
 
 void LLCone::render(S32 level_of_detail)
 {
@@ -263,8 +232,9 @@ void LLCone::render(S32 level_of_detail)
 	// center object at 0
 	glTranslatef(0.f, 0.f, - height / 2.0f);
 
-	glCallList(mDisplayListSide[level_of_detail]);
-	glCallList(mDisplayListBottom[level_of_detail]);
+	LLVertexBuffer::unbind();
+	drawSide(level_of_detail);
+	drawBottom(level_of_detail);
 
 	glMatrixMode(GL_MODELVIEW);
 	glPopMatrix();
@@ -288,15 +258,17 @@ void LLCone::renderface(S32 level_of_detail, S32 face)
 	glMatrixMode(GL_MODELVIEW);
 	glPushMatrix();
 
+	LLVertexBuffer::unbind();
+	
 	switch(face)
 	{
 	case 0:
 		glTranslatef(0.f, 0.f, -0.5f);
-		glCallList(mDisplayListSide[level_of_detail]);
+		drawSide(level_of_detail);
 		break;
 	case 1:
 		glTranslatef(0.f, 0.f, -0.5f);
-		glCallList(mDisplayListBottom[level_of_detail]);
+		drawBottom(level_of_detail);
 		break;
 	default:
 		llerror("LLCylinder::renderface() fell out of switch", 0);
diff --git a/indra/newview/llcylinder.h b/indra/newview/llcylinder.h
index 9150db4fb132dc6d27c4a9c7db6e5e8876c67ff6..94f32196074377f82840a75ede230949a15eb95e 100644
--- a/indra/newview/llcylinder.h
+++ b/indra/newview/llcylinder.h
@@ -20,20 +20,15 @@ const S32 CYLINDER_FACES = 3;
 
 class LLCylinder
 {
-protected:
-	U32	mDisplayListSide[CYLINDER_LEVELS_OF_DETAIL];
-	U32	mDisplayListTop[CYLINDER_LEVELS_OF_DETAIL];
-	U32	mDisplayListBottom[CYLINDER_LEVELS_OF_DETAIL];
-	U32		mTriangleCount[CYLINDER_LEVELS_OF_DETAIL];
-
 public:
 	void prerender();
+	void drawTop(S32 detail);
+	void drawSide(S32 detail);
+	void drawBottom(S32 detail);
 	void cleanupGL();
 
 	void render(F32 pixel_area);
 	void renderface(F32 pixel_area, S32 face);
-
-	U32		getTriangleCount(S32 level_of_detail)		{ return mTriangleCount[level_of_detail]; }
 };
 
 
@@ -46,20 +41,14 @@ const S32 CONE_LEVELS_OF_DETAIL = 4;
 const S32 CONE_FACES = 2;
 
 class LLCone
-{
-protected:
-	U32	mDisplayListSide[CONE_LEVELS_OF_DETAIL];
-	U32	mDisplayListBottom[CONE_LEVELS_OF_DETAIL];
-	U32		mTriangleCount[CONE_LEVELS_OF_DETAIL];
-
+{	
 public:
 	void prerender();
 	void cleanupGL();
-
+	void drawSide(S32 detail);
+	void drawBottom(S32 detail);
 	void render(S32 level_of_detail);
 	void renderface(S32 level_of_detail, S32 face);
-
-	U32		getTriangleCount(S32 level_of_detail)		{ return mTriangleCount[level_of_detail]; }
 };
 
 extern LLCylinder gCylinder;
diff --git a/indra/newview/lldrawable.cpp b/indra/newview/lldrawable.cpp
index c4ee6acb8c5e2cd919359b6b8f09ce1a76c0789d..868d61942d3edae149b36f5d546d3fe9a407e162 100644
--- a/indra/newview/lldrawable.cpp
+++ b/indra/newview/lldrawable.cpp
@@ -14,8 +14,6 @@
 #include "material_codes.h"
 
 // viewer includes
-#include "llagparray.h"
-#include "llagparray.inl"
 #include "llcriticaldamp.h"
 #include "llface.h"
 #include "lllightconstants.h"
@@ -58,6 +56,8 @@ U32 LLDrawable::sNumZombieDrawables = 0;
 F32 LLDrawable::sCurPixelAngle = 0;
 LLDynamicArrayPtr<LLPointer<LLDrawable> > LLDrawable::sDeadList;
 
+#define FORCE_INVISIBLE_AREA 16.f
+
 // static
 void LLDrawable::incrementVisible() 
 {
@@ -108,12 +108,11 @@ void LLDrawable::destroy()
 	std::for_each(mFaces.begin(), mFaces.end(), DeletePointer());
 	mFaces.clear();
 		
-	/*
-	if (!(sNumZombieDrawables % 10))
+	
+	/*if (!(sNumZombieDrawables % 10))
 	{
 		llinfos << "- Zombie drawables: " << sNumZombieDrawables << llendl;
-	}
-	*/
+	}*/	
 }
 
 void LLDrawable::markDead()
@@ -250,7 +249,7 @@ void LLDrawable::removeFace(const S32 i)
 }
 #endif
 
-LLFace*	LLDrawable::addFace(LLDrawPool *poolp, LLViewerImage *texturep, const BOOL shared_geom)
+LLFace*	LLDrawable::addFace(LLFacePool *poolp, LLViewerImage *texturep)
 {
 	LLMemType mt(LLMemType::MTYPE_DRAWABLE);
 	
@@ -259,16 +258,12 @@ LLFace*	LLDrawable::addFace(LLDrawPool *poolp, LLViewerImage *texturep, const BO
 	if (face)
 	{
 		mFaces.push_back(face);
-		face->setPool(poolp, texturep);
 
-		if (shared_geom)
+		if (poolp)
 		{
-			face->setState(LLFace::SHARED_GEOM);
-		}
-		else if (!isVisible()) 
-		{
-			face->setState(LLFace::BACKLIST);
+			face->setPool(poolp, texturep);
 		}
+
 		if (isState(UNLIT))
 		{
 			face->setState(LLFace::FULLBRIGHT);
@@ -277,7 +272,27 @@ LLFace*	LLDrawable::addFace(LLDrawPool *poolp, LLViewerImage *texturep, const BO
 	return face;
 }
 
-void LLDrawable::setNumFaces(const S32 newFaces, LLDrawPool *poolp, LLViewerImage *texturep)
+LLFace*	LLDrawable::addFace(const LLTextureEntry *te, LLViewerImage *texturep)
+{
+	LLMemType mt(LLMemType::MTYPE_DRAWABLE);
+	
+	LLFace *face = new LLFace(this, mVObjp);
+
+	face->setTEOffset(mFaces.size());
+	face->setTexture(texturep);
+	face->setPoolType(gPipeline.getPoolTypeFromTE(te, texturep));
+	mFaces.push_back(face);
+
+	if (isState(UNLIT))
+	{
+		face->setState(LLFace::FULLBRIGHT);
+	}
+
+	return face;
+
+}
+
+void LLDrawable::setNumFaces(const S32 newFaces, LLFacePool *poolp, LLViewerImage *texturep)
 {
 	if (newFaces == (S32)mFaces.size())
 	{
@@ -298,7 +313,7 @@ void LLDrawable::setNumFaces(const S32 newFaces, LLDrawPool *poolp, LLViewerImag
 	}
 }
 
-void LLDrawable::setNumFacesFast(const S32 newFaces, LLDrawPool *poolp, LLViewerImage *texturep)
+void LLDrawable::setNumFacesFast(const S32 newFaces, LLFacePool *poolp, LLViewerImage *texturep)
 {
 	if (newFaces <= (S32)mFaces.size() && newFaces >= (S32)mFaces.size()/2)
 	{
@@ -353,21 +368,43 @@ void LLDrawable::updateMaterial()
 
 void LLDrawable::makeActive()
 {		
+#if !LL_RELEASE_FOR_DOWNLOAD
+	if (mVObjp.notNull())
+	{
+		U32 pcode = mVObjp->getPCode();
+		if (pcode == LLViewerObject::LL_VO_WATER ||
+			pcode == LLViewerObject::LL_VO_SURFACE_PATCH ||
+			pcode == LLViewerObject::LL_VO_PART_GROUP ||
+			pcode == LLViewerObject::LL_VO_CLOUDS ||
+			pcode == LLViewerObject::LL_VO_STARS ||
+			pcode == LLViewerObject::LL_VO_GROUND ||
+			pcode == LLViewerObject::LL_VO_SKY)
+		{
+			llerrs << "Static viewer object has active drawable!" << llendl;
+		}
+	}
+#endif
+
 	if (!isState(ACTIVE)) // && mGeneration > 0)
 	{
 		setState(ACTIVE);
 		
+		//parent must be made active first
 		if (!isRoot() && !mParent->isActive())
 		{
 			mParent->makeActive();
 		}
-		
+
 		gPipeline.setActive(this, TRUE);
 
 		//all child objects must also be active
 		for (U32 i = 0; i < getChildCount(); i++)
 		{
-			getChild(i)->makeActive();
+			LLDrawable* drawable = getChild(i);
+			if (drawable)
+			{
+				drawable->makeActive();
+			}
 		}
 			
 		if (mVObjp->getPCode() == LL_PCODE_VOLUME)
@@ -384,7 +421,15 @@ void LLDrawable::makeActive()
 			gPipeline.markRebuild(this, LLDrawable::REBUILD_VOLUME, TRUE);
 		}
 	}
-	mQuietCount = 0;
+	updatePartition();
+	if (isRoot())
+	{
+		mQuietCount = 0;
+	}
+	else
+	{
+		getParent()->mQuietCount = 0;
+	}
 }
 
 
@@ -397,7 +442,7 @@ void LLDrawable::makeStatic()
 
 		if (mParent.notNull() && mParent->isActive())
 		{
-			llerrs << "Drawable became static with active parent!" << llendl;
+			llwarns << "Drawable becamse static with active parent!" << llendl;
 		}
 		
 		S32 child_count = mVObjp->mChildList.size();
@@ -406,6 +451,10 @@ void LLDrawable::makeStatic()
 			LLDrawable* child_drawable = mVObjp->mChildList[child_num]->mDrawable;
 			if (child_drawable)
 			{
+				if (child_drawable->getParent() != this)
+				{
+					llwarns << "Child drawable has unknown parent." << llendl;
+				}
 				child_drawable->makeStatic();
 			}
 		}
@@ -422,6 +471,7 @@ void LLDrawable::makeStatic()
 			setSpatialBridge(NULL);
 		}
 	}
+	updatePartition();
 }
 
 // Returns "distance" between target destination and resulting xfrom
@@ -480,9 +530,8 @@ F32 LLDrawable::updateXform(BOOL undamped)
 			if (scaled >= MIN_INTERPOLATE_DISTANCE_SQUARED)
 			{		
 				//scaling requires an immediate rebuild
-				gPipeline.markRebuild(this, LLDrawable::REBUILD_VOLUME, TRUE);
+				gPipeline.markRebuild(this, LLDrawable::REBUILD_POSITION, TRUE);
 			}
-
 		}
 		else
 		{
@@ -498,20 +547,6 @@ F32 LLDrawable::updateXform(BOOL undamped)
 	mXform.updateMatrix();
 	
 	mCurrentScale = target_scale;
-				
-	if (!getVOVolume())
-	{
-		movePartition();
-	}
-	else if (mSpatialBridge)
-	{
-		gPipeline.markMoved(mSpatialBridge, FALSE);
-	}
-	else
-	{
-		//a child prim moved and needs its verts regenerated
-		gPipeline.markRebuild(this, LLDrawable::REBUILD_VOLUME, TRUE);
-	}
 	
 	return dist_squared;
 }
@@ -521,11 +556,6 @@ void LLDrawable::setRadius(F32 radius)
 	if (mRadius != radius)
 	{
 		mRadius = radius;
-		updateBinRadius();
-		if (!getVOVolume())
-		{
-			movePartition();
-		}
 	}
 }
 
@@ -554,13 +584,10 @@ void LLDrawable::moveUpdatePipeline(BOOL moved)
 
 void LLDrawable::movePartition()
 {
-	if (getSpatialGroup() || getVOVolume())
+	LLSpatialPartition* part = getSpatialPartition();
+	if (part)
 	{
-		LLSpatialPartition* part = getSpatialPartition();
-		if (part)
-		{
-			part->move(this, getSpatialGroup());
-		}
+		part->move(this, getSpatialGroup());
 	}
 }
 
@@ -606,10 +633,27 @@ BOOL LLDrawable::updateMoveUndamped()
 	}
 
 	mVObjp->clearChanged(LLXform::MOVED);
-
+	
 	return TRUE;
 }
 
+void LLDrawable::updatePartition()
+{
+	if (!getVOVolume())
+	{
+		movePartition();
+	}
+	else if (mSpatialBridge)
+	{
+		gPipeline.markMoved(mSpatialBridge, FALSE);
+	}
+	else
+	{
+		//a child prim moved and needs its verts regenerated
+		gPipeline.markRebuild(this, LLDrawable::REBUILD_POSITION, TRUE);
+	}
+}
+
 BOOL LLDrawable::updateMoveDamped()
 {
 	F32 dist_squared = updateXform(FALSE);
@@ -635,25 +679,42 @@ BOOL LLDrawable::updateMoveDamped()
 
 void LLDrawable::updateDistance(LLCamera& camera)
 {
-	if (mVObjp->isHUDAttachment())
-	{
-		mDistanceWRTCamera = 1.0f;
-		if (sCurVisible % 16 == 0)
-		{
-			mVObjp->updateLOD();
-		}
-		return;
-	}
-
-	LLVector3 pos(getPositionGroup());
-	
-	pos -= camera.getOrigin();	
-	mDistanceWRTCamera = pos.magVec();
-	
 	//switch LOD with the spatial group to avoid artifacts
 	LLSpatialGroup* sg = getSpatialGroup();
+
+	LLVector3 pos;
+
 	if (!sg || sg->changeLOD())
 	{
+		LLVOVolume* volume = getVOVolume();
+		if (volume)
+		{
+			volume->updateRelativeXform();
+			pos = LLVector3(0,0,0) * volume->getRelativeXform();
+
+			for (S32 i = 0; i < getNumFaces(); i++)
+			{
+				LLFace* facep = getFace(i);
+				if (facep->getPoolType() == LLDrawPool::POOL_ALPHA)
+				{
+					LLVector3 box = (facep->mExtents[1] - facep->mExtents[0]) * 0.25f;
+					LLVector3 v = (facep->mCenterLocal-camera.getOrigin());
+					LLVector3 at = camera.getAtAxis();
+					for (U32 j = 0; j < 3; j++)
+					{
+						v.mV[j] -= box.mV[j] * at.mV[j];
+					}
+					facep->mDistance = v * camera.getAtAxis();
+				}
+			}
+		}
+		else
+		{
+			pos = LLVector3(getPositionGroup());
+		}
+
+		pos -= camera.getOrigin();	
+		mDistanceWRTCamera = llround(pos.magVec(), 0.01f);
 		mVObjp->updateLOD();
 	}
 }
@@ -668,67 +729,33 @@ void LLDrawable::updateTexture()
 		return;
 	}
 	
-	// *FIX: this updates textures on all faces in this drawable, not
-	// just the viewer object we care about
-	if (mVObjp->getNumTEs())
+	if (getNumFaces() != mVObjp->getNumTEs())
+	{ //drawable is transitioning its face count
+		return;
+	}
+
+	if (getVOVolume())
 	{
-		// For each face in this drawable, change the drawpool if necessary.
-		for (S32 i = 0; i < getNumFaces(); i++)
+		if (!isActive())
+		{
+			gPipeline.markMoved(this);
+		}
+		else
 		{
-			LLFace *facep = mFaces[i];
-			U32 pool_type = facep->getPool()->getType();
-
-			if ((pool_type == LLDrawPool::POOL_SIMPLE) ||
-				(pool_type == LLDrawPool::POOL_ALPHA) ||
-				(pool_type == LLDrawPool::POOL_HUD) ||
-				(pool_type == LLDrawPool::POOL_MEDIA) ||
-				(pool_type == LLDrawPool::POOL_BUMP))
+			if (isRoot())
 			{
-				LLViewerObject* objp = facep->getViewerObject();
-				S32 te_offset = facep->getTEOffset();
-
-				if (te_offset >= objp->getNumTEs()) // Shouldn't happen
-				{
-					llwarns << "TE offsets don't match!" << llendl;
-					facep->setTEOffset(-1);
-					continue;
-				}
-
-				LLDrawPool* poolp = NULL;
-				LLViewerImage* imagep = (te_offset >= 0) ? objp->getTEImage(te_offset) : facep->getTexture();
-				if (facep->isState(LLFace::HUD_RENDER))
-				{
-					poolp = gPipeline.getPool(LLDrawPool::POOL_HUD);
-				}
-				else if (te_offset >= 0)
-				{
-					// This face actually uses texture entries...
-					const LLTextureEntry* te = facep->getTextureEntry();
-					poolp = LLPipeline::getPoolFromTE(te, imagep);
-				}
-				else
-				{
-					// No texture entry for this face.
-					if (!imagep)
-					{
-						poolp = gPipeline.getPool(LLDrawPool::POOL_SIMPLE, NULL);
-					}
-					else if ((imagep->getComponents() == 4) || (imagep->getComponents() == 2))
-					{
-						poolp = gPipeline.getPool(LLDrawPool::POOL_ALPHA);
-					}
-					else
-					{
-						poolp = gPipeline.getPool(LLDrawPool::POOL_SIMPLE, imagep);
-					}
-				}
-				facep->setPool(poolp, imagep);
+				mQuietCount = 0;
+			}
+			else
+			{
+				getParent()->mQuietCount = 0;
 			}
 		}
+				
+		gPipeline.markRebuild(this, LLDrawable::REBUILD_MATERIAL, TRUE);
 	}
 }
 
-
 BOOL LLDrawable::updateGeometry(BOOL priority)
 {
 	llassert(mVObjp.notNull());
@@ -769,12 +796,9 @@ void LLDrawable::shiftPos(const LLVector3 &shift_vector)
 	mXform.setScale(1,1,1);
 	mXform.updateMatrix();
 
-	if (isStatic() || // *FIX: don't know why this is happening, but
-					// some terrain patches are becoming active
-					// (earth quake, maybe?) DP
-		getRenderType() == LLPipeline::RENDER_TYPE_TERRAIN)
+	if (isStatic())
 	{
-		LLStrider<LLVector3>  verticesp;
+		gPipeline.markRebuild(this, LLDrawable::REBUILD_GEOMETRY, TRUE);
 
 		for (S32 i = 0; i < getNumFaces(); i++)
 		{
@@ -783,34 +807,13 @@ void LLDrawable::shiftPos(const LLVector3 &shift_vector)
 			facep->mExtents[0] += shift_vector;
 			facep->mExtents[1] += shift_vector;
 			
-			if (facep->hasGeometry() && !facep->isState(LLFace::SHARED_GEOM))
+			if (facep->hasGeometry())
 			{
-				S32 index = facep->getVertices(verticesp);
-				if (index >= 0)
-				{
-					S32 vertex_count = facep->getGeomCount();
-					for (S32 j = 0; j < vertex_count; j++)
-					{
-						*verticesp += shift_vector;
-						verticesp++;
-					}
-				}
+				facep->mVertexBuffer = NULL;
+				facep->mLastVertexBuffer = NULL;
 			}
 		}
-	}
-	else
-	{
-		// Update the face centers.
-		for (S32 i = 0; i < getNumFaces(); i++)
-		{
-			LLFace *facep = getFace(i);
-			facep->mCenterAgent += shift_vector;
-		}
-	}
-
-	//update spatial extents
-	if (!getVOVolume() || isStatic())
-	{
+		
 		mExtents[0] += shift_vector;
 		mExtents[1] += shift_vector;
 		mPositionGroup += LLVector3d(shift_vector);
@@ -855,6 +858,8 @@ void LLDrawable::updateSpatialExtents()
 		mVObjp->updateSpatialExtents(mExtents[0], mExtents[1]);
 	}
 	
+	updateBinRadius();
+	
 	if (mSpatialBridge.notNull())
 	{
 		mPositionGroup.setVec(0,0,0);
@@ -864,11 +869,14 @@ void LLDrawable::updateSpatialExtents()
 
 void LLDrawable::updateBinRadius()
 {
-	S32 binLOD = mVObjp ? mVObjp->getLOD() : 2;
-	static F64 detail_bins[] = { 8, 4, 2, 1 };
-	F32 radius = getVOVolume() && isStatic() ? 
-				(mExtents[1]-mExtents[0]).magVec() : getRadius();
-	mBinRadius = detail_bins[binLOD] * llmax((F64) radius, (3-binLOD)*0.25);
+	if (mVObjp.notNull())
+	{
+		mBinRadius = mVObjp->getBinRadius();
+	}
+	else
+	{
+		mBinRadius = getRadius()*4.f;
+	}
 }
 
 void LLDrawable::updateLightSet()
@@ -879,6 +887,7 @@ void LLDrawable::updateLightSet()
 		return;
 	}
 
+	LLSpatialPartition* part = gPipeline.getSpatialPartition(LLPipeline::PARTITION_VOLUME);
 	LLVOVolume* light = getVOVolume();
 	if (isLight() && light)
 	{
@@ -888,7 +897,7 @@ void LLDrawable::updateLightSet()
 			gPipeline.markRelight(*iter);
 		}
 		mLightSet.clear();
-		gPipeline.mObjectPartition->getObjects(getPositionAgent(), light->getLightRadius(), mLightSet);
+		part->getObjects(getPositionAgent(), light->getLightRadius(), mLightSet);
 		for (drawable_set_t::iterator iter = mLightSet.begin(); iter != mLightSet.end(); iter++)
 		{
 			gPipeline.markRelight(*iter);
@@ -898,8 +907,8 @@ void LLDrawable::updateLightSet()
 	{
 		// mLightSet points to nearby lights
 		mLightSet.clear();
-		gPipeline.mObjectPartition->getLights(getPositionAgent(), getRadius(), mLightSet);
-		const U32 max_lights = 16;
+		part->getLights(getPositionAgent(), getRadius(), mLightSet);
+		const S32 max_lights = 16;
 		if (mLightSet.size() > max_lights)
 		{
 			typedef std::set<std::pair<F32,LLPointer<LLDrawable> > > sorted_pair_set_t;
@@ -1034,28 +1043,28 @@ LLSpatialPartition* LLDrawable::getSpatialPartition()
 { 
 	LLSpatialPartition* retval = NULL;
 	
-	if (mVObjp->isHUDAttachment())
-	{	//HUD attachments don't get space partitioned
-		return NULL;
-	}
-
 	if (!mVObjp || 
 		!getVOVolume() ||
 		isStatic())
 	{
-		retval = gPipeline.mObjectPartition;
+		retval = gPipeline.getSpatialPartition((LLViewerObject*) mVObjp);
 	}
-
-	//must be an active volume
-	if (!retval && isRoot())
-	{	
+	else if (isRoot())
+	{	//must be an active volume
 		if (!mSpatialBridge)
 		{
-			setSpatialBridge(new LLSpatialBridge(this));
+			if (mVObjp->isHUDAttachment())
+			{
+				setSpatialBridge(new LLHUDBridge(this));
+			}
+			else
+			{
+				setSpatialBridge(new LLVolumeBridge(this));
+			}
 		}
 		return mSpatialBridge->asPartition();
 	}
-	else if (!retval)
+	else 
 	{
 		retval = getParent()->getSpatialPartition();
 	}
@@ -1069,27 +1078,73 @@ LLSpatialPartition* LLDrawable::getSpatialPartition()
 	return retval;
 }
 
+
+BOOL LLDrawable::isVisible() const
+{
+	if (mVisible == sCurVisible)
+	{
+		return TRUE;
+	}
+	
+	if (isActive())
+	{
+		if (isRoot())
+		{
+			LLSpatialGroup* group = mSpatialBridge.notNull() ? mSpatialBridge->getSpatialGroup() :
+									getSpatialGroup();
+			if (!group || group->isVisible())
+			{
+				mVisible = sCurVisible;
+				return TRUE;
+			}
+		}
+		else
+		{
+			if (getParent()->isVisible())
+			{
+				mVisible = sCurVisible;
+				return TRUE;
+			}
+		}
+	}
+	else
+	{
+		LLSpatialGroup* group = getSpatialGroup();
+		if (!group || group->isVisible())
+		{
+			mVisible = sCurVisible;
+			return TRUE;
+		}
+	}
+
+	return FALSE;
+}
+
 //=======================================
 // Spatial Partition Bridging Drawable
 //=======================================
 
-LLSpatialBridge::LLSpatialBridge(LLDrawable* root)
+LLSpatialBridge::LLSpatialBridge(LLDrawable* root, U32 data_mask)
+: LLSpatialPartition(data_mask, FALSE)
 {
 	mDrawable = root;
 	root->setSpatialBridge(this);
 	
-	mRenderType = mDrawable->mRenderType; //w00! magic!
-		
+	mRenderType = mDrawable->mRenderType;
+	mDrawableType = mDrawable->mRenderType;
+	
+	mPartitionType = LLPipeline::PARTITION_VOLUME;
+	
 	mOctree->balance();
 	
-	gPipeline.mObjectPartition->put(this);
+	gPipeline.getSpatialPartition(mPartitionType)->put(this);
 }
 
 LLSpatialBridge::~LLSpatialBridge()
 {	
 	if (getSpatialGroup())
 	{
-		gPipeline.mObjectPartition->remove(this, getSpatialGroup());
+		gPipeline.getSpatialPartition(mPartitionType)->remove(this, getSpatialGroup());
 	}
 }
 
@@ -1097,7 +1152,6 @@ void LLSpatialBridge::updateSpatialExtents()
 {
 	LLSpatialGroup* root = (LLSpatialGroup*) mOctree->getListener(0);
 	
-	if (mOctree->getChildCount() > 0)
 	{
 		LLFastTimer ftm(LLFastTimer::FTM_CULL_REBOUND);
 		root->rebound();
@@ -1145,6 +1199,9 @@ void LLSpatialBridge::updateSpatialExtents()
 			}
 		}
 	}
+
+	LLVector3 diagonal = newMax - newMin;
+	mRadius = diagonal.magVec() * 0.5f;
 	
 	mPositionGroup.setVec((newMin + newMax) * 0.5f);
 	updateBinRadius();
@@ -1152,9 +1209,7 @@ void LLSpatialBridge::updateSpatialExtents()
 
 void LLSpatialBridge::updateBinRadius()
 {
-	F32 rad = ((mExtents[1]-mExtents[0])*0.5f).magVec();
-	mBinRadius = llmax(rad, 2.f);
-	mRadius = rad;
+	mBinRadius = llmin((F32) mOctree->getSize().mdV[0]*0.5f, 256.f);
 }
 
 LLCamera LLSpatialBridge::transformCamera(LLCamera& camera)
@@ -1162,39 +1217,123 @@ LLCamera LLSpatialBridge::transformCamera(LLCamera& camera)
 	LLCamera ret = camera;
 	LLXformMatrix* mat = mDrawable->getXform();
 	LLVector3 center = LLVector3(0,0,0) * mat->getWorldMatrix();
-	//LLQuaternion rotation = LLQuaternion(mat->getWorldMatrix());
+	LLQuaternion rotation = LLQuaternion(mat->getWorldMatrix());
 
-	//ret.rotate(~mat->getRotation());
 	LLVector3 delta = ret.getOrigin() - center;
-	delta *= ~mat->getRotation();
-	ret.setOrigin(delta);
+	LLQuaternion rot = ~mat->getRotation();
+
+	delta *= rot;
+	LLVector3 lookAt = ret.getAtAxis();
+	LLVector3 up_axis = ret.getUpAxis();
+	LLVector3 left_axis = ret.getLeftAxis();
 
+	lookAt *= rot;
+	up_axis *= rot;
+	left_axis *= rot;
+
+	ret.setOrigin(delta);
+	ret.setAxes(lookAt, left_axis, up_axis);
+		
 	return ret;
 }
 
 void LLDrawable::setVisible(LLCamera& camera, std::vector<LLDrawable*>* results, BOOL for_select)
 {
 	mVisible = sCurVisible;
+	
+#if 0 && !LL_RELEASE_FOR_DOWNLOAD
+	//crazy paranoid rules checking
+	if (getVOVolume())
+	{
+		if (!isRoot())
+		{
+			if (isActive() && !mParent->isActive())
+			{
+				llerrs << "Active drawable has static parent!" << llendl;
+			}
+			
+			if (isStatic() && !mParent->isStatic())
+			{
+				llerrs << "Static drawable has active parent!" << llendl;
+			}
+			
+			if (mSpatialBridge)
+			{
+				llerrs << "Child drawable has spatial bridge!" << llendl;
+			}
+		}
+		else if (isActive() && !mSpatialBridge)
+		{
+			llerrs << "Active root drawable has no spatial bridge!" << llendl;
+		}
+		else if (isStatic() && mSpatialBridge.notNull())
+		{
+			llerrs << "Static drawable has spatial bridge!" << llendl;
+		}
+	}
+#endif
 }
 
+class LLOctreeMarkNotCulled: public LLOctreeTraveler<LLDrawable>
+{
+public:
+	LLCamera* mCamera;
+	
+	LLOctreeMarkNotCulled(LLCamera* camera_in) : mCamera(camera_in) { }
+	
+	virtual void traverse(const LLOctreeNode<LLDrawable>* node)
+	{
+		LLSpatialGroup* group = (LLSpatialGroup*) node->getListener(0);
+		group->clearState(LLSpatialGroup::OCCLUDED | LLSpatialGroup::CULLED);
+		LLOctreeTraveler<LLDrawable>::traverse(node);
+	}
+	
+	void visit(const LLOctreeState<LLDrawable>* branch)
+	{
+		gPipeline.markNotCulled((LLSpatialGroup*) branch->getListener(0), *mCamera, TRUE);
+	}
+};
+
 void LLSpatialBridge::setVisible(LLCamera& camera_in, std::vector<LLDrawable*>* results, BOOL for_select)
 {
+	if (!gPipeline.hasRenderType(mDrawableType))
+	{
+		return;
+	}
+
+	LLViewerObject *vobj = mDrawable->getVObj();
+	if (vobj && vobj->isAttachment() && !vobj->isHUDAttachment())
+	{
+		LLVOAvatar* av;
+		LLDrawable* parent = mDrawable->getParent();
+
+		if (parent)
+		{
+			av = (LLVOAvatar*) parent->getVObj();
+		
+			if (!av->isVisible())
+			{
+				return;
+			}
+		}
+	}
+	
+
+	LLSpatialGroup* group = (LLSpatialGroup*) mOctree->getListener(0);
+	group->rebound();
+	
 	LLVector3 center = (mExtents[0] + mExtents[1]) * 0.5f;
 	LLVector3 size = (mExtents[1]-mExtents[0]) * 0.5f;
 
 	if (camera_in.AABBInFrustum(center, size))
 	{
-		LLVector3 lookAt = center - camera_in.getOrigin();
-		F32 distSqr = lookAt.magVecSquared();
-		F32 objRad = size.magVecSquared();
-		
-		if (objRad/distSqr < SG_MIN_DIST_RATIO*4)
+		if (LLPipeline::calcPixelArea(center, size, camera_in) < FORCE_INVISIBLE_AREA)
 		{
 			return;
 		}
 
 		LLDrawable::setVisible(camera_in);
-
+		
 		if (for_select)
 		{
 			results->push_back(mDrawable);
@@ -1203,42 +1342,36 @@ void LLSpatialBridge::setVisible(LLCamera& camera_in, std::vector<LLDrawable*>*
 				results->push_back(mDrawable->getChild(i));
 			}
 		}
-		else
+		else 
 		{
-			const LLVector3* extents = mDrawable->getSpatialExtents();
-			objRad = mDrawable->getRadius();
-			objRad *= objRad;
-			
-			if (objRad/distSqr > SG_MIN_DIST_RATIO)
-			{
-				gPipeline.markNotCulled(mDrawable, camera_in);
-			}
-			
-			for (U32 i = 0; i < mDrawable->getChildCount(); i++)
-			{
-				LLDrawable* child = mDrawable->getChild(i);
-				extents = child->getSpatialExtents();
-				objRad = child->getRadius();
-				objRad *= objRad;
-				
-				if (objRad/distSqr > SG_MIN_DIST_RATIO)
-				{
-					gPipeline.markNotCulled(mDrawable->getChild(i), camera_in);
-				}
-			}
-		}
+			LLCamera trans_camera = transformCamera(camera_in);
+			LLOctreeMarkNotCulled culler(&trans_camera);
+			culler.traverse(mOctree);
+		}		
 	}
 }
 
 void LLSpatialBridge::updateDistance(LLCamera& camera_in)
 {
+	if (mDrawable == NULL)
+	{
+		markDead();
+		return;
+	}
+
 	LLCamera camera = transformCamera(camera_in);
 	
 	mDrawable->updateDistance(camera);
 	
 	for (U32 i = 0; i < mDrawable->getChildCount(); ++i)
 	{
-		mDrawable->getChild(i)->updateDistance(camera);
+		LLDrawable* child = mDrawable->getChild(i);
+		if (!child)
+		{
+			llwarns << "Corrupt drawable found while updating spatial bridge distance." << llendl;
+			continue;
+		}
+		child->updateDistance(camera);
 	}
 }
 
@@ -1261,7 +1394,7 @@ void LLSpatialBridge::move(LLDrawable *drawablep, LLSpatialGroup *curp, BOOL imm
 BOOL LLSpatialBridge::updateMove()
 {
 	mOctree->balance();
-	gPipeline.mObjectPartition->move(this, getSpatialGroup(), TRUE);
+	gPipeline.getSpatialPartition(mPartitionType)->move(this, getSpatialGroup(), TRUE);
 	return TRUE;
 }
 
@@ -1319,3 +1452,81 @@ const LLVector3	LLDrawable::getPositionAgent() const
 	}
 }
 
+BOOL LLDrawable::isAnimating() const
+{
+	if (!getVObj())
+	{
+		return TRUE;
+	}
+
+	if (getScale() != mVObjp->getScale())
+	{
+		return TRUE;
+	}
+
+	if (mVObjp->isFlexible())
+	{
+		return TRUE;
+	}
+
+	if (mVObjp->getPCode() == LLViewerObject::LL_VO_PART_GROUP)
+	{
+		return TRUE;
+	}
+
+	if (mVObjp->getPCode() == LLViewerObject::LL_VO_CLOUDS)
+	{
+		return TRUE;
+	}
+
+	LLVOVolume* vol = getVOVolume();
+	if (vol && vol->mTextureAnimp)
+	{
+		return TRUE;
+	}
+
+	if (!isRoot() && !mVObjp->getAngularVelocity().isExactlyZero())
+	{
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+void LLDrawable::updateFaceSize(S32 idx)
+{
+	if (mVObjp.notNull())
+	{
+		mVObjp->updateFaceSize(idx);
+	}
+}
+
+LLBridgePartition::LLBridgePartition()
+: LLSpatialPartition(0, TRUE) 
+{ 
+	mRenderByGroup = FALSE; 
+	mDrawableType = LLPipeline::RENDER_TYPE_AVATAR; 
+	mPartitionType = LLPipeline::PARTITION_BRIDGE;
+	mLODPeriod = 1;
+	mSlopRatio = 0.f;
+}
+
+LLHUDBridge::LLHUDBridge(LLDrawable* drawablep)
+: LLVolumeBridge(drawablep)
+{
+	mDrawableType = LLPipeline::RENDER_TYPE_HUD;
+	mPartitionType = LLPipeline::PARTITION_HUD;
+	mSlopRatio = 0.0f;
+}
+
+F32 LLHUDBridge::calcPixelArea(LLSpatialGroup* group, LLCamera& camera)
+{
+	return 1024.f;
+}
+
+
+void LLHUDBridge::shiftPos(const LLVector3& vec)
+{
+	//don't shift hud bridges on region crossing
+}
+
diff --git a/indra/newview/lldrawable.h b/indra/newview/lldrawable.h
index cb9f9701068c78dbc5a8b59e139dbd2f7ed998a4..fef8b02ad5e7ad7a88a141ee7b57395deeb0ac61 100644
--- a/indra/newview/lldrawable.h
+++ b/indra/newview/lldrawable.h
@@ -19,8 +19,8 @@
 #include "v4coloru.h"
 #include "llquaternion.h"
 #include "xform.h"
+#include "llmemtype.h"
 #include "llprimitive.h"
-#include "llviewerimage.h"
 #include "lldarray.h"
 #include "llstat.h"
 #include "llviewerobject.h"
@@ -33,6 +33,7 @@ class LLSpatialGroup;
 class LLSpatialBridge;
 class LLSpatialPartition;
 class LLVOVolume;
+class LLViewerImage;
 
 extern F32 gFrameTimeSeconds;
 
@@ -55,7 +56,7 @@ public:
 
 	BOOL isLight() const;
 
-	BOOL isVisible() const		{ return (mVisible == sCurVisible); }
+	BOOL isVisible() const;		
 	virtual void setVisible(LLCamera& camera_in, std::vector<LLDrawable*>* results = NULL, BOOL for_select = FALSE);
 
 
@@ -102,10 +103,11 @@ public:
 	inline S32			getNumFaces()      	 const;
 
 	//void                removeFace(const S32 i); // SJB: Avoid using this, it's slow
-	LLFace*				addFace(LLDrawPool *poolp, LLViewerImage *texturep, const BOOL shared_geom = FALSE);
+	LLFace*				addFace(LLFacePool *poolp, LLViewerImage *texturep);
+	LLFace*				addFace(const LLTextureEntry *te, LLViewerImage *texturep);
 	void				deleteFaces(S32 offset, S32 count);
-	void                setNumFaces(const S32 numFaces, LLDrawPool *poolp, LLViewerImage *texturep);
-	void                setNumFacesFast(const S32 numFaces, LLDrawPool *poolp, LLViewerImage *texturep);
+	void                setNumFaces(const S32 numFaces, LLFacePool *poolp, LLViewerImage *texturep);
+	void                setNumFacesFast(const S32 numFaces, LLFacePool *poolp, LLViewerImage *texturep);
 	void				mergeFaces(LLDrawable* src);
 
 	void init();
@@ -119,6 +121,8 @@ public:
 
 	BOOL isActive()	const							{ return isState(ACTIVE); }
 	BOOL isStatic() const							{ return !isActive(); }
+	BOOL isAnimating() const;
+
 	virtual BOOL updateMove();
 	virtual void movePartition();
 	
@@ -127,6 +131,7 @@ public:
 	virtual void updateDistance(LLCamera& camera);
 	BOOL updateGeometry(BOOL priority);
 	BOOL updateLighting(BOOL priority);
+	void updateFaceSize(S32 idx);
 	void updateLightSet();
 	
 	F32  getSunShadowFactor() const				{ return mSunShadowFactor; }
@@ -176,6 +181,7 @@ public:
 protected:
 	virtual ~LLDrawable() { destroy(); }
 	void moveUpdatePipeline(BOOL moved);
+	void updatePartition();
 	BOOL updateMoveDamped();
 	BOOL updateMoveUndamped();
 	
@@ -187,6 +193,7 @@ public:
 	typedef std::set<LLPointer<LLDrawable> > drawable_set_t;
 	typedef std::vector<LLPointer<LLDrawable> > drawable_vector_t;
 	typedef std::list<LLPointer<LLDrawable> > drawable_list_t;
+	typedef std::queue<LLPointer<LLDrawable> > drawable_queue_t;
 	
 	struct CompareDistanceGreater
 	{
@@ -227,11 +234,14 @@ public:
 		UNLIT			= 0x00000200,
 		LIGHT			= 0x00000400,
 		LIGHTING_BUILT	= 0x00000800,
-		REBUILD_VOLUME	= 0x00001000,
-		REBUILD_TCOORD	= 0x00002000,
-		REBUILD_GEOMETRY= REBUILD_VOLUME|REBUILD_TCOORD,
-		REBUILD_LIGHTING= 0x00008000,
-		REBUILD_ALL		= REBUILD_GEOMETRY|REBUILD_LIGHTING,
+		REBUILD_VOLUME  = 0x00001000,	//volume changed LOD or parameters, or vertex buffer changed
+		REBUILD_TCOORD	= 0x00002000,	//texture coordinates changed
+		REBUILD_COLOR	= 0x00004000,	//color changed
+		REBUILD_LIGHTING= 0x00008000,	//lighting information changed
+		REBUILD_POSITION= 0x00010000,	//vertex positions/normals changed
+		REBUILD_GEOMETRY= REBUILD_POSITION|REBUILD_TCOORD|REBUILD_COLOR,
+		REBUILD_MATERIAL= REBUILD_TCOORD|REBUILD_COLOR,
+		REBUILD_ALL		= REBUILD_GEOMETRY|REBUILD_LIGHTING|REBUILD_VOLUME,
 		ON_SHIFT_LIST	= 0x00100000,
 // 		NO_INTERP_COLOR = 0x00200000,
 		BLOCKER			= 0x00400000,
@@ -266,6 +276,8 @@ public:
 	void setSpatialBridge(LLSpatialBridge* bridge) { mSpatialBridge = (LLDrawable*) bridge; }
 	LLSpatialBridge* getSpatialBridge() { return (LLSpatialBridge*) (LLDrawable*) mSpatialBridge; }
 	
+	static F32 sCurPixelAngle; //current pixels per radian
+
 protected:
 	typedef std::vector<LLFace*> face_list_t;
 	
@@ -277,7 +289,7 @@ protected:
 	LLPointer<LLDrawable> mSpatialBridge;
 	S32				mSpatialGroupOffset;
 	
-	U32				mVisible;
+	mutable U32		mVisible;
 	F32				mRadius;
 	LLVector3		mExtents[2];
 	LLVector3d		mPositionGroup;
@@ -289,7 +301,6 @@ protected:
 	LLVector3		mCurrentScale;
 	
 	static U32 sCurVisible; // Counter for what value of mVisible means currently visible
-	static F32 sCurPixelAngle; //current pixels per radian
 
 	static U32 sNumZombieDrawables;
 	static LLDynamicArrayPtr<LLPointer<LLDrawable> > sDeadList;
@@ -299,6 +310,7 @@ protected:
 inline LLFace* LLDrawable::getFace(const S32 i) const
 {
 	llassert((U32)i < mFaces.size());
+	llassert(mFaces[i]);
 	return mFaces[i];
 }
 
diff --git a/indra/newview/lldrawpool.cpp b/indra/newview/lldrawpool.cpp
index 899d49f380307593a7c810d41fc5ee698bc7c3b1..9ab6c700abcabfb3590b613dda583515f06a3806 100644
--- a/indra/newview/lldrawpool.cpp
+++ b/indra/newview/lldrawpool.cpp
@@ -13,7 +13,6 @@
 #include "llfasttimer.h"
 #include "llviewercontrol.h"
 
-#include "llagparray.h"
 #include "lldrawable.h"
 #include "lldrawpoolalpha.h"
 #include "lldrawpoolavatar.h"
@@ -24,53 +23,38 @@
 #include "lldrawpoolsky.h"
 #include "lldrawpoolstars.h"
 #include "lldrawpooltree.h"
-#include "lldrawpooltreenew.h"
 #include "lldrawpoolterrain.h"
 #include "lldrawpoolwater.h"
-#include "lldrawpoolhud.h"
 #include "llface.h"
 #include "llviewerobjectlist.h" // For debug listing.
-#include "llvotreenew.h"
 #include "pipeline.h"
 
-#include "llagparray.inl"
-
-U32 LLDrawPool::sDataSizes[LLDrawPool::DATA_MAX_TYPES] =
-{
-	12, // DATA_VERTICES
-	8,	// DATA_TEX_COORDS0
-	8,	// DATA_TEX_COORDS1
-	8,	// DATA_TEX_COORDS2
-	8,	// DATA_TEX_COORDS3
-	12, // DATA_NORMALS
-	4,	// DATA_VERTEX_WEIGHTS,
-	16, // DATA_CLOTHING_WEIGHTS
-	12,	// DATA_BINORMALS
-	4,	// DATA_COLORS
-};
-
 S32 LLDrawPool::sNumDrawPools = 0;
 
+
+//=============================
+// Draw Pool Implementation
+//=============================
 LLDrawPool *LLDrawPool::createPool(const U32 type, LLViewerImage *tex0)
 {
 	LLDrawPool *poolp = NULL;
 	switch (type)
 	{
 	case POOL_SIMPLE:
-		poolp = new LLDrawPoolSimple(tex0);
+		poolp = new LLDrawPoolSimple();
 		break;
 	case POOL_ALPHA:
 		poolp = new LLDrawPoolAlpha();
 		break;
+	case POOL_ALPHA_POST_WATER:
+		poolp = new LLDrawPoolAlphaPostWater();
+		break;
 	case POOL_AVATAR:
 		poolp = new LLDrawPoolAvatar();
 		break;
 	case POOL_TREE:
 		poolp = new LLDrawPoolTree(tex0);
 		break;
-	case POOL_TREE_NEW:
-		poolp = new LLDrawPoolTreeNew(tex0);			
-		break;
 	case POOL_TERRAIN:
 		poolp = new LLDrawPoolTerrain(tex0);
 		break;
@@ -80,9 +64,6 @@ LLDrawPool *LLDrawPool::createPool(const U32 type, LLViewerImage *tex0)
 	case POOL_STARS:
 		poolp = new LLDrawPoolStars();
 		break;
-	case POOL_CLOUDS:
-		poolp = new LLDrawPoolClouds();
-		break;
 	case POOL_WATER:
 		poolp = new LLDrawPoolWater();
 		break;
@@ -90,10 +71,7 @@ LLDrawPool *LLDrawPool::createPool(const U32 type, LLViewerImage *tex0)
 		poolp = new LLDrawPoolGround();
 		break;
 	case POOL_BUMP:
-		poolp = new LLDrawPoolBump(tex0);
-		break;
-	case POOL_HUD:
-		poolp = new LLDrawPoolHUD();
+		poolp = new LLDrawPoolBump();
 		break;
 	default:
 		llerrs << "Unknown draw pool type!" << llendl;
@@ -104,183 +82,86 @@ LLDrawPool *LLDrawPool::createPool(const U32 type, LLViewerImage *tex0)
 	return poolp;
 }
 
-LLDrawPool::LLDrawPool(const U32 type, const U32 data_mask_il, const U32 data_mask_nil)
+LLDrawPool::LLDrawPool(const U32 type)
 {
-	llassert(data_mask_il & DATA_VERTICES_MASK);
-	S32 i;
 	mType = type;
 	sNumDrawPools++;
 	mId = sNumDrawPools;
-
-	mDataMaskIL = data_mask_il;
-	mDataMaskNIL = data_mask_nil;
-
-	U32 cur_mask = 0x01;
-	U32 cur_offset = 0;
-	for (i = 0; i < DATA_MAX_TYPES; i++)
-	{
-		mDataOffsets[i] = cur_offset;
-		if (cur_mask & mDataMaskIL)
-		{
-			cur_offset += sDataSizes[i];
-		}
-		cur_mask <<= 1;
-	}
-
-	mStride = cur_offset;
-
-	mCleanupUnused = FALSE;
+	mVertexShaderLevel = 0;
 	mIndicesDrawn = 0;
-	mRebuildFreq = 128 + rand() % 5;
-	mRebuildTime = 0;
-	mGeneration  = 1;
-	mSkippedVertices = 0;
-
-	resetDrawOrders();
-	resetVertexData(0);
-
-	if (gGLManager.mHasATIVAO && !gGLManager.mIsRadeon9700)
-	{
-		// ATI 8500 doesn't like indices > 15 bit.
-		mMaxVertices = DEFAULT_MAX_VERTICES/2;
-	}
-	else
-	{
-		mMaxVertices = DEFAULT_MAX_VERTICES;
-	}
+}
 
-	// JC: This must happen last, as setUseAGP reads many of the
-	// above variables.
-	mUseAGP = FALSE;
-	setUseAGP(gPipeline.usingAGP());
+LLDrawPool::~LLDrawPool()
+{
 
-	for (i=0; i<NUM_BUCKETS; i++)
-	{
-		mFreeListGeomHead[i] = -1;
-		mFreeListIndHead[i] = -1;
-	}
-	mVertexShaderLevel = 0;
 }
 
-void LLDrawPool::destroy()
+LLViewerImage *LLDrawPool::getDebugTexture()
 {
-	if (!mReferences.empty())
-	{
-		llinfos << mReferences.size() << " references left on deletion of draw pool!" << llendl;
-	}
+	return NULL;
 }
 
-
-LLDrawPool::~LLDrawPool()
+//virtual
+void LLDrawPool::beginRenderPass( S32 pass )
 {
-	destroy();
-
-	llassert( gPipeline.findPool( getType(), getTexture() ) == NULL );
 }
 
-BOOL LLDrawPool::setUseAGP(BOOL use_agp)
+//virtual
+void LLDrawPool::endRenderPass( S32 pass )
 {
-	BOOL ok = TRUE;
-	S32 vertex_count = mMemory.count() / mStride;
-	if (vertex_count > mMaxVertices && use_agp)
-	{
-#ifdef DEBUG_AGP
-		llwarns << "Allocating " << vertex_count << " vertices in pool type " << getType() << ", disabling AGP!" << llendl
-#endif
-		use_agp = FALSE;
-		ok = FALSE;
-	}
-
-	if (mUseAGP != use_agp)
-	{
-		mUseAGP = use_agp;
-
-		BOOL ok = TRUE;
-		ok &= mMemory.setUseAGP(use_agp);
-
-		if (mDataMaskNIL & DATA_VERTEX_WEIGHTS_MASK)
-		{
-			ok &= mWeights.setUseAGP(use_agp);
-		}
-		if (mDataMaskNIL & DATA_CLOTHING_WEIGHTS_MASK)
-		{
-			ok &= mClothingWeights.setUseAGP(use_agp);
-		}
-
-		if (!ok)
-		{
-			// Disable AGP if any one of these doesn't have AGP, we don't want to try
-			// mixing AGP and non-agp arrays in a single pool.
-#ifdef DEBUG_AGP
-			llinfos << "Aborting using AGP because set failed on a mem block!" << llendl;
-#endif
-			setUseAGP(FALSE);
-			ok = FALSE;
-		}
-	}
-	return ok;
+	glDisableClientState ( GL_TEXTURE_COORD_ARRAY );
+	glDisableClientState ( GL_COLOR_ARRAY );
+	glDisableClientState ( GL_NORMAL_ARRAY );
 }
 
-void LLDrawPool::flushAGP()
+U32 LLDrawPool::getTrianglesDrawn() const
 {
-	mMemory.flushAGP();
-
-	if (mDataMaskNIL & DATA_VERTEX_WEIGHTS_MASK)
-	{
-		mWeights.flushAGP();
-	}
-	if (mDataMaskNIL & DATA_CLOTHING_WEIGHTS_MASK)
-	{
-		mClothingWeights.flushAGP();
-	}
+	return mIndicesDrawn / 3;
 }
 
-void LLDrawPool::syncAGP()
+void LLDrawPool::resetTrianglesDrawn()
 {
-	if (!getVertexCount())
-	{
-		return;
-	}
-	setUseAGP(gPipeline.usingAGP());
+	mIndicesDrawn = 0;
+}
 
-	BOOL all_agp_on = TRUE;
-	mMemory.sync();
-	all_agp_on &= mMemory.isAGP();
+void LLDrawPool::addIndicesDrawn(const U32 indices)
+{
+	mIndicesDrawn += indices;
+}
 
-	if (mDataMaskNIL & DATA_VERTEX_WEIGHTS_MASK)
-	{
-		mWeights.sync();
-		all_agp_on &= mWeights.isAGP();
-	}
+//=============================
+// Face Pool Implementation
+//=============================
+LLFacePool::LLFacePool(const U32 type)
+: LLDrawPool(type)
+{
+	resetDrawOrders();
+}
 
-	if (mDataMaskNIL & DATA_CLOTHING_WEIGHTS_MASK)
-	{
-		mClothingWeights.sync();
-		all_agp_on &= mClothingWeights.isAGP();
-	}
+LLFacePool::~LLFacePool()
+{
+	destroy();
+}
 
-	// Since sometimes AGP allocation is done during syncs, we need
-	// to make sure that if AGP allocation fails, we fallback to non-agp.
-	if (mUseAGP && !all_agp_on)
+void LLFacePool::destroy()
+{
+	if (!mReferences.empty())
 	{
-#ifdef DEBUG_AGP
-		llinfos << "setUseAGP false because of AGP sync failure!" << llendl;
-#endif
-		setUseAGP(FALSE);
+		llinfos << mReferences.size() << " references left on deletion of draw pool!" << llendl;
 	}
 }
 
-void LLDrawPool::dirtyTexture(const LLViewerImage *imagep)
+void LLFacePool::dirtyTextures(const std::set<LLViewerImage*>& textures)
 {
 }
 
-BOOL LLDrawPool::moveFace(LLFace *face, LLDrawPool *poolp, BOOL copy_data)
+BOOL LLFacePool::moveFace(LLFace *face, LLDrawPool *poolp, BOOL copy_data)
 {
 	return TRUE;
 }
 
 // static
-S32 LLDrawPool::drawLoop(face_array_t& face_list, const U32* index_array)
+S32 LLFacePool::drawLoop(face_array_t& face_list)
 {
 	S32 res = 0;
 	if (!face_list.empty())
@@ -289,19 +170,15 @@ S32 LLDrawPool::drawLoop(face_array_t& face_list, const U32* index_array)
 			 iter != face_list.end(); iter++)
 		{
 			LLFace *facep = *iter;
-			if (facep->mSkipRender)
-			{
-				continue;
-			}
-			facep->enableLights();
-			res += facep->renderIndexed(index_array);
+			//facep->enableLights();
+			res += facep->renderIndexed();
 		}
 	}
 	return res;
 }
 
 // static
-S32 LLDrawPool::drawLoopSetTex(face_array_t& face_list, const U32* index_array, S32 stage)
+S32 LLFacePool::drawLoopSetTex(face_array_t& face_list, S32 stage)
 {
 	S32 res = 0;
 	if (!face_list.empty())
@@ -310,117 +187,30 @@ S32 LLDrawPool::drawLoopSetTex(face_array_t& face_list, const U32* index_array,
 			 iter != face_list.end(); iter++)
 		{
 			LLFace *facep = *iter;
-			if (facep->mSkipRender)
-			{
-				continue;
-			}
 			facep->bindTexture(stage);
 			facep->enableLights();
-			res += facep->renderIndexed(index_array);
+			res += facep->renderIndexed();
 		}
 	}
 	return res;
 }
 
-void LLDrawPool::drawLoop()
+void LLFacePool::drawLoop()
 {
-	const U32* index_array = getRawIndices();	
 	if (!mDrawFace.empty())
 	{
-		mIndicesDrawn += drawLoop(mDrawFace, index_array);
+		mIndicesDrawn += drawLoop(mDrawFace);
 	}
 }
 
-BOOL LLDrawPool::getVertexStrider(LLStrider<LLVector3> &vertices, const U32 index)
-{
-	llassert(mDataMaskIL & LLDrawPool::DATA_VERTICES_MASK);
-	vertices = (LLVector3*)(mMemory.getMem() + mDataOffsets[DATA_VERTICES] + index * mStride); 
-	vertices.setStride(mStride);
-	return TRUE;
-}
-
-BOOL LLDrawPool::getTexCoordStrider(LLStrider<LLVector2> &tex_coords, const U32 index, const U32 pass)
-{
-	llassert(mDataMaskIL & (LLDrawPool::DATA_TEX_COORDS0_MASK << pass));
-	tex_coords = (LLVector2*)(mMemory.getMem() + mDataOffsets[DATA_TEX_COORDS0 + pass] + index * mStride); 
-	tex_coords.setStride(mStride);
-	return TRUE;
-}
-
-
-BOOL LLDrawPool::getVertexWeightStrider(LLStrider<F32> &vertex_weights, const U32 index)
-{
-	llassert(mDataMaskNIL & LLDrawPool::DATA_VERTEX_WEIGHTS_MASK);
-
-	vertex_weights = &mWeights[index];
-	vertex_weights.setStride( 0 );
-	return TRUE;
-}
-
-BOOL LLDrawPool::getClothingWeightStrider(LLStrider<LLVector4> &clothing_weights, const U32 index)
-{
-	llassert(mDataMaskNIL & LLDrawPool::DATA_CLOTHING_WEIGHTS_MASK);
-
-	clothing_weights= &mClothingWeights[index];
-	clothing_weights.setStride( 0 );
-
-	return TRUE;
-}
-
-BOOL LLDrawPool::getNormalStrider(LLStrider<LLVector3> &normals, const U32 index)
-{
-	llassert((mDataMaskIL) & LLDrawPool::DATA_NORMALS_MASK);
-
-	normals = (LLVector3*)(mMemory.getMem() + mDataOffsets[DATA_NORMALS] + index * mStride); 
-
-	normals.setStride( mStride );
-
-	return TRUE;
-}
-
-
-BOOL LLDrawPool::getBinormalStrider(LLStrider<LLVector3> &binormals, const U32 index)
-{
-	llassert((mDataMaskIL) & LLDrawPool::DATA_BINORMALS_MASK);
-
-	binormals = (LLVector3*)(mMemory.getMem() + mDataOffsets[DATA_BINORMALS] + index * mStride); 
-
-	binormals.setStride( mStride );
-
-	return TRUE;
-}
-
-BOOL LLDrawPool::getColorStrider(LLStrider<LLColor4U> &colors, const U32 index)
-{
-	llassert((mDataMaskIL) & LLDrawPool::DATA_COLORS_MASK);
-
-	colors = (LLColor4U*)(mMemory.getMem() + mDataOffsets[DATA_COLORS] + index * mStride); 
-
-	colors.setStride( mStride );
-
-	return TRUE;
-}
-
-//virtual
-void LLDrawPool::beginRenderPass( S32 pass )
-{
-}
-
-//virtual
-void LLDrawPool::endRenderPass( S32 pass )
-{
-	glDisableClientState ( GL_TEXTURE_COORD_ARRAY );
-	glDisableClientState ( GL_COLOR_ARRAY );
-	glDisableClientState ( GL_NORMAL_ARRAY );
-}
-void LLDrawPool::renderFaceSelected(LLFace *facep, 
+void LLFacePool::renderFaceSelected(LLFace *facep, 
 									LLImageGL *image, 
 									const LLColor4 &color,
 									const S32 index_offset, const S32 index_count)
 {
 }
 
-void LLDrawPool::renderVisibility()
+void LLFacePool::renderVisibility()
 {
 	if (mDrawFace.empty())
 	{
@@ -506,893 +296,284 @@ void LLDrawPool::renderVisibility()
 
 }
 
-void LLDrawPool::enqueue(LLFace* facep)
+void LLFacePool::enqueue(LLFace* facep)
 {
-	if (facep->isState(LLFace::BACKLIST))
-	{
-		mMoveFace.put(facep);
-	}
-	else
-	{
-#if ENABLE_FACE_LINKING
-		facep->mSkipRender = FALSE;
-		facep->mNextFace = NULL;
-		
-		if (mDrawFace.size() > 0)
-		{
-			LLFace* last_face = mDrawFace[mDrawFace.size()-1];
-			if (match(last_face, facep))
-			{
-				last_face->link(facep);
-			}
-		}
-#endif
-		mDrawFace.put(facep);
-	}
+	mDrawFace.push_back(facep);
 }
 
-void LLDrawPool::bindGLVertexPointer()
+// virtual
+BOOL LLFacePool::addFace(LLFace *facep)
 {
-	mMemory.bindGLVertexPointer(getStride(DATA_VERTICES), mDataOffsets[DATA_VERTICES]);
+	addFaceReference(facep);
+	return TRUE;
 }
 
-void LLDrawPool::bindGLTexCoordPointer(const U32 pass)
+// virtual
+BOOL LLFacePool::removeFace(LLFace *facep)
 {
-	mMemory.bindGLTexCoordPointer(getStride(DATA_TEX_COORDS0+pass), mDataOffsets[DATA_TEX_COORDS0+pass]);
-}
+	removeFaceReference(facep);
 
-void LLDrawPool::bindGLNormalPointer()
-{
-	mMemory.bindGLNormalPointer(getStride(DATA_NORMALS), mDataOffsets[DATA_NORMALS]);
-}
+	vector_replace_with_last(mDrawFace, facep);
 
-void LLDrawPool::bindGLBinormalPointer(S32 index)
-{
-	mMemory.bindGLBinormalPointer(index, getStride(DATA_BINORMALS), mDataOffsets[DATA_BINORMALS]);
+	return TRUE;
 }
 
-void LLDrawPool::bindGLColorPointer()
+// Not absolutely sure if we should be resetting all of the chained pools as well - djs
+void LLFacePool::resetDrawOrders()
 {
-	mMemory.bindGLColorPointer(getStride(DATA_COLORS), mDataOffsets[DATA_COLORS]);
+	mDrawFace.resize(0);
 }
 
-void LLDrawPool::bindGLVertexWeightPointer(S32 index)
+LLViewerImage *LLFacePool::getTexture()
 {
-	mWeights.bindGLVertexWeightPointer(index, 0, 0);
+	return NULL;
 }
 
-void LLDrawPool::bindGLVertexClothingWeightPointer(S32 index)
+void LLFacePool::removeFaceReference(LLFace *facep)
 {
-	mClothingWeights.bindGLVertexClothingWeightPointer(index, 0, 0);
+	if (facep->getReferenceIndex() != -1)
+	{
+		if (facep->getReferenceIndex() != (S32)mReferences.size())
+		{
+			LLFace *back = mReferences.back();
+			mReferences[facep->getReferenceIndex()] = back;
+			back->setReferenceIndex(facep->getReferenceIndex());
+		}
+		mReferences.pop_back();
+	}
+	facep->setReferenceIndex(-1);
 }
 
-
-U32* LLDrawPool::getIndices(S32 index)
+void LLFacePool::addFaceReference(LLFace *facep)
 {
-	return &mIndices[index];
+	if (-1 == facep->getReferenceIndex())
+	{
+		facep->setReferenceIndex(mReferences.size());
+		mReferences.push_back(facep);
+	}
 }
 
-const LLVector3& LLDrawPool::getVertex(const S32 index)
+BOOL LLFacePool::verify() const
 {
-	llassert(mDataMaskIL & DATA_VERTICES_MASK);
-	llassert(index < mMemory.count());
-	llassert(mMemory.getMem());
-	return *(LLVector3*)(mMemory.getMem() + mDataOffsets[DATA_VERTICES] + index * mStride); 
-}
+	BOOL ok = TRUE;
+	
+	for (std::vector<LLFace*>::const_iterator iter = mDrawFace.begin();
+		 iter != mDrawFace.end(); iter++)
+	{
+		const LLFace* facep = *iter;
+		if (facep->getPool() != this)
+		{
+			llinfos << "Face in wrong pool!" << llendl;
+			facep->printDebugInfo();
+			ok = FALSE;
+		}
+		else if (!facep->verify())
+		{
+			ok = FALSE;
+		}
+	}
 
-const LLVector2& LLDrawPool::getTexCoord(const S32 index, const U32 pass)
-{
-	llassert(mDataMaskIL & (LLDrawPool::DATA_TEX_COORDS0_MASK << pass));
-	llassert(index < mMemory.count());
-	return *(LLVector2*)(mMemory.getMem() + mDataOffsets[DATA_TEX_COORDS0 + pass] + index * mStride); 
+	return ok;
 }
 
-const LLVector3& LLDrawPool::getNormal(const S32 index)
+void LLFacePool::printDebugInfo() const
 {
-	llassert(mDataMaskIL & DATA_NORMALS_MASK);
-	llassert(index < mMemory.count());
-	return *(LLVector3*)(mMemory.getMem() + mDataOffsets[DATA_NORMALS] + index * mStride); 
+	llinfos << "Pool " << this << " Type: " << getType() << llendl;
 }
 
-const LLVector3& LLDrawPool::getBinormal(const S32 index)
+BOOL LLFacePool::LLOverrideFaceColor::sOverrideFaceColor = FALSE;
+
+void LLFacePool::LLOverrideFaceColor::setColor(const LLColor4& color)
 {
-	llassert(mDataMaskIL & DATA_BINORMALS_MASK);
-	llassert(index < mMemory.count());
-	return *(LLVector3*)(mMemory.getMem() + mDataOffsets[DATA_BINORMALS] + index * mStride); 
+	if (mPool->getVertexShaderLevel() > 0 && mPool->getMaterialAttribIndex() > 0)
+	{
+		glVertexAttrib4fvARB(mPool->getMaterialAttribIndex(), color.mV);
+	}
+	else
+	{
+		glColor4fv(color.mV);
+	}
 }
 
-const LLColor4U& LLDrawPool::getColor(const S32 index)
+void LLFacePool::LLOverrideFaceColor::setColor(const LLColor4U& color)
 {
-	llassert(mDataMaskIL & DATA_COLORS_MASK);
-	llassert(index < mMemory.count());
-	return *(LLColor4U*)(mMemory.getMem() + mDataOffsets[DATA_COLORS] + index * mStride); 
+	if (mPool->getVertexShaderLevel() > 0 && mPool->getMaterialAttribIndex() > 0)
+	{
+		glVertexAttrib4ubvARB(mPool->getMaterialAttribIndex(), color.mV);
+	}
+	else
+	{
+		glColor4ubv(color.mV);
+	}
 }
 
-const F32& LLDrawPool::getVertexWeight(const S32 index)
+void LLFacePool::LLOverrideFaceColor::setColor(F32 r, F32 g, F32 b, F32 a)
 {
-	llassert(mDataMaskNIL & DATA_VERTEX_WEIGHTS_MASK);
-	llassert(index < mWeights.count());
-	llassert(mWeights.getMem());
-	return mWeights[index];
+	if (mPool->getVertexShaderLevel() > 0 && mPool->getMaterialAttribIndex() > 0)
+	{
+		glVertexAttrib4fARB(mPool->getMaterialAttribIndex(), r,g,b,a);
+	}
+	else
+	{
+		glColor4f(r,g,b,a);
+	}
 }
 
-const LLVector4& LLDrawPool::getClothingWeight(const S32 index)
+
+//=============================
+// Render Pass Implementation
+//=============================
+LLRenderPass::LLRenderPass(const U32 type)
+: LLDrawPool(type)
 {
-	llassert(mDataMaskNIL & DATA_CLOTHING_WEIGHTS_MASK);
-	llassert(index < mClothingWeights.count());
-	llassert(mClothingWeights.getMem());
-	return mClothingWeights[index];
+
 }
 
-//////////////////////////////////////////////////////////////////////////////
+LLRenderPass::~LLRenderPass()
+{
 
-#define USE_FREE_LIST 0
-#define DEBUG_FREELIST 0
+}
 
-struct tFreeListNode
+LLDrawPool* LLRenderPass::instancePool()
 {
-	U32 count;
-	S32 next;
-};
+#if LL_RELEASE_FOR_DOWNLOAD
+	llwarns << "Attempting to instance a render pass.  Invalid operation." << llendl;
+#else
+	llerrs << "Attempting to instance a render pass.  Invalid operation." << llendl;
+#endif
+	return NULL;
+}
 
-#if DEBUG_FREELIST
-static void check_list(U8 *pool, S32 stride, S32 head, S32 max)
-{
-	int count = 0;
+void LLRenderPass::renderGroup(LLSpatialGroup* group, U32 type, U32 mask, BOOL texture)
+{					
+	std::vector<LLDrawInfo*>& draw_info = group->mDrawMap[type];
 	
-	while (head >= 0)
+	for (std::vector<LLDrawInfo*>::const_iterator k = draw_info.begin(); k != draw_info.end(); ++k)
 	{
-		tFreeListNode *node = (tFreeListNode *)(pool + head*stride);
-		count++;
-		if ((count > max) || ((node->count>>20) != 0xabc) || ((node->count&0xfffff) < 2))
-			llerrs << "Bad Ind List" << llendl;
-		head = node->next;
+		LLDrawInfo& params = **k;
+		pushBatch(params, mask, texture);
 	}
 }
-#define CHECK_LIST(x) check_list##x
-#else
-#define CHECK_LIST(x)
-#endif
 
-// DEBUG!
-void LLDrawPool::CheckIntegrity()
+void LLRenderPass::renderInvisible(U32 mask)
 {
-#if DEBUG_FREELIST
-	int bucket;
-	for (bucket=0; bucket<NUM_BUCKETS; bucket++)
+#if !LL_RELEASE_FOR_DOWNLOAD
+	LLGLState::checkClientArrays(mask);
+#endif
+	
+	std::vector<LLDrawInfo*>& draw_info = gPipeline.mRenderMap[LLRenderPass::PASS_INVISIBLE];
+
+	U32* indices_pointer = NULL;
+	for (std::vector<LLDrawInfo*>::iterator i = draw_info.begin(); i != draw_info.end(); ++i)
 	{
-		CHECK_LIST(((U8 *)mMemory.getMem(), mStride, mFreeListGeomHead[bucket], mMemory.count() / mStride));
-		CHECK_LIST(((U8 *)mIndices.getMem(), 4, mFreeListIndHead[bucket], mIndices.count()));
+		LLDrawInfo& params = **i;
+		params.mVertexBuffer->setBuffer(mask);
+		indices_pointer = (U32*) params.mVertexBuffer->getIndicesPointer();
+		glDrawRangeElements(GL_TRIANGLES, params.mStart, params.mEnd, params.mCount,
+							GL_UNSIGNED_INT, indices_pointer+params.mOffset);
+		gPipeline.mTrianglesDrawn += params.mCount/3;
 	}
-#endif
 }
 
-int LLDrawPool::freeListBucket(U32 count)
+void LLRenderPass::renderTexture(U32 type, U32 mask)
 {
-	int bucket;
+#if !LL_RELEASE_FOR_DOWNLOAD
+	LLGLState::checkClientArrays(mask);
+#endif
 
-	// llassert(NUM_BUCKETS == 8)
-	
-	if (count & ~511) // >= 512
-		bucket = 7;
-	else if (count & 256) // 256-511
-		bucket = 6;
-	else if (count & 128)
-		bucket = 5;
-	else if (count & 64)
-		bucket = 4;
-	else if (count & 32)
-		bucket = 3;
-	else if (count & 16)
-		bucket = 2;
-	else if (count & 8)	 // 8-15
-		bucket = 1;
-	else 				 // 0-7
-		bucket = 0;
-	return bucket;
-}
+	std::vector<LLDrawInfo*>& draw_info = gPipeline.mRenderMap[type];
 
-void remove_node(int nodeidx, int pidx, U8 *membase, int stride, int *head)
-{
-	LLDrawPool::FreeListNode *node = (LLDrawPool::FreeListNode *)(membase + nodeidx*stride);
-	if (pidx >= 0)
-	{
-		LLDrawPool::FreeListNode *pnode = (LLDrawPool::FreeListNode *)(membase + pidx*stride);
-		pnode->next = node->next;
-	}
-	else
+	for (std::vector<LLDrawInfo*>::iterator i = draw_info.begin(); i != draw_info.end(); ++i)
 	{
-		*head = node->next;
+		LLDrawInfo& params = **i;
+		pushBatch(params, mask, TRUE);
 	}
 }
 
-void LLDrawPool::freeListAddGeom(S32 index, U32 count)
+void LLRenderPass::pushBatch(LLDrawInfo& params, U32 mask, BOOL texture)
 {
-#if USE_FREE_LIST
-	int i;
-	U8 *membase = (U8*)mMemory.getMem();
-	// See if next block or previous block is free, if so combine them
-	for (i=0; i<NUM_BUCKETS; i++)
+	if (params.mVertexBuffer.isNull())
 	{
-		int pidx = -1;
-		int nodeidx = mFreeListGeomHead[i];
-		while(nodeidx >= 0)
-		{
-			int change = 0;
-			FreeListNode *node = (FreeListNode *)(membase + nodeidx*mStride);
-			int nodecount = node->count & 0xffff;
-			// Check for prev block
-			if (nodeidx + nodecount == index)
-			{
-				remove_node(nodeidx, pidx, membase, mStride, &mFreeListGeomHead[i]);
-				// Combine nodes
-				index = nodeidx;
-				count += nodecount;
-				i = 0; // start over ; i = NUM_BUCKETS // done
-				change = 1;
-				//break;
-			}
-			// Check for next block
-			if (nodeidx == index + count)
-			{
-				remove_node(nodeidx, pidx, membase, mStride, &mFreeListGeomHead[i]);
-				// Combine nodes
-				count += nodecount;
-				i = 0; // start over ; i = NUM_BUCKETS // done
-				change = 1;
-				//break;
-			}				
-			if (change)
-				break;
-			pidx = nodeidx;
-			nodeidx = node->next;
-		}
-	}
-	// Add (extended) block to free list
-	if (count >= 2) // need 2 words to store free list (theoreticly mStride could = 4)
-	{
-		CheckIntegrity();
-		if ((index + count)*mStride >= mMemory.count())
-		{
-			mMemory.shrinkTo(index*mStride);
-		}
-		else
-		{
-			int bucket = freeListBucket(count);
-			FreeListNode *node = (FreeListNode *)(membase + index*mStride);
-			node->count = count | (0xabc<<20);
-			node->next = mFreeListGeomHead[bucket];	
-			mFreeListGeomHead[bucket] = index;
-		}
-		CheckIntegrity();
+		return;
 	}
-#endif
-}
 
-void LLDrawPool::freeListAddInd(S32 index, U32 count)
-{
-#if USE_FREE_LIST
-	int i;
-	const U32 *membase = mIndices.getMem();
-	// See if next block or previous block is free, if so combine them
-	for (i=0; i<NUM_BUCKETS; i++)
+	if (texture)
 	{
-		int pidx = -1;
-		int nodeidx = mFreeListIndHead[i];
-		while(nodeidx >= 0)
+		if (params.mTexture.notNull())
 		{
-			int change = 0;
-			FreeListNode *node = (FreeListNode *)(membase + nodeidx);
-			int nodecount = node->count & 0xffff;
-			// Check for prev block
-			if (nodeidx + nodecount == index)
-			{
-				remove_node(nodeidx, pidx, (U8*)membase, 4, &mFreeListIndHead[i]);
-				// Combine nodes
-				index = nodeidx;
-				count += nodecount;
-				i = 0; // start over ; i = NUM_BUCKETS // done
-				change = 1;
-				//break;
-			}
-			// Check for next block
-			if (nodeidx == index + count)
+			params.mTexture->bind();
+			if (params.mTextureMatrix)
 			{
-				remove_node(nodeidx, pidx, (U8*)membase, 4, &mFreeListIndHead[i]);
-				// Combine nodes
-				count += nodecount;
-				i = 0; // start over ; i = NUM_BUCKETS // done
-				change = 1;
-				//break;
+				glMatrixMode(GL_TEXTURE);
+				glLoadMatrixf((GLfloat*) params.mTextureMatrix->mMatrix);
 			}
-			if (change)
-				break;
-			pidx = nodeidx;
-			nodeidx = node->next;
-		}
-	}
-	// Add (extended) block to free list
-	if (count >= 2) // need 2 words to store free list
-	{
-		CheckIntegrity();
-		if (index + count >= mIndices.count())
-		{
-			mIndices.shrinkTo(index);
+			params.mTexture->addTextureStats(params.mVSize);
 		}
 		else
 		{
-			int bucket = freeListBucket(count);
-			FreeListNode *node = (FreeListNode *)(membase + index);
-			node->count = count | (0xabc<<20);
-			node->next = mFreeListIndHead[bucket];
-			mFreeListIndHead[bucket] = index;
-		}
-		CheckIntegrity();
-	}
-#endif
-}
-
-S32 LLDrawPool::freeListFindGeom(U32 count)
-{
-#if USE_FREE_LIST
-	int i, nodeidx, pidx;
-	int firstbucket = freeListBucket(count);
-	U8 *membase = (U8*)mMemory.getMem();
-	for (i=firstbucket; i<NUM_BUCKETS; i++)
-	{
-		pidx = -1;
-		nodeidx = mFreeListGeomHead[i];
-		CHECK_LIST(((U8 *)mMemory.getMem(), mStride, mFreeListGeomHead[i], mMemory.count() / mStride));
-		while(nodeidx >= 0)
-		{
-			FreeListNode *node = (FreeListNode *)(membase + nodeidx*mStride);
-			int nodecount = node->count & 0xffff;
-			llassert((node->count>>20) == 0xabc);
-			if (nodecount >= count)
-			{
-				remove_node(nodeidx, pidx, membase, mStride, &mFreeListGeomHead[i]);
-#if 1
-				if (nodecount > count)
-				{
-					int leftover = nodecount - count;
-					freeListAddGeom(nodeidx + count, leftover);
-				}
-#endif
-				return nodeidx;
-			}
-			pidx = nodeidx;
-			nodeidx = node->next;
-		}
-	}
-#endif // USE_FREE_LIST
-	return -1;
-}
-
-S32 LLDrawPool::freeListFindInd(U32 count)
-{
-#if USE_FREE_LIST
-	int i, nodeidx, pidx;
-	int firstbucket = freeListBucket(count);
-	U32 *membase = (U32 *)mIndices.getMem();
-	for (i=firstbucket; i<NUM_BUCKETS; i++)
-	{
-		pidx = -1;
-		nodeidx = mFreeListIndHead[i];
-		CHECK_LIST(((U8 *)mIndices.getMem(), 4, mFreeListIndHead[i], mIndices.count()));
-		while(nodeidx >= 0)
-		{
-			FreeListNode *node = (FreeListNode *)(membase + nodeidx);
-			int nodecount = node->count & 0xffff;
-			llassert((node->count>>20) == 0xabc);
-			if (nodecount >= count)
-			{
-				remove_node(nodeidx, pidx, (U8*)membase, 4, &mFreeListIndHead[i]);
-#if 1
-				if (nodecount > count)
-				{
-					int leftover = nodecount - count;
-					freeListAddInd(nodeidx + count, leftover);
-				}
-#endif
-				return nodeidx;
-			}
-			pidx = nodeidx;
-			nodeidx = node->next;
+			LLImageGL::unbindTexture(0);
 		}
 	}
-#endif // USE_FREE_LIST
-	return -1;
-}
-
-//////////////////////////////////////////////////////////////////////////////
-
-S32 LLDrawPool::reserveGeom(const U32 geom_count)
-{
-	LLFastTimer t(LLFastTimer::FTM_GEO_RESERVE);
 	
-	S32 index;
-	index = freeListFindGeom(geom_count);
-	if (index < 0)
-	{
-		index = mMemory.count() / mStride;
-		if (!geom_count)
-		{
-			llwarns << "Attempting to reserve zero bytes!" << llendl;
-			return index;
-		}
-
-		S32 bytes = geom_count * mStride;
-
-		if ((index + (S32)geom_count) > (S32)mMaxVertices)
-		{
-			//
-			// Various drivers have issues with the number of indices being greater than a certain number.
-			// if you're using AGP.  Disable AGP if we've got more vertices than in the pool.
-			//
-#ifdef DEBUG_AGP
-			llinfos << "setUseAGP false because of large vertex count in reserveGeom" << llendl;
-#endif
-			setUseAGP(FALSE);
-		}
-
-		mMemory.reserve_block(bytes);
-		if (mDataMaskNIL & DATA_VERTEX_WEIGHTS_MASK)
-		{
-			mWeights.reserve_block(geom_count);
-		}
-		if (mDataMaskNIL & DATA_CLOTHING_WEIGHTS_MASK)
-		{
-			mClothingWeights.reserve_block(geom_count);
-		}
-	}
-	CHECK_LIST(((U8 *)mMemory.getMem(), mStride, mFreeListGeomHead[0], mMemory.count() / mStride));
-	return index;
-}
-
-S32 LLDrawPool::reserveInd(U32 indCount)
-{
-	S32 index;
-	index = freeListFindInd(indCount);
-	if (index < 0)
-	{
-		index = mIndices.count();
-
-		if (indCount)
-		{
-			mIndices.reserve_block(indCount);
-		}
-	}
-	for (U32 i=0;i<indCount;i++)
-	{
-		mIndices[index+i]=0;
-	}
-	CHECK_LIST(((U8 *)mIndices.getMem(), 4, mFreeListIndHead[0], mIndices.count()));
-	return index;
-}
-
-S32 LLDrawPool::unReserveGeom(const S32 index, const U32 count)
-{
-	if (index < 0 || count == 0)
-		return -1;
+	params.mVertexBuffer->setBuffer(mask);
+	U32* indices_pointer = (U32*) params.mVertexBuffer->getIndicesPointer();
+	glDrawRangeElements(GL_TRIANGLES, params.mStart, params.mEnd, params.mCount,
+						GL_UNSIGNED_INT, indices_pointer+params.mOffset);
+	gPipeline.mTrianglesDrawn += params.mCount/3;
 
-	freeListAddGeom(index, count);
-	
-#if 0
-	int i;
-	S32 bytes,words;
-	U32 *memp;
-	// Fill mem with bad data (for testing only)
-	bytes = count * mStride;
-	bytes -= sizeof(FreeListNode);
-	memp = (U32*)(mMemory.getMem() + index * mStride);	
-	memp += sizeof(FreeListNode)>>2;
-	words = bytes >> 2;
-	for (i=0; i<words; i++)
-		*memp++ = 0xffffffff;
-
-	words = count; //  (sizeof each array is a word)
-	if (mDataMaskNIL & DATA_VERTEX_WEIGHTS_MASK)
-	{
-		memp = (U32*)(&mWeights[index]);
-		for (i=0; i<words; i++)
-			*memp++ = 0xffffffff;
-	}
-	if (mDataMaskNIL & DATA_CLOTHING_WEIGHTS_MASK)
+	if (params.mTextureMatrix && texture && params.mTexture.notNull())
 	{
-		memp = (U32*)(&mClothingWeights[index]);
-		for (i=0; i<count; i++)
-			*memp++ = 0xffffffff;
+		glLoadIdentity();
+		glMatrixMode(GL_MODELVIEW);
 	}
-#endif
-	return -1;
 }
 
-S32 LLDrawPool::unReserveInd(const S32 index, const U32 count)
+void LLRenderPass::renderActive(U32 type, U32 mask, BOOL texture)
 {
-	if (index < 0 || count == 0)
-		return -1;
-
-	freeListAddInd(index, count);
-	
-#if 0
-	int i;
-	U32 *memp = &mIndices[index];
-	for (i=0; i<count; i++)
-		*memp++ = 0xffffffff;
+#if !LL_RELEASE_FOR_DOWNLOAD
+	LLGLState::checkClientArrays(mask);
 #endif
-	return -1;
-}
-
-//////////////////////////////////////////////////////////////////////////////
-
-const U32 LLDrawPool::getIndexCount() const
-{
-	return mIndices.count();
-}
-
-const U32 LLDrawPool::getVertexCount() const
-{
-	return mMemory.count() / mStride;
-}
-
-const U32 LLDrawPool::getTexCoordCount(U32 pass) const
-{
-	return mMemory.count() / mStride;
-}
-
-
-const U32 LLDrawPool::getNormalCount() const
-{
-	return mMemory.count() / mStride;
-}
-
-
-const U32 LLDrawPool::getBinormalCount() const
-{
-	return mMemory.count() / mStride;
-}
-
-const U32 LLDrawPool::getColorCount() const
-{
-	return mMemory.count() / mStride;
-}
-
-const U32 LLDrawPool::getVertexWeightCount() const
-{
-	return mWeights.count();
-}
-
-// virtual
-BOOL LLDrawPool::addFace(LLFace *facep)
-{
-	addFaceReference(facep);
-	return TRUE;
-}
-
-// virtual
-BOOL LLDrawPool::removeFace(LLFace *facep)
-{
-	removeFaceReference(facep);
-
-	vector_replace_with_last(mDrawFace, facep);
-
-	facep->unReserve();
-	
-	return TRUE;
-}
-
-// Not absolutely sure if we should be resetting all of the chained pools as well - djs
-void LLDrawPool::resetDrawOrders()
-{
-	mDrawFace.resize(0);
-}
 
-void LLDrawPool::resetIndices(S32 indices_count)
-{
-	mIndices.reset(indices_count);
-	for (S32 i=0; i<NUM_BUCKETS; i++)
-		mFreeListIndHead[i] = -1;
-}
-
-void LLDrawPool::resetVertexData(S32 reserve_count)
-{
-	mMemory.reset(reserve_count*mStride);
-		
-	for (S32 i=0; i<NUM_BUCKETS; i++)
-	{
-		mFreeListGeomHead[i] = -1;
-	}
-	if (mDataMaskNIL & DATA_VERTEX_WEIGHTS_MASK)
-	{
-		mWeights.reset(reserve_count);
-	}
-
-	if (mDataMaskNIL & DATA_CLOTHING_WEIGHTS_MASK)
-	{
-		mClothingWeights.reset(reserve_count);
-	}
-}
-
-void LLDrawPool::resetAll()
-{
-	resetDrawOrders();
-	resetVertexData(0);
-	mGeneration++;
-
-}
-
-S32 LLDrawPool::rebuild()
-{
-	mRebuildTime++;
-
-	BOOL needs_rebuild = FALSE;
-	S32 rebuild_cost = 0;
+	LLSpatialBridge* last_bridge = NULL;
+	glPushMatrix();
 	
-	if (mUseAGP)
+	for (LLSpatialGroup::sg_vector_t::iterator i = gPipeline.mActiveGroups.begin(); i != gPipeline.mActiveGroups.end(); ++i)
 	{
-		if (getVertexCount() > 0.75f*DEFAULT_MAX_VERTICES)
+		LLSpatialGroup* group = *i;
+		if (!group->isDead() &&
+			gPipeline.hasRenderType(group->mSpatialPartition->mDrawableType) &&
+			group->mDrawMap.find(type) != group->mDrawMap.end())
 		{
-			if (mRebuildTime > 8)
-			{
-				needs_rebuild = TRUE;
-			}
-#ifdef DEBUG_AGP
-			llwarns << "More than " << DEFAULT_MAX_VERTICES << " in pool type " << (S32)mType << " at rebuild!" << llendl;
-#endif
-		}
-	}
-	
-	// rebuild de-allocates 'stale' objects, so we still need to do a rebuild periodically
-	if (mRebuildFreq > 0 && mRebuildTime >= mRebuildFreq)
-	{
-		needs_rebuild = TRUE;
-	}
-
-	if (needs_rebuild) 
-	{
-		mGeneration++;
-
-		if (mReferences.empty())
-		{
-			resetIndices(0);
-			resetVertexData(0);
-		}
-		else
-		{
-			for (std::vector<LLFace*>::iterator iter = mReferences.begin();
-				 iter != mReferences.end(); iter++)
-			{
-				LLFace *facep = *iter;
-				if (facep->hasGeometry() && !facep->isState(LLFace::BACKLIST | LLFace::SHARED_GEOM))
-				{
-					facep->backup();
-				}
-			}
-			S32 tot_verts = 0;
-			S32 tot_indices = 0;
-			for (std::vector<LLFace*>::iterator iter = mDrawFace.begin();
-				 iter != mDrawFace.end(); iter++)
+			LLSpatialBridge* bridge = (LLSpatialBridge*) group->mSpatialPartition;
+			if (bridge != last_bridge)
 			{
-				LLFace *facep = *iter;
-				if (facep->isState(LLFace::BACKLIST))
-				{
-					tot_verts += facep->getGeomCount();
-					tot_indices += facep->getIndicesCount();
-				}
+				glPopMatrix();
+				glPushMatrix();
+				glMultMatrixf((F32*) bridge->mDrawable->getRenderMatrix().mMatrix);
+				last_bridge = bridge;
 			}
-			for (std::vector<LLFace*>::iterator iter = mMoveFace.begin();
-				 iter != mMoveFace.end(); iter++)
-			{
-				LLFace *facep = *iter;
-				if (facep->isState(LLFace::BACKLIST))
-				{
-					tot_verts += facep->getGeomCount();
-					tot_indices += facep->getIndicesCount();
-				}
-			}
-			
-			resetIndices(tot_indices);
-			flushAGP();
-			resetVertexData(tot_verts);
-
-			for (std::vector<LLFace*>::iterator iter = mDrawFace.begin();
-				 iter != mDrawFace.end(); iter++)
-			{
-				LLFace *facep = *iter;
-				llassert(facep->getPool() == this);
-				facep->restore();
-			}
-		}
-		mRebuildTime = 0;
-		setDirty();
-	}
-
-	if (!mMoveFace.empty())
-	{
-		for (std::vector<LLFace*>::iterator iter = mMoveFace.begin();
-			 iter != mMoveFace.end(); iter++)
-		{
-			LLFace *facep = *iter;
-			facep->restore();
-			enqueue(facep);
-		}
-		setDirty();
-		mMoveFace.reset();
-		rebuild_cost++;
-	}
-	return rebuild_cost;
-}
-
-LLViewerImage *LLDrawPool::getTexture()
-{
-	return NULL;
-}
-
-LLViewerImage *LLDrawPool::getDebugTexture()
-{
-	return NULL;
-}
 
-void LLDrawPool::removeFaceReference(LLFace *facep)
-{
-	if (facep->getReferenceIndex() != -1)
-	{
-		if (facep->getReferenceIndex() != (S32)mReferences.size())
-		{
-			LLFace *back = mReferences.back();
-			mReferences[facep->getReferenceIndex()] = back;
-			back->setReferenceIndex(facep->getReferenceIndex());
+			renderGroup(group,type,mask,texture);
 		}
-		mReferences.pop_back();
-	}
-	facep->setReferenceIndex(-1);
-}
-
-void LLDrawPool::addFaceReference(LLFace *facep)
-{
-	if (-1 == facep->getReferenceIndex())
-	{
-		facep->setReferenceIndex(mReferences.size());
-		mReferences.push_back(facep);
 	}
+	
+	glPopMatrix();
 }
 
-U32 LLDrawPool::getTrianglesDrawn() const
-{
-	return mIndicesDrawn / 3;
-}
-
-void LLDrawPool::resetTrianglesDrawn()
-{
-	mIndicesDrawn = 0;
-}
-
-void LLDrawPool::addIndicesDrawn(const U32 indices)
-{
-	mIndicesDrawn += indices;
-}
-
-BOOL LLDrawPool::verify() const
+void LLRenderPass::renderStatic(U32 type, U32 mask, BOOL texture)
 {
-	BOOL ok = TRUE;
-	// Verify all indices in the pool are in the right range
-	const U32 *indicesp = getRawIndices();
-	for (U32 i = 0; i < getIndexCount(); i++)
-	{
-		if (indicesp[i] > getVertexCount())
-		{
-			ok = FALSE;
-			llinfos << "Bad index in tree pool!" << llendl;
-		}
-	}
+#if !LL_RELEASE_FOR_DOWNLOAD
+	LLGLState::checkClientArrays(mask);
+#endif
 
-	for (std::vector<LLFace*>::const_iterator iter = mDrawFace.begin();
-		 iter != mDrawFace.end(); iter++)
+	for (LLSpatialGroup::sg_vector_t::iterator i = gPipeline.mVisibleGroups.begin(); i != gPipeline.mVisibleGroups.end(); ++i)
 	{
-		const LLFace* facep = *iter;
-		if (facep->getPool() != this)
+		LLSpatialGroup* group = *i;
+		if (!group->isDead() &&
+			gPipeline.hasRenderType(group->mSpatialPartition->mDrawableType) &&
+			group->mDrawMap.find(type) != group->mDrawMap.end())
 		{
-			llinfos << "Face in wrong pool!" << llendl;
-			facep->printDebugInfo();
-			ok = FALSE;
-		}
-		else if (!facep->verify())
-		{
-			ok = FALSE;
+			renderGroup(group,type,mask,texture);
 		}
 	}
-
-	return ok;
-}
-
-void LLDrawPool::printDebugInfo() const
-{
-	llinfos << "Pool " << this << " Type: " << getType() << llendl;
-	llinfos << "--------------------" << llendl;
-	llinfos << "Vertex count: "  << getVertexCount() << llendl;
-	llinfos << "Normal count: "  << getNormalCount() << llendl; 
-	llinfos << "Indices count: " << getIndexCount() << llendl;
-	llinfos << llendl;
-}
-
-
-S32 LLDrawPool::getMemUsage(const BOOL print)
-{
-	S32 mem_usage = 0;
-
-	mem_usage += sizeof(this);
-
-	// Usage beyond the pipeline allocated data (color and mMemory)
-	mem_usage += mIndices.getMax() * sizeof(U32);
-	mem_usage += mDrawFace.capacity() * sizeof(LLFace *);
-	mem_usage += mMoveFace.capacity() * sizeof(LLFace *);
-	mem_usage += mReferences.capacity() * sizeof(LLFace *);
-
-	mem_usage += mMemory.getSysMemUsage();
-	mem_usage += mWeights.getSysMemUsage();
-	mem_usage += mClothingWeights.getSysMemUsage();
-
-	return mem_usage;
-}
-
-LLColor3 LLDrawPool::getDebugColor() const
-{
-	return LLColor3(0.f, 0.f, 0.f);
-}
-
-void LLDrawPool::setDirty()
-{ 
-	mMemory.setDirty(); 
-	mWeights.setDirty();
-	mClothingWeights.setDirty();
 }
-
-BOOL LLDrawPool::LLOverrideFaceColor::sOverrideFaceColor = FALSE;
-
-void LLDrawPool::LLOverrideFaceColor::setColor(const LLColor4& color)
-{
-	if (mPool->mVertexShaderLevel > 0 && mPool->getMaterialAttribIndex() > 0)
-	{
-		glVertexAttrib4fvARB(mPool->getMaterialAttribIndex(), color.mV);
-	}
-	else
-	{
-		glColor4fv(color.mV);
-	}
-}
-
-void LLDrawPool::LLOverrideFaceColor::setColor(const LLColor4U& color)
-{
-	if (mPool->mVertexShaderLevel > 0 && mPool->getMaterialAttribIndex() > 0)
-	{
-		glVertexAttrib4ubvARB(mPool->getMaterialAttribIndex(), color.mV);
-	}
-	else
-	{
-		glColor4ubv(color.mV);
-	}
-}
-
-void LLDrawPool::LLOverrideFaceColor::setColor(F32 r, F32 g, F32 b, F32 a)
-{
-	if (mPool->mVertexShaderLevel > 0 && mPool->getMaterialAttribIndex() > 0)
-	{
-		glVertexAttrib4fARB(mPool->getMaterialAttribIndex(), r,g,b,a);
-	}
-	else
-	{
-		glColor4f(r,g,b,a);
-	}
-}
-
-// virtual
-void LLDrawPool::enableShade()
-{ }
-
-// virtual
-void LLDrawPool::disableShade()
-{ }
-
-// virtual
-void LLDrawPool::setShade(F32 shade)
-{ }
diff --git a/indra/newview/lldrawpool.h b/indra/newview/lldrawpool.h
index c00cbf14e4a38eb8a9081175286007ab47f305b7..007c0a2de3f3083a1255ed80bfaf3085a75f7e73 100644
--- a/indra/newview/lldrawpool.h
+++ b/indra/newview/lldrawpool.h
@@ -9,270 +9,167 @@
 #ifndef LL_LLDRAWPOOL_H
 #define LL_LLDRAWPOOL_H
 
-#include "llagparray.h"
-#include "lldarray.h"
-#include "lldlinked.h"
-#include "llstrider.h"
-#include "llviewerimage.h"
 #include "v4coloru.h"
 #include "v2math.h"
 #include "v3math.h"
-#include "llstrider.h"
+#include "llvertexbuffer.h"
 
 class LLFace;
 class LLImageGL;
 class LLViewerImage;
+class LLSpatialGroup;
+class LLDrawInfo;
 
 #define DEFAULT_MAX_VERTICES 65535
 
 class LLDrawPool
 {
 public:
-	typedef LLDynamicArray<LLFace*, 128> face_array_t;
-	
+	static S32 sNumDrawPools;
+
 	enum
 	{
-		SHADER_LEVEL_SCATTERING = 2
+		// Correspond to LLPipeline render type
+		POOL_SKY = 1,
+		POOL_STARS,
+		POOL_GROUND,
+		POOL_TERRAIN,	
+		POOL_SIMPLE,
+		POOL_BUMP,
+		POOL_AVATAR,
+		POOL_TREE,
+		POOL_ALPHA,
+		POOL_WATER,
+		POOL_ALPHA_POST_WATER,
+		NUM_POOL_TYPES,
 	};
-
-public:
-	LLDrawPool(const U32 type, const U32 data_mask_il, const U32 data_mask_nil); 
+	
+	LLDrawPool(const U32 type);
 	virtual ~LLDrawPool();
 
-	static LLDrawPool* createPool(const U32 type, LLViewerImage *tex0 = NULL);
+	virtual BOOL isDead() = 0;
 
-	void flushAGP(); // Flush the AGP buffers so they can be repacked and reallocated.
-	void syncAGP();
+	S32 getId() const { return mId; }
+	U32 getType() const { return mType; }
 
-	virtual LLDrawPool *instancePool() = 0;	// Create an empty new instance of the pool.
+	virtual LLViewerImage *getDebugTexture();
 	virtual void beginRenderPass( S32 pass );
 	virtual void endRenderPass( S32 pass );
 	virtual S32	 getNumPasses() { return 1; }
 	virtual void render(S32 pass = 0) = 0;
+	virtual void prerender() = 0;
+	virtual S32 getMaterialAttribIndex() = 0;
+	virtual U32 getVertexDataMask() = 0;
+	virtual BOOL verify() const { return TRUE; }		// Verify that all data in the draw pool is correct!
+	virtual S32 getVertexShaderLevel() const { return mVertexShaderLevel; }
+	
+	static LLDrawPool* createPool(const U32 type, LLViewerImage *tex0 = NULL);
+	virtual LLDrawPool *instancePool() = 0;	// Create an empty new instance of the pool.
+	virtual LLViewerImage* getTexture() = 0;
+	virtual BOOL isFacePool() { return FALSE; }
+	virtual void resetDrawOrders() = 0;
+
+	U32	getTrianglesDrawn() const;
+	void resetTrianglesDrawn();
+	void addIndicesDrawn(const U32 indices);
+
+protected:
+	S32 mVertexShaderLevel;
+	S32	mId;
+	U32 mType;				// Type of draw pool
+	S32	mIndicesDrawn;
+};
+
+class LLRenderPass : public LLDrawPool
+{
+public:
+	enum
+	{
+		PASS_SIMPLE = NUM_POOL_TYPES,
+		PASS_FULLBRIGHT,
+		PASS_INVISIBLE,
+		PASS_SHINY,
+		PASS_BUMP,
+		PASS_GRASS,
+		PASS_ALPHA,
+		NUM_RENDER_TYPES,
+	};
+
+	LLRenderPass(const U32 type);
+	virtual ~LLRenderPass();
+	/*virtual*/ LLDrawPool* instancePool();
+	/*vritual*/ S32 getMaterialAttribIndex() { return -1; }
+	/*virtual*/ LLViewerImage* getDebugTexture() { return NULL; }
+	LLViewerImage* getTexture() { return NULL; }
+	BOOL isDead() { return FALSE; }
+	void resetDrawOrders() { }
+
+	virtual void pushBatch(LLDrawInfo& params, U32 mask, BOOL texture);
+	virtual void renderGroup(LLSpatialGroup* group, U32 type, U32 mask, BOOL texture = TRUE);
+	virtual void renderStatic(U32 type, U32 mask, BOOL texture = TRUE);
+	virtual void renderActive(U32 type, U32 mask, BOOL texture = TRUE);
+	virtual void renderInvisible(U32 mask);
+	virtual void renderTexture(U32 type, U32 mask);
+
+};
+
+class LLFacePool : public LLDrawPool
+{
+public:
+	typedef std::vector<LLFace*> face_array_t;
+	
+	enum
+	{
+		SHADER_LEVEL_SCATTERING = 2
+	};
+
+public:
+	LLFacePool(const U32 type);
+	virtual ~LLFacePool();
+	
 	virtual void renderForSelect() = 0;
-	virtual BOOL match(LLFace* last_face, LLFace* facep) { return FALSE; }
+	BOOL isDead() { return mReferences.empty(); }
 	virtual void renderFaceSelected(LLFace *facep, LLImageGL *image, const LLColor4 &color,
 									const S32 index_offset = 0, const S32 index_count = 0);
 
-	virtual void prerender() = 0;
-	virtual S32 rebuild();
-
-	virtual S32 getMaterialAttribIndex() = 0;
-
 	virtual LLViewerImage *getTexture();
-	virtual LLViewerImage *getDebugTexture();
-	virtual void dirtyTexture(const LLViewerImage* texturep);
+	virtual void dirtyTextures(const std::set<LLViewerImage*>& textures);
 
 	virtual void enqueue(LLFace *face);
 	virtual BOOL addFace(LLFace *face);
 	virtual BOOL removeFace(LLFace *face);
 
 	virtual BOOL verify() const;		// Verify that all data in the draw pool is correct!
-	virtual LLColor3 getDebugColor() const; // For AGP debug display
-
+	
 	virtual void resetDrawOrders();
-	virtual void resetVertexData(S32 reserve_count);
-	virtual void resetIndices(S32 num_indices);
 	void resetAll();
 
 	BOOL moveFace(LLFace *face, LLDrawPool *poolp, BOOL copy_data = FALSE);
 
-
-	S32 getId() const { return mId; }
-	U32 getType() const { return mType; }
-
-	const U32 getStride() const;
-	inline const U32 getStride(const U32 data_type) const;
-	inline const U32 getOffset(const U32 data_type) const;
-
-	S32	reserveGeom(U32 count);
-	S32	reserveInd (U32 count);
-	S32 unReserveGeom(const S32 index, const U32 count);
-	S32 unReserveInd(const S32 index, const U32 count);
-
-	void bindGLVertexPointer();
-	void bindGLTexCoordPointer(const U32 pass=0);
-	void bindGLNormalPointer();
-	void bindGLBinormalPointer(S32 index);
-	void bindGLColorPointer();
-	void bindGLVertexWeightPointer(S32 index);
-	void bindGLVertexClothingWeightPointer(S32 index);
-
-	const U32  getIndexCount() const;
-	const U32  getTexCoordCount(const U32 pass=0) const;
-	const U32  getVertexCount() const;
-	const U32  getNormalCount() const;
-	const U32  getBinormalCount() const;
-	const U32  getColorCount() const;
-	const U32  getVertexWeightCount() const;
-
-	void  setDirty();
-	void  setDirtyMemory()						{ mMemory.setDirty(); }
-	void  setDirtyWeights()						{ mWeights.setDirty(); }
-
-	const U32* getRawIndices() const			{ return mIndices.getMem(); }
-
-	U32 getIndex(const S32 index)				{ return mIndices[index]; } // Use to get one index
-	U32 *getIndices(const S32 index); 			// Used to get an array of indices for reading/writing
-	void CheckIntegrity(); // DEBUG
-	
-	const LLVector3& getVertex(const S32 index);
-	const LLVector2& getTexCoord(const S32 index, const U32 pass);
-	const LLVector3& getNormal(const S32 index);
-	const LLVector3& getBinormal(const S32 index);
-	const LLColor4U& getColor(const S32 index);
-	const F32& getVertexWeight(const S32 index);
-	const LLVector4& getClothingWeight(const S32 index);
-
-	void setRebuild(const BOOL rebuild);
-
 	void destroy();
 
 	void buildEdges();
 
-	static S32 drawLoop(face_array_t& face_list, const U32* index_array);
-	static S32 drawLoopSetTex(face_array_t& face_list, const U32* index_array, S32 stage);
+	static S32 drawLoop(face_array_t& face_list);
+	static S32 drawLoopSetTex(face_array_t& face_list, S32 stage);
 	void drawLoop();
 
 	void renderVisibility();
 
 	void addFaceReference(LLFace *facep);
 	void removeFaceReference(LLFace *facep);
-	U32	getTrianglesDrawn() const;
-	void resetTrianglesDrawn();
-	void addIndicesDrawn(const U32 indices);
 
 	void printDebugInfo() const;
-	S32 getMemUsage(const BOOL print = FALSE);
 	
-	BOOL setUseAGP(BOOL use_agp);
-	BOOL canUseAGP() const		{ return mMemory.isAGP(); } // Return TRUE if this pool can use AGP
+	BOOL isFacePool() { return TRUE; }
 
-	S32 getMaxVertices() const	{ return mMaxVertices; }
-	S32 getVertexShaderLevel() const { return mVertexShaderLevel; }
-	
 	friend class LLFace;
 	friend class LLPipeline;
 public:
-
-	enum
-	{
-		// Correspond to LLPipeline render type
-		POOL_SKY = 1,
-		POOL_STARS,
-		POOL_GROUND,
-		POOL_TERRAIN,	
-		POOL_SIMPLE,
-		POOL_MEDIA,		// unused
-		POOL_BUMP,
-		POOL_AVATAR,
-		POOL_TREE,
-		POOL_TREE_NEW,
-		POOL_WATER,		
-		POOL_CLOUDS,
-		POOL_ALPHA,
-		POOL_HUD,
-	};
-
-
-	// If you change the order or add params to these, you also need to adjust the sizes in the
-	// mDataSizes array defined in lldrawpool.cpp
-	typedef enum e_data_type
-	{
-		DATA_VERTICES		= 0,
-		DATA_TEX_COORDS0	= 1,
-		DATA_TEX_COORDS1	= 2,
-		DATA_TEX_COORDS2	= 3,
-		DATA_TEX_COORDS3	= 4,
-		DATA_NORMALS		= 5,
-		DATA_VERTEX_WEIGHTS = 6,
-		DATA_CLOTHING_WEIGHTS = 7,
-		DATA_BINORMALS		= 8,
-		DATA_COLORS			= 9,
-		DATA_MAX_TYPES		= 10
-	} EDataType;
-
-	typedef enum e_data_mask
-	{
-		DATA_VERTICES_MASK			= 1 << DATA_VERTICES,
-		DATA_TEX_COORDS0_MASK		= 1 << DATA_TEX_COORDS0,
-		DATA_TEX_COORDS1_MASK		= 1 << DATA_TEX_COORDS1,
-		DATA_TEX_COORDS2_MASK		= 1 << DATA_TEX_COORDS2,
-		DATA_TEX_COORDS3_MASK		= 1 << DATA_TEX_COORDS3,
-		DATA_NORMALS_MASK			= 1 << DATA_NORMALS,
-		DATA_VERTEX_WEIGHTS_MASK	= 1 << DATA_VERTEX_WEIGHTS,
-		DATA_CLOTHING_WEIGHTS_MASK	= 1 << DATA_CLOTHING_WEIGHTS,
-		DATA_BINORMALS_MASK			= 1 << DATA_BINORMALS,
-		DATA_COLORS_MASK			= 1 << DATA_COLORS,
-
-		// Masks for standard types.
-		// IL for interleaved, NIL for non-interleaved.
-		DATA_SIMPLE_IL_MASK				= DATA_VERTICES_MASK | DATA_TEX_COORDS0_MASK | DATA_NORMALS_MASK,
-		DATA_SIMPLE_NIL_MASK			= 0,
-		DATA_BUMP_IL_MASK				= DATA_SIMPLE_IL_MASK | DATA_BINORMALS_MASK | DATA_TEX_COORDS1_MASK,
-	} EDataMask;
-
 	face_array_t	mDrawFace;
 	face_array_t	mMoveFace;
 	face_array_t	mReferences;
 
-	U32 mDataMaskIL;	// Interleaved data
-	U32 mDataMaskNIL;	// Non-interleaved data
-	U32 mDataOffsets[DATA_MAX_TYPES];
-	S32 mStride;
-
-	S32	mRebuildFreq;
-	S32	mRebuildTime;
-	S32	mGeneration;
-
-	
-	S32 mSkippedVertices;
-
-	static U32 sDataSizes[DATA_MAX_TYPES];
-	static S32 sNumDrawPools;
-
-protected:
-	LLAGPArray<U8>          mMemory;
-	LLAGPArray<F32>			mWeights;
-	LLAGPArray<LLVector4>	mClothingWeights;
-	LLAGPArray<U32>         mIndices;
-
-public:
-
-	BOOL getVertexStrider      (LLStrider<LLVector3> &vertices,   const U32 index = 0);
-	BOOL getTexCoordStrider    (LLStrider<LLVector2> &tex_coords, const U32 index = 0, const U32 pass=0);
-	BOOL getNormalStrider      (LLStrider<LLVector3> &normals,    const U32 index = 0);
-	BOOL getBinormalStrider    (LLStrider<LLVector3> &binormals,  const U32 index = 0);
-	BOOL getColorStrider   	   (LLStrider<LLColor4U> &colors,  const U32 index = 0);
-	BOOL getVertexWeightStrider(LLStrider<F32>       &vertex_weights, const U32 index = 0);
-	BOOL getClothingWeightStrider(LLStrider<LLVector4>       &clothing_weights, const U32 index = 0);
-
-public:
-	enum { NUM_BUCKETS = 8 };	// Need to change freeListBucket() if NUM_BUCKETS changes
-	struct FreeListNode
-	{
-		U32 count;
-		S32 next;
-	};
-protected:
-	int freeListBucket(U32 count);
-	void freeListAddGeom(S32 index, U32 count);
-	void freeListAddInd(S32 index, U32 count);
-	S32 freeListFindGeom(U32 count);
-	S32 freeListFindInd(U32 count);
-	
-protected:
-	BOOL mUseAGP;
-	S32 mVertexShaderLevel;
-	S32	mId;
-	U32 mType;				// Type of draw pool
-	S32 mMaxVertices;
-	S32	mIndicesDrawn;
-	BOOL mCleanupUnused; // Cleanup unused data when too full
-
-	S32 mFreeListGeomHead[8];
-	S32 mFreeListIndHead[8];
-
 public:
 	class LLOverrideFaceColor
 	{
@@ -311,38 +208,6 @@ public:
 		LLDrawPool* mPool;
 		static BOOL sOverrideFaceColor;
 	};
-
-	virtual void enableShade();
-	virtual void disableShade();
-	virtual void setShade(F32 shade);
-	
 };
 
-inline const U32 LLDrawPool::getStride() const
-{
-	return mStride;
-}
-
-inline const U32 LLDrawPool::getOffset(const U32 data_type) const
-{
-	return  mDataOffsets[data_type];
-}
-
-inline const U32 LLDrawPool::getStride(const U32 data_type) const
-{
-	if (mDataMaskIL & (1 << data_type))
-	{
-		return mStride;
-	}
-	else if (mDataMaskNIL & (1 << data_type))
-	{
-		return 0;
-	}
-	else
-	{
-		llerrs << "Getting stride for unsupported data type " << data_type << llendl;
-		return 0;
-	}
-}
-
 #endif //LL_LLDRAWPOOL_H
diff --git a/indra/newview/lldrawpoolalpha.cpp b/indra/newview/lldrawpoolalpha.cpp
index 8c520f663857da25ca0bdf7c80385f151e0e2e9f..5cb914c37e73f85eee1245b5017d1fd516f83675 100644
--- a/indra/newview/lldrawpoolalpha.cpp
+++ b/indra/newview/lldrawpoolalpha.cpp
@@ -10,11 +10,11 @@
 
 #include "lldrawpoolalpha.h"
 
+#include "llglheaders.h"
 #include "llviewercontrol.h"
 #include "llcriticaldamp.h"
 #include "llfasttimer.h"
 
-#include "llagparray.h"
 #include "llcubemap.h"
 #include "llsky.h"
 #include "llagent.h"
@@ -25,104 +25,25 @@
 #include "llviewerobjectlist.h" // For debugging
 #include "llviewerwindow.h"
 #include "pipeline.h"
-
-const F32 MAX_DIST = 512.f;
-const F32 ALPHA_FALLOFF_START_DISTANCE = 0.8f;
+#include "llviewerregion.h"
 
 BOOL LLDrawPoolAlpha::sShowDebugAlpha = FALSE;
 
-LLDrawPoolAlpha::LLDrawPoolAlpha() :
-	LLDrawPool(POOL_ALPHA,
-			   DATA_SIMPLE_IL_MASK | DATA_COLORS_MASK,
-			   DATA_SIMPLE_NIL_MASK)
+LLDrawPoolAlpha::LLDrawPoolAlpha(U32 type) :
+	LLRenderPass(type)
 {
-	mRebuiltLastFrame = FALSE;
-	mMinDistance = 0.f;
-	mMaxDistance = MAX_DIST;
-	mInvBinSize = NUM_ALPHA_BINS/(mMaxDistance - mMinDistance);
-	mCleanupUnused = TRUE;
-	//mRebuildFreq = -1 ; // Only rebuild if nearly full
-
-//	for (S32 i = 0; i < NUM_ALPHA_BINS; i++)
-//	{
-//		mDistanceBins[i].realloc(200);
-//	}
-}
 
-LLDrawPoolAlpha::~LLDrawPoolAlpha()
-{
 }
 
-LLDrawPool *LLDrawPoolAlpha::instancePool()
+LLDrawPoolAlphaPostWater::LLDrawPoolAlphaPostWater()
+: LLDrawPoolAlpha(POOL_ALPHA_POST_WATER)
 {
-	llerrs << "Should never be calling instancePool on an alpha pool!" << llendl;
-	return NULL;
 }
 
-void LLDrawPoolAlpha::enqueue(LLFace *facep)
+LLDrawPoolAlpha::~LLDrawPoolAlpha()
 {
-	if (!facep->isState(LLFace::GLOBAL))
-	{
-		facep->mCenterAgent = facep->mCenterLocal * facep->getRenderMatrix();
-	}
-	facep->mDistance = (facep->mCenterAgent - gCamera->getOrigin()) * gCamera->getAtAxis();
-	
-	if (facep->isState(LLFace::BACKLIST))
-	{
-		mMoveFace.put(facep);
-	}
-	else
-	{
-		mDrawFace.put(facep);
-	}
-
-	{
-		S32 dist_bin = lltrunc( (mMaxDistance - (facep->mDistance+32))*mInvBinSize );
-
-		if (dist_bin >= NUM_ALPHA_BINS)
-		{
-			mDistanceBins[NUM_ALPHA_BINS-1].put(facep);
-			//mDistanceBins[NUM_ALPHA_BINS-1].push(facep, (U32)(void*)facep->getTexture());
-		}
-		else if (dist_bin > 0)
-		{
-			mDistanceBins[dist_bin].put(facep);
-			//mDistanceBins[dist_bin].push(facep, (U32)(void*)facep->getTexture());
-		}
-		else
-		{
-			mDistanceBins[0].put(facep);
-			//mDistanceBins[0].push(facep, (U32)(void*)facep->getTexture());
-		}
-	}
 }
 
-BOOL LLDrawPoolAlpha::removeFace(LLFace *facep)
-{
-	BOOL removed = FALSE;
-
-	LLDrawPool::removeFace(facep);
-
-	{
-		for (S32 i = 0; i < NUM_ALPHA_BINS; i++)
-		{
-			if (mDistanceBins[i].removeObj(facep) != -1)
-			{
-				if (removed)
-				{
-					llerrs << "Warning! " << "Face in multiple distance bins on removal" << llendl;
-				}
-				removed = TRUE;
-			}
-		}
-	}
-	if (removed)
-	{
-		return TRUE;
-	}
-
-	return FALSE;
-}
 
 void LLDrawPoolAlpha::prerender()
 {
@@ -131,442 +52,270 @@ void LLDrawPoolAlpha::prerender()
 
 void LLDrawPoolAlpha::beginRenderPass(S32 pass)
 {
-	if (mDrawFace.empty())
-	{
-		// No alpha objects, early exit.
-		return;
-	}
-
 	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
-	glEnableClientState(GL_VERTEX_ARRAY);
 	glEnableClientState(GL_NORMAL_ARRAY);
-	if (gPipeline.getLightingDetail() >= 2)
-	{
-		glEnableClientState(GL_COLOR_ARRAY);
-	}
+	glEnableClientState(GL_COLOR_ARRAY);
 }
 
-
-void LLDrawPoolAlpha::render(S32 pass)
+void setup_clip_plane(BOOL pre_water)
 {
-	LLFastTimer t(LLFastTimer::FTM_RENDER_ALPHA);
+	F32 height = gAgent.getRegion()->getWaterHeight();
+	BOOL above = gCamera->getOrigin().mV[2] > height ? TRUE : FALSE;
 	
-	if (mDrawFace.empty())
-	{
-		// No alpha objects, early exit.
-		return;
-	}
-
-	GLfloat shiny[4] =
-	{
-		0.00f,
-		0.25f,
-		0.5f,
-		0.75f
-	};
-
-	GLint specularIndex = (mVertexShaderLevel > 0) ? 
-		gPipeline.mObjectAlphaProgram.mAttribute[LLPipeline::GLSL_SPECULAR_COLOR] : 0;
+	F64 plane[4];
+	
+	plane[0] = 0;
+	plane[1] = 0;
+	plane[2] = above == pre_water ? -1.0 : 1.0;
+	plane[3] = -plane[2] * height;
+	
+	glClipPlane(GL_CLIP_PLANE0, plane);
+}
 
-	S32 diffTex = 0;
-	S32 envTex = -1;
+void LLDrawPoolAlphaPostWater::render(S32 pass)
+{
+    LLFastTimer t(LLFastTimer::FTM_RENDER_ALPHA);
 
-	if (mVertexShaderLevel > 0) //alpha pass uses same shader as shiny/bump
+	if (gPipeline.hasRenderType(LLDrawPool::POOL_ALPHA))
 	{
-		envTex = gPipeline.mObjectAlphaProgram.enableTexture(LLPipeline::GLSL_ENVIRONMENT_MAP, GL_TEXTURE_CUBE_MAP_ARB);
-		LLCubeMap* cube_map = gSky.mVOSkyp->getCubeMap();
-		if (envTex >= 0 && cube_map)
-		{
-			cube_map->bind();
-			cube_map->setMatrix(1);
-		}
-		
-		if (specularIndex > 0)
-		{
-			glVertexAttrib4fARB(specularIndex, 0, 0, 0, 0);
-		}
-		
-		S32 scatterTex = gPipeline.mObjectAlphaProgram.enableTexture(LLPipeline::GLSL_SCATTER_MAP);
-		LLViewerImage::bindTexture(gSky.mVOSkyp->getScatterMap(), scatterTex);
-
-		diffTex = gPipeline.mObjectAlphaProgram.enableTexture(LLPipeline::GLSL_DIFFUSE_MAP);
+		LLGLEnable clip(GL_CLIP_PLANE0);
+		setup_clip_plane(FALSE);
+		LLDrawPoolAlpha::render(gPipeline.mAlphaGroupsPostWater);
 	}
-
-	bindGLVertexPointer();
-	bindGLTexCoordPointer();
-	bindGLNormalPointer();
-	if (gPipeline.getLightingDetail() >= 2)
+	else
 	{
-		bindGLColorPointer();
+		LLDrawPoolAlpha::render(gPipeline.mAlphaGroupsPostWater);
 	}
+}
 
-	S32 i, j;
-	glAlphaFunc(GL_GREATER,0.01f);
-	// This needs to be turned off or there will be lots of artifacting with the clouds - djs
-	LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+void LLDrawPoolAlpha::render(S32 pass)
+{
+	LLFastTimer t(LLFastTimer::FTM_RENDER_ALPHA);
+	
+	LLGLEnable clip(GL_CLIP_PLANE0);
+	setup_clip_plane(TRUE);
+	render(gPipeline.mAlphaGroups);
+}
 
+void LLDrawPoolAlpha::render(std::vector<LLSpatialGroup*>& groups)
+{
+	LLGLDepthTest gls_depth(GL_TRUE);
 	LLGLSPipelineAlpha gls_pipeline_alpha;
 
-	LLDynamicArray<LLFace*>* distance_bins;
-	distance_bins = mDistanceBins;
+	gPipeline.enableLightsDynamic(1.f);
+	renderAlpha(getVertexDataMask(), groups);
 
-	S32 num_bins_no_alpha_test = ((gPickAlphaThreshold != 0.f) && gUsePickAlpha) ? 
-		(NUM_ALPHA_BINS - llmax(2, (S32)(ALPHA_FALLOFF_START_DISTANCE * mInvBinSize))) : 
-		NUM_ALPHA_BINS;
-
-	typedef std::vector<LLFace*> face_list_t;
-
-	for (i = 0; i < num_bins_no_alpha_test; i++)
+	if (sShowDebugAlpha)
 	{
-		S32 obj_count = distance_bins[i].count();
-
-		if (!obj_count)
-		{
-			continue;
-		}
-		else if (i > (NUM_ALPHA_BINS / 2) && obj_count < 100)
-		{
-			face_list_t pri_queue;
-			pri_queue.reserve(distance_bins[i].count());
-			for (j = 0; j < distance_bins[i].count(); j++)
-			{
-				pri_queue.push_back(distance_bins[i][j]);
-			}
-			std::sort(pri_queue.begin(), pri_queue.end(), LLFace::CompareDistanceGreater());
-			
-			for (face_list_t::iterator iter = pri_queue.begin(); iter != pri_queue.end(); iter++)
-			{
-				const LLFace &face = *(*iter);
-				face.enableLights();
-				face.bindTexture(diffTex);
-				if ((mVertexShaderLevel > 0) && face.getTextureEntry() && specularIndex > 0)
-				{
-					U8 s = face.getTextureEntry()->getShiny();
-					glVertexAttrib4fARB(specularIndex, shiny[s], shiny[s], shiny[s], shiny[s]);
-				}
-				face.renderIndexed(getRawIndices());
-				mIndicesDrawn += face.getIndicesCount();
-			}
-		}
-		else
-		{
-			S32 count = distance_bins[i].count();
-			for (j = 0; j < count; j++)
-			{
-				const LLFace &face = *distance_bins[i][j];
-				face.enableLights();
-				face.bindTexture(diffTex);
-				if ((mVertexShaderLevel > 0) && face.getTextureEntry() && specularIndex > 0)
-				{
-					U8 s = face.getTextureEntry()->getShiny();
-					glVertexAttrib4fARB(specularIndex, shiny[s], shiny[s], shiny[s], shiny[s]);
-				}
-				face.renderIndexed(getRawIndices());
-				mIndicesDrawn += face.getIndicesCount();
-			}
-		}
+		glDisableClientState(GL_NORMAL_ARRAY);
+		glDisableClientState(GL_COLOR_ARRAY);
+		gPipeline.enableLightsFullbright(LLColor4(1,1,1,1));
+		glColor4f(1,0,0,1);
+		LLViewerImage::sSmokeImagep->addTextureStats(1024.f*1024.f);
+        LLViewerImage::sSmokeImagep->bind();
+		renderAlphaHighlight(LLVertexBuffer::MAP_VERTEX |
+							LLVertexBuffer::MAP_TEXCOORD, groups);
 	}
+}
 
-	GLfloat			ogl_matrix[16];
-	gCamera->getOpenGLTransform(ogl_matrix);
+void LLDrawPoolAlpha::renderAlpha(U32 mask, std::vector<LLSpatialGroup*>& groups)
+{
+#if !LL_RELEASE_FOR_DOWNLOAD
+	LLGLState::checkClientArrays(mask);
+#endif
 
-	for (i = num_bins_no_alpha_test; i < NUM_ALPHA_BINS; i++)
-	{
-		BOOL use_pri_queue = distance_bins[i].count() < 100;
+	LLSpatialBridge* last_bridge = NULL;
+	LLSpatialPartition* last_part = NULL;
+	glPushMatrix();
+	LLGLDepthTest depth(GL_TRUE, GL_FALSE);
 
-		face_list_t pri_queue;
-		
-		if (use_pri_queue)
-		{
-			pri_queue.reserve(distance_bins[i].count());
-			for (j = 0; j < distance_bins[i].count(); j++)
-			{
-				pri_queue.push_back(distance_bins[i][j]);
-			}
-			std::sort(pri_queue.begin(), pri_queue.end(), LLFace::CompareDistanceGreater());
-		}
-
-		S32 count = distance_bins[i].count();
-		for (j = 0; j < count; j++)
+	for (std::vector<LLSpatialGroup*>::iterator i = groups.begin(); i != groups.end(); ++i)
+	{
+		LLSpatialGroup* group = *i;
+		if (group->mSpatialPartition->mRenderByGroup &&
+			!group->isDead())
 		{
-			const LLFace &face = use_pri_queue ? *pri_queue[j] : *distance_bins[i][j];
-			F32 fade_value = face.mAlphaFade * gPickAlphaThreshold;
-
-			face.enableLights();
-			
-			if (fade_value < 1.f)
+			LLSpatialPartition* part = group->mSpatialPartition;
+			if (part != last_part)
 			{
+				LLSpatialBridge* bridge = part->asBridge();
+				if (bridge != last_bridge)
 				{
-					LLGLDepthTest gls_depth(GL_TRUE, GL_TRUE);
-					glAlphaFunc(GL_LESS, fade_value);
-					glBlendFunc(GL_ZERO, GL_ONE);
-					LLViewerImage::bindTexture(gPipeline.mAlphaSizzleImagep, diffTex);
-					LLVector4 s_params(ogl_matrix[2], ogl_matrix[6], ogl_matrix[10], ogl_matrix[14]);
-					LLVector4 t_params(ogl_matrix[1], ogl_matrix[5], ogl_matrix[9], ogl_matrix[13]);
-
-					LLGLEnable gls_texgen_s(GL_TEXTURE_GEN_S);
-					LLGLEnable gls_texgen_t(GL_TEXTURE_GEN_T);
-					glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
-					glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
-					glTexGenfv(GL_S, GL_OBJECT_PLANE, s_params.mV);
-					glTexGenfv(GL_T, GL_OBJECT_PLANE, t_params.mV);
-					if ((mVertexShaderLevel > 0) && face.getTextureEntry() && specularIndex > 0)
+					glPopMatrix();
+					glPushMatrix();
+					if (bridge)
 					{
-						U8 s = face.getTextureEntry()->getShiny();
-						glVertexAttrib4fARB(specularIndex, shiny[s], shiny[s], shiny[s], shiny[s]);
+						glMultMatrixf((F32*) bridge->mDrawable->getRenderMatrix().mMatrix);
 					}
-					face.renderIndexed(getRawIndices());
+					last_bridge = bridge;
 				}
 
-				{
-					// should get GL_GREATER to work, as it's faster
-					LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE, GL_LESS);
-					glAlphaFunc(GL_GEQUAL, fade_value);
-					glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-					face.bindTexture(diffTex);
-					if ((mVertexShaderLevel > 0) && face.getTextureEntry() && specularIndex > 0)
-					{
-						U8 s = face.getTextureEntry()->getShiny();
-						glVertexAttrib4fARB(specularIndex, shiny[s], shiny[s], shiny[s], shiny[s]);
-					}
-					face.renderIndexed(getRawIndices());
-				}
+//				if (!last_part || part->mDepthMask != last_part->mDepthMask)
+//				{
+//					glDepthMask(part->mDepthMask);
+//				}
+				last_part = part;
 			}
 
-			// render opaque portion of actual texture
-			glAlphaFunc(GL_GREATER, 0.98f);
-
-			face.bindTexture(diffTex);
-			face.renderIndexed(getRawIndices());
-
-			glAlphaFunc(GL_GREATER, 0.01f);
-
-			mIndicesDrawn += face.getIndicesCount();
+			renderGroupAlpha(group,LLRenderPass::PASS_ALPHA,mask,TRUE);
 		}
 	}
+	
+	glPopMatrix();
+}
 
-	if (mVertexShaderLevel > 0) //single pass shader driven shiny/bump
-	{
-		gPipeline.mObjectAlphaProgram.disableTexture(LLPipeline::GLSL_ENVIRONMENT_MAP, GL_TEXTURE_CUBE_MAP_ARB);
-		LLCubeMap* cube_map = gSky.mVOSkyp->getCubeMap();
-		if (envTex >= 0 && cube_map)
-		{
-			cube_map->restoreMatrix();
-		}
-		gPipeline.mObjectAlphaProgram.disableTexture(LLPipeline::GLSL_SCATTER_MAP);
-		gPipeline.mObjectAlphaProgram.disableTexture(LLPipeline::GLSL_DIFFUSE_MAP);
-
-		glClientActiveTextureARB(GL_TEXTURE0_ARB);
-		glActiveTextureARB(GL_TEXTURE0_ARB);
-		glEnable(GL_TEXTURE_2D);
-	}
+void LLDrawPoolAlpha::renderAlphaHighlight(U32 mask, std::vector<LLSpatialGroup*>& groups)
+{
+#if !LL_RELEASE_FOR_DOWNLOAD
+	LLGLState::checkClientArrays(mask);
+#endif
 
-	if (sShowDebugAlpha)
+	LLSpatialBridge* last_bridge = NULL;
+	LLSpatialPartition* last_part = NULL;
+	glPushMatrix();
+	
+	for (std::vector<LLSpatialGroup*>::iterator i = groups.begin(); i != groups.end(); ++i)
 	{
-		gPipeline.disableLights();
-		if ((mVertexShaderLevel > 0))
+		LLSpatialGroup* group = *i;
+		if (group->mSpatialPartition->mRenderByGroup &&
+			!group->isDead())
 		{
-			gPipeline.mHighlightProgram.bind();
-		}
-		
-		LLViewerImage::sSmokeImagep->bind();
-		LLOverrideFaceColor override_color(this, 1.f, 0.f, 0.f, 1.f);
-		glColor4f(1.f, 0.f, 0.f, 1.f); // in case vertex shaders are enabled
-		glDisableClientState(GL_COLOR_ARRAY);
-		
-		for (S32 i = 0; i < NUM_ALPHA_BINS; i++)
-		{
-			if (distance_bins[i].count() < 100)
+			LLSpatialPartition* part = group->mSpatialPartition;
+			if (part != last_part)
 			{
-				face_list_t pri_queue;
-				pri_queue.reserve(distance_bins[i].count());
-				for (j = 0; j < distance_bins[i].count(); j++)
-				{
-					pri_queue.push_back(distance_bins[i][j]);
-				}
-				std::sort(pri_queue.begin(), pri_queue.end(), LLFace::CompareDistanceGreater());
-
-				for (face_list_t::iterator iter = pri_queue.begin(); iter != pri_queue.end(); iter++)
+				LLSpatialBridge* bridge = part->asBridge();
+				if (bridge != last_bridge)
 				{
-					const LLFace &face = *(*iter);
-					face.renderIndexed(getRawIndices());
-					mIndicesDrawn += face.getIndicesCount();
+					glPopMatrix();
+					glPushMatrix();
+					if (bridge)
+					{
+						glMultMatrixf((F32*) bridge->mDrawable->getRenderMatrix().mMatrix);
+					}
+					last_bridge = bridge;
 				}
+				
+				last_part = part;
 			}
-			else
+
+			std::vector<LLDrawInfo*>& draw_info = group->mDrawMap[LLRenderPass::PASS_ALPHA];
+
+			for (std::vector<LLDrawInfo*>::const_iterator k = draw_info.begin(); k != draw_info.end(); ++k)
 			{
-				for (j = 0; j < distance_bins[i].count(); j++)
+				LLDrawInfo& params = **k;
+				
+				if (params.mParticle)
 				{
-					const LLFace &face = *distance_bins[i][j];
-					face.renderIndexed(getRawIndices());
-					mIndicesDrawn += face.getIndicesCount();
+					continue;
 				}
+				params.mVertexBuffer->setBuffer(mask);
+				U32* indices_pointer = (U32*) params.mVertexBuffer->getIndicesPointer();
+				glDrawRangeElements(GL_TRIANGLES, params.mStart, params.mEnd, params.mCount,
+									GL_UNSIGNED_INT, indices_pointer+params.mOffset);
+				
+				addIndicesDrawn(params.mCount);
 			}
 		}
-
-		if ((mVertexShaderLevel > 0))
-		{
-			gPipeline.mHighlightProgram.unbind();
-		}
-
 	}
-
+	glPopMatrix();
 }
 
-void LLDrawPoolAlpha::renderForSelect()
-{
-	if (mDrawFace.empty() || !mMemory.count())
-	{
-		return;
-	}
-
-	// force faces on focus object to proper alpha cutoff based on object bbox distance
-	if (gAgent.getFocusObject())
-	{
-		LLDrawable* drawablep = gAgent.getFocusObject()->mDrawable;
-
-		if (drawablep)
-		{
-			const S32 num_faces = drawablep->getNumFaces();
-
-			for (S32 f = 0; f < num_faces; f++)
-			{
-				LLFace* facep = drawablep->getFace(f);
-				facep->mDistance = gAgent.getFocusObjectDist();
-			}
-		}
-	}
+void LLDrawPoolAlpha::renderGroupAlpha(LLSpatialGroup* group, U32 type, U32 mask, BOOL texture)
+{					
+	BOOL light_enabled = TRUE;
 
-	glEnableClientState (GL_VERTEX_ARRAY);
-	glEnableClientState (GL_TEXTURE_COORD_ARRAY);
+	std::vector<LLDrawInfo*>& draw_info = group->mDrawMap[type];
+	
+	U32 prim_type = GL_TRIANGLES;
 
-	LLGLSObjectSelectAlpha gls_alpha;
+	//F32 width = (F32) gViewerWindow->getWindowDisplayWidth();
 
-	glBlendFunc(GL_ONE, GL_ZERO);
-	if (gPickTransparent)
+	//F32 view = gCamera->getView();
+	
+	if (group->mSpatialPartition->mDrawableType == LLPipeline::RENDER_TYPE_CLOUDS)
 	{
-		glAlphaFunc(GL_GEQUAL, 0.f);
+		glAlphaFunc(GL_GREATER, 0.f);
 	}
 	else
 	{
-		glAlphaFunc(GL_GEQUAL, gPickAlphaThreshold);
+		glAlphaFunc(GL_GREATER, 0.01f);
 	}
 
-	bindGLVertexPointer();
-	bindGLTexCoordPointer();
-
-	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,		GL_COMBINE_ARB);
-	glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB,		GL_REPLACE);
-	glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB,		GL_MODULATE);
-
-	glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB,		GL_PREVIOUS);
-	glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB,		GL_SRC_COLOR);
-
-	glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB,		GL_TEXTURE);
-	glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB,	GL_SRC_ALPHA);
-
-	glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA_ARB,		GL_PRIMARY_COLOR_ARB);
-	glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA_ARB,	GL_SRC_ALPHA);
+	/*LLGLEnable point_sprite(GL_POINT_SPRITE_ARB);
 
-	LLDynamicArray<LLFace*>* distance_bins;
-	distance_bins = mDistanceBins;
-
-	S32 i;
-	S32 j;
+	if (gGLManager.mHasPointParameters)
+	{
+		glTexEnvi(GL_POINT_SPRITE_ARB, GL_COORD_REPLACE_ARB, TRUE);
+		glPointParameterfARB(GL_POINT_SIZE_MIN_ARB, 0.f);
+		glPointParameterfARB(GL_POINT_SIZE_MAX_ARB, width*16.f);
+		glPointSize(width/(view*view));
+	}*/
 
-	for (i = 0; i < NUM_ALPHA_BINS; i++)
+	for (std::vector<LLDrawInfo*>::const_iterator k = draw_info.begin(); k != draw_info.end(); ++k)
 	{
-		S32 distance_bin_size = distance_bins[i].count();
-		if (distance_bin_size)
+		LLDrawInfo& params = **k;
+		if (texture && params.mTexture.notNull())
 		{
-			for (j = 0; j < distance_bin_size; j++)
+			params.mTexture->bind();
+			params.mTexture->addTextureStats(params.mVSize);
+			if (params.mTextureMatrix)
 			{
-				const LLFace &face = *distance_bins[i][j];
-
-				if (face.getDrawable() && !face.getDrawable()->isDead() && (face.getViewerObject()->mGLName))
-				{
-					face.bindTexture();
-					face.renderForSelect();
-				}
+				glMatrixMode(GL_TEXTURE);
+				glLoadMatrixf((GLfloat*) params.mTextureMatrix->mMatrix);
 			}
 		}
-	}
-
-	glAlphaFunc(GL_GREATER, 0.01f);
-	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
-	glDisableClientState (GL_TEXTURE_COORD_ARRAY);
-}
-
-
-void LLDrawPoolAlpha::renderFaceSelected(LLFace *facep, 
-									LLImageGL *image, 
-									const LLColor4 &color,
-									const S32 index_offset, const S32 index_count)
-{
-	facep->renderSelected(image, color, index_offset, index_count);
-}
-
-
-void LLDrawPoolAlpha::resetDrawOrders()
-{
-	LLDrawPool::resetDrawOrders();
-
-	for (S32 i = 0; i < NUM_ALPHA_BINS; i++)
-	{
-		mDistanceBins[i].resize(0);
-	}
-}
-
-BOOL LLDrawPoolAlpha::verify() const
-{
-	S32 i, j;
-	BOOL ok;
-	ok = LLDrawPool::verify();
-	for (i = 0; i < NUM_ALPHA_BINS; i++)
-	{
-		for (j = 0; j < mDistanceBins[i].count(); j++)
+		
+		if (params.mFullbright)
 		{
-			const LLFace &face = *mDistanceBins[i][j];
-			if (!face.verify())
+			if (light_enabled)
 			{
-				ok = FALSE;
+				gPipeline.enableLightsFullbright(LLColor4(1,1,1,1));
+				light_enabled = FALSE;
 			}
 		}
-	}
-	return ok;
-}
-
-LLViewerImage *LLDrawPoolAlpha::getDebugTexture()
-{
-	return LLViewerImage::sSmokeImagep;
-}
+		else if (!light_enabled)
+		{
+			gPipeline.enableLightsDynamic(1.f);
+			light_enabled = TRUE;
+		}
 
+		/*if (params.mParticle)
+		{
+			F32 size = params.mPartSize;
+			size *= size;
+			float param[] = { 0, 0, 0.01f/size*view*view };
+			prim_type = GL_POINTS;
+			glPointParameterfvARB(GL_POINT_DISTANCE_ATTENUATION_ARB, param);
+		}
+		else*/
+		{
+			prim_type = GL_TRIANGLES;
+		}
 
-LLColor3 LLDrawPoolAlpha::getDebugColor() const
-{
-	return LLColor3(1.f, 0.f, 0.f);
-}
+		params.mVertexBuffer->setBuffer(mask);
+		U32* indices_pointer = (U32*) params.mVertexBuffer->getIndicesPointer();
+		glDrawRangeElements(prim_type, params.mStart, params.mEnd, params.mCount,
+							GL_UNSIGNED_INT, indices_pointer+params.mOffset);
+		
+		addIndicesDrawn(params.mCount);
 
-S32 LLDrawPoolAlpha::getMaterialAttribIndex()
-{
-	return gPipeline.mObjectAlphaProgram.mAttribute[LLPipeline::GLSL_MATERIAL_COLOR];
-}
+		if (params.mTextureMatrix && texture && params.mTexture.notNull())
+		{
+			glLoadIdentity();
+			glMatrixMode(GL_MODELVIEW);
+		}
+	}
 
-// virtual
-void LLDrawPoolAlpha::enableShade()
-{
-	glDisableClientState(GL_COLOR_ARRAY);
-}
+	if (!light_enabled)
+	{
+		gPipeline.enableLightsDynamic(1.f);
+	}
 
-// virtual
-void LLDrawPoolAlpha::disableShade()
-{
-	glEnableClientState(GL_COLOR_ARRAY);
-}
+	/*glPointSize(1.f);
 
-// virtual
-void LLDrawPoolAlpha::setShade(F32 shade)
-{
-	glColor4f(0,0,0,shade);
-}
+	if (gGLManager.mHasPointParameters)
+	{
+		float param[] = {1, 0, 0 };
+		glPointParameterfvARB(GL_POINT_DISTANCE_ATTENUATION_ARB, param);
+	}*/
+}	
diff --git a/indra/newview/lldrawpoolalpha.h b/indra/newview/lldrawpoolalpha.h
index 6f24959e5065b987ab15e467ff28fa9e74a2752d..e33bbacd822f5c71f1aeda3c6b4b7c33c8a2a570 100644
--- a/indra/newview/lldrawpoolalpha.h
+++ b/indra/newview/lldrawpoolalpha.h
@@ -10,55 +10,43 @@
 #define LL_LLDRAWPOOLALPHA_H
 
 #include "lldrawpool.h"
-#include "llviewerimage.h"
 #include "llframetimer.h"
 
 class LLFace;
 class LLColor4;
 
-class LLDrawPoolAlpha: public LLDrawPool
+class LLDrawPoolAlpha: public LLRenderPass
 {
 public:
-	LLDrawPoolAlpha();
-	/*virtual*/ ~LLDrawPoolAlpha();
-
-	/*virtual*/ LLDrawPool *instancePool();
-
-	/*virtual*/ void beginRenderPass(S32 pass = 0);
-	/*virtual*/ void render(S32 pass = 0);
-	/*virtual*/ void renderFaceSelected(LLFace *facep, LLImageGL *image, const LLColor4 &color,
-										const S32 index_offset = 0, const S32 index_count = 0);
-	/*virtual*/ void prerender();
-	/*virtual*/ void renderForSelect();
-
-	/*virtual*/ void enqueue(LLFace *face);
-	/*virtual*/ BOOL removeFace(LLFace *face);
-	/*virtual*/ void resetDrawOrders();
-
-	/*virtual*/ void enableShade();
-	/*virtual*/ void disableShade();
-	/*virtual*/ void setShade(F32 shade);
-
-
-	virtual S32 getMaterialAttribIndex();
-
-	BOOL mRebuiltLastFrame;
 	enum
 	{
-		NUM_ALPHA_BINS = 1024
+		VERTEX_DATA_MASK =	LLVertexBuffer::MAP_VERTEX |
+							LLVertexBuffer::MAP_NORMAL |
+							LLVertexBuffer::MAP_COLOR |
+							LLVertexBuffer::MAP_TEXCOORD
 	};
+	virtual U32 getVertexDataMask() { return VERTEX_DATA_MASK; }
+
+	LLDrawPoolAlpha(U32 type = LLDrawPool::POOL_ALPHA);
+	/*virtual*/ ~LLDrawPoolAlpha();
 
-	/*virtual*/ BOOL verify() const;
-	/*virtual*/ LLViewerImage *getDebugTexture();
-	/*virtual*/ LLColor3 getDebugColor() const; // For AGP debug display
+	/*virtual*/ void beginRenderPass(S32 pass = 0);
+	virtual void render(S32 pass = 0);
+	void render(std::vector<LLSpatialGroup*>& groups);
+	/*virtual*/ void prerender();
 
+	void renderGroupAlpha(LLSpatialGroup* group, U32 type, U32 mask, BOOL texture = TRUE);
+	void renderAlpha(U32 mask, std::vector<LLSpatialGroup*>& groups);
+	void renderAlphaHighlight(U32 mask, std::vector<LLSpatialGroup*>& groups);
+	
 	static BOOL sShowDebugAlpha;
-protected:
-	F32 mMinDistance;
-	F32 mMaxDistance;
-	F32 mInvBinSize;
+};
 
-	LLDynamicArray<LLFace*> mDistanceBins[NUM_ALPHA_BINS];
+class LLDrawPoolAlphaPostWater : public LLDrawPoolAlpha
+{
+public:
+	LLDrawPoolAlphaPostWater();
+	virtual void render(S32 pass = 0);
 };
 
 #endif // LL_LLDRAWPOOLALPHA_H
diff --git a/indra/newview/lldrawpoolavatar.cpp b/indra/newview/lldrawpoolavatar.cpp
index dfe75084b57532d14b4ae0e7aa3efc23f49493d8..9b9825deffe86f535bfa2c8f0df72ba5bef899b0 100644
--- a/indra/newview/lldrawpoolavatar.cpp
+++ b/indra/newview/lldrawpoolavatar.cpp
@@ -13,7 +13,6 @@
 #include "llvoavatar.h"
 #include "m3math.h"
 
-#include "llagparray.h"
 #include "llagent.h"
 #include "lldrawable.h"
 #include "llface.h"
@@ -23,6 +22,11 @@
 #include "noise.h"
 #include "pipeline.h"
 
+static U32 sDataMask = LLDrawPoolAvatar::VERTEX_DATA_MASK;
+static U32 sBufferUsage = GL_STREAM_DRAW_ARB;
+static U32 sShaderLevel = 0;
+static LLGLSLShader* sVertexProgram = NULL;
+
 extern F32 gFrameDTClamped;
 extern BOOL gUseGLPick;
 
@@ -56,53 +60,13 @@ S32 AVATAR_VERTEX_BYTES = 48;
 
 
 BOOL gAvatarEmbossBumpMap = FALSE;
+static BOOL sRenderingSkinned = FALSE;
 
 LLDrawPoolAvatar::LLDrawPoolAvatar() :
-LLDrawPool(POOL_AVATAR, 
-		   DATA_SIMPLE_IL_MASK, 
-		   DATA_VERTEX_WEIGHTS_MASK | DATA_CLOTHING_WEIGHTS_MASK )
+LLFacePool(POOL_AVATAR)
 {
-	mCleanupUnused = FALSE;
-
-	// Overide the data layout
-	mDataMaskIL = 0;
-	mStride = 0;
-	for (S32 i = 0; i < DATA_MAX_TYPES; i++)
-	{
-		mDataOffsets[i] = 0;
-	}
-
-	// Note: padding is to speed up SSE code
-	mDataMaskIL |= DATA_VERTICES_MASK;
-	mDataOffsets[DATA_VERTICES] = mStride;
-	mStride += sDataSizes[DATA_VERTICES];
-
-	mStride += 4;
-
-	mDataMaskIL |= DATA_NORMALS_MASK;
-	mDataOffsets[DATA_NORMALS] = mStride;
-	mStride += sDataSizes[DATA_NORMALS];
-
-	mStride += 4;
-
-	// Note: binormals are stripped off in software blending
-	mDataMaskIL |= DATA_BINORMALS_MASK;
-	mDataOffsets[DATA_BINORMALS] = mStride;
-	mStride += sDataSizes[DATA_BINORMALS];
-
-	mStride += 4; // To keep the structure 16-byte aligned (for SSE happiness)
-
-	mDataMaskIL |= DATA_TEX_COORDS0_MASK;
-	mDataOffsets[DATA_TEX_COORDS0] = mStride;
-	mStride += sDataSizes[DATA_TEX_COORDS0];
-
-	mDataMaskIL |= DATA_TEX_COORDS1_MASK;
-	mDataOffsets[DATA_TEX_COORDS1] = mStride;
-	mStride += sDataSizes[DATA_TEX_COORDS1];
-
 	//LLDebugVarMessageBox::show("acceleration", &CLOTHING_ACCEL_FORCE_FACTOR, 10.f, 0.1f);
-	//LLDebugVarMessageBox::show("gravity", &CLOTHING_GRAVITY_EFFECT, 10.f, 0.1f);
-	
+	//LLDebugVarMessageBox::show("gravity", &CLOTHING_GRAVITY_EFFECT, 10.f, 0.1f);	
 }
 
 //-----------------------------------------------------------------------------
@@ -113,41 +77,194 @@ LLDrawPool *LLDrawPoolAvatar::instancePool()
 	return new LLDrawPoolAvatar();
 }
 
+BOOL gRenderAvatar = TRUE;
+static LLMatrix4 sModelViewMatrix = LLMatrix4();
 
-S32 LLDrawPoolAvatar::rebuild()
+S32 LLDrawPoolAvatar::getVertexShaderLevel() const
 {
-	mRebuildTime++;
-	if (mRebuildTime > mRebuildFreq)
-	{
-		flushAGP();
-
-		mRebuildTime = 0;
-	}
-
-	return 0;
+	return (S32) gPipeline.getVertexShaderLevel(LLPipeline::SHADER_AVATAR);
 }
 
-BOOL gRenderAvatar = TRUE;
-
 void LLDrawPoolAvatar::prerender()
 {
 	mVertexShaderLevel = gPipeline.getVertexShaderLevel(LLPipeline::SHADER_AVATAR);
+	sShaderLevel = mVertexShaderLevel;
+	
+	if (sShaderLevel > 0)
+	{
+		sBufferUsage = GL_STATIC_DRAW_ARB;
+	}
+	else
+	{
+		sBufferUsage = GL_STREAM_DRAW_ARB;
+	}
+}
+
+LLMatrix4& LLDrawPoolAvatar::getModelView()
+{
+	return sModelViewMatrix;
 }
 
 //-----------------------------------------------------------------------------
 // render()
 //-----------------------------------------------------------------------------
+
+S32 LLDrawPoolAvatar::getNumPasses()
+{
+	return 3;
+}
+
 void LLDrawPoolAvatar::render(S32 pass)
 {
 	LLFastTimer t(LLFastTimer::FTM_RENDER_CHARACTERS);
-	renderAvatars(NULL); // render all avatars
+	renderAvatars(NULL, pass); // render all avatars
+}
+
+void LLDrawPoolAvatar::beginRenderPass(S32 pass)
+{
+	//reset vertex buffer mappings
+	LLVertexBuffer::unbind();
+
+	switch (pass)
+	{
+	case 0:
+		beginFootShadow();
+		break;
+	case 1:
+		glGetFloatv(GL_MODELVIEW_MATRIX, (F32*) sModelViewMatrix.mMatrix);
+		beginRigid();
+		break;
+	case 2:
+		beginSkinned();
+		break;
+	}
+}
+
+void LLDrawPoolAvatar::endRenderPass(S32 pass)
+{
+	switch (pass)
+	{
+	case 0:
+		endFootShadow();
+		break;
+	case 1:
+		endRigid();
+		break;
+	case 2:
+		endSkinned();
+	}
+}
+
+void LLDrawPoolAvatar::beginFootShadow()
+{
+	glDepthMask(GL_FALSE);
+	gPipeline.enableLightsFullbright(LLColor4(1,1,1,1));
+	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+}
+
+void LLDrawPoolAvatar::endFootShadow()
+{
+	gPipeline.enableLightsDynamic(1.f);
+	glDepthMask(GL_TRUE);
+	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+}
+
+void LLDrawPoolAvatar::beginRigid()
+{
+	sVertexProgram = &gPipeline.mAvatarEyeballProgram;
+	glEnableClientState(GL_NORMAL_ARRAY);
+	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+
+	if (sShaderLevel > 0)
+	{	//eyeballs render with the specular shader
+		gPipeline.mAvatarEyeballProgram.bind();
+		gPipeline.mMaterialIndex = gPipeline.mAvatarEyeballProgram.mAttribute[LLPipeline::GLSL_MATERIAL_COLOR];
+		gPipeline.mSpecularIndex = gPipeline.mAvatarEyeballProgram.mAttribute[LLPipeline::GLSL_SPECULAR_COLOR];
+	}
+}
+
+void LLDrawPoolAvatar::endRigid()
+{
+	glDisableClientState(GL_NORMAL_ARRAY);
+	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+}
+
+void LLDrawPoolAvatar::beginSkinned()
+{
+	glEnableClientState(GL_NORMAL_ARRAY);
+	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+
+	sVertexProgram = &gPipeline.mAvatarProgram;
+
+	if (sShaderLevel > 0)  // for hardware blending
+	{
+		sRenderingSkinned = TRUE;
+		glClientActiveTextureARB(GL_TEXTURE1_ARB);
+		if (sShaderLevel >= SHADER_LEVEL_BUMP)
+		{
+			gPipeline.mMaterialIndex = sVertexProgram->mAttribute[LLPipeline::GLSL_MATERIAL_COLOR];
+			gPipeline.mSpecularIndex = sVertexProgram->mAttribute[LLPipeline::GLSL_SPECULAR_COLOR];
+		}
+		sVertexProgram->bind();
+		if (sShaderLevel >= SHADER_LEVEL_CLOTH)
+		{
+			enable_cloth_weights(sVertexProgram->mAttribute[LLPipeline::GLSL_AVATAR_CLOTHING]);
+		}
+		enable_vertex_weighting(sVertexProgram->mAttribute[LLPipeline::GLSL_AVATAR_WEIGHT]);
+
+		if (sShaderLevel >= SHADER_LEVEL_BUMP)
+		{
+			enable_binormals(sVertexProgram->mAttribute[LLPipeline::GLSL_BINORMAL]);
+		}
+		
+		sVertexProgram->enableTexture(LLPipeline::GLSL_BUMP_MAP);
+		glActiveTextureARB(GL_TEXTURE0_ARB);
+	}
 }
 
-void LLDrawPoolAvatar::renderAvatars(LLVOAvatar* single_avatar, BOOL no_shaders)
+void LLDrawPoolAvatar::endSkinned()
 {
-	if (no_shaders)
+	// if we're in software-blending, remember to set the fence _after_ we draw so we wait till this rendering is done
+	if (sShaderLevel > 0)
 	{
-		mVertexShaderLevel = 0;
+		sRenderingSkinned = FALSE;
+		sVertexProgram->disableTexture(LLPipeline::GLSL_BUMP_MAP);
+		glActiveTextureARB(GL_TEXTURE0_ARB);
+		glClientActiveTextureARB(GL_TEXTURE0_ARB);
+		disable_vertex_weighting(sVertexProgram->mAttribute[LLPipeline::GLSL_AVATAR_WEIGHT]);
+		if (sShaderLevel >= SHADER_LEVEL_BUMP)
+		{
+			disable_binormals(sVertexProgram->mAttribute[LLPipeline::GLSL_BINORMAL]);
+		}
+		if ((sShaderLevel >= SHADER_LEVEL_CLOTH))
+		{
+			disable_cloth_weights(sVertexProgram->mAttribute[LLPipeline::GLSL_AVATAR_CLOTHING]);
+		}
+
+		sVertexProgram->unbind();
+	}
+
+	glActiveTextureARB(GL_TEXTURE0_ARB);
+	glClientActiveTextureARB(GL_TEXTURE0_ARB);
+
+	glDisableClientState(GL_NORMAL_ARRAY);
+	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+}
+
+
+void LLDrawPoolAvatar::renderAvatars(LLVOAvatar* single_avatar, S32 pass)
+{
+	if (pass == -1)
+	{
+		for (S32 i = 1; i < getNumPasses(); i++)
+		{ //skip foot shadows
+			prerender();
+			beginRenderPass(i);
+			renderAvatars(single_avatar, i);
+			endRenderPass(i);
+		}
+
+		return;
 	}
 
 	if (!gRenderAvatar)
@@ -176,13 +293,95 @@ void LLDrawPoolAvatar::renderAvatars(LLVOAvatar* single_avatar, BOOL no_shaders)
 		avatarp = (LLVOAvatar *)(facep->getDrawable()->getVObj());
 	}
 
-	if (avatarp->isDead() || avatarp->mDrawable.isNull())
+    if (avatarp->isDead() || avatarp->mDrawable.isNull())
 	{
 		return;
 	}
 
     LLOverrideFaceColor color(this, 1.0f, 1.0f, 1.0f, 1.0f);
 	
+	if (pass == 0)
+	{
+		if (gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_FOOT_SHADOWS))
+		{
+			mIndicesDrawn += avatarp->renderFootShadows();	
+		}
+		return;
+	}
+
+	if (avatarp->mSpecialRenderMode == 0) // normal
+	{
+		gPipeline.enableLightsAvatar(avatarp->mDrawable->getSunShadowFactor());
+	}
+	else if (avatarp->mSpecialRenderMode == 1)  // anim preview
+	{
+		gPipeline.enableLightsAvatarEdit(LLColor4(0.7f, 0.6f, 0.3f, 1.f));
+	}
+	else // 2=image preview,  3=morph view
+	{
+		gPipeline.enableLightsAvatarEdit(LLColor4(.5f, .5f, .5f, 1.f));
+	}
+
+	if (pass == 1)
+	{
+		// render rigid meshes (eyeballs) first
+		mIndicesDrawn += avatarp->renderRigid();
+
+		if (!gRenderForSelect && avatarp->mIsSelf && LLVOAvatar::sAvatarLoadTest)
+		{
+			LLVector3 orig_pos_root = avatarp->mRoot.getPosition();
+			LLVector3 next_pos_root = orig_pos_root;
+			for (S32 i = 0; i < NUM_TEST_AVATARS; i++)
+			{
+				next_pos_root.mV[VX] += 1.f;
+				if (i % 5 == 0)
+				{
+					next_pos_root.mV[VY] += 1.f;
+					next_pos_root.mV[VX] = orig_pos_root.mV[VX];
+				}
+
+				avatarp->mRoot.setPosition(next_pos_root); // avatar load test
+				avatarp->mRoot.updateWorldMatrixChildren(); // avatar load test
+
+				mIndicesDrawn += avatarp->renderRigid();
+			}
+			avatarp->mRoot.setPosition(orig_pos_root); // avatar load test
+			avatarp->mRoot.updateWorldMatrixChildren(); // avatar load test
+		}
+		return;
+	}
+	
+
+	if (sShaderLevel > 0)
+	{
+		gPipeline.mAvatarMatrixParam = sVertexProgram->mUniform[LLPipeline::GLSL_AVATAR_MATRIX];
+	}
+    
+	if ((sShaderLevel >= SHADER_LEVEL_CLOTH))
+	{
+		LLMatrix4 rot_mat;
+		gCamera->getMatrixToLocal(rot_mat);
+		LLMatrix4 cfr(OGL_TO_CFR_ROTATION);
+		rot_mat *= cfr;
+		
+		LLVector4 wind;
+		wind.setVec(avatarp->mWindVec);
+		wind.mV[VW] = 0;
+		wind = wind * rot_mat;
+		wind.mV[VW] = avatarp->mWindVec.mV[VW];
+
+		sVertexProgram->vertexAttrib4fv(LLPipeline::GLSL_AVATAR_WIND, wind.mV);
+		F32 phase = -1.f * (avatarp->mRipplePhase);
+
+		F32 freq = 7.f + (noise1(avatarp->mRipplePhase) * 2.f);
+		LLVector4 sin_params(freq, freq, freq, phase);
+		sVertexProgram->vertexAttrib4fv(LLPipeline::GLSL_AVATAR_SINWAVE, sin_params.mV);
+
+		LLVector4 gravity(0.f, 0.f, -CLOTHING_GRAVITY_EFFECT, 0.f);
+		gravity = gravity * rot_mat;
+		sVertexProgram->vertexAttrib4fv(LLPipeline::GLSL_AVATAR_GRAVITY, gravity.mV);
+	}
+
 	if( !single_avatar || (avatarp == single_avatar) )
 	{
 		if (LLVOAvatar::sShowCollisionVolumes)
@@ -191,8 +390,6 @@ void LLDrawPoolAvatar::renderAvatars(LLVOAvatar* single_avatar, BOOL no_shaders)
 			avatarp->renderCollisionVolumes();
 		}
 
-		LLGLEnable normalize(GL_NORMALIZE);
-
 		if (avatarp->mIsSelf && LLAgent::sDebugDisplayTarget)
 		{
 			LLGLSNoTexture gls_no_texture;
@@ -248,171 +445,8 @@ void LLDrawPoolAvatar::renderAvatars(LLVOAvatar* single_avatar, BOOL no_shaders)
 			color.setColor(1.0f, 1.0f, 1.0f, 1.0f);
 		}
 
-		glEnableClientState(GL_VERTEX_ARRAY);
-		glEnableClientState(GL_NORMAL_ARRAY);
-		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
-
-		LLGLSLShader* vertex_program = &gPipeline.mAvatarProgram;
-		if (mVertexShaderLevel > 0)
-		{
-			gPipeline.mAvatarMatrixParam = vertex_program->mUniform[LLPipeline::GLSL_AVATAR_MATRIX];
-		}
-		
-		//--------------------------------------------------------------------------------
-		// this is where we first hit the software blending path
-		// if enabled, we need to set up the proper buffers and avoid setting other state
-		//--------------------------------------------------------------------------------
-		if (!(mVertexShaderLevel > 0))
-		{
-
-			// performance could be increased by better utilizing the buffers, for example, only using 1k buffers for lo-res
-			// avatars. But the only problem with using fewer buffers is that we're more likely to wait for a fence to complete
-
-			// vertex format:
-			//  vertices 12
-			//  texcoords 8
-			//  normals  12
-			//	binormals 12
-			//  padding  4
-			// total     48
-
-			// Rotate to the next buffer, round-robin.
-			gPipeline.bufferRotate();
-
-			// Wait until the hardware is done reading the last set of vertices from the buffer before writing the next set.
-			gPipeline.bufferWaitFence();
-
-			// Need to do this because we may be rendering without AGP even in AGP mode
-			U8* buffer_offset_start = gPipeline.bufferGetScratchMemory();
-			glVertexPointer(  3, GL_FLOAT, AVATAR_VERTEX_BYTES, buffer_offset_start + AVATAR_OFFSET_POS);
-			glTexCoordPointer(2, GL_FLOAT, AVATAR_VERTEX_BYTES, buffer_offset_start + AVATAR_OFFSET_TEX0);
-			glNormalPointer(     GL_FLOAT, AVATAR_VERTEX_BYTES, buffer_offset_start + AVATAR_OFFSET_NORMAL);
-			
-		}
-
-		if ((mVertexShaderLevel > 0))  // for hardware blending
-		{
-			bindGLVertexPointer();
-			bindGLNormalPointer();
-			bindGLTexCoordPointer(0);
-		}
-		
-		if ((mVertexShaderLevel > 0))
-		{	//eyeballs render with the specular shader
-			gPipeline.mAvatarEyeballProgram.bind();
-			gPipeline.mMaterialIndex = gPipeline.mAvatarEyeballProgram.mAttribute[LLPipeline::GLSL_MATERIAL_COLOR];
-			gPipeline.mSpecularIndex = gPipeline.mAvatarEyeballProgram.mAttribute[LLPipeline::GLSL_SPECULAR_COLOR];
-
-			S32 index = gPipeline.mAvatarEyeballProgram.enableTexture(LLPipeline::GLSL_SCATTER_MAP);		
-			gSky.mVOSkyp->getScatterMap()->bind(index);
-					
-			glActiveTextureARB(GL_TEXTURE0_ARB);
-		}
-		
-		if (avatarp->mSpecialRenderMode == 0) // normal
-		{
-			gPipeline.enableLightsAvatar(avatarp->mDrawable->getSunShadowFactor());
-		}
-		else if (avatarp->mSpecialRenderMode == 1)  // anim preview
-		{
-			gPipeline.enableLightsAvatarEdit(LLColor4(0.7f, 0.6f, 0.3f, 1.f));
-		}
-		else // 2=image preview,  3=morph view
-		{
-			gPipeline.enableLightsAvatarEdit(LLColor4(.5f, .5f, .5f, 1.f));
-		}
-		
-		// render rigid meshes (eyeballs) first
-		mIndicesDrawn += avatarp->renderRigid();
-
-		if ((mVertexShaderLevel > 0))
-		{	
-			gPipeline.mAvatarEyeballProgram.disableTexture(LLPipeline::GLSL_SCATTER_MAP);		
-			glActiveTextureARB(GL_TEXTURE0_ARB);
-		}
-		
-		if (!gRenderForSelect && avatarp->mIsSelf && LLVOAvatar::sAvatarLoadTest)
-		{
-			LLVector3 orig_pos_root = avatarp->mRoot.getPosition();
-			LLVector3 next_pos_root = orig_pos_root;
-			for (S32 i = 0; i < NUM_TEST_AVATARS; i++)
-			{
-				next_pos_root.mV[VX] += 1.f;
-				if (i % 5 == 0)
-				{
-					next_pos_root.mV[VY] += 1.f;
-					next_pos_root.mV[VX] = orig_pos_root.mV[VX];
-				}
-
-				avatarp->mRoot.setPosition(next_pos_root); // avatar load test
-				avatarp->mRoot.updateWorldMatrixChildren(); // avatar load test
-
-				mIndicesDrawn += avatarp->renderRigid();
-			}
-			avatarp->mRoot.setPosition(orig_pos_root); // avatar load test
-			avatarp->mRoot.updateWorldMatrixChildren(); // avatar load test
-		}
-
-		if ((mVertexShaderLevel > 0))  // for hardware blending
-		{
-			glClientActiveTextureARB(GL_TEXTURE1_ARB);
-			if ((mVertexShaderLevel >= SHADER_LEVEL_BUMP))
-			{
-				bindGLTexCoordPointer(1);
-				
-				bindGLBinormalPointer(vertex_program->mAttribute[LLPipeline::GLSL_BINORMAL]);
-				gPipeline.mMaterialIndex = vertex_program->mAttribute[LLPipeline::GLSL_MATERIAL_COLOR];
-				gPipeline.mSpecularIndex = vertex_program->mAttribute[LLPipeline::GLSL_SPECULAR_COLOR];
-			}
-			glClientActiveTextureARB(GL_TEXTURE0_ARB);
-			bindGLTexCoordPointer(0);
-			vertex_program->bind();
-			bindGLVertexWeightPointer(vertex_program->mAttribute[LLPipeline::GLSL_AVATAR_WEIGHT]);
-			if ((mVertexShaderLevel >= SHADER_LEVEL_CLOTH))
-			{
-				bindGLVertexClothingWeightPointer(vertex_program->mAttribute[LLPipeline::GLSL_AVATAR_CLOTHING]);
-				enable_cloth_weights(vertex_program->mAttribute[LLPipeline::GLSL_AVATAR_CLOTHING]);
-			}
-			enable_vertex_weighting(vertex_program->mAttribute[LLPipeline::GLSL_AVATAR_WEIGHT]);
-
-			if ((mVertexShaderLevel >= SHADER_LEVEL_BUMP))
-			{
-				enable_binormals(vertex_program->mAttribute[LLPipeline::GLSL_BINORMAL]);
-			}
-			
-			vertex_program->enableTexture(LLPipeline::GLSL_BUMP_MAP);
-			S32 index = vertex_program->enableTexture(LLPipeline::GLSL_SCATTER_MAP);
-			gSky.mVOSkyp->getScatterMap()->bind(index);
-			glActiveTextureARB(GL_TEXTURE0_ARB);
-		}
-
-		if ((mVertexShaderLevel >= SHADER_LEVEL_CLOTH))
-		{
-			LLMatrix4 rot_mat;
-			gCamera->getMatrixToLocal(rot_mat);
-			LLMatrix4 cfr(OGL_TO_CFR_ROTATION);
-			rot_mat *= cfr;
-			
-			LLVector4 wind;
-			wind.setVec(avatarp->mWindVec);
-			wind.mV[VW] = 0;
-			wind = wind * rot_mat;
-			wind.mV[VW] = avatarp->mWindVec.mV[VW];
-
-			vertex_program->vertexAttrib4fv(LLPipeline::GLSL_AVATAR_WIND, wind.mV);
-			F32 phase = -1.f * (avatarp->mRipplePhase);
-
-			F32 freq = 7.f + (noise1(avatarp->mRipplePhase) * 2.f);
-			LLVector4 sin_params(freq, freq, freq, phase);
-			vertex_program->vertexAttrib4fv(LLPipeline::GLSL_AVATAR_SINWAVE, sin_params.mV);
-
-			LLVector4 gravity(0.f, 0.f, -CLOTHING_GRAVITY_EFFECT, 0.f);
-			gravity = gravity * rot_mat;
-			vertex_program->vertexAttrib4fv(LLPipeline::GLSL_AVATAR_GRAVITY, gravity.mV);
-		}
-
 		mIndicesDrawn += avatarp->renderSkinned(AVATAR_RENDER_PASS_SINGLE);
-		
+				
 		if (!gRenderForSelect && avatarp->mIsSelf && LLVOAvatar::sAvatarLoadTest)
 		{
 			LLVector3 orig_pos_root = avatarp->mRoot.getPosition();
@@ -434,31 +468,6 @@ void LLDrawPoolAvatar::renderAvatars(LLVOAvatar* single_avatar, BOOL no_shaders)
 			avatarp->mRoot.setPosition(orig_pos_root); // avatar load test
 			avatarp->mRoot.updateWorldMatrixChildren(); // avatar load test
 		}
-
-		// if we're in software-blending, remember to set the fence _after_ we draw so we wait till this rendering is done
-		if (!(mVertexShaderLevel > 0))
-		{
-			// want for the previously bound fence to finish
-			gPipeline.bufferSendFence();
-		}
-		else
-		{
-			vertex_program->disableTexture(LLPipeline::GLSL_BUMP_MAP);
-			vertex_program->disableTexture(LLPipeline::GLSL_SCATTER_MAP);
-			glActiveTextureARB(GL_TEXTURE0_ARB);
-			glClientActiveTextureARB(GL_TEXTURE0_ARB);
-			disable_vertex_weighting(vertex_program->mAttribute[LLPipeline::GLSL_AVATAR_WEIGHT]);
-			if ((mVertexShaderLevel >= SHADER_LEVEL_BUMP))
-			{
-				disable_binormals(vertex_program->mAttribute[LLPipeline::GLSL_BINORMAL]);
-			}
-			if ((mVertexShaderLevel >= SHADER_LEVEL_CLOTH))
-			{
-				disable_cloth_weights(vertex_program->mAttribute[LLPipeline::GLSL_AVATAR_CLOTHING]);
-			}
-
-			vertex_program->unbind();
-		}
 	}
 }
 
@@ -471,14 +480,13 @@ void LLDrawPoolAvatar::renderForSelect()
 	{
 		return;
 	}
-	//gGLSObjectSelectDepthAlpha.set();
-
+	
 	if (!gRenderAvatar)
 	{
 		return;
 	}
 
-	if (mDrawFace.empty() || !mMemory.count())
+	if (mDrawFace.empty())
 	{
 		return;
 	}
@@ -498,80 +506,38 @@ void LLDrawPoolAvatar::renderForSelect()
 	glEnableClientState(GL_VERTEX_ARRAY);
 	glEnableClientState(GL_NORMAL_ARRAY);
 	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
-
-	LLGLSLShader* vertex_program = &gPipeline.mAvatarPickProgram;
-	if (mVertexShaderLevel > 0)
+	glGetFloatv(GL_MODELVIEW_MATRIX, (F32*) sModelViewMatrix.mMatrix);
+	sVertexProgram = &gPipeline.mAvatarPickProgram;
+	if (sShaderLevel > 0)
 	{
-		gPipeline.mAvatarMatrixParam = vertex_program->mUniform[LLPipeline::GLSL_AVATAR_MATRIX];
+		gPipeline.mAvatarMatrixParam = sVertexProgram->mUniform[LLPipeline::GLSL_AVATAR_MATRIX];
 	}
 	glAlphaFunc(GL_GEQUAL, 0.2f);
 	glBlendFunc(GL_ONE, GL_ZERO);
 
-	//--------------------------------------------------------------------------------
-	// this is where we first hit the software blending path
-	// if enabled, we need to set up the proper buffers and avoid setting other state
-	//--------------------------------------------------------------------------------
-	if (!(mVertexShaderLevel > 0) || gUseGLPick)
-	{
-
-		// Rotate to the next buffer, round-robin.
-		gPipeline.bufferRotate();
-
-		// Wait until the hardware is done reading the last set of vertices from the buffer before writing the next set.
-		gPipeline.bufferWaitFence();
-
-		// Need to do this because we may be rendering without AGP even in AGP mode
-		U8* buffer_offset_start = gPipeline.bufferGetScratchMemory();
-		glVertexPointer(  3, GL_FLOAT, AVATAR_VERTEX_BYTES, buffer_offset_start + AVATAR_OFFSET_POS);
-		glTexCoordPointer(2, GL_FLOAT, AVATAR_VERTEX_BYTES, buffer_offset_start + AVATAR_OFFSET_TEX0);
-		glNormalPointer(     GL_FLOAT, AVATAR_VERTEX_BYTES, buffer_offset_start + AVATAR_OFFSET_NORMAL);
-	}
-
 	S32 name = avatarp->mDrawable->getVObj()->mGLName;
 	LLColor4U color((U8)(name >> 16), (U8)(name >> 8), (U8)name);
 	glColor4ubv(color.mV);
 
-	if ((mVertexShaderLevel > 0) && !gUseGLPick)  // for hardware blending
-	{
-		bindGLVertexPointer();
-		bindGLNormalPointer();
-		bindGLTexCoordPointer(0);
-	}
-
 	// render rigid meshes (eyeballs) first
-	mIndicesDrawn += avatarp->renderRigid();
+	//mIndicesDrawn += avatarp->renderRigid();
 
-	if ((mVertexShaderLevel > 0) && !gUseGLPick)  // for hardware blending
+	if ((sShaderLevel > 0) && !gUseGLPick)  // for hardware blending
 	{
 		glClientActiveTextureARB(GL_TEXTURE0_ARB);
-		bindGLTexCoordPointer(0);
-		vertex_program->bind();
-		bindGLVertexWeightPointer(vertex_program->mAttribute[LLPipeline::GLSL_AVATAR_WEIGHT]);
-		/*if ((mVertexShaderLevel >= SHADER_LEVEL_CLOTH))
-		{
-			bindGLVertexClothingWeightPointer();
-			enable_cloth_weights();
-		}*/
-		enable_vertex_weighting(vertex_program->mAttribute[LLPipeline::GLSL_AVATAR_WEIGHT]);
+		sRenderingSkinned = TRUE;
+		sVertexProgram->bind();
+		enable_vertex_weighting(sVertexProgram->mAttribute[LLPipeline::GLSL_AVATAR_WEIGHT]);
 	}
-
+	
 	mIndicesDrawn += avatarp->renderSkinned(AVATAR_RENDER_PASS_SINGLE);
 
 	// if we're in software-blending, remember to set the fence _after_ we draw so we wait till this rendering is done
-	if (!(mVertexShaderLevel > 0) || gUseGLPick)
+	if ((sShaderLevel > 0) && !gUseGLPick)
 	{
-		// want for the previously bound fence to finish
-		gPipeline.bufferSendFence();
-	}
-	else
-	{
-		vertex_program->unbind();
-		disable_vertex_weighting(vertex_program->mAttribute[LLPipeline::GLSL_AVATAR_WEIGHT]);
-		
-		/*if ((mVertexShaderLevel >= SHADER_LEVEL_CLOTH))
-		{
-			disable_cloth_weights();
-		}*/
+		sRenderingSkinned = FALSE;
+		sVertexProgram->unbind();
+		disable_vertex_weighting(sVertexProgram->mAttribute[LLPipeline::GLSL_AVATAR_WEIGHT]);
 	}
 
 	glAlphaFunc(GL_GREATER, 0.01f);
@@ -608,3 +574,46 @@ LLColor3 LLDrawPoolAvatar::getDebugColor() const
 {
 	return LLColor3(0.f, 1.f, 0.f);
 }
+
+LLVertexBufferAvatar::LLVertexBufferAvatar()
+: LLVertexBuffer(sDataMask, 
+	gPipeline.getVertexShaderLevel(LLPipeline::SHADER_AVATAR) > 0 ?	
+	GL_STATIC_DRAW_ARB : 
+	GL_STREAM_DRAW_ARB)
+{
+
+}
+
+
+void LLVertexBufferAvatar::setupVertexBuffer(U32 data_mask) const
+{
+	if (sRenderingSkinned)
+	{
+		U8* base = useVBOs() ? NULL : mMappedData;
+
+		glVertexPointer(3,GL_FLOAT, mStride, (void*)(base + 0));
+		glNormalPointer(GL_FLOAT, mStride, (void*)(base + mOffsets[TYPE_NORMAL]));
+	
+		glClientActiveTextureARB(GL_TEXTURE1_ARB);
+		glTexCoordPointer(2,GL_FLOAT, mStride, (void*)(base + mOffsets[TYPE_TEXCOORD2]));
+		glClientActiveTextureARB(GL_TEXTURE0_ARB);
+		glTexCoordPointer(2,GL_FLOAT, mStride, (void*)(base + mOffsets[TYPE_TEXCOORD]));
+		
+		set_vertex_weights(sVertexProgram->mAttribute[LLPipeline::GLSL_AVATAR_WEIGHT], mStride, (F32*)(base + mOffsets[TYPE_WEIGHT]));
+
+		if (sShaderLevel >= LLDrawPoolAvatar::SHADER_LEVEL_BUMP)
+		{
+			set_binormals(sVertexProgram->mAttribute[LLPipeline::GLSL_BINORMAL], mStride, (LLVector3*)(base + mOffsets[TYPE_BINORMAL]));
+		}
+	
+		if (sShaderLevel >= LLDrawPoolAvatar::SHADER_LEVEL_CLOTH)
+		{
+			set_vertex_clothing_weights(sVertexProgram->mAttribute[LLPipeline::GLSL_AVATAR_CLOTHING], mStride, (LLVector4*)(base + mOffsets[TYPE_CLOTHWEIGHT]));
+		}
+	}
+	else
+	{
+		LLVertexBuffer::setupVertexBuffer(data_mask);
+	}
+}
+
diff --git a/indra/newview/lldrawpoolavatar.h b/indra/newview/lldrawpoolavatar.h
index 0d706b012bf8314bc9af7377c87ddc9104162f9b..e0b1f2098d45b1d1307af530d7dc18c2d5d8b213 100644
--- a/indra/newview/lldrawpoolavatar.h
+++ b/indra/newview/lldrawpoolavatar.h
@@ -13,7 +13,7 @@
 
 class LLVOAvatar;
 
-class LLDrawPoolAvatar : public LLDrawPool
+class LLDrawPoolAvatar : public LLFacePool
 {
 protected:
 	S32					mNumFaces;
@@ -24,21 +24,48 @@ public:
 		SHADER_LEVEL_CLOTH = 3
 	};
 	
+	enum
+	{
+		VERTEX_DATA_MASK =	LLVertexBuffer::MAP_VERTEX |
+							LLVertexBuffer::MAP_NORMAL |
+							LLVertexBuffer::MAP_TEXCOORD |
+							LLVertexBuffer::MAP_WEIGHT |
+							LLVertexBuffer::MAP_CLOTHWEIGHT |
+							LLVertexBuffer::MAP_BINORMAL
+							
+	};
+
+	virtual U32 getVertexDataMask() { return VERTEX_DATA_MASK; }
+
+	virtual S32 getVertexShaderLevel() const;
+
 	LLDrawPoolAvatar();
 
+	static LLMatrix4& getModelView();
+
 	/*virtual*/ LLDrawPool *instancePool();
 
+	/*virtual*/ S32  getNumPasses();
+	/*virtual*/ void beginRenderPass(S32 pass);
+	/*virtual*/ void endRenderPass(S32 pass);
 	/*virtual*/ void prerender();
 	/*virtual*/ void render(S32 pass = 0);
 	/*virtual*/ void renderForSelect();
-	/*virtual*/ S32 rebuild();
 
+	void beginRigid();
+	void beginFootShadow();
+	void beginSkinned();
+		
+	void endRigid();
+	void endFootShadow();
+	void endSkinned();
+		
 	/*virtual*/ LLViewerImage *getDebugTexture();
 	/*virtual*/ LLColor3 getDebugColor() const; // For AGP debug display
 
 	virtual S32 getMaterialAttribIndex() { return 0; }
 
-	void renderAvatars(LLVOAvatar *single_avatar, BOOL no_shaders = FALSE); // renders only one avatar if single_avatar is not null.
+	void renderAvatars(LLVOAvatar *single_avatar, S32 pass = -1); // renders only one avatar if single_avatar is not null.
 };
 
 
diff --git a/indra/newview/lldrawpoolbump.cpp b/indra/newview/lldrawpoolbump.cpp
index 68fa88c4566d7a914254c46ff7688c188460d315..377dbea2d08527211b19caaa8ff20f48750a7816 100644
--- a/indra/newview/lldrawpoolbump.cpp
+++ b/indra/newview/lldrawpoolbump.cpp
@@ -18,12 +18,10 @@
 #include "m4math.h"
 
 #include "llagent.h"
-#include "llagparray.h"
 #include "llcubemap.h"
 #include "lldrawable.h"
 #include "lldrawpoolsimple.h"
 #include "llface.h"
-#include "llgl.h"
 #include "llsky.h"
 #include "lltextureentry.h"
 #include "llviewercamera.h"
@@ -45,9 +43,11 @@ LLBumpImageList gBumpImageList;
 
 const S32 STD_BUMP_LATEST_FILE_VERSION = 1;
 
-S32 LLDrawPoolBump::sBumpTex = -1;
-S32 LLDrawPoolBump::sDiffTex = -1;
-S32 LLDrawPoolBump::sEnvTex = -1;
+const U32 VERTEX_MASK_SHINY = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_COLOR;
+const U32 VERTEX_MASK_BUMP = LLVertexBuffer::MAP_VERTEX |LLVertexBuffer::MAP_TEXCOORD | LLVertexBuffer::MAP_TEXCOORD2;
+
+U32 LLDrawPoolBump::sVertexMask = VERTEX_MASK_SHINY;
+static LLCubeMap* sCubeMap = NULL;
 
 // static 
 void LLStandardBumpmap::init()
@@ -109,7 +109,7 @@ void LLStandardBumpmap::restoreGL()
 			return;
 		}
 
-		llinfos << "Loading bumpmap: " << bump_file << " from viewerart" << llendl;
+// 		llinfos << "Loading bumpmap: " << bump_file << " from viewerart" << llendl;
 		gStandardBumpmapList[LLStandardBumpmap::sStandardBumpmapCount].mLabel = label;
 		gStandardBumpmapList[LLStandardBumpmap::sStandardBumpmapCount].mImage = gImageList.getImage( LLUUID(gViewerArt.getString(bump_file)) );
 		LLStandardBumpmap::sStandardBumpmapCount++;
@@ -133,15 +133,9 @@ void LLStandardBumpmap::destroyGL()
 
 ////////////////////////////////////////////////////////////////
 
-LLDrawPoolBump::LLDrawPoolBump(LLViewerImage *texturep) :
-	LLDrawPool(POOL_BUMP, DATA_BUMP_IL_MASK | DATA_COLORS_MASK, DATA_SIMPLE_NIL_MASK),
-	mTexturep(texturep)
-{
-}
-
-LLDrawPool *LLDrawPoolBump::instancePool()
+LLDrawPoolBump::LLDrawPoolBump() 
+: LLRenderPass(LLDrawPool::POOL_BUMP)
 {
-	return new LLDrawPoolBump(mTexturep);
 }
 
 
@@ -150,51 +144,16 @@ void LLDrawPoolBump::prerender()
 	mVertexShaderLevel = gPipeline.getVertexShaderLevel(LLPipeline::SHADER_OBJECT);
 }
 
-BOOL LLDrawPoolBump::match(LLFace* last_face, LLFace* facep)  
-{
-	if (gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_CHAIN_FACES) &&
-		!last_face->isState(LLFace::LIGHT | LLFace::FULLBRIGHT) &&
-		!facep->isState(LLFace::LIGHT | LLFace::FULLBRIGHT) &&
-		facep->getIndicesStart() == last_face->getIndicesStart()+last_face->getIndicesCount() &&
-		facep->getRenderColor() == last_face->getRenderColor() &&
-		facep->getTextureEntry()->getShiny() == last_face->getTextureEntry()->getShiny() &&
-		facep->getTextureEntry()->getBumpmap() == last_face->getTextureEntry()->getBumpmap())
-	{
-		if (facep->isState(LLFace::GLOBAL))
-		{
-			if (last_face->isState(LLFace::GLOBAL))
-			{
-				return TRUE;
-			}
-		}
-		else
-		{
-			if (!last_face->isState(LLFace::GLOBAL))
-			{
-				if (last_face->getRenderMatrix() == facep->getRenderMatrix())
-				{
-					return TRUE;
-				}
-			}
-		}
-	}
-	
-	return FALSE;
-}
-
 // static
 S32 LLDrawPoolBump::numBumpPasses()
 {
-	if (gPipeline.getVertexShaderLevel(LLPipeline::SHADER_OBJECT) > 0)
+	if (gSavedSettings.getBOOL("RenderObjectBump"))
 	{
-		return 1; // single pass for shaders
+		return 2;
 	}
-	else
+    else
 	{
-		if (gSavedSettings.getBOOL("RenderObjectBump"))
-			return 3;
-		else
-			return 1;
+		return 0;
 	}
 }
 
@@ -208,13 +167,10 @@ void LLDrawPoolBump::beginRenderPass(S32 pass)
 	switch( pass )
 	{
 		case 0:
-			beginPass0(this);
+			beginShiny();
 			break;
 		case 1:
-			beginPass1();
-			break;
-		case 2:
-			beginPass2();
+			beginBump();
 			break;
 		default:
 			llassert(0);
@@ -225,66 +181,17 @@ void LLDrawPoolBump::beginRenderPass(S32 pass)
 void LLDrawPoolBump::render(S32 pass)
 {
 	LLFastTimer t(LLFastTimer::FTM_RENDER_BUMP);
-	if (!mTexturep)
-	{
-		return;
-	}
-
-	if (mDrawFace.empty())
-	{
-		return;
-	}
-
-	const U32* index_array = getRawIndices();
 	
-	S32 indices = 0;
 	switch( pass )
 	{
 	  case 0:
 	  {
-		  stop_glerror();
-		  
-		  bindGLVertexPointer();
-		  bindGLTexCoordPointer();
-		  bindGLNormalPointer();
-		  if (gPipeline.getLightingDetail() >= 2)
-		  {
-			  bindGLColorPointer();
-		  }
-
-		  stop_glerror();
-		  
-		  LLGLState alpha_test(GL_ALPHA_TEST, FALSE);
-		  LLGLState blend(GL_BLEND, FALSE);
-		  LLViewerImage* tex = getTexture();
-		  if (tex && tex->getPrimaryFormat() == GL_ALPHA)
-		  {
-			  // Enable Invisibility Hack
-			  alpha_test.enable();
-			  blend.enable();
-		  }
-		  indices += renderPass0(this, mDrawFace, index_array, mTexturep);
+		  renderShiny();
 		  break;
 	  }
 	  case 1:
 	  {
-		  bindGLVertexPointer();
-		  bindGLNormalPointer();
-		  indices += renderPass1(mDrawFace, index_array, mTexturep);
-		  break;
-	  }
-	  case 2:
-	  {
-		  bindGLVertexPointer();
-		  // Texture unit 0
-		  glActiveTextureARB(GL_TEXTURE0_ARB);
-		  glClientActiveTextureARB(GL_TEXTURE0_ARB);
-		  bindGLTexCoordPointer();
-		  // Texture unit 1
-		  glActiveTextureARB(GL_TEXTURE1_ARB);
-		  glClientActiveTextureARB(GL_TEXTURE1_ARB);
-		  bindGLTexCoordPointer(1);
-		  indices += renderPass2(mDrawFace, index_array, mTexturep);
+		  renderBump();
 		  break;
 	  }
 	  default:
@@ -293,7 +200,6 @@ void LLDrawPoolBump::render(S32 pass)
 		  break;
 	  }
 	}
-	mIndicesDrawn += indices;
 }
 
 void LLDrawPoolBump::endRenderPass(S32 pass)
@@ -301,13 +207,10 @@ void LLDrawPoolBump::endRenderPass(S32 pass)
 	switch( pass )
 	{
 		case 0:
-			endPass0(this);
+			endShiny();
 			break;
 		case 1:
-			endPass1();
-			break;
-		case 2:
-			endPass2();
+			endBump();
 			break;
 		default:
 			llassert(0);
@@ -316,252 +219,171 @@ void LLDrawPoolBump::endRenderPass(S32 pass)
 }
 
 //static
-void LLDrawPoolBump::beginPass0(LLDrawPool* pool)
+void LLDrawPoolBump::beginShiny()
 {
-	stop_glerror();
-	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+	sVertexMask = VERTEX_MASK_SHINY;
+	// Second pass: environment map
 	glEnableClientState(GL_VERTEX_ARRAY);
 	glEnableClientState(GL_NORMAL_ARRAY);
-	if (gPipeline.getLightingDetail() >= 2)
-	{
-		glEnableClientState(GL_COLOR_ARRAY);
-	}
+	glEnableClientState(GL_COLOR_ARRAY);
 
-	if (pool->getVertexShaderLevel() > 0)
+	LLCubeMap* cube_map = gSky.mVOSkyp->getCubeMap();
+	if( cube_map )
 	{
-		enable_binormals(gPipeline.mObjectBumpProgram.mAttribute[LLPipeline::GLSL_BINORMAL]);
+		cube_map->enable(0);
+		cube_map->setMatrix(0);
+		cube_map->bind();
 
-		sEnvTex = gPipeline.mObjectBumpProgram.enableTexture(LLPipeline::GLSL_ENVIRONMENT_MAP, GL_TEXTURE_CUBE_MAP_ARB);
-		LLCubeMap* cube_map = gSky.mVOSkyp->getCubeMap();
-		if (sEnvTex >= 0 && cube_map)
+		if (gPipeline.getVertexShaderLevel(LLPipeline::SHADER_OBJECT) > 0)
 		{
-			cube_map->bind();
-			cube_map->setMatrix(1);
+			LLMatrix4 mat;
+			glGetFloatv(GL_MODELVIEW_MATRIX, (F32*) mat.mMatrix);
+			gPipeline.mObjectShinyProgram.bind();
+			LLVector3 vec = LLVector3(gPipeline.mShinyOrigin) * mat;
+			LLVector4 vec4(vec, gPipeline.mShinyOrigin.mV[3]);
+			glUniform4fvARB(gPipeline.mObjectShinyProgram.mUniform[LLPipeline::GLSL_SHINY_ORIGIN], 1,
+				vec4.mV);
+		}
+		else
+		{
+			glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,	GL_COMBINE_ARB);
+			
+			//use RGB from texture
+			glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB,	GL_REPLACE);
+			glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB,	GL_TEXTURE);
+			glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB,	GL_SRC_COLOR);
+
+			// use alpha from color
+			glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB,		GL_REPLACE);
+			glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB,		GL_PRIMARY_COLOR);
+			glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB,	GL_SRC_ALPHA);
 		}
-		
-		sBumpTex = gPipeline.mObjectBumpProgram.enableTexture(LLPipeline::GLSL_BUMP_MAP);
-		sDiffTex = gPipeline.mObjectBumpProgram.enableTexture(LLPipeline::GLSL_DIFFUSE_MAP);
-		S32 scatterTex = gPipeline.mObjectBumpProgram.enableTexture(LLPipeline::GLSL_SCATTER_MAP);
-		LLViewerImage::bindTexture(gSky.mVOSkyp->getScatterMap(), scatterTex);
 	}
-	stop_glerror();
 }
 
-//static
-S32 LLDrawPoolBump::renderPass0(LLDrawPool* pool, face_array_t& face_list, const U32* index_array, LLViewerImage* tex)
+void LLDrawPoolBump::renderShiny()
 {
-	if (!tex)
-	{
-		return 0;
-	}
+	LLFastTimer t(LLFastTimer::FTM_RENDER_SHINY);
 	
-	if (face_list.empty())
+	sCubeMap = NULL;
+
+	if( gSky.mVOSkyp->getCubeMap() )
 	{
-		return 0;
+		LLGLEnable blend_enable(GL_BLEND);
+		renderStatic(LLRenderPass::PASS_SHINY, sVertexMask);
+		renderActive(LLRenderPass::PASS_SHINY, sVertexMask);
 	}
+}
 
-	stop_glerror();
+void LLDrawPoolBump::renderActive(U32 type, U32 mask, BOOL texture)
+{
+#if !LL_RELEASE_FOR_DOWNLOAD
+	LLGLState::checkClientArrays(mask);
+#endif
 
-	S32 res = 0;
-	if (pool->getVertexShaderLevel() > 0)
+	LLSpatialBridge* last_bridge = NULL;
+	glPushMatrix();
+	
+	for (LLSpatialGroup::sg_vector_t::iterator i = gPipeline.mActiveGroups.begin(); i != gPipeline.mActiveGroups.end(); ++i)
 	{
-		LLFastTimer t(LLFastTimer::FTM_RENDER_BUMP);
-		pool->bindGLBinormalPointer(gPipeline.mObjectBumpProgram.mAttribute[LLPipeline::GLSL_BINORMAL]);
-		
-		LLViewerImage::bindTexture(tex, sDiffTex);
-
-		//single pass shader driven shiny/bump
-		LLGLDisable(GL_ALPHA_TEST);
-
-		LLViewerImage::sWhiteImagep->bind(sBumpTex);
-		
-		GLfloat alpha[4] =
+		LLSpatialGroup* group = *i;
+		if (!group->isDead() &&
+			gPipeline.hasRenderType(group->mSpatialPartition->mDrawableType) &&
+			group->mDrawMap.find(type) != group->mDrawMap.end())
 		{
-			0.00f,
-			0.25f,
-			0.5f,
-			0.75f
-		};
-
-		LLImageGL* last_bump = NULL;
-
-		for (std::vector<LLFace*>::iterator iter = face_list.begin();
-			 iter != face_list.end(); iter++)
-		{
-			LLFace *facep = *iter;
-			if (facep->mSkipRender)
-			{
-				continue;
-			}
-			
-			const LLTextureEntry* te = facep->getTextureEntry();
-			if (te)
+			LLSpatialBridge* bridge = (LLSpatialBridge*) group->mSpatialPartition;
+			if (bridge != last_bridge)
 			{
-				U8 index = te->getShiny();
-				LLColor4 col = te->getColor();
-				
-				gPipeline.mObjectBumpProgram.vertexAttrib4f(LLPipeline::GLSL_MATERIAL_COLOR,
-					col.mV[0], col.mV[1], col.mV[2], alpha[index]);
-				gPipeline.mObjectBumpProgram.vertexAttrib4f(LLPipeline::GLSL_SPECULAR_COLOR, 
-					alpha[index], alpha[index], alpha[index], alpha[index]);
-
-				LLImageGL* bump = getBumpMap(te, tex);
-				if (bump != last_bump)
+				glPopMatrix();
+				glPushMatrix();
+				glMultMatrixf((F32*) bridge->mDrawable->getRenderMatrix().mMatrix);
+				last_bridge = bridge;
+
+				if (LLPipeline::sDynamicReflections)
 				{
-					if (bump)
+					LLSpatialPartition* part = gPipeline.getSpatialPartition(LLPipeline::PARTITION_VOLUME);
+					LLSpatialGroup::OctreeNode* node = part->mOctree->getNodeAt(LLVector3d(bridge->mDrawable->getPositionAgent()), 32.0);
+					if (node)
 					{
-						bump->bind(sBumpTex);
-					}
-					else
-					{
-						LLViewerImage::sWhiteImagep->bind(sBumpTex);
+						sCubeMap = ((LLSpatialGroup*) node->getListener(0))->mReflectionMap;
 					}
 				}
-				last_bump = bump;
-
-				// Draw the geometry
-				facep->enableLights();
-				res += facep->renderIndexed(index_array);
-				stop_glerror();
-			}
-			else
-			{
-				llwarns << "DrawPoolBump has face with invalid texture entry." << llendl;
 			}
-		}
-	}
-	else
-	{
-		LLFastTimer t(LLFastTimer::FTM_RENDER_SIMPLE);
-		LLViewerImage::bindTexture(tex);
-		res = LLDrawPool::drawLoop(face_list, index_array);
-	}
-	return res;
-}
-
-//static
-void LLDrawPoolBump::endPass0(LLDrawPool* pool)
-{
-	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
-	glDisableClientState(GL_NORMAL_ARRAY);
-	glDisableClientState(GL_COLOR_ARRAY);
 
-	if (pool->getVertexShaderLevel() > 0)
-	{
-		gPipeline.mObjectBumpProgram.disableTexture(LLPipeline::GLSL_ENVIRONMENT_MAP, GL_TEXTURE_CUBE_MAP_ARB);
-		LLCubeMap* cube_map = gSky.mVOSkyp->getCubeMap();
-		if (sEnvTex >= 0 && cube_map)
-		{
-			cube_map->restoreMatrix();
+			renderGroup(group,type,mask,texture);
 		}
-		
-		gPipeline.mObjectBumpProgram.disableTexture(LLPipeline::GLSL_SCATTER_MAP);
-		gPipeline.mObjectBumpProgram.disableTexture(LLPipeline::GLSL_BUMP_MAP);
-		gPipeline.mObjectBumpProgram.disableTexture(LLPipeline::GLSL_DIFFUSE_MAP);
-
-		disable_binormals(gPipeline.mObjectBumpProgram.mAttribute[LLPipeline::GLSL_BINORMAL]);
-		
-		glActiveTextureARB(GL_TEXTURE0_ARB);
-		glEnable(GL_TEXTURE_2D);
 	}
+	
+	glPopMatrix();
 }
 
 
-//static
-void LLDrawPoolBump::beginPass1()
-{
-	// Second pass: environment map
-	glEnableClientState(GL_VERTEX_ARRAY);
-	glEnableClientState(GL_NORMAL_ARRAY);
 
-	LLCubeMap* cube_map = gSky.mVOSkyp->getCubeMap();
-	if( cube_map )
-	{
-		cube_map->enable(0);
-		cube_map->setMatrix(0);
-		cube_map->bind();
-	}
-}
-
-//static
-S32 LLDrawPoolBump::renderPass1(face_array_t& face_list, const U32* index_array, LLViewerImage* tex)
-{
-	LLFastTimer t(LLFastTimer::FTM_RENDER_SHINY);
-	if (gPipeline.getVertexShaderLevel(LLPipeline::SHADER_OBJECT) > 0) //everything happens in pass0
-	{
-		return 0;
-	}
-
-	S32 res = 0;
-	if( gSky.mVOSkyp->getCubeMap() )
+void LLDrawPoolBump::renderGroup(LLSpatialGroup* group, U32 type, U32 mask, BOOL texture = TRUE)
+{					
+	std::vector<LLDrawInfo*>& draw_info = group->mDrawMap[type];
+	
+	for (std::vector<LLDrawInfo*>::const_iterator k = draw_info.begin(); k != draw_info.end(); ++k)
 	{
-		//LLGLSPipelineAlpha gls;
-		//LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE, GL_EQUAL);
-		LLGLEnable blend_enable(GL_BLEND);
-		
-		GLfloat alpha[4] =
+		LLDrawInfo& params = **k;
+		if (LLPipeline::sDynamicReflections)
 		{
-			0.00f,
-			0.25f,
-			0.5f,
-			0.75f
-		};
-
-		for (std::vector<LLFace*>::iterator iter = face_list.begin();
-			 iter != face_list.end(); iter++)
-		{
-			LLFace *facep = *iter;
-			if (facep->mSkipRender)
+			if (params.mReflectionMap.notNull())
 			{
-				continue;
+				params.mReflectionMap->bind();
 			}
-			
-			const LLTextureEntry* te = facep->getTextureEntry();
-			if (te)
+			else
 			{
-				U8 index = te->getShiny();
-				if( index > 0 )
+				if (sCubeMap)
 				{
-					LLOverrideFaceColor override_color(facep->getPool(), 1, 1, 1, alpha[index]);
-				
-					// Draw the geometry
-					facep->enableLights();
-					res += facep->renderIndexed(index_array);
-					stop_glerror();
+					sCubeMap->bind();
+				}
+				else
+				{
+					gSky.mVOSkyp->getCubeMap()->bind();
 				}
-			}
-			else
-			{
-				llwarns << "DrawPoolBump has face with invalid texture entry." << llendl;
 			}
 		}
+		
+		params.mVertexBuffer->setBuffer(mask);
+		U32* indices_pointer = (U32*) params.mVertexBuffer->getIndicesPointer();
+		glDrawRangeElements(GL_TRIANGLES, params.mStart, params.mEnd, params.mCount,
+							GL_UNSIGNED_INT, indices_pointer+params.mOffset);
+		gPipeline.mTrianglesDrawn += params.mCount/3;
 	}
-	return res;
 }
 
-//static
-void LLDrawPoolBump::endPass1()
+void LLDrawPoolBump::endShiny()
 {
 	LLCubeMap* cube_map = gSky.mVOSkyp->getCubeMap();
 	if( cube_map )
 	{
 		cube_map->disable();
 		cube_map->restoreMatrix();
+
+		if (gPipeline.getVertexShaderLevel(LLPipeline::SHADER_OBJECT) > 0)
+		{
+			gPipeline.mObjectShinyProgram.unbind();
+		}
+
 		glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,		GL_MODULATE);
 	}
 	
 	LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
-	
+	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
 	glDisableClientState(GL_NORMAL_ARRAY);
+	glDisableClientState(GL_COLOR_ARRAY);
 }
 
 
 // static
-LLImageGL* LLDrawPoolBump::getBumpMap(const LLTextureEntry* te, LLViewerImage* tex)
+BOOL LLDrawPoolBump::bindBumpMap(LLDrawInfo& params)
 {
-	U32 bump_code = te->getBumpmap();
 	LLImageGL* bump = NULL;
 
+	U8 bump_code = params.mBump;
+	LLViewerImage* tex = params.mTexture;
+
 	switch( bump_code )
 	{
 	case BE_NO_BUMP:
@@ -579,28 +401,33 @@ LLImageGL* LLDrawPoolBump::getBumpMap(const LLTextureEntry* te, LLViewerImage* t
 		if( bump_code < LLStandardBumpmap::sStandardBumpmapCount )
 		{
 			bump = gStandardBumpmapList[bump_code].mImage;
+			gBumpImageList.addTextureStats(bump_code, tex->getID(), params.mVSize, 1, 1);
 		}
 		break;
 	}
 
-	return bump;
+	if (bump)
+	{
+		bump->bind(1);
+		bump->bind(0);
+		return TRUE;
+	}
+	return FALSE;
 }
 
 //static
-void LLDrawPoolBump::beginPass2()
+void LLDrawPoolBump::beginBump()
 {	
+	sVertexMask = VERTEX_MASK_BUMP;
 	LLFastTimer t(LLFastTimer::FTM_RENDER_BUMP);
-	// Optional third pass: emboss bump map
+	// Optional second pass: emboss bump map
 	stop_glerror();
 
 	// TEXTURE UNIT 0
 	// Output.rgb = texture at texture coord 0
 	glActiveTextureARB(GL_TEXTURE0_ARB);
 	glClientActiveTextureARB(GL_TEXTURE0_ARB);
-
-	glEnableClientState(GL_VERTEX_ARRAY);
 	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
-	glDisableClientState(GL_NORMAL_ARRAY);
 
 	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,	GL_COMBINE_ARB);
 	glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB,	GL_REPLACE);
@@ -613,14 +440,10 @@ void LLDrawPoolBump::beginPass2()
 	glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB,		GL_TEXTURE);
 	glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB,	GL_SRC_ALPHA);
 
-
 	// TEXTURE UNIT 1
 	glActiveTextureARB(GL_TEXTURE1_ARB);
 	glClientActiveTextureARB(GL_TEXTURE1_ARB);
-	
-	glEnableClientState(GL_VERTEX_ARRAY);
 	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
-	glDisableClientState(GL_NORMAL_ARRAY);
 
 	glEnable(GL_TEXTURE_2D); // Texture unit 1
 
@@ -650,68 +473,25 @@ void LLDrawPoolBump::beginPass2()
 	//		= dst.rgb + dst.rgb * (bump0 - bump1)
 	glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
 //	glBlendFunc(GL_ONE, GL_ZERO);  // temp
-
+	glActiveTextureARB(GL_TEXTURE0_ARB);
 	stop_glerror();
 }
 
 //static
-S32 LLDrawPoolBump::renderPass2(face_array_t& face_list, const U32* index_array, LLViewerImage* tex)
+void LLDrawPoolBump::renderBump()
 {
-	if (gPipeline.getVertexShaderLevel(LLPipeline::SHADER_OBJECT) > 0) //everything happens in pass0
-	{
-		return 0;
-	}
-
+	LLFastTimer ftm(LLFastTimer::FTM_RENDER_BUMP);
 	LLGLDisable fog(GL_FOG);
 	LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE, GL_EQUAL);
 	LLGLEnable tex2d(GL_TEXTURE_2D);
 	LLGLEnable blend(GL_BLEND);
-	S32 res = 0;
-
-	LLImageGL* last_bump = NULL;
-
-	for (std::vector<LLFace*>::iterator iter = face_list.begin();
-		 iter != face_list.end(); iter++)
-	{
-		LLFace *facep = *iter;
-		if (facep->mSkipRender)
-		{
-			continue;
-		}
-		LLOverrideFaceColor override_color(facep->getPool(), 1,1,1,1);
-	
-		const LLTextureEntry* te = facep->getTextureEntry();
-		LLImageGL* bump = getBumpMap(te, tex);
-		
-		if( bump )
-		{
-			if( bump != last_bump )
-			{
-				last_bump = bump;
-
-				// Texture unit 0
-				bump->bind(0);
-				stop_glerror();
-
-				// Texture unit 1
-				bump->bind(1);
-				stop_glerror();
-			}
-
-			// Draw the geometry
-			res += facep->renderIndexed(index_array);
-			stop_glerror();
-		}
-		else
-		{
-// 			llwarns << "Skipping invalid bump code " << (S32) te->getBumpmap() << llendl;
-		}
-	}
-	return res;
+	glColor4f(1,1,1,1);
+	renderBump(LLRenderPass::PASS_BUMP, sVertexMask);
+	renderBumpActive(LLRenderPass::PASS_BUMP, sVertexMask);
 }
 
 //static
-void LLDrawPoolBump::endPass2()
+void LLDrawPoolBump::endBump()
 {
 	// Disable texture unit 1
 	glActiveTextureARB(GL_TEXTURE1_ARB);
@@ -729,67 +509,6 @@ void LLDrawPoolBump::endPass2()
 	glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
 }
 
-
-void LLDrawPoolBump::renderForSelect()
-{
-	if (mDrawFace.empty() || !mMemory.count())
-	{
-		return;
-	}
-
-	glEnableClientState ( GL_VERTEX_ARRAY );
-
-	bindGLVertexPointer();
-
-	for (std::vector<LLFace*>::iterator iter = mDrawFace.begin();
-		 iter != mDrawFace.end(); iter++)
-	{
-		LLFace *facep = *iter;
-		if (facep->getDrawable() && !facep->getDrawable()->isDead() && (facep->getViewerObject()->mGLName))
-		{
-			facep->renderForSelect();
-		}
-	}
-}
-
-
-void LLDrawPoolBump::renderFaceSelected(LLFace *facep, 
-										LLImageGL *image, 
-										const LLColor4 &color,
-										const S32 index_offset, const S32 index_count)
-{
-	facep->renderSelected(image, color, index_offset, index_count);
-}
-
-
-void LLDrawPoolBump::dirtyTexture(const LLViewerImage *texturep)
-{
-	if (mTexturep == texturep)
-	{
-		for (std::vector<LLFace*>::iterator iter = mReferences.begin();
-			 iter != mReferences.end(); iter++)
-		{
-			LLFace *facep = *iter;
-			gPipeline.markTextured(facep->getDrawable());
-		}
-	}
-}
-
-LLViewerImage *LLDrawPoolBump::getTexture()
-{
-	return mTexturep;
-}
-
-LLViewerImage *LLDrawPoolBump::getDebugTexture()
-{
-	return mTexturep;
-}
-
-LLColor3 LLDrawPoolBump::getDebugColor() const
-{
-	return LLColor3(1.f, 1.f, 0.f);
-}
-
 ////////////////////////////////////////////////////////////////
 // List of one-component bump-maps created from other texures.
 
@@ -1113,25 +832,93 @@ void LLBumpImageList::onSourceLoaded( BOOL success, LLViewerImage *src_vi, LLIma
 	}
 }
 
-S32 LLDrawPoolBump::getMaterialAttribIndex()
+void LLDrawPoolBump::renderBumpActive(U32 type, U32 mask)
 {
-	return gPipeline.mObjectBumpProgram.mAttribute[LLPipeline::GLSL_MATERIAL_COLOR];
+#if !LL_RELEASE_FOR_DOWNLOAD
+	LLGLState::checkClientArrays(mask);
+#endif
+
+	LLSpatialBridge* last_bridge = NULL;
+	glPushMatrix();
+	
+	for (LLSpatialGroup::sg_vector_t::iterator i = gPipeline.mActiveGroups.begin(); i != gPipeline.mActiveGroups.end(); ++i)
+	{
+		LLSpatialGroup* group = *i;
+		if (!group->isDead() && 
+			group->mSpatialPartition->mRenderByGroup &&
+			group->mDrawMap.find(type) != group->mDrawMap.end())
+		{
+			LLSpatialBridge* bridge = (LLSpatialBridge*) group->mSpatialPartition;
+			if (bridge != last_bridge)
+			{
+				glPopMatrix();
+				glPushMatrix();
+				glMultMatrixf((F32*) bridge->mDrawable->getRenderMatrix().mMatrix);
+				last_bridge = bridge;
+			}
+
+			renderGroupBump(group,type,mask);
+		}
+	}
+	
+	glPopMatrix();
 }
 
-// virtual
-void LLDrawPoolBump::enableShade()
-{
-	glDisableClientState(GL_COLOR_ARRAY);
+void LLDrawPoolBump::renderBump(U32 type, U32 mask)
+{	
+#if !LL_RELEASE_FOR_DOWNLOAD
+	LLGLState::checkClientArrays(mask);
+#endif
+
+	std::vector<LLDrawInfo*>& draw_info = gPipeline.mRenderMap[type];
+
+	for (std::vector<LLDrawInfo*>::iterator i = draw_info.begin(); i != draw_info.end(); ++i)
+	{
+		LLDrawInfo& params = **i;
+
+		if (LLDrawPoolBump::bindBumpMap(params))
+		{
+			pushBatch(params, mask, FALSE);
+		}
+	}
 }
 
-// virtual
-void LLDrawPoolBump::disableShade()
-{
-	glEnableClientState(GL_COLOR_ARRAY);
+void LLDrawPoolBump::renderGroupBump(LLSpatialGroup* group, U32 type, U32 mask)
+{					
+	const std::vector<LLDrawInfo*>& draw_info = group->mDrawMap[type];
+	
+	for (std::vector<LLDrawInfo*>::const_iterator k = draw_info.begin(); k != draw_info.end(); ++k)
+	{
+		LLDrawInfo& params = **k;
+		
+		if (LLDrawPoolBump::bindBumpMap(params))
+		{
+			pushBatch(params, mask, FALSE);
+		}
+	}
 }
 
-// virtual
-void LLDrawPoolBump::setShade(F32 shade)
+void LLDrawPoolBump::pushBatch(LLDrawInfo& params, U32 mask, BOOL texture)
 {
-	glColor4f(0,0,0,shade);
+	if (params.mTextureMatrix)
+	{
+		glActiveTextureARB(GL_TEXTURE1_ARB);
+		glMatrixMode(GL_TEXTURE);
+		glLoadMatrixf((GLfloat*) params.mTextureMatrix->mMatrix);
+		glActiveTextureARB(GL_TEXTURE0_ARB);
+		glLoadMatrixf((GLfloat*) params.mTextureMatrix->mMatrix);
+	}
+	params.mVertexBuffer->setBuffer(mask);
+	U32* indices_pointer = (U32*) params.mVertexBuffer->getIndicesPointer();
+	glDrawRangeElements(GL_TRIANGLES, params.mStart, params.mEnd, params.mCount,
+						GL_UNSIGNED_INT, indices_pointer+params.mOffset);
+	gPipeline.mTrianglesDrawn += params.mCount/3;
+	if (params.mTextureMatrix)
+	{
+		glActiveTextureARB(GL_TEXTURE1_ARB);
+		glLoadIdentity();
+		glActiveTextureARB(GL_TEXTURE0_ARB);
+		glLoadIdentity();
+		glMatrixMode(GL_MODELVIEW);
+	}
 }
diff --git a/indra/newview/lldrawpoolbump.h b/indra/newview/lldrawpoolbump.h
index b74acb4561dd980ade8470b91dd453036e49f57d..6376dd8d33ccf4e173e008a07ebb37953469f4ae 100644
--- a/indra/newview/lldrawpoolbump.h
+++ b/indra/newview/lldrawpoolbump.h
@@ -15,57 +15,41 @@
 #include "lluuid.h"
 
 class LLImageRaw;
+class LLSpatialGroup;
+class LLDrawInfo;
 
-class LLDrawPoolBump : public LLDrawPool
+class LLDrawPoolBump : public LLRenderPass
 {
-protected:
-	LLPointer<LLViewerImage> mTexturep;  // The primary texture, not the bump texture
-
 public:
-	LLDrawPoolBump(LLViewerImage *texturep);
+	static U32 sVertexMask;
+
+	virtual U32 getVertexDataMask() { return sVertexMask; }
 
-	/*virtual*/ LLDrawPool *instancePool();
+	LLDrawPoolBump();
 
 	/*virtual*/ void render(S32 pass = 0);
 	/*virtual*/ void beginRenderPass( S32 pass );
 	/*virtual*/ void endRenderPass( S32 pass );
 	/*virtual*/ S32	 getNumPasses();
-	/*virtual*/ void renderFaceSelected(LLFace *facep, LLImageGL *image, const LLColor4 &color,
-										const S32 index_offset = 0, const S32 index_count = 0);
 	/*virtual*/ void prerender();
-	/*virtual*/ void renderForSelect();
-	/*virtual*/ void dirtyTexture(const LLViewerImage *texturep);
-	/*virtual*/ LLViewerImage *getTexture();
-	/*virtual*/ LLViewerImage *getDebugTexture();
-	/*virtual*/ LLColor3 getDebugColor() const; // For AGP debug display
-	/*virtual*/ BOOL match(LLFace* last_face, LLFace* facep);
+	/*virtual*/ void pushBatch(LLDrawInfo& params, U32 mask, BOOL texture);
+
+	void renderBump(U32 type, U32 mask);
+	void renderBumpActive(U32 type, U32 mask);
+	void renderGroup(LLSpatialGroup* group, U32 type, U32 mask, BOOL texture);
+	void renderGroupBump(LLSpatialGroup* group, U32 type, U32 mask);
 	
-	virtual S32 getMaterialAttribIndex();
-	static S32 numBumpPasses();
+	S32 numBumpPasses();
 	
-	static void beginPass0(LLDrawPool* pool);
-	static S32  renderPass0(LLDrawPool* pool, face_array_t& face_list, const U32* index_array, LLViewerImage* tex);
-	static void endPass0(LLDrawPool* pool);
-
-	static void beginPass1();
-	static S32  renderPass1(face_array_t& face_list, const U32* index_array, LLViewerImage* tex);
-	static void endPass1();
-
-	static void beginPass2();
-	static S32  renderPass2(face_array_t& face_list, const U32* index_array, LLViewerImage* tex);
-	static void endPass2();
-
-	/*virtual*/ void enableShade();
-	/*virtual*/ void disableShade();
-	/*virtual*/ void setShade(F32 shade);
-
-protected:
-	static LLImageGL* getBumpMap(const LLTextureEntry* te, LLViewerImage* tex);
-
-public:
-	static S32 sBumpTex;
-	static S32 sDiffTex;
-	static S32 sEnvTex;
+	void beginShiny();
+	void renderShiny();
+	void endShiny();
+	void renderActive(U32 type, U32 mask, BOOL texture = TRUE);
+
+	void beginBump();
+	void renderBump();
+	void endBump();
+	BOOL bindBumpMap(LLDrawInfo& params);
 };
 
 enum EBumpEffect
diff --git a/indra/newview/lldrawpoolclouds.cpp b/indra/newview/lldrawpoolclouds.cpp
index c279f085d55e60f99c5ae1d282b1283e935c5405..d611338248191475c8ece973e3f51b9a0ea4047e 100644
--- a/indra/newview/lldrawpoolclouds.cpp
+++ b/indra/newview/lldrawpoolclouds.cpp
@@ -17,7 +17,7 @@
 #include "pipeline.h"
 
 LLDrawPoolClouds::LLDrawPoolClouds() :
-	LLDrawPool(POOL_CLOUDS, DATA_SIMPLE_IL_MASK, 0)
+	LLDrawPool(POOL_CLOUDS)
 {
 }
 
@@ -26,16 +26,15 @@ LLDrawPool *LLDrawPoolClouds::instancePool()
 	return new LLDrawPoolClouds();
 }
 
+BOOL LLDrawPoolClouds::addFace(LLFace* face)
+{
+	llerrs << "WTF?" << llendl;
+	return FALSE;
+}
+
 void LLDrawPoolClouds::enqueue(LLFace *facep)
 {
-	if (facep->isState(LLFace::BACKLIST))
-	{
-		mMoveFace.put(facep);
-	}
-	else
-	{
-		mDrawFace.push_back(facep);
-	}
+	mDrawFace.push_back(facep);
 	facep->mDistance = (facep->mCenterAgent - gCamera->getOrigin()) * gCamera->getAtAxis();
 }
 
@@ -71,10 +70,6 @@ void LLDrawPoolClouds::render(S32 pass)
 	gPipeline.enableLightsFullbright(LLColor4(1.f,1.f,1.f));
 
 	mDrawFace[0]->bindTexture();
-	
-	bindGLVertexPointer();
-	bindGLTexCoordPointer();
-	bindGLNormalPointer();
 
 	std::sort(mDrawFace.begin(), mDrawFace.end(), LLFace::CompareDistanceGreater());
 
diff --git a/indra/newview/lldrawpoolclouds.h b/indra/newview/lldrawpoolclouds.h
index d333444400822554e87aeafdebb0b01ed3203cdb..fd04f3b2ecdd91efc5635914ca82941911ae984c 100644
--- a/indra/newview/lldrawpoolclouds.h
+++ b/indra/newview/lldrawpoolclouds.h
@@ -14,6 +14,16 @@
 class LLDrawPoolClouds : public LLDrawPool
 {
 public:
+	enum
+	{
+		VERTEX_DATA_MASK =	LLVertexBuffer::MAP_VERTEX |
+							LLVertexBuffer::MAP_NORMAL |
+							LLVertexBuffer::MAP_TEXCOORD
+	};
+
+	BOOL addFace(LLFace* face);
+	virtual U32 getVertexDataMask() { return VERTEX_DATA_MASK; }
+
 	LLDrawPoolClouds();
 
 	/*virtual*/ void prerender();
diff --git a/indra/newview/lldrawpoolground.cpp b/indra/newview/lldrawpoolground.cpp
index 91e92bab6e3e3ff1cb1671374d72b5a60e249782..edbb11ad7908778c596cc33c3797b4f441850ea2 100644
--- a/indra/newview/lldrawpoolground.cpp
+++ b/indra/newview/lldrawpoolground.cpp
@@ -12,7 +12,6 @@
 
 #include "llviewercontrol.h"
 
-#include "llagparray.h"
 #include "lldrawable.h"
 #include "llface.h"
 #include "llsky.h"
@@ -20,9 +19,11 @@
 #include "llviewerwindow.h"
 #include "llworld.h"
 #include "pipeline.h"
+#include "llagent.h"
+#include "llviewerregion.h"
 
 LLDrawPoolGround::LLDrawPoolGround() :
-	LLDrawPool(POOL_GROUND, DATA_SIMPLE_IL_MASK, DATA_SIMPLE_NIL_MASK)
+	LLFacePool(POOL_GROUND)
 {
 }
 
@@ -41,42 +42,38 @@ void LLDrawPoolGround::render(S32 pass)
 	if (mDrawFace.empty())
 	{
 		return;
-	}
-
+	}	
+	
 	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
 	glEnableClientState(GL_VERTEX_ARRAY);
-	glDisableClientState(GL_NORMAL_ARRAY);
-
-	bindGLVertexPointer();
-	bindGLTexCoordPointer();
 
 	LLGLSPipelineSkyBox gls_skybox;
+	LLGLDisable tex(GL_TEXTURE_2D);
 	LLGLDepthTest gls_depth(GL_FALSE, GL_FALSE);
 
 	glMatrixMode( GL_PROJECTION );
-
+		
 	glPushMatrix();
-	gViewerWindow->setup3DRender();
+	//gViewerWindow->setup3DRender();
 
 	glMatrixMode(GL_MODELVIEW);
 
-	LLGLState tex2d(GL_TEXTURE_2D, (mVertexShaderLevel > 0) ? TRUE : FALSE);
-	LLViewerImage::bindTexture(gSky.mVOSkyp->getScatterMap(), 0);
+	F32 water_height = gAgent.getRegion()->getWaterHeight();
+	glPushMatrix();
+	LLVector3 origin = gCamera->getOrigin();
+	glTranslatef(origin.mV[0], origin.mV[1], llmax(origin.mV[2], water_height));
 
 	LLFace *facep = mDrawFace[0];
 
-	if (!(mVertexShaderLevel > 0))
-	{
-		gPipeline.disableLights();
-	}
-
-	glColor4fv(facep->getFaceColor().mV);	
+	gPipeline.disableLights();
 
-	facep->renderIndexed(getRawIndices());
+	LLOverrideFaceColor col(this, gSky.mVOSkyp->getGLFogColor());
+	facep->renderIndexed();
 	
 	glMatrixMode( GL_PROJECTION );
 	glPopMatrix();
 	glMatrixMode( GL_MODELVIEW );
+	glPopMatrix();
 }
 
 void LLDrawPoolGround::renderForSelect()
diff --git a/indra/newview/lldrawpoolground.h b/indra/newview/lldrawpoolground.h
index 8b2dbf4353190a828a3b974ddf22d748e01f9ed8..3436b9aabde0c5eb5b202bc8bac842995051871d 100644
--- a/indra/newview/lldrawpoolground.h
+++ b/indra/newview/lldrawpoolground.h
@@ -12,9 +12,17 @@
 #include "lldrawpool.h"
 
 
-class LLDrawPoolGround : public LLDrawPool
+class LLDrawPoolGround : public LLFacePool
 {
 public:
+	enum
+	{
+		VERTEX_DATA_MASK =	LLVertexBuffer::MAP_VERTEX |
+							LLVertexBuffer::MAP_TEXCOORD
+	};
+
+	virtual U32 getVertexDataMask() { return VERTEX_DATA_MASK; }
+
 	LLDrawPoolGround();
 
 	/*virtual*/ LLDrawPool *instancePool();
diff --git a/indra/newview/lldrawpoolsimple.cpp b/indra/newview/lldrawpoolsimple.cpp
index 600e4b1fc6aea7558e140ac340514e400b5b3416..fcd466bfc48a1f5f3c57149d7cc939def3513fe8 100644
--- a/indra/newview/lldrawpoolsimple.cpp
+++ b/indra/newview/lldrawpoolsimple.cpp
@@ -11,57 +11,16 @@
 #include "lldrawpoolsimple.h"
 
 #include "llagent.h"
-#include "llagparray.h"
 #include "lldrawable.h"
 #include "llface.h"
 #include "llsky.h"
 #include "pipeline.h"
 
-S32 LLDrawPoolSimple::sDiffTex = 0;
-
-LLDrawPoolSimple::LLDrawPoolSimple(LLViewerImage *texturep) :
-	LLDrawPool(POOL_SIMPLE,
-			   DATA_SIMPLE_IL_MASK | DATA_COLORS_MASK,
-			   DATA_SIMPLE_NIL_MASK),  // ady temp
-	mTexturep(texturep)
+LLDrawPoolSimple::LLDrawPoolSimple() :
+	LLRenderPass(POOL_SIMPLE)
 {
 }
 
-LLDrawPool *LLDrawPoolSimple::instancePool()
-{
-	return new LLDrawPoolSimple(mTexturep);
-}
-
-BOOL LLDrawPoolSimple::match(LLFace* last_face, LLFace* facep)  
-{
-	if (gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_CHAIN_FACES) &&
-		!last_face->isState(LLFace::LIGHT | LLFace::FULLBRIGHT) &&
-		!facep->isState(LLFace::LIGHT | LLFace::FULLBRIGHT) &&
-		facep->getIndicesStart() == last_face->getIndicesStart()+last_face->getIndicesCount() &&
-		facep->getRenderColor() == last_face->getRenderColor())
-	{
-		if (facep->isState(LLFace::GLOBAL))
-		{
-			if (last_face->isState(LLFace::GLOBAL))
-			{
-				return TRUE;
-			}
-		}
-		else
-		{
-			if (!last_face->isState(LLFace::GLOBAL))
-			{
-				if (last_face->getRenderMatrix() == facep->getRenderMatrix())
-				{
-					return TRUE;
-				}
-			}
-		}
-	}
-	
-	return FALSE;
-}
-
 void LLDrawPoolSimple::prerender()
 {
 	mVertexShaderLevel = gPipeline.getVertexShaderLevel(LLPipeline::SHADER_OBJECT);
@@ -72,156 +31,50 @@ void LLDrawPoolSimple::beginRenderPass(S32 pass)
 	glEnableClientState(GL_VERTEX_ARRAY);
 	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
 	glEnableClientState(GL_NORMAL_ARRAY);
-	if (gPipeline.getLightingDetail() >= 2)
-	{
-		glEnableClientState(GL_COLOR_ARRAY);
-	}
-
-	if (mVertexShaderLevel > 0)
-	{
-		S32 scatterTex = gPipeline.mObjectSimpleProgram.enableTexture(LLPipeline::GLSL_SCATTER_MAP);
-		LLViewerImage::bindTexture(gSky.mVOSkyp->getScatterMap(), scatterTex);
-		sDiffTex = gPipeline.mObjectSimpleProgram.enableTexture(LLPipeline::GLSL_DIFFUSE_MAP);
-	}
+	glEnableClientState(GL_COLOR_ARRAY);
 }
 
-
 void LLDrawPoolSimple::render(S32 pass)
 {
-	LLFastTimer t(LLFastTimer::FTM_RENDER_SIMPLE);
-	if (mDrawFace.empty())
-	{
-		return;
-	}
-
-	bindGLVertexPointer();
-	bindGLTexCoordPointer();
-	bindGLNormalPointer();
-	if (gPipeline.getLightingDetail() >= 2)
-	{
-		bindGLColorPointer();
-	}
-
-	LLViewerImage* tex = getTexture();
-	LLGLState alpha_test(GL_ALPHA_TEST, FALSE);
-	LLGLState blend(GL_BLEND, FALSE);
-
-	if (tex)
-	{
-		LLViewerImage::bindTexture(tex,sDiffTex);
-		if (tex->getPrimaryFormat() == GL_ALPHA)
-		{
-			// Enable Invisibility Hack
-			alpha_test.enable();
-			blend.enable();
-		}
-	}
-	else
-	{
-		LLImageGL::unbindTexture(sDiffTex, GL_TEXTURE_2D);
-	}
-
-	drawLoop();
-}
-
-void LLDrawPoolSimple::endRenderPass(S32 pass)
-{
-	if (mVertexShaderLevel > 0)
-	{
-		gPipeline.mObjectSimpleProgram.disableTexture(LLPipeline::GLSL_SCATTER_MAP);
-		gPipeline.mObjectSimpleProgram.disableTexture(LLPipeline::GLSL_DIFFUSE_MAP);
-		glActiveTextureARB(GL_TEXTURE0_ARB);
-		glEnable(GL_TEXTURE_2D);
-	}
+	LLGLDisable blend(GL_BLEND);
+	LLGLDisable alpha_test(GL_ALPHA_TEST);
 	
-	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
-	glDisableClientState(GL_NORMAL_ARRAY);
-	if (gPipeline.getLightingDetail() >= 2)
 	{
-		glDisableClientState(GL_COLOR_ARRAY);
+		LLFastTimer t(LLFastTimer::FTM_RENDER_SIMPLE);
+		gPipeline.enableLightsDynamic(1.f);
+		renderTexture(LLRenderPass::PASS_SIMPLE, getVertexDataMask());
+		renderActive(LLRenderPass::PASS_SIMPLE, getVertexDataMask());
 	}
-}
 
-void LLDrawPoolSimple::renderForSelect()
-{
-	if (mDrawFace.empty() || !mMemory.count())
 	{
-		return;
+		LLFastTimer t(LLFastTimer::FTM_RENDER_GRASS);
+		LLGLEnable blend(GL_BLEND);
+		LLGLEnable alpha_test(GL_ALPHA_TEST);
+		glAlphaFunc(GL_GREATER, 0.5f);
+		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+		//render grass
+		LLRenderPass::renderTexture(LLRenderPass::PASS_GRASS, getVertexDataMask());
+		glAlphaFunc(GL_GREATER, 0.01f);
 	}
-
-	glEnableClientState ( GL_VERTEX_ARRAY );
-
-	bindGLVertexPointer();
-
-	for (std::vector<LLFace*>::iterator iter = mDrawFace.begin();
-		 iter != mDrawFace.end(); iter++)
+	
 	{
-		LLFace *facep = *iter;
-		LLDrawable *drawable = facep->getDrawable();
-		if (drawable && !drawable->isDead() && (facep->getViewerObject()->mGLName))
-		{
-			facep->renderForSelect();
-		}
+		LLFastTimer t(LLFastTimer::FTM_RENDER_FULLBRIGHT);
+		U32 fullbright_mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD | LLVertexBuffer::MAP_COLOR;
+		gPipeline.enableLightsFullbright(LLColor4(1,1,1,1));
+		glDisableClientState(GL_NORMAL_ARRAY);
+		renderTexture(LLRenderPass::PASS_FULLBRIGHT, fullbright_mask);
+		renderActive(LLRenderPass::PASS_FULLBRIGHT, fullbright_mask);
 	}
-}
-
-
-void LLDrawPoolSimple::renderFaceSelected(LLFace *facep, 
-									LLImageGL *image, 
-									const LLColor4 &color,
-									const S32 index_offset, const S32 index_count)
-{
-	facep->renderSelected(image, color, index_offset, index_count);
-}
-
 
-void LLDrawPoolSimple::dirtyTexture(const LLViewerImage *texturep)
-{
-	if (mTexturep == texturep)
 	{
-		for (std::vector<LLFace*>::iterator iter = mReferences.begin();
-			 iter != mReferences.end(); iter++)
-		{
-			LLFace *facep = *iter;
-			gPipeline.markTextured(facep->getDrawable());
-		}
+		LLFastTimer t(LLFastTimer::FTM_RENDER_INVISIBLE);
+		U32 invisi_mask = LLVertexBuffer::MAP_VERTEX;
+		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+		glDisableClientState(GL_COLOR_ARRAY);
+		glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+		renderInvisible(invisi_mask);
+		renderActive(LLRenderPass::PASS_INVISIBLE, invisi_mask);
+		glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
 	}
 }
 
-LLViewerImage *LLDrawPoolSimple::getTexture()
-{
-	return mTexturep;
-}
-
-LLViewerImage *LLDrawPoolSimple::getDebugTexture()
-{
-	return mTexturep;
-}
-
-LLColor3 LLDrawPoolSimple::getDebugColor() const
-{
-	return LLColor3(1.f, 1.f, 1.f);
-}
-
-S32 LLDrawPoolSimple::getMaterialAttribIndex()
-{
-	return gPipeline.mObjectSimpleProgram.mAttribute[LLPipeline::GLSL_MATERIAL_COLOR];
-}
-
-// virtual
-void LLDrawPoolSimple::enableShade()
-{
-	glDisableClientState(GL_COLOR_ARRAY);
-}
-
-// virtual
-void LLDrawPoolSimple::disableShade()
-{
-	glEnableClientState(GL_COLOR_ARRAY);
-}
-
-// virtual
-void LLDrawPoolSimple::setShade(F32 shade)
-{
-	glColor4f(0,0,0,shade);
-}
diff --git a/indra/newview/lldrawpoolsimple.h b/indra/newview/lldrawpoolsimple.h
index 41c4580ceae9c4872aa576951bf6a9da90f59ab2..f89230d8662fa853ece5a42eae428b3828bb3dad 100644
--- a/indra/newview/lldrawpoolsimple.h
+++ b/indra/newview/lldrawpoolsimple.h
@@ -11,64 +11,24 @@
 
 #include "lldrawpool.h"
 
-class LLFRInfo
+class LLDrawPoolSimple : public LLRenderPass
 {
-public:
-	U32 mPrimType;
-	U32 mGeomIndex;
-	U32 mGeomIndexEnd;
-	U32 mNumIndices;
-	U32 mIndicesStart;
-
-	LLFRInfo()
-	{
-	}
-
-	LLFRInfo(const U32 pt, const U32 gi, const U32 gc, const U32 ni, const U32 is) :
-		mPrimType(pt),
-		mGeomIndex(gi),
-		mGeomIndexEnd(gi+gc),
-		mNumIndices(ni),
-		mIndicesStart(is)
-	{
-	}
-};
-
-class LLDrawPoolSimple : public LLDrawPool
-{
-	LLPointer<LLViewerImage> mTexturep;
 public:
 	enum
 	{
-		SHADER_LEVEL_LOCAL_LIGHTS = 2
+		VERTEX_DATA_MASK =	LLVertexBuffer::MAP_VERTEX |
+							LLVertexBuffer::MAP_NORMAL |
+							LLVertexBuffer::MAP_TEXCOORD |
+							LLVertexBuffer::MAP_COLOR
 	};
-	
-	LLDrawPoolSimple(LLViewerImage *texturep);
-
-	/*virtual*/ LLDrawPool *instancePool();
+	virtual U32 getVertexDataMask() { return VERTEX_DATA_MASK; }
 
+	LLDrawPoolSimple();
+	
 	/*virtual*/ void beginRenderPass(S32 pass);
-	/*virtual*/ void endRenderPass(S32 pass);
 	/*virtual*/ void render(S32 pass = 0);
-	/*virtual*/ void renderFaceSelected(LLFace *facep, 
-									LLImageGL *image, 
-									const LLColor4 &color,
-									const S32 index_offset = 0, const S32 index_count = 0);
 	/*virtual*/ void prerender();
-	/*virtual*/ void renderForSelect();
-	/*virtual*/ void dirtyTexture(const LLViewerImage *texturep);
-	/*virtual*/ LLViewerImage *getTexture();
-	/*virtual*/ LLViewerImage *getDebugTexture();
-	/*virtual*/ LLColor3 getDebugColor() const; // For AGP debug display
-	/*virtual*/ BOOL match(LLFace* last_face, LLFace* facep);
-
-	/*virtual*/ void enableShade();
-	/*virtual*/ void disableShade();
-	/*virtual*/ void setShade(F32 shade);
-
-	virtual S32 getMaterialAttribIndex();
 
-	static S32 sDiffTex;
 };
 
 #endif // LL_LLDRAWPOOLSIMPLE_H
diff --git a/indra/newview/lldrawpoolsky.cpp b/indra/newview/lldrawpoolsky.cpp
index 96eb8ea7219783c38abf67a163d9d47ac6af12be..80340f63b98cb70a79dc3946fa7e6e081348942f 100644
--- a/indra/newview/lldrawpoolsky.cpp
+++ b/indra/newview/lldrawpoolsky.cpp
@@ -12,7 +12,6 @@
 
 #include "imageids.h"
 
-#include "llagparray.h"
 #include "llagent.h"
 #include "lldrawable.h"
 #include "llface.h"
@@ -26,7 +25,7 @@
 #include "pipeline.h"
 
 LLDrawPoolSky::LLDrawPoolSky() :
-	LLDrawPool(POOL_SKY, DATA_SIMPLE_IL_MASK, DATA_SIMPLE_NIL_MASK)
+	LLFacePool(POOL_SKY)
 {
 }
 
@@ -62,15 +61,17 @@ void LLDrawPoolSky::render(S32 pass)
 	glMatrixMode( GL_PROJECTION );
 
 	glPushMatrix();
-	gViewerWindow->setup3DRender();
+	//gViewerWindow->setup3DRender();
+
+	glMatrixMode(GL_MODELVIEW);
+	glPushMatrix();
+	LLVector3 origin = gCamera->getOrigin();
+	glTranslatef(origin.mV[0], origin.mV[1], origin.mV[2]);
 
 	glEnableClientState(GL_VERTEX_ARRAY);
 	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
 	glDisableClientState(GL_NORMAL_ARRAY);
 
-	bindGLVertexPointer();
-	bindGLTexCoordPointer();
-
 	S32 face_count = (S32)mDrawFace.size();
 
 	for (S32 i = 0; i < llmin(6, face_count); ++i)
@@ -78,13 +79,13 @@ void LLDrawPoolSky::render(S32 pass)
 		renderSkyCubeFace(i);
 	}
 	
-	const LLFace *hbfaces[3];
+	LLFace *hbfaces[3];
 	hbfaces[0] = NULL;
 	hbfaces[1] = NULL;
 	hbfaces[2] = NULL;
 	for (S32 curr_face = 0; curr_face < face_count; curr_face++)
 	{
-		const LLFace* facep = mDrawFace[curr_face];
+		LLFace* facep = mDrawFace[curr_face];
 		if (voskyp->isSameFace(LLVOSky::FACE_SUN, facep))
 		{
 			hbfaces[0] = facep;
@@ -118,11 +119,12 @@ void LLDrawPoolSky::render(S32 pass)
 	glMatrixMode( GL_PROJECTION );
 	glPopMatrix();
 	glMatrixMode( GL_MODELVIEW );
+	glPopMatrix();
 }
 
 void LLDrawPoolSky::renderSkyCubeFace(U8 side)
 {
-	const LLFace &face = *mDrawFace[LLVOSky::FACE_SIDE0 + side];
+	LLFace &face = *mDrawFace[LLVOSky::FACE_SIDE0 + side];
 	if (!face.getGeomCount())
 	{
 		return;
@@ -130,20 +132,20 @@ void LLDrawPoolSky::renderSkyCubeFace(U8 side)
 
 	mSkyTex[side].bindTexture(TRUE);
 	
-	face.renderIndexed(getRawIndices());
+	face.renderIndexed();
 
 	if (LLSkyTex::doInterpolate())
 	{
 		LLGLEnable blend(GL_BLEND);
 		mSkyTex[side].bindTexture(FALSE);
 		glColor4f(1, 1, 1, LLSkyTex::getInterpVal()); // lighting is disabled
-		face.renderIndexed(getRawIndices());
+		face.renderIndexed();
 	}
 
 	mIndicesDrawn += face.getIndicesCount();
 }
 
-void LLDrawPoolSky::renderHeavenlyBody(U8 hb, const LLFace* face)
+void LLDrawPoolSky::renderHeavenlyBody(U8 hb, LLFace* face)
 {
 	if ( !mHB[hb]->getDraw() ) return;
 	if (! face->getGeomCount()) return;
@@ -152,13 +154,13 @@ void LLDrawPoolSky::renderHeavenlyBody(U8 hb, const LLFace* face)
 	tex->bind();
 	LLColor4 color(mHB[hb]->getInterpColor());
 	LLOverrideFaceColor override(this, color);
-	face->renderIndexed(getRawIndices());
+	face->renderIndexed();
 	mIndicesDrawn += face->getIndicesCount();
 }
 
 
 
-void LLDrawPoolSky::renderSunHalo(const LLFace* face)
+void LLDrawPoolSky::renderSunHalo(LLFace* face)
 {
 	if (! mHB[0]->getDraw()) return;
 	if (! face->getGeomCount()) return;
@@ -169,7 +171,7 @@ void LLDrawPoolSky::renderSunHalo(const LLFace* face)
 	color.mV[3] = llclamp(mHB[0]->getHaloBrighness(), 0.f, 1.f);
 
 	LLOverrideFaceColor override(this, color);
-	face->renderIndexed(getRawIndices());
+	face->renderIndexed();
 	mIndicesDrawn += face->getIndicesCount();
 }
 
diff --git a/indra/newview/lldrawpoolsky.h b/indra/newview/lldrawpoolsky.h
index 881ce6d54277cbf5c4f3e6066c056229b6a43fe5..88d1ce03d0cf295798494867960234faaca2c58c 100644
--- a/indra/newview/lldrawpoolsky.h
+++ b/indra/newview/lldrawpoolsky.h
@@ -14,13 +14,21 @@
 class LLSkyTex;
 class LLHeavenBody;
 
-class LLDrawPoolSky : public LLDrawPool
+class LLDrawPoolSky : public LLFacePool
 {
 private:
 	LLSkyTex			*mSkyTex;
 	LLHeavenBody		*mHB[2]; // Sun and Moon
 
 public:
+	enum
+	{
+		VERTEX_DATA_MASK =	LLVertexBuffer::MAP_VERTEX |
+							LLVertexBuffer::MAP_TEXCOORD
+	};
+
+	virtual U32 getVertexDataMask() { return VERTEX_DATA_MASK; }
+
 	LLDrawPoolSky();
 
 	/*virtual*/ LLDrawPool *instancePool();
@@ -33,8 +41,8 @@ public:
 	void setMoon(LLHeavenBody* moon) { mHB[1] = moon; }
 
 	void renderSkyCubeFace(U8 side);
-	void renderHeavenlyBody(U8 hb, const LLFace* face);
-	void renderSunHalo(const LLFace* face);
+	void renderHeavenlyBody(U8 hb, LLFace* face);
+	void renderSunHalo(LLFace* face);
 
 	virtual S32 getMaterialAttribIndex() { return 0; }
 };
diff --git a/indra/newview/lldrawpoolterrain.cpp b/indra/newview/lldrawpoolterrain.cpp
index 3e6ae7bf850f09a11244ef205fcdeb9060634ec4..8c5c2e177ab5f7f7af2383c324056222727b84ae 100644
--- a/indra/newview/lldrawpoolterrain.cpp
+++ b/indra/newview/lldrawpoolterrain.cpp
@@ -13,7 +13,6 @@
 #include "llfasttimer.h"
 
 #include "llagent.h"
-#include "llagparray.h"
 #include "llviewercontrol.h"
 #include "lldrawable.h"
 #include "llface.h"
@@ -37,7 +36,7 @@ S32 LLDrawPoolTerrain::sDetailMode = 1;
 F32 LLDrawPoolTerrain::sDetailScale = DETAIL_SCALE;
 
 LLDrawPoolTerrain::LLDrawPoolTerrain(LLViewerImage *texturep) :
-	LLDrawPool(POOL_TERRAIN, DATA_SIMPLE_IL_MASK | DATA_COLORS_MASK | DATA_TEX_COORDS1_MASK, DATA_SIMPLE_NIL_MASK),
+	LLFacePool(POOL_TERRAIN),
 	mTexturep(texturep)
 {
 	// Hack!
@@ -75,6 +74,13 @@ void LLDrawPoolTerrain::prerender()
 #if 0 // 1.9.2
 	mVertexShaderLevel = gPipeline.getVertexShaderLevel(LLPipeline::SHADER_ENVIRONMENT);
 #endif
+	sDetailMode = gSavedSettings.getS32("RenderTerrainDetail");
+}
+
+//static
+S32 LLDrawPoolTerrain::getDetailMode()
+{
+	return sDetailMode;
 }
 
 void LLDrawPoolTerrain::render(S32 pass)
@@ -86,6 +92,15 @@ void LLDrawPoolTerrain::render(S32 pass)
 		return;
 	}
 
+	// Hack! Get the region that this draw pool is rendering from!
+	LLViewerRegion *regionp = mDrawFace[0]->getDrawable()->getVObj()->getRegion();
+	LLVLComposition *compp = regionp->getComposition();
+	for (S32 i = 0; i < 4; i++)
+	{
+		compp->mDetailTextures[i]->setBoostLevel(LLViewerImage::BOOST_TERRAIN);
+		compp->mDetailTextures[i]->addTextureStats(1024.f*1024.f); // assume large pixel area
+	}
+
 	if (!gGLManager.mHasMultitexture)
 	{
 		// No mulititexture, render simple land.
@@ -152,25 +167,16 @@ void LLDrawPoolTerrain::renderFull4TUShader()
 	glEnableClientState(GL_VERTEX_ARRAY);
 	glEnableClientState(GL_NORMAL_ARRAY);
 
-	bindGLVertexPointer();
-	bindGLNormalPointer();
 	if (gPipeline.getLightingDetail() >= 2)
 	{
 		glEnableClientState(GL_COLOR_ARRAY);
-		bindGLColorPointer();
 	}
-
+	
 	glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA);
 	
 	// Hack! Get the region that this draw pool is rendering from!
 	LLViewerRegion *regionp = mDrawFace[0]->getDrawable()->getVObj()->getRegion();
 	LLVLComposition *compp = regionp->getComposition();
-	for (S32 i = 0; i < 4; i++)
-	{
-		compp->mDetailTextures[i]->setBoostLevel(LLViewerImage::BOOST_TERRAIN);
-		compp->mDetailTextures[i]->addTextureStats(1024.f*1024.f); // assume large pixel area
-	}
-
 	LLViewerImage *detail_texture0p = compp->mDetailTextures[0];
 	LLViewerImage *detail_texture1p = compp->mDetailTextures[1];
 	LLViewerImage *detail_texture2p = compp->mDetailTextures[2];
@@ -200,8 +206,7 @@ void LLDrawPoolTerrain::renderFull4TUShader()
 	S32 detailTex0 = gPipeline.mTerrainProgram.enableTexture(LLPipeline::GLSL_TERRAIN_DETAIL0);
 	S32 detailTex1 = gPipeline.mTerrainProgram.enableTexture(LLPipeline::GLSL_TERRAIN_DETAIL1);
 	S32 rampTex = gPipeline.mTerrainProgram.enableTexture(LLPipeline::GLSL_TERRAIN_ALPHARAMP);
-	S32 scatterTex = gPipeline.mTerrainProgram.enableTexture(LLPipeline::GLSL_SCATTER_MAP);
-
+	
 	LLViewerImage::bindTexture(detail_texture0p,detailTex0);
 
 	glClientActiveTextureARB(GL_TEXTURE0_ARB);
@@ -222,7 +227,6 @@ void LLDrawPoolTerrain::renderFull4TUShader()
 
 	glClientActiveTextureARB(GL_TEXTURE1_ARB);
 	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
-	bindGLTexCoordPointer(1);
 
 	//
 	// Stage 2: Interpolate detail1 with existing based on ramp
@@ -239,9 +243,6 @@ void LLDrawPoolTerrain::renderFull4TUShader()
 	glTexGenfv(GL_S, GL_OBJECT_PLANE, tp0.mV);
 	glTexGenfv(GL_T, GL_OBJECT_PLANE, tp1.mV);
 
-	// Stage 4: Haze
-	LLViewerImage::bindTexture(gSky.mVOSkyp->getScatterMap(), scatterTex);
-
 	//
 	// Stage 3: Modulate with primary color for lighting
 	//
@@ -278,7 +279,6 @@ void LLDrawPoolTerrain::renderFull4TUShader()
 	glClientActiveTextureARB(GL_TEXTURE1_ARB);
 	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
 	glActiveTextureARB(GL_TEXTURE1_ARB);
-	bindGLTexCoordPointer(1);
 
 	// Set the texture matrix
 	glMatrixMode(GL_TEXTURE);
@@ -310,7 +310,6 @@ void LLDrawPoolTerrain::renderFull4TUShader()
 	glClientActiveTextureARB(GL_TEXTURE3_ARB);
 	glActiveTextureARB(GL_TEXTURE3_ARB);
 	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
-	bindGLTexCoordPointer(1);
 
 	// Set the texture matrix
 	glMatrixMode(GL_TEXTURE);
@@ -323,7 +322,6 @@ void LLDrawPoolTerrain::renderFull4TUShader()
 	}
 
 	// Disable multitexture
-	gPipeline.mTerrainProgram.disableTexture(LLPipeline::GLSL_SCATTER_MAP);
 	gPipeline.mTerrainProgram.disableTexture(LLPipeline::GLSL_TERRAIN_ALPHARAMP);
 	gPipeline.mTerrainProgram.disableTexture(LLPipeline::GLSL_TERRAIN_DETAIL0);
 	gPipeline.mTerrainProgram.disableTexture(LLPipeline::GLSL_TERRAIN_DETAIL1);
@@ -377,18 +375,9 @@ void LLDrawPoolTerrain::renderFull4TU()
 	glEnableClientState(GL_VERTEX_ARRAY);
 	glEnableClientState(GL_NORMAL_ARRAY);
 
-	bindGLVertexPointer();
-	bindGLNormalPointer();
-
 	// Hack! Get the region that this draw pool is rendering from!
 	LLViewerRegion *regionp = mDrawFace[0]->getDrawable()->getVObj()->getRegion();
 	LLVLComposition *compp = regionp->getComposition();
-	for (S32 i = 0; i < 4; i++)
-	{
-		compp->mDetailTextures[i]->setBoostLevel(LLViewerImage::BOOST_TERRAIN);
-		compp->mDetailTextures[i]->addTextureStats(1024.f*1024.f); // assume large pixel area
-	}
-
 	LLViewerImage *detail_texture0p = compp->mDetailTextures[0];
 	LLViewerImage *detail_texture1p = compp->mDetailTextures[1];
 	LLViewerImage *detail_texture2p = compp->mDetailTextures[2];
@@ -411,6 +400,7 @@ void LLDrawPoolTerrain::renderFull4TU()
 	//
 	// Stage 0: detail texture 0
 	//
+	glActiveTextureARB(GL_TEXTURE0_ARB);
 	LLViewerImage::bindTexture(detail_texture0p,0);
 	glClientActiveTextureARB(GL_TEXTURE0_ARB);
 
@@ -432,12 +422,12 @@ void LLDrawPoolTerrain::renderFull4TU()
 	//
 	// Stage 1: Generate alpha ramp for detail0/detail1 transition
 	//
+	glActiveTextureARB(GL_TEXTURE1_ARB);
 	LLViewerImage::bindTexture(m2DAlphaRampImagep,1);
 
 	glEnable(GL_TEXTURE_2D); // Texture unit 1
 	glClientActiveTextureARB(GL_TEXTURE1_ARB);
 	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
-	bindGLTexCoordPointer(1);
 
 	// Care about alpha only
 	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,		GL_COMBINE_ARB);
@@ -452,6 +442,7 @@ void LLDrawPoolTerrain::renderFull4TU()
 	//
 	// Stage 2: Interpolate detail1 with existing based on ramp
 	//
+	glActiveTextureARB(GL_TEXTURE2_ARB);
 	LLViewerImage::bindTexture(detail_texture1p,2);
 	glEnable(GL_TEXTURE_2D); // Texture unit 2
 	glClientActiveTextureARB(GL_TEXTURE2_ARB);
@@ -477,6 +468,7 @@ void LLDrawPoolTerrain::renderFull4TU()
 	//
 	// Stage 3: Modulate with primary (vertex) color for lighting
 	//
+	glActiveTextureARB(GL_TEXTURE3_ARB);
 	LLViewerImage::bindTexture(detail_texture1p,3); // bind any texture
 	glEnable(GL_TEXTURE_2D); // Texture unit 3
 	glClientActiveTextureARB(GL_TEXTURE3_ARB);
@@ -498,6 +490,7 @@ void LLDrawPoolTerrain::renderFull4TU()
 
 	// Stage 0: Write detail3 into base
 	//
+	glActiveTextureARB(GL_TEXTURE0_ARB);
 	LLViewerImage::bindTexture(detail_texture3p,0);
 	glClientActiveTextureARB(GL_TEXTURE0_ARB);
 
@@ -519,14 +512,13 @@ void LLDrawPoolTerrain::renderFull4TU()
 	//
 	// Stage 1: Generate alpha ramp for detail2/detail3 transition
 	//
+	glActiveTextureARB(GL_TEXTURE1_ARB);
 	LLViewerImage::bindTexture(m2DAlphaRampImagep,1);
 
 	glEnable(GL_TEXTURE_2D); // Texture unit 1
 	glClientActiveTextureARB(GL_TEXTURE1_ARB);
 
 	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
-	bindGLTexCoordPointer(1);
-
 
 	// Set the texture matrix
 	glMatrixMode(GL_TEXTURE);
@@ -547,6 +539,7 @@ void LLDrawPoolTerrain::renderFull4TU()
 	//
 	// Stage 2: Interpolate detail2 with existing based on ramp
 	//
+	glActiveTextureARB(GL_TEXTURE2_ARB);
 	LLViewerImage::bindTexture(detail_texture2p,2);
 	glEnable(GL_TEXTURE_2D); // Texture unit 2
 	glClientActiveTextureARB(GL_TEXTURE2_ARB);
@@ -573,19 +566,19 @@ void LLDrawPoolTerrain::renderFull4TU()
 	//
 	// Stage 3: Generate alpha ramp for detail1/detail2 transition
 	//
+	glActiveTextureARB(GL_TEXTURE3_ARB);
 	LLViewerImage::bindTexture(m2DAlphaRampImagep,3);
 
 	glEnable(GL_TEXTURE_2D); // Texture unit 3
 	glClientActiveTextureARB(GL_TEXTURE3_ARB);
 
 	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
-	bindGLTexCoordPointer(1);
 
 	// Set the texture matrix
 	glMatrixMode(GL_TEXTURE);
 	glLoadIdentity();
 	glTranslatef(-1.f, 0.f, 0.f);
-    
+  
 	// Set alpha texture and do lighting modulation
 	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,		GL_COMBINE_ARB);
 	glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB,		GL_MODULATE);
@@ -656,18 +649,9 @@ void LLDrawPoolTerrain::renderFull2TU()
 	glEnableClientState(GL_VERTEX_ARRAY);
 	glEnableClientState(GL_NORMAL_ARRAY);
 
-	bindGLVertexPointer();
-	bindGLNormalPointer();
-
 	// Hack! Get the region that this draw pool is rendering from!
 	LLViewerRegion *regionp = mDrawFace[0]->getDrawable()->getVObj()->getRegion();
 	LLVLComposition *compp = regionp->getComposition();
-	for (S32 i = 0; i < 4; i++)
-	{
-		compp->mDetailTextures[i]->setBoostLevel(LLViewerImage::BOOST_TERRAIN);
-		compp->mDetailTextures[i]->addTextureStats(1024.f*1024.f); // assume large pixel area
-	}
-
 	LLViewerImage *detail_texture0p = compp->mDetailTextures[0];
 	LLViewerImage *detail_texture1p = compp->mDetailTextures[1];
 	LLViewerImage *detail_texture2p = compp->mDetailTextures[2];
@@ -724,7 +708,6 @@ void LLDrawPoolTerrain::renderFull2TU()
 	glDisable(GL_TEXTURE_GEN_S);
 	glDisable(GL_TEXTURE_GEN_T);
 	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
-	bindGLTexCoordPointer(1);
 
 	// Care about alpha only
 	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,		GL_COMBINE_ARB);
@@ -781,7 +764,6 @@ void LLDrawPoolTerrain::renderFull2TU()
 	glTranslatef(-1.f, 0.f, 0.f);
 
 	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
-	bindGLTexCoordPointer(1);
 
 	// Care about alpha only
 	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,		GL_COMBINE_ARB);
@@ -839,7 +821,6 @@ void LLDrawPoolTerrain::renderFull2TU()
 	glTranslatef(-2.f, 0.f, 0.f);
 
 	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
-	bindGLTexCoordPointer(1);
 
 	// Care about alpha only
 	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,		GL_COMBINE_ARB);
@@ -919,9 +900,6 @@ void LLDrawPoolTerrain::renderSimple()
 	glEnableClientState(GL_VERTEX_ARRAY);
 	glEnableClientState(GL_NORMAL_ARRAY);
 
-	bindGLVertexPointer();
-	bindGLNormalPointer();
-
 	LLVector4 tp0, tp1;
 
 	//----------------------------------------------------------------------------
@@ -999,9 +977,6 @@ void LLDrawPoolTerrain::renderOwnership()
 	glEnableClientState(GL_VERTEX_ARRAY);
 	glDisableClientState(GL_NORMAL_ARRAY);
 
-	bindGLVertexPointer();
-	bindGLTexCoordPointer(0);
-
 	LLViewerImage::bindTexture(texturep);
 
 	glClientActiveTextureARB(GL_TEXTURE0_ARB);
@@ -1017,12 +992,12 @@ void LLDrawPoolTerrain::renderOwnership()
 	const F32 TEXTURE_FUDGE = 257.f / 256.f;
 	glScalef( TEXTURE_FUDGE, TEXTURE_FUDGE, 1.f );
 
-	const U32* index_array = getRawIndices();	
 	for (std::vector<LLFace*>::iterator iter = mDrawFace.begin();
 		 iter != mDrawFace.end(); iter++)
 	{
 		LLFace *facep = *iter;
-		facep->renderIndexed(index_array);
+		facep->renderIndexed(LLVertexBuffer::MAP_VERTEX |
+							LLVertexBuffer::MAP_TEXCOORD);
 	}
 
 	glMatrixMode(GL_TEXTURE);
@@ -1036,14 +1011,13 @@ void LLDrawPoolTerrain::renderOwnership()
 
 void LLDrawPoolTerrain::renderForSelect()
 {
-	if (mDrawFace.empty() || !mMemory.count())
+	if (mDrawFace.empty())
 	{
 		return;
 	}
 
-	glEnableClientState ( GL_VERTEX_ARRAY );
-
-	bindGLVertexPointer();
+	
+	LLImageGL::unbindTexture(0);
 
 	for (std::vector<LLFace*>::iterator iter = mDrawFace.begin();
 		 iter != mDrawFace.end(); iter++)
@@ -1051,14 +1025,14 @@ void LLDrawPoolTerrain::renderForSelect()
 		LLFace *facep = *iter;
 		if (!facep->getDrawable()->isDead() && (facep->getDrawable()->getVObj()->mGLName))
 		{
-			facep->renderForSelect();
+			facep->renderForSelect(LLVertexBuffer::MAP_VERTEX);
 		}
 	}
 }
 
-void LLDrawPoolTerrain::dirtyTexture(const LLViewerImage *texturep)
+void LLDrawPoolTerrain::dirtyTextures(const std::set<LLViewerImage*>& textures)
 {
-	if (mTexturep == texturep)
+	if (textures.find(mTexturep) != textures.end())
 	{
 		for (std::vector<LLFace*>::iterator iter = mReferences.begin();
 			 iter != mReferences.end(); iter++)
diff --git a/indra/newview/lldrawpoolterrain.h b/indra/newview/lldrawpoolterrain.h
index b5fe0a30fda400c33232205f56821df15e50dfac..4882532e8a212068b6c80d84f90a4bca26b17491 100644
--- a/indra/newview/lldrawpoolterrain.h
+++ b/indra/newview/lldrawpoolterrain.h
@@ -11,10 +11,22 @@
 
 #include "lldrawpool.h"
 
-class LLDrawPoolTerrain : public LLDrawPool
+class LLDrawPoolTerrain : public LLFacePool
 {
 	LLPointer<LLViewerImage> mTexturep;
 public:
+	enum
+	{
+		VERTEX_DATA_MASK =	LLVertexBuffer::MAP_VERTEX |
+							LLVertexBuffer::MAP_NORMAL |
+							LLVertexBuffer::MAP_TEXCOORD |
+							LLVertexBuffer::MAP_TEXCOORD2 |
+							LLVertexBuffer::MAP_COLOR
+	};
+
+	virtual U32 getVertexDataMask() { return VERTEX_DATA_MASK; }
+	static S32 getDetailMode();
+
 	LLDrawPoolTerrain(LLViewerImage *texturep);
 	virtual ~LLDrawPoolTerrain();
 
@@ -24,7 +36,7 @@ public:
 	/*virtual*/ void render(S32 pass = 0);
 	/*virtual*/ void prerender();
 	/*virtual*/ void renderForSelect();
-	/*virtual*/ void dirtyTexture(const LLViewerImage *texturep);
+	/*virtual*/ void dirtyTextures(const std::set<LLViewerImage*>& textures);
 	/*virtual*/ LLViewerImage *getTexture();
 	/*virtual*/ LLViewerImage *getDebugTexture();
 	/*virtual*/ LLColor3 getDebugColor() const; // For AGP debug display
diff --git a/indra/newview/lldrawpooltree.cpp b/indra/newview/lldrawpooltree.cpp
index c41ceedac7ab78c0b2310d53a59870b7c3fc465b..170669955cdb4d3967a7d30addf11708d3898f4c 100644
--- a/indra/newview/lldrawpooltree.cpp
+++ b/indra/newview/lldrawpooltree.cpp
@@ -10,7 +10,6 @@
 
 #include "lldrawpooltree.h"
 
-#include "llagparray.h"
 #include "lldrawable.h"
 #include "llface.h"
 #include "llsky.h"
@@ -22,7 +21,7 @@
 S32 LLDrawPoolTree::sDiffTex = 0;
 
 LLDrawPoolTree::LLDrawPoolTree(LLViewerImage *texturep) :
-	LLDrawPool(POOL_TREE, DATA_SIMPLE_IL_MASK, 0),
+	LLFacePool(POOL_TREE),
 	mTexturep(texturep)
 {
 	mTexturep->bind(0);
@@ -36,7 +35,7 @@ LLDrawPool *LLDrawPoolTree::instancePool()
 
 void LLDrawPoolTree::prerender()
 {
-	mVertexShaderLevel = gPipeline.getVertexShaderLevel(LLPipeline::SHADER_OBJECT);
+	mVertexShaderLevel = 0;
 }
 
 void LLDrawPoolTree::beginRenderPass(S32 pass)
@@ -44,13 +43,7 @@ void LLDrawPoolTree::beginRenderPass(S32 pass)
 	glEnableClientState(GL_VERTEX_ARRAY);
 	glEnableClientState(GL_NORMAL_ARRAY);
 	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
-	
-	if ((mVertexShaderLevel > 0))
-	{
-		S32 scatterTex = gPipeline.mObjectSimpleProgram.enableTexture(LLPipeline::GLSL_SCATTER_MAP);
-		LLViewerImage::bindTexture(gSky.mVOSkyp->getScatterMap(), scatterTex);
-		sDiffTex = gPipeline.mObjectSimpleProgram.enableTexture(LLPipeline::GLSL_DIFFUSE_MAP);
-	}
+	glAlphaFunc(GL_GREATER, 0.5f);
 }
 
 void LLDrawPoolTree::render(S32 pass)
@@ -64,34 +57,21 @@ void LLDrawPoolTree::render(S32 pass)
 
 	gPipeline.enableLightsDynamic(1.f);
 	LLGLSPipelineAlpha gls_pipeline_alpha;
-	
-	bindGLVertexPointer();
-	bindGLTexCoordPointer();
-	bindGLNormalPointer();
-
 	LLOverrideFaceColor color(this, 1.f, 1.f, 1.f, 1.f);
 
 	renderTree();
-	
 }
 
 void LLDrawPoolTree::endRenderPass(S32 pass)
 {
-	if ((mVertexShaderLevel > 0))
-	{
-		gPipeline.mObjectSimpleProgram.disableTexture(LLPipeline::GLSL_SCATTER_MAP);
-		gPipeline.mObjectSimpleProgram.disableTexture(LLPipeline::GLSL_DIFFUSE_MAP);
-		glActiveTextureARB(GL_TEXTURE0_ARB);
-		glEnable(GL_TEXTURE_2D);
-	}
-
+	glAlphaFunc(GL_GREATER, 0.01f);
 	glDisableClientState(GL_NORMAL_ARRAY);
 	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
 }
 
 void LLDrawPoolTree::renderForSelect()
 {
-	if (mDrawFace.empty() || !mMemory.count())
+	if (mDrawFace.empty())
 	{
 		return;
 	}
@@ -104,10 +84,7 @@ void LLDrawPoolTree::renderForSelect()
 	LLGLSObjectSelectAlpha gls_alpha;
 
 	glBlendFunc(GL_ONE, GL_ZERO);
-	glAlphaFunc(gPickTransparent ? GL_GEQUAL : GL_GREATER, 0.f);
-
-	bindGLVertexPointer();
-	bindGLTexCoordPointer();
+	glAlphaFunc(GL_GREATER, 0.5f);
 
 	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,		GL_COMBINE_ARB);
 	glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB,		GL_REPLACE);
@@ -147,6 +124,8 @@ void LLDrawPoolTree::renderTree(BOOL selecting)
 		}
 	}
 
+	U32 indices_drawn = 0;
+
 	glMatrixMode(GL_MODELVIEW);
 	
 	for (std::vector<LLFace*>::iterator iter = mDrawFace.begin();
@@ -155,11 +134,14 @@ void LLDrawPoolTree::renderTree(BOOL selecting)
 		LLFace *face = *iter;
 		LLDrawable *drawablep = face->getDrawable();
 
-		if (drawablep->isDead())
+		if (drawablep->isDead() || face->mVertexBuffer.isNull())
 		{
 			continue;
 		}
 
+		face->mVertexBuffer->setBuffer(LLDrawPoolTree::VERTEX_DATA_MASK);
+		U32* indicesp = (U32*) face->mVertexBuffer->getIndicesPointer();
+
 		// Render each of the trees
 		LLVOTree *treep = (LLVOTree *)drawablep->getVObj();
 
@@ -217,55 +199,26 @@ void LLDrawPoolTree::renderTree(BOOL selecting)
 				}
 			} 
 
-			if (app_angle > (THRESH_ANGLE_FOR_BILLBOARD + BLEND_RANGE_FOR_BILLBOARD))
-			{
-				//
-				//  Draw only the full geometry tree
-				//
-				//stop_depth = (app_angle < THRESH_ANGLE_FOR_RECURSION_REDUCTION);
-				glAlphaFunc(GL_GREATER, 0.5f);
-				LLDrawPool::LLOverrideFaceColor clr(this, color); 
-				treep->drawBranchPipeline(this, trunk_LOD, stop_depth, treep->mDepth, treep->mTrunkDepth, 1.0, treep->mTwist, droop, treep->mBranches, alpha);
-			}
-			else if (app_angle < (THRESH_ANGLE_FOR_BILLBOARD - BLEND_RANGE_FOR_BILLBOARD))
+			if (app_angle < (THRESH_ANGLE_FOR_BILLBOARD - BLEND_RANGE_FOR_BILLBOARD))
 			{
 				//
 				//  Draw only the billboard 
 				//
 				//  Only the billboard, can use closer to normal alpha func.
 				stop_depth = -1;
-				glAlphaFunc(GL_GREATER, 0.4f);
-				LLDrawPool::LLOverrideFaceColor clr(this, color); 
-				treep->drawBranchPipeline(this, trunk_LOD, stop_depth, treep->mDepth, treep->mTrunkDepth, 1.0, treep->mTwist, droop, treep->mBranches, alpha);
+				LLFacePool::LLOverrideFaceColor clr(this, color); 
+				indices_drawn += treep->drawBranchPipeline(indicesp, trunk_LOD, stop_depth, treep->mDepth, treep->mTrunkDepth, 1.0, treep->mTwist, droop, treep->mBranches, alpha);
 			}
-			else
+			else // if (app_angle > (THRESH_ANGLE_FOR_BILLBOARD + BLEND_RANGE_FOR_BILLBOARD))
 			{
 				//
-				//  Draw a blended version including both billboard and full tree 
-				// 
-				alpha = (app_angle - THRESH_ANGLE_FOR_BILLBOARD)/BLEND_RANGE_FOR_BILLBOARD;
-				BOOL billboard_depth = TRUE; // billboard gets alpha
-				if (alpha > 0.5f)
-				{
-					billboard_depth = FALSE;
-				}
-				alpha = alpha/2.f + 0.5f;
-								
-				glAlphaFunc(GL_GREATER, alpha*0.5f);
-				{
-					LLGLDepthTest gls_depth(GL_TRUE, billboard_depth ? GL_FALSE : GL_TRUE);
-					color.mV[3] = (U8) (llclamp(alpha, 0.0f, 1.0f) * 255);
-					LLDrawPool::LLOverrideFaceColor clr(this, color); 
-					treep->drawBranchPipeline(this, trunk_LOD, 0, treep->mDepth, treep->mTrunkDepth, 1.0, treep->mTwist, droop, treep->mBranches, alpha);
-				}
-				{
-					LLGLDepthTest gls_depth(GL_TRUE, billboard_depth ? GL_TRUE : GL_FALSE);
-					glAlphaFunc(GL_GREATER, (1.f - alpha)*0.1f);
-					color.mV[3] = (U8) (llclamp(1.f-alpha, 0.0f, 1.0f) * 255);
-					LLDrawPool::LLOverrideFaceColor clr(this, color); 
-					treep->drawBranchPipeline(this, trunk_LOD, -1, treep->mDepth, treep->mTrunkDepth, 1.0, treep->mTwist, droop, treep->mBranches, 1.f - alpha);
-				}
+				//  Draw only the full geometry tree
+				//
+				//stop_depth = (app_angle < THRESH_ANGLE_FOR_RECURSION_REDUCTION);
+				LLFacePool::LLOverrideFaceColor clr(this, color); 
+				indices_drawn += treep->drawBranchPipeline(indicesp, trunk_LOD, stop_depth, treep->mDepth, treep->mTrunkDepth, 1.0, treep->mTwist, droop, treep->mBranches, alpha);
 			}
+			
 			glPopMatrix();
 		}
 	}
@@ -279,45 +232,21 @@ void LLDrawPoolTree::renderTree(BOOL selecting)
 			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 		}
 	}
-	glAlphaFunc(GL_GREATER, 0.01f);
-}
-
-
-S32 LLDrawPoolTree::rebuild()
-{
-	mRebuildTime++;
-	if (mRebuildTime >  mRebuildFreq)
-	{
-		// Flush AGP to force an AGP realloc and reduce AGP fragmentation
-		flushAGP();
-		mRebuildTime = 0;
-	}
 
-	return 0;
+	addIndicesDrawn(indices_drawn);
 }
 
 BOOL LLDrawPoolTree::verify() const
 {
-	BOOL ok = TRUE;
-
-	// shared geometry.  Just verify that it's there and correct.
+/*	BOOL ok = TRUE;
 
-	// Verify all indices in the pool are in the right range
-	const U32 *indicesp = getRawIndices();
-	for (U32 i = 0; i < getIndexCount(); i++)
-	{
-		if (indicesp[i] > getVertexCount())
-		{
-			ok = FALSE;
-			llinfos << "Bad index in tree pool!" << llendl;
-		}
-	}
-	
 	if (!ok)
 	{
 		printDebugInfo();
 	}
-	return ok;
+	return ok;*/
+
+	return TRUE;
 }
 
 LLViewerImage *LLDrawPoolTree::getTexture()
diff --git a/indra/newview/lldrawpooltree.h b/indra/newview/lldrawpooltree.h
index 228b11a981e4f2ce911b183bce42643dbef924da..5b937cf688ac85c9da44b383d51ab343a630a0a3 100644
--- a/indra/newview/lldrawpooltree.h
+++ b/indra/newview/lldrawpooltree.h
@@ -11,10 +11,19 @@
 
 #include "lldrawpool.h"
 
-class LLDrawPoolTree : public LLDrawPool
+class LLDrawPoolTree : public LLFacePool
 {
 	LLPointer<LLViewerImage> mTexturep;
 public:
+	enum
+	{
+		VERTEX_DATA_MASK =	LLVertexBuffer::MAP_VERTEX |
+							LLVertexBuffer::MAP_NORMAL |
+							LLVertexBuffer::MAP_TEXCOORD							
+	};
+
+	virtual U32 getVertexDataMask() { return VERTEX_DATA_MASK; }
+
 	LLDrawPoolTree(LLViewerImage *texturep);
 
 	/*virtual*/ LLDrawPool *instancePool();
@@ -24,7 +33,6 @@ public:
 	/*virtual*/ void render(S32 pass = 0);
 	/*virtual*/ void endRenderPass( S32 pass );
 	/*virtual*/ void renderForSelect();
-	/*virtual*/ S32 rebuild();
 	/*virtual*/ BOOL verify() const;
 	/*virtual*/ LLViewerImage *getTexture();
 	/*virtual*/ LLViewerImage *getDebugTexture();
diff --git a/indra/newview/lldrawpoolwater.cpp b/indra/newview/lldrawpoolwater.cpp
index 108048efbc4f24f1511e2fdd11bf03136818a607..db5debc07974338830fb2a7a67822465ff16bf1f 100644
--- a/indra/newview/lldrawpoolwater.cpp
+++ b/indra/newview/lldrawpoolwater.cpp
@@ -16,7 +16,6 @@
 #include "m3math.h"
 
 #include "llagent.h"		// for gAgent for getRegion for getWaterHeight
-#include "llagparray.h"
 #include "llcubemap.h"
 #include "lldrawable.h"
 #include "llface.h"
@@ -44,11 +43,11 @@ int nhpo2(int v)
 }
 
 static GLuint sScreenTex = 0;
+BOOL LLDrawPoolWater::sSkipScreenCopy = FALSE;
 
 LLDrawPoolWater::LLDrawPoolWater() :
-	LLDrawPool(POOL_WATER, DATA_SIMPLE_IL_MASK, DATA_SIMPLE_NIL_MASK)
+	LLFacePool(POOL_WATER)
 {
-	mCleanupUnused = TRUE;
 	mHBTex[0] = gImageList.getImage(gSunTextureID, TRUE, TRUE);
 	mHBTex[0]->bind();
 	mHBTex[0]->setClamp(TRUE, TRUE);
@@ -116,11 +115,21 @@ extern LLColor4U MAX_WATER_COLOR;
 void LLDrawPoolWater::render(S32 pass)
 {
 	LLFastTimer ftm(LLFastTimer::FTM_RENDER_WATER);
-	if (mDrawFace.empty())
+	if (mDrawFace.empty() || LLDrawable::getCurrentFrame() <= 1)
 	{
 		return;
 	}
 
+	//do a quick 'n dirty depth sort
+	for (std::vector<LLFace*>::iterator iter = mDrawFace.begin();
+			 iter != mDrawFace.end(); iter++)
+	{
+		LLFace* facep = *iter;
+		facep->mDistance = -facep->mCenterLocal.mV[2];
+	}
+
+	std::sort(mDrawFace.begin(), mDrawFace.end(), LLFace::CompareDistanceGreater());
+
 	LLGLSPipelineAlpha alphaState;
 
 	if ((mVertexShaderLevel >= SHADER_LEVEL_RIPPLE))
@@ -145,7 +154,7 @@ void LLDrawPoolWater::render(S32 pass)
 		return;
 	}
 
-	const LLFace* refl_face = voskyp->getReflFace();
+	LLFace* refl_face = voskyp->getReflFace();
 
 	gPipeline.disableLights();
 	
@@ -157,10 +166,6 @@ void LLDrawPoolWater::render(S32 pass)
 	glEnableClientState(GL_VERTEX_ARRAY);
 	glEnableClientState(GL_NORMAL_ARRAY);
 	
-	bindGLVertexPointer();
-	bindGLNormalPointer();
-	bindGLTexCoordPointer();
-	
 	// Set up second pass first
 	glActiveTextureARB(GL_TEXTURE1_ARB);
 	mWaterImagep->addTextureStats(1024.f*1024.f);
@@ -227,7 +232,7 @@ void LLDrawPoolWater::render(S32 pass)
 			continue;
 		}
 		face->bindTexture();
-		face->renderIndexed(getRawIndices());
+		face->renderIndexed();
 		mIndicesDrawn += face->getIndicesCount();
 	}
 
@@ -288,7 +293,7 @@ void LLDrawPoolWater::render(S32 pass)
 
 			if (face->getGeomCount() > 0)
 			{					
-				face->renderIndexed(getRawIndices());
+				face->renderIndexed();
 				mIndicesDrawn += face->getIndicesCount();
 			}
 		}
@@ -335,7 +340,7 @@ void LLDrawPoolWater::renderShaderSimple()
 		return;
 	}
 
-	const LLFace* refl_face = voskyp->getReflFace();
+	LLFace* refl_face = voskyp->getReflFace();
 
 	LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
 
@@ -345,10 +350,6 @@ void LLDrawPoolWater::renderShaderSimple()
 	glEnableClientState(GL_VERTEX_ARRAY);
 	glEnableClientState(GL_NORMAL_ARRAY);
 	
-	bindGLVertexPointer();
-	bindGLNormalPointer();
-	bindGLTexCoordPointer();
-	
 	// Set up second pass first
 	S32 bumpTex = gPipeline.mWaterProgram.enableTexture(LLPipeline::GLSL_BUMP_MAP);
 	mWaterImagep->addTextureStats(1024.f*1024.f);
@@ -413,9 +414,6 @@ void LLDrawPoolWater::renderShaderSimple()
 		glMatrixMode(GL_MODELVIEW);
 	}
 
-	S32 scatterTex = gPipeline.mWaterProgram.enableTexture(LLPipeline::GLSL_SCATTER_MAP);
-	LLViewerImage::bindTexture(gSky.mVOSkyp->getScatterMap(), scatterTex);
-
 	S32 diffTex = gPipeline.mWaterProgram.enableTexture(LLPipeline::GLSL_DIFFUSE_MAP);
     
 	gPipeline.mWaterProgram.bind();
@@ -429,7 +427,7 @@ void LLDrawPoolWater::renderShaderSimple()
 			continue;
 		}
 		face->bindTexture(diffTex);
-		face->renderIndexed(getRawIndices());
+		face->renderIndexed();
 		mIndicesDrawn += face->getIndicesCount();
 	}
 		
@@ -450,8 +448,7 @@ void LLDrawPoolWater::renderShaderSimple()
 	glDisable(GL_TEXTURE_GEN_T); //texture unit 1
 
 	gPipeline.mWaterProgram.disableTexture(LLPipeline::GLSL_DIFFUSE_MAP);
-	gPipeline.mWaterProgram.disableTexture(LLPipeline::GLSL_SCATTER_MAP);
-
+	
 	// Disable texture coordinate and color arrays
 	LLImageGL::unbindTexture(diffTex, GL_TEXTURE_2D);
 
@@ -477,7 +474,7 @@ void LLDrawPoolWater::renderShaderSimple()
 	glDisableClientState(GL_NORMAL_ARRAY);
 }
 
-void LLDrawPoolWater::renderReflection(const LLFace* face)
+void LLDrawPoolWater::renderReflection(LLFace* face)
 {
 	LLVOSky *voskyp = gSky.mVOSkyp;
 
@@ -505,7 +502,7 @@ void LLDrawPoolWater::renderReflection(const LLFace* face)
 	LLViewerImage::bindTexture(mHBTex[dr]);
 
 	LLOverrideFaceColor override(this, face->getFaceColor().mV);
-	face->renderIndexed(getRawIndices());
+	face->renderIndexed();
 	mIndicesDrawn += face->getIndicesCount();
 
 	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
@@ -513,36 +510,44 @@ void LLDrawPoolWater::renderReflection(const LLFace* face)
 
 void bindScreenToTexture() 
 {
-	GLint viewport[4];
-	glGetIntegerv(GL_VIEWPORT, viewport);
-	GLuint resX = nhpo2(viewport[2]);
-	GLuint resY = nhpo2(viewport[3]);
-
-	glBindTexture(GL_TEXTURE_2D, sScreenTex);
-	GLint cResX;
-	GLint cResY;
-	glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &cResX);
-	glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &cResY);
-
-	if (cResX != (GLint)resX || cResY != (GLint)resY)
+	if (LLDrawPoolWater::sSkipScreenCopy)
 	{
-		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, resX, resY, 0, GL_RGB, GL_FLOAT, NULL);
-		gImageList.updateMaxResidentTexMem(-1, resX*resY*3);
+		glBindTexture(GL_TEXTURE_2D, 0);
 	}
+	else
+	{
 
-	glCopyTexSubImage2D(GL_TEXTURE_2D, 0, viewport[0], viewport[1], 0, 0, viewport[2], viewport[3]); 
+		GLint viewport[4];
+		glGetIntegerv(GL_VIEWPORT, viewport);
+		GLuint resX = nhpo2(viewport[2]);
+		GLuint resY = nhpo2(viewport[3]);
 
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+		glBindTexture(GL_TEXTURE_2D, sScreenTex);
+		GLint cResX;
+		GLint cResY;
+		glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &cResX);
+		glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &cResY);
+
+		if (cResX != (GLint)resX || cResY != (GLint)resY)
+		{
+			glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, resX, resY, 0, GL_RGB, GL_FLOAT, NULL);
+			gImageList.updateMaxResidentTexMem(-1, resX*resY*3);
+		}
 
-	float scale[2];
-	scale[0] = (float) viewport[2]/resX;
-	scale[1] = (float) viewport[3]/resY;
-	glUniform2fvARB(gPipeline.mWaterProgram.mUniform[LLPipeline::GLSL_WATER_FBSCALE], 1, scale);
+		glCopyTexSubImage2D(GL_TEXTURE_2D, 0, viewport[0], viewport[1], 0, 0, viewport[2], viewport[3]); 
 
-	LLImageGL::sBoundTextureMemory += resX * resY * 3;
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+		float scale[2];
+		scale[0] = (float) viewport[2]/resX;
+		scale[1] = (float) viewport[3]/resY;
+		glUniform2fvARB(gPipeline.mWaterProgram.mUniform[LLPipeline::GLSL_WATER_FBSCALE], 1, scale);
+
+		LLImageGL::sBoundTextureMemory += resX * resY * 3;
+	}
 }
 
 void LLDrawPoolWater::shade()
@@ -577,9 +582,6 @@ void LLDrawPoolWater::shade()
 	glEnableClientState(GL_VERTEX_ARRAY);
 	glEnableClientState(GL_NORMAL_ARRAY);
 	LLGLDisable blend(GL_BLEND);
-	bindGLVertexPointer();
-	bindGLNormalPointer();
-	bindGLTexCoordPointer();
 
 	LLColor3 light_diffuse(0,0,0);
 	F32 light_exp = 0.0f;
@@ -630,9 +632,6 @@ void LLDrawPoolWater::shade()
 	
 	bindScreenToTexture();
 	
-	S32 scatterTex = gPipeline.mWaterProgram.enableTexture(LLPipeline::GLSL_SCATTER_MAP);
-	LLViewerImage::bindTexture(gSky.mVOSkyp->getScatterMap(), scatterTex);
-
 	S32 diffTex = gPipeline.mWaterProgram.enableTexture(LLPipeline::GLSL_DIFFUSE_MAP);
 	
 	LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
@@ -678,7 +677,7 @@ void LLDrawPoolWater::shade()
 			}
 
 			face->bindTexture(diffTex);
-			face->renderIndexed(getRawIndices());
+			face->renderIndexed();
 			mIndicesDrawn += face->getIndicesCount();
 		}
 	}
@@ -686,7 +685,6 @@ void LLDrawPoolWater::shade()
 	gPipeline.mWaterProgram.disableTexture(LLPipeline::GLSL_ENVIRONMENT_MAP, GL_TEXTURE_CUBE_MAP_ARB);
 	gPipeline.mWaterProgram.disableTexture(LLPipeline::GLSL_WATER_SCREENTEX);
 	gPipeline.mWaterProgram.disableTexture(LLPipeline::GLSL_BUMP_MAP);
-	gPipeline.mWaterProgram.disableTexture(LLPipeline::GLSL_SCATTER_MAP);
 	gPipeline.mWaterProgram.disableTexture(LLPipeline::GLSL_DIFFUSE_MAP);
 
 	glActiveTextureARB(GL_TEXTURE0_ARB);
@@ -695,10 +693,6 @@ void LLDrawPoolWater::shade()
 	
 	glClientActiveTextureARB(GL_TEXTURE0_ARB);
 	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
-
-	/*glDisableClientState(GL_TEXTURE_COORD_ARRAY);
-	glDisableClientState(GL_VERTEX_ARRAY);
-	glDisableClientState(GL_NORMAL_ARRAY);*/
 }
 
 void LLDrawPoolWater::renderForSelect()
diff --git a/indra/newview/lldrawpoolwater.h b/indra/newview/lldrawpoolwater.h
index 7e661b37181629dfc4f20aee1bc6e658fe0fdea2..fafacf4d3423801fe800ecf167169fee86ec68db 100644
--- a/indra/newview/lldrawpoolwater.h
+++ b/indra/newview/lldrawpoolwater.h
@@ -16,7 +16,7 @@ class LLFace;
 class LLHeavenBody;
 class LLWaterSurface;
 
-class LLDrawPoolWater: public LLDrawPool
+class LLDrawPoolWater: public LLFacePool
 {
 protected:
 	LLPointer<LLViewerImage> mHBTex[2];
@@ -25,6 +25,16 @@ protected:
 
 	const LLWaterSurface *mWaterSurface;
 public:
+	static BOOL sSkipScreenCopy;
+	enum
+	{
+		VERTEX_DATA_MASK =	LLVertexBuffer::MAP_VERTEX |
+							LLVertexBuffer::MAP_NORMAL |
+							LLVertexBuffer::MAP_TEXCOORD	
+	};
+
+	virtual U32 getVertexDataMask() { return VERTEX_DATA_MASK; }
+
 	enum
 	{
 		SHADER_LEVEL_RIPPLE = 2,
@@ -45,7 +55,7 @@ public:
 	/*virtual*/ LLViewerImage *getDebugTexture();
 	/*virtual*/ LLColor3 getDebugColor() const; // For AGP debug display
 
-	void renderReflection(const LLFace* face);
+	void renderReflection(LLFace* face);
 	void shade();
 	void renderShaderSimple();
 
diff --git a/indra/newview/lldynamictexture.cpp b/indra/newview/lldynamictexture.cpp
index 0f0a9d9bda30e2fdd9c7d07e4db276985f7c3f41..f2981625f79a109a3acfd0687078d61443a092e8 100644
--- a/indra/newview/lldynamictexture.cpp
+++ b/indra/newview/lldynamictexture.cpp
@@ -16,6 +16,8 @@
 #include "llviewercamera.h"
 #include "llviewercontrol.h"
 #include "llviewerimage.h"
+#include "llvertexbuffer.h"
+
 
 // static
 LLLinkedList<LLDynamicTexture> LLDynamicTexture::sInstances[ LLDynamicTexture::ORDER_COUNT ];
@@ -174,6 +176,8 @@ BOOL LLDynamicTexture::updateAllInstances()
 		return TRUE;
 	}
 
+	BOOL started = FALSE;
+		
 	BOOL result = FALSE;
 	for( S32 order = 0; order < ORDER_COUNT; order++ )
 	{
@@ -183,6 +187,12 @@ BOOL LLDynamicTexture::updateAllInstances()
 		{
 			if (dynamicTexture->needsRender())
 			{
+				if (!started)
+				{
+					started = TRUE;
+					LLVertexBuffer::startRender();
+				}
+				
 				dynamicTexture->preRender();
 				if (dynamicTexture->render())
 				{
@@ -194,6 +204,11 @@ BOOL LLDynamicTexture::updateAllInstances()
 		}
 	}
 
+	if (started)
+	{
+		LLVertexBuffer::stopRender();
+	}
+	
 	return result;
 }
 
diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp
index cd24454caf616caa352b4ed5e923563f2f73ec0f..64edcecdc65d1b95a0a061e5ece7638070fb3e35 100644
--- a/indra/newview/llface.cpp
+++ b/indra/newview/llface.cpp
@@ -10,13 +10,13 @@
 
 #include "lldrawable.h" // lldrawable needs to be included before llface
 #include "llface.h"
+#include "llviewertextureanim.h"
 
 #include "llviewercontrol.h"
 #include "llvolume.h"
 #include "m3math.h"
 #include "v3color.h"
 
-#include "llagparray.h"
 #include "lldrawpoolsimple.h"
 #include "lldrawpoolbump.h"
 #include "llgl.h"
@@ -28,8 +28,6 @@
 #include "llvovolume.h"
 #include "pipeline.h"
 
-#include "llagparray.inl"
-
 #define LL_MAX_INDICES_COUNT 1000000
 
 extern BOOL gPickFaces;
@@ -118,25 +116,23 @@ void cylindricalProjection(LLVector2 &tc, const LLVolumeFace::VertexData &vd, co
 
 void LLFace::init(LLDrawable* drawablep, LLViewerObject* objp)
 {
-	mGeneration = DIRTY;
+	mLastUpdateTime = gFrameTimeSeconds;
+	mVSize = 0.f;
+	mPixelArea = 1024.f;
 	mState      = GLOBAL;
 	mDrawPoolp  = NULL;
+	mPoolType = 0;
 	mGeomIndex  = -1;
-	mSkipRender = FALSE;
-	mNextFace = NULL;
 	// mCenterLocal
 	// mCenterAgent
 	mDistance	= 0.f;
 
-	mPrimType		= LLTriangles;
 	mGeomCount		= 0;
 	mIndicesCount	= 0;
 	mIndicesIndex	= -1;
 	mTexture		= NULL;
 	mTEOffset		= -1;
 
-	mBackupMem = NULL;
-
 	setDrawable(drawablep);
 	mVObjp = objp;
 
@@ -144,6 +140,12 @@ void LLFace::init(LLDrawable* drawablep, LLViewerObject* objp)
 	mAlphaFade = 0.f;
 
 	mFaceColor = LLColor4(1,0,0,1);
+
+	mLastVertexBuffer = mVertexBuffer;
+	mLastGeomCount = mGeomCount;
+	mLastGeomIndex = mGeomIndex;
+	mLastIndicesCount = mIndicesCount;
+	mLastIndicesIndex = mIndicesIndex;
 }
 
 
@@ -157,11 +159,6 @@ void LLFace::destroy()
 		mDrawPoolp->removeFace(this);
 		mDrawPoolp = NULL;
 	}
-
-	// Remove light and blocker list references
-
-	delete[] mBackupMem;
-	mBackupMem = NULL;
 }
 
 
@@ -175,13 +172,7 @@ void LLFace::setWorldMatrix(const LLMatrix4 &mat)
 	llerrs << "Faces on this drawable are not independently modifiable\n" << llendl;
 }
 
-
-void LLFace::setDirty()
-{
-	mGeneration = DIRTY;
-}
-
-void LLFace::setPool(LLDrawPool* new_pool, LLViewerImage *texturep)
+void LLFace::setPool(LLFacePool* new_pool, LLViewerImage *texturep)
 {
 	LLMemType mt1(LLMemType::MTYPE_DRAWABLE);
 	
@@ -196,22 +187,12 @@ void LLFace::setPool(LLDrawPool* new_pool, LLViewerImage *texturep)
 		if (mDrawPoolp)
 		{
 			mDrawPoolp->removeFace(this);
-			mSkipRender = FALSE;
-			mNextFace = NULL;
 
-			// Invalidate geometry (will get rebuilt next frame)
-			setDirty();
 			if (mDrawablep)
 			{
 				gPipeline.markRebuild(mDrawablep, LLDrawable::REBUILD_ALL, TRUE);
 			}
 		}
-		if (isState(BACKLIST))
-		{
-			delete[] mBackupMem;
-			mBackupMem = NULL;
-			clearState(BACKLIST);
-		}
 		mGeomIndex = -1;
 
 		// Add to new pool
@@ -220,7 +201,6 @@ void LLFace::setPool(LLDrawPool* new_pool, LLViewerImage *texturep)
 			new_pool->addFace(this);
 		}
 		mDrawPoolp = new_pool;
-
 	}
 	mTexture = texturep;
 }
@@ -249,91 +229,12 @@ void LLFace::setDrawable(LLDrawable *drawable)
 	mXform      = &drawable->mXform;
 }
 
-S32 LLFace::allocBackupMem()
-{
-	LLMemType mt1(LLMemType::MTYPE_DRAWABLE);
-	
-	S32 size = 0;
-	size += mIndicesCount * 4;
-	size += mGeomCount * mDrawPoolp->getStride();
-
-	if (mDrawPoolp->mDataMaskNIL & LLDrawPool::DATA_VERTEX_WEIGHTS_MASK)
-	{
-		size += mGeomCount * mDrawPoolp->sDataSizes[LLDrawPool::DATA_VERTEX_WEIGHTS];
-	}
-
-	if (mDrawPoolp->mDataMaskNIL & LLDrawPool::DATA_CLOTHING_WEIGHTS_MASK)
-	{
-		size += mGeomCount * mDrawPoolp->sDataSizes[LLDrawPool::DATA_CLOTHING_WEIGHTS];
-	}
-
-	delete[] mBackupMem;
-	mBackupMem = new U8[size];
-	return size;
-}
-
-
 void LLFace::setSize(const S32 num_vertices, const S32 num_indices)
 {
 	LLMemType mt1(LLMemType::MTYPE_DRAWABLE);
 	
-	if (getState() & SHARED_GEOM)
-	{
-		mGeomCount    = num_vertices;
-		mIndicesCount = num_indices;
-		return; // Shared, don't allocate or do anything with memory
-	}
-	if (num_vertices != (S32)mGeomCount || num_indices != (S32)mIndicesCount)
-	{
-		setDirty();
-
-		delete[] mBackupMem;
-		mBackupMem = NULL;
-		clearState(BACKLIST);
-
-		mGeomCount    = num_vertices;
-		mIndicesCount = num_indices;
-	}
-
-}
-
-BOOL LLFace::reserveIfNeeded()
-{
-	LLMemType mt1(LLMemType::MTYPE_DRAWABLE);
-	
-	if (getDirty())
-	{
-		if (isState(BACKLIST))
-		{
-			llwarns << "Reserve on backlisted object!" << llendl;
-		}
-
-		if (0 == mGeomCount)
-		{
-			//llwarns << "Reserving zero bytes for face!" << llendl;
-			mGeomCount = 0;
-			mIndicesCount = 0;
-			return FALSE;
-		}
-
-		mGeomIndex	  = mDrawPoolp->reserveGeom(mGeomCount);
-		// (reserveGeom() always returns a valid index)
-		mIndicesIndex = mDrawPoolp->reserveInd (mIndicesCount);
-		mGeneration   = mDrawPoolp->mGeneration;
-	}
-
-	return TRUE;
-}
-
-void LLFace::unReserve()
-{
-	LLMemType mt1(LLMemType::MTYPE_DRAWABLE);
-	
-	if (!(isState(SHARED_GEOM)))
-	{
-		mGeomIndex    = mDrawPoolp->unReserveGeom(mGeomIndex, mGeomCount);
-		mIndicesIndex = mDrawPoolp->unReserveInd(mIndicesIndex, mIndicesCount);
-	}
+	mGeomCount    = num_vertices;
+	mIndicesCount = num_indices;
 }
 
 //============================================================================
@@ -347,55 +248,22 @@ S32 LLFace::getGeometryAvatar(
 						LLStrider<LLVector4> &clothing_weights)
 {
 	LLMemType mt1(LLMemType::MTYPE_DRAWABLE);
-	
-	if (mGeomCount <= 0)
-	{
-		return -1;
-	}
-	
-	if (isState(BACKLIST))
-	{
-		if (!mBackupMem)
-		{
-			llerrs << "No backup memory for backlist" << llendl;
-		}
-
-		vertices        = (LLVector3*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_VERTICES]);
-		normals         = (LLVector3*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_NORMALS]);
-		binormals       = (LLVector3*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_BINORMALS]);
-		tex_coords      = (LLVector2*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_TEX_COORDS0]);
-		clothing_weights = (LLVector4*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_CLOTHING_WEIGHTS]);
-		vertex_weights  = (F32*)(mBackupMem + (4 * mIndicesCount) + (mGeomCount * mDrawPoolp->getStride()));
-		tex_coords.setStride( mDrawPoolp->getStride());
-		vertices.setStride( mDrawPoolp->getStride());
-		normals.setStride( mDrawPoolp->getStride());
-		binormals.setStride( mDrawPoolp->getStride());
-		clothing_weights.setStride( mDrawPoolp->getStride());
 
-		return 0;
+	if (mVertexBuffer.notNull())
+	{
+		mVertexBuffer->getVertexStrider      (vertices, mGeomIndex);
+		mVertexBuffer->getNormalStrider      (normals, mGeomIndex);
+		mVertexBuffer->getBinormalStrider    (binormals, mGeomIndex);
+		mVertexBuffer->getTexCoordStrider    (tex_coords, mGeomIndex);
+		mVertexBuffer->getWeightStrider(vertex_weights, mGeomIndex);
+		mVertexBuffer->getClothWeightStrider(clothing_weights, mGeomIndex);
 	}
 	else
 	{
-		if (!reserveIfNeeded())
-		{
-			return -1;
-		}
-
-		llassert(mGeomIndex >= 0);
-		llassert(mIndicesIndex >= 0);
-	
-		mDrawPoolp->getVertexStrider      (vertices, mGeomIndex);
-		mDrawPoolp->getNormalStrider      (normals, mGeomIndex);
-		mDrawPoolp->getBinormalStrider    (binormals, mGeomIndex);
-		mDrawPoolp->getTexCoordStrider    (tex_coords, mGeomIndex);
-		mDrawPoolp->getVertexWeightStrider(vertex_weights, mGeomIndex);
-		mDrawPoolp->getClothingWeightStrider(clothing_weights, mGeomIndex);
-
-		mDrawPoolp->setDirty();
-
-		llassert(mGeomIndex >= 0);
-		return mGeomIndex;
+		mGeomIndex = -1;
 	}
+
+	return mGeomIndex;
 }
 
 S32 LLFace::getGeometryTerrain(
@@ -404,64 +272,29 @@ S32 LLFace::getGeometryTerrain(
 						LLStrider<LLColor4U> &colors,
 						LLStrider<LLVector2> &texcoords0,
 						LLStrider<LLVector2> &texcoords1,
-						U32 *&indicesp)
+						LLStrider<U32> &indicesp)
 {
 	LLMemType mt1(LLMemType::MTYPE_DRAWABLE);
 	
-	if (mGeomCount <= 0)
-	{
-		return -1;
-	}
-	
-	if (isState(BACKLIST))
+	if (mVertexBuffer.notNull())
 	{
-		if (!mBackupMem)
-		{
-			printDebugInfo();
-			llerrs << "No backup memory for face" << llendl;
-		}
-		vertices  = (LLVector3*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_VERTICES]);
-		normals   = (LLVector3*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_NORMALS]);
-		colors   =  (LLColor4U*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_COLORS]);
-		texcoords0= (LLVector2*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_TEX_COORDS0]);
-		texcoords1= (LLVector2*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_TEX_COORDS1]);
-		texcoords0.setStride(mDrawPoolp->getStride());
-		texcoords1.setStride(mDrawPoolp->getStride());
-		vertices.setStride( mDrawPoolp->getStride());
-		normals.setStride( mDrawPoolp->getStride());
-		colors.setStride( mDrawPoolp->getStride());
-		indicesp    = (U32*)mBackupMem;
-
-		return 0;
+		mVertexBuffer->getVertexStrider(vertices, mGeomIndex);
+		mVertexBuffer->getNormalStrider(normals, mGeomIndex);
+		mVertexBuffer->getColorStrider(colors, mGeomIndex);
+		mVertexBuffer->getTexCoordStrider(texcoords0, mGeomIndex);
+		mVertexBuffer->getTexCoord2Strider(texcoords1, mGeomIndex);
+		mVertexBuffer->getIndexStrider(indicesp, mIndicesIndex);
 	}
 	else
 	{
-		if (!reserveIfNeeded())
-		{
-			llinfos << "Get geometry failed!" << llendl;
-			return -1;
-		}
-
-		llassert(mGeomIndex >= 0);
-		llassert(mIndicesIndex >= 0);
-	
-		mDrawPoolp->getVertexStrider(vertices, mGeomIndex);
-		mDrawPoolp->getNormalStrider(normals, mGeomIndex);
-		mDrawPoolp->getColorStrider(colors, mGeomIndex);
-		mDrawPoolp->getTexCoordStrider(texcoords0, mGeomIndex, 0);
-		mDrawPoolp->getTexCoordStrider(texcoords1, mGeomIndex, 1);
-
-		indicesp = mDrawPoolp->getIndices(mIndicesIndex);
-
-		mDrawPoolp->setDirty();
-
-		llassert(mGeomIndex >= 0);
-		return mGeomIndex;
+		mGeomIndex = -1;
 	}
+	
+	return mGeomIndex;
 }
 
 S32 LLFace::getGeometry(LLStrider<LLVector3> &vertices, LLStrider<LLVector3> &normals,
-					    LLStrider<LLVector2> &tex_coords, U32 *&indicesp)
+					    LLStrider<LLVector2> &tex_coords, LLStrider<U32> &indicesp)
 {
 	LLMemType mt1(LLMemType::MTYPE_DRAWABLE);
 	
@@ -470,55 +303,31 @@ S32 LLFace::getGeometry(LLStrider<LLVector3> &vertices, LLStrider<LLVector3> &no
 		return -1;
 	}
 	
-	if (isState(BACKLIST))
+	if (mVertexBuffer.notNull())
 	{
-		if (!mBackupMem)
+		mVertexBuffer->getVertexStrider(vertices,   mGeomIndex);
+		if (mVertexBuffer->hasDataType(LLVertexBuffer::TYPE_NORMAL))
 		{
-			printDebugInfo();
-			llerrs << "No backup memory for face" << llendl;
+			mVertexBuffer->getNormalStrider(normals,    mGeomIndex);
+		}
+		if (mVertexBuffer->hasDataType(LLVertexBuffer::TYPE_TEXCOORD))
+		{
+			mVertexBuffer->getTexCoordStrider(tex_coords, mGeomIndex);
 		}
-		vertices  = (LLVector3*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_VERTICES]);
-		normals   = (LLVector3*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_NORMALS]);
-		tex_coords= (LLVector2*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_TEX_COORDS0]);
-		tex_coords.setStride(mDrawPoolp->getStride());
-		vertices.setStride( mDrawPoolp->getStride());
-		normals.setStride( mDrawPoolp->getStride());
-		indicesp    = (U32*)mBackupMem;
 
-		return 0;
+		mVertexBuffer->getIndexStrider(indicesp, mIndicesIndex);
 	}
 	else
 	{
-		if (!reserveIfNeeded())
-		{
-			return -1;
-		}
-
-		llassert(mGeomIndex >= 0);
-		llassert(mIndicesIndex >= 0);
-	
-		mDrawPoolp->getVertexStrider(vertices,   mGeomIndex);
-		if (mDrawPoolp->mDataMaskIL & LLDrawPool::DATA_NORMALS_MASK)
-		{
-			mDrawPoolp->getNormalStrider(normals,    mGeomIndex);
-		}
-		if (mDrawPoolp->mDataMaskIL & LLDrawPool::DATA_TEX_COORDS0_MASK)
-		{
-			mDrawPoolp->getTexCoordStrider(tex_coords, mGeomIndex);
-		}
-
-		indicesp      =mDrawPoolp->getIndices   (mIndicesIndex);
-
-		mDrawPoolp->setDirty();
-
-		llassert(mGeomIndex >= 0);
-		return mGeomIndex;
+		mGeomIndex = -1;
 	}
+	
+	return mGeomIndex;
 }
 
 S32 LLFace::getGeometryColors(LLStrider<LLVector3> &vertices, LLStrider<LLVector3> &normals,
 							  LLStrider<LLVector2> &tex_coords, LLStrider<LLColor4U> &colors,
-							  U32 *&indicesp)
+							  LLStrider<U32> &indicesp)
 {
 	S32 res = getGeometry(vertices, normals, tex_coords, indicesp);
 	if (res >= 0)
@@ -528,95 +337,25 @@ S32 LLFace::getGeometryColors(LLStrider<LLVector3> &vertices, LLStrider<LLVector
 	return res;
 }
 
-S32 LLFace::getGeometryMultiTexture(
-	LLStrider<LLVector3> &vertices, 
-	LLStrider<LLVector3> &normals,
-	LLStrider<LLVector3> &binormals,
-	LLStrider<LLVector2> &tex_coords0,
-	LLStrider<LLVector2> &tex_coords1,
-	U32 *&indicesp)
+void LLFace::updateCenterAgent()
 {
-	LLMemType mt1(LLMemType::MTYPE_DRAWABLE);
-	
-	if (mGeomCount <= 0)
+	if (mDrawablep->isActive())
 	{
-		return -1;
-	}
-	
-	if (isState(BACKLIST))
-	{
-		if (!mBackupMem)
-		{
-			printDebugInfo();
-			llerrs << "No backup memory for face" << llendl;
-		}
-		vertices	= (LLVector3*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_VERTICES]);
-		normals		= (LLVector3*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_NORMALS]);
-		tex_coords0	= (LLVector2*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_TEX_COORDS0]);
-		tex_coords0.setStride(	mDrawPoolp->getStride() );
-		vertices.setStride(		mDrawPoolp->getStride() );
-		normals.setStride(		mDrawPoolp->getStride() );
-		if (mDrawPoolp->mDataMaskIL & LLDrawPool::DATA_BINORMALS_MASK)
-		{
-			binormals	= (LLVector3*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_BINORMALS]);
-			binormals.setStride(	mDrawPoolp->getStride() );
-		}
-		if (mDrawPoolp->mDataMaskIL & LLDrawPool::DATA_TEX_COORDS1_MASK)
-		{
-			tex_coords1	= (LLVector2*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_TEX_COORDS1]);
-			tex_coords1.setStride(	mDrawPoolp->getStride() );
-		}
-		indicesp	= (U32*)mBackupMem;
-
-		return 0;
+		mCenterAgent = mCenterLocal * getRenderMatrix();
 	}
 	else
 	{
-		if (!reserveIfNeeded())
-		{
-			return -1;
-		}
-
-		llassert(mGeomIndex >= 0);
-		llassert(mIndicesIndex >= 0);
-	
-		mDrawPoolp->getVertexStrider(vertices, mGeomIndex);
-		if (mDrawPoolp->mDataMaskIL & LLDrawPool::DATA_NORMALS_MASK)
-		{
-			mDrawPoolp->getNormalStrider(normals, mGeomIndex);
-		}
-		if (mDrawPoolp->mDataMaskIL & LLDrawPool::DATA_TEX_COORDS0_MASK)
-		{
-			mDrawPoolp->getTexCoordStrider(tex_coords0, mGeomIndex);
-		}
-		if (mDrawPoolp->mDataMaskIL & LLDrawPool::DATA_BINORMALS_MASK)
-		{
-			mDrawPoolp->getBinormalStrider(binormals,  mGeomIndex);
-		}
-		if (mDrawPoolp->mDataMaskIL & LLDrawPool::DATA_TEX_COORDS1_MASK)
-		{
-			mDrawPoolp->getTexCoordStrider(tex_coords1, mGeomIndex, 1);
-		}
-		indicesp = mDrawPoolp->getIndices(mIndicesIndex);
-
-		mDrawPoolp->setDirty();
-
-		llassert(mGeomIndex >= 0);
-		return mGeomIndex;
+		mCenterAgent = mCenterLocal;
 	}
 }
 
-void LLFace::updateCenterAgent()
-{
-	mCenterAgent = mCenterLocal * getRenderMatrix();
-}
-
-void LLFace::renderForSelect() const
+void LLFace::renderForSelect(U32 data_mask)
 {
-	if(mGeomIndex < 0 || mDrawablep.isNull())
+	if(mGeomIndex < 0 || mDrawablep.isNull() || mVertexBuffer.isNull())
 	{
 		return;
 	}
+
 	if (mVObjp->mGLName)
 	{
 		S32 name = mVObjp->mGLName;
@@ -630,47 +369,25 @@ void LLFace::renderForSelect() const
 #endif
 		glColor4ubv(color.mV);
 
-		if (mVObjp->getPCode() == LL_PCODE_VOLUME)
+		if (!getPool())
 		{
-			LLVOVolume *volp;
-			volp = (LLVOVolume *)(LLViewerObject*)mVObjp;
-			if (volp->getNumFaces() == 1 && !volp->getVolumeChanged())
+			switch (getPoolType())
 			{
-				// We need to special case the coalesced face model.
-				S32 num_vfs = volp->getVolume()->getNumFaces();
-				S32 offset = 0;
-				S32 i;
-				
-				for (i = 0; i < num_vfs; i++)
-				{
-					if (gPickFaces)
-					{
-						// mask off high 4 bits (16 total possible faces)
-						color.mV[0] &= 0x0f;
-						color.mV[0] |= (i & 0x0f) << 4;
-						glColor4ubv(color.mV);
-					}
-					S32 count = volp->getVolume()->getVolumeFace(i).mIndices.size();
-					if (isState(GLOBAL))
-					{
-						glDrawElements(mPrimType, count, GL_UNSIGNED_INT, getRawIndices() + offset); 
-					}
-					else
-					{
-						glPushMatrix();
-						glMultMatrixf((float*)getRenderMatrix().mMatrix);
-						glDrawElements(mPrimType, count, GL_UNSIGNED_INT, getRawIndices() + offset); 
-						glPopMatrix();
-					}
-					offset += count;
-				}
-				// We're done, return.
-				return;
+			case LLDrawPool::POOL_ALPHA:
+				getTexture()->bind();
+				break;
+			default:
+				LLImageGL::unbindTexture(0);
+				break;
 			}
-
-			// We don't have coalesced faces, do this the normal way.
 		}
 
+		mVertexBuffer->setBuffer(data_mask);
+#if !LL_RELEASE_FOR_DOWNLOAD
+		LLGLState::checkClientArrays(data_mask);
+#endif
+		U32* indicesp = (U32*) mVertexBuffer->getIndicesPointer() + mIndicesIndex;
+
 		if (gPickFaces && mTEOffset != -1)
 		{
 			// mask off high 4 bits (16 total possible faces)
@@ -683,13 +400,13 @@ void LLFace::renderForSelect() const
 		{
 			if (isState(GLOBAL))
 			{
-				glDrawElements(mPrimType, mIndicesCount, GL_UNSIGNED_INT, getRawIndices()); 
+				glDrawElements(GL_TRIANGLES, mIndicesCount, GL_UNSIGNED_INT, indicesp); 
 			}
 			else
 			{
 				glPushMatrix();
 				glMultMatrixf((float*)getRenderMatrix().mMatrix);
-				glDrawElements(mPrimType, mIndicesCount, GL_UNSIGNED_INT, getRawIndices()); 
+				glDrawElements(GL_TRIANGLES, mIndicesCount, GL_UNSIGNED_INT, indicesp); 
 				glPopMatrix();
 			}
 		}
@@ -697,13 +414,13 @@ void LLFace::renderForSelect() const
 		{
 			if (isState(GLOBAL))
 			{
-				glDrawArrays(mPrimType, mGeomIndex, mGeomCount); 
+				glDrawArrays(GL_TRIANGLES, mGeomIndex, mGeomCount); 
 			}
 			else
 			{
 				glPushMatrix();
 				glMultMatrixf((float*)getRenderMatrix().mMatrix);
-				glDrawArrays(mPrimType, mGeomIndex, mGeomCount); 
+				glDrawArrays(GL_TRIANGLES, mGeomIndex, mGeomCount); 
 				glPopMatrix();
 			}
 		}
@@ -712,11 +429,12 @@ void LLFace::renderForSelect() const
 
 void LLFace::renderSelected(LLImageGL *imagep, const LLColor4& color, const S32 offset, const S32 count)
 {
-	if(mGeomIndex < 0 || mDrawablep.isNull())
+	if(mGeomIndex < 0 || mDrawablep.isNull() || mVertexBuffer.isNull())
 	{
 		return;
 	}
-	if (mGeomCount > 0)
+
+	if (mGeomCount > 0 && mIndicesCount > 0)
 	{
 		LLGLSPipelineAlpha gls_pipeline_alpha;
 		glColor4fv(color.mV);
@@ -729,110 +447,27 @@ void LLFace::renderSelected(LLImageGL *imagep, const LLColor4& color, const S32
 			glMultMatrixf((float*)getRenderMatrix().mMatrix);
 		}
 
-		if (sSafeRenderSelect)
-		{
-			glBegin(mPrimType);
-			if (count)
-			{
-				for (S32 i = offset; i < offset + count; i++)
-				{
-					LLVector2 tc = mDrawPoolp->getTexCoord(mDrawPoolp->getIndex(getIndicesStart() + i), 0);
-					glTexCoord2fv(tc.mV);
-					LLVector3 normal = mDrawPoolp->getNormal(mDrawPoolp->getIndex(getIndicesStart() + i));
-					glNormal3fv(normal.mV);
-					LLVector3 vertex = mDrawPoolp->getVertex(mDrawPoolp->getIndex(getIndicesStart() + i));
-					glVertex3fv(vertex.mV);
-				}
-			}
-			else
-			{
-				for (U32 i = 0; i < getIndicesCount(); i++)
-				{
-					LLVector2 tc = mDrawPoolp->getTexCoord(mDrawPoolp->getIndex(getIndicesStart() + i), 0);
-					glTexCoord2fv(tc.mV);
-					LLVector3 normal = mDrawPoolp->getNormal(mDrawPoolp->getIndex(getIndicesStart() + i));
-					glNormal3fv(normal.mV);
-					LLVector3 vertex = mDrawPoolp->getVertex(mDrawPoolp->getIndex(getIndicesStart() + i));
-					glVertex3fv(vertex.mV);
-				}
-			}
-			glEnd();
-
-			if( gSavedSettings.getBOOL("ShowTangentBasis") )
-			{
-				S32 start;
-				S32 end;
-				if (count)
-				{
-					start = offset;
-					end = offset + count;
-				}
-				else
-				{
-					start = 0;
-					end = getIndicesCount();
-				}
+		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+		glEnableClientState(GL_VERTEX_ARRAY);
+		glEnableClientState(GL_NORMAL_ARRAY);
 
-				LLGLSNoTexture gls_no_texture;
-				glColor4f(1, 1, 1, 1);
-				glBegin(GL_LINES);
-				for (S32 i = start; i < end; i++)
-				{
-					LLVector3 vertex = mDrawPoolp->getVertex(mDrawPoolp->getIndex(getIndicesStart() + i));
-					glVertex3fv(vertex.mV);
-					LLVector3 normal = mDrawPoolp->getNormal(mDrawPoolp->getIndex(getIndicesStart() + i));
-					glVertex3fv( (vertex + normal * 0.1f).mV );
-				}
-				glEnd();
+		mVertexBuffer->setBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD);
+#if !LL_RELEASE_FOR_DOWNLOAD
+		LLGLState::checkClientArrays(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD);
+#endif
+		U32* indicesp = ((U32*) mVertexBuffer->getIndicesPointer()) + mIndicesIndex;
 
-				if (mDrawPoolp->mDataMaskIL & LLDrawPool::DATA_BINORMALS_MASK)
-				{
-					glColor4f(0, 1, 0, 1);
-					glBegin(GL_LINES);
-					for (S32 i = start; i < end; i++)
-					{
-						LLVector3 vertex = mDrawPoolp->getVertex(mDrawPoolp->getIndex(getIndicesStart() + i));
-						glVertex3fv(vertex.mV);
-						LLVector3 binormal = mDrawPoolp->getBinormal(mDrawPoolp->getIndex(getIndicesStart() + i));
-						glVertex3fv( (vertex + binormal * 0.1f).mV );
-					}
-					glEnd();
-				}
-			}
+		if (count)
+		{
+			glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_INT, indicesp + offset); 
 		}
 		else
 		{
-			glEnableClientState(GL_TEXTURE_COORD_ARRAY);
-			glEnableClientState(GL_VERTEX_ARRAY);
-			glEnableClientState(GL_NORMAL_ARRAY);
-			if (count)
-			{
-				if (mIndicesCount > 0)
-				{
-					glDrawElements(mPrimType, count, GL_UNSIGNED_INT, getRawIndices() + offset); 
-				}
-				else
-				{
-					llerrs << "Rendering non-indexed volume face!" << llendl;
-					glDrawArrays(mPrimType, mGeomIndex, mGeomCount); 
-				}
-			}
-			else
-			{
-				if (mIndicesCount > 0)
-				{
-					glDrawElements(mPrimType, mIndicesCount, GL_UNSIGNED_INT, getRawIndices()); 
-				}
-				else
-				{
-					glDrawArrays(mPrimType, mGeomIndex, mGeomCount); 
-				}
-			}
-			glDisableClientState(GL_TEXTURE_COORD_ARRAY);
-			glDisableClientState(GL_VERTEX_ARRAY);
-			glDisableClientState(GL_NORMAL_ARRAY);
+			glDrawElements(GL_TRIANGLES, mIndicesCount, GL_UNSIGNED_INT, indicesp); 
 		}
-
+		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+		glDisableClientState(GL_NORMAL_ARRAY);
+		
 		if (!isState(GLOBAL))
 		{
 			// Restore the tranform for non-global objects
@@ -843,6 +478,7 @@ void LLFace::renderSelected(LLImageGL *imagep, const LLColor4& color, const S32
 
 void LLFace::renderSelectedUV(const S32 offset, const S32 count)
 {
+#if 0
 	LLUUID uv_img_red_blue_id(gViewerArt.getString("uv_test1.tga"));
 	LLUUID uv_img_green_id(gViewerArt.getString("uv_test2.tga"));
 	LLViewerImage* red_blue_imagep = gImageList.getImage(uv_img_red_blue_id, TRUE, TRUE);
@@ -892,7 +528,7 @@ void LLFace::renderSelectedUV(const S32 offset, const S32 count)
 			glPolygonOffset(factor, bias);
 			if (sSafeRenderSelect)
 			{
-				glBegin(mPrimType);
+				glBegin(GL_TRIANGLES);
 				if (count)
 				{
 					for (S32 i = offset; i < offset + count; i++)
@@ -924,7 +560,7 @@ void LLFace::renderSelectedUV(const S32 offset, const S32 count)
 				{
 					if (mIndicesCount > 0)
 					{
-						glDrawElements(mPrimType, count, GL_UNSIGNED_INT, getRawIndices() + offset); 
+						glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_INT, getRawIndices() + offset); 
 					}
 					else
 					{
@@ -936,15 +572,14 @@ void LLFace::renderSelectedUV(const S32 offset, const S32 count)
 				{
 					if (mIndicesCount > 0)
 					{
-						glDrawElements(mPrimType, mIndicesCount, GL_UNSIGNED_INT, getRawIndices()); 
+						glDrawElements(GL_TRIANGLES, mIndicesCount, GL_UNSIGNED_INT, getRawIndices()); 
 					}
 					else
 					{
-						glDrawArrays(mPrimType, mGeomIndex, mGeomCount); 
+						glDrawArrays(GL_TRIANGLES, mGeomIndex, mGeomCount); 
 					}
 				}
 				glDisableClientState(GL_TEXTURE_COORD_ARRAY);
-				glDisableClientState(GL_VERTEX_ARRAY);
 			}
 
 			glDisable(GL_POLYGON_OFFSET_FILL);
@@ -965,12 +600,13 @@ void LLFace::renderSelectedUV(const S32 offset, const S32 count)
 	
 	//restore blend func
 	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+#endif
 }
 
 
 void LLFace::printDebugInfo() const
 {
-	LLDrawPool *poolp = getPool();
+	LLFacePool *poolp = getPool();
 	llinfos << "Object: " << getViewerObject()->mID << llendl;
 	if (getDrawable())
 	{
@@ -986,10 +622,6 @@ void LLFace::printDebugInfo() const
 	}
 
 	llinfos << "Face: " << this << llendl;
-	if (isState(BACKLIST))
-	{
-		llinfos << "Backlisted!" << llendl;
-	}
 	llinfos << "State: " << getState() << llendl;
 	llinfos << "Geom Index Data:" << llendl;
 	llinfos << "--------------------" << llendl;
@@ -1018,7 +650,7 @@ void LLFace::printDebugInfo() const
 		llinfos << "Incorrect number of pool references!" << llendl;
 	}
 
-
+#if 0
 	llinfos << "Indices:" << llendl;
 	llinfos << "--------------------" << llendl;
 
@@ -1039,222 +671,229 @@ void LLFace::printDebugInfo() const
 		llinfos << mGeomIndex + i << ":" << poolp->getVertex(mGeomIndex + i) << llendl;
 	}
 	llinfos << llendl;
+#endif
+}
+
+// Transform the texture coordinates for this face.
+static void xform(LLVector2 &tex_coord, F32 cosAng, F32 sinAng, F32 offS, F32 offT, F32 magS, F32 magT)
+{
+	// New, good way
+	F32 s = tex_coord.mV[0];
+	F32 t = tex_coord.mV[1];
+
+	// Texture transforms are done about the center of the face.
+	s -= 0.5; 
+	t -= 0.5;
+
+	// Handle rotation
+	F32 temp = s;
+	s  = s     * cosAng + t * sinAng;
+	t  = -temp * sinAng + t * cosAng;
+
+	// Then scale
+	s *= magS;
+	t *= magT;
+
+	// Then offset
+	s += offS + 0.5f; 
+	t += offT + 0.5f;
+
+	tex_coord.mV[0] = s;
+	tex_coord.mV[1] = t;
 }
 
-S32 LLFace::backup()
+
+BOOL LLFace::genVolumeBBoxes(const LLVolume &volume, S32 f,
+								const LLMatrix4& mat_vert, const LLMatrix3& mat_normal, BOOL global_volume)
 {
 	LLMemType mt1(LLMemType::MTYPE_DRAWABLE);
+
+	const LLVolumeFace &face = volume.getVolumeFace(f);
 	
-	if (isState(BACKLIST))
-	{
-		llwarns << "Face is already backed up in LLFace::backup!" << llendl;
-		return mGeomCount;
-	}
-	if (mGeomIndex < 0)
-	{
-		// flexible objects can cause this
-		//llwarns << "No geometry to back-up" << llendl;
-		return 0;
-	}
-	
-	S32 size = 0;
-	if (!mBackupMem)
-	{
-		size = allocBackupMem();
-	}
-	else
+	//get bounding box
+	if (mDrawablep->isState(LLDrawable::REBUILD_ALL))
 	{
-		llerrs << "Memory already backed up!" << llendl;
-	}
+		//vertex buffer no longer valid
+		mVertexBuffer = NULL;
+		mLastVertexBuffer = NULL;
 
-	// Need to flag this, because we can allocate a non-zero backup mem if we have indices and no geometry.
+		LLVector3 min,max;
+			
+		min = face.mExtents[0];
+		max = face.mExtents[1];
 
-	if (mGeomCount || mIndicesCount)
-	{
-		setState(BACKLIST);
-#if !RELEASE_FOR_DOWNLOAD
-		if (mGeomIndex < 0 || mIndicesIndex < 0)
+		//min, max are in volume space, convert to drawable render space
+		LLVector3 center = ((min + max) * 0.5f)*mat_vert;
+		LLVector3 size = ((max-min) * 0.5f);
+		if (!global_volume)
 		{
-			llerrs << "LLFace::backup" << llendl;
+			size.scaleVec(mDrawablep->getVObj()->getScale());
 		}
-#endif
+		LLQuaternion rotation = LLQuaternion(mat_normal);
 		
-		U32 *backup  = (U32*)mBackupMem;
-		S32 stride   = mDrawPoolp->getStride();
+		LLVector3 v[4];
+		//get 4 corners of bounding box
+		v[0] = (size * rotation);
+		v[1] = (LLVector3(-size.mV[0], -size.mV[1], size.mV[2]) * rotation);
+		v[2] = (LLVector3(size.mV[0], -size.mV[1], -size.mV[2]) * rotation);
+		v[3] = (LLVector3(-size.mV[0], size.mV[1], -size.mV[2]) * rotation);
+
+		LLVector3& newMin = mExtents[0];
+		LLVector3& newMax = mExtents[1];
 		
-		U32 *index   = mDrawPoolp->getIndices(mIndicesIndex);
-		for (U32 i=0;i<mIndicesCount;i++)
+		newMin = newMax = center;
+		
+		for (U32 i = 0; i < 4; i++)
 		{
-			*backup++ = index[i] - mGeomIndex;
-			index[i]  = 0;
-		}
-
-		if (!mGeomCount)
-		{
-			return mGeomCount;
-		}
-		//
-		// Don't change the order of these unles you change the corresponding getGeometry calls that read out of
-		// backup memory, and also the other of the backup/restore pair!
-		//
-		memcpy(backup, (mDrawPoolp->mMemory.getMem() + mGeomIndex * stride), mGeomCount * stride);	 /*Flawfinder: ignore*/
-		backup += mGeomCount * stride / 4;
-
-		if (mDrawPoolp->mDataMaskNIL & LLDrawPool::DATA_CLOTHING_WEIGHTS_MASK)
-		{
-			memcpy(backup, &mDrawPoolp->getClothingWeight(mGeomIndex), mGeomCount * sizeof(LLVector4)); /*Flawfinder: ignore*/
-			backup += mGeomCount*4;
-		}
-
-		if (mDrawPoolp->mDataMaskNIL & LLDrawPool::DATA_VERTEX_WEIGHTS_MASK)
-		{
-			memcpy(backup, &mDrawPoolp->getVertexWeight(mGeomIndex), mGeomCount * sizeof(F32));	 /*Flawfinder: ignore*/
-			backup += mGeomCount;
+			for (U32 j = 0; j < 3; j++)
+			{
+				F32 delta = fabsf(v[i].mV[j]);
+				F32 min = center.mV[j] - delta;
+				F32 max = center.mV[j] + delta;
+				
+				if (min < newMin.mV[j])
+				{
+					newMin.mV[j] = min;
+				}
+				
+				if (max > newMax.mV[j])
+				{
+					newMax.mV[j] = max;
+				}
+			}
 		}
 
-		llassert((U8*)backup - mBackupMem == size);
-
-		unReserve();
+		mCenterLocal = (newMin+newMax)*0.5f;
+		updateCenterAgent();
 	}
-	return mGeomCount;
+
+	return TRUE;
 }
 
-void LLFace::restore()
+
+BOOL LLFace::getGeometryVolume(const LLVolume& volume,
+							   S32 f,
+								LLStrider<LLVector3>& vertices, 
+								LLStrider<LLVector3>& normals,
+								LLStrider<LLVector2>& tex_coords,
+								LLStrider<LLVector2>& tex_coords2,
+								LLStrider<LLColor4U>& colors,
+								LLStrider<U32>& indicesp,
+								const LLMatrix4& mat_vert, const LLMatrix3& mat_normal,
+								U32& index_offset)
 {
-	LLMemType mt1(LLMemType::MTYPE_DRAWABLE);
+	const LLVolumeFace &vf = volume.getVolumeFace(f);
+	S32 num_vertices = (S32)vf.mVertices.size();
+	S32 num_indices = (S32)vf.mIndices.size();
 	
-	if (!isState(BACKLIST))
-	{
-		// flexible objects can cause this
-// 		printDebugInfo();
-// 		llwarns << "not backlisted for restore" << llendl;
-		return;
-	}
-	if (!mGeomCount || !mBackupMem) 
-	{
-		if (!mBackupMem)
-		{
-			printDebugInfo();
-			llwarns  << "no backmem for restore" << llendl;
-		}
+	LLStrider<LLVector3> old_verts;
+	LLStrider<LLVector2> old_texcoords;
+	LLStrider<LLVector2> old_texcoords2;
+	LLStrider<LLVector3> old_normals;
+	LLStrider<LLColor4U> old_colors;
 
-		clearState(BACKLIST);
-		return;
-	}
-
-	S32 stride     = mDrawPoolp->getStride();
-	mGeomIndex     = mDrawPoolp->reserveGeom(mGeomCount);
-	mIndicesIndex  = mDrawPoolp->reserveInd (mIndicesCount);
-	mGeneration    = mDrawPoolp->mGeneration;
+	BOOL full_rebuild = mDrawablep->isState(LLDrawable::REBUILD_VOLUME);
+	BOOL moved = TRUE;
 
-	llassert(mGeomIndex >= 0);
-	llassert(mIndicesIndex >= 0);
-	
-	U32 *backup  = (U32*)mBackupMem;
-	U32 *index   = mDrawPoolp->getIndices(mIndicesIndex);
-
-	for (U32 i=0;i<mIndicesCount;i++)
+	BOOL global_volume = mDrawablep->getVOVolume()->isVolumeGlobal();
+	LLVector3 scale;
+	if (global_volume)
 	{
-		S32  ind = mGeomIndex + *backup;
-		index[i] = ind;
-		backup++;
+		scale.setVec(1,1,1);
 	}
-
-	mDrawPoolp->mMemory.copyToMem(mGeomIndex * stride, (U8 *)backup, mGeomCount * stride);
-	backup += mGeomCount * stride / 4;
-
-	//
-	// Don't change the order of these unles you change the corresponding getGeometry calls that read out of
-	// backup memory, and also the other of the backup/restore pair!
-	//
-	if (mDrawPoolp->mDataMaskNIL & LLDrawPool::DATA_CLOTHING_WEIGHTS_MASK)
-	{
-		mDrawPoolp->mClothingWeights.copyToMem(mGeomIndex, (U8 *)backup, mGeomCount);
-		backup += mGeomCount*4;
-	}
-
-	if (mDrawPoolp->mDataMaskNIL & LLDrawPool::DATA_VERTEX_WEIGHTS_MASK)
+	else
 	{
-		mDrawPoolp->mWeights.copyToMem(mGeomIndex, (U8 *)backup, mGeomCount);
-		backup += mGeomCount;
+		scale = mVObjp->getScale();
 	}
 
-	delete[] mBackupMem;
-	mBackupMem = NULL;
-	clearState(BACKLIST);
-}
-
-// Transform the texture coordinates for this face.
-static void xform(LLVector2 &tex_coord, F32 cosAng, F32 sinAng, F32 offS, F32 offT, F32 magS, F32 magT)
-{
-	// New, good way
-	F32 s = tex_coord.mV[0];
-	F32 t = tex_coord.mV[1];
-
-	// Texture transforms are done about the center of the face.
-	s -= 0.5; 
-	t -= 0.5;
-
-	// Handle rotation
-	F32 temp = s;
-	s  = s     * cosAng + t * sinAng;
-	t  = -temp * sinAng + t * cosAng;
-
-	// Then scale
-	s *= magS;
-	t *= magT;
-
-	// Then offset
-	s += offS + 0.5f; 
-	t += offT + 0.5f;
+	if (!full_rebuild)
+	{   
+		if (mLastVertexBuffer == mVertexBuffer && 
+			!mVertexBuffer->isEmpty())
+		{	//this face really doesn't need to be regenerated, try real hard not to do so
+			if (mLastGeomCount == mGeomCount &&
+				mLastGeomIndex == mGeomIndex &&
+				mLastIndicesCount == mIndicesCount &&
+				mLastIndicesIndex == mIndicesIndex)
+			{ //data is in same location in vertex buffer
+				moved = FALSE;
+			}
 
-	tex_coord.mV[0] = s;
-	tex_coord.mV[1] = t;
-}
+			if (!moved && !mDrawablep->isState(LLDrawable::REBUILD_ALL))
+			{ //nothing needs to be done
+				vertices += mGeomCount;
+				normals += mGeomCount;
+				tex_coords += mGeomCount;
+				colors += mGeomCount;
+				tex_coords2 += mGeomCount;
+				index_offset += mGeomCount;
+				indicesp += mIndicesCount;
+				return FALSE;
+			}
 
+			if (mLastGeomCount == mGeomCount)
+			{
+				if (mLastGeomIndex >= mGeomIndex && 
+					mLastGeomIndex + mGeomCount+1 < mVertexBuffer->getNumVerts())
+				{
+					//copy from further down the buffer
+					mVertexBuffer->getVertexStrider(old_verts, mLastGeomIndex);
+					mVertexBuffer->getTexCoordStrider(old_texcoords, mLastGeomIndex);
+					mVertexBuffer->getTexCoord2Strider(old_texcoords2, mLastGeomIndex);
+					mVertexBuffer->getNormalStrider(old_normals, mLastGeomIndex);
+					mVertexBuffer->getColorStrider(old_colors, mLastGeomIndex);
+
+					if (!mDrawablep->isState(LLDrawable::REBUILD_ALL))
+					{
+						//quick copy
+						for (S32 i = 0; i < mGeomCount; i++)
+						{
+							*vertices++ = *old_verts++;
+							*tex_coords++ = *old_texcoords++;
+							*tex_coords2++ = *old_texcoords2++;
+							*colors++ = *old_colors++;
+							*normals++ = *old_normals++;
+						}
 
-BOOL LLFace::genVolumeTriangles(const LLVolume &volume, S32 f,
-								const LLMatrix4& mat, const LLMatrix3& inv_trans_mat, BOOL global_volume)
-{
-	const LLVolumeFace &vf = volume.getVolumeFace(f);
-	S32 num_vertices = (S32)vf.mVertices.size();
-	S32 num_indices = (S32)vf.mIndices.size();
-	setSize(num_vertices, num_indices);
-	
-	return genVolumeTriangles(volume, f, f, mat, inv_trans_mat, global_volume);
-}
+						for (U32 i = 0; i < mIndicesCount; i++)
+						{
+							*indicesp++ = vf.mIndices[i] + index_offset;
+						}
 
-BOOL LLFace::genVolumeTriangles(const LLVolume &volume, S32 fstart, S32 fend, 
-								const LLMatrix4& mat_vert, const LLMatrix3& mat_normal, const BOOL global_volume)
-{
-	LLMemType mt1(LLMemType::MTYPE_DRAWABLE);
+						index_offset += mGeomCount;
+						mLastGeomIndex = mGeomIndex;
+						mLastIndicesCount = mIndicesCount;
+						mLastIndicesIndex = mIndicesIndex;
 
-	if (!mDrawablep)
+						return TRUE;
+					}
+				}
+				else
+				{
+					full_rebuild = TRUE;
+				}
+			}
+		}
+		else
+		{
+			full_rebuild = TRUE;
+		}
+	}
+	else
 	{
-		return TRUE;
+		mLastUpdateTime = gFrameTimeSeconds;
 	}
 
-	S32 index_offset;
-	F32 r, os, ot, ms, mt, cos_ang, sin_ang;
-	LLStrider<LLVector3> vertices;
-	LLStrider<LLVector3> normals;
-	LLStrider<LLVector3> binormals;
-	LLStrider<LLVector2> tex_coords;
-	LLStrider<LLVector2> tex_coords2;
-	U32                 *indicesp = NULL;
 
-	BOOL bump = mDrawPoolp && (mDrawPoolp->mDataMaskIL & LLDrawPool::DATA_BINORMALS_MASK);
+	BOOL rebuild_pos = full_rebuild || mDrawablep->isState(LLDrawable::REBUILD_POSITION);
+	BOOL rebuild_color = full_rebuild || mDrawablep->isState(LLDrawable::REBUILD_COLOR);
+	BOOL rebuild_tcoord = full_rebuild || mDrawablep->isState(LLDrawable::REBUILD_TCOORD);
+
+	F32 r = 0, os = 0, ot = 0, ms = 0, mt = 0, cos_ang = 0, sin_ang = 0;
+	
 	BOOL is_static = mDrawablep->isStatic();
 	BOOL is_global = is_static;
-	
-	if (bump)
-	{
-		index_offset = getGeometryMultiTexture(vertices, normals, binormals, tex_coords, tex_coords2, indicesp);
-	}
-	else
-	{
-		index_offset = getGeometry(vertices, normals, tex_coords, indicesp);
-	}
+
 	if (-1 == index_offset)
 	{
 		return TRUE;
@@ -1262,16 +901,6 @@ BOOL LLFace::genVolumeTriangles(const LLVolume &volume, S32 fstart, S32 fend,
 	
 	LLVector3 center_sum(0.f, 0.f, 0.f);
 	
-	LLVector3 render_pos;
-	
-	if (mDrawablep->isState(LLDrawable::REBUILD_TCOORD) &&
-		global_volume)
-	{
-		render_pos = mVObjp->getRenderPosition();
-	}
-	
-	setPrimType(LLTriangles);
-	
 	if (is_global)
 	{
 		setState(GLOBAL);
@@ -1280,26 +909,16 @@ BOOL LLFace::genVolumeTriangles(const LLVolume &volume, S32 fstart, S32 fend,
 	{
 		clearState(GLOBAL);
 	}
-	
-	LLVector3 min, max;
+
 	LLVector2 tmin, tmax;
 	
-	BOOL grab_first_vert = TRUE;
-	BOOL grab_first_tcoord = TRUE;
-	
-	for (S32 vol_face = fstart; vol_face <= fend; vol_face++)
+	const LLTextureEntry *tep = mVObjp->getTE(f);
+	U8  bump_code = tep ? bump_code = tep->getBumpmap() : 0;
+
+	if (rebuild_tcoord)
 	{
-		const LLVolumeFace &vf = volume.getVolumeFace(vol_face);
-		S32 num_vertices = (S32)vf.mVertices.size();
-		S32 num_indices = (S32)vf.mIndices.size();
-		llassert(num_indices > 0);
-		
-		U8  bump_code;		
-		const LLTextureEntry *tep = mVObjp->getTE(vol_face);
-		
 		if (tep)
 		{
-			bump_code = tep->getBumpmap();
 			r  = tep->getRotation();
 			os = tep->mOffsetS;
 			ot = tep->mOffsetT;
@@ -1310,7 +929,6 @@ BOOL LLFace::genVolumeTriangles(const LLVolume &volume, S32 fstart, S32 fend,
 		}
 		else
 		{
-			bump_code = 0;
 			cos_ang = 1.0f;
 			sin_ang = 0.0f;
 			os = 0.0f;
@@ -1318,209 +936,228 @@ BOOL LLFace::genVolumeTriangles(const LLVolume &volume, S32 fstart, S32 fend,
 			ms = 1.0f;
 			mt = 1.0f;
 		}
+	}
 
-		if (mDrawablep->isState(LLDrawable::REBUILD_VOLUME))
+	if (isState(TEXTURE_ANIM))
+	{
+		LLVOVolume* vobj = (LLVOVolume*) (LLViewerObject*) mVObjp;
+		U8 mode = vobj->mTexAnimMode;
+		if (mode & LLViewerTextureAnim::TRANSLATE)
 		{
-			// VERTICES & NORMALS			
-			for (S32 i = 0; i < num_vertices; i++)
-			{
-				LLVector3 v;
-				v = vf.mVertices[i].mPosition * mat_vert;
+			os = ot = 0.f;
+		}
+		if (mode & LLViewerTextureAnim::ROTATE)
+		{
+			r = 0.f;
+			cos_ang = 1.f;
+			sin_ang = 0.f;
+		}
+		if (mode & LLViewerTextureAnim::SCALE)
+		{
+			ms = mt = 1.f;
+		}
+	}
 
-				LLVector3 normal = vf.mVertices[i].mNormal * mat_normal;
-				normal.normVec();
-				*normals++ = normal;
-				
-				*vertices++ = v;
-				
-				if (grab_first_vert)
-				{
-					grab_first_vert = FALSE;
-					min = max = v;
-				}
-				else
-				{
-					for (U32 j = 0; j < 3; j++)
-					{
-						if (v.mV[j] < min.mV[j])
-						{
-							min.mV[j] = v.mV[j];
-						}
-						if (v.mV[j] > max.mV[j])
-						{
-							max.mV[j] = v.mV[j];
-						}
-					}
-				}
+	LLColor4U color = tep->getColor();
+
+	if (rebuild_color)
+	{
+		GLfloat alpha[4] =
+		{
+			0.00f,
+			0.25f,
+			0.5f,
+			0.75f
+		};
+
+		if (gPipeline.getPoolTypeFromTE(tep, getTexture()) == LLDrawPool::POOL_BUMP)
+		{
+			color.mV[3] = U8 (alpha[tep->getShiny()] * 255);
+		}
+	}
+
+    // INDICES
+	if (full_rebuild || moved)
+	{
+		for (S32 i = 0; i < num_indices; i++)
+		{
+			*indicesp++ = vf.mIndices[i] + index_offset;
+		}
+	}
+	else
+	{
+		indicesp += num_indices;
+	}
+	
+	//bump setup
+	LLVector3 binormal_dir( -sin_ang, cos_ang, 0 );
+	LLVector3 bump_s_primary_light_ray;
+	LLVector3 bump_t_primary_light_ray;
+	
+	if (bump_code)
+	{
+		F32 offset_multiple; 
+		switch( bump_code )
+		{
+			case BE_NO_BUMP:
+			offset_multiple = 0.f;
+			break;
+			case BE_BRIGHTNESS:
+			case BE_DARKNESS:
+			if( mTexture.notNull() && mTexture->getHasGLTexture())
+			{
+				// Offset by approximately one texel
+				S32 cur_discard = mTexture->getDiscardLevel();
+				S32 max_size = llmax( mTexture->getWidth(), mTexture->getHeight() );
+				max_size <<= cur_discard;
+				const F32 ARTIFICIAL_OFFSET = 2.f;
+				offset_multiple = ARTIFICIAL_OFFSET / (F32)max_size;
 			}
-			for (S32 i = 0; i < num_indices; i++)
+			else
 			{
-				S32 index = vf.mIndices[i] + index_offset;
-				llassert(index >= 0 && (i != 1 || *(indicesp-1)!=(U32)index));
-				*indicesp++ = index;
+				offset_multiple = 1.f/256;
 			}
+			break;
+
+			default:  // Standard bumpmap textures.  Assumed to be 256x256
+			offset_multiple = 1.f / 256;
+			break;
 		}
 
-		if ((mDrawablep->isState(LLDrawable::REBUILD_TCOORD)) ||
-			((bump || getTextureEntry()->getTexGen() != 0) && mDrawablep->isState(LLDrawable::REBUILD_VOLUME)))
+		F32 s_scale = 1.f;
+		F32 t_scale = 1.f;
+		if( tep )
 		{
-			// TEX COORDS AND BINORMALS
-			LLVector3 binormal_dir( -sin_ang, cos_ang, 0 );
-			LLVector3 bump_s_primary_light_ray;
-			LLVector3 bump_t_primary_light_ray;
-			if (bump)
-			{
-				F32 offset_multiple; 
-				switch( bump_code )
-				{
-				  case BE_NO_BUMP:
-					offset_multiple = 0.f;
-					break;
-				  case BE_BRIGHTNESS:
-				  case BE_DARKNESS:
-					if( mTexture.notNull() && mTexture->getHasGLTexture())
-					{
-						// Offset by approximately one texel
-						S32 cur_discard = mTexture->getDiscardLevel();
-						S32 max_size = llmax( mTexture->getWidth(), mTexture->getHeight() );
-						max_size <<= cur_discard;
-						const F32 ARTIFICIAL_OFFSET = 2.f;
-						offset_multiple = ARTIFICIAL_OFFSET / (F32)max_size;
-					}
-					else
-					{
-						offset_multiple = 1.f/256;
-					}
-					break;
-
-				  default:  // Standard bumpmap textures.  Assumed to be 256x256
-					offset_multiple = 1.f / 256;
-					break;
-				}
+			tep->getScale( &s_scale, &t_scale );
+		}
+		LLVector3   sun_ray  = gSky.getSunDirection();
+		LLVector3   moon_ray = gSky.getMoonDirection();
+		LLVector3& primary_light_ray = (sun_ray.mV[VZ] > 0) ? sun_ray : moon_ray;
+		bump_s_primary_light_ray = offset_multiple * s_scale * primary_light_ray;
+		bump_t_primary_light_ray = offset_multiple * t_scale * primary_light_ray;
+	}
+		
+	U8 texgen = getTextureEntry()->getTexGen();
 
-				F32 s_scale = 1.f;
-				F32 t_scale = 1.f;
-				if( tep )
-				{
-					tep->getScale( &s_scale, &t_scale );
-				}
-				LLVector3   sun_ray  = gSky.getSunDirection();
-				LLVector3   moon_ray = gSky.getMoonDirection();
-				LLVector3& primary_light_ray = (sun_ray.mV[VZ] > 0) ? sun_ray : moon_ray;
-				bump_s_primary_light_ray = offset_multiple * s_scale * primary_light_ray;
-				bump_t_primary_light_ray = offset_multiple * t_scale * primary_light_ray;
-			}
-			
-			for (S32 i = 0; i < num_vertices; i++)
+	for (S32 i = 0; i < num_vertices; i++)
+	{
+		if (rebuild_tcoord)
+		{
+			LLVector2 tc = vf.mVertices[i].mTexCoord;
+		
+			if (texgen != LLTextureEntry::TEX_GEN_DEFAULT)
 			{
-				LLVector2 tc = vf.mVertices[i].mTexCoord;
+				LLVector3 vec = vf.mVertices[i].mPosition; 
+			
+				vec.scaleVec(scale);
 
-				U8 texgen = getTextureEntry()->getTexGen();
-				if (texgen != LLTextureEntry::TEX_GEN_DEFAULT)
+				switch (texgen)
 				{
+					case LLTextureEntry::TEX_GEN_PLANAR:
+						planarProjection(tc, vf.mVertices[i], vf.mCenter, vec);
+						break;
+					case LLTextureEntry::TEX_GEN_SPHERICAL:
+						sphericalProjection(tc, vf.mVertices[i], vf.mCenter, vec);
+						break;
+					case LLTextureEntry::TEX_GEN_CYLINDRICAL:
+						cylindricalProjection(tc, vf.mVertices[i], vf.mCenter, vec);
+						break;
+					default:
+						break;
+				}		
+			}
 
-					LLVector3 vec = vf.mVertices[i].mPosition; //-vf.mCenter;
-					
-					if (global_volume)
-					{	
-						vec -= render_pos;
-					}
-					else
-					{
-						vec.scaleVec(mVObjp->getScale());
-					}
-
-					switch (texgen)
-					{
-						case LLTextureEntry::TEX_GEN_PLANAR:
-							planarProjection(tc, vf.mVertices[i], vf.mCenter, vec);
-							break;
-						case LLTextureEntry::TEX_GEN_SPHERICAL:
-							sphericalProjection(tc, vf.mVertices[i], vf.mCenter, vec);
-							break;
-						case LLTextureEntry::TEX_GEN_CYLINDRICAL:
-							cylindricalProjection(tc, vf.mVertices[i], vf.mCenter, vec);
-							break;
-						default:
-							break;
-					}		
-				}
-				xform(tc, cos_ang, sin_ang, os, ot, ms, mt);
-				*tex_coords++ = tc;
-				if (grab_first_tcoord)
-				{
-					grab_first_tcoord = FALSE;
-					tmin = tmax = tc;
-				}
-				else
-				{
-					for (U32 j = 0; j < 2; j++)
-					{
-						if (tmin.mV[j] > tc.mV[j])
-						{
-							tmin.mV[j] = tc.mV[j];
-						}
-						else if (tmax.mV[j] < tc.mV[j])
-						{
-							tmax.mV[j] = tc.mV[j];
-						}
-					}
-				}
-				if (bump)
-				{
-					LLVector3 tangent = vf.mVertices[i].mBinormal % vf.mVertices[i].mNormal;
-					LLMatrix3 tangent_to_object;
-					tangent_to_object.setRows(tangent, vf.mVertices[i].mBinormal, vf.mVertices[i].mNormal);
-					LLVector3 binormal = binormal_dir * tangent_to_object;
+			xform(tc, cos_ang, sin_ang, os, ot, ms, mt);
+			*tex_coords++ = tc;
+		
+			if (bump_code)
+			{
+				LLVector3 tangent = vf.mVertices[i].mBinormal % vf.mVertices[i].mNormal;
+				LLMatrix3 tangent_to_object;
+				tangent_to_object.setRows(tangent, vf.mVertices[i].mBinormal, vf.mVertices[i].mNormal);
+				LLVector3 binormal = binormal_dir * tangent_to_object;
 
-					if (!global_volume)
-					{
-						binormal = binormal * mat_normal;
-					}
-					binormal.normVec();
-					tangent.normVec();
-					
- 					tc += LLVector2( bump_s_primary_light_ray * tangent, bump_t_primary_light_ray * binormal );
-					*tex_coords2++ = tc;
+				binormal = binormal * mat_normal;
+				binormal.normVec();
 
-					*binormals++ = binormal;
-				}
+				tc += LLVector2( bump_s_primary_light_ray * tangent, bump_t_primary_light_ray * binormal );
+				*tex_coords2++ = tc;
+			}	
+		}
+		else if (moved)
+		{
+			*tex_coords++ = *old_texcoords++;
+			if (bump_code)
+			{
+				*tex_coords2++ = *old_texcoords2++;
 			}
 		}
+			
+		if (rebuild_pos)
+		{
+			*vertices++ = vf.mVertices[i].mPosition * mat_vert;
 
-		index_offset += num_vertices;
+			LLVector3 normal = vf.mVertices[i].mNormal * mat_normal;
+			normal.normVec();
+			
+			*normals++ = normal;
+		}
+		else if (moved)
+		{
+			*normals++ = *old_normals++;
+			*vertices++ = *old_verts++;
+		}
 
-		center_sum += vf.mCenter * mat_vert;
+		if (rebuild_color)
+		{
+			*colors++ = color;		
+		}
+		else if (moved)
+		{
+			*colors++ = *old_colors++;
+		}
 	}
-	
-	center_sum /= (F32)(fend-fstart+1);
-	
-	if (is_static)
+
+	if (!rebuild_pos && !moved)
 	{
-		mCenterAgent = center_sum;
-		mCenterLocal = mCenterAgent - mDrawablep->getPositionAgent();
+		vertices += num_vertices;
 	}
-	else
+
+	if (!rebuild_tcoord && !moved)
 	{
-		mCenterLocal = center_sum;
-		updateCenterAgent();
+		tex_coords2 += num_vertices;
+		tex_coords += num_vertices;
 	}
-	
-	if (!grab_first_vert && mDrawablep->isState(LLDrawable::REBUILD_VOLUME))
+	else if (!bump_code)
 	{
-		mExtents[0] = min;
-		mExtents[1] = max;
+		tex_coords2 += num_vertices;
 	}
-	
-	if (!grab_first_tcoord && mDrawablep->isState(LLDrawable::REBUILD_TCOORD))
+
+	if (!rebuild_color && !moved)
 	{
-		mTexExtents[0] = tmin;
-		mTexExtents[1] = tmax;
+		colors += num_vertices;
 	}
-	
+
+	if (rebuild_tcoord)
+	{
+		mTexExtents[0].setVec(0,0);
+		mTexExtents[1].setVec(1,1);
+		xform(mTexExtents[0], cos_ang, sin_ang, os, ot, ms, mt);
+		xform(mTexExtents[1], cos_ang, sin_ang, os, ot, ms, mt);		
+	}
+
+	index_offset += num_vertices;
+
+	mLastVertexBuffer = mVertexBuffer;
+	mLastGeomCount = mGeomCount;
+	mLastGeomIndex = mGeomIndex;
+	mLastIndicesCount = mIndicesCount;
+	mLastIndicesIndex = mIndicesIndex;
+
 	return TRUE;
 }
 
+#if 0
 BOOL LLFace::genLighting(const LLVolume* volume, const LLDrawable* drawablep, S32 fstart, S32 fend, 
 						 const LLMatrix4& mat_vert, const LLMatrix3& mat_normal, BOOL do_lighting)
 {
@@ -1643,21 +1280,20 @@ BOOL LLFace::genShadows(const LLVolume* volume, const LLDrawable* drawablep, S32
 	}
 	return TRUE;
 }
+#endif
 
 BOOL LLFace::verify(const U32* indices_array) const
 {
 	BOOL ok = TRUE;
 	// First, check whether the face data fits within the pool's range.
-	if ((mGeomIndex < 0) || (mGeomIndex + mGeomCount) > (S32)getPool()->getVertexCount())
+	if ((mGeomIndex < 0) || (mGeomIndex + mGeomCount) > mVertexBuffer->getNumVerts())
 	{
 		ok = FALSE;
 		llinfos << "Face not within pool range!" << llendl;
 	}
 
 	S32 indices_count = (S32)getIndicesCount();
-	S32 geom_start = getGeomStart();
-	S32 geom_count = mGeomCount;
-
+	
 	if (!indices_count)
 	{
 		return TRUE;
@@ -1669,6 +1305,10 @@ BOOL LLFace::verify(const U32* indices_array) const
 		llinfos << "Face has bogus indices count" << llendl;
 	}
 	
+#if 0
+	S32 geom_start = getGeomStart();
+	S32 geom_count = mGeomCount;
+
 	const U32 *indicesp = indices_array ? indices_array + mIndicesIndex : getRawIndices();
 
 	for (S32 i = 0; i < indices_count; i++)
@@ -1687,6 +1327,7 @@ BOOL LLFace::verify(const U32* indices_array) const
 			ok = FALSE;
 		}
 	}
+#endif
 
 	if (!ok)
 	{
@@ -1737,7 +1378,7 @@ const LLColor4& LLFace::getRenderColor() const
 	
 void LLFace::renderSetColor() const
 {
-	if (!LLDrawPool::LLOverrideFaceColor::sOverrideFaceColor)
+	if (!LLFacePool::LLOverrideFaceColor::sOverrideFaceColor)
 	{
 		const LLColor4* color = &(getRenderColor());
 		
@@ -1754,61 +1395,21 @@ void LLFace::renderSetColor() const
 
 S32 LLFace::pushVertices(const U32* index_array) const
 {
-	U32 indices_count = mIndicesCount;
-	S32 ret = 0;
-#if ENABLE_FACE_LINKING
-	LLFace* next = mNextFace;
-#endif
-	
-	if (mGeomCount < gGLManager.mGLMaxVertexRange && (S32) indices_count < gGLManager.mGLMaxIndexRange)
-	{
-		LLFace* current = (LLFace*) this;
-		S32 geom_count = mGeomCount;
-#if ENABLE_FACE_LINKING
-		while (current)
-		{
-			//chop up batch into implementation recommended sizes
-			while (next &&
-				   (current == next ||
-					((S32) (indices_count + next->mIndicesCount) < gGLManager.mGLMaxIndexRange &&
-					 geom_count + next->mGeomCount < gGLManager.mGLMaxVertexRange)))
-			{
-				indices_count += next->mIndicesCount;
-				geom_count += next->mGeomCount;
-				next = next->mNextFace;
-			}
-#endif
-			if (indices_count)
-			{
-				glDrawRangeElements(mPrimType, current->mGeomIndex, current->mGeomIndex + geom_count, indices_count, 
-										GL_UNSIGNED_INT, index_array + current->mIndicesIndex); 
-			}
-			ret += (S32) indices_count;
-			indices_count = 0;
-			geom_count = 0;
-#if ENABLE_FACE_LINKING
-			current = next;
-		}
-#endif
-	}
-	else
+	if (mIndicesCount)
 	{
-#if ENABLE_FACE_LINKING
-		while (next)
+		if (mGeomCount <= gGLManager.mGLMaxVertexRange && 
+			mIndicesCount <= (U32) gGLManager.mGLMaxIndexRange)
 		{
-			indices_count += next->mIndicesCount;
-			next = next->mNextFace;
+			glDrawRangeElements(GL_TRIANGLES, mGeomIndex, mGeomIndex + mGeomCount-1, mIndicesCount, 
+									GL_UNSIGNED_INT, index_array + mIndicesIndex); 
 		}
-#endif
-		if (indices_count)
+		else
 		{
-			glDrawElements(mPrimType, indices_count, GL_UNSIGNED_INT, index_array + mIndicesIndex); 
+			glDrawElements(GL_TRIANGLES, mIndicesCount, GL_UNSIGNED_INT, index_array+mIndicesIndex);
 		}
-		ret += (S32) indices_count;
 	}
 	
-	return ret;
-
+	return mIndicesCount;
 }
 
 const LLMatrix4& LLFace::getRenderMatrix() const
@@ -1835,19 +1436,25 @@ S32 LLFace::renderElements(const U32 *index_array) const
 	return ret;
 }
 
-S32 LLFace::renderIndexed(const U32 *index_array) const
+S32 LLFace::renderIndexed()
 {
-	if (mSkipRender)
+	if(mGeomIndex < 0 || mDrawablep.isNull() || mDrawPoolp == NULL)
 	{
 		return 0;
 	}
+	
+	return renderIndexed(mDrawPoolp->getVertexDataMask());
+}
 
-	if(mGeomIndex < 0 || mDrawablep.isNull())
+S32 LLFace::renderIndexed(U32 mask)
+{
+	if (mVertexBuffer.isNull())
 	{
 		return 0;
 	}
-	
-	renderSetColor();
+
+	mVertexBuffer->setBuffer(mask);
+	U32* index_array = (U32*) mVertexBuffer->getIndicesPointer();
 	return renderElements(index_array);
 }
 
@@ -1860,26 +1467,13 @@ S32 LLFace::getVertices(LLStrider<LLVector3> &vertices)
 	{
 		return -1;
 	}
-	if (isState(BACKLIST))
-	{
-		if (!mBackupMem)
-		{
-			printDebugInfo();
-			llerrs << "No backup memory for face" << llendl;
-		}
-		vertices = (LLVector3*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_VERTICES]);
-		vertices.setStride( mDrawPoolp->getStride());
-		return 0;
-	}
-	else
+	
+	if (mGeomIndex >= 0) // flexible objects may not have geometry
 	{
-		if (mGeomIndex >= 0) // flexible objects may not have geometry
-		{
-			mDrawPoolp->getVertexStrider(vertices, mGeomIndex);
-			mDrawPoolp->setDirty();
-		}
-		return mGeomIndex;
+		mVertexBuffer->getVertexStrider(vertices, mGeomIndex);
+		
 	}
+	return mGeomIndex;
 }
 
 S32 LLFace::getColors(LLStrider<LLColor4U> &colors)
@@ -1888,42 +1482,17 @@ S32 LLFace::getColors(LLStrider<LLColor4U> &colors)
 	{
 		return -1;
 	}
-	if (isState(BACKLIST))
-	{
-		llassert(mBackupMem);
-		colors   = (LLColor4U*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_COLORS]);
-		colors.setStride( mDrawPoolp->getStride());
-		return 0;
-	}
-	else
-	{
-		llassert(mGeomIndex >= 0);
-		mDrawPoolp->getColorStrider(colors, mGeomIndex);
-		return mGeomIndex;
-	}
-}
-
-S32	LLFace::getIndices(U32*  &indicesp)
-{
-	if (isState(BACKLIST))
-	{
-		indicesp = (U32*)mBackupMem;
-		return 0;
-	}
-	else
-	{
-		indicesp = mDrawPoolp->getIndices(mIndicesIndex);
-		llassert(mGeomIndex >= 0 && indicesp[0] != indicesp[1]);
-		return mGeomIndex;
-	}
+	
+	llassert(mGeomIndex >= 0);
+	mVertexBuffer->getColorStrider(colors, mGeomIndex);
+	return mGeomIndex;
 }
 
-void LLFace::link(LLFace* facep)
+S32	LLFace::getIndices(LLStrider<U32> &indicesp)
 {
-#if ENABLE_FACE_LINKING
-	mNextFace = facep;
-	facep->mSkipRender = TRUE;
-#endif
+	mVertexBuffer->getIndexStrider(indicesp, mIndicesIndex);
+	llassert(mGeomIndex >= 0 && indicesp[0] != indicesp[1]);
+	return mIndicesIndex;
 }
 
 LLVector3 LLFace::getPositionAgent() const
diff --git a/indra/newview/llface.h b/indra/newview/llface.h
index 0d017d6efe052156ea51728eeb26b933ca58b0a1..18db6453059e094ef954e8d9ff0c7935f6953f1b 100644
--- a/indra/newview/llface.h
+++ b/indra/newview/llface.h
@@ -19,18 +19,19 @@
 #include "llquaternion.h"
 #include "xform.h"
 #include "lldarrayptr.h"
+#include "llvertexbuffer.h"
+#include "llviewerimage.h"
 #include "llpagemem.h"
 #include "llstat.h"
 #include "lldrawable.h"
 
-#define ENABLE_FACE_LINKING 1 // Causes problems with snapshot rendering
-
-class LLDrawPool;
+class LLFacePool;
 class LLVolume;
 class LLViewerImage;
 class LLTextureEntry;
 class LLVertexProgram;
 class LLViewerImage;
+class LLGeometryManager;
 
 class LLFace
 {
@@ -38,26 +39,12 @@ public:
 
 	enum EMasks
 	{
-		SHARED_GEOM	 = 0x0001,
-		LIGHT		 = 0x0002,
-		REBUILD		 = 0x0004,	
-		GLOBAL		 = 0x0008,
-		VISIBLE		 = 0x0010,
-		BACKLIST	 = 0x0020,
-		INTERP		 = 0x0040,
-		FULLBRIGHT	 = 0x0080,
-		HUD_RENDER	 = 0x0100,
-		USE_FACE_COLOR	 = 0x0200,
-
-		POINT_SPRITE = 0x10000,
-		BOARD_SPRITE = 0x20000,
-		FIXED_SPRITE = 0x40000,
-		DEPTH_SPRITE = 0x80000
-	};
-
-	enum EDirty
-	{
-		DIRTY = -2
+		LIGHT			= 0x0001,
+		GLOBAL			= 0x0002,
+		FULLBRIGHT		= 0x0004,
+		HUD_RENDER		= 0x0008,
+		USE_FACE_COLOR	= 0x0010,
+		TEXTURE_ANIM	= 0x0020, 
 	};
 
 	static void initClass();
@@ -74,54 +61,64 @@ public:
 	const S32		getGeomIndex()		const	{ return mGeomIndex; }		// index into draw pool
 	const U32		getGeomStart()		const	{ return mGeomIndex; }		// index into draw pool
 	LLViewerImage*	getTexture()		const	{ return mTexture; }
+	void			setTexture(LLViewerImage* tex) { mTexture = tex; }
 	LLXformMatrix*	getXform()			const	{ return mXform; }
 	BOOL			hasGeometry()		const	{ return mGeomCount > 0; }
 	LLVector3		getPositionAgent()	const;
-	void			setPrimType(U32 primType)	{ mPrimType = primType; }
-	const U32		getPrimType()		const	{ return mPrimType; }
-
+	
 	U32				getState()			const	{ return mState; }
 	void			setState(U32 state)			{ mState |= state; }
 	void			clearState(U32 state)		{ mState &= ~state; }
-	BOOL			isState(U32 state)	const	{ return ((mState & state) != 0); }
-
+	BOOL			isState(U32 state)	const	{ return ((mState & state) != 0) ? TRUE : FALSE; }
+	void			setVirtualSize(F32 size) { mVSize = size; }
+	void			setPixelArea(F32 area)	{ mPixelArea = area; }
+	F32				getVirtualSize() const { return mVSize; }
+	F32				getPixelArea() const { return mPixelArea; }
 	void			bindTexture(S32 stage = 0)		const	{ LLViewerImage::bindTexture(mTexture, stage); }
 
 	void			enableLights() const;
 	void			renderSetColor() const;
 	S32				renderElements(const U32 *index_array) const;
-	S32				renderIndexed (const U32 *index_array) const;
+	S32				renderIndexed ();
+	S32				renderIndexed (U32 mask);
 	S32				pushVertices(const U32* index_array) const;
 	
 	void			setWorldMatrix(const LLMatrix4& mat);
 	const LLTextureEntry* getTextureEntry()	const { return mVObjp->getTE(mTEOffset); }
 
-	LLDrawPool*		getPool()			const	{ return mDrawPoolp; }
-	S32				getStride()			const	{ return mDrawPoolp->getStride(); }
-	const U32*		getRawIndices()		const	{ return &mDrawPoolp->mIndices[mIndicesIndex]; }
+	LLFacePool*		getPool()			const	{ return mDrawPoolp; }
+	U32				getPoolType()		const	{ return mPoolType; }
 	LLDrawable*		getDrawable()		const	{ return mDrawablep; }
 	LLViewerObject*	getViewerObject()	const	{ return mVObjp; }
-
-	void			clearDirty() { mGeneration = mDrawPoolp->mGeneration; };
-
-	S32				backup();
-	void			restore();
+	S32				getLOD()			const	{ return mVObjp.notNull() ? mVObjp->getLOD() : 0; }
+	LLVertexBuffer* getVertexBuffer()	const	{ return mVertexBuffer; }
+	void			setPoolType(U32 type)		{ mPoolType = type; }
+	S32				getTEOffset()				{ return mTEOffset; }
 
 	void			setViewerObject(LLViewerObject* object);
-	void			setPool(LLDrawPool *pool, LLViewerImage *texturep);
+	void			setPool(LLFacePool *pool, LLViewerImage *texturep);
+	
 	void			setDrawable(LLDrawable *drawable);
 	void			setTEOffset(const S32 te_offset);
-	S32				getTEOffset() { return mTEOffset; }
+	
 
 	void			setFaceColor(const LLColor4& color); // override material color
 	void			unsetFaceColor(); // switch back to material color
 	const LLColor4&	getFaceColor() const { return mFaceColor; } 
 	const LLColor4& getRenderColor() const;
 	
-	void unReserve();		// Set Removed from pool
-	
-	BOOL reserveIfNeeded(); // Reserves data if dirty.
-	
+	//for volumes
+	S32 getGeometryVolume(const LLVolume& volume,
+						S32 f,
+						LLStrider<LLVector3>& vertices, 
+						LLStrider<LLVector3>& normals,
+						LLStrider<LLVector2>& texcoords,
+						LLStrider<LLVector2>& texcoords2,
+						LLStrider<LLColor4U>& colors,
+						LLStrider<U32>& indices,
+						const LLMatrix4& mat_vert, const LLMatrix3& mat_normal,
+						U32& index_offset);
+
 	// For avatar
 	S32				 getGeometryAvatar(
 									LLStrider<LLVector3> &vertices,
@@ -137,69 +134,51 @@ public:
 									   LLStrider<LLColor4U> &colors,
 									   LLStrider<LLVector2> &texCoords0,
 									   LLStrider<LLVector2> &texCoords1,
-									   U32* &indices);
+									   LLStrider<U32> &indices);
 
 	// For volumes, etc.
 	S32				getGeometry(LLStrider<LLVector3> &vertices,  
 								LLStrider<LLVector3> &normals,
 								LLStrider<LLVector2> &texCoords, 
-								U32*  &indices);
+								LLStrider<U32>  &indices);
 
 	S32				getGeometryColors(LLStrider<LLVector3> &vertices,  
 									  LLStrider<LLVector3> &normals,
 									  LLStrider<LLVector2> &texCoords, 
 									  LLStrider<LLColor4U> &colors, 
-									  U32*  &indices);
+									  LLStrider<U32>  &indices);
 	
-	S32				getGeometryMultiTexture(LLStrider<LLVector3> &vertices,  
-											LLStrider<LLVector3> &normals,
-											LLStrider<LLVector3> &binormals,
-											LLStrider<LLVector2> &texCoords0, 
-											LLStrider<LLVector2> &texCoords1, 
-											U32*  &indices);
-
-
-	S32			getVertices	  (LLStrider<LLVector3> &vertices);
-	S32			getColors	  (LLStrider<LLColor4U> &colors);
-	S32			getIndices	  (U32*	 &indices);
+	S32 getVertices(LLStrider<LLVector3> &vertices);
+	S32 getColors(LLStrider<LLColor4U> &colors);
+	S32 getIndices(LLStrider<U32> &indices);
 
 	void		setSize(const S32 numVertices, const S32 num_indices = 0);
-	BOOL		getDirty() const { return (mGeneration != mDrawPoolp->mGeneration); }
-
-	BOOL		genVolumeTriangles(const LLVolume &volume, S32 f,
-								   const LLMatrix4& mat, const LLMatrix3& inv_trans_mat, BOOL global_volume = FALSE);
-	BOOL		genVolumeTriangles(const LLVolume &volume, S32 fstart, S32 fend,
+	
+	BOOL		genVolumeBBoxes(const LLVolume &volume, S32 f,
 								   const LLMatrix4& mat, const LLMatrix3& inv_trans_mat, BOOL global_volume = FALSE);
-	BOOL 		genLighting(const LLVolume* volume, const LLDrawable* drawablep, S32 fstart, S32 fend, 
-							const LLMatrix4& mat_vert, const LLMatrix3& mat_normal, BOOL do_lighting);
-
-	BOOL 		genShadows(const LLVolume* volume, const LLDrawable* drawablep, S32 fstart, S32 fend, 
-							const LLMatrix4& mat_vert, const LLMatrix3& mat_normal, BOOL use_shadow_factor);
-
+	
 	void		init(LLDrawable* drawablep, LLViewerObject* objp);
 	void		destroy();
 	void		update();
 
 	void		updateCenterAgent(); // Update center when xform has changed.
-	void renderSelectedUV(const S32 offset = 0, const S32 count = 0);
+	void		renderSelectedUV(const S32 offset = 0, const S32 count = 0);
 
-	void		renderForSelect() const;
+	void		renderForSelect(U32 data_mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD);
 	void		renderSelected(LLImageGL *image, const LLColor4 &color, const S32 offset = 0, const S32 count = 0);
 
 	F32			getKey()					const	{ return mDistance; }
 
-	S32			getGeneration() 			const	{ return mGeneration; }
 	S32			getReferenceIndex() 		const	{ return mReferenceIndex; }
 	void		setReferenceIndex(const S32 index)	{ mReferenceIndex = index; }
 
 	BOOL		verify(const U32* indices_array = NULL) const;
 	void		printDebugInfo() const;
 
-	void		link(LLFace* facep);
+	void		setGeomIndex(S32 idx) { mGeomIndex = idx; }
+	void		setIndicesIndex(S32 idx) { mIndicesIndex = idx; }
 	
 protected:
-	S32			allocBackupMem();	// Allocate backup memory based on the draw pool information.
-	void		setDirty();
 
 public:
 	LLVector3		mCenterLocal;
@@ -208,30 +187,39 @@ public:
 	LLVector2		mTexExtents[2];
 	F32				mDistance;
 	F32				mAlphaFade;
-	LLFace*			mNextFace;
-	BOOL			mSkipRender;
-	
+	LLPointer<LLVertexBuffer> mVertexBuffer;
+	LLPointer<LLVertexBuffer> mLastVertexBuffer;
+	F32			mLastUpdateTime;
+
 protected:
-	S32			mGeneration;
+	friend class LLGeometryManager;
+	friend class LLVolumeGeometryManager;
+
 	U32			mState;
-	LLDrawPool*	mDrawPoolp;
-	S32			mGeomIndex;			// index into draw pool
+	LLFacePool*	mDrawPoolp;
+	U32			mPoolType;
 	LLColor4	mFaceColor;			// overrides material color if state |= USE_FACE_COLOR
 	
-	U32			mPrimType;
 	S32			mGeomCount;			// vertex count for this face
+	S32			mGeomIndex;			// index into draw pool
 	U32			mIndicesCount;
 	S32			mIndicesIndex;		// index into draw pool for indices (yeah, I know!)
-	LLXformMatrix* mXform;
-	LLPointer<LLViewerImage> mTexture;
 
-	U8				*mBackupMem;
+	//previous rebuild's geometry info
+	S32			mLastGeomCount;
+	S32			mLastGeomIndex;
+	U32			mLastIndicesCount;
+	S32			mLastIndicesIndex;
 
+	LLXformMatrix* mXform;
+	LLPointer<LLViewerImage> mTexture;
 	LLPointer<LLDrawable> mDrawablep;
 	LLPointer<LLViewerObject> mVObjp;
 	S32			mTEOffset;
 
 	S32			mReferenceIndex;
+	F32			mVSize;
+	F32			mPixelArea;
 	
 protected:
 	static BOOL	sSafeRenderSelect;
@@ -245,6 +233,43 @@ public:
 		}
 	};
 	
+	struct CompareTexture
+	{
+		bool operator()(const LLFace* const& lhs, const LLFace* const& rhs)
+		{
+			return lhs->getTexture() < rhs->getTexture();
+		}
+	};
+
+	struct CompareTextureAndGeomCount
+	{
+		bool operator()(const LLFace* const& lhs, const LLFace* const& rhs)
+		{
+			return lhs->getTexture() == rhs->getTexture() ? 
+				lhs->getGeomCount() < rhs->getGeomCount() :
+				lhs->getTexture() > rhs->getTexture();
+		}
+	};
+
+	struct CompareTextureAndLOD
+	{
+		bool operator()(const LLFace* const& lhs, const LLFace* const& rhs)
+		{
+			return lhs->getTexture() == rhs->getTexture() ? 
+				lhs->getLOD() < rhs->getLOD() :
+				lhs->getTexture() < rhs->getTexture();
+		}
+	};
+
+	struct CompareTextureAndTime
+	{
+		bool operator()(const LLFace* const& lhs, const LLFace* const& rhs)
+		{
+			return lhs->getTexture() == rhs->getTexture() ? 
+				lhs->mLastUpdateTime < rhs->mLastUpdateTime :
+				lhs->getTexture() < rhs->getTexture();
+		}
+	};
 };
 
 #endif // LL_LLFACE_H
diff --git a/indra/newview/llfasttimerview.cpp b/indra/newview/llfasttimerview.cpp
index 57f786053a4dfe92326835da7b99e164645564ba..09610b01a3e9311df1e780f7d00b00b5689ac490 100644
--- a/indra/newview/llfasttimerview.cpp
+++ b/indra/newview/llfasttimerview.cpp
@@ -67,31 +67,41 @@ static struct ft_display_info ft_display_table[] =
 	{ LLFastTimer::FTM_RESET_DRAWORDER,		"  ResetDrawOrder",		&LLColor4::pink1, 0 },
 	{ LLFastTimer::FTM_WORLD_UPDATE,		"  World Update",		&LLColor4::blue1, 1 },
 	{ LLFastTimer::FTM_UPDATE_MOVE,			"   Move Objects",		&LLColor4::pink2, 0 },
-	{ LLFastTimer::FTM_OCTREE_BALANCE,		"    Octree Balance",	&LLColor4::red3, 0 },
-	{ LLFastTimer::FTM_CULL,				"   Object Cull",	&LLColor4::blue2, 0 },
-	{ LLFastTimer::FTM_CULL_REBOUND,		"    Rebound",		&LLColor4::blue3, 0 },
+	{ LLFastTimer::FTM_OCTREE_BALANCE,		"    Octree Balance", &LLColor4::red3, 0 },
+	{ LLFastTimer::FTM_TEMP1,				"   Blur",			&LLColor4::red1, 0 },
+	{ LLFastTimer::FTM_CULL,				"   Object Cull",	&LLColor4::blue2, 1 },
+    { LLFastTimer::FTM_CULL_REBOUND,		"    Rebound",		&LLColor4::blue3, 0 },
+	{ LLFastTimer::FTM_FRUSTUM_CULL,		"    Frustum Cull",	&LLColor4::blue4, 0 },
+	{ LLFastTimer::FTM_OCCLUSION,			"    Object Occlude", &LLColor4::pink1, 0 },
+	{ LLFastTimer::FTM_OCCLUSION_READBACK,	"    Occlusion Read", &LLColor4::red2, 0 },
 	{ LLFastTimer::FTM_HUD_EFFECTS,			"   HUD Effects",	&LLColor4::orange1, 0 },
 	{ LLFastTimer::FTM_HUD_UPDATE,			"   HUD Update",	&LLColor4::orange2, 0 },
-	{ LLFastTimer::FTM_OCCLUSION,			"   Object Occlude",&LLColor4::pink1, 0 },
-	{ LLFastTimer::FTM_OCCLUSION_READBACK,	"   Occlusion Read",&LLColor4::red2, 0 },
 	{ LLFastTimer::FTM_GEO_UPDATE,			"   Geo Update",	&LLColor4::blue3, 0 },
 	{ LLFastTimer::FTM_UPDATE_PRIMITIVES,	"    Volumes",		&LLColor4::blue4, 0 },
 	{ LLFastTimer::FTM_GEN_VOLUME,			"     Gen Volume",	&LLColor4::yellow3, 0 },
 	{ LLFastTimer::FTM_GEN_FLEX,			"     Flexible",	&LLColor4::yellow4, 0 },
 	{ LLFastTimer::FTM_GEN_TRIANGLES,		"     Triangles",	&LLColor4::yellow5, 0 },
+	{ LLFastTimer::FTM_UPDATE_AVATAR,		"    Avatar",		&LLColor4::yellow1, 0 },
+	{ LLFastTimer::FTM_UPDATE_TREE,			"    Tree",			&LLColor4::yellow2, 0 },
+	{ LLFastTimer::FTM_UPDATE_TERRAIN,		"    Terrain",		&LLColor4::yellow6, 0 },
+	{ LLFastTimer::FTM_UPDATE_CLOUDS,		"    Clouds",		&LLColor4::yellow7, 0 },
+	{ LLFastTimer::FTM_UPDATE_GRASS,		"    Grass",		&LLColor4::yellow8, 0 },
+	{ LLFastTimer::FTM_UPDATE_WATER,		"    Water",		&LLColor4::yellow9, 0 },
 	{ LLFastTimer::FTM_GEO_LIGHT,			"    Lighting",		&LLColor4::yellow1, 0 },
 	{ LLFastTimer::FTM_GEO_SHADOW,			"    Shadow",		&LLColor4::black, 0 },
 	{ LLFastTimer::FTM_UPDATE_PARTICLES,	"    Particles",	&LLColor4::blue5, 0 },
+	{ LLFastTimer::FTM_SIMULATE_PARTICLES,	"    Particle Sim",	&LLColor4::blue4, 0 },
 	{ LLFastTimer::FTM_GEO_RESERVE,			"    Reserve",		&LLColor4::blue6, 0 },
 	{ LLFastTimer::FTM_UPDATE_LIGHTS,		"    Lights",		&LLColor4::yellow2, 0 },
 	{ LLFastTimer::FTM_UPDATE_SKY,			"  Sky Update",		&LLColor4::cyan1, 0 },
-	{ LLFastTimer::FTM_OBJECTLIST_UPDATE,	"  Object Update",	&LLColor4::purple1, 1 },
+	{ LLFastTimer::FTM_OBJECTLIST_UPDATE,	"  Object Update",	&LLColor4::purple1, 0 },
 	{ LLFastTimer::FTM_AVATAR_UPDATE,		"   Avatars",		&LLColor4::purple2, 0 },
 	{ LLFastTimer::FTM_JOINT_UPDATE,		"    Joints",		&LLColor4::purple3, 0 },
 	{ LLFastTimer::FTM_ATTACHMENT_UPDATE,	"    Attachments",	&LLColor4::purple4, 0 },
 	{ LLFastTimer::FTM_UPDATE_ANIMATION,	"    Animation",	&LLColor4::purple5, 0 },
 	{ LLFastTimer::FTM_FLEXIBLE_UPDATE,		"   Flex Update",	&LLColor4::pink2, 0 },
 	{ LLFastTimer::FTM_LOD_UPDATE,			"   LOD Update",	&LLColor4::magenta1, 0 },
+	{ LLFastTimer::FTM_TEMP5,				"    Check",	&LLColor4::red1, 1},
 	{ LLFastTimer::FTM_REGION_UPDATE,		"  Region Update",	&LLColor4::cyan2, 0 },
 	{ LLFastTimer::FTM_NETWORK,				"  Network",		&LLColor4::orange1, 1 },
 	{ LLFastTimer::FTM_IDLE_NETWORK,		"   Decode Msgs",	&LLColor4::orange2, 0 },
@@ -106,17 +116,35 @@ static struct ft_display_info ft_display_table[] =
 	{ LLFastTimer::FTM_IMAGE_UPDATE,		"  Image Update",	&LLColor4::yellow4, 1 },
 	{ LLFastTimer::FTM_IMAGE_CREATE,		"   Image CreateGL",&LLColor4::yellow5, 0 },
 	{ LLFastTimer::FTM_IMAGE_DECODE,		"   Image Decode",	&LLColor4::yellow6, 0 },
+	{ LLFastTimer::FTM_IMAGE_MARK_DIRTY,	"   Dirty Textures",&LLColor4::red1, 0 },
 	{ LLFastTimer::FTM_VFILE_WAIT,			"  VFile Wait",		&LLColor4::cyan6, 0 },
 //	{ LLFastTimer::FTM_IDLE_CB,				"  Callbacks",		&LLColor4::pink1, 0 },
-	{ LLFastTimer::FTM_RENDER,				" Render",			&green0, 0 },
+	{ LLFastTimer::FTM_RENDER,				" Render",			&green0, 1 },
 	{ LLFastTimer::FTM_REBUILD,				"  Rebuild",		&LLColor4::green1, 1 },
 	{ LLFastTimer::FTM_STATESORT,			"   State Sort",	&LLColor4::orange1, 1 },
+	{ LLFastTimer::FTM_STATESORT_DRAWABLE,	"    Drawable",		&LLColor4::orange2, 0 },
+	{ LLFastTimer::FTM_STATESORT_POSTSORT,	"    Post Sort",	&LLColor4::orange3, 0 },
+	{ LLFastTimer::FTM_REBUILD_OCCLUSION_VB,"     Occlusion",		&LLColor4::cyan5, 0 },
+	{ LLFastTimer::FTM_REBUILD_VBO,			"     VBO Rebuild",	&LLColor4::red4, 0 },
+	{ LLFastTimer::FTM_REBUILD_VOLUME_VB,	"      Volume",		&LLColor4::blue1, 0 },
+	{ LLFastTimer::FTM_REBUILD_NONE_VB,		"       Unknown",	&LLColor4::cyan5, 0 },
+	{ LLFastTimer::FTM_REBUILD_BRIDGE_VB,	"      Bridge",		&LLColor4::blue2, 0 },
+	{ LLFastTimer::FTM_REBUILD_HUD_VB,		"      HUD",			&LLColor4::blue3, 0 },
+	{ LLFastTimer::FTM_REBUILD_TERRAIN_VB,	"      Terrain",		&LLColor4::blue4, 0 },
+	{ LLFastTimer::FTM_REBUILD_WATER_VB,	"      Water",		&LLColor4::blue5, 0 },
+	{ LLFastTimer::FTM_REBUILD_TREE_VB,		"      Tree",		&LLColor4::cyan1, 0 },
+	{ LLFastTimer::FTM_REBUILD_PARTICLE_VB,	"      Particle",	&LLColor4::cyan2, 0 },
+	{ LLFastTimer::FTM_REBUILD_CLOUD_VB,	"      Cloud",		&LLColor4::cyan3, 0 },
+	{ LLFastTimer::FTM_REBUILD_GRASS_VB,	"      Grass",		&LLColor4::cyan4, 0 },
 	{ LLFastTimer::FTM_RENDER_GEOMETRY,		"  Geometry",		&LLColor4::green2, 1 },
-	{ LLFastTimer::FTM_POOLS,				"   Pools",			&LLColor4::green3, 0 },
-	{ LLFastTimer::FTM_POOLRENDER,			"    RenderPool",	&LLColor4::green4, 0 },
+	{ LLFastTimer::FTM_POOLS,				"   Pools",			&LLColor4::green3, 1 },
+	{ LLFastTimer::FTM_POOLRENDER,			"    RenderPool",	&LLColor4::green4, 1 },
 	{ LLFastTimer::FTM_RENDER_TERRAIN,		"     Terrain",		&LLColor4::green6, 0 },
 	{ LLFastTimer::FTM_RENDER_CHARACTERS,	"     Avatars",		&LLColor4::yellow1, 0 },
 	{ LLFastTimer::FTM_RENDER_SIMPLE,		"     Simple",		&LLColor4::yellow2, 0 },
+	{ LLFastTimer::FTM_RENDER_FULLBRIGHT,	"     Fullbright",	&LLColor4::yellow5, 0 },
+	{ LLFastTimer::FTM_RENDER_GRASS,		"     Grass",		&LLColor4::yellow6, 0 },
+	{ LLFastTimer::FTM_RENDER_INVISIBLE,	"     Invisible",	&LLColor4::red2, 0 },
 	{ LLFastTimer::FTM_RENDER_SHINY,		"     Shiny",		&LLColor4::yellow3, 0 },
 	{ LLFastTimer::FTM_RENDER_BUMP,			"     Bump",		&LLColor4::yellow4, 0 },
 	{ LLFastTimer::FTM_RENDER_TREES,		"     Trees",		&LLColor4::yellow8, 0 },
@@ -130,15 +158,16 @@ static struct ft_display_info ft_display_table[] =
 //	{ LLFastTimer::FTM_RENDER_FONTS,		"   Fonts",			&LLColor4::pink1, 0 },
 //	{ LLFastTimer::FTM_UPDATE_TEXTURES,		"  Textures",		&LLColor4::pink2, 0 },
 	{ LLFastTimer::FTM_SWAP,				"  Swap",			&LLColor4::pink1, 0 },
+	{ LLFastTimer::FTM_TEMP6,				"  Client Copy",	&LLColor4::red1, 1},
 	
-//	{ LLFastTimer::FTM_TEMP1,				"  Temp1",			&LLColor4::red1, 0 },
-// 	{ LLFastTimer::FTM_TEMP2,				"  Temp2",			&LLColor4::magenta1, 0 },
-// 	{ LLFastTimer::FTM_TEMP3,				"  Temp3",			&LLColor4::red2, 0 },
-// 	{ LLFastTimer::FTM_TEMP4,				"  Temp4",			&LLColor4::magenta2, 0 },
-//	{ LLFastTimer::FTM_TEMP5,				"  Temp5",			&LLColor4::red3, 0 },
-//	{ LLFastTimer::FTM_TEMP6,				"  Temp6",			&LLColor4::magenta3, 0 },
-//	{ LLFastTimer::FTM_TEMP7,				"  Temp7",			&LLColor4::red4, 0 },
-//	{ LLFastTimer::FTM_TEMP8,				"  Temp8",			&LLColor4::magenta4, 0 },
+//	{ LLFastTimer::FTM_TEMP1,				" Temp1",			&LLColor4::red1, 0 },
+// 	{ LLFastTimer::FTM_TEMP2,				" Temp2",			&LLColor4::magenta1, 0 },
+// 	{ LLFastTimer::FTM_TEMP3,				" Temp3",			&LLColor4::red2, 0 },
+// 	{ LLFastTimer::FTM_TEMP4,				" Temp4",			&LLColor4::magenta2, 0 },
+//	{ LLFastTimer::FTM_TEMP5,				" Temp5",			&LLColor4::red3, 0 },
+//	{ LLFastTimer::FTM_TEMP6,				" Temp6",			&LLColor4::magenta3, 0 },
+//	{ LLFastTimer::FTM_TEMP7,				" Temp7",			&LLColor4::red4, 0 },
+//	{ LLFastTimer::FTM_TEMP8,				" Temp8",			&LLColor4::magenta4, 0 },
 
 	{ LLFastTimer::FTM_OTHER,				" Other",			&red0 }
 };
@@ -148,7 +177,7 @@ static const int FTV_DISPLAY_NUM  = (sizeof(ft_display_table)/sizeof(ft_display_
 S32 ft_display_idx[FTV_DISPLAY_NUM]; // line of table entry for display purposes (for collapse)
 
 LLFastTimerView::LLFastTimerView(const std::string& name, const LLRect& rect)
-:	LLView(name, rect, TRUE)
+	:	LLFloater(name, rect, "Fast Timers")
 {
 	setVisible(FALSE);
 	mDisplayMode = 0;
@@ -891,7 +920,10 @@ void LLFastTimerView::draw()
 			F32 ms = (F32)((F64)max_ticks * iclock_freq);
 			
 			//display y-axis range
-			LLString tdesc = llformat("%4.2f ms", ms);
+			LLString tdesc = mDisplayCalls ? 
+							llformat("%d calls", max_ticks) :
+							llformat("%4.2f ms", ms);
+							
 			x = graph_rect.mRight - LLFontGL::sMonospace->getWidth(tdesc)-5;
 			y = graph_rect.mTop - ((S32)LLFontGL::sMonospace->getLineHeight());
  
@@ -957,6 +989,13 @@ void LLFastTimerView::draw()
 				for (U32 j = 0; j < LLFastTimer::FTM_HISTORY_NUM; j++)
 				{
 					U64 ticks = ticks_sum[j+1][idx];
+					if (mDisplayCalls)
+					{
+						S32 tidx = ft_display_table[idx].timer;
+						S32 hidx = (LLFastTimer::sLastFrameIndex + j) % LLFastTimer::FTM_HISTORY_NUM;
+						ticks = (S32)LLFastTimer::sCallHistory[hidx][tidx];
+					}
+					
 					if (alpha == 1.f)
 					{ //normalize to highlighted timer
 						cur_max = llmax(cur_max, ticks);
diff --git a/indra/newview/llfasttimerview.h b/indra/newview/llfasttimerview.h
index eb30f60f3d58ab72b6a765ead86ce788e4fa2263..be66a8481ac814b8af1eabd7594962e193489460 100644
--- a/indra/newview/llfasttimerview.h
+++ b/indra/newview/llfasttimerview.h
@@ -9,10 +9,10 @@
 #ifndef LL_LLFASTTIMERVIEW_H
 #define LL_LLFASTTIMERVIEW_H
 
-#include "llview.h"
+#include "llfloater.h"
 #include "llframetimer.h"
 
-class LLFastTimerView : public LLView
+class LLFastTimerView : public LLFloater
 {
 public:
 	LLFastTimerView(const std::string& name, const LLRect& rect);
diff --git a/indra/newview/llfeaturemanager.cpp b/indra/newview/llfeaturemanager.cpp
index bee2f879ac5e919314fab26e9b7bdf1ebf72a00d..e3b5a2bb685cb4b2879fc3254374ea91eeb4621d 100644
--- a/indra/newview/llfeaturemanager.cpp
+++ b/indra/newview/llfeaturemanager.cpp
@@ -137,7 +137,7 @@ BOOL LLFeatureList::maskList(LLFeatureList &mask)
 	}
 
 #if 0 && !LL_RELEASE_FOR_DOWNLOAD
-	llinfos << "After appling mask " << mask.mName << llendl;
+	llinfos << "After applying mask " << mask.mName << llendl;
 	dump();
 #endif
 	return TRUE;
@@ -276,7 +276,6 @@ BOOL LLFeatureManager::loadFeatureTables()
 		}
 	}
 	file.close();
-	//flp->dump();
 
 	return TRUE;
 }
@@ -361,10 +360,11 @@ void LLFeatureManager::loadGPUClass()
 			llinfos << "GPU is " << label << llendl;
 			mGPUString = label;
 			mGPUClass = (S32) strtol(cls, NULL, 10);	
+			file.close();
+			return;
 		}
 	}
 	file.close();
-	//flp->dump();
 
 	llwarns << "Couldn't match GPU to a class: " << gGLManager.getRawGLString() << llendl;
 }
@@ -382,11 +382,7 @@ void LLFeatureManager::initCPUFeatureMasks()
 	{
 		maskFeatures("RAM256MB");
 	}
-	else if (gSysMemory.getPhysicalMemory() <= 512*1024*1024)
-	{
-		//maskFeatures("RAM512MB");
-	}
-
+	
 	if (gSysCPU.getMhz() < 1100)
 	{
 		maskFeatures("CPUSlow");
@@ -449,7 +445,11 @@ void LLFeatureManager::initGraphicsFeatureMasks()
 	}
 	if (gGLManager.mIsIntel)
 	{
-		maskFeatures("Brookdale");
+		maskFeatures("Intel");
+	}
+	if (gGLManager.mGLVersion < 1.5f)
+	{
+		maskFeatures("OpenGLPre15");
 	}
 
 	if (gGLManager.mIsMobilityRadeon9000)
@@ -464,303 +464,6 @@ void LLFeatureManager::initGraphicsFeatureMasks()
 
 extern LLOSInfo gSysOS;
 
-
-BOOL bad_hardware_dialog(const LLString &info_str, const LLString &url)
-{
-	if (!gSavedSettings.getWarning("AboutBadPCI"))
-	{
-		return FALSE;
-	}
-
-	// XUI:translate
-	std::string msg = llformat(
-			"[SECOND_LIFE] has detected that there may be a problem with.\n"
-			"hardware or drivers on your computer.  Often resolving these\n"
-			"issues can result in enhanced stability and performance.\n"
-			" \n"
-			"%s\n"
-			" \n"
-			"Would you like to view a web page with more detailed\n"
-			"information on this problem?\n", info_str.c_str());
-
-	// Warn them that runnin without DirectX 9 will
-	// not allow us to tell them about driver issues
-	S32 button = OSMessageBox(msg.c_str(),
-							  "Warning",
-							  OSMB_YESNO);
-	if (OSBTN_YES== button)
-	{
-		llinfos << "User quitting after detecting bad drivers" << llendl;
-		spawn_web_browser(url.c_str());
-		return TRUE;
-	}
-	else
-	{
-		// Don't warn about bad PCI stuff again, they've clicked past it.
-		gSavedSettings.setWarning("AboutBadPCI", FALSE);
-	}
-	return FALSE;
-}
-
-BOOL LLFeatureManager::initPCIFeatureMasks()
-{
-#if LL_WINDOWS
-	BOOL exit_after_bad = FALSE;
-
-	BOOL is_2000 = FALSE;
-	BOOL is_xp = FALSE;
-
-	if (gSysOS.mMajorVer != 5)
-	{
-		// Unknown windows version number, exit!"
-		llwarns << "Unknown Windows major version " << gSysOS.mMajorVer << ", aborting detection!" << llendl;
-		return FALSE;
-	}
-	if (gSysOS.mMinorVer == 0)
-	{
-		is_2000 = TRUE;
-	}
-	else if (gSysOS.mMinorVer == 1)
-	{
-		is_xp = TRUE;
-	}
-	else
-	{
-		llwarns << "Unknown Windows minor version " << gSysOS.mMinorVer << ", aborting detection!" << llendl;
-		return FALSE;
-	}
-
-	// This only works on Win32, as it relies on DX9 hardware detection
-	// The PCI masks are actually the inverse of the normal masks
-	// We actually look through the masks,and see if any hardware matches it.
-	// This is because the masks encode logic about
-
-	// Check for the broken AMD AGP controllers (751, 761, 762)
-
-	// Horrible cruddy fixed lookup table.
-	// Figure out what OS we're on, the version numbers are different.  Sigh...
-
-	LLDXDriverFile *dfilep = NULL;
-	LLDXDevice *devp = NULL;
-
-	// AMD(1022) AGP controllers
-	// 7007		AMD-751 AGP Controller
-	// 700F		AMD-761 AGP Controller
-	// 700D		AMD-762 AGP Controller
-	devp = gDXHardware.findDevice("VEN_1022", "DEV_7007|DEV_700F|DEV_700D");
-	if (devp)
-	{
-		// We're just pretty much screwed here, there are big problems with this hardware
-		// We've got trouble with the most recent nVidia drivers.  Check for this and warn.
-
-		// Note:  Need to detect that we're running with older nVidia hardware, probably
-		exit_after_bad |= bad_hardware_dialog("AMD AGP Controller",
-											  AMD_AGP_URL);
-	}
-
-	// VIA(1106) AGP Controllers
-	// These need upgrading on both Win2K and WinXP
-	//
-	// 8305		VT8363/8365			CPU to AGP - Apollo KT133/KM133
-	// 8598		VT82C598MVP/694X	CPU to AGP - Apollo MVP3/Pro133A
-	// 8605		VT8605				CPU to AGP - Apollo PM133
-	// B091		VT8633				CPU to AGP - Apollo Pro 266
-	// B099		VT8366/A/T			CPU to AGP - Apollo KT266/A/333
-	// B168		VT8235				CPU to AGP (AGP 2.0/3.0) - ProSavageDDR P4X333 chipset
-	// B188		VT8237				CPU to AGP (AGP 2.0/3.0) - K8T800
-	// B198		VT8237				CPU to AGP (AGP 2.0/3.0) - ProSavageDDR P4X600 chipset
-
-	devp = gDXHardware.findDevice("VEN_1106",
-								  "DEV_8305|DEV_8598|DEV_8605|DEV_B091|"
-								  "DEV_B099|DEV_B168|DEV_B188|DEV_B198");
-	if (devp)
-	{
-		BOOL out_of_date = FALSE;
-		// Wanted driver: VIAAGP1.SYS
-		// Version Format: M.mm.0000.vvvv
-		//		M.mm - Major/minor OS version (5.0 for Win2000, 5.1 for WinXP)
-		//		vvvv - driver version number
-		//
-		// Notes:
-		// 3442 is most recent as of 2/25/04, probably want at least 3430 (seems to be a common version)
-
-		// These are DELIBERATE assignments inside if statements, blech.
-		if (dfilep = devp->findDriver("pci.sys"))
-		{
-			// Old driver: pci.sys
-			// Version: 5.01.2600.xxxx
-			//
-			// Notes:
-			// Default WinXP driver for B168, B198?
-
-			// Old driver: pci.sys
-			// Version: 5.01.2195.xxxx
-			//
-			// Notes:
-			// Default Win2K driver for 8305?
-
-			llwarns << "Detected pci.sys" << llendl;
-			write_debug("Old driver (pci.sys) for VIA detected!");
-			out_of_date = TRUE;
-		}
-		else if (dfilep = devp->findDriver("VIAAGP.SYS"))
-		{
-			// Old driver: VIAAGP.SYS
-			// Version: 5.01.2600.xxxx
-			//
-			// Notes:
-			// Default WinXP driver for B09x?
-
-			llwarns << "Detected VIAAGP.SYS" << llendl;
-			write_debug("Old driver (VIAAGP.SYS) for VIA detected!");
-			out_of_date = TRUE;
-		}
-		else if (dfilep = devp->findDriver("VIAAGP1.SYS"))
-		{
-			if (dfilep->mVersion.getField(3) < 3430)
-			{
-				// They're using a pretty old version of the VIA AGP drivers
-				// Maybe they want to upgrade?
-			    llwarns << "Detected VIAAGP1.SYS" << llendl;
-			    write_debug("Old driver (VIAAGP1.SYS) for VIA detected!");
-				out_of_date = TRUE;
-			}
-		}
-		if (out_of_date)
-		{
-			exit_after_bad |= bad_hardware_dialog("Out of date VIA AGP chipset driver",
-												  VIA_URL);
-		}
-	}
-
-	// Intel(8086) AGP controllers (Win2K)
-	// These particular controllers only may need drivers on Win2K
-	//
-	// 1A31		82845[MP|MZ] Processor to AGP Controller
-	// 2532		82850/860 Processor to AGP Controller
-	if (is_2000)
-	{
-		devp = gDXHardware.findDevice("VEN_8086",
-									  "DEV_1A31");
-		if (devp)
-		{
-			if (dfilep = devp->findDriver("pci.sys"))
-			{
-				// Old driver: pci.sys
-				// Version 5.01.21[9|6]5.xxxx
-				//
-				// Notes:
-				// Default driver for Win2K?  Not sure what the "correct" driver is -
-				// maybe some variant of AGP440.SYS?
-				llwarns << "Detected pci.sys" << llendl;
-				write_debug("Old driver (pci.sys) for Intel 82845/850 on Win2K detected!");
-				exit_after_bad |= bad_hardware_dialog("Out of date Intel chipset driver",
-													  INTEL_CHIPSET_URL);
-			}
-		}
-	}
-
-	/* Removed 4/3/2006 by JC
-	   After talking with Doug, we don't know what the proper driver
-	   and/or version number should be for Intel 865.  Regardless, this
-	   code would _always_ complain if you had that chipset.
-
-	// Intel(8086) AGP controllers (All)
-	// These particular controllers may need drivers on both Win2K and WinXP
-	//
-	// 2561		82845G/GL/GE/PE/GV Processor to AGP Controller
-	// 2571		82865G/PE/P/GV/28248P Processor to AGP Controller
-	devp = gDXHardware.findDevice("VEN_8086",
-								  "DEV_2571");
-	if (devp)
-	{
-		// Wanted driver: AGP440.SYS(?)
-		//
-		// Notes:
-		// Not sure, need to verify with an actual 82865/75 (Dell 8300?)
-		
-		// Old driver: pci.sys
-		// Version 5.01.21[9|6]5.xxxx
-		//
-		// Notes:
-		// Default driver for Win2K?  Not sure what the "correct" driver is -
-		// maybe some variant of AGP440.SYS?
-		exit_after_bad |= bad_hardware_dialog("Out of date Intel chipset driver",
-											  INTEL_CHIPSET_URL);
-	}
-	*/
-
-
-	// SiS(1039) AGP controllers (All)
-	// These particular controllers may need drivers on both Win2K and WinXP
-	//
-	// 0001		SiS 530
-	// 0002		SiS SG86C202(???)
-	// 0003		SiS 648FX
-	devp = gDXHardware.findDevice("VEN_1039",
-								  "DEV_0001|DEV_0002|DEV_0003");
-	if (devp)
-	{
-		BOOL out_of_date = FALSE;
-		// Wanted driver: SISAGPX.SYS
-		//
-		// Notes:
-		// Not sure, need to verify with an actual 82865/75 (Dell 8300?)
-		
-		// Old driver: pci.sys
-		// Version 5.01.21[9|6]5.xxxx
-		//
-		// Notes:
-		// Default driver for Win2K?  Not sure what the "correct" driver is -
-		// maybe some variant of AGP440.SYS?
-		if (dfilep = devp->findDriver("pci.sys"))
-		{
-			// Old driver: pci.sys
-			// Version 5.01.21[9|6]5.xxxx
-			//
-			llwarns << "Detected pci.sys" << llendl;
-			write_debug("Old driver (pci.sys) for SiS detected!");
-			out_of_date = TRUE;
-		}
-
-		if (dfilep = devp->findDriver("sisagp.sys"))
-		{
-			// Old driver: pci.sys
-			// Version 5.01.21[9|6]5.xxxx
-			//
-			llwarns << "Detected sisagp.sys" << llendl;
-			write_debug("Old driver (sisagp.sys) for SiS detected!");
-			out_of_date = TRUE;
-		}
-
-		if (dfilep = devp->findDriver("sisagpx.sys"))
-		{
-			// Old driver: pci.sys
-			// Version 7.02.0000.xxxx
-			//
-			// Notes:
-			// Default driver for Win2K?  Not sure what the "correct" driver is -
-			// maybe some variant of AGP440.SYS?
-			if (dfilep->mVersion.getField(3) < 1160)
-			{
-				out_of_date = TRUE;
-				llwarns << "Detected sisagpx.sys" << llendl;
-				write_debug("Old driver (sisagpx.sys) for SiS detected!");
-			}
-		}
-		if (out_of_date)
-		{
-			exit_after_bad |= bad_hardware_dialog("Out of date SiS chipset driver",
-												  SIS_CHIPSET_URL);
-		}
-	}
-
-	return exit_after_bad;
-#else
-	return TRUE;
-#endif
-}
-
 void LLFeatureManager::applyRecommendedFeatures()
 {
 	// see featuretable.txt
@@ -770,14 +473,14 @@ void LLFeatureManager::applyRecommendedFeatures()
 	dump();
 #endif
 	
-	// Enabling AGP
-	if (getRecommendedLevel("RenderAGP"))
+	// Enabling VBO
+	if (getRecommendedLevel("RenderVBO"))
 	{
-		gSavedSettings.setBOOL("RenderUseAGP", TRUE);
+		gSavedSettings.setBOOL("RenderVBOEnable", TRUE);
 	}
 	else
 	{
-		gSavedSettings.setBOOL("RenderUseAGP", FALSE);
+		gSavedSettings.setBOOL("RenderVBOEnable", FALSE);
 	}
 
 	// Anisotropic rendering
diff --git a/indra/newview/llfeaturemanager.h b/indra/newview/llfeaturemanager.h
index fe37cd7638f9df5d63defa6139ccffa7c5829821..8988d790e8691fa346f1ecb1cef4acdd7273af7d 100644
--- a/indra/newview/llfeaturemanager.h
+++ b/indra/newview/llfeaturemanager.h
@@ -81,8 +81,7 @@ public:
 
 	void initCPUFeatureMasks();
 	void initGraphicsFeatureMasks();
-	BOOL initPCIFeatureMasks();
-
+	
 	void applyRecommendedFeatures();
 
 protected:
diff --git a/indra/newview/llfilepicker.cpp b/indra/newview/llfilepicker.cpp
index 98bbf23502c911d549984274e3628eb9898e8315..2b2a9fa85968c87b3757e943aca887131b93261f 100644
--- a/indra/newview/llfilepicker.cpp
+++ b/indra/newview/llfilepicker.cpp
@@ -345,6 +345,16 @@ BOOL LLFilePicker::getSaveFile(ESaveFilter filter, const char* filename)
 		mOFN.lpstrFilter =	RAW_FILTER \
 							L"\0";
 		break;
+	case FFSAVE_J2C:
+		if (!filename)
+		{
+			wcsncpy( mFilesW,L"untitled.j2c", FILENAME_BUFFER_SIZE);
+		}
+		mOFN.lpstrDefExt = L"j2c";
+		mOFN.lpstrFilter =
+			L"Compressed Images (*.j2c)\0*.j2c\0" \
+			L"\0";
+		break;
 	default:
 		return FALSE;
 	}
@@ -660,6 +670,12 @@ OSStatus	LLFilePicker::doNavSaveDialog(ESaveFilter filter, const char* filename)
 			extension = CFSTR(".raw");
 			break;
 
+		case FFSAVE_J2C:
+			type = '\?\?\?\?';
+			creator = 'prvw';
+			extension = CFSTR(".j2c");
+			break;
+		
 		case FFSAVE_ALL:
 		default:
 			type = '\?\?\?\?';
@@ -1063,6 +1079,10 @@ BOOL LLFilePicker::getSaveFile( ESaveFilter filter, const char* filename )
 			caption += "RAW File (*.raw)";
 			suggest_ext += ".raw";
 			break;
+		case FFSAVE_J2C:
+			caption += "Compressed Images (*.j2c)";
+			suggest_ext += ".j2c";
+			break;
 		default:;
 			break;
 		}
diff --git a/indra/newview/llfilepicker.h b/indra/newview/llfilepicker.h
index 9b0eddbe2d1ed1e24bd599cfaac43e5bd0b7ad23..c04279ee917d56cdbcd83705c905481832844f52 100644
--- a/indra/newview/llfilepicker.h
+++ b/indra/newview/llfilepicker.h
@@ -87,6 +87,7 @@ public:
 		FFSAVE_XML = 9,
 		FFSAVE_COLLADA = 10,
 		FFSAVE_RAW = 11,
+		FFSAVE_J2C = 12,
 	};
 
 	// open the dialog. This is a modal operation
diff --git a/indra/newview/llflexibleobject.cpp b/indra/newview/llflexibleobject.cpp
index 5e105ac7e20be29d2eb9e4ce06d247a5b6f40a78..4e03e346632856967591947007fd9ecfee3d5727 100644
--- a/indra/newview/llflexibleobject.cpp
+++ b/indra/newview/llflexibleobject.cpp
@@ -25,11 +25,6 @@
 #include "llviewerregion.h"
 #include "llworld.h"
 
-/*static*/ LLVolumeImplFlexible::lodset_t LLVolumeImplFlexible::sLODBins[ FLEXIBLE_OBJECT_MAX_LOD ];
-/*static*/ U64 LLVolumeImplFlexible::sCurrentUpdateFrame = 0;
-/*static*/ U32 LLVolumeImplFlexible::sDebugInserted = 0;
-/*static*/ U32 LLVolumeImplFlexible::sDebugVisible = 0;
-
 /*static*/ F32 LLVolumeImplFlexible::sUpdateFactor = 1.0f;
 
 // LLFlexibleObjectData::pack/unpack now in llprimitive.cpp
@@ -40,14 +35,13 @@
 LLVolumeImplFlexible::LLVolumeImplFlexible(LLViewerObject* vo, LLFlexibleObjectData* attributes) :
 		mVO(vo), mAttributes(attributes)
 {
+	static U32 seed = 0;
+	mID = seed++;
 	mInitialized = FALSE;
 	mUpdated = FALSE;
-	mJustShifted = FALSE;
 	mInitializedRes = -1;
 	mSimulateRes = 0;
 	mFrameNum = 0;
-	mLastUpdate = 0;
-
 }//-----------------------------------------------
 
 LLVector3 LLVolumeImplFlexible::getFramePosition() const
@@ -75,7 +69,6 @@ void LLVolumeImplFlexible::onShift(const LLVector3 &shift_vector)
 	{
 		mSection[section].mPosition += shift_vector;	
 	}
-	mVO->getVolume()->mBounds[0] += shift_vector;
 }
 
 //-----------------------------------------------------------------------------------------------
@@ -88,7 +81,7 @@ void LLVolumeImplFlexible::setParentPositionAndRotationDirectly( LLVector3 p, LL
 
 void LLVolumeImplFlexible::remapSections(LLFlexibleObjectSection *source, S32 source_sections,
 										 LLFlexibleObjectSection *dest, S32 dest_sections)
-{
+{	
 	S32 num_output_sections = 1<<dest_sections;
 	LLVector3 scale = mVO->mDrawable->getScale();
 	F32 source_section_length = scale.mV[VZ] / (F32)(1<<source_sections);
@@ -209,6 +202,7 @@ void LLVolumeImplFlexible::setAttributesOfAllSections()
 
 	F32 t_inc = 1.f/F32(num_sections);
 	F32 t = t_inc;
+
 	for ( int i=1; i<= num_sections; i++)
 	{
 		mSection[i].mAxisRotation.setQuat(lerp(begin_rot,end_rot,t),0,0,1);
@@ -217,18 +211,23 @@ void LLVolumeImplFlexible::setAttributesOfAllSections()
 			scale.mV[VY] * lerp(bottom_scale.mV[1], top_scale.mV[1], t));
 		t += t_inc;
 	}
-	mLastUpdate = 0;
 }//-----------------------------------------------------------------------------------
 
 
 void LLVolumeImplFlexible::onSetVolume(const LLVolumeParams &volume_params, const S32 detail)
 {
-	doIdleUpdate(gAgent, *gWorldp, 0.0);
+	if (mVO && mVO->mDrawable.notNull())
+	{
+		LLVOVolume* volume = (LLVOVolume*) mVO;
+		volume->regenFaces();
+	}
+
+	/*doIdleUpdate(gAgent, *gWorldp, 0.0);
 	if (mVO && mVO->mDrawable.notNull())
 	{
 		gPipeline.markRebuild(mVO->mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);
 		gPipeline.markMoved(mVO->mDrawable);
-	}
+	}*/
 }
 
 //---------------------------------------------------------------------------------
@@ -238,6 +237,13 @@ void LLVolumeImplFlexible::onSetVolume(const LLVolumeParams &volume_params, cons
 //---------------------------------------------------------------------------------
 BOOL LLVolumeImplFlexible::doIdleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
 {
+	if (!gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_FLEXIBLE))
+	{
+		return TRUE;
+	}
+
+	LLFastTimer ftm(LLFastTimer::FTM_FLEXIBLE_UPDATE);
+
 	if (mVO->mDrawable.isNull())
 	{
 		// Don't do anything until we have a drawable
@@ -248,46 +254,17 @@ BOOL LLVolumeImplFlexible::doIdleUpdate(LLAgent &agent, LLWorld &world, const F6
 	mVO->mDrawable->mQuietCount = 0;
 	if (!mVO->mDrawable->isRoot())
 	{
-		mVO->mDrawable->getParent()->mQuietCount = 0;
-	}
-	
-	if (((LLVOVolume*)mVO)->mLODChanged || 
-		mVO->mDrawable->isState(LLDrawable::IN_REBUILD_Q1))
-	{
-		mLastUpdate = 0; // Force an immediate update
+		LLViewerObject* parent = (LLViewerObject*) mVO->getParent();
+		parent->mDrawable->mQuietCount = 0;
 	}
-	// Relegate invisible objects to the lowest priority bin
-	S32 lod = 0;
-	F32 app_angle = mVO->getAppAngle()*DEG_TO_RAD/gCamera->getView();
-	if (mVO->mDrawable->isVisible())
-	{
-		sDebugVisible++;
-		if (mVO->isSelected())
-		{
-			// Force selected objects to update *every* frame
-			lod = FLEXIBLE_OBJECT_MAX_LOD-1;
-		}
-		else
-		{
-			if (app_angle > 0)
-			{
-				lod = 5 - (S32)(1.0f/sqrtf(app_angle));
-				if (lod < 1)
-				{
-					lod = 1;
-				}
-			}
+		
+	S32 new_res = mAttributes->getSimulateLOD();
 
-			if (mVO->isAttachment())
-			{
-				lod += 3;
-			}
-		}
-	}
+	//number of segments only cares about z axis
+	F32 app_angle = llround((F32) atan2( mVO->getScale().mV[2]*2.f, mVO->mDrawable->mDistanceWRTCamera) * RAD_TO_DEG, 0.01f);
 
-	S32 new_res = mAttributes->getSimulateLOD();
 	// Rendering sections increases with visible angle on the screen
-	mRenderRes = (S32)(FLEXIBLE_OBJECT_MAX_SECTIONS*4*app_angle);
+	mRenderRes = (S32)(FLEXIBLE_OBJECT_MAX_SECTIONS*4*app_angle*DEG_TO_RAD/gCamera->getView());
 	if (mRenderRes > FLEXIBLE_OBJECT_MAX_SECTIONS)
 	{
 		mRenderRes = FLEXIBLE_OBJECT_MAX_SECTIONS;
@@ -310,22 +287,32 @@ BOOL LLVolumeImplFlexible::doIdleUpdate(LLAgent &agent, LLWorld &world, const F6
 		mInitialized = TRUE;
 	}
 
-	sLODBins[lod].insert(this);
-	sDebugInserted++;
-	return TRUE;
-}
-
-// static
-void LLVolumeImplFlexible::resetUpdateBins()
-{
-	U32 lod;
-	for (lod=0; lod<FLEXIBLE_OBJECT_MAX_LOD; ++lod)
+	if (mVO->mDrawable->isVisible() &&
+		!mVO->mDrawable->isState(LLDrawable::IN_REBUILD_Q1) &&
+		mVO->getPixelArea() > 256.f)
 	{
-		sLODBins[lod].clear();
+		U32 id;
+		F32 pixel_area = mVO->getPixelArea();
+
+		if (mVO->isRootEdit())
+		{
+			id = mID;
+		}
+		else
+		{
+			LLVOVolume* parent = (LLVOVolume*) mVO->getParent();
+			id = parent->getVolumeInterfaceID();
+		}
+
+		U32 update_period = (U32) (gCamera->getScreenPixelArea()*0.01f/(pixel_area*(sUpdateFactor+1.f)))+1;
+
+		if ((LLDrawable::getCurrentFrame()+id)%update_period == 0)
+		{
+			gPipeline.markRebuild(mVO->mDrawable, LLDrawable::REBUILD_POSITION, FALSE);
+		}
 	}
-	++sCurrentUpdateFrame;
-	sDebugInserted = 0;
-	sDebugVisible = 0;
+	
+	return TRUE;
 }
 
 inline S32 log2(S32 x)
@@ -339,80 +326,15 @@ inline S32 log2(S32 x)
 	return ret;
 }
 
-// static
-void LLVolumeImplFlexible::doFlexibleUpdateBins()
-{
-	U32 lod;
-	U32 updated = 0;
-	U32 regen = 0;
-	U32 newflexies = 0;
-	F32 time_alloc[FLEXIBLE_OBJECT_MAX_LOD];
-	F32 total_time_alloc = 0;
-
-	bool new_objects_only = false;
-
-	if (!gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_FLEXIBLE))
-	{
-		new_objects_only = true;
-	}
-
-	for (lod=0; lod<FLEXIBLE_OBJECT_MAX_LOD; ++lod)
-	{
-		int count = sLODBins[lod].size();
-		if (count > 0)
-		{
-			time_alloc[lod] = (F32)((lod+1)*(log2(count)));
-		}
-		else
-		{
-			time_alloc[lod] = 0;
-		}
-		total_time_alloc += time_alloc[lod];
-	}
-	total_time_alloc = FLEXIBLE_OBJECT_TIMESLICE * (sUpdateFactor+0.01f) / total_time_alloc;
-
-	{
-		LLFastTimer t(LLFastTimer::FTM_FLEXIBLE_UPDATE);
-		LLTimer timer;
-		for (lod=0; lod<FLEXIBLE_OBJECT_MAX_LOD; ++lod)
-		{
-			LLVolumeImplFlexible::lodset_t::iterator itor = sLODBins[lod].begin();
-			int bin_count = 0;
-			if (!new_objects_only)
-			{
-				timer.reset();
-				double end_time = time_alloc[lod] * total_time_alloc;
-				for (; itor!=sLODBins[lod].end(); ++itor)
-				{
-
-					(*itor)->doFlexibleUpdate();
-					++updated;
-					(*itor)->doFlexibleRebuild();
-					++bin_count;
-					++regen;
-					if (timer.getElapsedTimeF64() > end_time)
-					{
-						break;
-					}
-				}
-			}
-			for (; itor != sLODBins[lod].end(); ++itor)
-			{
-				if ((*itor)->getLastUpdate() == 0)
-				{
-					// *Always* update newly-created objects, or objects which have changed LOD
-					(*itor)->doFlexibleUpdate();
-					(*itor)->doFlexibleRebuild();
-					++newflexies;
-				}
-			}
-		}
-	}
-}
-
 void LLVolumeImplFlexible::doFlexibleUpdate()
 {
 	LLPath *path = &mVO->getVolume()->getPath();
+	if (mSimulateRes == 0)
+	{
+		mVO->markForUpdate(TRUE);
+		doIdleUpdate(gAgent, *gWorldp, 0.0);
+	}
+	
 	S32 num_sections = 1 << mSimulateRes;
 
     F32 secondsThisFrame = mTimer.getElapsedTimeAndResetF32();
@@ -584,6 +506,10 @@ void LLVolumeImplFlexible::doFlexibleUpdate()
 		// calculate velocity
 		//------------------------------------------------------------------------------------------
 		mSection[i].mVelocity = mSection[i].mPosition - lastPosition;
+		if (mSection[i].mVelocity.magVecSquared() > 1.f)
+		{
+			mSection[i].mVelocity.normVec();
+		}
 	}
 
 	// Calculate derivatives (not necessary until normals are automagically generated)
@@ -624,11 +550,38 @@ void LLVolumeImplFlexible::doFlexibleUpdate()
 	LLFlexibleObjectSection newSection[ (1<<FLEXIBLE_OBJECT_MAX_SECTIONS)+1 ];
 	remapSections(mSection, mSimulateRes, newSection, mRenderRes);
 
+	//generate transform from global to prim space
+	LLVector3 delta_scale = LLVector3(1,1,1);
+	LLVector3 delta_pos;
+	LLQuaternion delta_rot;
+
+	delta_rot = ~getFrameRotation();
+	delta_pos = -getFramePosition()*delta_rot;
+		
+	// Vertex transform (4x4)
+	LLVector3 x_axis = LLVector3(delta_scale.mV[VX], 0.f, 0.f) * delta_rot;
+	LLVector3 y_axis = LLVector3(0.f, delta_scale.mV[VY], 0.f) * delta_rot;
+	LLVector3 z_axis = LLVector3(0.f, 0.f, delta_scale.mV[VZ]) * delta_rot;
+
+	LLMatrix4 rel_xform;
+	rel_xform.initRows(LLVector4(x_axis, 0.f),
+								LLVector4(y_axis, 0.f),
+								LLVector4(z_axis, 0.f),
+								LLVector4(delta_pos, 1.f));
+			
 	for (i=0; i<=num_render_sections; ++i)
 	{
 		new_point = &path->mPath[i];
-		new_point->mPos = newSection[i].mPosition;
-		new_point->mRot = mSection[i].mAxisRotation * newSection[i].mRotation;
+		LLVector3 pos = newSection[i].mPosition * rel_xform;
+		LLQuaternion rot = mSection[i].mAxisRotation * newSection[i].mRotation * delta_rot;
+		
+		if (!mUpdated || (new_point->mPos-pos).magVecSquared() > 0.000001f)
+		{
+			new_point->mPos = newSection[i].mPosition * rel_xform;
+			mUpdated = FALSE;
+		}
+
+		new_point->mRot = rot;
 		new_point->mScale = newSection[i].mScale;
 		new_point->mTexT = ((F32)i)/(num_render_sections);
 	}
@@ -639,13 +592,10 @@ void LLVolumeImplFlexible::doFlexibleUpdate()
 void LLVolumeImplFlexible::doFlexibleRebuild()
 {
 	mVO->getVolume()->regen();
-
-	mVO->markForUpdate(TRUE);
-
 	mUpdated = TRUE;
+}
 
-	mLastUpdate = sCurrentUpdateFrame;
-}//------------------------------------------------------------------
+//------------------------------------------------------------------
 
 void LLVolumeImplFlexible::onSetScale(const LLVector3& scale, BOOL damped)
 {
@@ -654,8 +604,6 @@ void LLVolumeImplFlexible::onSetScale(const LLVector3& scale, BOOL damped)
 
 BOOL LLVolumeImplFlexible::doUpdateGeometry(LLDrawable *drawable)
 {
-	BOOL compiled = FALSE;
-
 	LLVOVolume *volume = (LLVOVolume*)mVO;
 
 	if (volume->mDrawable.isNull()) // Not sure why this is happening, but it is...
@@ -663,60 +611,26 @@ BOOL LLVolumeImplFlexible::doUpdateGeometry(LLDrawable *drawable)
 		return TRUE; // No update to complete
 	}
 
-	volume->calcAllTEsSame();
-
-	if (volume->mVolumeChanged || volume->mFaceMappingChanged)
+	if (volume->mLODChanged)
 	{
-		compiled = TRUE;
-		volume->regenFaces();
-	}
-	else if (volume->mLODChanged)
-	{
-		LLPointer<LLVolume> old_volumep, new_volumep;
-		F32 old_lod, new_lod;
-
-		old_volumep = volume->getVolume();
-		old_lod = old_volumep->getDetail();
-
- 		LLVolumeParams volume_params = volume->getVolume()->getParams();
+		LLVolumeParams volume_params = volume->getVolume()->getParams();
 		volume->setVolume(volume_params, 0);
-		doFlexibleUpdate();
-		volume->getVolume()->regen();
-		
-		new_volumep = volume->getVolume();
-		new_lod = new_volumep->getDetail();
-
-		if (new_lod != old_lod)
-		{
-			compiled = TRUE;
-			if (new_volumep->getNumFaces() != old_volumep->getNumFaces())
-			{
-				volume->regenFaces();
-			}
-		}
-	}
-
-	if (mUpdated)
-	{
-		compiled = TRUE;
-		mUpdated = FALSE;
 	}
 
-	if(compiled)
+	volume->updateRelativeXform();
+	doFlexibleUpdate();
+	if (!mUpdated || volume->mFaceMappingChanged)
 	{
-		volume->updateRelativeXform(isVolumeGlobal());
-		volume->genTriangles(isVolumeGlobal());
-		LLPipeline::sCompiles++;
+		doFlexibleRebuild();
+		volume->genBBoxes(isVolumeGlobal());
 	}
-	
+			
 	volume->mVolumeChanged = FALSE;
 	volume->mLODChanged = FALSE;
 	volume->mFaceMappingChanged = FALSE;
 
 	// clear UV flag
 	drawable->clearState(LLDrawable::UV);
-
-	drawable->movePartition();
 	
 	return TRUE;
 }
@@ -792,42 +706,32 @@ LLQuaternion LLVolumeImplFlexible::getEndRotation()
 }//------------------------------------------------------------------
 
 
-void LLVolumeImplFlexible::updateRelativeXform(BOOL global_volume)
+void LLVolumeImplFlexible::updateRelativeXform()
 {
-	LLVOVolume* vo = (LLVOVolume*) mVO;
-	
-	LLVector3 delta_scale = LLVector3(1,1,1);
-	LLVector3 delta_pos;
 	LLQuaternion delta_rot;
+	LLVector3 delta_pos, delta_scale;
+	LLVOVolume* vo = (LLVOVolume*) mVO;
+
+	//matrix from local space to parent relative/global space
+	delta_rot = vo->mDrawable->isSpatialRoot() ? LLQuaternion() : vo->mDrawable->getRotation();
+	delta_pos = vo->mDrawable->isSpatialRoot() ? LLVector3(0,0,0) : vo->mDrawable->getPosition();
+	delta_scale = LLVector3(1,1,1);
 
-	if (!mVO->mDrawable->isRoot())
-	{	//global to parent relative
-		LLViewerObject* parent = (LLViewerObject*) vo->getParent();
-		delta_rot = ~parent->getRenderRotation();
-		delta_pos = -parent->getRenderPosition()*delta_rot;
-	}
-	else
-	{	//global to local
-		delta_rot = ~getFrameRotation();
-		delta_pos = -getFramePosition()*delta_rot;
-	}
-	
 	// Vertex transform (4x4)
 	LLVector3 x_axis = LLVector3(delta_scale.mV[VX], 0.f, 0.f) * delta_rot;
 	LLVector3 y_axis = LLVector3(0.f, delta_scale.mV[VY], 0.f) * delta_rot;
 	LLVector3 z_axis = LLVector3(0.f, 0.f, delta_scale.mV[VZ]) * delta_rot;
 
 	vo->mRelativeXform.initRows(LLVector4(x_axis, 0.f),
-								LLVector4(y_axis, 0.f),
-								LLVector4(z_axis, 0.f),
-								LLVector4(delta_pos, 1.f));
+							LLVector4(y_axis, 0.f),
+							LLVector4(z_axis, 0.f),
+							LLVector4(delta_pos, 1.f));
 			
 	x_axis.normVec();
 	y_axis.normVec();
 	z_axis.normVec();
 	
 	vo->mRelativeXformInvTrans.setRows(x_axis, y_axis, z_axis);
-
 }
 
 const LLMatrix4& LLVolumeImplFlexible::getWorldMatrix(LLXformMatrix* xform) const
diff --git a/indra/newview/llflexibleobject.h b/indra/newview/llflexibleobject.h
index 0d782d96aca47c76198c3a3bd282c219a1b72493..5a782bc61743379b2433095b4df8e78d64d911f2 100644
--- a/indra/newview/llflexibleobject.h
+++ b/indra/newview/llflexibleobject.h
@@ -57,6 +57,7 @@ class LLVolumeImplFlexible : public LLVolumeInterface
 		LLVolumeImplFlexible(LLViewerObject* volume, LLFlexibleObjectData* attributes);
 
 		// Implements LLVolumeInterface
+		U32 getID() const { return mID; }
 		LLVector3 getFramePosition() const;
 		LLQuaternion getFrameRotation() const;
 		LLVolumeInterfaceType getInterfaceType() const		{ return INTERFACE_FLEXIBLE; }
@@ -71,12 +72,10 @@ class LLVolumeImplFlexible : public LLVolumeInterface
 		bool isVolumeGlobal() const { return true; }
 		bool isActive() const { return true; }
 		const LLMatrix4& getWorldMatrix(LLXformMatrix* xform) const;
-		void updateRelativeXform(BOOL global_volume = FALSE);
+		void updateRelativeXform();
 		void doFlexibleUpdate(); // Called to update the simulation
 		void doFlexibleRebuild(); // Called to rebuild the geometry
-		static void resetUpdateBins();
-		static void doFlexibleUpdateBins();
-
+		
 		//void				setAttributes( LLFlexibleObjectData );
 		void				setParentPositionAndRotationDirectly( LLVector3 p, LLQuaternion r );
 		void				setUsingCollisionSphere( bool u );
@@ -109,10 +108,7 @@ class LLVolumeImplFlexible : public LLVolumeInterface
 		U32							mFrameNum;
 		LLVector3					mCollisionSpherePosition;
 		F32							mCollisionSphereRadius;
-		
-		U64							mLastUpdate;
-
-		BOOL						mJustShifted;
+		U32							mID;
 
 		//--------------------------------------
 		// private methods
@@ -121,29 +117,7 @@ class LLVolumeImplFlexible : public LLVolumeInterface
 
 		void remapSections(LLFlexibleObjectSection *source, S32 source_sections,
 										 LLFlexibleObjectSection *dest, S32 dest_sections);
-
-		U64 getLastUpdate() const	{ return mLastUpdate; }
-
-		// LOD Bins
-		struct FlexCompare
-		{
-			bool operator()(LLVolumeImplFlexible* a, LLVolumeImplFlexible* b) const
-			{
-				U64 a_update = a->getLastUpdate();
-				U64 b_update = b->getLastUpdate();
-				if (a_update == b_update)
-				{
-					return a < b; // compare pointers
-				}
-				return a_update < b_update;
-			}
-		};
-		typedef std::set<LLVolumeImplFlexible*, FlexCompare> lodset_t;
-		static lodset_t sLODBins[ FLEXIBLE_OBJECT_MAX_LOD ];
-		static U64					sCurrentUpdateFrame;
-		static U32					sDebugInserted;
-		static U32					sDebugVisible;
-
+		
 public:
 		// Global setting for update rate
 		static F32					sUpdateFactor;
diff --git a/indra/newview/llfloateranimpreview.cpp b/indra/newview/llfloateranimpreview.cpp
index 21248c7406d82d4b61b68f41828dd0b9f56450bf..e37850c997efaa8eff71bd324e5b7430dbe3c27a 100644
--- a/indra/newview/llfloateranimpreview.cpp
+++ b/indra/newview/llfloateranimpreview.cpp
@@ -997,7 +997,7 @@ LLPreviewAnimation::LLPreviewAnimation(S32 width, S32 height) : LLDynamicTexture
 	mDummyAvatar->updateGeometry(mDummyAvatar->mDrawable);
 	mDummyAvatar->startMotion(ANIM_AGENT_STAND, 5.f);
 	mDummyAvatar->mSkirtLOD.setVisible(FALSE, TRUE);
-	gPipeline.markVisible(mDummyAvatar->mDrawable);
+	gPipeline.markVisible(mDummyAvatar->mDrawable, *gCamera);
 
 	// stop extraneous animations
 	mDummyAvatar->stopMotion( ANIM_AGENT_HEAD_ROT, TRUE );
@@ -1073,6 +1073,10 @@ BOOL	LLPreviewAnimation::render()
 		avatarp->updateMotion();
 	}
 	
+	LLVertexBuffer::stopRender();
+	avatarp->updateLOD();
+	LLVertexBuffer::startRender();
+
 	avatarp->mRoot.updateWorldMatrixChildren();
 
 	stop_glerror();
@@ -1082,13 +1086,7 @@ BOOL	LLPreviewAnimation::render()
 	if (avatarp->mDrawable.notNull())
 	{
 		LLDrawPoolAvatar *avatarPoolp = (LLDrawPoolAvatar *)avatarp->mDrawable->getFace(0)->getPool();
-		gPipeline.unbindAGP();
-		avatarPoolp->syncAGP();
-		if (avatarPoolp->canUseAGP() && gPipeline.usingAGP())
-		{
-			gPipeline.bindAGP();
-		}
-		avatarPoolp->renderAvatars(avatarp, TRUE);  // renders only one avatar (no shaders)
+		avatarPoolp->renderAvatars(avatarp);  // renders only one avatar
 	}
 	
 	return TRUE;
diff --git a/indra/newview/llfloaterimagepreview.cpp b/indra/newview/llfloaterimagepreview.cpp
index 1ae6decfdd744d1e9336519cd2dd2dfa008145d2..2e77bbcfbd051c9b4284e7d36c2b33b54e18b416 100644
--- a/indra/newview/llfloaterimagepreview.cpp
+++ b/indra/newview/llfloaterimagepreview.cpp
@@ -535,7 +535,7 @@ LLImagePreviewAvatar::LLImagePreviewAvatar(S32 width, S32 height) : LLDynamicTex
 	mDummyAvatar->slamPosition();
 	mDummyAvatar->updateJointLODs();
 	mDummyAvatar->updateGeometry(mDummyAvatar->mDrawable);
-	gPipeline.markVisible(mDummyAvatar->mDrawable);
+	gPipeline.markVisible(mDummyAvatar->mDrawable, *gCamera);
 
 	mTextureName = 0;
 }
@@ -623,6 +623,10 @@ BOOL	LLImagePreviewAvatar::render()
 	gCamera->setView(gCamera->getDefaultFOV() / mCameraZoom);
 	gCamera->setPerspective(FALSE, mOrigin.mX, mOrigin.mY, mWidth, mHeight, FALSE);
 
+	LLVertexBuffer::stopRender();
+	avatarp->updateLOD();
+	LLVertexBuffer::startRender();
+
 	if (avatarp->mDrawable.notNull())
 	{
 		LLGLDepthTest gls_depth(GL_TRUE, GL_TRUE);
@@ -631,13 +635,7 @@ BOOL	LLImagePreviewAvatar::render()
 
 		LLDrawPoolAvatar *avatarPoolp = (LLDrawPoolAvatar *)avatarp->mDrawable->getFace(0)->getPool();
 		
-		gPipeline.unbindAGP();
-		avatarPoolp->syncAGP();
-		if (avatarPoolp->canUseAGP() && gPipeline.usingAGP())
-		{
-			gPipeline.bindAGP();
-		}
-		avatarPoolp->renderAvatars(avatarp, TRUE);  // renders only one avatar (no shaders)
+		avatarPoolp->renderAvatars(avatarp);  // renders only one avatar
 	}
 
 	return TRUE;
diff --git a/indra/newview/llfloatersnapshot.cpp b/indra/newview/llfloatersnapshot.cpp
index 05047e1a4aa74cd95cced54fc3d9ebbc77ab2b71..ac77b3c3f0a41a2b671c12a751a30ada17d502ac 100644
--- a/indra/newview/llfloatersnapshot.cpp
+++ b/indra/newview/llfloatersnapshot.cpp
@@ -805,8 +805,10 @@ void LLFloaterSnapshot::Impl::updateLayout(LLFloaterSnapshot* floaterp)
 
 		//RN: freeze all avatars
 		LLCharacter* avatarp;
-		for (avatarp = LLCharacter::sInstances.getFirstData(); avatarp; avatarp = LLCharacter::sInstances.getNextData())
+		for (std::vector<LLCharacter*>::iterator iter = LLCharacter::sInstances.begin();
+			iter != LLCharacter::sInstances.end(); ++iter)
 		{
+			avatarp = *iter;
 			sInstance->impl.mAvatarPauseHandles.push_back(avatarp->requestPause());
 		}
 
diff --git a/indra/newview/llglsandbox.cpp b/indra/newview/llglsandbox.cpp
index 2cb6db5a16d148de981c07675b4dc84a564be53c..7a6b7b1bf365ef97075ed1d0cf504a9513a44136 100644
--- a/indra/newview/llglsandbox.cpp
+++ b/indra/newview/llglsandbox.cpp
@@ -162,15 +162,17 @@ void LLToolSelectRect::handleRectangleSelection(S32 x, S32 y, MASK mask)
 	F32 select_dist_squared = gSavedSettings.getF32("MaxSelectDistance");
 	select_dist_squared = select_dist_squared * select_dist_squared;
 
-	x = llround((F32)x * LLUI::sGLScaleFactor.mV[VX]);
-	y = llround((F32)y * LLUI::sGLScaleFactor.mV[VY]);
-
 	BOOL deselect = (mask == MASK_CONTROL);
 	S32 left =	llmin(x, mDragStartX);
 	S32 right =	llmax(x, mDragStartX);
 	S32 top =	llmax(y, mDragStartY);
 	S32 bottom =llmin(y, mDragStartY);
 
+	left = llround((F32) left * LLUI::sGLScaleFactor.mV[VX]);
+	right = llround((F32) right * LLUI::sGLScaleFactor.mV[VX]);
+	top = llround((F32) top * LLUI::sGLScaleFactor.mV[VY]);
+	bottom = llround((F32) bottom * LLUI::sGLScaleFactor.mV[VY]);
+
 	F32 old_far_plane = gCamera->getFar();
 	F32 old_near_plane = gCamera->getNear();
 
@@ -238,7 +240,7 @@ void LLToolSelectRect::handleRectangleSelection(S32 x, S32 y, MASK mask)
 					continue;
 				}
 
-				S32 result = gCamera->sphereInFrustum(drawable->getWorldPosition(), drawable->getRadius());
+				S32 result = gCamera->sphereInFrustum(drawable->getPositionAgent(), drawable->getRadius());
 				switch (result)
 				{
 				case 0:
@@ -261,11 +263,16 @@ void LLToolSelectRect::handleRectangleSelection(S32 x, S32 y, MASK mask)
 	{
 		std::vector<LLDrawable*> potentials;
 		
-		if (gPipeline.mObjectPartition) 
+		
+		for (U32 i = 0; i < LLPipeline::NUM_PARTITIONS-1; i++)
 		{
-			gPipeline.mObjectPartition->cull(*gCamera, &potentials, TRUE);
+			LLSpatialPartition* part = gPipeline.getSpatialPartition(i);
+			if (part)
+			{
+				part->cull(*gCamera, &potentials, TRUE);
+			}
 		}
-
+		
 		for (std::vector<LLDrawable*>::iterator iter = potentials.begin();
 			 iter != potentials.end(); iter++)
 		{
@@ -285,7 +292,7 @@ void LLToolSelectRect::handleRectangleSelection(S32 x, S32 y, MASK mask)
 				continue;
 			}
 
-			S32 result = gCamera->sphereInFrustum(drawable->getWorldPosition(), drawable->getRadius());
+			S32 result = gCamera->sphereInFrustum(drawable->getPositionAgent(), drawable->getRadius());
 			if (result)
 			{
 				switch (result)
@@ -939,119 +946,6 @@ void LLViewerParcelMgr::renderCollisionSegments(U8* segments, BOOL use_pass, LLV
 	glEnd();
 }
 
-
-const S32 CLIENT_RECT_VPAD = 4;
-void LLPreviewTexture::draw()
-{
-	if( getVisible() )
-	{
-		updateAspectRatio();
-
-		LLPreview::draw();
-
-		if (!mMinimized)
-		{
-			LLGLSUIDefault gls_ui;
-			LLGLSNoTexture gls_notex;
-			
-			const LLRect& border = mClientRect;
-			LLRect interior = mClientRect;
-			interior.stretch( -PREVIEW_BORDER_WIDTH );
-
-			// ...border
-			gl_rect_2d( border, LLColor4(0.f, 0.f, 0.f, 1.f));
-			gl_rect_2d_checkerboard( interior );
-
-			if ( mImage.notNull() )
-			{
-				LLGLSTexture gls_no_texture;
-				// Draw the texture
-				glColor3f( 1.f, 1.f, 1.f );
-				gl_draw_scaled_image(interior.mLeft,
-									interior.mBottom,
-									interior.getWidth(),
-									interior.getHeight(),
-									mImage);
-
-				// Pump the texture priority
-				F32 pixel_area = mLoadingFullImage ? (F32)MAX_IMAGE_AREA  : (F32)(interior.getWidth() * interior.getHeight() );
-				mImage->addTextureStats( pixel_area );
-
-				// Don't bother decoding more than we can display, unless
-				// we're loading the full image.
-				if (!mLoadingFullImage)
-				{
-					S32 int_width = interior.getWidth();
-					S32 int_height = interior.getHeight();
-					mImage->setKnownDrawSize(int_width, int_height);
-				}
-				else
-				{
-					// Don't use this feature
-					mImage->setKnownDrawSize(0, 0);
-				}
-
-				if( mLoadingFullImage )
-				{
-					LLFontGL::sSansSerif->renderUTF8("Receiving:", 0,
-						interior.mLeft + 4, 
-						interior.mBottom + 4,
-						LLColor4::white, LLFontGL::LEFT, LLFontGL::BOTTOM,
-						LLFontGL::DROP_SHADOW);
-					
-					F32 data_progress = 0.0f;
-					F32 decode_progress = mImage->getDecodeProgress(&data_progress);
-					
-					// Draw the progress bar.
-					const S32 BAR_HEIGHT = 12;
-					const S32 BAR_LEFT_PAD = 80;
-					S32 left = interior.mLeft + 4 + BAR_LEFT_PAD;
-					S32 bar_width = mRect.getWidth() - left - RESIZE_HANDLE_WIDTH - 2;
-					S32 top = interior.mBottom + 4 + BAR_HEIGHT;
-					S32 right = left + bar_width;
-					S32 bottom = top - BAR_HEIGHT;
-
-					LLColor4 background_color(0.f, 0.f, 0.f, 0.75f);
-					LLColor4 decoded_color(0.f, 1.f, 0.f, 1.0f);
-					LLColor4 downloaded_color(0.f, 0.5f, 0.f, 1.0f);
-
-					gl_rect_2d(left, top, right, bottom, background_color);
-
-					if (data_progress > 0.0f)
-					{
-						// Decoded bytes
-						right = left + llfloor(decode_progress * (F32)bar_width);
-
-						if (left < right)
-						{
-							gl_rect_2d(left, top, right, bottom, decoded_color);
-						}
-
-						// Downloaded bytes
-						left = right;
-						right = left + llfloor((data_progress - decode_progress) * (F32)bar_width);
-
-						if (left < right)
-						{
-							gl_rect_2d(left, top, right, bottom, downloaded_color);
-						}
-					}
-				}
-				else
-				if( !mSavedFileTimer.hasExpired() )
-				{
-					LLFontGL::sSansSerif->renderUTF8("File Saved", 0,
-						interior.mLeft + 4,
-						interior.mBottom + 4,
-						LLColor4::white, LLFontGL::LEFT, LLFontGL::BOTTOM,
-						LLFontGL::DROP_SHADOW);
-				}
-			}
-		}
-	}
-}
-
-
 void draw_line_cube(F32 width, const LLVector3& center)
 {
 	width = 0.5f * width;
diff --git a/indra/newview/llhudeffecttrail.cpp b/indra/newview/llhudeffecttrail.cpp
index d76250d10afb4aad35e8ad63d2d687cc663ebf0b..05da40f21447ecda6f82b02f6bfd0921f6f29911 100644
--- a/indra/newview/llhudeffecttrail.cpp
+++ b/indra/newview/llhudeffecttrail.cpp
@@ -156,7 +156,7 @@ void LLHUDEffectSpiral::triggerLocal()
 	mKillTime = mTimer.getElapsedTimeF32() + mDuration;
 
 	BOOL show_beam = gSavedSettings.getBOOL("ShowSelectionBeam");
-	
+
 	LLColor4 color;
 	color.setVec(mColor);
 
@@ -250,20 +250,12 @@ void LLHUDEffectSpiral::setTargetObject(LLViewerObject *objp)
 
 void LLHUDEffectSpiral::render()
 {
-	if (!mSourceObject.isNull() && mSourceObject->isDead())
-	{
-		markDead();
-		return;
-	}
-
-	if(!mTargetObject.isNull() && mTargetObject->isDead())
-	{
-		markDead();
-		return;
-	}
-
 	F32 time = mTimer.getElapsedTimeF32();
-	if (mKillTime < time)
+
+	if (!mSourceObject.isNull() && mSourceObject->isDead() ||
+		!mTargetObject.isNull() && mTargetObject->isDead() ||
+		mKillTime < time ||
+		!gSavedSettings.getBOOL("ShowSelectionBeam"))
 	{
 		markDead();
 		return;
diff --git a/indra/newview/llhudicon.cpp b/indra/newview/llhudicon.cpp
index f2ab2dab1108423173a16ff45efa709aee0ef37a..02a3576dff69700b74ad969b59f95d57f1c32361 100644
--- a/indra/newview/llhudicon.cpp
+++ b/indra/newview/llhudicon.cpp
@@ -15,6 +15,7 @@
 #include "llviewerobject.h"
 #include "lldrawable.h"
 #include "llviewercamera.h"
+#include "llviewerimage.h"
 #include "llviewerwindow.h"
 
 //-----------------------------------------------------------------------------
diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp
index f4280aae9c7753c1b2894b7a84b631518e2f3b34..f090cea5f209650a23fe94c4624179b505fcf447 100644
--- a/indra/newview/llinventorybridge.cpp
+++ b/indra/newview/llinventorybridge.cpp
@@ -3187,7 +3187,7 @@ void rez_attachment(LLViewerInventoryItem* item, LLViewerJointAttachment* attach
 	rez_action->mItemID = item->getUUID();
 	rez_action->mAttachPt = gAgent.getAvatarObject()->mAttachmentPoints.reverseLookup(attachment);
 
-	if (attachment && attachment->getObject(0))
+	if (attachment && attachment->getObject())
 	{
 		gViewerWindow->alertXml("ReplaceAttachment", confirm_replace_attachment_rez, (void*)rez_action);
 	}
diff --git a/indra/newview/lljoystickbutton.h b/indra/newview/lljoystickbutton.h
index 8b0a5665d428ee16edb539e35e97e5af0d911f2c..8e0668ad8f131ac66f2e98f1449b277da0e8ae3b 100644
--- a/indra/newview/lljoystickbutton.h
+++ b/indra/newview/lljoystickbutton.h
@@ -11,6 +11,7 @@
 
 #include "llbutton.h"
 #include "llcoord.h"
+#include "llviewerimage.h"
 
 typedef enum e_joystick_quadrant
 {
diff --git a/indra/newview/llmaniprotate.cpp b/indra/newview/llmaniprotate.cpp
index e9a6b1d1baed39326a544dfee13b8c33da26e546..5a52fc5b0a0accb2bf0343bf46da11309ce08269 100644
--- a/indra/newview/llmaniprotate.cpp
+++ b/indra/newview/llmaniprotate.cpp
@@ -445,11 +445,7 @@ BOOL LLManipRotate::handleMouseUp(S32 x, S32 y, MASK mask)
 	mManipPart = LL_NO_PART;
 
 	// Might have missed last update due to timing.
-	if (mSendUpdateOnMouseUp)
-	{
-		gSelectMgr->sendMultipleUpdate( UPD_ROTATION | UPD_POSITION );
-		mSendUpdateOnMouseUp = FALSE;
-	}
+	gSelectMgr->sendMultipleUpdate( UPD_ROTATION | UPD_POSITION );
 	gSelectMgr->enableSilhouette(TRUE);
 	//gAgent.setObjectTracking(gSavedSettings.getBOOL("TrackFocusObject"));
 
@@ -505,12 +501,6 @@ extern U32 gFrameCount;
 // Freeform rotation
 void LLManipRotate::drag( S32 x, S32 y )
 {
-	static LLTimer	update_timer;
-	F32 elapsed_time = update_timer.getElapsedTimeF32();
-	const F32 UPDATE_DELAY = 0.1f;						//  min time between transmitted updates
-	BOOL send_rotation_update = FALSE;
-	BOOL send_position_update = FALSE;
-
 	if( !updateVisiblity() )
 	{
 		return;
@@ -530,7 +520,6 @@ void LLManipRotate::drag( S32 x, S32 y )
 
 	LLViewerObject* object;
 	LLSelectNode* selectNode;
-	BOOL using_linked_selection = gSavedSettings.getBOOL("SelectLinkedSet");
 
 	for( selectNode = mObjectSelection->getFirstNode(); selectNode != NULL; selectNode = mObjectSelection->getNextNode() )
 	{
@@ -577,12 +566,6 @@ void LLManipRotate::drag( S32 x, S32 y )
 				rebuild(object);
 			}
 
-			// don't send updates all the time for sub-objects
-			if (using_linked_selection && object->getRenderRotation() != new_rot)
-			{
-				send_rotation_update = TRUE;
-			}
-
 			// for individually selected roots, we need to counterrotate all the children
 			if (object->isRootEdit() && selectNode->mIndividualSelection)
 			{
@@ -674,11 +657,6 @@ void LLManipRotate::drag( S32 x, S32 y )
 				}
 			}
 
-			if (using_linked_selection && object->getPositionAgent() != new_position)
-			{
-				send_position_update = TRUE;
-			}
-
 			// for individually selected roots, we need to counter-translate all unselected children
 			if (object->isRootEdit() && selectNode->mIndividualSelection)
 			{
@@ -709,27 +687,19 @@ void LLManipRotate::drag( S32 x, S32 y )
 		}
 	}
 
-	if ((send_position_update || send_rotation_update) && (elapsed_time > UPDATE_DELAY))
+	// store changes to override updates
+	for (LLSelectNode* selectNode = gSelectMgr->getSelection()->getFirstNode();
+		 selectNode != NULL;
+		 selectNode = gSelectMgr->getSelection()->getNextNode())
 	{
-		U32 flag = UPD_NONE;
-		if (send_rotation_update) 
-		{
-			flag |= UPD_ROTATION;
-		}
-		if (send_position_update) 
+		LLViewerObject*cur = selectNode->getObject();
+		if( cur->permModify() && cur->permMove() && !cur->isAvatar())
 		{
-			flag |= UPD_POSITION;
+			selectNode->mLastRotation = cur->getRotation();
+			selectNode->mLastPositionLocal = cur->getPosition();
 		}
+	}	
 
-		gSelectMgr->sendMultipleUpdate( flag );
-		update_timer.reset();
-		mSendUpdateOnMouseUp = FALSE;
-	}
-	else
-	{
-		mSendUpdateOnMouseUp = TRUE;
-	}
-	
 	gSelectMgr->updateSelectionCenter();
 
 	// RN: just clear focus so camera doesn't follow spurious object updates
diff --git a/indra/newview/llmanipscale.cpp b/indra/newview/llmanipscale.cpp
index cec8ff0b13c1f0bab301b808602a3435b085b093..56a4352e221354c16b61f5392b85e2c3f34c992a 100644
--- a/indra/newview/llmanipscale.cpp
+++ b/indra/newview/llmanipscale.cpp
@@ -360,15 +360,27 @@ BOOL LLManipScale::handleMouseUp(S32 x, S32 y, MASK mask)
 	// first, perform normal processing in case this was a quick-click
 	handleHover(x, y, mask);
 
+	if( (LL_FACE_MIN <= (S32)mManipPart) 
+		&& ((S32)mManipPart <= LL_FACE_MAX) )
+	{
+		sendUpdates(TRUE,TRUE,FALSE);
+	}
+	else
+	if( (LL_CORNER_MIN <= (S32)mManipPart) 
+		&& ((S32)mManipPart <= LL_CORNER_MAX) )
+	{
+		sendUpdates(TRUE,TRUE,TRUE);
+	}
+	
+	//send texture update
+	gSelectMgr->adjustTexturesByScale(TRUE, getStretchTextures());
+	
 	gSelectMgr->enableSilhouette(TRUE);
 	mManipPart = LL_NO_PART;
 
 	// Might have missed last update due to UPDATE_DELAY timing
-	if (mSendUpdateOnMouseUp)
-	{
-		gSelectMgr->sendMultipleUpdate( mLastUpdateFlags );
-	}
-		
+	gSelectMgr->sendMultipleUpdate( mLastUpdateFlags );
+	
 	//gAgent.setObjectTracking(gSavedSettings.getBOOL("TrackFocusObject"));
 	gSelectMgr->saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK);
 	return LLManip::handleMouseUp(x, y, mask);
@@ -398,7 +410,7 @@ BOOL LLManipScale::handleHover(S32 x, S32 y, MASK mask)
 	}
 	
 	// Patch up textures, if possible.
-	gSelectMgr->adjustTexturesByScale(TRUE, getStretchTextures());
+	gSelectMgr->adjustTexturesByScale(FALSE, getStretchTextures());
 
 	gViewerWindow->getWindow()->setCursor(UI_CURSOR_TOOLSCALE);
 	return TRUE;
@@ -789,8 +801,21 @@ void LLManipScale::drag( S32 x, S32 y )
 		dragCorner( x, y );
 	}
 	
-	//gAgent.setObjectTracking(FALSE);
-	gAgent.clearFocusObject();
+	// store changes to override updates
+	for (LLSelectNode* selectNode = gSelectMgr->getSelection()->getFirstNode();
+		 selectNode != NULL;
+		 selectNode = gSelectMgr->getSelection()->getNextNode())
+	{
+		LLViewerObject*cur = selectNode->getObject();
+		if( cur->permModify() && cur->permMove() && !cur->isAvatar())
+		{
+			selectNode->mLastScale = cur->getScale();
+			selectNode->mLastPositionLocal = cur->getPosition();
+		}
+	}	
+
+	gSelectMgr->updateSelectionCenter();
+    gAgent.clearFocusObject();
 }
 
 // Scale around the 
@@ -801,10 +826,7 @@ void LLManipScale::dragCorner( S32 x, S32 y )
 	// Suppress scale if mouse hasn't moved.
 	if (x == mLastMouseX && y == mLastMouseY)
 	{
-		if (mSendUpdateOnMouseUp)
-		{
-			sendUpdates(TRUE,TRUE,TRUE);
-		}
+	//	sendUpdates(TRUE,TRUE,TRUE);
 		return;
 	}
 
@@ -1003,11 +1025,8 @@ void LLManipScale::dragCorner( S32 x, S32 y )
 			if (!selectNode->mIndividualSelection)
 			{
 				cur->setPosition(selectNode->mSavedPositionLocal * scale_factor);
-				continue;
 			}
 
-			LLVector3d new_pos_global = drag_global + (selectNode->mSavedPositionGlobal - drag_global) * scale_factor;
-			cur->setPositionAbsoluteGlobal( new_pos_global );
 			rebuild(cur);
 		}
 	}
@@ -1015,8 +1034,6 @@ void LLManipScale::dragCorner( S32 x, S32 y )
 	
 
 	mDragPointGlobal = drag_point_global;
-
-	sendUpdates( TRUE, TRUE, TRUE );
 }
 
 	
@@ -1025,10 +1042,7 @@ void LLManipScale::dragFace( S32 x, S32 y )
 	// Suppress scale if mouse hasn't moved.
 	if (x == mLastMouseX && y == mLastMouseY)
 	{
-		if (mSendUpdateOnMouseUp)
-		{
-			sendUpdates(TRUE,TRUE,FALSE);
-		}
+	//	sendUpdates(TRUE,TRUE,FALSE);
 		return;
 	}
 
@@ -1162,8 +1176,6 @@ void LLManipScale::dragFace( S32 x, S32 y )
 	send_scale_update = TRUE;
 
 	mDragPointGlobal = drag_point_global;
-
-	sendUpdates( send_position_update, send_scale_update );
 }
 
 void LLManipScale::sendUpdates( BOOL send_position_update, BOOL send_scale_update, BOOL corner )
@@ -1198,8 +1210,6 @@ void LLManipScale::sendUpdates( BOOL send_position_update, BOOL send_scale_updat
 		{
 			mSendUpdateOnMouseUp = TRUE;
 		}
-
-		gSelectMgr->updateSelectionCenter();
 		dialog_refresh_all();
 	}
 }
diff --git a/indra/newview/llmaniptranslate.cpp b/indra/newview/llmaniptranslate.cpp
index f308eb9f5c0ffc7c24819b472db2fbbb26a85834..93ae74db22efb987dc9782be308b75e108a40bce 100644
--- a/indra/newview/llmaniptranslate.cpp
+++ b/indra/newview/llmaniptranslate.cpp
@@ -40,6 +40,7 @@
 #include "llworld.h"
 #include "viewer.h"
 #include "llui.h"
+#include "pipeline.h"
 
 const S32 NUM_AXES = 3;
 const S32 MOUSE_DRAG_SLOP = 2;       // pixels
@@ -404,6 +405,8 @@ BOOL LLManipTranslate::handleHover(S32 x, S32 y, MASK mask)
 		}
 	}
 
+	LLViewerObject	*object;
+
 	// Suppress processing if mouse hasn't actually moved.
 	// This may cause problems if the camera moves outside of the
 	// rotation above.
@@ -450,7 +453,6 @@ BOOL LLManipTranslate::handleHover(S32 x, S32 y, MASK mask)
 
 	LLVector3		axis_f;
 	LLVector3d		axis_d;
-	LLViewerObject	*object;
 
 	// pick the first object to constrain to grid w/ common origin
 	// this is so we don't screw up groups
@@ -631,7 +633,7 @@ BOOL LLManipTranslate::handleHover(S32 x, S32 y, MASK mask)
 		selectNode = mObjectSelection->getNextNode() )
 	{
 		object = selectNode->getObject();
-
+		
 		// Only apply motion to root objects and objects selected
 		// as "individual".
 		if (!object->isRootEdit() && !selectNode->mIndividualSelection)
@@ -762,27 +764,12 @@ BOOL LLManipTranslate::handleHover(S32 x, S32 y, MASK mask)
 					send_update = TRUE;
 				}
 			}
+			selectNode->mLastPositionLocal  = object->getPosition();
 		}
 	}
 
-	// Handle throttling to 10 updates per second.
-	F32 elapsed_time = mUpdateTimer.getElapsedTimeF32();
-	const F32 UPDATE_DELAY = 0.1f;						//  min time between transmitted updates
-	if (send_update && (elapsed_time > UPDATE_DELAY))
-	{
-		gSelectMgr->sendMultipleUpdate(UPD_POSITION);
-		mUpdateTimer.reset();
-		mSendUpdateOnMouseUp = FALSE;
-	}
-	else
-	{
-		// ...suppressed update
-		mSendUpdateOnMouseUp = TRUE;
-	}
-
 	gSelectMgr->updateSelectionCenter();
 	gAgent.clearFocusObject();
-	//gAgent.setObjectTracking(FALSE);
 	dialog_refresh_all();		// ??? is this necessary?
 
 	lldebugst(LLERR_USER_INPUT) << "hover handled by LLManipTranslate (active)" << llendl;
@@ -1039,17 +1026,8 @@ BOOL LLManipTranslate::handleMouseUp(S32 x, S32 y, MASK mask)
 	gSelectMgr->enableSilhouette(TRUE);
 
 	// Might have missed last update due to UPDATE_DELAY timing.
-	if (mSendUpdateOnMouseUp)
-	{
-		gSelectMgr->sendMultipleUpdate( UPD_POSITION );
-		mSendUpdateOnMouseUp = FALSE;
-	}
-
-//	if (mCopyMadeThisDrag)
-//	{
-//		gSelectMgr->clearGridObjects();
-//	}
-
+	gSelectMgr->sendMultipleUpdate( UPD_POSITION );
+	
 	mInSnapRegime = FALSE;
 	gSelectMgr->saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK);
 	//gAgent.setObjectTracking(gSavedSettings.getBOOL("TrackFocusObject"));
@@ -1122,6 +1100,30 @@ void LLManipTranslate::renderSnapGuides()
 	//pick appropriate projection plane for snap rulers according to relative camera position
 	if (mManipPart >= LL_X_ARROW && mManipPart <= LL_Z_ARROW)
 	{
+		LLVector3 normal;
+		LLColor4 inner_color;
+		LLManip::EManipPart temp_manip = mManipPart;
+		switch (mManipPart)
+		{
+		case LL_X_ARROW:
+			normal.setVec(1,0,0);
+			inner_color.setVec(0,1,1,line_alpha);
+			mManipPart = LL_YZ_PLANE;
+			break;
+		case LL_Y_ARROW:
+			normal.setVec(0,1,0);
+			inner_color.setVec(1,0,1,line_alpha);
+			mManipPart = LL_XZ_PLANE;
+			break;
+		case LL_Z_ARROW:
+			normal.setVec(0,0,1);
+			inner_color.setVec(1,1,0,line_alpha);
+			mManipPart = LL_XY_PLANE;
+			break;
+		}
+
+		highlightIntersection(normal, selection_center, grid_rotation, inner_color);
+		mManipPart = temp_manip;
 		getManipAxis(first_object, mManipPart, translate_axis);
 
 		LLVector3 at_axis_abs;
@@ -1438,18 +1440,13 @@ void LLManipTranslate::renderSnapGuides()
 		// render gridlines for planar snapping
 
 		F32 u = 0, v = 0;
-		glPushMatrix();
-
-		F32 x,y,z,angle_radians;
-		grid_rotation.getAngleAxis(&angle_radians, &x, &y, &z);
-		glTranslatef(selection_center.mV[VX], selection_center.mV[VY], selection_center.mV[VZ]);
-		glRotatef(angle_radians * RAD_TO_DEG, x, y, z);
-
+        LLColor4 inner_color;
+		LLVector3 normal;
 		LLVector3 grid_center = selection_center - grid_origin;
-		grid_center *= ~grid_rotation;
-		
 		F32 usc = 1;
 		F32 vsc = 1;
+		
+		grid_center *= ~grid_rotation;
 
 		switch (mManipPart)
 		{
@@ -1458,23 +1455,38 @@ void LLManipTranslate::renderSnapGuides()
 			v = grid_center.mV[VZ];
 			usc = grid_scale.mV[VY];
 			vsc = grid_scale.mV[VZ];
+			inner_color.setVec(0,1,1,line_alpha);
+			normal.setVec(1,0,0);
 			break;
 		case LL_XZ_PLANE:
 			u = grid_center.mV[VX];
 			v = grid_center.mV[VZ];
 			usc = grid_scale.mV[VX];
 			vsc = grid_scale.mV[VZ];
+			inner_color.setVec(1,0,1,line_alpha);
+			normal.setVec(0,1,0);
 			break;
 		case LL_XY_PLANE:
 			u = grid_center.mV[VX];
 			v = grid_center.mV[VY];
 			usc = grid_scale.mV[VX];
 			vsc = grid_scale.mV[VY];
+			inner_color.setVec(1,1,0,line_alpha);
+			normal.setVec(0,0,1);
 			break;
 		default:
 			break;
 		}
 
+		highlightIntersection(normal, selection_center, grid_rotation, inner_color);
+
+		glPushMatrix();
+
+		F32 x,y,z,angle_radians;
+		grid_rotation.getAngleAxis(&angle_radians, &x, &y, &z);
+		glTranslatef(selection_center.mV[VX], selection_center.mV[VY], selection_center.mV[VZ]);
+		glRotatef(angle_radians * RAD_TO_DEG, x, y, z);
+		
 		F32 sz = mGridSizeMeters;
 		F32 tiles = sz;
 		glMatrixMode(GL_TEXTURE);
@@ -1591,6 +1603,104 @@ void LLManipTranslate::renderGrid(F32 x, F32 y, F32 size, F32 r, F32 g, F32 b, F
 	
 }
 
+void LLManipTranslate::highlightIntersection(LLVector3 normal, 
+											 LLVector3 selection_center, 
+											 LLQuaternion grid_rotation, 
+											 LLColor4 inner_color)
+{
+	if (!gSavedSettings.getBOOL("GridCrossSections"))
+	{
+		return;
+	}
+	
+	U32 types[] = { LLRenderPass::PASS_SIMPLE, LLRenderPass::PASS_ALPHA, LLRenderPass::PASS_FULLBRIGHT };
+
+	GLuint stencil_mask = 0xFFFFFFFF;
+	//stencil in volumes
+	{
+		glStencilMask(stencil_mask);
+		glClearStencil(1);
+		glClear(GL_STENCIL_BUFFER_BIT);
+		LLGLEnable cull_face(GL_CULL_FACE);
+		LLGLEnable stencil(GL_STENCIL_TEST);
+		LLGLDepthTest depth (GL_TRUE, GL_FALSE, GL_ALWAYS);
+		glStencilFunc(GL_ALWAYS, 0, stencil_mask);
+		glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+        LLGLDisable tex(GL_TEXTURE_2D);
+		glColor4f(1,1,1,1);
+
+		//setup clip plane
+		normal = normal * grid_rotation;
+		if (normal * (gCamera->getOrigin()-selection_center) < 0)
+		{
+			normal = -normal;
+		}
+		F32 d = -(selection_center * normal);
+		F64 plane[] = { normal.mV[0], normal.mV[1], normal.mV[2], d };
+		LLGLEnable clip(GL_CLIP_PLANE0);
+		glClipPlane(GL_CLIP_PLANE0, plane);
+
+		BOOL particles = gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_PARTICLES);
+		BOOL clouds = gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_CLOUDS);
+		
+		if (particles)
+		{
+			LLPipeline::toggleRenderType(LLPipeline::RENDER_TYPE_PARTICLES);
+		}
+		if (clouds)
+		{
+			LLPipeline::toggleRenderType(LLPipeline::RENDER_TYPE_CLOUDS);
+		}
+		
+		//stencil in volumes
+		glStencilOp(GL_INCR, GL_INCR, GL_INCR);
+		glCullFace(GL_FRONT);
+		for (U32 i = 0; i < 3; i++)
+		{
+			gPipeline.renderObjects(types[i], LLVertexBuffer::MAP_VERTEX, FALSE);
+		}
+
+		glStencilOp(GL_DECR, GL_DECR, GL_DECR);
+		glCullFace(GL_BACK);
+		for (U32 i = 0; i < 3; i++)
+		{
+			gPipeline.renderObjects(types[i], LLVertexBuffer::MAP_VERTEX, FALSE);
+		}
+		
+		if (particles)
+		{
+			LLPipeline::toggleRenderType(LLPipeline::RENDER_TYPE_PARTICLES);
+		}
+		if (clouds)
+		{
+			LLPipeline::toggleRenderType(LLPipeline::RENDER_TYPE_CLOUDS);
+		}
+
+		glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+	}
+
+	glPushMatrix();
+
+	F32 x,y,z,angle_radians;
+	grid_rotation.getAngleAxis(&angle_radians, &x, &y, &z);
+	glTranslatef(selection_center.mV[VX], selection_center.mV[VY], selection_center.mV[VZ]);
+	glRotatef(angle_radians * RAD_TO_DEG, x, y, z);
+	
+	F32 sz = mGridSizeMeters;
+	F32 tiles = sz;
+
+	//draw volume/plane intersections
+	{
+		LLGLDisable tex(GL_TEXTURE_2D);
+		LLGLDepthTest depth(GL_FALSE);
+		LLGLEnable stencil(GL_STENCIL_TEST);
+		glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
+		glStencilFunc(GL_EQUAL, 0, stencil_mask);
+		renderGrid(0,0,tiles,inner_color.mV[0], inner_color.mV[1], inner_color.mV[2], 0.25f);
+	}
+
+	glPopMatrix();
+}
 
 void LLManipTranslate::renderText()
 {
diff --git a/indra/newview/llmaniptranslate.h b/indra/newview/llmaniptranslate.h
index 5a93c0a24fef2db12a47484f4a314f2c7edc41b8..6a317cabcdd353894058a6df9821bdce926969c7 100644
--- a/indra/newview/llmaniptranslate.h
+++ b/indra/newview/llmaniptranslate.h
@@ -58,6 +58,10 @@ protected:
 	void		renderSnapGuides();
 	void		renderGrid(F32 x, F32 y, F32 size, F32 r, F32 g, F32 b, F32 a);
 	void		renderGridVert(F32 x_trans, F32 y_trans, F32 r, F32 g, F32 b, F32 alpha);
+	void		highlightIntersection(LLVector3 normal, 
+									 LLVector3 selection_center, 
+									 LLQuaternion grid_rotation, 
+									 LLColor4 inner_color);
 	F32			getMinGridScale();
 
 private:
diff --git a/indra/newview/llmemoryview.cpp b/indra/newview/llmemoryview.cpp
index 5edb373cbb2932dbda2a9919e643d303b8eff83d..7fd2572e085eee6dc3b535c4f6cb69493cdbc28f 100644
--- a/indra/newview/llmemoryview.cpp
+++ b/indra/newview/llmemoryview.cpp
@@ -16,6 +16,7 @@
 #include "llgl.h"
 #include "llmath.h"
 #include "llfontgl.h"
+#include "llmemtype.h"
 
 #include "viewer.h"
 #include "llui.h"
@@ -95,6 +96,7 @@ static struct mtv_display_info mtv_display_table[] =
 	{ LLMemType::MTYPE_PIPELINE,		"Pipeline",			&LLColor4::green3 },
 	{ LLMemType::MTYPE_PARTICLES,		"Particles",		&LLColor4::green4 },
 	{ LLMemType::MTYPE_SPACE_PARTITION,	"Space Partition",	&LLColor4::blue2 },
+	{ LLMemType::MTYPE_VERTEX_DATA,		"Vertex Buffer",	&LLColor4::blue3 },
 	{ LLMemType::MTYPE_AVATAR,			"Avatar",			&LLColor4::purple1 },
 	{ LLMemType::MTYPE_REGIONS,			"Regions",			&LLColor4::blue1 },
  	{ LLMemType::MTYPE_TEMP1,			"Temp1",			&LLColor4::red1 },
diff --git a/indra/newview/llpanelpick.cpp b/indra/newview/llpanelpick.cpp
index ab2a298a06c435c54257c78755e01aa95f81ec34..c13d76886f9cc518da98fb9445ea865c08f9fb8c 100644
--- a/indra/newview/llpanelpick.cpp
+++ b/indra/newview/llpanelpick.cpp
@@ -487,8 +487,11 @@ void LLPanelPick::onCommitAny(LLUICtrl* ctrl, void* data)
 		}
 		else
 		{*/
-			LLTabContainerVertical* tab = (LLTabContainerVertical*)self->getParent();
+		LLTabContainerVertical* tab = (LLTabContainerVertical*)self->getParent();
+		if (tab)
+		{
 			if(tab) tab->setCurrentTabName(self->mNameEditor->getText());
+		}
 		//}
 	}
 }
diff --git a/indra/newview/llpolymesh.cpp b/indra/newview/llpolymesh.cpp
index 76769c6c7ca3b64bdcaf78b0c37cac32133745ef..e818170ed2d7702e3ba4732bb4aee9a4c8c8eb13 100644
--- a/indra/newview/llpolymesh.cpp
+++ b/indra/newview/llpolymesh.cpp
@@ -1056,10 +1056,10 @@ BOOL LLPolySkeletalDistortion::setInfo(LLPolySkeletalDistortionInfo *info)
 		mJointScales[joint] = bone_info->mScaleDeformation;
 
 		// apply to children that need to inherit it
-		for (	LLViewerJoint *child_joint = (LLViewerJoint *)joint->mChildren.getFirstData();
-				child_joint != NULL;
-				child_joint = (LLViewerJoint *)joint->mChildren.getNextData() )
+		for (LLJoint::child_list_t::iterator iter = joint->mChildren.begin();
+			 iter != joint->mChildren.end(); ++iter)
 		{
+			LLViewerJoint* child_joint = (LLViewerJoint*)(*iter);
 			if (child_joint->inheritScale())
 			{
 				LLVector3 childDeformation = LLVector3(child_joint->getScale());
diff --git a/indra/newview/llpolymesh.h b/indra/newview/llpolymesh.h
index cefda9228856f8101ecc7560de8e58c9c5d1b163..2874f11798034e756a998872a42eb2e184ee2b9e 100644
--- a/indra/newview/llpolymesh.h
+++ b/indra/newview/llpolymesh.h
@@ -305,6 +305,7 @@ public:
 
 	U32				mFaceVertexOffset;
 	U32				mFaceVertexCount;
+	U32				mFaceIndexOffset;
 	U32				mFaceIndexCount;
 	U32				mCurVertexCount;
 private:
diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp
index f6b9f0e0182d9c8401f4ad31ec20bc80f4f6c233..479f1b1812fd6b14de6607eeb7d435b14187617b 100644
--- a/indra/newview/llpreviewscript.cpp
+++ b/indra/newview/llpreviewscript.cpp
@@ -1453,9 +1453,9 @@ void LLLiveLSLEditor::onLoadComplete(LLVFS *vfs, const LLUUID& asset_id,
 
 	if( LLLiveLSLEditor::sInstances.checkData(*xored_id) )
 	{
+		instance = LLLiveLSLEditor::sInstances[*xored_id];
 		if( LL_ERR_NOERR == status )
 		{
-			instance = LLLiveLSLEditor::sInstances[*xored_id];
 			instance->loadScriptText(vfs, asset_id, type);
 			instance->mAssetStatus = PREVIEW_ASSET_LOADED;
 		}
diff --git a/indra/newview/llpreviewtexture.cpp b/indra/newview/llpreviewtexture.cpp
index 95973a102e74e22aa3ccde7fe2ea28d50358809c..4b7be3701bd25171feb53bfcab06b89a5e01ed9b 100644
--- a/indra/newview/llpreviewtexture.cpp
+++ b/indra/newview/llpreviewtexture.cpp
@@ -10,19 +10,20 @@
 
 #include "llpreviewtexture.h"
 
-#include "llviewerimage.h"
-#include "llviewerimagelist.h"
-#include "llresmgr.h"
 #include "llagent.h"
 #include "llbutton.h"
-#include "llui.h"
+#include "llfilepicker.h"
+#include "llimagetga.h"
 #include "llinventoryview.h"
 #include "llinventory.h"
-#include "llviewerwindow.h"
+#include "llresmgr.h"
 #include "lltextbox.h"
-#include "llimagetga.h"
-#include "llfilepicker.h"
+#include "lltextureview.h"
+#include "llui.h"
+#include "llviewerimage.h"
+#include "llviewerimagelist.h"
 #include "llvieweruictrlfactory.h"
+#include "llviewerwindow.h"
 #include "lllineeditor.h"
 
 const S32 PREVIEW_TEXTURE_MIN_WIDTH = 300;
@@ -167,6 +168,105 @@ void LLPreviewTexture::init()
 	}
 }
 
+void LLPreviewTexture::draw()
+{
+	if( getVisible() )
+	{
+		updateAspectRatio();
+
+		LLPreview::draw();
+
+		if (!mMinimized)
+		{
+			LLGLSUIDefault gls_ui;
+			LLGLSNoTexture gls_notex;
+			
+			const LLRect& border = mClientRect;
+			LLRect interior = mClientRect;
+			interior.stretch( -PREVIEW_BORDER_WIDTH );
+
+			// ...border
+			gl_rect_2d( border, LLColor4(0.f, 0.f, 0.f, 1.f));
+			gl_rect_2d_checkerboard( interior );
+
+			if ( mImage.notNull() )
+			{
+				LLGLSTexture gls_no_texture;
+				// Draw the texture
+				glColor3f( 1.f, 1.f, 1.f );
+				gl_draw_scaled_image(interior.mLeft,
+									interior.mBottom,
+									interior.getWidth(),
+									interior.getHeight(),
+									mImage);
+
+				// Pump the texture priority
+				F32 pixel_area = mLoadingFullImage ? (F32)MAX_IMAGE_AREA  : (F32)(interior.getWidth() * interior.getHeight() );
+				mImage->addTextureStats( pixel_area );
+
+				// Don't bother decoding more than we can display, unless
+				// we're loading the full image.
+				if (!mLoadingFullImage)
+				{
+					S32 int_width = interior.getWidth();
+					S32 int_height = interior.getHeight();
+					mImage->setKnownDrawSize(int_width, int_height);
+				}
+				else
+				{
+					// Don't use this feature
+					mImage->setKnownDrawSize(0, 0);
+				}
+
+				if( mLoadingFullImage )
+				{
+					LLFontGL::sSansSerif->renderUTF8("Receiving:", 0,
+						interior.mLeft + 4, 
+						interior.mBottom + 4,
+						LLColor4::white, LLFontGL::LEFT, LLFontGL::BOTTOM,
+						LLFontGL::DROP_SHADOW);
+					
+					F32 data_progress = mImage->mDownloadProgress;
+					
+					// Draw the progress bar.
+					const S32 BAR_HEIGHT = 12;
+					const S32 BAR_LEFT_PAD = 80;
+					S32 left = interior.mLeft + 4 + BAR_LEFT_PAD;
+					S32 bar_width = mRect.getWidth() - left - RESIZE_HANDLE_WIDTH - 2;
+					S32 top = interior.mBottom + 4 + BAR_HEIGHT;
+					S32 right = left + bar_width;
+					S32 bottom = top - BAR_HEIGHT;
+
+					LLColor4 background_color(0.f, 0.f, 0.f, 0.75f);
+					LLColor4 decoded_color(0.f, 1.f, 0.f, 1.0f);
+					LLColor4 downloaded_color(0.f, 0.5f, 0.f, 1.0f);
+
+					gl_rect_2d(left, top, right, bottom, background_color);
+
+					if (data_progress > 0.0f)
+					{
+						// Downloaded bytes
+						right = left + llfloor(data_progress * (F32)bar_width);
+						if (right > left)
+						{
+							gl_rect_2d(left, top, right, bottom, downloaded_color);
+						}
+					}
+				}
+				else
+				if( !mSavedFileTimer.hasExpired() )
+				{
+					LLFontGL::sSansSerif->renderUTF8("File Saved", 0,
+						interior.mLeft + 4,
+						interior.mBottom + 4,
+						LLColor4::white, LLFontGL::LEFT, LLFontGL::BOTTOM,
+						LLFontGL::DROP_SHADOW);
+				}
+			}
+		}
+	}
+}
+
 
 // virtual
 BOOL LLPreviewTexture::canSaveAs()
diff --git a/indra/newview/llprogressview.cpp b/indra/newview/llprogressview.cpp
index 2faa9ba35156a556efede514beba2ea71b5174a7..1172bd8cf0e263436cdf47400cf60d6cebdd37cc 100644
--- a/indra/newview/llprogressview.cpp
+++ b/indra/newview/llprogressview.cpp
@@ -20,8 +20,9 @@
 #include "llglheaders.h"
 
 #include "llagent.h"
-#include "llfocusmgr.h"
 #include "llbutton.h"
+#include "llfocusmgr.h"
+#include "llstartup.h"
 #include "llviewercontrol.h"
 #include "llviewerimagelist.h"
 #include "llviewerwindow.h"
@@ -29,9 +30,8 @@
 
 LLProgressView* LLProgressView::sInstance = NULL;
 
-LLPointer<LLImageGL> gStartImageGL = NULL;
-S32						gStartImageWidth = 1;
-S32						gStartImageHeight = 1;
+S32 gStartImageWidth = 1;
+S32 gStartImageHeight = 1;
 const F32 FADE_IN_TIME = 1.f;
 
 const LLString ANIMATION_FILENAME = "Login Sequence ";
diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp
index c4e166d68dc93a23c2af94fbbd0eea465a91113a..27cf29cee0a97860787e12ba6ead04721ff8a793 100644
--- a/indra/newview/llselectmgr.cpp
+++ b/indra/newview/llselectmgr.cpp
@@ -196,6 +196,34 @@ void LLSelectMgr::updateEffects()
 	}
 }
 
+void LLSelectMgr::overrideObjectUpdates()
+{
+	//override any position updates from simulator on objects being edited
+	LLSelectNode* selectNode;
+	for (selectNode = gSelectMgr->getSelection()->getFirstNode();
+		 selectNode != NULL;
+		 selectNode = gSelectMgr->getSelection()->getNextNode())
+	{
+		LLViewerObject* object = selectNode->getObject();
+		
+		if (object->permMove())
+		{
+			if (!selectNode->mLastPositionLocal.isExactlyZero())
+			{
+				object->setPosition(selectNode->mLastPositionLocal);
+			}
+			if (selectNode->mLastRotation != LLQuaternion())
+			{
+				object->setRotation(selectNode->mLastRotation);
+			}
+			if (!selectNode->mLastScale.isExactlyZero())
+			{
+				object->setScale(selectNode->mLastScale);
+			}
+		}
+	}
+}
+
 //-----------------------------------------------------------------------------
 // Select just the object, not any other group members.
 //-----------------------------------------------------------------------------
@@ -3520,7 +3548,8 @@ void LLSelectMgr::sendAttach(U8 attachment_point)
 
 	BOOL build_mode = gToolMgr->inEdit();
 	// Special case: Attach to default location for this object.
-	if (0 == attachment_point)
+	if (0 == attachment_point ||
+		gAgent.getAvatarObject()->mAttachmentPoints.getIfThere(attachment_point))
 	{
 		sendListToRegions(
 			"ObjectAttach",
@@ -3533,48 +3562,6 @@ void LLSelectMgr::sendAttach(U8 attachment_point)
 			deselectAll();
 		}
 	}
-	else
-	{
-		LLViewerJointAttachment* attachment = gAgent.getAvatarObject()->mAttachmentPoints.getIfThere(attachment_point);
-		if (attachment)
-		{
-			LLQuaternion object_world_rot = attach_object->getRenderRotation();
-			LLQuaternion attachment_pt__world_rot = attachment->getWorldRotation();
-			LLQuaternion local_rot = object_world_rot * ~attachment_pt__world_rot;
-
-			F32 x,y,z;
-			local_rot.getEulerAngles(&x, &y, &z);
-			// snap to nearest 90 degree rotation
-			// make sure all euler angles are positive
-			if (x < F_PI_BY_TWO) x += F_TWO_PI;
-			if (y < F_PI_BY_TWO) y += F_TWO_PI;
-			if (z < F_PI_BY_TWO) z += F_TWO_PI;
-
-			// add 45 degrees so that rounding down becomes rounding off
-			x += F_PI_BY_TWO / 2.f;
-			y += F_PI_BY_TWO / 2.f;
-			z += F_PI_BY_TWO / 2.f;
-			// round down to nearest multiple of 90 degrees
-			x -= fmodf(x, F_PI_BY_TWO);
-			y -= fmodf(y, F_PI_BY_TWO);
-			z -= fmodf(z, F_PI_BY_TWO);
-
-			// pass the requested rotation on to the simulator
-			local_rot.setQuat(x, y, z);
-			attach_object->setRotation(local_rot);
-
-			sendListToRegions(
-				"ObjectAttach",
-				packAgentIDAndSessionAndAttachment,
-				packObjectIDAndRotation,
-				&attachment_point,
-				SEND_ONLY_ROOTS );
-			if (!build_mode)
-			{
-				deselectAll();
-			}
-		}
-	}
 }
 
 void LLSelectMgr::sendDetach()
@@ -3764,7 +3751,10 @@ void LLSelectMgr::saveSelectedObjectTransform(EActionType action_type)
 			if (object->isRootEdit())
 			{
 				LLXform* parent_xform = object->mDrawable->getXform()->getParent();
-				selectNode->mSavedPositionGlobal = gAgent.getPosGlobalFromAgent((object->getPosition() * parent_xform->getWorldRotation()) + parent_xform->getWorldPosition());
+				if (parent_xform)
+				{
+					selectNode->mSavedPositionGlobal = gAgent.getPosGlobalFromAgent((object->getPosition() * parent_xform->getWorldRotation()) + parent_xform->getWorldPosition());
+				}
 			}
 			else
 			{
@@ -4065,6 +4055,17 @@ void LLSelectMgr::sendListToRegions(const LLString& message_name,
 	S32 packets_sent = 0;
 	S32 objects_in_this_packet = 0;
 
+
+	//clear update override data (allow next update through)
+	for (node = mSelectedObjects->getFirstNode();
+		 node;
+		 node = mSelectedObjects->getNextNode())
+	{
+		node->mLastPositionLocal.setVec(0,0,0);
+		node->mLastRotation = LLQuaternion();
+		node->mLastScale.setVec(0,0,0);
+	}
+
 	std::queue<LLSelectNode*> nodes_to_send;
 	
 	switch(send_type)
@@ -4583,7 +4584,7 @@ void LLSelectMgr::updateSilhouettes()
 					|| objectp->isChanged(LLXform::SILHOUETTE)
 					|| (objectp->getParent() && objectp->getParent()->isChanged(LLXform::SILHOUETTE)))
 				{
-					if (num_sils_genned++ < MAX_SILS_PER_FRAME && objectp->mDrawable->isVisible())
+					if (num_sils_genned++ < MAX_SILS_PER_FRAME)// && objectp->mDrawable->isVisible())
 					{
 						generateSilhouette(node, gCamera->getOrigin());
 						changed_objects.put(objectp);
@@ -4816,7 +4817,6 @@ void LLSelectMgr::renderSilhouettes(BOOL for_hud)
 	}
 	if (mSelectedObjects->getNumNodes())
 	{
-		glPushAttrib(GL_FOG_BIT);
 		LLUUID inspect_item_id = LLFloaterInspect::getSelectedUUID();
 		for (S32 pass = 0; pass < 2; pass++)
 		{
@@ -4848,7 +4848,6 @@ void LLSelectMgr::renderSilhouettes(BOOL for_hud)
 				}
 			}
 		}
-		glPopAttrib();
 	}
 
 	if (mHighlightedObjects->getNumNodes())
@@ -5266,12 +5265,7 @@ void LLSelectNode::renderOneSilhouette(const LLColor4 &color)
 	}
 
 	BOOL is_hud_object = objectp->isHUDAttachment();
-
-	if (!drawable->isVisible() && !is_hud_object)
-	{
-		return;
-	}
-
+	
 	if (mSilhouetteVertices.size() == 0 || mSilhouetteNormals.size() != mSilhouetteVertices.size())
 	{
 		return;
@@ -5302,7 +5296,7 @@ void LLSelectNode::renderOneSilhouette(const LLColor4 &color)
 		else
 		{
 			LLVector3 view_vector = gCamera->getOrigin() - objectp->getRenderPosition();
-			silhouette_thickness = drawable->mDistanceWRTCamera * LLSelectMgr::sHighlightThickness * (gCamera->getView() / gCamera->getDefaultFOV());
+			silhouette_thickness = view_vector.magVec() * LLSelectMgr::sHighlightThickness * (gCamera->getView() / gCamera->getDefaultFOV());
 		}		
 		F32 animationTime = (F32)LLFrameTimer::getElapsedSeconds();
 
@@ -5328,7 +5322,6 @@ void LLSelectNode::renderOneSilhouette(const LLColor4 &color)
 				S32 i = 0;
 				for (S32 seg_num = 0; seg_num < (S32)mSilhouetteSegments.size(); seg_num++)
 				{
-// 					S32 first_i = i;
 					for(; i < mSilhouetteSegments[seg_num]; i++)
 					{
 						u_coord += u_divisor * LLSelectMgr::sHighlightUScale;
@@ -5337,11 +5330,6 @@ void LLSelectNode::renderOneSilhouette(const LLColor4 &color)
 						glTexCoord2f( u_coord, v_coord );
 						glVertex3fv( mSilhouetteVertices[i].mV );
 					}
-
-					/*u_coord += u_divisor * LLSelectMgr::sHighlightUScale;
-					glColor4f(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE], 0.4f);
-					glTexCoord2f( u_coord, v_coord );
-					glVertex3fv( mSilhouetteVertices[first_i].mV );*/
 				}
 			}
             glEnd();
@@ -5349,7 +5337,6 @@ void LLSelectNode::renderOneSilhouette(const LLColor4 &color)
 		}
 
 		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-		//glAlphaFunc(GL_GREATER, LLSelectMgr::sHighlightAlphaTest);
 		glBegin(GL_TRIANGLES);
 		{
 			S32 i = 0;
@@ -5468,6 +5455,10 @@ void LLSelectMgr::updateSelectionCenter()
 	const F32 MOVE_SELECTION_THRESHOLD = 1.f;		//  Movement threshold in meters for updating selection
 													//  center (tractor beam)
 
+	//override any object updates received
+	//for selected objects
+	gSelectMgr->overrideObjectUpdates();
+
 	LLViewerObject* object = mSelectedObjects->getFirstObject();
 	if (!object)
 	{
diff --git a/indra/newview/llselectmgr.h b/indra/newview/llselectmgr.h
index 2b1568fb458d3d29f1f1bc1aead1b3138d85f0e6..c0ddad41ebcf7442ec65d1abef3b630a4f3d0c13 100644
--- a/indra/newview/llselectmgr.h
+++ b/indra/newview/llselectmgr.h
@@ -210,6 +210,7 @@ public:
 	virtual BOOL canDuplicate();
 
 	void updateEffects(); // Update HUD effects
+	void overrideObjectUpdates();
 
 	void setForceSelection(BOOL force) { mForceSelection = force; }
 
@@ -580,9 +581,12 @@ public:
 	LLCategory		mCategory;
 	S16				mInventorySerial;
 	LLVector3		mSavedPositionLocal;	// for interactively modifying object position
+	LLVector3		mLastPositionLocal;
 	LLVector3d		mSavedPositionGlobal;	// for interactively modifying object position
 	LLVector3		mSavedScale;			// for interactively modifying object scale
+	LLVector3		mLastScale;
 	LLQuaternion	mSavedRotation;			// for interactively modifying object rotation
+	LLQuaternion	mLastRotation;
 	BOOL			mDuplicated;
 	LLVector3d		mDuplicatePos;
 	LLQuaternion	mDuplicateRot;
diff --git a/indra/newview/llsky.cpp b/indra/newview/llsky.cpp
index 022f02c57ef9b58b785801c433e7b42204ab344f..02f3855aceecbb6905e01cad15233c49b078e05a 100644
--- a/indra/newview/llsky.cpp
+++ b/indra/newview/llsky.cpp
@@ -347,7 +347,7 @@ void LLSky::updateCull()
 
 	if (mVOStarsp.notNull() && mVOStarsp->mDrawable.notNull())
 	{
-		gPipeline.markVisible(mVOStarsp->mDrawable);
+		gPipeline.markVisible(mVOStarsp->mDrawable, *gCamera);
 	}
 	else
 	{
@@ -372,10 +372,10 @@ void LLSky::updateSky()
 	}
 	if (mVOStarsp)
 	{
-		if (mVOStarsp->mDrawable)
-		{
-			gPipeline.markRebuild(mVOStarsp->mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);
-		}
+		//if (mVOStarsp->mDrawable)
+		//{
+		//	gPipeline.markRebuild(mVOStarsp->mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);
+		//}
 	}
 }
 
diff --git a/indra/newview/llspatialpartition.cpp b/indra/newview/llspatialpartition.cpp
index 8b5db02c8523b696638bf663ce252df1e94a6d2e..739d30bfe6838b18f9ef5a74c72d1e3315f1ba1a 100644
--- a/indra/newview/llspatialpartition.cpp
+++ b/indra/newview/llspatialpartition.cpp
@@ -10,25 +10,21 @@
 
 #include "llspatialpartition.h"
 
-#include "llglheaders.h"
-
+#include "llviewerwindow.h"
 #include "llviewerobjectlist.h"
 #include "llvovolume.h"
 #include "llviewercamera.h"
 #include "llface.h"
 #include "viewer.h"
-
+#include "llagent.h"
+#include "llviewerregion.h"
 #include "llcamera.h"
 #include "pipeline.h"
 
-static BOOL sIgnoreOcclusion = TRUE;
 static GLuint sBoxList = 0;
 
-const S32 SG_LOD_SWITCH_STAGGER = 4;
-const F32 SG_MAX_OBJ_RAD = 1.f;
-const F32 SG_OCCLUSION_FUDGE = 1.1f;
-const S32 SG_MOVE_PERIOD = 32;
-const S32 SG_LOD_PERIOD = 16;
+const F32 SG_OCCLUSION_FUDGE = 1.01f;
+//const S32 SG_LOD_PERIOD = 16;
 
 #define SG_DISCARD_TOLERANCE 0.25f
 
@@ -38,11 +34,12 @@ const S32 SG_LOD_PERIOD = 16;
 #define assert_octree_valid(x)
 #endif
 
+static U32 sZombieGroups = 0;
+
 static F32 sLastMaxTexPriority = 1.f;
 static F32 sCurMaxTexPriority = 1.f;
 
 //static counter for frame to switch LOD on
-S32 LLSpatialGroup::sLODSeed = 0;
 
 void sg_assert(BOOL expr)
 {
@@ -125,25 +122,22 @@ S32 LLSphereAABB(const LLVector3& center, const LLVector3& size, const LLVector3
 
 LLSpatialGroup::~LLSpatialGroup()
 {
-	LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
-	if (!safeToDelete())
+	if (isState(DEAD))
 	{
-#ifdef LL_RELEASE_FOR_DOWNLOAD
-		llwarns << "Spatial Group deleted while being tracked " << ((void*) mState) << llendl;
-#else
-		llerrs << "Spatial Group deleted while being tracked " << ((void*) mState) << llendl;
-#endif
+		sZombieGroups--;
 	}
 
-#if LL_OCTREE_PARANOIA_CHECK
-	for (U32 i = 0; i < mSpatialPartition->mOccludedList.size(); i++)
+	LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+	clearDrawMap();
+}
+
+void LLSpatialGroup::clearDrawMap()
+{
+	for (LLSpatialGroup::draw_map_t::iterator i = mDrawMap.begin(); i != mDrawMap.end(); ++i)
 	{
-		if (mSpatialPartition->mOccludedList[i] == this)
-		{
-			llerrs << "Spatial Group deleted while being tracked STATE ERROR " << ((void*) mState) << llendl;
-		}
+		std::for_each(i->second.begin(), i->second.end(), DeletePointer());
 	}
-#endif
+	mDrawMap.clear();
 }
 
 BOOL LLSpatialGroup::safeToDelete()
@@ -193,13 +187,13 @@ public:
 
 BOOL LLSpatialGroup::isVisible()
 {
-	if (sIgnoreOcclusion)
+	if (LLPipeline::sUseOcclusion)
 	{
-		return !isState(CULLED); 
+		return !isState(CULLED | OCCLUDED);
 	}
 	else
 	{
-		return !isState(CULLED | OCCLUDED);
+		return !isState(CULLED); 
 	}
 }
 
@@ -235,6 +229,88 @@ void LLSpatialGroup::validate()
 #endif
 }
 
+void validate_draw_info(LLDrawInfo& params)
+{
+#if LL_DEBUG
+/*	if (params.mVertexBuffer.isNull())
+	{
+		llerrs << "Draw batch has no vertex buffer." << llendl;
+	}
+	
+	//bad range
+	if (params.mStart >= params.mEnd)
+	{
+		llerrs << "Draw batch has invalid range." << llendl;
+	}
+	
+	if (params.mEnd >= params.mVertexBuffer->getNumVerts())
+	{
+		llerrs << "Draw batch has buffer overrun error." << llendl;
+	}
+	
+	if (params.mOffset + params.mCount > params.mVertexBuffer->getNumIndices())
+	{
+		llerrs << "Draw batch has index buffer ovverrun error." << llendl;
+	}
+	
+	//bad indices
+	U32* indicesp = (U32*) params.mVertexBuffer->getIndicesPointer();
+	if (indicesp)
+	{
+		for (U32 i = params.mOffset; i < params.mOffset+params.mCount; i++)
+		{
+			if (indicesp[i] < params.mStart)
+			{
+				llerrs << "Draw batch has vertex buffer index out of range error (index too low)." << llendl;
+			}
+			
+			if (indicesp[i] > params.mEnd)
+			{
+				llerrs << "Draw batch has vertex buffer index out of range error (index too high)." << llendl;
+			}
+		}
+	}*/
+#endif
+}
+
+void LLSpatialGroup::validateDrawMap()
+{
+/*	for (draw_map_t::iterator i = mDrawMap.begin(); i != mDrawMap.end(); ++i)
+	{
+		std::vector<LLDrawInfo*>& draw_vec = i->second;
+		for (std::vector<LLDrawInfo*>::iterator j = draw_vec.begin(); j != draw_vec.end(); ++j)
+		{
+			LLDrawInfo& params = **j;
+			
+			validate_draw_info(params);
+		}
+	}*/
+}
+
+void LLSpatialGroup::makeStatic()
+{
+	if (isState(GEOM_DIRTY | ALPHA_DIRTY))
+	{
+		return;
+	}
+	
+	if (mSpatialPartition->mRenderByGroup && mBufferUsage != GL_STATIC_DRAW_ARB)
+	{
+		mBufferUsage = GL_STATIC_DRAW_ARB;
+		if (mVertexBuffer.notNull())
+		{
+			mVertexBuffer->makeStatic();
+		}
+		
+		for (buffer_map_t::iterator i = mBufferMap.begin(); i != mBufferMap.end(); ++i)
+		{
+			i->second->makeStatic();
+		}
+		
+		mBuilt = 1.f;
+	}
+}
+
 BOOL LLSpatialGroup::updateInGroup(LLDrawable *drawablep, BOOL immediate)
 {
 	LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
@@ -246,6 +322,8 @@ BOOL LLSpatialGroup::updateInGroup(LLDrawable *drawablep, BOOL immediate)
 	{
 		unbound();
 		setState(OBJECT_DIRTY);
+		setState(GEOM_DIRTY);
+		gPipeline.markRebuild(this);
 		validate_drawable(drawablep);
 		return TRUE;
 	}
@@ -265,11 +343,83 @@ BOOL LLSpatialGroup::addObject(LLDrawable *drawablep, BOOL add_all, BOOL from_oc
 	{
 		drawablep->setSpatialGroup(this, 0);
 		validate_drawable(drawablep);
+		setState(OBJECT_DIRTY | GEOM_DIRTY);
+		gPipeline.markRebuild(this);
+		mLastAddTime = gFrameTimeSeconds;
+		if (drawablep->isSpatialBridge())
+		{
+			mBridgeList.push_back((LLSpatialBridge*) drawablep);
+		}
+		setState(IMAGE_DIRTY);
 	}
 
 	return TRUE;
 }
 
+void LLSpatialGroup::rebuildGeom()
+{
+	LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+	if (!isDead())
+	{
+		mSpatialPartition->rebuildGeom(this);
+	}
+}
+
+void LLSpatialPartition::rebuildGeom(LLSpatialGroup* group)
+{
+	if (group->changeLOD())
+	{
+		group->mLastUpdateDistance = group->mDistance;
+		group->mLastUpdateViewAngle = group->mViewAngle;
+	}
+
+	if (group->isDead() || !group->isState(LLSpatialGroup::GEOM_DIRTY))
+	{
+		return;
+	}
+
+	LLFastTimer ftm(LLFastTimer::FTM_REBUILD_VBO);	
+
+	group->clearDrawMap();
+	
+	//get geometry count
+	group->mIndexCount = 0;
+	group->mVertexCount = 0;
+
+	addGeometryCount(group, group->mVertexCount, group->mIndexCount);
+
+	if (group->mVertexCount > 0 && group->mIndexCount > 0)
+	{ //create vertex buffer containing volume geometry for this node
+		group->mBuilt = 1.f;
+		if (group->mVertexBuffer.isNull() || (group->mBufferUsage != group->mVertexBuffer->getUsage() && LLVertexBuffer::sEnableVBOs))
+		{
+			//LLFastTimer ftm(LLFastTimer::FTM_REBUILD_NONE_VB);
+			group->mVertexBuffer = createVertexBuffer(mVertexDataMask, group->mBufferUsage);
+			group->mVertexBuffer->allocateBuffer(group->mVertexCount, group->mIndexCount, true);
+			stop_glerror();
+		}
+		else
+		{
+			//LLFastTimer ftm(LLFastTimer::FTM_REBUILD_NONE_VB);
+			group->mVertexBuffer->resizeBuffer(group->mVertexCount, group->mIndexCount);
+			stop_glerror();
+		}
+		
+		{
+			LLFastTimer ftm((LLFastTimer::EFastTimerType) ((U32) LLFastTimer::FTM_REBUILD_VOLUME_VB + mPartitionType));
+			getGeometry(group);
+		}
+	}
+	else
+	{
+		group->mVertexBuffer = NULL;
+		group->mBufferMap.clear();
+	}
+
+	group->mLastUpdateTime = gFrameTimeSeconds;
+	group->clearState(LLSpatialGroup::GEOM_DIRTY | LLSpatialGroup::MATRIX_DIRTY);
+}
+
 BOOL LLSpatialGroup::boundObjects(BOOL empty, LLVector3& minOut, LLVector3& maxOut)
 {	
 	const OctreeState* node = mOctreeNode->getOctState();
@@ -285,7 +435,7 @@ BOOL LLSpatialGroup::boundObjects(BOOL empty, LLVector3& minOut, LLVector3& maxO
 
 	LLVector3& newMin = mObjectExtents[0];
 	LLVector3& newMax = mObjectExtents[1];
-
+	
 	if (isState(OBJECT_DIRTY))
 	{ //calculate new bounding box
 		clearState(OBJECT_DIRTY);
@@ -371,11 +521,28 @@ void LLSpatialGroup::unbound()
 	}
 }
 
+LLSpatialGroup* LLSpatialGroup::getParent()
+{
+	if (isDead())
+	{
+		return NULL;
+	}
+
+	OctreeNode* parent = mOctreeNode->getOctParent();
+
+	if (parent)
+	{
+		return (LLSpatialGroup*) parent->getListener(0);
+	}
+
+	return NULL;
+}
+
 BOOL LLSpatialGroup::removeObject(LLDrawable *drawablep, BOOL from_octree)
 {
 	LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
 	unbound();
-	if (!from_octree)
+	if (mOctreeNode && !from_octree)
 	{
 		if (!mOctreeNode->remove(drawablep))
 		{
@@ -385,6 +552,19 @@ BOOL LLSpatialGroup::removeObject(LLDrawable *drawablep, BOOL from_octree)
 	else
 	{
 		drawablep->setSpatialGroup(NULL, -1);
+		setState(GEOM_DIRTY);
+		gPipeline.markRebuild(this);
+		if (drawablep->isSpatialBridge())
+		{
+			for (bridge_list_t::iterator i = mBridgeList.begin(); i != mBridgeList.end(); ++i)
+			{
+				if (*i == drawablep)
+				{
+					mBridgeList.erase(i);
+					break;
+				}
+			}
+		}
 	}
 	return TRUE;
 }
@@ -401,6 +581,9 @@ void LLSpatialGroup::shift(const LLVector3 &offset)
 	mObjectBounds[0] += offset;
 	mObjectExtents[0] += offset;
 	mObjectExtents[1] += offset;
+
+	gPipeline.markRebuild(this);
+	setState(GEOM_DIRTY | MATRIX_DIRTY | OCCLUSION_DIRTY);
 }
 
 class LLSpatialSetState : public LLSpatialGroup::OctreeTraveler
@@ -515,29 +698,152 @@ void LLSpatialGroup::clearState(U32 state, S32 mode)
 //======================================
 
 LLSpatialGroup::LLSpatialGroup(OctreeNode* node, LLSpatialPartition* part)
-: mState(0), mOctreeNode(node), mSpatialPartition(part)
+: mOctreeNode(node), mState(0), mSpatialPartition(part), mVertexBuffer(NULL), 
+  mDistance(0.f), mLastUpdateDistance(-1.f), 
+  mViewAngle(0.f), mLastUpdateViewAngle(-1.f), 
+  mDepth(0.f), mBuilt(0.f),
+  mLastUpdateTime(gFrameTimeSeconds), mLastRenderTime(gFrameTimeSeconds),
+  mLastAddTime(gFrameTimeSeconds),
+  mBufferUsage(GL_STATIC_DRAW_ARB)
 {
 	LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+
 	sg_assert(mOctreeNode->getListenerCount() == 0);
 	mOctreeNode->addListener(this);
-	setState(DIRTY);
+	setState(SG_INITIAL_STATE_MASK);
 
 	mBounds[0] = LLVector3(node->getCenter());
 	mBounds[1] = LLVector3(node->getSize());
 
-	sLODSeed = (sLODSeed+1)%SG_LOD_PERIOD;
-	mLODHash = sLODSeed;
+	part->mLODSeed = (part->mLODSeed+1)%part->mLODPeriod;
+	mLODHash = part->mLODSeed;
+	
+	mRadius = 1;
+	mPixelArea = 1024.f;
+}
+
+void LLSpatialGroup::updateDistance(LLCamera &camera)
+{
+#if !LL_RELEASE_FOR_DOWNLOAD
+	if (isState(LLSpatialGroup::OBJECT_DIRTY))
+	{
+		llerrs << "Spatial group dirty on distance update." << llendl;
+	}
+#endif
+	if (!getData().empty())
+	{
+		mRadius = mSpatialPartition->mRenderByGroup ? mObjectBounds[1].magVec() :
+						(F32) mOctreeNode->getSize().magVec();
+		mDistance = mSpatialPartition->calcDistance(this, camera);
+		mPixelArea = mSpatialPartition->calcPixelArea(this, camera);
+	}
+}
+
+F32 LLSpatialPartition::calcDistance(LLSpatialGroup* group, LLCamera& camera)
+{
+	LLVector3 eye = group->mObjectBounds[0] - camera.getOrigin();
+
+	F32 dist = 0.f;
+
+	if (group->mDrawMap.find(LLRenderPass::PASS_ALPHA) != group->mDrawMap.end())
+	{
+		LLVector3 v = eye;
+		dist = eye.normVec();
+
+		if (!group->isState(LLSpatialGroup::ALPHA_DIRTY))
+		{
+			LLVector3 view_angle = LLVector3(eye * LLVector3(1,0,0),
+											eye * LLVector3(0,1,0),
+											eye * LLVector3(0,0,1));
+
+			if ((view_angle-group->mLastUpdateViewAngle).magVec() > 0.64f)
+			{
+				group->mViewAngle = view_angle;
+				group->mLastUpdateViewAngle = view_angle;
+				//for occasional alpha sorting within the group
+				//NOTE: If there is a trivial way to detect that alpha sorting here would not change the render order,
+				//not setting this node to dirty would be a very good thing
+				group->setState(LLSpatialGroup::ALPHA_DIRTY);
+			}		
+		}
+
+		//calculate depth of node for alpha sorting
+
+		LLVector3 at = camera.getAtAxis();
+
+		//front of bounding box
+		for (U32 i = 0; i < 3; i++)
+		{
+			v.mV[i] -= group->mObjectBounds[1].mV[i]*0.25f * at.mV[i];
+		}
+
+		group->mDepth = v * at;
+
+		F32 water_height = gAgent.getRegion()->getWaterHeight();
+		//figure out if this node is above or below water
+		if (group->mObjectBounds[0].mV[2] < water_height)
+		{
+			group->setState(LLSpatialGroup::BELOW_WATER);
+		}
+		else
+		{
+			group->clearState(LLSpatialGroup::BELOW_WATER);
+		}
+	}
+	else
+	{
+		dist = eye.magVec();
+	}
+
+	if (dist < 16.f)
+	{
+		dist /= 16.f;
+		dist *= dist;
+		dist *= 16.f;
+	}
+
+	return dist;
+}
+
+F32 LLSpatialPartition::calcPixelArea(LLSpatialGroup* group, LLCamera& camera)
+{
+	return LLPipeline::calcPixelArea(group->mObjectBounds[0], group->mObjectBounds[1], camera);
 }
 
 BOOL LLSpatialGroup::changeLOD()
 {
-	return LLDrawable::getCurrentFrame()%SG_LOD_PERIOD == mLODHash;
+	if (isState(ALPHA_DIRTY))
+	{ ///an alpha sort is going to happen, update distance and LOD
+		return TRUE;
+	}
+
+	if (mSpatialPartition->mSlopRatio > 0.f)
+	{
+		F32 ratio = (mDistance - mLastUpdateDistance)/(llmax(mLastUpdateDistance, mRadius));
+
+		if (fabsf(ratio) >= mSpatialPartition->mSlopRatio)
+		{
+			return TRUE;
+		}
+
+		if (mDistance > mRadius)
+		{
+			return FALSE;
+		}
+	}
+	
+	if (LLDrawable::getCurrentFrame()%mSpatialPartition->mLODPeriod == mLODHash)
+	{
+		return TRUE;
+	}
+	
+	return FALSE;
 }
 
-void LLSpatialGroup::handleInsertion(const TreeNode* node, LLDrawable* drawable)
+void LLSpatialGroup::handleInsertion(const TreeNode* node, LLDrawable* drawablep)
 {
 	LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
-	addObject(drawable, FALSE, TRUE);
+	addObject(drawablep, FALSE, TRUE);
 	unbound();
 	setState(OBJECT_DIRTY);
 }
@@ -552,29 +858,13 @@ void LLSpatialGroup::handleRemoval(const TreeNode* node, LLDrawable* drawable)
 void LLSpatialGroup::handleDestruction(const TreeNode* node)
 {
 	LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
-	
-	if (mOctreeNode)
-	{
-		OctreeState* state = mOctreeNode->getOctState();
-		for (OctreeState::element_iter i = state->getData().begin(); i != state->getData().end(); ++i)
-		{
-			LLDrawable* drawable = *i;
-			if (!drawable->isDead())
-			{
-				drawable->setSpatialGroup(NULL, -1);
-			}
-		}
-	}
-	
-	if (safeToDelete())
-	{
-        delete this;
-	}
-	else
-	{
-		setState(DEAD);
-		mOctreeNode = NULL;
-	}
+	setState(DEAD);
+	clearDrawMap();
+	mOcclusionVerts = NULL;
+	mVertexBuffer = NULL;
+	mBufferMap.clear();
+	sZombieGroups++;
+	mOctreeNode = NULL;
 }
 
 void LLSpatialGroup::handleStateChange(const TreeNode* node)
@@ -592,7 +882,8 @@ void LLSpatialGroup::handleChildAddition(const OctreeNode* parent, OctreeNode* c
 	LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
 	if (child->getListenerCount() == 0)
 	{
-		(new LLSpatialGroup(child, mSpatialPartition))->setState(mState & SG_STATE_INHERIT_MASK);
+		LLSpatialGroup* group = new LLSpatialGroup(child, mSpatialPartition);
+		group->setState(mState & SG_STATE_INHERIT_MASK);
 	}
 	else
 	{
@@ -616,7 +907,7 @@ BOOL LLSpatialGroup::rebound()
 	
 	LLVector3 oldBounds[2];
 	
-	if (isState(QUERY_OUT))
+	if (mSpatialPartition->isVolatile() && isState(QUERY_OUT))
 	{	//a query has been issued, if our bounding box changes significantly
 		//we need to discard the issued query
 		oldBounds[0] = mBounds[0];
@@ -681,7 +972,7 @@ BOOL LLSpatialGroup::rebound()
 		mBounds[1] = (newMax - newMin)*0.5f;
 	}
 	
-	if (isState(QUERY_OUT))
+	if (mSpatialPartition->isVolatile() && isState(QUERY_OUT))
 	{
 		for (U32 i = 0; i < 3 && !isState(DISCARD_QUERY); i++)
 		{
@@ -694,6 +985,8 @@ BOOL LLSpatialGroup::rebound()
 		}
 	}
 	
+	setState(OCCLUSION_DIRTY);
+	
 	clearState(DIRTY);
 
 	return TRUE;
@@ -701,9 +994,21 @@ BOOL LLSpatialGroup::rebound()
 
 //==============================================
 
-LLSpatialPartition::LLSpatialPartition()
+LLSpatialPartition::LLSpatialPartition(U32 data_mask, BOOL is_volatile, U32 buffer_usage)
 {
 	LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+	mDrawableType = 0;
+	mPartitionType = LLPipeline::PARTITION_NONE;
+	mVolatile = is_volatile;
+	mLODSeed = 0;
+	mLODPeriod = 1;
+	mVertexDataMask = data_mask;
+	mBufferUsage = buffer_usage;
+	mDepthMask = FALSE;
+	mSlopRatio = 0.25f;
+	mRenderByGroup = TRUE;
+	mImageEnabled = FALSE;
+
 	mOctree = new LLSpatialGroup::OctreeNode(LLVector3d(0,0,0), 
 											LLVector3d(1,1,1), 
 											new LLSpatialGroup::OctreeRoot(), NULL);
@@ -714,12 +1019,18 @@ LLSpatialPartition::LLSpatialPartition()
 LLSpatialPartition::~LLSpatialPartition()
 {
 	LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+	
+	for (U32 i = 0; i < mOcclusionQueries.size(); i++)
+	{
+		glDeleteQueriesARB(1, (GLuint*)(&(mOcclusionQueries[i])));
+	}
+	
 	delete mOctree;
 	mOctree = NULL;
 }
 
 
-LLSpatialGroup *LLSpatialPartition::put(LLDrawable *drawablep)
+LLSpatialGroup *LLSpatialPartition::put(LLDrawable *drawablep, BOOL was_visible)
 {
 	LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
 	const F32 MAX_MAG = 1000000.f*1000000.f; // 1 million
@@ -751,7 +1062,13 @@ LLSpatialGroup *LLSpatialPartition::put(LLDrawable *drawablep)
 
 	LLSpatialGroup::OctreeNode* node = mOctree->getNodeAt(drawablep);
 	
-	return (LLSpatialGroup*) node->getListener(0);
+	LLSpatialGroup* group = (LLSpatialGroup*) node->getListener(0);
+	if (was_visible && group->isState(LLSpatialGroup::QUERY_OUT))
+	{
+		group->setState(LLSpatialGroup::DISCARD_QUERY);
+	}
+
+	return group;
 }
 
 BOOL LLSpatialPartition::remove(LLDrawable *drawablep, LLSpatialGroup *curp)
@@ -774,14 +1091,16 @@ void LLSpatialPartition::move(LLDrawable *drawablep, LLSpatialGroup *curp, BOOL
 {
 	LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
 	LLFastTimer t(LLFastTimer::FTM_UPDATE_MOVE);
-	
+		
+	BOOL was_visible = curp ? curp->isVisible() : FALSE;
+
 	if (curp && curp->mSpatialPartition != this)
 	{
 		//keep drawable from being garbage collected
 		LLPointer<LLDrawable> ptr = drawablep;
 		if (curp->mSpatialPartition->remove(drawablep, curp))
 		{
-			put(drawablep);
+			put(drawablep, was_visible);
 			return;
 		}
 		else
@@ -804,7 +1123,7 @@ void LLSpatialPartition::move(LLDrawable *drawablep, LLSpatialGroup *curp, BOOL
 		OCT_ERRS << "Move couldn't find existing spatial group!" << llendl;
 	}
 
-	put(drawablep);
+	put(drawablep, was_visible);
 }
 
 class LLSpatialShift : public LLSpatialGroup::OctreeTraveler
@@ -829,13 +1148,9 @@ void LLSpatialPartition::shift(const LLVector3 &offset)
 
 BOOL LLSpatialPartition::checkOcclusion(LLSpatialGroup* group, LLCamera* camera)
 {
-	if (sIgnoreOcclusion)
-	{
-		return FALSE;
-	}
-	
-	if (!group->isState(LLSpatialGroup::ACTIVE_OCCLUSION | LLSpatialGroup::OCCLUDED) &&
-		!earlyFail(camera, group))
+	if (LLPipeline::sUseOcclusion &&
+		!group->isState(LLSpatialGroup::ACTIVE_OCCLUSION | LLSpatialGroup::OCCLUDED) &&
+		(!camera || !earlyFail(camera, group)))
 	{
 		group->setState(LLSpatialGroup::ACTIVE_OCCLUSION);
 		mQueryQueue.push(group);
@@ -853,8 +1168,8 @@ public:
 
 	virtual bool earlyFail(const LLSpatialGroup* group)
 	{
-		if (mRes &&	//never occlusion cull the root node
-			!sIgnoreOcclusion &&			//never occlusion cull selection
+		if (group->mOctreeNode->getParent() &&	//never occlusion cull the root node
+			LLPipeline::sUseOcclusion &&			//never occlusion cull selection
 			group->isState(LLSpatialGroup::OCCLUDED))
 		{
 			return true;
@@ -931,22 +1246,19 @@ public:
 				group->mSpatialPartition->checkOcclusion(group, mCamera);
 			}
 		}
+
+		if (LLPipeline::sDynamicReflections &&
+			group->mOctreeNode->getSize().mdV[0] == 16.0 && 
+			group->mDistance < 64.f &&
+			group->mLastAddTime < gFrameTimeSeconds - 2.f)
+		{
+			group->mSpatialPartition->markReimage(group);
+		}
 	}
 	
-	virtual void processDrawable(LLDrawable* drawable)
+	virtual void processGroup(LLSpatialGroup* group)
 	{
-		if (!drawable->isDead())
-		{
-			const LLVector3* extents = drawable->getSpatialExtents();
-			
-			F32 objRad = drawable->getRadius();
-			objRad *= objRad;
-			F32 distSqr = ((extents[0]+extents[1])*0.5f - mCamera->getOrigin()).magVecSquared();
-			if (objRad/distSqr > SG_MIN_DIST_RATIO)
-			{
-				gPipeline.markNotCulled(drawable, *mCamera);
-			}
-		}		
+		gPipeline.markNotCulled(group, *mCamera);
 	}
 	
 	virtual void visit(const LLSpatialGroup::OctreeState* branch) 
@@ -957,10 +1269,7 @@ public:
 		
 		if (checkObjects(branch, group))
 		{
-			for (LLSpatialGroup::OctreeState::const_element_iter i = branch->getData().begin(); i != branch->getData().end(); ++i)
-			{
-				processDrawable(*i);
-			}
+			processGroup(group);
 		}
 	}
 
@@ -979,34 +1288,32 @@ public:
 	virtual void lateFail(LLSpatialGroup* group) { }
 	virtual void preprocess(LLSpatialGroup* group) { }
 
-	virtual void processDrawable(LLDrawable* drawable)
+	virtual void processGroup(LLSpatialGroup* group)
 	{
-		if (!drawable->isDead())
+		LLSpatialGroup::OctreeState* branch = group->mOctreeNode->getOctState();
+
+		for (LLSpatialGroup::OctreeState::const_element_iter i = branch->getData().begin(); i != branch->getData().end(); ++i)
 		{
-			if (drawable->isSpatialBridge())
-			{
-				drawable->setVisible(*mCamera, mResults, TRUE);
-			}
-			else
+			LLDrawable* drawable = *i;
+			
+			if (!drawable->isDead())
 			{
-				mResults->push_back(drawable);
-			}
-		}		
+				if (drawable->isSpatialBridge())
+				{
+					drawable->setVisible(*mCamera, mResults, TRUE);
+				}
+				else
+				{
+					mResults->push_back(drawable);
+				}
+			}		
+		}
 	}
 	
 	std::vector<LLDrawable*>* mResults;
 };
 
 
-void drawBox(const LLVector3& c, const LLVector3& r)
-{
-	glPushMatrix();
-	glTranslatef(c.mV[0], c.mV[1], c.mV[2]);
-	glScalef(r.mV[0], r.mV[1], r.mV[2]);
-	glCallList(sBoxList);
-	glPopMatrix();
-}
-
 void genBoxList()
 {
 	if (sBoxList != 0)
@@ -1058,6 +1365,99 @@ void genBoxList()
 	glEndList();
 }
 
+void drawBox(const LLVector3& c, const LLVector3& r)
+{
+	genBoxList();
+
+	glPushMatrix();
+	glTranslatef(c.mV[0], c.mV[1], c.mV[2]);
+	glScalef(r.mV[0], r.mV[1], r.mV[2]);
+	glCallList(sBoxList);
+	glPopMatrix();
+}
+
+void drawBoxOutline(const LLVector3& pos, const LLVector3& size)
+{
+	LLVector3 v1 = size.scaledVec(LLVector3( 1, 1,1));
+	LLVector3 v2 = size.scaledVec(LLVector3(-1, 1,1));
+	LLVector3 v3 = size.scaledVec(LLVector3(-1,-1,1));
+	LLVector3 v4 = size.scaledVec(LLVector3( 1,-1,1));
+
+	glBegin(GL_LINE_LOOP); //top
+	glVertex3fv((pos+v1).mV);
+	glVertex3fv((pos+v2).mV);
+	glVertex3fv((pos+v3).mV);
+	glVertex3fv((pos+v4).mV);
+	glEnd();
+
+	glBegin(GL_LINE_LOOP); //bottom
+	glVertex3fv((pos-v1).mV);
+	glVertex3fv((pos-v2).mV);
+	glVertex3fv((pos-v3).mV);
+	glVertex3fv((pos-v4).mV);
+	glEnd();
+
+
+	glBegin(GL_LINES);
+
+	//right
+	glVertex3fv((pos+v1).mV);
+	glVertex3fv((pos-v3).mV);
+			
+	glVertex3fv((pos+v4).mV);
+	glVertex3fv((pos-v2).mV);
+
+	//left
+	glVertex3fv((pos+v2).mV);
+	glVertex3fv((pos-v4).mV);
+
+	glVertex3fv((pos+v3).mV);
+	glVertex3fv((pos-v1).mV);
+
+	glEnd();
+}
+
+class LLOctreeDirty : public LLOctreeTraveler<LLDrawable>
+{
+public:
+	virtual void visit(const LLOctreeState<LLDrawable>* state)
+	{
+		LLSpatialGroup* group = (LLSpatialGroup*) state->getListener(0);
+
+		group->setState(LLSpatialGroup::GEOM_DIRTY | 
+						LLSpatialGroup::OCCLUSION_DIRTY |
+						LLSpatialGroup::IMAGE_DIRTY);
+		group->mLastUpdateTime = gFrameTimeSeconds;
+		group->mVertexBuffer = NULL;
+		group->mBufferMap.clear();
+
+		group->mOcclusionVerts = NULL;
+		group->mReflectionMap = NULL;
+		group->clearDrawMap();
+	
+		for (LLSpatialGroup::element_iter i = group->getData().begin(); i != group->getData().end(); ++i)
+		{
+			LLDrawable* drawable = *i;
+			for (S32 j = 0; j < drawable->getNumFaces(); j++)
+			{
+				LLFace* facep = drawable->getFace(j);
+				facep->mVertexBuffer = NULL;
+				facep->mLastVertexBuffer = NULL;
+			}
+
+			if (drawable->getVObj() && !group->mSpatialPartition->mRenderByGroup)
+			{
+				gPipeline.markRebuild(drawable, LLDrawable::REBUILD_ALL, TRUE);
+			}
+		}
+
+		for (LLSpatialGroup::bridge_list_t::iterator i = group->mBridgeList.begin(); i != group->mBridgeList.end(); ++i)
+		{
+			LLSpatialBridge* bridge = *i;
+			traverse(bridge->mOctree);
+		}
+	}
+};
 void LLSpatialPartition::restoreGL()
 {
 	LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
@@ -1080,6 +1480,14 @@ void LLSpatialPartition::restoreGL()
 	genBoxList();
 }
 
+void LLSpatialPartition::resetVertexBuffers()
+{
+	LLOctreeDirty dirty;
+	dirty.traverse(mOctree);
+
+	mOcclusionIndices = NULL;
+}
+
 S32 LLSpatialPartition::cull(LLCamera &camera, std::vector<LLDrawable *>* results, BOOL for_select)
 {
 	LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
@@ -1096,11 +1504,11 @@ S32 LLSpatialPartition::cull(LLCamera &camera, std::vector<LLDrawable *>* result
 	}
 	else
 	{
+		LLFastTimer ftm(LLFastTimer::FTM_FRUSTUM_CULL);		
 		LLOctreeCull culler(&camera);
 		culler.traverse(mOctree);
 	}
 	
-	sIgnoreOcclusion = !(gSavedSettings.getBOOL("UseOcclusion") && gGLManager.mHasOcclusionQuery);
 	return 0;
 }
 
@@ -1155,9 +1563,10 @@ public:
 BOOL earlyFail(LLCamera* camera, LLSpatialGroup* group)
 {
 	LLVector3 c = group->mBounds[0];
-	LLVector3 r = group->mBounds[1] * (SG_OCCLUSION_FUDGE*2.f) + LLVector3(0.01f,0.01f,0.01f);
+	LLVector3 r = group->mBounds[1]*SG_OCCLUSION_FUDGE + LLVector3(0.2f,0.2f,0.2f);
     
-	if (group->isState(LLSpatialGroup::CULLED) || !camera->AABBInFrustum(c, r))
+	//if (group->isState(LLSpatialGroup::CULLED)) // || 
+	if (!camera->AABBInFrustum(c, r))
 	{
 		return TRUE;
 	}
@@ -1178,6 +1587,160 @@ BOOL earlyFail(LLCamera* camera, LLSpatialGroup* group)
 	return TRUE;
 }
 
+void LLSpatialPartition::processGeometry(LLCamera* camera)
+{
+	if (!mRenderByGroup || mBufferUsage == GL_STREAM_DRAW_ARB)
+	{
+		return;
+	}
+
+	U32 process_count = 8;
+
+	LLSpatialGroup* root = (LLSpatialGroup*) mOctree->getListener(0);
+	if (!root->isState(LLSpatialGroup::IN_GEOMETRY_QUEUE))
+	{
+		root->setState(LLSpatialGroup::IN_GEOMETRY_QUEUE);
+		mUpdateQueue.push(root);
+	}
+
+	while (process_count > 0 && !mUpdateQueue.empty())
+	{
+		process_count--;
+		LLPointer<LLSpatialGroup> group = mUpdateQueue.front();
+		mUpdateQueue.pop();
+	
+		group->clearState(LLSpatialGroup::IN_GEOMETRY_QUEUE);
+
+		if (group->isDead())
+		{
+			continue;
+		}
+
+		//push children onto queue
+		for (U32 i = 0; i < group->mOctreeNode->getChildCount(); i++)
+		{
+			LLSpatialGroup* child = (LLSpatialGroup*) group->mOctreeNode->getChild(i)->getListener(0);
+
+			if (!child->isState(LLSpatialGroup::IN_GEOMETRY_QUEUE))
+			{
+				child->setState(LLSpatialGroup::IN_GEOMETRY_QUEUE);
+				mUpdateQueue.push(child);
+			}
+		}
+
+		if (!group->isDead() && 
+			!group->isVisible() && 
+			!group->isState(LLSpatialGroup::OBJECT_DIRTY) && 
+			group->mBufferUsage != GL_STREAM_DRAW_ARB)
+		{
+			group->updateDistance(*camera);
+			for (LLSpatialGroup::element_iter i = group->getData().begin(); i != group->getData().end(); ++i)
+			{
+				LLDrawable* drawablep = *i;
+				if (!drawablep->isDead())
+				{
+					drawablep->updateDistance(*camera);
+				}
+			}
+		}
+	}
+}
+
+void LLSpatialPartition::markReimage(LLSpatialGroup* group)
+{
+	if (mImageEnabled && group->isState(LLSpatialGroup::IMAGE_DIRTY))
+	{	
+		if (!group->isState(LLSpatialGroup::IN_IMAGE_QUEUE))
+		{
+			group->setState(LLSpatialGroup::IN_IMAGE_QUEUE);
+			mImageQueue.push(group);
+		}
+	}
+}
+
+void LLSpatialPartition::processImagery(LLCamera* camera)
+{
+	if (!mImageEnabled)
+	{
+		return;
+	}
+
+	U32 process_count = 1;
+
+	while (process_count > 0 && !mImageQueue.empty())
+	{
+		LLPointer<LLSpatialGroup> group = mImageQueue.front();
+		mImageQueue.pop();
+	
+		group->clearState(LLSpatialGroup::IN_IMAGE_QUEUE);
+
+		if (group->isDead())
+		{
+			continue;
+		}
+
+		if (LLPipeline::sDynamicReflections)
+		{
+			process_count--;
+			LLVector3 origin = group->mBounds[0];
+			
+			LLCamera cube_cam;
+			cube_cam.setOrigin(origin);
+			cube_cam.setFar(64.f);
+
+			LLPointer<LLCubeMap> cube_map = group->mReflectionMap;
+			group->mReflectionMap = NULL;
+			if (cube_map.isNull())
+			{
+				cube_map = new LLCubeMap();
+				cube_map->initGL();
+			}
+
+			if (gPipeline.mCubeBuffer == NULL)
+			{
+				gPipeline.mCubeBuffer = new LLCubeMap();
+				gPipeline.mCubeBuffer->initGL();
+			}
+
+			gPipeline.generateReflectionMap(gPipeline.mCubeBuffer, cube_cam, 128);
+			gPipeline.blurReflectionMap(gPipeline.mCubeBuffer, cube_map, 32);
+			group->mReflectionMap = cube_map;
+			group->setState(LLSpatialGroup::GEOM_DIRTY);
+			gPipeline.markRebuild(group);
+		}
+
+		group->clearState(LLSpatialGroup::IMAGE_DIRTY);
+	}
+}
+
+void validate_occlusion_list(std::vector<LLPointer<LLSpatialGroup> >& occluded_list)
+{
+#if !LL_RELEASE_FOR_DOWNLOAD
+	for (U32 i = 0; i < occluded_list.size(); i++)
+	{
+		LLSpatialGroup* group = occluded_list[i];
+		for (U32 j = i+1; j < occluded_list.size(); j++)
+		{
+			if (occluded_list[i] == occluded_list[j])
+			{
+				llerrs << "Duplicate node in occlusion list." << llendl;
+			}
+		}
+
+		LLSpatialGroup::OctreeNode* parent = group->mOctreeNode->getOctParent();
+		while (parent)
+		{
+			LLSpatialGroup* parent_group = (LLSpatialGroup*) parent->getListener(0);
+			if (parent_group->isState(LLSpatialGroup::OCCLUDED))
+			{
+				llerrs << "Child node of occluded node in occlusion list (redundant query)." << llendl;
+			}
+			parent = parent->getOctParent();
+		}
+	}
+#endif
+}
+
 void LLSpatialPartition::processOcclusion(LLCamera* camera)
 {
 	LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
@@ -1198,12 +1761,12 @@ void LLSpatialPartition::processOcclusion(LLCamera* camera)
 	const U32 MAX_PUSHED = mOcclusionQueue.size();
 	U32 count = 0;
 	U32 pcount = 0;
-
+	
 	while (pcount < MAX_PUSHED && count < MAX_PULLED && !mOcclusionQueue.empty())
 	{
-	 	LLFastTimer t(LLFastTimer::FTM_OCCLUSION);
+		LLFastTimer t(LLFastTimer::FTM_OCCLUSION);
 
-		LLSpatialGroup* group = mOcclusionQueue.front();
+		LLPointer<LLSpatialGroup> group = mOcclusionQueue.front();
 		if (!group->isState(LLSpatialGroup::IN_QUEUE))
 		{
 			OCT_ERRS << "Spatial Group State Error.  Group in queue not tagged as such." << llendl;
@@ -1214,10 +1777,6 @@ void LLSpatialPartition::processOcclusion(LLCamera* camera)
 
 		if (group->isDead())
 		{
-			if (group->safeToDelete())
-			{
-				delete group;
-			}
 			continue;
 		}
 
@@ -1231,22 +1790,14 @@ void LLSpatialPartition::processOcclusion(LLCamera* camera)
 		{
 			LLSpatialGroup* child = (LLSpatialGroup*) group->mOctreeNode->getChild(i)->getListener(0);
 
-			if (!child->isState(LLSpatialGroup::OCCLUDED | LLSpatialGroup::CULLED)
-				&& !child->isState(LLSpatialGroup::IN_QUEUE | LLSpatialGroup::ACTIVE_OCCLUSION))
+			//if (!child->isState(LLSpatialGroup::OCCLUDED | LLSpatialGroup::CULLED)
+			if (!child->isState(LLSpatialGroup::IN_QUEUE | LLSpatialGroup::ACTIVE_OCCLUSION))
 			{
 				child->setState(LLSpatialGroup::IN_QUEUE);
 				mOcclusionQueue.push(child);
 			}
 		}
-
-		/*if (group->isState(LLSpatialGroup::QUERY_PENDING))
-		{ //already on the pending group, put it back
-			group->setState(LLSpatialGroup::IN_QUEUE);
-			mOcclusionQueue.push(group);
-			pcount++;
-			continue;
-		}*/
-
+		
 		if (earlyFail(camera, group))
 		{
 			sg_assert(!group->isState(LLSpatialGroup::OCCLUDED));
@@ -1265,7 +1816,6 @@ void LLSpatialPartition::processOcclusion(LLCamera* camera)
 				sg_assert(mOccludedList[i] != group);
 			}
 #endif
-			//group->setState(LLSpatialGroup::QUERY_PENDING);
 			group->setState(LLSpatialGroup::ACTIVE_OCCLUSION);
 			mQueryQueue.push(group);
 			count++;
@@ -1277,7 +1827,7 @@ void LLSpatialPartition::processOcclusion(LLCamera* camera)
 	{
 	 	LLFastTimer t(LLFastTimer::FTM_OCCLUSION_READBACK);
 
-		if (mOccludedList[i]->isDead() || !mOccludedList[i]->isState(LLSpatialGroup::ACTIVE_OCCLUSION))
+		if (mOccludedList[i]->isDead() || mOccludedList[i]->isState(LLSpatialGroup::DEACTIVATE_OCCLUSION))
 		{
 			continue;
 		}
@@ -1328,8 +1878,7 @@ void LLSpatialPartition::processOcclusion(LLCamera* camera)
 						LLSpatialGroup* parent = (LLSpatialGroup*) oct_parent->getListener(0);
 						
 						if (checkOcclusion(parent, camera))
-						{	//force a guess on the parent and siblings
-							
+						{	//force a guess on the parent and siblings		
 							for (U32 i = 0; i < parent->mOctreeNode->getChildCount(); i++)
 							{
 								LLSpatialGroup* child = (LLSpatialGroup*) parent->mOctreeNode->getChild(i)->getListener(0);
@@ -1337,13 +1886,17 @@ void LLSpatialPartition::processOcclusion(LLCamera* camera)
 							}
 						}
 					}
+
+					//take children off the active list
+					mOccludedList[i]->setState(LLSpatialGroup::DEACTIVATE_OCCLUSION, LLSpatialGroup::STATE_MODE_BRANCH);
+					mOccludedList[i]->clearState(LLSpatialGroup::DEACTIVATE_OCCLUSION);
 				}
 				mOccludedList[i]->setState(LLSpatialGroup::OCCLUDED, LLSpatialGroup::STATE_MODE_DIFF);
 			}
 			else
 			{
 				//take children off the active list
-				mOccludedList[i]->setState(LLSpatialGroup::DEACTIVATE_OCCLUSION, LLSpatialGroup::STATE_MODE_DIFF);
+				mOccludedList[i]->setState(LLSpatialGroup::DEACTIVATE_OCCLUSION, LLSpatialGroup::STATE_MODE_BRANCH);
 							
 				//keep this node on the active list
 				mOccludedList[i]->clearState(LLSpatialGroup::DEACTIVATE_OCCLUSION);
@@ -1364,15 +1917,13 @@ void LLSpatialPartition::processOcclusion(LLCamera* camera)
 			mOccludedList[i]->isState(LLSpatialGroup::DEACTIVATE_OCCLUSION)) //parent is occluded
 		{
 			LLSpatialGroup* groupp = mOccludedList[i];
-			mOccludedList.erase(mOccludedList.begin()+i);
-			groupp->clearState(LLSpatialGroup::ACTIVE_OCCLUSION);
-			groupp->clearState(LLSpatialGroup::DEACTIVATE_OCCLUSION);
-			groupp->clearState(LLSpatialGroup::OCCLUDING);
-
-			if (groupp->isDead() && groupp->safeToDelete())
+			if (!groupp->isDead())
 			{
-				delete groupp;
+				groupp->clearState(LLSpatialGroup::ACTIVE_OCCLUSION);
+				groupp->clearState(LLSpatialGroup::DEACTIVATE_OCCLUSION);
+				groupp->clearState(LLSpatialGroup::OCCLUDING);
 			}
+			mOccludedList.erase(mOccludedList.begin()+i);
 		}
 		else
 		{
@@ -1380,11 +1931,13 @@ void LLSpatialPartition::processOcclusion(LLCamera* camera)
 		}
 	}
 
+	validate_occlusion_list(mOccludedList);
+
 	//pump some non-culled items onto the occlusion list
 	//count = MAX_PULLED;
 	while (!mQueryQueue.empty())
 	{
-		LLSpatialGroup* group = mQueryQueue.front();
+		LLPointer<LLSpatialGroup> group = mQueryQueue.front();
 		mQueryQueue.pop();
         //group->clearState(LLSpatialGroup::QUERY_PENDING);
 		mOccludedList.push_back(group);
@@ -1399,12 +1952,161 @@ void LLSpatialPartition::processOcclusion(LLCamera* camera)
 	}
 }
 
+class LLOcclusionIndexBuffer : public LLVertexBuffer
+{
+public:
+	LLOcclusionIndexBuffer(U32 size)
+		: LLVertexBuffer(0, GL_STREAM_DRAW_ARB)
+	{
+		allocateBuffer(0, size, TRUE);
+
+		LLStrider<U32> idx;
+
+		getIndexStrider(idx);
+
+		//12 triangles' indices
+		idx[0] = 1; idx[1] = 0; idx[2] = 2; //front
+		idx[3] = 3; idx[4] = 2; idx[5] = 0;
+
+		idx[6] = 4; idx[7] = 5; idx[8] = 1; //top
+		idx[9] = 0; idx[10] = 1; idx[11] = 5; 
+
+		idx[12] = 5; idx[13] = 4; idx[14] = 6; //back
+		idx[15] = 7; idx[16] = 6; idx[17] = 4;
+
+		idx[18] = 6; idx[19] = 7; idx[20] = 3; //bottom
+		idx[21] = 2; idx[22] = 3; idx[23] = 7;
+
+		idx[24] = 0; idx[25] = 5; idx[26] = 3; //left
+		idx[27] = 6; idx[28] = 3; idx[29] = 5;
+
+		idx[30] = 4; idx[31] = 1; idx[32] = 7; //right
+		idx[33] = 2; idx[34] = 7; idx[35] = 1;
+	}
+
+	//virtual BOOL useVBOs() const { return FALSE; }
+
+	void setBuffer(U32 data_mask)
+	{
+		if (useVBOs())
+		{
+			glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, mGLIndices);
+			sIBOActive = TRUE;
+			unmapBuffer();
+		}
+		else if (sIBOActive)
+		{
+			glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
+			sIBOActive = FALSE;
+		}
+		
+		sGLRenderIndices = mGLIndices;
+	}
+};
+
+class LLOcclusionVertexBuffer : public LLVertexBuffer
+{
+public:
+	LLOcclusionVertexBuffer(S32 usage)
+		: LLVertexBuffer(MAP_VERTEX, usage)
+	{
+		allocateBuffer(8, 0, TRUE);
+	}
+
+	//virtual BOOL useVBOs() const { return FALSE; }
+
+	void setBuffer(U32 data_mask)
+	{
+		if (useVBOs())
+		{
+			glBindBufferARB(GL_ARRAY_BUFFER_ARB, mGLBuffer);
+			sVBOActive = TRUE;
+			unmapBuffer();
+		}
+		else if (sVBOActive)
+		{
+			glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
+			sVBOActive = FALSE;
+		}
+		
+		if (data_mask)
+		{
+			glVertexPointer(3,GL_FLOAT, 0, useVBOs() ? 0 : mMappedData);
+		}
+		
+		sGLRenderBuffer = mGLBuffer;
+	}
+};
+
+void LLSpatialPartition::buildOcclusion()
+{
+	if (mOccludedList.empty())
+	{
+		return;
+	}
+
+	BOOL reset_all = FALSE;
+	if (mOcclusionIndices.isNull())
+	{
+		mOcclusionIndices = new LLOcclusionIndexBuffer(36);
+		reset_all = TRUE;
+	}
+	
+	//fill occlusion vertex buffers
+	for (U32 i = 0; i < mOccludedList.size(); i++)
+	{
+		LLSpatialGroup* group = mOccludedList[i];
+
+		if (group->isState(LLSpatialGroup::OCCLUSION_DIRTY) || reset_all)
+		{
+			LLFastTimer ftm(LLFastTimer::FTM_REBUILD_OCCLUSION_VB);
+
+			if (group->mOcclusionVerts.isNull())
+			{
+				group->mOcclusionVerts = new LLOcclusionVertexBuffer(GL_STREAM_DRAW_ARB);
+			}
+	
+			group->clearState(LLSpatialGroup::OCCLUSION_DIRTY);
+			
+			LLStrider<LLVector3> vert;
+
+			group->mOcclusionVerts->getVertexStrider(vert);
+
+			LLVector3 r = group->mBounds[1]*SG_OCCLUSION_FUDGE + LLVector3(0.1f,0.1f,0.1f);
+
+			for (U32 k = 0; k < 3; k++)
+			{
+				r.mV[k] = llmin(group->mBounds[1].mV[k]+0.25f, r.mV[k]);
+			}
+
+			*vert++ = group->mBounds[0] + r.scaledVec(LLVector3(-1,1,1)); //   0 - left top front
+			*vert++ = group->mBounds[0] + r.scaledVec(LLVector3(1,1,1));  //   1 - right top front
+			*vert++ = group->mBounds[0] + r.scaledVec(LLVector3(1,-1,1)); //   2 - right bottom front
+			*vert++ = group->mBounds[0] + r.scaledVec(LLVector3(-1,-1,1)); //  3 - left bottom front
+
+			*vert++ = group->mBounds[0] + r.scaledVec(LLVector3(1,1,-1)); //  4 - left top back
+			*vert++ = group->mBounds[0] + r.scaledVec(LLVector3(-1,1,-1)); //   5 - right top back
+			*vert++ = group->mBounds[0] + r.scaledVec(LLVector3(-1,-1,-1)); //  6 - right bottom back
+			*vert++ = group->mBounds[0] + r.scaledVec(LLVector3(1,-1,-1)); // 7 -left bottom back
+		}
+	}
+
+/*	for (U32 i = 0; i < mOccludedList.size(); i++)
+	{
+		LLSpatialGroup* group = mOccludedList[i];
+		if (!group->mOcclusionVerts.isNull() && group->mOcclusionVerts->isLocked())
+		{
+			LLFastTimer ftm(LLFastTimer::FTM_REBUILD_OCCLUSION_VB);
+			group->mOcclusionVerts->setBuffer(0);
+		}
+	}*/
+}
+
 void LLSpatialPartition::doOcclusion(LLCamera* camera)
 {
 	LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
 
-	sIgnoreOcclusion = gUseWireframe;
- 	LLFastTimer t(LLFastTimer::FTM_RENDER_OCCLUSION);
+	LLFastTimer t(LLFastTimer::FTM_RENDER_OCCLUSION);
 
 #if LL_OCTREE_PARANOIA_CHECK  
 	LLSpatialGroup* check = (LLSpatialGroup*) mOctree->getListener(0);
@@ -1413,9 +2115,16 @@ void LLSpatialPartition::doOcclusion(LLCamera* camera)
 
 	stop_glerror();
 	
+	U32 num_verts = mOccludedList.size() * 8;
+
+	if (num_verts == 0)
+	{
+		return;
+	}
+
 	//actually perform the occlusion queries
 	LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
-	glDisable(GL_TEXTURE_2D);
+	LLGLDisable(GL_TEXTURE_2D);
 	gPipeline.disableLights();
 	LLGLEnable cull_face(GL_CULL_FACE);
 	LLGLDisable blend(GL_BLEND);
@@ -1424,25 +2133,16 @@ void LLSpatialPartition::doOcclusion(LLCamera* camera)
 	glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
 	glColor4f(1,1,1,1);
 
-	//sort occlusion queries front to back
-	/*for (U32 i = 0; i < mOccludedList.size(); i++)
-	{
-		LLSpatialGroup* group = mOccludedList[i];
-		
-		LLVector3 v = group->mOctreeNode->getCenter()-camera->getOrigin();
-		group->mDistance = v*v;
-	}
-
-	std::sort(mOccludedList.begin(), mOccludedList.end(), dist_greater());
+	mOcclusionIndices->setBuffer(0);
 
-	glClearStencil(0);
-	glClear(GL_STENCIL_BUFFER_BIT);
-	LLGLEnable stencil(GL_STENCIL_TEST);
-	glStencilFunc(GL_GREATER, 1, 0xFFFFFFFF);
-	glStencilOp(GL_KEEP, GL_SET, GL_KEEP);*/
-
-	genBoxList();
+	U32* indicesp = (U32*) mOcclusionIndices->getIndicesPointer();
 
+	glDisableClientState(GL_NORMAL_ARRAY);
+	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+	glDisableClientState(GL_COLOR_ARRAY);
+#if !LL_RELEASE_FOR_DOWNLOAD
+	LLGLState::checkClientArrays(LLVertexBuffer::MAP_VERTEX);
+#endif
 	for (U32 i = 0; i < mOccludedList.size(); i++)
 	{
 #if LL_OCTREE_PARANOIA_CHECK
@@ -1463,32 +2163,10 @@ void LLSpatialPartition::doOcclusion(LLCamera* camera)
 		}
 		else
 		{ //early rejection criteria passed, send some geometry to the query
-			LLVector3 c;
-			LLVector3 r;
-			
-			sg_assert(!group->isState(LLSpatialGroup::DIRTY));
-
-			c = group->mBounds[0];
-			r = group->mBounds[1]*SG_OCCLUSION_FUDGE + LLVector3(0.01f,0.01f,0.01f);
-			for (U32 k = 0; k < 3; k++)
-			{
-				r.mV[k] = llmin(group->mBounds[1].mV[k]+0.25f, r.mV[k]);
-			}
-			
-#if LL_OCTREE_PARANOIA_CHECK
-			LLVector3 e = camera->getOrigin();
-			LLVector3 min = c - r;
-			LLVector3 max = c + r;
-			BOOL outside = FALSE;
-			for (U32 j = 0; j < 3; j++)
-			{
-				outside = outside || (e.mV[j] < min.mV[j] || e.mV[j] > max.mV[j]);
-			}
-			sg_assert(outside);
-#endif
-					
+			group->mOcclusionVerts->setBuffer(LLVertexBuffer::MAP_VERTEX);
 			glBeginQueryARB(GL_SAMPLES_PASSED_ARB, mOcclusionQueries[i]);					
-			drawBox(c,r);
+			glDrawRangeElements(GL_TRIANGLES, 0, 7, 36,
+							GL_UNSIGNED_INT, indicesp);
 			glEndQueryARB(GL_SAMPLES_PASSED_ARB);
 
 			group->setState(LLSpatialGroup::QUERY_OUT);
@@ -1497,10 +2175,11 @@ void LLSpatialPartition::doOcclusion(LLCamera* camera)
 	}
 	stop_glerror();
 	
+	gPipeline.mTrianglesDrawn += mOccludedList.size()*12;
+
 	glFlush();
 
 	glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
-	glEnable(GL_TEXTURE_2D);
 }
 
 class LLOctreeGet : public LLSpatialGroup::OctreeTraveler
@@ -1599,6 +2278,8 @@ S32 LLSpatialPartition::getDrawables(const LLVector3& pos, F32 rad,
 
 S32 LLSpatialPartition::getObjects(const LLVector3& pos, F32 rad, LLDrawable::drawable_set_t &results)
 {
+	LLSpatialGroup* group = (LLSpatialGroup*) mOctree->getListener(0);
+	group->rebound();
 	return getDrawables(pos, rad, results, FALSE);
 }
 
@@ -1607,182 +2288,346 @@ S32 LLSpatialPartition::getLights(const LLVector3& pos, F32 rad, LLDrawable::dra
 	return getDrawables(pos, rad, results, TRUE);
 }
 
-class LLOctreeRenderNonOccluded : public LLOctreeTraveler<LLDrawable>
+void pushVerts(LLSpatialGroup* group, U32 mask)
 {
-public:
-	LLOctreeRenderNonOccluded() {}
-	
-	virtual void traverse(const LLSpatialGroup::OctreeNode* node)
+	LLDrawInfo* params = NULL;
+
+	for (LLSpatialGroup::draw_map_t::iterator i = group->mDrawMap.begin(); i != group->mDrawMap.end(); ++i)
 	{
-		const LLSpatialGroup::OctreeState* state = node->getOctState();
-		LLSpatialGroup* group = (LLSpatialGroup*) node->getListener(0);
+		for (std::vector<LLDrawInfo*>::iterator j = i->second.begin(); j != i->second.end(); ++j)
+		{
+			params = *j;
+			params->mVertexBuffer->setBuffer(mask);
+			U32* indicesp = (U32*) params->mVertexBuffer->getIndicesPointer();
+			glDrawRangeElements(params->mParticle ? GL_POINTS : GL_TRIANGLES, params->mStart, params->mEnd, params->mCount,
+							GL_UNSIGNED_INT, indicesp+params->mOffset);
+		}
+	}
+}
+
+void pushVertsColorCoded(LLSpatialGroup* group, U32 mask)
+{
+	LLDrawInfo* params = NULL;
+
+	LLColor4 colors[] = {
+		LLColor4::green,
+		LLColor4::green1,
+		LLColor4::green2,
+		LLColor4::green3,
+		LLColor4::green4,
+		LLColor4::green5,
+		LLColor4::green6
+	};
 		
-		if (!group->isState(LLSpatialGroup::OCCLUDED | LLSpatialGroup::CULLED))
+	static const U32 col_count = sizeof(colors)/sizeof(LLColor4);
+
+	U32 col = 0;
+
+	for (LLSpatialGroup::draw_map_t::iterator i = group->mDrawMap.begin(); i != group->mDrawMap.end(); ++i)
+	{
+		for (std::vector<LLDrawInfo*>::iterator j = i->second.begin(); j != i->second.end(); ++j)
 		{
-			state->accept(this);
+			params = *j;
+			glColor4f(colors[col].mV[0], colors[col].mV[1], colors[col].mV[2], 0.5f);
+			params->mVertexBuffer->setBuffer(mask);
+			U32* indicesp = (U32*) params->mVertexBuffer->getIndicesPointer();
+			glDrawRangeElements(params->mParticle ? GL_POINTS : GL_TRIANGLES, params->mStart, params->mEnd, params->mCount,
+							GL_UNSIGNED_INT, indicesp+params->mOffset);
+			col = (col+1)%col_count;
+		}
+	}
+}
 
-			for (U32 i = 0; i < state->getChildCount(); i++)
+void renderOctree(LLSpatialGroup* group)
+{
+	//render solid object bounding box, color
+	//coded by buffer usage and activity
+	LLGLDepthTest depth(GL_TRUE, GL_FALSE);
+	glBlendFunc(GL_SRC_ALPHA, GL_ONE);
+	LLVector4 col;
+	if (group->mBuilt > 0.f)
+	{
+		group->mBuilt -= 2.f * gFrameIntervalSeconds;
+		if (group->mBufferUsage == GL_STATIC_DRAW_ARB)
+		{
+			col.setVec(1.0f, 0, 0, group->mBuilt*0.5f);
+		}
+		else 
+		{
+			col.setVec(0.1f,0.1f,1,0.1f);
+			//col.setVec(1.0f, 1.0f, 0, sinf(group->mBuilt*3.14159f)*0.5f);
+		}
+
+		if (group->mBufferUsage != GL_STATIC_DRAW_ARB)
+		{
+			if (group->mBufferUsage == GL_DYNAMIC_DRAW_ARB)
 			{
-				traverse(state->getChild(i));
+				glColor4f(1,0,0,group->mBuilt);
 			}
-			
-			/*if (state->getElementCount() == 0)
+			else
 			{
-				return;
-			}*/
+				glColor4f(1,1,0,group->mBuilt);
+			}
 
-			//draw tight fit bounding box for spatial group
-			if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_OCTREE))
+			LLGLDepthTest gl_depth(FALSE, FALSE);
+			glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+			for (LLSpatialGroup::element_iter i = group->getData().begin(); i != group->getData().end(); ++i)
 			{
-				if (node->getElementCount() == 0)
+				LLDrawable* drawable = *i;
+				for (S32 j = 0; j < drawable->getNumFaces(); j++)
 				{
-					return;
-				}
-				
-				if (node->hasLeafState())
-				{
-					glColor4f(1,1,1,1);
-				}
-				else
-				{
-					glColor4f(0,1,1,1);
+					LLFace* face = drawable->getFace(j);
+					if (gFrameTimeSeconds - face->mLastUpdateTime < 0.5f && face->mVertexBuffer.notNull())
+					{ 
+						face->mVertexBuffer->setBuffer(LLVertexBuffer::MAP_VERTEX);
+						//drawBox((face->mExtents[0] + face->mExtents[1])*0.5f,
+						//		(face->mExtents[1]-face->mExtents[0])*0.5f);
+						glDrawElements(GL_TRIANGLES, face->getIndicesCount(), GL_UNSIGNED_INT, 
+							((U32*) face->mVertexBuffer->getIndicesPointer())+face->getIndicesStart());
+					}
 				}
+			}
+			glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+		}
+	}
+	else
+	{
+		if (group->mBufferUsage == GL_STATIC_DRAW_ARB && !group->getData().empty() 
+			&& group->mSpatialPartition->mRenderByGroup)
+		{
+			col.setVec(0.8f, 0.4f, 0.1f, 0.1f);
+		}
+		else
+		{
+			col.setVec(0.1f, 0.1f, 1.f, 0.1f);
+		}
+	}
 
+	glColor4fv(col.mV);
+	drawBox(group->mObjectBounds[0], group->mObjectBounds[1]*1.01f+LLVector3(0.001f, 0.001f, 0.001f));
+	glDepthMask(GL_TRUE);
+	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
 
-				LLVector3 pos; 
-				LLVector3 size;
-				
-				pos = group->mObjectBounds[0];
-				size = group->mObjectBounds[1];
-				
-				LLVector3 v1 = size.scaledVec(LLVector3( 1, 1,1));
-				LLVector3 v2 = size.scaledVec(LLVector3(-1, 1,1));
-				LLVector3 v3 = size.scaledVec(LLVector3(-1,-1,1));
-				LLVector3 v4 = size.scaledVec(LLVector3( 1,-1,1));
-
-				glBegin(GL_LINE_LOOP); //top
-				glVertex3fv((pos+v1).mV);
-				glVertex3fv((pos+v2).mV);
-				glVertex3fv((pos+v3).mV);
-				glVertex3fv((pos+v4).mV);
-				glEnd();
-
-				glBegin(GL_LINE_LOOP); //bottom
-				glVertex3fv((pos-v1).mV);
-				glVertex3fv((pos-v2).mV);
-				glVertex3fv((pos-v3).mV);
-				glVertex3fv((pos-v4).mV);
-				glEnd();
-
-
-				glBegin(GL_LINES);
-
-				//right
-				glVertex3fv((pos+v1).mV);
-				glVertex3fv((pos-v3).mV);
-						
-				glVertex3fv((pos+v4).mV);
-				glVertex3fv((pos-v2).mV);
-
-				//left
-				glVertex3fv((pos+v2).mV);
-				glVertex3fv((pos-v4).mV);
-
-				glVertex3fv((pos+v3).mV);
-				glVertex3fv((pos-v1).mV);
+	//draw opaque outline
+	glColor4f(col.mV[0], col.mV[1], col.mV[2], 1.f);
+	drawBoxOutline(group->mObjectBounds[0], group->mObjectBounds[1]);
 
-				glEnd();
+	if (group->mOctreeNode->hasLeafState())
+	{
+		glColor4f(1,1,1,1);
+	}
+	else
+	{
+		glColor4f(0,1,1,1);
+	}
+					
+	drawBoxOutline(group->mBounds[0],group->mBounds[1]);
+	
+//	LLSpatialGroup::OctreeNode* node = group->mOctreeNode;
+//	glColor4f(0,1,0,1);
+//	drawBoxOutline(LLVector3(node->getCenter()), LLVector3(node->getSize()));
+}
 
-				LLVector3 nc = LLVector3(node->getCenter());
-				LLVector3 ns = LLVector3(node->getSize());
+void renderVisibility(LLSpatialGroup* group)
+{
+	LLGLEnable blend(GL_BLEND);
+	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+	LLGLEnable cull(GL_CULL_FACE);
+	glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+	{
+		LLGLDepthTest depth_under(GL_TRUE, GL_FALSE, GL_GREATER);
+		glColor4f(0, 0.5f, 0, 0.5f);
+		pushVerts(group, LLVertexBuffer::MAP_VERTEX);
+	}
 
-				LLVector3 nv1 = ns.scaledVec(LLVector3( 1, 1,1));
-				LLVector3 nv2 = ns.scaledVec(LLVector3(-1, 1,1));
-				LLVector3 nv3 = ns.scaledVec(LLVector3(-1,-1,1));
-				LLVector3 nv4 = ns.scaledVec(LLVector3( 1,-1,1));
+	{
+		LLGLDepthTest depth_over(GL_TRUE, GL_FALSE, GL_LEQUAL);
+		pushVertsColorCoded(group, LLVertexBuffer::MAP_VERTEX);
 
-				
+		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
 
-				/*if (node->getElementCount() > 0)
-				{
-					//spokes
-				    glColor4f(1,1,0,1);
-					glVertex3fv(pos.mV);
-					glColor4f(1,1,0,0);
-					glVertex3fv(nc.mV);
-
-					glColor4f(1,1,0,1); glVertex3fv((pos+v1).mV); glColor4f(1,1,0,0);	glVertex3fv(pos.mV);
-					glColor4f(1,1,0,1); glVertex3fv((pos-v1).mV); glColor4f(1,1,0,0);	glVertex3fv(pos.mV);
-					glColor4f(1,1,0,1); glVertex3fv((pos+v2).mV); glColor4f(1,1,0,0);	glVertex3fv(pos.mV);
-					glColor4f(1,1,0,1); glVertex3fv((pos-v2).mV); glColor4f(1,1,0,0);	glVertex3fv(pos.mV);
-					glColor4f(1,1,0,1); glVertex3fv((pos+v3).mV); glColor4f(1,1,0,0);	glVertex3fv(pos.mV);
-					glColor4f(1,1,0,1); glVertex3fv((pos-v3).mV); glColor4f(1,1,0,0);	glVertex3fv(pos.mV);
-					glColor4f(1,1,0,1); glVertex3fv((pos+v4).mV); glColor4f(1,1,0,0);	glVertex3fv(pos.mV);
-					glColor4f(1,1,0,1); glVertex3fv((pos-v4).mV); glColor4f(1,1,0,0);	glVertex3fv(pos.mV);
-				}*/
+		pushVertsColorCoded(group, LLVertexBuffer::MAP_VERTEX);
+	}
+}
 
-				
+void renderBoundingBox(LLDrawable* drawable)
+{
+	if (drawable->isSpatialBridge())
+	{
+		glColor4f(1,0.5f,0,1);
+	}
+	else if (drawable->getVOVolume())
+	{
+		if (drawable->isRoot())
+		{
+			glColor4f(1,1,0,1);
+		}
+		else
+		{
+			glColor4f(0,1,0,1);
+		}
+	}
+	else if (drawable->getVObj())
+	{
+		switch (drawable->getVObj()->getPCode())
+		{
+			case LLViewerObject::LL_VO_SURFACE_PATCH:
+					glColor4f(0,1,1,1);
+					break;
+			case LLViewerObject::LL_VO_CLOUDS:
+					glColor4f(0.5f,0.5f,0.5f,1.0f);
+					break;
+			case LLViewerObject::LL_VO_PART_GROUP:
+					glColor4f(0,0,1,1);
+					break;
+			case LLViewerObject::LL_VO_WATER:
+					glColor4f(0,0.5f,1,1);
+					break;
+			case LL_PCODE_LEGACY_TREE:
+					glColor4f(0,0.5f,0,1);
+			default:
+					glColor4f(1,0,1,1);
+					break;
+		}
+	}
+	else 
+	{
+		glColor4f(1,0,0,1);
+	}
 
-				/*glColor4f(0,1,0,1);
-				glBegin(GL_LINE_LOOP); //top
-				glVertex3fv((nc+nv1).mV);
-				glVertex3fv((nc+nv2).mV);
-				glVertex3fv((nc+nv3).mV);
-				glVertex3fv((nc+nv4).mV);
-				glEnd();
+	const LLVector3* ext;
+	LLVector3 pos, size;
 
-				glBegin(GL_LINE_LOOP); //bottom
-				glVertex3fv((nc-nv1).mV);
-				glVertex3fv((nc-nv2).mV);
-				glVertex3fv((nc-nv3).mV);
-				glVertex3fv((nc-nv4).mV);
-				glEnd();
+	//render face bounding boxes
+	for (S32 i = 0; i < drawable->getNumFaces(); i++)
+	{
+		LLFace* facep = drawable->getFace(i);
 
+		ext = facep->mExtents;
 
-				glBegin(GL_LINES);
+		if (ext[0].isExactlyZero() && ext[1].isExactlyZero())
+		{
+			continue;
+		}
+		pos = (ext[0] + ext[1]) * 0.5f;
+		size = (ext[1] - ext[0]) * 0.5f;
+		drawBoxOutline(pos,size);
+	}
 
-				//right
-				glVertex3fv((nc+nv1).mV);
-				glVertex3fv((nc-nv3).mV);
-						
-				glVertex3fv((nc+nv4).mV);
-				glVertex3fv((nc-nv2).mV);
+	//render drawable bounding box
+	ext = drawable->getSpatialExtents();
 
-				//left
-				glVertex3fv((nc+nv2).mV);
-				glVertex3fv((nc-nv4).mV);
+	pos = (ext[0] + ext[1]) * 0.5f;
+	size = (ext[1] - ext[0]) * 0.5f;
+	
+	drawBoxOutline(pos,size);
+}
 
-				glVertex3fv((nc+nv3).mV);
-				glVertex3fv((nc-nv1).mV);
-				glEnd();*/
+void renderTexturePriority(LLDrawable* drawable)
+{
+	for (int face=0; face<drawable->getNumFaces(); ++face)
+	{
+		LLFace *facep = drawable->getFace(face);
+		
+		LLVector4 cold(0,0,0.25f);
+		LLVector4 hot(1,0.25f,0.25f);
+	
+		LLVector4 boost_cold(0,0,0,0);
+		LLVector4 boost_hot(0,1,0,1);
+		
+		LLGLDisable blend(GL_BLEND);
+		
+		//LLViewerImage* imagep = facep->getTexture();
+		//if (imagep)
+		{
+	
+			//F32 vsize = LLVOVolume::getTextureVirtualSize(facep);
+			//F32 vsize = imagep->mMaxVirtualSize;
+			F32 vsize = facep->getPixelArea();
 
-				glLineWidth(1);
-				
-				glDepthMask(GL_FALSE);
-				glBlendFunc(GL_SRC_ALPHA, GL_ONE);
-				glColor4f(0.1f,0.1f,1,0.1f);
-				drawBox(group->mObjectBounds[0], group->mObjectBounds[1]*1.01f+LLVector3(0.001f, 0.001f, 0.001f));
-				glDepthMask(GL_TRUE);
-				glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+			if (vsize > sCurMaxTexPriority)
+			{
+				sCurMaxTexPriority = vsize;
 			}
+			
+			F32 t = vsize/sLastMaxTexPriority;
+			
+			LLVector4 col = lerp(cold, hot, t);
+			glColor4fv(col.mV);
 		}
-		/*else
+		//else
+		//{
+		//	glColor4f(1,0,1,1);
+		//}
+
+		
+		
+		LLVector3 center = (facep->mExtents[1]+facep->mExtents[0])*0.5f;
+		LLVector3 size = (facep->mExtents[1]-facep->mExtents[0])*0.5f + LLVector3(0.01f, 0.01f, 0.01f);
+		drawBox(center, size);
+		
+		/*S32 boost = imagep->getBoostLevel();
+		if (boost)
+		{
+			F32 t = (F32) boost / (F32) (LLViewerImage::BOOST_MAX_LEVEL-1);
+			LLVector4 col = lerp(boost_cold, boost_hot, t);
+			LLGLEnable blend_on(GL_BLEND);
+			glBlendFunc(GL_SRC_ALPHA, GL_ONE);
+			glColor4fv(col.mV);
+			drawBox(center, size);
+			glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+		}*/
+	}
+}
+
+void renderPoints(LLDrawable* drawablep)
+{
+	LLGLDepthTest depth(GL_FALSE, GL_FALSE);
+	glBegin(GL_POINTS);
+	glColor3f(1,1,1);
+	LLVector3 center(drawablep->getPositionGroup());
+	for (S32 i = 0; i < drawablep->getNumFaces(); i++)
+	{
+		glVertex3fv(drawablep->getFace(i)->mCenterLocal.mV);
+	}
+	glEnd();
+}
+
+class LLOctreeRenderNonOccluded : public LLOctreeTraveler<LLDrawable>
+{
+public:
+	LLOctreeRenderNonOccluded() {}
+	
+	virtual void traverse(const LLSpatialGroup::OctreeNode* node)
+	{
+		const LLSpatialGroup::OctreeState* state = node->getOctState();
+		LLSpatialGroup* group = (LLSpatialGroup*) node->getListener(0);
+		
+	
+		if ((!gPipeline.sUseOcclusion || !group->isState(LLSpatialGroup::OCCLUDED)) &&
+			!group->isState(LLSpatialGroup::CULLED))
 		{
-			//occlusion paranoia check
-			const LLSpatialGroup::OctreeNode* parent = node;
-			while (parent != NULL)
+			state->accept(this);
+
+			for (U32 i = 0; i < state->getChildCount(); i++)
 			{
-				LLSpatialGroup* grp = (LLSpatialGroup*) parent->getListener(0);
-				if (grp->isState(LLSpatialGroup::ACTIVE_OCCLUSION))
-				{
-					return;
-				}
-				parent = (const LLSpatialGroup::OctreeNode*) parent->getParent();
+				traverse(state->getChild(i));
+			}
+			
+			//draw tight fit bounding boxes for spatial group
+			if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_OCTREE))
+			{	
+				renderOctree(group);
 			}
 
-			glColor4f(1,0,1,1);
-			drawBox(group->mBounds[0], group->mBounds[1]);
-		}*/
+			//render visibility wireframe
+			if (group->mSpatialPartition->mRenderByGroup &&
+				gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_OCCLUSION) &&
+				!group->isState(LLSpatialGroup::GEOM_DIRTY))
+			{
+				renderVisibility(group);
+			}
+		}
 	}
 
 	virtual void visit(const LLSpatialGroup::OctreeState* branch)
@@ -1797,276 +2642,24 @@ public:
 		LLVector3 nodeCenter = group->mBounds[0];
 		LLVector3 octCenter = LLVector3(group->mOctreeNode->getCenter());
 
-		if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_OCTREE))
-		{
-			glBegin(GL_LINES);
-			glColor4f(1,0.5f,0,1);
-			glVertex3fv(nodeCenter.mV);
-			glColor4f(0,1,1,0);
-			glVertex3fv(octCenter.mV);
-			glEnd();
-		}
-
 		for (LLSpatialGroup::OctreeState::const_element_iter i = branch->getData().begin(); i != branch->getData().end(); ++i)
 		{
 			LLDrawable* drawable = *i;
-			
-			if (drawable->isSpatialBridge())
-			{
-				LLSpatialBridge* bridge = (LLSpatialBridge*) drawable;
-				glPushMatrix();
-				glMultMatrixf((F32*)bridge->mDrawable->getWorldMatrix().mMatrix);
-				traverse(bridge->mOctree);
-				glPopMatrix();
-			}
-			
-			if (!drawable->isVisible())
-			{
-				continue;
-			}
-				
+						
 			if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_BBOXES))
 			{
-				if (drawable->isSpatialBridge())
-				{
-					glColor4f(1,0.5f,0,1);
-				}
-				else if (drawable->getVOVolume())
-				{
-					if (drawable->isRoot())
-					{
-						glColor4f(1,1,0,1);
-					}
-					else
-					{
-						glColor4f(0,1,0,1);
-					}
-				}
-				else if (drawable->getVObj())
-				{
-					switch (drawable->getVObj()->getPCode())
-					{
-						case LLViewerObject::LL_VO_SURFACE_PATCH:
-								glColor4f(0,1,1,1);
-								break;
-						case LLViewerObject::LL_VO_CLOUDS:
-								glColor4f(0.5f,0.5f,0.5f,1.0f);
-								break;
-						case LLViewerObject::LL_VO_PART_GROUP:
-								glColor4f(0,0,1,1);
-								break;
-						case LLViewerObject::LL_VO_WATER:
-								glColor4f(0,0.5f,1,1);
-								break;
-						case LL_PCODE_LEGACY_TREE:
-								glColor4f(0,0.5f,0,1);
-						default:
-								glColor4f(1,0,1,1);
-								break;
-					}
-				}
-				else 
-				{
-					glColor4f(1,0,0,1);
-				}
-				
-										
-				const LLVector3* ext = drawable->getSpatialExtents();
-				
-				LLVector3 pos = (ext[0] + ext[1]) * 0.5f;
-				LLVector3 size = (ext[1] - ext[0]) * 0.5f;
-				
-				LLVector3 v1 = size.scaledVec(LLVector3( 1, 1,1));
-				LLVector3 v2 = size.scaledVec(LLVector3(-1, 1,1));
-				LLVector3 v3 = size.scaledVec(LLVector3(-1,-1,1));
-				LLVector3 v4 = size.scaledVec(LLVector3( 1,-1,1));
-
-				glBegin(GL_LINE_LOOP); //top
-				glVertex3fv((pos+v1).mV);
-				glVertex3fv((pos+v2).mV);
-				glVertex3fv((pos+v3).mV);
-				glVertex3fv((pos+v4).mV);
-				glEnd();
-
-				glBegin(GL_LINE_LOOP); //bottom
-				glVertex3fv((pos-v1).mV);
-				glVertex3fv((pos-v2).mV);
-				glVertex3fv((pos-v3).mV);
-				glVertex3fv((pos-v4).mV);
-				glEnd();
-
-
-				glBegin(GL_LINES);
-
-				//right
-				glVertex3fv((pos+v1).mV);
-				glVertex3fv((pos-v3).mV);
-						
-				glVertex3fv((pos+v4).mV);
-				glVertex3fv((pos-v2).mV);
-
-				//left
-				glVertex3fv((pos+v2).mV);
-				glVertex3fv((pos-v4).mV);
-
-				glVertex3fv((pos+v3).mV);
-				glVertex3fv((pos-v1).mV);
-
-				glEnd();
-
-				//render space partition trace
-				glBegin(GL_LINE_STRIP);
-				glColor4f(1,0,0,1);
-				glVertex3fv(pos.mV);
-				glColor4f(0,1,0,1);
-				glVertex3dv(drawable->getPositionGroup().mdV);
-				glColor4f(0,0,1,1);
-				glVertex3fv(nodeCenter.mV);
-				glColor4f(1,1,0,1);
-				glVertex3fv(octCenter.mV);
-				glEnd();
+				renderBoundingBox(drawable);			
 			}
 			
-			if (drawable->getVOVolume() && gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_FACE_CHAINS | LLPipeline::RENDER_DEBUG_TEXTURE_PRIORITY))
+			if (drawable->getVOVolume() && gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_PRIORITY))
 			{
-				glLineWidth(3);
-				
-				for (int face=0; face<drawable->getNumFaces(); ++face)
-				{
-					LLFace *facep = drawable->getFace(face);
-					
-					if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_FACE_CHAINS))
-					{
-						LLGLDepthTest depth(GL_FALSE);
-						if (facep->mNextFace)
-						{
-							glBegin(GL_LINE_STRIP);
-
-							if (facep->isState(LLFace::GLOBAL))
-							{
-								glColor4f(0,1,0,1);
-							}
-							else
-							{
-								glColor4f(1,0.5f,0.25f,1);
-							}
-							
-							if (drawable->isActive())
-							{
-								glVertex3fv(facep->mCenterLocal.mV);
-								glVertex3fv(facep->mNextFace->mCenterLocal.mV);
-							}
-							else
-							{
-								glVertex3fv(facep->mCenterAgent.mV);
-								glVertex3fv(facep->mNextFace->mCenterAgent.mV);
-							}
-							
-							glEnd();
-						}
-						else
-						{
-							glPointSize(5);
-							glBegin(GL_POINTS);
-							
-							if (!facep->isState(LLFace::GLOBAL))
-							{
-								glColor4f(1,0.75f,0,1);
-								glVertex3fv(facep->mCenterLocal.mV);
-							}
-							else
-							{
-								glColor4f(0,0.75f,1,1);
-								glVertex3fv(facep->mCenterAgent.mV);
-							}
-							
-							glEnd();
-							glPointSize(1);
-						}
-					}
-					
-					if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_PRIORITY))
-					{
-						LLVector4 cold(0,0,0.25f);
-						LLVector4 hot(1,0.25f,0.25f);
-					
-						LLVector4 boost_cold(0,0,0,0);
-						LLVector4 boost_hot(0,1,0,1);
-						
-						LLGLDisable blend(GL_BLEND);
-						
-						LLViewerImage* imagep = facep->getTexture();
-						if (imagep)
-						{
-					
-							//F32 vsize = LLVOVolume::getTextureVirtualSize(facep);
-							F32 vsize = imagep->mMaxVirtualSize;
-							
-							if (vsize > sCurMaxTexPriority)
-							{
-								sCurMaxTexPriority = vsize;
-							}
-							
-							F32 t = vsize/sLastMaxTexPriority;
-							
-							LLVector4 col = lerp(cold, hot, t);
-							glColor4fv(col.mV);
-						}
-						else
-						{
-							glColor4f(1,0,1,1);
-						}
-						
-						LLVector3 center = (facep->mExtents[1]+facep->mExtents[0])*0.5f;
-						LLVector3 size = (facep->mExtents[1]-facep->mExtents[0])*0.5f + LLVector3(0.01f, 0.01f, 0.01f);
-						drawBox(center, size);
-						
-						S32 boost = imagep->getBoostLevel();
-						if (boost)
-						{
-							F32 t = (F32) boost / (F32) (LLViewerImage::BOOST_MAX_LEVEL-1);
-							LLVector4 col = lerp(boost_cold, boost_hot, t);
-							LLGLEnable blend_on(GL_BLEND);
-							glBlendFunc(GL_SRC_ALPHA, GL_ONE);
-							glColor4fv(col.mV);
-							drawBox(center, size);
-							glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-						}
-							
-					}
-				}
+				renderTexturePriority(drawable);
 			}
-			
+
 			if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_POINTS))
 			{
-				glPointSize(4);
-				glColor4f(1,1,1,1);
-				glBegin(GL_POINTS);
-				S32 num_faces = drawable->getNumFaces();
-				for (S32 i = 0; i < num_faces; i++)
-				{
-					LLStrider<LLVector3> vertices;
-					drawable->getFace(i)->getVertices(vertices);
-					
-					LLFace* face = drawable->getFace(i);
-					
-					for (S32 v = 0; v < (S32)drawable->getFace(i)->getGeomCount(); v++)
-					{
-						if (!face->getDrawable()->isActive())
-						{
-							//glVertex3fv(vertices[v].mV);
-						}
-						else
-						{
-							glVertex3fv((vertices[v]*face->getRenderMatrix()).mV);
-						}
-					}
-				}
-				glEnd();
-				glPointSize(1);
+				renderPoints(drawable);
 			}
-			
-			glLineWidth(1);
 		}
 	}
 };
@@ -2077,7 +2670,6 @@ void LLSpatialPartition::renderDebug()
 									  LLPipeline::RENDER_DEBUG_OCCLUSION |
 									  LLPipeline::RENDER_DEBUG_BBOXES |
 									  LLPipeline::RENDER_DEBUG_POINTS |
-									  LLPipeline::RENDER_DEBUG_FACE_CHAINS |
 									  LLPipeline::RENDER_DEBUG_TEXTURE_PRIORITY))
 	{
 		return;
@@ -2086,7 +2678,7 @@ void LLSpatialPartition::renderDebug()
 	if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_PRIORITY))
 	{
 		//sLastMaxTexPriority = lerp(sLastMaxTexPriority, sCurMaxTexPriority, gFrameIntervalSeconds);
-		sLastMaxTexPriority = sCurMaxTexPriority;
+		sLastMaxTexPriority = (F32) gCamera->getScreenPixelArea();
 		sCurMaxTexPriority = 0.f;
 	}
 
@@ -2100,78 +2692,50 @@ void LLSpatialPartition::renderDebug()
 		
 	LLOctreeRenderNonOccluded render_debug;
 	render_debug.traverse(mOctree);
-	
-	LLGLDisable cull_face(GL_CULL_FACE);
 
-	{
-		LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
-		
-		//draw frustum
-		glColor4f(0,0,1,0.5f);
-		glBegin(GL_QUADS);
-		//glVertex3fv(gCamera->mAgentFrustum[0].mV);
-		//glVertex3fv(gCamera->mAgentFrustum[1].mV);
-		//glVertex3fv(gCamera->mAgentFrustum[2].mV);
-		//glVertex3fv(gCamera->mAgentFrustum[3].mV);
+	LLGLDisable cull_face(GL_CULL_FACE);
 	
-		//glVertex3fv(gCamera->mAgentFrustum[4].mV);
-		//glVertex3fv(gCamera->mAgentFrustum[5].mV);
-		//glVertex3fv(gCamera->mAgentFrustum[6].mV);
-		//glVertex3fv(gCamera->mAgentFrustum[7].mV);
-
-		glVertex3fv(gCamera->mAgentFrustum[0].mV);
-		glVertex3fv(gCamera->mAgentFrustum[1].mV);
-		glVertex3fv(gCamera->mAgentFrustum[5].mV);
-		glVertex3fv(gCamera->mAgentFrustum[4].mV);
-
-		glVertex3fv(gCamera->mAgentFrustum[1].mV);
-		glVertex3fv(gCamera->mAgentFrustum[2].mV);
-		glVertex3fv(gCamera->mAgentFrustum[6].mV);
-		glVertex3fv(gCamera->mAgentFrustum[5].mV);
-
-		glVertex3fv(gCamera->mAgentFrustum[2].mV);
-		glVertex3fv(gCamera->mAgentFrustum[3].mV);
-		glVertex3fv(gCamera->mAgentFrustum[7].mV);
-		glVertex3fv(gCamera->mAgentFrustum[6].mV);
-
-		glVertex3fv(gCamera->mAgentFrustum[3].mV);
-		glVertex3fv(gCamera->mAgentFrustum[0].mV);
-		glVertex3fv(gCamera->mAgentFrustum[4].mV);
-		glVertex3fv(gCamera->mAgentFrustum[7].mV);
-
-		glEnd();
-	}
-
-	if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_OCCLUSION))
+	if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_OCCLUSION) && !mOccludedList.empty() &&
+		mOcclusionIndices.notNull())
 	{
 		LLGLDisable fog(GL_FOG);
 		LLGLDepthTest gls_depth(GL_FALSE);
 		glBlendFunc(GL_SRC_ALPHA, GL_ONE);
+		mOcclusionIndices->setBuffer(0);
+		U32* indicesp = (U32*) mOcclusionIndices->getIndicesPointer();
 
-		for (std::vector<LLSpatialGroup*>::iterator i = mOccludedList.begin(); i != mOccludedList.end(); ++i)
+		LLGLEnable blend(GL_BLEND);
+		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+		LLGLEnable cull(GL_CULL_FACE);
+		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+
+		for (U32 i = 0; i < mOccludedList.size(); i++)
 		{	//draw occluded nodes
-			LLSpatialGroup* node = *i;
-			if (node->isDead())
+			LLSpatialGroup* node = mOccludedList[i];
+			if (node->isDead() ||
+				!node->isState(LLSpatialGroup::OCCLUDED) ||
+				node->mOcclusionVerts.isNull())
 			{
 				continue;
 			}
-			if (!node->isState(LLSpatialGroup::OCCLUDED))
+			
+			node->mOcclusionVerts->setBuffer(LLVertexBuffer::MAP_VERTEX);
 			{
-				continue;
+				LLGLDepthTest depth_under(GL_TRUE, GL_FALSE, GL_GREATER);
+				glColor4f(0.5, 0.5f, 0, 0.25f);
+				glDrawRangeElements(GL_TRIANGLES, 0, 7, 36,
+							GL_UNSIGNED_INT, indicesp);
 			}
-			else
+
 			{
-				glColor4f(0.25f,0.125f,0.1f,0.125f);
+				LLGLDepthTest depth_over(GL_TRUE, GL_FALSE, GL_LEQUAL);
+				glColor4f(0.0,1.0f,1.0f,1.0f);
+				glDrawRangeElements(GL_TRIANGLES, 0, 7, 36,
+							GL_UNSIGNED_INT, indicesp);
 			}
-			LLVector3 c;
-			LLVector3 r;
-
-			c = node->mBounds[0];
-			r = node->mBounds[1]*SG_OCCLUSION_FUDGE + LLVector3(0.01f,0.01f,0.01f);;
-			
-			drawBox(c,r);
 		}
-		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
 	}	
 }
 
@@ -2253,3 +2817,19 @@ LLDrawable*	LLSpatialPartition::pickDrawable(const LLVector3& start, const LLVec
 	collision.setVec(pick.mEnd);
 	return ret;
 }
+
+LLDrawInfo::LLDrawInfo(U32 start, U32 end, U32 count, U32 offset, 
+					   LLViewerImage* texture, LLVertexBuffer* buffer,
+					   BOOL fullbright, U8 bump, BOOL particle, F32 part_size)
+: mStart(start), mEnd(end), mCount(count), mOffset(offset), 
+  mTexture(texture), mVertexBuffer(buffer),
+  mFullbright(fullbright), mBump(bump),
+  mParticle(particle), mPartSize(part_size),
+  mVSize(0.f), mTextureMatrix(NULL)
+{
+}
+
+LLVertexBuffer* LLGeometryManager::createVertexBuffer(U32 type_mask, U32 usage)
+{
+	return new LLVertexBuffer(type_mask, usage);
+}
diff --git a/indra/newview/llspatialpartition.h b/indra/newview/llspatialpartition.h
index 012e3a9e82b32ffd327c542914f562a978ea455b..10312ba0e6a656a1cd22d6398502f5194bdbd9ad 100644
--- a/indra/newview/llspatialpartition.h
+++ b/indra/newview/llspatialpartition.h
@@ -14,26 +14,76 @@
 #include "llmemory.h"
 #include "lldrawable.h"
 #include "lloctree.h"
+#include "llvertexbuffer.h"
 #include "llgltypes.h"
+#include "llcubemap.h"
 
 #include <queue>
 
-class LLCullInfo
+#define SG_STATE_INHERIT_MASK (CULLED | OCCLUDED)
+#define SG_INITIAL_STATE_MASK (OCCLUSION_DIRTY | DIRTY | GEOM_DIRTY)
+
+class LLSpatialPartition;
+class LLSpatialBridge;
+
+class LLDrawInfo
 {
 public:
-	LLVector3 mPos;
-	F32 mRadius;
-	LLPointer<LLDrawable> mDrawablep;
-};
+	LLDrawInfo(U32 start, U32 end, U32 count, U32 offset, 
+				LLViewerImage* image, LLVertexBuffer* buffer, 
+				BOOL fullbright = FALSE, U8 bump = 0, BOOL particle = FALSE, F32 part_size = 0);
+	
+	LLPointer<LLVertexBuffer> mVertexBuffer;
+	LLPointer<LLViewerImage> mTexture;
+	LLPointer<LLCubeMap> mReflectionMap;
+	const LLMatrix4* mTextureMatrix;
+	U32 mStart;
+	U32 mEnd;
+	U32 mCount;
+	U32 mOffset;
+	BOOL mFullbright;
+	U8 mBump;
+	BOOL mParticle;
+	F32 mPartSize;
+	F32 mVSize;
+	
+	struct CompareTexture
+	{
+		bool operator()(const LLDrawInfo& lhs, const LLDrawInfo& rhs)
+		{
+			return lhs.mTexture > rhs.mTexture;
+		}
+	};
 
-#define SG_STATE_INHERIT_MASK (CULLED | OCCLUDED)
-class LLSpatialPartition;
+	struct CompareTexturePtr
+	{
+		bool operator()(const LLDrawInfo* const& lhs, const LLDrawInfo* const& rhs)
+		{
+		
+			return lhs == NULL || rhs == NULL || lhs->mTexture > rhs->mTexture;
+		}
+	};
+
+	struct CompareBump
+	{
+		bool operator()(const LLDrawInfo* const& lhs, const LLDrawInfo* const& rhs)
+		{
+			return lhs == NULL || rhs == NULL || lhs->mBump > rhs->mBump;
+		}
+	};
+};
 
 class LLSpatialGroup : public LLOctreeListener<LLDrawable>
 {
 	friend class LLSpatialPartition;
 public:
 
+	typedef std::vector<LLPointer<LLSpatialGroup> > sg_vector_t;
+	typedef std::set<LLPointer<LLSpatialGroup> > sg_set_t;
+	typedef std::vector<LLPointer<LLSpatialBridge> > bridge_list_t;
+	typedef std::map<U32, std::vector<LLDrawInfo*> > draw_map_t;
+	typedef std::map<LLPointer<LLViewerImage>, LLPointer<LLVertexBuffer> > buffer_map_t;
+
 	typedef LLOctreeListener<LLDrawable>	BaseType;
 	typedef LLOctreeListener<LLDrawable>	OctreeListener;
 	typedef LLTreeNode<LLDrawable>			TreeNode;
@@ -41,6 +91,24 @@ public:
 	typedef LLOctreeRoot<LLDrawable>		OctreeRoot;
 	typedef LLOctreeState<LLDrawable>		OctreeState;
 	typedef LLOctreeTraveler<LLDrawable>	OctreeTraveler;
+	typedef LLOctreeState<LLDrawable>::element_iter element_iter;
+	typedef LLOctreeState<LLDrawable>::element_list element_list;
+
+	struct CompareDistanceGreater
+	{
+		bool operator()(const LLSpatialGroup* const& lhs, const LLSpatialGroup* const& rhs)
+		{
+			return lhs->mDistance > rhs->mDistance;
+		}
+	};
+
+	struct CompareDepthGreater
+	{
+		bool operator()(const LLSpatialGroup* const& lhs, const LLSpatialGroup* const& rhs)
+		{
+			return lhs->mDepth > rhs->mDepth;
+		}
+	};
 
 	typedef enum
 	{
@@ -56,10 +124,18 @@ public:
 		RESHADOW_QUEUE			= 0x00000200,
 		DIRTY					= 0x00000400,
 		OBJECT_DIRTY			= 0x00000800,
-		DISCARD_QUERY			= 0x00001000,
-		QUERY_OUT				= 0x00002000,
-		OCCLUDING				= 0x00004000,
-		SKIP_FRUSTUM_CHECK		= 0x00008000,
+		GEOM_DIRTY				= 0x00001000,
+		MATRIX_DIRTY			= 0x00002000,
+		ALPHA_DIRTY				= 0x00004000,
+		DISCARD_QUERY			= 0x00008000,
+		QUERY_OUT				= 0x00010000,
+		OCCLUDING				= 0x00020000,
+		SKIP_FRUSTUM_CHECK		= 0x00040000,
+		OCCLUSION_DIRTY			= 0x00080000,
+		BELOW_WATER				= 0x00100000,
+		IN_GEOMETRY_QUEUE		= 0x00200000,
+		IN_IMAGE_QUEUE		= 0x00400000,
+		IMAGE_DIRTY				= 0x00800000,
 	} eSpatialState;
 
 	typedef enum
@@ -73,17 +149,19 @@ public:
 	BOOL safeToDelete();
 	virtual ~LLSpatialGroup();
 
-	S32 getCount() const					{ return mObjects.size(); }
 	BOOL isDead()							{ return isState(DEAD); }
 	BOOL isState(U32 state) const			{ return mState & state ? TRUE : FALSE; }
 	U32 getState()							{ return mState; }
 	void setState(U32 state)				{ mState |= state; }
 	void clearState(U32 state)				{ mState &= ~state; }
 	
+	void clearDrawMap();
 	void validate();
-
+	void validateDrawMap();
+	
 	void setState(U32 state, S32 mode);
 
+	LLSpatialGroup* getParent();
 
 	void clearState(U32 state, S32 mode);
 	BOOL addObject(LLDrawable *drawablep, BOOL add_all = FALSE, BOOL from_octree = FALSE);
@@ -94,8 +172,15 @@ public:
 	BOOL boundObjects(BOOL empty, LLVector3& newMin, LLVector3& newMax);
 	void unbound();
 	BOOL rebound();
+	
+	void updateDistance(LLCamera& camera);
 	BOOL changeLOD();
-		
+	void rebuildGeom();
+	void makeStatic();
+	
+	void dirtyGeom() { setState(GEOM_DIRTY); }
+	element_list& getData() { return mOctreeNode->getOctState()->getData(); }
+
 	 //LISTENER FUNCTIONS
 	virtual void handleInsertion(const TreeNode* node, LLDrawable* face);
 	virtual void handleRemoval(const TreeNode* node, LLDrawable* face);
@@ -105,12 +190,15 @@ public:
 	virtual void handleChildRemoval(const OctreeNode* parent, const OctreeNode* child);
 
 protected:
-	std::vector<LLCullInfo> mObjects;
 	U32 mState;
 	S32 mLODHash;
 	static S32 sLODSeed;
 
 public:
+	bridge_list_t mBridgeList;
+	buffer_map_t mBufferMap; //used by volume buffers to store unique buffers per texture
+
+	F32 mBuilt;
 	OctreeNode* mOctreeNode;
 	LLSpatialPartition* mSpatialPartition;
 	LLVector3 mBounds[2];
@@ -118,69 +206,132 @@ public:
 	LLVector3 mObjectExtents[2];
 	LLVector3 mObjectBounds[2];
 
+	LLPointer<LLVertexBuffer> mVertexBuffer;
+	LLPointer<LLVertexBuffer> mOcclusionVerts;
+	LLPointer<LLCubeMap>	mReflectionMap;
+
+	U32 mBufferUsage;
+	draw_map_t mDrawMap;
+	
+	U32 mVertexCount;
+	U32 mIndexCount;
+	F32 mDistance;
+	F32 mDepth;
+	F32 mLastUpdateDistance;
+	F32 mLastUpdateTime;
+	F32 mLastAddTime;
+	F32 mLastRenderTime;
+	
+	LLVector3 mViewAngle;
+	LLVector3 mLastUpdateViewAngle;
+	
+	F32 mPixelArea;
+	F32 mRadius;
 };
 
-class LLSpatialPartition
+class LLGeometryManager
 {
 public:
-	LLSpatialPartition();
+	std::vector<LLFace*> mFaceList;
+	virtual ~LLGeometryManager() { }
+	virtual void rebuildGeom(LLSpatialGroup* group) = 0;
+	virtual void getGeometry(LLSpatialGroup* group) = 0;
+	virtual void addGeometryCount(LLSpatialGroup* group, U32 &vertex_count, U32 &index_count);
+	virtual LLVertexBuffer* createVertexBuffer(U32 type_mask, U32 usage);
+};
+
+class LLSpatialPartition: public LLGeometryManager
+{
+public:
+	LLSpatialPartition(U32 data_mask, BOOL is_volatile = FALSE, U32 mBufferUsage = GL_STATIC_DRAW_ARB);
 	virtual ~LLSpatialPartition();
 
-	LLSpatialGroup *put(LLDrawable *drawablep);
+	LLSpatialGroup *put(LLDrawable *drawablep, BOOL was_visible = FALSE);
 	BOOL remove(LLDrawable *drawablep, LLSpatialGroup *curp);
 	
 	LLDrawable*	pickDrawable(const LLVector3& start, const LLVector3& end, LLVector3& collision);
 	
 	// If the drawable moves, move it here.
 	virtual void move(LLDrawable *drawablep, LLSpatialGroup *curp, BOOL immediate = FALSE);
-	void shift(const LLVector3 &offset);
+	virtual void shift(const LLVector3 &offset);
+
+	virtual F32 calcDistance(LLSpatialGroup* group, LLCamera& camera);
+	virtual F32 calcPixelArea(LLSpatialGroup* group, LLCamera& camera);
+
+	virtual void rebuildGeom(LLSpatialGroup* group);
 
 	S32 cull(LLCamera &camera, std::vector<LLDrawable *>* results = NULL, BOOL for_select = FALSE); // Cull on arbitrary frustum
 	BOOL checkOcclusion(LLSpatialGroup* group, LLCamera* camera);
+	void markReimage(LLSpatialGroup* group);
+	void processGeometry(LLCamera* camera);
+	void processImagery(LLCamera* camera);
 	void processOcclusion(LLCamera* camera);
+	void buildOcclusion();
 	void doOcclusion(LLCamera* camera);
 	BOOL isVisible(const LLVector3& v);
-	
+	BOOL isVolatile() const { return mVolatile; }
+
+	virtual LLSpatialBridge* asBridge() { return NULL; }
 	S32 getObjects(const LLVector3& pos,  F32 rad,  LLDrawable::drawable_set_t &results );
 	S32 getLights(const LLVector3& pos,  F32 rad,  LLDrawable::drawable_set_t &results );
 	
 	void renderDebug();
 	void restoreGL();
+	void resetVertexBuffers();
 	
 protected:
 	S32 getDrawables(const LLVector3& pos,  F32 rad,  LLDrawable::drawable_set_t &results, BOOL get_lights );
 	
-	LLSpatialGroup *mLastAddedGroupp;
-
-	typedef std::set<LLSpatialGroup*> spatial_group_set_t;
+	typedef std::set<LLPointer<LLSpatialGroup> > spatial_group_set_t;
 	spatial_group_set_t mSpatialGroups;
 
 	//things that might be occluded
-	std::queue<LLSpatialGroup*> mOcclusionQueue;
+	typedef std::queue<LLPointer<LLSpatialGroup> > spatial_group_queue_t;
+	spatial_group_queue_t mOcclusionQueue;
+
+	//things that need a terse update
+	spatial_group_queue_t mUpdateQueue;
+
+	//things that need an image update
+	spatial_group_queue_t mImageQueue;
 
 	//things awaiting query
-	std::queue<LLSpatialGroup*> mQueryQueue;
+	spatial_group_queue_t mQueryQueue;
 
 	std::vector<LLGLuint> mOcclusionQueries;	
 
 public:
 	LLSpatialGroup::OctreeNode* mOctree;
 
-	//things that are occluded
-	std::vector<LLSpatialGroup*> mOccludedList;
-
-	std::queue<LLSpatialGroup*> mReshadowQueue;
+	U32 mBufferUsage;
+	BOOL mRenderByGroup;
+	BOOL mImageEnabled;
+	U32 mLODSeed;
+	U32 mLODPeriod;
+	U32 mVertexDataMask;
+	F32 mSlopRatio; //percentage distance must change before drawables receive LOD update (default is 0.25);
+	BOOL mVolatile; //if TRUE, occlusion queries will be discarded when nodes change size
+	BOOL mDepthMask; //if TRUE, objects in this partition will be written to depth during alpha rendering
+	U32 mDrawableType;
+	U32 mPartitionType;
+
+	//index buffer for occlusion verts
+	LLPointer<LLVertexBuffer> mOcclusionIndices;
 
+	//things that are occluded
+	std::vector<LLPointer<LLSpatialGroup> > mOccludedList;
 };
 
 // class for creating bridges between spatial partitions
 class LLSpatialBridge : public LLDrawable, public LLSpatialPartition
 {
 public:
-	LLSpatialBridge(LLDrawable* root);
+	typedef std::vector<LLPointer<LLSpatialBridge> > bridge_vector_t;
+	
+	LLSpatialBridge(LLDrawable* root, U32 data_mask);
 	virtual ~LLSpatialBridge();
 	
-	virtual BOOL		isSpatialBridge() const		{ return TRUE; }
+	virtual BOOL isSpatialBridge() const		{ return TRUE; }
 
 	virtual void updateSpatialExtents();
 	virtual void updateBinRadius();
@@ -193,11 +344,123 @@ public:
 	virtual void shiftPos(const LLVector3& vec);
 	virtual void cleanupReferences();
 	virtual LLSpatialPartition* asPartition()		{ return this; }
-	LLCamera transformCamera(LLCamera& camera);
+	virtual LLSpatialBridge* asBridge()				{ return this; }
+	
+	virtual LLCamera transformCamera(LLCamera& camera);
 	
 	LLDrawable* mDrawable;
 };
 
+//spatial partition for water (implemented in LLVOWater.cpp)
+class LLWaterPartition : public LLSpatialPartition
+{
+public:
+	LLWaterPartition();
+	virtual void getGeometry(LLSpatialGroup* group) {  }
+	virtual void addGeometryCount(LLSpatialGroup* group, U32 &vertex_count, U32& index_count) { }
+};
+
+//spatial partition for terrain (impelmented in LLVOSurfacePatch.cpp)
+class LLTerrainPartition : public LLSpatialPartition
+{
+public:
+	LLTerrainPartition();
+	virtual void getGeometry(LLSpatialGroup* group);
+	virtual LLVertexBuffer* createVertexBuffer(U32 type_mask, U32 usage);
+};
+
+//spatial partition for trees
+class LLTreePartition : public LLSpatialPartition
+{
+public:
+	LLTreePartition();
+	virtual void getGeometry(LLSpatialGroup* group) { }
+	virtual void addGeometryCount(LLSpatialGroup* group, U32 &vertex_count, U32& index_count) { }
+
+};
+
+//spatial partition for particles (implemented in LLVOPartGroup.cpp)
+class LLParticlePartition : public LLSpatialPartition
+{
+public:
+	LLParticlePartition();
+	virtual void getGeometry(LLSpatialGroup* group);
+	virtual void addGeometryCount(LLSpatialGroup* group, U32 &vertex_count, U32& index_count);
+	virtual F32 calcPixelArea(LLSpatialGroup* group, LLCamera& camera);
+protected:
+	U32 mRenderPass;
+};
+
+//spatial partition for grass (implemented in LLVOGrass.cpp)
+class LLGrassPartition : public LLParticlePartition
+{
+public:
+	LLGrassPartition();
+};
+
+//spatial partition for clouds (implemented in LLVOClouds.cpp)
+class LLCloudPartition : public LLParticlePartition
+{
+public:
+	LLCloudPartition();
+};
+
+//class for wrangling geometry out of volumes (implemented in LLVOVolume.cpp)
+class LLVolumeGeometryManager: public LLGeometryManager
+{
+public:
+	virtual ~LLVolumeGeometryManager() { }
+	virtual void rebuildGeom(LLSpatialGroup* group);
+	virtual void getGeometry(LLSpatialGroup* group);
+	void registerFace(LLSpatialGroup* group, LLFace* facep, U32 type);
+};
+
+//spatial partition that uses volume geometry manager (implemented in LLVOVolume.cpp)
+class LLVolumePartition : public LLSpatialPartition, public LLVolumeGeometryManager
+{
+public:
+	LLVolumePartition();
+	virtual void rebuildGeom(LLSpatialGroup* group) { LLVolumeGeometryManager::rebuildGeom(group); }
+	virtual void getGeometry(LLSpatialGroup* group) { LLVolumeGeometryManager::getGeometry(group); }
+	virtual void addGeometryCount(LLSpatialGroup* group, U32 &vertex_count, U32& index_count) { LLVolumeGeometryManager::addGeometryCount(group, vertex_count, index_count); }
+};
+
+//spatial bridge that uses volume geometry manager (implemented in LLVOVolume.cpp)
+class LLVolumeBridge : public LLSpatialBridge, public LLVolumeGeometryManager
+{
+public:
+	LLVolumeBridge(LLDrawable* drawable);
+	virtual void rebuildGeom(LLSpatialGroup* group) { LLVolumeGeometryManager::rebuildGeom(group); }
+	virtual void getGeometry(LLSpatialGroup* group) { LLVolumeGeometryManager::getGeometry(group); }
+	virtual void addGeometryCount(LLSpatialGroup* group, U32 &vertex_count, U32& index_count) { LLVolumeGeometryManager::addGeometryCount(group, vertex_count, index_count); }
+};
+
+class LLHUDBridge : public LLVolumeBridge
+{
+public:
+	LLHUDBridge(LLDrawable* drawablep);
+	virtual void shiftPos(const LLVector3& vec);
+	virtual F32 calcPixelArea(LLSpatialGroup* group, LLCamera& camera);
+};
+
+//spatial partition that holds nothing but spatial bridges
+class LLBridgePartition : public LLSpatialPartition
+{
+public:
+	LLBridgePartition();
+	virtual void getGeometry(LLSpatialGroup* group) { }
+	virtual void addGeometryCount(LLSpatialGroup* group, U32 &vertex_count, U32& index_count) {  }
+};
+
+class LLHUDPartition : public LLBridgePartition
+{
+public:
+	LLHUDPartition();
+	virtual void shift(const LLVector3 &offset);
+};
+
+void validate_draw_info(LLDrawInfo& params);
+
 extern const F32 SG_BOX_SIDE;
 extern const F32 SG_BOX_OFFSET;
 extern const F32 SG_BOX_RAD;
diff --git a/indra/newview/llsprite.cpp b/indra/newview/llsprite.cpp
index e614134080369304b3304ea6b1a3d4d606d27304..d253271b52dde445ee6c9593be46ce6e5962cdf4 100644
--- a/indra/newview/llsprite.cpp
+++ b/indra/newview/llsprite.cpp
@@ -75,14 +75,7 @@ void LLSprite::updateFace(LLFace &face)
 	// First, figure out how many vertices/indices we need.
 	U32 num_vertices, num_indices;
 	U32 vertex_count = 0;
-
-
-	LLStrider<LLVector3> verticesp;
-	LLStrider<LLVector3> normalsp;
-	LLStrider<LLVector2> tex_coordsp;
-	U32 *indicesp;
-	S32 index_offset;
-
+	
 	// Get the total number of vertices and indices
 	if (mFollow)
 	{
@@ -95,14 +88,7 @@ void LLSprite::updateFace(LLFace &face)
 		num_indices = 12;
 	}
 
-	// Setup face
-	face.setPrimType(LLTriangles);
 	face.setSize(num_vertices, num_indices);
-	index_offset = face.getGeometry(verticesp,normalsp,tex_coordsp, indicesp);
-	if (-1 == index_offset)
-	{
-		return;
-	}
 	
 	if (mFollow) 
 	{
@@ -187,7 +173,30 @@ void LLSprite::updateFace(LLFace &face)
 	}
 
 	face.setFaceColor(mColor);
-	
+
+	LLStrider<LLVector3> verticesp;
+	LLStrider<LLVector3> normalsp;
+	LLStrider<LLVector2> tex_coordsp;
+	LLStrider<U32> indicesp;
+	S32 index_offset;
+
+	// Setup face
+	if (face.mVertexBuffer.isNull())
+	{	
+		face.mVertexBuffer = new LLVertexBuffer(LLVertexBuffer::MAP_VERTEX | 
+												LLVertexBuffer::MAP_TEXCOORD,
+												GL_STREAM_DRAW_ARB);
+		face.mVertexBuffer->allocateBuffer(4, 12, TRUE);
+		face.setGeomIndex(0);
+		face.setIndicesIndex(0);
+	}
+		
+	index_offset = face.getGeometry(verticesp,normalsp,tex_coordsp, indicesp);
+	if (-1 == index_offset)
+	{
+		return;
+	}
+
 	*tex_coordsp = LLVector2(0.f, 0.f);
 	*verticesp = mC;
 	tex_coordsp++;
@@ -232,6 +241,7 @@ void LLSprite::updateFace(LLFace &face)
 		*indicesp++ = 3 + index_offset;
 	}
 
+	//face.mVertexBuffer->setBuffer(0);
 	face.mCenterAgent = mPosition;
 }
 
diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
index 04b5fe7340452f604d6b8dae97ecf3c045bf7ce9..421e836f93e275130d9dd6c88dc50f33d216a298 100644
--- a/indra/newview/llstartup.cpp
+++ b/indra/newview/llstartup.cpp
@@ -99,6 +99,8 @@
 #include "llsky.h"
 #include "llstatview.h"
 #include "llsurface.h"
+#include "lltexturecache.h"
+#include "lltexturefetch.h"
 #include "lltoolmgr.h"
 #include "llui.h"
 #include "llurlwhitelist.h"
@@ -167,7 +169,6 @@ const char* SCREEN_LAST_FILENAME = "screen_last.bmp";
 //
 // Imported globals
 //
-extern LLPointer<LLImageGL> gStartImageGL;
 extern S32 gStartImageWidth;
 extern S32 gStartImageHeight;
 extern std::string gSerialNumber;
@@ -176,6 +177,8 @@ extern std::string gSerialNumber;
 // local globals
 //
 
+LLPointer<LLImageGL> gStartImageGL;
+
 static LLHost gAgentSimHost;
 static BOOL gSkipOptionalUpdate = FALSE;
 
@@ -234,7 +237,13 @@ public:
 	}
 };
 
-
+void update_texture_fetch()
+{
+	gTextureCache->update(1); // unpauses the texture cache thread
+	gImageDecodeThread->update(1); // unpauses the image thread
+	gTextureFetch->update(1); // unpauses the texture fetch thread
+	gImageList.updateImages(0.10f);
+}
 
 // Returns FALSE to skip other idle processing. Should only return
 // TRUE when all initialization done.
@@ -285,8 +294,6 @@ BOOL idle_startup()
 
 	static BOOL samename = FALSE;
 
-	static BOOL did_precache = FALSE;
-	
 	BOOL do_normal_idle = FALSE;
 
 	// HACK: These are things from the main loop that usually aren't done
@@ -1590,6 +1597,8 @@ BOOL idle_startup()
 				args["[ERROR_MESSAGE]"] = emsg.str();
 				gViewerWindow->alertXml("ErrorMessage", args, login_alert_done);
 				gStartupState = STATE_LOGIN_SHOW;
+				gAutoLogin = FALSE;
+				show_connect_box = TRUE;
 			}
 		}
 		else
@@ -1605,6 +1614,8 @@ BOOL idle_startup()
 			args["[ERROR_MESSAGE]"] = emsg.str();
 			gViewerWindow->alertXml("ErrorMessage", args, login_alert_done);
 			gStartupState = STATE_LOGIN_SHOW;
+			gAutoLogin = FALSE;
+			show_connect_box = TRUE;
 		}
 		return do_normal_idle;
 	}
@@ -1637,6 +1648,9 @@ BOOL idle_startup()
 		//
 		// Initialize classes w/graphics stuff.
 		//
+		gImageList.doPrefetchImages();
+		update_texture_fetch();
+		
 		LLSurface::initClasses();
 
 		LLFace::initClass();
@@ -1799,7 +1813,7 @@ BOOL idle_startup()
 		llinfos << "Decoding images..." << llendl;
 		// For all images pre-loaded into viewer cache, decode them.
 		// Need to do this AFTER we init the sky
-		gImageList.decodeAllImages();
+		gImageList.decodeAllImages(2.f);
 		gStartupState++;
 
 		// JC - Do this as late as possible to increase likelihood Purify
@@ -2368,18 +2382,6 @@ BOOL idle_startup()
 	if (STATE_PRECACHE == gStartupState)
 	{
 		do_normal_idle = TRUE;
-		if (!did_precache)
-		{
-			did_precache = TRUE;
-			// Don't preload map information!  The amount of data for all the
-			// map items (icons for classifieds, avatar locations, etc.) is
-			// huge, and not throttled.  This overflows the downstream
-			// pipe during startup, when lots of information is being sent.
-			// The problem manifests itself as invisible avatars on login. JC
-			//gWorldMap->setCurrentLayer(0); // pre-load layer 0 of the world map
-
-			gImageList.doPreloadImages(); // pre-load some images from static VFS
-		}
 		
 		F32 timeout_frac = timeout.getElapsedTimeF32()/PRECACHING_DELAY;
 		// wait precache-delay and for agent's avatar or a lot longer.
@@ -2390,6 +2392,7 @@ BOOL idle_startup()
 		}
 		else
 		{
+			update_texture_fetch();
 			set_startup_status(0.50f + 0.50f * timeout_frac, "Precaching...",
 							   gAgent.mMOTD.c_str());
 		}
@@ -2418,6 +2421,7 @@ BOOL idle_startup()
 		}
 		else
 		{
+			update_texture_fetch();
 			set_startup_status(0.f + 0.25f * wearables_time / MAX_WEARABLES_TIME,
 							 "Downloading clothing...",
 							 gAgent.mMOTD.c_str());
@@ -2520,34 +2524,6 @@ BOOL idle_startup()
 // local function definition
 //
 
-void unsupported_graphics_callback(S32 option, void* userdata)
-{
-	if (0 == option)
-	{
-		llinfos << "User cancelled after driver check" << llendl;
-		std::string help_path;
-		help_path = gDirUtilp->getExpandedFilename(LL_PATH_HELP,
-			"unsupported_card.html");
-		app_force_quit( help_path.c_str() );
-	}
-
-	LLPanelLogin::giveFocus();
-}
-
-void check_driver_callback(S32 option, void* userdata)
-{
-	if (0 == option)
-	{
-		llinfos << "User cancelled after driver check" << llendl;
-		std::string help_path;
-		help_path = gDirUtilp->getExpandedFilename(LL_PATH_HELP,
-			"graphics_driver_update.html");
-		app_force_quit( help_path.c_str() );
-	}
-
-	LLPanelLogin::giveFocus();
-}
-
 void login_show()
 {
 	LLPanelLogin::show(	gViewerWindow->getVirtualWindowRect(),
@@ -2555,7 +2531,7 @@ void login_show()
 						login_callback, NULL );
 
 	// Make sure all the UI textures are present and decoded.
-	gImageList.decodeAllImages();
+	gImageList.decodeAllImages(2.f);
 
 	if( USERSERVER_OTHER == gUserServerChoice )
 	{
@@ -3027,8 +3003,8 @@ void use_circuit_callback(void**, S32 result)
 void register_viewer_callbacks(LLMessageSystem* msg)
 {
 	msg->setHandlerFuncFast(_PREHASH_LayerData,				process_layer_data );
-	msg->setHandlerFuncFast(_PREHASH_ImageData,				LLViewerImage::receiveImage );
-	msg->setHandlerFuncFast(_PREHASH_ImagePacket,				LLViewerImage::receiveImagePacket );
+	msg->setHandlerFuncFast(_PREHASH_ImageData,				LLViewerImageList::receiveImageHeader );
+	msg->setHandlerFuncFast(_PREHASH_ImagePacket,				LLViewerImageList::receiveImagePacket );
 	msg->setHandlerFuncFast(_PREHASH_ObjectUpdate,				process_object_update );
 	msg->setHandlerFunc("ObjectUpdateCompressed",				process_compressed_object_update );
 	msg->setHandlerFunc("ObjectUpdateCached",					process_cached_object_update );
@@ -3869,7 +3845,7 @@ void dialog_choose_gender_first_start()
 // location_id = 1 => home position
 void init_start_screen(S32 location_id)
 {
-	if (gStartImageGL)
+	if (gStartImageGL.notNull())
 	{
 		gStartImageGL = NULL;
 		llinfos << "re-initializing start screen" << llendl;
diff --git a/indra/newview/llstartup.h b/indra/newview/llstartup.h
index c7b3bc13d502e681f1519ff6de6841dd1eab75ae..e5952f4f5b509a0782f5d8eb4274dc335651151c 100644
--- a/indra/newview/llstartup.h
+++ b/indra/newview/llstartup.h
@@ -9,10 +9,13 @@
 #ifndef LL_LLSTARTUP_H
 #define LL_LLSTARTUP_H
 
+#include "llimagegl.h"
+
 // functions
 BOOL idle_startup();
 void cleanup_app();
 LLString load_password_from_disk();
+void release_start_screen();
 
 // constants, variables,  & enumerations
 extern const char* SCREEN_HOME_FILENAME;
@@ -59,6 +62,7 @@ enum EStartupState{
 // exorted symbol
 extern S32 gStartupState;
 extern bool gQuickTimeInitialized;
+extern LLPointer<LLImageGL> gStartImageGL;
 
 class LLStartUp
 {
diff --git a/indra/newview/llsurface.cpp b/indra/newview/llsurface.cpp
index fc0c46bbe11f9fad7f1f724b4600acfab18ebc4f..4087f7f96c7ab3cab15e2a78b59d77bdc100db3f 100644
--- a/indra/newview/llsurface.cpp
+++ b/indra/newview/llsurface.cpp
@@ -31,7 +31,7 @@
 #include "noise.h"
 #include "llviewercamera.h"
 #include "llglheaders.h"
-#include "lldrawpool.h"
+#include "lldrawpoolterrain.h"
 #include "lldrawable.h"
 
 extern LLPipeline gPipeline;
@@ -101,7 +101,7 @@ LLSurface::~LLSurface()
 	mNumberOfPatches = 0;
 	destroyPatchData();
 
-	LLDrawPool *poolp = gPipeline.findPool(LLDrawPool::POOL_TERRAIN, mSTexturep);
+	LLDrawPoolTerrain *poolp = (LLDrawPoolTerrain*) gPipeline.findPool(LLDrawPool::POOL_TERRAIN, mSTexturep);
 	if (!poolp)
 	{
 		llwarns << "No pool for terrain on destruction!" << llendl;
@@ -312,7 +312,6 @@ void LLSurface::setOriginGlobal(const LLVector3d &origin_global)
 		LLVector3d water_origin_global(x, y, z);
 
 		mWaterObjp->setPositionGlobal(water_origin_global);
-		gPipeline.markMoved(mWaterObjp->mDrawable);
 	}
 }
 
@@ -602,45 +601,45 @@ void LLSurface::updatePatchVisibilities(LLAgent &agent)
 	}
 }
 
-
-
-BOOL LLSurface::idleUpdate()
+BOOL LLSurface::idleUpdate(F32 max_update_time)
 {
 	if (!gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_TERRAIN))
 	{
-		return TRUE;
+		return FALSE;
 	}
 	
 	// Perform idle time update of non-critical stuff.
 	// In this case, texture and normal updates.
 	LLTimer update_timer;
-	LLSurfacePatch *patchp = NULL;
+	BOOL did_update = FALSE;
 
 	// If the Z height data has changed, we need to rebuild our
 	// property line vertex arrays.
-	if (mDirtyPatchList.count() > 0)
+	if (mDirtyPatchList.size() > 0)
 	{
 		getRegion()->dirtyHeights();
 	}
 
-	S32 i = 0;
-	while (i < mDirtyPatchList.count())
+	// Always call updateNormals() / updateVerticalStats()
+	//  every frame to avoid artifacts
+	for(std::set<LLSurfacePatch *>::iterator iter = mDirtyPatchList.begin();
+		iter != mDirtyPatchList.end(); )
 	{
-		patchp = mDirtyPatchList[i];
+		std::set<LLSurfacePatch *>::iterator curiter = iter++;
+		LLSurfacePatch *patchp = *curiter;
 		patchp->updateNormals();
 		patchp->updateVerticalStats();
-
-		if ((update_timer.getElapsedTimeF32() < 0.05f) && patchp->updateTexture())
-		{
-			patchp->clearDirty();
-			mDirtyPatchList.remove(i);
-		}
-		else
+		if (max_update_time == 0.f || update_timer.getElapsedTimeF32() < max_update_time)
 		{
-			i++;
+			if (patchp->updateTexture())
+			{
+				did_update = TRUE;
+				patchp->clearDirty();
+				mDirtyPatchList.erase(curiter);
+			}
 		}
 	}
-	return TRUE;
+	return did_update;
 }
 
 // TODO -- move this to LLViewerRegion class
@@ -1210,10 +1209,7 @@ void LLSurface::dirtyAllPatches()
 void LLSurface::dirtySurfacePatch(LLSurfacePatch *patchp)
 {
 	// Put surface patch on dirty surface patch list
-	if (-1 == mDirtyPatchList.find(patchp))
-	{
-		mDirtyPatchList.put(patchp);
-	}
+	mDirtyPatchList.insert(patchp);
 }
 
 
diff --git a/indra/newview/llsurface.h b/indra/newview/llsurface.h
index 7698c272ab0b8b2fc11b89ccc82203097c3e3e1f..ca225355425cbc3ae03b5fc3d4be344f831686f3 100644
--- a/indra/newview/llsurface.h
+++ b/indra/newview/llsurface.h
@@ -22,7 +22,6 @@
 
 #include "llvowater.h"
 #include "llpatchvertexarray.h"
-#include "lldarray.h"
 #include "llviewerimage.h"
 
 class LLTimer;
@@ -96,7 +95,7 @@ public:
 	LLSurfacePatch *resolvePatchGlobal(const LLVector3d &position_global) const;
 
 	// Update methods (called during idle, normally)
-	BOOL idleUpdate();
+	BOOL idleUpdate(F32 max_update_time);
 
 	void renderSurfaceBounds();
 	
@@ -181,7 +180,7 @@ protected:
 	// Array of grid normals, mGridsPerEdge * mGridsPerEdge
 	LLVector3 *mNorm;
 
-	LLDynamicArray<LLSurfacePatch *> mDirtyPatchList;
+	std::set<LLSurfacePatch *> mDirtyPatchList;
 
 
 	// The textures should never be directly initialized - use the setter methods!
diff --git a/indra/newview/llsurfacepatch.cpp b/indra/newview/llsurfacepatch.cpp
index ac0add8ae3cf9a5a2b2e564e441056a3ab777ebf..4be7cf41e77380618b1fcee36ae30b1068293af1 100644
--- a/indra/newview/llsurfacepatch.cpp
+++ b/indra/newview/llsurfacepatch.cpp
@@ -794,7 +794,7 @@ void LLSurfacePatch::connectNeighbor(LLSurfacePatch *neighbor_patchp, const U32
 
 void LLSurfacePatch::updateVisibility()
 {
-	if (mVObjp == (LLVOSurfacePatch*)NULL)
+	if (mVObjp.isNull())
 	{
 		return;
 	}
diff --git a/indra/newview/lltexturecache.cpp b/indra/newview/lltexturecache.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..13efadf2139b57f6668b46ea010495b2b1458078
--- /dev/null
+++ b/indra/newview/lltexturecache.cpp
@@ -0,0 +1,1365 @@
+/** 
+ * @file texturecache.cpp
+ * @brief Object which handles local texture caching
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lltexturecache.h"
+
+#include "llapr.h"
+#include "lldir.h"
+#include "llimage.h"
+#include "lllfsthread.h"
+#include "llviewercontrol.h"
+
+#define USE_LFS_READ 0
+#define USE_LFS_WRITE 0
+
+// Note: first 4 bytes store file size, rest is j2c data
+const S32 TEXTURE_CACHE_ENTRY_SIZE = FIRST_PACKET_SIZE; //1024;
+
+class LLTextureCacheWorker : public LLWorkerClass
+{
+	friend class LLTextureCache;
+
+private:
+	enum e_state
+	{
+		INIT = 0,
+		LOCAL = 1,
+		CACHE = 2,
+		HEADER = 3,
+		BODY = 4
+	};
+
+	class ReadResponder : public LLLFSThread::Responder
+	{
+	public:
+		ReadResponder(LLTextureCache* cache, handle_t handle) : mCache(cache), mHandle(handle) {}
+		~ReadResponder() {}
+		void completed(S32 bytes)
+		{
+			mCache->lockWorkers();
+			LLTextureCacheWorker* reader = mCache->getReader(mHandle);
+			if (reader) reader->ioComplete(bytes);
+			mCache->unlockWorkers();
+		}
+		LLTextureCache* mCache;
+		LLTextureCacheWorker::handle_t mHandle;
+	};
+
+	class WriteResponder : public LLLFSThread::Responder
+	{
+	public:
+		WriteResponder(LLTextureCache* cache, handle_t handle) : mCache(cache), mHandle(handle) {}
+		~WriteResponder() {}
+		void completed(S32 bytes)
+		{
+			mCache->lockWorkers();
+			LLTextureCacheWorker* writer = mCache->getWriter(mHandle);
+			if (writer) writer->ioComplete(bytes);
+			mCache->unlockWorkers();
+		}
+		LLTextureCache* mCache;
+		LLTextureCacheWorker::handle_t mHandle;
+	};
+	
+public:
+	LLTextureCacheWorker(LLTextureCache* cache, U32 priority, const LLUUID& id,
+						 U8* data, S32 datasize, S32 offset,
+						 S32 imagesize, // for writes
+						 LLTextureCache::Responder* responder)
+		: LLWorkerClass(cache, "LLTextureCacheWorker"),
+		  mCache(cache),
+		  mPriority(priority),
+		  mID(id),
+		  mState(INIT),
+		  mReadData(NULL),
+		  mWriteData(data),
+		  mDataSize(datasize),
+		  mOffset(offset),
+		  mImageSize(imagesize),
+		  mImageFormat(IMG_CODEC_J2C),
+		  mImageLocal(FALSE),
+		  mResponder(responder),
+		  mFileHandle(LLLFSThread::nullHandle()),
+		  mBytesToRead(0),
+		  mBytesRead(0)
+	{
+		mPriority &= LLWorkerThread::PRIORITY_LOWBITS;
+	}
+	~LLTextureCacheWorker()
+	{
+		llassert_always(!haveWork());
+		delete[] mReadData;
+	}
+
+	bool doRead();
+	bool doWrite();
+	virtual bool doWork(S32 param); // Called from LLWorkerThread::processRequest()
+
+	handle_t read() { addWork(0, LLWorkerThread::PRIORITY_HIGH | mPriority); return mRequestHandle; }
+	handle_t write() { addWork(1, LLWorkerThread::PRIORITY_HIGH | mPriority); return mRequestHandle; }
+	bool complete() { return checkWork(); }
+	void ioComplete(S32 bytes)
+	{
+		mBytesRead = bytes;
+		setPriority(LLWorkerThread::PRIORITY_HIGH | mPriority);
+	}
+	
+private:
+	virtual void startWork(S32 param); // called from addWork() (MAIN THREAD)
+	virtual void finishWork(S32 param, bool completed); // called from finishRequest() (WORK THREAD)
+	virtual void endWork(S32 param, bool aborted); // called from doWork() (MAIN THREAD)
+
+private:
+	LLTextureCache* mCache;
+	U32 mPriority;
+	LLUUID mID;
+	e_state mState;
+	
+	U8* mReadData;
+	U8* mWriteData;
+	S32 mDataSize;
+	S32 mOffset;
+	S32 mImageSize;
+	S32 mImageFormat;
+	BOOL mImageLocal;
+	LLPointer<LLTextureCache::Responder> mResponder;
+	LLLFSThread::handle_t mFileHandle;
+	S32 mBytesToRead;
+	LLAtomicS32 mBytesRead;
+};
+
+//virtual
+void LLTextureCacheWorker::startWork(S32 param)
+{
+}
+
+bool LLTextureCacheWorker::doRead()
+{
+	S32 local_size = 0;
+	std::string local_filename;
+	
+	if (mState == INIT)
+	{
+		std::string filename = mCache->getLocalFileName(mID);
+		local_filename = filename + ".j2c";
+		local_size = ll_apr_file_size(local_filename, mCache->getFileAPRPool());
+		if (local_size == 0)
+		{
+			local_filename = filename + ".tga";
+			local_size = ll_apr_file_size(local_filename, mCache->getFileAPRPool());
+			if (local_size > 0)
+			{
+				mImageFormat = IMG_CODEC_TGA;
+				mDataSize = local_size; // Only a complete .tga file is valid
+			}
+		}
+		if (local_size > 0)
+		{
+			mState = LOCAL;
+		}
+		else
+		{
+			mState = CACHE;
+		}
+	}
+
+	if (mState == LOCAL)
+	{
+#if USE_LFS_READ
+		if (mFileHandle == LLLFSThread::nullHandle())
+		{
+			mImageLocal = TRUE;
+			mImageSize = local_size;
+			if (!mDataSize || mDataSize + mOffset > local_size)
+			{
+				mDataSize = local_size - mOffset;
+			}
+			if (mDataSize <= 0)
+			{
+				// no more data to read
+				mDataSize = 0;
+				return true;
+			}
+			mReadData = new U8[mDataSize];
+			mBytesRead = -1;
+			mBytesToRead = mDataSize;
+			setPriority(LLWorkerThread::PRIORITY_LOW | mPriority);
+			mFileHandle = LLLFSThread::sLocal->read(local_filename, mReadData, mOffset, mDataSize,
+													new ReadResponder(mCache, mRequestHandle));
+			return false;
+		}
+		else
+		{
+			if (mBytesRead >= 0)
+			{
+				if (mBytesRead != mBytesToRead)
+				{
+					llwarns << "Error reading file from local cache: " << local_filename
+							<< " Bytes: " << mDataSize << " Offset: " << mOffset
+							<< " / " << mDataSize << llendl;
+					mDataSize = 0; // failed
+					delete[] mReadData;
+					mReadData = NULL;
+				}
+				return true;
+			}
+			else
+			{
+				return false;
+			}
+		}
+#else
+		if (!mDataSize || mDataSize > local_size)
+		{
+			mDataSize = local_size;
+		}
+		mReadData = new U8[mDataSize];
+		S32 bytes_read = ll_apr_file_read_ex(local_filename, mCache->getFileAPRPool(),
+											 mReadData, mOffset, mDataSize);
+		if (bytes_read != mDataSize)
+		{
+			llwarns << "Error reading file from local cache: " << local_filename
+					<< " Bytes: " << mDataSize << " Offset: " << mOffset
+					<< " / " << mDataSize << llendl;
+			mDataSize = 0;
+			delete[] mReadData;
+			mReadData = NULL;
+		}
+		else
+		{
+			mImageSize = local_size;
+			mImageLocal = TRUE;
+		}
+		return true;
+#endif
+	}
+
+	S32 idx = -1;
+	
+	if (mState == CACHE)
+	{
+		llassert_always(mImageSize == 0);
+		idx = mCache->getHeaderCacheEntry(mID, false, &mImageSize);
+		if (idx >= 0 && mImageSize > mOffset)
+		{
+			llassert_always(mImageSize > 0);
+			if (!mDataSize || mDataSize > mImageSize)
+			{
+				mDataSize = mImageSize;
+			}
+			mState = mOffset < TEXTURE_CACHE_ENTRY_SIZE ? HEADER : BODY;
+		}
+		else
+		{
+			mDataSize = 0; // no data
+			return true;
+		}
+	}
+
+	if (mState == HEADER)
+	{
+#if USE_LFS_READ
+		if (mFileHandle == LLLFSThread::nullHandle())
+		{
+			llassert_always(idx >= 0);
+			llassert_always(mOffset < TEXTURE_CACHE_ENTRY_SIZE);
+			S32 offset = idx * TEXTURE_CACHE_ENTRY_SIZE + mOffset;
+			S32 size = TEXTURE_CACHE_ENTRY_SIZE - mOffset;
+			llassert_always(mReadData == NULL);
+			mReadData = new U8[size];
+			mBytesRead = -1;
+			mBytesToRead = size;
+			setPriority(LLWorkerThread::PRIORITY_LOW | mPriority);
+			mFileHandle = LLLFSThread::sLocal->read(mCache->mHeaderDataFileName,
+													mReadData, offset, mBytesToRead,
+													new ReadResponder(mCache, mRequestHandle));
+			return false;
+		}
+		else
+		{
+			if (mBytesRead >= 0)
+			{
+				if (mBytesRead != mBytesToRead)
+				{
+					llwarns << "LLTextureCacheWorker: "  << mID
+							<< " incorrect number of bytes read from header: " << mBytesRead
+							<< " != " << mBytesToRead << llendl;
+					mDataSize = -1; // failed
+					return true;
+				}
+				if (mDataSize <= TEXTURE_CACHE_ENTRY_SIZE)
+				{
+					return true; // done
+				}
+				else
+				{
+					mFileHandle = LLLFSThread::nullHandle();
+					mState = BODY;
+				}
+			}
+			else
+			{
+				return false;
+			}
+		}
+#else
+		llassert_always(idx >= 0);
+		llassert_always(mOffset < TEXTURE_CACHE_ENTRY_SIZE);
+		S32 offset = idx * TEXTURE_CACHE_ENTRY_SIZE + mOffset;
+		S32 size = TEXTURE_CACHE_ENTRY_SIZE - mOffset;
+		mReadData = new U8[size];
+		S32 bytes_read = ll_apr_file_read_ex(mCache->mHeaderDataFileName, mCache->getFileAPRPool(),
+											 mReadData, offset, size);
+		if (bytes_read != size)
+		{
+			llwarns << "LLTextureCacheWorker: "  << mID
+					<< " incorrect number of bytes read from header: " << bytes_read
+					<< " / " << size << llendl;
+			mDataSize = -1; // failed
+			return true;
+		}
+		if (mDataSize <= TEXTURE_CACHE_ENTRY_SIZE)
+		{
+			return true; // done
+		}
+		else
+		{
+			mState = BODY;
+		}
+#endif
+	}
+
+	if (mState == BODY)
+	{
+#if USE_LFS_READ
+		if (mFileHandle == LLLFSThread::nullHandle())
+		{
+			std::string filename = mCache->getTextureFileName(mID);
+			S32 filesize = ll_apr_file_size(filename, mCache->getFileAPRPool());
+			if (filesize > mOffset)
+			{
+				S32 datasize = TEXTURE_CACHE_ENTRY_SIZE + filesize;
+				mDataSize = llmin(datasize, mDataSize);
+				S32 data_offset = TEXTURE_CACHE_ENTRY_SIZE - mOffset;
+				data_offset = llmax(data_offset, 0);
+				S32 file_size = mDataSize - data_offset;
+				S32 file_offset = mOffset - TEXTURE_CACHE_ENTRY_SIZE;
+				file_offset = llmax(file_offset, 0);
+
+				llassert_always(mDataSize > 0);
+				U8* data = new U8[mDataSize];
+				if (data_offset > 0)
+				{
+					llassert_always(mReadData);
+					llassert_always(data_offset <= mDataSize);
+					memcpy(data, mReadData, data_offset);
+					delete[] mReadData;
+					mReadData = NULL;
+				}
+				llassert_always(mReadData == NULL);
+				mReadData = data;
+
+				mBytesRead = -1;
+				mBytesToRead = file_size;
+				setPriority(LLWorkerThread::PRIORITY_LOW | mPriority);
+				llassert_always(data_offset + mBytesToRead <= mDataSize);
+				mFileHandle = LLLFSThread::sLocal->read(filename,
+														mReadData + data_offset, file_offset, mBytesToRead,
+														new ReadResponder(mCache, mRequestHandle));
+				return false;
+			}
+			else
+			{
+				mDataSize = TEXTURE_CACHE_ENTRY_SIZE;
+				return true; // done
+			}
+		}
+		else
+		{
+			if (mBytesRead >= 0)
+			{
+				if (mBytesRead != mBytesToRead)
+				{
+					llwarns << "LLTextureCacheWorker: "  << mID
+							<< " incorrect number of bytes read from body: " << mBytesRead
+							<< " != " << mBytesToRead << llendl;
+					mDataSize = -1; // failed
+				}
+				return true;
+			}
+			else
+			{
+				return false;
+			}
+		}
+#else
+		std::string filename = mCache->getTextureFileName(mID);
+		S32 filesize = ll_apr_file_size(filename, mCache->getFileAPRPool());
+		S32 bytes_read = 0;
+		if (filesize > mOffset)
+		{
+			S32 datasize = TEXTURE_CACHE_ENTRY_SIZE + filesize;
+			mDataSize = llmin(datasize, mDataSize);
+			S32 data_offset = TEXTURE_CACHE_ENTRY_SIZE - mOffset;
+			data_offset = llmax(data_offset, 0);
+			S32 file_size = mDataSize - data_offset;
+			S32 file_offset = mOffset - TEXTURE_CACHE_ENTRY_SIZE;
+			file_offset = llmax(file_offset, 0);
+			
+			U8* data = new U8[mDataSize];
+			if (data_offset > 0)
+			{
+				llassert_always(mReadData);
+				memcpy(data, mReadData, data_offset);
+				delete[] mReadData;
+			}
+			mReadData = data;
+			bytes_read = ll_apr_file_read_ex(filename, mCache->getFileAPRPool(),
+											 mReadData + data_offset,
+											 file_offset, file_size);
+			if (bytes_read != file_size)
+			{
+				llwarns << "LLTextureCacheWorker: "  << mID
+						<< " incorrect number of bytes read from body: " << bytes_read
+						<< " / " << file_size << llendl;
+				mDataSize = -1; // failed
+				return true;
+			}
+		}
+		else
+		{
+			mDataSize = TEXTURE_CACHE_ENTRY_SIZE;
+		}
+		
+		return true;
+#endif
+	}
+	
+	return false;
+}
+
+bool LLTextureCacheWorker::doWrite()
+{
+	S32 idx = -1;
+
+	if (mState == INIT)
+	{
+		llassert_always(mOffset == 0); // Currently don't support offsets
+		mState = CACHE;
+	}
+
+	// No LOCAL state for write()
+	
+	if (mState == CACHE)
+	{
+		S32 cur_imagesize = 0;
+		S32 offset = mOffset;
+		idx = mCache->getHeaderCacheEntry(mID, false, &cur_imagesize);
+		if (idx >= 0 && cur_imagesize > 0)
+		{
+			offset = TEXTURE_CACHE_ENTRY_SIZE; // don't re-write header
+		}
+		idx = mCache->getHeaderCacheEntry(mID, true, &mImageSize); // touch entry
+		if (idx >= 0)
+		{
+			llassert_always(cur_imagesize <= 0 || mImageSize == cur_imagesize);
+			mState = offset < TEXTURE_CACHE_ENTRY_SIZE ? HEADER : BODY;
+		}
+		else
+		{
+			mDataSize = -1; // failed
+			return true;
+		}
+	}
+	
+	if (mState == HEADER)
+	{
+#if USE_LFS_WRITE
+		if (mFileHandle == LLLFSThread::nullHandle())
+		{
+			llassert_always(idx >= 0);
+			llassert_always(mOffset < TEXTURE_CACHE_ENTRY_SIZE);
+			S32 offset = idx * TEXTURE_CACHE_ENTRY_SIZE + mOffset;
+			S32 size = TEXTURE_CACHE_ENTRY_SIZE - mOffset;
+			mBytesRead = -1;
+			mBytesToRead = size;
+			setPriority(LLWorkerThread::PRIORITY_LOW | mPriority);
+			mFileHandle = LLLFSThread::sLocal->write(mCache->mHeaderDataFileName,
+													 mWriteData, offset, mBytesToRead,
+													 new WriteResponder(mCache, mRequestHandle));
+			return false;
+		}
+		else
+		{
+			if (mBytesRead >= 0)
+			{
+				if (mBytesRead != mBytesToRead)
+				{
+					llwarns << "LLTextureCacheWorker: "  << mID
+							<< " incorrect number of bytes written to header: " << mBytesRead
+							<< " != " << mBytesToRead << llendl;
+					mDataSize = -1; // failed
+					return true;
+				}
+				if (mDataSize <=  mBytesToRead)
+				{
+					return true; // done
+				}
+				else
+				{
+					mFileHandle = LLLFSThread::nullHandle();
+					mState = BODY;
+				}
+			}
+			else
+			{
+				return false;
+			}
+		}
+#else
+		llassert_always(idx >= 0);
+		llassert_always(mOffset < TEXTURE_CACHE_ENTRY_SIZE);
+		S32 offset = idx * TEXTURE_CACHE_ENTRY_SIZE + mOffset;
+		S32 size = TEXTURE_CACHE_ENTRY_SIZE - mOffset;
+		S32 bytes_written = ll_apr_file_write_ex(mCache->mHeaderDataFileName, mCache->getFileAPRPool(),
+												 mWriteData, offset, size);
+
+		if (bytes_written <= 0)
+		{
+			llwarns << "LLTextureCacheWorker: missing entry: " << mID << llendl;
+			mDataSize = -1; // failed
+			return true;
+		}
+
+		if (mDataSize <= size)
+		{
+			return true; // done
+		}
+		else
+		{
+			mState = BODY;
+		}
+#endif
+	}
+	
+	if (mState == BODY)
+	{
+#if USE_LFS_WRITE
+		if (mFileHandle == LLLFSThread::nullHandle())
+		{
+			S32 data_offset = TEXTURE_CACHE_ENTRY_SIZE - mOffset;
+			data_offset = llmax(data_offset, 0);
+			S32 file_size = mDataSize - data_offset;
+			S32 file_offset = mOffset - TEXTURE_CACHE_ENTRY_SIZE;
+			file_offset = llmax(file_offset, 0);
+			if (file_size > 0 && mCache->appendToTextureEntryList(mID, file_size))
+			{
+				std::string filename = mCache->getTextureFileName(mID);
+				mBytesRead = -1;
+				mBytesToRead = file_size;
+				setPriority(LLWorkerThread::PRIORITY_LOW | mPriority);
+				mFileHandle = LLLFSThread::sLocal->write(filename,
+														 mWriteData + data_offset, file_offset, mBytesToRead,
+														 new WriteResponder(mCache, mRequestHandle));
+				return false;
+			}
+			else
+			{
+				mDataSize = 0; // no data written
+				return true; // done
+			}
+		}
+		else
+		{
+			if (mBytesRead >= 0)
+			{
+				if (mBytesRead != mBytesToRead)
+				{
+					llwarns << "LLTextureCacheWorker: "  << mID
+							<< " incorrect number of bytes written to body: " << mBytesRead
+							<< " != " << mBytesToRead << llendl;
+					mDataSize = -1; // failed
+				}
+				return true;
+			}
+			else
+			{
+				return false;
+			}
+		}
+#else
+		S32 data_offset = TEXTURE_CACHE_ENTRY_SIZE - mOffset;
+		data_offset = llmax(data_offset, 0);
+		S32 file_size = mDataSize - data_offset;
+		S32 file_offset = mOffset - TEXTURE_CACHE_ENTRY_SIZE;
+		file_offset = llmax(file_offset, 0);
+		S32 bytes_written = 0;
+		if (file_size > 0 && mCache->appendToTextureEntryList(mID, file_size))
+		{
+			std::string filename = mCache->getTextureFileName(mID);
+			bytes_written = ll_apr_file_write_ex(filename, mCache->getFileAPRPool(),
+												 mWriteData + data_offset,
+												 file_offset, file_size);
+			if (bytes_written <= 0)
+			{
+				mDataSize = -1; // failed
+			}
+		}
+		else
+		{
+			mDataSize = 0; // no data written
+		}
+
+		return true;
+#endif
+	}
+	
+	return false;
+}
+
+//virtual
+bool LLTextureCacheWorker::doWork(S32 param)
+{
+	bool res = false;
+	if (param == 0) // read
+	{
+		res = doRead();
+	}
+	else if (param == 1) // write
+	{
+		res = doWrite();
+	}
+	else
+	{
+		llassert_always(0);
+	}
+	return res;
+}
+
+//virtual (WORKER THREAD)
+void LLTextureCacheWorker::finishWork(S32 param, bool completed)
+{
+	if (mResponder.notNull())
+	{
+		bool success = (completed && mDataSize > 0);
+		if (param == 0)
+		{
+			// read
+			if (success)
+			{
+				mResponder->setData(mReadData, mDataSize, mImageSize, mImageFormat, mImageLocal);
+				mReadData = NULL; // responder owns data
+				mDataSize = 0;
+			}
+			else
+			{
+				delete[] mReadData;
+				mReadData = NULL;
+				
+			}
+		}
+		else
+		{
+			// write
+			mWriteData = NULL; // we never owned data
+			mDataSize = 0;
+		}
+		mResponder->completed(success);
+	}
+}
+
+//virtual (MAIN THREAD)
+void LLTextureCacheWorker::endWork(S32 param, bool aborted)
+{
+	if (aborted)
+	{
+		// Let the destructor handle any cleanup
+		return;
+	}
+	switch(param)
+	{
+	  default:
+	  case 0: // read
+	  case 1: // write
+	  {
+		  if (mDataSize < 0)
+		  {
+			  // failed
+			  mCache->removeFromCache(mID);
+		  }
+		  break;
+	  }
+	}
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+LLTextureCache::LLTextureCache(bool threaded)
+	: LLWorkerThread("TextureCache", threaded),
+	  mWorkersMutex(getAPRPool()),
+	  mHeaderMutex(getAPRPool()),
+	  mFileAPRPool(NULL),
+	  mReadOnly(FALSE),
+	  mTexturesSizeTotal(0),
+	  mDoPurge(FALSE)
+{
+	apr_pool_create(&mFileAPRPool, NULL);
+}
+
+LLTextureCache::~LLTextureCache()
+{
+	apr_pool_destroy(mFileAPRPool);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+//virtual
+S32 LLTextureCache::update(U32 max_time_ms)
+{
+	S32 res;
+	res = LLWorkerThread::update(max_time_ms);
+
+	lockWorkers();
+	for (std::vector<handle_t>::iterator iter1 = mPrioritizeWriteList.begin();
+		 iter1 != mPrioritizeWriteList.end(); ++iter1)
+	{
+		handle_t handle = *iter1;
+		handle_map_t::iterator iter2 = mWriters.find(handle);
+		if(iter2 != mWriters.end())
+		{
+			LLTextureCacheWorker* worker = iter2->second;
+			worker->setPriority(LLWorkerThread::PRIORITY_HIGH | worker->mPriority);
+		}
+	}
+	mPrioritizeWriteList.clear();
+	unlockWorkers();
+	return res;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+std::string LLTextureCache::getLocalFileName(const LLUUID& id)
+{
+	// Does not include extension
+	std::string idstr = id.asString();
+	std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_SKINS, "textures", idstr);
+	return filename;
+}
+
+std::string LLTextureCache::getTextureFileName(const LLUUID& id)
+{
+	std::string idstr = id.asString();
+	std::string delem = gDirUtilp->getDirDelimiter();
+	std::string filename = mTexturesDirName + delem + idstr[0] + delem + idstr;
+	return filename;
+}
+
+bool LLTextureCache::appendToTextureEntryList(const LLUUID& id, S32 bodysize)
+{
+	bool res = false;
+	bool purge = false;
+	// Append UUID to end of texture entries
+	{
+		LLMutexLock lock(&mHeaderMutex);
+		size_map_t::iterator iter = mTexturesSizeMap.find(id);
+		if (iter == mTexturesSizeMap.end() || iter->second < bodysize)
+		{
+			llassert_always(bodysize > 0);
+			Entry* entry = new Entry(id, bodysize, time(NULL));
+			ll_apr_file_write_ex(mTexturesDirEntriesFileName, getFileAPRPool(),
+								 (U8*)entry, -1, 1*sizeof(Entry));
+			delete entry;
+			if (iter != mTexturesSizeMap.end())
+			{
+				mTexturesSizeTotal -= iter->second;
+			}
+			mTexturesSizeTotal += bodysize;
+			mTexturesSizeMap[id] = bodysize;
+			if (mTexturesSizeTotal > sCacheMaxTexturesSize)
+			{
+				purge = true;
+			}
+			res = true;
+		}
+	}
+	if (purge)
+	{
+		mDoPurge = TRUE;
+	}
+	return res;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+//static
+const S32 MAX_REASONABLE_FILE_SIZE = 512*1024*1024; // 512 MB
+F32 LLTextureCache::sHeaderCacheVersion = 1.0f;
+U32 LLTextureCache::sCacheMaxEntries = MAX_REASONABLE_FILE_SIZE / TEXTURE_CACHE_ENTRY_SIZE;
+S64 LLTextureCache::sCacheMaxTexturesSize = 0; // no limit
+const char* entries_filename = "texture.entries";
+const char* cache_filename = "texture.cache";
+const char* textures_dirname = "textures";
+
+void LLTextureCache::setDirNames(ELLPath location)
+{
+	std::string delem = gDirUtilp->getDirDelimiter();
+	mHeaderEntriesFileName = gDirUtilp->getExpandedFilename(location, entries_filename);
+	mHeaderDataFileName = gDirUtilp->getExpandedFilename(location, cache_filename);
+	mTexturesDirName = gDirUtilp->getExpandedFilename(location, textures_dirname);
+	mTexturesDirEntriesFileName = mTexturesDirName + delem + entries_filename;
+}
+
+void LLTextureCache::purgeCache(ELLPath location)
+{
+	if (!mReadOnly)
+	{
+		setDirNames(location);
+	
+		ll_apr_file_remove(mHeaderEntriesFileName, NULL);
+		ll_apr_file_remove(mHeaderDataFileName, NULL);
+	}
+	purgeAllTextures(true);
+}
+
+S64 LLTextureCache::initCache(ELLPath location, S64 max_size, BOOL read_only)
+{
+	mReadOnly = read_only;
+	
+	S64 header_size = (max_size * 2) / 10;
+	S64 max_entries = header_size / TEXTURE_CACHE_ENTRY_SIZE;
+	sCacheMaxEntries = (S32)(llmin((S64)sCacheMaxEntries, max_entries));
+	header_size = sCacheMaxEntries * TEXTURE_CACHE_ENTRY_SIZE;
+	max_size -= header_size;
+	if (sCacheMaxTexturesSize > 0)
+		sCacheMaxTexturesSize = llmin(sCacheMaxTexturesSize, max_size);
+	else
+		sCacheMaxTexturesSize = max_size;
+	max_size -= sCacheMaxTexturesSize;
+	
+	llinfos << "TEXTURE CACHE: Headers: " << sCacheMaxEntries
+			<< " Textures size: " << sCacheMaxTexturesSize/(1024*1024) << " MB" << llendl;
+
+	setDirNames(location);
+	
+	if (!mReadOnly)
+	{
+		LLFile::mkdir(mTexturesDirName.c_str());
+		const char* subdirs = "0123456789abcdef";
+		for (S32 i=0; i<16; i++)
+		{
+			std::string dirname = mTexturesDirName + gDirUtilp->getDirDelimiter() + subdirs[i];
+			LLFile::mkdir(dirname.c_str());
+		}
+	}
+	readHeaderCache();
+	purgeTextures(true); // calc mTexturesSize and make some room in the texture cache if we need it
+
+	return max_size; // unused cache space
+}
+
+struct lru_data
+{
+	lru_data(U32 t, S32 i, const LLUUID& id) { time=t; index=i; uuid=id; }
+	U32 time;
+	S32 index;
+	LLUUID uuid;
+	struct Compare
+	{
+		// lhs < rhs
+		typedef const lru_data* lru_data_ptr;
+		bool operator()(const lru_data_ptr& a, const lru_data_ptr& b) const
+		{
+			if (!(a->time < b->time))
+				return true;
+			else if (!(b->time < a->time))
+				return false;
+			else
+				return a->index < b->index;
+		}
+	};				
+};
+
+// Called from either the main thread or the worker thread
+void LLTextureCache::readHeaderCache(apr_pool_t* poolp)
+{
+	LLMutexLock lock(&mHeaderMutex);
+	mHeaderEntriesInfo.mVersion = 0.f;
+	mHeaderEntriesInfo.mEntries = 0;
+	if (ll_apr_file_exists(mHeaderEntriesFileName, poolp))
+	{
+		ll_apr_file_read_ex(mHeaderEntriesFileName, poolp,
+							(U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo));
+	}
+	if (mHeaderEntriesInfo.mVersion != sHeaderCacheVersion)
+	{
+		if (!mReadOnly)
+		{
+			// Info with 0 entries
+			mHeaderEntriesInfo.mVersion = sHeaderCacheVersion;
+			ll_apr_file_write_ex(mHeaderEntriesFileName, poolp,
+								 (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo));
+		}
+	}
+	else
+	{
+		S32 num_entries = mHeaderEntriesInfo.mEntries;
+		if (num_entries)
+		{
+			Entry* entries = new Entry[num_entries];
+			ll_apr_file_read_ex(mHeaderEntriesFileName, poolp,
+								(U8*)entries, sizeof(EntriesInfo), num_entries*sizeof(Entry));
+			typedef std::set<lru_data*, lru_data::Compare> lru_set_t;
+			lru_set_t lru;
+			for (S32 i=0; i<num_entries; i++)
+			{
+				if (entries[i].mSize >= 0) // -1 indicates erased entry, skip
+				{
+					const LLUUID& id = entries[i].mID;
+					lru.insert(new lru_data(entries[i].mTime, i, id));
+					mHeaderIDMap[id] = i;
+				}
+			}
+			mLRU.clear();
+			S32 lru_entries = sCacheMaxEntries / 10;
+			for (lru_set_t::iterator iter = lru.begin(); iter != lru.end(); ++iter)
+			{
+				lru_data* data = *iter;
+				mLRU[data->index] = data->uuid;
+				if (--lru_entries <= 0)
+					break;
+			}
+			for_each(lru.begin(), lru.end(), DeletePointer());
+			delete[] entries;
+		}
+	}
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void LLTextureCache::purgeAllTextures(bool purge_directories)
+{
+	if (!mReadOnly)
+	{
+		const char* subdirs = "0123456789abcdef";
+		std::string delem = gDirUtilp->getDirDelimiter();
+		std::string mask = delem + "*";
+		for (S32 i=0; i<16; i++)
+		{
+			std::string dirname = mTexturesDirName + delem + subdirs[i];
+			gDirUtilp->deleteFilesInDir(dirname.c_str(),mask);
+			if (purge_directories)
+			{
+				LLFile::rmdir(dirname.c_str());
+			}
+		}
+		ll_apr_file_remove(mTexturesDirEntriesFileName, NULL);
+		if (purge_directories)
+		{
+			LLFile::rmdir(mTexturesDirName.c_str());
+		}
+	}
+	mTexturesSizeMap.clear();
+}
+
+void LLTextureCache::purgeTextures(bool validate)
+{
+	if (mReadOnly)
+	{
+		return;
+	}
+
+	LLMutexLock lock(&mHeaderMutex);
+	
+	S32 filesize = ll_apr_file_size(mTexturesDirEntriesFileName, NULL);
+	S32 num_entries = filesize / sizeof(Entry);
+	if (num_entries * sizeof(Entry) != filesize)
+	{
+		llwarns << "Bad cache file: " << mTexturesDirEntriesFileName << " Purging." << llendl;
+		purgeAllTextures(false);
+		return;
+	}
+	if (num_entries == 0)
+	{
+		return; // nothing to do
+	}
+	
+	Entry* entries = new Entry[num_entries];
+	S32 bytes_read = ll_apr_file_read_ex(mTexturesDirEntriesFileName, NULL,
+										 (U8*)entries, 0, num_entries*sizeof(Entry));
+	if (bytes_read != filesize)
+	{
+		llwarns << "Bad cache file (2): " << mTexturesDirEntriesFileName << " Purging." << llendl;
+		purgeAllTextures(false);
+		return;
+	}
+	
+	llinfos << "TEXTURE CACHE: Reading Entries..." << llendl;
+	
+	std::map<LLUUID, S32> entry_idx_map;
+	S64 total_size = 0;
+	for (S32 idx=0; idx<num_entries; idx++)
+	{
+		const LLUUID& id = entries[idx].mID;
+// 		llinfos << "Entry: " << id << " Size: " << entries[i].mSize << " Time: " << entries[i].mTime << llendl;
+		std::map<LLUUID, S32>::iterator iter = entry_idx_map.find(id);
+		if (iter != entry_idx_map.end())
+		{
+			// Newer entry replacing older entry
+			S32 pidx = iter->second;
+			total_size -= entries[pidx].mSize;
+			entries[pidx].mSize = 0; // flag: skip older entry
+		}
+		entry_idx_map[id] = idx;
+		total_size += entries[idx].mSize;
+	}
+
+	U32 validate_idx = 0;
+	if (validate)
+	{
+		validate_idx = gSavedSettings.getU32("CacheValidateCounter");
+		U32 next_idx = (++validate_idx) % 256;
+		gSavedSettings.setU32("CacheValidateCounter", next_idx);
+		llinfos << "TEXTURE CACHE: Validating: " << validate_idx << llendl;
+	}
+	
+	S64 min_cache_size = (sCacheMaxTexturesSize * 9) / 10;
+	S32 purge_count = 0;
+	S32 next_idx = 0;
+	for (S32 idx=0; idx<num_entries; idx++)
+	{
+		if (entries[idx].mSize == 0)
+		{
+			continue;
+		}
+		bool purge_entry = false;
+		std::string filename = getTextureFileName(entries[idx].mID);
+		if (total_size >= min_cache_size)
+		{
+			purge_entry = true;
+		}
+		else if (validate)
+		{
+			// make sure file exists and is the correct size
+			S32 uuididx = entries[idx].mID.mData[0];
+			if (uuididx == validate_idx)
+			{
+// 				llinfos << "Validating: " << filename << "Size: " << entries[idx].mSize << llendl;
+				S32 bodysize = ll_apr_file_size(filename, NULL);
+				if (bodysize != entries[idx].mSize)
+				{
+					llwarns << "TEXTURE CACHE BODY HAS BAD SIZE: " << bodysize << " != " << entries[idx].mSize
+							<< filename << llendl;
+					purge_entry = true;
+				}
+			}
+		}
+		if (purge_entry)
+		{
+			purge_count++;
+//	 		llinfos << "PURGING: " << filename << llendl;
+			ll_apr_file_remove(filename, NULL);
+			total_size -= entries[idx].mSize;
+			entries[idx].mSize = 0;
+		}
+		else
+		{
+			if (next_idx != idx)
+			{
+				entries[next_idx] = entries[idx];
+			}
+			++next_idx;
+		}
+	}
+	num_entries = next_idx;
+
+	llinfos << "TEXTURE CACHE: Writing Entries: " << num_entries << llendl;
+	
+	ll_apr_file_remove(mTexturesDirEntriesFileName, NULL);
+	ll_apr_file_write_ex(mTexturesDirEntriesFileName, NULL,
+						 (U8*)&entries[0], 0, num_entries*sizeof(Entry));
+	
+	mTexturesSizeTotal = 0;
+	mTexturesSizeMap.clear();
+	for (S32 idx=0; idx<num_entries; idx++)
+	{
+		mTexturesSizeMap[entries[idx].mID] = entries[idx].mSize;
+		mTexturesSizeTotal += entries[idx].mSize;
+	}
+	llassert(mTexturesSizeTotal == total_size);
+	
+	delete[] entries;
+	
+	llinfos << "TEXTURE CACHE:"
+			<< " PURGED: " << purge_count
+			<< " ENTRIES: " << num_entries
+			<< " CACHE SIZE: " << total_size / 1024*1024 << " MB"
+			<< llendl;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+// call lockWorkers() first!
+LLTextureCacheWorker* LLTextureCache::getReader(handle_t handle)
+{
+	LLTextureCacheWorker* res = NULL;
+	handle_map_t::iterator iter = mReaders.find(handle);
+	if (iter != mReaders.end())
+	{
+		res = iter->second;
+	}
+	return res;
+}
+
+LLTextureCacheWorker* LLTextureCache::getWriter(handle_t handle)
+{
+	LLTextureCacheWorker* res = NULL;
+	handle_map_t::iterator iter = mWriters.find(handle);
+	if (iter != mWriters.end())
+	{
+		res = iter->second;
+	}
+	return res;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+// Called from work thread
+S32 LLTextureCache::getHeaderCacheEntry(const LLUUID& id, bool touch, S32* imagesize)
+{
+	bool retry = false;
+	S32 idx = -1;
+
+	{
+		LLMutexLock lock(&mHeaderMutex);
+		id_map_t::iterator iter = mHeaderIDMap.find(id);
+		if (iter != mHeaderIDMap.end())
+		{
+			idx = iter->second;
+		}
+		else if (touch && !mReadOnly)
+		{
+			if (mHeaderEntriesInfo.mEntries < sCacheMaxEntries)
+			{
+				// Add an entry
+				idx = mHeaderEntriesInfo.mEntries++;
+				mHeaderIDMap[id] = idx;
+				// Update Info
+				ll_apr_file_write_ex(mHeaderEntriesFileName, getFileAPRPool(),
+									(U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo));
+			}
+			else if (!mLRU.empty())
+			{
+				idx = mLRU.begin()->first; // will be erased below
+				const LLUUID& oldid = mLRU.begin()->second;
+				mHeaderIDMap.erase(oldid);
+				mTexturesSizeMap.erase(oldid);
+				mHeaderIDMap[id] = idx;
+			}
+			else
+			{
+				idx = -1;
+				retry = true;
+			}
+		}
+		if (idx >= 0)
+		{
+			if (touch && !mReadOnly)
+			{
+				// Update the lru entry
+				mLRU.erase(idx);
+				llassert_always(imagesize && *imagesize > 0);
+				Entry* entry = new Entry(id, *imagesize, time(NULL));
+				S32 offset = sizeof(EntriesInfo) + idx * sizeof(Entry);
+				ll_apr_file_write_ex(mHeaderEntriesFileName, getFileAPRPool(),
+									 (U8*)entry, offset, sizeof(Entry));
+				delete entry;
+			}
+			else if (imagesize)
+			{
+				// Get the image size
+				Entry entry;
+				S32 offset = sizeof(EntriesInfo) + idx * sizeof(Entry);
+				ll_apr_file_read_ex(mHeaderEntriesFileName, getFileAPRPool(),
+									(U8*)&entry, offset, sizeof(Entry));
+				*imagesize = entry.mSize;
+			}
+		}
+	}
+	if (retry)
+	{
+		readHeaderCache(getFileAPRPool()); // updates the lru
+		llassert_always(!mLRU.empty() || mHeaderEntriesInfo.mEntries < sCacheMaxEntries);
+		idx = getHeaderCacheEntry(id, touch, imagesize); // assert above ensures no inf. recursion
+	}
+	return idx;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+// Calls from texture pipeline thread (i.e. LLTextureFetch)
+
+LLTextureCache::handle_t LLTextureCache::readFromCache(const LLUUID& id, U32 priority,
+													   S32 offset, S32 size, ReadResponder* responder)
+{
+	// Note: checking to see if an entry exists can cause a stall,
+	//  so let the thread handle it
+	LLMutexLock lock(&mWorkersMutex);
+	LLTextureCacheWorker* worker = new LLTextureCacheWorker(this, priority, id,
+															NULL, size, offset, 0,
+															responder);
+	handle_t handle = worker->read();
+	mReaders[handle] = worker;
+	return handle;
+}
+
+bool LLTextureCache::readComplete(handle_t handle, bool abort)
+{
+	lockWorkers();
+	handle_map_t::iterator iter = mReaders.find(handle);
+	llassert_always(iter != mReaders.end());
+	LLTextureCacheWorker* worker = iter->second;
+	bool res = worker->complete();
+	if (res || abort)
+	{
+		mReaders.erase(handle);
+		unlockWorkers();
+		worker->scheduleDelete();
+		return true;
+	}
+	else
+	{
+		unlockWorkers();
+		return false;
+	}
+}
+
+LLTextureCache::handle_t LLTextureCache::writeToCache(const LLUUID& id, U32 priority,
+													  U8* data, S32 datasize, S32 imagesize,
+													  WriteResponder* responder)
+{
+	if (mReadOnly)
+	{
+		return LLWorkerThread::nullHandle();
+	}
+	if (mDoPurge)
+	{
+		// NOTE: This may cause an occasional hiccup,
+		//  but it really needs to be done on the control thread
+		//  (i.e. here)
+		purgeTextures(false);
+		mDoPurge = FALSE;
+	}
+	if (datasize >= TEXTURE_CACHE_ENTRY_SIZE)
+	{
+		LLMutexLock lock(&mWorkersMutex);
+		llassert_always(imagesize > 0);
+		LLTextureCacheWorker* worker = new LLTextureCacheWorker(this, priority, id,
+																data, datasize, 0,
+																imagesize, responder);
+		handle_t handle = worker->write();
+		mWriters[handle] = worker;
+		return handle;
+	}
+	return LLWorkerThread::nullHandle();
+}
+
+bool LLTextureCache::writeComplete(handle_t handle, bool abort)
+{
+	lockWorkers();
+	handle_map_t::iterator iter = mWriters.find(handle);
+	llassert_always(iter != mWriters.end());
+	LLTextureCacheWorker* worker = iter->second;
+	if (worker->complete() || abort)
+	{
+		mWriters.erase(handle);
+		unlockWorkers();
+		worker->scheduleDelete();
+		return true;
+	}
+	else
+	{
+		unlockWorkers();
+		return false;
+	}
+}
+
+void LLTextureCache::prioritizeWrite(handle_t handle)
+{
+	// Don't prioritize yet, we might be working on this now
+	//   which could create a deadlock
+	mPrioritizeWriteList.push_back(handle);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+// Called from MAIN thread (endWork())
+
+bool LLTextureCache::removeHeaderCacheEntry(const LLUUID& id)
+{
+	if (mReadOnly)
+	{
+		return false;
+	}
+	LLMutexLock lock(&mHeaderMutex);
+	id_map_t::iterator iter = mHeaderIDMap.find(id);
+	if (iter != mHeaderIDMap.end())
+	{
+		S32 idx = iter->second;
+		if (idx >= 0)
+		{
+			Entry* entry = new Entry(id, -1, time(NULL));
+			S32 offset = sizeof(EntriesInfo) + idx * sizeof(Entry);
+			ll_apr_file_write_ex(mHeaderEntriesFileName, NULL,
+								 (U8*)entry, offset, sizeof(Entry));
+			delete entry;
+			mLRU[idx] = id;
+			mHeaderIDMap.erase(id);
+			mTexturesSizeMap.erase(id);
+			return true;
+		}
+	}
+	return false;
+}
+
+void LLTextureCache::removeFromCache(const LLUUID& id)
+{
+	llwarns << "Removing texture from cache: " << id << llendl;
+	if (!mReadOnly)
+	{
+		removeHeaderCacheEntry(id);
+		ll_apr_file_remove(getTextureFileName(id), NULL);
+	}
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+LLTextureCache::ReadResponder::ReadResponder()
+	: mImageSize(0),
+	  mImageLocal(FALSE)
+{
+}
+
+void LLTextureCache::ReadResponder::setData(U8* data, S32 datasize, S32 imagesize, S32 imageformat, BOOL imagelocal)
+{
+	if (mFormattedImage.notNull())
+	{
+		llassert_always(mFormattedImage->getCodec() == imageformat);
+		mFormattedImage->appendData(data, datasize);
+	}
+	else
+	{
+		mFormattedImage = LLImageFormatted::createFromType(imageformat);
+		mFormattedImage->setData(data,datasize);
+	}
+	mImageSize = imagesize;
+	mImageLocal = imagelocal;
+}
+
+//////////////////////////////////////////////////////////////////////////////
diff --git a/indra/newview/lltexturecache.h b/indra/newview/lltexturecache.h
new file mode 100644
index 0000000000000000000000000000000000000000..3b16b26b4a851e7c734509d2ce27caae969ad446
--- /dev/null
+++ b/indra/newview/lltexturecache.h
@@ -0,0 +1,149 @@
+/** 
+ * @file lltexturecache.h
+ * @brief Object for managing texture cachees.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTEXTURECACHE_
+#define LL_LLTEXTURECACHE_H
+
+#include "lldir.h"
+#include "llstl.h"
+#include "llstring.h"
+#include "lluuid.h"
+
+#include "llworkerthread.h"
+
+class LLTextureCacheWorker;
+
+class LLTextureCache : public LLWorkerThread
+{
+	friend class LLTextureCacheWorker;
+
+public:
+
+	class Responder : public LLResponder
+	{
+	public:
+		virtual void setData(U8* data, S32 datasize, S32 imagesize, S32 imageformat, BOOL imagelocal) = 0;
+	};
+	
+	class ReadResponder : public Responder
+	{
+	public:
+		ReadResponder();
+		void setData(U8* data, S32 datasize, S32 imagesize, S32 imageformat, BOOL imagelocal);
+		void setImage(LLImageFormatted* image) { mFormattedImage = image; }
+	protected:
+		LLPointer<LLImageFormatted> mFormattedImage;
+		S32 mImageSize;
+		BOOL mImageLocal;
+	};
+
+	class WriteResponder : public Responder
+	{
+		void setData(U8* data, S32 datasize, S32 imagesize, S32 imageformat, BOOL imagelocal)
+		{
+			// not used
+		}
+	};
+	
+	LLTextureCache(bool threaded);
+	~LLTextureCache();
+
+	/*virtual*/ S32 update(U32 max_time_ms);	
+	
+	void purgeCache(ELLPath location);
+	S64 initCache(ELLPath location, S64 maxsize, BOOL read_only);
+
+	handle_t readFromCache(const LLUUID& id, U32 priority, S32 offset, S32 size,
+						   ReadResponder* responder);
+	bool readComplete(handle_t handle, bool abort);
+	handle_t writeToCache(const LLUUID& id, U32 priority, U8* data, S32 datasize, S32 imagesize,
+						  WriteResponder* responder);
+	bool writeComplete(handle_t handle, bool abort = false);
+	void prioritizeWrite(handle_t handle);
+
+	void removeFromCache(const LLUUID& id);
+
+	// For LLTextureCacheWorker::Responder
+	LLTextureCacheWorker* getReader(handle_t handle);
+	LLTextureCacheWorker* getWriter(handle_t handle);
+	void lockWorkers() { mWorkersMutex.lock(); }
+	void unlockWorkers() { mWorkersMutex.unlock(); }
+
+	// debug
+	S32 getNumReads() { return mReaders.size(); }
+	S32 getNumWrites() { return mWriters.size(); }
+
+protected:
+	// Accessed by LLTextureCacheWorker
+	apr_pool_t* getFileAPRPool() { return mFileAPRPool; }
+	bool appendToTextureEntryList(const LLUUID& id, S32 size);
+	std::string getLocalFileName(const LLUUID& id);
+	std::string getTextureFileName(const LLUUID& id);
+	
+private:
+	void setDirNames(ELLPath location);
+	void readHeaderCache(apr_pool_t* poolp = NULL);
+	void purgeAllTextures(bool purge_directories);
+	void purgeTextures(bool validate);
+	S32 getHeaderCacheEntry(const LLUUID& id, bool touch, S32* imagesize = NULL);
+	bool removeHeaderCacheEntry(const LLUUID& id);
+	void lockHeaders() { mHeaderMutex.lock(); }
+	void unlockHeaders() { mHeaderMutex.unlock(); }
+	
+private:
+	// Internal
+	LLMutex mWorkersMutex;
+	LLMutex mHeaderMutex;
+	apr_pool_t* mFileAPRPool;
+	
+	typedef std::map<handle_t, LLTextureCacheWorker*> handle_map_t;
+	handle_map_t mReaders;
+	handle_map_t mWriters;
+	std::vector<handle_t> mPrioritizeWriteList;
+	
+	BOOL mReadOnly;
+	
+	// Entries
+	struct EntriesInfo
+	{
+		F32 mVersion;
+		U32 mEntries;
+	};
+	struct Entry
+	{
+		Entry() {}
+		Entry(const LLUUID& id, S32 size, U32 time) : mID(id), mSize(size), mTime(time) {}
+		LLUUID mID; // 128 bits
+		S32 mSize; // total size of image if known (NOT size cached)
+		U32 mTime; // seconds since 1/1/1970
+	};
+
+	// HEADERS (Include first mip)
+	std::string mHeaderEntriesFileName;
+	std::string mHeaderDataFileName;
+	EntriesInfo mHeaderEntriesInfo;
+	typedef std::map<S32,LLUUID> index_map_t;
+	index_map_t mLRU; // index, id; stored as a map for fast removal
+	typedef std::map<LLUUID,S32> id_map_t;
+	id_map_t mHeaderIDMap;
+
+	// BODIES (TEXTURES minus headers)
+	std::string mTexturesDirName;
+	std::string mTexturesDirEntriesFileName;
+	typedef std::map<LLUUID,S32> size_map_t;
+	size_map_t mTexturesSizeMap;
+	S64 mTexturesSizeTotal;
+	LLAtomic32<BOOL> mDoPurge;
+	
+	// Statics
+	static F32 sHeaderCacheVersion;
+	static U32 sCacheMaxEntries;
+	static S64 sCacheMaxTexturesSize;
+};
+
+#endif // LL_LLTEXTURECACHE_H
diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp
index 75777024dae907748826c7d4b94f1ac9f6baf9c9..db747c60fc83356cf599ce0724717d04850dedaa 100644
--- a/indra/newview/lltexturefetch.cpp
+++ b/indra/newview/lltexturefetch.cpp
@@ -1,6 +1,6 @@
 /** 
- * @file llviewerimage.cpp
- * @brief Object which handles a received image (and associated texture(s))
+ * @file lltexturecache.cpp
+ * @brief Object which fetches textures from the cache and/or network
  *
  * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
  * $License$
@@ -12,96 +12,283 @@
 
 #include "lltexturefetch.h"
 
-#include "llworkerthread.h"
+#include "llcurl.h"
+#include "llhttpclient.h"
 #include "llimage.h"
-#include "llvfs.h"
+#include "llimageworker.h"
+#include "llworkerthread.h"
 
+#include "llagent.h"
+#include "lltexturecache.h"
+#include "llviewerimagelist.h"
 #include "llviewerimage.h"
+#include "llviewerregion.h"
 #include "viewer.h"
 
 //////////////////////////////////////////////////////////////////////////////
 
+//static
 class LLTextureFetchWorker : public LLWorkerClass
 {
-	friend class LLTextureFetchImpl;
-
-public:
-	// From LLWorkerClass
-	static void initClass(bool threaded, bool runalways);
-	static void updateClass();
-	static void cleanupClass();
-
-	// New methods
-	static LLTextureFetchWorker* getWorker(const LLUUID& id, const LLHost& host,
-										   F32 mPriority, S32 discard,
-										   BOOL needs_aux = FALSE);
-	static LLTextureFetchWorker* getActiveWorker(const LLUUID& id);
+	friend class LLTextureFetch;
 	
 private:
-	static void sendRequestListToSimulators();
+	class URLResponder : public LLHTTPClient::Responder
+	{
+	public:
+		URLResponder(LLTextureFetch* fetcher, const LLUUID& id)
+			: mFetcher(fetcher), mID(id)
+		{
+		}
+		~URLResponder()
+		{
+		}
+		virtual void error(U32 status, const std::string& reason)
+		{
+			mFetcher->lockQueue();
+			LLTextureFetchWorker* worker = mFetcher->getWorker(mID);
+			if (worker)
+			{
+				llinfos << "LLTextureFetchWorker::URLResponder::error " << status << ": " << reason << llendl;
+ 				worker->callbackURLReceived(LLSD(), false);
+			}
+			mFetcher->unlockQueue();
+		}
+		virtual void result(const LLSD& content)
+		{
+			mFetcher->lockQueue();
+			LLTextureFetchWorker* worker = mFetcher->getWorker(mID);
+			if (worker)
+			{
+ 				worker->callbackURLReceived(content, true);
+			}
+			mFetcher->unlockQueue();
+		}
+	private:
+		LLTextureFetch* mFetcher;
+		LLUUID mID;
+	};
+
+	class HTTPGetResponder : public LLCurl::Responder
+	{
+	public:
+		HTTPGetResponder(LLTextureFetch* fetcher, const LLUUID& id)
+			: mFetcher(fetcher), mID(id)
+		{
+		}
+		~HTTPGetResponder()
+		{
+		}
+		virtual void completed(U32 status, const std::stringstream& content)
+		{
+			mFetcher->lockQueue();
+			LLTextureFetchWorker* worker = mFetcher->getWorker(mID);
+			if (worker)
+			{
+				const std::string& cstr = content.str();
+				if (200 <= status &&  status < 300)
+				{
+					if (203 == status) // partial information (i.e. last block)
+					{
+						worker->callbackHttpGet((U8*)cstr.c_str(), cstr.length(), true);
+					}
+					else
+					{
+						worker->callbackHttpGet((U8*)cstr.c_str(), cstr.length(), false);
+					}
+				}
+				else
+				{
+					llinfos << "LLTextureFetchWorker::HTTPGetResponder::error " << status << ": " << cstr << llendl;
+					worker->callbackHttpGet(NULL, -1, true);
+				}
+			}
+			mFetcher->unlockQueue();
+		}
+	private:
+		LLTextureFetch* mFetcher;
+		LLUUID mID;
+	};
+
+	class CacheReadResponder : public LLTextureCache::ReadResponder
+	{
+	public:
+		CacheReadResponder(LLTextureFetch* fetcher, const LLUUID& id, LLImageFormatted* image)
+			: mFetcher(fetcher), mID(id)
+		{
+			setImage(image);
+		}
+		virtual void completed(bool success)
+		{
+			mFetcher->lockQueue();
+			LLTextureFetchWorker* worker = mFetcher->getWorker(mID);
+			if (worker)
+			{
+ 				worker->callbackCacheRead(success, mFormattedImage, mImageSize, mImageLocal);
+			}
+			mFetcher->unlockQueue();
+		}
+	private:
+		LLTextureFetch* mFetcher;
+		LLUUID mID;
+	};
+
+	class CacheWriteResponder : public LLTextureCache::WriteResponder
+	{
+	public:
+		CacheWriteResponder(LLTextureFetch* fetcher, const LLUUID& id)
+			: mFetcher(fetcher), mID(id)
+		{
+		}
+		virtual void completed(bool success)
+		{
+			mFetcher->lockQueue();
+			LLTextureFetchWorker* worker = mFetcher->getWorker(mID);
+			if (worker)
+			{
+				worker->callbackCacheWrite(success);
+			}
+			mFetcher->unlockQueue();
+		}
+	private:
+		LLTextureFetch* mFetcher;
+		LLUUID mID;
+	};
+	
+	class DecodeResponder : public LLResponder
+	{
+	public:
+		DecodeResponder(LLTextureFetch* fetcher, const LLUUID& id, LLTextureFetchWorker* worker)
+			: mFetcher(fetcher), mID(id), mWorker(worker)
+		{
+		}
+		virtual void completed(bool success)
+		{
+			mFetcher->lockQueue();
+			LLTextureFetchWorker* worker = mFetcher->getWorker(mID);
+			if (worker)
+			{
+ 				worker->callbackDecoded(success);
+			}
+			mFetcher->unlockQueue();
+		}
+	private:
+		LLTextureFetch* mFetcher;
+		LLUUID mID;
+		LLTextureFetchWorker* mWorker; // debug only (may get deleted from under us, use mFetcher/mID)
+	};
+
+	struct Compare
+	{
+		// lhs < rhs
+		bool operator()(const LLTextureFetchWorker* lhs, const LLTextureFetchWorker* rhs) const
+		{
+			// greater priority is "less"
+			const F32 lpriority = lhs->mImagePriority;
+			const F32 rpriority = rhs->mImagePriority;
+			if (lpriority > rpriority) // higher priority
+				return true;
+			else if (lpriority < rpriority)
+				return false;
+			else
+				return lhs < rhs;
+		}
+	};
 	
 public:
-	virtual bool doWork(S32 param); // Called from LLWorkerThread::processRequest()
+	/*virtual*/ bool doWork(S32 param); // Called from LLWorkerThread::processRequest()
+	/*virtual*/ void finishWork(S32 param, bool completed); // called from finishRequest() (WORK THREAD)
+	/*virtual*/ bool deleteOK(); // called from update() (WORK THREAD)
 
 	~LLTextureFetchWorker();
 	void relese() { --mActiveCount; }
 
-
 protected:
-	LLTextureFetchWorker(const LLUUID& id, const LLHost& host, F32 mPriority, S32 discard);
+	LLTextureFetchWorker(LLTextureFetch* fetcher, const LLUUID& id, const LLHost& host,
+						 F32 priority, S32 discard, S32 size);
 
 private:
-	virtual void startWork(S32 param); // called from addWork() (MAIN THREAD)
-	virtual void endWork(S32 param, bool aborted); // called from doWork() (MAIN THREAD)
+	/*virtual*/ void startWork(S32 param); // called from addWork() (MAIN THREAD)
+	/*virtual*/ void endWork(S32 param, bool aborted); // called from doWork() (MAIN THREAD)
 
+	void resetFormattedData();
+	
 	void setImagePriority(F32 priority);
-	void setDesiredDiscard(S32 discard);
-	void insertPacket(S32 index, U8* data, S32 size);
+	void setDesiredDiscard(S32 discard, S32 size);
+	bool insertPacket(S32 index, U8* data, S32 size);
 	void clearPackets();
 	U32 calcWorkPriority();
-	bool startVFSLoad(LLVFS* vfs, LLAssetType::EType asset_type);
-	bool loadFromVFS();
+	void removeFromCache();
 	bool processSimulatorPackets();
 	void startDecode();
 	bool decodeImage();
-
+	bool writeToCacheComplete();
+	
 	void lockWorkData() { mWorkMutex.lock(); }
 	void unlockWorkData() { mWorkMutex.unlock(); }
-	
-	static void lockQueue() { sDataMutex->lock(); }
-	static void unlockQueue() { sDataMutex->unlock(); }
+
+	void callbackURLReceived(const LLSD& data, bool success);
+	void callbackHttpGet(U8* data, S32 data_size, bool last_block);
+	void callbackCacheRead(bool success, LLImageFormatted* image,
+						   S32 imagesize, BOOL islocal);
+	void callbackCacheWrite(bool success);
+	void callbackDecoded(bool success);
 	
 private:
 	enum e_state
 	{
-		INIT = 1,
-		LOAD_FROM_CACHE,
+		// NOTE: Affects LLTextureBar::draw in lltextureview.cpp (debug hack)
+		INVALID = 0,
+		INIT,
+		LOAD_FROM_TEXTURE_CACHE,
+		CACHE_POST,
+		LOAD_FROM_NETWORK,
 		LOAD_FROM_SIMULATOR,
-		WRITE_TO_VFS,
+		LOAD_FROM_HTTP_GET_URL,
+		LOAD_FROM_HTTP_GET_DATA,
 		DECODE_IMAGE,
 		DECODE_IMAGE_UPDATE,
+		WRITE_TO_CACHE,
+		WAIT_ON_WRITE,
 		DONE
 	};
+	static const char* sStateDescs[];
 	e_state mState;
+	LLTextureFetch* mFetcher;
+	LLImageWorker* mImageWorker;
 	LLPointer<LLImageFormatted> mFormattedImage;
 	LLPointer<LLImageRaw> mRawImage;
 	LLPointer<LLImageRaw> mAuxImage;
 	LLUUID mID;
 	LLHost mHost;
-	F32 mPriority;
+	F32 mImagePriority;
+	U32 mWorkPriority;
+	F32 mRequestedPriority;
 	S32 mDesiredDiscard;
+	S32 mSimRequestedDiscard;
 	S32 mRequestedDiscard;
+	S32 mLoadedDiscard;
 	S32 mDecodedDiscard;
 	LLFrameTimer mRequestedTimer;
 	LLFrameTimer mIdleTimer;
-	LLVFSThread::handle_t mFileHandle;
+	LLTextureCache::handle_t mCacheReadHandle;
+	LLTextureCache::handle_t mCacheWriteHandle;
 	U8* mBuffer;
 	S32 mBufferSize;
 	S32 mRequestedSize;
+	S32 mDesiredSize;
+	S32 mFileSize;
+	S32 mCachedSize;
 	BOOL mLoaded;
 	BOOL mRequested;
 	BOOL mDecoded;
+	BOOL mWritten;
 	BOOL mNeedsAux;
+	BOOL mHaveAllData;
+	BOOL mUseHTTPGet;
+	BOOL mInLocalCache;
+	S32 mRetryAttempt;
+	std::string mURL;
 	S32 mActiveCount;
 
 	// Work Data
@@ -110,520 +297,817 @@ private:
 	{
 		PacketData(U8* data, S32 size) { mData = data; mSize = size; }
 		~PacketData() { clearData(); }
-		void clearData() { delete mData; mData = NULL; }
+		void clearData() { delete[] mData; mData = NULL; }
 		U8* mData;
 		U32 mSize;
 	};
 	std::vector<PacketData*> mPackets;
+	S32 mFirstPacket;
 	S32 mLastPacket;
-	S32 mTotalPackets;
-	S32 mTotalBytes;
-	
-	// Class variables (statics)
-	
-	static LLWorkerThread* sWorkerThread;
-	static LLMutex* sDataMutex;
-
-	// Map of all requests by UUID
-	typedef std::map<LLUUID,LLTextureFetchWorker*> map_t;
-	static map_t sRequests;
-
-	// Set of requests that require network data
-	typedef std::set<LLTextureFetchWorker*> queue_t ;
-	static queue_t sNetworkQueue;
-
-	static LLFrameTimer sNetworkTimer;
+	U16 mTotalPackets;
+	U8 mImageCodec;
+	LLFrameTimer mFetchTimer; // debug
 };
 
-//statics
-LLTextureFetchWorker::map_t LLTextureFetchWorker::sRequests;
-LLTextureFetchWorker::queue_t LLTextureFetchWorker::sNetworkQueue;
-LLFrameTimer LLTextureFetchWorker::sNetworkTimer;
-LLWorkerThread* LLTextureFetchWorker::sWorkerThread = NULL;
-LLMutex* LLTextureFetchWorker::sDataMutex = NULL;
-
-// called from MAIN THREAD
-//static
-void LLTextureFetchWorker::initClass(bool threaded, bool runalways)
-{
-	sWorkerThread = new LLWorkerThread(threaded, runalways);
-	sDataMutex = new LLMutex(sWorkerThread->getAPRPool());
-}
-
-// called from MAIN THREAD
 //static
-void LLTextureFetchWorker::updateClass()
-{
-	const F32 REQUEST_TIME = 1.f;
-	const F32 MIN_IDLE_TIME = 1.f * 60.f; // 1 minute
-	const F32 MAX_IDLE_TIME = 5.f * 60.f; // 5 minutes
-	const S32 MIN_IDLE_COUNT = 16; // always keep last 16 idle requests
-	const F32 MAX_IDLE_COUNT = 1024; // max number of idle requests
-
-	// Periodically, gather the list of textures that need data from the network
-	// And send the requests out to the simulators
-	if (sNetworkTimer.getElapsedTimeF32() >= REQUEST_TIME)
-	{
-		sNetworkTimer.reset();
-		sendRequestListToSimulators();
-	}
-	// Remove any old requests (releasing their raw data)
-	typedef std::pair<F32, LLTextureFetchWorker*> idle_pair;
-	typedef std::set<idle_pair, compare_pair_greater<F32,LLTextureFetchWorker*> > idle_set;
-	idle_set remove_set;
-	for (map_t::iterator iter = sRequests.begin(); iter != sRequests.end(); ++iter)
-	{
-		LLTextureFetchWorker* worker = iter->second;
-		if (worker->mActiveCount > 0)
-			continue;
-		if (worker->haveWork())
-			continue;
-		F32 idletime = worker->mIdleTimer.getElapsedTimeF32();
-		if (idletime < MIN_IDLE_TIME)
-			continue;
-		remove_set.insert(std::make_pair(idletime, worker));
-	}
-	S32 num_left = remove_set.size();
-	for (idle_set::iterator iter = remove_set.begin(); iter != remove_set.end(); ++iter)
-	{
-		if (num_left <= MIN_IDLE_COUNT)
-			break;
-		if (iter->first < MAX_IDLE_TIME &&
-			num_left < MAX_IDLE_COUNT)
-			break;
-		num_left--;
-	}
-}
+const char* LLTextureFetchWorker::sStateDescs[] = {
+	"INVALID",
+	"INIT",
+	"LOAD_FROM_TEXTURE_CACHE",
+	"CACHE_POST",
+	"LOAD_FROM_NETWORK",
+	"LOAD_FROM_SIMULATOR",
+	"LOAD_FROM_HTTP_URL",
+	"LOAD_FROM_HTTP_DATA",
+	"DECODE_IMAGE",
+	"DECODE_IMAGE_UPDATE",
+	"WRITE_TO_CACHE",
+	"WAIT_ON_WRITE",
+	"DONE",
+};
 
 // called from MAIN THREAD
-//static
-void LLTextureFetchWorker::sendRequestListToSimulators()
-{
-	const F32 LAZY_FLUSH_TIMEOUT = 60.f;
-	const S32 IMAGES_PER_REQUEST = 50;
-	// Send requests
-	typedef std::map< LLHost, std::vector<LLTextureFetchWorker*> > work_request_map_t;
-	work_request_map_t requests;
-	for (queue_t::iterator iter = sNetworkQueue.begin(); iter != sNetworkQueue.end(); ++iter)
-	{
-		LLTextureFetchWorker* req = *iter;
-		if (req->haveWork())
-		{
-			continue; // busy
-		}
-		if ((req->mRequestedDiscard == req->mDesiredDiscard) &&
-			(req->mRequestedTimer.getElapsedTimeF32() < LAZY_FLUSH_TIMEOUT))
-		{
-			continue;
-		}
-		req->mRequestedDiscard = req->mDesiredDiscard;
-		req->mRequestedTimer.reset();
-		requests[req->mHost].push_back(req);
-	}
-	for (work_request_map_t::iterator iter1 = requests.begin();
-		 iter1 != requests.end(); ++iter1)
-	{
-		LLHost host = iter1->first;
-		// invalid host = load from static VFS
-		if (host != LLHost::invalid)
-		{
-			S32 request_count = 0;
-			for (std::vector<LLTextureFetchWorker*>::iterator iter2 = iter1->second.begin();
-				 iter2 != iter1->second.end(); ++iter2)
-			{
-				LLTextureFetchWorker* req = *iter2;
-				if (0 == request_count)
-				{
-					gMessageSystem->newMessageFast(_PREHASH_RequestImage);
-				}
-				S32 packet = req->mLastPacket + 1;
-				gMessageSystem->nextBlockFast(_PREHASH_RequestImage);
-				gMessageSystem->addUUIDFast(_PREHASH_Image, req->mID);
-				gMessageSystem->addS32Fast(_PREHASH_DiscardLevel, req->mDesiredDiscard);
-				gMessageSystem->addF32Fast(_PREHASH_DownloadPriority, req->mPriority);
-				gMessageSystem->addU32Fast(_PREHASH_Packet, packet);
-
-				request_count++;
-				if (request_count >= IMAGES_PER_REQUEST)
-				{
-					gMessageSystem->sendSemiReliable(host, NULL, NULL);
-					request_count = 0;
-				}
-			}
-			if (request_count >= IMAGES_PER_REQUEST)
-			{
-				gMessageSystem->sendSemiReliable(host, NULL, NULL);
-			}
-		}
-	}
-}
-
-//static
-LLTextureFetchWorker* LLTextureFetchWorker::getWorker(const LLUUID& id,
-													  const LLHost& host,
-													  F32 priority,
-													  S32 discard,
-													  BOOL needs_aux)
-{
-	LLTextureFetchWorker* res;
-	lockQueue();
-	map_t::iterator iter = sRequests.find(id);
-	if (iter != sRequests.end())
-	{
-		res = iter->second;
-		if (res->mHost != host)
-		{
-			llerrs << "LLTextureFetchWorker::getWorker called with multiple hosts" << llendl;
-		}
-		res->setImagePriority(priority);
-		res->setDesiredDiscard(discard);
-		
-	}
-	else
-	{
-		res = new LLTextureFetchWorker(id, host, priority, discard);
-	}
-	unlockQueue();
-	res->mActiveCount++;
-	res->mNeedsAux = needs_aux;
-	return res;
-}
-
-LLTextureFetchWorker* LLTextureFetchWorker::getActiveWorker(const LLUUID& id)
-{
-	LLTextureFetchWorker* res = NULL;
-	lockQueue();
-	map_t::iterator iter = sRequests.find(id);
-	if (iter != sRequests.end())
-	{
-		res = iter->second;
-	}
-	unlockQueue();
-	return res;
-}
 
-LLTextureFetchWorker::LLTextureFetchWorker(const LLUUID& id,	// Image UUID
+LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher,
+										   const LLUUID& id,	// Image UUID
 										   const LLHost& host,	// Simulator host
 										   F32 priority,		// Priority
-										   S32 discard)			// Desired discard level
-	: LLWorkerClass(sWorkerThread, "TextureFetch"),
+										   S32 discard,			// Desired discard
+										   S32 size)			// Desired size
+	: LLWorkerClass(fetcher, "TextureFetch"),
 	  mState(INIT),
+	  mFetcher(fetcher),
+	  mImageWorker(NULL),
 	  mID(id),
 	  mHost(host),
-	  mPriority(priority),
-	  mDesiredDiscard(discard),
+	  mImagePriority(priority),
+	  mWorkPriority(0),
+	  mRequestedPriority(0.f),
+	  mDesiredDiscard(-1),
+	  mSimRequestedDiscard(-1),
 	  mRequestedDiscard(-1),
+	  mLoadedDiscard(-1),
 	  mDecodedDiscard(-1),
-	  mFileHandle(LLVFSThread::nullHandle()),
+	  mCacheReadHandle(LLTextureCache::nullHandle()),
+	  mCacheWriteHandle(LLTextureCache::nullHandle()),
 	  mBuffer(NULL),
 	  mBufferSize(0),
 	  mRequestedSize(0),
+	  mDesiredSize(FIRST_PACKET_SIZE),
+	  mFileSize(0),
+	  mCachedSize(0),
 	  mLoaded(FALSE),
 	  mRequested(FALSE),
 	  mDecoded(FALSE),
+	  mWritten(FALSE),
+	  mNeedsAux(FALSE),
+	  mHaveAllData(FALSE),
+	  mUseHTTPGet(FALSE),
+	  mInLocalCache(FALSE),
+	  mRetryAttempt(0),
 	  mActiveCount(0),
-	  mWorkMutex(sWorkerThread->getAPRPool()),
+	  mWorkMutex(fetcher->getWorkerAPRPool()),
+	  mFirstPacket(0),
 	  mLastPacket(-1),
 	  mTotalPackets(0),
-	  mTotalBytes(0)
+	  mImageCodec(IMG_CODEC_INVALID)
 {
-	lockQueue();
-	sRequests[mID] = this;
-	unlockQueue();	
-	addWork(0, calcWorkPriority());
+	calcWorkPriority();
+	if ((gSavedSettings.getBOOL("ImagePipelineUseHTTP")) &&
+		(host == LLHost::invalid))
+	{
+		mUseHTTPGet = TRUE;
+	}
+	if (!mFetcher->mDebugPause)
+	{
+		U32 work_priority = mWorkPriority | LLWorkerThread::PRIORITY_HIGH;
+		addWork(0, work_priority );
+	}
+	setDesiredDiscard(discard, size);
 }
 
 LLTextureFetchWorker::~LLTextureFetchWorker()
 {
-	lockQueue();
-	mFormattedImage = NULL;
-	map_t::iterator iter = sRequests.find(mID);
-	if (iter != sRequests.end() && iter->second == this)
+	llassert_always(!haveWork());
+	lockWorkData();
+	if (mCacheReadHandle != LLTextureCache::nullHandle())
 	{
-		sRequests.erase(iter);
+		mFetcher->mTextureCache->readComplete(mCacheReadHandle, true);
 	}
-	sNetworkQueue.erase(this);
-	unlockQueue();
+	if (mCacheWriteHandle != LLTextureCache::nullHandle())
+	{
+		mFetcher->mTextureCache->writeComplete(mCacheWriteHandle, true);
+	}
+	if (mImageWorker)
+	{
+		mImageWorker->scheduleDelete();
+	}
+	mFormattedImage = NULL;
 	clearPackets();
+	unlockWorkData();
 }
 
 void LLTextureFetchWorker::clearPackets()
 {
-	lockWorkData();
 	for_each(mPackets.begin(), mPackets.end(), DeletePointer());
 	mPackets.clear();
-	unlockWorkData();
+	mTotalPackets = 0;
+	mLastPacket = -1;
+	mFirstPacket = 0;
 }
 
 U32 LLTextureFetchWorker::calcWorkPriority()
 {
 	F32 priority_scale = (F32)LLWorkerThread::PRIORITY_LOWBITS / LLViewerImage::maxDecodePriority();
-	U32 priority = (U32)(mPriority * priority_scale);
-	return LLWorkerThread::PRIORITY_NORMAL | priority;
+	mWorkPriority = (U32)(mImagePriority * priority_scale);
+	return mWorkPriority;
 }
 
-void LLTextureFetchWorker::setDesiredDiscard(S32 discard)
+void LLTextureFetchWorker::setDesiredDiscard(S32 discard, S32 size)
 {
 	if (mDesiredDiscard != discard)
 	{
-		mDesiredDiscard = discard;
 		if (!haveWork())
 		{
-			addWork(0, calcWorkPriority());
+			calcWorkPriority();
+			if (!mFetcher->mDebugPause)
+			{
+				U32 work_priority = mWorkPriority | LLWorkerThread::PRIORITY_HIGH;
+				addWork(0, work_priority);
+			}
+		}
+		else if (mDesiredDiscard < discard)
+		{
+			U32 work_priority = mWorkPriority | LLWorkerThread::PRIORITY_HIGH;
+			setPriority(work_priority);
 		}
+		mDesiredDiscard = discard;
+		mDesiredSize = size;
 	}
-}
-
-void LLTextureFetchWorker::setImagePriority(F32 priority)
-{
-	if (priority != mPriority)
+	else if (size > mDesiredSize)
 	{
-		mPriority = priority;
-		setPriority(calcWorkPriority());
+		mDesiredSize = size;
+		U32 work_priority = mWorkPriority | LLWorkerThread::PRIORITY_HIGH;
+		setPriority(work_priority);
 	}
 }
 
-void LLTextureFetchWorker::insertPacket(S32 index, U8* data, S32 size)
+void LLTextureFetchWorker::setImagePriority(F32 priority)
 {
-	PacketData* packet = new PacketData(data, size);
-	
-	lockWorkData();
-	if (index >= (S32)mPackets.size())
-	{
-		mPackets.resize(index+1, (PacketData*)NULL); // initializes v to NULL pointers
-	}
-	if (mPackets[index] != NULL)
-	{
-		llwarns << "LLTextureFetchWorker::insertPacket called for duplicate packet: " << index << llendl;
-	}
-	mPackets[index] = packet;
-	while (mLastPacket+1 < (S32)mPackets.size() && mPackets[mLastPacket+1] != NULL)
+	F32 delta = fabs(priority - mImagePriority);
+	if (delta > (mImagePriority * .05f)) // 5%
 	{
-		++mLastPacket;
+		mImagePriority = priority;
+		calcWorkPriority();
+		U32 work_priority = mWorkPriority | (getPriority() & LLWorkerThread::PRIORITY_HIGHBITS);
+		setPriority(work_priority);
 	}
-	unlockWorkData();
 }
 
-// Called from LLWorkerThread::processRequest()
-bool LLTextureFetchWorker::doWork(S32 param)
+void LLTextureFetchWorker::resetFormattedData()
 {
-	switch(mState)
-	{
-	  case INIT:
-	  {
-		  // fall through
-	  }
-	  case LOAD_FROM_CACHE:
-	  {
-		  // Load any existing data from the cache
-		  if (mFileHandle == LLVFSThread::nullHandle())
-		  {
-			  bool res = startVFSLoad(gVFS, LLAssetType::AT_TEXTURE);
-			  if (!res) res = startVFSLoad(gStaticVFS, LLAssetType::AT_TEXTURE_TGA);
-			  if (!res) res = startVFSLoad(gStaticVFS, LLAssetType::AT_TEXTURE);
-			  if (!res)
-			  {
-				  // Didn't load from VFS
-				  mFormattedImage = new LLImageJ2C;
-				  mState = LOAD_FROM_SIMULATOR;
-			  }
-		  }
-		  if (mFileHandle != LLVFSThread::nullHandle())
-		  {
-			  if (!loadFromVFS())
-			  {
-				  return false; // not done
-			  }
-			  if (!mLoaded)
-			  {
-				  llwarns << "Load from VFS failed on: " << mID << llendl;
-				  return true;
-			  }
-			  bool res = mFormattedImage->setData(mBuffer, mBufferSize);
-			  if (!res)
-			  {
-				  llwarns << "loadLocalImage() - setData() failed" << llendl;
-				  mFormattedImage->deleteData();
-				  return true;
-			  }
-			  // Successfully loaded
-			  if (mFormattedImage->getDiscardLevel() <= mRequestedDiscard)
-			  {
-				  // we have enough data, decode it
-				  mState = DECODE_IMAGE;
-				  mRequestedSize = mBufferSize;
-			  }
-			  else
-			  {
-				  // need more data
-				  mState = LOAD_FROM_SIMULATOR;
-				  mRequestedSize = mFormattedImage->calcDataSize(mRequestedDiscard);
-			  }
-		  }
-		  return false;
-	  }
-	  case LOAD_FROM_SIMULATOR:
-	  {
-		  if (!mRequested)
-		  {
-			  lockQueue();
-			  sNetworkQueue.insert(this);
-			  unlockQueue();
-			  mRequested = TRUE;
-		  }
-		  if (processSimulatorPackets())
-		  {
-			  mState = WRITE_TO_VFS;
-		  }
-		  return false;
-	  }
-	  case WRITE_TO_VFS:
-	  {
-		  mState = DECODE_IMAGE;
-		  // fall through
-	  }
-	  case DECODE_IMAGE:
-	  {
-		  startDecode();
-		  mState = DECODE_IMAGE_UPDATE;
-		  // fall through
-	  }
-	  case DECODE_IMAGE_UPDATE:
-	  {
-		  if (decodeImage())
-		  {
-			  mState = DONE;
-		  }
-		  return false;
-	  }
-	  case DONE:
-	  {
-		  // Do any cleanup here
-		  // Destroy the formatted image, we don't need it any more (raw image is still valid)
-		  mFormattedImage = NULL;
-		  mIdleTimer.reset();
-		  return true;
-	  }
-	  default:
-	  {
-		  llerrs << "LLTextureFetchWorker::doWork() has illegal state" << llendl;
-		  return true;
-	  }
+	delete[] mBuffer;
+	mBuffer = NULL;
+	mBufferSize = 0;
+	if (mFormattedImage.notNull())
+	{
+		mFormattedImage->deleteData();
 	}
+	mHaveAllData = FALSE;
 }
 
 // Called from MAIN thread
 void LLTextureFetchWorker::startWork(S32 param)
 {
+	llassert(mImageWorker == NULL);
+	llassert(mFormattedImage.isNull());
 }
 
-void LLTextureFetchWorker::endWork(S32 param, bool aborted)
-{
-}
-
-//////////////////////////////////////////////////////////////////////////////
+#include "llviewerimagelist.h" // debug
 
-bool LLTextureFetchWorker::startVFSLoad(LLVFS* vfs, LLAssetType::EType asset_type)
+// Called from LLWorkerThread::processRequest()
+bool LLTextureFetchWorker::doWork(S32 param)
 {
-	// Start load from VFS if it's there
-	if (vfs->getExists(mID, asset_type))
-	{
-		mBufferSize = vfs->getSize(mID, asset_type);
-		mBuffer = new U8[mBufferSize];
-		mFileHandle = LLVFSThread::sLocal->read(vfs, mID, asset_type, mBuffer, 0, mBufferSize);		/* Flawfinder: ignore */
-		if (mFileHandle == LLVFSThread::nullHandle())
-		{
-			llwarns << "loadLocalImage() - vfs read failed in static VFS: " << mID << llendl;
-			delete mBuffer;
-			mBuffer = NULL;
-			return false;
-		}
-		if (asset_type == LLAssetType::AT_TEXTURE_TGA)
-		{
-			mFormattedImage = new LLImageTGA;
-		}
-		else if (asset_type == LLAssetType::AT_TEXTURE)
-		{
-			mFormattedImage = new LLImageJ2C;
-		}
-		else
-		{
-			llerrs << "LLTextureFetchWorker::startVFSLoad called with bad asset type: " << asset_type << llendl;
-		}
-		return true;
-	}
-	return false;
-}
+	LLMutexLock lock(&mWorkMutex);
 
-bool LLTextureFetchWorker::loadFromVFS()
-{
-	LLMemType mt1(LLMemType::MTYPE_APPFMTIMAGE);
+	e_state old_state = mState;
+	mFetchTimer.reset();
 
-	llassert(mLoaded == FALSE);
-	
-	// Check loading status
-	LLVFSThread::status_t status = LLVFSThread::sLocal->getRequestStatus(mFileHandle);
-	if (status == LLVFSThread::STATUS_QUEUED || status == LLVFSThread::STATUS_INPROGRESS)
+	if (mFetcher->mDebugPause)
 	{
-		return false;
+		return false; // debug: don't do any work
 	}
-	else if (status == LLVFSThread::STATUS_COMPLETE)
+	if (mID == mFetcher->mDebugID)
 	{
-		mLoaded = TRUE;
-		return true;
+		mFetcher->mDebugCount++; // for setting breakpoints
 	}
-	else
+
+	if (mState == INIT)
 	{
-		llwarns << "loadLocalImage() - vfs read failed" << llendl;
-		LLVFSThread::Request* req = (LLVFSThread::Request*)LLVFSThread::sLocal->getRequest(mFileHandle);
-		if (req && mFormattedImage.notNull())
-		{
-			LLVFS* vfs = req->getVFS();
-			LLAssetType::EType asset_type = mFormattedImage->getCodec() == IMG_CODEC_TGA ? LLAssetType::AT_TEXTURE_TGA : LLAssetType::AT_TEXTURE;
-			vfs->removeFile(mID, asset_type);
-		}
-		return true;
+		mRequestedDiscard = -1;
+		mLoadedDiscard = -1;
+		mDecodedDiscard = -1;
+		mRequestedSize = 0;
+		mFileSize = 0;
+		mCachedSize = 0;
+		mLoaded = FALSE;
+		mRequested = FALSE;
+		mDecoded  = FALSE;
+		mWritten  = FALSE;
+		delete[] mBuffer;
+		mBuffer = NULL;
+		mBufferSize = 0;
+		mHaveAllData = FALSE;
+		clearPackets();
+		mCacheReadHandle = LLTextureCache::nullHandle();
+		mCacheWriteHandle = LLTextureCache::nullHandle();
+		mURL.clear();
+		mState = LOAD_FROM_TEXTURE_CACHE;
+		// fall through
 	}
-}
-
-
-//////////////////////////////////////////////////////////////////////////////
 
-bool LLTextureFetchWorker::processSimulatorPackets()
-{
-	bool res = false;
-	lockWorkData();
-	if (mLastPacket >= 0)
+	if (mState == LOAD_FROM_TEXTURE_CACHE)
 	{
-		S32 data_size = 0;
-		for (S32 i = 0; i<=mLastPacket; i++)
+		if (mCacheReadHandle == LLTextureCache::nullHandle())
 		{
-			data_size += mPackets[i]->mSize;
+			U32 cache_priority = mWorkPriority;
+			S32 offset = mFormattedImage.notNull() ? mFormattedImage->getDataSize() : 0;
+			S32 size = mDesiredSize - offset;
+			if (size <= 0)
+			{
+				mState = CACHE_POST;
+				return false;
+			}
+			mFileSize = 0;
+			mLoaded = FALSE;
+			setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it
+			CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage);
+			mCacheReadHandle = mFetcher->mTextureCache->readFromCache(mID, cache_priority,
+																	  offset, size, responder);
 		}
-		if (data_size >= mRequestedSize || mLastPacket == mTotalPackets)
+
+		if (mLoaded)
 		{
-			/// We have enough (or all) data, copy it into mBuffer
-			if (mBufferSize < data_size)
+			// Make sure request is complete. *TODO: make this auto-complete
+			if (mFetcher->mTextureCache->readComplete(mCacheReadHandle, false))
 			{
-				delete mBuffer;
-				mBuffer = new U8[data_size];
-				mBufferSize = data_size;
+				mCacheReadHandle = LLTextureCache::nullHandle();
+				mState = CACHE_POST;
+				// fall through
 			}
-			S32 offset = 0;
-			for (S32 i = 0; i<=mLastPacket; i++)
+			else
 			{
-				if (mPackets[i]->mData != NULL)
-				{
-					memcpy(mBuffer + offset, mPackets[i]->mData, mPackets[i]->mSize); /* Flawfinder: ignore */
-					offset += mPackets[i]->mSize;
-				}
+				return false;
 			}
-			res = true;
+		}
+		else
+		{
+			return false;
 		}
 	}
-	unlockWorkData();
-	return res;
-}
+
+	if (mState == CACHE_POST)
+	{
+		mCachedSize = mFormattedImage.notNull() ? mFormattedImage->getDataSize() : 0;
+		// Successfully loaded
+		if ((mCachedSize >= mDesiredSize) || mHaveAllData)
+		{
+			// we have enough data, decode it
+			llassert_always(mFormattedImage->getDataSize() > 0);
+			mState = DECODE_IMAGE;
+			// fall through
+		}
+		else
+		{
+			// need more data
+			mState = LOAD_FROM_NETWORK;
+			// fall through
+		}
+	}
+
+	if (mState == LOAD_FROM_NETWORK)
+	{
+		if (mFormattedImage.isNull())
+		{
+			mFormattedImage = new LLImageJ2C;
+		}
+		mState = mUseHTTPGet ? LOAD_FROM_HTTP_GET_URL : LOAD_FROM_SIMULATOR;
+		return false;
+	}
+	
+	if (mState == LOAD_FROM_SIMULATOR)
+	{
+		if (!mRequested)
+		{
+			S32 data_size = mFormattedImage->getDataSize();
+			if (data_size > 0)
+			{
+				mFirstPacket = (data_size - FIRST_PACKET_SIZE) / MAX_IMG_PACKET_SIZE + 1;
+				if (FIRST_PACKET_SIZE + (mFirstPacket-1) * MAX_IMG_PACKET_SIZE != data_size)
+				{
+					llwarns << "Bad CACHED TEXTURE size: " << data_size << " removing." << llendl;
+					removeFromCache();
+					resetFormattedData();
+					clearPackets();
+				}
+				else
+				{
+					mLastPacket = mFirstPacket-1;
+					mTotalPackets = (mFileSize - FIRST_PACKET_SIZE + MAX_IMG_PACKET_SIZE-1) / MAX_IMG_PACKET_SIZE + 1;
+				}
+			}
+			mRequested = TRUE;
+			mRequestedSize = mDesiredSize;
+			mRequestedDiscard = mDesiredDiscard;
+			mFetcher->lockQueue();
+			mFetcher->addToNetworkQueue(this);
+			mFetcher->unlockQueue();
+		}
+		if (processSimulatorPackets())
+		{
+			mFetcher->lockQueue();
+			mFetcher->removeFromNetworkQueue(this);
+			mFetcher->unlockQueue();
+			if (!mFormattedImage->getDataSize())
+			{
+				// processSimulatorPackets() failed
+				llwarns << "processSimulatorPackets() failed to load buffer" << llendl;
+				return true; // failed
+			}
+			setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
+			mState = DECODE_IMAGE;
+		}
+		else
+		{
+			setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
+		}
+		return false;
+	}
+	
+	if (mState == LOAD_FROM_HTTP_GET_URL)
+	{
+		if (!mRequested)
+		{
+			mRequested = TRUE;
+			mLoaded = FALSE;
+			std::string url;
+			LLViewerRegion* region = gAgent.getRegion();
+			if (region)
+			{
+				url = region->getCapability("RequestTextureDownload");
+			}
+			if (!url.empty())
+			{
+				LLSD sd;
+				sd = mID.asString();
+				LLHTTPClient::post(url, sd, new URLResponder(mFetcher, mID));
+//*TODO:uncomment 				setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
+				return false;
+			}
+			else
+			{
+				llwarns << mID << ": HTTP get url failed, requesting from simulator" << llendl;
+				mRequested = FALSE;
+				mState = LOAD_FROM_SIMULATOR;
+				return false;
+			}
+		}
+		else
+		{
+			if (mLoaded)
+			{
+				if (!mURL.empty())
+				{
+					mState = LOAD_FROM_HTTP_GET_DATA;
+					mRequested = FALSE; // reset
+					mLoaded = FALSE; // reset
+				}
+				else
+				{
+					llwarns << mID << ": HTTP get url is empty, requesting from simulator" << llendl;
+					mRequested = FALSE;
+					mState = LOAD_FROM_SIMULATOR;
+					return false;
+				}
+			}
+		}
+		// fall through
+	}
+	
+	if (mState == LOAD_FROM_HTTP_GET_DATA)
+	{
+		if (!mRequested)
+		{
+			mRequested = TRUE;
+			S32 cur_size = mFormattedImage->getDataSize(); // amount of data we already have
+			mRequestedSize = mDesiredSize;
+			mRequestedDiscard = mDesiredDiscard;
+#if 1 // *TODO: LLCurl::getByteRange is broken (ignores range)
+			cur_size = 0;
+			mFormattedImage->deleteData();
+#endif
+			mRequestedSize -= cur_size;
+			// 			  F32 priority = mImagePriority / (F32)LLViewerImage::maxDecodePriority(); // 0-1
+			S32 offset = cur_size;
+			mBufferSize = cur_size; // This will get modified by callbackHttpGet()
+			std::string url;
+			if (mURL.empty())
+			{
+				//url = "http://asset.agni/0000002f-38ae-0e17-8e72-712e58964e9c.texture";
+				std::stringstream urlstr;
+				urlstr << "http://asset.agni/" << mID.asString() << ".texture";
+				url = urlstr.str();
+			}
+			else
+			{
+				url = mURL;
+			}
+			mLoaded = FALSE;
+// 			llinfos << "HTTP GET: " << mID << " Offset: " << offset << " Bytes: " << mRequestedSize << llendl;
+			LLCurl::getByteRange(url, offset, mRequestedSize,
+								 new HTTPGetResponder(mFetcher, mID)); // *TODO: use mWorkPriority
+//*TODO:uncomment	setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
+			return false; // not done
+		}
+
+		if (mLoaded)
+		{
+			S32 cur_size = mFormattedImage->getDataSize();
+			if (mRequestedSize < 0)
+			{
+				llwarns << "http get failed for: " << mID << llendl;
+				if (cur_size == 0)
+				{
+					resetFormattedData();
+					return true; // failed
+				}
+				else
+				{
+					mState = DECODE_IMAGE;
+					return false; // use what we have
+				}
+			}
+			llassert(mBufferSize == cur_size + mRequestedSize);
+			if (mHaveAllData)
+			{
+				mFileSize = mBufferSize;
+			}
+			if (mRequestedSize > 0)
+			{
+				U8* buffer = new U8[mBufferSize];
+				if (cur_size > 0)
+				{
+					memcpy(buffer, mFormattedImage->getData(), cur_size);
+				}
+				memcpy(buffer + cur_size, mBuffer, mRequestedSize); // append
+				// NOTE: setData releases current data and owns new data (buffer)
+				mFormattedImage->setData(buffer, mBufferSize);
+				// delete temp data
+				delete[] mBuffer; // Note: not 'buffer' (assigned in setData())
+				mBuffer = NULL;
+				mBufferSize = 0;
+			}
+			else
+			{
+				llassert_always(cur_size);
+			}
+			mLoadedDiscard = mRequestedDiscard;
+			setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
+			mState = DECODE_IMAGE;
+			return false;
+		}
+
+		// NOTE: Priority gets updated when the http get completes (in callbackHTTPGet())
+		setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
+		return false;
+	}
+
+	if (mState == DECODE_IMAGE)
+	{
+		llassert_always(mFormattedImage->getDataSize() > 0);
+		setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it
+		startDecode();
+		mState = DECODE_IMAGE_UPDATE;
+		// fall though (need to call requestDecodedData() to start work)
+	}
+	
+	if (mState == DECODE_IMAGE_UPDATE)
+	{
+		if (decodeImage())
+		{
+			if (mDecodedDiscard < 0)
+			{
+				if (mCachedSize > 0 && !mInLocalCache && mRetryAttempt == 0)
+				{
+					// Cache file should be deleted, try again
+					llwarns << mID << ": Decode of cached file failed (removed), retrying" << llendl;
+					mFormattedImage = NULL;
+					++mRetryAttempt;
+					setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
+					mState = INIT;
+					return false;
+				}
+				else
+				{
+					llwarns << "UNABLE TO LOAD TEXTURE: " << mID << " RETRIES: " << mRetryAttempt << llendl;
+					mState = DONE; // failed
+				}
+			}
+			else
+			{
+				setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
+				mState = WRITE_TO_CACHE;
+			}
+			// fall through
+		}
+		else
+		{
+			return false;
+		}
+	}
+
+	if (mState == WRITE_TO_CACHE)
+	{
+		if (mInLocalCache || !mFileSize || !mRequested)
+		{
+			// If we're in a local cache or we didn't actually receive any new data, skip
+			mState = DONE;
+			return false;
+		}
+		S32 datasize = mFormattedImage->getDataSize();
+		llassert_always(datasize);
+		setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it
+		U32 cache_priority = mWorkPriority;
+		mWritten = FALSE;
+		CacheWriteResponder* responder = new CacheWriteResponder(mFetcher, mID);
+		mCacheWriteHandle = mFetcher->mTextureCache->writeToCache(mID, cache_priority,
+																  mFormattedImage->getData(), datasize,
+																  mFileSize, responder);
+		mState = WAIT_ON_WRITE;
+		// fall through
+	}
+	
+	if (mState == WAIT_ON_WRITE)
+	{
+		if (writeToCacheComplete())
+		{
+			mState = DONE;
+			// fall through
+		}
+		else
+		{
+			if (mDesiredDiscard < mDecodedDiscard)
+			{
+				// We're waiting for this write to complete before we can receive more data
+				// (we can't touch mFormattedImage until the write completes)
+				// Prioritize the write
+				mFetcher->mTextureCache->prioritizeWrite(mCacheWriteHandle);
+			}
+			return false;
+		}
+	}
+
+	if (mState == DONE)
+	{
+		if (mDecodedDiscard >= 0 && mDesiredDiscard < mDecodedDiscard)
+		{
+			// More data was requested, return to INIT
+			mState = INIT;
+			setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
+			return false;
+		}
+		if (old_state != DONE)
+		{
+			mIdleTimer.reset();
+		}
+		return true;
+	}
+	
+	return false;
+}
+
+// Called from MAIN thread
+void LLTextureFetchWorker::endWork(S32 param, bool aborted)
+{
+	if (mImageWorker)
+	{
+		mImageWorker->scheduleDelete();
+		mImageWorker = NULL;
+	}
+	mFormattedImage = NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+// virtual
+void LLTextureFetchWorker::finishWork(S32 param, bool completed)
+{
+	// The following are required in case the work was aborted
+	if (mCacheReadHandle != LLTextureCache::nullHandle())
+	{
+		mFetcher->mTextureCache->readComplete(mCacheReadHandle, true);
+		mCacheReadHandle = LLTextureCache::nullHandle();
+	}
+	if (mCacheWriteHandle != LLTextureCache::nullHandle())
+	{
+		mFetcher->mTextureCache->writeComplete(mCacheWriteHandle, true);
+		mCacheWriteHandle = LLTextureCache::nullHandle();
+	}
+}
+
+// virtual
+bool LLTextureFetchWorker::deleteOK()
+{
+	bool delete_ok = true;
+	// Allow any pending reads or writes to complete
+	if (mCacheReadHandle != LLTextureCache::nullHandle())
+	{
+		if (mFetcher->mTextureCache->readComplete(mCacheReadHandle, true))
+		{
+			mCacheReadHandle = LLTextureCache::nullHandle();
+		}
+		else
+		{
+			delete_ok = false;
+		}
+	}
+	if (mCacheWriteHandle != LLTextureCache::nullHandle())
+	{
+		if (mFetcher->mTextureCache->writeComplete(mCacheWriteHandle))
+		{
+			mCacheWriteHandle = LLTextureCache::nullHandle();
+		}
+		else
+		{
+			delete_ok = false;
+		}
+	}
+	// Don't delete while waiting for network requests *TODO: Need LLCurl::abort()
+	// Don't delete while waiting on writes
+	if ((mState >= LLTextureFetchWorker::LOAD_FROM_HTTP_GET_URL &&
+		 mState <= LLTextureFetchWorker::LOAD_FROM_HTTP_GET_DATA) ||
+		(mState >= LLTextureFetchWorker::WRITE_TO_CACHE &&
+		 mState <= LLTextureFetchWorker::WAIT_ON_WRITE))
+	{
+		delete_ok = false;
+	}
+	return delete_ok;
+}
+
+
+void LLTextureFetchWorker::removeFromCache()
+{
+	if (!mInLocalCache)
+	{
+		mFetcher->mTextureCache->removeFromCache(mID);
+	}
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+
+bool LLTextureFetchWorker::processSimulatorPackets()
+{
+	if (mLastPacket >= mFirstPacket)
+	{
+		S32 buffer_size = mFormattedImage->getDataSize();
+		for (S32 i = mFirstPacket; i<=mLastPacket; i++)
+		{
+			buffer_size += mPackets[i]->mSize;
+		}
+		bool have_all_data = mLastPacket >= mTotalPackets-1;
+		llassert_always(mRequestedSize > 0);
+		if (buffer_size >= mRequestedSize || have_all_data)
+		{
+			/// We have enough (or all) data
+			if (have_all_data)
+			{
+				mHaveAllData = TRUE;
+			}
+			S32 cur_size = mFormattedImage->getDataSize();
+			if (buffer_size > cur_size)
+			{
+				/// We have new data
+				U8* buffer = new U8[buffer_size];
+				S32 offset = 0;
+				if (cur_size > 0 && mFirstPacket > 0)
+				{
+					memcpy(buffer, mFormattedImage->getData(), cur_size);
+					offset = cur_size;
+				}
+				for (S32 i=mFirstPacket; i<=mLastPacket; i++)
+				{
+					memcpy(buffer + offset, mPackets[i]->mData, mPackets[i]->mSize);
+					offset += mPackets[i]->mSize;
+				}
+				// NOTE: setData releases current data
+				mFormattedImage->setData(buffer, buffer_size);
+			}
+			mLoadedDiscard = mRequestedDiscard;
+			return true;
+		}
+	}
+	return false;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void LLTextureFetchWorker::callbackURLReceived(const LLSD& data, bool success)
+{
+	LLMutexLock lock(&mWorkMutex);
+	if (success)
+	{
+		mURL = data.asString();
+	}
+	mLoaded = TRUE;
+	setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void LLTextureFetchWorker::callbackHttpGet(U8* data, S32 data_size, bool last_block)
+{
+	LLMutexLock lock(&mWorkMutex);
+	llassert_always(mRequested);
+// 	llinfos << "HTTP RECEIVED: " << mID.asString() << " Bytes: " << data_size << llendl;
+	if (mLoaded)
+	{
+		llwarns << "Duplicate callback for " << mID.asString() << llendl;
+		return; // ignore duplicate callback
+	}
+	if (data_size >= 0)
+	{
+		if (data_size > 0)
+		{
+			mBuffer = new U8[data_size];
+			// *TODO: set the formatted image data here
+			memcpy(mBuffer, data, data_size);
+			mBufferSize += data_size;
+			if (data_size < mRequestedSize || last_block == true)
+			{
+				mHaveAllData = TRUE;
+			}
+			else if (data_size > mRequestedSize)
+			{
+				// *TODO: This will happen until we fix LLCurl::getByteRange()
+				llinfos << "HUH?" << llendl;
+				mHaveAllData = TRUE;
+				mFormattedImage->deleteData();
+				mBufferSize = data_size;
+			}
+		}
+		else
+		{
+			// We requested data but received none (and no error),
+			// so presumably we have all of it
+			mHaveAllData = TRUE;
+		}
+		mRequestedSize = data_size;
+	}
+	else
+	{
+		mRequestedSize = -1; // error
+	}
+	mLoaded = TRUE;
+	setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void LLTextureFetchWorker::callbackCacheRead(bool success, LLImageFormatted* image,
+											 S32 imagesize, BOOL islocal)
+{
+	LLMutexLock lock(&mWorkMutex);
+	if (success)
+	{
+		llassert_always(imagesize > 0);
+		mFileSize = imagesize;
+		mFormattedImage = image;
+		mImageCodec = image->getCodec();
+		mInLocalCache = islocal;
+		if (mFileSize != 0 && mFormattedImage->getDataSize() >= mFileSize)
+		{
+			mHaveAllData = TRUE;
+		}
+	}
+	mLoaded = TRUE;
+	setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
+}
+
+void LLTextureFetchWorker::callbackCacheWrite(bool success)
+{
+	LLMutexLock lock(&mWorkMutex);
+	mWritten = TRUE;
+	setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void LLTextureFetchWorker::callbackDecoded(bool success)
+{
+// 	llinfos << mID << " : DECODE COMPLETE " << llendl;
+	setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
+}
 
 //////////////////////////////////////////////////////////////////////////////
 
@@ -631,117 +1115,632 @@ void LLTextureFetchWorker::startDecode()
 {
 	mRawImage = NULL;
 	mAuxImage = NULL;
+	llassert_always(mImageWorker == NULL);
+	llassert_always(mFormattedImage.notNull());
+	S32 discard = mHaveAllData ? 0 : mLoadedDiscard;
+	U32 image_priority = LLWorkerThread::PRIORITY_NORMAL | mWorkPriority;
+// 	llinfos << mID << " : DECODE STARTED : " << discard
+// 			<< " Pri: " << priority
+// 			<< " Components:" << (S32)mFormattedImage->getComponents() << llendl;
+	mImageWorker = new LLImageWorker(mFormattedImage, image_priority, discard, new DecodeResponder(mFetcher, mID, this));
 }
 
 bool LLTextureFetchWorker::decodeImage()
 {
-	const F32 MAX_DECODE_TIME = .001f; // 1 ms
-	if (mRawImage->getDataSize() == 0)
+	llassert_always(mImageWorker);
+	bool res = true;
+	if (mRawImage.isNull())
 	{
-		if (!mFormattedImage->requestDecodedData(mRawImage, -1, MAX_DECODE_TIME))
+		res = false;
+		if (mImageWorker->requestDecodedData(mRawImage, -1))
 		{
-			return false;
+			res = true;
+// 			llinfos << mID << " : BASE DECODE FINISHED" << llendl;
+		}
+	}
+	if (res &&
+		(mRawImage.notNull() && mRawImage->getDataSize() > 0) &&
+		(mNeedsAux && mAuxImage.isNull()))
+	{
+		res = false;
+		if (mImageWorker->requestDecodedAuxData(mAuxImage, 4, -1))
+		{
+			res = true;
+// 			llinfos << mID << " : AUX DECODE FINISHED" << llendl;
 		}
-		mFormattedImage->releaseDecodedData(); // so that we have the only ref to the raw image
 	}
-	if (mNeedsAux && mAuxImage->getDataSize() == 0)
+	if (res)
 	{
-		if (!mFormattedImage->requestDecodedAuxData(mAuxImage, 4, -1, MAX_DECODE_TIME ))
+		if ((mRawImage.notNull() && mRawImage->getDataSize() > 0) &&
+			(!mNeedsAux || (mAuxImage.notNull() && mAuxImage->getDataSize() > 0)))
+		{
+			mDecodedDiscard = mFormattedImage->getDiscardLevel();
+// 			llinfos << mID << " : DECODE FINISHED. DISCARD: " << mDecodedDiscard << llendl;
+		}
+		else
+		{
+			llwarns << "DECODE FAILED: " << mID << " Discard: " << (S32)mFormattedImage->getDiscardLevel() << llendl;
+			removeFromCache();
+		}
+		mImageWorker->scheduleDelete();
+		mImageWorker = NULL;
+	}
+	else
+	{
+		U32 image_priority = LLWorkerThread::PRIORITY_NORMAL | mWorkPriority;
+		mImageWorker->setPriority(image_priority);
+		//llinfos << worker->mID << " : DECODE PRIORITY : " << priority << llendl;
+	}
+	return res;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+bool LLTextureFetchWorker::writeToCacheComplete()
+{
+	// Complete write to cache
+	if (mCacheWriteHandle != LLTextureCache::nullHandle())
+	{
+		if (!mWritten)
+		{
+			return false;
+		}
+		if (mFetcher->mTextureCache->writeComplete(mCacheWriteHandle))
+		{
+			mCacheWriteHandle = LLTextureCache::nullHandle();
+		}
+		else
 		{
 			return false;
 		}
-		mFormattedImage->releaseDecodedData(); // so that we have the only ref to the raw image
 	}
-	mDecodedDiscard = mFormattedImage->getDiscardLevel();
 	return true;
 }
 
+
+//////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+// public
+
+LLTextureFetch::LLTextureFetch(LLTextureCache* cache, bool threaded)
+	: LLWorkerThread("TextureFetch", threaded),
+	  mDebugCount(0),
+	  mDebugPause(0),
+	  mTextureCache(cache),
+	  mQueueMutex(getAPRPool())
+{
+}
+
+LLTextureFetch::~LLTextureFetch()
+{
+	// ~LLQueuedThread() called here
+}
+
+bool LLTextureFetch::createRequest(const LLUUID& id, const LLHost& host, F32 priority,
+								   S32 w, S32 h, S32 c, S32 discard, bool needs_aux)
+{
+	LLTextureFetchWorker* worker = NULL;
+	LLMutexLock lock(&mQueueMutex);
+	map_t::iterator iter = mRequestMap.find(id);
+	if (iter != mRequestMap.end())
+	{
+		worker = iter->second;
+		if (worker->mHost != host)
+		{
+ 			llwarns << "LLTextureFetch::createRequest " << id << " called with multiple hosts" << llendl;
+			removeRequest(worker, false);
+			worker = NULL;
+		}
+	}
+	// If the requester knows the dimentions of the image,
+	// this will calculate how much data we need without having to parse the header
+	S32 desired_size;
+	if (w*h*c > 0)
+	{
+		desired_size = LLImageJ2C::calcDataSizeJ2C(w, h, c, discard);
+	}
+	else
+	{
+		desired_size = FIRST_PACKET_SIZE;
+		discard = MAX_DISCARD_LEVEL;
+	}
+	if (worker)
+	{
+		if (worker->wasAborted())
+		{
+			return false; // need to wait for previous aborted request to complete
+		}
+		worker->lockWorkData();
+		worker->setImagePriority(priority);
+		worker->setDesiredDiscard(discard, desired_size);
+		worker->unlockWorkData();
+	}
+	else
+	{
+		worker = new LLTextureFetchWorker(this, id, host, priority, discard, desired_size);
+		mRequestMap[id] = worker;
+	}
+	worker->mActiveCount++;
+	worker->mNeedsAux = needs_aux;
+// 	llinfos << "REQUESTED: " << id << " Discard: " << discard << llendl;
+	return true;
+}
+
+void LLTextureFetch::deleteRequest(const LLUUID& id, bool cancel)
+{
+	LLMutexLock lock(&mQueueMutex);
+	LLTextureFetchWorker* worker = getWorker(id);
+	if (worker)
+	{		
+		removeRequest(worker, cancel);
+	}
+}
+
+// protected
+
+// call lockQueue() first!
+void LLTextureFetch::addToNetworkQueue(LLTextureFetchWorker* worker)
+{
+	if (mRequestMap.find(worker->mID) != mRequestMap.end())
+	{
+		// only add to the queue if in the request map
+		// i.e. a delete has not been requested
+		mNetworkQueue.insert(worker->mID);
+	}
+}
+
+// call lockQueue() first!
+void LLTextureFetch::removeFromNetworkQueue(LLTextureFetchWorker* worker)
+{
+	mNetworkQueue.erase(worker->mID);
+}
+
+// call lockQueue() first!
+void LLTextureFetch::removeRequest(LLTextureFetchWorker* worker, bool cancel)
+{
+	mRequestMap.erase(worker->mID);
+	size_t erased = mNetworkQueue.erase(worker->mID);
+	if (cancel && erased > 0)
+	{
+		mCancelQueue[worker->mHost].insert(worker->mID);
+	}
+	worker->scheduleDelete();
+}
+
+// call lockQueue() first!
+LLTextureFetchWorker* LLTextureFetch::getWorker(const LLUUID& id)
+{
+	LLTextureFetchWorker* res = NULL;
+	map_t::iterator iter = mRequestMap.find(id);
+	if (iter != mRequestMap.end())
+	{
+		res = iter->second;
+	}
+	return res;
+}
+
+
+bool LLTextureFetch::getRequestFinished(const LLUUID& id, S32& discard_level,
+										LLPointer<LLImageRaw>& raw, LLPointer<LLImageRaw>& aux)
+{
+	bool res = false;
+	LLMutexLock lock(&mQueueMutex);
+	LLTextureFetchWorker* worker = getWorker(id);
+	if (worker)
+	{
+		if (worker->wasAborted())
+		{
+			res = true;
+		}
+		else if (!worker->haveWork())
+		{
+			// Should only happen if we set mDebugPause...
+			if (!mDebugPause)
+			{
+				worker->addWork(0, LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority);
+			}
+		}
+		else if (worker->checkWork())
+		{
+			discard_level = worker->mDecodedDiscard;
+			raw = worker->mRawImage; worker->mRawImage = NULL;
+			aux = worker->mAuxImage; worker->mAuxImage = NULL;
+			res = true;
+		}
+		else
+		{
+			worker->lockWorkData();
+			if ((worker->mDecodedDiscard >= 0) &&
+				(worker->mDecodedDiscard < discard_level || discard_level < 0) &&
+				(worker->mState >= LLTextureFetchWorker::WAIT_ON_WRITE))
+			{
+				// Not finished, but data is ready
+				discard_level = worker->mDecodedDiscard;
+				if (worker->mRawImage) raw = worker->mRawImage;
+				if (worker->mAuxImage) aux = worker->mAuxImage;
+			}
+			worker->unlockWorkData();
+		}
+	}
+	else
+	{
+		res = true;
+	}
+	return res;
+}
+
+bool LLTextureFetch::updateRequestPriority(const LLUUID& id, F32 priority)
+{
+	bool res = false;
+	LLMutexLock lock(&mQueueMutex);
+	LLTextureFetchWorker* worker = getWorker(id);
+	if (worker)
+	{
+		worker->lockWorkData();
+		worker->setImagePriority(priority);
+		worker->unlockWorkData();
+		res = true;
+	}
+	return res;
+}
+
 //////////////////////////////////////////////////////////////////////////////
 
-#if 0
-// static
-void LLTextureFetchWorker::receiveImageHeader(LLMessageSystem *msg, void **user_data)
+//virtual
+S32 LLTextureFetch::update(U32 max_time_ms)
 {
-	LLFastTimer t(LLFastTimer::FTM_PROCESS_IMAGES);
+	S32 res;
+	res = LLWorkerThread::update(max_time_ms);
+	
+	const F32 REQUEST_TIME = 1.f;
+
+	// Periodically, gather the list of textures that need data from the network
+	// And send the requests out to the simulators
+	if (mNetworkTimer.getElapsedTimeF32() >= REQUEST_TIME)
+	{
+		mNetworkTimer.reset();
+		sendRequestListToSimulators();
+	}
+
+#if 0 // Currently this logic is handled in LLViewer
+	{
+		LLMutexLock lock(&mQueueMutex);
+		const F32 MIN_IDLE_TIME = 1.f * 60.f; // 1 minute
+		const F32 MAX_IDLE_TIME = 5.f * 60.f; // 5 minutes
+		const S32 MIN_IDLE_COUNT = 16; // always keep last 16 idle requests
+		const F32 MAX_IDLE_COUNT = 1024; // max number of idle requests
+		// Remove any old requests (releasing their raw data)
+		typedef std::pair<F32, LLTextureFetchWorker*> idle_pair;
+		typedef std::set<idle_pair, compare_pair_greater<F32,LLTextureFetchWorker*> > idle_set;
+		idle_set remove_set;
+		for (map_t::iterator iter = mRequestMap.begin(); iter != mRequestMap.end(); ++iter)
+		{
+			LLTextureFetchWorker* worker = iter->second;
+			if (worker->mActiveCount > 0)
+				continue;
+			if (worker->haveWork())
+				continue;
+			F32 idletime = worker->mIdleTimer.getElapsedTimeF32();
+			if (idletime < MIN_IDLE_TIME)
+				continue;
+			remove_set.insert(std::make_pair(idletime, worker));
+		}
+		S32 num_left = remove_set.size();
+		for (idle_set::iterator iter = remove_set.begin(); iter != remove_set.end(); ++iter)
+		{
+			if (num_left <= MIN_IDLE_COUNT)
+				break;
+			if (iter->first < MAX_IDLE_TIME &&
+				num_left < MAX_IDLE_COUNT)
+				break;
+			num_left--;
+		}
+	}
+#endif
 	
-	// Receive image header, copy into image object and decompresses 
-	// if this is a one-packet image. 
+	return res;
+}
 
-	gImageList.sTextureBits += msg->getReceiveBytes();
-	gImageList.sTexturePackets++;
+//////////////////////////////////////////////////////////////////////////////
+
+void LLTextureFetch::sendRequestListToSimulators()
+{
+	const S32 IMAGES_PER_REQUEST = 50;
+	const F32 LAZY_FLUSH_TIMEOUT = 15.f; // 10.0f // temp
+	const F32 MIN_REQUEST_TIME = 1.0f;
+	const F32 MIN_DELTA_PRIORITY = 1000.f;
 
-	LLUUID id;
-	msg->getUUIDFast(_PREHASH_ImageID, _PREHASH_ID, id);
-// 	LLString ip_string(u32_to_ip_string(msg->getSenderIP()));
+	LLMutexLock lock(&mQueueMutex);
 	
-	LLTextureFetchWorker* worker = getActiveWorker(id);
+	// Send requests
+	typedef std::set<LLTextureFetchWorker*,LLTextureFetchWorker::Compare> request_list_t;
+	typedef std::map< LLHost, request_list_t > work_request_map_t;
+	work_request_map_t requests;
+	for (queue_t::iterator iter = mNetworkQueue.begin(); iter != mNetworkQueue.end(); ++iter)
+	{
+		LLTextureFetchWorker* req = getWorker(*iter);
+		if (req->mID == mDebugID)
+		{
+			mDebugCount++; // for setting breakpoints
+		}
+		if (req->mTotalPackets > 0 && req->mLastPacket >= req->mTotalPackets-1)
+		{
+			// We have all the packets... make sure this is high priority
+			req->setPriority(LLWorkerThread::PRIORITY_HIGH | req->mWorkPriority);
+			continue;
+		}
+		F32 elapsed = req->mRequestedTimer.getElapsedTimeF32();
+		F32 delta_priority = llabs(req->mRequestedPriority - req->mImagePriority);
+		if ((req->mSimRequestedDiscard != req->mDesiredDiscard) ||
+			(delta_priority > MIN_DELTA_PRIORITY && elapsed >= MIN_REQUEST_TIME) ||
+			(elapsed >= LAZY_FLUSH_TIMEOUT))
+		{
+			requests[req->mHost].insert(req);
+		}
+	}
+	for (work_request_map_t::iterator iter1 = requests.begin();
+		 iter1 != requests.end(); ++iter1)
+	{
+		LLHost host = iter1->first;
+		// invalid host = use agent host
+		if (host == LLHost::invalid)
+		{
+			host = gAgent.getRegionHost();
+		}
+
+		S32 request_count = 0;
+		for (request_list_t::iterator iter2 = iter1->second.begin();
+			 iter2 != iter1->second.end(); ++iter2)
+		{
+			LLTextureFetchWorker* req = *iter2;
+			if (0 == request_count)
+			{
+				gMessageSystem->newMessageFast(_PREHASH_RequestImage);
+				gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+				gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+				gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+			}
+			S32 packet = req->mLastPacket + 1;
+			gMessageSystem->nextBlockFast(_PREHASH_RequestImage);
+			gMessageSystem->addUUIDFast(_PREHASH_Image, req->mID);
+			gMessageSystem->addS8Fast(_PREHASH_DiscardLevel, (S8)req->mSimRequestedDiscard);
+			gMessageSystem->addF32Fast(_PREHASH_DownloadPriority, req->mImagePriority);
+			gMessageSystem->addU32Fast(_PREHASH_Packet, packet);
+			U8 type = host.isOk() ? LLImageBase::TYPE_AVATAR_BAKE : LLImageBase::TYPE_NORMAL;
+			gMessageSystem->addU8Fast(_PREHASH_Type, type);
+// 			llinfos << "IMAGE REQUEST: " << req->mID << " Discard: " << req->mDesiredDiscard
+// 					<< " Packet: " << packet << " Priority: " << req->mImagePriority << llendl;
+
+			req->lockWorkData();
+			req->mSimRequestedDiscard = req->mDesiredDiscard;
+			req->mRequestedPriority = req->mImagePriority;
+			req->mRequestedTimer.reset();
+			req->unlockWorkData();
+			request_count++;
+			if (request_count >= IMAGES_PER_REQUEST)
+			{
+// 				llinfos << "REQUESTING " << request_count << " IMAGES FROM HOST: " << host.getIPString() << llendl;
+				gMessageSystem->sendSemiReliable(host, NULL, NULL);
+				request_count = 0;
+				break; // only send the top requests
+			}
+		}
+		if (request_count > 0 && request_count < IMAGES_PER_REQUEST)
+		{
+// 			llinfos << "REQUESTING " << request_count << " IMAGES FROM HOST: " << host.getIPString() << llendl;
+			gMessageSystem->sendSemiReliable(host, NULL, NULL);
+		}
+	}
+	
+	// Send cancelations
+	if (!mCancelQueue.empty())
+	{
+		for (cancel_queue_t::iterator iter1 = mCancelQueue.begin();
+			 iter1 != mCancelQueue.end(); ++iter1)
+		{
+			LLHost host = iter1->first;
+			// invalid host = use agent host
+			if (host == LLHost::invalid)
+			{
+				host = gAgent.getRegionHost();
+			}
+			S32 request_count = 0;
+			for (queue_t::iterator iter2 = iter1->second.begin();
+				 iter2 != iter1->second.end(); ++iter2)
+			{
+				if (0 == request_count)
+				{
+					gMessageSystem->newMessageFast(_PREHASH_RequestImage);
+					gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+					gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+					gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+				}
+				gMessageSystem->nextBlockFast(_PREHASH_RequestImage);
+				gMessageSystem->addUUIDFast(_PREHASH_Image, *iter2);
+				gMessageSystem->addS8Fast(_PREHASH_DiscardLevel, -1);
+				gMessageSystem->addF32Fast(_PREHASH_DownloadPriority, 0);
+				gMessageSystem->addU32Fast(_PREHASH_Packet, 0);
+				gMessageSystem->addU8Fast(_PREHASH_Type, 0);
+// 				llinfos << "CANCELING IMAGE REQUEST: " << (*iter2) << llendl;
+
+				request_count++;
+				if (request_count >= IMAGES_PER_REQUEST)
+				{
+					gMessageSystem->sendSemiReliable(host, NULL, NULL);
+					request_count = 0;
+				}
+			}
+			if (request_count > 0 && request_count < IMAGES_PER_REQUEST)
+			{
+				gMessageSystem->sendSemiReliable(host, NULL, NULL);
+			}
+		}
+		mCancelQueue.clear();
+	}
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+bool LLTextureFetchWorker::insertPacket(S32 index, U8* data, S32 size)
+{
+	mRequestedTimer.reset();
+	if (index >= mTotalPackets)
+	{
+		llwarns << "Received Image Packet " << index << " > max: " << mTotalPackets << " Skipping. " << llendl;
+		return false;
+	}
+	if (index > 0 && index < mTotalPackets-1 && size != MAX_IMG_PACKET_SIZE)
+	{
+		llwarns << "Received bad sized packet: " << index << ", " << size << " != " << MAX_IMG_PACKET_SIZE << " Skipping. " << llendl;
+		return false;
+	}
+	
+	if (index >= (S32)mPackets.size())
+	{
+		mPackets.resize(index+1, (PacketData*)NULL); // initializes v to NULL pointers
+	}
+	else if (mPackets[index] != NULL)
+	{
+// 		llwarns << "LLTextureFetchWorker::insertPacket called for duplicate packet: " << index << llendl;
+		return false;
+	}
+
+	mPackets[index] = new PacketData(data, size);
+	while (mLastPacket+1 < (S32)mPackets.size() && mPackets[mLastPacket+1] != NULL)
+	{
+		++mLastPacket;
+	}
+	return true;
+}
+
+bool LLTextureFetch::receiveImageHeader(const LLUUID& id, U8 codec, U16 packets, U32 totalbytes,
+										U16 data_size, U8* data)
+{
+	LLMutexLock lock(&mQueueMutex);
+	LLTextureFetchWorker* worker = getWorker(id);
 	if (!worker)
 	{
-		llwarns << "receiveImageHeader for non active worker: " << id << llendl;
-		return;
+// 		llwarns << "receiveImageHeader for non active worker: " << id << llendl;
+		return false;
 	}
-	worker->mRequestedTimer.reset();
-		
 	// check to see if we've gotten this packet before
 	if (worker->mLastPacket != -1)
 	{
-		llwarns << "Img: " << id << ":" << " Duplicate Image Header" << llendl;
-		return;
+// 		llwarns << "Img: " << id << ":" << " Duplicate Image Header" << llendl;
+		return false;
 	}
 
-	//	Copy header data into image object
 	worker->lockWorkData();
-	msg->getU8Fast(_PREHASH_ImageID, _PREHASH_Codec, image->mDataCodec);
-	msg->getU16Fast(_PREHASH_ImageID, _PREHASH_Packets, image->mTotalPackets);
-	msg->getU32Fast(_PREHASH_ImageID, _PREHASH_Size, image->mTotalBytes);
-	if (0 == image->mTotalPackets)
+
+	//	Copy header data into image object
+	worker->mImageCodec = codec;
+	worker->mTotalPackets = packets;
+	worker->mFileSize = (S32)totalbytes;	
+	llassert_always(totalbytes > 0);
+	bool res = false;
+	if (data_size)
 	{
-		llwarns << "Img: " << id << ":" << " Number of packets is 0" << llendl;
+		llassert(data_size == FIRST_PACKET_SIZE || data_size == worker->mFileSize);
+		res = worker->insertPacket(0, data, data_size);
+		worker->setPriority(LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority);
 	}
 	worker->unlockWorkData();
+	return res;
+}
 
-	U16 data_size = msg->getSizeFast(_PREHASH_ImageData, _PREHASH_Data); 
+bool LLTextureFetch::receiveImagePacket(const LLUUID& id, U16 packet_num, U16 data_size, U8* data)
+{
+	LLMutexLock lock(&mQueueMutex);
+	LLTextureFetchWorker* worker = getWorker(id);
+	if (!worker)
+	{
+// 		llwarns << "receiveImagePacket " << packet_num << " for non active worker: " << id << llendl;
+		return false;
+	}
+	if (worker->mLastPacket == -1)
+	{
+// 		llwarns << "Img: " << id << ":" << " Image Packet " << packet_num << " received before header" << llendl;
+		return false;
+	}
+
+	bool res = false;
 	if (data_size)
 	{
-		U8 *data = new U8[data_size];
-		msg->getBinaryDataFast(_PREHASH_ImageData, _PREHASH_Data, data, data_size);
-		worker->insertPacket(0, data, data_size)
+		worker->lockWorkData();
+		res = worker->insertPacket(packet_num, data, data_size);
+		worker->setPriority(LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority);
+		worker->unlockWorkData();
 	}
+	return res;
 }
 
-///////////////////////////////////////////////////////////////////////////////
-// static
-void LLTextureFetchWorker::receiveImagePacket(LLMessageSystem *msg, void **user_data)
+//////////////////////////////////////////////////////////////////////////////
+
+S32 LLTextureFetch::getFetchState(const LLUUID& id, F32& data_progress_p, F32& requested_priority_p,
+								  U32& fetch_priority_p, F32& fetch_dtime_p, F32& request_dtime_p)
 {
-	LLMemType mt1(LLMemType::MTYPE_APPFMTIMAGE);
-	LLFastTimer t(LLFastTimer::FTM_PROCESS_IMAGES);
+	S32 state = LLTextureFetchWorker::INVALID;
+	F32 data_progress = 0.0f;
+	F32 requested_priority = 0.0f;
+	F32 fetch_dtime = 999999.f;
+	F32 request_dtime = 999999.f;
+	U32 fetch_priority = 0;
 	
-	gImageList.sTextureBits += msg->getReceiveBytes();
-	gImageList.sTexturePackets++;
-
-	LLUUID id;
-	msg->getUUIDFast(_PREHASH_ImageID, _PREHASH_ID, id);
-// 	LLString ip_string(u32_to_ip_string(msg->getSenderIP()));
-
-	U16 packet_num;
-	msg->getU16Fast(_PREHASH_ImageID, _PREHASH_Packet, packet_num);
-
-	LLTextureFetchWorker* worker = getActiveWorker(id);
-	if (!worker)
+	LLMutexLock lock(&mQueueMutex);
+	LLTextureFetchWorker* worker = getWorker(id);
+	if (worker && worker->haveWork())
 	{
-		llwarns << "receiveImageHeader for non active worker: " << id << llendl;
-		return;
+		worker->lockWorkData();
+		state = worker->mState;
+		fetch_dtime = worker->mFetchTimer.getElapsedTimeF32();
+		request_dtime = worker->mRequestedTimer.getElapsedTimeF32();
+		if (worker->mFileSize > 0)
+		{
+			if (state == LLTextureFetchWorker::LOAD_FROM_SIMULATOR)
+			{
+				S32 data_size = FIRST_PACKET_SIZE + (worker->mLastPacket-1) * MAX_IMG_PACKET_SIZE;
+				data_size = llmax(data_size, 0);
+				data_progress = (F32)data_size / (F32)worker->mFileSize;
+			}
+			else if (worker->mFormattedImage.notNull())
+			{
+				data_progress = (F32)worker->mFormattedImage->getDataSize() / (F32)worker->mFileSize;
+			}
+		}
+		if (state >= LLTextureFetchWorker::LOAD_FROM_NETWORK && state <= LLTextureFetchWorker::LOAD_FROM_HTTP_GET_DATA)
+		{
+			requested_priority = worker->mRequestedPriority;
+		}
+		else
+		{
+			requested_priority = worker->mImagePriority;
+		}
+		fetch_priority = worker->getPriority();
+		worker->unlockWorkData();
 	}
-	worker->mRequestedTimer.reset();
+	data_progress_p = data_progress;
+	requested_priority_p = requested_priority;
+	fetch_priority_p = fetch_priority;
+	fetch_dtime_p = fetch_dtime;
+	request_dtime_p = request_dtime;
+	return state;
+}
 
-	U16 data_size = msg->getSizeFast(_PREHASH_ImageData, _PREHASH_Data); 
-	if (data_size)
+void LLTextureFetch::dump()
+{
+	llinfos << "LLTextureFetch REQUESTS:" << llendl;
+	for (request_queue_t::iterator iter = mRequestQueue.begin();
+		 iter != mRequestQueue.end(); ++iter)
 	{
-		U8 *data = new U8[data_size];
-		msg->getBinaryDataFast(_PREHASH_ImageData, _PREHASH_Data, data, data_size);
-		worker->insertPacket(0, data, data_size)
+		LLQueuedThread::QueuedRequest* qreq = *iter;
+		LLWorkerThread::WorkRequest* wreq = (LLWorkerThread::WorkRequest*)qreq;
+		LLTextureFetchWorker* worker = (LLTextureFetchWorker*)wreq->getWorkerClass();
+		llinfos << " ID: " << worker->mID
+				<< " PRI: " << llformat("0x%08x",wreq->getPriority())
+				<< " STATE: " << worker->sStateDescs[worker->mState]
+				<< llendl;
 	}
 }
-#endif
 
- //////////////////////////////////////////////////////////////////////////////
+
+//////////////////////////////////////////////////////////////////////////////
diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h
index 9d841ae23aaf2762388b1df17695b3933e2f48e9..b3510d9e823b7e3576ee00dcd0f1d7788f0bbc71 100644
--- a/indra/newview/lltexturefetch.h
+++ b/indra/newview/lltexturefetch.h
@@ -9,20 +9,77 @@
 #ifndef LL_LLTEXTUREFETCH_H
 #define LL_LLTEXTUREFETCH_H
 
+#include "lldir.h"
+#include "llimage.h"
+#include "lluuid.h"
 #include "llworkerthread.h"
 
 class LLViewerImage;
+class LLTextureFetchWorker;
+class LLTextureCache;
+class LLHost;
 
 // Interface class
-class LLTextureFetch
+class LLTextureFetch : public LLWorkerThread
 {
+	friend class LLTextureFetchWorker;
+	
 public:
-	static void initClass();
-	static void updateClass();
-	static void cleanupClass();
+	LLTextureFetch(LLTextureCache* cache, bool threaded);
+	~LLTextureFetch();
 
-	static LLWorkerClass::handle_t addRequest(LLImageFormatted* image, S32 discard);
-	static bool getRequestFinished(LLWorkerClass::handle_t handle);
+	/*virtual*/ S32 update(U32 max_time_ms);	
+
+	bool createRequest(const LLUUID& id, const LLHost& host, F32 priority,
+					   S32 w, S32 h, S32 c, S32 discard, bool needs_aux);
+	void deleteRequest(const LLUUID& id, bool cancel);
+	bool getRequestFinished(const LLUUID& id, S32& discard_level,
+							LLPointer<LLImageRaw>& raw, LLPointer<LLImageRaw>& aux);
+	bool updateRequestPriority(const LLUUID& id, F32 priority);
+
+	bool receiveImageHeader(const LLUUID& id, U8 codec, U16 packets, U32 totalbytes, U16 data_size, U8* data);
+	bool receiveImagePacket(const LLUUID& id, U16 packet_num, U16 data_size, U8* data);
+
+	// Debug
+	S32 getFetchState(const LLUUID& id, F32& decode_progress_p, F32& requested_priority_p,
+					  U32& fetch_priority_p, F32& fetch_dtime_p, F32& request_dtime_p);
+	void dump();
+	S32 getNumRequests() { return mRequestMap.size(); }
+	
+	// Public for access by callbacks
+	void lockQueue() { mQueueMutex.lock(); }
+	void unlockQueue() { mQueueMutex.unlock(); }
+	LLTextureFetchWorker* getWorker(const LLUUID& id);
+	
+protected:
+	void addToNetworkQueue(LLTextureFetchWorker* worker);
+	void removeFromNetworkQueue(LLTextureFetchWorker* worker);
+	void removeRequest(LLTextureFetchWorker* worker, bool cancel);
+
+private:
+	void sendRequestListToSimulators();
+
+public:
+	LLUUID mDebugID;
+	S32 mDebugCount;
+	BOOL mDebugPause;
+	
+private:
+	LLMutex mQueueMutex;
+
+	LLTextureCache* mTextureCache;
+	
+	// Map of all requests by UUID
+	typedef std::map<LLUUID,LLTextureFetchWorker*> map_t;
+	map_t mRequestMap;
+
+	// Set of requests that require network data
+	typedef std::set<LLUUID> queue_t;
+	queue_t mNetworkQueue;
+	typedef std::map<LLHost,std::set<LLUUID> > cancel_queue_t;
+	cancel_queue_t mCancelQueue;
+
+	LLFrameTimer mNetworkTimer;
 };
 
 #endif LL_LLTEXTUREFETCH_H
diff --git a/indra/newview/lltextureview.cpp b/indra/newview/lltextureview.cpp
index d115cf1158d1e954d739709d6158ab0cd9f137a2..f051236fbac965390238174dcc6a5b3fb8895c40 100644
--- a/indra/newview/lltextureview.cpp
+++ b/indra/newview/lltextureview.cpp
@@ -14,46 +14,510 @@
 
 #include "llrect.h"
 #include "llerror.h"
-
-#include "viewer.h"
+#include "lllfsthread.h"
 #include "llui.h"
+#include "llimageworker.h"
 
-#include "llviewerimagelist.h"
+#include "llhoverview.h"
 #include "llselectmgr.h"
+#include "lltexlayer.h"
+#include "lltexturecache.h"
+#include "lltexturefetch.h"
+#include "lltexturetable.h"
 #include "llviewerobject.h"
 #include "llviewerimage.h"
-#include "llhoverview.h"
+#include "llviewerimagelist.h"
+#include "viewer.h"
+
+extern F32 texmem_lower_bound_scale;
 
 LLTextureView *gTextureView = NULL;
 
 //static
 std::set<LLViewerImage*> LLTextureView::sDebugImages;
 
+////////////////////////////////////////////////////////////////////////////
+
+static LLString title_string1a("Tex UUID Area  DDis(Req)  DecodePri(Fetch)     [download]        pk/max");
+static LLString title_string1b("Tex UUID Area  DDis(Req)  Fetch(DecodePri)     [download]        pk/max");
+static LLString title_string2("State");
+static LLString title_string3("Pkt Bnd");
+static LLString title_string4("  W x H (Dis) Mem");
+
+static S32 title_x1 = 0;
+static S32 title_x2 = 440;
+static S32 title_x3 = title_x2 + 40;
+static S32 title_x4 = title_x3 + 50;
+static S32 texture_bar_height = 8;
+
+////////////////////////////////////////////////////////////////////////////
+
+class LLTextureBar : public LLView
+{
+public:
+	LLPointer<LLViewerImage> mImagep;
+	S32 mHilite;
+
+public:
+	LLTextureBar(const std::string& name, const LLRect& r, LLTextureView* texview)
+		: LLView(name, r, FALSE),
+		  mHilite(0),
+		  mTextureView(texview)
+	{
+	}
+
+	virtual EWidgetType getWidgetType() const { return WIDGET_TYPE_TEXTURE_BAR; }
+	virtual LLString getWidgetTag() const { return LL_TEXTURE_BAR_TAG; }
+
+	virtual void draw();
+	virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+	virtual LLRect getRequiredRect();	// Return the height of this object, given the set options.
+
 // Used for sorting
-struct SortTextureBars
+	struct sort
+	{
+		bool operator()(const LLView* i1, const LLView* i2)
+		{
+			LLTextureBar* bar1p = (LLTextureBar*)i1;
+			LLTextureBar* bar2p = (LLTextureBar*)i2;
+			LLViewerImage *i1p = bar1p->mImagep;
+			LLViewerImage *i2p = bar2p->mImagep;
+			F32 pri1 = i1p->getDecodePriority(); // i1p->mRequestedDownloadPriority
+			F32 pri2 = i2p->getDecodePriority(); // i2p->mRequestedDownloadPriority
+			if (pri1 > pri2)
+				return true;
+			else if (pri2 > pri1)
+				return false;
+			else
+				return i1p->getID() < i2p->getID();
+		}
+	};
+
+	struct sort_fetch
+	{
+		bool operator()(const LLView* i1, const LLView* i2)
+		{
+			LLTextureBar* bar1p = (LLTextureBar*)i1;
+			LLTextureBar* bar2p = (LLTextureBar*)i2;
+			LLViewerImage *i1p = bar1p->mImagep;
+			LLViewerImage *i2p = bar2p->mImagep;
+			U32 pri1 = i1p->mFetchPriority;
+			U32 pri2 = i2p->mFetchPriority;
+			if (pri1 > pri2)
+				return true;
+			else if (pri2 > pri1)
+				return false;
+			else
+				return i1p->getID() < i2p->getID();
+		}
+	};	
+private:
+	LLTextureView* mTextureView;
+};
+
+void LLTextureBar::draw()
 {
-	bool operator()(const LLView* i1, const LLView* i2)
-	{
-		LLTextureBar* bar1p = (LLTextureBar*)i1;
-		LLTextureBar* bar2p = (LLTextureBar*)i2;
-		LLViewerImage *i1p = bar1p->mImagep;
-		LLViewerImage *i2p = bar2p->mImagep;
-		F32 pri1 = i1p->getDecodePriority(); // i1p->mRequestedDownloadPriority
-		F32 pri2 = i2p->getDecodePriority(); // i2p->mRequestedDownloadPriority
-		if (pri1 > pri2)
-			return true;
-		else if (pri2 > pri1)
-			return false;
+	if (!mImagep)
+	{
+		return;
+	}
+
+	LLColor4 color;
+	if (mImagep->getID() == gTextureFetch->mDebugID)
+	{
+		color = LLColor4::cyan2;
+	}
+	else if (mHilite)
+	{
+		S32 idx = llclamp(mHilite,1,4);
+		if (idx==1) color = LLColor4::yellow;
+		if (idx==2) color = LLColor4::cyan;
+		if (idx==3) color = LLColor4::magenta;
+		if (idx==4) color = LLColor4::blue;
+	}
+	else if (mImagep->getBoostLevel())
+	{
+		color = LLColor4::magenta;
+	}
+	else if (mImagep->mDontDiscard)
+	{
+		color = LLColor4::pink2;
+	}
+	else if (!mImagep->getUseMipMaps())
+	{
+		color = LLColor4::green4;
+	}
+	else if (mImagep->getDecodePriority() == 0.0f)
+	{
+		color = LLColor4::grey; color[VALPHA] = .7f;
+	}
+	else
+	{
+		color = LLColor4::white; color[VALPHA] = .7f;
+	}
+
+	// We need to draw:
+	// The texture UUID or name
+	// The progress bar for the texture, highlighted if it's being download
+	// Various numerical stats.
+	char tex_str[256];
+	S32 left, right;
+	S32 top = 0;
+	S32 bottom = top + 6;
+	LLColor4 clr;
+
+	LLGLSUIDefault gls_ui;
+	
+	// Get the name or UUID of the image.
+	gTextureTable.getName(mImagep->mID);
+	
+	// Name, pixel_area, requested pixel area, decode priority
+	char uuid_str[255];
+	mImagep->mID.toString(uuid_str);
+	uuid_str[8] = 0;
+	if (mTextureView->mOrderFetch)
+	{
+		sprintf(tex_str, "%s %7.0f %d(%d) 0x%08x(%8.0f)",
+				uuid_str,
+				mImagep->mMaxVirtualSize,
+				mImagep->mDesiredDiscardLevel,
+				mImagep->mRequestedDiscardLevel,
+				mImagep->mFetchPriority,
+				mImagep->getDecodePriority());
+	}
+	else
+	{
+		sprintf(tex_str, "%s %7.0f %d(%d) %8.0f(0x%08x)",
+				uuid_str,
+				mImagep->mMaxVirtualSize,
+				mImagep->mDesiredDiscardLevel,
+				mImagep->mRequestedDiscardLevel,
+				mImagep->getDecodePriority(),
+				mImagep->mFetchPriority);
+	}
+
+	LLFontGL::sMonospace->renderUTF8(tex_str, 0, title_x1, mRect.getHeight(),
+									 color, LLFontGL::LEFT, LLFontGL::TOP);
+
+	// State
+	// Hack: mirrored from lltexturefetch.cpp
+	struct { const char* desc; LLColor4 color; } fetch_state_desc[] = {
+		{ "---", LLColor4::red },	// INVALID
+		{ "INI", LLColor4::white },	// INIT
+		{ "DSK", LLColor4::cyan },	// LOAD_FROM_TEXTURE_CACHE
+		{ "DSK", LLColor4::blue },	// CACHE_POST
+		{ "NET", LLColor4::green },	// LOAD_FROM_NETWORK
+		{ "SIM", LLColor4::green },	// LOAD_FROM_SIMULATOR
+		{ "URL", LLColor4::green2 },// LOAD_FROM_HTTP_GET_URL
+		{ "HTP", LLColor4::green },	// LOAD_FROM_HTTP_GET_DATA
+		{ "DEC", LLColor4::yellow },// DECODE_IMAGE
+		{ "DEC", LLColor4::yellow },// DECODE_IMAGE_UPDATE
+		{ "WRT", LLColor4::purple },// WRITE_TO_CACHE
+		{ "WRT", LLColor4::orange },// WAIT_ON_WRITE
+		{ "END", LLColor4::red },   // DONE
+#define LAST_STATE 12
+		{ "CRE", LLColor4::magenta }, // LAST_STATE+1
+		{ "FUL", LLColor4::green }, // LAST_STATE+2
+		{ "BAD", LLColor4::red }, // LAST_STATE+3
+		{ "MIS", LLColor4::red }, // LAST_STATE+4
+		{ "---", LLColor4::white }, // LAST_STATE+5
+	};
+	const S32 fetch_state_desc_size = (S32)(sizeof(fetch_state_desc)/sizeof(fetch_state_desc[0]));
+	S32 state =
+		mImagep->mNeedsCreateTexture ? LAST_STATE+1 :
+		mImagep->mFullyLoaded ? LAST_STATE+2 :
+		mImagep->mMinDiscardLevel > 0 ? LAST_STATE+3 :
+		mImagep->mIsMissingAsset ? LAST_STATE+4 :
+		!mImagep->mIsFetching ? LAST_STATE+5 :
+		mImagep->mFetchState;
+	state = llclamp(state,0,fetch_state_desc_size-1);
+
+	LLFontGL::sMonospace->renderUTF8(fetch_state_desc[state].desc, 0, title_x2, mRect.getHeight(),
+									 fetch_state_desc[state].color,
+									 LLFontGL::LEFT, LLFontGL::TOP);
+	LLGLSNoTexture gls_no_texture;
+
+	// Draw the progress bar.
+	S32 bar_width = 100;
+	S32 bar_left = 280;
+	left = bar_left;
+	right = left + bar_width;
+
+	glColor4f(0.f, 0.f, 0.f, 0.75f);
+	gl_rect_2d(left, top, right, bottom);
+
+	F32 data_progress = mImagep->mDownloadProgress;
+	
+	if (data_progress > 0.0f)
+	{
+		// Downloaded bytes
+		right = left + llfloor(data_progress * (F32)bar_width);
+		if (right > left)
+		{
+			glColor4f(0.f, 0.f, 1.f, 0.75f);
+			gl_rect_2d(left, top, right, bottom);
+		}
+	}
+
+	S32 pip_width = 6;
+	S32 pip_space = 14;
+	S32 pip_x = title_x3 + pip_space/2;
+	
+	// Draw the packet pip
+	F32 last_event = mImagep->mLastPacketTimer.getElapsedTimeF32();
+	if (last_event < 1.f)
+	{
+		clr = LLColor4::white; 
+	}
+	else
+	{
+		last_event = mImagep->mRequestDeltaTime;
+		if (last_event < 1.f)
+		{
+			clr = LLColor4::green;
+		}
 		else
-			return i1p->getID() < i2p->getID();
+		{
+			last_event = mImagep->mFetchDeltaTime;
+			if (last_event < 1.f)
+			{
+				clr = LLColor4::yellow;
+			}
+		}
+	}
+	if (last_event < 1.f)
+	{
+		clr.setAlpha(1.f - last_event);
+		glColor4fv(clr.mV);
+		gl_rect_2d(pip_x, top, pip_x + pip_width, bottom);
+	}
+	pip_x += pip_width + pip_space;
+
+	// we don't want to show bind/resident pips for textures using the default texture
+	if (mImagep->getHasGLTexture())
+	{
+		// Draw the bound pip
+		last_event = mImagep->sLastFrameTime - mImagep->mLastBindTime;
+		if (last_event < 1.f)
+		{
+			clr = mImagep->getMissed() ? LLColor4::red : LLColor4::magenta1;
+			clr.setAlpha(1.f - last_event);
+			glColor4fv(clr.mV);
+			gl_rect_2d(pip_x, top, pip_x + pip_width, bottom);
+		}
+	}
+	pip_x += pip_width + pip_space;
+
+	
+	{
+		LLGLSUIDefault gls_ui;
+		// draw the packet data
+// 		{
+// 			LLString num_str = llformat("%3d/%3d", mImagep->mLastPacket+1, mImagep->mPackets);
+// 			LLFontGL::sMonospace->renderUTF8(num_str, 0, bar_left + 100, mRect.getHeight(), color,
+// 											 LLFontGL::LEFT, LLFontGL::TOP);
+// 		}
+		
+		// draw the image size at the end
+		{
+			LLString num_str = llformat("%3dx%3d (%d) %7d", mImagep->getWidth(), mImagep->getHeight(),
+										mImagep->getDiscardLevel(), mImagep->mTextureMemory);
+			LLFontGL::sMonospace->renderUTF8(num_str, 0, title_x4, mRect.getHeight(), color,
+											LLFontGL::LEFT, LLFontGL::TOP);
+		}
+	}
+
+}
+
+BOOL LLTextureBar::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+	if (mask & MASK_ALT)
+	{
+		gTextureFetch->mDebugID = mImagep->getID();
+		return TRUE;
+	}
+	return LLView::handleMouseDown(x,y,mask);
+}
+
+LLRect LLTextureBar::getRequiredRect()
+{
+	LLRect rect;
+
+	rect.mTop = texture_bar_height;
+
+	return rect;
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+class LLGLTexMemBar : public LLView
+{
+public:
+	LLGLTexMemBar(const std::string& name, LLTextureView* texview)
+		: LLView(name, FALSE),
+		  mTextureView(texview)
+	{
+		S32 line_height = (S32)(LLFontGL::sMonospace->getLineHeight() + .5f);
+		setRect(LLRect(0,0,100,line_height * 4));
+		updateRect();
 	}
+
+	virtual EWidgetType getWidgetType() const { return WIDGET_TYPE_TEX_MEM_BAR; };
+	virtual LLString getWidgetTag() const { return LL_GL_TEX_MEM_BAR_TAG; };
+
+	virtual void draw();	
+	virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+	virtual LLRect getRequiredRect();	// Return the height of this object, given the set options.
+
+private:
+	LLTextureView* mTextureView;
 };
 
+void LLGLTexMemBar::draw()
+{
+	S32 bound_mem = LLViewerImage::sBoundTextureMemory;
+ 	S32 max_bound_mem = LLViewerImage::sMaxBoundTextureMem;
+	S32 total_mem = LLViewerImage::sTotalTextureMemory;
+	S32 max_total_mem = LLViewerImage::sMaxTotalTextureMem;
+	F32 discard_bias = LLViewerImage::sDesiredDiscardBias;
+	S32 line_height = (S32)(LLFontGL::sMonospace->getLineHeight() + .5f);
+	
+	//----------------------------------------------------------------------------
+	LLGLSUIDefault gls_ui;
+	F32 text_color[] = {1.f, 1.f, 1.f, 0.75f};
+	
+	std::string text;
+	text = llformat("GL Tot: %d/%d MB Bound: %d/%d MB Discard Bias: %.2f",
+					total_mem/(1024*1024),
+					max_total_mem/(1024*1024),
+					bound_mem/(1024*1024),
+					max_bound_mem/(1024*1024),
+					discard_bias);
+
+	LLFontGL::sMonospace->renderUTF8(text, 0, 0, line_height*3,
+									 text_color, LLFontGL::LEFT, LLFontGL::TOP);
+
+	//----------------------------------------------------------------------------
+	S32 bar_left = 380;
+	S32 bar_width = 200;
+	S32 top = line_height*3 - 2;
+	S32 bottom = top - 6;
+	S32 left = bar_left;
+	S32 right = left + bar_width;
+
+	F32 bar_scale = (F32)bar_width / (max_bound_mem * 1.5f);
+	
+	LLGLSNoTexture gls_no_texture;
+	
+	glColor4f(0.5f, 0.5f, 0.5f, 0.75f);
+	gl_rect_2d(left, top, right, bottom);
+
+	
+	left = bar_left;
+	right = left + llfloor(bound_mem * bar_scale);
+	if (bound_mem < llfloor(max_bound_mem * texmem_lower_bound_scale))
+	{
+		glColor4f(0.f, 1.f, 0.f, 0.75f);
+	}
+	else if (bound_mem < max_bound_mem)
+	{
+		glColor4f(1.f, 1.f, 0.f, 0.75f);
+	}
+	else
+	{
+		glColor4f(1.f, 0.f, 0.f, 0.75f);
+	}
+	gl_rect_2d(left, top, right, bottom);
+
+	bar_scale = (F32)bar_width / (max_total_mem * 1.5f);
+	
+	top = bottom - 2;
+	bottom = top - 6;
+	left = bar_left;
+	right = left + llfloor(total_mem * bar_scale);
+	if (total_mem < llfloor(max_total_mem * texmem_lower_bound_scale))
+	{
+		glColor4f(0.f, 1.f, 0.f, 0.75f);
+	}
+	else if (total_mem < max_total_mem)
+	{
+		glColor4f(1.f, 1.f, 0.f, 0.75f);
+	}
+	else
+	{
+		glColor4f(1.f, 0.f, 0.f, 0.75f);
+	}
+	gl_rect_2d(left, top, right, bottom);
+
+	//----------------------------------------------------------------------------
+
+	LLGLEnable tex(GL_TEXTURE_2D);
+	
+	text = llformat("Textures: Count: %d Fetch: %d(%d) Cache R/W: %d/%d LFS:%d IW:%d(%d) RAW:%d",
+					gImageList.getNumImages(),
+					gTextureFetch->getNumRequests(), gTextureFetch->getNumDeletes(),
+					gTextureCache->getNumReads(), gTextureCache->getNumWrites(),
+					LLLFSThread::sLocal->getPending(),
+					LLImageWorker::sCount, LLImageWorker::getWorkerThread()->getNumDeletes(),
+					LLImageRaw::sRawImageCount);
+
+	LLFontGL::sMonospace->renderUTF8(text, 0, 0, line_height*2,
+									 text_color, LLFontGL::LEFT, LLFontGL::TOP);
+	
+	S32 dx1 = 0;
+	if (gTextureFetch->mDebugPause)
+	{
+		LLFontGL::sMonospace->renderUTF8("!", 0, title_x1, line_height,
+										 text_color, LLFontGL::LEFT, LLFontGL::TOP);
+		dx1 += 8;
+	}
+	if (mTextureView->mFreezeView)
+	{
+		LLFontGL::sMonospace->renderUTF8("*", 0, title_x1, line_height,
+										 text_color, LLFontGL::LEFT, LLFontGL::TOP);
+		dx1 += 8;
+	}
+	if (mTextureView->mOrderFetch)
+	{
+		LLFontGL::sMonospace->renderUTF8(title_string1b, 0, title_x1+dx1, line_height,
+										 text_color, LLFontGL::LEFT, LLFontGL::TOP);
+	}
+	else
+	{	
+		LLFontGL::sMonospace->renderUTF8(title_string1a, 0, title_x1+dx1, line_height,
+										 text_color, LLFontGL::LEFT, LLFontGL::TOP);
+	}
+	
+	LLFontGL::sMonospace->renderUTF8(title_string2, 0, title_x2, line_height,
+									 text_color, LLFontGL::LEFT, LLFontGL::TOP);
+
+	LLFontGL::sMonospace->renderUTF8(title_string3, 0, title_x3, line_height,
+									 text_color, LLFontGL::LEFT, LLFontGL::TOP);
+
+	LLFontGL::sMonospace->renderUTF8(title_string4, 0, title_x4, line_height,
+									 text_color, LLFontGL::LEFT, LLFontGL::TOP);
+}
+
+BOOL LLGLTexMemBar::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+	return FALSE;
+}
+
+LLRect LLGLTexMemBar::getRequiredRect()
+{
+	LLRect rect;
+	rect.mTop = 8;
+	return rect;
+}
+
+////////////////////////////////////////////////////////////////////////////
+
 LLTextureView::LLTextureView(const std::string& name, const LLRect& rect)
 :	LLContainerView(name, rect)
 {
 	setVisible(FALSE);
 	mFreezeView = FALSE;
+	mOrderFetch = FALSE;
 	
 	mNumTextureBars = 0;
 	setDisplayChildren(TRUE);
@@ -103,29 +567,40 @@ void LLTextureView::draw()
 		typedef std::multiset<decode_pair_t, compare_decode_pair > display_list_t;
 		display_list_t display_image_list;
 	
-		for (LLViewerImageList::image_list_t::iterator iter = gImageList.mImageList.begin();
+		for (LLViewerImageList::image_priority_list_t::iterator iter = gImageList.mImageList.begin();
 			 iter != gImageList.mImageList.end(); )
 		{
 			LLPointer<LLViewerImage> imagep = *iter++;
-#if 1
+#if 0
 			if (imagep->getDontDiscard())
 			{
 				continue;
 			}
-#endif
+
 			if (imagep->isMissingAsset())
 			{
 				continue;
 			}
-			
-#define HIGH_PRIORITY 100000000.f
-			F32 pri = imagep->getDecodePriority();
+#endif
 
+#define HIGH_PRIORITY 100000000.f
+			F32 pri;
+			if (mOrderFetch)
+			{
+				pri = ((F32)imagep->mFetchPriority)/256.f;
+			}
+			else
+			{
+				pri = imagep->getDecodePriority();
+			}
+			
 			if (sDebugImages.find(imagep) != sDebugImages.end())
 			{
 				pri += 3*HIGH_PRIORITY;
 			}
-			
+
+			if (!mOrderFetch)
+			{
 #if 1
 			if (pri < HIGH_PRIORITY && gSelectMgr)
 			{
@@ -173,13 +648,15 @@ void LLTextureView::draw()
 			if (pri > 0.f && pri < HIGH_PRIORITY)
 			{
 				if (imagep->mLastPacketTimer.getElapsedTimeF32() < 1.f ||
-					imagep->mLastDecodeTime.getElapsedTimeF32() < 1.f)
+					imagep->mFetchDeltaTime < 0.25f)
 				{
 					pri += 1*HIGH_PRIORITY;
 				}
 			}
 #endif
-//	 		if (pri > 0.0f)
+			}
+			
+	 		if (pri > 0.0f)
 			{
 				display_image_list.insert(std::make_pair(pri, imagep));
 			}
@@ -206,9 +683,12 @@ void LLTextureView::draw()
 			}
 		}
 
-		sortChildren(SortTextureBars());
+		if (mOrderFetch)
+			sortChildren(LLTextureBar::sort_fetch());
+		else
+			sortChildren(LLTextureBar::sort());
 	
-		mGLTexMemBar = new LLGLTexMemBar("gl texmem bar");
+		mGLTexMemBar = new LLGLTexMemBar("gl texmem bar", this);
 		addChild(mGLTexMemBar);
 	
 		reshape(mRect.getWidth(), mRect.getHeight(), TRUE);
@@ -238,54 +718,41 @@ void LLTextureView::draw()
 
 BOOL LLTextureView::addBar(LLViewerImage *imagep, S32 hilite)
 {
-	if (!imagep)
-	{
-		return FALSE;
-	}
-
+	llassert(imagep);
+	
 	LLTextureBar *barp;
 	LLRect r;
 
 	mNumTextureBars++;
 
-	for (std::vector<LLTextureBar*>::iterator iter = mTextureBars.begin();
-		 iter != mTextureBars.end(); iter++)
-	{
-		LLTextureBar* barp = *iter;
-		if (barp->mImagep == imagep)
-		{
-			barp->mHilite = hilite;
-			return FALSE;
-		}
-	}
-
-	barp = new LLTextureBar("texture bar", r);
+	barp = new LLTextureBar("texture bar", r, this);
 	barp->mImagep = imagep;	
 	barp->mHilite = hilite;
 
 	addChild(barp);
 	mTextureBars.push_back(barp);
 
-	// Rearrange all child bars.
-	reshape(mRect.getWidth(), mRect.getHeight());
 	return TRUE;
 }
 
 BOOL LLTextureView::handleMouseDown(S32 x, S32 y, MASK mask)
 {
-	if (mask & MASK_SHIFT)
+	if ((mask & MASK_CONTROL) && (mask & MASK_SHIFT))
 	{
-		mFreezeView = !mFreezeView;
+		gTextureFetch->mDebugPause = !gTextureFetch->mDebugPause;
 		return TRUE;
 	}
-	else if (mask & MASK_CONTROL)
+	if (mask & MASK_SHIFT)
 	{
-		return FALSE;
+		mFreezeView = !mFreezeView;
+		return TRUE;
 	}
-	else
+	if (mask & MASK_CONTROL)
 	{
-		return FALSE;
+		mOrderFetch = !mOrderFetch;
+		return TRUE;
 	}
+	return LLView::handleMouseDown(x,y,mask);
 }
 
 BOOL LLTextureView::handleMouseUp(S32 x, S32 y, MASK mask)
@@ -295,11 +762,6 @@ BOOL LLTextureView::handleMouseUp(S32 x, S32 y, MASK mask)
 
 BOOL LLTextureView::handleKey(KEY key, MASK mask, BOOL called_from_parent)
 {
-	if (key == ' ')
-	{
-		mFreezeView = !mFreezeView;
-		return TRUE;
-	}
 	return FALSE;
 }
 
diff --git a/indra/newview/lltextureview.h b/indra/newview/lltextureview.h
index e832a55211319f89ebb84a7a7345e6916246d247..691438c64ea9ae2c5f5c0bd3524c7e2ea8c22af1 100644
--- a/indra/newview/lltextureview.h
+++ b/indra/newview/lltextureview.h
@@ -9,11 +9,12 @@
 #ifndef LL_LLTEXTUREVIEW_H
 #define LL_LLTEXTUREVIEW_H
 
-#include "lltexturebar.h"
 #include "llcontainerview.h"
 #include "linked_lists.h"
 
 class LLViewerImage;
+class LLTextureBar;
+class LLGLTexMemBar;
 
 class LLTextureView : public LLContainerView
 {
@@ -36,6 +37,10 @@ public:
 private:
 	BOOL addBar(LLViewerImage *image, BOOL hilight = FALSE);
 	void removeAllBars();
+
+public:
+	BOOL mFreezeView;
+	BOOL mOrderFetch;
 	
 private:
 	LLTextBox *mInfoTextp;
@@ -44,8 +49,6 @@ private:
 	U32 mNumTextureBars;
 
 	LLGLTexMemBar* mGLTexMemBar;
-
-	BOOL mFreezeView;
 	
 public:
 	static std::set<LLViewerImage*> sDebugImages;
diff --git a/indra/newview/lltoolcomp.cpp b/indra/newview/lltoolcomp.cpp
index 3f111583b511f6dcb91a4d45ad8e0ccb88896eb7..1947459c6adeebbc5e57c329b60b0fada2806ba2 100644
--- a/indra/newview/lltoolcomp.cpp
+++ b/indra/newview/lltoolcomp.cpp
@@ -204,7 +204,7 @@ BOOL LLToolCompTranslate::handleHover(S32 x, S32 y, MASK mask)
 BOOL LLToolCompTranslate::handleMouseDown(S32 x, S32 y, MASK mask)
 {
 	mMouseDown = TRUE;
-	gViewerWindow->hitObjectOrLandGlobalAsync(x, y, mask, pickCallback);
+	gViewerWindow->hitObjectOrLandGlobalAsync(x, y, mask, pickCallback, TRUE);
 	return TRUE;
 }
 
diff --git a/indra/newview/lltoolfocus.cpp b/indra/newview/lltoolfocus.cpp
index c439797c47cf8ae8d4624aa1b7cf12ae1fec4779..cdfc6c2ebfb1a5a2a81c898860e1a032fc599a90 100644
--- a/indra/newview/lltoolfocus.cpp
+++ b/indra/newview/lltoolfocus.cpp
@@ -69,13 +69,19 @@ LLToolCamera::~LLToolCamera()
 // virtual
 void LLToolCamera::handleSelect()
 {
-	if (gFloaterTools) gFloaterTools->setStatusText("Click and drag to change view");
+	if (gFloaterTools)
+	{
+		gFloaterTools->setStatusText("Click and drag to change view");
+	}
 }
 
 // virtual
 void LLToolCamera::handleDeselect()
 {
-	if (gFloaterTools) gFloaterTools->setStatusText("");
+	if (gFloaterTools)
+	{
+		gFloaterTools->setStatusText("");
+	}
 //	gAgent.setLookingAtAvatar(FALSE);
 }
 
diff --git a/indra/newview/lltoolmorph.cpp b/indra/newview/lltoolmorph.cpp
index 87e64965888fc7c95339044733791979a68777f0..49f21909a543736fff04671dcb8289d5368a2a51 100644
--- a/indra/newview/lltoolmorph.cpp
+++ b/indra/newview/lltoolmorph.cpp
@@ -215,10 +215,7 @@ BOOL LLVisualParamHint::render()
 	{
 		LLDrawPoolAvatar *avatarPoolp = (LLDrawPoolAvatar *)avatarp->mDrawable->getFace(0)->getPool();
 		LLGLDepthTest gls_depth(GL_TRUE, GL_TRUE);
-		gPipeline.unbindAGP();
-		avatarPoolp->syncAGP();
-		gPipeline.bindAGP();
-		avatarPoolp->renderAvatars(avatarp, TRUE);  // renders only one avatar (no shaders)
+		avatarPoolp->renderAvatars(avatarp);  // renders only one avatar
 	}
 	avatarp->setVisualParamWeight(mVisualParam, mLastParamWeight);
 	
diff --git a/indra/newview/lltoolpie.cpp b/indra/newview/lltoolpie.cpp
index e0b4483224944e359743f9666da0e7e4bb1fed40..68d9ddbd54219c9eef0ab3424e39b2d120813d95 100644
--- a/indra/newview/lltoolpie.cpp
+++ b/indra/newview/lltoolpie.cpp
@@ -61,10 +61,9 @@ LLToolPie::LLToolPie()
 
 BOOL LLToolPie::handleMouseDown(S32 x, S32 y, MASK mask)
 {
-	// if buttons swapped, don't pick transparent so users can't "pay" 
-	// transparent objects
+	//left mouse down always picks transparent
 	gViewerWindow->hitObjectOrLandGlobalAsync(x, y, mask, leftMouseCallback, 
-											  FALSE, TRUE);
+											  TRUE, TRUE);
 	mGrabMouseButtonDown = TRUE;
 	return TRUE;
 }
diff --git a/indra/newview/lltoolplacer.cpp b/indra/newview/lltoolplacer.cpp
index 20677fea983b39810389a3b53368822555cbf26d..66ed7cd1feb9f72c728a07f2494bb08657d09566 100644
--- a/indra/newview/lltoolplacer.cpp
+++ b/indra/newview/lltoolplacer.cpp
@@ -152,7 +152,6 @@ LLPCode LLToolPlacerPanel::sSphere		= LL_PCODE_SPHERE;
 LLPCode LLToolPlacerPanel::sSphereHemi	= LL_PCODE_SPHERE_HEMI;
 LLPCode LLToolPlacerPanel::sTree		= LL_PCODE_LEGACY_TREE;
 LLPCode LLToolPlacerPanel::sGrass		= LL_PCODE_LEGACY_GRASS;
-LLPCode LLToolPlacerPanel::sTreeNew		= LL_PCODE_TREE_NEW;
 
 S32			LLToolPlacerPanel::sButtonsAdded = 0;
 LLButton*	LLToolPlacerPanel::sButtons[ TOOL_PLACER_NUM_BUTTONS ];
diff --git a/indra/newview/lltoolplacer.h b/indra/newview/lltoolplacer.h
index 650fc03b01e64ce09a3faae57e4ace5f3612f4e5..b38272cfa940f5b9e2e8ddedc24a7cf51a61c196 100644
--- a/indra/newview/lltoolplacer.h
+++ b/indra/newview/lltoolplacer.h
@@ -66,7 +66,6 @@ public:
 	static LLPCode sSphereHemi;
 	static LLPCode sTree;
 	static LLPCode sGrass;
-	static LLPCode sTreeNew;
 
 private:
 	void		addButton( const LLString& up_state, const LLString& down_state, LLPCode* pcode );
diff --git a/indra/newview/llviewercamera.cpp b/indra/newview/llviewercamera.cpp
index c4c25b33f4328facdce9aa035bb4c9eee9347894..2c7c89280769630a4c430d14dc44a50d3a402566 100644
--- a/indra/newview/llviewercamera.cpp
+++ b/indra/newview/llviewercamera.cpp
@@ -119,7 +119,8 @@ void LLViewerCamera::calcProjection(const F32 far_distance) const
 
 LLMatrix4 gProjectionMat;
 
-void LLViewerCamera::updateFrustumPlanes()
+//static
+void LLViewerCamera::updateFrustumPlanes(LLCamera& camera, BOOL ortho)
 {
 	GLint viewport[4];
 	GLdouble model[16];
@@ -140,23 +141,26 @@ void LLViewerCamera::updateFrustumPlanes()
 	frust[2].setVec((F32)objX,(F32)objY,(F32)objZ);
 	gluUnProject(viewport[0],viewport[1]+viewport[3],0,model,proj,viewport,&objX,&objY,&objZ);
 	frust[3].setVec((F32)objX,(F32)objY,(F32)objZ);
-	/*gluUnProject(viewport[0],viewport[1],1,model,proj,viewport,&objX,&objY,&objZ);
-	frust[4].setVec((F32)objX,(F32)objY,(F32)objZ);
-	gluUnProject(viewport[0]+viewport[2],viewport[1],1,model,proj,viewport,&objX,&objY,&objZ);
-	frust[5].setVec((F32)objX,(F32)objY,(F32)objZ);
-	gluUnProject(viewport[0]+viewport[2],viewport[1]+viewport[3],1,model,proj,viewport,&objX,&objY,&objZ);
-	frust[6].setVec((F32)objX,(F32)objY,(F32)objZ);
-	gluUnProject(viewport[0],viewport[1]+viewport[3],1,model,proj,viewport,&objX,&objY,&objZ);
-	frust[7].setVec((F32)objX,(F32)objY,(F32)objZ);*/
 	
-	for (U32 i = 0; i < 4; i++)
+	if (ortho)
 	{
-		LLVector3 vec = frust[i] - getOrigin();
-		vec.normVec();
-		frust[i+4] = getOrigin() + vec*getFar()*2.0;
+		LLVector3 far_shift = LLVector3(camera.getFar()*2.0f,0,0);
+		for (U32 i = 0; i < 4; i++)
+		{
+			frust[i+4] = frust[i] + far_shift;
+		}
+	}
+	else
+	{
+		for (U32 i = 0; i < 4; i++)
+		{
+			LLVector3 vec = frust[i] - camera.getOrigin();
+			vec.normVec();
+			frust[i+4] = camera.getOrigin() + vec*camera.getFar()*2.0f;
+		}
 	}
 
-	calcAgentFrustumPlanes(frust);
+	camera.calcAgentFrustumPlanes(frust);
 }
 
 void LLViewerCamera::setPerspective(BOOL for_selection,
@@ -255,7 +259,7 @@ void LLViewerCamera::setPerspective(BOOL for_selection,
 		glGetIntegerv(GL_VIEWPORT, (GLint*)gGLViewport);
 	}
 
-	updateFrustumPlanes();
+	updateFrustumPlanes(*this);
 
 	if (gSavedSettings.getBOOL("CameraOffset"))
 	{
@@ -583,22 +587,34 @@ BOOL LLViewerCamera::areVertsVisible(LLViewerObject* volumep, BOOL all_verts)
 		return FALSE;
 	}
 
-	num_faces = drawablep->getNumFaces();
+	LLVolume* volume = volumep->getVolume();
+	if (!volume)
+	{
+		return FALSE;
+	}
+
+	LLVOVolume* vo_volume = (LLVOVolume*) volumep;
+
+	vo_volume->updateRelativeXform();
+	LLMatrix4 mat = vo_volume->getRelativeXform();
+	
+	LLMatrix4 render_mat(vo_volume->getRenderRotation(), LLVector4(vo_volume->getRenderPosition()));
+
+	num_faces = volume->getNumFaces();
 	for (i = 0; i < num_faces; i++)
 	{
-		LLStrider<LLVector3> vertices;
-		LLFace* face = drawablep->getFace(i);
-		face->getVertices(vertices);
-		
-		for (S32 v = 0; v < (S32)drawablep->getFace(i)->getGeomCount(); v++)
+		const LLVolumeFace& face = volume->getVolumeFace(i);
+				
+		for (U32 v = 0; v < face.mVertices.size(); v++)
 		{
-			LLVector3 vec = vertices[v];
-			if (!face->isState(LLFace::GLOBAL))
+			LLVector4 vec = LLVector4(face.mVertices[v].mPosition) * mat;
+
+			if (drawablep->isActive())
 			{
-				vec = vec*face->getRenderMatrix();
+				vec = vec * render_mat;	
 			}
-			
-			BOOL in_frustum = pointInFrustum(vec) > 0;
+
+			BOOL in_frustum = pointInFrustum(LLVector3(vec)) > 0;
 
 			if ( !in_frustum && all_verts ||
 				 in_frustum && !all_verts)
diff --git a/indra/newview/llviewercamera.h b/indra/newview/llviewercamera.h
index 679710399750387b00dfe4348c4132040b250f63..6ce1dcc0467f1ff2a01bfaf418722ee1cfd9ae00 100644
--- a/indra/newview/llviewercamera.h
+++ b/indra/newview/llviewercamera.h
@@ -36,7 +36,7 @@ public:
 								const LLVector3 &up_direction,
 								const LLVector3 &point_of_interest);
 
-	void updateFrustumPlanes();
+	static void updateFrustumPlanes(LLCamera& camera, BOOL ortho = FALSE);
 	void setPerspective(BOOL for_selection, S32 x, S32 y_from_bot, S32 width, S32 height, BOOL limit_select_distance, F32 z_near = 0, F32 z_far = 0);
 
 	const LLMatrix4 &getProjection() const;
diff --git a/indra/newview/llviewercontrol.h b/indra/newview/llviewercontrol.h
index 55d92c2d7ad14aade4148b9be70d369aecad5759..389f0e8bc6662c4569323544ca451ed213f7d9fc 100644
--- a/indra/newview/llviewercontrol.h
+++ b/indra/newview/llviewercontrol.h
@@ -36,6 +36,7 @@ protected:
 
 //setting variables are declared in this function
 void declare_settings();
+void settings_version_fixup();
 
 // saved at end of session
 extern LLControlGroup gSavedSettings;
diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp
index 9c3643128b96a7da43c8a41938de085633ee32d6..837eaa90e925e22bf29c2cc0aa085794205d7cf0 100644
--- a/indra/newview/llviewerdisplay.cpp
+++ b/indra/newview/llviewerdisplay.cpp
@@ -44,6 +44,9 @@
 #include "llfloatertools.h"
 #include "llviewerimagelist.h"
 #include "llfocusmgr.h"
+#include "llcubemap.h"
+#include "llviewerregion.h"
+#include "lldrawpoolwater.h"
 
 extern U32 gFrameCount;
 extern LLPointer<LLImageGL> gStartImageGL;
@@ -84,8 +87,12 @@ void display_startup()
 		return; 
 	}
 
-	LLDynamicTexture::updateAllInstances();
-
+	// Required for HTML update in login screen
+	static S32 frame_count = 0;
+	if (frame_count++ > 1) // make sure we have rendered a frame first
+	{
+		LLDynamicTexture::updateAllInstances();
+	}
 	glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
 	LLGLSDefault gls_default;
 	LLGLSUIDefault gls_ui;
@@ -364,6 +371,7 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield)
 	//
 
 	gCamera->setZoomParameters(zoom_factor, subfield);
+	gCamera->setNear(MIN_NEAR_PLANE);
 
 	//////////////////////////
 	//
@@ -379,6 +387,8 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield)
 	else if (!gViewerWindow->isPickPending())
 	{
 		glClear( GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
+		//DEBUG TEMPORARY
+		glClear(GL_COLOR_BUFFER_BIT);
 	}
 	gViewerWindow->setupViewport();
 
@@ -399,7 +409,13 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield)
 		glClearColor(0.5f, 0.5f, 0.5f, 0.f);
 		glClear(GL_COLOR_BUFFER_BIT);
 		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+		LLPipeline::sUseOcclusion = FALSE;
 	}
+	else
+	{
+		LLPipeline::sUseOcclusion = gSavedSettings.getBOOL("UseOcclusion") && gGLManager.mHasOcclusionQuery;
+	}
+
 	stop_glerror();
 
 	///////////////////////////////////////
@@ -412,7 +428,8 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield)
 	glLightModelfv (GL_LIGHT_MODEL_AMBIENT,one);
 	stop_glerror();
 	
-	//LLGLState::verify();
+	//Increment drawable frame counter
+	LLDrawable::incrementVisible();
 
 	/////////////////////////////////////
 	//
@@ -422,6 +439,11 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield)
 	//
 	if (!gDisconnected)
 	{
+		if (gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_HUD))
+		{ //don't draw hud objects in this frame
+			gPipeline.toggleRenderType(LLPipeline::RENDER_TYPE_HUD);
+		}
+		
 		LLFastTimer t(LLFastTimer::FTM_WORLD_UPDATE);
 		stop_glerror();
 		display_update_camera();
@@ -437,34 +459,41 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield)
 		gPipeline.updateGeom(max_geom_update_time);
 		stop_glerror();
 		
+		LLSpatialPartition* part = gPipeline.getSpatialPartition(LLPipeline::PARTITION_VOLUME);
+		part->processImagery(gCamera);
+
+		display_update_camera();
+
 		gFrameStats.start(LLFrameStats::UPDATE_CULL);
-		gPipeline.updateCull();
+		gPipeline.updateCull(*gCamera);
 		stop_glerror();
 		
-		if (rebuild)
+		///////////////////////////////////
+		//
+		// StateSort
+		//
+		// Responsible for taking visible objects, and adding them to the appropriate draw orders.
+		// In the case of alpha objects, z-sorts them first.
+		// Also creates special lists for outlines and selected face rendering.
+		//
 		{
 			LLFastTimer t(LLFastTimer::FTM_REBUILD);
-
-			///////////////////////////////////
-			//
-			// StateSort
-			//
-			// Responsible for taking visible objects, and adding them to the appropriate draw orders.
-			// In the case of alpha objects, z-sorts them first.
-			// Also creates special lists for outlines and selected face rendering.
-			//
-			gFrameStats.start(LLFrameStats::STATE_SORT);
-			gPipeline.stateSort();
-			stop_glerror();
 			
-			//////////////////////////////////////
-			//
-			// rebuildPools
-			//
-			//
-			gFrameStats.start(LLFrameStats::REBUILD);
-			gPipeline.rebuildPools();
+			gFrameStats.start(LLFrameStats::STATE_SORT);
+			gPipeline.stateSort(*gCamera);
 			stop_glerror();
+				
+			if (rebuild)
+			{
+				//////////////////////////////////////
+				//
+				// rebuildPools
+				//
+				//
+				gFrameStats.start(LLFrameStats::REBUILD);
+				gPipeline.rebuildPools();
+				stop_glerror();
+			}
 		}
 	}
 
@@ -509,10 +538,65 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield)
 			&& !gRestoreGL
 			&& !gDisconnected)
 	{
-		gPipeline.renderGeom();
+		gPipeline.renderGeom(*gCamera);
 		stop_glerror();
 	}
 
+	//render hud attachments
+	glMatrixMode(GL_PROJECTION);
+	glPushMatrix();
+	glMatrixMode(GL_MODELVIEW);
+	glPushMatrix();
+	if (LLPipeline::sShowHUDAttachments && !gDisconnected && setup_hud_matrices(FALSE))
+	{
+		LLCamera hud_cam = *gCamera;
+		glClear(GL_DEPTH_BUFFER_BIT);
+		LLVector3 origin = hud_cam.getOrigin();
+		hud_cam.setOrigin(-1.f,0,0);
+		hud_cam.setAxes(LLVector3(1,0,0), LLVector3(0,1,0), LLVector3(0,0,1));
+		LLViewerCamera::updateFrustumPlanes(hud_cam, TRUE);
+		//only render hud objects
+		U32 mask = gPipeline.getRenderTypeMask();
+		gPipeline.setRenderTypeMask(0);
+		gPipeline.toggleRenderType(LLPipeline::RENDER_TYPE_HUD);
+
+		BOOL has_ui = gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI);
+		if (has_ui)
+		{
+			gPipeline.toggleRenderDebugFeature((void*) LLPipeline::RENDER_DEBUG_FEATURE_UI);
+		}
+
+		BOOL use_occlusion = gSavedSettings.getBOOL("UseOcclusion");
+		gSavedSettings.setBOOL("UseOcclusion", FALSE);
+
+		//cull, sort, and render hud objects
+		gPipeline.updateCull(hud_cam);
+
+		gPipeline.toggleRenderType(LLDrawPool::POOL_ALPHA_POST_WATER);
+		gPipeline.toggleRenderType(LLPipeline::RENDER_TYPE_BUMP);
+		gPipeline.toggleRenderType(LLPipeline::RENDER_TYPE_SIMPLE);
+		gPipeline.toggleRenderType(LLPipeline::RENDER_TYPE_VOLUME);
+		
+		{
+			LLFastTimer ftm(LLFastTimer::FTM_REBUILD);
+			gPipeline.stateSort(hud_cam);
+		}
+		
+		gPipeline.renderGeom(hud_cam);
+
+		//restore type mask
+		gPipeline.setRenderTypeMask(mask);
+		if (has_ui)
+		{
+			gPipeline.toggleRenderDebugFeature((void*) LLPipeline::RENDER_DEBUG_FEATURE_UI);
+		}
+		gSavedSettings.setBOOL("UseOcclusion", use_occlusion);
+	}
+	glMatrixMode(GL_PROJECTION);
+	glPopMatrix();
+	glMatrixMode(GL_MODELVIEW);
+	glPopMatrix();
+
 	gFrameStats.start(LLFrameStats::RENDER_UI);
 
 	if (gHandleKeysAsync)
@@ -535,6 +619,64 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield)
 
 }
 
+BOOL setup_hud_matrices(BOOL for_select)
+{
+	LLVOAvatar* my_avatarp = gAgent.getAvatarObject();
+	if (my_avatarp && my_avatarp->hasHUDAttachment())
+	{
+		if (!for_select)
+		{
+			// clamp target zoom level to reasonable values
+			my_avatarp->mHUDTargetZoom = llclamp(my_avatarp->mHUDTargetZoom, 0.1f, 1.f);
+			// smoothly interpolate current zoom level
+			my_avatarp->mHUDCurZoom = lerp(my_avatarp->mHUDCurZoom, my_avatarp->mHUDTargetZoom, LLCriticalDamp::getInterpolant(0.03f));
+		}
+
+		F32 zoom_level = my_avatarp->mHUDCurZoom;
+		// clear z buffer and set up transform for hud
+		if (!for_select)
+		{
+			glClear(GL_DEPTH_BUFFER_BIT);
+		}
+		LLBBox hud_bbox = my_avatarp->getHUDBBox();
+
+		// set up transform to encompass bounding box of HUD
+		glMatrixMode(GL_PROJECTION);
+		glLoadIdentity();
+		F32 hud_depth = llmax(1.f, hud_bbox.getExtentLocal().mV[VX] * 1.1f);
+		if (for_select)
+		{
+			//RN: reset viewport to window extents so ortho screen is calculated with proper reference frame
+			gViewerWindow->setupViewport();
+		}
+		glOrtho(-0.5f * gCamera->getAspect(), 0.5f * gCamera->getAspect(), -0.5f, 0.5f, 0.f, hud_depth);
+
+		// apply camera zoom transform (for high res screenshots)
+		F32 zoom_factor = gCamera->getZoomFactor();
+		S16 sub_region = gCamera->getZoomSubRegion();
+		if (zoom_factor > 1.f)
+		{
+			float offset = zoom_factor - 1.f;
+			int pos_y = sub_region / llceil(zoom_factor);
+			int pos_x = sub_region - (pos_y*llceil(zoom_factor));
+			glTranslatef(gCamera->getAspect() * 0.5f * (offset - (F32)pos_x * 2.f), 0.5f * (offset - (F32)pos_y * 2.f), 0.f);
+			glScalef(zoom_factor, zoom_factor, 1.f);
+		}
+
+		glMatrixMode(GL_MODELVIEW);
+		glLoadIdentity();
+		glLoadMatrixf(OGL_TO_CFR_ROTATION);		// Load Cory's favorite reference frame
+		glTranslatef(-hud_bbox.getCenterLocal().mV[VX] + (hud_depth * 0.5f), 0.f, 0.f);
+		glScalef(zoom_level, zoom_level, zoom_level);
+
+		return TRUE;
+	}
+	else
+	{
+		return FALSE;
+	}
+}
+
 
 void render_ui_and_swap()
 {
@@ -546,7 +688,7 @@ void render_ui_and_swap()
 	{
 		LLGLSUIDefault gls_ui;
 		gPipeline.disableLights();
-		
+		LLVertexBuffer::startRender();
 		if (gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI))
 		{
 			LLFastTimer t(LLFastTimer::FTM_RENDER_UI);
@@ -563,6 +705,8 @@ void render_ui_and_swap()
 			LLGLState::checkStates();
 #endif
 		}
+		LLVertexBuffer::stopRender();
+		glFlush();
 
 		// now do the swap buffer
 		if (gDisplaySwapBuffers)
@@ -570,6 +714,11 @@ void render_ui_and_swap()
 			LLFastTimer t(LLFastTimer::FTM_SWAP);
 			gViewerWindow->mWindow->swapBuffers();
 		}
+
+		{
+// 			LLFastTimer ftm(LLFastTimer::FTM_TEMP6);
+			LLVertexBuffer::clientCopy();
+		}
 	}
 }
 
@@ -585,8 +734,6 @@ void render_ui_3d()
 	//
 
 	// Render selections
-
-	glDisableClientState(GL_VERTEX_ARRAY);
 	glDisableClientState(GL_COLOR_ARRAY);
 	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
 	glDisableClientState(GL_NORMAL_ARRAY);
@@ -643,7 +790,6 @@ void render_ui_2d()
 		LLFontGL::sCurOrigin.mY -= llround((F32)gViewerWindow->getWindowHeight() * (F32)pos_y / zoom_factor);
 	}
 
-
 	stop_glerror();
 	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
 
diff --git a/indra/newview/llviewerjoint.cpp b/indra/newview/llviewerjoint.cpp
index cfd23e71204e3719e4285dad9933aee30cd86091..0ab1fd1cbf660e2ffdb111d7ec1b2fbf59ec5b16 100644
--- a/indra/newview/llviewerjoint.cpp
+++ b/indra/newview/llviewerjoint.cpp
@@ -82,10 +82,10 @@ void LLViewerJoint::setValid( BOOL valid, BOOL recursive )
 	//----------------------------------------------------------------
 	if (recursive)
 	{
-		for (	LLViewerJoint *joint = (LLViewerJoint*)mChildren.getFirstData();
-				joint != NULL;
-				joint = (LLViewerJoint*)mChildren.getNextData() )
+		for (child_list_t::iterator iter = mChildren.begin();
+			 iter != mChildren.end(); ++iter)
 		{
+			LLViewerJoint* joint = (LLViewerJoint*)(*iter);
 			joint->setValid(valid, TRUE);
 		}
 	}
@@ -198,10 +198,10 @@ void LLViewerJoint::renderSkeleton(BOOL recursive)
 	//----------------------------------------------------------------
 	if (recursive)
 	{
-		for (	LLViewerJoint *joint = (LLViewerJoint*)mChildren.getFirstData();
-				joint != NULL;
-				joint = (LLViewerJoint*)mChildren.getNextData() )
+		for (child_list_t::iterator iter = mChildren.begin();
+			 iter != mChildren.end(); ++iter)
 		{
+			LLViewerJoint* joint = (LLViewerJoint*)(*iter);
 			joint->renderSkeleton();
 		}
 	}
@@ -216,7 +216,7 @@ void LLViewerJoint::renderSkeleton(BOOL recursive)
 //--------------------------------------------------------------------
 // render()
 //--------------------------------------------------------------------
-U32 LLViewerJoint::render( F32 pixelArea )
+U32 LLViewerJoint::render( F32 pixelArea, BOOL first_pass )
 {
 	U32 triangle_count = 0;
 
@@ -226,73 +226,68 @@ U32 LLViewerJoint::render( F32 pixelArea )
 	if ( mValid )
 	{
 
+
 		//----------------------------------------------------------------
 		// if object is transparent, defer it, otherwise
 		// give the joint subclass a chance to draw itself
 		//----------------------------------------------------------------
 		if ( gRenderForSelect )
 		{
-			triangle_count += drawShape( pixelArea );
+			triangle_count += drawShape( pixelArea, first_pass );
 		}
 		else if ( isTransparent() )
 		{
-			LLGLEnable blend(GL_BLEND);
 			// Hair and Skirt
 			if ((pixelArea > MIN_PIXEL_AREA_3PASS_HAIR))
 			{
 				// render all three passes
-				LLGLEnable alpha_test(GL_ALPHA_TEST);
 				LLGLDisable cull(GL_CULL_FACE);
 				// first pass renders without writing to the z buffer
 				{
 					LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
-					triangle_count += drawShape( pixelArea );
+					triangle_count += drawShape( pixelArea, first_pass);
 				}
 				// second pass writes to z buffer only
 				glColorMask(FALSE, FALSE, FALSE, FALSE);
 				{
-					triangle_count += drawShape( pixelArea );
+					triangle_count += drawShape( pixelArea, FALSE );
 				}
 				// third past respects z buffer and writes color
 				glColorMask(TRUE, TRUE, TRUE, TRUE);
 				{
 					LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
-					triangle_count += drawShape( pixelArea );
+					triangle_count += drawShape( pixelArea, FALSE );
 				}
 			}
 			else
 			{
-				LLGLEnable alpha_test(GL_ALPHA_TEST);
 				// Render Inside (no Z buffer write)
 				glCullFace(GL_FRONT);
 				{
 					LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
-					triangle_count += drawShape( pixelArea );
+					triangle_count += drawShape( pixelArea, first_pass );
 				}
 				// Render Outside (write to the Z buffer)
 				glCullFace(GL_BACK);
 				{
-					triangle_count += drawShape( pixelArea );
+					triangle_count += drawShape( pixelArea, FALSE );
 				}
 			}
 		}
 		else
 		{
 			// set up render state
-			LLGLDisable blend(GL_BLEND);
-			LLGLSPipelineAvatar gls_pipeline_avatar;
-			triangle_count += drawShape( pixelArea );
+			triangle_count += drawShape( pixelArea, first_pass );
 		}
 	}
 
 	//----------------------------------------------------------------
 	// render children
 	//----------------------------------------------------------------
-	LLViewerJoint *joint;
-	for (	joint = (LLViewerJoint *)mChildren.getFirstData();
-			joint != NULL;
-			joint = (LLViewerJoint *)mChildren.getNextData() )
+	for (child_list_t::iterator iter = mChildren.begin();
+		 iter != mChildren.end(); ++iter)
 	{
+		LLViewerJoint* joint = (LLViewerJoint*)(*iter);
 		F32 jointLOD = joint->getLOD();
 		if (pixelArea >= jointLOD || sDisableLOD)
 		{
@@ -305,7 +300,6 @@ U32 LLViewerJoint::render( F32 pixelArea )
 		}
 	}
 
-	glColorMask(TRUE, TRUE, TRUE, TRUE);
 	return triangle_count;
 }
 
@@ -377,7 +371,7 @@ BOOL LLViewerJoint::isTransparent()
 //--------------------------------------------------------------------
 // drawShape()
 //--------------------------------------------------------------------
-U32 LLViewerJoint::drawShape( F32 pixelArea )
+U32 LLViewerJoint::drawShape( F32 pixelArea, BOOL first_pass )
 {
 	return 0;
 }
@@ -390,65 +384,75 @@ void LLViewerJoint::setSkeletonComponents( U32 comp, BOOL recursive )
 	mComponents = comp;
 	if (recursive)
 	{
-		for (	LLViewerJoint *joint = (LLViewerJoint *)mChildren.getFirstData();
-				joint != NULL;
-				joint = (LLViewerJoint *)mChildren.getNextData() )
+		for (child_list_t::iterator iter = mChildren.begin();
+			 iter != mChildren.end(); ++iter)
 		{
+			LLViewerJoint* joint = (LLViewerJoint*)(*iter);
 			joint->setSkeletonComponents(comp, recursive);
 		}
 	}
 }
 
-void LLViewerJoint::updateFaceSizes(U32 &num_vertices, F32 pixel_area)
+void LLViewerJoint::updateFaceSizes(U32 &num_vertices, U32& num_indices, F32 pixel_area)
 {
-	for (	LLViewerJoint *joint = (LLViewerJoint*)mChildren.getFirstData();
-			joint != NULL;
-			joint = (LLViewerJoint*)mChildren.getNextData() )
+	for (child_list_t::iterator iter = mChildren.begin();
+		 iter != mChildren.end(); ++iter)
 	{
-		F32 jointLOD = joint->getLOD();
-		if (pixel_area >= jointLOD || sDisableLOD)
+		LLViewerJoint* joint = (LLViewerJoint*)(*iter);
+		//F32 jointLOD = joint->getLOD();
+		//if (pixel_area >= jointLOD || sDisableLOD)
 		{
-			joint->updateFaceSizes(num_vertices, pixel_area);
+			joint->updateFaceSizes(num_vertices, num_indices, pixel_area);
 
-			if (jointLOD != DEFAULT_LOD)
-			{
-				break;
-			}
+		//	if (jointLOD != DEFAULT_LOD)
+		//	{
+		//		break;
+		//	}
 		}
 	}
 }
 
 void LLViewerJoint::updateFaceData(LLFace *face, F32 pixel_area, BOOL damp_wind)
 {
-	for (	LLViewerJoint *joint = (LLViewerJoint*)mChildren.getFirstData();
-			joint != NULL;
-			joint = (LLViewerJoint*)mChildren.getNextData() )
+	for (child_list_t::iterator iter = mChildren.begin();
+		 iter != mChildren.end(); ++iter)
 	{
-		F32 jointLOD = joint->getLOD();
-		if (pixel_area >= jointLOD || sDisableLOD)
+		LLViewerJoint* joint = (LLViewerJoint*)(*iter);
+		//F32 jointLOD = joint->getLOD();
+		//if (pixel_area >= jointLOD || sDisableLOD)
 		{
 			joint->updateFaceData(face, pixel_area, damp_wind);
 
-			if (jointLOD != DEFAULT_LOD)
-			{
-				break;
-			}
+		//	if (jointLOD != DEFAULT_LOD)
+		//	{
+		//		break;
+		//	}
 		}
 	}
 }
 
+void LLViewerJoint::updateGeometry()
+{
+	for (child_list_t::iterator iter = mChildren.begin();
+		 iter != mChildren.end(); ++iter)
+	{
+		LLViewerJoint* joint = (LLViewerJoint*)(*iter);
+		joint->updateGeometry();
+	}
+}
+
 
 BOOL LLViewerJoint::updateLOD(F32 pixel_area, BOOL activate)
 {
 	BOOL lod_changed = FALSE;
 	BOOL found_lod = FALSE;
 
-	for (	LLViewerJoint *joint = (LLViewerJoint*)mChildren.getFirstData();
-			joint != NULL;
-			joint = (LLViewerJoint*)mChildren.getNextData() )
+	for (child_list_t::iterator iter = mChildren.begin();
+		 iter != mChildren.end(); ++iter)
 	{
+		LLViewerJoint* joint = (LLViewerJoint*)(*iter);
 		F32 jointLOD = joint->getLOD();
-
+		
 		if (found_lod || jointLOD == DEFAULT_LOD)
 		{
 			// we've already found a joint to enable, so enable the rest as alternatives
@@ -472,12 +476,12 @@ BOOL LLViewerJoint::updateLOD(F32 pixel_area, BOOL activate)
 
 void LLViewerJoint::dump()
 {
-	for (	LLViewerJoint *joint = (LLViewerJoint*)mChildren.getFirstData();
-			joint != NULL;
-			joint = (LLViewerJoint*)mChildren.getNextData() )
-		{
-			joint->dump();
-		}
+	for (child_list_t::iterator iter = mChildren.begin();
+		 iter != mChildren.end(); ++iter)
+	{
+		LLViewerJoint* joint = (LLViewerJoint*)(*iter);
+		joint->dump();
+	}
 }
 
 void LLViewerJoint::setVisible(BOOL visible, BOOL recursive)
@@ -486,12 +490,12 @@ void LLViewerJoint::setVisible(BOOL visible, BOOL recursive)
 
 	if (recursive)
 	{
-		for (	LLViewerJoint *joint = (LLViewerJoint*)mChildren.getFirstData();
-				joint != NULL;
-				joint = (LLViewerJoint*)mChildren.getNextData() )
-			{
-				joint->setVisible(visible, recursive);
-			}
+		for (child_list_t::iterator iter = mChildren.begin();
+			 iter != mChildren.end(); ++iter)
+		{
+			LLViewerJoint* joint = (LLViewerJoint*)(*iter);
+			joint->setVisible(visible, recursive);
+		}
 	}
 }
 
@@ -511,10 +515,10 @@ void LLViewerJoint::writeCAL3D(apr_file_t* fp)
 	LLQuaternion bone_rot;
 
 	S32 num_children = 0;
-	for (LLViewerJoint *joint = (LLViewerJoint*)mChildren.getFirstData();
-		joint != NULL;
-		joint = (LLViewerJoint*)mChildren.getNextData() )
+	for (child_list_t::iterator iter = mChildren.begin();
+		 iter != mChildren.end(); ++iter)
 	{
+		LLViewerJoint* joint = (LLViewerJoint*)(*iter);
 		if (joint->mJointNum != -1)
 		{
 			num_children++;
@@ -541,10 +545,10 @@ void LLViewerJoint::writeCAL3D(apr_file_t* fp)
 	apr_file_printf(fp, "		<LOCALROTATION>0 0 0 1</LOCALROTATION>\n");
 	apr_file_printf(fp, "		<PARENTID>%d</PARENTID>\n", mParent ? mParent->mJointNum + 1 : -1);
 	
-	for (LLViewerJoint *joint = (LLViewerJoint*)mChildren.getFirstData();
-		joint != NULL;
-		joint = (LLViewerJoint*)mChildren.getNextData() )
+	for (child_list_t::iterator iter = mChildren.begin();
+		 iter != mChildren.end(); ++iter)
 	{
+		LLViewerJoint* joint = (LLViewerJoint*)(*iter);
 		if (joint->mJointNum != -1)
 		{
 			apr_file_printf(fp, "		<CHILDID>%d</CHILDID>\n", joint->mJointNum + 1);
@@ -553,10 +557,10 @@ void LLViewerJoint::writeCAL3D(apr_file_t* fp)
 	apr_file_printf(fp, "	</BONE>\n");
 
 	// recurse
-	for (LLViewerJoint *joint = (LLViewerJoint*)mChildren.getFirstData();
-		joint != NULL;
-		joint = (LLViewerJoint*)mChildren.getNextData() )
+	for (child_list_t::iterator iter = mChildren.begin();
+		 iter != mChildren.end(); ++iter)
 	{
+		LLViewerJoint* joint = (LLViewerJoint*)(*iter);
 		if (joint->mJointNum != -1)
 		{
 			joint->writeCAL3D(fp);
diff --git a/indra/newview/llviewerjoint.h b/indra/newview/llviewerjoint.h
index e38c1c8e7dd940821f87a7f2dcccebe1c72f986c..9b5185267f79b8f013c856d01f0a546f1a42044e 100644
--- a/indra/newview/llviewerjoint.h
+++ b/indra/newview/llviewerjoint.h
@@ -43,7 +43,7 @@ public:
 	// Traverses the entire joint hierarchy, setting up
 	// transforms and calling the drawShape().
 	// Derived classes may add text/graphic output.
-	virtual U32 render( F32 pixelArea );	// Returns triangle count
+	virtual U32 render( F32 pixelArea, BOOL first_pass = TRUE );	// Returns triangle count
 
 	// Draws a bone graphic to the parent joint.
 	// Derived classes may add text/graphic output.
@@ -59,7 +59,7 @@ public:
 
 	// Draws the shape attached to a joint.
 	// Called by render().
-	virtual U32 drawShape( F32 pixelArea );
+	virtual U32 drawShape( F32 pixelArea, BOOL first_pass = TRUE );
 	virtual void drawNormals() {}
 
 	enum Components
@@ -78,9 +78,9 @@ public:
 	// Sets the level of detail for this node as a minimum
 	// pixel area threshold.  If the current pixel area for this
 	// object is less than the specified threshold, the node is
-	// not traversed.  In additin, if a value is specified (not
+	// not traversed.  In addition, if a value is specified (not
 	// default of 0.0), and the pixel area is larger than the
-	// specified miniumn, the node is rendered, but no other siblings
+	// specified minimum, the node is rendered, but no other siblings
 	// of this node under the same parent will be.
 	F32 getLOD() { return mMinPixelArea; }
 	void setLOD( F32 pixelArea ) { mMinPixelArea = pixelArea; }
@@ -101,9 +101,10 @@ public:
 	void setPickName(PickName name) { mPickName = name; }
 	PickName getPickName() { return mPickName; }
 
-	virtual void updateFaceSizes(U32 &num_vertices, F32 pixel_area);
+	virtual void updateFaceSizes(U32 &num_vertices, U32& num_indices, F32 pixel_area);
 	virtual void updateFaceData(LLFace *face, F32 pixel_area, BOOL damp_wind = FALSE);
 	virtual BOOL updateLOD(F32 pixel_area, BOOL activate);
+	virtual void updateGeometry();
 	virtual void dump();
 
 	void setVisible( BOOL visible, BOOL recursive );
@@ -135,3 +136,4 @@ public:
 
 #endif // LL_LLVIEWERJOINT_H
 
+
diff --git a/indra/newview/llviewerjointattachment.cpp b/indra/newview/llviewerjointattachment.cpp
index 4ae527eee52c3d3af306febc4bc59484d682584d..090276ef513081dc94ccea076ddddc8a8ab6c749 100644
--- a/indra/newview/llviewerjointattachment.cpp
+++ b/indra/newview/llviewerjointattachment.cpp
@@ -60,7 +60,7 @@ BOOL LLViewerJointAttachment::isTransparent()
 //-----------------------------------------------------------------------------
 // drawShape()
 //-----------------------------------------------------------------------------
-U32 LLViewerJointAttachment::drawShape( F32 pixelArea )
+U32 LLViewerJointAttachment::drawShape( F32 pixelArea, BOOL first_pass )
 {
 	if (LLVOAvatar::sShowAttachmentPoints)
 	{
@@ -277,47 +277,20 @@ void LLViewerJointAttachment::removeObject(LLViewerObject *object)
 //-----------------------------------------------------------------------------
 void LLViewerJointAttachment::setAttachmentVisibility(BOOL visible)
 {
-	if (!mAttachedObject || mAttachedObject->mDrawable.isNull())
+	if (!mAttachedObject || mAttachedObject->mDrawable.isNull() || 
+		!(mAttachedObject->mDrawable->getSpatialBridge()))
 		return;
 
 	if (visible)
 	{
 		// Hack to make attachments not visible by disabling their type mask!
 		// This will break if you can ever attach non-volumes! - djs 02/14/03
-		mAttachedObject->mDrawable->setRenderType(LLPipeline::RENDER_TYPE_VOLUME);
-		for (LLViewerObject::child_list_t::iterator iter = mAttachedObject->mChildList.begin();
-			 iter != mAttachedObject->mChildList.end(); ++iter)
-		{
-			LLViewerObject* childp = *iter;
-			if (childp && childp->mDrawable.notNull())
-			{
-				childp->mDrawable->setRenderType(LLPipeline::RENDER_TYPE_VOLUME);
-			}
-		}
+		mAttachedObject->mDrawable->getSpatialBridge()->mDrawableType = 
+			mAttachedObject->isHUDAttachment() ? LLPipeline::RENDER_TYPE_HUD : LLPipeline::RENDER_TYPE_VOLUME;
 	}
 	else
 	{
-		//RN: sometimes we call this on objects that are already invisible, so check for that case
-		if (!mAttachedObject->mDrawable->isRenderType(LLPipeline::RENDER_TYPE_VOLUME) && 
-			!mAttachedObject->mDrawable->isRenderType(0))
-		{
-			llerrs << "Tried to attach non-volume to a joint, visibility hack dangerous!" << llendl;
-		}
-		mAttachedObject->mDrawable->setRenderType(0);
-		for (LLViewerObject::child_list_t::iterator iter = mAttachedObject->mChildList.begin();
-			 iter != mAttachedObject->mChildList.end(); ++iter)
-		{
-			LLViewerObject* childp = *iter;
-			if (childp && childp->mDrawable.notNull())
-			{
-				if (!childp->mDrawable->isRenderType(LLPipeline::RENDER_TYPE_VOLUME) && 
-					!mAttachedObject->mDrawable->isRenderType(0))
-				{
-					llerrs << "Tried to attach non-volume to a joint, visibility hack dangerous!" << llendl;
-				}
-				childp->mDrawable->setRenderType(0);
-			}
-		}
+		mAttachedObject->mDrawable->getSpatialBridge()->mDrawableType = 0;
 	}
 }
 
diff --git a/indra/newview/llviewerjointattachment.h b/indra/newview/llviewerjointattachment.h
index b3a6ccb89c8acbff040257807fb9a53021787ea4..e0354a8c10a335f54c8bb90cecbbe1ca4acd664b 100644
--- a/indra/newview/llviewerjointattachment.h
+++ b/indra/newview/llviewerjointattachment.h
@@ -37,7 +37,7 @@ public:
 
 	// Draws the shape attached to a joint.
 	// Called by render().
-	/*virtual*/ U32 drawShape( F32 pixelArea );
+	/*virtual*/ U32 drawShape( F32 pixelArea, BOOL first_pass );
 	
 	/*virtual*/ BOOL updateLOD(F32 pixel_area, BOOL activate);
 
@@ -60,7 +60,7 @@ public:
 	S32 getGroup() { return mGroup; }
 	S32 getPieSlice() { return mPieSlice; }
 	BOOL getAttachmentDirty() { return mAttachmentDirty && mAttachedObject; }
-	LLViewerObject *getObject(S32 i) { return mAttachedObject; }
+	LLViewerObject *getObject() { return mAttachedObject; }
 	S32	getNumObjects() { return (mAttachedObject ? 1 : 0); }
 	const LLUUID& getItemID() { return mItemID; }
 
diff --git a/indra/newview/llviewerjointmesh.cpp b/indra/newview/llviewerjointmesh.cpp
index aec15a8d6ce15c9b916804f2dfffad786773f15f..512ddc8565bd1a05535d1efb062a89a262bb2139 100644
--- a/indra/newview/llviewerjointmesh.cpp
+++ b/indra/newview/llviewerjointmesh.cpp
@@ -19,7 +19,6 @@
 #include "llfasttimer.h"
 
 #include "llagent.h"
-#include "llagparray.h"
 #include "llbox.h"
 #include "lldrawable.h"
 #include "lldrawpoolavatar.h"
@@ -43,6 +42,10 @@ extern PFNGLVERTEXBLENDARBPROC glVertexBlendARB;
 #endif
 extern BOOL gRenderForSelect;
 
+static LLPointer<LLVertexBuffer> sRenderBuffer = NULL;
+static const U32 sRenderMask = LLVertexBuffer::MAP_VERTEX |
+							   LLVertexBuffer::MAP_NORMAL |
+							   LLVertexBuffer::MAP_TEXCOORD;
 LLMatrix4 gBlendMat;
 
 //-----------------------------------------------------------------------------
@@ -375,11 +378,11 @@ void LLViewerJointMesh::setupJoint(LLViewerJoint* current_joint)
 	}
 
 	// depth-first traversal
-	for (LLJoint *child_joint = current_joint->mChildren.getFirstData(); 
-		child_joint; 
-		child_joint = current_joint->mChildren.getNextData())
+	for (LLJoint::child_list_t::iterator iter = current_joint->mChildren.begin();
+		 iter != current_joint->mChildren.end(); ++iter)
 	{
-		setupJoint((LLViewerJoint*)child_joint);
+		LLViewerJoint* child_joint = (LLViewerJoint*)(*iter);
+		setupJoint(child_joint);
 	}
 }
 
@@ -412,7 +415,7 @@ void LLViewerJointMesh::uploadJointMatrices()
 
 		if (hardware_skinning)
 		{
-			joint_mat *= gCamera->getModelview();
+			joint_mat *= LLDrawPoolAvatar::getModelView();
 		}
 		gJointMat[joint_num] = joint_mat;
 		gJointRot[joint_num] = joint_mat.getMat3();
@@ -513,620 +516,39 @@ int compare_int(const void *a, const void *b)
 	else return 0;
 }
 
-#if LL_WINDOWS || (LL_DARWIN && __i386__) // SSE optimizations in avatar code
-
-#if LL_DARWIN
-#include <xmmintrin.h>
-
-	// On Windows, this class is defined in fvec.h.  I've only reproduced the parts of it we use here for now.
-	#pragma pack(push,16) /* Must ensure class & union 16-B aligned */
-	class F32vec4
-	{
-	protected:
-		 __m128 vec;
-	public:
-
-		/* Constructors: __m128, 4 floats, 1 float */
-		F32vec4() {}
-
-		/* initialize 4 SP FP with __m128 data type */
-		F32vec4(__m128 m)					{ vec = m;}
-
-		/* Explicitly initialize each of 4 SP FPs with same float */
-		explicit F32vec4(float f)	{ vec = _mm_set_ps1(f); }
-	};
-	#pragma pack(pop) /* 16-B aligned */
-	
-	
-#endif
-
-void blend_SSE_32_32_batch(const int vert_offset, const int vert_count, float* output,
-					 LLStrider<LLVector3>& vertices, LLStrider<LLVector2>& texcoords, LLStrider<LLVector3>& normals, LLStrider<F32>& weights)
+void llDrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices)
 {
-	F32 last_weight = F32_MAX;
-	LLMatrix4 *blend_mat = &gBlendMat;
-
-	for (S32 index = vert_offset; index < vert_offset + vert_count; index++)
+	if (end-start+1 > (U32) gGLManager.mGLMaxVertexRange ||
+		count > gGLManager.mGLMaxIndexRange)
 	{
-		F32     w  = weights  [index];        // register copy of weight
-		F32  *vin  = &vertices[index].mV[0];  // pointer to input vertex data, assumed to be V3+T2+N3+whatever
-		F32  *vout = output + index * (AVATAR_VERTEX_BYTES/sizeof(F32));    // pointer to the output vertex data, assumed to be 16 byte aligned
-
-		if (w == last_weight)
-		{
-			// load input and output vertices, and last blended matrix
-			__asm {
-				mov		esi,  vin
-				mov		edi,  vout
-
-				mov		edx,  blend_mat
-				movaps	xmm4, [edx]
-				movaps	xmm5, [edx+0x10]
-				movaps	xmm6, [edx+0x20]
-				movaps	xmm7, [edx+0x30]
-			}
-		}
-		else
-		{
-			last_weight = w;
-			S32 joint = llfloor(w);
-			w -= joint;
-
-			LLMatrix4 *m0 = &(gJointMat[joint+1]);
-			LLMatrix4 *m1 = &(gJointMat[joint+0]);
-
-			// some initial code to load Matrix 0 into SSE registers
-			__asm {
-				mov		esi,  vin
-				mov		edi,  vout
-
-				//matrix2
-				mov		edx,  m0
-				movaps	xmm4, [edx]
-				movaps	xmm5, [edx+0x10]
-				movaps	xmm6, [edx+0x20]
-				movaps	xmm7, [edx+0x30]
-			};
-
-			// if w == 1.0f, we don't need to blend.
-			// but since we do the trick of blending the matrices, here, if w != 1.0,
-			// we load Matrix 1 into the other 4 SSE registers and blend both matrices
-			// based on the weight (which we load ingo a 16-byte aligned vector: w,w,w,w)
-
-			if (w != 1.0f) 
-			{
-					F32vec4 weight(w);
-
-				__asm { // do blending of matrices instead of verts and normals -- faster
-					mov		edx,  m1
-					movaps	xmm0, [edx]
-					movaps	xmm1, [edx+0x10]
-					movaps	xmm2, [edx+0x20]
-					movaps	xmm3, [edx+0x30]
-
-					subps	xmm4,  xmm0 // do blend for each matrix column
-					subps	xmm5,  xmm1 // diff, then multiply weight and re-add
-					subps	xmm6,  xmm2
-					subps	xmm7,  xmm3
-
-					mulps   xmm4,  weight
-					mulps   xmm5,  weight
-					mulps   xmm6,  weight
-					mulps   xmm7,  weight
-
-					addps   xmm4,  xmm0
-					addps   xmm5,  xmm1
-					addps   xmm6,  xmm2
-					addps   xmm7,  xmm3
-				};
-			}
-
-			__asm {
-				// save off blended matrix
-				mov		edx,   blend_mat;
-				movaps	[edx], xmm4;
-				movaps	[edx+0x10], xmm5;
-				movaps	[edx+0x20], xmm6;
-				movaps	[edx+0x30], xmm7;
-			}
-		}
-
-		// now, we have either a blended matrix in xmm4-7 or the original Matrix 0
-		// we then multiply each vertex and normal by this one matrix.
-
-		// For SSE2, we would try to keep the original two matrices in other registers
-		// and avoid reloading them. However, they should ramain in L1 cache in the 
-		// current case.
-
-		// One possible optimization would be to sort the vertices by weight instead
-		// of just index (we still want to uniqify). If we note when two or more vertices
-		// share the same weight, we can avoid doing the middle SSE code above and just
-		// re-use the blended matrix for those vertices
-
-
-		// now, we do the actual vertex blending
-		__asm {			
-			// load Vertex into xmm0.
-			movaps	xmm0, [esi] // change aps to ups when input is no longer 16-baligned
-			movaps	xmm1, xmm0  // copy vector into xmm0 through xmm2 (x,y,z)
-			movaps	xmm2, xmm0
-			shufps	xmm0, xmm0, _MM_SHUFFLE(0,0,0,0); // clone vertex (x) across vector
-			shufps	xmm1, xmm1, _MM_SHUFFLE(1,1,1,1); // clone vertex (y) across vector
-			shufps	xmm2, xmm2, _MM_SHUFFLE(2,2,2,2); // same for Z
-			mulps	xmm0, xmm4 // do the actual matrix multipication for r0
-			mulps	xmm1, xmm5 // for r1
-			mulps	xmm2, xmm6 // for r2
-			addps	xmm0, xmm1 // accumulate 
-			addps	xmm0, xmm2 // accumulate
-			addps	xmm0, xmm7 // add in the row 4 which holds the x,y,z translation. assumes w=1 (vertex-w, not weight)
-
-			movaps  [edi], xmm0 // store aligned in output array
-
-			// load Normal into xmm0.
-			movaps	xmm0, [esi + 0x10]  // change aps to ups when input no longer 16-byte aligned
-			movaps	xmm1, xmm0  // 
-			movaps	xmm2, xmm0
-			shufps	xmm0, xmm0, _MM_SHUFFLE(0,0,0,0); // since UV sits between vertex and normal, normal starts at element 1, not 0
-			shufps	xmm1, xmm1, _MM_SHUFFLE(1,1,1,1);
-			shufps	xmm2, xmm2, _MM_SHUFFLE(2,2,2,2);
-			mulps	xmm0, xmm4 // multiply by matrix
-			mulps	xmm1, xmm5 // multiply
-			mulps	xmm2, xmm6 // multiply
-			addps	xmm0, xmm1 // accumulate
-			addps	xmm0, xmm2 // accumulate. note: do not add translation component to normals, save time too
-			movaps  [edi + 0x10], xmm0 // store aligned 
-		}
-
-		*(LLVector2*)(vout + (AVATAR_OFFSET_TEX0/sizeof(F32))) = texcoords[index]; // write texcoord into appropriate spot. 
-	}
-}
-
-#elif LL_LINUX
-
-void blend_SSE_32_32_batch(const int vert_offset, const int vert_count, float* output,
-					 LLStrider<LLVector3>& vertices, LLStrider<LLVector2>& texcoords, LLStrider<LLVector3>& normals, LLStrider<F32>& weights)
-{
-    assert(0);
-}
-
-#elif LL_DARWIN
-// AltiVec versions of the same...
-
-static inline vector float loadAlign(int offset, vector float *addr)
-{
-	vector float in0 = vec_ld(offset, addr);
-	vector float in1 = vec_ld(offset + 16, addr);
-	vector unsigned char perm = vec_lvsl(0, (unsigned char*)addr);
-	
-	return(vec_perm(in0, in1, perm));
-}
-
-static inline void storeAlign(vector float v, int offset, vector float *addr)
-{
-	vector float in0 = vec_ld(offset, addr);
-	vector float in1 = vec_ld(offset + 16, addr);
-	vector unsigned char perm = vec_lvsr(0, (unsigned char *)addr);
-	vector float temp = vec_perm(v, v, perm);
-	vector unsigned char mask = (vector unsigned char)vec_cmpgt(perm, vec_splat_u8(15));
-	
-	in0 = vec_sel(in0, temp, (vector unsigned int)mask);
-	in1 = vec_sel(temp, in1, (vector unsigned int)mask);
-
-	vec_st(in0, offset, addr);
-	vec_st(in1, offset + 16, addr);
-}
-
-void blend_SSE_32_32_batch(const int vert_offset, const int vert_count, float* output,
-					 LLStrider<LLVector3>& vertices, LLStrider<LLVector2>& texcoords, LLStrider<LLVector3>& normals, LLStrider<F32>& weights)
-{
-	F32 last_weight = F32_MAX;
-//	LLMatrix4 &blend_mat = gBlendMat;
-
-	vector float matrix0_0, matrix0_1, matrix0_2, matrix0_3;
-	vector unsigned char out0perm = (vector unsigned char) ( 0x10,0x11,0x12,0x13, 0x14,0x15,0x16,0x17, 0x18,0x19,0x1A,0x1B, 0x0C,0x0D,0x0E,0x0F ); 
-// 	vector unsigned char out1perm = (vector unsigned char) ( 0x00,0x01,0x02,0x03, 0x10,0x11,0x12,0x13, 0x14,0x15,0x16,0x17, 0x18,0x19,0x1A,0x1B ); 
-	vector unsigned char out1perm = (vector unsigned char) ( 0x10,0x11,0x12,0x13, 0x14,0x15,0x16,0x17, 0x18,0x19,0x1A,0x1B, 0x0C,0x0D,0x0E,0x0F ); 
-
-	vector float zero = (vector float)vec_splat_u32(0);
-
-	for (U32 index = vert_offset; index < vert_offset + vert_count; index++)
-	{
-		F32     w  = weights  [index];        // register copy of weight
-		F32  *vin  = &vertices[index].mV[0];  // pointer to input vertex data, assumed to be V3+T2+N3+whatever
-		F32  *vout = output + index * (AVATAR_VERTEX_BYTES/sizeof(F32));    // pointer to the output vertex data, assumed to be 16 byte aligned
-		
-		// MBW -- XXX -- If this isn't the case, this code gets more complicated.
-		if(0x0000000F & (U32)vin)
-		{
-			llerrs << "blend_SSE_batch: input not 16-byte aligned!" << llendl;
-		}
-		if(0x0000000F & (U32)vout)
-		{
-			llerrs << "blend_SSE_batch: output not 16-byte aligned!" << llendl;
-		}
-//		if(0x0000000F & (U32)&(blend_mat.mMatrix))
-//		{
-//			llerrs << "blend_SSE_batch: blend_mat not 16-byte aligned!" << llendl;
-//		}
-		
-		if (w == last_weight)
-		{
-			// load last blended matrix
-			// Still loaded from last time through the loop.
-//			matrix0_0 = vec_ld(0x00, (vector float*)&(blend_mat.mMatrix));
-//			matrix0_1 = vec_ld(0x10, (vector float*)&(blend_mat.mMatrix));
-//			matrix0_2 = vec_ld(0x20, (vector float*)&(blend_mat.mMatrix));
-//			matrix0_3 = vec_ld(0x30, (vector float*)&(blend_mat.mMatrix));
-		}
-		else
-		{
-			last_weight = w;
-			S32 joint = llfloor(w);
-			w -= joint;
-
-			LLMatrix4 &m0 = gJointMat[joint+1];
-			LLMatrix4 &m1 = gJointMat[joint+0];
-
-			// load Matrix 0 into vector registers
-			matrix0_0 = vec_ld(0x00, (vector float*)&(m0.mMatrix));
-			matrix0_1 = vec_ld(0x10, (vector float*)&(m0.mMatrix));
-			matrix0_2 = vec_ld(0x20, (vector float*)&(m0.mMatrix));
-			matrix0_3 = vec_ld(0x30, (vector float*)&(m0.mMatrix));
-
-			// if w == 1.0f, we don't need to blend.
-			// but since we do the trick of blending the matrices, here, if w != 1.0,
-			// we load Matrix 1 into the other 4 SSE registers and blend both matrices
-			// based on the weight (which we load ingo a 16-byte aligned vector: w,w,w,w)
-
-			if (w != 1.0f) 
-			{
-				vector float matrix1_0, matrix1_1, matrix1_2, matrix1_3;
-
-				// This loads the weight somewhere in the vector register
-				vector float weight = vec_lde(0, &(w));
-				// and this splats it to all elements.
-				weight = vec_splat(vec_perm(weight, weight, vec_lvsl(0, &(w))), 0);
-
-				// do blending of matrices instead of verts and normals -- faster
-				matrix1_0 = vec_ld(0x00, (vector float*)&(m1.mMatrix));
-				matrix1_1 = vec_ld(0x10, (vector float*)&(m1.mMatrix));
-				matrix1_2 = vec_ld(0x20, (vector float*)&(m1.mMatrix));
-				matrix1_3 = vec_ld(0x30, (vector float*)&(m1.mMatrix));
-				
-				// m0[col] = ((m0[col] - m1[col]) * weight) + m1[col];
-				matrix0_0 = vec_madd(vec_sub(matrix0_0, matrix1_0), weight, matrix1_0);
-				matrix0_1 = vec_madd(vec_sub(matrix0_1, matrix1_1), weight, matrix1_1);
-				matrix0_2 = vec_madd(vec_sub(matrix0_2, matrix1_2), weight, matrix1_2);
-				matrix0_3 = vec_madd(vec_sub(matrix0_3, matrix1_3), weight, matrix1_3);
-			}
-
-			// save off blended matrix
-//			vec_st(matrix0_0, 0x00, (vector float*)&(blend_mat.mMatrix));
-//			vec_st(matrix0_1, 0x10, (vector float*)&(blend_mat.mMatrix));
-//			vec_st(matrix0_2, 0x20, (vector float*)&(blend_mat.mMatrix));
-//			vec_st(matrix0_3, 0x30, (vector float*)&(blend_mat.mMatrix));
-		}
-
-		// now, we have either a blended matrix in matrix0_0-3 or the original Matrix 0
-		// we then multiply each vertex and normal by this one matrix.
-
-		// For SSE2, we would try to keep the original two matrices in other registers
-		// and avoid reloading them. However, they should ramain in L1 cache in the 
-		// current case.
-
-		// One possible optimization would be to sort the vertices by weight instead
-		// of just index (we still want to uniqify). If we note when two or more vertices
-		// share the same weight, we can avoid doing the middle SSE code above and just
-		// re-use the blended matrix for those vertices
-
-
-		// now, we do the actual vertex blending
-
-		vector float in0 = vec_ld(AVATAR_OFFSET_POS, (vector float*)vin);
-		vector float in1 = vec_ld(AVATAR_OFFSET_NORMAL, (vector float*)vin);
-	
-		// Matrix multiply vertex
-		vector float out0 = vec_madd
-		(
-			vec_splat(in0, 0), 
-			matrix0_0, 
-			vec_madd
-			(
-				vec_splat(in0, 1),
-				matrix0_1,
-				vec_madd
-				(
-					vec_splat(in0, 2),
-					matrix0_2,
-					matrix0_3
-				)
-			)
-		);
-		
-		// Matrix multiply normal
-		vector float out1 = vec_madd
-		(
-			vec_splat(in1, 0), 
-			matrix0_0, 
-			vec_madd
-			(
-				vec_splat(in1, 1),
-				matrix0_1,
-				vec_madd
-				(
-					vec_splat(in1, 2),
-					matrix0_2,
-					// no translation for normals
-					(vector float)vec_splat_u32(0)
-				)
-			)
-		);
-
-		// indexed store
-		vec_stl(vec_perm(in0, out0, out0perm), AVATAR_OFFSET_POS,    (vector float*)vout); // Pos
-		vec_stl(vec_perm(in1, out1, out1perm), AVATAR_OFFSET_NORMAL, (vector float*)vout); // Norm
-		*(LLVector2*)(vout + (AVATAR_OFFSET_TEX0/sizeof(F32))) = texcoords[index]; // write texcoord into appropriate spot. 
-	}
-}
-
-#endif
-
-
-void llDrawElementsBatchBlend(const U32 vert_offset, const U32 vert_count, LLFace *face, const S32 index_count, const U32 *indices)
-{
-	U8* gAGPVertices = gPipeline.bufferGetScratchMemory();
-	
-	if (gAGPVertices)
-	{
-		LLStrider<LLVector3> vertices;
-		LLStrider<LLVector3> normals; 
-		LLStrider<LLVector2> tcoords0;
-		LLStrider<F32>       weights; 
-
-		LLStrider<LLVector3> o_vertices;
-		LLStrider<LLVector3> o_normals;
-		LLStrider<LLVector2> o_texcoords0;
-
-		
-		LLStrider<LLVector3> binormals; 
-		LLStrider<LLVector2> o_texcoords1;
-		// get the source vertices from the draw pool. We index these ourselves, as there was
-		// no guarantee the indices for a single jointmesh were contigious
-		
-		LLDrawPool *pool = face->getPool();
-		pool->getVertexStrider      (vertices,  0);
-		pool->getTexCoordStrider   (tcoords0,  0, 0);
-		pool->getNormalStrider      (normals,   0);
-		pool->getBinormalStrider    (binormals, 0);
-		pool->getVertexWeightStrider(weights,   0);
-
-		// load the addresses of the output striders
-		o_vertices  = (LLVector3*)(gAGPVertices + AVATAR_OFFSET_POS);		o_vertices.setStride(  AVATAR_VERTEX_BYTES);
-		o_normals   = (LLVector3*)(gAGPVertices + AVATAR_OFFSET_NORMAL);	o_normals.setStride(   AVATAR_VERTEX_BYTES);
-		o_texcoords0= (LLVector2*)(gAGPVertices + AVATAR_OFFSET_TEX0);		o_texcoords0.setStride(AVATAR_VERTEX_BYTES);
-		o_texcoords1= (LLVector2*)(gAGPVertices + AVATAR_OFFSET_TEX1);		o_texcoords1.setStride(AVATAR_VERTEX_BYTES);
-
-#if !LL_LINUX // !!! *TODO: do the linux implementation
-		if (gGLManager.mSoftwareBlendSSE)
-		{
-			// do SSE blend without binormals or extra texcoords
-			blend_SSE_32_32_batch(vert_offset, vert_count, (float*)gAGPVertices,
-								  vertices, tcoords0, normals, weights);
-		}
-		else // fully backwards compatible software blending, no SSE
-#endif
-		{
-			LLVector4 tpos0, tnorm0, tpos1, tnorm1, tbinorm0, tbinorm1;
-			F32 last_weight = F32_MAX;
-			LLMatrix3 gBlendRotMat;
-
-			{
-				for (U32 index=vert_offset; index < vert_offset + vert_count; index++)
-				{
-					// blend by first matrix
-					F32     w = weights [index];
-					
-					if (w != last_weight)
-					{
-						last_weight = w;
-
-						S32 joint = llfloor(w);
-						w -= joint;
-
-						LLMatrix4 &m0 = gJointMat[joint+1];
-						LLMatrix4 &m1 = gJointMat[joint+0];
-						LLMatrix3 &n0 = gJointRot[joint+1];
-						LLMatrix3 &n1 = gJointRot[joint+0];
-
-						if (w == 1.0f)
-						{
-							gBlendMat = m0;
-							gBlendRotMat = n0;
-						}	
-						else
-						{
-							gBlendMat.mMatrix[VX][VX] = lerp(m1.mMatrix[VX][VX], m0.mMatrix[VX][VX], w);
-							gBlendMat.mMatrix[VX][VY] = lerp(m1.mMatrix[VX][VY], m0.mMatrix[VX][VY], w);
-							gBlendMat.mMatrix[VX][VZ] = lerp(m1.mMatrix[VX][VZ], m0.mMatrix[VX][VZ], w);
-
-							gBlendMat.mMatrix[VY][VX] = lerp(m1.mMatrix[VY][VX], m0.mMatrix[VY][VX], w);
-							gBlendMat.mMatrix[VY][VY] = lerp(m1.mMatrix[VY][VY], m0.mMatrix[VY][VY], w);
-							gBlendMat.mMatrix[VY][VZ] = lerp(m1.mMatrix[VY][VZ], m0.mMatrix[VY][VZ], w);
-
-							gBlendMat.mMatrix[VZ][VX] = lerp(m1.mMatrix[VZ][VX], m0.mMatrix[VZ][VX], w);
-							gBlendMat.mMatrix[VZ][VY] = lerp(m1.mMatrix[VZ][VY], m0.mMatrix[VZ][VY], w);
-							gBlendMat.mMatrix[VZ][VZ] = lerp(m1.mMatrix[VZ][VZ], m0.mMatrix[VZ][VZ], w);
-
-							gBlendMat.mMatrix[VW][VX] = lerp(m1.mMatrix[VW][VX], m0.mMatrix[VW][VX], w);
-							gBlendMat.mMatrix[VW][VY] = lerp(m1.mMatrix[VW][VY], m0.mMatrix[VW][VY], w);
-							gBlendMat.mMatrix[VW][VZ] = lerp(m1.mMatrix[VW][VZ], m0.mMatrix[VW][VZ], w);
-
-							gBlendRotMat.mMatrix[VX][VX] = lerp(n1.mMatrix[VX][VX], n0.mMatrix[VX][VX], w);
-							gBlendRotMat.mMatrix[VX][VY] = lerp(n1.mMatrix[VX][VY], n0.mMatrix[VX][VY], w);
-							gBlendRotMat.mMatrix[VX][VZ] = lerp(n1.mMatrix[VX][VZ], n0.mMatrix[VX][VZ], w);
-
-							gBlendRotMat.mMatrix[VY][VX] = lerp(n1.mMatrix[VY][VX], n0.mMatrix[VY][VX], w);
-							gBlendRotMat.mMatrix[VY][VY] = lerp(n1.mMatrix[VY][VY], n0.mMatrix[VY][VY], w);
-							gBlendRotMat.mMatrix[VY][VZ] = lerp(n1.mMatrix[VY][VZ], n0.mMatrix[VY][VZ], w);
-
-							gBlendRotMat.mMatrix[VZ][VX] = lerp(n1.mMatrix[VZ][VX], n0.mMatrix[VZ][VX], w);
-							gBlendRotMat.mMatrix[VZ][VY] = lerp(n1.mMatrix[VZ][VY], n0.mMatrix[VZ][VY], w);
-							gBlendRotMat.mMatrix[VZ][VZ] = lerp(n1.mMatrix[VZ][VZ], n0.mMatrix[VZ][VZ], w);
-						}
-					}
-
-					// write result
-					o_vertices  [index]     = vertices[index] * gBlendMat;
-					o_normals   [index]     = normals [index] * gBlendRotMat;
-					o_texcoords0[index]		= tcoords0[index];
-
-					/*
-					// Verification code.  Leave this here.  It's useful for keeping the SSE and non-SSE versions in sync.
-					LLVector3 temp;
-					temp = tpos0;
-					if( (o_vertices[index] - temp).magVecSquared() > 0.001f )
-					{
-						llerrs << "V SSE: " << o_vertices[index] << " v. " << temp << llendl;
-					}
-	
-					temp = tnorm0;
-					if( (o_normals[index] - temp).magVecSquared() > 0.001f )
-					{
-						llerrs << "N SSE: " << o_normals[index] << " v. " << temp << llendl;
-					}
-	
-					if( (o_texcoords0[index] - tcoords0[index]).magVecSquared() > 0.001f )
-					{
-						llerrs << "T0 SSE: " << o_texcoords0[index] << " v. " << tcoords0[index] << llendl;
-					}
-					*/
-				}
-			}
-		}
-
-#if LL_DARWIN
-		// *HACK* *CHOKE* *PUKE*
-		// No way does this belong here.
-		glFlushVertexArrayRangeAPPLE(AVATAR_VERTEX_BYTES * vert_count, gAGPVertices + (AVATAR_VERTEX_BYTES * vert_offset));
-#endif
-		glDrawElements(GL_TRIANGLES, index_count, GL_UNSIGNED_INT, indices); // draw it!
+		glDrawElements(mode,count,type,indices);
 	}
 	else
 	{
-		glDrawElements(GL_TRIANGLES, index_count, GL_UNSIGNED_INT, indices);
+		glDrawRangeElements(mode,start,end,count,type,indices);
 	}
 }
 
-
-
-//--------------------------------------------------------------------
-// DrawElements
-
-// works just like glDrawElements, except it assumes GL_TRIANGLES and GL_UNSIGNED_INT indices
-
-// why? because the destination buffer may not be the AGP buffer and the eyes do not use blending
-// separate the eyes into their own drawpools and this code goes away.
-
-//--------------------------------------------------------------------
-
-void llDrawElements(const S32 count, const U32 *indices, LLFace *face)
-{
-	U8* gAGPVertices = gPipeline.bufferGetScratchMemory();
-	
-	if (gAGPVertices)
-	{
-#if LL_DARWIN
-		U32   minIndex = indices[0];
-		U32   maxIndex = indices[0];
-#endif
-		{
-			LLStrider<LLVector3> vertices;
-			LLStrider<LLVector3> normals; 
-			LLStrider<LLVector2> tcoords;
-			LLStrider<F32>       weights; 
-
-			LLStrider<LLVector3> o_vertices;
-			LLStrider<LLVector3> o_normals;
-			LLStrider<LLVector2> o_texcoords0;
-
-			LLDrawPool *pool = face->getPool();
-			pool->getVertexStrider      (vertices,0);
-			pool->getNormalStrider      (normals, 0);
-			pool->getTexCoordStrider    (tcoords, 0);
-
-			o_vertices  = (LLVector3*)(gAGPVertices + AVATAR_OFFSET_POS);		o_vertices.setStride(  AVATAR_VERTEX_BYTES);
-			o_normals   = (LLVector3*)(gAGPVertices + AVATAR_OFFSET_NORMAL);	o_normals.setStride(   AVATAR_VERTEX_BYTES);
-			o_texcoords0= (LLVector2*)(gAGPVertices + AVATAR_OFFSET_TEX0);		o_texcoords0.setStride(AVATAR_VERTEX_BYTES);
-
-			for (S32 i=0; i < count; i++)
-			{
-				U32   index = indices[i];
-
-				o_vertices  [index] = vertices[index];
-				o_normals   [index] = normals [index];
-				o_texcoords0[index] = tcoords [index];
-
-#if LL_DARWIN
-				maxIndex = llmax(index, maxIndex);
-				minIndex = llmin(index, minIndex);
-#endif
-			}
-		}
-
-#if LL_DARWIN
-		// *HACK* *CHOKE* *PUKE*
-		// No way does this belong here.
-		glFlushVertexArrayRangeAPPLE(AVATAR_VERTEX_BYTES * (maxIndex + 1 - minIndex), gAGPVertices + (AVATAR_VERTEX_BYTES * minIndex));
-#endif
-
-		glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_INT, indices);
-	}
-	else
-	{
-		glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_INT, indices);
-	}
-}
-
-
 //--------------------------------------------------------------------
 // LLViewerJointMesh::drawShape()
 //--------------------------------------------------------------------
-U32 LLViewerJointMesh::drawShape( F32 pixelArea )
+U32 LLViewerJointMesh::drawShape( F32 pixelArea, BOOL first_pass)
 {
-	if (!mValid || !mVisible) return 0;
-
-	U32 triangle_count = 0;
-
-	//----------------------------------------------------------------
-	// if no mesh bail out now
-	//----------------------------------------------------------------
-	if ( !mMesh || !mFace)
+	if (!mValid || !mMesh || !mFace || !mVisible || 
+		mFace->mVertexBuffer.isNull() ||
+		mMesh->getNumFaces() == 0) 
 	{
 		return 0;
 	}
 
-	//----------------------------------------------------------------
-	// if we have no faces, bail out now
-	//----------------------------------------------------------------
-	if ( mMesh->getNumFaces() == 0 )
-	{
-		return 0;
-	}
+	U32 triangle_count = 0;
 
 	stop_glerror();
 	
 	//----------------------------------------------------------------
 	// setup current color
 	//----------------------------------------------------------------
-	if (gRenderForSelect)
-	{
-		S32 name = mFace->getDrawable() ? mFace->getDrawable()->getVObj()->mGLName : 0;
-		LLColor4U color((U8)(name >> 16), (U8)(name >> 8), (U8)name, 0xff);
-		LLColor4 color_float(color);
-	
-		glColor4f(color_float.mV[0], color_float.mV[1], color_float.mV[2], 1.f);
-	}
-	else
+	if (!gRenderForSelect)
 	{
 		if ((mFace->getPool()->getVertexShaderLevel() > 0))
 		{
@@ -1150,7 +572,6 @@ U32 LLViewerJointMesh::drawShape( F32 pixelArea )
 
 	stop_glerror();
 	
-// 	LLGLSSpecular specular(mSpecular, gRenderForSelect ? 0.0f : mShiny);
 	LLGLSSpecular specular(LLColor4(1.f,1.f,1.f,1.f), gRenderForSelect ? 0.0f : mShiny && !(mFace->getPool()->getVertexShaderLevel() > 0));
 
 	LLGLEnable texture_2d((gRenderForSelect && isTransparent()) ? GL_TEXTURE_2D : 0);
@@ -1160,11 +581,6 @@ U32 LLViewerJointMesh::drawShape( F32 pixelArea )
 	//----------------------------------------------------------------
 	llassert( !(mTexture.notNull() && mLayerSet) );  // mutually exclusive
 
-	//GLuint test_image_name = 0;
-
-	// 
-	LLGLState force_alpha_test(GL_ALPHA_TEST, isTransparent());
-
 	if (mTestImageName)
 	{
 		LLImageGL::bindExternalTexture( mTestImageName, 0, GL_TEXTURE_2D ); 
@@ -1217,11 +633,12 @@ U32 LLViewerJointMesh::drawShape( F32 pixelArea )
 		gImageList.getImage(IMG_DEFAULT_AVATAR)->bind();
 	}
 	
+	LLGLDisable tex(gRenderForSelect && !isTransparent() ? GL_TEXTURE_2D : 0);
+
 	if (gRenderForSelect)
 	{
 		if (isTransparent())
 		{
-			//gGLSObjectSelectDepthAlpha.set();
 			glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,		GL_COMBINE_ARB);
 			glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB,		GL_REPLACE);
 			glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB,		GL_MODULATE);
@@ -1232,19 +649,14 @@ U32 LLViewerJointMesh::drawShape( F32 pixelArea )
 			glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB,		GL_TEXTURE);  // GL_TEXTURE_ENV_COLOR is set in renderPass1
 			glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB,	GL_SRC_ALPHA);
 		}
-		else
-		{
-			//gGLSObjectSelectDepth.set();
-		}
 	}
 	else
 	{
 		//----------------------------------------------------------------
 		// by default, backface culling is enabled
 		//----------------------------------------------------------------
-		if (sRenderPass == AVATAR_RENDER_PASS_CLOTHING_INNER)
+		/*if (sRenderPass == AVATAR_RENDER_PASS_CLOTHING_INNER)
 		{
-			//LLGLSPipelineAvatar gls_pipeline_avatar;
 			LLImageGL::bindExternalTexture( sClothingMaskImageName, 1, GL_TEXTURE_2D );
 
 			glClientActiveTextureARB(GL_TEXTURE0_ARB);
@@ -1284,7 +696,6 @@ U32 LLViewerJointMesh::drawShape( F32 pixelArea )
 		}
 		else if (sRenderPass == AVATAR_RENDER_PASS_CLOTHING_OUTER)
 		{
-			//gGLSPipelineAvatarAlphaPass1.set();
 			glAlphaFunc(GL_GREATER, 0.1f);
 			LLImageGL::bindExternalTexture( sClothingMaskImageName, 1, GL_TEXTURE_2D );
 
@@ -1315,81 +726,48 @@ U32 LLViewerJointMesh::drawShape( F32 pixelArea )
 
 			glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB,		GL_TEXTURE);
 			glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB,	GL_SRC_ALPHA);
-		}
-		else if ( isTransparent())
-		{
-			//gGLSNoCullFaces.set();
-		}
-		else
-		{
-			//gGLSCullFaces.set();
-		}
+		}*/
 	}
 
-	if (mMesh->hasWeights())
-	{
-		uploadJointMatrices();
+	mFace->mVertexBuffer->setBuffer(sRenderMask);
 
+	U32 start = mMesh->mFaceVertexOffset;
+	U32 end = start + mMesh->mFaceVertexCount - 1;
+	U32 count = mMesh->mFaceIndexCount;
+	U32* indicesp = ((U32*) mFace->mVertexBuffer->getIndicesPointer()) + mMesh->mFaceIndexOffset;
 
+	if (mMesh->hasWeights())
+	{
 		if ((mFace->getPool()->getVertexShaderLevel() > 0))
 		{
-			glMatrixMode(GL_MODELVIEW);
-			glPushMatrix();
-			glLoadIdentity();
-
-			glDrawElements(GL_TRIANGLES, mMesh->mFaceIndexCount, GL_UNSIGNED_INT, mMesh->getIndices());
-
-			glPopMatrix();
+			if (first_pass)
+			{
+				uploadJointMatrices();
+			}
+			llDrawRangeElements(GL_TRIANGLES, start, end, count, GL_UNSIGNED_INT, indicesp);
 		}
 		else
 		{
-			if (mFace->getGeomIndex() < 0)
-			{
-				llerrs << "Invalid geometry index in LLViewerJointMesh::drawShape() " << mFace->getGeomIndex() << llendl;
-			}
-
-			if ((S32)(mMesh->mFaceVertexOffset + mMesh->mFaceVertexCount) > mFace->getGeomCount())
-			{
-				((LLVOAvatar*)mFace->getDrawable()->getVObj())->mRoot.dump();
-				llerrs << "Rendering outside of vertex bounds with mesh " << mName << " at pixel area " << pixelArea << llendl;
-			}
-			llDrawElementsBatchBlend(mMesh->mFaceVertexOffset, mMesh->mFaceVertexCount,
-									 mFace, mMesh->mFaceIndexCount, mMesh->getIndices());
+			llDrawRangeElements(GL_TRIANGLES, start, end, count, GL_UNSIGNED_INT, indicesp);
 		}
-
 	}
 	else
 	{
 		glPushMatrix();
 		LLMatrix4 jointToWorld = getWorldMatrix();
-		jointToWorld *= gCamera->getModelview();
-		glLoadMatrixf((GLfloat*)jointToWorld.mMatrix);
-
-		if ((mFace->getPool()->getVertexShaderLevel() > 0))
-		{
-			glDrawElements(GL_TRIANGLES, mMesh->mFaceIndexCount, GL_UNSIGNED_INT, mMesh->getIndices());
-		}
-		else // this else clause handles non-weighted vertices. llDrawElements just copies and draws
-		{
-			llDrawElements(mMesh->mFaceIndexCount, mMesh->getIndices(), mFace);
-		}
-		
+		glMultMatrixf((GLfloat*)jointToWorld.mMatrix);
+		llDrawRangeElements(GL_TRIANGLES, start, end, count, GL_UNSIGNED_INT, indicesp);
 		glPopMatrix();
 	}
 
 	triangle_count += mMesh->mFaceIndexCount;
-
-	if (gRenderForSelect)
-	{
-		glColor4fv(mColor.mV);
-	}
-
+	
 	if (mTestImageName)
 	{
 		glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
 	}
 
-	if (sRenderPass != AVATAR_RENDER_PASS_SINGLE)
+	/*if (sRenderPass != AVATAR_RENDER_PASS_SINGLE)
 	{
 		LLImageGL::unbindTexture(1, GL_TEXTURE_2D);
 		glActiveTextureARB(GL_TEXTURE1_ARB);
@@ -1402,7 +780,7 @@ U32 LLViewerJointMesh::drawShape( F32 pixelArea )
 
 		glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB,		GL_MODULATE);
 		glAlphaFunc(GL_GREATER, 0.01f);
-	}
+	}*/
 
 	if (mTexture.notNull()) {
 		if (!mTexture->getClampS()) {
@@ -1419,19 +797,20 @@ U32 LLViewerJointMesh::drawShape( F32 pixelArea )
 //-----------------------------------------------------------------------------
 // updateFaceSizes()
 //-----------------------------------------------------------------------------
-void LLViewerJointMesh::updateFaceSizes(U32 &num_vertices, F32 pixel_area)
+void LLViewerJointMesh::updateFaceSizes(U32 &num_vertices, U32& num_indices, F32 pixel_area)
 {
 	// Do a pre-alloc pass to determine sizes of data.
 	if (mMesh && mValid)
 	{
 		mMesh->mFaceVertexOffset = num_vertices;
 		mMesh->mFaceVertexCount = mMesh->getNumVertices();
+		mMesh->mFaceIndexOffset = num_indices;
+		mMesh->mFaceIndexCount = mMesh->getSharedData()->mNumTriangleIndices;
+
 		mMesh->getReferenceMesh()->mCurVertexCount = mMesh->mFaceVertexCount;
-		num_vertices += mMesh->getNumVertices();
 
-		mMesh->mFaceIndexCount = mMesh->getSharedData()->mNumTriangleIndices;
-	
-		mMesh->getSharedData()->genIndices(mMesh->mFaceVertexOffset);
+		num_vertices += mMesh->getNumVertices();
+		num_indices += mMesh->mFaceIndexCount;
 	}
 }
 
@@ -1441,9 +820,7 @@ void LLViewerJointMesh::updateFaceSizes(U32 &num_vertices, F32 pixel_area)
 void LLViewerJointMesh::updateFaceData(LLFace *face, F32 pixel_area, BOOL damp_wind)
 {
 	U32 i;
-
-	if (!mValid) return;
-
+	
 	mFace = face;
 
 	LLStrider<LLVector3> verticesp;
@@ -1452,13 +829,15 @@ void LLViewerJointMesh::updateFaceData(LLFace *face, F32 pixel_area, BOOL damp_w
 	LLStrider<LLVector2> tex_coordsp;
 	LLStrider<F32>		 vertex_weightsp;
 	LLStrider<LLVector4> clothing_weightsp;
+	LLStrider<U32> indicesp;
 
 	// Copy data into the faces from the polymesh data.
-	if (mMesh)
+	if (mMesh && mValid)
 	{
 		if (mMesh->getNumVertices())
 		{
 			S32 index = face->getGeometryAvatar(verticesp, normalsp, binormalsp, tex_coordsp, vertex_weightsp, clothing_weightsp);
+			face->mVertexBuffer->getIndexStrider(indicesp);
 
 			if (-1 == index)
 			{
@@ -1474,11 +853,20 @@ void LLViewerJointMesh::updateFaceData(LLFace *face, F32 pixel_area, BOOL damp_w
 				vertex_weightsp[mMesh->mFaceVertexOffset + i] = *(mMesh->getWeights() + i);
 				if (damp_wind)
 				{
-					clothing_weightsp[mMesh->mFaceVertexOffset + i].setVec(0,0,0,0);
+					clothing_weightsp[mMesh->mFaceVertexOffset + i] = LLVector4(0,0,0,0);
 				}
 				else
 				{
-					clothing_weightsp[mMesh->mFaceVertexOffset + i].setVec(*(mMesh->getClothingWeights() + i));
+					clothing_weightsp[mMesh->mFaceVertexOffset + i] = (*(mMesh->getClothingWeights() + i));
+				}
+			}
+
+			for (S32 i = 0; i < mMesh->getNumFaces(); i++)
+			{
+				for (U32 j = 0; j < 3; j++)
+				{
+					U32 k = i*3+j+mMesh->mFaceIndexOffset;
+					indicesp[k] = mMesh->getFaces()[i][j] + mMesh->mFaceVertexOffset;
 				}
 			}
 		}
@@ -1495,6 +883,92 @@ BOOL LLViewerJointMesh::updateLOD(F32 pixel_area, BOOL activate)
 	return (valid != activate);
 }
 
+void LLViewerJointMesh::updateGeometry()
+{
+	if (mValid && mMesh && mFace &&
+		mMesh->hasWeights() &&
+		mFace->mVertexBuffer.notNull() &&
+		gPipeline.getVertexShaderLevel(LLPipeline::SHADER_AVATAR) == 0)
+	{
+		uploadJointMatrices();
+		LLStrider<LLVector3> o_vertices;
+		LLStrider<LLVector3> o_normals;
+
+		//get vertex and normal striders
+		LLVertexBuffer *buffer = mFace->mVertexBuffer;
+		buffer->getVertexStrider(o_vertices,  0);
+		buffer->getNormalStrider(o_normals,   0);
+
+		{
+			LLVector4 tpos0, tnorm0, tpos1, tnorm1, tbinorm0, tbinorm1;
+			F32 last_weight = F32_MAX;
+			LLMatrix3 gBlendRotMat;
+
+		
+			for (U32 index= 0; index < mMesh->getNumVertices(); index++)
+			{
+				// blend by first matrix
+				F32 w = mMesh->getWeights()[index]; 
+				
+				if (w != last_weight)
+				{
+					last_weight = w;
+
+					S32 joint = llfloor(w);
+					w -= joint;
+
+					LLMatrix4 &m0 = gJointMat[joint+1];
+					LLMatrix4 &m1 = gJointMat[joint+0];
+					LLMatrix3 &n0 = gJointRot[joint+1];
+					LLMatrix3 &n1 = gJointRot[joint+0];
+
+					if (w == 1.0f)
+					{
+						gBlendMat = m0;
+						gBlendRotMat = n0;
+					}	
+					else
+					{
+						gBlendMat.mMatrix[VX][VX] = lerp(m1.mMatrix[VX][VX], m0.mMatrix[VX][VX], w);
+						gBlendMat.mMatrix[VX][VY] = lerp(m1.mMatrix[VX][VY], m0.mMatrix[VX][VY], w);
+						gBlendMat.mMatrix[VX][VZ] = lerp(m1.mMatrix[VX][VZ], m0.mMatrix[VX][VZ], w);
+
+						gBlendMat.mMatrix[VY][VX] = lerp(m1.mMatrix[VY][VX], m0.mMatrix[VY][VX], w);
+						gBlendMat.mMatrix[VY][VY] = lerp(m1.mMatrix[VY][VY], m0.mMatrix[VY][VY], w);
+						gBlendMat.mMatrix[VY][VZ] = lerp(m1.mMatrix[VY][VZ], m0.mMatrix[VY][VZ], w);
+
+						gBlendMat.mMatrix[VZ][VX] = lerp(m1.mMatrix[VZ][VX], m0.mMatrix[VZ][VX], w);
+						gBlendMat.mMatrix[VZ][VY] = lerp(m1.mMatrix[VZ][VY], m0.mMatrix[VZ][VY], w);
+						gBlendMat.mMatrix[VZ][VZ] = lerp(m1.mMatrix[VZ][VZ], m0.mMatrix[VZ][VZ], w);
+
+						gBlendMat.mMatrix[VW][VX] = lerp(m1.mMatrix[VW][VX], m0.mMatrix[VW][VX], w);
+						gBlendMat.mMatrix[VW][VY] = lerp(m1.mMatrix[VW][VY], m0.mMatrix[VW][VY], w);
+						gBlendMat.mMatrix[VW][VZ] = lerp(m1.mMatrix[VW][VZ], m0.mMatrix[VW][VZ], w);
+
+						gBlendRotMat.mMatrix[VX][VX] = lerp(n1.mMatrix[VX][VX], n0.mMatrix[VX][VX], w);
+						gBlendRotMat.mMatrix[VX][VY] = lerp(n1.mMatrix[VX][VY], n0.mMatrix[VX][VY], w);
+						gBlendRotMat.mMatrix[VX][VZ] = lerp(n1.mMatrix[VX][VZ], n0.mMatrix[VX][VZ], w);
+
+						gBlendRotMat.mMatrix[VY][VX] = lerp(n1.mMatrix[VY][VX], n0.mMatrix[VY][VX], w);
+						gBlendRotMat.mMatrix[VY][VY] = lerp(n1.mMatrix[VY][VY], n0.mMatrix[VY][VY], w);
+						gBlendRotMat.mMatrix[VY][VZ] = lerp(n1.mMatrix[VY][VZ], n0.mMatrix[VY][VZ], w);
+
+						gBlendRotMat.mMatrix[VZ][VX] = lerp(n1.mMatrix[VZ][VX], n0.mMatrix[VZ][VX], w);
+						gBlendRotMat.mMatrix[VZ][VY] = lerp(n1.mMatrix[VZ][VY], n0.mMatrix[VZ][VY], w);
+						gBlendRotMat.mMatrix[VZ][VZ] = lerp(n1.mMatrix[VZ][VZ], n0.mMatrix[VZ][VZ], w);
+					}
+				}
+
+				// write result
+				U32 bidx = index + mMesh->mFaceVertexOffset;
+
+				o_vertices[bidx] = mMesh->getCoords()[index] * gBlendMat;
+				o_normals[bidx] = mMesh->getNormals()[index] * gBlendRotMat;
+			}
+		}
+	}
+}
+
 void LLViewerJointMesh::dump()
 {
 	if (mValid)
diff --git a/indra/newview/llviewerjointmesh.h b/indra/newview/llviewerjointmesh.h
index 963ad05595b54bc5e2fbeb9356b9744012502b27..b6fd8afcdb5e25c7b9dffa5e8aa45a873d458dca 100644
--- a/indra/newview/llviewerjointmesh.h
+++ b/indra/newview/llviewerjointmesh.h
@@ -114,11 +114,12 @@ public:
 	// overloaded from base class
 	/*virtual*/ void drawBone();
 	/*virtual*/ BOOL isTransparent();
-	/*virtual*/ U32 drawShape( F32 pixelArea );
+	/*virtual*/ U32 drawShape( F32 pixelArea, BOOL first_pass );
 
-	/*virtual*/ void updateFaceSizes(U32 &num_vertices, F32 pixel_area);
+	/*virtual*/ void updateFaceSizes(U32 &num_vertices, U32& num_indices, F32 pixel_area);
 	/*virtual*/ void updateFaceData(LLFace *face, F32 pixel_area, BOOL damp_wind = FALSE);
 	/*virtual*/ BOOL updateLOD(F32 pixel_area, BOOL activate);
+	/*virtual*/ void updateGeometry();
 	/*virtual*/ void dump();
 
 	void setIsTransparent(BOOL is_transparent) { mIsTransparent = is_transparent; }
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index f6873c61b8f0c759a092e436f31ba0d30dd3b616..e9e98a491614d530957a36b548ed256407e10ada 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -57,7 +57,6 @@
 #include "lldir.h"
 #include "lldrawable.h"
 #include "lldrawpoolalpha.h"
-#include "lldrawpoolhud.h"
 #include "lldrawpooltree.h"
 #include "llface.h"
 #include "llfirstuse.h"
@@ -263,7 +262,6 @@ typedef LLMemberListener<LLView> view_listener_t;
 //
 // Local prototypes
 //
-BOOL enable_attach(void*);
 void handle_leave_group(void *);
 
 // File Menu
@@ -335,9 +333,6 @@ BOOL check_toggle_hacked_godmode(void*);
 void toggle_glow(void *);
 BOOL check_glow(void *);
 
-void toggle_vbo(void *);
-BOOL check_vbo(void *);
-
 void toggle_vertex_shaders(void *);
 BOOL check_vertex_shaders(void *);
 
@@ -758,6 +753,12 @@ void init_client_menu(LLMenuGL* menu)
 	menu->append(new LLMenuItemToggleGL("Quiet Snapshots to Disk",
 										&gQuietSnapshot));
 
+	menu->append(new LLMenuItemCheckGL( "Compress Snapshots to Disk",
+										&menu_toggle_control,
+										NULL,
+										&menu_check_control,
+										(void*)"CompressSnapshotsToDisk"));
+
 	menu->append(new LLMenuItemCheckGL("Show Mouselook Crosshairs",
 									   &menu_toggle_control,
 									   NULL,
@@ -880,6 +881,11 @@ void init_client_menu(LLMenuGL* menu)
 	menu->append(new LLMenuItemToggleGL("Disable Camera Constraints", 
 		&LLViewerCamera::sDisableCameraConstraints));
 
+	menu->append(new LLMenuItemCheckGL("Mouse Smoothing",
+										&menu_toggle_control,
+										NULL,
+										&menu_check_control,
+										(void*) "MouseSmooth"));
 	menu->appendSeparator();
 
 	menu->append(new LLMenuItemCheckGL( "Console Window", 
@@ -1008,56 +1014,56 @@ void init_debug_rendering_menu(LLMenuGL* menu)
 	menu->appendMenu(sub_menu);
 
 	sub_menu->append(new LLMenuItemCheckGL("Simple",
-											&LLPipeline::toggleRenderType, NULL,
-											&LLPipeline::toggleRenderTypeControl,
+											&LLPipeline::toggleRenderTypeControl, NULL,
+											&LLPipeline::hasRenderTypeControl,
 											(void*)LLPipeline::RENDER_TYPE_SIMPLE,	'1', MASK_CONTROL|MASK_ALT|MASK_SHIFT));
 	sub_menu->append(new LLMenuItemCheckGL("Alpha",
-											&LLPipeline::toggleRenderType, NULL,
-											&LLPipeline::toggleRenderTypeControl,
+											&LLPipeline::toggleRenderTypeControl, NULL,
+											&LLPipeline::hasRenderTypeControl,
 											(void*)LLPipeline::RENDER_TYPE_ALPHA, '2', MASK_CONTROL|MASK_ALT|MASK_SHIFT));
 	sub_menu->append(new LLMenuItemCheckGL("Tree",
-											&LLPipeline::toggleRenderType, NULL,
-											&LLPipeline::toggleRenderTypeControl,
+											&LLPipeline::toggleRenderTypeControl, NULL,
+											&LLPipeline::hasRenderTypeControl,
 											(void*)LLPipeline::RENDER_TYPE_TREE, '3', MASK_CONTROL|MASK_ALT|MASK_SHIFT));
 	sub_menu->append(new LLMenuItemCheckGL("Character",
-											&LLPipeline::toggleRenderType, NULL,
-											&LLPipeline::toggleRenderTypeControl,
+											&LLPipeline::toggleRenderTypeControl, NULL,
+											&LLPipeline::hasRenderTypeControl,
 											(void*)LLPipeline::RENDER_TYPE_AVATAR, '4', MASK_CONTROL|MASK_ALT|MASK_SHIFT));
 	sub_menu->append(new LLMenuItemCheckGL("SurfacePatch",
-											&LLPipeline::toggleRenderType, NULL,
-											&LLPipeline::toggleRenderTypeControl,
+											&LLPipeline::toggleRenderTypeControl, NULL,
+											&LLPipeline::hasRenderTypeControl,
 											(void*)LLPipeline::RENDER_TYPE_TERRAIN, '5', MASK_CONTROL|MASK_ALT|MASK_SHIFT));
 	sub_menu->append(new LLMenuItemCheckGL("Sky",
-											&LLPipeline::toggleRenderType, NULL,
-											&LLPipeline::toggleRenderTypeControl,
+											&LLPipeline::toggleRenderTypeControl, NULL,
+											&LLPipeline::hasRenderTypeControl,
 											(void*)LLPipeline::RENDER_TYPE_SKY, '6', MASK_CONTROL|MASK_ALT|MASK_SHIFT));
 	sub_menu->append(new LLMenuItemCheckGL("Water",
-											&LLPipeline::toggleRenderType, NULL,
-											&LLPipeline::toggleRenderTypeControl,
+											&LLPipeline::toggleRenderTypeControl, NULL,
+											&LLPipeline::hasRenderTypeControl,
 											(void*)LLPipeline::RENDER_TYPE_WATER, '7', MASK_CONTROL|MASK_ALT|MASK_SHIFT));
 	sub_menu->append(new LLMenuItemCheckGL("Ground",
-											&LLPipeline::toggleRenderType, NULL,
-											&LLPipeline::toggleRenderTypeControl,
+											&LLPipeline::toggleRenderTypeControl, NULL,
+											&LLPipeline::hasRenderTypeControl,
 											(void*)LLPipeline::RENDER_TYPE_GROUND, '8', MASK_CONTROL|MASK_ALT|MASK_SHIFT));
 	sub_menu->append(new LLMenuItemCheckGL("Volume",
-											&LLPipeline::toggleRenderType, NULL,
-											&LLPipeline::toggleRenderTypeControl,
+											&LLPipeline::toggleRenderTypeControl, NULL,
+											&LLPipeline::hasRenderTypeControl,
 											(void*)LLPipeline::RENDER_TYPE_VOLUME, '9', MASK_CONTROL|MASK_ALT|MASK_SHIFT));
 	sub_menu->append(new LLMenuItemCheckGL("Grass",
-											&LLPipeline::toggleRenderType, NULL,
-											&LLPipeline::toggleRenderTypeControl,
+											&LLPipeline::toggleRenderTypeControl, NULL,
+											&LLPipeline::hasRenderTypeControl,
 											(void*)LLPipeline::RENDER_TYPE_GRASS, '0', MASK_CONTROL|MASK_ALT|MASK_SHIFT));
 	sub_menu->append(new LLMenuItemCheckGL("Clouds",
-											&LLPipeline::toggleRenderType, NULL,
-											&LLPipeline::toggleRenderTypeControl,
+											&LLPipeline::toggleRenderTypeControl, NULL,
+											&LLPipeline::hasRenderTypeControl,
 											(void*)LLPipeline::RENDER_TYPE_CLOUDS, '-', MASK_CONTROL|MASK_ALT| MASK_SHIFT));
 	sub_menu->append(new LLMenuItemCheckGL("Particles",
-											&LLPipeline::toggleRenderType, NULL,
-											&LLPipeline::toggleRenderTypeControl,
+											&LLPipeline::toggleRenderTypeControl, NULL,
+											&LLPipeline::hasRenderTypeControl,
 											(void*)LLPipeline::RENDER_TYPE_PARTICLES, '=', MASK_CONTROL|MASK_ALT|MASK_SHIFT));
 	sub_menu->append(new LLMenuItemCheckGL("Bump",
-											&LLPipeline::toggleRenderType, NULL,
-											&LLPipeline::toggleRenderTypeControl,
+											&LLPipeline::toggleRenderTypeControl, NULL,
+											&LLPipeline::hasRenderTypeControl,
 											(void*)LLPipeline::RENDER_TYPE_BUMP, '\\', MASK_CONTROL|MASK_ALT|MASK_SHIFT));
 	sub_menu->createJumpKeys();
 	sub_menu = new LLMenuGL("Features");
@@ -1078,6 +1084,10 @@ void init_debug_rendering_menu(LLMenuGL* menu)
 											&LLPipeline::toggleRenderDebugFeature, NULL,
 											&LLPipeline::toggleRenderDebugFeatureControl,
 											(void*)LLPipeline::RENDER_DEBUG_FEATURE_DYNAMIC_TEXTURES, '4', MASK_ALT|MASK_CONTROL));
+	sub_menu->append(new LLMenuItemCheckGL( "Foot Shadows", 
+											&LLPipeline::toggleRenderDebugFeature, NULL,
+											&LLPipeline::toggleRenderDebugFeatureControl,
+											(void*)LLPipeline::RENDER_DEBUG_FEATURE_FOOT_SHADOWS, '5', MASK_ALT|MASK_CONTROL));
 	sub_menu->append(new LLMenuItemCheckGL("Fog",
 											&LLPipeline::toggleRenderDebugFeature, NULL,
 											&LLPipeline::toggleRenderDebugFeatureControl,
@@ -1091,13 +1101,9 @@ void init_debug_rendering_menu(LLMenuGL* menu)
 											&LLPipeline::toggleRenderDebugFeatureControl,
 											(void*)LLPipeline::RENDER_DEBUG_FEATURE_FR_INFO, '8', MASK_ALT|MASK_CONTROL));
 	sub_menu->append(new LLMenuItemCheckGL( "Flexible Objects", 
-										&LLPipeline::toggleRenderDebugFeature, NULL,
+											&LLPipeline::toggleRenderDebugFeature, NULL,
 											&LLPipeline::toggleRenderDebugFeatureControl,
 											(void*)LLPipeline::RENDER_DEBUG_FEATURE_FLEXIBLE, '9', MASK_ALT|MASK_CONTROL));
-	sub_menu->append(new LLMenuItemCheckGL( "Chain Faces", 
-										&LLPipeline::toggleRenderDebugFeature, NULL,
-											&LLPipeline::toggleRenderDebugFeatureControl,
-											(void*)LLPipeline::RENDER_DEBUG_FEATURE_CHAIN_FACES, '0', MASK_ALT|MASK_CONTROL));
 	sub_menu->createJumpKeys();
 
 	/////////////////////////////
@@ -1110,9 +1116,6 @@ void init_debug_rendering_menu(LLMenuGL* menu)
 	sub_menu->append(new LLMenuItemCheckGL("Verify",	&LLPipeline::toggleRenderDebug, NULL,
 													&LLPipeline::toggleRenderDebugControl,
 													(void*)LLPipeline::RENDER_DEBUG_VERIFY));
-	sub_menu->append(new LLMenuItemCheckGL("AGP Map",	&LLPipeline::toggleRenderDebug, NULL,
-													&LLPipeline::toggleRenderDebugControl,
-													(void*)LLPipeline::RENDER_DEBUG_AGP_MEM));
 	sub_menu->append(new LLMenuItemCheckGL("BBoxes",	&LLPipeline::toggleRenderDebug, NULL,
 													&LLPipeline::toggleRenderDebugControl,
 													(void*)LLPipeline::RENDER_DEBUG_BBOXES));
@@ -1125,12 +1128,18 @@ void init_debug_rendering_menu(LLMenuGL* menu)
 	sub_menu->append(new LLMenuItemCheckGL("Occlusion",	&LLPipeline::toggleRenderDebug, NULL,
 													&LLPipeline::toggleRenderDebugControl,
 													(void*)LLPipeline::RENDER_DEBUG_OCCLUSION));
-	sub_menu->append(new LLMenuItemCheckGL("Face Chains",	&LLPipeline::toggleRenderDebug, NULL,
-													&LLPipeline::toggleRenderDebugControl,
-													(void*)LLPipeline::RENDER_DEBUG_FACE_CHAINS));
 	sub_menu->append(new LLMenuItemCheckGL("Texture Priority",	&LLPipeline::toggleRenderDebug, NULL,
 													&LLPipeline::toggleRenderDebugControl,
-													(void*)LLPipeline::RENDER_DEBUG_TEXTURE_PRIORITY));													
+													(void*)LLPipeline::RENDER_DEBUG_TEXTURE_PRIORITY));
+	sub_menu->append(new LLMenuItemCheckGL("Texture Area (sqrt(A))",&LLPipeline::toggleRenderDebug, NULL,
+													&LLPipeline::toggleRenderDebugControl,
+													(void*)LLPipeline::RENDER_DEBUG_TEXTURE_AREA));
+	sub_menu->append(new LLMenuItemCheckGL("Pick Render",	&LLPipeline::toggleRenderDebug, NULL,
+													&LLPipeline::toggleRenderDebugControl,
+													(void*)LLPipeline::RENDER_DEBUG_PICKING));
+	sub_menu->append(new LLMenuItemCheckGL("Particles",	&LLPipeline::toggleRenderDebug, NULL,
+													&LLPipeline::toggleRenderDebugControl,
+													(void*)LLPipeline::RENDER_DEBUG_PARTICLES));
 	sub_menu->append(new LLMenuItemCheckGL("Composition", &LLPipeline::toggleRenderDebug, NULL,
 													&LLPipeline::toggleRenderDebugControl,
 													(void*)LLPipeline::RENDER_DEBUG_COMPOSITION));
@@ -1140,16 +1149,7 @@ void init_debug_rendering_menu(LLMenuGL* menu)
 	sub_menu->append(new LLMenuItemCheckGL("LightTrace",&LLPipeline::toggleRenderDebug, NULL,
 													&LLPipeline::toggleRenderDebugControl,
 													(void*)LLPipeline::RENDER_DEBUG_LIGHT_TRACE));
-	sub_menu->append(new LLMenuItemCheckGL("Pools",     &LLPipeline::toggleRenderDebug, NULL,
-													&LLPipeline::toggleRenderDebugControl,
-													(void*)LLPipeline::RENDER_DEBUG_POOLS));
-	sub_menu->append(new LLMenuItemCheckGL("Queues",    &LLPipeline::toggleRenderDebug, NULL,
-													&LLPipeline::toggleRenderDebugControl,
-													(void*)LLPipeline::RENDER_DEBUG_QUEUES));
-	sub_menu->append(new LLMenuItemCheckGL("Map",       &LLPipeline::toggleRenderDebug, NULL,
-													LLPipeline::toggleRenderDebugControl,
-													(void*)LLPipeline::RENDER_DEBUG_MAP));
-
+	
 	sub_menu->append(new LLMenuItemCheckGL("Show Depth Buffer",
 										   &menu_toggle_control,
 										   NULL,
@@ -1174,8 +1174,6 @@ void init_debug_rendering_menu(LLMenuGL* menu)
 
 	menu->appendSeparator();
 	menu->append(new LLMenuItemCheckGL("Axes", menu_toggle_control, NULL, menu_check_control, (void*)"ShowAxes"));
-	menu->append(new LLMenuItemCheckGL("Use VBO", toggle_vbo, NULL, check_vbo, NULL));
-	menu->append(new LLMenuItemCheckGL("Light Glows", toggle_glow, NULL, check_glow, NULL));
 //	menu->append(new LLMenuItemCheckGL("Cull Small Objects", toggle_cull_small, NULL, menu_check_control, (void*)"RenderCullBySize"));
 
 	menu->appendSeparator();
@@ -1200,6 +1198,19 @@ void init_debug_rendering_menu(LLMenuGL* menu)
 	item = new LLMenuItemCheckGL("Disable Textures", menu_toggle_variable, NULL, menu_check_variable, (void*)&LLViewerImage::sDontLoadVolumeTextures);
 	menu->append(item);
 	
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+	item = new LLMenuItemCheckGL("HTTP Get Textures", menu_toggle_control, NULL, menu_check_control, (void*)"ImagePipelineUseHTTP");
+	menu->append(item);
+#endif
+	
+	item = new LLMenuItemCheckGL("Run Multiple Threads", menu_toggle_control, NULL, menu_check_control, (void*)"RunMultipleThreads");
+	menu->append(item);
+
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+	item = new LLMenuItemCheckGL("Dynamic Reflections", menu_toggle_control, NULL, menu_check_control, (void*)"RenderDynamicReflections");
+	menu->append(item);
+#endif
+	
 	item = new LLMenuItemCheckGL("Cheesy Beacon", menu_toggle_control, NULL, menu_check_control, (void*)"CheesyBeacon");
 	menu->append(item);
 	
@@ -1256,10 +1267,6 @@ void init_debug_avatar_menu(LLMenuGL* menu)
 	//menu->append(new LLMenuItemToggleGL("Show Attachment Points", &LLVOAvatar::sShowAttachmentPoints));
 	menu->append(new LLMenuItemToggleGL("Show Collision Plane", &LLVOAvatar::sShowFootPlane));
 	menu->append(new LLMenuItemToggleGL("Show Collision Skeleton", &LLVOAvatar::sShowCollisionVolumes));
-	menu->append(new LLMenuItemToggleGL("Software Blending SSE", &gGLManager.mSoftwareBlendSSE));
-#if 0 // Removed since this feature doesn't actually work as of 1.9.1 --TomY
-	menu->append(new LLMenuItemToggleGL("Character Load Test", &LLVOAvatar::sAvatarLoadTest));
-#endif
 	menu->append(new LLMenuItemToggleGL( "Display Agent Target", &LLAgent::sDebugDisplayTarget));
 	menu->append(new LLMenuItemToggleGL( "Debug Rotation", &gDebugAvatarRotation));
 	menu->append(new LLMenuItemCallGL("Dump Attachments", handle_dump_attachments));
@@ -1319,7 +1326,8 @@ void init_server_menu(LLMenuGL* menu)
 		menu->appendMenu(sub);
 
 		sub->append(new LLMenuItemCallGL( "Take Copy",
-										   &force_take_copy, &enable_god_customer_service, NULL));
+										  &force_take_copy, &enable_god_customer_service, NULL,
+										  'O', MASK_SHIFT | MASK_ALT | MASK_CONTROL));
 #ifdef _CORY_TESTING
 		sub->append(new LLMenuItemCallGL( "Export Copy",
 										   &force_export_copy, NULL, NULL));
@@ -1509,37 +1517,6 @@ class LLObjectEnableReportAbuse : public view_listener_t
 	}
 };
 
-
-BOOL enable_attach(void*)
-{
-	// All root objects must be owned by agent.
-	LLObjectSelectionHandle selection = gSelectMgr->getSelection();
-	BOOL rv = FALSE;
-	LLViewerObject* obj = selection->getFirstRootObject();
-	if(obj)
-	{
-		rv = TRUE;
-		for(obj = selection->getFirstRootObject() ; obj != NULL; obj = selection->getNextRootObject())
-		{
-			for (U32 child_num = 0; child_num < obj->mChildList.size(); child_num++ )
-			{
-				LLViewerObject *child = obj->mChildList[child_num];
-				if (child->isAvatar())
-				{
-					return FALSE;
-				}
-			}
-			if(!obj->permMove())
-			{
-				rv = FALSE;
-				break;
-			}
-		}
-	}
-	return rv;
-}
-
-
 class LLObjectTouch : public view_listener_t
 {
 	bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
@@ -1886,7 +1863,7 @@ class LLSelfEnableRemoveAllAttachments : public view_listener_t
 				attachmentp;
 				attachmentp = avatarp->mAttachmentPoints.getNextData())
 			{
-				if (attachmentp->getObject(0))
+				if (attachmentp->getObject())
 				{
 					new_value = true;
 					break;
@@ -6096,15 +6073,6 @@ class LLToolsLookAtSelection : public view_listener_t
 	}
 };
 
-/*
-void handle_reset_rotation(void*)
-{
-	gSelectMgr->selectionResetRotation();
-
-	dialog_refresh_all();
-}
-*/
-
 class LLAvatarAddFriend : public view_listener_t
 {
 	bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
@@ -6789,7 +6757,10 @@ private:
 		LLViewerObject* selectedObject = sObjectSelection->getFirstRootObject();
 		if (selectedObject)
 		{
-			LLViewerJointAttachment* attachment_point = gAgent.getAvatarObject()->mAttachmentPoints[userdata.asInteger()];
+			S32 index = userdata.asInteger();
+			LLViewerJointAttachment* attachment_point = index > 0 ?
+				gAgent.getAvatarObject()->mAttachmentPoints[index] :
+				NULL;
 			confirm_replace_attachment(0, attachment_point);
 		}
 		return true;
@@ -6830,7 +6801,7 @@ void handle_attach_to_avatar(void* user_data)
     {
         LLViewerJointAttachment *attachment = (LLViewerJointAttachment *)user_data;
 
-        if (attachment && attachment->getObject(0))
+        if (attachment && attachment->getObject())
         {
             gViewerWindow->alertXml("ReplaceAttachment", confirm_replace_attachment, user_data);
         }
@@ -6921,7 +6892,7 @@ void handle_detach_from_avatar(void* user_data)
 {
 	LLViewerJointAttachment *attachment = (LLViewerJointAttachment *)user_data;
 	
-	LLViewerObject* attached_object = attachment->getObject(0);
+	LLViewerObject* attached_object = attachment->getObject();
 
 	if (attached_object)
 	{
@@ -6942,7 +6913,7 @@ void attach_label(LLString& label, void* user_data)
 	if (attachmentp)
 	{
 		label = attachmentp->getName();
-		if (attachmentp->getObject(0))
+		if (attachmentp->getObject())
 		{
 			LLViewerInventoryItem* itemp = gInventory.getItem(attachmentp->getItemID());
 			if (itemp)
@@ -6959,7 +6930,7 @@ void detach_label(LLString& label, void* user_data)
 	if (attachmentp)
 	{
 		label = attachmentp->getName();
-		if (attachmentp->getObject(0))
+		if (attachmentp->getObject())
 		{
 			LLViewerInventoryItem* itemp = gInventory.getItem(attachmentp->getItemID());
 			if (itemp)
@@ -7159,23 +7130,7 @@ class LLObjectEnableWear : public view_listener_t
 {
 	bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
 	{
-		bool new_value = false;
-		if (gSelectMgr)
-		{
-			LLObjectSelectionHandle selection = gSelectMgr->getSelection();
-			LLViewerObject* first_root = selection->getFirstRootObject();
-			if (first_root)
-			{
-				new_value = selection->getRootObjectCount() == 1
-								&& first_root->getPCode() == LL_PCODE_VOLUME
-								&& first_root->permYouOwner()
-								&& !((LLViewerObject*)selection->getFirstRootObject()->getRoot())->isAvatar()
-								&& (first_root->getNVPair("AssetContainer") == NULL);
-			}
-		}
-
-		gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
-		return true;
+		return object_selected_and_point_valid(NULL);
 	}
 };
 
@@ -7184,7 +7139,7 @@ BOOL object_attached(void *user_data)
 {
 	LLViewerJointAttachment *attachment = (LLViewerJointAttachment *)user_data;
 
-	return attachment->getObject(0) != NULL;
+	return attachment->getObject() != NULL;
 }
 
 class LLAvatarSendIM : public view_listener_t
@@ -7481,16 +7436,16 @@ void handle_dump_attachments(void*)
 		attachment = avatar->mAttachmentPoints.getNextData() )
 	{
 		S32 key = avatar->mAttachmentPoints.getCurrentKeyWithoutIncrement();
-		BOOL visible = (attachment->getObject(0) != NULL &&
-						attachment->getObject(0)->mDrawable.notNull() && 
-						!attachment->getObject(0)->mDrawable->isRenderType(0));
+		BOOL visible = (attachment->getObject() != NULL &&
+						attachment->getObject()->mDrawable.notNull() && 
+						!attachment->getObject()->mDrawable->isRenderType(0));
 		LLVector3 pos;
-		if (visible) pos = attachment->getObject(0)->mDrawable->getPosition();
+		if (visible) pos = attachment->getObject()->mDrawable->getPosition();
 		llinfos << "ATTACHMENT " << key << ": item_id=" << attachment->getItemID()
-				<< (attachment->getObject(0) ? " present " : " absent ")
+				<< (attachment->getObject() ? " present " : " absent ")
 				<< (visible ? "visible " : "invisible ")
 				<<  " at " << pos
-				<< " and " << (visible ? attachment->getObject(0)->getPosition() : LLVector3::zero)
+				<< " and " << (visible ? attachment->getObject()->getPosition() : LLVector3::zero)
 				<< llendl;
 	}
 }
@@ -7873,26 +7828,6 @@ BOOL enable_god_basic(void*)
 	return gAgent.getGodLevel() > GOD_NOT;
 }
 
-void toggle_vbo(void *)
-{
-	gPipeline.mUseVBO = !gPipeline.mUseVBO;
-
-	if (!gPipeline.usingAGP())
-	{
-		return;
-	}
-
-	gPipeline.setUseAGP(FALSE);
-	gPipeline.setUseAGP(TRUE);
-
-	gSavedSettings.setBOOL("RenderUseVBO", gPipeline.mUseVBO);
-}
-
-BOOL check_vbo(void *)
-{
-	return gPipeline.mUseVBO;
-}
-
 #if 0 // 1.9.2
 void toggle_vertex_shaders(void *)
 {
@@ -7906,19 +7841,6 @@ BOOL check_vertex_shaders(void *)
 }
 #endif
 
-void toggle_glow(void *)
-{
-	gRenderLightGlows = !gRenderLightGlows;
-
-	gSavedSettings.setBOOL("RenderLightGlows", gRenderLightGlows);
-}
-
-BOOL check_glow(void *)
-{
-	return gRenderLightGlows;
-}
-
-
 void toggle_show_xui_names(void *)
 {
 	BOOL showXUINames = gSavedSettings.getBOOL("ShowXUINames");
@@ -8428,7 +8350,7 @@ class LLViewToggleRenderType : public view_listener_t
 		LLString type = userdata.asString();
 		if (type == "particles")
 		{
-			LLPipeline::toggleRenderType((void *)(S32)LLPipeline::RENDER_TYPE_PARTICLES);
+			LLPipeline::toggleRenderType(LLPipeline::RENDER_TYPE_PARTICLES);
 		}
 		return true;
 	}
@@ -8449,12 +8371,11 @@ class LLViewCheckRenderType : public view_listener_t
 	}
 };
 
-// TomY TODO: Get rid of these?
 class LLViewShowHUDAttachments : public view_listener_t
 {
 	bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
 	{
-		LLDrawPoolHUD::sShowHUDAttachments = !LLDrawPoolHUD::sShowHUDAttachments;
+		LLPipeline::sShowHUDAttachments = !LLPipeline::sShowHUDAttachments;
 		return true;
 	}
 };
@@ -8463,7 +8384,7 @@ class LLViewCheckHUDAttachments : public view_listener_t
 {
 	bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
 	{
-		bool new_value = LLDrawPoolHUD::sShowHUDAttachments;
+		bool new_value = LLPipeline::sShowHUDAttachments;
 		gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
 		return true;
 	}
diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp
index e1f51586f44f5f34edb57c75f9c42c484162fdc7..8ed5406e856ea08ce19d5c551acad5618c2f2835 100644
--- a/indra/newview/llviewermessage.cpp
+++ b/indra/newview/llviewermessage.cpp
@@ -2893,7 +2893,8 @@ void send_agent_update(BOOL force_send, BOOL send_reliable)
 extern U32 gObjectBits;
 
 void process_object_update(LLMessageSystem *mesgsys, void **user_data)
-{
+{	
+	LLMemType mt(LLMemType::MTYPE_OBJECT);
 	// Update the data counters
 	if (mesgsys->getReceiveCompressedSize())
 	{
@@ -2911,6 +2912,7 @@ void process_object_update(LLMessageSystem *mesgsys, void **user_data)
 
 void process_compressed_object_update(LLMessageSystem *mesgsys, void **user_data)
 {
+	LLMemType mt(LLMemType::MTYPE_OBJECT);
 	// Update the data counters
 	if (mesgsys->getReceiveCompressedSize())
 	{
@@ -2928,6 +2930,7 @@ void process_compressed_object_update(LLMessageSystem *mesgsys, void **user_data
 
 void process_cached_object_update(LLMessageSystem *mesgsys, void **user_data)
 {
+	LLMemType mt(LLMemType::MTYPE_OBJECT);
 	// Update the data counters
 	if (mesgsys->getReceiveCompressedSize())
 	{
@@ -2946,6 +2949,7 @@ void process_cached_object_update(LLMessageSystem *mesgsys, void **user_data)
 
 void process_terse_object_update_improved(LLMessageSystem *mesgsys, void **user_data)
 {
+	LLMemType mt(LLMemType::MTYPE_OBJECT);
 	if (mesgsys->getReceiveCompressedSize())
 	{
 		gObjectBits += mesgsys->getReceiveCompressedSize() * 8;
diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp
index 1ff2d81b0c3747bfb42821c11d0c1662a8c83d82..615bc1bf4f16c28b9a90c42a62c2d9c2eef18983 100644
--- a/indra/newview/llviewerobject.cpp
+++ b/indra/newview/llviewerobject.cpp
@@ -68,7 +68,6 @@
 #include "llvosurfacepatch.h"
 #include "llvotextbubble.h"
 #include "llvotree.h"
-#include "llvotreenew.h"
 #include "llvovolume.h"
 #include "llvowater.h"
 #include "llworld.h"
@@ -112,7 +111,6 @@ LLViewerObject *LLViewerObject::createObject(const LLUUID &id, const LLPCode pco
 	case LL_PCODE_TREE_NEW:
 		llwarns << "Creating new tree!" << llendl;
 //		res = new LLVOTree(id, pcode, regionp); break;
-//		res = new LLVOTreeNew(id, pcode, regionp); break;
 		res = NULL; break;
 	case LL_PCODE_LEGACY_TEXT_BUBBLE:
 	  res = new LLVOTextBubble(id, pcode, regionp); break;
@@ -154,10 +152,11 @@ LLViewerObject::LLViewerObject(const LLUUID &id, const LLPCode pcode, LLViewerRe
 	mText(),
 	mLastInterpUpdateSecs(0.f),
 	mLastMessageUpdateSecs(0.f),
+	mLatestRecvPacketID(0),
 	mData(NULL),
 	mAudioSourcep(NULL),
 	mAppAngle(0.f),
-	mPixelArea(0.f),
+	mPixelArea(1024.f),
 	mInventory(NULL),
 	mInventorySerialNum(0),
 	mRegionp( regionp ),
@@ -169,7 +168,6 @@ LLViewerObject::LLViewerObject(const LLUUID &id, const LLPCode pcode, LLViewerRe
 	mOnActiveList(FALSE),
 	mOnMap(FALSE),
 	mStatic(FALSE),
-	mFaceIndexOffset(0),
 	mNumFaces(0),
 	mLastUpdateFrame(0),
 	mTimeDilation(1.f),
@@ -349,10 +347,12 @@ void LLViewerObject::dump() const
 	llinfos << "Velocity: " << getVelocity() << llendl;
 	if (mDrawable.notNull() && mDrawable->getNumFaces())
 	{
-		LLDrawPool *poolp = mDrawable->getFace(0)->getPool();
-		llinfos << "Pool: " << poolp << llendl;
-		llinfos << "Pool reference count: " << poolp->mReferences.size() << llendl;
-		llinfos << "Pool vertex count: " << poolp->getVertexCount() << llendl;
+		LLFacePool *poolp = mDrawable->getFace(0)->getPool();
+		if (poolp)
+		{
+			llinfos << "Pool: " << poolp << llendl;
+			llinfos << "Pool reference count: " << poolp->mReferences.size() << llendl;
+		}
 	}
 	//llinfos << "BoxTree Min: " << mDrawable->getBox()->getMin() << llendl;
 	//llinfos << "BoxTree Max: " << mDrawable->getBox()->getMin() << llendl;
@@ -596,11 +596,24 @@ BOOL LLViewerObject::setDrawableParent(LLDrawable* parentp)
 		return FALSE;
 	}
 
+	LLDrawable* old_parent = mDrawable->mParent;
+
 	mDrawable->mParent = parentp; 
 	
 	BOOL ret = mDrawable->mXform.setParent(parentp ? &parentp->mXform : NULL);
 	gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);
-	gPipeline.markMoved(mDrawable, FALSE);
+	if (old_parent || (parentp && parentp->isActive()))
+	{
+		gPipeline.markMoved(mDrawable, FALSE);
+	}
+	else
+	{
+		mDrawable->updateXform(TRUE);
+		if (!mDrawable->getSpatialGroup())
+		{
+			mDrawable->movePartition();
+		}
+	}
 	
 	return ret;
 }
@@ -655,7 +668,7 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys,
 	LLVector3 new_acc;
 	LLVector3 new_angv;
 	LLQuaternion new_rot;
-	LLVector3 new_scale;
+	LLVector3 new_scale = getScale();
 
 	U32	parent_id = 0;
 	U8	material = 0;
@@ -881,13 +894,6 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys,
 				// ...new objects that should come in selected need to be added to the selected list
 				mCreateSelected = ((flags & FLAGS_CREATE_SELECTED) != 0);
 
-				// Set the change flags for scale
-				if (new_scale != getScale())
-				{
-					setChanged(SCALED | SILHOUETTE);
-					setScale(new_scale);  // Must follow setting permYouOwner()
-				}
-
 				// Set all name value pairs
 				S32 nv_size = mesgsys->getSizeFast(_PREHASH_ObjectData, block_num, _PREHASH_NameValue);
 				if (nv_size > 0)
@@ -1465,14 +1471,6 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys,
 
 					// ...new objects that should come in selected need to be added to the selected list
 				mCreateSelected = ((flags & FLAGS_CREATE_SELECTED) != 0);
-
-				// Set the change flags for scale
-				if (new_scale != getScale())
-				{
-					setChanged(SCALED | SILHOUETTE);
-					setScale(new_scale);  // Must follow setting permYouOwner()
-				}
-
 			}
 			break;
 
@@ -1744,6 +1742,23 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys,
 	//
 	//
 
+	U32 packet_id = mesgsys->getCurrentRecvPacketID(); 
+	if (packet_id < mLatestRecvPacketID && 
+		mLatestRecvPacketID - packet_id < 65536)
+	{
+		//skip application of this message, it's old
+		return retval;
+	}
+
+	mLatestRecvPacketID = packet_id;
+
+	// Set the change flags for scale
+	if (new_scale != getScale())
+	{
+		setChanged(SCALED | SILHOUETTE);
+		setScale(new_scale);  // Must follow setting permYouOwner()
+	}
+
 	// first, let's see if the new position is actually a change
 
 	//static S32 counter = 0;
@@ -1771,14 +1786,10 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys,
 
 	if (new_rot != mLastRot)
 	{
-	//	if (getAngularVelocity().isExactlyZero() || 
-	//		new_angv != getAngularVelocity())
-		{
-			mLastRot = new_rot;
-			setChanged(ROTATED | SILHOUETTE);
-			setRotation(new_rot);
-			resetRot();
-		}
+		mLastRot = new_rot;
+		setChanged(ROTATED | SILHOUETTE);
+		setRotation(new_rot);
+		resetRot();
 	}
 
 
@@ -2625,6 +2636,11 @@ BOOL LLViewerObject::updateGeometry(LLDrawable *drawable)
 	return TRUE;
 }
 
+void LLViewerObject::updateFaceSize(S32 idx)
+{
+	
+}
+
 LLDrawable* LLViewerObject::createDrawable(LLPipeline *pipeline)
 {
 	return NULL;
@@ -2665,13 +2681,23 @@ void LLViewerObject::setScale(const LLVector3 &scale, BOOL damped)
 void LLViewerObject::updateSpatialExtents(LLVector3& newMin, LLVector3 &newMax)
 {
 	LLVector3 center = getRenderPosition();
-	F32 sz = llmin(mDrawable->getRadius(), 256.f);
-	LLVector3 size = LLVector3(sz,sz,sz);
+	LLVector3 size = getScale();
 	newMin.setVec(center-size);
 	newMax.setVec(center+size);
 	mDrawable->setPositionGroup((newMin + newMax) * 0.5f);
 }
 
+F32 LLViewerObject::getBinRadius()
+{
+	if (mDrawable.notNull())
+	{
+		const LLVector3* ext = mDrawable->getSpatialExtents();
+		return (ext[1]-ext[0]).magVec();
+	}
+	
+	return getScale().magVec();
+}
+
 F32 LLViewerObject::getMaxScale() const
 {
 	return llmax(getScale().mV[VX],getScale().mV[VY], getScale().mV[VZ]);
@@ -3574,11 +3600,6 @@ S32 LLViewerObject::setTETexGen(const U8 te, const U8 texgen)
 	{
 		retval = LLPrimitive::setTETexGen(te, texgen);
 		setChanged(TEXTURE);
-		if (mDrawable.notNull() && retval)
-		{
-			gPipeline.markTextured(mDrawable);
-			gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_ALL, TRUE);
-		}
 	}
 	return retval;
 }
@@ -3595,11 +3616,6 @@ S32 LLViewerObject::setTEShiny(const U8 te, const U8 shiny)
 	{
 		retval = LLPrimitive::setTEShiny(te, shiny);
 		setChanged(TEXTURE);
-		if (mDrawable.notNull() && retval)
-		{
-			gPipeline.markTextured(mDrawable);
-			gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_GEOMETRY, TRUE);
-		}
 	}
 	return retval;
 }
@@ -3744,7 +3760,7 @@ LLViewerImage *LLViewerObject::getTEImage(const U8 face) const
 		}
 	}
 
-	llerrs << "Requested invalid face!" << llendl;
+	llerrs << llformat("Requested Image from invalid face: %d/%d",face,getNumTEs()) << llendl;
 
 	return NULL;
 }
@@ -3838,6 +3854,11 @@ void LLViewerObject::setCanSelect(BOOL canSelect)
 
 void LLViewerObject::setDebugText(const std::string &utf8text)
 {
+	if (utf8text.empty() && !mText)
+	{
+		return;
+	}
+
 	if (!mText)
 	{
 		mText = (LLHUDText *)LLHUDObject::addHUDObject(LLHUDObject::LL_HUD_TEXT);
@@ -4558,6 +4579,7 @@ void LLViewerObject::markForUpdate(BOOL priority)
 void LLViewerObject::setRegion(LLViewerRegion *regionp)
 {
 	llassert(regionp);
+	mLatestRecvPacketID = 0;
 	mRegionp = regionp;
 	setChanged(MOVED | SILHOUETTE);
 	updateDrawable(FALSE);
@@ -4646,3 +4668,30 @@ void LLViewerObject::resetRot()
 {
 	mRotTime = 0.0f;
 }
+
+U32 LLViewerObject::getPartitionType() const
+{ 
+	return LLPipeline::PARTITION_NONE; 
+}
+
+BOOL LLAlphaObject::isParticle()
+{
+	return FALSE;
+}
+
+F32 LLAlphaObject::getPartSize(S32 idx)
+{
+	return 0.f;
+}
+
+// virtual
+void LLStaticViewerObject::updateDrawable(BOOL force_damped)
+{
+	// Force an immediate rebuild on any update
+	if (mDrawable.notNull())
+	{
+		mDrawable->updateXform(TRUE);
+		gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_ALL, TRUE);
+	}
+	clearChanged(SHIFTED);
+}
diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h
index 285d684dc6aca6a80acb11398043adcd27bd805c..d8b5a14897d4a0081ac79e4d73e5796e607dbcd5 100644
--- a/indra/newview/llviewerobject.h
+++ b/indra/newview/llviewerobject.h
@@ -18,6 +18,7 @@
 #include "llhudicon.h"
 #include "llinventory.h"
 #include "llmemory.h"
+#include "llmemtype.h"
 #include "llprimitive.h"
 #include "lluuid.h"
 #include "llvoinventorylistener.h"
@@ -25,6 +26,7 @@
 #include "llquaternion.h"
 #include "v3dmath.h"
 #include "v3math.h"
+#include "llvertexbuffer.h"
 
 class LLAgent;			// TODO: Get rid of this.
 class LLAudioSource;
@@ -168,6 +170,7 @@ public:
 	
 	virtual LLDrawable* createDrawable(LLPipeline *pipeline);
 	virtual BOOL		updateGeometry(LLDrawable *drawable);
+	virtual void		updateFaceSize(S32 idx);
 	virtual BOOL		updateLOD();
 	virtual BOOL		setDrawableParent(LLDrawable* parentp);
 	virtual BOOL		updateLighting(BOOL do_lighting) { return TRUE; };
@@ -184,7 +187,7 @@ public:
 	LLViewerRegion* getRegion() const				{ return mRegionp; }
 
 	BOOL isSelected() const							{ return mUserSelected; }
-	void setSelected(BOOL sel)						{ mUserSelected = sel; mRotTime = 0.f;}
+	virtual void setSelected(BOOL sel)				{ mUserSelected = sel; mRotTime = 0.f;}
 
 	const LLUUID &getID() const						{ return mID; }
 	U32 getLocalID() const							{ return mLocalID; }
@@ -274,8 +277,6 @@ public:
 	/*virtual*/	BOOL	setMaterial(const U8 material);
 	virtual		void	setTEImage(const U8 te, LLViewerImage *imagep); // Not derived from LLPrimitive
 	LLViewerImage		*getTEImage(const U8 te) const;
-
-	S32					getFaceIndexOffset() { return mFaceIndexOffset; }
 	
 	void fitFaceTexture(const U8 face);
 	void sendTEUpdate() const;			// Sends packed representation of all texture entry information
@@ -288,6 +289,8 @@ public:
 	U8 getState()							{ return mState; }
 
 	F32 getAppAngle() const					{ return mAppAngle; }
+	F32 getPixelArea() const				{ return mPixelArea; }
+	void setPixelArea(F32 area)				{ mPixelArea = area; }
 	F32 getMaxScale() const;
 	F32 getMidScale() const;
 	F32 getMinScale() const;
@@ -320,6 +323,7 @@ public:
 	void markForUpdate(BOOL priority);
 	void updateVolume(const LLVolumeParams& volume_params);
 	virtual	void updateSpatialExtents(LLVector3& min, LLVector3& max);
+	virtual F32 getBinRadius();
 	
 	LLBBox				getBoundingBoxAgent() const;
 
@@ -417,6 +421,7 @@ public:
 	void printNameValuePairs() const;
 
 	virtual S32 getLOD() const { return 3; } 
+	virtual U32 getPartitionType() const;
 
 	virtual LLNetworkData* getParameterEntry(U16 param_type) const;
 	virtual bool setParameterEntry(U16 param_type, const LLNetworkData& new_value, bool local_origin);
@@ -528,7 +533,7 @@ protected:
 
 	F64				mLastInterpUpdateSecs;			// Last update for purposes of interpolation
 	F64				mLastMessageUpdateSecs;			// Last update from a message from the simulator
-
+	TPACKETID		mLatestRecvPacketID;			// Latest time stamp on message from simulator
 	// extra data sent from the sim...currently only used for tree species info
 	U8* mData;
 
@@ -559,7 +564,6 @@ protected:
 	BOOL			mOnActiveList;
 	BOOL			mOnMap;						// On the map.
 	BOOL			mStatic;					// Object doesn't move.
-	S32				mFaceIndexOffset;			// offset into drawable's faces, zero except in special cases
 	S32				mNumFaces;
 
 	S32				mLastUpdateFrame;			// frames in which an object had last moved for smart coalescing of drawables 
@@ -592,7 +596,6 @@ private:
 	static S32 sNumObjects;
 };
 
-
 ///////////////////
 //
 // Inlines
@@ -623,4 +626,34 @@ public:
 	U8 mMediaType;			// see LLTextureEntry::WEB_PAGE, etc.
 };
 
+// subclass of viewer object that can be added to particle partitions
+class LLAlphaObject : public LLViewerObject
+{
+public:
+	LLAlphaObject(const LLUUID &id, const LLPCode type, LLViewerRegion *regionp)
+	: LLViewerObject(id,type,regionp) 
+	{ mDepth = 0.f; }
+
+	virtual BOOL isParticle();
+	virtual F32 getPartSize(S32 idx);
+	virtual void getGeometry(S32 idx,
+								LLStrider<LLVector3>& verticesp,
+								LLStrider<LLVector3>& normalsp, 
+								LLStrider<LLVector2>& texcoordsp,
+								LLStrider<LLColor4U>& colorsp, 
+								LLStrider<U32>& indicesp) = 0;
+
+	F32 mDepth;
+};
+
+class LLStaticViewerObject : public LLViewerObject
+{
+public:
+	LLStaticViewerObject(const LLUUID& id, const LLPCode type, LLViewerRegion* regionp)
+		: LLViewerObject(id,type,regionp)
+	{ }
+
+	virtual void updateDrawable(BOOL force_damped);
+};
+
 #endif
diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp
index f4c733299f6b0948d6fdcb2f212f72b8fdbe6650..68193604bb6a3623e30ca9972f4cce88fda48709 100644
--- a/indra/newview/llviewerobjectlist.cpp
+++ b/indra/newview/llviewerobjectlist.cpp
@@ -731,6 +731,15 @@ void LLViewerObjectList::update(LLAgent &agent, LLWorld &world)
 	mNumVisCulledStat.addValue(mNumVisCulled);
 }
 
+void LLViewerObjectList::clearDebugText()
+{
+	for (S32 i = 0; i < mObjects.count(); i++)
+	{
+		mObjects[i]->setDebugText("");
+	}
+}
+
+
 void LLViewerObjectList::cleanupReferences(LLViewerObject *objectp)
 {
 	LLMemType mt(LLMemType::MTYPE_OBJECT);
@@ -1052,7 +1061,15 @@ U32 LLViewerObjectList::renderObjectsForSelect(LLCamera &camera, BOOL pick_parce
 		mSelectPickList.clear();
 
 		std::vector<LLDrawable*> pick_drawables;
-		gPipeline.mObjectPartition->cull(camera, &pick_drawables, TRUE);
+
+		for (i = 0; i < LLPipeline::NUM_PARTITIONS-1; i++)
+		{
+			LLSpatialPartition* part = gPipeline.getSpatialPartition(i);
+			if (part)
+			{
+				part->cull(camera, &pick_drawables, TRUE);
+			}
+		}
 
 		for (std::vector<LLDrawable*>::iterator iter = pick_drawables.begin();
 			iter != pick_drawables.end(); iter++)
@@ -1074,10 +1091,10 @@ U32 LLViewerObjectList::renderObjectsForSelect(LLCamera &camera, BOOL pick_parce
 
 		LLHUDText::addPickable(mSelectPickList);
 
-		for (objectp = (LLVOAvatar*)LLCharacter::sInstances.getFirstData(); 
-			objectp; 
-			objectp = (LLVOAvatar*)LLCharacter::sInstances.getNextData())
+		for (std::vector<LLCharacter*>::iterator iter = LLCharacter::sInstances.begin();
+			iter != LLCharacter::sInstances.end(); ++iter)
 		{
+			objectp = (LLVOAvatar*) *iter;
 			if (!objectp->isDead())
 			{
 				if (objectp->mDrawable.notNull() && objectp->mDrawable->isVisible())
@@ -1098,7 +1115,7 @@ U32 LLViewerObjectList::renderObjectsForSelect(LLCamera &camera, BOOL pick_parce
 			{
 				if (attachmentp->getIsHUDAttachment())
 				{
-					LLViewerObject* objectp = attachmentp->getObject(0);
+					LLViewerObject* objectp = attachmentp->getObject();
 					if (objectp)
 					{
 						mSelectPickList.insert(objectp);		
@@ -1114,32 +1131,36 @@ U32 LLViewerObjectList::renderObjectsForSelect(LLCamera &camera, BOOL pick_parce
 				}
 			}
 		}
-
+		
 		S32 num_pickables = (S32)mSelectPickList.size() + LLHUDIcon::getNumInstances();
 
-		S32 step = (0x000fffff - GL_NAME_INDEX_OFFSET) / num_pickables;
-
-		std::set<LLViewerObject*>::iterator pick_it;
-		i = 0;
-		for (pick_it = mSelectPickList.begin(); pick_it != mSelectPickList.end();)
+		if (num_pickables != 0)
 		{
-			LLViewerObject* objp = (*pick_it);
-			if (!objp || objp->isDead() || !objp->mbCanSelect)
+			S32 step = (0x000fffff - GL_NAME_INDEX_OFFSET) / num_pickables;
+
+			std::set<LLViewerObject*>::iterator pick_it;
+			i = 0;
+			for (pick_it = mSelectPickList.begin(); pick_it != mSelectPickList.end();)
 			{
-				mSelectPickList.erase(pick_it++);
-				continue;
+				LLViewerObject* objp = (*pick_it);
+				if (!objp || objp->isDead() || !objp->mbCanSelect)
+				{
+					mSelectPickList.erase(pick_it++);
+					continue;
+				}
+				
+				objp->mGLName = (i * step) + GL_NAME_INDEX_OFFSET;
+				i++;
+				++pick_it;
 			}
 
-			objp->mGLName = (i * step) + GL_NAME_INDEX_OFFSET;
-			i++;
-			++pick_it;
+			LLHUDIcon::generatePickIDs(i * step, step);
+		
+			// At this point, we should only have live drawables/viewer objects
+			gPipeline.renderForSelect(mSelectPickList);
 		}
-
-		LLHUDIcon::generatePickIDs(i * step, step);
 	}
 
-	// At this point, we should only have live drawables/viewer objects
-	gPipeline.renderForSelect();
 	//
 	// Render pass for selected objects
 	//
@@ -1432,6 +1453,7 @@ void LLViewerObjectList::findOrphans(LLViewerObject* objectp, U32 ip, U32 port)
 	}
 }
 
+////////////////////////////////////////////////////////////////////////////
 
 LLViewerObjectList::OrphanInfo::OrphanInfo()
 {
diff --git a/indra/newview/llviewerobjectlist.h b/indra/newview/llviewerobjectlist.h
index a2893c4b7dee33816b3a7cdff1d35b394a4da501..3e148c822fe6ea82abcdce022b050371aa55fe3a 100644
--- a/indra/newview/llviewerobjectlist.h
+++ b/indra/newview/llviewerobjectlist.h
@@ -96,6 +96,7 @@ public:
 	void addToMap(LLViewerObject *objectp);
 	void removeFromMap(LLViewerObject *objectp);
 
+	void clearDebugText();
 
 	////////////////////////////////////////////
 	//
diff --git a/indra/newview/llviewerparcelmgr.cpp b/indra/newview/llviewerparcelmgr.cpp
index c1d2f2742eee1d16a93e127f698dfb8eba6f5245..cd915317ba46b498b1a3c1be2282c50cf421ac41 100644
--- a/indra/newview/llviewerparcelmgr.cpp
+++ b/indra/newview/llviewerparcelmgr.cpp
@@ -1552,7 +1552,7 @@ void LLViewerParcelMgr::processParcelProperties(LLMessageSystem *msg, void **use
 				gParcelMgr->resetSegments(gParcelMgr->mHighlightSegments);
 				gParcelMgr->writeSegmentsFromBitmap( bitmap, gParcelMgr->mHighlightSegments );
 
-				delete bitmap;
+				delete[] bitmap;
 				bitmap = NULL;
 
 				gParcelMgr->mCurrentParcelSelection->mWholeParcelSelected = TRUE;
@@ -1604,7 +1604,7 @@ void LLViewerParcelMgr::processParcelProperties(LLMessageSystem *msg, void **use
 		gParcelMgr->resetSegments(gParcelMgr->mCollisionSegments);
 		gParcelMgr->writeSegmentsFromBitmap( bitmap, gParcelMgr->mCollisionSegments );
 
-		delete bitmap;
+		delete[] bitmap;
 		bitmap = NULL;
 
 	}
@@ -1753,10 +1753,10 @@ void optionally_start_music(const LLString& music_url)
 {
 	if (gSavedSettings.getWarning("FirstStreamingMusic"))
 	{
-		void* data = (void*)strdup(music_url.c_str());
+		std::string* newstring = new std::string(music_url);
 		gViewerWindow->alertXml("ParcelCanPlayMusic",
 			callback_start_music,
-			(void*)data);
+			(void*)newstring);
 
 	}
 	else if (gSavedSettings.getBOOL("AudioStreamingMusic"))
@@ -1779,7 +1779,7 @@ void optionally_start_music(const LLString& music_url)
 
 void callback_start_music(S32 option, void* data)
 {
-	const char* music_url = (const char*)data;
+	std::string* music_url = (std::string*)data;
 
 	if (0 == option)
 	{
@@ -1787,7 +1787,7 @@ void callback_start_music(S32 option, void* data)
 		llinfos << "Starting first parcel music " << music_url << llendl;
 		if (gAudiop) 
 		{
-			gAudiop->startInternetStream(music_url);
+			gAudiop->startInternetStream(music_url->c_str());
 			LLMediaRemoteCtrl* ctrl = gOverlayBar->getMusicRemoteControl();
 			ctrl->setTransportState( LLMediaRemoteCtrl::Play, FALSE );
 		}
@@ -1799,7 +1799,7 @@ void callback_start_music(S32 option, void* data)
 
 	gSavedSettings.setWarning("FirstStreamingMusic", FALSE);
 
-	delete [] music_url;
+	delete music_url;
 	music_url = NULL;
 }
 
diff --git a/indra/newview/llviewerparceloverlay.cpp b/indra/newview/llviewerparceloverlay.cpp
index a159bacd16f10ad14e5c1df755c64829ba9d1cf8..f5d7aa1094516db4ffe1d7b28faef78458a38fd1 100644
--- a/indra/newview/llviewerparceloverlay.cpp
+++ b/indra/newview/llviewerparceloverlay.cpp
@@ -761,27 +761,6 @@ S32 LLViewerParcelOverlay::renderPropertyLines	()
 	const S32 GRID_STEP = S32( PARCEL_GRID_STEP_METERS );
 	const S32 vertex_per_edge = 3 + 2 * (GRID_STEP-1) + 3;
 
-	/* JC - Don't do this.  Unbinding AGP stalls the draw process,
-	dropping frame rate.  Not unbinding AGP causes random crashes
-	on nVidia cards due to binding non-AGP arrays.
-
-	gPipeline.unbindAGP();
-	glEnableClientState(GL_VERTEX_ARRAY);
-	glEnableClientState(GL_COLOR_ARRAY);
-	glVertexPointer(3, GL_FLOAT,         0, mVertexArray );
-	glColorPointer( 4, GL_UNSIGNED_BYTE, 0, mColorArray );
-
-	S32 i;
-	for (i = 0; i < mVertexCount; i += vertex_per_edge)
-	{
-		// Each edge is several vertices
-		glDrawArrays(GL_LINE_STRIP, i, vertex_per_edge);
-	}
-	
-	glDisableClientState(GL_COLOR_ARRAY);
-	glDisableClientState(GL_VERTEX_ARRAY);
-	*/
-
 	// Stomp the camera into two dimensions
 	LLVector3 camera_region = mRegion->getPosRegionFromGlobal( gAgent.getCameraPositionGlobal() );
 
diff --git a/indra/newview/llviewerpartsim.cpp b/indra/newview/llviewerpartsim.cpp
index db18b74d1ff4cae19caedb9317066dad7eddeee5..a6895aff2f38d18919c8323e698125b410d95b2d 100644
--- a/indra/newview/llviewerpartsim.cpp
+++ b/indra/newview/llviewerpartsim.cpp
@@ -13,6 +13,7 @@
 #include "llviewercontrol.h"
 
 #include "llagent.h"
+#include "llviewercamera.h"
 #include "llviewerobjectlist.h"
 #include "llviewerpartsource.h"
 #include "llviewerregion.h"
@@ -22,7 +23,7 @@
 
 const S32 MAX_PART_COUNT = 4096;
 
-const F32 PART_SIM_BOX_SIDE = 32.f;
+const F32 PART_SIM_BOX_SIDE = 16.f;
 const F32 PART_SIM_BOX_OFFSET = 0.5f*PART_SIM_BOX_SIDE;
 const F32 PART_SIM_BOX_RAD = 0.5f*F_SQRT3*PART_SIM_BOX_SIDE;
 
@@ -33,18 +34,28 @@ S32 LLViewerPartSim::sParticleCount = 0;
 
 U32 LLViewerPart::sNextPartID = 1;
 
+F32 calc_desired_size(LLVector3 pos, LLVector2 scale)
+{
+	F32 desired_size = (pos-gCamera->getOrigin()).magVec();
+	desired_size /= 4;
+	return llclamp(desired_size, scale.magVec()*0.5f, PART_SIM_BOX_SIDE*2);
+}
+
 LLViewerPart::LLViewerPart()
 {
+	LLMemType mt(LLMemType::MTYPE_PARTICLES);
 	mPartSourcep = NULL;
 }
 
 LLViewerPart::~LLViewerPart()
 {
+	LLMemType mt(LLMemType::MTYPE_PARTICLES);
 	mPartSourcep = NULL;
 }
 
 LLViewerPart &LLViewerPart::operator=(const LLViewerPart &part)
 {
+	LLMemType mt(LLMemType::MTYPE_PARTICLES);
 	mPartID = part.mPartID;
 	mFlags = part.mFlags;
 	mMaxAge = part.mMaxAge;
@@ -74,6 +85,7 @@ LLViewerPart &LLViewerPart::operator=(const LLViewerPart &part)
 
 void LLViewerPart::init(LLViewerPartSource *sourcep, LLViewerImage *imagep, LLVPCallback cb)
 {
+	LLMemType mt(LLMemType::MTYPE_PARTICLES);
 	mPartID = LLViewerPart::sNextPartID;
 	LLViewerPart::sNextPartID++;
 	mFlags = 0x00f;
@@ -96,8 +108,13 @@ void LLViewerPart::init(LLViewerPartSource *sourcep, LLViewerImage *imagep, LLVP
 
 LLViewerPartGroup::LLViewerPartGroup(const LLVector3 &center_agent, const F32 box_side)
 {
+	LLMemType mt(LLMemType::MTYPE_PARTICLES);
 	mVOPartGroupp = NULL;
+	mUniformParticles = TRUE;
+
 	mRegionp = gWorldPointer->getRegionFromPosAgent(center_agent);
+	llassert_always(center_agent.isFinite());
+	
 	if (!mRegionp)
 	{
 		//llwarns << "No region at position, using agent region!" << llendl;
@@ -106,28 +123,39 @@ LLViewerPartGroup::LLViewerPartGroup(const LLVector3 &center_agent, const F32 bo
 	mCenterAgent = center_agent;
 	mBoxRadius = F_SQRT3*box_side*0.5f;
 
-	LLVector3 rad_vec(box_side*0.5f, box_side*0.5f, box_side*0.5f);
-	rad_vec += LLVector3(0.001f, 0.001f, 0.001f);
-	mMinObjPos = mCenterAgent - rad_vec;
-	mMaxObjPos = mCenterAgent + rad_vec;
+	mVOPartGroupp = (LLVOPartGroup *)gObjectList.createObjectViewer(LLViewerObject::LL_VO_PART_GROUP, getRegion());
+	mVOPartGroupp->setViewerPartGroup(this);
+	mVOPartGroupp->setPositionAgent(getCenterAgent());
+	F32 scale = box_side * 0.5f;
+	mVOPartGroupp->setScale(LLVector3(scale,scale,scale));
+	gPipeline.addObject(mVOPartGroupp);
+
+	LLSpatialGroup* group = mVOPartGroupp->mDrawable->getSpatialGroup();
+
+	LLVector3 center(group->mOctreeNode->getCenter());
+	LLVector3 size(group->mOctreeNode->getSize());
+	size += LLVector3(0.01f, 0.01f, 0.01f);
+	mMinObjPos = center - size;
+	mMaxObjPos = center + size;
+
+	static U32 id_seed = 0;
+	mID = ++id_seed;
 }
 
 LLViewerPartGroup::~LLViewerPartGroup()
 {
+	LLMemType mt(LLMemType::MTYPE_PARTICLES);
 	cleanup();
-	S32 count = mParticles.count();
-	S32 i;
-
-	for (i = 0; i < count; i++)
-	{
-		mParticles[i].mPartSourcep = NULL;
-	}
-	mParticles.reset();
+	
+	S32 count = (S32) mParticles.size();
+	mParticles.clear();
+	
 	LLViewerPartSim::decPartCount(count);
 }
 
 void LLViewerPartGroup::cleanup()
 {
+	LLMemType mt(LLMemType::MTYPE_PARTICLES);
 	if (mVOPartGroupp)
 	{
 		if (!mVOPartGroupp->isDead())
@@ -138,8 +166,9 @@ void LLViewerPartGroup::cleanup()
 	}
 }
 
-BOOL LLViewerPartGroup::posInGroup(const LLVector3 &pos)
+BOOL LLViewerPartGroup::posInGroup(const LLVector3 &pos, const F32 desired_size)
 {
+	LLMemType mt(LLMemType::MTYPE_PARTICLES);
 	if ((pos.mV[VX] < mMinObjPos.mV[VX])
 		|| (pos.mV[VY] < mMinObjPos.mV[VY])
 		|| (pos.mV[VZ] < mMinObjPos.mV[VZ]))
@@ -154,29 +183,33 @@ BOOL LLViewerPartGroup::posInGroup(const LLVector3 &pos)
 		return FALSE;
 	}
 
+	if (desired_size > 0 && 
+		(desired_size < mBoxRadius*0.5f ||
+		desired_size > mBoxRadius*2.f))
+	{
+		return FALSE;
+	}
+
 	return TRUE;
 }
 
 
-BOOL LLViewerPartGroup::addPart(LLViewerPart &part)
+BOOL LLViewerPartGroup::addPart(LLViewerPart* part, F32 desired_size)
 {
-	if (!posInGroup(part.mPosAgent) || 
-		(mVOPartGroupp.notNull() && (part.mImagep != mVOPartGroupp->getTEImage(0))))
+	LLMemType mt(LLMemType::MTYPE_PARTICLES);
+	BOOL uniform_part = part->mScale.mV[0] == part->mScale.mV[1] && 
+					!(part->mFlags & LLPartData::LL_PART_FOLLOW_VELOCITY_MASK);
+
+	if (!posInGroup(part->mPosAgent, desired_size) ||
+		(mUniformParticles && !uniform_part) ||
+		(!mUniformParticles && uniform_part))
 	{
 		return FALSE;
 	}
 
-	if (!mVOPartGroupp)
-	{
-		mVOPartGroupp = (LLVOPartGroup *)gObjectList.createObjectViewer(LLViewerObject::LL_VO_PART_GROUP, getRegion());
-		mVOPartGroupp->setViewerPartGroup(this);
-		mVOPartGroupp->setPositionAgent(getCenterAgent());
-		mVOPartGroupp->setScale(LLVector3(PART_SIM_BOX_SIDE, PART_SIM_BOX_SIDE, PART_SIM_BOX_SIDE));
-		mVOPartGroupp->setTEImage(0, part.mImagep);
-		gPipeline.addObject(mVOPartGroupp);
-	}
+	gPipeline.markRebuild(mVOPartGroupp->mDrawable, LLDrawable::REBUILD_ALL, TRUE);
 	
-	mParticles.put(part);
+	mParticles.push_back(part);
 	LLViewerPartSim::incPartCount(1);
 	return TRUE;
 }
@@ -184,33 +217,29 @@ BOOL LLViewerPartGroup::addPart(LLViewerPart &part)
 
 void LLViewerPartGroup::removePart(const S32 part_num)
 {
+	LLMemType mt(LLMemType::MTYPE_PARTICLES);
 	// Remove the entry for the particle we just deleted.
-	LLPointer<LLViewerPartSource> ps = mParticles[mParticles.count() - 1].mPartSourcep;
-
-	mParticles[mParticles.count() - 1].mPartSourcep = NULL;
-	mParticles.remove(part_num);
-	if (part_num < mParticles.count())
+	mParticles.erase(mParticles.begin() + part_num);
+	if (mVOPartGroupp.notNull())
 	{
-		mParticles[part_num].mPartSourcep = ps;
+		gPipeline.markRebuild(mVOPartGroupp->mDrawable, LLDrawable::REBUILD_ALL, TRUE);
 	}
-
 	LLViewerPartSim::decPartCount(1);
 }
 
-
 void LLViewerPartGroup::updateParticles(const F32 dt)
 {
+	LLMemType mt(LLMemType::MTYPE_PARTICLES);
 	S32 i, count;
-
-
+	
 	LLVector3 gravity(0.f, 0.f, -9.8f);
 
 	LLViewerRegion *regionp = getRegion();
-	count = mParticles.count();
+	count = (S32) mParticles.size();
 	for (i = 0; i < count; i++)
 	{
 		LLVector3 a(0.f, 0.f, 0.f);
-		LLViewerPart &part = mParticles[i];
+		LLViewerPart& part = *((LLViewerPart*) mParticles[i]);
 
 		// Update current time
 		const F32 cur_time = part.mLastUpdateTime + dt;
@@ -252,8 +281,6 @@ void LLViewerPartGroup::updateParticles(const F32 dt)
 
 			part.mVelocity *= (1.f - step);
 			part.mVelocity += step*delta_pos;
-			//part.mPosAgent *= 1.f - to_target_frac;
-			//part.mPosAgent += to_target_frac*part.mPartSourcep->mTargetPosAgent;
 		}
 
 
@@ -322,18 +349,22 @@ void LLViewerPartGroup::updateParticles(const F32 dt)
 			i--;
 			count--;
 		}
-		else if (!posInGroup(part.mPosAgent))
+		else 
 		{
-			// Transfer particles between groups
-			gWorldPointer->mPartSim.put(part);
-			removePart(i);
-			i--;
-			count--;
+			F32 desired_size = calc_desired_size(part.mPosAgent, part.mScale);
+			if (!posInGroup(part.mPosAgent, desired_size))
+			{
+				// Transfer particles between groups
+				gWorldPointer->mPartSim.put(&part);
+				removePart(i);
+				i--;
+				count--;
+			}
 		}
 	}
 
 	// Kill the viewer object if this particle group is empty
-	if (!mParticles.count())
+	if (mParticles.empty())
 	{
 		gObjectList.killObject(mVOPartGroupp);
 		mVOPartGroupp = NULL;
@@ -343,15 +374,16 @@ void LLViewerPartGroup::updateParticles(const F32 dt)
 
 void LLViewerPartGroup::shift(const LLVector3 &offset)
 {
+	LLMemType mt(LLMemType::MTYPE_PARTICLES);
 	mCenterAgent += offset;
 	mMinObjPos += offset;
 	mMaxObjPos += offset;
 
-	S32 count = mParticles.count();
+	S32 count = (S32) mParticles.size();
 	S32 i;
 	for (i = 0; i < count; i++)
 	{
-		mParticles[i].mPosAgent += offset;
+		mParticles[i]->mPosAgent += offset;
 	}
 }
 
@@ -365,34 +397,34 @@ void LLViewerPartGroup::shift(const LLVector3 &offset)
 
 LLViewerPartSim::LLViewerPartSim()
 {
+	LLMemType mt(LLMemType::MTYPE_PARTICLES);
 	sMaxParticleCount = gSavedSettings.getS32("RenderMaxPartCount");
+	static U32 id_seed = 0;
+	mID = ++id_seed;
 }
 
 
 LLViewerPartSim::~LLViewerPartSim()
 {
+	LLMemType mt(LLMemType::MTYPE_PARTICLES);
 	S32 i;
 	S32 count;
 
 	// Kill all of the groups (and particles)
-	count = mViewerPartGroups.count();
+	count = (S32) mViewerPartGroups.size();
 	for (i = 0; i < count; i++)
 	{
 		delete mViewerPartGroups[i];
 	}
-	mViewerPartGroups.reset();
+	mViewerPartGroups.clear();
 
 	// Kill all of the sources 
-	count = mViewerPartSources.count();
-	for (i = 0; i < count; i++)
-	{
-		mViewerPartSources[i] = NULL;
-	}
-	mViewerPartSources.reset();
+	mViewerPartSources.clear();
 }
 
 BOOL LLViewerPartSim::shouldAddPart()
 {
+	LLMemType mt(LLMemType::MTYPE_PARTICLES);
 	if (sParticleCount > 0.75f*sMaxParticleCount)
 	{
 
@@ -413,33 +445,35 @@ BOOL LLViewerPartSim::shouldAddPart()
 	return TRUE;
 }
 
-void LLViewerPartSim::addPart(LLViewerPart &part)
+void LLViewerPartSim::addPart(LLViewerPart* part)
 {
+	LLMemType mt(LLMemType::MTYPE_PARTICLES);
 	if (sParticleCount < MAX_PART_COUNT)
 	{
 		put(part);
 	}
 }
 
-LLViewerPartGroup *LLViewerPartSim::put(LLViewerPart &part)
+
+LLViewerPartGroup *LLViewerPartSim::put(LLViewerPart* part)
 {
+	LLMemType mt(LLMemType::MTYPE_PARTICLES);
 	const F32 MAX_MAG = 1000000.f*1000000.f; // 1 million
-	if (part.mPosAgent.magVecSquared() > MAX_MAG)
+	if (part->mPosAgent.magVecSquared() > MAX_MAG || !part->mPosAgent.isFinite())
 	{
-#ifndef LL_RELEASE_FOR_DOWNLOAD
+#if !LL_RELEASE_FOR_DOWNLOAD
 		llwarns << "LLViewerPartSim::put Part out of range!" << llendl;
-		llwarns << part.mPosAgent << llendl;
+		llwarns << part->mPosAgent << llendl;
 #endif
 		return NULL;
 	}
+	
+	F32 desired_size = calc_desired_size(part->mPosAgent, part->mScale);
 
-	S32 i;
-	S32 count;
-
-	count = mViewerPartGroups.count();
-	for (i = 0; i < count; i++)
+	S32 count = (S32) mViewerPartGroups.size();
+	for (S32 i = 0; i < count; i++)
 	{
-		if (mViewerPartGroups[i]->addPart(part))
+		if (mViewerPartGroups[i]->addPart(part, desired_size))
 		{
 			// We found a spatial group that we fit into, add us and exit
 			return mViewerPartGroups[i];
@@ -448,43 +482,27 @@ LLViewerPartGroup *LLViewerPartSim::put(LLViewerPart &part)
 
 	// Hmm, we didn't fit in any of the existing spatial groups
 	// Create a new one...
-	LLViewerPartGroup *groupp = createViewerPartGroup(part.mPosAgent);
+	llassert_always(part->mPosAgent.isFinite());
+	LLViewerPartGroup *groupp = createViewerPartGroup(part->mPosAgent, desired_size);
+	groupp->mUniformParticles = (part->mScale.mV[0] == part->mScale.mV[1] && 
+							!(part->mFlags & LLPartData::LL_PART_FOLLOW_VELOCITY_MASK));
 	if (!groupp->addPart(part))
 	{
 		llwarns << "LLViewerPartSim::put - Particle didn't go into its box!" << llendl;
 		llinfos << groupp->getCenterAgent() << llendl;
-		llinfos << part.mPosAgent << llendl;
+		llinfos << part->mPosAgent << llendl;
 		return NULL;
 	}
 	return groupp;
 }
 
-LLViewerPartGroup *LLViewerPartSim::createViewerPartGroup(const LLVector3 &pos_agent)
+LLViewerPartGroup *LLViewerPartSim::createViewerPartGroup(const LLVector3 &pos_agent, const F32 desired_size)
 {
-	F32 x_origin = ((S32)(pos_agent.mV[VX]/PART_SIM_BOX_SIDE))*PART_SIM_BOX_SIDE;
-	if (x_origin > pos_agent.mV[VX])
-	{
-		x_origin -= PART_SIM_BOX_SIDE;
-	}
-
-	F32 y_origin = ((S32)(pos_agent.mV[VY]/PART_SIM_BOX_SIDE))*PART_SIM_BOX_SIDE;
-	if (y_origin > pos_agent.mV[VY])
-	{
-		y_origin -= PART_SIM_BOX_SIDE;
-	}
-
-	F32 z_origin = ((S32)(pos_agent.mV[VZ]/PART_SIM_BOX_SIDE))*PART_SIM_BOX_SIDE;
-	if (z_origin > pos_agent.mV[VZ])
-	{
-		z_origin -= PART_SIM_BOX_SIDE;
-	}
-
-	LLVector3 group_center(x_origin + PART_SIM_BOX_OFFSET,
-							y_origin + PART_SIM_BOX_OFFSET,
-							z_origin + PART_SIM_BOX_OFFSET);
-
-	LLViewerPartGroup *groupp = new LLViewerPartGroup(group_center, PART_SIM_BOX_SIDE);
-	mViewerPartGroups.put(groupp);
+	LLMemType mt(LLMemType::MTYPE_PARTICLES);
+	//find a box that has a center position divisible by PART_SIM_BOX_SIDE that encompasses
+	//pos_agent
+	LLViewerPartGroup *groupp = new LLViewerPartGroup(pos_agent, desired_size);
+	mViewerPartGroups.push_back(groupp);
 	return groupp;
 }
 
@@ -494,7 +512,7 @@ void LLViewerPartSim::shift(const LLVector3 &offset)
 	S32 i;
 	S32 count;
 
-	count = mViewerPartSources.count();
+	count = (S32) mViewerPartSources.size();
 	for (i = 0; i < count; i++)
 	{
 		mViewerPartSources[i]->mPosAgent += offset;
@@ -502,13 +520,20 @@ void LLViewerPartSim::shift(const LLVector3 &offset)
 		mViewerPartSources[i]->mLastUpdatePosAgent += offset;
 	}
 
-	count = mViewerPartGroups.count();
+	count = (S32) mViewerPartGroups.size();
 	for (i = 0; i < count; i++)
 	{
 		mViewerPartGroups[i]->shift(offset);
 	}
 }
 
+S32 dist_rate_func(F32 distance)
+{
+	//S32 dist = (S32) sqrtf(distance);
+	//dist /= 2;
+	//return llmax(dist,1);
+	return 1;
+}
 
 void LLViewerPartSim::updateSimulation()
 {
@@ -523,13 +548,15 @@ void LLViewerPartSim::updateSimulation()
 		return;
 	}
 
+	LLFastTimer ftm(LLFastTimer::FTM_SIMULATE_PARTICLES);
+
 	// Start at a random particle system so the same
 	// particle system doesn't always get first pick at the
 	// particles.  Theoretically we'd want to do this in distance
 	// order or something, but sorting particle sources will be a big
 	// pain.
 	S32 i;
-	S32 count = mViewerPartSources.count();
+	S32 count = (S32) mViewerPartSources.size();
 	S32 start = (S32)ll_frand((F32)count);
 	S32 dir = 1;
 	if (ll_frand() > 0.5f)
@@ -551,12 +578,24 @@ void LLViewerPartSim::updateSimulation()
 
 		if (!mViewerPartSources[i]->isDead())
 		{
-			mViewerPartSources[i]->update(dt);
+			LLViewerObject* source_object = mViewerPartSources[i]->mSourceObjectp;
+			if (source_object && source_object->mDrawable.notNull())
+			{
+                S32 dist = dist_rate_func(source_object->mDrawable->mDistanceWRTCamera);
+				if ((LLDrawable::getCurrentFrame()+mViewerPartSources[i]->mID)%dist == 0)
+				{
+					mViewerPartSources[i]->update(dt*dist);
+				}
+			}
+			else
+			{
+				mViewerPartSources[i]->update(dt);
+			}
 		}
 
 		if (mViewerPartSources[i]->isDead())
 		{
-			mViewerPartSources.remove(i);
+			mViewerPartSources.erase(mViewerPartSources.begin() + i);
 			count--;
 		}
 		else
@@ -567,16 +606,36 @@ void LLViewerPartSim::updateSimulation()
 	}
 
 
-	count = mViewerPartGroups.count();
+	count = (S32) mViewerPartGroups.size();
 	for (i = 0; i < count; i++)
 	{
-		mViewerPartGroups[i]->updateParticles(dt);
-		if (!mViewerPartGroups[i]->getCount())
+		LLViewerObject* vobj = mViewerPartGroups[i]->mVOPartGroupp;
+
+		S32 dist = vobj && !vobj->mDrawable->isState(LLDrawable::IN_REBUILD_Q1) ? 
+				dist_rate_func(vobj->mDrawable->mDistanceWRTCamera) : 1;
+		if (vobj)
 		{
-			delete mViewerPartGroups[i];
-			mViewerPartGroups.remove(i);
-			i--;
-			count--;
+			LLSpatialGroup* group = vobj->mDrawable->getSpatialGroup();
+			if (group && !group->isVisible()) // && !group->isState(LLSpatialGroup::OBJECT_DIRTY))
+			{
+				dist *= 8;
+			}
+		}
+
+		if ((LLDrawable::getCurrentFrame()+mViewerPartGroups[i]->mID)%dist == 0)
+		{
+			if (vobj)
+			{
+				gPipeline.markRebuild(vobj->mDrawable, LLDrawable::REBUILD_ALL, TRUE);
+			}
+			mViewerPartGroups[i]->updateParticles(dt*dist);
+			if (!mViewerPartGroups[i]->getCount())
+			{
+				delete mViewerPartGroups[i];
+				mViewerPartGroups.erase(mViewerPartGroups.begin() + i);
+				i--;
+				count--;
+			}
 		}
 	}
 	//llinfos << "Particles: " << sParticleCount << llendl;
@@ -585,42 +644,40 @@ void LLViewerPartSim::updateSimulation()
 
 void LLViewerPartSim::addPartSource(LLViewerPartSource *sourcep)
 {
+	LLMemType mt(LLMemType::MTYPE_PARTICLES);
 	if (!sourcep)
 	{
 		llwarns << "Null part source!" << llendl;
 		return;
 	}
-	mViewerPartSources.put(sourcep);
+	mViewerPartSources.push_back(sourcep);
 }
 
 
 void LLViewerPartSim::cleanupRegion(LLViewerRegion *regionp)
 {
-	S32 i, count;
-	count = mViewerPartGroups.count();
-	for (i = 0; i < count; i++)
+	LLMemType mt(LLMemType::MTYPE_PARTICLES);
+	for (group_list_t::iterator i = mViewerPartGroups.begin(); i != mViewerPartGroups.end(); )
 	{
-		if (mViewerPartGroups[i]->getRegion() == regionp)
+		group_list_t::iterator iter = i++;
+
+		if ((*iter)->getRegion() == regionp)
 		{
-			delete mViewerPartGroups[i];
-			mViewerPartGroups.remove(i);
-			i--;
-			count--;
+			i = mViewerPartGroups.erase(iter);			
 		}
 	}
 }
 
 void LLViewerPartSim::cleanMutedParticles(const LLUUID& task_id)
 {
-	S32 i;
-	S32 count = mViewerPartSources.count();
-	for (i = 0; i < count; ++i)
+	LLMemType mt(LLMemType::MTYPE_PARTICLES);
+	for (source_list_t::iterator i = mViewerPartSources.begin(); i != mViewerPartSources.end(); )
 	{
-		if (mViewerPartSources[i]->getOwnerUUID() == task_id)
+		source_list_t::iterator iter = i++;
+
+		if ((*iter)->getOwnerUUID() == task_id)
 		{
-			mViewerPartSources.remove(i);
-			i--;
-			count--;
+			i = mViewerPartSources.erase(iter);			
 		}
 	}
 }
diff --git a/indra/newview/llviewerpartsim.h b/indra/newview/llviewerpartsim.h
index 83c374a42e9db27cb0b527668c555568400abfb6..b0b9500ac6113eb91570e1fd1b08c6a5037eb79e 100644
--- a/indra/newview/llviewerpartsim.h
+++ b/indra/newview/llviewerpartsim.h
@@ -31,7 +31,7 @@ typedef void (*LLVPCallback)(LLViewerPart &part, const F32 dt);
 //
 
 
-class LLViewerPart : public LLPartData
+class LLViewerPart : public LLPartData, public LLRefCount
 {
 public:
 	LLViewerPart();
@@ -71,19 +71,26 @@ public:
 
 	void cleanup();
 
-	BOOL addPart(LLViewerPart &part);
+	BOOL addPart(LLViewerPart* part, const F32 desired_size = -1.f);
 	
 	void updateParticles(const F32 dt);
 
-	BOOL posInGroup(const LLVector3 &pos);
+	BOOL posInGroup(const LLVector3 &pos, const F32 desired_size = -1.f);
 
 	void shift(const LLVector3 &offset);
 
-	LLDynamicArray<LLViewerPart> mParticles;
+	typedef std::vector<LLPointer<LLViewerPart> > part_list_t;
+	part_list_t mParticles;
 
 	const LLVector3 &getCenterAgent() const		{ return mCenterAgent; }
-	S32 getCount() const					{ return mParticles.count(); }
+	S32 getCount() const					{ return (S32) mParticles.size(); }
 	LLViewerRegion *getRegion() const		{ return mRegionp; }
+
+	LLPointer<LLVOPartGroup> mVOPartGroupp;
+
+	BOOL mUniformParticles;
+	U32 mID;
+
 protected:
 	void removePart(const S32 part_num);
 
@@ -93,11 +100,9 @@ protected:
 	LLVector3 mMinObjPos;
 	LLVector3 mMaxObjPos;
 
-	LLPointer<LLVOPartGroup> mVOPartGroupp;
 	LLViewerRegion *mRegionp;
 };
 
-
 class LLViewerPartSim
 {
 public:
@@ -113,7 +118,7 @@ public:
 	void cleanupRegion(LLViewerRegion *regionp);
 
 	BOOL shouldAddPart(); // Just decides whether this particle should be added or not (for particle count capping)
-	void addPart(LLViewerPart &part);
+	void addPart(LLViewerPart* part);
 	void cleanMutedParticles(const LLUUID& task_id);
 
 	friend class LLViewerPartGroup;
@@ -125,15 +130,18 @@ public:
 	static void incPartCount(const S32 count)			{ sParticleCount += count; }
 	static void decPartCount(const S32 count)			{ sParticleCount -= count; }
 	
+	U32 mID;
+
 protected:
-	LLViewerPartGroup *createViewerPartGroup(const LLVector3 &pos_agent);
-	LLViewerPartGroup *put(LLViewerPart &part);
+	LLViewerPartGroup *createViewerPartGroup(const LLVector3 &pos_agent, const F32 desired_size);
+	LLViewerPartGroup *put(LLViewerPart* part);
 
 protected:
-	LLDynamicArray<LLViewerPartGroup *> mViewerPartGroups;
-	LLDynamicArrayPtr<LLPointer<LLViewerPartSource> > mViewerPartSources;
+	typedef std::vector<LLViewerPartGroup *> group_list_t;
+	typedef std::vector<LLPointer<LLViewerPartSource> > source_list_t;
+	group_list_t mViewerPartGroups;
+	source_list_t mViewerPartSources;
 	LLFrameTimer mSimulationTimer;
-
 	static S32 sMaxParticleCount;
 	static S32 sParticleCount;
 };
diff --git a/indra/newview/llviewerpartsource.cpp b/indra/newview/llviewerpartsource.cpp
index 981c5531c9daca706ef1599db5f6598a0e95e2dc..b14a82e3f1c1d6df73ad3ee5eac328b41e620059 100644
--- a/indra/newview/llviewerpartsource.cpp
+++ b/indra/newview/llviewerpartsource.cpp
@@ -18,6 +18,7 @@
 #include "llviewerobjectlist.h"
 #include "llvoavatar.h"
 #include "llworld.h"
+#include "pipeline.h"
 
 LLViewerPartSource::LLViewerPartSource(const U32 type) :
 	mType(type),
@@ -26,6 +27,8 @@ LLViewerPartSource::LLViewerPartSource(const U32 type) :
 	mLastUpdateTime = 0.f;
 	mLastPartTime = 0.f;
 	mIsDead = FALSE;
+	static U32 id_seed = 0;
+	mID = ++id_seed;
 }
 
 void LLViewerPartSource::setDead()
@@ -48,6 +51,7 @@ void LLViewerPartSource::update(const F32 dt)
 LLViewerPartSourceScript::LLViewerPartSourceScript(LLViewerObject *source_objp) :
 	LLViewerPartSource(LL_PART_SOURCE_SCRIPT)
 {
+	LLMemType mt(LLMemType::MTYPE_PARTICLES);
 	llassert(source_objp);
 	mSourceObjectp = source_objp;
 	mPosAgent = mSourceObjectp->getPositionAgent();
@@ -61,18 +65,18 @@ LLViewerPartSourceScript::LLViewerPartSourceScript(LLViewerObject *source_objp)
 
 void LLViewerPartSourceScript::setDead()
 {
+	LLMemType mt(LLMemType::MTYPE_PARTICLES);
 	mIsDead = TRUE;
 	mSourceObjectp = NULL;
 	mTargetObjectp = NULL;
 }
 
-
-
 void LLViewerPartSourceScript::update(const F32 dt)
 {
+	LLMemType mt(LLMemType::MTYPE_PARTICLES);
 	F32 old_update_time = mLastUpdateTime;
 	mLastUpdateTime += dt;
-
+	
 	F32 dt_update = mLastUpdateTime - mLastPartTime;
 
 	// Update this for objects which have the follow flag set...
@@ -123,6 +127,17 @@ void LLViewerPartSourceScript::update(const F32 dt)
 		return;
 	}
 
+
+	if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_PARTICLES))
+	{
+		if (mSourceObjectp.notNull())
+		{
+			std::ostringstream ostr;
+			ostr << mPartSysData;
+			mSourceObjectp->setDebugText(ostr.str());
+		}
+	}
+
 	BOOL first_run = FALSE;
 	if (old_update_time <= 0.f)
 	{
@@ -134,21 +149,7 @@ void LLViewerPartSourceScript::update(const F32 dt)
 	while ((dt_update > mPartSysData.mBurstRate) || first_run)
 	{
 		first_run = FALSE;
-		LLViewerPart part;
-
-		part.init(this, mImagep, NULL);
-		part.mFlags = mPartSysData.mPartData.mFlags;
-		part.mMaxAge = mPartSysData.mPartData.mMaxAge;
-		part.mStartColor = mPartSysData.mPartData.mStartColor;
-		part.mEndColor = mPartSysData.mPartData.mEndColor;
-		part.mColor = part.mStartColor;
-
-		part.mStartScale = mPartSysData.mPartData.mStartScale;
-		part.mEndScale = mPartSysData.mPartData.mEndScale;
-		part.mScale = part.mStartScale;
-
-		part.mAccel = mPartSysData.mPartAccel;
-
+		
 		// Update the rotation of the particle source by the angular velocity
 		// First check to see if there is still an angular velocity.
 		F32 angular_velocity_mag = mPartSysData.mAngularVelocity.magVec();
@@ -181,14 +182,29 @@ void LLViewerPartSourceScript::update(const F32 dt)
 				continue;
 			}
 
+			LLPointer<LLViewerPart> part = new LLViewerPart();
+
+			part->init(this, mImagep, NULL);
+			part->mFlags = mPartSysData.mPartData.mFlags;
+			part->mMaxAge = mPartSysData.mPartData.mMaxAge;
+			part->mStartColor = mPartSysData.mPartData.mStartColor;
+			part->mEndColor = mPartSysData.mPartData.mEndColor;
+			part->mColor = part->mStartColor;
+
+			part->mStartScale = mPartSysData.mPartData.mStartScale;
+			part->mEndScale = mPartSysData.mPartData.mEndScale;
+			part->mScale = part->mStartScale;
+
+			part->mAccel = mPartSysData.mPartAccel;
+
 			if (mPartSysData.mPattern & LLPartSysData::LL_PART_SRC_PATTERN_DROP)
 			{
-				part.mPosAgent = mPosAgent;
-				part.mVelocity.setVec(0.f, 0.f, 0.f);
+				part->mPosAgent = mPosAgent;
+				part->mVelocity.setVec(0.f, 0.f, 0.f);
 			}
 			else if (mPartSysData.mPattern & LLPartSysData::LL_PART_SRC_PATTERN_EXPLODE)
 			{
-				part.mPosAgent = mPosAgent;
+				part->mPosAgent = mPosAgent;
 				LLVector3 part_dir_vector;
 
 				F32 mvs;
@@ -202,19 +218,18 @@ void LLViewerPartSourceScript::update(const F32 dt)
 				while ((mvs > 1.f) || (mvs < 0.01f));
 
 				part_dir_vector.normVec();
-				part.mPosAgent += mPartSysData.mBurstRadius*part_dir_vector;
-				part.mVelocity = part_dir_vector;
+				part->mPosAgent += mPartSysData.mBurstRadius*part_dir_vector;
+				part->mVelocity = part_dir_vector;
 				F32 speed = mPartSysData.mBurstSpeedMin + ll_frand(mPartSysData.mBurstSpeedMax - mPartSysData.mBurstSpeedMin);
-				part.mVelocity *= speed;
+				part->mVelocity *= speed;
 			}
 			else if (mPartSysData.mPattern & LLPartSysData::LL_PART_SRC_PATTERN_ANGLE
 				|| mPartSysData.mPattern & LLPartSysData::LL_PART_SRC_PATTERN_ANGLE_CONE)
-			{
-				part.mPosAgent = mPosAgent;
+			{				
+				part->mPosAgent = mPosAgent;
 				
 				// original implemenetation for part_dir_vector was just:					
 				LLVector3 part_dir_vector(0.0, 0.0, 1.0);
-
 				// params from the script...
 				// outer = outer cone angle
 				// inner = inner cone angle
@@ -224,13 +239,11 @@ void LLViewerPartSourceScript::update(const F32 dt)
 
 				// generate a random angle within the given space...
 				F32 angle = innerAngle + ll_frand(outerAngle - innerAngle);
-
 				// split which side it will go on randomly...
 				if (ll_frand() < 0.5) 
 				{
 					angle = -angle;
 				}
-				
 				// Both patterns rotate around the x-axis first:
 				part_dir_vector.rotVec(angle, 1.0, 0.0, 0.0);
 
@@ -239,31 +252,32 @@ void LLViewerPartSourceScript::update(const F32 dt)
 				{
 					part_dir_vector.rotVec(ll_frand(4*F_PI), 0.0, 0.0, 1.0);
 				}
-					
+								
 				// Only apply this rotation if using the deprecated angles. 
 				if (! (mPartSysData.mFlags & LLPartSysData::LL_PART_USE_NEW_ANGLE))
 				{
 					// Deprecated...
 					part_dir_vector.rotVec(outerAngle, 1.0, 0.0, 0.0);
 				}
-
+				
 				if (mSourceObjectp)
 				{
 					part_dir_vector = part_dir_vector * mSourceObjectp->getRenderRotation();
 				}
+								
 				part_dir_vector = part_dir_vector * mRotation;
+								
+				part->mPosAgent += mPartSysData.mBurstRadius*part_dir_vector;
 
-				part.mPosAgent += mPartSysData.mBurstRadius*part_dir_vector;
-
-				part.mVelocity = part_dir_vector;
+				part->mVelocity = part_dir_vector;
 
 				F32 speed = mPartSysData.mBurstSpeedMin + ll_frand(mPartSysData.mBurstSpeedMax - mPartSysData.mBurstSpeedMin);
-				part.mVelocity *= speed;
+				part->mVelocity *= speed;
 			}
 			else
 			{
-				part.mPosAgent = mPosAgent;
-				part.mVelocity.setVec(0.f, 0.f, 0.f);
+				part->mPosAgent = mPosAgent;
+				part->mVelocity.setVec(0.f, 0.f, 0.f);
 				//llwarns << "Unknown source pattern " << (S32)mPartSysData.mPattern << llendl;
 			}
 
@@ -278,6 +292,7 @@ void LLViewerPartSourceScript::update(const F32 dt)
 // static
 LLViewerPartSourceScript *LLViewerPartSourceScript::unpackPSS(LLViewerObject *source_objp, LLViewerPartSourceScript *pssp, const S32 block_num)
 {
+	LLMemType mt(LLMemType::MTYPE_PARTICLES);
 	if (!pssp)
 	{
 		if (LLPartSysData::isNullPS(block_num))
@@ -319,6 +334,7 @@ LLViewerPartSourceScript *LLViewerPartSourceScript::unpackPSS(LLViewerObject *so
 
 LLViewerPartSourceScript *LLViewerPartSourceScript::unpackPSS(LLViewerObject *source_objp, LLViewerPartSourceScript *pssp, LLDataPacker &dp)
 {
+	LLMemType mt(LLMemType::MTYPE_PARTICLES);
 	if (!pssp)
 	{
 		LLViewerPartSourceScript *new_pssp = new LLViewerPartSourceScript(source_objp);
@@ -350,11 +366,13 @@ LLViewerPartSourceScript *LLViewerPartSourceScript::unpackPSS(LLViewerObject *so
 
 void LLViewerPartSourceScript::setImage(LLViewerImage *imagep)
 {
+	LLMemType mt(LLMemType::MTYPE_PARTICLES);
 	mImagep = imagep;
 }
 
 void LLViewerPartSourceScript::setTargetObject(LLViewerObject *objp)
 {
+	LLMemType mt(LLMemType::MTYPE_PARTICLES);
 	mTargetObjectp = objp;
 }
 
@@ -370,6 +388,7 @@ LLViewerPartSourceSpiral::LLViewerPartSourceSpiral(const LLVector3 &pos) :
 
 void LLViewerPartSourceSpiral::setDead()
 {
+	LLMemType mt(LLMemType::MTYPE_PARTICLES);
 	mIsDead = TRUE;
 	mSourceObjectp = NULL;
 }
@@ -377,6 +396,7 @@ void LLViewerPartSourceSpiral::setDead()
 
 void LLViewerPartSourceSpiral::updatePart(LLViewerPart &part, const F32 dt)
 {
+	LLMemType mt(LLMemType::MTYPE_PARTICLES);
 	F32 frac = part.mLastUpdateTime/part.mMaxAge;
 
 	LLVector3 center_pos;
@@ -401,6 +421,7 @@ void LLViewerPartSourceSpiral::updatePart(LLViewerPart &part, const F32 dt)
 
 void LLViewerPartSourceSpiral::update(const F32 dt)
 {
+	LLMemType mt(LLMemType::MTYPE_PARTICLES);
 	if (!mImagep)
 	{
 		LLUUID id;
@@ -429,18 +450,18 @@ void LLViewerPartSourceSpiral::update(const F32 dt)
 		{
 			mPosAgent = mSourceObjectp->getRenderPosition();
 		}
-		LLViewerPart part;
-		part.init(this, mImagep, updatePart);
-		part.mStartColor = mColor;
-		part.mEndColor = mColor;
-		part.mEndColor.mV[3] = 0.f;
-		part.mPosAgent = mPosAgent;
-		part.mMaxAge = 1.f;
-		part.mFlags = LLViewerPart::LL_PART_INTERP_COLOR_MASK;
-		part.mLastUpdateTime = 0.f;
-		part.mScale.mV[0] = 0.25f;
-		part.mScale.mV[1] = 0.25f;
-		part.mParameter = ll_frand(F_TWO_PI);
+		LLPointer<LLViewerPart> part = new LLViewerPart();
+		part->init(this, mImagep, updatePart);
+		part->mStartColor = mColor;
+		part->mEndColor = mColor;
+		part->mEndColor.mV[3] = 0.f;
+		part->mPosAgent = mPosAgent;
+		part->mMaxAge = 1.f;
+		part->mFlags = LLViewerPart::LL_PART_INTERP_COLOR_MASK;
+		part->mLastUpdateTime = 0.f;
+		part->mScale.mV[0] = 0.25f;
+		part->mScale.mV[1] = 0.25f;
+		part->mParameter = ll_frand(F_TWO_PI);
 
 		gWorldPointer->mPartSim.addPart(part);
 	}
@@ -448,6 +469,7 @@ void LLViewerPartSourceSpiral::update(const F32 dt)
 
 void LLViewerPartSourceSpiral::setSourceObject(LLViewerObject *objp)
 {
+	LLMemType mt(LLMemType::MTYPE_PARTICLES);
 	mSourceObjectp = objp;
 }
 
@@ -471,6 +493,7 @@ LLViewerPartSourceBeam::~LLViewerPartSourceBeam()
 
 void LLViewerPartSourceBeam::setDead()
 {
+	LLMemType mt(LLMemType::MTYPE_PARTICLES);
 	mIsDead = TRUE;
 	mSourceObjectp = NULL;
 	mTargetObjectp = NULL;
@@ -484,6 +507,7 @@ void LLViewerPartSourceBeam::setColor(const LLColor4 &color)
 
 void LLViewerPartSourceBeam::updatePart(LLViewerPart &part, const F32 dt)
 {
+	LLMemType mt(LLMemType::MTYPE_PARTICLES);
 	F32 frac = part.mLastUpdateTime/part.mMaxAge;
 
 	LLViewerPartSource *ps = (LLViewerPartSource*)part.mPartSourcep;
@@ -528,7 +552,7 @@ void LLViewerPartSourceBeam::updatePart(LLViewerPart &part, const F32 dt)
 
 void LLViewerPartSourceBeam::update(const F32 dt)
 {
-
+	LLMemType mt(LLMemType::MTYPE_PARTICLES);
 	const F32 RATE = 0.025f;
 
 	mLastUpdateTime += dt;
@@ -576,25 +600,25 @@ void LLViewerPartSourceBeam::update(const F32 dt)
 			mImagep = gImageList.getImage(id);
 		}
 
-		LLViewerPart part;
-		part.init(this, mImagep, NULL);
+		LLPointer<LLViewerPart> part = new LLViewerPart();
+		part->init(this, mImagep, NULL);
 
-		part.mFlags = LLPartData::LL_PART_INTERP_COLOR_MASK |
+		part->mFlags = LLPartData::LL_PART_INTERP_COLOR_MASK |
 						LLPartData::LL_PART_INTERP_SCALE_MASK |
 						LLPartData::LL_PART_TARGET_POS_MASK |
 						LLPartData::LL_PART_FOLLOW_VELOCITY_MASK;
-		part.mMaxAge = 0.5f;
-		part.mStartColor = mColor;
-		part.mEndColor = part.mStartColor;
-		part.mEndColor.mV[3] = 0.4f;
-		part.mColor = part.mStartColor;
+		part->mMaxAge = 0.5f;
+		part->mStartColor = mColor;
+		part->mEndColor = part->mStartColor;
+		part->mEndColor.mV[3] = 0.4f;
+		part->mColor = part->mStartColor;
 
-		part.mStartScale = LLVector2(0.1f, 0.1f);
-		part.mEndScale = LLVector2(0.1f, 0.1f);
-		part.mScale = part.mStartScale;
+		part->mStartScale = LLVector2(0.1f, 0.1f);
+		part->mEndScale = LLVector2(0.1f, 0.1f);
+		part->mScale = part->mStartScale;
 
-		part.mPosAgent = mPosAgent;
-		part.mVelocity = mTargetPosAgent - mPosAgent;
+		part->mPosAgent = mPosAgent;
+		part->mVelocity = mTargetPosAgent - mPosAgent;
 
 		gWorldPointer->mPartSim.addPart(part);
 	}
@@ -602,11 +626,13 @@ void LLViewerPartSourceBeam::update(const F32 dt)
 
 void LLViewerPartSourceBeam::setSourceObject(LLViewerObject* objp)
 {
+	LLMemType mt(LLMemType::MTYPE_PARTICLES);
 	mSourceObjectp = objp;
 }
 
 void LLViewerPartSourceBeam::setTargetObject(LLViewerObject* objp)
 {
+	LLMemType mt(LLMemType::MTYPE_PARTICLES);
 	mTargetObjectp = objp;
 }
 
@@ -621,6 +647,7 @@ LLViewerPartSourceChat::LLViewerPartSourceChat(const LLVector3 &pos) :
 
 void LLViewerPartSourceChat::setDead()
 {
+	LLMemType mt(LLMemType::MTYPE_PARTICLES);
 	mIsDead = TRUE;
 	mSourceObjectp = NULL;
 }
@@ -628,6 +655,7 @@ void LLViewerPartSourceChat::setDead()
 
 void LLViewerPartSourceChat::updatePart(LLViewerPart &part, const F32 dt)
 {
+	LLMemType mt(LLMemType::MTYPE_PARTICLES);
 	F32 frac = part.mLastUpdateTime/part.mMaxAge;
 
 	LLVector3 center_pos;
@@ -652,6 +680,7 @@ void LLViewerPartSourceChat::updatePart(LLViewerPart &part, const F32 dt)
 
 void LLViewerPartSourceChat::update(const F32 dt)
 {
+	LLMemType mt(LLMemType::MTYPE_PARTICLES);
 	if (!mImagep)
 	{
 		LLUUID id;
@@ -690,18 +719,18 @@ void LLViewerPartSourceChat::update(const F32 dt)
 		{
 			mPosAgent = mSourceObjectp->getRenderPosition();
 		}
-		LLViewerPart part;
-		part.init(this, mImagep, updatePart);
-		part.mStartColor = mColor;
-		part.mEndColor = mColor;
-		part.mEndColor.mV[3] = 0.f;
-		part.mPosAgent = mPosAgent;
-		part.mMaxAge = 1.f;
-		part.mFlags = LLViewerPart::LL_PART_INTERP_COLOR_MASK;
-		part.mLastUpdateTime = 0.f;
-		part.mScale.mV[0] = 0.25f;
-		part.mScale.mV[1] = 0.25f;
-		part.mParameter = ll_frand(F_TWO_PI);
+		LLPointer<LLViewerPart> part = new LLViewerPart();
+		part->init(this, mImagep, updatePart);
+		part->mStartColor = mColor;
+		part->mEndColor = mColor;
+		part->mEndColor.mV[3] = 0.f;
+		part->mPosAgent = mPosAgent;
+		part->mMaxAge = 1.f;
+		part->mFlags = LLViewerPart::LL_PART_INTERP_COLOR_MASK;
+		part->mLastUpdateTime = 0.f;
+		part->mScale.mV[0] = 0.25f;
+		part->mScale.mV[1] = 0.25f;
+		part->mParameter = ll_frand(F_TWO_PI);
 
 		gWorldPointer->mPartSim.addPart(part);
 	}
@@ -709,6 +738,7 @@ void LLViewerPartSourceChat::update(const F32 dt)
 
 void LLViewerPartSourceChat::setSourceObject(LLViewerObject *objp)
 {
+	LLMemType mt(LLMemType::MTYPE_PARTICLES);
 	mSourceObjectp = objp;
 }
 
diff --git a/indra/newview/llviewerpartsource.h b/indra/newview/llviewerpartsource.h
index 05fa007eaddab3cc8a2c06d97d3568f334cf12af..4bd577b7a54b29dd3c2e023debdf1d0fab25690f 100644
--- a/indra/newview/llviewerpartsource.h
+++ b/indra/newview/llviewerpartsource.h
@@ -51,13 +51,16 @@ public:
 	LLVector3	mPosAgent; // Location of the particle source
 	LLVector3	mTargetPosAgent; // Location of the target position
 	LLVector3	mLastUpdatePosAgent;
+	LLPointer<LLViewerObject>	mSourceObjectp;
+	U32 mID;
+
 protected:
 	U32			mType;
 	BOOL		mIsDead;
 	F32			mLastUpdateTime;
 	F32			mLastPartTime;
 	LLUUID		mOwnerUUID;
-
+	
 	// Particle information
 	U32			mPartFlags; // Flags for the particle
 };
@@ -96,7 +99,6 @@ public:
 protected:
 	LLQuaternion				mRotation;			// Current rotation for particle source
 	LLPointer<LLViewerImage>	mImagep;			// Cached image pointer of the mPartSysData UUID
-	LLPointer<LLViewerObject>	mSourceObjectp;		// Source object that this particle system is attached to
 	LLPointer<LLViewerObject>	mTargetObjectp;		// Target object for the particle source
 };
 
@@ -122,7 +124,6 @@ public:
 	LLColor4 mColor;
 protected:
 	LLPointer<LLViewerImage>	mImagep;
-	LLPointer<LLViewerObject>	mSourceObjectp;
 	LLVector3d mLKGSourcePosGlobal;
 };
 
@@ -150,7 +151,6 @@ public:
 
 	static void updatePart(LLViewerPart &part, const F32 dt);
 	LLPointer<LLViewerImage>	mImagep;
-	LLPointer<LLViewerObject>	mSourceObjectp;
 	LLPointer<LLViewerObject>	mTargetObjectp;
 	LLVector3d		mLKGTargetPosGlobal;
 	LLColor4 mColor;
@@ -180,7 +180,6 @@ public:
 	LLColor4 mColor;
 protected:
 	LLPointer<LLViewerImage>	mImagep;
-	LLPointer<LLViewerObject>	mSourceObjectp;
 	LLVector3d mLKGSourcePosGlobal;
 };
 
diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp
index fa68e25b0ec805ae3750dae4c89b94c1a50b71a2..52354101562b71b89d185704c11309241ae6eb31 100644
--- a/indra/newview/llviewerregion.cpp
+++ b/indra/newview/llviewerregion.cpp
@@ -575,23 +575,25 @@ void LLViewerRegion::dirtyHeights()
 	}
 }
 
-BOOL LLViewerRegion::idleUpdate(LLTimer &timer, const F32 max_time)
+BOOL LLViewerRegion::idleUpdate(F32 max_update_time)
 {
-	BOOL done = mLandp->idleUpdate();
-
+	// did_update returns TRUE if we did at least one significant update
+	BOOL did_update = mLandp->idleUpdate(max_update_time);
+	
 	if (mParcelOverlay)
 	{
+		// Hopefully not a significant time sink...
 		mParcelOverlay->idleUpdate();
 	}
 
-	return done;
+	return did_update;
 }
 
 
 // As above, but forcibly do the update.
 void LLViewerRegion::forceUpdate()
 {
-	mLandp->idleUpdate();
+	mLandp->idleUpdate(0.f);
 
 	if (mParcelOverlay)
 	{
@@ -826,7 +828,7 @@ LLVector3 LLViewerRegion::getPosAgentFromRegion(const LLVector3 &pos_region) con
 
 LLVector3 LLViewerRegion::getPosRegionFromAgent(const LLVector3 &pos_agent) const
 {
-	return getPosRegionFromGlobal(gAgent.getPosGlobalFromAgent(pos_agent));
+	return pos_agent - getOriginAgent();
 }
 
 F32 LLViewerRegion::getLandHeightRegion(const LLVector3& region_pos)
@@ -1260,6 +1262,7 @@ void LLViewerRegion::setSeedCapability(const std::string& url)
 	capabilityNames.append("MapLayerGod");
 	capabilityNames.append("NewAgentInventory");
 	capabilityNames.append("EventQueueGet");
+	capabilityNames.append("RequestTextureDownload");
 	LLHTTPClient::post(url, capabilityNames, BaseCapabilitiesComplete::build(this));
 }
 
diff --git a/indra/newview/llviewerregion.h b/indra/newview/llviewerregion.h
index 3ce6c89430034ebdd13a95160c18a35f2d3ebc75..b3392baf8142c78b44685d9639ed7d835d96c94a 100644
--- a/indra/newview/llviewerregion.h
+++ b/indra/newview/llviewerregion.h
@@ -153,7 +153,7 @@ public:
 
 	F32	getWidth() const						{ return mWidth; }
 
-	BOOL idleUpdate(LLTimer &timer, const F32 max_time);
+	BOOL idleUpdate(F32 max_update_time);
 
 	// Like idleUpdate, but forces everything to complete regardless of
 	// how long it takes.
diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp
index baf241697b8607842fe37861dbd8d7a8e0f0b271..4a5cbd20e605761efc10e96d0d352d097f7fc72a 100644
--- a/indra/newview/llviewerstats.cpp
+++ b/indra/newview/llviewerstats.cpp
@@ -103,13 +103,13 @@ const StatAttributes STAT_INFO[LLViewerStats::ST_COUNT] =
 	// ST_SIM_FPS_20_SECONDS
 	StatAttributes("Seconds with sim FPS below 20", TRUE, TRUE),
 	// ST_PHYS_FPS_20_SECONDS
-	StatAttributes("Seconds with physics FPS below 20", TRUE, TRUE),
+	StatAttributes("Seconds with physics FPS below 20", FALSE, TRUE),
 	// ST_LOSS_05_SECONDS
 	StatAttributes("Seconds with packet loss > 5%", TRUE, TRUE),
 	// ST_FPS_DROP_50_RATIO
-	StatAttributes("Ratio of frames 2x longer than previous", TRUE, FALSE),
-	// ST_MEDIA_OBJECT_LIST_LENGTH
-	StatAttributes("Number of objects that want to display web pages", TRUE, FALSE),
+	StatAttributes("Ratio of frames 2x longer than previous", FALSE, FALSE),
+	// ST_ENABLE_VBO
+	StatAttributes("Vertex Buffers Enabled", TRUE, FALSE),
 	// ST_DELTA_BANDWIDTH
 	StatAttributes("Increase/Decrease in bandwidth based on packet loss", TRUE, FALSE),
 	// ST_MAX_BANDWIDTH
diff --git a/indra/newview/llviewerstats.h b/indra/newview/llviewerstats.h
index 055b21d116925e5632cb38f221b199d123867c14..33ed6bcbcf8e8d0d1b3b9dac79ab46a25248c87c 100644
--- a/indra/newview/llviewerstats.h
+++ b/indra/newview/llviewerstats.h
@@ -114,7 +114,7 @@ public:
 		ST_PHYS_FPS_20_SECONDS = 32,
 		ST_LOSS_05_SECONDS = 33,
 		ST_FPS_DROP_50_RATIO = 34,
-		ST_MEDIA_OBJECT_LIST_LENGTH = 35,
+		ST_ENABLE_VBO = 35,
 		ST_DELTA_BANDWIDTH = 36,
 		ST_MAX_BANDWIDTH = 37,
 		ST_LIGHTING_DETAIL = 38,
diff --git a/indra/newview/llviewerthrottle.cpp b/indra/newview/llviewerthrottle.cpp
index 4ffdabbbc9cbbb4670e63d8e869229c5b8bec65d..ceef3bbac5c9928a6098540533c6e34f077fc9c5 100644
--- a/indra/newview/llviewerthrottle.cpp
+++ b/indra/newview/llviewerthrottle.cpp
@@ -18,11 +18,7 @@
 
 // consts
 
-// The viewer is allowed to set the under-the-hood bandwidth to 50%
-// greater than the prefs UI shows, under the assumption that the
-// viewer won't receive all the different message types at once.
-// I didn't design this, don't know who did. JC
-const F32 MAX_FRACTIONAL = 1.5f;
+const F32 MAX_FRACTIONAL = 1.0f; // was 1.5, which was causing packet loss, reduced to 1.0 - SJB
 const F32 MIN_FRACTIONAL = 0.2f;
 
 const F32 MIN_BANDWIDTH = 50.f;
diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
index 1b39c5bf7ecfe833910696def72a25c03c8c4b91..9d4367cc7965aacc68047ebb082a85e08fafbea2 100644
--- a/indra/newview/llviewerwindow.cpp
+++ b/indra/newview/llviewerwindow.cpp
@@ -114,6 +114,8 @@
 #include "llimview.h"
 #include "lltexlayer.h"
 #include "lltextbox.h"
+#include "lltexturecache.h"
+#include "lltexturefetch.h"
 #include "lltextureview.h"
 #include "lltool.h"
 #include "lltoolbar.h"
@@ -1203,6 +1205,13 @@ LLViewerWindow::LLViewerWindow(
 	
 	LLFontManager::initClass();
 
+	if (!gFeatureManagerp->isFeatureAvailable("RenderVBO") ||
+		!gGLManager.mHasVertexBufferObject)
+	{
+		gSavedSettings.setBOOL("RenderVBOEnable", FALSE);
+	}
+	LLVertexBuffer::initClass(gSavedSettings.getBOOL("RenderVBOEnable"));
+
 	//
 	// We want to set this stuff up BEFORE we initialize the pipeline, so we can turn off
 	// stuff like AGP if we think that it'll crash the viewer.
@@ -1231,7 +1240,6 @@ LLViewerWindow::LLViewerWindow(
 		gPipeline.init();
 		stop_glerror();
 		initGLDefaults();
-		LLViewerImage::initClass();
 	}
 
 	//
@@ -1244,6 +1252,7 @@ LLViewerWindow::LLViewerWindow(
 	// Init the image list.  Must happen after GL is initialized and before the images that
 	// LLViewerWindow needs are requested.
 	gImageList.init();
+	LLViewerImage::initClass();
 	gBumpImageList.init();
 
 	// Create container for all sub-views
@@ -1677,11 +1686,23 @@ LLViewerWindow::~LLViewerWindow()
 
 	LLWorldMapView::cleanupTextures();
 
+	llinfos << "Cleaning up pipeline" << llendl;
+	gPipeline.cleanup();
+	stop_glerror();
+
 	LLViewerImage::cleanupClass();
 	
 	delete[] mPickBuffer;
 	mPickBuffer = NULL;
 
+	if (gSelectMgr)
+	{
+		llinfos << "Cleaning up select manager" << llendl;
+		gSelectMgr->cleanup();
+	}
+
+	LLVertexBuffer::cleanupClass();
+
 	llinfos << "Stopping GL during shutdown" << llendl;
 	if (!gNoRender)
 	{
@@ -1689,15 +1710,7 @@ LLViewerWindow::~LLViewerWindow()
 		stop_glerror();
 	}
 
-	if (gSelectMgr)
-	{
-		llinfos << "Cleaning up select manager" << llendl;
-		gSelectMgr->cleanup();
-	}
 
-	llinfos << "Cleaning up pipeline" << llendl;
-	gPipeline.cleanup();
-	stop_glerror();
 	llinfos << "Destroying Window" << llendl;
 	destroyWindow();
 }
@@ -1829,6 +1842,13 @@ void LLViewerWindow::reshape(S32 width, S32 height)
 
 		gViewerStats->setStat(LLViewerStats::ST_WINDOW_WIDTH, (F64)width);
 		gViewerStats->setStat(LLViewerStats::ST_WINDOW_HEIGHT, (F64)height);
+
+		//reposition HUD attachments
+		LLVOAvatar* avatarp = gAgent.getAvatarObject();
+		if (avatarp)
+		{
+			avatarp->resetHUDAttachments();
+		}
 	}
 }
 
@@ -2263,44 +2283,15 @@ void LLViewerWindow::handleScrollWheel(S32 clicks)
 
 void LLViewerWindow::moveCursorToCenter()
 {
-#if 1 // old version
-
-#if 0 // Dave's changes - this reportedly is making the drift worse on some systems?
-	S32 x = llround((F32) mVirtualWindowRect.getWidth() / 2);
-	S32 y = llround((F32) mVirtualWindowRect.getHeight() / 2);
-#else
 	S32 x = mVirtualWindowRect.getWidth() / 2;
 	S32 y = mVirtualWindowRect.getHeight() / 2;
-#endif
 	
 	//on a forced move, all deltas get zeroed out to prevent jumping
 	mCurrentMousePoint.set(x,y);
 	mLastMousePoint.set(x,y);
 	mCurrentMouseDelta.set(0,0);	
 
-	LLUI::setCursorPositionScreen(x, y);
-	
-#else // Richard's version - fails on intel macs
-
-	S32 x = llround((F32) mWindowRect.getWidth() / 2);
-	S32 y = llround((F32) mWindowRect.getHeight() / 2);
-	
-	LLCoordWindow window_point;
-	mWindow->convertCoords(LLCoordGL(x, y), &window_point);
-	mWindow->setCursorPosition(window_point);
-	
-	// read back cursor position
-	mWindow->getCursorPosition(&window_point);
-	LLCoordGL new_mouse_pos;
-	mWindow->convertCoords(window_point, &new_mouse_pos);
-	new_mouse_pos.mX = llround((F32)new_mouse_pos.mX / mDisplayScale.mV[VX]);
-	new_mouse_pos.mY = llround((F32)new_mouse_pos.mY / mDisplayScale.mV[VY]);
-
-	//on a forced move, all deltas get zeroed out to prevent jumping
-	mCurrentMousePoint = new_mouse_pos;
-	mLastMousePoint = new_mouse_pos;
-	mCurrentMouseDelta.set(0,0);
-#endif
+	LLUI::setCursorPositionScreen(x, y);	
 }
 
 //////////////////////////////////////////////////////////////////////
@@ -2329,10 +2320,29 @@ BOOL LLViewerWindow::handlePerFrameHover()
 		mMouseInWindow = TRUE;
 	}
 
-	S32 dx = mCurrentMousePoint.mX - mLastMousePoint.mX;
-	S32 dy = mCurrentMousePoint.mY - mLastMousePoint.mY;
-	mCurrentMouseDelta.set(dx,dy);
-	LLVector2 mouse_vel((F32)dx, (F32)dy);
+	S32 dx = lltrunc((F32) (mCurrentMousePoint.mX - mLastMousePoint.mX) * LLUI::sGLScaleFactor.mV[VX]);
+	S32 dy = lltrunc((F32) (mCurrentMousePoint.mY - mLastMousePoint.mY) * LLUI::sGLScaleFactor.mV[VY]);
+
+	LLVector2 mouse_vel; 
+
+	if (gSavedSettings.getBOOL("MouseSmooth"))
+	{
+		static F32 fdx = 0.f;
+		static F32 fdy = 0.f;
+
+		F32 amount = 16.f;
+		fdx = fdx + ((F32) dx - fdx) * llmin(gFrameIntervalSeconds*amount,1.f);
+		fdy = fdy + ((F32) dy - fdy) * llmin(gFrameIntervalSeconds*amount,1.f);
+
+		mCurrentMouseDelta.set(llround(fdx), llround(fdy));
+		mouse_vel.setVec(fdx,fdy);
+	}
+	else
+	{
+		mCurrentMouseDelta.set(dx, dy);
+		mouse_vel.setVec((F32) dx, (F32) dy);
+	}
+    
 	mMouseVelocityStat.addValue(mouse_vel.magVec());
 
 	if (gNoRender)
@@ -3377,8 +3387,8 @@ void LLViewerWindow::analyzeHit(
 				const S32 UV_PICK_WIDTH = 41;
 				const S32 UV_PICK_HALF_WIDTH = (UV_PICK_WIDTH - 1) / 2;
 				U8 uv_pick_buffer[UV_PICK_WIDTH * UV_PICK_WIDTH * 4];
-				S32 pick_face = ((LLVOVolume*)objectp)->getAllTEsSame() ? 0 : face;
-				LLFace* facep = objectp->mDrawable->getFace(objectp->getFaceIndexOffset() + pick_face);
+				S32 pick_face = face;
+				LLFace* facep = objectp->mDrawable->getFace(pick_face);
 				gCamera->setPerspective(FOR_SELECTION, scaled_x - UV_PICK_HALF_WIDTH, scaled_y - UV_PICK_HALF_WIDTH, UV_PICK_WIDTH, UV_PICK_WIDTH, FALSE);
 				glViewport(scaled_x - UV_PICK_HALF_WIDTH, scaled_y - UV_PICK_HALF_WIDTH, UV_PICK_WIDTH, UV_PICK_WIDTH);
 				gPipeline.renderFaceForUVSelect(facep);
@@ -3604,22 +3614,38 @@ BOOL LLViewerWindow::mousePointOnLandGlobal(const S32 x, const S32 y, LLVector3d
 }
 
 // Saves an image to the harddrive as "SnapshotX" where X >= 1.
-BOOL LLViewerWindow::saveImageNumbered(LLImageRaw *raw)
+BOOL LLViewerWindow::saveImageNumbered(LLImageRaw *raw, const LLString& extension_in)
 {
 	if (! raw)
 	{
 		return FALSE;
 	}
+
+	LLString extension(extension_in);
+	if (extension.empty())
+	{
+		extension = (gSavedSettings.getBOOL("CompressSnapshotsToDisk")) ? ".j2c" : ".bmp";
+	}
+
+	LLFilePicker::ESaveFilter pick_type;
+	if (extension == ".j2c")
+		pick_type = LLFilePicker::FFSAVE_J2C;
+	else if (extension == ".bmp")
+		pick_type = LLFilePicker::FFSAVE_BMP;
+	else if (extension == ".tga")
+		pick_type = LLFilePicker::FFSAVE_TGA;
+	else
+		pick_type = LLFilePicker::FFSAVE_ALL; // ???
 	
 	// Get a directory if this is the first time.
 	if (strlen(sSnapshotDir) == 0)		/* Flawfinder: ignore */
 	{
 		LLString proposed_name( sSnapshotBaseName );
-		proposed_name.append( ".bmp" );
+		proposed_name.append( extension );
 
 		// pick a directory in which to save
 		LLFilePicker& picker = LLFilePicker::instance();
-		if (!picker.getSaveFile(LLFilePicker::FFSAVE_BMP, proposed_name.c_str()))
+		if (!picker.getSaveFile(pick_type, proposed_name.c_str()))
 		{
 			// Clicked cancel
 			return FALSE;
@@ -3634,8 +3660,8 @@ BOOL LLViewerWindow::saveImageNumbered(LLImageRaw *raw)
 		S32 length = strlen(directory);		/* Flawfinder: ignore */
 		S32 index = length;
 
-		// Back up over ".bmp"
-		index -= 4;
+		// Back up over extension
+		index -= extension.length();
 		if (index >= 0 && directory[index] == '.')
 		{
 			directory[index] = '\0';
@@ -3674,10 +3700,9 @@ BOOL LLViewerWindow::saveImageNumbered(LLImageRaw *raw)
 
 	do
 	{
-		char extension[100];		/* Flawfinder: ignore */
-		snprintf( extension, sizeof(extension), "_%.3d.bmp", i );		/* Flawfinder: ignore */
 		filepath = sSnapshotDir;
 		filepath += sSnapshotBaseName;
+		filepath += llformat("_%.3d",i);
 		filepath += extension;
 
 		struct stat stat_info;
@@ -3686,12 +3711,12 @@ BOOL LLViewerWindow::saveImageNumbered(LLImageRaw *raw)
 	}
 	while( -1 != err );  // search until the file is not found (i.e., stat() gives an error).
 
-	LLPointer<LLImageBMP> bmp_image = new LLImageBMP;
+	LLPointer<LLImageFormatted> formatted_image = LLImageFormatted::createFromExtension(extension);
 	LLImageBase::setSizeOverride(TRUE);
-	BOOL success = bmp_image->encode(raw);
+	BOOL success = formatted_image->encode(raw);
 	if( success )
 	{
-		success = bmp_image->save(filepath);
+		success = formatted_image->save(filepath);
 	}
 	else
 	{
@@ -3894,10 +3919,11 @@ BOOL LLViewerWindow::rawSnapshot(LLImageRaw *raw, S32 image_width, S32 image_hei
 		LLPipeline::toggleRenderDebugFeature((void*)LLPipeline::RENDER_DEBUG_FEATURE_UI);
 	}
 
-	BOOL hide_hud = !gSavedSettings.getBOOL("RenderHUDInSnapshot") && gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_HUD);
+
+	BOOL hide_hud = !gSavedSettings.getBOOL("RenderHUDInSnapshot") && LLPipeline::sShowHUDAttachments;
 	if (hide_hud)
 	{
-		LLPipeline::toggleRenderType((void*)LLPipeline::RENDER_TYPE_HUD);
+		LLPipeline::sShowHUDAttachments = FALSE;
 	}
 
 	// Copy screen to a buffer
@@ -4034,7 +4060,7 @@ BOOL LLViewerWindow::rawSnapshot(LLImageRaw *raw, S32 image_width, S32 image_hei
 
 	if (hide_hud)
 	{
-		LLPipeline::toggleRenderType((void*)LLPipeline::RENDER_TYPE_HUD);
+		LLPipeline::sShowHUDAttachments = TRUE;
 	}
 
 	if (high_res)
@@ -4296,6 +4322,12 @@ void LLViewerWindow::stopGL(BOOL save_state)
 	if (!gGLManager.mIsDisabled)
 	{
 		llinfos << "Shutting down GL..." << llendl;
+
+		// Pause texture decode threads (will get unpaused during main loop)
+		gTextureCache->pause();
+		gImageDecodeThread->pause();
+		gTextureFetch->pause();
+		
 		gSky.destroyGL();
 		stop_glerror();
 	
@@ -4346,7 +4378,6 @@ void LLViewerWindow::restoreGL(const LLString& progress_message)
 		LLManipTranslate::restoreGL();
 		gImageList.restoreGL();
 		gBumpImageList.restoreGL();
-		gPipeline.setUseAGP(gSavedSettings.getBOOL("RenderUseAGP"));
 		LLDynamicTexture::restoreGL();
 		LLVOAvatar::restoreGL();
 
diff --git a/indra/newview/llviewerwindow.h b/indra/newview/llviewerwindow.h
index 0097a00b7afe11161f7ed48e2fa1fbdaa31a160c..5b56bbae06ae2c463189f4d41ce9a741d0bf405a 100644
--- a/indra/newview/llviewerwindow.h
+++ b/indra/newview/llviewerwindow.h
@@ -189,7 +189,7 @@ public:
 	BOOL			saveSnapshot(const LLString&  filename, S32 image_width, S32 image_height, BOOL show_ui = TRUE, BOOL do_rebuild = FALSE, ESnapshotType type = SNAPSHOT_TYPE_COLOR);
 	BOOL			rawSnapshot(LLImageRaw *raw, S32 image_width, S32 image_height, BOOL keep_window_aspect = TRUE, 
 		BOOL show_ui = TRUE, BOOL do_rebuild = FALSE, ESnapshotType type = SNAPSHOT_TYPE_COLOR );
-	BOOL		    saveImageNumbered(LLImageRaw *raw);
+	BOOL		    saveImageNumbered(LLImageRaw *raw, const LLString& extension = "");
 
 	void			playSnapshotAnimAndSound();
 	
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index 193fd4e325880397a417d780cc3c0310a1faa12d..2c920ecde843a351133918888e774b980f67c382 100644
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -28,7 +28,6 @@
 #include "timing.h"
 
 #include "llagent.h"			//  Get state values from here
-#include "llagparray.h"
 #include "llviewercontrol.h"
 #include "llcriticaldamp.h"
 #include "lldir.h"
@@ -191,7 +190,7 @@ const F32 UNDERWATER_FREQUENCY_DAMP = 0.33f;
 const F32 APPEARANCE_MORPH_TIME = 0.65f;
 const F32 CAMERA_SHAKE_ACCEL_THRESHOLD_SQUARED = 5.f * 5.f;
 const F32 TIME_BEFORE_MESH_CLEANUP = 5.f; // seconds
-const S32 AVATAR_AGP_RELEASE_THRESHOLD = 10; // number of avatar instances before releasing AGP memory
+const S32 AVATAR_RELEASE_THRESHOLD = 10; // number of avatar instances before releasing memory
 const F32 FOOT_GROUND_COLLISION_TOLERANCE = 0.25f;
 const F32 AVATAR_LOD_TWEAK_RANGE = 0.7f;
 const S32 MAX_LOD_CHANGES_PER_FRAME = 2;
@@ -770,7 +769,6 @@ LLVOAvatar::LLVOAvatar(
 	mEyesLayerSet( NULL ),
 	mSkirtLayerSet( NULL ),
 	mRenderPriority(1.0f),
-	mNumAGPVertices(0),
 	mNameString(),
 	mTitle(),
 	mNameAway(FALSE),
@@ -988,7 +986,7 @@ LLVOAvatar::LLVOAvatar(
 	//-------------------------------------------------------------------------
 	// register motions
 	//-------------------------------------------------------------------------
-	if (LLCharacter::sInstances.getLength() == 1)
+	if (LLCharacter::sInstances.size() == 1)
 	{
 		LLKeyframeMotion::setVFS(gStaticVFS);
 		addMotion( ANIM_AGENT_BUSY,						LLNullMotion::create );
@@ -1132,7 +1130,8 @@ void LLVOAvatar::markDead()
 BOOL LLVOAvatar::isFullyBaked()
 {
 	if (mIsDummy) return TRUE;
-
+	if (getNumTEs() == 0) return FALSE;
+	
 	BOOL head_baked = ( getTEImage( TEX_HEAD_BAKED )->getID() != IMG_DEFAULT_AVATAR );
 	BOOL upper_baked = ( getTEImage( TEX_UPPER_BAKED )->getID() != IMG_DEFAULT_AVATAR );
 	BOOL lower_baked = ( getTEImage( TEX_LOWER_BAKED )->getID() != IMG_DEFAULT_AVATAR );
@@ -1161,10 +1160,10 @@ void LLVOAvatar::deleteLayerSetCaches()
 // static 
 BOOL LLVOAvatar::areAllNearbyInstancesBaked()
 {
-	for( LLVOAvatar* inst = (LLVOAvatar*)LLCharacter::sInstances.getFirstData();
-		inst;
-		inst = (LLVOAvatar*)LLCharacter::sInstances.getNextData() )
+	for (std::vector<LLCharacter*>::iterator iter = LLCharacter::sInstances.begin();
+		iter != LLCharacter::sInstances.end(); ++iter)
 	{
+		LLVOAvatar* inst = (LLVOAvatar*) *iter;
 		if( inst->isDead() )
 		{
 			continue;
@@ -1194,10 +1193,10 @@ void LLVOAvatar::dumpBakedStatus()
 {
 	LLVector3d camera_pos_global = gAgent.getCameraPositionGlobal();
 
-	for( LLVOAvatar* inst = (LLVOAvatar*)LLCharacter::sInstances.getFirstData();
-		inst;
-		inst = (LLVOAvatar*)LLCharacter::sInstances.getNextData() )
+	for (std::vector<LLCharacter*>::iterator iter = LLCharacter::sInstances.begin();
+		iter != LLCharacter::sInstances.end(); ++iter)
 	{
+		LLVOAvatar* inst = (LLVOAvatar*) *iter;
 		llinfos << "Avatar ";
 
 		LLNameValue* firstname = inst->getNVPair("FirstName");
@@ -1386,11 +1385,10 @@ void LLVOAvatar::initVertexPrograms()
 //static
 void LLVOAvatar::restoreGL()
 {
-	LLVOAvatar* inst;
-	for( inst = (LLVOAvatar*)LLCharacter::sInstances.getFirstData();
-		inst;
-		inst = (LLVOAvatar*)LLCharacter::sInstances.getNextData() )
+	for (std::vector<LLCharacter*>::iterator iter = LLCharacter::sInstances.begin();
+		iter != LLCharacter::sInstances.end(); ++iter)
 	{
+		LLVOAvatar* inst = (LLVOAvatar*) *iter;
 		inst->setCompositeUpdatesEnabled( TRUE );
 		inst->invalidateComposite( inst->mHeadLayerSet,		FALSE );
 		inst->invalidateComposite( inst->mLowerBodyLayerSet,	FALSE );
@@ -1414,10 +1412,10 @@ void LLVOAvatar::deleteCachedImages()
 	if (LLTexLayerSet::sHasCaches)
 	{
 		lldebugs << "Deleting layer set caches" << llendl;
-		for( LLVOAvatar* inst = (LLVOAvatar*)LLCharacter::sInstances.getFirstData();
-			inst;
-			inst = (LLVOAvatar*)LLCharacter::sInstances.getNextData() )
+		for (std::vector<LLCharacter*>::iterator iter = LLCharacter::sInstances.begin();
+		iter != LLCharacter::sInstances.end(); ++iter)
 		{
+			LLVOAvatar* inst = (LLVOAvatar*) *iter;
 			inst->deleteLayerSetCaches();
 		}
 		LLTexLayerSet::sHasCaches = FALSE;
@@ -1518,30 +1516,30 @@ void LLVOAvatar::initClass()
 	sSkeletonInfo = new LLVOAvatarSkeletonInfo;
 	if (!sSkeletonInfo->parseXml(sSkeletonXMLTree.getRoot()))
 	{
-		llerrs << "Error parsing skeleton XML file" << llendl;
+		llerrs << "Error parsing skeleton XML file: " << skeleton_path << llendl;
 	}
 	// parse avatar_lad.xml
 	llassert(!sAvatarInfo);
 	sAvatarInfo = new LLVOAvatarInfo;
 	if (!sAvatarInfo->parseXmlSkeletonNode(root))
 	{
-		llerrs << "Error parsing skeleton node in avatar XML file" << llendl;
+		llerrs << "Error parsing skeleton node in avatar XML file: " << skeleton_path << llendl;
 	}
 	if (!sAvatarInfo->parseXmlMeshNodes(root))
 	{
-		llerrs << "Error parsing skeleton node in avatar XML file" << llendl;
+		llerrs << "Error parsing skeleton node in avatar XML file: " << skeleton_path << llendl;
 	}
 	if (!sAvatarInfo->parseXmlColorNodes(root))
 	{
-		llerrs << "Error parsing skeleton node in avatar XML file" << llendl;
+		llerrs << "Error parsing skeleton node in avatar XML file: " << skeleton_path << llendl;
 	}
 	if (!sAvatarInfo->parseXmlLayerNodes(root))
 	{
-		llerrs << "Error parsing skeleton node in avatar XML file" << llendl;
+		llerrs << "Error parsing skeleton node in avatar XML file: " << skeleton_path << llendl;
 	}
 	if (!sAvatarInfo->parseXmlDriverNodes(root))
 	{
-		llerrs << "Error parsing skeleton node in avatar XML file" << llendl;
+		llerrs << "Error parsing skeleton node in avatar XML file: " << skeleton_path << llendl;
 	}
 }
 
@@ -1557,6 +1555,19 @@ void LLVOAvatar::cleanupClass()
 }
 
 
+void LLVOAvatar::updateSpatialExtents(LLVector3& newMin, LLVector3 &newMax)
+{
+	LLVector3 center = getRenderPosition();
+	LLVector3 size = getScale();
+	//maximum amount an animation can move avatar from drawable position
+	LLVector3 animation_buffer(5, 5, 5);
+
+	newMin.setVec((center-size)-animation_buffer);
+	newMax.setVec(center+size+animation_buffer);
+	mDrawable->setPositionGroup((newMin + newMax) * 0.5f);
+}
+
+
 //-----------------------------------------------------------------------------
 // parseSkeletonFile()
 //-----------------------------------------------------------------------------
@@ -1709,10 +1720,13 @@ BOOL LLVOAvatar::buildSkeleton(LLVOAvatarSkeletonInfo *info)
 	}
 
 	// add special-purpose "screen" joint
-	mScreenp = new LLViewerJoint("mScreen", NULL);
-	// for now, put screen at origin, as it is only used during special
-	// HUD rendering mode
-	mScreenp->setWorldPosition(LLVector3::zero);
+	if (mIsSelf)
+	{
+		mScreenp = new LLViewerJoint("mScreen", NULL);
+		// for now, put screen at origin, as it is only used during special
+		// HUD rendering mode
+		mScreenp->setWorldPosition(LLVector3::zero);
+	}
 
 	return TRUE;
 }
@@ -1793,7 +1807,7 @@ void LLVOAvatar::buildCharacter()
 		return;
 	}
 
-	gPrintMessagesThisFrame = TRUE;
+// 	gPrintMessagesThisFrame = TRUE;
 	lldebugs << "Avatar load took " << timer.getElapsedTimeF32() << " seconds." << llendl;
 
 	if ( ! status )
@@ -2076,7 +2090,7 @@ void LLVOAvatar::releaseMeshData()
 {
 	LLMemType mt(LLMemType::MTYPE_AVATAR);
 	
-	if (sInstances.getLength() < AVATAR_AGP_RELEASE_THRESHOLD || mIsDummy)
+	if (sInstances.size() < AVATAR_RELEASE_THRESHOLD || mIsDummy)
 	{
 		return;
 	}
@@ -2093,18 +2107,11 @@ void LLVOAvatar::releaseMeshData()
 	mEyeBallRightLOD.setValid(FALSE, TRUE);
 	mSkirtLOD.setValid(FALSE, TRUE);
 
-	//cleanup AGP data
+	//cleanup data
 	if (mDrawable.notNull())
 	{
 		LLFace* facep = mDrawable->getFace(0);
 		facep->setSize(0, 0);
-		mNumAGPVertices = 0;
-
-		// You need to reset the vertex data in order to guarantee
-		// that the data is deallocated, AND you start at the 0 index
-		// when you restore the face.  We're assuming ONLY ONE FACE per
-		// avatar pool, this logic is broken if that isn't the case!
-		facep->getPool()->resetVertexData(0);
 	}
 	
 	for (LLViewerJointAttachment *attachmentPoint = mAttachmentPoints.getFirstData();
@@ -2148,7 +2155,7 @@ void LLVOAvatar::restoreMeshData()
 	}
 
 	// force mesh update as LOD might not have changed to trigger this
-	updateMeshData();
+	gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_GEOMETRY, TRUE);
 }
 
 //-----------------------------------------------------------------------------
@@ -2161,29 +2168,28 @@ void LLVOAvatar::updateMeshData()
 		LLFace* facep = mDrawable->getFace(0);
 
 		U32 num_vertices = 0;
+		U32 num_indices = 0;
 
 		// this order is determined by number of LODS
 		// if a mesh earlier in this list changed LODs while a later mesh doesn't,
 		// the later mesh's index offset will be inaccurate
-		mEyeBallLeftLOD.updateFaceSizes(num_vertices, mAdjustedPixelArea);
-		mEyeBallRightLOD.updateFaceSizes(num_vertices, mAdjustedPixelArea);
-		mEyeLashLOD.updateFaceSizes(num_vertices, mAdjustedPixelArea);
-		mHeadLOD.updateFaceSizes(num_vertices, mAdjustedPixelArea);
-		mLowerBodyLOD.updateFaceSizes(num_vertices, mAdjustedPixelArea);
-		mSkirtLOD.updateFaceSizes(num_vertices, mAdjustedPixelArea);
-		mUpperBodyLOD.updateFaceSizes(num_vertices, mAdjustedPixelArea);
-		mHairLOD.updateFaceSizes(num_vertices, mAdjustedPixelArea);
-
-		if (num_vertices != mNumAGPVertices)
-		{
-			// resize immediately
-//			llinfos << "Resizing avatar AGP buffer!" << llendl;
-			facep->getPool()->resetVertexData(num_vertices);
-			facep->setSize(0,0);
-			facep->setSize(num_vertices, 0);
-			mNumAGPVertices = num_vertices;
-		}
-
+		mEyeBallLeftLOD.updateFaceSizes(num_vertices, num_indices, mAdjustedPixelArea);
+		mEyeBallRightLOD.updateFaceSizes(num_vertices, num_indices, mAdjustedPixelArea);
+		mEyeLashLOD.updateFaceSizes(num_vertices, num_indices, mAdjustedPixelArea);
+		mHeadLOD.updateFaceSizes(num_vertices, num_indices, mAdjustedPixelArea);
+		mLowerBodyLOD.updateFaceSizes(num_vertices, num_indices, mAdjustedPixelArea);
+		mSkirtLOD.updateFaceSizes(num_vertices, num_indices, mAdjustedPixelArea);
+		mUpperBodyLOD.updateFaceSizes(num_vertices, num_indices, mAdjustedPixelArea);
+		mHairLOD.updateFaceSizes(num_vertices, num_indices, mAdjustedPixelArea);
+
+		// resize immediately
+		facep->setSize(num_vertices, num_indices);
+
+		facep->mVertexBuffer = new LLVertexBufferAvatar();
+		facep->mVertexBuffer->allocateBuffer(num_vertices, num_indices, TRUE);
+		facep->setGeomIndex(0);
+		facep->setIndicesIndex(0);
+		
 		// This is a hack! Avatars have their own pool, so we are detecting
 		//   the case of more than one avatar in the pool (thus > 0 instead of >= 0)
 		if (facep->getGeomIndex() > 0)
@@ -2366,13 +2372,8 @@ BOOL LLVOAvatar::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
 	setPixelAreaAndAngle(gAgent);
 
 	// Update the LOD of the joints
-	static const F32 UPDATE_TIME = .5f;
- 	if (mUpdateLODTimer.hasExpired())
-	{
- 		mUpdateLODTimer.setTimerExpirySec(UPDATE_TIME * (.75f + ll_frand(0.5f)));
-		updateJointLODs();
-	}
-	
+	//static const F32 UPDATE_TIME = .5f;
+ 	
 	// force asynchronous drawable update
 	if(mDrawable.notNull() && !gNoRender)
 	{	
@@ -2432,6 +2433,17 @@ BOOL LLVOAvatar::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
 
 	updateCharacter(agent);
 	
+
+		
+	if (LLVOAvatar::sJointDebug)
+	{
+		llinfos << getNVPair("FirstName")->getString() << getNVPair("LastName")->getString() << ": joint touches: " << LLJoint::sNumTouches << " updates: " << LLJoint::sNumUpdates << llendl;
+	}
+
+	LLJoint::sNumUpdates = 0;
+	LLJoint::sNumTouches = 0;
+
+
 	if (gNoRender)
 	{
 		return TRUE;
@@ -2440,7 +2452,7 @@ BOOL LLVOAvatar::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
 	// *NOTE: this is necessary for the floating name text above your head.
 	if (mDrawable.notNull())
 	{
-		gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);
+		gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_SHADOW, TRUE);
 	}
 
 	// update attachments positions
@@ -2450,8 +2462,12 @@ BOOL LLVOAvatar::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
 			attachment;
 			attachment = mAttachmentPoints.getNextData())
 		{
-			LLViewerObject *attached_object = attachment->getObject(0);
-			if (attached_object && !attached_object->isDead() && attachment->getValid())
+			LLViewerObject *attached_object = attachment->getObject();
+
+			BOOL visibleAttachment = isVisible() || !(attached_object && attached_object->mDrawable->getSpatialBridge()
+										  && (attached_object->mDrawable->getSpatialBridge()->getRadius() < 2.0));
+
+			if (visibleAttachment && attached_object && !attached_object->isDead() && attachment->getValid())
 			{
 				// if selecting any attachments, update all of them as non-damped
 				if (gSelectMgr->getSelection()->getObjectCount() && gSelectMgr->getSelection()->isAttachment())
@@ -2605,9 +2621,9 @@ BOOL LLVOAvatar::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
 	const F32 FADE_DURATION = gSavedSettings.getF32("RenderNameFadeDuration"); // seconds
 	BOOL visible_chat = gSavedSettings.getBOOL("UseChatBubbles") && (mChats.size() || mTyping);
 	BOOL render_name =	visible_chat ||
-						(mVisible &&
-						(sRenderName == RENDER_NAME_ALWAYS) ||
-						(sRenderName == RENDER_NAME_FADE && time_visible < NAME_SHOW_TIME));
+						(isVisible() &&
+						((sRenderName == RENDER_NAME_ALWAYS) ||
+						(sRenderName == RENDER_NAME_FADE && time_visible < NAME_SHOW_TIME)));
 	// If it's your own avatar, don't draw in mouselook, and don't
 	// draw if we're specifically hiding our own name.
 	if (mIsSelf)
@@ -2984,6 +3000,15 @@ void LLVOAvatar::updateCharacter(LLAgent &agent)
 {
 	LLMemType mt(LLMemType::MTYPE_AVATAR);
 
+	// update screen joint size
+	if (mScreenp)
+	{
+		F32 aspect = gCamera->getAspect();
+		LLVector3 scale(1.f, aspect, 1.f);
+		mScreenp->setScale(scale);
+		mScreenp->updateWorldMatrixChildren();
+	}
+
 	// clear debug text
 	mDebugText.clear();
 
@@ -3020,14 +3045,6 @@ void LLVOAvatar::updateCharacter(LLAgent &agent)
 			}
 	}
 
-	if (LLVOAvatar::sJointDebug)
-	{
-		llinfos << getNVPair("FirstName")->getString() << ": joint touches: " << LLJoint::sNumTouches << " updates: " << LLJoint::sNumUpdates << llendl;
-
-		LLJoint::sNumUpdates = 0;
-		LLJoint::sNumTouches = 0;
-	}
-//	llinfos << mPixelArea << llendl;
 	if (gNoRender)
 	{
 		// Hack if we're running drones...
@@ -3046,10 +3063,15 @@ void LLVOAvatar::updateCharacter(LLAgent &agent)
 		return;
 	}
 
+	if (!mIsSelf && !isVisible())
+	{
+		return;
+	}
+
 	// change animation time quanta based on avatar render load
 	if (!mIsSelf)
 	{
-		F32 time_quantum = clamp_rescale((F32)sInstances.getLength(), 10.f, 35.f, 0.f, 0.25f);
+		F32 time_quantum = clamp_rescale((F32)sInstances.size(), 10.f, 35.f, 0.f, 0.25f);
 		F32 pixel_area_scale = clamp_rescale(mPixelArea, 100, 5000, 1.f, 0.f);
 		F32 time_step = time_quantum * pixel_area_scale;
 		if (time_step != 0.f)
@@ -3078,10 +3100,6 @@ void LLVOAvatar::updateCharacter(LLAgent &agent)
 		mTimeVisible.reset();
 	}
 
-	// update screen joint size
-	mScreenp->setScale(LLVector3(1.f, gCamera->getAspect(), 1.f));
-	mScreenp->updateWorldMatrixChildren();
-	
 	//--------------------------------------------------------------------
 	// create local variables in world coords for region position values
 	//--------------------------------------------------------------------
@@ -3092,6 +3110,8 @@ void LLVOAvatar::updateCharacter(LLAgent &agent)
 	xyVel.mV[VZ] = 0.0f;
 	speed = xyVel.magVec();
 
+	BOOL throttle = TRUE;
+
 	if (!(mIsSitting && getParent()))
 	{
 		//--------------------------------------------------------------------
@@ -3102,6 +3122,7 @@ void LLVOAvatar::updateCharacter(LLAgent &agent)
 		if (mTimeLast == 0.0f)
 		{
 			mTimeLast = animation_time;
+			throttle = FALSE;
 
 			// put the pelvis at slaved position/mRotation
 			mRoot.setWorldPosition( getPositionAgent() ); // first frame
@@ -3145,7 +3166,16 @@ void LLVOAvatar::updateCharacter(LLAgent &agent)
 		// correct for the fact that the pelvis is not necessarily the center 
 		// of the agent's physical representation
 		root_pos.mdV[VZ] -= (0.5f * mBodySize.mV[VZ]) - mPelvisToFoot;
-		mRoot.setWorldPosition(gAgent.getPosAgentFromGlobal(root_pos) ); // regular update
+		
+
+		LLVector3 newPosition = gAgent.getPosAgentFromGlobal(root_pos);
+
+		if (newPosition != mRoot.getXform()->getWorldPosition())
+		{		
+			mRoot.touch();
+			mRoot.setWorldPosition(newPosition ); // regular update
+		}
+
 
 		//--------------------------------------------------------------------
 		// Propagate viewer object rotation to root of avatar
@@ -3315,9 +3345,10 @@ void LLVOAvatar::updateCharacter(LLAgent &agent)
 			//}
 			//end Ventrella
 
-			F32 u = llclamp((deltaTime / pelvis_lag_time), 0.0f, 1.0f);
-			
+			F32 u = llclamp((deltaTime / pelvis_lag_time), 0.0f, 1.0f);	
+
 			mRoot.setWorldRotation( slerp(u, mRoot.getWorldRotation(), wQv) );
+			
 		}
 	}
 	else if (mDrawable.notNull())
@@ -3329,8 +3360,17 @@ void LLVOAvatar::updateCharacter(LLAgent &agent)
 	//--------------------------------------------------------------------
 	// the rest should only be done when close enough to see it
 	//--------------------------------------------------------------------
+	
+
+	if (mPixelArea > 12.0f)
+		throttle = FALSE;
+	if (mPixelArea < 400.0f)
+	{
+		throttle = (LLDrawable::getCurrentFrame()+mID.mData[0])%2 != 0;
+	}
+
 	if ( !(mIsSitting && getParent()) && 
-		((mPixelArea < 12.0f) || 
+		(throttle || 
 		(!isVisible() && (mPixelArea < MIN_PIXEL_AREA_FOR_COMPOSITE))) )
 	{
 		mRoot.setWorldRotation( getRotation() );
@@ -3434,7 +3474,7 @@ void LLVOAvatar::updateCharacter(LLAgent &agent)
 			}
 		}
 	}
-	
+
 	mRoot.updateWorldMatrixChildren();
 
 	if (!mDebugText.size() && mText.notNull())
@@ -3491,10 +3531,8 @@ void LLVOAvatar::updateVisibility(BOOL force_invisible)
 	else if (!force_invisible)
 	{
 		// calculate avatar distance wrt head
-		LLVector3         pos = mDrawable->getPositionAgent();
-		pos                  -= gCamera->getOrigin();
-		mDrawable->mDistanceWRTCamera   = pos.magVec();
-
+		mDrawable->updateDistance(*gCamera);
+		
 		if (!mDrawable->getSpatialGroup() || mDrawable->getSpatialGroup()->isVisible())
 		{
 			visible = TRUE;
@@ -3566,9 +3604,9 @@ void LLVOAvatar::updateVisibility(BOOL force_invisible)
 				attachment;
 				attachment = mAttachmentPoints.getNextData())
 			{
-				if (attachment->getObject(0))
+				if (attachment->getObject())
 				{
-					if(attachment->getObject(0)->mDrawable->isVisible())
+					if(attachment->getObject()->mDrawable->isVisible())
 					{
 						llinfos << attachment->getName() << " visible" << llendl;
 					}
@@ -3592,7 +3630,6 @@ void LLVOAvatar::updateVisibility(BOOL force_invisible)
 		{
 			restoreMeshData();
 		}
-		gPipeline.markVisible(mDrawable);
 	}
 	else
 	{
@@ -3619,11 +3656,12 @@ void LLVOAvatar::updateVisibility(BOOL force_invisible)
 void LLVOAvatar::updateAllAvatarVisiblity()
 {
 	LLVOAvatar::sNumVisibleAvatars = 0;
-	LLVOAvatar *avatarp;
-
+	
 	F32 render_priority = (F32)LLVOAvatar::sMaxVisible;
-	for (avatarp = (LLVOAvatar*)sInstances.getFirstData(); avatarp; avatarp = (LLVOAvatar*)sInstances.getNextData())
+	for (std::vector<LLCharacter*>::iterator iter = LLCharacter::sInstances.begin();
+		iter != LLCharacter::sInstances.end(); ++iter)
 	{
+		LLVOAvatar* avatarp = (LLVOAvatar*) *iter;
 		if (avatarp->isDead())
 		{
 			continue;
@@ -3761,26 +3799,22 @@ U32 LLVOAvatar::renderSkinned(EAvatarRenderPass pass)
 
 	if (pass == AVATAR_RENDER_PASS_SINGLE)
 	{
+		BOOL first_pass = TRUE;
 		if (!mIsSelf || gAgent.needsRenderHead())
 		{
 			num_indices += mHeadLOD.render(mAdjustedPixelArea);
+			first_pass = FALSE;
 		}
-		num_indices += mUpperBodyLOD.render(mAdjustedPixelArea);
-		num_indices += mLowerBodyLOD.render(mAdjustedPixelArea);
-		if( isWearingWearableType( WT_SKIRT ) )
-		{
-			glAlphaFunc(GL_GREATER,0.25f);
-			num_indices += mSkirtLOD.render(mAdjustedPixelArea);
-			glAlphaFunc(GL_GREATER,0.01f);
-		}
+		num_indices += mUpperBodyLOD.render(mAdjustedPixelArea, first_pass);
+		num_indices += mLowerBodyLOD.render(mAdjustedPixelArea, FALSE);
 
-		if (!mIsSelf || gAgent.needsRenderHead())
 		{
-			num_indices += mEyeLashLOD.render(mAdjustedPixelArea);
-			num_indices += mHairLOD.render(mAdjustedPixelArea);
+			LLGLEnable blend(GL_BLEND);
+			LLGLEnable test(GL_ALPHA_TEST);
+			num_indices += renderTransparent();
 		}
 	}
-	else if (pass == AVATAR_RENDER_PASS_CLOTHING_INNER)
+	/*else if (pass == AVATAR_RENDER_PASS_CLOTHING_INNER)
 	{
 		if (!mIsSelf || gAgent.needsRenderHead())
 		{
@@ -3813,7 +3847,7 @@ U32 LLVOAvatar::renderSkinned(EAvatarRenderPass pass)
 		LLViewerJointMesh::sClothingMaskImageName = mLowerMaskTexName;
 		num_indices += mLowerBodyLOD.render(mAdjustedPixelArea);
 		LLViewerJointMesh::sClothingMaskImageName = 0;
-	}
+	}*/
 
 	LLViewerJointMesh::sRenderPass = AVATAR_RENDER_PASS_SINGLE;
 	
@@ -3824,6 +3858,27 @@ U32 LLVOAvatar::renderSkinned(EAvatarRenderPass pass)
 	return num_indices;
 }
 
+U32 LLVOAvatar::renderTransparent()
+{
+	U32 num_indices = 0;
+	BOOL first_pass = FALSE;
+	if( isWearingWearableType( WT_SKIRT ) )
+	{
+		glAlphaFunc(GL_GREATER,0.25f);
+		num_indices += mSkirtLOD.render(mAdjustedPixelArea, FALSE);
+		first_pass = FALSE;
+		glAlphaFunc(GL_GREATER,0.01f);
+	}
+
+	if (!mIsSelf || gAgent.needsRenderHead())
+	{
+		num_indices += mEyeLashLOD.render(mAdjustedPixelArea, first_pass);
+		num_indices += mHairLOD.render(mAdjustedPixelArea, FALSE);
+	}
+
+	return num_indices;
+}
+
 //-----------------------------------------------------------------------------
 // renderRigid()
 //-----------------------------------------------------------------------------
@@ -3852,6 +3907,39 @@ U32 LLVOAvatar::renderRigid()
 	return num_indices;
 }
 
+U32 LLVOAvatar::renderFootShadows()
+{
+	U32 num_indices = 0;
+
+	if (!mIsBuilt)
+	{
+		return 0;
+	}
+
+	if (mIsSelf && (!gAgent.needsRenderAvatar() || !gAgent.needsRenderHead()))
+	{
+		return 0;
+	}
+	
+	if (!mIsBuilt)
+	{
+		return 0;
+	}
+
+	U32 foot_mask = LLVertexBuffer::MAP_VERTEX |
+					LLVertexBuffer::MAP_TEXCOORD;
+
+	//render foot shadows
+	LLGLEnable blend(GL_BLEND);
+	mShadowImagep->bind();
+	glColor4fv(mShadow0Facep->getRenderColor().mV);
+	mShadow0Facep->renderIndexed(foot_mask);
+	glColor4fv(mShadow1Facep->getRenderColor().mV);
+	mShadow1Facep->renderIndexed(foot_mask);
+	
+	return num_indices;
+}
+
 //-----------------------------------------------------------------------------
 // renderCollisionVolumes()
 //-----------------------------------------------------------------------------
@@ -3868,6 +3956,7 @@ void LLVOAvatar::renderCollisionVolumes()
 //------------------------------------------------------------------------
 void LLVOAvatar::updateTextures(LLAgent &agent)
 {
+	LLFastTimer ftm(LLFastTimer::FTM_TEMP5);
 	BOOL render_avatar = TRUE;
 
 	if (mIsDummy || gNoRender)
@@ -3942,6 +4031,8 @@ void LLVOAvatar::updateTextures(LLAgent &agent)
 	}
 	*/
 
+	mMaxPixelArea = 0.f;
+	mMinPixelArea = 99999999.f;
 	for (U32 i = 0; i < getNumTEs(); i++)
 	{
 		LLViewerImage *imagep = getTEImage(i);
@@ -4039,40 +4130,35 @@ void LLVOAvatar::updateTextures(LLAgent &agent)
 			case TEX_HEAD_BAKED:
 				if (head_baked)
 				{
-					imagep->setBoostLevel(boost_level);
-					imagep->addTextureStats(mPixelArea, texel_area_ratio);
+					addBakedTextureStats( imagep, mPixelArea, texel_area_ratio, boost_level );
 				}
 				break;
 
 			case TEX_UPPER_BAKED:
 				if (upper_baked)
 				{
-					imagep->setBoostLevel(boost_level);
-					imagep->addTextureStats(mPixelArea, texel_area_ratio);
+					addBakedTextureStats( imagep, mPixelArea, texel_area_ratio, boost_level );
 				}
 				break;
 			
 			case TEX_LOWER_BAKED:
 				if (lower_baked)
 				{
-					imagep->setBoostLevel(boost_level);
-					imagep->addTextureStats(mPixelArea, texel_area_ratio);
+					addBakedTextureStats( imagep, mPixelArea, texel_area_ratio, boost_level );
 				}
 				break;
 			
 			case TEX_EYES_BAKED:
 				if (eyes_baked)
 				{
-					imagep->setBoostLevel(boost_level);
-					imagep->addTextureStats(mPixelArea, texel_area_ratio);
+					addBakedTextureStats( imagep, mPixelArea, texel_area_ratio, boost_level );
 				}
 				break;
 
 			case TEX_SKIRT_BAKED:
 				if (skirt_baked)
 				{
-					imagep->setBoostLevel(boost_level);
-					imagep->addTextureStats(mPixelArea, texel_area_ratio);
+					addBakedTextureStats( imagep, mPixelArea, texel_area_ratio, boost_level );
 				}
 				break;
 
@@ -4080,15 +4166,8 @@ void LLVOAvatar::updateTextures(LLAgent &agent)
 				// Hair is neither a local texture used for baking, nor the output
 				// of the baking process.  It's just a texture that happens to be
 				// used to draw avatars.  Hence BOOST_AVATAR.  JC
-				if (mIsSelf)
-				{
-			  		imagep->setBoostLevel(LLViewerImage::BOOST_AVATAR_SELF);
-				}
-				else
-				{
-			  		imagep->setBoostLevel(LLViewerImage::BOOST_AVATAR);
-			  	}
-				imagep->addTextureStats(mPixelArea, texel_area_ratio);
+			  	boost_level = mIsSelf ? LLViewerImage::BOOST_AVATAR_SELF : LLViewerImage::BOOST_AVATAR;
+			  	addBakedTextureStats( imagep, mPixelArea, texel_area_ratio, boost_level );
 				break;
 			
 			default:
@@ -4098,6 +4177,11 @@ void LLVOAvatar::updateTextures(LLAgent &agent)
 		}
 	}
 
+	if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_AREA))
+	{
+		setDebugText(llformat("%4.0f:%4.0f", fsqrtf(mMinPixelArea),fsqrtf(mMaxPixelArea)));
+	}	
+	
 	if( render_avatar )
 	{
 		mShadowImagep->addTextureStats(mPixelArea, 1.f);
@@ -4127,6 +4211,15 @@ void LLVOAvatar::addLocalTextureStats( LLVOAvatar::ELocTexIndex idx, LLViewerIma
 	}
 }
 
+			    
+void LLVOAvatar::addBakedTextureStats( LLViewerImage* imagep, F32 pixel_area, F32 texel_area_ratio, S32 boost_level)
+{
+	mMaxPixelArea = llmax(pixel_area, mMaxPixelArea);
+	mMinPixelArea = llmin(pixel_area, mMinPixelArea);
+	imagep->addTextureStats(pixel_area, texel_area_ratio);
+	imagep->setBoostLevel(boost_level);
+}
+
 //-----------------------------------------------------------------------------
 // resolveHeight()
 //-----------------------------------------------------------------------------
@@ -4611,7 +4704,7 @@ F32 LLVOAvatar::getTimeDilation()
 //-----------------------------------------------------------------------------
 // LLVOAvatar::getPixelArea()
 //-----------------------------------------------------------------------------
-F32 LLVOAvatar::getPixelArea()
+F32 LLVOAvatar::getPixelArea() const
 {
 	if (mIsDummy)
 	{
@@ -4973,6 +5066,11 @@ BOOL LLVOAvatar::loadSkeletonNode ()
 			 iter != sAvatarInfo->mAttachmentInfoList.end(); iter++)
 		{
 			LLVOAvatarInfo::LLVOAvatarAttachmentInfo *info = *iter;
+			if (!isSelf() && info->mJointName == "mScreen")
+			{ //don't process screen joint for other avatars
+				continue;
+			}
+
 			LLViewerJointAttachment* attachment = new LLViewerJointAttachment();
 
 			attachment->setName(info->mName);
@@ -5323,15 +5421,6 @@ void LLVOAvatar::setPixelAreaAndAngle(LLAgent &agent)
 {
 	LLMemType mt(LLMemType::MTYPE_AVATAR);
 		
-	LLVector3 viewer_pos_agent = agent.getCameraPositionAgent();
-	LLVector3 pos_agent;
-	
-	pos_agent = getRenderPosition();
-
-	F32 dx = viewer_pos_agent.mV[VX] - pos_agent.mV[VX];
-	F32 dy = viewer_pos_agent.mV[VY] - pos_agent.mV[VY];
-	F32 dz = viewer_pos_agent.mV[VZ] - pos_agent.mV[VZ];
-
 	F32 max_scale = getMaxScale();
 	F32 mid_scale = getMidScale();
 	F32 min_scale = llmin( getScale().mV[VX], llmin( getScale().mV[VY], getScale().mV[VZ] ) );
@@ -5340,7 +5429,7 @@ void LLVOAvatar::setPixelAreaAndAngle(LLAgent &agent)
 	// to try to get a min distance from face, subtract min_scale/2 from the range.
 	// This means we'll load too much detail sometimes, but that's better than not enough
 	// I don't think there's a better way to do this without calculating distance per-poly
-	F32 range = sqrt(dx*dx + dy*dy + dz*dz) - min_scale/2;
+	F32 range = (getRenderPosition()-gCamera->getOrigin()).magVec() - min_scale/2;
 
 	if (range < 0.001f)		// range == zero
 	{
@@ -5375,17 +5464,11 @@ void LLVOAvatar::setPixelAreaAndAngle(LLAgent &agent)
 //-----------------------------------------------------------------------------
 // updateJointLODs()
 //-----------------------------------------------------------------------------
-void LLVOAvatar::updateJointLODs()
+BOOL LLVOAvatar::updateJointLODs()
 {
-	if (!mMeshValid)
-	{
-		return;
-	}
-
 	F32 lod_factor = (sLODFactor * AVATAR_LOD_TWEAK_RANGE + (1.f - AVATAR_LOD_TWEAK_RANGE));
 	F32 avatar_num_min_factor = clamp_rescale(sLODFactor, 0.f, 1.f, 0.25f, 0.6f);
 	F32 avatar_num_factor = clamp_rescale((F32)sNumVisibleAvatars, 8, 25, 1.f, avatar_num_min_factor);
-
 	{
 		if (mIsSelf)
 		{
@@ -5408,16 +5491,17 @@ void LLVOAvatar::updateJointLODs()
 			mAdjustedPixelArea = (F32)mPixelArea * lod_factor * lod_factor * avatar_num_factor * avatar_num_factor;
 		}
 
-		// now select meshes to render based on adjusted pixel area, and perform AGP data push as necessary
+		// now select meshes to render based on adjusted pixel area
 		BOOL res = mRoot.updateLOD(mAdjustedPixelArea, TRUE);
  		if (res)
 		{
 			sNumLODChangesThisFrame++;
-			updateMeshData();
+			dirtyMesh();
+			return TRUE;
 		}
 	}
 
-	return;
+	return FALSE;
 }
 
 //-----------------------------------------------------------------------------
@@ -5428,24 +5512,22 @@ LLDrawable *LLVOAvatar::createDrawable(LLPipeline *pipeline)
 	pipeline->allocDrawable(this);
 	mDrawable->setLit(FALSE);
 
-	LLDrawPool *poolp = gPipeline.getPool(LLDrawPool::POOL_AVATAR);
+	LLDrawPoolAvatar *poolp = (LLDrawPoolAvatar*) gPipeline.getPool(LLDrawPool::POOL_AVATAR);
 
 	// Only a single face (one per avatar)
 	mDrawable->setState(LLDrawable::ACTIVE);
 	mDrawable->addFace(poolp, NULL);
+	mDrawable->setRenderType(LLPipeline::RENDER_TYPE_AVATAR);
 	
-	poolp = gPipeline.getPool(LLDrawPool::POOL_ALPHA);
-
 	LLFace *facep;
 
 	// Add faces for the foot shadows
-	facep = mDrawable->addFace(poolp, mShadowImagep);
+	facep = mDrawable->addFace((LLFacePool*) NULL, mShadowImagep);
 	mShadow0Facep = facep;
 
-	facep = mDrawable->addFace(poolp, mShadowImagep);
+	facep = mDrawable->addFace((LLFacePool*) NULL, mShadowImagep);
 	mShadow1Facep = facep;
 
-	gPipeline.markMaterialed(mDrawable);
 	dirtyMesh();
 	return mDrawable;
 }
@@ -5456,6 +5538,7 @@ LLDrawable *LLVOAvatar::createDrawable(LLPipeline *pipeline)
 //-----------------------------------------------------------------------------
 BOOL LLVOAvatar::updateGeometry(LLDrawable *drawable)
 {
+	LLFastTimer ftm(LLFastTimer::FTM_UPDATE_AVATAR);
  	if (!(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_AVATAR)))
 	{
 		return TRUE;
@@ -5471,30 +5554,9 @@ BOOL LLVOAvatar::updateGeometry(LLDrawable *drawable)
 		llerrs << "LLVOAvatar::updateGeometry() called with NULL drawable" << llendl;
 	}
 
-	// Update the shadow, tractor, and text label geometry.
-
-	updateShadowFaces();
-
-	if (!drawable->isVisible())
-	{
-		return TRUE;
-	}
-
-	LLFace* facep = drawable->getFace(0);
-	if (!mDirtyMesh && !facep->getDirty())
-	{
-		return TRUE;
-	}
-
-	// U32 num_vertices = 0;
-
-	updateMeshData();
-
-	mDirtyMesh = FALSE;
 	return TRUE;
 }
 
-
 //-----------------------------------------------------------------------------
 // updateShadowFaces()
 //-----------------------------------------------------------------------------
@@ -5710,28 +5772,26 @@ void LLVOAvatar::removeChild(LLViewerObject *childp)
 	detachObject(childp);
 }
 
-//-----------------------------------------------------------------------------
-// attachObject()
-//-----------------------------------------------------------------------------
-BOOL LLVOAvatar::attachObject(LLViewerObject *viewer_object)
+LLViewerJointAttachment* LLVOAvatar::getTargetAttachmentPoint(LLViewerObject* viewer_object)
 {
 	S32 attachmentID = ATTACHMENT_ID_FROM_STATE(viewer_object->getState());
-	//clamp((S32)(viewer_object->getState() & AGENT_ATTACH_MASK) >> AGENT_ATTACH_OFFSET, 1, 0xff);
-
-	//if (mIsSelf)
-	//{
-	//	gSelectMgr->deselectObjectAndFamily(viewer_object);
-	//}
 
 	LLViewerJointAttachment* attachment = mAttachmentPoints.getIfThere(attachmentID);
 
 	if (!attachment)
 	{
-		llwarns << "Tried to attach object to invalid attachment point: " << attachmentID << llendl;
-		return FALSE;
+		llwarns << "Object attachment point invalid: " << attachmentID << llendl;
 	}
 
-//	LLQuaternion object_world_rot = viewer_object->getWorldRotation();
+	return attachment;
+}
+
+//-----------------------------------------------------------------------------
+// attachObject()
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::attachObject(LLViewerObject *viewer_object)
+{
+	LLViewerJointAttachment* attachment = getTargetAttachmentPoint(viewer_object);
 
 	if (!attachment->addObject(viewer_object))
 	{
@@ -5744,10 +5804,6 @@ BOOL LLVOAvatar::attachObject(LLViewerObject *viewer_object)
 		gSelectMgr->updatePointAt();
 	}
 
-//	LLQuaternion desired_rot = (object_world_rot * ~attachment->getWorldRotation());
-
-	lldebugs << "Attaching object (" << attachmentID << ") item_id=" << attachment->getItemID() << " task_id=" << viewer_object->getID() << "to " << attachment->getName() << llendl;
-	
 	if (mIsSelf)
 	{
 		updateAttachmentVisibility(gAgent.getCameraMode());
@@ -5780,6 +5836,23 @@ void LLVOAvatar::lazyAttach()
 		}
 }
 
+void LLVOAvatar::resetHUDAttachments()
+{
+	for(LLViewerJointAttachment* attachment = mAttachmentPoints.getFirstData();
+		attachment;
+		attachment = mAttachmentPoints.getNextData())
+	{
+		if (attachment->getIsHUDAttachment())
+		{
+			LLViewerObject* obj = attachment->getObject();
+			if (obj && obj->mDrawable.notNull())
+			{
+				gPipeline.markMoved(obj->mDrawable);
+			}
+		}
+	}
+}
+
 //-----------------------------------------------------------------------------
 // detachObject()
 //-----------------------------------------------------------------------------
@@ -5790,7 +5863,7 @@ BOOL LLVOAvatar::detachObject(LLViewerObject *viewer_object)
 		attachment = mAttachmentPoints.getNextData())
 	{
 		// only one object per attachment point for now
-		if (attachment->getObject(0) == viewer_object)
+		if (attachment->getObject() == viewer_object)
 		{
 			LLUUID item_id = attachment->getItemID();
 			attachment->removeObject(viewer_object);
@@ -5983,7 +6056,7 @@ LLViewerObject* LLVOAvatar::getWornAttachment( const LLUUID& inv_item_id )
 	{
 		if( attachment_point->getItemID() == inv_item_id )
 		{
-			return attachment_point->getObject(0);
+			return attachment_point->getObject();
 		}
 	}
 	return NULL;
@@ -6321,10 +6394,10 @@ const LLUUID& LLVOAvatar::getLocalTextureID( S32 index )
 void LLVOAvatar::dumpTotalLocalTextureByteCount()
 {
 	S32 total_gl_bytes = 0;
-	for( LLVOAvatar* cur = (LLVOAvatar*)LLCharacter::sInstances.getFirstData();
-		cur;
-		cur = (LLVOAvatar*)LLCharacter::sInstances.getNextData() )
+	for (std::vector<LLCharacter*>::iterator iter = LLCharacter::sInstances.begin();
+		iter != LLCharacter::sInstances.end(); ++iter)
 	{
+		LLVOAvatar* cur = (LLVOAvatar*) *iter;
 		S32 gl_bytes = 0;
 		cur->getLocalTextureByteCount(&gl_bytes );
 		total_gl_bytes += gl_bytes;
@@ -7724,7 +7797,7 @@ BOOL LLVOAvatar::hasHUDAttachment()
 		attachment;
 		attachment = mAttachmentPoints.getNextData())
 		{
-			if (attachment->getIsHUDAttachment() && attachment->getObject(0))
+			if (attachment->getIsHUDAttachment() && attachment->getObject())
 			{
 				return TRUE;
 			}
@@ -7739,9 +7812,9 @@ LLBBox LLVOAvatar::getHUDBBox()
 	attachment;
 	attachment = mAttachmentPoints.getNextData())
 	{
-		if (attachment->getIsHUDAttachment() && attachment->getObject(0))
+		if (attachment->getIsHUDAttachment() && attachment->getObject())
 		{
-			LLViewerObject* hud_object = attachment->getObject(0);
+			LLViewerObject* hud_object = attachment->getObject();
 
 			// initialize bounding box to contain identity orientation and center point for attached object
 			bbox.addPointLocal(hud_object->getPosition());
@@ -7759,52 +7832,6 @@ LLBBox LLVOAvatar::getHUDBBox()
 
 void LLVOAvatar::rebuildHUD()
 {
-	if (!mIsSelf)
-	{
-		return;
-	}
-
-	for(LLViewerJointAttachment *attachment = mAttachmentPoints.getFirstData();
-	attachment;
-	attachment = mAttachmentPoints.getNextData())
-	{
-		if (attachment->getIsHUDAttachment() && attachment->getObject(0))
-		{
-			LLViewerObject* hud_object = attachment->getObject(0);
-			LLDrawable* hud_drawable = hud_object->mDrawable;
-
-			if (hud_drawable)
-			{
-				// this assumes that an AGP sync will happen because face has been backlisted, 
-				// so that pool has been rebuilt this frame and is scheduled for a sync
-				for(S32 face_index = 0; face_index < hud_drawable->getNumFaces(); ++face_index)
-				{
-					LLFace* facep = hud_drawable->getFace(face_index);
-					if (facep->isState(LLFace::BACKLIST))
-					{
-						facep->restore();
-					}
-				}
-			}
-			for (U32 child_num = 0; child_num < hud_object->mChildList.size(); ++child_num)
-			{
-				LLViewerObject* childp = hud_object->mChildList[child_num];
-				LLDrawable* child_drawable = childp->mDrawable;
-
-				if (child_drawable)
-				{
-					for(S32 face_index = 0; face_index < child_drawable->getNumFaces(); ++face_index)
-					{
-						LLFace* facep = child_drawable->getFace(face_index);
-						if (facep->isState(LLFace::BACKLIST))
-						{
-							facep->restore();
-						}
-					}
-				}
-			}
-		}
-	}
 }
 
 //-----------------------------------------------------------------------------
@@ -8061,8 +8088,7 @@ void LLVOAvatar::onBakedTextureMasksLoaded( BOOL success, LLViewerImage *src_vi,
 		{
 			if (!aux_src->getData())
 			{
-				llwarns << "No auxiliary source data for onBakedTextureMasksLoaded" << llendl;
-				src_vi->startImageDecode();
+				llerrs << "No auxiliary source data for onBakedTextureMasksLoaded" << llendl;
 				return;
 			}
 
@@ -8370,10 +8396,10 @@ void LLVOAvatar::dumpArchetypeXML( void* )
 S32 LLVOAvatar::getUnbakedPixelAreaRank()
 {
 	S32 rank = 1;
-	for( LLVOAvatar* inst = (LLVOAvatar*)LLCharacter::sInstances.getFirstData();
-		inst; 
-		inst = (LLVOAvatar*)LLCharacter::sInstances.getNextData() )
+	for (std::vector<LLCharacter*>::iterator iter = LLCharacter::sInstances.begin();
+		iter != LLCharacter::sInstances.end(); ++iter)
 	{
+		LLVOAvatar* inst = (LLVOAvatar*) *iter;
 		if( inst == this )
 		{
 			return rank;
@@ -8392,16 +8418,15 @@ S32 LLVOAvatar::getUnbakedPixelAreaRank()
 // static
 void LLVOAvatar::cullAvatarsByPixelArea()
 {
-	LLVOAvatar::sInstances.bubbleSortList();
-
-
+	std::sort(LLCharacter::sInstances.begin(), LLCharacter::sInstances.end(), CompareScreenAreaGreater());
+	
 	// Update the avatars that have changed status
 	S32 rank = 1;
 
-	for( LLVOAvatar* inst = (LLVOAvatar*)LLCharacter::sInstances.getFirstData();
-		inst; 
-		inst = (LLVOAvatar*)LLCharacter::sInstances.getNextData() )
+	for (std::vector<LLCharacter*>::iterator iter = LLCharacter::sInstances.begin();
+		iter != LLCharacter::sInstances.end(); ++iter)
 	{
+		LLVOAvatar* inst = (LLVOAvatar*) *iter;
 		BOOL culled;
 		if( inst->isDead() )
 		{
@@ -8591,8 +8616,6 @@ void LLVOAvatar::dumpLocalTextures()
 			else
 			{
 				LLViewerImage* image = mLocalTexture[i];
-				F32 data_progress = 0.0f;
-				F32 decode_progress = image->getDecodeProgress(&data_progress);
 
 				llinfos << "LocTex " << names[i] << ": "
 						<< "Discard " << image->getDiscardLevel() << ", "
@@ -8602,10 +8625,7 @@ void LLVOAvatar::dumpLocalTextures()
 					// makes textures easier to steal
 						<< image->getID() << " "
 #endif
-						<< "Data: " << (data_progress * 100) << "% "
-						<< "Decode: " << (decode_progress * 100) << "% "
-						<< "Priority: " << image->getDecodePriority() << " "
-						<< (image->needsDecode() ? "pending decode" : "not pending decode")
+						<< "Priority: " << image->getDecodePriority()
 						<< llendl;
 			}
 		}
@@ -8707,7 +8727,7 @@ BOOL LLVOAvatarBoneInfo::parseXml(LLXmlTreeNode* node)
 	}
 	else
 	{
-		llerrs << "Invalid node " << node->getName() << llendl;
+		llwarns << "Invalid node " << node->getName() << llendl;
 		return FALSE;
 	}
 
@@ -8765,7 +8785,7 @@ BOOL LLVOAvatarSkeletonInfo::parseXml(LLXmlTreeNode* node)
 	static LLStdStringHandle num_bones_string = LLXmlTree::addAttributeString("num_bones");
 	if (!node->getFastAttributeS32(num_bones_string, mNumBones))
 	{
-		llerrs << "Couldn't find number of bones." << llendl;
+		llwarns << "Couldn't find number of bones." << llendl;
 		return FALSE;
 	}
 
@@ -8779,7 +8799,8 @@ BOOL LLVOAvatarSkeletonInfo::parseXml(LLXmlTreeNode* node)
 		if (!info->parseXml(child))
 		{
 			delete info;
-			llerrs << "Error parsing bone in skeleton file" << llendl;
+			llwarns << "Error parsing bone in skeleton file" << llendl;
+			return FALSE;
 		}
 		mBoneInfoList.push_back(info);
 	}
@@ -9210,7 +9231,7 @@ void LLVOAvatar::writeCAL3D(std::string& path, std::string& file_base)
 		attachment;
 		attachment = mAttachmentPoints.getNextData())
 		{
-			LLViewerObject *attached_object = attachment->getObject(0);
+			LLViewerObject *attached_object = attachment->getObject();
 			if (attached_object && !attached_object->isDead() && attached_object->mDrawable.notNull() &&
 				attached_object->getPCode() == LL_PCODE_VOLUME)
 			{
@@ -9231,7 +9252,7 @@ void LLVOAvatar::writeCAL3D(std::string& path, std::string& file_base)
 		attachment;
 		attachment = mAttachmentPoints.getNextData())
 		{
-			LLViewerObject *attached_object = attachment->getObject(0);
+			LLViewerObject *attached_object = attachment->getObject();
 			if (attached_object && !attached_object->isDead() && attached_object->getPCode() == LL_PCODE_VOLUME)
 			{
 				LLVOVolume* attached_volume = (LLVOVolume*)attached_object;
@@ -9443,3 +9464,58 @@ LLHost LLVOAvatar::getObjectHost() const
 		return LLHost::invalid;
 	}
 }
+
+BOOL LLVOAvatar::updateLOD()
+{
+	BOOL res = updateJointLODs();
+
+	LLFace* facep = mDrawable->getFace(0);
+	if (facep->mVertexBuffer.isNull() ||
+		LLVertexBuffer::sEnableVBOs &&
+		((facep->mVertexBuffer->getUsage() == GL_STATIC_DRAW ? TRUE : FALSE) !=
+		(facep->getPool()->getVertexShaderLevel() > 0 ? TRUE : FALSE)))
+	{
+		mDirtyMesh = TRUE;
+	}
+
+	if (mDirtyMesh || mDrawable->isState(LLDrawable::REBUILD_GEOMETRY))
+	{	//LOD changed or new mesh created, allocate new vertex buffer if needed
+		updateMeshData();
+        mDirtyMesh = FALSE;
+		mDrawable->clearState(LLDrawable::REBUILD_GEOMETRY);
+	}
+	
+	if (facep->getPool()->getVertexShaderLevel() <= 0)
+	{
+		//generate animated mesh
+		mLowerBodyLOD.updateGeometry();
+		mUpperBodyLOD.updateGeometry();
+
+		if( isWearingWearableType( WT_SKIRT ) )
+		{
+			mSkirtLOD.updateGeometry();
+		}
+
+		if (!mIsSelf || gAgent.needsRenderHead())
+		{
+			mEyeLashLOD.updateGeometry();
+			mHeadLOD.updateGeometry();
+			mHairLOD.updateGeometry();
+		}
+	}
+
+	// Update the shadow, tractor, and text label geometry.
+	if (mDrawable->isState(LLDrawable::REBUILD_SHADOW))
+	{
+		updateShadowFaces();
+		mDrawable->clearState(LLDrawable::REBUILD_SHADOW);
+	}
+
+	return res;
+}
+
+U32 LLVOAvatar::getPartitionType() const
+{ //avatars merely exist as drawables in the bridge partition
+	return LLPipeline::PARTITION_BRIDGE;
+}
+
diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h
index 871502a373fc5c4e82f4fcb1d9f90e4324c71ffb..2660e7c90d5aa1443527c439e089000debff47d4 100644
--- a/indra/newview/llvoavatar.h
+++ b/indra/newview/llvoavatar.h
@@ -57,6 +57,13 @@ class LLDriverParamInfo;
 class LLHUDText;
 class LLHUDEffectSpiral;
 
+class LLVertexBufferAvatar : public LLVertexBuffer
+{
+public:
+	LLVertexBufferAvatar();
+	virtual void setupVertexBuffer(U32 data_mask) const;
+};
+
 typedef enum e_mesh_id
 {
 	MESH_ID_HAIR,
@@ -213,6 +220,25 @@ protected:
 	virtual ~LLVOAvatar();
 
 public:
+
+	struct CompareScreenAreaGreater
+	{		
+		bool operator()(const LLCharacter* const& lhs, const LLCharacter* const& rhs)
+		{
+			return lhs->getPixelArea() > rhs->getPixelArea();
+		}
+	};
+
+	enum 
+	{
+		VERTEX_DATA_MASK =	(1 << LLVertexBuffer::TYPE_VERTEX) |
+							(1 << LLVertexBuffer::TYPE_NORMAL) |
+							(1 << LLVertexBuffer::TYPE_TEXCOORD) |
+							(1 << LLVertexBuffer::TYPE_WEIGHT) |
+							(1 << LLVertexBuffer::TYPE_CLOTHWEIGHT)							
+	}
+	eVertexDataMask;
+
 	LLVOAvatar(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp);
 	/*virtual*/ void markDead();
 
@@ -228,20 +254,24 @@ public:
 										const EObjectUpdateType update_type,
 										LLDataPacker *dp);
 	virtual BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time);
+	virtual BOOL updateLOD();
 	void setFootPlane(const LLVector4 &plane) { mFootPlane = plane; }
 	/*virtual*/ BOOL    isActive() const; // Whether this object needs to do an idleUpdate.
 
 	// Graphical stuff for objects - maybe broken out into render class later?
 
-	U32 renderSkinned(EAvatarRenderPass pass);
+	U32 renderFootShadows();
 	U32 renderRigid();
-
+	U32 renderSkinned(EAvatarRenderPass pass);
+	U32 renderTransparent();
 	void renderCollisionVolumes();
 	
 	/*virtual*/ void updateTextures(LLAgent &agent);
 	// If setting a baked texture, need to request it from a non-local sim.
 	/*virtual*/ S32 setTETexture(const U8 te, const LLUUID& uuid);
-
+	
+	virtual U32 getPartitionType() const;
+	
 	void updateVisibility(BOOL force_invisible);
 	void updateAttachmentVisibility(U32 camera_mode);
 	void clampAttachmentPositions();
@@ -259,11 +289,14 @@ public:
 	void updateShadowFaces();
 
 	/*virtual*/ void		setPixelAreaAndAngle(LLAgent &agent);
-	void					updateJointLODs();
+	BOOL					updateJointLODs();
 
 	void writeCAL3D(std::string& path, std::string& file_base);
 
 	virtual void updateRegion(LLViewerRegion *regionp);
+
+	
+	void updateSpatialExtents(LLVector3& newMin, LLVector3 &newMax);
 	
 	//--------------------------------------------------------------------
 	// texture entry assignment
@@ -323,7 +356,7 @@ public:
 	virtual BOOL allocateCharacterJoints( U32 num );
 	virtual LLJoint *getCharacterJoint( U32 num );
 	virtual void requestStopMotion( LLMotion* motion );
-	virtual F32 getPixelArea();
+	virtual F32 getPixelArea() const;
 	virtual LLPolyMesh*	getHeadMesh();
 	virtual LLPolyMesh*	getUpperBodyMesh();
 	virtual LLVector3d	getPosGlobalFromAgent(const LLVector3 &position);
@@ -343,7 +376,7 @@ public:
 	// Other public functions
 	//--------------------------------------------------------------------
 	BOOL			allocateCollisionVolumes( U32 num );
-
+	void			resetHUDAttachments();
 	static void		getAnimLabels( LLDynamicArray<const char*>* labels );
 	static void		getAnimNames( LLDynamicArray<const char*>* names );
 
@@ -420,6 +453,7 @@ public:
 	virtual void addChild(LLViewerObject *childp);
 	virtual void removeChild(LLViewerObject *childp);
 
+	LLViewerJointAttachment* getTargetAttachmentPoint(LLViewerObject* viewer_object);
 	BOOL attachObject(LLViewerObject *viewer_object);
 	BOOL detachObject(LLViewerObject *viewer_object);
 	void lazyAttach();
@@ -817,7 +851,6 @@ protected:
 
 	F32		mRenderPriority;
 	F32		mAdjustedPixelArea;
-	S32		mNumAGPVertices;
 
 	LLWString mNameString;
 	LLString  mTitle;
@@ -858,6 +891,8 @@ protected:
 	U32					mLowerMaskTexName;
 
 	BOOL				mCulled;
+	F32					mMinPixelArea; // debug
+	F32					mMaxPixelArea; // debug
 	
 	//--------------------------------------------------------------------
 	// Global Colors
@@ -884,6 +919,7 @@ protected:
 	
 	void			requestLayerSetUpdate(LLVOAvatar::ELocTexIndex i);
 	void			addLocalTextureStats(LLVOAvatar::ELocTexIndex i, LLViewerImage* imagep, F32 texel_area_ratio, BOOL rendered, BOOL covered_by_baked);
+	void			addBakedTextureStats( LLViewerImage* imagep, F32 pixel_area, F32 texel_area_ratio, S32 boost_level);
 	static void		onInitialBakedTextureLoaded( BOOL success, LLViewerImage *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata );
 	static void		onBakedTextureLoaded(BOOL success, LLViewerImage *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata);
 	void			useBakedTexture(const LLUUID& id);
diff --git a/indra/newview/llvoclouds.cpp b/indra/newview/llvoclouds.cpp
index b4924a45f1544fe859cf29076463226cdb27b98d..1e5caa17d42439ac075ed5fd854e868e16e0c1a1 100644
--- a/indra/newview/llvoclouds.cpp
+++ b/indra/newview/llvoclouds.cpp
@@ -10,6 +10,8 @@
 
 #include "llvoclouds.h"
 
+#include "lldrawpoolalpha.h"
+
 #include "llviewercontrol.h"
 
 #include "llagent.h"		// to get camera position
@@ -27,7 +29,7 @@
 #include "viewer.h"
 
 LLVOClouds::LLVOClouds(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp)
-:	LLViewerObject(id, LL_VO_CLOUDS, regionp)
+:	LLAlphaObject(id, LL_VO_CLOUDS, regionp)
 {
 	mCloudGroupp = NULL;
 	mbCanSelect = FALSE;
@@ -80,77 +82,132 @@ LLDrawable* LLVOClouds::createDrawable(LLPipeline *pipeline)
 	mDrawable->setLit(FALSE);
 	mDrawable->setRenderType(LLPipeline::RENDER_TYPE_CLOUDS);
 
-	LLDrawPool *pool = gPipeline.getPool(LLDrawPool::POOL_CLOUDS);
-
-	mDrawable->setNumFaces(1, pool, getTEImage(0));
-
 	return mDrawable;
 }
 
 BOOL LLVOClouds::updateGeometry(LLDrawable *drawable)
 {
+	LLFastTimer ftm(LLFastTimer::FTM_UPDATE_CLOUDS);
  	if (!(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_CLOUDS)))
 		return TRUE;
 	
-	LLVector3 at;
-	LLVector3 up;
-	LLVector3 right;
-	LLVector3 normal(0.f,0.f,-1.f);
-	LLVector3 position_agent;
-	//LLVector3 v[4];
 	LLFace *facep;
-	const LLVector3 region_pos_agent = mRegionp->getOriginAgent();
-	const LLVector3 camera_agent = gAgent.getCameraPositionAgent();
-	LLVector3 center_offset = getPositionRegion();
-	LLVector2 uvs[4];
-
-	uvs[0].setVec(0.f, 1.f);
-	uvs[1].setVec(0.f, 0.f);
-	uvs[2].setVec(1.f, 1.f);
-	uvs[3].setVec(1.f, 0.f);
-
-	LLVector3 vtx[4];
-
+	
 	S32 num_faces = mCloudGroupp->getNumPuffs();
 
-	drawable->setNumFacesFast(num_faces, gPipeline.getPool(LLDrawPool::POOL_CLOUDS), getTEImage(0));
+	if (num_faces > drawable->getNumFaces())
+	{
+		drawable->setNumFacesFast(num_faces, NULL, getTEImage(0));
+	}
+
+	mDepth = (getPositionAgent()-gCamera->getOrigin())*gCamera->getAtAxis();
 
 	S32 face_indx = 0;
 	for ( ;	face_indx < num_faces; face_indx++)
 	{
 		facep = drawable->getFace(face_indx);
-
-		LLStrider<LLVector3> verticesp, normalsp;
-		LLStrider<LLVector2> texCoordsp;
-		U32 *indicesp;
-		S32 index_offset;
-
-		facep->setPrimType(LLTriangles);
-		facep->setSize(4, 6);
-		index_offset = facep->getGeometry(verticesp,normalsp,texCoordsp, indicesp);
-		if (-1 == index_offset)
+		if (isParticle())
 		{
-			return TRUE;
+			facep->setSize(1,1);
 		}
-
+		else
+		{
+			facep->setSize(4, 6);
+		}
+		facep->setTEOffset(face_indx);
+		facep->setTexture(getTEImage(0));
 		const LLCloudPuff &puff = mCloudGroupp->getPuff(face_indx);
 		const LLVector3 puff_pos_agent = gAgent.getPosAgentFromGlobal(puff.getPositionGlobal());
-		facep->mCenterAgent = puff_pos_agent;
+		facep->mCenterLocal = puff_pos_agent;
+	}
+	for ( ; face_indx < drawable->getNumFaces(); face_indx++)
+	{
+		facep = drawable->getFace(face_indx);
+		facep->setTEOffset(face_indx);
+		facep->setSize(0,0);
+	}
+
+	drawable->movePartition();
+
+	return TRUE;
+}
+
+BOOL LLVOClouds::isParticle()
+{
+	return FALSE; // gGLManager.mHasPointParameters;
+}
+
+F32 LLVOClouds::getPartSize(S32 idx)
+{
+	return (CLOUD_PUFF_HEIGHT+CLOUD_PUFF_WIDTH)*0.5f;
+}
+
+void LLVOClouds::getGeometry(S32 te, 
+							LLStrider<LLVector3>& verticesp, 
+							LLStrider<LLVector3>& normalsp, 
+							LLStrider<LLVector2>& texcoordsp, 
+							LLStrider<LLColor4U>& colorsp, 
+							LLStrider<U32>& indicesp)
+{
 
-		LLVector3 from_camera_vec = gCamera->getAtAxis();//puff_pos_agent - camera_agent;
-		at = from_camera_vec;
+	if (te >= mCloudGroupp->getNumPuffs())
+	{
+		return;
+	}
+
+	LLDrawable* drawable = mDrawable;
+	LLFace *facep = drawable->getFace(te);
+
+	if (!facep->hasGeometry())
+	{
+		return;
+	}
+	
+	LLVector3 normal(0.f,0.f,-1.f);
+
+	const LLCloudPuff &puff = mCloudGroupp->getPuff(te);
+	S32 index_offset = facep->getGeomIndex();
+	LLColor4U color(255, 255, 255, (U8) (puff.getAlpha()*255));
+	facep->setFaceColor(LLColor4(color));
+		
+	
+	if (isParticle())
+	{
+		*verticesp++ = facep->mCenterLocal;
+		*texcoordsp++ = LLVector2(0.5f, 0.5f);
+		*colorsp++ = color;
+		*normalsp++ = normal;
+		*indicesp++ = facep->getGeomIndex();
+	}
+	else
+	{
+		LLVector3 up;
+		LLVector3 right;
+		LLVector3 at;
+
+		const LLVector3& puff_pos_agent = facep->mCenterLocal;
+		LLVector2 uvs[4];
+
+		uvs[0].setVec(0.f, 1.f);
+		uvs[1].setVec(0.f, 0.f);
+		uvs[2].setVec(1.f, 1.f);
+		uvs[3].setVec(1.f, 0.f);
+
+		LLVector3 vtx[4];
+
+		at = gCamera->getAtAxis();
 		right = at % LLVector3(0.f, 0.f, 1.f);
 		right.normVec();
 		up = right % at;
 		up.normVec();
 		right *= 0.5f*CLOUD_PUFF_WIDTH;
 		up *= 0.5f*CLOUD_PUFF_HEIGHT;;
-
-		facep->mCenterAgent = puff_pos_agent;
-
-		LLColor4 color(1.f, 1.f, 1.f, puff.getAlpha());
-		facep->setFaceColor(color);
 		
+		*colorsp++ = color;
+		*colorsp++ = color;
+		*colorsp++ = color;
+		*colorsp++ = color;
+
 		vtx[0] = puff_pos_agent - right + up;
 		vtx[1] = puff_pos_agent - right - up;
 		vtx[2] = puff_pos_agent + right + up;
@@ -161,10 +218,10 @@ BOOL LLVOClouds::updateGeometry(LLDrawable *drawable)
 		*verticesp++  = vtx[2];
 		*verticesp++  = vtx[3];
 
-		*texCoordsp++ = uvs[0];
-		*texCoordsp++ = uvs[1];
-		*texCoordsp++ = uvs[2];
-		*texCoordsp++ = uvs[3];
+		*texcoordsp++ = uvs[0];
+		*texcoordsp++ = uvs[1];
+		*texcoordsp++ = uvs[2];
+		*texcoordsp++ = uvs[3];
 
 		*normalsp++   = normal;
 		*normalsp++   = normal;
@@ -179,10 +236,28 @@ BOOL LLVOClouds::updateGeometry(LLDrawable *drawable)
 		*indicesp++ = index_offset + 3;
 		*indicesp++ = index_offset + 2;
 	}
-	for ( ; face_indx < drawable->getNumFaces(); face_indx++) 
+}
+
+U32 LLVOClouds::getPartitionType() const
+{
+	return LLPipeline::PARTITION_CLOUD;
+}
+
+// virtual
+void LLVOClouds::updateDrawable(BOOL force_damped)
+{
+	// Force an immediate rebuild on any update
+	if (mDrawable.notNull())
 	{
-		drawable->getFace(face_indx)->setSize(0,0);
+		mDrawable->updateXform(TRUE);
+		gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_ALL, TRUE);
 	}
+	clearChanged(SHIFTED);
+}
 
-	return TRUE;
+LLCloudPartition::LLCloudPartition()
+{
+	mDrawableType = LLPipeline::RENDER_TYPE_CLOUDS;
+	mPartitionType = LLPipeline::PARTITION_CLOUD;
 }
+
diff --git a/indra/newview/llvoclouds.h b/indra/newview/llvoclouds.h
index 0debe207301b5bdd57fe8b9d97982dd856607d61..180e4703b9560d6d7f1ec937187758050fc6060b 100644
--- a/indra/newview/llvoclouds.h
+++ b/indra/newview/llvoclouds.h
@@ -19,7 +19,7 @@ class LLViewerCloudGroup;
 class LLCloudGroup;
 
 
-class LLVOClouds : public LLViewerObject
+class LLVOClouds : public LLAlphaObject
 {
 public:
 	LLVOClouds(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp );
@@ -28,16 +28,29 @@ public:
 	// Initialize data that's only inited once per class.
 	static void initClass();
 
+	void updateDrawable(BOOL force_damped); 
+
 	/*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline);
 	/*virtual*/ BOOL        updateGeometry(LLDrawable *drawable);
+	/*virtual*/ void		getGeometry(S32 te, 
+							LLStrider<LLVector3>& verticesp, 
+							LLStrider<LLVector3>& normalsp, 
+							LLStrider<LLVector2>& texcoordsp, 
+							LLStrider<LLColor4U>& colorsp, 
+							LLStrider<U32>& indicesp);
 
 	/*virtual*/ BOOL    isActive() const; // Whether this object needs to do an idleUpdate.
+	BOOL isParticle();
+	F32 getPartSize(S32 idx);
 
 	/*virtual*/ void updateTextures(LLAgent &agent);
 	/*virtual*/ void setPixelAreaAndAngle(LLAgent &agent); // generate accurate apparent angle and area
-
+	
+	void updateFaceSize(S32 idx) { }
 	BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time);
 
+	virtual U32 getPartitionType() const;
+
 	void setCloudGroup(LLCloudGroup *cgp)		{ mCloudGroupp = cgp; }
 protected:
 	LLCloudGroup *mCloudGroupp;
diff --git a/indra/newview/llvograss.cpp b/indra/newview/llvograss.cpp
index b9d39272acdc92a8e106613ebe73e5315b662916..418a810f6a67f4d688c78fcca447d23b5f6023b6 100644
--- a/indra/newview/llvograss.cpp
+++ b/indra/newview/llvograss.cpp
@@ -49,7 +49,7 @@ S32 LLVOGrass::sMaxGrassSpecies = 0;
 
 
 LLVOGrass::LLVOGrass(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp)
-:	LLViewerObject(id, pcode, regionp)
+:	LLAlphaObject(id, pcode, regionp)
 {
 	mPatch               = NULL;
 	mLastPatchUpdateTime = 0;
@@ -299,38 +299,45 @@ void LLVOGrass::setPixelAreaAndAngle(LLAgent &agent)
 {
 	// This should be the camera's center, as soon as we move to all region-local.
 	LLVector3 relative_position = getPositionAgent() - agent.getCameraPositionAgent();
-	F32 range = relative_position.magVec();				// ugh, square root
+	F32 range = relative_position.magVec();
 
 	F32 max_scale = getMaxScale();
 
 	mAppAngle = (F32) atan2( max_scale, range) * RAD_TO_DEG;
 
 	// Compute pixels per meter at the given range
-	F32 pixels_per_meter = gCamera->getViewHeightInPixels() / 
-						(tan(gCamera->getView()) * range);
+	F32 pixels_per_meter = gCamera->getViewHeightInPixels() / (tan(gCamera->getView()) * range);
 
-	// Assume grass texture is a 1 meter by 1 meter sprite at the grass object's center
-	mPixelArea = (pixels_per_meter) * (pixels_per_meter);
+	// Assume grass texture is a 5 meter by 5 meter sprite at the grass object's center
+	mPixelArea = (pixels_per_meter) * (pixels_per_meter) * 25.f;
 }
 
 
 // BUG could speed this up by caching the relative_position and range calculations
 void LLVOGrass::updateTextures(LLAgent &agent)
 {
-	// dot_product = A B cos T
-	// BUT at_axis is unit, so dot_product = B cos T
-	LLVector3 relative_position = getPositionAgent() - agent.getCameraPositionAgent();
-	F32 dot_product = relative_position * agent.getFrameAgent().getAtAxis();
-	F32 cos_angle = dot_product / relative_position.magVec();
+	F32 texel_area_ratio = 1.f;
+	F32 cos_angle = 1.f;
 
 	if (getTEImage(0))
 	{
-		getTEImage(0)->addTextureStats(mPixelArea*20.f, 1.f, cos_angle);
+		if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_AREA))
+		{
+			setDebugText(llformat("%4.0f", fsqrtf(mPixelArea)));
+		}
+		getTEImage(0)->addTextureStats(mPixelArea, texel_area_ratio, cos_angle);
 	}
 }
 
 BOOL LLVOGrass::updateLOD()
 {
+	if (mDrawable->getNumFaces() <= 0)
+	{
+		return FALSE;
+	}
+	
+	LLFace* face = mDrawable->getFace(0);
+
 	F32 tan_angle = 0.f;
 	S32 num_blades = 0;
 
@@ -344,6 +351,7 @@ BOOL LLVOGrass::updateLOD()
 			mNumBlades <<= 1;
 		}
 
+		face->setSize(mNumBlades*8, mNumBlades*12);
 		gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_ALL, TRUE);
 	}
 	else if (num_blades <= (mNumBlades >> 1))
@@ -353,6 +361,7 @@ BOOL LLVOGrass::updateLOD()
 			mNumBlades >>=1;
 		}
 
+		face->setSize(mNumBlades*8, mNumBlades*12);
 		gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_ALL, TRUE);
 		return TRUE;
 	}
@@ -363,53 +372,20 @@ BOOL LLVOGrass::updateLOD()
 LLDrawable* LLVOGrass::createDrawable(LLPipeline *pipeline)
 {
 	pipeline->allocDrawable(this);
-// 	mDrawable->setLit(FALSE);
 	mDrawable->setRenderType(LLPipeline::RENDER_TYPE_GRASS);
-
-	LLDrawPool *pool = gPipeline.getPool(LLDrawPool::POOL_ALPHA);
-
-	mDrawable->setNumFaces(1, pool, getTEImage(0));
-
+	
 	return mDrawable;
 }
 
 BOOL LLVOGrass::updateGeometry(LLDrawable *drawable)
 {
+	LLFastTimer ftm(LLFastTimer::FTM_UPDATE_GRASS);
 	plantBlades();
 	return TRUE;
 }
 
 void LLVOGrass::plantBlades()
 {
-	mPatch               = mRegionp->getLand().resolvePatchRegion(getPositionRegion());
-	mLastPatchUpdateTime = mPatch->getLastUpdateTime();
-	
-	LLVector3 position;
-	// Create random blades of grass with gaussian distribution
-	F32 x,y,xf,yf,dzx,dzy;
-
-	LLVector3 normal(0,0,1);
-	LLColor4U color(0,0,0,1);
-
-	LLFace *face = mDrawable->getFace(0);
-
-	LLStrider<LLVector3> verticesp;
-	LLStrider<LLVector3> normalsp;
-	LLStrider<LLVector2> texCoordsp;
-	LLStrider<LLColor4U> colorsp;
-
-	U32 *indicesp;
-
-	face->setPool(face->getPool(), getTEImage(0));
-	face->setState(LLFace::GLOBAL);
-	face->setSize(mNumBlades * 4, mNumBlades * 12);
-	face->setPrimType(LLTriangles);
-	S32 index_offset = face->getGeometryColors(verticesp,normalsp,texCoordsp,colorsp,indicesp);
-	if (-1 == index_offset)
-	{
-		return;
-	}
-
 	// It is possible that the species of a grass is not defined
 	// This is bad, but not the end of the world.
 	if (!sSpeciesTable.count(mSpecies))
@@ -418,9 +394,49 @@ void LLVOGrass::plantBlades()
 		return;
 	}
 	
+	if (mDrawable->getNumFaces() < 1)
+	{
+		mDrawable->setNumFaces(1, NULL, getTEImage(0));
+	}
+		
+	LLFace *face = mDrawable->getFace(0);
+
+	face->setTexture(getTEImage(0));
+	face->setState(LLFace::GLOBAL);
+	face->setSize(mNumBlades * 8, mNumBlades * 12);
+	face->mVertexBuffer = NULL;
+	face->setTEOffset(0);
+	face->mCenterLocal = mPosition + mRegionp->getOriginAgent();
+	
+	mDepth = (face->mCenterLocal - gCamera->getOrigin())*gCamera->getAtAxis();
+	mDrawable->setPosition(face->mCenterLocal);
+	mDrawable->movePartition();
+	LLPipeline::sCompiles++;
+}
+
+void LLVOGrass::getGeometry(S32 idx,
+								LLStrider<LLVector3>& verticesp,
+								LLStrider<LLVector3>& normalsp, 
+								LLStrider<LLVector2>& texcoordsp,
+								LLStrider<LLColor4U>& colorsp, 
+								LLStrider<U32>& indicesp)
+{
+	mPatch = mRegionp->getLand().resolvePatchRegion(getPositionRegion());
+	mLastPatchUpdateTime = mPatch->getLastUpdateTime();
+	
+	LLVector3 position;
+	// Create random blades of grass with gaussian distribution
+	F32 x,y,xf,yf,dzx,dzy;
+
+	LLColor4U color(255,255,255,255);
+
+	LLFace *face = mDrawable->getFace(idx);
+
 	F32 width  = sSpeciesTable[mSpecies]->mBladeSizeX;
 	F32 height = sSpeciesTable[mSpecies]->mBladeSizeY;
 
+	U32 index_offset = face->getGeomIndex();
+
 	for (S32 i = 0;  i < mNumBlades; i++)
 	{
 		x   = exp_x[i] * mScale.mV[VX];
@@ -430,70 +446,113 @@ void LLVOGrass::plantBlades()
 		dzx = dz_x [i];
 		dzy = dz_y [i];
 
+		LLVector3 v1,v2,v3;
 		F32 blade_height= GRASS_BLADE_HEIGHT * height * w_mod[i];
 
-		*texCoordsp++   = LLVector2(0, 0);
-		*texCoordsp++   = LLVector2(0, 0.98f);
-		*texCoordsp++   = LLVector2(1, 0);
-		*texCoordsp++   = LLVector2(1, 0.98f);
+		*texcoordsp++   = LLVector2(0, 0);
+		*texcoordsp++   = LLVector2(0, 0);
+		*texcoordsp++   = LLVector2(0, 0.98f);
+		*texcoordsp++   = LLVector2(0, 0.98f);
+		*texcoordsp++   = LLVector2(1, 0);
+		*texcoordsp++   = LLVector2(1, 0);
+		*texcoordsp++   = LLVector2(1, 0.98f);
+		*texcoordsp++   = LLVector2(1, 0.98f);
 
 		position.mV[0]  = mPosition.mV[VX] + x + xf;
 		position.mV[1]  = mPosition.mV[VY] + y + yf;
-		position.mV[2]  = 0.f;
 		position.mV[2]  = mRegionp->getLand().resolveHeightRegion(position);
-		*verticesp++    = position + mRegionp->getOriginAgent();
+		*verticesp++    = v1 = position + mRegionp->getOriginAgent();
+		*verticesp++    = v1;
+
 
 		position.mV[0] += dzx;
 		position.mV[1] += dzy;
 		position.mV[2] += blade_height;
-		*verticesp++    = position + mRegionp->getOriginAgent();
+		*verticesp++    = v2 = position + mRegionp->getOriginAgent();
+		*verticesp++    = v2;
 
 		position.mV[0]  = mPosition.mV[VX] + x - xf;
 		position.mV[1]  = mPosition.mV[VY] + y - xf;
-		position.mV[2]  = 0.f;
 		position.mV[2]  = mRegionp->getLand().resolveHeightRegion(position);
-		*verticesp++    = position + mRegionp->getOriginAgent();
+		*verticesp++    = v3 = position + mRegionp->getOriginAgent();
+		*verticesp++    = v3;
+
+		LLVector3 normal1 = (v1-v2) % (v2-v3);
+		normal1.mV[VZ] = 0.75f;
+		normal1.normVec();
+		LLVector3 normal2 = -normal1;
+		normal2.mV[VZ] = -normal2.mV[VZ];
 
 		position.mV[0] += dzx;
 		position.mV[1] += dzy;
 		position.mV[2] += blade_height;
-		*verticesp++    = position + mRegionp->getOriginAgent();
+		*verticesp++    = v1 = position + mRegionp->getOriginAgent();
+		*verticesp++    = v1;
+
+		*(normalsp++)   = normal1;
+		*(normalsp++)   = normal2;
+		*(normalsp++)   = normal1;
+		*(normalsp++)   = normal2;
 
-		*(normalsp++)   = normal;
-		*(normalsp++)   = normal;
-		*(normalsp++)   = normal;
-		*(normalsp++)   = normal;
+		*(normalsp++)   = normal1;
+		*(normalsp++)   = normal2;
+		*(normalsp++)   = normal1;
+		*(normalsp++)   = normal2;
 
+		*(colorsp++)   = color;
+		*(colorsp++)   = color;
+		*(colorsp++)   = color;
+		*(colorsp++)   = color;
 		*(colorsp++)   = color;
 		*(colorsp++)   = color;
 		*(colorsp++)   = color;
 		*(colorsp++)   = color;
 		
 		*indicesp++     = index_offset + 0;
-		*indicesp++     = index_offset + 1;
 		*indicesp++     = index_offset + 2;
+		*indicesp++     = index_offset + 4;
 
-		*indicesp++     = index_offset + 1;
-		*indicesp++     = index_offset + 3;
 		*indicesp++     = index_offset + 2;
+		*indicesp++     = index_offset + 6;
+		*indicesp++     = index_offset + 4;
 
-		*indicesp++     = index_offset + 0;
-		*indicesp++     = index_offset + 2;
 		*indicesp++     = index_offset + 1;
+		*indicesp++     = index_offset + 5;
+		*indicesp++     = index_offset + 3;
 
-		*indicesp++     = index_offset + 1;
-		*indicesp++     = index_offset + 2;
 		*indicesp++     = index_offset + 3;
-		index_offset   += 4;
+		*indicesp++     = index_offset + 5;
+		*indicesp++     = index_offset + 7;
+		index_offset   += 8;
 	}
 
 	LLPipeline::sCompiles++;
-
-	face->mCenterLocal = mPosition;
-
 }
 
+U32 LLVOGrass::getPartitionType() const
+{
+	return LLPipeline::PARTITION_GRASS;
+}
 
+LLGrassPartition::LLGrassPartition()
+{
+	mDrawableType = LLPipeline::RENDER_TYPE_GRASS;
+	mPartitionType = LLPipeline::PARTITION_GRASS;
+	mLODPeriod = 16;
+	mDepthMask = TRUE;
+	mSlopRatio = 0.1f;
+	mRenderPass = LLRenderPass::PASS_GRASS;
+	mBufferUsage = GL_DYNAMIC_DRAW_ARB;
+}
 
-
-
+// virtual
+void LLVOGrass::updateDrawable(BOOL force_damped)
+{
+	// Force an immediate rebuild on any update
+	if (mDrawable.notNull())
+	{
+		mDrawable->updateXform(TRUE);
+		gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_ALL, TRUE);
+	}
+	clearChanged(SHIFTED);
+}
diff --git a/indra/newview/llvograss.h b/indra/newview/llvograss.h
index ef3d30c5eaae68afaba6b4ddc7b2a85e64888e97..38aef64cd8f63f6c0ab3958c050be990a1ae2f71 100644
--- a/indra/newview/llvograss.h
+++ b/indra/newview/llvograss.h
@@ -17,7 +17,7 @@ class LLSurfacePatch;
 class LLViewerImage;
 
 
-class LLVOGrass : public LLViewerObject
+class LLVOGrass : public LLAlphaObject
 {
 public:
 	LLVOGrass(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp);
@@ -27,6 +27,8 @@ public:
 	static void initClass();
 	static void cleanupClass();
 
+	virtual U32 getPartitionType() const;
+
 	/*virtual*/ U32 processUpdateMessage(LLMessageSystem *mesgsys,
 											void **user_data,
 											U32 block_num, 
@@ -34,11 +36,19 @@ public:
 											LLDataPacker *dp);
 	static void import(FILE *file, LLMessageSystem *mesgsys, const LLVector3 &pos);
 	/*virtual*/ void exportFile(FILE *file, const LLVector3 &position);
-	
+
+	void updateDrawable(BOOL force_damped);
 
 	/*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline);
 	/*virtual*/ BOOL		updateGeometry(LLDrawable *drawable);
-
+	/*virtual*/ void		getGeometry(S32 idx,
+								LLStrider<LLVector3>& verticesp,
+								LLStrider<LLVector3>& normalsp, 
+								LLStrider<LLVector2>& texcoordsp,
+								LLStrider<LLColor4U>& colorsp, 
+								LLStrider<U32>& indicesp);
+
+	void updateFaceSize(S32 idx) { }
 	/*virtual*/ void updateTextures(LLAgent &agent);											
 	/*virtual*/ BOOL updateLOD();
 	/*virtual*/ void setPixelAreaAndAngle(LLAgent &agent); // generate accurate apparent angle and area
diff --git a/indra/newview/llvoground.cpp b/indra/newview/llvoground.cpp
index dd3ef61173aabef28294c86a761a761fff86a5df..ef8194371870e2a1db77bdcc7b874e29c11dd43d 100644
--- a/indra/newview/llvoground.cpp
+++ b/indra/newview/llvoground.cpp
@@ -9,6 +9,7 @@
 #include "llviewerprecompiledheaders.h"
 
 #include "llvoground.h"
+#include "lldrawpoolground.h"
 
 #include "llviewercontrol.h"
 
@@ -21,7 +22,7 @@
 #include "pipeline.h"
 
 LLVOGround::LLVOGround(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp)
-:	LLViewerObject(id, pcode, regionp)
+:	LLStaticViewerObject(id, pcode, regionp)
 {
 	mbCanSelect = FALSE;
 }
@@ -38,10 +39,10 @@ BOOL LLVOGround::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
 		return TRUE;
 	}
 	
-	if (mDrawable)
+	/*if (mDrawable)
 	{
 		gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);
-	}
+	}*/
 	return TRUE;
 }
 
@@ -56,7 +57,8 @@ LLDrawable *LLVOGround::createDrawable(LLPipeline *pipeline)
 	pipeline->allocDrawable(this);
 	mDrawable->setLit(FALSE);
 
-	LLDrawPool *poolp = gPipeline.getPool(LLDrawPool::POOL_GROUND);
+	mDrawable->setRenderType(LLPipeline::RENDER_TYPE_GROUND);
+	LLDrawPoolGround *poolp = (LLDrawPoolGround*) gPipeline.getPool(LLDrawPool::POOL_GROUND);
 
 	mDrawable->addFace(poolp, NULL);
 
@@ -68,15 +70,25 @@ BOOL LLVOGround::updateGeometry(LLDrawable *drawable)
 	LLStrider<LLVector3> verticesp;
 	LLStrider<LLVector3> normalsp;
 	LLStrider<LLVector2> texCoordsp;
-	U32 *indicesp;
+	LLStrider<U32> indicesp;
 	S32 index_offset;
 	LLFace *face;	
 
+	LLDrawPoolGround *poolp = (LLDrawPoolGround*) gPipeline.getPool(LLDrawPool::POOL_GROUND);
+
 	if (drawable->getNumFaces() < 1)
-		drawable->addFace(gPipeline.getPool(LLDrawPool::POOL_GROUND), NULL);
+		drawable->addFace(poolp, NULL);
 	face = drawable->getFace(0); 
-	face->setPrimType(LLTriangles);
-	face->setSize(6, 12);
+		
+	if (face->mVertexBuffer.isNull())
+	{
+		face->setSize(5, 12);
+		face->mVertexBuffer = new LLVertexBuffer(LLDrawPoolGround::VERTEX_DATA_MASK, GL_STREAM_DRAW_ARB);
+		face->mVertexBuffer->allocateBuffer(face->getGeomCount(), face->getIndicesCount(), TRUE);
+		face->setGeomIndex(0);
+		face->setIndicesIndex(0);
+	}
+	
 	index_offset = face->getGeometry(verticesp,normalsp,texCoordsp, indicesp);
 	if (-1 == index_offset)
 	{
@@ -98,68 +110,40 @@ BOOL LLVOGround::updateGeometry(LLDrawable *drawable)
 	left_dir.normVec();
 
 	// Our center top point
-	LLVector3 center_top, center_bottom;
-
 	LLColor4 ground_color = gSky.getFogColor();
 	ground_color.mV[3] = 1.f;
 	face->setFaceColor(ground_color);
 	
-	if (gCamera->getOrigin().mV[VZ] < gAgent.getRegion()->getWaterHeight())
-	{
-		// Underwater
-		//center_top = gCamera->getOrigin() + at_dir*gCamera->getFar();
-		center_top = gCamera->getOrigin() - LLVector3(0, 0, 5);
-		center_bottom = gCamera->getOrigin() + at_dir*gCamera->getFar();;
-		//center_top.mV[VZ] = gAgent.getRegion()->getWaterHeight() + 0.5f;
-		center_bottom.mV[VZ] = -100.f;
-	}
-	else
-	{
-		// Above water
-		center_top = gCamera->getOrigin() - LLVector3(0, 0, 30);
-		if ((gPipeline.getVertexShaderLevel(LLPipeline::SHADER_ENVIRONMENT) > 0))
-		{
-			center_top.mV[VZ] = gAgent.getRegion()->getWaterHeight();
-		}
-		//center_top = gCamera->getOrigin() + at_dir*9000.f;
-		center_bottom = gCamera->getOrigin() - at_dir*gCamera->getFar();
-		//center_top.mV[VZ] = 0.f;
-		//center_bottom.mV[VZ] = gAgent.getRegion()->getWaterHeight();
-	}
-
-	*(verticesp++)  = center_top + LLVector3(6000, 6000, 0);
-	*(verticesp++)  = center_top + LLVector3(-6000, 6000, 0);
-	*(verticesp++)  = center_top + LLVector3(-6000, -6000, 0);
-	
-	*(verticesp++)  = center_top + LLVector3(-6000, -6000, 0);
-	*(verticesp++)  = center_top + LLVector3(6000, -6000, 0);
-	*(verticesp++)  = center_top + LLVector3(6000, 6000, 0);
+	*(verticesp++)  = LLVector3(64, 64, 0);
+	*(verticesp++)  = LLVector3(-64, 64, 0);
+	*(verticesp++)  = LLVector3(-64, -64, 0);
+	*(verticesp++)  = LLVector3(64, -64, 0);
+	*(verticesp++)  = LLVector3(0, 0, -1024);
 	
 	
 	// Triangles for each side
 	*indicesp++ = index_offset + 0;
 	*indicesp++ = index_offset + 1;
-	*indicesp++ = index_offset + 3;
+	*indicesp++ = index_offset + 4;
 
-	*indicesp++ = index_offset + 0;
-	*indicesp++ = index_offset + 3;
+	*indicesp++ = index_offset + 1;
 	*indicesp++ = index_offset + 2;
+	*indicesp++ = index_offset + 4;
 
 	*indicesp++ = index_offset + 2;
 	*indicesp++ = index_offset + 3;
-	*indicesp++ = index_offset + 5;
+	*indicesp++ = index_offset + 4;
 
-	*indicesp++ = index_offset + 2;
-	*indicesp++ = index_offset + 5;
+	*indicesp++ = index_offset + 3;
+	*indicesp++ = index_offset + 0;
 	*indicesp++ = index_offset + 4;
 
 	*(texCoordsp++) = LLVector2(0.f, 0.f);
 	*(texCoordsp++) = LLVector2(1.f, 0.f);
-	*(texCoordsp++) = LLVector2(0.f, 1.f);
 	*(texCoordsp++) = LLVector2(1.f, 1.f);
-	*(texCoordsp++) = LLVector2(0.f, 2.f);
-	*(texCoordsp++) = LLVector2(1.f, 2.f);
-
+	*(texCoordsp++) = LLVector2(0.f, 1.f);
+	*(texCoordsp++) = LLVector2(0.5f, 0.5f);
+	
 	LLPipeline::sCompiles++;
 	return TRUE;
 }
diff --git a/indra/newview/llvoground.h b/indra/newview/llvoground.h
index 228d872eafcd7448a95b8ca6ae41c953903cfa79..587b0064de7e6a7363b089ec183241a19dd9c50c 100644
--- a/indra/newview/llvoground.h
+++ b/indra/newview/llvoground.h
@@ -15,7 +15,7 @@
 #include "llviewerimage.h"
 #include "llviewerobject.h"
 
-class LLVOGround : public LLViewerObject
+class LLVOGround : public LLStaticViewerObject
 {
 protected:
 public:
diff --git a/indra/newview/llvopartgroup.cpp b/indra/newview/llvopartgroup.cpp
index 38db15c0c73df12cbca0d870180081be5697ae48..b641bf6f276e70eb02df4a047b9823bc99a979e2 100644
--- a/indra/newview/llvopartgroup.cpp
+++ b/indra/newview/llvopartgroup.cpp
@@ -10,6 +10,8 @@
 
 #include "llvopartgroup.h"
 
+#include "lldrawpoolalpha.h"
+
 #include "llfasttimer.h"
 #include "message.h"
 #include "v2math.h"
@@ -28,7 +30,7 @@ const F32 MAX_PART_LIFETIME = 120.f;
 extern U64 gFrameTime;
 
 LLVOPartGroup::LLVOPartGroup(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp)
-	:	LLViewerObject(id, pcode, regionp),
+	:	LLAlphaObject(id, pcode, regionp),
 		mViewerPartGroupp(NULL)
 {
 	setNumTEs(1);
@@ -42,37 +44,37 @@ LLVOPartGroup::~LLVOPartGroup()
 {
 }
 
+
 BOOL LLVOPartGroup::isActive() const
 {
 	return TRUE;
 }
 
+F32 LLVOPartGroup::getBinRadius()
+{ 
+	return mScale.mV[0]*2.f;
+}
+
+void LLVOPartGroup::updateSpatialExtents(LLVector3& newMin, LLVector3& newMax)
+{		
+	LLVector3 pos_agent = getPositionAgent();
+	mExtents[0] = pos_agent - mScale;
+	mExtents[1] = pos_agent + mScale;
+	newMin = mExtents[0];
+	newMax = mExtents[1];
+	mDrawable->setPositionGroup(pos_agent);
+}
+
 BOOL LLVOPartGroup::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
 {
-	if (gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_PARTICLES))
-	{
-		gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);
-	}
-
-	// Particle groups don't need any of default idleUpdate velocity interpolation stuff.
-	//LLViewerObject::idleUpdate(agent, world, time);
 	return TRUE;
 }
 
-
 void LLVOPartGroup::setPixelAreaAndAngle(LLAgent &agent)
 {
 	// mPixelArea is calculated during render
-
-	LLVector3 viewer_pos_agent = agent.getCameraPositionAgent();
-	LLVector3 pos_agent = getRenderPosition();
-
-	F32 dx = viewer_pos_agent.mV[VX] - pos_agent.mV[VX];
-	F32 dy = viewer_pos_agent.mV[VY] - pos_agent.mV[VY];
-	F32 dz = viewer_pos_agent.mV[VZ] - pos_agent.mV[VZ];
-
 	F32 mid_scale = getMidScale();
-	F32 range = sqrt(dx*dx + dy*dy + dz*dz);
+	F32 range = (getRenderPosition()-gCamera->getOrigin()).magVec();
 
 	if (range < 0.001f || isHUDAttachment())		// range == zero
 	{
@@ -86,7 +88,7 @@ void LLVOPartGroup::setPixelAreaAndAngle(LLAgent &agent)
 
 void LLVOPartGroup::updateTextures(LLAgent &agent)
 {
-	// Texture stats for particles will need to be updated in a different way...
+	// Texture stats for particles need to be updated in a different way...
 }
 
 
@@ -95,37 +97,45 @@ LLDrawable* LLVOPartGroup::createDrawable(LLPipeline *pipeline)
 	pipeline->allocDrawable(this);
 	mDrawable->setLit(FALSE);
 	mDrawable->setRenderType(LLPipeline::RENDER_TYPE_PARTICLES);
-
-	LLDrawPool *pool = gPipeline.getPool(LLDrawPool::POOL_ALPHA);
-	mDrawable->setNumFaces(mViewerPartGroupp->getCount(), pool, getTEImage(0));
 	return mDrawable;
 }
 
  const F32 MAX_PARTICLE_AREA_SCALE = 0.02f; // some tuned constant, limits on how much particle area to draw
 
-BOOL LLVOPartGroup::updateGeometry(LLDrawable *drawable)
+F32 LLVOPartGroup::getPartSize(S32 idx)
 {
- 	LLFastTimer t(LLFastTimer::FTM_UPDATE_PARTICLES);
-	
-	LLVector3 at;
-	LLVector3 up;
-	LLVector3 right;
-	LLVector3 position_agent;
-	LLVector3 camera_agent = gAgent.getCameraPositionAgent();
-	LLVector2 uvs[4];
+	if (idx < (S32) mViewerPartGroupp->mParticles.size())
+	{
+		return mViewerPartGroupp->mParticles[idx]->mScale.mV[0];
+	}
+
+	return 0.f;
+}
 
-	uvs[0].setVec(0.f, 1.f);
-	uvs[1].setVec(0.f, 0.f);
-	uvs[2].setVec(1.f, 1.f);
-	uvs[3].setVec(1.f, 0.f);
+BOOL LLVOPartGroup::updateGeometry(LLDrawable *drawable)
+{
+	LLFastTimer ftm(LLFastTimer::FTM_UPDATE_PARTICLES);
 
-	LLDrawPool *drawpool = gPipeline.getPool(LLDrawPool::POOL_ALPHA);
+ 	LLVector3 at;
+	LLVector3 position_agent;
+	LLVector3 camera_agent = gCamera->getOrigin();
+	
 	S32 num_parts = mViewerPartGroupp->getCount();
 	LLFace *facep;
-	
+	LLSpatialGroup* group = drawable->getSpatialGroup();
+	if (!group && num_parts)
+	{
+		drawable->movePartition();
+		group = drawable->getSpatialGroup();
+	}
+
 	if (!num_parts)
 	{
-		drawable->setNumFaces(0, drawpool, getTEImage(0));
+		if (drawable->getNumFaces())
+		{
+			group->dirtyGeom();
+		}
+		drawable->setNumFaces(0, NULL, getTEImage(0));
 		LLPipeline::sCompiles++;
 		return TRUE;
 	}
@@ -135,30 +145,30 @@ BOOL LLVOPartGroup::updateGeometry(LLDrawable *drawable)
 		return TRUE;
 	}
 
-// 	drawable->setNumFaces(num_parts, drawpool, getTEImage(0));
-	drawable->setNumFacesFast(num_parts, drawpool, getTEImage(0));
-	
-	LLVector3 normal(-gCamera->getXAxis()); // make point agent face camera
-	
-	LLStrider<LLVector3> verticesp;
-	LLStrider<LLVector3> normalsp;
-	LLStrider<LLVector2> texCoordsp;
-	U32 *indicesp = 0;
+	if (num_parts > drawable->getNumFaces())
+	{
+		drawable->setNumFacesFast(num_parts+num_parts/4, NULL, getTEImage(0));
+	}
 
-	S32 vert_offset;
 	F32 tot_area = 0;
+	BOOL is_particle = isParticle();
+
 	F32 max_area = LLViewerPartSim::getMaxPartCount() * MAX_PARTICLE_AREA_SCALE; 
-	
+	F32 pixel_meter_ratio = gCamera->getPixelMeterRatio();
+	pixel_meter_ratio *= pixel_meter_ratio;
+
 	S32 count=0;
-	for (S32 i = 0; i < num_parts; i++)
+	S32 i;
+	F32 max_width = 0.f;
+	mDepth = 0.f;
+
+	for (i = 0; i < num_parts; i++)
 	{
-		const LLViewerPart &part = mViewerPartGroupp->mParticles[i];
+		const LLViewerPart &part = *((LLViewerPart*) mViewerPartGroupp->mParticles[i]);
 
 		LLVector3 part_pos_agent(part.mPosAgent);
 		at = part_pos_agent - camera_agent;
 
-		//F32 invcamdist = 1.0f / at.magVec();
-		//area += (part.mScale.mV[0]*invcamdist)*(part.mScale.mV[1]*invcamdist);
 		F32 camera_dist_squared = at.magVecSquared();
 		F32 inv_camera_dist_squared;
 		if (camera_dist_squared > 1.f)
@@ -167,29 +177,124 @@ BOOL LLVOPartGroup::updateGeometry(LLDrawable *drawable)
 			inv_camera_dist_squared = 1.f;
 		F32 area = part.mScale.mV[0] * part.mScale.mV[1] * inv_camera_dist_squared;
 		tot_area += area;
- 		if (tot_area > max_area)
+ 		
+		if (!is_particle && tot_area > max_area)
 		{
 			break;
 		}
-		
+	
 		count++;
 
 		facep = drawable->getFace(i);
+		facep->setTEOffset(i);
 		const F32 NEAR_PART_DIST_SQ = 5.f*5.f;  // Only discard particles > 5 m from the camera
 		const F32 MIN_PART_AREA = .005f*.005f;  // only less than 5 mm x 5 mm at 1 m from camera
-		if (camera_dist_squared > NEAR_PART_DIST_SQ && area < MIN_PART_AREA)
+		
+		if (!is_particle)
 		{
-			facep->setSize(0, 0);
-			continue;
+			if (camera_dist_squared > NEAR_PART_DIST_SQ && area < MIN_PART_AREA)
+			{
+				facep->setSize(0, 0);
+				continue;
+			}
+
+			facep->setSize(4, 6);
+		}
+		else
+		{		
+			facep->setSize(1,1);
+		}
+
+		facep->setViewerObject(this);
+
+		if (part.mFlags & LLPartData::LL_PART_EMISSIVE_MASK)
+		{
+			facep->setState(LLFace::FULLBRIGHT);
 		}
-		facep->setSize(4, 6);
-		facep->mCenterAgent = part_pos_agent;
-		vert_offset = facep->getGeometry(verticesp,normalsp,texCoordsp, indicesp);
-		if (-1 == vert_offset)
+		else
 		{
-			return TRUE;
+			facep->clearState(LLFace::FULLBRIGHT);
 		}
+
+		facep->mCenterLocal = part.mPosAgent;
+		facep->setFaceColor(part.mColor);
+		facep->setTexture(part.mImagep);
 		
+		if (i == 0)
+		{
+			mExtents[0] = mExtents[1] = part.mPosAgent;
+		}
+		else
+		{
+			update_min_max(mExtents[0], mExtents[1], part.mPosAgent);
+		}
+
+		max_width = llmax(max_width, part.mScale.mV[0]);
+		max_width = llmax(max_width, part.mScale.mV[1]);
+
+		mPixelArea = tot_area * pixel_meter_ratio;
+		const F32 area_scale = 10.f; // scale area to increase priority a bit
+		facep->setVirtualSize(mPixelArea*area_scale);
+	}
+	for (i = count; i < drawable->getNumFaces(); i++)
+	{
+		LLFace* facep = drawable->getFace(i);
+		facep->setTEOffset(i);
+		facep->setSize(0,0);
+	}
+	
+	LLVector3 y = gCamera->mYAxis;
+	LLVector3 z = gCamera->mZAxis;
+
+	LLVector3 pad;
+	for (i = 0; i < 3; i++)
+	{
+		pad.mV[i] = llmax(max_width, max_width * (fabsf(y.mV[i]) + fabsf(z.mV[i])));
+	}
+	
+	mExtents[0] -= pad;
+	mExtents[1] += pad;
+	
+	mDrawable->movePartition();
+	LLPipeline::sCompiles++;
+	return TRUE;
+}
+
+void LLVOPartGroup::getGeometry(S32 idx,
+								LLStrider<LLVector3>& verticesp,
+								LLStrider<LLVector3>& normalsp, 
+								LLStrider<LLVector2>& texcoordsp,
+								LLStrider<LLColor4U>& colorsp, 
+								LLStrider<U32>& indicesp)
+{
+	if (idx >= (S32) mViewerPartGroupp->mParticles.size())
+	{
+		return;
+	}
+
+	const LLViewerPart &part = *((LLViewerPart*) (mViewerPartGroupp->mParticles[idx]));
+
+	U32 vert_offset = mDrawable->getFace(idx)->getGeomIndex();
+
+	if (isParticle())
+	{
+		LLVector3 part_pos_agent(part.mPosAgent);
+
+		const LLVector3& normal = -gCamera->getXAxis();
+
+		*verticesp++ = part_pos_agent;
+		*normalsp++ = normal;
+		*colorsp++ = part.mColor;
+		*texcoordsp++ = LLVector2(0.5f, 0.5f);
+		*indicesp++ = vert_offset;
+	}
+	else
+	{
+		LLVector3 part_pos_agent(part.mPosAgent);
+		LLVector3 camera_agent = gAgent.getCameraPositionAgent();
+		LLVector3 at = part_pos_agent - camera_agent;
+		LLVector3 up, right;
+
 		right = at % LLVector3(0.f, 0.f, 1.f);
 		right.normVec();
 		up = right % at;
@@ -217,31 +322,28 @@ BOOL LLVOPartGroup::updateGeometry(LLDrawable *drawable)
 		right *= 0.5f*part.mScale.mV[0];
 		up *= 0.5f*part.mScale.mV[1];
 
+		const LLVector3& normal = -gCamera->getXAxis();
+		
 		*verticesp++ = part_pos_agent + up - right;
 		*verticesp++ = part_pos_agent - up - right;
 		*verticesp++ = part_pos_agent + up + right;
 		*verticesp++ = part_pos_agent - up + right;
 
-		*texCoordsp++ = uvs[0];
-		*texCoordsp++ = uvs[1];
-		*texCoordsp++ = uvs[2];
-		*texCoordsp++ = uvs[3];
+		*colorsp++ = part.mColor;
+		*colorsp++ = part.mColor;
+		*colorsp++ = part.mColor;
+		*colorsp++ = part.mColor;
+
+		*texcoordsp++ = LLVector2(0.f, 1.f);
+		*texcoordsp++ = LLVector2(0.f, 0.f);
+		*texcoordsp++ = LLVector2(1.f, 1.f);
+		*texcoordsp++ = LLVector2(1.f, 0.f);
 
 		*normalsp++   = normal;
 		*normalsp++   = normal;
 		*normalsp++   = normal;
 		*normalsp++   = normal;
 
-		if (part.mFlags & LLPartData::LL_PART_EMISSIVE_MASK)
-		{
-			facep->setState(LLFace::FULLBRIGHT);
-		}
-		else
-		{
-			facep->clearState(LLFace::FULLBRIGHT);
-		}
-		facep->setFaceColor(part.mColor);
-		
 		*indicesp++ = vert_offset + 0;
 		*indicesp++ = vert_offset + 1;
 		*indicesp++ = vert_offset + 2;
@@ -250,17 +352,149 @@ BOOL LLVOPartGroup::updateGeometry(LLDrawable *drawable)
 		*indicesp++ = vert_offset + 3;
 		*indicesp++ = vert_offset + 2;
 	}
-	for (S32 j = count; j < drawable->getNumFaces(); j++) 
+}
+
+BOOL LLVOPartGroup::isParticle()
+{
+	return FALSE; //gGLManager.mHasPointParameters && mViewerPartGroupp->mUniformParticles;
+}
+
+U32 LLVOPartGroup::getPartitionType() const
+{ 
+	return LLPipeline::PARTITION_PARTICLE; 
+}
+
+LLParticlePartition::LLParticlePartition()
+: LLSpatialPartition(LLDrawPoolAlpha::VERTEX_DATA_MASK)
+{
+	mRenderPass = LLRenderPass::PASS_ALPHA;
+	mDrawableType = LLPipeline::RENDER_TYPE_PARTICLES;
+	mPartitionType = LLPipeline::PARTITION_PARTICLE;
+	mBufferUsage = GL_DYNAMIC_DRAW_ARB;
+	mSlopRatio = 0.f;
+	mLODPeriod = 1;
+}
+
+void LLParticlePartition::addGeometryCount(LLSpatialGroup* group, U32& vertex_count, U32& index_count)
+{
+	group->mBufferUsage = mBufferUsage;
+
+	mFaceList.clear();
+
+	for (LLSpatialGroup::element_iter i = group->getData().begin(); i != group->getData().end(); ++i)
 	{
-		drawable->getFace(j)->setSize(0,0);
+		LLDrawable* drawablep = *i;
+		
+		if (drawablep->isDead())
+		{
+			continue;
+		}
+
+		LLAlphaObject* obj = (LLAlphaObject*) drawablep->getVObj();
+		obj->mDepth = 0.f;
+		
+		if (drawablep->isAnimating())
+		{
+			group->mBufferUsage = GL_STREAM_DRAW_ARB;
+		}
+
+		U32 count = 0;
+		for (S32 j = 0; j < drawablep->getNumFaces(); ++j)
+		{
+			drawablep->updateFaceSize(j);
+
+			LLFace* facep = drawablep->getFace(j);
+			if (!facep->hasGeometry())
+			{
+				continue;
+			}
+			
+			count++;
+			facep->mDistance = (facep->mCenterLocal - gCamera->getOrigin()) * gCamera->getAtAxis();
+			obj->mDepth += facep->mDistance;
+			
+			mFaceList.push_back(facep);
+			vertex_count += facep->getGeomCount();
+			index_count += facep->getIndicesCount();
+		}
+		
+		obj->mDepth /= count;
 	}
-	
-	mPixelArea = tot_area * gCamera->getPixelMeterRatio() * gCamera->getPixelMeterRatio();
-	const F32 area_scale = 10.f; // scale area to increase priority a bit
-	getTEImage(0)->addTextureStats(mPixelArea * area_scale);
+}
 
-	LLPipeline::sCompiles++;
+void LLParticlePartition::getGeometry(LLSpatialGroup* group)
+{
+	LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
 
-	return TRUE;
+	std::sort(mFaceList.begin(), mFaceList.end(), LLFace::CompareDistanceGreater());
+
+	U32 index_count = 0;
+	U32 vertex_count = 0;
+
+	group->clearDrawMap();
+
+	LLVertexBuffer* buffer = group->mVertexBuffer;
+
+	LLStrider<U32> indicesp;
+	LLStrider<LLVector3> verticesp;
+	LLStrider<LLVector3> normalsp;
+	LLStrider<LLVector2> texcoordsp;
+	LLStrider<LLColor4U> colorsp;
+
+	buffer->getVertexStrider(verticesp);
+	buffer->getNormalStrider(normalsp);
+	buffer->getColorStrider(colorsp);
+	buffer->getTexCoordStrider(texcoordsp);
+	buffer->getIndexStrider(indicesp);
+
+	std::vector<LLDrawInfo*>& draw_vec = group->mDrawMap[mRenderPass];
+
+	for (std::vector<LLFace*>::iterator i = mFaceList.begin(); i != mFaceList.end(); ++i)
+	{
+		LLFace* facep = *i;
+		LLAlphaObject* object = (LLAlphaObject*) facep->getViewerObject();
+		facep->setGeomIndex(vertex_count);
+		facep->setIndicesIndex(index_count);
+		facep->mVertexBuffer = buffer;
+		facep->setPoolType(LLDrawPool::POOL_ALPHA);
+		object->getGeometry(facep->getTEOffset(), verticesp, normalsp, texcoordsp, colorsp, indicesp);
+		
+		vertex_count += facep->getGeomCount();
+		index_count += facep->getIndicesCount();
+
+		S32 idx = draw_vec.size()-1;
+
+		BOOL fullbright = facep->isState(LLFace::FULLBRIGHT);
+		F32 vsize = facep->getVirtualSize();
+
+		if (idx >= 0 && draw_vec[idx]->mEnd == facep->getGeomIndex()-1 &&
+			draw_vec[idx]->mTexture == facep->getTexture() &&
+			draw_vec[idx]->mEnd - draw_vec[idx]->mStart + facep->getGeomCount() <= (U32) gGLManager.mGLMaxVertexRange &&
+			draw_vec[idx]->mCount + facep->getIndicesCount() <= (U32) gGLManager.mGLMaxIndexRange &&
+			draw_vec[idx]->mEnd - draw_vec[idx]->mStart + facep->getGeomCount() < 4096 &&
+			draw_vec[idx]->mFullbright == fullbright)
+		{
+			draw_vec[idx]->mCount += facep->getIndicesCount();
+			draw_vec[idx]->mEnd += facep->getGeomCount();
+			draw_vec[idx]->mVSize = llmax(draw_vec[idx]->mVSize, vsize);
+		}
+		else
+		{
+			U32 start = facep->getGeomIndex();
+			U32 end = start + facep->getGeomCount()-1;
+			U32 offset = facep->getIndicesStart();
+			U32 count = facep->getIndicesCount();
+			LLDrawInfo* info = new LLDrawInfo(start,end,count,offset,facep->getTexture(), buffer, fullbright); 
+			info->mVSize = vsize;
+			draw_vec.push_back(info);
+		}
+	}
+
+	mFaceList.clear();
+}
+
+F32 LLParticlePartition::calcPixelArea(LLSpatialGroup* group, LLCamera& camera)
+{
+	return 1024.f;
 }
 
diff --git a/indra/newview/llvopartgroup.h b/indra/newview/llvopartgroup.h
index 657d1824d0fe99032b022f3a47891c41d6cd64d8..40827009714f1ae57d591ff2718502461f3aa700 100644
--- a/indra/newview/llvopartgroup.h
+++ b/indra/newview/llvopartgroup.h
@@ -16,26 +16,50 @@
 
 class LLViewerPartGroup;
 
-class LLVOPartGroup : public LLViewerObject
+class LLVOPartGroup : public LLAlphaObject
 {
 public:
+	enum 
+	{
+		VERTEX_DATA_MASK =	(1 << LLVertexBuffer::TYPE_VERTEX) |
+							(1 << LLVertexBuffer::TYPE_NORMAL) |
+							(1 << LLVertexBuffer::TYPE_TEXCOORD) |
+							(1 << LLVertexBuffer::TYPE_COLOR)
+	}
+	eVertexDataMask;
+
 	LLVOPartGroup(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp);
 
 	~LLVOPartGroup();
 
 	/*virtual*/ BOOL    isActive() const; // Whether this object needs to do an idleUpdate.
 	BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time);
+	BOOL isParticle();
 
+	virtual F32 getBinRadius();
+	virtual void updateSpatialExtents(LLVector3& newMin, LLVector3& newMax);
+	virtual U32 getPartitionType() const;
+	
 	/*virtual*/ void setPixelAreaAndAngle(LLAgent &agent);
 	/*virtual*/ void updateTextures(LLAgent &agent);
 
 	/*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline);
 	/*virtual*/ BOOL        updateGeometry(LLDrawable *drawable);
+				void		getGeometry(S32 idx,
+								LLStrider<LLVector3>& verticesp,
+								LLStrider<LLVector3>& normalsp, 
+								LLStrider<LLVector2>& texcoordsp,
+								LLStrider<LLColor4U>& colorsp, 
+								LLStrider<U32>& indicesp);
 
+	void updateFaceSize(S32 idx) { }
+	F32 getPartSize(S32 idx);
 	void setViewerPartGroup(LLViewerPartGroup *part_groupp)		{ mViewerPartGroupp = part_groupp; }
+	LLViewerPartGroup* getViewerPartGroup()	{ return mViewerPartGroupp; }
+
 protected:
 	LLViewerPartGroup *mViewerPartGroupp;
-
+	LLVector3 mExtents[2];
 	LLColor4 mDebugColor;
 };
 
diff --git a/indra/newview/llvosky.cpp b/indra/newview/llvosky.cpp
index 2f580ba5f77bef648d9d77e06aa787eb09aac350..eb62f8ceb09f9e2cdf83c86613c44cb8e31c1544 100644
--- a/indra/newview/llvosky.cpp
+++ b/indra/newview/llvosky.cpp
@@ -421,7 +421,7 @@ void LLSkyTex::initEmpty(const S32 tex)
 		}
 	}
 
-	createTexture(tex);
+	createGLImage(tex);
 }
 
 
@@ -448,10 +448,10 @@ void LLSkyTex::create(const F32 brightness_scale, const LLColor3& multiscatt)
 			*pix = temp1.mAll;
 		}
 	}
-	createTexture(sCurrent);
+	createGLImage(sCurrent);
 }
 
-void LLSkyTex::createTexture(S32 which)
+void LLSkyTex::createGLImage(S32 which)
 {	
 	mImageGL[which]->createGLTexture(0, mImageRaw[which]);
 	mImageGL[which]->setClamp(TRUE, TRUE);
@@ -475,7 +475,7 @@ S32 LLVOSky::sTileResX = sResolution/NUM_TILES_X;
 S32 LLVOSky::sTileResY = sResolution/NUM_TILES_Y;
 
 LLVOSky::LLVOSky(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp)
-:	LLViewerObject(id, pcode, regionp),
+:	LLStaticViewerObject(id, pcode, regionp),
 	mSun(SUN_DISK_RADIUS), mMoon(MOON_DISK_RADIUS),
 	mBrightnessScale(1.f),
 	mBrightnessScaleNew(0.f),
@@ -1035,11 +1035,9 @@ void LLVOSky::calcBrightnessScaleAndColors()
 	}
 
 	calculateColors(); // MSMSM Moved this down here per Milo Lindens suggestion, to fix orange flashing bug at sunset.
-	generateScatterMap();
 }
 
 
-
 void LLVOSky::calculateColors()
 {
 	const F32 h = -0.1f;
@@ -1049,14 +1047,13 @@ void LLVOSky::calculateColors()
 	F32 sun_factor = 1;
 	
 	// Sun Diffuse
-	if (calcHitsEarth(mCameraPosAgent, tosun) < 0)
-	{
-		mSunDiffuse = mBrightnessScaleGuess * mSun.getIntensity() * mTransp.calcTransp(tosun.mV[2], h);
-	}
-	else
-	{
-		mSunDiffuse = mBrightnessScaleGuess * mSun.getIntensity() * mTransp.calcTransp(0, h);
-	}
+	F32 sun_height = tosun.mV[2];
+
+	if (sun_height <= 0.0)
+		sun_height = 0.0;
+	
+	mSunDiffuse = mBrightnessScaleGuess * mSun.getIntensity() * mTransp.calcTransp(sun_height, h);
+
 	mSunDiffuse = 1.0f * color_norm(mSunDiffuse);
 
 	// Sun Ambient
@@ -1169,8 +1166,6 @@ BOOL LLVOSky::updateSky()
 		return TRUE;
 	}
 
-	setPositionAgent(gAgent.getCameraPositionAgent());
-
 	static S32 next_frame = 0;
 	const S32 total_no_tiles = 6 * NUM_TILES;
 	const S32 cycle_frame_no = total_no_tiles + 1;
@@ -1234,21 +1229,27 @@ BOOL LLVOSky::updateSky()
 							LLImageRaw* raw1 = mSkyTex[side].getImageRaw(TRUE);
 							LLImageRaw* raw2 = mSkyTex[side].getImageRaw(FALSE);
 							raw2->copy(raw1);
-							mSkyTex[side].createTexture(mSkyTex[side].getWhich(FALSE));
+							mSkyTex[side].createGLImage(mSkyTex[side].getWhich(FALSE));
 						}
 						next_frame = 0;	
 						//llSkyTex::stepCurrent();
 					}
 
-					std::vector<LLPointer<LLImageRaw> > images;
-					for (S32 side = 0; side < 6; side++)
+					if (!gSavedSettings.getBOOL("RenderDynamicReflections"))
 					{
-						images.push_back(mSkyTex[side].getImageRaw(FALSE));
+						std::vector<LLPointer<LLImageRaw> > images;
+						for (S32 side = 0; side < 6; side++)
+						{
+							images.push_back(mSkyTex[side].getImageRaw(FALSE));
+						}
+						mCubeMap->init(images);
 					}
-					mCubeMap->init(images);
 				}
 			}
 
+			gPipeline.markRebuild(gSky.mVOGroundp->mDrawable, LLDrawable::REBUILD_ALL, TRUE);
+			gPipeline.markRebuild(gSky.mVOStarsp->mDrawable, LLDrawable::REBUILD_ALL, TRUE);
+
 			mForceUpdate = FALSE;
 		}
 		else
@@ -1287,7 +1288,8 @@ LLDrawable *LLVOSky::createDrawable(LLPipeline *pipeline)
 	poolp->setSkyTex(mSkyTex);
 	poolp->setSun(&mSun);
 	poolp->setMoon(&mMoon);
-
+	mDrawable->setRenderType(LLPipeline::RENDER_TYPE_SKY);
+	
 	for (S32 i = 0; i < 6; ++i)
 	{
 		mFace[FACE_SIDE0 + i] = mDrawable->addFace(poolp, NULL);
@@ -1297,9 +1299,6 @@ LLDrawable *LLVOSky::createDrawable(LLPipeline *pipeline)
 	mFace[FACE_MOON] = mDrawable->addFace(poolp, mMoonTexturep);
 	mFace[FACE_BLOOM] = mDrawable->addFace(poolp, mBloomTexturep);
 
-	//mDrawable->addFace(poolp, LLViewerImage::sDefaultImagep);
-	gPipeline.markMaterialed(mDrawable);
-
 	return mDrawable;
 }
 
@@ -1307,70 +1306,74 @@ BOOL LLVOSky::updateGeometry(LLDrawable *drawable)
 {
 	if (mFace[FACE_REFLECTION] == NULL)
 	{
-		mFace[FACE_REFLECTION] = drawable->addFace(gPipeline.getPool(LLDrawPool::POOL_WATER), NULL);
+		LLDrawPoolWater *poolp = (LLDrawPoolWater*) gPipeline.getPool(LLDrawPool::POOL_WATER);
+		mFace[FACE_REFLECTION] = drawable->addFace(poolp, NULL);
 	}
 
 	mCameraPosAgent = drawable->getPositionAgent();
 	mEarthCenter.mV[0] = mCameraPosAgent.mV[0];
 	mEarthCenter.mV[1] = mCameraPosAgent.mV[1];
 
-
 	LLVector3 v_agent[8];
 	for (S32 i = 0; i < 8; ++i)
 	{
 		F32 x_sgn = (i&1) ? 1.f : -1.f;
 		F32 y_sgn = (i&2) ? 1.f : -1.f;
 		F32 z_sgn = (i&4) ? 1.f : -1.f;
-		v_agent[i] = mCameraPosAgent + HORIZON_DIST * LLVector3(x_sgn, y_sgn, z_sgn);
+		v_agent[i] = HORIZON_DIST*0.25f * LLVector3(x_sgn, y_sgn, z_sgn);
 	}
 
 	LLStrider<LLVector3> verticesp;
 	LLStrider<LLVector3> normalsp;
 	LLStrider<LLVector2> texCoordsp;
-	U32 *indicesp;
+	LLStrider<U32> indicesp;
 	S32 index_offset;
 	LLFace *face;	
 
 	for (S32 side = 0; side < 6; ++side)
 	{
 		face = mFace[FACE_SIDE0 + side]; 
-		face->setPrimType(LLTriangles);
-		face->setSize(4, 6);
-		index_offset = face->getGeometry(verticesp,normalsp,texCoordsp, indicesp);
-		if (-1 == index_offset)
+
+		if (face->mVertexBuffer.isNull())
 		{
-			return TRUE;
+			face->setSize(4, 6);
+			face->setGeomIndex(0);
+			face->setIndicesIndex(0);
+			face->mVertexBuffer = new LLVertexBuffer(LLDrawPoolSky::VERTEX_DATA_MASK, GL_STREAM_DRAW_ARB);
+			face->mVertexBuffer->allocateBuffer(4, 6, TRUE);
+			
+			index_offset = face->getGeometry(verticesp,normalsp,texCoordsp, indicesp);
+			
+			S32 vtx = 0;
+			S32 curr_bit = side >> 1; // 0/1 = Z axis, 2/3 = Y, 4/5 = X
+			S32 side_dir = side & 1;  // even - 0, odd - 1
+			S32 i_bit = (curr_bit + 2) % 3;
+			S32 j_bit = (i_bit + 2) % 3;
+
+			LLVector3 axis;
+			axis.mV[curr_bit] = 1;
+			face->mCenterAgent = (F32)((side_dir << 1) - 1) * axis * HORIZON_DIST;
+
+			vtx = side_dir << curr_bit;
+			*(verticesp++)  = v_agent[vtx];
+			*(verticesp++)  = v_agent[vtx | 1 << j_bit];
+			*(verticesp++)  = v_agent[vtx | 1 << i_bit];
+			*(verticesp++)  = v_agent[vtx | 1 << i_bit | 1 << j_bit];
+
+			*(texCoordsp++) = TEX00;
+			*(texCoordsp++) = TEX01;
+			*(texCoordsp++) = TEX10;
+			*(texCoordsp++) = TEX11;
+
+			// Triangles for each side
+			*indicesp++ = index_offset + 0;
+			*indicesp++ = index_offset + 1;
+			*indicesp++ = index_offset + 3;
+
+			*indicesp++ = index_offset + 0;
+			*indicesp++ = index_offset + 3;
+			*indicesp++ = index_offset + 2;
 		}
-
-		S32 vtx = 0;
-		S32 curr_bit = side >> 1; // 0/1 = Z axis, 2/3 = Y, 4/5 = X
-		S32 side_dir = side & 1;  // even - 0, odd - 1
-		S32 i_bit = (curr_bit + 2) % 3;
-		S32 j_bit = (i_bit + 2) % 3;
-
-		LLVector3 axis;
-		axis.mV[curr_bit] = 1;
-		face->mCenterAgent = mCameraPosAgent + (F32)((side_dir << 1) - 1) * axis * HORIZON_DIST;
-
-		vtx = side_dir << curr_bit;
-		*(verticesp++)  = v_agent[vtx];
-		*(verticesp++)  = v_agent[vtx | 1 << j_bit];
-		*(verticesp++)  = v_agent[vtx | 1 << i_bit];
-		*(verticesp++)  = v_agent[vtx | 1 << i_bit | 1 << j_bit];
-
-		*(texCoordsp++) = TEX00;
-		*(texCoordsp++) = TEX01;
-		*(texCoordsp++) = TEX10;
-		*(texCoordsp++) = TEX11;
-
-		// Triangles for each side
-		*indicesp++ = index_offset + 0;
-		*indicesp++ = index_offset + 1;
-		*indicesp++ = index_offset + 3;
-
-		*indicesp++ = index_offset + 0;
-		*indicesp++ = index_offset + 3;
-		*indicesp++ = index_offset + 2;
 	}
 
 	const LLVector3 &look_at = gCamera->getAtAxis();
@@ -1445,7 +1448,7 @@ BOOL LLVOSky::updateHeavenlyBodyGeometry(LLDrawable *drawable, const S32 f, cons
 	LLStrider<LLVector3> verticesp;
 	LLStrider<LLVector3> normalsp;
 	LLStrider<LLVector2> texCoordsp;
-	U32 *indicesp;
+	LLStrider<U32> indicesp;
 	S32 index_offset;
 	LLFace *facep;
 
@@ -1493,8 +1496,16 @@ BOOL LLVOSky::updateHeavenlyBodyGeometry(LLDrawable *drawable, const S32 f, cons
 	hb.setVisible(TRUE);
 
 	facep = mFace[f]; 
-	facep->setPrimType(LLTriangles);
-	facep->setSize(4, 6);
+
+	if (facep->mVertexBuffer.isNull())
+	{
+		facep->setSize(4, 6);
+		facep->mVertexBuffer = new LLVertexBuffer(LLDrawPoolWater::VERTEX_DATA_MASK, GL_STREAM_DRAW_ARB);
+		facep->mVertexBuffer->allocateBuffer(facep->getGeomCount(), facep->getIndicesCount(), TRUE);
+		facep->setGeomIndex(0);
+		facep->setIndicesIndex(0);
+	}
+
 	index_offset = facep->getGeometry(verticesp,normalsp,texCoordsp, indicesp);
 	if (-1 == index_offset)
 	{
@@ -1623,7 +1634,7 @@ void LLVOSky::updateSunHaloGeometry(LLDrawable *drawable )
 	LLStrider<LLVector3> verticesp;
 	LLStrider<LLVector3> normalsp;
 	LLStrider<LLVector2> texCoordsp;
-	U32 *indicesp;
+	LLStrider<U32> indicesp;
 	S32 index_offset;
 	LLFace *face;
 
@@ -1642,8 +1653,16 @@ void LLVOSky::updateSunHaloGeometry(LLDrawable *drawable )
 	v_glow_corner[3] = draw_pos + right - up;
 
 	face = mFace[FACE_BLOOM]; 
-	face->setPrimType(LLTriangles);
-	face->setSize(4, 6);
+
+	if (face->mVertexBuffer.isNull())
+	{
+		face->setSize(4, 6);
+		face->setGeomIndex(0);
+		face->setIndicesIndex(0);
+		face->mVertexBuffer = new LLVertexBuffer(LLDrawPoolWater::VERTEX_DATA_MASK, GL_STREAM_DRAW_ARB);
+		face->mVertexBuffer->allocateBuffer(4, 6, TRUE);
+	}
+
 	index_offset = face->getGeometry(verticesp,normalsp,texCoordsp, indicesp);
 	if (-1 == index_offset)
 	{
@@ -1877,15 +1896,23 @@ void LLVOSky::updateReflectionGeometry(LLDrawable *drawable, F32 H,
 		dt_clip = -0.1f;
 	}
 
+	LLFace *face = mFace[FACE_REFLECTION]; 
+
+	if (face->mVertexBuffer.isNull() || quads*4 != face->getGeomCount())
+	{
+		face->setSize(quads * 4, quads * 6);
+		face->mVertexBuffer = new LLVertexBuffer(LLDrawPoolWater::VERTEX_DATA_MASK, GL_STREAM_DRAW_ARB);
+		face->mVertexBuffer->allocateBuffer(face->getGeomCount(), face->getIndicesCount(), TRUE);
+		face->setIndicesIndex(0);
+		face->setGeomIndex(0);
+	}
+	
 	LLStrider<LLVector3> verticesp;
 	LLStrider<LLVector3> normalsp;
 	LLStrider<LLVector2> texCoordsp;
-	U32 *indicesp;
+	LLStrider<U32> indicesp;
 	S32 index_offset;
-	LLFace *face = mFace[FACE_REFLECTION]; 
-
-	face->setPrimType(LLTriangles);
-	face->setSize(quads * 4, quads * 6);
+	
 	index_offset = face->getGeometry(verticesp,normalsp,texCoordsp, indicesp);
 	if (-1 == index_offset)
 	{
@@ -2113,16 +2140,14 @@ void LLVOSky::updateFog(const F32 distance)
 
 	color_gamma_correct(sky_fog_color);
 
-	if (!(gPipeline.getVertexShaderLevel(LLPipeline::SHADER_ENVIRONMENT) > LLDrawPool::SHADER_LEVEL_SCATTERING))
-	{
-		render_fog_color = sky_fog_color;
-	}
-
+	render_fog_color = sky_fog_color;
+	
 	if (camera_height > water_height)
 	{
 		fog_distance = mFogRatio * distance;
 		LLColor4 fog(render_fog_color);
 		glFogfv(GL_FOG_COLOR, fog.mV);
+		mGLFogCol = fog;
 	}
 	else
 	{
@@ -2142,6 +2167,7 @@ void LLVOSky::updateFog(const F32 distance)
 		LLColor4 fogCol = brightness * (color_frac * render_fog_color + (1.f - color_frac) * LLColor4(0.f, 0.2f, 0.3f, 1.f));
 		fogCol.setAlpha(1);
 		glFogfv(GL_FOG_COLOR, (F32 *) &fogCol.mV);
+		mGLFogCol = fogCol;
 	}
 
 	mFogColor = sky_fog_color;
diff --git a/indra/newview/llvosky.h b/indra/newview/llvosky.h
index c140e97e5e71c2c54b35c2b88237d2a81c73305c..c705c3951e59b31cc3b63d022620f84562bac3f7 100644
--- a/indra/newview/llvosky.h
+++ b/indra/newview/llvosky.h
@@ -178,7 +178,7 @@ protected:
 	}
 
 	LLImageRaw* getImageRaw(BOOL curr=TRUE)			{ return mImageRaw[getWhich(curr)]; }
-	void createTexture(BOOL curr=TRUE);
+	void createGLImage(BOOL curr=TRUE);
 };
 
 
@@ -492,7 +492,7 @@ public:
 class LLCubeMap;
 
 
-class LLVOSky : public LLViewerObject
+class LLVOSky : public LLStaticViewerObject
 {
 public:
 	enum
@@ -574,6 +574,7 @@ public:
 	LLColor4 getMoonAmbientColor() const					{ return mMoonAmbient; }
 	const LLColor4& getTotalAmbientColor() const			{ return mTotalAmbient; }
 	LLColor4 getFogColor() const							{ return mFogColor; }
+	LLColor4 getGLFogColor() const							{ return mGLFogCol; }
 	
 	LLVector3 calcUpVec(const LLVector3 &pos) const
 	{
@@ -710,6 +711,8 @@ protected:
 	F32					sInterpVal;
 
 	LLColor4			mFogColor;
+	LLColor4			mGLFogCol;
+	
 	F32					mFogRatio;
 	F32					mWorldScale;
 
@@ -720,7 +723,7 @@ protected:
 	LLColor3			mMoonDiffuse;
 	LLColor4U			mFadeColor;					// Color to fade in from	
 
-	LLCubeMap			*mCubeMap;					// Cube map for the sky
+	LLCubeMap			*mCubeMap;					// Cube map for the environment
 	S32					mDrawRefl;
 
 	LLFrameTimer		mUpdateTimer;
diff --git a/indra/newview/llvosurfacepatch.cpp b/indra/newview/llvosurfacepatch.cpp
index ae7d46dfee315ff540312407388a3dcde63b1d84..759493504eefc35f80bc6062e921162c2694af9c 100644
--- a/indra/newview/llvosurfacepatch.cpp
+++ b/indra/newview/llvosurfacepatch.cpp
@@ -10,6 +10,8 @@
 
 #include "llvosurfacepatch.h"
 
+#include "lldrawpoolterrain.h"
+
 #include "lldrawable.h"
 #include "llface.h"
 #include "llprimitive.h"
@@ -22,8 +24,52 @@
 #include "llvovolume.h"
 #include "pipeline.h"
 
+//============================================================================
+
+class LLVertexBufferTerrain : public LLVertexBuffer
+{
+public:
+	LLVertexBufferTerrain() :
+		LLVertexBuffer(MAP_VERTEX | MAP_NORMAL | MAP_TEXCOORD | MAP_TEXCOORD2 | MAP_COLOR, GL_DYNAMIC_DRAW_ARB)
+	{
+	};
+
+	// virtual
+	void setupVertexBuffer(U32 data_mask) const
+	{		
+		if (LLDrawPoolTerrain::getDetailMode() == 0)
+		{
+			LLVertexBuffer::setupVertexBuffer(data_mask);
+		}
+		else if (data_mask & LLVertexBuffer::MAP_TEXCOORD2)
+		{
+			U8* base = useVBOs() ? NULL : mMappedData;
+	
+			glVertexPointer(3,GL_FLOAT, mStride, (void*)(base + 0));
+			glNormalPointer(GL_FLOAT, mStride, (void*)(base + mOffsets[TYPE_NORMAL]));
+			glColorPointer(4, GL_UNSIGNED_BYTE, mStride, (void*)(base + mOffsets[TYPE_COLOR]));
+		
+			glClientActiveTextureARB(GL_TEXTURE3_ARB);
+			glTexCoordPointer(2,GL_FLOAT, mStride, (void*)(base + mOffsets[TYPE_TEXCOORD2]));
+			glClientActiveTextureARB(GL_TEXTURE2_ARB);
+			glTexCoordPointer(2,GL_FLOAT, mStride, (void*)(base + mOffsets[TYPE_TEXCOORD2]));
+			glClientActiveTextureARB(GL_TEXTURE1_ARB);
+			glTexCoordPointer(2,GL_FLOAT, mStride, (void*)(base + mOffsets[TYPE_TEXCOORD2]));
+			glClientActiveTextureARB(GL_TEXTURE0_ARB);
+			glTexCoordPointer(2,GL_FLOAT, mStride, (void*)(base + mOffsets[TYPE_TEXCOORD2]));
+		}
+		else
+		{
+			LLVertexBuffer::setupVertexBuffer(data_mask);
+		}
+		llglassertok();		
+	}
+};
+
+//============================================================================
+
 LLVOSurfacePatch::LLVOSurfacePatch(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp)
-:	LLViewerObject(id, LL_VO_SURFACE_PATCH, regionp)
+:	LLStaticViewerObject(id, LL_VO_SURFACE_PATCH, regionp)
 {
 	// Terrain must draw during selection passes so it can block objects behind it.
 	mbCanSelect = TRUE;
@@ -76,9 +122,9 @@ void LLVOSurfacePatch::updateTextures(LLAgent &agent)
 }
 
 
-LLDrawPool *LLVOSurfacePatch::getPool()
+LLFacePool *LLVOSurfacePatch::getPool()
 {
-	mPool = gPipeline.getPool(LLDrawPool::POOL_TERRAIN, mPatchp->getSurface()->getSTexture());
+	mPool = (LLDrawPoolTerrain*) gPipeline.getPool(LLDrawPool::POOL_TERRAIN, mPatchp->getSurface()->getSTexture());
 
 	return mPool;
 }
@@ -106,15 +152,18 @@ LLDrawable *LLVOSurfacePatch::createDrawable(LLPipeline *pipeline)
 		range = 3;
 	}
 
-	LLDrawPool *poolp = getPool();
+	LLFacePool *poolp = getPool();
 
 	mDrawable->addFace(poolp, NULL);
+
 	return mDrawable;
 }
 
 
 BOOL LLVOSurfacePatch::updateGeometry(LLDrawable *drawable)
 {
+	LLFastTimer ftm(LLFastTimer::FTM_UPDATE_TERRAIN);
+
 	S32 min_comp, max_comp, range;
 	min_comp = lltrunc(mPatchp->getMinComposition());
 	max_comp = lltrunc(ceil(mPatchp->getMaxComposition()));
@@ -166,57 +215,63 @@ BOOL LLVOSurfacePatch::updateGeometry(LLDrawable *drawable)
 		east_stride = render_stride;
 	}
 
+	mLastLength = length;
+	mLastStride = render_stride;
+	mLastNorthStride = north_stride;
+	mLastEastStride = east_stride;
+
+	return TRUE;
+}
+
+void LLVOSurfacePatch::updateFaceSize(S32 idx)
+{
+	if (idx != 0)
+	{
+		llwarns << "Terrain partition requested invalid face!!!" << llendl;
+		return;
+	}
+
+	LLFace* facep = mDrawable->getFace(idx);
+
 	S32 num_vertices = 0;
 	S32 num_indices = 0;
-	S32 new_north_offset = 0;
-	S32 new_east_offset = 0;
-
-	getGeomSizesMain(render_stride, num_vertices, num_indices);
-	new_north_offset = num_vertices;
-	getGeomSizesNorth(render_stride, north_stride, num_vertices, num_indices);
-	new_east_offset = num_vertices;
-	getGeomSizesEast(render_stride, east_stride, num_vertices, num_indices);
-	S32 new_num_vertices = num_vertices;
-	S32 new_num_indices = num_indices;
-
-	LLFace *facep = NULL;
-
-	// Update the allocated face
-	LLStrider<LLVector3> verticesp;
-	LLStrider<LLVector3> normalsp;
-	LLStrider<LLVector2> texCoords0p;
-	LLStrider<LLVector2> texCoords1p;
-	LLStrider<LLColor4U> colorsp;
-	U32*       indicesp = NULL;
-	S32 index_offset;
-
-	facep = mDrawable->getFace(0);
-
-	facep->setSize(new_num_vertices, new_num_indices);
-	facep->setPrimType(LLTriangles);
-
-	index_offset = facep->getGeometryTerrain(
-							     verticesp,
-								 normalsp,
-								 colorsp,
-								 texCoords0p,
-								 texCoords1p,
-								 indicesp);
-	if (-1 == index_offset)
+	
+	if (mLastStride)
 	{
-		return TRUE;
+		getGeomSizesMain(mLastStride, num_vertices, num_indices);
+		getGeomSizesNorth(mLastStride, mLastNorthStride, num_vertices, num_indices);
+		getGeomSizesEast(mLastStride, mLastEastStride, num_vertices, num_indices);
 	}
 
-	mDrawable->updateLightSet();
+	facep->setSize(num_vertices, num_indices);	
+}
+
+BOOL LLVOSurfacePatch::updateLOD()
+{
+	//mDrawable->updateLightSet();
+	mDrawable->setState(LLDrawable::LIGHTING_BUILT);
+	return TRUE;
+}
+
+void LLVOSurfacePatch::getGeometry(LLStrider<LLVector3> &verticesp,
+								LLStrider<LLVector3> &normalsp,
+								LLStrider<LLColor4U> &colorsp,
+								LLStrider<LLVector2> &texCoords0p,
+								LLStrider<LLVector2> &texCoords1p,
+								LLStrider<U32> &indicesp)
+{
+	LLFace* facep = mDrawable->getFace(0);
+
+	U32 index_offset = facep->getGeomIndex();
 
 	updateMainGeometry(facep, 
-					   verticesp,
-					   normalsp,
-					   colorsp,
-					   texCoords0p,
-					   texCoords1p,
-					   indicesp,
-					   index_offset);
+					verticesp,
+					normalsp,
+					colorsp,
+					texCoords0p,
+					texCoords1p,
+					indicesp,
+					index_offset);
 	updateNorthGeometry(facep, 
 						verticesp,
 						normalsp,
@@ -233,24 +288,6 @@ BOOL LLVOSurfacePatch::updateGeometry(LLDrawable *drawable)
 						texCoords1p,
 						indicesp,
 						index_offset);
-
-	if (mLastLength != 0)
-	{
-		// lazy, should cache the geom sizes so we know the offsets.
-		num_vertices = 0;
-		num_indices = 0;
-
-	}
-
-	mLastLength = length;
-	mLastStride = render_stride;
-	mLastNorthStride = north_stride;
-	mLastEastStride = east_stride;
-
-	mDrawable->setState(LLDrawable::LIGHTING_BUILT);
-	
-	LLPipeline::sCompiles++;
-	return TRUE;
 }
 
 void LLVOSurfacePatch::updateMainGeometry(LLFace *facep,
@@ -259,8 +296,8 @@ void LLVOSurfacePatch::updateMainGeometry(LLFace *facep,
 										LLStrider<LLColor4U> &colorsp,
 										LLStrider<LLVector2> &texCoords0p,
 										LLStrider<LLVector2> &texCoords1p,
-										U32* &indicesp,
-										S32 &index_offset)
+										LLStrider<U32> &indicesp,
+										U32 &index_offset)
 {
 	S32 i, j, x, y;
 
@@ -268,7 +305,7 @@ void LLVOSurfacePatch::updateMainGeometry(LLFace *facep,
 	S32 num_vertices, num_indices;
 	U32 index;
 
-	render_stride = mPatchp->getRenderStride();
+	render_stride = mLastStride;
 	patch_size = mPatchp->getSurface()->getGridsPerPatchEdge();
 	S32 vert_size = patch_size / render_stride;
 
@@ -364,29 +401,20 @@ void LLVOSurfacePatch::updateNorthGeometry(LLFace *facep,
 										LLStrider<LLColor4U> &colorsp,
 										LLStrider<LLVector2> &texCoords0p,
 										LLStrider<LLVector2> &texCoords1p,
-										U32* &indicesp,
-										S32 &index_offset)
+										LLStrider<U32> &indicesp,
+										U32 &index_offset)
 {
 	S32 vertex_count = 0;
 	S32 i, x, y;
 
 	S32 num_vertices, num_indices;
 
-	U32 render_stride = mPatchp->getRenderStride();
+	U32 render_stride = mLastStride;
 	S32 patch_size = mPatchp->getSurface()->getGridsPerPatchEdge();
 	S32 length = patch_size / render_stride;
 	S32 half_length = length / 2;
-
-	U32 north_stride;
-	if (mPatchp->getNeighborPatch(NORTH))
-	{
-		north_stride = mPatchp->getNeighborPatch(NORTH)->getRenderStride();
-	}
-	else
-	{
-		north_stride = render_stride;
-	}
-
+	U32 north_stride = mLastNorthStride;
+	
 	///////////////////////////
 	//
 	// Render the north strip
@@ -586,27 +614,19 @@ void LLVOSurfacePatch::updateEastGeometry(LLFace *facep,
 										  LLStrider<LLColor4U> &colorsp,
 										  LLStrider<LLVector2> &texCoords0p,
 										  LLStrider<LLVector2> &texCoords1p,
-										  U32* &indicesp,
-										  S32 &index_offset)
+										  LLStrider<U32> &indicesp,
+										  U32 &index_offset)
 {
 	S32 i, x, y;
 
 	S32 num_vertices, num_indices;
 
-	U32 render_stride = mPatchp->getRenderStride();
+	U32 render_stride = mLastStride;
 	S32 patch_size = mPatchp->getSurface()->getGridsPerPatchEdge();
 	S32 length = patch_size / render_stride;
 	S32 half_length = length / 2;
 
-	U32 east_stride;
-	if (mPatchp->getNeighborPatch(EAST))
-	{
-		east_stride = mPatchp->getNeighborPatch(EAST)->getRenderStride();
-	}
-	else
-	{
-		east_stride = render_stride;
-	}
+	U32 east_stride = mLastEastStride;
 
 	// Stride lengths are the same
 	if (east_stride == render_stride)
@@ -825,10 +845,6 @@ void LLVOSurfacePatch::setPatch(LLSurfacePatch *patchp)
 
 void LLVOSurfacePatch::dirtyPatch()
 {
-	if (mDrawable)
-	{
-		gPipeline.markMoved(mDrawable);
-	}
 	mDirtiedPatch = TRUE;
 	dirtyGeom();
 	mDirtyTerrain = TRUE;
@@ -846,6 +862,8 @@ void LLVOSurfacePatch::dirtyGeom()
 	if (mDrawable)
 	{
 		gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_ALL, TRUE);
+		mDrawable->getFace(0)->mVertexBuffer = NULL;
+		mDrawable->movePartition();
 	}
 }
 
@@ -918,7 +936,67 @@ void LLVOSurfacePatch::updateSpatialExtents(LLVector3& newMin, LLVector3 &newMax
 {
 	LLVector3 posAgent = getPositionAgent();
 	LLVector3 scale = getScale();
-	newMin = posAgent-scale;
-	newMax = posAgent+scale;
+	newMin = posAgent-scale*0.5f;
+	newMax = posAgent+scale*0.5f;
 	mDrawable->setPositionGroup((newMin+newMax)*0.5f);
 }
+
+U32 LLVOSurfacePatch::getPartitionType() const
+{ 
+	return LLPipeline::PARTITION_TERRAIN; 
+}
+
+LLTerrainPartition::LLTerrainPartition()
+: LLSpatialPartition(LLDrawPoolTerrain::VERTEX_DATA_MASK)
+{
+	mRenderByGroup = FALSE;
+	mBufferUsage = GL_DYNAMIC_DRAW_ARB;
+	mDrawableType = LLPipeline::RENDER_TYPE_TERRAIN;
+	mPartitionType = LLPipeline::PARTITION_TERRAIN;
+}
+
+LLVertexBuffer* LLTerrainPartition::createVertexBuffer(U32 type_mask, U32 usage)
+{
+	return new LLVertexBufferTerrain();
+}
+
+void LLTerrainPartition::getGeometry(LLSpatialGroup* group)
+{
+	LLVertexBuffer* buffer = group->mVertexBuffer;
+
+	//get vertex buffer striders
+	LLStrider<LLVector3> vertices;
+	LLStrider<LLVector3> normals;
+	LLStrider<LLVector2> texcoords2;
+	LLStrider<LLVector2> texcoords;
+	LLStrider<LLColor4U> colors;
+	LLStrider<U32> indices;
+
+	buffer->getVertexStrider(vertices);
+	buffer->getNormalStrider(normals);
+	buffer->getTexCoordStrider(texcoords);
+	buffer->getTexCoord2Strider(texcoords2);
+	buffer->getColorStrider(colors);
+	buffer->getIndexStrider(indices);
+
+	U32 indices_index = 0;
+	U32 index_offset = 0;
+
+	for (std::vector<LLFace*>::iterator i = mFaceList.begin(); i != mFaceList.end(); ++i)
+	{
+		LLFace* facep = *i;
+
+		facep->setIndicesIndex(indices_index);
+		facep->setGeomIndex(index_offset);
+		facep->mVertexBuffer = buffer;
+
+		LLVOSurfacePatch* patchp = (LLVOSurfacePatch*) facep->getViewerObject();
+		patchp->getGeometry(vertices, normals, colors, texcoords, texcoords2, indices);
+
+		indices_index += facep->getIndicesCount();
+		index_offset += facep->getGeomCount();
+	}
+
+	mFaceList.clear();
+}
+
diff --git a/indra/newview/llvosurfacepatch.h b/indra/newview/llvosurfacepatch.h
index f1c44aa5988080f911c56481a89e9a8d76e5489e..fd2eff710bbe4e577ded3ed196d9f538a134ef5a 100644
--- a/indra/newview/llvosurfacepatch.h
+++ b/indra/newview/llvosurfacepatch.h
@@ -16,9 +16,19 @@ class LLSurfacePatch;
 class LLDrawPool;
 class LLVector2;
 
-class LLVOSurfacePatch : public LLViewerObject
+class LLVOSurfacePatch : public LLStaticViewerObject
 {
 public:
+	enum 
+	{
+		VERTEX_DATA_MASK =	(1 << LLVertexBuffer::TYPE_VERTEX) |
+							(1 << LLVertexBuffer::TYPE_NORMAL) |
+							(1 << LLVertexBuffer::TYPE_TEXCOORD) |
+							(1 << LLVertexBuffer::TYPE_TEXCOORD2) |
+							(1 << LLVertexBuffer::TYPE_COLOR) 
+	}
+	eVertexDataMask;
+
 	LLVOSurfacePatch(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp);
 	virtual ~LLVOSurfacePatch();
 
@@ -27,8 +37,18 @@ public:
 	// Initialize data that's only inited once per class.
 	static void initClass();
 
+	virtual U32 getPartitionType() const;
+
 	/*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline);
 	/*virtual*/ BOOL        updateGeometry(LLDrawable *drawable);
+	/*virtual*/ BOOL		updateLOD();
+	/*virtual*/ void		updateFaceSize(S32 idx);
+	void getGeometry(LLStrider<LLVector3> &verticesp,
+								LLStrider<LLVector3> &normalsp,
+								LLStrider<LLColor4U> &colorsp,
+								LLStrider<LLVector2> &texCoords0p,
+								LLStrider<LLVector2> &texCoords1p,
+								LLStrider<U32> &indicesp);
 
 	/*virtual*/ void updateTextures(LLAgent &agent);
 	/*virtual*/ void setPixelAreaAndAngle(LLAgent &agent); // generate accurate apparent angle and area
@@ -44,8 +64,8 @@ public:
 
 	BOOL			mDirtiedPatch;
 protected:
-	LLDrawPool		*mPool;
-	LLDrawPool		*getPool();
+	LLFacePool		*mPool;
+	LLFacePool		*getPool();
 	S32				mBaseComp;
 	LLSurfacePatch	*mPatchp;
 	BOOL			mDirtyTexture;
@@ -70,24 +90,24 @@ protected:
 					   LLStrider<LLColor4U> &colorsp,
 					   LLStrider<LLVector2> &texCoords0p,
 					   LLStrider<LLVector2> &texCoords1p,
-					   U32* &indicesp,
-					   S32 &index_offset);
+					   LLStrider<U32> &indicesp,
+					   U32 &index_offset);
 	void updateNorthGeometry(LLFace *facep,
 					   LLStrider<LLVector3> &verticesp,
 					   LLStrider<LLVector3> &normalsp,
 					   LLStrider<LLColor4U> &colorsp,
 					   LLStrider<LLVector2> &texCoords0p,
 					   LLStrider<LLVector2> &texCoords1p,
-					   U32* &indicesp,
-					   S32 &index_offset);
+					   LLStrider<U32> &indicesp,
+					   U32 &index_offset);
 	void updateEastGeometry(LLFace *facep,
 					   LLStrider<LLVector3> &verticesp,
 					   LLStrider<LLVector3> &normalsp,
 					   LLStrider<LLColor4U> &colorsp,
 					   LLStrider<LLVector2> &texCoords0p,
 					   LLStrider<LLVector2> &texCoords1p,
-					   U32* &indicesp,
-					   S32 &index_offset);
+					   LLStrider<U32> &indicesp,
+					   U32 &index_offset);
 };
 
 #endif // LL_VOSURFACEPATCH_H
diff --git a/indra/newview/llvotextbubble.cpp b/indra/newview/llvotextbubble.cpp
index d9f3d83f1124e9b8221e59b5e527198bc9fb6540..1a68fa88bf46409fd05119b150c314c27132ef47 100644
--- a/indra/newview/llvotextbubble.cpp
+++ b/indra/newview/llvotextbubble.cpp
@@ -24,7 +24,7 @@
 #include "pipeline.h"
 
 LLVOTextBubble::LLVOTextBubble(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp)
-:	LLViewerObject(id, pcode, regionp)
+:	LLAlphaObject(id, pcode, regionp)
 {
 	setScale(LLVector3(1.5f, 1.5f, 0.25f));
 	mbCanSelect = FALSE;
@@ -83,8 +83,10 @@ BOOL LLVOTextBubble::idleUpdate(LLAgent &agent, LLWorld	&world, const F64 &time)
 	for (i = 0; i < getNumTEs(); i++)
 	{
 		setTEColor(i, color);
+		setTEFullbright(i, TRUE);
 	}
 
+	gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);
 	return TRUE;
 }
 
@@ -126,15 +128,14 @@ LLDrawable *LLVOTextBubble::createDrawable(LLPipeline *pipeline)
 	pipeline->allocDrawable(this);
 	mDrawable->setLit(FALSE);
 	mDrawable->setRenderType(LLPipeline::RENDER_TYPE_VOLUME);
-	LLDrawPool *poolp;
+	
 	for (U32 i = 0; i < getNumTEs(); i++)
 	{
 		LLViewerImage *imagep;
 		const LLTextureEntry *texture_entry = getTE(i);
 		imagep = gImageList.getImage(texture_entry->getID());
 
-		poolp = gPipeline.getPool(LLDrawPool::POOL_ALPHA);
-		mDrawable->addFace(poolp, imagep);
+		mDrawable->addFace((LLFacePool*) NULL, imagep);
 	}
 
 	return mDrawable;
@@ -181,24 +182,65 @@ BOOL LLVOTextBubble::updateGeometry(LLDrawable *drawable)
 	for (S32 i = 0; i < drawable->getNumFaces(); i++)
 	{
 		LLFace *face = drawable->getFace(i);
-
-		if (i == 0)
-		{
-			face->setSize(0);
-			continue;
-		}
-		if (i == 2)
-		{
-			face->setSize(0);
-			continue;
-		}
-
-		// Need to figure out how to readd logic to determine face dirty vs. entire object dirty.
 		face->setTEOffset(i);
-		face->clearState(LLFace::GLOBAL);
-		face->genVolumeTriangles(*getVolume(), i, identity4, identity3);
+		face->setTexture(LLViewerImage::sSmokeImagep);
+		face->setState(LLFace::FULLBRIGHT);
 	}
+
 	mVolumeChanged = FALSE;
 
+	mDrawable->movePartition();
 	return TRUE;
 }
+
+void LLVOTextBubble::updateFaceSize(S32 idx)
+{
+	LLFace* face = mDrawable->getFace(idx);
+	
+	if (idx == 0 || idx == 2)
+	{
+		face->setSize(0,0);
+	}
+	else
+	{
+		const LLVolumeFace& vol_face = getVolume()->getVolumeFace(idx);
+		face->setSize(vol_face.mVertices.size(), vol_face.mIndices.size());
+	}
+}
+
+void LLVOTextBubble::getGeometry(S32 idx,
+								LLStrider<LLVector3>& verticesp,
+								LLStrider<LLVector3>& normalsp, 
+								LLStrider<LLVector2>& texcoordsp,
+								LLStrider<LLColor4U>& colorsp, 
+								LLStrider<U32>& indicesp) 
+{
+	if (idx == 0 || idx == 2)
+	{
+		return;
+	}
+
+	const LLVolumeFace& face = getVolume()->getVolumeFace(idx);
+	
+	LLVector3 pos = getPositionAgent();
+	LLColor4U color = LLColor4U(getTE(idx)->getColor());
+	U32 offset = mDrawable->getFace(idx)->getGeomIndex();
+	
+	for (U32 i = 0; i < face.mVertices.size(); i++)
+	{
+		*verticesp++ = face.mVertices[i].mPosition.scaledVec(getScale()) + pos;
+		*normalsp++ = face.mVertices[i].mNormal;
+		*texcoordsp++ = face.mVertices[i].mTexCoord;
+		*colorsp++ = color;
+	}
+	
+	for (U32 i = 0; i < face.mIndices.size(); i++)
+	{
+		*indicesp++ = face.mIndices[i] + offset;
+	}
+}
+
+U32 LLVOTextBubble::getPartitionType() const
+{ 
+	return LLPipeline::PARTITION_PARTICLE; 
+}
diff --git a/indra/newview/llvotextbubble.h b/indra/newview/llvotextbubble.h
index 9fe6386a3be048445173e6c5e08857e592d1caab..a348a53c7c067fac4129a3a64bfe5be9a0d38b59 100644
--- a/indra/newview/llvotextbubble.h
+++ b/indra/newview/llvotextbubble.h
@@ -12,7 +12,7 @@
 #include "llviewerobject.h"
 #include "llframetimer.h"
 
-class LLVOTextBubble : public LLViewerObject
+class LLVOTextBubble : public LLAlphaObject
 {
 public:
 	LLVOTextBubble(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp);
@@ -25,6 +25,16 @@ public:
 	/*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline);
 	/*virtual*/ BOOL		updateGeometry(LLDrawable *drawable);
 	/*virtual*/ BOOL		updateLOD();
+	/*virtual*/ void		updateFaceSize(S32 idx);
+	
+	/*virtual*/ void		getGeometry(S32 idx,
+								LLStrider<LLVector3>& verticesp,
+								LLStrider<LLVector3>& normalsp, 
+								LLStrider<LLVector2>& texcoordsp,
+								LLStrider<LLColor4U>& colorsp, 
+								LLStrider<U32>& indicesp);
+
+	virtual U32 getPartitionType() const;
 
 	LLColor4 mColor;
 	S32 mLOD;
diff --git a/indra/newview/llvotree.cpp b/indra/newview/llvotree.cpp
index a4b61f13bdf1e2d4dcc71c4a2a2815682669cc5a..f67188ff0587a10accb7dbae6141e9e0c3532bdf 100644
--- a/indra/newview/llvotree.cpp
+++ b/indra/newview/llvotree.cpp
@@ -10,6 +10,8 @@
 
 #include "llvotree.h"
 
+#include "lldrawpooltree.h"
+
 #include "llviewercontrol.h"
 #include "lldir.h"
 #include "llprimitive.h"
@@ -19,7 +21,6 @@
 #include "object_flags.h"
 
 #include "llagent.h"
-#include "llagparray.h"
 #include "llcylinder.h"
 #include "lldrawable.h"
 #include "llface.h"
@@ -389,6 +390,10 @@ void LLVOTree::updateTextures(LLAgent &agent)
 	F32 cos_angle = 1.f;
 	if (mTreeImagep)
 	{
+		if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_AREA))
+		{
+			setDebugText(llformat("%4.0f", fsqrtf(mPixelArea)));
+		}
 		mTreeImagep->addTextureStats(mPixelArea, texel_area_ratio, cos_angle);
 	}
 
@@ -402,14 +407,12 @@ LLDrawable* LLVOTree::createDrawable(LLPipeline *pipeline)
 
 	mDrawable->setRenderType(LLPipeline::RENDER_TYPE_TREE);
 
-	LLDrawPool *poolp = gPipeline.getPool(LLDrawPool::POOL_TREE, mTreeImagep);
+	LLDrawPoolTree *poolp = (LLDrawPoolTree*) gPipeline.getPool(LLDrawPool::POOL_TREE, mTreeImagep);
 
 	// Just a placeholder for an actual object...
-	LLFace *facep = mDrawable->addFace(poolp, mTreeImagep, TRUE);
+	LLFace *facep = mDrawable->addFace(poolp, mTreeImagep);
 	facep->setSize(1, 3);
 
-	gPipeline.markMaterialed(mDrawable);
-
 	updateRadius();
 
 	return mDrawable;
@@ -422,6 +425,7 @@ const S32 LEAF_VERTICES = 16;
 
 BOOL LLVOTree::updateGeometry(LLDrawable *drawable)
 {
+	LLFastTimer ftm(LLFastTimer::FTM_UPDATE_TREE);
 	U32 i, j;
 	const S32 MAX_SLICES = 32;
 
@@ -444,20 +448,6 @@ BOOL LLVOTree::updateGeometry(LLDrawable *drawable)
 	face->mCenterAgent = getPositionAgent();
 	face->mCenterLocal = face->mCenterAgent;
 
-	LLDrawPool *poolp = face->getPool();
-	
-	if (poolp->getVertexCount())
-	{
-		return TRUE;
-	}
-
-	if (!face->getDirty())
-	{
-		return FALSE;
-	}
-
-	poolp->setDirty();
-
 	for (lod = 0; lod < 4; lod++)
 	{
 		slices = sLODSlices[lod];
@@ -472,10 +462,17 @@ BOOL LLVOTree::updateGeometry(LLDrawable *drawable)
 	LLStrider<LLVector3> vertices;
 	LLStrider<LLVector3> normals;
 	LLStrider<LLVector2> tex_coords;
-	U32 *indicesp;
+	LLStrider<U32> indicesp;
 
 	face->setSize(max_vertices, max_indices);
+
+	face->mVertexBuffer = new LLVertexBuffer(LLDrawPoolTree::VERTEX_DATA_MASK, GL_STATIC_DRAW_ARB);
+	face->mVertexBuffer->allocateBuffer(max_vertices, max_indices, TRUE);
+	face->setGeomIndex(0);
+	face->setIndicesIndex(0);
+
 	face->getGeometry(vertices, normals, tex_coords, indicesp);
+	
 
 	S32 vertex_count = 0;
 	S32 index_count = 0;
@@ -774,17 +771,13 @@ BOOL LLVOTree::updateGeometry(LLDrawable *drawable)
 	return TRUE;
 }
 
-void LLVOTree::drawBranchPipeline(LLDrawPool *draw_pool, S32 trunk_LOD, S32 stop_level, U16 depth, U16 trunk_depth,  F32 scale, F32 twist, F32 droop,  F32 branches, F32 alpha)
+U32 LLVOTree::drawBranchPipeline(U32* indicesp, S32 trunk_LOD, S32 stop_level, U16 depth, U16 trunk_depth,  F32 scale, F32 twist, F32 droop,  F32 branches, F32 alpha)
 {
+	U32 ret = 0;
 	//
 	//  Draws a tree by recursing, drawing branches and then a 'leaf' texture.
 	//  If stop_level = -1, simply draws the whole tree as a billboarded texture
 	//
-
-	if (!draw_pool->getIndexCount())
-	{
-		return; // safety net
-	}
 	
 	static F32 constant_twist;
 	static F32 width = 0;
@@ -808,15 +801,15 @@ void LLVOTree::drawBranchPipeline(LLDrawPool *draw_pool, S32 trunk_LOD, S32 stop
 				width = scale * length * aspect;
 				glPushMatrix();
 				glScalef(width,width,scale * length);
- 				//glDrawElements(GL_TRIANGLES, sLODIndexCount[trunk_LOD], GL_UNSIGNED_INT, draw_pool.getRawIndices() + sLODIndexOffset[trunk_LOD]);
-				glDrawRangeElements(GL_TRIANGLES,
+ 				glDrawElements(GL_TRIANGLES, sLODIndexCount[trunk_LOD], GL_UNSIGNED_INT, indicesp + sLODIndexOffset[trunk_LOD]);
+				/*glDrawRangeElements(GL_TRIANGLES,
 									sLODVertexOffset[trunk_LOD],
 									sLODVertexOffset[trunk_LOD] + sLODVertexCount[trunk_LOD]-1,
 									sLODIndexCount[trunk_LOD],
 									GL_UNSIGNED_INT,
-									draw_pool->getRawIndices() + sLODIndexOffset[trunk_LOD]);
+									indicesp + sLODIndexOffset[trunk_LOD]);*/
 				stop_glerror();
-				draw_pool->addIndicesDrawn(sLODIndexCount[trunk_LOD]);
+				ret += sLODIndexCount[trunk_LOD];
 				glPopMatrix();
 			}
 			
@@ -828,7 +821,7 @@ void LLVOTree::drawBranchPipeline(LLDrawPool *draw_pool, S32 trunk_LOD, S32 stop
 				glRotatef((constant_twist + ((i%2==0)?twist:-twist))*i, 0.f, 0.f, 1.f);
 				glRotatef(droop, 0.f, 1.f, 0.f);
 				glRotatef(20.f, 0.f, 0.f, 1.f);				// rotate 20deg about axis of new branch to add some random variation
-				drawBranchPipeline(draw_pool, trunk_LOD, stop_level, depth - 1, 0, scale*mScaleStep, twist, droop, branches, alpha);
+				ret += drawBranchPipeline(indicesp, trunk_LOD, stop_level, depth - 1, 0, scale*mScaleStep, twist, droop, branches, alpha);
 				glPopMatrix();
 			}
 			//  Recurse to continue trunk
@@ -837,7 +830,7 @@ void LLVOTree::drawBranchPipeline(LLDrawPool *draw_pool, S32 trunk_LOD, S32 stop
 				glPushMatrix();
 				glTranslatef(0.f, 0.f, scale * length);
 				glRotatef(70.5f, 0.f, 0.f, 1.f);					// rotate a bit around Z when ascending 
-				drawBranchPipeline(draw_pool, trunk_LOD, stop_level, depth, trunk_depth-1, scale*mScaleStep, twist, droop, branches, alpha);
+				ret += drawBranchPipeline(indicesp, trunk_LOD, stop_level, depth, trunk_depth-1, scale*mScaleStep, twist, droop, branches, alpha);
 				glPopMatrix();
 			}
 		}
@@ -852,15 +845,15 @@ void LLVOTree::drawBranchPipeline(LLDrawPool *draw_pool, S32 trunk_LOD, S32 stop
 				//width = scale * (TREE_BRANCH_ASPECT + TREE_LEAF_ASPECT);
 				glScalef(scale*mLeafScale, scale*mLeafScale, scale*mLeafScale);
 				//glScalef(1.5f*width*mLeafScale,1,1.5f*scale*mLeafScale);
-// 				glDrawElements(GL_TRIANGLES, LEAF_INDICES, GL_UNSIGNED_INT, draw_pool.getRawIndices());
-				glDrawRangeElements(GL_TRIANGLES,
+ 				glDrawElements(GL_TRIANGLES, LEAF_INDICES, GL_UNSIGNED_INT, indicesp);
+				/*glDrawRangeElements(GL_TRIANGLES,
 									0,
 									LEAF_VERTICES-1,
 									LEAF_INDICES,
 									GL_UNSIGNED_INT,
-									draw_pool->getRawIndices());
+									indicesp);*/
 				stop_glerror();
-				draw_pool->addIndicesDrawn(LEAF_INDICES);
+				ret += LEAF_INDICES;
 				glPopMatrix();
 			}
 		}
@@ -878,21 +871,23 @@ void LLVOTree::drawBranchPipeline(LLDrawPool *draw_pool, S32 trunk_LOD, S32 stop
 		{
 			glPushMatrix();
 			glScalef(mBillboardScale*mBillboardRatio, mBillboardScale*mBillboardRatio, mBillboardScale);
-// 			glDrawElements(GL_TRIANGLES, LEAF_INDICES, GL_UNSIGNED_INT, draw_pool.getRawIndices());
-			glDrawRangeElements(GL_TRIANGLES,
+ 			glDrawElements(GL_TRIANGLES, LEAF_INDICES, GL_UNSIGNED_INT, indicesp);
+/*			glDrawRangeElements(GL_TRIANGLES,
 								0,
 								LEAF_VERTICES-1,
 								LEAF_INDICES,
 								GL_UNSIGNED_INT,
-								draw_pool->getRawIndices());
+								indicesp);*/
 			stop_glerror();
-			draw_pool->addIndicesDrawn(LEAF_INDICES);
+			ret += LEAF_INDICES;
 			glPopMatrix();
 		}
 		glMatrixMode(GL_TEXTURE);
 		glPopMatrix();
 		glMatrixMode(GL_MODELVIEW);
 	}
+
+	return ret;
 }
 
 void LLVOTree::updateRadius()
@@ -904,3 +899,30 @@ void LLVOTree::updateRadius()
 		
 	mDrawable->setRadius(32.0f);
 }
+
+void LLVOTree::updateSpatialExtents(LLVector3& newMin, LLVector3& newMax)
+{
+	LLVector3 center = getRenderPosition();
+	LLVector3 size = getScale();
+	center.mV[2] += size.mV[2];
+
+	newMin.setVec(center-size);
+	newMax.setVec(center+size);
+	mDrawable->setPositionGroup((newMin + newMax) * 0.5f);
+}
+
+U32 LLVOTree::getPartitionType() const
+{ 
+	return LLPipeline::PARTITION_TREE; 
+}
+
+LLTreePartition::LLTreePartition()
+: LLSpatialPartition(0)
+{
+	mRenderByGroup = FALSE;
+	mDrawableType = LLPipeline::RENDER_TYPE_TREE;
+	mPartitionType = LLPipeline::PARTITION_TREE;
+	mSlopRatio = 0.f;
+	mLODPeriod = 1;
+}
+
diff --git a/indra/newview/llvotree.h b/indra/newview/llvotree.h
index f96251680324c622d4b05f19fcfc6aa0fda1be1f..487dd706df1964c87b6746ee41783583ba207dcd 100644
--- a/indra/newview/llvotree.h
+++ b/indra/newview/llvotree.h
@@ -20,6 +20,14 @@ class LLDrawPool;
 class LLVOTree : public LLViewerObject
 {
 public:
+	enum 
+	{
+		VERTEX_DATA_MASK =	(1 << LLVertexBuffer::TYPE_VERTEX) |
+							(1 << LLVertexBuffer::TYPE_NORMAL) |
+							(1 << LLVertexBuffer::TYPE_TEXCOORD)
+	}
+	eVertexDataMask;
+
 	LLVOTree(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp);
 	virtual ~LLVOTree();
 
@@ -40,10 +48,13 @@ public:
 
 	/*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline);
 	/*virtual*/ BOOL		updateGeometry(LLDrawable *drawable);
+	/*virtual*/ void		updateSpatialExtents(LLVector3 &min, LLVector3 &max);
+
+	virtual U32 getPartitionType() const;
 
 	void updateRadius();
 
-	void drawBranchPipeline(LLDrawPool *draw_pool, S32 trunk_LOD, S32 stop_level, U16 depth, U16 trunk_depth,  F32 scale, F32 twist, F32 droop,  F32 branches, F32 alpha);
+	U32 drawBranchPipeline(U32* indicesp, S32 trunk_LOD, S32 stop_level, U16 depth, U16 trunk_depth,  F32 scale, F32 twist, F32 droop,  F32 branches, F32 alpha);
  
 	void drawBranch(S32 stop_level, U16 depth, U16 trunk_depth, F32 scale, F32 twist, F32 droop,  F32 branches, F32 alpha, BOOL draw_leaves);
 
diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp
index c1cc5b9ecb1e6f25c82bce3769e7fbf7dbceb2c6..55445f21fece9cc480aa24b7cab584d72ed38cdf 100644
--- a/indra/newview/llvovolume.cpp
+++ b/indra/newview/llvovolume.cpp
@@ -23,7 +23,6 @@
 #include "material_codes.h"
 #include "message.h"
 #include "object_flags.h"
-
 #include "llagent.h"
 #include "lldrawable.h"
 #include "lldrawpoolsimple.h"
@@ -45,14 +44,14 @@
 #include "pipeline.h"
 
 const S32 MIN_QUIET_FRAMES_COALESCE = 30;
-
-//#define	LLDEBUG_DISPLAY_LODS 1
+const F32 FORCE_SIMPLE_RENDER_AREA = 512.f;
+const F32 FORCE_CULL_AREA = 8.f;
 
 BOOL gAnimateTextures = TRUE;
+extern BOOL gHideSelectedObjects;
 
 F32 LLVOVolume::sLODFactor = 1.f;
 F32	LLVOVolume::sLODSlopDistanceFactor = 0.5f; //Changing this to zero, effectively disables the LOD transition slop 
-F32	LLVOVolume::sLODComplexityDistanceBias = 0.0f;//Changing this to zero makes all prims LOD equally regardless of complexity
 F32 LLVOVolume::sDistanceFactor = 1.0f;
 S32 LLVOVolume::sNumLODChanges = 0;
 
@@ -65,12 +64,8 @@ LLVOVolume::LLVOVolume(const LLUUID &id, const LLPCode pcode, LLViewerRegion *re
 
 	mLOD = MIN_LOD;
 	mInited = FALSE;
-	mAllTEsSame = FALSE;
 	mTextureAnimp = NULL;
 	mGlobalVolume = FALSE;
-
-	mTextureAnimp = NULL;
-	mAllTEsSame = FALSE;
 	mVObjRadius = LLVector3(1,1,0.5f).magVec();
 	mNumFaces = 0;
 }
@@ -124,11 +119,17 @@ U32 LLVOVolume::processUpdateMessage(LLMessageSystem *mesgsys,
 					}
 				}
 				mTextureAnimp->unpackTAMessage(mesgsys, block_num);
+				gPipeline.markTextured(mDrawable);
 			}
 			else
 			{
-				delete mTextureAnimp;
-				mTextureAnimp = NULL;
+				if (mTextureAnimp)
+				{
+					delete mTextureAnimp;
+					mTextureAnimp = NULL;
+					gPipeline.markTextured(mDrawable);
+					mFaceMappingChanged = TRUE;
+				}
 			}
 
 			// Unpack volume data
@@ -239,52 +240,39 @@ BOOL LLVOVolume::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
 
 	if (mTextureAnimp && gAnimateTextures)
 	{
-		F32 off_s, off_t, scale_s, scale_t, rot;
+		F32 off_s = 0.f, off_t = 0.f, scale_s = 1.f, scale_t = 1.f, rot = 0.f;
 		S32 result;
-		if ((result = mTextureAnimp->animateTextures(off_s, off_t, scale_s, scale_t, rot)))
+		if (result = mTextureAnimp->animateTextures(off_s, off_t, scale_s, scale_t, rot))
 		{
-			U8 has_bump = 0;
-			if (mTextureAnimp->mFace <= -1)
+			mTexAnimMode = result | mTextureAnimp->mMode;
+			LLQuaternion quat;
+			LLVector3 scale(1,1,1);
+
+			if (result & LLViewerTextureAnim::ROTATE)
 			{
-				S32 face;
-				for (face = 0; face < getNumTEs(); face++)
-				{
-					if (result & LLViewerTextureAnim::TRANSLATE)
-					{
-						setTEOffset(face, off_s, off_t);
-					}
-					if (result & LLViewerTextureAnim::SCALE)
-					{
-						setTEScale(face, scale_s, scale_t);
-					}
-					if (result & LLViewerTextureAnim::ROTATE)
-					{
-						setTERotation(face, rot);
-					}
-					has_bump |= getTE(face)->getBumpmap();
-				}
+				quat.setQuat(rot, 0, 0, -1);
 			}
-			else if (mTextureAnimp->mFace < getNumTEs())
+			
+			if (!(result & LLViewerTextureAnim::TRANSLATE))
 			{
-				if (result & LLViewerTextureAnim::TRANSLATE)
-				{
-					setTEOffset(mTextureAnimp->mFace, off_s, off_t);
-				}
-				if (result & LLViewerTextureAnim::SCALE)
-				{
-					setTEScale(mTextureAnimp->mFace, scale_s, scale_t);
-				}
-				if (result & LLViewerTextureAnim::ROTATE)
-				{
-					setTERotation(mTextureAnimp->mFace, rot);
-				}
-				has_bump |= getTE(mTextureAnimp->mFace)->getBumpmap();
+				off_s = off_t = 0.f;
 			}
-// 			mFaceMappingChanged = TRUE;
-			if (mDrawable->isVisible())
+			
+			LLVector3 trans(off_s+0.5f, off_t+0.5f, 0.f);
+
+			mTextureMatrix.identity();
+			mTextureMatrix.translate(LLVector3(-0.5f, -0.5f, 0.f));
+			mTextureMatrix.rotate(quat);
+
+			if (result & LLViewerTextureAnim::SCALE)
 			{
-				gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_TCOORD, TRUE);
+				scale.setVec(scale_s, scale_t, 1.f);
+				LLMatrix4 mat;
+				mat.initAll(scale, LLQuaternion(), LLVector3());
+				mTextureMatrix *= mat;
 			}
+			
+			mTextureMatrix.translate(trans);
 		}
 	}
 
@@ -299,71 +287,40 @@ BOOL LLVOVolume::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
 
 void LLVOVolume::updateTextures(LLAgent &agent)
 {
-
+// 	LLFastTimer t(LLFastTimer::FTM_TEMP6);
+	const F32 TEXTURE_AREA_REFRESH_TIME = 5.f; // seconds
+	if (mTextureUpdateTimer.getElapsedTimeF32() > TEXTURE_AREA_REFRESH_TIME)
+	{
+		if (mDrawable->isVisible())
+		{
+			updateTextures();
+		}
+	}
 }
 
-//static
-F32 LLVOVolume::getTextureVirtualSize(const LLFace* face)
+void LLVOVolume::updateTextures()
 {
-	//LLVector2 tdim = face->mTexExtents[1] - face->mTexExtents[0];
-	//F32 pixel_area = 1.f/llmin(llmax(tdim.mV[0] * tdim.mV[1], 1.f), 10.f);
-	LLVector3 cross_vec = (face->mExtents[1] - face->mExtents[0]);
-	
-
-	LLVector3 lookAt = (face->getPositionAgent()-gCamera->getOrigin());
-	F32 dist = lookAt.normVec();
-
-	F32 face_area;	
-	
-	if (face->isState(LLFace::GLOBAL))
-	{
-		face_area = cross_vec.mV[0]*cross_vec.mV[1]*fabsf(lookAt.mV[2]) +  
-					cross_vec.mV[1]*cross_vec.mV[2]*fabsf(lookAt.mV[0]) +
-					cross_vec.mV[0]*cross_vec.mV[2]*fabsf(lookAt.mV[1]);
-	}
-	else
-	{
-		face_area = cross_vec.mV[0]*cross_vec.mV[1] + 
-					cross_vec.mV[1]*cross_vec.mV[2] +
-					cross_vec.mV[0]*cross_vec.mV[2];
-	}
+	// Update the pixel area of all faces
 
-	if (face_area <= 0)
+	if (!gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_SIMPLE))
 	{
-		return 0.f;
+		return;
 	}
-
-	F32 view = llmax(lookAt*gCamera->getAtAxis(), 0.5f);
-	F32 dist_ramp = dist * view/face_area;
-	//ramp down distance for things closer than 16 m * lookAt
-	dist /= dist_ramp;
-	dist *= dist;
-	dist *= dist_ramp;
-
-	F32 dist_ratio = face_area / llmax(dist, 0.1f);
-	F32 pixel_area = dist_ratio*gCamera->getScreenPixelArea();
 	
-	return view*pixel_area;
-}
-
-void LLVOVolume::updateTextures(S32 lod)
-{
-	// Update the image levels of all textures...
-	// First we do some quick checks.
-
-	// This doesn't take into account whether the object is in front
-	// or behind...
-
-	if (LLViewerImage::sDontLoadVolumeTextures || mDrawable.isNull() || !mDrawable->isVisible())
+	if (LLViewerImage::sDontLoadVolumeTextures || mDrawable.isNull()) // || !mDrawable->isVisible())
 	{
 		return;
 	}
-		
-	const S32 num_faces = mDrawable->getNumFaces();
+
+	mTextureUpdateTimer.reset();
 	
+	mPixelArea = 0.f;
+	const S32 num_faces = mDrawable->getNumFaces();
+
+	F32 min_vsize=999999999.f, max_vsize=0.f;
 	for (S32 i = 0; i < num_faces; i++)
 	{
-		const LLFace* face = mDrawable->getFace(i);
+		LLFace* face = mDrawable->getFace(i);
 		const LLTextureEntry *te = face->getTextureEntry();
 		LLViewerImage *imagep = face->getTexture();
 
@@ -376,23 +333,77 @@ void LLVOVolume::updateTextures(S32 lod)
 		
 		if (isHUDAttachment())
 		{
-			vsize = (F32) (imagep->getWidth(0) * imagep->getHeight(0));
+			F32 area = (F32) gCamera->getScreenPixelArea();
+			vsize = area;
 			imagep->setBoostLevel(LLViewerImage::BOOST_HUD);
+ 			face->setPixelArea(area); // treat as full screen
 		}
 		else
 		{
 			vsize = getTextureVirtualSize(face);
 		}
 
+		mPixelArea = llmax(mPixelArea, face->getPixelArea());
+		face->setVirtualSize(vsize);
 		imagep->addTextureStats(vsize);
-
-
-		U8 bump = te->getBumpmap();
-		if( te && bump)
+		if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_AREA))
 		{
-			gBumpImageList.addTextureStats( bump, imagep->getID(), vsize, 1, 1);
+			if (vsize < min_vsize) min_vsize = vsize;
+			if (vsize > max_vsize) max_vsize = vsize;
 		}
+		else if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_PRIORITY))
+		{
+			F32 pri = imagep->getDecodePriority();
+			if (pri < min_vsize) min_vsize = pri;
+			if (pri > max_vsize) max_vsize = pri;
+		}
+	//	U8 bump = te->getBumpmap();
+	//	if( te && bump)
+	//	{
+	//		gBumpImageList.addTextureStats( bump, imagep->getID(), vsize, 1, 1);
+	//	}
+	}
+
+	if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_AREA))
+	{
+		setDebugText(llformat("%.0f:%.0f", fsqrtf(min_vsize),fsqrtf(max_vsize)));
+	}
+	else if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_PRIORITY))
+	{
+		setDebugText(llformat("%.0f:%.0f", fsqrtf(min_vsize),fsqrtf(max_vsize)));
+	}
+}
+
+F32 LLVOVolume::getTextureVirtualSize(LLFace* face)
+{
+	//get area of circle around face
+	LLVector3 center = face->getPositionAgent();
+	LLVector3 size = //isFlexible() ? 
+					//	getScale()*3.f :
+						(face->mExtents[1] - face->mExtents[0]) * 0.5f;
+	
+	F32 face_area = LLPipeline::calcPixelArea(center, size, *gCamera);
+
+	face->setPixelArea(face_area);
+
+	if (face_area <= 0)
+	{
+		return 0.f;
+	}
+
+	//get area of circle in texture space
+	LLVector2 tdim = face->mTexExtents[1] - face->mTexExtents[0];
+	F32 texel_area = (tdim * 0.5f).magVecSquared()*3.14159f;
+	if (texel_area <= 0)
+	{
+		// Probably animated, use default
+		texel_area = 1.f;
 	}
+
+	//apply texel area to face area to get accurate ratio
+	face_area /= llclamp(texel_area, 1.f/64.f, 16.f);
+
+	return face_area;
 }
 
 BOOL LLVOVolume::isActive() const
@@ -436,7 +447,7 @@ void LLVOVolume::setScale(const LLVector3 &scale, BOOL damped)
 
 		//since drawable transforms do not include scale, changing volume scale
 		//requires an immediate rebuild of volume verts.
-		gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);
+		gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_POSITION, TRUE);
 	}
 }
 
@@ -444,16 +455,7 @@ LLFace* LLVOVolume::addFace(S32 f)
 {
 	const LLTextureEntry* te = getTE(f);
 	LLViewerImage* imagep = getTEImage(f);
-	LLDrawPool* poolp;
-	if (isHUDAttachment())
-	{
-		poolp = gPipeline.getPool(LLDrawPool::POOL_HUD);
-	}
-	else
-	{
-		poolp = LLPipeline::getPoolFromTE(te, imagep);
-	}
-	return mDrawable->addFace(poolp, imagep);
+	return mDrawable->addFace(te, imagep);
 }
 
 LLDrawable *LLVOVolume::createDrawable(LLPipeline *pipeline)
@@ -461,7 +463,7 @@ LLDrawable *LLVOVolume::createDrawable(LLPipeline *pipeline)
 	pipeline->allocDrawable(this);
 	mDrawable->setRenderType(LLPipeline::RENDER_TYPE_VOLUME);
 
-	S32 max_tes_to_set = calcAllTEsSame() ? 1 : getNumTEs();
+	S32 max_tes_to_set = getNumTEs();
 	for (S32 i = 0; i < max_tes_to_set; i++)
 	{
 		LLFace* face = addFace(i);
@@ -518,10 +520,6 @@ BOOL LLVOVolume::setVolume(const LLVolumeParams &volume_params, const S32 detail
 	}
 	mGlobalVolume = (mVolumeImpl && mVolumeImpl->isVolumeGlobal());
 	
-	//MSMSM Recompute LOD here in case the object was just created,
-	// its LOD might be incorrectly set to minumum detail...
-	calcLOD();
-
 	if (LLPrimitive::setVolume(volume_params, mLOD, (mVolumeImpl && mVolumeImpl->isVolumeUnique())))
 	{
 		mFaceMappingChanged = TRUE;
@@ -536,45 +534,12 @@ BOOL LLVOVolume::setVolume(const LLVolumeParams &volume_params, const S32 detail
 	return FALSE;
 }
 
-
-F32	LLVOVolume::computeLODProfilePathComplexityBias(){
-	//compute a complexity cost from 0 to 1.0 where the 'simplest' prim has a cost of 0.0
-	// and the 'heaviest' prim has a cost of 1.0
-//	LLVolume*	volume = getVolume();
-	F32			complexity = 0.0f;
-//	const LLVolumeParams&	params = volume->getParams();
-//	U8 type = volume->getPathType();
-//	U8 pcode = this->getPCode();
-//	U8 proftype = volume->getProfileType();
-
-	//if(params.getHollow()>0.0f){// || (proftype == 1) || (proftype == 0)){
-		//If it is hollow, or a cube/pyramid(subdivided), the complexity is roughly doubled
-	//	complexity+=0.5f;
-	//}
-
-	if(this->getVolume()->getProfile().mParams.getCurveType()==LL_PCODE_PROFILE_SQUARE &&
-		this->getVolume()->getPath().mParams.getCurveType()==LL_PCODE_PATH_LINE)
-	{
-		//Object is a cube so bias it heavily since cubes are subdivided alot.
-//		this->setDebugText("CUBE");
-		complexity += 1.0f;
-	}
-
-//	if(params.getTwist() != params.getTwistBegin()){
-		//if there is twist.. the complexity is bumped
-//		complexity+=0.25f;
-//	}
-//	if(type != LL_PCODE_PATH_LINE)//If the path is not a line it is more complex
-//		complexity+=0.2f;
-	return complexity * sLODComplexityDistanceBias;
-}
-
 S32	LLVOVolume::computeLODDetail(F32 distance, F32 radius)
 {
 	S32	cur_detail;
 	// We've got LOD in the profile, and in the twist.  Use radius.
 	F32 tan_angle = (LLVOVolume::sLODFactor*radius)/distance;
-	cur_detail = LLVolumeLODGroup::getDetailFromTan(tan_angle);
+	cur_detail = LLVolumeLODGroup::getDetailFromTan(llround(tan_angle, 0.01f));
 	return cur_detail;
 }
 
@@ -584,62 +549,35 @@ BOOL LLVOVolume::calcLOD()
 	{
 		return FALSE;
 	}
+
+	//update textures here as well
+	updateTextures();
+
 	S32 cur_detail = 0;
-	/*if (isHUDAttachment())
+	
+	F32 radius = mVolumep->mLODScaleBias.scaledVec(getScale()).magVec();
+	F32 distance = mDrawable->mDistanceWRTCamera;
+	distance *= sDistanceFactor;
+			
+	F32 rampDist = LLVOVolume::sLODFactor * 2;
+	
+	if (distance < rampDist)
 	{
-		cur_detail = LLVolumeLODGroup::NUM_LODS-1; // max detail
+		// Boost LOD when you're REALLY close
+		distance *= 1.0f/rampDist;
+		distance *= distance;
+		distance *= rampDist;
 	}
-	else*/
-	{	
-		F32 radius = (mVolumep->mLODScaleBias.scaledVec(getScale())).magVec();
-		F32 distance = mDrawable->mDistanceWRTCamera;
-		distance *= sDistanceFactor;
-				
-		F32 rampDist = LLVOVolume::sLODFactor * 2;
-		
-		if (distance < rampDist)
-		{
-			// Boost LOD when you're REALLY close
-			distance *= 1.0f/rampDist;
-			distance *= distance;
-			distance *= rampDist;
-		}
-		else
-		{
-			//Now adjust the computed distance by some factor based on the geometric complexity of the primitive
-			distance += computeLODProfilePathComplexityBias();
-		}
-		// Compensate for field of view changing on FOV zoom.
-		distance *= gCamera->getView();
-
-		cur_detail = computeLODDetail(distance, radius);
-
-		//update textures with what the real LOD is
-		updateTextures(cur_detail);
+	
+	// DON'T Compensate for field of view changing on FOV zoom.
+	distance *= 3.14159f/3.f;
 
-		if(cur_detail != mLOD)
-		{
-			// Here we test whether the LOD is increasing or decreasing to introduce a slop factor
-			if(cur_detail < mLOD)
-			{
-				// Viewer is moving away from the object
-				// so bias our LOD by adding a fixed amount to the distance.
-				// This will reduce the problem of LOD twitching when the 
-				// user makes slight movements near the LOD transition threshhold.
-				F32	test_distance = distance - (distance*sLODSlopDistanceFactor/(1.0f+sLODFactor));
-				if(test_distance < 0.0f) test_distance = 0.0f;
-				S32	potential_detail = computeLODDetail( test_distance, radius );
-				if(potential_detail >= mLOD )
-				{	//The LOD has truly not changed
-					cur_detail = mLOD;
-				}
-			}
-		}
-	}
+	cur_detail = computeLODDetail(llround(distance, 0.01f), 
+									llround(radius, 0.01f));
 
 	if (cur_detail != mLOD)
 	{
-		mAppAngle = (F32) atan2( mDrawable->getRadius(), mDrawable->mDistanceWRTCamera) * RAD_TO_DEG;
+		mAppAngle = llround((F32) atan2( mDrawable->getRadius(), mDrawable->mDistanceWRTCamera) * RAD_TO_DEG, 0.01f);
 		mLOD = cur_detail;		
 		return TRUE;
 	}
@@ -657,17 +595,10 @@ BOOL LLVOVolume::updateLOD()
 	}
 	
 	BOOL lod_changed = calcLOD();
-	
-#if LLDEBUG_DISPLAY_LODS
-	//MS Enable this to display LOD numbers on objects
-	std::ostringstream msg;
-	msg << cur_detail;//((cur_detail<mLOD)?"-":cur_detail==mLOD?"=":"+") << (int)cur_detail << " , " << mDrawable->mDistanceWRTCamera << " , " << ((LLVOVolume::sLODFactor*mVObjRadius)/mDrawable->mDistanceWRTCamera);
-	this->setDebugText(msg.str());
-#endif
-	
+
 	if (lod_changed)
 	{
-		gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_GEOMETRY, FALSE);
+		gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, FALSE);
 		mLODChanged = TRUE;
 	}
 
@@ -684,8 +615,8 @@ BOOL LLVOVolume::setDrawableParent(LLDrawable* parentp)
 
 	if (!mDrawable->isRoot())
 	{
-		// parent is dynamic, so I'll need to share its drawable, must rebuild to share drawables
-		gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_GEOMETRY, TRUE);
+		// rebuild vertices in parent relative space
+		gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);
 
 		if (mDrawable->isActive() && !parentp->isActive())
 		{
@@ -704,7 +635,7 @@ void LLVOVolume::updateFaceFlags()
 {
 	for (S32 i = 0; i < getVolume()->getNumFaces(); i++)
 	{
-		LLFace *face = mDrawable->getFace(i + mFaceIndexOffset);
+		LLFace *face = mDrawable->getFace(i);
 		BOOL fullbright = getTE(i)->getFullbright();
 		face->clearState(LLFace::FULLBRIGHT | LLFace::HUD_RENDER | LLFace::LIGHT);
 
@@ -720,10 +651,6 @@ void LLVOVolume::updateFaceFlags()
 		{
 			face->setState(LLFace::HUD_RENDER);
 		}
-		if (getAllTEsSame())
-		{
-			break; // only 1 face
-		}
 	}
 }
 
@@ -731,104 +658,74 @@ void LLVOVolume::updateFaceFlags()
 void LLVOVolume::regenFaces()
 {
 	// remove existing faces
-	// use mDrawable->getVOVolume() in case of shared drawables
-	mDrawable->getVOVolume()->deleteFaces(this);
-	mFaceIndexOffset = mDrawable->getNumFaces();
+	deleteFaces();
+	
 	// add new faces
-	mNumFaces = getAllTEsSame() ? 1 : getNumTEs();
+	mNumFaces = getNumTEs();
 	for (S32 i = 0; i < mNumFaces; i++)
 	{
 		LLFace* facep = addFace(i);
 		facep->setViewerObject(this);
 		facep->setTEOffset(i);
 	}
-	// Need to do this as texture entries may not correspond to faces any more!
-	mDrawable->updateTexture();
-	gPipeline.markMaterialed(mDrawable);
 }
 
-BOOL LLVOVolume::genTriangles(BOOL force_global)
+BOOL LLVOVolume::genBBoxes(BOOL force_global)
 {
 	BOOL res = TRUE;
 
 	LLVector3 min,max;
 
-	if (getAllTEsSame())
+	BOOL rebuild = mDrawable->isState(LLDrawable::REBUILD_VOLUME | LLDrawable::REBUILD_POSITION);
+
+	for (S32 i = 0; i < getVolume()->getNumFaces(); i++)
 	{
-		setupSingleFace(mFaceIndexOffset);
-		LLFace *face = mDrawable->getFace(mFaceIndexOffset);
-		S32 num_faces = getVolume()->getNumFaces();
-		res = face->genVolumeTriangles(*getVolume(), 0, num_faces-1,
-									   mRelativeXform, mRelativeXformInvTrans,
-									   mGlobalVolume | force_global);
+		LLFace *face = mDrawable->getFace(i);
+		res &= face->genVolumeBBoxes(*getVolume(), i,
+										mRelativeXform, mRelativeXformInvTrans,
+										mGlobalVolume | force_global);
 		
-		if (mDrawable->isState(LLDrawable::REBUILD_VOLUME))
-		{
-			min = face->mExtents[0];
-			max = face->mExtents[1];
-		}
-		mWereAllTEsSame = TRUE;
-	}
-	else
-	{
-		for (S32 i = 0; i < getVolume()->getNumFaces(); i++)
+		if (rebuild)
 		{
-			LLFace *face = mDrawable->getFace(i + mFaceIndexOffset);
-			res &= face->genVolumeTriangles(*getVolume(), i,
-											mRelativeXform, mRelativeXformInvTrans,
-											mGlobalVolume | force_global);
-			
-			if (mDrawable->isState(LLDrawable::REBUILD_VOLUME))
+			if (i == 0)
 			{
-				if (i == 0)
-				{
-					min = face->mExtents[0];
-					max = face->mExtents[1];
-				}
-				else
+				min = face->mExtents[0];
+				max = face->mExtents[1];
+			}
+			else
+			{
+				for (U32 i = 0; i < 3; i++)
 				{
-					for (U32 i = 0; i < 3; i++)
+					if (face->mExtents[0].mV[i] < min.mV[i])
 					{
-						if (face->mExtents[0].mV[i] < min.mV[i])
-						{
-							min.mV[i] = face->mExtents[0].mV[i];
-						}
-						if (face->mExtents[1].mV[i] > max.mV[i])
-						{
-							max.mV[i] = face->mExtents[1].mV[i];
-						}
+						min.mV[i] = face->mExtents[0].mV[i];
+					}
+					if (face->mExtents[1].mV[i] > max.mV[i])
+					{
+						max.mV[i] = face->mExtents[1].mV[i];
 					}
 				}
 			}
 		}
-		mWereAllTEsSame = FALSE;
 	}
-
-	if (mDrawable->isState(LLDrawable::REBUILD_VOLUME))
+	
+	if (rebuild)
 	{
 		mDrawable->setSpatialExtents(min,max);
-		if (!isVolumeGlobal())
-		{
-			mDrawable->setPositionGroup((min+max)*0.5f);	
-		}
-		else
-		{
-			mDrawable->setPositionGroup(getPosition());
-		}
-
-		updateRadius();
-		mDrawable->updateBinRadius();
-		mDrawable->movePartition();
+		mDrawable->setPositionGroup((min+max)*0.5f);	
 	}
-	
+
+	updateRadius();
+	mDrawable->movePartition();
+			
 	return res;
 }
 
-void LLVOVolume::updateRelativeXform(BOOL global_volume)
+void LLVOVolume::updateRelativeXform()
 {
 	if (mVolumeImpl)
 	{
-		mVolumeImpl->updateRelativeXform(global_volume);
+		mVolumeImpl->updateRelativeXform();
 		return;
 	}
 	
@@ -854,12 +751,25 @@ void LLVOVolume::updateRelativeXform(BOOL global_volume)
 								LLVector4(y_axis, 0.f),
 								LLVector4(z_axis, 0.f),
 								LLVector4(delta_pos, 1.f));
-				
-		x_axis.normVec();
-		y_axis.normVec();
-		z_axis.normVec();
+
+		
+		// compute inverse transpose for normals
+		// mRelativeXformInvTrans.setRows(x_axis, y_axis, z_axis);
+		// mRelativeXformInvTrans.invert(); 
+		// mRelativeXformInvTrans.setRows(x_axis, y_axis, z_axis);
+		// grumble - invert is NOT a matrix invert, so we do it by hand:
+
+		LLMatrix3 rot_inverse = LLMatrix3(~delta_rot);
+
+		LLMatrix3 scale_inverse;
+		scale_inverse.setRows(LLVector3(1.0, 0.0, 0.0) / delta_scale.mV[VX],
+							  LLVector3(0.0, 1.0, 0.0) / delta_scale.mV[VY],
+							  LLVector3(0.0, 0.0, 1.0) / delta_scale.mV[VZ]);
+							   
 		
-		mRelativeXformInvTrans.setRows(x_axis, y_axis, z_axis);
+		mRelativeXformInvTrans = rot_inverse * scale_inverse;
+
+		mRelativeXformInvTrans.transpose();
 	}
 	else
 	{
@@ -886,34 +796,35 @@ void LLVOVolume::updateRelativeXform(BOOL global_volume)
 								LLVector4(z_axis, 0.f),
 								LLVector4(pos, 1.f));
 
-		x_axis.normVec();
-		y_axis.normVec();
-		z_axis.normVec();
-																					
-		mRelativeXformInvTrans.setRows(x_axis, y_axis, z_axis);
+		// compute inverse transpose for normals
+		LLMatrix3 rot_inverse = LLMatrix3(~rot);
+
+		LLMatrix3 scale_inverse;
+		scale_inverse.setRows(LLVector3(1.0, 0.0, 0.0) / scale.mV[VX],
+							  LLVector3(0.0, 1.0, 0.0) / scale.mV[VY],
+							  LLVector3(0.0, 0.0, 1.0) / scale.mV[VZ]);
+							   
+		
+		mRelativeXformInvTrans = rot_inverse * scale_inverse;
+
+		mRelativeXformInvTrans.transpose();
 	}
 }
 
 BOOL LLVOVolume::updateGeometry(LLDrawable *drawable)
 {
 	LLFastTimer t(LLFastTimer::FTM_UPDATE_PRIMITIVES);
-
+	
 	if (mVolumeImpl != NULL)
 	{
 		LLFastTimer t(LLFastTimer::FTM_GEN_FLEX);
 		BOOL res = mVolumeImpl->doUpdateGeometry(drawable);
 		updateFaceFlags();
-		if (res)
-		{
-			drawable->clearState(LLDrawable::REBUILD_GEOMETRY);
-		}
-
 		return res;
 	}
 	
 	BOOL compiled = FALSE;
-	BOOL change_shared = FALSE;
-		
+			
 	updateRelativeXform();
 	
 	if (mDrawable.isNull()) // Not sure why this is happening, but it is...
@@ -921,28 +832,23 @@ BOOL LLVOVolume::updateGeometry(LLDrawable *drawable)
 		return TRUE; // No update to complete
 	}
 
-	calcAllTEsSame();
-	
-	if (mVolumeChanged || mFaceMappingChanged || change_shared)
+	if (mVolumeChanged || mFaceMappingChanged )
 	{
 		compiled = TRUE;
 		mInited = TRUE;
 
+		if (mVolumeChanged)
 		{
 			LLFastTimer ftm(LLFastTimer::FTM_GEN_VOLUME);
 			LLVolumeParams volume_params = getVolume()->getParams();
 			setVolume(volume_params, 0);
-		}
-		drawable->setState(LLDrawable::REBUILD_GEOMETRY);
-		if (mVolumeChanged || change_shared)
-		{
-			drawable->setState(LLDrawable::REBUILD_LIGHTING);
+			drawable->setState(LLDrawable::REBUILD_VOLUME);
 		}
 
 		{
 			LLFastTimer t(LLFastTimer::FTM_GEN_TRIANGLES);
 			regenFaces();
-			genTriangles(FALSE);
+			genBBoxes(FALSE);
 		}
 	}
 	else if (mLODChanged)
@@ -964,9 +870,9 @@ BOOL LLVOVolume::updateGeometry(LLDrawable *drawable)
 		if (new_lod != old_lod)
 		{
 			compiled = TRUE;
-			sNumLODChanges += (getAllTEsSame() ? 1 : getVolume()->getNumFaces());
+			sNumLODChanges += getVolume()->getNumFaces();
 	
-			drawable->setState(LLDrawable::REBUILD_ALL); // for face->genVolumeTriangles()
+			drawable->setState(LLDrawable::REBUILD_VOLUME); // for face->genVolumeTriangles()
 
 			{
 				LLFastTimer t(LLFastTimer::FTM_GEN_TRIANGLES);
@@ -974,7 +880,7 @@ BOOL LLVOVolume::updateGeometry(LLDrawable *drawable)
 				{
 					regenFaces();
 				}
-				genTriangles(FALSE);
+				genBBoxes(FALSE);
 			}
 		}
 	}
@@ -984,7 +890,7 @@ BOOL LLVOVolume::updateGeometry(LLDrawable *drawable)
 		compiled = TRUE;
 		// All it did was move or we changed the texture coordinate offset
 		LLFastTimer t(LLFastTimer::FTM_GEN_TRIANGLES);
-		genTriangles(FALSE);
+		genBBoxes(FALSE);
 	}
 
 	// Update face flags
@@ -999,11 +905,16 @@ BOOL LLVOVolume::updateGeometry(LLDrawable *drawable)
 	mLODChanged = FALSE;
 	mFaceMappingChanged = FALSE;
 
-	drawable->clearState(LLDrawable::REBUILD_GEOMETRY);
-
 	return TRUE;
 }
 
+void LLVOVolume::updateFaceSize(S32 idx)
+{
+	const LLVolumeFace& vol_face = getVolume()->getVolumeFace(idx);
+	LLFace* facep = mDrawable->getFace(idx);
+	facep->setSize(vol_face.mVertices.size(), vol_face.mIndices.size());
+}
+
 BOOL LLVOVolume::isRootEdit() const
 {
 	if (mParent && !((LLViewerObject*)mParent)->isAvatar())
@@ -1015,178 +926,121 @@ BOOL LLVOVolume::isRootEdit() const
 
 void LLVOVolume::setTEImage(const U8 te, LLViewerImage *imagep)
 {
-//	llinfos << "SetTEImage:" << llendl;
 	BOOL changed = (mTEImages[te] != imagep);
 	LLViewerObject::setTEImage(te, imagep);
-	if (mDrawable.notNull())
+	if (changed)
 	{
-		if (changed)
-		{
-			calcAllTEsSame();
-			gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_GEOMETRY, TRUE);
-			mFaceMappingChanged = TRUE;
-		}
+		gPipeline.markTextured(mDrawable);
+		mFaceMappingChanged = TRUE;
 	}
 }
 
 S32 LLVOVolume::setTETexture(const U8 te, const LLUUID &uuid)
 {
-	BOOL changed = (uuid != getTE(te)->getID() || (uuid == LLUUID::null));
-
 	S32 res = LLViewerObject::setTETexture(te, uuid);
-	if (mDrawable.notNull())
+	if (res)
 	{
-		if (changed)
-		{
-			calcAllTEsSame();
-			gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_GEOMETRY, TRUE);
-			mFaceMappingChanged = TRUE;
-		}
+		gPipeline.markTextured(mDrawable);
+		mFaceMappingChanged = TRUE;
 	}
 	return res;
 }
 
 S32 LLVOVolume::setTEColor(const U8 te, const LLColor4 &color)
 {
-	BOOL changed = (color != getTE(te)->getColor());
 	S32 res = LLViewerObject::setTEColor(te, color);
-	if (mDrawable.notNull())
+	if (res)
 	{
-		if (changed)
-		{
-			calcAllTEsSame();
-// 			mFaceMappingChanged = TRUE;
-		}
+		gPipeline.markTextured(mDrawable);
+		mFaceMappingChanged = TRUE;
 	}
 	return  res;
 }
 
 S32 LLVOVolume::setTEBumpmap(const U8 te, const U8 bumpmap)
 {
-	BOOL changed = (bumpmap != getTE(te)->getBumpmap());
 	S32 res = LLViewerObject::setTEBumpmap(te, bumpmap);
-	if (mDrawable.notNull())
+	if (res)
 	{
-		if (changed)
-		{
-			calcAllTEsSame();
-			mFaceMappingChanged = TRUE;
-		}
+		gPipeline.markTextured(mDrawable);
+		mFaceMappingChanged = TRUE;
 	}
 	return  res;
 }
 
 S32 LLVOVolume::setTETexGen(const U8 te, const U8 texgen)
 {
-	BOOL changed = (texgen != getTE(te)->getTexGen());
 	S32 res = LLViewerObject::setTETexGen(te, texgen);
-	if (mDrawable.notNull())
+	if (res)
 	{
-		if (changed)
-		{
-			calcAllTEsSame();
-			mFaceMappingChanged = TRUE;
-		}
+		gPipeline.markTextured(mDrawable);
+		mFaceMappingChanged = TRUE;
 	}
 	return  res;
 }
 
 S32 LLVOVolume::setTEShiny(const U8 te, const U8 shiny)
 {
-	BOOL changed = (shiny != getTE(te)->getShiny());
 	S32 res = LLViewerObject::setTEShiny(te, shiny);
-	if (mDrawable.notNull())
+	if (res)
 	{
-		if (changed)
-		{
-			calcAllTEsSame();
-			mFaceMappingChanged = TRUE;
-		}
+		gPipeline.markTextured(mDrawable);
+		mFaceMappingChanged = TRUE;
 	}
 	return  res;
 }
 
 S32 LLVOVolume::setTEFullbright(const U8 te, const U8 fullbright)
 {
-	BOOL changed = (fullbright != getTE(te)->getFullbright());
 	S32 res = LLViewerObject::setTEFullbright(te, fullbright);
-	if (mDrawable.notNull())
+	if (res)
 	{
-		if (changed)
-		{
-			calcAllTEsSame();
-			if (!mDrawable->isState(LLDrawable::REBUILD_VOLUME))
-			{
-				updateFaceFlags();
-			}
-			mFaceMappingChanged = TRUE;
-		}
+		gPipeline.markTextured(mDrawable);
+		mFaceMappingChanged = TRUE;
 	}
 	return  res;
 }
 
 S32 LLVOVolume::setTEMediaFlags(const U8 te, const U8 media_flags)
 {
-	bool changed = (media_flags != getTE(te)->getMediaFlags());
 	S32 res = LLViewerObject::setTEMediaFlags(te, media_flags);
-	if (mDrawable.notNull())
+	if (res)
 	{
-		if (changed)
-		{
-			calcAllTEsSame();
-			mFaceMappingChanged = TRUE;
-		}
+		gPipeline.markTextured(mDrawable);
+		mFaceMappingChanged = TRUE;
 	}
 	return  res;
 }
 
 S32 LLVOVolume::setTEScale(const U8 te, const F32 s, const F32 t)
 {
-	F32 olds,oldt;
-	getTE(te)->getScale(&olds, &oldt);
-	bool changed = (s != olds || t != oldt);
 	S32 res = LLViewerObject::setTEScale(te, s, t);
-	if (mDrawable.notNull())
+	if (res)
 	{
-		if (changed)
-		{
-			calcAllTEsSame();
-			mFaceMappingChanged = TRUE;
-		}
+		gPipeline.markTextured(mDrawable);
+		mFaceMappingChanged = TRUE;
 	}
 	return res;
 }
 
 S32 LLVOVolume::setTEScaleS(const U8 te, const F32 s)
 {
-	F32 olds,oldt;
-	getTE(te)->getScale(&olds, &oldt);
-	bool changed = (s != olds);
 	S32 res = LLViewerObject::setTEScaleS(te, s);
-	if (mDrawable.notNull())
+	if (res)
 	{
-		if (changed)
-		{
-			calcAllTEsSame();
-			mFaceMappingChanged = TRUE;
-		}
+		gPipeline.markTextured(mDrawable);
+		mFaceMappingChanged = TRUE;
 	}
 	return res;
 }
 
 S32 LLVOVolume::setTEScaleT(const U8 te, const F32 t)
 {
-	F32 olds,oldt;
-	getTE(te)->getScale(&olds, &oldt);
-	bool changed = (t != oldt);
 	S32 res = LLViewerObject::setTEScaleT(te, t);
-	if (mDrawable.notNull())
+	if (res)
 	{
-		if (changed)
-		{
-			calcAllTEsSame();
-			mFaceMappingChanged = TRUE;
-		}
+		gPipeline.markTextured(mDrawable);
+		mFaceMappingChanged = TRUE;
 	}
 	return res;
 }
@@ -1195,142 +1049,16 @@ void LLVOVolume::updateTEData()
 {
 	if (mDrawable.notNull())
 	{
-		calcAllTEsSame();
 		mFaceMappingChanged = TRUE;
-		gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_GEOMETRY, TRUE);
+		gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_MATERIAL, TRUE);
 	}
 }
 
-BOOL LLVOVolume::calcAllTEsSame()
-{
-	BOOL is_alpha = FALSE;
-	BOOL was_same = mAllTEsSame;
-	BOOL all_same = TRUE;
-	S32 num_tes = getNumTEs();
-
-	LLViewerImage *first_texturep = getTEImage(0);
-	if (!first_texturep)
-	{
-		return FALSE;
-	}
-
-	const LLTextureEntry *tep = getTE(0);
-	if (!tep)
-	{
-		llwarns << "Volume with zero textures!" << llendl;
-		return FALSE;
-	}
+//----------------------------------------------------------------------------
 
-	if (tep->getColor().mV[3] != 1.f)
-	{
-		is_alpha = TRUE;
-	}
-	const LLColor4 first_color = tep->getColor();
-	const U8 first_bump = tep->getBumpShinyFullbright();
-	const U8 first_media_flags = tep->getMediaTexGen();
-
-	if (first_texturep->getComponents() == 4)
-	{
-		is_alpha = TRUE;
-	}
-
-	F32 s_scale, t_scale;
-	tep->getScale(&s_scale, &t_scale);
-
-	for (S32 f = 1; f < num_tes; f++)
-	{
-		LLViewerImage *texturep = getTEImage(f);
-		if (texturep != first_texturep)
-		{
-			all_same = FALSE;
-			break;
-		}
-
-		tep = getTE(f);
-
-		if( tep->getBumpShinyFullbright() != first_bump )
-		{
-			all_same = FALSE;
-			break;
-		}
-
-		if (first_bump)
-		{
-			F32 cur_s, cur_t;
-			tep->getScale(&cur_s, &cur_t);
-			if ((cur_s != s_scale) || (cur_t != t_scale))
-			{
-				all_same = FALSE;
-				break;
-			}
-		}
-
-		if ((texturep->getComponents() == 4) || (tep->getColor().mV[3] != 1.f))
-		{
-			if (!is_alpha)
-			{
-				all_same = FALSE;
-				break;
-			}
-		}
-		else if (is_alpha)
-		{
-			all_same = FALSE;
-			break;
-		}
-
-		if (tep->getColor() != first_color)
-		{
-			all_same = FALSE;
-			break;
-		}
-
-		if (tep->getMediaTexGen() != first_media_flags)
-		{
-			all_same = FALSE;
-			break;
-		}
-	}
-
-	mAllTEsSame = all_same;
-	if (was_same != all_same)
-	{
-		gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_ALL, TRUE); // rebuild NOW
-		mFaceMappingChanged = TRUE;
-	}
-	return mAllTEsSame;
-}
-
-void LLVOVolume::setupSingleFace(S32 face_offset)
-{
-	S32 num_indices = 0;
-	S32 num_vertices = 0;
-
-	if (mDrawable.isNull())
-	{
-		llerrs << "setupSingleFace called with NULL mDrawable" << llendl;
-	}
-	if (face_offset >= mDrawable->getNumFaces())
-	{
-		llerrs << "setupSingleFace called with invalid face_offset" << llendl;
-	}
-	
-	const S32 num_faces = getVolume()->getNumFaces();
-	for (S32 i = 0; i < num_faces; i++)
-	{
-		const LLVolumeFace &vf = getVolume()->getVolumeFace(i);
-		num_vertices += vf.mVertices.size();
-		num_indices += vf.mIndices.size();
-	}
-	LLFace *facep = mDrawable->getFace(face_offset);
-	facep->setSize(num_vertices, num_indices);
-}
-
-//----------------------------------------------------------------------------
-
-void LLVOVolume::setIsLight(BOOL is_light)
-{
-	if (is_light != getIsLight())
+void LLVOVolume::setIsLight(BOOL is_light)
+{
+	if (is_light != getIsLight())
 	{
 		if (is_light)
 		{
@@ -1532,7 +1260,7 @@ F32 LLVOVolume::calcLightAtPoint(const LLVector3& pos, const LLVector3& norm, LL
 	LLVector3 light_dir = light_pos - pos;
 	F32 dist = light_dir.normVec();
 	F32 dp = norm * light_dir;
-	if ((gPipeline.getVertexShaderLevel(LLPipeline::SHADER_OBJECT) >= LLDrawPoolSimple::SHADER_LEVEL_LOCAL_LIGHTS))
+	if ((gPipeline.getLightingDetail() > 2))
 	{
 		if (dp <= 0)
 		{
@@ -1571,7 +1299,7 @@ F32 LLVOVolume::calcLightAtPoint(const LLVector3& pos, const LLVector3& norm, LL
 BOOL LLVOVolume::updateLighting(BOOL do_lighting)
 {
 	LLMemType mt1(LLMemType::MTYPE_DRAWABLE);
-
+#if 0
 	if (mDrawable->isStatic())
 	{
 		do_lighting = FALSE;
@@ -1581,31 +1309,31 @@ BOOL LLVOVolume::updateLighting(BOOL do_lighting)
 	const LLMatrix3& mat_normal = LLMatrix3(mDrawable->getWorldRotation());
 	
 	LLVolume* volume = getVolume();
-	if (getAllTEsSame())
+
+	for (S32 i = 0; i < volume->getNumFaces(); i++)
 	{
-		LLFace *face = mDrawable->getFace(mFaceIndexOffset);
-		S32 num_faces = volume->getNumFaces();
+		LLFace *face = mDrawable->getFace(i);
 		if (face && face->getGeomCount())
 		{
-			face->genLighting(volume, mDrawable, 0, num_faces-1, mat_vert, mat_normal, do_lighting);
-		}
-	}
-	else
-	{
-		for (S32 i = 0; i < volume->getNumFaces(); i++)
-		{
-			LLFace *face = mDrawable->getFace(i + mFaceIndexOffset);
-			if (face && face->getGeomCount())
-			{
-				face->genLighting(volume, mDrawable, i, i, mat_vert, mat_normal, do_lighting);
-			}
+			face->genLighting(volume, mDrawable, i, i, mat_vert, mat_normal, do_lighting);
 		}
 	}		
+#endif
 	return TRUE;
 }
 
 //----------------------------------------------------------------------------
 
+U32 LLVOVolume::getVolumeInterfaceID() const
+{
+	if (mVolumeImpl)
+	{
+		return mVolumeImpl->getID();
+	}
+
+	return 0;
+}
+
 BOOL LLVOVolume::isFlexible() const
 {
 	if (getParameterEntryInUse(LLNetworkData::PARAMS_FLEXIBLE))
@@ -1696,16 +1424,18 @@ void LLVOVolume::generateSilhouette(LLSelectNode* nodep, const LLVector3& view_p
 		LLVector3 view_vector;
 		view_vector = view_point; 
 
+		//transform view vector into volume space
+		view_vector -= getRenderPosition();
+		mDrawable->mDistanceWRTCamera = view_vector.magVec();
+		LLQuaternion worldRot = getRenderRotation();
+		view_vector = view_vector * ~worldRot;
 		if (!isVolumeGlobal())
-		{	//transform view vector into volume space
-			view_vector -= getRenderPosition();
-			LLQuaternion worldRot = getRenderRotation();
-			view_vector = view_vector * ~worldRot;
+		{
 			LLVector3 objScale = getScale();
 			LLVector3 invObjScale(1.f / objScale.mV[VX], 1.f / objScale.mV[VY], 1.f / objScale.mV[VZ]);
 			view_vector.scaleVec(invObjScale);
 		}
-
+		
 		updateRelativeXform();
 		volume->generateSilhouetteVertices(nodep->mSilhouetteVertices, nodep->mSilhouetteNormals, nodep->mSilhouetteSegments, view_vector, mRelativeXform, mRelativeXformInvTrans);
 
@@ -1713,33 +1443,15 @@ void LLVOVolume::generateSilhouette(LLSelectNode* nodep, const LLVector3& view_p
 	}
 }
 
-void LLVOVolume::deleteFaces(LLVOVolume* childp)
+void LLVOVolume::deleteFaces()
 {
-	S32 face_count = childp->mNumFaces;
-	S32 start_index = childp->mFaceIndexOffset;
+	S32 face_count = mNumFaces;
 	if (mDrawable.notNull())
 	{
-		mDrawable->deleteFaces(start_index, face_count);
-	}
-	if (mFaceIndexOffset > start_index)
-	{
-		mFaceIndexOffset -= face_count;
+		mDrawable->deleteFaces(0, face_count);
 	}
 
-	for (U32 i = 0; i < mChildList.size(); i++)
-	{
-		LLViewerObject* siblingp = mChildList[i];
-		if (siblingp != childp)
-		{
-			if (siblingp->getPCode() == LL_PCODE_VOLUME && 
-				((LLVOVolume*)siblingp)->mFaceIndexOffset > start_index)
-			{
-				((LLVOVolume*)siblingp)->mFaceIndexOffset -= face_count;
-			}
-		}
-	}
-	childp->mFaceIndexOffset = 0;
-	childp->mNumFaces = 0;
+	mNumFaces = 0;
 }
 
 void LLVOVolume::updateRadius()
@@ -1787,7 +1499,8 @@ const LLMatrix4 LLVOVolume::getRenderMatrix() const
 
 void LLVOVolume::writeCAL3D(apr_file_t* fp, std::string& path, std::string& file_base, S32 joint_num, LLVector3& pos, LLQuaternion& rot, S32& material_index, S32& texture_index, std::multimap<LLUUID, LLMaterialExportInfo*>& material_map)
 {
-	LLPointer<LLImageTGA> tga_image = new LLImageTGA;
+#if 0
+	LLImageTGA tga_image;
 
 	if (mDrawable.isNull())
 	{
@@ -1868,10 +1581,10 @@ void LLVOVolume::writeCAL3D(apr_file_t* fp, std::string& path, std::string& file
 					llinfos << "No image data available for " << filename << llendl;
 					continue;
 				}
-				LLPointer<LLImageRaw> raw_image = new LLImageRaw;
+				LLImageRaw raw_image;
 				imagep->readBackRaw(-1, raw_image);
-				BOOL success = tga_image->encode(raw_image);
-				success = tga_image->save(filename);
+				BOOL success = tga_image.encode(raw_image);
+				success = tga_image.save(filename);
 			}
 
 			material_info = new LLMaterialExportInfo(my_material, my_texture, face_color);
@@ -1912,6 +1625,7 @@ void LLVOVolume::writeCAL3D(apr_file_t* fp, std::string& path, std::string& file
 	{
 		((LLVOVolume*)(LLViewerObject*)mChildList[i])->writeCAL3D(fp, path, file_base, joint_num, final_pos, final_rot, material_index, texture_index, material_map);
 	}
+#endif
 }
 
 //static
@@ -1942,10 +1656,62 @@ void LLVOVolume::parameterChanged(U16 param_type, LLNetworkData* data, BOOL in_u
 	}
 }
 
+void LLVOVolume::setSelected(BOOL sel)
+{
+	LLViewerObject::setSelected(sel);
+	if (mDrawable.notNull())
+	{
+		mDrawable->movePartition();
+	}
+}
+
 void LLVOVolume::updateSpatialExtents(LLVector3& newMin, LLVector3& newMax)
 {		
 }
 
+F32 LLVOVolume::getBinRadius()
+{
+	F32 radius;
+	
+	const LLVector3* ext = mDrawable->getSpatialExtents();
+	
+	BOOL shrink_wrap = mDrawable->isAnimating();
+	BOOL alpha_wrap = FALSE;
+	//if (!shrink_wrap)
+	{
+		for (S32 i = 0; i < mDrawable->getNumFaces(); i++)
+		{
+			if (mDrawable->getFace(i)->getPoolType() == LLDrawPool::POOL_ALPHA)
+			{
+				alpha_wrap = TRUE;
+				break;
+			}
+		}
+	}
+
+	if (alpha_wrap)
+	{
+		LLVector3 bounds = getScale();
+		radius = llmin(bounds.mV[1], bounds.mV[2]);
+		radius = llmin(radius, bounds.mV[0]);
+		radius *= 0.5f;
+	}
+	else if (shrink_wrap)
+	{
+		radius = (ext[1]-ext[0]).magVec()*0.5f;
+	}
+	else if (mDrawable->isStatic())
+	{
+		radius = 32.f;
+	}
+	else
+	{
+		radius = 8.f;
+	}
+
+	return llclamp(radius, 0.5f, 256.f);
+}
+
 const LLVector3 LLVOVolume::getPivotPositionAgent() const
 {
 	if (mVolumeImpl)
@@ -1961,6 +1727,8 @@ void LLVOVolume::onShift(const LLVector3 &shift_vector)
 	{
 		mVolumeImpl->onShift(shift_vector);
 	}
+
+	updateRelativeXform();
 }
 
 const LLMatrix4& LLVOVolume::getWorldMatrix(LLXformMatrix* xform) const
@@ -1974,14 +1742,9 @@ const LLMatrix4& LLVOVolume::getWorldMatrix(LLXformMatrix* xform) const
 
 LLVector3 LLVOVolume::agentPositionToVolume(const LLVector3& pos) const
 {
-	if (isVolumeGlobal())
-	{
-		return pos;
-	}
-	
 	LLVector3 ret = pos - getRenderPosition();
 	ret = ret * ~getRenderRotation();
-	LLVector3 objScale = getScale();
+	LLVector3 objScale = isVolumeGlobal() ? LLVector3(1,1,1) : getScale();
 	LLVector3 invObjScale(1.f / objScale.mV[VX], 1.f / objScale.mV[VY], 1.f / objScale.mV[VZ]);
 	ret.scaleVec(invObjScale);
 	
@@ -1990,7 +1753,7 @@ LLVector3 LLVOVolume::agentPositionToVolume(const LLVector3& pos) const
 
 LLVector3 LLVOVolume::agentDirectionToVolume(const LLVector3& dir) const
 {
-	return isVolumeGlobal() ? dir : (dir * ~getRenderRotation());
+	return dir * ~getRenderRotation();
 }
 
 LLVector3 LLVOVolume::volumePositionToAgent(const LLVector3& dir) const
@@ -2005,6 +1768,9 @@ LLVector3 LLVOVolume::volumePositionToAgent(const LLVector3& dir) const
 
 BOOL LLVOVolume::lineSegmentIntersect(const LLVector3& start, LLVector3& end) const
 {
+	return FALSE;
+	
+#if 0 // needs to be rewritten to use face extents instead of volume bounds
 	LLVolume* volume = getVolume();
 	BOOL ret = FALSE;
 	if (volume)
@@ -2024,4 +1790,538 @@ BOOL LLVOVolume::lineSegmentIntersect(const LLVector3& start, LLVector3& end) co
 		}
 	}
 	return ret;
+#endif
+}
+
+U32 LLVOVolume::getPartitionType() const
+{
+	if (isHUDAttachment())
+	{
+		return LLPipeline::PARTITION_HUD;
+	}
+
+	return LLPipeline::PARTITION_VOLUME;
+}
+
+LLVolumePartition::LLVolumePartition()
+: LLSpatialPartition(LLVOVolume::VERTEX_DATA_MASK, FALSE)
+{
+	mLODPeriod = 16;
+	mDepthMask = FALSE;
+	mDrawableType = LLPipeline::RENDER_TYPE_VOLUME;
+	mPartitionType = LLPipeline::PARTITION_VOLUME;
+	mSlopRatio = 0.25f;
+	mBufferUsage = GL_DYNAMIC_DRAW_ARB;
+	mImageEnabled = TRUE;
+}
+
+LLVolumeBridge::LLVolumeBridge(LLDrawable* drawablep)
+: LLSpatialBridge(drawablep, LLVOVolume::VERTEX_DATA_MASK)
+{
+	mDepthMask = FALSE;
+	mLODPeriod = 16;
+	mDrawableType = LLPipeline::RENDER_TYPE_VOLUME;
+	mPartitionType = LLPipeline::PARTITION_BRIDGE;
+	
+	mBufferUsage = GL_DYNAMIC_DRAW_ARB;
+
+	mSlopRatio = 0.25f;
+}
+
+void LLVolumeGeometryManager::registerFace(LLSpatialGroup* group, LLFace* facep, U32 type)
+{
+	LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+
+	if (facep->getViewerObject()->isSelected() && gHideSelectedObjects)
+	{
+		return;
+	}
+
+	//add face to drawmap
+	std::vector<LLDrawInfo*>& draw_vec = group->mDrawMap[type];
+
+	S32 idx = draw_vec.size()-1;
+
+
+	BOOL fullbright = (type == LLRenderPass::PASS_FULLBRIGHT ||
+					  type == LLRenderPass::PASS_ALPHA) ? facep->isState(LLFace::FULLBRIGHT) : FALSE;
+	BOOL texanim = (type == LLRenderPass::PASS_SHINY) ? FALSE : facep->isState(LLFace::TEXTURE_ANIM);
+	U8 bump = (type == LLRenderPass::PASS_BUMP ? facep->getTextureEntry()->getBumpmap() : 0);
+
+	const LLMatrix4* tex_mat = NULL;
+	if (texanim)
+	{
+		LLVOVolume* volume = (LLVOVolume*) facep->getViewerObject();
+		tex_mat = volume->getTextureMatrix();
+	}
+
+	//LLViewerImage* tex = facep->mAppAngle < FORCE_SIMPLE_RENDER_ANGLE ? NULL : facep->getTexture();
+	LLViewerImage* tex = facep->getTexture();
+
+	if (idx >= 0 && 
+		draw_vec[idx]->mVertexBuffer == facep->mVertexBuffer &&
+		draw_vec[idx]->mEnd == facep->getGeomIndex()-1 &&
+		draw_vec[idx]->mTexture == tex &&
+		//draw_vec[idx]->mEnd - draw_vec[idx]->mStart + facep->getGeomCount() <= (U32) gGLManager.mGLMaxVertexRange &&
+		//draw_vec[idx]->mCount + facep->getIndicesCount() <= (U32) gGLManager.mGLMaxIndexRange &&
+		draw_vec[idx]->mFullbright == fullbright &&
+		draw_vec[idx]->mBump == bump &&
+		draw_vec[idx]->mTextureMatrix == tex_mat)
+	{
+		draw_vec[idx]->mCount += facep->getIndicesCount();
+		draw_vec[idx]->mEnd += facep->getGeomCount();
+		draw_vec[idx]->mVSize = llmax(draw_vec[idx]->mVSize, facep->getVirtualSize());
+		validate_draw_info(*draw_vec[idx]);
+	}
+	else
+	{
+		U32 start = facep->getGeomIndex();
+		U32 end = start + facep->getGeomCount()-1;
+		U32 offset = facep->getIndicesStart();
+		U32 count = facep->getIndicesCount();
+		LLDrawInfo* draw_info = new LLDrawInfo(start,end,count,offset,tex, 
+			facep->mVertexBuffer, fullbright, bump); 
+		draw_info->mVSize = facep->getVirtualSize();
+		draw_vec.push_back(draw_info);
+		draw_info->mReflectionMap = group->mReflectionMap;
+		draw_info->mTextureMatrix = tex_mat;
+		validate_draw_info(*draw_info);
+	}
+}
+
+void LLVolumeGeometryManager::getGeometry(LLSpatialGroup* group)
+{
+
+}
+
+void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
+{
+	if (group->changeLOD())
+	{
+		group->mLastUpdateDistance = group->mDistance;
+	}
+
+	group->mLastUpdateViewAngle = group->mViewAngle;
+
+	if (!group->isState(LLSpatialGroup::GEOM_DIRTY |
+						LLSpatialGroup::ALPHA_DIRTY))
+	{
+		return;
+	}
+
+	group->mBuilt = 1.f;
+	LLFastTimer ftm(LLFastTimer::FTM_REBUILD_VBO);	
+
+	LLFastTimer ftm2(LLFastTimer::FTM_REBUILD_VOLUME_VB);
+
+	//find reflection map
+	if (group->mSpatialPartition->mImageEnabled)
+	{
+		if (group->mReflectionMap.isNull())
+		{
+			LLSpatialGroup* parent = group->getParent();
+			while (parent && group->mReflectionMap.isNull())
+			{
+				group->mReflectionMap = parent->mReflectionMap;
+				parent = parent->getParent();
+			}
+		}
+	}
+
+	group->clearDrawMap();
+
+	mFaceList.clear();
+
+	std::vector<LLFace*> alpha_faces;
+	U32 vertex_count = 0;
+	U32 index_count = 0;
+	U32 useage = group->mSpatialPartition->mBufferUsage;
+
+	//get all the faces into a list, putting alpha faces in their own list
+	for (LLSpatialGroup::element_iter drawable_iter = group->getData().begin(); drawable_iter != group->getData().end(); ++drawable_iter)
+	{
+		LLDrawable* drawablep = *drawable_iter;
+		
+		if (drawablep->isDead())
+		{
+			continue;
+		}
+	
+		if (drawablep->isAnimating())
+		{ //fall back to stream draw for animating verts
+			useage = GL_STREAM_DRAW_ARB;
+		}
+
+		LLVOVolume* vobj = drawablep->getVOVolume();
+
+		//for each face
+		for (S32 i = 0; i < drawablep->getNumFaces(); i++)
+		{
+			//sum up face verts and indices
+			drawablep->updateFaceSize(i);
+			LLFace* facep = drawablep->getFace(i);
+			if (facep->hasGeometry() && facep->mPixelArea > FORCE_CULL_AREA)
+			{
+				const LLTextureEntry* te = facep->getTextureEntry();
+				LLViewerImage* tex = facep->getTexture();
+
+				BOOL force_simple = (facep->mPixelArea < FORCE_SIMPLE_RENDER_AREA);
+				U32 type = gPipeline.getPoolTypeFromTE(te, tex);
+				if (type != LLDrawPool::POOL_ALPHA && force_simple)
+				{
+					type = LLDrawPool::POOL_SIMPLE;
+				}
+				facep->setPoolType(type);
+
+				if (vobj->isHUDAttachment())
+				{
+					facep->setState(LLFace::FULLBRIGHT);
+				}
+
+				if (vobj->mTextureAnimp)
+				{
+					if (vobj->mTextureAnimp->mFace <= -1)
+					{
+						S32 face;
+						for (face = 0; face < vobj->getNumTEs(); face++)
+						{
+							if (vobj->mTextureAnimp->mMode & LLViewerTextureAnim::ON)
+							{
+								drawablep->getFace(face)->setState(LLFace::TEXTURE_ANIM);
+							}
+							else
+							{
+								drawablep->getFace(face)->clearState(LLFace::TEXTURE_ANIM);
+							}
+						}
+					}
+					else if (vobj->mTextureAnimp->mFace < vobj->getNumTEs())
+					{
+						drawablep->getFace(vobj->mTextureAnimp->mFace)->setState(LLFace::TEXTURE_ANIM);
+					}
+				}
+
+				if (type == LLDrawPool::POOL_ALPHA)
+				{
+					vertex_count += facep->getGeomCount();
+					index_count += facep->getIndicesCount();
+					alpha_faces.push_back(facep);
+				}
+				else
+				{
+					if (drawablep->isState(LLDrawable::REBUILD_VOLUME))
+					{
+						facep->mLastUpdateTime = gFrameTimeSeconds;
+					}
+					mFaceList.push_back(facep);
+				}
+			}
+			else
+			{	//face has no renderable geometry
+				facep->mVertexBuffer = NULL;
+				facep->mLastVertexBuffer = NULL;
+				//don't alpha wrap drawables that have only tiny tiny alpha faces
+				facep->setPoolType(LLDrawPool::POOL_SIMPLE);
+			}
+
+			vobj->updateTextures();
+		}
+	}
+
+	group->mVertexCount = vertex_count;
+	group->mIndexCount = index_count;
+	group->mBufferUsage = useage;
+
+	LLStrider<LLVector3> vertices;
+	LLStrider<LLVector3> normals;
+	LLStrider<LLVector2> texcoords2;
+	LLStrider<LLVector2> texcoords;
+	LLStrider<LLColor4U> colors;
+	LLStrider<U32> indices;
+
+	//PROCESS NON-ALPHA FACES
+	{
+		//sort faces by texture
+		std::sort(mFaceList.begin(), mFaceList.end(), LLFace::CompareTextureAndTime());
+		
+		std::vector<LLFace*>::iterator face_iter = mFaceList.begin();
+		
+		LLSpatialGroup::buffer_map_t buffer_map;
+
+		while (face_iter != mFaceList.end())
+		{
+			//pull off next face
+			LLFace* facep = *face_iter;
+			LLViewerImage* tex = facep->getTexture();
+
+			U32 index_count = facep->getIndicesCount();
+			U32 geom_count = facep->getGeomCount();
+
+			//sum up vertices needed for this texture
+			std::vector<LLFace*>::iterator i = face_iter;
+			++i;
+			while (i != mFaceList.end() && (*i)->getTexture() == tex)
+			{
+				facep = *i;
+				++i;
+				index_count += facep->getIndicesCount();
+				geom_count += facep->getGeomCount();
+			}
+		
+			//create/delete/resize vertex buffer if needed
+			LLVertexBuffer* buffer = NULL;
+			LLSpatialGroup::buffer_map_t::iterator found_iter = group->mBufferMap.find(tex);
+			if (found_iter != group->mBufferMap.end())
+			{
+				buffer = found_iter->second;
+			}
+						
+			if (!buffer)
+			{ //create new buffer if needed
+				buffer = createVertexBuffer(group->mSpatialPartition->mVertexDataMask, 
+												group->mBufferUsage);
+				buffer->allocateBuffer(geom_count, index_count, TRUE);
+			}
+			else 
+			{
+				if (LLVertexBuffer::sEnableVBOs && buffer->getUsage() != group->mBufferUsage)
+				{
+					buffer = createVertexBuffer(group->mSpatialPartition->mVertexDataMask, 
+												group->mBufferUsage);
+					buffer->allocateBuffer(geom_count, index_count, TRUE);
+				}
+				else
+				{
+					buffer->resizeBuffer(geom_count, index_count);
+				}
+			}
+
+			BOOL clean = TRUE;
+			buffer_map[tex] = buffer;
+
+			//add face geometry
+		
+			//get vertex buffer striders
+			buffer->getVertexStrider(vertices);
+			buffer->getNormalStrider(normals);
+			buffer->getTexCoordStrider(texcoords);
+			buffer->getTexCoord2Strider(texcoords2);
+			buffer->getColorStrider(colors);
+			buffer->getIndexStrider(indices);
+
+			U32 indices_index = 0;
+			U32 index_offset = 0;
+
+			while (face_iter < i)
+			{
+				facep = *face_iter;
+				LLDrawable* drawablep = facep->getDrawable();
+				LLVOVolume* vobj = drawablep->getVOVolume();
+				LLVolume* volume = vobj->getVolume();
+
+				U32 te_idx = facep->getTEOffset();
+				facep->mIndicesIndex = indices_index;
+				facep->mGeomIndex = index_offset;
+				facep->mVertexBuffer = buffer;
+				{
+					if (facep->getGeometryVolume(*volume, te_idx, vertices, normals, texcoords, texcoords2, colors, indices, 
+						vobj->getRelativeXform(), vobj->getRelativeXformInvTrans(), index_offset))
+					{
+						clean = FALSE;
+						buffer->markDirty(facep->getGeomIndex(), facep->getGeomCount(), 
+							facep->getIndicesStart(), facep->getIndicesCount());
+					}
+				}
+
+				indices_index += facep->mIndicesCount;
+
+				BOOL force_simple = facep->mPixelArea < FORCE_SIMPLE_RENDER_AREA;
+				BOOL fullbright = facep->isState(LLFace::FULLBRIGHT);
+				const LLTextureEntry* te = facep->getTextureEntry();
+
+				if (tex->getPrimaryFormat() == GL_ALPHA)
+				{
+					registerFace(group, facep, LLRenderPass::PASS_INVISIBLE);
+				}
+				else if (fullbright)
+				{
+					registerFace(group, facep, LLRenderPass::PASS_FULLBRIGHT);
+				}
+				else
+				{
+					registerFace(group, facep, LLRenderPass::PASS_SIMPLE);
+				}
+
+				facep->setPoolType(LLDrawPool::POOL_SIMPLE);
+
+				if (te->getShiny())
+				{
+					registerFace(group, facep, LLRenderPass::PASS_SHINY);
+				}
+
+				if (!force_simple && te->getBumpmap())
+				{
+					registerFace(group, facep, LLRenderPass::PASS_BUMP);
+				}
+				
+				++face_iter;
+			}
+
+			if (clean)
+			{
+				buffer->markClean();
+			}
+		}
+
+		group->mBufferMap.clear();
+		for (LLSpatialGroup::buffer_map_t::iterator i = buffer_map.begin(); i != buffer_map.end(); ++i)
+		{
+			group->mBufferMap[i->first] = i->second;
+		}
+	}
+
+	//PROCESS ALPHA FACES
+	if (!alpha_faces.empty())
+	{
+		//sort alpha faces by distance
+		std::sort(alpha_faces.begin(), alpha_faces.end(), LLFace::CompareDistanceGreater());
+
+		//store alpha faces in root vertex buffer
+		if (group->mVertexBuffer.isNull() || (LLVertexBuffer::sEnableVBOs && group->mBufferUsage != group->mVertexBuffer->getUsage()))
+		{
+			group->mVertexBuffer = createVertexBuffer(group->mSpatialPartition->mVertexDataMask, 
+													  group->mBufferUsage);
+			group->mVertexBuffer->allocateBuffer(group->mVertexCount, group->mIndexCount, true);
+			stop_glerror();
+		}
+		else
+		{
+			group->mVertexBuffer->resizeBuffer(group->mVertexCount, group->mIndexCount);
+			stop_glerror();
+		}
+
+		//get vertex buffer striders
+		LLVertexBuffer* buffer = group->mVertexBuffer;
+
+		BOOL clean = TRUE;
+
+		buffer->getVertexStrider(vertices);
+		buffer->getNormalStrider(normals);
+		buffer->getTexCoordStrider(texcoords);
+		buffer->getTexCoord2Strider(texcoords2);
+		buffer->getColorStrider(colors);
+		buffer->getIndexStrider(indices);
+
+		U32 index_offset = 0;
+		U32 indices_index = 0;
+
+		for (std::vector<LLFace*>::iterator i = alpha_faces.begin(); i != alpha_faces.end(); ++i)
+		{
+			LLFace* facep = *i;
+			LLDrawable* drawablep = facep->getDrawable();
+			LLVOVolume* vobj = drawablep->getVOVolume();
+			LLVolume* volume = vobj->getVolume();
+
+			U32 te_idx = facep->getTEOffset();
+			facep->mIndicesIndex = indices_index;
+			facep->mGeomIndex = index_offset;
+			facep->mVertexBuffer = group->mVertexBuffer;
+			if (facep->getGeometryVolume(*volume, te_idx, vertices, normals, texcoords, texcoords2, colors, indices, 
+				vobj->getRelativeXform(), vobj->getRelativeXformInvTrans(), index_offset))
+			{
+				clean = FALSE;
+				buffer->markDirty(facep->getGeomIndex(), facep->getGeomCount(), 
+					facep->getIndicesStart(), facep->getIndicesCount());
+			}
+
+			indices_index += facep->mIndicesCount;
+
+			registerFace(group, facep, LLRenderPass::PASS_ALPHA);
+		}
+
+		if (clean)
+		{
+			buffer->markClean();
+		}
+	}
+	else
+	{
+		group->mVertexBuffer = NULL;
+	}
+
+	//get all the faces into a list, putting alpha faces in their own list
+	for (LLSpatialGroup::element_iter drawable_iter = group->getData().begin(); drawable_iter != group->getData().end(); ++drawable_iter)
+	{
+		LLDrawable* drawablep = *drawable_iter;
+		drawablep->clearState(LLDrawable::REBUILD_ALL);
+	}
+
+	group->mLastUpdateTime = gFrameTimeSeconds;
+	group->clearState(LLSpatialGroup::GEOM_DIRTY | LLSpatialGroup::MATRIX_DIRTY |
+						LLSpatialGroup::ALPHA_DIRTY);
+
+	mFaceList.clear();
 }
+
+void LLGeometryManager::addGeometryCount(LLSpatialGroup* group, U32 &vertex_count, U32 &index_count)
+{	
+	//initialize to default usage for this partition
+	U32 usage = group->mSpatialPartition->mBufferUsage;
+	
+	//clear off any old faces
+	mFaceList.clear();
+
+	//for each drawable
+	for (LLSpatialGroup::element_iter drawable_iter = group->getData().begin(); drawable_iter != group->getData().end(); ++drawable_iter)
+	{
+		LLDrawable* drawablep = *drawable_iter;
+		
+		if (drawablep->isDead())
+		{
+			continue;
+		}
+	
+		if (drawablep->isAnimating())
+		{ //fall back to stream draw for animating verts
+			usage = GL_STREAM_DRAW_ARB;
+		}
+
+		//for each face
+		for (S32 i = 0; i < drawablep->getNumFaces(); i++)
+		{
+			//sum up face verts and indices
+			drawablep->updateFaceSize(i);
+			LLFace* facep = drawablep->getFace(i);
+			if (facep->hasGeometry() && facep->mPixelArea > FORCE_CULL_AREA)
+			{
+				vertex_count += facep->getGeomCount();
+				index_count += facep->getIndicesCount();
+
+				//remember face (for sorting)
+				mFaceList.push_back(facep);
+			}
+			else
+			{
+				facep->mVertexBuffer = NULL;
+				facep->mLastVertexBuffer = NULL;
+			}
+		}
+	}
+	
+	group->mBufferUsage = usage;
+}
+
+LLHUDPartition::LLHUDPartition()
+{
+	mPartitionType = LLPipeline::PARTITION_HUD;
+	mDrawableType = LLPipeline::RENDER_TYPE_HUD;
+	mSlopRatio = 0.f;
+	mLODPeriod = 16;
+}
+
+void LLHUDPartition::shift(const LLVector3 &offset)
+{
+	//HUD objects don't shift with region crossing.  That would be silly.
+}
+
+
diff --git a/indra/newview/llvovolume.h b/indra/newview/llvovolume.h
index 26764d62c99ead308464255528b02f15c1fd432e..f36c367f52c03e1d8ceaf84d20ec3d0edda43259 100644
--- a/indra/newview/llvovolume.h
+++ b/indra/newview/llvovolume.h
@@ -10,6 +10,7 @@
 #define LL_LLVOVOLUME_H
 
 #include "llviewerobject.h"
+#include "llspatialpartition.h"
 #include "llviewerimage.h"
 #include "llframetimer.h"
 #include "llapr.h"
@@ -41,7 +42,8 @@ public:
 	virtual bool isVolumeGlobal() const = 0; // Are we in global space?
 	virtual bool isActive() const = 0; // Is this object currently active?
 	virtual const LLMatrix4& getWorldMatrix(LLXformMatrix* xform) const = 0;
-	virtual void updateRelativeXform(BOOL global_volume = FALSE) = 0;
+	virtual void updateRelativeXform() = 0;
+	virtual U32 getID() const = 0;
 };
 
 // Class which embodies all Volume objects (with pcode LL_PCODE_VOLUME)
@@ -50,17 +52,24 @@ class LLVOVolume : public LLViewerObject
 public:
 	static		void	initClass();
 	static 		void 	preUpdateGeom();
-	static		F32		getTextureVirtualSize(const LLFace* face);
-	
-	BOOL mWereAllTEsSame;
 	
+	enum 
+	{
+		VERTEX_DATA_MASK =	(1 << LLVertexBuffer::TYPE_VERTEX) |
+							(1 << LLVertexBuffer::TYPE_NORMAL) |
+							(1 << LLVertexBuffer::TYPE_TEXCOORD) |
+							(1 << LLVertexBuffer::TYPE_TEXCOORD2) |
+							(1 << LLVertexBuffer::TYPE_COLOR)
+	}
+	eVertexDataMask;
+
 public:
 						LLVOVolume(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp);
 	virtual				~LLVOVolume();
 
 	/*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline);
 
-				void	deleteFaces(LLVOVolume* childp);
+				void	deleteFaces();
 
 	/*virtual*/ BOOL	idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time);
 
@@ -71,12 +80,12 @@ public:
 
 				void	generateSilhouette(LLSelectNode* nodep, const LLVector3& view_point);
 
-				BOOL	getAllTEsSame() const					{ return mAllTEsSame; }
 				F32		getIndividualRadius()					{ return mRadius; }
 				S32		getLOD() const							{ return mLOD; }
 	const LLVector3		getPivotPositionAgent() const;
 	const LLMatrix4&	getRelativeXform() const				{ return mRelativeXform; }
 	const LLMatrix3&	getRelativeXformInvTrans() const		{ return mRelativeXformInvTrans; }
+	const LLMatrix4*	getTextureMatrix() const				{ return &mTextureMatrix; }
 	/*virtual*/	const LLMatrix4	getRenderMatrix() const;
 
 	/*virtual*/ BOOL	lineSegmentIntersect(const LLVector3& start, LLVector3& end) const;
@@ -86,6 +95,7 @@ public:
 
 				
 				BOOL	getVolumeChanged() const				{ return mVolumeChanged; }
+				F32		getTextureVirtualSize(LLFace* face);
 	/*virtual*/ F32  	getRadius() const						{ return mVObjRadius; };
 				const LLMatrix4& getWorldMatrix(LLXformMatrix* xform) const;
 
@@ -101,6 +111,7 @@ public:
 											U32 block_num, const EObjectUpdateType update_type,
 											LLDataPacker *dp);
 
+	/*virtual*/ void	setSelected(BOOL sel);
 	/*virtual*/ BOOL	setDrawableParent(LLDrawable* parentp);
 
 	/*virtual*/ void	setScale(const LLVector3 &scale, BOOL damped);
@@ -121,17 +132,19 @@ public:
 				void	setTexture(const S32 face);
 
 	/*virtual*/ BOOL	setVolume(const LLVolumeParams &volume_params, const S32 detail, bool unique_volume = false);
-				void	updateRelativeXform(BOOL global_volume = FALSE);
+				void	updateRelativeXform();
 	/*virtual*/ BOOL	updateGeometry(LLDrawable *drawable);
+	/*virtual*/ void	updateFaceSize(S32 idx);
 	/*virtual*/ BOOL	updateLOD();
 				void	updateRadius();
 	/*virtual*/ void	updateTextures(LLAgent &agent);
-				void	updateTextures(S32 lod);
+				void	updateTextures();
 
 				void	updateFaceFlags();
 				void	regenFaces();
-				BOOL	genTriangles(BOOL force_global);
+				BOOL	genBBoxes(BOOL force_global);
 	virtual		void	updateSpatialExtents(LLVector3& min, LLVector3& max);
+	virtual		F32		getBinRadius();
 	virtual		void	writeCAL3D(apr_file_t* fp, 
 							std::string& path,
 							std::string& file_base,
@@ -142,6 +155,9 @@ public:
 							S32& texture_index, 
 							std::multimap<LLUUID, LLMaterialExportInfo*>& material_map);
 
+
+	virtual U32 getPartitionType() const;
+
 	// For Lights
 	void setIsLight(BOOL is_light);
 	void setLightColor(const LLColor3& color);
@@ -159,6 +175,7 @@ public:
 	F32 getLightDistance(const LLVector3& pos) const; // returns < 0 if inside radius
 
 	// Flexible Objects
+	U32 getVolumeInterfaceID() const;
 	virtual BOOL isFlexible() const;
 	BOOL isVolumeGlobal() const;
 	BOOL canBeFlexible() const;
@@ -169,27 +186,26 @@ public:
 	BOOL updateLighting(BOOL do_lighting);
 
 protected:
-	F32	computeLODProfilePathComplexityBias();
 	S32	computeLODDetail(F32	distance, F32 radius);
 	BOOL calcLOD();
-	void setupSingleFace(S32 face_offset); // Set up the face for combined volumes.
 	LLFace* addFace(S32 face_index);
 	void updateTEData();
-	BOOL calcAllTEsSame();
 
 public:
 	LLViewerTextureAnim *mTextureAnimp;
+	U8 mTexAnimMode;
 
 protected:
 	friend class LLDrawable;
 	
-	BOOL		mAllTEsSame; // All TE's have the same pool/texture
 	BOOL		mFaceMappingChanged;
 	BOOL		mGlobalVolume;
 	BOOL		mInited;
+	LLFrameTimer mTextureUpdateTimer;
 	S32			mLOD;
 	BOOL		mLODChanged;
 	F32			mRadius;
+	LLMatrix4	mTextureMatrix;
 	LLMatrix4	mRelativeXform;
 	LLMatrix3	mRelativeXformInvTrans;
 	BOOL		mVolumeChanged;
@@ -199,8 +215,6 @@ protected:
 	// statics
 public:
 	static F32 sLODSlopDistanceFactor;// Changing this to zero, effectively disables the LOD transition slop 
-	static F32 sLODComplexityDistanceBias;	  // Changing this to zero makes all prims LOD at the same distance, 
-										  // regardless of complexity
 	static F32 sLODFactor;				// LOD scale factor
 	static F32 sDistanceFactor;			// LOD distance factor
 		
diff --git a/indra/newview/llvowater.cpp b/indra/newview/llvowater.cpp
index 135873b5b887e5a67714c7f32a660204b00cf444..abff230c4edb3803f253a20e83e27f3e37c41bd2 100644
--- a/indra/newview/llvowater.cpp
+++ b/indra/newview/llvowater.cpp
@@ -45,22 +45,8 @@ const F32 WAVE_STEP_INV	= (1. / WAVE_STEP);
 
 const F32 g				= 9.81f;          // gravitational constant (m/s^2)
 
-///////////////////////////////////
-
-LLWaterSurface::LLWaterSurface() :
-	mInitialized(FALSE),
-	mWind(9, 0, 0),
-	mA(0.2f),
-	mVisc(0.001f),
-	mShininess(8.0f)
-{}
-
-
-LLWaterGrid *LLVOWater::sGrid = 0;
-
-
 LLVOWater::LLVOWater(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp)
-:	LLViewerObject(id, LL_VO_WATER, regionp)
+:	LLStaticViewerObject(id, LL_VO_WATER, regionp)
 {
 	// Terrain must draw during selection passes so it can block objects behind it.
 	mbCanSelect = FALSE;
@@ -94,18 +80,6 @@ void LLVOWater::updateTextures(LLAgent &agent)
 {
 }
 
-// virtual
-void LLVOWater::updateDrawable(BOOL force_damped)
-{
-	// Force an immediate rebuild on any update
-	if (mDrawable.notNull())
-	{
-		gPipeline.updateMoveNormalAsync(mDrawable);
-		gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_ALL, TRUE);
-	}
-	clearChanged(SHIFTED);
-}
-
 // Never gets called
 BOOL LLVOWater::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
 {
@@ -142,17 +116,13 @@ LLDrawable *LLVOWater::createDrawable(LLPipeline *pipeline)
 
 BOOL LLVOWater::updateGeometry(LLDrawable *drawable)
 {
-	return updateGeometryFlat(drawable);
-}
-
-
-BOOL LLVOWater::updateGeometryFlat(LLDrawable *drawable)
-{
+	LLFastTimer ftm(LLFastTimer::FTM_UPDATE_WATER);
 	LLFace *face;
 
 	if (drawable->getNumFaces() < 1)
 	{
-		drawable->addFace(gPipeline.getPool(LLDrawPool::POOL_WATER), NULL);
+		LLDrawPoolWater *poolp = (LLDrawPoolWater*) gPipeline.getPool(LLDrawPool::POOL_WATER);
+		drawable->addFace(poolp, NULL);
 	}
 	face = drawable->getFace(0);
 
@@ -161,15 +131,26 @@ BOOL LLVOWater::updateGeometryFlat(LLDrawable *drawable)
 
 	LLStrider<LLVector3> verticesp, normalsp;
 	LLStrider<LLVector2> texCoordsp;
-	U32 *indicesp;
+	LLStrider<U32> indicesp;
 	S32 index_offset;
 
 	S32 size = 16;
 
-	S32 num_quads = size*size;
-
-	face->setPrimType(LLTriangles);
-	face->setSize(4*num_quads, 6*num_quads);
+	if (face->mVertexBuffer.isNull())
+	{
+		S32 num_quads = size*size;	
+		face->setSize(4*num_quads, 6*num_quads);
+		
+		face->mVertexBuffer = new LLVertexBuffer(LLDrawPoolWater::VERTEX_DATA_MASK, GL_DYNAMIC_DRAW_ARB);
+		face->mVertexBuffer->allocateBuffer(4*num_quads, 6*num_quads, TRUE);
+		face->setIndicesIndex(0);
+		face->setGeomIndex(0);
+	}
+	else
+	{
+		face->mVertexBuffer->resizeBuffer(face->getGeomCount(), face->getIndicesCount());
+	}
+		
 	index_offset = face->getGeometry(verticesp,normalsp,texCoordsp, indicesp);
 	if (-1 == index_offset)
 	{
@@ -179,6 +160,7 @@ BOOL LLVOWater::updateGeometryFlat(LLDrawable *drawable)
 	LLVector3 position_agent;
 	position_agent = getPositionAgent();
 	face->mCenterAgent = position_agent;
+	face->mCenterLocal = position_agent;
 
 	S32 x, y;
 	F32 step_x = getScale().mV[0] / size;
@@ -237,378 +219,20 @@ BOOL LLVOWater::updateGeometryFlat(LLDrawable *drawable)
 			*indicesp++ = toffset + 2;
 		}
 	}
-
-
-	mDrawable->movePartition();
-	LLPipeline::sCompiles++;
-	return TRUE;
-}
-
-
-BOOL LLVOWater::updateGeometryHeightFieldRoam(LLDrawable *drawable)
-{
-	LLVector3 position_agent = getPositionAgent();
-	const LLVector3 region_size = getScale();
-	const LLVector3 region_origin = position_agent - region_size * 0.5f;
-
-	S32 patch_origx = llround(region_origin.mV[VX] - sGrid->mRegionOrigin.mV[VX]) / sGrid->mRegionWidth;
-	S32 patch_origy = llround(region_origin.mV[VY] - sGrid->mRegionOrigin.mV[VY]) / sGrid->mRegionWidth;
-	S32 patch_dimx = llround(region_size.mV[VX]) / sGrid->mRegionWidth;
-	S32 patch_dimy = llround(region_size.mV[VY]) / sGrid->mRegionWidth;
-
-	static S32 res = (S32)sGrid->mPatchRes;
-	if (patch_origx < 0)
-	{
-		patch_dimx -= - patch_origx;
-		if (patch_dimx < 1)
-		{
-			return TRUE;
-		}
-		patch_origx = 0;
-	}
-	if (patch_origy < 0)
-	{
-		patch_dimy -= - patch_origy;
-		if (patch_dimy < 1)
-		{
-			return TRUE;
-		}
-		patch_origy = 0;
-	}
-	if (patch_origx >= res)
-	{
-		return TRUE;
-	}
-	if (patch_origy >= res)
-	{
-		return TRUE;
-	}
-
-	patch_dimx = llmin<U32>(patch_dimx, res - patch_origx);
-	patch_dimy = llmin<U32>(patch_dimy, res - patch_origy);
-
-	U32 num_of_tris = 0;
-	S32 px, py;
-	for (py = patch_origy; py < patch_origy + patch_dimy; py++)
-	{
-		for (px = patch_origx; px < patch_origx + patch_dimx; px++)
-		{
-			const U32 ind = py * sGrid->mPatchRes + px;
-			if (sGrid->mPatches[ind].visible() && sGrid->mTab[px][py] == 0)
-			{
-				num_of_tris += sGrid->mPatches[ind].numTris();
-				sGrid->mTab[px][py] = this;
-			}
-		}
-	}
-
-	if (num_of_tris == 0)
-	{
-		return TRUE;
-	}
-
-	if (drawable->getNumFaces() < 1)
-	{
-		drawable->addFace((LLDrawPoolWater*) gPipeline.getPool(LLDrawPool::POOL_WATER),
-								gWorldp->getDefaultWaterTexture());
-	}
-
-	LLFace *face;
-
-	face = drawable->getFace(0);
-	face->mCenterAgent = position_agent;
-
-	LLStrider<LLVector3> verticesp, normalsp;
-	LLStrider<LLVector2> texCoordsp;
-	U32 *indicesp;
-	S32 index_offset;
-
-	const F32 water_height = getRegion()->getWaterHeight();
-
 	
-	face->setPrimType(LLTriangles);
-	face->setSize(3 * num_of_tris, 3 * num_of_tris);
-	index_offset = face->getGeometry(verticesp,normalsp,texCoordsp, indicesp);
-	if (-1 == index_offset)
-	{
-		return TRUE;
-	}
-
-	U32 num_of_vtx = 0;
-
-	for (py = patch_origy; py < patch_origy + patch_dimy; py++)
-	{
-		for (px = patch_origx; px < patch_origx + patch_dimx; px++)
-		{
-			for (U8 h = 0; h < 2; h++)
-			{
-				const U32 ind = py * sGrid->mPatchRes + px;
-				if (!sGrid->mPatches[ind].visible() || sGrid->mTab[px][py] != this)
-					continue;
-				LLWaterTri* half = (LLWaterTri*) sGrid->mPatches[ind].half(h);
-				for (const LLWaterTri*  tri = (LLWaterTri*) half->getFirstLeaf();
-										tri != NULL;
-										tri = (LLWaterTri*) tri->getNextLeaf())
-				{
-					/////// check for coordinates
-					*(verticesp++)  = sGrid->vtx(tri->Lvtx(), water_height);
-					*(verticesp++)  = sGrid->vtx(tri->Rvtx(), water_height);
-					*(verticesp++)  = sGrid->vtx(tri->Tvtx(), water_height);
-					
-					*(normalsp++)   = sGrid->norm(tri->Lvtx());
-					*(normalsp++)   = sGrid->norm(tri->Rvtx());
-					*(normalsp++)   = sGrid->norm(tri->Tvtx());
-					
-					*(indicesp++) = index_offset + num_of_vtx + 0;
-					*(indicesp++) = index_offset + num_of_vtx + 1;
-					*(indicesp++) = index_offset + num_of_vtx + 2;
-					num_of_vtx += 3;
-				}
-			}
-		}
-	}
-
-
-	LLPipeline::sCompiles++;
-	return TRUE;
-}
-
-
-
-BOOL LLVOWater::updateGeometryHeightFieldSimple(LLDrawable *drawable)
-{
-	LLVector3 position_agent = getPositionAgent();
-	const LLVector3 region_size = getScale();
-	const LLVector3 region_origin = position_agent - region_size * 0.5f;
-
-	S32 patch_origx = llround(region_origin.mV[VX] - sGrid->mRegionOrigin.mV[VX]) / sGrid->mRegionWidth;
-	S32 patch_origy = llround(region_origin.mV[VY] - sGrid->mRegionOrigin.mV[VY]) / sGrid->mRegionWidth;
-	S32 patch_dimx = llround(region_size.mV[VX]) / sGrid->mRegionWidth;
-	S32 patch_dimy = llround(region_size.mV[VY]) / sGrid->mRegionWidth;
-
-	static S32 res = sGrid->mPatchRes;
-	if (patch_origx < 0)
-	{
-		patch_dimx -= - patch_origx;
-		if (patch_dimx < 1)
-		{
-			return TRUE;
-		}
-		patch_origx = 0;
-	}
-	if (patch_origy < 0)
-	{
-		patch_dimy -= - patch_origy;
-		if (patch_dimy < 1)
-		{
-			return TRUE;
-		}
-		patch_origy = 0;
-	}
-	if (patch_origx >= res)
-	{
-		return TRUE;
-	}
-	if (patch_origy >= res)
-	{
-		return TRUE;
-	}
-
-	patch_dimx = llmin<U32>(patch_dimx, res - patch_origx);
-	patch_dimy = llmin<U32>(patch_dimy, res - patch_origy);
-
-
-	U32 num_of_regions = 0;
-	S32 px, py;
-
-	for (py = patch_origy; py < patch_origy + patch_dimy; py++)
-	{
-		for (px = patch_origx; px < patch_origx + patch_dimx; px++)
-		{
-		//	if (sGrid->mTab[px][py] != 0)
-		//		bool stop = true;
-			if (sGrid->mPatches[py * sGrid->mPatchRes + px].visible() && sGrid->mTab[px][py] == 0)
-			{
-				num_of_regions++;
-				sGrid->mTab[px][py] = this;
-			}
-		}
-	}
-
-	if (num_of_regions == 0)
-	{
-		return TRUE;
-	}
-
-	if (drawable->getNumFaces() < 1)
-	{
-		drawable->addFace((LLDrawPoolWater*) gPipeline.getPool(LLDrawPool::POOL_WATER),
-								gWorldp->getDefaultWaterTexture());
-	}
-
-	LLFace *face;
-
-	face = drawable->getFace(0);
-	face->mCenterAgent = position_agent;
-
-	LLStrider<LLVector3> verticesp, normalsp;
-	LLStrider<LLVector2> texCoordsp;
-	U32 *indicesp;
-	S32 index_offset;
-
-	const F32 water_height = getRegion()->getWaterHeight();
-
-	const U32 steps_in_region = sGrid->mStepsInRegion / sGrid->mResDecrease;
-	const U32 num_quads = steps_in_region * steps_in_region * num_of_regions;
-
-	face->setPrimType(LLTriangles);
-	face->setSize(4*num_quads, 6*num_quads);
-	index_offset = face->getGeometry(verticesp,normalsp,texCoordsp, indicesp);
-	if (-1 == index_offset)
-	{
-		return TRUE;
-	}
-
-	U32 num_of_vtx = 0;
-
-	for (py = patch_origy; py < patch_origy + patch_dimy; py++)
-	{
-		for (px = patch_origx; px < patch_origx + patch_dimx; px++)
-		{
-			if (!sGrid->mPatches[py * sGrid->mPatchRes + px].visible() || sGrid->mTab[px][py] != this)
-			{
-				continue;
-			}
-
-			U32 orig_indx = px * sGrid->mStepsInRegion;
-			U32 orig_indy = py * sGrid->mStepsInRegion;
-
-			for (U32 qy = 0; qy < steps_in_region; qy++)
-			{
-				for (U32 qx = 0; qx < steps_in_region; qx++)
-				{
-					const S32 x0 = orig_indx + qx * sGrid->mResDecrease;
-					const S32 y0 = orig_indy + qy * sGrid->mResDecrease;
-					const S32 x1 = x0 + sGrid->mResDecrease;
-					const S32 y1 = y0 + sGrid->mResDecrease;
-					
-					sGrid->setVertex(x0, y1, water_height, *(verticesp));
-					verticesp++;
-					sGrid->setVertex(x0, y0, water_height, *(verticesp));
-					verticesp++;
-					sGrid->setVertex(x1, y1, water_height, *(verticesp));
-					verticesp++;
-					sGrid->setVertex(x1, y0, water_height, *(verticesp));
-					verticesp++;
-					/*
-					*(verticesp++)  = sGrid->vtx(x0, y1, water_height);
-					*(verticesp++)  = sGrid->vtx(x0, y0, water_height);
-					*(verticesp++)  = sGrid->vtx(x1, y1, water_height);
-					*(verticesp++)  = sGrid->vtx(x1, y0, water_height);
-					*/
-					*(normalsp++)   = sGrid->norm(x0, y1);
-					*(normalsp++)   = sGrid->norm(x0, y0);
-					*(normalsp++)   = sGrid->norm(x1, y1);
-					*(normalsp++)   = sGrid->norm(x1, y0);
-					
-					const S32 curr_index_offset = index_offset + num_of_vtx;
-
-					*indicesp++ = curr_index_offset  + 0;
-					*indicesp++ = curr_index_offset  + 1;
-					*indicesp++ = curr_index_offset  + 2;
-					
-					*indicesp++ = curr_index_offset  + 1;
-					*indicesp++ = curr_index_offset  + 3;
-					*indicesp++ = curr_index_offset  + 2;
-					num_of_vtx += 4;
-				}
-			}
-		}
-	}
-
-
+	mDrawable->movePartition();
 	LLPipeline::sCompiles++;
 	return TRUE;
 }
 
 void LLVOWater::initClass()
 {
-	sGrid = new LLWaterGrid;
 }
 
 void LLVOWater::cleanupClass()
 {
-	if (sGrid)
-	{
-		sGrid->cleanup();
-		delete sGrid;
-		sGrid = 0;
-	}
-}
-
-
-LLWaterGrid::LLWaterGrid() : mResIncrease(1)//0.5)
-{
-	init();
 }
 
-
-void LLWaterGrid::init()
-{
-	//mRegionOrigin = LLVector3(-2 * mRegionWidth, -2 * mRegionWidth, 0);
-	mRegionWidth = 256;
-	mPatchRes = 5;
-	mMaxGridSize = mPatchRes * mRegionWidth;
-	mMinStep = (U32)(WAVE_STEP * mResIncrease);
-
-	LLWaterTri::sMinStep = mMinStep;
-	LLWaterTri::sQueues = &mRoam;
-
-	setGridDim(mMaxGridSize / mMinStep);
-
-	mVtx = new LLVector3[mGridDim1 * mGridDim1];
-	mNorms = new LLVector3[mGridDim1 * mGridDim1];
-
-	mPatches = new LLWaterPatch[mPatchRes * mPatchRes];
-
-	mStepsInRegion = mRegionWidth / mMinStep;
-	const U32 max_div_level = 2 * (U32)(log((F32)mStepsInRegion) / log(2.0f));
-
-	for (U32 y = 0; y < mPatchRes; y++)
-	{
-		for (U32 x = 0; x < mPatchRes; x++)
-		{
-			LLVector3 patch_center(mRegionWidth * (x + 0.5f), mRegionWidth * (y + 0.5f), 0);
-
-			mPatches[y * mPatchRes + x].set(x * mStepsInRegion, y * mStepsInRegion,
-											mStepsInRegion, mRegionWidth, patch_center, max_div_level);
-			if (x > 0)
-			{
-				mPatches[y * mPatchRes + x].left()->setRight(mPatches[y * mPatchRes + x - 1].right());
-				mPatches[y * mPatchRes + x - 1].right()->setRight(mPatches[y * mPatchRes + x].left());
-			}
-			if (y > 0)
-			{
-				mPatches[y * mPatchRes + x].left()->setLeft(mPatches[(y - 1) * mPatchRes + x].right());
-				mPatches[(y - 1) * mPatchRes + x].right()->setLeft(mPatches[y * mPatchRes + x].left());
-			}
-		}
-	}
-}
-
-void LLWaterGrid::cleanup()
-{
-	delete[] mVtx;
-	mVtx = NULL;
-
-	delete[] mNorms;
-	mNorms = NULL;
-
-	delete[] mPatches;
-	mPatches = NULL;
-}
-
-
 void setVecZ(LLVector3& v)
 {
 	v.mV[VX] = 0;
@@ -616,413 +240,31 @@ void setVecZ(LLVector3& v)
 	v.mV[VZ] = 1;
 }
 
-void LLWaterGrid::update()
-{
-	static LLViewerRegion* prev_region = gAgent.getRegion();
-	LLViewerRegion* region = gAgent.getRegion();
-
-	mRegionOrigin = region->getOriginAgent();
-	mRegionOrigin.mV[VX] -= 2 * mRegionWidth;
-	mRegionOrigin.mV[VY] -= 2 * mRegionWidth;
-	mRegionOrigin.mV[VZ] = 0;
-
-	const F32 clip_far = gCamera->getFar() - 31;
-	const F32 clip_far2 = clip_far * clip_far;
-
-	const LLVector3 camera_pos = gAgent.getCameraPositionAgent();
-	const LLVector3 look_at = gCamera->getAtAxis();
-
-
-	if (camera_pos.mV[VZ] > 200)
-	{
-		mResDecrease = 4;
-	}
-	else if (camera_pos.mV[VZ] > 100)
-	{
-		mResDecrease = 2;
-	}
-	else
-	{
-		mResDecrease = 1;
-	}
-
-
-	//U32 mResDecrease = res_decrease;
-	U32 res_decrease = 1;
-
-	const F32 res_change = mResIncrease;// * res_decrease ;
-
-	F32 height;
-
-	// Set the grid
-
-	//U32 fractions = 1;
-	U32 fractions_res = res_decrease;
-	if (res_change < 1)
-	{
-		//fractions = llround(1. / res_change);
-		fractions_res = llround(1.f / mResIncrease);
-	}
-
-
-	//const U32 fractions_res = fractions * res_decrease;
-
-	LLVector3 cur_pos;
-	U32 x, y;
-	U32 ind = 0;
-	for (y = 0; y < mGridDim1; y += fractions_res)
-	{
-		const F32 dispy = (F32)(y * mMinStep);//step;
-		for (x = 0; x < mGridDim1; x += fractions_res)
-		{
-			const F32 dispx = (F32)(x * mMinStep);//step;
-			cur_pos = mRegionOrigin;
-			cur_pos.mV[VX] += dispx;
-			cur_pos.mV[VY] += dispy;
-
-			const F32 x_dist = cur_pos.mV[VX] - camera_pos.mV[VX];
-			const F32 y_dist = cur_pos.mV[VY] - camera_pos.mV[VY];
-
-			if (x_dist * look_at.mV[VX] + y_dist * look_at.mV[VY] < 0)
-			{
-				mVtx[ind] = cur_pos;
-				setVecZ(mNorms[ind]);
-				ind++;
-				continue;
-			}
-				
-			const F32 dist_to_vtx2 = x_dist * x_dist + y_dist * y_dist;
-			if (dist_to_vtx2 > .81 * clip_far2)
-			{
-				mVtx[ind] = cur_pos;
-				setVecZ(mNorms[ind]);
-				ind++;
-				continue;
-			}
-
-			mWater.getIntegerHeightAndNormal(llround(WAVE_STEP_INV * dispx),
-					llround(WAVE_STEP_INV * dispy),	height, mNorms[ind]);
-
-			cur_pos.mV[VZ] += height;
-			mVtx[ind] = cur_pos;
-			ind++;
-		}
-	}
-
-	if (res_change < 1)
-	{
-		U32 fractions = llround(1.f / mResIncrease);
-		for (y = 0; y < mGridDim1; y += fractions_res)
-		{
-			for (x = 0; x < mGridDim; x += fractions_res)
-			{
-				const U32 ind00 = index(x, y);
-				const U32 ind01 = ind00 + fractions_res;
-				for (U32 frx = 1; frx < fractions; frx += res_decrease)
-				{
-					const U32 ind = ind00 + frx;
-					mNorms[ind] = LERP(mNorms[ind00], mNorms[ind01], frx * res_change);
-					mVtx[ind]   = LERP(  mVtx[ind00],   mVtx[ind01], frx * res_change);
-				}
-			}
-		}
-		for (x = 0; x < mGridDim1; x += res_decrease)
-		{
-			for (y = 0; y < mGridDim; y += fractions_res)
-			{
-				const U32 ind00 = index(x, y);
-				const U32 ind10 = ind00 + fractions_res * mGridDim1;//(y + fractions) * quad_resx1 + x;
-				for (U32 fry = 1; fry < fractions; fry += res_decrease)
-				{
-					const U32 ind = ind00 + fry * mGridDim1;//(y + fry) * quad_resx1 + x;
-					mNorms[ind] = LERP(mNorms[ind00], mNorms[ind10], fry * res_change);
-					mVtx[ind]   = LERP(  mVtx[ind00],   mVtx[ind10], fry * res_change);
-				}
-			}
-		}
-	}
-
-	if (gUseRoam)
-	{
-		updateTree(camera_pos, look_at, clip_far, prev_region != region);
-	}
-	else
-	{
-		updateVisibility(camera_pos, look_at, clip_far);
-	}
-
-	prev_region = region;
-
-
-	//mTab[0][0] = 0;
-	for (y = 0; y < mPatchRes; y++)
-	{
-		for (x = 0; x < mPatchRes; x++)
-			mTab[x][y] = 0;
-	}
-
-}
-
-void LLWaterGrid::updateTree(const LLVector3 &camera_pos, const LLVector3 &look_at, F32 clip_far,
-							 BOOL restart = FALSE)
-{
-	static S8 recalculate_frame = 0;
-
-	if (restart)
-	{
-		recalculate_frame = 0;
-	}
-
-	if (recalculate_frame == 0)
-	{
-		LLWaterTri::nextRound();
-		setCamPosition(LLWaterTri::sCam, camera_pos);
-		LLWaterTri::sClipFar = clip_far;
-
-
-		const U32 step = (U32)(WAVE_STEP * mResIncrease * mResDecrease);
-		const U32 steps_in_region = mRegionWidth / step;
-		LLWaterTri::sMaxDivLevel = 2 * llround(log((F32)steps_in_region) / log(2.0f));
-
-		for (U32 y = 0; y < mPatchRes; y++)
-		{
-			for (U32 x = 0; x < mPatchRes; x++)
-			{
-				U32 patch_ind = y * mPatchRes + x;				
-				mPatches[patch_ind].updateTree(camera_pos, look_at, mRegionOrigin);
-			}
-		}
-
-		mRoam.process();
-
-		// debug
-		/*
-		for (y = 0; y < mPatchRes; y++)
-		{
-			for (U32 x = 0; x < mPatchRes; x++)
-			{
-				//mPatches[y * mPatchRes + x].checkUpToDate();
-				//mPatches[y * mPatchRes + x].checkConsistensy();
-				mPatches[y * mPatchRes + x].checkCount();
-			}
-		}
-		*/
-	}
-	++recalculate_frame;
-	recalculate_frame = recalculate_frame % 2;
-}
-
-void LLWaterGrid::updateVisibility(const LLVector3 &camera_pos, const LLVector3 &look_at, F32 clip_far)
-{
-	for (U32 y = 0; y < mPatchRes; y++)
-	{
-		for (U32 x = 0; x < mPatchRes; x++)
-		{
-			mPatches[y * mPatchRes + x].updateVisibility(camera_pos, look_at, mRegionOrigin);
-		}
-	}
-}
-
-
 void LLVOWater::setUseTexture(const BOOL use_texture)
 {
 	mUseTexture = use_texture;
 }
 
-F32 LLWaterSurface::agentDepth() const
+void LLVOWater::updateSpatialExtents(LLVector3 &newMin, LLVector3& newMax)
 {
-	const LLViewerRegion* region = gAgent.getRegion();
-	LLVector3 position_agent = region->getOriginAgent();// getPositionAgent();
-	const LLVector3 region_origin = position_agent;
-	const LLVector3 camera_pos = gAgent.getCameraPositionAgent();
+	LLVector3 pos = getPositionAgent();
+	LLVector3 scale = getScale();
 
-	F32 height;
-	LLVector3 normal;
+	newMin = pos - scale * 0.5f;
+	newMax = pos + scale * 0.5f;
 
-	getHeightAndNormal(WAVE_STEP_INV * camera_pos.mV[VX], 
-						WAVE_STEP_INV * camera_pos.mV[VY], height, normal);
-	F32 agent_water_height = gAgent.getRegion()->getWaterHeight();
-	return camera_pos.mV[VZ] - (agent_water_height + height);
+	mDrawable->setPositionGroup((newMin + newMax) * 0.5f);
 }
 
-////////////////////////////////////////////////
-
-
-void LLWaterSurface::getHeightAndNormal(F32 i, F32 j, F32& wave_height, LLVector3& normal) const
-{
-	S32 i_ind = llfloor(i);
-	S32 j_ind = llfloor(j);
-	F32 i_fr = i - i_ind;
-	F32 j_fr = j - j_ind;
-
-	i_ind = i_ind % N_RES;
-	j_ind = j_ind % N_RES;
-
-	S32 i_ind_next = i_ind + 1;
-	S32 j_ind_next = j_ind + 1;
-	if (i_ind_next == (S32)N_RES)	i_ind_next = 0;
-	if (j_ind_next == (S32)N_RES)	j_ind_next = 0;
-
-	const F32 i_fr1 = 1 - i_fr;
-	const F32 j_fr1 = 1 - j_fr;
-
-	const F32 hi0	= i_fr1 * height(i_ind, j_ind)		+	i_fr * height(i_ind_next, j_ind);
-	const F32 hi1	= i_fr1 * height(i_ind, j_ind_next)	+	i_fr * height(i_ind_next, j_ind_next);
-	wave_height		= j_fr1 * hi0 + j_fr * hi1;
-
-	normal			= i_fr1 * mNorms[i_ind][j_ind];
-	normal			+=				i_fr * mNorms[i_ind_next][j_ind];
-	LLVector3 vi1	= i_fr1 * mNorms[i_ind][j_ind_next];
-	vi1				+=				i_fr * mNorms[i_ind_next][j_ind_next];
-	normal			*= j_fr1;
-	normal			+=				j_fr * vi1;
-
-	//normal.normVec();
-}
-
-void LLWaterSurface::getIntegerHeightAndNormal(S32 i, S32 j, F32& wave_height, LLVector3& normal) const
-{
-	S32 i_ind = i % N_RES;
-	S32 j_ind = j % N_RES;
-
-	wave_height		= height(i_ind, j_ind);
-	normal			= mNorms[i_ind][j_ind];
+U32 LLVOWater::getPartitionType() const
+{ 
+	return LLPipeline::PARTITION_WATER; 
 }
 
-F32 LLWaterSurface::phillips(const LLVector2& k, const LLVector2& wind_n, F32 L, F32 L_small)
+LLWaterPartition::LLWaterPartition()
+: LLSpatialPartition(0)
 {
-	F32 k2 = k * k;
-	F32 k_dot_wind = k * wind_n;
-	F32 spectrum = mA * (F32) exp(-1 / (L * L * k2)) / (k2 * k2) * (k_dot_wind * k_dot_wind / k2);
-
-	if (k_dot_wind < 0) spectrum *= .25f;  // scale down waves that move opposite to the wind
-
-	F32 damp = (F32) exp(- k2 * L_small * L_small);
-
-	return (spectrum * damp);
+	mRenderByGroup = FALSE;
+	mDrawableType = LLPipeline::RENDER_TYPE_WATER;
+	mPartitionType = LLPipeline::PARTITION_WATER;
 }
-
-
-
-void LLWaterSurface::initAmplitudes()
-{
-	U16 i, j;
-	LLVector2 k;
-	F32 sqrtPhillips;
-
-	const LLVector2 wind(mWind.mV);
-
-	LLVector2 wind_n = wind;
-	const F32 wind_vel = wind_n.normVec();
-
-	const F32 L = wind_vel * wind_vel / g;	// largest wave arising from constant wind of speed wind_vel
-
-	const F32 L_small = L / 70;		// eliminate waves with very small length (L_small << L)
-
-
-	for (i = 0; i <= N_RES; i++)
-	{
-		k.mV[VX] = (- (S32)N_RES_HALF + i) * (F_TWO_PI / WIDTH);
-		for (j = 0; j <= N_RES; j++)
-		{
-			k.mV[VY] = (- (S32)N_RES_HALF + j) * (F_TWO_PI / WIDTH);
-
-			const F32 k_mag = k.magVec();
-			mOmega[i][j] = (F32) sqrt(g * k_mag);
-
-			if (k_mag < F_APPROXIMATELY_ZERO)
-				sqrtPhillips = 0;
-			else
-				sqrtPhillips = (F32) sqrt(phillips(k, wind_n, L, L_small));
-
-			//const F32 r1 = rand() / (F32) RAND_MAX;
-			//const F32 r2 = rand() / (F32) RAND_MAX;
-			const F32 r1 = randGauss(0, 1);
-			const F32 r2 = randGauss(0, 1);
-
-			mHtilda0[i][j].re = sqrtPhillips * r1 * OO_SQRT2;
-			mHtilda0[i][j].im = sqrtPhillips * r2 * OO_SQRT2;
-
-		}
-	}
-
-	mPlan.init(N_RES, N_RES);
-	mInitialized = 1;  // initialization complete
-}
-
-void LLWaterSurface::generateWaterHeightField(F64 t)
-{
-	S32 i, j;
-	S32 mi, mj;                // -K indices
-	COMPLEX plus, minus;
-	
-	if (!mInitialized) initAmplitudes();
-	
-	for (i = 0; i < (S32)N_RES_HALF; i++)
-	{
-		mi = N_RES - i;
-		for (j = 0; j < (S32)N_RES ; j++)
-		{
-			mj = N_RES - j;
-
-			const F32 cos_wt =  cosf(mOmega[i][j] * t); // = cos(-mOmega[i][j] * t)
-			const F32 sin_wt =  sinf(mOmega[i][j] * t); // = -sin(-mOmega[i][j] * t)
-			plus.re  =  mHtilda0[i][j].re   * cos_wt - mHtilda0[i][j].im   * sin_wt;
-			plus.im  =  mHtilda0[i][j].re   * sin_wt + mHtilda0[i][j].im   * cos_wt;
-			minus.re =  mHtilda0[mi][mj].re * cos_wt - mHtilda0[mi][mj].im * sin_wt;
-			minus.im = -mHtilda0[mi][mj].re * sin_wt - mHtilda0[mi][mj].im * cos_wt;
-
-			// now sum the plus and minus waves to get the total wave amplitude h
-			mHtilda[i * N_RES + j].re = plus.re + minus.re;
-			mHtilda[i * N_RES + j].im = plus.im + minus.im;
-			if (mi < (S32)N_RES && mj < (S32)N_RES)
-			{
-				mHtilda[mi * N_RES + mj].re = plus.re + minus.re;
-				mHtilda[mi * N_RES + mj].im = -plus.im - minus.im;
-			}
-		}
-	}
-
-	inverse_fft(mPlan, mHtilda, N_RES, N_RES);
-
-	calcNormals();
-}
-
-
-/*
- * Computer normals by taking finite differences.  
- */
-
-void LLWaterSurface::calcNormals()
-{
-	LLVector3 n;
-	
-	for (U32 i = 0; i < N_RES; i++)
-	{
-		for (U32 j = 0; j < N_RES; j++)
-		{
-			F32 px = heightWrapped(i + 1, j);
-			F32 mx = heightWrapped(i - 1, j);
-			F32 py = heightWrapped(i, j + 1);
-			F32 my = heightWrapped(i, j - 1);
-			F32 pxpy = heightWrapped(i + 1, j + 1);
-			F32 pxmy = heightWrapped(i + 1, j - 1);
-			F32 mxpy = heightWrapped(i - 1, j + 1); 
-			F32 mxmy = heightWrapped(i - 1, j - 1);
-
-			n.mV[VX] = -((2 * px + pxpy + pxmy) - (2 * mx + mxpy + mxmy));
-			n.mV[VY] = -((2 * py + pxpy + mxpy) - (2 * my + pxmy + mxmy));
-			n.mV[VZ] = 8 * WIDTH / (F32) N_RES;
-			n.normVec();
-			
-			mNorms[i][j] = n;
-		}
-	}
-}
-
-void LLVOWater::generateNewWaves(F64 time)
-{
-	getWaterSurface()->generateWaterHeightField(time);
-	sGrid->update();
-}
-
diff --git a/indra/newview/llvowater.h b/indra/newview/llvowater.h
index 2b846dba2028aedb0307827581195073e885c02f..74a5e81d8c9813ce582a785908db42a708a1ca49 100644
--- a/indra/newview/llvowater.h
+++ b/indra/newview/llvowater.h
@@ -16,195 +16,25 @@
 
 #include "llwaterpatch.h"
 
-
 const U32 N_RES	= 16; //32			// number of subdivisions of wave tile
 const U8  WAVE_STEP		= 8;
 
-/*
-#define N_DET		32			// number of subdivisions of wave tile for details
-
-class LLWaterDetail
-{
-protected:
-	S32				mResolution;
-	LLViewerImage	*mTex;
-	U8				*mTexData;
-
-public:
-	LLWaterDetail() : mResolution(N_DET), mTex(0), mTexData(0) {init();}
-	void init();
-
-	~LLWaterDetail()
-	{
-		delete[] mTexData;
-		mTexData = NULL;
-	}
-
-
-	//void initEmpty();
-
-	void setPixel(const LLVector3 &norm, const S32 i, const S32 j)
-	{
-		S32 offset = (i * mResolution + j) * 3;
-		mTexData[offset]   = llround(norm.mV[VX] * 255);
-		mTexData[offset+1] = llround(norm.mV[VX] * 255);
-		mTexData[offset+2] = llround(norm.mV[VX] * 255);
-	}
-	void setPixel(F32 x, F32 y, F32 z, const S32 i, const S32 j)
-	{
-		S32 offset = (i * mResolution + j) * 3;
-		mTexData[offset]   = llround(x * 255);
-		mTexData[offset+1] = llround(y * 255);
-		mTexData[offset+2] = llround(z * 255);
-	}
-
-	S32 getResolution()						{ return mResolution; }
-
-	void createDetailBumpmap(F32* u, F32* v);
-	void createTexture() const			{ mTex->createTexture(); }
-	void bindTexture() const			{ mTex->bindTexture(); }
-	LLViewerImage* getTexture() const	{ return mTex; }
-};
-*/
-
-class LLWaterSurface
-{
-protected:
-	BOOL mInitialized;
-	LLVector3 mWind;
-	F32 mA;
-	F32 mVisc; // viscosity of the fluid
-	F32 mShininess;
-
-	//LLWaterDetail* mDetail;
-
-	LLFFTPlan mPlan;
-	F32 mOmega[N_RES+1][N_RES+1];		// wave frequency
-	COMPLEX mHtilda0[N_RES+1][N_RES+1];	// wave amplitudes and phases at time 0.
-	LLVector3 mNorms[N_RES][N_RES];
-	COMPLEX mHtilda[N_RES * N_RES];
-
-public:
-	LLWaterSurface();
-	~LLWaterSurface() {}
-
-	//void initSpecularLookup();
-
-	F32 phillips(const LLVector2& k, const LLVector2& wind_n, F32 L, F32 L_small);
-
-	void initAmplitudes();
-
-	F32 height(S32 i, S32 j) const { return mHtilda[i * N_RES + j].re; }
-	F32 heightWrapped(S32 i, S32 j) const
-	{
-		return height((i + N_RES) % N_RES, (j + N_RES) % N_RES);
-	}
-
-	void generateWaterHeightField(F64 time);
-	void calcNormals();
-
-	void getHeightAndNormal(F32 i, F32 j, F32& height, LLVector3& normal) const;
-	void getIntegerHeightAndNormal(S32 i, S32 j, F32& height, LLVector3& normal) const;
-	F32 agentDepth() const;
-
-//	const LLWaterHeightField* hField() const { return &mHeightField; }
-
-	//void generateDetail(F64 t);
-	//void fluidSolver(F32* u, F32* v, F32* u0, F32* v0, F64 dt);
-
-	const LLVector3& getWind() const { return mWind; }
-	void setWind(const LLVector3& w) { mWind = w; } // initialized = 0?
-	F32 A() const { return mA; }
-	void setA(F32 a) { mA = a; }
-	F32 getShininess() const { return mShininess; }
-	//LLViewerImage* getSpecularLookup() const { return mSpecularLookup; }
-	//LLViewerImage* getDetail() const { return mDetail->getTexture(); }
-};
-
-class LLVOWater;
-
-class LLWaterGrid
-{
-public:
-	LLWaterGrid();
-
-	void init();
-	void cleanup();
-
-	LLWaterSurface* getWaterSurface() { return &mWater; }
-
-	void update();
-	void updateTree(const LLVector3 &camera_pos, const LLVector3 &look_at, F32 clip_far,
-		BOOL restart);
-	void updateVisibility(const LLVector3 &camera_pos, const LLVector3 &look_at, F32 clip_far);
-
-	LLVector3 mRegionOrigin;
-
-	LLVector3* mVtx;
-	LLVector3* mNorms;
-	U32 mRegionWidth;
-	U32 mMaxGridSize;
-	U32 mPatchRes;
-	U32 mMinStep;
-	U32 mStepsInRegion;
-	LLWaterPatch* mPatches;
-	LLRoam mRoam;
-	F32 mResIncrease;
-	U32 mResDecrease;
-
-	LLVOWater* mTab[5][5];
-
-	U32 gridDim() const					{ return mGridDim; }
-	U32 rowSize() const					{ return mGridDim1; }
-	void setGridDim(U32 gd)				{ mGridDim = gd; mGridDim1 = mGridDim + 1; }
-	U32 index(const LL2Coord& c) const	{ return c.y() * mGridDim1 + c.x(); }
-	U32 index(U32 x, U32 y) const		{ return y * mGridDim1 + x; }
-
-	LLVector3 vtx(const LL2Coord& c, F32 raised) const
-	{
-		LLVector3 v = mVtx[index(c)];
-		v.mV[VZ] += raised;
-		return v;
-	}
-
-	LLVector3 vtx(U32 x, U32 y, F32 raised) const
-	{
-		LLVector3 v = mVtx[index(x, y)];
-		v.mV[VZ] += raised;
-		return v;
-	}
-
-	void setVertex(const U32 x, const U32 y, const F32 raised, LLVector3 &vertex) const
-	{
-		vertex = mVtx[index(x, y)];
-		vertex.mV[VZ] += raised;
-	}
-
-	void setCamPosition(LL2Coord& cam, const LLVector3& cam_pos)
-	{
-		cam.x() = llround((cam_pos.mV[VX] - mRegionOrigin.mV[VX]) / mMinStep);
-		cam.y() = llround((cam_pos.mV[VY] - mRegionOrigin.mV[VY]) / mMinStep);
-	}
-
-	LLVector3 vtx(const LL2Coord& c) const		{ return mVtx[index(c)]; }
-	LLVector3 norm(const LL2Coord& c) const		{ return mNorms[index(c)]; }
-	LLVector3 vtx(U32 x, U32 y) const			{ return mVtx[index(x, y)]; }
-	LLVector3 norm(U32 x, U32 y) const			{ return mNorms[index(x, y)]; }
-
-protected:
-	LLWaterSurface mWater;
-	U32 mGridDim;
-	U32 mGridDim1;
-};
-
 class LLSurface;
 class LLHeavenBody;
 class LLVOSky;
 class LLFace;
 
-class LLVOWater : public LLViewerObject
+class LLVOWater : public LLStaticViewerObject
 {
 public:
+	enum 
+	{
+		VERTEX_DATA_MASK =	(1 << LLVertexBuffer::TYPE_VERTEX) |
+							(1 << LLVertexBuffer::TYPE_NORMAL) |
+							(1 << LLVertexBuffer::TYPE_TEXCOORD) 
+	}
+	eVertexDataMask;
+
 	LLVOWater(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp);
 	virtual ~LLVOWater() {}
 
@@ -217,24 +47,19 @@ public:
 	/*virtual*/ BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time);
 	/*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline);
 	/*virtual*/ BOOL        updateGeometry(LLDrawable *drawable);
-				BOOL		updateGeometryFlat(LLDrawable *drawable);
-				BOOL		updateGeometryHeightFieldSimple(LLDrawable *drawable);
-				BOOL		updateGeometryHeightFieldRoam(LLDrawable *drawable);
+	/*virtual*/ void		updateSpatialExtents(LLVector3& newMin, LLVector3& newMax);
 
-	/*virtual*/ void updateDrawable(BOOL force_damped);
 	/*virtual*/ void updateTextures(LLAgent &agent);
 	/*virtual*/ void setPixelAreaAndAngle(LLAgent &agent); // generate accurate apparent angle and area
 
+	virtual U32 getPartitionType() const;
+
 	/*virtual*/ BOOL isActive() const; // Whether this object needs to do an idleUpdate.
 
 	void setUseTexture(const BOOL use_texture);
 
-	static void generateNewWaves(F64 time);
-	static LLWaterSurface* getWaterSurface() { return sGrid->getWaterSurface(); }//return &mWater; }
-	static const LLWaterGrid* getGrid() { return sGrid; }
 protected:
 	BOOL mUseTexture;
-	static LLWaterGrid *sGrid;
 };
 
 #endif // LL_VOSURFACEPATCH_H
diff --git a/indra/newview/llworld.cpp b/indra/newview/llworld.cpp
index 813eb0c7ea7502c73692f7dfaaf5e900a165b7cb..752d5a38c62ef0f2a8b0636b1c1b1a5af6b2fdf7 100644
--- a/indra/newview/llworld.cpp
+++ b/indra/newview/llworld.cpp
@@ -43,7 +43,6 @@ U32			gAgentPauseSerialNum = 0;
 // Constants
 //
 const S32 MAX_NUMBER_OF_CLOUDS	= 750;
-const F32 MIN_IDLE_UPDATE_TIME = 0.025f;
 const S32 WORLD_PATCH_SIZE = 16;
 
 extern LLColor4U MAX_WATER_COLOR;
@@ -63,7 +62,6 @@ LLWorld::LLWorld(const U32 grids_per_region, const F32 meters_per_grid)
 	mLastPacketsOut = 0;
 	mLastPacketsLost = 0;
 	mLandFarClip = DEFAULT_FAR_PLANE;
-	mIdleUpdateTime = MIN_IDLE_UPDATE_TIME;
 
 	if (gNoRender)
 	{
@@ -616,31 +614,22 @@ void LLWorld::updateVisibilities()
 	gCamera->setFar(cur_far_clip);
 }
 
-
-void LLWorld::updateRegions()
+void LLWorld::updateRegions(F32 max_update_time)
 {
 	LLViewerRegion *regionp;
 	LLTimer update_timer;
-
+	BOOL did_one = FALSE;
+	
 	// Perform idle time updates for the regions (and associated surfaces)
 	for (regionp = mRegionList.getFirstData();
 		 regionp;
 		 regionp = mRegionList.getNextData())
 	{
-		update_timer.reset();
-		if (!regionp->idleUpdate(update_timer, mIdleUpdateTime))
-		{
-			// Didn't finish all the updates.  Slightly increase the idle update time.
-			mIdleUpdateTime *= 1.05f;
-		}
-		else
-		{
-			mIdleUpdateTime *= 0.9f;
-			if (mIdleUpdateTime < MIN_IDLE_UPDATE_TIME)
-			{
-				mIdleUpdateTime = MIN_IDLE_UPDATE_TIME;
-			}
-		}
+		F32 max_time = max_update_time - update_timer.getElapsedTimeF32();
+		if (did_one && max_time <= 0.f)
+			break;
+		max_time = llmin(max_time, max_update_time*.1f);
+		did_one |= regionp->idleUpdate(max_update_time);
 	}
 }
 
diff --git a/indra/newview/llworld.h b/indra/newview/llworld.h
index 71d7a023222a58c6cc36339fbabf5dfa46395955..0d0d1b3211345ca95774f3a77f70546137803f02 100644
--- a/indra/newview/llworld.h
+++ b/indra/newview/llworld.h
@@ -95,7 +95,7 @@ public:
 	F32						getRegionMinHeight() const		{ return -mWidthInMeters; }
 	F32						getRegionMaxHeight() const		{ return 3.f*mWidthInMeters; }
 
-	void					updateRegions();
+	void					updateRegions(F32 max_update_time);
 	void					updateVisibilities();
 	void					updateParticles();
 	void					updateClouds(const F32 dt);
@@ -140,7 +140,6 @@ private:
 	const F32 mWidthInMeters;
 
 	F32 mLandFarClip;					// Far clip distance for land.
-	F32 mIdleUpdateTime;
 	LLPatchVertexArray		mLandPatch;
 	S32 mLastPacketsIn;
 	S32 mLastPacketsOut;
diff --git a/indra/newview/llworldmapview.cpp b/indra/newview/llworldmapview.cpp
index 10994d38d22f088dcfde20892613eaf8a3aa3860..fde1411563902a689cf6c770e187ea51a817b476 100644
--- a/indra/newview/llworldmapview.cpp
+++ b/indra/newview/llworldmapview.cpp
@@ -298,7 +298,6 @@ void LLWorldMapView::draw()
 	{
 		LLGLSNoTexture no_texture;
 	
-
 		glMatrixMode(GL_MODELVIEW);
 
 		// Clear the background alpha to 0
@@ -362,14 +361,12 @@ void LLWorldMapView::draw()
 		current_image->setBoostLevel(LLViewerImage::BOOST_MAP_LAYER);
 		current_image->setKnownDrawSize(llround(pix_width), llround(pix_height));
 		
-#if 1 || LL_RELEASE_FOR_DOWNLOAD
 		if (!current_image->getHasGLTexture())
 		{
 			continue; // better to draw nothing than the default image
 		}
-#endif
 
-		LLTextureView::addDebugImage(current_image);
+// 		LLTextureView::addDebugImage(current_image);
 		
 		// Draw using the texture.  If we don't clamp we get artifact at
 		// the edge.
@@ -522,7 +519,7 @@ void LLWorldMapView::draw()
 			overlayimage->setKnownDrawSize(draw_size, draw_size);
 		}
 			
-		LLTextureView::addDebugImage(simimage);
+// 		LLTextureView::addDebugImage(simimage);
 
 		if (sim_visible && info->mAlpha > 0.001f)
 		{
@@ -815,6 +812,39 @@ void LLWorldMapView::draw()
 	updateVisibleBlocks();
 }
 
+//virtual
+void LLWorldMapView::setVisible(BOOL visible)
+{
+	LLPanel::setVisible(visible);
+	if (!visible && gWorldMap)
+	{
+		for (S32 map = 0; map < MAP_SIM_IMAGE_TYPES; map++)
+		{
+			for (U32 layer_idx=0; layer_idx<gWorldMap->mMapLayers[map].size(); ++layer_idx)
+			{
+				if (gWorldMap->mMapLayers[map][layer_idx].LayerDefined)
+				{
+					LLWorldMapLayer *layer = &gWorldMap->mMapLayers[map][layer_idx];
+					layer->LayerImage->setBoostLevel(0);
+				}
+			}
+		}
+		for (LLWorldMap::sim_info_map_t::iterator it = gWorldMap->mSimInfoMap.begin();
+			 it != gWorldMap->mSimInfoMap.end(); ++it)
+		{
+			LLSimInfo* info = (*it).second;
+			if (info->mCurrentImage.notNull())
+			{
+				info->mCurrentImage->setBoostLevel(0);
+			}
+			if (info->mOverlayImage.notNull())
+			{
+				info->mOverlayImage->setBoostLevel(0);
+			}
+		}
+	}
+}
+
 void LLWorldMapView::drawGenericItems(const LLWorldMap::item_info_list_t& items, LLPointer<LLViewerImage> image)
 {
 	LLWorldMap::item_info_list_t::const_iterator e;
diff --git a/indra/newview/llworldmapview.h b/indra/newview/llworldmapview.h
index d0a6aeacb2cc3f181d22c7e79106da1e6c941ca9..9b353efc72c2c5134122192d4b81d7bd530f0269 100644
--- a/indra/newview/llworldmapview.h
+++ b/indra/newview/llworldmapview.h
@@ -47,12 +47,13 @@ public:
 	virtual ~LLWorldMapView();
 
 	virtual void	reshape(S32 width, S32 height, BOOL called_from_parent = TRUE );
+	virtual void	setVisible(BOOL visible);
 
 	virtual BOOL	handleMouseDown(S32 x, S32 y, MASK mask);
 	virtual BOOL	handleMouseUp(S32 x, S32 y, MASK mask);
 	virtual BOOL	handleDoubleClick( S32 x, S32 y, MASK mask );
 	virtual BOOL	handleHover( S32 x, S32 y, MASK mask );
-	/*virtual*/ BOOL	handleToolTip( S32 x, S32 y, LLString& msg, LLRect* sticky_rect_screen );
+	virtual BOOL	handleToolTip( S32 x, S32 y, LLString& msg, LLRect* sticky_rect_screen );
 
 	bool			checkItemHit(S32 x, S32 y, LLItemInfo& item, LLUUID* id, bool track);
 	void			handleClick(S32 x, S32 y, MASK mask, S32* hit_type, LLUUID* id);
diff --git a/indra/newview/macview_Prefix.h b/indra/newview/macview_Prefix.h
index 60097bd422d374fea046208d47b47d73c2a53b17..06a2c1604bc1621f23945d725261db13d804beb2 100644
--- a/indra/newview/macview_Prefix.h
+++ b/indra/newview/macview_Prefix.h
@@ -111,17 +111,15 @@
 #include "lldrawpoolalpha.h"
 #include "lldrawpoolavatar.h"
 #include "lldrawpooltree.h"
-#include "lldrawpooltreenew.h"
 #include "lldrawpoolterrain.h"
 #include "lldrawpoolsky.h"
 #include "lldrawpoolwater.h"
 #include "lldrawpoolground.h"
 #include "lldrawpoolbump.h"
-#include "llvotreenew.h"
 
 /////////////////// From llface.cpp
 #include "llgl.h"
-#include "llviewerimagelist.h"
+#include "llviewerimage.h"
 #include "llsky.h"
 #include "llvosky.h"
 #include "llcontrol.h"
diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp
index 445f8c6fbfcaeeb0b1b5ceed7cb256c9db0cddca..4108298dfde7334bf68beadea46882a3e8833610 100644
--- a/indra/newview/pipeline.cpp
+++ b/indra/newview/pipeline.cpp
@@ -13,12 +13,12 @@
 // library includes
 #include "audioengine.h" // For MAX_BUFFERS for debugging.
 #include "imageids.h"
-#include "llagpmempool.h"
 #include "llerror.h"
 #include "llviewercontrol.h"
 #include "llfasttimer.h"
 #include "llfontgl.h"
 #include "llmemory.h"
+#include "llmemtype.h"
 #include "llnamevalue.h"
 #include "llprimitive.h"
 #include "llvolume.h"
@@ -26,17 +26,17 @@
 #include "timing.h"
 #include "v3color.h"
 #include "llui.h"
+#include "llglheaders.h"
 
 // newview includes
 #include "llagent.h"
-#include "llagparray.h"
 #include "lldrawable.h"
 #include "lldrawpoolalpha.h"
 #include "lldrawpoolavatar.h"
 #include "lldrawpoolground.h"
 #include "lldrawpoolsimple.h"
+#include "lldrawpoolbump.h"
 #include "lldrawpooltree.h"
-#include "lldrawpoolhud.h"
 #include "lldrawpoolwater.h"
 #include "llface.h"
 #include "llfeaturemanager.h"
@@ -63,10 +63,13 @@
 #include "llvosky.h"
 #include "llvotree.h"
 #include "llvovolume.h"
+#include "llvosurfacepatch.h"
+#include "llvowater.h"
+#include "llvotree.h"
+#include "llvopartgroup.h"
 #include "llworld.h"
 #include "viewer.h"
-#include "llagpmempoolarb.h"
-#include "llagparray.inl"
+#include "llcubemap.h"
 
 #ifdef _DEBUG
 // Debug indices is disabled for now for debug performance - djs 4/24/02
@@ -75,6 +78,8 @@
 //#define DEBUG_INDICES
 #endif
 
+#define AGGRESSIVE_OCCLUSION 0
+
 const F32 BACKLIGHT_DAY_MAGNITUDE_AVATAR = 0.2f;
 const F32 BACKLIGHT_NIGHT_MAGNITUDE_AVATAR = 0.1f;
 const F32 BACKLIGHT_DAY_MAGNITUDE_OBJECT = 0.1f;
@@ -98,10 +103,8 @@ extern S32 gBoxFrame;
 extern BOOL gRenderLightGlows;
 extern BOOL gHideSelectedObjects;
 
-
 BOOL	gAvatarBacklight = FALSE;
 
-F32		gMinObjectDistance = MIN_NEAR_PLANE;
 S32		gTrivialAccepts = 0;
 
 BOOL	gRenderForSelect = FALSE;
@@ -157,6 +160,13 @@ const char* LLPipeline::sTerrainUniforms[] =
 
 U32 LLPipeline::sTerrainUniformCount = sizeof(LLPipeline::sTerrainUniforms)/sizeof(char*);
 
+const char* LLPipeline::sShinyUniforms[] = 
+{
+	"origin"
+};
+
+U32 LLPipeline::sShinyUniformCount = sizeof(LLPipeline::sShinyUniforms)/sizeof(char*);
+
 const char* LLPipeline::sWaterUniforms[] =
 {
 	"screenTex",
@@ -173,8 +183,6 @@ const char* LLPipeline::sWaterUniforms[] =
 
 U32 LLPipeline::sWaterUniformCount =  sizeof(LLPipeline::sWaterUniforms)/sizeof(char*);
 
-// the SSE variable is dependent on software blending being enabled. 
-
 //----------------------------------------
 
 void stamp(F32 x, F32 y, F32 xs, F32 ys)
@@ -196,40 +204,35 @@ void stamp(F32 x, F32 y, F32 xs, F32 ys)
 //----------------------------------------
 
 S32		LLPipeline::sCompiles = 0;
-S32		LLPipeline::sAGPMaxPoolSize = 1 << 25; // 32MB
+
+BOOL	LLPipeline::sShowHUDAttachments = TRUE;
 BOOL	LLPipeline::sRenderPhysicalBeacons = FALSE;
 BOOL	LLPipeline::sRenderScriptedBeacons = FALSE;
 BOOL	LLPipeline::sRenderParticleBeacons = FALSE;
 BOOL	LLPipeline::sRenderSoundBeacons = FALSE;
+BOOL	LLPipeline::sUseOcclusion = FALSE;
+BOOL	LLPipeline::sSkipUpdate = FALSE;
+BOOL	LLPipeline::sDynamicReflections = FALSE;
 
 LLPipeline::LLPipeline() :
 	mVertexShadersEnabled(FALSE),
 	mVertexShadersLoaded(0),
 	mLastRebuildPool(NULL),
 	mAlphaPool(NULL),
+	mAlphaPoolPostWater(NULL),
 	mSkyPool(NULL),
 	mStarsPool(NULL),
-	mCloudsPool(NULL),
 	mTerrainPool(NULL),
 	mWaterPool(NULL),
 	mGroundPool(NULL),
-	mHUDPool(NULL),
-	mAGPMemPool(NULL),
-	mGlobalFence(0),
-	mBufferIndex(0),
-	mBufferCount(kMaxBufferCount),
-	mUseOcclusionCulling(FALSE),
+	mSimplePool(NULL),
+	mBumpPool(NULL),
 	mLightMask(0),
-	mLightMovingMask(0)
+	mLightMovingMask(0),
+	mCubeBuffer(NULL),
+	mCubeList(0)
 {
-	for(S32 i = 0; i < kMaxBufferCount; i++)
-	{
-		mBufferMemory[i] = NULL;
-	}
-	for (S32 i = 0; i < kMaxBufferCount; i++)
-	{
-		mBufferFence[i] = 0;
-	}
+
 }
 
 void LLPipeline::init()
@@ -238,8 +241,25 @@ void LLPipeline::init()
 	
 	stop_glerror();
 
-	mAGPBound = FALSE;
-	mObjectPartition = new LLSpatialPartition;
+	//create object partitions
+	//MUST MATCH declaration of eObjectPartitions
+	mObjectPartition.push_back(new LLVolumePartition());	//PARTITION_VOLUME
+	mObjectPartition.push_back(new LLBridgePartition());	//PARTITION_BRIDGE
+	mObjectPartition.push_back(new LLHUDPartition());		//PARTITION_HUD
+	mObjectPartition.push_back(new LLTerrainPartition());	//PARTITION_TERRAIN
+	mObjectPartition.push_back(new LLWaterPartition());		//PARTITION_WATER
+	mObjectPartition.push_back(new LLTreePartition());		//PARTITION_TREE
+	mObjectPartition.push_back(new LLParticlePartition());	//PARTITION_PARTICLE
+	mObjectPartition.push_back(new LLCloudPartition());		//PARTITION_CLOUD
+	mObjectPartition.push_back(new LLGrassPartition());		//PARTITION_GRASS
+	mObjectPartition.push_back(NULL);						//PARTITION_NONE
+	
+	//create render pass pools
+	getPool(LLDrawPool::POOL_ALPHA);
+	getPool(LLDrawPool::POOL_ALPHA_POST_WATER);
+	getPool(LLDrawPool::POOL_SIMPLE);
+	getPool(LLDrawPool::POOL_BUMP);
+
 	mTrianglesDrawnStat.reset();
 	resetFrameStats();
 
@@ -248,43 +268,15 @@ void LLPipeline::init()
 	mRenderFeatureMask = 0;	// All features start off
 	mRenderDebugMask = 0;	// All debug starts off
 
+	mOldRenderDebugMask = mRenderDebugMask;
+	
 	mBackfaceCull = TRUE;
 
-	// Disable AGP initially.
-	mRenderFeatureMask &= ~RENDER_FEATURE_AGP;
-
 	stop_glerror();
 	
 	// Enable features
-
-	mUseVBO = gSavedSettings.getBOOL("RenderUseVBO");
-
-	// Allocate the shared buffers for software skinning
-	for(S32 i=0; i < mBufferCount; i++)
-	{
-		mBufferMemory[i] = new LLAGPArray<U8>;
-		mBufferMemory[i]->reserve_block(AVATAR_VERTEX_BYTES*AVATAR_BUFFER_ELEMENTS);
-	}
-
-	if (gFeatureManagerp->isFeatureAvailable("RenderAGP"))
-	{
-		setUseAGP(gSavedSettings.getBOOL("RenderUseAGP") && gGLManager.mHasAnyAGP);
-	}
-	else
-	{
-		setUseAGP(FALSE);
-	}
-
 	stop_glerror();
-	
-	for(S32 i=0; i < mBufferCount; i++)
-	{
-		if (!mBufferMemory[i]->isAGP() && usingAGP())
-		{
-			llwarns << "pipeline buffer memory is non-AGP when AGP available!" << llendl;
-		}
-	}
-	
+		
 	setShaders();
 }
 
@@ -307,7 +299,17 @@ void LLPipeline::cleanup()
 	{
 		pool_set_t::iterator curiter = iter++;
 		LLDrawPool* poolp = *curiter;
-		if (poolp->mReferences.empty())
+		if (poolp->isFacePool())
+		{
+			LLFacePool* face_pool = (LLFacePool*) poolp;
+			if (face_pool->mReferences.empty())
+			{
+				mPools.erase(curiter);
+				removeFromQuickLookup( poolp );
+				delete poolp;
+			}
+		}
+		else
 		{
 			mPools.erase(curiter);
 			removeFromQuickLookup( poolp );
@@ -315,10 +317,6 @@ void LLPipeline::cleanup()
 		}
 	}
 	
-	if (!mSimplePools.empty())
-	{
-		llwarns << "Simple Pools not cleaned up" << llendl;
-	}
 	if (!mTerrainPools.empty())
 	{
 		llwarns << "Terrain Pools not cleaned up" << llendl;
@@ -327,208 +325,107 @@ void LLPipeline::cleanup()
 	{
 		llwarns << "Tree Pools not cleaned up" << llendl;
 	}
-	if (!mTreeNewPools.empty())
-	{
-		llwarns << "TreeNew Pools not cleaned up" << llendl;
-	}
-	if (!mBumpPools.empty())
-	{
-		llwarns << "Bump Pools not cleaned up" << llendl;
-	}
+		
 	delete mAlphaPool;
 	mAlphaPool = NULL;
+	delete mAlphaPoolPostWater;
+	mAlphaPoolPostWater = NULL;
 	delete mSkyPool;
 	mSkyPool = NULL;
 	delete mStarsPool;
 	mStarsPool = NULL;
-	delete mCloudsPool;
-	mCloudsPool = NULL;
 	delete mTerrainPool;
 	mTerrainPool = NULL;
 	delete mWaterPool;
 	mWaterPool = NULL;
 	delete mGroundPool;
 	mGroundPool = NULL;
-	delete mHUDPool;
-	mHUDPool = NULL;
+	delete mSimplePool;
+	mSimplePool = NULL;
+	delete mBumpPool;
+	mBumpPool = NULL;
 
-	mBloomImagep = NULL;
-	mBloomImage2p = NULL;
-	mFaceSelectImagep = NULL;
-	mAlphaSizzleImagep = NULL;
-
-	for(S32 i=0; i < mBufferCount; i++)
+	if (mCubeBuffer)
 	{
-		delete mBufferMemory[i];
-		mBufferMemory[i] = NULL;
+		delete mCubeBuffer;
+		mCubeBuffer = NULL;
 	}
 
-	delete mObjectPartition;
-	mObjectPartition = NULL;
-
-	if (mAGPMemPool && mGlobalFence)
+	if (mCubeList)
 	{
-		mAGPMemPool->deleteFence(mGlobalFence);
-		mGlobalFence = 0;
+		glDeleteLists(mCubeList, 1);
+		mCubeList = 0;
 	}
-	delete mAGPMemPool;
-	mAGPMemPool = NULL;
-}
-
-//============================================================================
 
-BOOL LLPipeline::initAGP()
-{
-	LLMemType mt(LLMemType::MTYPE_PIPELINE);
-	
-	mAGPMemPool = LLAGPMemPool::createPool(sAGPMaxPoolSize, mUseVBO);
+	mBloomImagep = NULL;
+	mBloomImage2p = NULL;
+	mFaceSelectImagep = NULL;
+	mAlphaSizzleImagep = NULL;
 
-	if (!mAGPMemPool)
+	for (S32 i = 0; i < NUM_PARTITIONS-1; i++)
 	{
-		llinfos << "Warning! Couldn't allocate AGP memory!" << llendl;
-		llinfos << "Disabling AGP!" << llendl;
-		mAGPMemPool = NULL;
-		mRenderFeatureMask &= ~RENDER_FEATURE_AGP; // Need to disable the using AGP flag
-		return FALSE;
-	}
-	else if (!mAGPMemPool->getSize())
-	{
-		llinfos << "Warning!  Unable to allocate AGP memory! Disabling AGP" << llendl;
-		delete mAGPMemPool;
-		mAGPMemPool = NULL;
-		mRenderFeatureMask &= ~RENDER_FEATURE_AGP; // Need to disable the using AGP flag
-		return FALSE;
-	}
-	else
-	{
-		llinfos << "Allocated " << mAGPMemPool->getSize() << " bytes of AGP memory" << llendl;
-		mAGPMemPool->bind();
-
-		if (mAGPMemPool->getSize() < MIN_AGP_SIZE)
-		{
-			llwarns << "Not enough AGP memory!" << llendl;
-			delete mAGPMemPool;
-			mAGPMemPool = NULL;
-			mRenderFeatureMask &= ~RENDER_FEATURE_AGP; // Need to disable the using AGP flag
-			return FALSE;
-		}
-
-
-	    if (mAGPMemPool)
-	    {
-	        // Create the fence that we use for global synchronization.
-	        mGlobalFence = mAGPMemPool->createFence();
-	    }
-		return TRUE;
+		delete mObjectPartition[i];
 	}
+	mObjectPartition.clear();
 
+	mGroupQ.clear();
+	mVisibleList.clear();
+	mVisibleGroups.clear();
+	mDrawableGroups.clear();
+	mActiveGroups.clear();
+	mVisibleBridge.clear();
+	mMovedBridge.clear();
+	mOccludedBridge.clear();
+	mAlphaGroups.clear();
+	clearRenderMap();
 }
 
-void LLPipeline::cleanupAGP()
-{
-	int i;
-	for(i=0; i < mBufferCount; i++)
-	{
-		mBufferMemory[i]->deleteFence(mBufferFence[i]);
-		mBufferMemory[i]->setUseAGP(FALSE);
-	}
-	
-	flushAGPMemory();
-	if (mAGPMemPool && mGlobalFence)
-	{
-	    mAGPMemPool->deleteFence(mGlobalFence);
-	    mGlobalFence = 0;
-	}
-	delete mAGPMemPool;
-	mAGPMemPool = NULL;
-}
+//============================================================================
 
-BOOL LLPipeline::usingAGP() const
+void LLPipeline::destroyGL() 
 {
-	return (mRenderFeatureMask & RENDER_FEATURE_AGP) ? TRUE : FALSE; 
-}
+	stop_glerror();
+	unloadShaders();
+	mHighlightFaces.clear();
+	mGroupQ.clear();
+	mVisibleList.clear();
+	mVisibleGroups.clear();
+	mDrawableGroups.clear();
+	mActiveGroups.clear();
+	mVisibleBridge.clear();
+	mOccludedBridge.clear();
+	mAlphaGroups.clear();
+	clearRenderMap();
+	resetVertexBuffers();
 
-void LLPipeline::setUseAGP(const BOOL use_agp)
-{
-	LLMemType mt(LLMemType::MTYPE_PIPELINE);
-	
-	if (use_agp == usingAGP())
+	if (mCubeBuffer)
 	{
-		return;
+		delete mCubeBuffer;
+		mCubeBuffer = NULL;
 	}
-	else if (use_agp)
-	{
-		mRenderFeatureMask |= RENDER_FEATURE_AGP;
-		initAGP();
 
-		// Forces us to allocate an AGP memory block immediately.
-		int i;
-		for(i=0; i < mBufferCount; i++)
-		{
-			mBufferMemory[i]->setUseAGP(use_agp);
-			mBufferMemory[i]->realloc(mBufferMemory[i]->getMax());
-			mBufferFence[i] = mBufferMemory[i]->createFence();
-		}
-		
-		// Must be done AFTER you initialize AGP
-		for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter)
-		{
-			LLDrawPool *poolp = *iter;
-			poolp->setUseAGP(use_agp);
-		}
-	}
-	else
+	if (mCubeList)
 	{
-		unbindAGP();
-		mRenderFeatureMask &= ~RENDER_FEATURE_AGP;
-
-		// Must be done BEFORE you blow away AGP
-		for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter)
-		{
-			LLDrawPool *poolp = *iter;
-			poolp->setUseAGP(use_agp);
-		}
-
-		int i;
-		for(i=0; i < mBufferCount; i++)
-		{
-			if (mBufferMemory[i])
-			{
-				mBufferMemory[i]->setUseAGP(use_agp);
-				mBufferMemory[i]->deleteFence(mBufferFence[i]);
-				mBufferFence[i] = 0;
-			}
-			else
-			{
-				llerrs << "setUseAGP without buffer memory" << llendl;
-			}
-		}
-		
-		cleanupAGP();
+		glDeleteLists(mCubeList, 1);
+		mCubeList = 0;
 	}
-
-}
-
-//============================================================================
-
-void LLPipeline::destroyGL() 
-{
-	setUseAGP(FALSE);
-	stop_glerror();
-	unloadShaders();
-	mHighlightFaces.reset();
 }
 
 void LLPipeline::restoreGL() 
 {
+	resetVertexBuffers();
+
 	if (mVertexShadersEnabled)
 	{
 		setShaders();
 	}
 	
-	if (mObjectPartition)
+	for (U32 i = 0; i < mObjectPartition.size()-1; i++)
 	{
-		mObjectPartition->restoreGL();
+		if (mObjectPartition[i])
+		{
+			mObjectPartition[i]->restoreGL();
+		}
 	}
 }
 
@@ -596,7 +493,7 @@ GLhandleARB LLPipeline::loadShader(const LLString& filename, S32 cls, GLenum typ
 		fname << gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "shaders/class");
 		fname << gpu_class << "/" << filename;
 		
-		llinfos << "Looking in " << fname.str().c_str() << llendl;
+// 		llinfos << "Looking in " << fname.str().c_str() << llendl;
 		file = fopen(fname.str().c_str(), "r");		/* Flawfinder: ignore */
 		if (file)
 		{
@@ -771,6 +668,11 @@ BOOL LLPipeline::validateProgramObject(GLhandleARB obj)
 
 void LLPipeline::setShaders()
 {
+	sDynamicReflections = gSavedSettings.getBOOL("RenderDynamicReflections");
+
+	//hack to reset buffers that change behavior with shaders
+	resetVertexBuffers();
+
 	if (gViewerWindow)
 	{
 		gViewerWindow->setCursor(UI_CURSOR_WAIT);
@@ -789,6 +691,8 @@ void LLPipeline::setShaders()
 	{
 		S32 light_class = 2;
 		S32 env_class = 2;
+		S32 obj_class = 0;
+
 		if (getLightingDetail() == 0)
 		{
 			light_class = 1;
@@ -798,7 +702,11 @@ void LLPipeline::setShaders()
 		mMaxVertexShaderLevel[SHADER_LIGHTING] = light_class;
 		mVertexShaderLevel[SHADER_ENVIRONMENT] = env_class;
 		mMaxVertexShaderLevel[SHADER_ENVIRONMENT] = env_class;
+		mVertexShaderLevel[SHADER_OBJECT] = obj_class;
+		mMaxVertexShaderLevel[SHADER_OBJECT] = obj_class;
+
 		BOOL loaded = loadShadersLighting();
+
 		if (loaded)
 		{
 			mVertexShadersEnabled = TRUE;
@@ -806,7 +714,7 @@ void LLPipeline::setShaders()
 
 			// Load all shaders to set max levels
 			loadShadersEnvironment();
-		
+			loadShadersObject();
 			// Load max avatar shaders to set the max level
 			mVertexShaderLevel[SHADER_AVATAR] = 3;
 			mMaxVertexShaderLevel[SHADER_AVATAR] = 3;
@@ -878,6 +786,7 @@ BOOL LLPipeline::canUseVertexShaders()
 void LLPipeline::unloadShaders()
 {
 	mObjectSimpleProgram.unload();
+	mObjectShinyProgram.unload();
 	mObjectBumpProgram.unload();
 	mObjectAlphaProgram.unload();
 	mWaterProgram.unload();
@@ -919,7 +828,8 @@ BOOL LLPipeline::loadShaders()
 		light_class = 2; // Use medium lighting shader
 	}
 	mVertexShaderLevel[SHADER_LIGHTING] = light_class;
-	mVertexShaderLevel[SHADER_OBJECT] = llmin(mMaxVertexShaderLevel[SHADER_OBJECT], gSavedSettings.getS32("VertexShaderLevelObject"));
+	//mVertexShaderLevel[SHADER_OBJECT] = llmin(mMaxVertexShaderLevel[SHADER_OBJECT], gSavedSettings.getS32("VertexShaderLevelObject"));
+	mVertexShaderLevel[SHADER_OBJECT] = 0;
 	mVertexShaderLevel[SHADER_AVATAR] = llmin(mMaxVertexShaderLevel[SHADER_AVATAR], gSavedSettings.getS32("VertexShaderLevelAvatar"));
 	mVertexShaderLevel[SHADER_ENVIRONMENT] = llmin(mMaxVertexShaderLevel[SHADER_ENVIRONMENT], gSavedSettings.getS32("VertexShaderLevelEnvironment"));
 	mVertexShaderLevel[SHADER_INTERFACE] = mMaxVertexShaderLevel[SHADER_INTERFACE];
@@ -1100,12 +1010,14 @@ BOOL LLPipeline::loadShadersObject()
 
 	if (mVertexShaderLevel[SHADER_OBJECT] == 0)
 	{
+		mObjectShinyProgram.unload();
 		mObjectSimpleProgram.unload();
 		mObjectBumpProgram.unload();
 		mObjectAlphaProgram.unload();
 		return FALSE;
 	}
-	
+
+#if 0
 	if (success)
 	{
 		//load object (volume/tree) vertex shader
@@ -1166,6 +1078,28 @@ BOOL LLPipeline::loadShadersObject()
 			llwarns << "Failed to load " << alphavertex << llendl;
 		}
 	}
+#endif
+
+	if (success)
+	{
+		//load shiny vertex shader
+		std::string shinyvertex = "objects/shinyV.glsl";
+		std::string shinyfragment = "objects/shinyF.glsl";
+		mObjectShinyProgram.mProgramObject = glCreateProgramObjectARB();
+		mObjectShinyProgram.attachObjects(baseObjects, baseCount);
+		mObjectShinyProgram.attachObject(loadShader(shinyvertex, SHADER_OBJECT, GL_VERTEX_SHADER_ARB));
+		mObjectShinyProgram.attachObject(loadShader(shinyfragment, SHADER_OBJECT, GL_FRAGMENT_SHADER_ARB));
+
+		success = mObjectShinyProgram.mapAttributes();
+		if (success)
+		{
+			success = mObjectShinyProgram.mapUniforms(LLPipeline::sShinyUniforms, LLPipeline::sShinyUniformCount);
+		}
+		if( !success )
+		{
+			llwarns << "Failed to load " << shinyvertex << llendl;
+		}
+	}
 
 	if( !success )
 	{
@@ -1322,11 +1256,11 @@ void LLPipeline::enableShadows(const BOOL enable_shadows)
 
 S32 LLPipeline::getMaxLightingDetail() const
 {
-	if (mVertexShaderLevel[SHADER_OBJECT] >= LLDrawPoolSimple::SHADER_LEVEL_LOCAL_LIGHTS)
+	/*if (mVertexShaderLevel[SHADER_OBJECT] >= LLDrawPoolSimple::SHADER_LEVEL_LOCAL_LIGHTS)
 	{
 		return 3;
 	}
-	else
+	else*/
 	{
 		return 1;
 	}
@@ -1356,79 +1290,63 @@ S32 LLPipeline::setLightingDetail(S32 level)
 	return mLightingDetail;
 }
 
-LLAGPMemBlock *LLPipeline::allocAGPFromPool(const S32 bytes, const U32 target)
+class LLOctreeDirtyTexture : public LLOctreeTraveler<LLDrawable>
 {
-	LLMemType mt(LLMemType::MTYPE_PIPELINE);
-	
-	if (!mAGPMemPool)
-	{
-		llwarns << "Attempting to allocate AGP memory when AGP disabled!" << llendl;
-		return NULL;
-	}
-	else
+public:
+	const std::set<LLViewerImage*>& mTextures;
+
+	LLOctreeDirtyTexture(const std::set<LLViewerImage*>& textures) : mTextures(textures) { }
+
+	virtual void visit(const LLOctreeState<LLDrawable>* state)
 	{
-		if (mUseVBO)
+		LLSpatialGroup* group = (LLSpatialGroup*) state->getNode()->getListener(0);
+
+		if (!group->isState(LLSpatialGroup::GEOM_DIRTY) && !group->getData().empty())
 		{
-			return ((LLAGPMemPoolARB*) mAGPMemPool)->allocBlock(bytes, target);
+			for (LLSpatialGroup::draw_map_t::iterator i = group->mDrawMap.begin(); i != group->mDrawMap.end(); ++i)
+			{
+				for (std::vector<LLDrawInfo*>::iterator j = i->second.begin(); j != i->second.end(); ++j)
+				{
+					LLDrawInfo* params = *j;
+					if (mTextures.find(params->mTexture) != mTextures.end())
+					{ 
+						group->setState(LLSpatialGroup::GEOM_DIRTY);
+						gPipeline.markRebuild(group);
+					}
+				}
+			}
 		}
-		else
+
+		for (LLSpatialGroup::bridge_list_t::iterator i = group->mBridgeList.begin(); i != group->mBridgeList.end(); ++i)
 		{
-			return mAGPMemPool->allocBlock(bytes);
+			LLSpatialBridge* bridge = *i;
+			traverse(bridge->mOctree);
 		}
 	}
-}
-
-
-void LLPipeline::unbindAGP()
-{
-	if (mAGPMemPool && mAGPBound)
-	{
-		mAGPMemPool->disable();
-		mAGPBound = FALSE;
-	}
-}
-
-void LLPipeline::bindAGP()
-{
-	LLMemType mt(LLMemType::MTYPE_PIPELINE);
-	
-	if (mAGPMemPool && !mAGPBound && usingAGP())
-	{
-		mAGPMemPool->enable();
-		mAGPBound = TRUE;
-	}
-}
-
-U8* LLPipeline::bufferGetScratchMemory(void)
-{
-	LLMemType mt(LLMemType::MTYPE_PIPELINE);
-	return(mBufferMemory[mBufferIndex]->getScratchMemory());
-}
-
-void LLPipeline::bufferWaitFence(void)
-{
-	mBufferMemory[mBufferIndex]->waitFence(mBufferFence[mBufferIndex]);
-}
-
-void LLPipeline::bufferSendFence(void)
-{
-	mBufferMemory[mBufferIndex]->sendFence(mBufferFence[mBufferIndex]);
-}
+};
 
-void LLPipeline::bufferRotate(void)
+// Called when a texture changes # of channels (causes faces to move to alpha pool)
+void LLPipeline::dirtyPoolObjectTextures(const std::set<LLViewerImage*>& textures)
 {
-	mBufferIndex++;
-	if(mBufferIndex >= mBufferCount)
-		mBufferIndex = 0;
-}
+	// *TODO: This is inefficient and causes frame spikes; need a better way to do this
+	//        Most of the time is spent in dirty.traverse.
 
-// Called when a texture changes # of channels (rare, may cause faces to move to alpha pool)
-void LLPipeline::dirtyPoolObjectTextures(const LLViewerImage *texturep)
-{
 	for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter)
 	{
 		LLDrawPool *poolp = *iter;
-		poolp->dirtyTexture(texturep);
+		if (poolp->isFacePool())
+		{
+			((LLFacePool*) poolp)->dirtyTextures(textures);
+		}
+	}
+	
+	LLOctreeDirtyTexture dirty(textures);
+	for (U32 i = 0; i < mObjectPartition.size(); i++)
+	{
+		if (mObjectPartition[i])
+		{
+			dirty.traverse(mObjectPartition[i]->mOctree);
+		}
 	}
 }
 
@@ -1438,33 +1356,29 @@ LLDrawPool *LLPipeline::findPool(const U32 type, LLViewerImage *tex0)
 	switch( type )
 	{
 	case LLDrawPool::POOL_SIMPLE:
-		poolp = get_if_there(mSimplePools, (uintptr_t)tex0, (LLDrawPool*)0 );
+		poolp = mSimplePool;
 		break;
 
 	case LLDrawPool::POOL_TREE:
 		poolp = get_if_there(mTreePools, (uintptr_t)tex0, (LLDrawPool*)0 );
 		break;
 
-	case LLDrawPool::POOL_TREE_NEW:
-		poolp = get_if_there(mTreeNewPools, (uintptr_t)tex0, (LLDrawPool*)0 );
-		break;
-
 	case LLDrawPool::POOL_TERRAIN:
 		poolp = get_if_there(mTerrainPools, (uintptr_t)tex0, (LLDrawPool*)0 );
 		break;
 
 	case LLDrawPool::POOL_BUMP:
-		poolp = get_if_there(mBumpPools, (uintptr_t)tex0, (LLDrawPool*)0 );
-		break;
-
-	case LLDrawPool::POOL_MEDIA:
-		poolp = get_if_there(mMediaPools, (uintptr_t)tex0, (LLDrawPool*)0 );
+		poolp = mBumpPool;
 		break;
 
 	case LLDrawPool::POOL_ALPHA:
 		poolp = mAlphaPool;
 		break;
 
+	case LLDrawPool::POOL_ALPHA_POST_WATER:
+		poolp = mAlphaPoolPostWater;
+		break;
+
 	case LLDrawPool::POOL_AVATAR:
 		break; // Do nothing
 
@@ -1476,10 +1390,6 @@ LLDrawPool *LLPipeline::findPool(const U32 type, LLViewerImage *tex0)
 		poolp = mStarsPool;
 		break;
 
-	case LLDrawPool::POOL_CLOUDS:
-		poolp = mCloudsPool;
-		break;
-
 	case LLDrawPool::POOL_WATER:
 		poolp = mWaterPool;
 		break;
@@ -1488,10 +1398,6 @@ LLDrawPool *LLPipeline::findPool(const U32 type, LLViewerImage *tex0)
 		poolp = mGroundPool;
 		break;
 
-	case LLDrawPool::POOL_HUD:
-		poolp = mHUDPool;
-		break;
-
 	default:
 		llassert(0);
 		llerrs << "Invalid Pool Type in  LLPipeline::findPool() type=" << type << llendl;
@@ -1522,29 +1428,37 @@ LLDrawPool *LLPipeline::getPool(const U32 type,	LLViewerImage *tex0)
 LLDrawPool* LLPipeline::getPoolFromTE(const LLTextureEntry* te, LLViewerImage* imagep)
 {
 	LLMemType mt(LLMemType::MTYPE_PIPELINE);
+	U32 type = getPoolTypeFromTE(te, imagep);
+	return gPipeline.getPool(type, imagep);
+}
+
+//static 
+U32 LLPipeline::getPoolTypeFromTE(const LLTextureEntry* te, LLViewerImage* imagep)
+{
+	LLMemType mt(LLMemType::MTYPE_PIPELINE);
+	
+	if (!te || !imagep)
+	{
+		return 0;
+	}
+		
 	bool alpha = te->getColor().mV[3] < 0.999f;
 	if (imagep)
 	{
 		alpha = alpha || (imagep->getComponents() == 4) || (imagep->getComponents() == 2);
 	}
-#if 0 // Not currently used
-	if (te->getMediaFlags() == LLTextureEntry::MF_WEB_PAGE)
-	{
-		return gPipeline.getPool(LLDrawPool::POOL_MEDIA, imagep);
-	}
-	else
-#endif
+
 	if (alpha)
 	{
-		return gPipeline.getPool(LLDrawPool::POOL_ALPHA);
+		return LLDrawPool::POOL_ALPHA;
 	}
 	else if ((te->getBumpmap() || te->getShiny()))
 	{
-		return gPipeline.getPool(LLDrawPool::POOL_BUMP, imagep);
+		return LLDrawPool::POOL_BUMP;
 	}
 	else
 	{
-		return gPipeline.getPool(LLDrawPool::POOL_SIMPLE, imagep);
+		return LLDrawPool::POOL_SIMPLE;
 	}
 }
 
@@ -1575,14 +1489,20 @@ void LLPipeline::allocDrawable(LLViewerObject *vobj)
 }
 
 
-void LLPipeline::unlinkDrawable(LLDrawable *drawablep)
+void LLPipeline::unlinkDrawable(LLDrawable *drawable)
 {
 	LLFastTimer t(LLFastTimer::FTM_PIPELINE);
+
+	LLPointer<LLDrawable> drawablep = drawable; // make sure this doesn't get deleted before we are done
 	
 	// Based on flags, remove the drawable from the queues that it's on.
 	if (drawablep->isState(LLDrawable::ON_MOVE_LIST))
 	{
-		mMovedList.erase(drawablep);
+		LLDrawable::drawable_vector_t::iterator iter = std::find(mMovedList.begin(), mMovedList.end(), drawablep);
+		if (iter != mMovedList.end())
+		{
+			mMovedList.erase(iter);
+		}
 	}
 
 	if (drawablep->getSpatialGroup())
@@ -1612,9 +1532,6 @@ U32 LLPipeline::addObject(LLViewerObject *vobj)
 
 	llassert(drawablep);
 
-	//mCompleteSet.put(drawable);
-	//gResyncObjects = TRUE;
-
 	if (vobj->getParent())
 	{
 		vobj->setDrawableParent(((LLViewerObject*)vobj->getParent())->mDrawable); // LLPipeline::addObject 1
@@ -1624,28 +1541,6 @@ U32 LLPipeline::addObject(LLViewerObject *vobj)
 		vobj->setDrawableParent(NULL); // LLPipeline::addObject 2
 	}
 
-	
-	if ((!drawablep->getVOVolume()) &&
-		(vobj->getPCode() != LLViewerObject::LL_VO_SKY) &&
-		(vobj->getPCode() != LLViewerObject::LL_VO_STARS) &&
-		(vobj->getPCode() != LLViewerObject::LL_VO_GROUND))
-	{
-		drawablep->getSpatialPartition()->put(drawablep);
-		if (!drawablep->getSpatialGroup())
-		{
-#ifdef LL_RELEASE_FOR_DOWNLOAD
-			llwarns << "Failure adding drawable to object partition!" << llendl;
-#else
-			llerrs << "Failure adding drawable to object partition!" << llendl;
-#endif
-		}
-	}
-	else
-	{
-		markMoved(drawablep);
-	}
-	
-	markMaterialed(drawablep);
 	markRebuild(drawablep, LLDrawable::REBUILD_ALL, TRUE);
 
 	return 1;
@@ -1654,11 +1549,27 @@ U32 LLPipeline::addObject(LLViewerObject *vobj)
 
 void LLPipeline::resetFrameStats()
 {
+	mCompilesStat.addValue(sCompiles);
+	mLightingChangesStat.addValue(mLightingChanges);
+	mGeometryChangesStat.addValue(mGeometryChanges);
+	mTrianglesDrawnStat.addValue(mTrianglesDrawn/1000.f);
+	mVerticesRelitStat.addValue(mVerticesRelit);
+	mNumVisibleFacesStat.addValue(mNumVisibleFaces);
+	mNumVisibleDrawablesStat.addValue((S32)mVisibleList.size());
+
+	mTrianglesDrawn = 0;
 	sCompiles        = 0;
 	mVerticesRelit   = 0;
 	mLightingChanges = 0;
 	mGeometryChanges = 0;
 	mNumVisibleFaces = 0;
+
+	if (mOldRenderDebugMask != mRenderDebugMask)
+	{
+		gObjectList.clearDebugText();
+		mOldRenderDebugMask = mRenderDebugMask;
+	}
+		
 }
 
 //external functions for asynchronous updating
@@ -1683,7 +1594,7 @@ void LLPipeline::updateMoveDampedAsync(LLDrawable* drawablep)
 	// Put on move list so that EARLY_MOVE gets cleared
 	if (!drawablep->isState(LLDrawable::ON_MOVE_LIST))
 	{
-		mMovedList.insert(drawablep);
+		mMovedList.push_back(drawablep);
 		drawablep->setState(LLDrawable::ON_MOVE_LIST);
 	}
 }
@@ -1709,28 +1620,17 @@ void LLPipeline::updateMoveNormalAsync(LLDrawable* drawablep)
 	// Put on move list so that EARLY_MOVE gets cleared
 	if (!drawablep->isState(LLDrawable::ON_MOVE_LIST))
 	{
-		mMovedList.insert(drawablep);
+		mMovedList.push_back(drawablep);
 		drawablep->setState(LLDrawable::ON_MOVE_LIST);
 	}
 }
 
-void LLPipeline::updateMove()
+void LLPipeline::updateMovedList(LLDrawable::drawable_vector_t& moved_list)
 {
-	mObjectPartition->mOctree->validate();
-	LLFastTimer t(LLFastTimer::FTM_UPDATE_MOVE);
-	LLMemType mt(LLMemType::MTYPE_PIPELINE);
-
-	if (gSavedSettings.getBOOL("FreezeTime"))
+	for (LLDrawable::drawable_vector_t::iterator iter = moved_list.begin();
+		 iter != moved_list.end(); )
 	{
-		return;
-	}
-
-	mMoveChangesStat.addValue((F32)mMovedList.size());
-
-	for (LLDrawable::drawable_set_t::iterator iter = mMovedList.begin();
-		 iter != mMovedList.end(); )
-	{
-		LLDrawable::drawable_set_t::iterator curiter = iter++;
+		LLDrawable::drawable_vector_t::iterator curiter = iter++;
 		LLDrawable *drawablep = *curiter;
 		BOOL done = TRUE;
 		if (!drawablep->isDead() && (!drawablep->isState(LLDrawable::EARLY_MOVE)))
@@ -1740,10 +1640,36 @@ void LLPipeline::updateMove()
 		drawablep->clearState(LLDrawable::EARLY_MOVE | LLDrawable::MOVE_UNDAMPED);
 		if (done)
 		{
-			mMovedList.erase(curiter);
 			drawablep->clearState(LLDrawable::ON_MOVE_LIST);
+			iter = moved_list.erase(curiter);
+		}
+	}
+}
+
+void LLPipeline::updateMove()
+{
+	//LLFastTimer t(LLFastTimer::FTM_UPDATE_MOVE);
+	LLMemType mt(LLMemType::MTYPE_PIPELINE);
+
+	if (gSavedSettings.getBOOL("FreezeTime"))
+	{
+		return;
+	}
+
+	mMoveChangesStat.addValue((F32)mMovedList.size());
+
+	for (LLDrawable::drawable_set_t::iterator iter = mRetexturedList.begin();
+		 iter != mRetexturedList.end(); ++iter)
+	{
+		LLDrawable* drawablep = *iter;
+		if (drawablep && !drawablep->isDead())
+		{
+			drawablep->updateTexture();
 		}
 	}
+	mRetexturedList.clear();
+
+	updateMovedList(mMovedList);
 
 	for (LLDrawable::drawable_set_t::iterator iter = mActiveQ.begin();
 		 iter != mActiveQ.end(); )
@@ -1752,7 +1678,8 @@ void LLPipeline::updateMove()
 		LLDrawable* drawablep = *curiter;
 		if (drawablep && !drawablep->isDead()) 
 		{
-			if (drawablep->mQuietCount++ > MAX_ACTIVE_OBJECT_QUIET_FRAMES && 
+			if (drawablep->isRoot() && 
+				drawablep->mQuietCount++ > MAX_ACTIVE_OBJECT_QUIET_FRAMES && 
 				(!drawablep->getParent() || !drawablep->getParent()->isActive()))
 			{
 				drawablep->makeStatic(); // removes drawable and its children from mActiveQ
@@ -1765,172 +1692,168 @@ void LLPipeline::updateMove()
 		}
 	}
 
-	for (LLDrawable::drawable_set_t::iterator iter = mRetexturedList.begin();
-		 iter != mRetexturedList.end(); ++iter)
-	{
-		LLDrawable* drawablep = *iter;
-		if (drawablep && !drawablep->isDead())
-		{
-			drawablep->updateTexture();
-		}
-	}
-	mRetexturedList.clear();
-
-	for (LLDrawable::drawable_set_t::iterator iter = mRematerialedList.begin();
-		 iter != mRematerialedList.end(); ++iter)
+	//balance octrees
 	{
-		LLDrawable* drawablep = *iter;
-		if (drawablep && !drawablep->isDead())
+ 		LLFastTimer ot(LLFastTimer::FTM_OCTREE_BALANCE);
+		for (U32 i = 0; i < mObjectPartition.size()-1; i++)
 		{
-			drawablep->updateMaterial();
+			if (mObjectPartition[i])
+			{
+				mObjectPartition[i]->mOctree->balance();
+			}
 		}
 	}
-	mRematerialedList.clear();
-
-	if (mObjectPartition->mOctree)
-	{
-		//balance octree
- 		LLFastTimer ot(LLFastTimer::FTM_OCTREE_BALANCE);
-		mObjectPartition->mOctree->validate();
-		mObjectPartition->mOctree->balance();
-		mObjectPartition->mOctree->validate();
-	}
 }
 
 /////////////////////////////////////////////////////////////////////////////
 // Culling and occlusion testing
 /////////////////////////////////////////////////////////////////////////////
 
-void LLPipeline::updateCull()
+//static
+F32 LLPipeline::calcPixelArea(LLVector3 center, LLVector3 size, LLCamera &camera)
 {
-	LLFastTimer t(LLFastTimer::FTM_CULL);
-	LLMemType mt(LLMemType::MTYPE_PIPELINE);
-
-	LLDrawable::incrementVisible();
-	mVisibleList.resize(0);
-	mVisibleList.reserve(ESTIMATED_VISIBLE_OBJECT_COUNT);
-	
-	gTrivialAccepts = 0;
+	LLVector3 lookAt = center - camera.getOrigin();
+	LLVector3 cross_vec = size * 2.f;	
+	F32 dist = lookAt.magVec();
 
-	if (mObjectPartition) 
+	//ramp down distance for nearby objects
+	if (dist < 16.f)
 	{
-		if (gSavedSettings.getBOOL("UseOcclusion") && gGLManager.mHasOcclusionQuery)
-		{
-			mObjectPartition->processOcclusion(gCamera);
-			stop_glerror();
-		}
-		mObjectPartition->cull(*gCamera);
+		dist /= 16.f;
+		dist *= dist;
+		dist *= 16.f;
 	}
-	
-	// Hack for avatars - warning - this is really FRAGILE! - djs 05/06/02
-	LLVOAvatar::updateAllAvatarVisiblity();
 
-	// If there are any other hacks here, make sure to add them to the
-	// standard pick code.
-
-	gMinObjectDistance = llclamp(gMinObjectDistance, MIN_NEAR_PLANE, MAX_NEAR_PLANE);
+	//get area of circle around node
+	F32 app_angle = atanf((cross_vec*0.5f).magVec()/dist);
+	F32 radius = app_angle*LLDrawable::sCurPixelAngle;
+	return radius*radius * 3.14159f;
+}
 
-	F32 water_height = gAgent.getRegion()->getWaterHeight();
-	F32 camera_height = gAgent.getCameraPositionAgent().mV[2];
-	if (fabs(camera_height - water_height) < 2.f)
-	{
-		gMinObjectDistance = MIN_NEAR_PLANE;
-	}
+void LLPipeline::updateCull(LLCamera& camera)
+{
+	LLFastTimer t(LLFastTimer::FTM_CULL);
+	LLMemType mt(LLMemType::MTYPE_PIPELINE);
 
-	gCamera->setNear(gMinObjectDistance);
+	mVisibleList.clear();
+	mVisibleGroups.clear();
+	mDrawableGroups.clear();
+	mActiveGroups.clear();
+	gTrivialAccepts = 0;
+	mVisibleBridge.clear();
 
-	// Disable near clip stuff for now...
+	processOcclusion(camera);
 
-	// now push it back out to max value
-	gMinObjectDistance = MIN_NEAR_PLANE;
+	for (U32 i = 0; i < mObjectPartition.size(); i++)
+	{	
+		if (mObjectPartition[i] && hasRenderType(mObjectPartition[i]->mDrawableType))
+		{
+			mObjectPartition[i]->cull(camera);
+		}
+	}
 
+	//do a terse update on some off-screen geometry
+	processGeometry(camera);
+	
 	if (gSky.mVOSkyp.notNull() && gSky.mVOSkyp->mDrawable.notNull())
 	{
 		// Hack for sky - always visible.
-		gSky.mVOSkyp->mDrawable->setVisible(*gCamera);
-		mVisibleList.push_back(gSky.mVOSkyp->mDrawable);
-		gSky.updateCull();
-		stop_glerror();
+		if (hasRenderType(LLPipeline::RENDER_TYPE_SKY)) 
+		{
+			gSky.mVOSkyp->mDrawable->setVisible(camera);
+			mVisibleList.push_back(gSky.mVOSkyp->mDrawable);
+			gSky.updateCull();
+			stop_glerror();
+		}
 	}
 	else
 	{
 		llinfos << "No sky drawable!" << llendl;
 	}
 
-	if (gSky.mVOGroundp.notNull() && gSky.mVOGroundp->mDrawable.notNull())
+	if (hasRenderType(LLPipeline::RENDER_TYPE_GROUND) && gSky.mVOGroundp.notNull() && gSky.mVOGroundp->mDrawable.notNull())
 	{
-		gSky.mVOGroundp->mDrawable->setVisible(*gCamera);
+		gSky.mVOGroundp->mDrawable->setVisible(camera);
 		mVisibleList.push_back(gSky.mVOGroundp->mDrawable);
 	}
+}
 
-	// add all HUD attachments
-	LLVOAvatar* my_avatarp = gAgent.getAvatarObject();
-	if (my_avatarp && my_avatarp->hasHUDAttachment())
+void LLPipeline::markNotCulled(LLSpatialGroup* group, LLCamera& camera, BOOL active)
+{
+	if (group->getData().empty())
+	{ 
+		return;
+	}
+	
+	if (!sSkipUpdate)
 	{
-		for (LLViewerJointAttachment* attachmentp = my_avatarp->mAttachmentPoints.getFirstData();
-			attachmentp;
-			attachmentp = my_avatarp->mAttachmentPoints.getNextData())
-		{
-			if (attachmentp->getIsHUDAttachment() && attachmentp->getObject(0))
-			{
-				LLViewerObject* objectp = attachmentp->getObject(0);
-				markVisible(objectp->mDrawable);
-				objectp->mDrawable->updateDistance(*gCamera);
-				for (S32 i = 0; i < (S32)objectp->mChildList.size(); i++)
-				{
-					LLViewerObject* childp = objectp->mChildList[i];
-					if (childp->mDrawable.notNull())
-					{
-						markVisible(childp->mDrawable);
-						childp->mDrawable->updateDistance(*gCamera);
-					}
-				}
-			}
-		}
+		group->updateDistance(camera);
 	}
-}
+	
+	const F32 MINIMUM_PIXEL_AREA = 16.f;
 
-void LLPipeline::markNotCulled(LLDrawable* drawablep, LLCamera& camera)
-{
-	if (drawablep->isVisible())
+	if (group->mPixelArea < MINIMUM_PIXEL_AREA)
 	{
 		return;
 	}
 	
-	// Tricky render mode to hide selected objects, but we definitely
-	// don't want to do any unnecessary pointer dereferences.  JC
-	if (gHideSelectedObjects)
-	{
-		if (drawablep->getVObj() && drawablep->getVObj()->isSelected())
+	group->mLastRenderTime = gFrameTimeSeconds;
+	if (!group->mSpatialPartition->mRenderByGroup)
+	{ //render by drawable
+		mDrawableGroups.push_back(group);
+		for (LLSpatialGroup::element_iter i = group->getData().begin(); i != group->getData().end(); ++i)
 		{
-			return;
+			markVisible(*i, camera);
 		}
 	}
-
-	if (drawablep && (hasRenderType(drawablep->mRenderType)))
-	{
-		if (!drawablep->isState(LLDrawable::INVISIBLE|LLDrawable::FORCE_INVISIBLE))
+	else
+	{ //render by group
+		if (active)
 		{
-			mVisibleList.push_back(drawablep);
-			drawablep->setVisible(camera, NULL, FALSE);
+			mActiveGroups.push_back(group);
 		}
-		else if (drawablep->isState(LLDrawable::CLEAR_INVISIBLE))
+		else
 		{
-			// clear invisible flag here to avoid single frame glitch
-			drawablep->clearState(LLDrawable::FORCE_INVISIBLE|LLDrawable::CLEAR_INVISIBLE);
+			mVisibleGroups.push_back(group);
+			for (LLSpatialGroup::bridge_list_t::iterator i = group->mBridgeList.begin(); i != group->mBridgeList.end(); ++i)
+			{
+				LLSpatialBridge* bridge = *i;
+				markVisible(bridge, camera);
+			}
 		}
 	}
 }
 
-void LLPipeline::doOcclusion()
+void LLPipeline::doOcclusion(LLCamera& camera)
 {
-	if (gSavedSettings.getBOOL("UseOcclusion") && gGLManager.mHasOcclusionQuery)
+	if (sUseOcclusion)
 	{
-		mObjectPartition->doOcclusion(gCamera);
+		for (U32 i = 0; i < mObjectPartition.size(); i++)
+		{
+			if (mObjectPartition[i] && hasRenderType(mObjectPartition[i]->mDrawableType))
+			{
+				mObjectPartition[i]->doOcclusion(&camera);
+			}
+		}
+		
+#if AGGRESSIVE_OCCLUSION
+		for (LLSpatialBridge::bridge_vector_t::iterator i = mVisibleBridge.begin(); i != mVisibleBridge.end(); ++i)
+		{
+			LLSpatialBridge* bridge = *i;
+			if (!bridge->isDead() && hasRenderType(bridge->mDrawableType))
+			{
+				glPushMatrix();
+				glMultMatrixf((F32*)bridge->mDrawable->getRenderMatrix().mMatrix);
+				LLCamera trans = bridge->transformCamera(camera);
+				bridge->doOcclusion(&trans);
+				glPopMatrix();
+				mOccludedBridge.push_back(bridge);
+			}
+		}
+#endif 
 	}
 }
 	
-
 BOOL LLPipeline::updateDrawableGeom(LLDrawable* drawablep, BOOL priority)
 {
 	BOOL update_complete = drawablep->updateGeometry(priority);
@@ -1955,19 +1878,31 @@ void LLPipeline::updateGeom(F32 max_dtime)
 	LLVOVolume::preUpdateGeom();
 
 	// Iterate through all drawables on the priority build queue,
-	for (LLDrawable::drawable_set_t::iterator iter = mBuildQ1.begin();
+	for (LLDrawable::drawable_list_t::iterator iter = mBuildQ1.begin();
 		 iter != mBuildQ1.end();)
 	{
-		LLDrawable::drawable_set_t::iterator curiter = iter++;
+		LLDrawable::drawable_list_t::iterator curiter = iter++;
 		LLDrawable* drawablep = *curiter;
-		BOOL update_complete = TRUE;
 		if (drawablep && !drawablep->isDead())
 		{
-			update_complete = updateDrawableGeom(drawablep, TRUE);
+			if (drawablep->isState(LLDrawable::IN_REBUILD_Q2))
+			{
+				drawablep->clearState(LLDrawable::IN_REBUILD_Q2);
+				LLDrawable::drawable_list_t::iterator find = std::find(mBuildQ2.begin(), mBuildQ2.end(), drawablep);
+				if (find != mBuildQ2.end())
+				{
+					mBuildQ2.erase(find);
+				}
+			}
+
+			if (updateDrawableGeom(drawablep, TRUE))
+			{
+				drawablep->clearState(LLDrawable::IN_REBUILD_Q1);
+				mBuildQ1.erase(curiter);
+			}
 		}
-		if (update_complete)
+		else
 		{
-			drawablep->clearState(LLDrawable::IN_REBUILD_Q1);
 			mBuildQ1.erase(curiter);
 		}
 	}
@@ -1978,20 +1913,34 @@ void LLPipeline::updateGeom(F32 max_dtime)
 	{
 		min_count = mBuildQ2.size();
 	}
-	else
-	{
-		mBuildQ2.sort(LLDrawable::CompareDistanceGreaterVisibleFirst());
-	}
-	
+		
 	S32 count = 0;
 	
 	max_dtime = llmax(update_timer.getElapsedTimeF32()+0.001f, max_dtime);
-		
+	LLSpatialGroup* last_group = NULL;
+	LLSpatialBridge* last_bridge = NULL;
+
 	for (LLDrawable::drawable_list_t::iterator iter = mBuildQ2.begin();
 		 iter != mBuildQ2.end(); )
 	{
 		LLDrawable::drawable_list_t::iterator curiter = iter++;
 		LLDrawable* drawablep = *curiter;
+
+		LLSpatialBridge* bridge = drawablep->isRoot() ? drawablep->getSpatialBridge() :
+									drawablep->getParent()->getSpatialBridge();
+
+		if (drawablep->getSpatialGroup() != last_group && 
+			(!last_bridge || bridge != last_bridge) &&
+			(update_timer.getElapsedTimeF32() >= max_dtime) && count > min_count)
+		{
+			break;
+		}
+
+		//make sure updates don't stop in the middle of a spatial group
+		//to avoid thrashing (objects are enqueued by group)
+		last_group = drawablep->getSpatialGroup();
+		last_bridge = bridge;
+
 		BOOL update_complete = TRUE;
 		if (drawablep && !drawablep->isDead())
 		{
@@ -2003,14 +1952,12 @@ void LLPipeline::updateGeom(F32 max_dtime)
 			drawablep->clearState(LLDrawable::IN_REBUILD_Q2);
 			mBuildQ2.erase(curiter);
 		}
-		if ((update_timer.getElapsedTimeF32() >= max_dtime) && count > min_count)
-		{
-			break;
-		}
-	}
+	}	
+
+	updateMovedList(mMovedBridge);
 }
 
-void LLPipeline::markVisible(LLDrawable *drawablep)
+void LLPipeline::markVisible(LLDrawable *drawablep, LLCamera& camera)
 {
 	LLMemType mt(LLMemType::MTYPE_PIPELINE);
 	if(!drawablep || drawablep->isDead())
@@ -2018,11 +1965,36 @@ void LLPipeline::markVisible(LLDrawable *drawablep)
 		llwarns << "LLPipeline::markVisible called with NULL drawablep" << llendl;
 		return;
 	}
-	if (!drawablep->isVisible())
+	
+	
+#if LL_DEBUG
+	if (drawablep->isSpatialBridge())
+	{
+		if (std::find(mVisibleBridge.begin(), mVisibleBridge.end(), (LLSpatialBridge*) drawablep) !=
+			mVisibleBridge.end())
+		{
+			llerrs << "Spatial bridge marked visible redundantly." << llendl;
+		}
+	}
+	else
+	{
+		if (std::find(mVisibleList.begin(), mVisibleList.end(), drawablep) !=
+			mVisibleList.end())
+		{
+			llerrs << "Drawable marked visible redundantly." << llendl;
+		}
+	}
+#endif
+
+	if (drawablep->isSpatialBridge())
+	{
+		mVisibleBridge.push_back((LLSpatialBridge*) drawablep);
+	}
+	else
 	{
-		drawablep->setVisible(*gCamera);
 		mVisibleList.push_back(drawablep);
 	}
+	drawablep->setVisible(camera);
 }
 
 void LLPipeline::markMoved(LLDrawable *drawablep, BOOL damped_motion)
@@ -2046,10 +2018,17 @@ void LLPipeline::markMoved(LLDrawable *drawablep, BOOL damped_motion)
 		markMoved(drawablep->getParent(), damped_motion);
 	}
 
-	
+
 	if (!drawablep->isState(LLDrawable::ON_MOVE_LIST))
 	{
-		mMovedList.insert(drawablep);
+		if (drawablep->isSpatialBridge())
+		{
+			mMovedBridge.push_back(drawablep);
+		}
+		else
+		{
+			mMovedList.push_back(drawablep);
+		}
 		drawablep->setState(LLDrawable::ON_MOVE_LIST);
 	}
 	if (damped_motion == FALSE)
@@ -2098,30 +2077,33 @@ void LLPipeline::shiftObjects(const LLVector3 &offset)
 	}
 	mShiftList.resize(0);
 
-	mObjectPartition->shift(offset);
+	for (U32 i = 0; i < mObjectPartition.size()-1; i++)
+	{
+		if (mObjectPartition[i])
+		{
+			mObjectPartition[i]->shift(offset);
+		}
+	}
 }
 
 void LLPipeline::markTextured(LLDrawable *drawablep)
 {
 	LLMemType mt(LLMemType::MTYPE_PIPELINE);
-	if (!drawablep->isDead())
+	if (drawablep && !drawablep->isDead())
 	{
 		mRetexturedList.insert(drawablep);
 	}
 }
 
-void LLPipeline::markMaterialed(LLDrawable *drawablep)
+void LLPipeline::markRebuild(LLSpatialGroup* group)
 {
-	LLMemType mt(LLMemType::MTYPE_PIPELINE);
-	if (!drawablep->isDead())
-	{
-		mRematerialedList.insert(drawablep);
-	}
+	mGroupQ.insert(group);
 }
 
 void LLPipeline::markRebuild(LLDrawable *drawablep, LLDrawable::EDrawableFlags flag, BOOL priority)
 {
 	LLMemType mt(LLMemType::MTYPE_PIPELINE);
+
 	if (drawablep && !drawablep->isDead())
 	{
 		if (!drawablep->isState(LLDrawable::BUILT))
@@ -2130,15 +2112,18 @@ void LLPipeline::markRebuild(LLDrawable *drawablep, LLDrawable::EDrawableFlags f
 		}
 		if (priority)
 		{
-			mBuildQ1.insert(drawablep);
-			drawablep->setState(LLDrawable::IN_REBUILD_Q1); // flag is not needed, just for debugging
+			if (!drawablep->isState(LLDrawable::IN_REBUILD_Q1))
+			{
+				mBuildQ1.push_back(drawablep);
+				drawablep->setState(LLDrawable::IN_REBUILD_Q1); // mark drawable as being in priority queue
+			}
 		}
 		else if (!drawablep->isState(LLDrawable::IN_REBUILD_Q2))
 		{
 			mBuildQ2.push_back(drawablep);
 			drawablep->setState(LLDrawable::IN_REBUILD_Q2); // need flag here because it is just a list
 		}
-		if (flag & LLDrawable::REBUILD_VOLUME)
+		if (flag & (LLDrawable::REBUILD_VOLUME | LLDrawable::REBUILD_POSITION))
 		{
 			drawablep->getVObj()->setChanged(LLXform::SILHOUETTE);
 		}
@@ -2165,111 +2150,406 @@ void LLPipeline::markRelight(LLDrawable *drawablep, const BOOL priority)
 	}
 }
 
-void LLPipeline::stateSort()
+void LLPipeline::stateSort(LLCamera& camera)
 {
 	LLFastTimer ftm(LLFastTimer::FTM_STATESORT);
 	LLMemType mt(LLMemType::MTYPE_PIPELINE);
 
+	for (LLSpatialGroup::sg_vector_t::iterator iter = mVisibleGroups.begin(); iter != mVisibleGroups.end(); ++iter)
+	{
+		stateSort(*iter, camera);
+	}
+		
+	for (LLSpatialBridge::bridge_vector_t::iterator i = mVisibleBridge.begin(); i != mVisibleBridge.end(); ++i)
+	{
+		LLSpatialBridge* bridge = *i;
+		if (!bridge->isDead())
+		{
+			stateSort(bridge, camera);
+		}
+	}
+
 	for (LLDrawable::drawable_vector_t::iterator iter = mVisibleList.begin();
 		 iter != mVisibleList.end(); iter++)
 	{
 		LLDrawable *drawablep = *iter;
-		if (drawablep->isDead())
+		if (!drawablep->isDead())
 		{
-			continue;
+			stateSort(drawablep, camera);
 		}
+	}
+
+	for (LLSpatialGroup::sg_vector_t::iterator iter = mActiveGroups.begin(); iter != mActiveGroups.end(); ++iter)
+	{
+		stateSort(*iter, camera);
+	}
 
-		if (!drawablep->isActive())
+	postSort(camera);
+}
+
+void LLPipeline::stateSort(LLSpatialGroup* group, LLCamera& camera)
+{
+	LLMemType mt(LLMemType::MTYPE_PIPELINE);
+	if (!sSkipUpdate && group->changeLOD())
+	{
+		for (LLSpatialGroup::element_iter i = group->getData().begin(); i != group->getData().end(); ++i)
 		{
-			drawablep->updateDistance(*gCamera);
+			LLDrawable* drawablep = *i;
+			stateSort(drawablep, camera);
 		}
+	}
+	
+	if (gFrameTimeSeconds - group->mLastUpdateTime > 4.f)
+	{
+		group->makeStatic();
+	}
+}
 
-		/*
-		if (!drawablep->isState(LLDrawable::BUILT))
+void LLPipeline::stateSort(LLSpatialBridge* bridge, LLCamera& camera)
+{
+	LLMemType mt(LLMemType::MTYPE_PIPELINE);
+	if (!sSkipUpdate)
+	{
+		bridge->updateDistance(camera);
+	}
+}
+
+void LLPipeline::stateSort(LLDrawable* drawablep, LLCamera& camera)
+{
+	LLMemType mt(LLMemType::MTYPE_PIPELINE);
+	LLFastTimer ftm(LLFastTimer::FTM_STATESORT_DRAWABLE);
+	
+	if (drawablep->isDead() || !hasRenderType(drawablep->getRenderType()))
+	{
+		return;
+	}
+	
+	if (gHideSelectedObjects)
+	{
+		if (drawablep->getVObj() &&
+			drawablep->getVObj()->isSelected())
 		{
-			// This geometry hasn't been rebuilt but it's visible, make sure it gets put on the rebuild list.
-			llerrs << "Visible object " << drawablep << ":" << drawablep->getVObj()->getPCodeString();
-			llcont << " visible but not built, put on rebuild" << llendl;
-			markRebuild(drawablep);
-			continue;
+			return;
 		}
-		*/
+	}
 
-		for (LLDrawable::face_list_t::iterator iter = drawablep->mFaces.begin();
-			 iter != drawablep->mFaces.end(); iter++)
+	if (drawablep && (hasRenderType(drawablep->mRenderType)))
+	{
+		if (!drawablep->isState(LLDrawable::INVISIBLE|LLDrawable::FORCE_INVISIBLE))
 		{
-			LLFace* facep = *iter;
-			if (facep->hasGeometry())
-			{
-				facep->getPool()->enqueue(facep);
-			}
+			drawablep->setVisible(camera, NULL, FALSE);
 		}
-		
-		if (sRenderPhysicalBeacons)
-		{
-			// Only show the beacon on the root object.
-			LLViewerObject *vobj = drawablep->getVObj();
-			if (vobj 
-				&& !vobj->isAvatar() 
-				&& !vobj->getParent()
-				&& vobj->usePhysics())
-			{
-				gObjectList.addDebugBeacon(vobj->getPositionAgent(), "", LLColor4(0.f, 1.f, 0.f, 0.5f), LLColor4(1.f, 1.f, 1.f, 0.5f));
-			}
+		else if (drawablep->isState(LLDrawable::CLEAR_INVISIBLE))
+		{
+			// clear invisible flag here to avoid single frame glitch
+			drawablep->clearState(LLDrawable::FORCE_INVISIBLE|LLDrawable::CLEAR_INVISIBLE);
 		}
+	}
 
-		if (sRenderScriptedBeacons)
+	if (!drawablep->isActive() && drawablep->isVisible())
+	{
+		if (!sSkipUpdate)
 		{
-			// Only show the beacon on the root object.
-			LLViewerObject *vobj = drawablep->getVObj();
-			if (vobj 
-				&& !vobj->isAvatar() 
-				&& !vobj->getParent()
-				&& vobj->flagScripted())
-			{
-				gObjectList.addDebugBeacon(vobj->getPositionAgent(), "", LLColor4(1.f, 0.f, 0.f, 0.5f), LLColor4(1.f, 1.f, 1.f, 0.5f));
-			}
+			drawablep->updateDistance(camera);
 		}
+	}
+	else if (drawablep->isAvatar() && drawablep->isVisible())
+	{
+		LLVOAvatar* vobj = (LLVOAvatar*) drawablep->getVObj();
+		vobj->updateVisibility(FALSE);
+	}
+
+	for (LLDrawable::face_list_t::iterator iter = drawablep->mFaces.begin();
+			iter != drawablep->mFaces.end(); iter++)
+	{
+		LLFace* facep = *iter;
 
-		if (sRenderParticleBeacons)
+		if (facep->hasGeometry())
 		{
-			// Look for attachments, objects, etc.
-			LLViewerObject *vobj = drawablep->getVObj();
-			if (vobj 
-				&& vobj->isParticleSource())
+			if (facep->getPool())
 			{
-				LLColor4 light_blue(0.5f, 0.5f, 1.f, 0.5f);
-				gObjectList.addDebugBeacon(vobj->getPositionAgent(), "", light_blue, LLColor4(1.f, 1.f, 1.f, 0.5f));
+				facep->getPool()->enqueue(facep);
+			}
+			else
+			{
+				break;
 			}
 		}
+	}
+	
+	
+	mNumVisibleFaces += drawablep->getNumFaces();
+}
 
-		// Draw physical objects in red.
-		if (gHUDManager->getShowPhysical())
+
+void LLPipeline::forAllDrawables(LLSpatialGroup::sg_vector_t& groups, void (*func)(LLDrawable*))
+{
+	for (LLSpatialGroup::sg_vector_t::iterator i = groups.begin(); i != groups.end(); ++i)
+	{
+		for (LLSpatialGroup::element_iter j = (*i)->getData().begin(); j != (*i)->getData().end(); ++j)
+		{
+			func(*j);	
+		}
+	}
+}
+
+void LLPipeline::forAllVisibleDrawables(void (*func)(LLDrawable*))
+{
+	forAllDrawables(mDrawableGroups, func);
+	forAllDrawables(mVisibleGroups, func);
+	forAllDrawables(mActiveGroups, func);
+}
+
+//function for creating scripted beacons
+void renderScriptedBeacons(LLDrawable* drawablep)
+{
+	LLViewerObject *vobj = drawablep->getVObj();
+	if (vobj 
+		&& !vobj->isAvatar() 
+		&& !vobj->getParent()
+		&& vobj->flagScripted())
+	{
+		gObjectList.addDebugBeacon(vobj->getPositionAgent(), "", LLColor4(1.f, 0.f, 0.f, 0.5f), LLColor4(1.f, 1.f, 1.f, 0.5f));
+	}
+}
+
+void renderPhysicalBeacons(LLDrawable* drawablep)
+{
+	LLViewerObject *vobj = drawablep->getVObj();
+	if (vobj 
+		&& !vobj->isAvatar() 
+		&& !vobj->getParent()
+		&& vobj->usePhysics())
+	{
+		gObjectList.addDebugBeacon(vobj->getPositionAgent(), "", LLColor4(0.f, 1.f, 0.f, 0.5f), LLColor4(1.f, 1.f, 1.f, 0.5f));
+	}
+}
+
+void renderParticleBeacons(LLDrawable* drawablep)
+{
+	// Look for attachments, objects, etc.
+	LLViewerObject *vobj = drawablep->getVObj();
+	if (vobj 
+		&& vobj->isParticleSource())
+	{
+		LLColor4 light_blue(0.5f, 0.5f, 1.f, 0.5f);
+		gObjectList.addDebugBeacon(vobj->getPositionAgent(), "", light_blue, LLColor4(1.f, 1.f, 1.f, 0.5f));
+	}
+}
+
+void LLPipeline::highlightPhysical(LLDrawable* drawablep)
+{
+	LLMemType mt(LLMemType::MTYPE_PIPELINE);
+	LLViewerObject *vobj;
+	vobj = drawablep->getVObj();
+	if (vobj && !vobj->isAvatar())
+	{
+		if (!vobj->isAvatar() && 
+			(vobj->usePhysics() || vobj->flagHandleTouch()))
+		{
+			S32 face_id;
+			for (face_id = 0; face_id < drawablep->getNumFaces(); face_id++)
+			{
+				gPipeline.mHighlightFaces.push_back(drawablep->getFace(face_id) );
+			}
+		}
+	}
+}
+
+void LLPipeline::postSort(LLCamera& camera)
+{
+	LLMemType mt(LLMemType::MTYPE_PIPELINE);
+	LLFastTimer ftm(LLFastTimer::FTM_STATESORT_POSTSORT);
+	//reset render data sets
+	clearRenderMap();
+	mAlphaGroups.clear();
+	mAlphaGroupsPostWater.clear();
+	
+	if (!gSavedSettings.getBOOL("RenderRippleWater") && hasRenderType(LLDrawPool::POOL_ALPHA))
+	{	//turn off clip plane for non-ripple water
+		toggleRenderType(LLDrawPool::POOL_ALPHA);
+	}
+
+	F32 water_height = gAgent.getRegion()->getWaterHeight();
+	BOOL above_water = gCamera->getOrigin().mV[2] > water_height ? TRUE : FALSE;
+
+	//prepare occlusion geometry
+	if (sUseOcclusion)
+	{
+		for (U32 i = 0; i < mObjectPartition.size(); i++)
+		{
+			if (mObjectPartition[i] && hasRenderType(mObjectPartition[i]->mDrawableType))
+			{
+				mObjectPartition[i]->buildOcclusion();
+			}
+		}
+		
+#if AGGRESSIVE_OCCLUSION
+		for (LLSpatialBridge::bridge_vector_t::iterator i = mVisibleBridge.begin(); i != mVisibleBridge.end(); ++i)
+		{
+			LLSpatialBridge* bridge = *i;
+			if (!bridge->isDead() && hasRenderType(bridge->mDrawableType))
+			{	
+				bridge->buildOcclusion();
+			}
+		}
+#endif
+	}
+
+
+	//rebuild offscreen geometry
+	if (!sSkipUpdate)
+	{
+		for (LLSpatialGroup::sg_set_t::iterator iter = mGroupQ.begin(); iter != mGroupQ.end(); ++iter)
+		{
+			LLSpatialGroup* group = *iter;
+			group->rebuildGeom();
+		}
+		mGroupQ.clear();
+	
+		//rebuild drawable geometry
+		for (LLSpatialGroup::sg_vector_t::iterator i = mDrawableGroups.begin(); i != mDrawableGroups.end(); ++i)
+		{
+			LLSpatialGroup* group = *i;
+			group->rebuildGeom();
+		}
+	}
+
+	//build render map
+	for (LLSpatialGroup::sg_vector_t::iterator i = mVisibleGroups.begin(); i != mVisibleGroups.end(); ++i)
+	{
+		LLSpatialGroup* group = *i;
+		if (!sSkipUpdate)
+		{
+			group->rebuildGeom();
+		}
+		for (LLSpatialGroup::draw_map_t::iterator j = group->mDrawMap.begin(); j != group->mDrawMap.end(); ++j)
 		{
-			LLViewerObject *vobj;
-			vobj = drawablep->getVObj();
-			if (vobj && !vobj->isAvatar())
+			std::vector<LLDrawInfo*>& src_vec = j->second;
+			std::vector<LLDrawInfo*>& dest_vec = mRenderMap[j->first];
+
+			for (std::vector<LLDrawInfo*>::iterator k = src_vec.begin(); k != src_vec.end(); ++k)
+			{
+				dest_vec.push_back(*k);
+			}
+		}
+		
+		LLSpatialGroup::draw_map_t::iterator alpha = group->mDrawMap.find(LLRenderPass::PASS_ALPHA);
+		
+		if (alpha != group->mDrawMap.end())
+		{ //store alpha groups for sorting
+			if (!sSkipUpdate)
+			{
+				group->updateDistance(camera);
+			}
+			
+			if (hasRenderType(LLDrawPool::POOL_ALPHA))
 			{
-				if (!vobj->isAvatar() && 
-					(vobj->usePhysics() || vobj->flagHandleTouch()))
+				BOOL above = group->mObjectBounds[0].mV[2] + group->mObjectBounds[1].mV[2] > water_height ? TRUE : FALSE;
+				BOOL below = group->mObjectBounds[0].mV[2] - group->mObjectBounds[1].mV[2] < water_height ? TRUE : FALSE;
+				
+				if (below == above_water || above == below)
 				{
-					if (!drawablep->isVisible())
-					{
-						// Skip objects that aren't visible.
-						continue;
-					}
-					S32 face_id;
-					for (face_id = 0; face_id < drawablep->getNumFaces(); face_id++)
-					{
-						mHighlightFaces.put(drawablep->getFace(face_id) );
-					}
+					mAlphaGroups.push_back(group);
+				}
+
+				if (above == above_water || below == above)
+				{
+					mAlphaGroupsPostWater.push_back(group);
 				}
 			}
+			else
+			{
+				mAlphaGroupsPostWater.push_back(group);
+			}
 		}
-
-		mNumVisibleFaces += drawablep->getNumFaces();
 	}
 	
+	//store active alpha groups
+	for (LLSpatialGroup::sg_vector_t::iterator i = mActiveGroups.begin(); i != mActiveGroups.end(); ++i)
+	{
+		LLSpatialGroup* group = *i;
+		if (!sSkipUpdate)
+		{
+			group->rebuildGeom();
+		}
+		LLSpatialGroup::draw_map_t::iterator alpha = group->mDrawMap.find(LLRenderPass::PASS_ALPHA);
+		
+		if (alpha != group->mDrawMap.end())
+		{
+			LLSpatialBridge* bridge = group->mSpatialPartition->asBridge();
+			LLCamera trans_camera = bridge->transformCamera(camera);
+			if (!sSkipUpdate)
+			{
+				group->updateDistance(trans_camera);
+			}
+			
+			if (hasRenderType(LLDrawPool::POOL_ALPHA))
+			{
+				LLSpatialGroup* bridge_group = bridge->getSpatialGroup();
+				BOOL above = bridge_group->mObjectBounds[0].mV[2] + bridge_group->mObjectBounds[1].mV[2] > water_height ? TRUE : FALSE;
+				BOOL below = bridge_group->mObjectBounds[0].mV[2] - bridge_group->mObjectBounds[1].mV[2] < water_height ? TRUE : FALSE;
+					
+				
+				if (below == above_water || above == below)
+				{
+					mAlphaGroups.push_back(group);
+				}
+				
+				if (above == above_water || below == above)
+				{
+					mAlphaGroupsPostWater.push_back(group);
+				}
+			}
+			else
+			{
+				mAlphaGroupsPostWater.push_back(group);
+			}
+		}
+	}
+
+	//sort by texture or bump map
+	for (U32 i = 0; i < LLRenderPass::NUM_RENDER_TYPES; ++i)
+	{
+		if (!mRenderMap[i].empty())
+		{
+			if (i == LLRenderPass::PASS_BUMP)
+			{
+				std::sort(mRenderMap[i].begin(), mRenderMap[i].end(), LLDrawInfo::CompareBump());
+			}
+			else 
+			{
+				std::sort(mRenderMap[i].begin(), mRenderMap[i].end(), LLDrawInfo::CompareTexturePtr());
+			}
+		}
+	}
+
+	std::sort(mAlphaGroups.begin(), mAlphaGroups.end(), LLSpatialGroup::CompareDepthGreater());
+	std::sort(mAlphaGroupsPostWater.begin(), mAlphaGroupsPostWater.end(), LLSpatialGroup::CompareDepthGreater());
+
+	if (sRenderScriptedBeacons)
+	{
+		// Only show the beacon on the root object.
+		forAllVisibleDrawables(renderScriptedBeacons);
+	}
+
+	if (sRenderPhysicalBeacons)
+	{
+		// Only show the beacon on the root object.
+		forAllVisibleDrawables(renderPhysicalBeacons);
+	}
+
+	if (sRenderParticleBeacons)
+	{
+		forAllVisibleDrawables(renderParticleBeacons);
+	}
+
+	// Draw physical objects in red.
+	if (gHUDManager->getShowPhysical())
+	{
+		forAllVisibleDrawables(highlightPhysical);
+	}
+
 	// If god mode, also show audio cues
 	if (sRenderSoundBeacons && gAudiop)
 	{
@@ -2292,7 +2572,7 @@ void LLPipeline::stateSort()
 		LLFloaterTelehub::addBeacons();
 	}
 
-	mSelectedFaces.reset();
+	mSelectedFaces.clear();
 	
 	// Draw face highlights for selected faces.
 	if (gSelectMgr->getTEMode())
@@ -2303,54 +2583,7 @@ void LLPipeline::stateSort()
 
 		while (vobjp)
 		{
-			LLDrawable *drawablep = vobjp->mDrawable;
-			if (!drawablep || drawablep->isDead() || (!vobjp->isHUDAttachment() && !drawablep->isVisible()))
-			{
-				llwarns << "Dead drawable on selected face list!" << llendl;
-			}
-			else
-			{
-				LLVOVolume *volp = drawablep->getVOVolume();
-				if (volp)
-				{
-					if (volp->getAllTEsSame())
-					{
-						SelectedFaceInfo* faceinfo = mSelectedFaces.reserve_block(1);
-						faceinfo->mFacep = drawablep->getFace(vobjp->getFaceIndexOffset());
-						faceinfo->mTE = te;
-					}
-					else
-					{
-						// This is somewhat inefficient, but works correctly.
-						S32 face_id;
-						for (face_id = 0; face_id < vobjp->getVolume()->getNumFaces(); face_id++)
-						{
-							LLFace *facep = drawablep->getFace(face_id + vobjp->getFaceIndexOffset());
-							if (te == facep->getTEOffset())
-							{
-								SelectedFaceInfo* faceinfo = mSelectedFaces.reserve_block(1);
-								faceinfo->mFacep = facep;
-								faceinfo->mTE = -1;
-							}
-						}
-					}
-				}
-				else
-				{
-					// This is somewhat inefficient, but works correctly.
-					S32 face_id;
-					for (face_id = 0; face_id <  drawablep->getNumFaces(); face_id++)
-					{
-						LLFace *facep = drawablep->getFace(face_id + vobjp->getFaceIndexOffset());
-						if (te == facep->getTEOffset())
-						{
-							SelectedFaceInfo* faceinfo = mSelectedFaces.reserve_block(1);
-							faceinfo->mFacep = facep;
-							faceinfo->mTE = -1;
-						}
-					}
-				}
-			}
+			mSelectedFaces.push_back(vobjp->mDrawable->getFace(te));
 			gSelectMgr->getSelection()->getNextTE(&vobjp,&te);
 		}
 	}
@@ -2363,13 +2596,13 @@ static void render_hud_elements()
 	gPipeline.disableLights();		
 	
 	gPipeline.renderDebug();
-	
+
 	LLGLDisable fog(GL_FOG);
 	LLGLSUIDefault gls_ui;
 	
 	if (gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI))
 	{
-		gViewerWindow->renderSelections(FALSE, FALSE, FALSE); // For HUD bersion in render_ui_3d()
+		gViewerWindow->renderSelections(FALSE, FALSE, FALSE); // For HUD version in render_ui_3d()
 	
 		// Draw the tracking overlays
 		LLTracker::render3D();
@@ -2384,7 +2617,7 @@ static void render_hud_elements()
 			gParcelMgr->render();
 			gParcelMgr->renderParcelCollision();
 		}
-		
+	
 		// Render debugging beacons.
 		gObjectList.renderObjectBeacons();
 		LLHUDObject::renderAll();
@@ -2395,11 +2628,15 @@ static void render_hud_elements()
 		// This is only set when not rendering the UI, for parcel snapshots
 		gParcelMgr->render();
 	}
-
+	else if (gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_HUD))
+	{
+		LLHUDText::renderAllHUD();
+	}
 }
 
 void LLPipeline::renderHighlights()
 {
+	LLMemType mt(LLMemType::MTYPE_PIPELINE);
 	// Draw 3D UI elements here (before we clear the Z buffer in POOL_HUD)
 	// Render highlighted faces.
 	LLColor4 color(1.f, 1.f, 1.f, 0.5f);
@@ -2421,48 +2658,15 @@ void LLPipeline::renderHighlights()
 		}
 		mFaceSelectImagep->addTextureStats((F32)MAX_IMAGE_AREA);
 
-		for (S32 i = 0; i < mSelectedFaces.count(); i++)
+		for (U32 i = 0; i < mSelectedFaces.size(); i++)
 		{
-			LLFace *facep = mSelectedFaces[i].mFacep;
+			LLFace *facep = mSelectedFaces[i];
 			if (!facep || facep->getDrawable()->isDead())
 			{
 				llerrs << "Bad face on selection" << llendl;
 			}
 			
-			LLDrawPool* poolp = facep->getPool();
-
-			if (!poolp->canUseAGP())
-			{
-				unbindAGP();
-			}
-			else if (usingAGP())
-			{
-				bindAGP();
-			}
-
-			if (mSelectedFaces[i].mTE == -1)
-			{
-				// Yes, I KNOW this is stupid...
-				poolp->renderFaceSelected(facep, mFaceSelectImagep, color);
-			}
-			else
-			{
-				LLVOVolume *volp = (LLVOVolume *)facep->getViewerObject();
-				// Do the special coalesced face mode.
-				S32 j;
-				S32 offset = 0;
-				S32 count = volp->getVolume()->getVolumeFace(0).mIndices.size();
-				for (j = 0; j <= mSelectedFaces[i].mTE; j++)
-				{
-					count = volp->getVolume()->getVolumeFace(j).mIndices.size();
-					if (j < mSelectedFaces[i].mTE)
-					{
-						offset += count;
-					}
-				}
-
-				poolp->renderFaceSelected(facep, mFaceSelectImagep, color, offset, count);
-			}
+			facep->renderSelected(mFaceSelectImagep, color);
 		}
 	}
 
@@ -2470,26 +2674,16 @@ void LLPipeline::renderHighlights()
 	{
 		// Paint 'em red!
 		color.setVec(1.f, 0.f, 0.f, 0.5f);
-		for (S32 i = 0; i < mHighlightFaces.count(); i++)
+		for (U32 i = 0; i < mHighlightFaces.size(); i++)
 		{
 			LLFace* facep = mHighlightFaces[i];
-			LLDrawPool* poolp = facep->getPool();
-			if (!poolp->canUseAGP())
-			{
-				unbindAGP();
-			}
-			else if (usingAGP())
-			{
-				bindAGP();
-			}
-
-			poolp->renderFaceSelected(facep, LLViewerImage::sNullImagep, color);
+			facep->renderSelected(LLViewerImage::sNullImagep, color);
 		}
 	}
 
 	// Contains a list of the faces of objects that are physical or
 	// have touch-handlers.
-	mHighlightFaces.reset();
+	mHighlightFaces.clear();
 
 	if (mVertexShaderLevel[SHADER_INTERFACE] > 0)
 	{
@@ -2497,11 +2691,11 @@ void LLPipeline::renderHighlights()
 	}
 }
 
-void LLPipeline::renderGeom()
+void LLPipeline::renderGeom(LLCamera& camera)
 {
 	LLMemType mt(LLMemType::MTYPE_PIPELINE);
 	LLFastTimer t(LLFastTimer::FTM_RENDER_GEOMETRY);
-
+	
 	if (!mAlphaSizzleImagep)
 	{
 		mAlphaSizzleImagep = gImageList.getImage(LLUUID(gViewerArt.getString("alpha_sizzle.tga")), MIPMAP_TRUE, TRUE);
@@ -2513,6 +2707,8 @@ void LLPipeline::renderGeom()
 	//
 	//
 
+	glEnableClientState(GL_VERTEX_ARRAY);
+
 	stop_glerror();
 	gFrameStats.start(LLFrameStats::RENDER_SYNC);
 
@@ -2520,6 +2716,7 @@ void LLPipeline::renderGeom()
 #ifndef LL_RELEASE_FOR_DOWNLOAD
 	LLGLState::checkStates();
 	LLGLState::checkTextureChannels();
+	LLGLState::checkClientArrays();
 #endif
 	if (mRenderDebugMask & RENDER_DEBUG_VERIFY)
 	{
@@ -2529,27 +2726,23 @@ void LLPipeline::renderGeom()
 		}
 	}
 
-	if (mAGPMemPool)
 	{
-		mAGPMemPool->waitFence(mGlobalFence);
+		//LLFastTimer ftm(LLFastTimer::FTM_TEMP6);
+		LLVertexBuffer::startRender();
 	}
 
-	unbindAGP();
 	for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter)
 	{
 		LLDrawPool *poolp = *iter;
 		if (hasRenderType(poolp->getType()))
 		{
 			poolp->prerender();
-			poolp->syncAGP();
 		}
 	}
 
 	gFrameStats.start(LLFrameStats::RENDER_GEOM);
 
 	// Initialize lots of GL state to "safe" values
-	mTrianglesDrawn = 0;
-	
 	glMatrixMode(GL_TEXTURE);
 	glLoadIdentity();
 	glMatrixMode(GL_MODELVIEW);
@@ -2557,13 +2750,13 @@ void LLPipeline::renderGeom()
 	LLGLSPipeline gls_pipeline;
 	
 	LLGLState gls_color_material(GL_COLOR_MATERIAL, mLightingDetail < 2);
-	LLGLState normalize(GL_NORMALIZE, TRUE);
+	// LLGLState normalize(GL_NORMALIZE, TRUE);
 			
 	// Toggle backface culling for debugging
 	LLGLEnable cull_face(mBackfaceCull ? GL_CULL_FACE : 0);
 	// Set fog
 	LLGLEnable fog_enable(hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_FOG) ? GL_FOG : 0);
-
+	gSky.updateFog(camera.getFar());
 
 	LLViewerImage::sDefaultImagep->bind(0);
 	LLViewerImage::sDefaultImagep->setClamp(FALSE, FALSE);
@@ -2572,19 +2765,20 @@ void LLPipeline::renderGeom()
 	//
 	// Actually render all of the geometry
 	//
-	//
-
+	//	
 	stop_glerror();
-	BOOL non_agp = FALSE;
 	BOOL did_hud_elements = FALSE;
-	
+	BOOL occlude = sUseOcclusion;
+
 	U32 cur_type = 0;
 
-	S32 skipped_vertices = 0;
+	if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_PICKING))
+	{
+		gObjectList.renderObjectsForSelect(camera);
+	}
+	else
 	{
 		LLFastTimer t(LLFastTimer::FTM_POOLS);
-		BOOL occlude = TRUE;
-
 		calcNearbyLights();
 
 		pool_set_t::iterator iter1 = mPools.begin();
@@ -2594,80 +2788,27 @@ void LLPipeline::renderGeom()
 			
 			cur_type = poolp->getType();
 
-			if (cur_type >= LLDrawPool::POOL_TREE && occlude)
-			{ //all the occluders have been drawn, do occlusion queries
-				if (mVertexShadersEnabled)
-				{
-					glUseProgramObjectARB(0);
-				}
-				doOcclusion();
+			if (occlude && cur_type > LLDrawPool::POOL_AVATAR)
+			{
 				occlude = FALSE;
+				doOcclusion(camera);
 			}
-			
-			if (cur_type >= LLDrawPool::POOL_HUD && !did_hud_elements)
+
+			if (cur_type > LLDrawPool::POOL_ALPHA_POST_WATER && !did_hud_elements)
 			{
 				renderHighlights();
 				// Draw 3D UI elements here (before we clear the Z buffer in POOL_HUD)
-				if (mVertexShadersEnabled)
-				{
-					glUseProgramObjectARB(0);
-				}
 				render_hud_elements();
 				did_hud_elements = TRUE;
 			}
 
 			pool_set_t::iterator iter2 = iter1;
-			if (hasRenderType(poolp->getType()))
+			if (hasRenderType(poolp->getType()) && poolp->getNumPasses() > 0)
 			{
 				LLFastTimer t(LLFastTimer::FTM_POOLRENDER);
 
 				setupHWLights(poolp);
-				
-				if (mVertexShadersEnabled && poolp->getVertexShaderLevel() == 0)
-				{
-					glUseProgramObjectARB(0);
-				}
-				else if (mVertexShadersEnabled)
-				{
-					mMaterialIndex = mSpecularIndex = 0;
-					switch(cur_type)
-					{
-					  case LLDrawPool::POOL_SKY:
-					  case LLDrawPool::POOL_STARS:
-					  case LLDrawPool::POOL_CLOUDS:
-					  	glUseProgramObjectARB(0); 
-						break;
-					  case LLDrawPool::POOL_TERRAIN:
-						mTerrainProgram.bind();
-						break;
-					  case LLDrawPool::POOL_GROUND:
-						mGroundProgram.bind();
-						break;
-					  case LLDrawPool::POOL_TREE:
-					  case LLDrawPool::POOL_TREE_NEW:
-					  case LLDrawPool::POOL_SIMPLE:
-					  case LLDrawPool::POOL_MEDIA:
-						mObjectSimpleProgram.bind();
-						break;
-					  case LLDrawPool::POOL_BUMP:
-						mObjectBumpProgram.bind();
-						break;
-					  case LLDrawPool::POOL_AVATAR:
-						glUseProgramObjectARB(0);
-                        break;
-					  case LLDrawPool::POOL_WATER:
-						glUseProgramObjectARB(0);
-						break;
-					  case LLDrawPool::POOL_ALPHA:
-						mObjectAlphaProgram.bind();
-						break;
-					  case LLDrawPool::POOL_HUD:
-					  default:
-						glUseProgramObjectARB(0);
-						break;
-					}
-				}
-
+			
 				for( S32 i = 0; i < poolp->getNumPasses(); i++ )
 				{
 					poolp->beginRenderPass(i);
@@ -2678,33 +2819,19 @@ void LLPipeline::renderGeom()
 						{
 							break;
 						}
-						if (p->getType() != LLDrawPool::POOL_AVATAR
-							&& p->getType() != LLDrawPool::POOL_ALPHA
-							&& p->getType() != LLDrawPool::POOL_HUD
-							&& (!p->getIndexCount() || !p->getVertexCount()))
-						{
-							continue;
-						}
-
-						if (p->canUseAGP() && usingAGP())
-						{
-							bindAGP();
-						}
-						else
-						{
-							//llinfos << "Rendering pool type " << p->getType() << " without AGP!" << llendl;
-							unbindAGP();
-							non_agp = TRUE;
-						}
-					
+						
 						p->resetTrianglesDrawn();
 						p->render(i);
 						mTrianglesDrawn += p->getTrianglesDrawn();
-						skipped_vertices += p->mSkippedVertices;
-						p->mSkippedVertices = 0;
 					}
 					poolp->endRenderPass(i);
 #ifndef LL_RELEASE_FOR_DOWNLOAD
+					GLint depth;
+					glGetIntegerv(GL_MODELVIEW_STACK_DEPTH, &depth);
+					if (depth > 3)
+					{
+						llerrs << "GL matrix stack corrupted!" << llendl;
+					}
 					LLGLState::checkStates();
 					LLGLState::checkTextureChannels();
 					LLGLState::checkClientArrays();
@@ -2726,21 +2853,18 @@ void LLPipeline::renderGeom()
 			iter1 = iter2;
 			stop_glerror();
 		}
-
-		if (occlude)
-		{
-			if (mVertexShadersEnabled)
-			{
-				glUseProgramObjectARB(0);
-			}
-			doOcclusion();
-		}
 	}
-	stop_glerror();
-	
-	if (mVertexShadersEnabled)
+
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+		LLGLState::checkStates();
+		LLGLState::checkTextureChannels();
+		LLGLState::checkClientArrays();
+#endif
+
+	if (occlude)
 	{
-		glUseProgramObjectARB(0);
+		doOcclusion(camera);
+		occlude = FALSE;
 	}
 
 	if (!did_hud_elements)
@@ -2748,32 +2872,65 @@ void LLPipeline::renderGeom()
 		renderHighlights();
 		render_hud_elements();
 	}
+
+	stop_glerror();
 	
-	static S32 agp_mix_count = 0;
-	if (non_agp && usingAGP())
-	{
-		if (0 == agp_mix_count % 16)
-		{
-			lldebugs << "Mixing AGP and non-AGP pools, slow!" << llendl;
-		}
-		agp_mix_count++;
-	}
-	else
 	{
-		agp_mix_count = 0;
+		LLVertexBuffer::stopRender();
 	}
-
+	
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+		LLGLState::checkStates();
+		LLGLState::checkTextureChannels();
+		LLGLState::checkClientArrays();
+#endif
+	
 	// Contains a list of the faces of objects that are physical or
 	// have touch-handlers.
-	mHighlightFaces.reset();
+	mHighlightFaces.clear();
+}
+
+void LLPipeline::processGeometry(LLCamera& camera)
+{
+	if (sSkipUpdate)
+	{
+		return;
+	}
+
+	for (U32 i = 0; i < mObjectPartition.size(); i++)
+	{
+		if (mObjectPartition[i] && hasRenderType(mObjectPartition[i]->mDrawableType))
+		{
+			mObjectPartition[i]->processGeometry(&camera);
+		}
+	}
+}
 
-	// This wait is in case we try to do multiple renders of a frame,
-	// I don't know what happens when we send a fence multiple times without
-	// checking it.
-	if (mAGPMemPool)
+void LLPipeline::processOcclusion(LLCamera& camera)
+{
+	//process occlusion (readback)
+	if (sUseOcclusion)
 	{
-		mAGPMemPool->waitFence(mGlobalFence);
-		mAGPMemPool->sendFence(mGlobalFence);
+		for (U32 i = 0; i < mObjectPartition.size(); i++)
+		{
+			if (mObjectPartition[i] && hasRenderType(mObjectPartition[i]->mDrawableType))
+			{
+				mObjectPartition[i]->processOcclusion(&camera);
+			}
+		}
+		
+#if AGGRESSIVE_OCCLUSION
+		for (LLSpatialBridge::bridge_vector_t::iterator i = mOccludedBridge.begin(); i != mOccludedBridge.end(); ++i)
+		{
+			LLSpatialBridge* bridge = *i;
+			if (!bridge->isDead() && hasRenderType(bridge->mDrawableType))
+			{
+				LLCamera trans = bridge->transformCamera(camera);
+				bridge->processOcclusion(&trans);
+			}
+		}
+#endif
+		mOccludedBridge.clear();
 	}
 }
 
@@ -2782,13 +2939,30 @@ void LLPipeline::renderDebug()
 	LLMemType mt(LLMemType::MTYPE_PIPELINE);
 
 	// Disable all client state
-    glDisableClientState(GL_VERTEX_ARRAY);
-	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
     glDisableClientState(GL_NORMAL_ARRAY);
-    glDisableClientState(GL_VERTEX_ARRAY);
+	glDisableClientState(GL_COLOR_ARRAY);
 
 	// Debug stuff.
-	mObjectPartition->renderDebug();
+	for (U32 i = 0; i < mObjectPartition.size(); i++)
+	{
+		if (mObjectPartition[i] && hasRenderType(mObjectPartition[i]->mDrawableType))
+		{
+			mObjectPartition[i]->renderDebug();
+		}
+	}
+
+	for (LLSpatialBridge::bridge_vector_t::iterator i = mVisibleBridge.begin(); i != mVisibleBridge.end(); ++i)
+	{
+		LLSpatialBridge* bridge = *i;
+		if (!bridge->isDead() && hasRenderType(bridge->mDrawableType))
+		{
+			glPushMatrix();
+			glMultMatrixf((F32*)bridge->mDrawable->getRenderMatrix().mMatrix);
+			bridge->renderDebug();
+			glPopMatrix();
+		}
+	}
 
 	if (mRenderDebugMask & LLPipeline::RENDER_DEBUG_LIGHT_TRACE)
 	{
@@ -2835,81 +3009,6 @@ void LLPipeline::renderDebug()
 		}
 	}
 
-	mCompilesStat.addValue(sCompiles);
-	mLightingChangesStat.addValue(mLightingChanges);
-	mGeometryChangesStat.addValue(mGeometryChanges);
-	mTrianglesDrawnStat.addValue(mTrianglesDrawn/1000.f);
-	mVerticesRelitStat.addValue(mVerticesRelit);
-	mNumVisibleFacesStat.addValue(mNumVisibleFaces);
-	mNumVisibleDrawablesStat.addValue((S32)mVisibleList.size());
-
-	if (gRenderLightGlows)
-	{
-		displaySSBB();
-	}
-
-	/*if (mRenderDebugMask & RENDER_DEBUG_BBOXES)
-	{
-		LLGLSPipelineAlpha gls_pipeline_alpha;
-		LLGLSNoTexture no_texture;
-
-		for (LLDrawable::drawable_vector_t::iterator iter = mVisibleList.begin(); iter != mVisibleList.end(); iter++)
-		{
-			LLDrawable *drawablep = *iter;
-			if (drawablep->isDead())
-			{
-				continue;
-			}
-			LLVector3 min, max;
-			
-			if (drawablep->getVObj() && drawablep->getVObj()->getPCode() == LLViewerObject::LL_VO_SURFACE_PATCH)
-			{
-				// Render drawable bbox
-				drawablep->getBounds(min, max);
-				glColor4f(0.f, 1.f, 0.f, 0.25f);
-				render_bbox(min, max);
-
-				// Render object bbox
-				LLVector3 scale = drawablep->getVObj()->getScale();
-				LLVector3 pos = drawablep->getVObj()->getPositionAgent();
-				min = pos - scale * 0.5f;
-				max = pos + scale * 0.5f;
-				glColor4f(1.f, 0.f, 0.f, 0.25f);
-				render_bbox(min, max);
-			}
-		}
-	}*/
-
-	/*
-	// Debugging code for parcel sound.
-	F32 x, y;
-
-	LLGLSNoTexture gls_no_texture;
-
-	glBegin(GL_POINTS);
-	if (gAgent.getRegion())
-	{
-		// Draw the composition layer for the region that I'm in.
-		for (x = 0; x <= 260; x++)
-		{
-			for (y = 0; y <= 260; y++)
-			{
-				if (gParcelMgr->isSoundLocal(gAgent.getRegion()->getOriginGlobal() + LLVector3d(x, y, 0.f)))
-				{
-					glColor4f(1.f, 0.f, 0.f, 1.f);
-				}
-				else
-				{
-					glColor4f(0.f, 0.f, 1.f, 1.f);
-				}
-
-				glVertex3f(x, y, gAgent.getRegion()->getLandHeightRegion(LLVector3(x, y, 0.f)));
-			}
-		}
-	}
-	glEnd();
-	*/
-
 	if (mRenderDebugMask & RENDER_DEBUG_COMPOSITION)
 	{
 		// Debug composition layers
@@ -2942,358 +3041,163 @@ void LLPipeline::renderDebug()
 		}
 		glEnd();
 	}
-
-	if (mRenderDebugMask & RENDER_DEBUG_AGP_MEM)
-	{
-		displayAGP();
-	}
-
-	if (mRenderDebugMask & RENDER_DEBUG_POOLS)
-	{
-		displayPools();
-	}
-
-// 	if (mRenderDebugMask & RENDER_DEBUG_QUEUES)
-// 	{
-// 		displayQueues();
-// 	}
-
-	if (mRenderDebugMask & RENDER_DEBUG_MAP)
-	{
-		displayMap();
-	}
 }
 
-
-
-
-BOOL compute_min_max(LLMatrix4& box, LLVector2& min, LLVector2& max)
+void LLPipeline::renderForSelect(std::set<LLViewerObject*>& objects)
 {
-	min.setVec(1000,1000);
-	max.setVec(-1000,-1000);
-
-	if (box.mMatrix[3][3] <= 0.0f) return FALSE;
+	LLMemType mt(LLMemType::MTYPE_PIPELINE);
+	
+	LLVertexBuffer::startRender();
+	
+	glMatrixMode(GL_MODELVIEW);
 
-	const F32 vec[8][3] = { 
-		{ -0.5f,-0.5f,-0.5f },
-		{ -0.5f,-0.5f,+0.5f },
-		{ -0.5f,+0.5f,-0.5f },
-		{ -0.5f,+0.5f,+0.5f },
-		{ +0.5f,-0.5f,-0.5f },
-		{ +0.5f,-0.5f,+0.5f },
-		{ +0.5f,+0.5f,-0.5f },
-		{ +0.5f,+0.5f,+0.5f } };
-   
-	LLVector4 v;
+	LLGLSDefault gls_default;
+	LLGLSObjectSelect gls_object_select;
+	LLGLDepthTest gls_depth(GL_TRUE,GL_TRUE);
+	disableLights();
+	
+    glEnableClientState ( GL_VERTEX_ARRAY );
 
-	for (S32 i=0;i<8;i++)
+	//for each drawpool
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+	LLGLState::checkStates();
+	LLGLState::checkTextureChannels();
+	LLGLState::checkClientArrays();
+	U32 last_type = 0;
+#endif
+	for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter)
 	{
-		v.setVec(vec[i][0],vec[i][1],vec[i][2],1);
-		v = v * box;
-		F32 iw = 1.0f / v.mV[3];
-		v.mV[0] *= iw;
-		v.mV[1] *= iw;
-
-		min.mV[0] = llmin(min.mV[0],v.mV[0]);
-		max.mV[0] = llmax(max.mV[0],v.mV[0]);
-
-		min.mV[1] = llmin(min.mV[1],v.mV[1]);
-		max.mV[1] = llmax(max.mV[1],v.mV[1]);
+		LLDrawPool *poolp = *iter;
+		if (poolp->isFacePool() && hasRenderType(poolp->getType()))
+		{
+			LLFacePool* face_pool = (LLFacePool*) poolp;
+			face_pool->renderForSelect();
+		
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+			if (poolp->getType() != last_type)
+			{
+				last_type = poolp->getType();
+				LLGLState::checkStates();
+				LLGLState::checkTextureChannels();
+				LLGLState::checkClientArrays();
+			}
+#endif
+		}
 	}
 
-	/*
-	min.mV[0] = max.mV[0] = box.mMatrix[3][0];
-	min.mV[1] = max.mV[1] = box.mMatrix[3][1];
-	F32        iw  = 1.0f / box.mMatrix[3][3];
-
-	F32 f0 = (fabs(box.mMatrix[0][0])+fabs(box.mMatrix[1][0])+fabs(box.mMatrix[2][0])) * 0.5f;
-	F32 f1 = (fabs(box.mMatrix[0][1])+fabs(box.mMatrix[1][1])+fabs(box.mMatrix[2][1])) * 0.5f;
-	F32 f2 = (fabs(box.mMatrix[0][2])+fabs(box.mMatrix[1][2])+fabs(box.mMatrix[2][2])) * 0.5f;
-
-	min.mV[0] -= f0; 
-	min.mV[1] -= f1; 
-
-	max.mV[0] += f0; 
-	max.mV[1] += f1; 
-
-	min.mV[0] *= iw;
-	min.mV[1] *= iw;
-
-	max.mV[0] *= iw;
-	max.mV[1] *= iw;
-	*/
-	return TRUE;
-}
-
-void LLPipeline::displaySSBB()
-{
-	LLMatrix4 proj;
-	LLMatrix4 cfr(OGL_TO_CFR_ROTATION);
-	LLMatrix4 camera;
-	LLMatrix4 comb;
-
-	gCamera->getMatrixToLocal(camera);
-	
-	if (!mBloomImagep)
+	LLGLEnable tex(GL_TEXTURE_2D);
+	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+	LLGLEnable alpha_test(GL_ALPHA_TEST);
+	if (gPickTransparent)
 	{
-		mBloomImagep = gImageList.getImage(IMG_BLOOM1);
+		glAlphaFunc(GL_GEQUAL, 0.0f);
+	}
+	else
+	{
+		glAlphaFunc(GL_GREATER, 0.2f);
 	}
 
-	// don't write to depth buffer with light glows so that chat bubbles can pop through
-	LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
-	LLViewerImage::bindTexture(mBloomImagep);
-
-	glGetFloatv(GL_PROJECTION_MATRIX,(float*)proj.mMatrix);
-
-	glMatrixMode(GL_PROJECTION);
-	glPushMatrix();
-	glLoadIdentity();
-
-	glMatrixMode(GL_MODELVIEW);
-	glPushMatrix();
-	glLoadIdentity();
+	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,		GL_COMBINE_ARB);
+	glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB,		GL_REPLACE);
+	glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB,		GL_MODULATE);
 
-	LLGLSPipelineAlpha gls_pipeline_alpha;
+	glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB,		GL_PRIMARY_COLOR);
+	glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB,		GL_SRC_COLOR);
 
-	//glScalef(0.25,0.25,0.25);
+	glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB,		GL_TEXTURE);
+	glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB,	GL_SRC_ALPHA);
 
-	S32 sizex = gViewerWindow->getWindowWidth() / 2;
-	S32 sizey = gViewerWindow->getWindowHeight() / 2;
+	glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA_ARB,		GL_PRIMARY_COLOR_ARB);
+	glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA_ARB,	GL_SRC_ALPHA);
 
-	F32 aspect = (float)sizey / (float)sizex;
+	U32 prim_mask = LLVertexBuffer::MAP_VERTEX | 
+					LLVertexBuffer::MAP_TEXCOORD;
 
-	for (LLDrawable::drawable_set_t::iterator iter = mLights.begin();
-		 iter != mLights.end(); iter++)
+	for (std::set<LLViewerObject*>::iterator i = objects.begin(); i != objects.end(); ++i)
 	{
-		LLDrawable *lightp = *iter;
-		if (lightp->isDead())
+		LLViewerObject* vobj = *i;
+		LLDrawable* drawable = vobj->mDrawable;
+		if (vobj->isDead() || 
+			vobj->isHUDAttachment() ||
+			(gHideSelectedObjects && vobj->isSelected()) ||
+			drawable->isDead() || 
+			!hasRenderType(drawable->getRenderType()))
 		{
 			continue;
 		}
-		
-		LLMatrix4 mat = lightp->mXform.getWorldMatrix();
-
-		mat *= camera;
-		mat *= cfr;
-		mat *= proj;
-
-		U8  color[64];
 
-		LLVector2 min,max;
-		if (mat.mMatrix[3][3] < 160 && compute_min_max(mat,min,max))
+		for (S32 j = 0; j < drawable->getNumFaces(); ++j)
 		{
-			F32 cx = (max.mV[0] + min.mV[0]) * 0.5f;
-			F32 cy = (max.mV[1] + min.mV[1]) * 0.5f;
-			F32 sx = (max.mV[0] - min.mV[0]) * 2.0f;
-			F32 sy = (max.mV[1] - min.mV[1]) * 2.0f;
-			S32 x  = (S32)(cx * (F32)sizex) + sizex;
-			S32 y  = (S32)(cy * (F32)sizey) + sizey;
-
-			if (cx > -1 && cx < 1 && cy > -1 && cy < 1)
+			LLFace* facep = drawable->getFace(j);
+			if (!facep->getPool())
 			{
-				glReadPixels(x-2,y-2,4,4,GL_RGBA,GL_UNSIGNED_BYTE,&color[0]);
-
-				S32 total = 0;
-				for (S32 i=0;i<64;i++)
-				{
-					total += color[i];
-				}
-				total /= 64;
-
-				sx = (sy = (sx + sy) * 0.5f * ((float)total/255.0f)) * aspect;
-
-
-				if (total > 60)
-				{
-					color[3+32] = total >> 1;
-					glBegin(GL_QUADS);
-					glColor4ubv(&color[32]);
-					glTexCoord2f(0,0);
-					glVertex3f(cx-sx,cy-sy,0);
-					glTexCoord2f(1,0);
-					glVertex3f(cx+sx,cy-sy,0);
-					glTexCoord2f(1,1);
-					glVertex3f(cx+sx,cy+sy,0);
-					glTexCoord2f(0,1);
-					glVertex3f(cx-sx,cy+sy,0);
-					glEnd();
-				}
+				facep->renderForSelect(prim_mask);
 			}
 		}
-
 	}
 
-	// sun
+	// pick HUD objects
+	LLVOAvatar* avatarp = gAgent.getAvatarObject();
+	if (avatarp && sShowHUDAttachments)
 	{
-		LLVector4 sdir(gSky.getSunDirection() * 10000.0f + gAgent.getPositionAgent());
-		sdir.mV[3] = 1.0f;
-		sdir = sdir * camera;
-		sdir = sdir * cfr;
-		sdir = sdir * proj; // todo: preconcat
-
-		sdir.mV[0] /= sdir.mV[3];
-		sdir.mV[1] /= sdir.mV[3];
-
-		U8  color[64];
+		glMatrixMode(GL_PROJECTION);
+		glPushMatrix();
+		glMatrixMode(GL_MODELVIEW);
+		glPushMatrix();
 
-		if (sdir.mV[3] > 0)
+		setup_hud_matrices(TRUE);
+		LLViewerJointAttachment* attachmentp;
+		for (attachmentp = avatarp->mAttachmentPoints.getFirstData();
+			attachmentp;
+			attachmentp = avatarp->mAttachmentPoints.getNextData())
 		{
-			F32 cx = sdir.mV[0];
-			F32 cy = sdir.mV[1];
-			F32 sx, sy;
-			S32 x  = (S32)(cx * (F32)sizex) + sizex;
-			S32 y  = (S32)(cy * (F32)sizey) + sizey;
-
-			if (cx > -1 && cx < 1 && cy > -1 && cy < 1)
+			if (attachmentp->getIsHUDAttachment())
 			{
-				glReadPixels(x-2,y-2,4,4,GL_RGBA,GL_UNSIGNED_BYTE,&color[0]);
-
-				S32 total = 0;
-				for (S32 i=0;i<64;i++)
+				LLViewerObject* objectp = attachmentp->getObject();
+				if (objectp)
 				{
-					total += color[i];
-				}
-				total >>= 7;
-
-				sx = (sy = ((float)total/255.0f)) * aspect;
-
-				const F32 fix = -0.1f;
+					LLDrawable* drawable = objectp->mDrawable;
+					if (drawable->isDead())
+					{
+						continue;
+					}
 
-				color[32] = (U8)(color[32] * 0.5f + 255 * 0.5f);
-				color[33] = (U8)(color[33] * 0.5f + 255 * 0.5f);
-				color[34] = (U8)(color[34] * 0.5f + 255 * 0.5f);
+					for (S32 j = 0; j < drawable->getNumFaces(); ++j)
+					{
+						LLFace* facep = drawable->getFace(j);
+						if (!facep->getPool())
+						{
+							facep->renderForSelect(prim_mask);
+						}
+					}
 
-				if (total > 80)
-				{
-					color[32+3]  = (U8)total;
-					glBegin(GL_QUADS);
-					glColor4ubv(&color[32]);
-					glTexCoord2f(0,0);
-					glVertex3f(cx-sx,cy-sy+fix,0);
-					glTexCoord2f(1,0);
-					glVertex3f(cx+sx,cy-sy+fix,0);
-					glTexCoord2f(1,1);
-					glVertex3f(cx+sx,cy+sy+fix,0);
-					glTexCoord2f(0,1);
-					glVertex3f(cx-sx,cy+sy+fix,0);
-					glEnd();
-				}
+					//render child faces
+					for (U32 k = 0; k < drawable->getChildCount(); ++k)
+					{
+						LLDrawable* child = drawable->getChild(k);
+						for (S32 l = 0; l < child->getNumFaces(); ++l)
+						{
+							LLFace* facep = child->getFace(l);
+							if (!facep->getPool())
+							{
+								facep->renderForSelect(prim_mask);
+							}
+						}
+					}
+				}	
 			}
 		}
-	}
-
-	glMatrixMode(GL_PROJECTION);
-	glPopMatrix();
-	glMatrixMode(GL_MODELVIEW);
-	glPopMatrix();
-}
-
-void LLPipeline::displayMap()
-{
-	LLGLSPipelineAlpha gls_pipeline_alpha;
-	LLGLSNoTexture no_texture;
-	LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
-
-	glMatrixMode(GL_MODELVIEW);
-	glPushMatrix();
-	glLoadIdentity();
-
-	glMatrixMode(GL_PROJECTION);
-	glPushMatrix();
-	glLoadIdentity();
-
-	glTranslatef(-1,-1,0);
-	glScalef(2,2,0);
-
-	glColor4f(0,0,0,0.5);
-	glBegin(GL_QUADS);
-	glVertex3f(0,0,0);
-	glVertex3f(1,0,0);
-	glVertex3f(1,1,0);
-	glVertex3f(0,1,0);
-	glEnd();
-
-	static F32 totalW = 1.5f;
-	static F32 offset = 0.5f;
-	static F32 scale  = 1.0f;
-
-	S32 mousex = gViewerWindow->getCurrentMouseX();
-	S32 mousey = gViewerWindow->getCurrentMouseY();
-	S32 w      = gViewerWindow->getWindowWidth();
-	S32 h      = gViewerWindow->getWindowHeight();
-
-	if (mousex < 20 && offset > 0) offset -= (20 - mousex) * 0.02f;
-	if (mousex > (w - 20))         offset += (20 - (w - mousex)) * 0.02f;
-	if (offset > (totalW-1))       offset = (totalW-1);
-
-	if (mousey < 20 && scale > 0.1)        scale -= (20 - mousey) * 0.001f;
-	if (mousey > (h - 20) && scale < 1.0f) scale += (20 - (h - mousey)) * 0.001f;
-
-	glScalef(scale*scale,scale,1);
-	glTranslatef(-offset,0,0);
-	//totalW = mStaticTree->render2D(0,0.8f/scale);
-	//mDynamicTree->render2D(0,0.4f/scale);
-
-	glMatrixMode(GL_PROJECTION);
-	glPopMatrix();
-	glMatrixMode(GL_MODELVIEW);
-	glPopMatrix();
-}
-
-void LLPipeline::renderForSelect()
-{
-	LLMemType mt(LLMemType::MTYPE_PIPELINE);
-	//for each drawpool
-
-	glMatrixMode(GL_MODELVIEW);
-
-	LLGLSDefault gls_default;
-	LLGLSObjectSelect gls_object_select;
-	LLGLDepthTest gls_depth(GL_TRUE);
-	disableLights();
-	
-    glEnableClientState ( GL_VERTEX_ARRAY );
-	glDisableClientState( GL_NORMAL_ARRAY );
-    glDisableClientState( GL_TEXTURE_COORD_ARRAY );
 
-#ifndef LL_RELEASE_FOR_DOWNLOAD
-	LLGLState::checkStates();
-	LLGLState::checkTextureChannels();
-	LLGLState::checkClientArrays();
-	U32 last_type = 0;
-#endif
-	for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter)
-	{
-		LLDrawPool *poolp = *iter;
-		if (poolp->canUseAGP() && usingAGP())
-		{
-			bindAGP();
-		}
-		else
-		{
-			//llinfos << "Rendering pool type " << p->getType() << " without AGP!" << llendl;
-			unbindAGP();
-		}
-		poolp->renderForSelect();
-#ifndef LL_RELEASE_FOR_DOWNLOAD
-		if (poolp->getType() != last_type)
-		{
-			last_type = poolp->getType();
-			LLGLState::checkStates();
-			LLGLState::checkTextureChannels();
-			LLGLState::checkClientArrays();
-		}
-#endif
+		glMatrixMode(GL_PROJECTION);
+		glPopMatrix();
+		glMatrixMode(GL_MODELVIEW);
+		glPopMatrix();
 	}
 
-	// Disable all of the client state
-	glDisableClientState( GL_VERTEX_ARRAY );
-
-	if (mAGPMemPool)
-	{
-		mAGPMemPool->waitFence(mGlobalFence);
-		mAGPMemPool->sendFence(mGlobalFence);
-	}
+	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+	glDisableClientState( GL_TEXTURE_COORD_ARRAY );
+	
+	LLVertexBuffer::stopRender();
 }
 
 void LLPipeline::renderFaceForUVSelect(LLFace* facep)
@@ -3305,7 +3209,6 @@ void LLPipeline::rebuildPools()
 {
 	LLMemType mt(LLMemType::MTYPE_PIPELINE);
 	S32 max_count = mPools.size();
-	S32 num_rebuilds = 0;
 	pool_set_t::iterator iter1 = mPools.upper_bound(mLastRebuildPool);
 	while(max_count > 0 && mPools.size() > 0) // && num_rebuilds < MAX_REBUILDS)
 	{
@@ -3314,8 +3217,8 @@ void LLPipeline::rebuildPools()
 			iter1 = mPools.begin();
 		}
 		LLDrawPool* poolp = *iter1;
-		num_rebuilds += poolp->rebuild();
-		if (poolp->mReferences.empty())
+
+		if (poolp->isDead())
 		{
 			mPools.erase(iter1++);
 			removeFromQuickLookup( poolp );
@@ -3345,601 +3248,211 @@ void LLPipeline::addToQuickLookup( LLDrawPool* new_poolp )
 	switch( new_poolp->getType() )
 	{
 	case LLDrawPool::POOL_SIMPLE:
-		mSimplePools[ uintptr_t(new_poolp->getTexture()) ] = new_poolp ;
+		if (mSimplePool)
+		{
+			llassert(0);
+			llwarns << "Ignoring duplicate simple pool." << llendl;
+		}
+		else
+		{
+			mSimplePool = (LLRenderPass*) new_poolp;
+		}
 		break;
 
 	case LLDrawPool::POOL_TREE:
 		mTreePools[ uintptr_t(new_poolp->getTexture()) ] = new_poolp ;
 		break;
-
-	case LLDrawPool::POOL_TREE_NEW:
-		mTreeNewPools[ uintptr_t(new_poolp->getTexture()) ] = new_poolp ;
-		break;
  
 	case LLDrawPool::POOL_TERRAIN:
 		mTerrainPools[ uintptr_t(new_poolp->getTexture()) ] = new_poolp ;
 		break;
 
 	case LLDrawPool::POOL_BUMP:
-		mBumpPools[ uintptr_t(new_poolp->getTexture()) ] = new_poolp ;
-		break;
-
-	case LLDrawPool::POOL_MEDIA:
-		mMediaPools[ uintptr_t(new_poolp->getTexture()) ] = new_poolp ;
-		break;
-
-	case LLDrawPool::POOL_ALPHA:
-		if( mAlphaPool )
-		{
-			llassert(0);
-			llwarns << "LLPipeline::addPool(): Ignoring duplicate Alpha pool" << llendl;
-		}
-		else
-		{
-			mAlphaPool = new_poolp;
-		}
-		break;
-
-	case LLDrawPool::POOL_AVATAR:
-		break; // Do nothing
-
-	case LLDrawPool::POOL_SKY:
-		if( mSkyPool )
-		{
-			llassert(0);
-			llwarns << "LLPipeline::addPool(): Ignoring duplicate Sky pool" << llendl;
-		}
-		else
-		{
-			mSkyPool = new_poolp;
-		}
-		break;
-
-	case LLDrawPool::POOL_STARS:
-		if( mStarsPool )
-		{
-			llassert(0);
-			llwarns << "LLPipeline::addPool(): Ignoring duplicate Stars pool" << llendl;
-		}
-		else
-		{
-			mStarsPool = new_poolp;
-		}
-		break;
-
-	case LLDrawPool::POOL_CLOUDS:
-		if( mCloudsPool )
+		if (mBumpPool)
 		{
 			llassert(0);
-			llwarns << "LLPipeline::addPool(): Ignoring duplicate Clouds pool" << llendl;
+			llwarns << "Ignoring duplicate bump pool." << llendl;
 		}
 		else
 		{
-			mCloudsPool = new_poolp;
+			mBumpPool = new_poolp;
 		}
 		break;
 
-	case LLDrawPool::POOL_WATER:
-		if( mWaterPool )
-		{
-			llassert(0);
-			llwarns << "LLPipeline::addPool(): Ignoring duplicate Water pool" << llendl;
-		}
-		else
-		{
-			mWaterPool = new_poolp;
-		}
-		break;
-
-	case LLDrawPool::POOL_GROUND:
-		if( mGroundPool )
-		{
-			llassert(0);
-			llwarns << "LLPipeline::addPool(): Ignoring duplicate Ground Pool" << llendl;
-		}
-		else
-		{ 
-			mGroundPool = new_poolp;
-		}
-		break;
-
-	case LLDrawPool::POOL_HUD:
-		if( mHUDPool )
-		{
-			llerrs << "LLPipeline::addPool(): Duplicate HUD Pool!" << llendl;
-		}
-		mHUDPool = new_poolp;
-		break;
-
-	default:
-		llassert(0);
-		llwarns << "Invalid Pool Type in  LLPipeline::addPool()" << llendl;
-		break;
-	}
-}
-
-void LLPipeline::removePool( LLDrawPool* poolp )
-{
-	removeFromQuickLookup(poolp);
-	mPools.erase(poolp);
-	delete poolp;
-}
-
-void LLPipeline::removeFromQuickLookup( LLDrawPool* poolp )
-{
-	LLMemType mt(LLMemType::MTYPE_PIPELINE);
-	switch( poolp->getType() )
-	{
-	case LLDrawPool::POOL_SIMPLE:
-		#ifdef _DEBUG
-			{
-				BOOL found = mSimplePools.erase( (uintptr_t)poolp->getTexture() );
-				llassert( found );
-			}
-		#else
-			mSimplePools.erase( (uintptr_t)poolp->getTexture() );
-		#endif
-		break;
-
-	case LLDrawPool::POOL_TREE:
-		#ifdef _DEBUG
-			{
-				BOOL found = mTreePools.erase( (uintptr_t)poolp->getTexture() );
-				llassert( found );
-			}
-		#else
-			mTreePools.erase( (uintptr_t)poolp->getTexture() );
-		#endif
-		break;
-
-	case LLDrawPool::POOL_TREE_NEW:
-		#ifdef _DEBUG
-			{
-				BOOL found = mTreeNewPools.erase( (uintptr_t)poolp->getTexture() );
-				llassert( found );
-			}
-		#else
-			mTreeNewPools.erase( (uintptr_t)poolp->getTexture() );
-		#endif
-		break;
-
-	case LLDrawPool::POOL_TERRAIN:
-		#ifdef _DEBUG
-			{
-				BOOL found = mTerrainPools.erase( (uintptr_t)poolp->getTexture() );
-				llassert( found );
-			}
-		#else
-			mTerrainPools.erase( (uintptr_t)poolp->getTexture() );
-		#endif
-		break;
-
-	case LLDrawPool::POOL_BUMP:
-		#ifdef _DEBUG
-			{
-				BOOL found = mBumpPools.erase( (uintptr_t)poolp->getTexture() );
-				llassert( found );
-			}
-		#else
-			mBumpPools.erase( (uintptr_t)poolp->getTexture() );
-		#endif
-		break;
-
-	case LLDrawPool::POOL_MEDIA:
-		#ifdef _DEBUG
-			{
-				BOOL found = mMediaPools.erase( (uintptr_t)poolp->getTexture() );
-				llassert( found );
-			}
-		#else
-			mMediaPools.erase( (uintptr_t)poolp->getTexture() );
-		#endif
-		break;
-
 	case LLDrawPool::POOL_ALPHA:
-		llassert( poolp == mAlphaPool );
-		mAlphaPool = NULL;
-		break;
-
-	case LLDrawPool::POOL_AVATAR:
-		break; // Do nothing
-
-	case LLDrawPool::POOL_SKY:
-		llassert( poolp == mSkyPool );
-		mSkyPool = NULL;
-		break;
-
-	case LLDrawPool::POOL_STARS:
-		llassert( poolp == mStarsPool );
-		mStarsPool = NULL;
-		break;
-
-	case LLDrawPool::POOL_CLOUDS:
-		llassert( poolp == mCloudsPool );
-		mCloudsPool = NULL;
-		break;
-
-	case LLDrawPool::POOL_WATER:
-		llassert( poolp == mWaterPool );
-		mWaterPool = NULL;
-		break;
-
-	case LLDrawPool::POOL_GROUND:
-		llassert( poolp == mGroundPool );
-		mGroundPool = NULL;
-		break;
-
-	case LLDrawPool::POOL_HUD:
-		llassert( poolp == mHUDPool );
-		mHUDPool = NULL;
-		break;
-
-	default:
-		llassert(0);
-		llwarns << "Invalid Pool Type in  LLPipeline::removeFromQuickLookup() type=" << poolp->getType() << llendl;
-		break;
-	}
-}
-
-void LLPipeline::flushAGPMemory()
-{
-	LLMemType mt(LLMemType::MTYPE_PIPELINE);
-
-	for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter)
-	{
-		LLDrawPool *poolp = *iter;
-		poolp->flushAGP();
-	}
-}
-
-void LLPipeline::resetDrawOrders()
-{
-	// Iterate through all of the draw pools and rebuild them.
-	for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter)
-	{
-		LLDrawPool *poolp = *iter;
-		poolp->resetDrawOrders();
-	}
-}
-
-//-------------------------------
-
-
-LLViewerObject *LLPipeline::nearestObjectAt(F32 yaw, F32 pitch)
-{
-	// Stub to find nearest Object at given yaw and pitch
-
-	/*
-	LLEdge *vd = NULL;
-
-	if (vd) 
-	{
-		return (LLViewerObject*)vd->mDrawablep->getVObj();
-	}
-	*/
-
-	return NULL;
-}
-
-void LLPipeline::printPools()
-{
-	/*
-	// Iterate through all of the draw pools and rebuild them.
-	for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter)
-	{
-		LLDrawPool *poolp = *iter;
-		if (pool->mTexturep[0])
-		{
-			llinfos << "Op pool " << pool->mTexturep[0]->mID << llendflush;
-		}
-		else
-		{
-			llinfos << "Opaque pool NULL" << llendflush;
-		}
-		llinfos << " Vertices: \t" << pool->getVertexCount() << llendl;
-	}
-
-
-	for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter)
-	{
-		LLDrawPool *poolp = *iter;
-		llinfos << "Al pool " << pool;
-		llcont << " Vertices: \t" << pool->getVertexCount() << llendl;
-	}
-
-	pool = mHighlightPools.getFirst();
-	llinfos << "Si pool " << pool;
-	llcont << " Vertices: \t" << pool->getVertexCount() << llendl;
-	*/
-}
-
-
-void LLPipeline::displayPools()
-{
-	// Needs to be fixed to handle chained pools - djs
-	LLUI::setLineWidth(1.0);
-	glMatrixMode(GL_PROJECTION);
-	glPushMatrix();
-	glLoadIdentity();
-	glMatrixMode(GL_MODELVIEW);
-	glPushMatrix();
-	glLoadIdentity();
-
-	LLGLSPipelineAlpha gls_pipeline_alpha;
-	LLGLSNoTexture no_texture;
-	LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
-
-	glScalef(2,2,1);
-	glTranslatef(-0.5f,-0.5f,0);
-
-	F32 x   = 0.0f,    y   = 0.05f;
-	F32 xs  = 0.01f,   ys  = 0.01f;
-	F32 xs2 = xs*0.1f, ys2 = ys * 0.1f;
-
-	for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter)
-	{
-		LLGLSTexture gls_texture;
-		LLDrawPool *poolp = *iter;
-		if (poolp->getDebugTexture())
-		{
-			poolp->getDebugTexture()->bind();
-			glColor4f(1,1,1,1);
-			stamp(x,y,xs*4,ys*4);
-		}
-
-		LLGLSNoTexture no_texture;
-
-		F32 a = 1.f - (F32)poolp->mRebuildTime / (F32)poolp->mRebuildFreq;
-		glColor4f(a,a,a,1);
-		stamp(x,y,xs,ys);
-
-		x =  (xs + xs2) * 4.0f;
-
-		F32 h = ys * 0.5f;
-
-		S32 total = 0;
-		
-		for (std::vector<LLFace*>::iterator iter = poolp->mReferences.begin();
-			 iter != poolp->mReferences.end(); iter++)
-		{
-			LLFace *face = *iter;
-			F32        w = xs;
-
-			if (!face || !face->getDrawable())
-			{
-				w = 16 / 3000.0f;
-
-				stamp(x,y,w,h);
-
-				if (x+w > 0.95f) 
-				{
-					x  = (xs + xs2) * 4.0f;
-					y +=  h + ys2;
-				}
-				else
-				{
-					if (w) x += w + xs2;
-				}
-
-				continue;
-			}
-
-			if (face->getDrawable()->isVisible())
-			{
-				if (face->isState(LLFace::BACKLIST))
-				{
-					glColor4f(1,0,1,1);
-				}
-				else if (total > poolp->getMaxVertices())
-				{
-					glColor4f(1,0,0,1);
-				}
-				else
-				{
-					glColor4f(0,1,0,1);
-					total += face->getGeomCount();
-				}
-			}
-			else
-			{
-				if (face->isState(LLFace::BACKLIST))
-				{
-					glColor4f(0,0,1,1);
-				}
-				else
-				{
-					glColor4f(1,1,0,1);
-				}
-			}
-
-			w = face->getGeomCount() / 3000.0f;
-
-			stamp(x,y,w,h);
-
-			if (x+w > 0.95f) 
-			{
-				x  = (xs + xs2) * 4.0f;
-				y +=  h + ys2;
-			}
-			else
-			{
-				if (w) x += w + xs2;
-			}
-		}
-
-		y   += ys + ys2;
-		x    = 0;
-	}
-
-	glPopMatrix();
-	glMatrixMode(GL_PROJECTION);
-	glPopMatrix();
-	glMatrixMode(GL_MODELVIEW);
-}
-
-static F32 xs  = 0.01f,   ys  = 0.01f;
-static F32 xs2 = xs*0.1f, ys2 = ys * 0.1f;
-static F32 winx, winy;
-
-void displayDrawable(F32 &x, F32 &y, LLDrawable *drawable, F32 alpha = 0.5f)
-{
-	F32 w   = 0;
-	F32 h   = ys * 0.5f;
-
-	if (drawable && !drawable->isDead())
-	{
-		for (S32 f=0;f < drawable->getNumFaces(); f++)
+		if( mAlphaPool )
 		{
-			w += drawable->getFace(f)->getGeomCount() / 30000.0f;
+			llassert(0);
+			llwarns << "LLPipeline::addPool(): Ignoring duplicate Alpha pool" << llendl;
 		}
-		w+=xs;
-		glColor4f(1,1,0, alpha);
-	}
-	else
-	{
-		w = 0.01f;
-		glColor4f(0,0,0,alpha);
-	}
+		else
+		{
+			mAlphaPool = new_poolp;
+		}
+		break;
 
-//	const LLFontGL* font = gResMgr->getRes( LLFONT_SANSSERIF_SMALL );
+	case LLDrawPool::POOL_ALPHA_POST_WATER:
+		if( mAlphaPoolPostWater )
+		{
+			llassert(0);
+			llwarns << "LLPipeline::addPool(): Ignoring duplicate Alpha pool" << llendl;
+		}
+		else
+		{
+			mAlphaPoolPostWater = new_poolp;
+		}
+		break;
 
-//	U8 pcode = drawable->getVObj()->getPCode();
+	case LLDrawPool::POOL_AVATAR:
+		break; // Do nothing
 
-	//char *string = (char*)LLPrimitive::pCodeToString(pcode);
-	//if (pcode == 0x3e)      string = "terrain";
-	//else if (pcode == 0x2e) string = "cloud";
-	//string[3] = 0;
+	case LLDrawPool::POOL_SKY:
+		if( mSkyPool )
+		{
+			llassert(0);
+			llwarns << "LLPipeline::addPool(): Ignoring duplicate Sky pool" << llendl;
+		}
+		else
+		{
+			mSkyPool = new_poolp;
+		}
+		break;
 
-	stamp(x * winx,y * winy,w * winx,h * winy);
+	case LLDrawPool::POOL_STARS:
+		if( mStarsPool )
+		{
+			llassert(0);
+			llwarns << "LLPipeline::addPool(): Ignoring duplicate Stars pool" << llendl;
+		}
+		else
+		{
+			mStarsPool = new_poolp;
+		}
+		break;
+	
+	case LLDrawPool::POOL_WATER:
+		if( mWaterPool )
+		{
+			llassert(0);
+			llwarns << "LLPipeline::addPool(): Ignoring duplicate Water pool" << llendl;
+		}
+		else
+		{
+			mWaterPool = new_poolp;
+		}
+		break;
 
-	/*
-	glColor4f(0,0,0,1);
-	font->render(string,x*winx+1,y*winy+1);
-	LLGLSNoTexture no_texture;
-	*/
+	case LLDrawPool::POOL_GROUND:
+		if( mGroundPool )
+		{
+			llassert(0);
+			llwarns << "LLPipeline::addPool(): Ignoring duplicate Ground Pool" << llendl;
+		}
+		else
+		{ 
+			mGroundPool = new_poolp;
+		}
+		break;
 
-	if (x+w > 0.95f) 
-	{
-		x  = (xs + xs2) * 4.0f;
-		y +=  h + ys2;
-	}
-	else
-	{
-		if (w) x += w + xs2;
+	default:
+		llassert(0);
+		llwarns << "Invalid Pool Type in  LLPipeline::addPool()" << llendl;
+		break;
 	}
-
 }
 
-#if 0 // No longer up date
-
-void displayQueue(F32 &x, F32 &y, LLDynamicArray<LLDrawable*>& processed, LLDynamicQueuePtr<LLPointer<LLDrawable> >& remaining)
+void LLPipeline::removePool( LLDrawPool* poolp )
 {
-	S32 i;
-	for (i=0;i<processed.count();i++)
-	{
-		displayDrawable(x,y,processed[i],1);
-	}
-
-	x += xs * 2;
-
-	S32 count = remaining.count();
-	for (i=0;i<count;i++)
-	{
-		LLDrawable* drawablep = remaining[(i + remaining.getFirst()) % remaining.getMax()];
-		if (drawablep && !drawablep->isDead())
-		{
-			displayDrawable(x,y,drawable,0.5);
-		}
-	}
-
-	y  += ys * 4;
-	x   = (xs + xs2) * 4.0f;
-
+	removeFromQuickLookup(poolp);
+	mPools.erase(poolp);
+	delete poolp;
 }
 
-void LLPipeline::displayQueues()
+void LLPipeline::removeFromQuickLookup( LLDrawPool* poolp )
 {
-	LLUI::setLineWidth(1.0);
-	glMatrixMode(GL_PROJECTION);
-	glPushMatrix();
-	glLoadIdentity();
-	glMatrixMode(GL_MODELVIEW);
-	glPushMatrix();
-	glLoadIdentity();
-
-	LLGLSPipelineAlpha gls_pipeline_alpha;
-	LLGLSNoTexture no_texture;
-	LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+	LLMemType mt(LLMemType::MTYPE_PIPELINE);
+	switch( poolp->getType() )
+	{
+	case LLDrawPool::POOL_SIMPLE:
+		llassert(mSimplePool == poolp);
+		mSimplePool = NULL;
+		break;
 
-	glScalef(2,2,1);
-	glTranslatef(-0.5f,-0.5f,0);
+	case LLDrawPool::POOL_TREE:
+		#ifdef _DEBUG
+			{
+				BOOL found = mTreePools.erase( (uintptr_t)poolp->getTexture() );
+				llassert( found );
+			}
+		#else
+			mTreePools.erase( (uintptr_t)poolp->getTexture() );
+		#endif
+		break;
 
-	winx = (F32)gViewerWindow->getWindowWidth();
-	winy = (F32)gViewerWindow->getWindowHeight();
+	case LLDrawPool::POOL_TERRAIN:
+		#ifdef _DEBUG
+			{
+				BOOL found = mTerrainPools.erase( (uintptr_t)poolp->getTexture() );
+				llassert( found );
+			}
+		#else
+			mTerrainPools.erase( (uintptr_t)poolp->getTexture() );
+		#endif
+		break;
 
-	glScalef(1.0f/winx,1.0f/winy,1);
+	case LLDrawPool::POOL_BUMP:
+		llassert( poolp == mBumpPool );
+		mBumpPool = NULL;
+		break;
+	
+	case LLDrawPool::POOL_ALPHA:
+		llassert( poolp == mAlphaPool );
+		mAlphaPool = NULL;
+		break;
 
-	F32 x  = (xs + xs2) * 4.0f;
-	F32 y = 0.1f;
+	case LLDrawPool::POOL_ALPHA_POST_WATER:
+		llassert( poolp == mAlphaPoolPostWater );
+		mAlphaPoolPostWater = NULL;
+		break;
 
-	const LLFontGL* font = gResMgr->getRes( LLFONT_SANSSERIF );
+	case LLDrawPool::POOL_AVATAR:
+		break; // Do nothing
 
-	font->renderUTF8("Build1", 0,0,(S32)(y*winy),LLColor4(1,1,1,1));
-	displayQueue(x,y, gBuildProcessed, mBuildQ1);
+	case LLDrawPool::POOL_SKY:
+		llassert( poolp == mSkyPool );
+		mSkyPool = NULL;
+		break;
 
-	glPopMatrix();
-	glMatrixMode(GL_PROJECTION);
-	glPopMatrix();
-	glMatrixMode(GL_MODELVIEW);
+	case LLDrawPool::POOL_STARS:
+		llassert( poolp == mStarsPool );
+		mStarsPool = NULL;
+		break;
 
-}
+	case LLDrawPool::POOL_WATER:
+		llassert( poolp == mWaterPool );
+		mWaterPool = NULL;
+		break;
 
-#endif
+	case LLDrawPool::POOL_GROUND:
+		llassert( poolp == mGroundPool );
+		mGroundPool = NULL;
+		break;
 
-// static
-void render_bbox(const LLVector3 &min, const LLVector3 &max)
-{
-	S32 i;
-	LLVector3 verticesp[16];
-
-	verticesp[0].setVec(min.mV[0],min.mV[1],max.mV[2]);
-	verticesp[1].setVec(min.mV[0],min.mV[1],min.mV[2]);
-	verticesp[2].setVec(min.mV[0],max.mV[1],min.mV[2]);
-	verticesp[3].setVec(min.mV[0],max.mV[1],max.mV[2]);
-	verticesp[4].setVec(max.mV[0],max.mV[1],max.mV[2]);
-	verticesp[5].setVec(max.mV[0],max.mV[1],min.mV[2]);
-	verticesp[6].setVec(max.mV[0],min.mV[1],min.mV[2]);
-	verticesp[7].setVec(max.mV[0],min.mV[1],max.mV[2]);
-	verticesp[8 ] = verticesp[0];
-	verticesp[9 ] = verticesp[1];
-	verticesp[10] = verticesp[6];
-	verticesp[11] = verticesp[7];
-	verticesp[12] = verticesp[4];
-	verticesp[13] = verticesp[5];
-	verticesp[14] = verticesp[2];
-	verticesp[15] = verticesp[3];
-
-	LLGLSNoTexture gls_no_texture;
-	{
-		LLUI::setLineWidth(1.f);
-		glBegin(GL_LINE_LOOP);
-		for (i = 0; i < 16; i++)
-		{
-			glVertex3fv(verticesp[i].mV);
-		}
-		glEnd();
+	default:
+		llassert(0);
+		llwarns << "Invalid Pool Type in  LLPipeline::removeFromQuickLookup() type=" << poolp->getType() << llendl;
+		break;
 	}
+}
+
+void LLPipeline::resetDrawOrders()
+{
+	// Iterate through all of the draw pools and rebuild them.
+	for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter)
 	{
-		LLGLDepthTest gls_depth(GL_TRUE);
-		LLUI::setLineWidth(3.0f);
-		glBegin(GL_LINE_LOOP);
-		for (i = 0; i < 16; i++)
-		{
-			glVertex3fv(verticesp[i].mV);
-		}
-		glEnd();
+		LLDrawPool *poolp = *iter;
+		poolp->resetDrawOrders();
 	}
-	LLUI::setLineWidth(1.0f);
 }
 
 //============================================================================
@@ -4153,8 +3666,6 @@ void LLPipeline::setupHWLights(LLDrawPool* pool)
 {
 	const LLColor4 black(0,0,0,1);
 
-	setLightingDetail(-1); // update
-	
 	// Ambient
 	LLColor4 ambient = gSky.getTotalAmbientColor();
 	glLightModelfv(GL_LIGHT_MODEL_AMBIENT,ambient.mV);
@@ -4410,16 +3921,6 @@ class LLVOBranch;
 class LLVOLeaf;
 class Foo;
 
-template<> char* LLAGPArray<U8>::sTypeName               = "U8        [AGP]";
-template<> char* LLAGPArray<U32>::sTypeName              = "U32       [AGP]";
-template<> char* LLAGPArray<F32>::sTypeName              = "F32       [AGP]";
-template<> char* LLAGPArray<LLColor4>::sTypeName         = "LLColor4  [AGP]";
-template<> char* LLAGPArray<LLColor4U>::sTypeName        = "LLColor4U [AGP]";
-template<> char* LLAGPArray<LLVector4>::sTypeName        = "LLVector4 [AGP]";
-template<> char* LLAGPArray<LLVector3>::sTypeName        = "LLVector3 [AGP]";
-template<> char* LLAGPArray<LLVector2>::sTypeName        = "LLVector2 [AGP]";
-template<> char* LLAGPArray<LLFace*>::sTypeName          = "LLFace*   [AGP]";
-
 void scale_stamp(const F32 x, const F32 y, const F32 xs, const F32 ys)
 {
 	stamp(0.25f + 0.5f*x,
@@ -4431,249 +3932,34 @@ void scale_stamp(const F32 x, const F32 y, const F32 xs, const F32 ys)
 void drawBars(const F32 begin, const F32 end, const F32 height = 1.f)
 {
 	if (begin >= 0 && end <=1)
-	{
-		F32 lines  = 40.0f;
-		S32 ibegin = (S32)(begin * lines);
-		S32 iend   = (S32)(end   * lines);
-		F32 fbegin = begin * lines - ibegin;
-		F32 fend   = end   * lines - iend;
-
-		F32 line_height = height/lines;
-
-		if (iend == ibegin)
-		{
-			scale_stamp(fbegin, (F32)ibegin/lines,fend-fbegin, line_height);
-		}
-		else
-		{
-			// Beginning row
-			scale_stamp(fbegin, (F32)ibegin/lines,  1.0f-fbegin, line_height);
-
-			// End row
-			scale_stamp(0.0,    (F32)iend/lines,  fend, line_height);
-
-			// Middle rows
-			for (S32 l = (ibegin+1); l < iend; l++)
-			{
-				scale_stamp(0.0f, (F32)l/lines, 1.0f, line_height);
-			}
-		}
-	}
-}
-
-void LLPipeline::displayAGP()
-{
-	LLUI::setLineWidth(1.0);
-	glMatrixMode(GL_PROJECTION);
-	glPushMatrix();
-	glLoadIdentity();
-	glMatrixMode(GL_MODELVIEW);
-	glPushMatrix();
-	glLoadIdentity();
-
-	LLGLSPipelineAlpha gls_alpha;
-	LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
-
-	LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
-	glScalef(2,2,1);
-	glTranslatef(-0.5f,-0.5f,0);
-
-	glColor4f(0,0,0,0.5f);
-	scale_stamp(0,0,1,1);
-
-	F32 x   = 0.0f,    y   = 0.05f;
-	F32 xs  = 0.015f,   ys  = 0.015f;
-	F32 xs2 = xs*0.1f, ys2 = ys * 0.1f;
-	F32 w   = xs+xs2, h    = ys + ys2;
-	S32 i=0;
-
-	F32 agp_size = 1.f;
-	if (mAGPMemPool)
-	{
-		agp_size = (F32)mAGPMemPool->getSize();
-	}
-
-	x  = (xs + xs2) * 4.0f;
-	
-	static float c = 0.0f;
-	c += 0.0001f;
-
-	LLAGPMemBlock *blockp = mBufferMemory[mBufferIndex]->getAGPMemBlock();
-
-	pool_set_t::iterator iter = mPools.begin();
-	LLDrawPool *poolp = *iter;
-	
-	// Dump the shared AGP buffer
-	if (blockp)
-	{
-		F32 begin = blockp->getOffset()/agp_size;
-		F32 end   = begin + (blockp->getSize()/agp_size);
-		F32 used = begin + (poolp->mMemory.count()/agp_size);
-
-		LLViewerImage::bindTexture(NULL);
-		glColor4f(0.5f, 0.5f, 0.5f, 0.5f);
-		drawBars(begin,end);
-
-		LLViewerImage::bindTexture(NULL);
-		glColor4f(0.5f, 0.5f, 0.5f, 0.5f);
-		drawBars(begin,end);
-
-		LLViewerImage::bindTexture(NULL);
-		glColor4f(0.5f, 0.5f, 0.5f, 0.5f);
-		drawBars(begin,used, 0.5f);
-
-		glColor4f(0.5f, 0.5f, 0.5f, 0.5f);
-		drawBars(begin,end, 0.25f);
-	}
-
-	S32 used_bytes = 0;
-	S32 total_bytes = 0;
-	while (iter != mPools.end())
-	{
-		poolp = *iter++;
-
-		BOOL synced = FALSE;
-		i++;
-		total_bytes += poolp->mMemory.getMax();
-		used_bytes += poolp->mMemory.count();
-		LLViewerImage *texturep = poolp->getDebugTexture();
-
-
-		if (poolp->mMemory.mSynced)
-		{
-			poolp->mMemory.mSynced = FALSE;
-			synced = TRUE;
-		}
-
-		LLAGPMemBlock *blockp = poolp->mMemory.getAGPMemBlock();
-		if (blockp)
-		{
-			F32 begin = blockp->getOffset()/agp_size;
-			F32 end   = begin + (blockp->getSize()/agp_size);
-			F32 used = begin + (poolp->mMemory.count()/agp_size);
-
-			LLViewerImage::bindTexture(NULL);
-			glColor4f(1.f, 0.5f, 0.5f, 0.5f);
-			drawBars(begin,end);
-
-			LLViewerImage::bindTexture(texturep);
-			glColor4f(1.f, 0.75f, 0.75f, 0.5f);
-			drawBars(begin,end);
-
-			LLViewerImage::bindTexture(NULL);
-			glColor4f(1.f, 0.75f, 0.75f, 1.f);
-			drawBars(begin,used, 0.5f);
-
-			glColor3fv(poolp->getDebugColor().mV);
-			drawBars(begin,end, 0.25f);
-
-			if (synced)
-			{
-				LLViewerImage::bindTexture(NULL);
-				glColor4f(1.f, 1.f, 1.f, 0.4f);
-				drawBars(begin,end);
-			}
-		}
-
-		synced = FALSE;
-		if (poolp->mWeights.mSynced)
-		{
-			poolp->mWeights.mSynced = FALSE;
-			synced = TRUE;
-		}
-		blockp = poolp->mWeights.getAGPMemBlock();
-		if (blockp)
-		{
-			F32 begin = blockp->getOffset()/agp_size;
-			F32 end   = begin + (blockp->getSize()/agp_size);
-			F32 used = begin + (poolp->mWeights.count()*sizeof(float)/agp_size);
-
-			LLViewerImage::bindTexture(NULL);
-			glColor4f(0.0f, 0.f, 0.75f, 0.5f);
-			drawBars(begin,end);
-
-			LLViewerImage::bindTexture(texturep);
-			glColor4f(0.0, 0.f, 0.75f, 0.5f);
-			drawBars(begin,end);
-
-			LLViewerImage::bindTexture(NULL);
-			glColor4f(0.0, 0.f, 0.75f, 1.f);
-			drawBars(begin,used, 0.5f);
-
-			LLViewerImage::bindTexture(NULL);
-			glColor3fv(poolp->getDebugColor().mV);
-			drawBars(begin,end, 0.25f);
+	{
+		F32 lines  = 40.0f;
+		S32 ibegin = (S32)(begin * lines);
+		S32 iend   = (S32)(end   * lines);
+		F32 fbegin = begin * lines - ibegin;
+		F32 fend   = end   * lines - iend;
 
-			if (synced)
-			{
-				LLViewerImage::bindTexture(NULL);
-				glColor4f(1.f, 1.f, 1.f, 0.4f);
-				drawBars(begin,end);
-			}
-		}
+		F32 line_height = height/lines;
 
-		synced = FALSE;
-		if (poolp->mClothingWeights.mSynced)
+		if (iend == ibegin)
 		{
-			poolp->mClothingWeights.mSynced = FALSE;
-			synced = TRUE;
+			scale_stamp(fbegin, (F32)ibegin/lines,fend-fbegin, line_height);
 		}
-		blockp = poolp->mClothingWeights.getAGPMemBlock();
-		if (blockp)
+		else
 		{
-			F32 begin = blockp->getOffset()/agp_size;
-			F32 end   = begin + (blockp->getSize()/agp_size);
-			F32 used = begin + (poolp->mClothingWeights.count()*sizeof(LLVector4)/agp_size);
-
-			LLViewerImage::bindTexture(NULL);
-			glColor4f(0.75f, 0.f, 0.75f, 0.5f);
-			drawBars(begin,end);
-
-			LLViewerImage::bindTexture(texturep);
-			glColor4f(0.75f, 0.f, 0.75f, 0.5f);
-			drawBars(begin,end);
-
-			LLViewerImage::bindTexture(NULL);
-			glColor4f(0.75f, 0.f, 0.75f, 0.5f);
-			drawBars(begin,used, 0.5f);
+			// Beginning row
+			scale_stamp(fbegin, (F32)ibegin/lines,  1.0f-fbegin, line_height);
 
-			LLViewerImage::bindTexture(NULL);
-			glColor3fv(poolp->getDebugColor().mV);
-			drawBars(begin,end, 0.25f);
+			// End row
+			scale_stamp(0.0,    (F32)iend/lines,  fend, line_height);
 
-			if (synced)
+			// Middle rows
+			for (S32 l = (ibegin+1); l < iend; l++)
 			{
-				LLViewerImage::bindTexture(NULL);
-				glColor4f(1.f, 1.f, 1.f, 0.5f);
-				drawBars(begin,end);
+				scale_stamp(0.0f, (F32)l/lines, 1.0f, line_height);
 			}
 		}
-
-		//
-		// Stamps on bottom of screen
-		//
-		LLViewerImage::bindTexture(texturep);
-		glColor4f(1.f, 1.f, 1.f, 1.f);
-		stamp(x,y,xs,ys);
-
-		LLViewerImage::bindTexture(NULL);
-		glColor3fv(poolp->getDebugColor().mV);
-		stamp(x,y,xs, ys*0.25f);
-		if (x+w > 0.95f) 
-		{
-			x  = (xs + xs2) * 4.0f;
-			y +=  h;
-		}
-		else
-		{
-			x += w + xs2;
-		}
 	}
-
-	glPopMatrix();
-	glMatrixMode(GL_PROJECTION);
-	glPopMatrix();
-	glMatrixMode(GL_MODELVIEW);
 }
 
 void LLPipeline::findReferences(LLDrawable *drawablep)
@@ -4686,7 +3972,7 @@ void LLPipeline::findReferences(LLDrawable *drawablep)
 	{
 		llinfos << "In mLights" << llendl;
 	}
-	if (mMovedList.find(drawablep) != mMovedList.end())
+	if (std::find(mMovedList.begin(), mMovedList.end(), drawablep) != mMovedList.end())
 	{
 		llinfos << "In mMovedList" << llendl;
 	}
@@ -4698,18 +3984,14 @@ void LLPipeline::findReferences(LLDrawable *drawablep)
 	{
 		llinfos << "In mRetexturedList" << llendl;
 	}
-	if (mRematerialedList.find(drawablep) != mRematerialedList.end())
-	{
-		llinfos << "In mRematerialedList" << llendl;
-	}
-
+	
 	if (mActiveQ.find(drawablep) != mActiveQ.end())
 	{
 		llinfos << "In mActiveQ" << llendl;
 	}
-	if (mBuildQ1.find(drawablep) != mBuildQ1.end())
+	if (std::find(mBuildQ1.begin(), mBuildQ1.end(), drawablep) != mBuildQ1.end())
 	{
-		llinfos << "In mBuildQ2" << llendl;
+		llinfos << "In mBuildQ1" << llendl;
 	}
 	if (std::find(mBuildQ2.begin(), mBuildQ2.end(), drawablep) != mBuildQ2.end())
 	{
@@ -4717,19 +3999,7 @@ void LLPipeline::findReferences(LLDrawable *drawablep)
 	}
 
 	S32 count;
-	/*
-	count = mStaticTree->count(drawablep);
-	if (count)
-	{
-		llinfos << "In mStaticTree: " << count << " references" << llendl;
-	}
-
-	count = mDynamicTree->count(drawablep);
-	if (count)
-	{
-		llinfos << "In mStaticTree: " << count << " references" << llendl;
-	}
-	*/
+	
 	count = gObjectList.findReferences(drawablep);
 	if (count)
 	{
@@ -4756,53 +4026,6 @@ BOOL LLPipeline::verify()
 	return ok;
 }
 
-S32 LLPipeline::getAGPMemUsage()
-{
-	if (mAGPMemPool)
-	{
-		return mAGPMemPool->getSize();
-	}
-	else
-	{
-		return 0;
-	}
-}
-
-S32 LLPipeline::getMemUsage(const BOOL print)
-{
-	S32 mem_usage = 0;
-
-	if (mAGPMemPool)
-	{
-		S32 agp_usage = 0;
-		agp_usage = mAGPMemPool->getSize();
-		if (print)
-		{
-			llinfos << "AGP Mem: " << agp_usage << llendl;
-			llinfos << "AGP Mem used: " << mAGPMemPool->getTotalAllocated() << llendl;
-		}
-		mem_usage += agp_usage;
-	}
-
-
-	S32 pool_usage = 0;
-	for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter)
-	{
-		LLDrawPool *poolp = *iter;
-		pool_usage += poolp->getMemUsage(print);
-	}
-
-	if (print)
-	{
-		llinfos << "Pool Mem: " << pool_usage << llendl;
-	}
-
-	mem_usage += pool_usage;
-
-	return mem_usage;
-
-}
-
 //////////////////////////////
 //
 // Collision detection
@@ -4913,8 +4136,8 @@ void LLPipeline::setLight(LLDrawable *drawablep, BOOL is_light)
 		}
 		else
 		{
-			mLights.erase(drawablep);
 			drawablep->clearState(LLDrawable::LIGHT);
+			mLights.erase(drawablep);
 		}
 		markRelight(drawablep);
 	}
@@ -4933,9 +4156,16 @@ void LLPipeline::setActive(LLDrawable *drawablep, BOOL active)
 }
 
 //static
-void LLPipeline::toggleRenderType(void* data)
+void LLPipeline::toggleRenderType(U32 type)
 {
-	S32 type = (S32)(intptr_t)data;
+	U32 bit = (1<<type);
+	gPipeline.mRenderTypeMask ^= bit;
+}
+
+//static
+void LLPipeline::toggleRenderTypeControl(void* data)
+{
+	U32 type = (U32)(intptr_t)data;
 	U32 bit = (1<<type);
 	if (gPipeline.hasRenderType(type))
 	{
@@ -4945,13 +4175,13 @@ void LLPipeline::toggleRenderType(void* data)
 	{
 		llinfos << "Toggling render type mask " << std::hex << bit << " on" << std::dec << llendl;
 	}
-	gPipeline.mRenderTypeMask ^= bit;
+	gPipeline.toggleRenderType(type);
 }
 
 //static
-BOOL LLPipeline::toggleRenderTypeControl(void* data)
+BOOL LLPipeline::hasRenderTypeControl(void* data)
 {
-	S32 type = (S32)(intptr_t)data;
+	U32 type = (U32)(intptr_t)data;
 	return gPipeline.hasRenderType(type);
 }
 
@@ -4990,14 +4220,6 @@ BOOL LLPipeline::toggleRenderDebugControl(void* data)
 void LLPipeline::toggleRenderDebugFeature(void* data)
 {
 	U32 bit = (U32)(intptr_t)data;
-	if (gPipeline.hasRenderDebugFeatureMask(bit))
-	{
-		llinfos << "Toggling render debug feature mask " << std::hex << bit << " off" << std::dec << llendl;
-	}
-	else
-	{
-		llinfos << "Toggling render debug feature mask " << std::hex << bit << " on" << std::dec << llendl;
-	}
 	gPipeline.mRenderDebugFeatureMask ^= bit;
 }
 
@@ -5310,8 +4532,375 @@ void LLGLSLShader::vertexAttrib4fv(U32 index, GLfloat* v)
 
 LLViewerObject* LLPipeline::pickObject(const LLVector3 &start, const LLVector3 &end, LLVector3 &collision)
 {
-	LLDrawable* drawable = mObjectPartition->pickDrawable(start, end, collision);
+	LLDrawable* drawable = mObjectPartition[PARTITION_VOLUME]->pickDrawable(start, end, collision);
 	return drawable ? drawable->getVObj() : NULL;
 }
 
+LLSpatialPartition* LLPipeline::getSpatialPartition(LLViewerObject* vobj)
+{
+	if (vobj)
+	{
+		return getSpatialPartition(vobj->getPartitionType());
+	}
+	return NULL;
+}
+
+LLSpatialPartition* LLPipeline::getSpatialPartition(U32 type)
+{
+	if (type < mObjectPartition.size())
+	{
+		return mObjectPartition[type];
+	}
+	return NULL;
+}
+
+void LLPipeline::clearRenderMap()
+{
+	for (U32 i = 0; i < LLRenderPass::NUM_RENDER_TYPES; i++)
+	{
+		mRenderMap[i].clear();
+	}
+}
+
+
+void LLPipeline::resetVertexBuffers(LLDrawable* drawable)
+{
+	for (S32 i = 0; i < drawable->getNumFaces(); i++)
+	{
+		LLFace* facep = drawable->getFace(i);
+		facep->mVertexBuffer = NULL;
+		facep->mLastVertexBuffer = NULL;
+	}
+}
+
+void LLPipeline::resetVertexBuffers()
+{
+	for (U32 i = 0; i < mObjectPartition.size(); ++i)
+	{
+		if (mObjectPartition[i])
+		{
+			mObjectPartition[i]->resetVertexBuffers();
+		}
+	}
+
+	resetDrawOrders();
+
+	if (gSky.mVOSkyp.notNull())
+	{
+		resetVertexBuffers(gSky.mVOSkyp->mDrawable);
+		resetVertexBuffers(gSky.mVOGroundp->mDrawable);
+		resetVertexBuffers(gSky.mVOStarsp->mDrawable);
+		markRebuild(gSky.mVOSkyp->mDrawable, LLDrawable::REBUILD_ALL, TRUE);
+		markRebuild(gSky.mVOGroundp->mDrawable, LLDrawable::REBUILD_ALL, TRUE);
+		markRebuild(gSky.mVOStarsp->mDrawable, LLDrawable::REBUILD_ALL, TRUE);
+	}
+
+	if (LLVertexBuffer::sGLCount > 0)
+	{
+		LLVertexBuffer::cleanupClass();
+	}
+}
+
+void LLPipeline::renderObjects(U32 type, U32 mask, BOOL texture)
+{
+	mSimplePool->renderStatic(type, mask, texture);
+	mSimplePool->renderActive(type, mask, texture);
+}
+
+void LLPipeline::setUseVBO(BOOL use_vbo)
+{
+	if (use_vbo != LLVertexBuffer::sEnableVBOs)
+	{
+		if (use_vbo)
+		{
+			llinfos << "Enabling VBO." << llendl;
+		}
+		else
+		{ 
+			llinfos << "Disabling VBO." << llendl;
+		}
+		
+		resetVertexBuffers();
+		LLVertexBuffer::initClass(use_vbo);
+	}
+}
+
+void apply_cube_face_rotation(U32 face)
+{
+	switch (face)
+	{
+		case 0: 
+			glRotatef(90.f, 0, 1, 0);
+			glRotatef(180.f, 1, 0, 0);
+		break;
+		case 2: 
+			glRotatef(-90.f, 1, 0, 0);
+		break;
+		case 4:
+			glRotatef(180.f, 0, 1, 0);
+			glRotatef(180.f, 0, 0, 1);
+		break;
+		case 1: 
+			glRotatef(-90.f, 0, 1, 0);
+			glRotatef(180.f, 1, 0, 0);
+		break;
+		case 3:
+			glRotatef(90, 1, 0, 0);
+		break;
+		case 5: 
+			glRotatef(180, 0, 0, 1);
+		break;
+	}
+}
+void LLPipeline::generateReflectionMap(LLCubeMap* cube_map, LLCamera& cube_cam, GLsizei res)
+{
+	//render dynamic cube map
+	U32 type_mask = gPipeline.getRenderTypeMask();
+	BOOL use_occlusion = LLPipeline::sUseOcclusion;
+	LLPipeline::sUseOcclusion = FALSE;
+	LLPipeline::sSkipUpdate = TRUE;
+	static GLuint blur_tex = 0;
+	if (!blur_tex)
+	{
+		glGenTextures(1, &blur_tex);
+	}
+
+	BOOL toggle_ui = gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI);
+	if (toggle_ui)
+	{
+		gPipeline.toggleRenderDebugFeature((void*) LLPipeline::RENDER_DEBUG_FEATURE_UI);
+	}
+	
+	U32 cube_mask = (1 << LLPipeline::RENDER_TYPE_SIMPLE) |
+					(1 << LLPipeline::RENDER_TYPE_WATER) |
+					(1 << LLPipeline::RENDER_TYPE_BUMP) |
+					(1 << LLPipeline::RENDER_TYPE_ALPHA) |
+					(1 << LLPipeline::RENDER_TYPE_TREE) |
+					(1 << LLDrawPool::POOL_ALPHA_POST_WATER) |
+					//(1 << LLPipeline::RENDER_TYPE_PARTICLES) |
+					(1 << LLPipeline::RENDER_TYPE_CLOUDS) |
+					//(1 << LLPipeline::RENDER_TYPE_STARS) |
+					//(1 << LLPipeline::RENDER_TYPE_AVATAR) |
+					(1 << LLPipeline::RENDER_TYPE_GRASS) |
+					(1 << LLPipeline::RENDER_TYPE_VOLUME) |
+					(1 << LLPipeline::RENDER_TYPE_TERRAIN) |
+					(1 << LLPipeline::RENDER_TYPE_SKY) |
+					(1 << LLPipeline::RENDER_TYPE_GROUND);
+	
+	LLDrawPoolWater::sSkipScreenCopy = TRUE;
+	cube_mask = cube_mask & type_mask;
+	gPipeline.setRenderTypeMask(cube_mask);
+
+	glMatrixMode(GL_PROJECTION);
+	glPushMatrix();
+	glMatrixMode(GL_MODELVIEW);
+	glPushMatrix();
+
+	glViewport(0,0,res,res);
+	
+	glClearColor(0,0,0,0);			
+	
+	U32 cube_face[] = 
+	{
+		GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB,
+		GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB,
+		GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB,
+		GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB,
+		GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB,
+		GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB,
+	};
+	
+	LLVector3 origin = cube_cam.getOrigin();
+
+	gPipeline.calcNearbyLights();
+
+	for (S32 i = 0; i < 6; i++)
+	{
+		
+		glMatrixMode(GL_PROJECTION);
+		glLoadIdentity();
+		gluPerspective(90.f, 1.f, 0.1f, 1024.f);
+		glMatrixMode(GL_MODELVIEW);
+		glLoadIdentity();
+		
+		apply_cube_face_rotation(i);
+
+		
+		glTranslatef(-origin.mV[0], -origin.mV[1], -origin.mV[2]);
+		cube_cam.setOrigin(origin);
+		LLViewerCamera::updateFrustumPlanes(cube_cam);
+		cube_cam.setOrigin(gCamera->getOrigin());
+		gPipeline.updateCull(cube_cam);
+		gPipeline.stateSort(cube_cam);
+		
+		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+		gPipeline.renderGeom(cube_cam);
+
+		cube_map->enable(0);
+		cube_map->bind();
+		glCopyTexImage2D(cube_face[i], 0, GL_RGB, 0, 0, res, res, 0);
+		cube_map->disable();
+	}
+
+	cube_cam.setOrigin(origin);
+	gPipeline.resetDrawOrders();
+	gPipeline.mShinyOrigin.setVec(cube_cam.getOrigin(), cube_cam.getFar()*2.f);
+	glMatrixMode(GL_PROJECTION);
+	glPopMatrix();
+	glMatrixMode(GL_MODELVIEW);
+	glPopMatrix();
+
+	gPipeline.setRenderTypeMask(type_mask);
+	LLPipeline::sUseOcclusion = use_occlusion;
+	LLPipeline::sSkipUpdate = FALSE;
+
+	if (toggle_ui)
+	{
+		gPipeline.toggleRenderDebugFeature((void*)LLPipeline::RENDER_DEBUG_FEATURE_UI);
+	}
+	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+	LLDrawPoolWater::sSkipScreenCopy = FALSE;
+}
+
+//send cube map vertices and texture coordinates
+void render_cube_map()
+{
+	if (gPipeline.mCubeList == 0)
+	{
+		gPipeline.mCubeList = glGenLists(1);
+		glNewList(gPipeline.mCubeList, GL_COMPILE);
+
+		U32 idx[36];
+
+		idx[0] = 1; idx[1] = 0; idx[2] = 2; //front
+		idx[3] = 3; idx[4] = 2; idx[5] = 0;
+
+		idx[6] = 4; idx[7] = 5; idx[8] = 1; //top
+		idx[9] = 0; idx[10] = 1; idx[11] = 5; 
+
+		idx[12] = 5; idx[13] = 4; idx[14] = 6; //back
+		idx[15] = 7; idx[16] = 6; idx[17] = 4;
+
+		idx[18] = 6; idx[19] = 7; idx[20] = 3; //bottom
+		idx[21] = 2; idx[22] = 3; idx[23] = 7;
+
+		idx[24] = 0; idx[25] = 5; idx[26] = 3; //left
+		idx[27] = 6; idx[28] = 3; idx[29] = 5;
+
+		idx[30] = 4; idx[31] = 1; idx[32] = 7; //right
+		idx[33] = 2; idx[34] = 7; idx[35] = 1;
+
+		LLVector3 vert[8];
+		LLVector3 r = LLVector3(1,1,1);
+
+		vert[0] = r.scaledVec(LLVector3(-1,1,1)); //   0 - left top front
+		vert[1] = r.scaledVec(LLVector3(1,1,1));  //   1 - right top front
+		vert[2] = r.scaledVec(LLVector3(1,-1,1)); //   2 - right bottom front
+		vert[3] = r.scaledVec(LLVector3(-1,-1,1)); //  3 - left bottom front
+
+		vert[4] = r.scaledVec(LLVector3(1,1,-1)); //  4 - left top back
+		vert[5] = r.scaledVec(LLVector3(-1,1,-1)); //   5 - right top back
+		vert[6] = r.scaledVec(LLVector3(-1,-1,-1)); //  6 - right bottom back
+		vert[7] = r.scaledVec(LLVector3(1,-1,-1)); // 7 -left bottom back
+
+		glBegin(GL_TRIANGLES);
+		for (U32 i = 0; i < 36; i++)
+		{
+			glTexCoord3fv(vert[idx[i]].mV);
+			glVertex3fv(vert[idx[i]].mV);
+		}
+		glEnd();
+
+		glEndList();
+	}
+
+	glCallList(gPipeline.mCubeList);
+
+}
+
+void LLPipeline::blurReflectionMap(LLCubeMap* cube_in, LLCubeMap* cube_out, U32 res)
+{
+	LLGLEnable cube(GL_TEXTURE_CUBE_MAP_ARB);
+	LLGLDepthTest depth(GL_FALSE);
+
+	glMatrixMode(GL_PROJECTION);
+	glPushMatrix();
+	glLoadIdentity();
+	gluPerspective(90.f+45.f/res, 1.f, 0.1f, 1024.f);
+	glMatrixMode(GL_MODELVIEW);
+	glPushMatrix();
+
+	glViewport(0, 0, res, res);
+	LLGLEnable blend(GL_BLEND);
+	
+	S32 kernel = 2;
+	F32 step = 90.f/res;
+	F32 alpha = 1.f/((kernel*2+1));
+
+	glColor4f(1,1,1,alpha);
+
+	S32 x = 0;
+
+	U32 cube_face[] = 
+	{
+		GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB,
+		GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB,
+		GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB,
+		GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB,
+		GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB,
+		GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB,
+	};
+
+	LLVector3 axis[] = 
+	{
+		LLVector3(1,0,0),
+		LLVector3(0,1,0),
+		LLVector3(0,0,1)
+	};
+
+	
+	glBlendFunc(GL_SRC_ALPHA_SATURATE, GL_ONE);
+	//3-axis blur
+	for (U32 j = 0; j < 3; j++)
+	{
+		glViewport(0,0,res, res*6);
+		glClear(GL_COLOR_BUFFER_BIT);
+		if (j == 0)
+		{
+			cube_in->bind();
+		}
+
+		for (U32 i = 0; i < 6; i++)
+		{
+			glViewport(0,i*res, res, res);
+			glLoadIdentity();
+			apply_cube_face_rotation(i);
+			for (x = -kernel; x <= kernel; ++x)
+			{
+				glPushMatrix();
+				glRotatef(x*step, axis[j].mV[0], axis[j].mV[1], axis[j].mV[2]);
+				render_cube_map();
+				glPopMatrix();
+			}
+		}	
+
+		//readback
+		if (j == 0)
+		{
+			cube_out->bind();
+		}
+		for (U32 i = 0; i < 6; i++)
+		{
+			glCopyTexImage2D(cube_face[i], 0, GL_RGB, 0, i*res, res, res, 0);
+		}
+	}
 	
+	glMatrixMode(GL_PROJECTION);
+	glPopMatrix();
+	glMatrixMode(GL_MODELVIEW);
+	glPopMatrix();
+
+	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+}
+
+
diff --git a/indra/newview/pipeline.h b/indra/newview/pipeline.h
index f4144b6afae13c275a5e4306dcc40edc1d8ca1a7..af772fd60df30589de1ffa3187d324cce71b2bfe 100644
--- a/indra/newview/pipeline.h
+++ b/indra/newview/pipeline.h
@@ -9,7 +9,6 @@
 #ifndef LL_PIPELINE_H
 #define LL_PIPELINE_H
 
-#include "llagparray.h"
 #include "lldarrayptr.h"
 #include "lldqueueptr.h"
 #include "llstat.h"
@@ -31,8 +30,7 @@ class LLAgent;
 class LLDisplayPrimitive;
 class LLTextureEntry;
 class LLRenderFunc;
-class LLAGPMemPool;
-class LLAGPMemBlock;
+class LLCubeMap;
 
 typedef enum e_avatar_skinning_method
 {
@@ -43,6 +41,7 @@ typedef enum e_avatar_skinning_method
 BOOL compute_min_max(LLMatrix4& box, LLVector2& min, LLVector2& max); // Shouldn't be defined here!
 bool LLRayAABB(const LLVector3 &center, const LLVector3 &size, const LLVector3& origin, const LLVector3& dir, LLVector3 &coord, F32 epsilon = 0);
 BOOL LLLineSegmentAABB(const LLVector3& start, const LLVector3& end, const LLVector3& center, const LLVector3& size);
+BOOL setup_hud_matrices(BOOL for_select);
 
 class LLGLSLShader
 {
@@ -72,7 +71,6 @@ public:
 	void bind();
 	void unbind();
 
-
 	GLhandleARB mProgramObject;
 	std::vector<GLint> mAttribute;
 	std::vector<GLint> mUniform;
@@ -88,6 +86,11 @@ public:
 
 	void destroyGL();
 	void restoreGL();
+	void resetVertexBuffers();
+	void resetVertexBuffers(LLDrawable* drawable);
+	void setUseVBO(BOOL use_vbo);
+	void generateReflectionMap(LLCubeMap* cube_map, LLCamera& camera, GLsizei res);
+	void blurReflectionMap(LLCubeMap* cube_in, LLCubeMap* cube_out, U32 res);
 
 	void init();
 	void cleanup();
@@ -102,6 +105,7 @@ public:
 
 	/// @brief Figures out draw pool type from texture entry. Creates pool if necessary.
 	static LLDrawPool* getPoolFromTE(const LLTextureEntry* te, LLViewerImage* te_image);
+	static U32 getPoolTypeFromTE(const LLTextureEntry* te, LLViewerImage* imagep);
 
 	void		 addPool(LLDrawPool *poolp);	// Only to be used by LLDrawPool classes for splitting pools!
 	void		 removePool( LLDrawPool* poolp );
@@ -111,28 +115,26 @@ public:
 	void		 unlinkDrawable(LLDrawable*);
 
 	// Object related methods
-	void        markVisible(LLDrawable *drawablep);
-	void		doOcclusion();
-	void		markNotCulled(LLDrawable* drawablep, LLCamera& camera);
+	void        markVisible(LLDrawable *drawablep, LLCamera& camera);
+	void		doOcclusion(LLCamera& camera);
+	void		markNotCulled(LLSpatialGroup* group, LLCamera &camera, BOOL active = FALSE);
 	void        markMoved(LLDrawable *drawablep, BOOL damped_motion = FALSE);
 	void        markShift(LLDrawable *drawablep);
 	void        markTextured(LLDrawable *drawablep);
-	void		markMaterialed(LLDrawable *drawablep);
 	void        markRebuild(LLDrawable *drawablep, LLDrawable::EDrawableFlags flag = LLDrawable::REBUILD_ALL, BOOL priority = FALSE);
+	void		markRebuild(LLSpatialGroup* groupp);
 	void        markRelight(LLDrawable *drawablep, const BOOL now = FALSE);
 	
 	//get the object between start and end that's closest to start.  Return the point of collision in collision.
 	LLViewerObject* pickObject(const LLVector3 &start, const LLVector3 &end, LLVector3 &collision);
-	
-	void        dirtyPoolObjectTextures(const LLViewerImage *texture); // Something about this texture has changed.  Dirty it.
+
+	// Something about these textures has changed.  Dirty them.
+	void        dirtyPoolObjectTextures(const std::set<LLViewerImage*>& textures);
 
 	void        resetDrawOrders();
 
 	U32         addObject(LLViewerObject *obj);
 
-	BOOL		usingAGP() const;
-	void		setUseAGP(const BOOL use_agp);	// Attempt to use AGP;
-
 	void		enableShadows(const BOOL enable_shadows);
 
 // 	void		setLocalLighting(const BOOL local_lighting);
@@ -155,61 +157,43 @@ public:
 	BOOL		validateProgramObject(GLhandleARB obj);
 	GLhandleARB loadShader(const LLString& filename, S32 cls, GLenum type);
 
-	void		setUseOcclusionCulling(BOOL b) { mUseOcclusionCulling = b; }
-	BOOL		getUseOcclusionCulling() const { return mUseOcclusionCulling; }
-
-	BOOL initAGP();
-	void cleanupAGP();
-	LLAGPMemBlock *allocAGPFromPool(const S32 bytes, const U32 target); // Static flag is ignored for now.
-	void flushAGPMemory(); // Clear all AGP memory blocks (to pack & reduce fragmentation)
-
 	// phases
 	void resetFrameStats();
 
 	void updateMoveDampedAsync(LLDrawable* drawablep);
 	void updateMoveNormalAsync(LLDrawable* drawablep);
+	void updateMovedList(LLDrawable::drawable_vector_t& move_list);
 	void updateMove();
-	void updateCull();
+	void updateCull(LLCamera& camera);
 	void updateGeom(F32 max_dtime);
 
-	void stateSort();
+	//calculate pixel area of given box from vantage point of given camera
+	static F32 calcPixelArea(LLVector3 center, LLVector3 size, LLCamera& camera);
 
-	void renderGeom();
+	void stateSort(LLCamera& camera);
+	void stateSort(LLSpatialGroup* group, LLCamera& camera);
+	void stateSort(LLSpatialBridge* bridge, LLCamera& camera);
+	void stateSort(LLDrawable* drawablep, LLCamera& camera);
+	void postSort(LLCamera& camera);
+	void forAllDrawables(LLSpatialGroup::sg_vector_t& groups, void (*func)(LLDrawable*));
+	void forAllVisibleDrawables(void (*func)(LLDrawable*));
+	static void highlightPhysical(LLDrawable* drawablep);
+
+	void renderObjects(U32 type, U32 mask, BOOL texture = TRUE);
+
+	void renderGeom(LLCamera& camera);
 	void renderHighlights();
 	void renderDebug();
+	void processGeometry(LLCamera& camera);
+	void processOcclusion(LLCamera& camera);
 
-	void renderForSelect();
+	void renderForSelect(std::set<LLViewerObject*>& objects);
 	void renderFaceForUVSelect(LLFace* facep);
 	void rebuildPools(); // Rebuild pools
 
-	// bindAGP and unbindAGP are used to bind AGP memory.
-	// AGP should never be bound if you're writing/copying data to AGP.
-	// bindAGP will do the correct thing if AGP rendering has been disabled globally.
-	void bindAGP();
-	void unbindAGP();
-	inline BOOL isAGPBound() const			{ return mAGPBound; }
-
-	void setupVisibility();
-	void computeVisibility();
-
-	LLViewerObject *nearestObjectAt(F32 yaw, F32 pitch);	// CCW yaw from +X = 0 radians, pitch down from +Y = 0 radians, NULL if no object
-
-	void displayPools();
-	void displayAGP();
-	void displayMap();
-	void displaySSBB();
-	void displayQueues();
-	void printPools();
-
 	void findReferences(LLDrawable *drawablep);	// Find the lists which have references to this object
 	BOOL verify();						// Verify that all data in the pipeline is "correct"
 
-	// just the AGP part
-	S32 getAGPMemUsage();
-
-	// all pools
-	S32 getMemUsage(const BOOL print = FALSE);
-
 	S32  getVisibleCount() const { return mVisibleList.size(); }
 	S32  getLightCount() const { return mLights.size(); }
 
@@ -234,18 +218,15 @@ public:
 	BOOL hasRenderDebugFeatureMask(const U32 mask) const	{ return (mRenderDebugFeatureMask & mask) ? TRUE : FALSE; }
 	BOOL hasRenderFeatureMask(const U32 mask) const			{ return (mRenderFeatureMask & mask) ? TRUE : FALSE; }
 	BOOL hasRenderDebugMask(const U32 mask) const			{ return (mRenderDebugMask & mask) ? TRUE : FALSE; }
-
-	// Vertex buffer stuff?
-	U8* bufferGetScratchMemory(void);
-	void bufferWaitFence(void);
-	void bufferSendFence(void);
-	void bufferRotate(void);
+	void setRenderTypeMask(const U32 mask)					{ mRenderTypeMask = mask; }
+	U32  getRenderTypeMask() const							{ return mRenderTypeMask; }
+	static void toggleRenderType(U32 type);
 
 	// For UI control of render features
-	static void toggleRenderType(void* data);
+	static BOOL hasRenderTypeControl(void* data);
 	static void toggleRenderDebug(void* data);
 	static void toggleRenderDebugFeature(void* data);
-	static BOOL toggleRenderTypeControl(void* data);
+	static void toggleRenderTypeControl(void* data);
 	static BOOL toggleRenderTypeControlNegated(void* data);
 	static BOOL toggleRenderDebugControl(void* data);
 	static BOOL toggleRenderDebugFeatureControl(void* data);
@@ -295,20 +276,17 @@ public:
 		RENDER_TYPE_GROUND		= LLDrawPool::POOL_GROUND,	
 		RENDER_TYPE_TERRAIN		= LLDrawPool::POOL_TERRAIN,
 		RENDER_TYPE_SIMPLE		= LLDrawPool::POOL_SIMPLE,
-		RENDER_TYPE_MEDIA		= LLDrawPool::POOL_MEDIA,
 		RENDER_TYPE_BUMP		= LLDrawPool::POOL_BUMP,
 		RENDER_TYPE_AVATAR		= LLDrawPool::POOL_AVATAR,
 		RENDER_TYPE_TREE		= LLDrawPool::POOL_TREE,
-		RENDER_TYPE_TREE_NEW	= LLDrawPool::POOL_TREE_NEW,
 		RENDER_TYPE_WATER		= LLDrawPool::POOL_WATER,
- 		RENDER_TYPE_CLOUDS		= LLDrawPool::POOL_CLOUDS,
  		RENDER_TYPE_ALPHA		= LLDrawPool::POOL_ALPHA,
- 		RENDER_TYPE_HUD			= LLDrawPool::POOL_HUD,
-
 		// Following are object types (only used in drawable mRenderType)
+		RENDER_TYPE_HUD = LLDrawPool::NUM_POOL_TYPES,
 		RENDER_TYPE_VOLUME,
 		RENDER_TYPE_GRASS,
 		RENDER_TYPE_PARTICLES,
+		RENDER_TYPE_CLOUDS,
 	};
 
 	enum LLRenderDebugFeatureMask
@@ -322,13 +300,12 @@ public:
 		RENDER_DEBUG_FEATURE_FOG				= 0x0020,
 		RENDER_DEBUG_FEATURE_PALETTE			= 0x0040,
 		RENDER_DEBUG_FEATURE_FR_INFO			= 0x0080,
-		RENDER_DEBUG_FEATURE_CHAIN_FACES		= 0x0100
+		RENDER_DEBUG_FEATURE_FOOT_SHADOWS		= 0x0100,
 	};
 
 	enum LLRenderFeatureMask
 	{
-		RENDER_FEATURE_AGP					= 0x01,
-// 		RENDER_FEATURE_LOCAL_LIGHTING		= 0x02,
+		RENDER_FEATURE_LOCAL_LIGHTING		= 0x02,
 		RENDER_FEATURE_OBJECT_BUMP			= 0x04,
 		RENDER_FEATURE_AVATAR_BUMP			= 0x08,
 // 		RENDER_FEATURE_SHADOWS				= 0x10,
@@ -337,26 +314,44 @@ public:
 
 	enum LLRenderDebugMask
 	{
-		RENDER_DEBUG_LIGHT_TRACE		= 0x0001,
-		RENDER_DEBUG_POOLS				= 0x0002,
-		RENDER_DEBUG_MAP				= 0x0004,
-		RENDER_DEBUG_AGP_MEM			= 0x0008,
-		RENDER_DEBUG_QUEUES				= 0x0010,
-		RENDER_DEBUG_COMPOSITION		= 0x0020,
-		RENDER_DEBUG_SSBB				= 0x0040,
-		RENDER_DEBUG_VERIFY				= 0x0080,
-		RENDER_DEBUG_SHADOW_MAP			= 0x0100,
-		RENDER_DEBUG_BBOXES				= 0x0200,
-		RENDER_DEBUG_OCTREE				= 0x0400,
-		RENDER_DEBUG_FACE_CHAINS		= 0x0800,
-		RENDER_DEBUG_OCCLUSION			= 0x1000,
-		RENDER_DEBUG_POINTS				= 0x2000,
-		RENDER_DEBUG_TEXTURE_PRIORITY	= 0x4000,
+		RENDER_DEBUG_LIGHT_TRACE		= 0x00001,
+		RENDER_DEBUG_COMPOSITION		= 0x00020,
+		RENDER_DEBUG_VERIFY				= 0x00080,
+		RENDER_DEBUG_SHADOW_MAP			= 0x00100,
+		RENDER_DEBUG_BBOXES				= 0x00200,
+		RENDER_DEBUG_OCTREE				= 0x00400,
+		RENDER_DEBUG_PICKING			= 0x00800,
+		RENDER_DEBUG_OCCLUSION			= 0x01000,
+		RENDER_DEBUG_POINTS				= 0x02000,
+		RENDER_DEBUG_TEXTURE_PRIORITY	= 0x04000,
+		RENDER_DEBUG_TEXTURE_AREA		= 0x08000,
+		RENDER_DEBUG_PARTICLES			= 0x10000,
 	};
 
 	LLPointer<LLViewerImage>	mAlphaSizzleImagep;
 
-	LLSpatialPartition *mObjectPartition;
+	//MUST MATCH THE ORDER OF DECLARATION IN LLPipeline::init()
+	typedef enum 
+	{
+		PARTITION_VOLUME = 0,
+		PARTITION_BRIDGE,
+		PARTITION_HUD,
+		PARTITION_TERRAIN,
+		PARTITION_WATER,
+		PARTITION_TREE,
+		PARTITION_PARTICLE,
+		PARTITION_CLOUD,
+		PARTITION_GRASS,
+		PARTITION_NONE,
+		NUM_PARTITIONS
+	} eObjectPartitions;
+
+private:
+	std::vector<LLSpatialPartition*> mObjectPartition;
+public:
+	
+	LLSpatialPartition* getSpatialPartition(LLViewerObject* vobj);
+	LLSpatialPartition* getSpatialPartition(U32 index);
 
 	BOOL					 mBackfaceCull;
 	S32						 mTrianglesDrawn;
@@ -375,10 +370,16 @@ public:
 	LLStat					 mNumVisibleFacesStat;
 	LLStat					 mNumVisibleDrawablesStat;
 
-	static S32				sAGPMaxPoolSize;
 	static S32				sCompiles;
 
-	BOOL					mUseVBO;		// Use ARB vertex buffer objects, if available
+	static BOOL				sShowHUDAttachments;
+	static BOOL				sUseOcclusion;
+	static BOOL				sSkipUpdate; //skip lod updates
+	static BOOL				sDynamicReflections;
+
+	//cube map for anti-aliasing reflections
+	LLCubeMap*				mCubeBuffer;
+	GLuint					mCubeList;
 
 	class LLScatterShader
 	{
@@ -413,15 +414,25 @@ public:
 		GLSL_SPECULAR_MAP,
 		GLSL_BUMP_MAP,
 		GLSL_ENVIRONMENT_MAP,
-		GLSL_SCATTER_MAP,
 		GLSL_END_RESERVED_UNIFORMS
 	} eGLSLReservedUniforms;
 
+	static const char* sShinyUniforms[];
+	static U32 sShinyUniformCount;
+
+	typedef enum
+	{
+		GLSL_SHINY_ORIGIN = GLSL_END_RESERVED_UNIFORMS
+	} eShinyUniforms;
+
+	LLVector4				mShinyOrigin;
+
 	//object shaders
 	LLGLSLShader			mObjectSimpleProgram;
 	LLGLSLShader			mObjectAlphaProgram;
 	LLGLSLShader			mObjectBumpProgram;
-	
+	LLGLSLShader			mObjectShinyProgram;
+
 	//water parameters
 	static const char* sWaterUniforms[];
 	static U32 sWaterUniformCount;
@@ -496,15 +507,17 @@ public:
 
 	LLColor4				mSunDiffuse;
 	LLVector3				mSunDir;
+
+	LLSpatialGroup::sg_vector_t mActiveGroups;
+	std::vector<LLDrawInfo*> mRenderMap[LLRenderPass::NUM_RENDER_TYPES];
+	std::vector<LLSpatialGroup* > mAlphaGroups;
+	std::vector<LLSpatialGroup* > mAlphaGroupsPostWater;
+	LLSpatialGroup::sg_vector_t mVisibleGroups;
+	LLSpatialGroup::sg_vector_t mDrawableGroups;
+
+	void clearRenderMap();
 	
 protected:
-	class SelectedFaceInfo
-	{
-	public:
-		LLFace *mFacep;
-		S32 mTE;
-	};
-
 	BOOL					mVertexShadersEnabled;
 	S32						mVertexShadersLoaded; // 0 = no, 1 = yes, -1 = failed
 	S32						mVertexShaderLevel[SHADER_COUNT];
@@ -515,12 +528,16 @@ protected:
 	U32						mRenderDebugFeatureMask;
 	U32						mRenderDebugMask;
 
+	U32						mOldRenderDebugMask;
+	
 	/////////////////////////////////////////////
 	//
 	//
 	LLDrawable::drawable_vector_t	mVisibleList;
-	LLDrawable::drawable_set_t		mMovedList;
-	
+	LLSpatialBridge::bridge_vector_t mVisibleBridge;
+	LLSpatialBridge::bridge_vector_t mOccludedBridge;
+	LLDrawable::drawable_vector_t	mMovedList;
+	LLDrawable::drawable_vector_t mMovedBridge;
 	LLDrawable::drawable_vector_t	mShiftList;
 
 	/////////////////////////////////////////////
@@ -559,13 +576,13 @@ protected:
 	//
 	// Different queues of drawables being processed.
 	//
-	LLDrawable::drawable_set_t 		mBuildQ1; // priority
+	LLDrawable::drawable_list_t 	mBuildQ1; // priority
 	LLDrawable::drawable_list_t 	mBuildQ2; // non-priority
+	LLSpatialGroup::sg_set_t		mGroupQ; //spatial groups
 	
 	LLDrawable::drawable_set_t		mActiveQ;
 	
 	LLDrawable::drawable_set_t		mRetexturedList;
-	LLDrawable::drawable_set_t		mRematerialedList;
 
 	//////////////////////////////////////////////////
 	//
@@ -598,45 +615,27 @@ protected:
 	LLDrawPool*	mLastRebuildPool;
 	
 	// For quick-lookups into mPools (mapped by texture pointer)
-	std::map<uintptr_t, LLDrawPool*>	mSimplePools;
 	std::map<uintptr_t, LLDrawPool*>	mTerrainPools;
 	std::map<uintptr_t, LLDrawPool*>	mTreePools;
-	std::map<uintptr_t, LLDrawPool*>	mTreeNewPools;
-	std::map<uintptr_t, LLDrawPool*>	mBumpPools;
-	std::map<uintptr_t, LLDrawPool*>	mMediaPools;
 	LLDrawPool*					mAlphaPool;
+	LLDrawPool*					mAlphaPoolPostWater;
 	LLDrawPool*					mSkyPool;
 	LLDrawPool*					mStarsPool;
-	LLDrawPool*					mCloudsPool;
 	LLDrawPool*					mTerrainPool;
 	LLDrawPool*					mWaterPool;
 	LLDrawPool*					mGroundPool;
-	LLDrawPool*					mHUDPool;
+	LLRenderPass*				mSimplePool;
+	LLDrawPool*					mBumpPool;
 	// Note: no need to keep an quick-lookup to avatar pools, since there's only one per avatar
 	
 
-	LLDynamicArray<LLFace*>		mHighlightFaces;	// highlight faces on physical objects
-	LLDynamicArray<SelectedFaceInfo>	mSelectedFaces;
+	std::vector<LLFace*>		mHighlightFaces;	// highlight faces on physical objects
+	std::vector<LLFace*>		mSelectedFaces;
 
 	LLPointer<LLViewerImage>	mFaceSelectImagep;
 	LLPointer<LLViewerImage>	mBloomImagep;
 	LLPointer<LLViewerImage>	mBloomImage2p;
-
-	BOOL					mAGPBound;
-	LLAGPMemPool            *mAGPMemPool;
-	U32						mGlobalFence;
-
-	// Round-robin AGP buffers for use by the software skinner
-	enum
-	{
-		kMaxBufferCount = 4
-	};
 	
-	S32						mBufferIndex;
-	S32						mBufferCount;
-	LLAGPArray<U8>			*mBufferMemory[kMaxBufferCount];
-	U32						mBufferFence[kMaxBufferCount];
-	BOOL					mUseOcclusionCulling;	// object-object occlusion culling
 	U32						mLightMask;
 	U32						mLightMovingMask;
 	S32						mLightingDetail;
diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py
index 9f6a001e4f7df657df67609b62baedbdefc84b79..baa023cd4eee9952c82987617053d5e0467cf372 100755
--- a/indra/newview/viewer_manifest.py
+++ b/indra/newview/viewer_manifest.py
@@ -49,6 +49,8 @@ class ViewerManifest(LLManifest):
 
                 # XUI
                 if self.prefix(src="skins"):
+                        # include the entire textures directory recursively
+                        self.path("textures")
                         self.path("paths.xml")
                         self.path("xui/*/*.xml")
                         self.path('words.*.txt')
@@ -98,6 +100,8 @@ class WindowsManifest(ViewerManifest):
                 # *NOTE: these are the only two executable names that the crash reporter recognizes
                 if self.args['grid'] == '':
                         return "SecondLife.exe"
+                elif self.args['grid'] == 'firstlook':
+                        return "SecondLifeFirstLook.exe"
                 else:
                         return "SecondLifePreview.exe"
                         # return "SecondLifePreview%s.exe" % (self.args['grid'], )
@@ -396,7 +400,7 @@ class Linux_i686Manifest(LinuxManifest):
                         self.path("libvorbis.so.0")
                         self.path("libvorbisfile.so.0")
                         self.path("libvorbisenc.so.0")
-                        self.path("libcurl.so.3")
+                        self.path("libcurl.so.4")
                         self.path("libcrypto.so.0.9.7")
                         self.path("libssl.so.0.9.7")
                         self.path("libexpat.so.1")