/*------------------------------------------------------------------------------------- Copyright (c) 2006 John Judnich This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. -------------------------------------------------------------------------------------*/ #define PAGEDGEOMETRY_VERSION_MAJOR 1 #define PAGEDGEOMETRY_VERSION_MINOR 0 #define PAGEDGEOMETRY_VERSION_PATCH 7 //PagedGeometry.h //Main header file for the PagedGeometry engine. //------------------------------------------------------------------------------------- //-------- The following is the main API documentation page (parsed by doxygen) -------- /** \page Main PagedGeometry API Documentation \section A Introduction Although the PagedGeometry engine is fairly simple and easy to use, there are some advanced features that may be difficult to learn on you own. This API reference is here for your convenience, to aid you in learning how to get the most out of the PagedGeometry engine. Every feature of the engine is covered here in detail, so you won't be left in the dark about any aspect of PagedGeometry's use (however, some of the internal workings of the engine are not documented in here - you'll have to refer to the source code comments for that). \section B What is PagedGeometry? The PagedGeometry engine is an add-on to the OGRE Graphics Engine, which provides highly optimized methods for rendering massive amounts of small meshes covering a possibly infinite area. This is especially well suited for dense forests and outdoor scenes, with millions of trees, bushes, grass, rocks, etc., etc. Paged geometry gives you many advantages over plain entities, the main one being speed: With proper usage of detail levels, outdoor scenes managed by PagedGeometry can be >100x faster than plain entities. Another advantage is that the geometry is paged; in other words, only entities which are immediately needed (to be displayed) are loaded. This allows you to expand the boundaries of your virtual world almost infinitely (only limited by floating point precision), providing the player with a more realistically scaled game area. \section C Getting Started The first thing you should do is follow the instructions in "Getting Started.txt" to get PagedGeometry compiled and the examples running. Note: Keep in mind that the art in the examples is not the best, and is there simply to demonstrate the performance of the engine. When you're ready to start learning how to use PagedGeometry, the best place to start is with Tutorial 1 (in the docs folder). The tutorials will teach you how to use many important PagedGeometry features, step by step. This API reference isn't recommended for learning, but is a valuable resource when you need specific in-depth information about a certain function or class. \section E Credits \section D License Copyright (c) 2007 John Judnich This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ //-------------------------------------------------------------------------------------- // this small snipped disables some warnings under MSVC that can be ignored normally #ifdef _MSC_VER // disable MSVC warning "... possible loss of data" # pragma warning(disable: 4244) #endif //_MSC_VER #ifndef __PagedGeometry_H__ #define __PagedGeometry_H__ #include // numeric_limits<> #include #include #include #include #include #include #include #include #include #include #include #include namespace Forests { class GeometryPageManager; class PageLoader; /// Define TBounds as a TRect using Real numeric units. typedef Ogre::TRect TBounds; //Enable PagedGeometry::setCoordinateSystem() //#define PAGEDGEOMETRY_ALTERNATE_COORDSYSTEM //Enable per-entity user-defined data and callbacks //#define PAGEDGEOMETRY_USER_DATA //------------------------------------------------------------------------------------- /** \brief A class providing highly optimized methods for rendering massive amounts of geometry. The PagedGeometry class provides highly optimized methods for rendering massive amounts of small meshes, covering a large area. This is especially well suited for dense forests, with thousands of trees, bushes, grass, rocks, etc., etc. The paged geometry works by loading only the geometry that is visible (or will soon be visible), to save memory. The PagedGeometry engine can display entities using many different methods (static geometry, impostors, etc.) What method is used depends on the entities' distance from the camera, and how you configured these methods (see PagedGeometry::addDetailLevel() for more info about this). The loading of pages is done through a PageLoader, which you define. This way, you can program the PagedGeometry to load pages any way you want, whether it's from a file on your hard drive, from a procedural generation algorithm, or anything else. See the documentation for the PageLoader class for more information about this. \note Always remember to call each PagedGeometry object's update() function every frame; otherwise the geometry you're trying to display will not appear correctly. See PagedGeometry::update() for more information. */ class PagedGeometry { public: /** \brief Initializes a PagedGeometry object. \param cam A camera which the PagedGeometry object will use for LOD calculations. \param pageSize The page size (pages are square) pageSize sets the size of a single "page" of geometry. If your pages are too big, you may experience "hiccuping" during the game as these regions are loaded. However, regions that are too small may result in lower frame rates (depending on what detail levels you are using). Also, using larger pages uses slightly less memory, although you should generally give performance precedence over memory usage. \note You do not need to specify the page size or camera in the constructor if you don't want to. PagedGeometry::setCamera() and PagedGeometry::setPageSize() allows you to configure these values later. \see setCamera(), setPageSize(), setBounds(), setInfinite(), setPageLoader() */ PagedGeometry(Ogre::Camera *cam = NULL, Ogre::Real pageSize = 100, Ogre::RenderQueueGroupID queue = Ogre::RENDER_QUEUE_6); ~PagedGeometry(); /** \brief Sets the camera to use when calculating levels of detail. \param cam The camera to assign to this PagedGeometry object. \note The specified camera must belong to the same scene manager as previous ones; once a camera is set, the scene manager that camera belongs to will be used permanently for this PagedGeometry instance. \warning Changing cameras will often result in low cache efficiency in infinite mode. If you are constantly switching between multiple cameras that are relatively far apart, consider using bounded mode. */ void setCamera(Ogre::Camera *cam); /** \brief Sets the output directory for the imposter pages */ void setTempDir(Ogre::String dir); Ogre::String getTempdir() { return this->tempdir; }; /** \brief Gets the camera which is used to calculate levels of detail. \returns The camera assigned to this PagedGeometry object. \warning Be careful when storing a local copy of this - the camera returned may change at any time, so it's usually best to call getCamera() repeatedly, instead of storing a local copy. This is an inline function, so don't worry too much about performance. */ inline Ogre::Camera *getCamera() const { return sceneCam; } /** \brief Gets the scene manager which is being used to display the geometry \returns A SceneManager This function simply returns the SceneManager that this PagedGeometry object is using. If no camera has been set yet, this will return NULL, since PagedGeometry has no way of knowing which SceneManager to use. However, once a camera is set, the SceneManager this function returns will always remain the same - even if the camera is later set to NULL. */ inline Ogre::SceneManager *getSceneManager() const { return sceneMgr; } /** \brief Gets the scene node to which all PagedGeometry geometry is attached \returns A SceneNode \note Feel free to ignore this function - you can fully make use of PagedGeometry's features without it. This function returns the SceneNode which PagedGeometry uses to render all it's geometry. Everything that PagedGeometry renders to the screen can be found under this scene node. You don't need to use this function at all to fully make use of PagedGeometry's features - it's primary use is for PagedGeometry's internal subsystems to be able to create geometry using the proper scene node. \warning If no camera has been set yet, this will return NULL, since PagedGeometry can't create the SceneNode until it know which SceneManager to use (which is determined from the assigned camera). However, once a camera is set, the SceneNode this function returns will always remain the same - even if the camera is later set to NULL. */ inline Ogre::SceneNode *getSceneNode() const { return rootNode; } #ifdef PAGEDGEOMETRY_ALTERNATE_COORDSYSTEM /** \brief Sets the coordinate system to be used by PagedGeometry \param up A vector pointing to whatever direction you consider to be "up" \param right A vector pointing to whatever direction you consider to be "right" By default, PagedGeometry uses the standard coordinate system where X is right, Y is up, and Z is back. If you use an alternate coordinate system, for example where Z is up, you'll have to use this function to configure PagedGeometry to use that coordinate system; otherwise, LOD calculations, impostors, etc. will be all messed up. To do so, simply supply which directions you consider "right" and "up". For example, if your coordinate system uses X as right, Y as forward, and Z as up, you would set the "right" parameter to Vector3::UNIT_X and the "up" parameter to Vector3::UNIT_Z. The forward direction (Vector3::UNIT_Y in this case) doesn't need to be supplied since it will be automatically calculated from the right and up vectors. \warning Be sure to configure PagedGeometry with your coordinate system before using any PageLoader's, since they may depend on the current coordinate system to function properly. \note By default this function is disabled and won't appear in the PagedGeometry library. To enable it, reenable the line near the top of PagedGeometry.h where PAGEDGEOMETRY_ALTERNATE_COORDSYSTEM is defined by un-commenting it (then recompile). */ void setCoordinateSystem(Ogre::Vector3 up, Ogre::Vector3 right = Ogre::Vector3::UNIT_X); #endif /** \brief Switches to bounded mode and uses the given boundaries By default, PagedGeometry does not place boundaries on the geometry that can be added to it though the PageLoader. However, there are cases where specifying a strict boundary can improve performance. When bounded mode is used, PagedGeometry allocates certain lightweight data structures to fill the boundaries. This may result in a slight frame rate boost compared to an infinite world, although it will take a little more memory (especially with large areas). Since bounded mode requires more memory for larger boundaries, it is best suited for small to medium sized worlds, while infinite mode is best for huge or even near-infinite worlds. \note Bounds must be square. \see setInfinite() */ void setBounds(const TBounds bounds); /** \brief Switches to infinite mode By default, PagedGeometry will just allocate enough memory to display and cache what is on the screen. This behavior is called "infinite mode", and can be activated by calling this function if not already activated (it will be by default). Most game worlds have boundaries of some sort, but infinite mode allows you to expand the size of your game world almost to infinity. Since only what's on the screen is actually loaded, it makes little difference if your world is 100 square miles, or 1,000,000 square miles. The only disadvantage to using infinite mode is that cache efficiency will be slightly reduced in some cases. For example, bounded mode will achieve better performance if you are often switching between multiple cameras, since the cache is more globally based (unlike the locally based cache of infinite mode). \see setBounds() */ void setInfinite(); /** \brief Gets the current geometry boundary. \returns The geometry boundary which was set in the constructor. \see The PagedGeometry constructor for information about the geometry boundary. This returns a TBounds value, which contains information about the boundaries of the geometry. Since TBounds is simply a typedef for TRect, accessing the boundary information is easy, for example: \code (...) = PagedGeometry::getBounds().top; (...) = PagedGeometry::getBounds().bottom; (etc.) \endcode Ogre's documentation should contain more information about TRect members. */ inline const TBounds &getBounds() const { return m_bounds; } /** \brief Convert an Ogre::AxisAlignedBox to a TBounds coplanar to the plane defined by the UP axis. */ TBounds convertAABToTBounds( const Ogre::AxisAlignedBox & aab ) const; /** \brief Sets the page size This sets the size of a single "page" of geometry. If your pages are too big, you may experience "hiccuping" during the game as these regions are loaded. However, regions that are too small may result in lower frame rates (depending on what detail levels you are using). Also, using larger pages uses slightly less memory, although you should generally give performance precedence over memory usage. */ void setPageSize(Ogre::Real size); /** \brief Gets the current page size. \returns The current page size. \see setPageSize() for more information about page size. */ inline Ogre::Real getPageSize() { return pageSize; } /** \brief Adds a detail level to the PagedGeometry object. \param PageType The page type class you want to use for this detail level. \param maxRange The maximum distance this detail level will be used at. \param transitionLength The desired length of fade transitions - optional \param data An extra parameter to pass to the PageType constructor - optional \note PageType is not really a function parameter, but a template parameter. See the code below for an example on how this "parameter" is used. On it's own, a plain PagedGeometry object can't display anything. It needs you to add detail levels to it with this function. This way, you can easily customize the behavior of the PagedGeometry. To use this function, simply use the form: \code pagedGeometry::addDetailLevel(farRange); \endcode In the above example, "farRange" specifies the maximum view distance this detail level will be used at. However, the most important part is "". Here you substitute "PageType" with the name of a installed page type you want to use for this detail level. A page type is simply a method of displaying or otherwise representing entities. The PagedGeometry engine currently comes pre-installed with a few page types: BatchPage and ImpostorPage. Refer to their class documentation for detailed information about them (although you never actually access these classes yourself). You can use these page types in any configuration you want. For example: \code pagedTrees->addDetailLevel(100); //Use batched geometry from 0-100 pagedTrees->addDetailLevel(500); //Use impostors from 100-500 \endcode That example would set up the PagedGeometry object called pagedTrees to use batched geometry (StaticGeometry) from 0 to 100 units from the camera, and impostors from 100 to 500 units from the camera. If the included page types aren't adequate, you can fairly easily add your own by simply extending the virtual GeometryPage class properly. By default, no fade transitions are used. This means that there will be a noticeable "popping" effect when your tree changes from an impostor to a batch, for example. Enabling fade transitions will smooth out the change by slowly "morphing" between the two detail levels. To enable fade transitions, simply add a second parameter to you addDetailLevel() call: \code //Use batched geometry from 0-100, and transition to the next LOD for 50 units pagedTrees->addDetailLevel(100, 50); //Use impostors from 100-500 pagedTrees->addDetailLevel(500); \endcode The second parameter seen above will enable transitions between BatchPage and the next LOD (which is ImpostorPage, in this case). The number you supply will specify how long the transition is. Longer transitions result in smoother "morphs", although shorter transitions will give slightly better frame rates. The transition parameter can also be applied to the last detail level to cause it to fade out: \code //Use batched geometry from 0-100, and transition to the next LOD for 50 units pagedTrees->addDetailLevel(100, 50); //Use impostors from 100-400, and fade out for 100 units beyond that pagedTrees->addDetailLevel(400, 100); \endcode In the example above, batching is used up to 100 units, where the batched geometry starts transitioning for 50 units into the next level of detail (impostors). The impostors continue up to 400 units, where they begin to fade out for 100 more units. \warning Depending on your page size and transition length, enabling fade transitions will often reduce PagedGeometry's rendering performance anywhere from 10 - 30%. Transitions can be disabled by omitting the transitionLength parameter, or setting it to 0. \note Make sure you make any calls to setPageSize() and setBounds() / setInfinite() before adding detail levels with this function. After a detail level is added you cannot call these functions without first removing them with removeDetailLevels(). The "data" parameter is entirely optional, so there should never be any requirement that it be used. Just what type of data this parameter accepts, and what it does, depends entirely on the specific GeometryPage implementation you're using. See the appropriate page type documentation for info on how this parameter can be used (if at all). \see The GeometryPage class documention for more information on adding custom page types. */ template inline GeometryPageManager* addDetailLevel(Ogre::Real maxRange, Ogre::Real transitionLength = 0, const Ogre::Any &data = Ogre::Any(), Ogre::uint32 queryFlag = 0); /** \brief Removes all detail levels from the PagedGeometry object. This removes all detail levels (added with addDetailLevel) from the PagedGeometry object. This also removes all geometry associated with PagedGeometry from the scene. Remember that you will need to re-add all the detail levels again with addDetailLevel() before any of the geometry will be displayed. */ void removeDetailLevels(); /** \brief Returns a reference to a list of all added detail levels. This returns a std::list of all detail levels (GeometryPageManager's). These objects can be used to set/get advanced properties, such as view ranges and cache speeds. Normally you won't ever have to access this data, but it's there in case you need it. */ inline const std::list &getDetailLevels() { return managerList; } /** \brief Assigns a PageLoader object for the PagedGeometry. \param loader A PageLoader object. When the page manager decides it should cache a certain region of geometry, it calls on your PageLoader to do the job. This way you can load entities from RAM, a hard-drive, the internet, or even procedurally. Simply create a PageLoader class and use this function to link it to a PagedGeometry object. \warning Since you must instantiate your PageLoader yourself, you must also be sure to deallocate it properly (as with any other class instance). PagedGeometry will not do this for you. \see PageLoader documentation for more information on setting up a page loader. */ void setPageLoader(PageLoader *loader); /** \brief Gets the PageLoader currently being used to load geometry. \returns A PageLoader object which is currently being used to load geometry. This can be useful if you want to retrieve the current page loader to delete it, or any other task that needs to be done to the currently used page loader. */ inline PageLoader *getPageLoader() { return pageLoader; } /** \brief Updates this PagedGeometry object This function must be called each frame in order for the PagedGeometry object to calculate LODs and perform paging. If this function is not called every frame, none of the geometry managed by this PagedGeometry instance will appear (or if it does, it will appear incorrectly) */ void update(); /** \brief Reloads all visible geometry. If your PageLoader changes it's output during runtime, you normally won't see the changes immediately (and in many cases, you will never see the changes). This function provides a way to reload the geometry to force the changes to take effect immediately. This function will cause ALL visible geometry to be reloaded during the next update. This can take up to several seconds, depending on the complexity of the current scene, so use this function only when absolutely necessary. */ void reloadGeometry(); /** \brief Reloads geometry at the given location. \param point The point in 3D space where geometry needs to be reloaded. If your PageLoader changes it's output during runtime, you normally won't see the changes immediately (and in many cases, you will never see the changes). This function provides a way to reload the geometry to force the changes to take effect immediately. This function will cause a certain page of visible geometry to be reloaded during the next update. Unlike reloadGeometry(), this function allows pinpoint reloading to take place, resulting in better performance if a small portion of the geometry changes. Since this doesn't actually reload anything immediately, you can call this function as many times as you need without worrying too much about performance. For example, if you update 150 trees in your game, simply supply this function with the locations of each tree. When the scene is about to be rendered, the appropriate geometry pages will automatically be reloaded. */ void reloadGeometryPage(const Ogre::Vector3 &point, bool forceLoadImmediately = false); /** \brief Reloads geometry in the given radius area. \param center The center of the area to be reloaded \param radius The radius from the center where geometry needs to be reloaded \note This is identical to reloadGeometryPage() except it allows you to reload an entire area rather than a single point. If your PageLoader changes it's output during runtime, you normally won't see the changes immediately (and in many cases, you will never see the changes). This function provides a way to reload the geometry to force the changes to take effect immediately. This function will cause a certain area of visible geometry to be reloaded during the next update. Unlike reloadGeometry(), this function allows selective reloading to take place, resulting in better performance if a small portion of the geometry changes. Since this doesn't actually reload anything immediately, you can call this function as many times as you need without worrying too much about performance. For example, if you update 150 trees in your game, simply supply this function with the locations of each tree. When the scene is about to be rendered, the appropriate geometry pages will automatically be reloaded. */ void reloadGeometryPages(const Ogre::Vector3 ¢er, Ogre::Real radius); /** \brief Reloads geometry in the given rect area. \param area A rectangular area that needs reloading \note This is identical to reloadGeometryPage() except it allows you to reload an entire area rather than a single point. If your PageLoader changes it's output during runtime, you normally won't see the changes immediately (and in many cases, you will never see the changes). This function provides a way to reload the geometry to force the changes to take effect immediately. This function will cause a certain area of visible geometry to be reloaded during the next update. Unlike reloadGeometry(), this function allows selective reloading to take place, resulting in better performance if a small portion of the geometry changes. Since this doesn't actually reload anything immediately, you can call this function as many times as you need without worrying too much about performance. For example, if you update 150 trees in your game, simply supply this function with the locations of each tree. When the scene is about to be rendered, the appropriate geometry pages will automatically be reloaded. */ void reloadGeometryPages(const TBounds & area); /** \brief Preloads a region of geometry (loads once and never loads again) \param area A rectangular area of the world that needs to be preloaded You can use this function to preload entire areas of geometry. Doing this will basically turn off dynamic paging for the given region, since all the pages effecting it will stay loaded forever (until you delete the PagedGeometry object, or if using infinite mode, until you move away from the region). \note The rectangular bounds value you supply does not indicate a rectangular area to preload, but instead a rectangular area in which you need the camera to be able to freely move around without having to dynamically load any pages. In other words, this function will preload all geometry within viewing range of the given bounds area. */ void preloadGeometry(const TBounds & area); /** \brief Releases geometry preloaded with preloadGeometry() to be unloaded if necessary When you call preloadGeometry() to preload region(s) of geometry, it makes those regions un-unloadable; in other words, they will never be unloaded automatically by PagedGeometry (except for some cases in infinite mode). This way you can load a region of your world once and never have to load it again (optimally). This function allows you to undo all this by allowing all the geometry to be unloaded once again when necessary. It won't unload anything, but it will allow geometry to unload that previously was not allowed. */ void resetPreloadedGeometry(); /** \brief Hides or unhides all geometry managed by this PagedGeometry instance \params visible Whether or not you want this PagedGeometry to be visible By default everything is visible. This can be used to hide an entire PagedGeometry "group" of geometry if desired. */ void setVisible(bool visible) { geometryAllowedVisible = visible; } /** \brief Returns whether or not geometry managed by this PagedGeometry instance is visible By default, everything will be visible of course. This function simply returns the visible/invisible state as set by the setVisible() command. */ bool getVisible() { return geometryAllowedVisible; } /** \brief disables the use of shaders */ void setShadersEnabled(bool value) { shadersEnabled=value; } bool getShadersEnabled() { return shadersEnabled; } /* \brief Immediately loads visible geometry. \param maxTime The maximum amount of time (in milliseconds) which cacheGeometry() is allowed to use before returning (roughly). \returns Whether or not everything was cached. PagedGeometry automatically loads and caches geometry near the camera in real-time. This function allows you to easily pre-load this geometry outside of your render loop. For example, in your loading code, you might call PagedGeometry::cacheGeometry() to load all your trees/etc. managed by PagedGeometry instantly, rather than later on. If it takes several seconds to cache geometry, you may want to update a progress bar more often. The maxTime parameter allows you to split up this task into smaller segments for this purpose. Simply call cacheGeometry(maxTime) repeatedly until everything is cached (cacheGeometry() will return true when finished ). */ //todo //bool cacheGeometry(unsigned long maxTime = 0); /** INTERNAL FUNCTION - DO NOT USE */ Ogre::Vector3 _convertToLocal(const Ogre::Vector3 &globalVec) const; /** \brief Sets or creates a custom parameter for an entity managed by PagedGeometry This can be used to set custom parameters / data for entities which can be accessed from other PagedGeometry subsystems or your own code. Primarily, this is intended for use with GeometryPage implementations or PageLoader implementations. PagedGeometry includes a GeometryPage implementation, "WindBatchPage", which applies a wind animation shader to your trees, which you can control using these custom parameters: windFactorX and windFactorY. If you're using 3rd party PagedGeometry "plugins" like GeometryPage implementations, etc., there may be more custom parameters available to you. Check with the appropriate module documentation for info on supported custom parameters and their usage. \param entity Name of the entity \param paramName Name of the parameter for this entity \param paramValue Value to assign to the parameter */ void setCustomParam( std::string entity, std::string paramName, float paramValue); /** \brief Sets or creates a custom parameter for an entity managed by PagedGeometry This can be used to set custom parameters / data for entities which can be accessed from other PagedGeometry subsystems or your own code. Primarily, this is intended for use with GeometryPage implementations or PageLoader implementations. PagedGeometry includes a GeometryPage implementation, "WindBatchPage", which applies a wind animation shader to your trees, which you can control using these custom parameters: windFactorX and windFactorY. If you're using 3rd party PagedGeometry "plugins" like GeometryPage implementations, etc., there may be more custom parameters available to you. Check with the appropriate module documentation for info on supported custom parameters and their usage. \param entity Name of the entity \param paramName Name of the parameter for this entity \param paramValue Value to assign to the parameter */ void setCustomParam( std::string paramName, float paramValue); /** \brief Returns the value of the custom parameter \param entity Name of the entity \param paramName Name of the parameter for this entity \param defaultParamValue Value to return if no entry is found \returns float value if entry is found or the defaultParamValue if not */ float getCustomParam( std::string entity, std::string paramName, float defaultParamValue) const; /** \brief Returns the value of the custom parameter \param entity Name of the entity \param paramName Name of the parameter for this entity \param defaultParamValue Value to return if no entry is found \returns float value if entry is found or the defaultParamValue if not */ float getCustomParam( std::string paramName, float defaultParamValue) const; /** \brief Returns the rendering queue that paged geometry was constructed with \returns Ogre::RenderQueue number of the rendering queue */ Ogre::RenderQueueGroupID getRenderQueue() const; protected: //Internal function - do not use void _addDetailLevel(GeometryPageManager *mgr, Ogre::Real maxRange, Ogre::Real transitionLength); Ogre::SceneManager *sceneMgr; Ogre::SceneNode *rootNode; //PagedGeometry's own "root" node bool shadersEnabled; bool geometryAllowedVisible; //If set to false, all geometry managed by this PagedGeometry is hidden #ifdef PAGEDGEOMETRY_ALTERNATE_COORDSYSTEM Ogre::Quaternion coordinateSystemQuat; //The orientation of rootNode #endif //Camera data Ogre::Camera *sceneCam; Ogre::Vector3 oldCamPos; Ogre::Camera *lastSceneCam; Ogre::Vector3 lastOldCamPos; //This list keeps track of all the GeometryPageManager's added with addPageManager() std::list managerList; //The assigned PageLoader used to load entities PageLoader *pageLoader; //The bounds and page size TBounds m_bounds; //The page size Ogre::Real pageSize; //The used rendering queue Ogre::RenderQueueGroupID mRenderQueue; //Time-related data Ogre::Timer timer; unsigned long lastTime; Ogre::String tempdir; private: std::map customParam; }; //------------------------------------------------------------------------------------- /** \brief This base-class can be extended to provide different ways of representing entities. The PagedGeometry engine comes pre-installed with a few GeometryPage sub-classes (BatchPage, and ImpostorPage). These "page types" can all be supplied to a PagedGeometry object through addDetailLevel(). \see PagedGeometry::addDetailLevel() for more information about setting up detail levels. If you need more than the pre-installed page types, you can easily create your own! Simply make a new class inheriting GeometryPage. Then implement the necessary member functions, and it should work immediately. No additional setup is required. There are several virtual member functions you will need to implement in your class: \code virtual void init(SceneManager *mgr, Camera *cam) = 0; virtual void setRegion(Real left, Real top, Real right, Real bottom) = 0; virtual void addEntity(Entity *ent, const Vector3 &position, const Quaternion &rotation, const Vector3 &scale, const Ogre::ColourValue &color, void* userData = NULL) = 0; virtual void build() {} virtual void removeEntities() = 0; virtual void setVisible(bool visible) = 0; virtual void setFade(bool enabled, Real visibleDist, Real invisibleDist) = 0; virtual void update() {} \endcode \note For detailed information on implementing each of these functions, please refer to their documentation. Here is how the page manager uses these functions: \b 1. When a PagedGeometry first creates a GeometryPage, it immediately calls GeometryPage::init(). This function is called just like a constructor, and you should use it the same way. \b 2. GeometryPage::setRegion() is called to provide you with the area where upcoming entities will be added. You can use this information any way you like, or you can omit this step completely by omitting the setRegion() function from your class definition. \b 3. To load a page, the addEntity() function is used to insert all the entities into the scene. Then, build() is called. Entities don't actually have to be displayed until after build() is called. \b 4. setVisible() and setFade() will be called occasionally to update the visibility/fade status of the page. \b 5. When the page has become obsolete, the contents of it is deleted with removeEntities(). This should return the page to the state it was before addEntity() and build(). \b 6. Steps 2-5 are repeated as pages are loaded/unloaded Implementing your own geometry page is really very simple. As long as the functions do their jobs right, everything will work fine. If you learn best be example, you may want to take a look at how the included page types are implemented also. \see The BatchPage or GrassPage code for examples of how page types are implemented (the ImpostorPage can also be used as an example, although it is a somewhat complex technique, and is not recommended for learning how GeometryPage's work). */ class GeometryPage { friend class GeometryPageManager; public: /** \brief Prepare a geometry page for use. \param geom The PagedGeometry object that's creating this GeometryPage. \param data A single parameter of custom data (optional). This is called immediately after creating a new GeometryPage. It is never called more than once for a single instance of your geometry page. The "data" parameter is set for all pages when PagedGeometry::addDetailLevel() is called. This parameter is optional and can be used for whatever you like if you need a constructor parameter of some kind. Be sure to document what kind of variable the user needs to supply and what it's purpose is in your GeometryPage implementation. \note If you need to get the current camera, scene manager, etc., use the geom parameter. The PagedGeometry class contains inline methods you can use to access the camera and scene manager. \warning Do NOT store a local copy of geom->getCamera()! The camera returned by this function may change at any time! */ virtual void init(PagedGeometry *geom, const Ogre::Any &data) = 0; void setQueryFlag(Ogre::uint32 flag) { mHasQueryFlag = true; mQueryFlag = flag; }; bool hasQueryFlag() { return mHasQueryFlag; }; Ogre::uint32 getQueryFlag() { return mQueryFlag; }; /** \brief Prepare a geometry page for entities \param left The minimum x-coordinate any entities will have. \param top The minimum z-coordinate any entities will have. \param right The maximum x-coordinate any entities will have. \param bottom The maximum z-coordinate any entities will have. This basically provides you with a region where upcoming entities will be located, since many geometry rendering methods require this data. It's up to you how this data is used, if at all. setRegion() is never called when the page contains entities; only once just before a load process (when entities are added with addEntity). \note Implementing this funtion in your GeometryPage is completely optional, since most of the time you don't need region information. */ virtual void setRegion(Ogre::Real left, Ogre::Real top, Ogre::Real right, Ogre::Real bottom) {}; /** \brief Add an entity to the page, at the specified position, rotation, and scale. \param ent The entity that is being added. Keep in mind that the same entity may be added multiple times. \param position The position where the entity must be placed. Under normal circumstances, this will never be outside of the bounds supplied to init(). The only exception is when a PageLoader tries to add an entity outside of the bounds it was given. \param rotation The rotation which should be applied to the entity. \param scale The scale which should be applied to the entity. \param color The desired color to apply to the whole entity \note The entity does not have to actually appear in the scene until build() is called. */ virtual void addEntity(Ogre::Entity *ent, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation, const Ogre::Vector3 &scale, const Ogre::ColourValue &color) = 0; virtual void addModel(Ember::OgreView::Model::Model* model, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation, const Ogre::Vector3 &scale, const Ogre::ColourValue &color) {} /** \brief Perform any final steps to make added entities appear in the scene. build() is automatically called right after all the entities have been added with addEntity(). Use this if there are any final steps that need to be performed after addEntity() has been called in order to display the entities. \note This function is not pure virtual, so you don't have to override it if you don't need to. */ virtual void build() {} /** \brief Remove all geometry/entities from the page completely. Make sure this completely reverses the effects of both build() and addEntity(). This is necessary, because after this is called, the entities will most likely be added again with addEntity() and build(). Do not leave any remains of the entities in memory after this function is called. One of the advantages of using paged geometry is that you can have near-infinite game worlds, which would normally exceed a computer's RAM capacity. This advantage would completely disappear if you did not clean up properly when the page manager calls this function. */ virtual void removeEntities() = 0; /** \brief Sets fade behavior for this page. \param enabled Whether or not to enable fading \param visibleDist The distance where geometry will be fully opaque (alpha 1) \param invisibleDist The distance where geometry will be invisible (alpha 0) This is called whenever a page needs fading enabled/disabled. The distance ranges given specify how the final alpha values should be calculated - geometry at visibleDist should have alpha values of 1, while geometry at invisibleDist should have alpha values of 0. Important: Distances must be calculated in the xz plane only - the y coordinate should be disregarded when calculating distance. setFade() won't be called unless the user's computer supports vertex shaders. \note invisibleDist may be greater than or less than visibleDist, depending on whether the geometry is fading out or in to the distance. */ virtual void setFade(bool enabled, Ogre::Real visibleDist = 0, Ogre::Real invisibleDist = 0) = 0; /** \brief Toggle the entire page's visibility. \param visible Whether or not this page should be visible. */ virtual void setVisible(bool visible) = 0; /** \brief Do whatever needs to be done to keep the page geometry up-to-date. update() is called each frame for each GeometryPage instance. This function should perform any operations that are needed to keep the geometry page up-to-date. \note Overriding this function is optional, however, since not all implementations of geometry may need to be updated. */ virtual void update() {} /** \brief Gets the center point of the page. \returns The center points of the page. \note This is a non-virtual utility function common to all GeometryPage classes, don't try to override it. */ inline Ogre::Vector3 &getCenterPoint() { return _centerPoint; } /** \brief Return the current visibility status of the page. \returns The current visibility status of the page. \note This is a non-virtual utility function common to all GeometryPage classes, don't try to override it. */ inline bool isVisible() { return (_visible && _loaded); } /** \brief Advanced: Return the bounding box computed with addEntityToBoundingBox() Advanced: Override this function only if your page implementation already computes a bounding box (local to the page center) for added entities. This way you can prevent the bounding box from being computed twice. When performing fade transitions, the page manager needs to know the actual boundaries of an entire page of entities in order to avoid entities "popping" into view without a smooth transition due to loose grid boundaries. Anyway, as long as this function returns the combined bounding box of all entities added to this page properly, fade transitions should work fairly smoothly. Important: If you implement this function, you must also override addEntityToBoundingBox() and clearBoundingBox() (although you don't need to implement them as long as getBoundingBox() functions as expected). Otherwise the default implementations of these function will be used and therefore result in the bounding box being calculated twice. */ virtual const Ogre::AxisAlignedBox &getBoundingBox(); /** \brief Advanced: Expand the current bounding box to include the given entity Advanced: Override this function only if your page implementation already computes a bounding box (local to the page center) for added entities. This way you can prevent the bounding box from being computed twice. \see getBoundingBox() for important details. */ virtual void addEntityToBoundingBox(Ogre::MovableObject *ent, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation, const Ogre::Vector3 &scale); /** \brief Advanced: Reset the bounding box used by addEntityToBoundingBox() Advanced: Override this function only if your page implementation already computes a bounding box (local to the page center) for added entities. This way you can prevent the bounding box from being computed twice. \see getBoundingBox() for important details. */ virtual void clearBoundingBox(); /** \brief Destructor This is defined here so the destructors of derived classes are called properly. Whether or not you actually implement a destructor is up to you. */ virtual ~GeometryPage() {} /** \brief Constructor Initialise everything to zero, false or NULL except for _trueBoundsUndefined that is set to true. */ GeometryPage(); private: //These values and functions are used by the GeometryPageManager internally. Ogre::Vector3 _centerPoint; //The central point of this page (used to visibility calculation) int _xIndex, _zIndex; //The absolute grid position of this page unsigned long _inactiveTime; //How long this page has been inactive (used to calculate expired pages) bool _visible; //Flag indicating if page is visible bool _fadeEnable; //Flag indicating if page fading is enabled bool _pending; //Flag indicating if page needs loading bool _loaded; //Flag indicating if page is loaded bool _needsUnload; //Flag indicating if page needs unloading before next load bool _keepLoaded; //Flag indicating if the page should not be unloaded // std::list::iterator _iter; //Iterator in loadedList Ogre::AxisAlignedBox _trueBounds; //Actual bounding box of the 3D geometry added to this page bool _trueBoundsUndefined; //Flag indicating if _trueBounds has not been defined yet void *_userData; //Misc. data associated with this page by the PageLoader bool mHasQueryFlag; Ogre::uint32 mQueryFlag; }; //------------------------------------------------------------------------------------- /** \brief Useful page information supplied to a pageloader. When your page loader's loadPage() or unloadPage() is called, you are supplied with a PageInfo variable. This basically tells you what region in space is to be loaded into the page, in addition to some other useful information about that region. \see the PageLoader::loadPage() and PageLoader::unloadPage() documentation for more information. */ struct PageInfo { /** \brief The page boundaries in which all entities should be placed. This specifies the rectangular boundary of the page. Every entity contained in the page should reside within these boundaries.
  • bounds.left is the minimum X coordinate allowed for any entity in the page.
  • bounds.right is the maximum X coordinate allowed for any entity in the page.
  • bounds.top is the minimum Z coordinate allowed for any entity in the page.
  • bounds.bottom is the maximum Z coordinate allowed for any entity in the page.
*/ TBounds bounds; /** \brief The center of the page (simply the middle of the bounds). \note Since there is no way of knowing the elevation of your entities, centerPoint.y will always be defaulted at 0. */ Ogre::Vector3 centerPoint; /** \brief The X index of the page tile. If all the geometry pages were arranged in a big 2D grid, this would be the X index of this page in that grid. This is mathematically equivalent to Math::Floor( bounds.left / bounds.width() ), although this should be used instead due to floating point precision issues which may occur otherwise. */ int xIndex; /** \brief The Z index of the page tile. If all the geometry pages were arranged in a big 2D grid, this would be the Z index of this page in that grid. This is mathematically equivalent to Math::Floor( bounds.top / bounds.height() ), although this should be used instead due to floating point precision issues which may occur otherwise. */ int zIndex; /** \brief Misc. custom data to associate with this page tile. This field can be set in PageLoader::loadPage() to point to custom data allocated during the loading of a page. You can later retreive this data in PageLoader::unloadPage() in order to deallocate the data if desired. \warning After a page is unloaded, userData becomes NULL. Don't attempt to use userData to reference an object longer than the page's life span; anything userData points to should be fully deallocated when PageLoader::unloadPage() is called. */ void *userData; std::vector meshList; }; /** \brief A class which you extend to provide a callback function for loading entities. \note PagedGeometry comes pre-installed with several PageLoader classes, so you probably won't need to create you're own from scratch. See TreeLoader2D, TreeLoader3D, and GrassLoader for more info. Unlike most entity managers, PagedGeometry does not allow you to simply add all your entities to the object, and let the engine run from that point on. Since the PagedGeometry engine is designed to work with extremely large game areas, preloading everything would take far too much memory. Instead, it pages the geometry. In other words, it loads the geometry only as needed. Whenever the engine needs a specific region of geometry to be loaded, it calls on your page loader class's loadPage() function. It's completely up to you how this function loads the entities, just as long as it gets the job done. When loadPage() is called, you are provided with a PageInfo variable, which specifies the boundary which must be loaded (in addition to other useful info). Make sure you don't add anything outside of this boundary, otherwise the results may be unpredictable. Within loadPage(), you add entities by calling addEntity(). Simply supply the entity you want to add (you can add the same entity as many times as you want - in fact, you should do this as much as possible for better performance), and it's position, rotation, and scale (optional). Note that your page loader may be asked to load an area which is out of your world's bounds (if you have any). In this case simply return without adding any entities. To set up a page loader, just make a new sub-class (call it anything you want) of PageLoader. Your class must have one function: \code void loadPage(const PageInfo &page); \endcode Make sure you get the declaration right, otherwise it won't compile. The rest is up to you. You can define your own member functions if that helps, just as long as your loadPage() function does it's job right. Once you've created your PageLoader-derived class, you need to attach it to a PagedGeometry object. To do this, simply create an instance of your class, and call \code pagedGeometry->setPageLoader(yourPageLoader); \endcode Remember: If you ever delete a PagedGeometry object, you will have to delete your page loader yourself (if you want to). The PagedGeometry destructor won't do this for you. */ class PageLoader { public: /** \brief Called right before loading. This allows the loader to perform asynchronous loading by returning false and then later on reloading the page. \return True if the loading should continue, false if it shouldn't. */ virtual bool preparePage(PageInfo &page) { return true; } /** \brief This should be overridden to load a specified region of entities. \param page A PageInfo variable which includes boundary information and other useful values. Override this function to load entities within the specified boundary. The boundary information is contained in the "page" parameter, along with other useful information as well (see the PageInfo documentation for more info about this). Simply use the member function addEntity() to add all the entities you want. If you create your own objects inside this function, you are responsible for deleting it appropriately in unloadPage() or somewhere else. The PageInfo::userData member is useful here since you can point it to your data structures for later reference in unloadPage(). \warning Do not ever add an entity outside of the given region, otherwise this may crash the program (depending on how the current page types handle this situation). \see PagedGeometry::addDetailLevel() for information about page types. */ virtual void loadPage(PageInfo &page) = 0; /** \brief This may be overridden (optional) to unload custom data associated with a page. \param page A PageInfo variable which includes boundary information and other useful values. During a PageLoader::loadPage() call, you are supposed to add entities by calling the addEntity() member function. In case you created anything else (particle systems, sound effects, etc.), this function gives you a chance to delete them along with the rest of the entities added with addEntity(). \note Entities added with addEntity() will automatically be deleted after this function returns, so you don't need to worry about them. In most cases you won't need to implement this function in your page loader at all, since addEntity() is usually all that is used. */ virtual void unloadPage(PageInfo &page) {} /** \brief Provides a method for you to perform per-frame tasks for your PageLoader if overridden (optional) frameUpdate() is completely optional, and unnecessary in most cases. However, if you ever need to update anything in your PageLoader per-frame, this function is here for that purpose. \warning This function is actually called every time PagedGeometry::update() is called, so if the application doesn't call PagedGeometry::update() as it should, this function will not be called either. \note frameUpdate() will be called after PagedGeometry::update() is called but before any GeometryPage's are actually loaded/unloaded for the frame. */ virtual void frameUpdate() {} /** \brief Destructor This is defined here so the destructors of derived classes are called properly. Whether or not you actually implement a destructor is up to you. */ virtual ~PageLoader() {} protected: /** \brief Call this from loadPage() to add an entity to the page being loaded. \param ent The entity to add. You may add the same entity multiple times. \param position The position where the entity will be placed. \param rotation The rotation to apply to the entity. \param scale The scale to apply to the entity. \param color The color to apply to the whole entity \note This copies the entity into the page, so don't make copies of the entity yourself; you may simply add the same entity over and over again. You are also free to destroy any entities you used when you are finished adding them. \warning This does not double-check whether or not your entity is within the proper boundaries (for better performance), so be careful not to add entities out of bounds. Depending on what current page types are being used, an out-of-bounds entity could cause your program to crash. \see PagedGeometry::addDetailLevel() for information about page types. */ void addEntity(Ogre::Entity *ent, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation, const Ogre::Vector3 &scale = Ogre::Vector3::UNIT_SCALE, const Ogre::ColourValue &color = Ogre::ColourValue::White) { geomPage->addEntity(ent, position, rotation, scale, color); geomPage->addEntityToBoundingBox(ent, position, rotation, scale); } void addModel(Ember::OgreView::Model::Model *model, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation, const Ogre::Vector3 &scale = Ogre::Vector3::UNIT_SCALE, const Ogre::ColourValue &color = Ogre::ColourValue::White) { geomPage->addModel(model, position, rotation, scale, color); model->doWithMovables([&](Ogre::MovableObject* movable, int index){ geomPage->addEntityToBoundingBox(movable, position, rotation, scale); }); } private: friend class GeometryPageManager; //Do NOT modify or use this variable - it is used internally by addEntity() GeometryPage *geomPage; }; //------------------------------------------------------------------------------------- /** \brief Manages the rendering of geometry for a detail level type. \warning This class is used internally by PagedGeometry, and in most cases you should ignore it completely. However, this does provide some advanced capabilities such as modifying the near and far view ranges, which may come in handy. This class manages pages of geometry, cacheing, deleting, etc. them as necessary. It analyzes the motion of the camera to determine how fast pages need to be cached, and deletes obsolete pages which have been invisible for a certain amount of time. When you call PagedGeometry::addDetailLevel(), a GeometryPageManager is created to manage the new detail level. addDetailLevel() returns a pointer to this page manager, allowing you access to some useful functions, documented below. \note Some functions (marked by "DO NOT USE" in the documentation) should never be called by you. Only the internal processes of PagedGeometry can safely use these functions, so be careful. Using these functions will cause unpredictable results. */ class GeometryPageManager { public: /** \brief A std::list of pointers to geometry pages */ typedef std::set TPGeometryPages; /** \brief Internal function - DO NOT USE */ GeometryPageManager(PagedGeometry *mainGeom); /** \brief Internal function - DO NOT USE */ ~GeometryPageManager(); /** \brief Sets the near viewing range of this page manager. \param nearRange The distance where this page manager starts displaying geometry. All geometry displayed by this page manager is confined within a certain radius gap from the camera. This function sets the closest distance geometry is allowed near the camera. */ inline void setNearRange(Ogre::Real nearRange) { nearDist = nearRange; nearDistSq = nearDist * nearDist; } /** \brief Sets the far viewing range of this page manager. \param farRange The distance where this page manager stops displaying geometry. All geometry displayed by this page manager is confined within a certain radius gap from the camera. This function sets the farthest distance geometry is allowed from the camera. */ inline void setFarRange(Ogre::Real farRange) { farDist = farRange; farDistSq = farDist * farDist; farTransDist = farDist + fadeLength; farTransDistSq = farTransDist * farTransDist; } /** \brief Gets the near viewing range of this page manager. \returns The near viewing range of this page manager. \see setNearRange() for more info about the near viewing range. */ inline Ogre::Real getNearRange() const { return nearDist; } /** \brief Gets the far viewing range of this page manager. \returns The far viewing range of this page manager. \see setFarRange() for more info about the near viewing range. */ inline Ogre::Real getFarRange() const { return farDist; } /** \brief Customizes the cache behaviour (advanced). \param maxCacheInterval The maximum period of time (milliseconds) before another page is loaded. \param inactivePageLife The maximum period of time (milliseconds) a inactive (invisible) page is allowed to stay loaded. The GeometryPageManager automatically determines how fast pages should be cached to keep everything running as smooth as possible, but there are a few options that are adjustable. This function allows you to adjust these variables to fine-tune cache performance. The maxCacheInterval is basically a minimum rate at which pages are cached. Normally, a stopped camera would cause the cache rate prediction algorithm to say 0 pages-per-second must be cached. However, this is not optimal, since idle time should be taken advantage of to finish loading. By adjusting this value, you can set how much caching you want going on when the camera is stopped or moving very slowly. The inactivePageLife allows you to set how long inactive pages remain in memory. An inactive page is one that is out of the cache range and may not be immediately needed. By allowing these pages to remain in memory for a short period of time, the camera can return to it's previous position with no need to reload anything. \note Even with large inactivePageLife values, pages may be unloaded if the camera moves far enough from them, so setting extremely high inactivePageLife values won't result in massive memory usage. */ inline void setCacheSpeed(unsigned long maxCacheInterval = 200, unsigned long inactivePageLife = 2000) { GeometryPageManager::maxCacheInterval = maxCacheInterval; GeometryPageManager::inactivePageLife = inactivePageLife; } inline void setTransition(Ogre::Real transitionLength) { if (transitionLength > 0) { //Setup valid transition fadeLength = transitionLength; fadeLengthSq = fadeLength * fadeLength; fadeEnabled = true; } else { //<= 0 indicates disabled transition fadeLength = 0; fadeLengthSq = 0; fadeEnabled = false; } farTransDist = farDist + fadeLength; farTransDistSq = farTransDist * farTransDist; } inline Ogre::Real getTransition() const { return fadeLength; } /** \brief Internal function - DO NOT USE */ inline TPGeometryPages getLoadedPages() const { return loadedList; } /** \brief Internal function - DO NOT USE */ template void initPages(const TBounds& bounds, const Ogre::Any &data = Ogre::Any(), Ogre::uint32 queryFlag = 0); /** \brief Internal function - DO NOT USE */ void update(unsigned long deltaTime, Ogre::Vector3 &camPos, Ogre::Vector3 &camSpeed, bool &enableCache, GeometryPageManager *prevManager); /** \brief Internal function - DO NOT USE */ void reloadGeometry(); /** \brief Internal function - DO NOT USE */ void reloadGeometryPage(const Ogre::Vector3 &point, bool forceLoadImmediately); /** \brief Internal function - DO NOT USE */ void reloadGeometryPages(const Ogre::Vector3 ¢er, Ogre::Real radius); /** \brief Internal function - DO NOT USE */ void reloadGeometryPages(const TBounds & area); /** \brief Internal function - DO NOT USE */ void preloadGeometry(const TBounds & area); /** \brief Internal function - DO NOT USE */ void resetPreloadedGeometry(); private: PagedGeometry *mainGeom; //geomGrid is a 2D array storing all the GeometryPage instances managed by this object. GeometryPage **geomGrid; //A dynamic 2D array of pointers (2D grid of GeometryPage's) GeometryPage **scrollBuffer; //A dynamic 1D array of pointers (temporary GeometryPage's used in scrolling geomGrid) int geomGridX, geomGridZ; //The dimensions of the dynamic array TBounds gridBounds; //Current grid bounds //Fade transitions Ogre::Real fadeLength, fadeLengthSq; bool fadeEnabled; //Inline function used to get geometry page tiles inline GeometryPage *_getGridPage(const int x, const int z) { #ifdef _DEBUG if(x >= geomGridX || z >= geomGridZ ) OGRE_EXCEPT(Ogre::Exception::ERR_INVALIDPARAMS, "Grid dimension is out of bounds", "GeometryPageManager::_getGridPage()"); #endif return geomGrid[z * geomGridX + x]; } inline void _setGridPage(const int x, const int z, GeometryPage *page) { #ifdef _DEBUG if(x >= geomGridX || z >= geomGridZ ) OGRE_EXCEPT(Ogre::Exception::ERR_INVALIDPARAMS, "Grid dimension is out of bounds", "GeometryPageManager::_setGridPage()"); #endif geomGrid[z * geomGridX + x] = page; } //Utility functions for loading/unloading geometry pages (see source for detailed descriptions) void _loadPage(GeometryPage *page); void _unloadPage(GeometryPage *page); void _unloadPageDelayed(GeometryPage *page); //Utility function for scrolling pages in the grid by the given amount void _scrollGridPages(int shiftX, int shiftZ); //Timer counting how long it has been since the last page has been cached unsigned long cacheTimer; TPGeometryPages pendingList; //Pages of geometry to be loaded TPGeometryPages loadedList; //Pages of geometry already loaded TPGeometryPages reloadList; //Pages of geometry which should be reloaded //Cache settings unsigned long maxCacheInterval; unsigned long inactivePageLife; //Near and far visibility ranges for this type of geometry Ogre::Real nearDist, nearDistSq; Ogre::Real farDist, farDistSq; Ogre::Real farTransDist, farTransDistSq; //farTransDist = farDist + fadeLength }; //------------------------------------------------------------------------------------- template inline GeometryPageManager* PagedGeometry::addDetailLevel(Ogre::Real maxRange, Ogre::Real transitionLength, const Ogre::Any &data, Ogre::uint32 queryFlag) { //Create a new page manager GeometryPageManager *mgr = new GeometryPageManager(this); //If vertex shaders aren't supported, don't use transitions Ogre::Root *root = root->getSingletonPtr(); //Work-around for Linux compiler bug if (!root->getRenderSystem()->getCapabilities()->hasCapability(Ogre::RSC_VERTEX_PROGRAM)) transitionLength = 0; //Add it to the list (also initializing maximum viewing distance) _addDetailLevel(mgr, maxRange, transitionLength); //And initialize the paged (dependent on maximum viewing distance) mgr->initPages(getBounds(), data, queryFlag); return mgr; } template inline void GeometryPageManager::initPages(const TBounds& bounds, const Ogre::Any &data, Ogre::uint32 queryFlag) { // Calculate grid size, if left is Real minimum, it means that bounds are infinite // scrollBuffer is used as a flag. If it is allocated than infinite bounds are used // !!! Two cases are required because of the way scrolling is implemented // if it is redesigned it would allow to use the same functionality. if(bounds.width() < 0.00001) { // In case of infinite bounds bounding rect needs to be calculated in a different manner, since // it represents local bounds, which are shifted along with the player's movements around the world. geomGridX = (2 * farTransDist / mainGeom->getPageSize()) + 4; gridBounds.top = 0; gridBounds.left = 0; gridBounds.right = geomGridX * mainGeom->getPageSize(); gridBounds.bottom = geomGridX * mainGeom->getPageSize(); // Allocate scroll buffer (used in scrolling the grid) scrollBuffer = new GeometryPage *[geomGridX]; //Note: All this padding and transition preparation is performed because even in infinite //mode, a local grid size must be chosen. Unfortunately, this also means that view ranges //and transition lengths cannot be exceeded dynamically with set functions. } else { //Bounded mode gridBounds = bounds; // In case the devision does not give the round number use the next largest integer geomGridX = std::ceil(gridBounds.width() / mainGeom->getPageSize()); } geomGridZ = geomGridX; //Note: geomGridX == geomGridZ; Need to merge. //Allocate grid array geomGrid = new GeometryPage *[geomGridX * geomGridZ]; int xioffset = Ogre::Math::Floor(gridBounds.left / mainGeom->getPageSize()); int zioffset = Ogre::Math::Floor(gridBounds.top / mainGeom->getPageSize()); for (int x = 0; x < geomGridX; ++x) { for (int z = 0; z < geomGridZ; ++z) { GeometryPage* page = new PageType(); page->init(mainGeom, data); // 0,0 page is located at (gridBounds.left,gridBounds.top) corner of the bounds page->_centerPoint.x = ((x + 0.5f) * mainGeom->getPageSize()) + gridBounds.left; page->_centerPoint.z = ((z + 0.5f) * mainGeom->getPageSize()) + gridBounds.top; page->_centerPoint.y = 0.0f; page->_xIndex = x + xioffset; page->_zIndex = z + zioffset; page->_inactiveTime = 0; page->_loaded = false; page->_needsUnload = false; page->_pending = false; page->_keepLoaded = false; page->_visible = false; page->_userData = 0; page->_fadeEnable = false; page->setQueryFlag(queryFlag); page->clearBoundingBox(); _setGridPage(x, z, page); } } } } #endif