From 53245d754e350fc96370e6d28b7678c439a857c2 Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Tue, 28 Aug 2012 20:16:43 +0200 Subject: [PATCH 01/12] Added detection of joystick disconnect on X11. --- src/x11_joystick.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/x11_joystick.c b/src/x11_joystick.c index 8492850b..7f4fdbc1 100644 --- a/src/x11_joystick.c +++ b/src/x11_joystick.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -109,6 +110,7 @@ static void pollJoystickEvents(void) { #ifdef _GLFW_USE_LINUX_JOYSTICKS int i; + ssize_t result; struct js_event e; for (i = 0; i <= GLFW_JOYSTICK_LAST; i++) @@ -117,8 +119,17 @@ static void pollJoystickEvents(void) continue; // Read all queued events (non-blocking) - while (read(_glfwLibrary.X11.joystick[i].fd, &e, sizeof(e)) > 0) + for (;;) { + errno = 0; + result = read(_glfwLibrary.X11.joystick[i].fd, &e, sizeof(e)); + + if (errno == ENODEV) + _glfwLibrary.X11.joystick[i].present = GL_FALSE; + + if (result <= 0) + break; + // We don't care if it's an init event or not e.type &= ~JS_EVENT_INIT; @@ -221,6 +232,8 @@ void _glfwTerminateJoysticks(void) int _glfwPlatformGetJoystickParam(int joy, int param) { + pollJoystickEvents(); + if (!_glfwLibrary.X11.joystick[joy].present) return 0; @@ -251,11 +264,11 @@ int _glfwPlatformGetJoystickPos(int joy, float* pos, int numAxes) { int i; + pollJoystickEvents(); + if (!_glfwLibrary.X11.joystick[joy].present) return 0; - pollJoystickEvents(); - if (_glfwLibrary.X11.joystick[joy].numAxes < numAxes) numAxes = _glfwLibrary.X11.joystick[joy].numAxes; @@ -275,11 +288,11 @@ int _glfwPlatformGetJoystickButtons(int joy, unsigned char* buttons, { int i; + pollJoystickEvents(); + if (!_glfwLibrary.X11.joystick[joy].present) return 0; - pollJoystickEvents(); - if (_glfwLibrary.X11.joystick[joy].numButtons < numButtons) numButtons = _glfwLibrary.X11.joystick[joy].numButtons; From 54fceaaf64a7d5205953a472e774d1ac748ee31a Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Tue, 28 Aug 2012 20:24:37 +0200 Subject: [PATCH 02/12] Clarified result comparison. --- src/x11_joystick.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/x11_joystick.c b/src/x11_joystick.c index 7f4fdbc1..09885373 100644 --- a/src/x11_joystick.c +++ b/src/x11_joystick.c @@ -127,7 +127,7 @@ static void pollJoystickEvents(void) if (errno == ENODEV) _glfwLibrary.X11.joystick[i].present = GL_FALSE; - if (result <= 0) + if (result < sizeof(e)) break; // We don't care if it's an init event or not From f50d38f14815979b3a9f21febbf4a96433778b45 Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Tue, 28 Aug 2012 22:56:35 +0200 Subject: [PATCH 03/12] Moved glfwinfo version check to before glfwInit. --- tests/glfwinfo.c | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/tests/glfwinfo.c b/tests/glfwinfo.c index 90fd329f..11a84612 100644 --- a/tests/glfwinfo.c +++ b/tests/glfwinfo.c @@ -181,6 +181,28 @@ int main(int argc, char** argv) argc -= optind; argv += optind; + // Report GLFW version + + glfwGetVersion(&major, &minor, &revision); + + printf("GLFW header version: %u.%u.%u\n", + GLFW_VERSION_MAJOR, + GLFW_VERSION_MINOR, + GLFW_VERSION_REVISION); + + printf("GLFW library version: %u.%u.%u\n", major, minor, revision); + + if (major != GLFW_VERSION_MAJOR || + minor != GLFW_VERSION_MINOR || + revision != GLFW_VERSION_REVISION) + { + printf("*** WARNING: GLFW version mismatch! ***\n"); + } + + printf("GLFW library version string: \"%s\"\n", glfwGetVersionString()); + + // Initialize GLFW and create window + glfwSetErrorCallback(error_callback); if (!glfwInit()) @@ -216,26 +238,6 @@ int main(int argc, char** argv) glfwMakeContextCurrent(window); - // Report GLFW version - - glfwGetVersion(&major, &minor, &revision); - - printf("GLFW header version: %u.%u.%u\n", - GLFW_VERSION_MAJOR, - GLFW_VERSION_MINOR, - GLFW_VERSION_REVISION); - - printf("GLFW library version: %u.%u.%u\n", major, minor, revision); - - if (major != GLFW_VERSION_MAJOR || - minor != GLFW_VERSION_MINOR || - revision != GLFW_VERSION_REVISION) - { - printf("*** WARNING: GLFW version mismatch! ***\n"); - } - - printf("GLFW library version string: \"%s\"\n", glfwGetVersionString()); - // Report OpenGL version printf("OpenGL context version string: \"%s\"\n", glGetString(GL_VERSION)); From 3ef7c554585ce80621d5710c7ce052739d82fad4 Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Wed, 29 Aug 2012 01:24:53 +0200 Subject: [PATCH 04/12] Added credit. --- readme.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/readme.html b/readme.html index 39d0739b..23c9113f 100644 --- a/readme.html +++ b/readme.html @@ -950,6 +950,8 @@ their skills. Special thanks go out to:

  • Samuli Tuomola, for support, bug reports and testing
  • +
  • Torsten Walluhn, for fixing various compilation issues on OS X
  • +
  • Frank Wille, for helping with the AmigaOS port and making GLFW compile under IRIX 5.3
  • From 54f1a57f8d33faaa2f569a74cebdff557b5a851b Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Wed, 29 Aug 2012 16:00:54 +0200 Subject: [PATCH 05/12] Added channel bit depth hint defaults. --- src/window.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/window.c b/src/window.c index acd48195..e59a1d25 100644 --- a/src/window.c +++ b/src/window.c @@ -84,6 +84,12 @@ void _glfwSetDefaultWindowHints(void) // The default is to allow window resizing _glfwLibrary.hints.resizable = GL_TRUE; + + // The default is 24 bits of depth, 8 bits of color + _glfwLibrary.hints.depthBits = 24; + _glfwLibrary.hints.redBits = 8; + _glfwLibrary.hints.greenBits = 8; + _glfwLibrary.hints.blueBits = 8; } From 74f5cd6fa7f00cd1a8b838371dc15458620c9e0a Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Wed, 29 Aug 2012 16:12:44 +0200 Subject: [PATCH 06/12] Removed unused example files. --- examples/particles.c | 1152 ----------------------------------- examples/pong3d.c | 854 -------------------------- examples/pong3d_field.tga | Bin 17816 -> 0 bytes examples/pong3d_instr.tga | Bin 21279 -> 0 bytes examples/pong3d_menu.tga | Bin 1835 -> 0 bytes examples/pong3d_title.tga | Bin 106516 -> 0 bytes examples/pong3d_winner1.tga | Bin 861 -> 0 bytes examples/pong3d_winner2.tga | Bin 891 -> 0 bytes 8 files changed, 2006 deletions(-) delete mode 100644 examples/particles.c delete mode 100644 examples/pong3d.c delete mode 100644 examples/pong3d_field.tga delete mode 100644 examples/pong3d_instr.tga delete mode 100644 examples/pong3d_menu.tga delete mode 100644 examples/pong3d_title.tga delete mode 100644 examples/pong3d_winner1.tga delete mode 100644 examples/pong3d_winner2.tga diff --git a/examples/particles.c b/examples/particles.c deleted file mode 100644 index c7904e11..00000000 --- a/examples/particles.c +++ /dev/null @@ -1,1152 +0,0 @@ -//======================================================================== -// This is a simple, but cool particle engine (buzz-word meaning many -// small objects that are treated as points and drawn as textures -// projected on simple geometry). -// -// This demonstration generates a colorful fountain-like animation. It -// uses several advanced OpenGL teqhniques: -// -// 1) Lighting (per vertex) -// 2) Alpha blending -// 3) Fog -// 4) Texturing -// 5) Display lists (for drawing the static environment geometry) -// 6) Vertex arrays (for drawing the particles) -// 7) GL_EXT_separate_specular_color is used (if available) -// -// Even more so, this program uses multi threading. The program is -// essentialy divided into a main rendering thread and a particle physics -// calculation thread. My benchmarks under Windows 2000 on a single -// processor system show that running this program as two threads instead -// of a single thread means no difference (there may be a very marginal -// advantage for the multi threaded case). On dual processor systems I -// have had reports of 5-25% of speed increase when running this program -// as two threads instead of one thread. -// -// The default behaviour of this program is to use two threads. To force -// a single thread to be used, use the command line switch -s. -// -// To run a fixed length benchmark (60 s), use the command line switch -b. -// -// Benchmark results (640x480x16, best of three tests): -// -// CPU GFX 1 thread 2 threads -// Athlon XP 2700+ GeForce Ti4200 (oc) 757 FPS 759 FPS -// P4 2.8 GHz (SMT) GeForce FX5600 548 FPS 550 FPS -// -// One more thing: Press 'w' during the demo to toggle wireframe mode. -//======================================================================== - -#include -#include -#include -#include -#include - -// Define tokens for GL_EXT_separate_specular_color if not already defined -#ifndef GL_EXT_separate_specular_color -#define GL_LIGHT_MODEL_COLOR_CONTROL_EXT 0x81F8 -#define GL_SINGLE_COLOR_EXT 0x81F9 -#define GL_SEPARATE_SPECULAR_COLOR_EXT 0x81FA -#endif // GL_EXT_separate_specular_color - -// Some 's do not define M_PI -#ifndef M_PI -#define M_PI 3.141592654 -#endif - -// Desired fullscreen resolution -#define WIDTH 640 -#define HEIGHT 480 - - -//======================================================================== -// Type definitions -//======================================================================== - -typedef struct { float x,y,z; } VEC; - -// This structure is used for interleaved vertex arrays (see the -// DrawParticles function) - Note: This structure SHOULD be packed on most -// systems. It uses 32-bit fields on 32-bit boundaries, and is a multiple -// of 64 bits in total (6x32=3x64). If it does not work, try using pragmas -// or whatever to force the structure to be packed. -typedef struct { - GLfloat s, t; // Texture coordinates - GLuint rgba; // Color (four ubytes packed into an uint) - GLfloat x, y, z; // Vertex coordinates -} VERTEX; - - -//======================================================================== -// Program control global variables -//======================================================================== - -// "Running" flag (true if program shall continue to run) -int running; - -// Window dimensions -int width, height; - -// "wireframe" flag (true if we use wireframe view) -int wireframe; - -// "multithreading" flag (true if we use multithreading) -int multithreading; - -// Thread synchronization -struct { - double t; // Time (s) - float dt; // Time since last frame (s) - int p_frame; // Particle physics frame number - int d_frame; // Particle draw frame number - GLFWcond p_done; // Condition: particle physics done - GLFWcond d_done; // Condition: particle draw done - GLFWmutex particles_lock; // Particles data sharing mutex -} thread_sync; - - -//======================================================================== -// Texture declarations (we hard-code them into the source code, since -// they are so simple) -//======================================================================== - -#define P_TEX_WIDTH 8 // Particle texture dimensions -#define P_TEX_HEIGHT 8 -#define F_TEX_WIDTH 16 // Floor texture dimensions -#define F_TEX_HEIGHT 16 - -// Texture object IDs -GLuint particle_tex_id, floor_tex_id; - -// Particle texture (a simple spot) -const unsigned char particle_texture[ P_TEX_WIDTH * P_TEX_HEIGHT ] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x11, 0x22, 0x22, 0x11, 0x00, 0x00, - 0x00, 0x11, 0x33, 0x88, 0x77, 0x33, 0x11, 0x00, - 0x00, 0x22, 0x88, 0xff, 0xee, 0x77, 0x22, 0x00, - 0x00, 0x22, 0x77, 0xee, 0xff, 0x88, 0x22, 0x00, - 0x00, 0x11, 0x33, 0x77, 0x88, 0x33, 0x11, 0x00, - 0x00, 0x00, 0x11, 0x33, 0x22, 0x11, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - -// Floor texture (your basic checkered floor) -const unsigned char floor_texture[ F_TEX_WIDTH * F_TEX_HEIGHT ] = { - 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, - 0xff, 0xf0, 0xcc, 0xf0, 0xf0, 0xf0, 0xff, 0xf0, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, - 0xf0, 0xcc, 0xee, 0xff, 0xf0, 0xf0, 0xf0, 0xf0, 0x30, 0x66, 0x30, 0x30, 0x30, 0x20, 0x30, 0x30, - 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xee, 0xf0, 0xf0, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, - 0xf0, 0xf0, 0xf0, 0xf0, 0xcc, 0xf0, 0xf0, 0xf0, 0x30, 0x30, 0x55, 0x30, 0x30, 0x44, 0x30, 0x30, - 0xf0, 0xdd, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0x33, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, - 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xff, 0xf0, 0xf0, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x60, 0x30, - 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0x33, 0x33, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, - 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x33, 0x30, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, - 0x30, 0x30, 0x30, 0x30, 0x30, 0x20, 0x30, 0x30, 0xf0, 0xff, 0xf0, 0xf0, 0xdd, 0xf0, 0xf0, 0xff, - 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x55, 0x33, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xff, 0xf0, 0xf0, - 0x30, 0x44, 0x66, 0x30, 0x30, 0x30, 0x30, 0x30, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, - 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0xf0, 0xf0, 0xf0, 0xaa, 0xf0, 0xf0, 0xcc, 0xf0, - 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0xff, 0xf0, 0xf0, 0xf0, 0xff, 0xf0, 0xdd, 0xf0, - 0x30, 0x30, 0x30, 0x77, 0x30, 0x30, 0x30, 0x30, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, - 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, -}; - - -//======================================================================== -// These are fixed constants that control the particle engine. In a -// modular world, these values should be variables... -//======================================================================== - -// Maximum number of particles -#define MAX_PARTICLES 3000 - -// Life span of a particle (in seconds) -#define LIFE_SPAN 8.0f - -// A new particle is born every [BIRTH_INTERVAL] second -#define BIRTH_INTERVAL (LIFE_SPAN/(float)MAX_PARTICLES) - -// Particle size (meters) -#define PARTICLE_SIZE 0.7f - -// Gravitational constant (m/s^2) -#define GRAVITY 9.8f - -// Base initial velocity (m/s) -#define VELOCITY 8.0f - -// Bounce friction (1.0 = no friction, 0.0 = maximum friction) -#define FRICTION 0.75f - -// "Fountain" height (m) -#define FOUNTAIN_HEIGHT 3.0f - -// Fountain radius (m) -#define FOUNTAIN_RADIUS 1.6f - -// Minimum delta-time for particle phisics (s) -#define MIN_DELTA_T (BIRTH_INTERVAL * 0.5f) - - -//======================================================================== -// Particle system global variables -//======================================================================== - -// This structure holds all state for a single particle -typedef struct { - float x,y,z; // Position in space - float vx,vy,vz; // Velocity vector - float r,g,b; // Color of particle - float life; // Life of particle (1.0 = newborn, < 0.0 = dead) - int active; // Tells if this particle is active -} PARTICLE; - -// Global vectors holding all particles. We use two vectors for double -// buffering. -static PARTICLE particles[ MAX_PARTICLES ]; - -// Global variable holding the age of the youngest particle -static float min_age; - -// Color of latest born particle (used for fountain lighting) -static float glow_color[4]; - -// Position of latest born particle (used for fountain lighting) -static float glow_pos[4]; - - -//======================================================================== -// Object material and fog configuration constants -//======================================================================== - -const GLfloat fountain_diffuse[4] = {0.7f,1.0f,1.0f,1.0f}; -const GLfloat fountain_specular[4] = {1.0f,1.0f,1.0f,1.0f}; -const GLfloat fountain_shininess = 12.0f; -const GLfloat floor_diffuse[4] = {1.0f,0.6f,0.6f,1.0f}; -const GLfloat floor_specular[4] = {0.6f,0.6f,0.6f,1.0f}; -const GLfloat floor_shininess = 18.0f; -const GLfloat fog_color[4] = {0.1f, 0.1f, 0.1f, 1.0f}; - - -//======================================================================== -// InitParticle() - Initialize a new particle -//======================================================================== - -void InitParticle( PARTICLE *p, double t ) -{ - float xy_angle, velocity; - - // Start position of particle is at the fountain blow-out - p->x = 0.0f; - p->y = 0.0f; - p->z = FOUNTAIN_HEIGHT; - - // Start velocity is up (Z)... - p->vz = 0.7f + (0.3f/4096.f) * (float) (rand() & 4095); - - // ...and a randomly chosen X/Y direction - xy_angle = (2.f * (float)M_PI / 4096.f) * (float) (rand() & 4095); - p->vx = 0.4f * (float) cos( xy_angle ); - p->vy = 0.4f * (float) sin( xy_angle ); - - // Scale velocity vector according to a time-varying velocity - velocity = VELOCITY*(0.8f + 0.1f*(float)(sin( 0.5*t )+sin( 1.31*t ))); - p->vx *= velocity; - p->vy *= velocity; - p->vz *= velocity; - - // Color is time-varying - p->r = 0.7f + 0.3f * (float) sin( 0.34*t + 0.1 ); - p->g = 0.6f + 0.4f * (float) sin( 0.63*t + 1.1 ); - p->b = 0.6f + 0.4f * (float) sin( 0.91*t + 2.1 ); - - // Store settings for fountain glow lighting - glow_pos[0] = 0.4f * (float) sin( 1.34*t ); - glow_pos[1] = 0.4f * (float) sin( 3.11*t ); - glow_pos[2] = FOUNTAIN_HEIGHT + 1.0f; - glow_pos[3] = 1.0f; - glow_color[0] = p->r; - glow_color[1] = p->g; - glow_color[2] = p->b; - glow_color[3] = 1.0f; - - // The particle is new-born and active - p->life = 1.0f; - p->active = 1; -} - - -//======================================================================== -// UpdateParticle() - Update a particle -//======================================================================== - -#define FOUNTAIN_R2 (FOUNTAIN_RADIUS+PARTICLE_SIZE/2)*(FOUNTAIN_RADIUS+PARTICLE_SIZE/2) - -void UpdateParticle( PARTICLE *p, float dt ) -{ - // If the particle is not active, we need not do anything - if( !p->active ) - { - return; - } - - // The particle is getting older... - p->life = p->life - dt * (1.0f / LIFE_SPAN); - - // Did the particle die? - if( p->life <= 0.0f ) - { - p->active = 0; - return; - } - - // Update particle velocity (apply gravity) - p->vz = p->vz - GRAVITY * dt; - - // Update particle position - p->x = p->x + p->vx * dt; - p->y = p->y + p->vy * dt; - p->z = p->z + p->vz * dt; - - // Simple collision detection + response - if( p->vz < 0.0f ) - { - // Particles should bounce on the fountain (with friction) - if( (p->x*p->x + p->y*p->y) < FOUNTAIN_R2 && - p->z < (FOUNTAIN_HEIGHT + PARTICLE_SIZE/2) ) - { - p->vz = -FRICTION * p->vz; - p->z = FOUNTAIN_HEIGHT + PARTICLE_SIZE/2 + - FRICTION * (FOUNTAIN_HEIGHT + - PARTICLE_SIZE/2 - p->z); - } - - // Particles should bounce on the floor (with friction) - else if( p->z < PARTICLE_SIZE/2 ) - { - p->vz = -FRICTION * p->vz; - p->z = PARTICLE_SIZE/2 + - FRICTION * (PARTICLE_SIZE/2 - p->z); - } - - } -} - - -//======================================================================== -// ParticleEngine() - The main frame for the particle engine. Called once -// per frame. -//======================================================================== - -void ParticleEngine( double t, float dt ) -{ - int i; - float dt2; - - // Update particles (iterated several times per frame if dt is too - // large) - while( dt > 0.0f ) - { - // Calculate delta time for this iteration - dt2 = dt < MIN_DELTA_T ? dt : MIN_DELTA_T; - - // Update particles - for( i = 0; i < MAX_PARTICLES; i ++ ) - { - UpdateParticle( &particles[ i ], dt2 ); - } - - // Increase minimum age - min_age += dt2; - - // Should we create any new particle(s)? - while( min_age >= BIRTH_INTERVAL ) - { - min_age -= BIRTH_INTERVAL; - - // Find a dead particle to replace with a new one - for( i = 0; i < MAX_PARTICLES; i ++ ) - { - if( !particles[ i ].active ) - { - InitParticle( &particles[ i ], t + min_age ); - UpdateParticle( &particles[ i ], min_age ); - break; - } - } - } - - // Decrease frame delta time - dt -= dt2; - } -} - - -//======================================================================== -// DrawParticles() - Draw all active particles. We use OpenGL 1.1 vertex -// arrays for this in order to accelerate the drawing. -//======================================================================== - -#define BATCH_PARTICLES 70 // Number of particles to draw in each batch - // (70 corresponds to 7.5 KB = will not blow - // the L1 data cache on most CPUs) -#define PARTICLE_VERTS 4 // Number of vertices per particle - -void DrawParticles( double t, float dt ) -{ - int i, particle_count; - VERTEX vertex_array[ BATCH_PARTICLES * PARTICLE_VERTS ], *vptr; - float alpha; - GLuint rgba; - VEC quad_lower_left, quad_lower_right; - GLfloat mat[ 16 ]; - PARTICLE *pptr; - - // Here comes the real trick with flat single primitive objects (s.c. - // "billboards"): We must rotate the textured primitive so that it - // always faces the viewer (is coplanar with the view-plane). - // We: - // 1) Create the primitive around origo (0,0,0) - // 2) Rotate it so that it is coplanar with the view plane - // 3) Translate it according to the particle position - // Note that 1) and 2) is the same for all particles (done only once). - - // Get modelview matrix. We will only use the upper left 3x3 part of - // the matrix, which represents the rotation. - glGetFloatv( GL_MODELVIEW_MATRIX, mat ); - - // 1) & 2) We do it in one swift step: - // Although not obvious, the following six lines represent two matrix/ - // vector multiplications. The matrix is the inverse 3x3 rotation - // matrix (i.e. the transpose of the same matrix), and the two vectors - // represent the lower left corner of the quad, PARTICLE_SIZE/2 * - // (-1,-1,0), and the lower right corner, PARTICLE_SIZE/2 * (1,-1,0). - // The upper left/right corners of the quad is always the negative of - // the opposite corners (regardless of rotation). - quad_lower_left.x = (-PARTICLE_SIZE/2) * (mat[0] + mat[1]); - quad_lower_left.y = (-PARTICLE_SIZE/2) * (mat[4] + mat[5]); - quad_lower_left.z = (-PARTICLE_SIZE/2) * (mat[8] + mat[9]); - quad_lower_right.x = (PARTICLE_SIZE/2) * (mat[0] - mat[1]); - quad_lower_right.y = (PARTICLE_SIZE/2) * (mat[4] - mat[5]); - quad_lower_right.z = (PARTICLE_SIZE/2) * (mat[8] - mat[9]); - - // Don't update z-buffer, since all particles are transparent! - glDepthMask( GL_FALSE ); - - // Enable blending - glEnable( GL_BLEND ); - glBlendFunc( GL_SRC_ALPHA, GL_ONE ); - - // Select particle texture - if( !wireframe ) - { - glEnable( GL_TEXTURE_2D ); - glBindTexture( GL_TEXTURE_2D, particle_tex_id ); - } - - // Set up vertex arrays. We use interleaved arrays, which is easier to - // handle (in most situations) and it gives a linear memeory access - // access pattern (which may give better performance in some - // situations). GL_T2F_C4UB_V3F means: 2 floats for texture coords, - // 4 ubytes for color and 3 floats for vertex coord (in that order). - // Most OpenGL cards / drivers are optimized for this format. - glInterleavedArrays( GL_T2F_C4UB_V3F, 0, vertex_array ); - - // Is particle physics carried out in a separate thread? - if( multithreading ) - { - // Wait for particle physics thread to be done - glfwLockMutex( thread_sync.particles_lock ); - while( running && thread_sync.p_frame <= thread_sync.d_frame ) - { - glfwWaitCond( thread_sync.p_done, thread_sync.particles_lock, - 0.1 ); - } - - // Store the frame time and delta time for the physics thread - thread_sync.t = t; - thread_sync.dt = dt; - - // Update frame counter - thread_sync.d_frame ++; - } - else - { - // Perform particle physics in this thread - ParticleEngine( t, dt ); - } - - // Loop through all particles and build vertex arrays. - particle_count = 0; - vptr = vertex_array; - pptr = particles; - for( i = 0; i < MAX_PARTICLES; i ++ ) - { - if( pptr->active ) - { - // Calculate particle intensity (we set it to max during 75% - // of its life, then it fades out) - alpha = 4.0f * pptr->life; - if( alpha > 1.0f ) - { - alpha = 1.0f; - } - - // Convert color from float to 8-bit (store it in a 32-bit - // integer using endian independent type casting) - ((GLubyte *)&rgba)[0] = (GLubyte)(pptr->r * 255.0f); - ((GLubyte *)&rgba)[1] = (GLubyte)(pptr->g * 255.0f); - ((GLubyte *)&rgba)[2] = (GLubyte)(pptr->b * 255.0f); - ((GLubyte *)&rgba)[3] = (GLubyte)(alpha * 255.0f); - - // 3) Translate the quad to the correct position in modelview - // space and store its parameters in vertex arrays (we also - // store texture coord and color information for each vertex). - - // Lower left corner - vptr->s = 0.0f; - vptr->t = 0.0f; - vptr->rgba = rgba; - vptr->x = pptr->x + quad_lower_left.x; - vptr->y = pptr->y + quad_lower_left.y; - vptr->z = pptr->z + quad_lower_left.z; - vptr ++; - - // Lower right corner - vptr->s = 1.0f; - vptr->t = 0.0f; - vptr->rgba = rgba; - vptr->x = pptr->x + quad_lower_right.x; - vptr->y = pptr->y + quad_lower_right.y; - vptr->z = pptr->z + quad_lower_right.z; - vptr ++; - - // Upper right corner - vptr->s = 1.0f; - vptr->t = 1.0f; - vptr->rgba = rgba; - vptr->x = pptr->x - quad_lower_left.x; - vptr->y = pptr->y - quad_lower_left.y; - vptr->z = pptr->z - quad_lower_left.z; - vptr ++; - - // Upper left corner - vptr->s = 0.0f; - vptr->t = 1.0f; - vptr->rgba = rgba; - vptr->x = pptr->x - quad_lower_right.x; - vptr->y = pptr->y - quad_lower_right.y; - vptr->z = pptr->z - quad_lower_right.z; - vptr ++; - - // Increase count of drawable particles - particle_count ++; - } - - // If we have filled up one batch of particles, draw it as a set - // of quads using glDrawArrays. - if( particle_count >= BATCH_PARTICLES ) - { - // The first argument tells which primitive type we use (QUAD) - // The second argument tells the index of the first vertex (0) - // The last argument is the vertex count - glDrawArrays( GL_QUADS, 0, PARTICLE_VERTS * particle_count ); - particle_count = 0; - vptr = vertex_array; - } - - // Next particle - pptr ++; - } - - // We are done with the particle data: Unlock mutex and signal physics - // thread - if( multithreading ) - { - glfwUnlockMutex( thread_sync.particles_lock ); - glfwSignalCond( thread_sync.d_done ); - } - - // Draw final batch of particles (if any) - glDrawArrays( GL_QUADS, 0, PARTICLE_VERTS * particle_count ); - - // Disable vertex arrays (Note: glInterleavedArrays implicitly called - // glEnableClientState for vertex, texture coord and color arrays) - glDisableClientState( GL_VERTEX_ARRAY ); - glDisableClientState( GL_TEXTURE_COORD_ARRAY ); - glDisableClientState( GL_COLOR_ARRAY ); - - // Disable texturing and blending - glDisable( GL_TEXTURE_2D ); - glDisable( GL_BLEND ); - - // Allow Z-buffer updates again - glDepthMask( GL_TRUE ); -} - - -//======================================================================== -// Fountain geometry specification -//======================================================================== - -#define FOUNTAIN_SIDE_POINTS 14 -#define FOUNTAIN_SWEEP_STEPS 32 - -static const float fountain_side[ FOUNTAIN_SIDE_POINTS*2 ] = { - 1.2f, 0.0f, 1.0f, 0.2f, 0.41f, 0.3f, 0.4f, 0.35f, - 0.4f, 1.95f, 0.41f, 2.0f, 0.8f, 2.2f, 1.2f, 2.4f, - 1.5f, 2.7f, 1.55f,2.95f, 1.6f, 3.0f, 1.0f, 3.0f, - 0.5f, 3.0f, 0.0f, 3.0f -}; - -static const float fountain_normal[ FOUNTAIN_SIDE_POINTS*2 ] = { - 1.0000f, 0.0000f, 0.6428f, 0.7660f, 0.3420f, 0.9397f, 1.0000f, 0.0000f, - 1.0000f, 0.0000f, 0.3420f,-0.9397f, 0.4226f,-0.9063f, 0.5000f,-0.8660f, - 0.7660f,-0.6428f, 0.9063f,-0.4226f, 0.0000f,1.00000f, 0.0000f,1.00000f, - 0.0000f,1.00000f, 0.0000f,1.00000f -}; - - -//======================================================================== -// DrawFountain() - Draw a fountain -//======================================================================== - -void DrawFountain( void ) -{ - static GLuint fountain_list = 0; - double angle; - float x, y; - int m, n; - - // The first time, we build the fountain display list - if( !fountain_list ) - { - // Start recording of a new display list - fountain_list = glGenLists( 1 ); - glNewList( fountain_list, GL_COMPILE_AND_EXECUTE ); - - // Set fountain material - glMaterialfv( GL_FRONT, GL_DIFFUSE, fountain_diffuse ); - glMaterialfv( GL_FRONT, GL_SPECULAR, fountain_specular ); - glMaterialf( GL_FRONT, GL_SHININESS, fountain_shininess ); - - // Build fountain using triangle strips - for( n = 0; n < FOUNTAIN_SIDE_POINTS-1; n ++ ) - { - glBegin( GL_TRIANGLE_STRIP ); - for( m = 0; m <= FOUNTAIN_SWEEP_STEPS; m ++ ) - { - angle = (double) m * (2.0*M_PI/(double)FOUNTAIN_SWEEP_STEPS); - x = (float) cos( angle ); - y = (float) sin( angle ); - - // Draw triangle strip - glNormal3f( x * fountain_normal[ n*2+2 ], - y * fountain_normal[ n*2+2 ], - fountain_normal[ n*2+3 ] ); - glVertex3f( x * fountain_side[ n*2+2 ], - y * fountain_side[ n*2+2 ], - fountain_side[ n*2+3 ] ); - glNormal3f( x * fountain_normal[ n*2 ], - y * fountain_normal[ n*2 ], - fountain_normal[ n*2+1 ] ); - glVertex3f( x * fountain_side[ n*2 ], - y * fountain_side[ n*2 ], - fountain_side[ n*2+1 ] ); - } - glEnd(); - } - - // End recording of display list - glEndList(); - } - else - { - // Playback display list - glCallList( fountain_list ); - } -} - - -//======================================================================== -// TesselateFloor() - Recursive function for building variable tesselated -// floor -//======================================================================== - -void TesselateFloor( float x1, float y1, float x2, float y2, - int recursion ) -{ - float delta, x, y; - - // Last recursion? - if( recursion >= 5 ) - { - delta = 999999.0f; - } - else - { - x = (float) (fabs(x1) < fabs(x2) ? fabs(x1) : fabs(x2)); - y = (float) (fabs(y1) < fabs(y2) ? fabs(y1) : fabs(y2)); - delta = x*x + y*y; - } - - // Recurse further? - if( delta < 0.1f ) - { - x = (x1+x2) * 0.5f; - y = (y1+y2) * 0.5f; - TesselateFloor( x1,y1, x, y, recursion + 1 ); - TesselateFloor( x,y1, x2, y, recursion + 1 ); - TesselateFloor( x1, y, x,y2, recursion + 1 ); - TesselateFloor( x, y, x2,y2, recursion + 1 ); - } - else - { - glTexCoord2f( x1*30.0f, y1*30.0f ); - glVertex3f( x1*80.0f, y1*80.0f , 0.0f ); - glTexCoord2f( x2*30.0f, y1*30.0f ); - glVertex3f( x2*80.0f, y1*80.0f , 0.0f ); - glTexCoord2f( x2*30.0f, y2*30.0f ); - glVertex3f( x2*80.0f, y2*80.0f , 0.0f ); - glTexCoord2f( x1*30.0f, y2*30.0f ); - glVertex3f( x1*80.0f, y2*80.0f , 0.0f ); - } -} - - -//======================================================================== -// DrawFloor() - Draw floor. We builde the floor recursively, and let the -// tesselation in the centre (near x,y=0,0) be high, while the selleation -// around the edges be low. -//======================================================================== - -void DrawFloor( void ) -{ - static GLuint floor_list = 0; - - // Select floor texture - if( !wireframe ) - { - glEnable( GL_TEXTURE_2D ); - glBindTexture( GL_TEXTURE_2D, floor_tex_id ); - } - - // The first time, we build the floor display list - if( !floor_list ) - { - // Start recording of a new display list - floor_list = glGenLists( 1 ); - glNewList( floor_list, GL_COMPILE_AND_EXECUTE ); - - // Set floor material - glMaterialfv( GL_FRONT, GL_DIFFUSE, floor_diffuse ); - glMaterialfv( GL_FRONT, GL_SPECULAR, floor_specular ); - glMaterialf( GL_FRONT, GL_SHININESS, floor_shininess ); - - // Draw floor as a bunch of triangle strips (high tesselation - // improves lighting) - glNormal3f( 0.0f, 0.0f, 1.0f ); - glBegin( GL_QUADS ); - TesselateFloor( -1.0f,-1.0f, 0.0f,0.0f, 0 ); - TesselateFloor( 0.0f,-1.0f, 1.0f,0.0f, 0 ); - TesselateFloor( 0.0f, 0.0f, 1.0f,1.0f, 0 ); - TesselateFloor( -1.0f, 0.0f, 0.0f,1.0f, 0 ); - glEnd(); - - // End recording of display list - glEndList(); - } - else - { - // Playback display list - glCallList( floor_list ); - } - - glDisable( GL_TEXTURE_2D ); - -} - - -//======================================================================== -// SetupLights() - Position and configure light sources -//======================================================================== - -void SetupLights( void ) -{ - float l1pos[4], l1amb[4], l1dif[4], l1spec[4]; - float l2pos[4], l2amb[4], l2dif[4], l2spec[4]; - - // Set light source 1 parameters - l1pos[0] = 0.0f; l1pos[1] = -9.0f; l1pos[2] = 8.0f; l1pos[3] = 1.0f; - l1amb[0] = 0.2f; l1amb[1] = 0.2f; l1amb[2] = 0.2f; l1amb[3] = 1.0f; - l1dif[0] = 0.8f; l1dif[1] = 0.4f; l1dif[2] = 0.2f; l1dif[3] = 1.0f; - l1spec[0] = 1.0f; l1spec[1] = 0.6f; l1spec[2] = 0.2f; l1spec[3] = 0.0f; - - // Set light source 2 parameters - l2pos[0] = -15.0f; l2pos[1] = 12.0f; l2pos[2] = 1.5f; l2pos[3] = 1.0f; - l2amb[0] = 0.0f; l2amb[1] = 0.0f; l2amb[2] = 0.0f; l2amb[3] = 1.0f; - l2dif[0] = 0.2f; l2dif[1] = 0.4f; l2dif[2] = 0.8f; l2dif[3] = 1.0f; - l2spec[0] = 0.2f; l2spec[1] = 0.6f; l2spec[2] = 1.0f; l2spec[3] = 0.0f; - - // Configure light sources in OpenGL - glLightfv( GL_LIGHT1, GL_POSITION, l1pos ); - glLightfv( GL_LIGHT1, GL_AMBIENT, l1amb ); - glLightfv( GL_LIGHT1, GL_DIFFUSE, l1dif ); - glLightfv( GL_LIGHT1, GL_SPECULAR, l1spec ); - glLightfv( GL_LIGHT2, GL_POSITION, l2pos ); - glLightfv( GL_LIGHT2, GL_AMBIENT, l2amb ); - glLightfv( GL_LIGHT2, GL_DIFFUSE, l2dif ); - glLightfv( GL_LIGHT2, GL_SPECULAR, l2spec ); - glLightfv( GL_LIGHT3, GL_POSITION, glow_pos ); - glLightfv( GL_LIGHT3, GL_DIFFUSE, glow_color ); - glLightfv( GL_LIGHT3, GL_SPECULAR, glow_color ); - - // Enable light sources - glEnable( GL_LIGHT1 ); - glEnable( GL_LIGHT2 ); - glEnable( GL_LIGHT3 ); -} - - -//======================================================================== -// Draw() - Main rendering function -//======================================================================== - -void Draw( double t ) -{ - double xpos, ypos, zpos, angle_x, angle_y, angle_z; - static double t_old = 0.0; - float dt; - - // Calculate frame-to-frame delta time - dt = (float)(t-t_old); - t_old = t; - - // Setup viewport - glViewport( 0, 0, width, height ); - - // Clear color and Z-buffer - glClearColor( 0.1f, 0.1f, 0.1f, 1.0f ); - glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); - - // Setup projection - glMatrixMode( GL_PROJECTION ); - glLoadIdentity(); - gluPerspective( 65.0, (double)width/(double)height, 1.0, 60.0 ); - - // Setup camera - glMatrixMode( GL_MODELVIEW ); - glLoadIdentity(); - - // Rotate camera - angle_x = 90.0 - 10.0; - angle_y = 10.0 * sin( 0.3 * t ); - angle_z = 10.0 * t; - glRotated( -angle_x, 1.0, 0.0, 0.0 ); - glRotated( -angle_y, 0.0, 1.0, 0.0 ); - glRotated( -angle_z, 0.0, 0.0, 1.0 ); - - // Translate camera - xpos = 15.0 * sin( (M_PI/180.0) * angle_z ) + - 2.0 * sin( (M_PI/180.0) * 3.1 * t ); - ypos = -15.0 * cos( (M_PI/180.0) * angle_z ) + - 2.0 * cos( (M_PI/180.0) * 2.9 * t ); - zpos = 4.0 + 2.0 * cos( (M_PI/180.0) * 4.9 * t ); - glTranslated( -xpos, -ypos, -zpos ); - - // Enable face culling - glFrontFace( GL_CCW ); - glCullFace( GL_BACK ); - glEnable( GL_CULL_FACE ); - - // Enable lighting - SetupLights(); - glEnable( GL_LIGHTING ); - - // Enable fog (dim details far away) - glEnable( GL_FOG ); - glFogi( GL_FOG_MODE, GL_EXP ); - glFogf( GL_FOG_DENSITY, 0.05f ); - glFogfv( GL_FOG_COLOR, fog_color ); - - // Draw floor - DrawFloor(); - - // Enable Z-buffering - glEnable( GL_DEPTH_TEST ); - glDepthFunc( GL_LEQUAL ); - glDepthMask( GL_TRUE ); - - // Draw fountain - DrawFountain(); - - // Disable fog & lighting - glDisable( GL_LIGHTING ); - glDisable( GL_FOG ); - - // Draw all particles (must be drawn after all solid objects have been - // drawn!) - DrawParticles( t, dt ); - - // Z-buffer not needed anymore - glDisable( GL_DEPTH_TEST ); -} - - -//======================================================================== -// Resize() - GLFW window resize callback function -//======================================================================== - -void GLFWCALL Resize( int x, int y ) -{ - width = x; - height = y > 0 ? y : 1; // Prevent division by zero in aspect calc. -} - - -//======================================================================== -// Input callback functions -//======================================================================== - -void GLFWCALL KeyFun( int key, int action ) -{ - if( action == GLFW_PRESS ) - { - switch( key ) - { - case GLFW_KEY_ESC: - running = 0; - break; - case 'W': - wireframe = !wireframe; - glPolygonMode( GL_FRONT_AND_BACK, - wireframe ? GL_LINE : GL_FILL ); - break; - default: - break; - } - } -} - - -//======================================================================== -// PhysicsThreadFun() - Thread for updating particle physics -//======================================================================== - -void GLFWCALL PhysicsThreadFun( void *arg ) -{ - while( running ) - { - // Lock mutex - glfwLockMutex( thread_sync.particles_lock ); - - // Wait for particle drawing to be done - while( running && thread_sync.p_frame > thread_sync.d_frame ) - { - glfwWaitCond( thread_sync.d_done, thread_sync.particles_lock, - 0.1 ); - } - - // No longer running? - if( !running ) - { - break; - } - - // Update particles - ParticleEngine( thread_sync.t, thread_sync.dt ); - - // Update frame counter - thread_sync.p_frame ++; - - // Unlock mutex and signal drawing thread - glfwUnlockMutex( thread_sync.particles_lock ); - glfwSignalCond( thread_sync.p_done ); - } -} - - -//======================================================================== -// main() -//======================================================================== - -int main( int argc, char **argv ) -{ - int i, frames, benchmark; - double t0, t; - GLFWthread physics_thread = 0; - - // Use multithreading by default, but don't benchmark - multithreading = 1; - benchmark = 0; - - // Check command line arguments - for( i = 1; i < argc; i ++ ) - { - // Use benchmarking? - if( strcmp( argv[i], "-b" ) == 0 ) - { - benchmark = 1; - } - - // Force multithreading off? - else if( strcmp( argv[i], "-s" ) == 0 ) - { - multithreading = 0; - } - - // With a Finder launch on Mac OS X we get a bogus -psn_0_46268417 - // kind of argument (actual numbers vary). Ignore it. - else if( strncmp( argv[i], "-psn_", 5) == 0 ); - - // Usage - else - { - if( strcmp( argv[i], "-?" ) != 0 ) - { - printf( "Unknonwn option %s\n\n", argv[ i ] ); - } - printf( "Usage: %s [options]\n", argv[ 0 ] ); - printf( "\n"); - printf( "Options:\n" ); - printf( " -b Benchmark (run program for 60 s)\n" ); - printf( " -s Run program as single thread (default is to use two threads)\n" ); - printf( " -? Display this text\n" ); - printf( "\n"); - printf( "Program runtime controls:\n" ); - printf( " w Toggle wireframe mode\n" ); - printf( " ESC Exit program\n" ); - exit( 0 ); - } - } - - // Initialize GLFW - if( !glfwInit() ) - { - fprintf( stderr, "Failed to initialize GLFW\n" ); - exit( EXIT_FAILURE ); - } - - // Open OpenGL fullscreen window - if( !glfwCreateWindow( WIDTH, HEIGHT, 0,0,0,0, 16,0, GLFW_FULLSCREEN ) ) - { - fprintf( stderr, "Failed to open GLFW window\n" ); - glfwTerminate(); - exit( EXIT_FAILURE ); - } - - // Set window title - glfwSetWindowTitle( "Particle engine" ); - - // Disable VSync (we want to get as high FPS as possible!) - glfwSwapInterval( 0 ); - - // Window resize callback function - glfwSetWindowSizeCallback( Resize ); - - // Set keyboard input callback function - glfwSetKeyCallback( KeyFun ); - - // Upload particle texture - glGenTextures( 1, &particle_tex_id ); - glBindTexture( GL_TEXTURE_2D, particle_tex_id ); - glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP ); - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP ); - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); - glTexImage2D( GL_TEXTURE_2D, 0, GL_LUMINANCE, P_TEX_WIDTH, P_TEX_HEIGHT, - 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, particle_texture ); - - // Upload floor texture - glGenTextures( 1, &floor_tex_id ); - glBindTexture( GL_TEXTURE_2D, floor_tex_id ); - glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); - 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 ); - glTexImage2D( GL_TEXTURE_2D, 0, GL_LUMINANCE, F_TEX_WIDTH, F_TEX_HEIGHT, - 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, floor_texture ); - - // Check if we have GL_EXT_separate_specular_color, and if so use it - if( glfwExtensionSupported( "GL_EXT_separate_specular_color" ) ) - { - glLightModeli( GL_LIGHT_MODEL_COLOR_CONTROL_EXT, - GL_SEPARATE_SPECULAR_COLOR_EXT ); - } - - // Set filled polygon mode as default (not wireframe) - glPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); - wireframe = 0; - - // Clear particle system - for( i = 0; i < MAX_PARTICLES; i ++ ) - { - particles[ i ].active = 0; - } - min_age = 0.0f; - - // Set "running" flag - running = 1; - - // Set initial times - thread_sync.t = 0.0; - thread_sync.dt = 0.001f; - - // Init threading - if( multithreading ) - { - thread_sync.p_frame = 0; - thread_sync.d_frame = 0; - thread_sync.particles_lock = glfwCreateMutex(); - thread_sync.p_done = glfwCreateCond(); - thread_sync.d_done = glfwCreateCond(); - physics_thread = glfwCreateThread( PhysicsThreadFun, NULL ); - } - - // Main loop - t0 = glfwGetTime(); - frames = 0; - while( running ) - { - // Get frame time - t = glfwGetTime() - t0; - - // Draw... - Draw( t ); - - // Swap buffers - glfwSwapBuffers(); - - // Check if window was closed - running = running && glfwGetWindowParam( GLFW_OPENED ); - - // Increase frame count - frames ++; - - // End of benchmark? - if( benchmark && t >= 60.0 ) - { - running = 0; - } - } - t = glfwGetTime() - t0; - - // Wait for particle physics thread to die - if( multithreading ) - { - glfwWaitThread( physics_thread, GLFW_WAIT ); - } - - // Display profiling information - printf( "%d frames in %.2f seconds = %.1f FPS", frames, t, - (double)frames / t ); - printf( " (multithreading %s)\n", multithreading ? "on" : "off" ); - - // Terminate OpenGL - glfwTerminate(); - - exit( EXIT_SUCCESS ); -} - diff --git a/examples/pong3d.c b/examples/pong3d.c deleted file mode 100644 index 58c9ee20..00000000 --- a/examples/pong3d.c +++ /dev/null @@ -1,854 +0,0 @@ -//======================================================================== -// 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( !glfwCreateWindow( 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 ); -} - diff --git a/examples/pong3d_field.tga b/examples/pong3d_field.tga deleted file mode 100644 index cc20bbdb82434faa3bbc01d27fd199d5214c8b2f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17816 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 T6XevNs>&3`i95eA{_g(;MdbFK diff --git a/examples/pong3d_instr.tga b/examples/pong3d_instr.tga deleted file mode 100644 index 758eb447a97187cef812cb9100aa94631954c256..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21279 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 diff --git a/examples/pong3d_menu.tga b/examples/pong3d_menu.tga deleted file mode 100644 index d0d6c5a431687bfd6442e43c3878a9eb905c48b8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1835 zcmbVMTawc-5FFd7KrLK{i*bQLe#C)-km6&1*q~+M0-Qi+$_YgGjAbh+zSu1}vefFC z>1kQyjg-Il@=jjMRenoIj~IXChe+&m7gLVsZQ6^Zkej3-2jP$yvZRY#urXlCQ#Jn=lGQ*?msDM%?fom_#3D0WLYd>| zrM8HEH4zdvDSlgLUu?J06w%uHU$F5BH?%^)6{nIMA=d zk04{L6gsNXLvPU$y7bcLR=F~R;{Yz~AoE6761IR~EZy-Sc90#?@jjEKjdZ2xgd?t2 zqtTm)Fw3kYxE=nmQqN{37J-&5_=7y4JqUx5<790bksq4`P6Xm?W2J#gsv94$c80Ep zhB(C(Tcv$%4nwfg>5lD?ncV@L-j6_|Q=WiD2HsmBQ7_k*Bwh}mIAS5_(&Zv)0+JQ*BV=)qzNL$W#l<{{t=av1 zh6fQJ3*Rf(fcY7y8DcVufs`P8r)X65U6x`6n^qTRM5LcL_vtFnz@hK-l+BKK+wK>L@83So!V-2z3xXZm}7`ZB*l$gvAC5tvMEd)H$-L j=(lM?g-Vm}Fwx!OsnJ{IV_$0Jn){}t&ktU$$5s9UqXSX} diff --git a/examples/pong3d_title.tga b/examples/pong3d_title.tga deleted file mode 100644 index d0d8e36d74011c25b908647632b4997dc0cf6456..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 106516 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 diff --git a/examples/pong3d_winner1.tga b/examples/pong3d_winner1.tga deleted file mode 100644 index f963720c5b897c01b8694a0fb2d76ad617877a7d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 861 zcmZvaO-{ow5QV29sw!Ag>q$zL$Sb2xaDZHc?={d~z#x?;&rAh7an>TNM%UG8<_V`T}Av+dQR1Q!U7Qc6{sSH8wNZP39q9qy#b$XF9@I6ez)S<6n zq_ehk;iMZ&fsh(meZGobq4LV7e~pc@d~^GlelDh;wKF}zCi`6*Je6~M>is%9SNEbW LDfk1czN>u!GHiyO diff --git a/examples/pong3d_winner2.tga b/examples/pong3d_winner2.tga deleted file mode 100644 index ea8266dea9f6ca7b261f2163b4f2bc4777a59027..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 891 zcmZvbOHRWu5Qe8Asw!Ag>q)E8}th{PDhL`jrJ%{=Jv6BL#k|RH6{>RMJ?8(;O zi#^-HZuV}m>~grW8Ozl^%t9=dzKDzE#2U(`*Z?%X-syv|S(? zK)sHv$a>wtB4Eb?Ma3Y>!sKURS)Tb0r3+%vTaEo6DJm{if#^X&j{?aT=fbk|<~&_O zt+PmDbft=sKHTt?QQd&&`CbHe)uYgASx)wtida}Bc}9?zn%(@SUw-irZBG_CWXznz z=XiOM7P58fg0$VeHnc3XW7$gDg_J8@PnncbAuX(+Ga!3cnA!amNonh?123}NqEvQz zr;kDo0gOh0x+DVBrpbD_p`wQ*Phz^)ltEEbz;t}sX=m;%O{Moj&@6$p=8wfP;J5)? zkhFgfjw0#^%tT0ME$!44Ga|_7pj0Ep>Z9on#mYG5)@84;kMi2DpOfl)jnK+)SAA~d cBD_m`U#oV84}EF9M`aSmW&0a^ddAJZ0j)ZbKmY&$ From cdcf3be4620e7eb5acb7990d837cdf8d4381ba02 Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Wed, 29 Aug 2012 17:26:54 +0200 Subject: [PATCH 07/12] Removed event debug printfs. --- src/x11_window.c | 42 ------------------------------------------ 1 file changed, 42 deletions(-) diff --git a/src/x11_window.c b/src/x11_window.c index a34449d8..b6c7f4ae 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -477,10 +477,7 @@ static void processSingleEvent(void) // A keyboard key was pressed window = findWindow(event.xkey.window); if (window == NULL) - { - fprintf(stderr, "Cannot find GLFW window structure for KeyPress event\n"); return; - } // Translate and report key press _glfwInputKey(window, translateKey(event.xkey.keycode), GLFW_PRESS); @@ -496,10 +493,7 @@ static void processSingleEvent(void) // A keyboard key was released window = findWindow(event.xkey.window); if (window == NULL) - { - fprintf(stderr, "Cannot find GLFW window structure for KeyRelease event\n"); return; - } // Do not report key releases for key repeats. For key repeats we // will get KeyRelease/KeyPress pairs with similar or identical @@ -538,10 +532,7 @@ static void processSingleEvent(void) // A mouse button was pressed or a scrolling event occurred window = findWindow(event.xbutton.window); if (window == NULL) - { - fprintf(stderr, "Cannot find GLFW window structure for ButtonPress event\n"); return; - } if (event.xbutton.button == Button1) _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_LEFT, GLFW_PRESS); @@ -570,10 +561,7 @@ static void processSingleEvent(void) // A mouse button was released window = findWindow(event.xbutton.window); if (window == NULL) - { - fprintf(stderr, "Cannot find GLFW window structure for ButtonRelease event\n"); return; - } if (event.xbutton.button == Button1) { @@ -601,10 +589,7 @@ static void processSingleEvent(void) // The cursor entered the window window = findWindow(event.xcrossing.window); if (window == NULL) - { - fprintf(stderr, "Cannot find GLFW window structure for EnterNotify event\n"); return; - } if (window->cursorMode == GLFW_CURSOR_HIDDEN) hideCursor(window); @@ -618,10 +603,7 @@ static void processSingleEvent(void) // The cursor left the window window = findWindow(event.xcrossing.window); if (window == NULL) - { - fprintf(stderr, "Cannot find GLFW window structure for LeaveNotify event\n"); return; - } if (window->cursorMode == GLFW_CURSOR_HIDDEN) showCursor(window); @@ -635,10 +617,7 @@ static void processSingleEvent(void) // The cursor was moved window = findWindow(event.xmotion.window); if (window == NULL) - { - fprintf(stderr, "Cannot find GLFW window structure for MotionNotify event\n"); return; - } if (event.xmotion.x != window->X11.cursorPosX || event.xmotion.y != window->X11.cursorPosY) @@ -676,10 +655,7 @@ static void processSingleEvent(void) // The window configuration changed somehow window = findWindow(event.xconfigure.window); if (window == NULL) - { - fprintf(stderr, "Cannot find GLFW window structure for ConfigureNotify event\n"); return; - } _glfwInputWindowSize(window, event.xconfigure.width, @@ -697,10 +673,7 @@ static void processSingleEvent(void) // Custom client message, probably from the window manager window = findWindow(event.xclient.window); if (window == NULL) - { - fprintf(stderr, "Cannot find GLFW window structure for ClientMessage event\n"); return; - } if ((Atom) event.xclient.data.l[0] == _glfwLibrary.X11.wmDeleteWindow) { @@ -731,10 +704,7 @@ static void processSingleEvent(void) // The window was mapped window = findWindow(event.xmap.window); if (window == NULL) - { - fprintf(stderr, "Cannot find GLFW window structure for MapNotify event\n"); return; - } _glfwInputWindowIconify(window, GL_FALSE); break; @@ -745,10 +715,7 @@ static void processSingleEvent(void) // The window was unmapped window = findWindow(event.xmap.window); if (window == NULL) - { - fprintf(stderr, "Cannot find GLFW window structure for UnmapNotify event\n"); return; - } _glfwInputWindowIconify(window, GL_TRUE); break; @@ -759,10 +726,7 @@ static void processSingleEvent(void) // The window gained focus window = findWindow(event.xfocus.window); if (window == NULL) - { - fprintf(stderr, "Cannot find GLFW window structure for FocusIn event\n"); return; - } _glfwInputWindowFocus(window, GL_TRUE); @@ -777,10 +741,7 @@ static void processSingleEvent(void) // The window lost focus window = findWindow(event.xfocus.window); if (window == NULL) - { - fprintf(stderr, "Cannot find GLFW window structure for FocusOut event\n"); return; - } _glfwInputWindowFocus(window, GL_FALSE); @@ -795,10 +756,7 @@ static void processSingleEvent(void) // The window's contents was damaged window = findWindow(event.xexpose.window); if (window == NULL) - { - fprintf(stderr, "Cannot find GLFW window structure for Expose event\n"); return; - } _glfwInputWindowDamage(window); break; From 38e4cc3dad598583240cd1176080a19567513ad5 Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Wed, 29 Aug 2012 17:29:46 +0200 Subject: [PATCH 08/12] Removed superfluous comments. --- src/x11_window.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/x11_window.c b/src/x11_window.c index b6c7f4ae..4ee16644 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -479,12 +479,8 @@ static void processSingleEvent(void) if (window == NULL) return; - // Translate and report key press _glfwInputKey(window, translateKey(event.xkey.keycode), GLFW_PRESS); - - // Translate and report character input _glfwInputChar(window, translateChar(&event.xkey)); - break; } @@ -521,9 +517,7 @@ static void processSingleEvent(void) } } - // Translate and report key release _glfwInputKey(window, translateKey(event.xkey.keycode), GLFW_RELEASE); - break; } From 2502e4d6f36011732d14e9253faeeb88a743e536 Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Wed, 29 Aug 2012 18:31:12 +0200 Subject: [PATCH 09/12] Renamed glfwGetJoystickPos to glfwGetJoystickAxes. --- include/GL/glfw3.h | 2 +- readme.html | 1 + src/cocoa_joystick.m | 8 ++++---- src/internal.h | 2 +- src/joystick.c | 8 ++++---- src/win32_joystick.c | 14 +++++++------- src/x11_joystick.c | 4 ++-- tests/joysticks.c | 2 +- 8 files changed, 21 insertions(+), 20 deletions(-) diff --git a/include/GL/glfw3.h b/include/GL/glfw3.h index 5172da21..45f44339 100644 --- a/include/GL/glfw3.h +++ b/include/GL/glfw3.h @@ -564,7 +564,7 @@ GLFWAPI void glfwSetScrollCallback(GLFWscrollfun cbfun); /* Joystick input */ GLFWAPI int glfwGetJoystickParam(int joy, int param); -GLFWAPI int glfwGetJoystickPos(int joy, float* pos, int numaxes); +GLFWAPI int glfwGetJoystickAxes(int joy, float* axes, int numaxes); GLFWAPI int glfwGetJoystickButtons(int joy, unsigned char* buttons, int numbuttons); /* Clipboard */ diff --git a/readme.html b/readme.html index 23c9113f..d330d1c1 100644 --- a/readme.html +++ b/readme.html @@ -300,6 +300,7 @@ version of GLFW.

  • Renamed GLFW_BUILD_DLL to _GLFW_BUILD_DLL
  • Renamed version test to glfwinfo
  • Renamed GLFW_NO_GLU to GLFW_INCLUDE_GLU and made it disabled by default
  • +
  • Renamed glfwGetJoystickPos to glfwGetJoystickAxes to match glfwGetJoystickButtons
  • Renamed mouse position functions to cursor position equivalents
  • Replaced glfwOpenWindow and glfwCloseWindow with glfwCreateWindow and glfwDestroyWindow
  • Replaced ad hoc build system with CMake
  • diff --git a/src/cocoa_joystick.m b/src/cocoa_joystick.m index ff4ba958..7eac7f91 100644 --- a/src/cocoa_joystick.m +++ b/src/cocoa_joystick.m @@ -528,7 +528,7 @@ int _glfwPlatformGetJoystickParam(int joy, int param) // Get joystick axis positions //======================================================================== -int _glfwPlatformGetJoystickPos(int joy, float* pos, int numaxes) +int _glfwPlatformGetJoystickAxes(int joy, float* axes, int numaxes) { int i; @@ -556,12 +556,12 @@ int _glfwPlatformGetJoystickPos(int joy, float* pos, int numaxes) long readScale = axes->maxReport - axes->minReport; if (readScale == 0) - pos[i] = axes->value; + axes[i] = axes->value; else - pos[i] = (2.0f * (axes->value - axes->minReport) / readScale) - 1.0f; + axes[i] = (2.0f * (axes->value - axes->minReport) / readScale) - 1.0f; if (i & 1) - pos[i] = -pos[i]; + axes[i] = -axes[i]; } return numaxes; diff --git a/src/internal.h b/src/internal.h index 8fe3b241..c83b0949 100644 --- a/src/internal.h +++ b/src/internal.h @@ -273,7 +273,7 @@ const char* _glfwPlatformGetClipboardString(_GLFWwindow* window); // Joystick input int _glfwPlatformGetJoystickParam(int joy, int param); -int _glfwPlatformGetJoystickPos(int joy, float* pos, int numaxes); +int _glfwPlatformGetJoystickAxes(int joy, float* axes, int numaxes); int _glfwPlatformGetJoystickButtons(int joy, unsigned char* buttons, int numbuttons); // Time input diff --git a/src/joystick.c b/src/joystick.c index d7f98758..84763951 100644 --- a/src/joystick.c +++ b/src/joystick.c @@ -61,7 +61,7 @@ GLFWAPI int glfwGetJoystickParam(int joy, int param) // Get joystick axis positions //======================================================================== -GLFWAPI int glfwGetJoystickPos(int joy, float* pos, int numaxes) +GLFWAPI int glfwGetJoystickAxes(int joy, float* axes, int numaxes) { int i; @@ -77,7 +77,7 @@ GLFWAPI int glfwGetJoystickPos(int joy, float* pos, int numaxes) return 0; } - if (pos == NULL || numaxes < 0) + if (axes == NULL || numaxes < 0) { _glfwSetError(GLFW_INVALID_VALUE, NULL); return 0; @@ -85,9 +85,9 @@ GLFWAPI int glfwGetJoystickPos(int joy, float* pos, int numaxes) // Clear positions for (i = 0; i < numaxes; i++) - pos[i] = 0.0f; + axes[i] = 0.0f; - return _glfwPlatformGetJoystickPos(joy, pos, numaxes); + return _glfwPlatformGetJoystickAxes(joy, axes, numaxes); } diff --git a/src/win32_joystick.c b/src/win32_joystick.c index fcc6b599..a51773d3 100644 --- a/src/win32_joystick.c +++ b/src/win32_joystick.c @@ -116,7 +116,7 @@ int _glfwPlatformGetJoystickParam(int joy, int param) // Get joystick axis positions //======================================================================== -int _glfwPlatformGetJoystickPos(int joy, float* pos, int numaxes) +int _glfwPlatformGetJoystickAxes(int joy, float* axes, int numaxes) { JOYCAPS jc; JOYINFOEX ji; @@ -137,22 +137,22 @@ int _glfwPlatformGetJoystickPos(int joy, float* pos, int numaxes) // Get position values for all axes axis = 0; if (axis < numaxes) - pos[axis++] = calcJoystickPos(ji.dwXpos, jc.wXmin, jc.wXmax); + axes[axis++] = calcJoystickPos(ji.dwXpos, jc.wXmin, jc.wXmax); if (axis < numaxes) - pos[axis++] = -calcJoystickPos(ji.dwYpos, jc.wYmin, jc.wYmax); + axes[axis++] = -calcJoystickPos(ji.dwYpos, jc.wYmin, jc.wYmax); if (axis < numaxes && jc.wCaps & JOYCAPS_HASZ) - pos[axis++] = calcJoystickPos(ji.dwZpos, jc.wZmin, jc.wZmax); + axes[axis++] = calcJoystickPos(ji.dwZpos, jc.wZmin, jc.wZmax); if (axis < numaxes && jc.wCaps & JOYCAPS_HASR) - pos[axis++] = calcJoystickPos(ji.dwRpos, jc.wRmin, jc.wRmax); + axes[axis++] = calcJoystickPos(ji.dwRpos, jc.wRmin, jc.wRmax); if (axis < numaxes && jc.wCaps & JOYCAPS_HASU) - pos[axis++] = calcJoystickPos(ji.dwUpos, jc.wUmin, jc.wUmax); + axes[axis++] = calcJoystickPos(ji.dwUpos, jc.wUmin, jc.wUmax); if (axis < numaxes && jc.wCaps & JOYCAPS_HASV) - pos[axis++] = -calcJoystickPos(ji.dwVpos, jc.wVmin, jc.wVmax); + axes[axis++] = -calcJoystickPos(ji.dwVpos, jc.wVmin, jc.wVmax); return axis; } diff --git a/src/x11_joystick.c b/src/x11_joystick.c index 09885373..a5700c68 100644 --- a/src/x11_joystick.c +++ b/src/x11_joystick.c @@ -260,7 +260,7 @@ int _glfwPlatformGetJoystickParam(int joy, int param) // Get joystick axis positions //======================================================================== -int _glfwPlatformGetJoystickPos(int joy, float* pos, int numAxes) +int _glfwPlatformGetJoystickAxes(int joy, float* axes, int numAxes) { int i; @@ -273,7 +273,7 @@ int _glfwPlatformGetJoystickPos(int joy, float* pos, int numAxes) numAxes = _glfwLibrary.X11.joystick[joy].numAxes; for (i = 0; i < numAxes; i++) - pos[i] = _glfwLibrary.X11.joystick[joy].axis[i]; + axes[i] = _glfwLibrary.X11.joystick[joy].axis[i]; return numAxes; } diff --git a/tests/joysticks.c b/tests/joysticks.c index 488f98ad..40202ce7 100644 --- a/tests/joysticks.c +++ b/tests/joysticks.c @@ -137,7 +137,7 @@ static void refresh_joysticks(void) j->axes = realloc(j->axes, j->axis_count * sizeof(float)); } - glfwGetJoystickPos(GLFW_JOYSTICK_1 + i, j->axes, j->axis_count); + glfwGetJoystickAxes(GLFW_JOYSTICK_1 + i, j->axes, j->axis_count); button_count = glfwGetJoystickParam(GLFW_JOYSTICK_1 + i, GLFW_BUTTONS); if (button_count != j->button_count) From 733cd5372fd932dbf1754dbb9a7f63bb5a3dcc0c Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Thu, 30 Aug 2012 02:17:08 +0200 Subject: [PATCH 10/12] Fixed and separated GLFW version check. --- tests/glfwinfo.c | 49 ++++++++++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/tests/glfwinfo.c b/tests/glfwinfo.c index 11a84612..52d6e0de 100644 --- a/tests/glfwinfo.c +++ b/tests/glfwinfo.c @@ -119,6 +119,32 @@ static void list_extensions(int major, int minor) putchar('\n'); } +static GLboolean valid_version(void) +{ + int major, minor, revision; + + glfwGetVersion(&major, &minor, &revision); + + printf("GLFW header version: %u.%u.%u\n", + GLFW_VERSION_MAJOR, + GLFW_VERSION_MINOR, + GLFW_VERSION_REVISION); + + printf("GLFW library version: %u.%u.%u\n", major, minor, revision); + + if (major != GLFW_VERSION_MAJOR) + { + printf("*** ERROR: GLFW major version mismatch! ***\n"); + return GL_FALSE; + } + + if (minor != GLFW_VERSION_MINOR || revision != GLFW_VERSION_REVISION) + printf("*** WARNING: GLFW version mismatch! ***\n"); + + printf("GLFW library version string: \"%s\"\n", glfwGetVersionString()); + return GL_TRUE; +} + int main(int argc, char** argv) { int ch, profile = 0, strategy = 0, major = 1, minor = 0, revision; @@ -126,6 +152,9 @@ int main(int argc, char** argv) GLint flags, mask; GLFWwindow window; + if (!valid_version()) + exit(EXIT_FAILURE); + while ((ch = getopt(argc, argv, "dfhlm:n:p:r:")) != -1) { switch (ch) @@ -181,26 +210,6 @@ int main(int argc, char** argv) argc -= optind; argv += optind; - // Report GLFW version - - glfwGetVersion(&major, &minor, &revision); - - printf("GLFW header version: %u.%u.%u\n", - GLFW_VERSION_MAJOR, - GLFW_VERSION_MINOR, - GLFW_VERSION_REVISION); - - printf("GLFW library version: %u.%u.%u\n", major, minor, revision); - - if (major != GLFW_VERSION_MAJOR || - minor != GLFW_VERSION_MINOR || - revision != GLFW_VERSION_REVISION) - { - printf("*** WARNING: GLFW version mismatch! ***\n"); - } - - printf("GLFW library version string: \"%s\"\n", glfwGetVersionString()); - // Initialize GLFW and create window glfwSetErrorCallback(error_callback); From 52c27113d3de0f02f130267bd1822403f959f028 Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Sun, 2 Sep 2012 15:21:40 +0200 Subject: [PATCH 11/12] Shortened PC boolean name. --- src/win32_platform.h | 2 +- src/win32_time.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/win32_platform.h b/src/win32_platform.h index 79bbbb94..ba10039e 100644 --- a/src/win32_platform.h +++ b/src/win32_platform.h @@ -191,7 +191,7 @@ typedef struct _GLFWlibraryWin32 // Timer data struct { - GLboolean hasPerformanceCounter; + GLboolean hasPC; double resolution; unsigned int t0_32; __int64 t0_64; diff --git a/src/win32_time.c b/src/win32_time.c index 1e1d57f4..96f7b454 100644 --- a/src/win32_time.c +++ b/src/win32_time.c @@ -45,13 +45,13 @@ void _glfwInitTimer(void) if (QueryPerformanceFrequency((LARGE_INTEGER*) &freq)) { - _glfwLibrary.Win32.timer.hasPerformanceCounter = GL_TRUE; + _glfwLibrary.Win32.timer.hasPC = GL_TRUE; _glfwLibrary.Win32.timer.resolution = 1.0 / (double) freq; QueryPerformanceCounter((LARGE_INTEGER*) &_glfwLibrary.Win32.timer.t0_64); } else { - _glfwLibrary.Win32.timer.hasPerformanceCounter = GL_FALSE; + _glfwLibrary.Win32.timer.hasPC = GL_FALSE; _glfwLibrary.Win32.timer.resolution = 0.001; // winmm resolution is 1 ms _glfwLibrary.Win32.timer.t0_32 = _glfw_timeGetTime(); } @@ -71,7 +71,7 @@ double _glfwPlatformGetTime(void) double t; __int64 t_64; - if (_glfwLibrary.Win32.timer.hasPerformanceCounter) + if (_glfwLibrary.Win32.timer.hasPC) { QueryPerformanceCounter((LARGE_INTEGER*) &t_64); t = (double)(t_64 - _glfwLibrary.Win32.timer.t0_64); @@ -91,7 +91,7 @@ void _glfwPlatformSetTime(double t) { __int64 t_64; - if (_glfwLibrary.Win32.timer.hasPerformanceCounter) + if (_glfwLibrary.Win32.timer.hasPC) { QueryPerformanceCounter((LARGE_INTEGER*) &t_64); _glfwLibrary.Win32.timer.t0_64 = t_64 - (__int64) (t / _glfwLibrary.Win32.timer.resolution); From a339098bc01b246d67ad165bcd2380ce51d666db Mon Sep 17 00:00:00 2001 From: Camilla Berglund Date: Sun, 2 Sep 2012 15:22:56 +0200 Subject: [PATCH 12/12] Formatting. --- include/GL/glfw3.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/include/GL/glfw3.h b/include/GL/glfw3.h index 45f44339..1e90a5bc 100644 --- a/include/GL/glfw3.h +++ b/include/GL/glfw3.h @@ -527,11 +527,11 @@ GLFWAPI void glfwSetGammaRamp(const GLFWgammaramp* ramp); GLFWAPI void glfwWindowHint(int target, int hint); GLFWAPI GLFWwindow glfwCreateWindow(int width, int height, int mode, const char* title, GLFWwindow share); GLFWAPI void glfwDestroyWindow(GLFWwindow window); -GLFWAPI void glfwSetWindowTitle(GLFWwindow, const char* title); -GLFWAPI void glfwGetWindowSize(GLFWwindow, int* width, int* height); -GLFWAPI void glfwSetWindowSize(GLFWwindow, int width, int height); -GLFWAPI void glfwGetWindowPos(GLFWwindow, int* xpos, int* ypos); -GLFWAPI void glfwSetWindowPos(GLFWwindow, int xpos, int ypos); +GLFWAPI void glfwSetWindowTitle(GLFWwindow window, const char* title); +GLFWAPI void glfwGetWindowSize(GLFWwindow window, int* width, int* height); +GLFWAPI void glfwSetWindowSize(GLFWwindow window, int width, int height); +GLFWAPI void glfwGetWindowPos(GLFWwindow window, int* xpos, int* ypos); +GLFWAPI void glfwSetWindowPos(GLFWwindow window, int xpos, int ypos); GLFWAPI void glfwIconifyWindow(GLFWwindow window); GLFWAPI void glfwRestoreWindow(GLFWwindow window); GLFWAPI int glfwGetWindowParam(GLFWwindow window, int param);