Index: GTP/trunk/Lib/Vis/OnlineCullingCHC/OGRE/src/OgreBiTerrainSceneManager.cpp
===================================================================
--- GTP/trunk/Lib/Vis/OnlineCullingCHC/OGRE/src/OgreBiTerrainSceneManager.cpp	(revision 2348)
+++ GTP/trunk/Lib/Vis/OnlineCullingCHC/OGRE/src/OgreBiTerrainSceneManager.cpp	(revision 2348)
@@ -0,0 +1,931 @@
+/*
+-----------------------------------------------------------------------------
+This source file is part of the GameTools Project
+http://www.gametools.org
+
+Author: Martin Szydlowski
+-----------------------------------------------------------------------------
+*/
+#include <OgreGpuProgramManager.h>
+#include <OgreConfigFile.h>
+#include <OgreMaterialManager.h>
+#include <OgreLogManager.h>
+
+#include "OgreBiTerrainPage.h"
+#include "OgreBiHeightmapTerrainPageSource.h"
+#include "OgreBiTerrainVertexProgram.h"
+
+#include "OgreBiTerrainSceneManager.h"
+
+#define TERRAIN_MATERIAL_NAME "TerrainSceneManager/Terrain"
+
+namespace Ogre
+{
+
+BiTerrainSceneManager::BiTerrainSceneManager(const String& name, GtpVisibility::VisibilityManager *vm):
+BiHierarchySceneManager(name, vm),
+mUseCustomMaterial(false),
+mUseNamedParameterLodMorph(false),
+mLodMorphParamIndex(3),
+mTerrainRoot(0),
+mActivePageSource(0),
+mPagingEnabled(false),
+mLivePageMargin(0),
+mBufferedPageMargin(0)
+{
+
+}
+//-----------------------------------------------------------------------
+BiTerrainSceneManager::~BiTerrainSceneManager()
+{
+	shutdown();
+}
+//-----------------------------------------------------------------------
+const String& BiTerrainSceneManager::getTypeName() const
+{
+	return BiTerrainSceneManagerFactory::FACTORY_TYPE_NAME;
+}
+//-----------------------------------------------------------------------
+void BiTerrainSceneManager::clearScene()
+{
+	BiHierarchySceneManager::clearScene();
+
+	// clear terrain
+	mTerrainPages.clear();
+	destroyLevelIndexes();
+	// sm has destroyed our root
+	mTerrainRoot = 0;
+}
+//-----------------------------------------------------------------------
+bool BiTerrainSceneManager::setOption(const String& strKey, const void* pValue)
+{
+	// options for terrain
+	if (strKey == "PageSize")
+	{
+		setPageSize(*static_cast<const int*>(pValue));
+		return true;
+	} 
+	else if (strKey == "TileSize")
+	{
+		setTileSize(*static_cast<const int*>(pValue));
+		return true;
+	}
+	else if (strKey == "PrimaryCamera")
+	{
+		setPrimaryCamera(static_cast<const Camera*>(pValue));
+		return true;
+	}
+	else if (strKey == "MaxMipMapLevel")
+	{
+		setMaxGeoMipMapLevel(*static_cast<const int*>(pValue));
+		return true;
+	}
+	else if (strKey == "Scale")
+	{
+		setScale(*static_cast<const Vector3*>(pValue));
+		return true;
+	}
+	else if (strKey == "MaxPixelError")
+	{
+		setMaxPixelError(*static_cast<const int*>(pValue));
+		return true;
+	}
+	else if (strKey == "UseTriStrips")
+	{
+		setUseTriStrips(*static_cast<const bool*>(pValue));
+		return true;
+	}
+	else if (strKey == "VertexProgramMorph")
+	{
+		setUseLODMorph(*static_cast<const bool*>(pValue));
+		return true;
+	}
+	else if (strKey == "DetailTile")
+	{
+		setDetailTextureRepeat(*static_cast<const int*>(pValue));
+		return true;
+	}
+	else if (strKey == "LodMorphStart")
+	{
+		setLODMorphStart(*static_cast<const Real*>(pValue));
+		return true;
+	}
+	else if (strKey == "VertexNormals")
+	{
+		setUseVertexNormals(*static_cast<const bool*>(pValue));
+		return true;
+	}
+	else if (strKey == "VertexColours")
+	{
+		setUseVertexColours(*static_cast<const bool*>(pValue));
+		return true;
+	}
+	else if (strKey == "MorphLODFactorParamName")
+	{
+		setCustomMaterialMorphFactorParam(*static_cast<const String*>(pValue));
+		return true;
+	}
+	else if (strKey == "MorphLODFactorParamIndex")
+	{
+		setCustomMaterialMorphFactorParam(*static_cast<const uint*>(pValue));
+		return true;
+	}
+	else if (strKey == "CustomMaterialName")
+	{
+		setCustomMaterial(*static_cast<const String*>(pValue));
+		return true;
+	}
+	else if (strKey == "WorldTexture")
+	{
+		setWorldTexture(*static_cast<const String*>(pValue));
+		return true;
+	}
+	else if (strKey == "DetailTexture")
+	{
+		setDetailTexture(*static_cast<const String*>(pValue));
+		return true;
+	}
+
+	return BiHierarchySceneManager::setOption(strKey, pValue);
+}
+//-----------------------------------------------------------------------
+Camera* BiTerrainSceneManager::createCamera(const String &name )
+{
+	Camera* cam = BiHierarchySceneManager::createCamera(name);
+
+	// Set primary camera, if none
+	if (!mOptions.primaryCamera)
+		setPrimaryCamera(cam);
+
+	return cam;
+}
+//-------------------------------------------------------------------------
+//void _findVisibleObjects(Camera *cam, bool onlyShadowCasters)
+//{
+//	BiHierarchySceneManager::_findVisibleObjects(cam, onlyShadowCasters);
+//}
+//-------------------------------------------------------------------------
+void BiTerrainSceneManager::_renderScene(Camera* cam, Viewport *vp, bool includeOverlays)
+{
+	// For now, no paging and expect immediate response
+	if (!mTerrainPages.empty() && mTerrainPages[0][0] == 0)
+	{
+		mActivePageSource->requestPage(0, 0);
+	}
+	BiHierarchySceneManager::_renderScene(cam, vp, includeOverlays);
+
+}
+//-----------------------------------------------------------------------
+//void BiTerrainSceneManager::_renderVisibleObjects()
+//{
+//	BiHierarchySceneManager::_renderVisibleObjects();
+//}
+//-------------------------------------------------------------------------
+void BiTerrainSceneManager::shutdown(void)
+{
+	// Make sure the indexes are destroyed during orderly shutdown
+	// and not when statics are destroyed (may be too late)
+	mIndexCache.shutdown();
+	destroyLevelIndexes();
+
+	// Make sure we free up material (static)
+	mOptions.terrainMaterial.setNull();
+
+}
+//-------------------------------------------------------------------------
+void BiTerrainSceneManager::loadConfig(DataStreamPtr& stream)
+{
+	/* Set up the options */
+	ConfigFile config;
+	String val;
+
+	config.load( stream );
+
+	val = config.getSetting( "DetailTile" );
+	if ( !val.empty() )
+		setDetailTextureRepeat(atoi(val.c_str()));
+
+	val = config.getSetting( "MaxMipMapLevel" );
+	if ( !val.empty() )
+		setMaxGeoMipMapLevel(atoi( val.c_str() ));
+
+
+	val = config.getSetting( "PageSize" );
+	if ( !val.empty() )
+		setPageSize(atoi( val.c_str() ));
+	else
+		OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, "Missing option 'PageSize'",
+		"TerrainSceneManager::loadConfig");
+
+
+	val = config.getSetting( "TileSize" );
+	if ( !val.empty() )
+		setTileSize(atoi( val.c_str() ));
+	else
+		OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, "Missing option 'TileSize'",
+		"TerrainSceneManager::loadConfig");
+
+	Vector3 v = Vector3::UNIT_SCALE;
+
+	val = config.getSetting( "PageWorldX" );
+	if ( !val.empty() )
+		v.x = atof( val.c_str() );
+
+	val = config.getSetting( "MaxHeight" );
+	if ( !val.empty() )
+		v.y = atof( val.c_str() );
+
+	val = config.getSetting( "PageWorldZ" );
+	if ( !val.empty() )
+		v.z = atof( val.c_str() );
+
+	// Scale x/z relative to pagesize
+	v.x /= mOptions.pageSize;
+	v.z /= mOptions.pageSize;
+	setScale(v);
+
+	val = config.getSetting( "MaxPixelError" );
+	if ( !val.empty() )
+		setMaxPixelError(atoi( val.c_str() ));
+
+	mDetailTextureName = config.getSetting( "DetailTexture" );
+
+	mWorldTextureName = config.getSetting( "WorldTexture" );
+
+	if ( config.getSetting( "VertexColours" ) == "yes" )
+		mOptions.coloured = true;
+
+	if ( config.getSetting( "VertexNormals" ) == "yes" )
+		mOptions.lit = true;
+
+	if ( config.getSetting( "UseTriStrips" ) == "yes" )
+		setUseTriStrips(true);
+
+	if ( config.getSetting( "VertexProgramMorph" ) == "yes" )
+		setUseLODMorph(true);
+
+	val = config.getSetting( "LODMorphStart");
+	if ( !val.empty() )
+		setLODMorphStart(atof(val.c_str()));
+
+	val = config.getSetting( "CustomMaterialName" );
+	if ( !val.empty() )
+		setCustomMaterial(val);
+
+	val = config.getSetting( "MorphLODFactorParamName" );
+	if ( !val.empty() )
+		setCustomMaterialMorphFactorParam(val);
+
+	val = config.getSetting( "MorphLODFactorParamIndex" );
+	if ( !val.empty() )
+		setCustomMaterialMorphFactorParam(atoi(val.c_str()));
+
+	// Now scan through the remaining settings, looking for any PageSource
+	// prefixed items
+	String pageSourceName = config.getSetting("PageSource");
+	if (pageSourceName == "")
+	{
+		OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, "Missing option 'PageSource'",
+			"TerrainSceneManager::loadConfig");
+	}
+	BiTerrainPageSourceOptionList optlist;
+	ConfigFile::SettingsIterator setIt = config.getSettingsIterator();
+	while (setIt.hasMoreElements())
+	{
+		String name = setIt.peekNextKey();
+		String value = setIt.getNext();
+		if (StringUtil::startsWith(name, pageSourceName, false))
+		{
+			optlist.push_back(BiTerrainPageSourceOption(name, value));
+		}
+	}
+	// set the page source
+	selectPageSource(pageSourceName, optlist);
+}
+//-------------------------------------------------------------------------
+void BiTerrainSceneManager::setupTerrainMaterial(void)
+{
+	if (mCustomMaterialName == "")
+	{
+		// define our own material
+		mOptions.terrainMaterial = 
+			MaterialManager::getSingleton().getByName(TERRAIN_MATERIAL_NAME);
+		if (mOptions.terrainMaterial.isNull())
+		{
+			// Make unique terrain material name
+			StringUtil::StrStreamType s;
+			s << mName << "/Terrain";
+			mOptions.terrainMaterial = MaterialManager::getSingleton().create(
+				s.str(),
+				ResourceGroupManager::getSingleton().getWorldResourceGroupName());
+
+		}
+		else
+		{
+			mOptions.terrainMaterial->getTechnique(0)->getPass(0)->removeAllTextureUnitStates();
+		}
+
+		Pass* pass = mOptions.terrainMaterial->getTechnique(0)->getPass(0);
+
+		if ( mWorldTextureName != "" )
+		{
+			pass->createTextureUnitState( mWorldTextureName, 0 );
+		}
+		if ( mDetailTextureName != "" )
+		{
+			pass->createTextureUnitState( mDetailTextureName, 1 );
+		}
+
+		mOptions.terrainMaterial -> setLightingEnabled( mOptions.lit );
+
+		if (mOptions.lodMorph && 
+			mDestRenderSystem->getCapabilities()->hasCapability(RSC_VERTEX_PROGRAM) &&
+			GpuProgramManager::getSingleton().getByName("Terrain/VertexMorph").isNull())
+		{
+			// Create & assign LOD morphing vertex program
+			String syntax;
+			if (GpuProgramManager::getSingleton().isSyntaxSupported("arbvp1"))
+			{
+				syntax = "arbvp1";
+			}
+			else
+			{
+				syntax = "vs_1_1";
+			}
+
+			// Get source, and take into account current fog mode
+			FogMode fm = getFogMode();
+			const String& source = BiTerrainVertexProgram::getProgramSource(
+				fm, syntax);
+
+			GpuProgramPtr prog = GpuProgramManager::getSingleton().createProgramFromString(
+				"Terrain/VertexMorph", ResourceGroupManager::getSingleton().getWorldResourceGroupName(), 
+				source, GPT_VERTEX_PROGRAM, syntax);
+
+			// Attach
+			pass->setVertexProgram("Terrain/VertexMorph");
+
+			// Get params
+			GpuProgramParametersSharedPtr params = pass->getVertexProgramParameters();
+
+			// worldviewproj
+			params->setAutoConstant(0, GpuProgramParameters::ACT_WORLDVIEWPROJ_MATRIX);
+			// morph factor
+			params->setAutoConstant(4, GpuProgramParameters::ACT_CUSTOM, MORPH_CUSTOM_PARAM_ID);
+			// fog exp density(if relevant)
+			if (fm == FOG_EXP || fm == FOG_EXP2)
+			{
+				params->setConstant(5, Vector3(getFogDensity(), 0, 0));
+				// Override scene fog since otherwise it's applied twice
+				// Set to linear and we derive [0,1] fog value in the shader
+				pass->setFog(true, FOG_LINEAR, getFogColour(), 0, 1, 0);
+			}
+
+			// Also set shadow receiver program
+			const String& source2 = BiTerrainVertexProgram::getProgramSource(
+				fm, syntax, true);
+
+			prog = GpuProgramManager::getSingleton().createProgramFromString(
+				"Terrain/VertexMorphShadowReceive", 
+				ResourceGroupManager::getSingleton().getWorldResourceGroupName(), 
+				source2, GPT_VERTEX_PROGRAM, syntax);
+			pass->setShadowReceiverVertexProgram("Terrain/VertexMorphShadowReceive");
+			params = pass->getShadowReceiverVertexProgramParameters();
+			// worldviewproj
+			params->setAutoConstant(0, GpuProgramParameters::ACT_WORLDVIEWPROJ_MATRIX);
+			// world
+			params->setAutoConstant(4, GpuProgramParameters::ACT_WORLD_MATRIX);
+			// texture view / proj
+			params->setAutoConstant(8, GpuProgramParameters::ACT_TEXTURE_VIEWPROJ_MATRIX);
+			// morph factor
+			params->setAutoConstant(12, GpuProgramParameters::ACT_CUSTOM, MORPH_CUSTOM_PARAM_ID);
+
+
+			// Set param index
+			mLodMorphParamName = "";
+			mLodMorphParamIndex = 4;
+		}
+
+		mOptions.terrainMaterial->load();
+
+	}
+	else
+	{
+		// Custom material
+		mOptions.terrainMaterial = 
+			MaterialManager::getSingleton().getByName(mCustomMaterialName);
+		mOptions.terrainMaterial->load();
+
+	}
+
+	// now set up the linkage between vertex program and LOD morph param
+	if (mOptions.lodMorph)
+	{
+		Technique* t = mOptions.terrainMaterial->getBestTechnique();
+		for (ushort i = 0; i < t->getNumPasses(); ++i)
+		{
+			Pass* p = t->getPass(i);
+			if (p->hasVertexProgram())
+			{
+				// we have to assume vertex program includes LOD morph capability
+				GpuProgramParametersSharedPtr params = 
+					p->getVertexProgramParameters();
+				// Check to see if custom param is already there
+				GpuProgramParameters::AutoConstantIterator aci = params->getAutoConstantIterator();
+				bool found = false;
+				while (aci.hasMoreElements())
+				{
+					const GpuProgramParameters::AutoConstantEntry& ace = aci.getNext();
+					if (ace.paramType == GpuProgramParameters::ACT_CUSTOM && 
+						ace.data == MORPH_CUSTOM_PARAM_ID)
+					{
+						found = true;
+					}
+				}
+				if (!found)
+				{
+					if(mLodMorphParamName != "")
+					{
+						params->setNamedAutoConstant(mLodMorphParamName, 
+							GpuProgramParameters::ACT_CUSTOM, MORPH_CUSTOM_PARAM_ID);
+					}
+					else
+					{
+						params->setAutoConstant(mLodMorphParamIndex, 
+							GpuProgramParameters::ACT_CUSTOM, MORPH_CUSTOM_PARAM_ID);
+					}
+				}
+
+			}
+		}
+	}
+
+}
+//-------------------------------------------------------------------------
+void BiTerrainSceneManager::setupTerrainPages(void)
+{
+
+	//create a root terrain node.
+	if (!mTerrainRoot)
+		mTerrainRoot = getRootSceneNode() -> createChildSceneNode( "Terrain" );
+
+	//setup the page array.
+	unsigned short pageSlots = 1 + (mBufferedPageMargin * 2);
+	unsigned short i, j;
+	for (i = 0; i < pageSlots; ++i)
+	{
+		mTerrainPages.push_back(BiTerrainPageRow());
+		for (j = 0; j < pageSlots; ++j)
+		{
+			mTerrainPages[i].push_back(0);
+		}
+	}
+
+	// If we're not paging, load immediate for convenience
+	if ( mActivePageSource && !mPagingEnabled )
+		mActivePageSource->requestPage(0,0);
+
+
+}
+//-------------------------------------------------------------------------
+void BiTerrainSceneManager::setWorldGeometry( const String& filename )
+{
+	// try to open in the current folder first
+	std::ifstream fs;
+	fs.open(filename.c_str());
+	if (fs)
+	{
+		// Wrap as a stream
+		DataStreamPtr stream(
+			new FileStreamDataStream(filename, &fs, false));
+		setWorldGeometry(stream);
+	}
+	else
+	{
+		// otherwise try resource system
+		DataStreamPtr stream = 
+			ResourceGroupManager::getSingleton().openResource(filename, 
+			ResourceGroupManager::getSingleton().getWorldResourceGroupName());
+
+		setWorldGeometry(stream);
+	}
+}
+//-------------------------------------------------------------------------
+void BiTerrainSceneManager::setWorldGeometry(DataStreamPtr& stream, const String& typeName)
+{
+	// Clear out any existing world resources (if not default)
+	if (ResourceGroupManager::getSingleton().getWorldResourceGroupName() != 
+		ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME)
+	{
+		ResourceGroupManager::getSingleton().clearResourceGroup(
+			ResourceGroupManager::getSingleton().getWorldResourceGroupName());
+	}
+	destroyLevelIndexes();
+	mTerrainPages.clear();
+	// Load the configuration
+	loadConfig(stream);
+	initLevelIndexes();
+
+	// Resize the octree, allow for 1 page for now
+	//float max_x = mOptions.scale.x * mOptions.pageSize;
+	//float max_y = mOptions.scale.y;
+	//float max_z = mOptions.scale.z * mOptions.pageSize;
+	//resize( AxisAlignedBox( 0, 0, 0, max_x, max_y, max_z ) );
+
+	setupTerrainMaterial();
+
+	setupTerrainPages();
+
+}
+//-------------------------------------------------------------------------
+void BiTerrainSceneManager::attachPage(ushort pageX, ushort pageZ, BiTerrainPage* page)
+{
+	assert(pageX == 0 && pageZ == 0 && "Multiple pages not yet supported");
+
+	assert(mTerrainPages[pageX][pageZ] == 0 && "Page at that index not yet expired!");
+	// Insert page into list
+	mTerrainPages[pageX][pageZ] = page;
+	// Attach page to terrain root
+	mTerrainRoot->addChild(page->pageSceneNode);
+
+}
+//-------------------------------------------------------------------------
+float BiTerrainSceneManager::getHeightAt( float x, float z )
+{
+
+
+	Vector3 pt( x, 0, z );
+
+	BiTerrainRenderable * t = getTerrainTile( pt );
+
+	if ( t == 0 )
+	{
+		//  printf( "No tile found for point\n" );
+		return -1;
+	}
+
+	float h = t -> getHeightAt( x, z );
+
+	// printf( "Height is %f\n", h );
+	return h;
+
+}
+//-------------------------------------------------------------------------
+BiTerrainPage* BiTerrainSceneManager::getTerrainPage( const Vector3 & pt )
+{
+	if (mPagingEnabled)
+	{
+		// TODO
+		return 0;
+	}
+	else
+	{
+		// Single page
+		if (mTerrainPages.empty() || mTerrainPages[0].empty())
+			return 0;
+		return mTerrainPages[0][0];
+	}
+}
+//-------------------------------------------------------------------------
+BiTerrainRenderable * BiTerrainSceneManager::getTerrainTile( const Vector3 & pt )
+{
+	BiTerrainPage* tp = getTerrainPage(pt);
+	if (!tp)
+		return NULL;
+	else
+		return tp->getTerrainTile(pt);
+}
+//-------------------------------------------------------------------------
+bool BiTerrainSceneManager::intersectSegment( const Vector3 & start, 
+										  const Vector3 & end, Vector3 * result )
+{
+	BiTerrainRenderable * t = getTerrainTile( start );
+
+	if ( t == 0 )
+	{
+		*result = Vector3( -1, -1, -1 );
+		return false;
+	}
+
+	return t -> intersectSegment( start, end, result );
+}
+//-------------------------------------------------------------------------
+void BiTerrainSceneManager::setUseTriStrips(bool useStrips)
+{
+	mOptions.useTriStrips = useStrips;
+}
+//-------------------------------------------------------------------------
+void BiTerrainSceneManager::setUseLODMorph(bool morph)
+{
+	// Set true only if vertex programs are supported
+	mOptions.lodMorph = morph && 
+		mDestRenderSystem->getCapabilities()->hasCapability(RSC_VERTEX_PROGRAM);
+}
+//-------------------------------------------------------------------------
+void BiTerrainSceneManager::setUseVertexNormals(bool useNormals)
+{
+	mOptions.lit = useNormals;
+}
+//-------------------------------------------------------------------------
+void BiTerrainSceneManager::setUseVertexColours(bool useColours)
+{
+	mOptions.coloured = useColours;
+}
+//-------------------------------------------------------------------------
+void BiTerrainSceneManager::setWorldTexture(const String& textureName)
+{
+	mWorldTextureName = textureName;
+}
+//-------------------------------------------------------------------------
+void BiTerrainSceneManager::setDetailTexture(const String& textureName)
+{
+	mDetailTextureName = textureName;
+
+}
+//-------------------------------------------------------------------------
+void BiTerrainSceneManager::setDetailTextureRepeat(int repeat)
+{
+	mOptions.detailTile = repeat;
+}
+//-------------------------------------------------------------------------
+void BiTerrainSceneManager::setTileSize(int size) 
+{
+	mOptions.tileSize = size;
+}
+//-------------------------------------------------------------------------
+void BiTerrainSceneManager::setPageSize(int size)
+{
+	mOptions.pageSize = size;
+}
+//-------------------------------------------------------------------------
+void BiTerrainSceneManager::setMaxPixelError(int pixelError) 
+{
+	mOptions.maxPixelError = pixelError;
+}
+//-------------------------------------------------------------------------
+void BiTerrainSceneManager::setScale(const Vector3& scale)
+{
+	mOptions.scale = scale;
+}
+//-------------------------------------------------------------------------
+void BiTerrainSceneManager::setMaxGeoMipMapLevel(int maxMip)
+{
+	mOptions.maxGeoMipMapLevel = maxMip;
+}
+//-------------------------------------------------------------------------
+void BiTerrainSceneManager::setCustomMaterial(const String& materialName)
+{
+	mCustomMaterialName = materialName;
+	if (materialName != "")
+		mUseCustomMaterial = true;
+	else
+		mUseCustomMaterial = false;
+}
+//-------------------------------------------------------------------------
+void BiTerrainSceneManager::setCustomMaterialMorphFactorParam(const String& paramName)
+{
+	mUseNamedParameterLodMorph = true;
+	mLodMorphParamName = paramName;
+
+}
+//-------------------------------------------------------------------------
+void BiTerrainSceneManager::setCustomMaterialMorphFactorParam(uint paramIndex)
+{
+	mUseNamedParameterLodMorph = false;
+	mLodMorphParamIndex = paramIndex;
+}
+//-------------------------------------------------------------------------
+void BiTerrainSceneManager::setLODMorphStart(Real morphStart)
+{
+	mOptions.lodMorphStart = morphStart;
+}
+//-------------------------------------------------------------------------
+void BiTerrainSceneManager::setPrimaryCamera(const Camera* cam)
+{
+	mOptions.primaryCamera = cam;
+}
+//-------------------------------------------------------------------------
+void BiTerrainSceneManager::registerPageSource(const String& typeName, 
+											BiTerrainPageSource* source)
+{
+	std::pair<BiPageSourceMap::iterator, bool> retPair = 
+		mPageSources.insert(
+		BiPageSourceMap::value_type(typeName, source));
+	if (!retPair.second)
+	{
+		OGRE_EXCEPT(Exception::ERR_DUPLICATE_ITEM, 
+			"The page source " + typeName + " is already registered",
+			"TerrainSceneManager::registerPageSource");
+	}
+	LogManager::getSingleton().logMessage(
+		"TerrainSceneManager: Registered a new PageSource for "
+		"type " + typeName);
+}
+//-------------------------------------------------------------------------
+void BiTerrainSceneManager::selectPageSource(const String& typeName, 
+										  BiTerrainPageSourceOptionList& optionList)
+{
+	BiPageSourceMap::iterator i = mPageSources.find(typeName);
+	if (i == mPageSources.end())
+	{
+		OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, 
+			"Cannot locate a TerrainPageSource for type " + typeName,
+			"TerrainSceneManager::selectPageSource");
+	}
+
+	if (mActivePageSource)
+	{
+		mActivePageSource->shutdown();
+	}
+	mActivePageSource = i->second;
+	mActivePageSource->initialise(this, mOptions.tileSize, mOptions.pageSize,
+		mPagingEnabled, optionList);
+
+	LogManager::getSingleton().logMessage(
+		"TerrainSceneManager: Activated PageSource " + typeName);
+
+}
+//-------------------------------------------------------------------------
+int BiTerrainSceneManager::getDetailTextureRepeat(void)
+{
+	return (int)mOptions.detailTile;
+}
+//-------------------------------------------------------------------------
+int BiTerrainSceneManager::getTileSize(void)
+{
+	return (int)mOptions.tileSize;
+}
+//-------------------------------------------------------------------------
+int BiTerrainSceneManager::getPageSize(void)
+{
+	return (int)mOptions.pageSize;
+}
+//-------------------------------------------------------------------------
+int BiTerrainSceneManager::getMaxPixelError(void)
+{
+	return (int)mOptions.maxPixelError;
+}
+//-------------------------------------------------------------------------
+const Vector3& BiTerrainSceneManager::getScale(void)
+{
+	return mOptions.scale;
+}
+//-------------------------------------------------------------------------
+int BiTerrainSceneManager::getMaxGeoMipMapLevel(void)
+{
+	return (int)mOptions.maxGeoMipMapLevel;
+}
+//-----------------------------------------------------------------------
+void BiTerrainSceneManager::initLevelIndexes()
+{
+	if ( mLevelIndex.size() == 0 )
+	{
+		for ( int i = 0; i < 16; i++ )
+		{
+
+			mLevelIndex.push_back( new IndexMap() );
+
+		}
+
+	}
+}
+//-----------------------------------------------------------------------
+void BiTerrainSceneManager::destroyLevelIndexes()
+{
+	for ( size_t i = 0; i < mLevelIndex.size(); i++ )
+	{
+		delete mLevelIndex[i];
+	}
+	mLevelIndex.clear();
+}
+//-------------------------------------------------------------------------
+//-------------------------------------------------------------------------
+RaySceneQuery* 
+BiTerrainSceneManager::createRayQuery(const Ray& ray, unsigned long mask)
+{
+	BiTerrainRaySceneQuery *trsq = new BiTerrainRaySceneQuery(this);
+	trsq->setRay(ray);
+	trsq->setQueryMask(mask);
+	return trsq;
+}
+//-------------------------------------------------------------------------
+BiTerrainRaySceneQuery::BiTerrainRaySceneQuery(SceneManager* creator)
+:DefaultRaySceneQuery(creator)
+{
+	mSupportedWorldFragments.insert(SceneQuery::WFT_SINGLE_INTERSECTION);
+}
+//-------------------------------------------------------------------------
+BiTerrainRaySceneQuery::~BiTerrainRaySceneQuery()
+{
+}
+//-------------------------------------------------------------------------
+void BiTerrainRaySceneQuery::execute(RaySceneQueryListener* listener)
+{
+	mWorldFrag.fragmentType = SceneQuery::WFT_SINGLE_INTERSECTION;
+
+	const Vector3& dir = mRay.getDirection();
+	const Vector3& origin = mRay.getOrigin();
+	// Straight up / down?
+	if (dir == Vector3::UNIT_Y || dir == Vector3::NEGATIVE_UNIT_Y)
+	{
+		Real height = static_cast<BiTerrainSceneManager*>(mParentSceneMgr)->getHeightAt(
+			origin.x, origin.z);
+		if (height != -1 && (height <= origin.y && dir.y < 0) || (height >= origin.y && dir.y > 0))
+		{
+			mWorldFrag.singleIntersection.x = origin.x;
+			mWorldFrag.singleIntersection.z = origin.z;
+			mWorldFrag.singleIntersection.y = height;
+			if (!listener->queryResult(&mWorldFrag, 
+				(mWorldFrag.singleIntersection - origin).length()))
+				return;
+		}
+	}
+	else
+	{
+		// Perform arbitrary query
+		if (static_cast<BiTerrainSceneManager*>(mParentSceneMgr)->intersectSegment(
+			origin, origin + (dir * 100000), &mWorldFrag.singleIntersection))
+		{
+			if (!listener->queryResult(&mWorldFrag, 
+				(mWorldFrag.singleIntersection - origin).length()))
+				return;
+		}
+	}
+	DefaultRaySceneQuery::execute(listener);
+}
+//-------------------------------------------------------------------------
+MaterialPtr& BiTerrainSceneManager::getTerrainMaterial(void)
+{
+	return mOptions.terrainMaterial;
+}
+//-------------------------------------------------------------------------
+BiTerrainSceneManager::BiPageSourceIterator BiTerrainSceneManager::getPageSourceIterator(void)
+{
+	return BiPageSourceIterator(mPageSources.begin(), mPageSources.end());
+}
+//-------------------------------------------------------------------------
+void BiTerrainSceneManager::setWorldGeometryRenderQueue(uint8 qid)
+{
+	for (BiTerrainPage2D::iterator pi = mTerrainPages.begin(); 
+		pi != mTerrainPages.end(); ++pi)
+	{
+		BiTerrainPageRow& row = *pi;
+		for (BiTerrainPageRow::iterator ri = row.begin(); ri != row.end(); ++ri)
+		{
+			BiTerrainPage* page = *ri;
+			if (page)
+			{
+				page->setRenderQueue(qid);
+			}
+		}
+	}
+
+}
+/************************************************************************/
+/* Factory for BiTreeTerrainSceneManager                                */
+/************************************************************************/
+//-----------------------------------------------------------------------
+const String BiTerrainSceneManagerFactory::FACTORY_TYPE_NAME = "BiTreeTerrainSceneManager";
+//-----------------------------------------------------------------------
+void BiTerrainSceneManagerFactory::initMetaData(void) const
+{
+	mMetaData.typeName = FACTORY_TYPE_NAME;
+	mMetaData.description = "Scene manager that organises the scene based on a Bi-tree and renders terrain";
+	mMetaData.sceneTypeMask = 0xFFFF; // support all types of scenes (hopefully)
+	mMetaData.worldGeometrySupported = true;
+}
+//-----------------------------------------------------------------------
+BiTerrainSceneManagerFactory::
+BiTerrainSceneManagerFactory(GtpVisibility::VisibilityManager * vm)
+{
+	visManager = vm;
+}
+//-----------------------------------------------------------------------
+BiTerrainSceneManagerFactory::~BiTerrainSceneManagerFactory()
+{
+	for (BiTerrainPageSources::iterator i = mTerrainPageSources.begin();
+		i != mTerrainPageSources.end(); ++i)
+	{
+		delete *i;
+	}
+	mTerrainPageSources.clear();
+}
+//-----------------------------------------------------------------------
+SceneManager* BiTerrainSceneManagerFactory::createInstance(
+	const String& instanceName)
+{
+	BiTerrainSceneManager * sm = 
+		new BiTerrainSceneManager(instanceName, visManager);
+
+	BiHeightmapTerrainPageSource* ps = new BiHeightmapTerrainPageSource();
+	mTerrainPageSources.push_back(ps);
+	sm->registerPageSource("Heightmap", ps);
+
+	return sm;
+}
+//-----------------------------------------------------------------------
+void BiTerrainSceneManagerFactory::destroyInstance(SceneManager* instance)
+{
+	delete instance;
+}
+} // namespace Ogre
Index: GTP/trunk/Lib/Vis/OnlineCullingCHC/OGRE/src/OgreBiHierarchy.cpp
===================================================================
--- GTP/trunk/Lib/Vis/OnlineCullingCHC/OGRE/src/OgreBiHierarchy.cpp	(revision 2348)
+++ GTP/trunk/Lib/Vis/OnlineCullingCHC/OGRE/src/OgreBiHierarchy.cpp	(revision 2348)
@@ -0,0 +1,2083 @@
+/*
+-----------------------------------------------------------------------------
+This source file is part of the GameTools Project
+http://www.gametools.org
+
+Author: Martin Szydlowski
+-----------------------------------------------------------------------------
+*/
+
+#include "OgreBiHierarchy.h"
+#include "OgreBihRenderable.h"
+#include "OgreBiHierarchySceneNode.h"
+#include "OgreBiHierarchySceneManager.h"
+
+#include <OgreStringConverter.h>
+#include <OgreLogManager.h>
+#include <OgreRoot.h>
+#include <OgreTimer.h>
+
+#include <OgreMaterialManager.h>
+
+#define PLT_SIZE 101
+
+namespace Ogre
+{
+
+enum BihIntersection
+{
+	OUTSIDE=0,
+	INSIDE=1,
+	INTERSECT=2
+};
+
+static BihIntersection intersect( const Ray &one, const AxisAlignedBox &two )
+{
+	// Null box?
+	if (two.isNull()) return OUTSIDE;
+
+	bool inside = true;
+	const Vector3* pCorners = two.getAllCorners();
+	Vector3 origin = one.getOrigin();
+	Vector3 dir = one.getDirection();
+
+	Vector3 maxT(-1, -1, -1);
+
+	int i = 0;
+	for(i=0; i<3; i++ )
+	{
+		if( origin[i] < pCorners[0][i] )
+		{
+			inside = false;
+			if( dir[i] > 0 )
+			{
+				maxT[i] = (pCorners[0][i] - origin[i])/ dir[i];
+			}
+		}
+		else if( origin[i] > pCorners[4][i] )
+		{
+			inside = false;
+			if( dir[i] < 0 )
+			{
+				maxT[i] = (pCorners[4][i] - origin[i]) / dir[i];
+			}
+		}
+	}
+
+	if( inside )
+	{
+		return INTERSECT;
+	}
+	int whichPlane = 0;
+	if( maxT[1] > maxT[whichPlane])
+		whichPlane = 1;
+	if( maxT[2] > maxT[whichPlane])
+		whichPlane = 2;
+
+	if( ((int)maxT[whichPlane]) & 0x80000000 )
+	{
+		return OUTSIDE;
+	}
+	for(i=0; i<3; i++ )
+	{
+		if( i!= whichPlane )
+		{
+			float f = origin[i] + maxT[whichPlane] * dir[i];
+			if ( f < (pCorners[0][i] - 0.00001f) ||
+				f > (pCorners[4][i] +0.00001f ) )
+			{
+				return OUTSIDE;
+			}
+		}
+	}
+
+	return INTERSECT;
+
+}
+
+
+/** Checks how the second box intersects with the first.
+*/
+static BihIntersection intersect( const PlaneBoundedVolume &one, const AxisAlignedBox &two )
+{
+	// Null box?
+	if (two.isNull()) return OUTSIDE;
+
+	// Get corners of the box
+	const Vector3* pCorners = two.getAllCorners();
+
+	// For each plane, see if all points are on the negative side
+	// If so, object is not visible.
+	// If one or more are, it's partial.
+	// If all aren't, full
+	int corners[ 8 ] = {0, 4, 3, 5, 2, 6, 1, 7};
+	bool all_inside = true;
+	PlaneList::const_iterator i, iend;
+	iend = one.planes.end();
+	for (i = one.planes.begin(); i != iend; ++i)
+	{
+		const Plane& plane = *i;
+		bool all_outside = true;
+
+		float distance = 0;
+
+		for ( int corner = 0; corner < 8; ++corner )
+		{
+			distance = plane.getDistance( pCorners[ corners[ corner ] ] );
+			all_outside = all_outside && ( distance < 0 );
+			all_inside = all_inside && ( distance >= 0 );
+
+			if ( !all_outside && !all_inside )
+				break;
+		}
+
+		if ( all_outside )
+			return OUTSIDE;
+	}
+
+	if ( all_inside )
+		return INSIDE;
+	else
+		return INTERSECT;
+
+}
+
+
+/** Checks how the second box intersects with the first.
+*/
+static BihIntersection intersect( const AxisAlignedBox &one, const AxisAlignedBox &two )
+{
+	// Null box?
+	if (one.isNull() || two.isNull()) return OUTSIDE;
+
+	const Vector3 * outside = one.getAllCorners();
+	const Vector3 *inside = two.getAllCorners();
+
+	if ( inside[ 4 ].x < outside[ 0 ].x ||
+		inside[ 4 ].y < outside[ 0 ].y ||
+		inside[ 4 ].z < outside[ 0 ].z ||
+		inside[ 0 ].x > outside[ 4 ].x ||
+		inside[ 0 ].y > outside[ 4 ].y ||
+		inside[ 0 ].z > outside[ 4 ].z )
+	{
+		return OUTSIDE;
+	}
+
+	bool full = ( inside[ 0 ].x > outside[ 0 ].x &&
+		inside[ 0 ].y > outside[ 0 ].y &&
+		inside[ 0 ].z > outside[ 0 ].z &&
+		inside[ 4 ].x < outside[ 4 ].x &&
+		inside[ 4 ].y < outside[ 4 ].y &&
+		inside[ 4 ].z < outside[ 4 ].z );
+
+	if ( full )
+		return INSIDE;
+	else
+		return INTERSECT;
+
+}
+
+/** Checks how the box intersects with the sphere.
+*/
+static BihIntersection intersect( const Sphere &one, const AxisAlignedBox &two )
+{
+	// Null box?
+	if (two.isNull()) return OUTSIDE;
+
+	float sradius = one.getRadius();
+
+	sradius *= sradius;
+
+	Vector3 scenter = one.getCenter();
+
+	const Vector3 *corners = two.getAllCorners();
+
+	float s, d = 0;
+
+	Vector3 mndistance = ( corners[ 0 ] - scenter );
+	Vector3 mxdistance = ( corners[ 4 ] - scenter );
+
+	if ( mndistance.squaredLength() < sradius &&
+		mxdistance.squaredLength() < sradius )
+	{
+		return INSIDE;
+	}
+
+	//find the square of the distance
+	//from the sphere to the box
+	for ( int i = 0 ; i < 3 ; i++ )
+	{
+		if ( scenter[ i ] < corners[ 0 ][ i ] )
+		{
+			s = scenter[ i ] - corners[ 0 ][ i ];
+			d += s * s;
+		}
+
+		else if ( scenter[ i ] > corners[ 4 ][ i ] )
+		{
+			s = scenter[ i ] - corners[ 4 ][ i ];
+			d += s * s;
+		}
+
+	}
+
+	bool partial = ( d <= sradius );
+
+	if ( !partial )
+	{
+		return OUTSIDE;
+	}
+
+	else
+	{
+		return INTERSECT;
+	}
+}
+
+Real BihPlaneEvent::KT = 2.0;
+Real BihPlaneEvent::KI = 2.0;
+
+//----------------------------------------------------------------------------
+// determine if this event is left or right of the reference event
+// if the absolute flag is set, we decide by the objectcenter wether the 
+// event goes to left or right. 
+void BihPlaneEvent::classify(const BihPlaneEvent& e, BihPlaneEvent::Side side, bool absolute)
+{
+	// only classify events of the same dimension
+	if (mDimension == e.mDimension)
+	{
+		if (!absolute)
+		{
+			if (mType == PET_END && mPosition[mDimension] <= e.mPosition[e.mDimension])
+			{
+				mRenderable->setSide(PES_LEFT);
+				return;
+			}
+			else if (mType == PET_START && mPosition[mDimension] >= e.mPosition[e.mDimension])
+			{
+				mRenderable->setSide(PES_RIGHT);
+				return;
+			}
+			else if (mType == PET_ON)
+			{
+				if (mPosition[mDimension] < e.mPosition[e.mDimension] ||
+					(mPosition[mDimension] == e.mPosition[e.mDimension] && side == PES_LEFT))
+				{
+					mRenderable->setSide(PES_LEFT);
+					return;
+				}
+				if (mPosition[mDimension] > e.mPosition[e.mDimension] ||
+					(mPosition[mDimension] == e.mPosition[e.mDimension] && side == PES_RIGHT))
+				{
+					mRenderable->setSide(PES_RIGHT);
+					return;
+				}
+			}
+		} else
+		{
+			// The object lies on the splitting plane
+			// we decide with the object center if it goes right or left. 
+			Vector3 mMid=mRenderable->getBoundingBox().getCenter();
+			float mMidDimension=0;
+			switch (mDimension)
+			{
+				case BihPlaneEvent::PED_X:
+					mMidDimension=mMid.x;
+					break;
+				case BihPlaneEvent::PED_Y:
+					mMidDimension=mMid.y;
+					break;
+				case BihPlaneEvent::PED_Z:
+					mMidDimension=mMid.z;
+					break;
+			}
+			if (mMidDimension <= e.mPosition[e.mDimension])
+			{
+				mRenderable->setSide(PES_LEFT);
+				return;
+			}
+			else if (mMidDimension > e.mPosition[e.mDimension])
+			{
+				mRenderable->setSide(PES_RIGHT);
+				return;
+			}
+		}
+
+
+
+
+	}
+}
+
+//----------------------------------------------------------------------------
+// clip this event to an aabb (move it so that the plane on one of the box planes)
+BihPlaneEvent BihPlaneEvent::clip(AxisAlignedBox& box, BihPlaneEvent::Dimension dim)
+{
+	Vector3 newpos = mPosition, min = box.getMinimum(), max = box.getMaximum();
+	if (newpos[dim] < min[dim])
+		newpos[dim] = min[dim];
+	else if (newpos[dim] > max[dim])
+		newpos[dim] = max[dim];
+
+	return BihPlaneEvent(mRenderable, newpos, mDimension, mType);
+}
+
+//----------------------------------------------------------------------------
+// the surface area heuristic to determine the cost of splitting the parent aabb
+// along the plane represented by this event
+void BihPlaneEvent::SAH(const AxisAlignedBox& parent, int nLeft, int nPlane, int nRight, BihSplitInfo& split)
+{
+	Real mu = splitBox(parent, split.bleft, split.bright);
+	Real sav = surfaceArea(parent);
+	Real pl = surfaceArea(split.bleft) / sav;
+	Real pr = surfaceArea(split.bright) / sav;
+	Real costl = splitCost(pl, pr, nLeft + nPlane, nRight, mu);
+	Real costr = splitCost(pl, pr, nLeft, nPlane + nRight, mu);
+
+	if (costl < costr)
+	{
+		split.cost = costl;
+		split.side = PES_LEFT;
+		split.nleft = nLeft + nPlane;
+		split.nright = nRight;
+	}
+	else
+	{
+		split.cost = costr;
+		split.side = PES_RIGHT;
+		split.nleft = nLeft;
+		split.nright = nPlane + nRight;
+
+	}
+	split.event = *this;
+}
+
+//----------------------------------------------------------------------------
+// the surface area heuristic to determine the cost of splitting the parent aabb
+// along the plane represented by this event modified for BV
+
+void BihPlaneEvent::SAH(const AxisAlignedBox& parent, int nLeft, int nPlane, int nRight, BihSplitInfo& split,AxisAlignedBox left,AxisAlignedBox right)
+{
+	//Real mu = splitBox(parent, split.bleft, split.bright);
+	split.bleft=left;
+	split.bright=right;
+	Vector3 bmin = parent.getMinimum();
+	Vector3 bmax = parent.getMaximum();
+	Real mu=lookupPenalty(
+		(mPosition[mDimension] - bmin[mDimension]) / 
+		(bmax[mDimension] - bmin[mDimension]));
+	Real sav = surfaceArea(parent);
+	Real pl = surfaceArea(split.bleft) / sav;
+	Real pr = surfaceArea(split.bright) / sav;
+	Real costl = splitCost(pl, pr, nLeft + nPlane, nRight, mu);
+	Real costr = splitCost(pl, pr, nLeft, nPlane + nRight, mu);
+
+	if (costl < costr)
+	{
+		split.cost = costl;
+		split.side = PES_LEFT;
+		split.nleft = nLeft + nPlane;
+		split.nright = nRight;
+	}
+	else
+	{
+		split.cost = costr;
+		split.side = PES_RIGHT;
+		split.nleft = nLeft;
+		split.nright = nPlane + nRight;
+
+	}
+	split.event = *this;
+}
+
+//----------------------------------------------------------------------------
+// split the parent aabb with the plane in this event
+Real BihPlaneEvent::splitBox(const AxisAlignedBox& parent, AxisAlignedBox& left, AxisAlignedBox& right)
+{
+	Vector3 bmin = parent.getMinimum();
+	Vector3 bmax = parent.getMaximum();
+	// calculate the penalty for spliting the box that way
+	Real mu = lookupPenalty(
+		(mPosition[mDimension] - bmin[mDimension]) / 
+		(bmax[mDimension] - bmin[mDimension]));
+	// set corners which are the same as parent AABB
+	left.setMinimum(bmin);
+	right.setMaximum(bmax);
+	// modify "inner" corners
+	bmin[mDimension] = mPosition[mDimension];
+	bmax[mDimension] = mPosition[mDimension];
+	// set the corners on the split plane
+	left.setMaximum(bmax);
+	right.setMinimum(bmin);
+
+	return mu;
+}
+
+//----------------------------------------------------------------------------
+// compute surface area of a box ... DUH!
+Real BihPlaneEvent::surfaceArea(const AxisAlignedBox& box)
+{
+	Vector3 sides = box.getMaximum() - box.getMinimum();
+	return	2 * sides.x * sides.y +
+		2 * sides.y * sides.z +
+		2 * sides.z * sides.x;
+}
+
+//----------------------------------------------------------------------------
+// lookup the penalty for placing the splitting plane near to the edge of the AABB
+// 0.0 <= p <= 1.0, p = 0.5 means the plane divides the aabb in half
+Real BihPlaneEvent::lookupPenalty(Real p)
+{
+	// precomputed table of {x^6 + 1|0 <= x <= 1}
+	static Real mPenaltyLookupTable[PLT_SIZE];
+	static bool init_done = false;
+
+	if (!init_done)
+	{
+		Real step = 1.0  / (PLT_SIZE - 1);
+		Real x = 0.0; 
+		//LogManager::getSingleton().logMessage("### Calculating Lookup Table ###");
+		for (int i = 0; i < PLT_SIZE; i++)
+		{
+			mPenaltyLookupTable[i] = Math::Pow(x, 6) + 1.0;
+			x += step;
+			//LogManager::getSingleton().logMessage("### mPenaltyLookupTable[" + StringConverter::toString(i,3) + "]=" + StringConverter::toString(mPenaltyLookupTable[i]));
+		}
+		init_done = true;
+		//LogManager::getSingleton().logMessage("### Lookup Table Calculated ###");
+	}
+
+	// normalize p to [0,1]
+	Real x = Math::Abs(p * 2 - 1);
+	// compute index
+	int i = Math::IFloor(x * (PLT_SIZE - 1));
+
+	return mPenaltyLookupTable[i];
+}
+
+//----------------------------------------------------------------------------
+// compute cost of the split, reward splitting of empty space (lambda, const),
+// penalize splitting off 'thin' slices (mu, const)
+Real BihPlaneEvent::splitCost(Real pLeft, Real pRight, int nLeft, int nRight)
+{
+	// reward splitting off chunks of empty space
+	Real lambda = 1.0;
+	if (nLeft == 0 || nRight == 0)
+	{
+		lambda = 0.8;
+	}
+
+	// penalize splitting off small chunks (thin slices)
+	Real mu = 1.0;
+	if (pLeft < 0.1 || pRight < 0.1 || pLeft > 0.9 || pRight > 0.9)
+	{
+		mu = 1.5;
+	}
+	return lambda * mu * (KT + (KI * (pLeft*nLeft + pRight*nRight)));
+}
+
+//----------------------------------------------------------------------------
+// compute cost of the split, reward splitting of empty space (lambda, const),
+// penalize splitting off 'thin' slices (mu, parameter)
+Real BihPlaneEvent::splitCost(Real pLeft, Real pRight, int nLeft, int nRight, Real mu)
+{
+	// reward splitting off chunks of empty space
+	Real lambda = 1.0;
+	if (nLeft == 0 || nRight == 0)
+	{
+		lambda = 0.8;
+	}
+
+	return lambda * mu * (KT + (KI * (pLeft*nLeft + pRight*nRight)));
+}
+
+//----------------------------------------------------------------------------
+// surface area heuristic modified for the priority queue method of building
+// the probabilities (p, pl, pr) are relative to the global (all enclosing) aabb
+void BihPlaneEvent::pqSAH(Real globalSA, 
+						  Real parentSA, 
+						  int nLeft, 
+						  int nPlane, 
+						  int nRight, 
+						  AxisAlignedBox& parentBox, 
+						  BihSplitInfo& split)
+{
+	Real mu = splitBox(parentBox, split.bleft, split.bright);
+	Real p = parentSA / globalSA;
+	Real pl = surfaceArea(split.bleft) / globalSA;
+	Real pr = surfaceArea(split.bright) / globalSA;
+	Real costl = pqSplitCost(p, pl, pr, nLeft + nPlane, nRight, mu);
+	Real costr = pqSplitCost(p, pl, pr, nLeft, nPlane + nRight, mu);
+
+	if (costl < costr)
+	{
+		split.cost = costl;
+		split.side = PES_LEFT;
+		split.nleft = nLeft + nPlane;
+		split.nright = nRight;
+	}
+	else
+	{
+		split.cost = costr;
+		split.side = PES_RIGHT;
+		split.nleft = nLeft;
+		split.nright = nPlane + nRight;
+
+	}
+	split.event = *this;
+}
+
+//----------------------------------------------------------------------------
+// compute split cost without any penalties
+Real BihPlaneEvent::pqSplitCost(Real pParent, Real pLeft, Real pRight, int nLeft, int nRight, Real mu)
+{
+	return pParent * KT + (KI * (pLeft * nLeft + pRight * nRight));
+}
+
+//----------------------------------------------------------------------------
+// DEBUG
+String BihPlaneEvent::print()
+{
+	String dim, type;
+	if (mDimension == PED_X)
+		dim = "X";
+	if (mDimension == PED_Y)
+		dim = "Y";
+	if (mDimension == PED_Z)
+		dim = "Z";
+	if (mType == PET_END)
+		type = "END  ";
+	if (mType == PET_ON)
+		type = "ON   ";
+	if (mType == PET_START)
+		type = "START";
+	//return StringConverter::toString(mPosition[mDimension]) + " " + dim + " " + type;
+	return type + " " + dim + " " + StringConverter::toString(mPosition[mDimension]);
+};
+
+//----------------------------------------------------------------------------
+String BihSplitInfo::print()
+{
+	String sside;
+	if (side == BihPlaneEvent::PES_BOTH)
+		sside = "BOTH ";
+	if (side == BihPlaneEvent::PES_LEFT)
+		sside = "LEFT ";
+	if (side == BihPlaneEvent::PES_RIGHT)
+		sside = "RIGHT";
+	return "nLeft: " + StringConverter::toString(nleft, 4) + " nRight: " + 
+		StringConverter::toString(nright, 4) + " Cost: " + StringConverter::toString(cost, 8, 9) +
+		" Side: " + sside + " Event: " + event.print() + " Leftbox: " + 
+		StringConverter::toString(bleft.getMinimum()) + ", " + 
+		StringConverter::toString(bleft.getMaximum()) + " Rightbox: " +
+		StringConverter::toString(bright.getMinimum()) + ", " + 
+		StringConverter::toString(bright.getMaximum());
+};
+
+//----------------------------------------------------------------------------
+String BiHierarchy::BihSubdivisionCandidate::print()
+{		
+	String sside;
+	if (side == BihPlaneEvent::PES_BOTH)
+		sside = "BOTH ";
+	if (side == BihPlaneEvent::PES_LEFT)
+		sside = "LEFT ";
+	if (side == BihPlaneEvent::PES_RIGHT)
+		sside = "RIGHT";
+	return "List: " + StringConverter::toString(events->size(), 4) + " Objects: " +
+		StringConverter::toString(nObjects, 4) + " Cost: " + StringConverter::toString(cost, 8, 9) + 
+		" Decrease: " + StringConverter::toString(decrease, 8, 9) + " Side: " + sside + " Best: " +
+		best->print() +  " Box: " + StringConverter::toString(aabb.getMinimum()) + ", "
+		+ StringConverter::toString(aabb.getMaximum());
+
+};
+
+//----------------------------------------------------------------------------
+//----------------------------------------------------------------------------
+//----------------------------------------------------------------------------
+
+BiHierarchy::BiHierarchy(int maxdepth, BuildMethod bm): 
+mMaxDepth(maxdepth), 
+mBuildMethod(bm), 
+mHiLiteLevel(0),
+mShowAllBoxes(false),
+mShowNodes(true),
+mBihRoot(0), 
+mBuildLog(0),
+mAlgorithm(BIHAL_SAH)
+{
+	init();
+}
+
+BiHierarchy::BiHierarchy(int maxdepth, BuildMethod bm, int hilite, bool allboxes, bool shownodes):
+mMaxDepth(maxdepth), 
+mBuildMethod(bm), 
+mHiLiteLevel(hilite),
+mShowAllBoxes(allboxes),
+mShowNodes(shownodes),
+mBihRoot(0), 
+mBuildLog(0),
+mAlgorithm(BIHAL_SAH)
+{
+	init();
+}
+
+BiHierarchy::~BiHierarchy()
+{
+	delete mBihRoot;
+}
+
+void BiHierarchy::init()
+{
+	MaterialPtr mat;
+	TextureUnitState *tex;
+
+	// init visualization materials (unlit solid green/yellow)
+	mat = MaterialManager::getSingleton().getByName("BiHierarchy/BoxHiLite");
+	if (mat.isNull())
+	{
+		ColourValue green(0, 1, 0);
+		mat = MaterialManager::getSingleton().create("BiHierarchy/BoxHiLite", "General");
+		//mat->setAmbient(green);
+		//mat->setDiffuse(green);
+		mat->setLightingEnabled(false);
+		tex = mat->getTechnique(0)->getPass(0)->createTextureUnitState();
+		tex->setColourOperationEx(LBX_SOURCE2, LBS_CURRENT, LBS_MANUAL, green, green);
+	}
+
+	mat = MaterialManager::getSingleton().getByName("BiHierarchy/BoxLeft");
+	if (mat.isNull())
+	{
+		ColourValue red(1, 0, 0);
+		mat = MaterialManager::getSingleton().create("BiHierarchy/BoxLeft", "General");
+		//mat->setAmbient(green);
+		//mat->setDiffuse(green);
+		mat->setLightingEnabled(false);
+		tex = mat->getTechnique(0)->getPass(0)->createTextureUnitState();
+		
+		tex->setColourOperationEx(LBX_SOURCE2, LBS_CURRENT, LBS_MANUAL, red, red);
+	}
+
+	mat = MaterialManager::getSingleton().getByName("BiHierarchy/BoxRight");
+	if (mat.isNull())
+	{
+		ColourValue green(0, 1, 0);
+		mat = MaterialManager::getSingleton().create("BiHierarchy/BoxRight", "General");
+		//mat->setAmbient(green);
+		//mat->setDiffuse(green);
+		mat->setLightingEnabled(false);
+		tex = mat->getTechnique(0)->getPass(0)->createTextureUnitState();
+		tex->setColourOperationEx(LBX_SOURCE2, LBS_CURRENT, LBS_MANUAL, green, green);
+	}
+	mat = MaterialManager::getSingleton().getByName("BiHierarchy/BoxLeftDark");
+	if (mat.isNull())
+	{
+		ColourValue red(0.5, 0, 0);
+		mat = MaterialManager::getSingleton().create("BiHierarchy/BoxLeftDark", "General");
+		//mat->setAmbient(green);
+		//mat->setDiffuse(green);
+		mat->setLightingEnabled(false);
+		tex = mat->getTechnique(0)->getPass(0)->createTextureUnitState();
+		
+		tex->setColourOperationEx(LBX_SOURCE2, LBS_CURRENT, LBS_MANUAL, red, red);
+	}
+
+	mat = MaterialManager::getSingleton().getByName("BiHierarchy/BoxRightDark");
+	if (mat.isNull())
+	{
+		ColourValue green(0, 0.5, 0);
+		mat = MaterialManager::getSingleton().create("BiHierarchy/BoxRightDark", "General");
+		//mat->setAmbient(green);
+		//mat->setDiffuse(green);
+		mat->setLightingEnabled(false);
+		tex = mat->getTechnique(0)->getPass(0)->createTextureUnitState();
+		tex->setColourOperationEx(LBX_SOURCE2, LBS_CURRENT, LBS_MANUAL, green, green);
+	}
+
+	mat = MaterialManager::getSingleton().getByName("BiHierarchy/BoxViz");
+	if (mat.isNull())
+	{
+		ColourValue yellow(1, 1, 0);
+		mat = MaterialManager::getSingleton().create("BiHierarchy/BoxViz", "General");
+		//mat->setAmbient(yellow);
+		//mat->setDiffuse(yellow);
+		mat->setLightingEnabled(false);
+		tex = mat->getTechnique(0)->getPass(0)->createTextureUnitState();
+		tex->setColourOperationEx(LBX_SOURCE2, LBS_CURRENT, LBS_MANUAL, yellow, yellow);
+	}
+	mat = MaterialManager::getSingleton().getByName("BaseWhiteNoLighting");
+	if (mat.isNull())
+	{
+		ColourValue white(1, 1, 0);
+		mat = MaterialManager::getSingleton().create("BaseWhiteNoLighting", "General");
+		//mat->setAmbient(yellow);
+		//mat->setDiffuse(yellow);
+		mat->setLightingEnabled(false);
+		tex = mat->getTechnique(0)->getPass(0)->createTextureUnitState();
+		tex->setColourOperationEx(LBX_SOURCE2, LBS_CURRENT, LBS_MANUAL, white, white);
+	}
+
+	// retrieve or create build log
+	try
+	{
+		mBuildLog = LogManager::getSingleton().getLog(BiHierarchy_LOGNAME);
+	}
+	catch (Exception&)
+	{		
+		mBuildLog = LogManager::getSingleton().createLog(BiHierarchy_LOGNAME);
+	}
+
+	setEnhancedVis(false);
+}
+
+/************************************************************************/
+/* BiHierarchy insert/delete functions                                       */
+/************************************************************************/
+
+void BiHierarchy::remove(BihRenderable * rend)
+{
+	// DEBUG
+	//LogManager::getSingleton().logMessage("-- Removing SceneNode: " + (static_cast<BiHierarchySceneNode *>(rend))->getName());
+	std::queue<LeafPtr> cleanup;
+	LeafPtr leaf;
+	LeafSet ls = rend->getLeaves();
+	LeafSet::iterator it = ls.begin();
+	LeafSet::iterator end = ls.end();
+	while (it != end)
+	{
+		cleanup.push(*it);
+		it++;
+	}
+	while (!cleanup.empty())
+	{
+		leaf = cleanup.front();
+		cleanup.pop();
+		leaf->remove(rend);
+		bool gone = rend->detachFrom(leaf);
+		assert(gone && "Failed to detach leave which is abso-fuckin-lutely impossible!!!");
+		//dump();
+		recDelete(leaf);
+		//dump();
+	}
+}
+
+void BiHierarchy::recDelete(BiHierarchy::Node * node)
+{
+	if (node == 0) // DEBUG
+	{
+		OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR,
+			"SNAFU while inserting BihRenderable into BiHierarchy.",
+			"BiHierarchy::recInsert" );
+		return;
+	}
+
+	if (node->isEmpty())
+	{
+		if (node == mBihRoot)
+		{
+			OGRE_DELETE(mBihRoot);
+		} 
+		else
+		{
+			BiHierarchy::Branch * parent = BIHBRANCHPTR_CAST(node->getParent());
+			if (node == parent->mLeft)
+			{
+				OGRE_DELETE(parent->mLeft);
+			}
+			else if (node == parent->mRight)
+			{
+				OGRE_DELETE(parent->mRight);
+			}
+			recDelete(parent);
+		}
+	}
+}
+
+void BiHierarchy::insert(BihRenderable * rend)
+{
+	// make sure the tree exists
+	if (mBihRoot)
+	{
+		// make sure the renderable is inside the world bounding box
+		AxisAlignedBox aabb = rend->getBoundingBox();
+		AxisAlignedBox isect = mBihRoot->mAABB.intersection(aabb);
+		if (isect.getMinimum() == aabb.getMinimum() && isect.getMaximum() == aabb.getMaximum())
+		{
+			recInsert(mBihRoot, rend);
+		}
+		else
+		{
+			//LogManager::getSingleton().logMessage("Inserted node outside of world AABB.");
+			BihRenderableList nodelist;
+			nodelist.push_back(rend);
+			addRendToList(mBihRoot, nodelist);
+			OGRE_DELETE(mBihRoot);
+			mBihRoot = buildFromList(nodelist, 0, AxisAlignedBox());
+		}
+	}
+	// otherwise build a new one
+	else
+	{
+		build(rend);
+	}
+}
+
+void BiHierarchy::recInsert(BiHierarchy::Node * node, BihRenderable * rend)
+{
+	if (node == 0) // DEBUG
+	{
+		OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR,
+			"SNAFU while inserting BihRenderable into BiHierarchy.",
+			"BiHierarchy::recInsert" );
+		return;
+	}
+
+	// rebuild the leaf/replace with subtree ...
+	if (node->isLeaf())
+	{
+		//LogManager::getSingleton().logMessage("## Replacing leaf.");
+		rebuildSubtree(node, rend);
+	}
+	else
+	{
+		BiHierarchy::Branch * branch = BIHBRANCHPTR_CAST(node);
+		AxisAlignedBox aabb = rend->getBoundingBox();
+		Plane::Side smin = branch->mSplitPlane->getSide(aabb.getMinimum());
+		Plane::Side smax = branch->mSplitPlane->getSide(aabb.getMaximum());
+		if (smin == smax)
+		{
+			if (smax == Plane::NEGATIVE_SIDE ||
+				(smax == Plane::NO_SIDE && branch->mPlaneSide == BihPlaneEvent::PES_LEFT))
+			{
+				if (branch->mLeft)
+					recInsert(branch->mLeft, rend);
+				else
+					recInsertNew(branch, BihPlaneEvent::PES_LEFT, rend);
+			}
+			else if (smin == Plane::POSITIVE_SIDE || 
+				(smin == Plane::NO_SIDE && branch->mPlaneSide == BihPlaneEvent::PES_RIGHT))
+			{
+				if (branch->mRight)
+					recInsert(branch->mRight, rend);
+				else
+					recInsertNew(branch, BihPlaneEvent::PES_RIGHT, rend);
+			}
+		}
+		else
+		{
+			if (smin == Plane::NEGATIVE_SIDE && smax == Plane::NO_SIDE)
+			{
+				if (branch->mLeft)
+					recInsert(branch->mLeft, rend);
+				else
+					recInsertNew(branch, BihPlaneEvent::PES_LEFT, rend);
+			}
+			else if (smax == Plane::POSITIVE_SIDE && smin == Plane::NO_SIDE)
+			{
+				if (branch->mRight)
+					recInsert(branch->mRight, rend);
+				else
+					recInsertNew(branch, BihPlaneEvent::PES_RIGHT, rend);
+			}
+			else
+			{
+				// to avoid SNAFUs, rebuild whole subtree
+				//LogManager::getSingleton().logMessage("## Rebuilding subtree for insertion");
+				rebuildSubtree(node, rend);
+			}
+		}
+	}
+}
+
+void BiHierarchy::recInsertNew(BiHierarchy::Branch * parent, BihPlaneEvent::Side side, BihRenderable * rend)
+{
+	//LogManager::getSingleton().logMessage("## Inserting into new subtree");
+
+	AxisAlignedBox left, rigth, aabb;
+	BihPlaneEventList events;
+	int nObjects;
+	
+	rend->computeScene(events, aabb, nObjects, false);
+
+	// override aabb
+	splitBox(*parent, left, rigth);
+	if (side == BihPlaneEvent::PES_LEFT)
+	{
+		aabb = left;
+		if (mBuildMethod == BIHBM_RECURSIVE)
+			parent->mLeft = recBuild(events, nObjects, aabb, parent);
+		else if (mBuildMethod == BIHBM_PRIORITYQUEUE)
+			parent->mLeft = pqBuild(events, nObjects, aabb, parent);
+		else
+		{
+			OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, 
+				"Invalid build method for the BiHierarchy.",
+				"BiHierarchy::buildFromList");
+			parent->mLeft = 0;
+		}
+		
+	}
+	else if (side == BihPlaneEvent::PES_RIGHT)
+	{
+		aabb = rigth;
+		if (mBuildMethod == BIHBM_RECURSIVE)
+			parent->mRight = recBuild(events, nObjects, aabb, parent);
+		else if (mBuildMethod == BIHBM_PRIORITYQUEUE)
+			parent->mRight = pqBuild(events, nObjects, aabb, parent);
+		else
+		{
+			OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, 
+				"Invalid build method for the BiHierarchy.",
+				"BiHierarchy::buildFromList");
+			parent->mRight = 0;
+		}
+	}
+}
+
+void BiHierarchy::rebuildSubtree(BiHierarchy::Node * node, BihRenderable * rend)
+{
+	//LogManager::getSingleton().logMessage("## Rebuilding subtree");
+
+	AxisAlignedBox aabb = node->mAABB;
+	
+	BihRenderableList nodelist;
+	nodelist.push_back(rend);
+	addRendToList(node, nodelist);
+
+	if (node == mBihRoot)
+	{
+		OGRE_DELETE(mBihRoot);
+		mBihRoot = buildFromList(nodelist, 0, aabb);
+	}
+	else
+	{
+		BiHierarchy::Branch * parent = BIHBRANCHPTR_CAST(node->getParent());
+
+		if (node == parent->mLeft)
+		{
+			OGRE_DELETE(parent->mLeft);
+			parent->mLeft = buildFromList(nodelist, parent, aabb);
+		}
+		else if (node == parent->mRight)
+		{
+			OGRE_DELETE(parent->mRight);
+			parent->mRight = buildFromList(nodelist, parent, aabb);
+		}
+		else
+		{
+			OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR,
+				"SNAFU while inserting BihRenderable into BiHierarchy.",
+				"BiHierarchy::recInsert" );
+		}
+	}
+}
+
+// compute both AABB then return the requested one.
+void BiHierarchy::splitBox(const BiHierarchy::Branch& parent, AxisAlignedBox& left, AxisAlignedBox& right)
+{
+	Vector3 bmin = parent.mAABB.getMinimum();
+	Vector3 bmax = parent.mAABB.getMaximum();
+	Real pos = - parent.mSplitPlane->d;
+	int dim = static_cast<int>(parent.mSplitPlane->normal.dotProduct(Vector3(0,1,2)));
+	// set corners which are the same as parent AABB
+	left.setMinimum(bmin);
+	right.setMaximum(bmax);
+	// modify "inner" corners
+	bmin[dim] = pos;
+	bmax[dim] = pos;
+	// set the corners on the split plane
+	left.setMaximum(bmax);
+	right.setMinimum(bmin);
+}
+
+void BiHierarchy::addRendToList(BiHierarchy::Node * node, BihRenderableList& nodelist)
+{
+	if (node->isLeaf())
+	{
+		BiHierarchy::Leaf * leaf = BIHLEAFPTR_CAST(node);
+		BihRenderableList::iterator it  = leaf->mBihRenderables.begin();
+		BihRenderableList::iterator end = leaf->mBihRenderables.end();
+		while (it != end)
+		{
+			nodelist.push_back(*it);
+			it++;
+		}
+	}
+	else
+	{
+		BiHierarchy::Branch * branch = BIHBRANCHPTR_CAST(node);
+		if (branch->mLeft)
+			addRendToList(branch->mLeft, nodelist);
+		if (branch->mRight)
+			addRendToList(branch->mRight, nodelist);
+	}
+}
+
+/************************************************************************/
+/*                BiHierarchy build functions                           */
+/************************************************************************/
+
+void BiHierarchy::build(BihRenderable * sceneRoot)
+{
+	Timer *timer = Root::getSingleton().getTimer();
+	unsigned long t1, t2, t3, t4;
+
+	mTreeStats.clear();
+	
+	// data we want to collect
+	BihPlaneEventList events;
+	AxisAlignedBox aabb;
+	int nObjects = 0;
+
+	t1 = timer->getMicroseconds(); // DEBUG
+	sceneRoot->computeScene(events, aabb, nObjects);
+	t2 = timer->getMicroseconds(); // DEBUG
+	events.sort();
+	t3 = timer->getMicroseconds(); // DEBUG
+
+	mTreeStats.mNumSceneNodes = nObjects;
+
+	assert(! aabb.isNull() && "Teh stubid worldAABB iz NULL ... waht now?");
+	// hack to avoid SNAFU with "2-dimensional" scene
+	aabb.merge(aabb.getMaximum()+Vector3(1,1,1));
+	aabb.merge(aabb.getMinimum()+Vector3(-1,-1,-1));
+
+	if (mBuildMethod == BIHBM_RECURSIVE)
+		mBihRoot = recBuild(events, nObjects, aabb, 0);
+	else if (mBuildMethod == BIHBM_PRIORITYQUEUE)
+		mBihRoot = pqBuild(events, nObjects, aabb, 0);
+	else
+	{
+		OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, 
+			"Invalid build method for the BiHierarchy.",
+			"BiHierarchy::build");
+		mBihRoot = 0;
+	}
+
+	t4 = timer->getMicroseconds(); // DEBUG
+
+	String method = "Invalid";
+	if (mBuildMethod == BIHBM_RECURSIVE)
+		method = "Recursive";
+	else if (mBuildMethod == BIHBM_PRIORITYQUEUE)
+		method = "Priority Queue";
+
+	mBuildLog->logMessage("######## SAH Statistics ########");
+	mBuildLog->logMessage("Build Method: " + method);
+	mBuildLog->logMessage("Time for events build: " + StringConverter::toString(t2 - t1) + "µs");
+	mBuildLog->logMessage("Time for events sort: " + StringConverter::toString(t3 - t2) + "µs");
+	mBuildLog->logMessage("Time for tree build: " + StringConverter::toString(t4 - t3) + "µs");
+	mBuildLog->logMessage("Total time: " + StringConverter::toString(t4 - t1) + "µs");
+	mBuildLog->logMessage("Tree Depth: " + StringConverter::toString(mMaxDepth));
+	mBuildLog->logMessage("Number of Objects: " + StringConverter::toString(mTreeStats.mNumSceneNodes));
+	mBuildLog->logMessage("Number of Leaves: " + StringConverter::toString(mTreeStats.mNumLeaves));
+	mBuildLog->logMessage("Number of Nodes: " + StringConverter::toString(mTreeStats.mNumNodes));
+	mBuildLog->logMessage("Total cost: " + StringConverter::toString(calcCost()));
+	mBuildLog->logMessage("################################");
+}
+
+BiHierarchy::Node * BiHierarchy::buildFromList(BihRenderableList& nodelist, BiHierarchy::Branch * parent, AxisAlignedBox& aabb)
+{
+	// data we want to collect
+	BihPlaneEventList events;
+	AxisAlignedBox nodeaabb;
+	int nObjects = 0;
+
+	BihRenderableList::iterator it = nodelist.begin();
+	BihRenderableList::iterator end = nodelist.end();
+	while (it != end)
+	{
+		(*it)->computeScene(events, nodeaabb, nObjects, false);
+		it++;
+	}
+
+	events.sort();
+	
+	// HACK
+	if (aabb.isNull())
+	{
+		aabb = nodeaabb;
+	}
+
+	if (mBuildMethod == BIHBM_RECURSIVE)
+		return recBuild(events, nObjects, aabb, parent);
+	else if (mBuildMethod == BIHBM_PRIORITYQUEUE)
+		return pqBuild(events, nObjects, aabb, parent);
+	else
+	{
+		OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, 
+			"Invalid build method for the BiHierarchy.",
+			"BiHierarchy::buildFromList");
+		return 0;
+	}
+}
+
+BiHierarchy::Node * BiHierarchy::recBuild(BihPlaneEventList& events, int nObjects, AxisAlignedBox& aabb, BiHierarchy::Branch * parent)
+{
+	// determine the depth we are on
+	int level = 0;
+	if (parent)
+	{
+		level = parent->getLevel() + 1;
+	}
+
+	/************************************************/
+	/**************** BEGIN FINDPLANE ***************/
+	/************************************************/
+	// find optimal split plane and split node accordingly
+
+	// initialize
+	const int dim = 3;
+	int pStart, pOn, pEnd;
+	int nLeft[dim], nPlane[dim], nRight[dim];
+	int nObjectCount[dim];
+	float LeftOffset[dim],RightOffset[dim];
+	for (int i = 0; i < dim; i++)
+	{
+		nLeft[i] = 0; nPlane[i] = 0; nRight[i] = nObjects;
+		nObjectCount[i]=0;
+	}
+
+	BihSplitInfo split, best;
+	best.cost = Math::POS_INFINITY;
+
+	BihPlaneEventList::iterator begin = events.begin();
+	BihPlaneEventList::iterator end = events.end();
+	BihPlaneEventList::iterator it = begin;
+
+	BihPlaneEventList::iterator it_a = begin; // iterator for adjusting the bounding box
+    
+	AxisAlignedBox minLefts[dim];
+	AxisAlignedBox minRights[dim];
+
+	bool LeftMod[dim];
+	bool RightMod[dim];
+
+	AxisAlignedBox minLeft;
+	AxisAlignedBox minRight;
+	
+
+	// try all planes to find the best one for the given set of objects
+	
+	// Use SAH method
+	if (mAlgorithm==BIHAL_SAH)
+	{
+		while (it != end)
+		{
+			BihPlaneEventList::iterator p = it;
+			pStart = pOn = pEnd = 0;
+			
+			while (it != end && p->equalsType(*it, BihPlaneEvent::PET_END))
+			{
+				it++; pEnd++;
+			}
+			while (it != end && p->equalsType(*it, BihPlaneEvent::PET_ON))
+			{
+				it++; pOn++;
+			}
+			while (it != end && p->equalsType(*it, BihPlaneEvent::PET_START))
+			{
+				it++; pStart++;
+			}
+			
+			
+			int d = p->getDimension();
+			nPlane[d] = pOn;
+			//nRight[d] -= pOn; nRight[d] -= pEnd;
+
+            // find the corrected offsets for the left and right bounding box
+			it_a = begin;
+			LeftOffset[d]=0;
+			RightOffset[d]=0;
+			minLefts[d].setNull();
+	        minRights[d].setNull();
+			nRight[d]=0;
+			nLeft[d]=0;
+			
+
+			while (it_a != end)
+			{
+				if (it_a->getDimension()==d)
+				{
+					it_a->getRenderable()->setSide(BihPlaneEvent::PES_BOTH);
+					it_a->getRenderable()->setClassified(false);
+					it_a->classify(*p, split.side,true);
+					if (it_a->getRenderable()->getSide() == BihPlaneEvent::PES_RIGHT)
+					{
+						if (!it_a->getRenderable()->isClassified())
+						{
+							it_a->getRenderable()->setClassified(true);
+							nRight[d]++;
+						}
+						minRights[d].merge(it_a->getRenderable()->getBoundingBox());
+						
+					}
+					// left-only nodes go in left list
+					else if (it_a->getRenderable()->getSide() == BihPlaneEvent::PES_LEFT)
+					{
+						if (!it_a->getRenderable()->isClassified())
+						{
+							it_a->getRenderable()->setClassified(true);
+							nLeft[d]++;
+						}
+						minLefts[d].merge(it_a->getRenderable()->getBoundingBox());
+						
+					}
+				}
+
+
+				it_a++;
+			}
+
+			
+				p->SAH(aabb, nLeft[d], nPlane[d], nRight[d], split,minLefts[d],minRights[d]);
+				mBuildLog->logMessage("SAH " + StringConverter::toString(best.cost)+ " " + StringConverter::toString(split.cost) + 
+					"dim: " + StringConverter::toString(d));
+				if (split.cost < best.cost)
+				{
+					best = split;
+				}
+			
+
+			//nLeft[d] += pStart; nLeft[d] += pOn; 
+			
+		    nPlane[d] = 0;
+		}
+	} else
+	{   
+		
+		int d;
+		while (it != end)
+		{
+			BihPlaneEventList::iterator p = it;
+			d = p->getDimension();
+			//if ((p->equalsType(*it, BihPlaneEvent::PET_START)) || (p->equalsType(*it, BihPlaneEvent::PET_ON)))
+			  nObjectCount[d]++;
+			it++;
+		}
+
+		int MaxDimension=1;
+		for (int i = 1; i < dim; i++)
+	    {
+		    if (nObjectCount[i]>nObjectCount[i-1]) MaxDimension=i;
+	    }
+		it = begin;
+		
+		nObjectCount[MaxDimension]/=2;
+		while ((it != end) && (nObjectCount[MaxDimension]))
+		{
+			BihPlaneEventList::iterator p = it;
+			if  (p->getDimension()== MaxDimension) 
+			{
+			  nObjectCount[MaxDimension]--;
+			  best.event=*it;
+			  best.cost=0;
+			  
+			}
+			it++;
+		}
+
+		
+
+		
+	
+
+
+
+
+	}
+
+
+	/************************************************/
+	/**************** BEGIN TERMINATE ***************/
+	/************************************************/
+	// check terminating condition
+	if (/*best.cost > BihPlaneEvent::KI*nObjects ||*/ level >= mMaxDepth || nObjects<20 )
+	{
+		// Terminating condition reached, create leaf and add renderables to list
+		mBuildLog->logMessage("Terminate " + StringConverter::toString(best.cost)+ " o:" + StringConverter::toString(nObjects) + 
+				" ki: " + StringConverter::toString(BihPlaneEvent::KI*nObjects)+ " dim: " + StringConverter::toString(best.event.getDimension()));
+		BiHierarchy::Leaf * leaf = new BiHierarchy::Leaf(this, level, aabb, parent);
+		if (parent->GetSide()==1) leaf->SetToLeft(); else leaf->SetToRight();
+
+		
+		BihRenderable *rend;
+		it = begin;
+		while (it != end)
+		{
+			rend = it->getRenderable();
+			rend->setClassified(false);
+			if (rend->attachTo(leaf, this))
+			{
+				leaf->insert(rend);
+			}
+			it++;
+		}
+		// update stats
+		++ mTreeStats.mNumNodes;
+		++ mTreeStats.mNumLeaves;
+		// update bounding box
+		leaf->_updateBounds(false);
+		return leaf;
+	}
+
+	/************************************************/
+	/**************** BEGIN CLASSIFY ****************/
+	/************************************************/
+	// split the event list in left and right sub-lists
+	else
+	{
+		BihPlaneEventList eventsRight, eventsLeft;
+		it = begin;
+		// slightly redundant, since we mark each renderable up to 6 times
+		while (it != end)
+		{
+			it->getRenderable()->setSide(BihPlaneEvent::PES_BOTH);
+			it->getRenderable()->setClassified(false);
+			it++;
+		}
+		// now classify all renderables. do they belong to the left child, the right child or both?
+		it = begin;
+		while (it != end)
+		{
+			it->classify(best.event, best.side,true);
+			it++;
+		}
+		// divide the event lists
+		int nLeftS = 0, nRightS = 0, nBothS = 0;
+		it = begin;
+
+		minLeft.setNull();
+	    minRight.setNull();
+
+		
+	
+		while (it != end)
+		{
+			// right-only nodes go in right list
+			if (it->getRenderable()->getSide() == BihPlaneEvent::PES_RIGHT)
+			{
+				if (!it->getRenderable()->isClassified())
+				{
+					it->getRenderable()->setClassified(true);
+					nRightS++;
+				}
+				minRight.merge(it->getRenderable()->getBoundingBox());
+				//mBuildLog->logMessage("merge right " + StringConverter::toString(minRight.volume()));
+				eventsRight.push_back(*it);
+			}
+			// left-only nodes go in left list
+			else if (it->getRenderable()->getSide() == BihPlaneEvent::PES_LEFT)
+			{
+				if (!it->getRenderable()->isClassified())
+				{
+					it->getRenderable()->setClassified(true);
+					nLeftS++;
+				}
+				minLeft.merge(it->getRenderable()->getBoundingBox());
+				//mBuildLog->logMessage("merge left " + StringConverter::toString(minLeft.volume()));
+				eventsLeft.push_back(*it);
+			}
+			// remaining nodes go in both lists, bust must be clipped to prevent
+			// events from lying outside the new nodes aabb
+			else
+			{
+				if (!it->getRenderable()->isClassified())
+				{
+					it->getRenderable()->setClassified(true);
+					nBothS++;
+				}
+				
+				// calculate the intersecting box for the right side
+                
+                 
+				  minRight.merge(best.bright.intersection(it->getRenderable()->getBoundingBox()));
+				
+
+				//mBuildLog->logMessage("merge Right Overlap " + StringConverter::toString(minRight.volume()));
+				eventsRight.push_back(it->clip(best.bright, best.event.getDimension()));
+				
+				  minLeft.merge(best.bleft.intersection(it->getRenderable()->getBoundingBox()));
+				
+				//mBuildLog->logMessage("merge Left Overlap " + StringConverter::toString(minLeft.volume()));
+				eventsLeft.push_back(it->clip(best.bleft, best.event.getDimension()));
+			}
+			it++;
+		}
+
+		mBuildLog->logMessage("Split R:" + StringConverter::toString(nRightS)+ " L:" + StringConverter::toString(nLeftS) + 
+				" O: " + StringConverter::toString(nObjects));
+
+
+		// calculate minimum of the left bounding box
+		/*
+		AxisAlignedBox minimum;
+		minimum.setNull();
+		it=eventsLeft.begin();
+		while (it!=eventsLeft.end())
+		{
+			minimum.merge(it->getRenderable()->getBoundingBox());
+			it++;
+		}
+		*/
+
+
+
+		// create a new branch node and continue recursion
+		BiHierarchy::Branch * branch = new BiHierarchy::Branch(this, level, aabb, parent, 
+			best.event.getSplitPlane(), best.side);
+
+		// now create the child nodes and continue recursion
+		/*
+		if (best.bleft.volume()>minLeft.volume())
+		{
+			mBuildLog->logMessage("Volume Smaller Left " + StringConverter::toString(minLeft.volume()) + " " + StringConverter::toString(best.bleft.volume(),3,3));
+		}
+
+		if (best.bright.volume()>minRight.volume())
+		{
+			mBuildLog->logMessage("Volume Smaller Right " + StringConverter::toString(minRight.volume()) + " " + StringConverter::toString(best.bright.volume(),3,3));
+		}
+		*/
+
+		if (eventsLeft.size() > 0)
+		{
+			
+			branch->mLeft = recBuild(eventsLeft, nLeftS + nBothS, /*best.bleft*/minLeft, branch);
+			branch->mLeft->SetToLeft();
+			mBuildLog->logMessage("SetToLeft");
+			//branch->mLeft = recBuild(eventsLeft, nLeftS + nBothS, best.bleft, branch);
+		}
+		if (eventsRight.size() > 0)
+		{
+			
+			branch->mRight = recBuild(eventsRight, nBothS + nRightS, /*best.bright*/ minRight, branch);
+			branch->mRight->SetToRight();
+			mBuildLog->logMessage("SetToRight");
+
+
+			//branch->mRight = recBuild(eventsRight, nBothS + nRightS, best.bright, branch);
+		}
+
+		// update stats
+		++ mTreeStats.mNumNodes;
+
+		// update bounding box
+		branch->_updateBounds(false);
+
+		return branch;
+	}
+}
+
+BiHierarchy::Node * BiHierarchy::pqBuild(BihPlaneEventList& events, 
+										 int nObjects, AxisAlignedBox& aabb, 
+										 BiHierarchy::Branch * parent)
+{
+	SplitCandidatePQ pqueue;
+	Real globalTreeCost;
+	Real globalSA;
+
+	BiHierarchy::Node * newNode = 0, * topNode = 0;
+	// init global tree cost
+	globalTreeCost = BihPlaneEvent::KI * nObjects;
+	globalSA = BihPlaneEvent::surfaceArea(aabb);
+
+	BihPlaneEventList * e = new BihPlaneEventList(events);
+
+	// inital split candidate
+	BihSplitInfo * best = pqFindPlane(e, nObjects, aabb, globalSA);
+	BihSubdivisionCandidate splitcandidate(e, nObjects, aabb, parent, globalTreeCost, 
+		globalTreeCost - best->cost, best, BihPlaneEvent::PES_BOTH);
+	pqueue.push(splitcandidate);
+
+
+	/*** BEGIN ITERATIVE BUILD ***/
+	while (!pqueue.empty())
+	{
+		BihSubdivisionCandidate sc = pqueue.top();
+		pqueue.pop();
+
+		// determine the depth we are on
+		int level = 0;
+		if (sc.parent)
+		{
+			level = sc.parent->getLevel() + 1;
+		}
+
+		/************************************************/
+		/**************** BEGIN TERMINATE ***************/
+		/************************************************/
+		// check terminating condition
+		if (sc.decrease < 0.0 || level >= mMaxDepth)
+		{
+			// Terminating condition reached, create leaf and add renderables to list
+			BiHierarchy::Leaf * leaf = new BiHierarchy::Leaf(this, level, sc.aabb, sc.parent);
+			BihRenderable *rend;
+			BihPlaneEventList::iterator begin = sc.events->begin();
+			BihPlaneEventList::iterator end = sc.events->end();
+			BihPlaneEventList::iterator it = begin;
+			while (it != end)
+			{
+				rend = it->getRenderable();
+				rend->setClassified(false);
+				if (rend->attachTo(leaf, this))
+				{
+					leaf->insert(rend);
+				}
+				it++;
+			}
+			newNode = leaf;
+			if (!topNode)
+				topNode = leaf;
+			// update stats
+			++ mTreeStats.mNumNodes;
+			++ mTreeStats.mNumLeaves;
+		}
+
+		/************************************************/
+		/**************** BEGIN CLASSIFY ****************/
+		/************************************************/
+		else
+		{
+			BihPlaneEventList * eventsLeft = new BihPlaneEventList();
+			BihPlaneEventList * eventsRight = new BihPlaneEventList();
+			BihPlaneEventList::iterator begin = sc.events->begin();
+			BihPlaneEventList::iterator end = sc.events->end();
+			BihPlaneEventList::iterator it = begin;
+			// slightly redundant, since we mark each renderable up to 6 times
+			while (it != end)
+			{
+				it->getRenderable()->setSide(BihPlaneEvent::PES_BOTH);
+				it->getRenderable()->setClassified(false);
+				it++;
+			}
+
+			// now classify all renderables. do they belong to the left child, the right child or both?
+			it = begin;
+			while (it != end)
+			{
+				it->classify(sc.best->event, sc.best->side,false);
+				it++;
+			}
+
+			// divide the event lists
+			int nLeftS = 0, nRightS = 0, nBothS = 0;
+			it = begin;
+			while (it != end)
+			{
+				// right-only events go in the right list
+				if (it->getRenderable()->getSide() == BihPlaneEvent::PES_RIGHT)
+				{
+					if (!it->getRenderable()->isClassified())
+					{
+						it->getRenderable()->setClassified(true);
+						nRightS++;
+					}
+
+					eventsRight->push_back(*it);
+				}
+				// left-only events go in the left list
+				else if (it->getRenderable()->getSide() == BihPlaneEvent::PES_LEFT)
+				{
+					if (!it->getRenderable()->isClassified())
+					{
+						it->getRenderable()->setClassified(true);
+						nLeftS++;
+					}
+					eventsLeft->push_back(*it);
+				}
+				// the rest goes in both lists after being clipped
+				else
+				{
+					if (!it->getRenderable()->isClassified())
+					{
+						it->getRenderable()->setClassified(true);
+						nBothS++;
+					}
+
+					eventsRight->push_back(it->clip(sc.best->bright, sc.best->event.getDimension()));
+					eventsLeft->push_back(it->clip(sc.best->bleft, sc.best->event.getDimension()));
+				}
+				it++;
+			}
+
+			// create a new branch node
+			BiHierarchy::Branch * branch = new BiHierarchy::Branch(this, level, sc.aabb, sc.parent, 
+				sc.best->event.getSplitPlane(), sc.best->side);
+
+			globalTreeCost -= sc.decrease;
+
+
+			// now for each potential child node, compute the split plane and the cost decrease
+			// and place them in the queue
+			if (eventsLeft->size() > 0)
+			{
+				best = pqFindPlane(eventsLeft, nLeftS + nBothS, sc.best->bleft, globalSA);
+				Real old = BihPlaneEvent::surfaceArea(sc.best->bleft)/globalSA*BihPlaneEvent::KI*(nLeftS + nBothS);
+				BihSubdivisionCandidate scleft(eventsLeft, nLeftS + nBothS, sc.best->bleft, 
+					branch, old, old - best->cost, best, BihPlaneEvent::PES_LEFT);
+				pqueue.push(scleft);
+			}
+			// cleanup
+			else
+			{
+				delete eventsLeft;
+			}
+
+			if (eventsRight->size() > 0)
+			{
+				best = pqFindPlane(eventsRight, nBothS + nRightS, sc.best->bright, globalSA);
+				Real old = BihPlaneEvent::surfaceArea(sc.best->bright)/globalSA*BihPlaneEvent::KI*(nBothS + nRightS);
+				BihSubdivisionCandidate scright(eventsRight, nBothS + nRightS, sc.best->bright, 
+					branch, old, old - best->cost, best, BihPlaneEvent::PES_RIGHT);
+				pqueue.push(scright);
+			}
+			// cleanup
+			else
+			{
+				delete eventsRight;
+			}
+
+			newNode = branch;
+			if (!topNode)
+				topNode = branch;
+
+
+			// update stats
+			++ mTreeStats.mNumNodes;
+		}
+
+		// attach new node to parent
+		if (sc.parent)
+		{
+			if (sc.side == BihPlaneEvent::PES_LEFT)
+			{
+				sc.parent->mLeft = newNode;
+			}
+			else if (sc.side == BihPlaneEvent::PES_RIGHT)
+			{
+				sc.parent->mRight = newNode;
+			}
+			else
+			{
+				OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR,
+					"PQBUILD SNAFU - CHILD I AM NOT LEFT NOR RIGHT","BiHierarchy::pqBuild");
+			}
+		}
+
+		// update bounding boxes when geometry (leaf) was added
+		if (newNode->isLeaf())
+			newNode->_updateBounds();
+
+		//cleanup
+		OGRE_DELETE(sc.events);
+		OGRE_DELETE(sc.best);
+	}
+	mBuildLog->logMessage("Cost after PQBUILD " + StringConverter::toString(globalTreeCost));
+	return topNode;
+}
+
+//-------------------------------------------------------------------------
+BihSplitInfo * BiHierarchy::pqFindPlane(BihPlaneEventList * events, int nObjects, AxisAlignedBox& aabb, Real globalSA)
+{
+	static const int dim = 3;
+	int pStart, pOn, pEnd;
+	int nLeft[dim], nPlane[dim], nRight[dim];
+	for (int i = 0; i < dim; i++)
+	{
+		nLeft[i] = 0; nPlane[i] = 0; nRight[i] = nObjects;
+	}
+
+	BihSplitInfo split, best;
+	best.cost = Math::POS_INFINITY;
+
+	Real parentSA = BihPlaneEvent::surfaceArea(aabb);
+
+	BihPlaneEventList::iterator begin = events->begin();
+	BihPlaneEventList::iterator end = events->end();
+	BihPlaneEventList::iterator it = begin;
+	// try all planes to find the best one for the given set of objects
+	while (it != end)
+	{
+		BihPlaneEventList::iterator p = it;
+		pStart = pOn = pEnd = 0;
+		while (it != end && p->equalsType(*it, BihPlaneEvent::PET_END))
+		{
+			it++; pEnd++;
+		}
+		while (it != end && p->equalsType(*it, BihPlaneEvent::PET_ON))
+		{
+			it++; pOn++;
+		}
+		while (it != end && p->equalsType(*it, BihPlaneEvent::PET_START))
+		{
+			it++; pStart++;
+		}
+		int d = p->getDimension();
+		nPlane[d] = pOn; nRight[d] -= pOn; nRight[d] -= pEnd;
+		p->pqSAH(globalSA, parentSA, nLeft[d], nPlane[d], nRight[d], aabb, split);
+		if (split.cost < best.cost)
+		{
+			best = split;
+		}
+		nLeft[d] += pStart; nLeft[d] += pOn; nPlane[d] = 0;
+	}
+	return new BihSplitInfo(best);
+}
+
+
+
+/************************************************************************/
+/*               BiHierarchy rendering functions                        */
+/************************************************************************/
+
+
+//-------------------------------------------------------------------------
+void BiHierarchy::queueVisibleObjects(BiHierarchyCamera* cam, RenderQueue* queue, bool onlyShadowCasters, 
+	bool showBoxes, BiHierarchy::NodeList& visibleNodes)
+{
+	mFrameStats.clear();
+	cam->mNumVisQueries = 0;
+
+	if (mBihRoot)
+		recQueueVisibleObjects(mBihRoot, Root::getSingleton().getCurrentFrameNumber(),
+			cam, queue, onlyShadowCasters, showBoxes, visibleNodes);
+
+	mFrameStats.mTraversedNodes = cam->mNumVisQueries;
+}
+
+//-------------------------------------------------------------------------
+void BiHierarchy::recQueueVisibleObjects(BiHierarchy::Node * node, unsigned long currentFrame, 
+	BiHierarchyCamera* cam, RenderQueue* queue, bool onlyShadowCasters, bool showBoxes, 
+	BiHierarchy::NodeList& visibleNodes, bool fullVis)
+{
+	BiHierarchyCamera::NodeVisibility vis = BiHierarchyCamera::BIHNV_PART;
+	// test visibility
+	//if (cam->isVisible(node->mAABB))
+	if (fullVis || 
+		((vis = (cam->*getVisibility)(node->mAABB)) != BiHierarchyCamera::BIHNV_NONE))
+	{
+		visibleNodes.push_back(node);
+
+		bool v = (fullVis || vis == BiHierarchyCamera::BIHNV_FULL);
+
+		node->queueVisibleObjects(currentFrame, cam, queue, onlyShadowCasters, showBoxes, v);
+
+		if (v || node->isLeaf())
+			++ mFrameStats.mRenderedNodes;
+
+		if (!v)
+		{
+
+			if (node->getLeftChild())
+				recQueueVisibleObjects(node->getLeftChild(), currentFrame, 
+				cam, queue, onlyShadowCasters, showBoxes, visibleNodes, v);
+			if (node->getRightChild())
+				recQueueVisibleObjects(node->getRightChild(), currentFrame, 
+				cam, queue, onlyShadowCasters, showBoxes, visibleNodes, v);
+		}
+	}
+	else
+	{
+		++ mFrameStats.mFrustumCulledNodes;
+	}
+}
+
+//-------------------------------------------------------------------------
+void BiHierarchy::setEnhancedVis(bool enh)
+{
+	if (enh)
+		getVisibility = &BiHierarchyCamera::getVisibilityEnhanced;
+	else
+		getVisibility = &BiHierarchyCamera::getVisibilitySimple;
+}
+
+//-------------------------------------------------------------------------
+bool BiHierarchy::getEnhancedVis()
+{
+	return getVisibility == &BiHierarchyCamera::getVisibilityEnhanced;
+}
+
+//-------------------------------------------------------------------------
+//void BiHierarchy::findVisibleNodes(NodeList& visibleNodes, Camera * cam)
+//{
+//	if (mBihRoot)
+//		recFindVisibleNodes(mBihRoot, visibleNodes, cam);
+//}
+
+////-------------------------------------------------------------------------
+//void BiHierarchy::recFindVisibleNodes(BiHierarchy::Node * node, NodeList& visibleNodes, Camera * cam)
+//{
+//	// test visibility
+//	if (cam->isVisible(node->mAABB))
+//	{
+//		visibleNodes.push_back(node);
+
+//		if (node->getLeftChild())
+//			recFindVisibleNodes(node->getLeftChild(), visibleNodes, cam);
+//		if (node->getRightChild())
+//			recFindVisibleNodes(node->getRightChild(), visibleNodes, cam);
+//	}
+//}
+
+void BiHierarchy::findNodesIn(const AxisAlignedBox &box, std::list<SceneNode *> &list, SceneNode *exclude)
+{
+	if (mBihRoot)
+		recFindNodesIn(box, list, exclude, mBihRoot);
+}
+
+void BiHierarchy::findNodesIn(const Sphere &sphere, std::list<SceneNode *> &list, SceneNode *exclude)
+{
+	if (mBihRoot)
+		recFindNodesIn(sphere, list, exclude, mBihRoot);
+}
+
+void BiHierarchy::findNodesIn(const PlaneBoundedVolume &volume, std::list<SceneNode *> &list, SceneNode *exclude)
+{
+	if (mBihRoot)
+		recFindNodesIn(volume, list, exclude, mBihRoot);
+}
+
+void BiHierarchy::findNodesIn(const Ray &ray, std::list<SceneNode *> &list, SceneNode *exclude)
+{
+	if (mBihRoot)
+		recFindNodesIn(ray, list, exclude, mBihRoot);
+}
+
+void BiHierarchy::recFindNodesIn(const AxisAlignedBox &box, std::list<SceneNode *> &list, SceneNode *exclude, Node * node, bool full)
+{
+	// check intersection
+	if ( !full )
+	{
+		BihIntersection isect = intersect(box, node->_getWorldAABB());
+
+		if ( isect == OUTSIDE )
+			return ;
+
+		full = ( isect == INSIDE );
+	}
+
+	if (node->isLeaf())
+	{
+		LeafPtr leaf = BIHLEAFPTR_CAST(node);
+		for (BihRenderableList::iterator it = leaf->mBihRenderables.begin();
+			it != leaf->mBihRenderables.end(); it ++)
+		{
+			SceneNode *sn = static_cast<BiHierarchySceneNode *>(*it);
+
+			if (sn != exclude)
+			{
+				if (full)
+				{
+					list.push_back(sn);
+				}
+				else
+				{
+					BihIntersection nsect = intersect(box, sn->_getWorldAABB());
+
+					if ( nsect != OUTSIDE )
+					{
+						list.push_back(sn);
+					}
+				}
+			}
+		}
+	}
+	else
+	{
+		if (node->getLeftChild())
+			recFindNodesIn(box, list, exclude, node->getLeftChild(), full);
+		if (node->getRightChild())
+			recFindNodesIn(box, list, exclude, node->getRightChild(), full);
+	}
+}
+
+
+void BiHierarchy::recFindNodesIn(const Sphere &sphere, std::list<SceneNode *> &list, SceneNode *exclude, Node * node, bool full)
+{
+	// TODO
+}
+
+
+void BiHierarchy::recFindNodesIn(const PlaneBoundedVolume &volume, std::list<SceneNode *> &list, SceneNode *exclude, Node * node, bool full)
+{
+	// TODO
+}
+
+
+void BiHierarchy::recFindNodesIn(const Ray &ray, std::list<SceneNode *> &list, SceneNode *exclude, Node * node, bool full)
+{
+	// TODO
+}
+
+
+/************************************************************************/
+/*              BiHierarchy debug & helper functions                    */
+/************************************************************************/
+
+//-------------------------------------------------------------------------
+void BiHierarchy::dump()
+{
+	//LogManager::getSingleton().logMessage("#@#@#@#@ Dumping BiHierarchy #@#@#@#@");
+	mBuildLog->logMessage("#@#@#@#@ Dumping BiHierarchy #@#@#@#@");
+	if (mBihRoot)
+		dump(mBihRoot);
+}
+
+//-------------------------------------------------------------------------
+void BiHierarchy::dump(BiHierarchy::Node * node)
+{
+	//LogManager * log = LogManager::getSingletonPtr();
+	BiHierarchySceneNode * scenenode;
+	String pad;
+	int p;
+	
+	pad = "";
+	for (p = 0; p < node->getLevel(); p++)
+	{
+		pad += " ";
+	}
+
+	if (node->isLeaf())
+	{
+		BiHierarchy::Leaf * leaf = BIHLEAFPTR_CAST(node);
+		BihRenderableList::iterator it  = leaf->mBihRenderables.begin();
+		BihRenderableList::iterator end = leaf->mBihRenderables.end();
+		while (it != end)
+		{
+			scenenode = static_cast<BiHierarchySceneNode *>(*it);
+			mBuildLog->logMessage(pad + "# Leaf   level " + 
+				StringConverter::toString(node->getLevel()) + 
+				" SceneNode " + scenenode->getName());
+			mBuildLog->logMessage(pad + "## Objects: " + scenenode->dumpToString());
+			it++;
+		}
+	}
+	else
+	{
+		BiHierarchy::Branch * branch = BIHBRANCHPTR_CAST(node);
+		if (branch->mLeft)
+		{
+			mBuildLog->logMessage(pad + "# Branch level " + 
+				StringConverter::toString(node->getLevel()) + " Left Child");
+			dump(branch->mLeft);
+		}
+		if (branch->mRight)
+		{
+			mBuildLog->logMessage(pad + "# Branch level " + 
+				StringConverter::toString(node->getLevel()) + " Right Child");
+			dump(branch->mRight);
+		}
+	}
+}
+
+//-------------------------------------------------------------------------
+Real BiHierarchy::calcCost()
+{
+	if (mBihRoot)
+		return calcCost(mBihRoot, BihPlaneEvent::surfaceArea(mBihRoot->mAABB));
+	else
+		return 0;
+}
+
+//-------------------------------------------------------------------------
+Real BiHierarchy::calcCost(BiHierarchy::Node * node, Real vs)
+{
+	if (node == 0)
+		return 0.0;
+
+	if (node->isLeaf())
+	{
+		BiHierarchy::Leaf * leaf = BIHLEAFPTR_CAST(node);
+		return (BihPlaneEvent::surfaceArea(node->mAABB)/vs) *
+			BihPlaneEvent::KI * leaf->mBihRenderables.size();
+	}
+	else
+	{
+		BiHierarchy::Branch * branch = BIHBRANCHPTR_CAST(node);
+		return (BihPlaneEvent::surfaceArea(node->mAABB)/vs) * BihPlaneEvent::KT + 
+			calcCost(branch->mLeft, vs) + calcCost(branch->mRight, vs);
+	}
+}
+
+/************************************************************************/
+/* BiHierarchy::Node/Branch/Leaf functions                                   */
+/************************************************************************/
+
+//-------------------------------------------------------------------------
+BiHierarchy::Leaf::~Leaf()
+{
+	// detach all scene nodes in the case that we are rebuilding
+	// the tree but not the scene
+	BihRenderableList::iterator it = mBihRenderables.begin();
+	BihRenderableList::iterator end = mBihRenderables.end();
+	while (it != end)
+	{
+		(*it)->detachFrom(this);
+		it++;
+	}
+	mBihRenderables.clear();
+}
+
+//-------------------------------------------------------------------------
+void BiHierarchy::Leaf::queueVisibleObjects(unsigned long currentFrame, 
+	Camera* cam, RenderQueue* queue, bool onlyShadowCasters, bool showBoxes, bool fullVis)
+{
+	BihRenderableList::iterator it  = mBihRenderables.begin();
+	BihRenderableList::iterator end = mBihRenderables.end();
+	while (it != end)
+	{			
+		if (!(*it)->isQueued(currentFrame, cam))
+		{
+			(*it)->queueObjects(cam, queue, onlyShadowCasters);
+		}
+		it++;
+	}
+
+	if (showBoxes)
+	{
+        if (mLevel == mOwner->getHiLiteLevel()|| (mLevel-1) == mOwner->getHiLiteLevel() || mOwner->getShowAllBoxes())
+		queue->addRenderable(getWireBoundingBox());
+		/*
+		if (mLevel == mOwner->getHiLiteLevel() || mOwner->getShowAllBoxes())
+			queue->addRenderable(getWireBoundingBox());
+		*/
+	}
+
+}
+
+//-------------------------------------------------------------------------
+void BiHierarchy::Leaf::getGeometryList(GeometryVector *geometryList)
+{
+	BihRenderableList::iterator it = mBihRenderables.begin();
+	BihRenderableList::iterator end = mBihRenderables.end();
+	while (it != end)
+	{
+		(*it)->getGeometryList(geometryList);
+		it++;
+	}
+}
+
+//-------------------------------------------------------------------------
+// update the world aabb based on the contained geometry
+void BiHierarchy::Leaf::_updateBounds(bool recurse)
+{
+	// reset box
+	mWorldAABB.setNull();
+
+	// merge boxes from attached geometry
+	BihRenderableList::iterator it = mBihRenderables.begin();
+	BihRenderableList::iterator end = mBihRenderables.end();
+	while (it != end)
+	{
+		//(*it)->_updateBounds();
+		mWorldAABB.merge((*it)->getBoundingBox());
+		it++;
+	}
+
+	// update parent recursively
+	if (recurse && mParent)
+		mParent->_updateBounds(recurse);
+}
+
+} // namespace Ogre
Index: GTP/trunk/Lib/Vis/OnlineCullingCHC/OGRE/src/OgreBiTerrainPageSource.cpp
===================================================================
--- GTP/trunk/Lib/Vis/OnlineCullingCHC/OGRE/src/OgreBiTerrainPageSource.cpp	(revision 2348)
+++ GTP/trunk/Lib/Vis/OnlineCullingCHC/OGRE/src/OgreBiTerrainPageSource.cpp	(revision 2348)
@@ -0,0 +1,177 @@
+/*
+-----------------------------------------------------------------------------
+This source file is part of OGRE
+(Object-oriented Graphics Rendering Engine)
+For the latest info, see http://www.ogre3d.org/
+
+Copyright (c) 2000-2005 The OGRE Team
+Also see acknowledgements in Readme.html
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU Lesser General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA 02111-1307, USA, or go to
+http://www.gnu.org/copyleft/lesser.txt.
+-----------------------------------------------------------------------------
+*/
+/*
+-----------------------------------------------------------------------------
+This source file is part of the GameTools Project
+http://www.gametools.org
+
+Author: Martin Szydlowski
+-----------------------------------------------------------------------------
+*/
+
+#include <OgreSceneNode.h>
+#include "OgreBiTerrainPage.h"
+#include "OgreBiTerrainRenderable.h"
+#include "OgreBiTerrainPageSource.h"
+#include "OgreBiTerrainSceneManager.h"
+
+#pragma warning(disable : 4267)
+
+namespace Ogre {
+
+    //-----------------------------------------------------------------------
+    template<> BiTerrainPageSourceListenerManager* Singleton<BiTerrainPageSourceListenerManager>::ms_Singleton = 0;
+    BiTerrainPageSourceListenerManager* BiTerrainPageSourceListenerManager::getSingletonPtr(void)
+    {
+        return ms_Singleton;
+    }
+    BiTerrainPageSourceListenerManager& BiTerrainPageSourceListenerManager::getSingleton(void)
+    {  
+        assert( ms_Singleton );  return ( *ms_Singleton );  
+    }
+    //-------------------------------------------------------------------------
+	void BiTerrainPageSourceListenerManager::addListener(BiTerrainPageSourceListener* pl)
+	{
+        mPageSourceListeners.push_back(pl);
+	}
+    //-------------------------------------------------------------------------
+	void BiTerrainPageSourceListenerManager::removeListener(BiTerrainPageSourceListener* pl)
+	{
+        BiPageSourceListenerList::iterator i, iend;
+        iend = mPageSourceListeners.end();
+        for(i = mPageSourceListeners.begin(); i != iend; ++i)
+        {
+            if (*i == pl)
+            {
+                mPageSourceListeners.erase(i);
+                break;
+            }
+        }
+	}
+    //-------------------------------------------------------------------------
+	void BiTerrainPageSourceListenerManager::firePageConstructed(
+		BiTerrainSceneManager* sm, uint pagex, uint pagez, Real* heightData)
+	{
+        BiPageSourceListenerList::iterator i, iend;
+        iend = mPageSourceListeners.end();
+        for(i = mPageSourceListeners.begin(); i != iend; ++i)
+        {
+            (*i)->pageConstructed(sm, pagex, pagez, heightData);
+        }
+	}
+	//-------------------------------------------------------------------------
+	BiTerrainPageSource::BiTerrainPageSource() : mSceneManager(0), mAsyncLoading(false) {
+	}
+	//-------------------------------------------------------------------------
+	BiTerrainPage* BiTerrainPageSource::buildPage(Real* heightData, const MaterialPtr& pMaterial)
+    {
+        String name;
+
+        // Create a Terrain Page
+        BiTerrainPage* page = new BiTerrainPage((mPageSize-1) / (mTileSize-1));
+        // Create a node for all tiles to be attached to
+        // Note we sequentially name since page can be attached at different points
+        // so page x/z is not appropriate
+		StringUtil::StrStreamType page_str;
+		uint pageIndex = mSceneManager->_getPageCount();
+		page_str << pageIndex;
+        name = "page[";
+        name += page_str.str() + "]";
+        page->pageSceneNode = mSceneManager->createSceneNode(name);
+        
+        size_t q = 0;
+        for ( size_t j = 0; j < mPageSize - 1; j += ( mTileSize - 1 ) )
+        {
+            size_t p = 0;
+
+            for ( size_t i = 0; i < mPageSize - 1; i += ( mTileSize - 1 ) )
+            {
+				StringUtil::StrStreamType new_name_str;
+				
+                // Create scene node for the tile and the TerrainRenderable
+                new_name_str << "tile[" << pageIndex << "][" << (int)p << "," << (int)q << "]";
+				name = new_name_str.str();
+
+                SceneNode *c = page->pageSceneNode->createChildSceneNode( name );
+                BiTerrainRenderable *tile = new BiTerrainRenderable(name, mSceneManager);
+				// set queue
+				tile->setRenderQueueGroup(mSceneManager->getWorldGeometryRenderQueue());
+                // Initialise the tile
+                tile->setMaterial(pMaterial);
+                tile->initialise(i, j, heightData);
+                // Attach it to the page
+                page->tiles[ p ][ q ] = tile;
+                // Attach it to the node
+                c ->attachObject( tile );
+                p++;
+            }
+
+            q++;
+
+        }
+
+        pageIndex++;
+
+        // calculate neighbours for page
+        page->linkNeighbours();
+
+		if(mSceneManager->getOptions().lit)
+		{
+			q = 0;
+			for ( size_t j = 0; j < mPageSize - 1; j += ( mTileSize - 1 ) )
+			{
+				size_t p = 0;
+
+				for ( size_t i = 0; i < mPageSize - 1; i += ( mTileSize - 1 ) )
+				{
+					page->tiles[ p ][ q ]->_calculateNormals();
+					p++;
+				}
+				q++;
+			}
+		}
+
+        return page;
+    }
+    //-------------------------------------------------------------------------
+    void BiTerrainPageSource::firePageConstructed(size_t pagex, size_t pagez, Real* heightData)
+    {
+		BiTerrainPageSourceListenerManager::getSingleton().firePageConstructed(
+			mSceneManager, pagex, pagez, heightData);
+    }
+    //-------------------------------------------------------------------------
+    void BiTerrainPageSource::addListener(BiTerrainPageSourceListener* pl)
+    {
+		BiTerrainPageSourceListenerManager::getSingleton().addListener(pl);
+    }
+    //-------------------------------------------------------------------------
+    void BiTerrainPageSource::removeListener(BiTerrainPageSourceListener* pl)
+    {
+		BiTerrainPageSourceListenerManager::getSingleton().removeListener(pl);
+
+    }
+    //-------------------------------------------------------------------------
+
+}
Index: GTP/trunk/Lib/Vis/OnlineCullingCHC/OGRE/src/OgreBiHierarchyCamera.cpp
===================================================================
--- GTP/trunk/Lib/Vis/OnlineCullingCHC/OGRE/src/OgreBiHierarchyCamera.cpp	(revision 2348)
+++ GTP/trunk/Lib/Vis/OnlineCullingCHC/OGRE/src/OgreBiHierarchyCamera.cpp	(revision 2348)
@@ -0,0 +1,104 @@
+/*
+-----------------------------------------------------------------------------
+This source file is part of the GameTools Project
+http://www.gametools.org
+
+Author: Martin Szydlowski
+-----------------------------------------------------------------------------
+*/
+
+#include "OgreBiHierarchyCamera.h"
+
+namespace Ogre
+{
+
+BiHierarchyCamera::BiHierarchyCamera(const String& name, SceneManager *sm):
+Camera(name, sm)
+{
+
+}
+
+BiHierarchyCamera::~BiHierarchyCamera()
+{
+
+}
+
+//-----------------------------------------------------------------------
+// enhanced vis calculation - borrowed from OctreeCamera - indicate when entire box inside the frustum
+BiHierarchyCamera::NodeVisibility BiHierarchyCamera::getVisibilityEnhanced(const AxisAlignedBox& box) const
+{
+	++ mNumVisQueries;
+	
+	// Null boxes always invisible
+	if ( box.isNull() )
+		return BIHNV_NONE;
+
+	// Make any pending updates to the calculated frustum planes
+	updateFrustumPlanes();
+
+	// Get corners of the box
+	const Vector3* pCorners = box.getAllCorners();
+
+	// For each plane, see if all points are on the negative side
+	// If so, object is not visible.
+	// If one or more are, it's partial.
+	// If all aren't, full
+
+	int corners[ 8 ] = {0, 4, 3, 5, 2, 6, 1, 7};
+
+	int planes[ 6 ] = {FRUSTUM_PLANE_TOP, FRUSTUM_PLANE_BOTTOM,
+		FRUSTUM_PLANE_LEFT, FRUSTUM_PLANE_RIGHT,
+		FRUSTUM_PLANE_FAR, FRUSTUM_PLANE_NEAR };
+
+	bool all_inside = true;
+
+	for ( int plane = 0; plane < 6; ++plane )
+	{
+
+		// Skip far plane if infinite view frustum
+		if (mFarDist == 0 && planes[ plane ] == FRUSTUM_PLANE_FAR)
+			continue;
+
+		bool all_outside = true;
+
+		float distance = 0;
+
+		for ( int corner = 0; corner < 8; ++corner )
+		{
+			distance = mFrustumPlanes[ planes[ plane ] ].getDistance( pCorners[ corners[ corner ] ] );
+			all_outside = all_outside && ( distance < 0 );
+			all_inside = all_inside && ( distance >= 0 );
+
+			if ( !all_outside && !all_inside )
+				break;
+		}
+
+		if ( all_outside )
+			return BIHNV_NONE;
+	}
+
+	if ( all_inside )
+		return BIHNV_FULL;
+	else
+		return BIHNV_PART;
+}
+
+//-----------------------------------------------------------------------
+// simple vis calculation - does the same as isVisible, only different return type
+BiHierarchyCamera::NodeVisibility BiHierarchyCamera::getVisibilitySimple(const AxisAlignedBox& box) const
+{
+	++ mNumVisQueries;
+
+	// dummy
+	FrustumPlane* culledBy = 0;
+	if (mCullFrustum)
+	{
+		return mCullFrustum->isVisible(box, culledBy) ? BIHNV_PART : BIHNV_NONE;
+	}
+	else
+	{
+		return Frustum::isVisible(box, culledBy) ? BIHNV_PART : BIHNV_NONE;
+	}
+}
+
+} // namespace Ogre
Index: GTP/trunk/Lib/Vis/OnlineCullingCHC/OGRE/src/OgreBiTerrainRenderable.cpp
===================================================================
--- GTP/trunk/Lib/Vis/OnlineCullingCHC/OGRE/src/OgreBiTerrainRenderable.cpp	(revision 2348)
+++ GTP/trunk/Lib/Vis/OnlineCullingCHC/OGRE/src/OgreBiTerrainRenderable.cpp	(revision 2348)
@@ -0,0 +1,1593 @@
+/*
+-----------------------------------------------------------------------------
+This source file is part of OGRE
+(Object-oriented Graphics Rendering Engine)
+For the latest info, see http://www.ogre3d.org/
+
+Copyright (c) 2000-2005 The OGRE Team
+Also see acknowledgements in Readme.html
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU Lesser General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA 02111-1307, USA, or go to
+http://www.gnu.org/copyleft/lesser.txt.
+-----------------------------------------------------------------------------
+*/
+/***************************************************************************
+terrainrenderable.cpp  -  description
+-------------------
+begin                : Sat Oct 5 2002
+copyright            : (C) 2002 by Jon Anderson
+email                : janders@users.sf.net
+
+Enhancements 2003 - 2004 (C) The OGRE Team
+
+***************************************************************************/
+
+#include "OgreBiTerrainRenderable.h"
+#include "OgreSceneNode.h"
+#include "OgreRenderQueue.h"
+#include "OgreRenderOperation.h"
+#include "OgreCamera.h"
+#include "OgreRoot.h"
+#include "OgreBiTerrainSceneManager.h"
+#include "OgreLogManager.h"
+#include "OgreStringConverter.h"
+#include "OgreViewport.h"
+#include "OgreException.h"
+
+#pragma  warning(disable : 4267)
+
+namespace Ogre
+{
+    //-----------------------------------------------------------------------
+    #define MAIN_BINDING 0
+    #define DELTA_BINDING 1
+    //-----------------------------------------------------------------------
+    //-----------------------------------------------------------------------
+    String BiTerrainRenderable::mType = "TerrainMipMap";
+    //-----------------------------------------------------------------------
+    //-----------------------------------------------------------------------
+#ifdef GTP_VISIBILITY_MODIFIED_OGRE
+	int BiTerrainRenderable::msCurrentRenderLevelIndex = 0;
+#endif // GTP_VISIBILITY_MODIFIED_OGRE
+    //-----------------------------------------------------------------------
+    BiTerrainRenderable::BiTerrainRenderable(const String& name, BiTerrainSceneManager* tsm)
+        : Renderable(), MovableObject(name), mSceneManager(tsm), mTerrain(0), mDeltaBuffers(0), mPositionBuffer(0)
+    {
+        mForcedRenderLevel = -1;
+        mLastNextLevel = -1;
+
+        mMinLevelDistSqr = 0;
+
+        mInit = false;
+        mLightListDirty = true;
+		MovableObject::mCastShadows = false;
+
+        for ( int i = 0; i < 4; i++ )
+        {
+            mNeighbors[ i ] = 0;
+        }
+
+		mOptions = &(mSceneManager->getOptions());
+
+
+    }
+    //-----------------------------------------------------------------------
+    BiTerrainRenderable::~BiTerrainRenderable()
+    {
+
+        deleteGeometry();
+    }
+	//-----------------------------------------------------------------------
+	uint32 BiTerrainRenderable::getTypeFlags(void) const
+	{
+		// return world flag
+		return SceneManager::WORLD_GEOMETRY_TYPE_MASK;
+	}
+    //-----------------------------------------------------------------------
+    void BiTerrainRenderable::deleteGeometry()
+    {
+        if(mTerrain)
+            delete mTerrain;
+
+        if (mPositionBuffer)
+            delete [] mPositionBuffer;
+
+        if (mDeltaBuffers)
+            delete [] mDeltaBuffers;
+
+        if ( mMinLevelDistSqr != 0 )
+            delete [] mMinLevelDistSqr;
+    }
+    //-----------------------------------------------------------------------
+    void BiTerrainRenderable::initialise(int startx, int startz,  
+        Real* pageHeightData)
+    {
+
+        if ( mOptions->maxGeoMipMapLevel != 0 )
+        {
+            uint i = ( uint ) 1 << ( mOptions->maxGeoMipMapLevel - 1 ) ;
+
+            if ( ( i + 1 ) > mOptions->tileSize )
+            {
+                printf( "Invalid maximum mipmap specifed, must be n, such that 2^(n-1)+1 < tileSize \n" );
+                return ;
+            }
+        }
+
+        deleteGeometry();
+
+        //calculate min and max heights;
+        Real min = 256000, max = 0;
+
+        mTerrain = new VertexData;
+        mTerrain->vertexStart = 0;
+        mTerrain->vertexCount = mOptions->tileSize * mOptions->tileSize;
+
+        VertexDeclaration* decl = mTerrain->vertexDeclaration;
+        VertexBufferBinding* bind = mTerrain->vertexBufferBinding;
+
+        // positions
+        size_t offset = 0;
+        decl->addElement(MAIN_BINDING, offset, VET_FLOAT3, VES_POSITION);
+        offset += VertexElement::getTypeSize(VET_FLOAT3);
+        if (mOptions->lit)
+        {
+            decl->addElement(MAIN_BINDING, offset, VET_FLOAT3, VES_NORMAL);
+            offset += VertexElement::getTypeSize(VET_FLOAT3);
+        }
+        // texture coord sets
+        decl->addElement(MAIN_BINDING, offset, VET_FLOAT2, VES_TEXTURE_COORDINATES, 0);
+        offset += VertexElement::getTypeSize(VET_FLOAT2);
+        decl->addElement(MAIN_BINDING, offset, VET_FLOAT2, VES_TEXTURE_COORDINATES, 1);
+        offset += VertexElement::getTypeSize(VET_FLOAT2);
+        if (mOptions->coloured)
+        {
+            decl->addElement(MAIN_BINDING, offset, VET_COLOUR, VES_DIFFUSE);
+            offset += VertexElement::getTypeSize(VET_COLOUR);
+        }
+
+        // Create shared vertex buffer
+        mMainBuffer =
+            HardwareBufferManager::getSingleton().createVertexBuffer(
+            decl->getVertexSize(MAIN_BINDING),
+            mTerrain->vertexCount, 
+            HardwareBuffer::HBU_STATIC_WRITE_ONLY);
+        // Create system memory copy with just positions in it, for use in simple reads
+        mPositionBuffer = new float[mTerrain->vertexCount * 3];
+
+        bind->setBinding(MAIN_BINDING, mMainBuffer);
+
+        if (mOptions->lodMorph)
+        {
+            // Create additional element for delta
+            decl->addElement(DELTA_BINDING, 0, VET_FLOAT1, VES_BLEND_WEIGHTS);
+            // NB binding is not set here, it is set when deriving the LOD
+        }
+
+
+        mInit = true;
+
+#ifdef GTP_VISIBILITY_MODIFIED_OGRE
+		for (int i = 0; i < MAX_RENDERLEVEL_INDEX; ++i )
+		{
+			mRenderLevel[i] = 1;
+		}
+#else
+		mRenderLevel = 1;
+#endif // GTP_VISIBILITY_MODIFIED_OGRE
+
+        mMinLevelDistSqr = new Real[ mOptions->maxGeoMipMapLevel ];
+
+        int endx = startx + mOptions->tileSize;
+
+        int endz = startz + mOptions->tileSize;
+
+        Vector3 left, down, here;
+
+        const VertexElement* poselem = decl->findElementBySemantic(VES_POSITION);
+        const VertexElement* texelem0 = decl->findElementBySemantic(VES_TEXTURE_COORDINATES, 0);
+        const VertexElement* texelem1 = decl->findElementBySemantic(VES_TEXTURE_COORDINATES, 1);
+        float* pSysPos = mPositionBuffer;
+
+        unsigned char* pBase = static_cast<unsigned char*>(mMainBuffer->lock(HardwareBuffer::HBL_DISCARD));
+
+        for ( int j = startz; j < endz; j++ )
+        {
+            for ( int i = startx; i < endx; i++ )
+            {
+                float *pPos, *pTex0, *pTex1;
+                poselem->baseVertexPointerToElement(pBase, &pPos);
+                texelem0->baseVertexPointerToElement(pBase, &pTex0);
+                texelem1->baseVertexPointerToElement(pBase, &pTex1);
+    
+                Real height = pageHeightData[j * mOptions->pageSize + i];
+                height = height * mOptions->scale.y; // scale height 
+
+                *pSysPos++ = *pPos++ = ( float ) i * mOptions->scale.x; //x
+                *pSysPos++ = *pPos++ = height; // y
+                *pSysPos++ = *pPos++ = ( float ) j * mOptions->scale.z; //z
+
+                *pTex0++ = ( float ) i / ( float ) ( mOptions->pageSize - 1 );
+                *pTex0++ = ( float ) j / ( float ) ( mOptions->pageSize - 1 );
+
+                *pTex1++ = ( ( float ) i / ( float ) ( mOptions->tileSize - 1 ) ) * mOptions->detailTile;
+                *pTex1++ = ( ( float ) j / ( float ) ( mOptions->tileSize - 1 ) ) * mOptions->detailTile;
+
+                if ( height < min )
+                    min = ( Real ) height;
+
+                if ( height > max )
+                    max = ( Real ) height;
+
+                pBase += mMainBuffer->getVertexSize();
+            }
+        }
+
+        mMainBuffer->unlock();
+
+        mBounds.setExtents( 
+            ( Real ) startx * mOptions->scale.x, 
+            min, 
+            ( Real ) startz * mOptions->scale.z,
+            ( Real ) ( endx - 1 ) * mOptions->scale.x, 
+            max, 
+            ( Real ) ( endz - 1 ) * mOptions->scale.z );
+
+
+        mCenter = Vector3( ( startx * mOptions->scale.x + (endx - 1) * mOptions->scale.x ) / 2,
+            ( min + max ) / 2,
+            ( startz * mOptions->scale.z + (endz - 1) * mOptions->scale.z ) / 2 );
+
+        mBoundingRadius =
+            std::max(max - min,
+                std::max((endx - 1 - startx) * mOptions->scale.x,
+                         (endz - 1 - startz) * mOptions->scale.z)) / 2;
+
+        // Create delta buffer list if required to morph
+        if (mOptions->lodMorph)
+        {
+            // Create delta buffer for all except the lowest mip
+            mDeltaBuffers = new HardwareVertexBufferSharedPtr[mOptions->maxGeoMipMapLevel - 1];
+        }
+
+        Real C = _calculateCFactor();
+
+        _calculateMinLevelDist2( C );
+
+    }
+    //-----------------------------------------------------------------------
+    void BiTerrainRenderable::_getNormalAt( float x, float z, Vector3 * result )
+    {
+
+        assert(mOptions->lit && "No normals present");
+
+        Vector3 here, left, down;
+        here.x = x;
+        here.y = getHeightAt( x, z );
+        here.z = z;
+
+        left.x = x - 1;
+        left.y = getHeightAt( x - 1, z );
+        left.z = z;
+
+        down.x = x;
+        down.y = getHeightAt( x, z + 1 );
+        down.z = z + 1;
+
+        left = left - here;
+
+        down = down - here;
+
+        left.normalise();
+        down.normalise();
+
+        *result = left.crossProduct( down );
+        result -> normalise();
+
+        // result->x = - result->x;
+        // result->y = - result->y;
+        // result->z = - result->z;
+    }
+    //-----------------------------------------------------------------------
+    void BiTerrainRenderable::_calculateNormals()
+    {
+
+        Vector3 norm;
+
+        assert (mOptions->lit && "No normals present");
+
+        HardwareVertexBufferSharedPtr vbuf = 
+            mTerrain->vertexBufferBinding->getBuffer(MAIN_BINDING);
+        const VertexElement* elem = mTerrain->vertexDeclaration->findElementBySemantic(VES_NORMAL);
+        unsigned char* pBase = static_cast<unsigned char*>( vbuf->lock(HardwareBuffer::HBL_DISCARD) );
+        float* pNorm;
+
+        for ( uint j = 0; j < mOptions->tileSize; j++ )
+        {
+            for ( uint i = 0; i < mOptions->tileSize; i++ )
+            {
+
+                _getNormalAt( _vertex( i, j, 0 ), _vertex( i, j, 2 ), &norm );
+
+                //  printf( "Normal = %5f,%5f,%5f\n", norm.x, norm.y, norm.z );
+                elem->baseVertexPointerToElement(pBase, &pNorm);
+                *pNorm++ = norm.x;
+                *pNorm++ = norm.y;
+                *pNorm++ = norm.z;
+                pBase += vbuf->getVertexSize();
+            }
+
+        }
+        vbuf->unlock();
+    }
+    //-----------------------------------------------------------------------
+    void BiTerrainRenderable::_notifyCurrentCamera( Camera* cam )
+    {
+		MovableObject::_notifyCurrentCamera(cam);
+
+        if ( mForcedRenderLevel >= 0 )
+        {
+#ifdef GTP_VISIBILITY_MODIFIED_OGRE
+            mRenderLevel[msCurrentRenderLevelIndex] = mForcedRenderLevel;
+#else
+			mRenderLevel = mForcedRenderLevel;
+#endif // GTP_VISIBILITY_MODIFIED_OGRE
+            return ;
+        }
+
+
+        Vector3 cpos = cam -> getDerivedPosition();
+        const AxisAlignedBox& aabb = getWorldBoundingBox(true);
+        Vector3 diff(0, 0, 0);
+        diff.makeFloor(cpos - aabb.getMinimum());
+        diff.makeCeil(cpos - aabb.getMaximum());
+
+        Real L = diff.squaredLength();
+
+#ifdef GTP_VISIBILITY_MODIFIED_OGRE
+        mRenderLevel[msCurrentRenderLevelIndex] = -1;;
+#else
+		mRenderLevel = -1;
+#endif // GTP_VISIBILITY_MODIFIED_OGRE
+
+
+        for ( uint i = 0; i < mOptions->maxGeoMipMapLevel; i++ )
+        {
+            if ( mMinLevelDistSqr[ i ] > L )
+            {
+#ifdef GTP_VISIBILITY_MODIFIED_OGRE
+                mRenderLevel[msCurrentRenderLevelIndex] = i - 1;
+#else
+				mRenderLevel = i - 1;
+#endif // GTP_VISIBILITY_MODIFIED_OGRE
+                break;
+            }
+        }
+
+#ifdef GTP_VISIBILITY_MODIFIED_OGRE
+        if ( getRenderLevel() < 0 )
+            mRenderLevel[msCurrentRenderLevelIndex] = mOptions->maxGeoMipMapLevel - 1;
+#else
+		if ( mRenderLevel < 0 )
+            mRenderLevel = mOptions->maxGeoMipMapLevel - 1;
+#endif // GTP_VISIBILITY_MODIFIED_OGRE
+
+        if (mOptions->lodMorph)
+        {
+            // Get the next LOD level down
+#ifdef GTP_VISIBILITY_MODIFIED_OGRE
+            int nextLevel = mNextLevelDown[getRenderLevel()];
+#else
+			int nextLevel = mNextLevelDown[mRenderLevel];
+#endif // GTP_VISIBILITY_MODIFIED_OGRE
+            if (nextLevel == 0)
+            {
+                // No next level, so never morph
+                mLODMorphFactor = 0;
+            }
+            else
+            {
+                // Set the morph such that the morph happens in the last 0.25 of
+                // the distance range
+#ifdef GTP_VISIBILITY_MODIFIED_OGRE
+				Real range = mMinLevelDistSqr[nextLevel] - mMinLevelDistSqr[getRenderLevel()];
+#else
+                Real range = mMinLevelDistSqr[nextLevel] - mMinLevelDistSqr[mRenderLevel];
+#endif // GTP_VISIBILITY_MODIFIED_OGRE   
+                if (range)
+                {
+#ifdef GTP_VISIBILITY_MODIFIED_OGRE
+					Real percent = (L - mMinLevelDistSqr[getRenderLevel()]) / range;
+#else
+                    Real percent = (L - mMinLevelDistSqr[mRenderLevel]) / range;
+#endif // GTP_VISIBILITY_MODIFIED_OGRE
+                    // scale result so that msLODMorphStart == 0, 1 == 1, clamp to 0 below that
+                    Real rescale = 1.0f / (1.0f - mOptions->lodMorphStart);
+                    mLODMorphFactor = std::max((percent - mOptions->lodMorphStart) * rescale, 
+						static_cast<Real>(0.0));
+                }
+                else
+                {
+                    // Identical ranges
+                    mLODMorphFactor = 0.0f;
+                }
+
+                assert(mLODMorphFactor >= 0 && mLODMorphFactor <= 1);
+            }
+
+            // Bind the correct delta buffer if it has changed
+            // nextLevel - 1 since the first entry is for LOD 1 (since LOD 0 never needs it)
+            if (mLastNextLevel != nextLevel)
+            {
+                if (nextLevel > 0)
+                {
+                    mTerrain->vertexBufferBinding->setBinding(DELTA_BINDING, 
+                        mDeltaBuffers[nextLevel - 1]);
+                }
+                else
+                {
+                    // bind dummy (incase bindings checked)
+                    mTerrain->vertexBufferBinding->setBinding(DELTA_BINDING, 
+                        mDeltaBuffers[0]);
+                }
+            }
+            mLastNextLevel = nextLevel;
+
+        }
+
+    }
+    //-----------------------------------------------------------------------
+    void BiTerrainRenderable::_updateRenderQueue( RenderQueue* queue )
+    {
+        // Notify need to calculate light list when our sending to render queue
+        mLightListDirty = true;
+
+        queue->addRenderable( this );
+    }
+    //-----------------------------------------------------------------------
+    void BiTerrainRenderable::getRenderOperation( RenderOperation& op )
+    {
+        //setup indexes for vertices and uvs...
+
+        assert( mInit && "Uninitialized" );
+
+        op.useIndexes = true;
+        op.operationType = mOptions->useTriStrips ? 
+            RenderOperation::OT_TRIANGLE_STRIP : RenderOperation::OT_TRIANGLE_LIST;
+        op.vertexData = mTerrain;
+        op.indexData = getIndexData();
+
+
+    }
+    //-----------------------------------------------------------------------
+    void BiTerrainRenderable::getWorldTransforms( Matrix4* xform ) const
+    {
+        *xform = mParentNode->_getFullTransform();
+    }
+    //-----------------------------------------------------------------------
+    const Quaternion& BiTerrainRenderable::getWorldOrientation(void) const
+    {
+        return mParentNode->_getDerivedOrientation();
+    }
+    //-----------------------------------------------------------------------
+    const Vector3& BiTerrainRenderable::getWorldPosition(void) const
+    {
+        return mParentNode->_getDerivedPosition();
+    }
+    //-----------------------------------------------------------------------
+    bool BiTerrainRenderable::_checkSize( int n )
+    {
+        for ( int i = 0; i < 10; i++ )
+        {
+            if ( ( ( 1 << i ) + 1 ) == n )
+                return true;
+        }
+
+        return false;
+    }
+    //-----------------------------------------------------------------------
+    void BiTerrainRenderable::_calculateMinLevelDist2( Real C )
+    {
+        //level 0 has no delta.
+        mMinLevelDistSqr[ 0 ] = 0;
+
+        int i, j;
+
+        for ( int level = 1; level < mOptions->maxGeoMipMapLevel; level++ )
+        {
+            mMinLevelDistSqr[ level ] = 0;
+
+            int step = 1 << level;
+            // The step of the next higher LOD
+            int higherstep = step >> 1;
+
+            float* pDeltas = 0;
+            if (mOptions->lodMorph)
+            {
+                // Create a set of delta values (store at index - 1 since 0 has none)
+                mDeltaBuffers[level - 1]  = createDeltaBuffer();
+                // Lock, but don't discard (we want the pre-initialised zeros)
+                pDeltas = static_cast<float*>(
+                    mDeltaBuffers[level - 1]->lock(HardwareBuffer::HBL_NORMAL));
+            }
+
+            for ( j = 0; j < mOptions->tileSize - step; j += step )
+            {
+                for ( i = 0; i < mOptions->tileSize - step; i += step )
+                {
+                    /* Form planes relating to the lower detail tris to be produced
+                    For tri lists and even tri strip rows, they are this shape:
+                    x---x
+                    | / |
+                    x---x
+                    For odd tri strip rows, they are this shape:
+                    x---x
+                    | \ |
+                    x---x
+                    */
+
+                    Vector3 v1(_vertex( i, j, 0 ), _vertex( i, j, 1 ), _vertex( i, j, 2 ));
+                    Vector3 v2(_vertex( i + step, j, 0 ), _vertex( i + step, j, 1 ), _vertex( i + step, j, 2 ));
+                    Vector3 v3(_vertex( i, j + step, 0 ), _vertex( i, j + step, 1 ), _vertex( i, j + step, 2 ));
+                    Vector3 v4(_vertex( i + step, j + step, 0 ), _vertex( i + step, j + step, 1 ), _vertex( i + step, j + step, 2 ));
+
+                    Plane t1, t2;
+                    bool backwardTri = false;
+                    if (!mOptions->useTriStrips || j % 2 == 0)
+                    {
+                        t1.redefine(v1, v3, v2);
+                        t2.redefine(v2, v3, v4);
+                    }
+                    else
+                    {
+                        t1.redefine(v1, v3, v4);
+                        t2.redefine(v1, v4, v2);
+                        backwardTri = true;
+                    }
+
+                    // include the bottommost row of vertices if this is the last row
+                    int zubound = (j == (mOptions->tileSize - step)? step : step - 1);
+                    for ( int z = 0; z <= zubound; z++ )
+                    {
+                        // include the rightmost col of vertices if this is the last col
+                        int xubound = (i == (mOptions->tileSize - step)? step : step - 1);
+                        for ( int x = 0; x <= xubound; x++ )
+                        {
+                            int fulldetailx = i + x;
+                            int fulldetailz = j + z;
+                            if ( fulldetailx % step == 0 && 
+                                fulldetailz % step == 0 )
+                            {
+                                // Skip, this one is a vertex at this level
+                                continue;
+                            }
+
+                            Real zpct = (Real)z / (Real)step;
+                            Real xpct = (Real)x / (Real)step;
+
+                            //interpolated height
+                            Vector3 actualPos(
+                                _vertex( fulldetailx, fulldetailz, 0 ), 
+                                _vertex( fulldetailx, fulldetailz, 1 ), 
+                                _vertex( fulldetailx, fulldetailz, 2 ));
+                            Real interp_h;
+                            // Determine which tri we're on 
+                            if ((xpct + zpct <= 1.0f && !backwardTri) ||
+                                (xpct + (1-zpct) <= 1.0f && backwardTri))
+                            {
+                                // Solve for x/z
+                                interp_h = 
+                                    (-(t1.normal.x * actualPos.x)
+                                    - t1.normal.z * actualPos.z
+                                    - t1.d) / t1.normal.y;
+                            }
+                            else
+                            {
+                                // Second tri
+                                interp_h = 
+                                    (-(t2.normal.x * actualPos.x)
+                                    - t2.normal.z * actualPos.z
+                                    - t2.d) / t2.normal.y;
+                            }
+
+                            Real actual_h = _vertex( fulldetailx, fulldetailz, 1 );
+                            Real delta = fabs( interp_h - actual_h );
+
+                            Real D2 = delta * delta * C * C;
+
+                            if ( mMinLevelDistSqr[ level ] < D2 )
+                                mMinLevelDistSqr[ level ] = D2;
+
+                            // Should be save height difference?
+                            // Don't morph along edges
+                            if (mOptions->lodMorph && 
+                                fulldetailx != 0  && fulldetailx != (mOptions->tileSize - 1) && 
+                                fulldetailz != 0  && fulldetailz != (mOptions->tileSize - 1) )
+                            {
+                                // Save height difference 
+                                pDeltas[fulldetailx + (fulldetailz * mOptions->tileSize)] = 
+                                    interp_h - actual_h;
+                            }
+
+                        }
+
+                    }
+                }
+            }
+
+            // Unlock morph deltas if required
+            if (mOptions->lodMorph)
+            {
+                mDeltaBuffers[level - 1]->unlock();
+            }
+        }
+
+
+
+        // Post validate the whole set
+        for ( i = 1; i < mOptions->maxGeoMipMapLevel; i++ )
+        {
+
+            // Make sure no LOD transition within the tile
+            // This is especially a problem when using large tiles with flat areas
+            /* Hmm, this can look bad on some areas, disable for now
+            Vector3 delta(_vertex(0,0,0), mCenter.y, _vertex(0,0,2));
+            delta = delta - mCenter;
+            Real minDist = delta.squaredLength();
+            mMinLevelDistSqr[ i ] = std::max(mMinLevelDistSqr[ i ], minDist);
+            */
+
+            //make sure the levels are increasing...
+            if ( mMinLevelDistSqr[ i ] < mMinLevelDistSqr[ i - 1 ] )
+            {
+                mMinLevelDistSqr[ i ] = mMinLevelDistSqr[ i - 1 ];
+            }
+        }
+
+        // Now reverse traverse the list setting the 'next level down'
+        Real lastDist = -1;
+        int lastIndex = 0;
+        for (i = mOptions->maxGeoMipMapLevel - 1; i >= 0; --i)
+        {
+            if (i == mOptions->maxGeoMipMapLevel - 1)
+            {
+                // Last one is always 0
+                lastIndex = i;
+                lastDist = mMinLevelDistSqr[i];
+                mNextLevelDown[i] = 0;
+            }
+            else
+            {
+                mNextLevelDown[i] = lastIndex;
+                if (mMinLevelDistSqr[i] != lastDist)
+                {
+                    lastIndex = i;
+                    lastDist = mMinLevelDistSqr[i];
+                }
+            }
+
+        }
+
+
+    }
+    //-----------------------------------------------------------------------
+    void BiTerrainRenderable::_adjustRenderLevel( int i )
+    {
+
+#ifdef GTP_VISIBILITY_MODIFIED_OGRE
+        mRenderLevel[msCurrentRenderLevelIndex] = i;
+#else
+		mRenderLevel = i;
+#endif // GTP_VISIBILITY_MODIFIED_OGRE
+    }
+    //-----------------------------------------------------------------------
+    Real BiTerrainRenderable::_calculateCFactor()
+    {
+        Real A, T;
+
+        if (!mOptions->primaryCamera)
+        {
+            OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, 
+                "You have not created a camera yet!", 
+                "BiTerrainRenderable::_calculateCFactor");
+        }
+
+        //A = 1 / Math::Tan(Math::AngleUnitsToRadians(opts.primaryCamera->getFOVy()));
+        // Turn off detail compression at higher FOVs
+        A = 1.0f;
+
+        int vertRes = mOptions->primaryCamera->getViewport()->getActualHeight();
+
+        T = 2 * ( Real ) mOptions->maxPixelError / ( Real ) vertRes;
+
+        return A / T;
+    }
+    //-----------------------------------------------------------------------
+    float BiTerrainRenderable::getHeightAt( float x, float z )
+    {
+        Vector3 start;
+        Vector3 end;
+
+        start.x = _vertex( 0, 0, 0 );
+        start.y = _vertex( 0, 0, 1 );
+        start.z = _vertex( 0, 0, 2 );
+
+        end.x = _vertex( mOptions->tileSize - 1, mOptions->tileSize - 1, 0 );
+        end.y = _vertex( mOptions->tileSize - 1, mOptions->tileSize - 1, 1 );
+        end.z = _vertex( mOptions->tileSize - 1, mOptions->tileSize - 1, 2 );
+
+        /* Safety catch, if the point asked for is outside
+        * of this tile, it will ask the appropriate tile
+        */
+
+        if ( x < start.x )
+        {
+            if ( mNeighbors[ WEST ] != 0 )
+                return mNeighbors[ WEST ] ->getHeightAt( x, z );
+            else
+                x = start.x;
+        }
+
+        if ( x > end.x )
+        {
+            if ( mNeighbors[ EAST ] != 0 )
+                return mNeighbors[ EAST ] ->getHeightAt( x, z );
+            else
+                x = end.x;
+        }
+
+        if ( z < start.z )
+        {
+            if ( mNeighbors[ NORTH ] != 0 )
+                return mNeighbors[ NORTH ] ->getHeightAt( x, z );
+            else
+                z = start.z;
+        }
+
+        if ( z > end.z )
+        {
+            if ( mNeighbors[ SOUTH ] != 0 )
+                return mNeighbors[ SOUTH ] ->getHeightAt( x, z );
+            else
+                z = end.z;
+        }
+
+
+
+        float x_pct = ( x - start.x ) / ( end.x - start.x );
+        float z_pct = ( z - start.z ) / ( end.z - start.z );
+
+        float x_pt = x_pct * ( float ) ( mOptions->tileSize - 1 );
+        float z_pt = z_pct * ( float ) ( mOptions->tileSize - 1 );
+
+        int x_index = ( int ) x_pt;
+        int z_index = ( int ) z_pt;
+
+        // If we got to the far right / bottom edge, move one back
+        if (x_index == mOptions->tileSize - 1)
+        {
+            --x_index;
+            x_pct = 1.0f;
+        }
+        else
+        {
+            // get remainder
+            x_pct = x_pt - x_index;
+        }
+        if (z_index == mOptions->tileSize - 1)
+        {
+            --z_index;
+            z_pct = 1.0f;
+        }
+        else
+        {
+            z_pct = z_pt - z_index;
+        }
+
+        //bilinear interpolate to find the height.
+
+        float t1 = _vertex( x_index, z_index, 1 );
+        float t2 = _vertex( x_index + 1, z_index, 1 );
+        float b1 = _vertex( x_index, z_index + 1, 1 );
+        float b2 = _vertex( x_index + 1, z_index + 1, 1 );
+
+        float midpoint = (b1 + t2) / 2.0;
+
+        if (x_pct + z_pct <= 1) {
+            b2 = midpoint + (midpoint - t1);
+        } else {
+            t1 = midpoint + (midpoint - b2);
+        }
+
+        float t = ( t1 * ( 1 - x_pct ) ) + ( t2 * ( x_pct ) );
+        float b = ( b1 * ( 1 - x_pct ) ) + ( b2 * ( x_pct ) );
+
+        float h = ( t * ( 1 - z_pct ) ) + ( b * ( z_pct ) );
+
+        return h;
+    }
+    //-----------------------------------------------------------------------
+    bool BiTerrainRenderable::intersectSegment( const Vector3 & start, const Vector3 & end, Vector3 * result )
+    {
+        Vector3 dir = end - start;
+        Vector3 ray = start;
+
+        //special case...
+        if ( dir.x == 0 && dir.z == 0 )
+        {
+            if ( ray.y <= getHeightAt( ray.x, ray.z ) )
+            {
+                if ( result != 0 )
+                    * result = start;
+
+                return true;
+            }
+        }
+
+        dir.normalise();
+
+        //dir.x *= mScale.x;
+        //dir.y *= mScale.y;
+        //dir.z *= mScale.z;
+
+        const Vector3 * corners = getBoundingBox().getAllCorners();
+
+        //start with the next one...
+        ray += dir;
+
+
+        while ( ! ( ( ray.x < corners[ 0 ].x ) ||
+            ( ray.x > corners[ 4 ].x ) ||
+            ( ray.z < corners[ 0 ].z ) ||
+            ( ray.z > corners[ 4 ].z ) ) )
+        {
+
+
+            float h = getHeightAt( ray.x, ray.z );
+
+            if ( ray.y <= h )
+            {
+                if ( result != 0 )
+                    * result = ray;
+
+                return true;
+            }
+
+            else
+            {
+                ray += dir;
+            }
+
+        }
+
+        if ( ray.x < corners[ 0 ].x && mNeighbors[ WEST ] != 0 )
+            return mNeighbors[ WEST ] ->intersectSegment( ray, end, result );
+        else if ( ray.z < corners[ 0 ].z && mNeighbors[ NORTH ] != 0 )
+            return mNeighbors[ NORTH ] ->intersectSegment( ray, end, result );
+        else if ( ray.x > corners[ 4 ].x && mNeighbors[ EAST ] != 0 )
+            return mNeighbors[ EAST ] ->intersectSegment( ray, end, result );
+        else if ( ray.z > corners[ 4 ].z && mNeighbors[ SOUTH ] != 0 )
+            return mNeighbors[ SOUTH ] ->intersectSegment( ray, end, result );
+        else
+        {
+            if ( result != 0 )
+                * result = Vector3( -1, -1, -1 );
+
+            return false;
+        }
+    }
+    //-----------------------------------------------------------------------
+    void BiTerrainRenderable::_generateVertexLighting( const Vector3 &sun, ColourValue ambient )
+    {
+
+        Vector3 pt;
+        Vector3 normal;
+        Vector3 light;
+
+        HardwareVertexBufferSharedPtr vbuf = 
+            mTerrain->vertexBufferBinding->getBuffer(MAIN_BINDING);
+        const VertexElement* elem = mTerrain->vertexDeclaration->findElementBySemantic(VES_DIFFUSE);
+        //for each point in the terrain, see if it's in the line of sight for the sun.
+        for ( size_t i = 0; i < mOptions->tileSize; i++ )
+        {
+            for ( size_t j = 0; j < mOptions->tileSize; j++ )
+            {
+                //  printf( "Checking %f,%f,%f ", pt.x, pt.y, pt.z );
+                pt.x = _vertex( i, j, 0 );
+                pt.y = _vertex( i, j, 1 );
+                pt.z = _vertex( i, j, 2 );
+
+                light = sun - pt;
+
+                light.normalise();
+
+                if ( ! intersectSegment( pt, sun, 0 ) )
+                {
+                    //
+                    _getNormalAt( _vertex( i, j, 0 ), _vertex( i, j, 2 ), &normal );
+
+                    float l = light.dotProduct( normal );
+
+                    ColourValue v;
+                    v.r = ambient.r + l;
+                    v.g = ambient.g + l;
+                    v.b = ambient.b + l;
+
+                    if ( v.r > 1 ) v.r = 1;
+
+                    if ( v.g > 1 ) v.g = 1;
+
+                    if ( v.b > 1 ) v.b = 1;
+
+                    if ( v.r < 0 ) v.r = 0;
+
+                    if ( v.g < 0 ) v.g = 0;
+
+                    if ( v.b < 0 ) v.b = 0;
+
+                    RGBA colour;
+                    Root::getSingleton().convertColourValue( v, &colour );
+                    vbuf->writeData(
+                        (_index( i, j ) * vbuf->getVertexSize()) + elem->getOffset(),
+                        sizeof(RGBA), &colour);
+                }
+
+                else
+                {
+                    RGBA colour;
+                    Root::getSingleton().convertColourValue( ambient, &colour );
+
+                    vbuf->writeData(
+                        (_index( i, j ) * vbuf->getVertexSize()) + elem->getOffset(), 
+                        sizeof(RGBA), &colour);
+                }
+
+            }
+
+        }
+
+        printf( "." );
+    }
+    //-----------------------------------------------------------------------
+    Real BiTerrainRenderable::getSquaredViewDepth(const Camera* cam) const
+    {
+        Vector3 diff = mCenter - cam->getDerivedPosition();
+        // Use squared length to avoid square root
+        return diff.squaredLength();
+    }
+
+    //-----------------------------------------------------------------------
+    const LightList& BiTerrainRenderable::getLights(void) const
+    {
+        if (mLightListDirty)
+        {
+            getParentSceneNode()->getCreator()->_populateLightList(
+                mCenter, this->getBoundingRadius(), mLightList);
+            mLightListDirty = false;
+        }
+        return mLightList;
+    }
+    //-----------------------------------------------------------------------
+    IndexData* BiTerrainRenderable::getIndexData(void)
+    {
+        unsigned int stitchFlags = 0;
+#ifdef GTP_VISIBILITY_MODIFIED_OGRE
+        if ( mNeighbors[ EAST ] != 0 && mNeighbors[ EAST ] -> getRenderLevel() > getRenderLevel() )
+        {
+            stitchFlags |= STITCH_EAST;
+            stitchFlags |= 
+                (mNeighbors[ EAST ] -> getRenderLevel() - getRenderLevel()) << STITCH_EAST_SHIFT;
+        }
+
+        if ( mNeighbors[ WEST ] != 0 && mNeighbors[ WEST ] -> getRenderLevel() > getRenderLevel() )
+        {
+            stitchFlags |= STITCH_WEST;
+            stitchFlags |= 
+                (mNeighbors[ WEST ] -> getRenderLevel() - getRenderLevel()) << STITCH_WEST_SHIFT;
+        }
+
+        if ( mNeighbors[ NORTH ] != 0 && mNeighbors[ NORTH ] -> getRenderLevel() > getRenderLevel() )
+        {
+            stitchFlags |= STITCH_NORTH;
+            stitchFlags |= 
+                (mNeighbors[ NORTH ] -> getRenderLevel() - getRenderLevel()) << STITCH_NORTH_SHIFT;
+        }
+
+        if ( mNeighbors[ SOUTH ] != 0 && mNeighbors[ SOUTH ] -> getRenderLevel() > getRenderLevel() )
+        {
+            stitchFlags |= STITCH_SOUTH;
+            stitchFlags |= 
+                (mNeighbors[ SOUTH ] -> getRenderLevel() - getRenderLevel()) << STITCH_SOUTH_SHIFT;
+        }
+#else
+         if ( mNeighbors[ EAST ] != 0 && mNeighbors[ EAST ] -> mRenderLevel > mRenderLevel )
+        {
+            stitchFlags |= STITCH_EAST;
+            stitchFlags |= 
+                (mNeighbors[ EAST ] -> mRenderLevel - mRenderLevel) << STITCH_EAST_SHIFT;
+        }
+
+        if ( mNeighbors[ WEST ] != 0 && mNeighbors[ WEST ] -> mRenderLevel > mRenderLevel )
+        {
+            stitchFlags |= STITCH_WEST;
+            stitchFlags |= 
+                (mNeighbors[ WEST ] -> mRenderLevel - mRenderLevel) << STITCH_WEST_SHIFT;
+        }
+
+        if ( mNeighbors[ NORTH ] != 0 && mNeighbors[ NORTH ] -> mRenderLevel > mRenderLevel )
+        {
+            stitchFlags |= STITCH_NORTH;
+            stitchFlags |= 
+                (mNeighbors[ NORTH ] -> mRenderLevel - mRenderLevel) << STITCH_NORTH_SHIFT;
+        }
+
+        if ( mNeighbors[ SOUTH ] != 0 && mNeighbors[ SOUTH ] -> mRenderLevel > mRenderLevel )
+        {
+            stitchFlags |= STITCH_SOUTH;
+            stitchFlags |= 
+                (mNeighbors[ SOUTH ] -> mRenderLevel - mRenderLevel) << STITCH_SOUTH_SHIFT;
+        }
+#endif // GTP_VISIBILITY_MODIFIED_OGRE
+        // Check preexisting
+		LevelArray& levelIndex = mSceneManager->_getLevelIndex();
+/*** msz: added for v1-2-0 compatibility ***/
+#ifdef GTP_VISIBILITY_MODIFIED_OGRE
+		IndexMap::iterator ii = levelIndex[ getRenderLevel() ]->find( stitchFlags );
+#else
+        IndexMap::iterator ii = levelIndex[ mRenderLevel ]->find( stitchFlags );
+#endif // GTP_VISIBILITY_MODIFIED_OGRE
+        IndexData* indexData;
+/*** msz: added for v1-2-0 compatibility ***/
+#ifdef GTP_VISIBILITY_MODIFIED_OGRE
+		if ( ii == levelIndex[ getRenderLevel() ]->end())
+#else
+		if ( ii == levelIndex[ mRenderLevel ]->end())
+#endif // GTP_VISIBILITY_MODIFIED_OGRE
+        {
+            // Create
+            if (mOptions->useTriStrips)
+            {
+                indexData = generateTriStripIndexes(stitchFlags);
+            }
+            else
+            {
+                indexData = generateTriListIndexes(stitchFlags);
+            }
+/*** msz: added for v1-2-0 compatibility ***/
+#ifdef GTP_VISIBILITY_MODIFIED_OGRE
+            levelIndex[ getRenderLevel() ]->insert(
+                IndexMap::value_type(stitchFlags, indexData));
+#else
+            levelIndex[ mRenderLevel ]->insert(
+                IndexMap::value_type(stitchFlags, indexData));
+#endif // GTP_VISIBILITY_MODIFIED_OGRE
+        }
+        else
+        {
+            indexData = ii->second;
+        }
+
+
+        return indexData;
+
+
+    }
+    //-----------------------------------------------------------------------
+    IndexData* BiTerrainRenderable::generateTriStripIndexes(unsigned int stitchFlags)
+    {
+/*** msz: added for v1-2-0 compatibility ***/
+#ifdef GTP_VISIBILITY_MODIFIED_OGRE
+        // The step used for the current level
+        int step = 1 << getRenderLevel();
+        // The step used for the lower level
+        int lowstep = 1 << (getRenderLevel() + 1);
+#else
+        // The step used for the current level
+        int step = 1 << mRenderLevel;
+        // The step used for the lower level
+        int lowstep = 1 << (mRenderLevel + 1);
+#endif // GTP_VISIBILITY_MODIFIED_OGRE
+
+        int numIndexes = 0;
+
+        // Calculate the number of indexes required
+        // This is the number of 'cells' at this detail level x 2
+        // plus 3 degenerates to turn corners
+        int numTrisAcross = (((mOptions->tileSize-1) / step) * 2) + 3;
+        // Num indexes is number of tris + 2
+        int new_length = numTrisAcross * ((mOptions->tileSize-1) / step) + 2;
+        //this is the maximum for a level.  It wastes a little, but shouldn't be a problem.
+
+        IndexData* indexData = new IndexData;
+        indexData->indexBuffer = 
+            HardwareBufferManager::getSingleton().createIndexBuffer(
+            HardwareIndexBuffer::IT_16BIT,
+            new_length, HardwareBuffer::HBU_STATIC_WRITE_ONLY);//, false);
+
+        mSceneManager->_getIndexCache().mCache.push_back( indexData );
+
+        unsigned short* pIdx = static_cast<unsigned short*>(
+            indexData->indexBuffer->lock(0, 
+            indexData->indexBuffer->getSizeInBytes(), 
+            HardwareBuffer::HBL_DISCARD));
+
+        // Stripified mesh
+        for ( uint j = 0; j < mOptions->tileSize - 1; j += step )
+        {
+            uint i;
+            // Forward strip
+            // We just do the |/ here, final | done after
+            for ( i = 0; i < mOptions->tileSize - 1; i += step )
+            {
+                int x[4], y[4];
+                x[0] = x[1] = i;
+                x[2] = x[3] = i + step;
+                y[0] = y[2] = j;
+                y[1] = y[3] = j + step;
+
+                if (j == 0  && (stitchFlags & STITCH_NORTH))
+                {
+                    // North reduction means rounding x[0] and x[2]
+                    if (x[0] % lowstep != 0)
+                    {
+                        // Since we know we only drop down one level of LOD,
+                        // removing 1 step of higher LOD should return to lower
+                        x[0] -= step;
+                    }
+                    if (x[2] % lowstep != 0)
+                    {
+                        x[2] -= step;
+                    }
+                }
+
+                // Never get a south tiling on a forward strip (always finish on 
+                // a backward strip)
+
+                if (i == 0  && (stitchFlags & STITCH_WEST))
+                {
+                    // West reduction means rounding y[0] / y[1]
+                    if (y[0] % lowstep != 0)
+                    {
+                        y[0] -= step;
+                    }
+                    if (y[1] % lowstep != 0)
+                    {
+                        y[1] -= step;
+                    }
+                }
+                if (i == (mOptions->tileSize - 1 - step) && (stitchFlags & STITCH_EAST))
+                {
+                    // East tiling means rounding y[2] & y[3]
+                    if (y[2] % lowstep != 0)
+                    {
+                        y[2] -= step;
+                    }
+                    if (y[3] % lowstep != 0)
+                    {
+                        y[3] -= step;
+                    }
+                }
+
+                //triangles
+                if (i == 0)
+                {
+                    // Starter
+                    *pIdx++ = _index( x[0], y[0] ); numIndexes++;
+                }
+                *pIdx++ = _index( x[1], y[1] ); numIndexes++;
+                *pIdx++ = _index( x[2], y[2] ); numIndexes++;
+
+                if (i == mOptions->tileSize - 1 - step)
+                {
+                    // Emit extra index to finish row
+                    *pIdx++ = _index( x[3], y[3] ); numIndexes++;
+                    if (j < mOptions->tileSize - 1 - step)
+                    {
+                        // Emit this index twice more (this is to turn around without
+                        // artefacts)
+                        // ** Hmm, looks like we can drop this and it's unnoticeable
+                        //*pIdx++ = _index( x[3], y[3] ); numIndexes++;
+                        //*pIdx++ = _index( x[3], y[3] ); numIndexes++;
+                    }
+                }
+
+            }
+            // Increment row
+            j += step;
+            // Backward strip
+            for ( i = mOptions->tileSize - 1; i > 0 ; i -= step )
+            {
+                int x[4], y[4];
+                x[0] = x[1] = i;
+                x[2] = x[3] = i - step;
+                y[0] = y[2] = j;
+                y[1] = y[3] = j + step;
+
+                // Never get a north tiling on a backward strip (always
+                // start on a forward strip)
+                if (j == (mOptions->tileSize - 1 - step) && (stitchFlags & STITCH_SOUTH))
+                {
+                    // South reduction means rounding x[1] / x[3]
+                    if (x[1] % lowstep != 0)
+                    {
+                        x[1] -= step;
+                    }
+                    if (x[3] % lowstep != 0)
+                    {
+                        x[3] -= step;
+                    }
+                }
+
+                if (i == step  && (stitchFlags & STITCH_WEST))
+                {
+                    // West tiling on backward strip is rounding of y[2] / y[3]
+                    if (y[2] % lowstep != 0)
+                    {
+                        y[2] -= step;
+                    }
+                    if (y[3] % lowstep != 0)
+                    {
+                        y[3] -= step;
+                    }
+                }
+                if (i == mOptions->tileSize - 1 && (stitchFlags & STITCH_EAST))
+                {
+                    // East tiling means rounding y[0] and y[1] on backward strip
+                    if (y[0] % lowstep != 0)
+                    {
+                        y[0] -= step;
+                    }
+                    if (y[1] % lowstep != 0)
+                    {
+                        y[1] -= step;
+                    }
+                }
+
+                //triangles
+                if (i == mOptions->tileSize)
+                {
+                    // Starter
+                    *pIdx++ = _index( x[0], y[0] ); numIndexes++;
+                }
+                *pIdx++ = _index( x[1], y[1] ); numIndexes++;
+                *pIdx++ = _index( x[2], y[2] ); numIndexes++;
+
+                if (i == step)
+                {
+                    // Emit extra index to finish row
+                    *pIdx++ = _index( x[3], y[3] ); numIndexes++;
+                    if (j < mOptions->tileSize - 1 - step)
+                    {
+                        // Emit this index once more (this is to turn around)
+                        *pIdx++ = _index( x[3], y[3] ); numIndexes++;
+                    }
+                }
+            }
+        }
+
+
+        indexData->indexBuffer->unlock();
+        indexData->indexCount = numIndexes;
+        indexData->indexStart = 0;
+
+        return indexData;
+
+    }
+    //-----------------------------------------------------------------------
+    IndexData* BiTerrainRenderable::generateTriListIndexes(unsigned int stitchFlags)
+    {
+
+        int numIndexes = 0;
+/*** msz: added for v1-2-0 compatibility ***/
+#ifdef GTP_VISIBILITY_MODIFIED_OGRE
+		int step = 1 << getRenderLevel();
+#else
+        int step = 1 << mRenderLevel;
+#endif // GTP_VISIBILITY_MODIFIED_OGRE
+        IndexData* indexData = 0;
+
+        int north = stitchFlags & STITCH_NORTH ? step : 0;
+        int south = stitchFlags & STITCH_SOUTH ? step : 0;
+        int east = stitchFlags & STITCH_EAST ? step : 0;
+        int west = stitchFlags & STITCH_WEST ? step : 0;
+
+        int new_length = ( mOptions->tileSize / step ) * ( mOptions->tileSize / step ) * 2 * 2 * 2 ;
+        //this is the maximum for a level.  It wastes a little, but shouldn't be a problem.
+
+        indexData = new IndexData;
+        indexData->indexBuffer = 
+            HardwareBufferManager::getSingleton().createIndexBuffer(
+            HardwareIndexBuffer::IT_16BIT,
+            new_length, HardwareBuffer::HBU_STATIC_WRITE_ONLY);//, false);
+
+        mSceneManager->_getIndexCache().mCache.push_back( indexData );
+
+        unsigned short* pIdx = static_cast<unsigned short*>(
+            indexData->indexBuffer->lock(0, 
+            indexData->indexBuffer->getSizeInBytes(), 
+            HardwareBuffer::HBL_DISCARD));
+
+        // Do the core vertices, minus stitches
+        for ( uint j = north; j < mOptions->tileSize - 1 - south; j += step )
+        {
+            for ( uint i = west; i < mOptions->tileSize - 1 - east; i += step )
+            {
+                //triangles
+                *pIdx++ = _index( i, j ); numIndexes++;
+                *pIdx++ = _index( i, j + step ); numIndexes++;
+                *pIdx++ = _index( i + step, j ); numIndexes++;
+
+                *pIdx++ = _index( i, j + step ); numIndexes++;
+                *pIdx++ = _index( i + step, j + step ); numIndexes++;
+                *pIdx++ = _index( i + step, j ); numIndexes++;
+            }
+        }
+
+        // North stitching
+        if ( north > 0 )
+        {
+/*** msz: added for v1-2-0 compatibility ***/
+#ifdef GTP_VISIBILITY_MODIFIED_OGRE
+            numIndexes += stitchEdge(NORTH, getRenderLevel(), mNeighbors[NORTH]->getRenderLevel(),
+                west > 0, east > 0, &pIdx);
+#else
+			numIndexes += stitchEdge(NORTH, mRenderLevel, mNeighbors[NORTH]->mRenderLevel,
+                west > 0, east > 0, &pIdx);
+#endif // GTP_VISIBILITY_MODIFIED_OGRE
+        }
+        // East stitching
+        if ( east > 0 )
+        {
+/*** msz: added for v1-2-0 compatibility ***/
+#ifdef GTP_VISIBILITY_MODIFIED_OGRE
+            numIndexes += stitchEdge(EAST, getRenderLevel(), mNeighbors[EAST]->getRenderLevel(),
+                north > 0, south > 0, &pIdx);
+#else
+			numIndexes += stitchEdge(EAST, mRenderLevel, mNeighbors[EAST]->mRenderLevel,
+                north > 0, south > 0, &pIdx);
+#endif // GTP_VISIBILITY_MODIFIED_OGRE
+        }
+        // South stitching
+        if ( south > 0 )
+        {
+/*** msz: added for v1-2-0 compatibility ***/
+#ifdef GTP_VISIBILITY_MODIFIED_OGRE
+            numIndexes += stitchEdge(SOUTH, getRenderLevel(), mNeighbors[SOUTH]->getRenderLevel(),
+                east > 0, west > 0, &pIdx);
+#else
+			numIndexes += stitchEdge(SOUTH, mRenderLevel, mNeighbors[SOUTH]->mRenderLevel,
+                east > 0, west > 0, &pIdx);
+#endif // GTP_VISIBILITY_MODIFIED_OGRE
+		}
+        // West stitching
+        if ( west > 0 )
+        {
+/*** msz: added for v1-2-0 compatibility ***/
+#ifdef GTP_VISIBILITY_MODIFIED_OGRE
+            numIndexes += stitchEdge(WEST, getRenderLevel(), mNeighbors[WEST]->getRenderLevel(),
+                south > 0, north > 0, &pIdx);
+#else
+			numIndexes += stitchEdge(WEST, mRenderLevel, mNeighbors[WEST]->mRenderLevel,
+                south > 0, north > 0, &pIdx);
+#endif // GTP_VISIBILITY_MODIFIED_OGRE
+        }
+
+
+        indexData->indexBuffer->unlock();
+        indexData->indexCount = numIndexes;
+        indexData->indexStart = 0;
+
+        return indexData;
+    }
+    //-----------------------------------------------------------------------
+    HardwareVertexBufferSharedPtr BiTerrainRenderable::createDeltaBuffer(void)
+    {
+        // Delta buffer is a 1D float buffer of height offsets
+        HardwareVertexBufferSharedPtr buf = 
+            HardwareBufferManager::getSingleton().createVertexBuffer(
+            VertexElement::getTypeSize(VET_FLOAT1), 
+            mOptions->tileSize * mOptions->tileSize,
+            HardwareBuffer::HBU_STATIC_WRITE_ONLY);
+        // Fill the buffer with zeros, we will only fill in delta
+        void* pVoid = buf->lock(HardwareBuffer::HBL_DISCARD);
+        memset(pVoid, 0, mOptions->tileSize * mOptions->tileSize * sizeof(float));
+        buf->unlock();
+
+        return buf;
+
+    }
+    //-----------------------------------------------------------------------
+    void BiTerrainRenderable::_updateCustomGpuParameter(
+        const GpuProgramParameters::AutoConstantEntry& constantEntry, 
+        GpuProgramParameters* params) const
+    {
+        if (constantEntry.data == MORPH_CUSTOM_PARAM_ID)
+        {
+            // Update morph LOD factor
+            params->setConstant(constantEntry.index, mLODMorphFactor);
+        }
+        else
+        {
+            Renderable::_updateCustomGpuParameter(constantEntry, params);
+        }
+
+    }
+    //-----------------------------------------------------------------------
+    int BiTerrainRenderable::stitchEdge(Neighbor neighbor, int hiLOD, int loLOD, 
+        bool omitFirstTri, bool omitLastTri, unsigned short** ppIdx)
+    {
+        assert(loLOD > hiLOD);
+        /* 
+        Now do the stitching; we can stitch from any level to any level.
+        The stitch pattern is like this for each pair of vertices in the lower LOD
+        (excuse the poor ascii art):
+
+        lower LOD
+        *-----------*
+        |\  \ 3 /  /|
+        |1\2 \ / 4/5|
+        *--*--*--*--*
+        higher LOD
+
+        The algorithm is, for each pair of lower LOD vertices:
+        1. Iterate over the higher LOD vertices, generating tris connected to the 
+        first lower LOD vertex, up to and including 1/2 the span of the lower LOD 
+        over the higher LOD (tris 1-2). Skip the first tri if it is on the edge 
+        of the tile and that edge is to be stitched itself.
+        2. Generate a single tri for the middle using the 2 lower LOD vertices and 
+        the middle vertex of the higher LOD (tri 3). 
+        3. Iterate over the higher LOD vertices from 1/2 the span of the lower LOD
+        to the end, generating tris connected to the second lower LOD vertex 
+        (tris 4-5). Skip the last tri if it is on the edge of a tile and that
+        edge is to be stitched itself.
+
+        The same algorithm works for all edges of the patch; stitching is done
+        clockwise so that the origin and steps used change, but the general
+        approach does not.
+        */
+
+        // Get pointer to be updated
+        unsigned short* pIdx = *ppIdx;
+
+        // Work out the steps ie how to increment indexes
+        // Step from one vertex to another in the high detail version
+        int step = 1 << hiLOD;
+        // Step from one vertex to another in the low detail version
+        int superstep = 1 << loLOD;
+        // Step half way between low detail steps
+        int halfsuperstep = superstep >> 1;
+
+        // Work out the starting points and sign of increments
+        // We always work the strip clockwise
+        int startx, starty, endx, rowstep;
+        bool horizontal;
+        switch(neighbor)
+        {
+        case NORTH:
+            startx = starty = 0;
+            endx = mOptions->tileSize - 1;
+            rowstep = step;
+            horizontal = true;
+            break;
+        case SOUTH:
+            // invert x AND y direction, helps to keep same winding
+            startx = starty = mOptions->tileSize - 1;
+            endx = 0;
+            rowstep = -step;
+            step = -step;
+            superstep = -superstep;
+            halfsuperstep = -halfsuperstep;
+            horizontal = true;
+            break;
+        case EAST:
+            startx = 0;
+            endx = mOptions->tileSize - 1;
+            starty = mOptions->tileSize - 1;
+            rowstep = -step;
+            horizontal = false;
+            break;
+        case WEST:
+            startx = mOptions->tileSize - 1;
+            endx = 0;
+            starty = 0;
+            rowstep = step;
+            step = -step;
+            superstep = -superstep;
+            halfsuperstep = -halfsuperstep;
+            horizontal = false;
+            break;
+        };
+
+        int numIndexes = 0;
+
+        for ( int j = startx; j != endx; j += superstep )
+        {
+            int k;
+            for (k = 0; k != halfsuperstep; k += step)
+            {
+                int jk = j + k;
+                //skip the first bit of the corner?
+                if ( j != startx || k != 0 || !omitFirstTri )
+                {
+                    if (horizontal)
+                    {
+                        *pIdx++ = _index( j , starty ); numIndexes++;
+                        *pIdx++ = _index( jk, starty + rowstep ); numIndexes++;
+                        *pIdx++ = _index( jk + step, starty + rowstep ); numIndexes++;
+                    }
+                    else
+                    {
+                        *pIdx++ = _index( starty, j ); numIndexes++;
+                        *pIdx++ = _index( starty + rowstep, jk ); numIndexes++;
+                        *pIdx++ = _index( starty + rowstep, jk + step); numIndexes++;
+                    }
+                }
+            }
+
+            // Middle tri
+            if (horizontal)
+            {
+                *pIdx++ = _index( j, starty ); numIndexes++;
+                *pIdx++ = _index( j + halfsuperstep, starty + rowstep); numIndexes++;
+                *pIdx++ = _index( j + superstep, starty ); numIndexes++;
+            }
+            else
+            {
+                *pIdx++ = _index( starty, j ); numIndexes++;
+                *pIdx++ = _index( starty + rowstep, j + halfsuperstep ); numIndexes++;
+                *pIdx++ = _index( starty, j + superstep ); numIndexes++;
+            }
+
+            for (k = halfsuperstep; k != superstep; k += step)
+            {
+                int jk = j + k;
+                if ( j != endx - superstep || k != superstep - step || !omitLastTri )
+                {
+                    if (horizontal)
+                    {
+                        *pIdx++ = _index( j + superstep, starty ); numIndexes++;
+                        *pIdx++ = _index( jk, starty + rowstep ); numIndexes++;
+                        *pIdx++ = _index( jk + step, starty + rowstep ); numIndexes++;
+                    }
+                    else
+                    {
+                        *pIdx++ = _index( starty, j + superstep ); numIndexes++;
+                        *pIdx++ = _index( starty + rowstep, jk ); numIndexes++;
+                        *pIdx++ = _index( starty + rowstep, jk + step ); numIndexes++;
+                    }
+                }
+            }
+        }
+
+        *ppIdx = pIdx;
+
+        return numIndexes;
+
+    }
+
+#ifdef GTP_VISIBILITY_MODIFIED_OGRE
+	//-----------------------------------------------------------------------
+	void BiTerrainRenderable::ResetRenderLevelIndex() 
+	{
+		msCurrentRenderLevelIndex = 0;
+	}
+	//-----------------------------------------------------------------------
+	void BiTerrainRenderable::NextRenderLevelIndex()
+	{
+		if ((++ msCurrentRenderLevelIndex) > MAX_RENDERLEVEL_INDEX)
+			msCurrentRenderLevelIndex = 0;
+	}
+#endif // GTP_VISIBILITY_MODIFIED_OGRE
+
+} //namespace
Index: GTP/trunk/Lib/Vis/OnlineCullingCHC/OGRE/src/Plugin_VisibilitySceneManager.vcproj
===================================================================
--- GTP/trunk/Lib/Vis/OnlineCullingCHC/OGRE/src/Plugin_VisibilitySceneManager.vcproj	(revision 2332)
+++ GTP/trunk/Lib/Vis/OnlineCullingCHC/OGRE/src/Plugin_VisibilitySceneManager.vcproj	(revision 2348)
@@ -53,4 +53,5 @@
 				Name="VCPostBuildEventTool"
 				CommandLine="copy $(OutDir)\$(TargetFileName) $(OGRE_PATH)\Samples\Common\bin\$(ConfigurationName)
+copy $(OutDir)\$(TargetFileName) C:\private\Uni\GameTools\GTP\trunk\App\Demos\Vis\KdTreeDemo\OGRE\bin\$(ConfigurationName)
 "/>
 			<Tool
@@ -124,4 +125,5 @@
 				Name="VCPostBuildEventTool"
 				CommandLine="copy $(OutDir)\$(TargetFileName) $(OGRE_PATH)\Samples\Common\bin\$(ConfigurationName)
+rem copy $(OutDir)\$(TargetFileName) C:\private\Uni\GameTools\GTP\trunk\App\Demos\Vis\KdTreeDemo\OGRE\bin\$(ConfigurationName)
 "/>
 			<Tool
@@ -679,4 +681,100 @@
 			</File>
 		</Filter>
+		<Filter
+			Name="BiHierarchySceneManager"
+			Filter="">
+			<Filter
+				Name="Source Files"
+				Filter="">
+				<File
+					RelativePath=".\OgreBiHierarchy.cpp">
+				</File>
+				<File
+					RelativePath=".\OgreBiHierarchyCamera.cpp">
+				</File>
+				<File
+					RelativePath=".\OgreBiHierarchyInterface.cpp">
+				</File>
+				<File
+					RelativePath=".\OgreBiHierarchySceneManager.cpp">
+				</File>
+				<File
+					RelativePath=".\OgreBiHierarchySceneNode.cpp">
+				</File>
+			</Filter>
+			<Filter
+				Name="Header Files"
+				Filter="">
+				<File
+					RelativePath="..\include\OgreBiHierarchy.h">
+				</File>
+				<File
+					RelativePath="..\include\OgreBiHierarchyCamera.h">
+				</File>
+				<File
+					RelativePath="..\include\OgreBiHierarchyInterface.h">
+				</File>
+				<File
+					RelativePath="..\include\OgreBiHierarchySceneManager.h">
+				</File>
+				<File
+					RelativePath="..\include\OgreBiHierarchySceneNode.h">
+				</File>
+				<File
+					RelativePath="..\include\OgreBihRenderable.h">
+				</File>
+			</Filter>
+		</Filter>
+		<Filter
+			Name="BiTerrainSceneManager"
+			Filter="">
+			<Filter
+				Name="Source Files"
+				Filter="">
+				<File
+					RelativePath=".\OgreBiHeightmapTerrainPageSource.cpp">
+				</File>
+				<File
+					RelativePath=".\OgreBiTerrainPage.cpp">
+				</File>
+				<File
+					RelativePath=".\OgreBiTerrainPageSource.cpp">
+				</File>
+				<File
+					RelativePath=".\OgreBiTerrainRenderable.cpp">
+				</File>
+				<File
+					RelativePath=".\OgreBiTerrainSceneManager.cpp">
+				</File>
+				<File
+					RelativePath=".\OgreBiTerrainVertexProgram.cpp">
+				</File>
+			</Filter>
+			<Filter
+				Name="Header Files"
+				Filter="">
+				<File
+					RelativePath="..\include\OgreBiHeightmapTerrainPageSource.h">
+				</File>
+				<File
+					RelativePath="..\include\OgreBiTerrainPage.h">
+				</File>
+				<File
+					RelativePath="..\include\OgreBiTerrainPageSource.h">
+				</File>
+				<File
+					RelativePath="..\include\OgreBiTerrainPrerequisites.h">
+				</File>
+				<File
+					RelativePath="..\include\OgreBiTerrainRenderable.h">
+				</File>
+				<File
+					RelativePath="..\include\OgreBiTerrainSceneManager.h">
+				</File>
+				<File
+					RelativePath="..\include\OgreBiTerrainVertexProgram.h">
+				</File>
+			</Filter>
+		</Filter>
 		<File
 			RelativePath=".\VTune\Plugin_VisibilitySceneManager.vpj">
Index: GTP/trunk/Lib/Vis/OnlineCullingCHC/OGRE/src/OgreBiHeightmapTerrainPageSource.cpp
===================================================================
--- GTP/trunk/Lib/Vis/OnlineCullingCHC/OGRE/src/OgreBiHeightmapTerrainPageSource.cpp	(revision 2348)
+++ GTP/trunk/Lib/Vis/OnlineCullingCHC/OGRE/src/OgreBiHeightmapTerrainPageSource.cpp	(revision 2348)
@@ -0,0 +1,283 @@
+/*
+-----------------------------------------------------------------------------
+This source file is part of OGRE
+(Object-oriented Graphics Rendering Engine)
+For the latest info, see http://www.ogre3d.org/
+
+Copyright (c) 2000-2005 The OGRE Team
+Also see acknowledgements in Readme.html
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU Lesser General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA 02111-1307, USA, or go to
+http://www.gnu.org/copyleft/lesser.txt.
+-----------------------------------------------------------------------------
+*/
+
+#include <OgreException.h>
+#include <OgreStringConverter.h>
+#include <OgreResourceManager.h>
+#include <OgreLogManager.h>
+
+#include "OgreBiHeightmapTerrainPageSource.h"
+#include "OgreBiTerrainSceneManager.h"
+#include "OgreBiTerrainPage.h"
+
+namespace Ogre {
+
+    //-------------------------------------------------------------------------
+    BiHeightmapTerrainPageSource::BiHeightmapTerrainPageSource()
+        : mIsRaw(false), mFlipTerrain(false), mPage(0)
+    {
+    }
+    //-------------------------------------------------------------------------
+    BiHeightmapTerrainPageSource::~BiHeightmapTerrainPageSource()
+    {
+        shutdown();
+    }
+    //-------------------------------------------------------------------------
+    void BiHeightmapTerrainPageSource::shutdown(void)
+    {
+        // Image will destroy itself
+        delete mPage;
+        mPage = 0;
+    }
+    //-------------------------------------------------------------------------
+    void BiHeightmapTerrainPageSource::loadHeightmap(void)
+    {
+        size_t imgSize;
+        // Special-case RAW format
+        if (mIsRaw)
+        {
+            // Image size comes from setting (since RAW is not self-describing)
+            imgSize = mRawSize;
+            
+            // Load data
+            mRawData.setNull();
+            DataStreamPtr stream = 
+                ResourceGroupManager::getSingleton().openResource(
+                    mSource, ResourceGroupManager::getSingleton().getWorldResourceGroupName());
+            mRawData = MemoryDataStreamPtr(new MemoryDataStream(mSource, stream));
+
+            // Validate size
+            size_t numBytes = imgSize * imgSize * mRawBpp;
+            if (mRawData->size() != numBytes)
+            {
+                shutdown();
+                OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, 
+                    "RAW size (" + StringConverter::toString(mRawData->size()) + 
+                    ") does not agree with configuration settings.", 
+                    "HeightmapTerrainPageSource::loadHeightmap");
+            }
+        }
+        else
+        {
+            mImage.load(mSource, ResourceGroupManager::getSingleton().getWorldResourceGroupName());
+            // Must be square (dimensions checked later)
+            if ( mImage.getWidth() != mImage.getHeight())
+            {
+                shutdown();
+                OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
+                    "Heightmap must be square",
+                    "HeightmapTerrainPageSource::loadHeightmap");
+            }
+            imgSize = mImage.getWidth();
+        }
+        //check to make sure it's the expected size
+        if ( imgSize != mPageSize)
+        {
+            shutdown();
+            String err = "Error: Invalid heightmap size : " +
+                StringConverter::toString( imgSize ) +
+                ". Should be " + StringConverter::toString(mPageSize);
+            OGRE_EXCEPT( Exception::ERR_INVALIDPARAMS, err, 
+                "HeightmapTerrainPageSource::loadHeightmap" );
+        }
+
+    }
+    //-------------------------------------------------------------------------
+    void BiHeightmapTerrainPageSource::initialise(BiTerrainSceneManager* tsm, 
+		uint tileSize, uint pageSize, bool asyncLoading, 
+        BiTerrainPageSourceOptionList& optionList)
+    {
+        // Shutdown to clear any previous data
+        shutdown();
+
+        BiTerrainPageSource::initialise(tsm, tileSize, pageSize, asyncLoading, optionList);
+
+        // Get source image
+        BiTerrainPageSourceOptionList::iterator ti, tiend;
+        tiend = optionList.end();
+        bool imageFound = false;
+        mIsRaw = false;
+        bool rawSizeFound = false;
+        bool rawBppFound = false;
+        for (ti = optionList.begin(); ti != tiend; ++ti)
+        {
+            String val = ti->first;
+            StringUtil::trim(val);
+            if (StringUtil::startsWith(val, "Heightmap.image", false))
+            {
+                mSource = ti->second;
+                imageFound = true;
+                // is it a raw?
+                if (StringUtil::endsWith(mSource, "raw"))
+                {
+                    mIsRaw = true;
+                }
+            }
+            else if (StringUtil::startsWith(val, "Heightmap.raw.size", false))
+            {
+                mRawSize = atoi(ti->second.c_str());
+                rawSizeFound = true;
+            }
+            else if (StringUtil::startsWith(val, "Heightmap.raw.bpp", false))
+            {
+                mRawBpp = atoi(ti->second.c_str());
+                if (mRawBpp < 1 || mRawBpp > 2)
+                {
+                    OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, 
+                        "Invalid value for 'Heightmap.raw.bpp', must be 1 or 2",
+                        "HeightmapTerrainPageSource::initialise");
+                }
+                rawBppFound = true;
+            }
+            else if (StringUtil::startsWith(val, "Heightmap.flip", false))
+            {
+                mFlipTerrain = StringConverter::parseBool(ti->second);
+            }
+            else
+            {
+                LogManager::getSingleton().logMessage("Warning: ignoring unknown Heightmap option '"
+                    + val + "'");
+            }
+        }
+        if (!imageFound)
+        {
+            OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, 
+                "Missing option 'Heightmap.image'", 
+                "HeightmapTerrainPageSource::initialise");
+        }
+        if (mIsRaw && 
+            (!rawSizeFound || !rawBppFound))
+        {
+            OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, 
+                "Options 'Heightmap.raw.size' and 'Heightmap.raw.bpp' must "
+                "be specified for RAW heightmap sources", 
+                "HeightmapTerrainPageSource::initialise");
+        }
+        // Load it!
+        loadHeightmap();
+    }
+    //-------------------------------------------------------------------------
+    void BiHeightmapTerrainPageSource::requestPage(ushort x, ushort y)
+    {
+        // Only 1 page provided
+        if (x == 0 && y == 0 && !mPage)
+        {
+            // Convert the image data to unscaled floats
+            ulong totalPageSize = mPageSize * mPageSize; 
+            Real *heightData = new Real[totalPageSize];
+            const uchar* pOrigSrc, *pSrc;
+            Real* pDest = heightData;
+            Real invScale;
+            bool is16bit = false;
+            
+            if (mIsRaw)
+            {
+                pOrigSrc = mRawData->getPtr();
+                is16bit = (mRawBpp == 2);
+            }
+            else
+            {
+                PixelFormat pf = mImage.getFormat();
+                if (pf != PF_L8 && pf != PF_L16)
+                {
+                    OGRE_EXCEPT( Exception::ERR_INVALIDPARAMS, 
+                        "Error: Image is not a grayscale image.",
+                        "HeightmapTerrainPageSource::requestPage" );
+                }
+
+                pOrigSrc = mImage.getData();
+                is16bit = (pf == PF_L16);
+            }
+            // Determine mapping from fixed to floating
+            ulong rowSize;
+            if ( is16bit )
+            {
+                invScale = 1.0f / 65535.0f; 
+                rowSize =  mPageSize * 2;
+            }
+            else 
+            {
+                invScale = 1.0f / 255.0f; 
+                rowSize =  mPageSize;
+            }
+            // Read the data
+            pSrc = pOrigSrc;
+            for (ulong j = 0; j < mPageSize; ++j)
+            {
+                if (mFlipTerrain)
+                {
+                    // Work backwards 
+                    pSrc = pOrigSrc + (rowSize * (mPageSize - j - 1));
+                }
+                for (ulong i = 0; i < mPageSize; ++i)
+                {
+                    if (is16bit)
+                    {
+                        #if OGRE_ENDIAN == OGRE_ENDIAN_BIG
+                            ushort val = *pSrc++ << 8;
+                            val += *pSrc++;
+                        #else
+                            ushort val = *pSrc++;
+                            val += *pSrc++ << 8;
+                        #endif
+                        *pDest++ = Real(val) * invScale;
+                    }
+                    else
+                    {
+                        *pDest++ = Real(*pSrc++) * invScale;
+                    }
+                }
+            }
+
+            // Call listeners
+            firePageConstructed(0, 0, heightData);
+            // Now turn into TerrainPage
+            // Note that we're using a single material for now
+            if (mSceneManager)
+            {
+                mPage = buildPage(heightData, 
+                    mSceneManager->getOptions().terrainMaterial);
+                mSceneManager->attachPage(0, 0, mPage);
+            }
+
+            // Free temp store
+            delete [] heightData;
+        }
+    }
+    //-------------------------------------------------------------------------
+    void BiHeightmapTerrainPageSource::expirePage(ushort x, ushort y)
+    {
+        // Single page
+        if (x == 0 && y == 0 && mPage)
+        {
+            delete mPage;
+            mPage = 0;
+        }
+
+    }
+    //-------------------------------------------------------------------------
+
+
+}
Index: GTP/trunk/Lib/Vis/OnlineCullingCHC/OGRE/src/OgreVisibilitySceneManagerDll.cpp
===================================================================
--- GTP/trunk/Lib/Vis/OnlineCullingCHC/OGRE/src/OgreVisibilitySceneManagerDll.cpp	(revision 2280)
+++ GTP/trunk/Lib/Vis/OnlineCullingCHC/OGRE/src/OgreVisibilitySceneManagerDll.cpp	(revision 2348)
@@ -27,4 +27,5 @@
 #include "OgreKdTreeSceneManager.h"
 #include "OgreKdTerrainSceneManager.h"
+#include "OgreBiTerrainSceneManager.h"
 #include <OgreRoot.h>
 #include <OgreLogManager.h>
@@ -32,4 +33,5 @@
 
 #include "OgreBvHierarchySceneManager.h"
+#include "OgreBiHierarchySceneManager.h"
 #include "VisibilityManager.h"
 #include "VisibilityEnvironment.h"
@@ -46,5 +48,7 @@
 KdTreeSceneManagerFactory *kdTreeFactory;
 KdTerrainSceneManagerFactory *kdTerrainFactory;
+BiTerrainSceneManagerFactory *biTerrainFactory;
 BvHierarchySceneManagerFactory *bvHierarchyFactory;
+BiHierarchySceneManagerFactory *biHierarchyFactory;
 
 //-----------------------------------------------------------------------
@@ -59,9 +63,13 @@
 	kdTreeFactory = new KdTreeSceneManagerFactory(visManager);
 	kdTerrainFactory = new KdTerrainSceneManagerFactory(visManager);
+	biTerrainFactory = new BiTerrainSceneManagerFactory(visManager);
 	bvHierarchyFactory = new BvHierarchySceneManagerFactory(visManager);
+	biHierarchyFactory = new BiHierarchySceneManagerFactory(visManager);
+
 
 	// Construct listener manager singleton
 	new TerrainPageSourceListenerManager();
 	new KdTerrainPageSourceListenerManager();
+	new BiTerrainPageSourceListenerManager();
 
 	// Register
@@ -69,5 +77,7 @@
 	Root::getSingleton().addSceneManagerFactory(kdTreeFactory);
 	Root::getSingleton().addSceneManagerFactory(kdTerrainFactory);
+	Root::getSingleton().addSceneManagerFactory(biTerrainFactory);
 	Root::getSingleton().addSceneManagerFactory(bvHierarchyFactory);
+	Root::getSingleton().addSceneManagerFactory(biHierarchyFactory);
 }
 //-----------------------------------------------------------------------
@@ -77,9 +87,12 @@
 	Root::getSingleton().removeSceneManagerFactory(kdTreeFactory);
 	Root::getSingleton().removeSceneManagerFactory(kdTerrainFactory);
+	Root::getSingleton().removeSceneManagerFactory(biTerrainFactory);
 	Root::getSingleton().removeSceneManagerFactory(bvHierarchyFactory);
+	Root::getSingleton().removeSceneManagerFactory(biHierarchyFactory);
 
 	// destroy listener manager
 	delete TerrainPageSourceListenerManager::getSingletonPtr();
 	delete KdTerrainPageSourceListenerManager::getSingletonPtr();
+	delete BiTerrainPageSourceListenerManager::getSingletonPtr();
 }
 //-----------------------------------------------------------------------
@@ -91,5 +104,7 @@
 	delete kdTreeFactory;
 	delete kdTerrainFactory;
+	delete biTerrainFactory;
 	delete bvHierarchyFactory;
+	delete biHierarchyFactory;
 
 	delete visManager;
Index: GTP/trunk/Lib/Vis/OnlineCullingCHC/OGRE/src/OgreBiHierarchySceneManager.cpp
===================================================================
--- GTP/trunk/Lib/Vis/OnlineCullingCHC/OGRE/src/OgreBiHierarchySceneManager.cpp	(revision 2348)
+++ GTP/trunk/Lib/Vis/OnlineCullingCHC/OGRE/src/OgreBiHierarchySceneManager.cpp	(revision 2348)
@@ -0,0 +1,1463 @@
+/*
+-----------------------------------------------------------------------------
+This source file is part of the GameTools Project
+http://www.gametools.org
+
+Author: Martin Szydlowski
+-----------------------------------------------------------------------------
+*/
+
+#include <OgreCamera.h>
+#include <OgreSubEntity.h>
+#include <OgreMaterialManager.h>
+#include <OgreLogManager.h>
+#include <OgreStringConverter.h>
+
+#include "OgreBiHierarchySceneManager.h"
+#include "OgreBiHierarchySceneNode.h"
+#include "OgreBiHierarchy.h"
+
+#include <OgreMeshInstance.h>
+#include <OgreBoundingBoxConverter.h>
+#include <OgreTypeConverter.h>
+#include <VisibilityEnvironment.h>
+#include "OgreVisibilityOptionsManager.h"
+
+namespace Ogre
+{
+
+BiHierarchySceneManager::BiHierarchySceneManager(const String& name, 
+												 GtpVisibility::VisibilityManager *vm):
+SceneManager(name), 
+mVisibilityManager(vm), 
+mBiHierarchy(0), 
+mMaxDepth(BiHierarchy_MAX_DEPTH),
+mShowBoxes(false),
+mHiLiteLevel(0),
+mShowAllBoxes(false),
+mEnhancedVisiblity(true),
+mBuildMethod(BiHierarchy::BIHBM_RECURSIVE),
+mRenderMethod(BiHierarchy::BIHRM_INTERNAL),
+mShowVisualization(false),
+mRenderNodesForViz(false),
+mRenderNodesContentForViz(false),
+mVisualizeCulledNodes(false),
+mLeavePassesInQueue(0),
+mDelayRenderTransparents(true),
+mUseDepthPass(false),
+mIsDepthPassPhase(false),
+mUseItemBuffer(false),
+mIsItemBufferPhase(false),
+mCurrentEntityId(1),
+mEnableDepthWrite(true),
+mSkipTransparents(false),
+mRenderTransparentsForItemBuffer(true),
+mExecuteVertexProgramForAllPasses(false),
+mIsHierarchicalCulling(false),
+mDeleteQueueAfterRendering(true),
+mViewCellsLoaded(false),
+mUseViewCells(false),
+mUseVisibilityFilter(false),
+mViewCellsManager(0),
+mElementaryViewCell(0),
+mCurrentViewCell(0)
+{
+	// Replace root node with my node
+	OGRE_DELETE(mSceneRoot);
+	mSceneRoot = new BiHierarchySceneNode(this, "root node");
+	mSceneRoot->_notifyRootNode();
+
+	// init heirarchy interface
+	mHierarchyInterface = new BiHierarchyInterface(this, mDestRenderSystem);
+}
+
+BiHierarchySceneManager::~BiHierarchySceneManager(void)
+{
+	delete mHierarchyInterface;
+	delete mBiHierarchy;
+}
+
+void BiHierarchySceneManager::clearScene()
+{
+	// DEBUG
+	if (mBiHierarchy)
+		mBiHierarchy->dump();
+
+	// must happen before actual scene is cleared
+	OGRE_DELETE(mBiHierarchy);
+
+	SceneManager::clearScene();
+}
+
+
+const String& BiHierarchySceneManager::getTypeName(void) const
+{
+	return BiHierarchySceneManagerFactory::FACTORY_TYPE_NAME;
+}
+
+void BiHierarchySceneManager::setShowBoxes(bool showboxes)
+{
+	mShowBoxes = showboxes;
+}
+
+bool BiHierarchySceneManager::getShowBoxes(void) const
+{
+	return mShowBoxes;
+}
+
+bool BiHierarchySceneManager::setOption(const String& strKey, const void* pValue)
+{
+	// change max depth of the BiHierarchy
+	// rebuild the tree if already exists
+	if (strKey == "BiHierarchyMaxDepth")
+	{
+		int maxdepth = *static_cast<const int *>(pValue);
+
+		// no negative depth, plz!
+		if (maxdepth < 0)
+		{
+			return false;
+		}
+		else
+		{
+			mMaxDepth = maxdepth;
+			if (mHiLiteLevel > mMaxDepth)
+				mHiLiteLevel = mMaxDepth;
+			return true;
+		}
+		return true;
+	}
+	else if (strKey == "KT")
+	{
+		Real kt = *static_cast<const Real *>(pValue);
+		if (kt > 0)
+		{
+			BihPlaneEvent::KT = kt;
+			return true;
+		}
+		else
+		{
+			return false;
+		}
+	}
+	else if (strKey == "KI")
+	{
+		Real ki = *static_cast<const Real *>(pValue);
+		if (ki > 0)
+		{
+			BihPlaneEvent::KI = ki;
+			return true;
+		}
+		else
+		{
+			return false;
+		}
+	}
+	else if (strKey == "RebuildBiHierarchy")
+	{
+		OGRE_DELETE(mBiHierarchy);
+		mBiHierarchy = new BiHierarchy(mMaxDepth, mBuildMethod, mHiLiteLevel, mShowAllBoxes, mShowNodes);
+		mBiHierarchy->build(static_cast<BiHierarchySceneNode *>(mSceneRoot));
+		mBiHierarchy->setEnhancedVis(mEnhancedVisiblity);
+		return true;
+	}
+	else if (strKey == "EnhancedVisibility")
+	{
+		bool enh = *static_cast<const bool *>(pValue);
+		mEnhancedVisiblity = enh;
+		if (mBiHierarchy)
+			mBiHierarchy->setEnhancedVis(mEnhancedVisiblity);
+		//setEnhancedVis(enh);
+		return true;
+	}
+	else if (strKey == "BuildMethod")
+	{
+		BiHierarchy::BuildMethod bm = *static_cast<const BiHierarchy::BuildMethod *>(pValue);
+		if (bm == BiHierarchy::BIHBM_RECURSIVE || bm == BiHierarchy::BIHBM_PRIORITYQUEUE)
+		{
+			mBuildMethod = bm;
+			return true;
+		}
+		else
+		{
+			return false;
+		}
+	}
+	else if (strKey == "RenderMethod")
+	{
+		BiHierarchy::RenderMethod rm = *static_cast<const BiHierarchy::RenderMethod *>(pValue);
+		if (rm == BiHierarchy::BIHRM_INTERNAL)
+		{
+			mRenderMethod = rm;
+			return true;
+		}
+		else if (rm == BiHierarchy::BIHRM_GTP_VFC)
+		{
+			mRenderMethod = rm;
+			int cmt = GtpVisibility::VisibilityEnvironment::FRUSTUM_CULLING;
+			return VisibilityOptionsManager(mVisibilityManager, mHierarchyInterface)
+				.setOption("Algorithm", &cmt);
+		}
+		else if (rm == BiHierarchy::BIHRM_GTP_SWC)
+		{
+			mRenderMethod = rm;
+			int cmt = GtpVisibility::VisibilityEnvironment::STOP_AND_WAIT_CULLING;
+			return VisibilityOptionsManager(mVisibilityManager, mHierarchyInterface)
+				.setOption("Algorithm", &cmt);
+		}
+		else if (rm == BiHierarchy::BIHRM_GTP_CHC)
+		{
+			mRenderMethod = rm;
+			int cmt = GtpVisibility::VisibilityEnvironment::COHERENT_HIERARCHICAL_CULLING;
+			return VisibilityOptionsManager(mVisibilityManager, mHierarchyInterface)
+				.setOption("Algorithm", &cmt);
+		}
+		else
+		{
+			return false;
+		}
+	}
+	// little hack in case someone uses "Algorithm" option from VisOptMan directly
+	else if (strKey == "Algorithm")
+	{
+		bool success = VisibilityOptionsManager(mVisibilityManager, mHierarchyInterface)
+			.setOption(strKey, pValue);
+		// change setting only if change in VisOptMan was successful
+		if (success)
+		{
+			int val = *static_cast<const int *>(pValue);
+			if (val == GtpVisibility::VisibilityEnvironment::FRUSTUM_CULLING)
+				mRenderMethod = BiHierarchy::BIHRM_GTP_VFC;
+			else if (val == GtpVisibility::VisibilityEnvironment::STOP_AND_WAIT_CULLING)
+				mRenderMethod = BiHierarchy::BIHRM_GTP_SWC;
+			else if (val == GtpVisibility::VisibilityEnvironment::COHERENT_HIERARCHICAL_CULLING)
+				mRenderMethod = BiHierarchy::BIHRM_GTP_CHC;
+			// default, should never happen
+			else
+				mRenderMethod = BiHierarchy::BIHRM_INTERNAL;
+		}
+		else
+		{
+			mRenderMethod = BiHierarchy::BIHRM_INTERNAL;
+		}
+		return success;
+	}
+	else if (strKey == "ShowBiHierarchy")
+	{
+		bool sk = *static_cast<const bool *>(pValue);
+		mShowBoxes = sk;
+		return true;
+	}
+	else if (strKey == "HiLiteLevel")
+	{
+		int hl = *static_cast<const int *>(pValue);
+		if (hl >= 0 && hl <= mMaxDepth)
+		{
+			mHiLiteLevel = hl;
+			if (mBiHierarchy)
+				mBiHierarchy->setHiLiteLevel(mHiLiteLevel);
+			return true;
+		}
+		else
+		{
+			return false;
+		}
+	}
+	else if (strKey == "ShowAllBoxes")
+	{
+		bool sa = *static_cast<const bool *>(pValue);
+		mShowAllBoxes = sa;
+		if (mBiHierarchy)
+			mBiHierarchy->setShowAllBoxes(mShowAllBoxes);
+		return true;
+	}
+	else if (strKey == "ShowNodes")
+	{
+		bool sn = *static_cast<const bool *>(pValue);
+		mShowNodes = sn;
+		if (mBiHierarchy)
+			mBiHierarchy->setShowNodes(mShowNodes);
+		return true;
+	}
+	// options for CHC
+	else if (strKey == "UseDepthPass")
+	{
+		mUseDepthPass = (*static_cast<const bool *>(pValue));
+		return true;
+	}
+	else if (strKey == "PrepareVisualization")
+	{
+		mShowVisualization = (*static_cast<const bool *>(pValue));
+		return true;
+	}
+	else if (strKey == "RenderNodesForViz")
+	{
+		mRenderNodesForViz = (*static_cast<const bool *>(pValue));
+		return true;
+	}
+	else if (strKey == "RenderNodesContentForViz")
+	{
+		mRenderNodesContentForViz = (*static_cast<const bool *>(pValue));
+		return true;
+	}
+	else if (strKey == "SkyBoxEnabled")
+	{
+		mSkyBoxEnabled = (*static_cast<const bool *>(pValue));
+		return true;
+	}
+	else if (strKey == "SkyPlaneEnabled")
+	{
+		mSkyPlaneEnabled = (*static_cast<const bool *>(pValue));
+		return true;
+	}
+	else if (strKey == "SkyDomeEnabled")
+	{
+		mSkyDomeEnabled = (*static_cast<const bool *>(pValue));
+		return true;
+	}
+	else if (strKey == "VisualizeCulledNodes")
+	{
+		mVisualizeCulledNodes = (*static_cast<const bool *>(pValue));
+		return true;
+	}
+	else if (strKey == "DelayRenderTransparents")
+	{
+		mDelayRenderTransparents = (*static_cast<const bool *>(pValue));
+		return true;
+	}
+
+	else if (strKey == "DepthWrite")
+	{
+		mEnableDepthWrite = (*static_cast<const bool *>(pValue));
+		return true;
+	}
+	else if (strKey == "UseItemBuffer")
+	{
+		mUseItemBuffer = (*static_cast<const bool *>(pValue));
+		return true;
+	}
+	else if (strKey == "ExecuteVertexProgramForAllPasses")
+	{ 
+		mExecuteVertexProgramForAllPasses  = (*static_cast<const bool *>(pValue));
+		return true;
+	}
+	else if (strKey == "RenderTransparentsForItemBuffer")
+	{ 
+		mRenderTransparentsForItemBuffer  = (*static_cast<const bool *>(pValue));
+		return true;
+	}
+	else if (strKey == "DeleteRenderQueue")
+	{
+		mDeleteQueueAfterRendering = (*static_cast<const bool *>(pValue));
+		return true;
+	}
+	// options for viewcells
+	else if (strKey == "LoadViewCells")
+	{ 
+		if (!mViewCellsLoaded)
+		{
+			String filename(static_cast<const char *>(pValue));
+			mViewCellsLoaded = LoadViewCells(filename);	
+		}
+
+		return mViewCellsLoaded;
+	}
+	else if (strKey == "UseViewCells")
+	{ 
+		if (mViewCellsLoaded)
+		{
+			mUseViewCells = *static_cast<const bool *>(pValue);
+
+			// reset view cell
+			OGRE_DELETE(mCurrentViewCell);
+
+			if (mUseViewCells)
+				mCurrentViewCell = mViewCellsManager->GenerateViewCell();
+
+			mElementaryViewCell = NULL;
+
+			// if using view cells, all objects are set to false initially
+			SetObjectsVisible(!mUseViewCells);
+
+			return true;
+		}
+		else
+		{
+			return false;
+		}
+
+	}
+	else if (strKey == "UseVisibilityFilter")
+	{
+		if (mViewCellsLoaded)
+		{
+			mUseVisibilityFilter = *static_cast<const bool *>(pValue);
+			// set null =>recomputation of the pvs
+			mElementaryViewCell = NULL;
+			return true;
+		}
+		else
+		{
+			return false;
+		}
+	}
+
+	return VisibilityOptionsManager(mVisibilityManager, mHierarchyInterface)
+		.setOption(strKey, pValue) || SceneManager::setOption(strKey, pValue);
+}
+
+bool BiHierarchySceneManager::getOption(const String& strKey, void* pDestValue)
+{
+	if (strKey == "BiHierarchyMaxDepth")
+	{
+		*static_cast<int *>(pDestValue) = mMaxDepth;
+		return true;
+	}
+	else if (strKey == "KT")
+	{
+		*static_cast<Real *>(pDestValue) = BihPlaneEvent::KT;
+		return true;
+	}
+	else if (strKey == "KI")
+	{
+		*static_cast<Real *>(pDestValue) = BihPlaneEvent::KI;
+		return true;
+	}
+	else if (strKey == "EnhancedVisibility")
+	{
+		if (mBiHierarchy)
+			*static_cast<bool *>(pDestValue) = mBiHierarchy->getEnhancedVis();
+		else
+			*static_cast<bool *>(pDestValue) = mEnhancedVisiblity;
+		return true;
+	}
+	else if (strKey == "BuildMethod")
+	{
+		*static_cast<BiHierarchy::BuildMethod *>(pDestValue) = mBuildMethod;
+		return true;
+	}
+	else if (strKey == "RenderMethod")
+	{
+		*static_cast<BiHierarchy::RenderMethod *>(pDestValue) = mRenderMethod;
+		return true;
+	}
+	else if (strKey == "ShowBiHierarchy")
+	{
+		*static_cast<bool *>(pDestValue) = mShowBoxes;
+		return true;
+	}	
+	else if (strKey == "HiLiteLevel")
+	{
+		*static_cast<int *>(pDestValue) = mHiLiteLevel;
+		return true;
+	}
+	else if (strKey == "ShowAllBoxes")
+	{
+		*static_cast<bool *>(pDestValue) = mShowAllBoxes;
+		return true;
+	}
+	else if (strKey == "ShowNodes")
+	{
+		*static_cast<bool *>(pDestValue) = mShowNodes;
+		return true;
+	}
+	// vis options
+	else if (strKey == "NumFrustumCulledNodes")
+	{
+		if (mRenderMethod == BiHierarchy::BIHRM_INTERNAL)
+		{
+			unsigned int numnodes = 0;
+			if (mBiHierarchy)
+				numnodes = mBiHierarchy->getFramesStats().mFrustumCulledNodes;
+
+			* static_cast<unsigned int *>(pDestValue) = (unsigned int)numnodes;
+			return true;
+		}
+		// otherwise let fall through to VisibilityOptionsManager
+	}
+	else if (strKey == "NumQueryCulledNodes")
+	{
+		if (mRenderMethod == BiHierarchy::BIHRM_INTERNAL)
+		{
+			* static_cast<unsigned int *>(pDestValue) = 0;
+			return true;
+		}
+		// otherwise let fall through to VisibilityOptionsManager
+	}
+	else if (strKey == "NumHierarchyNodes")
+	{
+		unsigned int numnodes = 0;
+		if (mBiHierarchy)
+			numnodes = mBiHierarchy->getTreeStats().mNumNodes;
+
+		* static_cast<unsigned int *>(pDestValue) = (unsigned int)numnodes;
+		return true;
+	}
+	else if (strKey == "NumRenderedNodes")
+	{
+		if (mRenderMethod == BiHierarchy::BIHRM_INTERNAL)
+		{
+			unsigned int numnodes = 0;
+			if (mBiHierarchy)
+				numnodes = mBiHierarchy->getFramesStats().mRenderedNodes;
+
+			* static_cast<unsigned int *>(pDestValue) = (unsigned int)numnodes;
+			return true;
+		}
+		// otherwise let fall through to VisibilityOptionsManager
+	}
+	else if (strKey == "NumQueriesIssued")
+	{
+		if (mRenderMethod == BiHierarchy::BIHRM_INTERNAL)
+		{
+			* static_cast<unsigned int *>(pDestValue) = 0;
+			return true;
+		}
+		// otherwise let fall through to VisibilityOptionsManager
+	}
+	else if (strKey == "NumTraversedNodes")
+	{
+		if (mRenderMethod == BiHierarchy::BIHRM_INTERNAL)
+		{
+			unsigned int numnodes = 0;
+			if (mBiHierarchy)
+				numnodes = mBiHierarchy->getFramesStats().mTraversedNodes;
+
+			* static_cast<unsigned int *>(pDestValue) = (unsigned int)numnodes;
+			return true;
+		}
+		// otherwise let fall through to VisibilityOptionsManager
+	}
+	else if (strKey == "VisibilityManager")
+	{
+		* static_cast<GtpVisibility::VisibilityManager **>(pDestValue) = 
+			(GtpVisibility::VisibilityManager *)mVisibilityManager;
+		return true;
+	}
+	else if (strKey == "HierarchInterface")
+	{
+		* static_cast<GtpVisibility::HierarchyInterface **>(pDestValue) = 
+			(GtpVisibility::HierarchyInterface *)mHierarchyInterface;
+		return true;
+	}
+	// view cell options
+
+	else if (strKey == "UseViewCells")
+	{ 
+		*static_cast<bool *>(pDestValue) = mUseViewCells;
+		return mViewCellsLoaded;
+	}
+	else if (strKey == "UseVisibilityFilter")
+	{
+		*static_cast<bool *>(pDestValue) = mUseVisibilityFilter;
+		return mViewCellsLoaded;
+	}
+
+	return VisibilityOptionsManager(mVisibilityManager, mHierarchyInterface)
+		.getOption(strKey, pDestValue) || SceneManager::getOption(strKey, pDestValue);
+}
+
+bool BiHierarchySceneManager::getOptionKeys(StringVector &refKeys)
+{
+	refKeys.push_back("Algorithm");
+	refKeys.push_back("BuildMethod");
+	refKeys.push_back("DelayRenderTransparents");
+	refKeys.push_back("DeleteRenderQueue");
+	refKeys.push_back("DepthWrite");
+	refKeys.push_back("EnhancedVisibility");
+	refKeys.push_back("ExecuteVertexProgramForAllPasses");
+	refKeys.push_back("HiLiteLevel");
+	refKeys.push_back("HierarchInterface");
+	refKeys.push_back("KI");
+	refKeys.push_back("KT");
+	refKeys.push_back("BiHierarchyMaxDepth");
+	refKeys.push_back("LoadViewCells");
+	refKeys.push_back("NumHierarchyNodes");
+	refKeys.push_back("PrepareVisualization");
+	refKeys.push_back("RebuildBiHierarchy");
+	refKeys.push_back("RenderMethod");
+	refKeys.push_back("RenderNodesContentForViz");
+	refKeys.push_back("RenderNodesForViz");
+	refKeys.push_back("RenderTransparentsForItemBuffer");
+	refKeys.push_back("ShowAllBoxes");
+	refKeys.push_back("ShowBiHierarchy");
+	refKeys.push_back("ShowNodes");
+	refKeys.push_back("SkyBoxEnabled");
+	refKeys.push_back("SkyDomeEnabled");
+	refKeys.push_back("SkyPlaneEnabled");
+	refKeys.push_back("UseDepthPass");
+	refKeys.push_back("UseItemBuffer");
+	refKeys.push_back("UseViewCells");
+	refKeys.push_back("UseVisibilityFilter");
+	refKeys.push_back("VisibilityManager");
+	refKeys.push_back("VisualizeCulledNodes");
+	return VisibilityOptionsManager(mVisibilityManager, mHierarchyInterface)
+		.getOptionKeys(refKeys);
+}
+
+bool BiHierarchySceneManager::getOptionValues(const String & key, StringVector &refValueList)
+{
+	return SceneManager::getOptionValues(key, refValueList);
+}
+
+void BiHierarchySceneManager::setVisibilityManager(GtpVisibility::VisibilityManager *visManager)
+{
+	mVisibilityManager = visManager;
+}
+
+GtpVisibility::VisibilityManager * BiHierarchySceneManager::getVisibilityManager()
+{
+	return mVisibilityManager;
+}
+
+GtpVisibility::VisibilityManager * BiHierarchySceneManager::GetVisibilityManager()
+{
+	return mVisibilityManager;
+}
+
+BiHierarchyInterface * BiHierarchySceneManager::GetHierarchyInterface()
+{
+	return mHierarchyInterface;
+}
+
+Camera* BiHierarchySceneManager::createCamera(const String& name)
+{
+	// Check name not used
+	if (mCameras.find(name) != mCameras.end())
+	{
+		OGRE_EXCEPT(
+			Exception::ERR_DUPLICATE_ITEM,
+			"A camera with the name " + name + " already exists",
+			"SceneManager::createCamera" );
+	}
+
+	Camera *c = new BiHierarchyCamera(name, this);
+	mCameras.insert(CameraList::value_type(name, c));
+
+	return c;
+}
+
+SceneNode* BiHierarchySceneManager::createSceneNode(void)
+{
+	SceneNode* sn = new BiHierarchySceneNode(this);
+	assert(mSceneNodes.find(sn->getName()) == mSceneNodes.end());
+	mSceneNodes[sn->getName()] = sn;
+	return sn;
+}
+
+SceneNode* BiHierarchySceneManager::createSceneNode(const String& name)
+{
+	// Check name not used
+	if (mSceneNodes.find(name) != mSceneNodes.end())
+	{
+		OGRE_EXCEPT(
+			Exception::ERR_DUPLICATE_ITEM,
+			"A scene node with the name " + name + " already exists",
+			"BiHierarchySceneManager::createSceneNode" );
+	}
+
+	SceneNode* sn = new BiHierarchySceneNode(this, name);
+	mSceneNodes[sn->getName()] = sn;
+	return sn;
+}
+
+Entity * BiHierarchySceneManager::createEntity(const String& entityName, const String& meshName)
+{
+	Entity *ent = SceneManager::createEntity(entityName, meshName);
+#ifdef GTP_VISIBILITY_MODIFIED_OGRE
+	for (int i = 0; i < (int)ent->getNumSubEntities(); ++i)
+	{
+		ent->getSubEntity(i)->setId(mCurrentEntityId);
+	}
+
+	// increase counter of entity id values
+	++ mCurrentEntityId;
+#endif
+	return ent;
+}
+
+// make sure it's called only for non-empty nodes .. avoids one uneccessary funciton call
+void BiHierarchySceneManager::_updateNode(BiHierarchySceneNode *node)
+{
+	//LogManager::getSingleton().logMessage("### _updateNode called for " + node->getName());
+
+	/* Rebuild BiHierarchy when it was wiped out
+		* Usually this happens only before the first frame
+		* The initial AABB shall enclose all objects present
+		* in the scene at the time of construction
+		* TODO: find a more appropriate place for it,
+		* e.g., before the first render call
+		* teh stupid thing is I can't find any other place so far ...
+		*/
+	if (!mBiHierarchy)
+	{
+		mBiHierarchy = new BiHierarchy(mMaxDepth, mBuildMethod);
+		mBiHierarchy->build(static_cast<BiHierarchySceneNode *>(mSceneRoot));
+		mBiHierarchy->setEnhancedVis(mEnhancedVisiblity);
+	}
+
+	// if the node is in the tree and _updateNode was called, then the node was moved/rotated/resized/wahtever
+	if (node->isAttached())
+	{
+		// TEST: perfomance when adding & removing for every frame
+		//mBiHierarchy->remove(node);
+		//mBiHierarchy->insert(node);
+	}
+	// there's a new node in town ... 
+	else
+	{			
+		// inserting single nodes yields sub-optimal results
+		// rebuilding tree takes too long
+		// "What now?", spoke Zeus ...
+
+		//mBiHierarchy->insert(node);
+		
+		//delete mBiHierarchy;
+		//mBiHierarchy = new BiHierarchy(mMaxDepth);
+		//mBiHierarchy->build(static_cast<BiHierarchySceneNode *>(mSceneRoot));
+	}
+
+}
+
+void BiHierarchySceneManager::_updateSceneGraph(Camera* cam)
+{
+	mVisibilityManager->GetCullingManager()->SetHierarchyInterface(mHierarchyInterface);
+	mHierarchyInterface->SetRenderSystem(mDestRenderSystem);
+
+	SceneManager::_updateSceneGraph(cam);
+}
+
+void BiHierarchySceneManager::_findVisibleObjects(Camera *cam, bool onlyShadowCasters)
+{
+	if (mRenderMethod == BiHierarchy::BIHRM_INTERNAL)
+	{
+		getRenderQueue()->clear();
+		if (mShowVisualization)
+		{
+			PrepareVisualization(cam);
+		}
+		else
+		{
+			mVisibleNodes.clear();
+			if (mBiHierarchy)
+			{
+				// determine visibility
+				mBiHierarchy->queueVisibleObjects(BIHCAMPTR_CAST(cam), getRenderQueue(), onlyShadowCasters, mShowBoxes, mVisibleNodes);
+				// apply view cell pvs
+				updatePvs(cam);
+			}
+		}
+	}
+	else
+	{
+		//-- show visible scene nodes and octree bounding boxes from last frame
+		if (mShowVisualization)
+		{
+			PrepareVisualization(cam);
+		}
+		else 
+		{	
+			// for hierarchical culling, we interleave identification 
+			// and rendering of objects in _renderVisibibleObjects
+
+			// for the shadow pass we use only standard rendering
+			// because of low occlusion
+			if (mShadowTechnique == SHADOWTYPE_TEXTURE_MODULATIVE && 
+				mIlluminationStage == IRS_RENDER_TO_TEXTURE)
+			{
+				getRenderQueue()->clear();
+				if (mBiHierarchy)
+					mBiHierarchy->queueVisibleObjects(BIHCAMPTR_CAST(cam), getRenderQueue(), onlyShadowCasters, mShowBoxes, mVisibleNodes);
+			}
+
+			// only shadow casters will be rendered in shadow texture pass
+			if (0) mHierarchyInterface->SetOnlyShadowCasters(onlyShadowCasters);
+
+
+			//-- apply view cell pvs -
+			updatePvs(cam);
+		}
+		mVisibleNodes.clear();	
+	}
+}
+
+void BiHierarchySceneManager::_renderVisibleObjects()
+{
+	if (mRenderMethod == BiHierarchy::BIHRM_INTERNAL)
+	{
+		SceneManager::_renderVisibleObjects();
+	}
+	else
+	{
+		InitDepthPass(); 	  // create material for depth pass
+		InitItemBufferPass(); // create material for item buffer pass
+
+		// save ambient light to reset later
+		ColourValue savedAmbient = mAmbientLight;
+
+		//-- apply standard rendering for some modes (e.g., visualization, shadow pass)
+
+		if (mShowVisualization ||
+			(mShadowTechnique == SHADOWTYPE_TEXTURE_MODULATIVE && 
+			mIlluminationStage == IRS_RENDER_TO_TEXTURE))
+		{	
+			IlluminationRenderStage savedStage = mIlluminationStage; 
+
+			if (mShowVisualization) 
+			{
+				// disable illumination stage to prevent rendering shadows
+				mIlluminationStage = IRS_NONE;
+			}
+
+			// standard rendering for shadow maps because of performance
+			SceneManager::_renderVisibleObjects();
+
+			mIlluminationStage = savedStage;
+		}
+		else //-- the hierarchical culling algorithm
+		{
+			// this is also called in TerrainSceneManager: really necessary?
+			//mDestRenderSystem -> setLightingEnabled(false);
+
+			// don't render backgrounds for item buffer
+			if (mUseItemBuffer)
+			{
+				clearSpecialCaseRenderQueues();
+				getRenderQueue()->clear();
+			}
+
+			//-- hierarchical culling
+
+			// the objects of different layers (e.g., background, scene, 
+			// overlay) must be identified and rendered one after another
+
+			//-- render all early skies
+			clearSpecialCaseRenderQueues();
+			addSpecialCaseRenderQueue(RENDER_QUEUE_BACKGROUND);
+			addSpecialCaseRenderQueue(RENDER_QUEUE_SKIES_EARLY);
+			setSpecialCaseRenderQueueMode(SceneManager::SCRQM_INCLUDE);
+
+			SceneManager::_renderVisibleObjects();
+
+#ifdef GTP_VISIBILITY_MODIFIED_OGRE
+			// delete previously rendered content
+			_deleteRenderedQueueGroups();
+#endif
+
+			//-- prepare queue for visible objects (i.e., all but overlay and skies late)
+			clearSpecialCaseRenderQueues();
+			addSpecialCaseRenderQueue(RENDER_QUEUE_SKIES_LATE);
+			addSpecialCaseRenderQueue(RENDER_QUEUE_OVERLAY);
+
+			// exclude this queues from hierarchical rendering
+			setSpecialCaseRenderQueueMode(SceneManager::SCRQM_EXCLUDE);
+
+
+			// set all necessary parameters for 
+			// hierarchical visibility culling and rendering
+			InitVisibilityCulling(mCameraInProgress);
+
+
+			/** 
+			* the hierarchical culling algorithm
+			* for depth pass: we just find objects and update depth buffer
+			* for "delayed" rendering: we render some passes afterwards
+			* e.g., transparents, because they need front-to-back sorting
+			**/
+
+			mVisibilityManager->ApplyVisibilityCulling();
+
+			// delete remaining renderables from queue:
+			// all which are not in mLeavePassesInQueue
+#ifdef GTP_VISIBILITY_MODIFIED_OGRE
+			_deleteRenderedQueueGroups(mLeavePassesInQueue);
+#endif
+
+			//-- reset parameters
+			mIsDepthPassPhase = false;
+			mIsItemBufferPhase = false;
+			mSkipTransparents = false;
+			mIsHierarchicalCulling = false;
+
+			mLeavePassesInQueue = 0;
+
+			// add visible nodes found by the visibility culling algorithm
+			if (mUseDepthPass)
+			{
+				BiHierarchy::NodeList::const_iterator it, end = mVisibleNodes.end();
+				for (it = mVisibleNodes.begin(); it != end; it++)
+				{
+					(*it)->queueVisibleObjects(mHierarchyInterface->GetFrameId(), mCameraInProgress,
+						getRenderQueue(), false, mShowBoxes);
+				}
+			}
+
+			//-- now we can render all remaining queue objects
+			//-- used for depth pass, transparents, overlay
+			clearSpecialCaseRenderQueues();
+
+			SceneManager::_renderVisibleObjects();
+		} // hierarchical culling
+
+
+		// reset ambient light
+		setAmbientLight(savedAmbient);
+
+
+		// almost same effect as below
+		getRenderQueue()->clear(mDeleteQueueAfterRendering);
+
+		//if (!mDeleteQueueAfterRendering)
+		//	getRenderQueue()->clear(true); // finally clear render queue
+		//else
+		//	OGRE_DELETE(mRenderQueue); // HACK: should rather only be cleared ...
+
+		if (0) WriteLog(); // write out stats
+	}
+}
+
+
+void BiHierarchySceneManager::_renderNode(BiHierarchy::NodePtr node, Camera * cam, 
+									 bool onlyShadowCasters, int leavePassesInQueue)
+{
+	RenderQueueGroup *currentGroup = 
+		getRenderQueue()->getQueueGroup(getRenderQueue()->getDefaultQueueGroup());
+	currentGroup->clear(leavePassesInQueue);
+
+	node->queueVisibleObjects(mHierarchyInterface->GetFrameId(), cam, getRenderQueue(),
+		onlyShadowCasters, mShowBoxes);
+	mVisibleNodes.push_back(node);
+
+	_renderQueueGroupObjects(currentGroup, QueuedRenderableCollection::OM_PASS_GROUP);
+}
+
+//-----------------------------------------------------------------------
+const Pass *BiHierarchySceneManager::_setPass(const Pass* pass, bool evenIfSuppressed)
+{
+	if (mRenderMethod == BiHierarchy::BIHRM_INTERNAL)
+	{
+		return SceneManager::_setPass(pass);
+	}
+
+	// TODO: setting vertex program is not efficient
+	//Pass *usedPass = ((mIsDepthPassPhase && !pass->hasVertexProgram()) ? mDepthPass : pass); 
+
+	// set depth fill pass if we currently do not make an aabb occlusion query
+	const bool useDepthPass = 
+		(mIsDepthPassPhase && !mHierarchyInterface->IsBoundingBoxQuery());
+
+	const IlluminationRenderStage savedStage = mIlluminationStage; 
+
+	// set illumination stage to NONE so no shadow material is used 
+	// for depth pass or for occlusion query
+	if (mIsDepthPassPhase || mHierarchyInterface->IsBoundingBoxQuery())
+	{
+		mIlluminationStage = IRS_NONE;
+	}
+
+	// --- set vertex program of current pass in order to set correct depth
+	if (mExecuteVertexProgramForAllPasses && 
+		mIsDepthPassPhase && 
+		pass->hasVertexProgram())
+	{
+		// add vertex program of current pass to depth pass
+		mDepthPass->setVertexProgram(pass->getVertexProgramName());
+
+		if (mDepthPass->hasVertexProgram())
+		{
+			const GpuProgramPtr& prg = mDepthPass->getVertexProgram();
+			// Load this program if not done already
+			if (!prg->isLoaded())
+				prg->load();
+			// Copy params
+			mDepthPass->setVertexProgramParameters(pass->getVertexProgramParameters());
+		}
+	}
+	else if (mDepthPass->hasVertexProgram()) // reset vertex program
+	{
+		mDepthPass->setVertexProgram("");
+	}
+
+	const Pass *usedPass = useDepthPass ? mDepthPass : pass;
+
+	// save old depth write: needed for item buffer
+	const bool IsDepthWrite = usedPass->getDepthWriteEnabled();
+
+	// global option which enables / disables depth writes
+	if (!mEnableDepthWrite)
+	{
+		//	usedPass->setDepthWriteEnabled(false);
+	}
+	//else if (mIsItemBufferPass) {usedPass = mItemBufferPass;}
+
+
+	//-- set actual pass here
+	const Pass *result = SceneManager::_setPass(usedPass);
+
+
+	// reset depth write
+	if (!mEnableDepthWrite)
+	{
+		//	usedPass->setDepthWriteEnabled(IsDepthWrite);
+	}
+
+	// reset illumination stage
+	mIlluminationStage = savedStage;
+
+	return result;
+}
+//-----------------------------------------------------------------------
+void BiHierarchySceneManager::renderBasicQueueGroupObjects(RenderQueueGroup* pGroup,
+													  QueuedRenderableCollection::OrganisationMode om)
+{
+	// Basic render loop
+	// Iterate through priorities
+	RenderQueueGroup::PriorityMapIterator groupIt = pGroup->getIterator();
+
+	while (groupIt.hasMoreElements())
+	{
+		RenderPriorityGroup* pPriorityGrp = groupIt.getNext();
+
+		// Sort the queue first
+		pPriorityGrp->sort(mCameraInProgress);
+
+		// Do solids
+		renderObjects(pPriorityGrp->getSolidsBasic(), om, true);
+
+		// for correct rendering, transparents must be rendered after hierarchical culling
+		// => do nothing
+
+		// Do transparents (always descending)
+		if (mRenderMethod == BiHierarchy::BIHRM_INTERNAL || !mSkipTransparents)
+		{
+			renderObjects(pPriorityGrp->getTransparents(), 
+				QueuedRenderableCollection::OM_SORT_DESCENDING, true);
+		}
+
+
+	}// for each priority
+}
+//-----------------------------------------------------------------------
+bool BiHierarchySceneManager::validatePassForRendering(Pass* pass)
+{
+	if (mRenderMethod == BiHierarchy::BIHRM_INTERNAL)
+	{
+		return SceneManager::validatePassForRendering(pass);
+	}
+
+	// skip all but first pass if we are doing the depth pass
+	if ((mIsDepthPassPhase || mIsItemBufferPhase) && (pass->getIndex() > 0))
+	{
+		return false;
+	}
+	// all but first pass
+	/*else if ((!mIsDepthPassPhase || mIsItemBufferPhase) && (pass->getIndex() != 0))
+	{
+	return false;
+	}*/
+
+	return SceneManager::validatePassForRendering(pass);
+}
+//-----------------------------------------------------------------------
+void BiHierarchySceneManager::_renderQueueGroupObjects(RenderQueueGroup* pGroup, 
+												  QueuedRenderableCollection::OrganisationMode om)
+{
+	if (mRenderMethod == BiHierarchy::BIHRM_INTERNAL || !mIsItemBufferPhase)
+	{
+		SceneManager::_renderQueueGroupObjects(pGroup, om);
+		return;
+	}
+#ifdef  ITEM_BUFFER
+	//-- item buffer
+	//-- item buffer: render objects using false colors
+
+	// Iterate through priorities
+	RenderQueueGroup::PriorityMapIterator groupIt = pGroup->getIterator();
+
+	while (groupIt.hasMoreElements())
+	{
+		RenderItemBuffer(groupIt.getNext());
+	}
+#endif // ITEM_BUFFER
+}
+//-----------------------------------------------------------------------
+void BiHierarchySceneManager::renderAdditiveStencilShadowedQueueGroupObjects(
+	RenderQueueGroup* pGroup, QueuedRenderableCollection::OrganisationMode om)
+{
+	// only render solid passes during hierarchical culling
+	if (mIsHierarchicalCulling)
+	{
+		RenderQueueGroup::PriorityMapIterator groupIt = pGroup->getIterator();
+		LightList lightList;
+
+		while (groupIt.hasMoreElements())
+		{
+			RenderPriorityGroup* pPriorityGrp = groupIt.getNext();
+
+			// Sort the queue first
+			pPriorityGrp->sort(mCameraInProgress);
+
+			// Clear light list
+			lightList.clear();
+
+			// Render all the ambient passes first, no light iteration, no lights
+			/*** msz: no more IRS_AMBIENT, see OgreSceneManager.h ***/
+			// mIlluminationStage = IRS_AMBIENT;
+
+			SceneManager::renderObjects(pPriorityGrp->getSolidsBasic(), om, false, &lightList);
+			// Also render any objects which have receive shadows disabled
+			SceneManager::renderObjects(pPriorityGrp->getSolidsNoShadowReceive(), om, true);
+		}
+	}
+	else // render the rest of the passes
+	{
+		SceneManager::renderAdditiveStencilShadowedQueueGroupObjects(pGroup, om);
+	}
+}
+//-----------------------------------------------------------------------
+void BiHierarchySceneManager::renderModulativeStencilShadowedQueueGroupObjects(
+	RenderQueueGroup* pGroup, QueuedRenderableCollection::OrganisationMode om)
+{
+	if (mIsHierarchicalCulling)
+	{
+		// Iterate through priorities
+		RenderQueueGroup::PriorityMapIterator groupIt = pGroup->getIterator();
+
+		while (groupIt.hasMoreElements())
+		{
+			RenderPriorityGroup* pPriorityGrp = groupIt.getNext();
+
+			// Sort the queue first
+			pPriorityGrp->sort(mCameraInProgress);
+
+			// Do (shadowable) solids
+			SceneManager::renderObjects(pPriorityGrp->getSolidsBasic(), om, true);
+		}
+	}
+	else
+	{
+		SceneManager::renderModulativeStencilShadowedQueueGroupObjects(pGroup, om);
+	}
+}
+//-----------------------------------------------------------------------
+void BiHierarchySceneManager::InitDepthPass()
+{
+	MaterialPtr depthMat = MaterialManager::getSingleton().getByName("Visibility/DepthPass");
+
+	if (depthMat.isNull())
+	{
+		depthMat = MaterialManager::getSingleton().create(
+			"Visibility/DepthPass",
+			ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
+
+		mDepthPass = depthMat->getTechnique(0)->getPass(0);
+		mDepthPass->setColourWriteEnabled(false);
+		mDepthPass->setDepthWriteEnabled(true);
+		mDepthPass->setLightingEnabled(false);
+	}
+	else
+	{
+		mDepthPass = depthMat->getTechnique(0)->getPass(0);
+	}
+}
+//-----------------------------------------------------------------------
+void BiHierarchySceneManager::InitItemBufferPass()
+{
+	MaterialPtr itemBufferMat = MaterialManager::getSingleton().
+		getByName("Visibility/ItemBufferPass");
+
+	if (itemBufferMat.isNull())
+	{
+		// Init
+		itemBufferMat = MaterialManager::getSingleton().create("Visibility/ItemBufferPass",
+			ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
+
+		mItemBufferPass = itemBufferMat->getTechnique(0)->getPass(0);
+		mItemBufferPass->setColourWriteEnabled(true);
+		mItemBufferPass->setDepthWriteEnabled(true);
+		mItemBufferPass->setLightingEnabled(true);
+		//mItemBufferPass->setLightingEnabled(false);
+	}
+	else
+	{
+		mItemBufferPass = itemBufferMat->getTechnique(0)->getPass(0);
+	}
+	//mItemBufferPass->setAmbient(1, 1, 0);
+}
+//-----------------------------------------------------------------------
+void BiHierarchySceneManager::PrepareVisualization(Camera *cam)
+{
+	// add player camera for visualization purpose
+	try 
+	{
+		Camera *c;
+		if ((c = getCamera("PlayerCam")) != NULL)
+		{
+			getRenderQueue()->addRenderable(c);
+		}   
+	}
+	catch (...)
+	{
+		// ignore
+	}
+
+	if (mRenderNodesForViz || mRenderNodesContentForViz)
+	{
+		RenderQueue * queue = getRenderQueue();
+		unsigned long frameid = 0;
+
+		if (mRenderMethod == BiHierarchy::BIHRM_INTERNAL)
+			frameid = Root::getSingleton().getCurrentFrameNumber();
+		else
+			frameid = mHierarchyInterface->GetFrameId();
+
+		for (BiHierarchy::NodeList::iterator it = mVisibleNodes.begin(); it != mVisibleNodes.end(); ++it)
+		{
+			if (mRenderNodesForViz && (*it)->isLeaf())
+			{
+				WireBoundingBox * wirebox = (*it)->getWireBoundingBox();
+				wirebox->setMaterial("BiHierarchy/BoxViz");
+				getRenderQueue()->addRenderable(wirebox);
+			}
+			// add renderables itself
+			if (mRenderNodesContentForViz) 
+			{
+				(*it)->queueVisibleObjects(frameid, cam, queue, false, false);
+			}
+		}
+	}	
+}
+//-----------------------------------------------------------------------
+void BiHierarchySceneManager::InitVisibilityCulling(Camera *cam)
+{
+	// reset culling manager stats
+	mVisibilityManager->GetCullingManager()->InitFrame(mVisualizeCulledNodes);
+
+	// set depth pass flag before rendering
+	mIsDepthPassPhase = mUseDepthPass;
+
+	mIsHierarchicalCulling = true; // during hierarchical culling
+
+	// item buffer needs full ambient lighting to use item colors as unique id
+	if (mUseItemBuffer) 
+	{
+		mIsItemBufferPhase = true;
+		setAmbientLight(ColourValue(1,1,1,1));
+	}
+
+
+	// set passes which are stored in render queue 
+	// for rendering AFTER hierarchical culling, i.e., passes which need 
+	// a special rendering order
+
+	mLeavePassesInQueue = 0;
+
+	// if we have the depth pass or use an item buffer, no passes are left in the queue
+	if (1 && !mUseDepthPass && !mUseItemBuffer)
+	{
+		if (mShadowTechnique == SHADOWTYPE_STENCIL_ADDITIVE)
+		{
+			// TODO: remove this pass because it should be processed during hierarchical culling
+			//mLeavePassesInQueue |= RenderPriorityGroup::SOLID_PASSES_NOSHADOW;
+
+			mLeavePassesInQueue |= RenderPriorityGroup::SOLID_PASSES_DECAL;
+			mLeavePassesInQueue |= RenderPriorityGroup::SOLID_PASSES_DIFFUSE_SPECULAR;
+			mLeavePassesInQueue |= RenderPriorityGroup::TRANSPARENT_PASSES;
+
+			// just render ambient passes
+			/*** msz: no more IRS_AMBIENT, see OgreSceneManager.h ***/
+			// mIlluminationStage = IRS_AMBIENT; 
+			//getRenderQueue()->setSplitPassesByLightingType(true);
+		}
+
+		if (mShadowTechnique == SHADOWTYPE_STENCIL_MODULATIVE)
+		{
+			mLeavePassesInQueue |= RenderPriorityGroup::SOLID_PASSES_NOSHADOW;
+			mLeavePassesInQueue |= RenderPriorityGroup::TRANSPARENT_PASSES;
+		}
+
+		// transparents should be rendered after hierarchical culling to 
+		// provide front-to-back ordering
+		if (mDelayRenderTransparents)
+		{
+			mLeavePassesInQueue |= RenderPriorityGroup::TRANSPARENT_PASSES;
+		}
+	}
+
+	// skip rendering transparents during the hierarchical culling
+	// (because they will be rendered afterwards)
+	mSkipTransparents = 
+		(mIsDepthPassPhase || (mLeavePassesInQueue & RenderPriorityGroup::TRANSPARENT_PASSES));
+
+	// -- initialise interface for rendering traversal of the hierarchy
+	mHierarchyInterface->SetHierarchyRoot(mBiHierarchy->getRoot());
+
+	// possible two cameras (one for culling, one for rendering)
+	mHierarchyInterface->InitTraversal(mCameraInProgress, 
+		/*mCullCamera ? getCamera("CullCamera") : */NULL,
+		mLeavePassesInQueue);
+
+}
+
+//-------------------------------------------------------------------------
+void BiHierarchySceneManager::SetObjectsVisible(const bool visible)
+{
+	GtpVisibilityPreprocessor::ObjectContainer::iterator it, it_end = mObjects.end();
+
+	for (it = mObjects.begin(); it != it_end; ++ it)
+	{
+		OgreMeshInstance *omi = static_cast<OgreMeshInstance *>(*it);
+		Entity *ent = omi->GetEntity();
+
+		ent->setVisible(visible);
+	}
+}
+//-----------------------------------------------------------------------
+bool BiHierarchySceneManager::LoadViewCells(const String &filename)
+{
+	// objects are set to invisible initially
+	SetObjectsVisible(false);
+
+	const string bboxesFilename = 
+		mVisibilityManager->GetVisibilityEnvironment()->getViewCellsFileName();
+
+	// converter between view cell ids and Ogre entites 
+	GtpVisibilityPreprocessor::IndexedBoundingBoxContainer iboxes;
+	BihBoundingBoxConverter bconverter(this);
+
+	// load the view cells and assigns the objects in the pvs to 
+	// the scene objects using the bounding boxes
+	mViewCellsManager = 
+		GtpVisibilityPreprocessor::ViewCellsManager::
+		LoadViewCells(filename, mObjects, false, &bconverter);
+
+	return (mViewCellsManager != NULL);
+}
+//-------------------------------------------------------------------------
+void BiHierarchySceneManager::applyViewCellPvs(GtpVisibilityPreprocessor::ViewCell *vc, 
+													const bool load)
+{	
+	if (!vc) 
+	{	
+		// set everything visible for savety
+
+		// NOTE: should not happen, rather apply view cell 
+		// representing unbounded space then
+		SetObjectsVisible(true);
+		return;
+	}
+
+	//////////
+	//-- set PVS of view cell visible
+	GtpVisibilityPreprocessor::ObjectPvsIterator pit = vc->GetPvs().GetIterator();
+
+	while (pit.HasMoreEntries())
+	{		
+		GtpVisibilityPreprocessor::Intersectable *entry = pit.Next();
+
+		if (entry)
+		{
+			OgreMeshInstance *omi = static_cast<OgreMeshInstance *>(entry);
+			omi->GetEntity()->setVisible(load);
+		}
+	}
+}
+//-------------------------------------------------------------------------
+void BiHierarchySceneManager::updatePvs(Camera *cam)
+{
+	if (!(mViewCellsLoaded && mUseViewCells))
+		return;
+
+	const GtpVisibilityPreprocessor::Vector3 viewPoint = 
+		OgreTypeConverter::ConvertFromOgre(cam->getDerivedPosition());
+
+	GtpVisibilityPreprocessor::ViewCell *newElementary = 
+		mViewCellsManager->GetViewCell(viewPoint);
+
+	// elementary view cell did not change => apply same pvs
+	if (mElementaryViewCell == newElementary)
+		return;
+
+	mElementaryViewCell = newElementary;
+	//LogManager::getSingleton().logMessage("unloading");
+	//-- unload old pvs
+	applyViewCellPvs(mCurrentViewCell, false);
+
+
+	//-- the new view cell
+
+	GtpVisibilityPreprocessor::ViewCell *viewCell;
+
+
+	if (mUseVisibilityFilter)
+	{
+		//-- compute new filtered cell
+		GtpVisibilityPreprocessor::PrVs prvs;
+		mViewCellsManager->GetPrVS(viewPoint, prvs, 5);
+		viewCell = prvs.mViewCell;
+	}
+	else
+	{
+		viewCell = newElementary;
+	}
+	//LogManager::getSingleton().logMessage("loading");
+	//-- load new pvs
+	applyViewCellPvs(viewCell, true);
+
+	// store pvs
+	mCurrentViewCell->SetPvs(viewCell->GetPvs());
+
+	// delete merge tree of filtered view cell
+	if (mUseVisibilityFilter)
+		mViewCellsManager->DeleteLocalMergeTree(viewCell);
+}
+
+//-----------------------------------------------------------------------
+void BiHierarchySceneManager::WriteLog()
+{
+	std::stringstream d;
+
+	d << "Depth pass: " << StringConverter::toString(mUseDepthPass) << ", "
+		<< "Delay transparents: " << StringConverter::toString(mDelayRenderTransparents) << ", "
+		<< "Use optimization: " << StringConverter::toString(mHierarchyInterface->GetTestGeometryForVisibleLeaves()) << ", "
+		<< "Algorithm type: " << mVisibilityManager->GetCullingManagerType() << ", "
+		<< "Hierarchy nodes: " << (mBiHierarchy ? mBiHierarchy->getTreeStats().mNumNodes : 0) << ", " 
+		<< "Traversed nodes: " << mHierarchyInterface->GetNumTraversedNodes() << ", "
+		<< "Rendered nodes: " << mHierarchyInterface->GetNumRenderedNodes() << ", "
+		<< "Query culled nodes: " << mVisibilityManager->GetCullingManager()->GetNumQueryCulledNodes() << ", "
+		<< "Frustum culled nodes: " << mVisibilityManager->GetCullingManager()->GetNumFrustumCulledNodes() << ", "
+		<< "Queries issued: " << mVisibilityManager->GetCullingManager()->GetNumQueriesIssued() << ", "
+		<< "Found objects: " << (int)mVisibleNodes.size() << "\n"
+		;
+
+	LogManager::getSingleton().logMessage(d.str());
+}
+
+/************************************************************************/
+/* Factory for BiHierarchySceneManager                                       */
+/************************************************************************/
+//-----------------------------------------------------------------------
+//-----------------------------------------------------------------------
+const String BiHierarchySceneManagerFactory::FACTORY_TYPE_NAME = "BiHierarchySceneManager";
+//-----------------------------------------------------------------------
+void BiHierarchySceneManagerFactory::initMetaData(void) const
+{
+	mMetaData.typeName = FACTORY_TYPE_NAME;
+	mMetaData.description = "Scene manager that organises the scene based on a kd-tree";
+	mMetaData.sceneTypeMask = 0xFFFF; // support all types of scenes (hopefully)
+	mMetaData.worldGeometrySupported = false;
+}
+//-----------------------------------------------------------------------
+SceneManager* BiHierarchySceneManagerFactory::createInstance(
+	const String& instanceName)
+{
+	return new BiHierarchySceneManager(instanceName, visManager);
+}
+//-----------------------------------------------------------------------
+void BiHierarchySceneManagerFactory::destroyInstance(SceneManager* instance)
+{
+	delete instance;
+}
+
+} // namespace Ogre
+
Index: GTP/trunk/Lib/Vis/OnlineCullingCHC/OGRE/src/OgreBiTerrainVertexProgram.cpp
===================================================================
--- GTP/trunk/Lib/Vis/OnlineCullingCHC/OGRE/src/OgreBiTerrainVertexProgram.cpp	(revision 2348)
+++ GTP/trunk/Lib/Vis/OnlineCullingCHC/OGRE/src/OgreBiTerrainVertexProgram.cpp	(revision 2348)
@@ -0,0 +1,342 @@
+
+/*
+-----------------------------------------------------------------------------
+This source file is part of OGRE
+(Object-oriented Graphics Rendering Engine)
+For the latest info, see http://www.ogre3d.org/
+
+Copyright (c) 2000-2005 The OGRE Team
+Also see acknowledgements in Readme.html
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU Lesser General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA 02111-1307, USA, or go to
+http://www.gnu.org/copyleft/lesser.txt.
+-----------------------------------------------------------------------------
+*/
+
+#include "OgreBiTerrainVertexProgram.h"
+
+namespace Ogre {
+
+    String BiTerrainVertexProgram::mNoFogArbvp1 = 
+        "!!ARBvp1.0\n"
+        "PARAM c5 = { 1, 1, 1, 1 };\n"
+        "#var float4x4 worldViewProj :  : c[0], 4 : 8 : 1\n"
+        "#var float morphFactor :  : c[4] : 9 : 1\n"
+        "TEMP R0;\n"
+        "ATTRIB v17 = vertex.weight;\n"
+        "ATTRIB v25 = vertex.texcoord[1];\n"
+        "ATTRIB v24 = vertex.texcoord[0];\n"
+        "ATTRIB v16 = vertex.position;\n"
+        "PARAM c0[4] = { program.local[0..3] };\n"
+        "PARAM c4 = program.local[4];\n"
+        "	MOV result.texcoord[0], v24;\n"
+        "	MOV result.texcoord[1], v25;\n"
+        "	MOV R0, v16;\n"
+        "	MAD R0.y, v17.x, c4.x, R0.y;\n"
+        "	DP4 result.position.x, c0[0], R0;\n"
+        "	DP4 result.position.y, c0[1], R0;\n"
+        "	DP4 result.position.z, c0[2], R0;\n"
+        "	DP4 result.position.w, c0[3], R0;\n"
+        "	MOV result.color.front.primary, c5.x;\n"
+        "END\n";
+
+	String BiTerrainVertexProgram::mShadowReceiverArbvp1 = 
+		"!!ARBvp1.0\n"
+		"PARAM c[14] = { program.local[0..12], { 1 } };\n"
+		"TEMP R0;\n"
+		"TEMP R1;\n"
+		"TEMP R2;\n"
+		"MOV result.color, c[13].x;\n"
+		"MUL R0.x, vertex.weight, c[12];\n"
+		"ADD R1.y, R0.x, vertex.position;\n"
+		"MOV R1.xzw, vertex.position;\n"
+		"DP4 result.position.w, R1, c[3];\n"
+		"DP4 result.position.z, R1, c[2];\n"
+		"DP4 R0.w, R1, c[7];\n"
+		"DP4 R0.z, R1, c[6];\n"
+		"DP4 R0.y, R1, c[5];\n"
+		"DP4 R0.x, R1, c[4];\n"
+		"DP4 result.position.y, R1, c[1];\n"
+		"DP4 R2.y, R0, c[9];\n"
+		"DP4 R2.z, R0, c[11];\n"
+		"DP4 R2.x, R0, c[8];\n"
+		"RCP R0.x, R2.z;\n"
+		"DP4 result.position.x, R1, c[0];\n"
+		"MUL result.texcoord[0].xy, R2, R0.x;\n"
+		"END\n";
+    String BiTerrainVertexProgram::mLinearFogArbvp1 = 
+        "!!ARBvp1.0\n"
+        "PARAM c5 = { 1, 1, 1, 1 };\n"
+        "#var float4x4 worldViewProj :  : c[0], 4 : 9 : 1\n"
+        "#var float morphFactor :  : c[4] : 10 : 1\n"
+        "TEMP R0, R1;\n"
+        "ATTRIB v17 = vertex.weight;\n"
+        "ATTRIB v25 = vertex.texcoord[1];\n"
+        "ATTRIB v24 = vertex.texcoord[0];\n"
+        "ATTRIB v16 = vertex.position;\n"
+        "PARAM c0[4] = { program.local[0..3] };\n"
+        "PARAM c4 = program.local[4];\n"
+        "	MOV result.texcoord[0], v24;\n"
+        "	MOV result.texcoord[1], v25;\n"
+        "	MOV R1, v16;\n"
+        "	MAD R1.y, v17.x, c4.x, R1.y;\n"
+        "	DP4 R0.x, c0[0], R1;\n"
+        "	DP4 R0.y, c0[1], R1;\n"
+        "	DP4 R0.z, c0[2], R1;\n"
+        "	DP4 R0.w, c0[3], R1;\n"
+        "	MOV result.fogcoord.x, R0.z;\n"
+        "	MOV result.position, R0;\n"
+        "	MOV result.color.front.primary, c5.x;\n"
+        "END\n";
+    String BiTerrainVertexProgram::mExpFogArbvp1 = 
+        "!!ARBvp1.0\n"
+        "PARAM c6 = { 1, 1, 1, 1 };\n"
+        "PARAM c7 = { 2.71828, 0, 0, 0 };\n"
+        "#var float4x4 worldViewProj :  : c[0], 4 : 9 : 1\n"
+        "#var float morphFactor :  : c[4] : 10 : 1\n"
+        "#var float fogDensity :  : c[5] : 11 : 1\n"
+        "TEMP R0, R1;\n"
+        "ATTRIB v17 = vertex.weight;\n"
+        "ATTRIB v25 = vertex.texcoord[1];\n"
+        "ATTRIB v24 = vertex.texcoord[0];\n"
+        "ATTRIB v16 = vertex.position;\n"
+        "PARAM c5 = program.local[5];\n"
+        "PARAM c0[4] = { program.local[0..3] };\n"
+        "PARAM c4 = program.local[4];\n"
+        "	MOV result.texcoord[0], v24;\n"
+        "	MOV result.texcoord[1], v25;\n"
+        "	MOV R1, v16;\n"
+        "	MAD R1.y, v17.x, c4.x, R1.y;\n"
+        "	DP4 R0.x, c0[0], R1;\n"
+        "	DP4 R0.y, c0[1], R1;\n"
+        "	DP4 R0.z, c0[2], R1;\n"
+        "	DP4 R0.w, c0[3], R1;\n"
+        "	MOV result.position, R0;\n"
+        "	MOV result.color.front.primary, c6.x;\n"
+        "	MUL R0.zw, R0.z, c5.x;\n"
+        "	MOV R0.xy, c7.x;\n"
+        "	LIT R0.z, R0;\n"
+        "	RCP result.fogcoord.x, R0.z;\n"
+        "END\n";
+    String BiTerrainVertexProgram::mExp2FogArbvp1 = 
+        "!!ARBvp1.0\n"
+        "PARAM c6 = { 1, 1, 1, 1 };\n"
+        "PARAM c7 = { 0.002, 2.71828, 0, 0 };\n"
+        "#var float4x4 worldViewProj :  : c[0], 4 : 9 : 1\n"
+        "#var float morphFactor :  : c[4] : 10 : 1\n"
+        "#var float fogDensity :  : c[5] : 11 : 1\n"
+        "TEMP R0, R1;\n"
+        "ATTRIB v17 = vertex.weight;\n"
+        "ATTRIB v25 = vertex.texcoord[1];\n"
+        "ATTRIB v24 = vertex.texcoord[0];\n"
+        "ATTRIB v16 = vertex.position;\n"
+        "PARAM c0[4] = { program.local[0..3] };\n"
+        "PARAM c4 = program.local[4];\n"
+        "	MOV result.texcoord[0], v24;\n"
+        "	MOV result.texcoord[1], v25;\n"
+        "	MOV R1, v16;\n"
+        "	MAD R1.y, v17.x, c4.x, R1.y;\n"
+        "	DP4 R0.x, c0[0], R1;\n"
+        "	DP4 R0.y, c0[1], R1;\n"
+        "	DP4 R0.z, c0[2], R1;\n"
+        "	DP4 R0.w, c0[3], R1;\n"
+        "	MOV result.position, R0;\n"
+        "	MOV result.color.front.primary, c6.x;\n"
+        "	MUL R0.x, R0.z, c7.x;\n"
+        "	MUL R0.zw, R0.x, R0.x;\n"
+        "	MOV R0.xy, c7.y;\n"
+        "	LIT R0.z, R0;\n"
+        "	RCP result.fogcoord.x, R0.z;\n"
+        "END\n";
+
+    String BiTerrainVertexProgram::mNoFogVs_1_1 = 
+        "vs_1_1\n"
+        "def c5, 1, 1, 1, 1\n"
+        "//var float4x4 worldViewProj :  : c[0], 4 : 8 : 1\n"
+        "//var float morphFactor :  : c[4] : 9 : 1\n"
+        "dcl_blendweight v1\n"
+        "dcl_texcoord1 v8\n"
+        "dcl_texcoord0 v7\n"
+        "dcl_position v0\n"
+        "	mov oT0.xy, v7\n"
+        "	mov oT1.xy, v8\n"
+        "	mov r0, v0\n"
+        "	mad r0.y, v1.x, c4.x, r0.y\n"
+        "	dp4 oPos.x, c0, r0\n"
+        "	dp4 oPos.y, c1, r0\n"
+        "	dp4 oPos.z, c2, r0\n"
+        "	dp4 oPos.w, c3, r0\n"
+        "	mov oD0, c5.x\n";
+    String BiTerrainVertexProgram::mLinearFogVs_1_1 = 
+        "vs_1_1\n"
+        "def c5, 1, 1, 1, 1\n"
+        "//var float4x4 worldViewProj :  : c[0], 4 : 9 : 1\n"
+        "//var float morphFactor :  : c[4] : 10 : 1\n"
+        "dcl_blendweight v1\n"
+        "dcl_texcoord1 v8\n"
+        "dcl_texcoord0 v7\n"
+        "dcl_position v0\n"
+        "	mov oT0.xy, v7\n"
+        "	mov oT1.xy, v8\n"
+        "	mov r1, v0\n"
+        "	mad r1.y, v1.x, c4.x, r1.y\n"
+        "	dp4 r0.x, c0, r1\n"
+        "	dp4 r0.y, c1, r1\n"
+        "	dp4 r0.z, c2, r1\n"
+        "	dp4 r0.w, c3, r1\n"
+        "	mov oFog, r0.z\n"
+        "	mov oPos, r0\n"
+        "	mov oD0, c5.x\n";
+
+	String BiTerrainVertexProgram::mShadowReceiverVs_1_1 = 
+		"vs_1_1\n"
+		"def c13, 1, 1, 1, 1\n"
+		"dcl_blendweight v1\n"
+		"dcl_texcoord1 v8\n"
+		"dcl_texcoord0 v7\n"
+		"dcl_position v0\n"
+		"mov r1, v0\n"
+		"mad r1.y, v1.x, c12.x, r1.y\n"
+		"dp4 oPos.x, c0, r1\n"
+		"dp4 oPos.y, c1, r1\n"
+		"dp4 oPos.z, c2, r1\n"
+		"dp4 oPos.w, c3, r1\n"
+		"dp4 r0.x, c4, r1\n"
+		"dp4 r0.y, c5, r1\n"
+		"dp4 r0.z, c6, r1\n"
+		"dp4 r0.w, c7, r1\n"
+		"dp4 r1.x, c8, r0\n"
+		"dp4 r1.y, c9, r0\n"
+		"dp4 r1.w, c11, r0\n"
+		"rcp r0.x, r1.w\n"
+		"mul oT0.xy, r1.xy, r0.x\n"
+		"mov oD0, c13\n";
+	String BiTerrainVertexProgram::mExpFogVs_1_1 = 
+        "vs_1_1\n"
+        "def c6, 1, 1, 1, 1\n"
+        "def c7, 2.71828, 0, 0, 0\n"
+        "//var float4x4 worldViewProj :  : c[0], 4 : 9 : 1\n"
+        "//var float morphFactor :  : c[4] : 10 : 1\n"
+        "//var float fogDensity :  : c[5] : 11 : 1\n"
+        "dcl_blendweight v1\n"
+        "dcl_texcoord1 v8\n"
+        "dcl_texcoord0 v7\n"
+        "dcl_position v0\n"
+        "	mov oT0.xy, v7\n"
+        "	mov oT1.xy, v8\n"
+        "	mov r1, v0\n"
+        "	mad r1.y, v1.x, c4.x, r1.y\n"
+        "	dp4 r0.x, c0, r1\n"
+        "	dp4 r0.y, c1, r1\n"
+        "	dp4 r0.z, c2, r1\n"
+        "	dp4 r0.w, c3, r1\n"
+        "	mov oPos, r0\n"
+        "	mov oD0, c6.x\n"
+        "	mul r0.zw, r0.z, c5.x\n"
+        "	mov r0.xy, c7.x\n"
+        "	lit r0.z, r0\n"
+        "	rcp oFog, r0.z\n";
+    String BiTerrainVertexProgram::mExp2FogVs_1_1 = 
+        "vs_1_1\n"
+        "def c6, 1, 1, 1, 1\n"
+        "def c7, 0.002, 2.71828, 0, 0\n"
+        "//var float4x4 worldViewProj :  : c[0], 4 : 9 : 1\n"
+        "//var float morphFactor :  : c[4] : 10 : 1\n"
+        "//var float fogDensity :  : c[5] : 11 : 1\n"
+        "dcl_blendweight v1\n"
+        "dcl_texcoord1 v8\n"
+        "dcl_texcoord0 v7\n"
+        "dcl_position v0\n"
+        "	mov oT0.xy, v7\n"
+        "	mov oT1.xy, v8\n"
+        "	mov r1, v0\n"
+        "	mad r1.y, v1.x, c4.x, r1.y\n"
+        "	dp4 r0.x, c0, r1\n"
+        "	dp4 r0.y, c1, r1\n"
+        "	dp4 r0.z, c2, r1\n"
+        "	dp4 r0.w, c3, r1\n"
+        "	mov oPos, r0\n"
+        "	mov oD0, c6.x\n"
+        "	mul r0.x, r0.z, c7.x\n"
+        "	mul r0.zw, r0.x, r0.x\n"
+        "	mov r0.xy, c7.y\n"
+        "	lit r0.z, r0\n"
+        "	rcp oFog, r0.z\n";
+
+    const String& BiTerrainVertexProgram::getProgramSource(
+        FogMode fogMode, const String syntax, bool shadowReceiver)
+    {
+		if (shadowReceiver)
+		{
+			if (syntax == "arbvp1")
+			{
+				return mShadowReceiverArbvp1;
+			}
+			else
+			{
+				return mShadowReceiverVs_1_1;
+			}
+		}
+		else
+		{
+
+			switch(fogMode)
+			{
+			case FOG_NONE:
+				if (syntax == "arbvp1")
+				{
+					return mNoFogArbvp1;
+				}
+				else
+				{
+					return mNoFogVs_1_1;
+				}
+				break;
+			case FOG_LINEAR:
+				if (syntax == "arbvp1")
+				{
+					return mLinearFogArbvp1;
+				}
+				else
+				{
+					return mLinearFogVs_1_1;
+				}
+				break;
+			case FOG_EXP:
+				if (syntax == "arbvp1")
+				{
+					return mExpFogArbvp1;
+				}
+				else
+				{
+					return mExpFogVs_1_1;
+				}
+				break;
+			case FOG_EXP2:
+				if (syntax == "arbvp1")
+				{
+					return mExp2FogArbvp1;
+				}
+				else
+				{
+					return mExp2FogVs_1_1;
+				}
+				break;
+			};
+		}
+        // default
+        return StringUtil::BLANK;
+
+    }
+}
Index: GTP/trunk/Lib/Vis/OnlineCullingCHC/OGRE/src/OgreBvHierarchy.cpp
===================================================================
--- GTP/trunk/Lib/Vis/OnlineCullingCHC/OGRE/src/OgreBvHierarchy.cpp	(revision 2280)
+++ GTP/trunk/Lib/Vis/OnlineCullingCHC/OGRE/src/OgreBvHierarchy.cpp	(revision 2348)
@@ -882,5 +882,5 @@
 	unsigned long t1, t2, t3, t4;
 
-	mTreeStats.clear();
+	mTreeStats.clear();  
 	
 	// data we want to collect
Index: GTP/trunk/Lib/Vis/OnlineCullingCHC/OGRE/src/OgreBiHierarchyInterface.cpp
===================================================================
--- GTP/trunk/Lib/Vis/OnlineCullingCHC/OGRE/src/OgreBiHierarchyInterface.cpp	(revision 2348)
+++ GTP/trunk/Lib/Vis/OnlineCullingCHC/OGRE/src/OgreBiHierarchyInterface.cpp	(revision 2348)
@@ -0,0 +1,144 @@
+/*
+-----------------------------------------------------------------------------
+This source file is part of the GameTools Project
+http://www.gametools.org
+
+Author: Martin Szydlowski
+-----------------------------------------------------------------------------
+*/
+
+#include "OgreBihRenderable.h"
+#include "OgreBiHierarchySceneManager.h"
+#include "OgreBiHierarchyInterface.h"
+
+namespace Ogre
+{
+
+BiHierarchyInterface::BiHierarchyInterface(BiHierarchySceneManager *sm, RenderSystem *rsys):
+PlatformHierarchyInterface(sm, rsys)
+{
+
+}
+
+bool BiHierarchyInterface::IsLeaf(GtpVisibility::HierarchyNode *node) const
+{
+	return BIHNODEPTR_CAST(node)->isLeaf();
+}
+
+void BiHierarchyInterface::TraverseNode(GtpVisibility::HierarchyNode *node)
+{
+	++ mNumTraversedNodes;
+
+	BiHierarchy::Node * kdnode = BIHNODEPTR_CAST(node);
+
+	// if the node is a leaf and has geometry => render it
+	if (kdnode->isLeaf())
+	{
+		if (!kdnode->isEmpty())
+		{
+			RenderNode(node);
+		}
+	}
+	else
+	{
+		if (kdnode->getLeftChild())
+			mDistanceQueue->push(kdnode->getLeftChild());
+		if (kdnode->getRightChild())
+			mDistanceQueue->push(kdnode->getRightChild());
+	}
+}
+
+void BiHierarchyInterface::RenderNode(GtpVisibility::HierarchyNode *node)
+{
+	BiHierarchy::Node * kdnode = BIHNODEPTR_CAST(node);
+	if (kdnode->lastRendered() != mFrameId)
+	{
+		kdnode->setLastRendered(mFrameId);
+		BiHierarchySceneManager * ksm = static_cast<BiHierarchySceneManager *>(mSceneManager);
+
+		ksm->_renderNode(kdnode, mCamera, mOnlyShadowCasters, mLeavePassesInQueue);
+
+		mVisibleNodes.push_back(node);
+	}
+}
+
+void BiHierarchyInterface::PullUpVisibility(GtpVisibility::HierarchyNode *node) const
+{
+	BiHierarchy::Node * kdnode = BIHNODEPTR_CAST(node);
+
+	while (kdnode && !kdnode->isNodeVisible())
+	{
+		kdnode->setNodeVisible(true);
+		kdnode = kdnode->getParent();
+	}
+}
+
+float BiHierarchyInterface::GetSquaredDistance(GtpVisibility::HierarchyNode *node) const
+{
+	const Vector3 pos = BIHNODEPTR_CAST(node)->mAABB.getCenter();
+	return (mCameraPosition - pos).squaredLength();
+}
+
+AxisAlignedBox * BiHierarchyInterface::GetBoundingBox(GtpVisibility::HierarchyNode *node)
+{
+	// reuse box if node is the same
+	// only create renderable bounding box for new node
+	if (node != mSavedNode)
+	{
+		mSavedNode = node;
+		mBox = BIHNODEPTR_CAST(node)->_getWorldAABB();
+	}
+
+	return &mBox;
+}
+
+bool BiHierarchyInterface::HasGeometry(GtpVisibility::HierarchyNode *node) const
+{
+	return BIHNODEPTR_CAST(node)->hasGeometry();
+}
+
+void BiHierarchyInterface::SetNodeVisible(GtpVisibility::HierarchyNode *node, const bool visible) const
+{
+	BIHNODEPTR_CAST(node)->setNodeVisible(visible);
+}
+
+bool BiHierarchyInterface::IsNodeVisible(GtpVisibility::HierarchyNode *node) const
+{
+	return BIHNODEPTR_CAST(node)->isNodeVisible();
+}
+
+void BiHierarchyInterface::SetLastVisited(GtpVisibility::HierarchyNode *node, const unsigned int frameId) const
+{
+	BIHNODEPTR_CAST(node)->setLastVisited(frameId);
+}
+
+unsigned int BiHierarchyInterface::LastVisited(GtpVisibility::HierarchyNode *node) const
+{
+	return BIHNODEPTR_CAST(node)->lastVisited();
+}
+
+void BiHierarchyInterface::VisualizeCulledNode(GtpVisibility::HierarchyNode *node, 
+												   GtpVisibility::CullingType type) const
+{
+	WireBoundingBox *box = BIHNODEPTR_CAST(node)->getWireBoundingBox();
+
+	if (type == GtpVisibility::FRUSTUM_CULLED)
+	{
+		box->setMaterial("FrustumCulledNodesMaterial");
+	}
+	else // type == GtpVisibility::QUERY_CULLED
+	{
+		box->setMaterial("QueryCulledNodesMaterial");
+	}
+
+	static_cast<BiHierarchySceneManager *>(mSceneManager)->getRenderQueue()->addRenderable(box);
+}
+
+void BiHierarchyInterface::GetNodeGeometryList(GtpVisibility::HierarchyNode *node, 
+												   GeometryVector *geometryList, 
+												   bool includeChildren) 
+{
+	BIHNODEPTR_CAST(node)->getGeometryList(geometryList);
+}
+
+} // namespace Ogre
Index: GTP/trunk/Lib/Vis/OnlineCullingCHC/OGRE/src/OgreBiTerrainPage.cpp
===================================================================
--- GTP/trunk/Lib/Vis/OnlineCullingCHC/OGRE/src/OgreBiTerrainPage.cpp	(revision 2348)
+++ GTP/trunk/Lib/Vis/OnlineCullingCHC/OGRE/src/OgreBiTerrainPage.cpp	(revision 2348)
@@ -0,0 +1,134 @@
+/*
+-----------------------------------------------------------------------------
+This source file is part of OGRE
+(Object-oriented Graphics Rendering Engine)
+For the latest info, see http://www.ogre3d.org/
+
+Copyright (c) 2000-2005 The OGRE Team
+Also see acknowledgements in Readme.html
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU Lesser General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA 02111-1307, USA, or go to
+http://www.gnu.org/copyleft/lesser.txt.
+-----------------------------------------------------------------------------
+*/
+#include "OgreBiTerrainPage.h"
+#include "OgreBiTerrainRenderable.h"
+
+namespace Ogre {
+
+    //-------------------------------------------------------------------------
+    BiTerrainPage::BiTerrainPage(unsigned short numTiles)
+    {
+        tilesPerPage = numTiles;
+        // Set up an empty array of TerrainRenderable pointers
+        int i, j;
+        for ( i = 0; i < tilesPerPage; i++ )
+        {
+            tiles.push_back( BiTerrainRow() );
+
+            for ( j = 0; j < tilesPerPage; j++ )
+            {
+                tiles[ i ].push_back( 0 );
+            }
+        }
+
+        pageSceneNode = 0;
+
+    }
+    //-------------------------------------------------------------------------
+    BiTerrainPage::~BiTerrainPage()
+    {
+        BiTerrain2D::iterator i, iend;
+        iend = tiles.end();
+        for (i = tiles.begin(); i != iend; ++i)
+        {
+            BiTerrainRow::iterator j, jend;
+            jend = i->end();
+            for (j = i->begin(); j != jend; ++j)
+            {
+                delete *j;
+                *j = 0;
+            }
+        }
+
+    }
+    //-------------------------------------------------------------------------
+    void BiTerrainPage::linkNeighbours(void)
+    {
+        //setup the neighbor links.
+
+        for ( size_t j = 0; j < tilesPerPage; j++ )
+        {
+            for ( size_t i = 0; i < tilesPerPage; i++ )
+            {
+                if ( j != tilesPerPage - 1 )
+                {
+                    tiles[ i ][ j ] -> _setNeighbor( BiTerrainRenderable::SOUTH, tiles[ i ][ j + 1 ] );
+                    tiles[ i ][ j + 1 ] -> _setNeighbor( BiTerrainRenderable::NORTH, tiles[ i ][ j ] );
+                }
+
+                if ( i != tilesPerPage - 1 )
+                {
+                    tiles[ i ][ j ] -> _setNeighbor( BiTerrainRenderable::EAST, tiles[ i + 1 ][ j ] );
+                    tiles[ i + 1 ][ j ] -> _setNeighbor( BiTerrainRenderable::WEST, tiles[ i ][ j ] );
+                }
+
+            }
+        }
+    }
+    //-------------------------------------------------------------------------
+    BiTerrainRenderable * BiTerrainPage::getTerrainTile( const Vector3 & pt )
+    {
+        /* Since we don't know if the terrain is square, or has holes, we use a line trace
+        to find the containing tile...
+        */
+
+        BiTerrainRenderable * tile = tiles[ 0 ][ 0 ];
+
+        while ( tile != 0 )
+        {
+            AxisAlignedBox b = tile -> getBoundingBox();
+            const Vector3 *corners = b.getAllCorners();
+
+            if ( pt.x < corners[ 0 ].x )
+                tile = tile -> _getNeighbor( BiTerrainRenderable::WEST );
+            else if ( pt.x > corners[ 4 ].x )
+                tile = tile -> _getNeighbor( BiTerrainRenderable::EAST );
+            else if ( pt.z < corners[ 0 ].z )
+                tile = tile -> _getNeighbor( BiTerrainRenderable::NORTH );
+            else if ( pt.z > corners[ 4 ].z )
+                tile = tile -> _getNeighbor( BiTerrainRenderable::SOUTH );
+            else
+                return tile;
+        }
+
+        return 0;
+    }
+	//-------------------------------------------------------------------------
+	void BiTerrainPage::setRenderQueue(uint8 qid)
+	{
+		for ( size_t j = 0; j < tilesPerPage; j++ )
+		{
+			for ( size_t i = 0; i < tilesPerPage; i++ )
+			{
+				if ( j != tilesPerPage - 1 )
+				{
+					tiles[ i ][ j ]->setRenderQueueGroup(qid);
+				}
+			}
+		}
+	}
+
+}
+
Index: GTP/trunk/Lib/Vis/OnlineCullingCHC/OGRE/src/OgreBiHierarchySceneNode.cpp
===================================================================
--- GTP/trunk/Lib/Vis/OnlineCullingCHC/OGRE/src/OgreBiHierarchySceneNode.cpp	(revision 2348)
+++ GTP/trunk/Lib/Vis/OnlineCullingCHC/OGRE/src/OgreBiHierarchySceneNode.cpp	(revision 2348)
@@ -0,0 +1,191 @@
+/*
+-----------------------------------------------------------------------------
+This source file is part of the GameTools Project
+http://www.gametools.org
+
+Author: Martin Szydlowski
+-----------------------------------------------------------------------------
+*/
+
+#include "OgreBiHierarchySceneNode.h"
+#include "OgreBiHierarchySceneManager.h"
+
+#include <OgreStringConverter.h>
+#include <OgreLogManager.h>
+
+namespace Ogre
+{
+	// TRIVIA: this function is _not_ called for nodes which did not change since the last frame
+	// only exception: the root node ... it's aabb should be null however, so we don't care
+	void BiHierarchySceneNode::_updateBounds(void)
+	{
+		// Reset bounds first
+		mWorldAABB.setNull();
+
+		// Update bounds from own attached objects
+		ObjectMap::iterator i;
+		for (i = mObjectsByName.begin(); i != mObjectsByName.end(); ++i)
+		{
+			// Merge world bounds of each object
+			mWorldAABB.merge(i->second->getWorldBoundingBox(true));
+		}
+
+		if (!mWorldAABB.isNull())
+		{
+			static_cast<BiHierarchySceneManager *>(mCreator)->_updateNode(this);
+		}
+	}
+
+	void BiHierarchySceneNode::computeScene(BihPlaneEventList& events, AxisAlignedBox& aabb, int& nObjects, bool includeChildren)
+	{
+		/**************************************************************************************/
+		/* HACK: do not include camera node, FUCKS UP world AABB really BAD, dunno why        */
+		/* TODO: find a generic solution where MOVABLE nodes are NOT INSERTED into the BiHierarchy */
+		/**************************************************************************************/
+		//if (mName == "PlayerCamNode")
+		//	return;
+
+		// first compute nodes AABB
+		mWorldAABB.setNull();
+		// Update bounds from own attached objects
+		ObjectMap::iterator i;
+		for (i = mObjectsByName.begin(); i != mObjectsByName.end(); ++i)
+		{
+			// ignore objects with no actual geometry like frustums & lights
+			if (getExcludedMovables().find(i->second->getMovableType()) == getExcludedMovables().end())
+			{
+				//LogManager::getSingleton().getLog(BiHierarchy_LOGNAME)->logMessage("Adding movable type " + i->second->getMovableType() + " to BiHierarchy");
+				// Merge world bounds of each object
+				mWorldAABB.merge(i->second->getWorldBoundingBox(true));
+			}
+		}
+
+		// create information for BiHierarchy/SAH
+		if (!mWorldAABB.isNull())
+		{
+			// merge aabb with global AABB
+			aabb.merge(mWorldAABB);
+
+			// increase object count
+			nObjects++;
+
+			// generate events
+			BihPlaneEvent::Dimension axes[] = 
+				{BihPlaneEvent::PED_X, BihPlaneEvent::PED_Y, BihPlaneEvent::PED_Z};
+			Vector3 min = mWorldAABB.getMinimum();
+			Vector3 max = mWorldAABB.getMaximum();
+			for (int i = 0; i < 3; i++)
+			{
+				// 1-D and 2-D objects
+				if (min[i] == max[i])
+				{	
+					//e = new BihPlaneEvent(this, min, axes[i], BihPlaneEvent::PET_ON);
+					//sceneinfo.events.push_back(*e);
+					events.push_back(BihPlaneEvent(this, min, axes[i], BihPlaneEvent::PET_ON));
+				}
+				else
+				{
+					//e = new BihPlaneEvent(this, min, axes[i], BihPlaneEvent::PET_START);
+					//sceneinfo.events.push_back(*e);
+					//e = new BihPlaneEvent(this, max, axes[i], BihPlaneEvent::PET_END);
+					//sceneinfo.events.push_back(*e);
+					events.push_back(BihPlaneEvent(this, min, axes[i], BihPlaneEvent::PET_START));
+					events.push_back(BihPlaneEvent(this, max, axes[i], BihPlaneEvent::PET_END));
+				}
+			}
+		}
+
+		// Merge with children
+		if (includeChildren)
+		{
+			ChildNodeMap::iterator child;
+			for (child = mChildren.begin(); child != mChildren.end(); ++child)
+			{
+				BiHierarchySceneNode* sceneChild = static_cast<BiHierarchySceneNode*>(child->second);
+				sceneChild->computeScene(events, aabb, nObjects);
+			}
+		}
+	}
+
+	//void BiHierarchySceneNode::_findVisibleObjects(Camera* cam, RenderQueue* queue, 
+	//	bool includeChildren, bool displayNodes, bool onlyShadowCasters)
+	void BiHierarchySceneNode::queueObjects(Camera* cam, RenderQueue* queue, bool onlyShadowCasters)
+	{
+		//SceneNode::_findVisibleObjects(cam, queue, includeChildren, displayNodes, onlyShadowCasters);
+
+		// Add all entities
+		ObjectMap::iterator iobj;
+		ObjectMap::iterator iobjend = mObjectsByName.end();
+		for (iobj = mObjectsByName.begin(); iobj != iobjend; ++iobj)
+		{
+			// Tell attached objects about camera position (incase any extra processing they want to do)
+			iobj->second->_notifyCurrentCamera(cam);
+			if (iobj->second->isVisible() &&
+				(!onlyShadowCasters || iobj->second->getCastShadows()))
+			{
+				iobj->second->_updateRenderQueue(queue);
+			}
+		}
+
+		// Check if the bounding box should be shown.
+		// See if our flag is set or if the scene manager flag is set.
+		if (mShowBoundingBox || (mCreator && mCreator->getShowBoundingBoxes())) 
+		{ 
+			_addBoundingBoxToQueue(queue);
+		}
+	}
+
+	void BiHierarchySceneNode::getGeometryList(GeometryVector *geometryList)
+	{
+		SceneNode::ObjectIterator objIt = getAttachedObjectIterator();
+
+		while (objIt.hasMoreElements())
+		{
+			MovableObject *movable = objIt.getNext();
+
+			// we are interested only in the entities, i.e., instances of geometry
+			if (movable->getMovableType() == "Entity")
+			{
+				Entity *ent = static_cast<Entity *>(movable);
+				geometryList->push_back(ent);
+			}
+		}
+	}
+
+	// recalculate the world aabb
+	AxisAlignedBox BiHierarchySceneNode::getBoundingBox(void) const
+	{
+		return mWorldAABB;
+	}
+
+	BiHierarchySceneNode::StringSet& BiHierarchySceneNode::getExcludedMovables(void)
+	{
+		static StringSet sExluded;
+
+		// initialise once
+		if (sExluded.empty())
+		{
+			sExluded.insert("Camera");
+			sExluded.insert("Frustum");
+			sExluded.insert("Light");
+		}
+
+		return sExluded;
+	}
+
+	// DEBUG
+	String BiHierarchySceneNode::dumpToString(void)
+	{
+		String objects;
+		// Add all entities
+		ObjectMap::iterator iobj = mObjectsByName.begin();
+		ObjectMap::iterator iobjend = mObjectsByName.end();
+		while (iobj != iobjend)
+		{
+			objects += " " + iobj->second->getName();
+			iobj++;
+		}
+		objects += " Attached to " + StringConverter::toString(mLeaves.size()) + " KdLeaves.";
+		return objects;
+	}
+}
