Syllabus   Blank Homework   Quizzes  
Notes   Labs   Scores   Blank


Lecture Notes
Dr. Tong Yu, Sept. 2013
All the materials presented here are adopted from the textbook and the listed references.
    1. Introduction
    2. Line Drawing
    3. Drawing Objects
    4. More Drawing Tools
    5. Vertex Array
    6. Normal Vectors and Polygonal Models of Surfaces
    7. Viewing I -- Affine Transformations
    8. Viewing II -- Projections
    9. Color
    10. Lighting
    11. Blending, antialiasing, fog ..
    12. Display Lists, Bitmaps and Images
    Appendix. Games and SDL

    Games and SDL

    1. Introduction

      Simple DirectMedia Layer ( SDL ) is a cross-platform multimedia library designed to provide low level access to audio, keyboard, mouse, joystick, 3D hardware via OpenGL, and 2D video framebuffer. It is used by MPEG playback software, emulators, and many popular games.

    2. It can be used in:
      • games
      • game SDKs
      • emulators
      • demos
      • multimedia applications

    3. OpenGL is usually the fastest and most flexible way to do graphics under SDL

    4. Applications:
      • video -- it can set a video at any depth ( 8-bpp or greater ) with optional conversion; write directly to a linear graphics framebuffer; create surfaces with colorkey or alpha blending attributes
      • events -- provides event change detection like keyboard input, mouse input; each event can be enabled or disabled with SDL_EventState()
      • audio -- set audio playback of 8-bit and 16-bit audio, mono or stereo; audio runs independently in a separate thread, filled via a user callback mechanism; provides complete CD audio control API
      • threads -- provides simple thread creation API and simple binary semaphores for synchronization
      • timers -- gets the number of milliseconds elapsed and waits a specified number of milliseconds
    5. Initializing the Library

      Use SDL_Init() to dynamically load and initialize the library. This function takes a set of flags corresponding to the portions you want to activate:

      SDL_INIT_AUDIO
      SDL_INIT_VIDEO
      SDL_INIT_CDROM
      SDL_INIT_TIMER
      SDL_INIT_EVERYTHING

      Use SDL_Quit() to clean up the library when you are done with it.

      Tip:
      SDL dynamically loads the SDL library from the standard system library locations. Use the SDL_SetLibraryPath() function to use an alternate location for the dynamic libraries distributed with your application.
      Example:
      #include <stdlib.h>
      #include "SDL.h"
      
      main(int argc, char *argv[])
      {
          if ( SDL_Init(SDL_INIT_AUDIO|SDL_INIT_VIDEO) < 0 ) {
              fprintf(stderr, "Unable to init SDL: %s\n", SDL_GetError());
              exit(1);
          }
          //atexit(SDL_Quit);
          SDL_Quit();
          ...
      }
      

    6. Video
      Choosing and setting video modes (the easy way)

      You just choose your favorite bit-depth and resolution, and set it!

      Set the video mode with SDL_SetVideoMode(), which returns an SDL_Surface. All graphics in SDL, including the screen, are stored in a surface. Choose a video mode based on the following information: Full screen or windowed, Screen size, Window properties ( has border, resize, .. ), Bits per Pixel, Surface type ( software surface, hardware surface )

      Tip #1:
      You can find the fastest video depth supported by the hardware with the function SDL_GetVideoInfo().

      Tip #2:
      You can get a list of supported video resulutions at a particular bit-depth using the function SDL_ListModes().

      Example The following code asks for a 640x480 full screen software surface with a pixel format that is that same as the current display setting.:
      int options = (
          SDL_ANYFORMAT  |
          SDL_FULLSCREEN |
          SDL_SWSURFACE
      );
      
      SDL_Surface *screen = NULL;
      
      screen = SDL_SetVideoMode(640, 480, 0, options);
      if (NULL == screen)
      {
          printf("Can't set video mode");
          exit(1);
      }
        

      Example:
      {   SDL_Surface *screen;
      
          screen = SDL_SetVideoMode(640, 480, 16, SDL_SWSURFACE);
          if ( screen == NULL ) {
              fprintf(stderr, "Unable to set 640x480 video: %s\n", SDL_GetError());
              exit(1);
          }
      }
      

      Drawing pixels on the screen

      Drawing to the screen is done by writing directly to the graphics framebuffer, and calling the screen update function.

      Nothing you draw on a software surface is visible until it has been copied from memory to the display buffer on the video card. SDL provides two ways to do that: SDL_Flip() and SDL_UpdateRect(). SDL_Flip() copies the entire software surface to the screen. If your screen is set to 640x480 at 4 bytes per pixel, SDL_Flip() will copy 1.2 megabytes per frame and your frame rate will be limited by how fast your computer can copy images to the screen.

      SDL_UpdateRect() is designed to let you use a "dirty pixels" scheme. It lets you specify a list of rectangular areas that have been changed and only copies those areas to the screen. This technique is ideal for a game with a complex background but only a small number of moving or changing items. Tracking dirty pixels can give you a dramatic improvement in performance.

      Tip:
      If you know that you will be doing a lot of drawing, it is best to lock the screen (if necessary) once before drawing, draw while keeping a list of areas that need to be updated, and unlock the screen again before updating the display.
      Example:

      Drawing a pixel on a screen of arbitrary format

      void DrawPixel(SDL_Surface *screen, Uint8 R, Uint8 G, Uint8 B)
      {
          Uint32 color = SDL_MapRGB(screen->format, R, G, B);
      
          if ( SDL_MUSTLOCK(screen) ) {
              if ( SDL_LockSurface(screen) < 0 ) {
                  return;
              }
          }
          switch (screen->format->BytesPerPixel) {
              case 1: { /* Assuming 8-bpp */
      
                  Uint8 *bufp;
      
                  bufp = (Uint8 *)screen->pixels + y*screen->pitch + x;
                  *bufp = color;
              }
              break;
      
              case 2: { /* Probably 15-bpp or 16-bpp */
                  Uint16 *bufp;
      
                  bufp = (Uint16 *)screen->pixels + y*screen->pitch/2 + x;
                  *bufp = color;
              }
              break;
      
              case 3: { /* Slow 24-bpp mode, usually not used */
      
                  Uint8 *bufp;
      
                  bufp = (Uint8 *)screen->pixels + y*screen->pitch + x;
                  *(bufp+screen->format->Rshift/8) = R;
                  *(bufp+screen->format->Gshift/8) = G;
                  *(bufp+screen->format->Bshift/8) = B;
              }
              break;
      
              case 4: { /* Probably 32-bpp */
      
                  Uint32 *bufp;
      
                  bufp = (Uint32 *)screen->pixels + y*screen->pitch/4 + x;
                  *bufp = color;
              }
              break;
          }
          if ( SDL_MUSTLOCK(screen) ) {
              SDL_UnlockSurface(screen);
          }
          SDL_UpdateRect(screen, x, y, 1, 1);
      }
      
      Loading and displaying images

      SDL provides a single image loading routine for your convenience, SDL_LoadBMP().

      You can display images by using SDL_BlitSurface() to blit them into the graphics framebuffer. SDL_BlitSurface() automatically clips the blit rectangle, which should be passed to SDL_UpdateRects() to update the portion of the screen which has changed.

      Tip #1:
      If you are loading an image to be displayed many times, you can improve blitting speed by convert it to the format of the screen. The function SDL_DisplayFormat() does this conversion for you.

      Tip #2:
      Many sprite images have a transparent background. You can enable transparent blits (colorkey blit) with the SDL_SetColorKey() function.

      Example:
      void ShowBMP(char *file, SDL_Surface *screen, int x, int y)
      {
          SDL_Surface *image;
          SDL_Rect dest;
      
          /* Load the BMP file into a surface */
      
          image = SDL_LoadBMP(file);
          if ( image == NULL ) {
              fprintf(stderr, "Couldn't load %s: %s\n", file, SDL_GetError());
              return;
          }
      
          /* Blit onto the screen surface.
             The surfaces should not be locked at this point.
           */
          dest.x = x;
          dest.y = y;
          dest.w = image->w;
          dest.h = image->h;
          SDL_BlitSurface(image, NULL, screen, &dest);
      
          /* Update the changed portion of the screen */
          SDL_UpdateRects(screen, 1, &dest);
      }

    7. Events
      • Waiting for events

      Wait for events using the SDL_WaitEvent() function.

      Tip:
      SDL has international keyboard support, translating key events and placing the UNICODE equivalents into event.key.keysym.unicode. Since this has some processing overhead involved, it must be enabled using SDL_EnableUNICODE().
      Example:
      {
          SDL_Event event;
      
          SDL_WaitEvent(&event);
      
          switch (event.type) {
              case SDL_KEYDOWN:
                  printf("The %s key was pressed!\n",
                         SDL_GetKeyName(event.key.keysym.sym));
                  break;
              case SDL_QUIT:
                  exit(0);
          }
      }
      
      
      • Polling for events

      Poll for events using the SDL_PollEvent() function.

      Tip:
      You can peek at events in the event queue without removing them by passing the SDL_PEEKEVENT action to SDL_PeepEvents().
      Example:
      {
          SDL_Event event;
      
          while ( SDL_PollEvent(&event) ) {
              switch (event.type) {
                  case SDL_MOUSEMOTION:
                      printf("Mouse moved by %d,%d to (%d,%d)\n", 
                             event.motion.xrel, event.motion.yrel,
                             event.motion.x, event.motion.y);
                      break;
                  case SDL_MOUSEBUTTONDOWN:
                      printf("Mouse button %d pressed at (%d,%d)\n",
                             event.button.button, event.button.x, event.button.y);
                      break;
                  case SDL_QUIT:
                      exit(0);
              }
          }
      }
      
      
      • Polling event state

      In addition to handling events directly, each type of event has a function which allows you to check the application event state. If you use this exclusively, you should ignore all events with the SDL_EventState() function, and call SDL_PumpEvents() periodically to update the application event state.

      Tip:
      You can hide or show the system mouse cursor using SDL_ShowCursor().
      Example:
      {
          SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);
      }
      
      void CheckMouseHover(void)
      {
          int mouse_x, mouse_y;
      
          SDL_PumpEvents();
      
          SDL_GetMouseState(&mouse_x, &mouse_y);
          if ( (mouse_x < 32) && (mouse_y < 32) ) {
              printf("Mouse in upper left hand corner!\n");
          }
      }

    8. A Simple example

      Adopted from http://www.linuxdevcenter.com/pub/a/linux/2003/05/15/sdl_anim.html

      //softlines.cpp
      #include <stdlib.h>
      #include <stdio.h>
      #include <string.h>
      
      #include "SDL.h"
      
      //----------------------------------------------------------
      
      // A set of very useful macros 
      
      #define max(a,b) (((a) > (b)) ? (a) : (b))
      #define min(a,b) (((a) < (b)) ? (a) : (b))
      #define abs(a) (((a)<0) ? -(a) : (a))
      #define sign(a) (((a)<0) ? -1 : (a)>0 ? 1 : 0)
      
      //----------------------------------------------------------
      
      // The following code implements a Bresenham line drawing
      // algorithm. There are 4 separate routines each optimized
      // for one of the four pixel depths supported by SDL. SDL
      // support many pixel formats, but it only support 8, 16,
      // 24, and 32 bit pixels.
      
      //----------------------------------------------------------
      
      // Draw lines in 8 bit surfaces.
      
      static void line8(SDL_Surface *s, 
                        int x1, int y1, 
                        int x2, int y2, 
                        Uint32 color)
      {
        int d;
        int x;
        int y;
        int ax;
        int ay;
        int sx;
        int sy;
        int dx;
        int dy;
      
        Uint8 *lineAddr;
        Sint32 yOffset;
      
        dx = x2 - x1;  
        ax = abs(dx) << 1;  
        sx = sign(dx);
      
        dy = y2 - y1;  
        ay = abs(dy) << 1;  
        sy = sign(dy);
        yOffset = sy * s->pitch;
      
        x = x1;
        y = y1;
      
        lineAddr = ((Uint8 *)(s->pixels)) + (y * s->pitch);
        if (ax>ay)
        {                      /* x dominant */
          d = ay - (ax >> 1);
          for (;;)
          {
            *(lineAddr + x) = (Uint8)color;
      
            if (x == x2)
            {
              return;
            }
            if (d>=0)
            {
              y += sy;
              lineAddr += yOffset;
              d -= ax;
            }
            x += sx;
            d += ay;
          }
        }
        else
        {                      /* y dominant */
          d = ax - (ay >> 1);
          for (;;)
          {
            *(lineAddr + x) = (Uint8)color;
      
            if (y == y2)
            {
              return;
            }
            if (d>=0) 
            {
              x += sx;
              d -= ay;
            }
            y += sy;
            lineAddr += yOffset;
            d += ax;
          }
        }
      }
      
      //----------------------------------------------------------
      
      // Draw lines in 16 bit surfaces. Note that this code will
      // also work on 15 bit surfaces.
      
      static void line16(SDL_Surface *s, 
                         int x1, int y1, 
                         int x2, int y2, 
                         Uint32 color)
      {
        int d;
        int x;
        int y;
        int ax;
        int ay;
        int sx;
        int sy;
        int dx;
        int dy;
      
        Uint8 *lineAddr;
        Sint32 yOffset;
      
        dx = x2 - x1;  
        ax = abs(dx) << 1;  
        sx = sign(dx);
      
        dy = y2 - y1;  
        ay = abs(dy) << 1;  
        sy = sign(dy);
        yOffset = sy * s->pitch;
      
        x = x1;
        y = y1;
      
        lineAddr = ((Uint8 *)s->pixels) + (y * s->pitch);
        if (ax>ay)
        {                      /* x dominant */
          d = ay - (ax >> 1);
          for (;;)
          {
            *((Uint16 *)(lineAddr + (x << 1))) = (Uint16)color;
      
            if (x == x2)
            {
              return;
            }
            if (d>=0)
            {
              y += sy;
              lineAddr += yOffset;
              d -= ax;
            }
            x += sx;
            d += ay;
          }
        }
        else
        {                      /* y dominant */
          d = ax - (ay >> 1);
          for (;;)
          {
            *((Uint16 *)(lineAddr + (x << 1))) = (Uint16)color;
      
            if (y == y2)
            {
              return;
            }
            if (d>=0) 
            {
              x += sx;
              d -= ay;
            }
            y += sy;
            lineAddr += yOffset;
            d += ax;
          }
        }
      }
      
      //----------------------------------------------------------
      
      // Draw lines in 24 bit surfaces. 24 bit surfaces require
      // special handling because the pixels don't fall on even
      // address boundaries. Instead of being able to store a
      // single byte, word, or long you have to store 3
      // individual bytes. As a result 24 bit graphics is slower
      // than the other pixel sizes.
      
      static void line24(SDL_Surface *s, 
                         int x1, int y1, 
                         int x2, int y2, 
                         Uint32 color)
      {
        int d;
        int x;
        int y;
        int ax;
        int ay;
        int sx;
        int sy;
        int dx;
        int dy;
      
        Uint8 *lineAddr;
        Sint32 yOffset;
      
      #if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
        color <<= 8;
      #endif
      
        dx = x2 - x1;  
        ax = abs(dx) << 1;  
        sx = sign(dx);
      
        dy = y2 - y1;  
        ay = abs(dy) << 1;  
        sy = sign(dy);
        yOffset = sy * s->pitch;
      
        x = x1;
        y = y1;
      
        lineAddr = ((Uint8 *)(s->pixels)) + (y * s->pitch);
        if (ax>ay)
        {                      /* x dominant */
          d = ay - (ax >> 1);
          for (;;)
          {
            Uint8 *p = (lineAddr + (x * 3));
            memcpy(p, &color, 3);
      
            if (x == x2)
            {
              return;
            }
            if (d>=0)
            {
              y += sy;
              lineAddr += yOffset;
              d -= ax;
            }
            x += sx;
            d += ay;
          }
        }
        else
        {                      /* y dominant */
          d = ax - (ay >> 1);
          for (;;)
          {
            Uint8 *p = (lineAddr + (x * 3));
            memcpy(p, &color, 3);
      
            if (y == y2)
            {
              return;
            }
            if (d>=0) 
            {
              x += sx;
              d -= ay;
            }
            y += sy;
            lineAddr += yOffset;
            d += ax;
          }
        }
      }
      
      //----------------------------------------------------------
      
      // Draw lines in 32 bit surfaces. Note that this routine
      // ignores alpha values. It writes them into the surface
      // if they are included in the pixel, but does nothing
      // else with them.
      
      static void line32(SDL_Surface *s, 
                         int x1, int y1, 
                         int x2, int y2, 
                         Uint32 color)
      {
        int d;
        int x;
        int y;
        int ax;
        int ay;
        int sx;
        int sy;
        int dx;
        int dy;
      
        Uint8 *lineAddr;
        Sint32 yOffset;
      
        dx = x2 - x1;  
        ax = abs(dx) << 1;  
        sx = sign(dx);
      
        dy = y2 - y1;  
        ay = abs(dy) << 1;  
        sy = sign(dy);
        yOffset = sy * s->pitch;
      
        x = x1;
        y = y1;
      
        lineAddr = ((Uint8 *)(s->pixels)) + (y * s->pitch);
        if (ax>ay)
        {                      /* x dominant */
          d = ay - (ax >> 1);
          for (;;)
          {
            *((Uint32 *)(lineAddr + (x << 2))) = (Uint32)color;
      
            if (x == x2)
            {
              return;
            }
            if (d>=0)
            {
              y += sy;
              lineAddr += yOffset;
              d -= ax;
            }
            x += sx;
            d += ay;
          }
        }
        else
        {                      /* y dominant */
          d = ax - (ay >> 1);
          for (;;)
          {
            *((Uint32 *)(lineAddr + (x << 2))) = (Uint32)color;
      
            if (y == y2)
            {
              return;
            }
            if (d>=0) 
            {
              x += sx;
              d -= ay;
            }
            y += sy;
            lineAddr += yOffset;
            d += ax;
          }
        }
      }
      
      //----------------------------------------------------------
      
      // Examine the depth of a surface and select a line
      // drawing routine optimized for the bytes/pixel of the
      // surface.
      
      static void line(SDL_Surface *s, 
                       int x1, int y1, 
                       int x2, int y2, 
                       Uint32 color)
      {
        switch (s->format->BytesPerPixel)
        {
        case 1:
          line8(s, x1, y1, x2, y2, color);
          break;
        case 2:
          line16(s, x1, y1, x2, y2, color);
          break;
        case 3:
          line24(s, x1, y1, x2, y2, color);
          break;
        case 4:
          line32(s, x1, y1, x2, y2, color);
          break;
        }
      }
      
      //----------------------------------------------------------
      
      // sweepLine animates a line on a surface based on the
      // elapsed time.
      
      class sweepLine
      {
      private:
        SDL_Surface *s;             // The surface to draw on.
        Uint32 color;               // The color of the line.
        int last;                   // last time update() was
                                    // called.
        int maxx;                   // Maximum valid X value.
        int maxy;                   // Maximum valid Y value.
        float x1, y1;               // The current location
        float dx1, dy1;             // and velocity of the line
        float x2, y2;               // end points.
        float dx2, dy2;
      
        // movePoint computes the new location of a point based
        // on its initial location, its velocity, and the
        // elapsed time.
      
        void movePoint(float &x, float &y, 
                       float &dx, float &dy,
                       int dt)
        {
          // Compute the new X location.
      
          x += (dx * dt);
      
          // if the X value is off of the screen, move it back
          // on and reverse the velocity in the X direction.
      
          if (x >= maxx)
          {
            x = maxx;
            dx = -dx;
          }
          else if (x <= 0)
          {
            x = 0;
            dx = -dx;
          }
      
          // Same thing for Y.
          y += (dy * dt);
          if (y >= maxy)
          {
            y = maxy;
            dy = -dy;
          }
          else if (y <= 0)
          {
            y = 0;
            dy = -dy;
          }
        }
      
      public:
      
        // sweepLine animates a line on a surface. It is
        // initialized with a pointer to the surface to draw the
        // line on, a pixel value that specifies the color of
        // the line, the current time, and the initial locations
        // of the line end points and their
        // velocities. Velocities are specified in
        // pixels/millisecond.
      
        // This method initializes the class and forces the end
        // points of the lines to be inside the boundaries of
        // the surface. If it didn't do that the line drawing
        // code would try to write outside of the surface and
        // crash the program.
      
        sweepLine(SDL_Surface *s, 
                  Uint32 color,
                  int time,
                  float x1,  float y1,
                  float dx1, float dy1,
                  float x2,  float y2,
                  float dx2, float dy2): 
          s(s),
          color(color),
          last(time),
          x1(x1), y1(y1),
          dx1(dx1), dy1(dy1),
          x2(x2), y2(y2),
          dx2(dx2), dy2(dy2)
        {
      
          // Set the values of maxx and maxy to one less than
          // the width and height. Do this makes clipping easier
          // to code.
      
          maxx = 0;
          maxy = 0;
      
          if (NULL != s)
          {
            maxx = s->w - 1;
            maxy = s->h - 1;
          }
      
          // Force the line end points onto the screen.
      
          x1 = max(x1, 0);
          y1 = max(y1, 0);
      
          x2 = max(x2, 0);
          y2 = max(y2, 0);
      
          x1 = min(x1, maxx);
          y1 = min(y1, maxy);
      
          x2 = min(x2, maxx);
          y2 = min(y2, maxy);
        }
      
        void update(long now)
        {
          int dt = now - last;
          last = now;
      
          // Update the locations of the line end points.
      
          movePoint(x1, y1, dx1, dy1, dt);
          movePoint(x2, y2, dx2, dy2, dt);
      
          // Draw the line at its new location.
      
          line(s, 
               (int)x1, (int)y1, 
               (int)x2, (int)y2, 
               color);
        }
      
      };
      
      //----------------------------------------------------------
      
      // gameTime keeps track of game time as opposed to real
      // time. Game time can start and stop and even change its
      // speed while real time just keeps ticking along.
      
      class gameTime
      {
      private:
        int startTime;              // Last time the clock was
                                    // started.
        int baseTime;               // How much game time passed
                                    // before the last time the
                                    // clock was started.
        bool running;               // Is the clock running or
                                    // not?
      
      public:
      
        // Initialize the class variables. At this point no game
        // time has elapsed and the clock is not running.
      
        gameTime()
        {
          startTime = 0;
          baseTime = 0;
          running = false;
        }
      
        // Start the clock.
      
        void start()
        {
          if (!running)
          {
            startTime = SDL_GetTicks();
            running = true;
          }
        }
      
        // stop the clock
      
        void stop()
        {
          if (running)
          {
            baseTime = baseTime + (SDL_GetTicks() - startTime);
            running = false;
          }
        }
      
        // True if the clock is paused.
      
        bool stopped()
        {
          return !running;
        }
      
        // Get this clocks current time in milliseconds.
      
        int time()
        {
          if (running)
          {
            return baseTime + (SDL_GetTicks() - startTime);
          }
          else
          {
            return baseTime;
          }
        }
      };
      
      //----------------------------------------------------------
      
      int main(int argc, char **argv)
      {
      
        // Declare all the local variables.
      
        gameTime gt;
        char *name = argv[0];
      
        SDL_Surface *screen = NULL;
        SDL_Event event;
        SDL_PixelFormat *pf = NULL;
        Uint32 black;
        Uint32 red;
        Uint32 green;
        Uint32 blue;
      
        int screenWidth = 640;
        int screenHeight = 480;
      
        bool done = false;
      
        sweepLine *rl = NULL;
        sweepLine *gl = NULL;
        sweepLine *bl = NULL;
      
        // Try to initialize SDL. If it fails, then give up.
      
        if (-1 == SDL_Init(SDL_INIT_EVERYTHING))
        {
          printf("Can't initialize SDL\n");
          exit(1);
        }
      
        // Safety first. If the program exits in an unexpected
        // way the atexit() call should ensure that SDL will be
        // shut down properly and the screen returned to a
        // reasonable state.
      
        atexit(SDL_Quit);
      
        // Initialize the display. Here I'm asking for a 640x480
        // window with any pixel format and any pixel depth. If
        // you uncomment SDL_FULLSCREEN you should get a 640x480
        // full screen display.
      
        screen = SDL_SetVideoMode(screenWidth, 
                                  screenHeight, 
                                  0, 
                                  SDL_ANYFORMAT |
                                  //SDL_FULLSCREEN |
                                  SDL_SWSURFACE
                                  );
      
        if (NULL == screen)
        {
          printf("Can't set video mode\n");
          exit(1);
        }
      
        // Grab the pixel format for the screen. SDL_MapRGB()
        // needs the pixel format to create pixels that are laid
        // out correctly for the screen.
      
        pf = screen->format;
      
        //Create the pixel values used in the program. Black is
        //for clearing the background and the other three are
        //for line colors. Note that in SDL you specify color
        //intensities in the rang 0 to 255 (hex ff). That
        //doesn't mean that you always get 24 or 32 bits of
        //color. If the format doesn't support the full color
        //range, SDL scales it to the range that is correct for
        //the pixel format.
      
        black = SDL_MapRGB(pf, 0x00, 0x00, 0x00);
        red = SDL_MapRGB(pf, 0xff, 0x00, 0x00);
        green = SDL_MapRGB(pf, 0x00, 0xff, 0x00);
        blue = SDL_MapRGB(pf, 0x00, 0x00, 0xff);
      
        // Set the window caption and the icon caption for the
        // program. In this case I'm just setting it to whatever
        // the name of the program happens to be.
      
        SDL_WM_SetCaption(name, name);
      
        // Create the three animating lines. It is amazing to
        // see the different kinds of behavior you can get from
        // such a simple animation object.
      
        rl = new sweepLine(screen, 
                           red, 
                           gt.time(),
                           screen->w - 1, 0,
                           -0.3, 0,
                           0, screen->h - 1,
                           0.3, 0);
        gl = new sweepLine(screen, 
                           green, 
                           gt.time(),
                           0, 0,
                           0, 0.1,
                           screen->w - 1, screen->h - 1,
                           0, -0.1);
        bl = new sweepLine(screen, 
                           blue, 
                           gt.time(),
                           screen->w - 1, 0,
                           -0.1, -0.5,
                           0, screen->h - 1,
                           0.4, 0.2);
      
        // Start the game clock.
      
        gt.start();
      
        // The animation loop.
      
        while (!done)
        {
      
          // Loop while reading all pending event.
      
          while (!done && SDL_PollEvent(&event))
          {
            switch (event.type)
            {
      
              // Here we are looking for two special keys. If we
              // get an event telling us that the escape key has
              // been pressed the program will quit. If we see
              // the F1 key we either start or stop the
              // animation by starting or stopping the clock.
      
            case SDL_KEYDOWN:
              switch(event.key.keysym.sym)
              {
              case SDLK_ESCAPE:
                done = true;
                break;
      
              case SDLK_F1:
                if (gt.stopped())
                {
                  gt.start();
                }
                else
                {
                  gt.stop();
                }
                break;
      
              default:
                break;
              }
              break;
      
              // The SDL_QUIT event is generated when you click
              // on the close button on a window. If we see that
              // event we should exit the program. So, we do.
      
            case SDL_QUIT:
              done = true;
              break;
            }
          }
      
          // Erase the old picture by painting the whole buffer
          // black.
      
          SDL_FillRect(screen, NULL, black);
      
          // Get the current game time. Note that if the clock
          // is stopped this method will return the same value
          // over and over.
      
          int t = gt.time();
      
          // Based on the current time update the location of
          // each line and draw the line into the buffer.
      
          rl->update(t);
          gl->update(t);
          bl->update(t);
      
          // Since I'm using a software buffer the call to
          // SDL_Flip() copies the software buffer to the
          // display. That gives you the effect of double
          // buffering without asking for it and without the
          // speed you would get from a hardware double buffered
          // display.
      
          SDL_Flip(screen);
      
          // The call to SDL_Delay(10) forces the program to
          // pause for 10 milliseconds and has the effect of
          // limiting the frame rate to less than 100
          // frames/second. It also keeps the program from
          // hogging the CPU which seems to result in smoother
          // animation because the program isn't interrupted by
          // the operating system for long periods.
      
          SDL_Delay(10);
        }
      
        // When we get here, just clean up and quit. Yes, the
        // atexit() call makes this redundant. But, it doesn't
        // hurt and I'd rather be safe than sorry.
      
        SDL_Quit();
      }
      /*
      #Makefile
      #CS 420, Tong Yu, Fall 2005
      CC = g++
      PROG    = softlines
      #CFLAGS = -w -s -O2 -ansi -DSHM -D_REENTRANT -DHAVE_OPENGL
      CFLAGS  = -g -O2  -D_REENTRANT -DHAVE_OPENGL
      XLIBS   = -lX11 -lXext -lXmu -lXext -lXmu -lXt -lXi -lSM -lICE
      LIBS    = -lglut -lGLU -lGL
      LIBSDL =  -L/usr/local/lib -Wl,-rpath,-L/usr/local/lib -lSDL -lpthread
      INCLS   = -I/usr/X11R/include -I/share/mesa/include -I/usr/local/include/SDL
      LIBDIR  = -L/usr/X11/lib -L/usr/X11R6/lib
      #source codes
      SRCS = $(PROG).cpp
      #substitute .cpp by .o to obtain object filenames
      OBJS = $(SRCS:.cpp=.o)
       
      #$< evaluates to the target's dependencies,
      #$@ evaluates to the target
       
      $(PROG): $(OBJS)
              $(CC) -o $@ $(OBJS)  $(LIBDIR) $(LIBS) $(XLIBS) $(LIBSDL)
       
      $(OBJS):
              $(CC) -c  $*.cpp $(INCLS) $(CFLAGS)
                                                                                      
      clean:
              rm $(OBJS)
      
      */
        

    9. An OpenGL example on Clipping and Reflections using Stencil Buffer

      
      /*	ball3d.c
      	Adopted from the code created by Banu Cosmin aka Choko - 20 may 2000
       */
      
      #include <math.h>			
      #include <stdio.h>			
      #include <GL/gl.h>	// Header File For The OpenGL32 Library
      #include <GL/glu.h>	// Header File For The GLu32 Library
      #include "SDL.h"
      
      #define BOOL    int
      #define FALSE   0
      #define TRUE    1
      
      Uint8*		keys;			// Array Used For The Keyboard Routine
      BOOL		active=TRUE;		// Window Active Flag Set To TRUE By Default
      BOOL		fullscreen=FALSE;	// Fullscreen Flag Set To Fullscreen Mode By Default
      
      // Light Parameters
      static GLfloat LightAmb[] = {0.7f, 0.7f, 0.7f, 1.0f};	// Ambient Light
      static GLfloat LightDif[] = {1.0f, 1.0f, 1.0f, 1.0f};	// Diffuse Light
      static GLfloat LightPos[] = {4.0f, 4.0f, 6.0f, 1.0f};	// Light Position
      
      GLUquadricObj	*q;				// Quadratic For Drawing A Sphere
      
      GLfloat		xrot		=  0.0f;	// X Rotation
      GLfloat		yrot		=  0.0f;	// Y Rotation
      GLfloat		xrotspeed	=  0.0f;	// X Rotation Speed
      GLfloat		yrotspeed	=  0.0f;	// Y Rotation Speed
      GLfloat		zoom		= -7.0f;	// Depth Into The Screen
      GLfloat		height		=  2.0f;	// Height Of Ball From Floor
      
      GLuint		texture[3];			// 3 Textures
      
      
      GLvoid ReSizeGLScene(GLsizei width, GLsizei height)	// Resize And Initialize The GL Window
      {
        if (height==0)		// Prevent A Divide By Zero By
          height=1;			// Making Height Equal One
        glViewport(0,0,width,height);	// Reset The Current Viewport
        glMatrixMode(GL_PROJECTION);	// Select The Projection Matrix
        glLoadIdentity();		// Reset The Projection Matrix
      
        // Calculate The Aspect Ratio Of The Window
        gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,100.0f);
      
        glMatrixMode(GL_MODELVIEW);	// Select The Modelview Matrix
        glLoadIdentity();		// Reset The Modelview Matrix
      }
      
      SDL_Surface *LoadBMP(char *filename)
      {
        Uint8 *rowhi, *rowlo;
        Uint8 *tmpbuf, tmpch;
        SDL_Surface *image;
        int i, j;
      
        image = SDL_LoadBMP(filename);
        if ( image == NULL ) {
          fprintf(stderr, "Unable to load %s: %s\n", filename, SDL_GetError());
          return(NULL);
        }
      
        /* GL surfaces are upsidedown and RGB, not BGR :-) */
        tmpbuf = (Uint8 *)malloc(image->pitch);
        if ( tmpbuf == NULL ) {
          fprintf(stderr, "Out of memory\n");
          return(NULL);
        }
        rowhi = (Uint8 *)image->pixels;
        rowlo = rowhi + (image->h * image->pitch) - image->pitch;
        for ( i=0; i<image->h/2; ++i ) {
          for ( j=0; j<image->w; ++j ) {
      	tmpch = rowhi[j*3];
      	rowhi[j*3] = rowhi[j*3+2];
      	rowhi[j*3+2] = tmpch;
      	tmpch = rowlo[j*3];
      	rowlo[j*3] = rowlo[j*3+2];
      	rowlo[j*3+2] = tmpch;
          }
          memcpy(tmpbuf, rowhi, image->pitch);
          memcpy(rowhi, rowlo, image->pitch);
          memcpy(rowlo, tmpbuf, image->pitch);
          rowhi += image->pitch;
          rowlo -= image->pitch;
        }
        free(tmpbuf);
        return(image);
      }
      
      int LoadGLTextures() 		// Load Bitmaps And Convert To Textures
      {
        int Status=FALSE;		// Status Indicator
        int loop;
        SDL_Surface *TextureImage[3];	// Create Storage Space For The Textures
        memset(TextureImage,0,sizeof(void *)*3);	// Set The Pointer To NULL
        if ((TextureImage[0]=LoadBMP("Envwall.bmp")) &&  // Load The Floor Texture
              (TextureImage[1]=LoadBMP("Ball.bmp")) &&	  // Load the Light Texture
              (TextureImage[2]=LoadBMP("Envroll.bmp")))  // Load the Wall Texture
        {   
          Status=TRUE;					
          glGenTextures(3, &texture[0]);		// Create The Texture
          for (loop=0; loop<3; loop++) {		// Loop Through 3 Textures
      	glBindTexture(GL_TEXTURE_2D, texture[loop]);
      	glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[loop]->w, TextureImage[loop]->h, 
      		0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[loop]->pixels);
      	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
      	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
          }
          for (loop=0; loop<3; loop++) {		// Loop Through 3 Textures
      	if (TextureImage[loop]) {		// If Texture Exists
      	  SDL_FreeSurface(TextureImage[loop]);
      	}
          }
        }
        return Status;	
      }
      
      int InitGL(GLvoid)			// All Setup For OpenGL Goes Here
      {
        if (!LoadGLTextures())		// If Loading The Textures Failed
          return FALSE;								
        glShadeModel(GL_SMOOTH);		// Enable Smooth Shading
        glClearColor(0.2f, 0.5f, 1.0f, 1.0f);	// Background
        glClearDepth(1.0f);			// Depth Buffer Setup
        glClearStencil(0);			// Clear The Stencil Buffer To 0
        glEnable(GL_DEPTH_TEST);		// Enables Depth Testing
        glDepthFunc(GL_LEQUAL);		// The Type Of Depth Testing To Do
        glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);	// Really Nice Perspective Calculations
        glEnable(GL_TEXTURE_2D);		// Enable 2D Texture Mapping
      
        glLightfv(GL_LIGHT0, GL_AMBIENT, LightAmb);	// Set The Ambient Lighting For Light0
        glLightfv(GL_LIGHT0, GL_DIFFUSE, LightDif);	// Set The Diffuse Lighting For Light0
        glLightfv(GL_LIGHT0, GL_POSITION, LightPos);	// Set The Position For Light0
      
        glEnable(GL_LIGHT0);				// Enable Light 0
        glEnable(GL_LIGHTING);			// Enable Lighting
      
        q = gluNewQuadric();				// Create A New Quadratic
        gluQuadricNormals(q, GL_SMOOTH);		// Generate Smooth Normals For The Quad
        gluQuadricTexture(q, GL_TRUE);		// Enable Texture Coords For The Quad
      
        glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);	// Set Up Sphere Mapping
        glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);	// Set Up Sphere Mapping
      
        return TRUE;						// Initialization Went OK
      }
      
      void DrawObject()				// Draw Our Ball
      {
        glColor3f(1.0f, 1.0f, 1.0f);			// Set Color To White
        glBindTexture(GL_TEXTURE_2D, texture[1]);	// Select Texture 2 (1)
        gluSphere(q, 0.35f, 32, 16);			// Draw First Sphere
      
        glBindTexture(GL_TEXTURE_2D, texture[2]);	// Select Texture 3 (2)
        glColor4f(1.0f, 1.0f, 1.0f, 0.4f);		// Set Color To White With 40% Alpha
        glEnable(GL_BLEND);				// Enable Blending
        glBlendFunc(GL_SRC_ALPHA, GL_ONE);		// Set Blending Mode To Mix Based On SRC Alpha
        glEnable(GL_TEXTURE_GEN_S);			// Enable Sphere Mapping
        glEnable(GL_TEXTURE_GEN_T);			// Enable Sphere Mapping
      
        gluSphere(q, 0.35f, 32, 16);			// Draw Another Sphere Using New Texture
      						// Textures Will Mix Creating A MultiTexture Effect (Reflection)
        glDisable(GL_TEXTURE_GEN_S);			// Disable Sphere Mapping
        glDisable(GL_TEXTURE_GEN_T);			// Disable Sphere Mapping
        glDisable(GL_BLEND);				// Disable Blending
      }
      
      void DrawFloor()				// Draws The Floor
      {
        glBindTexture(GL_TEXTURE_2D, texture[0]);	// Select Texture 1 (0)
        glBegin(GL_QUADS);				// Begin Drawing A Quad
        glNormal3f(0.0, 1.0, 0.0);			// Normal Pointing Up
        glTexCoord2f(0.0f, 1.0f);			// Bottom Left Of Texture
        glVertex3f(-2.0, 0.0, 2.0);			// Bottom Left Corner Of Floor
      			
        glTexCoord2f(0.0f, 0.0f);			// Top Left Of Texture
        glVertex3f(-2.0, 0.0,-2.0);			// Top Left Corner Of Floor
      			
        glTexCoord2f(1.0f, 0.0f);			// Top Right Of Texture
        glVertex3f( 2.0, 0.0,-2.0);			// Top Right Corner Of Floor
      			
        glTexCoord2f(1.0f, 1.0f);			// Bottom Right Of Texture
        glVertex3f( 2.0, 0.0, 2.0);			// Bottom Right Corner Of Floor
        glEnd();					// Done Drawing The Quad
      }
      
      int DrawGLScene(GLvoid)				// Draw Everything
      {
        // Clip Plane Equations
        double eqr[] = {0.0f,-1.0f, 0.0f, 0.0f};	// Plane Equation To Use For The Reflected Objects
      
        // Clear Screen, Depth Buffer & Stencil Buffer
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
      
        glLoadIdentity();			// Reset The Modelview Matrix
        glTranslatef(0.0f, -0.6f, zoom);	// Zoom And Raise Camera Above The Floor (Up 0.6 Units)
        glColorMask(0,0,0,0);			// Set Color Mask
        glEnable(GL_STENCIL_TEST);		// Enable Stencil Buffer For "marking" The Floor
        glStencilFunc(GL_ALWAYS, 1, 1);	// Always Passes, 1 Bit Plane, 1 As Mask
        /* 
           Set The Stencil Buffer To 1  Where We Draw Any Polygon
           Keep If Test Fails, Keep If Test Passes But Buffer Test Fails 
           Replace If Test Passes 
        */
        glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);	
        glDisable(GL_DEPTH_TEST);		// Disable Depth Testing
        DrawFloor();				// Draw The Floor (Draws To The Stencil Buffer)
      					// We Only Want To Mark It In The Stencil Buffer
        glEnable(GL_DEPTH_TEST);		// Enable Depth Testing
        glColorMask(1,1,1,1);			// Set Color Mask to TRUE, TRUE, TRUE, TRUE
        glStencilFunc(GL_EQUAL, 1, 1);	// We Draw Only Where The Stencil Is 1
      					// (i.e. Where The Floor Was Drawn)
        glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); 	// Don't Change The Stencil Buffer
        glEnable(GL_CLIP_PLANE0);			// Enable Clip Plane For Removing Artifacts
      						// (When The Object Crosses The Floor)
        glClipPlane(GL_CLIP_PLANE0, eqr);		// Equation For Reflected Objects
        glPushMatrix();				// Push The Matrix Onto The Stack
        glScalef(1.0f, -1.0f, 1.0f);			// Mirror Y Axis
        glLightfv(GL_LIGHT0, GL_POSITION, LightPos);	// Set Up Light0
        glTranslatef(0.0f, height, 0.0f);		// Position The Object
        glRotatef(xrot, 1.0f, 0.0f, 0.0f);		// Rotate Local Coordinate System On X Axis
        glRotatef(yrot, 0.0f, 1.0f, 0.0f);		// Rotate Local Coordinate System On Y Axis
        DrawObject();					// Draw The Sphere (Reflection)
        glPopMatrix();				// Pop The Matrix Off The Stack
        glDisable(GL_CLIP_PLANE0);			// Disable Clip Plane For Drawing The Floor
        glDisable(GL_STENCIL_TEST);			// We Don't Need The Stencil Buffer Any More (Disable)
        glLightfv(GL_LIGHT0, GL_POSITION, LightPos);	// Set Up Light0 Position
        glEnable(GL_BLEND);				// Enable Blending (Otherwise The Reflected Object Wont Show)
        glDisable(GL_LIGHTING);			// Since We Use Blending, We Disable Lighting
        glColor4f(1.0f, 1.0f, 1.0f, 0.8f);		// Set Color To White With 80% Alpha
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Blending colors
        DrawFloor();					// Draw The Floor To The Screen
        glEnable(GL_LIGHTING);			// Enable Lighting
        glDisable(GL_BLEND);				// Disable Blending
        glTranslatef(0.0f, height, 0.0f);		// Position The Ball At Proper Height
        glRotatef(xrot, 1.0f, 0.0f, 0.0f);		// Rotate On The X Axis
        glRotatef(yrot, 0.0f, 1.0f, 0.0f);		// Rotate On The Y Axis
        DrawObject();					// Draw The Ball
        xrot += xrotspeed;				// Update X Rotation Angle By xrotspeed
        yrot += yrotspeed;				// Update Y Rotation Angle By yrotspeed
        glFlush();					// Flush The GL Pipeline
        return TRUE;					// Everything Went OK
      }
      
      void ProcessKeyboard()				// Process Keyboard Results
      {
        if (keys[SDLK_RIGHT])	yrotspeed += 0.08f;	// Right Arrow Pressed (Increase yrotspeed)
        if (keys[SDLK_LEFT])	yrotspeed -= 0.08f;	// Left Arrow Pressed (Decrease yrotspeed)
        if (keys[SDLK_DOWN])	xrotspeed += 0.08f;	// Down Arrow Pressed (Increase xrotspeed)
        if (keys[SDLK_UP])	xrotspeed -= 0.08f;	// Up Arrow Pressed (Decrease xrotspeed)
      
        if (keys[SDLK_a])	zoom +=0.05f;		// 'A' Key Pressed ... Zoom In
        if (keys[SDLK_z])	zoom -=0.05f;		// 'Z' Key Pressed ... Zoom Out
      
        if (keys[SDLK_PAGEUP]) height +=0.03f;	// Page Up Key Pressed Move Ball Up
        if (keys[SDLK_PAGEDOWN]) height -=0.03f;	// Page Down Key Pressed Move Ball Down
      }
      
      GLvoid KillGLWindow(GLvoid)			// Properly Kill The Window
      {
      }
      
      /*	This Code Creates Our OpenGL Window.  Parameters Are:			*
       *	title			- Title To Appear At The Top Of The Window	*
       *	width			- Width Of The GL Window Or Fullscreen Mode	*
       *	height			- Height Of The GL Window Or Fullscreen Mode	*
       *	bits			- Number Of Bits To Use For Color (8/16/24/32)	*
       *	fullscreenflag	- Use Fullscreen Mode (TRUE) Or Windowed Mode (FALSE)	*/
       
      BOOL CreateGLWindow(char* title, int width, int height, int bits, BOOL fullscreenflag)
      {
        Uint32 flags;
        int size;
      
        fullscreen=fullscreenflag;	// Set The Global Fullscreen Flag
        flags = SDL_OPENGL;
        if ( fullscreenflag ) {
          flags |= SDL_FULLSCREEN;
        }
        SDL_GL_SetAttribute( SDL_GL_STENCIL_SIZE, 1 );
        if ( SDL_SetVideoMode(width, height, 0, flags) == NULL ) {
          return FALSE;
        }
        SDL_GL_GetAttribute( SDL_GL_STENCIL_SIZE, &size);
        printf("Got a stencil buffer %d bits deep\n", size);
      
        SDL_WM_SetCaption(title, "opengl");
      
        ReSizeGLScene(width, height);		// Set Up Our Perspective GL Screen
      
        if (!InitGL())			// Initialize Our Newly Created GL Window
        {
          KillGLWindow();			// Reset The Display
          return FALSE;			// Return FALSE
        }
        return TRUE;				// Success
      }
      
      
      main(int argc, char *argv[])
      {
        BOOL	done=FALSE;			// Bool Variable To Exit Loop
      
        /* Initialize SDL */
        if ( SDL_Init(SDL_INIT_VIDEO) < 0 ) {
          fprintf(stderr, "Couldn't init SDL: %s\n", SDL_GetError());
          return 1;
        }
      
        // Create Our OpenGL Window
        if (!CreateGLWindow("Stencil & Reflection Demo", 640, 480, 32, fullscreen))
        {
          SDL_Quit();
          return 0;				// Quit If Window Was Not Created
        }
      
        while(!done) {			// Loop That Runs While done=FALSE
          SDL_Event event;
          while ( SDL_PollEvent(&event) ) {
      	switch (event.type) {
      	  case SDL_QUIT:
      	    done=TRUE;			// If So done=TRUE
      	    break;
      	  default:
      	    break;
      	}
          }
          keys = SDL_GetKeyState(NULL);
      
          // Draw The Scene.  Watch For ESC Key And Quit Messages From DrawGLScene()
          if ((active && !DrawGLScene()) || keys[SDLK_ESCAPE])// Active? Was There A Quit Received?
      	done=TRUE;			// ESC or DrawGLScene Signalled A Quit
          else				// Not Time To Quit, Update Screen
      	SDL_GL_SwapBuffers();		// Swap Buffers (Double Buffering)
          ProcessKeyboard();			// Processed Keyboard Presses
        }
      
        // Shutdown
        KillGLWindow();			// Kill The Window
        SDL_Quit();
        return 0;				// Exit The Program
      }
        

      The Makefile

      #sample Makefile for using OpenGL of Red Hat Linux 7.x
      #CS 420, Tong Yu, Fall 2002
      CC = gcc 
      PROG	= ball3d
      #CFLAGS	= -w -s -O2 -ansi -DSHM -D_REENTRANT -DHAVE_OPENGL
      CFLAGS	= -g -O2  -D_REENTRANT -DHAVE_OPENGL
      XLIBS	= -lX11 -lXext -lXmu -lXext -lXmu -lXt -lXi -lSM -lICE
      LIBS	= -lglut -lGLU -lGL
      LIBSDL =  -L/usr/local/lib -Wl,-rpath,/usr/local/lib -lSDL -lpthread
      INCLS	= -I/usr/X11R/include -I/share/mesa/include -I/usr/local/include/SDL 
      LIBDIR	= -L/usr/X11/lib -L/usr/X11R6/lib 
      #source codes
      SRCS = ball3d.c
      #substitute .c by .o to obtain object filenames
      OBJS = $(SRCS:.c=.o)
      
      #$< evaluates to the target's dependencies, 
      #$@ evaluates to the target
      
      $(PROG): $(OBJS)
      	$(CC) -o $@ $(OBJS)  $(LIBDIR) $(LIBS) $(XLIBS) $(LIBSDL)
      $(OBJS): 
      	$(CC) -c  $*.c $(INCLS) $(CFLAGS)
      clean:
      	rm $(OBJS) 
        

      You need the following 3 bmp images to create the graphics

      Ball.bmp

      Envroll.bmp

      Envwall.bmp

    10. Audio
      • Opening the audio device

      You need to have a callback function written which mixed your audio data and puts it in the audio stream. After that, choose your desired audio format and rate, and open the audio device.

      The audio won't actually start playing until you call SDL_PauseAudio(0), allowing you to perform other audio initialization as needed before your callback function is run. After you are done using the sound output, you should close it with the SDL_CloseAudio() function.

      Tip:
      If your application can handle different audio formats, pass a second SDL_AudioSpec pointer to SDL_OpenAudio() to get the actual hardware audio format. If you leave the second pointer NULL, the audio data will be converted to the hardware audio format at runtime.
      Example:
      
      #include "SDL.h"
      #include "SDL_audio.h"
      {
          extern void mixaudio(void *unused, Uint8 *stream, int len);
          SDL_AudioSpec fmt;
      
          /* Set 16-bit stereo audio at 22Khz */
      
          fmt.freq = 22050;
          fmt.format = AUDIO_S16;
          fmt.channels = 2;
          fmt.samples = 512;        /* A good value for games */
          fmt.callback = mixaudio;
          fmt.userdata = NULL;
      
          /* Open the audio device and start playing sound! */
          if ( SDL_OpenAudio(&fmt, NULL) < 0 ) {
              fprintf(stderr, "Unable to open audio: %s\n", SDL_GetError());
              exit(1);
          }
          SDL_PauseAudio(0);
      
          ...
      
          SDL_CloseAudio();
      }
      
      
      • Loading and playing sounds

      SDL provides a single sound loading routine for your convenience, SDL_LoadWAV(). After you load your sounds, you should convert them to the audio format of the sound output using SDL_ConvertAudio(), and make them available to your mixing function.

      Tip:
      SDL audio facilities are designed for a low level software audio mixer. A complete example mixer implementation available under the LGPL license can be found in the SDL demos archive.
      Example:
      #define NUM_SOUNDS 2
      struct sample {
          Uint8 *data;
          Uint32 dpos;
          Uint32 dlen;
      } sounds[NUM_SOUNDS];
      
      
      void mixaudio(void *unused, Uint8 *stream, int len)
      {
          int i;
          Uint32 amount;
      
          for ( i=0; i<NUM_SOUNDS; ++i ) {
              amount = (sounds[i].dlen-sounds[i].dpos);
              if ( amount > len ) {
                  amount = len;
              }
              SDL_MixAudio(stream, &sounds[i].data[sounds[i].dpos], amount, SDL_MIX_MAXVOLUME);
              sounds[i].dpos += amount;
          }
      }
      
      
      void PlaySound(char *file)
      {
          int index;
          SDL_AudioSpec wave;
          Uint8 *data;
          Uint32 dlen;
          SDL_AudioCVT cvt;
      
          /* Look for an empty (or finished) sound slot */
          for ( index=0; index<NUM_SOUNDS; ++index ) {
              if ( sounds[index].dpos == sounds[index].dlen ) {
                  break;
              }
          }
          if ( index == NUM_SOUNDS )
              return;
      
          /* Load the sound file and convert it to 16-bit stereo at 22kHz */
      
          if ( SDL_LoadWAV(file, &wave, &data, &dlen) == NULL ) {
              fprintf(stderr, "Couldn't load %s: %s\n", file, SDL_GetError());
              return;
          }
          SDL_BuildAudioCVT(&cvt, wave.format, wave.channels, wave.freq,
                                  AUDIO_S16,   2,             22050);
          cvt.buf = malloc(dlen*cvt.len_mult);
          memcpy(cvt.buf, data, dlen);
          cvt.len = dlen;
          SDL_ConvertAudio(&cvt);
          SDL_FreeWAV(data);
      
          /* Put the sound data in the slot (it starts playing immediately) */
          if ( sounds[index].data ) {
              free(sounds[index].data);
          }
          SDL_LockAudio();
          sounds[index].data = cvt.buf;
          sounds[index].dlen = cvt.len_cvt;
          sounds[index].dpos = 0;
          SDL_UnlockAudio();
      }
      

      A complete Example

      // game_audio.c
      // sample code using audio 
      // Adopted from code  created by Jeff Molofee '99
      // and ported to SDL by Marius Andra '2000 (marius@hot.ee))
      
      #include <stdio.h>
      #include <stdarg.h>
      #include <stdlib.h>
      #include <GL/gl.h>	// Header File For The OpenGL32 Library
      #include <GL/glu.h>	// Header File For The GLu32 Library
      #include "SDL.h"
      #include "SDL_timer.h"
      
      #ifdef SOUND
      #include "SDL_mixer.h"
      #endif
      
      char vline[11][10];
      char hline[10][11];
      char ap,filled,gameover,anti=1,active=1,fullscreen=1;
      
      
      #ifdef SOUND
      Mix_Chunk *complete, *die, *freeze, *hglass;                  // sfx
      Mix_Music *music;                                             // music
      #endif
      int loop1,loop2,delay,adjust=3,lives=5,level=1,level2=1,stage=1;
      
      struct object
      {
        int fx,fy,x,y;
        float spin;
      };
      
      struct object player;
      struct object enemy[9];
      struct object hourglass;
      
      int steps[6]={1,2,4,5,10,20};
      
      GLuint texture[2], base;
      
      float TimerGetTime()
      {
        return SDL_GetTicks();
      }
      
      #ifdef SOUND
      Mix_Chunk * load_sound(char * file) 	// Load a sound file
      {
        Mix_Chunk * sound;
        sound = Mix_LoadWAV(file);
        if (!sound)
        {
          fprintf(stderr, "Error: %s\n\n", SDL_GetError());
          SDL_Quit();
          exit(2);
        }
        return(sound);
      }
      #endif
      
      void ResetObjects()
      {
        player.x=0;
        player.y=0;
        player.fx=0;
        player.fy=0;
      
        for(loop1=0;loop1<(stage*level);loop1++)
        {
          enemy[loop1].x=5+rand()%6;
          enemy[loop1].y=rand()%11;
          enemy[loop1].fx=enemy[loop1].x*60;
          enemy[loop1].fy=enemy[loop1].y*40;
        }
      }
      
      SDL_Surface *LoadBMP(char *filename)
      {
          Uint8 *rowhi, *rowlo;
          Uint8 *tmpbuf, tmpch;
          SDL_Surface *image;
          int i, j;
      
          image = SDL_LoadBMP(filename);
          if ( image == NULL ) {
              fprintf(stderr, "Unable to load %s: %s\n", filename, SDL_GetError());
              return(NULL);
          }
      
          /* GL surfaces are upsidedown and RGB, not BGR :-) */
          tmpbuf = (Uint8 *)malloc(image->pitch);
          if ( tmpbuf == NULL ) {
              fprintf(stderr, "Out of memory\n");
              return(NULL);
          }
          rowhi = (Uint8 *)image->pixels;
          rowlo = rowhi + (image->h * image->pitch) - image->pitch;
          for ( i=0; i<image->h/2; ++i ) {
              for ( j=0; j<image->w; ++j ) {
                  tmpch = rowhi[j*3];
                  rowhi[j*3] = rowhi[j*3+2];
                  rowhi[j*3+2] = tmpch;
                  tmpch = rowlo[j*3];
                  rowlo[j*3] = rowlo[j*3+2];
                  rowlo[j*3+2] = tmpch;
              }
              memcpy(tmpbuf, rowhi, image->pitch);
              memcpy(rowhi, rowlo, image->pitch);
              memcpy(rowlo, tmpbuf, image->pitch);
              rowhi += image->pitch;
              rowlo -= image->pitch;
          }
          free(tmpbuf);
          return(image);
      }
      
      // Load Bitmaps And Convert To Textures
      void LoadGLTextures(void)
      {
        // A bit messy, isn't it?
      
        // Load Texture
        SDL_Surface *image1;
      
        image1 = LoadBMP("Font.bmp");
        if (!image1) {
          SDL_Quit();
          exit(1);
        }
      
        // Create Texture
        glGenTextures(1, &texture[0]);
        glBindTexture(GL_TEXTURE_2D, texture[0]);   // 2d texture (x and y size)
      
        // scale linearly when image bigger than texture
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); 
        
        // scale linearly when image smalled than texture
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); 
      
        /* 2d texture, level of detail 0 (normal), 3 components (red, green, blue), x size from 
      	image, y size from image,
           border 0 (normal), rgb color data, unsigned byte data, and finally the data itself.
        */
        glTexImage2D(GL_TEXTURE_2D, 0, 3, image1->w, image1->h, 0, GL_RGB, GL_UNSIGNED_BYTE, image1->pixels);
      
        image1 = LoadBMP("Image.bmp");
        if (!image1) {
          SDL_Quit();
          exit(1);
        }
      
        // Create Texture
        glGenTextures(1, &texture[1]);
        glBindTexture(GL_TEXTURE_2D, texture[1]);   // 2d texture (x and y size)
      
        // scale linearly when image bigger than texture
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); 
        // scale linearly when image smaller than texture
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); 
      
        // 2d texture, level of detail 0 (normal), 3 components (red, green, blue), x size from image, y size from image,
        // border 0 (normal), rgb color data, unsigned byte data, and finally the data itself.
        glTexImage2D(GL_TEXTURE_2D, 0, 3, image1->w, image1->h, 0, GL_RGB, GL_UNSIGNED_BYTE, image1->pixels);
      }
      
      GLvoid BuildFont(GLvoid)
      {
        float cx,cy;
        base=glGenLists(256);				// Creating 256 Display Lists
        glBindTexture(GL_TEXTURE_2D, texture[0]);	// Select Our Font Texture
        for (loop1=0; loop1<256; loop1++) {		// Loop Through All 256 Lists
          cx=(float)(loop1%16)/16.0f;			// X Position Of Current Character
          cy=(float)(loop1/16)/16.0f;			// Y Position Of Current Character
      
          glNewList(base+loop1,GL_COMPILE);		// Start Building A List
          glBegin(GL_QUADS);				// Use A Quad For Each Character
            glTexCoord2f(cx,1.0f-cy-0.0625f);		// Texture Coord (Bottom Left)
            glVertex2d(0,16);				// Vertex Coord (Bottom Left)
            glTexCoord2f(cx+0.0625f,1.0f-cy-0.0625f);	// Texture Coord (Bottom Right)
            glVertex2i(16,16);			// Vertex Coord (Bottom Right)
            glTexCoord2f(cx+0.0625f,1.0f-cy);		// Texture Coord (Top Right)
            glVertex2i(16,0);				// Vertex Coord (Top Right)
            glTexCoord2f(cx,1.0f-cy);			// Texture Coord (Top Left)
            glVertex2i(0,0);				// Vertex Coord (Top Left)
          glEnd();					// Done Building Our Quad (Character)
          glTranslated(15,0,0);			// Move To The Right Of The Character
          glEndList();				// Done Building The Display List
        }						// Loop Until All 256 Are Built
      }
      
      GLvoid KillFont(GLvoid)
      {
        glDeleteLists(base,256);
      }
      
      GLvoid glPrint(GLint x,GLint y,int set, const char *text)
      {
        if (set>1)	// Did User Choose An Invalid Character Set? 
          set=1;	// If So, Select Set 1 (Italic)
      
        glEnable(GL_TEXTURE_2D);	// Enable Texture Mapping
        glLoadIdentity();		// Reset The Modelview Matrix
        glTranslated(x,y,0);		// Position The Text (0,0 - Bottom Left)
        glListBase(base-32+(128*set));// Choose The Font Set (0 or 1)
      
        if (set==0)			// If Set 0 Is Being Used Enlarge Font
          glScalef(1.5f,2.0f,1.0f);	// Enlarge Font Width And Height
      
        glCallLists(strlen(text),GL_UNSIGNED_BYTE, text);	// Write The Text To The Screen
        glDisable(GL_TEXTURE_2D);				// Disable Texture Mapping
      }
      
      // A general OpenGL initialization function.  Sets all of the initial parameters. 
      // We call this right after our OpenGL window is created.
      void InitGL(int Width, int Height)	   
      {
        LoadGLTextures();			// Load The Texture(s)
        BuildFont();
        glShadeModel(GL_SMOOTH);
      
        glClearColor(0.0f, 0.0f, 0.0f, 0.5f);	// Clear The Background Color To Blue
        glClearDepth(1.0);			// Enables Clearing Of The Depth Buffer
        glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
      
        glViewport(0, 0, Width, Height);
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();			// Reset The Projection Matrix
      
        glOrtho(0.0f,Width,Height,0.0f,-1.0f,1.0f);
      
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
      }
      
      /* The main drawing function. */
      int display()
      {
        char temp[256];
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);	// Clear Screen And Depth Buffer
        glBindTexture(GL_TEXTURE_2D, texture[0]);		// Select Our Font Texture
        glColor3f(1.0f,0.5f,1.0f);				// Set Color To Purple
        glPrint(207,24,0,"GRID CRAZY");	// Write GRID CRAZY On The Screen
        glColor3f(1.0f,1.0f,0.0f);		// Set Color To Yellow
      
        sprintf(temp,"Level:%2i",level2);
        glPrint(20,20,1,temp);		// Write Actual Level Stats
        sprintf(temp,"Stage:%2i",stage);
        glPrint(20,40,1,temp);		// Write Stage Stats
      
        if (gameover) {	// Is The Game Over?
          glColor3ub(rand()%255,rand()%255,rand()%255); // Pick A Random Color
          glPrint(472,20,1,"GAME OVER");	// Write GAME OVER To The Screen
          glPrint(456,40,1,"PRESS SPACE");	// Write PRESS SPACE To The Screen
        }
      
        for (loop1=0; loop1<lives-1; loop1++){// Loop Through Lives Minus Current Life
          glLoadIdentity();			// Reset The View
          glTranslatef(490+(loop1*40.0f),40.0f,0.0f);	// Move To The Right Of Our Title Text
          glRotatef(-player.spin,0.0f,0.0f,1.0f);	// Rotate Counter Clockwise
          glColor3f(0.0f,1.0f,0.0f);		// Set Player Color To Light Green
          glBegin(GL_LINES);			// Start Drawing Our Player Using Lines
            glVertex2d(-5,-5);		// Top Left Of Player
            glVertex2d( 5, 5);		// Bottom Right Of Player
            glVertex2d( 5,-5);		// Top Right Of Player
            glVertex2d(-5, 5);		// Bottom Left Of Player
          glEnd();				// Done Drawing The Player
          glRotatef(-player.spin*0.5f,0.0f,0.0f,1.0f); // Rotate Counter Clockwise
          glColor3f(0.0f,0.75f,0.0f);		// Set Player Color To Dark Green
          glBegin(GL_LINES);			// Start Drawing Our Player Using Lines
            glVertex2d(-7, 0);		// Left Center Of Player
            glVertex2d( 7, 0);		// Right Center Of Player
            glVertex2d( 0,-7);		// Top Center Of Player
            glVertex2d( 0, 7);		// Bottom Center Of Player
          glEnd();				// Done Drawing The Player
        } //for loop1
      
        filled=1;				// Set Filled To 1 Before Testing
        glLineWidth(2.0f);			// Set Line Width For Cells To 2.0f
        glDisable(GL_LINE_SMOOTH);		// Disable Antialiasing
        glLoadIdentity();			// Reset The Current Modelview Matrix
        for (loop1=0; loop1<11; loop1++) {	// Loop From Left To Right
          for (loop2=0; loop2<11; loop2++) {	// Loop From Top To Bottom
            glColor3f(0.0f,0.5f,1.0f);	// Set Line Color To Blue
            if (hline[loop1][loop2]){		// Has The Horizontal Line Been Traced
      	glColor3f(1.0f,1.0f,1.0f);	// If So, Set Line Color To White
            }
      
            if (loop1<10) {			// Dont Draw To Far Right
      	if (!hline[loop1][loop2]){	// If A Horizontal Line Isn't Filled
      	  filled=0;			// filled Becomes 0
              }
              glBegin(GL_LINES);		// Start Drawing Horizontal Cell Borders
      	  glVertex2d(20+(loop1*60),70+(loop2*40));// Left Side Of Horizontal Line
      	  glVertex2d(80+(loop1*60),70+(loop2*40));// Right Side Of Horizontal Line
              glEnd();				// Done Drawing Horizontal Cell Borders
            } //for loop2
      
            glColor3f(0.0f,0.5f,1.0f);		// Set Line Color To Blue
            if (vline[loop1][loop2]) {		// Has The Horizontal Line Been Traced
              glColor3f(1.0f,1.0f,1.0f);	// If So, Set Line Color To White
            }
            if (loop2<10) {			// Dont Draw To Far Down
            	if (!vline[loop1][loop2]) {	// If A Verticle Line Isn't Filled
      	  filled=0;			// filled Becomes 0
              }
              glBegin(GL_LINES);		// Start Drawing Verticle Cell Borders
      	  glVertex2d(20+(loop1*60),70+(loop2*40));// Left Side Of Horizontal Line
      	  glVertex2d(20+(loop1*60),110+(loop2*40));// Right Side Of Horizontal Line
              glEnd();				// Done Drawing Verticle Cell Borders
            }
      
            glEnable(GL_TEXTURE_2D);		// Enable Texture Mapping
            glColor3f(1.0f,1.0f,1.0f);		// Bright White Color
            glBindTexture(GL_TEXTURE_2D, texture[1]);	// Select The Tile Image
            if ((loop1<10) && (loop2<10)) {	// If In Bounds, Fill In Traced Boxes
            	// Are All Sides Of The Box Traced?
            	if (hline[loop1][loop2] && hline[loop1][loop2+1] && vline[loop1][loop2] 
      		&& vline[loop1+1][loop2]) {
      	  glBegin(GL_QUADS);		// Draw A Textured Quad
      	    glTexCoord2f((float)(loop1/10.0f)+0.1f,1.0f-((float)(loop2/10.0f)));
      	    glVertex2d(20+(loop1*60)+59,(70+loop2*40+1));		// Top Right
      	    glTexCoord2f((float)(loop1/10.0f),1.0f-((float)(loop2/10.0f)));
      	    glVertex2d(20+(loop1*60)+1,(70+loop2*40+1));		// Top Left
      	    glTexCoord2f((float)(loop1/10.0f),1.0f-((float)(loop2/10.0f)+0.1f));
      	    glVertex2d(20+(loop1*60)+1,(70+loop2*40)+39);		// Bottom Left
      	    glTexCoord2f((float)(loop1/10.0f)+0.1f,1.0f-((float)(loop2/10.0f)+0.1f));
      	    glVertex2d(20+(loop1*60)+59,(70+loop2*40)+39);	// Bottom Right
      	  glEnd();			// Done Texturing The Box
              } //if
            } //if
            glDisable(GL_TEXTURE_2D);		// Disable Texture Mapping
          } //for loop1
        }
        glLineWidth(1.0f);			// Set The Line Width To 1.0f
      
        if (anti)				// Is Anti 1?
          glEnable(GL_LINE_SMOOTH);		// If So, Enable Antialiasing
      
        if (hourglass.fx==1) {		// If fx=1 Draw The Hourglass
          glLoadIdentity();			// Reset The Modelview Matrix
          glTranslatef(20.0f+(hourglass.x*60),70.0f+(hourglass.y*40),0.0f);	// Move To The Fine Hourglass Position
          glRotatef(hourglass.spin,0.0f,0.0f,1.0f);	// Rotate Clockwise
          glColor3ub(rand()%255,rand()%255,rand()%255);// Set Hourglass Color To Random Color
          glBegin(GL_LINES);			// Start Drawing Our Hourglass Using Lines
            glVertex2d(-5,-5);		// Top Left Of Hourglass
            glVertex2d( 5, 5);		// Bottom Right Of Hourglass
            glVertex2d( 5,-5);		// Top Right Of Hourglass
            glVertex2d(-5, 5);		// Bottom Left Of Hourglass
            glVertex2d(-5, 5);		// Bottom Left Of Hourglass
            glVertex2d( 5, 5);		// Bottom Right Of Hourglass
            glVertex2d(-5,-5);		// Top Left Of Hourglass
            glVertex2d( 5,-5);		// Top Right Of Hourglass
          glEnd();				// Done Drawing The Hourglass
        }
      
        glLoadIdentity();			// Reset The Modelview Matrix
        glTranslatef(player.fx+20.0f,player.fy+70.0f,0.0f);	// Move To The Fine Player Position
        glRotatef(player.spin,0.0f,0.0f,1.0f);// Rotate Clockwise
        glColor3f(0.0f,1.0f,0.0f);		// Set Player Color To Light Green
        glBegin(GL_LINES);			// Start Drawing Our Player Using Lines
          glVertex2d(-5,-5);			// Top Left Of Player
          glVertex2d( 5, 5);			// Bottom Right Of Player
          glVertex2d( 5,-5);			// Top Right Of Player
          glVertex2d(-5, 5);			// Bottom Left Of Player
        glEnd();				// Done Drawing The Player
        glRotatef(player.spin*0.5f,0.0f,0.0f,1.0f);	// Rotate Clockwise
        glColor3f(0.0f,0.75f,0.0f);		// Set Player Color To Dark Green
        glBegin(GL_LINES);			// Start Drawing Our Player Using Lines
          glVertex2d(-7, 0);			// Left Center Of Player
          glVertex2d( 7, 0);			// Right Center Of Player
          glVertex2d( 0,-7);			// Top Center Of Player
          glVertex2d( 0, 7);			// Bottom Center Of Player
        glEnd();				// Done Drawing The Player
      
        for (loop1=0; loop1<(stage*level); loop1++)	// Loop To Draw Enemies
        {
          glLoadIdentity();			// Reset The Modelview Matrix
          glTranslatef(enemy[loop1].fx+20.0f,enemy[loop1].fy+70.0f,0.0f);
          glColor3f(1.0f,0.5f,0.5f);		// Make Enemy Body Pink
          glBegin(GL_LINES);			// Start Drawing Enemy
            glVertex2d( 0,-7);		// Top Point Of Body
            glVertex2d(-7, 0);		// Left Point Of Body
            glVertex2d(-7, 0);		// Left Point Of Body
            glVertex2d( 0, 7);		// Bottom Point Of Body
            glVertex2d( 0, 7);		// Bottom Point Of Body
            glVertex2d( 7, 0);		// Right Point Of Body
            glVertex2d( 7, 0);		// Right Point Of Body
            glVertex2d( 0,-7);		// Top Point Of Body
          glEnd();				// Done Drawing Enemy Body
          glRotatef(enemy[loop1].spin,0.0f,0.0f,1.0f);	// Rotate The Enemy Blade
          glColor3f(1.0f,0.0f,0.0f);		// Make Enemy Blade Red
          glBegin(GL_LINES);			// Start Drawing Enemy Blade
            glVertex2d(-7,-7);		// Top Left Of Enemy
            glVertex2d( 7, 7);		// Bottom Right Of Enemy
            glVertex2d(-7, 7);		// Bottom Left Of Enemy
            glVertex2d( 7,-7);		// Top Right Of Enemy
          glEnd();				// Done Drawing Enemy Blade
        }
        return 1;				// Everything Went OK
      }
      
      int main(int argc, char **argv)
      {
        int done;
        float start;
      
        Uint8 *keys;
        /* Initialize SDL for audio and video output */
        if ( SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO) < 0 ) {
          fprintf(stderr, "Unable to initialize SDL: %s\n", SDL_GetError());
          return(1);
        }
      
        #ifdef SOUND
        if (Mix_OpenAudio(22050, AUDIO_S16, 2, 512) < 0)
        {
          fprintf(stderr, "Error: %s\n\n", SDL_GetError());
        }
      
        complete = load_sound("Complete.wav");                 // Load Sounds
        die = load_sound("Die.wav");
        freeze = load_sound("Freeze.wav");
        hglass = load_sound("Hourglass.wav");
        music = Mix_LoadMUS("lktheme.mod");                    // Load music
        #endif
      
      
        /* Create a 640x480 OpenGL screen */
        if ( SDL_SetVideoMode(640, 480, 0, SDL_OPENGL) == NULL ) {
          fprintf(stderr, "Unable to create OpenGL screen: %s\n", SDL_GetError());
          SDL_Quit();
          return(2);
        }
      
        /* Set the title bar in environments that support it */
        SDL_WM_SetCaption("Jeff Molofee's GL Code Tutorial ... NeHe '99", NULL);
      
        /* Loop, drawing and checking events */
        InitGL(640, 480);
        done = 0;
      
        ResetObjects();
      #ifdef SOUND
        Mix_PlayMusic(music,-1);
      #endif
      
        while ( ! done ) {
          start=TimerGetTime();
      
          keys = SDL_GetKeyState(NULL);
          if((active && !display()) || keys[SDLK_ESCAPE])
            done=1;
          else
            SDL_GL_SwapBuffers();
      
          while(TimerGetTime()<start+(float)(steps[adjust]*2.0f)) ;
      
          if (!gameover && active) {	// If Game Isn't Over And Programs Active Move Objects
            for (loop1=0; loop1<(stage*level); loop1++) {// Loop Through The Different Stages
      	if ((enemy[loop1].x<player.x) && (enemy[loop1].fy==enemy[loop1].y*40))
      	  enemy[loop1].x++;		// Move The Enemy Right
      
      	if ((enemy[loop1].x>player.x) && (enemy[loop1].fy==enemy[loop1].y*40))
      	  enemy[loop1].x--;		// Move The Enemy Left
      
      	if ((enemy[loop1].y<player.y) && (enemy[loop1].fx==enemy[loop1].x*60))
      	  enemy[loop1].y++;		// Move The Enemy Down
      
      	if ((enemy[loop1].y>player.y) && (enemy[loop1].fx==enemy[loop1].x*60))
      	  enemy[loop1].y--;		// Move The Enemy Up
      
      	// If Our Delay Is Done And Player Doesn't Have Hourglass
      	if (delay>(3-level) && (hourglass.fx!=2)) {
      	  delay=0;			// Reset The Delay Counter Back To Zero
      	  for (loop2=0; loop2<(stage*level); loop2++) {	// Loop Through All The Enemies
      	    if (enemy[loop2].fx<enemy[loop2].x*60) {  // Is Fine Position On X Axis Lower Than Intended Position?
      	      enemy[loop2].fx+=steps[adjust];	// If So, Increase Fine Position On X Axis
      	      enemy[loop2].spin+=steps[adjust];	// Spin Enemy Clockwise
      	    }
      	    if (enemy[loop2].fx>enemy[loop2].x*60)// Is Fine Position On X Axis Higher Than Intended Position?
      	    {
      	      enemy[loop2].fx-=steps[adjust];	// If So, Decrease Fine Position On X Axis
      	      enemy[loop2].spin-=steps[adjust];	// Spin Enemy Counter Clockwise
      	    }
      	    if (enemy[loop2].fy<enemy[loop2].y*40)// Is Fine Position On Y Axis Lower Than Intended Position?
      	    {
      	      enemy[loop2].fy+=steps[adjust];	// If So, Increase Fine Position On Y Axis
      	      enemy[loop2].spin+=steps[adjust];	// Spin Enemy Clockwise
      	    }
      	    if (enemy[loop2].fy>enemy[loop2].y*40)// Is Fine Position On Y Axis Higher Than Intended Position?
      	    {
      	      enemy[loop2].fy-=steps[adjust];	// If So, Decrease Fine Position On Y Axis
      	      enemy[loop2].spin-=steps[adjust];	// Spin Enemy Counter Clockwise
      	    }
      	  }
              }
      
      	// Are Any Of The Enemies On Top Of The Player?
      	if ((enemy[loop1].fx==player.fx) && (enemy[loop1].fy==player.fy)){
      	  lives--;				// If So, Player Loses A Life
      
      	if (lives==0)				// Are We Out Of Lives?
      	  gameover=1;				// If So, gameover Becomes 1
      
      	ResetObjects();				// Reset Player / Enemy Positions
      #ifdef SOUND
        Mix_PlayChannel(-1, die, 0);
      #endif
            }
        }
      
        if (keys[SDLK_RIGHT] && (player.x<10) && (player.fx==player.x*60) && 
      			(player.fy==player.y*40)) {
          hline[player.x][player.y]=1;		// Mark The Current Horizontal Border As Filled
          player.x++;					// Move The Player Right
        }
        if (keys[SDLK_LEFT] && (player.x>0) && (player.fx==player.x*60) && (player.fy==player.y*40)){
          player.x--;					// Move The Player Left
          hline[player.x][player.y]=1;		// Mark The Current Horizontal Border As Filled
        }
        if (keys[SDLK_DOWN] && (player.y<10) && (player.fx==player.x*60) && (player.fy==player.y*40)){
          vline[player.x][player.y]=1;		// Mark The Current Verticle Border As Filled
          player.y++;					// Move The Player Down
        }
        if (keys[SDLK_UP] && (player.y>0) && (player.fx==player.x*60) && (player.fy==player.y*40)){
          player.y--;					// Move The Player Up
          vline[player.x][player.y]=1;		// Mark The Current Verticle Border As Filled
        }
      
        if (player.fx<player.x*60)	// Is Fine Position On X Axis Lower Than Intended Position?
          player.fx+=steps[adjust];	// If So, Increase The Fine X Position
        if (player.fx>player.x*60)	// Is Fine Position On X Axis Greater Than Intended Position?
          player.fx-=steps[adjust];	// If So, Decrease The Fine X Position
        if (player.fy<player.y*40)	// Is Fine Position On Y Axis Lower Than Intended Position?
          player.fy+=steps[adjust];	// If So, Increase The Fine Y Position
        if (player.fy>player.y*40)	// Is Fine Position On Y Axis Lower Than Intended Position?
          player.fy-=steps[adjust];	// If So, Decrease The Fine Y Position
        else  			// Otherwise
          if (keys[' ']){		// If Spacebar Is Being Pressed
      	gameover=0;		// gameover Becomes 0
      	filled=1;		// filled Becomes 1
      	level=1;		// Starting Level Is Set Back To One
      	level2=1;		// Displayed Level Is Also Set To One
      	stage=0;		// Game Stage Is Set To Zero
      	lives=5;		// Lives Is Set To Five
          }
        }
      
        if (filled){			// Is The Grid Filled In?
      #ifdef SOUND
        Mix_PlayChannel(-1, complete, 0);
      #endif
          stage++;			// Increase The Stage
          if (stage>3)		// Is The Stage Higher Than 3?
          {
      	stage=1;		// If So, Set The Stage To One
      	level++;		// Increase The Level
      	level2++;		// Increase The Displayed Level
      	if (level>3)		// Is The Level Greater Than 3?
      	{
      	  level=3;		// If So, Set The Level To 3
      	  lives++;		// Give The Player A Free Life
      	  if (lives>5)		// Does The Player Have More Than 5 Lives?
      	    lives=5;		// If So, Set Lives To Five
      	}
          }
      
          ResetObjects();		// Reset Player / Enemy Positions
      
          for (loop1=0; loop1<11; loop1++)	// Loop Through The Grid X Coordinates
          {
            for (loop2=0; loop2<11; loop2++)	// Loop Through The Grid Y Coordinates
            {
      	if (loop1<10)			// If X Coordinate Is Less Than 10
      	  hline[loop1][loop2]=0;	// Set The Current Horizontal Value To 0
      	if (loop2<10)			// If Y Coordinate Is Less Than 10
      	  vline[loop1][loop2]=0;	// Set The Current Vertical Value To 0
            }
          }
        }
      
        // If The Player Hits The Hourglass While It's Being Displayed On The Screen
        if ((player.fx==hourglass.x*60) && (player.fy==hourglass.y*40) 
      		&& (hourglass.fx==1))
        {
      	// Play Freeze Enemy Sound
      #ifdef SOUND
          Mix_PlayChannel(-1, freeze, 0);
      #endif
          hourglass.fx=2;		// Set The hourglass fx Variable To 
          hourglass.fy=0;		// Set The hourglass fy Variable To Zero
        }
      
        player.spin+=0.5f*steps[adjust];	// Spin The Player Clockwise
        if (player.spin>360.0f)		// Is The spin Value Greater Than 360?
          player.spin-=360;			// If So, Subtract 360
      
          hourglass.spin-=0.25f*steps[adjust];// Spin The Hourglass Counter Clockwise
          if (hourglass.spin<0.0f)		// Is The spin Value Less Than 0?
      	hourglass.spin+=360.0f;		// If So, Add 360
      
          hourglass.fy+=steps[adjust];	// Increase The hourglass fy Variable
          // Is The hourglass fx Variable Equal To 0 And The fy ?
          if ((hourglass.fx==0) && (hourglass.fy>6000/level))	
          {				// Variable Greater Than 6000 Divided By The Current Level?
      #ifdef SOUND
      
           	Mix_PlayChannel(-1, hglass, 0);
      #endif
              hourglass.x=rand()%10+1;// Give The Hourglass A Random X Value
      	hourglass.y=rand()%11;	// Give The Hourglass A Random Y Value
      	hourglass.fx=1;		// Set hourglass fx Variable To One (Hourglass Stage)
      	hourglass.fy=0;		// Set hourglass fy Variable To Zero (Counter)
          }
      
          // Is The hourglass fx Variable Equal To 1 And The fy
          if ((hourglass.fx==1) && (hourglass.fy>6000/level))	
          {				// Variable Greater Than 6000 Divided By The Current Level?
      	hourglass.fx=0;		// If So, Set fx To Zero (Hourglass Will Vanish)
      	hourglass.fy=0;		// Set fy to Zero (Counter Is Reset)
          }
      
         // Is The hourglass fx Variable Equal To 2 And The fy
          if ((hourglass.fx==2) && (hourglass.fy>500+(500*level)))	
          {				// Variable Greater Than 500 Plus 500 Times The Current Level?
      				//PlaySound(NULL, NULL, 0);// If So, Kill The Freeze Sound
      	hourglass.fx=0;		// Set hourglass fx Variable To Zero
      	hourglass.fy=0;		// Set hourglass fy Variable To Zero
          }
      
      	delay++;		// Increase The Enemy Delay Counter
      
          /* This could go in a separate function */
          { SDL_Event event;
            while ( SDL_PollEvent(&event) ) {
              if ( event.type == SDL_QUIT ) {
      #ifdef SOUND
                  Mix_HaltChannel(-1);
                  Mix_CloseAudio();
      #endif
                done = 1;
              }
              if ( event.type == SDL_KEYDOWN ) {
                if ( event.key.keysym.sym == SDLK_ESCAPE ) {
      #ifdef SOUND
      
                  Mix_HaltChannel(-1);
                  Mix_CloseAudio();
      #endif
                  done = 1;
                }
                if ( event.key.keysym.sym == SDLK_a ) {
                  anti = !anti;
                }
              }
            }
          }
        }
        SDL_Quit();
        return 1;
      }
      
      /*
      #Makefile
      CC = gcc
      PROG    = game_audio
      CFLAGS  = -g -O2  -D_REENTRANT -DHAVE_OPENGL -DSOUND
      XLIBS   = -lX11 -lXext -lXmu -lXext -lXmu -lXt -lXi -lSM -lICE
      LIBS    = -lglut -lGLU -lGL
      LIBSDL =  -L/usr/local/lib -Wl,-rpath,/usr/local/lib -lSDL -lpthread -lSDL_mixer
      INCLS   = -I/usr/X11R/include -I/share/mesa/include -I/usr/local/include/SDL
      LIBDIR  = -L/usr/X11/lib -L/usr/X11R6/lib
      #source codes
      SRCS = game_audio.c
      #substitute .c by .o to obtain object filenames
      OBJS = $(SRCS:.c=.o)
       
      #$< evaluates to the target's dependencies,
      #$@ evaluates to the target
       
      $(PROG): $(OBJS)
              $(CC) -o $@ $(OBJS)  $(LIBDIR) $(LIBS) $(XLIBS) $(LIBSDL)
      $(OBJS):
              $(CC) -c  $*.c $(INCLS) $(CFLAGS)
                                                                                                      
      clean:
              rm $(OBJS)
      
      */
      

    11. Threads
      • Create a simple thread

      Creating a thread is done by passing a function to SDL_CreateThread(). When the function returns, if successful, your function is now running concurrently with the rest of your application, in its own running context (stack, registers, etc.) and able to access memory and file handles used by the rest of the application.

      Tip:
      The second argument to SDL_CreateThread() is passed as a parameter to the thread function. You can use this to pass in values on the stack, or just a pointer to data for use by the thread.
      Example:
      #include "SDL_thread.h"
      
      int global_data = 0;
      
      int thread_func(void *unused)
      {
          int last_value = 0;
      
          while ( global_data != -1 ) {
              if ( global_data != last_value ) {
                  printf("Data value changed to %d\n", global_data);
                  last_value = global_data;
              }
              SDL_Delay(100);
          }
          printf("Thread quitting\n");
          return(0);
      }
      
      {
          SDL_Thread *thread;
          int i;
      
          thread = SDL_CreateThread(thread_func, NULL);
          if ( thread == NULL ) {
              fprintf(stderr, "Unable to create thread: %s\n", SDL_GetError());
              return;
          }
      
          for ( i=0; i<5; ++i ) {
              printf("Changing value to %d\n", i);
              global_data = i;
              SDL_Delay(1000);
          }
      
          printf("Signaling thread to quit\n");
          global_data = -1;
          SDL_WaitThread(thread, NULL);
      }
      
      
      • Synchronizing access to a resource

      You can prevent more than one thread from accessing a resource by creating a mutex and surrounding access to that resource with lock (SDL_mutexP()) and unlock (SDL_mutexV()) calls.

      Tip:
      Any data that can be accessed by more than one thread should be protected by a mutex.
      Example:
      #include "SDL_thread.h"
      #include "SDL_mutex.h"
      
      int potty = 0;
      int gotta_go;
      
      int thread_func(void *data)
      {
          SDL_mutex *lock = (SDL_mutex *)data;
          int times_went;
      
          times_went = 0;
          while ( gotta_go ) {
              SDL_mutexP(lock);    /* Lock  the potty */
      
              ++potty;
              printf("Thread %d using the potty\n", SDL_ThreadID());
              if ( potty > 1 ) {
                  printf("Uh oh, somebody else is using the potty!\n");
              }
              --potty;
              SDL_mutexV(lock);
              ++times_went;
          }
          printf("Yep\n");
          return(times_went);
      }
      
      {
          const int progeny = 5;
          SDL_Thread *kids[progeny];
          SDL_mutex  *lock;
          int i, lots;
      
          /* Create the synchronization lock */
      
          lock = SDL_CreateMutex();
      
          gotta_go = 1;
          for ( i=0; i<progeny; ++i ) {
              kids[i] = SDL_CreateThread(thread_func, lock);
          }
      
          SDL_Delay(5*1000);
          SDL_mutexP(lock);
          printf("Everybody done?\n");
          gotta_go = 0;
          SDL_mutexV(lock);
      
          for ( i=0; i<progeny; ++i ) {
              SDL_WaitThread(kids[i], &lots);
              printf("Thread %d used the potty %d times\n", i+1, lots);
          }
          SDL_DestroyMutex(lock);
      }