Index: GTP/trunk/App/Demos/Vis/KdTreeDemo/OGRE/include/TestKdTree.h
===================================================================
--- GTP/trunk/App/Demos/Vis/KdTreeDemo/OGRE/include/TestKdTree.h	(revision 1187)
+++ GTP/trunk/App/Demos/Vis/KdTreeDemo/OGRE/include/TestKdTree.h	(revision 1195)
@@ -6,4 +6,20 @@
 #include <ivreader.h>
 #include "TestKdTreeAppListener.h"
+
+
+class KdTreeAppRenderTargetListener : public RenderTargetListener
+{
+public:
+	KdTreeAppRenderTargetListener(SceneManager *sceneMgr);
+
+protected:
+	void preViewportUpdate (const RenderTargetViewportEvent &evt);
+	void postRenderTargetUpdate (const RenderTargetEvent &evt);
+
+	SceneManager *mSceneMgr;
+
+	ShadowTechnique mSavedShadowTechnique;
+	ColourValue mSavedAmbientLight;
+};
 
 class KdTreeApp : public ExampleApplication
@@ -15,4 +31,5 @@
 	~KdTreeApp()
 	{
+		delete mRenderTargerListener;
 		delete mFrameListener;
 	}
@@ -24,4 +41,5 @@
 	Camera *mFollowCam;
 	KdTreeAppListener *mFrameListener;
+	KdTreeAppRenderTargetListener *mRenderTargerListener;
 
 	String  mSceneFiles;
@@ -69,18 +87,3 @@
 };
 
-class KdTreeAppRenderTargetListener : public RenderTargetListener
-{
-public:
-	KdTreeAppRenderTargetListener(SceneManager *sceneMgr);
-
-protected:
-	void preViewportUpdate (const RenderTargetViewportEvent &evt);
-	void postRenderTargetUpdate (const RenderTargetEvent &evt);
-
-	SceneManager *mSceneMgr;
-
-	ShadowTechnique mSavedShadowTechnique;
-	ColourValue mSavedAmbientLight;
-};
-
 #endif
Index: GTP/trunk/App/Demos/Vis/KdTreeDemo/OGRE/include/TestKdTreeAppListener.h
===================================================================
--- GTP/trunk/App/Demos/Vis/KdTreeDemo/OGRE/include/TestKdTreeAppListener.h	(revision 1187)
+++ GTP/trunk/App/Demos/Vis/KdTreeDemo/OGRE/include/TestKdTreeAppListener.h	(revision 1195)
@@ -55,4 +55,5 @@
 	bool mShowTree;
 	bool mFreeMove;
+	bool mTopCamFollow;
 
 	Real mDelayedQueriesIssued;
@@ -71,4 +72,5 @@
 	OverlayElement *mRenderMethodInfo;
 	OverlayElement *mMovementInfo;
+	OverlayElement *mTopCamInfo;
 
 	OverlayElement *mFrustumCulledNodesInfo;
@@ -107,5 +109,36 @@
     void updateStats(void);
 
+	// stuff for walkthrough recording/playback
+	struct FrameInfo
+	{
+		FrameInfo(Vector3 pos, Quaternion or, Real time):
+		mPosition(pos), mOrientation(or), mElapsedTime(time)
+		{ }
+
+		Vector3 mPosition;
+		Quaternion mOrientation;
+		Real	mElapsedTime;
+	};
+
+	typedef std::list<FrameInfo> FrameList;
+
+	enum AppState
+	{
+		AS_NORMAL,
+		AS_RECORD,
+		AS_PLAYBACK
+	};
+
+	FrameList mFrameList;
+
+	AppState mAppState;
+
+	void toggleRecord();
+	void togglePlayback();
+	void saveFrameInfo(Real elapsedTime);
+
 public:
+	void loadFrames(const String& filename);
+	void saveFrames(const String& filename);
 
 	// visualization modes for scene nodes
Index: GTP/trunk/App/Demos/Vis/KdTreeDemo/OGRE/src/TestKdTree.cpp
===================================================================
--- GTP/trunk/App/Demos/Vis/KdTreeDemo/OGRE/src/TestKdTree.cpp	(revision 1190)
+++ GTP/trunk/App/Demos/Vis/KdTreeDemo/OGRE/src/TestKdTree.cpp	(revision 1195)
@@ -246,5 +246,5 @@
 			light->setDirection(-1,-1,-1);
 		}
-		light->setDiffuseColour(ColourValue());
+		light->setDiffuseColour(ColourValue::White);
 		light->setSpecularColour(ColourValue(1, 1, 0.2));
 
@@ -276,5 +276,6 @@
 	mFrameListener->showDebugOverlay( true );
 	mRoot->addFrameListener(mFrameListener);
-	mWindow->addListener(new KdTreeAppRenderTargetListener(mSceneMgr));
+	mRenderTargerListener = new KdTreeAppRenderTargetListener(mSceneMgr);
+	mWindow->addListener(mRenderTargerListener);
 }
 
@@ -312,5 +313,5 @@
 	mTopCam->setDirection(0,0,-1);
 	mTopCam->pitch(Radian(-Math::HALF_PI));
-	mTopCam->setCullingFrustum(mCamera);
+	//mTopCam->setCullingFrustum(mCamera);
 	mTopCam->setNearClipDistance(1);
 
Index: GTP/trunk/App/Demos/Vis/KdTreeDemo/OGRE/src/TestKdTreeAppListener.cpp
===================================================================
--- GTP/trunk/App/Demos/Vis/KdTreeDemo/OGRE/src/TestKdTreeAppListener.cpp	(revision 1192)
+++ GTP/trunk/App/Demos/Vis/KdTreeDemo/OGRE/src/TestKdTreeAppListener.cpp	(revision 1195)
@@ -1,2 +1,5 @@
+#include <fstream>
+#include <cstdlib>
+
 #include "TestKdTreeAppListener.h"
 
@@ -92,4 +95,5 @@
 	}
 
+	mTopCamFollow = true;
 	mFreeMove = false;
 	mShowTree = false;
@@ -97,4 +101,6 @@
 	mVizCamTransVect = Vector3::ZERO;
 	mSeqNum = 0;
+
+	mAppState = AS_NORMAL;
 
 	mDelayedQueriesIssued = 0.0;
@@ -127,4 +133,6 @@
 
 	showDebugOverlay(true);
+
+	loadFrames("demo-in.txt");
 }
 //-----------------------------------------------------------------------
@@ -139,4 +147,8 @@
 		PlatformManager::getSingleton().destroyInputReader( mInputDevice );
 	}
+
+	// save demo
+	if (!mFrameList.empty())
+		saveFrames("demo-out.txt");
 }
 //-----------------------------------------------------------------------
@@ -183,5 +195,5 @@
 	const int vert_space = 15;
 	const String na = ": N/A";
-	String sMD, sKT, sKI, sHL, sBM, sRM, sFM;
+	String sMD, sKT, sKI, sHL, sBM, sRM, sFM, sTC;
 
 	int top = border_height;
@@ -244,4 +256,10 @@
 		sFM = ": Ground";
 
+	sTC = na;
+	if (mTopCamFollow)
+		sTC = ": Follow";
+	else
+		sTC = ": Free";
+
 	initOverlayElement(&mRenderMethodInfo, ext, "RenderMethod", top, sRM); top += vert_space;
 	initOverlayElement(&mBuildMethodInfo, ext, "BuildMethod", top, sBM); top += vert_space;
@@ -251,4 +269,5 @@
 	initOverlayElement(&mKIInfo, ext, "KI", top, sKI); top += vert_space;
 	initOverlayElement(&mMovementInfo, ext, "Movement", top, sFM); top += vert_space;
+	initOverlayElement(&mTopCamInfo, ext, "TopCam", top, sTC); top += vert_space;
 
 	OverlayElement *visPanel = OverlayManager::getSingleton().
@@ -313,32 +332,46 @@
 bool KdTreeAppListener::processUnbufferedKeyInput(const FrameEvent& evt)
 {
+	// demo recording stuff
+	if (mInputDevice->isKeyDown(KC_F5) && mTimeUntilNextToggle <= 0)
+	{
+		toggleRecord();
+		mTimeUntilNextToggle = 0.5;
+	}
+
+	if (mInputDevice->isKeyDown(KC_F9) && mTimeUntilNextToggle <= 0)
+	{
+		togglePlayback();
+		mTimeUntilNextToggle = 0.5;
+	}
+
+	const static int vizCamSpeedup = 12;
 	// moving the cull camera
 	if (mInputDevice->isKeyDown(KC_NUMPAD2))
 	{
-		mVizCamTransVect.y = -mMoveScale;
+		mVizCamTransVect.y = -mMoveScale * vizCamSpeedup;
 	}
 	if (mInputDevice->isKeyDown(KC_NUMPAD8))
 	{
-		mVizCamTransVect.y = mMoveScale;
+		mVizCamTransVect.y = mMoveScale * vizCamSpeedup;
 	}
 	if (mInputDevice->isKeyDown(KC_NUMPAD4))
 	{
-		mVizCamTransVect.x = mMoveScale;
+		mVizCamTransVect.x = -mMoveScale * vizCamSpeedup;
 	}
 	if (mInputDevice->isKeyDown(KC_NUMPAD6))
 	{
-		mVizCamTransVect.x = -mMoveScale;
+		mVizCamTransVect.x = mMoveScale * vizCamSpeedup;
 	}
 	if (mInputDevice->isKeyDown(KC_SUBTRACT))
 	{
-		mVizCamTransVect.z = mMoveScale;
+		mVizCamTransVect.z = mMoveScale * vizCamSpeedup;
 	}
 	if (mInputDevice->isKeyDown(KC_ADD))
 	{
-		mVizCamTransVect.z = -mMoveScale;
+		mVizCamTransVect.z = -mMoveScale * vizCamSpeedup;
 	}
 
 	// stuff for the overlay
-	static String sNA = ": N/A";
+	const static String sNA = ": N/A";
 
 	static String bmOptions[] = { "Recursive", "PriorityQueue" };
@@ -504,8 +537,8 @@
 	}
 
-	if (mInputDevice->isKeyDown(KC_Q) && mTimeUntilNextToggle <= 0)
+	if (mInputDevice->isKeyDown(KC_U) && mTimeUntilNextToggle <= 0)
 	{
 		mFreeMove = !mFreeMove;
-		String move = "N/A";
+		String move = ": N/A";
 		if (mFreeMove)
 			move = ": Free";
@@ -518,4 +551,17 @@
 	}
 
+	if (mInputDevice->isKeyDown(KC_I) && mTimeUntilNextToggle <= 0)
+	{
+		mTopCamFollow = !mTopCamFollow;
+		String move = ": N/A";
+		if (mTopCamFollow)
+			move = ": Follow";
+		else
+			move = ": Free";
+
+		mTopCamInfo->setCaption(move);
+
+		mTimeUntilNextToggle = 0.5;
+	}
 
 	if (mInputDevice->isKeyDown(KC_BACK) && mTimeUntilNextToggle <= 0)
@@ -801,4 +847,13 @@
 	mCamNode->translate(mCamNode->getLocalAxes(), mTranslateVector);
 	mTopCam->moveRelative(mVizCamTransVect);
+	if (mTopCamFollow)
+	{
+		Vector3 toppos = mTopCam->getPosition();
+		Vector3 campos = mCamNode->getPosition();
+		mTopCam->setPosition(Vector3::ZERO);
+		mTopCam->setOrientation(Quaternion::IDENTITY);
+		mTopCam->setPosition(campos.x, toppos.y, campos.z);
+		mTopCam->pitch(Radian(-Math::HALF_PI));
+	}
 
 	if (!mFreeMove)
@@ -894,4 +949,12 @@
 
 	}
+
+
+	//saveFrameInfo(evt.timeSinceLastFrame);
+	if (mAppState == AS_RECORD)
+	{
+		mFrameList.push_back(FrameInfo(mCamNode->getPosition(), mCamNode->getOrientation(), evt.timeSinceLastFrame));
+	}
+
 
 	//Camera	*followCam = mSceneMgr->getCamera("FollowCam");
@@ -953,2 +1016,107 @@
 	}
 }
+
+/************************************************************************/
+/* functions for recording & playback of demos                          */
+/************************************************************************/
+
+//-----------------------------------------------------------------------------
+void KdTreeAppListener::toggleRecord()
+{
+	// TODO: overlay
+	if (mAppState == AS_NORMAL)
+	{
+		mAppState = AS_RECORD;
+	}
+	else if (mAppState == AS_RECORD)
+	{
+		mAppState = AS_NORMAL;
+	}
+}
+
+//-----------------------------------------------------------------------------
+void KdTreeAppListener::togglePlayback()
+{
+	// TODO: overlay
+	if (mAppState == AS_NORMAL)
+	{
+		mAppState = AS_PLAYBACK;
+	}
+	else if (mAppState == AS_PLAYBACK)
+	{
+		mAppState = AS_NORMAL;
+	}
+}
+
+//-----------------------------------------------------------------------------
+void KdTreeAppListener::saveFrameInfo(Real elapsedTime)
+{
+	mFrameList.push_back(FrameInfo(mCamNode->getPosition(), mCamNode->getOrientation(), elapsedTime));
+}
+
+//-----------------------------------------------------------------------------
+void KdTreeAppListener::saveFrames(const String& filename)
+{
+	// open file
+	std::ofstream dest(filename.c_str());
+	if (!dest)
+	{
+		LogManager::getSingleton().logMessage("Failed to open file for saving demo: " + filename);
+		return;
+	}
+
+	FrameList::iterator it = mFrameList.begin();
+	FrameList::iterator end = mFrameList.end();
+
+	// dump values
+	while (it != end)
+	{
+		dest << 
+			StringConverter::toString(it->mPosition) << " " << 
+			StringConverter::toString(it->mOrientation) << " " << 
+			StringConverter::toString(it->mElapsedTime) << "\n";
+		++ it;
+	}
+
+	dest.close();
+}
+
+//-----------------------------------------------------------------------------
+void KdTreeAppListener::loadFrames(const String& filename)
+{
+	// open file
+	std::ifstream src(filename.c_str());
+	if (!src)
+	{
+		LogManager::getSingleton().logMessage("Failed to open file for reading demo: " + filename);
+		return;
+	}
+
+	// clear the list
+	mFrameList.clear();
+
+	Vector3 pos;
+	Quaternion or;
+	Real time;
+
+	while (!src.eof())
+	{
+		src >> pos.x;	
+		src >> pos.y;	
+		src >> pos.z;
+
+		src >> or.w;
+		src >> or.x;
+		src >> or.y;
+		src >> or.z;
+
+		src >> time;
+
+		mFrameList.push_back(FrameInfo(pos, or, time));
+	}
+
+	// HACK pop last frame, was doubled while reading
+	mFrameList.pop_back();
+
+	src.close();
+}
