ember/external/pagedgeometry/include/PagedGeometry.h
2019-12-26 21:32:58 +01:00

1674 lines
68 KiB
C++

/*-------------------------------------------------------------------------------------
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 <a href="http://www.ogre3d.org">OGRE
Graphics Engine</a>, 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
<ul>
<li><b>John Judnich</b> - <i>Programming / design / documentation</i></li>
<li><b><a href="http://sjcomp.com">Alexander Shyrokov</a></b> (aka. sj) - <i>Testing / co-design</i></li>
<li><b><a href="http://www.pop-3d.com">Tuan Kuranes</a></b> - <i>Imposter image render technique</i></li>
<li><b></b> (Falagard) - <i>Camera-facing billboard vertex shader</i></li>
<li><b><a href="http://www.wendigostudios.com/">Wendigo Studios</a></b> - <i>Tree animation code & various patches/improvements</i></li>
<li><b><a href="http://www.thomasfischer.biz/">Thomas Fischer</a></b> - <i>Maintainer from Jun/2009</i></li>
</ul>
\section D License
<b>Copyright (c) 2007 John Judnich</b>
<i>
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:
<b>1.</b> 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.
<b>2.</b> Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
<b>3.</b> This notice may not be removed or altered from any source distribution.
</i>
*/
//--------------------------------------------------------------------------------------
// 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 <limits> // numeric_limits<>
#include <memory>
#include <set>
#include <OgreRoot.h>
#include <OgrePrerequisites.h>
#include <OgreRenderSystem.h>
#include <OgreEntity.h>
#include <OgreCommon.h>
#include <OgreCamera.h>
#include <OgreVector.h>
#include <OgreTimer.h>
#include <OgreMesh.h>
#include <components/ogre/model/Model.h>
namespace Forests {
class GeometryPageManager;
class PageLoader;
/// Define TBounds as a TRect using Real numeric units.
typedef Ogre::TRect<Ogre::Real> 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<Real>, 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<PageType>(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 "<PageType>". 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<BatchPage>(100); //Use batched geometry from 0-100
pagedTrees->addDetailLevel<ImpostorPage>(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<BatchPage>(100, 50);
//Use impostors from 100-500
pagedTrees->addDetailLevel<ImpostorPage>(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<BatchPage>(100, 50);
//Use impostors from 100-400, and fade out for 100 units beyond that
pagedTrees->addDetailLevel<ImpostorPage>(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 <class PageType> 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<GeometryPageManager*> &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 &center, 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<GeometryPageManager*> 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<std::string, float> 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<GeometryPage*>::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.
<ul>
<li>bounds.left is the minimum X coordinate allowed for any entity in the page.</li>
<li>bounds.right is the maximum X coordinate allowed for any entity in the page.</li>
<li>bounds.top is the minimum Z coordinate allowed for any entity in the page.</li>
<li>bounds.bottom is the maximum Z coordinate allowed for any entity in the page.</li>
</ul>
*/
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<Ogre::Mesh*> 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<GeometryPage*> 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 <class PageType> 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 &center, 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 <class PageType> 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<PageType>(getBounds(), data, queryFlag);
return mgr;
}
template <class PageType> 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