Index: code/branches/presentation3/src/libraries/tools/CMakeLists.txt
===================================================================
--- code/branches/presentation3/src/libraries/tools/CMakeLists.txt	(revision 6912)
+++ code/branches/presentation3/src/libraries/tools/CMakeLists.txt	(revision 7073)
@@ -15,4 +15,5 @@
 COMPILATION_END
 )
+#ADD_SUBDIRECTORY(bsp)
 ADD_SUBDIRECTORY(interfaces)
 
Index: code/branches/presentation3/src/libraries/tools/bsp/CMakeLists.txt
===================================================================
--- code/branches/presentation3/src/libraries/tools/bsp/CMakeLists.txt	(revision 7073)
+++ code/branches/presentation3/src/libraries/tools/bsp/CMakeLists.txt	(revision 7073)
@@ -0,0 +1,8 @@
+ADD_SOURCE_FILES(TOOLS_SRC_FILES
+  DemoApp.cpp
+  OgreFramework.cpp
+  OgreGpuCommandBufferFlush.cpp
+  Q3Map.cpp
+  Q3Map_Bezier.cpp
+  Q3Map_BspVertex.cpp
+)
Index: code/branches/presentation3/src/libraries/tools/bsp/DemoApp.cpp
===================================================================
--- code/branches/presentation3/src/libraries/tools/bsp/DemoApp.cpp	(revision 7073)
+++ code/branches/presentation3/src/libraries/tools/bsp/DemoApp.cpp	(revision 7073)
@@ -0,0 +1,602 @@
+//|||||||||||||||||||||||||||||||||||||||||||||||
+
+#include "DemoApp.hpp"
+
+#include <OgreLight.h>
+#include <OgreWindowEventUtilities.h>
+
+//|||||||||||||||||||||||||||||||||||||||||||||||
+
+DemoApp::DemoApp()
+{
+	
+}
+
+//|||||||||||||||||||||||||||||||||||||||||||||||
+
+DemoApp::~DemoApp()
+{
+	StereoConfig_Write() ; // write out the stereo configuration.  Just rough for now, a binary file
+
+	
+}
+
+//|||||||||||||||||||||||||||||||||||||||||||||||
+
+void DemoApp::startDemo()
+{
+	
+	
+	m_bShutdown = false;
+	
+	
+
+
+
+
+
+		new OgreFramework();
+
+		
+
+		OgreFramework::getSingletonPtr()->initOgre("Video Start", this, 0);
+		
+		BZNConfig_Read() ;
+
+		OgreFramework::getSingletonPtr()->AdditionalSetup() ;
+
+		if(!setupDemoScene()) return ; // failure setting up level
+		
+		
+		
+		//runDemo_Default();
+		runDemo_MethodA();
+
+		OgreFramework::getSingletonPtr()->UnloadMap(true) ;
+		//OgreFramework::getSingletonPtr()->OFBug.MessageInt(333) ;
+		//delete OgreFramework::getSingletonPtr(); // causes a crash on exit in 1.7, was ok in 1.6.3
+		//OgreFramework::getSingletonPtr()->
+		
+		OgreFramework::getSingletonPtr()->FinalShutdown() ;
+
+		
+		//OgreFramework::getSingletonPtr()->OFBug.MessageInt(333) ;
+
+}
+
+//|||||||||||||||||||||||||||||||||||||||||||||||
+
+int DemoApp::setupDemoScene()
+{
+	return OgreFramework::getSingletonPtr()->LoadMap() ;
+}
+
+//|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
+
+// four different versions of runDemo() depending on method of GPUBuffer flushing.
+
+int DemoApp::runDemo_Default()
+{
+
+	OgreFramework::getSingletonPtr()->m_pLog->logMessage("Start main loop (Default)...");
+
+//	char chMessage[1024] ;
+	
+	UINT uFrameStartTime=OgreFramework::getSingletonPtr()->m_pTimer->getMilliseconds();
+	UINT uFrameTotalTime=0 ;
+
+	OgreFramework::getSingletonPtr()->m_pRenderWnd->resetStatistics() ;
+	
+	
+
+
+
+	while(!m_bShutdown && !OgreFramework::getSingletonPtr()->isOgreToBeShutDown()) 
+	{
+
+
+
+
+		if(OgreFramework::getSingletonPtr()->m_pRenderWnd->isClosed())m_bShutdown = true;
+
+#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
+			Ogre::WindowEventUtilities::messagePump() ;
+#endif	
+
+
+
+
+		if(OgreFramework::getSingletonPtr()->m_pRenderWnd->isActive())
+		{
+
+
+         // get start time of frame
+         uFrameStartTime=OgreFramework::getSingletonPtr()->m_pTimer->getMicroseconds() ;
+            
+         // update input and physics
+         OgreFramework::getSingletonPtr()->m_pKeyboard->capture();
+         OgreFramework::getSingletonPtr()->m_pMouse->capture();
+         OgreFramework::getSingletonPtr()->updateOgre(uFrameTotalTime/1000.0f);
+
+
+         // render the frame
+				 OgreFramework::getSingletonPtr()->UpdateRenderTargets() ;
+
+
+
+				
+
+			// calculate frame time.
+			uFrameTotalTime=OgreFramework::getSingletonPtr()->m_pTimer->getMicroseconds()-uFrameStartTime ;
+
+		}
+		else
+		{
+			Sleep(1000);
+		}
+
+
+	}
+
+
+
+	OgreFramework::getSingletonPtr()->m_pLog->logMessage("Main loop quit (Default)");
+	OgreFramework::getSingletonPtr()->m_pLog->logMessage("Shutdown OGRE...");
+
+
+	return 1 ;
+}
+
+int DemoApp::runDemo_MethodA()
+{
+
+	OgreFramework::getSingletonPtr()->m_pLog->logMessage("Start main loop (MethodA)...");
+
+//	char chMessage[1024] ;
+	
+	UINT uFrameStartTime=OgreFramework::getSingletonPtr()->m_pTimer->getMilliseconds();
+	UINT uFrameTotalTime=0 ;
+
+	OgreFramework::getSingletonPtr()->m_pRenderWnd->resetStatistics() ;
+	
+	
+
+
+	/////////////////////////////////////////////////////////////////////////////////////////////
+	// use a renderQueueListener with 1 to 4 HardwareOcclusionQueries to prevent the CPU from 
+	// getting too far ahead of the GPU and causing input lag from keyboard and mouse.
+	// thanks to Sinbad for this suggestion and code outline.
+	// We aren't actually doing Hardware Occlusion Culling, just exploiting the way we can
+	// make it flush the GPU buffer for prior frames.
+	// Messing with the GPU command buffer can be turned off completely by seting m_nMaxGPUQuery to 0 
+
+	
+	// get the maximum gpu queries to be used.  
+	int nMaxGPUQuery=OgreFramework::getSingletonPtr()->m_nMaxGPUQuery ;
+	unsigned int dummy=0 ;
+	int nNewQuery=0 ;
+	int nOldQuery=0 ;
+	rQueueListener* rqListener=NULL ;
+	Ogre::HardwareOcclusionQuery** pHOQ=NULL ;
+
+	int nQueryReady[MAXGPUQUERY] ;
+		
+
+	if(nMaxGPUQuery!=0) // if querying is turned on
+	{
+		// make sure it is in range.
+		if(nMaxGPUQuery<1) 
+			nMaxGPUQuery=1 ;
+		else
+			if(nMaxGPUQuery>MAXGPUQUERY) 
+			nMaxGPUQuery=MAXGPUQUERY ;
+
+
+		rqListener = new rQueueListener ;
+		
+		OgreFramework::getSingletonPtr()->m_pSceneMgr->addRenderQueueListener(rqListener) ;
+
+		// create our queries
+		pHOQ=rqListener->Query ;
+		for(nNewQuery=0 ; nNewQuery<nMaxGPUQuery ; nNewQuery++)
+			pHOQ[nNewQuery] = OgreFramework::getSingletonPtr()->m_pRoot->getRenderSystem()->createHardwareOcclusionQuery() ;
+
+		nNewQuery=nOldQuery-1 ;
+		if(nNewQuery<0)
+			nNewQuery+=nMaxGPUQuery ;
+
+		// set the queries to not ready
+		for(int nReadyLoop=0 ; nReadyLoop<MAXGPUQUERY ; nReadyLoop++)
+			nQueryReady[nReadyLoop]=0 ;
+	}
+	//
+	/////////////////////////////////////////////////////////////////////////////////////////////
+
+
+
+	while(!m_bShutdown && !OgreFramework::getSingletonPtr()->isOgreToBeShutDown()) 
+	{
+
+
+
+
+		if(OgreFramework::getSingletonPtr()->m_pRenderWnd->isClosed())m_bShutdown = true;
+
+#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
+			Ogre::WindowEventUtilities::messagePump() ;
+#endif	
+
+
+			//OgreFramework::getSingletonPtr()->m_pLog->logMessage("Update START");
+
+
+		if(OgreFramework::getSingletonPtr()->m_pRenderWnd->isActive())
+		{
+
+			// get start time of frame
+			uFrameStartTime=OgreFramework::getSingletonPtr()->m_pTimer->getMicroseconds() ;
+				
+			// update input and physics
+			OgreFramework::getSingletonPtr()->m_pKeyboard->capture();
+			OgreFramework::getSingletonPtr()->m_pMouse->capture();
+			OgreFramework::getSingletonPtr()->updateOgre(uFrameTotalTime/1000.0f);
+
+			
+			if(nMaxGPUQuery==0) // querying the GPU command buffer is disabled
+			{
+				// render the frame
+				OgreFramework::getSingletonPtr()->UpdateRenderTargets() ;
+			}
+			else								// querying the GPU command buffer is enabled
+			{
+				// increment the buffer.  
+				nNewQuery=nOldQuery ;
+				nOldQuery++ ;
+				if(nOldQuery==nMaxGPUQuery)
+					nOldQuery=0 ;
+
+
+				// define query beginning for this frame
+				pHOQ[ nNewQuery ]->beginOcclusionQuery() ;
+
+				// render the frame
+				//OgreFramework::getSingletonPtr()->m_pRoot->renderOneFrame();
+				OgreFramework::getSingletonPtr()->UpdateRenderTargets() ;
+
+				// define query end for this frame
+				pHOQ[ nNewQuery ]->endOcclusionQuery() ;
+				nQueryReady[ nNewQuery ]=1 ;
+			
+
+
+				// pull query for a prior frame.  Flushes GPU command buffer up to the end of a prior frame but no further.
+				if(nQueryReady[ nOldQuery ])
+				{
+					pHOQ[ nOldQuery ]->pullOcclusionQuery(&dummy) ;
+					nQueryReady[ nOldQuery ]=0 ;
+				}
+			}
+			
+
+
+			// calculate frame time.
+			uFrameTotalTime=OgreFramework::getSingletonPtr()->m_pTimer->getMicroseconds()-uFrameStartTime ;
+
+		}
+		else
+		{
+			Sleep(1000);
+		}
+
+
+	}
+
+	if(nMaxGPUQuery>0) // if necessary, clean up our HOQ queries and renderQueueListener
+	{
+	
+		for(nNewQuery=0 ; nNewQuery<nMaxGPUQuery ; nNewQuery++)
+			OgreFramework::getSingletonPtr()->m_pRoot->getRenderSystem()->destroyHardwareOcclusionQuery( pHOQ[nNewQuery] ) ;
+		
+		OgreFramework::getSingletonPtr()->m_pSceneMgr->removeRenderQueueListener(rqListener) ;
+		delete rqListener ;
+	}
+
+
+	OgreFramework::getSingletonPtr()->m_pLog->logMessage("Main loop quit (MethodA)");
+	OgreFramework::getSingletonPtr()->m_pLog->logMessage("Shutdown OGRE...");
+
+
+	return 1 ;
+}
+
+int DemoApp::runDemo_MethodB()
+{
+
+	OgreFramework::getSingletonPtr()->m_pLog->logMessage("Start main loop (MethodB)...");
+
+//	char chMessage[1024] ;
+	
+	UINT uFrameStartTime=OgreFramework::getSingletonPtr()->m_pTimer->getMilliseconds();
+	UINT uFrameTotalTime=0 ;
+
+	OgreFramework::getSingletonPtr()->m_pRenderWnd->resetStatistics() ;
+	
+
+	
+	int numberOfQueuedFrames=1 ;
+	bool GPUBufferSetupDone=false ;
+
+
+	while(!m_bShutdown && !OgreFramework::getSingletonPtr()->isOgreToBeShutDown()) 
+	{
+
+
+
+
+		if(OgreFramework::getSingletonPtr()->m_pRenderWnd->isClosed())m_bShutdown = true;
+
+#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
+			Ogre::WindowEventUtilities::messagePump() ;
+#endif	
+
+
+		if(OgreFramework::getSingletonPtr()->m_pRenderWnd->isActive())
+		{
+
+
+			   if(GPUBufferSetupDone==false) // I added it here because I assume I can be very sure there's an active render window by now.
+         {
+            GPUBufferSetupDone=true ;
+            mBufferFlush.start(numberOfQueuedFrames);
+         }
+
+         // get start time of frame
+         uFrameStartTime=OgreFramework::getSingletonPtr()->m_pTimer->getMicroseconds() ;
+            
+         // update input and physics
+         OgreFramework::getSingletonPtr()->m_pKeyboard->capture();
+         OgreFramework::getSingletonPtr()->m_pMouse->capture();
+         OgreFramework::getSingletonPtr()->updateOgre(uFrameTotalTime/1000.0f);
+
+
+         // render the frame
+				 OgreFramework::getSingletonPtr()->UpdateRenderTargets() ;
+
+
+
+
+			// calculate frame time.
+			uFrameTotalTime=OgreFramework::getSingletonPtr()->m_pTimer->getMicroseconds()-uFrameStartTime ;
+
+		}
+		else
+		{
+			Sleep(1000);
+		}
+
+
+
+	}
+
+
+	mBufferFlush.stop() ;
+
+	OgreFramework::getSingletonPtr()->m_pLog->logMessage("Main loop quit (MethodB)");
+	OgreFramework::getSingletonPtr()->m_pLog->logMessage("Shutdown OGRE...");
+
+
+	return 1 ;
+}
+
+
+int DemoApp::runDemo_MethodC()
+{
+
+	OgreFramework::getSingletonPtr()->m_pLog->logMessage("Start main loop (MethodC)...");
+
+	char chMessage[1024] ;
+	
+	int uFrameStartTime=OgreFramework::getSingletonPtr()->m_pTimer->getMilliseconds();
+	int uFrameRenderTime=0 ;
+	int uFrameTargetTime=10000 ;
+
+	OgreFramework::getSingletonPtr()->m_pRenderWnd->resetStatistics() ;
+	
+	
+
+	int uSleepTime=0 ;
+
+	while(!m_bShutdown && !OgreFramework::getSingletonPtr()->isOgreToBeShutDown()) 
+	{
+
+
+
+
+		if(OgreFramework::getSingletonPtr()->m_pRenderWnd->isClosed())m_bShutdown = true;
+
+#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
+			Ogre::WindowEventUtilities::messagePump() ;
+#endif	
+
+
+
+
+		if(OgreFramework::getSingletonPtr()->m_pRenderWnd->isActive())
+		{
+
+
+       // get start time of frame
+       uFrameStartTime=OgreFramework::getSingletonPtr()->m_pTimer->getMicroseconds() ;
+            
+         // update input and physics
+         OgreFramework::getSingletonPtr()->m_pKeyboard->capture();
+         OgreFramework::getSingletonPtr()->m_pMouse->capture();
+         OgreFramework::getSingletonPtr()->updateOgre(uFrameTargetTime/1000.0f);
+
+
+         // render the frame
+				 OgreFramework::getSingletonPtr()->UpdateRenderTargets() ;
+
+
+			// calculate frame time.
+			uFrameRenderTime=OgreFramework::getSingletonPtr()->m_pTimer->getMicroseconds()-uFrameStartTime ;
+			
+
+			uFrameTargetTime-=1000 ;
+			if(uFrameTargetTime<0) uFrameTargetTime=0 ;
+
+			if(uFrameRenderTime>uFrameTargetTime-10000)
+				uFrameTargetTime=uFrameRenderTime+20000 ;
+
+			if(uFrameTargetTime>100000) uFrameTargetTime=100000 ;
+
+
+			
+
+
+			uSleepTime=(int)((uFrameTargetTime-uFrameRenderTime)/1000.0f) ;
+			if(uSleepTime>0) Sleep(uSleepTime) ;
+
+			sprintf(chMessage, "render %i, target %i, sleep %i", uFrameRenderTime, uFrameTargetTime, uSleepTime) ;
+			OgreFramework::getSingletonPtr()->m_pLog->logMessage(chMessage);
+
+		}
+		else
+		{
+			Sleep(1000);
+		}
+
+
+	}
+
+
+
+	OgreFramework::getSingletonPtr()->m_pLog->logMessage("Main loop quit (MethodC)");
+	OgreFramework::getSingletonPtr()->m_pLog->logMessage("Shutdown OGRE...");
+
+
+	return 1 ;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+
+
+
+
+
+void DemoApp::ResetSectionTimer()
+{
+	m_uLastTime=OgreFramework::getSingletonPtr()->m_pTimer->getMicroseconds() ;
+}
+
+UINT DemoApp::GetSectionTimer()
+{
+	UINT uThisTime=OgreFramework::getSingletonPtr()->m_pTimer->getMicroseconds() ;
+	UINT uSectionTime=uThisTime-m_uLastTime ;
+	m_uLastTime=uThisTime ;
+	return uSectionTime ;
+}
+
+//|||||||||||||||||||||||||||||||||||||||||||||||
+
+
+bool DemoApp::keyPressed(const OIS::KeyEvent &keyEventRef)
+{
+	
+	OgreFramework::getSingletonPtr()->keyPressed(keyEventRef);
+	
+	if(OgreFramework::getSingletonPtr()->m_pKeyboard->isKeyDown(OIS::KC_F))
+	{
+		 //do something
+	}
+	
+	return true;
+}
+
+//|||||||||||||||||||||||||||||||||||||||||||||||
+
+bool DemoApp::keyReleased(const OIS::KeyEvent &keyEventRef)
+{
+	OgreFramework::getSingletonPtr()->keyReleased(keyEventRef);
+	
+	return true;
+}
+
+
+//|||||||||||||||||||||||||||||||||||||||||||||||
+
+void DemoApp::StereoConfig_Write( void )
+{
+	int fh;
+	if( (fh = _open( "stereoscopic.cfg", _O_RDWR | _O_BINARY | _O_CREAT | _O_TRUNC, 
+                               _S_IREAD | _S_IWRITE )) != -1 )
+	{
+		write( fh, (void*)&OgreFramework::getSingletonPtr()->m_nGoggleMode,						sizeof(int)) ;
+		write( fh, (void*)&OgreFramework::getSingletonPtr()->m_flGoggleZPos,					sizeof(float))	;
+		write( fh, (void*)&OgreFramework::getSingletonPtr()->m_flGoggleXGap,					sizeof(float))	;
+		write( fh, (void*)&OgreFramework::getSingletonPtr()->m_flGoggleXScale,				sizeof(float))	;
+		write( fh, (void*)&OgreFramework::getSingletonPtr()->m_flGoggleYScale,				sizeof(float))	;
+		_close( fh );
+	}
+}
+bool DemoApp::StereoConfig_Read(void)
+{
+ 	int fh;
+	bool found_file=true ;
+
+	// Open file for input
+	if( (fh = _open( "stereoscopic.cfg", _O_RDONLY | _O_BINARY )) == -1 )
+	{
+		found_file=false ;
+	}
+	else
+	{
+		read( fh, (void*)&OgreFramework::getSingletonPtr()->m_nGoggleMode,						sizeof(int)) ;
+		read( fh, (void*)&OgreFramework::getSingletonPtr()->m_flGoggleZPos,						sizeof(float))	;
+		read( fh, (void*)&OgreFramework::getSingletonPtr()->m_flGoggleXGap,						sizeof(float))	;
+		read( fh, (void*)&OgreFramework::getSingletonPtr()->m_flGoggleXScale,					sizeof(float))	;
+		read( fh, (void*)&OgreFramework::getSingletonPtr()->m_flGoggleYScale,					sizeof(float))	;
+		_close( fh );
+	 }//  end if else
+	return found_file ;
+}
+
+
+void DemoApp::BZNConfig_Write(void)
+{
+	int fh;
+	if( (fh = _open( "bzn.cfg", _O_RDWR | _O_BINARY | _O_CREAT | _O_TRUNC, 
+                               _S_IREAD | _S_IWRITE )) != -1 )
+	{
+		write( fh, (void*)OgreFramework::getSingletonPtr()->m_chBZNConfig,						BZN_CONFIG_SIZE) ;
+		_close( fh );
+	}
+}
+
+bool DemoApp::BZNConfig_Read(void)
+{
+
+	int fh;
+	bool found_file=true ;
+
+	// make sure the config memory is blank
+	ZeroMemory((void*)OgreFramework::getSingletonPtr()->m_chBZNConfig, BZN_CONFIG_SIZE) ;
+
+	// Open file for input
+	if( (fh = _open( "bzn.cfg", _O_RDONLY | _O_BINARY )) == -1 )
+	{
+		found_file=false ;
+	}
+	else
+	{
+		read( fh, (void*)OgreFramework::getSingletonPtr()->m_chBZNConfig,						BZN_CONFIG_SIZE) ;
+		_close( fh );
+		OgreFramework::getSingletonPtr()->ParseBZNConfig() ;
+	 }//  end if else
+	
+	
+	return found_file ;
+
+}
Index: code/branches/presentation3/src/libraries/tools/bsp/DemoApp.hpp
===================================================================
--- code/branches/presentation3/src/libraries/tools/bsp/DemoApp.hpp	(revision 7073)
+++ code/branches/presentation3/src/libraries/tools/bsp/DemoApp.hpp	(revision 7073)
@@ -0,0 +1,57 @@
+//|||||||||||||||||||||||||||||||||||||||||||||||
+
+#ifndef OGRE_DEMO_HPP
+#define OGRE_DEMO_HPP
+
+//|||||||||||||||||||||||||||||||||||||||||||||||
+
+#include "OgreFramework.hpp"
+
+// for loading/saving the stereo config file
+#include <io.h>
+#include <fcntl.h>      /* Needed only for _O_RDWR definition */
+#include <sys/stat.h>
+
+//|||||||||||||||||||||||||||||||||||||||||||||||
+
+class DemoApp : public OIS::KeyListener
+{
+public:
+	DemoApp();
+	~DemoApp();
+
+	void startDemo();
+	
+	bool keyPressed(const OIS::KeyEvent &keyEventRef);
+	bool keyReleased(const OIS::KeyEvent &keyEventRef);
+
+private:
+	int setupDemoScene();
+	int runDemo_Default(); // no gpu buffer flushing code
+	int runDemo_MethodA(); // my HOQ gpu buffer flushing code
+	int runDemo_MethodB(); // ogre HOQ gpu buffer flushing code
+	int runDemo_MethodC(); // my blit gpu buffer flushing code
+
+	UINT m_uLastTime ;
+	void ResetSectionTimer() ;
+	UINT GetSectionTimer() ;
+
+
+	void StereoConfig_Write( void ) ;
+	bool StereoConfig_Read(void) ;
+
+	void BZNConfig_Write(void) ;
+	bool BZNConfig_Read(void) ;
+
+	
+
+	Ogre::GpuCommandBufferFlush mBufferFlush;
+
+	bool						m_bShutdown;
+};
+
+//|||||||||||||||||||||||||||||||||||||||||||||||
+
+#endif 
+
+//|||||||||||||||||||||||||||||||||||||||||||||||
Index: code/branches/presentation3/src/libraries/tools/bsp/ExampleApplication.h
===================================================================
--- code/branches/presentation3/src/libraries/tools/bsp/ExampleApplication.h	(revision 7073)
+++ code/branches/presentation3/src/libraries/tools/bsp/ExampleApplication.h	(revision 7073)
@@ -0,0 +1,257 @@
+/*
+-----------------------------------------------------------------------------
+This source file is part of OGRE
+(Object-oriented Graphics Rendering Engine)
+For the latest info, see http://www.ogre3d.org/
+
+Copyright (c) 2000-2006 Torus Knot Software Ltd
+Also see acknowledgements in Readme.html
+
+You may use this sample code for anything you like, it is not covered by the
+LGPL like the rest of the engine.
+-----------------------------------------------------------------------------
+*/
+/*
+-----------------------------------------------------------------------------
+Filename:    ExampleApplication.h
+Description: Base class for all the OGRE examples
+-----------------------------------------------------------------------------
+*/
+
+#ifndef __ExampleApplication_H__
+#define __ExampleApplication_H__
+
+#include "Ogre.h"
+#include "OgreConfigFile.h"
+#include "ExampleFrameListener.h"
+
+#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
+#include <CoreFoundation/CoreFoundation.h>
+
+// This function will locate the path to our application on OS X,
+// unlike windows you can not rely on the curent working directory
+// for locating your configuration files and resources.
+std::string macBundlePath()
+{
+    char path[1024];
+    CFBundleRef mainBundle = CFBundleGetMainBundle();
+    assert(mainBundle);
+
+    CFURLRef mainBundleURL = CFBundleCopyBundleURL(mainBundle);
+    assert(mainBundleURL);
+
+    CFStringRef cfStringRef = CFURLCopyFileSystemPath( mainBundleURL, kCFURLPOSIXPathStyle);
+    assert(cfStringRef);
+
+    CFStringGetCString(cfStringRef, path, 1024, kCFStringEncodingASCII);
+
+    CFRelease(mainBundleURL);
+    CFRelease(cfStringRef);
+
+    return std::string(path);
+}
+#endif
+
+using namespace Ogre;
+
+/** Base class which manages the standard startup of an Ogre application.
+    Designed to be subclassed for specific examples if required.
+*/
+class ExampleApplication
+{
+public:
+    /// Standard constructor
+    ExampleApplication()
+    {
+        mFrameListener = 0;
+        mRoot = 0;
+		// Provide a nice cross platform solution for locating the configuration files
+		// On windows files are searched for in the current working directory, on OS X however
+		// you must provide the full path, the helper function macBundlePath does this for us.
+#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
+		mResourcePath = macBundlePath() + "/Contents/Resources/";
+#else
+		mResourcePath = "";
+#endif
+    }
+    /// Standard destructor
+    virtual ~ExampleApplication()
+    {
+        if (mFrameListener)
+            delete mFrameListener;
+        if (mRoot)
+            OGRE_DELETE mRoot;
+    }
+
+    /// Start the example
+    virtual void go(void)
+    {
+        if (!setup())
+            return;
+
+        mRoot->startRendering();
+
+        // clean up
+        destroyScene();
+    }
+
+protected:
+    Root *mRoot;
+    Camera* mCamera;
+    SceneManager* mSceneMgr;
+    ExampleFrameListener* mFrameListener;
+    RenderWindow* mWindow;
+	Ogre::String mResourcePath;
+
+    // These internal methods package up the stages in the startup process
+    /** Sets up the application - returns false if the user chooses to abandon configuration. */
+    virtual bool setup(void)
+    {
+
+		String pluginsPath;
+		// only use plugins.cfg if not static
+#ifndef OGRE_STATIC_LIB
+		pluginsPath = mResourcePath + "plugins.cfg";
+#endif
+		
+        mRoot = OGRE_NEW Root(pluginsPath, 
+            mResourcePath + "ogre.cfg", mResourcePath + "Ogre.log");
+
+        setupResources();
+
+        bool carryOn = configure();
+        if (!carryOn) return false;
+
+        chooseSceneManager();
+        createCamera();
+        createViewports();
+
+        // Set default mipmap level (NB some APIs ignore this)
+        TextureManager::getSingleton().setDefaultNumMipmaps(5);
+
+		// Create any resource listeners (for loading screens)
+		createResourceListener();
+		// Load resources
+		loadResources();
+
+		// Create the scene
+        createScene();
+
+        createFrameListener();
+
+        return true;
+
+    }
+    /** Configures the application - returns false if the user chooses to abandon configuration. */
+    virtual bool configure(void)
+    {
+        // Show the configuration dialog and initialise the system
+        // You can skip this and use root.restoreConfig() to load configuration
+        // settings if you were sure there are valid ones saved in ogre.cfg
+        if(mRoot->showConfigDialog())
+        {
+            // If returned true, user clicked OK so initialise
+            // Here we choose to let the system create a default rendering window by passing 'true'
+            mWindow = mRoot->initialise(true);
+            return true;
+        }
+        else
+        {
+            return false;
+        }
+    }
+
+    virtual void chooseSceneManager(void)
+    {
+        // Create the SceneManager, in this case a generic one
+        mSceneMgr = mRoot->createSceneManager(ST_GENERIC, "ExampleSMInstance");
+    }
+    virtual void createCamera(void)
+    {
+        // Create the camera
+        mCamera = mSceneMgr->createCamera("PlayerCam");
+
+        // Position it at 500 in Z direction
+        mCamera->setPosition(Vector3(0,0,500));
+        // Look back along -Z
+        mCamera->lookAt(Vector3(0,0,-300));
+        mCamera->setNearClipDistance(5);
+
+    }
+    virtual void createFrameListener(void)
+    {
+        mFrameListener= new ExampleFrameListener(mWindow, mCamera);
+        mFrameListener->showDebugOverlay(true);
+        mRoot->addFrameListener(mFrameListener);
+    }
+
+    virtual void createScene(void) = 0;    // pure virtual - this has to be overridden
+
+    virtual void destroyScene(void){}    // Optional to override this
+
+    virtual void createViewports(void)
+    {
+        // Create one viewport, entire window
+        Viewport* vp = mWindow->addViewport(mCamera);
+        vp->setBackgroundColour(ColourValue(0,0,0));
+
+        // Alter the camera aspect ratio to match the viewport
+        mCamera->setAspectRatio(
+            Real(vp->getActualWidth()) / Real(vp->getActualHeight()));
+    }
+
+    /// Method which will define the source of resources (other than current folder)
+    virtual void setupResources(void)
+    {
+        // Load resource paths from config file
+        ConfigFile cf;
+        cf.load(mResourcePath + "resources.cfg");
+
+        // Go through all sections & settings in the file
+        ConfigFile::SectionIterator seci = cf.getSectionIterator();
+
+        String secName, typeName, archName;
+        while (seci.hasMoreElements())
+        {
+            secName = seci.peekNextKey();
+            ConfigFile::SettingsMultiMap *settings = seci.getNext();
+            ConfigFile::SettingsMultiMap::iterator i;
+            for (i = settings->begin(); i != settings->end(); ++i)
+            {
+                typeName = i->first;
+                archName = i->second;
+#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
+                // OS X does not set the working directory relative to the app,
+                // In order to make things portable on OS X we need to provide
+                // the loading with it's own bundle path location
+                ResourceGroupManager::getSingleton().addResourceLocation(
+                    String(macBundlePath() + "/" + archName), typeName, secName);
+#else
+                ResourceGroupManager::getSingleton().addResourceLocation(
+                    archName, typeName, secName);
+#endif
+            }
+        }
+    }
+
+	/// Optional override method where you can create resource listeners (e.g. for loading screens)
+	virtual void createResourceListener(void)
+	{
+
+	}
+
+	/// Optional override method where you can perform resource group loading
+	/// Must at least do ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
+	virtual void loadResources(void)
+	{
+		// Initialise, parse scripts etc
+		ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
+
+	}
+
+
+
+};
+
+
+#endif
Index: code/branches/presentation3/src/libraries/tools/bsp/ExampleFrameListener.h
===================================================================
--- code/branches/presentation3/src/libraries/tools/bsp/ExampleFrameListener.h	(revision 7073)
+++ code/branches/presentation3/src/libraries/tools/bsp/ExampleFrameListener.h	(revision 7073)
@@ -0,0 +1,419 @@
+/*
+-----------------------------------------------------------------------------
+This source file is part of OGRE
+    (Object-oriented Graphics Rendering Engine)
+For the latest info, see http://www.ogre3d.org/
+
+Copyright (c) 2000-2006 Torus Knot Software Ltd
+Also see acknowledgements in Readme.html
+
+You may use this sample code for anything you like, it is not covered by the
+LGPL like the rest of the engine.
+-----------------------------------------------------------------------------
+*/
+/*
+-----------------------------------------------------------------------------
+Filename:    ExampleFrameListener.h
+Description: Defines an example frame listener which responds to frame events.
+This frame listener just moves a specified camera around based on
+keyboard and mouse movements.
+Mouse:    Freelook
+W or Up:  Forward
+S or Down:Backward
+A:        Step left
+D:        Step right
+             PgUp:     Move upwards
+             PgDown:   Move downwards
+             F:        Toggle frame rate stats on/off
+			 R:        Render mode
+             T:        Cycle texture filtering
+                       Bilinear, Trilinear, Anisotropic(8)
+             P:        Toggle on/off display of camera position / orientation
+-----------------------------------------------------------------------------
+*/
+
+#ifndef __ExampleFrameListener_H__
+#define __ExampleFrameListener_H__
+
+#include "Ogre.h"
+#include "OgreStringConverter.h"
+#include "OgreException.h"
+
+//Use this define to signify OIS will be used as a DLL
+//(so that dll import/export macros are in effect)
+#ifndef OIS_DYNAMIC_LIB
+#  define OIS_DYNAMIC_LIB
+#endif
+#include <OIS/OIS.h>
+
+using namespace Ogre;
+
+class ExampleFrameListener: public FrameListener, public WindowEventListener
+{
+protected:
+	virtual void updateStats(void)
+	{
+		static String currFps = "Current FPS: ";
+		static String avgFps = "Average FPS: ";
+		static String bestFps = "Best FPS: ";
+		static String worstFps = "Worst FPS: ";
+		static String tris = "Triangle Count: ";
+		static String batches = "Batch Count: ";
+
+		// update stats when necessary
+		try {
+			OverlayElement* guiAvg = OverlayManager::getSingleton().getOverlayElement("Core/AverageFps");
+			OverlayElement* guiCurr = OverlayManager::getSingleton().getOverlayElement("Core/CurrFps");
+			OverlayElement* guiBest = OverlayManager::getSingleton().getOverlayElement("Core/BestFps");
+			OverlayElement* guiWorst = OverlayManager::getSingleton().getOverlayElement("Core/WorstFps");
+
+			const RenderTarget::FrameStats& stats = mWindow->getStatistics();
+			guiAvg->setCaption(avgFps + StringConverter::toString(stats.avgFPS));
+			guiCurr->setCaption(currFps + StringConverter::toString(stats.lastFPS));
+			guiBest->setCaption(bestFps + StringConverter::toString(stats.bestFPS)
+				+" "+StringConverter::toString(stats.bestFrameTime)+" ms");
+			guiWorst->setCaption(worstFps + StringConverter::toString(stats.worstFPS)
+				+" "+StringConverter::toString(stats.worstFrameTime)+" ms");
+
+			OverlayElement* guiTris = OverlayManager::getSingleton().getOverlayElement("Core/NumTris");
+			guiTris->setCaption(tris + StringConverter::toString(stats.triangleCount));
+
+			OverlayElement* guiBatches = OverlayManager::getSingleton().getOverlayElement("Core/NumBatches");
+			guiBatches->setCaption(batches + StringConverter::toString(stats.batchCount));
+
+			OverlayElement* guiDbg = OverlayManager::getSingleton().getOverlayElement("Core/DebugText");
+			guiDbg->setCaption(mDebugText);
+		}
+		catch(...) { /* ignore */ }
+	}
+
+public:
+	// Constructor takes a RenderWindow because it uses that to determine input context
+	ExampleFrameListener(RenderWindow* win, Camera* cam, bool bufferedKeys = false, bool bufferedMouse = false,
+			     bool bufferedJoy = false ) :
+		mCamera(cam), mTranslateVector(Vector3::ZERO), mCurrentSpeed(0), mWindow(win), mStatsOn(true), mNumScreenShots(0),
+		mMoveScale(0.0f), mRotScale(0.0f), mTimeUntilNextToggle(0), mFiltering(TFO_BILINEAR),
+		mAniso(1), mSceneDetailIndex(0), mMoveSpeed(100), mRotateSpeed(36), mDebugOverlay(0),
+		mInputManager(0), mMouse(0), mKeyboard(0), mJoy(0)
+	{
+
+		mDebugOverlay = OverlayManager::getSingleton().getByName("Core/DebugOverlay");
+
+		LogManager::getSingletonPtr()->logMessage("*** Initializing OIS ***");
+		OIS::ParamList pl;
+		size_t windowHnd = 0;
+		std::ostringstream windowHndStr;
+
+		win->getCustomAttribute("WINDOW", &windowHnd);
+		windowHndStr << windowHnd;
+		pl.insert(std::make_pair(std::string("WINDOW"), windowHndStr.str()));
+
+		mInputManager = OIS::InputManager::createInputSystem( pl );
+
+		//Create all devices (We only catch joystick exceptions here, as, most people have Key/Mouse)
+		mKeyboard = static_cast<OIS::Keyboard*>(mInputManager->createInputObject( OIS::OISKeyboard, bufferedKeys ));
+		mMouse = static_cast<OIS::Mouse*>(mInputManager->createInputObject( OIS::OISMouse, bufferedMouse ));
+		try {
+			mJoy = static_cast<OIS::JoyStick*>(mInputManager->createInputObject( OIS::OISJoyStick, bufferedJoy ));
+		}
+		catch(...) {
+			mJoy = 0;
+		}
+
+		//Set initial mouse clipping size
+		windowResized(mWindow);
+
+		showDebugOverlay(true);
+
+		//Register as a Window listener
+		WindowEventUtilities::addWindowEventListener(mWindow, this);
+	}
+
+	//Adjust mouse clipping area
+	virtual void windowResized(RenderWindow* rw)
+	{
+		unsigned int width, height, depth;
+		int left, top;
+		rw->getMetrics(width, height, depth, left, top);
+
+		const OIS::MouseState &ms = mMouse->getMouseState();
+		ms.width = width;
+		ms.height = height;
+	}
+
+	//Unattach OIS before window shutdown (very important under Linux)
+	virtual void windowClosed(RenderWindow* rw)
+	{
+		//Only close for window that created OIS (the main window in these demos)
+		if( rw == mWindow )
+		{
+			if( mInputManager )
+			{
+				mInputManager->destroyInputObject( mMouse );
+				mInputManager->destroyInputObject( mKeyboard );
+				mInputManager->destroyInputObject( mJoy );
+
+				OIS::InputManager::destroyInputSystem(mInputManager);
+				mInputManager = 0;
+			}
+		}
+	}
+
+	virtual ~ExampleFrameListener()
+	{
+		//Remove ourself as a Window listener
+		WindowEventUtilities::removeWindowEventListener(mWindow, this);
+		windowClosed(mWindow);
+	}
+
+	virtual bool processUnbufferedKeyInput(const FrameEvent& evt)
+	{
+
+		if(mKeyboard->isKeyDown(OIS::KC_A))
+			mTranslateVector.x = -mMoveScale;	// Move camera left
+
+		if(mKeyboard->isKeyDown(OIS::KC_D))
+			mTranslateVector.x = mMoveScale;	// Move camera RIGHT
+
+		if(mKeyboard->isKeyDown(OIS::KC_UP) || mKeyboard->isKeyDown(OIS::KC_W) )
+			mTranslateVector.z = -mMoveScale;	// Move camera forward
+
+		if(mKeyboard->isKeyDown(OIS::KC_DOWN) || mKeyboard->isKeyDown(OIS::KC_S) )
+			mTranslateVector.z = mMoveScale;	// Move camera backward
+
+		if(mKeyboard->isKeyDown(OIS::KC_PGUP))
+			mTranslateVector.y = mMoveScale;	// Move camera up
+
+		if(mKeyboard->isKeyDown(OIS::KC_PGDOWN))
+			mTranslateVector.y = -mMoveScale;	// Move camera down
+
+		if(mKeyboard->isKeyDown(OIS::KC_RIGHT))
+			mCamera->yaw(-mRotScale);
+
+		if(mKeyboard->isKeyDown(OIS::KC_LEFT))
+			mCamera->yaw(mRotScale);
+
+		if( mKeyboard->isKeyDown(OIS::KC_ESCAPE) || mKeyboard->isKeyDown(OIS::KC_Q) )
+			return false;
+
+       	if( mKeyboard->isKeyDown(OIS::KC_F) && mTimeUntilNextToggle <= 0 )
+		{
+			mStatsOn = !mStatsOn;
+			showDebugOverlay(mStatsOn);
+			mTimeUntilNextToggle = 1;
+		}
+
+		if( mKeyboard->isKeyDown(OIS::KC_T) && mTimeUntilNextToggle <= 0 )
+		{
+			switch(mFiltering)
+			{
+			case TFO_BILINEAR:
+				mFiltering = TFO_TRILINEAR;
+				mAniso = 1;
+				break;
+			case TFO_TRILINEAR:
+				mFiltering = TFO_ANISOTROPIC;
+				mAniso = 8;
+				break;
+			case TFO_ANISOTROPIC:
+				mFiltering = TFO_BILINEAR;
+				mAniso = 1;
+				break;
+			default: break;
+			}
+			MaterialManager::getSingleton().setDefaultTextureFiltering(mFiltering);
+			MaterialManager::getSingleton().setDefaultAnisotropy(mAniso);
+
+			showDebugOverlay(mStatsOn);
+			mTimeUntilNextToggle = 1;
+		}
+
+		if(mKeyboard->isKeyDown(OIS::KC_SYSRQ) && mTimeUntilNextToggle <= 0)
+		{
+			std::ostringstream ss;
+			ss << "screenshot_" << ++mNumScreenShots << ".png";
+			mWindow->writeContentsToFile(ss.str());
+			mTimeUntilNextToggle = 0.5;
+			mDebugText = "Saved: " + ss.str();
+		}
+
+		if(mKeyboard->isKeyDown(OIS::KC_R) && mTimeUntilNextToggle <=0)
+		{
+			mSceneDetailIndex = (mSceneDetailIndex+1)%3 ;
+			switch(mSceneDetailIndex) {
+				case 0 : mCamera->setPolygonMode(PM_SOLID); break;
+				case 1 : mCamera->setPolygonMode(PM_WIREFRAME); break;
+				case 2 : mCamera->setPolygonMode(PM_POINTS); break;
+			}
+			mTimeUntilNextToggle = 0.5;
+		}
+
+		static bool displayCameraDetails = false;
+		if(mKeyboard->isKeyDown(OIS::KC_P) && mTimeUntilNextToggle <= 0)
+		{
+			displayCameraDetails = !displayCameraDetails;
+			mTimeUntilNextToggle = 0.5;
+			if (!displayCameraDetails)
+				mDebugText = "";
+		}
+
+		// Print camera details
+		if(displayCameraDetails)
+			mDebugText = "P: " + StringConverter::toString(mCamera->getDerivedPosition()) +
+						 " " + "O: " + StringConverter::toString(mCamera->getDerivedOrientation());
+
+		// Return true to continue rendering
+		return true;
+	}
+
+	virtual bool processUnbufferedMouseInput(const FrameEvent& evt)
+	{
+
+		// Rotation factors, may not be used if the second mouse button is pressed
+		// 2nd mouse button - slide, otherwise rotate
+		const OIS::MouseState &ms = mMouse->getMouseState();
+		if( ms.buttonDown( OIS::MB_Right ) )
+		{
+			mTranslateVector.x += ms.X.rel * 0.13;
+			mTranslateVector.y -= ms.Y.rel * 0.13;
+		}
+		else
+		{
+			mRotX = Degree(-ms.X.rel * 0.13);
+			mRotY = Degree(-ms.Y.rel * 0.13);
+		}
+
+		return true;
+	}
+
+	virtual void moveCamera()
+	{
+		// Make all the changes to the camera
+		// Note that YAW direction is around a fixed axis (freelook style) rather than a natural YAW
+		//(e.g. airplane)
+		mCamera->yaw(mRotX);
+		mCamera->pitch(mRotY);
+		mCamera->moveRelative(mTranslateVector);
+	}
+
+	virtual void showDebugOverlay(bool show)
+	{
+		if (mDebugOverlay)
+		{
+			if (show)
+				mDebugOverlay->show();
+			else
+				mDebugOverlay->hide();
+		}
+	}
+
+	// Override frameRenderingQueued event to process that (don't care about frameEnded)
+	bool frameRenderingQueued(const FrameEvent& evt)
+	{
+
+		if(mWindow->isClosed())	return false;
+
+		mSpeedLimit = mMoveScale * evt.timeSinceLastFrame;
+
+		//Need to capture/update each device
+		mKeyboard->capture();
+		mMouse->capture();
+		if( mJoy ) mJoy->capture();
+
+		bool buffJ = (mJoy) ? mJoy->buffered() : true;
+
+    	Ogre::Vector3 lastMotion = mTranslateVector;
+
+		//Check if one of the devices is not buffered
+		if( !mMouse->buffered() || !mKeyboard->buffered() || !buffJ )
+		{
+			// one of the input modes is immediate, so setup what is needed for immediate movement
+			if (mTimeUntilNextToggle >= 0)
+				mTimeUntilNextToggle -= evt.timeSinceLastFrame;
+
+			// Move about 100 units per second
+			mMoveScale = mMoveSpeed * evt.timeSinceLastFrame;
+			// Take about 10 seconds for full rotation
+			mRotScale = mRotateSpeed * evt.timeSinceLastFrame;
+
+			mRotX = 0;
+			mRotY = 0;
+			mTranslateVector = Ogre::Vector3::ZERO;
+
+		}
+
+		//Check to see which device is not buffered, and handle it
+		if( !mKeyboard->buffered() )
+			if( processUnbufferedKeyInput(evt) == false )
+				return false;
+		if( !mMouse->buffered() )
+			if( processUnbufferedMouseInput(evt) == false )
+				return false;
+
+		// ramp up / ramp down speed
+    	if (mTranslateVector == Ogre::Vector3::ZERO)
+		{
+			// decay (one third speed)
+			mCurrentSpeed -= evt.timeSinceLastFrame * 0.3;
+			mTranslateVector = lastMotion;
+		}
+		else
+		{
+			// ramp up
+			mCurrentSpeed += evt.timeSinceLastFrame;
+
+		}
+		// Limit motion speed
+		if (mCurrentSpeed > 1.0)
+			mCurrentSpeed = 1.0;
+		if (mCurrentSpeed < 0.0)
+			mCurrentSpeed = 0.0;
+
+		mTranslateVector *= mCurrentSpeed;
+
+
+		if( !mMouse->buffered() || !mKeyboard->buffered() || !buffJ )
+			moveCamera();
+
+		return true;
+	}
+
+	bool frameEnded(const FrameEvent& evt)
+	{
+		updateStats();
+		return true;
+	}
+
+protected:
+	Camera* mCamera;
+
+	Vector3 mTranslateVector;
+	Real mCurrentSpeed;
+	RenderWindow* mWindow;
+	bool mStatsOn;
+
+	std::string mDebugText;
+
+	unsigned int mNumScreenShots;
+	float mMoveScale;
+	float mSpeedLimit;
+	Degree mRotScale;
+	// just to stop toggles flipping too fast
+	Real mTimeUntilNextToggle ;
+	Radian mRotX, mRotY;
+	TextureFilterOptions mFiltering;
+	int mAniso;
+
+	int mSceneDetailIndex ;
+	Real mMoveSpeed;
+	Degree mRotateSpeed;
+	Overlay* mDebugOverlay;
+
+	//OIS Input devices
+	OIS::InputManager* mInputManager;
+	OIS::Mouse*    mMouse;
+	OIS::Keyboard* mKeyboard;
+	OIS::JoyStick* mJoy;
+};
+
+#endif
Index: code/branches/presentation3/src/libraries/tools/bsp/OgreFramework.cpp
===================================================================
--- code/branches/presentation3/src/libraries/tools/bsp/OgreFramework.cpp	(revision 7073)
+++ code/branches/presentation3/src/libraries/tools/bsp/OgreFramework.cpp	(revision 7073)
@@ -0,0 +1,7002 @@
+//|||||||||||||||||||||||||||||||||||||||||||||||
+/*
+===========================================================================
+Copyright (C) 2010 Jared Prince
+
+This file is part of bsp-renderer source code.
+
+bsp-renderer is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+bsp-renderer 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with bsp-renderer.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "OgreFramework.hpp"
+
+#include <io.h>
+#include <fcntl.h>      /* Needed only for _O_RDWR definition */
+#include <sys/stat.h>
+#include "Q3Map_misc.h"	// safe pointer deletes
+
+
+
+//|||||||||||||||||||||||||||||||||||||||||||||||
+
+using namespace Ogre;
+
+//|||||||||||||||||||||||||||||||||||||||||||||||
+
+template<> OgreFramework* Ogre::Singleton<class OgreFramework>::ms_Singleton = 0;
+
+
+
+OgreFramework::OgreFramework()
+{
+
+	m_nNewCount=0 ;
+	ZeroMemory((void*)m_nNewCheck, sizeof(m_nNewCheck)) ;
+
+	m_MoveSpeed			= 0.1;
+	m_RotateSpeed		= 0.3;
+
+	m_bShutDownOgre		= false;
+	m_iNumScreenShots	= 0;
+
+	m_pRoot				= 0;
+	m_pSceneMgr			= 0;
+	m_pRenderWnd		= 0;
+	m_pCamera			= 0;
+	m_pViewport			= 0;
+	m_pLog				= 0;
+	m_pTimer			= 0;
+
+	m_pInputMgr			= 0;
+	m_pKeyboard			= 0;
+	m_pMouse			= 0;
+
+	m_pDebugOverlay		= 0;
+	m_pInfoOverlay		= 0;
+
+	//mRawFileManager=NULL ; // pointer for text file resource handler
+	m_pRawBspFile=NULL ;
+	m_bRawBspFileIsLoaded=false ;
+
+	// pointers for q3map stuff
+	m_TempMem=NULL ;
+	m_Q3Map=NULL ;
+	
+
+	m_pZoneMO=NULL ;
+	m_pZoneMesh=NULL ;
+	m_pZoneEntity=NULL ;
+	m_pZoneEntityMaterialType=NULL ;
+
+	int nPTex=0 ;
+	for(nPTex=0 ; nPTex<MAX_PROJECTORTEX ; nPTex++)
+	{
+		m_pZoneEntityMaterial_Base[nPTex]=NULL ;
+		m_pZoneEntityMaterial_Fast[nPTex]=NULL ;
+	}
+
+	m_pZoneEntityMaterial_Black=NULL ;
+
+	m_pZoneEntityMaterial_DfShColour=NULL ;
+	m_pZoneEntityMaterial_Shadow=NULL ;
+	m_pZoneEntityMaterial_ShadeFront=NULL ;
+	m_pZoneEntityMaterial_ShadeBack=NULL ;
+	m_pZoneEntityMaterial_DfShPosition=NULL ;
+	m_pZoneEntityMaterial_DfShDiffuse=NULL ;
+	//m_pZoneEntityMaterial_DSNormal=NULL ;
+	m_pZoneEntityMaterial_DfShFuncTNB=NULL ;
+	m_pZoneEntityMaterial_DfShSpecular=NULL ;
+	m_pZoneEntityMaterial_DfShEmissive=NULL ;
+	//m_pZoneEntityMaterial_DfShData=NULL ;
+	m_pZoneEntityMaterial_DfShMix=NULL ;
+
+
+	/////////////////////////////////
+
+	m_nMaxEntity=0 ;
+	m_pEntityInfo=NULL ;
+	m_nMaxVisibleEntity=0 ;
+	m_pVisibleEntity=NULL ;
+	m_nMaxFrustumEntity=0 ;
+	m_pFrustumEntity=NULL ;
+
+
+	// debug light meshes
+	m_pLightMO=NULL ;
+	m_pPortalMO=NULL ;
+	m_pPortalNode=NULL ;
+
+	m_pCubeNode			= 0;
+	m_pCubeEntity		= 0;
+
+	nMap=2 ;
+	m_nLoadToggle=0 ;
+	m_nJumpToggle=0 ;
+	m_nJumpVal=-1 ;
+
+	
+
+
+
+	m_nDebugA=0 ;
+	m_nDebugB=0 ;
+	m_nDebugC=0 ;
+	m_nRecurseCount=0 ;
+
+
+	m_nDebugLightBox=0 ;
+	m_nZoneCullingActive=1 ;
+	
+	m_nPortalDebug=0 ;
+	m_nPortalToggle=0 ;
+	m_nPortalState=1 ;
+
+	m_nKeyDown_Shift=0 ;
+	m_nKeyDown_Ctrl=0 ;
+
+
+
+	m_flStereoFrustumTweak=0.0f ;
+
+	m_nGoggleMode=GOGGLE_MODE_OFF ;
+	m_pGoggleL=NULL ;
+	m_pGoggleR=NULL ;
+	m_pNodeGoggles=NULL ;
+
+	// default goggle settings
+	m_flGoggleZPos=-250 ;
+	m_flGoggleXGap=0.0 ;
+	m_flGoggleXScale=1.0 ;
+	m_flGoggleYScale=1.0 ;
+
+	m_nMapLoaded=0 ;
+
+	m_nTime=0 ;
+
+	
+
+	ZeroMemory((void*)m_nFrameTime, sizeof(m_nFrameTime)) ;
+	m_nFrame=0 ;
+	m_nGotInput=0 ;
+
+	ZeroMemory((void*)m_nKeyToggle, sizeof(m_nKeyToggle)) ;
+
+//	OFBug.LogInit() ;
+
+	m_nRTTAssetsExist=0 ;
+	
+	m_nToggle=0 ;
+	
+	m_nFlashLight=0 ;
+
+
+
+	m_flAspectRatio=1.0f ;
+	m_flFOV=60.0f ;
+
+	// whether we are drawing the render or one of the info screens, such as wireframe or one of the deferred screens
+	m_nDisplayInfoMode=0 ;
+
+	m_nSpeedRender=0 ;
+
+	//m_nRenderHDR=0 ;
+
+	srand( (unsigned)time( NULL ) );
+
+}
+
+//|||||||||||||||||||||||||||||||||||||||||||||||
+
+OgreFramework::~OgreFramework()
+{
+//	OFBug.LogSave("OFBug.txt") ;
+
+	/*
+	char chMessage[1024] ;
+	int nFrame=0 ;
+	m_pLog->logMessage("   ");
+	m_pLog->logMessage("   Mircosecs:   Physics    Render    Ideal   Average    Pragma     Total");
+	m_pLog->logMessage("   ----------------------------------------");
+	for(nFrame=0 ; nFrame<MAXFRAME ; nFrame++)
+	{
+		//sprintf(chMessage, "   Frame %3i:  %8i  %8i  %8i", nFrame, m_nFrameTime[nFrame][0], m_nFrameTime[nFrame][1], m_nFrameTime[nFrame][2]) ;
+		sprintf(chMessage, "  Frame %3i:  %8i  %8i  %8i  %8i  %8i  %8i", nFrame, m_nFrameTime[nFrame][0], m_nFrameTime[nFrame][1], m_nFrameTime[nFrame][2], m_nFrameTime[nFrame][3], m_nFrameTime[nFrame][4], m_nFrameTime[nFrame][5]) ;
+		m_pLog->logMessage(chMessage);
+	}
+	m_pLog->logMessage("");
+	*/
+
+	//UnloadMap(true) ;
+
+
+	//mRawFileManager->~RawFileManager() ; //!! is this correct?  
+	//DELETE_POINTER( mRawFileManager ) ;
+
+
+	DELETE_POINTER( m_pKeyboard );
+	DELETE_POINTER( m_pMouse );
+	OIS::InputManager::destroyInputSystem(m_pInputMgr);
+
+	
+	//DELETE_POINTER( m_pRoot );
+	//delete m_pRoot ;
+}
+
+
+
+//|||||||||||||||||||||||||||||||||||||||||||||||
+
+void OgreFramework::initOgre(Ogre::String wndTitle, OIS::KeyListener *pKeyListener, OIS::MouseListener *pMouseListener)
+{
+//	Ogre::LogManager* logMgr = new Ogre::LogManager();
+	
+	m_pLog = Ogre::LogManager::getSingleton().createLog("OgreLogfile.log", true, true, false);
+	m_pLog->setDebugOutputEnabled(true);
+	
+	m_pRoot = new Ogre::Root();
+
+	// our manager for the raw bsp
+	//mRawFileManager = new RawFileManager();
+	//ResourceGroupManager::getSingleton().createResourceGroup("Raw Bsp") ;
+
+	
+
+
+	m_pRoot->showConfigDialog();
+
+	if (!m_pRoot->restoreConfig() && !m_pRoot->showConfigDialog())
+	{
+		throw Exception(52, "User canceled the config dialog!", "Application::setupRenderSystem()");
+	}
+
+	
+	m_pRenderWnd = m_pRoot->initialise(true, wndTitle);
+
+	m_pSceneMgr = m_pRoot->createSceneManager(ST_GENERIC, "SceneManager");
+	m_pSceneMgr->setAmbientLight(Ogre::ColourValue(0.0, 0.0, 0.0));
+
+
+	m_pCamera = m_pSceneMgr->createCamera("Camera");
+	m_pCamera->setPosition(Vector3(0, 0, 0));
+	m_pCamera->lookAt(Vector3(0,0,0));
+	m_pCamera->setNearClipDistance(NEARCLIP);
+  m_pCamera->setFarClipDistance(FARCLIP);
+	m_pCamera->setFOVy( Degree(m_flFOV) ) ;
+
+	m_pCameraCopy = m_pSceneMgr->createCamera("CameraCopy");
+	m_pCameraCopy->setNearClipDistance(NEARCLIP);
+  m_pCameraCopy->setFarClipDistance(FARCLIP);
+
+
+	
+	m_pCameraNoJitter = m_pSceneMgr->createCamera("CameraNoJitter");
+
+
+	
+	
+
+	m_pViewport = m_pRenderWnd->addViewport(m_pCamera);
+
+	m_pViewport->setBackgroundColour(ColourValue(0.5, 0.5, 0.5, 1.0));
+
+	m_flAspectRatio= (float)m_pViewport->getActualWidth() / (float)m_pViewport->getActualHeight() ;
+
+	m_flGoggleAspectRatio=m_flAspectRatio ; // we use this to set up the Goggles
+	m_pCamera->setAspectRatio( Real(m_flAspectRatio) );
+	m_pCameraCopy->setAspectRatio(  Real(m_flAspectRatio) );
+	
+	m_pViewport->setCamera(m_pCamera);
+	m_pViewport->setClearEveryFrame(true);//, FBT_DEPTH) ;
+
+	
+
+
+	unsigned long hWnd = 0;
+    OIS::ParamList paramList;
+    m_pRenderWnd->getCustomAttribute("WINDOW", &hWnd);
+
+	paramList.insert(OIS::ParamList::value_type("WINDOW", Ogre::StringConverter::toString(hWnd)));
+
+	m_pInputMgr = OIS::InputManager::createInputSystem(paramList);
+
+  m_pKeyboard = static_cast<OIS::Keyboard*>(m_pInputMgr->createInputObject(OIS::OISKeyboard, true));
+	m_pMouse = static_cast<OIS::Mouse*>(m_pInputMgr->createInputObject(OIS::OISMouse, true));
+
+    
+	m_pMouse->getMouseState().height = m_pRenderWnd->getHeight();
+	m_pMouse->getMouseState().width	 = m_pRenderWnd->getWidth();
+
+	if(pKeyListener == 0)
+		m_pKeyboard->setEventCallback(this);
+	else
+		m_pKeyboard->setEventCallback(pKeyListener);
+
+	if(pMouseListener == 0)
+		m_pMouse->setEventCallback(this);
+	else
+		m_pMouse->setEventCallback(pMouseListener);
+
+	SetupResourceLocations() ;
+
+	// D3D or OGL
+	if (Root::getSingleton().getRenderSystem()->getName().find("GL") != String::npos)
+		m_IsOpenGL = true;
+	else
+		m_IsOpenGL = false;
+
+
+
+
+
+	m_pTimer = new Ogre::Timer();
+	m_pTimer->reset();
+	
+
+	
+
+
+	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+	Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
+	
+
+	
+
+	
+
+	m_nRTTWidth = m_pRenderWnd->getWidth() ;
+	m_nRTTHeight = m_pRenderWnd->getHeight() ;
+	
+
+	m_pRenderWnd->addListener(this);
+	m_pRenderWnd->setActive(true);
+
+
+	// default values, may be changed from the bzn.cfg file
+	m_nShadowMapSize=512 ;
+	m_nShadowRGBSize=512 ;
+	m_nColouredShadow=1 ;
+	m_nRenderHDR=1 ;
+	m_nMaxGPUQuery=1 ;
+	
+}
+
+void OgreFramework::FinalShutdown(void)
+{
+
+	//Ogre::ResourceGroupManager::getSingleton().shutdownAll();
+	//m_pRoot->destroySceneManager(m_pSceneMgr);
+	//m_pRoot->shutdown() ;
+	delete m_pRoot ;
+}
+
+void OgreFramework::AdditionalSetup()
+{
+
+	CreateRTTAssets() ;
+}
+
+
+void OgreFramework::SetupResourceLocations()
+{
+	Ogre::String secName, typeName, archName;
+	Ogre::ConfigFile cf;
+    cf.load("resources.cfg");
+
+	Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator();
+    while (seci.hasMoreElements())
+    {
+        secName = seci.peekNextKey();
+		Ogre::ConfigFile::SettingsMultiMap *settings = seci.getNext();
+        Ogre::ConfigFile::SettingsMultiMap::iterator i;
+        for (i = settings->begin(); i != settings->end(); ++i)
+        {
+            typeName = i->first;
+            archName = i->second;
+            Ogre::ResourceGroupManager::getSingleton().addResourceLocation(archName, typeName, secName);
+        }
+    }
+	Ogre::TextureManager::getSingleton().setDefaultNumMipmaps(5);
+}
+
+
+// create the textures, materials and miniscreens needed for the RTTs
+void OgreFramework::CreateRTTAssets()
+{
+	
+
+	// I originally had problems with OpenGL 2.1.1 suddenly running very slow if I had more than 12 RTT surfaces
+	// but that issue seems to have vanished, perhaps because all my surfaces are now FLOAT16_RGB or FLOAT16_RGBA.
+	// It doesn't like FLOAT32 surfaces, and it also didn't seem to like mixing R8G8B8 with FLOAT16, at least
+	// those configurations seem to have been problems in the past, so I try to keep everything FLOAT16_RGB where possible.
+
+	// I go through quite a few RTT surfaces, and I don't use MRT since I couldn't get it working.
+
+	MaterialManager::getSingleton().initialise() ;
+
+	// need our own resource group so that the RTTs dont get destroyed when we load a new level.
+	Ogre::ResourceGroupManager::getSingleton().createResourceGroup("RTTResources") ;
+
+	Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();	
+
+	int RTTWidth = m_nRTTWidth ;//m_pRenderWnd->getWidth() ;
+	int RTTHeight = m_nRTTHeight ; //m_pRenderWnd->getHeight() ;
+
+	int DSWidth = m_nRTTWidth ;
+	int DSHeight = m_nRTTHeight ;
+
+	int DSWidthB = m_nSpeedRender ? m_nRTTWidth/2 : m_nRTTWidth ;
+	int DSHeightB = m_nSpeedRender ? m_nRTTHeight/2 : m_nRTTHeight ;
+
+	int TEMPWidth = m_nRTTWidth ;
+	int TEMPHeight = m_nRTTHeight ;
+
+
+	Ogre::ColourValue ClearColour=Ogre::ColourValue(0,0,0,1) ; // clears alpha as well. for RGBA textures 
+	//!! for now use alpha 1, since Ogre seems to force it to 1 anyway on shadow render.
+
+
+	
+	
+	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+	// Shadow
+	
+	RTT_Texture_Shadow = Ogre::TextureManager::getSingleton().createManual("RttTex_Shadow",
+      "RTTResources", TEX_TYPE_2D, m_nShadowMapSize, m_nShadowMapSize, 0, PF_FLOAT16_RGB,
+      TU_RENDERTARGET);
+
+	
+	renderTexture_Shadow = RTT_Texture_Shadow->getBuffer()->getRenderTarget();
+	renderTexture_Shadow->addViewport(m_pCamera);
+	renderTexture_Shadow->getViewport(0)->setClearEveryFrame(true, FBT_DEPTH);
+	renderTexture_Shadow->getViewport(0)->setBackgroundColour(ColourValue::White);
+	renderTexture_Shadow->getViewport(0)->setOverlaysEnabled(false);
+
+	RTT_Mat_Shadow = MaterialManager::getSingleton().create("RttMat_Shadow", "RTTResources");
+	RTT_Technique_Shadow = RTT_Mat_Shadow->createTechnique();
+	RTT_Technique_Shadow->createPass();
+	TextureUnitState* tState_Shadow = RTT_Mat_Shadow->getTechnique(0)->getPass(0)->createTextureUnitState("RttTex_Shadow");
+	tState_Shadow->setTextureAddressingMode(Ogre::TextureUnitState::TAM_CLAMP) ;
+	RTT_Mat_Shadow->getTechnique(0)->getPass(0)->setLightingEnabled(false);
+	RTT_Mat_Shadow->getTechnique(0)->getPass(0)->setTextureFiltering(TFO_NONE) ;
+	
+	miniScreen_Shadow = new Ogre::Rectangle2D(true);
+	miniScreen_Shadow->setCorners(-1.0001, 1.0001, 1.0, -1.0);
+	miniScreen_Shadow->setBoundingBox(AxisAlignedBox(-100000.0*Vector3::UNIT_SCALE, 100000.0*Vector3::UNIT_SCALE)); 
+	miniScreenNode_Shadow = m_pSceneMgr->getRootSceneNode()->createChildSceneNode("MiniScreenNode_Shadow");
+	miniScreenNode_Shadow->attachObject(miniScreen_Shadow);
+	miniScreen_Shadow->setMaterial("RttMat_Shadow");
+
+
+	if(m_nColouredShadow)
+	{
+		//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+		// ShadeFront
+		
+		RTT_Texture_ShadeFront = Ogre::TextureManager::getSingleton().createManual("RttTex_ShadeFront",
+				"RTTResources", TEX_TYPE_2D, m_nShadowRGBSize, m_nShadowRGBSize, 0, PF_FLOAT16_RGBA,
+				TU_RENDERTARGET);
+
+		
+		renderTexture_ShadeFront = RTT_Texture_ShadeFront->getBuffer()->getRenderTarget();
+		renderTexture_ShadeFront->addViewport(m_pCamera);
+		renderTexture_ShadeFront->getViewport(0)->setClearEveryFrame(true);//, FBT_DEPTH);
+		renderTexture_ShadeFront->getViewport(0)->setBackgroundColour(ColourValue::White);
+		renderTexture_ShadeFront->getViewport(0)->setOverlaysEnabled(false);
+
+		RTT_Mat_ShadeFront = MaterialManager::getSingleton().create("RttMat_ShadeFront", "RTTResources");
+		RTT_Technique_ShadeFront = RTT_Mat_ShadeFront->createTechnique();
+		RTT_Technique_ShadeFront->createPass();
+		TextureUnitState* tState_ShadeFront = RTT_Mat_ShadeFront->getTechnique(0)->getPass(0)->createTextureUnitState("RttTex_ShadeFront");
+		tState_ShadeFront->setTextureAddressingMode(Ogre::TextureUnitState::TAM_CLAMP) ;
+		RTT_Mat_ShadeFront->getTechnique(0)->getPass(0)->setLightingEnabled(false);
+		RTT_Mat_ShadeFront->getTechnique(0)->getPass(0)->setTextureFiltering(TFO_NONE) ;
+		
+		miniScreen_ShadeFront = new Ogre::Rectangle2D(true);
+		miniScreen_ShadeFront->setCorners(-1.0001, 1.0001, 1.0, -1.0);
+		miniScreen_ShadeFront->setBoundingBox(AxisAlignedBox(-100000.0*Vector3::UNIT_SCALE, 100000.0*Vector3::UNIT_SCALE)); 
+		miniScreenNode_ShadeFront = m_pSceneMgr->getRootSceneNode()->createChildSceneNode("MiniScreenNode_ShadeFront");
+		miniScreenNode_ShadeFront->attachObject(miniScreen_ShadeFront);
+		miniScreen_ShadeFront->setMaterial("RttMat_ShadeFront");
+		
+		//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+		// ShadeBack
+		
+		RTT_Texture_ShadeBack = Ogre::TextureManager::getSingleton().createManual("RttTex_ShadeBack",
+				"RTTResources", TEX_TYPE_2D, m_nShadowRGBSize, m_nShadowRGBSize, 0, PF_FLOAT16_RGBA,
+				TU_RENDERTARGET);
+
+		
+		renderTexture_ShadeBack = RTT_Texture_ShadeBack->getBuffer()->getRenderTarget();
+		renderTexture_ShadeBack->addViewport(m_pCamera);
+		renderTexture_ShadeBack->getViewport(0)->setClearEveryFrame(true);//, FBT_DEPTH);
+		renderTexture_ShadeBack->getViewport(0)->setBackgroundColour(ColourValue::White);
+		renderTexture_ShadeBack->getViewport(0)->setOverlaysEnabled(false);
+
+		RTT_Mat_ShadeBack = MaterialManager::getSingleton().create("RttMat_ShadeBack", "RTTResources");
+		RTT_Technique_ShadeBack = RTT_Mat_ShadeBack->createTechnique();
+		RTT_Technique_ShadeBack->createPass();
+		TextureUnitState* tState_ShadeBack = RTT_Mat_ShadeBack->getTechnique(0)->getPass(0)->createTextureUnitState("RttTex_ShadeBack");
+		tState_ShadeBack->setTextureAddressingMode(Ogre::TextureUnitState::TAM_CLAMP) ;
+		RTT_Mat_ShadeBack->getTechnique(0)->getPass(0)->setLightingEnabled(false);
+		RTT_Mat_ShadeBack->getTechnique(0)->getPass(0)->setTextureFiltering(TFO_NONE) ;
+		
+		miniScreen_ShadeBack = new Ogre::Rectangle2D(true);
+		miniScreen_ShadeBack->setCorners(-1.0001, 1.0001, 1.0, -1.0);
+		miniScreen_ShadeBack->setBoundingBox(AxisAlignedBox(-100000.0*Vector3::UNIT_SCALE, 100000.0*Vector3::UNIT_SCALE)); 
+		miniScreenNode_ShadeBack = m_pSceneMgr->getRootSceneNode()->createChildSceneNode("MiniScreenNode_ShadeBack");
+		miniScreenNode_ShadeBack->attachObject(miniScreen_ShadeBack);
+		miniScreen_ShadeBack->setMaterial("RttMat_ShadeBack");
+	
+	} // end if coloured shadows
+	
+	
+	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+	// DfShPosition
+	
+	RTT_Texture_DfShPosition = Ogre::TextureManager::getSingleton().createManual("RttTex_DfShPosition",
+      "RTTResources", TEX_TYPE_2D, DSWidth, DSHeight, 0, PF_FLOAT16_RGB,
+      TU_RENDERTARGET);
+
+	
+	renderTexture_DfShPosition = RTT_Texture_DfShPosition->getBuffer()->getRenderTarget();
+	renderTexture_DfShPosition->addViewport(m_pCamera);
+	renderTexture_DfShPosition->getViewport(0)->setClearEveryFrame(true, FBT_DEPTH);
+	renderTexture_DfShPosition->getViewport(0)->setBackgroundColour(ColourValue::Black);
+	renderTexture_DfShPosition->getViewport(0)->setOverlaysEnabled(false);
+
+	RTT_Mat_DfShPosition = MaterialManager::getSingleton().create("RttMat_DfShPosition", "RTTResources");
+	RTT_Technique_DfShPosition = RTT_Mat_DfShPosition->createTechnique();
+	RTT_Technique_DfShPosition->createPass();
+	TextureUnitState* tState_DfShPosition = RTT_Mat_DfShPosition->getTechnique(0)->getPass(0)->createTextureUnitState("RttTex_DfShPosition");
+	tState_DfShPosition->setTextureAddressingMode(Ogre::TextureUnitState::TAM_CLAMP) ;
+	RTT_Mat_DfShPosition->getTechnique(0)->getPass(0)->setLightingEnabled(false);
+	RTT_Mat_DfShPosition->getTechnique(0)->getPass(0)->setTextureFiltering(TFO_NONE) ;
+	
+	
+	//renderTexture_DfShPosition->addListener(this);
+
+	miniScreen_DfShPosition = new Ogre::Rectangle2D(true);
+	miniScreen_DfShPosition->setCorners(-1.0001, 1.0001, 1.0, -1.0);
+	miniScreen_DfShPosition->setBoundingBox(AxisAlignedBox(-100000.0*Vector3::UNIT_SCALE, 100000.0*Vector3::UNIT_SCALE)); 
+	miniScreenNode_DfShPosition = m_pSceneMgr->getRootSceneNode()->createChildSceneNode("MiniScreenNode_DfShPosition");
+	miniScreenNode_DfShPosition->attachObject(miniScreen_DfShPosition);
+	miniScreen_DfShPosition->setMaterial("RttMat_DfShPosition");
+	
+	
+
+	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+	// DfShDiffuse
+	
+	RTT_Texture_DfShDiffuse = Ogre::TextureManager::getSingleton().createManual("RttTex_DfShDiffuse",
+      "RTTResources", TEX_TYPE_2D, DSWidth, DSHeight, 0, PF_FLOAT16_RGB,
+      TU_RENDERTARGET);
+
+	
+	renderTexture_DfShDiffuse = RTT_Texture_DfShDiffuse->getBuffer()->getRenderTarget();
+	renderTexture_DfShDiffuse->addViewport(m_pCamera);
+	renderTexture_DfShDiffuse->getViewport(0)->setClearEveryFrame(true, FBT_DEPTH);
+	renderTexture_DfShDiffuse->getViewport(0)->setBackgroundColour(ColourValue::Black);
+	renderTexture_DfShDiffuse->getViewport(0)->setOverlaysEnabled(false);
+
+	RTT_Mat_DfShDiffuse = MaterialManager::getSingleton().create("RttMat_DfShDiffuse", "RTTResources");
+	RTT_Technique_DfShDiffuse = RTT_Mat_DfShDiffuse->createTechnique();
+	RTT_Technique_DfShDiffuse->createPass();
+	TextureUnitState* tState_DfShDiffuse = RTT_Mat_DfShDiffuse->getTechnique(0)->getPass(0)->createTextureUnitState("RttTex_DfShDiffuse");
+	tState_DfShDiffuse->setTextureAddressingMode(Ogre::TextureUnitState::TAM_CLAMP) ;
+	RTT_Mat_DfShDiffuse->getTechnique(0)->getPass(0)->setLightingEnabled(false);
+	RTT_Mat_DfShDiffuse->getTechnique(0)->getPass(0)->setTextureFiltering(TFO_NONE) ;
+	
+	
+	//renderTexture_DfShDiffuse->addListener(this);
+
+	miniScreen_DfShDiffuse = new Ogre::Rectangle2D(true);
+	miniScreen_DfShDiffuse->setCorners(-1.0001, 1.0001, 1.0, -1.0);
+	miniScreen_DfShDiffuse->setBoundingBox(AxisAlignedBox(-100000.0*Vector3::UNIT_SCALE, 100000.0*Vector3::UNIT_SCALE)); 
+	miniScreenNode_DfShDiffuse = m_pSceneMgr->getRootSceneNode()->createChildSceneNode("MiniScreenNode_DfShDiffuse");
+	miniScreenNode_DfShDiffuse->attachObject(miniScreen_DfShDiffuse);
+	miniScreen_DfShDiffuse->setMaterial("RttMat_DfShDiffuse");
+
+	/*
+	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+	// DSNormal
+	
+	RTT_Texture_DSNormal = Ogre::TextureManager::getSingleton().createManual("RttTex_DSNormal",
+      "RTTResources", TEX_TYPE_2D, DSWidth, DSHeight, 0, PF_FLOAT16_RGB,
+      TU_RENDERTARGET);
+
+	
+	renderTexture_DSNormal = RTT_Texture_DSNormal->getBuffer()->getRenderTarget();
+	renderTexture_DSNormal->addViewport(m_pCamera);
+	renderTexture_DSNormal->getViewport(0)->setClearEveryFrame(true, FBT_DEPTH);
+	renderTexture_DSNormal->getViewport(0)->setBackgroundColour(ColourValue::Black);
+	renderTexture_DSNormal->getViewport(0)->setOverlaysEnabled(false);
+
+	RTT_Mat_DSNormal = MaterialManager::getSingleton().create("RttMat_DSNormal", "RTTResources");
+	RTT_Technique_DSNormal = RTT_Mat_DSNormal->createTechnique();
+	RTT_Technique_DSNormal->createPass();
+	TextureUnitState* tState_DSNormal = RTT_Mat_DSNormal->getTechnique(0)->getPass(0)->createTextureUnitState("RttTex_DSNormal");
+	tState_DSNormal->setTextureAddressingMode(Ogre::TextureUnitState::TAM_CLAMP) ;
+	RTT_Mat_DSNormal->getTechnique(0)->getPass(0)->setLightingEnabled(false);
+	RTT_Mat_DSNormal->getTechnique(0)->getPass(0)->setTextureFiltering(TFO_NONE) ;
+	
+	
+	
+	//renderTexture_DSNormal->addListener(this);
+
+	miniScreen_DSNormal = new Ogre::Rectangle2D(true);
+	miniScreen_DSNormal->setCorners(-1.0001, 1.0001, 1.0, -1.0);
+	miniScreen_DSNormal->setBoundingBox(AxisAlignedBox(-100000.0*Vector3::UNIT_SCALE, 100000.0*Vector3::UNIT_SCALE)); 
+	miniScreenNode_DSNormal = m_pSceneMgr->getRootSceneNode()->createChildSceneNode("MiniScreenNode_DSNormal");
+	miniScreenNode_DSNormal->attachObject(miniScreen_DSNormal);
+	miniScreen_DSNormal->setMaterial("RttMat_DSNormal");
+	
+	*/
+
+	
+	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+	// DfShFuncTNB
+
+	RTT_Texture_DfShFuncTNB = Ogre::TextureManager::getSingleton().createManual("RttTex_DfShFuncTNB",
+      "RTTResources", TEX_TYPE_2D, DSWidth, DSHeight, 0, PF_FLOAT16_RGB,
+      TU_RENDERTARGET);
+
+	
+	renderTexture_DfShFuncTNB = RTT_Texture_DfShFuncTNB->getBuffer()->getRenderTarget();
+	renderTexture_DfShFuncTNB->addViewport(m_pCamera);
+	renderTexture_DfShFuncTNB->getViewport(0)->setClearEveryFrame(true, FBT_DEPTH);
+	renderTexture_DfShFuncTNB->getViewport(0)->setBackgroundColour(ColourValue::Black);
+	renderTexture_DfShFuncTNB->getViewport(0)->setOverlaysEnabled(false);
+
+	RTT_Mat_DfShFuncTNB = MaterialManager::getSingleton().create("RttMat_DfShFuncTNB", "RTTResources");
+	RTT_Technique_DfShFuncTNB = RTT_Mat_DfShFuncTNB->createTechnique();
+	RTT_Technique_DfShFuncTNB->createPass();
+	TextureUnitState* tState_DfShFuncTNB = RTT_Mat_DfShFuncTNB->getTechnique(0)->getPass(0)->createTextureUnitState("RttTex_DfShFuncTNB");
+	tState_DfShFuncTNB->setTextureAddressingMode(Ogre::TextureUnitState::TAM_CLAMP) ;
+	RTT_Mat_DfShFuncTNB->getTechnique(0)->getPass(0)->setLightingEnabled(false);
+	RTT_Mat_DfShFuncTNB->getTechnique(0)->getPass(0)->setTextureFiltering(TFO_NONE) ;
+	
+	//renderTexture_DfShFuncTNB->addListener(this);
+
+	miniScreen_DfShFuncTNB = new Ogre::Rectangle2D(true);
+	miniScreen_DfShFuncTNB->setCorners(-1.0001, 1.0001, 1.0, -1.0);
+	miniScreen_DfShFuncTNB->setBoundingBox(AxisAlignedBox(-100000.0*Vector3::UNIT_SCALE, 100000.0*Vector3::UNIT_SCALE)); 
+	miniScreenNode_DfShFuncTNB = m_pSceneMgr->getRootSceneNode()->createChildSceneNode("MiniScreenNode_DfShFuncTNB");
+	miniScreenNode_DfShFuncTNB->attachObject(miniScreen_DfShFuncTNB);
+	miniScreen_DfShFuncTNB->setMaterial("RttMat_DfShFuncTNB");
+
+	
+
+	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+	// DfShSpecular
+
+	RTT_Texture_DfShSpecular = Ogre::TextureManager::getSingleton().createManual("RttTex_DfShSpecular",
+      "RTTResources", TEX_TYPE_2D, DSWidthB, DSHeightB, 0, PF_FLOAT16_RGBA,
+      TU_RENDERTARGET);
+
+	
+	renderTexture_DfShSpecular = RTT_Texture_DfShSpecular->getBuffer()->getRenderTarget();
+	renderTexture_DfShSpecular->addViewport(m_pCamera);
+	renderTexture_DfShSpecular->getViewport(0)->setClearEveryFrame(true, FBT_DEPTH);
+	renderTexture_DfShSpecular->getViewport(0)->setBackgroundColour(ColourValue::Black);
+	renderTexture_DfShSpecular->getViewport(0)->setOverlaysEnabled(false);
+
+	RTT_Mat_DfShSpecular = MaterialManager::getSingleton().create("RttMat_DfShSpecular", "RTTResources");
+	RTT_Technique_DfShSpecular = RTT_Mat_DfShSpecular->createTechnique();
+	RTT_Technique_DfShSpecular->createPass();
+	TextureUnitState* tState_DfShSpecular = RTT_Mat_DfShSpecular->getTechnique(0)->getPass(0)->createTextureUnitState("RttTex_DfShSpecular");
+	tState_DfShSpecular->setTextureAddressingMode(Ogre::TextureUnitState::TAM_CLAMP) ;
+	RTT_Mat_DfShSpecular->getTechnique(0)->getPass(0)->setLightingEnabled(false);
+	if(m_nSpeedRender)
+		RTT_Mat_DfShSpecular->getTechnique(0)->getPass(0)->setTextureFiltering(TFO_BILINEAR) ;
+	else
+		RTT_Mat_DfShSpecular->getTechnique(0)->getPass(0)->setTextureFiltering(TFO_NONE) ;
+	
+
+	//renderTexture_DfShSpecular->addListener(this);
+
+	miniScreen_DfShSpecular = new Ogre::Rectangle2D(true);
+	miniScreen_DfShSpecular->setCorners(-1.0001, 1.0001, 1.0, -1.0);
+	miniScreen_DfShSpecular->setBoundingBox(AxisAlignedBox(-100000.0*Vector3::UNIT_SCALE, 100000.0*Vector3::UNIT_SCALE)); 
+	miniScreenNode_DfShSpecular = m_pSceneMgr->getRootSceneNode()->createChildSceneNode("MiniScreenNode_DfShSpecular");
+	miniScreenNode_DfShSpecular->attachObject(miniScreen_DfShSpecular);
+	miniScreen_DfShSpecular->setMaterial("RttMat_DfShSpecular");
+
+	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+	// DfShEmissive
+
+	RTT_Texture_DfShEmissive = Ogre::TextureManager::getSingleton().createManual("RttTex_DfShEmissive",
+      "RTTResources", TEX_TYPE_2D, DSWidth, DSHeight, 0, PF_FLOAT16_RGB,
+      TU_RENDERTARGET);
+
+	
+	renderTexture_DfShEmissive = RTT_Texture_DfShEmissive->getBuffer()->getRenderTarget();
+	renderTexture_DfShEmissive->addViewport(m_pCamera);
+	renderTexture_DfShEmissive->getViewport(0)->setClearEveryFrame(true, FBT_DEPTH);
+	renderTexture_DfShEmissive->getViewport(0)->setBackgroundColour(ColourValue::Black);
+	renderTexture_DfShEmissive->getViewport(0)->setOverlaysEnabled(false);
+
+	RTT_Mat_DfShEmissive = MaterialManager::getSingleton().create("RttMat_DfShEmissive", "RTTResources");
+	RTT_Technique_DfShEmissive = RTT_Mat_DfShEmissive->createTechnique();
+	RTT_Technique_DfShEmissive->createPass();
+	TextureUnitState* tState_DfShEmissive = RTT_Mat_DfShEmissive->getTechnique(0)->getPass(0)->createTextureUnitState("RttTex_DfShEmissive");
+	tState_DfShEmissive->setTextureAddressingMode(Ogre::TextureUnitState::TAM_CLAMP) ;
+	RTT_Mat_DfShEmissive->getTechnique(0)->getPass(0)->setLightingEnabled(false);
+	if(m_nSpeedRender)
+		RTT_Mat_DfShEmissive->getTechnique(0)->getPass(0)->setTextureFiltering(TFO_BILINEAR) ;
+	else
+		RTT_Mat_DfShEmissive->getTechnique(0)->getPass(0)->setTextureFiltering(TFO_NONE) ;
+	
+
+	//renderTexture_DfShEmissive->addListener(this);
+
+	miniScreen_DfShEmissive = new Ogre::Rectangle2D(true);
+	miniScreen_DfShEmissive->setCorners(-1.0001, 1.0001, 1.0, -1.0);
+	miniScreen_DfShEmissive->setBoundingBox(AxisAlignedBox(-100000.0*Vector3::UNIT_SCALE, 100000.0*Vector3::UNIT_SCALE)); 
+	miniScreenNode_DfShEmissive = m_pSceneMgr->getRootSceneNode()->createChildSceneNode("MiniScreenNode_DfShEmissive");
+	miniScreenNode_DfShEmissive->attachObject(miniScreen_DfShEmissive);
+	miniScreen_DfShEmissive->setMaterial("RttMat_DfShEmissive");
+
+	
+	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+	// DfShLamp
+
+	RTT_Texture_DfShLamp = Ogre::TextureManager::getSingleton().createManual("RttTex_DfShLamp",
+      "RTTResources", TEX_TYPE_2D, DSWidth, DSHeight, 0, PF_FLOAT16_RGB, 
+      TU_RENDERTARGET);//, 0, false, 6);
+	
+	renderTexture_DfShLamp = RTT_Texture_DfShLamp->getBuffer()->getRenderTarget();
+	renderTexture_DfShLamp->addViewport(m_pCamera);
+	renderTexture_DfShLamp->getViewport(0)->setClearEveryFrame(true);//, FBT_DEPTH); 
+	renderTexture_DfShLamp->getViewport(0)->setBackgroundColour(ColourValue::Black);
+	renderTexture_DfShLamp->getViewport(0)->setOverlaysEnabled(false);
+
+	RTT_Mat_DfShLamp = MaterialManager::getSingleton().create("RttMat_DfShLamp", "RTTResources");
+	RTT_Technique_DfShLamp = RTT_Mat_DfShLamp->createTechnique();
+	RTT_Technique_DfShLamp->createPass();
+	TextureUnitState* tState_DfShLamp = RTT_Mat_DfShLamp->getTechnique(0)->getPass(0)->createTextureUnitState("RttTex_DfShLamp");
+	tState_DfShLamp->setTextureAddressingMode(Ogre::TextureUnitState::TAM_CLAMP) ;
+	RTT_Mat_DfShLamp->getTechnique(0)->getPass(0)->setLightingEnabled(false);
+	RTT_Mat_DfShLamp->getTechnique(0)->getPass(0)->setTextureFiltering(TFO_NONE) ;
+	//renderTexture_DfShLamp->addListener(this);
+
+	miniScreen_DfShLamp = new Ogre::Rectangle2D(true);
+	miniScreen_DfShLamp->setCorners(-1.0001, 1.0001, 1.0, -1.0);
+	miniScreen_DfShLamp->setBoundingBox(AxisAlignedBox(-100000.0*Vector3::UNIT_SCALE, 100000.0*Vector3::UNIT_SCALE)); 
+	miniScreenNode_DfShLamp = m_pSceneMgr->getRootSceneNode()->createChildSceneNode("MiniScreenNode_DfShLamp");
+	miniScreenNode_DfShLamp->attachObject(miniScreen_DfShLamp);
+	miniScreen_DfShLamp->setMaterial("RttMat_DfShLamp");
+	
+	
+
+	/*
+	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+	// DfShData
+
+	RTT_Texture_DfShData = Ogre::TextureManager::getSingleton().createManual("RttTex_DfShData",
+      "RTTResources", TEX_TYPE_2D, DSWidthB, DSHeightB, 0, PF_FLOAT16_RGB,
+      TU_RENDERTARGET);
+	
+	renderTexture_DfShData = RTT_Texture_DfShData->getBuffer()->getRenderTarget();
+	renderTexture_DfShData->addViewport(m_pCamera);
+	renderTexture_DfShData->getViewport(0)->setClearEveryFrame(true, FBT_DEPTH); 
+	renderTexture_DfShData->getViewport(0)->setBackgroundColour(ColourValue::Black);
+	renderTexture_DfShData->getViewport(0)->setOverlaysEnabled(false);
+
+	RTT_Mat_DfShData = MaterialManager::getSingleton().create("RttMat_DfShData", "RTTResources");
+	RTT_Technique_DfShData = RTT_Mat_DfShData->createTechnique();
+	RTT_Technique_DfShData->createPass();
+	TextureUnitState* tState_DfShData = RTT_Mat_DfShData->getTechnique(0)->getPass(0)->createTextureUnitState("RttTex_DfShData");
+	tState_DfShData->setTextureAddressingMode(Ogre::TextureUnitState::TAM_CLAMP) ;
+	RTT_Mat_DfShData->getTechnique(0)->getPass(0)->setLightingEnabled(false);
+	if(m_nSpeedRender)
+		RTT_Mat_DfShData->getTechnique(0)->getPass(0)->setTextureFiltering(TFO_BILINEAR) ;
+	else
+		RTT_Mat_DfShData->getTechnique(0)->getPass(0)->setTextureFiltering(TFO_NONE) ;
+	//renderTexture_DfShData->addListener(this);
+
+	miniScreen_DfShData = new Ogre::Rectangle2D(true);
+	miniScreen_DfShData->setCorners(-1.0001, 1.0001, 1.0, -1.0);
+	miniScreen_DfShData->setBoundingBox(AxisAlignedBox(-100000.0*Vector3::UNIT_SCALE, 100000.0*Vector3::UNIT_SCALE)); 
+	miniScreenNode_DfShData = m_pSceneMgr->getRootSceneNode()->createChildSceneNode("MiniScreenNode_DfShData");
+	miniScreenNode_DfShData->attachObject(miniScreen_DfShData);
+	miniScreen_DfShData->setMaterial("RttMat_DfShData");
+	
+	*/
+
+	
+	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+	// DfShTemp
+
+	RTT_Texture_DfShTemp = Ogre::TextureManager::getSingleton().createManual("RttTex_DfShTemp",
+      "RTTResources", TEX_TYPE_2D, TEMPWidth, TEMPHeight, 0, PF_FLOAT16_RGB, 
+      TU_RENDERTARGET);//, 0, false, 6);
+	
+	renderTexture_DfShTemp = RTT_Texture_DfShTemp->getBuffer()->getRenderTarget();
+	renderTexture_DfShTemp->addViewport(m_pCamera);
+	renderTexture_DfShTemp->getViewport(0)->setClearEveryFrame(true, FBT_DEPTH); 
+	renderTexture_DfShTemp->getViewport(0)->setBackgroundColour(ColourValue::Black);
+	renderTexture_DfShTemp->getViewport(0)->setOverlaysEnabled(false);
+
+	RTT_Mat_DfShTemp = MaterialManager::getSingleton().create("RttMat_DfShTemp", "RTTResources");
+	RTT_Technique_DfShTemp = RTT_Mat_DfShTemp->createTechnique();
+	RTT_Technique_DfShTemp->createPass();
+	TextureUnitState* tState_DfShTemp = RTT_Mat_DfShTemp->getTechnique(0)->getPass(0)->createTextureUnitState("RttTex_DfShTemp");
+	tState_DfShTemp->setTextureAddressingMode(Ogre::TextureUnitState::TAM_CLAMP) ;
+	RTT_Mat_DfShTemp->getTechnique(0)->getPass(0)->setLightingEnabled(false);
+	RTT_Mat_DfShTemp->getTechnique(0)->getPass(0)->setTextureFiltering(TFO_NONE) ;
+	//renderTexture_DfShTemp->addListener(this);
+
+	miniScreen_DfShTemp = new Ogre::Rectangle2D(true);
+	miniScreen_DfShTemp->setCorners(-1.0001, 1.0001, 1.0, -1.0);
+	miniScreen_DfShTemp->setBoundingBox(AxisAlignedBox(-100000.0*Vector3::UNIT_SCALE, 100000.0*Vector3::UNIT_SCALE)); 
+	miniScreenNode_DfShTemp = m_pSceneMgr->getRootSceneNode()->createChildSceneNode("MiniScreenNode_DfShTemp");
+	miniScreenNode_DfShTemp->attachObject(miniScreen_DfShTemp);
+	miniScreen_DfShTemp->setMaterial("RttMat_DfShTemp");
+
+
+	
+	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+	// DfShMix
+
+		RTT_Texture_DfShMix = Ogre::TextureManager::getSingleton().createManual("RttTex_DfShMix",
+      "RTTResources", TEX_TYPE_2D, RTTWidth, RTTHeight, 0, PF_FLOAT16_RGB,
+      TU_RENDERTARGET);
+
+	
+	renderTexture_DfShMix = RTT_Texture_DfShMix->getBuffer()->getRenderTarget();
+	renderTexture_DfShMix->addViewport(m_pCamera);
+	renderTexture_DfShMix->getViewport(0)->setClearEveryFrame(false);//, FBT_DEPTH);
+	renderTexture_DfShMix->getViewport(0)->setBackgroundColour(ColourValue::Black);
+	renderTexture_DfShMix->getViewport(0)->setOverlaysEnabled(false);
+
+	RTT_Mat_DfShMix = MaterialManager::getSingleton().create("RttMat_DfShMix", "RTTResources");
+	RTT_Technique_DfShMix = RTT_Mat_DfShMix->createTechnique();
+	RTT_Technique_DfShMix->createPass();
+	TextureUnitState* tState_DfShMix = RTT_Mat_DfShMix->getTechnique(0)->getPass(0)->createTextureUnitState("RttTex_DfShMix");
+	tState_DfShMix->setTextureAddressingMode(Ogre::TextureUnitState::TAM_CLAMP) ;
+	RTT_Mat_DfShMix->getTechnique(0)->getPass(0)->setLightingEnabled(false);
+	RTT_Mat_DfShMix->getTechnique(0)->getPass(0)->setTextureFiltering(TFO_NONE) ;
+
+
+	miniScreen_DfShMix = new Ogre::Rectangle2D(true);
+	miniScreen_DfShMix->setCorners(-1.0001, 1.0001, 1.0, -1.0);
+	miniScreen_DfShMix->setBoundingBox(AxisAlignedBox(-100000.0*Vector3::UNIT_SCALE, 100000.0*Vector3::UNIT_SCALE)); 
+	miniScreenNode_DfShMix = m_pSceneMgr->getRootSceneNode()->createChildSceneNode("MiniScreenNode_DfShMix");
+	miniScreenNode_DfShMix->attachObject(miniScreen_DfShMix);
+	miniScreen_DfShMix->setMaterial("RttMat_DfShMix");
+	
+	if(m_nRenderHDR)
+	{
+
+		
+		//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+		// BlurA
+		
+		RTT_Texture_BlurA = Ogre::TextureManager::getSingleton().createManual("RttTex_BlurA",
+				"RTTResources", TEX_TYPE_2D, DSWidth/2, DSHeight/2, 0, PF_FLOAT16_RGB,
+				TU_RENDERTARGET);
+		
+		renderTexture_BlurA = RTT_Texture_BlurA->getBuffer()->getRenderTarget();
+		renderTexture_BlurA->addViewport(m_pCamera);
+		renderTexture_BlurA->getViewport(0)->setClearEveryFrame(false);
+		renderTexture_BlurA->getViewport(0)->setBackgroundColour(ColourValue::Black);
+		renderTexture_BlurA->getViewport(0)->setOverlaysEnabled(false);
+
+		RTT_Mat_BlurA = MaterialManager::getSingleton().create("RttMat_BlurA", "RTTResources");
+		RTT_Technique_BlurA = RTT_Mat_BlurA->createTechnique();
+		RTT_Technique_BlurA->createPass();
+		TextureUnitState* tState_BlurA = RTT_Mat_BlurA->getTechnique(0)->getPass(0)->createTextureUnitState("RttTex_BlurA");
+		tState_BlurA->setTextureAddressingMode(Ogre::TextureUnitState::TAM_CLAMP) ;
+		RTT_Mat_BlurA->getTechnique(0)->getPass(0)->setLightingEnabled(false);
+		RTT_Mat_BlurA->getTechnique(0)->getPass(0)->setTextureFiltering(TFO_BILINEAR) ;
+
+		miniScreen_BlurA = new Ogre::Rectangle2D(true);
+		miniScreen_BlurA->setCorners(-1.0001, 1.0001, 1.0, -1.0);
+		miniScreen_BlurA->setBoundingBox(AxisAlignedBox(-100000.0*Vector3::UNIT_SCALE, 100000.0*Vector3::UNIT_SCALE)); 
+		miniScreenNode_BlurA = m_pSceneMgr->getRootSceneNode()->createChildSceneNode("MiniScreenNode_BlurA");
+		miniScreenNode_BlurA->attachObject(miniScreen_BlurA);
+		miniScreen_BlurA->setMaterial("RttMat_BlurA");
+
+		//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+		// BlurB
+		
+		RTT_Texture_BlurB = Ogre::TextureManager::getSingleton().createManual("RttTex_BlurB",
+				"RTTResources", TEX_TYPE_2D, DSWidth/2, DSHeight/2, 0, PF_FLOAT16_RGB,
+				TU_RENDERTARGET);
+		
+		renderTexture_BlurB = RTT_Texture_BlurB->getBuffer()->getRenderTarget();
+		renderTexture_BlurB->addViewport(m_pCamera);
+		renderTexture_BlurB->getViewport(0)->setClearEveryFrame(false);
+		renderTexture_BlurB->getViewport(0)->setBackgroundColour(ColourValue::Black);
+		renderTexture_BlurB->getViewport(0)->setOverlaysEnabled(false);
+
+		RTT_Mat_BlurB = MaterialManager::getSingleton().create("RttMat_BlurB", "RTTResources");
+		RTT_Technique_BlurB = RTT_Mat_BlurB->createTechnique();
+		RTT_Technique_BlurB->createPass();
+		TextureUnitState* tState_BlurB = RTT_Mat_BlurB->getTechnique(0)->getPass(0)->createTextureUnitState("RttTex_BlurB");
+		tState_BlurB->setTextureAddressingMode(Ogre::TextureUnitState::TAM_CLAMP) ;
+		RTT_Mat_BlurB->getTechnique(0)->getPass(0)->setLightingEnabled(false);
+		RTT_Mat_BlurB->getTechnique(0)->getPass(0)->setTextureFiltering(TFO_BILINEAR) ;
+
+		miniScreen_BlurB = new Ogre::Rectangle2D(true);
+		miniScreen_BlurB->setCorners(-1.0001, 1.0001, 1.0, -1.0);
+		miniScreen_BlurB->setBoundingBox(AxisAlignedBox(-100000.0*Vector3::UNIT_SCALE, 100000.0*Vector3::UNIT_SCALE)); 
+		miniScreenNode_BlurB = m_pSceneMgr->getRootSceneNode()->createChildSceneNode("MiniScreenNode_BlurB");
+		miniScreenNode_BlurB->attachObject(miniScreen_BlurB);
+		miniScreen_BlurB->setMaterial("RttMat_BlurB");
+		
+	} // end if m_nRenderHDR	
+	
+	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+	// mixing quad
+	miniScreen_DfShMaster = new Ogre::Rectangle2D(true);
+	miniScreen_DfShMaster->setCorners(-1.0001, 1.0001, 1.0, -1.0);
+	miniScreen_DfShMaster->setBoundingBox(AxisAlignedBox(-100000.0*Vector3::UNIT_SCALE, 100000.0*Vector3::UNIT_SCALE)); 
+	miniScreenNode_DfShMaster = m_pSceneMgr->getRootSceneNode()->createChildSceneNode("MiniScreenNode_DfShMaster");
+	miniScreenNode_DfShMaster->attachObject(miniScreen_DfShMaster);
+	//miniScreen_DfShMaster->setMaterial("RttMat_DfShDiffuse") ;
+	//miniScreen_DfShMaster->setMaterial("DfShMix") ; // doesn't matter what material we use, since we mix from pre-rendered textures
+	
+	//RTT_Mat_Shadow->getTechnique(0)->getPass(0)->getTextureUnitState("RttTex_Shadow")->setTextureAddressingMode(Ogre::TextureUnitState::TAM_CLAMP) ;
+	
+}
+
+void OgreFramework::DestroyRTTAssets()
+{
+}
+
+void OgreFramework::UpdateRenderTargets()
+{
+
+	int nZoneList=0 ;
+	int nZone=0 ;
+	Ogre::Entity*	pEntity=NULL ;
+	int nMO=0 ;
+	int nZGStart=0 ;
+	int nZGEnd=0 ;
+
+	int nVisEnt=0 ;
+	int nEntity=0 ;
+//	int nMaxEntity=0 ;
+	int nSubMesh=0 ;
+	int nMaxSubMesh=0 ;
+
+	int nMaxVisibleZone=*m_pGlobal_MaxVisibleZone ;
+
+	SetAllLightsOff() ;
+
+	
+	// extra texture params
+	// TexParam.x is 1 for d3d, -1 for ogl, needed for screen space UV calculations y flip
+	// TexParam.y is game time, in 1/10th of a second since start of level
+
+	Ogre::Vector4 TexParam ;
+	if(m_IsOpenGL) 
+		TexParam.x=-1 ; 
+	else 
+		TexParam.x= 1 ;
+	TexParam.y=m_GameTime/100 ;
+	TexParam.z=512.0f ; // pixelnoise subdivisions for the emmissive data function.
+
+
+	// remove all the nodes, so we start with a clean slate
+	m_pSceneMgr->getRootSceneNode()->removeAllChildren() ; // remove all the objects
+	
+
+	/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+	// 
+	// deferred shading data textures
+	//
+	/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+	SetZoneNodeAttachments(m_uVisibleZoneListL, &m_nMaxVisibleZoneL, MAT_OPAQUE) ;
+
+	// attach the entity masternode, update positions and orientations
+	for(nVisEnt=0 ; nVisEnt<m_nMaxFrustumEntity ; nVisEnt++)
+	{
+		nEntity=m_pFrustumEntity[nVisEnt] ;
+		m_pEntityInfo[ nEntity ].pMasterNode->setPosition( m_pEntityInfo[nEntity].Postition ) ; // set position.
+		m_pSceneMgr->getRootSceneNode()->addChild(m_pEntityInfo[ nEntity ].pMasterNode) ;
+	}
+
+
+	
+	
+	/////////////////////////////////////////////////////////////////////
+	// DfShPosition
+	
+	// change material
+	for(nZoneList=0 ; nZoneList<nMaxVisibleZone ; nZoneList++)
+	{
+		nZone=m_pGlobal_VisibleZoneList[nZoneList] ;
+		nZGStart	=	m_nZoneMOStart[nZone] ;
+		nZGEnd		=	m_nZoneMOStart[nZone+1] ;
+			
+		for(nMO=nZGStart ; nMO<nZGEnd ; nMO++)
+			if(m_pZoneEntityMaterialType[nMO]==MAT_OPAQUE)
+			{
+				pEntity=m_pZoneEntity[nMO] ;
+				pEntity->setMaterial( m_pZoneEntityMaterial_DfShPosition[nMO] ) ;
+			}// end for entities
+
+	}// end for zonelist
+
+
+	// first time we set materials we need to set non-opaque to the be invisible.  After we don't need to do it, they'll already be invisible
+	// Set the entity material
+
+	for(nVisEnt=0 ; nVisEnt<m_nMaxFrustumEntity ; nVisEnt++)
+	{
+		nEntity=m_pFrustumEntity[nVisEnt] ;
+		nMaxSubMesh=m_pEntityInfo[nEntity].MaxSubMesh ;
+
+		for(nSubMesh=0 ; nSubMesh<nMaxSubMesh ; nSubMesh++)
+			if(m_pEntityInfo[nEntity].SubMeshMaterialType[nSubMesh]==MAT_OPAQUE)
+			{
+				m_pEntityInfo[nEntity].pEnt->getSubEntity(nSubMesh)->setMaterial( m_pEntityInfo[nEntity].Material_DfShPosition[nSubMesh] ) ;
+				m_pEntityInfo[nEntity].pEnt->getSubEntity(nSubMesh)->setVisible(true) ; // first time around, make sure it is visible
+			}
+			else
+				m_pEntityInfo[nEntity].pEnt->getSubEntity(nSubMesh)->setVisible(false) ;
+	}
+
+
+
+	renderTexture_DfShPosition->update(true) ;
+	
+	/////////////////////////////////////////////////////////////////////
+	// DfShFuncTNB
+
+	// change material
+	for(nZoneList=0 ; nZoneList<nMaxVisibleZone ; nZoneList++)
+	{
+		nZone=m_pGlobal_VisibleZoneList[nZoneList] ;
+		nZGStart	=	m_nZoneMOStart[nZone] ;
+		nZGEnd		=	m_nZoneMOStart[nZone+1] ;
+			
+		for(nMO=nZGStart ; nMO<nZGEnd ; nMO++)
+			if(m_pZoneEntityMaterialType[nMO]==MAT_OPAQUE)
+			{
+				pEntity=m_pZoneEntity[nMO] ;
+				pEntity->setMaterial( m_pZoneEntityMaterial_DfShFuncTNB[nMO] ) ;
+			}// end for entities
+
+	}// end for zonelist
+
+	for(nVisEnt=0 ; nVisEnt<m_nMaxFrustumEntity ; nVisEnt++)
+	{
+		nEntity=m_pFrustumEntity[nVisEnt] ;
+		nMaxSubMesh=m_pEntityInfo[nEntity].MaxSubMesh ;
+
+		for(nSubMesh=0 ; nSubMesh<nMaxSubMesh ; nSubMesh++)
+			if(m_pEntityInfo[nEntity].SubMeshMaterialType[nSubMesh]==MAT_OPAQUE)
+				m_pEntityInfo[nEntity].pEnt->getSubEntity(nSubMesh)->setMaterial( m_pEntityInfo[nEntity].Material_DfShFuncTNB[nSubMesh] ) ;
+	}
+
+
+	renderTexture_DfShFuncTNB->update(true) ;
+	
+	
+	/////////////////////////////////////////////////////////////////////
+	// DfShDiffuse
+	
+	// the diffuse pass is used for the wireframe render as well
+
+
+
+	if(m_nDisplayInfoMode!=1)
+	{
+		// change material
+		for(nZoneList=0 ; nZoneList<nMaxVisibleZone ; nZoneList++)
+		{
+			nZone=m_pGlobal_VisibleZoneList[nZoneList] ;
+			nZGStart	=	m_nZoneMOStart[nZone] ;
+			nZGEnd		=	m_nZoneMOStart[nZone+1] ;
+				
+			for(nMO=nZGStart ; nMO<nZGEnd ; nMO++)
+				if(m_pZoneEntityMaterialType[nMO]==MAT_OPAQUE)
+				{
+					pEntity=m_pZoneEntity[nMO] ;
+					pEntity->setMaterial( m_pZoneEntityMaterial_DfShDiffuse[nMO] ) ;
+				}// end for entities
+
+		}// end for zonelist
+
+		// set the entity material
+
+		for(nVisEnt=0 ; nVisEnt<m_nMaxFrustumEntity ; nVisEnt++)
+		{
+			nEntity=m_pFrustumEntity[nVisEnt] ;
+			nMaxSubMesh=m_pEntityInfo[nEntity].MaxSubMesh ;
+
+			for(nSubMesh=0 ; nSubMesh<nMaxSubMesh ; nSubMesh++)
+				if(m_pEntityInfo[nEntity].SubMeshMaterialType[nSubMesh]==MAT_OPAQUE)
+					m_pEntityInfo[nEntity].pEnt->getSubEntity(nSubMesh)->setMaterial( m_pEntityInfo[nEntity].Material_DfShDiffuse[nSubMesh] ) ;
+		}
+
+
+		renderTexture_DfShDiffuse->update(true) ;
+	}
+	else // if we are doing wireframe we use this pass for it
+	{
+		// add gels just for the wireframe
+		SetZoneNodeAttachments(m_uVisibleZoneListL, &m_nMaxVisibleZoneL, MAT_GEL) ;	
+
+
+		// clear the RTT texture, since our wireframe won't clear it.
+		m_pRoot->getRenderSystem()->_setViewport(renderTexture_DfShDiffuse->getViewport(0)) ;
+		m_pRoot->getRenderSystem()->clearFrameBuffer(FBT_COLOUR|FBT_DEPTH, Ogre::ColourValue(0,0,0,1)) ;
+		
+		// set the camera to wireframe mode
+		m_pCamera->setPolygonMode(PM_WIREFRAME);
+
+		// change material
+		for(nZoneList=0 ; nZoneList<nMaxVisibleZone ; nZoneList++)
+		{
+			nZone=m_pGlobal_VisibleZoneList[nZoneList] ;
+			nZGStart	=	m_nZoneMOStart[nZone] ;
+			nZGEnd		=	m_nZoneMOStart[nZone+1] ;
+				
+			for(nMO=nZGStart ; nMO<nZGEnd ; nMO++)
+				if((m_pZoneEntityMaterialType[nMO]==MAT_OPAQUE) || (m_pZoneEntityMaterialType[nMO]==MAT_GEL))
+				{
+					pEntity=m_pZoneEntity[nMO] ;
+					pEntity->setMaterial( MaterialManager::getSingleton().getByName("White") ) ;
+				}// end for entities
+
+		}// end for zonelist
+
+		for(nVisEnt=0 ; nVisEnt<m_nMaxFrustumEntity ; nVisEnt++)
+		{
+			nEntity=m_pFrustumEntity[nVisEnt] ;
+			nMaxSubMesh=m_pEntityInfo[nEntity].MaxSubMesh ;
+
+			for(nSubMesh=0 ; nSubMesh<nMaxSubMesh ; nSubMesh++)
+			{
+				if(m_pEntityInfo[nEntity].SubMeshMaterialType[nSubMesh]!=MAT_OPAQUE) // any non-opaque submeshes need to be made visible
+					m_pEntityInfo[nEntity].pEnt->getSubEntity(nSubMesh)->setVisible(true) ;
+
+				m_pEntityInfo[nEntity].pEnt->getSubEntity(nSubMesh)->setMaterial(MaterialManager::getSingleton().getByName("White") ) ;
+			}
+		}
+
+		renderTexture_DfShDiffuse->update(true) ;
+	
+		// reset back to pre-wireframe setup
+		m_pCamera->setPolygonMode(PM_SOLID);
+
+		// remove everything and then add back OPAQUE
+		m_pSceneMgr->getRootSceneNode()->removeAllChildren() ; // remove all the objects
+		SetZoneNodeAttachments(m_uVisibleZoneListL, &m_nMaxVisibleZoneL, MAT_OPAQUE) ;
+
+
+		// reattach entities, make non-opaque subentities invisible
+		for(nVisEnt=0 ; nVisEnt<m_nMaxFrustumEntity ; nVisEnt++)
+		{
+			nEntity=m_pFrustumEntity[nVisEnt] ;
+			nMaxSubMesh=m_pEntityInfo[nEntity].MaxSubMesh ;
+
+			m_pSceneMgr->getRootSceneNode()->addChild(m_pEntityInfo[  nEntity  ].pMasterNode) ;
+
+			for(nSubMesh=0 ; nSubMesh<nMaxSubMesh ; nSubMesh++)
+			{
+				if(m_pEntityInfo[nEntity].SubMeshMaterialType[nSubMesh]!=MAT_OPAQUE) 
+				m_pEntityInfo[nEntity].pEnt->getSubEntity(nSubMesh)->setVisible(false) ;
+			}
+		}
+
+
+
+	}
+
+	
+	
+	/////////////////////////////////////////////////////////////////////
+	// DfShSpecular
+
+	// change material
+	for(nZoneList=0 ; nZoneList<nMaxVisibleZone ; nZoneList++)
+	{
+		nZone=m_pGlobal_VisibleZoneList[nZoneList] ;
+		nZGStart	=	m_nZoneMOStart[nZone] ;
+		nZGEnd		=	m_nZoneMOStart[nZone+1] ;
+			
+		for(nMO=nZGStart ; nMO<nZGEnd ; nMO++)
+			if(m_pZoneEntityMaterialType[nMO]==MAT_OPAQUE)
+			{
+				pEntity=m_pZoneEntity[nMO] ;
+				pEntity->setMaterial( m_pZoneEntityMaterial_DfShSpecular[nMO] ) ;
+			}// end for entities
+
+	}// end for zonelist
+
+	for(nVisEnt=0 ; nVisEnt<m_nMaxFrustumEntity ; nVisEnt++)
+	{
+		nEntity=m_pFrustumEntity[nVisEnt] ;
+		nMaxSubMesh=m_pEntityInfo[nEntity].MaxSubMesh ;
+
+		// set the entity material
+		for(nSubMesh=0 ; nSubMesh<nMaxSubMesh ; nSubMesh++)
+			if(m_pEntityInfo[nEntity].SubMeshMaterialType[nSubMesh]==MAT_OPAQUE)
+				m_pEntityInfo[nEntity].pEnt->getSubEntity(nSubMesh)->setMaterial( m_pEntityInfo[nEntity].Material_DfShSpecular[nSubMesh] ) ;
+	}
+
+	renderTexture_DfShSpecular->update(true) ;
+	
+	/*
+	/////////////////////////////////////////////////////////////////////
+	// DfShData
+
+	// change material
+	for(nZoneList=0 ; nZoneList<nMaxVisibleZone ; nZoneList++)
+	{
+		nZone=m_pGlobal_VisibleZoneList[nZoneList] ;
+		nZGStart	=	m_nZoneMOStart[nZone] ;
+		nZGEnd		=	m_nZoneMOStart[nZone+1] ;
+			
+		for(nMO=nZGStart ; nMO<nZGEnd ; nMO++)
+			if(m_pZoneEntityMaterialType[nMO]==MAT_OPAQUE)
+			{
+				pEntity=m_pZoneEntity[nMO] ;
+				pEntity->setMaterial( m_pZoneEntityMaterial_DfShData[nMO] ) ;
+			}// end for entities
+
+	}// end for zonelist
+
+	renderTexture_DfShData->update(true) ;
+	*/
+
+	
+	/////////////////////////////////////////////////////////////////////
+	// DfShEmissive
+
+	// we add the glow entities for this pass, if we are doing HDR
+	if(m_nRenderHDR)
+		SetZoneNodeAttachments(m_uVisibleZoneListL, &m_nMaxVisibleZoneL, MAT_GLOW) ;
+
+
+	// change material
+	for(nZoneList=0 ; nZoneList<nMaxVisibleZone ; nZoneList++)
+	{
+		nZone=m_pGlobal_VisibleZoneList[nZoneList] ;
+		nZGStart	=	m_nZoneMOStart[nZone] ;
+		nZGEnd		=	m_nZoneMOStart[nZone+1] ;
+			
+		for(nMO=nZGStart ; nMO<nZGEnd ; nMO++)
+			if((m_pZoneEntityMaterialType[nMO]==MAT_OPAQUE) || (m_pZoneEntityMaterialType[nMO]==MAT_GLOW))
+			{
+				pEntity=m_pZoneEntity[nMO] ;
+				pEntity->setMaterial( m_pZoneEntityMaterial_DfShEmissive[nMO] ) ;
+
+				TexParam.z=512.0f ; // pixelnoise subdivisions for the emmissive data function.
+				pEntity->getSubEntity(0)->setCustomParameter(4, TexParam) ; // emissive makes use of gametime for some lighting effects
+			}// end for entities
+
+	}// end for zonelist
+
+	// set the entity material
+	for(nVisEnt=0 ; nVisEnt<m_nMaxFrustumEntity ; nVisEnt++)
+	{
+		nEntity=m_pFrustumEntity[nVisEnt] ;
+		nMaxSubMesh=m_pEntityInfo[nEntity].MaxSubMesh ;
+
+		for(nSubMesh=0 ; nSubMesh<nMaxSubMesh ; nSubMesh++)
+			if((m_pEntityInfo[nEntity].SubMeshMaterialType[nSubMesh]==MAT_OPAQUE) || (m_pEntityInfo[nEntity].SubMeshMaterialType[nSubMesh]==MAT_GLOW))
+			{
+				// make glow stuff visible
+				if(m_pEntityInfo[nEntity].SubMeshMaterialType[nSubMesh]==MAT_GLOW)
+					m_pEntityInfo[nEntity].pEnt->getSubEntity(nSubMesh)->setVisible(true) ;
+
+				m_pEntityInfo[nEntity].pEnt->getSubEntity(nSubMesh)->setMaterial( m_pEntityInfo[nEntity].Material_DfShEmissive[nSubMesh] ) ;
+			}
+	}
+
+	renderTexture_DfShEmissive->update(true) ;
+
+	
+
+	////////////////////////////////////////////////////////////////////
+	// we need to know how to colour stuff if we're seeing it through gel
+	// this colouring will be applied when we mix lamp and emmissive down to DfShMix
+	//
+	// Note that this leaves the light/lamp glow entities attached in HDR mode,
+	// we need them to block the gels otherwise the blur on the lights/lamps 
+	// comes out incorrectly coloured by gel that should be behind it. 
+
+	// add the transparent stuff
+	SetZoneNodeAttachments(m_uVisibleZoneListL, &m_nMaxVisibleZoneL, MAT_GEL) ;
+
+	// change material
+	for(nZoneList=0 ; nZoneList<nMaxVisibleZone ; nZoneList++)
+	{
+		nZone=m_pGlobal_VisibleZoneList[nZoneList] ;
+		nZGStart	=	m_nZoneMOStart[nZone] ;
+		nZGEnd		=	m_nZoneMOStart[nZone+1] ;
+			
+		for(nMO=nZGStart ; nMO<nZGEnd ; nMO++)
+			if((m_pZoneEntityMaterialType[nMO]==MAT_OPAQUE) || (m_pZoneEntityMaterialType[nMO]==MAT_GEL) || (m_pZoneEntityMaterialType[nMO]==MAT_GLOW))
+			{
+				pEntity=m_pZoneEntity[nMO] ;
+				pEntity->setMaterial( m_pZoneEntityMaterial_DfShColour[nMO] ) ;
+			}// end for entities
+
+
+	}// end for zonelist
+
+	for(nVisEnt=0 ; nVisEnt<m_nMaxFrustumEntity ; nVisEnt++)
+	{
+		nEntity=m_pFrustumEntity[nVisEnt] ;
+		nMaxSubMesh=m_pEntityInfo[nEntity].MaxSubMesh ;
+
+		// set the entity material
+		for(nSubMesh=0 ; nSubMesh<nMaxSubMesh ; nSubMesh++)
+			if((m_pEntityInfo[nEntity].SubMeshMaterialType[nSubMesh]==MAT_OPAQUE) || (m_pEntityInfo[nEntity].SubMeshMaterialType[nSubMesh]==MAT_GEL) || (m_pEntityInfo[nEntity].SubMeshMaterialType[nSubMesh]==MAT_GLOW))
+			{
+				// make gel stuff visible (glow was already made visible on the emissive pass)
+				if(m_pEntityInfo[nEntity].SubMeshMaterialType[nSubMesh]==MAT_GEL)
+					m_pEntityInfo[nEntity].pEnt->getSubEntity(nSubMesh)->setVisible(true) ;
+
+				m_pEntityInfo[nEntity].pEnt->getSubEntity(nSubMesh)->setMaterial( m_pEntityInfo[nEntity].Material_DfShColour[nSubMesh] ) ;
+			}
+	}
+
+	// render colour to the temp surface
+	renderTexture_DfShTemp->update(true) ;
+
+	
+
+	//clear the mix buffer once
+	m_pRoot->getRenderSystem()->_setViewport(renderTexture_DfShMix->getViewport(0)) ;
+	m_pRoot->getRenderSystem()->clearFrameBuffer(FBT_COLOUR|FBT_DEPTH, Ogre::ColourValue(0,0,0,1)) ;
+
+	// remove all the nodes
+	m_pSceneMgr->getRootSceneNode()->removeAllChildren() ; // remove all the objects
+
+	/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+	// 
+	// deferred shading non-shadowing point lights
+	//
+	/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+
+	if(true)
+	{
+		SetZoneNodeAttachments(m_uVisibleZoneListL, &m_nMaxVisibleZoneL, MAT_LAMP) ; // attach the lamp boxes
+
+		// this renders our deferred shader lamps
+		renderTexture_DfShLamp->update(true) ;
+
+		// this shader combines the deferred light render with the emissive render and colours it with any gels
+		miniScreen_DfShMaster->setMaterial("Mix_Lamps") ; 
+
+		// get rid of all the scene geometry
+		m_pSceneMgr->getRootSceneNode()->removeAllChildren() ;
+
+		// put up a fullscreen quad so DfShMix has some UV coordinates.
+		m_pSceneMgr->getRootSceneNode()->addChild(miniScreenNode_DfShMaster) ;
+
+		// update DfShMix, but don't swap buffers yet since we may be back again next loop with more lights to add.
+		renderTexture_DfShMix->update(false) ; 
+
+		// now DfShMix has all the deferred render stuff mixed onto it, as well as emissive stuff, 
+		// and it's ready for forward rendered shadowing lights to be additively blended over it.
+	} // end if deferred render active
+
+
+
+	
+
+	/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+	// 
+	// forward rendered shadowing spotlights
+	//
+	/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+	// we now add entities from m_pVisibleEntity instead of m_pFrustumEntity since entities outside the frustum might still contribute shadows.
+	//''
+	if(true)
+	{
+
+
+		// multiple attempts at getting additive blending of the shadows failed, so I have to do an extra pass to "accumulate" the data :(
+
+		int nMaxVisibleLight=*m_pGlobal_MaxVisibleLight ;
+
+		// the texture view projection per light.  We use four Vector4 instead of a single Matrix4 because we have to pass them to the shaders as custom params
+		Ogre::Vector4	tvpMat0 ;
+		Ogre::Vector4	tvpMat1 ;
+		Ogre::Vector4	tvpMat2 ;
+		Ogre::Vector4	tvpMat3 ;
+
+
+
+		int nVisibleLight=0 ;
+		int nLt=0 ;
+		int nProjector=0 ;
+		int nEntLtIndex=0 ;
+		int nEntLtIndexMax=0 ;
+
+		m_nVisibleLightCount=0 ; // total shadow lights added from all loops 
+
+
+		miniScreen_DfShMaster->setMaterial("Mix_ShadowLights") ;
+		
+		
+		// loop through shadow casting lights
+		for(nVisibleLight=0 ; nVisibleLight<nMaxVisibleLight ; nVisibleLight++)
+		{
+			 // copy the camera's original settings because we'll be modifying it
+			m_pCameraCopy->synchroniseBaseSettingsWith(m_pCamera) ;
+
+			nLt=m_pGlobal_VisibleLightList[nVisibleLight] ;
+
+			// setup the light and light's shadow camera, skip if light isn't really visible
+			if(SetupSingleVisibleLightAndShadowCamera(nLt, m_pGlobal_LightVis, m_pGlobal_VisibleLightList, m_pCamera))
+			{
+				// count the light
+				m_nVisibleLightCount++ ;
+				
+				
+
+				
+				/////////////////////////////////////////////////////////////////////////////////////////////////////
+				// Render the shadow map
+
+				// add all the opaque stuff
+				m_pSceneMgr->getRootSceneNode()->removeAllChildren() ; 
+				SetZoneNodeAttachments(m_uVisibleZoneListL, &m_nMaxVisibleZoneL, MAT_OPAQUE) ; 
+
+				
+				for(nVisEnt=0 ; nVisEnt<m_nMaxVisibleEntity ; nVisEnt++)
+				{
+					// check if this entity touches this light
+					nEntity=m_pVisibleEntity[nVisEnt] ;
+					
+					m_pSceneMgr->getRootSceneNode()->addChild(m_pEntityInfo[ nEntity ].pMasterNode) ;
+
+					nEntLtIndexMax=m_pEntityInfo[nEntity].Light[INDEX_LIGHTPERENTITY] ;
+					m_pEntityInfo[nEntity].LightTouch=0 ; // default, doesn't touch this light, use black material
+					for(nEntLtIndex=0 ; nEntLtIndex<nEntLtIndexMax ; nEntLtIndex++)
+						if(m_pEntityInfo[nEntity].Light[nEntLtIndex]==nLt) // found a match, entity is touched by this light
+						{
+							m_pEntityInfo[nEntity].LightTouch=1 ; // touches this light, don't use black
+							break ;
+						}
+
+				}// end for nVisEnt
+				
+
+
+
+
+
+
+				// change to shadow pass material
+				for(nZoneList=0 ; nZoneList<nMaxVisibleZone ; nZoneList++)
+				{
+					nZone=m_pGlobal_VisibleZoneList[nZoneList] ;
+					nZGStart	=	m_nZoneMOStart[nZone] ;
+					nZGEnd		=	m_nZoneMOStart[nZone+1] ;
+						
+					for(nMO=nZGStart ; nMO<nZGEnd ; nMO++)
+						if(m_pZoneEntityMaterialType[nMO]==MAT_OPAQUE)
+						{
+							pEntity=m_pZoneEntity[nMO] ;
+							pEntity->setMaterial( m_pZoneEntityMaterial_Shadow[nMO] ) ;
+						}// end for entities
+
+				}// end for zonelist
+
+				for(nVisEnt=0 ; nVisEnt<m_nMaxVisibleEntity ; nVisEnt++)
+				{
+					nEntity=m_pVisibleEntity[nVisEnt] ;
+					nMaxSubMesh=m_pEntityInfo[nEntity].MaxSubMesh ;
+
+					// set the entity material
+					for(nSubMesh=0 ; nSubMesh<nMaxSubMesh ; nSubMesh++)
+						if(m_pEntityInfo[nEntity].SubMeshMaterialType[nSubMesh]==MAT_OPAQUE)
+							m_pEntityInfo[nEntity].pEnt->getSubEntity(nSubMesh)->setMaterial( m_pEntityInfo[nEntity].Material_Shadow[nSubMesh] ) ;
+						else
+							m_pEntityInfo[nEntity].pEnt->getSubEntity(nSubMesh)->setVisible(false) ; // non-opaque stuff is invisible
+				}
+
+
+				// render the shadow map out to the Shadow texture
+				renderTexture_Shadow->update(true) ;
+
+				// Opaque nodes are already in the scene, add all the Transparent stuff
+				SetZoneNodeAttachments(m_uVisibleZoneListL, &m_nMaxVisibleZoneL, MAT_GEL) ; 
+
+				// if this light is flagged as needing colourized shadows, we do the slower shadow method
+				// this means rendering an extra couple of coloured shadow maps here, plus a different material for the view render
+				if(m_nColouredShadow)
+				{
+					
+					/////////////////////////////////////////////////////////////////////////////////////////////////////
+					// Render the ShadeFront map
+
+					
+
+					// change to shadow pass material
+					for(nZoneList=0 ; nZoneList<nMaxVisibleZone ; nZoneList++)
+					{
+						nZone=m_pGlobal_VisibleZoneList[nZoneList] ;
+						nZGStart	=	m_nZoneMOStart[nZone] ;
+						nZGEnd		=	m_nZoneMOStart[nZone+1] ;
+							
+						for(nMO=nZGStart ; nMO<nZGEnd ; nMO++)
+							if((m_pZoneEntityMaterialType[nMO]==MAT_OPAQUE) || (m_pZoneEntityMaterialType[nMO]==MAT_GEL))
+							{
+								pEntity=m_pZoneEntity[nMO] ;
+								pEntity->setMaterial( m_pZoneEntityMaterial_ShadeFront[nMO] ) ;
+							}// end for entities
+
+					}// end for zonelist
+
+					for(nVisEnt=0 ; nVisEnt<m_nMaxVisibleEntity ; nVisEnt++)
+					{
+						nEntity=m_pVisibleEntity[nVisEnt] ;
+						nMaxSubMesh=m_pEntityInfo[nEntity].MaxSubMesh ;
+
+						// set the entity material (pClearMaterial is already set)
+						for(nSubMesh=0 ; nSubMesh<nMaxSubMesh ; nSubMesh++)
+							if((m_pEntityInfo[nEntity].SubMeshMaterialType[nSubMesh]==MAT_OPAQUE) || (m_pEntityInfo[nEntity].SubMeshMaterialType[nSubMesh]==MAT_GEL))
+							{
+								// make gel stuff visible
+								if(m_pEntityInfo[nEntity].SubMeshMaterialType[nSubMesh]==MAT_GEL)
+									m_pEntityInfo[nEntity].pEnt->getSubEntity(nSubMesh)->setVisible(true) ;
+
+								m_pEntityInfo[nEntity].pEnt->getSubEntity(nSubMesh)->setMaterial( m_pEntityInfo[nEntity].Material_ShadeFront[nSubMesh] ) ;
+							}
+					}
+
+					// render the shadow map out to the Shadow texture
+					renderTexture_ShadeFront->update(true) ;
+
+					
+					/////////////////////////////////////////////////////////////////////////////////////////////////////
+					// Render the ShadeBack map
+
+					// change to shadow pass material
+					for(nZoneList=0 ; nZoneList<nMaxVisibleZone ; nZoneList++)
+					{
+						nZone=m_pGlobal_VisibleZoneList[nZoneList] ;
+						nZGStart	=	m_nZoneMOStart[nZone] ;
+						nZGEnd		=	m_nZoneMOStart[nZone+1] ;
+							
+						for(nMO=nZGStart ; nMO<nZGEnd ; nMO++)
+							if((m_pZoneEntityMaterialType[nMO]==MAT_OPAQUE) || (m_pZoneEntityMaterialType[nMO]==MAT_GEL))
+							{
+								pEntity=m_pZoneEntity[nMO] ;
+								pEntity->setMaterial( m_pZoneEntityMaterial_ShadeBack[nMO] ) ;
+							}// end for entities
+
+					}// end for zonelist
+
+					for(nVisEnt=0 ; nVisEnt<m_nMaxVisibleEntity ; nVisEnt++)
+					{
+						nEntity=m_pVisibleEntity[nVisEnt] ;
+						nMaxSubMesh=m_pEntityInfo[nEntity].MaxSubMesh ;
+
+						// set the entity material (pClearMaterial is already set)
+						for(nSubMesh=0 ; nSubMesh<nMaxSubMesh ; nSubMesh++)
+							if((m_pEntityInfo[nEntity].SubMeshMaterialType[nSubMesh]==MAT_OPAQUE) || (m_pEntityInfo[nEntity].SubMeshMaterialType[nSubMesh]==MAT_GEL))
+								m_pEntityInfo[nEntity].pEnt->getSubEntity(nSubMesh)->setMaterial( m_pEntityInfo[nEntity].Material_ShadeBack[nSubMesh] ) ;
+					}
+
+					// render the shadow map out to the Shadow texture
+					renderTexture_ShadeBack->update(true) ;
+					
+				} // end if we need coloured shadows
+
+
+	
+
+
+
+
+				/////////////////////////////////////////////////////////////////////////////////////////////////////
+				// Apply the shadow map to the view
+
+				// calculate the texture view projection matrix for this light
+				Matrix4 TexViewProj=CreateTextureViewProjectionMatrix(m_pCamera) ;
+
+				// change the matrix to four Vector4s so we can pass it in as a shader parameter 
+				tvpMat0[0]=TexViewProj[0][0] ; tvpMat0[1]=TexViewProj[0][1] ; tvpMat0[2]=TexViewProj[0][2] ; tvpMat0[3]=TexViewProj[0][3] ;
+				tvpMat1[0]=TexViewProj[1][0] ; tvpMat1[1]=TexViewProj[1][1] ; tvpMat1[2]=TexViewProj[1][2] ; tvpMat1[3]=TexViewProj[1][3] ;
+				tvpMat2[0]=TexViewProj[2][0] ; tvpMat2[1]=TexViewProj[2][1] ; tvpMat2[2]=TexViewProj[2][2] ; tvpMat2[3]=TexViewProj[2][3] ;
+				tvpMat3[0]=TexViewProj[3][0] ; tvpMat3[1]=TexViewProj[3][1] ; tvpMat3[2]=TexViewProj[3][2] ; tvpMat3[3]=TexViewProj[3][3] ;
+
+				// put the camera back to its proper position and settings.
+				m_pCamera->synchroniseBaseSettingsWith(m_pCameraCopy) ;
+				m_pCamera->setFOVy( Degree(m_flFOV) ) ;
+				m_pCamera->setAspectRatio( Real(m_flAspectRatio) );
+
+
+				nProjector=m_Q3Map->m_pLight[nLt].Texture ; // what projector texture is this light using?
+
+				// change material and set all the tvp custom params
+				for(nZoneList=0 ; nZoneList<nMaxVisibleZone ; nZoneList++)
+				{
+					nZone=m_pGlobal_VisibleZoneList[nZoneList] ;
+					nZGStart	=	m_nZoneMOStart[nZone] ;
+					nZGEnd		=	m_nZoneMOStart[nZone+1] ;
+						
+					for(nMO=nZGStart ; nMO<nZGEnd ; nMO++)
+						if((m_pZoneEntityMaterialType[nMO]==MAT_OPAQUE) || (m_pZoneEntityMaterialType[nMO]==MAT_GEL))
+						{
+							pEntity=m_pZoneEntity[nMO] ;
+
+
+							// since the spot light pass is one of the slowest, we set the material of all the zones that don't touch
+							// this light to just a black material.  Only the zone of this light gets the real lighting material
+							// We need to have the other zones though or we end up seeing through walls.
+							// ALSO don't set gels to black, or else they block out the light in other zones.
+
+							
+							if((m_pZoneEntityMaterialType[nMO]==MAT_GEL) || (LightTouchesZone(nLt, nZone)) )
+							{
+								if(m_nColouredShadow)
+									pEntity->setMaterial( m_pZoneEntityMaterial_Base[nProjector][nMO] ) ; // coloured shadows
+								else
+									pEntity->setMaterial( m_pZoneEntityMaterial_Fast[nProjector][nMO] ) ; // fast shadows
+							}
+							else
+								pEntity->setMaterial( m_pZoneEntityMaterial_Black[nMO] ) ; // just flat black, alpha tested for some
+
+
+							
+				
+
+							// set the custom parameter, our texture view projection matrix for this light
+							pEntity->getSubEntity(0)->setCustomParameter(0, tvpMat0) ;
+							pEntity->getSubEntity(0)->setCustomParameter(1, tvpMat1) ;
+							pEntity->getSubEntity(0)->setCustomParameter(2, tvpMat2) ;
+							pEntity->getSubEntity(0)->setCustomParameter(3, tvpMat3) ;
+
+							pEntity->getSubEntity(0)->setCustomParameter(4, TexParam) ;
+
+						}// end for entities
+
+				}// end for zonelist
+
+				for(nVisEnt=0 ; nVisEnt<m_nMaxVisibleEntity ; nVisEnt++)
+				{
+					nEntity=m_pVisibleEntity[nVisEnt] ;
+					nMaxSubMesh=m_pEntityInfo[nEntity].MaxSubMesh ;
+
+					// set the entity material
+					for(nSubMesh=0 ; nSubMesh<nMaxSubMesh ; nSubMesh++)
+						if((m_pEntityInfo[nEntity].SubMeshMaterialType[nSubMesh]==MAT_OPAQUE) || (m_pEntityInfo[nEntity].SubMeshMaterialType[nSubMesh]==MAT_GEL))
+						{
+
+							// if it's gel or the light touches the entity, use proper material, else use black
+							if((m_pEntityInfo[nEntity].SubMeshMaterialType[nSubMesh]==MAT_GEL) || m_pEntityInfo[nEntity].LightTouch)
+							{
+								if(m_nColouredShadow)
+									m_pEntityInfo[nEntity].pEnt->getSubEntity(nSubMesh)->setMaterial( m_pEntityInfo[nEntity].Material_Base[nProjector][nSubMesh] ) ; // coloured shadows
+								else
+									m_pEntityInfo[nEntity].pEnt->getSubEntity(nSubMesh)->setMaterial( m_pEntityInfo[nEntity].Material_Fast[nProjector][nSubMesh] ) ; // fast shadows
+							}
+							else
+								m_pEntityInfo[nEntity].pEnt->getSubEntity(nSubMesh)->setMaterial( m_pEntityInfo[nEntity].Material_Black[nSubMesh] ) ; // flat black
+
+
+
+							// set the custom parameter, our texture view projection matrix for this light
+								m_pEntityInfo[nEntity].pEnt->getSubEntity(nSubMesh)->setCustomParameter(0, tvpMat0) ;
+								m_pEntityInfo[nEntity].pEnt->getSubEntity(nSubMesh)->setCustomParameter(1, tvpMat1) ;
+								m_pEntityInfo[nEntity].pEnt->getSubEntity(nSubMesh)->setCustomParameter(2, tvpMat2) ;
+								m_pEntityInfo[nEntity].pEnt->getSubEntity(nSubMesh)->setCustomParameter(3, tvpMat3) ;
+
+								m_pEntityInfo[nEntity].pEnt->getSubEntity(nSubMesh)->setCustomParameter(4, TexParam) ;
+						}
+						else
+								m_pEntityInfo[nEntity].pEnt->getSubEntity(nSubMesh)->setMaterial( pClearMaterial ) ;
+				}
+
+
+
+
+				// if this is the first visible light, add emmissive transparent/alphablend particles.  
+				// they are added here so that they render correctly according to gels.
+				// But since they aren't shadowed, we only need render them for one light.
+				if(m_nVisibleLightCount==1)
+					m_pSceneMgr->getRootSceneNode()->addChild(m_pParticleNode) ; // attach the particles
+
+
+
+				/////////////////////////////////////////////////////////////////////////////////////////////
+				// render the scene out to the DfShTemp texture.
+				//
+
+				renderTexture_DfShTemp->update(true) ; 
+
+				//
+				/////////////////////////////////////////////////////////////////////////////////////////////
+				
+				
+				
+				// remove the particles
+				if(m_nVisibleLightCount==1)
+					m_pSceneMgr->getRootSceneNode()->removeChild(m_pParticleNode) ;
+
+
+				///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+				// Render Mix
+
+				//?? unfortunately Ogre shares depth buffers in a manner that won't allow us to just additively render to DfShMix.
+				// If we try, we get glitching from the buffer sharing between Shadow and DfShMix.
+				// So instead we get rid of all geometry and additively render a fullscreen quad of DfShTemp to DfShMix.
+				// Slows things down a little, but at least it works.
+
+				// get rid of all the scene geometry
+				m_pSceneMgr->getRootSceneNode()->removeAllChildren() ;
+
+				// put up a fullscreen quad so DfShMix has some UV coordinates.
+				m_pSceneMgr->getRootSceneNode()->addChild(miniScreenNode_DfShMaster) ;
+
+				// update DfShMix, but don't swap buffers yet since we may be back again next loop with more lights to add.
+				renderTexture_DfShMix->update(false) ; 
+
+			}// end if light is visible
+
+
+		}// end for nVisibleLight
+			
+	}// end if forward render active
+
+	///////////////////////////////////////////////////////////////////////////////////////
+
+
+
+	// after we've finished with all the lights and mixing, time to swap the mix buffer
+	renderTexture_DfShMix->swapBuffers(false) ;
+
+	
+	/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+	// 
+	// HDR bloom
+	//
+	/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+	
+	if(m_nRenderHDR)
+	{
+		/////////////////////////////////////////////////////////
+		// copy DfShMix to BlurA
+
+		// get rid of all the scene geometry
+		m_pSceneMgr->getRootSceneNode()->removeAllChildren() ;
+
+		// this shader renders DfShMix onto a surface 
+		miniScreen_DfShMaster->setMaterial("HDR_MixToBlurA") ; 
+
+		// put up a fullscreen quad so BlurA has some UV coordinates.
+		m_pSceneMgr->getRootSceneNode()->addChild(miniScreenNode_DfShMaster) ;
+
+		// render DfShMix to BlurA
+		renderTexture_BlurA->update(true) ;
+
+		
+
+
+		//////////////////////////////////////////////////////////////
+		// now we do a few rounds of ping-pong bluring of A to B, B to A.
+		int nMaxBlur=4 ;
+//		int nLastBlur=nMaxBlur-1 ;
+		int nBlurLoop=0 ;
+
+		
+		// opengl and direct3d give different results for the same blur shader, 
+		// the issue is that d3d with bilinear filtering will offset stuff, 
+		// leading to a right-down drift that we need to compensate for.
+		// So we need two versions of the blur shaders, one for ogl and one for d3d.
+
+		if(m_IsOpenGL)
+		{
+			miniScreen_BlurA->setMaterial("HDR_BlurA_ogl");
+			miniScreen_BlurB->setMaterial("HDR_BlurB_ogl");
+		}
+		else
+		{
+			miniScreen_BlurA->setMaterial("HDR_BlurA_d3d");
+			miniScreen_BlurB->setMaterial("HDR_BlurB_d3d");
+		}
+
+				
+		// the main blur
+		for(nBlurLoop=0 ; nBlurLoop<nMaxBlur ; nBlurLoop++)
+		{
+			// blur A onto B
+			m_pSceneMgr->getRootSceneNode()->removeAllChildren() ;
+			m_pSceneMgr->getRootSceneNode()->addChild(miniScreenNode_BlurA) ;
+			renderTexture_BlurB->update(true) ;
+
+			// blur B onto A
+			m_pSceneMgr->getRootSceneNode()->removeAllChildren() ;
+			m_pSceneMgr->getRootSceneNode()->addChild(miniScreenNode_BlurB) ;
+			renderTexture_BlurA->update(true) ;
+		}
+		
+
+		// If I don't set these materials back to their originals, I get a crash on exit. 
+		// Not sure why.  Something to do with deleting the originals?
+		// Doesn't matter, this fixes things.
+
+		miniScreen_BlurA->setMaterial("RttMat_BlurA");
+		miniScreen_BlurB->setMaterial("RttMat_BlurB");
+		
+		
+
+		//////////////////////////////////////////////////////////////////
+		// blend BlurA onto DfShMix
+
+		// get rid of all the scene geometry
+		m_pSceneMgr->getRootSceneNode()->removeAllChildren() ;
+
+		// this shader blends BlurA onto a surface
+		if(m_IsOpenGL)
+			miniScreen_DfShMaster->setMaterial("HDR_BlurAToMix_ogl") ;
+		else
+			miniScreen_DfShMaster->setMaterial("HDR_BlurAToMix_d3d") ;
+
+		// put up a fullscreen quad so DfShMix has some UV coordinates.
+		m_pSceneMgr->getRootSceneNode()->addChild(miniScreenNode_DfShMaster) ;
+
+		// update DfShMix.
+		renderTexture_DfShMix->update(true) ;
+
+	}// end if HDR
+	
+
+	/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+	// 
+	// display a rendered window
+	//
+	/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+	
+	m_pSceneMgr->getRootSceneNode()->removeAllChildren() ; // remove all the objects
+	
+	
+	if(m_nGoggleMode==GOGGLE_MODE_OFF) // stereo rendering not active
+	{
+		switch(m_nDisplayInfoMode)
+		{
+
+			case 0:	m_pSceneMgr->getRootSceneNode()->addChild(miniScreenNode_DfShMix) ; break ;
+			case 1:	m_pSceneMgr->getRootSceneNode()->addChild(miniScreenNode_DfShTemp) ; break ; // for wireframe view
+			case 2:	m_pSceneMgr->getRootSceneNode()->addChild(miniScreenNode_DfShLamp) ; break ;
+			case 3:	m_pSceneMgr->getRootSceneNode()->addChild(miniScreenNode_BlurA) ; break ;
+
+			case 4: // instead of showing the original position texture, we show a scaled version, since the original is outside 0-1 range
+
+				// get rid of all the scene geometry
+				m_pSceneMgr->getRootSceneNode()->removeAllChildren() ;
+
+				// this shader renders DfShMix onto a surface 
+				miniScreen_DfShMaster->setMaterial("Mix_ScalePosition") ; 
+
+				// put up a fullscreen quad so BlurA has some UV coordinates.
+				m_pSceneMgr->getRootSceneNode()->addChild(miniScreenNode_DfShMaster) ;
+
+				// render DfShPostion to DfShMix
+				renderTexture_DfShMix->update(true) ;
+
+				// display mix, which is a scaled copy of position.
+				m_pSceneMgr->getRootSceneNode()->addChild(miniScreenNode_DfShMix) ;
+				break ;
+			
+			case 5:	m_pSceneMgr->getRootSceneNode()->addChild(miniScreenNode_DfShFuncTNB) ; break ;
+			case 6:	m_pSceneMgr->getRootSceneNode()->addChild(miniScreenNode_DfShDiffuse) ; break ;
+			case 7:	m_pSceneMgr->getRootSceneNode()->addChild(miniScreenNode_DfShSpecular) ; break ;			
+			case 8:	m_pSceneMgr->getRootSceneNode()->addChild(miniScreenNode_DfShEmissive) ; break ;
+		}
+
+
+
+
+		
+		//m_pSceneMgr->getRootSceneNode()->addChild(miniScreenNode_DfShPosition) ;
+		//m_pSceneMgr->getRootSceneNode()->addChild(miniScreenNode_DfShFuncTNB) ;
+		//m_pSceneMgr->getRootSceneNode()->addChild(miniScreenNode_DfShSpecular) ;
+		//m_pSceneMgr->getRootSceneNode()->addChild(miniScreenNode_DfShDiffuse) ;
+		//m_pSceneMgr->getRootSceneNode()->addChild(miniScreenNode_DfShEmissive) ;
+		//m_pSceneMgr->getRootSceneNode()->addChild(miniScreenNode_DfShData) ;
+		//m_pSceneMgr->getRootSceneNode()->addChild(miniScreenNode_DfShLamp) ;
+
+		//m_pSceneMgr->getRootSceneNode()->addChild(miniScreenNode_BlurA) ;
+
+		//m_pSceneMgr->getRootSceneNode()->addChild(miniScreenNode_DfShMix) ;
+		
+		//m_pSceneMgr->getRootSceneNode()->addChild(miniScreenNode_Shadow) ;
+		//m_pSceneMgr->getRootSceneNode()->addChild(miniScreenNode_DfShTemp) ;
+		//m_pSceneMgr->getRootSceneNode()->addChild(miniScreenNode_ShadeBack) ;
+		//m_pSceneMgr->getRootSceneNode()->addChild(miniScreenNode_ShadeFront) ;
+	}
+	else
+	{
+		// add and position the Goggles
+		m_pSceneMgr->getRootSceneNode()->addChild(m_pNodeGoggles) ;
+		m_pNodeGoggles->setOrientation( m_pCamera->getOrientation() ) ;
+		m_pNodeGoggles->setPosition( m_pCamera->getPosition() ) ;
+	}
+
+	
+	//m_pSceneMgr->getRootSceneNode()->removeAllChildren() ;
+	//m_pSceneMgr->getRootSceneNode()->addChild(miniScreenNode_DfShDiffuse) ;
+	
+	
+	m_pRenderWnd->update(true) ;
+
+
+	//m_pSceneMgr->getRootSceneNode()->removeChild(miniScreenNode_DfShSpecular) ;
+	m_pSceneMgr->getRootSceneNode()->removeAllChildren() ;
+
+	
+
+
+	///////////////////////////////////////////
+	// blittomemory experiment
+	//uchar pixelData[64] ;
+	//RTT_Texture_DfShPosition->getBuffer()->blitToMemory( Box( 0, 0, 1, 1), PixelBox(2, 2, 1, PF_FLOAT16_RGB, (void*)pixelData) ) ;
+
+
+}
+
+// generate the texture view Projection matrix used in shadow mapping
+Ogre::Matrix4 OgreFramework::CreateTextureViewProjectionMatrix(Ogre::Camera* pCamera)
+{
+
+	const Matrix4 PROJECTIONCLIPSPACE2DTOIMAGESPACE_PERSPECTIVE(
+		      0.5,    0,    0,  0.5,
+			    0,   -0.5,    0,  0.5,
+				  0,      0,    1,    0,
+					0,      0,    0,    1);
+
+	Matrix4 TexViewProj = PROJECTIONCLIPSPACE2DTOIMAGESPACE_PERSPECTIVE * pCamera->getProjectionMatrixWithRSDepth() * pCamera->getViewMatrix();
+
+	return TexViewProj ;
+
+}
+
+void OgreFramework::DestroyGoggles()
+{
+	//m_pGoggleL->~ManualObject() ;
+	//m_pGoggleR->~ManualObject() ;
+
+	//m_pNodeGoggles->~SceneNode() ;
+	
+
+}
+
+// create the "virtual" virtual reality goggles we use to get around stereoscopic driver problems.
+int OgreFramework::CreateGoggles()
+{
+
+	
+	float flMinX=0.0f ;
+	float flMinY=0.0f ;
+	float flMaxX=0.0f ;
+	float flMaxY=0.0f ;
+
+
+	// if they already exist, destroy them so we can recreate them
+	if(m_pNodeGoggles!=NULL)	m_pNodeGoggles->detachAllObjects() ;
+	if(m_pGoggleL!=NULL)			m_pGoggleL->clear() ;
+	if(m_pGoggleR!=NULL)			m_pGoggleR->clear() ;
+	
+
+	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+	// left goggle
+
+		if(m_pGoggleL==NULL) m_pGoggleL=m_pSceneMgr->createManualObject("GoggleL") ;
+		m_pGoggleL->begin("RttMat_DfShMix", RenderOperation::OT_TRIANGLE_LIST) ;
+	
+		flMinX=-100*m_flGoggleAspectRatio*m_flGoggleXScale ;
+		flMinY=-50*m_flGoggleYScale ;
+
+
+		flMaxX=0 ;
+		flMaxY=50*m_flGoggleYScale ;
+
+		
+		m_pGoggleL->position(flMinX-m_flGoggleXGap, flMaxY, m_flGoggleZPos) ;
+		m_pGoggleL->normal(0.0, 0.0, -1) ;
+		m_pGoggleL->textureCoord(0.0, 0.0) ;
+		m_pGoggleL->position(flMaxX-m_flGoggleXGap, flMaxY, m_flGoggleZPos) ;
+		m_pGoggleL->normal(0.0, 0.0, -1) ;
+		m_pGoggleL->textureCoord(1.0, 0.0) ;
+		m_pGoggleL->position(flMaxX-m_flGoggleXGap, flMinY, m_flGoggleZPos) ;
+		m_pGoggleL->normal(0.0, 0.0, -1) ;
+		m_pGoggleL->textureCoord(1.0, 1.0) ;
+		m_pGoggleL->position(flMinX-m_flGoggleXGap, flMinY, m_flGoggleZPos) ;
+		m_pGoggleL->normal(0.0, 0.0, -1) ;
+		m_pGoggleL->textureCoord(0.0, 1.0) ;
+
+		m_pGoggleL->quad(3, 2, 1, 0) ;
+		m_pGoggleL->end() ;
+
+		m_pGoggleL->setCastShadows(false) ;
+
+	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+	// right goggle
+
+		if(m_pGoggleR==NULL) m_pGoggleR=m_pSceneMgr->createManualObject("GoggleR") ;
+		m_pGoggleR->begin("RttMat_DfShMix", RenderOperation::OT_TRIANGLE_LIST) ;
+
+
+		flMinX=0 ;
+		flMinY=-50*m_flGoggleYScale ;
+
+
+		flMaxX=100*m_flGoggleAspectRatio*m_flGoggleXScale ;
+		flMaxY=50*m_flGoggleYScale ;
+
+		
+		m_pGoggleR->position(flMinX+m_flGoggleXGap, flMaxY, m_flGoggleZPos) ;
+		m_pGoggleR->normal(0.0, 0.0, -1) ;
+		m_pGoggleR->textureCoord(0.0, 0.0) ;
+		m_pGoggleR->position(flMaxX+m_flGoggleXGap, flMaxY, m_flGoggleZPos) ;
+		m_pGoggleR->normal(0.0, 0.0, -1) ;
+		m_pGoggleR->textureCoord(1.0, 0.0) ;
+		m_pGoggleR->position(flMaxX+m_flGoggleXGap, flMinY, m_flGoggleZPos) ;
+		m_pGoggleR->normal(0.0, 0.0, -1) ;
+		m_pGoggleR->textureCoord(1.0, 1.0) ;
+		m_pGoggleR->position(flMinX+m_flGoggleXGap, flMinY, m_flGoggleZPos) ;
+		m_pGoggleR->normal(0.0, 0.0, -1) ;
+		m_pGoggleR->textureCoord(0.0, 1.0) ;
+
+		m_pGoggleR->quad(3, 2, 1, 0) ;
+		m_pGoggleR->end() ;
+
+		m_pGoggleR->setCastShadows(false) ;
+		
+	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+	// attach the goggles to the node.
+	if(m_pNodeGoggles==NULL) m_pNodeGoggles=m_pSceneMgr->getRootSceneNode()->createChildSceneNode("NodeGoggles") ;
+	m_pNodeGoggles->attachObject(m_pGoggleL) ;
+	m_pNodeGoggles->attachObject(m_pGoggleR) ;
+
+	return 1 ;
+}
+
+// create the "virtual" virtual reality goggles we use to get around stereoscopic driver problems.
+// This version of the goggles just has a test image for setting up the correct real driver settings,
+// so that the left eye sees only the left "goggle" and the right eye only the right "goggle",
+// and the goggles have the right depth relative to the screen (flat to it)
+int OgreFramework::CreateGogglesTestImage() 
+{
+
+	float flMinX=0.0f ;
+	float flMinY=0.0f ;
+	float flMaxX=0.0f ;
+	float flMaxY=0.0f ;
+
+
+	// if they already exist, destroy them so we can recreate them
+	if(m_pNodeGoggles!=NULL)	m_pNodeGoggles->detachAllObjects() ;
+	if(m_pGoggleL!=NULL)			m_pGoggleL->clear() ;
+	if(m_pGoggleR!=NULL)			m_pGoggleR->clear() ;
+	
+
+	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+	// left goggle
+
+		if(m_pGoggleL==NULL) m_pGoggleL=m_pSceneMgr->createManualObject("GoggleL") ;
+		m_pGoggleL->begin("GoggleTest", RenderOperation::OT_TRIANGLE_LIST) ;
+	
+		flMinX=-100*m_flGoggleAspectRatio*m_flGoggleXScale ;
+		flMinY=-50*m_flGoggleYScale ;
+
+
+		flMaxX=0 ;
+		flMaxY=50*m_flGoggleYScale ;
+
+		
+		m_pGoggleL->position(flMinX-m_flGoggleXGap, flMaxY, m_flGoggleZPos) ;
+		m_pGoggleL->normal(0.0, 0.0, -1) ;
+		m_pGoggleL->textureCoord(0.0, 0.0) ;
+		m_pGoggleL->position(flMaxX-m_flGoggleXGap, flMaxY, m_flGoggleZPos) ;
+		m_pGoggleL->normal(0.0, 0.0, -1) ;
+		m_pGoggleL->textureCoord(1.0, 0.0) ;
+		m_pGoggleL->position(flMaxX-m_flGoggleXGap, flMinY, m_flGoggleZPos) ;
+		m_pGoggleL->normal(0.0, 0.0, -1) ;
+		m_pGoggleL->textureCoord(1.0, 1.0) ;
+		m_pGoggleL->position(flMinX-m_flGoggleXGap, flMinY, m_flGoggleZPos) ;
+		m_pGoggleL->normal(0.0, 0.0, -1) ;
+		m_pGoggleL->textureCoord(0.0, 1.0) ;
+
+		m_pGoggleL->quad(3, 2, 1, 0) ;
+		m_pGoggleL->end() ;
+
+		m_pGoggleL->setCastShadows(false) ;
+
+	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+	// right goggle
+
+		if(m_pGoggleR==NULL) m_pGoggleR=m_pSceneMgr->createManualObject("GoggleR") ;
+		m_pGoggleR->begin("GoggleTest", RenderOperation::OT_TRIANGLE_LIST) ;
+
+
+		flMinX=0 ;
+		flMinY=-50*m_flGoggleYScale ;
+
+
+		flMaxX=100*m_flGoggleAspectRatio*m_flGoggleXScale ;
+		flMaxY=50*m_flGoggleYScale ;
+
+		
+		m_pGoggleR->position(flMinX+m_flGoggleXGap, flMaxY, m_flGoggleZPos) ;
+		m_pGoggleR->normal(0.0, 0.0, -1) ;
+		m_pGoggleR->textureCoord(1.0, 1.0) ;
+		m_pGoggleR->position(flMaxX+m_flGoggleXGap, flMaxY, m_flGoggleZPos) ;
+		m_pGoggleR->normal(0.0, 0.0, -1) ;
+		m_pGoggleR->textureCoord(0.0, 1.0) ;
+		m_pGoggleR->position(flMaxX+m_flGoggleXGap, flMinY, m_flGoggleZPos) ;
+		m_pGoggleR->normal(0.0, 0.0, -1) ;
+		m_pGoggleR->textureCoord(0.0, 0.0) ;
+		m_pGoggleR->position(flMinX+m_flGoggleXGap, flMinY, m_flGoggleZPos) ;
+		m_pGoggleR->normal(0.0, 0.0, -1) ;
+		m_pGoggleR->textureCoord(1.0, 0.0) ;
+
+		m_pGoggleR->quad(3, 2, 1, 0) ;
+		m_pGoggleR->end() ;
+
+		m_pGoggleR->setCastShadows(false) ;
+		
+	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+	// attach the goggles to the node.
+	if(m_pNodeGoggles==NULL) m_pNodeGoggles=m_pSceneMgr->getRootSceneNode()->createChildSceneNode("NodeGoggles") ;
+	m_pNodeGoggles->attachObject(m_pGoggleL) ;
+	m_pNodeGoggles->attachObject(m_pGoggleR) ;
+
+	return 1 ;
+}
+
+//|||||||||||||||||||||||||||||||||||||||||||||||
+
+bool OgreFramework::keyPressed(const OIS::KeyEvent &keyEventRef)
+{
+	//m_pLog->logMessage("OgreFramework::keyPressed");
+	
+	if(m_pKeyboard->isKeyDown(OIS::KC_ESCAPE))
+	{
+			m_bShutDownOgre = true;
+			return true;
+	}
+
+	if(m_pKeyboard->isKeyDown(OIS::KC_SYSRQ))
+	{
+		std::ostringstream ss;
+		ss << "screenshot_" << ++m_iNumScreenShots << ".png";
+		m_pRenderWnd->writeContentsToFile(ss.str());
+		return true;
+	}
+
+	/*
+	if(m_pKeyboard->isKeyDown(OIS::KC_M))
+	{
+		static int mode = 0;
+		
+		if(mode == 2)
+		{
+			m_pCamera->setPolygonMode(PM_SOLID);
+			mode = 0;
+		}
+		else if(mode == 0)
+		{
+			 m_pCamera->setPolygonMode(PM_WIREFRAME);
+			 mode = 1;
+		}
+		else if(mode == 1)
+		{
+			m_pCamera->setPolygonMode(PM_POINTS);
+			mode = 2;
+		}
+	}
+	*/
+
+	if(m_pKeyboard->isKeyDown(OIS::KC_O))
+	{
+		if(m_pDebugOverlay)
+		{
+			if(!m_pDebugOverlay->isVisible())
+				m_pDebugOverlay->show();
+			else
+				m_pDebugOverlay->hide();
+		}
+	}
+
+
+
+	return true;
+}
+
+//|||||||||||||||||||||||||||||||||||||||||||||||
+
+bool OgreFramework::keyReleased(const OIS::KeyEvent &keyEventRef)
+{
+	return true;
+}
+
+//|||||||||||||||||||||||||||||||||||||||||||||||
+
+bool OgreFramework::mouseMoved(const OIS::MouseEvent &evt)
+{
+	m_pCamera->yaw(Degree(evt.state.X.rel * -0.1));
+	m_pCamera->pitch(Degree(evt.state.Y.rel * -0.1));
+	
+	return true;
+}
+
+//|||||||||||||||||||||||||||||||||||||||||||||||
+
+bool OgreFramework::mousePressed(const OIS::MouseEvent &evt, OIS::MouseButtonID id)
+{
+	return true;
+}
+
+//|||||||||||||||||||||||||||||||||||||||||||||||
+
+bool OgreFramework::mouseReleased(const OIS::MouseEvent &evt, OIS::MouseButtonID id)
+{
+	return true;
+}
+
+//|||||||||||||||||||||||||||||||||||||||||||||||
+
+void OgreFramework::updateOgre(double timeSinceLastFrame)
+{
+
+
+
+
+	// update the game time
+	m_GameTime+=timeSinceLastFrame ;
+
+
+	m_MoveScale = m_MoveSpeed   * timeSinceLastFrame;
+	m_RotScale  = m_RotateSpeed * timeSinceLastFrame;
+		
+	m_TranslateVector = Vector3::ZERO;
+
+//	char chMessage[1024] ;
+	//sprintf(chMessage, "winmessage %u, %u, %u", m_pRenderWnd->m_Test_uMsg, (UINT)m_pRenderWnd->m_Test_wParam, (UINT)m_pRenderWnd->m_Test_lParam) ; 
+	//m_pLog->logMessage(chMessage) ;
+
+
+
+	/*
+	if((m_pRenderWnd->m_Test_uMsg==258) && (m_pRenderWnd->m_Test_wParam==0x78))
+	//if(GetAsyncKeyState(0x78))
+	{
+
+		m_TranslateVector.z = -m_MoveScale;
+		//m_nGotInput=1 ;
+		
+		//sprintf(chMessage, "T %i: winmessage %u, %u, %u", m_nTime, m_pRenderWnd->m_Test_uMsg, (UINT)m_pRenderWnd->m_Test_wParam, (UINT)m_pRenderWnd->m_Test_lParam) ; 
+		//m_pLog->logMessage(chMessage) ;
+	}
+	else
+		m_nGotInput=0 ;
+	*/
+
+	// add a test particle to the system
+	/*
+	if( m_ps->getNumParticles() < m_ps->getParticleQuota() )
+	{
+		Ogre::Particle *p = m_ps->createParticle() ;
+		p->setDimensions( m_ps->getDefaultWidth(), m_ps->getDefaultHeight() ) ;
+		p->colour = Ogre::ColourValue::White;
+		p->direction = Ogre::Vector3::UNIT_Y;
+		p->position = Ogre::Vector3::ZERO;
+		p->timeToLive = p->totalTimeToLive = 10;
+		p->rotation = Ogre::Radian(0);   
+		p->rotationSpeed = Ogre::Radian(0);
+	}
+	*/
+
+
+	m_ps->_update(timeSinceLastFrame/1000.0f) ;
+
+	getInput();
+
+	moveCamera();
+
+
+	//Ogre::Vector3 Pos ;
+	//float CamPos[3] ;
+	//Pos=m_pCamera->getPosition() ;
+	//CamPos[0]=Pos.x ;
+	//CamPos[1]=Pos.y ;
+	//CamPos[2]=Pos.z ;
+
+
+
+	if(m_nGoggleMode==GOGGLE_MODE_OFF)
+	{
+
+
+		m_pCameraCopy->synchroniseBaseSettingsWith(m_pCamera) ; // copy the camera's original settings because we'll be modifying it for the LR views.
+		m_pCameraCopy->setFOVy( Degree(m_flFOV) ) ;
+		m_pCameraCopy->setAspectRatio( Real(m_flAspectRatio) );
+
+
+
+		if(m_nMapLoaded)
+		{
+			SetupEntityZones() ;
+			
+			CalculateZoneVisibility(m_pCameraCopy, &m_nCameraZoneL, m_chZoneVisL, m_chLightVisL, m_chSubLightVisL, m_chPortalVisL, &m_nMaxVisibleLightL, m_uVisibleLightListL) ;
+			CalculateEntityVisibility(m_pCameraCopy, m_chZoneVisL) ;
+
+			UpdateVisibleZoneList(m_chZoneVisL, m_uVisibleZoneListL, &m_nMaxVisibleZoneL) ;
+
+			//m_nVisibleLightCount=m_nMaxVisibleLightL ;
+			m_nVisibleZoneCount=m_nMaxVisibleZoneL ;
+		}
+
+
+
+		m_pGlobal_VisibleZoneList=m_uVisibleZoneListL ;
+		m_pGlobal_MaxVisibleZone=&m_nMaxVisibleZoneL ;
+
+		m_pGlobal_VisibleLightList	=	m_uVisibleLightListL ;
+		m_pGlobal_MaxVisibleLight		=	&m_nMaxVisibleLightL ;
+		m_pGlobal_LightVis					=	m_chLightVisL ;
+
+
+	}
+	else
+	{
+		// "virtual" stereo goggle mode.  Render everything twice, L and R view, and output to L quad and a R quad.
+
+		m_pCameraCopy->synchroniseBaseSettingsWith(m_pCamera) ; // copy the camera's original settings because we'll be modifying it for the LR views.
+
+		if(m_nMapLoaded)
+		{
+			SetupEntityZones() ;
+
+			CalculateZoneVisibility(m_pCameraCopy, &m_nCameraZoneL, m_chZoneVisL, m_chLightVisL, m_chSubLightVisL, m_chPortalVisL, &m_nMaxVisibleLightL, m_uVisibleLightListL) ;
+			UpdateVisibleZoneList(m_chZoneVisL, m_uVisibleZoneListL, &m_nMaxVisibleZoneL) ;
+
+			
+			//m_nVisibleLightCount=m_nMaxVisibleLightL ;
+			m_nVisibleZoneCount=m_nMaxVisibleZoneL ;
+		}
+
+		m_pGlobal_VisibleZoneList=m_uVisibleZoneListL ;
+		m_pGlobal_MaxVisibleZone=&m_nMaxVisibleZoneL ;
+	}
+
+
+	updateStats();
+
+
+	//,,
+	//sprintf(m_chFrame, "End of updateOgre frame %i",m_nFrame) ;
+	//OFBug.LogAddCR(m_chFrame) ;
+	
+
+
+	m_nFrame++ ;
+
+
+	
+}
+
+
+
+//|||||||||||||||||||||||||||||||||||||||||||||||
+
+void OgreFramework::updateStats() 
+{ 
+	
+	
+	Ogre::Vector3 Pos ;
+	Pos=m_pCamera->getPosition() ;
+	Ogre::Real nCamX=Pos.x ;
+	Ogre::Real nCamY=Pos.y ;
+	Ogre::Real nCamZ=Pos.z ;
+
+
+	static String currFps = "FPS: "; 
+    static String avgFps = "Av FPS: "; 
+    static String bestFps = "Best FPS: "; 
+    static String worstFps = "Worst FPS: "; 
+    static String tris = "Triangle Count: "; 
+    static String batches = "Batch Count: "; 
+
+ 
+    OverlayElement* guiAvg = OverlayManager::getSingleton().getOverlayElement("Core/AverageFps"); 
+    OverlayElement* guiCurr = OverlayManager::getSingleton().getOverlayElement("Core/CurrFps"); 
+    OverlayElement* guiBest = OverlayManager::getSingleton().getOverlayElement("Core/BestFps"); 
+    OverlayElement* guiWorst = OverlayManager::getSingleton().getOverlayElement("Core/WorstFps"); 
+
+	const RenderTarget::FrameStats& stats = m_pRenderWnd->getStatistics();
+    
+		//int LastFrameTime=1000000/stats.lastFPS ;
+		float LastFrameTime=1000/stats.lastFPS ;
+
+
+
+
+
+		switch(m_nDisplayInfoMode)
+		{
+
+			case 0:
+				if(m_IsOpenGL)
+					guiCurr->setCaption("OGL FPS: " + StringConverter::toString(stats.lastFPS) +" "+StringConverter::toString(LastFrameTime)+" ms"); 
+				else
+					guiCurr->setCaption("D3D FPS: " + StringConverter::toString(stats.lastFPS) +" "+StringConverter::toString(LastFrameTime)+" ms"); 
+				break ;
+
+			case 1: guiCurr->setCaption("Wireframe") ; break ;
+			case 2: guiCurr->setCaption("Lamps") ; break ;
+			case 3: guiCurr->setCaption("HDR Blur") ; break ;
+			case 4: guiCurr->setCaption("DefShade Position (Scaled)") ; break ;
+			case 5: guiCurr->setCaption("DefShade Normals") ; break ;
+			case 6: guiCurr->setCaption("DefShade Diffuse") ; break ;
+			case 7: guiCurr->setCaption("DefShade Specular") ; break ;
+			case 8: guiCurr->setCaption("DefShade Emissive") ; break ;
+
+		}
+
+		guiAvg->setCaption("Lt: " + StringConverter::toString(m_nVisibleLightCount)
+			+ " Zn: " + StringConverter::toString(m_nVisibleZoneCount)  
+			+ " Cam: "+StringConverter::toString(nCamX)+" "+StringConverter::toString(nCamY)+" "+StringConverter::toString(nCamZ));
+
+
+
+
+		//m_flDebug0=m_nTotalTriangles ;
+		//m_flDebug1=m_nMaxVisibleEntity ;
+		//m_flDebug2=m_nMaxFrustumEntity ;
+		guiWorst->setCaption("ZnEnt: " + StringConverter::toString(m_nMaxVisibleEntity) + " Tri: " + StringConverter::toString(m_nVisibleEntityTriangleCount) + " FwdTri: " + StringConverter::toString(m_nVisibleEntityTriangleCount)    ) ;
+		guiBest->setCaption("FrEnt: " + StringConverter::toString(m_nMaxFrustumEntity) + " Tri: " + StringConverter::toString(m_nFrustumEntityTriangleCount) + " DefTri: " + StringConverter::toString(m_nFrustumEntityTriangleCount)    ) ;
+
+
+
+
+		//guiWorst->setCaption(StringConverter::toString(m_flDebug0) + " " + StringConverter::toString(m_flDebug1) + " " + StringConverter::toString(m_flDebug2)) ;
+		//guiBest->setCaption(StringConverter::toString(m_flDebug3) + " " + StringConverter::toString(m_flDebug4) + " " + StringConverter::toString(m_flDebug5)) ;
+		/*
+		guiWorst->setCaption(
+			" A "+StringConverter::toString(m_flDebugMatrix[0][0])+" "+StringConverter::toString(m_flDebugMatrix[0][1])+" "+StringConverter::toString(m_flDebugMatrix[0][2])+" "+StringConverter::toString(m_flDebugMatrix[0][3]) +
+			" B "+StringConverter::toString(m_flDebugMatrix[1][0])+" "+StringConverter::toString(m_flDebugMatrix[1][1])+" "+StringConverter::toString(m_flDebugMatrix[1][2])+" "+StringConverter::toString(m_flDebugMatrix[1][3]) ) ;
+
+		guiBest->setCaption(
+			" C "+StringConverter::toString(m_flDebugMatrix[2][0])+" "+StringConverter::toString(m_flDebugMatrix[2][1])+" "+StringConverter::toString(m_flDebugMatrix[2][2])+" "+StringConverter::toString(m_flDebugMatrix[2][3]) +
+			" D "+StringConverter::toString(m_flDebugMatrix[3][0])+" "+StringConverter::toString(m_flDebugMatrix[3][1])+" "+StringConverter::toString(m_flDebugMatrix[3][2])+" "+StringConverter::toString(m_flDebugMatrix[3][3]) ) ;
+		*/
+	
+		
+		
+		
+		//guiWorst->setCaption(StringConverter::toString(flVarA)+" "+StringConverter::toString(flVarB)+" "+StringConverter::toString(flVarC)+" "+StringConverter::toString(flVarD)
+   
+	//guiBest->setCaption(bestFps + StringConverter::toString(stats.bestFPS) 
+  //          +" "+StringConverter::toString(stats.bestFrameTime)+" ms"); 
+	//guiWorst->setCaption(worstFps + StringConverter::toString(stats.worstFPS) 
+  //          +" "+StringConverter::toString(stats.worstFrameTime)+" ms"); 
+	
+	//guiWorst->setCaption(worstFps + StringConverter::toString(nCamX)+" " + StringConverter::toString(nCamY)+" " + StringConverter::toString(nCamZ)+" ") ;
+    
+	
+	OverlayElement* guiTris = OverlayManager::getSingleton().getOverlayElement("Core/NumTris"); 
+	guiTris->setCaption("MpTri: " + StringConverter::toString(m_nTotalTriangles)); 
+
+	OverlayElement* guiBatches = OverlayManager::getSingleton().getOverlayElement("Core/NumBatches"); 
+    guiBatches->setCaption(batches + StringConverter::toString(stats.batchCount)); 
+
+	OverlayElement* guiDbg = OverlayManager::getSingleton().getOverlayElement("Core/DebugText"); 
+		guiDbg->setCaption("A:"+StringConverter::toString(m_nDebugA) + " B:" + StringConverter::toString(m_nDebugB) + " C:" + StringConverter::toString(m_nDebugC) + " ") ;
+	
+	//guiDbg->setCaption(
+	//	" A "+StringConverter::toString(m_flDebugMatrix[0][0])+" "+StringConverter::toString(m_flDebugMatrix[0][1])+" "+StringConverter::toString(m_flDebugMatrix[0][2])+" "+StringConverter::toString(m_flDebugMatrix[0][3]) +
+	//	" B "+StringConverter::toString(m_flDebugMatrix[1][0])+" "+StringConverter::toString(m_flDebugMatrix[1][1])+" "+StringConverter::toString(m_flDebugMatrix[1][2])+" "+StringConverter::toString(m_flDebugMatrix[1][3]) +
+	//	" C "+StringConverter::toString(m_flDebugMatrix[2][0])+" "+StringConverter::toString(m_flDebugMatrix[2][1])+" "+StringConverter::toString(m_flDebugMatrix[2][2])+" "+StringConverter::toString(m_flDebugMatrix[2][3]) +
+	//	" D "+StringConverter::toString(m_flDebugMatrix[3][0])+" "+StringConverter::toString(m_flDebugMatrix[3][1])+" "+StringConverter::toString(m_flDebugMatrix[3][2])+" "+StringConverter::toString(m_flDebugMatrix[3][3]) ) ;
+
+
+
+
+	//guiDbg->setCaption("");
+} 
+
+//|||||||||||||||||||||||||||||||||||||||||||||||
+
+void OgreFramework::moveCamera()
+{
+	if( m_nKeyDown_Ctrl && m_nKeyDown_Shift ) // super slow
+			m_pCamera->moveRelative(m_TranslateVector/50);
+		else
+			if(m_nKeyDown_Shift)	// fast
+				m_pCamera->moveRelative(m_TranslateVector*10);
+			else
+				if(m_nKeyDown_Ctrl) // slow 
+					m_pCamera->moveRelative(m_TranslateVector/5);
+				else
+					m_pCamera->moveRelative(m_TranslateVector*2); // normal
+}
+
+//|||||||||||||||||||||||||||||||||||||||||||||||
+
+void OgreFramework::getInput()
+{
+	if(m_pKeyboard->isKeyDown(OIS::KC_LCONTROL))
+		m_nKeyDown_Ctrl=1 ;
+	else
+		m_nKeyDown_Ctrl=0 ;
+
+	if(m_pKeyboard->isKeyDown(OIS::KC_LSHIFT))
+		m_nKeyDown_Shift=1 ;
+	else
+		m_nKeyDown_Shift=0 ;
+
+
+
+
+
+
+	if(m_pKeyboard->isKeyDown(OIS::KC_A))
+	{
+		m_TranslateVector.x = -m_MoveScale;
+	}
+
+	if(m_pKeyboard->isKeyDown(OIS::KC_D))
+	{
+		m_TranslateVector.x = m_MoveScale;
+	}
+
+	if(m_pKeyboard->isKeyDown(OIS::KC_W))
+	{
+		m_TranslateVector.z = -m_MoveScale;
+		m_nGotInput=1 ;
+	}
+
+	if(m_pKeyboard->isKeyDown(OIS::KC_S))
+	{
+		m_TranslateVector.z = m_MoveScale;
+	}
+
+
+	// generic toggle for debugging
+	if(m_pKeyboard->isKeyDown(OIS::KC_T))
+	{
+		if(m_nKeyToggle[OIS::KC_T]==0)
+		{
+			m_nKeyToggle[OIS::KC_T]=1 ; // toggle to stop key repeating
+			m_nToggle=1-m_nToggle ;
+			//OFBug.MessageInt(m_nToggle) ;
+		}
+	}
+	else
+		m_nKeyToggle[OIS::KC_T]=0 ;
+
+	//m_flDebug0=m_nKeyToggle[OIS::KC_T] ;
+	//m_flDebug1=m_nToggle ;
+
+	/*
+	if(m_pKeyboard->isKeyDown(OIS::KC_LEFT))
+	{
+		m_pCamera->yaw(m_RotScale);
+	}
+
+	if(m_pKeyboard->isKeyDown(OIS::KC_RIGHT))
+	{
+		m_pCamera->yaw(-m_RotScale);
+	}
+
+	if(m_pKeyboard->isKeyDown(OIS::KC_UP))
+	{
+		m_pCamera->pitch(m_RotScale);
+	}
+
+	if(m_pKeyboard->isKeyDown(OIS::KC_DOWN))
+	{
+		m_pCamera->pitch(-m_RotScale);
+	}
+	*/
+
+	/////////////////////////////////////////////////////////////////////////////////////////////////////////
+	//
+	// Stereo controls
+
+	
+
+	// toggle stereo test image mode
+	if(m_pKeyboard->isKeyDown(OIS::KC_DELETE))
+	{
+		if(m_nKeyToggle[OIS::KC_DELETE]==0)
+		{
+			m_nKeyToggle[OIS::KC_DELETE]=1 ; // toggle to stop key repeating
+			
+			m_nGoggleMode++ ; // cyle through the goggle modes
+			if(m_nGoggleMode>=GOGGLE_MODE_MAX)
+				m_nGoggleMode=GOGGLE_MODE_OFF ;
+
+			if(m_nGoggleMode==GOGGLE_MODE_TEST)
+				CreateGogglesTestImage() ; // create the test goggles
+			else
+				if(m_nGoggleMode==GOGGLE_MODE_ON)
+					CreateGoggles() ; // create the stereo goggles
+		}
+	}
+	else
+		m_nKeyToggle[OIS::KC_DELETE]=0 ;
+
+	if(m_nGoggleMode==GOGGLE_MODE_TEST) // controls for setting up the test image
+	{
+		int nDoPause=0 ; // if any control gets used we do a quick pause to make the rate of change a bit more consistent, not as frame-rate dependent
+
+		if(m_pKeyboard->isKeyDown(OIS::KC_PGUP))
+		{
+			nDoPause=1 ;
+
+			if(m_nKeyDown_Shift)
+				m_flGoggleZPos-=1.0 ;
+			else
+				if(m_nKeyDown_Ctrl)
+					m_flGoggleZPos-=0.01 ;
+				else
+					m_flGoggleZPos-=0.1 ;
+
+			CreateGogglesTestImage() ;
+		}
+
+		if(m_pKeyboard->isKeyDown(OIS::KC_PGDOWN))
+		{
+			nDoPause=1 ;
+
+			if(m_nKeyDown_Shift)
+				m_flGoggleZPos+=1.0 ;
+			else
+				if(m_nKeyDown_Ctrl)
+					m_flGoggleZPos+=0.01 ;
+				else
+					m_flGoggleZPos+=0.1 ;
+
+			CreateGogglesTestImage() ;
+		}
+
+		if(m_pKeyboard->isKeyDown(OIS::KC_HOME))
+		{
+			nDoPause=1 ;
+
+			if(m_nKeyDown_Shift)
+				m_flGoggleXGap-=0.1 ;
+			else
+				if(m_nKeyDown_Ctrl)
+					m_flGoggleXGap-=0.001 ;
+				else
+					m_flGoggleXGap-=0.01 ;
+
+			if(m_flGoggleXGap<0)
+				m_flGoggleXGap=0 ;
+
+			CreateGogglesTestImage() ;
+		}
+
+		if(m_pKeyboard->isKeyDown(OIS::KC_END))
+		{
+			nDoPause=1 ;
+
+			if(m_nKeyDown_Shift)
+				m_flGoggleXGap+=0.1 ;
+			else
+				if(m_nKeyDown_Ctrl)
+					m_flGoggleXGap+=0.001 ;
+				else
+					m_flGoggleXGap+=0.01 ;
+
+			if(m_flGoggleXGap<0)
+				m_flGoggleXGap=0 ;
+
+			CreateGogglesTestImage() ;
+		}
+
+		if(m_pKeyboard->isKeyDown(OIS::KC_UP))
+		{
+			nDoPause=1 ;
+
+			if(m_nKeyDown_Shift && m_nKeyDown_Ctrl)
+				m_flGoggleYScale=1.0 ;
+			else
+				if(m_nKeyDown_Shift)
+					m_flGoggleYScale+=0.002 ;
+				else
+					if(m_nKeyDown_Ctrl)
+						m_flGoggleYScale+=0.00002 ;
+					else
+						m_flGoggleYScale+=0.0002 ;
+
+				if(m_flGoggleYScale<0)
+					m_flGoggleYScale=0 ;
+
+				CreateGogglesTestImage() ;
+		}
+
+		if(m_pKeyboard->isKeyDown(OIS::KC_DOWN))
+		{
+			nDoPause=1 ;
+
+			if(m_nKeyDown_Shift && m_nKeyDown_Ctrl)
+				m_flGoggleYScale=1.0 ;
+			else
+				if(m_nKeyDown_Shift)
+					m_flGoggleYScale-=0.002 ;
+				else
+					if(m_nKeyDown_Ctrl)
+						m_flGoggleYScale-=0.00002 ;
+					else
+						m_flGoggleYScale-=0.0002 ;
+
+			if(m_flGoggleYScale<0)
+				m_flGoggleYScale=0 ;
+
+			CreateGogglesTestImage() ;
+		}
+
+		if(m_pKeyboard->isKeyDown(OIS::KC_LEFT))
+		{
+			nDoPause=1 ;
+
+			if(m_nKeyDown_Shift && m_nKeyDown_Ctrl)
+				m_flGoggleXScale=1.0 ;
+			else
+				if(m_nKeyDown_Shift)
+					m_flGoggleXScale+=0.002 ;
+				else
+					if(m_nKeyDown_Ctrl)
+						m_flGoggleXScale+=0.00002 ;
+					else
+						m_flGoggleXScale+=0.0002 ;
+
+			if(m_flGoggleXScale<0)
+				m_flGoggleXScale=0 ;
+
+			CreateGogglesTestImage() ;
+		}
+
+		if(m_pKeyboard->isKeyDown(OIS::KC_RIGHT))
+		{
+			nDoPause=1 ;
+
+			if(m_nKeyDown_Shift && m_nKeyDown_Ctrl)
+				m_flGoggleXScale=1.0 ;
+			else
+				if(m_nKeyDown_Shift)
+					m_flGoggleXScale-=0.002 ;
+				else
+					if(m_nKeyDown_Ctrl)
+						m_flGoggleXScale-=0.00002 ;
+					else
+						m_flGoggleXScale-=0.0002 ;
+
+			if(m_flGoggleXScale<0)
+				m_flGoggleXScale=0 ;
+
+			CreateGogglesTestImage() ;
+		}
+
+		
+
+		if(nDoPause) Sleep(20) ; // pause makes rate more consisent, less frame rate dependent.
+
+	}// end test image controls
+
+	
+
+	//
+	//
+	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+	// flashlight
+	if(m_pKeyboard->isKeyDown(OIS::KC_F))
+	{
+		if(m_nKeyToggle[OIS::KC_F]==0)
+		{
+			m_nKeyToggle[OIS::KC_F]=1 ; // toggle to stop key repeating
+			m_nFlashLight=1-m_nFlashLight ;
+			m_nDebugA=m_nFlashLight ;
+			//OFBug.MessageInt(m_nToggle) ;
+		}
+	}
+	else
+		m_nKeyToggle[OIS::KC_F]=0 ;
+
+
+	if(m_pKeyboard->isKeyDown(OIS::KC_J))
+	{
+		if(m_nJumpToggle==0)
+		{
+			m_nJumpToggle=1 ;
+
+
+			m_nJumpVal++ ;
+			if(m_nJumpVal>=m_Q3Map->m_nLightMax)
+				m_nJumpVal=0 ;
+
+			//m_flDebug0=m_nJumpVal ;
+			
+			
+			//m_nJumpVal=12 ; // for now always jump to light 0
+			//m_pCamera->setPosition(m_Q3Map->m_pLight[m_nJumpVal].Position[0], m_Q3Map->m_pLight[m_nJumpVal].Position[1], m_Q3Map->m_pLight[m_nJumpVal].Position[2]) ;
+			//m_nJumpVal=13 ;
+			//m_pCamera->lookAt(Vector3(m_Q3Map->m_pLight[m_nJumpVal].Position[0], m_Q3Map->m_pLight[m_nJumpVal].Position[1], m_Q3Map->m_pLight[m_nJumpVal].Position[2]));
+			
+			//m_pCamera->setPosition(166, 222, -111) ;
+			//m_pCamera->setDirection(0.707, 0, -0.707) ;
+
+			//m_pCamera->setPosition(m_Q3Map->m_pLight[0].Position[0], m_Q3Map->m_pLight[0].Position[1], m_Q3Map->m_pLight[0].Position[2]) ;
+			//m_pCamera->setDirection(m_Q3Map->m_pLight[0].Direction[0], m_Q3Map->m_pLight[0].Direction[1], m_Q3Map->m_pLight[0].Direction[2]) ;
+			//m_pCamera->setFOVy( Degree(m_Q3Map->m_pLight[0].Angle ) ) ;
+
+			//m_pCamera->setDirection(m_pLight[m_nJumpVal].Direction[0], m_pLight[m_nJumpVal].Direction[1], m_pLight[m_nJumpVal].Direction[2]) ;
+			// 880 120 -340
+			//m_pCamera->lookAt(Vector3(880,120,-340));
+			//m_pCamera->lookAt(Vector3(915,155,-340));
+
+
+			m_pCamera->setPosition(600, 200, -250) ;
+			//m_pCamera->setDirection(1.0, 0.0, 0.0) ;
+			m_pCamera->lookAt(700, 200, -450);
+
+			
+
+		}
+	}
+	else
+		m_nJumpToggle=0 ;
+	
+
+	if(m_pKeyboard->isKeyDown(OIS::KC_L))
+	{
+		if(m_nLoadToggle==0)
+		{
+			UnloadMap(false) ;
+			nMap++ ;
+			LoadMap() ;
+			//m_nLoadToggle=1 ;
+		}
+	}
+	else
+		m_nLoadToggle=0 ;
+
+
+		// generic toggle for debugging
+	if(m_pKeyboard->isKeyDown(OIS::KC_M))
+	{
+		if(m_nKeyToggle[OIS::KC_M]==0)
+		{
+			m_nKeyToggle[OIS::KC_M]=1 ; // toggle to stop key repeating
+
+			m_nDisplayInfoMode++ ;
+			if(m_nDisplayInfoMode==9)
+				m_nDisplayInfoMode=0 ;
+
+
+
+		}
+	}
+	else
+		m_nKeyToggle[OIS::KC_M]=0 ;
+
+
+
+
+
+	//if(m_pKeyboard->isKeyDown(OIS::KC_K)) m_nVideoRestart=1 ;
+
+
+	if(m_pKeyboard->isKeyDown(OIS::KC_P))
+	{
+		if(m_nPortalToggle==0)
+		{
+			m_nPortalState=1-m_nPortalState ;
+			m_nPortalToggle=1 ;
+		}
+	}
+	else
+		m_nPortalToggle=0 ;
+
+	if(m_pKeyboard->isKeyDown(OIS::KC_ESCAPE))
+	{
+			m_bShutDownOgre = true;
+	}
+
+}
+
+//|||||||||||||||||||||||||||||||||||||||||||||||
+//shutdown
+// the dynamic memory deletes are all safe, so it's ok to try and delete them even if they don't exist.
+void OgreFramework::UnloadMap(bool bShutdown)
+{	
+
+	//if(!bShutdown) DestroyRTTAssets() ;
+
+	//if(ResourceGroupManager::getSingleton().isResourceGroupLoaded("Raw Bsp"))
+	//	ResourceGroupManager::getSingleton().clearResourceGroup("Raw Bsp") ; 
+
+
+	// only do if there was a definitely a map loaded.
+	if(m_nMapLoaded)
+	{
+		char chSceneNodeName[1024] ;
+		// make sure all the zone nodes are re-attached to the root node or they won't get deleted
+		for(int nZone=0 ; nZone<m_Q3Map->m_nMaxZone ; nZone++)
+		{
+			// Opaque nodes
+			if(m_nOpaqueNodeUsed[nZone]) // only do this if the node actually exists
+			{
+				if(!m_pOpaqueNode[nZone]->isInSceneGraph())
+					m_pSceneMgr->getRootSceneNode()->addChild(m_pOpaqueNode[nZone]) ;
+
+				sprintf(chSceneNodeName, "Opaque%05i", nZone) ;
+				m_pSceneMgr->getRootSceneNode()->removeAndDestroyChild(chSceneNodeName) ;
+
+			}
+
+			// Trans nodes
+			if(m_nTransNodeUsed[nZone]) // only do this if the node actually exists
+			{
+				if(!m_pTransNode[nZone]->isInSceneGraph())
+					m_pSceneMgr->getRootSceneNode()->addChild(m_pTransNode[nZone]) ;
+
+				sprintf(chSceneNodeName, "Trans%05i", nZone) ;
+				m_pSceneMgr->getRootSceneNode()->removeAndDestroyChild(chSceneNodeName) ;
+
+			}
+
+			// Lamp nodes
+			if(m_nLampNodeUsed[nZone]) // only do this if the node actually exists
+			{
+				if(!m_pLampNode[nZone]->isInSceneGraph())
+					m_pSceneMgr->getRootSceneNode()->addChild(m_pLampNode[nZone]) ;
+
+				sprintf(chSceneNodeName, "Lamp%05i", nZone) ;
+				m_pSceneMgr->getRootSceneNode()->removeAndDestroyChild(chSceneNodeName) ;
+
+			}
+
+			// Glow nodes
+			if(m_nGlowNodeUsed[nZone]) // only do this if the node actually exists
+			{
+				if(!m_pGlowNode[nZone]->isInSceneGraph())
+					m_pSceneMgr->getRootSceneNode()->addChild(m_pGlowNode[nZone]) ;
+
+				sprintf(chSceneNodeName, "Glow%05i", nZone) ;
+				m_pSceneMgr->getRootSceneNode()->removeAndDestroyChild(chSceneNodeName) ;
+
+			}
+
+		}
+
+
+		// reattach all entity scenenodes.
+		int nLoop=0 ;
+//		int nSubMesh=0 ;
+//		int nMaxSubMesh=0 ;
+		for(nLoop=0 ; nLoop<m_nMaxEntity ; nLoop++)
+			m_pSceneMgr->getRootSceneNode()->addChild(m_pEntityInfo[nLoop].pMasterNode) ;
+
+	}// end if map loaded
+
+
+	// free ogre stuff
+	//Ogre::ResourceGroupManager::getSingleton().clearResourceGroup(ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME) ;
+
+	//Ogre::ResourcePtr RTT=Ogre::TextureManager::getSingleton().getByName("RttTex") ;
+	//Ogre::TextureManager::getSingleton().remove(RTT) ;
+	
+	//RTT_Texture->unload() ;
+
+	
+
+
+	
+	
+
+	//m_pSceneMgr->clearScene() ;
+	m_pSceneMgr->getRootSceneNode()->removeAndDestroyAllChildren() ; // destroy all scenenodes
+	
+	m_pSceneMgr->destroyAllEntities() ;
+	m_pSceneMgr->destroyAllLights() ;
+	m_pSceneMgr->destroyAllManualObjects() ;
+	
+
+	/*
+	// unload all the meshes
+	int nMaxMesh=m_nZoneMOStart[m_Q3Map->m_nMaxZone] ;
+	int nMesh=0 ;
+	for(nMesh=0 ; nMesh<nMaxMesh ; nMesh++)
+	{
+		m_pSceneMgr->destroyEntity(m_pZoneEntity[nMesh]) ;
+		//m_pZoneMesh[nMesh]->cleanupDictionary() ;
+		//m_pZoneMesh[nMesh]->unload() ;
+
+
+	}
+	*/
+	
+	MeshManager::getSingleton().removeAll() ; // this destroys all the meshes
+
+	
+
+	//OFBug.MessageInt(333) ;
+	//Ogre::TextureManager::getSingleton().unloadAll() ;
+	//Ogre::TextureManager::getSingleton().removeAll() ;
+	
+	//Ogre::MaterialManager::getSingleton().unloadAll() ;
+	//Ogre::MaterialManager::getSingleton().removeAll() ;
+
+	
+
+
+
+	//SetupResourceLocations() ;
+
+	// delete our array of Zone group manual objects 
+	//CHECKDELETE_ARRAY( m_pZoneMO ) ;
+	CHECKDELETE_ARRAY( m_pZoneMesh, NEW_CHECK_m_pZoneMesh ) ; 
+	CHECKDELETE_ARRAY( m_pZoneEntity, NEW_CHECK_m_pZoneEntity  ) ; 
+	CHECKDELETE_ARRAY( m_pZoneEntityMaterialType, NEW_CHECK_m_pZoneEntityMaterialType  ) ; 
+
+
+	CHECKDELETE_ARRAY( m_pZoneEntityMaterial_Shadow, NEW_CHECK_m_pZoneEntityMaterial_Shadow  ) ; 
+	CHECKDELETE_ARRAY( m_pZoneEntityMaterial_ShadeFront, NEW_CHECK_m_pZoneEntityMaterial_ShadeFront  ) ; 
+	CHECKDELETE_ARRAY( m_pZoneEntityMaterial_ShadeBack, NEW_CHECK_m_pZoneEntityMaterial_ShadeBack  ) ; 
+
+	int nPTex=0 ;
+	for(nPTex=0 ; nPTex<MAX_PROJECTORTEX ; nPTex++)
+	{
+		CHECKDELETE_ARRAY( m_pZoneEntityMaterial_Base[nPTex], NEW_CHECK_m_pZoneEntityMaterial_Base  ) ; 
+		CHECKDELETE_ARRAY( m_pZoneEntityMaterial_Fast[nPTex], NEW_CHECK_m_pZoneEntityMaterial_Fast  ) ; 
+	}
+
+	CHECKDELETE_ARRAY( m_pZoneEntityMaterial_Black, NEW_CHECK_m_pZoneEntityMaterial_Black  ) ; 
+	CHECKDELETE_ARRAY( m_pZoneEntityMaterial_DfShColour, NEW_CHECK_m_pZoneEntityMaterial_DfShColour  ) ; 
+	CHECKDELETE_ARRAY( m_pZoneEntityMaterial_DfShPosition, NEW_CHECK_m_pZoneEntityMaterial_DfShPosition  ) ; 
+	CHECKDELETE_ARRAY( m_pZoneEntityMaterial_DfShDiffuse, NEW_CHECK_m_pZoneEntityMaterial_DfShDiffuse  ) ; 
+	//CHECKDELETE_ARRAY( m_pZoneEntityMaterial_DSNormal ) ;
+	CHECKDELETE_ARRAY( m_pZoneEntityMaterial_DfShFuncTNB, NEW_CHECK_m_pZoneEntityMaterial_DfShFuncTNB  ) ; 
+	CHECKDELETE_ARRAY( m_pZoneEntityMaterial_DfShSpecular, NEW_CHECK_m_pZoneEntityMaterial_DfShSpecular  ) ; 
+	CHECKDELETE_ARRAY( m_pZoneEntityMaterial_DfShEmissive, NEW_CHECK_m_pZoneEntityMaterial_DfShEmissive  ) ; 
+	//CHECKDELETE_ARRAY( m_pZoneEntityMaterial_DfShData ) ;
+	CHECKDELETE_ARRAY( m_pZoneEntityMaterial_DfShMix, NEW_CHECK_m_pZoneEntityMaterial_DfShMix  ) ; 
+
+	////////////////////////////////////////////////////////
+	CHECKDELETE_ARRAY( m_pEntityInfo, NEW_CHECK_m_pEntityInfo  ) ; 
+	CHECKDELETE_ARRAY( m_pVisibleEntity, NEW_CHECK_m_pVisibleEntity  ) ;
+	CHECKDELETE_ARRAY( m_pFrustumEntity, NEW_CHECK_m_pFrustumEntity  ) ;
+
+
+	// was leaking before, but this seems to have fixed things.
+	Ogre::ResourceGroupManager::getSingleton().clearResourceGroup(ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME) ;
+
+	// delete our debug light manualobjects/zones
+//	CHECKDELETE_ARRAY( m_pLightMO ) ;
+
+//	CHECKDELETE_ARRAY( m_pPortalMO ) ;
+//	CHECKDELETE_ARRAY( m_pPortalNode ) ;
+
+	// free our dynamic memory
+	if(m_Q3Map)
+	{
+		m_Q3Map->~Q3Map() ;		// get rid of all the m_Q3Map stuff
+		CHECKDELETE_POINTER( m_Q3Map, NEW_CHECK_m_Q3Map) ;  
+		m_Q3Map=NULL ;
+	}
+
+	if(m_bRawBspFileIsLoaded) 
+	{
+		//m_pRawBspFile->unload() ;
+		CHECKDELETE_ARRAY(m_pRawBspFile, NEW_CHECK_m_pRawBspFile ) ;  
+		m_bRawBspFileIsLoaded=false ;
+	}
+
+	// indicate that there is no map.
+	m_nMapLoaded=0 ;
+
+	
+
+	
+	int nCheck=0 ;
+	for(nCheck=0 ; nCheck<MAX_NEW_CHECK ; nCheck++)
+	if(m_nNewCheck[nCheck]!=0)
+	{
+		sprintf(m_chBug, "MEMORY LEAK: NewCheck %i, NewCount %i", nCheck, m_nNewCheck[nCheck]) ;
+		m_pLog->logMessage(m_chBug) ;
+	}
+
+
+		//OFBug.MessageInt(333) ;
+		//Sleep(50) ;
+}
+
+
+
+
+
+
+
+
+ /***********************************************************************************************************\
+                                               
+                                                 LOAD MAP 
+                                                
+ \***********************************************************************************************************/
+
+
+
+
+int OgreFramework::LoadMap(void)
+{	
+	m_pLog->logMessage("LoadMap.") ;
+
+
+
+	ZeroMemory((void*)m_nKeyToggle, sizeof(m_nKeyToggle)) ; // good place to clear the keytoggle just in case
+
+	m_pLog->logMessage("Pre InitAllResourceGroups.") ;
+	Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();	
+	m_pLog->logMessage("Post InitAllResourceGroups.") ;
+
+	// setup the clear material
+	pClearMaterial=MaterialManager::getSingleton().getByName("Clear") ;
+
+	
+	//CreateRTTAssets() ;
+
+
+
+	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+		m_pDebugOverlay = OverlayManager::getSingleton().getByName("Core/DebugOverlay");
+		m_pDebugOverlay->show();
+
+		//m_MapName="test04e.bsp" ;
+		//m_MapName="test04g.bsp" ;
+		//m_MapName="test03f.bsp" ;
+
+		//m_MapName="testnew00a.bsp" ;
+		//m_MapName="test04gNew.bsp" ;
+
+		//m_MapName="test10a.bsp" ;
+		m_MapName="testmap06.bsp" ;
+		
+
+		//////////////////////////////////////////////////////////////////////////////////////////////////
+		// Load the file, get the data
+	
+
+
+		// open the stream to the bsp
+		Ogre::DataStreamPtr stream = Ogre::ResourceGroupManager::getSingleton().openResource(m_MapName, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, true);
+
+		// create memory to hold the bsp
+		size_t Size = stream->size() ;
+		m_pRawBspFile= new char[Size] ;
+		if( m_pRawBspFile==NULL) 
+			{ m_pLog->logMessage("Error loading map: Initial load out of memory.")			; UnloadMap(false) ; return 0 ; }
+		else
+		{m_nNewCount++ ; m_nNewCheck[NEW_CHECK_m_pRawBspFile]++ ; }
+
+		// stream bsp into memory
+		stream->read((void*)m_pRawBspFile, Size) ;
+
+		// close the stream, don't need it anymore
+		stream->close() ;
+		
+		// indicate that the raw bsp is loaded
+		m_bRawBspFileIsLoaded=true ;
+
+
+
+	
+
+		
+		//////////////////////////////////////////////////////////////////////////////////////////////////
+		// Setup Q3Map
+
+		m_Q3Map=new Q3Map();
+		if(m_Q3Map==NULL) 
+			{ m_pLog->logMessage("Error loading map: New Map.")			; UnloadMap(false) ; return 0 ; }
+		else
+			{m_nNewCount++ ; m_nNewCheck[NEW_CHECK_m_Q3Map]++ ; }
+
+
+
+		m_pLog->logMessage("pre ParseMap") ;
+
+		// process the bsp, log errors and fail if needed.
+		switch(m_Q3Map->ParseAndTriangulateMap( m_pRawBspFile, Size ) )
+		{
+			case ERROR_ParseMap:						m_pLog->logMessage("Error loading map: Parse Map.") ;							UnloadMap(false) ; return 0 ;
+			case ERROR_ParseEntities:				m_pLog->logMessage("Error loading map: Parse Entities.") ;				UnloadMap(false) ; return 0 ;
+			case ERROR_AllocateVertex:			m_pLog->logMessage("Error loading map: Allocate Vertex.") ;				UnloadMap(false) ; return 0 ;
+			case ERROR_AllocateTriangle:		m_pLog->logMessage("Error loading map: Allocate Triangle.") ;			UnloadMap(false) ; return 0 ;
+			case ERROR_InitializeFaces:			m_pLog->logMessage("Error loading map: Initialize Faces") ;				UnloadMap(false) ; return 0 ;
+			case ERROR_ConvertFaces:				m_pLog->logMessage("Error loading map: Convert Faces.") ;					UnloadMap(false) ; return 0 ;
+			case ERROR_ConvertPatches:			m_pLog->logMessage("Error loading map: Convert Patches.") ;				UnloadMap(false) ; return 0 ;
+			case ERROR_ConvertTexLamp:			m_pLog->logMessage("Error loading map: Convert TexLamps.") ;			UnloadMap(false) ; return 0 ;
+			case ERROR_ConvertLamps:				m_pLog->logMessage("Error loading map: Convert Lamps.") ;					UnloadMap(false) ; return 0 ;
+			case ERROR_ConvertLampGlow:			m_pLog->logMessage("Error loading map: Convert Lamp Glow.") ;			UnloadMap(false) ; return 0 ;
+			case ERROR_ConvertLightGlow:		m_pLog->logMessage("Error loading map: Convert Light Glow.") ;		UnloadMap(false) ; return 0 ;
+			case ERROR_AssignTriangles:			m_pLog->logMessage("Error loading map: Assign Triangles") ;				UnloadMap(false) ; return 0 ;
+			case ERROR_SortTriangles:				m_pLog->logMessage("Error loading map: Sort Triangles") ;					UnloadMap(false) ; return 0 ;
+			case ERROR_SetupTransTextures:	m_pLog->logMessage("Error loading map: Setup Trans Textures") ;		UnloadMap(false) ; return 0 ;
+			case ERROR_SortGroups:					m_pLog->logMessage("Error loading map: Sort Groups") ;						UnloadMap(false) ; return 0 ;
+		}
+
+		m_pLog->logMessage("post ParseMap") ;
+
+		//////////////////////////////////////////////////////////////////////////////////////////////////
+		// use the triangles to build our zone/texture manualObjects, which make up the map
+		if(!ConstructMapFromTriangles()){ m_pLog->logMessage("Error loading map: Construct Map") ;		UnloadMap(false) ; return 0 ; }
+
+		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+		// delete all the stuff that was only needed for parsing the bsp and converting it into manaulObjects.
+		m_Q3Map->FreeParseMem() ;
+	
+
+
+		
+
+		
+		
+		//////////////////////////////////////////////////////////////////////////////////////////////////
+		// Create the scenenodes for the zones and attach the manualObjects
+
+		int nZone=0 ;
+		char chSceneNodeName[1024] ;
+		int nMO=0 ;
+		int nZGStart=0 ;
+		int nZGEnd=0 ;
+
+		char chMessage[1024] ;
+		sprintf(chMessage, "Max Zones %i", m_Q3Map->m_nMaxZone) ; m_pLog->logMessage(chMessage) ;
+		for(nZone=0 ; nZone<m_Q3Map->m_nMaxZone ; nZone++)
+		{
+			//sprintf(chMessage, "Zone %i Top", nZone) ; m_pLog->logMessage(chMessage) ;
+
+
+			// create Opaque node if needed
+			if(m_nOpaqueNodeUsed[nZone])
+			{
+				sprintf(chSceneNodeName, "Opaque%05i", nZone) ;
+				m_pOpaqueNode[nZone]=m_pSceneMgr->getRootSceneNode()->createChildSceneNode(chSceneNodeName) ;
+			}
+
+			// create Trans node if needed
+			if(m_nTransNodeUsed[nZone])
+			{
+				sprintf(chSceneNodeName, "Trans%05i", nZone) ;
+				m_pTransNode[nZone]=m_pSceneMgr->getRootSceneNode()->createChildSceneNode(chSceneNodeName) ;
+			}
+
+			// create Lamp node if needed
+			if(m_nLampNodeUsed[nZone])
+			{
+				sprintf(chSceneNodeName, "Lamp%05i", nZone) ;
+				m_pLampNode[nZone]=m_pSceneMgr->getRootSceneNode()->createChildSceneNode(chSceneNodeName) ;
+			}
+
+			// create Glow node if needed
+			if(m_nGlowNodeUsed[nZone])
+			{
+				sprintf(chSceneNodeName, "Glow%05i", nZone) ;
+				m_pGlowNode[nZone]=m_pSceneMgr->getRootSceneNode()->createChildSceneNode(chSceneNodeName) ;
+			}
+
+			// get start and end of this zone's batches.  
+			// If there's none, the start and end will be the same. (don't have to worry about checking for m_pZoneMO[nZone]==NULL)
+			nZGStart	=	m_nZoneMOStart[nZone] ;
+			nZGEnd		=	m_nZoneMOStart[nZone+1] ;
+			
+			//sprintf(chMessage, "  Zone %i, ZGStart %i, ZGEnd %i", nZone, nZGStart, nZGEnd) ; m_pLog->logMessage(chMessage) ;
+
+			for(nMO=nZGStart ; nMO<nZGEnd ; nMO++)
+			{
+				//sprintf(chMessage, "    Zone %i, nMO %i", nZone, nMO) ; m_pLog->logMessage(chMessage) ;
+
+				// attach the entity to the appropriate node
+
+				if(m_pZoneEntityMaterialType[nMO]==MAT_OPAQUE)
+					m_pOpaqueNode[nZone]->attachObject( m_pZoneEntity[nMO] ) ;
+				else
+				if(m_pZoneEntityMaterialType[nMO]==MAT_GEL)
+					m_pTransNode[nZone]->attachObject( m_pZoneEntity[nMO] ) ;
+				else
+				if(m_pZoneEntityMaterialType[nMO]==MAT_LAMP)
+					m_pLampNode[nZone]->attachObject( m_pZoneEntity[nMO] ) ;
+				else
+				if(m_pZoneEntityMaterialType[nMO]==MAT_GLOW)
+					m_pGlowNode[nZone]->attachObject( m_pZoneEntity[nMO] ) ;
+			}
+
+		}
+
+
+		// add the game entities
+		if(!SetupGameEntities()) { m_pLog->logMessage("Error loading map: Setup Entities") ;		UnloadMap(false) ; return 0 ; }
+
+
+		// prior to the first frame, all zone scenenodes are detached.
+		m_pSceneMgr->getRootSceneNode()->removeAllChildren() ;
+
+		//////////////////////////////////////////////////////////////////////////////////////////////////////
+		// Setup the lights
+
+
+
+		
+		initLight() ; 
+
+		//if(m_nDebugLightBox)
+		//	AddLightCullingBoxes() ;
+
+
+		//if(m_nPortalDebug)
+		//	AddPortalBoxes() ;
+
+
+		///////////////////////////////////////////////////////////////////////////////////////////////////
+		// misc finalization of the map
+
+		m_nCameraZone=-1 ; // indicate that we need to look up the zone the player is in.
+		m_nCameraZoneL=-1 ;
+		m_nCameraZoneR=-1 ;
+
+		m_nMapLoaded=1 ; // map was loaded ok
+
+
+
+		// set the camera's start position here.
+		// I tried setting it during startup, but for some strange reason it didn't work IF the camera was in a zone 
+		// but was ok if the camera started outside a zone.  Bit of a mystery... but it makes more sense to set it here anyway.
+		m_pCamera->setPosition(Vector3(0, 0, 0));
+
+		if(m_Q3Map->m_nLightMax>0) // make sure there's at least one light before we point to it!
+			m_pCamera->lookAt(Ogre::Vector3(m_Q3Map->m_pLight[0].Position[0], m_Q3Map->m_pLight[0].Position[1], m_Q3Map->m_pLight[0].Position[2])) ;
+
+
+		
+
+		if(m_nGoggleMode==GOGGLE_MODE_TEST)
+				CreateGogglesTestImage() ; // create the test goggles
+			else
+				if(m_nGoggleMode==GOGGLE_MODE_ON)
+					CreateGoggles() ; // create the stereo goggles
+
+
+		SetupParticles() ;
+
+
+		m_pLog->logMessage("LoadMap bottom.") ;
+
+		// reset the game timer
+		m_GameTime=0.0 ;
+		m_nFrame=0 ;
+
+
+
+		//OFBug.MessageInt(m_nNewCount) ;
+
+		return 1 ;
+}
+
+//''
+int OgreFramework::SetupParticles()
+{
+			ParticleSystem::setDefaultNonVisibleUpdateTimeout(5);  // set nonvisible timeout
+
+		
+
+    // create some nice fireworks and place it at the origin
+    //m_ps = m_pSceneMgr->createParticleSystem("Fireworks", "particle/fallspark00");
+		m_ps = m_pSceneMgr->createParticleSystem("Fireworks", "particle/spark00");
+		m_pParticleNode = m_pSceneMgr->getRootSceneNode()->createChildSceneNode("ParticleNode") ;
+		m_pParticleNode->attachObject(m_ps) ;
+
+		//m_pParticleNode->setPosition(360.0f, 360.0f, -860.0f) ;
+		m_pParticleNode->setPosition(586.0f, 228.0f, -520.0f) ;
+
+
+	return 1 ;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+// take the sorted list of triangles and contruct our Zone manualObjects array, m_pZoneMO, etc
+// and setup all the materials.
+int OgreFramework::ConstructMapFromTriangles(void)
+{
+
+
+	int nVert=0 ;
+	int nMaxVertex=0 ;
+	
+	float flPosX=0.0f ;
+	float flPosY=0.0f ;
+	float flPosZ=0.0f ;
+	float	flNormX=0.0f ;
+	float	flNormY=0.0f ;
+	float	flNormZ=0.0f ;
+	float flTexU=0.0f ;
+	float flTexV=0.0f ;
+
+	float flLampPosX=0.0f ;
+	float flLampPosY=0.0f ;
+	float flLampPosZ=0.0f ;
+	float flLampBrightness=0.0f ;
+	float flLampR=0.0f ;
+	float flLampG=0.0f ;
+	float flLampB=0.0f ;
+
+	int nVertA=0 ;
+	int nVertB=0 ;
+	int nVertC=0 ;
+
+	char chMessage[1024] ;
+
+	int nZone=0 ;
+	char chManualName[1024] ; // name for manual objects we create
+	char chMeshName[1024] ;
+
+	int nTriangle=0 ;
+//	int nStart=0 ;
+	int nEnd=0 ;
+	int nMaterial=0 ;
+	int nGroup=0 ;
+	int nTri=0 ;
+	int nPos=0 ;
+
+	ZeroMemory((void*)m_nOpaqueNodeUsed, sizeof(m_nOpaqueNodeUsed)) ; // set all as unused
+	ZeroMemory((void*)m_nTransNodeUsed, sizeof(m_nTransNodeUsed)) ; // set all as unused
+	ZeroMemory((void*)m_nLampNodeUsed, sizeof(m_nLampNodeUsed)) ; // set all as unused
+	ZeroMemory((void*)m_nGlowNodeUsed, sizeof(m_nGlowNodeUsed)) ; // set all as unused
+	
+	char chMaterial_Base[1024] ;
+	MaterialPtr pMaterial_Base ; // used for checking if material scripts exist, and stored for the material switching that happens in deferred shading
+	
+	char chMaterial_Fast[1024] ;
+	MaterialPtr pMaterial_Fast ; // faster version of the base, mainly non-coloured shadowning.
+
+	char chMaterial_Black[1024] ;
+	MaterialPtr pMaterial_Black ; // just plain black, or black with alpha testing.
+
+	char chMaterial_DfShColour[1024] ;
+	MaterialPtr pMaterial_DfShColour ; // used for checking if material scripts exist, and stored for the material switching that happens in deferred shading
+	
+
+	char chMaterial_Shadow[1024] ;
+	MaterialPtr pMaterial_Shadow ;
+
+	char chMaterial_ShadeFront[1024] ;
+	MaterialPtr pMaterial_ShadeFront ;
+
+	char chMaterial_ShadeBack[1024] ;
+	MaterialPtr pMaterial_ShadeBack ;
+
+	char chMaterial_DfShPosition[1024] ;
+	MaterialPtr pMaterial_DfShPosition ;
+	
+	char chMaterial_DfShDiffuse[1024] ;
+	MaterialPtr pMaterial_DfShDiffuse ;
+	
+	//char chMaterial_DSNormal[1024] ;
+	//MaterialPtr pMaterial_DSNormal ;
+	
+	char chMaterial_DfShFuncTNB[1024] ;
+	MaterialPtr pMaterial_DfShFuncTNB ;
+
+	char chMaterial_DfShSpecular[1024] ;
+	MaterialPtr pMaterial_DfShSpecular ;
+
+	char chMaterial_DfShEmissive[1024] ;
+	MaterialPtr pMaterial_DfShEmissive ;
+
+	//char chMaterial_DfShData[1024] ;
+	//MaterialPtr pMaterial_DfShData ;
+
+	char chMaterial_DfShMix[1024] ;
+	MaterialPtr pMaterial_DfShMix ;
+
+	char chCompare[1024] ;
+
+	// for calculating bounds of mesh
+	float flMinX=0.0f ;
+	float flMinY=0.0f ;
+	float flMinZ=0.0f ;
+	float flMaxX=0.0f ;
+	float flMaxY=0.0f ;
+	float flMaxZ=0.0f ;
+//	float flMidX=0.0f ;
+//	float flMidY=0.0f ;
+//	float flMidZ=0.0f ;
+	float flDisX=0.0f ;
+	float flDisY=0.0f ;
+	float flDisZ=0.0f ;
+	float flRadius=0.0f ;
+	AxisAlignedBox AABB ;
+	unsigned short src, dest; // for tangent vectors
+
+
+	// Work out how many manual objects we are going to need.  We need one for every group within every material within every zone.
+	// Groups tend to all be 0 except for transparent triangles which have different groups within the same material/zone,
+	// due to the need to sort and render transparent things in a specific depth order relative to the camera.
+
+
+	int nMOCount=0 ;
+	nMaterial=-1 ;
+	nZone=-1 ;
+	nGroup=-1 ;
+
+	// loop through all triangles to count how many manual objects we'll need.
+	for(nTri=0 ; nTri<m_Q3Map->m_nTriangleMax ; nTri++)
+	{
+		
+		if(m_Q3Map->m_pTriangle[nTri].Zone!=nZone) // found a new zone
+		{
+			nZone=m_Q3Map->m_pTriangle[nTri].Zone ; 
+			nMaterial=m_Q3Map->m_pTriangle[nTri].Texture ;
+			nGroup=m_Q3Map->m_pTriangle[nTri].Group ;
+			nMOCount++ ;
+		}
+		else
+			if(m_Q3Map->m_pTriangle[nTri].Texture!=nMaterial) // found a new material within the current zone
+			{
+				nMaterial=m_Q3Map->m_pTriangle[nTri].Texture ;
+				nGroup=m_Q3Map->m_pTriangle[nTri].Group ;
+				nMOCount++ ;
+			}
+			else
+				if(m_Q3Map->m_pTriangle[nTri].Group!=nGroup) // found a new group within the current material within the current zone
+				{
+					nGroup=m_Q3Map->m_pTriangle[nTri].Group ;
+					nMOCount++ ;
+				}
+
+	}// end for tri
+	
+
+
+	// create the manualobject array for our zones and materials
+	m_pZoneMO = new Ogre::ManualObject*[nMOCount] ;
+	if(m_pZoneMO==NULL) 
+		return 0 ; // fail, out of mem
+	else
+		{m_nNewCount++ ; m_nNewCheck[NEW_CHECK_m_pZoneMO]++ ; }
+
+	m_pZoneMesh = new Ogre::MeshPtr[nMOCount] ;
+	if(m_pZoneMesh==NULL) 
+		return 0 ; // fail, out of mem
+	else
+		{m_nNewCount++ ; m_nNewCheck[NEW_CHECK_m_pZoneMesh]++ ; }
+
+	// our entities for the meshes
+	m_pZoneEntity = new Ogre::Entity*[nMOCount] ;
+	if(m_pZoneEntity==NULL) 
+		return 0 ; // fail, out of mem
+	else
+		{m_nNewCount++ ; m_nNewCheck[NEW_CHECK_m_pZoneEntity]++ ; }
+
+	// need to keep track of which entities are transparent
+	m_pZoneEntityMaterialType = new int[nMOCount] ;
+	if(m_pZoneEntityMaterialType==NULL) 
+		return 0 ; // fail, out of mem
+	else
+		{m_nNewCount++ ; m_nNewCheck[NEW_CHECK_m_pZoneEntityMaterialType]++ ; }
+	ZeroMemory((void*)m_pZoneEntityMaterialType, nMOCount*sizeof(int)) ; // set all as non-transparent
+
+
+	int nPTex=0 ;
+	for(nPTex=0 ; nPTex<MAX_PROJECTORTEX ; nPTex++)
+	{
+		// our base materials for the meshes
+		m_pZoneEntityMaterial_Base[nPTex] = new Ogre::MaterialPtr[nMOCount] ;
+		if(m_pZoneEntityMaterial_Base[nPTex]==NULL) 
+			return 0 ; // fail, out of mem
+		else
+			{m_nNewCount++ ; m_nNewCheck[NEW_CHECK_m_pZoneEntityMaterial_Base]++ ; }
+
+		// our fast materials for the meshes
+		m_pZoneEntityMaterial_Fast[nPTex] = new Ogre::MaterialPtr[nMOCount] ;
+		if(m_pZoneEntityMaterial_Fast[nPTex]==NULL) 
+			return 0 ; // fail, out of mem
+		else
+			{m_nNewCount++ ; m_nNewCheck[NEW_CHECK_m_pZoneEntityMaterial_Fast]++ ; }
+	}
+
+	// our black materials for the meshes
+	m_pZoneEntityMaterial_Black = new Ogre::MaterialPtr[nMOCount] ;
+	if(m_pZoneEntityMaterial_Black==NULL) 
+		return 0 ; // fail, out of mem
+	else
+		{m_nNewCount++ ; m_nNewCheck[NEW_CHECK_m_pZoneEntityMaterial_Black]++ ; }
+
+	// our base materials for the meshes
+	m_pZoneEntityMaterial_DfShColour = new Ogre::MaterialPtr[nMOCount] ;
+	if(m_pZoneEntityMaterial_DfShColour==NULL) 
+		return 0 ; // fail, out of mem
+	else
+		{m_nNewCount++ ; m_nNewCheck[NEW_CHECK_m_pZoneEntityMaterial_DfShColour]++ ; }
+
+	// materials for the meshes
+	m_pZoneEntityMaterial_Shadow = new Ogre::MaterialPtr[nMOCount] ;
+	if(m_pZoneEntityMaterial_Shadow==NULL) 
+		return 0 ; // fail, out of mem
+	else
+		{m_nNewCount++ ; m_nNewCheck[NEW_CHECK_m_pZoneEntityMaterial_Shadow]++ ; }
+
+	// materials for the meshes
+	m_pZoneEntityMaterial_ShadeFront = new Ogre::MaterialPtr[nMOCount] ;
+	if(m_pZoneEntityMaterial_ShadeFront==NULL) 
+		return 0 ; // fail, out of mem
+	else
+		{m_nNewCount++ ; m_nNewCheck[NEW_CHECK_m_pZoneEntityMaterial_ShadeFront]++ ; }
+
+	// materials for the meshes
+	m_pZoneEntityMaterial_ShadeBack = new Ogre::MaterialPtr[nMOCount] ;
+	if(m_pZoneEntityMaterial_ShadeBack==NULL) 
+		return 0 ; // fail, out of mem
+	else
+		{m_nNewCount++ ; m_nNewCheck[NEW_CHECK_m_pZoneEntityMaterial_ShadeBack]++ ; }
+
+	// materials for the meshes
+	m_pZoneEntityMaterial_DfShPosition = new Ogre::MaterialPtr[nMOCount] ;
+	if(m_pZoneEntityMaterial_DfShPosition==NULL) 
+		return 0 ; // fail, out of mem
+	else
+		{m_nNewCount++ ; m_nNewCheck[NEW_CHECK_m_pZoneEntityMaterial_DfShPosition]++ ; }
+
+	// materials for the meshes
+	m_pZoneEntityMaterial_DfShDiffuse = new Ogre::MaterialPtr[nMOCount] ;
+	if(m_pZoneEntityMaterial_DfShDiffuse==NULL) 
+		return 0 ; // fail, out of mem
+	else
+		{m_nNewCount++ ; m_nNewCheck[NEW_CHECK_m_pZoneEntityMaterial_DfShDiffuse]++ ; }
+
+	// materials for the meshes
+	//m_pZoneEntityMaterial_DSNormal = new Ogre::MaterialPtr[nMOCount] ;
+	//if(m_pZoneEntityMaterial_DSNormal==NULL) return 0 ; // fail, out of mem
+
+	// materials for the meshes
+	m_pZoneEntityMaterial_DfShFuncTNB = new Ogre::MaterialPtr[nMOCount] ;
+	if(m_pZoneEntityMaterial_DfShFuncTNB==NULL) 
+		return 0 ; // fail, out of mem
+	else
+		{m_nNewCount++ ; m_nNewCheck[NEW_CHECK_m_pZoneEntityMaterial_DfShFuncTNB]++ ; }
+
+	// materials for the meshes
+	m_pZoneEntityMaterial_DfShSpecular = new Ogre::MaterialPtr[nMOCount] ;
+	if(m_pZoneEntityMaterial_DfShSpecular==NULL) 
+		return 0 ; // fail, out of mem
+	else
+		{m_nNewCount++ ; m_nNewCheck[NEW_CHECK_m_pZoneEntityMaterial_DfShSpecular]++ ; }
+
+	// materials for the meshes
+	m_pZoneEntityMaterial_DfShEmissive = new Ogre::MaterialPtr[nMOCount] ;
+	if(m_pZoneEntityMaterial_DfShEmissive==NULL) 
+		return 0 ; // fail, out of mem
+	else
+		{m_nNewCount++ ; m_nNewCheck[NEW_CHECK_m_pZoneEntityMaterial_DfShEmissive]++ ; }
+
+
+	// materials for the meshes
+	//m_pZoneEntityMaterial_DfShData = new Ogre::MaterialPtr[nMOCount] ;
+	//if(m_pZoneEntityMaterial_DfShData==NULL) return 0 ; // fail, out of mem
+
+	// materials for the meshes
+	m_pZoneEntityMaterial_DfShMix = new Ogre::MaterialPtr[nMOCount] ;
+	if(m_pZoneEntityMaterial_DfShMix==NULL) 
+		return 0 ; // fail, out of mem
+	else
+		{m_nNewCount++ ; m_nNewCheck[NEW_CHECK_m_pZoneEntityMaterial_DfShMix]++ ; }
+
+
+	// memory for keeping track of re-assigned verts, since we only add verts to a manualobject if they are needed, we don't add every vertex
+	int* pVertIndex = new int [m_Q3Map->m_nVertexMax] ; // at most, there can be m_Q3Map->m_nVertexMax verts in a zone if the map is one big subzone.
+	if(pVertIndex==NULL) //{ CHECKDELETE_ARRAY( m_pZoneMO ) ; return 0 ; } // fail, out of mem
+		return 0 ;
+	else
+		{m_nNewCount++ ; m_nNewCheck[NEW_CHECK_pVertIndex]++ ; }
+
+
+
+
+
+	// go through all the zones and create their manualobjects in m_pZoneMO, one for each material
+	int nMOIndex=0 ;
+	for(nZone=0 ; nZone<m_Q3Map->m_nMaxZone ; nZone++)
+	{
+		m_nZoneTriangleCount[nZone]=0 ; // just for our stats
+
+
+		m_nZoneMOStart[nZone]=nMOIndex ; // record where we start from.
+
+		// because the triangles have been sorted in order of zone, if there are any triangles in this zone
+		// then nTriangle will already be on the first example.  However it could be there is a zone that contains
+		// no triangles.  If we have such an empty zone, we don't create any manual object for it and set its pointer
+		// to NULL.
+
+		if(m_Q3Map->m_pTriangle[nTriangle].Zone!=nZone)
+		{
+			m_pZoneMO[nMOIndex]=NULL ;
+			continue ;
+			//break ;
+		}
+
+		
+
+
+		// scan through all triangles in this zone
+		while((nTriangle<m_Q3Map->m_nTriangleMax) && (m_Q3Map->m_pTriangle[nTriangle].Zone==nZone))
+		{
+			
+
+			
+			//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+			//
+			// start adding a begin/end block of all triangles in a zone with the same material/group
+			//
+			//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+
+
+			
+			nMaterial=m_Q3Map->m_pTriangle[nTriangle].Texture ;
+			nGroup=m_Q3Map->m_pTriangle[nTriangle].Group ;
+			
+
+			
+
+			// create the zone's manualobject
+			sprintf(chManualName, "ZoneMO_%04i_%04i_%05i", nZone, nMaterial, nTriangle) ;
+			m_pZoneMO[nMOIndex]=m_pSceneMgr->createManualObject(chManualName) ;
+			
+			
+
+			// scan forward to find the end of this block of triangles with the same texture and group
+			nEnd=nTriangle+1 ;
+			while(
+								(nEnd<m_Q3Map->m_nTriangleMax) 
+							&&(m_Q3Map->m_pTriangle[nEnd].Zone==nZone)
+							&&(m_Q3Map->m_pTriangle[nEnd].Texture==nMaterial)
+							&&(m_Q3Map->m_pTriangle[nEnd].Group==nGroup)
+						)
+				nEnd++ ;
+
+			// copy the material name.
+			// q3 texture names are a max of 64 characters ( Q3NAMESIZE ) and may not be null terminated.  They have no extension either.
+			nPos=-1 ;
+			while((++nPos<Q3NAMESIZE) && (m_Q3Map->m_pTexture[nMaterial].name[nPos]!=' ') && (m_Q3Map->m_pTexture[nMaterial].name[nPos]!='\0'))
+				chMaterial_Base[nPos]=m_Q3Map->m_pTexture[nMaterial].name[nPos] ;
+
+			// make sure name is null terminated
+			chMaterial_Base[nPos]='\0' ;
+
+
+
+			//..
+
+			//if(strcmp(chMaterial_Base, "textures/common/exptex")==0) strcpy(chMaterial_Base, "GEL_ShinyGlassPlateTL") ; 
+			//if(strcmp(chMaterial_Base, "textures/common/exptex")==0) strcpy(chMaterial_Base, "GEL_DSC_Prototype") ; 
+			//if(strcmp(chMaterial_Base, "textures/common/exptex")==0) strcpy(chMaterial_Base, "GEL_DSC_Prototype") ; 
+			if(strcmp(chMaterial_Base, "textures/common/exptex")==0) strcpy(chMaterial_Base, "GEL_DSC_glassred") ; 
+			if(strcmp(chMaterial_Base, "textures/common/exptex2")==0) strcpy(chMaterial_Base, "GEL_GreyGlass") ; 
+			if(strcmp(chMaterial_Base, "textures/common/alphaplate")==0) strcpy(chMaterial_Base, "ALPHAPF_DSCE_Prototype") ;
+			//if(strcmp(chMaterial_Base, "textures/common/alphaplate")==0) strcpy(chMaterial_Base, "OPAQUE_DSC_SimplePlateB") ;
+			//if(strcmp(chMaterial_Base, "textures/common/alphaplate")==0) strcpy(chMaterial_Base, "ALPHAPF_DS_alphaplategreen") ;
+
+
+			
+			// if the material is a lamp material we need to specify either an OpenGl or Direct3D version
+			strcpy(chCompare, chMaterial_Base) ;
+			chCompare[12]='\0' ;
+			if(strcmp("lights/lamp_", chCompare)==0) // material is LAMP 
+			{
+				if(m_IsOpenGL)
+					strcat(chMaterial_Base, "_ogl") ;
+				else
+					strcat(chMaterial_Base, "_d3d") ;
+
+			}
+
+			strcpy(chCompare, chMaterial_Base) ;
+			chCompare[17]='\0' ;
+			if(strcmp("lights/lamp2pass_", chCompare)==0) // material is LAMP, 2 pass version 
+			{
+				if(m_IsOpenGL)
+					strcat(chMaterial_Base, "_ogl") ;
+				else
+					strcat(chMaterial_Base, "_d3d") ;
+			}
+			
+
+
+			// check that this material script exists, if not set to the default
+			pMaterial_Base=MaterialManager::getSingleton().getByName(chMaterial_Base) ;
+			if(pMaterial_Base.isNull())
+			{
+				strcat(chMaterial_Base, " *** MISSING ***") ;
+//				OFBug.LogAddCR(chMaterial_Base) ;
+				
+				//sprintf(chMessage, "Missing material, using default: %s", chMaterial_Base) ;
+				//m_pLog->logMessage(chMessage);
+
+				/*
+				switch(rand()%6)
+				{
+					case 0: strcpy(chMaterial_Base, "OPAQUE_DSC_Plate2x2A") ;					break ;
+					case 1: strcpy(chMaterial_Base, "OPAQUE_DSC_HexColourA") ;				break ;
+					case 2: strcpy(chMaterial_Base, "OPAQUE_DSC_SimplePlateB") ;			break ;
+					case 3: strcpy(chMaterial_Base, "OPAQUE_DSC_FloorA") ;						break ;
+					case 4: strcpy(chMaterial_Base, "OPAQUE_DSCE_WallTrimUD_A") ;			break ;
+					case 5: strcpy(chMaterial_Base, "OPAQUE_DSCE_TechRadar") ;				break ;
+				}
+				*/
+
+				//strcpy(chMaterial_Base, "OPAQUE_DSC_SimplePlateB") ;	
+
+				//strcpy(chMaterial_Base, "OPAQUE_DSC_Plate2x2A") ;
+				//strcpy(chMaterial_Base, "OPAQUE_DSC_HexColourA") ;
+				//strcpy(chMaterial_Base, "OPAQUE_DSC_SimplePlateB") ;
+				//strcpy(chMaterial_Base, "OPAQUE_DSC_FloorA") ;
+				strcpy(chMaterial_Base, "textures/metaltech/OPAQUE_DSCE_TechRadar") ;
+				//strcpy(chMaterial_Base, "OPAQUE_DSCE_WallTrimA") ;
+				//strcpy(chMaterial_Base, "OPAQUE_DSCE_WallTrimB") ;
+
+				//strcpy(chMaterial_Base, "Standard_Normal") ;
+				//strcpy(chMaterial_Base, "test") ;
+				//strcpy(chMaterial_Base, "metalN") ;
+				//strcpy(chMaterial_Base, "OPAQUE_DSC_Prototype") ;
+				
+				//strcpy(chMaterial_Base, "textures/common/exptex") ;
+				//strcpy(chMaterial_Base, "metalN_DSNormal") ;
+				//strcpy(chMaterial_Base, "metalN_DfShFuncTNB") ;
+				//strcpy(chMaterial_Base, "metalN_DfShDiffuse") ;
+				//strcpy(chMaterial_Base, "metalN_DfShPosition") ;
+				//strcpy(chMaterial_Base, "metalD") ;
+				//strcpy(chMaterial_Base, "metalR") ;	
+				//strcpy(chMaterial_Base, "WonderShader_Lim3_Fast") ;
+				//strcpy(chMaterial_Base, "reliefMaterial") ;
+				//strcpy(chMaterial_Base, "tute02") ;reliefMaterial
+				//strcpy(chMaterial_Base, "Examples/OffsetMapping/Specular") ;
+				pMaterial_Base=MaterialManager::getSingleton().getByName(chMaterial_Base) ;
+			}
+			else
+//				OFBug.LogAddCR(chMaterial_Base) ;
+
+
+
+			if(nMOIndex==20)
+			{
+				sprintf(m_chBug, "****** %s *******", chMaterial_Base) ;
+				m_pLog->logMessage(m_chBug);
+			}
+
+
+
+
+			// setup whether this material and it's entities are transparent or whatever
+
+			if(strstr(chMaterial_Base, "OPAQUE")!=NULL) // material and its entity are OPAQUE
+			{
+				m_nOpaqueNodeUsed[nZone]=1 ;
+				m_pZoneEntityMaterialType[nMOIndex]=MAT_OPAQUE ; // note that the derived entity will be opaque
+			}
+			else
+			{
+
+				if(strstr(chMaterial_Base, "ALPHAPF")!=NULL) // material and its entity are ALPHAPF
+				{
+					m_nOpaqueNodeUsed[nZone]=1 ;
+					m_pZoneEntityMaterialType[nMOIndex]=MAT_OPAQUE ; // note that the derived entity will be opaque
+				}
+				else
+				{
+
+					if(strstr(chMaterial_Base, "GEL")!=NULL) // material and its entity are GEL
+					{
+						m_nTransNodeUsed[nZone]=1 ;
+						m_pZoneEntityMaterialType[nMOIndex]=MAT_GEL ; // note that the derived entity will be transparent
+					}
+					else
+					{
+
+						strcpy(chCompare, chMaterial_Base) ;
+						chCompare[12]='\0' ;
+						if(strcmp("lights/lamp_", chCompare)==0) // material and its entity are LAMP (spotlights don't ever make triangles, only lamps)
+						{
+							m_nLampNodeUsed[nZone]=1 ;
+							m_pZoneEntityMaterialType[nMOIndex]=MAT_LAMP ; // note that the derived entity will be lamp
+						}
+						else
+						{
+							strcpy(chCompare, chMaterial_Base) ;
+							chCompare[17]='\0' ;
+							if(strcmp("lights/lamp2pass_", chCompare)==0) // material and its entity are LAMP (spotlights don't ever make triangles, only lamps)
+							{
+								m_nLampNodeUsed[nZone]=1 ;
+								m_pZoneEntityMaterialType[nMOIndex]=MAT_LAMP ; // note that the derived entity will be lamp
+							}
+							else
+							{
+
+								if(strstr(chMaterial_Base, "GLOW_")!=NULL) // material and its entity are GLOW
+								{
+									m_nGlowNodeUsed[nZone]=1 ;
+									m_pZoneEntityMaterialType[nMOIndex]=MAT_GLOW ; // note that the derived entity will be glow
+								}
+							}	
+						}					
+					}
+				}
+			}
+
+
+
+
+
+
+			///////////////////////////////////////////////////////////////////////////////////////////////////////
+			//
+			// Setup the different material names and pointers needed for various passes
+			//
+			// Lamps need only one material, pMaterial_Base, which is already assigned above, so it skips all this.
+			//
+			// Glow needs two materials, pMaterial_Base plus pMaterial_DfShColour for the coloured gel deferred pass.
+			// However it's better to make a third, and duplicate the pMaterial_Base to pMaterial_Emissive, since
+			// when glow gets rendered on the Emmisive pass it would be odd to switch to the base material instead
+			// of the emmisive.
+			//
+			// Other objects need multiple materials for various different deferred shading and FX passes
+			
+			if((m_pZoneEntityMaterialType[nMOIndex]!=MAT_LAMP) && (m_pZoneEntityMaterialType[nMOIndex]!=MAT_GLOW))
+			{
+
+				// material for fast shading
+				strcpy(chMaterial_Fast, chMaterial_Base) ;
+				strcat(chMaterial_Fast, "_Fast") ;
+
+				pMaterial_Fast=MaterialManager::getSingleton().getByName(chMaterial_Fast) ;
+				if(pMaterial_Fast.isNull())
+				{
+					sprintf(chMessage, "Missing material, using default: %s", chMaterial_Fast) ;
+					m_pLog->logMessage(chMessage);
+
+					strcpy(chMaterial_Fast, "Fast") ;
+					pMaterial_Fast=MaterialManager::getSingleton().getByName(chMaterial_Fast) ;
+				}
+
+				// material for black shading
+				strcpy(chMaterial_Black, chMaterial_Base) ;
+				strcat(chMaterial_Black, "_Black") ;
+				pMaterial_Black=MaterialManager::getSingleton().getByName(chMaterial_Black) ;
+				if(pMaterial_Black.isNull())
+				{
+					// only issue a warning if this is an alpha testing material, others don't need specialized Black materials
+					if(strstr(chMaterial_Base, "ALPHAPF")!=NULL)
+					{
+						sprintf(chMessage, "Missing material, using default: %s", chMaterial_Black) ;
+						m_pLog->logMessage(chMessage);
+					}
+
+					strcpy(chMaterial_Black, "Black") ;
+					pMaterial_Black=MaterialManager::getSingleton().getByName(chMaterial_Black) ;
+				}
+				
+				// material for shadow casting
+				strcpy(chMaterial_Shadow, chMaterial_Base) ;
+				strcat(chMaterial_Shadow, "_Shadow") ;
+				pMaterial_Shadow=MaterialManager::getSingleton().getByName(chMaterial_Shadow) ;
+				if(pMaterial_Shadow.isNull())
+				{
+					strcpy(chMaterial_Shadow, "OPAQUE_Shadow") ;
+					pMaterial_Shadow=MaterialManager::getSingleton().getByName(chMaterial_Shadow) ;
+				}
+
+				
+				
+				// material for shadow casting
+				strcpy(chMaterial_ShadeFront, chMaterial_Base) ;
+				strcat(chMaterial_ShadeFront, "_ShadeFront") ;
+				pMaterial_ShadeFront=MaterialManager::getSingleton().getByName(chMaterial_ShadeFront) ;
+				if(pMaterial_ShadeFront.isNull())
+				{
+					 // standard opaque ShadeFront.  
+					// Transparent surfaces MUST have their own _ShadeFront material or they will not colour light and can interfere with other trans surfaces.
+					strcpy(chMaterial_ShadeFront, "OPAQUE_ShadeFront") ;
+
+					pMaterial_ShadeFront=MaterialManager::getSingleton().getByName(chMaterial_ShadeFront) ;
+				}
+
+
+				// material for shadow casting
+				strcpy(chMaterial_ShadeBack, chMaterial_Base) ;
+				strcat(chMaterial_ShadeBack, "_ShadeBack") ;
+				pMaterial_ShadeBack=MaterialManager::getSingleton().getByName(chMaterial_ShadeBack) ;
+				if(pMaterial_ShadeBack.isNull())
+				{
+					 // standard opaque ShadeBack.  
+					// Transparent surfaces MUST have their own _ShadeBack material or they will not colour light and can interfere with other trans surfaces.
+					strcpy(chMaterial_ShadeBack, "OPAQUE_ShadeBack") ;
+
+					pMaterial_ShadeBack=MaterialManager::getSingleton().getByName(chMaterial_ShadeBack) ;
+				}
+
+				// material for deferred shading
+				strcpy(chMaterial_DfShColour, chMaterial_Base) ;
+				strcat(chMaterial_DfShColour, "_DfShColour") ;
+				pMaterial_DfShColour=MaterialManager::getSingleton().getByName(chMaterial_DfShColour) ;
+				if(pMaterial_DfShColour.isNull())
+				{
+					sprintf(chMessage, "Missing material, using default: %s", chMaterial_DfShColour) ;
+					m_pLog->logMessage(chMessage);
+
+					strcpy(chMaterial_DfShColour, "DfShColour") ;
+					pMaterial_DfShColour=MaterialManager::getSingleton().getByName(chMaterial_DfShColour) ;
+				}
+				
+
+				// material for deferred shading
+				strcpy(chMaterial_DfShPosition, chMaterial_Base) ;
+				strcat(chMaterial_DfShPosition, "_DfShPosition") ;
+				pMaterial_DfShPosition=MaterialManager::getSingleton().getByName(chMaterial_DfShPosition) ;
+				if(pMaterial_DfShPosition.isNull())
+				{
+					// only issue a warning if this is an alpha testing material, others don't need specialized DfShPosition materials
+					if(strstr(chMaterial_Base, "ALPHAPF")!=NULL)
+					{
+						sprintf(chMessage, "Missing material, using default: %s", chMaterial_DfShPosition) ;
+						m_pLog->logMessage(chMessage);
+					}
+
+					strcpy(chMaterial_DfShPosition, "DfShPosition") ;
+					pMaterial_DfShPosition=MaterialManager::getSingleton().getByName(chMaterial_DfShPosition) ;
+				}
+				
+				// material for deferred shading
+				strcpy(chMaterial_DfShDiffuse, chMaterial_Base) ;
+				strcat(chMaterial_DfShDiffuse, "_DfShDiffuse") ;
+				pMaterial_DfShDiffuse=MaterialManager::getSingleton().getByName(chMaterial_DfShDiffuse) ;
+				if(pMaterial_DfShDiffuse.isNull())
+				{
+					sprintf(chMessage, "Missing material, using default: %s", chMaterial_DfShDiffuse) ;
+					m_pLog->logMessage(chMessage);
+
+					strcpy(chMaterial_DfShDiffuse, "DfShDiffuse") ;
+					pMaterial_DfShDiffuse=MaterialManager::getSingleton().getByName(chMaterial_DfShDiffuse) ;
+				}
+
+				/*
+				// material for deferred shading // UNUSED
+				strcpy(chMaterial_DSNormal, chMaterial_Base) ;
+				strcat(chMaterial_DSNormal, "_DSNormal") ;
+				pMaterial_DSNormal=MaterialManager::getSingleton().getByName(chMaterial_DSNormal) ;
+				if(pMaterial_DSNormal.isNull())
+				{
+					sprintf(chMessage, "Missing material, using default: %s", chMaterial_DSNormal) ;
+					m_pLog->logMessage(chMessage);
+
+					strcpy(chMaterial_DSNormal, "DSNormal") ;
+					pMaterial_DSNormal=MaterialManager::getSingleton().getByName(chMaterial_DSNormal) ;
+				}
+				*/
+
+				// material for deferred shading
+				strcpy(chMaterial_DfShFuncTNB, chMaterial_Base) ;
+				strcat(chMaterial_DfShFuncTNB, "_DfShFuncTNB") ;
+				pMaterial_DfShFuncTNB=MaterialManager::getSingleton().getByName(chMaterial_DfShFuncTNB) ;
+				if(pMaterial_DfShFuncTNB.isNull())
+				{
+					sprintf(chMessage, "Missing material, using default: %s", chMaterial_DfShFuncTNB) ;
+					m_pLog->logMessage(chMessage);
+
+					strcpy(chMaterial_DfShFuncTNB, "DfShFuncTNB") ;
+					pMaterial_DfShFuncTNB=MaterialManager::getSingleton().getByName(chMaterial_DfShFuncTNB) ;
+				}
+
+				// material for deferred shading
+				strcpy(chMaterial_DfShSpecular, chMaterial_Base) ;
+				strcat(chMaterial_DfShSpecular, "_DfShSpecular") ;
+				pMaterial_DfShSpecular=MaterialManager::getSingleton().getByName(chMaterial_DfShSpecular) ;
+				if(pMaterial_DfShSpecular.isNull())
+				{
+					sprintf(chMessage, "Missing material, using default: %s", chMaterial_DfShSpecular) ;
+					m_pLog->logMessage(chMessage);
+
+					strcpy(chMaterial_DfShSpecular, "DfShSpecular") ;
+					pMaterial_DfShSpecular=MaterialManager::getSingleton().getByName(chMaterial_DfShSpecular) ;
+				}
+
+				// material for deferred shading
+				strcpy(chMaterial_DfShEmissive, chMaterial_Base) ;
+				strcat(chMaterial_DfShEmissive, "_DfShEmissive") ;
+				pMaterial_DfShEmissive=MaterialManager::getSingleton().getByName(chMaterial_DfShEmissive) ;
+				if(pMaterial_DfShEmissive.isNull())
+				{
+					sprintf(chMessage, "Missing material, using default: %s", chMaterial_DfShEmissive) ;
+					m_pLog->logMessage(chMessage);
+
+					strcpy(chMaterial_DfShEmissive, "DfShEmissive") ;
+					pMaterial_DfShEmissive=MaterialManager::getSingleton().getByName(chMaterial_DfShEmissive) ;
+				}
+
+
+				/*
+				// material for deferred shading
+				strcpy(chMaterial_DfShData, chMaterial_Base) ;
+				strcat(chMaterial_DfShData, "_DfShData") ;
+				pMaterial_DfShData=MaterialManager::getSingleton().getByName(chMaterial_DfShData) ;
+				if(pMaterial_DfShData.isNull())
+				{
+					sprintf(chMessage, "Missing material, using default: %s", chMaterial_DfShData) ;
+					m_pLog->logMessage(chMessage);
+
+					strcpy(chMaterial_DfShData, "DfShData") ; 
+					pMaterial_DfShData=MaterialManager::getSingleton().getByName(chMaterial_DfShData) ;
+				}
+				*/
+
+				// material for deferred shading
+				strcpy(chMaterial_DfShMix, chMaterial_Base) ;
+				strcat(chMaterial_DfShMix, "_DfShMix") ;
+				pMaterial_DfShMix=MaterialManager::getSingleton().getByName(chMaterial_DfShMix) ;
+				if(pMaterial_DfShMix.isNull())
+				{
+					// Pretty much all materials just use the default mix material,
+					// so don't issue warnings if there isn't a specialized version
+					//sprintf(chMessage, "Missing material, using default: %s", chMaterial_DfShMix) ;
+					//m_pLog->logMessage(chMessage);
+
+					strcpy(chMaterial_DfShMix, "DfShMix") ;
+					pMaterial_DfShMix=MaterialManager::getSingleton().getByName(chMaterial_DfShMix) ;
+				}
+				
+			
+
+
+
+			}// end if not MAT_LAMP or MAT_GLOW
+			else
+				if(m_pZoneEntityMaterialType[nMOIndex]==MAT_GLOW) // the two materials that glow needs
+				{
+
+					// when I get around to allowing user defined GLOW materials as well as the automatic ones,
+					// this'll be the place to add it.  
+					// For now, just the automatically generated lamp/light stuff has a glow.
+					
+					// if the base material was "GLOW_lamp"	
+					strcpy(chCompare, chMaterial_Base) ;
+					chCompare[strlen("GLOW_lamp")]='\0' ;
+					if(strcmp("GLOW_lamp", chCompare)==0) 
+					{
+						strcpy(chMaterial_DfShColour, "GLOW_lampcol") ;
+						pMaterial_DfShColour=MaterialManager::getSingleton().getByName(chMaterial_DfShColour) ;
+
+						strcpy(chMaterial_DfShEmissive, "GLOW_lamp") ;
+						pMaterial_DfShEmissive=MaterialManager::getSingleton().getByName(chMaterial_DfShEmissive) ;
+					}
+					else
+					{
+
+						// if the base material was "GLOW_light"
+						strcpy(chCompare, chMaterial_Base) ;
+						chCompare[strlen("GLOW_light")]='\0' ;
+						if(strcmp("GLOW_light", chCompare)==0) 
+						{
+							strcpy(chMaterial_DfShColour, "GLOW_lightcol") ;
+							pMaterial_DfShColour=MaterialManager::getSingleton().getByName(chMaterial_DfShColour) ;
+
+							strcpy(chMaterial_DfShEmissive, "GLOW_light") ;
+							pMaterial_DfShEmissive=MaterialManager::getSingleton().getByName(chMaterial_DfShEmissive) ;
+						}
+
+					}
+				}
+
+
+
+
+
+
+			// work out which vertices we'll need, we don't add every vertex, just used ones.
+			nMaxVertex=0 ;
+			for(nVert=0 ; nVert<m_Q3Map->m_nVertexMax ; nVert++)
+				pVertIndex[nVert]=-1 ; // initialize all verts as unused.
+			
+
+			// start defining the manualObject
+			m_pZoneMO[nMOIndex]->begin(chMaterial_Base, RenderOperation::OT_TRIANGLE_LIST) ;
+
+
+			// reset the min max stuff
+			flMinX=flMinY=flMinZ=MINMAXLIMIT ;
+			flMaxX=flMaxY=flMaxZ=-MINMAXLIMIT ;
+	
+
+			// add the verts.  We avoid repeating the same vert multiple times where it is used by many triangles
+			for(nTri=nTriangle ; nTri<nEnd ; nTri++)
+				for(nVert=0 ; nVert<3 ; nVert++)
+				{
+					if(pVertIndex[ m_Q3Map->m_pTriangle[nTri].VIndex[nVert] ]==-1) // this vert hasn't been added yet
+					{
+						pVertIndex[ m_Q3Map->m_pTriangle[nTri].VIndex[nVert] ]=nMaxVertex++ ; // so we can correctly look it up when we add the triangles
+
+						flPosX=m_Q3Map->m_pVertex[	m_Q3Map->m_pTriangle[nTri].VIndex[nVert]  ].position[0] ;
+						flPosY=m_Q3Map->m_pVertex[	m_Q3Map->m_pTriangle[nTri].VIndex[nVert]	].position[1] ;
+						flPosZ=m_Q3Map->m_pVertex[	m_Q3Map->m_pTriangle[nTri].VIndex[nVert]	].position[2] ;
+						flNormX=m_Q3Map->m_pVertex[ m_Q3Map->m_pTriangle[nTri].VIndex[nVert]	].normal[0] ;
+						flNormY=m_Q3Map->m_pVertex[ m_Q3Map->m_pTriangle[nTri].VIndex[nVert]	].normal[1] ;
+						flNormZ=m_Q3Map->m_pVertex[ m_Q3Map->m_pTriangle[nTri].VIndex[nVert]	].normal[2] ;
+						
+						
+						
+						// lamps have some different vertex data, also glow
+						// lamp needs colour and also info about the origin, which is stored in the UVs.
+						// glow only needs the colour, but it doesn't hurt to store the orgin too, no harm and simpler code.
+						if((m_pZoneEntityMaterialType[nMOIndex]==MAT_LAMP) || (m_pZoneEntityMaterialType[nMOIndex]==MAT_GLOW))
+						{
+							flLampPosX =			m_Q3Map->m_pVertex[	m_Q3Map->m_pTriangle[nTri].VIndex[nVert]	].texcoord[0][0] ;
+							flLampPosY =			m_Q3Map->m_pVertex[	m_Q3Map->m_pTriangle[nTri].VIndex[nVert]	].texcoord[0][1] ;
+							flLampPosZ =			m_Q3Map->m_pVertex[	m_Q3Map->m_pTriangle[nTri].VIndex[nVert]	].texcoord[1][0] ;
+							flLampBrightness=	m_Q3Map->m_pVertex[	m_Q3Map->m_pTriangle[nTri].VIndex[nVert]	].texcoord[1][1] ;
+
+							flLampR=m_Q3Map->m_pVertex[	m_Q3Map->m_pTriangle[nTri].VIndex[nVert]	].color[0]/255.0f ;
+							flLampG=m_Q3Map->m_pVertex[	m_Q3Map->m_pTriangle[nTri].VIndex[nVert]	].color[1]/255.0f ;
+							flLampB=m_Q3Map->m_pVertex[	m_Q3Map->m_pTriangle[nTri].VIndex[nVert]	].color[2]/255.0f ;
+
+							// set the vertex data
+							m_pZoneMO[nMOIndex]->position(flPosX, flPosY, flPosZ) ;
+							m_pZoneMO[nMOIndex]->normal(flNormX, flNormY, flNormZ) ;
+							m_pZoneMO[nMOIndex]->textureCoord(flLampPosX, flLampPosY, flLampPosZ, flLampBrightness) ;
+							m_pZoneMO[nMOIndex]->colour(flLampR, flLampG, flLampB) ;
+
+						}
+						else // non-lamps
+						{
+							flTexU=m_Q3Map->m_pVertex[	m_Q3Map->m_pTriangle[nTri].VIndex[nVert]	].texcoord[0][0] ;
+							flTexV=m_Q3Map->m_pVertex[	m_Q3Map->m_pTriangle[nTri].VIndex[nVert]	].texcoord[0][1] ;
+
+							// set the vertex data
+							m_pZoneMO[nMOIndex]->position(flPosX, flPosY, flPosZ) ;
+							m_pZoneMO[nMOIndex]->normal(flNormX, flNormY, flNormZ) ;
+							m_pZoneMO[nMOIndex]->textureCoord(flTexU, flTexV) ;
+						}
+
+						// update bounds
+						if(flPosX<flMinX) flMinX=flPosX ;
+						if(flPosY<flMinY) flMinY=flPosY ;
+						if(flPosZ<flMinZ) flMinZ=flPosZ ;
+						if(flPosX>flMaxX) flMaxX=flPosX ;
+						if(flPosY>flMaxY) flMaxY=flPosY ;
+						if(flPosZ>flMaxZ) flMaxZ=flPosZ ;
+
+					}// end if adding vert
+				}
+
+			// add the triangles
+			for(nTri=nTriangle ; nTri<nEnd ; nTri++)
+			{
+				nVertA=pVertIndex[ m_Q3Map->m_pTriangle[nTri].VIndex[0] ] ;
+				nVertB=pVertIndex[ m_Q3Map->m_pTriangle[nTri].VIndex[1] ] ;
+				nVertC=pVertIndex[ m_Q3Map->m_pTriangle[nTri].VIndex[2] ] ;
+				m_pZoneMO[nMOIndex]->triangle(nVertA, nVertC, nVertB) ;
+				m_nZoneTriangleCount[nZone]++ ;
+				//m_pZoneMO[nMOIndex]->triangle(nVertA, nVertB, nVertC) ;
+			}
+
+
+			// all done for this block of material
+			m_pZoneMO[nMOIndex]->end() ;
+			m_pZoneMO[nMOIndex]->setCastShadows(true) ;
+			m_pZoneMO[nMOIndex]->setDynamic(false) ;
+
+			// convert the manualobject to a mesh
+			sprintf(chMeshName, "ZoneMesh_%04i_%04i_%05i", nZone, nMaterial, nTriangle) ;
+			m_pZoneMesh[nMOIndex]= m_pZoneMO[nMOIndex]->convertToMesh(chMeshName);
+
+			// don't need the manual object anymore
+			m_pSceneMgr->destroyManualObject(m_pZoneMO[nMOIndex]) ;
+
+
+			// temp cludge to add thickness
+			flMinX-=1.0f ;
+			flMinY-=1.0f ;
+			flMinZ-=1.0f ;
+			flMaxX+=1.0f ;
+			flMaxY+=1.0f ;
+			flMaxZ+=1.0f ;
+
+			// set axis aligned bounding box of this mesh
+			AABB.setMinimumX(flMinX) ; 
+			AABB.setMinimumY(flMinY) ; 
+			AABB.setMinimumZ(flMinZ) ;
+			AABB.setMaximumX(flMaxX) ; 
+			AABB.setMaximumY(flMaxY) ; 
+			AABB.setMaximumZ(flMaxZ) ;
+			m_pZoneMesh[nMOIndex]->_setBounds(AABB, false) ;
+			
+			// set bounding sphere of this mesh
+			flDisX=flMaxX-flMinX ;
+			flDisY=flMaxY-flMinY ;
+			flDisZ=flMaxZ-flMinZ ;
+			flRadius=sqrt( flDisX*flDisX + flDisY*flDisY + flDisZ*flDisZ )/2.0 ;
+			m_pZoneMesh[nMOIndex]->_setBoundingSphereRadius(flRadius) ;
+
+
+			//if(strcmp(chMaterial_Base, "textures/metaltech/GEL_DSC_glassred")==0)
+			//	OFBug.MessageFloat(flRadius, flMinX, flMinY, flMinZ, flMaxX, flMaxY, flMaxZ) ;
+
+			//if(strcmp(chMaterial_Base, "textures/metaltech/GEL_DSC_glassgreen")==0)
+			//	OFBug.MessageFloat(flRadius, flMinX, flMinY, flMinZ, flMaxX, flMaxY, flMaxZ) ;
+
+			// calculate tangents for the mesh		
+			if((m_pZoneEntityMaterialType[nMOIndex]!=MAT_LAMP) && (m_pZoneEntityMaterialType[nMOIndex]!=MAT_GLOW))// lamps and glow don't need tangent vectors
+			{
+				m_pZoneMesh[nMOIndex]->suggestTangentVectorBuildParams(VES_TANGENT, src, dest) ;
+				m_pZoneMesh[nMOIndex]->buildTangentVectors(VES_TANGENT, src, dest);
+			}
+   	
+			m_pZoneMesh[nMOIndex]->load() ;
+
+			m_pZoneMesh[nMOIndex]->getSubMesh(0)->generateExtremes(8) ;
+
+			// create an entity for this mesh.  We need this so we can change the material during deferred shading
+			m_pZoneEntity[nMOIndex]=m_pSceneMgr->createEntity(m_pZoneMesh[nMOIndex]->getName(), m_pZoneMesh[nMOIndex]->getName()) ;
+
+			// if the entity is a lamp or glow, set its material because they never get changed
+			if((m_pZoneEntityMaterialType[nMOIndex]==MAT_LAMP) || (m_pZoneEntityMaterialType[nMOIndex]==MAT_GLOW)) 
+				m_pZoneEntity[nMOIndex]->setMaterial( pMaterial_Base) ;
+
+
+			//AABB=m_pZoneEntity[nMOIndex]->getBoundingBox() ;
+			//Ogre::Vector3 mn, mx ;
+			//mn=AABB.getMinimum() ;
+			//mx=AABB.getMaximum() ;
+			//if(strcmp(chMaterial_Base, "textures/metaltech/GEL_DSC_glassred")==0)
+			//	OFBug.MessageFloat(mn.x, mn.y, mn.z, mx.x, mx.y, mx.z) ; 
+
+
+
+
+
+
+			// store the pointers to the materials of this entity
+			//m_pZoneEntityMaterial_Base[nMOIndex]=pMaterial_Base ;
+			//m_pZoneEntityMaterial_Fast[nMOIndex]=pMaterial_Fast ;
+			m_pZoneEntityMaterial_Black[nMOIndex]=pMaterial_Black ;
+			m_pZoneEntityMaterial_DfShColour[nMOIndex]=pMaterial_DfShColour ;
+			m_pZoneEntityMaterial_Shadow[nMOIndex]=pMaterial_Shadow ;
+			m_pZoneEntityMaterial_ShadeFront[nMOIndex]=pMaterial_ShadeFront ;
+			m_pZoneEntityMaterial_ShadeBack[nMOIndex]=pMaterial_ShadeBack ;
+			m_pZoneEntityMaterial_DfShPosition[nMOIndex]=pMaterial_DfShPosition ;
+			m_pZoneEntityMaterial_DfShDiffuse[nMOIndex]=pMaterial_DfShDiffuse ;
+			//m_pZoneEntityMaterial_DSNormal[nMOIndex]=pMaterial_DSNormal ;
+			m_pZoneEntityMaterial_DfShFuncTNB[nMOIndex]=pMaterial_DfShFuncTNB ;
+			m_pZoneEntityMaterial_DfShSpecular[nMOIndex]=pMaterial_DfShSpecular ;
+			m_pZoneEntityMaterial_DfShEmissive[nMOIndex]=pMaterial_DfShEmissive ;
+			//m_pZoneEntityMaterial_DfShData[nMOIndex]=pMaterial_DfShData ;
+			m_pZoneEntityMaterial_DfShMix[nMOIndex]=pMaterial_DfShMix ;
+
+
+			//////////////////////////////////////////////////////////////////////////////////////////
+
+			char chNewName[1024] ;
+			for(nPTex=0 ; nPTex<MAX_PROJECTORTEX ; nPTex++)
+			{
+				Ogre::AliasTextureNamePairList AliasList ;
+				
+				// check the necessary texture exists
+				//if(TextureManager::getSingleton().getByName(m_Q3Map->m_chSpotlightTexture[nPTex]).isNull())
+				//	OFBug.MessageInt(666) ;
+				
+				
+				AliasList.insert(AliasTextureNamePairList::value_type("projector_tex", m_Q3Map->m_chSpotlightTexture[nPTex]));
+
+				
+
+				sprintf(chNewName, "%s_%i", chMaterial_Base, 0) ;
+				m_pZoneEntityMaterial_Base[nPTex][nMOIndex]=pMaterial_Base->clone(chNewName) ;
+				// change the texture unit
+				m_pZoneEntityMaterial_Base[nPTex][nMOIndex]->applyTextureAliases(AliasList) ;
+
+				sprintf(chNewName, "%s_%i", chMaterial_Fast, 0) ;
+				m_pZoneEntityMaterial_Fast[nPTex][nMOIndex]=pMaterial_Fast->clone(chNewName) ;
+				// change the texture unit
+				m_pZoneEntityMaterial_Fast[nPTex][nMOIndex]->applyTextureAliases(AliasList) ;
+
+			}
+
+
+
+			//////////////////////////////////////////////////////////////////////////////////////////
+
+
+			// move to the end of this block of materials
+			nTriangle=nEnd ;
+			nMOIndex++ ;
+
+
+			//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+			//
+			// end adding a begin/end block of all triangles in a zone with the same material
+			//
+			//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+
+
+		}// end scanning triangles in a zone
+
+
+
+	}// end looping through all zones
+
+	m_nZoneMOStart[m_Q3Map->m_nMaxZone]=nMOIndex ; // indicates the end pos of all the manual objects 
+
+	CHECKDELETE_ARRAY( pVertIndex, NEW_CHECK_pVertIndex  );  // free the vert index memory we used
+
+	CHECKDELETE_ARRAY( m_pZoneMO, NEW_CHECK_m_pZoneMO  ) ;  // free the manual object memory we used
+	// quick check
+	if(nMOCount!=nMOIndex)
+	{
+		sprintf(chMessage, "ERROR ConstructMap: MOIndex %i does not match MOCount %i", nMOIndex, nMOCount) ;
+		m_pLog->logMessage(chMessage);
+		//CHECKDELETE_ARRAY( m_pZoneMO ) ;
+		CHECKDELETE_ARRAY( m_pZoneMesh, NEW_CHECK_m_pZoneMesh  ) ; 
+		CHECKDELETE_ARRAY( m_pZoneEntity, NEW_CHECK_m_pZoneEntity  ) ; 
+		CHECKDELETE_ARRAY( m_pZoneEntityMaterialType, NEW_CHECK_m_pZoneEntityMaterialType  ) ; 
+
+		for(nPTex=0 ; nPTex<MAX_PROJECTORTEX ; nPTex++)
+		{
+			CHECKDELETE_ARRAY( m_pZoneEntityMaterial_Base[nPTex], NEW_CHECK_m_pZoneEntityMaterial_Base  ) ; 
+			CHECKDELETE_ARRAY( m_pZoneEntityMaterial_Fast[nPTex], NEW_CHECK_m_pZoneEntityMaterial_Fast  ) ; 
+		}
+
+		CHECKDELETE_ARRAY( m_pZoneEntityMaterial_Black, NEW_CHECK_m_pZoneEntityMaterial_Black  ) ; 
+		CHECKDELETE_ARRAY( m_pZoneEntityMaterial_DfShColour, NEW_CHECK_m_pZoneEntityMaterial_DfShColour  ) ; 
+		CHECKDELETE_ARRAY( m_pZoneEntityMaterial_Shadow, NEW_CHECK_m_pZoneEntityMaterial_Shadow  ) ; 
+		CHECKDELETE_ARRAY( m_pZoneEntityMaterial_ShadeFront, NEW_CHECK_m_pZoneEntityMaterial_ShadeFront  ) ; 
+		CHECKDELETE_ARRAY( m_pZoneEntityMaterial_ShadeBack, NEW_CHECK_m_pZoneEntityMaterial_ShadeBack  ) ; 
+		CHECKDELETE_ARRAY( m_pZoneEntityMaterial_DfShPosition, NEW_CHECK_m_pZoneEntityMaterial_DfShPosition  ) ; 
+		CHECKDELETE_ARRAY( m_pZoneEntityMaterial_DfShDiffuse, NEW_CHECK_m_pZoneEntityMaterial_DfShDiffuse  ) ; 
+		//CHECKDELETE_ARRAY( m_pZoneEntityMaterial_DSNormal ) ;
+		CHECKDELETE_ARRAY( m_pZoneEntityMaterial_DfShFuncTNB, NEW_CHECK_m_pZoneEntityMaterial_DfShFuncTNB  ) ; 
+		CHECKDELETE_ARRAY( m_pZoneEntityMaterial_DfShSpecular, NEW_CHECK_m_pZoneEntityMaterial_DfShSpecular  ) ; 
+		CHECKDELETE_ARRAY( m_pZoneEntityMaterial_DfShEmissive, NEW_CHECK_m_pZoneEntityMaterial_DfShEmissive  ) ; 
+		//CHECKDELETE_ARRAY( m_pZoneEntityMaterial_DfShData ) ;
+		CHECKDELETE_ARRAY( m_pZoneEntityMaterial_DfShMix, NEW_CHECK_m_pZoneEntityMaterial_DfShMix  ) ; 
+
+
+		return 0 ;
+	}
+
+
+	return 1 ;
+
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// setup the game entities
+//
+
+int OgreFramework::SetupGameEntities(void)
+{
+	int nEntity=0 ;
+	char chMeshName[1024] ;
+	
+
+	// work out the maximum number of entities we need
+	m_nMaxEntity=10 ;
+	int nHalfMax=m_nMaxEntity/2 ;
+	
+
+
+
+	// create the dynamic memory for the entities
+	m_pEntityInfo=new ENTITYINFO[m_nMaxEntity] ;
+	if(m_pEntityInfo==NULL) 
+		return 0 ;
+	else
+		{m_nNewCount++ ; m_nNewCheck[NEW_CHECK_m_pEntityInfo]++ ; }
+
+	m_pVisibleEntity=new int[m_nMaxEntity] ;
+	if(m_pVisibleEntity==NULL) 
+		return 0 ;
+	else
+		{m_nNewCount++ ; m_nNewCheck[NEW_CHECK_m_pVisibleEntity]++ ; }
+
+	m_pFrustumEntity=new int[m_nMaxEntity] ;
+	if(m_pFrustumEntity==NULL) 
+		return 0 ;
+	else
+		{m_nNewCount++ ; m_nNewCheck[NEW_CHECK_m_pFrustumEntity]++ ; }
+
+
+
+	// setup materials and default data for the entities
+	for(nEntity=0 ; nEntity<m_nMaxEntity ; nEntity++)
+	{
+		//switch(nEntity%3)
+		//{
+		//	case 0: strcpy(chMeshName, "robot.mesh") ; break ;
+		//	case 1: strcpy(chMeshName, "ninja.mesh") ; break ;
+		//	case 2: strcpy(chMeshName, "athene.mesh") ; break ;
+		//}
+
+		strcpy(chMeshName, "robot.mesh") ;
+
+		SetupEntity(nEntity, chMeshName) ;
+	}
+
+
+
+
+	// setup start data for each entity
+	for(nEntity=0 ; nEntity<nHalfMax ; nEntity++)
+	{
+		m_pEntityInfo[nEntity].Postition=Ogre::Vector3(750.0f, 128.0f, -1950.0f-nEntity*40.0f) ;
+		//m_pEntityInfo[nEntity].pMasterNode->setPosition(750.0f, 128.0f, -1950.0f-nEntity*40.0f) ;
+	}
+
+	for(nEntity=nHalfMax ; nEntity<m_nMaxEntity ; nEntity++)
+	{
+		m_pEntityInfo[nEntity].Postition=Ogre::Vector3(590.0f, 128.0f, -320.0f-nEntity*40.0f) ;
+		//m_pEntityInfo[nEntity].pMasterNode->setPosition(590.0f, 128.0f, -320.0f-nEntity*40.0f) ;
+	}
+
+	
+
+	for(nEntity=0 ; nEntity<m_nMaxEntity ; nEntity++)
+	{
+		m_pEntityInfo[nEntity].Active=1 ;
+		m_pVisibleEntity[nEntity]=nEntity ;
+		m_pFrustumEntity[nEntity]=nEntity ;
+	}
+
+
+	m_nMaxVisibleEntity=m_nMaxEntity ;
+	m_nMaxFrustumEntity=m_nMaxEntity ;
+
+	return 1 ;
+
+}
+
+
+int OgreFramework::SetupEntity(int nEntity, char *chMeshName)
+{
+
+	char chEntityName[1024] ;
+	//char chMeshName[1024] ;
+
+
+	int nSubMesh=0 ;
+	int nMaxSubMesh=0 ;
+
+
+
+	Ogre::String MaterialName ;
+
+
+	char chMaterial_Base[1024] ;
+	MaterialPtr pMaterial_Base ; // used for checking if material scripts exist, and stored for the material switching that happens in deferred shading
+	
+	char chMaterial_Fast[1024] ;
+	MaterialPtr pMaterial_Fast ; // faster version of the base, mainly non-coloured shadowning.
+
+	char chMaterial_Black[1024] ;
+	MaterialPtr pMaterial_Black ; // just plain black, or black with alpha testing.
+
+	char chMaterial_DfShColour[1024] ;
+	MaterialPtr pMaterial_DfShColour ; // used for checking if material scripts exist, and stored for the material switching that happens in deferred shading
+	
+
+	char chMaterial_Shadow[1024] ;
+	MaterialPtr pMaterial_Shadow ;
+
+	char chMaterial_ShadeFront[1024] ;
+	MaterialPtr pMaterial_ShadeFront ;
+
+	char chMaterial_ShadeBack[1024] ;
+	MaterialPtr pMaterial_ShadeBack ;
+
+	char chMaterial_DfShPosition[1024] ;
+	MaterialPtr pMaterial_DfShPosition ;
+	
+	char chMaterial_DfShDiffuse[1024] ;
+	MaterialPtr pMaterial_DfShDiffuse ;
+	
+	//char chMaterial_DSNormal[1024] ;
+	//MaterialPtr pMaterial_DSNormal ;
+	
+	char chMaterial_DfShFuncTNB[1024] ;
+	MaterialPtr pMaterial_DfShFuncTNB ;
+
+	char chMaterial_DfShSpecular[1024] ;
+	MaterialPtr pMaterial_DfShSpecular ;
+
+	char chMaterial_DfShEmissive[1024] ;
+	MaterialPtr pMaterial_DfShEmissive ;
+
+	//char chMaterial_DfShData[1024] ;
+	//MaterialPtr pMaterial_DfShData ;
+
+	char chMaterial_DfShMix[1024] ;
+	MaterialPtr pMaterial_DfShMix ;
+
+	char chCompare[1024] ;
+
+	unsigned short src, dest; // for tangent vectors
+
+	char chMasterNodeName[1024] ;
+	char chSubNodeName[1024] ;
+
+	char chMessage[1024] ;
+	int nPTex=0 ;
+
+
+	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+		sprintf(chEntityName, "Entity_%05i", nEntity) ;
+		//strcpy(chMeshName, "robot.mesh") ;
+
+
+		// load the mesh
+
+		m_pEntityInfo[nEntity].pEnt=m_pSceneMgr->createEntity( chEntityName, chMeshName );
+
+		m_pEntityInfo[nEntity].TriangleCount=0 ;
+		
+
+		// create the master scenenode
+		sprintf(chMasterNodeName, "Entity_%05i", nEntity) ;
+		m_pEntityInfo[nEntity].pMasterNode=m_pSceneMgr->getRootSceneNode()->createChildSceneNode(chMasterNodeName) ;
+		m_pEntityInfo[nEntity].pMasterNode->attachObject(m_pEntityInfo[nEntity].pEnt) ;
+
+		nMaxSubMesh=m_pEntityInfo[nEntity].pEnt->getMesh()->getNumSubMeshes() ;
+		if(nMaxSubMesh>MAX_SUBMESH)
+		{
+			sprintf(m_chBug, "MESH ERROR: Mesh %s has %i submeshes, max is %i", chMeshName, m_pEntityInfo[nEntity].MaxSubMesh, MAX_SUBMESH) ;
+			m_pLog->logMessage(m_chBug);
+			nMaxSubMesh=MAX_SUBMESH ;
+		}
+		m_pEntityInfo[nEntity].MaxSubMesh=nMaxSubMesh ;
+
+		sprintf(m_chBug, "Entity %i, Mesh %s, MaxSubMesh %i", nEntity, chMeshName,  m_pEntityInfo[nEntity].MaxSubMesh) ;
+		m_pLog->logMessage(m_chBug);
+
+		// assign all the materials needed for all the submeshes
+		for(nSubMesh=0 ; nSubMesh<nMaxSubMesh ; nSubMesh++)
+		{
+			// we count the triangles just for stats
+			m_pEntityInfo[nEntity].TriangleCount += m_pEntityInfo[nEntity].pEnt->getMesh()->getSubMesh(nSubMesh)->indexData->indexCount/3 ;
+
+
+			MaterialName=m_pEntityInfo[nEntity].pEnt->getMesh()->getSubMesh(nSubMesh)->getMaterialName() ;
+		
+			
+			
+
+			if(MaterialName.size()<512) // make sure name isn't too long before we copy it.
+				strcpy(chMaterial_Base, MaterialName.c_str()) ;
+			else
+			{
+				sprintf(m_chBug, "MESH ERROR: Mesh %s submesh %i material name is too long.", chMeshName, nSubMesh) ;
+				m_pLog->logMessage(m_chBug);
+				m_pLog->logMessage(MaterialName) ;
+				chMaterial_Base[0]='\0' ; // no material
+			}
+
+			sprintf(m_chBug, "Entity %i SubMesh %i, Material %s", nEntity, nSubMesh, chMaterial_Base) ;
+			m_pLog->logMessage(m_chBug);
+
+
+			// if the material is a lamp material we need to specify either an OpenGl or Direct3D version
+			strcpy(chCompare, chMaterial_Base) ;
+			chCompare[12]='\0' ;
+			if(strcmp("lights/lamp_", chCompare)==0) // material is LAMP 
+			{
+				if(m_IsOpenGL)
+					strcat(chMaterial_Base, "_ogl") ;
+				else
+					strcat(chMaterial_Base, "_d3d") ;
+
+			}
+
+			strcpy(chCompare, chMaterial_Base) ;
+			chCompare[17]='\0' ;
+			if(strcmp("lights/lamp2pass_", chCompare)==0) // material is LAMP, 2 pass version 
+			{
+				if(m_IsOpenGL)
+					strcat(chMaterial_Base, "_ogl") ;
+				else
+					strcat(chMaterial_Base, "_d3d") ;
+			}
+			
+			// check that this material script exists, if not set to the default
+			pMaterial_Base=MaterialManager::getSingleton().getByName(chMaterial_Base) ;
+			if(true)//pMaterial_Base.isNull())
+			{
+				strcat(chMaterial_Base, " *** MISSING ***") ;
+//				OFBug.LogAddCR(chMaterial_Base) ;
+				
+				strcpy(chMaterial_Base, "textures/metaltech/OPAQUE_DSCE_TechRadar") ;
+				pMaterial_Base=MaterialManager::getSingleton().getByName(chMaterial_Base) ;
+			}
+
+
+			// setup whether this material and it's entities are transparent or whatever
+			
+			m_pEntityInfo[nEntity].SubMeshMaterialType[nSubMesh]=0 ;// default to nothing
+			
+			if(strstr(chMaterial_Base, "OPAQUE")!=NULL) // material and its entity are OPAQUE
+			{
+				m_pEntityInfo[nEntity].SubMeshMaterialType[nSubMesh]=MAT_OPAQUE ; // note that the derived entity will be opaque
+
+			}
+			else
+			{
+
+				if(strstr(chMaterial_Base, "ALPHAPF")!=NULL) // material and its entity are ALPHAPF
+				{
+					m_pEntityInfo[nEntity].SubMeshMaterialType[nSubMesh]=MAT_OPAQUE ; // note that the derived entity will be opaque
+				}
+				else
+				{
+
+					if(strstr(chMaterial_Base, "GEL")!=NULL) // material and its entity are GEL
+					{
+						m_pEntityInfo[nEntity].SubMeshMaterialType[nSubMesh]=MAT_GEL ; // note that the derived entity will be transparent
+					}
+					else
+					{
+
+						strcpy(chCompare, chMaterial_Base) ;
+						chCompare[12]='\0' ;
+						if(strcmp("lights/lamp_", chCompare)==0) // material and its entity are LAMP (spotlights don't ever make triangles, only lamps)
+						{
+							m_pEntityInfo[nEntity].SubMeshMaterialType[nSubMesh]=MAT_LAMP ; // note that the derived entity will be lamp
+						}
+						else
+						{
+							strcpy(chCompare, chMaterial_Base) ;
+							chCompare[17]='\0' ;
+							if(strcmp("lights/lamp2pass_", chCompare)==0) // material and its entity are LAMP (spotlights don't ever make triangles, only lamps)
+							{
+								m_pEntityInfo[nEntity].SubMeshMaterialType[nSubMesh]=MAT_LAMP ; // note that the derived entity will be lamp
+							}
+							else
+							{
+
+								if(strstr(chMaterial_Base, "GLOW_")!=NULL) // material and its entity are GLOW
+								{
+									m_pEntityInfo[nEntity].SubMeshMaterialType[nSubMesh]=MAT_GLOW ; // note that the derived entity will be glow
+								}
+							}	
+						}					
+					}
+				}
+			}
+
+
+
+			
+
+			///////////////////////////////////////////////////////////////////////////////////////////////////////
+			//
+			// Setup the different material names and pointers needed for various passes
+			//
+			// Lamps need only one material, pMaterial_Base, which is already assigned above, so it skips all this.
+			//
+			// Glow needs two materials, pMaterial_Base plus pMaterial_DfShColour for the coloured gel deferred pass.
+			// However it's better to make a third, and duplicate the pMaterial_Base to pMaterial_Emissive, since
+			// when glow gets rendered on the Emmisive pass it would be odd to switch to the base material instead
+			// of the emmisive.
+			//
+			// Other objects need multiple materials for various different deferred shading and FX passes
+			
+			if((m_pEntityInfo[nEntity].SubMeshMaterialType[nSubMesh]!=MAT_LAMP) && (m_pEntityInfo[nEntity].SubMeshMaterialType[nSubMesh]!=MAT_GLOW))
+			{
+
+				// material for fast shading
+				strcpy(chMaterial_Fast, chMaterial_Base) ;
+				strcat(chMaterial_Fast, "_Fast") ;
+				pMaterial_Fast=MaterialManager::getSingleton().getByName(chMaterial_Fast) ;
+				if(pMaterial_Fast.isNull())
+				{
+					sprintf(chMessage, "Missing material, using default: %s", chMaterial_Fast) ;
+					m_pLog->logMessage(chMessage);
+
+					strcpy(chMaterial_Fast, "Fast") ;
+					pMaterial_Fast=MaterialManager::getSingleton().getByName(chMaterial_Fast) ;
+				}
+
+				// material for black shading
+				strcpy(chMaterial_Black, chMaterial_Base) ;
+				strcat(chMaterial_Black, "_Black") ;
+				pMaterial_Black=MaterialManager::getSingleton().getByName(chMaterial_Black) ;
+				if(pMaterial_Black.isNull())
+				{
+					// only issue a warning if this is an alpha testing material, others don't need specialized Black materials
+					if(strstr(chMaterial_Base, "ALPHAPF")!=NULL)
+					{
+						sprintf(chMessage, "Missing material, using default: %s", chMaterial_Black) ;
+						m_pLog->logMessage(chMessage);
+					}
+
+					strcpy(chMaterial_Black, "Black") ;
+					pMaterial_Black=MaterialManager::getSingleton().getByName(chMaterial_Black) ;
+				}
+				
+				// material for shadow casting
+				strcpy(chMaterial_Shadow, chMaterial_Base) ;
+				strcat(chMaterial_Shadow, "_Shadow") ;
+				pMaterial_Shadow=MaterialManager::getSingleton().getByName(chMaterial_Shadow) ;
+				if(pMaterial_Shadow.isNull())
+				{
+					strcpy(chMaterial_Shadow, "OPAQUE_Shadow") ;
+					pMaterial_Shadow=MaterialManager::getSingleton().getByName(chMaterial_Shadow) ;
+				}
+
+				
+				
+				// material for shadow casting
+				strcpy(chMaterial_ShadeFront, chMaterial_Base) ;
+				strcat(chMaterial_ShadeFront, "_ShadeFront") ;
+				pMaterial_ShadeFront=MaterialManager::getSingleton().getByName(chMaterial_ShadeFront) ;
+				if(pMaterial_ShadeFront.isNull())
+				{
+					 // standard opaque ShadeFront.  
+					// Transparent surfaces MUST have their own _ShadeFront material or they will not colour light and can interfere with other trans surfaces.
+					strcpy(chMaterial_ShadeFront, "OPAQUE_ShadeFront") ;
+
+					pMaterial_ShadeFront=MaterialManager::getSingleton().getByName(chMaterial_ShadeFront) ;
+				}
+
+
+				// material for shadow casting
+				strcpy(chMaterial_ShadeBack, chMaterial_Base) ;
+				strcat(chMaterial_ShadeBack, "_ShadeBack") ;
+				pMaterial_ShadeBack=MaterialManager::getSingleton().getByName(chMaterial_ShadeBack) ;
+				if(pMaterial_ShadeBack.isNull())
+				{
+					 // standard opaque ShadeBack.  
+					// Transparent surfaces MUST have their own _ShadeBack material or they will not colour light and can interfere with other trans surfaces.
+					strcpy(chMaterial_ShadeBack, "OPAQUE_ShadeBack") ;
+
+					pMaterial_ShadeBack=MaterialManager::getSingleton().getByName(chMaterial_ShadeBack) ;
+				}
+
+				// material for deferred shading
+				strcpy(chMaterial_DfShColour, chMaterial_Base) ;
+				strcat(chMaterial_DfShColour, "_DfShColour") ;
+				pMaterial_DfShColour=MaterialManager::getSingleton().getByName(chMaterial_DfShColour) ;
+				if(pMaterial_DfShColour.isNull())
+				{
+					sprintf(chMessage, "Missing material, using default: %s", chMaterial_DfShColour) ;
+					m_pLog->logMessage(chMessage);
+
+					strcpy(chMaterial_DfShColour, "DfShColour") ;
+					pMaterial_DfShColour=MaterialManager::getSingleton().getByName(chMaterial_DfShColour) ;
+				}
+				
+
+				// material for deferred shading
+				strcpy(chMaterial_DfShPosition, chMaterial_Base) ;
+				strcat(chMaterial_DfShPosition, "_DfShPosition") ;
+				pMaterial_DfShPosition=MaterialManager::getSingleton().getByName(chMaterial_DfShPosition) ;
+				if(pMaterial_DfShPosition.isNull())
+				{
+					// only issue a warning if this is an alpha testing material, others don't need specialized DfShPosition materials
+					if(strstr(chMaterial_Base, "ALPHAPF")!=NULL)
+					{
+						sprintf(chMessage, "Missing material, using default: %s", chMaterial_DfShPosition) ;
+						m_pLog->logMessage(chMessage);
+					}
+
+					strcpy(chMaterial_DfShPosition, "DfShPosition") ;
+					pMaterial_DfShPosition=MaterialManager::getSingleton().getByName(chMaterial_DfShPosition) ;
+				}
+				
+				// material for deferred shading
+				strcpy(chMaterial_DfShDiffuse, chMaterial_Base) ;
+				strcat(chMaterial_DfShDiffuse, "_DfShDiffuse") ;
+				pMaterial_DfShDiffuse=MaterialManager::getSingleton().getByName(chMaterial_DfShDiffuse) ;
+				if(pMaterial_DfShDiffuse.isNull())
+				{
+					sprintf(chMessage, "Missing material, using default: %s", chMaterial_DfShDiffuse) ;
+					m_pLog->logMessage(chMessage);
+
+					strcpy(chMaterial_DfShDiffuse, "DfShDiffuse") ;
+					pMaterial_DfShDiffuse=MaterialManager::getSingleton().getByName(chMaterial_DfShDiffuse) ;
+				}
+
+				/*
+				// material for deferred shading // UNUSED
+				strcpy(chMaterial_DSNormal, chMaterial_Base) ;
+				strcat(chMaterial_DSNormal, "_DSNormal") ;
+				pMaterial_DSNormal=MaterialManager::getSingleton().getByName(chMaterial_DSNormal) ;
+				if(pMaterial_DSNormal.isNull())
+				{
+					sprintf(chMessage, "Missing material, using default: %s", chMaterial_DSNormal) ;
+					m_pLog->logMessage(chMessage);
+
+					strcpy(chMaterial_DSNormal, "DSNormal") ;
+					pMaterial_DSNormal=MaterialManager::getSingleton().getByName(chMaterial_DSNormal) ;
+				}
+				*/
+
+				// material for deferred shading
+				strcpy(chMaterial_DfShFuncTNB, chMaterial_Base) ;
+				strcat(chMaterial_DfShFuncTNB, "_DfShFuncTNB") ;
+				pMaterial_DfShFuncTNB=MaterialManager::getSingleton().getByName(chMaterial_DfShFuncTNB) ;
+				if(pMaterial_DfShFuncTNB.isNull())
+				{
+					sprintf(chMessage, "Missing material, using default: %s", chMaterial_DfShFuncTNB) ;
+					m_pLog->logMessage(chMessage);
+
+					strcpy(chMaterial_DfShFuncTNB, "DfShFuncTNB") ;
+					pMaterial_DfShFuncTNB=MaterialManager::getSingleton().getByName(chMaterial_DfShFuncTNB) ;
+				}
+
+				// material for deferred shading
+				strcpy(chMaterial_DfShSpecular, chMaterial_Base) ;
+				strcat(chMaterial_DfShSpecular, "_DfShSpecular") ;
+				pMaterial_DfShSpecular=MaterialManager::getSingleton().getByName(chMaterial_DfShSpecular) ;
+				if(pMaterial_DfShSpecular.isNull())
+				{
+					sprintf(chMessage, "Missing material, using default: %s", chMaterial_DfShSpecular) ;
+					m_pLog->logMessage(chMessage);
+
+					strcpy(chMaterial_DfShSpecular, "DfShSpecular") ;
+					pMaterial_DfShSpecular=MaterialManager::getSingleton().getByName(chMaterial_DfShSpecular) ;
+				}
+
+				// material for deferred shading
+				strcpy(chMaterial_DfShEmissive, chMaterial_Base) ;
+				strcat(chMaterial_DfShEmissive, "_DfShEmissive") ;
+				pMaterial_DfShEmissive=MaterialManager::getSingleton().getByName(chMaterial_DfShEmissive) ;
+				if(pMaterial_DfShEmissive.isNull())
+				{
+					sprintf(chMessage, "Missing material, using default: %s", chMaterial_DfShEmissive) ;
+					m_pLog->logMessage(chMessage);
+
+					strcpy(chMaterial_DfShEmissive, "DfShEmissive") ;
+					pMaterial_DfShEmissive=MaterialManager::getSingleton().getByName(chMaterial_DfShEmissive) ;
+				}
+
+
+				/*
+				// material for deferred shading
+				strcpy(chMaterial_DfShData, chMaterial_Base) ;
+				strcat(chMaterial_DfShData, "_DfShData") ;
+				pMaterial_DfShData=MaterialManager::getSingleton().getByName(chMaterial_DfShData) ;
+				if(pMaterial_DfShData.isNull())
+				{
+					sprintf(chMessage, "Missing material, using default: %s", chMaterial_DfShData) ;
+					m_pLog->logMessage(chMessage);
+
+					strcpy(chMaterial_DfShData, "DfShData") ; 
+					pMaterial_DfShData=MaterialManager::getSingleton().getByName(chMaterial_DfShData) ;
+				}
+				*/
+
+				// material for deferred shading
+				strcpy(chMaterial_DfShMix, chMaterial_Base) ;
+				strcat(chMaterial_DfShMix, "_DfShMix") ;
+				pMaterial_DfShMix=MaterialManager::getSingleton().getByName(chMaterial_DfShMix) ;
+				if(pMaterial_DfShMix.isNull())
+				{
+					// Pretty much all materials just use the default mix material,
+					// so don't issue warnings if there isn't a specialized version
+					//sprintf(chMessage, "Missing material, using default: %s", chMaterial_DfShMix) ;
+					//m_pLog->logMessage(chMessage);
+
+					strcpy(chMaterial_DfShMix, "DfShMix") ;
+					pMaterial_DfShMix=MaterialManager::getSingleton().getByName(chMaterial_DfShMix) ;
+				}
+				
+			
+
+
+
+			}// end if not MAT_LAMP or MAT_GLOW
+			else
+				if(m_pEntityInfo[nEntity].SubMeshMaterialType[nSubMesh]==MAT_GLOW) // the two materials that glow needs
+				{
+
+					// when I get around to allowing user defined GLOW materials as well as the automatic ones,
+					// this'll be the place to add it.  
+					// For now, just the automatically generated lamp/light stuff has a glow.
+					
+					// if the base material was "GLOW_lamp"	
+					strcpy(chCompare, chMaterial_Base) ;
+					chCompare[strlen("GLOW_lamp")]='\0' ;
+					if(strcmp("GLOW_lamp", chCompare)==0) 
+					{
+						strcpy(chMaterial_DfShColour, "GLOW_lampcol") ;
+						pMaterial_DfShColour=MaterialManager::getSingleton().getByName(chMaterial_DfShColour) ;
+
+						strcpy(chMaterial_DfShEmissive, "GLOW_lamp") ;
+						pMaterial_DfShEmissive=MaterialManager::getSingleton().getByName(chMaterial_DfShEmissive) ;
+					}
+					else
+					{
+
+						// if the base material was "GLOW_light"
+						strcpy(chCompare, chMaterial_Base) ;
+						chCompare[strlen("GLOW_light")]='\0' ;
+						if(strcmp("GLOW_light", chCompare)==0) 
+						{
+							strcpy(chMaterial_DfShColour, "GLOW_lightcol") ;
+							pMaterial_DfShColour=MaterialManager::getSingleton().getByName(chMaterial_DfShColour) ;
+
+							strcpy(chMaterial_DfShEmissive, "GLOW_light") ;
+							pMaterial_DfShEmissive=MaterialManager::getSingleton().getByName(chMaterial_DfShEmissive) ;
+						}
+
+					}
+				}
+
+
+
+
+			// if the entity is a lamp or glow, set its material because they never get changed
+			//if((m_pZoneEntityMaterialType[nMOIndex]==MAT_LAMP) || (m_pZoneEntityMaterialType[nMOIndex]==MAT_GLOW)) 
+			//	m_pZoneEntity[nMOIndex]->setMaterial( pMaterial_Base) ;
+
+
+			// store the pointers to the materials of this entity
+			//m_pZoneEntityMaterial_Base[nMOIndex]=pMaterial_Base ;
+			//m_pZoneEntityMaterial_Fast[nMOIndex]=pMaterial_Fast ;
+			m_pEntityInfo[nEntity].Material_Black[nSubMesh]=pMaterial_Black ;
+			m_pEntityInfo[nEntity].Material_DfShColour[nSubMesh]=pMaterial_DfShColour ;
+			m_pEntityInfo[nEntity].Material_Shadow[nSubMesh]=pMaterial_Shadow ;
+			m_pEntityInfo[nEntity].Material_ShadeFront[nSubMesh]=pMaterial_ShadeFront ;
+			m_pEntityInfo[nEntity].Material_ShadeBack[nSubMesh]=pMaterial_ShadeBack ;
+			m_pEntityInfo[nEntity].Material_DfShPosition[nSubMesh]=pMaterial_DfShPosition ;
+			m_pEntityInfo[nEntity].Material_DfShDiffuse[nSubMesh]=pMaterial_DfShDiffuse ;
+			//m_pEntityInfo[nEntity].Material_DSNormal[nSubMesh]=pMaterial_DSNormal ;
+			m_pEntityInfo[nEntity].Material_DfShFuncTNB[nSubMesh]=pMaterial_DfShFuncTNB ;
+			m_pEntityInfo[nEntity].Material_DfShSpecular[nSubMesh]=pMaterial_DfShSpecular ;
+			m_pEntityInfo[nEntity].Material_DfShEmissive[nSubMesh]=pMaterial_DfShEmissive ;
+			//m_pEntityInfo[nEntity].Material_DfShData[nSubMesh]=pMaterial_DfShData ;
+			m_pEntityInfo[nEntity].Material_DfShMix[nSubMesh]=pMaterial_DfShMix ;
+
+			
+			//////////////////////////////////////////////////////////////////////////////////////////
+
+			char chNewName[1024] ;
+			for(nPTex=0 ; nPTex<MAX_PROJECTORTEX ; nPTex++)
+			{
+				Ogre::AliasTextureNamePairList AliasList ;
+				
+				// check the necessary texture exists
+				//if(TextureManager::getSingleton().getByName(m_Q3Map->m_chSpotlightTexture[nPTex]).isNull())
+				//	OFBug.MessageInt(666) ;
+				
+				AliasList.insert(AliasTextureNamePairList::value_type("projector_tex", m_Q3Map->m_chSpotlightTexture[nPTex]));
+
+				sprintf(chNewName, "%s_%i", chMaterial_Base, 0) ;
+				m_pEntityInfo[nEntity].Material_Base[nPTex][nSubMesh]=pMaterial_Base->clone(chNewName) ;
+				// change the texture unit
+				m_pEntityInfo[nEntity].Material_Base[nPTex][nSubMesh]->applyTextureAliases(AliasList) ;
+
+				sprintf(chNewName, "%s_%i", chMaterial_Fast, 0) ;
+				m_pEntityInfo[nEntity].Material_Fast[nPTex][nSubMesh]=pMaterial_Fast->clone(chNewName) ;
+				// change the texture unit
+				m_pEntityInfo[nEntity].Material_Fast[nPTex][nSubMesh]->applyTextureAliases(AliasList) ;
+
+			}
+
+			// create the sub scenenodes
+			//sprintf(chSubNodeName, "Entity_%05i_%05i", nEntity, nSubMesh) ;
+			//m_pEntityInfo[nEntity].pSubNode[nSubMesh]=m_pEntityInfo[nEntity].pMasterNode->createChildSceneNode(chSubNodeName) ;
+
+
+			sprintf(m_chBug, "Node %s, Subnode %s, Type %i", chMasterNodeName, chSubNodeName, m_pEntityInfo[nEntity].SubMeshMaterialType[nSubMesh]) ;
+			m_pLog->logMessage(m_chBug);
+		
+		}// end for submesh
+
+		// build tangent vectors for the mesh
+		m_pEntityInfo[nEntity].pEnt->getMesh()->suggestTangentVectorBuildParams(VES_TANGENT, src, dest) ;
+		m_pEntityInfo[nEntity].pEnt->getMesh()->buildTangentVectors(VES_TANGENT, src, dest);
+
+
+	// default to not active, not visible and in no zones
+	m_pEntityInfo[nEntity].Active=0 ;
+	m_pEntityInfo[nEntity].Visible=0 ;
+	m_pEntityInfo[nEntity].Zone[INDEX_ENTITYZONECOUNT]=0 ;
+
+	// add some info about the bounding box
+	Ogre::AxisAlignedBox aabb ;
+	Ogre::Vector3 Minimum ;
+	Ogre::Vector3 Maximum ;
+
+	aabb=m_pEntityInfo[nEntity].pEnt->getBoundingBox() ;
+	Minimum=aabb.getMinimum() ;
+	Maximum=aabb.getMaximum() ;
+
+	m_pEntityInfo[nEntity].AABB=aabb ;
+	m_pEntityInfo[nEntity].Centre.x=(Maximum.x-Minimum.x)/2.0f ;
+	m_pEntityInfo[nEntity].Centre.y=(Maximum.y-Minimum.y)/2.0f ;
+	m_pEntityInfo[nEntity].Centre.z=(Maximum.z-Minimum.z)/2.0f ;
+
+	m_pEntityInfo[nEntity].AABBMin[0]=Minimum.x ;
+	m_pEntityInfo[nEntity].AABBMin[1]=Minimum.y ;
+	m_pEntityInfo[nEntity].AABBMin[2]=Minimum.z ;
+	m_pEntityInfo[nEntity].AABBMax[0]=Maximum.x ;
+	m_pEntityInfo[nEntity].AABBMax[1]=Maximum.y ;
+	m_pEntityInfo[nEntity].AABBMax[2]=Maximum.z ;
+
+
+	m_pEntityInfo[nEntity].Postition=Ogre::Vector3(0.0f, 0.0f, 0.0f) ;
+
+
+
+	return 1 ;
+}
+
+
+// Hmm... wanted to control some CG compiling, mainly to turn shadow filtering on or off, but I couln't get this to work.
+// I ended up just manually creating filtered and unfiltered version of the materials.
+/*
+int OgreFramework::ApplyCGDefines(void)
+{
+	int nZoneMO=0 ;
+	int nMaxZoneMO=m_nZoneMOStart[m_Q3Map->m_nMaxZone] ;
+	int nPTex=0 ;
+	
+	Ogre::Pass *CGPass ;
+	int nPass=0 ;
+	int nMaxPass=0 ;
+
+	for(nZoneMO=0 ; nZoneMO<nMaxZoneMO ; nZoneMO++)
+	{
+		//OFBug.MessageInt(nZoneMO, nMaxZoneMO, m_pZoneEntityMaterialType[nZoneMO]) ;
+		
+		if(m_pZoneEntityMaterialType[nZoneMO]==MAT_OPAQUE)
+			for(nPTex=0 ; nPTex<MAX_PROJECTORTEX ; nPTex++)
+			{
+				//m_pZoneEntityMaterial_Base[nPTex][nZoneMO]->getTechnique(0)->getPass(1)->getFragmentProgram()->setParameter("compile_arguments", "-DFILTER_ON 1") ;
+				nMaxPass=m_pZoneEntityMaterial_Base[nPTex][nZoneMO]->getTechnique(0)->getNumPasses() ;
+				
+				for(nPass=0 ; nPass<nMaxPass ; nPass++)
+				{
+					m_pZoneEntityMaterial_Base[nPTex][nZoneMO]->getTechnique(0)->getPass(nPass)->getFragmentProgram()->setParameter("compile_arguments", "-DFILTER_ON=1") ;
+					//m_pZoneEntityMaterial_Base[nPTex][nZoneMO]->getTechnique(0)->getPass(nPass)->getFragmentProgram()->reload();
+				}	
+				//CGPass=m_pZoneEntityMaterial_Base[nPTex][nZoneMO]->getTechnique(0)->getPass(0) ;
+			}
+	}
+	
+
+	//m_pZoneEntityMaterial_Base[0][0]->getTechnique(0)->getPass(1)->getFragmentProgram()->setParameter("compile_arguments", "-DFILTER_ON 1") ;
+		
+
+	return 1 ;
+
+
+}
+*/
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// create light manual objects, just for debugging/testing.
+// This is an array of manualobject AABBs that show the light culling box (double sided) for each light
+
+int OgreFramework::AddLightCullingBoxes()
+{
+	int nLt=0 ;
+	// for calculating bounds of mesh
+	float flMinX=0.0f ;
+	float flMinY=0.0f ;
+	float flMinZ=0.0f ;
+	float flMaxX=0.0f ;
+	float flMaxY=0.0f ;
+	float flMaxZ=0.0f ;
+	int nMaxLight=m_Q3Map->m_nLightMax ;
+
+	char chSceneNodeName[1024] ;
+	char chManualName[1024] ;
+
+	m_pLightMO = new Ogre::ManualObject*[nMaxLight] ;
+	if(m_pLightMO==NULL) return 0 ; // fail, out of mem
+
+	for(nLt=0 ; nLt<nMaxLight ; nLt++)
+	{
+		
+		sprintf(chManualName, "LightMO_%05i", nLt) ;
+		m_pLightMO[nLt]=m_pSceneMgr->createManualObject(chManualName) ;
+
+		// start defining the manualObject
+		m_pLightMO[nLt]->begin("lighttest", RenderOperation::OT_TRIANGLE_LIST) ;
+
+		
+		flMinX=m_Q3Map->m_pLight[nLt].Min[0] ;
+		flMinY=m_Q3Map->m_pLight[nLt].Min[1] ;
+		flMinZ=m_Q3Map->m_pLight[nLt].Min[2] ;
+
+		flMaxX=m_Q3Map->m_pLight[nLt].Max[0] ;
+		flMaxY=m_Q3Map->m_pLight[nLt].Max[1] ;
+		flMaxZ=m_Q3Map->m_pLight[nLt].Max[2] ;
+		
+		
+		//////////////////////////////////////////////////////
+		// back face
+		m_pLightMO[nLt]->position(flMinX, flMaxY, flMinZ) ;
+		m_pLightMO[nLt]->normal(0.0, 0.0, -1) ;
+		m_pLightMO[nLt]->textureCoord(1.0, 0.0) ;
+
+		m_pLightMO[nLt]->position(flMaxX, flMaxY, flMinZ) ;
+		m_pLightMO[nLt]->normal(0.0, 0.0, -1) ;
+		m_pLightMO[nLt]->textureCoord(0.0, 0.0) ;
+
+		m_pLightMO[nLt]->position(flMaxX, flMinY, flMinZ) ;
+		m_pLightMO[nLt]->normal(0.0, 0.0, -1) ;
+		m_pLightMO[nLt]->textureCoord(0.0, 1.0) ;
+
+		m_pLightMO[nLt]->position(flMinX, flMinY, flMinZ) ;
+		m_pLightMO[nLt]->normal(0.0, 0.0, -1) ;
+		m_pLightMO[nLt]->textureCoord(1.0, 1.0) ;
+
+		m_pLightMO[nLt]->quad(0, 1, 2, 3) ;
+		m_pLightMO[nLt]->quad(3, 2, 1, 0) ;
+
+		//////////////////////////////////////////////////////
+		// front face
+		m_pLightMO[nLt]->position(flMinX, flMaxY, flMaxZ) ;
+		m_pLightMO[nLt]->normal(0.0, 0.0, 1) ;
+		m_pLightMO[nLt]->textureCoord(0.0, 0.0) ;
+
+		m_pLightMO[nLt]->position(flMaxX, flMaxY, flMaxZ) ;
+		m_pLightMO[nLt]->normal(0.0, 0.0, 1) ;
+		m_pLightMO[nLt]->textureCoord(1.0, 0.0) ;
+
+		m_pLightMO[nLt]->position(flMaxX, flMinY, flMaxZ) ;
+		m_pLightMO[nLt]->normal(0.0, 0.0, 1) ;
+		m_pLightMO[nLt]->textureCoord(1.0, 1.0) ;
+
+		m_pLightMO[nLt]->position(flMinX, flMinY, flMaxZ) ;
+		m_pLightMO[nLt]->normal(0.0, 0.0, 1) ;
+		m_pLightMO[nLt]->textureCoord(0.0, 1.0) ;
+
+		m_pLightMO[nLt]->quad(7, 6, 5, 4) ;
+		m_pLightMO[nLt]->quad(4, 5, 6, 7) ;
+
+		//////////////////////////////////////////////////////
+		// left face
+		m_pLightMO[nLt]->position(flMinX, flMaxY, flMaxZ) ;
+		m_pLightMO[nLt]->normal(0.0, 0.0, -1) ;
+		m_pLightMO[nLt]->textureCoord(1.0, 0.0) ;
+
+		m_pLightMO[nLt]->position(flMinX, flMaxY, flMinZ) ;
+		m_pLightMO[nLt]->normal(0.0, 0.0, -1) ;
+		m_pLightMO[nLt]->textureCoord(0.0, 0.0) ;
+
+		m_pLightMO[nLt]->position(flMinX, flMinY, flMinZ) ;
+		m_pLightMO[nLt]->normal(0.0, 0.0, -1) ;
+		m_pLightMO[nLt]->textureCoord(0.0, 1.0) ;
+
+		m_pLightMO[nLt]->position(flMinX, flMinY, flMaxZ) ;
+		m_pLightMO[nLt]->normal(0.0, 0.0, -1) ;
+		m_pLightMO[nLt]->textureCoord(1.0, 1.0) ;
+
+		m_pLightMO[nLt]->quad(8, 9, 10, 11) ;
+		m_pLightMO[nLt]->quad(11, 10, 9, 8) ;
+
+		
+		//////////////////////////////////////////////////////
+		// right face
+		m_pLightMO[nLt]->position(flMaxX, flMaxY, flMaxZ) ;
+		m_pLightMO[nLt]->normal(0.0, 0.0, 1) ;
+		m_pLightMO[nLt]->textureCoord(0.0, 0.0) ;
+
+		m_pLightMO[nLt]->position(flMaxX, flMaxY, flMinZ) ;
+		m_pLightMO[nLt]->normal(0.0, 0.0, 1) ;
+		m_pLightMO[nLt]->textureCoord(1.0, 0.0) ;
+
+		m_pLightMO[nLt]->position(flMaxX, flMinY, flMinZ) ;
+		m_pLightMO[nLt]->normal(0.0, 0.0, 1) ;
+		m_pLightMO[nLt]->textureCoord(1.0, 1.0) ;
+
+		m_pLightMO[nLt]->position(flMaxX, flMinY, flMaxZ) ;
+		m_pLightMO[nLt]->normal(0.0, 0.0, 1) ;
+		m_pLightMO[nLt]->textureCoord(0.0, 1.0) ;
+
+		m_pLightMO[nLt]->quad(15, 14, 13, 12) ;
+		m_pLightMO[nLt]->quad(12, 13, 14, 15) ;
+
+		//////////////////////////////////////////////////////
+		// top face
+		m_pLightMO[nLt]->position(flMinX, flMaxY, flMaxZ) ;
+		m_pLightMO[nLt]->normal(0.0, 0.0, -1) ;
+		m_pLightMO[nLt]->textureCoord(0.0, 1.0) ;
+
+		m_pLightMO[nLt]->position(flMaxX, flMaxY, flMaxZ) ;
+		m_pLightMO[nLt]->normal(0.0, 0.0, -1) ;
+		m_pLightMO[nLt]->textureCoord(1.0, 1.0) ;
+
+		m_pLightMO[nLt]->position(flMaxX, flMaxY, flMinZ) ;
+		m_pLightMO[nLt]->normal(0.0, 0.0, -1) ;
+		m_pLightMO[nLt]->textureCoord(1.0, 0.0) ;
+
+		m_pLightMO[nLt]->position(flMinX, flMaxY, flMinZ) ;
+		m_pLightMO[nLt]->normal(0.0, 0.0, -1) ;
+		m_pLightMO[nLt]->textureCoord(0.0, 0.0) ;
+
+		m_pLightMO[nLt]->quad(16, 17, 18, 19) ;
+		m_pLightMO[nLt]->quad(19, 18, 17, 16) ;
+
+		//////////////////////////////////////////////////////
+		// bottom face
+		m_pLightMO[nLt]->position(flMinX, flMinY, flMaxZ) ;
+		m_pLightMO[nLt]->normal(0.0, 0.0, -1) ;
+		m_pLightMO[nLt]->textureCoord(0.0, 0.0) ;
+
+		m_pLightMO[nLt]->position(flMaxX, flMinY, flMaxZ) ;
+		m_pLightMO[nLt]->normal(0.0, 0.0, -1) ;
+		m_pLightMO[nLt]->textureCoord(1.0, 0.0) ;
+
+		m_pLightMO[nLt]->position(flMaxX, flMinY, flMinZ) ;
+		m_pLightMO[nLt]->normal(0.0, 0.0, -1) ;
+		m_pLightMO[nLt]->textureCoord(1.0, 1.0) ;
+
+		m_pLightMO[nLt]->position(flMinX, flMinY, flMinZ) ;
+		m_pLightMO[nLt]->normal(0.0, 0.0, -1) ;
+		m_pLightMO[nLt]->textureCoord(0.0, 1.0) ;
+
+		m_pLightMO[nLt]->quad(23, 22, 21, 20) ;
+		m_pLightMO[nLt]->quad(20, 21, 22, 23) ;
+		//////////////////////////////////////////////////////
+
+		m_pLightMO[nLt]->end() ;
+
+
+
+		sprintf(chSceneNodeName, "LSN%05i", nLt) ;
+		m_pSceneMgr->getRootSceneNode()->createChildSceneNode(chSceneNodeName)->attachObject(m_pLightMO[nLt]) ;
+
+		
+	}
+
+	return 1 ;
+}
+
+int OgreFramework::AddPortalBoxes()
+{
+	int nPt=0 ;
+	// for calculating bounds of mesh
+	float flMinX=0.0f ;
+	float flMinY=0.0f ;
+	float flMinZ=0.0f ;
+	float flMaxX=0.0f ;
+	float flMaxY=0.0f ;
+	float flMaxZ=0.0f ;
+	int nMaxPortal=m_Q3Map->m_iNumPortals ;
+
+	char chSceneNodeName[1024] ;
+	char chManualName[1024] ;
+
+	m_pPortalMO = new Ogre::ManualObject*[nMaxPortal] ;
+	if(m_pPortalMO==NULL) return 0 ; // fail, out of mem
+
+	m_pPortalNode = new Ogre::Node*[nMaxPortal] ;
+	if(m_pPortalNode==NULL) 
+	{
+		//CHECKDELETE_ARRAY( m_pPortalMO ) ;
+		return 0 ; // fail, out of mem
+	}
+
+	for(nPt=0 ; nPt<nMaxPortal ; nPt++)
+	{
+		
+		sprintf(chManualName, "PortalMO_%05i", nPt) ;
+		m_pPortalMO[nPt]=m_pSceneMgr->createManualObject(chManualName) ;
+
+		// start defining the manualObject
+		m_pPortalMO[nPt]->begin("white", RenderOperation::OT_TRIANGLE_LIST) ;
+
+		flMinX=m_Q3Map->m_pPortals[nPt].Min[0] ;
+		flMinY=m_Q3Map->m_pPortals[nPt].Min[1] ;
+		flMinZ=m_Q3Map->m_pPortals[nPt].Min[2] ;
+
+		flMaxX=m_Q3Map->m_pPortals[nPt].Max[0] ;
+		flMaxY=m_Q3Map->m_pPortals[nPt].Max[1] ;
+		flMaxZ=m_Q3Map->m_pPortals[nPt].Max[2] ;
+		
+		
+		//////////////////////////////////////////////////////
+		// back face
+		m_pPortalMO[nPt]->position(flMinX, flMaxY, flMinZ) ;
+		m_pPortalMO[nPt]->normal(0.0, 0.0, -1) ;
+		m_pPortalMO[nPt]->textureCoord(1.0, 0.0) ;
+
+		m_pPortalMO[nPt]->position(flMaxX, flMaxY, flMinZ) ;
+		m_pPortalMO[nPt]->normal(0.0, 0.0, -1) ;
+		m_pPortalMO[nPt]->textureCoord(0.0, 0.0) ;
+
+		m_pPortalMO[nPt]->position(flMaxX, flMinY, flMinZ) ;
+		m_pPortalMO[nPt]->normal(0.0, 0.0, -1) ;
+		m_pPortalMO[nPt]->textureCoord(0.0, 1.0) ;
+
+		m_pPortalMO[nPt]->position(flMinX, flMinY, flMinZ) ;
+		m_pPortalMO[nPt]->normal(0.0, 0.0, -1) ;
+		m_pPortalMO[nPt]->textureCoord(1.0, 1.0) ;
+
+		m_pPortalMO[nPt]->quad(0, 1, 2, 3) ;
+		m_pPortalMO[nPt]->quad(3, 2, 1, 0) ;
+
+		//////////////////////////////////////////////////////
+		// front face
+		m_pPortalMO[nPt]->position(flMinX, flMaxY, flMaxZ) ;
+		m_pPortalMO[nPt]->normal(0.0, 0.0, 1) ;
+		m_pPortalMO[nPt]->textureCoord(0.0, 0.0) ;
+
+		m_pPortalMO[nPt]->position(flMaxX, flMaxY, flMaxZ) ;
+		m_pPortalMO[nPt]->normal(0.0, 0.0, 1) ;
+		m_pPortalMO[nPt]->textureCoord(1.0, 0.0) ;
+
+		m_pPortalMO[nPt]->position(flMaxX, flMinY, flMaxZ) ;
+		m_pPortalMO[nPt]->normal(0.0, 0.0, 1) ;
+		m_pPortalMO[nPt]->textureCoord(1.0, 1.0) ;
+
+		m_pPortalMO[nPt]->position(flMinX, flMinY, flMaxZ) ;
+		m_pPortalMO[nPt]->normal(0.0, 0.0, 1) ;
+		m_pPortalMO[nPt]->textureCoord(0.0, 1.0) ;
+
+		m_pPortalMO[nPt]->quad(7, 6, 5, 4) ;
+		m_pPortalMO[nPt]->quad(4, 5, 6, 7) ;
+
+		//////////////////////////////////////////////////////
+		// left face
+		m_pPortalMO[nPt]->position(flMinX, flMaxY, flMaxZ) ;
+		m_pPortalMO[nPt]->normal(0.0, 0.0, -1) ;
+		m_pPortalMO[nPt]->textureCoord(1.0, 0.0) ;
+
+		m_pPortalMO[nPt]->position(flMinX, flMaxY, flMinZ) ;
+		m_pPortalMO[nPt]->normal(0.0, 0.0, -1) ;
+		m_pPortalMO[nPt]->textureCoord(0.0, 0.0) ;
+
+		m_pPortalMO[nPt]->position(flMinX, flMinY, flMinZ) ;
+		m_pPortalMO[nPt]->normal(0.0, 0.0, -1) ;
+		m_pPortalMO[nPt]->textureCoord(0.0, 1.0) ;
+
+		m_pPortalMO[nPt]->position(flMinX, flMinY, flMaxZ) ;
+		m_pPortalMO[nPt]->normal(0.0, 0.0, -1) ;
+		m_pPortalMO[nPt]->textureCoord(1.0, 1.0) ;
+
+		m_pPortalMO[nPt]->quad(8, 9, 10, 11) ;
+		m_pPortalMO[nPt]->quad(11, 10, 9, 8) ;
+
+		
+		//////////////////////////////////////////////////////
+		// right face
+		m_pPortalMO[nPt]->position(flMaxX, flMaxY, flMaxZ) ;
+		m_pPortalMO[nPt]->normal(0.0, 0.0, 1) ;
+		m_pPortalMO[nPt]->textureCoord(0.0, 0.0) ;
+
+		m_pPortalMO[nPt]->position(flMaxX, flMaxY, flMinZ) ;
+		m_pPortalMO[nPt]->normal(0.0, 0.0, 1) ;
+		m_pPortalMO[nPt]->textureCoord(1.0, 0.0) ;
+
+		m_pPortalMO[nPt]->position(flMaxX, flMinY, flMinZ) ;
+		m_pPortalMO[nPt]->normal(0.0, 0.0, 1) ;
+		m_pPortalMO[nPt]->textureCoord(1.0, 1.0) ;
+
+		m_pPortalMO[nPt]->position(flMaxX, flMinY, flMaxZ) ;
+		m_pPortalMO[nPt]->normal(0.0, 0.0, 1) ;
+		m_pPortalMO[nPt]->textureCoord(0.0, 1.0) ;
+
+		m_pPortalMO[nPt]->quad(15, 14, 13, 12) ;
+		m_pPortalMO[nPt]->quad(12, 13, 14, 15) ;
+
+		//////////////////////////////////////////////////////
+		// top face
+		m_pPortalMO[nPt]->position(flMinX, flMaxY, flMaxZ) ;
+		m_pPortalMO[nPt]->normal(0.0, 0.0, -1) ;
+		m_pPortalMO[nPt]->textureCoord(0.0, 1.0) ;
+
+		m_pPortalMO[nPt]->position(flMaxX, flMaxY, flMaxZ) ;
+		m_pPortalMO[nPt]->normal(0.0, 0.0, -1) ;
+		m_pPortalMO[nPt]->textureCoord(1.0, 1.0) ;
+
+		m_pPortalMO[nPt]->position(flMaxX, flMaxY, flMinZ) ;
+		m_pPortalMO[nPt]->normal(0.0, 0.0, -1) ;
+		m_pPortalMO[nPt]->textureCoord(1.0, 0.0) ;
+
+		m_pPortalMO[nPt]->position(flMinX, flMaxY, flMinZ) ;
+		m_pPortalMO[nPt]->normal(0.0, 0.0, -1) ;
+		m_pPortalMO[nPt]->textureCoord(0.0, 0.0) ;
+
+		m_pPortalMO[nPt]->quad(16, 17, 18, 19) ;
+		m_pPortalMO[nPt]->quad(19, 18, 17, 16) ;
+
+		//////////////////////////////////////////////////////
+		// bottom face
+		m_pPortalMO[nPt]->position(flMinX, flMinY, flMaxZ) ;
+		m_pPortalMO[nPt]->normal(0.0, 0.0, -1) ;
+		m_pPortalMO[nPt]->textureCoord(0.0, 0.0) ;
+
+		m_pPortalMO[nPt]->position(flMaxX, flMinY, flMaxZ) ;
+		m_pPortalMO[nPt]->normal(0.0, 0.0, -1) ;
+		m_pPortalMO[nPt]->textureCoord(1.0, 0.0) ;
+
+		m_pPortalMO[nPt]->position(flMaxX, flMinY, flMinZ) ;
+		m_pPortalMO[nPt]->normal(0.0, 0.0, -1) ;
+		m_pPortalMO[nPt]->textureCoord(1.0, 1.0) ;
+
+		m_pPortalMO[nPt]->position(flMinX, flMinY, flMinZ) ;
+		m_pPortalMO[nPt]->normal(0.0, 0.0, -1) ;
+		m_pPortalMO[nPt]->textureCoord(0.0, 1.0) ;
+
+		m_pPortalMO[nPt]->quad(23, 22, 21, 20) ;
+		m_pPortalMO[nPt]->quad(20, 21, 22, 23) ;
+		//////////////////////////////////////////////////////
+
+		m_pPortalMO[nPt]->end() ;
+
+
+
+		sprintf(chSceneNodeName, "PSN%05i", nPt) ;
+		m_pSceneMgr->getRootSceneNode()->createChildSceneNode(chSceneNodeName)->attachObject(m_pPortalMO[nPt]) ;
+		m_pPortalNode[nPt]=m_pSceneMgr->getRootSceneNode()->getChild(chSceneNodeName) ;
+		
+	}
+
+	return 1 ;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+
+
+// this function calculates which zones are visible.  
+// If they are in the view frustum and any connecting portals are open, they're visible.  
+// It calls a recursive functions that goes into each visible zone and scans it's portals 
+// for more visible zones, cutting down the frustum as it goes.
+//
+// This function also checks which lights are visible.
+// First it scans the original zone the camera is in, any light whose centre is in that zone
+// is visible regardless of how many other zones that light touches.
+// Lights that are completely contained in any of the further zones are checked against the
+// current cut down frustum.
+void OgreFramework::CalculateZoneVisibility(Ogre::Camera *pCamera, int *pCameraZone, char* pZoneVis, char* pLightVis, char* pSubLightVis, char* pPortalVis, int* pMaxVisibleLight, unsigned short* pVisibleLightList)
+{
+
+	
+	int nZone=0 ;
+//	int nZoneIndex=0 ;
+	int nSubZone=0 ;
+//	int nSubZoneIndex=0 ;
+	int nMaxSubZone=0 ;
+	int nPortal=0 ;
+//	int nPortalIndex=0 ;
+	int nMaxPortal=0 ;
+//	int nMaxPortalZone=0 ;
+//	int nPortalZone=0 ;
+//	int nPortalZoneIndex=0 ;
+//	int nMaxZone=0 ;
+	
+
+	int nMaxLight=m_Q3Map->m_nLightMax ;
+	int nLt=0 ;
+
+//	int nMaxSubLight=m_Q3Map->m_nSubLightMax ;
+	int nSubLt=0 ;
+	
+	m_nMaxVisibleLight=0 ;
+
+	
+
+	Ogre::Vector3 Pos=pCamera->getPosition() ;
+	float flPos[3]={Pos.x, Pos.y, Pos.z} ;
+
+	Ogre::AxisAlignedBox AAB ;
+
+
+
+	// initially all lights are invisible
+	// set all remaining real lights to invisible
+	SetAllLightsOff() ;
+
+	m_nTotalTriangles=0 ;
+
+	*pMaxVisibleLight=0 ; // no lights in the list 
+	
+	if(m_nZoneCullingActive) // if zone culling is active
+	{
+		
+		// by default, all zones are not visible
+		nZone=m_Q3Map->m_nMaxZone ;
+		while(nZone)
+			pZoneVis[--nZone]=ZONE_UNCHECKED ;
+
+		// by default, all lights are not visible
+		nLt=nMaxLight ;
+		while(nLt)
+			pLightVis[--nLt]=LIGHT_UNCHECKED ;
+
+		// by default, all lights are not visible
+		nSubLt=m_Q3Map->m_nSubLightMax ;
+		while(nSubLt)
+			pSubLightVis[--nSubLt]=LIGHT_UNCHECKED ;
+
+		// by default, all portals are not visible
+		nPortal=m_Q3Map->m_iNumPortals ;
+		while(nPortal)
+			pPortalVis[--nPortal]=PORTAL_UNCHECKED ;
+	}
+	else
+	{
+		// for debugging, all zones are visible
+		nZone=m_Q3Map->m_nMaxZone ;
+		while(nZone)
+			pZoneVis[--nZone]=ZONE_VISIBLE ;
+
+
+		// by default, all lights are visible
+		nLt=nMaxLight ;
+		while(nLt)
+			pLightVis[--nLt]=LIGHT_VISIBLE ;
+
+		nSubLt=m_Q3Map->m_nSubLightMax ;
+		while(nSubLt)
+			pSubLightVis[--nSubLt]=LIGHT_VISIBLE ;
+
+		// by default, all portals are visible
+		nPortal=m_Q3Map->m_iNumPortals ;
+		while(nPortal)
+			pPortalVis[--nPortal]=PORTAL_VISIBLE ;
+
+		return ;
+	}
+
+
+
+
+
+
+
+
+	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+	
+
+
+	/////////////////////////////////////////////////////////////////////////////////////
+	// check camera is actually in the zone designated, otherwise update it.
+
+	if((*pCameraZone==-1) || (!m_Q3Map->PointInZone(flPos, *pCameraZone)) )
+	{
+
+		nSubZone=m_Q3Map->GetNextSubZone(flPos, -1, m_Q3Map->m_iNumSubZones) ;
+			
+		// subzone might still be -1 if the point isn't in any zone.  If this happens, set all zones in the view frustum as visible.
+		// this shouldn't generally happen, but if we aren't clipping and are flying around testing, it can.
+		// Don't worry about optimizing this.
+
+
+
+		if(nSubZone==-1)
+		{
+
+			nMaxSubZone=m_Q3Map->m_iNumSubZones ;
+			for(nSubZone=0 ; nSubZone<nMaxSubZone ; nSubZone++)
+			{
+				AAB.setExtents(m_Q3Map->m_pSubZones[nSubZone].Min[0], m_Q3Map->m_pSubZones[nSubZone].Min[1], m_Q3Map->m_pSubZones[nSubZone].Min[2], m_Q3Map->m_pSubZones[nSubZone].Max[0], m_Q3Map->m_pSubZones[nSubZone].Max[1], m_Q3Map->m_pSubZones[nSubZone].Max[2]) ;
+
+				if(pCamera->isVisible(AAB))
+					pZoneVis[ m_Q3Map->m_pSubZones[nSubZone].Zone ]=ZONE_VISIBLE ;
+			}	
+
+			*pCameraZone=-1 ;
+			return ; // skip everything else
+		}
+
+		*pCameraZone=m_Q3Map->m_pSubZones[nSubZone].Zone ; // update the zone
+	
+	}
+
+	//
+	/////////////////////////////////////////////////////////////////////////////////////
+
+
+	// update the portal states
+	nMaxPortal=m_Q3Map->m_iNumPortals ;
+	nPortal=nMaxPortal ;
+	while(nPortal)
+	{
+		nPortal-- ;
+
+		if(m_nPortalDebug==0)
+			m_chPortalState[nPortal]=PORTALSTATE_OPEN ; 
+		else
+		{
+			//if(m_nPortalState==1)
+				m_chPortalState[nPortal]=PORTALSTATE_OPEN ; 
+			//else
+			//	m_chPortalState[nPortal]=PORTALSTATE_CLOSED ;
+
+
+			
+		}
+		
+		//m_chPortalState[nPortal]|=PORTAL_FLAG_UNCHECKED ; // by default all portals are unchecked
+		
+		//m_chPortalState[nPortal]&=~(PORTAL_FLAG_VISCHECK|PORTAL_FLAG_VISIBLE) ; // by default, all portals are vis unchecked and not visible
+	}
+
+	//m_chPortalState[0]=PORTAL_FLAG_CLOSED ;
+	if(m_nPortalState==1)
+		m_chPortalState[1]=PORTALSTATE_OPEN ;
+	else
+		m_chPortalState[1]=PORTALSTATE_CLOSED ;
+
+
+
+
+	//Ogre::Camera* pTestCamera ;
+	//pTestCamera->synchroniseBaseSettingsWith(pCamera) ;
+
+	//Ogre::Real OrigL, OrigR, OrigT, OrigB ; // original frustum extents
+	//pCamera->getFrustumExtents(OrigL, OrigR, OrigT, OrigB) ;
+	
+	//pCamera->setFrustumExtents(OrigL/2.0, OrigR/2.0, OrigT/2.0, OrigB/2.0) ; // reset the original frustum extents
+
+	m_nRecurseCount=0 ;
+
+	
+	// work out the aspect scaling we'll need
+	Ogre::Real OrigL, OrigR, OrigT, OrigB, ScaleX, ScaleY ; // frustum extents per each portal in this zone
+	Ogre::Real NewL, NewR, NewT, NewB ;
+	pCamera->getFrustumExtents(OrigL, OrigR, OrigT, OrigB) ;
+
+	m_ViewMatrix=pCamera->getViewMatrix(true) ;
+	m_ProjectionMatrix=pCamera->getProjectionMatrix() ;
+	
+
+	//if(m_nOriginalZoneOpenPortals=0) // no portals on player zone are open, so we can only see the current zones.
+	//	pZoneVis[nZone]=ZONE_VISIBLE ; // flag this subzone as visible
+	//else
+	{
+
+		/*
+		// apply the stereofrustum tweak if needed
+		if(m_flStereoFrustumTweak!=0.0f)
+		{
+
+			NewL=OrigL-m_flStereoFrustumTweak ;
+			NewR=OrigR+m_flStereoFrustumTweak ;
+			NewT=OrigT ;
+			NewB=OrigB ;
+			pCamera->setFrustumExtents(NewL, NewR, NewT, NewB) ;
+		}
+		else
+		*/
+		{
+			NewL=OrigL ;
+			NewR=OrigR ;
+			NewT=OrigT ;
+			NewB=OrigB ;
+		}
+		
+		ScaleX=NewR ;
+		ScaleY=NewT ;
+
+
+		PortalScan(pCamera, *pCameraZone, ScaleX, ScaleY, pZoneVis, pLightVis, pSubLightVis, pPortalVis, pMaxVisibleLight, pVisibleLightList) ; // recursively scan portals and work out zone visibility
+
+		
+		pCamera->setFrustumExtents(OrigL, OrigR, OrigT, OrigB) ; // reset the original frustum extents
+
+		CheckMultiZoneLights(*pCameraZone, pZoneVis, pLightVis, pSubLightVis) ;
+		AddZonesFromMultiZoneLights(pZoneVis, pLightVis, pSubLightVis) ;
+
+		// check entity visibility
+		
+
+	}
+
+
+
+
+
+
+
+	
+}
+
+
+void OgreFramework::SetupEntityZones(void)
+{
+	// work out what zones any active entities are in.
+	int nEntity=0 ;
+	float Centre[3] ;
+	float AABBMin[3] ;
+	float AABBMax[3] ;
+	float* pLightMin ;
+	float* pLightMax ;
+	
+	int nOldZone=0 ;
+	int nCurrentZone=0 ;
+	int nMaxOldZone=0 ;
+	int nZonePerZoneIndex=0 ;
+	int nZone=0 ;
+	int nZoneIndex=0 ;
+	int nMaxZone=0 ;
+	int nMaxSubLightIndex=0 ;
+	int nSubLightIndex=0 ;
+	int nSubLight=0 ;
+
+//	int nLight=0;
+	int nLightIndex=0 ;
+
+
+	
+
+	for(nEntity=0 ; nEntity<m_nMaxEntity ; nEntity++)
+		if(m_pEntityInfo[nEntity].Active)
+		{
+			nLightIndex=0 ;
+			// to calculate what zones an entity is in, we test the centre of it's bounding box to get the main zone.
+			// we then test all zones that touch the central zone and see if they touch the entity bounding box.
+
+			// check if the entity central point is still in the same zone as last time.
+			Centre[0]=m_pEntityInfo[nEntity].Centre.x + m_pEntityInfo[nEntity].Postition.x ;
+			Centre[1]=m_pEntityInfo[nEntity].Centre.y + m_pEntityInfo[nEntity].Postition.y ;
+			Centre[2]=m_pEntityInfo[nEntity].Centre.z + m_pEntityInfo[nEntity].Postition.z ;
+
+			nMaxOldZone=m_pEntityInfo[nEntity].Zone[INDEX_ENTITYZONECOUNT] ;
+
+
+
+			if(nMaxOldZone>0)
+			{
+				nOldZone=m_pEntityInfo[nEntity].Zone[0] ;
+				if(m_Q3Map->PointInZone(Centre, nOldZone))
+					nCurrentZone=nOldZone ;	// still in the same zone as last round
+				else
+				{
+					// see if we are in one of the other zones that were touched by this entity's aabb last round
+
+					nCurrentZone=-1 ;
+					for(nZoneIndex=1 ; nZoneIndex<nMaxOldZone ; nZoneIndex++)
+					{
+						if(m_Q3Map->PointInZone(Centre, m_pEntityInfo[nEntity].Zone[nZoneIndex]))
+						{
+							nCurrentZone=m_pEntityInfo[nEntity].Zone[nZoneIndex] ; // found the current zone we are in
+							break ;
+						}// end if point in zone
+
+					}// end for zoneindex
+
+				}// end if not in old zone.
+			}
+			else
+				nCurrentZone=-1 ;
+
+
+			// if we haven't found the current zone so far, we need to check every zone.
+			if(nCurrentZone==-1)
+			{
+				nZone=m_Q3Map->m_nMaxZone ;
+				while(nZone)
+					if(m_Q3Map->PointInZone(Centre, --nZone))
+					{
+						nCurrentZone=nZone ;
+						break ;
+					}
+			}// end if nCurrentZone -1
+
+
+			
+			// now either nCurrentZone is the zone the central point is in, or the entity isn't in any zone
+			if(nCurrentZone==-1)
+				m_pEntityInfo[nEntity].Zone[INDEX_ENTITYZONECOUNT]=0 ;
+			else
+			{
+				// scan through all the zones that touch this entitys central zone
+				// see if the entitys AABB touches those other zones.
+				m_pEntityInfo[nEntity].Zone[0]=nCurrentZone ;
+				nZonePerZoneIndex=1 ;
+				
+				AABBMin[0]=m_pEntityInfo[nEntity].Postition.x + m_pEntityInfo[nEntity].AABBMin[0] ; 
+				AABBMin[1]=m_pEntityInfo[nEntity].Postition.y + m_pEntityInfo[nEntity].AABBMin[1] ; 
+				AABBMin[2]=m_pEntityInfo[nEntity].Postition.z + m_pEntityInfo[nEntity].AABBMin[2] ;
+
+				AABBMax[0]=m_pEntityInfo[nEntity].Postition.x + m_pEntityInfo[nEntity].AABBMax[0] ; 
+				AABBMax[1]=m_pEntityInfo[nEntity].Postition.y + m_pEntityInfo[nEntity].AABBMax[1] ; 
+				AABBMax[2]=m_pEntityInfo[nEntity].Postition.z + m_pEntityInfo[nEntity].AABBMax[2] ;
+
+
+				nMaxZone=m_Q3Map->m_nZoneTouchesZone[nCurrentZone][INDEX_ZONEPERZONECOUNT] ;
+				for(nZoneIndex=0 ; nZoneIndex<nMaxZone ; nZoneIndex++)
+				{
+					nZone=m_Q3Map->m_nZoneTouchesZone[nCurrentZone][nZoneIndex] ;
+
+					if(m_Q3Map->AABBTouchesZone(AABBMin, AABBMax, nZone)) 
+					{
+						m_pEntityInfo[nEntity].Zone[nZonePerZoneIndex]=nZone ;
+						nZonePerZoneIndex++ ;
+
+						if(nZonePerZoneIndex>MAX_ZONEPERENTITY)
+							nZonePerZoneIndex=MAX_ZONEPERENTITY ;
+					}
+
+					
+
+				}// end for nZoneIndex
+
+
+				m_pEntityInfo[nEntity].Zone[INDEX_ENTITYZONECOUNT]=nZonePerZoneIndex ;
+
+				// go through all the zones and check which lights touch this entity AABB
+				for(nZoneIndex=0 ; nZoneIndex<nZonePerZoneIndex ; nZoneIndex++)
+				{
+					nZone=m_pEntityInfo[nEntity].Zone[nZoneIndex] ;
+
+					nMaxSubLightIndex=m_Q3Map->m_nZoneTouchesSubLight[nZone][MAX_LIGHTPERZONE] ;
+					for(nSubLightIndex=0 ; nSubLightIndex<nMaxSubLightIndex ; nSubLightIndex++)
+					{
+						nSubLight=m_Q3Map->m_nZoneTouchesSubLight[nZone][nSubLightIndex] ;
+						pLightMin=m_Q3Map->m_SubLight[nSubLight].Min ;
+						pLightMax=m_Q3Map->m_SubLight[nSubLight].Max ;
+
+						// if the entity overlaps the sublight
+						if(
+									 (AABBMin[0]<pLightMax[0]) && (AABBMax[0]>pLightMin[0])
+								&& (AABBMin[1]<pLightMax[1]) && (AABBMax[1]>pLightMin[1])
+								&& (AABBMin[2]<pLightMax[2]) && (AABBMax[2]>pLightMin[2])
+							)
+						{
+							// add this to the list of lights.
+							// it's possible it might get listed more than once if this is a multizone light, that's ok.
+			
+							m_pEntityInfo[nEntity].Light[nLightIndex]=m_Q3Map->m_SubLight[nSubLight].Light ;
+							nLightIndex++ ;
+							if(nLightIndex>MAX_LIGHTPERENTITY)
+								nLightIndex=MAX_LIGHTPERENTITY ;
+
+
+						} // end if overlap
+
+					}// end for nSubLightIndex
+
+				}// end for nZoneIndex
+
+			}// end if we have a central zone
+			
+			
+			m_pEntityInfo[nEntity].Light[INDEX_LIGHTPERENTITY]=nLightIndex ;
+
+
+		}// end if entity is active
+}
+
+// spotlights need entities set as visible if they are in a visible zone, even if they aren't in the frustum, 
+// because they might cast shadows that are in the frustum.
+// However the deferred render only needs consider entities which are in a visible zone AND in the frustum.
+// m_pVisibleEntity holds entities in visible zones,
+// m_pFrustumEntity holds entities in visible zones that are also in the view frustum.
+void OgreFramework::CalculateEntityVisibility(Ogre::Camera* pCamera, char* pZoneVis)
+{
+
+	int nEntity=0 ;
+	int nMaxZone=0 ;
+	int nZone=0 ;
+	int nZoneIndex=0 ;
+//	int nVisible=0 ;
+	Ogre::AxisAlignedBox AABB ;
+
+
+	m_nMaxVisibleEntity=0 ; // default to an empty list 
+	m_nMaxFrustumEntity=0 ; // default to an empty list 
+
+	m_nVisibleEntityTriangleCount=0 ;
+	m_nFrustumEntityTriangleCount=0 ;
+
+	for(nEntity=0 ; nEntity<m_nMaxEntity ; nEntity++)
+		if(m_pEntityInfo[nEntity].Active)
+		{
+			m_pEntityInfo[nEntity].Visible=0 ; // default to not visible
+			m_pEntityInfo[nEntity].Frustum=0 ; // default to not visible and not in frustum
+
+			nMaxZone=m_pEntityInfo[nEntity].Zone[INDEX_ENTITYZONECOUNT] ;
+
+
+
+				for(nZoneIndex=0 ; nZoneIndex<nMaxZone ; nZoneIndex++)
+				{
+					nZone=m_pEntityInfo[nEntity].Zone[nZoneIndex] ;
+
+					// if a zone this entity touches is visible, test if it is in the view frustrum	
+					if(pZoneVis[nZone]==ZONE_VISIBLE)
+					{
+
+						m_pEntityInfo[nEntity].Visible=1 ;
+						m_nVisibleEntityTriangleCount+=m_pEntityInfo[nEntity].TriangleCount ;
+						m_pVisibleEntity[m_nMaxVisibleEntity++]=nEntity ;
+						
+
+						// is this entity also in the view frustum?
+						AABB.setMinimum( m_pEntityInfo[nEntity].AABB.getMinimum() + m_pEntityInfo[nEntity].Postition ) ;
+						AABB.setMaximum( m_pEntityInfo[nEntity].AABB.getMaximum() + m_pEntityInfo[nEntity].Postition ) ;
+
+						if(pCamera->isVisible(AABB))
+						{
+							m_pEntityInfo[nEntity].Frustum=1 ;
+							m_nFrustumEntityTriangleCount+=m_pEntityInfo[nEntity].TriangleCount ;
+							m_pFrustumEntity[m_nMaxFrustumEntity++]=nEntity ;
+						}
+
+
+
+
+
+						break ; // get out of the loop, we know the entity is visible.
+
+					}// end if zone is visible
+
+				}// end for nZoneIndex
+
+		}// end if entity is active
+
+}
+
+
+
+
+
+void OgreFramework::SetAllLightsOff(void)
+{
+	Light *light;
+	char chLightName[1024] ;
+	int nLt=0 ;
+	for(nLt=0 ; nLt<MAXLIGHTPERLOOP ; nLt++)
+	{
+		sprintf(chLightName, "LT%02i", nLt) ;
+		light=m_pSceneMgr->getLight(chLightName) ;
+		light->setVisible(false) ;
+	}
+}
+
+void OgreFramework::SetSingleVisibleLight(int nLt, bool bOn)
+{
+	Light *light;
+
+	light=m_pSceneMgr->getLight("LT00") ;
+
+
+	// if we're just turning the light off, make it invisible and return.
+	if(bOn==false)
+	{
+		light->setVisible(false) ;
+		return ;
+	}
+
+	
+			
+	light->setDiffuseColour(Ogre::ColourValue(1,1,1));
+	light->setSpecularColour(Ogre::ColourValue(1,1,1));
+			
+	light->setAttenuation(m_Q3Map->m_pLight[nLt].Cutoff, 1.0, 1.0, 0.005);
+	light->setPosition(Ogre::Vector3(m_Q3Map->m_pLight[nLt].Position[0], m_Q3Map->m_pLight[nLt].Position[1], m_Q3Map->m_pLight[nLt].Position[2]));
+	light->setDirection(Ogre::Vector3(m_Q3Map->m_pLight[nLt].Direction[0], m_Q3Map->m_pLight[nLt].Direction[1], m_Q3Map->m_pLight[nLt].Direction[2]));
+			
+	light->setSpotlightRange(Degree(m_Q3Map->m_pLight[nLt].Angle-5.0), Degree(m_Q3Map->m_pLight[nLt].Angle), 1.0) ;
+	light->setVisible(true) ;
+}
+
+
+// sets up a single visible light from the visible light list and assigns the camera to its position and settings
+// returns true if successful, false if not (such as if the light isn't actually visible)
+
+bool OgreFramework::SetupSingleVisibleLightAndShadowCamera(int nLt, char* pLightVis, unsigned short* pVisibleLightList, Ogre::Camera* pCamera)
+{
+	//if(nLt!=4) return false ;
+	
+	if((pLightVis[nLt]&LIGHT_VISIBLE)==0) // we need to check this again, since there's a chance a multi-zone light might have been culled at the last stage
+		return false ;
+
+	
+	Light *light=m_pSceneMgr->getLight("LT00") ;
+	
+	light->setDiffuseColour(Ogre::ColourValue(m_Q3Map->m_pLight[nLt].Colour[0], m_Q3Map->m_pLight[nLt].Colour[1], m_Q3Map->m_pLight[nLt].Colour[2]));
+
+	float	flRange=m_Q3Map->m_pLight[nLt].Cutoff ; // set the range
+	if(flRange<10240.0) //!! Ogre seems to cut off lights wrongly if the range is low, so set it to a minimum.
+		flRange=10240 ;
+
+	// The light attenuation parameters have been co-opted by me... they no longer represent range, constant, linear, quadratic
+	// instead, 
+	// first parameter is the above range, just set high so Ogre doesn't turn lights off itself.  The shaders ignore this parameter.
+	// second parameter is the cutoff used in the shader.  Light suddenly cuts off after this distance.
+	// third parameter is the brightness.  This controls the quadratic falloff of the light.
+	// fourth parameter is unused, leave as 1.0 for now.
+
+	light->setAttenuation(flRange, m_Q3Map->m_pLight[nLt].Cutoff, m_Q3Map->m_pLight[nLt].Brightness, 1.0) ;
+	
+	
+	
+	// Ogre uses a fixed yaw axis which will cause pCamera->setDirection to malfunction if we point directly up or down.
+	// To avoid this we need to check for such lights and shift their direction a little.
+	
+	float DirX=m_Q3Map->m_pLight[nLt].Direction[0] ;
+	float DirY=m_Q3Map->m_pLight[nLt].Direction[1] ;
+	float DirZ=m_Q3Map->m_pLight[nLt].Direction[2] ;
+	
+
+	if((DirY>1.0-CAMERA_EPSILON) && (DirY<1.0+CAMERA_EPSILON)) // camera is pointing directly up
+	{
+		DirX=0.0f ;
+		DirY=1.0f ;
+		DirZ=0.000001f ;
+	}
+	else
+		if((DirY>-1.0-CAMERA_EPSILON) && (DirY<-1.0+CAMERA_EPSILON)) // camera is pointing directly down
+		{
+			DirX=0.0f ;
+			DirY=-1.0f ;
+			DirZ=0.000001f ;
+		}
+	
+	light->setPosition(Ogre::Vector3(m_Q3Map->m_pLight[nLt].Position[0], m_Q3Map->m_pLight[nLt].Position[1], m_Q3Map->m_pLight[nLt].Position[2]));
+	light->setDirection(Ogre::Vector3(DirX, DirY, DirZ));
+			
+	light->setSpotlightRange(Degree(m_Q3Map->m_pLight[nLt].Angle-5.0), Degree(m_Q3Map->m_pLight[nLt].Angle), 1.0) ;
+	light->setVisible(true) ;
+
+	// setup the camera
+	pCamera->setAspectRatio(1) ;
+	pCamera->yaw(Ogre::Radian(0)) ;
+	pCamera->roll(Ogre::Radian(0)) ;
+	pCamera->pitch(Ogre::Radian(0)) ;
+	pCamera->setPosition(m_Q3Map->m_pLight[nLt].Position[0], m_Q3Map->m_pLight[nLt].Position[1], m_Q3Map->m_pLight[nLt].Position[2]) ;
+	pCamera->setDirection(DirX, DirY, DirZ) ;
+	pCamera->setFOVy( Degree(m_Q3Map->m_pLight[nLt].Angle ) ) ;
+
+	return true ;
+
+}
+
+
+void OgreFramework::UpdateVisibleZoneList(char* pZoneVis, unsigned short* pVisibleZoneList, int* pMaxVisibleZone)
+{
+	int nZone=0 ;
+	int nMaxVisibleZone=0 ;
+
+	for(nZone=0 ; nZone<m_Q3Map->m_nMaxZone ; nZone++)
+		if(pZoneVis[nZone]==ZONE_VISIBLE)
+			pVisibleZoneList[nMaxVisibleZone++]=nZone ;
+
+	*pMaxVisibleZone=nMaxVisibleZone ;
+
+}
+
+void OgreFramework::SetZoneNodeAttachments(unsigned short* pVisibleZoneList, int* pMaxVisibleZone, int nMaterialFlags)
+{
+
+		int nListPos=0 ;
+		int nZone=0 ;
+		int nMaxListPos=*pMaxVisibleZone ;
+
+
+
+		for(nListPos=0 ; nListPos<nMaxListPos ; nListPos++)
+		{
+			nZone=pVisibleZoneList[nListPos] ;
+
+				// if the zone node is not already attached, attach it.
+
+				if( m_nOpaqueNodeUsed[nZone] && (nMaterialFlags&MAT_OPAQUE) && !m_pOpaqueNode[nZone]->isInSceneGraph() )
+					m_pSceneMgr->getRootSceneNode()->addChild(m_pOpaqueNode[nZone]) ;
+
+				if( m_nTransNodeUsed[nZone] && (nMaterialFlags&MAT_GEL) && !m_pTransNode[nZone]->isInSceneGraph() )
+					m_pSceneMgr->getRootSceneNode()->addChild(m_pTransNode[nZone]) ;
+
+				if( m_nLampNodeUsed[nZone] && (nMaterialFlags&MAT_LAMP) && !m_pLampNode[nZone]->isInSceneGraph() )
+					m_pSceneMgr->getRootSceneNode()->addChild(m_pLampNode[nZone]) ;
+
+				if( m_nGlowNodeUsed[nZone] && (nMaterialFlags&MAT_GLOW) && !m_pGlowNode[nZone]->isInSceneGraph() )
+					m_pSceneMgr->getRootSceneNode()->addChild(m_pGlowNode[nZone]) ;
+
+		}
+
+
+			
+/*
+		if(m_nDebugLightBox)
+		{
+			int nLt=0 ;
+			int nMaxLight=m_Q3Map->m_nLightMax ;
+
+			for(nLt=0 ; nLt<nMaxLight ; nLt++)
+				if(m_chLightVis[nLt]==LIGHT_VISIBLE)
+				{
+					m_pLightMO[nLt]->setVisible(true) ;
+
+				}
+				else
+					m_pLightMO[nLt]->setVisible(false) ;
+
+
+
+		}
+
+		if(m_nPortalDebug)
+		{
+			int nPt=0 ;
+			int nMaxPortal=m_Q3Map->m_iNumPortals ;
+
+			for(nPt=0 ; nPt<nMaxPortal ; nPt++)
+				if(
+							(m_chPortalVis[nPt]&PORTAL_VISIBLE) 
+							&&
+							(m_chPortalState[nPt]&PORTALSTATE_CLOSED)
+					)
+					m_pPortalMO[nPt]->setVisible(true) ;
+				else
+					m_pPortalMO[nPt]->setVisible(false) ;
+		}		
+			
+	*/
+}
+
+
+void OgreFramework::SetZoneNodeAttachments(unsigned short* pVisibleZoneList, int* pMaxVisibleZone, int nMaterialFlags, int nLight)
+{
+
+		nLight=IGNORE_LIGHT ;
+
+		int nListPos=0 ;
+		int nZone=0 ;
+		int nMaxListPos=*pMaxVisibleZone ;
+
+		int nLightZone=0 ;
+		int nMaxLightZone=0 ;
+		int nFoundZone=0 ;
+
+
+		for(nListPos=0 ; nListPos<nMaxListPos ; nListPos++)
+		{
+			nZone=pVisibleZoneList[nListPos] ;
+
+			// is this zone touched by this light?
+			if(nLight!=IGNORE_LIGHT)
+			{
+				nMaxLightZone=m_Q3Map->m_nLightTouchesZone[nLight][INDEX_LIGHTZONECOUNT] ;
+
+				nFoundZone=0 ;
+				for(nLightZone=0 ; nLightZone<nMaxLightZone ; nLightZone++)
+					if(m_Q3Map->m_nLightTouchesZone[nLight][nLightZone]==nZone)
+					{
+						nFoundZone=1 ;
+						break ;
+					}
+
+			}
+			else
+				nFoundZone=1 ;
+
+
+			if(nFoundZone) // if the light touches this zone, or we're ignoring lights
+			{
+				// if the zone node is not already attached, attach it.
+
+				if( m_nOpaqueNodeUsed[nZone] && (nMaterialFlags&MAT_OPAQUE) && !m_pOpaqueNode[nZone]->isInSceneGraph() )
+					m_pSceneMgr->getRootSceneNode()->addChild(m_pOpaqueNode[nZone]) ;
+
+				if( m_nTransNodeUsed[nZone] && (nMaterialFlags&MAT_GEL) && !m_pTransNode[nZone]->isInSceneGraph() )
+					m_pSceneMgr->getRootSceneNode()->addChild(m_pTransNode[nZone]) ;
+
+				if( m_nLampNodeUsed[nZone] && (nMaterialFlags&MAT_LAMP) && !m_pLampNode[nZone]->isInSceneGraph() )
+					m_pSceneMgr->getRootSceneNode()->addChild(m_pLampNode[nZone]) ;
+
+				if( m_nGlowNodeUsed[nZone] && (nMaterialFlags&MAT_GLOW) && !m_pGlowNode[nZone]->isInSceneGraph() )
+					m_pSceneMgr->getRootSceneNode()->addChild(m_pGlowNode[nZone]) ;
+			}
+		}
+
+}
+
+bool OgreFramework::LightTouchesZone(int nLight, int nZone)
+{
+		int nLightZone=0 ;
+		int nMaxLightZone=0 ;
+
+		nMaxLightZone=m_Q3Map->m_nLightTouchesZone[nLight][INDEX_LIGHTZONECOUNT] ;
+				
+		for(nLightZone=0 ; nLightZone<nMaxLightZone ; nLightZone++)
+			if(m_Q3Map->m_nLightTouchesZone[nLight][nLightZone]==nZone)
+				return true ;
+
+		return false ;
+}
+
+
+// last chance to cull some more multizone lights
+// Then we set all zones the remaining multizone lights touch to visible so that we get correct shadows
+void OgreFramework::CheckMultiZoneLights(int nCameraZone, char* pZoneVis, char* pLightVis, char* pSubLightVis)
+{
+	int nLightMax=m_Q3Map->m_nMaxMultiZoneLight ;
+	int nLight=0 ;
+	int nLightIndex=0 ;
+//	int nLightVal=0 ;
+	int nOpenPortals=0 ;
+//	int nZoneHasOpenPortals=0 ;
+	int nPortal=0 ;
+	int nPortalIndex=0 ;
+	int nMaxPortal=0 ;
+//	int nZone=0 ;
+//	int nMaxZone=0 ;
+//	int nZoneIndex=0 ;
+
+	int nSubLightMax=0 ;
+//	int nSubLightIndex=0 ;
+	int nSubLight=0 ;
+	int nSubLightStart=0 ;
+
+	int nPortalLightMax=0 ;
+	int nPortalLightIndex=0 ;
+//	int nPortalLight=0 ;
+
+	int nCentreZone=0 ;
+
+
+	for(nLightIndex=0 ; nLightIndex<nLightMax ; nLightIndex++)
+	{
+		nLight=m_Q3Map->m_nMultiZoneLight[nLightIndex] ;
+		if(pLightVis[nLight]==LIGHT_VISIBLE) // try to kill multizone lights that are currently set as visible
+		{
+
+			////////////////////////////////////////////////////////////////////////////////////////////////////////////.
+			// if the zone that contains the centre isn't visible, 
+			// and the portals that the light touches are closed,
+			// then this light can't be visible anywhere else
+
+			nCentreZone=m_Q3Map->m_pLight[nLight].CentreZone ;
+			
+			
+			// is the zone that contains the light centre not visible?
+			if(pZoneVis[nCentreZone]!=ZONE_VISIBLE)
+			{
+				// check the portals on the centre sublight zone.  
+				// If the ones that the light touches are closed, 
+				// then this light can't be visible
+
+				
+				nMaxPortal=m_Q3Map->m_nZoneTouchesPortal[nCentreZone][INDEX_PORTALCOUNT] ;
+				nOpenPortals=0 ;
+				nSubLightStart=m_Q3Map->m_pLight[nLight].SubLightStart ; // the first sublight also contains the light centre
+
+				for(nPortalIndex=0 ; nPortalIndex<nMaxPortal ; nPortalIndex++)
+				{
+					nPortal=m_Q3Map->m_nZoneTouchesPortal[nCentreZone][nPortalIndex] ;
+
+					// if portal's open...
+					if(m_chPortalState[nPortal]&PORTALSTATE_OPEN)
+					{
+						nPortalLightMax=m_Q3Map->m_nPortalTouchesLight[nPortal][INDEX_PORTALLIGHTCOUNT] ;
+						
+						// scan all the lights that touch this portal to see if our current light is one of them
+						for(nPortalLightIndex=0 ; nPortalLightIndex<nPortalLightMax ; nPortalLightIndex++)
+							if(m_Q3Map->m_nPortalTouchesLight[nPortal][nPortalLightIndex]==nLight)
+							{
+								nOpenPortals=1 ;
+								break ;
+							}
+
+					}// end if portal is open
+
+					if(nOpenPortals) break ; // get out if we found an open portal that the light touches
+				}// end for portals
+
+
+
+				// if none of the portals touched by the centre light were open, set this light and it's sublights to not visible
+				if(nOpenPortals==0)
+				{
+					pLightVis[nLight]=LIGHT_UNCHECKED ;
+
+					nSubLightStart=m_Q3Map->m_pLight[nLight].SubLightStart ; // the first sublight also contains the light centre
+					nSubLightMax=nSubLightStart+m_Q3Map->m_pLight[nLight].ZoneCount ; 
+
+					for(nSubLight=nSubLightStart ; nSubLight<nSubLightMax ; nSubLight++)
+						pSubLightVis[nSubLightStart]=LIGHT_UNCHECKED ;
+
+				}// end if no light centre portals that touched lights were open
+
+
+
+			}// end if light centre zone isn't visible
+
+		}// end if light is visibile
+
+	}// end for lightindex
+
+}
+
+
+// for lights that touch multiple zones, work out what additional zones we need to make
+// visible in order that lighting will come out with correct shadows.
+void OgreFramework::AddZonesFromMultiZoneLights(char* pZoneVis, char* pLightVis, char* pSubLightVis)
+{
+	int nLightMax=m_Q3Map->m_nMaxMultiZoneLight ;
+	int nLight=0 ;
+	int nLightIndex=0 ;
+	float flMinX=0.0f ;
+	float flMaxX=0.0f ;
+	float flMinY=0.0f ;
+	float flMaxY=0.0f ;
+	float flMinZ=0.0f ;
+	float flMaxZ=0.0f ;
+
+	int nSubLightMax=0 ;
+//	int nSubLightIndex=0 ;
+	int nSubLight=0 ;
+	int nSubLightStart=0 ;
+	int nSubLightFirst=0 ;
+
+	int nInnerLight=0 ;
+
+
+	for(nLightIndex=0 ; nLightIndex<nLightMax ; nLightIndex++)
+	{
+		nLight=m_Q3Map->m_nMultiZoneLight[nLightIndex] ;
+		if(pLightVis[nLight]==LIGHT_VISIBLE)
+		{
+			nSubLightStart=m_Q3Map->m_pLight[nLight].SubLightStart ; // the first sublight also contains the light centre
+			nSubLightFirst=nSubLightStart+1 ;// start from the first sublight after the one containing the centre
+			nSubLightMax=nSubLightStart+m_Q3Map->m_pLight[nLight].ZoneCount ; 
+			
+
+			for(nSubLight=nSubLightStart ; nSubLight<nSubLightMax ; nSubLight++)	
+				if(pSubLightVis[nSubLight]==LIGHT_VISIBLE) // sublight is visible (and unchecked)
+				{
+					// create the new AABB
+					// this is bounded by this sublight and the centre holding sublight.  
+					flMinX = m_Q3Map->m_SubLight[nSubLight].Min[0] < m_Q3Map->m_SubLight[nSubLightStart].Min[0] ? m_Q3Map->m_SubLight[nSubLight].Min[0] : m_Q3Map->m_SubLight[nSubLightStart].Min[0] ; 
+					flMinY = m_Q3Map->m_SubLight[nSubLight].Min[1] < m_Q3Map->m_SubLight[nSubLightStart].Min[1] ? m_Q3Map->m_SubLight[nSubLight].Min[1] : m_Q3Map->m_SubLight[nSubLightStart].Min[1] ; 
+					flMinZ = m_Q3Map->m_SubLight[nSubLight].Min[2] < m_Q3Map->m_SubLight[nSubLightStart].Min[2] ? m_Q3Map->m_SubLight[nSubLight].Min[2] : m_Q3Map->m_SubLight[nSubLightStart].Min[2] ; 
+				
+					flMaxX = m_Q3Map->m_SubLight[nSubLight].Max[0] > m_Q3Map->m_SubLight[nSubLightStart].Max[0] ? m_Q3Map->m_SubLight[nSubLight].Max[0] : m_Q3Map->m_SubLight[nSubLightStart].Max[0] ; 
+					flMaxY = m_Q3Map->m_SubLight[nSubLight].Max[1] > m_Q3Map->m_SubLight[nSubLightStart].Max[1] ? m_Q3Map->m_SubLight[nSubLight].Max[1] : m_Q3Map->m_SubLight[nSubLightStart].Max[1] ; 
+					flMaxZ = m_Q3Map->m_SubLight[nSubLight].Max[2] > m_Q3Map->m_SubLight[nSubLightStart].Max[2] ? m_Q3Map->m_SubLight[nSubLight].Max[2] : m_Q3Map->m_SubLight[nSubLightStart].Max[2] ; 
+				
+					for(nInnerLight=nSubLightStart ; nInnerLight<nSubLightMax ; nInnerLight++)
+					{
+
+						
+						if(
+									(nInnerLight!=nSubLight) // no need to test itself
+								&&(pZoneVis[  m_Q3Map->m_SubLight[nInnerLight].Zone  ] != ZONE_VISIBLE) // don't test if already visible
+								// if overlap, InnerLight's zone is visible
+								&&(flMinX<m_Q3Map->m_SubLight[nInnerLight].Max[0]) && (flMaxX>m_Q3Map->m_SubLight[nInnerLight].Min[0])
+								&&(flMinY<m_Q3Map->m_SubLight[nInnerLight].Max[1]) && (flMaxY>m_Q3Map->m_SubLight[nInnerLight].Min[1])
+								&&(flMinZ<m_Q3Map->m_SubLight[nInnerLight].Max[2]) && (flMaxZ>m_Q3Map->m_SubLight[nInnerLight].Min[2])
+							)
+						{
+							pZoneVis[  m_Q3Map->m_SubLight[nInnerLight].Zone  ]=ZONE_VISIBLE ; // flag this zone as visible
+							m_nTotalTriangles+=m_nZoneTriangleCount[ m_Q3Map->m_SubLight[nInnerLight].Zone ] ; // just some stats for interest
+						}
+
+					} // end for innerlight
+					
+				}// end if sublight is visible
+
+		}// end if light is visible
+
+	}// end for nLightIndex
+
+}
+
+// scan all the unchecked portals in a subzone to see if they are visible, 
+// then move into any visible subzones and recurse.
+// scalex and scaley will convert the screen space numbers (-1 to 1) into frustum numbers (varies, often -0.5ish to 0.5ish) which is probably due to screen aspect ratios etc
+
+// we also add visible lights if they are in the view frustum
+void OgreFramework::PortalScan(Ogre::Camera *pCamera, int nZone, Ogre::Real ScaleX, Ogre::Real ScaleY, char* pZoneVis, char* pLightVis, char* pSubLightVis, char* pPortalVis, int* pMaxVisibleLight, unsigned short* pVisibleLightList)
+{
+	Ogre::Real OrigL, OrigR, OrigT, OrigB ; // original frustum extents
+	Ogre::Real PortalL, PortalR, PortalT, PortalB ; // frustum extents per each portal in this zone
+	pCamera->getFrustumExtents(OrigL, OrigR, OrigT, OrigB) ;
+	
+
+	
+
+	Ogre::AxisAlignedBox AAB ;
+
+
+	int nPortalMax=m_Q3Map->m_nZoneTouchesPortal[nZone][INDEX_PORTALCOUNT] ;
+	int nPortalIndex=0 ;
+	int nPortal=0 ;
+
+	int nPortalZoneMax=0 ;
+	int nPortalZone=0 ;
+	int nPortalZoneIndex=0 ;
+
+
+	//////////////////////////////////////////////////////////////////////////////////////
+	//
+	// LIGHTING CHECKS
+	//
+
+	int nLight=0;
+	int nLightVal=0 ;
+//	int nLightIndex=0 ;
+//	int nMaxLight=0 ;
+
+	int nSubLight=0 ;
+	int nSubLightIndex=0 ;
+	int nMaxSubLight=0 ;
+//	int nSubLightVal=0 ;
+
+	
+	// check all lights completely contained in this zone to see if they're visible
+	nMaxSubLight=m_Q3Map->m_nZoneTouchesSubLight[nZone][INDEX_LIGHTCOUNT] ;
+	for(nSubLightIndex=0 ; nSubLightIndex<nMaxSubLight ; nSubLightIndex++)
+	{
+		nSubLight=m_Q3Map->m_nZoneTouchesSubLight[nZone][nSubLightIndex] ;
+		//nSubLightVal=m_chSubLightVis[nSubLight] ;
+
+		nLight=m_Q3Map->m_SubLight[nSubLight].Light ;
+		nLightVal=pLightVis[nLight] ;
+
+		// if we haven't previously established that the light is visible or out of the frustum, check it.
+
+			AAB.setExtents(m_Q3Map->m_SubLight[nSubLight].Min[0], m_Q3Map->m_SubLight[nSubLight].Min[1], m_Q3Map->m_SubLight[nSubLight].Min[2], m_Q3Map->m_SubLight[nSubLight].Max[0], m_Q3Map->m_SubLight[nSubLight].Max[1], m_Q3Map->m_SubLight[nSubLight].Max[2]) ;
+			if(pCamera->isVisible(AAB)) // sublight is visible
+			{
+				// flag this light and sublight as visible
+				if((nLightVal!=LIGHT_VISIBLE) && (*pMaxVisibleLight<MAXVISIBLELIGHT))
+				{
+					pVisibleLightList[*pMaxVisibleLight]=nLight ;
+					*pMaxVisibleLight=*pMaxVisibleLight+1 ;
+				}
+				pSubLightVis[nSubLight] = pLightVis[nLight] = LIGHT_VISIBLE ;
+				
+			}
+
+
+	}// end for lights in zone
+	
+
+	//
+	///////////////////////////////////////////////////////////////////////////////////////
+
+
+
+
+
+	pZoneVis[nZone]=ZONE_VISIBLE ; // flag this zone as visible
+	m_nTotalTriangles+=m_nZoneTriangleCount[nZone] ; // just some stats for interest
+
+
+	// loop through all the portals in this zone
+	for(nPortalIndex=0 ; nPortalIndex<nPortalMax ; nPortalIndex++)
+	{
+		
+
+		nPortal=m_Q3Map->m_nZoneTouchesPortal[nZone][nPortalIndex] ;
+
+
+		if((pPortalVis[nPortal]&PORTAL_VISCHECK)==0) // portal hasn't already been checked
+		{
+
+			pPortalVis[nPortal] |= PORTAL_VISCHECK ; // flag portal as vischecked
+			AAB.setExtents(m_Q3Map->m_pPortals[nPortal].Min[0], m_Q3Map->m_pPortals[nPortal].Min[1], m_Q3Map->m_pPortals[nPortal].Min[2], m_Q3Map->m_pPortals[nPortal].Max[0], m_Q3Map->m_pPortals[nPortal].Max[1], m_Q3Map->m_pPortals[nPortal].Max[2]) ;
+		
+			if(pCamera->isVisible(AAB)) // portal is in the view frustum
+			{			
+
+				pPortalVis[nPortal] |= PORTAL_VISIBLE ; // flag portal as visible
+
+				// if the portal is open, go in and check the new zone.
+				if(m_chPortalState[nPortal]&PORTALSTATE_OPEN)
+				{
+			
+					// calculate the new frustum extents through this portal
+					PortalL=OrigL ;
+					PortalR=OrigR ;
+					PortalT=OrigT ;
+					PortalB=OrigB ;
+
+					if(CalculatePortalFrustum(pCamera, nPortal, &PortalL, &PortalR, &PortalT, &PortalB, ScaleX, ScaleY))
+					{
+
+
+
+
+
+						// Loop through all the zones attached to this portal
+						nPortalZoneMax=m_Q3Map->m_nPortalTouchesZone[nPortal][INDEX_PORTALZONECOUNT] ;
+						for(nPortalZoneIndex=0 ; nPortalZoneIndex<nPortalZoneMax ; nPortalZoneIndex++)
+						{
+							nPortalZone=m_Q3Map->m_nPortalTouchesZone[nPortal][nPortalZoneIndex] ;
+							if(pZoneVis[nPortalZone]==ZONE_UNCHECKED) // don't go back into zones we've already scanned.
+							{
+
+								pCamera->setFrustumExtents(PortalL, PortalR, PortalT, PortalB) ; // set the frustum extents
+
+								// recurse
+								//m_nRecurseCount++ ;
+								PortalScan(pCamera, nPortalZone, ScaleX, ScaleY, pZoneVis, pLightVis, pSubLightVis, pPortalVis, pMaxVisibleLight, pVisibleLightList) ;
+								//m_nRecurseCount-- ;
+
+								
+								
+							}// end if zone hasn't already been checked
+
+						}// end scanning all zones connected by this portal
+
+					}// end if frustum is still active
+
+				}// end if portal is open
+				
+			}// end if portal is in the view frustum
+		
+		}// end if portal hasn't been checked	
+
+		// reset the frustum
+		pCamera->setFrustumExtents(OrigL, OrigR, OrigT, OrigB) ; // reset the original frustum extents
+	
+	}// end for portalindex
+
+
+}
+
+// gives new frustum settings, and returns 0 if the frustum has been killed completely.
+int OgreFramework::CalculatePortalFrustum(Ogre::Camera *pCamera, int nPortal, Ogre::Real* pPortalL, Ogre::Real* pPortalR, Ogre::Real* pPortalT, Ogre::Real* pPortalB, Ogre::Real ScaleX, Ogre::Real ScaleY)
+{
+
+
+	// if the camera is actually inside the portal then don't change the frustum but return OK
+	Vector3 CamPos=pCamera->getPosition() ;
+
+	if(
+				(CamPos.x>=m_Q3Map->m_pPortals[nPortal].Min[0]) && (CamPos.x<=m_Q3Map->m_pPortals[nPortal].Max[0])
+			&&(CamPos.y>=m_Q3Map->m_pPortals[nPortal].Min[1]) && (CamPos.y<=m_Q3Map->m_pPortals[nPortal].Max[1])
+			&&(CamPos.z>=m_Q3Map->m_pPortals[nPortal].Min[2]) && (CamPos.z<=m_Q3Map->m_pPortals[nPortal].Max[2])
+		)
+		return 1 ;
+
+
+
+
+
+	int nLoop=0 ;
+	int nBehindPoint=0 ; // keep track of how many transformed points are behind the camera
+	//Real OrigL=*pPortalL ;
+	//Real OrigR=*pPortalR ;
+	//Real OrigT=*pPortalT ;
+	//Real OrigB=*pPortalB ;
+
+	Real MinX=MINMAXLIMIT ;
+	Real MaxX=-MINMAXLIMIT ;
+	Real MaxY=-MINMAXLIMIT ;
+	Real MinY=MINMAXLIMIT ;
+
+
+	// set portal corners as 8 individual points.  order is xyz, Xyz, xYz, XYz, xyZ, XyZ, xYZ, XYZ, 
+	Vector3 xyz[8] ;
+
+	
+	xyz[0].x=m_Q3Map->m_pPortals[nPortal].Min[0] ;
+	xyz[0].y=m_Q3Map->m_pPortals[nPortal].Min[1] ;
+	xyz[0].z=m_Q3Map->m_pPortals[nPortal].Min[2] ;
+
+	xyz[1].x=m_Q3Map->m_pPortals[nPortal].Max[0] ;
+	xyz[1].y=m_Q3Map->m_pPortals[nPortal].Min[1] ;
+	xyz[1].z=m_Q3Map->m_pPortals[nPortal].Min[2] ;
+
+	xyz[2].x=m_Q3Map->m_pPortals[nPortal].Min[0] ;
+	xyz[2].y=m_Q3Map->m_pPortals[nPortal].Max[1] ;
+	xyz[2].z=m_Q3Map->m_pPortals[nPortal].Min[2] ;
+
+	xyz[3].x=m_Q3Map->m_pPortals[nPortal].Max[0] ;
+	xyz[3].y=m_Q3Map->m_pPortals[nPortal].Max[1] ;
+	xyz[3].z=m_Q3Map->m_pPortals[nPortal].Min[2] ;
+
+	xyz[4].x=m_Q3Map->m_pPortals[nPortal].Min[0] ;
+	xyz[4].y=m_Q3Map->m_pPortals[nPortal].Min[1] ;
+	xyz[4].z=m_Q3Map->m_pPortals[nPortal].Max[2] ;
+
+	xyz[5].x=m_Q3Map->m_pPortals[nPortal].Max[0] ;
+	xyz[5].y=m_Q3Map->m_pPortals[nPortal].Min[1] ;
+	xyz[5].z=m_Q3Map->m_pPortals[nPortal].Max[2] ;
+
+	xyz[6].x=m_Q3Map->m_pPortals[nPortal].Min[0] ;
+	xyz[6].y=m_Q3Map->m_pPortals[nPortal].Max[1] ;
+	xyz[6].z=m_Q3Map->m_pPortals[nPortal].Max[2] ;
+
+	xyz[7].x=m_Q3Map->m_pPortals[nPortal].Max[0] ;
+	xyz[7].y=m_Q3Map->m_pPortals[nPortal].Max[1] ;
+	xyz[7].z=m_Q3Map->m_pPortals[nPortal].Max[2] ;
+
+	// transform the points to eyespace
+	for(nLoop=0 ; nLoop<8 ; nLoop++)
+		xyz[nLoop]=m_ViewMatrix*xyz[nLoop] ;//pCamera->getViewMatrix(true)*xyz[nLoop] ;
+
+	// transform the points to screen space
+	for(nLoop=0 ; nLoop<8 ; nLoop++)
+		if(xyz[nLoop].z<0) // less than zero means point is in front of camera
+		{
+			xyz[nLoop]=m_ProjectionMatrix*xyz[nLoop] ;// pCamera->getProjectionMatrix()*xyz[nLoop] ;
+			if(xyz[nLoop].x<MinX) MinX=xyz[nLoop].x ;
+			if(xyz[nLoop].x>MaxX) MaxX=xyz[nLoop].x ;
+			if(xyz[nLoop].y<MinY) MinY=xyz[nLoop].y ;
+			if(xyz[nLoop].y>MaxY) MaxY=xyz[nLoop].y ;
+
+		}
+		else // point is behind the camera
+			nBehindPoint++ ;
+		
+
+
+	
+	MinX*=ScaleX ;
+	MaxX*=ScaleX ;
+	MinY*=ScaleY ;
+	MaxY*=ScaleY ;
+	
+	// apply the stereofrustum tweak if needed
+	//float flTweak=0.0f ;
+	//if(m_flStereoFrustumTweak!=0.0f)
+	//{
+	//	MinX-=m_flStereoFrustumTweak ;
+	//	MaxX+=m_flStereoFrustumTweak ;
+	//}
+
+
+	// if we have more than three behindpoints, don't cull
+	if(nBehindPoint>3) return 1 ;
+
+	// use these to cut down the frustum
+	if(MinX>*pPortalL) *pPortalL=MinX ;
+	if(MaxX<*pPortalR) *pPortalR=MaxX ;
+	if(MinY>*pPortalB) *pPortalB=MinY ;
+	if(MaxY<*pPortalT) *pPortalT=MaxY ;
+
+
+
+	
+
+	
+
+	// check if frustum has been cut out of existence
+	// culling at this point didn't work, so this is a hack to make if function.
+	if((*pPortalL>=*pPortalR) || (*pPortalB>=*pPortalT))
+		return 0 ;
+
+
+	/*
+	if(*pPortalL>=*pPortalR)
+	{
+		*pPortalL=(*pPortalL+*pPortalR)/2.0-0.01 ;
+		*pPortalR=*pPortalL+0.02 ;
+	}
+
+	if(*pPortalB>=*pPortalT)
+	{
+		*pPortalB=(*pPortalB+*pPortalT)/2.0-0.01 ;
+		*pPortalT=*pPortalB+0.02 ;
+	}
+	*/
+
+		
+		//return 0 ;
+
+
+	return 1 ;
+}
+
+
+
+
+
+
+
+
+ /***********************************************************************************************************\
+                                               
+                                            LIGHTING SETUP
+                                                
+ \***********************************************************************************************************/
+
+
+
+
+
+void OgreFramework::initLight() 
+{
+//	char chMessage[1024] ;
+
+		// these lights only have placeholder settings to begin with, since they get moved around and reused thoughout the level.
+	
+		int nLt=0 ;
+		int nMaxLt=MAXLIGHTPERLOOP ;
+		Light *light;
+		char chLightName[1024] ;
+
+
+		
+
+		for(nLt=0 ; nLt<nMaxLt ; nLt++)
+		{
+			sprintf(chLightName, "LT%02i", nLt) ;
+			
+			light = m_pSceneMgr->createLight(chLightName);
+			light->setType(Light::LT_SPOTLIGHT);
+			light->setCastShadows(true);
+			light->setVisible(false) ;
+
+		}
+	
+}
+
+ /***********************************************************************************************************\
+                                               
+                                            CONFIG FILE
+                                                
+ \***********************************************************************************************************/
+
+void OgreFramework::ParseBZNConfig(void)
+{
+	const int KVSIZE=1024 ;
+	// make sure the config is a string
+	int nConfigPos=-1 ;
+//	int nPos=0 ;
+	int nKeyPos=0 ;
+	int nValuePos=0 ;
+
+	char chKey[KVSIZE] ;
+	char chValue[KVSIZE] ;
+	int nValue=0 ;
+
+	
+
+	while(nConfigPos<BZN_CONFIG_SIZE)
+	{
+		// scan forward to find the next key
+		while	(			
+								(++nConfigPos<BZN_CONFIG_SIZE)
+						&&	((m_chBZNConfig[nConfigPos]<'A') || (m_chBZNConfig[nConfigPos]>'z'))
+					) ;
+
+		if(nConfigPos<BZN_CONFIG_SIZE)
+		{
+
+
+
+			// find the end of the key
+			nKeyPos=0 ;
+			while	((m_chBZNConfig[nConfigPos]!=' ') && (m_chBZNConfig[nConfigPos]!='/') && (m_chBZNConfig[nConfigPos]!=0x0A) && (m_chBZNConfig[nConfigPos]!=0x0D))
+			{
+				chKey[nKeyPos++]=m_chBZNConfig[nConfigPos++] ;
+				if((nConfigPos==BZN_CONFIG_SIZE) || (nKeyPos==KVSIZE))
+					break ;
+			}
+
+			if((nConfigPos<BZN_CONFIG_SIZE) && (nKeyPos<KVSIZE))
+			{
+				chKey[nKeyPos]='\0' ; // null terminate the key
+			
+				// scan off the the number
+				nValuePos=0 ;
+				
+
+				while	((m_chBZNConfig[nConfigPos]!=0x0A) && (m_chBZNConfig[nConfigPos]!=0x0D) && (m_chBZNConfig[nConfigPos]!='/'))
+				{
+					if(
+									((m_chBZNConfig[nConfigPos]>='0') && (m_chBZNConfig[nConfigPos]<='9'))
+							||	(m_chBZNConfig[nConfigPos]=='-') 
+							||	(m_chBZNConfig[nConfigPos]=='.')
+						)
+						chValue[nValuePos++]=m_chBZNConfig[nConfigPos] ;
+
+					nConfigPos++ ;
+					if((nConfigPos==BZN_CONFIG_SIZE) || (nKeyPos==KVSIZE))
+						break ;
+				}
+
+				if((nConfigPos<BZN_CONFIG_SIZE) && (nValuePos<KVSIZE))
+				{
+					chValue[nValuePos]='\0' ; // null terminate the value
+				
+					// convert value string to a float
+					nValue=atoi(chValue) ;
+
+					// assign that value
+					if(strcmp("r_shadowmapsize", chKey)==0)		{ m_nShadowMapSize=nValue ; if(m_nShadowMapSize<2) m_nShadowMapSize=2 ; }
+					if(strcmp("r_shadowrgbsize", chKey)==0)		{ m_nShadowRGBSize=nValue ; if(m_nShadowRGBSize<2) m_nShadowRGBSize=2 ; }
+					if(strcmp("r_colouredshadow", chKey)==0)	{ m_nColouredShadow=nValue ; }
+					if(strcmp("r_renderhdr", chKey)==0)				{ m_nRenderHDR=nValue ; }
+					if(strcmp("r_maxgpuquery", chKey)==0)			{ m_nMaxGPUQuery=nValue ; if(m_nMaxGPUQuery<0) m_nMaxGPUQuery=0 ; if(m_nMaxGPUQuery>MAXGPUQUERY) m_nMaxGPUQuery=MAXGPUQUERY ; }
+
+					//sprintf(m_chBug, "Key: %s, Value: %i", chKey, nValue) ;
+					//m_pLog->logMessage(m_chBug);
+				
+				}// end found end of value
+
+
+
+
+
+
+
+			}// end found end of key
+
+
+
+
+
+		}// end found start of key
+						
+
+
+	}// end scanning whole config
+
+
+}
Index: code/branches/presentation3/src/libraries/tools/bsp/OgreFramework.hpp
===================================================================
--- code/branches/presentation3/src/libraries/tools/bsp/OgreFramework.hpp	(revision 7073)
+++ code/branches/presentation3/src/libraries/tools/bsp/OgreFramework.hpp	(revision 7073)
@@ -0,0 +1,724 @@
+/*
+===========================================================================
+Copyright (C) 2010 Jared Prince
+
+This file is part of bsp-renderer source code.
+
+bsp-renderer is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+bsp-renderer 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with bsp-renderer.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+//|||||||||||||||||||||||||||||||||||||||||||||||
+
+#ifndef OGRE_FRAMEWORK_HPP
+#define OGRE_FRAMEWORK_HPP
+
+//|||||||||||||||||||||||||||||||||||||||||||||||
+
+#include <OgreCamera.h>
+#include <OgreEntity.h>
+#include <OgreLogManager.h>
+#include <OgreOverlay.h>
+#include <OgreOverlayElement.h>
+#include <OgreOverlayManager.h>
+#include <OgreRoot.h>
+#include <OgreViewport.h>
+#include <OgreSceneManager.h>
+#include <OgreRenderWindow.h>
+#include <OgreConfigFile.h>
+#include <OgreManualObject.h>
+#include <OgreMaterialManager.h>
+#include <OgreRenderSystem.h> 
+#include <OgreOverlayContainer.h> 
+#include <OgreHardwarePixelBuffer.h>
+#include <OgreRenderQueueListener.h>
+#include <OgreHardwareOcclusionQuery.h>
+#include <OgreTextureUnitState.h>
+#include "OgreGpuCommandBufferFlush.h"
+
+
+#include "ExampleFrameListener.h"
+//#include <OgreFrameListener.h>
+//#include <OgreRenderTargetListener.h>
+
+
+#include <OIS/OIS.h>
+#include <OIS/OISEvents.h>
+#include <OIS/OISInputManager.h>
+#include <OIS/OISKeyboard.h>
+#include <OIS/OISMouse.h>
+
+#include "Q3Map.h"
+
+//#include "JarDebug.h" //!! just for debugging, remove from final build
+
+
+#define CHECKDELETE_ARRAY(x, y)		if(x) { delete [] x ; x=0 ; m_nNewCount-- ; m_nNewCheck[y]-- ;}
+#define CHECKDELETE_POINTER(x, y)  if(x) { delete x		; x=0 ; m_nNewCount-- ; m_nNewCheck[y]-- ;}
+
+enum {
+	NEW_CHECK_m_pRawBspFile=0,
+	NEW_CHECK_m_Q3Map,
+	NEW_CHECK_m_pZoneMO,
+	NEW_CHECK_m_pZoneMesh,
+	NEW_CHECK_m_pZoneEntity,
+	NEW_CHECK_m_pZoneEntityMaterialType,
+	NEW_CHECK_m_pZoneEntityMaterial_Base,
+	NEW_CHECK_m_pZoneEntityMaterial_Fast,
+	NEW_CHECK_m_pZoneEntityMaterial_Black,
+	NEW_CHECK_m_pZoneEntityMaterial_DfShColour,
+	NEW_CHECK_m_pZoneEntityMaterial_Shadow,
+	NEW_CHECK_m_pZoneEntityMaterial_ShadeFront,
+	NEW_CHECK_m_pZoneEntityMaterial_ShadeBack,
+	NEW_CHECK_m_pZoneEntityMaterial_DfShPosition,
+	NEW_CHECK_m_pZoneEntityMaterial_DfShDiffuse,
+	NEW_CHECK_m_pZoneEntityMaterial_DfShFuncTNB,
+	NEW_CHECK_m_pZoneEntityMaterial_DfShSpecular,
+	NEW_CHECK_m_pZoneEntityMaterial_DfShEmissive,
+	NEW_CHECK_m_pZoneEntityMaterial_DfShMix,
+	NEW_CHECK_pVertIndex,
+	NEW_CHECK_m_pEntityInfo,
+	NEW_CHECK_m_pVisibleEntity,
+	NEW_CHECK_m_pFrustumEntity,
+	MAX_NEW_CHECK
+} ;
+
+#define MAXFRAME 1000 // debugging/testing, copy out data on a per-frame basis
+
+#define MAXGPUQUERY 4
+
+#define MAXVISIBLELIGHT	128 // most shadow lights ever visible.
+#define MAXLIGHTPERLOOP	1 // most shadow lights rendered per deferred shader sub-loop.  So shadow lights are rendered in lots of 4// no longer used
+
+#define NEARCLIP 1			// camera default near clip distance
+#define FARCLIP 10000		// camera default far clip distance
+
+#define CAMERA_EPSILON  0.000001f // small value to check for up/down cameras that would break with Ogre's fixed Yaw.
+
+#define IGNORE_LIGHT		-1
+
+enum GoggleMode
+{
+	GOGGLE_MODE_OFF = 0,
+	GOGGLE_MODE_TEST,
+	GOGGLE_MODE_ON,
+	GOGGLE_MODE_MAX
+} ;
+
+#define MAT_OPAQUE		1				// Typical solid stuff.  Can have emissive parts.
+#define MAT_TRANS			2				// Unused
+#define MAT_GLOW			4				// Only rendered in the deferred shader emmisive pass.  Used to create the hdr bloom for light points.
+#define MAT_GEL				8				// Translucent, colours spotlights that pass through it.
+#define MAT_ALPHAPF		16			// Same as MAT_OPAQUE, but with alpha testing.
+#define MAT_LAMP			32			// The bounding boxes of deferred shading lights (lamps). Rendered as the last phase in deferred shading.
+
+#define BZN_CONFIG_SIZE	1024
+
+class rQueueListener: public Ogre::RenderQueueListener
+{
+public:
+	
+	rQueueListener() {}
+	~rQueueListener() {}
+
+	Ogre::HardwareOcclusionQuery* Query[MAXGPUQUERY] ;
+
+	void renderQueueStarted(Ogre::uint8 queueGroupId, const Ogre::String &invocation, bool &skipThisInvocation) {}
+	void renderQueueEnded(Ogre::uint8 queueGroupId, const Ogre::String &invocation, bool &skipThisInvocation) {}
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#define MAX_SUBMESH						16	// most submeshes allowed in a mesh.
+#define MAX_ZONEPERENTITY			8		// the most zones an entity can touch at once.
+#define INDEX_ENTITYZONECOUNT	8		// index to value that holds the count of how many zones this entity touches
+#define MAX_LIGHTPERENTITY		64	// most shadowing lights that an entity can be lit by
+#define INDEX_LIGHTPERENTITY	64	// index to value
+typedef struct
+{
+	int Active ;
+	int Visible ;
+	int Frustum ;
+	int LightTouch ;
+
+	int TriangleCount ;
+
+	Ogre::Vector3 Postition ;
+	Ogre::Quaternion Orientation ;
+
+	Ogre::AxisAlignedBox AABB ;
+	Ogre::Vector3 Centre ;
+	float AABBMin[3] ;
+	float AABBMax[3] ;
+
+	unsigned short Zone[MAX_ZONEPERENTITY+1] ;		// the zones this entity touches.  Last value is a count of how many zones were touched.
+	unsigned short Light[MAX_LIGHTPERENTITY+1] ;	// the shadow casting spotlights this entity touches.  Last value is a count of how many lights.
+
+	int MaxSubMesh ;
+	int SubMeshMaterialType[MAX_SUBMESH] ;
+
+	Ogre::Entity*				pEnt ;
+	Ogre::SceneNode*    pMasterNode ;
+
+	Ogre::MaterialPtr		Material_Base[MAX_PROJECTORTEX][MAX_SUBMESH] ;
+	Ogre::MaterialPtr		Material_Fast[MAX_PROJECTORTEX][MAX_SUBMESH] ;
+
+	Ogre::MaterialPtr		Material_Black[MAX_SUBMESH] ;
+
+	Ogre::MaterialPtr		Material_DfShColour[MAX_SUBMESH] ;
+	Ogre::MaterialPtr		Material_Shadow[MAX_SUBMESH] ;
+	Ogre::MaterialPtr		Material_ShadeFront[MAX_SUBMESH] ;
+	Ogre::MaterialPtr		Material_ShadeBack[MAX_SUBMESH] ;
+	Ogre::MaterialPtr		Material_DfShPosition[MAX_SUBMESH] ;
+	Ogre::MaterialPtr		Material_DfShDiffuse[MAX_SUBMESH] ;
+	Ogre::MaterialPtr		Material_DfShFuncTNB[MAX_SUBMESH] ;
+	Ogre::MaterialPtr		Material_DfShSpecular[MAX_SUBMESH] ;
+	Ogre::MaterialPtr		Material_DfShEmissive[MAX_SUBMESH] ;
+	Ogre::MaterialPtr		Material_DfShMix[MAX_SUBMESH] ;
+}
+ENTITYINFO;
+
+typedef struct
+{
+
+	int Type ;
+
+	Ogre::ParticleSystem*	PSystem ;
+	Ogre::SceneNode*			PNode ;
+
+}
+PARTICLEINFO;
+
+//|||||||||||||||||||||||||||||||||||||||||||||||
+
+class OgreFramework : public Ogre::Singleton<OgreFramework>, OIS::KeyListener, OIS::MouseListener, public RenderTargetListener
+{
+public:
+	OgreFramework();
+	~OgreFramework();
+
+	int m_nNewCount ;
+	int m_nNewCheck[MAX_NEW_CHECK] ;
+
+	int m_nStartTime ;
+	int m_nTime ;
+
+//	CJarDebug OFBug ; //!! just for debugging, remove from final build
+	char m_chBug[10240] ;
+	int m_nFrameTime[MAXFRAME][10] ;
+	int m_nFrame ;
+	char m_chFrame[1024] ;
+	int m_nGotInput ;
+
+	void initOgre(Ogre::String wndTitle, OIS::KeyListener *pKeyListener = 0, OIS::MouseListener *pMouseListener = 0);
+	void AdditionalSetup() ;
+
+	void updateOgre(double timeSinceLastFrame);
+	void updateStats();
+	void moveCamera();
+	void getInput();
+
+	bool isOgreToBeShutDown()const{return m_bShutDownOgre;}  
+
+	bool keyPressed(const OIS::KeyEvent &keyEventRef);
+	bool keyReleased(const OIS::KeyEvent &keyEventRef);
+
+	bool mouseMoved(const OIS::MouseEvent &evt);
+	bool mousePressed(const OIS::MouseEvent &evt, OIS::MouseButtonID id); 
+	bool mouseReleased(const OIS::MouseEvent &evt, OIS::MouseButtonID id);
+	
+	Ogre::Root*					m_pRoot;
+	Ogre::SceneManager*			m_pSceneMgr;
+	Ogre::RenderWindow*			m_pRenderWnd;
+	Ogre::Camera*				m_pCamera;
+	Ogre::Viewport*				m_pViewport;
+	Ogre::Log*					m_pLog;
+	Ogre::Timer*				m_pTimer;
+
+	Ogre::Camera* m_pCameraCopy ;
+	Ogre::Camera* m_pCameraNoJitter ;
+
+	float m_flAspectRatio ;
+	float m_flFOV ;
+
+
+	//RawFilePtr m_pRawBspFile ;	bool m_bRawBspFileIsLoaded ;
+
+	char *m_pRawBspFile ;	
+	bool m_bRawBspFileIsLoaded ;
+	
+	OIS::InputManager*			m_pInputMgr;
+	OIS::Keyboard*				m_pKeyboard;
+	OIS::Mouse*					m_pMouse;
+
+	int nMap ;
+	Ogre::String m_MapName ;
+	int m_nLoadToggle ;
+
+	int m_nJumpToggle ;
+	int m_nJumpVal;
+
+	int m_nToggle ;
+
+	void UnloadMap(bool bShutdown) ;
+	int LoadMap(void) ;
+	int m_nMapLoaded ;
+
+	int m_nDebugLightBox ;
+	int m_nZoneCullingActive ;
+	int m_nPortalDebug ;
+	int m_nPortalToggle ;
+	int m_nPortalState ; 
+
+	// Add this to OgreFramework and initialize it from 0 to MAXGPUQUERY
+	// If you're good you'll make it private and add some get/set functions, 
+	// I just made it public for now.
+
+	// A value of 0 means don't mess with the GPU buffers at all.  There
+	// might be some systems where the queries cause problems, so let the user 
+	// deactivate the queries completely if desired.
+
+	// A value of 1 means we flush every frame, so no GPU command buffering.
+	// This is good for low FPS because even just 1 buffer gives noticable
+	// input lag.  However users with high FPS can afford a few buffers.
+	
+	int m_nMaxGPUQuery ;
+
+
+	int m_nDebugA ;
+	int m_nDebugB ;
+	int m_nDebugC ;
+	int m_nVisibleLightCount ;
+	int m_nVisibleZoneCount ;
+
+	float m_flDebugMatrix[4][4] ;
+	
+	int m_nRecurseCount ;
+	char m_chDebug[10240] ;
+	float m_flDebug0 ;
+	float m_flDebug1 ;
+	float m_flDebug2 ;
+	float m_flDebug3 ;
+	float m_flDebug4 ;
+	float m_flDebug5 ;
+
+
+	
+	Ogre::ManualObject* m_pGoggleL ;
+	Ogre::ManualObject* m_pGoggleR ;
+	Ogre::SceneNode*		m_pNodeGoggles ;
+	int m_nGoggleMode ;
+	float m_flGoggleAspectRatio	;
+	float m_flGoggleZPos ;
+	float m_flGoggleXGap ;
+	float m_flGoggleXScale ;
+	float m_flGoggleYScale ;
+	int m_nGoggleTestImage ;
+	void DestroyGoggles() ;
+	int CreateGogglesTestImage() ;
+	int CreateGoggles() ;
+
+
+	void UpdateRenderTargets() ;
+
+	double m_GameTime ;
+
+	char m_chBZNConfig[BZN_CONFIG_SIZE] ;
+	void ParseBZNConfig(void) ;
+
+	void FinalShutdown(void) ;
+
+private:
+	OgreFramework(const OgreFramework&);
+	OgreFramework& operator= (const OgreFramework&);
+
+	Ogre::Overlay*				m_pDebugOverlay;
+	Ogre::Overlay*				m_pInfoOverlay;
+	int							m_iNumScreenShots;
+
+	bool						m_bShutDownOgre;
+	
+	Ogre::Vector3				m_TranslateVector;
+	Ogre::Real					m_MoveSpeed; 
+	Ogre::Degree				m_RotateSpeed; 
+	float						m_MoveScale; 
+	Ogre::Degree				m_RotScale;
+
+	int m_nKeyDown_Shift ;
+	int m_nKeyDown_Ctrl ;
+
+
+	
+
+
+	//RawFileManager *mRawFileManager;
+
+	char* m_TempMem ;
+	Q3Map *m_Q3Map;
+
+
+	Ogre::SceneNode*			m_pCubeNode;
+	Ogre::Entity*				m_pCubeEntity;
+
+	Ogre::SceneNode *m_pOpaqueNode[MAX_ZONE] ;
+	int m_nOpaqueNodeUsed[MAX_ZONE] ; 
+	Ogre::SceneNode *m_pTransNode[MAX_ZONE] ;
+	int m_nTransNodeUsed[MAX_ZONE] ;
+	Ogre::SceneNode *m_pLampNode[MAX_ZONE] ;
+	int m_nLampNodeUsed[MAX_ZONE] ;
+	Ogre::SceneNode *m_pGlowNode[MAX_ZONE] ;
+	int m_nGlowNodeUsed[MAX_ZONE] ;
+
+	Ogre::Matrix4	m_ViewMatrix ;
+	Ogre::Matrix4	m_ProjectionMatrix ;
+
+	Ogre::ManualObject** m_pZoneMO ; // the Zone's manualObject array
+	Ogre::MeshPtr* m_pZoneMesh ;
+	Ogre::Entity** m_pZoneEntity ;
+	int* m_pZoneEntityMaterialType ;
+
+	Ogre::MaterialPtr* m_pZoneEntityMaterial_Base[MAX_PROJECTORTEX] ;
+	Ogre::MaterialPtr* m_pZoneEntityMaterial_Fast[MAX_PROJECTORTEX] ;
+
+	Ogre::MaterialPtr* m_pZoneEntityMaterial_Black ;
+
+	Ogre::MaterialPtr* m_pZoneEntityMaterial_DfShColour ;
+	Ogre::MaterialPtr* m_pZoneEntityMaterial_Shadow ;
+	Ogre::MaterialPtr* m_pZoneEntityMaterial_ShadeFront ;
+	Ogre::MaterialPtr* m_pZoneEntityMaterial_ShadeBack ;
+	Ogre::MaterialPtr* m_pZoneEntityMaterial_DfShPosition ;
+	Ogre::MaterialPtr* m_pZoneEntityMaterial_DfShDiffuse ;
+	//Ogre::MaterialPtr* m_pZoneEntityMaterial_DSNormal ;
+	Ogre::MaterialPtr* m_pZoneEntityMaterial_DfShFuncTNB ;
+	Ogre::MaterialPtr* m_pZoneEntityMaterial_DfShSpecular ;
+	Ogre::MaterialPtr* m_pZoneEntityMaterial_DfShEmissive ;
+	//Ogre::MaterialPtr* m_pZoneEntityMaterial_DfShData ;
+	Ogre::MaterialPtr* m_pZoneEntityMaterial_DfShMix ;
+
+	Ogre::MaterialPtr* m_pZoneEntityMaterial_BlurA ;
+
+
+	/////////////////////////////////////////////////////////////////////////////////
+
+
+
+
+	Ogre::MeshPtr* m_pGameMesh ;
+	ENTITYINFO* m_pEntityInfo ;
+	int* m_pVisibleEntity ; // entities that are in a visible zone
+	int* m_pFrustumEntity ; // entities that are in a visible zone and also in the view frustum
+
+	int m_nMaxGameMesh ;
+	int m_nMaxEntity ;
+	int m_nMaxVisibleEntity ;
+	int m_nMaxFrustumEntity ;
+	int m_nVisibleEntityTriangleCount ;
+	int m_nFrustumEntityTriangleCount ;
+	int m_nTotalTriangles ;
+	int m_nZoneTriangleCount[MAX_ZONE] ;
+
+	/////////////////////////////////////////////////////////////////////////////////
+
+	// for debugging/testing, make light volumes into meshes
+	Ogre::ManualObject** m_pLightMO ; // the manualObject array
+	int AddLightCullingBoxes(void) ;
+
+	// for debugging/testing, make portal volumes into meshes
+	Ogre::ManualObject** m_pPortalMO ; // the manualObject array
+	Ogre::Node** m_pPortalNode ; // the node of this portal
+	int AddPortalBoxes(void) ;
+
+	/////////////////////////////////////////////////////////////////////////////////
+
+	
+	
+
+
+	void SetupResourceLocations() ;
+	
+
+
+	float m_flStereoFrustumTweak ; // in stereoscopic mode the portal frustums might be too tight, so enlarge them a bit.
+
+
+	bool m_IsOpenGL ;
+
+	
+	void initLight() ;
+
+	Ogre::Matrix4 CreateTextureViewProjectionMatrix(Ogre::Camera* pCamera) ;
+	bool SetupSingleVisibleLightAndShadowCamera(int nLt, char* pLightVis, unsigned short* pVisibleLightList, Ogre::Camera* pCamera) ;
+	
+
+	int SetupParticles() ;
+	Ogre::ParticleSystem*		m_ps;
+	Ogre::SceneNode*				m_pParticleNode;
+	
+	int m_nMaxParticleSystem ;
+	
+	
+	int ConstructMapFromTriangles(void) ;
+	
+	int SetupGameEntities(void) ;
+	int SetupEntity(int nEntity, char *chMeshName) ;
+	
+	
+
+	int m_nKeyToggle[OIS::KC_MEDIASELECT+1] ; // for preventing multiple key triggers when needed.
+
+	int m_nFlashLight ;
+	
+
+	
+
+
+
+
+	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+	// left/mono visibility info
+	char m_chZoneVisL[MAX_ZONE] ;
+	char m_chLightVisL[MAX_LIGHT] ;
+	char m_chSubLightVisL[MAX_SUBLIGHT] ;
+	char m_chPortalVisL[MAX_PORTAL] ;
+	int  m_nMaxVisibleZoneL ;
+	unsigned short m_uVisibleZoneListL[MAX_ZONE] ;				// if MAX_ZONE goes above 65536 this will need to change to an int 
+	int  m_nMaxVisibleLightL ;
+	unsigned short m_uVisibleLightListL[MAXVISIBLELIGHT]; // if MAX_LIGHT goes above 65536 this will need to change to an int
+	int m_nCameraZoneL ; // zone player left eye is in.
+	
+	// right visibility info
+	char m_chZoneVisR[MAX_ZONE] ;
+	char m_chLightVisR[MAX_LIGHT] ;
+	char m_chSubLightVisR[MAX_SUBLIGHT] ;
+	char m_chPortalVisR[MAX_PORTAL] ;
+	int  m_nMaxVisibleZoneR ;
+	unsigned short m_uVisibleZoneListR[MAX_ZONE] ;				// if MAX_ZONE goes above 65536 this will need to change to an int 
+	int  m_nMaxVisibleLightR ;
+	unsigned short m_uVisibleLightListR[MAXVISIBLELIGHT]; // if MAX_LIGHT goes above 65536 this will need to change to an int 
+	int m_nCameraZoneR ; // zone player right eye is in.
+	
+	int m_nCameraZone ;  // zone player is in.
+	
+	
+
+
+
+	char* m_pGlobal_ZoneVis ;
+	char* m_pGlobal_LightVis ;
+	unsigned short* m_pGlobal_VisibleZoneList ;
+	int*  m_pGlobal_MaxVisibleZone ;
+	unsigned short* m_pGlobal_VisibleLightList ;
+	int*  m_pGlobal_MaxVisibleLight ;
+
+	char m_chPortalState[MAX_PORTAL] ;
+	
+	int  m_nMaxVisibleLight ;
+
+
+
+
+
+
+
+	int m_nZoneMOStart[MAX_ZONE+1] ; // index into m_pZoneMO for each zone, since each material within a zone is another manualObject.
+
+	void CalculateZoneVisibility(Ogre::Camera* pCamera, int *pCameraZone, char* pZoneVis, char* pLightVis, char* pSubLightVis, char* pPortalVis, int* pMaxVisibleLight, unsigned short* pVisibleLightList) ;
+	void SetupEntityZones(void) ;
+	void CalculateEntityVisibility(Ogre::Camera* pCamera, char* pZoneVis) ;
+
+	void SetAllLightsOff(void) ;
+	void SetSingleVisibleLight(int nLt, bool bOn) ;
+	void UpdateVisibleZoneList(char* pZoneVis, unsigned short* pVisibleZoneList, int* pMaxVisibleZone) ;
+	
+	void SetZoneNodeAttachments(unsigned short* pVisibleZoneList, int* pMaxVisibleZone, int nMaterialFlags) ;
+	void SetZoneNodeAttachments(unsigned short* pVisibleZoneList, int* pMaxVisibleZone, int nMaterialFlags, int nLight) ;
+	bool LightTouchesZone(int nLight, int nZone) ;
+
+	void PortalScan(Ogre::Camera *pCamera, int nZone, Ogre::Real ScaleX, Ogre::Real ScaleY, char* pZoneVis, char* pLightVis, char* pSubLightVis, char* pPortalVis, int* pMaxVisibleLight, unsigned short* pVisibleLightList) ;
+	int m_nOriginalZoneOpenPortals ;
+	void CheckMultiZoneLights(int nCameraZone, char* pZoneVis, char* pLightVis, char* pSubLightVis) ;
+	void AddZonesFromMultiZoneLights(char* pZoneVis, char* pLightVis, char* pSubLightVis) ;
+	int CalculatePortalFrustum(Ogre::Camera *pCamera, int nPortal, Ogre::Real* pPortalL, Ogre::Real* pPortalR, Ogre::Real* pPortalT, Ogre::Real* pPortalB, Ogre::Real ScaleX, Ogre::Real ScaleY) ;
+
+
+
+
+	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+	
+
+
+
+	//int ApplyCGDefines(void) ;
+	
+
+	
+
+	int m_nSpeedRender ;
+	int m_nRenderHDR ;
+	int m_nColouredShadow ;
+	int m_nShadowMapSize ;
+	int m_nShadowRGBSize ;
+
+	int m_nDisplayInfoMode ;
+	
+	///////////////////////////////////////////////////////////////////////////////////////////
+	// render to texture quasi-deferred shading stuff
+	
+	void CreateRTTAssets() ;
+	void DestroyRTTAssets() ;
+	int m_nRTTAssetsExist ;
+
+	// Shadow
+	Rectangle2D*					miniScreen_Shadow;
+	Ogre::SceneNode*			miniScreenNode_Shadow;
+	Ogre::TexturePtr			RTT_Texture_Shadow ;
+	Ogre::RenderTexture*	renderTexture_Shadow ;
+	Ogre::MaterialPtr			RTT_Mat_Shadow ;
+	Ogre::Technique*			RTT_Technique_Shadow ;
+
+	// ShadeFront
+	Rectangle2D*					miniScreen_ShadeFront;
+	Ogre::SceneNode*			miniScreenNode_ShadeFront;
+	Ogre::TexturePtr			RTT_Texture_ShadeFront ;
+	Ogre::RenderTexture*	renderTexture_ShadeFront ;
+	Ogre::MaterialPtr			RTT_Mat_ShadeFront ;
+	Ogre::Technique*			RTT_Technique_ShadeFront ;
+
+	// ShadeBack
+	Rectangle2D*					miniScreen_ShadeBack;
+	Ogre::SceneNode*			miniScreenNode_ShadeBack;
+	Ogre::TexturePtr			RTT_Texture_ShadeBack ;
+	Ogre::RenderTexture*	renderTexture_ShadeBack ;
+	Ogre::MaterialPtr			RTT_Mat_ShadeBack ;
+	Ogre::Technique*			RTT_Technique_ShadeBack ;
+
+	// DfShPosition
+	Rectangle2D*					miniScreen_DfShPosition;
+	SceneNode*						miniScreenNode_DfShPosition;
+	TexturePtr						RTT_Texture_DfShPosition ;
+	RenderTexture*				renderTexture_DfShPosition ;
+	MaterialPtr						RTT_Mat_DfShPosition ;
+	Technique*						RTT_Technique_DfShPosition ;
+	RenderTargetListener* Listener_DfShPosition ;
+
+	// DfShDiffuse
+	Rectangle2D*					miniScreen_DfShDiffuse;
+	Ogre::SceneNode*			miniScreenNode_DfShDiffuse;
+	Ogre::TexturePtr			RTT_Texture_DfShDiffuse ;
+	Ogre::RenderTexture*	renderTexture_DfShDiffuse ;
+	Ogre::MaterialPtr			RTT_Mat_DfShDiffuse ;
+	Ogre::Technique*			RTT_Technique_DfShDiffuse ;
+
+	/*
+	// DSNormal // UNUSED
+	Rectangle2D*					miniScreen_DSNormal;
+	Ogre::SceneNode*			miniScreenNode_DSNormal;
+	Ogre::TexturePtr			RTT_Texture_DSNormal ;
+	Ogre::RenderTexture*	renderTexture_DSNormal ;
+	Ogre::MaterialPtr			RTT_Mat_DSNormal ;
+	Ogre::Technique*			RTT_Technique_DSNormal ;
+	*/
+
+	// DfShFuncTNB
+	Rectangle2D*					miniScreen_DfShFuncTNB;
+	Ogre::SceneNode*			miniScreenNode_DfShFuncTNB;
+	Ogre::TexturePtr			RTT_Texture_DfShFuncTNB ;
+	Ogre::RenderTexture*	renderTexture_DfShFuncTNB ;
+	Ogre::MaterialPtr			RTT_Mat_DfShFuncTNB ;
+	Ogre::Technique*			RTT_Technique_DfShFuncTNB ;
+
+	// DfShSpecular
+	Rectangle2D*					miniScreen_DfShSpecular;
+	Ogre::SceneNode*			miniScreenNode_DfShSpecular;
+	Ogre::TexturePtr			RTT_Texture_DfShSpecular ;
+	Ogre::RenderTexture*	renderTexture_DfShSpecular ;
+	Ogre::MaterialPtr			RTT_Mat_DfShSpecular ;
+	Ogre::Technique*			RTT_Technique_DfShSpecular ;
+
+	// DfShEmissive
+	Rectangle2D*					miniScreen_DfShEmissive;
+	Ogre::SceneNode*			miniScreenNode_DfShEmissive;
+	Ogre::TexturePtr			RTT_Texture_DfShEmissive ;
+	Ogre::RenderTexture*	renderTexture_DfShEmissive ;
+	Ogre::MaterialPtr			RTT_Mat_DfShEmissive ;
+	Ogre::Technique*			RTT_Technique_DfShEmissive ;
+
+	// DfShLamp
+	Rectangle2D*					miniScreen_DfShLamp;
+	Ogre::SceneNode*			miniScreenNode_DfShLamp;
+	Ogre::TexturePtr			RTT_Texture_DfShLamp ;
+	Ogre::RenderTexture*	renderTexture_DfShLamp ;
+	Ogre::MaterialPtr			RTT_Mat_DfShLamp ;
+	Ogre::Technique*			RTT_Technique_DfShLamp ;
+
+	/*
+	// DfShData
+	Rectangle2D*					miniScreen_DfShData;
+	Ogre::SceneNode*			miniScreenNode_DfShData;
+	Ogre::TexturePtr			RTT_Texture_DfShData ;
+	Ogre::RenderTexture*	renderTexture_DfShData ;
+	Ogre::MaterialPtr			RTT_Mat_DfShData ;
+	Ogre::Technique*			RTT_Technique_DfShData ;
+	*/
+
+	// DfShMix
+	Rectangle2D*					miniScreen_DfShMix;
+	Ogre::SceneNode*			miniScreenNode_DfShMix;
+	Ogre::TexturePtr			RTT_Texture_DfShMix ;
+	Ogre::RenderTexture*	renderTexture_DfShMix ;
+	Ogre::MaterialPtr			RTT_Mat_DfShMix ;
+	Ogre::Technique*			RTT_Technique_DfShMix ;
+
+	
+	// DfShTemp
+	Rectangle2D*					miniScreen_DfShTemp;
+	Ogre::SceneNode*			miniScreenNode_DfShTemp;
+	Ogre::TexturePtr			RTT_Texture_DfShTemp ;
+	Ogre::RenderTexture*	renderTexture_DfShTemp ;
+	Ogre::MaterialPtr			RTT_Mat_DfShTemp ;
+	Ogre::Technique*			RTT_Technique_DfShTemp ;
+
+	Rectangle2D*					miniScreen_DfShMaster;
+	Ogre::SceneNode*			miniScreenNode_DfShMaster;
+
+	int m_nRTTWidth ;
+	int m_nRTTHeight ;
+
+	// blur for hdr
+	Rectangle2D*					miniScreen_BlurA;
+	Ogre::SceneNode*			miniScreenNode_BlurA;
+	Ogre::TexturePtr			RTT_Texture_BlurA ;
+	Ogre::RenderTexture*	renderTexture_BlurA ;
+	Ogre::MaterialPtr			RTT_Mat_BlurA ;
+	Ogre::Technique*			RTT_Technique_BlurA ;
+
+	Rectangle2D*					miniScreen_BlurB;
+	Ogre::SceneNode*			miniScreenNode_BlurB;
+	Ogre::TexturePtr			RTT_Texture_BlurB ;
+	Ogre::RenderTexture*	renderTexture_BlurB ;
+	Ogre::MaterialPtr			RTT_Mat_BlurB ;
+	Ogre::Technique*			RTT_Technique_BlurB ;
+	
+	
+
+	Ogre::MaterialPtr			pClearMaterial ;
+
+
+};
+
+//|||||||||||||||||||||||||||||||||||||||||||||||
+
+#endif 
+
+//|||||||||||||||||||||||||||||||||||||||||||||||
Index: code/branches/presentation3/src/libraries/tools/bsp/OgreGpuCommandBufferFlush.cpp
===================================================================
--- code/branches/presentation3/src/libraries/tools/bsp/OgreGpuCommandBufferFlush.cpp	(revision 7073)
+++ code/branches/presentation3/src/libraries/tools/bsp/OgreGpuCommandBufferFlush.cpp	(revision 7073)
@@ -0,0 +1,105 @@
+#include "OgreGpuCommandBufferFlush.h"
+#include "OgreRoot.h"
+#include "OgreRenderSystem.h"
+#include "OgreHardwareOcclusionQuery.h"
+
+namespace Ogre
+{
+	//---------------------------------------------------------------------
+	GpuCommandBufferFlush::GpuCommandBufferFlush()
+		: mUseOcclusionQuery(true)
+		, mMaxQueuedFrames(2)
+		, mCurrentFrame(0)
+		, mStartPull(false)
+		, mStarted(false)
+	{
+
+	}
+	//---------------------------------------------------------------------
+	GpuCommandBufferFlush::~GpuCommandBufferFlush()
+	{
+		stop();
+	}
+	//---------------------------------------------------------------------
+	void GpuCommandBufferFlush::start(size_t maxQueuedFrames)
+	{
+		if (!Root::getSingletonPtr() || !Root::getSingletonPtr()->getRenderSystem())
+			return;
+
+		stop();
+		mMaxQueuedFrames = maxQueuedFrames;
+		RenderSystem* rsys = Root::getSingleton().getRenderSystem();
+		mUseOcclusionQuery = rsys->getCapabilities()->hasCapability(RSC_HWOCCLUSION);
+
+		if (mUseOcclusionQuery)
+		{
+			for (size_t i = 0; i < mMaxQueuedFrames; ++i)
+			{
+				HardwareOcclusionQuery* hoq = rsys->createHardwareOcclusionQuery();
+				mHOQList.push_back(hoq);
+			}
+		}
+
+		mCurrentFrame = 0;
+		mStartPull = false;
+
+		Root::getSingleton().addFrameListener(this);
+
+		mStarted = true;
+
+	}
+	//---------------------------------------------------------------------
+	void GpuCommandBufferFlush::stop()
+	{
+		if (!mStarted || !Root::getSingletonPtr() || !Root::getSingletonPtr()->getRenderSystem())
+			return;
+
+		RenderSystem* rsys = Root::getSingleton().getRenderSystem();
+		for (HOQList::iterator i = mHOQList.begin(); i != mHOQList.end(); ++i)
+		{
+			rsys->destroyHardwareOcclusionQuery(*i);
+		}
+		mHOQList.clear();
+
+		Root::getSingleton().removeFrameListener(this);
+
+		mStarted = false;
+
+	}
+	//---------------------------------------------------------------------
+	bool GpuCommandBufferFlush::frameStarted(const FrameEvent& evt)
+	{
+		if (mUseOcclusionQuery)
+		{
+
+			mHOQList[mCurrentFrame]->beginOcclusionQuery();
+
+		}
+
+		return true;
+	}
+	//---------------------------------------------------------------------
+	bool GpuCommandBufferFlush::frameEnded(const FrameEvent& evt)
+	{
+		if (mUseOcclusionQuery)
+		{
+			mHOQList[mCurrentFrame]->endOcclusionQuery();
+		}
+		mCurrentFrame = (mCurrentFrame + 1) % mMaxQueuedFrames;
+		// If we've wrapped around, time to start pulling
+		if (mCurrentFrame == 0)
+			mStartPull = true;
+
+		if (mStartPull)
+		{
+			unsigned int dummy;
+			mHOQList[mCurrentFrame]->pullOcclusionQuery(&dummy);
+		}
+
+		return true;
+	}
+	//---------------------------------------------------------------------
+
+
+}
+
Index: code/branches/presentation3/src/libraries/tools/bsp/OgreGpuCommandBufferFlush.h
===================================================================
--- code/branches/presentation3/src/libraries/tools/bsp/OgreGpuCommandBufferFlush.h	(revision 7073)
+++ code/branches/presentation3/src/libraries/tools/bsp/OgreGpuCommandBufferFlush.h	(revision 7073)
@@ -0,0 +1,43 @@
+#ifndef __GPUCOMMANDBUFFERFLUSH_H__
+#define __GPUCOMMANDBUFFERFLUSH_H__
+
+#include "OgrePrerequisites.h"
+#include "OgreFrameListener.h"
+
+namespace Ogre
+{
+	
+	/** Helper class which can assist you in making sure the GPU command
+		buffer is regularly flushed, so in cases where the CPU is outpacing the
+		GPU we do not hit a situation where the CPU suddenly has to stall to 
+		wait for more space in the buffer.
+	*/
+	class GpuCommandBufferFlush : public FrameListener
+	{
+	protected:
+		bool mUseOcclusionQuery;
+		typedef std::vector<HardwareOcclusionQuery*> HOQList;
+		HOQList mHOQList;
+		size_t mMaxQueuedFrames;
+		size_t mCurrentFrame;
+		bool mStartPull;
+		bool mStarted;
+
+	public:
+		GpuCommandBufferFlush();
+		virtual ~GpuCommandBufferFlush();
+
+		void start(size_t maxQueuedFrames = 2);
+		void stop();
+		bool frameStarted(const FrameEvent& evt);
+		bool frameEnded(const FrameEvent& evt);
+
+	};
+
+
+}
+
+
+#endif
+
+
Index: code/branches/presentation3/src/libraries/tools/bsp/Q3Map.cpp
===================================================================
--- code/branches/presentation3/src/libraries/tools/bsp/Q3Map.cpp	(revision 7073)
+++ code/branches/presentation3/src/libraries/tools/bsp/Q3Map.cpp	(revision 7073)
@@ -0,0 +1,4881 @@
+/*
+===========================================================================
+Copyright (C) 2008 Daniel Örstadius
+Copyright (C) 2009 Jared Prince
+
+This file is part of bsp-renderer source code.
+
+bsp-renderer is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+bsp-renderer 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with bsp-renderer.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+// Q3Map.cpp -- handles the map data
+
+#include <string>
+#include <math.h>
+
+#include "Q3Map.h"
+#include "Q3Map_misc.h"
+
+
+//temp
+//#include <io.h>
+//#include <fcntl.h>      /* Needed only for _O_RDWR definition */
+//#include <sys/stat.h>
+
+Q3Map::Q3Map()
+{
+	m_nDebugA=0 ;
+
+	m_nNewCount=0 ;
+	
+  m_pTexturesOrig=NULL ;
+  m_pFaces=NULL ;
+  m_pVertices=NULL ;
+  m_pMeshVerts=NULL ;
+  m_pLeafs=NULL ;
+  m_pLeafFaces=NULL ;
+  m_pPlanes=NULL ;
+  m_pNodes=NULL ;
+
+	//m_VisData->vecs=NULL ; // can't NULL this because m_VisData doesn't exist yet!
+	m_VisData=NULL ;
+   
+  m_pBrushes=NULL ;
+  m_pLeafBrushes=NULL ;
+  m_pBrushSides=NULL ;
+  m_pLightMaps=NULL ;
+  m_pEntities=NULL ;
+
+	m_pSubZones=NULL ;
+	m_pPortals=NULL ;
+	//////////////////////////////////////////////////////
+
+	// initiallize our triangle memory management
+	m_nTriangleSize=0 ;
+	m_pTriangleMem=NULL ;
+	m_pTriangle=NULL ;
+	m_nTriangleMax=0 ;
+	m_nTriangleLimit=0 ;
+
+	m_nVertexSize=0 ;
+	m_pVertexMem=NULL ;
+	m_pVertex=NULL ;
+	m_nVertexMax=0 ;
+	m_nVertexLimit=0 ;
+
+	m_nLightSize=0 ;
+	m_pLightMem=NULL ;
+	m_pLight=NULL ;
+	m_nLightMax=0 ;
+	m_nLightLimit=0 ;
+	m_nMaxMultiZoneLight=0 ;
+
+	m_nLampSize=0 ;
+	m_pLampMem=NULL ;
+	m_pLamp=NULL ;
+	m_nLampMax=0 ;
+	m_nLampLimit=0 ;
+
+	m_nTextureSize=0 ;
+	m_pTextureMem=NULL ;
+	m_pTexture=NULL ;
+	m_nTextureMax=0 ;
+	m_nTextureLimit=0 ;
+
+	m_nTexLampSize=0 ;
+	m_pTexLampMem=NULL ;
+	m_pTexLamp=NULL ;
+	m_nTexLampMax=0 ;
+	m_nTexLampLimit=0 ;
+
+	m_BspFaces=NULL;
+
+	m_pTransTexture=NULL ;
+
+
+//	Q3Bug.LogInit() ;
+
+}
+
+Q3Map::~Q3Map()
+{
+//	Q3Bug.LogSave("Q3Bug.log") ;
+
+	DELETE_ARRAY(m_pTransTexture) ;
+	//DELETE_ARRAY(m_pSubZones) ;
+	DELETE_ARRAY(mVisibleFaces) ;
+
+	FreeLightMemory() ;
+	FreeLampMemory() ;
+	FreeVertexMemory() ;
+	FreeTriangleMemory() ;
+	FreeTextureMemory() ;
+	FreeTexLampMemory() ;
+	DestroyBspFacesMemory() ;	// free up face and patch data.  Normally already done if we're just loading a new level or quitting, but we might also be here due to a loading fail or something.
+
+	// no need to delete any of the pointers to the lumps since their memory belongs to m_pRawBspFile over in OgreFramework.
+
+	
+}
+
+void Q3Map::DestroyBspFacesMemory(void)
+{
+	if(m_BspFaces) 
+	{
+		// clean up any memory declared for patches
+		for (int i=0; i < m_NumBspFaces; i++)
+      if ((m_BspFaces[i].type == PATCH) && (m_BspFaces[i].patch != NULL))
+			{
+				DELETE_ARRAY( m_BspFaces[i].patch->bezier ) ;
+				DELETE_POINTER( m_BspFaces[i].patch ) ;
+			}
+
+		// delete the faces memory
+		DELETE_ARRAY( m_BspFaces ) ;
+	}
+}
+
+
+
+int Q3Map::parseMap(const char* pMem, size_t Size)
+{	
+	// we check all lump info to make sure it isn't trying to go out of bounds,
+	// in case some mangled bsp is trying to do something devious or is just corrupted
+	
+	
+	
+
+
+	m_BspHeader=*((Q3BspHeader_t*)pMem) ; // pointer to the header
+
+	// run a check that the total size of all the iLengths plus the header isn't too large
+	size_t TotalLength=sizeof(Q3BspHeader_t) ; // initialize to the size of the header
+	int nLump=0 ;
+	int nOther=0 ;
+	int nLumpMin=0 ;
+	int nLumpMax=0 ;
+	int nOtherMin=0 ;
+	int nOtherMax=0 ;
+	for(nLump=0 ; nLump<MAX_LUMP ; nLump++)
+	{
+		if(m_BspHeader.Lumps[nLump].iLength<0) return -1 ; // lumps shouldn't have a negative size. FAIL!
+		if(m_BspHeader.Lumps[nLump].iLength>MAX_LUMP_SIZE) return -2 ; // no lump has a right to be this big... FAIL!
+		
+		
+		if( (m_BspHeader.Lumps[nLump].iLength>0) && (m_BspHeader.Lumps[nLump].iOffset<sizeof(Q3BspHeader_t)) )
+			return -3 ; // lump overlaps header, FAIL!
+
+		if((m_BspHeader.Lumps[nLump].iLength==0) && (m_BspHeader.Lumps[nLump].iOffset!=0))
+			return -4 ; // lump size is zero and yet offset is not zero???  FAIL!
+
+
+		TotalLength+=m_BspHeader.Lumps[nLump].iLength ;
+		if(TotalLength>Size) return -5 ;// this file is messed up, the lumps add up to more than the file size.  FAIL!
+
+		// make sure this lump doesn't overlap any other lumps
+		nLumpMin=m_BspHeader.Lumps[nLump].iOffset ;
+		nLumpMax=nLumpMin+m_BspHeader.Lumps[nLump].iLength-1 ;
+
+		for(nOther=nLump+1 ; nOther<MAX_LUMP ; nOther++)
+			if((m_BspHeader.Lumps[nLump].iLength>0) && (m_BspHeader.Lumps[nOther].iLength>0)) // don't check zero sized lumps
+			{
+				nOtherMin=m_BspHeader.Lumps[nOther].iOffset ;
+				nOtherMax=nOtherMin+m_BspHeader.Lumps[nOther].iLength-1 ;
+
+				if((nLumpMax>=nOtherMin) && (nLumpMin<=nOtherMax)) 
+				return -6 ; // lump overlaps another lump, FAIL!
+			}
+	}
+
+	
+
+
+	// setup pointers to the lumps
+
+	if((m_BspHeader.Lumps[0].iOffset<0) || (m_BspHeader.Lumps[0].iOffset>=Size)) return -7 ; // fail if out of memory bounds
+	m_pEntities=(char*)(pMem+m_BspHeader.Lumps[0].iOffset) ;
+
+	if((m_BspHeader.Lumps[Faces].iOffset<0) || (m_BspHeader.Lumps[Faces].iOffset>=Size)) return -8 ; // out of bounds
+	if(m_BspHeader.Lumps[Faces].iOffset+m_BspHeader.Lumps[Faces].iLength>Size) return -9 ; // out of bounds
+  m_iNumFaces = m_BspHeader.Lumps[Faces].iLength / sizeof(Q3BspFace_t);
+	m_pFaces=(Q3BspFace_t*)(pMem+m_BspHeader.Lumps[Faces].iOffset) ;
+
+	if((m_BspHeader.Lumps[Vertices].iOffset<0) || (m_BspHeader.Lumps[Vertices].iOffset>=Size)) return -10 ; // out of bounds
+	if(m_BspHeader.Lumps[Vertices].iOffset+m_BspHeader.Lumps[Vertices].iLength>Size) return -11 ; // out of bounds
+  m_iNumVertices = m_BspHeader.Lumps[Vertices].iLength / sizeof(Q3BspVertex);
+	m_pVertices=(Q3BspVertex*)(pMem+m_BspHeader.Lumps[Vertices].iOffset) ;
+
+	if((m_BspHeader.Lumps[MeshVerts].iOffset<0) || (m_BspHeader.Lumps[MeshVerts].iOffset>=Size)) return -12 ; // out of bounds
+	if(m_BspHeader.Lumps[MeshVerts].iOffset+m_BspHeader.Lumps[MeshVerts].iLength>Size) return -13 ; // out of bounds
+  m_iNumMeshVerts = m_BspHeader.Lumps[MeshVerts].iLength / sizeof(int);
+	m_pMeshVerts=(int*)(pMem+m_BspHeader.Lumps[MeshVerts].iOffset) ;
+
+	if((m_BspHeader.Lumps[Leafs].iOffset<0) || (m_BspHeader.Lumps[Leafs].iOffset>=Size)) return -14 ; // out of bounds
+	if(m_BspHeader.Lumps[Leafs].iOffset+m_BspHeader.Lumps[Leafs].iLength>Size) return -15 ; // out of bounds
+  m_iNumLeafs = m_BspHeader.Lumps[Leafs].iLength / sizeof(Q3BspLeaf);
+	m_pLeafs=(Q3BspLeaf*)(pMem+m_BspHeader.Lumps[Leafs].iOffset) ;
+
+	if((m_BspHeader.Lumps[LeafFaces].iOffset<0) || (m_BspHeader.Lumps[LeafFaces].iOffset>=Size)) return -16 ; // out of bounds
+	if(m_BspHeader.Lumps[LeafFaces].iOffset+m_BspHeader.Lumps[LeafFaces].iLength>Size) return -17 ; // out of bounds
+  m_iNumLeafFaces = m_BspHeader.Lumps[LeafFaces].iLength / sizeof(int);
+	m_pLeafFaces=(int*)(pMem+m_BspHeader.Lumps[LeafFaces].iOffset) ;
+
+	if((m_BspHeader.Lumps[LeafBrushes].iOffset<0) || (m_BspHeader.Lumps[LeafBrushes].iOffset>=Size)) return -18 ; // out of bounds
+	if(m_BspHeader.Lumps[LeafBrushes].iOffset+m_BspHeader.Lumps[LeafBrushes].iLength>Size) return -19 ; // out of bounds
+  m_iNumLeafBrushes = m_BspHeader.Lumps[LeafBrushes].iLength / sizeof(int);
+	m_pLeafBrushes=(int*)(pMem+m_BspHeader.Lumps[LeafBrushes].iOffset) ;
+
+	if((m_BspHeader.Lumps[Textures].iOffset<0) || (m_BspHeader.Lumps[Textures].iOffset>=Size)) return -20 ; // out of bounds
+	if(m_BspHeader.Lumps[Textures].iOffset+m_BspHeader.Lumps[Textures].iLength>Size) return -21 ; // out of bounds
+  m_iNumTexs = m_BspHeader.Lumps[Textures].iLength / sizeof(Q3BspTexture);
+	m_pTexturesOrig=(Q3BspTexture*)(pMem+m_BspHeader.Lumps[Textures].iOffset) ;
+
+	if((m_BspHeader.Lumps[Planes].iOffset<0) || (m_BspHeader.Lumps[Planes].iOffset>=Size)) return -22 ; // out of bounds
+	if(m_BspHeader.Lumps[Planes].iOffset+m_BspHeader.Lumps[Planes].iLength>Size) return -23 ; // out of bounds
+  m_iNumPlanes = m_BspHeader.Lumps[Planes].iLength / sizeof(Q3BspPlane);
+	m_pPlanes=(Q3BspPlane*)(pMem+m_BspHeader.Lumps[Planes].iOffset) ;
+
+	if((m_BspHeader.Lumps[Nodes].iOffset<0) || (m_BspHeader.Lumps[Nodes].iOffset>=Size)) return -24 ; // out of bounds
+	if(m_BspHeader.Lumps[Nodes].iOffset+m_BspHeader.Lumps[Nodes].iLength>Size) return -25 ; // out of bounds
+  m_iNumNodes = m_BspHeader.Lumps[Nodes].iLength / sizeof(Q3BspNode);
+	m_pNodes=(Q3BspNode*)(pMem+m_BspHeader.Lumps[Nodes].iOffset) ;
+
+  //m_iNumModels = m_BspHeader.Lumps[Models].iLength / sizeof(Q3BspModel);
+  //m_pModels = new Q3BspModel[m_iNumModels];
+
+	// bzn doesn't use lightmaps
+  //m_iNumLightMaps = m_BspHeader.Lumps[LightMaps].iLength / sizeof(Q3BspLightMap);
+	//m_pLightMaps=(Q3BspLightMap*)(pMem+m_BspHeader.Lumps[LightMaps].iOffset) ;
+
+	if((m_BspHeader.Lumps[Brushes].iOffset<0) || (m_BspHeader.Lumps[Brushes].iOffset>=Size)) return -26 ; // out of bounds
+	if(m_BspHeader.Lumps[Brushes].iOffset+m_BspHeader.Lumps[Brushes].iLength>Size) return -27 ; // out of bounds
+  m_iNumBrushes = m_BspHeader.Lumps[Brushes].iLength / sizeof(Q3BspBrush);
+	m_pBrushes=(Q3BspBrush*)(pMem+m_BspHeader.Lumps[Brushes].iOffset) ;
+
+
+	if((m_BspHeader.Lumps[BrushSides].iOffset<0) || (m_BspHeader.Lumps[BrushSides].iOffset>=Size)) return -28 ; // out of bounds
+	if(m_BspHeader.Lumps[BrushSides].iOffset+m_BspHeader.Lumps[BrushSides].iLength>Size) return -29 ; // out of bounds
+  m_iNumBrushSides = m_BspHeader.Lumps[BrushSides].iLength / sizeof(Q3BspBrushSide);
+	m_pBrushSides=(Q3BspBrushSide*)(pMem+m_BspHeader.Lumps[BrushSides].iOffset) ;
+
+  //m_iNumEffects = m_BspHeader.Lumps[Effects].iLength / sizeof(Q3BspEffect);
+  //m_pEffects = new Q3BspEffect[m_iNumEffects];
+  //
+  //m_pImages = new BDTexture[m_iNumTexs];
+
+	// bzn doesn't use visdata
+	//m_VisData=(Q3BspVisData*)(pMem+m_BspHeader.Lumps[VisData].iOffset) ;
+	//m_VisData->vecs=(unsigned char*)(pMem+m_BspHeader.Lumps[VisData].iOffset + 2*sizeof(int)) ;
+
+	///////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+	// bzn specific data
+	if((m_BspHeader.Lumps[SubZoneData].iOffset<0) || (m_BspHeader.Lumps[SubZoneData].iOffset>=Size)) return -30 ; // out of bounds
+	if(m_BspHeader.Lumps[SubZoneData].iOffset+m_BspHeader.Lumps[SubZoneData].iLength>Size) return -31 ; // out of bounds
+	m_iNumSubZones = m_BspHeader.Lumps[SubZoneData].iLength / sizeof(BZN_SubZone_t);
+  m_pSubZones=(BZN_SubZone_t*)(pMem+m_BspHeader.Lumps[SubZoneData].iOffset) ;
+
+	if((m_BspHeader.Lumps[PortalData].iOffset<0) || (m_BspHeader.Lumps[PortalData].iOffset>=Size)) return -32 ; // out of bounds
+	if(m_BspHeader.Lumps[PortalData].iOffset+m_BspHeader.Lumps[PortalData].iLength>Size) return -33 ; // out of bounds
+	m_iNumPortals = m_BspHeader.Lumps[PortalData].iLength / sizeof(BZN_Portal_t);
+	m_pPortals=(BZN_Portal_t*)(pMem+m_BspHeader.Lumps[PortalData].iOffset) ;
+
+	
+
+
+
+
+	// fix coords and setup face memory
+	swizzleCoords();
+  mVisibleFaces = new int[m_iNumFaces];
+
+
+	// we need a new version of the textures, because when we parse the lights they will have textures to add to it,
+	// and we can't expand the texture lump because it's in the middle of a block of memory containing all the lumps.
+
+	// copy the texture lump
+	int nTexture=0 ;
+	for(nTexture=0 ; nTexture<m_iNumTexs ; nTexture++)
+	{
+		if(!AddTexture(m_pTexturesOrig[nTexture])) return -34 ; // failed to add texture, probably out of memory
+	}
+	
+	return 0 ;
+
+}
+
+
+void Q3Map::swizzleCoords(void)
+{
+  //DEBUG_OUTPUT("swizziling data...");
+  // vertices
+  for (int i=0; i < m_iNumVertices; i++)
+  {
+    swizzleFloat3(m_pVertices[i].position);
+    swizzleFloat3(m_pVertices[i].normal);
+    //m_pVertices[i].texcoord[0][0] = 1.0f - m_pVertices[i].texcoord[0][0];		
+  }
+
+  // leafs
+  for (int i=0; i < m_iNumLeafs; i++)
+  {
+    swizzleInt3(m_pLeafs[i].maxs);
+    swizzleInt3(m_pLeafs[i].mins);
+  }
+
+  // faces, do lightmaps later...
+  for (int i=0; i < m_iNumFaces; i++)
+  {
+    swizzleFloat3(m_pFaces[i].normal);
+  }
+
+  // planes
+  for (int i=0; i < m_iNumPlanes; i++)
+  {
+    swizzleFloat3(m_pPlanes[i].normal);		
+  }
+
+  // nodes
+  for (int i=0; i < m_iNumNodes; i++)
+  {
+    swizzleInt3(m_pNodes[i].maxs);
+    swizzleInt3(m_pNodes[i].mins);
+  }
+
+	
+	// subzones
+	float flTemp=0.0f ;
+	for (int i=0; i < m_iNumSubZones; i++)
+  {
+		swizzleFloat3(m_pSubZones[i].Max);		
+		swizzleFloat3(m_pSubZones[i].Min);
+
+		// swizzling will mix up z max and min due to the sign change, so swap them
+		flTemp=m_pSubZones[i].Max[2] ;
+		m_pSubZones[i].Max[2]=m_pSubZones[i].Min[2] ;
+		m_pSubZones[i].Min[2]=flTemp ;
+
+  }
+
+	// portals
+	for (int i=0; i < m_iNumPortals; i++)
+  {
+		swizzleFloat3(m_pPortals[i].Max);		
+		swizzleFloat3(m_pPortals[i].Min);
+
+		// swizzling will mix up z max and min due to the sign change, so swap them
+		flTemp=m_pPortals[i].Max[2] ;
+		m_pPortals[i].Max[2]=m_pPortals[i].Min[2] ;
+		m_pPortals[i].Min[2]=flTemp ;
+
+  }
+
+	
+	
+
+}
+
+void Q3Map::swizzleFloat3(float t[3])
+{	
+  float temp;
+  temp = t[1];
+  t[1] = t[2];
+  t[2] = -temp;
+}
+
+void Q3Map::swizzleInt3(int t[3])
+{	
+  int temp;
+  temp = t[1];
+  t[1] = t[2];
+  t[2] = -temp;
+}
+
+
+
+Q3BspPatch *Q3Map::handlePatch(int faceIndex)
+{
+  Q3BspPatch *q3patch;
+  q3patch = new Q3BspPatch;
+
+  int patch_size_x = (m_pFaces[faceIndex].size[0] - 1) / 2;
+  int patch_size_y = (m_pFaces[faceIndex].size[1] - 1) / 2;
+  int num_bezier_patches = patch_size_y * patch_size_x;
+
+  q3patch->size = num_bezier_patches;
+  q3patch->bezier = new Bezier[q3patch->size];
+
+  int patchIndex =  0;
+  int ii, n, j, nn;
+  for (ii = 0, n = 0; n < patch_size_x; n++, ii = 2*n)
+  {				
+    for (j=0, nn=0; nn < patch_size_y; nn++, j = 2*nn)
+    {
+      int index = 0;
+      for (int ctr = 0; ctr < 3; ctr++)
+      { 
+        int pos = ctr * m_pFaces[faceIndex].size[0];
+
+        q3patch->bezier[patchIndex].mControls[index++] = 
+          BspVertex(
+          // position
+          m_pVertices[m_pFaces[faceIndex].vertex + 
+          ii + 
+          m_pFaces[faceIndex].size[0] * j + 
+          pos].position,
+          // texture coordinates
+          m_pVertices[m_pFaces[faceIndex].vertex + 
+          ii + 
+          m_pFaces[faceIndex].size[0] * j +
+          pos].texcoord,
+          // normal
+          m_pVertices[m_pFaces[faceIndex].vertex +
+          ii +
+          m_pFaces[faceIndex].size[0] * j +
+          pos].normal);
+
+        q3patch->bezier[patchIndex].mControls[index++] = 
+                BspVertex(
+                  m_pVertices[m_pFaces[faceIndex].vertex + ii + m_pFaces[faceIndex].size[0] * j + pos + 1].position,
+                  m_pVertices[m_pFaces[faceIndex].vertex + ii + m_pFaces[faceIndex].size[0] * j + pos + 1].texcoord,
+                  m_pVertices[m_pFaces[faceIndex].vertex + ii + m_pFaces[faceIndex].size[0] * j + pos + 1].normal);
+
+        q3patch->bezier[patchIndex].mControls[index++] = 
+                BspVertex(
+                  m_pVertices[m_pFaces[faceIndex].vertex + ii + m_pFaces[faceIndex].size[0] * j + pos + 2].position,
+                  m_pVertices[m_pFaces[faceIndex].vertex + ii + m_pFaces[faceIndex].size[0] * j + pos + 2].texcoord,
+                  m_pVertices[m_pFaces[faceIndex].vertex + ii + m_pFaces[faceIndex].size[0] * j + pos + 2].normal);						
+      }      
+      q3patch->bezier[patchIndex].tessellate(5);
+      patchIndex++;
+    }
+  }
+
+  return q3patch;
+}
+
+
+int Q3Map::findVisibleFaces(const QVECTOR *camPos, int *facesToRender)
+{
+  int leaf;
+  int visCluster;	
+
+  leaf = findLeaf(camPos);	
+
+  visCluster = m_pLeafs[leaf].cluster;
+
+  memset(mVisibleFaces, 0, sizeof(int) * m_iNumFaces);	
+
+  int faceindex;
+  int renderindex=0;
+  m_ClusterCount=0 ;
+  
+  for (int i=0; i < m_iNumLeafs; i++)
+  {
+    if (isClusterVisible(visCluster, m_pLeafs[i].cluster))
+    {				
+			m_ClusterCount++ ;
+      bool vis=true ;//bool vis = mViewFrustum->checkIfBoxInside(m_pLeafs[i].mins, m_pLeafs[i].maxs);						
+      
+	  if (vis)
+      {
+        for (int k=0; k < m_pLeafs[i].n_leaffaces; k++)
+        {					
+          faceindex =	m_pLeafFaces[m_pLeafs[i].leafface + k];				
+          if (mVisibleFaces[faceindex] == 0)
+          {
+            mVisibleFaces[faceindex] = 1;						
+            facesToRender[renderindex++] = faceindex;
+          }
+        }
+      }			
+    }
+  }
+  
+
+  facesToRender[renderindex] = -1;	
+
+  return renderindex;
+}
+
+
+int Q3Map::findLeaf(const QVECTOR *camPos) const
+{
+  int index = 0;
+
+  while (index >= 0)
+  {
+    const Q3BspNode *node = &m_pNodes[index];
+    const Q3BspPlane *plane = &m_pPlanes[node->plane];
+
+    // distance from point to plane
+    //QVECTOR normal = QVECTOR(plane->normal);		 
+    QVECTOR normal ;
+	normal[0]=plane->normal[0] ;
+	normal[1]=plane->normal[1] ;
+	normal[2]=plane->normal[2] ;
+	
+	
+	//const float distance = D3DXVec3Dot(&normal,camPos) - plane->dist;
+
+	const float distance=(normal[0]* *camPos[0] + normal[1]* *camPos[1] + normal[2]* *camPos[2]) - plane->dist ;
+
+    if(distance >= 0)
+      index = node->children[0];
+    else
+      index = node->children[1];
+  }
+
+  return -index - 1;
+}
+
+bool Q3Map::isClusterVisible(int visCluster, int testCluster) const
+{
+  if (m_VisData == NULL)
+    return true;
+
+  if ((m_VisData->vecs == NULL) || (visCluster < 0)) 	
+    return true;    
+
+  int i = (visCluster * m_VisData->sz_vecs) + (testCluster >> 3);
+  unsigned char visSet = m_VisData->vecs[i];
+
+  return (visSet & (1 << (testCluster & 7))) != 0;
+}
+
+Q3BspFace_t *Q3Map::getFaces(void)
+{
+  return m_pFaces;
+}
+
+
+
+
+
+ /***********************************************************************************************************\
+                                               
+                                   New Parsing and Triangulation Functions 
+                                                
+ \***********************************************************************************************************/
+
+
+
+
+// This routine is basically an overview of the entire process that converts the BSP
+// into something our Ogre code can use to construct the map's mesh and level data.
+// In essence, it converts the map geometry into a list of triangles sorted by zone and material,
+// as well as extracting other map info like zone and portal bounding boxes, lights, entities etc.
+
+int Q3Map::ParseAndTriangulateMap(const char* pData, size_t Size)
+{
+	
+//	char chMessage[1024] ;
+	int nError=0 ;
+
+		// setup pointers to the various lumps and get their quantities
+		nError=parseMap( pData, Size ) ;
+		if(nError<0)
+		{
+			//sprintf(chMessage, "Parse Map Error: %i", nError) ; 
+			//Q3Bug.LogAddCR(chMessage) ;
+			return ERROR_ParseMap ;
+		}
+
+		// extract entities such as lights, monsters, etc
+		if(!ParseEntities())										return ERROR_ParseEntities ;
+
+		// initial memory allocation for triangles
+		m_nVertexMax=0 ;
+		if(!AllocateVertexMemory(m_iNumVertices))	return ERROR_AllocateVertex ;
+		if(!AllocateTriangleMemory())						return ERROR_AllocateTriangle ;
+		if(!initFaces())												return ERROR_InitializeFaces ;
+
+		// no new map textures should be added after here, or else SetupTransTextures won't work
+		if(!SetupTransTextures())								return ERROR_SetupTransTextures ;
+
+		// work out the zones
+		SetupZones() ;
+
+		// convert faces to triangles
+		if(!ConvertFacesToTriangles())					return ERROR_ConvertFaces ;
+
+		if(!ConvertPatchesToTriangles())				return ERROR_ConvertPatches ;
+
+		if(!ConvertLampsToTriangles())					return ERROR_ConvertLamps ;
+
+		if(!ConvertLampsToGlowTriangles())			return ERROR_ConvertLampGlow ;
+
+		if(!ConvertLightsToGlowTriangles())			return ERROR_ConvertLightGlow ;
+
+		GetTexLampTextureNumbers() ; // find out which textures, if any, are textures/common/bzn_lightnode0 to textures/common/bzn_lightnode3
+
+		// assign triangles to zones, splitting them where necessary
+		if(!AssignTrianglesToZones())						return ERROR_AssignTriangles ;
+
+		if(!ConvertTexLampsToLampTriangles())		return ERROR_ConvertTexLamp ;
+
+		// sort by group and re-range the group numbers
+		if(!SortTrianglesIntoGroups())					return ERROR_SortGroups ;
+
+		// sort the triangles in order of zone and texture.  This will also get rid of any unsubzoned triangles.
+		if(!SortTrianglesIntoBatches())					return ERROR_SortTriangles ;
+
+		
+
+
+		// Setup the portals, lights and various bits of map connectivity
+		AssignPortalsToZones() ;		// what portals each zone touches
+		AssignLightsToZones() ;			// what lights each zone touches
+		AssignLightsToPortals() ;		// what lights each portal touches
+		AssignZonesToZones() ;			// what zones each zone touches
+
+
+		return NOERROR ;
+}
+
+void Q3Map::FreeParseMem(void)
+{
+	FreeVertexMemory() ;
+	FreeTriangleMemory() ;
+	DestroyBspFacesMemory() ;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// memory management
+
+int Q3Map::AllocateTriangleMemory(void)
+{
+	// memory for the Triangle
+	m_nTriangleSize=MEMADD ; // starting memory size
+	m_pTriangleMem=malloc(m_nTriangleSize) ; // allocate starting memory space
+	m_pTriangle=(triangle_t*)m_pTriangleMem ; // a pointer to the memory cast as a triangle_t
+	m_nTriangleMax=0 ;
+	m_nTriangleLimit=m_nTriangleSize/sizeof(triangle_t) ; // if pos reaches this memory must expand
+
+	if(m_pTriangleMem==NULL)
+		return 0 ;
+
+	return 1 ;
+}
+
+void Q3Map::FreeTriangleMemory(void)
+{
+	if(m_pTriangleMem) free(m_pTriangleMem) ;
+	m_pTriangleMem=NULL ;
+	m_pTriangle=NULL ;
+	m_nTriangleMax=0 ;
+	m_nTriangleLimit=0 ;
+}
+
+// increase size of Triangle memory, return 0 if failed
+int Q3Map::ExpandTriangleMemory(void)
+{
+	m_nTriangleSize+=MEMADD ; // increase size
+	m_pTriangleMem=realloc(m_pTriangleMem, m_nTriangleSize) ; // reallocate the memory
+	if(m_pTriangleMem==NULL) return 0 ; // failed to allocate memory, get out and return false
+
+	// if here then memory allocation succeeded
+	m_pTriangle=(triangle_t*)m_pTriangleMem ; // pointer to the memory cast as a triangle_t
+	m_nTriangleLimit=m_nTriangleSize/sizeof(triangle_t) ; // if pos reaches this memory must expand
+	return 1 ; // ok
+}
+
+int Q3Map::AddTriangle(triangle_t Triangle)
+{
+	if(m_nTriangleMax>=m_nTriangleLimit)
+		if( !ExpandTriangleMemory() )
+			return 0 ;
+
+	m_pTriangle[m_nTriangleMax++]=Triangle ;
+
+	return 1 ;
+}
+
+int Q3Map::AllocateVertexMemory(int nVertNum)
+{
+	// memory for the Vertex
+	m_nVertexSize=nVertNum*sizeof(Q3BspVertex)+MEMADD ; // starting memory size
+	m_pVertexMem=malloc(m_nVertexSize) ; // allocate starting memory space
+	m_pVertex=(Q3BspVertex*)m_pVertexMem ; // a pointer to the memory cast as a triangle_t
+	m_nVertexLimit=m_nVertexSize/sizeof(Q3BspVertex) ; // if pos reaches this memory must expand
+
+	if(m_pVertexMem==NULL)
+		return 0 ;
+
+	return 1 ;
+}
+
+void Q3Map::FreeVertexMemory(void)
+{
+	if(m_pVertexMem) free(m_pVertexMem) ;
+	m_pVertexMem=NULL ;
+	m_pVertex=NULL ;
+	m_nVertexMax=0 ;
+	m_nVertexLimit=0 ;
+}
+
+// increase size of Vertex memory, return 0 if failed
+int Q3Map::ExpandVertexMemory(void)
+{
+	m_nVertexSize+=MEMADD ; // increase size
+	m_pVertexMem=realloc(m_pVertexMem, m_nVertexSize) ; // reallocate the memory
+	if(m_pVertexMem==NULL) return 0 ; // failed to allocate memory, get out and return false
+
+	// if here then memory allocation succeeded
+	m_pVertex=(Q3BspVertex*)m_pVertexMem ; // pointer to the memory cast as a triangle_t
+	m_nVertexLimit=m_nVertexSize/sizeof(Q3BspVertex) ; // if pos reaches this memory must expand
+	return 1 ; // ok
+}
+
+int Q3Map::AddVertex(Q3BspVertex Vertex)
+{
+
+	if(m_nVertexMax>=m_nVertexLimit)
+		if( !ExpandVertexMemory() )
+			return 0 ;
+
+	m_pVertex[m_nVertexMax++]=Vertex ;
+
+	return 1 ;
+}
+
+int Q3Map::AllocateLightMemory(void)
+{
+	// memory for the Light
+	m_nLightSize=MEMADD ; // starting memory size
+	m_pLightMem=malloc(m_nLightSize) ; // allocate starting memory space
+	m_pLight=(light_t*)m_pLightMem ; // a pointer to the memory cast as a light_t
+	m_nLightMax=0 ;
+	m_nLightLimit=m_nLightSize/sizeof(light_t) ; // if pos reaches this memory must expand
+
+	if(m_pLightMem==NULL)
+		return 0 ;
+
+	return 1 ;
+}
+
+void Q3Map::FreeLightMemory(void)
+{
+	if(m_pLightMem) free(m_pLightMem) ;
+	m_pLightMem=NULL ;
+	m_pLight=NULL ;
+	m_nLightMax=0 ;
+	m_nLightLimit=0 ;
+}
+
+// increase size of Light memory, return 0 if failed
+int Q3Map::ExpandLightMemory(void)
+{
+	m_nLightSize+=MEMADD ; // increase size
+	m_pLightMem=realloc(m_pLightMem, m_nLightSize) ; // reallocate the memory
+	if(m_pLightMem==NULL) return 0 ; // failed to allocate memory, get out and return false
+
+	// if here then memory allocation succeeded
+	m_pLight=(light_t*)m_pLightMem ; // pointer to the memory cast as a light_t
+	m_nLightLimit=m_nLightSize/sizeof(light_t) ; // if pos reaches this memory must expand
+	return 1 ; // ok
+}
+
+int Q3Map::AddLight(light_t Light)
+{
+	if(m_nLightLimit==0) // light memory hasn't been allocated yet
+	{
+		if( !AllocateLightMemory() )
+			return 0 ;
+	}
+	else
+		if(m_nLightMax>=m_nLightLimit)
+			if( !ExpandLightMemory() )
+				return 0 ;
+
+	m_pLight[m_nLightMax++]=Light ;
+
+	return 1 ;
+}
+
+// lamps are deferred shading, non-shadowing point lights
+int Q3Map::AllocateLampMemory(void)
+{
+	// memory for the Lamp
+	m_nLampSize=MEMADD ; // starting memory size
+	m_pLampMem=malloc(m_nLampSize) ; // allocate starting memory space
+	m_pLamp=(lamp_t*)m_pLampMem ; // a pointer to the memory cast as a lamp_t
+	m_nLampMax=0 ;
+	m_nLampLimit=m_nLampSize/sizeof(lamp_t) ; // if pos reaches this memory must expand
+
+	if(m_pLampMem==NULL)
+		return 0 ;
+
+	return 1 ;
+}
+
+void Q3Map::FreeLampMemory(void)
+{
+	if(m_pLampMem) free(m_pLampMem) ;
+	m_pLampMem=NULL ;
+	m_pLamp=NULL ;
+	m_nLampMax=0 ;
+	m_nLampLimit=0 ;
+}
+
+// increase size of Lamp memory, return 0 if failed
+int Q3Map::ExpandLampMemory(void)
+{
+	m_nLampSize+=MEMADD ; // increase size
+	m_pLampMem=realloc(m_pLampMem, m_nLampSize) ; // reallocate the memory
+	if(m_pLampMem==NULL) return 0 ; // failed to allocate memory, get out and return false
+
+	// if here then memory allocation succeeded
+	m_pLamp=(lamp_t*)m_pLampMem ; // pointer to the memory cast as a lamp_t
+	m_nLampLimit=m_nLampSize/sizeof(lamp_t) ; // if pos reaches this memory must expand
+	return 1 ; // ok
+}
+
+int Q3Map::AddLamp(lamp_t Lamp)
+{
+	if(m_nLampLimit==0) // Lamp memory hasn't been allocated yet
+	{
+		if( !AllocateLampMemory() )
+			return 0 ;
+	}
+	else
+		if(m_nLampMax>=m_nLampLimit)
+			if( !ExpandLampMemory() )
+				return 0 ;
+
+	m_pLamp[m_nLampMax++]=Lamp ;
+
+	return 1 ;
+}
+
+//////////////
+// Q3BspTexture textures.  We duplicate the loaded texture mem and then add lighting textures to it.
+
+int Q3Map::AllocateTextureMemory(void)
+{
+	// memory for the Texture
+	m_nTextureSize=MEMADD ; // starting memory size
+	m_pTextureMem=malloc(m_nTextureSize) ; // allocate starting memory space
+	m_pTexture=(Q3BspTexture*)m_pTextureMem ; // a pointer to the memory cast as a Q3BspTexture
+	m_nTextureMax=0 ;
+	m_nTextureLimit=m_nTextureSize/sizeof(Q3BspTexture) ; // if pos reaches this memory must expand
+
+	if(m_pTextureMem==NULL)
+		return 0 ;
+
+	return 1 ;
+}
+
+void Q3Map::FreeTextureMemory(void)
+{
+	if(m_pTextureMem) free(m_pTextureMem) ;
+	m_pTextureMem=NULL ;
+	m_pTexture=NULL ;
+	m_nTextureMax=0 ;
+	m_nTextureLimit=0 ;
+}
+
+// increase size of Texture memory, return 0 if failed
+int Q3Map::ExpandTextureMemory(void)
+{
+	m_nTextureSize+=MEMADD ; // increase size
+	m_pTextureMem=realloc(m_pTextureMem, m_nTextureSize) ; // reallocate the memory
+	if(m_pTextureMem==NULL) return 0 ; // failed to allocate memory, get out and return false
+
+	// if here then memory allocation succeeded
+	m_pTexture=(Q3BspTexture*)m_pTextureMem ; // pointer to the memory cast as a Q3BspTexture
+	m_nTextureLimit=m_nTextureSize/sizeof(Q3BspTexture) ; // if pos reaches this memory must expand
+	return 1 ; // ok
+}
+
+int Q3Map::AddTexture(Q3BspTexture Texture)
+{
+	if(m_nTextureLimit==0) // Texture memory hasn't been allocated yet
+	{
+		if( !AllocateTextureMemory() )
+			return 0 ;
+	}
+	else
+		if(m_nTextureMax>=m_nTextureLimit)
+			if( !ExpandTextureMemory() )
+				return 0 ;
+
+	m_pTexture[m_nTextureMax++]=Texture ;
+
+	return 1 ;
+}
+
+// special version of the Add function, will not add if the texture name already exist.  
+// Will succeed even if the texture is already on the list, but fails if it can't add a new texture
+// returns texture index, or -1 on fail
+// Q3 texture names can be tricky, I think I've had cases where they ended in spaces instead of nulls,
+// and they might go all the way to the end without either.
+
+int Q3Map::AddTextureUnique(Q3BspTexture Texture)
+{
+	if(m_nTextureLimit==0) // Texture memory hasn't been allocated yet
+		if( !AllocateTextureMemory() )
+			return ADDTEXTUREUNIQUE_FAIL ; // fail
+
+
+	// scan through all the newly added textures so far and see if this one already exists.
+	int nTexture=0 ;
+	int nPos=0 ;
+
+	bool bMatch=false ;
+
+	for(nTexture=0 ; nTexture<m_nTextureMax ; nTexture++)
+	{
+		bMatch=true ;
+		// scan through the characters of the texture names, comparing them.  We start after the original textures
+		//for(nPos=m_iNumTexs ; nPos<Q3NAMESIZE ; nPos++)
+		for(nPos=0 ; nPos<Q3NAMESIZE ; nPos++)
+		{
+			// is it the end of the texture name?  
+			if(
+						((Texture.name[nPos]							==0)	|| (Texture.name[nPos]							==' ')) // Texture    name end
+					&&((m_pTexture[nTexture].name[nPos] ==0)	|| (m_pTexture[nTexture].name[nPos]	==' ')) // m_pTexture name end
+				)
+				break ;
+
+			// do the two textures have a difference in the name at this position?
+			if(Texture.name[nPos]!=m_pTexture[nTexture].name[nPos])
+			{
+				bMatch=false ;
+				break ;
+			}
+		
+		}// end scanning name
+
+		if(bMatch) // found a match, so return ok but don't add a texture
+		{
+			return nTexture ;  // we don't add any new texture, return this texture's index
+		}
+	}
+
+	// if we got this far, we must have a unique texture
+
+	// add the texture, it is unique
+	if(m_nTextureMax>=m_nTextureLimit)
+		if( !ExpandTextureMemory() )
+			return ADDTEXTUREUNIQUE_FAIL ; // fail
+
+	m_pTexture[m_nTextureMax++]=Texture ;
+
+	return m_nTextureMax-1 ; // return this new texture's index
+
+}
+
+
+////////////////////////////////////////
+
+int Q3Map::AllocateTexLampMemory(void)
+{
+	// memory for the TexLamp
+	m_nTexLampSize=MEMADD ; // starting memory size
+	m_pTexLampMem=malloc(m_nTexLampSize) ; // allocate starting memory space
+	m_pTexLamp=(int*)m_pTexLampMem ; // a pointer to the memory cast as an int
+	m_nTexLampMax=0 ;
+	m_nTexLampLimit=m_nTexLampSize/sizeof(int) ; // if pos reaches this memory must expand
+
+	if(m_pTexLampMem==NULL)
+		return 0 ;
+
+	return 1 ;
+}
+
+void Q3Map::FreeTexLampMemory(void)
+{
+	if(m_pTexLampMem) free(m_pTexLampMem) ;
+	m_pTexLampMem=NULL ;
+	m_pTexLamp=NULL ;
+	m_nTexLampMax=0 ;
+	m_nTexLampLimit=0 ;
+}
+
+// increase size of TexLamp memory, return 0 if failed
+int Q3Map::ExpandTexLampMemory(void)
+{
+	m_nTexLampSize+=MEMADD ; // increase size
+	m_pTexLampMem=realloc(m_pTexLampMem, m_nTexLampSize) ; // reallocate the memory
+	if(m_pTexLampMem==NULL) return 0 ; // failed to allocate memory, get out and return false
+
+	// if here then memory allocation succeeded
+	m_pTexLamp=(int*)m_pTexLampMem ; // pointer to the memory cast as an int
+	m_nTexLampLimit=m_nTexLampSize/sizeof(int) ; // if pos reaches this memory must expand
+	return 1 ; // ok
+}
+
+int Q3Map::AddTexLamp(int TexLamp)
+{
+	if(m_nTexLampMax>=m_nTexLampLimit)
+		if( !ExpandTexLampMemory() )
+			return 0 ;
+
+	m_pTexLamp[m_nTexLampMax++]=TexLamp ;
+
+	return 1 ;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// extract entities from bsp entities lump
+int Q3Map::ParseEntities(void)
+{
+//	char chKey[MAX_TOKENSIZE+1] ;   // +1 to leave room for null terminator
+//	char chValue[MAX_TOKENSIZE+1] ; // +1 to leave room for null terminator
+	int nPos=0 ;
+	int nMaxPos=m_BspHeader.Lumps[0].iLength ;
+	int nEntityType=0 ;
+
+
+	// reset the spotlight textures
+	m_nMaxSpotlightTexture=0 ;
+	for(nPos=0 ; nPos<MAX_PROJECTORTEX ; nPos++)
+		m_chSpotlightTexture[nPos][0]='\0' ;
+
+	strcpy(m_chSpotlightTexture[m_nMaxSpotlightTexture], "spotlight.dds") ; // the default spotlight texture
+	m_nMaxSpotlightTexture++ ;
+
+	////////////////////////////////////////////////////////////////////////////
+	// before adding all the entities we need to add some default textures
+	Q3BspTexture Texture ;
+	Texture.contents=0 ;
+	Texture.flags=0 ;
+
+	// default lamp texture
+	strcpy(Texture.name, "lights/lamp_default") ;
+	m_nDefaultTextureIndexLamp=AddTextureUnique(Texture) ;
+	if(m_nDefaultTextureIndexLamp==ADDTEXTUREUNIQUE_FAIL) return 0 ;
+
+	// default lamp2Pass texture
+	strcpy(Texture.name, "lights/lamp2pass_default") ;
+	m_nDefaultTextureIndexLamp2Pass=AddTextureUnique(Texture) ;
+	if(m_nDefaultTextureIndexLamp2Pass==ADDTEXTUREUNIQUE_FAIL) return 0 ;
+
+	// default glow texture
+	strcpy(Texture.name, "GLOW_lamp") ;
+	m_nDefaultTextureIndexGlowLamp=AddTextureUnique(Texture) ;
+	if(m_nDefaultTextureIndexGlowLamp==ADDTEXTUREUNIQUE_FAIL) return 0 ;
+
+	// default glow texture
+	strcpy(Texture.name, "GLOW_light") ;
+	m_nDefaultTextureIndexGlowLight=AddTextureUnique(Texture) ;
+	if(m_nDefaultTextureIndexGlowLight==ADDTEXTUREUNIQUE_FAIL) return 0 ;
+
+
+	// default spotlight texture
+	//strcpy(Texture.name, "lights/light_default") ;
+	//m_nDefaultTextureIndexSpotlight=AddTextureUnique(Texture) ;
+	//if(m_nDefaultTextureIndexSpotlight==ADDTEXTUREUNIQUE_FAIL) return 0 ;
+
+	//
+	///////////////////////////////////////////////////////////////////////////
+
+
+	nPos=-1 ;
+	while(NextEntity(&nPos, nMaxPos))
+	{
+		
+		nEntityType=GetEntityType(nPos, nMaxPos) ; // what type of entity is it?
+
+
+		switch(nEntityType)
+		{
+			case ENTITY_ERROR: return 0 ; // something is wrong with the entity data
+
+			case ENTITY_LIGHT: 
+				if(!ParseAndAddLight(&nPos, nMaxPos)) 
+					return 0 ; // something went wrong with parsing the light
+				break ;
+		}// end switch entity type
+
+
+	}// end get next entity
+
+
+
+	// everything is ok.
+	return 1 ;
+}
+
+// move to the beginning of the next entity.
+// fail if there are no more.
+int Q3Map::NextEntity(int* pPos, int nMaxPos)
+{
+	while((++*pPos<nMaxPos) && (m_pEntities[*pPos]!='{')) ;
+
+	if(*pPos==nMaxPos) 
+		return 0 ;
+
+	return 1 ;
+}
+
+// find out what type of entity this is.
+// Since the classname might not be at the beginning we have to scan through the whole entity
+// This function also doesn't update the position like the others do,
+// since scanning of further key/values will have to start at the beginning of the entity again.
+int Q3Map::GetEntityType(int nPos, int nMaxPos)
+{
+	char chKey[MAX_TOKENSIZE+1] ;   // +1 to leave room for null terminator
+	char chValue[MAX_TOKENSIZE+1] ; // +1 to leave room for null terminator
+
+	while(nPos<nMaxPos)
+	{
+		if(GetEntityKeyAndValue(&nPos, nMaxPos, chKey, chValue)!=KEY_OK) return ENTITY_ERROR ; // something went wrong, couldn't find any good keys
+		
+		if(strcmp(chKey, "classname")==0) // found the classname key
+		{
+			if(strcmp(chValue, "worldspawn")==0)			return ENTITY_WORLDSPAWN ;
+			if(strcmp(chValue, "light")==0)						return ENTITY_LIGHT ;
+
+			// wasn't any entity we recognize
+			return ENTITY_UNKNOWN ;
+
+		}// end if got entity classname
+
+	}// end while nPos
+
+	// didn't find any classname
+	return ENTITY_ERROR ;
+}
+
+// get next entity key and value
+int Q3Map::GetEntityKeyAndValue(int* pPos, int nMaxPos, char* pKey, char* pValue)
+{
+
+	char* pEntText=m_pEntities ;
+	int nEntPos=*pPos ;
+	int nKeyPos=-1 ;
+	int nValuePos=-1 ;
+
+	// clear key and value strings
+	pKey[0]='\0' ;
+	pValue[0]='\0' ;
+
+	/////////////////////////////////////////////////////////////////////////////////////////
+	// Key
+
+	// find the next "
+	while((++nEntPos<nMaxPos) && (pEntText[nEntPos]!='}') && (pEntText[nEntPos]!='"')) ;
+
+	// didn't find key, get out
+	if((nEntPos==nMaxPos) || (pEntText[nEntPos]=='}'))
+		return KEY_NONE ;
+
+
+	// copy key
+	while((++nEntPos<nMaxPos) && (nKeyPos<MAX_TOKENSIZE) && (pEntText[nEntPos]!='}') && (pEntText[nEntPos]!='"')) 
+		pKey[++nKeyPos]=pEntText[nEntPos] ;
+
+	if((nEntPos==nMaxPos) || (nKeyPos==MAX_TOKENSIZE) || (pEntText[nEntPos]=='}'))
+		return KEY_ERROR ; // entity was incomplete or too big
+
+	pKey[++nKeyPos]='\0' ;
+
+	/////////////////////////////////////////////////////////////////////////////////////////
+	// value
+
+	// find the next "
+	while((++nEntPos<nMaxPos) && (pEntText[nEntPos]!='}') && (pEntText[nEntPos]!='"')) ;
+
+	// didn't find value, get out
+	if((nEntPos==nMaxPos) || (pEntText[nEntPos]=='}'))
+		return KEY_ERROR ; 
+
+	// copy value
+	while((++nEntPos<nMaxPos) && (nValuePos<MAX_TOKENSIZE) && (pEntText[nEntPos]!='}') && (pEntText[nEntPos]!='"')) 
+		pValue[++nValuePos]=pEntText[nEntPos] ;
+
+	if((nEntPos==nMaxPos) || (nValuePos==MAX_TOKENSIZE) || (pEntText[nEntPos]=='}'))
+		return KEY_ERROR ; // entity was incomplete or too big
+
+	pValue[++nValuePos]='\0' ;
+
+	/////////////////////////////////////////////////////////////////////////////////////////
+
+	*pPos=nEntPos+1 ;
+
+	return KEY_OK ;
+}
+
+
+// fills array pNumber with the values extracted from pValue, returns how many numbers it got.
+// float version
+int Q3Map::GetNumbersFromValue(char* pValue, float *pNumber, int nNumberSize)
+{
+	int nLength=strlen(pValue) ;
+	if(nLength<1) return 0 ;
+
+	int nPos=-1 ;
+	int nCount=0 ;
+	char chTemp[MAX_TOKENSIZE+1] ;
+	int nTempPos=0 ;
+
+	do
+	{
+
+		nPos++ ;
+		
+		if(
+				((pValue[nPos]>='0') && (pValue[nPos]<='9')) // found another digit
+				||
+				(pValue[nPos]=='.')
+				||
+				(pValue[nPos]=='-')
+			)
+		{
+			chTemp[nTempPos++]=pValue[nPos] ;
+			if(nTempPos==MAX_TOKENSIZE) return 0 ; // number too big
+		}
+		else // anything else means the end of the number
+		{
+			
+			chTemp[nTempPos]='\0' ;
+			pNumber[nCount++]=atof(chTemp) ;
+			nTempPos=0 ;
+		}
+		
+
+	}while((nPos<nLength) && (nCount<nNumberSize) && (pValue[nPos]!='\0')) ;
+
+
+	return nCount ;
+}
+
+// integer version
+int Q3Map::GetNumbersFromValue(char* pValue, int *pNumber, int nNumberSize)
+{
+	int nLength=strlen(pValue) ;
+	if(nLength<1) return 0 ;
+
+	int nPos=-1 ;
+	int nCount=0 ;
+	char chTemp[MAX_TOKENSIZE+1] ;
+	int nTempPos=0 ;
+
+	do
+	{
+
+		nPos++ ;
+		
+		if(
+				((pValue[nPos]>='0') && (pValue[nPos]<='9')) // found another digit
+				||
+				(pValue[nPos]=='.')
+				||
+				(pValue[nPos]=='-')
+			)
+		{
+			chTemp[nTempPos++]=pValue[nPos] ;
+			if(nTempPos==MAX_TOKENSIZE) return 0 ; // number too big
+		}
+		else // anything else means the end of the number
+		{
+			
+			chTemp[nTempPos]='\0' ;
+			pNumber[nCount++]=atoi(chTemp) ;
+			nTempPos=0 ;
+		}
+		
+
+	}while((nPos<nLength) && (nCount<nNumberSize) && (pValue[nPos]!='\0')) ;
+
+
+	return nCount ;
+}
+
+//''
+// extracts data for either forward rendered shadow casting spotlights or deferred shading non-shadowing point lights
+// the point lights ("lamps") will later be changed into map triangles.
+int Q3Map::ParseAndAddLight(int* pPos, int nMaxPos)
+{
+//	char chMessage[1024] ;
+
+
+	char chKey[MAX_TOKENSIZE+1] ;   // +1 to leave room for null terminator
+	char chValue[MAX_TOKENSIZE+1] ; // +1 to leave room for null terminator
+	float flOrigin[3]={0.0f, 0.0f, 0.0f} ;
+	float flColour[3]={1.0f, 1.0f, 1.0f} ;
+	float flCentre[3]={0.0f, 0.0f, 0.0f} ;
+	float flAimvec[3]={0.0f, 0.0f, 0.0f} ;
+	float flRadius[3]={0.0f, 0.0f, 0.0f} ;
+	
+	float flTemp[3]={0.0f, 0.0f, 0.0f} ;
+	int nTemp[3] ;
+
+	float flAngle=0.0f ;
+	float flCutoff=0.0f ;
+	float flLength=0.0f ;
+	float flBrightness=0.0f ;
+	int nLightNode=-1 ;
+
+	bool bSpotLight=false ;
+
+	Q3BspTexture Q3Texture ;
+	Q3Texture.flags=0 ;
+	Q3Texture.contents=0 ;
+	Q3Texture.name[0]=0 ;
+
+
+	light_t NewLight ; // forward rendered shadow casting spotlight
+	ZeroMemory((void*)&NewLight, sizeof(light_t)) ;
+
+	lamp_t NewLamp ; // deferred shading non-shadowing point light
+	ZeroMemory((void*)&NewLamp, sizeof(lamp_t)) ;
+
+	int nKeyReturn=KEY_NONE ; 
+
+	do
+	{
+		nKeyReturn=GetEntityKeyAndValue(pPos, nMaxPos, chKey, chValue) ;
+	
+		if(nKeyReturn==KEY_OK) // found a key
+		{
+
+			if(strcmp(chKey, "origin")==0)
+			{
+				if(GetNumbersFromValue(chValue, flOrigin, 3)!=3) return 0 ; // extract the numbers
+				swizzleFloat3(flOrigin) ; // fix coordinates
+			}
+			else
+			if(strcmp(chKey, "_color")==0)
+			{
+				if(GetNumbersFromValue(chValue, flColour, 3)!=3) return 0 ; // extract the numbers
+			}
+			else
+			if(strcmp(chKey, "light_center")==0)
+			{
+				if(GetNumbersFromValue(chValue, flCentre, 3)!=3) return 0 ; // extract the numbers		
+				swizzleFloat3(flCentre) ; // fix coordinates
+			}
+			else
+			if(strcmp(chKey, "light_target")==0)
+			{
+				if(GetNumbersFromValue(chValue, flAimvec, 3)!=3) return 0 ; // extract the numbers		
+				swizzleFloat3(flAimvec) ; // fix coordinates
+				bSpotLight=true ; // if there's a target key, then this must be a spotlight
+			}
+			else
+			if(strcmp(chKey, "light_radius")==0)
+			{
+				if(GetNumbersFromValue(chValue, flRadius, 3)!=3) return 0 ; // extract the numbers
+				swizzleFloat3(flRadius) ; // fix coordinates
+				// make sure all values are positive
+				flRadius[0]=fabs(flRadius[0]) ;
+				flRadius[1]=fabs(flRadius[1]) ;
+				flRadius[2]=fabs(flRadius[2]) ;
+			}
+			else
+			if(strcmp(chKey, "light_abc")==0)
+			{
+				if(GetNumbersFromValue(chValue, flTemp, 3)!=3) return 0 ; // extract the numbers
+				flAngle				= flTemp[0] ;
+				flBrightness	= flTemp[1] ;
+				flCutoff			= flTemp[2] ;
+			}
+			else
+			if(strcmp(chKey, "texture")==0)
+			{
+				strcpy(Q3Texture.name, chValue) ;
+			}
+			if(strcmp(chKey, "light_node")==0)
+			{
+				if(GetNumbersFromValue(chValue, nTemp, 1)!=1) return 0 ; // extract the number
+				nLightNode				= nTemp[0] ;	
+				if((nLightNode<0) || (nLightNode>3)) return 0 ; // something dodgy about the lightnode number
+			}
+
+		}// end if key ok
+
+		
+
+	}while(nKeyReturn==KEY_OK) ;  // end do looping through keys
+
+
+	// return a fail if there was a problem with the keys
+	if(nKeyReturn==KEY_ERROR) return 0 ; 
+
+
+	if(bSpotLight)// found a light_target so this must be a spotlight
+	{
+		// light settings.
+		NewLight.Position[0]=flOrigin[0]+flCentre[0] ;
+		NewLight.Position[1]=flOrigin[1]+flCentre[1] ;
+		NewLight.Position[2]=flOrigin[2]+flCentre[2] ;
+
+		NewLight.Min[0]=flOrigin[0]-flRadius[0] ;
+		NewLight.Min[1]=flOrigin[1]-flRadius[1] ;
+		NewLight.Min[2]=flOrigin[2]-flRadius[2] ;
+
+		NewLight.Max[0]=flOrigin[0]+flRadius[0] ;
+		NewLight.Max[1]=flOrigin[1]+flRadius[1] ;
+		NewLight.Max[2]=flOrigin[2]+flRadius[2] ;
+
+		NewLight.Colour[0]=flColour[0] ;
+		NewLight.Colour[1]=flColour[1] ;
+		NewLight.Colour[2]=flColour[2] ;
+
+		NewLight.Angle=flAngle ;
+		NewLight.Cutoff=flCutoff ;
+		NewLight.Brightness=flBrightness * BRIGHTNESSTWEAK ;
+
+
+		// direction light points, as a normal
+		flLength=sqrt( flAimvec[0]*flAimvec[0] + flAimvec[1]*flAimvec[1] + flAimvec[2]*flAimvec[2] ) ;
+		if(flLength>0.0f)
+		{
+			NewLight.Direction[0]=flAimvec[0]/flLength ;
+			NewLight.Direction[1]=flAimvec[1]/flLength ;
+			NewLight.Direction[2]=flAimvec[2]/flLength ;
+		}
+		else
+		{ // default to pointing down
+			NewLight.Direction[0]=0.0f ;
+			NewLight.Direction[1]=1.0f ;
+			NewLight.Direction[2]=0.0f ;
+		}
+
+		NewLight.ZoneCount=0 ;
+		NewLight.CentreZone=0 ;
+	
+		
+		if(Q3Texture.name[0]==0)	
+			strcpy(Q3Texture.name, "spotlight.dds") ;
+		
+		NewLight.Texture=AddSpolightTexture(Q3Texture.name) ;
+
+
+		if(NewLight.Texture==ADDSPOTLIGHTTEXTURE_FAIL)
+			return 0 ; // failure	
+	
+		//AddTextureUnique(Q3Texture) ;
+		/*
+		// add the light's texture index
+		if(Q3Texture.name[0]==0)
+			NewLight.Texture=m_nDefaultTextureIndexSpotlight ;
+		else
+		{
+			NewLight.Texture=AddTextureUnique(Q3Texture) ; // this will add the texture name to the list if it is unique, as well as setting the index
+			if(NewLight.Texture==ADDTEXTUREUNIQUE_FAIL) 
+				return 0 ;
+		}
+		*/
+
+		return AddLight(NewLight) ;
+	}
+	else // add a non-shadowing deferred shading point light
+	{
+		// light settings.
+		NewLamp.Position[0]=flOrigin[0]+flCentre[0] ;
+		NewLamp.Position[1]=flOrigin[1]+flCentre[1] ;
+		NewLamp.Position[2]=flOrigin[2]+flCentre[2] ;
+
+		NewLamp.Min[0]=flOrigin[0]-flRadius[0] ;
+		NewLamp.Min[1]=flOrigin[1]-flRadius[1] ;
+		NewLamp.Min[2]=flOrigin[2]-flRadius[2] ;
+
+		NewLamp.Max[0]=flOrigin[0]+flRadius[0] ;
+		NewLamp.Max[1]=flOrigin[1]+flRadius[1] ;
+		NewLamp.Max[2]=flOrigin[2]+flRadius[2] ;
+
+		NewLamp.Colour[0]=flColour[0] ;
+		NewLamp.Colour[1]=flColour[1] ;
+		NewLamp.Colour[2]=flColour[2] ;
+
+		NewLamp.Brightness=flBrightness * BRIGHTNESSTWEAK ;
+
+		NewLamp.LightNode=nLightNode ; // typically -1, but may be 0 to 3 if this lamp is the node for some TexLamp freeform deferred shading geometry.
+
+
+		// Note that m_pLamp.Zone will be set after we first convert lamps into triangles and then 
+		// assign those triangles to zones.  At that point, if the assigned triangle is also flagged as
+		// coming from a lamp, then the lamp's list of zones will be updated.
+
+				// add the light's texture index
+		if(Q3Texture.name[0]==0)
+		{
+			if(nLightNode==-1)
+				NewLamp.Texture=m_nDefaultTextureIndexLamp ; // normal 1 pass deferred shading
+			else
+				NewLamp.Texture=m_nDefaultTextureIndexLamp2Pass ; // special 2 pass deferred shading to texlamps
+		}
+		else
+		{
+			NewLamp.Texture=AddTextureUnique(Q3Texture) ; // this will add the texture name to the list if it is unique, as well as setting the index
+			if(NewLamp.Texture==ADDTEXTUREUNIQUE_FAIL) 
+				return 0 ;
+		}	
+
+		return AddLamp(NewLamp) ;
+	}
+
+}
+
+// adds a spotlight texture name if it is unique, returns the index to that texture name either way.
+// returns ADDSPOTLIGHTTEXTURE_FAIL on a fail
+
+int Q3Map::AddSpolightTexture(char TEXNAME[])
+{
+	if((strlen(TEXNAME)>Q3NAMESIZE) || (m_nMaxSpotlightTexture>=MAX_PROJECTORTEX))
+		return ADDSPOTLIGHTTEXTURE_FAIL ;
+
+
+	// scan through all the newly added textures so far and see if this one already exists.
+	int nTexture=0 ;
+	int nPos=0 ;
+
+	bool bMatch ;
+
+	for(nTexture=0 ; nTexture<m_nMaxSpotlightTexture ; nTexture++)
+	{
+		bMatch=true ;
+		// scan through the characters of the texture names, comparing them. 
+		for(nPos=0 ; nPos<Q3NAMESIZE ; nPos++)
+		{
+			// do the two textures have a difference in the name at this position?
+			if(m_chSpotlightTexture[nTexture][nPos]!=TEXNAME[nPos])
+			{
+				bMatch=false ;
+				break ;
+			}
+
+			// is it the end of the texture name?  
+			if(TEXNAME[nPos]=='\0') // end of texture
+				break ;
+		
+		}// end scanning name
+
+		if(bMatch) // found a match, so return ok but don't add a texture
+			return nTexture ;  // we don't add any new texture, return this texture's index
+	}
+
+	// if we got this far, we must have a unique texture
+	strcpy(m_chSpotlightTexture[m_nMaxSpotlightTexture], TEXNAME) ;
+	m_nMaxSpotlightTexture++ ;
+
+	return m_nMaxSpotlightTexture-1 ; // return this new texture's index
+
+}
+
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+int Q3Map::initFaces(void)
+{
+
+	m_nGroup=0 ; // reset the group counter, used to work out transparent groupings for triangles.
+	
+  Q3BspFace_t *faces = getFaces();
+
+  m_BspFaces = new Q3BspFaceRenderer[m_iNumFaces];
+	if(m_BspFaces==NULL) return 0 ; // fail, out of memory.
+  m_NumBspFaces = m_iNumFaces;
+
+	
+  for (int i=0; i < m_NumBspFaces; i++)
+  {					
+    m_BspFaces[i].lm_index = faces[i].lm_index;
+    m_BspFaces[i].meshvert = faces[i].meshvert;
+    m_BspFaces[i].n_meshverts = faces[i].n_meshverts;
+    m_BspFaces[i].n_vertexes = faces[i].n_vertexes;
+    for (int j=0; j<3; j++)
+      m_BspFaces[i].normal[j] = faces[i].normal[j];
+    m_BspFaces[i].texture = faces[i].texture;
+    m_BspFaces[i].type = faces[i].type;
+    m_BspFaces[i].vertex = faces[i].vertex;
+
+    m_BspFaces[i].n_triangles = m_BspFaces[i].n_meshverts / 3;
+
+    if (m_BspFaces[i].type == PATCH)
+    {
+      m_BspFaces[i].patch = handlePatch(i);
+    }
+    else
+    {
+      m_BspFaces[i].patch = NULL;
+    }
+
+
+  }
+
+
+	// check patches aren't degenerate
+	int numIndex = 0;
+  int numVertex = 0;
+
+
+    for (int i=0; i < m_NumBspFaces; i++)
+    {		
+
+			numIndex = 0;
+			numVertex = 0;
+
+      if ((m_BspFaces[i].type == PATCH) && (m_BspFaces[i].patch != NULL))
+			{
+          for (int j=0; j < m_BspFaces[i].patch->size; j++)
+          {
+            numIndex += m_BspFaces[i].patch->bezier[j].mNumIndex;
+            numVertex += m_BspFaces[i].patch->bezier[j].mNumVertex;
+          }
+
+					if((numIndex==0) || (numVertex==0))
+					{
+						DELETE_ARRAY( m_BspFaces[i].patch->bezier ) ;
+						DELETE_POINTER( m_BspFaces[i].patch ) ;
+					}
+
+      }// end if patch
+
+
+    }// end for 
+
+
+
+	// copy the vertices over.  
+	// We need to work on a copy because we need to create new verts when splitting triangles that cross subzones, and for patches
+	for(int i=0 ; i<m_iNumVertices ; i++)
+	{
+		m_pVertex[i]=m_pVertices[i] ;
+		m_nVertexMax++ ; // need to update this manually since we aren't adding new verts, but filling in the original mem.
+	}
+
+
+	return 1 ;
+
+}
+
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// stuff for assigning triangles to subzones, splitting them where necessary.
+//
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+void Q3Map::SetupZones(void)
+{
+	int nSubZone=0 ;
+	int nZone=0 ;
+	int nPos=0 ;
+
+//	float flMinX=0.0f ;
+//	float flMinY=0.0f ;
+//	float flMinZ=0.0f ;
+//	float flMaxX=0.0f ;
+//	float flMaxY=0.0f ;
+//	float flMaxZ=0.0f ;
+
+	m_nMaxZone=0 ;
+	
+	for(nZone=0 ; nZone<MAX_ZONE ; nZone++)
+	{
+		m_nZone[nZone][INDEX_SUBZONECOUNT]=0 ;
+		m_ZoneBoundary[nZone].Min[0]=MINMAXLIMIT ;
+		m_ZoneBoundary[nZone].Min[1]=MINMAXLIMIT ;
+		m_ZoneBoundary[nZone].Min[2]=MINMAXLIMIT ;
+		m_ZoneBoundary[nZone].Max[0]=-MINMAXLIMIT ;
+		m_ZoneBoundary[nZone].Max[1]=-MINMAXLIMIT ;
+		m_ZoneBoundary[nZone].Max[2]=-MINMAXLIMIT ;
+	}
+
+
+	// find the highest used zone number, then +1 to get our limit.
+	for(nSubZone=0 ; nSubZone<m_iNumSubZones ; nSubZone++)
+		if(m_pSubZones[nSubZone].Zone>m_nMaxZone)
+			m_nMaxZone=m_pSubZones[nSubZone].Zone ;
+
+	m_nMaxZone++ ; // our limit
+
+	
+	//char chMessage[1024] ;
+
+
+	// fill in what subzones are in each zone
+	for(nSubZone=0 ; nSubZone<m_iNumSubZones ; nSubZone++)
+	{
+		
+
+		nZone=m_pSubZones[nSubZone].Zone ;
+
+		// find next empty slot in this zone
+		nPos=0 ;
+		while((nPos<MAX_SUBZONEPERZONE) && (nPos<m_nZone[nZone][INDEX_SUBZONECOUNT]))
+			nPos++ ;
+
+		// if there's room, add the subzone to this zone
+		if(nPos<MAX_SUBZONEPERZONE) 
+		{
+			m_nZone[nZone][nPos]=nSubZone ;
+
+			// limits used for cutting up lights (not useful for other boundary stuff, check subzones instead)
+			if(m_pSubZones[nSubZone].Min[0]<m_ZoneBoundary[nZone].Min[0]) 
+				m_ZoneBoundary[nZone].Min[0]= m_pSubZones[nSubZone].Min[0] ;
+			if(m_pSubZones[nSubZone].Min[1]<m_ZoneBoundary[nZone].Min[1]) 
+				m_ZoneBoundary[nZone].Min[1]= m_pSubZones[nSubZone].Min[1] ;
+			if(m_pSubZones[nSubZone].Min[2]<m_ZoneBoundary[nZone].Min[2]) 
+				m_ZoneBoundary[nZone].Min[2]= m_pSubZones[nSubZone].Min[2] ;
+
+			if(m_pSubZones[nSubZone].Max[0]>m_ZoneBoundary[nZone].Max[0]) 
+				m_ZoneBoundary[nZone].Max[0]= m_pSubZones[nSubZone].Max[0] ;
+			if(m_pSubZones[nSubZone].Max[1]>m_ZoneBoundary[nZone].Max[1]) 
+				m_ZoneBoundary[nZone].Max[1]= m_pSubZones[nSubZone].Max[1] ;
+			if(m_pSubZones[nSubZone].Max[2]>m_ZoneBoundary[nZone].Max[2]) 
+				m_ZoneBoundary[nZone].Max[2]= m_pSubZones[nSubZone].Max[2] ;
+
+			m_nZone[nZone][INDEX_SUBZONECOUNT]++ ;
+		}
+
+
+	}
+
+
+}
+
+
+// work out what zone each triangle is in.
+// if it is in more than one, cut it up into smaller triangles that are only in one zone each.
+int Q3Map::AssignTrianglesToZones(void)
+{
+	int nCurrentTriangle=0 ;
+//	int nZone=0 ;
+
+
+	/*
+	char chMessage[1024] ;
+	float flPos[3] ;
+	int nTri=0 ;
+	float flVert[3][3] ;
+	
+
+	for(nTri=0 ; nTri<m_nTriangleMax ; nTri++)
+	{
+		flVert[0][0]=m_pVertex[  m_pTriangle[nTri].VIndex[0]  ].position[0] ;
+		flVert[0][1]=m_pVertex[  m_pTriangle[nTri].VIndex[0]  ].position[1] ;
+		flVert[0][2]=m_pVertex[  m_pTriangle[nTri].VIndex[0]  ].position[2] ;
+
+		flVert[1][0]=m_pVertex[  m_pTriangle[nTri].VIndex[1]  ].position[0] ;
+		flVert[1][1]=m_pVertex[  m_pTriangle[nTri].VIndex[1]  ].position[1] ;
+		flVert[1][2]=m_pVertex[  m_pTriangle[nTri].VIndex[1]  ].position[2] ;
+
+		flVert[2][0]=m_pVertex[  m_pTriangle[nTri].VIndex[2]  ].position[0] ;
+		flVert[2][1]=m_pVertex[  m_pTriangle[nTri].VIndex[2]  ].position[1] ;
+		flVert[2][2]=m_pVertex[  m_pTriangle[nTri].VIndex[2]  ].position[2] ;
+
+
+		flPos[0]=(flVert[0][0]+flVert[1][0]+flVert[2][0])/3.0f ;
+		flPos[1]=(flVert[0][1]+flVert[1][1]+flVert[2][1])/3.0f ;
+		flPos[2]=(flVert[0][2]+flVert[1][2]+flVert[2][2])/3.0f ;
+
+		nZone=0 ;
+		while((nZone<m_iNumSubZones) && !PointInZone(flPos, nZone))
+			nZone++ ;
+
+
+
+
+	}
+	*/
+
+
+
+	for(nCurrentTriangle=0 ; nCurrentTriangle<m_nTriangleMax ; nCurrentTriangle++)
+	{
+		if(!SetupTriangleZone(nCurrentTriangle)) return 0 ; // what zone completely contains this triangle, if any
+
+	}// end for current triangle
+
+	return 1 ;
+}
+
+// return the zone this triangle is in, or -1 if it is not entirely contained by any zone
+// this is also a convienient spot for us to update Lamp zones when we discover what zones its triangles are in,
+// and also a handy place to note if the triangle is a TexLamp or not 
+// (TexLamp is free form geometry that is bound to a lamp and gets converted to deferred shading lights, allowing us to do fake shadowing)
+
+int Q3Map::FindTriangleZone(int nTriangle)
+{
+	int nZone=0 ;
+//	int nSubZone=0 ;
+	int nPos=0 ;
+	bool bVertInSubZone=false ;
+	int nVert=0 ;
+	bool bTriangleInZone=false ;
+//	int nMaxSubZone=m_iNumSubZones ;
+	float flVert[6][3] ; // verts 0, 1, 2 are the original triangle corners, verts 3, 4, 5 are interpolated edge points.
+	// we need the edge points, since it's possible for all the 
+	// triangle verts to be in one L shaped zone but the triangle they form not be in that zone. 
+	// (such as if there's a vert at the corner and two ends of the "L")
+
+
+	flVert[0][0]=m_pVertex[  m_pTriangle[nTriangle].VIndex[0]  ].position[0] ;
+	flVert[0][1]=m_pVertex[  m_pTriangle[nTriangle].VIndex[0]  ].position[1] ;
+	flVert[0][2]=m_pVertex[  m_pTriangle[nTriangle].VIndex[0]  ].position[2] ;
+
+	flVert[1][0]=m_pVertex[  m_pTriangle[nTriangle].VIndex[1]  ].position[0] ;
+	flVert[1][1]=m_pVertex[  m_pTriangle[nTriangle].VIndex[1]  ].position[1] ;
+	flVert[1][2]=m_pVertex[  m_pTriangle[nTriangle].VIndex[1]  ].position[2] ;
+
+	flVert[2][0]=m_pVertex[  m_pTriangle[nTriangle].VIndex[2]  ].position[0] ;
+	flVert[2][1]=m_pVertex[  m_pTriangle[nTriangle].VIndex[2]  ].position[1] ;
+	flVert[2][2]=m_pVertex[  m_pTriangle[nTriangle].VIndex[2]  ].position[2] ;
+
+	flVert[3][0]=(flVert[0][0]+flVert[1][0])/2.0f ;
+	flVert[3][1]=(flVert[0][1]+flVert[1][1])/2.0f ;
+	flVert[3][2]=(flVert[0][2]+flVert[1][2])/2.0f ;
+
+	flVert[4][0]=(flVert[1][0]+flVert[2][0])/2.0f ;
+	flVert[4][1]=(flVert[1][1]+flVert[2][1])/2.0f ;
+	flVert[4][2]=(flVert[1][2]+flVert[2][2])/2.0f ;
+
+	flVert[5][0]=(flVert[2][0]+flVert[0][0])/2.0f ;
+	flVert[5][1]=(flVert[2][1]+flVert[0][1])/2.0f ;
+	flVert[5][2]=(flVert[2][2]+flVert[0][2])/2.0f ;
+
+
+	// scan through the zones until:
+	// a) we find a zone that completely contains the six vertices
+	// b) we run out of zones.
+
+
+	do
+	{
+		nVert=0 ;
+		bTriangleInZone=false ;
+
+		// scan through the verts until:
+		// a) we find a vert that isn't in this zone
+		// b) we run out of verts
+
+		do
+		{
+
+			// scan through the zone's subzones until: 
+			// a) we find one that contains this vert, 
+			// b) we hit the subzoneperzone limit, 
+			// c) this zone runs out of subzones
+
+			nPos=0 ;
+			do
+			{
+				bVertInSubZone=PointInSubZone(flVert[nVert], m_nZone[nZone][nPos]) ;
+			}while( !bVertInSubZone && (++nPos<MAX_SUBZONEPERZONE) && (nPos<m_nZone[nZone][INDEX_SUBZONECOUNT])) ;
+
+			// if bVertInSubZone is false, we found a vert that isn't in this zone.
+
+		}while(bVertInSubZone && (++nVert<6)) ;
+
+		if(bVertInSubZone) bTriangleInZone=true ;
+
+	}while(!bTriangleInZone && (++nZone<m_nMaxZone)) ;
+
+
+	
+
+
+	if(!bTriangleInZone)
+		return -1 ;
+	else
+	{
+
+		// if this triangle came from a lamp entity, we note in that lamp entity that it touches this zone
+		int nLamp=m_pTriangle[nTriangle].Lamp ;
+		if(nLamp>-1) // if we have a lamp
+		{
+
+
+			int nSlot=m_pLamp[nLamp].Zone[MAX_ZONEPERLIGHT] ;
+
+			if(nSlot<MAX_ZONEPERLIGHT) // if the lamp isn't maxed out on zones
+			{
+				// check if we already have this zone recorded
+				int nCheckSlot=0 ;
+				int nFoundDuplicate=0 ;
+				for(nCheckSlot=0 ; nCheckSlot<nSlot ; nCheckSlot++)
+					if(m_pLamp[nLamp].Zone[nCheckSlot]==nZone)
+					{
+						nFoundDuplicate=1 ;
+						break ;
+					}
+
+				if(!nFoundDuplicate)
+				{
+					m_pLamp[nLamp].Zone[nSlot]=nZone ;  // write the zone into this slot
+					m_pLamp[nLamp].Zone[MAX_ZONEPERLIGHT]++ ; // note that we have one more zone
+				}
+
+			}
+		}
+
+
+		// if this triangle is a TexLamp triangle (free form deferred lighting shapes)
+		// then we remember this, in order to make assigning it to a lamp faster
+		int nTexture=m_pTriangle[nTriangle].Texture ;
+		if((nTexture==m_nBZN_LightNode0) || (nTexture==m_nBZN_LightNode1) || (nTexture==m_nBZN_LightNode2) || (nTexture==m_nBZN_LightNode3))
+			if(!AddTexLamp(nTriangle)) 
+				return ADDTEXLAMP_FAIL ; //if we failed to note the texlamp (probably out of memory), the whole level load will fail.
+
+
+		// finally, return the zone the triangle is in.
+		return nZone ;
+	}
+}
+
+
+
+// work out what zone a triangle is in, cut it up if it's in more than one.
+int Q3Map::SetupTriangleZone(int nTriangle)
+{
+
+
+	int nZone=0 ;
+
+	nZone=FindTriangleZone(nTriangle) ;
+
+	if(nZone==ADDTEXLAMP_FAIL)
+		return 0 ; 
+
+
+	if(nZone!=-1) // triangle was completely in a zone
+		m_pTriangle[nTriangle].Zone=nZone ;
+	else
+	{
+		// This triangle is in more than one zone.  (Or no subzone at all)
+		// we chop it up along the edges of every subzone it is in (regardless of whether they are a common zone or not)
+		// so that the resulting triangles will all be in just one subzone, and therefore in only one zone.
+		// this might produce a few extra triangles more than we strictly need, since some will both be in the same zone,
+		// but it is simple and the extra triangles are trivial in number.
+
+		// As we go to each new cut plane, it must be applied progressively to all newly created triangles too.
+
+		int nInitialTrianglePos=m_nTriangleMax ; // where we start adding new triangles
+
+		int nTriLoop=0 ;
+		int nTriangleToCut=0 ;
+		int nMaxTriangle=0 ;
+		int nSide=0 ;
+		int nAxis=0 ;
+		float flCutPos=0.0f ;
+		int nSubZone=0 ;
+		int nMaxSubZone=m_iNumSubZones ;
+		int nVert=0 ;
+		float flVert[6][3] ; // verts 0, 1, 2 are the original triangle corners, verts 3, 4, 5 are interpolated edge points.
+		// we need the edge points, since it's possible for all the 
+		// triangle verts to be in one L shaped zone but the triangle they form not be in that zone. 
+		// (such as if there's a vert at the corner and two ends of the "L")
+
+
+		flVert[0][0]=m_pVertex[  m_pTriangle[nTriangle].VIndex[0]  ].position[0] ;
+		flVert[0][1]=m_pVertex[  m_pTriangle[nTriangle].VIndex[0]  ].position[1] ;
+		flVert[0][2]=m_pVertex[  m_pTriangle[nTriangle].VIndex[0]  ].position[2] ;
+
+		flVert[1][0]=m_pVertex[  m_pTriangle[nTriangle].VIndex[1]  ].position[0] ;
+		flVert[1][1]=m_pVertex[  m_pTriangle[nTriangle].VIndex[1]  ].position[1] ;
+		flVert[1][2]=m_pVertex[  m_pTriangle[nTriangle].VIndex[1]  ].position[2] ;
+
+		flVert[2][0]=m_pVertex[  m_pTriangle[nTriangle].VIndex[2]  ].position[0] ;
+		flVert[2][1]=m_pVertex[  m_pTriangle[nTriangle].VIndex[2]  ].position[1] ;
+		flVert[2][2]=m_pVertex[  m_pTriangle[nTriangle].VIndex[2]  ].position[2] ;
+
+		flVert[3][0]=(flVert[0][0]+flVert[1][0])/2.0f ;
+		flVert[3][1]=(flVert[0][1]+flVert[1][1])/2.0f ;
+		flVert[3][2]=(flVert[0][2]+flVert[1][2])/2.0f ;
+
+		flVert[4][0]=(flVert[1][0]+flVert[2][0])/2.0f ;
+		flVert[4][1]=(flVert[1][1]+flVert[2][1])/2.0f ;
+		flVert[4][2]=(flVert[1][2]+flVert[2][2])/2.0f ;
+
+		flVert[5][0]=(flVert[2][0]+flVert[0][0])/2.0f ;
+		flVert[5][1]=(flVert[2][1]+flVert[0][1])/2.0f ;
+		flVert[5][2]=(flVert[2][2]+flVert[0][2])/2.0f ;
+	
+
+
+
+		for(nSubZone=0 ; nSubZone<nMaxSubZone ; nSubZone++)
+		{
+			// are any of our original verts in this subzone?
+
+			nVert=0 ;
+			while( !PointInSubZone(flVert[nVert], nSubZone) && (++nVert<6)) ;
+
+			if(nVert<6) // one of the verts must have been in this subzone.
+			{
+				// cutup all triangles by the sides of this subzone.
+				// we'll need to cut more and more triangles as this progresses. (or at least test if they need to be cut)
+				for(nSide=0 ; nSide<6 ; nSide++)
+				{
+					switch(nSide)
+					{
+						case 0: nAxis=AXIS_X ; flCutPos=m_pSubZones[nSubZone].Min[0] ; break ;
+						case 1: nAxis=AXIS_X ; flCutPos=m_pSubZones[nSubZone].Max[0] ; break ;
+						case 2: nAxis=AXIS_Y ; flCutPos=m_pSubZones[nSubZone].Min[1] ; break ;
+						case 3: nAxis=AXIS_Y ; flCutPos=m_pSubZones[nSubZone].Max[1] ; break ;
+						case 4: nAxis=AXIS_Z ; flCutPos=m_pSubZones[nSubZone].Min[2] ; break ;
+						case 5: nAxis=AXIS_Z ; flCutPos=m_pSubZones[nSubZone].Max[2] ; break ;
+					}
+
+
+					nMaxTriangle=m_nTriangleMax-nInitialTrianglePos ;  // how may new triangles have been created since we first started cutting the original.
+
+					for(nTriLoop=-1 ; nTriLoop<nMaxTriangle ; nTriLoop++)
+					{
+						// work out if we are cutting up the original triangle or one of the newly created ones.
+						if(nTriLoop==-1)
+							nTriangleToCut=nTriangle ; // the original triangle, perhaps heavily cutup by now.
+						else
+							nTriangleToCut=nInitialTrianglePos+nTriLoop ;  // one of the newly created triangles.
+
+						if(!SplitTriangle(nTriangleToCut, nAxis, flCutPos)) return 0 ; // cut up the triangle, fail if we're out of memory or whatever.
+
+
+					}// end for nTriLoop
+
+				}// end cutting by each side of the subzone
+
+			}// end if one of the verts was in this subzone
+
+		}// end going through all subzones
+
+
+		// now that the triangle is well and truly chopped up, assign it a zone.
+		// Even though it should be entirely in a subzone by now, there's still the chance that
+		// it might not be inside any subzone at all.  If so, it will be assigned -1 zone and 
+		// exluded from the manualobjects
+		
+		m_pTriangle[nTriangle].Zone=FindTriangleZone(nTriangle) ;
+
+		// we don't have to worry about assigning zones to the newly created triangles, 
+		// they'll get theirs when the AssignTrianglesToZones loop reaches them at the end.
+
+	}// end if triangle was in more than one subzone (or no subzone at all)
+
+	return 1 ;
+}
+
+
+// cut a triangle along some axial plane, turning into 2 or 3 triangles.
+// If the plane doesn't go through the triangle then nothing will happen.
+int Q3Map::SplitTriangle(int nTriangle, int nAxis, float flCutPos)
+{
+
+	triangle_t NewTri ;
+
+	// these will stay -1 if no vert is created, else will be index of the new vert
+	int nABNum=-1 ;
+	int nBCNum=-1 ;
+	int nCANum=-1 ;
+
+	Q3BspVertex	VertA = m_pVertex[  m_pTriangle[ nTriangle ].VIndex[0]  ] ;
+	Q3BspVertex	VertB = m_pVertex[  m_pTriangle[ nTriangle ].VIndex[1]  ] ;
+	Q3BspVertex	VertC = m_pVertex[  m_pTriangle[ nTriangle ].VIndex[2]  ] ;
+	Q3BspVertex	VertexAB ;
+	Q3BspVertex	VertexBC ;
+	Q3BspVertex	VertexCA ;
+
+	float flSpan=0.0f ;
+	float flCutSpan=0.0f ;
+	float flPercent=0.0f ;
+
+	switch(nAxis)
+	{
+
+		////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+		case AXIS_X:
+
+			////////////////////////////////////////////////////////////////////////////////////////////////
+			// if VertA is on one side of the cut and VertB is on the other, create VertexAB on the cutline.
+			if( 
+					((VertA.position[0]<flCutPos-SUBZONE_EPSILON) && (VertB.position[0]>flCutPos+SUBZONE_EPSILON))
+					||
+					((VertA.position[0]>flCutPos+SUBZONE_EPSILON) && (VertB.position[0]<flCutPos-SUBZONE_EPSILON))
+				)
+			{
+				// work out the span and percentage
+				if(VertA.position[0]<flCutPos)
+				{
+					flSpan=VertB.position[0]-VertA.position[0] ;
+					flCutSpan=flCutPos-VertA.position[0] ;
+					flPercent=1.0f-flCutSpan/flSpan ;
+				}
+				else
+				{
+					flSpan=VertA.position[0]-VertB.position[0] ;
+					flCutSpan=flCutPos-VertB.position[0] ;
+					flPercent=flCutSpan/flSpan ;
+				}
+
+				CreateTweenVert(&VertA, &VertB, flPercent, &VertexAB) ;
+				nABNum=m_nVertexMax ;
+				if(!AddVertex(VertexAB)) 
+					return 0 ;
+			}// end if need create VertexAB
+
+			////////////////////////////////////////////////////////////////////////////////////////////////
+			// if VertB is on one side of the cut and VertC is on the other, create VertexBC on the cutline.
+			if( 
+					((VertB.position[0]<flCutPos-SUBZONE_EPSILON) && (VertC.position[0]>flCutPos+SUBZONE_EPSILON))
+					||
+					((VertB.position[0]>flCutPos+SUBZONE_EPSILON) && (VertC.position[0]<flCutPos-SUBZONE_EPSILON))
+				)
+			{
+				// work out the span and percentage
+				if(VertB.position[0]<flCutPos)
+				{
+					flSpan=VertC.position[0]-VertB.position[0] ;
+					flCutSpan=flCutPos-VertB.position[0] ;
+					flPercent=1.0f-flCutSpan/flSpan ;
+				}
+				else
+				{
+					flSpan=VertB.position[0]-VertC.position[0] ;
+					flCutSpan=flCutPos-VertC.position[0] ;
+					flPercent=flCutSpan/flSpan ;
+				}
+
+				CreateTweenVert(&VertB, &VertC, flPercent, &VertexBC) ;
+				nBCNum=m_nVertexMax ;
+				if(!AddVertex(VertexBC)) 
+					return 0 ;
+
+			}// end if need create VertexBC
+
+			////////////////////////////////////////////////////////////////////////////////////////////////
+			// if VertC is on one side of the cut and VertA is on the other, create VertexCA on the cutline.
+			if( 
+					((VertC.position[0]<flCutPos) && (VertA.position[0]>flCutPos))
+					||
+					((VertC.position[0]>flCutPos) && (VertA.position[0]<flCutPos))
+				)
+			{
+				// work out the span and percentage
+				if(VertC.position[0]<flCutPos)
+				{
+					flSpan=VertA.position[0]-VertC.position[0] ;
+					flCutSpan=flCutPos-VertC.position[0] ;
+					flPercent=1.0f-flCutSpan/flSpan ;
+				}
+				else
+				{
+					flSpan=VertC.position[0]-VertA.position[0] ;
+					flCutSpan=flCutPos-VertA.position[0] ;
+					flPercent=flCutSpan/flSpan ;
+				}
+
+				CreateTweenVert(&VertC, &VertA, flPercent, &VertexCA) ;
+				nCANum=m_nVertexMax ;
+				if(!AddVertex(VertexCA)) 
+					return 0 ;
+
+			}// end if need create VertexCA
+			break ;
+
+		
+		////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+		case AXIS_Y:
+
+			////////////////////////////////////////////////////////////////////////////////////////////////
+			// if VertA is on one side of the cut and VertB is on the other, create VertexAB on the cutline.
+			if( 
+					((VertA.position[1]<flCutPos-SUBZONE_EPSILON) && (VertB.position[1]>flCutPos+SUBZONE_EPSILON))
+					||
+					((VertA.position[1]>flCutPos+SUBZONE_EPSILON) && (VertB.position[1]<flCutPos-SUBZONE_EPSILON))
+				)
+			{
+				// work out the span and percentage
+				if(VertA.position[1]<flCutPos)
+				{
+					flSpan=VertB.position[1]-VertA.position[1] ;
+					flCutSpan=flCutPos-VertA.position[1] ;
+					flPercent=1.0f-flCutSpan/flSpan ;
+				}
+				else
+				{
+					flSpan=VertA.position[1]-VertB.position[1] ;
+					flCutSpan=flCutPos-VertB.position[1] ;
+					flPercent=flCutSpan/flSpan ;
+				}
+
+				CreateTweenVert(&VertA, &VertB, flPercent, &VertexAB) ;
+				nABNum=m_nVertexMax ;
+				if(!AddVertex(VertexAB)) 
+					return 0 ;
+			}// end if need create VertexAB
+
+			////////////////////////////////////////////////////////////////////////////////////////////////
+			// if VertB is on one side of the cut and VertC is on the other, create VertexBC on the cutline.
+			if( 
+					((VertB.position[1]<flCutPos-SUBZONE_EPSILON) && (VertC.position[1]>flCutPos+SUBZONE_EPSILON))
+					||
+					((VertB.position[1]>flCutPos+SUBZONE_EPSILON) && (VertC.position[1]<flCutPos-SUBZONE_EPSILON))
+				)
+			{
+				// work out the span and percentage
+				if(VertB.position[1]<flCutPos)
+				{
+					flSpan=VertC.position[1]-VertB.position[1] ;
+					flCutSpan=flCutPos-VertB.position[1] ;
+					flPercent=1.0f-flCutSpan/flSpan ;
+				}
+				else
+				{
+					flSpan=VertB.position[1]-VertC.position[1] ;
+					flCutSpan=flCutPos-VertC.position[1] ;
+					flPercent=flCutSpan/flSpan ;
+				}
+
+				CreateTweenVert(&VertB, &VertC, flPercent, &VertexBC) ;
+				nBCNum=m_nVertexMax ;
+				if(!AddVertex(VertexBC)) 
+					return 0 ;
+
+			}// end if need create VertexBC
+
+			////////////////////////////////////////////////////////////////////////////////////////////////
+			// if VertC is on one side of the cut and VertA is on the other, create VertexCA on the cutline.
+			if( 
+					((VertC.position[1]<flCutPos) && (VertA.position[1]>flCutPos))
+					||
+					((VertC.position[1]>flCutPos) && (VertA.position[1]<flCutPos))
+				)
+			{
+				// work out the span and percentage
+				if(VertC.position[1]<flCutPos)
+				{
+					flSpan=VertA.position[1]-VertC.position[1] ;
+					flCutSpan=flCutPos-VertC.position[1] ;
+					flPercent=1.0f-flCutSpan/flSpan ;
+				}
+				else
+				{
+					flSpan=VertC.position[1]-VertA.position[1] ;
+					flCutSpan=flCutPos-VertA.position[1] ;
+					flPercent=flCutSpan/flSpan ;
+				}
+
+				CreateTweenVert(&VertC, &VertA, flPercent, &VertexCA) ;
+				nCANum=m_nVertexMax ;
+				if(!AddVertex(VertexCA)) 
+					return 0 ;
+
+			}// end if need create VertexCA
+			break ;
+
+		////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+		case AXIS_Z:
+
+			////////////////////////////////////////////////////////////////////////////////////////////////
+			// if VertA is on one side of the cut and VertB is on the other, create VertexAB on the cutline.
+			if( 
+					((VertA.position[2]<flCutPos-SUBZONE_EPSILON) && (VertB.position[2]>flCutPos+SUBZONE_EPSILON))
+					||
+					((VertA.position[2]>flCutPos+SUBZONE_EPSILON) && (VertB.position[2]<flCutPos-SUBZONE_EPSILON))
+				)
+			{
+				// work out the span and percentage
+				if(VertA.position[2]<flCutPos)
+				{
+					flSpan=VertB.position[2]-VertA.position[2] ;
+					flCutSpan=flCutPos-VertA.position[2] ;
+					flPercent=1.0f-flCutSpan/flSpan ;
+				}
+				else
+				{
+					flSpan=VertA.position[2]-VertB.position[2] ;
+					flCutSpan=flCutPos-VertB.position[2] ;
+					flPercent=flCutSpan/flSpan ;
+				}
+
+				CreateTweenVert(&VertA, &VertB, flPercent, &VertexAB) ;
+				nABNum=m_nVertexMax ;
+				if(!AddVertex(VertexAB)) 
+					return 0 ;
+			}// end if need create VertexAB
+
+			////////////////////////////////////////////////////////////////////////////////////////////////
+			// if VertB is on one side of the cut and VertC is on the other, create VertexBC on the cutline.
+			if( 
+					((VertB.position[2]<flCutPos-SUBZONE_EPSILON) && (VertC.position[2]>flCutPos+SUBZONE_EPSILON))
+					||
+					((VertB.position[2]>flCutPos+SUBZONE_EPSILON) && (VertC.position[2]<flCutPos-SUBZONE_EPSILON))
+				)
+			{
+				// work out the span and percentage
+				if(VertB.position[2]<flCutPos)
+				{
+					flSpan=VertC.position[2]-VertB.position[2] ;
+					flCutSpan=flCutPos-VertB.position[2] ;
+					flPercent=1.0f-flCutSpan/flSpan ;
+				}
+				else
+				{
+					flSpan=VertB.position[2]-VertC.position[2] ;
+					flCutSpan=flCutPos-VertC.position[2] ;
+					flPercent=flCutSpan/flSpan ;
+				}
+
+				CreateTweenVert(&VertB, &VertC, flPercent, &VertexBC) ;
+				nBCNum=m_nVertexMax ;
+				if(!AddVertex(VertexBC)) 
+					return 0 ;
+
+			}// end if need create VertexBC
+
+			////////////////////////////////////////////////////////////////////////////////////////////////
+			// if VertC is on one side of the cut and VertA is on the other, create VertexCA on the cutline.
+			if( 
+					((VertC.position[2]<flCutPos) && (VertA.position[2]>flCutPos))
+					||
+					((VertC.position[2]>flCutPos) && (VertA.position[2]<flCutPos))
+				)
+			{
+				// work out the span and percentage
+				if(VertC.position[2]<flCutPos)
+				{
+					flSpan=VertA.position[2]-VertC.position[2] ;
+					flCutSpan=flCutPos-VertC.position[2] ;
+					flPercent=1.0f-flCutSpan/flSpan ;
+				}
+				else
+				{
+					flSpan=VertC.position[2]-VertA.position[2] ;
+					flCutSpan=flCutPos-VertA.position[2] ;
+					flPercent=flCutSpan/flSpan ;
+				}
+
+				CreateTweenVert(&VertC, &VertA, flPercent, &VertexCA) ;
+				nCANum=m_nVertexMax ;
+				if(!AddVertex(VertexCA)) 
+					return 0 ;
+
+			}// end if need create VertexCA
+			break ;
+
+
+	}
+
+//	int nInitialTrianglePos=m_nTriangleMax ; // debugging
+
+	// default parameters for all new triangles
+	NewTri.Texture	=	m_pTriangle[ nTriangle ].Texture ;
+	//NewTri.Lightmap	=	m_pTriangle[ nTriangle ].Lightmap ; // bzn doesn't use lightmaps
+	NewTri.Lamp			= m_pTriangle[ nTriangle ].Lamp ; 
+	NewTri.Group = m_pTriangle[ nTriangle ].Group ;
+
+	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+	// now we create new triangles depending on the verts we created.
+	if((nABNum!=-1) && (nCANum!=-1))
+	{
+		// add (AB, B, C)
+		NewTri.VIndex[0]=nABNum ;
+		NewTri.VIndex[1]=m_pTriangle[ nTriangle ].VIndex[1] ;
+		NewTri.VIndex[2]=m_pTriangle[ nTriangle ].VIndex[2] ;
+		if(!AddTriangle(NewTri)) return 0 ;
+
+		// add (C, CA, AB)
+		NewTri.VIndex[0]=m_pTriangle[ nTriangle ].VIndex[2] ;
+		NewTri.VIndex[1]=nCANum ;
+		NewTri.VIndex[2]=nABNum ;
+		if(!AddTriangle(NewTri)) return 0 ;
+
+		// overwrite the original triangle with (A, AB, CA)
+		NewTri.VIndex[0]=m_pTriangle[ nTriangle ].VIndex[0] ;
+		NewTri.VIndex[1]=nABNum ;
+		NewTri.VIndex[2]=nCANum ;
+		m_pTriangle[ nTriangle ]=NewTri ;
+	}
+	else
+	if((nABNum!=-1) && (nBCNum!=-1))
+	{
+		// add (BC, C, A)
+		NewTri.VIndex[0]=nBCNum ;
+		NewTri.VIndex[1]=m_pTriangle[ nTriangle ].VIndex[2] ;
+		NewTri.VIndex[2]=m_pTriangle[ nTriangle ].VIndex[0] ;
+		if(!AddTriangle(NewTri)) return 0 ;
+
+		// add (A, AB, BC)
+		NewTri.VIndex[0]=m_pTriangle[ nTriangle ].VIndex[0] ;
+		NewTri.VIndex[1]=nABNum ;
+		NewTri.VIndex[2]=nBCNum ;
+		if(!AddTriangle(NewTri)) return 0 ;
+
+		// overwrite the original triangle with (B, BC, AB)
+		NewTri.VIndex[0]=m_pTriangle[ nTriangle ].VIndex[1] ;
+		NewTri.VIndex[1]=nBCNum ;
+		NewTri.VIndex[2]=nABNum ;
+		m_pTriangle[ nTriangle ]=NewTri ;
+	}
+	else
+	if((nBCNum!=-1) && (nCANum!=-1))
+	{
+		// add (CA, A, B)
+		NewTri.VIndex[0]=nCANum ;
+		NewTri.VIndex[1]=m_pTriangle[ nTriangle ].VIndex[0] ;
+		NewTri.VIndex[2]=m_pTriangle[ nTriangle ].VIndex[1] ;
+		if(!AddTriangle(NewTri)) return 0 ;
+
+		// add (B, BC, CA)
+		NewTri.VIndex[0]=m_pTriangle[ nTriangle ].VIndex[1] ;
+		NewTri.VIndex[1]=nBCNum ;
+		NewTri.VIndex[2]=nCANum ;
+		if(!AddTriangle(NewTri)) return 0 ;
+
+		// overwrite the original triangle with (C, CA, BC)
+		NewTri.VIndex[0]=m_pTriangle[ nTriangle ].VIndex[2] ;
+		NewTri.VIndex[1]=nCANum ;
+		NewTri.VIndex[2]=nBCNum ;
+		m_pTriangle[ nTriangle ]=NewTri ;
+	}
+	else
+	if(nABNum!=-1)
+	{
+		// add (AB, B, C)
+		NewTri.VIndex[0]=nABNum ;
+		NewTri.VIndex[1]=m_pTriangle[ nTriangle ].VIndex[1] ;
+		NewTri.VIndex[2]=m_pTriangle[ nTriangle ].VIndex[2] ;
+		if(!AddTriangle(NewTri)) return 0 ;
+
+		// overwrite the original triangle with (A, AB, C)
+		NewTri.VIndex[0]=m_pTriangle[ nTriangle ].VIndex[0] ;
+		NewTri.VIndex[1]=nABNum ;
+		NewTri.VIndex[2]=m_pTriangle[ nTriangle ].VIndex[2] ;
+		m_pTriangle[ nTriangle ]=NewTri ;
+	}
+	else
+	if(nBCNum!=-1)
+	{
+		// add (BC, C, A)
+		NewTri.VIndex[0]=nBCNum ;
+		NewTri.VIndex[1]=m_pTriangle[ nTriangle ].VIndex[2] ;
+		NewTri.VIndex[2]=m_pTriangle[ nTriangle ].VIndex[0] ;
+		if(!AddTriangle(NewTri)) return 0 ;
+
+		// overwrite the original triangle with (B, BC, A)
+		NewTri.VIndex[0]=m_pTriangle[ nTriangle ].VIndex[1] ;
+		NewTri.VIndex[1]=nBCNum ;
+		NewTri.VIndex[2]=m_pTriangle[ nTriangle ].VIndex[0] ;
+		m_pTriangle[ nTriangle ]=NewTri ;
+	}
+	else
+	if(nCANum!=-1)
+	{
+		// add (CA, A, B)
+		NewTri.VIndex[0]=nCANum ;
+		NewTri.VIndex[1]=m_pTriangle[ nTriangle ].VIndex[0] ;
+		NewTri.VIndex[2]=m_pTriangle[ nTriangle ].VIndex[1] ;
+		if(!AddTriangle(NewTri)) return 0 ;
+
+		// overwrite the original triangle with (C, CA, B)
+		NewTri.VIndex[0]=m_pTriangle[ nTriangle ].VIndex[2] ;
+		NewTri.VIndex[1]=nCANum ;
+		NewTri.VIndex[2]=m_pTriangle[ nTriangle ].VIndex[1] ;
+		m_pTriangle[ nTriangle ]=NewTri ;
+	}
+
+	return 1 ;
+}
+
+void Q3Map::CreateTweenVert(Q3BspVertex* pVertA, Q3BspVertex* pVertB, float flPercent0, Q3BspVertex* pVertexAB)
+{
+	float flPercent1=1.0f-flPercent0 ;
+
+	pVertexAB->color[0]=(unsigned char)(flPercent0*pVertA->color[0] + flPercent1*pVertB->color[0]) ;
+	pVertexAB->color[1]=(unsigned char)(flPercent0*pVertA->color[1] + flPercent1*pVertB->color[1]) ;
+	pVertexAB->color[2]=(unsigned char)(flPercent0*pVertA->color[2] + flPercent1*pVertB->color[2]) ;
+	pVertexAB->color[3]=(unsigned char)(flPercent0*pVertA->color[3] + flPercent1*pVertB->color[3]) ;
+
+	pVertexAB->position[0]=flPercent0*pVertA->position[0] + flPercent1*pVertB->position[0] ;
+	pVertexAB->position[1]=flPercent0*pVertA->position[1] + flPercent1*pVertB->position[1] ;
+	pVertexAB->position[2]=flPercent0*pVertA->position[2] + flPercent1*pVertB->position[2] ;
+				
+	pVertexAB->texcoord[0][0]=flPercent0*pVertA->texcoord[0][0] + flPercent1*pVertB->texcoord[0][0] ;
+	pVertexAB->texcoord[0][1]=flPercent0*pVertA->texcoord[0][1] + flPercent1*pVertB->texcoord[0][1] ;
+	pVertexAB->texcoord[1][0]=flPercent0*pVertA->texcoord[1][0] + flPercent1*pVertB->texcoord[1][0] ;
+	pVertexAB->texcoord[1][1]=flPercent0*pVertA->texcoord[1][1] + flPercent1*pVertB->texcoord[1][1] ;
+
+	pVertexAB->normal[0]=flPercent0*pVertA->normal[0] + flPercent1*pVertB->normal[0] ;
+	pVertexAB->normal[1]=flPercent0*pVertA->normal[1] + flPercent1*pVertB->normal[1] ;
+	pVertexAB->normal[2]=flPercent0*pVertA->normal[2] + flPercent1*pVertB->normal[2] ;
+	// normalize
+	float flLen=sqrt(pVertexAB->normal[0]*pVertexAB->normal[0] + pVertexAB->normal[1]*pVertexAB->normal[1] + pVertexAB->normal[2]*pVertexAB->normal[2]) ;
+	if(flLen!=0.0f) // don't divide by zero... but normal is messed up.
+	{
+		pVertexAB->normal[0]/=flLen ;
+		pVertexAB->normal[1]/=flLen ;
+		pVertexAB->normal[2]/=flLen ;
+	}
+	else
+	{
+		// default a messed up normal to point upward
+		pVertexAB->normal[0]=0.0f ;
+		pVertexAB->normal[1]=1.0f ;
+		pVertexAB->normal[2]=0.0f ;
+	}
+}
+
+// returns the next subzone a point is in after the start subzone, or -1 if there are no more subzones
+int Q3Map::GetNextSubZone(float *flPoint, int nStart, int nMax)
+{
+	while(++nStart<nMax)
+		if(PointInSubZone(flPoint, nStart))
+			break ;
+
+	if(nStart==nMax)
+		return -1 ;
+	else
+		return nStart ;
+}
+
+// returns true if a point is in a subzone.
+bool Q3Map::PointInSubZone(float *flPoint, int nSubZone)
+{
+	if(
+				(flPoint[0]>=m_pSubZones[nSubZone].Min[0]-SUBZONE_EPSILON) && (flPoint[0]<=m_pSubZones[nSubZone].Max[0]+SUBZONE_EPSILON)
+			&&(flPoint[1]>=m_pSubZones[nSubZone].Min[1]-SUBZONE_EPSILON) && (flPoint[1]<=m_pSubZones[nSubZone].Max[1]+SUBZONE_EPSILON)
+			&&(flPoint[2]>=m_pSubZones[nSubZone].Min[2]-SUBZONE_EPSILON) && (flPoint[2]<=m_pSubZones[nSubZone].Max[2]+SUBZONE_EPSILON)
+		)
+		return true ;
+
+	return false ;
+}
+
+// returns true if a point is in a zone.
+bool Q3Map::PointInZone(float *flPos, int nZone)
+{
+	int nMaxSubZone=m_nZone[nZone][INDEX_SUBZONECOUNT] ;
+
+	for(int nSubZoneIndex=0 ; nSubZoneIndex<nMaxSubZone ; nSubZoneIndex++)
+		if(PointInSubZone(flPos, m_nZone[nZone][nSubZoneIndex]))
+			return true ;
+		
+	return false ;
+}
+
+// returns true if an axis aligned bounding box touches a subzone.
+bool Q3Map::AABBTouchesSubZone(float *flPointMin, float *flPointMax, int nSubZone)
+{
+	// if test AABB overlaps the subzone AABB
+	if(
+				 (m_pSubZones[nSubZone].Min[0]<flPointMax[0]) && (m_pSubZones[nSubZone].Max[0]>flPointMin[0])
+			&& (m_pSubZones[nSubZone].Min[1]<flPointMax[1]) && (m_pSubZones[nSubZone].Max[1]>flPointMin[1])
+			&& (m_pSubZones[nSubZone].Min[2]<flPointMax[2]) && (m_pSubZones[nSubZone].Max[2]>flPointMin[2])
+		)
+		return true ;
+
+	return false ;
+}
+
+// returns true if an axis aligned bounding box touches a zone.
+bool Q3Map::AABBTouchesZone(float *flPosMin, float *flPosMax, int nZone)
+{
+	int nMaxSubZone=m_nZone[nZone][INDEX_SUBZONECOUNT] ;
+
+	for(int nSubZoneIndex=0 ; nSubZoneIndex<nMaxSubZone ; nSubZoneIndex++)
+		if(AABBTouchesSubZone(flPosMin, flPosMax, m_nZone[nZone][nSubZoneIndex]))
+			return true ;
+		
+	return false ;
+}
+
+// take the faces from the BSP and add them to our list of triangles.
+int Q3Map::ConvertFacesToTriangles(void)
+{
+	int nFaceIndex = 0 ;
+	
+//	int nVertex=0 ;
+//	int nVertexMax=0 ;
+	int nTriangle=0 ;
+	int nTriangleMax=0 ;
+
+//	float flPosX=0.0f ;
+//	float flPosY=0.0f ;
+//	float flPosZ=0.0f ;
+//	float	flNormX=0.0f ;
+//	float	flNormY=0.0f ;
+//	float	flNormZ=0.0f ;
+//	float flTexU=0.0f ;
+//	float flTexV=0.0f ;
+	int nMeshVert=0 ;
+//	int nMeshVertA=0 ;
+//	int nMeshVertB=0 ;
+//	int nMeshVertC=0 ;
+
+//	Q3BspVertex *vertices = m_pVertices ;
+	int *meshverts = m_pMeshVerts ;
+	
+	QVECTOR junk ;
+	junk[0]=0.0f ;
+	junk[1]=0.0f ;
+	junk[2]=0.0f ;
+
+
+	nFaceIndex = 0 ;	
+	while(nFaceIndex<m_iNumFaces)
+	{
+
+		switch (m_BspFaces[nFaceIndex].type)
+		{
+			case MESH:    
+			case POLYGON:	
+
+				nTriangleMax=m_BspFaces[nFaceIndex].n_triangles ;
+				nMeshVert=m_BspFaces[nFaceIndex].meshvert ;
+				
+				for(nTriangle=0 ; nTriangle<nTriangleMax ; nTriangle++)
+				{
+
+					// if we are out of memory, grow it.  If we can't grow it, fail
+					if(m_nTriangleMax>=m_nTriangleLimit)
+						if(!ExpandTriangleMemory())
+							return 0 ;
+
+					
+
+					m_pTriangle[ m_nTriangleMax ].Texture=		m_BspFaces[nFaceIndex].texture ;
+					//m_pTriangle[ m_nTriangleMax ].Lightmap=		m_BspFaces[nFaceIndex].lm_index ; // bzn doesn't use lightmaps
+					m_pTriangle[ m_nTriangleMax ].VIndex[0]=	meshverts[ nMeshVert++ ]+m_BspFaces[nFaceIndex].vertex ;
+					m_pTriangle[ m_nTriangleMax ].VIndex[1]=	meshverts[ nMeshVert++ ]+m_BspFaces[nFaceIndex].vertex ;
+					m_pTriangle[ m_nTriangleMax ].VIndex[2]=	meshverts[ nMeshVert++ ]+m_BspFaces[nFaceIndex].vertex ;
+
+					m_pTriangle[ m_nTriangleMax ].Lamp=-1 ; // assume it didn't come from a lamp, this will be updated later
+					
+					m_pTriangle[ m_nTriangleMax ].Group=m_nGroup ; // increment group number.  
+
+					m_nTriangleMax++ ;
+				}// end for nTriangle
+				m_nGroup++ ; // increment group.  Every face is a new group.
+				
+			break ;
+
+		}// end switch
+		
+		nFaceIndex++;
+	} // end while	
+
+	return 1 ;
+
+}
+
+
+// convert the patch info from the BSP into bezier curved triangle meshes and add to our triangle list.
+int Q3Map::ConvertPatchesToTriangles(void)
+{
+
+//	float flPosX=0.0f ;
+//	float flPosY=0.0f ;
+//	float flPosZ=0.0f ;
+//	float	flNormX=0.0f ;
+//	float	flNormY=0.0f ;
+//	float	flNormZ=0.0f ;
+//	float flTexU=0.0f ;
+//	float flTexV=0.0f ;
+//	int nMeshVert=0 ;
+	int nMeshVertA=0 ;
+	int nMeshVertB=0 ;
+	int nMeshVertC=0 ;
+
+
+	int nTriPerRow=0 ;
+	int nRow=0 ;
+
+	int nFirstVertex=m_nVertexMax ;
+	
+	int nVertCount=nFirstVertex ;
+//	int nPatchCount=0 ;
+
+	int* pIndexBuffer=NULL ;
+
+	Q3BspVertex NewVert ;
+		
+
+
+
+	int nCount=0 ;
+	int nCountB=0 ;
+
+
+
+		
+    int indexBufferindex = 0;
+	
+    int vertexBufferindex = 0;
+
+    for (int faceIndex=0; faceIndex < m_iNumFaces; faceIndex++)
+    {		
+
+			nCount++ ;
+			if(nCount==1)
+			{
+				nCountB+=nCount ;
+				nCount=0 ;
+			}
+
+
+      if (m_BspFaces[faceIndex].type == PATCH)
+      {
+        Q3BspPatch *patch = m_BspFaces[faceIndex].patch;
+
+        if (patch != NULL)
+        {
+					
+          for (int bezierIndex=0; bezierIndex < patch->size; bezierIndex++)
+          {
+						indexBufferindex = 0;
+						pIndexBuffer = new int[patch->bezier[bezierIndex].mNumIndex] ;
+						if(pIndexBuffer==NULL) return 0 ; // ran out of memory
+
+
+            for (int index=0; index < patch->bezier[bezierIndex].mNumIndex; index++)
+            {	
+              pIndexBuffer[indexBufferindex] = patch->bezier[bezierIndex].mIndex[index];
+              indexBufferindex++;
+            }
+
+            for (int vertex=0; vertex < patch->bezier[bezierIndex].mNumVertex; vertex++)
+            {
+
+              BspVertex *bspVertex = &patch->bezier[bezierIndex].mVertex[vertex];
+
+							NewVert.position[0]=bspVertex->mPosition[0] ;
+							NewVert.position[1]=bspVertex->mPosition[1] ;
+							NewVert.position[2]=bspVertex->mPosition[2] ;
+							NewVert.normal[0]=bspVertex->mNormal[0] ;
+							NewVert.normal[1]=bspVertex->mNormal[1] ;
+							NewVert.normal[2]=bspVertex->mNormal[2] ;
+							NewVert.texcoord[0][0]=bspVertex->mTexcoord[0][0] ;
+							NewVert.texcoord[0][1]=bspVertex->mTexcoord[0][1] ;
+
+							
+
+							// if we are out of memory, grow it.  If we can't grow it, fail
+							if(m_nVertexMax>=m_nVertexLimit)
+								if(!ExpandVertexMemory())
+								{
+									if(pIndexBuffer) DELETE_ARRAY( pIndexBuffer ) ;
+									return 0 ;
+								}
+
+
+							if(!AddVertex(NewVert)) 
+							{
+								if(pIndexBuffer) DELETE_ARRAY( pIndexBuffer ) ;
+								return 0 ;
+							}
+
+							nVertCount++ ;
+              vertexBufferindex++;
+
+            }// end for vertex
+
+
+						for (int j=0; j < 5; j++)
+						{
+							nRow=m_BspFaces[faceIndex].patch->bezier[bezierIndex].mRowIndex[j] ;
+							nTriPerRow=m_BspFaces[faceIndex].patch->bezier[bezierIndex].mTrianglesPerRow[j] ;
+
+							nMeshVertA=pIndexBuffer[nRow+0]+nFirstVertex ;
+							nMeshVertB=pIndexBuffer[nRow+1]+nFirstVertex ;
+							
+							for(int nVert=2 ; nVert<nTriPerRow ; nVert++)
+							{
+								// if we are out of memory, grow it.  If we can't grow it, fail
+								if(m_nTriangleMax>=m_nTriangleLimit)
+									if(!ExpandTriangleMemory())
+									{
+										if(pIndexBuffer) DELETE_ARRAY( pIndexBuffer ) ;
+										return 0 ;
+									}
+
+								m_pTriangle[ m_nTriangleMax ].Texture=		m_BspFaces[faceIndex].texture ;
+								//m_pTriangle[ m_nTriangleMax ].Lightmap=		m_BspFaces[faceIndex].lm_index ; // bzn doesn't use lightmaps
+
+								
+								
+								nMeshVertC=pIndexBuffer[nRow+nVert]+nFirstVertex ;
+
+
+								if(nVert&1)
+								{
+									m_pTriangle[ m_nTriangleMax ].VIndex[0]=	nMeshVertB ;
+									m_pTriangle[ m_nTriangleMax ].VIndex[1]=	nMeshVertA ;
+									m_pTriangle[ m_nTriangleMax ].VIndex[2]=	nMeshVertC ;
+								}
+								else
+								{
+									m_pTriangle[ m_nTriangleMax ].VIndex[0]=	nMeshVertA ;
+									m_pTriangle[ m_nTriangleMax ].VIndex[1]=	nMeshVertB ;
+									m_pTriangle[ m_nTriangleMax ].VIndex[2]=	nMeshVertC ;
+								}
+
+								m_pTriangle[ m_nTriangleMax ].Lamp=-1 ; // assume it didn't come from a lamp, this will be updated later
+
+
+								m_pTriangle[ m_nTriangleMax ].Group=m_nGroup ;
+
+
+								m_nTriangleMax++ ;
+
+								nMeshVertA=nMeshVertB ;
+								nMeshVertB=nMeshVertC ;
+							}
+				
+
+						}
+						
+
+						// finished with the index buffer
+						if(pIndexBuffer) 
+							DELETE_ARRAY( pIndexBuffer ) ; 
+
+						nFirstVertex=nVertCount ;
+          }// end for bezier index
+
+
+					m_nGroup++ ; // increment the group number.  Each patch is treated as a single group.
+
+        }// end if patch not null
+      }// end if patch
+    }// end for faceIndex
+
+	return 1 ;
+
+}
+
+//''
+// some triangles might be designed to be deferred shading shapes inside of Lamp entities.
+// If so, change their material to the deferred shading configuration for that lamp entity.
+int Q3Map::ConvertTexLampsToLampTriangles(void)
+{
+
+	float flCentreX=0.0f ;
+	float flCentreY=0.0f ;
+	float flCentreZ=0.0f ;
+
+//	float flMinX=0.0f ;
+//	float flMinY=0.0f ;
+//	float flMinZ=0.0f ;
+//	float flMaxX=0.0f ;
+//	float flMaxY=0.0f ;
+//	float flMaxZ=0.0f ;
+
+//	float	flNormX=0.0f ;
+//	float	flNormY=0.0f ;
+//	float	flNormZ=0.0f ;
+//	float flTexU=0.0f ;
+//	float flTexV=0.0f ;
+
+	float flColR=0.0f ;
+	float flColG=0.0f ;
+	float flColB=0.0f ;
+
+	float flBrightness=0.0f ;
+
+	int nLamp=0 ;
+	int nTriangle=0 ;
+	int nTexLampListPos=0 ;
+	int nLightNode=0 ;
+	int nTexture=0 ;
+	int nZone=0 ;
+//	int nLampZone=0 ;
+//	int nMaxLampZone=0 ;
+//	int nZoneMatch=0 ;
+
+
+	for(nTexLampListPos=0 ; nTexLampListPos<m_nTexLampMax ; nTexLampListPos++)
+	{
+		nTriangle=m_pTexLamp[nTexLampListPos] ;
+
+		nZone=m_pTriangle[nTriangle].Zone ;
+
+		nTexture=m_pTriangle[nTriangle].Texture ;
+
+		if(nTexture==m_nBZN_LightNode0)
+			nLightNode=0 ;
+		else
+		if(nTexture==m_nBZN_LightNode1)
+			nLightNode=1 ;
+		else
+		if(nTexture==m_nBZN_LightNode2)
+			nLightNode=2 ;
+		else
+		if(nTexture==m_nBZN_LightNode3)
+			nLightNode=3 ;
+		else
+			return 0 ; // didn't match any lightnode, something went wrong.
+
+
+		// scan through all the lamps, finding the ones that touch the same zone as this triangle and that have the same lightnode number
+		for(nLamp=0 ; nLamp<m_nLampMax ; nLamp++)
+		{
+			// skip if the lightnode doesn't match
+			if(m_pLamp[nLamp].LightNode!=nLightNode)
+				continue ;
+
+
+			/*
+			// lightnode matches, check if lamp touches the same zone (lamps can touch multiple zones so we have to check them all)
+			nZoneMatch=0 ;
+			nMaxLampZone=m_pLamp[nLamp].Zone[MAX_ZONEPERLIGHT] ;
+			for(nLampZone=0 ; nLampZone<nMaxLampZone ; nLampZone++)
+			{
+				if(m_pLamp[nLamp].Zone[nLampZone]==nZone)
+				{
+					nZoneMatch=1 ;
+					break ;
+				}
+			}
+			
+			
+			// if the zone didn't match, continue
+			if(!nZoneMatch) continue ;
+
+			*/
+
+
+
+			// check if all three vertices of this triangle fall within the bounds of this lamp
+			if(
+						// first vert
+						(m_pVertex[  m_pTriangle[nTriangle].VIndex[0]  ].position[0]>=m_pLamp[nLamp].Min[0])
+					&&(m_pVertex[  m_pTriangle[nTriangle].VIndex[0]  ].position[0]<=m_pLamp[nLamp].Max[0])
+					&&(m_pVertex[  m_pTriangle[nTriangle].VIndex[0]  ].position[1]>=m_pLamp[nLamp].Min[1])
+					&&(m_pVertex[  m_pTriangle[nTriangle].VIndex[0]  ].position[1]<=m_pLamp[nLamp].Max[1])
+					&&(m_pVertex[  m_pTriangle[nTriangle].VIndex[0]  ].position[2]>=m_pLamp[nLamp].Min[2])
+					&&(m_pVertex[  m_pTriangle[nTriangle].VIndex[0]  ].position[2]<=m_pLamp[nLamp].Max[2])	
+					&&
+						// second vert
+						(m_pVertex[  m_pTriangle[nTriangle].VIndex[1]  ].position[0]>=m_pLamp[nLamp].Min[0])
+					&&(m_pVertex[  m_pTriangle[nTriangle].VIndex[1]  ].position[0]<=m_pLamp[nLamp].Max[0])
+					&&(m_pVertex[  m_pTriangle[nTriangle].VIndex[1]  ].position[1]>=m_pLamp[nLamp].Min[1])
+					&&(m_pVertex[  m_pTriangle[nTriangle].VIndex[1]  ].position[1]<=m_pLamp[nLamp].Max[1])
+					&&(m_pVertex[  m_pTriangle[nTriangle].VIndex[1]  ].position[2]>=m_pLamp[nLamp].Min[2])
+					&&(m_pVertex[  m_pTriangle[nTriangle].VIndex[1]  ].position[2]<=m_pLamp[nLamp].Max[2])	
+					&&
+						// third vert
+						(m_pVertex[  m_pTriangle[nTriangle].VIndex[2]  ].position[0]>=m_pLamp[nLamp].Min[0])
+					&&(m_pVertex[  m_pTriangle[nTriangle].VIndex[2]  ].position[0]<=m_pLamp[nLamp].Max[0])
+					&&(m_pVertex[  m_pTriangle[nTriangle].VIndex[2]  ].position[1]>=m_pLamp[nLamp].Min[1])
+					&&(m_pVertex[  m_pTriangle[nTriangle].VIndex[2]  ].position[1]<=m_pLamp[nLamp].Max[1])
+					&&(m_pVertex[  m_pTriangle[nTriangle].VIndex[2]  ].position[2]>=m_pLamp[nLamp].Min[2])
+					&&(m_pVertex[  m_pTriangle[nTriangle].VIndex[2]  ].position[2]<=m_pLamp[nLamp].Max[2])	
+	
+					)
+			{
+
+
+				m_pTriangle[nTriangle].Texture=m_pLamp[nLamp].Texture ;
+				m_pTriangle[nTriangle].Lamp=nLamp ;
+
+				flCentreX =		m_pLamp[nLamp].Position[0] ;
+				flCentreY =		m_pLamp[nLamp].Position[1] ;
+				flCentreZ =		m_pLamp[nLamp].Position[2] ;
+				flColR =			m_pLamp[nLamp].Colour[0]*255.0f ;
+				flColG =			m_pLamp[nLamp].Colour[1]*255.0f ;
+				flColB =			m_pLamp[nLamp].Colour[2]*255.0f ;
+				flBrightness=	m_pLamp[nLamp].Brightness ; 
+
+				m_pVertex[  m_pTriangle[nTriangle].VIndex[0]  ].texcoord[0][0]=flCentreX ;
+				m_pVertex[  m_pTriangle[nTriangle].VIndex[0]  ].texcoord[0][1]=flCentreY ;
+				m_pVertex[  m_pTriangle[nTriangle].VIndex[0]  ].texcoord[1][0]=flCentreZ ;
+				m_pVertex[  m_pTriangle[nTriangle].VIndex[0]  ].texcoord[1][1]=flBrightness ;
+				m_pVertex[  m_pTriangle[nTriangle].VIndex[0]  ].color[0]=(unsigned char)flColR ;
+				m_pVertex[  m_pTriangle[nTriangle].VIndex[0]  ].color[1]=(unsigned char)flColG ;
+				m_pVertex[  m_pTriangle[nTriangle].VIndex[0]  ].color[2]=(unsigned char)flColB ;
+
+				m_pVertex[  m_pTriangle[nTriangle].VIndex[1]  ].texcoord[0][0]=flCentreX ;
+				m_pVertex[  m_pTriangle[nTriangle].VIndex[1]  ].texcoord[0][1]=flCentreY ;
+				m_pVertex[  m_pTriangle[nTriangle].VIndex[1]  ].texcoord[1][0]=flCentreZ ;
+				m_pVertex[  m_pTriangle[nTriangle].VIndex[1]  ].texcoord[1][1]=flBrightness ;
+				m_pVertex[  m_pTriangle[nTriangle].VIndex[1]  ].color[0]=(unsigned char)flColR ;
+				m_pVertex[  m_pTriangle[nTriangle].VIndex[1]  ].color[1]=(unsigned char)flColG ;
+				m_pVertex[  m_pTriangle[nTriangle].VIndex[1]  ].color[2]=(unsigned char)flColB ;
+
+				m_pVertex[  m_pTriangle[nTriangle].VIndex[2]  ].texcoord[0][0]=flCentreX ;
+				m_pVertex[  m_pTriangle[nTriangle].VIndex[2]  ].texcoord[0][1]=flCentreY ;
+				m_pVertex[  m_pTriangle[nTriangle].VIndex[2]  ].texcoord[1][0]=flCentreZ ;
+				m_pVertex[  m_pTriangle[nTriangle].VIndex[2]  ].texcoord[1][1]=flBrightness ;
+				m_pVertex[  m_pTriangle[nTriangle].VIndex[2]  ].color[0]=(unsigned char)flColR ;
+				m_pVertex[  m_pTriangle[nTriangle].VIndex[2]  ].color[1]=(unsigned char)flColG ;
+				m_pVertex[  m_pTriangle[nTriangle].VIndex[2]  ].color[2]=(unsigned char)flColB ;
+
+				
+				m_pTriangle[ m_nTriangleMax ].Group=m_nGroup ;
+
+
+			}// end triangle is in bounds of lamp
+
+
+			m_nGroup++ ; // increment group number.  Every texture lamp is a group.
+
+
+		}// end for nLamp
+
+
+	}
+
+
+
+	return 1 ;
+}
+
+
+// convert lamps into boxes.  Lamps are deferred shading non-shadowing lights, and are rendered as triangles
+// The texture coords are actually the light centre point, the spot the shaders calculate as the source of the lighting, 
+// and also the brightness
+// The triangles created will remember the lamp they came from, via Triangle.Lamp
+
+int Q3Map::ConvertLampsToTriangles(void)
+{
+	float flCentreX=0.0f ;
+	float flCentreY=0.0f ;
+	float flCentreZ=0.0f ;
+
+	float flMinX=0.0f ;
+	float flMinY=0.0f ;
+	float flMinZ=0.0f ;
+	float flMaxX=0.0f ;
+	float flMaxY=0.0f ;
+	float flMaxZ=0.0f ;
+
+//	float	flNormX=0.0f ;
+//	float	flNormY=0.0f ;
+//	float	flNormZ=0.0f ;
+//	float flTexU=0.0f ;
+//	float flTexV=0.0f ;
+
+	float flColR=0.0f ;
+	float flColG=0.0f ;
+	float flColB=0.0f ;
+
+	float flBrightness=0.0f ;
+
+	int nLamp=0 ;
+
+	// lower case = min, upper case = max
+	Q3BspVertex Vert_xyz ;
+	Q3BspVertex Vert_Xyz ;
+	Q3BspVertex Vert_xYz ;
+	Q3BspVertex Vert_XYz ;
+	Q3BspVertex Vert_xyZ ;
+	Q3BspVertex Vert_XyZ ;
+	Q3BspVertex Vert_xYZ ;
+	Q3BspVertex Vert_XYZ ;
+
+	int n_xyz=0 ;
+	int n_Xyz=0 ;
+	int n_xYz=0 ;
+	int n_XYz=0 ;
+	int n_xyZ=0 ;
+	int n_XyZ=0 ;
+	int n_xYZ=0 ;
+	int n_XYZ=0 ;
+
+	int nFirstVertex=0 ;
+
+	triangle_t Triangle ;
+	ZeroMemory((void*)&Triangle, sizeof(triangle_t)) ;
+	
+	for(nLamp=0 ; nLamp<m_nLampMax ; nLamp++)
+	{
+		if(m_pLamp[nLamp].LightNode>-1) continue ; // lamps that are lightnodes don't add their own triangles.  They just exist as information for TexLamps.
+
+
+		flCentreX =		m_pLamp[nLamp].Position[0] ;
+		flCentreY =		m_pLamp[nLamp].Position[1] ;
+		flCentreZ =		m_pLamp[nLamp].Position[2] ;
+
+		flMinX =			m_pLamp[nLamp].Min[0] ;
+		flMinY =			m_pLamp[nLamp].Min[1] ;
+		flMinZ =			m_pLamp[nLamp].Min[2] ;
+
+		flMaxX =			m_pLamp[nLamp].Max[0] ;
+		flMaxY =			m_pLamp[nLamp].Max[1] ;
+		flMaxZ =			m_pLamp[nLamp].Max[2] ;
+
+		flColR =			m_pLamp[nLamp].Colour[0]*255.0f ;
+		flColG =			m_pLamp[nLamp].Colour[1]*255.0f ;
+		flColB =			m_pLamp[nLamp].Colour[2]*255.0f ;
+		
+		flBrightness=	m_pLamp[nLamp].Brightness ; 
+		
+		//////////////////////////////////////
+		// setup our 8 vertices.  Normal isn't that important, I just approximate regardless of actual box shape
+
+
+		nFirstVertex=m_nVertexMax ; // we need to remember which vertex is which for defining the triangles
+
+		// vertex numbers
+		n_xyz=nFirstVertex+0 ;
+		n_Xyz=nFirstVertex+1 ;
+		n_xYz=nFirstVertex+2 ;
+		n_XYz=nFirstVertex+3 ;
+		n_xyZ=nFirstVertex+4 ;
+		n_XyZ=nFirstVertex+5 ;
+		n_xYZ=nFirstVertex+6 ;
+		n_XYZ=nFirstVertex+7 ;
+
+
+		Vert_xyz.position[0]=flMinX ;
+		Vert_xyz.position[1]=flMinY ;
+		Vert_xyz.position[2]=flMinZ ;
+		Vert_xyz.normal[0]=-0.5773502691896 ;
+		Vert_xyz.normal[1]=-0.5773502691896 ;
+		Vert_xyz.normal[2]=-0.5773502691896 ;
+		Vert_xyz.texcoord[0][0]=flCentreX ;
+		Vert_xyz.texcoord[0][1]=flCentreY ;
+		Vert_xyz.texcoord[1][0]=flCentreZ ;
+		Vert_xyz.texcoord[1][1]=flBrightness ;
+		Vert_xyz.color[0]=(unsigned char)flColR ;
+		Vert_xyz.color[1]=(unsigned char)flColG ;
+		Vert_xyz.color[2]=(unsigned char)flColB ;
+		if(!AddVertex(Vert_xyz)) return 0 ;
+			
+
+		Vert_Xyz.position[0]=flMaxX ;
+		Vert_Xyz.position[1]=flMinY ;
+		Vert_Xyz.position[2]=flMinZ ;
+		Vert_Xyz.normal[0]= 0.5773502691896 ;
+		Vert_Xyz.normal[1]=-0.5773502691896 ;
+		Vert_Xyz.normal[2]=-0.5773502691896 ;
+		Vert_Xyz.texcoord[0][0]=flCentreX ;
+		Vert_Xyz.texcoord[0][1]=flCentreY ;
+		Vert_Xyz.texcoord[1][0]=flCentreZ ;
+		Vert_Xyz.texcoord[1][1]=flBrightness ;
+		Vert_Xyz.color[0]=(unsigned char)flColR ;
+		Vert_Xyz.color[1]=(unsigned char)flColG ;
+		Vert_Xyz.color[2]=(unsigned char)flColB ;
+		if(!AddVertex(Vert_Xyz)) return 0 ;
+
+		Vert_xYz.position[0]=flMinX ;
+		Vert_xYz.position[1]=flMaxY ;
+		Vert_xYz.position[2]=flMinZ ;
+		Vert_xYz.normal[0]=-0.5773502691896 ;
+		Vert_xYz.normal[1]= 0.5773502691896 ;
+		Vert_xYz.normal[2]=-0.5773502691896 ;
+		Vert_xYz.texcoord[0][0]=flCentreX ;
+		Vert_xYz.texcoord[0][1]=flCentreY ;
+		Vert_xYz.texcoord[1][0]=flCentreZ ;
+		Vert_xYz.texcoord[1][1]=flBrightness ;
+		Vert_xYz.color[0]=(unsigned char)flColR ;
+		Vert_xYz.color[1]=(unsigned char)flColG ;
+		Vert_xYz.color[2]=(unsigned char)flColB ;
+		if(!AddVertex(Vert_xYz)) return 0 ;
+
+		Vert_XYz.position[0]=flMaxX ;
+		Vert_XYz.position[1]=flMaxY ;
+		Vert_XYz.position[2]=flMinZ ;
+		Vert_XYz.normal[0]= 0.5773502691896 ;
+		Vert_XYz.normal[1]= 0.5773502691896 ;
+		Vert_XYz.normal[2]=-0.5773502691896 ;
+		Vert_XYz.texcoord[0][0]=flCentreX ;
+		Vert_XYz.texcoord[0][1]=flCentreY ;
+		Vert_XYz.texcoord[1][0]=flCentreZ ;
+		Vert_XYz.texcoord[1][1]=flBrightness ;
+		Vert_XYz.color[0]=(unsigned char)flColR ;
+		Vert_XYz.color[1]=(unsigned char)flColG ;
+		Vert_XYz.color[2]=(unsigned char)flColB ;
+		if(!AddVertex(Vert_XYz)) return 0 ;
+
+		//////////////////////////////////////
+
+		Vert_xyZ.position[0]=flMinX ;
+		Vert_xyZ.position[1]=flMinY ;
+		Vert_xyZ.position[2]=flMaxZ ;
+		Vert_xyZ.normal[0]=-0.5773502691896 ;
+		Vert_xyZ.normal[1]=-0.5773502691896 ;
+		Vert_xyZ.normal[2]= 0.5773502691896 ;
+		Vert_xyZ.texcoord[0][0]=flCentreX ;
+		Vert_xyZ.texcoord[0][1]=flCentreY ;
+		Vert_xyZ.texcoord[1][0]=flCentreZ ;
+		Vert_xyZ.texcoord[1][1]=flBrightness ;
+		Vert_xyZ.color[0]=(unsigned char)flColR ;
+		Vert_xyZ.color[1]=(unsigned char)flColG ;
+		Vert_xyZ.color[2]=(unsigned char)flColB ;
+		if(!AddVertex(Vert_xyZ)) return 0 ;
+
+		Vert_XyZ.position[0]=flMaxX ;
+		Vert_XyZ.position[1]=flMinY ;
+		Vert_XyZ.position[2]=flMaxZ ;
+		Vert_XyZ.normal[0]= 0.5773502691896 ;
+		Vert_XyZ.normal[1]=-0.5773502691896 ;
+		Vert_XyZ.normal[2]= 0.5773502691896 ;
+		Vert_XyZ.texcoord[0][0]=flCentreX ;
+		Vert_XyZ.texcoord[0][1]=flCentreY ;
+		Vert_XyZ.texcoord[1][0]=flCentreZ ;
+		Vert_XyZ.texcoord[1][1]=flBrightness ;
+		Vert_XyZ.color[0]=(unsigned char)flColR ;
+		Vert_XyZ.color[1]=(unsigned char)flColG ;
+		Vert_XyZ.color[2]=(unsigned char)flColB ;
+		if(!AddVertex(Vert_XyZ)) return 0 ;
+
+		Vert_xYZ.position[0]=flMinX ;
+		Vert_xYZ.position[1]=flMaxY ;
+		Vert_xYZ.position[2]=flMaxZ ;
+		Vert_xYZ.normal[0]=-0.5773502691896 ;
+		Vert_xYZ.normal[1]= 0.5773502691896 ;
+		Vert_xYZ.normal[2]= 0.5773502691896 ;
+		Vert_xYZ.texcoord[0][0]=flCentreX ;
+		Vert_xYZ.texcoord[0][1]=flCentreY ;
+		Vert_xYZ.texcoord[1][0]=flCentreZ ;
+		Vert_xYZ.texcoord[1][1]=flBrightness ;
+		Vert_xYZ.color[0]=(unsigned char)flColR ;
+		Vert_xYZ.color[1]=(unsigned char)flColG ;
+		Vert_xYZ.color[2]=(unsigned char)flColB ;
+		if(!AddVertex(Vert_xYZ)) return 0 ;
+
+		Vert_XYZ.position[0]=flMaxX ;
+		Vert_XYZ.position[1]=flMaxY ;
+		Vert_XYZ.position[2]=flMaxZ ;
+		Vert_XYZ.normal[0]= 0.5773502691896 ;
+		Vert_XYZ.normal[1]= 0.5773502691896 ;
+		Vert_XYZ.normal[2]= 0.5773502691896 ;
+		Vert_XYZ.texcoord[0][0]=flCentreX ;
+		Vert_XYZ.texcoord[0][1]=flCentreY ;
+		Vert_XYZ.texcoord[1][0]=flCentreZ ;
+		Vert_XYZ.texcoord[1][1]=flBrightness ;
+		Vert_XYZ.color[0]=(unsigned char)flColR ;
+		Vert_XYZ.color[1]=(unsigned char)flColG ;
+		Vert_XYZ.color[2]=(unsigned char)flColB ;
+		if(!AddVertex(Vert_XYZ)) return 0 ;
+
+		////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+		Triangle.Texture=m_pLamp[nLamp].Texture ;
+		Triangle.Lamp=nLamp ;
+		Triangle.Group=m_nGroup ;
+
+		/////////////////////////////////////
+
+		Triangle.VIndex[2]=n_xyz ;
+		Triangle.VIndex[1]=n_xyZ ;
+		Triangle.VIndex[0]=n_xYZ ;
+		if(!AddTriangle(Triangle)) return 0 ;
+
+		Triangle.VIndex[2]=n_xYZ ;
+		Triangle.VIndex[1]=n_xYz ;
+		Triangle.VIndex[0]=n_xyz ;
+		if(!AddTriangle(Triangle)) return 0 ;
+	
+		/////////////////////////////////////
+
+		Triangle.VIndex[0]=n_Xyz ;
+		Triangle.VIndex[1]=n_XyZ ;
+		Triangle.VIndex[2]=n_XYZ ;
+		if(!AddTriangle(Triangle)) return 0 ;
+
+		Triangle.VIndex[0]=n_XYZ ;
+		Triangle.VIndex[1]=n_XYz ;
+		Triangle.VIndex[2]=n_Xyz ;
+		if(!AddTriangle(Triangle)) return 0 ;
+
+		/////////////////////////////////////
+
+		Triangle.VIndex[2]=n_xyz ;
+		Triangle.VIndex[1]=n_xYz ;
+		Triangle.VIndex[0]=n_XYz ;
+		if(!AddTriangle(Triangle)) return 0 ;
+
+		Triangle.VIndex[2]=n_XYz ;
+		Triangle.VIndex[1]=n_Xyz ;
+		Triangle.VIndex[0]=n_xyz ;
+		if(!AddTriangle(Triangle)) return 0 ;
+	
+		/////////////////////////////////////
+
+		Triangle.VIndex[0]=n_xyZ ;
+		Triangle.VIndex[1]=n_xYZ ;
+		Triangle.VIndex[2]=n_XYZ ;
+		if(!AddTriangle(Triangle)) return 0 ;
+
+		Triangle.VIndex[0]=n_XYZ ;
+		Triangle.VIndex[1]=n_XyZ ;
+		Triangle.VIndex[2]=n_xyZ ;
+		if(!AddTriangle(Triangle)) return 0 ;
+	
+		/////////////////////////////////////
+
+		Triangle.VIndex[0]=n_xyz ;
+		Triangle.VIndex[1]=n_xyZ ;
+		Triangle.VIndex[2]=n_XyZ ;
+		if(!AddTriangle(Triangle)) return 0 ;
+
+		Triangle.VIndex[0]=n_XyZ ;
+		Triangle.VIndex[1]=n_Xyz ;
+		Triangle.VIndex[2]=n_xyz ;
+		if(!AddTriangle(Triangle)) return 0 ;
+
+		/////////////////////////////////////
+
+		Triangle.VIndex[2]=n_xYz ;
+		Triangle.VIndex[1]=n_xYZ ;
+		Triangle.VIndex[0]=n_XYZ ;
+		if(!AddTriangle(Triangle)) return 0 ;
+
+		Triangle.VIndex[2]=n_XYZ ;
+		Triangle.VIndex[1]=n_XYz ;
+		Triangle.VIndex[0]=n_xYz ;
+		if(!AddTriangle(Triangle)) return 0 ;
+	
+		m_nGroup++ ; // increment group once for every lamp
+	}
+	
+
+
+	return 1 ;
+}
+
+
+int Q3Map::ConvertLampsToGlowTriangles(void)
+{
+	float flCentreX=0.0f ;
+	float flCentreY=0.0f ;
+	float flCentreZ=0.0f ;
+
+	float flMinX=0.0f ;
+	float flMinY=0.0f ;
+	float flMinZ=0.0f ;
+	float flMaxX=0.0f ;
+	float flMaxY=0.0f ;
+	float flMaxZ=0.0f ;
+
+//	float	flNormX=0.0f ;
+//	float	flNormY=0.0f ;
+//	float	flNormZ=0.0f ;
+//	float flTexU=0.0f ;
+//	float flTexV=0.0f ;
+
+	float flColR=0.0f ;
+	float flColG=0.0f ;
+	float flColB=0.0f ;
+
+	float flBrightness=0.0f ;
+
+	int nLamp=0 ;
+
+
+	Q3BspVertex Vert_L ;
+	Q3BspVertex Vert_R ;
+	Q3BspVertex Vert_F ;
+	Q3BspVertex Vert_B ;
+	Q3BspVertex Vert_U ;
+	Q3BspVertex Vert_D ;
+
+
+	int n_L=0 ;
+	int n_R=0 ;
+	int n_F=0 ;
+	int n_B=0 ;
+	int n_U=0 ;
+	int n_D=0 ;
+	
+	int nFirstVertex=0 ;
+
+	float flBaseGlowSize=0.15f ;//0.2f;//0.001f ;
+	float flGlowSize=0.0f ;
+
+
+
+	triangle_t Triangle ;
+	ZeroMemory((void*)&Triangle, sizeof(triangle_t)) ;
+	
+	for(nLamp=0 ; nLamp<m_nLampMax ; nLamp++)
+	{
+
+		flGlowSize=flBaseGlowSize*m_pLamp[nLamp].Brightness ;
+
+		flCentreX =		m_pLamp[nLamp].Position[0] ;
+		flCentreY =		m_pLamp[nLamp].Position[1] ;
+		flCentreZ =		m_pLamp[nLamp].Position[2] ;
+
+		flMinX =			flCentreX-flGlowSize ;
+		flMinY =			flCentreY-flGlowSize ;
+		flMinZ =			flCentreZ-flGlowSize ;
+
+		flMaxX =			flCentreX+flGlowSize ;
+		flMaxY =			flCentreY+flGlowSize ;
+		flMaxZ =			flCentreZ+flGlowSize ;
+
+		flColR =			m_pLamp[nLamp].Colour[0]*255.0f ;
+		flColG =			m_pLamp[nLamp].Colour[1]*255.0f ;
+		flColB =			m_pLamp[nLamp].Colour[2]*255.0f ;
+		
+		flBrightness=	m_pLamp[nLamp].Brightness*4.0f ; 
+		
+		//////////////////////////////////////
+		// setup our 8 vertices.  Normal isn't that important, I just approximate regardless of actual box shape
+
+
+		nFirstVertex=m_nVertexMax ; // we need to remember which vertex is which for defining the triangles
+
+		// vertex numbers
+		n_L=nFirstVertex+0 ;
+		n_R=nFirstVertex+1 ;
+		n_F=nFirstVertex+2 ;
+		n_B=nFirstVertex+3 ;
+		n_U=nFirstVertex+4 ;
+		n_D=nFirstVertex+5 ;
+
+
+
+		Vert_L.position[0]=flMinX ;
+		Vert_L.position[1]=flCentreY ;
+		Vert_L.position[2]=flCentreZ ;
+		Vert_L.normal[0]=-1.0 ;
+		Vert_L.normal[1]=0.0 ;
+		Vert_L.normal[2]=0.0 ;
+		Vert_L.texcoord[0][0]=flCentreX ;
+		Vert_L.texcoord[0][1]=flCentreY ;
+		Vert_L.texcoord[1][0]=flCentreZ ;
+		Vert_L.texcoord[1][1]=flBrightness ;
+		Vert_L.color[0]=(unsigned char)flColR ;
+		Vert_L.color[1]=(unsigned char)flColG ;
+		Vert_L.color[2]=(unsigned char)flColB ;
+		if(!AddVertex(Vert_L)) return 0 ;
+
+		Vert_R.position[0]=flMaxX ;
+		Vert_R.position[1]=flCentreY ;
+		Vert_R.position[2]=flCentreZ ;
+		Vert_R.normal[0]=1.0 ;
+		Vert_R.normal[1]=0.0 ;
+		Vert_R.normal[2]=0.0 ;
+		Vert_R.texcoord[0][0]=flCentreX ;
+		Vert_R.texcoord[0][1]=flCentreY ;
+		Vert_R.texcoord[1][0]=flCentreZ ;
+		Vert_R.texcoord[1][1]=flBrightness ;
+		Vert_R.color[0]=(unsigned char)flColR ;
+		Vert_R.color[1]=(unsigned char)flColG ;
+		Vert_R.color[2]=(unsigned char)flColB ;
+		if(!AddVertex(Vert_R)) return 0 ;
+
+		Vert_F.position[0]=flCentreX ;
+		Vert_F.position[1]=flCentreY ;
+		Vert_F.position[2]=flMinZ ;
+		Vert_F.normal[0]=0.0 ;
+		Vert_F.normal[1]=0.0 ;
+		Vert_F.normal[2]=-1.0 ;
+		Vert_F.texcoord[0][0]=flCentreX ;
+		Vert_F.texcoord[0][1]=flCentreY ;
+		Vert_F.texcoord[1][0]=flCentreZ ;
+		Vert_F.texcoord[1][1]=flBrightness ;
+		Vert_F.color[0]=(unsigned char)flColR ;
+		Vert_F.color[1]=(unsigned char)flColG ;
+		Vert_F.color[2]=(unsigned char)flColB ;
+		if(!AddVertex(Vert_F)) return 0 ;
+		
+		Vert_B.position[0]=flCentreX ;
+		Vert_B.position[1]=flCentreY ;
+		Vert_B.position[2]=flMaxZ ;
+		Vert_B.normal[0]=0.0 ;
+		Vert_B.normal[1]=0.0 ;
+		Vert_B.normal[2]=1.0 ;
+		Vert_B.texcoord[0][0]=flCentreX ;
+		Vert_B.texcoord[0][1]=flCentreY ;
+		Vert_B.texcoord[1][0]=flCentreZ ;
+		Vert_B.texcoord[1][1]=flBrightness ;
+		Vert_B.color[0]=(unsigned char)flColR ;
+		Vert_B.color[1]=(unsigned char)flColG ;
+		Vert_B.color[2]=(unsigned char)flColB ;
+		if(!AddVertex(Vert_B)) return 0 ;
+
+		Vert_U.position[0]=flCentreX ;
+		Vert_U.position[1]=flMaxY ;
+		Vert_U.position[2]=flCentreZ ;
+		Vert_U.normal[0]=0.0 ;
+		Vert_U.normal[1]=1.0 ;
+		Vert_U.normal[2]=0.0 ;
+		Vert_U.texcoord[0][0]=flCentreX ;
+		Vert_U.texcoord[0][1]=flCentreY ;
+		Vert_U.texcoord[1][0]=flCentreZ ;
+		Vert_U.texcoord[1][1]=flBrightness ;
+		Vert_U.color[0]=(unsigned char)flColR ;
+		Vert_U.color[1]=(unsigned char)flColG ;
+		Vert_U.color[2]=(unsigned char)flColB ;
+		if(!AddVertex(Vert_U)) return 0 ;
+
+		Vert_D.position[0]=flCentreX ;
+		Vert_D.position[1]=flMinY ;
+		Vert_D.position[2]=flCentreZ ;
+		Vert_D.normal[0]=0.0 ;
+		Vert_D.normal[1]=-1.0 ;
+		Vert_D.normal[2]=0.0 ;
+		Vert_D.texcoord[0][0]=flCentreX ;
+		Vert_D.texcoord[0][1]=flCentreY ;
+		Vert_D.texcoord[1][0]=flCentreZ ;
+		Vert_D.texcoord[1][1]=flBrightness ;
+		Vert_D.color[0]=(unsigned char)flColR ;
+		Vert_D.color[1]=(unsigned char)flColG ;
+		Vert_D.color[2]=(unsigned char)flColB ;
+		if(!AddVertex(Vert_D)) return 0 ;
+
+		////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+		Triangle.Texture=m_nDefaultTextureIndexGlowLamp ;
+		Triangle.Lamp=-1 ;
+		Triangle.Group=m_nGroup ;
+
+		/////////////////////////////////////
+
+		Triangle.VIndex[0]=n_U ;
+		Triangle.VIndex[1]=n_L ;
+		Triangle.VIndex[2]=n_F ;
+		if(!AddTriangle(Triangle)) return 0 ;
+
+		Triangle.VIndex[0]=n_U ;
+		Triangle.VIndex[1]=n_F ;
+		Triangle.VIndex[2]=n_R ;
+		if(!AddTriangle(Triangle)) return 0 ;
+	
+		Triangle.VIndex[0]=n_U ;
+		Triangle.VIndex[1]=n_R ;
+		Triangle.VIndex[2]=n_B ;
+		if(!AddTriangle(Triangle)) return 0 ;
+
+		Triangle.VIndex[0]=n_U ;
+		Triangle.VIndex[1]=n_B ;
+		Triangle.VIndex[2]=n_L ;
+		if(!AddTriangle(Triangle)) return 0 ;
+
+		//////////////////////////////////////
+
+		Triangle.VIndex[2]=n_D ;
+		Triangle.VIndex[1]=n_L ;
+		Triangle.VIndex[0]=n_F ;
+		if(!AddTriangle(Triangle)) return 0 ;
+
+		Triangle.VIndex[2]=n_D ;
+		Triangle.VIndex[1]=n_F ;
+		Triangle.VIndex[0]=n_R ;
+		if(!AddTriangle(Triangle)) return 0 ;
+	
+		Triangle.VIndex[2]=n_D ;
+		Triangle.VIndex[1]=n_R ;
+		Triangle.VIndex[0]=n_B ;
+		if(!AddTriangle(Triangle)) return 0 ;
+
+		Triangle.VIndex[2]=n_D ;
+		Triangle.VIndex[1]=n_B ;
+		Triangle.VIndex[0]=n_L ;
+		if(!AddTriangle(Triangle)) return 0 ;
+
+		m_nGroup++ ; // increment group once for each glow
+		
+	}
+	
+
+
+	return 1 ;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+int Q3Map::ConvertLightsToGlowTriangles(void)
+{
+	float flCentreX=0.0f ;
+	float flCentreY=0.0f ;
+	float flCentreZ=0.0f ;
+
+	float flMinX=0.0f ;
+	float flMinY=0.0f ;
+	float flMinZ=0.0f ;
+	float flMaxX=0.0f ;
+	float flMaxY=0.0f ;
+	float flMaxZ=0.0f ;
+
+//	float	flNormX=0.0f ;
+//	float	flNormY=0.0f ;
+//	float	flNormZ=0.0f ;
+//	float flTexU=0.0f ;
+//	float flTexV=0.0f ;
+
+	float flColR=0.0f ;
+	float flColG=0.0f ;
+	float flColB=0.0f ;
+
+	float flBrightness=0.0f ;
+
+	int nLight=0 ;
+
+	Q3BspVertex Vert_Or ;
+	Q3BspVertex Vert_A0 ;
+	Q3BspVertex Vert_A1 ;
+	Q3BspVertex Vert_B0 ;
+	Q3BspVertex Vert_B1 ;
+
+
+	int n_Or=0 ;
+	int n_A0=0 ;
+	int n_A1=0 ;
+	int n_B0=0 ;
+	int n_B1=0 ;
+	
+	int nFirstVertex=0 ;
+
+	float flBaseGlowSize=0.2f ;//0.001f ;
+	float flGlowSize=0.0f ;
+
+//	char chMessage[1024] ;
+
+
+
+	triangle_t Triangle ;
+	ZeroMemory((void*)&Triangle, sizeof(triangle_t)) ;
+	
+	for(nLight=0 ; nLight<m_nLightMax ; nLight++)
+	//for(nLight=0 ; nLight<1 ; nLight++)
+	{
+		//if(m_pLight[nLight].LightNode>-1) continue ; // Lights that are lightnodes don't add their own triangles.  They just exist as information for TexLights.
+
+		flGlowSize=flBaseGlowSize*m_pLight[nLight].Brightness ;
+
+		flCentreX =		m_pLight[nLight].Position[0] ;
+		flCentreY =		m_pLight[nLight].Position[1] ;
+		flCentreZ =		m_pLight[nLight].Position[2] ;
+
+		flMinX =			flCentreX-flGlowSize ;
+		flMinY =			flCentreY-flGlowSize ;
+		flMinZ =			flCentreZ-flGlowSize ;
+
+		flMaxX =			flCentreX+flGlowSize ;
+		flMaxY =			flCentreY+flGlowSize ;
+		flMaxZ =			flCentreZ+flGlowSize ;
+
+		flColR =			m_pLight[nLight].Colour[0]*255.0f ;
+		flColG =			m_pLight[nLight].Colour[1]*255.0f ;
+		flColB =			m_pLight[nLight].Colour[2]*255.0f ;
+		
+		flBrightness=	m_pLight[nLight].Brightness * 0.75 ;//*2.0 ; 
+		
+		//////////////////////////////////////
+		// setup our 5 vertices.
+
+
+		nFirstVertex=m_nVertexMax ; // we need to remember which vertex is which for defining the triangles
+
+		// vertex numbers
+		n_Or=nFirstVertex+0 ;
+		n_A0=nFirstVertex+1 ;
+		n_A1=nFirstVertex+2 ;
+		n_B0=nFirstVertex+3 ;
+		n_B1=nFirstVertex+4 ;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+		
+	
+	float flHALFPI =	1.5707963 ;
+	float flTWOPI =		6.2831853 ;
+	float flPI =			3.1415926 ;
+	float flAngle=(m_pLight[nLight].Angle/360.0f*flTWOPI)/2.0 ;
+	float flCutoff=32.0f ;//m_pLight[nLight].Brightness ;
+	
+	Q3BspVertex color ;
+
+	Q3BspVertex NormalVert ;
+	SetVertex(&NormalVert, m_pLight[nLight].Direction[0], m_pLight[nLight].Direction[1], m_pLight[nLight].Direction[2]) ;
+	NormalVert=GetNormalised(&NormalVert) ;
+
+	Q3BspVertex normal ;
+	SetVertex(&normal, m_pLight[nLight].Direction[0]*flCutoff, m_pLight[nLight].Direction[1]*flCutoff, m_pLight[nLight].Direction[2]*flCutoff) ;
+
+	Q3BspVertex adjust=normal ;
+	VertexScale(&adjust, 0.5) ;
+
+	Q3BspVertex start ;		SetVertex(&start, flCentreX, flCentreY, flCentreZ) ;
+	Q3BspVertex end ;			SetVertex(&end, flCentreX+normal.position[0], flCentreY+normal.position[1], flCentreZ+normal.position[2]) ;
+
+	Q3BspVertex xaxis ; SetVertex(&xaxis, 1,0,0) ;
+	Q3BspVertex xaxisneg ; SetVertex(&xaxisneg, -1,0,0) ;
+	Q3BspVertex yaxis ; SetVertex(&yaxis, 0,1,0) ;
+	Q3BspVertex Origin ; SetVertex(&Origin, 0,0,0) ;
+	Q3BspVertex tangentA ;
+	Q3BspVertex tangentB ;
+	Q3BspVertex tangentStart ;
+	Q3BspVertex tangentEnd ;
+	Q3BspVertex tangentEndA0 ;
+	Q3BspVertex tangentEndA1 ;
+	Q3BspVertex tangentEndB0 ;
+	Q3BspVertex tangentEndB1 ;
+
+	Q3BspVertex tangentANorm ;
+	Q3BspVertex tangentBNorm ;
+
+	Q3BspVertex raytangent ;
+
+	Q3BspVertex ray ;
+	float theta=0.0f ;
+	float adjacent=0.0f ;
+	SetVertex(&color, 0.66, 0.66, 0.66) ;
+
+	if(flAngle<0.0001) return 0 ;
+
+	if(flAngle>flPI-0.01) // near on 180 degrees
+		flAngle=flPI-0.01 ;
+
+
+	Q3BspVertex backshift ;
+	backshift=normal ;
+	VertexScale(&backshift, 0.95) ;
+	
+
+	if( !VectorsAreEqual(&NormalVert, &xaxis) && !VectorsAreEqual(&NormalVert, &xaxisneg) )
+		tangentA=NormalizedCrossProduct(Origin, normal, xaxis) ;
+	else
+		tangentA=NormalizedCrossProduct(Origin, normal, yaxis) ;
+	
+	tangentB=NormalizedCrossProduct(Origin, normal, tangentA) ;
+
+	tangentANorm=tangentA ;
+	tangentBNorm=tangentB ;
+
+
+	
+	theta=flHALFPI-flAngle ; // angle between adjacent and hypotenuse (the normal is the "opposite" side, and we know there's a right angle)
+	adjacent=VertexDistance(&Origin, &normal)/tan(theta) ; 
+
+	//////////////////////////////////////////////////////////////////////
+	
+	Vert_Or.position[0]=end.position[0]-backshift.position[0] ;
+	Vert_Or.position[1]=end.position[1]-backshift.position[1] ;
+	Vert_Or.position[2]=end.position[2]-backshift.position[2] ;
+	Vert_Or.normal[0]= NormalVert.position[0] ;
+	Vert_Or.normal[1]= NormalVert.position[1] ;
+	Vert_Or.normal[2]= NormalVert.position[2] ;
+	Vert_Or.texcoord[0][0]=flCentreX ;
+	Vert_Or.texcoord[0][1]=flCentreY ;
+	Vert_Or.texcoord[1][0]=flCentreZ ;
+	Vert_Or.texcoord[1][1]=flBrightness ;
+	Vert_Or.color[0]=(unsigned char)flColR ;
+	Vert_Or.color[1]=(unsigned char)flColG ;
+	Vert_Or.color[2]=(unsigned char)flColB ;
+	if(!AddVertex(Vert_Or)) return 0 ;
+
+
+
+
+	//////////////////////////////////////////////////////////////////////
+
+	flColR=0.0f ;
+	flColG=0.0f ;
+	flColB=0.0f ;
+
+	tangentA=GetNormalised(&tangentA) ;
+	VertexScale(&tangentA,  adjacent) ; 
+
+	tangentStart=start ;
+	tangentEnd=VectorSubtract(&end, &tangentA) ;
+	ray=VectorSubtract(&tangentEnd, &tangentStart) ;
+	ray=GetNormalised(&ray) ;
+	VertexScale(&ray, flCutoff) ;
+	tangentStart=start ;
+	tangentEndA0=VectorAdd(&start, &ray) ;
+
+	raytangent=VectorSubtract(&end, &tangentEndA0) ;
+	raytangent=VectorAdd(&raytangent, &adjust) ;
+	raytangent=GetNormalised(&raytangent) ;
+
+
+
+	tangentEndA0=VectorSubtract(&tangentEndA0, &backshift) ;
+	Vert_A0.position[0]=tangentEndA0.position[0] ;
+	Vert_A0.position[1]=tangentEndA0.position[1] ;
+	Vert_A0.position[2]=tangentEndA0.position[2] ;
+	Vert_A0.normal[0]= -raytangent.position[0] ;
+	Vert_A0.normal[1]= -raytangent.position[1] ;
+	Vert_A0.normal[2]= -raytangent.position[2] ;
+	Vert_A0.texcoord[0][0]=flCentreX ;
+	Vert_A0.texcoord[0][1]=flCentreY ;
+	Vert_A0.texcoord[1][0]=flCentreZ ;
+	Vert_A0.texcoord[1][1]=flBrightness ;
+	Vert_A0.color[0]=(unsigned char)flColR ;//abs(Vert_A0.normal[0])*255 ;//0.0f ;
+	Vert_A0.color[1]=(unsigned char)flColG ;//abs(Vert_A0.normal[1])*255 ;//0.0f ;
+	Vert_A0.color[2]=(unsigned char)flColB ;//abs(Vert_A0.normal[2])*255 ;//0.0f ;
+	if(!AddVertex(Vert_A0)) return 0 ;
+
+
+	tangentStart=start ;
+	tangentEnd=VectorAdd(&end, &tangentA) ;
+	ray=VectorSubtract(&tangentEnd, &tangentStart) ;
+	ray=GetNormalised(&ray) ;
+	VertexScale(&ray, flCutoff) ; //ray.getScaledBy(cutoff) ;
+	tangentStart=start ;
+	tangentEndA1=VectorAdd(&start, &ray) ;
+
+	raytangent=VectorSubtract(&end, &tangentEndA1) ;
+	raytangent=VectorAdd(&raytangent, &adjust) ;
+	raytangent=GetNormalised(&raytangent) ;
+
+	tangentEndA1=VectorSubtract(&tangentEndA1, &backshift) ;
+	Vert_A1.position[0]=tangentEndA1.position[0] ;
+	Vert_A1.position[1]=tangentEndA1.position[1] ;
+	Vert_A1.position[2]=tangentEndA1.position[2] ;
+	Vert_A1.normal[0]= -raytangent.position[0] ;
+	Vert_A1.normal[1]= -raytangent.position[1] ;
+	Vert_A1.normal[2]= -raytangent.position[2] ;
+	Vert_A1.texcoord[0][0]=flCentreX ;
+	Vert_A1.texcoord[0][1]=flCentreY ;
+	Vert_A1.texcoord[1][0]=flCentreZ ;
+	Vert_A1.texcoord[1][1]=flBrightness ;
+	Vert_A1.color[0]=(unsigned char)flColR ;//abs(Vert_A1.normal[0])*255 ;//0.0f ;
+	Vert_A1.color[1]=(unsigned char)flColG ;//abs(Vert_A1.normal[1])*255 ;//0.0f ;
+	Vert_A1.color[2]=(unsigned char)flColB ;//abs(Vert_A1.normal[2])*255 ;//0.0f ;
+	if(!AddVertex(Vert_A1)) return 0 ;
+
+	//////////////////////////////////////////////////////////////////////
+
+	tangentB=GetNormalised(&tangentB) ;
+	VertexScale(&tangentB, adjacent) ; //tangentB.getScaledBy(adjacent) ;
+
+	tangentStart=start ;
+	tangentEnd=VectorSubtract(&end, &tangentB) ;
+	ray=VectorSubtract(&tangentEnd, &tangentStart) ;
+	ray=GetNormalised(&ray) ;
+	VertexScale(&ray, flCutoff) ; //ray.getScaledBy(cutoff) ;
+	tangentStart=start ;
+	tangentEndB0=VectorAdd(&start, &ray) ;
+
+	raytangent=VectorSubtract(&end, &tangentEndB0) ;
+	raytangent=VectorAdd(&raytangent, &adjust) ;
+	raytangent=GetNormalised(&raytangent) ;
+
+	tangentEndB0=VectorSubtract(&tangentEndB0, &backshift) ;
+	Vert_B0.position[0]=tangentEndB0.position[0] ;
+	Vert_B0.position[1]=tangentEndB0.position[1] ;
+	Vert_B0.position[2]=tangentEndB0.position[2] ;
+	Vert_B0.normal[0]= -raytangent.position[0] ;
+	Vert_B0.normal[1]= -raytangent.position[1] ;
+	Vert_B0.normal[2]= -raytangent.position[2] ;
+	Vert_B0.texcoord[0][0]=flCentreX ;
+	Vert_B0.texcoord[0][1]=flCentreY ;
+	Vert_B0.texcoord[1][0]=flCentreZ ;
+	Vert_B0.texcoord[1][1]=flBrightness ;
+	Vert_B0.color[0]=(unsigned char)flColR ;//abs(Vert_B0.normal[0])*255 ;//0.0f ;
+	Vert_B0.color[1]=(unsigned char)flColG ;//abs(Vert_B0.normal[1])*255 ;//0.0f ;
+	Vert_B0.color[2]=(unsigned char)flColB ;//abs(Vert_B0.normal[2])*255 ;//0.0f ;
+	if(!AddVertex(Vert_B0)) return 0 ;
+
+	tangentStart=start ;
+	tangentEnd=VectorAdd(&end, &tangentB) ;
+	ray=VectorSubtract(&tangentEnd, &tangentStart) ;
+	ray=GetNormalised(&ray) ;
+	VertexScale(&ray, flCutoff) ; //ray.getScaledBy(cutoff) ;
+	tangentStart=start ;
+	tangentEndB1=VectorAdd(&start, &ray) ;
+
+	raytangent=VectorSubtract(&end, &tangentEndB1) ;
+	raytangent=VectorAdd(&raytangent, &adjust) ;
+	raytangent=GetNormalised(&raytangent) ;
+
+	tangentEndB1=VectorSubtract(&tangentEndB1, &backshift) ;
+	Vert_B1.position[0]=tangentEndB1.position[0] ;
+	Vert_B1.position[1]=tangentEndB1.position[1] ;
+	Vert_B1.position[2]=tangentEndB1.position[2] ;
+	Vert_B1.normal[0]= -raytangent.position[0] ;
+	Vert_B1.normal[1]= -raytangent.position[1] ;
+	Vert_B1.normal[2]= -raytangent.position[2] ;
+	Vert_B1.texcoord[0][0]=flCentreX ;
+	Vert_B1.texcoord[0][1]=flCentreY ;
+	Vert_B1.texcoord[1][0]=flCentreZ ;
+	Vert_B1.texcoord[1][1]=flBrightness ;
+	Vert_B1.color[0]=(unsigned char)flColR ;//abs(Vert_B1.normal[0])*255 ;//0.0f ;
+	Vert_B1.color[1]=(unsigned char)flColG ;//abs(Vert_B1.normal[1])*255 ;//0.0f ;
+	Vert_B1.color[2]=(unsigned char)flColB ;//abs(Vert_B1.normal[2])*255 ;//0.0f ;
+	if(!AddVertex(Vert_B1)) return 0 ;
+
+	/////////////////////////////////////////////////////////////////////
+
+
+
+
+	// the four verts are position correctly to make the large end of a cone (or rather, pyramid) that would be consistent
+	// with the angle and their distance from the origin.  However we cheat and move them back on top of the origin
+	// so that map lights come out looking better.
+	
+	/*
+	SetVertex(&normal, m_pLight[nLight].Direction[0]*flCutoff, m_pLight[nLight].Direction[1]*flCutoff, m_pLight[nLight].Direction[2]*flCutoff) ;
+
+	tangentEndA0=VectorSubtract(&tangentEndA0, &normal) ;
+	Vert_A0.position[0]=tangentEndA0.position[0] ;
+	Vert_A0.position[1]=tangentEndA0.position[1] ;
+	Vert_A0.position[2]=tangentEndA0.position[2] ;
+
+	tangentEndA1=VectorSubtract(&tangentEndA1, &normal) ;
+	Vert_A1.position[0]=tangentEndA1.position[0] ;
+	Vert_A1.position[1]=tangentEndA1.position[1] ;
+	Vert_A1.position[2]=tangentEndA1.position[2] ;
+
+	tangentEndB0=VectorSubtract(&tangentEndB0, &normal) ;
+	Vert_A0.position[0]=tangentEndB0.position[0] ;
+	Vert_A0.position[1]=tangentEndB0.position[1] ;
+	Vert_A0.position[2]=tangentEndB0.position[2] ;
+
+	tangentEndB1=VectorSubtract(&tangentEndB1, &normal) ;
+	Vert_A1.position[0]=tangentEndB1.position[0] ;
+	Vert_A1.position[1]=tangentEndB1.position[1] ;
+	Vert_A1.position[2]=tangentEndB1.position[2] ;
+	*/
+
+
+	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+		Triangle.Texture=m_nDefaultTextureIndexGlowLight ;
+		Triangle.Lamp=-1 ;
+		Triangle.Group=m_nGroup ;
+
+		/////////////////////////////////////
+
+		Triangle.VIndex[0]=n_Or ;
+		Triangle.VIndex[2]=n_A0 ;
+		Triangle.VIndex[1]=n_B1 ;
+		if(!AddTriangle(Triangle)) return 0 ;
+
+		Triangle.VIndex[0]=n_Or ;
+		Triangle.VIndex[2]=n_B1 ;
+		Triangle.VIndex[1]=n_A1 ;
+		if(!AddTriangle(Triangle)) return 0 ;
+
+		Triangle.VIndex[0]=n_Or ;
+		Triangle.VIndex[2]=n_A1 ;
+		Triangle.VIndex[1]=n_B0 ;
+		if(!AddTriangle(Triangle)) return 0 ;
+		
+		Triangle.VIndex[0]=n_Or ;
+		Triangle.VIndex[2]=n_B0 ;
+		Triangle.VIndex[1]=n_A0 ;
+		if(!AddTriangle(Triangle)) return 0 ;
+
+		m_nGroup++ ; // increment group once for each glow
+		
+	}
+	
+	return 1 ;
+}
+
+
+
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////
+// some vertex manipulation functions used for setting up the light disk.
+// They only work on the position, so other parts of the struct need to be setup seperately
+
+
+void Q3Map::SetVertex(Q3BspVertex *pVert, float flXPos, float flYPos, float flZPos)
+{
+	pVert->position[0]=flXPos ;
+	pVert->position[1]=flYPos ;
+	pVert->position[2]=flZPos ;
+}
+
+// this doesn't test exact equivalence, but rather within the range of an epsilon VERYSMALL
+bool Q3Map::VectorsAreEqual(Q3BspVertex* pVecA, Q3BspVertex* pVecB)
+{
+	if( fabs(pVecA->position[0] - pVecB->position[0])>VERYSMALL ) return false ;
+	if( fabs(pVecA->position[1] - pVecB->position[1])>VERYSMALL ) return false ;
+	if( fabs(pVecA->position[2] - pVecB->position[2])>VERYSMALL ) return false ;
+
+	return true ;
+}
+
+// VertA is the origin of VertB and VertC
+Q3BspVertex Q3Map::NormalizedCrossProduct(Q3BspVertex VertA, Q3BspVertex VertB, Q3BspVertex VertC)
+{
+
+
+	Q3BspVertex Cross ;
+
+	// edge vectors
+	float flVecXA_CP = VertA.position[0] - VertB.position[0] ;
+	float flVecYA_CP = VertA.position[1] - VertB.position[1] ;
+	float flVecZA_CP = VertA.position[2] - VertB.position[2] ;
+	float flVecXB_CP = VertA.position[0] - VertC.position[0] ;
+	float flVecYB_CP = VertA.position[1] - VertC.position[1] ;
+	float flVecZB_CP = VertA.position[2] - VertC.position[2] ;
+
+	// cross product
+	float flCpx_CP = (flVecZA_CP * flVecYB_CP) - (flVecYA_CP * flVecZB_CP);
+	float flCpy_CP = (flVecXA_CP * flVecZB_CP) - (flVecZA_CP * flVecXB_CP);
+	float flCpz_CP = (flVecYA_CP * flVecXB_CP) - (flVecXA_CP * flVecYB_CP);
+
+	// Normalize 
+	float flR_CP = sqrt(flCpx_CP * flCpx_CP + flCpy_CP * flCpy_CP + flCpz_CP * flCpz_CP);
+
+	Cross.position[0] = flCpx_CP / flR_CP;
+	Cross.position[1] = flCpy_CP / flR_CP;
+	Cross.position[2] = flCpz_CP / flR_CP;
+
+	return Cross ;
+}
+
+float Q3Map::VertexDistance(Q3BspVertex* VertA, Q3BspVertex* VertB)
+{
+	float flXDis=VertA->position[0]-VertB->position[0] ;
+	float flYDis=VertA->position[1]-VertB->position[1] ;
+	float flZDis=VertA->position[2]-VertB->position[2] ;
+
+	return sqrt(flXDis*flXDis+flYDis*flYDis+flZDis*flZDis) ;
+}
+
+void Q3Map::VertexScale(Q3BspVertex* pVert, float flScale)
+{
+	pVert->position[0]*=flScale ;
+	pVert->position[1]*=flScale ;
+	pVert->position[2]*=flScale ;
+}
+
+Q3BspVertex Q3Map::GetNormalised(Q3BspVertex* pVector)
+{
+	float flLength=sqrt((pVector->position[0]*pVector->position[0])+(pVector->position[1]*pVector->position[1])+(pVector->position[2]*pVector->position[2])) ;
+
+	Q3BspVertex Vector=*pVector ;
+
+	Vector.position[0]/=flLength ;
+	Vector.position[1]/=flLength ;
+	Vector.position[2]/=flLength ;
+
+	return Vector ;
+}
+
+Q3BspVertex Q3Map::VectorAdd(Q3BspVertex* pVecA, Q3BspVertex* pVecB)
+{
+	Q3BspVertex Vector ;
+
+	Vector.position[0]=pVecA->position[0] + pVecB->position[0] ;
+	Vector.position[1]=pVecA->position[1] + pVecB->position[1] ;
+	Vector.position[2]=pVecA->position[2] + pVecB->position[2] ;
+
+	return Vector ;
+}
+
+Q3BspVertex Q3Map::VectorSubtract(Q3BspVertex* pVecA, Q3BspVertex* pVecB)
+{
+	Q3BspVertex Vector ;
+
+	Vector.position[0]=pVecA->position[0] - pVecB->position[0] ;
+	Vector.position[1]=pVecA->position[1] - pVecB->position[1] ;
+	Vector.position[2]=pVecA->position[2] - pVecB->position[2] ;
+
+	return Vector ;
+}
+
+Q3BspVertex Q3Map::VectorMultiply(Q3BspVertex* pVecA, Q3BspVertex* pVecB)
+{
+	Q3BspVertex Vector ;
+
+	Vector.position[0]=pVecA->position[0] * pVecB->position[0] ;
+	Vector.position[1]=pVecA->position[1] * pVecB->position[1] ;
+	Vector.position[2]=pVecA->position[2] * pVecB->position[2] ;
+
+	return Vector ;
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+
+
+void Q3Map::GetTexLampTextureNumbers()
+{
+	m_nBZN_LightNode0=-1 ;
+	m_nBZN_LightNode1=-1 ;
+	m_nBZN_LightNode2=-1 ;
+	m_nBZN_LightNode3=-1 ;
+
+	int nTex=0;
+	char chTexName[1024] ;
+	int nLen=0 ;
+	int nPos=0 ;
+
+	int nMatch=0 ;
+
+	strcpy(chTexName, "textures/common/bzn_lightnode") ;
+	nLen=strlen(chTexName) ;
+
+	for(nTex=0 ; nTex<m_nTextureMax ; nTex++)
+	{
+		nMatch=1 ;
+		for(nPos=nLen-1 ; nPos>=0 ; nPos--) // faster to check backwards since lots of textures will start with "textures/common/"
+			if(m_pTexture[nTex].name[nPos]!=chTexName[nPos])
+			{
+				nMatch=0 ;
+				break ;
+			}
+
+		if(nMatch)
+		{
+			// what is the last character?
+			if(m_pTexture[nTex].name[nLen]=='0')
+				m_nBZN_LightNode0=nTex ;
+			else
+			if(m_pTexture[nTex].name[nLen]=='1')
+				m_nBZN_LightNode1=nTex ;
+			else
+			if(m_pTexture[nTex].name[nLen]=='2')
+				m_nBZN_LightNode2=nTex ;
+			else
+			if(m_pTexture[nTex].name[nLen]=='3')
+				m_nBZN_LightNode3=nTex ;
+			
+		}// end if match
+
+
+	}// end for nTex
+
+}
+
+int Q3Map::SortTrianglesIntoGroups(void)
+{
+
+
+	int nNewSize=0 ; // we will drop non-subzoned (-1 zone) triangles here, so new number of triangles may be less than old number.
+
+	ULONGLONG *pFaceOrder = new ULONGLONG[m_nTriangleMax] ;
+	if(pFaceOrder==NULL) return 0 ; // out of memory.
+
+	// temporary copy of m_pTriangles to make sorting easier
+	triangle_t *TempTriangle = new triangle_t[m_nTriangleMax];
+	if(TempTriangle==NULL) { DELETE_ARRAY( pFaceOrder ) ; return 0 ; } // out of memory
+	memcpy((void*)TempTriangle, (void*)m_pTriangle, m_nTriangleMax*sizeof(triangle_t)) ;
+
+	// create the initial face "value" by setting the most significant bits to it's criteria number
+	for (int i=0; i < m_nTriangleMax; i++)
+		if(m_pTriangle[i].Zone!=-1) // drop unsubzoned triangles
+			pFaceOrder[nNewSize++] = ( ((ULONGLONG)m_pTriangle[i].Zone)<<GROUPSORT_SUBZONE_SHIFT ) + ( ((ULONGLONG)m_pTriangle[i].Texture)<<GROUPSORT_GROUP_SHIFT ) + i ;
+
+	// sort the ULONGLONGs, this will order things according to our criteria
+	// sorting is based firstly on subzone, secondly on texture.  
+	// So all triangles in a subzone will be grouped together, 
+	// and within that group all triangles with the same material will be grouped together.  
+
+	qsort( (void *)pFaceOrder, nNewSize, sizeof(ULONGLONG), &Q3Map::compareGroups);
+
+	// copy the sorted faces back to the original
+	for (int i=0; i < nNewSize; i++)
+		m_pTriangle[i]=TempTriangle[ (pFaceOrder[i]&FACESORT_FACE_MASK) ] ;
+
+	// update m_nTriangleMax to show that there may now be less triangles
+	m_nTriangleMax=nNewSize ;
+
+	// clean up the memory we used
+	DELETE_ARRAY( pFaceOrder ) ;
+	DELETE_ARRAY( TempTriangle ) ;
+
+
+	// now that the triangles are sorting according to zone and group, 
+	// re-assign the group numbers per zone, from 0 to FACESORT_GROUP_LIMIT-1
+	// All non-trans triangles will be group 0, trans triangles will be 1 to FACESORT_GROUP_LIMIT-1
+
+	int nTri=0 ;
+	int nZone=-1 ;
+	int nGroup=-1 ;
+	int nNewGroup=0 ;
+
+	//char chMessage[1024] ;
+
+	for(nTri=0 ; nTri<m_nTriangleMax ; nTri++)
+	{
+		// reset the newgroup if we've got a new zone
+		if(m_pTriangle[nTri].Zone!=nZone) 
+		{
+			nZone=m_pTriangle[nTri].Zone ;
+			nGroup=-1 ;
+			nNewGroup=0 ;
+		}	
+
+		// if we have a new group, increment the newgroup number, fail if we have too many
+		if(m_pTriangle[nTri].Group!=nGroup) 
+		{
+			// if this is a trans triangle, inc the newgroup
+			if(m_pTransTexture[ m_pTriangle[nTri].Texture ])
+			{
+				nNewGroup++ ; // will always be at least 1
+				if(nNewGroup>=FACESORT_GROUP_LIMIT) return 0 ; // too many groups in a zone.
+			}
+
+			nGroup=m_pTriangle[nTri].Group ;
+		}
+
+		if(m_pTransTexture[ m_pTriangle[nTri].Texture ]==0)
+			m_pTriangle[nTri].Group=0 ;
+		else
+			m_pTriangle[nTri].Group=nNewGroup ;
+
+
+
+	}// end for tri
+
+	return 1 ;
+}
+
+
+// static function for sorting groups, required by qsort
+int Q3Map::compareGroups( const void *arg1, const void *arg2 )
+{
+	ULONGLONG FaceA= *(ULONGLONG*)arg1 ;
+	ULONGLONG FaceB= *(ULONGLONG*)arg2 ;
+
+	if(FaceA < FaceB)
+		return -1 ;
+	else
+		if(FaceA > FaceB)
+			return 1 ;
+		
+	return 0 ;
+}
+
+
+
+// sort faces so that we can batch effectively when constructing our manualobjects
+// Currently grouped according to zone and texture.
+// Triangles not in any subzone will be dropped at this stage.
+int Q3Map::SortTrianglesIntoBatches(void)
+{
+
+
+	int nNewSize=0 ; // we will drop non-subzoned (-1 zone) triangles here, so new number of triangles may be less than old number.
+
+	ULONGLONG *pFaceOrder = new ULONGLONG[m_nTriangleMax] ;
+	if(pFaceOrder==NULL) return 0 ; // out of memory.
+
+	// temporary copy of m_pTriangles to make sorting easier
+	triangle_t *TempTriangle = new triangle_t[m_nTriangleMax];
+	if(TempTriangle==NULL) { DELETE_ARRAY( pFaceOrder ) ; return 0 ; } // out of memory
+	memcpy((void*)TempTriangle, (void*)m_pTriangle, m_nTriangleMax*sizeof(triangle_t)) ;
+
+	// create the initial face "value" by setting the most significant bits to it's criteria number
+	for (int i=0; i < m_nTriangleMax; i++)
+		if(m_pTriangle[i].Zone!=-1) // drop unsubzoned triangles
+			pFaceOrder[nNewSize++] = ( ((ULONGLONG)m_pTriangle[i].Zone)<<FACESORT_SUBZONE_SHIFT ) + ( ((ULONGLONG)m_pTriangle[i].Texture)<<FACESORT_TEXTURE_SHIFT ) + ( ((ULONGLONG)m_pTriangle[i].Group)<<FACESORT_GROUP_SHIFT ) + i ;
+
+	// sort the ULONGLONGs, this will order things according to our criteria
+	// sorting is based firstly on subzone, secondly on texture.  
+	// So all triangles in a subzone will be grouped together, 
+	// and within that group all triangles with the same material will be grouped together.  
+
+	qsort( (void *)pFaceOrder, nNewSize, sizeof(ULONGLONG), &Q3Map::compareTriangles);
+
+	// copy the sorted faces back to the original
+	for (int i=0; i < nNewSize; i++)
+		m_pTriangle[i]=TempTriangle[ (pFaceOrder[i]&FACESORT_FACE_MASK) ] ;
+
+	// update m_nTriangleMax to show that there may now be less triangles
+	m_nTriangleMax=nNewSize ;
+
+	// clean up the memory we used
+	DELETE_ARRAY( pFaceOrder ) ;
+	DELETE_ARRAY( TempTriangle ) ;
+
+	return 1 ;
+}
+
+// static function for sorting triangles, required by qsort
+int Q3Map::compareTriangles( const void *arg1, const void *arg2 )
+{
+	ULONGLONG FaceA= *(ULONGLONG*)arg1 ;
+	ULONGLONG FaceB= *(ULONGLONG*)arg2 ;
+
+	if(FaceA < FaceB)
+		return -1 ;
+	else
+		if(FaceA > FaceB)
+			return 1 ;
+		
+	return 0 ;
+}
+
+
+
+
+// note which texture numbers correspond to textures that have transparency.
+// needed for when we work out transparency groups and related stuff
+int Q3Map::SetupTransTextures(void)
+{
+	int nMaterial=0 ;
+	int nPos=0 ;
+	char chMaterial[1024] ;
+
+	// create the memory for the transtextures
+	m_pTransTexture = new int[m_nTextureMax] ;
+	if(m_pTransTexture==NULL) return 0 ; // out of memory.
+
+	for(nMaterial=0 ; nMaterial<m_nTextureMax ; nMaterial++)
+	{
+		// copy the material name.
+		// q3 texture names are a max of 64 characters ( Q3NAMESIZE ) and may not be null terminated.  They have no extension either.
+		nPos=-1 ;
+		while((++nPos<Q3NAMESIZE) && (m_pTexture[nMaterial].name[nPos]!=' ') && (m_pTexture[nMaterial].name[nPos]!='\0'))
+			chMaterial[nPos]=m_pTexture[nMaterial].name[nPos] ;
+
+		// make sure name is null terminated
+		chMaterial[nPos]='\0' ;
+
+
+		// is the texture a type we need to flag as transparent?
+		if(strstr(chMaterial, "GEL")!=NULL)
+			m_pTransTexture[nMaterial]=1 ;
+		else
+			m_pTransTexture[nMaterial]=0 ;
+
+	}// end for nMaterial
+
+	return 1 ;
+
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+int Q3Map::AssignPortalsToZones(void)
+{
+	int nZone=0 ;
+	float flMinX=0.0f ;
+	float flMaxX=0.0f ;
+	float flMinY=0.0f ;
+	float flMaxY=0.0f ;
+	float flMinZ=0.0f ;
+	float flMaxZ=0.0f ;
+	int nMaxSubZone=m_iNumSubZones ;
+	int nSubZone=0 ;
+	int nMaxPortal=m_iNumPortals ;
+	int nPortal=0 ;
+	int nIndex=0 ;
+	int nPos=0 ;
+
+
+
+	// clear the portal settings.
+	for(nZone=0 ; nZone<m_nMaxZone ; nZone++)
+		m_nZoneTouchesPortal[nZone][INDEX_PORTALCOUNT]=0 ;
+
+	for(nPortal=0 ; nPortal<nMaxPortal ; nPortal++)
+		m_nPortalTouchesZone[nPortal][INDEX_PORTALZONECOUNT]=0 ;
+
+
+
+	// now test each subzone against each portal to see what zones each portal contains.
+	for(nSubZone=0 ; nSubZone<nMaxSubZone ; nSubZone++)
+	{
+		nZone=m_pSubZones[nSubZone].Zone ;
+		flMinX=m_pSubZones[nSubZone].Min[0] ;
+		flMaxX=m_pSubZones[nSubZone].Max[0] ;
+		flMinY=m_pSubZones[nSubZone].Min[1] ;
+		flMaxY=m_pSubZones[nSubZone].Max[1] ;
+		flMinZ=m_pSubZones[nSubZone].Min[2] ;
+		flMaxZ=m_pSubZones[nSubZone].Max[2] ;
+
+		// test all the portals to see if any overlap this subzone
+		for(nPortal=0 ; nPortal<nMaxPortal ; nPortal++)
+		{
+
+			// if AABB overlap
+			if(
+						 (flMinX<m_pPortals[nPortal].Max[0]) && (flMaxX>m_pPortals[nPortal].Min[0])
+					&& (flMinY<m_pPortals[nPortal].Max[1]) && (flMaxY>m_pPortals[nPortal].Min[1])
+					&& (flMinZ<m_pPortals[nPortal].Max[2]) && (flMaxZ>m_pPortals[nPortal].Min[2])
+				)
+			{
+
+				// add this portal to the zone's portal list
+				nIndex=m_nZoneTouchesPortal[nZone][INDEX_PORTALCOUNT] ;
+				if(nIndex<MAX_PORTALPERZONE) // only add if we aren't already maxed out.
+				{
+					// we need to check this portal isn't already on the list.
+					// it might have gotten on by another subzone, since a zone can have multiple subzones.
+					nPos=-1 ;
+					while((++nPos<nIndex) && (m_nZoneTouchesPortal[nZone][nPos]!=nPortal)) ; 
+
+					if(nPos==nIndex) // this can only be true if we didn't already find nPortal in the list
+					{
+						m_nZoneTouchesPortal[nZone][nIndex]=nPortal ;
+						m_nZoneTouchesPortal[nZone][INDEX_PORTALCOUNT]++ ;
+					}
+				}
+
+				// add this zone to the portal's list
+				nIndex=m_nPortalTouchesZone[nPortal][INDEX_PORTALZONECOUNT] ;
+				if(nIndex<MAX_ZONEPERPORTAL)
+				{
+					// we need to check this zone isn't already on the list.
+					// it might have gotten on by another subzone, since a zone can have multiple subzones.
+					nPos=-1 ;
+					while((++nPos<nIndex) && (m_nPortalTouchesZone[nPortal][nPos]!=nZone)) ; 
+
+					if(nPos==nIndex) // this can only be true if we didn't already find nZone in the list
+					{
+						m_nPortalTouchesZone[nPortal][nIndex]=nZone ;
+						m_nPortalTouchesZone[nPortal][INDEX_PORTALZONECOUNT]++ ;
+					}
+				}
+
+			}// end if portal overlaps subzone
+
+		}// end for portal
+
+	}// end for subzone
+
+	return 1 ;
+}
+
+int Q3Map::AssignLightsToZones(void)
+{
+	int nZone=0 ;
+	float flMinX=0.0f ;
+	float flMaxX=0.0f ;
+	float flMinY=0.0f ;
+	float flMaxY=0.0f ;
+	float flMinZ=0.0f ;
+	float flMaxZ=0.0f ;
+	int nMaxSubZone=m_iNumSubZones ;
+	int nSubZone=0 ;
+	int nMaxLight=m_nLightMax ;
+	int nLight=0 ;
+	int nIndex=0 ;
+	int nPos=0 ;
+
+
+
+	// clear the light settings.
+	for(nZone=0 ; nZone<m_nMaxZone ; nZone++)
+		m_nZoneContainsLightCentre[nZone][INDEX_LIGHTCOUNT]=m_nZoneTouchesSubLight[nZone][INDEX_LIGHTCOUNT]=0 ;
+
+	for(nLight=0 ; nLight<nMaxLight ; nLight++)
+		m_nLightTouchesZone[nLight][INDEX_LIGHTZONECOUNT]=0 ;
+	
+
+
+
+	// now test each subzone against each light what contains/touches what
+	for(nSubZone=0 ; nSubZone<nMaxSubZone ; nSubZone++)
+	{
+		nZone=m_pSubZones[nSubZone].Zone ;
+		flMinX=m_pSubZones[nSubZone].Min[0] ;
+		flMaxX=m_pSubZones[nSubZone].Max[0] ;
+		flMinY=m_pSubZones[nSubZone].Min[1] ;
+		flMaxY=m_pSubZones[nSubZone].Max[1] ;
+		flMinZ=m_pSubZones[nSubZone].Min[2] ;
+		flMaxZ=m_pSubZones[nSubZone].Max[2] ;
+
+		// test all the lights to see if any have centers inside this subzone
+		for(nLight=0 ; nLight<nMaxLight ; nLight++)
+		{
+
+			/////////////////////////////////////////////////////////////////////////////////////
+
+			// if light center is in this subzone
+			if(
+						 (m_pLight[nLight].Position[0]>=flMinX) && (m_pLight[nLight].Position[0]<=flMaxX)
+					&& (m_pLight[nLight].Position[1]>=flMinY) && (m_pLight[nLight].Position[1]<=flMaxY)
+					&& (m_pLight[nLight].Position[2]>=flMinZ) && (m_pLight[nLight].Position[2]<=flMaxZ)
+				)
+			{
+
+				// add this light to the zone's light list
+				nIndex=m_nZoneContainsLightCentre[nZone][INDEX_LIGHTCOUNT] ;
+				if(nIndex<MAX_LIGHTPERZONE) // only add if we aren't already maxed out.
+				{
+					// we need to check this light isn't already on the list.
+					// it might have gotten on by another subzone, since a zone can have multiple subzones.
+					nPos=-1 ;
+					while((++nPos<nIndex) && (m_nZoneContainsLightCentre[nZone][nPos]!=nLight)) ; 
+
+					if(nPos==nIndex) // this can only be true if we didn't already find nLight in the list
+					{
+						m_nZoneContainsLightCentre[nZone][nIndex]=nLight ;
+						m_nZoneContainsLightCentre[nZone][INDEX_LIGHTCOUNT]++ ;
+
+						// assign this zone as the light's centre.  We only allow one zone to be the light's centre zone,
+						// so this will get overwritten if the light is on the border.
+						m_pLight[nLight].CentreZone=nZone ;
+					}
+				}
+
+			}// end if light centre contained by subzone
+
+			/////////////////////////////////////////////////////////////////////////////////////
+
+			// if light touches subzone (we store it in ZoneTouchesSubLight for now, will overwrite later)
+			// if light AABB overlaps the zone AABB
+			if(
+						 (flMinX<m_pLight[nLight].Max[0]) && (flMaxX>m_pLight[nLight].Min[0])
+					&& (flMinY<m_pLight[nLight].Max[1]) && (flMaxY>m_pLight[nLight].Min[1])
+					&& (flMinZ<m_pLight[nLight].Max[2]) && (flMaxZ>m_pLight[nLight].Min[2])
+				)
+			{
+				// add this light to the zone's light list
+				nIndex=m_nZoneTouchesSubLight[nZone][INDEX_LIGHTCOUNT] ;
+				if(nIndex<MAX_LIGHTPERZONE) // only add if we aren't already maxed out.
+				{
+					// we need to check this light isn't already on the list.
+					// it might have gotten on by another subzone, since a zone can have multiple subzones.
+					nPos=-1 ;
+					while((++nPos<nIndex) && (m_nZoneTouchesSubLight[nZone][nPos]!=nLight)) ; 
+
+					if(nPos==nIndex) // this can only be true if we didn't already find nPortal in the list
+					{
+						m_nZoneTouchesSubLight[nZone][nIndex]=nLight ;
+						m_nZoneTouchesSubLight[nZone][INDEX_LIGHTCOUNT]++ ;
+					}
+				}
+
+				// add this zone to the light's list
+				nIndex=m_nLightTouchesZone[nLight][INDEX_LIGHTZONECOUNT] ;
+				if(nIndex<MAX_ZONEPERLIGHT)
+				{
+					// we need to check this zone isn't already on the list.
+					// it might have gotten on by another subzone, since a zone can have multiple subzones.
+					nPos=-1 ;
+					while((++nPos<nIndex) && (m_nLightTouchesZone[nLight][nPos]!=nZone)) ; 
+
+					if(nPos==nIndex) // this can only be true if we didn't already find nZone in the list
+					{
+						m_nLightTouchesZone[nLight][nIndex]=nZone ;
+						m_nLightTouchesZone[nLight][INDEX_LIGHTZONECOUNT]++ ;
+					}
+				}
+
+			}// end if light touches contained by subzone
+
+			/////////////////////////////////////////////////////////////////////////////////////
+
+
+
+
+		}// end for light
+
+	}// end for subzone
+
+
+	/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+	//
+	// work out the sublights.  These are cut up boxes made from the light boxes, one per zone.
+	// Most lights will just become a single sublight, but multizone lights will be cut up into
+	// multiple sublights.  These are used for calculating light visibility.
+	//
+
+	m_nSubLightMax=0 ;
+	int nMaxZone=0 ;
+	int nZoneIndex=0 ;
+	int nSubLightCentre=0 ;
+	sublight_t TempSubLight ;
+	for(nLight=0 ; nLight<nMaxLight ; nLight++)
+	{
+
+		m_pLight[nLight].ZoneCount=m_nLightTouchesZone[nLight][INDEX_LIGHTZONECOUNT] ; // in the light, note how many zones it touches
+
+		if((m_nLightTouchesZone[nLight][INDEX_LIGHTZONECOUNT]>1) && (m_nMaxMultiZoneLight<MAX_LIGHT))// this is a multizone light
+			m_nMultiZoneLight[m_nMaxMultiZoneLight++]=nLight ;
+
+
+		m_pLight[nLight].SubLightStart=m_nSubLightMax ; // where this light's cut up sublights start in m_SubLight
+
+
+		// break the light up into it's different sublights, cut by zones.
+		nMaxZone=m_nLightTouchesZone[nLight][INDEX_LIGHTZONECOUNT] ;
+		for(nZoneIndex=0 ; nZoneIndex<nMaxZone ; nZoneIndex++)
+		{
+			nZone=m_nLightTouchesZone[nLight][nZoneIndex] ;
+
+			flMinX= m_pLight[nLight].Min[0]<m_ZoneBoundary[nZone].Min[0] ? m_ZoneBoundary[nZone].Min[0] : m_pLight[nLight].Min[0] ; 
+			flMinY= m_pLight[nLight].Min[1]<m_ZoneBoundary[nZone].Min[1] ? m_ZoneBoundary[nZone].Min[1] : m_pLight[nLight].Min[1] ; 
+			flMinZ= m_pLight[nLight].Min[2]<m_ZoneBoundary[nZone].Min[2] ? m_ZoneBoundary[nZone].Min[2] : m_pLight[nLight].Min[2] ;
+
+			flMaxX= m_pLight[nLight].Max[0]>m_ZoneBoundary[nZone].Max[0] ? m_ZoneBoundary[nZone].Max[0] : m_pLight[nLight].Max[0] ; 
+			flMaxY= m_pLight[nLight].Max[1]>m_ZoneBoundary[nZone].Max[1] ? m_ZoneBoundary[nZone].Max[1] : m_pLight[nLight].Max[1] ; 
+			flMaxZ= m_pLight[nLight].Max[2]>m_ZoneBoundary[nZone].Max[2] ? m_ZoneBoundary[nZone].Max[2] : m_pLight[nLight].Max[2] ; 
+
+			// add the cut down light as a sublight
+			m_SubLight[m_nSubLightMax].Light=nLight ;
+			m_SubLight[m_nSubLightMax].Zone=nZone ;
+			m_SubLight[m_nSubLightMax].Min[0]=flMinX ;
+			m_SubLight[m_nSubLightMax].Min[1]=flMinY ;
+			m_SubLight[m_nSubLightMax].Min[2]=flMinZ ;
+			m_SubLight[m_nSubLightMax].Max[0]=flMaxX ;
+			m_SubLight[m_nSubLightMax].Max[1]=flMaxY ;
+			m_SubLight[m_nSubLightMax].Max[2]=flMaxZ ;
+
+			// remember which sublight is the centre
+			if(
+						 (m_pLight[nLight].Position[0]>=flMinX) && (m_pLight[nLight].Position[0]<=flMaxX)
+					&& (m_pLight[nLight].Position[1]>=flMinY) && (m_pLight[nLight].Position[1]<=flMaxY)
+					&& (m_pLight[nLight].Position[2]>=flMinZ) && (m_pLight[nLight].Position[2]<=flMaxZ)
+				)
+				nSubLightCentre=m_nSubLightMax ;
+
+					
+			m_nSubLightMax++ ; // we don't have to worry about bound checking this, because we've already checked there aren't too many lights.
+
+		}// end for zoneindex			
+		
+		// move the sublight that contains the centre to the beginning of the sublights for this light.
+		// We always want the first sublight to contain the centre to make the culling algos work better.
+		TempSubLight=m_SubLight[ m_pLight[nLight].SubLightStart ] ;
+		m_SubLight[ m_pLight[nLight].SubLightStart ] = m_SubLight[ nSubLightCentre ] ;
+		m_SubLight[ nSubLightCentre ] = TempSubLight ;
+
+
+		
+	}// end for light
+
+	/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+	//
+	// recalculate m_nZoneTouchesSubLight using the newly created sublights 
+	// (instead of the complete lights that were originally used)
+	//
+
+	int nSubLight=0 ;
+
+
+	// clear the light settings.
+	for(nZone=0 ; nZone<m_nMaxZone ; nZone++)
+		m_nZoneTouchesSubLight[nZone][INDEX_LIGHTCOUNT]=0 ;
+
+	for(nSubZone=0 ; nSubZone<nMaxSubZone ; nSubZone++)
+	{
+		nZone=m_pSubZones[nSubZone].Zone ;
+		flMinX=m_pSubZones[nSubZone].Min[0] ;
+		flMaxX=m_pSubZones[nSubZone].Max[0] ;
+		flMinY=m_pSubZones[nSubZone].Min[1] ;
+		flMaxY=m_pSubZones[nSubZone].Max[1] ;
+		flMinZ=m_pSubZones[nSubZone].Min[2] ;
+		flMaxZ=m_pSubZones[nSubZone].Max[2] ;
+
+		// test all the lights to see if any have centers inside this subzone
+		for(nSubLight=0 ; nSubLight<m_nSubLightMax ; nSubLight++)
+		{
+			// if light touches subzone (we store it in ZoneTouchesSubLight for now, will overwrite later)
+			// if light AABB overlaps the zone AABB
+			if(
+						 (flMinX<m_SubLight[nSubLight].Max[0]) && (flMaxX>m_SubLight[nSubLight].Min[0])
+					&& (flMinY<m_SubLight[nSubLight].Max[1]) && (flMaxY>m_SubLight[nSubLight].Min[1])
+					&& (flMinZ<m_SubLight[nSubLight].Max[2]) && (flMaxZ>m_SubLight[nSubLight].Min[2])
+				)
+			{
+				// add this light to the zone's light list
+				nIndex=m_nZoneTouchesSubLight[nZone][INDEX_LIGHTCOUNT] ;
+				if(nIndex<MAX_LIGHTPERZONE) // only add if we aren't already maxed out.
+				{
+					// we need to check this light isn't already on the list.
+					// it might have gotten on by another subzone, since a zone can have multiple subzones.
+					nPos=-1 ;
+					while((++nPos<nIndex) && (m_nZoneTouchesSubLight[nZone][nPos]!=nSubLight)) ; 
+
+					if(nPos==nIndex) // this can only be true if we didn't already find nSubLight in the list
+					{
+						m_nZoneTouchesSubLight[nZone][nIndex]=nSubLight ;
+						m_nZoneTouchesSubLight[nZone][INDEX_LIGHTCOUNT]++ ;
+					}
+
+				}
+
+			}// end if overlap
+
+		}// end for nSubLight
+
+	}// end for nSubZone
+
+
+
+
+
+	return 1 ;
+}
+
+
+int Q3Map::AssignLightsToPortals(void)
+{
+	float flMinX=0.0f ;
+	float flMaxX=0.0f ;
+	float flMinY=0.0f ;
+	float flMaxY=0.0f ;
+	float flMinZ=0.0f ;
+	float flMaxZ=0.0f ;
+	int nMaxPortal=m_iNumPortals ;
+	int nPortal=0 ;
+	int nMaxLight=m_nLightMax ;
+	int nLight=0 ;
+	int nIndex=0 ;
+	int nPos=0 ;
+
+
+
+	// clear the light settings.
+	for(nPortal=0 ; nPortal<nMaxPortal ; nPortal++)
+		m_nPortalTouchesLight[nPortal][INDEX_PORTALLIGHTCOUNT]=0 ;
+
+	// now test each portal against each light to see if they touch
+	for(nPortal=0 ; nPortal<nMaxPortal ; nPortal++)
+	{
+		flMinX=m_pPortals[nPortal].Min[0] ;
+		flMaxX=m_pPortals[nPortal].Max[0] ;
+		flMinY=m_pPortals[nPortal].Min[1] ;
+		flMaxY=m_pPortals[nPortal].Max[1] ;
+		flMinZ=m_pPortals[nPortal].Min[2] ;
+		flMaxZ=m_pPortals[nPortal].Max[2] ;
+
+		// test all the lights to see if any touch this portal
+		for(nLight=0 ; nLight<nMaxLight ; nLight++)
+		{
+
+			// if light AABB overlaps the portal AABB
+			if(
+						 (flMinX<m_pLight[nLight].Max[0]) && (flMaxX>m_pLight[nLight].Min[0])
+					&& (flMinY<m_pLight[nLight].Max[1]) && (flMaxY>m_pLight[nLight].Min[1])
+					&& (flMinZ<m_pLight[nLight].Max[2]) && (flMaxZ>m_pLight[nLight].Min[2])
+				)
+			{
+
+				// add this light to the portal's light list
+				nIndex=m_nPortalTouchesLight[nPortal][INDEX_PORTALLIGHTCOUNT] ;
+				if(nIndex<MAX_LIGHTPERPORTAL) // only add if we aren't already maxed out.
+				{
+					// we need to check this light isn't already on the list.
+					// it might have gotten on by another subzone, since a zone can have multiple subzones.
+					nPos=-1 ;
+					while((++nPos<nIndex) && (m_nPortalTouchesLight[nPortal][nPos]!=nLight)) ; 
+
+					if(nPos==nIndex) // this can only be true if we didn't already find nLight in the list
+					{
+						m_nPortalTouchesLight[nPortal][nIndex]=nLight ;
+						m_nPortalTouchesLight[nPortal][INDEX_PORTALLIGHTCOUNT]++ ;
+					}
+				}
+
+				// we don't keep a list of the portals that a light touches, we don't use such a thing.
+
+			}// end if light touches portal
+
+		}// end for light
+
+	}// end for portal
+
+	return 1 ;
+}
+
+// work out all the other zones each zone touches, via portals
+int Q3Map::AssignZonesToZones(void)
+{
+	
+
+	int nCentralZone=0 ;
+	int nPortalZone=0 ;
+	int nMaxPortalZone=0 ;
+	int nPortalZoneIndex=0 ;
+	int nPortal=0 ;
+	int nPortalIndex=0 ;
+	int nMaxPortal=0 ;
+
+	int nTouchZoneIndex=0 ;
+	int nMaxTouchZone=0 ;
+	int nAddZone=0 ;
+
+
+	// scan through all the zones (consider them "central zones")
+	for(nCentralZone=0 ; nCentralZone<m_nMaxZone ; nCentralZone++)
+	{
+		nMaxTouchZone=0 ;
+
+		// scan through all the portals in this centralzone.
+		nMaxPortal=m_nZoneTouchesPortal[nCentralZone][INDEX_PORTALCOUNT] ;
+		for(nPortalIndex=0 ; nPortalIndex<nMaxPortal; nPortalIndex++)
+		{
+			nPortal=m_nZoneTouchesPortal[nCentralZone][nPortalIndex] ;
+
+			// scan through all the zones this portal touches and add then to the central zone's list
+			nMaxPortalZone=m_nPortalTouchesZone[nPortal][INDEX_PORTALZONECOUNT] ;
+			for(nPortalZoneIndex=0 ; nPortalZoneIndex<nMaxPortalZone ; nPortalZoneIndex++)
+			{
+				nPortalZone=m_nPortalTouchesZone[nPortal][nPortalZoneIndex] ;
+
+				// we've got a portal zone.  
+				if(nPortalZone==nCentralZone) continue ; // if it's the central zone, skip it.
+
+				// check we don't already have it listed.
+				nAddZone=1 ;
+				for(nTouchZoneIndex=0 ; nTouchZoneIndex<nMaxTouchZone ; nTouchZoneIndex++)
+				{
+					if(m_nZoneTouchesPortal[nCentralZone][nTouchZoneIndex]==nPortalZone)
+					{
+						nAddZone=0 ;
+						break ;
+					}// end if
+				}// end for nTouchZoneIndex
+	
+				if(nAddZone)
+				{
+					m_nZoneTouchesZone[nCentralZone][nMaxTouchZone]=nPortalZone ;
+					nMaxTouchZone++ ;
+					if(nMaxTouchZone>=MAX_ZONEPERZONE) 
+						nMaxTouchZone=MAX_ZONEPERZONE-1 ;
+
+					
+				}
+
+
+			}// end for portal zone index
+
+
+		}// end for portal index
+		
+		// set the maximum
+		m_nZoneTouchesZone[nCentralZone][INDEX_ZONEPERZONECOUNT]=nMaxTouchZone ;
+
+		/*
+		sprintf(m_chBug, "CentralZone %i, TouchedZoneCount %i", nCentralZone, m_nZoneTouchesPortal[nCentralZone][INDEX_ZONEPERZONECOUNT]) ;
+		Q3Bug.LogAddCR(m_chBug) ;
+		for(nTouchZoneIndex=0 ; nTouchZoneIndex<m_nZoneTouchesZone[nCentralZone][INDEX_ZONEPERZONECOUNT] ; nTouchZoneIndex++)
+		{
+			sprintf(m_chBug, "     TouchedZone %i", m_nZoneTouchesZone[nCentralZone][nTouchZoneIndex]) ;
+			Q3Bug.LogAddCR(m_chBug) ;
+		}
+		*/
+
+
+	}// end for central zone
+
+
+
+
+	return 1 ;
+}
+
+
+
Index: code/branches/presentation3/src/libraries/tools/bsp/Q3Map.h
===================================================================
--- code/branches/presentation3/src/libraries/tools/bsp/Q3Map.h	(revision 7073)
+++ code/branches/presentation3/src/libraries/tools/bsp/Q3Map.h	(revision 7073)
@@ -0,0 +1,709 @@
+/*
+===========================================================================
+Copyright (C) 2008 Daniel Örstadius
+Copyright (C) 2009 Jared Prince
+
+This file is part of bsp-renderer source code.
+
+bsp-renderer is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+bsp-renderer 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with bsp-renderer.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+// Q3Map.h -- handles the map data
+
+#ifndef _Q3MAP_H
+#define _Q3MAP_H
+
+#define WIN32_LEAN_AND_MEAN
+#include "windows.h"
+
+#include "Q3Map_Bezier.h"
+
+#include <string>
+#include <vector>
+
+#include "Q3Map_misc.h"
+
+//#include "JarDebug.h" //!! just for debugging, remove from final build
+
+#define MEMADD	65536 // memory is grown in 66k blocks
+#define MAX_TOKENSIZE 1024 // make sure tokens in texts aren't ridiculously large
+
+
+
+#define MINMAXLIMIT 10000000.0
+
+#define BRIGHTNESSTWEAK	0.5f //1.25f				// tweak for spotlight brightness
+
+#define ADDTEXTUREUNIQUE_FAIL		-1
+#define ADDTEXLAMP_FAIL					-2
+
+#define ADDSPOTLIGHTTEXTURE_FAIL	-1
+
+////////////////////////////////////////////////////
+
+#define AXIS_X	0
+#define AXIS_Y	1
+#define AXIS_Z	2
+
+////////////////////////////////////////////////////
+/*
+#define FACESORT_FACE_LIMIT			16777216
+#define FACESORT_FACE_SHIFT			0
+#define FACESORT_FACE_MASK			0x0000000000FFFFFF
+#define FACESORT_TEXTURE_LIMIT	4096
+#define FACESORT_TEXTURE_SHIFT	24
+#define FACESORT_TEXTURE_MASK		0x0000000FFF000000
+#define FACESORT_SUBZONE_LIMIT			4096 // also see MAX_ZONE
+#define FACESORT_SUBZONE_SHIFT			36
+#define FACESORT_SUBZONE_MASK			0x0000FFF000000000
+*/
+
+#define FACESORT_FACE_LIMIT			16777216
+#define FACESORT_FACE_SHIFT			0
+#define FACESORT_FACE_MASK			0x0000000000FFFFFF
+#define FACESORT_GROUP_LIMIT		65536	// also see MAX_TRANS
+#define FACESORT_GROUP_SHIFT		24
+#define FACESORT_GROUP_MASK			0x000000FFFF000000
+#define FACESORT_TEXTURE_LIMIT	4096
+#define FACESORT_TEXTURE_SHIFT	40
+#define FACESORT_TEXTURE_MASK		0x000FFF0000000000
+#define FACESORT_SUBZONE_LIMIT	4096 // also see MAX_ZONE
+#define FACESORT_SUBZONE_SHIFT	52
+#define FACESORT_SUBZONE_MASK		0xFFF0000000000000
+
+#define GROUPSORT_FACE_LIMIT		16777216
+#define GROUPSORT_FACE_SHIFT		0
+#define GROUPSORT_FACE_MASK			0x0000000000FFFFFF
+#define GROUPSORT_GROUP_LIMIT		16777216
+#define GROUPSORT_GROUP_SHIFT		24
+#define GROUPSORT_GROUP_MASK		0x0000FFFFFF000000
+#define GROUPSORT_SUBZONE_LIMIT	4096 // also see MAX_ZONE
+#define GROUPSORT_SUBZONE_SHIFT	52
+#define GROUPSORT_SUBZONE_MASK	0xFFF0000000000000
+
+
+
+////////////////////////////////////////////////////
+
+#define MAX_ZONE	4096							// max subzones and max zones. Also see FACESORT_SUBZONE_LIMIT
+#define MAX_SUBZONEPERZONE	16			// the most subzones that can make up a zone, further subzones ignored. INDEX_SUBZONECOUNT must be same as this
+#define INDEX_SUBZONECOUNT	16			// helps subzone counting in m_nZone.  See MAX_SUBZONEPERZONE
+
+#define MAX_PORTAL	32768						// max subzoneportals in a map.
+#define MAX_PORTALPERZONE 64				// the most portals that a zone can have, further portals ignored.  INDEX_PORTALCOUNT must be same as this
+#define MAX_ZONEPERPORTAL	8					// the most zones a portal can connect, further zones ignored.  INDEX_PORTALZONECOUNT must be the same as this
+#define MAX_LIGHTPERPORTAL 16				// the most lights that can touch a portal, further lights ignored. INDEX_PORTALLIGHTCOUNT must be the same as this
+#define INDEX_PORTALCOUNT	64				// helps portal counting in m_nZoneTouchesPortal. See MAX_PORTALSPERZONE
+#define INDEX_PORTALZONECOUNT 8			// helps portalconnect counting in m_nPortalTouchesZone. See MAX_ZONEPERPORTAL
+#define INDEX_PORTALLIGHTCOUNT 16		// helps portallight counting in m_PortalConnectsLight.	See MAX_LIGHTPERPORTAL
+
+#define MAX_ZONEPERZONE		64				// the most zones that a zone can connect to.
+#define INDEX_ZONEPERZONECOUNT	64  // helps zone per zone counting.
+
+#define MAX_LIGHT		32768						// max lights in a map, both from the map and other things (flashlight, missiles, etc)
+#define MAX_LIGHTPERZONE 64					// the most lights that a zone can have, further lights ignored.  INDEX_LIGHTCOUNT must be same as this
+#define MAX_ZONEPERLIGHT	8					// the most zones a light can touch, further zones ignored.  INDEX_LIGHTZONECOUNT must be the same as this
+#define INDEX_LIGHTCOUNT	64				// helps light counting in m_nZoneContainsLight.  see MAX_LIGHTPERZONE
+#define INDEX_LIGHTZONECOUNT	8			// helps light counting in m_nLightContainedByZone. See MAX_ZONEPERLIGHT
+
+#define MAX_SUBLIGHT	262144				// must be MAX_LIGHT * MAX_ZONEPERLIGHT
+#define MAX_TRANS			65536					// most transparent objects per zone (objects may be defined in various ways).  see FACESORT_TRANS_LIMIT
+
+
+
+////////////////////////////////////////////////////
+
+#define SUBZONE_EPSILON	0.01f	// small buffer of extra size around subzones so that we don't accidentally exclude a point due to float accuracy errors.
+#define FRUSTUM_EPSILON 0.0001f // once frustum is this tiny consider it dead
+#define VERYSMALL 0.00001			// small number
+
+#define PORTAL_UNCHECKED		0
+#define PORTAL_VISCHECK			1
+#define PORTAL_VISIBLE			2
+
+#define PORTALSTATE_OPENCHECK	0
+#define PORTALSTATE_CLOSED			1
+#define PORTALSTATE_OPEN				2
+
+#define ZONE_UNCHECKED			0
+#define ZONE_VISIBLE				1
+
+#define LIGHT_UNCHECKED				0
+#define LIGHT_OUTOFFRUSTUM		1
+#define LIGHT_INSIDEFRUSTUM		2
+#define LIGHT_NOTVISIBLE			4
+#define LIGHT_VISIBLE					8
+#define LIGHT_CHECKED					16
+
+///////////////////////////////////////////////////
+
+#define MAX_PROJECTORTEX	16		// most types of projector textures we can have per map.
+
+
+///////////////////////////////////////////////////
+
+// NOERROR is already defined in a windows file, it is 0
+#define ERROR_ParseMap						1
+#define ERROR_ParseEntities				2
+#define ERROR_AllocateVertex			3
+#define ERROR_AllocateTriangle		4
+#define ERROR_InitializeFaces			5
+#define ERROR_ConvertFaces				6
+#define ERROR_ConvertPatches			7
+#define ERROR_ConvertLamps				8
+#define ERROR_ConvertLampGlow			9
+#define ERROR_ConvertLightGlow		10
+#define ERROR_AssignTriangles			11
+#define ERROR_SortTriangles				12
+#define ERROR_ConvertTexLamp			13
+#define ERROR_SetupTransTextures	14
+#define ERROR_SortGroups					15
+
+
+///////////////////////////////////////////////////
+// error values for parsing key/values
+#define KEY_OK										0
+#define KEY_NONE									1
+#define KEY_ERROR									2
+
+//////////////////////////////////////////////////
+// entity types
+#define ENTITY_ERROR							0
+#define ENTITY_UNKNOWN						1
+#define ENTITY_WORLDSPAWN					2
+#define ENTITY_LIGHT							3
+
+///////////////////////////////////////////////////
+
+
+
+// The lump idexes that make up a bsp file.
+// Bzn bsp lump data is almost identical to q3a bsp lump data,  except that the
+// Effects, Lightmaps and VisData lumps are removed and there are two new lumps, one
+// describing the subzones and one describing the portals. The other difference is the lighting
+// entities (which are stored as normal entities in lump 0) since q3a bsps don't usually
+// store the light entities at all, and BZN has it's own unique set of lighting keys.
+
+const int Textures = 1;
+const int Planes = 2;
+const int Nodes = 3;
+const int Leafs = 4;
+const int LeafFaces = 5;
+const int LeafBrushes = 6;
+const int Brushes = 8;
+const int BrushSides = 9;
+const int Vertices = 10;
+const int MeshVerts = 11;
+const int Effects = 12;				// removed from bzn
+const int Faces = 13;
+const int LightMaps = 14;			// removed from bzn
+const int VisData = 16;				// removed from bzn
+const int SubZoneData = 17 ;	// added to bzn: overlapping subzones form zones. Zones are like groups of axial boxes.
+const int PortalData = 18 ;		// added to bzn: portals overlapping different zones allow them to see each other.
+const int MAX_LUMP = 19 ;
+
+const int MAX_LUMP_SIZE = 100000000 ; // this value is very large and arbitrary, 100 megabytes.  Just make sure MAX_LUMP_SIZE * MAX_LUMP is less than the max size for a size_t or parseMap error checking won't work.
+
+enum {POLYGON = 1, PATCH, MESH, BILLBOARD};
+
+typedef struct{
+  int iOffset;
+  int iLength;
+} direntry_t;
+
+#define Q3NAMESIZE	64
+typedef struct {
+  char name[Q3NAMESIZE];
+  int flags;
+  int contents;
+} Q3BspTexture;
+
+typedef struct{
+  unsigned char magic[4];
+  int version;
+  direntry_t Lumps[MAX_LUMP];
+} Q3BspHeader_t;
+
+typedef struct{
+  int texture;
+  int effect;
+  int type;
+  int vertex;
+  int n_vertexes;
+  int meshvert;
+  int n_meshverts;
+  int lm_index;
+  int lm_start[2];
+  int lm_size[2];
+  float lm_origin[3];
+  float lm_vecs[2][3];
+  float normal[3];
+  int size[2];
+} Q3BspFace_t;
+
+typedef struct{
+  float position[3];
+  float texcoord[2][2];
+  float normal[3];
+  unsigned char color[4];
+} Q3BspVertex;
+
+typedef struct {
+  int cluster;
+  int area;
+  int mins[3];
+  int maxs[3];
+  int leafface;
+  int n_leaffaces;
+  int leafbrush;
+  int n_leafbrushes;
+} Q3BspLeaf;
+
+typedef struct {
+  float normal[3];
+  float dist;
+} Q3BspPlane;
+
+typedef struct {
+  int plane;
+  int children[2];
+  int mins[3];
+  int maxs[3];
+} Q3BspNode;
+
+typedef struct {
+  int brushside;
+  int n_brushsides;
+  int texture;
+} Q3BspBrush;
+
+typedef struct {
+  int plane;
+  int texture;
+} Q3BspBrushSide;
+
+typedef struct {
+  unsigned char lightmap[128][128][3];
+} Q3BspLightMap;
+
+typedef struct {
+  int n_vecs;
+  int sz_vecs;
+  unsigned char *vecs;
+} Q3BspVisData;
+
+typedef struct {
+  int size;
+  Bezier *bezier;
+} Q3BspPatch;
+
+
+// this struct must be the same as in BZNq3map2
+typedef struct
+{
+	int Zone ;
+	float Min[3] ;
+	float Max[3] ;
+}
+BZN_SubZone_t;
+
+// this struct must be the same as in BZNq3map2
+typedef struct
+{
+	float Min[3] ;
+	float Max[3] ;
+}
+BZN_Portal_t;
+
+
+typedef struct{
+	int texture;    
+	int type;
+	int vertex;
+	int n_vertexes;
+	int meshvert;
+	int n_meshverts;
+	int n_triangles;
+	int lm_index;    
+	float normal[3]; 
+	int zone ; // added by Jared 
+	Q3BspPatch *patch;
+} Q3BspFaceRenderer;
+
+//////////////////////////////////////
+typedef struct {
+	int Zone ; // this gets set to -1 for triangles in no zone, and they don't get added to the manualobjects
+	int Texture ;
+	//int Lightmap ; // bzn doesn't use lightmaps
+	int VIndex[3] ;
+	int Lamp ; // which lamp this triangle came from, -1 for most triangles.
+
+	int Group ; // triangles from the same face are the same group, triangles from the same patch are the same group.  Most stuff is group 0 unless it needs special attention, such as transparent stuff
+} triangle_t;
+
+typedef struct {
+	unsigned int Flags ;
+	float	Position[3] ;
+	float Direction[3] ;
+	float Colour[3] ;
+	float Min[3] ;
+	float Max[3] ;
+	float	Cutoff ;
+	float	Angle ;
+	float Brightness ;
+	int Texture ; // texture index of light
+	unsigned int ZoneCount ; // how many zones this light touches
+	unsigned int CentreZone ; // which zone contains the centre of this light.  even if on a boundary, the light will only choose one zone as its centre.
+	short SubLightStart ; // where this light's sublights start in the sublight list.
+} light_t ;
+
+typedef struct {
+	unsigned int Flags ;
+	float	Position[3] ;
+	float Colour[3] ;
+	float Min[3] ;
+	float Max[3] ;
+	float Brightness ;
+	int Texture ; // texture index of light
+	int LightNode ; // -1 if not a node, 0 - 3 if a node.  LightNodes are used as the data for TexLamps, freeform geometry that gets converted to deferred shading lamp triangles.
+	unsigned short Zone[MAX_ZONEPERLIGHT+1] ; // the zones this lamp touches
+} lamp_t ;
+
+typedef struct
+{
+	float Min[3] ;
+	float Max[3] ;
+}
+minmax_t;
+
+// this used for the light culling algo, where each cutup light is treated as a subzone.
+typedef struct
+{
+	unsigned short Light ;
+	unsigned short Zone ;
+	float Min[3] ;
+	float Max[3] ;
+}
+sublight_t;
+
+
+class Q3Map
+{
+public:
+  Q3Map();
+  ~Q3Map();
+
+	int m_nNewCount ;
+
+	int m_nDebugA ;
+
+//	CJarDebug Q3Bug ; //!! just for debugging, remove from final build
+	char m_chBug[10240] ;
+
+	int ParseAndTriangulateMap(const char* pData, size_t Size) ;
+	void FreeParseMem(void) ;
+
+	int parseMap(const char* pMem, size_t Size);
+
+  int findVisibleFaces(const QVECTOR *camPos, int *facesToRender);
+  Q3BspFace_t *getFaces(void);
+  Q3BspPatch *handlePatch(int faceIndex);
+
+	char m_chSpotlightTexture[MAX_PROJECTORTEX][Q3NAMESIZE] ;
+	int m_nMaxSpotlightTexture ;
+	int AddSpolightTexture(char TEXNAME[]) ;
+
+private:
+
+  
+  
+  
+
+  	
+
+  int findLeaf(const QVECTOR *camPos) const;
+  bool isClusterVisible(int visCluster, int testCluster) const;	
+
+  int *mVisibleFaces;
+
+  std::vector<Q3BspVisData> *patches;
+
+public:
+  // map data
+
+  Q3BspHeader_t m_BspHeader;
+
+  char *m_pEntities;
+
+  int m_iNumTexs;
+  Q3BspTexture *m_pTexturesOrig;
+
+  int m_iNumFaces;
+  Q3BspFace_t *m_pFaces; 
+
+  int m_iNumVertices;
+  Q3BspVertex *m_pVertices;
+
+  int m_iNumMeshVerts;	
+  int *m_pMeshVerts;
+
+  int m_iNumLeafs;
+  Q3BspLeaf *m_pLeafs;
+
+  int m_iNumLeafFaces;
+  int *m_pLeafFaces;
+
+  int m_iNumPlanes;
+  Q3BspPlane *m_pPlanes;
+
+  int m_iNumNodes;
+  Q3BspNode *m_pNodes;
+
+  int m_iNumLeafBrushes;
+  int *m_pLeafBrushes;
+
+  int m_iNumBrushes;
+  Q3BspBrush *m_pBrushes;
+
+  int m_iNumBrushSides;
+  Q3BspBrushSide *m_pBrushSides;
+
+  int m_iNumLightMaps;
+  Q3BspLightMap *m_pLightMaps;
+
+  Q3BspVisData *m_VisData;
+
+	int m_iNumSubZones ;
+  BZN_SubZone_t *m_pSubZones;
+
+	int m_iNumPortals ;
+  BZN_Portal_t *m_pPortals;
+
+
+
+
+  int m_ClusterCount ;
+
+	void swizzleCoords(void);
+  void swizzleFloat3(float t[3]);
+  void swizzleInt3(int t[3]);
+
+	/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+	// memory management for the triangles
+	int AllocateTriangleMemory(void) ;
+	void FreeTriangleMemory(void) ;
+	int ExpandTriangleMemory(void) ;
+	int AddTriangle(triangle_t Triangle) ;
+	int m_nTriangleSize ; // starting memory size
+	void* m_pTriangleMem ; // allocate starting memory space
+	triangle_t* m_pTriangle ; // a pointer to the memory cast as a triangle_t
+	int m_nTriangleMax ;
+	int m_nTriangleLimit ;
+
+	// memory management for the vertices
+	int AllocateVertexMemory(int nVertNum) ;
+	void FreeVertexMemory(void) ;
+	int ExpandVertexMemory(void) ;
+	int AddVertex(Q3BspVertex Vertex) ;
+	int m_nVertexSize ; // starting memory size
+	void* m_pVertexMem ; // allocate starting memory space
+	Q3BspVertex* m_pVertex ; // a pointer to the memory cast as a Q3BspVertex
+	int m_nVertexMax ;
+	int m_nVertexLimit ;
+
+	// memory management for the lights
+	int AllocateLightMemory(void) ;
+	void FreeLightMemory(void) ;
+	int ExpandLightMemory(void) ;
+	int AddLight(light_t Light) ;
+	int m_nLightSize ; // starting memory size
+	void* m_pLightMem ; // allocate starting memory space
+	light_t* m_pLight ; // a pointer to the memory cast as a light_t
+	int m_nLightMax ;
+	int m_nLightLimit ;
+
+	// memory management for the lamps (a lamp is a deferred shading non-shadowing point light)
+	int AllocateLampMemory(void) ;
+	void FreeLampMemory(void) ;
+	int ExpandLampMemory(void) ;
+	int AddLamp(lamp_t Lamp) ;
+	int m_nLampSize ; // starting memory size
+	void* m_pLampMem ; // allocate starting memory space
+	lamp_t* m_pLamp ; // a pointer to the memory cast as a lamp_t
+	int m_nLampMax ;
+	int m_nLampLimit ;
+
+	// memory management for the textures
+	int AllocateTextureMemory(void) ;
+	void FreeTextureMemory(void) ;
+	int ExpandTextureMemory(void) ;
+	int AddTexture(Q3BspTexture Texture) ;
+	int AddTextureUnique(Q3BspTexture Texture) ; // special version of the Add function, will not add if the texture name already exist.  returns texture index, or -1 on fail
+	int m_nTextureSize ; // starting memory size
+	void* m_pTextureMem ; // allocate starting memory space
+	Q3BspTexture* m_pTexture ; // a pointer to the memory cast as a Texture_t
+	int m_nTextureMax ;
+	int m_nTextureLimit ;
+
+	// memory management for the TexLamp Triangles
+	int AllocateTexLampMemory(void) ;
+	void FreeTexLampMemory(void) ;
+	int ExpandTexLampMemory(void) ;
+	int AddTexLamp(int TexLamp) ;
+	int m_nTexLampSize ; // starting memory size
+	void* m_pTexLampMem ; // allocate starting memory space
+	int* m_pTexLamp ; // a pointer to the memory cast as an int
+	int m_nTexLampMax ;
+	int m_nTexLampLimit ;
+
+	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+	int ParseEntities(void) ;
+	int NextEntity(int* pPos, int nMaxPos) ;
+	int GetEntityType(int nPos, int nMaxPos) ;
+	int GetEntityKeyAndValue(int* pPos, int nMaxPos, char* pKey, char* pValue) ;
+	int GetNumbersFromValue(char* pValue, float *pNumber, int nNumberSize) ;
+	int GetNumbersFromValue(char* pValue, int *pNumber, int nNumberSize) ;
+	int ParseAndAddLight(int* pPos, int nMaxPos) ;
+
+	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+	Q3BspFaceRenderer *m_BspFaces;	
+	int m_NumBspFaces;
+	void DestroyBspFacesMemory(void) ;
+	int initFaces(void) ;
+
+	// we keep track of common texture indexes to avoid slow calls to AddTextureUnique when we add default light texture indexes
+	int m_nDefaultTextureIndexLamp ;
+	int m_nDefaultTextureIndexLamp2Pass ;
+	int m_nDefaultTextureIndexGlowLamp ;
+	int m_nDefaultTextureIndexGlowLight ;
+	int m_nDefaultTextureIndexSpotlight ;
+
+	void GetTexLampTextureNumbers() ;
+	int m_nBZN_LightNode0 ;
+	int m_nBZN_LightNode1 ;
+	int m_nBZN_LightNode2 ;
+	int m_nBZN_LightNode3 ;
+
+	int m_nGroup ;
+
+	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+	//
+	// Much easier to keep track of how zones/subzones/portals/lights connect to each other using hardwired arrays
+	// than dynamically allocated memory, even if it does waste a little space and impose some design limits.
+	// It's fast, easy to manage, and the total memory used is reasonably small.  
+	// The design limits are generous enough that it's unlikely maps will get anywhere near hitting them.
+	//
+	// Memory Footprints:
+	//
+	// m_nZone:											 4096 * (16+1) * sizeof(unsigned short) =	  139,264
+	// m_ZoneBoundary								 4096 *					 sizeof(minmax_t)24			=	   98,304
+	// m_nZoneTouchesPortal					 4096 * (64+1) * sizeof(unsigned short) =	  532,480
+	// m_nPortalTouchesZone					32768 * ( 8+1) * sizeof(unsigned short) =	  589,824
+	//
+	// m_nZoneContainsLightCentre		 4096 * (64+1) * sizeof(unsigned short) =	  532,480 
+	// m_nLightTouchesZone					32768 * ( 8+1) * sizeof(unsigned short) =	  589,824
+	// m_nZoneTouchesSubLight				 4096 * (64+1) * sizeof(unsigned short) =	  532,480
+	//
+	// m_nMultiZoneLight						32768 *					 sizeof(unsigned short)	=		 65,536	
+	// m_SubLight										32768 * 8			 * sizeof(sublight_t)28		=	6,815,744
+	// m_nZoneTouchesZone						 4096 * (64+1) * sizeof(unsigned short) =	  532,480
+	//
+	//																																TOTAL = 10,428,416 (9.9 meg)
+
+	unsigned short m_nZone[MAX_ZONE][MAX_SUBZONEPERZONE+1] ;									// last index is used as a counter, INDEX_SUBZONECOUNT
+	minmax_t m_ZoneBoundary[MAX_ZONE] ; // min and max of a zone.  Since zones might not be square, NEVER use this as the real zone boundary, work it out using the subzones instead.  This is just for cutting up lights.
+	
+	unsigned short m_nZoneTouchesPortal[MAX_ZONE][MAX_PORTALPERZONE+1] ;			// last index is used as a counter, INDEX_PORTALCOUNT
+	unsigned short m_nPortalTouchesZone[MAX_PORTAL][MAX_ZONEPERPORTAL+1] ;		// last index is used as a counter, INDEX_PORTALZONECOUNT
+	
+	// PortalTouchesLight is true if the light AABB touches the portal.
+	unsigned short m_nPortalTouchesLight[MAX_PORTAL][MAX_LIGHTPERPORTAL+1] ;	// last index is used as a counter, INDEX_PORTALLIGHTCOUNT
+
+	unsigned short m_nZoneContainsLightCentre[MAX_ZONE][MAX_LIGHTPERZONE+1] ;				// last index is used as a counter, INDEX_LIGHTCOUNT
+	unsigned short m_nLightTouchesZone[MAX_LIGHT][MAX_ZONEPERLIGHT+1] ;				// last index is used as a counter, INDEX_LIGHTZONECOUNT
+	unsigned short m_nZoneTouchesSubLight[MAX_ZONE][MAX_LIGHTPERZONE+1] ;				// last index is used as a counter, INDEX_LIGHTCOUNT
+
+	unsigned short m_nZoneTouchesZone[MAX_ZONE][MAX_ZONEPERZONE+1] ;				// last index is used as a counter, INDEX_ZONEPERZONECOUNT
+
+	// list of lights that touche more than one zone.  These require more complex visibility checks
+	unsigned short m_nMultiZoneLight[MAX_LIGHT] ; 
+	int m_nMaxMultiZoneLight ;
+	
+	sublight_t m_SubLight[MAX_SUBLIGHT] ;
+	int m_nSubLightMax ;
+	 
+
+	//
+	/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+	int m_nMaxZone ;
+
+	int ConvertFacesToTriangles(void) ;
+	int ConvertPatchesToTriangles(void) ;
+	int ConvertTexLampsToLampTriangles(void) ;
+	int ConvertLampsToTriangles(void) ;
+	int ConvertLampsToGlowTriangles(void) ;
+	int ConvertLightsToGlowTriangles(void) ;
+
+
+	void SetVertex(Q3BspVertex *pVert, float flXPos, float flYPos, float flZPos) ;
+	bool VectorsAreEqual(Q3BspVertex* pVecA, Q3BspVertex* pVecB) ;
+	Q3BspVertex NormalizedCrossProduct(Q3BspVertex VertA, Q3BspVertex VertB, Q3BspVertex VertC) ;
+	float VertexDistance(Q3BspVertex* VertA, Q3BspVertex* VertB) ;
+	void VertexScale(Q3BspVertex* pVert, float flScale) ;
+	Q3BspVertex GetNormalised(Q3BspVertex* pVector) ;
+	Q3BspVertex VectorAdd(Q3BspVertex* pVecA, Q3BspVertex* pVecB) ;
+	Q3BspVertex VectorSubtract(Q3BspVertex* pVecA, Q3BspVertex* pVecB) ;
+	Q3BspVertex VectorMultiply(Q3BspVertex* pVecA, Q3BspVertex* pVecB) ;
+
+	void SetupZones(void) ;
+	int GetNextSubZone(float *flPoint, int nStart, int nMax) ;
+
+	bool PointInSubZone(float *flPoint, int nSubZone) ;
+	bool PointInZone(float *flPos, int nZone) ;
+
+	bool AABBTouchesSubZone(float *flPointMin, float *flPointMax, int nSubZone) ;
+	bool AABBTouchesZone(float *flPosMin, float *flPosMax, int nZone) ;
+
+	int AssignTrianglesToZones(void) ;
+	int FindTriangleZone(int nTriangle) ;
+	int SetupTriangleZone(int nTriangle) ;
+	int SplitTriangle(int nTriangle, int nAxis, float flCutPos) ; // only does axial cuts.  returns false on failure, probably due to lack of memory.
+	void CreateTweenVert(Q3BspVertex* pVertA, Q3BspVertex* pVertB, float flPercent0, Q3BspVertex* pVertAB) ;
+	
+
+	int SortTrianglesIntoGroups(void) ;
+	static int compareGroups( const void *arg1, const void *arg2 ) ;
+
+	int SortTrianglesIntoBatches(void) ;
+	static int compareTriangles( const void *arg1, const void *arg2 ) ;
+
+	int SetupTransTextures(void) ;
+	int* m_pTransTexture ;
+
+
+	int AssignPortalsToZones(void) ;
+	int AssignLightsToPortals(void) ;
+	int AssignLightsToZones(void) ;
+	int AssignZonesToZones(void) ;
+
+};
+
+#endif /* _Q3MAP_H */
Index: code/branches/presentation3/src/libraries/tools/bsp/Q3Map_Bezier.cpp
===================================================================
--- code/branches/presentation3/src/libraries/tools/bsp/Q3Map_Bezier.cpp	(revision 7073)
+++ code/branches/presentation3/src/libraries/tools/bsp/Q3Map_Bezier.cpp	(revision 7073)
@@ -0,0 +1,131 @@
+/*
+===========================================================================
+Copyright (C) 2008 Daniel Örstadius
+
+This file is part of bsp-renderer source code.
+
+bsp-renderer is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+bsp-renderer 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with bsp-renderer.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+// Bezier.cpp -- tessellates the Bezier patches
+
+/* The tessellation code is taken from the document 
+"Rendering Quake 3 Maps" at 
+http://graphics.cs.brown.edu/games/quake/quake3.html
+The author states that it is based on code from 
+Paul Baker's Octagon project,
+http://www.paulsprojects.net/opengl/octagon/octagon.html
+which is released under the New BSD license.
+Please see the licenses folder.
+*/
+
+#include "Q3Map_Bezier.h"
+#include "Q3Map_misc.h"
+
+
+Bezier::Bezier()
+{	
+  mVertex = 0;
+  mIndex = 0;
+  mTrianglesPerRow = 0;
+  mRowIndex = 0;
+}
+
+Bezier::~Bezier()
+{
+  DELETE_ARRAY(mVertex);
+  DELETE_ARRAY(mIndex);
+  DELETE_ARRAY(mTrianglesPerRow);
+  DELETE_ARRAY(mRowIndex);
+}
+
+
+
+
+
+void Bezier::tessellate(int L) 
+{
+  // The number of vertices along a side is 1 + num edges
+  const int L1 = L + 1;
+
+  mVertex = new BspVertex[L1*L1];
+  mNumVertex = L1*L1;
+
+  // Compute the vertices    
+  for (int i = 0; i <= L; ++i)
+  {
+    float a = (float)i / L;
+    float b = 1.0f - a;
+
+    mVertex[i] =
+      mControls[0] * (b * b) + 
+      mControls[3] * (2 * b * a) +
+      mControls[6] * (a * a);
+  }
+
+  for (int i = 1; i <= L; i++) 
+  {
+    float a = (float)i / L;
+    float b = 1.0f - a;
+
+    BspVertex temp[3];
+
+    for (int j = 0; j < 3; j++)
+    {
+      int k = 3 * j;
+      temp[j] =
+        mControls[k + 0] * (b * b) + 
+        mControls[k + 1] * (2 * b * a) +
+        mControls[k + 2] * (a * a);
+    }
+
+    for(int j = 0; j <= L; ++j) 
+    {
+      float a = (float)j / L;
+      float b = 1.0f - a;
+
+      mVertex[i * L1 + j]=
+        temp[0] * (b * b) + 
+        temp[1] * (2 * b * a) +
+        temp[2] * (a * a);
+    }
+  }
+
+  // Compute the indices     
+  mIndex = new unsigned int[L * (L + 1) * 2];
+  mNumIndex = L * (L + 1) * 2;
+
+  for (int row = 0; row < L; ++row) 
+  {
+    for(int col = 0; col <= L; ++col)	
+    {
+      mIndex[(row * (L + 1) + col) * 2 + 1] = row       * L1 + col;
+      mIndex[(row * (L + 1) + col) * 2]     = (row + 1) * L1 + col;
+    }
+  }
+
+  mTrianglesPerRow = new unsigned int[L];
+  mRowIndex = new unsigned int[L];
+
+  for (int row = 0; row < L; ++row) 
+  {
+    mTrianglesPerRow[row] = 2 * L1;
+    //rowIndexes[row]      = &indexes[row * 2 * L1];
+    mRowIndex[row]      = row * 2 * L1;
+  }
+
+  for (int i=0; i < L1*L1; i++)
+    mVertex[i].normalise();
+}
Index: code/branches/presentation3/src/libraries/tools/bsp/Q3Map_Bezier.h
===================================================================
--- code/branches/presentation3/src/libraries/tools/bsp/Q3Map_Bezier.h	(revision 7073)
+++ code/branches/presentation3/src/libraries/tools/bsp/Q3Map_Bezier.h	(revision 7073)
@@ -0,0 +1,48 @@
+/*
+===========================================================================
+Copyright (C) 2008 Daniel Örstadius
+
+This file is part of bsp-renderer source code.
+
+bsp-renderer is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+bsp-renderer 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with bsp-renderer.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+// Bezier.h -- class for the Bezier patches
+
+#ifndef _BEZIER_H
+#define _BEZIER_H
+
+#include "Q3Map_BspVertex.h"
+
+class Bezier 
+{
+public:
+  Bezier();
+  ~Bezier();
+  
+  void tessellate(int level);
+
+  BspVertex mControls[9];
+  BspVertex *mVertex;
+  unsigned int *mIndex;
+  unsigned int *mTrianglesPerRow;
+  unsigned int *mRowIndex;
+  unsigned int mNumIndex;
+  unsigned int mNumVertex;
+  int mBaseBufferindex;
+  int mBaseVertexIndex;
+};
+
+#endif
Index: code/branches/presentation3/src/libraries/tools/bsp/Q3Map_BspVertex.cpp
===================================================================
--- code/branches/presentation3/src/libraries/tools/bsp/Q3Map_BspVertex.cpp	(revision 7073)
+++ code/branches/presentation3/src/libraries/tools/bsp/Q3Map_BspVertex.cpp	(revision 7073)
@@ -0,0 +1,106 @@
+/*
+===========================================================================
+Copyright (C) 2008 Daniel Örstadius
+
+This file is part of bsp-renderer source code.
+
+bsp-renderer is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+bsp-renderer 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with bsp-renderer.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+// BspVertex.cpp -- used in the tessellation of the Bezier patches
+
+
+#include <math.h>
+#include "Q3Map_BspVertex.h"
+
+
+BspVertex::BspVertex()
+{
+  for (int i=0; i<3; i++)
+    mPosition[i] = 0;
+
+  for (int i=0; i<3; i++)
+    mNormal[i] = 0;
+
+  for(int i=0; i<2; i++)
+    for(int j=0; j<2; j++)
+      mTexcoord[i][j] = 0;
+}
+
+BspVertex::BspVertex(float p[3], float texcoord[2][2], float n[3])
+{
+  for (int i=0; i<3; i++)
+    mPosition[i] = p[i];
+
+  for (int i=0; i<3; i++)
+    mNormal[i] = n[i];
+
+  for(int i=0; i<2; i++)
+    for(int j=0; j<2; j++)
+    {		
+      this->mTexcoord[i][j] = texcoord[i][j];
+    }
+}
+
+BspVertex BspVertex::operator+(BspVertex a)
+{
+  BspVertex res;
+
+  for (int i=0; i<3; i++)
+  {
+    res.mPosition[i] = this->mPosition[i] + a.mPosition[i];
+    res.mNormal[i] = this->mNormal[i] + a.mNormal[i];
+  }
+
+  for(int i=0; i<2; i++)
+    for(int j=0; j<2; j++)
+    {			
+      res.mTexcoord[i][j] = this->mTexcoord[i][j] + a.mTexcoord[i][j];
+    }
+
+    return res;
+}
+
+BspVertex BspVertex::operator*(float a)
+{
+  BspVertex res;
+
+  for (int i=0; i<3; i++)
+  {
+    res.mPosition[i] = this->mPosition[i] * a;
+    res.mNormal[i] = this->mNormal[i] * a;
+  }
+
+  for(int i=0; i<2; i++)
+    for(int j=0; j<2; j++)
+      res.mTexcoord[i][j] = this->mTexcoord[i][j] * a;
+
+  return res;
+}
+
+void BspVertex::normalise(void)
+{
+  //QVECTOR n((float)mNormal[0], (float)mNormal[1], (float)mNormal[2]);
+
+  //D3DXVec3Normalize(&n, &n);
+  //mNormal[0] = n.x;
+  //mNormal[1] = n.y;
+  //mNormal[2] = n.z;
+
+	float flLen=sqrt( mNormal[0]*mNormal[0] + mNormal[1]*mNormal[1] + mNormal[2]*mNormal[2] ) ;
+	mNormal[0]/=flLen ;
+	mNormal[1]/=flLen ;
+	mNormal[2]/=flLen ;
+}
Index: code/branches/presentation3/src/libraries/tools/bsp/Q3Map_BspVertex.h
===================================================================
--- code/branches/presentation3/src/libraries/tools/bsp/Q3Map_BspVertex.h	(revision 7073)
+++ code/branches/presentation3/src/libraries/tools/bsp/Q3Map_BspVertex.h	(revision 7073)
@@ -0,0 +1,44 @@
+/*
+===========================================================================
+Copyright (C) 2008 Daniel Örstadius
+
+This file is part of bsp-renderer source code.
+
+bsp-renderer is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+bsp-renderer 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with bsp-renderer.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+// BspVertex.h -- used in the tessellation of the Bezier patches
+
+#ifndef _BSPVERTEX_H
+#define _BSPVERTEX_H
+
+class BspVertex
+{
+public:
+  BspVertex();
+  BspVertex(float p[3], float texcoord[2][2], float n[3]);
+
+  float mPosition[3];
+  float mTexcoord[2][2];
+  float mNormal[3];
+
+  BspVertex operator+(BspVertex a);
+  BspVertex operator*(float a);
+
+  void normalise(void);
+
+};
+
+#endif 
Index: code/branches/presentation3/src/libraries/tools/bsp/Q3Map_misc.h
===================================================================
--- code/branches/presentation3/src/libraries/tools/bsp/Q3Map_misc.h	(revision 7073)
+++ code/branches/presentation3/src/libraries/tools/bsp/Q3Map_misc.h	(revision 7073)
@@ -0,0 +1,33 @@
+/*
+===========================================================================
+Copyright (C) 2008 Daniel Örstadius
+Copyright (C) 2009 Jared Prince
+
+This file is part of bsp-renderer source code.
+
+bsp-renderer is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+bsp-renderer 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with bsp-renderer.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+// misc.h -- various definitions and functions
+
+#ifndef _MISC_H
+#define _MISC_H
+
+#define DELETE_POINTER(x) if(x) { delete x		; x=0 ; }
+#define DELETE_ARRAY(x)		if(x) { delete [] x ; x=0 ; }
+
+typedef float QVECTOR[3];
+
+#endif /* _MISC_H */
