adding geometry LODs

Seregvan

20-06-2008 09:45:33

Hi, can you do this? =) Explaining: PagedGeometry has only Meshes and billboards for now. But I think that will be very usefull to add mesh LODs depends on distance. And this "LOD meshes" should be implemented somewhere, maybe in file or in code.

And this is for example :lol: :

JohnJ

20-06-2008 17:58:56

There are a few ways this could be done, but basically since PagedGeometry primarily uses batching to increase performance, adding multiple LODs would most likely decrease paging performance so much that it wouldn't be worth the extra runtime performance. For example when batching the geometry if there are 2 versions of the same mesh, it will take longer to generate the batches because there's a lot more vertexes and triangles per mesh.

However, it could be practical to implement an EntityPage type that draws the trees with the full per-entity LOD support Ogre provides, and in the distance use batches, then impostors. The reason I haven't done this already is that this would be a slightly different direction than where PagedGeometryis currently headed, and I'm saving these features for a completely rewritten forest renderer, where I won't have to resort to hacky implementations to get it done.

Jules Robichaud Gagnon

25-06-2008 23:45:02

A possibility to implement this quickly would be to make a child to the BatchedPages/BatchedGeometry that would store the data of LOD 1 instead of the mesh. Would need some refactoring and overriding of those classes.

Then you would add two differents detail levels. Such as addDetailLevel<BatchPage> and addDetailLevel<BatchPageLOD1>.

I have no idea of the performance impact but it would be a nice test.

syedhs

26-06-2008 02:47:00

I think that is possible even without PagedGeometry (so there is no need to change PagedGeometry). But since we are in this forum, here is my thought:-

1) While exporting the modelling to .mesh file, include the option to prebuild the low-res mesh into the file (yes lower LOD can be generated automatically).

2) When using PagedGeometry, make sure the PG's lower detail is configured to be of greater distance than your lower LOD. Lower LOD distance can be configured in your material file.

So the end result will be this:-

Full-res mesh -> Low res mesh -> <<Paged Geometry LOD>>

Jules Robichaud Gagnon

26-06-2008 11:21:43

1) While exporting the modelling to .mesh file, include the option to prebuild the low-res mesh into the file (yes lower LOD can be generated automatically). We had some issues with auto-gemerated LOD of trees using billboards a while ago, I think it work only with "closed" mesh. Anyone can confirm this please? :oops:

2) When using PagedGeometry, make sure the PG's lower detail is configured to be of greater distance than your lower LOD. Lower LOD distance can be configured in your material file. Paged Geometry as it is does not store the mesh LOD's data. When making the wind batch page we did not see any mention of the mesh LOD. The page would need to store the LODs meshes as well in the batched geometry when building from the entities. When _updateRenderQueue is called, BatchedGeometry would have to add to the queue the LOD renderable it selected when _notifyCurrentCamera was called.

JohnJ

28-06-2008 02:35:54

That's correct, PagedGeometry currently does not handle LODs at all. To properly implement an LOD system with a combination of entities and batches, the BatchPage system would probably have to be modified to use the lowest LOD of entities, in addition to other changes. Adding per-batch LOD changes would be possible but in most cases completely useless (in addition to the extremely inflated memory requirements, it wouldn't look that good).

P.S. Jules, sorry I missed your message earlier about this post.

syedhs

28-06-2008 03:01:12

Paged Geometry as it is does not store the mesh LOD's data.

Actually what I mean is EntityPage (is still still in the source code?) consist of normal Entities which Ogre provide LOD internally. So the LOD change can occur without Paged Geometry intervention.

Jules Robichaud Gagnon

28-06-2008 13:50:23

Paged Geometry as it is does not store the mesh LOD's data.

Actually what I mean is EntityPage (is still still in the source code?) consist of normal Entities which Ogre provide LOD internally. So the LOD change can occur without Paged Geometry intervention.


Oh sorry, it did not cross my mind. :roll:

Jules Robichaud Gagnon

07-07-2008 01:53:47

Found a solution for LOD

Index: examples/source/Example8.cpp
===================================================================
--- examples/source/Example8.cpp (revision 2471)
+++ examples/source/Example8.cpp (working copy)
@@ -22,7 +22,7 @@
#include "ImpostorPage.h"
#include "TreeLoader2D.h"

-#define WIND
+//#define WIND

//Include "HeightFunction.h", a header that provides some useful functions for quickly and easily
//getting the height of the terrain at a given point.
@@ -252,6 +252,7 @@
trees->addDetailLevel<WindBatchPage>(90, 30); //Use batches up to 150 units away, and fade for 30 more units
#else
trees->addDetailLevel<BatchPage>(90, 30); //Use batches up to 150 units away, and fade for 30 more units
+ trees->addDetailLevel<BatchPage>(190, 30,reinterpret_cast<void*>(1)); //Use batches up to 150 units away, and fade for 30 more units
#endif
trees->addDetailLevel<ImpostorPage>(700, 50); //Use impostors up to 400 units, and for for 50 more units

Index: include/BatchPage.h
===================================================================
--- include/BatchPage.h (revision 2471)
+++ include/BatchPage.h (working copy)
@@ -43,6 +43,7 @@
class BatchPage: public GeometryPage
{
public:
+ BatchPage( void * data = NULL ){ mLODLevel = reinterpret_cast<size_t>(data); }
virtual void init(PagedGeometry *geom);
~BatchPage();

@@ -69,6 +70,7 @@

static unsigned long refCount;
static unsigned long GUID;
+ size_t mLODLevel;

private:
static inline Ogre::String getUniqueID(const Ogre::String &prefix)
Index: include/GrassLoader.h
===================================================================
--- include/GrassLoader.h (revision 2471)
+++ include/GrassLoader.h (working copy)
@@ -521,6 +521,7 @@
class GrassPage: public GeometryPage
{
public:
+ GrassPage( void * data = NULL ){};
void init(PagedGeometry *geom);
~GrassPage();

Index: include/ImpostorPage.h
===================================================================
--- include/ImpostorPage.h (revision 2471)
+++ include/ImpostorPage.h (working copy)
@@ -70,6 +70,7 @@
friend class ImpostorTexture;

public:
+ ImpostorPage( void * data = NULL ){};
void init(PagedGeometry *geom);
~ImpostorPage();

Index: include/PagedGeometry.h
===================================================================
--- include/PagedGeometry.h (revision 2471)
+++ include/PagedGeometry.h (working copy)
@@ -440,7 +440,7 @@
\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);
+ template <class PageType> inline GeometryPageManager* addDetailLevel(Ogre::Real maxRange, Ogre::Real transitionLength = 0, void * data = NULL);

/**
\brief Removes all detail levels from the PagedGeometry object.
@@ -1339,7 +1339,7 @@
inline TPGeometryPages getLoadedPages() const { return loadedList; }

/** \brief Internal function - DO NOT USE */
- template <class PageType> void initPages(const TBounds& bounds);
+ template <class PageType> void initPages(const TBounds& bounds, void * data = NULL);

/** \brief Internal function - DO NOT USE */
void update(unsigned long deltaTime, Ogre::Vector3 &camPos, Ogre::Vector3 &camSpeed, bool &enableCache, GeometryPageManager *prevManager);
@@ -1422,7 +1422,7 @@

//-------------------------------------------------------------------------------------

-template <class PageType> inline GeometryPageManager* PagedGeometry::addDetailLevel(Ogre::Real maxRange, Ogre::Real transitionLength)
+template <class PageType> inline GeometryPageManager* PagedGeometry::addDetailLevel(Ogre::Real maxRange, Ogre::Real transitionLength, void * data )
{
//Create a new page manager
GeometryPageManager *mgr = new GeometryPageManager(this);
@@ -1436,12 +1436,12 @@
_addDetailLevel(mgr, maxRange, transitionLength);

//And initialize the paged (dependent on maximum viewing distance)
- mgr->initPages<PageType>(getBounds());
+ mgr->initPages<PageType>(getBounds(), data);

return mgr;
}

-template <class PageType> inline void GeometryPageManager::initPages(const TBounds& bounds)
+template <class PageType> inline void GeometryPageManager::initPages(const TBounds& bounds, void * data )
{
// 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
@@ -1482,7 +1482,7 @@
{
for (int z = 0; z < geomGridZ; ++z)
{
- GeometryPage* page = new PageType();
+ GeometryPage* page = new PageType(data);
page->init(mainGeom);
// 0,0 page is located at (gridBounds.left,gridBounds.top) corner of the bounds
page->_centerPoint.x = ((x + 0.5f) * mainGeom->getPageSize()) + gridBounds.left;
Index: include/WindBatchPage.h
===================================================================
--- include/WindBatchPage.h (revision 2471)
+++ include/WindBatchPage.h (working copy)
@@ -53,6 +53,7 @@
class WindBatchPage: public BatchPage
{
public:
+ WindBatchPage( void * data ):BatchPage(data){}
void init(PagedGeometry *geom);

protected:
Index: source/BatchPage.cpp
===================================================================
--- source/BatchPage.cpp (revision 2471)
+++ source/BatchPage.cpp (working copy)
@@ -61,7 +61,17 @@

void BatchPage::addEntity(Entity *ent, const Vector3 &position, const Quaternion &rotation, const Vector3 &scale, const Ogre::ColourValue &color)
{
- batch->addEntity(ent, position, rotation, scale, color);
+ if (mLODLevel == 0)
+ batch->addEntity(ent, position, rotation, scale, color);
+ else
+ {
+#ifdef _DEBUG
+ if( ent->getNumManualLodLevels() < mLODLevel )
+ OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,ent->getName() + " entity has less than " + Ogre::StringConverter::toString(mLODLevel) + " manual lod levels","BatchPage::addEntity");
+#endif
+ Ogre::Entity * lod = ent->getManualLodLevel( mLODLevel-1 );
+ batch->addEntity(lod, position, rotation, scale, color);
+ }
}

void BatchPage::build()


Basically I added an extra param to the templates. In the BatchPage I use it to select which manual LOD to put in batch.

Take note that all the entities must at least all have the the same number of ManualLod than the batchpage's config. Take note you will have to create manual LOD on your own. This code alone cause the program to create an exception since the actual meshes has no LOD.

How it is used:

trees->addDetailLevel<BatchPage>(90, 30);
trees->addDetailLevel<BatchPage>(190, 30,reinterpret_cast<void*>(1));

Jules Robichaud Gagnon

07-07-2008 02:43:17

How it is coded, it works with the wind batch page too.

Since most of the time trees are made with billboards, I believe the progressive meshes will not work properly. It is why I made it with the manual meshes.

Jules Robichaud Gagnon

09-07-2008 00:34:08

My previous patch was not working properly, I made a new patch where i also separated the sample test and the real patch.

I added some documentation, changed to choose the "best" LOD available instead of letting Ogre crash and added a warn in debug about the performance warning of using a LOD batch without enough LOD in the entity.

Index: include/BatchPage.h
===================================================================
--- include/BatchPage.h (revision 2471)
+++ include/BatchPage.h (working copy)
@@ -32,7 +32,7 @@

To use this page type, use:
\code
-PagedGeometry::addDetailLevel<BatchPage>(farRange);
+PagedGeometry::addDetailLevel<BatchPage>(farRange, transitionLength, reinterpret_cast<void*>(LODLevel));
\endcode

This page type uses batched geometry (Ogre::StaticGeometry) to represent the entities.
@@ -43,6 +43,7 @@
class BatchPage: public GeometryPage
{
public:
+ BatchPage( void * data = NULL ){ mLODLevel = reinterpret_cast<size_t>(data); }
virtual void init(PagedGeometry *geom);
~BatchPage();

@@ -69,6 +70,7 @@

static unsigned long refCount;
static unsigned long GUID;
+ size_t mLODLevel;

private:
static inline Ogre::String getUniqueID(const Ogre::String &prefix)
Index: include/GrassLoader.h
===================================================================
--- include/GrassLoader.h (revision 2471)
+++ include/GrassLoader.h (working copy)
@@ -521,6 +521,7 @@
class GrassPage: public GeometryPage
{
public:
+ GrassPage( void * data = NULL ){};
void init(PagedGeometry *geom);
~GrassPage();

Index: include/ImpostorPage.h
===================================================================
--- include/ImpostorPage.h (revision 2471)
+++ include/ImpostorPage.h (working copy)
@@ -70,6 +70,7 @@
friend class ImpostorTexture;

public:
+ ImpostorPage( void * data = NULL ){};
void init(PagedGeometry *geom);
~ImpostorPage();

Index: include/PagedGeometry.h
===================================================================
--- include/PagedGeometry.h (revision 2471)
+++ include/PagedGeometry.h (working copy)
@@ -358,6 +358,7 @@
\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 Extra parameters that can be used in the constructor of a PageType - 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.
@@ -440,7 +441,7 @@
\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);
+ template <class PageType> inline GeometryPageManager* addDetailLevel(Ogre::Real maxRange, Ogre::Real transitionLength = 0, void * data = NULL);

/**
\brief Removes all detail levels from the PagedGeometry object.
@@ -1339,7 +1340,7 @@
inline TPGeometryPages getLoadedPages() const { return loadedList; }

/** \brief Internal function - DO NOT USE */
- template <class PageType> void initPages(const TBounds& bounds);
+ template <class PageType> void initPages(const TBounds& bounds, void * data = NULL);

/** \brief Internal function - DO NOT USE */
void update(unsigned long deltaTime, Ogre::Vector3 &camPos, Ogre::Vector3 &camSpeed, bool &enableCache, GeometryPageManager *prevManager);
@@ -1422,7 +1423,7 @@

//-------------------------------------------------------------------------------------

-template <class PageType> inline GeometryPageManager* PagedGeometry::addDetailLevel(Ogre::Real maxRange, Ogre::Real transitionLength)
+template <class PageType> inline GeometryPageManager* PagedGeometry::addDetailLevel(Ogre::Real maxRange, Ogre::Real transitionLength, void * data )
{
//Create a new page manager
GeometryPageManager *mgr = new GeometryPageManager(this);
@@ -1436,12 +1437,12 @@
_addDetailLevel(mgr, maxRange, transitionLength);

//And initialize the paged (dependent on maximum viewing distance)
- mgr->initPages<PageType>(getBounds());
+ mgr->initPages<PageType>(getBounds(), data);

return mgr;
}

-template <class PageType> inline void GeometryPageManager::initPages(const TBounds& bounds)
+template <class PageType> inline void GeometryPageManager::initPages(const TBounds& bounds, void * data )
{
// 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
@@ -1482,7 +1483,7 @@
{
for (int z = 0; z < geomGridZ; ++z)
{
- GeometryPage* page = new PageType();
+ GeometryPage* page = new PageType(data);
page->init(mainGeom);
// 0,0 page is located at (gridBounds.left,gridBounds.top) corner of the bounds
page->_centerPoint.x = ((x + 0.5f) * mainGeom->getPageSize()) + gridBounds.left;
Index: include/WindBatchPage.h
===================================================================
--- include/WindBatchPage.h (revision 2471)
+++ include/WindBatchPage.h (working copy)
@@ -53,6 +53,7 @@
class WindBatchPage: public BatchPage
{
public:
+ WindBatchPage( void * data ):BatchPage(data){}
void init(PagedGeometry *geom);

protected:
Index: source/BatchPage.cpp
===================================================================
--- source/BatchPage.cpp (revision 2471)
+++ source/BatchPage.cpp (working copy)
@@ -24,6 +24,7 @@
#include <OgreRenderSystemCapabilities.h>
#include <OgreHighLevelGpuProgram.h>
#include <OgreHighLevelGpuProgramManager.h>
+#include <OgreLogManager.h>
using namespace Ogre;

namespace Forests {
@@ -61,7 +62,22 @@

void BatchPage::addEntity(Entity *ent, const Vector3 &position, const Quaternion &rotation, const Vector3 &scale, const Ogre::ColourValue &color)
{
- batch->addEntity(ent, position, rotation, scale, color);
+ const size_t numManLod = ent->getNumManualLodLevels();
+
+#ifdef _DEBUG
+ //Warns if using LOD batch and entities does not have enough LOD support.
+ if ( mLODLevel > 0 && numManLod < mLODLevel )
+ Ogre::LogManager::getSingleton().logMessage( "BatchPage::addEntity: " + ent->getName() + " entity has less than " + Ogre::StringConverter::toString(mLODLevel) + " manual lod level(s). Performance warning.");
+#endif
+
+ if (mLODLevel == 0 || numManLod == 0)
+ batch->addEntity(ent, position, rotation, scale, color);
+ else
+ {
+ const size_t bestLod = (numManLod<mLODLevel-1)?numManLod:(mLODLevel-1);
+ Ogre::Entity * lod = ent->getManualLodLevel( bestLod );
+ batch->addEntity(lod, position, rotation, scale, color);
+ }
}

void BatchPage::build()



Here's a patch to test at mixing the LODs:

Index: examples/source/Example8.cpp
===================================================================
--- examples/source/Example8.cpp (revision 2471)
+++ examples/source/Example8.cpp (working copy)
@@ -22,7 +22,7 @@
#include "ImpostorPage.h"
#include "TreeLoader2D.h"

-#define WIND
+//#define WIND

//Include "HeightFunction.h", a header that provides some useful functions for quickly and easily
//getting the height of the terrain at a given point.
@@ -251,9 +251,10 @@
//WindBatchPage is a variation of BatchPage which includes a wind animation shader
trees->addDetailLevel<WindBatchPage>(90, 30); //Use batches up to 150 units away, and fade for 30 more units
#else
- trees->addDetailLevel<BatchPage>(90, 30); //Use batches up to 150 units away, and fade for 30 more units
+ trees->addDetailLevel<BatchPage>(60, 0); //Use batches up to 150 units away, and fade for 30 more units
+ trees->addDetailLevel<BatchPage>(190, 0,reinterpret_cast<void*>(1)); //Use batches up to 150 units away, and fade for 30 more units
#endif
- trees->addDetailLevel<ImpostorPage>(700, 50); //Use impostors up to 400 units, and for for 50 more units
+ //trees->addDetailLevel<ImpostorPage>(700, 50); //Use impostors up to 400 units, and for for 50 more units

//Create a new TreeLoader2D object
TreeLoader2D *treeLoader = new TreeLoader2D(trees, TBounds(0, 0, 1500, 1500));
@@ -270,7 +271,8 @@
//does. This is because the bounds you specify in the TreeLoader2D constructor are used to apply
//the color map.
treeLoader->setColorMap("terrain_lightmap.jpg");
-
+ Ogre::MeshPtr mesh = Ogre::MeshManager::getSingleton().load("fir05_30.mesh", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
+ mesh->createManualLodLevel(500,"fir14_25.mesh");
//Load a tree entity
Entity *tree1 = sceneMgr->createEntity("Tree1", "fir05_30.mesh");

Jules Robichaud Gagnon

15-07-2008 11:30:26

JohnJ committed a new version of the patch for the LODs. It is used as follow:

pagedGeometry->addDetailLevel<BatchPage>(farRange, transitionLength, Ogre::Any(LODLevel));

0 uses the regular entity
1 uses index 0 of manual LOD
2 uses index 1 of manual LOD
etc.

As mentioned above, it will choose the closest LOD available and warn in debug's log if a given entity does not possess the exact LOD of the Batch.

We changed to use Ogre::Any instead of a void * to be more user friendly. The type cast is not by the user this way.

Enjoy!