//======================================================================== // This is a small test application for GLFW. // This is an OpenGL port of the famous "PONG" game (the first computer // game ever?). It is very simple, and could be improved alot. It was // created in order to show off the gaming capabilities of GLFW. //======================================================================== #include #include #include #include //======================================================================== // Constants //======================================================================== // Screen resolution #define WIDTH 640 #define HEIGHT 480 // Player size (units) #define PLAYER_XSIZE 0.05f #define PLAYER_YSIZE 0.15f // Ball size (units) #define BALL_SIZE 0.02f // Maximum player movement speed (units / second) #define MAX_SPEED 1.5f // Player movement acceleration (units / seconds^2) #define ACCELERATION 4.0f // Player movement deceleration (units / seconds^2) #define DECELERATION 2.0f // Ball movement speed (units / second) #define BALL_SPEED 0.4f // Menu options #define MENU_NONE 0 #define MENU_PLAY 1 #define MENU_QUIT 2 // Game events #define NOBODY_WINS 0 #define PLAYER1_WINS 1 #define PLAYER2_WINS 2 // Winner ID #define NOBODY 0 #define PLAYER1 1 #define PLAYER2 2 // Camera positions #define CAMERA_CLASSIC 0 #define CAMERA_ABOVE 1 #define CAMERA_SPECTATOR 2 #define CAMERA_DEFAULT CAMERA_CLASSIC //======================================================================== // Textures //======================================================================== #define TEX_TITLE 0 #define TEX_MENU 1 #define TEX_INSTR 2 #define TEX_WINNER1 3 #define TEX_WINNER2 4 #define TEX_FIELD 5 #define NUM_TEXTURES 6 // Texture names char * tex_name[ NUM_TEXTURES ] = { "pong3d_title.tga", "pong3d_menu.tga", "pong3d_instr.tga", "pong3d_winner1.tga", "pong3d_winner2.tga", "pong3d_field.tga" }; // OpenGL texture object IDs GLuint tex_id[ NUM_TEXTURES ]; //======================================================================== // Global variables //======================================================================== // Display information int width, height; // Frame information double thistime, oldtime, dt, starttime; // Camera information int camerapos; // Player information struct { double ypos; // -1.0 to +1.0 double yspeed; // -MAX_SPEED to +MAX_SPEED } player1, player2; // Ball information struct { double xpos, ypos; double xspeed, yspeed; } ball; // And the winner is... int winner; // Lighting configuration const GLfloat env_ambient[4] = {1.0f,1.0f,1.0f,1.0f}; const GLfloat light1_position[4] = {-3.0f,3.0f,2.0f,1.0f}; const GLfloat light1_diffuse[4] = {1.0f,1.0f,1.0f,0.0f}; const GLfloat light1_ambient[4] = {0.0f,0.0f,0.0f,0.0f}; // Object material properties const GLfloat player1_diffuse[4] = {1.0f,0.3f,0.3f,1.0f}; const GLfloat player1_ambient[4] = {0.3f,0.1f,0.0f,1.0f}; const GLfloat player2_diffuse[4] = {0.3f,1.0f,0.3f,1.0f}; const GLfloat player2_ambient[4] = {0.1f,0.3f,0.1f,1.0f}; const GLfloat ball_diffuse[4] = {1.0f,1.0f,0.5f,1.0f}; const GLfloat ball_ambient[4] = {0.3f,0.3f,0.1f,1.0f}; const GLfloat border_diffuse[4] = {0.3f,0.3f,1.0f,1.0f}; const GLfloat border_ambient[4] = {0.1f,0.1f,0.3f,1.0f}; const GLfloat floor_diffuse[4] = {1.0f,1.0f,1.0f,1.0f}; const GLfloat floor_ambient[4] = {0.3f,0.3f,0.3f,1.0f}; //======================================================================== // LoadTextures() - Load textures from disk and upload to OpenGL card //======================================================================== GLboolean LoadTextures( void ) { int i; // Generate texture objects glGenTextures( NUM_TEXTURES, tex_id ); // Load textures for( i = 0; i < NUM_TEXTURES; i ++ ) { // Select texture object glBindTexture( GL_TEXTURE_2D, tex_id[ i ] ); // Set texture parameters glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); // Upload texture from file to texture memory if( !glfwLoadTexture2D( tex_name[ i ], 0 ) ) { fprintf( stderr, "Failed to load texture %s\n", tex_name[ i ] ); return GL_FALSE; } } return GL_TRUE; } //======================================================================== // DrawImage() - Draw a 2D image as a texture //======================================================================== void DrawImage( int texnum, float x1, float x2, float y1, float y2 ) { glEnable( GL_TEXTURE_2D ); glBindTexture( GL_TEXTURE_2D, tex_id[ texnum ] ); glBegin( GL_QUADS ); glTexCoord2f( 0.0f, 1.0f ); glVertex2f( x1, y1 ); glTexCoord2f( 1.0f, 1.0f ); glVertex2f( x2, y1 ); glTexCoord2f( 1.0f, 0.0f ); glVertex2f( x2, y2 ); glTexCoord2f( 0.0f, 0.0f ); glVertex2f( x1, y2 ); glEnd(); glDisable( GL_TEXTURE_2D ); } //======================================================================== // GameMenu() - Game menu (returns menu option) //======================================================================== int GameMenu( void ) { int option; // Enable sticky keys glfwEnable( GLFW_STICKY_KEYS ); // Wait for a game menu key to be pressed do { // Get window size glfwGetWindowSize( &width, &height ); // Set viewport glViewport( 0, 0, width, height ); // Clear display glClearColor( 0.0f, 0.0f, 0.0f, 0.0f ); glClear( GL_COLOR_BUFFER_BIT ); // Setup projection matrix glMatrixMode( GL_PROJECTION ); glLoadIdentity(); glOrtho( 0.0f, 1.0f, 1.0f, 0.0f, -1.0f, 1.0f ); // Setup modelview matrix glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); // Display title glColor3f( 1.0f, 1.0f, 1.0f ); DrawImage( TEX_TITLE, 0.1f, 0.9f, 0.0f, 0.3f ); // Display menu glColor3f( 1.0f, 1.0f, 0.0f ); DrawImage( TEX_MENU, 0.38f, 0.62f, 0.35f, 0.5f ); // Display instructions glColor3f( 0.0f, 1.0f, 1.0f ); DrawImage( TEX_INSTR, 0.32f, 0.68f, 0.65f, 0.85f ); // Swap buffers glfwSwapBuffers(); // Check for keys if( glfwGetKey( 'Q' ) || !glfwGetWindowParam( GLFW_OPENED ) ) { option = MENU_QUIT; } else if( glfwGetKey( GLFW_KEY_F1 ) ) { option = MENU_PLAY; } else { option = MENU_NONE; } // To avoid horrible busy waiting, sleep for at least 20 ms glfwSleep( 0.02 ); } while( option == MENU_NONE ); // Disable sticky keys glfwDisable( GLFW_STICKY_KEYS ); return option; } //======================================================================== // NewGame() - Initialize a new game //======================================================================== void NewGame( void ) { // Frame information starttime = thistime = glfwGetTime(); // Camera information camerapos = CAMERA_DEFAULT; // Player 1 information player1.ypos = 0.0; player1.yspeed = 0.0; // Player 2 information player2.ypos = 0.0; player2.yspeed = 0.0; // Ball information ball.xpos = -1.0 + PLAYER_XSIZE; ball.ypos = player1.ypos; ball.xspeed = 1.0; ball.yspeed = 1.0; } //======================================================================== // PlayerControl() - Player control //======================================================================== void PlayerControl( void ) { float joy1pos[ 2 ], joy2pos[ 2 ]; // Get joystick X & Y axis positions glfwGetJoystickPos( GLFW_JOYSTICK_1, joy1pos, 2 ); glfwGetJoystickPos( GLFW_JOYSTICK_2, joy2pos, 2 ); // Player 1 control if( glfwGetKey( 'A' ) || joy1pos[ 1 ] > 0.2f ) { player1.yspeed += dt * ACCELERATION; if( player1.yspeed > MAX_SPEED ) { player1.yspeed = MAX_SPEED; } } else if( glfwGetKey( 'Z' ) || joy1pos[ 1 ] < -0.2f ) { player1.yspeed -= dt * ACCELERATION; if( player1.yspeed < -MAX_SPEED ) { player1.yspeed = -MAX_SPEED; } } else { player1.yspeed /= exp( DECELERATION * dt ); } // Player 2 control if( glfwGetKey( 'K' ) || joy2pos[ 1 ] > 0.2f ) { player2.yspeed += dt * ACCELERATION; if( player2.yspeed > MAX_SPEED ) { player2.yspeed = MAX_SPEED; } } else if( glfwGetKey( 'M' ) || joy2pos[ 1 ] < -0.2f ) { player2.yspeed -= dt * ACCELERATION; if( player2.yspeed < -MAX_SPEED ) { player2.yspeed = -MAX_SPEED; } } else { player2.yspeed /= exp( DECELERATION * dt ); } // Update player 1 position player1.ypos += dt * player1.yspeed; if( player1.ypos > 1.0 - PLAYER_YSIZE ) { player1.ypos = 1.0 - PLAYER_YSIZE; player1.yspeed = 0.0; } else if( player1.ypos < -1.0 + PLAYER_YSIZE ) { player1.ypos = -1.0 + PLAYER_YSIZE; player1.yspeed = 0.0; } // Update player 2 position player2.ypos += dt * player2.yspeed; if( player2.ypos > 1.0 - PLAYER_YSIZE ) { player2.ypos = 1.0 - PLAYER_YSIZE; player2.yspeed = 0.0; } else if( player2.ypos < -1.0 + PLAYER_YSIZE ) { player2.ypos = -1.0 + PLAYER_YSIZE; player2.yspeed = 0.0; } } //======================================================================== // BallControl() - Ball control //======================================================================== int BallControl( void ) { int event; double ballspeed; // Calculate new ball speed ballspeed = BALL_SPEED * (1.0 + 0.02*(thistime-starttime)); ball.xspeed = ball.xspeed > 0 ? ballspeed : -ballspeed; ball.yspeed = ball.yspeed > 0 ? ballspeed : -ballspeed; ball.yspeed *= 0.74321; // Update ball position ball.xpos += dt * ball.xspeed; ball.ypos += dt * ball.yspeed; // Did the ball hit a top/bottom wall? if( ball.ypos >= 1.0 ) { ball.ypos = 2.0 - ball.ypos; ball.yspeed = -ball.yspeed; } else if( ball.ypos <= -1.0 ) { ball.ypos = -2.0 - ball.ypos; ball.yspeed = -ball.yspeed; } // Did the ball hit/miss a player? event = NOBODY_WINS; // Is the ball entering the player 1 goal? if( ball.xpos < -1.0 + PLAYER_XSIZE ) { // Did player 1 catch the ball? if( ball.ypos > (player1.ypos-PLAYER_YSIZE) && ball.ypos < (player1.ypos+PLAYER_YSIZE) ) { ball.xpos = -2.0 + 2.0*PLAYER_XSIZE - ball.xpos; ball.xspeed = -ball.xspeed; } else { event = PLAYER2_WINS; } } // Is the ball entering the player 2 goal? if( ball.xpos > 1.0 - PLAYER_XSIZE ) { // Did player 2 catch the ball? if( ball.ypos > (player2.ypos-PLAYER_YSIZE) && ball.ypos < (player2.ypos+PLAYER_YSIZE) ) { ball.xpos = 2.0 - 2.0*PLAYER_XSIZE - ball.xpos; ball.xspeed = -ball.xspeed; } else { event = PLAYER1_WINS; } } return event; } //======================================================================== // DrawBox() - Draw a 3D box //======================================================================== #define TEX_SCALE 4.0f void DrawBox( float x1, float y1, float z1, float x2, float y2, float z2 ) { // Draw six sides of a cube glBegin( GL_QUADS ); // Side 1 (down) glNormal3f( 0.0f, 0.0f, -1.0f ); glTexCoord2f( 0.0f, 0.0f ); glVertex3f( x1,y2,z1 ); glTexCoord2f( TEX_SCALE, 0.0f ); glVertex3f( x2,y2,z1 ); glTexCoord2f( TEX_SCALE, TEX_SCALE ); glVertex3f( x2,y1,z1 ); glTexCoord2f( 0.0f, TEX_SCALE ); glVertex3f( x1,y1,z1 ); // Side 2 (up) glNormal3f( 0.0f, 0.0f, 1.0f ); glTexCoord2f( 0.0f, 0.0f ); glVertex3f( x1,y1,z2 ); glTexCoord2f( TEX_SCALE, 0.0f ); glVertex3f( x2,y1,z2 ); glTexCoord2f( TEX_SCALE, TEX_SCALE ); glVertex3f( x2,y2,z2 ); glTexCoord2f( 0.0f, TEX_SCALE ); glVertex3f( x1,y2,z2 ); // Side 3 (backward) glNormal3f( 0.0f, -1.0f, 0.0f ); glTexCoord2f( 0.0f, 0.0f ); glVertex3f( x1,y1,z1 ); glTexCoord2f( TEX_SCALE, 0.0f ); glVertex3f( x2,y1,z1 ); glTexCoord2f( TEX_SCALE, TEX_SCALE ); glVertex3f( x2,y1,z2 ); glTexCoord2f( 0.0f, TEX_SCALE ); glVertex3f( x1,y1,z2 ); // Side 4 (forward) glNormal3f( 0.0f, 1.0f, 0.0f ); glTexCoord2f( 0.0f, 0.0f ); glVertex3f( x1,y2,z2 ); glTexCoord2f( TEX_SCALE, 0.0f ); glVertex3f( x2,y2,z2 ); glTexCoord2f( TEX_SCALE, TEX_SCALE ); glVertex3f( x2,y2,z1 ); glTexCoord2f( 0.0f, TEX_SCALE ); glVertex3f( x1,y2,z1 ); // Side 5 (left) glNormal3f( -1.0f, 0.0f, 0.0f ); glTexCoord2f( 0.0f, 0.0f ); glVertex3f( x1,y1,z2 ); glTexCoord2f( TEX_SCALE, 0.0f ); glVertex3f( x1,y2,z2 ); glTexCoord2f( TEX_SCALE, TEX_SCALE ); glVertex3f( x1,y2,z1 ); glTexCoord2f( 0.0f, TEX_SCALE ); glVertex3f( x1,y1,z1 ); // Side 6 (right) glNormal3f( 1.0f, 0.0f, 0.0f ); glTexCoord2f( 0.0f, 0.0f ); glVertex3f( x2,y1,z1 ); glTexCoord2f( TEX_SCALE, 0.0f ); glVertex3f( x2,y2,z1 ); glTexCoord2f( TEX_SCALE, TEX_SCALE ); glVertex3f( x2,y2,z2 ); glTexCoord2f( 0.0f, TEX_SCALE ); glVertex3f( x2,y1,z2 ); glEnd(); } //======================================================================== // UpdateDisplay() - Draw graphics (all game related OpenGL stuff goes // here) //======================================================================== void UpdateDisplay( void ) { // Get window size glfwGetWindowSize( &width, &height ); // Set viewport glViewport( 0, 0, width, height ); // Clear display glClearColor( 0.02f, 0.02f, 0.02f, 0.0f ); glClearDepth( 1.0f ); glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); // Setup projection matrix glMatrixMode( GL_PROJECTION ); glLoadIdentity(); gluPerspective( 55.0f, // Angle of view (GLfloat)width/(GLfloat)height, // Aspect 1.0f, // Near Z 100.0f // Far Z ); // Setup modelview matrix glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); switch( camerapos ) { default: case CAMERA_CLASSIC: gluLookAt( 0.0f, 0.0f, 2.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f ); break; case CAMERA_ABOVE: gluLookAt( 0.0f, 0.0f, 2.5f, (float)ball.xpos, (float)ball.ypos, 0.0f, 0.0f, 1.0f, 0.0f ); break; case CAMERA_SPECTATOR: gluLookAt( 0.0f, -2.0, 1.2f, (float)ball.xpos, (float)ball.ypos, 0.0f, 0.0f, 0.0f, 1.0f ); break; } // Enable depth testing glEnable( GL_DEPTH_TEST ); glDepthFunc( GL_LEQUAL ); // Enable lighting glEnable( GL_LIGHTING ); glLightModelfv( GL_LIGHT_MODEL_AMBIENT, env_ambient ); glLightModeli( GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE ); glLightModeli( GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE ); glLightfv( GL_LIGHT1, GL_POSITION, light1_position ); glLightfv( GL_LIGHT1, GL_DIFFUSE, light1_diffuse ); glLightfv( GL_LIGHT1, GL_AMBIENT, light1_ambient ); glEnable( GL_LIGHT1 ); // Front face is counter-clock-wise glFrontFace( GL_CCW ); // Enable face culling (not necessary, but speeds up rendering) glCullFace( GL_BACK ); glEnable( GL_CULL_FACE ); // Draw Player 1 glMaterialfv( GL_FRONT, GL_DIFFUSE, player1_diffuse ); glMaterialfv( GL_FRONT, GL_AMBIENT, player1_ambient ); DrawBox( -1.f, (GLfloat)player1.ypos-PLAYER_YSIZE, 0.f, -1.f+PLAYER_XSIZE, (GLfloat)player1.ypos+PLAYER_YSIZE, 0.1f ); // Draw Player 2 glMaterialfv( GL_FRONT, GL_DIFFUSE, player2_diffuse ); glMaterialfv( GL_FRONT, GL_AMBIENT, player2_ambient ); DrawBox( 1.f-PLAYER_XSIZE, (GLfloat)player2.ypos-PLAYER_YSIZE, 0.f, 1.f, (GLfloat)player2.ypos+PLAYER_YSIZE, 0.1f ); // Draw Ball glMaterialfv( GL_FRONT, GL_DIFFUSE, ball_diffuse ); glMaterialfv( GL_FRONT, GL_AMBIENT, ball_ambient ); DrawBox( (GLfloat)ball.xpos-BALL_SIZE, (GLfloat)ball.ypos-BALL_SIZE, 0.f, (GLfloat)ball.xpos+BALL_SIZE, (GLfloat)ball.ypos+BALL_SIZE, BALL_SIZE*2 ); // Top game field border glMaterialfv( GL_FRONT, GL_DIFFUSE, border_diffuse ); glMaterialfv( GL_FRONT, GL_AMBIENT, border_ambient ); DrawBox( -1.1f, 1.0f, 0.0f, 1.1f, 1.1f, 0.1f ); // Bottom game field border glColor3f( 0.0f, 0.0f, 0.7f ); DrawBox( -1.1f, -1.1f, 0.0f, 1.1f, -1.0f, 0.1f ); // Left game field border DrawBox( -1.1f, -1.0f, 0.0f, -1.0f, 1.0f, 0.1f ); // Left game field border DrawBox( 1.0f, -1.0f, 0.0f, 1.1f, 1.0f, 0.1f ); // Enable texturing glEnable( GL_TEXTURE_2D ); glBindTexture( GL_TEXTURE_2D, tex_id[ TEX_FIELD ] ); // Game field floor glMaterialfv( GL_FRONT, GL_DIFFUSE, floor_diffuse ); glMaterialfv( GL_FRONT, GL_AMBIENT, floor_ambient ); DrawBox( -1.01f, -1.01f, -0.01f, 1.01f, 1.01f, 0.0f ); // Disable texturing glDisable( GL_TEXTURE_2D ); // Disable face culling glDisable( GL_CULL_FACE ); // Disable lighting glDisable( GL_LIGHTING ); // Disable depth testing glDisable( GL_DEPTH_TEST ); } //======================================================================== // GameOver() //======================================================================== void GameOver( void ) { // Enable sticky keys glfwEnable( GLFW_STICKY_KEYS ); // Until the user presses ESC or SPACE while( !glfwGetKey( GLFW_KEY_ESC ) && !glfwGetKey( ' ' ) && glfwGetWindowParam( GLFW_OPENED ) ) { // Draw display UpdateDisplay(); // Setup projection matrix glMatrixMode( GL_PROJECTION ); glLoadIdentity(); glOrtho( 0.0f, 1.0f, 1.0f, 0.0f, -1.0f, 1.0f ); // Setup modelview matrix glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); // Enable blending glEnable( GL_BLEND ); // Dim background glBlendFunc( GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA ); glColor4f( 0.3f, 0.3f, 0.3f, 0.3f ); glBegin( GL_QUADS ); glVertex2f( 0.0f, 0.0f ); glVertex2f( 1.0f, 0.0f ); glVertex2f( 1.0f, 1.0f ); glVertex2f( 0.0f, 1.0f ); glEnd(); // Display winner text glBlendFunc( GL_ONE, GL_ONE_MINUS_SRC_COLOR ); if( winner == PLAYER1 ) { glColor4f( 1.0f, 0.5f, 0.5f, 1.0f ); DrawImage( TEX_WINNER1, 0.35f, 0.65f, 0.46f, 0.54f ); } else if( winner == PLAYER2 ) { glColor4f( 0.5f, 1.0f, 0.5f, 1.0f ); DrawImage( TEX_WINNER2, 0.35f, 0.65f, 0.46f, 0.54f ); } // Disable blending glDisable( GL_BLEND ); // Swap buffers glfwSwapBuffers(); } // Disable sticky keys glfwDisable( GLFW_STICKY_KEYS ); } //======================================================================== // GameLoop() - Game loop //======================================================================== void GameLoop( void ) { int playing, event; // Initialize a new game NewGame(); // Enable sticky keys glfwEnable( GLFW_STICKY_KEYS ); // Loop until the game ends playing = GL_TRUE; while( playing && glfwGetWindowParam( GLFW_OPENED ) ) { // Frame timer oldtime = thistime; thistime = glfwGetTime(); dt = thistime - oldtime; // Get user input and update player positions PlayerControl(); // Move the ball, and check if a player hits/misses the ball event = BallControl(); // Did we have a winner? switch( event ) { case PLAYER1_WINS: winner = PLAYER1; playing = GL_FALSE; break; case PLAYER2_WINS: winner = PLAYER2; playing = GL_FALSE; break; default: break; } // Did the user press ESC? if( glfwGetKey( GLFW_KEY_ESC ) ) { playing = GL_FALSE; } // Did the user change camera view? if( glfwGetKey( '1' ) ) { camerapos = CAMERA_CLASSIC; } else if( glfwGetKey( '2' ) ) { camerapos = CAMERA_ABOVE; } else if( glfwGetKey( '3' ) ) { camerapos = CAMERA_SPECTATOR; } // Draw display UpdateDisplay(); // Swap buffers glfwSwapBuffers(); } // Disable sticky keys glfwDisable( GLFW_STICKY_KEYS ); // Show winner GameOver(); } //======================================================================== // main() - Program entry point //======================================================================== int main( void ) { int menuoption; // Initialize GLFW if( !glfwInit() ) { fprintf( stderr, "Failed to initialize GLFW\n" ); exit( EXIT_FAILURE ); } // Open OpenGL window if( !glfwOpenWindow( WIDTH, HEIGHT, 0,0,0,0, 16,0, GLFW_FULLSCREEN ) ) { fprintf( stderr, "Failed to open GLFW window\n" ); glfwTerminate(); exit( EXIT_FAILURE ); } glfwSwapInterval( 1 ); // Load all textures if( !LoadTextures() ) { glfwTerminate(); exit( EXIT_FAILURE ); } // Main loop do { // Get menu option menuoption = GameMenu(); // If the user wants to play, let him... if( menuoption == MENU_PLAY ) { GameLoop(); } } while( menuoption != MENU_QUIT ); // Unload all textures if( glfwGetWindowParam( GLFW_OPENED ) ) { glDeleteTextures( NUM_TEXTURES, tex_id ); } // Terminate GLFW glfwTerminate(); exit( EXIT_SUCCESS ); }