File: volfog.c

/****************************************************/
/* Volumetric fog                                   */
/* An OpenGL demo by A.P.Gardner                    */
/* ported to Cpw by Jim Mathies                     */
/****************************************************/

/* 
  Cpw link options:

  #define CPW_EXTERN        - link to the static lib
  #define CPWDLL_EXTERN     - link to the release dll's stub
  #define CPWDLLDBG_EXTERN  - link to the debug dll's stub
  #define CPWEXTDLL_EXTERN  - link to the release dll with cpw addons 
*/

#define CPWDLL_EXTERN

#include <cpw.h>
#include "../../library/addons/glextensions/cpwext_glextensions.h"

/* extension support info */

static GLExtensionSupport glExtInfo;

/* definitions */

#define NUM_PANELS 16	/* number of panels in scene */

/* vertex data */

typedef struct
{
	float x;
	float y;
	float z;
} world_vector;

/* vertex data for the panels */

world_vector vertices[NUM_PANELS*4] = {
	{-15.0, 10.0, 0.0},{-5.0, 10.0, 0.0},{-5.0, 20.0, 0.0},{-15.0, 20.0, 0.0}, /* back wall */
	{-5.0, 10.0, 0.0},{5.0, 10.0, 0.0},{5.0, 20.0, 0.0},{-5.0, 20.0, 0.0},
	{5.0, 10.0, 0.0},{15.0, 10.0, 0.0},{15.0, 20.0, 0.0},{5.0, 20.0, 0.0},
	{-15.0, 0.0, 0.0},{-5.0, 0.0, 0.0},{-5.0, 10.0, 0.0},{-15.0, 10.0, 0.0},
	{-5.0, 0.0, 0.0},{5.0, 0.0, 0.0},{5.0, 10.0, 0.0},{-5.0, 10.0, 0.0},
	{5.0, 0.0, 0.0},{15.0, 0.0, 0.0},{15.0, 10.0, 0.0},{5.0, 10.0, 0.0},
	{-5.0, -10.0, 0.0},{5.0, -10.0, 0.0},{5.0, 0.0, 0.0},{-5.0, 0.0, 0.0}, /* foggy bit */
	{-5.0, -10.0, 10.0},{-5.0, -10.0, 0.0},{-5.0, 0.0, 0.0},{-5.0, 0.0, 10.0},
	{-5.0, -10.0, 20.0},{-5.0, -10.0, 10.0},{-5.0, 0.0, 10.0},{-5.0, 0.0, 20.0},
	{5.0, -10.0, 0.0},{5.0, -10.0, 10.0},{5.0, 0.0, 10.0},{5.0, 0.0, 0.0},
	{5.0, -10.0, 10.0},{5.0, -10.0, 20.0},{5.0, 0.0, 20.0},{5.0, 0.0, 10.0},
	{-5.0, -10.0, 10.0},{5.0, -10.0, 10.0},{5.0, -10.0, 0.0},{-5.0, -10.0, 0.0},
	{-15.0, 0.0, 10.0},{-5.0, 0.0, 10.0},{-5.0, 0.0, 0.0},{-15.0, 0.0, 0.0}, /* floor */
	{5.0, 0.0, 10.0},{15.0, 0.0, 10.0},{15.0, 0.0, 0.0},{5.0, 0.0, 0.0},
	{-15.0, 0.0, 20.0},{-5.0, 0.0, 20.0},{-5.0, 0.0, 10.0},{-15.0, 0.0, 10.0},
	{5.0, 0.0, 20.0},{15.0, 0.0, 20.0},{15.0, 0.0, 10.0},{5.0, 0.0, 10.0}
};

/* texture references for the panels */

int panel_texture[NUM_PANELS] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0};

/* area references for the panels */

int fog_area[NUM_PANELS] = {0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0};

/* mode flag */

static int fog = 1;

/* fog color */

static float red = (float)0.6;
static float green = (float)0.25;

/* store of texture references */

static GLuint texName[2];
static CpwImage images[2];

/****************************************************/
/*  Load texture function                           */
/****************************************************/

bool registerTexture( pCpw cpw, char * filename, uint_32 id )
{
    /* load the bitmap */

    if ( !cpwLoadImage( cpw, &images[id], CPW_IMAGEFORMAT_BMP, filename, true ) ) {
        printf( "File not found: %s\n", filename );
        return false;
    }

	  /* create texture object */

	  glBindTexture( GL_TEXTURE_2D, texName[id] );

	  /* set up wrap & filter params */

	  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
	  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

	  /* load the texture into gl */

	  glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, images[id].width, images[id].height,
				   0, GL_RGB, GL_UNSIGNED_BYTE, images[id].data );

    return true;
}

/****************************************************/
/*  Init                                            */
/****************************************************/

bool init(pCpw cpw)
{
	  GLfloat fogColor[4] = {red, green, 0.0, 1.0};

	  /* set background colour */

	  glClearColor(0.0, 0.0, 0.0, 0.0);

	  /* initialise viewing parameters */

	  glMatrixMode(GL_PROJECTION);
	  glLoadIdentity();
    gluPerspective(50, 1.0, 0.2, 40);
	  glMatrixMode(GL_MODELVIEW);
	  glLoadIdentity();

	  /* generate texture names */

	  texName[0] = 0;
	  texName[1] = 0;
	  glGenTextures( 2, texName );

	  /* load textures */

	  if ( !registerTexture( cpw, "textures/floor.bmp", 0 ) ) return false;

	  if ( !registerTexture( cpw, "textures/wall2.bmp", 1 ) ) return false;

	  /* enable texturing & set texturing function */

	  glEnable(GL_TEXTURE_2D);
	  glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );

    /* init gl extensions we might use if they exists */

    cpwextInitOpenGLExtensions( cpw, &glExtInfo );

    if ( glExtInfo.EXT_fog_coord ) {

        printf( "Using EXT_fog_coord extension with OpenGL fog.\n" );

        glEnable( GL_FOG );
        glFogi( GL_FOG_MODE, GL_LINEAR );
        glFogfv( GL_FOG_COLOR, fogColor );
        glFogf( GL_FOG_START, 0.0 );
        glFogf( GL_FOG_END, 10.0 );
        glFogi( GL_FOG_COORDINATE_SOURCE_EXT, GL_FOG_COORDINATE_EXT );

    } else {

        printf( "No EXT_fog_coord support, using alpha fog.\n" );

    }

    return true;
}

/****************************************************/
/*  Cleanup texture memory                          */
/****************************************************/

void cleanup(void)
{
	  /* delete texture objects */

	  glDeleteTextures(2, texName);
}

/****************************************************/
/*  Fog color per vertex                            */
/****************************************************/

void set_fog_color_ext(int p, int v)
{
    float z;	/* distance of vertex from edge of fog volume */

    /* calculate depth through fog */

    if (fog_area[p]) {
	    z = (float)0.0 - vertices[v].y;
    } else {
	    z = 0.0;
    }

    /* set depth for this coord */

    glFogCoordfEXT(z);
}

void set_fog_color_alpha(int v)
{
	  float z;	/* distance of vertex from edge of fog volume */
	  float f;	/* fog factor */

	  z = (float)0.0 - vertices[v].y;

    /* linear fog: f = (end - z)/(end - start) */
	  f = ((float)10.0 - z) / ((float)10.0 - (float)0.0);

    if ( fog == 2 ) glColor4f( red, green, 0.0, 1.0 );
    else glColor4f( red, green, 0.0, f );
}

/****************************************************/
/*  Draw a panel                                    */
/****************************************************/

void draw_panel_ext(int ref)
{
    int v;		/* store of next vertex ref */

    /* bind texture */

    glBindTexture(GL_TEXTURE_2D, texName[panel_texture[ref]]);

    /* draw panel */

    v = ref * 4;
    glBegin(GL_QUADS);
	    set_fog_color_ext(ref, v);
	    glTexCoord2f(0.0, 0.0);
	    glVertex3f(vertices[v].x, vertices[v].y, vertices[v].z); v++;
	    set_fog_color_ext(ref, v);
	    glTexCoord2f(1.0, 0.0);
	    glVertex3f(vertices[v].x, vertices[v].y, vertices[v].z); v++;
	    set_fog_color_ext(ref, v);
	    glTexCoord2f(1.0, 1.0);
	    glVertex3f(vertices[v].x, vertices[v].y, vertices[v].z); v++;
	    set_fog_color_ext(ref, v);
	    glTexCoord2f(0.0, 1.0);
	    glVertex3f(vertices[v].x, vertices[v].y, vertices[v].z);
    glEnd();
}

void draw_panel_alpha(int ref)
{
	  int v;		/* store of next vertex ref */

	  /* disable writes to z buffer (if fog area) */

	  if (fog_area[ref])
	  {
		  glDepthMask(GL_FALSE);
	  }

	  /* bind texture */

	  glBindTexture(GL_TEXTURE_2D, texName[panel_texture[ref]]);

	  /* draw the panel */

	  v = ref * 4;
	  glBegin(GL_QUADS);
		  glTexCoord2f(0.0, 0.0);
		  glVertex3f(vertices[v].x, vertices[v].y, vertices[v].z); v++;
		  glTexCoord2f(1.0, 0.0);
		  glVertex3f(vertices[v].x, vertices[v].y, vertices[v].z); v++;
		  glTexCoord2f(1.0, 1.0);
		  glVertex3f(vertices[v].x, vertices[v].y, vertices[v].z); v++;
		  glTexCoord2f(0.0, 1.0);
		  glVertex3f(vertices[v].x, vertices[v].y, vertices[v].z);
	  glEnd();

	  /* perform rendering pass for fog if required */

	  if (fog_area[ref])
	  {
		  /* restore z buffer writes, set blend params & disable texturing */

		  glDepthMask(GL_TRUE);
		  glEnable(GL_BLEND);
      if ( fog == 1 )
		    glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA);
      else if ( fog == 2 )
		    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
      else if ( fog == 3 )
		    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

		  glDisable(GL_TEXTURE_2D);

		  /* draw the fog panel */

		  v = ref * 4;
		  glBegin(GL_QUADS);
			  set_fog_color_alpha(v);
			  glVertex3f(vertices[v].x, vertices[v].y, vertices[v].z); v++;
			  set_fog_color_alpha(v);
			  glVertex3f(vertices[v].x, vertices[v].y, vertices[v].z); v++;
			  set_fog_color_alpha(v);
			  glVertex3f(vertices[v].x, vertices[v].y, vertices[v].z); v++;
			  set_fog_color_alpha(v);
			  glVertex3f(vertices[v].x, vertices[v].y, vertices[v].z);
		  glEnd();

		  /* restore rendering state */

		  glEnable(GL_TEXTURE_2D);
		  glDisable(GL_BLEND);
	  }
}

/****************************************************/
/*  Window Draw Event callback                      */
/****************************************************/

void display( pCpw cpw, uint_32 winid )
{
	  int n;		/* counter */

	  /* clear all pixels in colour buffer */

	  glClear( GL_COLOR_BUFFER_BIT );

	  /* position viewer */

	  glLoadIdentity();
	  glTranslatef(0.0, -3.0, -32.0);
	  glRotatef(10.0, 1.0, 0.0, 0.0);

	  /* draw the panels */

	  for (n=0; n<NUM_PANELS; n++) {

      if ( glExtInfo.EXT_fog_coord )
		    draw_panel_ext(n);
      else
		    draw_panel_alpha(n);

    }

    cpwSwapWindowBuffers( cpw, winid );
}

/****************************************************/
/*  Window Resize Event callback                    */
/****************************************************/

void reshape( pCpw cpw, uint_32 winid, uint_32 width, uint_32 height )
{
	  glMatrixMode(GL_PROJECTION);
	  glLoadIdentity();
    gluPerspective(50, 1.0, 0.2, 40);
	  glMatrixMode(GL_MODELVIEW);
	  glLoadIdentity();
    glViewport( 0, 0, width, height );
}

/****************************************************/
/*  Keyboard Event callback                         */
/****************************************************/

void keyboard( pCpw cpw, uint_32 id, uint_32 key, uint_32 state, uint_32 x, uint_32 y )
{
    if ( state == CPW_KEYMOD_UP ) return;

    if ( key == 'f' || key == 'F' ) fog++;
    if ( fog == 4 ) fog = 1;

    cpwPostRedisplay( cpw );
}

/****************************************************/
/*  Main                                            */
/****************************************************/

#ifdef _WINDOWS
int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
#elif  _CONSOLE
int main(int argc, char* argv[])
#endif
{
    pCpw cpw = null;
    cpwInitContext( &cpw );
    cpwInitDisplayMode( cpw, CPW_SURFACE_DOUBLE | CPW_SURFACE_RGBA );
    cpwInitWindowProperty( cpw, CPW_WINDOWPROP_POSITION, 100, 100 );
    cpwInitWindowProperty( cpw, CPW_WINDOWPROP_SIZE, 200, 200 );

    cpwCreateWindow(cpw,"OpenGL Demo - Volumetric Fog");

    glEnable( GL_CULL_FACE );

    if ( !init(cpw) ) {
        cpwFreeContext( &cpw );
        return false;
    }

    cpwReshapeCallback( cpw, reshape );
    cpwDisplayCallback(cpw,display);
    cpwKeyboardCallback( cpw, keyboard );
    cpwMainLoop(cpw);
    cleanup();

	  return 0;