Commits

Steve Streeting  committed c70b742

Paging component update; flesh out the loading sequence, start adding tests

  • Participants
  • Parent commits 97b12e7

Comments (0)

Files changed (18)

File Components/Paging/CMakeLists.txt

 	include/OgrePagedWorldSection.h
 	include/OgrePageFileFormats.h
 	include/OgrePageManager.h
+	include/OgrePageRequestQueue.h
 	include/OgrePageStrategy.h
 	include/OgrePagingPrerequisites.h
 )
 	src/OgrePagedWorld.cpp
 	src/OgrePagedWorldSection.cpp
 	src/OgrePageManager.cpp
+	src/OgrePageRequestQueue.cpp
 	src/OgrePageStrategy.cpp
 )
 

File Components/Paging/include/OgreGrid2DPageStrategy.h

 		Real mLoadRadiusInCells;
 		Real mHoldRadiusInCells;
 
-		static const uint32 msChunkID;
-		static const uint16 msChunkVersion;
 	public:
+		static const uint32 CHUNK_ID;
+		static const uint16 CHUNK_VERSION;
+
 		Grid2DPageStrategyData();
 		~Grid2DPageStrategyData();
 
 		/// Get the Hold radius as a multiple of cells
 		virtual Real getHoldRadiusInCells(){ return mHoldRadiusInCells; }
 
-		/// Load this data from a stream
-		void load(StreamSerialiser& stream);
+		/// Load this data from a stream (returns true if successful)
+		bool load(StreamSerialiser& stream);
 		/// Save this data to a stream
 		void save(StreamSerialiser& stream);
 

File Components/Paging/include/OgrePage.h

 	*  @{
 	*/
 
+	/** Page class
+	*/
+	class Page : public PageAlloc
+	{
+	protected:
+		PageID mID;
+		PagedWorldSection* mParent;
+		unsigned long mFrameLastHeld;
+	public:
+		Page(PageID pageID);
+		virtual ~Page();
+
+		/// Get the ID of this page, unique withing the parent
+		virtual PageID getID() const { return mID; }
+		/// Get the PagedWorldSection this page belongs to, or zero if unattached
+		virtual PagedWorldSection* getParentSection() const { return mParent; }
+		/** Get the frame number in which this Page was last loaded or held.
+		@remarks
+			A Page that has not been requested to be loaded or held in the recent
+			past will be a candidate for removal.
+		*/
+		virtual unsigned long getFrameLastHeld() { return mFrameLastHeld; }
+		/// 'Touch' the page to let it know it's being used
+		virtual void touch();
+		/// Get whether or not this page is currently attached 
+		virtual bool isAttached() const { return mParent != 0; }
+
+
+		/// Internal method to notify a page that it is attached
+		virtual void _notifyAttached(PagedWorldSection* parent);
+
+	};
+
 	/** @} */
 	/** @} */
 }

File Components/Paging/include/OgrePageManager.h

 	*  Some details on paging component
 	*  @{
 	*/
+
+	/** Abstract class that can be implemented by the user application to 
+		provide a way to retrieve paging data from a source of their choosing.
+	*/
+	class PageStreamProvider : public PageAlloc
+	{
+	public:
+		PageStreamProvider() {}
+		virtual ~PageStreamProvider() {}
+
+		/** Get a serialiser set up to read PagedWorld data for the given world filename. 
+		@remarks
+		The StreamSerialiser returned is the responsibility of the caller to
+		delete. 
+		*/
+		virtual StreamSerialiser* readWorldStream(const String& filename) { return 0; }
+		/** Get a serialiser set up to write PagedWorld data for the given world filename. 
+		@remarks
+		The StreamSerialiser returned is the responsibility of the caller to
+		delete. 
+		*/
+		virtual StreamSerialiser* writeWorldStream(const String& filename) { return 0; }
+		/** Get a serialiser set up to read Page data for the given PageID, 
+			or null if this provider cannot supply one. 
+		@remarks
+			The StreamSerialiser returned is the responsibility of the caller to
+			delete. 
+		@param pageID The ID of the page being requested
+		@param section The parent section to which this page will belong
+		*/
+		virtual StreamSerialiser* readPageStream(PageID pageID, PagedWorldSection* section) { return 0; }
+
+		/** Get a serialiser set up to write Page data for the given PageID, 
+		or null if this provider cannot supply one. 
+		@remarks
+		The StreamSerialiser returned is the responsibility of the caller to
+		delete. 
+		@param pageID The ID of the page being requested
+		@param section The parent section to which this page will belong
+		*/
+		virtual StreamSerialiser* writePageStream(PageID pageID, PagedWorldSection* section) { return 0; }
+	};
 	
 	/** The PageManager is the entry point through which you load all PagedWorld instances, 
 		and the place where PageStrategy instances and factory classes are
 		registered to customise the paging behaviour.
 	*/
-	class _OgrePagingExport PageManager : public GeneralAllocatedObject
+	class _OgrePagingExport PageManager : public PageAlloc
 	{
 	public:
 		PageManager();
 		@param filename The filename to save the data to
 		@param arch The Archive that filename is relative to (optional)
 		*/
-		void saveWorld(PagedWorld* world, const String& filename, Archive* arch = 0);
+		void saveWorld(PagedWorld* world, const String& filename);
 		/** Save a PagedWorld instance to a file. 
 		@param world The world to be saved
 		@param stream The stream to save the data to
 		/** Get a reference to the registered strategies.
 		*/
 		const StrategyMap& getStrategies() const;
+		/// Get the request queue
+		PageRequestQueue* getQueue() const { return mQueue; }
+
+
+		/** Set the PageStreamProvider which can provide streams for any Page. 
+		@remarks
+			This is the top-level way that you can direct how Page data is loaded. 
+			When data for a Page is requested for a PagedWorldSection, the following
+			sequence of classes will be checked to see if they have a provider willing
+			to supply the stream: PagedWorldSection, PagedWorld, PageManager.
+			If none of these do, then the default behaviour is to look for a file
+			called worldname_sectionname_pageID.page. 
+		@note
+			The caller remains responsible for the destruction of the provider.
+		*/
+		void setPageStreamProvider(PageStreamProvider* provider) { mPageStreamProvider = provider; }
+		
+		/** Get the PageStreamProvider which can provide streams for any Page. */
+		PageStreamProvider* getPageStreamProvider() const { return mPageStreamProvider; }
+
+		/** Get a serialiser set up to read Page data for the given PageID. 
+		@param pageID The ID of the page being requested
+		@param section The parent section to which this page will belong
+		@remarks
+			The StreamSerialiser returned is the responsibility of the caller to
+			delete. 
+		*/
+		StreamSerialiser* _readPageStream(PageID pageID, PagedWorldSection* section);
+
+		/** Get a serialiser set up to write Page data for the given PageID. 
+		@param pageID The ID of the page being requested
+		@param section The parent section to which this page will belong
+		@remarks
+		The StreamSerialiser returned is the responsibility of the caller to
+		delete. 
+		*/
+		StreamSerialiser* _writePageStream(PageID pageID, PagedWorldSection* section);
+		/** Get a serialiser set up to read PagedWorld data for the given world name. 
+		@remarks
+			The StreamSerialiser returned is the responsibility of the caller to
+			delete. 
+		*/
+		StreamSerialiser* _readWorldStream(const String& filename);
+
+		/** Get a serialiser set up to write PagedWorld data. 
+		@remarks
+		The StreamSerialiser returned is the responsibility of the caller to
+		delete. 
+		*/
+		StreamSerialiser* _writeWorldStream(const String& filename);
+
+		/** Get the resource group that will be used to read/write files when the
+			default load routines are used. 
+		*/
+		const String& getPageResourceGroup() const { return mPageResourceGroup; }
+		/** Set the resource group that will be used to read/write files when the
+		default load routines are used. 
+		*/
+		void getPageResourceGroup(const String& g) { mPageResourceGroup = g; }
 	protected:
 		WorldMap mWorlds;
 		StrategyMap mStrategies;
 		NameGenerator mWorldNameGenerator;
-
+		PageRequestQueue* mQueue;
+		PageStreamProvider* mPageStreamProvider;
+		String mPageResourceGroup;
 	};
 
 	/** @} */

File Components/Paging/include/OgrePageRequestQueue.h

+/*
+-----------------------------------------------------------------------------
+This source file is part of OGRE
+(Object-oriented Graphics Rendering Engine)
+For the latest info, see http://www.ogre3d.org/
+
+Copyright (c) 2000-2009 Torus Knot Software Ltd
+Also see acknowledgements in Readme.html
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU Lesser General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA 02111-1307, USA, or go to
+http://www.gnu.org/copyleft/lesser.txt.
+
+You may alternatively use this source under the terms of a specific version of
+the OGRE Unrestricted License provided you have obtained such a license from
+Torus Knot Software Ltd.
+-----------------------------------------------------------------------------
+*/
+
+#ifndef __Ogre_PageRequestQueue_H__
+#define __Ogre_PageRequestQueue_H__
+
+#include "OgrePagingPrerequisites.h"
+#include "OgreString.h"
+#include "OgreCommon.h"
+
+namespace Ogre
+{
+	/** \addtogroup Optional Components
+	*  @{
+	*/
+	/** \addtogroup Paging
+	*  Some details on paging component
+	*  @{
+	*/
+
+	/** The PageRequestQueue is where pages are queued for loading and freeing.
+	*/
+	class _OgrePagingExport PageRequestQueue : public PageAlloc
+	{
+	public:
+		PageRequestQueue(PageManager* manager);
+		virtual ~PageRequestQueue();
+
+		/** Load a Page with a given ID, for a given PagedWorldSection.
+		@remarks
+			This method is called from the main rendering thread in all cases. 
+			If threading is enabled, the request is queued and processed by 
+			a separate thread. At some point later on the loaded page will be attached
+			to the section which requested it. 
+		*/
+		void loadPage(PageID pageID, PagedWorldSection* section);
+		/** Dispose of a page */
+		void unloadPage(Page* page);
+
+
+
+	protected:
+		PageManager* mManager;
+	};
+
+	/** @} */
+	/** @} */
+}
+
+
+
+
+
+#endif 

File Components/Paging/include/OgrePageStrategy.h

 		PageStrategyData() {}
 		virtual ~PageStrategyData() {}
 
-		/// Load this data from a stream
-		virtual void load(StreamSerialiser& stream) = 0;
+		/// Load this data from a stream (returns true if successful)
+		virtual bool load(StreamSerialiser& stream) = 0;
 		/// Save this data to a stream
 		virtual void save(StreamSerialiser& stream) = 0;
 

File Components/Paging/include/OgrePagedWorld.h

 	protected:
 		String mName;
 		PageManager* mManager;
+		PageStreamProvider* mPageStreamProvider;
 
-		static const uint32 msChunkID;
-		static const uint16 msChunkVersion;
 	public:
+		static const uint32 CHUNK_ID;
+		static const uint16 CHUNK_VERSION;
 		/** Constructor.
 		@param name The name of the world, which must be enough to identify the 
 			place where data for it can be loaded from (doesn't have to be a filename
 		void load(const String& filename);
 		/// Load world data from a stream
 		void load(const DataStreamPtr& stream);
-		/// Load world data from a serialiser
-		void load(StreamSerialiser& stream);
+		/// Load world data from a serialiser (returns true if successful)
+		bool load(StreamSerialiser& stream);
 		/** Save world data to a file
-		@param filename The name of the file to create
-		@param Archive Optional archive which the filename is relative to
+		@param filename The name of the file to create; this can either be an 
+			absolute filename or 
 		*/
-		void save(const String& filename, Archive* arch = 0);
+		void save(const String& filename);
 		/// Save world data to a stream
 		void save(const DataStreamPtr& stream);
 		/// Save world data to a serialiser
 		/** Destroy a section of world. */
 		void destroySection(PagedWorldSection* sec);
 
+		/** Get the number of sections this world has. */
+		size_t getSectionCount() const { return mSections.size(); }
+
 		/** Retrieve a section of the world. */
 		PagedWorldSection* getSection(const String& name);
 
 		/// Retrieve a const reference to all the sections in this world
 		const SectionMap& getSections() const { return mSections; }
 
+		/** Set the PageStreamProvider which can provide streams for Pages in this world. 
+		@remarks
+			This is the top-level way that you can direct how Page data is loaded. 
+			When data for a Page is requested for a PagedWorldSection, the following
+			sequence of classes will be checked to see if they have a provider willing
+			to supply the stream: PagedWorldSection, PagedWorld, PageManager.
+			If none of these do, then the default behaviour is to look for a file
+			called worldname_sectionname_pageID.page. 
+		@note
+			The caller remains responsible for the destruction of the provider.
+		*/
+		void setPageStreamProvider(PageStreamProvider* provider) { mPageStreamProvider = provider; }
+		
+		/** Get the PageStreamProvider which can provide streams for Pages in this world. */
+		PageStreamProvider* getPageStreamProvider() const { return mPageStreamProvider; }
+
+		/** Get a serialiser set up to read Page data for the given PageID. 
+		@param pageID The ID of the page being requested
+		@param section The parent section to which this page will belong
+		@remarks
+		The StreamSerialiser returned is the responsibility of the caller to
+		delete. 
+		*/
+		StreamSerialiser* _readPageStream(PageID pageID, PagedWorldSection* section);
+
+		/** Get a serialiser set up to read Page data for the given PageID. 
+		@param pageID The ID of the page being requested
+		@param section The parent section to which this page will belong
+		@remarks
+		The StreamSerialiser returned is the responsibility of the caller to
+		delete. 
+		*/
+		StreamSerialiser* _writePageStream(PageID pageID, PagedWorldSection* section);
+
+
 	protected:
 		SectionMap mSections;
 		NameGenerator mSectionNameGenerator;

File Components/Paging/include/OgrePagedWorldSection.h

 	*/
 	class PagedWorldSection : public PageAlloc
 	{
+	public:
+		typedef map<PageID, Page*>::type PageMap;
 	protected:
 		String mName;
 		AxisAlignedBox mAABB;
 		PagedWorld* mParent;
 		PageStrategy* mStrategy;
 		PageStrategyData* mStrategyData;
+		PageMap mPages;
+		PageStreamProvider* mPageStreamProvider;
 
-		static const uint32 msChunkID;
-		static const uint16 msChunkVersion;
 
 	public:
+		static const uint32 CHUNK_ID;
+		static const uint16 CHUNK_VERSION;
+
+		/** Construct a new instance, specifying just the parent (expecting to load). */
+		PagedWorldSection(PagedWorld* parent); 
+
 		/** Construct a new instance, specifying the parent and assigned strategy. */
 		PagedWorldSection(const String& name, PagedWorld* parent, PageStrategy* strategy);
 		virtual ~PagedWorldSection();
 		virtual const AxisAlignedBox& getBoundingBox() const;
 
 
-		/// Load this section from a stream
-		virtual void load(StreamSerialiser& stream);
+		/// Load this section from a stream (returns true if successful)
+		virtual bool load(StreamSerialiser& stream);
 		/// Save this section to a stream
 		virtual void save(StreamSerialiser& stream);
 
 		*/
 		virtual void holdPage(PageID pageID);
 
+		/** Retrieves a Page.
+		@remarks
+			This method will only return Page instances that are already loaded. It
+			will return null if a page is not loaded. 
+		*/
+		virtual Page* getPage(PageID pageID);
+
+		/** Attach a page to this section. 
+		@remarks
+			This method is usually called by the loading routine and not directly. 
+			This class becomes responsible for deleting the Page.
+		*/
+		virtual void attachPage(Page* page);
+
+		/** Detach a page to this section. 
+		@remarks
+		This method is usually called by the unloading routine and not directly. 
+		This class is no longer responsible for deleting the Page.
+		*/
+		virtual void detachPage(Page* page);
+
+		/** Set the PageStreamProvider which can provide streams Pages in this section. 
+		@remarks
+			This is the top-level way that you can direct how Page data is loaded. 
+			When data for a Page is requested for a PagedWorldSection, the following
+			sequence of classes will be checked to see if they have a provider willing
+			to supply the stream: PagedWorldSection, PagedWorld, PageManager.
+			If none of these do, then the default behaviour is to look for a file
+			called worldname_sectionname_pageID.page. 
+		@note
+			The caller remains responsible for the destruction of the provider.
+		*/
+		void setPageStreamProvider(PageStreamProvider* provider) { mPageStreamProvider = provider; }
+		
+		/** Get the PageStreamProvider which can provide streams for Pages in this section. */
+		PageStreamProvider* getPageStreamProvider() const { return mPageStreamProvider; }
+
+		/** Get a serialiser set up to read Page data for the given PageID. 
+		@param pageID The ID of the page being requested
+		@remarks
+		The StreamSerialiser returned is the responsibility of the caller to
+		delete. 
+		*/
+		StreamSerialiser* _readPageStream(PageID pageID);
+
+		/** Get a serialiser set up to write Page data for the given PageID. 
+		@param pageID The ID of the page being requested
+		@remarks
+		The StreamSerialiser returned is the responsibility of the caller to
+		delete. 
+		*/
+		StreamSerialiser* _writePageStream(PageID pageID);
+
 	};
 
 	/** @} */

File Components/Paging/include/OgrePagingPrerequisites.h

 	class PagedWorld;
 	class PagedWorldSection;
 	class PageManager;
+	class PageRequestQueue;
 	class PageStrategy;
 	class PageStrategyData;
+	class PageStreamProvider;
 
 	typedef GeneralAllocatedObject PageAlloc;
 

File Components/Paging/src/OgreGrid2DPageStrategy.cpp

 namespace Ogre
 {
 	//---------------------------------------------------------------------
-	const uint32 Grid2DPageStrategyData::msChunkID = StreamSerialiser::makeIdentifier("G2DD");
-	const uint16 Grid2DPageStrategyData::msChunkVersion = 1;
+	const uint32 Grid2DPageStrategyData::CHUNK_ID = StreamSerialiser::makeIdentifier("G2DD");
+	const uint16 Grid2DPageStrategyData::CHUNK_VERSION = 1;
 	//---------------------------------------------------------------------
 	Grid2DPageStrategyData::Grid2DPageStrategyData()
 		: PageStrategyData()
 		mHoldRadiusInCells = mHoldRadius / mCellSize;
 	}
 	//---------------------------------------------------------------------
-	void Grid2DPageStrategyData::load(StreamSerialiser& ser)
+	bool Grid2DPageStrategyData::load(StreamSerialiser& ser)
 	{
 		const StreamSerialiser::Chunk* chunk = ser.readChunkBegin();
-		if (chunk->id != msChunkID)
+		if (chunk->id != CHUNK_ID)
 		{
 			ser.undoReadChunk(chunk->id);
-			OGRE_EXCEPT(Exception::ERR_INVALID_STATE, 
-				"Stream does not contain Grid2DPageStrategyData data!", 
-				"Grid2DPageStrategyData::load");
+			return false;
 		}
 
 		// Check version
-		if (chunk->version > msChunkVersion)
+		if (chunk->version > CHUNK_VERSION)
 		{
 			// skip the rest
 			ser.readChunkEnd(chunk->id);
 		ser.read(&mHoldRadius);
 
 		ser.readChunkEnd(chunk->id);
+
+		return true;
 	}
 	//---------------------------------------------------------------------
 	void Grid2DPageStrategyData::save(StreamSerialiser& ser)
 	{
-		ser.writeChunkBegin(msChunkID, msChunkVersion);
+		ser.writeChunkBegin(CHUNK_ID, CHUNK_VERSION);
 
 		uint8 readMode = (uint8)mMode;
 		ser.write(&readMode);
 		ser.write(&mLoadRadius);
 		ser.write(&mHoldRadius);
 
-		ser.writeChunkEnd(msChunkID);
+		ser.writeChunkEnd(CHUNK_ID);
 	}
 	//---------------------------------------------------------------------
 	//---------------------------------------------------------------------

File Components/Paging/src/OgrePage.cpp

 -----------------------------------------------------------------------------
 */
 #include "OgrePage.h"
+#include "OgreRoot.h"
 
 namespace Ogre
 {
+	//---------------------------------------------------------------------
+	Page::Page(PageID pageID)
+		: mID(pageID)
+		, mParent(0)
+	{
+
+	}
+	//---------------------------------------------------------------------
+	Page::~Page()
+	{
+
+	}
+	//---------------------------------------------------------------------
+	void Page::_notifyAttached(PagedWorldSection* parent)
+	{
+		mParent = parent;
+	}
+	//---------------------------------------------------------------------
+	void Page::touch()
+	{
+		mFrameLastHeld = Root::getSingleton().getNextFrameNumber();
+	}
 
 }
 

File Components/Paging/src/OgrePageManager.cpp

 #include "OgrePageStrategy.h"
 #include "OgreStringConverter.h"
 #include "OgreException.h"
+#include "OgrePageRequestQueue.h"
+#include "OgrePagedWorldSection.h"
+#include "OgrePagedWorld.h"
+#include "OgreStreamSerialiser.h"
 
 namespace Ogre
 {
 	//---------------------------------------------------------------------
 	PageManager::PageManager()
 		: mWorldNameGenerator("World")
+		, mQueue(0)
+		, mPageStreamProvider(0)
+		, mPageResourceGroup(ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME)
 	{
-
+		mQueue = OGRE_NEW PageRequestQueue(this);
 	}
 	//---------------------------------------------------------------------
 	PageManager::~PageManager()
 	{
-
+		OGRE_DELETE mQueue;
 	}
 	//---------------------------------------------------------------------
 	PagedWorld* PageManager::createWorld(const String& name)
 	{
 		PagedWorld* ret = createWorld(name);
 
-		ret->load(filename);
+		StreamSerialiser* ser = _readWorldStream(filename);
+		ret->load(*ser);
+		OGRE_DELETE ser;
 
 		return ret;
 
 		return ret;
 	}
 	//---------------------------------------------------------------------
-	void PageManager::saveWorld(PagedWorld* world, const String& filename, Archive* arch)
+	void PageManager::saveWorld(PagedWorld* world, const String& filename)
 	{
-		world->save(filename, arch);
+		world->save(filename);
 	}
 	//---------------------------------------------------------------------
 	void PageManager::saveWorld(PagedWorld* world, const DataStreamPtr& stream)
 		return mStrategies;
 	}
 	//---------------------------------------------------------------------
+	StreamSerialiser* PageManager::_readPageStream(PageID pageID, PagedWorldSection* section)
+	{
+		StreamSerialiser* ser = 0;
+		if (mPageStreamProvider)
+			ser = mPageStreamProvider->readPageStream(pageID, section);
+		if (!ser)
+		{
+			// use default implementation
+			StringUtil::StrStreamType nameStr;
+			nameStr << section->getWorld()->getName() << "_" << section->getName() 
+				<< "_" << pageID << ".page";
+			DataStreamPtr stream = ResourceGroupManager::getSingleton().openResource(nameStr.str());
+
+			ser = OGRE_NEW StreamSerialiser(stream);
+
+		}
+
+		return ser;
+
+	}
+	//---------------------------------------------------------------------
+	StreamSerialiser* PageManager::_writePageStream(PageID pageID, PagedWorldSection* section)
+	{
+		StreamSerialiser* ser = 0;
+		if (mPageStreamProvider)
+			ser = mPageStreamProvider->writePageStream(pageID, section);
+		if (!ser)
+		{
+			// use default implementation
+			StringUtil::StrStreamType nameStr;
+			nameStr << section->getWorld()->getName() << "_" << section->getName() 
+				<< "_" << pageID << ".page";
+			
+			// create file, overwrite if necessary
+			DataStreamPtr stream = ResourceGroupManager::getSingleton().createResource(
+				nameStr.str(), mPageResourceGroup, true);
+
+			ser = OGRE_NEW StreamSerialiser(stream);
+
+		}
+
+		return ser;
+
+	}
+	//---------------------------------------------------------------------
+	StreamSerialiser* PageManager::_readWorldStream(const String& filename)
+	{
+		StreamSerialiser* ser = 0;
+		if (mPageStreamProvider)
+			ser = mPageStreamProvider->readWorldStream(filename);
+		if (!ser)
+		{
+			// use default implementation
+			DataStreamPtr stream = ResourceGroupManager::getSingleton().openResource(
+				filename);
+
+			ser = OGRE_NEW StreamSerialiser(stream);
+
+		}
+
+		return ser;
+
+	}
+	//---------------------------------------------------------------------
+	StreamSerialiser* PageManager::_writeWorldStream(const String& filename)
+	{
+		StreamSerialiser* ser = 0;
+		if (mPageStreamProvider)
+			ser = mPageStreamProvider->writeWorldStream(filename);
+		if (!ser)
+		{
+			// use default implementation
+			// create file, overwrite if necessary
+			DataStreamPtr stream = ResourceGroupManager::getSingleton().createResource(
+				filename, mPageResourceGroup, true);
+
+			ser = OGRE_NEW StreamSerialiser(stream);
+
+		}
+
+		return ser;
+
+	}
+
 
 
 }

File Components/Paging/src/OgrePageRequestQueue.cpp

+/*
+-----------------------------------------------------------------------------
+This source file is part of OGRE
+(Object-oriented Graphics Rendering Engine)
+For the latest info, see http://www.ogre3d.org/
+
+Copyright (c) 2000-2009 Torus Knot Software Ltd
+Also see acknowledgements in Readme.html
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU Lesser General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA 02111-1307, USA, or go to
+http://www.gnu.org/copyleft/lesser.txt.
+
+You may alternatively use this source under the terms of a specific version of
+the OGRE Unrestricted License provided you have obtained such a license from
+Torus Knot Software Ltd.
+-----------------------------------------------------------------------------
+*/
+#include "OgrePageRequestQueue.h"
+#include "OgreStringConverter.h"
+#include "OgreException.h"
+
+namespace Ogre
+{
+	//---------------------------------------------------------------------
+	PageRequestQueue::PageRequestQueue(PageManager* manager)
+		: mManager(manager)
+	{
+	}
+	//---------------------------------------------------------------------
+	PageRequestQueue::~PageRequestQueue()
+	{
+	}
+	//---------------------------------------------------------------------
+	void PageRequestQueue::loadPage(PageID pageID, PagedWorldSection* section)
+	{
+		// TODO
+	}
+	//---------------------------------------------------------------------
+	void PageRequestQueue::unloadPage(Page* page)
+	{
+		// TODO
+	}
+
+
+}
+

File Components/Paging/src/OgrePagedWorld.cpp

 namespace Ogre
 {
 	//---------------------------------------------------------------------
-	const uint32 PagedWorld::msChunkID = StreamSerialiser::makeIdentifier("PWLD");
-	const uint16 PagedWorld::msChunkVersion = 1;
+	const uint32 PagedWorld::CHUNK_ID = StreamSerialiser::makeIdentifier("PWLD");
+	const uint16 PagedWorld::CHUNK_VERSION = 1;
 	//---------------------------------------------------------------------
 	PagedWorld::PagedWorld(const String& name, PageManager* manager)
-		:mName(name), mManager(manager), mSectionNameGenerator("Section")
+		:mName(name), mManager(manager), mSectionNameGenerator("Section"), mPageStreamProvider(0)
 	{
 
 	}
 	//---------------------------------------------------------------------
 	void PagedWorld::load(const String& filename)
 	{
-		// Load from any resource location
-		DataStreamPtr stream = ResourceGroupManager::getSingleton().openResource(filename);
-
-		if (stream.isNull())
-		{
-			// try manual load (absolute files)
-			struct stat tagStat;
-			if (stat(filename.c_str(), &tagStat) == 0)
-			{
-				std::ifstream* fstr = OGRE_NEW_T(std::ifstream, MEMCATEGORY_GENERAL)();
-				fstr->open(filename.c_str(), std::ios::in | std::ios::binary);
-				if (fstr->fail())
-				{
-					OGRE_DELETE_T(fstr, basic_ifstream, MEMCATEGORY_GENERAL);
-				}
-				else
-				{
-					stream = DataStreamPtr(OGRE_NEW FileStreamDataStream(filename,
-						fstr, tagStat.st_size, true));
-				}
-			}
-		}
-
-		if (stream.isNull())
-		{
-			OGRE_EXCEPT(Exception::ERR_FILE_NOT_FOUND,
-				"Cannot open world file: " + filename,
-				"PagedWorld::load");
-		}
-
-		load(stream);
-
+		StreamSerialiser* ser = mManager->_readWorldStream(filename);
+		load(*ser);
+		OGRE_DELETE ser;
 	}
 	//---------------------------------------------------------------------
 	void PagedWorld::load(const DataStreamPtr& stream)
 		load(ser);
 	}
 	//---------------------------------------------------------------------
-	void PagedWorld::load(StreamSerialiser& ser)
+	bool PagedWorld::load(StreamSerialiser& ser)
 	{
 		const StreamSerialiser::Chunk* chunk = ser.readChunkBegin();
-		if (chunk->id != msChunkID)
+		if (chunk->id != CHUNK_ID)
 		{
 			ser.undoReadChunk(chunk->id);
-			OGRE_EXCEPT(Exception::ERR_INVALID_STATE, 
-				"Stream does not contain PagedWorld data!", 
-				"PagedWorld::load");
+			return false;
 		}
 
 		// Check version
-		if (chunk->version > msChunkVersion)
+		if (chunk->version > CHUNK_VERSION)
 		{
 			// skip the rest
 			ser.readChunkEnd(chunk->id);
 		// Name
 		ser.read(&mName);
 		// Sections
-		for (SectionMap::iterator i = mSections.begin(); i != mSections.end(); ++i)
-			i->second->load(ser);
+		bool sectionsOk = true;
+		while(ser.peekNextChunkID() == PagedWorldSection::CHUNK_ID)
+		{
+			PagedWorldSection* sec = OGRE_NEW PagedWorldSection(this);
+			bool sectionsOk = sec->load(ser);
+			if (sectionsOk)
+				mSections[sec->getName()] = sec;
+			else
+			{
+				OGRE_DELETE sec;
+				break;
+			}
+		}
 
-		ser.readChunkEnd(msChunkID);
+		ser.readChunkEnd(CHUNK_ID);
+
+		return true;
 
 	}
 	//---------------------------------------------------------------------
-	void PagedWorld::save(const String& filename, Archive* arch)
+	void PagedWorld::save(const String& filename)
 	{
-		DataStreamPtr stream;
-		if (arch)
-		{
-			stream = arch->create(filename);
-		}
-		else
-		{
-			std::fstream* fstr = OGRE_NEW_T(std::fstream, MEMCATEGORY_GENERAL)();
-			fstr->open(filename.c_str(), std::ios::out | std::ios::binary);
-			if (fstr->fail())
-			{
-				OGRE_DELETE_T(fstr, basic_fstream, MEMCATEGORY_GENERAL);
-				OGRE_EXCEPT(Exception::ERR_FILE_NOT_FOUND,
-					"Cannot open world file for writing: " + filename,
-					"PageManager::saveWorld");
-			}
-			stream = DataStreamPtr(OGRE_NEW FileStreamDataStream(filename,
-				fstr, 0, true));
-
-		}
-
-		save(stream);
-		
+		StreamSerialiser* ser = mManager->_writeWorldStream(filename);
+		save(*ser);
+		OGRE_DELETE ser;
 	}
 	//---------------------------------------------------------------------
 	void PagedWorld::save(const DataStreamPtr& stream)
 	//---------------------------------------------------------------------
 	void PagedWorld::save(StreamSerialiser& ser)
 	{
-		ser.writeChunkBegin(msChunkID, msChunkVersion);
+		ser.writeChunkBegin(CHUNK_ID, CHUNK_VERSION);
 
 		// Name
 		ser.write(&mName);
 		for (SectionMap::iterator i = mSections.begin(); i != mSections.end(); ++i)
 			i->second->save(ser);
 
-		ser.writeChunkEnd(msChunkID);
+		ser.writeChunkEnd(CHUNK_ID);
 	}
 	//---------------------------------------------------------------------
 	PagedWorldSection* PagedWorld::createSection(const String& strategyName, 
 
 	}
 	//---------------------------------------------------------------------
+	StreamSerialiser* PagedWorld::_readPageStream(PageID pageID, PagedWorldSection* section)
+	{
+		StreamSerialiser* ser = 0;
+		if (mPageStreamProvider)
+			ser = mPageStreamProvider->readPageStream(pageID, section);
+		if (!ser)
+			ser = mManager->_readPageStream(pageID, section);
+		return ser;
+
+	}
+	//---------------------------------------------------------------------
+	StreamSerialiser* PagedWorld::_writePageStream(PageID pageID, PagedWorldSection* section)
+	{
+		StreamSerialiser* ser = 0;
+		if (mPageStreamProvider)
+			ser = mPageStreamProvider->writePageStream(pageID, section);
+		if (!ser)
+			ser = mManager->_writePageStream(pageID, section);
+		return ser;
+
+	}
 
 
 

File Components/Paging/src/OgrePagedWorldSection.cpp

 #include "OgreException.h"
 #include "OgrePagedWorld.h"
 #include "OgrePageManager.h"
+#include "OgrePage.h"
+#include "OgrePageRequestQueue.h"
+#include "OgreLogManager.h"
 
 namespace Ogre
 {
 	//---------------------------------------------------------------------
-	const uint32 PagedWorldSection::msChunkID = StreamSerialiser::makeIdentifier("PWSC");
-	const uint16 PagedWorldSection::msChunkVersion = 1;
+	const uint32 PagedWorldSection::CHUNK_ID = StreamSerialiser::makeIdentifier("PWSC");
+	const uint16 PagedWorldSection::CHUNK_VERSION = 1;
+	//---------------------------------------------------------------------
+	PagedWorldSection::PagedWorldSection(PagedWorld* parent)
+		: mParent(parent), mStrategy(0), mPageStreamProvider(0)
+	{
+
+	}
 	//---------------------------------------------------------------------
 	PagedWorldSection::PagedWorldSection(const String& name, PagedWorld* parent, PageStrategy* strategy)
-		: mName(name), mParent(parent), mStrategy(0)
+		: mName(name), mParent(parent), mStrategy(0), mPageStreamProvider(0)
 	{
 		setStrategy(strategy);
 	}
 	//---------------------------------------------------------------------
 	PagedWorldSection::~PagedWorldSection()
 	{
-		mStrategy->destroyData(mStrategyData);
-		mStrategyData = 0;
+		if (mStrategy)
+		{
+			mStrategy->destroyData(mStrategyData);
+			mStrategyData = 0;
+		}
 	}
 	//---------------------------------------------------------------------
 	void PagedWorldSection::setBoundingBox(const AxisAlignedBox& box)
 			}
 
 			mStrategy = strat;
-			mStrategyData = mStrategy->createData();
+			if (mStrategy)
+				mStrategyData = mStrategy->createData();
 		}
 	}
 	//---------------------------------------------------------------------
 		setStrategy(mParent->getManager()->getStrategy(stratName));
 	}
 	//---------------------------------------------------------------------
-	void PagedWorldSection::load(StreamSerialiser& ser)
+	bool PagedWorldSection::load(StreamSerialiser& ser)
 	{
 		const StreamSerialiser::Chunk* chunk = ser.readChunkBegin();
-		if (chunk->id != msChunkID)
+		if (chunk->id != CHUNK_ID)
 		{
 			ser.undoReadChunk(chunk->id);
-			OGRE_EXCEPT(Exception::ERR_INVALID_STATE, 
-				"Stream does not contain PagedWorldSection data!", 
-				"PagedWorldSection::load");
+			return false;
 		}
 
 		// Check version
-		if (chunk->version > msChunkVersion)
+		if (chunk->version > CHUNK_VERSION)
 		{
 			// skip the rest
 			ser.readChunkEnd(chunk->id);
 		ser.read(&stratname);
 		setStrategy(stratname);
 		// Page Strategy Data
-		mStrategyData->load(ser);
+		bool strategyDataOk = mStrategyData->load(ser);
+		if (!strategyDataOk)
+			LogManager::getSingleton().stream() << "Error: PageStrategyData for section '"
+			<< mName << "' was not loaded correctly, check file contents";
 
-		ser.readChunkEnd(msChunkID);
+		ser.readChunkEnd(CHUNK_ID);
+
+		return true;
 
 	}
 	//---------------------------------------------------------------------
 	void PagedWorldSection::save(StreamSerialiser& ser)
 	{
-		ser.writeChunkBegin(msChunkID, msChunkVersion);
+		ser.writeChunkBegin(CHUNK_ID, CHUNK_VERSION);
 
 		// Name
 		ser.write(&mName);
 		// Page Strategy Data
 		mStrategyData->save(ser);
 
-		ser.writeChunkEnd(msChunkID);
+		ser.writeChunkEnd(CHUNK_ID);
 
 	}
 	//---------------------------------------------------------------------
 	void PagedWorldSection::loadPage(PageID pageID)
 	{
-		// TODO
+		PageMap::iterator i = mPages.find(pageID);
+		if (i == mPages.end())
+			mParent->getManager()->getQueue()->loadPage(pageID, this);
+		else
+			i->second->touch();
 	}
 	//---------------------------------------------------------------------
 	void PagedWorldSection::holdPage(PageID pageID)
 	{
-		// TODO
+		PageMap::iterator i = mPages.find(pageID);
+		if (i != mPages.end())
+			i->second->touch();
+	}
+	//---------------------------------------------------------------------
+	Page* PagedWorldSection::getPage(PageID pageID)
+	{
+		PageMap::iterator i = mPages.find(pageID);
+		if (i != mPages.end())
+			return i->second;
+		else
+			return 0;
+	}
+	//---------------------------------------------------------------------
+	void PagedWorldSection::attachPage(Page* page)
+	{
+		// try to insert
+		std::pair<PageMap::iterator, bool> ret = mPages.insert(
+			PageMap::value_type(page->getID(), page));
+
+		if (!ret.second)
+		{
+			// page with this ID already in map
+			if (ret.first->second != page)
+			{
+				// replacing a page, delete the old one
+				OGRE_DELETE ret.first->second;
+				ret.first->second = page;
+			}
+		}
+		page->_notifyAttached(this);
+			
+	}
+	//---------------------------------------------------------------------
+	void PagedWorldSection::detachPage(Page* page)
+	{
+		PageMap::iterator i = mPages.find(page->getID());
+		if (i != mPages.end() && i->second == page)
+		{
+			mPages.erase(i);
+			page->_notifyAttached(0);
+		}
+
 	}
 	//---------------------------------------------------------------------
 	void PagedWorldSection::frameStart(Real timeSinceLastFrame)
 	{
 		mStrategy->notifyCamera(cam, this);
 	}
+	//---------------------------------------------------------------------
+	StreamSerialiser* PagedWorldSection::_readPageStream(PageID pageID)
+	{
+		StreamSerialiser* ser = 0;
+		if (mPageStreamProvider)
+			ser = mPageStreamProvider->readPageStream(pageID, this);
+		if (!ser)
+			ser = mParent->_readPageStream(pageID, this);
+		return ser;
+
+	}
+	//---------------------------------------------------------------------
+	StreamSerialiser* PagedWorldSection::_writePageStream(PageID pageID)
+	{
+		StreamSerialiser* ser = 0;
+		if (mPageStreamProvider)
+			ser = mPageStreamProvider->writePageStream(pageID, this);
+		if (!ser)
+			ser = mParent->_writePageStream(pageID, this);
+		return ser;
+
+	}
 
 
 

File Tests/CMakeLists.txt

 		src/main.cpp
 	)
 
+	if (OGRE_BUILD_COMPONENT_PAGING)
+	  include_directories(${CMAKE_CURRENT_SOURCE_DIR}/Components/Paging/include
+	    ${CMAKE_SOURCE_DIR}/Components/Paging/include)
+	  
+	  set(OGRE_LIBRARIES ${OGRE_LIBRARIES} OgrePaging)
+	  set(HEADER_FILES ${HEADER_FILES}
+	    Components/Paging/include/PageCoreTests.h
+	  )
+	  set(SOURCE_FILES ${SOURCE_FILES}
+	    Components/Paging/src/PageCoreTests.cpp
+	  )
+	endif ()
+
 	add_executable(Test_Ogre WIN32 ${HEADER_FILES} ${SOURCE_FILES} ${RESOURCE_FILES} )
 	ogre_config_common(Test_Ogre)
 	target_link_libraries(Test_Ogre ${OGRE_LIBRARIES} ${CppUnit_LIBRARIES})

File Tests/Components/Paging/include/PageCoreTests.h

+/*
+-----------------------------------------------------------------------------
+This source file is part of OGRE
+(Object-oriented Graphics Rendering Engine)
+For the latest info, see http://www.ogre3d.org/
+
+Copyright (c) 2000-2009 Torus Knot Software Ltd
+Also see acknowledgements in Readme.html
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU Lesser General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA 02111-1307, USA, or go to
+http://www.gnu.org/copyleft/lesser.txt.
+
+You may alternatively use this source under the terms of a specific version of
+the OGRE Unrestricted License provided you have obtained such a license from
+Torus Knot Software Ltd.
+-----------------------------------------------------------------------------
+*/
+#include <cppunit/TestFixture.h>
+#include <cppunit/extensions/HelperMacros.h>
+
+#include "OgreRoot.h"
+#include "OgrePageManager.h"
+#include "OgreGrid2DPageStrategy.h"
+
+using namespace Ogre; 
+
+class PageCoreTests : public CppUnit::TestFixture
+{
+	// CppUnit macros for setting up the test suite
+	CPPUNIT_TEST_SUITE( PageCoreTests );
+	CPPUNIT_TEST(testSimpleCreateSaveLoadWorld);
+	CPPUNIT_TEST_SUITE_END();
+
+	Root* mRoot;
+	PageManager* mPageManager;
+	Grid2DPageStrategy* mGridStrategy;
+public:
+	void setUp();
+	void tearDown();
+	void testSimpleCreateSaveLoadWorld();
+	void testLoadWorld();
+};

File Tests/Components/Paging/src/PageCoreTests.cpp

+/*
+-----------------------------------------------------------------------------
+This source file is part of OGRE
+(Object-oriented Graphics Rendering Engine)
+For the latest info, see http://www.ogre3d.org/
+
+Copyright (c) 2000-2009 Torus Knot Software Ltd
+Also see acknowledgements in Readme.html
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU Lesser General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA 02111-1307, USA, or go to
+http://www.gnu.org/copyleft/lesser.txt.
+
+You may alternatively use this source under the terms of a specific version of
+the OGRE Unrestricted License provided you have obtained such a license from
+Torus Knot Software Ltd.
+-----------------------------------------------------------------------------
+*/
+#include "PageCoreTests.h"
+#include "OgrePagedWorld.h"
+#include "OgrePagedWorldSection.h"
+
+CPPUNIT_TEST_SUITE_REGISTRATION( PageCoreTests );
+
+void PageCoreTests::setUp()
+{
+	mRoot = OGRE_NEW Root();
+	mPageManager = OGRE_NEW PageManager();
+	mGridStrategy = OGRE_NEW Grid2DPageStrategy(mPageManager);
+	mPageManager->addStrategy(mGridStrategy);
+
+	mRoot->addResourceLocation("./", "FileSystem");
+
+}
+
+void PageCoreTests::tearDown()
+{
+	OGRE_DELETE mPageManager;
+	OGRE_DELETE mGridStrategy;
+	OGRE_DELETE mRoot;
+}
+
+
+void PageCoreTests::testSimpleCreateSaveLoadWorld()
+{
+	String worldName = "MyWorld";
+	String filename = "myworld.world";
+	String sectionName1 = "Section1";
+	String sectionName2 = "Section2";
+	PagedWorld* world = mPageManager->createWorld(worldName);
+	PagedWorldSection* section = world->createSection(mGridStrategy, sectionName1);
+	section = world->createSection(mGridStrategy, sectionName2);
+	
+	world->save(filename);
+
+	mPageManager->destroyWorld(world);
+	world = 0;
+
+	world = mPageManager->loadWorld(filename);
+
+	CPPUNIT_ASSERT_EQUAL(worldName, world->getName());
+	CPPUNIT_ASSERT_EQUAL((size_t)2, world->getSectionCount());
+
+	section = world->getSection(sectionName1);
+	CPPUNIT_ASSERT(section != 0);
+	section = world->getSection(sectionName2);
+	CPPUNIT_ASSERT(section != 0);
+
+
+
+}
+