#include #include #include #include "SDL.h" #include "SDL_opengl.h" //---------------------------------------------------------- // A set of very useful macros that you will find in most // code that I write whether I use them in a program or // not. #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) //---------------------------------------------------------- // sweepLine animates a line on a surface based on the // elapsed time. class sweepLine { private: SDL_Surface *s; // The surface to draw on. float red; // Red, Green, and Blue float green; // color components for float blue; // the animating 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, color components for 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. sweepLine(SDL_Surface *s, float red, float green, float blue, int time, float x1, float y1, float dx1, float dy1, float x2, float y2, float dx2, float dy2): s(s), red(red), green(green), blue(blue), 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. 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. glColor3f(red, green, blue); glBegin(GL_LINES); glVertex2f(x1, y1); glVertex2f(x2, y2); glEnd(); } }; //---------------------------------------------------------- // 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]; Uint32 start = 0; Uint32 frames = 0; SDL_Surface *screen = NULL; SDL_Event event; 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:%s\n", SDL_GetError()); 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); // Set the OpenGL attributes. Here all we ask for is an // RGB pixel format and that it be double buffered. We // only ask for one bit of red, green, and blue knowing // that we will get at least that many bits. SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 1); SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 1); SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 1); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); // Initialize the display. Here I'm just asking for a // 640x480 OpenGL surfacer in a window. Everything else // is picked up from the OpenGL attributes we set above. screen = SDL_SetVideoMode(screenWidth, screenHeight, 0, SDL_OPENGL ); // Check to see if we got a window. if (NULL == screen) { printf("Can't set video mode:%s\n", SDL_GetError()); exit(1); } // Set up the OpenGL context for a 2D application. glViewport(0, 0, // coordinates of the // lower left hand corner // of the window which we // will change in the // glOrtho() call. screen->w, // Set the logical width screen->h); // and height of the // window to match the // actual width and // height as measured in // pixels. // Set the coordinate system for the window moving (0,0) // to the upper left hand corner of the window. glOrtho(0.0, // left edge is zero (GLdouble)screen->w, // right edge is width (GLdouble)screen->h, // bottom edge is height 0.0, // top edge is zero 0.0, 1.0); // near and far clipping // planes. // Set the clear color to black. glClearColor(0.0, 0.0, 0.0, 0.0); // 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, 1.0, 0.0, 0.0, gt.time(), screen->w - 1, 0, -0.3, 0, 0, screen->h - 1, 0.3, 0); gl = new sweepLine(screen, 0.0, 1.0, 0.0, gt.time(), 0, 0, 0, 0.1, screen->w - 1, screen->h - 1, 0, -0.1); bl = new sweepLine(screen, 0.0, 0.0, 1.0, gt.time(), screen->w - 1, 0, -0.1, -0.5, 0, screen->h - 1, 0.4, 0.2); // Start the game clock. start = SDL_GetTicks(); gt.start(); // The animation loop. while (!done) { // Loop while reading all pending events. 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 // with the current clear color. glClear(GL_COLOR_BUFFER_BIT); // 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 an OpenGL surface I have to use // SDL_GL_SwapBuffers() which may, or may not, wait // for vertical retrace before it swaps the buffers. SDL_GL_SwapBuffers(); frames++; } // Compute the average number of frames drawn per // second. This number can be useful for diagnosing some // kinds of programming errors. But, usually it is just // fun to see. float seconds = ((float) (SDL_GetTicks() - start)) / 1000.0; printf("frames/second=%f\n", ((float)frames) / seconds); // 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(); }