About this Blog

This is my first blog. Ever.

It is simply going to be about my hobby; playing with computer programming. I do not know much about blogging, but I will use this one to learn a bit more about it.

Programming has always been a bit of a passion for me, as from those early days when I first tapped in a sample BASIC program on my old Sinclair Spectrum back in 1986. I have been through many platforms, languages and OS's since, but always carried the hobby with me. I am not particularly good at it; perfection requires a large time investment and continuous practice. I do not have the luxury of the amount of time required to keep the fire burning constantly, so the hobby has inevitably gone through periods of extreme withering. I have, however, finally settled for C++, as the title of this blog implies, and play around with it for some entertainment when ever I can.

This here will serve me as a written record of what I am up to, and hopefully be a reinforcement to my memory every now and then. That is all there is to it.

So, if you read this blog, please don't expect anything snazzy, but be you welcome just the same!
Showing posts with label OpenGL. Show all posts
Showing posts with label OpenGL. Show all posts

Saturday, 31 March 2012

Allegro 5.1 / OpenGL Heightmap Loader

Back again after a break from coding, enforced by work (alas). This time I am here with some more OpenGL and something I always wanted to tackle; heightmaps. Again, I am using Allegro 5.1 as the API, and decided to use some of A5's features to accomplish the task. Two of the neat things about A5 for this purpose are the al_get_pixel() and al_unmap_rgb() functions, which allow you to get the pixel color and extract them into their rgb components, perfect for reading heightmaps. Also, to speed up the reading of a bitmap, pixel by pixel, there is the al_lock_bitmap() function, which turns the bitmap temporarily into a memory bitmap. It all helped. Now, I decided to use greyscale images, which each shade is represented by an equal value of the rgb components (eg; mid grey might be something like r = 128, g = 128, b = 128), so I would only need to use one of the components.

All that is very well, but I also had another objective; that my heightmap loader be able to read (and convert into terrain) any size of bitmap, so that I would not be restricted to reading only 100 x 100 or any other fixed size greyscale heightmap. To accomplish this, I decided to use STL vector, so that any size in total pixels could be stored in the vector, depending on the dimensions of the loaded heightmap bitmap. The basic idea is that each pixel will represent a point of terrain, with 3D Cartesian coordinates, where, the X and Y dimensions of the bitmap will be the respective OpenGL X and Z coordinates, when multiplied by a land_scale factor, and the color will be the elevation (OpenGL Y coordinate), the lighter the grey, the higher the elevation. Here's the heightmap I made for the experiment...


Originally, my intention was to use GL_TRIANGLE_STRIP to make the terrain, but around the web I found reports that it showed some (speed) inferiority when compared to using straight forward triangles with the now familiar glEnableClientState() function, so I dispensed with the idea of using GL_TRIANGLE_STRIP, and quite happily, too, as I was not too fond of it really.

So, that constitutes the preamble of the conditions I set myself for declaring mission accomplished. As per usual, the full code listing is posted at the end of the discussion. I will get to it, now.

The first task would be to load the bitmap, read the pixels, and create the 3D coordinates for each point. This is, really, the easy bit. Here's a code snippet...


vector<GLfloat> verts;

int BMP_WIDTH;
int BMP_HEIGHT;

ALLEGRO_BITMAP *ht_bmp = al_load_bitmap("landtile.jpg"); // load the heightmap.
ALLEGRO_COLOR ht_pixel;

int bmp_x = 0;
int bmp_z = 0; // the Y axis of the bmp, really, but considered "z" for conversion to OpenGL coords.

GLfloat land_scale = 5.0f;
GLfloat height_scale = 100.0f;
GLfloat height_true = 0.0f;

unsigned char r, g, b; // To store the retreived r,g,b components of each pixel.

// here I get the dimensions, in pixels, of the loaded heightmap.

BMP_WIDTH = al_get_bitmap_width(ht_map);
BMP_HEIGHT = al_get_bitmap_height(ht_bmp);

// In the full listing, there are a couple of "autocentering variables here, omitted in this snippet.
// Also, in the full listing, look out for the al_lock_bitmap() around here.

So, I now have my heightmap loaded and the program knows the bitmap's dimensions. Each pixel (point) needs to be "expanded" into 3 place holders for the x,y,z coords.

// Reserve the space in the vector, which is a good practice, with the total number of coordinate place holders...
verts.reserve(size_t(BMP_HEIGHT * BMP_WIDTH * 3)); // An x,y,z for each pixel read.

// ..and populate the verts vector...
for(bmp_z = 0; bmp_z < BMP_HEIGHT; bmp_z++)
{
    for(bmp_x = 0; bmp_x < BMP_WIDTH; bmp_x++)
    {
        verts.pushback(GLfloat(bmp_x * land_scale)); 

        // Get the "height" of the OpenGL Y coord according to the "color" of the pixel...
        ht_pixel = al_get_pixel(ht_bmp, bmp_x, bmp_z);
        al_unmap_rgb(ht_pixel, &r, &g, &b); // Thanks, Tomasu!

        height_true = GLfloat(float(r) / 255.0f) * height_scale; // Uses only the red component...
        verts.pushback(height_true); // Assign the value to the vector.

        verts.pushback(GLfloat(bmp_z * land_scale));
    }

}

al_destroy_bitmap(ht_bmp); // Once the values are stored in the vector, no further need for the bitmap.

So yeah, the height of the bitmap times the width times three will create the required number of places for the x, y, z for each pixel.
Now, to continue, I will be using the OpenGL glDrawElements() function. This, essentially, does the same thing that I did manually on this post. that is, connecting the points by indexing. Basically, it works this way;

glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, verts);
glDrawElements(GL_TRIANGLES, points_count, GL_UNSIGNED_INT, points);
glDisableClientState(GL_VERTEX_ARRAY);

Looks pretty simple. verts is the array that holds the vertices of each point to be connected, in sets of three (x,y,z). points is the order in which the vertices need to be connected to make the triangles. But hang on. I need to generate those connection points automatically. I will look at a simplified version of the problem. Here is an array of the points, considered a multidimesnional array, but really a contiguous vector.



25 points (0-24), in this case, gives me 16 squares into which I need to draw 32 triangles. First step, I need to reduce 25 to 16. So, BMP_WIDTH is 5 and BMP_HEIGHT is 5. I can get 16 very easily...

(BMP_WIDTH - 1) * (BMP_HEIGHT - 1) = 16



And I can draw the triangles very easily using the triangle pair count (which is the 16 that I got). First triangle...

vector<GLuint> points;

for(i = 0; i < triangle_count; i++)
{
    // Triangle 1
    points.push_back(i);
    points.push_back(i + 1);
    points.push_back(i + BMP_WIDTH);
    ....

And the second triangle...

    //Triangle 2
    points.push_back(i + 1);
    points.push_back(i + BMP_WIDTH);
    points.push_back(i + BMP_WIDTH + 1);
}

Going around counter clockwise. But there's a problem. Every time the calculation gets to the end of a row, it needs to skip a triangle pair in order to start drawing a new row, otherwise I get the last triangle of the first row warping across to the first triangle of the second row.


I need to have a dummy number to skip at the end of each row, so that the drawing of the triangles breaks. I modify the triangle count by the number of rows of squares, which is, indeed, the (BMP_HEIGHT - 1). So triangle count looks like this...

triangle_count = ((BMP_WIDTH - 1) * (BMP_HEIGHT - 1)) + (BMP_HEIGHT -1);

And implement a row skip counter (call it width_count). The function now looks like this...


int width_count = 0;
triangle_count = ((BMP_WIDTH - 1) * (BMP_HEIGHT - 1)) + (BMP_HEIGHT -1);

for(i = 0; i < triangle_count; i++)
{
    if(width_count == (BMP_WIDTH - 1)) // That is to say, it counts up to the end of the row...
    {
        continue; // Skip drawing that triangle pair...
        width_count = 0; // Reset width_count to 0.
    }
    else
    {
        // Triangle 1
        points.push_back(i);
        points.push_back(i + 1);
        points.push_back(i + BMP_WIDTH);

        //Triangle 2
        points.push_back(i + 1);
        points.push_back(i + BMP_WIDTH);
        points.push_back(i + BMP_WIDTH + 1);

        width_count++; //count each pair of triangles drawn.
    }
}



And that is, basically, how I did it. I suppose you could use a modulus of (BMP_WIDTH - 1) to accomplish the same thing, and omit the width_count, right?

Here's a screen shot and the complete listing on my pastebin account (easier than formatting it here...).


THE COMPLETE ALLEGRO 5.1 LISTING HERE!

And with a most basic color for height algorithm function added somewhere in there it is easy to make a pseudo scenery generator! Had some fun with this one.


That code not included here, maybe a refined version next time, so...

Until the next post; bye, for now!






Wednesday, 14 December 2011

OpenGL / Allegro 5.1 Animation and Back Face Culling

So, in this post I might say that I am now ready to tackle a slightly more complex integration of OpenGL into Allegro 5.1; Animation.


Much the same stuff that has been in my previous posts, up to a point; include the Allegro and GLU headers, set up a camera, draw a flat board with a texture on it, etcetera...

/* Comment --- reference library link line 
`pkg-config --libs allegro-5.1 allegro_image-5.1`

-lGL

-lGLU
*/


#include "allegro5/allegro.h"
#include "allegro5/allegro_image.h"
#include "allegro5/allegro_opengl.h"
#include "GL/glu.h"


const int SCREEN_X = 800;
const int SCREEN_Y = 600;
const float FPS = 60.0;

// Function to keep angle values between 0 and 359
void angle_max(GLfloat &_angle)
{
if(_angle >= 360.0)
_angle -= 360.0;
else if(_angle < 0.0)
_angle += 360.0;
}

// Function to set up camera
void setup_3D_camera()
{
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(35.0, GLdouble(SCREEN_X) / GLdouble(SCREEN_Y), 1.0, 100.0);
}
Except that now instead of drawing the camera straight into int main() function, I have the camera setting up in its own custom function. I also added a function called angle_max(), that receives a reference pointer of an angle. Here is a brief break down.

//DEMO EXAMPLE
const int SCREEN_X = 800;
const int SCREEN_Y = 600;
These are just the display size, in integers, to create an 800 x 600 pixel window. It may seem pretty obvoius, but I mention it here for one reason alone, one which I have not mentioned before. Allegro creates displays using integers values with al_create_display(). OpenGL uses the values of the display width and height to get the aspect ratio of the screen for the gluPerspective() function, but requires the aspect ratio to be in a double type. These integers need to be type cast to double in the gluPerspective() function, or you get some funny perspective effects when the program runs. It caught me out. That's all!

// DEMO EXAMPLE
const float FPS = 60.0;


60 frames per second. Allegro stuff. This will be the speed of the timer to draw my animation. It is just declared here.

// DEMO EXAMPLE
void angle_max(GLfloat &_angle)
{
if(_angle >= 360.0f)
_angle -= 360.0f;
else if(_angle < 0.0f)
_angle += 360.0f;
}
Very simple, and needed only to keep the angle inside the 0º to 360º constraints. OpenGL uses angles in degrees. This function accepts an angle and makes sure that it never exceeds 359.99º or falls below 0º. It is a generic angle "corrector", and any angle of float type in degrees can be passed to it for the check, simplifying the code.

The void setup_3D_camera() function is simply what has been seen in other posts on this blog, only here it is in a function. Big deal. In the end it only gets called once, anyway, to set up the glMatrixMode(GL_PROJECTION).

Now the function to draw the board, also in its own custom function this time around...

// Function to draw the 3D bill board
void draw_board(GLuint _tex, GLfloat _ang_y)
{
glMatrixMode(GL_MODELVIEW),
glLoadIdentity();

glTranslatef(0.0f, 0.0f, -20.0f);

glRotatef(0.0f, 1.0f, 0.0f, 0.0f);
glRotatef(_ang_y, 0.0f, 1.0f, 0.0f); // Y axis, _ang_y variable used here.
glRotatef(0.0f, 0.0f, 0.0f, 1.0f);

glEnable(GL_TEXTURE_2D);
//glEnable(GL_CULL_FACE);
glBindTexture(GL_TEXTURE_2D, _tex);

glBegin(GL_QUADS);

glTexCoord2f(0.0f, 0.0f);
glVertex3f(-2.5f, -2.5f, 0.0f); //lower lh corner
glTexCoord2f(1.0f, 0.0f);
glVertex3f(2.5f, -2.5f, 0.0f); //lower rh corner
glTexCoord2f(1.0f, 1.0f);
glVertex3f(2.5f, 2.5f, 0.0f); //upper rh corner
glTexCoord2f(0.0f, 1.0f);
glVertex3f(-2.5f, 2.5f, 0.0f); //upper lh corner

glEnd();

//glDisable(GL_CULL_FACE);
glDisable(GL_TEXTURE_2D);
}


Apart from the drawing of the bill board here being in a function, there are only two new and notable features. First, note that a variable for an angle is passed to this funtion (GLfloat _ang_y), and is used in the glRotatef() call for the Y axis. The implicatin is that every time the function is called, a new value of the Y axis angle may be passed to the function. That would give me an "animation" of a sorts, right?

The other feature of note is the inclusion of glEnable(GL_CULL_FACE), though commented out for now. There will be more on this face culling business at the end of the post. Just for now, related to this topic, note that the bill board has been drawn in a counter-clockwise sense; that is, I started at the bottom left vertex, went to the bottom right, up to the top right, then to the top left. The order in which the vertices are drawn is important, when it comes to face culling.

int main(int argc, char *argv[])
{
ALLEGRO_DISPLAY *display = NULL;
ALLEGRO_BITMAP *tex_bmp = NULL;
ALLEGRO_EVENT_QUEUE *event_queue = NULL;
ALLEGRO_TIMER *timer = NULL;

bool loop = true;
bool draw = false;

GLfloat angle_y = 0.0f;
GLfloat rate_y = 1.0f;
GLuint ogl_tex = 0;

al_init(); // Again, avoiding error trapping for clarity...
al_init_image_addon();

al_set_new_display_flags(ALLEGRO_OPENGL);
display = al_create_display(SCREEN_X, SCREEN_Y);
tex_bmp = al_load_bitmap("texture1.jpg");
ogl_tex = al_get_opengl_texture(tex_bmp);

event_queue = al_create_event_queue();
timer = al_create_timer(1.0f/FPS);

al_register_event_source(event_queue, al_get_display_event_source(display));
al_register_event_source(event_queue, al_get_timer_event_source(timer));

al_start_timer(timer);
setup_3D_camera();

//glCullFace(GL_BACK);
//glCullFace(GL_FRONT);

So, what's new as from the previous posts? For starters I have declared an ALLEGRO_EVENT_QUEUE and an ALLEGRO_TIMER. There are also two booleans, loop and draw. Finally, there are two new GLfloats (angle_y and rate_y). Taken from the top...

The ALLEGRO_EVENT_QUEUE is created with al_create_event_queue(), and it will be used to store events from different sources of input into the program (such as the display, or the timer, keyboard, mouse, and so on). However, in order for the event_queue to receive input from a ny of these sources, an "avenue" of communication must be opened from the source to the event_queue by using al_register_event_source(), or the program simply will not know that an input was intended at all. I register inputs for the event_queue from the display using al_get_display_event_source() and from the timer using al_get_timer_event_source(). It makes the program keep an "eye open" for events from these two sources.

The ALLEGRO_TIMER is created with al_create_timer(1.0f/FPS). Basically (remembering that FPS = 60.0), I am dividing 1 second into 60 parts. This will create a timer that will throw an ALLEGRO_TIMER_EVENT into the event_queue every 1/60 of a second. In the code (later) I can retrieve and use that timer event to control the flow of the "animation".

All of what is covered in the last two paragraphs is not specifically for OpenGL integration; it is plain old Allegro, and can be used in any Allegro 5 program. More on it at these two links.

Allegro 5 events tutorial

Allegro 5 timer tutorial

The GLfloat angle_y variable is the angle that will be passed to the custom draw_board() function, in order to set the angle of the board around its Y axis. The GLfloat rate_y is the amount by which the angle will change each frame loop of the animation.

I then start the timer with al_start_timer(timer). With this, the timer is now running and throwing ALLEGRO_EVENT_TIMER every 1/60 of a second. And finally, I call the setup_3D_camera() function.

I will ignore the two two commented out glCullFace() lines for now.

By the way, here is the bitmap I used for this sample program (an Anime "Oh! My Goddess" wallpaper cropped and "gimped")...


Now the loop that will detect the events and handle them...

 // Start the loop
while(loop)
{
ALLEGRO_EVENT event;
al_wait_for_event(event_queue, &event);
// Detect the events
switch(event.type)
{
case ALLEGRO_EVENT_DISPLAY_CLOSE:
loop = false;
break;

case ALLEGRO_EVENT_TIMER:
draw = true;
break;
}

// Draw the screen if draw = true
if(draw == true && al_event_queue_is_empty(event_queue))
{
angle_y += rate_y;
angle_max(angle_y);
al_clear_to_color(al_map_rgb_f(0.0f, 0.0f, 0.5f));
draw_board(ogl_tex, angle_y);
al_flip_display();
draw = false;
}
}


Yeah, while the boolean loop variable is true, the loop will keep cycling. First, I will make an empty ALLEGRO_EVENT structure variable, and then wait for an event to be dropped into the event_queue with al_wait_for_event(). When an event occurs, I can examine it in the event structure using a switch control loop.

If I get an ALLEGRO_EVENT_DISPLAY_CLOSE event (ie; I click on the window "X" icon to close it), I set the loop variable to false, and the program exits the loop on the next while() cycle.

If I get an ALLEGRO_EVENT_TIMER, and I will 1/60 of a second just so long as the timer is running, I set the draw variable to true. This will cause the drawing to take place by executing the if(draw == true...) statement.

When the loop variable is set to false, the while(loop) exits and the following block is performed. The application ends.

 // Clean up and kill the application
al_destroy_bitmap(tex_bmp);
al_stop_timer(timer);
al_destroy_timer(timer);
al_flush_event_queue(event_queue);
al_destroy_event_queue(event_queue);
al_destroy_display(display);
return 0;
}


Now, what about that back face culling business? If the blocks of code above (barring, of course, the //DEMO EXAMPLE blocks) are strung together and compiled, I get an animation of the goddesses spinning around on a bill board. Both sides of the bill board will be seen, the second one being a reverse image of the first. The face that shows the black haired goddess with the red shawl (Skuld, by name, incidentally) on the left is the front face, with the vertices arranged counter clockwise. If that board it flipped around around (as the animation does) the vertices will no longer be in a counter clockwise orientation. This is the back face, with Skuld on the right. All very well, but maybe I only want the front face to be visible. In this case, I un-comment the lines in the draw_board() function;

...
//glEnable(GL_CULL_FACE);
...
//glDisable(GL_CULL_FACE);
...


By default, OpenGL culls (or suppresses the drawing of) the backface, that one with clockwise vertices. If I run the program now, we will only see the face of the board that has Skuld on the left. However, we can manipulate OpenGL very easily so that it culls only the front face and leaves the back face alone. If I find the following line, just after al_start_timer()...

//glCullFace(GL_FRONT);

...and un-comment it, then recompile and run, I will only see the face that has Skuld on the right (ie; the back face).

Back face culling is useful to speed up a program that has many objects in it, which the back faces need not be drawn (for example, they are the inside of a space ship that the user will never see anyway. Some more neat tricks with back face culling here...

Video Tutorials Rock Back face Culling

That's all for this post.

Sunday, 4 December 2011

OpenGL / Allegro 5.1 2D bitmap overlay panel

And here's another small tidbit of using OpenGL with Allegro 5.1. Sooner or later, rendering 3D objects is not going to be enough to interact with an application. We are going to need a 2D "control panel", which will have indicators and switches, etcetera, like this very lame one I threw together...

It is a straight forward PNG bitmap, 800 x 197 pixels. The object is to put this "panel" at the bottom of my OpenGL display, after I have rendered my "3D" object.

First some familiar stuff, except this time I'll make a flat triangle as my object just to vary a little...

Note: Once again, all the following blocks of code can be put together to create the working sample program.

#include "allegro5/allegro.h"
#include "allegro5/allegro_opengl.h"
#include "allegro5/allegro_image.h"
#include "GL/glu.h"

int main()
{
ALLEGRO_DISPLAY *display = NULL;
ALLEGRO_BITMAP *bmp_panel = NULL;

al_init();
al_init_image_addon();

al_set_new_display_flags(ALLEGRO_OPENGL);
display = al_create_display(800, 600);

bmp_panel = al_load_bitmap("panel.png");

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(35.0, GLdouble(800.0 / 600.0), 1.0, 200.0);

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.0, 0.75, -10.0);

glBegin(GL_TRIANGLES);

glColor3f(0.7, 0.0, 0.1);
glVertex3f(-1.5, -1.5, 0.0);
glColor3f(0.3, 0.0, 0.3);
glVertex3f(1.5, -1.5, 0.0);
glColor3f(0.2, 0.0, 0.7);
glVertex3f(0.0, 1.5, 0.0);

glEnd();

Nothing really new here, but if I tried to put my panel onto the screen right now with al_draw_bitmap(), I would not get any results at all, as I would be trying to draw a 2D panel "into" a 3D world. OpenGL does not know how to fit it. However, there is a way. The display projection needs to be reset to 2D. This is where glOrtho() comes in handy. The following bit of code will make the OpenGL display 2D "compatible".
glMatrixMode (GL_PROJECTION);
glLoadIdentity ();
glOrtho (0.0, 800.0, 600.0, 0.0, 0.0, 1.0);

More about 2D drawing with OpenGL here...

Now I am able to draw my bitmap onto the screen with al_draw_bitmap(). Note, I use al_get_bitmap_height() to obtain how many pixels high the panel is, then subtract that value from the display Y value to place it properly on the display...
int bmp_height = al_get_bitmap_height(bmp_panel);
al_draw_bitmap(bmp_panel, 0.0, 600.0 - float(bmp_height), 0);

al_flip_display();
al_rest(5.0);

al_destroy_bitmap(bmp_panel);
al_destroy_display(display);

return 0;
}

And that would be it. Here's the result...

OpenGL / Allegro 5.1 MipMaps

With thanks to Tomasu on #allegro IRC for the help with this...

Here's how to enable mipmaps for OpenGL programs using Allegro 5.1.

In the previous post, I applied a texture to a "board" on OpenGL utilizing the allegro function al_load_bitmap(), followed by al_get_opengl_texture(). However, that texture was simply an applied bitmap. If I tried using one of the mipmap options for glTexParameterf(), such as;

glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);


...the texture would not be mapped at all onto the board (it would be plain white). The solution is in making the bitmap a mipmap before using it as an OpenGL texture using al_set_new_bitmap_flags(). This must be called prior to loading the bitmap, in order for Allegro to generate the mipmaps. I opted for this as an experiment...

al_set_new_bitmap_flags(ALLEGRO_MIPMAP | ALLEGRO_MIN_LINEAR);
bmp = al_load_bitmap("texture.jpg"); // 256 x 256 pixels
ogl_tex = al_get_opengl_texture(bmp);

Then, when I called glTextParameterf() for setting the the GL_TEXTURE_MIN_FILTER and GL_TEXTURE_MAG_FILTER, I did it this way...
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

The result is now a mipmap texture on the board. Pretty cool. When done loading bitmaps as mipmaps, the bitmap loading can be reset to default by resetting the flags, as such...
al_set_new_bitmap_flags(ALLEGRO_CONVERT_BITMAP);

That's the default, and any loading of subsequent bitmaps will no longer be mipmaps. The complete listing that can be used to test this function is in my previous post on this blog.

Thursday, 1 December 2011

Open GL / Allegro 5.1 Basics: Textures

So, after a year, I decided to tackle OpenGL again to find that I have forgotten almost everything about it. Previously, I had used glut and explored the possibility, with some limited success, of using Allegro 4's OpenGL functionality. However, now there is a new version of Allegro; 5.1.0. These posts are going to be about using OpenGL with that particular programming API, on Linux (Debian Wheezy), with the Code::Blocks IDE.

Here are some links...

Video Tutorials Rock
Allegro Tutorial
OpenGL Reference (2.1)
OpenGL Reference (1.1)
Code::Blocks
Allegro.cc (find out more about Allegro API here...)

I believe the support for the version of OpenGL on my Linux (libgl1-mesa-dev and libglu-mesa-dev) is for version 2.1, complete. In any case, here's the pertinent output of my versions from glxinfo run in a Linux terminal (that goes with the binary NVIDIA-Linux driver for my on board GPU)....
direct rendering: Yes
client glx vendor string: NVIDIA Corporation
client glx version string: 1.4
...
OpenGL vendor string: NVIDIA Corporation
OpenGL renderer string: GeForce 7025 / nForce 630a/PCI/SSE2
OpenGL version string: 2.1.2 NVIDIA 290.10
OpenGL shading language version string: 1.20 NVIDIA via Cg compiler

With all that in mind, here we go...

The objective here is simply render a texture on a GL_QUAD-rilateral. But first it will be necessary to initialize Allegro, and make it acceptable for working with OpenGL. This is relatively easy...

(Note: The following blocks of code are completely contiguous. They can be pasted together in the order that they appear to create the complete, working listing, unless a particular block excludes itself specifically with a CAPITALIZED comment saying //DEMO EXAMPLE. Such commented blocks need not or should not be placed in the listing).

#include "allegro5/allegro.h"
#include "allegro5/allegro_opengl.h"
#include "allegro5/allegro_image.h"
#include "GL/glu.h"


int main()
{
ALLEGRO_DISPLAY *display = NULL;
ALLEGRO_BITMAP *bmp = NULL;

GLuint ogl_tex; // The OpenGL texture id.

al_init();
al_init_image_addon();

al_set_new_display_flags(ALLEGRO_OPENGL);
display = al_create_display(1200, 800);

// Load a bitmap to use as a texture
bmp = al_load_bitmap("texture.jpg"); // 256 x 256 pixels
ogl_tex = al_get_opengl_texture(bmp);

...and here is the "texture.jpg" I am using, by the way. It is some screen background I got off the web and "Gimped" down to 256 x 256.
 

So, what's important here? Several things, straight off the bat, which are going to be useful, if not indispensable, for using OpenGL with Allegro 5.1.0. In the include headers lines is "allegro_opengl.h", required to use OpenGL with the API. Also, there are the "allegro_image.h" and "glu.h" headers. The image addon is initiated in the code with the line al_init_image_addon(), and will be needed for handling the loading of the jpg image that will be used as the texture. The glu.h is needed for for using the gluPerspective() function, which will provide the camera for my OpenGL world.

The respective libraries are linked the following way, for Allegro-5.1.0;
`pkg-config --libs allegro-5.1 allegro_image-5.1`
-lGL
-lGLU
Now, before creating the ALLEGRO_DISPLAY, the call to al_set_new_display_flags(ALLEGRO_OPENGL) is, apparently, required. That said, I have tried running OpenGL through Allegro-5.1.0 excluding this call, and it works perfectly well and without any notable difference, which is strange(?).

Finally, that little block of code loads texture.jpg as an ALLEGRO_BITMAP with al_load_bitmap(), and then assigns that bitmap as a (type) GLuint texture id (ogl_tex) with al_get_opengl_texture(). It is now usable as an OpenGL texture for later on.

Onwards...
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(35.0, 1200.0 / 800.0, 1.0, 400.0);

This is the camera view on the OpenGL world.

glMatrixMode(GL_PROJECTION) puts OpenGL in the mode that allows the definition of the view camera. glLoadIdentity() then pr oceeds to enable, or execute that mode, replacing any glMatrixMode that may have been in use before. Finally, gluPerspective() sets up the qualities of the camera. In order, the first parameter sets the angle, in degrees that the camera sees. With it, you can simulate a telephoto lens (with low FOV angles), or fish eye lenses (with wide FOV angles). The second parameter is the aspect ratio of the view port, or display. It is obtained by dividing the display width by the display height, (1200 / 800) in this case. The last two parameters are the closest render distance and farthest render distance, respectively, in OpenGL units. Anything closer than 1.0 or farther than 400.0 units from the Camera will not be rendered. Yeah, okay. I understand all the above better worded that way! And that's the camera done.

The next bit of code builds the object (a flat board!) that we are going to see, and applies the texture we prepared earlier...
al_clear_to_color(al_map_rgb(0,0,50));

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

glTranslatef(2.0f, -1.5f, -18.0f);
glRotatef(75.0, -1.0f, 0.0f, 0.0f);
glRotatef(10.0, 0.0f, -1.0f, 0.0f);
glRotatef(0.0, 0.0f, 0.0f, 1.0f);

glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, ogl_tex);

glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

// Other options for GL_TEXTURE_WRAP_S and GL_TEXTURE_WRAP_T...
// GL_CLAMP, GL_CLAMP_TO_BORDER, GL_CLAMP_TO_EDGE, GL_MIRRORED_REPEAT, GL_REPEAT

glBegin(GL_QUADS);

glTexCoord2f(0.0, 0.0); // Bottom left hand corner
glVertex3f(-3.5, -6.5, 0.0); // X,Y,Z
glTexCoord2f(6.0, 0.0); // Bottom right hand corner
glVertex3f(3.5, -6.5, 0.0); // X,Y,Z
glTexCoord2f(6.0, 8.0); // Top right hand corner
glVertex3f(3.5, 6.5, 0.0); // X,Y,Z
glTexCoord2f(0.0, 8.0); // Top left hand corner
glVertex3f(-3.5, 6.5, 0.0); // X,Y,Z

glEnd();

glDisable(GL_TEXTURE_2D);

Okay, that looks quite long and confusing right now, but it is not the end of the world. I will dissect it...

al_clear_to_color() is self explanatory, really. The only reason I mention it is to show that it is perfectly compatible with OpenGL.

And again, we use glMatrixMode() and glLoadIdentity(), though this time we set the GL_MODELVIEW mode, as we are going to make a flat quadrilateral (that is, a GL_QUAD).

glTranslatef(X,Y,Z) needs a very quick review. It moves the "origin" (let's say) of our object to be created to a point in 3D space, defined by X,Y,Z, in OpenGL units. In this case, I moved the origin +2.0 X (to the right), -1.5 Y (down) and -18.0 Z (into the screen). Basically, it is a matrix translate, as the name implies.

glRotatef(angle, X,Y,Z) is a matrix rotate , around the origin. Here I break the rotation process up into three separate operations, one for each axis, with it's own angular change. To put it simply, positive rotation values rotate the object around it's origin counter-clockwise, as viewed parallel from the positive side of local object's local axis it is being rotated around.

We now come to the texture handling part. The first thing to do is to enable the texture with glEnable(GL_TEXTURE_2D). There's a hell of a lot of things that can be "enabled" with this function in OpenGL. Here, suffice to say that our texture(s) are enabled so that we can use them on our object, okay? The function has an opposite number, glDisable(), which is used to "disable" just about everything that can be enabled, as the program situations require, and is described on the same reference page as glEnable(). Note that at the end of the code block above, I disable GL_TEXTURE_2D, after I have used it.

Following that, I invoked glBindTexture(GL_TEXTURE_2D, ogl_tex). As "bind" suggests, this function "attaches" or "associates" (but does not specifically "place" or "position") the texture, referenced by its GLui nt identifier, to the object we are about to build. Enough said about that, really. Let's get onto setting that texture and making the object...

It's time to look at setting some parameters for that texture. For this, I use glTexParameterf() or glTexParameteri(). GL_TEXTURE_MAG_FILTER and GL_TEXTURE_MIN_FILTER basically define how the texture image is "compressed" or "spread" onto the object when the rendered siz e of the object is not equal to the true size of the texture bitmap. This will occur most of the time. Right here, I set both of them as GL_LINEAR, as I am not doing any mip-mapping just yet.

glTexParameter can also be used to set the "tiling" (wrapping) behavior characteristics of the texture on the object. GL_TEXTURE_WRAP_S determines how the texture will behave horizontally across the object, if it is repeated more than once, that is. More on how that is done in a minute. I want the texture to repeat itself mirroring itself every time it is rendered horizontally, so I use GL_MIRRORED_REPEAT.

GL_TEXTURE_WRAP_T determines how the texture will repeat vertically. Here I just want it to repeat and be a non-mirrored replica of itself, so I use GL_REPEAT.

In all cases above the target (the first parameter of glTexParameter) was my 2D texture, that is GL_TEXTURE_2D.

At last, I can get down to making my object. Fir st I will tell it what, exactly, I am planning to build, so that OpenGL has an idea of how many vertices will be involved. I emp loy glBegin(GL_QUADS) for this. A quadrilateral, with four vertices.

Now, I could just go ahead and make a QUAD with the following;
// DEMO EXAMPLE
glVertex3f(-3.5, -6.5, 0.0); // X,Y,Z bottom left
glVertex3f(3.5, -6.5, 0.0); // X,Y,Z bottom right
glVertex3f(3.5, 6.5, 0.0); // X,Y,Z top right
glVertex3f(-3.5, 6.5, 0.0); // X,Y,Z top left

...which is fairly self explanatory, but I will have wasted all that effort in setting up the texture.

I will "place", or "position" if you like, the texture on my QUAD by using glTexCoord2f(). Now that reference description I found a bit overly complicated for the present purposes. It would suffice here to say that these are more or less the same thing as u/v coords, as used in 3D modeling programs. In this case, take a look at the picture of the texture up above in this post. The lower left corner is (X,Y) position (0,0), the lower right (1,0), the top right (1,1) and the top left (0,1). The function glTexCoord2f() uses these coords to map the 2D texture onto the object, as many times as we want, abiding by the repeat properties set with the GL_TEXTURE_WRAP parameter. glTexCoord2f() is specified just before creating the respective vertex point with glVertex3f().

The following example would map the texture exactly onto the QUAD once;
// DEMO EXAMPLE
// Bottom left
glTexCoord2f(0.0, 0.0);
glVertex3f(-3.5, -6.5, 0.0);

// Bottom right
glTexCoord2f(1.0, 0.0);
glVertex3f(3.5, -6.5, 0.0);

// Top right
glTexCoord2f(1.0, 1.0);
glVertex3f(3.5, 6.5, 0.0);

// Top left
glTexCoord2f(0.0, 1.0);
glVertex3f(-3.5, 6.5, 0.0);
...and this would map it 2 times horizontally and once vertically...
// DEMO EXAMPLE
// Bottom left
glTexCoord2f(0.0, 0.0);
glVertex3f(-3.5, -6.5, 0.0);

// Bottom right
glTexCoord2f(2.0, 0.0);
glVertex3f(3.5, -6.5, 0.0);

// Top right
glTexCoord2f(2.0, 1.0);
glVertex3f(3.5, 6.5, 0.0);

// Top left
glTexCoord2f(0.0, 1.0);
glVertex3f(-3.5, 6.5, 0.0);
Once I have finished drawing my QUAD and placing the texture as I want it, I end the drawing with glEnd(), and disable work on the texture with glDisable(GL_TEXTURE_2D). Then it is just a case of flipping the buffer to the display, letting the image show for a few seconds, and cleaning up...
al_flip_display();
al_rest(10.0);

glDeleteTextures(1, &ogl_tex);
al_destroy_bitmap(bmp);
al_destroy_display(display);
return 0;
}

That would be it. You would get something like this...


More coming soon...