Introduction to AllegroGL

Introduction to using AllegroGL with Dev-C++

If you've gone through the tutorial on using Allegro with Dev-C++, then you know that you now have enough to do a complete game. For some, that might be all you'd need. However, if you are like me, you feel more comfortable using OpenGL for the graphics rendering any number of reasons. OpenGL is widely used, easy to learn (or you already know it), etc. Whatever your reason may be, you would like to keep using OpenGL for graphics, but Allegro for everything else. Well, there is a not-so-difficult solution to this in the form of AllegroGL. Currently, AllegroGL works on Windows and POSIX systems (no word on Mac systems… yet.)

Let's get under way installing AllegroGL for Dev-C++…

Requirements

Fix AllegroGL for MinGW

You will need to configure the AllegroGL makefile for MinGW32. To do this, open a DOS prompt and go to the directory that contains the extracted AllegroGL source code. Type fix mingw32 to configure the makefile.

Build and Install AllegroGL

From a DOS prompt in the directory of the AllegroGL source code, type make to build the AllegroGL library. To install AllegroGL to the default directories, type make install. AllegroGL is now ready for use.


Your First AllegroGL Program

Now that AllegroGL is installed for Dev-C++, its time to do something with it. Our first program will use Allegro to initialize a fullscreen window, and OpenGL to render a rotating square.

Create a New Project

Create the project for the AllegroGL example. Go to File→New→Project and select Empty Project on the Basic tab. Specify a project name, and click OK.

Add a Blank Document

The template will have generated a project with no files, so we'll need to add one now. Right-click on the project name at the left side of the screen and choose New File to add a blank document to the project. You can go ahead and save this as main.cpp.

Write Some Code

Include the headers for OpenGL, Allegro, and AllegroGL.

#include <gl\gl.h>
#include <GL\glu.h>
 
#include <Allegro.h>
#include <AllegGL.h>

For this illustration, the timer will consist of a simple increment routine. We will have a global integer, and a function that the timer will call to increment that integer. Because of this we will need to lock the variable and the function (global variables should be locked, as should functions inside the timer handler.) More on that later.

volatile int iTimer	= 0;
 
 
void	TimerFunc()
{
	// Increment the time counter:
	iTimer++;
}

Because we are locking the timer function, we need to let Allegro know where the end of the function is located (This isn't necessary for inline functions.)

END_OF_FUNCTION( TimerFunc );

We'll write a function to take care of the Allegro and AllegroGL initialization. This will call Allegro's initialize routine, then install AllegroGL. So we can read the key press when the user is ready to quit, we'll install the keyboard. Lastly, we'll install the timer so we can get some animation.

void	InitAllegro()
{
	// Initialize Allegro:
	allegro_init();
 
	// Install AllegroGL:
	install_allegro_gl();
 
	// Install the keyboard:
	install_keyboard();
 
	// Install the timer:
	install_timer();
}

Initialize the OpenGL scene. Use AllegroGL's allegro_gl_set to set the color depth and z-buffer depth.

void	InitGL()
{
    // Set the color depth to 32 bits-per-pixel:
    allegro_gl_set( AGL_COLOR_DEPTH, 32 );
    allegro_gl_set( AGL_Z_DEPTH, 8 );
    allegro_gl_set( AGL_SUGGEST, AGL_COLOR_DEPTH | AGL_Z_DEPTH );

Create a 640×480 pixel fullscreen window by using Allegro's set_gfx_mode( int card, int w, int h, int v_w, int v_h ). The card parameter could be GFX_OPENGL_FULLSCREEN, GFX_OPENGL_WINDOWED, or GFX_OPENGL and indicates if the window should be windowed or fullscreen. You might want to just use GFX_OPENGL, since that will choose the best mode based on the requested resolution. For instance, if the requested size is not allowable for fullscreen resolution, it will be windowed. If the requested size is an allowable resolution, it will be full screen. You can override this by using GFX_OPENGL_FULLSCREEN or GFX_OPENGL_WINDOWED. The latter might be useful when you absolutely want a windowed application, but be careful forcing an application to be fullscreen. If the system cannot use the requested resolution, set_gfx_mode will return an error.

The w and h parameters specify the requested width and height of the display, respectively, and v_w and v_h are the minimum virtual screen dimensions. Once the screen is set up, you can get the dimensions using the macros SCREEN_W, SCREEN_H, VIRTUAL_W, and VIRTUAL_H.

	// Create an 800x600 fullscreen window:
	if ( set_gfx_mode( GFX_OPENGL_FULLSCREEN, 800, 600, 0, 0 ) )
	{
		// Error creating window:
		allegro_message( "There was an error creating the window" );
		exit( 1 );
	}

Specify the extents of the OpenGL viewport using glViewport. You could use various values to get a letterbox effect, or if you were doing multiple viewports. For this example, we will use the entire screen size.

	// Set the OpenGL viewport:
	glViewport( 0, 0, SCREEN_W, SCREEN_H );

We need to setup the projection transformation matrix. If you worked through the GLUT tutorial, you saw how we can use glFrustum instead of gluPerspective. In that tutorial, we just stretched the image to cover the full screen. This time, we want to get a little more precise and maintain the proper aspect ratio (so the square won't look distorted.) To do this, we need to calculate the aspect ratio of the screen:

    screen_aspect_ratio = screen_width / screen_height

If the aspect ratio is greater than 1.0, then the screen is wider than it is tall. We set the x-range to its full value (i.e.. -1.0 to +1.0), and divide them by the aspect ratio in order to calculate the y-values. Similarly, if the aspect ratio is less than 1.0, the screen is taller than it is wide, so we set the y-values to full and multiply them by the aspect ratio in order to get the x-values.

	// We need to set the projection transformation matrix,
	// so switch to the projection matrix:
 	glMatrixMode( GL_PROJECTION );
 	glLoadIdentity();
 
	// Define the projection transformation matrix.  Notice
	// that this is using glFrustum instead of gluPerspective:
	// In order to use this we need to calculate the min and
	// max x/y range so as to maintain proper aspect ratio:
	float	fMinX = -1.0f, fMaxX = 1.0f,
			fMinY = -1.0f, fMaxY = 1.0f;
	float	fAspectRatio	= SCREEN_W / (float)SCREEN_H;
	if ( fAspectRatio > 1.0f )
	{
		// The height is greater than the width, so we need to
		// recalculate the x-range:
		fMinY	= fMinX / fAspectRatio;
		fMaxY	= fMaxX / fAspectRatio;
	}
	else
	{
		// The width is greater than the height, so we need to
		// re-calculate the y-range:
		fMinX	= fMinY * fAspectRatio;
		fMaxX	= fMaxY * fAspectRatio;
	}
	glFrustum( fMinX, fMaxX, fMinY, fMaxY, 1.5f, 20.0f );

Switch back to the modelling matrix, and set the OpenGL background (clear) color:

	// Switch back to the modelview matrix:
	glMatrixMode( GL_MODELVIEW );
 
	// Set the background color:
	glClearColor( 0, 0, 0, 0 );
}

Define a function for rendering the scene. This will simply clear the screen and depth buffer, then render a unit square with a different color for each vertex. Once it has finished rendering, it will use the method allegro_gl_flip to copy the rendered scene to Allegro's screen.

void	RenderScene()
{
	// Clear the screen and depth buffer:
	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
 
	// Render the square as a quad:
	glBegin( GL_QUADS );
		// Assign a different color to each vertex
		glColor3f( 1.0f, 0.0f, 0.0f );
		glVertex3f( -1.0f, -1.0f, 0.0f );
 
 
		glColor3f( 0.0f, 1.0f, 0.0f );
		glVertex3f(  1.0f, -1.0f, 0.0f );
 
 
		glColor3f( 0.0f, 0.0f, 1.0f );
		glVertex3f(  1.0f,  1.0f, 0.0f );
 
 
		glColor3f( 1.0f, 1.0f, 1.0f );
		glVertex3f( -1.0f,  1.0f, 0.0f );
	glEnd();
 
	// Flush the rendering pipeline:
	glFlush();
 
	// Flip the backbuffer to the Allegro screen:
	allegro_gl_flip();
}

The program's main procedure. The first things we need to do is to call our routines to initialize Allegro (and AllegroGL) and OpenGL.

int main()
{
	// Initialize Allegro:
	InitAllegro();
 
	// Initialize OpenGL:
	InitGL();

Use the LOCK_VARIABLE macro to lock any global variables. The LOCK_FUNCTION macro needs to be used for any function that will be sent to the timer handler.

	// Because we are using a timer, and have a global variable
	// and a function inside the timer handler, we need to lock
	// the timer variable and the timer function:
 	LOCK_VARIABLE( iTimer );
 	LOCK_FUNCTION( TimerFunc );

We can now setup the timer using Allegro's install_int_ex function. We pass this the name of the timer functions (TimerFunc) and the timer speed. Using the macro BPS_TO_TIMER(60) we tell the timer to tick 60 times per second.

 	// Setup the timer to run at 60 frames-per-second:
 	install_int_ex( TimerFunc, BPS_TO_TIMER( 60 ) );

We translate the scene back so it will be visible. We also need to calculate how much the rotation should increment per frame. Since each frame should be 1/60th of a second, and we want the square to do one full rotation per second, this value is 1/60*360.

 	// Set the translation of the scene back a bit so
	// it will be visible:
 	glLoadIdentity();
 	glTranslatef( 0.0f, 0.0f, -5.0f );
 
 	float	fAngleIncrement	= 1.0f / 60.0f * 360.0f;

We will keep rotating the square until the user hits the Escape key. Allegro has an array of flags indicating the key states called (surprisingly) key. If a key's value returns TRUE, then the key is pressed. Inside the loop, we increment the rotation, and render the scene.

 	// Go into the main loop until the 'Escape' key is pressed:
  	while ( !key[ KEY_ESC ] )
 	{
 		// Rotate the scene based on the elapsed time:
 		glRotatef( fAngleIncrement, 0.0f, 0.0f, 1.0f );
 
 		// Render the scene:
 		RenderScene();
 	}
}

The last thing we need to do is add the END_OF_MAIN() macro. This ensures cross-platform compatibility by mangling the main procedure to something the operating system likes (for example, Windows likes WinMain.)

END_OF_MAIN();

Remember to add the following entries under under Linker in Project→Project Options on the Parameters tab:

          -lagl -lalleg -lopengl32 -lglu32 -lgdi32 

Now you can compile and run the example.


Downloads

MyFirstAllegroGL.zip The above application, ready for Dev-C++.
DevCpp_AllegroGL_template.zip A project template for Dev-C++ to generate a simple AllegroGL application, like the one above.