|
|
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();
...
}
|
Choosing and setting video
modes (the easy way)
|
Tip #1:
You can find the fastest video depth supported by the hardware with the function SDL_GetVideoInfo(). Tip
#2: |
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
|
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
|
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: |
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);
}
|
|
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);
}
}
|
|
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);
}
}
}
|
|
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");
}
}
|
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)
*/
|
/* 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 |
|
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();
}
|
|
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)
*/
|
|
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);
}
|
|
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);
}
|