iDevGames Forums
Decals on terrain, objects etc. - Printable Version

+- iDevGames Forums (http://www.idevgames.com/forums)
+-- Forum: Development Zone (/forum-3.html)
+--- Forum: Graphics & Audio Programming (/forum-9.html)
+--- Thread: Decals on terrain, objects etc. (/thread-3290.html)



Decals on terrain, objects etc. - IBethune - May 10, 2007 04:56 AM

I'm wanting to implement a decal system in my game so that explosions, bullets etc. will leave marks on the terrain or other objects they impact. Having trouble finding much through google on this subject, but I'm pretty sure someone here will have implemented this before, or have a link to some decent tutorials.

A few questions I had were:

Currently the objects I'd like to stick decals on are a heightmap mesh (no overhangs) and other 3D objects scattered around the scene. Is it easier to combine all these into one big mesh?

How would I do decalling where the decal is on the heightmap, but it also overlaps onto another object?

Thanks in advance,

- iain


Decals on terrain, objects etc. - TomorrowPlusX - May 10, 2007 10:26 AM

I've done this in the past.

First, you need to have some sort of collision detection that can give you triangles which intersect, say, a sphere. So when you blow up a grenade or whatever, you can get a list of triangles which intersect the spherical radius of "damage".

Then, you'll do a sort of projected texturing. When I did this, I picked a single "normal" and "center" for the decal triangles ( I think I averaged their normals and centroids ) and using that I was able to assign texture coordinates to the triangles radially.

Then, I just used glPolygonOffset and rendered the triangle list after rendering the solid geometry.

It works pretty well for convex geometry. When dealing with non-convex, it still works, but you can get some oddities. In retrospect, I would have been smart to only apply the decal triangles if they "faced" the explosion.

I've got source code if you want to see.


Decals on terrain, objects etc. - IBethune - May 11, 2007 12:33 AM

A look at your code would be very helpful.

You can either post it here or mail it to me iain < at> pyramid <dash> productions <dot> net

Cheers

- Iain


Decals on terrain, objects etc. - TomorrowPlusX - May 11, 2007 07:44 AM

Here you go:

http://zakariya.net/shamyl/etc/Decals/

I hadn't looked at the code in a long time, so I was a little wrong about how I generated texture coords. It turns out I was using GL's texgen facilities, but it's easy enough to wrangle meaningfully.

The relevant code is here:
Code:
Decal::Decal( TrimeshTriangleVec &triangles, GLuint textureID, float scale,
              vec3 center, vec3 normal, float lifespan, vec4 color, vec3 referenceY ):
    VisibilityListener( false, false ),
    _triangleArray(0), _nTriangles(0),
    _textureID( textureID ),
    _scale( scale ), _lifespan( lifespan ), _age(0),
    _center( center ), _normal( normal ), _color( color ),
    _expired( false ),
    _family(0)
{
    /*
        Add to scene graph
    */
    place( GameWorld::instance()->stage()->sceneGraph(), AABB( center, scale * 0.5f ));

    /*
        Copy triangles to triangle array, and generate an AABB
    */

    _nTriangles = triangles.size();
    _triangleArray = new TrimeshTriangle[ _nTriangles ];
    for ( int i = 0; i < _nTriangles; i++ )
    {
        _triangleArray[i] = triangles[i];        
    }
    
    vec3 origin( _center + _normal );
    vec3 pointOnTexturePlane( _center - origin );
    
    pointOnTexturePlane.normalize();
    pointOnTexturePlane += _center;

    /*
        Get s & t axes for this plane -- the plane faces
        the center
    */
    vec3 projectionDirection( _center - pointOnTexturePlane );
    projectionDirection.normalize();
    
    vec3 sAxis, tAxis;
    
    sAxis.cross( projectionDirection, referenceY );
    sAxis.normalize();
    
    tAxis.cross( sAxis, projectionDirection );
    tAxis.normalize();
    
    /*
        Get plane equation for the texture plane
    */
    vec4 texturePlaneEquation( calculatePlaneEquation( pointOnTexturePlane, pointOnTexturePlane + sAxis, pointOnTexturePlane + tAxis ));


    /*
        Calculate the origin of the plane
    */
    vec3 planeOrigin( texturePlaneEquation );
    planeOrigin *= -texturePlaneEquation.w;


    /*
        Nor calculate the position of pointOnTexturePlane in coordinates
        relative to the texture plane, 2D.
    */
    vec3 normalizedClosestProjection( pointOnTexturePlane - planeOrigin );
    float hypLength = normalizedClosestProjection.normalize();
    float sOffset = 0, tOffset = 0, rScale = 1.0f / _scale;
    
    if ( hypLength > 0.001f )
    {
        sOffset = ( normalizedClosestProjection * sAxis ) * hypLength;
        tOffset = ( normalizedClosestProjection * tAxis ) * hypLength;
    }
    
    /*
        Now scale the axes and the s & t positions, subtraction of 0.5
        centers the texture.
    */
                
    sOffset *= rScale;
    tOffset *= rScale;
    
    sOffset -= 0.5f;
    tOffset -= 0.5f;
                
    vec3 scaledSAxis( sAxis * rScale ),
         scaledTAxis( tAxis * rScale );

    _textureSAxis = vec4( scaledSAxis.x, scaledSAxis.y, scaledSAxis.z, -sOffset );
    _textureTAxis = vec4( scaledTAxis.x, scaledTAxis.y, scaledTAxis.z, -tOffset );
    
}

And the rendering code:
Code:
void Decal::displaySecondPass( float deltaT )
{
    if ( hasExpired( deltaT )) return;
        
    /*
        Setup
    */

    glDisable( GL_LIGHTING );
    glDepthMask( GL_FALSE );

    glEnable( GL_BLEND );
    glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );

    glEnable( GL_ALPHA_TEST );
    glAlphaFunc( GL_GREATER, 0.01f );
    
    glDepthFunc( GL_LEQUAL );
            
    PolygonOffset::push( 0, -10 );

    glEnable( GL_TEXTURE_2D );
    glBindTexture( GL_TEXTURE_2D, _textureID );
    glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );

    glEnable(GL_TEXTURE_GEN_S);
    glEnable(GL_TEXTURE_GEN_T);
    glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR );
    glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR );

    glEnableClientState( GL_VERTEX_ARRAY );

    setDisplayVA();

    /*
        Teardown
    */
    glDisableClientState( GL_VERTEX_ARRAY );

    glEnable( GL_LIGHTING );
    glDepthMask( GL_TRUE );
    glDisable( GL_BLEND );
    glDisable( GL_ALPHA_TEST );
    glDisable( GL_TEXTURE_GEN_S );
    glDisable( GL_TEXTURE_GEN_T);
    glDisable( GL_TEXTURE_2D );    

    PolygonOffset::pop();
}

void Decal::setDisplayVA( void )
{
    /*
        Give TexGen our axes and offsets
    */
    glTexGenfv( GL_S, GL_OBJECT_PLANE, _textureSAxis.v );
    glTexGenfv( GL_T, GL_OBJECT_PLANE, _textureTAxis.v );

    /*
        Assign a color with alpha approaching zero as
        age approaches lifespan.
    */

    vec4 color( _color );

    if ( _lifespan > 0 )
    {
        color.w *= 1.0f - ( _age / _lifespan );
    }
    
    if ( LOD == 1 )
    {
        color.w *= 0.5f;
    }
    else if ( LOD >= 2 )
    {
        color.w *= 0.25f;    
    }
    
    glColor4fv( color );        

    glVertexPointer( 3, GL_FLOAT, 0, _triangleArray );
    glDrawArrays( GL_TRIANGLES, 0, _nTriangles * 3 );
}