OgreDecal        

decal.jpg
Current Maintainer: fd9_
Project: External OGRE Project
Type: OGRE Library
License: zlib
Supported Platforms: Windows / Linux / Mac OS X
Sources: Github Repository | Direct Source Download
Forum Thread: Link to forum

Description: OgreDecal is a mesh decal generator which should support any triangle mesh and/or scene manager. Unlike the Projective Decal Tutorial, the decals are mesh-based and hence do not require any extra passes to render them, reducing the run-time cost considerably.

How To Use


Simply download the source and include OgreDecal.h.

Generation of a decal requires four parameters:

  • The mesh to project onto (pointer to a TriangleMesh object)
  • The center position of the projection box
  • The width and height of the projected decal
  • The material to use for the decal


The first and most important parameter to understand is the mesh object. Typically this will be your world object and can therefore be very large, probably consisting of a large number of triangles. The algorithm will need to collect all of the triangles within the bounding box area you wish to project a decal onto. Because of this, any object you pass in must derive from TriangleMesh, which is an abstract class with a single virtual function called findTrianglesInAABB(). By default, you can use the OgreMesh object, which uses a brute-force test to check each triangle against the AABB. Below is a complete example using OgreMesh (in the example, it is assumed that the decal is projected onto the mesh by means of casting a ray from the camera into the scene).

/// This is the mesh object that we will pass to the decal generator.
/// Do not create any more than one OgreMesh per mesh, even if you have multiple instances of the same mesh in your scene.
OgreDecal::OgreMesh worldMesh;

/// This method will extract all of the triangles from the mesh to be used later. Only should be called once.
/// If you scale your mesh at all, pass it in here.
worldMesh.initialize( entity->getMesh(), 1.0 );

/// Get the DecalGenerator singleton and initialize it
OgreDecal::DecalGenerator& generator = OgreDecal::DecalGenerator::getSingleton();
generator.initialize( sceneMgr );

/// Set Decal parameters:
Ogre::Vector3 pos = getRaycastPoint(); /// Send a ray into the mesh
float width = 10;
float height = 10;
std::string textureName = "MyTexture"; /// Make sure your texture has a depth_bias greater than 1 to avoid z-fighting

/// We have everything ready to go. Now it's time to actually generate the decal:
OgreDecal::Decal decal = generator.createDecal( &worldMesh, pos, width, height, textureName );

/// Render the decal object. Always verify the returned object - it will be NULL if no decal could be created.
if (decal.object)
{
    sceneMgr->getRootSceneNode()->createChildSceneNode()->attachObject( decal.object );
}


However, you can also derive your own class from TriangleMesh and implement findTrianglesInAABB() yourself. This gives you several advantages over using the OgreMesh object. For example, your world may consist of more than one mesh. If so, you can derive your own class to lookup triangles within each one. In addition, you may be using a physics engine that has optimizations which allows you to find triangles quicker. I have provided below an example using the Bullet physics engine, using the processAllTriangles() function. Note: Bullet actually performs a triangle bounding box test, which is faster, but also means that there can be many false positives. Because of this, you must test each triangle with collide_triangle_exact() before saving it. If you plan on using a different physics engine, be sure to check the documentation to see if this applies.

Example class using the Bullet physics engine
class BulletMesh : public OgreDecal::TriangleMesh, public btTriangleCallback
{
public:
    
    BulletMesh()
    {
        mMesh = 0;
        mTriangles = 0;
    }
    
    BulletMesh( btConcaveShape* mesh )
    {
        setMesh( mesh );
        mTriangles = 0;
    }
    
    void setMesh( btConcaveShape* mesh )
    {
        mMesh = mesh;
    }
    
    void processTriangle( btVector3* triangle, int partId, int triangleIndex)
    {
        /// Convert Bullet vectors to Ogre vectors
        Ogre::Vector3 v1( triangle[0].getX(), triangle[0].getY(), triangle[0].getZ() );
        Ogre::Vector3 v2( triangle[1].getX(), triangle[1].getY(), triangle[1].getZ() );
        Ogre::Vector3 v3( triangle[2].getX(), triangle[2].getY(), triangle[2].getZ() );
        
        OgreDecal::Triangle t( v1, v2, v3 );
        
        /// Check for false-positive
        if (OgreDecal::collide_triangle_exact( mAABBMin, mAABBMax, t ))
            mTriangles->push_back( t );
    }
    
    void findTrianglesInAABB( const Ogre::Vector3& aabbMin, const Ogre::Vector3& aabbMax, std::vector< OgreDecal::Triangle >& triangles )
    {
        mTriangles = &triangles;
        mTriangles->clear();
        
        mAABBMin = aabbMin;
        mAABBMax = aabbMax;
        
         if (mMesh)
            mMesh->processAllTriangles( this, OgreToBullet(aabbMin), OgreToBullet(aabbMax) );
        
        /// We no longer need to point to the triangle list
        mTriangles = 0;
    }
    
private:
    
    Ogre::Vector3 mAABBMin, mAABBMax;
    std::vector< OgreDecal::Triangle >* mTriangles;
    btConcaveShape* mMesh;
    
};

Dynamic Decals


If you are generating a new decal per frame with the same material, it is highly recommended to re-use the same decal object. To do this, pass in the decal object as the last parameter when calling createDecal(). This way, you're updating the existing manual object instead of creating a new one each frame.

This can also be useful anytime you want to update an existing decal, instead of creating a new one. Note: when updating an existing decal, the material cannot be changed.

Debug Drawing


decal_debug.jpg

Debug drawing will render the vertices and triangles of each decal. To enable it, set DEBUG_ENABLED to true at the top of OgreDecal.cpp. Then call turnDebugOn(). You'll also need to add Debug.material to your program.

/// At the top of OgreDecal.cpp, this is required:
const bool DEBUG_ENABLED = true;

OgreDecal::DecalGenerator::getSingleton().turnDebugOn();

/// Alternatively, you can flip the debug state on/off:
OgreDecal::DecalGenerator::getSingleton().flipDebug();

Limitations


1) Decal projection is an approximation and will not always produce desired results. Stretching and cropping will sometimes occur, depending on the geometry. Any contributions to make the algorithm more robust is welcomed.

2) Currently does not properly support disjointed triangles, back facing triangles which are not culled, or any enclosed geometry which is smaller than the projection box. For example, consider thin surfaces which have opposite facing triangles, like a wall or a floor that is not very thick. The algorithm makes no effort to distinguish one side from the other, so it will simply attempt to project onto both faces. In other words, it uses all triangles found within the projection box. Another example is a small room filled with objects, where your projection box is large enough to encompass both the wall and the objects inside. One idea is to provide an optional parameter that specifies a normal (could possibly be the ray vector negated, or the normal of the triangle closest to the center position) and a rotational deviation limit/threshold which would ignore any triangles that are "facing away", as determined by the threshold. While this approach may fix the thin wall scénario, it would still not correct for the room example. In other words, it can quickly escalate in complexity.

3) Currently, the OgreMesh class only supports static meshes with their default orientation and position unchanged, for sake of simplicity. If you can't get your decals to work, make sure that you haven't reset the position or orientation of your mesh at runtime, including initialization.

4) Any decals which are generated on top of eachother will have z-fighting. To overcome this, you'll need to change the material of one the decals to have a different depth_bias.