Home » Easy » Lesson 1: Textured Cube

Lesson 1: Textured Cube

In this tutorial we will explain to you how to make a textured cube using OpenGL and SDL. You will need to have the GNU C Compiler (short gcc) installed.  Furthermore you of course need OpenGL and SDL installed. If you are on Ubuntu Linux you can use the commands

sudo apt-get install libsdl*

sudo apt-get install gcc*

Once you have the environment set up, lets get started :-)

First create a directory for your project called /opengl/.  Create a file called cube.c  using your favorite editor inside this directory.

Paste the following code:

/*
*  OpenGL Textured Cube
*  https://talkera.org.cp-in-1.webhostbox.net/opengl/  
*  http://openglsamples.sf.net
*
*  Compilation trough:
*    gcc cube.c -lglut -lGL -lGLU -lSDL 
*
*/


#include 
#include 
#include 
#include 
#include "SDL/SDL.h"

/* screen width, height, and bit depth */
#define SCREEN_WIDTH  640
#define SCREEN_HEIGHT 480
#define SCREEN_BPP     16

/* Setup useful booleans */
#define TRUE  1
#define FALSE 0

/* This is our SDL surface */
SDL_Surface *surface;

/* Used for rotating the cube */
GLfloat rotationXaxis; 
GLfloat rotationYaxis; 
GLfloat rotationZaxis; 

/* Storage For One Texture ( NEW ) */
GLuint texture[1]; 

/* Release resources and quit  */
void Quit( int returnCode ) {
    SDL_Quit( );
    exit( returnCode );
}

/* Loads in a bitmap as a GL texture */
int LoadGLTextures( ) {
    int Status = FALSE;

    /* Create storage space for the texture */
    SDL_Surface *TextureImage[1]; 

    /* Load The Bitmap into Memory */
    if ((TextureImage[0] = SDL_LoadBMP("data/texture.bmp"))) {
            Status = TRUE;
            glGenTextures( 1, &texture[0] );
            glBindTexture( GL_TEXTURE_2D, texture[0] );
            glTexImage2D( GL_TEXTURE_2D, 0, 3, TextureImage[0]->w,
                          TextureImage[0]->h, 0, GL_BGR,
                          GL_UNSIGNED_BYTE, TextureImage[0]->pixels );
            glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
            glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
        }

    /* Free up some memory */
    if ( TextureImage[0] )
            SDL_FreeSurface( TextureImage[0] );

    return Status;
}

/* function to reset our viewport after a window resize */
int resizeWindow( int width, int height ) {
    /* Height / width ration */
    GLfloat ratio;
 
    /* Protect against a divide by zero */
    if ( height == 0 )
        height = 1;

    ratio = ( GLfloat )width / ( GLfloat )height;

    /* Setup our viewport. */
    glViewport( 0, 0, ( GLint )width, ( GLint )height );

    /*
     * change to the projection matrix and set
     * our viewing volume.
     */
    glMatrixMode( GL_PROJECTION );
    glLoadIdentity( );

    /* Set our perspective */
    gluPerspective( 45.0f, ratio, 0.1f, 100.0f );

    /* Make sure we're chaning the model view and not the projection */
    glMatrixMode( GL_MODELVIEW );

    /* Reset The View */
    glLoadIdentity( );

    return( TRUE );
}

/* function to handle key press events */
void handleKeyPress( SDL_keysym *keysym )
{
    switch ( keysym->sym )
        {
        case SDLK_ESCAPE:
            /* ESC key was pressed */
            Quit( 0 );
            break;
        case SDLK_F1:
            /* F1 key was pressed
             * this toggles fullscreen mode
             */
            SDL_WM_ToggleFullScreen( surface );
            break;
        default:
            break;
        }

    return;
}

/* OpenGL initialization function */
int initGL( GLvoid )
{

    /* Load in the texture */
    if ( !LoadGLTextures( ) )
        return FALSE;

    /* Enable Texture Mapping ( NEW ) */
    glEnable( GL_TEXTURE_2D );

    /* Enable smooth shading */
    glShadeModel( GL_SMOOTH );

    /* Set the background black */
    glClearColor( 0.0f, 0.0f, 0.0f, 0.5f );

    /* Depth buffer setup */
    glClearDepth( 1.0f );

    /* Enables Depth Testing */
    glEnable( GL_DEPTH_TEST );

    /* The Type Of Depth Test To Do */
    glDepthFunc( GL_LEQUAL );

    /* Really Nice Perspective Calculations */
    glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST );

    return( TRUE );
}

/* Here goes our drawing code */
int drawGLScene( GLvoid )
{
    /* Clear The Screen And The Depth Buffer */
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

    /* Move Into The Screen 5 Units */
    glLoadIdentity( );
    glTranslatef( 0.0f, 0.0f, -5.0f );

    glRotatef( rotationXaxis, 1.0f, 0.0f, 0.0f); /* Rotate On The X Axis */
    glRotatef( rotationYaxis, 0.0f, 1.0f, 0.0f); /* Rotate On The Y Axis */
    glRotatef( rotationZaxis, 0.0f, 0.0f, 1.0f); /* Rotate On The Z Axis */

    /* Select Our Texture */
    glBindTexture( GL_TEXTURE_2D, texture[0] );

    /*
     * Draw the cube. A cube consists of six quads, with four coordinates (glVertex3f) 
     * per quad.
     *
     */
    glBegin(GL_QUADS);
      /* Front Face */
      glTexCoord2f( 0.0f, 1.0f ); glVertex3f( -1.0f, -1.0f, 1.0f );
      glTexCoord2f( 1.0f, 1.0f ); glVertex3f(  1.0f, -1.0f, 1.0f );
      glTexCoord2f( 1.0f, 0.0f ); glVertex3f(  1.0f,  1.0f, 1.0f );
      glTexCoord2f( 0.0f, 0.0f ); glVertex3f( -1.0f,  1.0f, 1.0f );

      /* Back Face */
      glTexCoord2f( 0.0f, 0.0f ); glVertex3f( -1.0f, -1.0f, -1.0f );
      glTexCoord2f( 0.0f, 1.0f ); glVertex3f( -1.0f,  1.0f, -1.0f );
      glTexCoord2f( 1.0f, 1.0f ); glVertex3f(  1.0f,  1.0f, -1.0f );
      glTexCoord2f( 1.0f, 0.0f ); glVertex3f(  1.0f, -1.0f, -1.0f );

      /* Top Face */
      glTexCoord2f( 1.0f, 1.0f ); glVertex3f( -1.0f,  1.0f, -1.0f );
      glTexCoord2f( 1.0f, 0.0f ); glVertex3f( -1.0f,  1.0f,  1.0f );
      glTexCoord2f( 0.0f, 0.0f ); glVertex3f(  1.0f,  1.0f,  1.0f );
      glTexCoord2f( 0.0f, 1.0f ); glVertex3f(  1.0f,  1.0f, -1.0f );

      /* Bottom Face */
      /* Top Right Of The Texture and Quad */
      glTexCoord2f( 0.0f, 1.0f ); glVertex3f( -1.0f, -1.0f, -1.0f );
      glTexCoord2f( 1.0f, 1.0f ); glVertex3f(  1.0f, -1.0f, -1.0f );
      glTexCoord2f( 1.0f, 0.0f ); glVertex3f(  1.0f, -1.0f,  1.0f );
      glTexCoord2f( 0.0f, 0.0f ); glVertex3f( -1.0f, -1.0f,  1.0f );

      /* Right face */
      glTexCoord2f( 0.0f, 0.0f ); glVertex3f( 1.0f, -1.0f, -1.0f );
      glTexCoord2f( 0.0f, 1.0f ); glVertex3f( 1.0f,  1.0f, -1.0f );
      glTexCoord2f( 1.0f, 1.0f ); glVertex3f( 1.0f,  1.0f,  1.0f );
      glTexCoord2f( 1.0f, 0.0f ); glVertex3f( 1.0f, -1.0f,  1.0f );

      /* Left Face */
      glTexCoord2f( 1.0f, 0.0f ); glVertex3f( -1.0f, -1.0f, -1.0f );
      glTexCoord2f( 0.0f, 0.0f ); glVertex3f( -1.0f, -1.0f,  1.0f );
      glTexCoord2f( 0.0f, 1.0f ); glVertex3f( -1.0f,  1.0f,  1.0f );
      glTexCoord2f( 1.0f, 1.0f ); glVertex3f( -1.0f,  1.0f, -1.0f );
    glEnd( );

    /* Draw it to the screen */
    SDL_GL_SwapBuffers( );

    /* Rotate Cube */
    rotationXaxis += 0.3f; 
    rotationYaxis += 0.2f;

    return( TRUE );
}

/* https://talkera.org.cp-in-1.webhostbox.net/opengl/ */
int main( int argc, char **argv )
{
    /* Flags to pass to SDL_SetVideoMode */
    int videoFlags;
    /* main loop variable */
    int done = FALSE;
    /* used to collect events */
    SDL_Event event;
    /* this holds some info about our display */
    const SDL_VideoInfo *videoInfo;
    /* whether or not the window is active */
    int isActive = TRUE;

    /* initialize SDL */
    if ( SDL_Init( SDL_INIT_VIDEO ) hw_available )
        videoFlags |= SDL_HWSURFACE;
    else
        videoFlags |= SDL_SWSURFACE;

    /* This checks if hardware blits can be done */
    if ( videoInfo->blit_hw )
        videoFlags |= SDL_HWACCEL;

    /* Sets up OpenGL double buffering */
    SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );

    /* get a SDL surface */
    surface = SDL_SetVideoMode( SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP,
                                videoFlags );

    /* Verify there is a surface */
    if ( !surface )
        {
            fprintf( stderr,  "Video mode set failed: %s\n", SDL_GetError( ) );
            Quit( 1 );
        }

    /* initialize OpenGL */
    initGL( );

    /* resize the initial window */
    resizeWindow( SCREEN_WIDTH, SCREEN_HEIGHT );

    /* wait for events */
    while ( !done )
        {
            /* handle the events in the queue */

            while ( SDL_PollEvent( &event ) )
                {
                    switch( event.type )
                        {
                        case SDL_ACTIVEEVENT:
                            if ( event.active.gain == 0 )
                                isActive = FALSE;
                            else
                                isActive = TRUE;
                            break;                          
                        case SDL_VIDEORESIZE:
                            /* handle resize event */
                            surface = SDL_SetVideoMode( event.resize.w,
                                                        event.resize.h,
                                                        16, videoFlags );
                            if ( !surface )
                                {
                                    fprintf( stderr, "Could not get a surface after resize: %s\n", SDL_GetError( ) );
                                    Quit( 1 );
                                }
                            resizeWindow( event.resize.w, event.resize.h );
                            break;
                        case SDL_KEYDOWN:
                            /* handle key presses */
                            handleKeyPress( &event.key.keysym );
                           
                            break;
                        case SDL_QUIT:
                            /* handle quit requests */
                            done = TRUE;
                            break;
                        default:
                            break;
                        }
                }

            /* draw the scene */
            if ( isActive )
                drawGLScene( );
        }

    /* clean ourselves up and exit */
    Quit( 0 );

    /* Should never get here */
    return( 0 );
}

Then create a directory within it called /data/. Save this texture image into /opengl/data/  as a bitmap called texture.bmp.

Finally, open a terminal, go to the directory /opengl/  and type this command

gcc cube.c -lglut -lGL -lGLU -lSDL

This command compiles the source code above into a binary called a.out. Run the program using the command

./a.out

If you want another program name you could use

gcc cube.c -o cube -lglut -lGL -lGLU -lSDL

 

Explanation

A cube consists of 6 faces (also referred to as quads or squares).  Every face or square has 4 individual points, specified by glVertex3f.

      glVertex3f( -1.0f,  1.0f, -1.0f );
      glVertex3f( -1.0f,  1.0f,  1.0f );
      glVertex3f(  1.0f,  1.0f,  1.0f );
      glVertex3f(  1.0f,  1.0f, -1.0f );

The command

glBegin(GL_QUADS);

is necessary to tell OpenGL to draw quads. OpenGL has support for various shapes,  such as triangles and quads.

The command

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

set the camera.  Thus our camera is at (0,0,-5) and the position of the cube is around (0,0,1). If we would set it to

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

the camera would be more far away, and thus the cube would appear smaller.

The commands

    glRotatef( rotationXaxis, 1.0f, 0.0f, 0.0f); /* Rotate On The X Axis */
    glRotatef( rotationYaxis, 0.0f, 1.0f, 0.0f); /* Rotate On The Y Axis */
    glRotatef( rotationZaxis, 0.0f, 0.0f, 1.0f); /* Rotate On The Z Axis */

rotate the cube around their axis. You will notice the glRotatef function has four parameters. The last three are x, y and z-rotation.

The command

    SDL_GL_SwapBuffers( );

is mandatory. It sends all previously given drawing commands to the videocard GPU, thus drawing on screen. The rest of the code is initialization code.