summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorÉloi Rivard <azmeuk@gmail.com>2013-03-04 13:11:44 +0100
committerÉloi Rivard <azmeuk@gmail.com>2013-04-04 11:17:37 +0200
commit94b3f0a6927efda744a5b2e1222645bcd4dd6ee9 (patch)
tree76e011038f3347f7ca66ae07ef3d930e555cf630
parentd78dcc9b74ce35842b1dbe0bc31d087fb133f32f (diff)
* Added glfw headers and testcases.
-rw-r--r--src/library_glfw.js1
-rw-r--r--tests/glfw/Makefile56
-rw-r--r--tests/glfw/boing.c615
-rw-r--r--tests/glfw/bundle.sh46
-rw-r--r--tests/glfw/gears.c373
-rw-r--r--tests/glfw/getopt.c253
-rw-r--r--tests/glfw/getopt.h63
-rw-r--r--tests/glfw/heightmap.c850
-rw-r--r--tests/glfw/listmodes.c48
-rw-r--r--tests/glfw/mipmaps.c122
-rw-r--r--tests/glfw/mipmaps.tgabin0 -> 66322 bytes
-rw-r--r--tests/glfw/mtbench.c301
-rw-r--r--tests/glfw/mthello.c48
-rw-r--r--tests/glfw/particles.c1152
-rw-r--r--tests/glfw/pong3d.c854
-rw-r--r--tests/glfw/pong3d_field.tgabin0 -> 17816 bytes
-rw-r--r--tests/glfw/pong3d_instr.tgabin0 -> 21279 bytes
-rw-r--r--tests/glfw/pong3d_menu.tgabin0 -> 1835 bytes
-rw-r--r--tests/glfw/pong3d_title.tgabin0 -> 106516 bytes
-rw-r--r--tests/glfw/pong3d_winner1.tgabin0 -> 861 bytes
-rw-r--r--tests/glfw/pong3d_winner2.tgabin0 -> 891 bytes
-rw-r--r--tests/glfw/splitview.c514
-rw-r--r--tests/glfw/triangle.c94
-rw-r--r--tests/glfw/wave.c399
24 files changed, 5789 insertions, 0 deletions
diff --git a/src/library_glfw.js b/src/library_glfw.js
index 20c75a29..e197efd2 100644
--- a/src/library_glfw.js
+++ b/src/library_glfw.js
@@ -573,3 +573,4 @@ var LibraryGLFW = {
autoAddDeps(LibraryGLFW, '$GLFW');
mergeInto(LibraryManager.library, LibraryGLFW);
+
diff --git a/tests/glfw/Makefile b/tests/glfw/Makefile
new file mode 100644
index 00000000..89138d74
--- /dev/null
+++ b/tests/glfw/Makefile
@@ -0,0 +1,56 @@
+##########################################################################
+# Makefile for GLFW example programs on X11 (generated by compile.sh)
+##########################################################################
+CC = emcc
+CFLAGS = -I../include
+
+LIB = -lglfw
+SOLIB =
+LFLAGS = $(LIB)
+SO_LFLAGS = $(SOLIB)
+
+BINARIES = triangle.js listmodes.js mthello.js pong3d.js mtbench.js particles.js splitview.js \
+ mipmaps.js gears.js boing.js heightmap.js
+## wave
+
+all: $(BINARIES)
+
+triangle.js: triangle.c
+ $(CC) $(CFLAGS) triangle.c $(LFLAGS) -o $@
+
+listmodes.js: listmodes.c
+ $(CC) $(CFLAGS) listmodes.c $(LFLAGS) -o $@
+
+mthello.js: mthello.c
+ $(CC) $(CFLAGS) mthello.c $(LFLAGS) -o $@
+
+pong3d.js: pong3d.c
+ $(CC) $(CFLAGS) pong3d.c $(LFLAGS) -o $@
+
+mtbench.js: mtbench.c
+ $(CC) $(CFLAGS) mtbench.c $(LFLAGS) -o $@
+
+particles.js: particles.c
+ $(CC) $(CFLAGS) particles.c $(LFLAGS) -o $@
+
+splitview.js: splitview.c
+ $(CC) $(CFLAGS) splitview.c $(LFLAGS) -o $@
+
+mipmaps.js: mipmaps.c
+ $(CC) $(CFLAGS) mipmaps.c $(LFLAGS) -o $@
+
+gears.js: gears.c
+ $(CC) $(CFLAGS) gears.c $(LFLAGS) -o $@
+
+boing.js: boing.c
+ $(CC) $(CFLAGS) boing.c $(LFLAGS) -o $@
+
+wave.js: wave.c
+ $(CC) $(CFLAGS) wave.c $(LFLAGS) -o $@
+
+heightmap.js: heightmap.c
+ $(CC) $(CFLAGS) heightmap.c $(LFLAGS) -o $@
+
+clean:
+ rm -f $(BINARIES)
+
diff --git a/tests/glfw/boing.c b/tests/glfw/boing.c
new file mode 100644
index 00000000..08544d1a
--- /dev/null
+++ b/tests/glfw/boing.c
@@ -0,0 +1,615 @@
+/*****************************************************************************
+ * Title: GLBoing
+ * Desc: Tribute to Amiga Boing.
+ * Author: Jim Brooks <gfx@jimbrooks.org>
+ * Original Amiga authors were R.J. Mical and Dale Luck.
+ * GLFW conversion by Marcus Geelnard
+ * Notes: - 360' = 2*PI [radian]
+ *
+ * - Distances between objects are created by doing a relative
+ * Z translations.
+ *
+ * - Although OpenGL enticingly supports alpha-blending,
+ * the shadow of the original Boing didn't affect the color
+ * of the grid.
+ *
+ * - [Marcus] Changed timing scheme from interval driven to frame-
+ * time based animation steps (which results in much smoother
+ * movement)
+ *
+ * History of Amiga Boing:
+ *
+ * Boing was demonstrated on the prototype Amiga (codenamed "Lorraine") in
+ * 1985. According to legend, it was written ad-hoc in one night by
+ * R. J. Mical and Dale Luck. Because the bouncing ball animation was so fast
+ * and smooth, attendees did not believe the Amiga prototype was really doing
+ * the rendering. Suspecting a trick, they began looking around the booth for
+ * a hidden computer or VCR.
+ *****************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <GL/glfw.h>
+
+
+/*****************************************************************************
+ * Various declarations and macros
+ *****************************************************************************/
+
+/* Prototypes */
+void init( void );
+void display( void );
+void GLFWCALL reshape( int w, int h );
+void DrawBoingBall( void );
+void BounceBall( double dt );
+void DrawBoingBallBand( GLfloat long_lo, GLfloat long_hi );
+void DrawGrid( void );
+
+#define RADIUS 70.f
+#define STEP_LONGITUDE 22.5f /* 22.5 makes 8 bands like original Boing */
+#define STEP_LATITUDE 22.5f
+
+#define DIST_BALL (RADIUS * 2.f + RADIUS * 0.1f)
+
+#define VIEW_SCENE_DIST (DIST_BALL * 3.f + 200.f)/* distance from viewer to middle of boing area */
+#define GRID_SIZE (RADIUS * 4.5f) /* length (width) of grid */
+#define BOUNCE_HEIGHT (RADIUS * 2.1f)
+#define BOUNCE_WIDTH (RADIUS * 2.1f)
+
+#define SHADOW_OFFSET_X -20.f
+#define SHADOW_OFFSET_Y 10.f
+#define SHADOW_OFFSET_Z 0.f
+
+#define WALL_L_OFFSET 0.f
+#define WALL_R_OFFSET 5.f
+
+/* Animation speed (50.0 mimics the original GLUT demo speed) */
+#define ANIMATION_SPEED 50.f
+
+/* Maximum allowed delta time per physics iteration */
+#define MAX_DELTA_T 0.02f
+
+/* Draw ball, or its shadow */
+typedef enum { DRAW_BALL, DRAW_BALL_SHADOW } DRAW_BALL_ENUM;
+
+/* Vertex type */
+typedef struct {float x; float y; float z;} vertex_t;
+
+/* Global vars */
+GLfloat deg_rot_y = 0.f;
+GLfloat deg_rot_y_inc = 2.f;
+GLfloat ball_x = -RADIUS;
+GLfloat ball_y = -RADIUS;
+GLfloat ball_x_inc = 1.f;
+GLfloat ball_y_inc = 2.f;
+DRAW_BALL_ENUM drawBallHow;
+double t;
+double t_old = 0.f;
+double dt;
+
+/* Random number generator */
+#ifndef RAND_MAX
+ #define RAND_MAX 4095
+#endif
+
+/* PI */
+#ifndef M_PI
+ #define M_PI 3.1415926535897932384626433832795
+#endif
+
+
+/*****************************************************************************
+ * Truncate a degree.
+ *****************************************************************************/
+GLfloat TruncateDeg( GLfloat deg )
+{
+ if ( deg >= 360.f )
+ return (deg - 360.f);
+ else
+ return deg;
+}
+
+/*****************************************************************************
+ * Convert a degree (360-based) into a radian.
+ * 360' = 2 * PI
+ *****************************************************************************/
+double deg2rad( double deg )
+{
+ return deg / 360 * (2 * M_PI);
+}
+
+/*****************************************************************************
+ * 360' sin().
+ *****************************************************************************/
+double sin_deg( double deg )
+{
+ return sin( deg2rad( deg ) );
+}
+
+/*****************************************************************************
+ * 360' cos().
+ *****************************************************************************/
+double cos_deg( double deg )
+{
+ return cos( deg2rad( deg ) );
+}
+
+/*****************************************************************************
+ * Compute a cross product (for a normal vector).
+ *
+ * c = a x b
+ *****************************************************************************/
+void CrossProduct( vertex_t a, vertex_t b, vertex_t c, vertex_t *n )
+{
+ GLfloat u1, u2, u3;
+ GLfloat v1, v2, v3;
+
+ u1 = b.x - a.x;
+ u2 = b.y - a.y;
+ u3 = b.y - a.z;
+
+ v1 = c.x - a.x;
+ v2 = c.y - a.y;
+ v3 = c.z - a.z;
+
+ n->x = u2 * v3 - v2 * v3;
+ n->y = u3 * v1 - v3 * u1;
+ n->z = u1 * v2 - v1 * u2;
+}
+
+/*****************************************************************************
+ * Calculate the angle to be passed to gluPerspective() so that a scene
+ * is visible. This function originates from the OpenGL Red Book.
+ *
+ * Parms : size
+ * The size of the segment when the angle is intersected at "dist"
+ * (ie at the outermost edge of the angle of vision).
+ *
+ * dist
+ * Distance from viewpoint to scene.
+ *****************************************************************************/
+GLfloat PerspectiveAngle( GLfloat size,
+ GLfloat dist )
+{
+ GLfloat radTheta, degTheta;
+
+ radTheta = 2.f * (GLfloat) atan2( size / 2.f, dist );
+ degTheta = (180.f * radTheta) / (GLfloat) M_PI;
+ return degTheta;
+}
+
+
+
+#define BOING_DEBUG 0
+
+
+/*****************************************************************************
+ * init()
+ *****************************************************************************/
+void init( void )
+{
+ /*
+ * Clear background.
+ */
+ glClearColor( 0.55f, 0.55f, 0.55f, 0.f );
+
+ glShadeModel( GL_FLAT );
+}
+
+
+/*****************************************************************************
+ * display()
+ *****************************************************************************/
+void display(void)
+{
+ glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
+ glPushMatrix();
+
+ drawBallHow = DRAW_BALL_SHADOW;
+ DrawBoingBall();
+
+ DrawGrid();
+
+ drawBallHow = DRAW_BALL;
+ DrawBoingBall();
+
+ glPopMatrix();
+ glFlush();
+}
+
+
+/*****************************************************************************
+ * reshape()
+ *****************************************************************************/
+void GLFWCALL reshape( int w, int h )
+{
+ glViewport( 0, 0, (GLsizei)w, (GLsizei)h );
+
+ glMatrixMode( GL_PROJECTION );
+ glLoadIdentity();
+
+ gluPerspective( PerspectiveAngle( RADIUS * 2, 200 ),
+ (GLfloat)w / (GLfloat)h,
+ 1.0,
+ VIEW_SCENE_DIST );
+
+ glMatrixMode( GL_MODELVIEW );
+ glLoadIdentity();
+
+ gluLookAt( 0.0, 0.0, VIEW_SCENE_DIST,/* eye */
+ 0.0, 0.0, 0.0, /* center of vision */
+ 0.0, -1.0, 0.0 ); /* up vector */
+}
+
+
+/*****************************************************************************
+ * Draw the Boing ball.
+ *
+ * The Boing ball is sphere in which each facet is a rectangle.
+ * Facet colors alternate between red and white.
+ * The ball is built by stacking latitudinal circles. Each circle is composed
+ * of a widely-separated set of points, so that each facet is noticably large.
+ *****************************************************************************/
+void DrawBoingBall( void )
+{
+ GLfloat lon_deg; /* degree of longitude */
+ double dt_total, dt2;
+
+ glPushMatrix();
+ glMatrixMode( GL_MODELVIEW );
+
+ /*
+ * Another relative Z translation to separate objects.
+ */
+ glTranslatef( 0.0, 0.0, DIST_BALL );
+
+ /* Update ball position and rotation (iterate if necessary) */
+ dt_total = dt;
+ while( dt_total > 0.0 )
+ {
+ dt2 = dt_total > MAX_DELTA_T ? MAX_DELTA_T : dt_total;
+ dt_total -= dt2;
+ BounceBall( dt2 );
+ deg_rot_y = TruncateDeg( deg_rot_y + deg_rot_y_inc*((float)dt2*ANIMATION_SPEED) );
+ }
+
+ /* Set ball position */
+ glTranslatef( ball_x, ball_y, 0.0 );
+
+ /*
+ * Offset the shadow.
+ */
+ if ( drawBallHow == DRAW_BALL_SHADOW )
+ {
+ glTranslatef( SHADOW_OFFSET_X,
+ SHADOW_OFFSET_Y,
+ SHADOW_OFFSET_Z );
+ }
+
+ /*
+ * Tilt the ball.
+ */
+ glRotatef( -20.0, 0.0, 0.0, 1.0 );
+
+ /*
+ * Continually rotate ball around Y axis.
+ */
+ glRotatef( deg_rot_y, 0.0, 1.0, 0.0 );
+
+ /*
+ * Set OpenGL state for Boing ball.
+ */
+ glCullFace( GL_FRONT );
+ glEnable( GL_CULL_FACE );
+ glEnable( GL_NORMALIZE );
+
+ /*
+ * Build a faceted latitude slice of the Boing ball,
+ * stepping same-sized vertical bands of the sphere.
+ */
+ for ( lon_deg = 0;
+ lon_deg < 180;
+ lon_deg += STEP_LONGITUDE )
+ {
+ /*
+ * Draw a latitude circle at this longitude.
+ */
+ DrawBoingBallBand( lon_deg,
+ lon_deg + STEP_LONGITUDE );
+ }
+
+ glPopMatrix();
+
+ return;
+}
+
+
+/*****************************************************************************
+ * Bounce the ball.
+ *****************************************************************************/
+void BounceBall( double dt )
+{
+ GLfloat sign;
+ GLfloat deg;
+
+ /* Bounce on walls */
+ if ( ball_x > (BOUNCE_WIDTH/2 + WALL_R_OFFSET ) )
+ {
+ ball_x_inc = -0.5f - 0.75f * (GLfloat)rand() / (GLfloat)RAND_MAX;
+ deg_rot_y_inc = -deg_rot_y_inc;
+ }
+ if ( ball_x < -(BOUNCE_HEIGHT/2 + WALL_L_OFFSET) )
+ {
+ ball_x_inc = 0.5f + 0.75f * (GLfloat)rand() / (GLfloat)RAND_MAX;
+ deg_rot_y_inc = -deg_rot_y_inc;
+ }
+
+ /* Bounce on floor / roof */
+ if ( ball_y > BOUNCE_HEIGHT/2 )
+ {
+ ball_y_inc = -0.75f - 1.f * (GLfloat)rand() / (GLfloat)RAND_MAX;
+ }
+ if ( ball_y < -BOUNCE_HEIGHT/2*0.85 )
+ {
+ ball_y_inc = 0.75f + 1.f * (GLfloat)rand() / (GLfloat)RAND_MAX;
+ }
+
+ /* Update ball position */
+ ball_x += ball_x_inc * ((float)dt*ANIMATION_SPEED);
+ ball_y += ball_y_inc * ((float)dt*ANIMATION_SPEED);
+
+ /*
+ * Simulate the effects of gravity on Y movement.
+ */
+ if ( ball_y_inc < 0 ) sign = -1.0; else sign = 1.0;
+
+ deg = (ball_y + BOUNCE_HEIGHT/2) * 90 / BOUNCE_HEIGHT;
+ if ( deg > 80 ) deg = 80;
+ if ( deg < 10 ) deg = 10;
+
+ ball_y_inc = sign * 4.f * (float) sin_deg( deg );
+}
+
+
+/*****************************************************************************
+ * Draw a faceted latitude band of the Boing ball.
+ *
+ * Parms: long_lo, long_hi
+ * Low and high longitudes of slice, resp.
+ *****************************************************************************/
+void DrawBoingBallBand( GLfloat long_lo,
+ GLfloat long_hi )
+{
+ vertex_t vert_ne; /* "ne" means south-east, so on */
+ vertex_t vert_nw;
+ vertex_t vert_sw;
+ vertex_t vert_se;
+ vertex_t vert_norm;
+ GLfloat lat_deg;
+ static int colorToggle = 0;
+
+ /*
+ * Iterate thru the points of a latitude circle.
+ * A latitude circle is a 2D set of X,Z points.
+ */
+ for ( lat_deg = 0;
+ lat_deg <= (360 - STEP_LATITUDE);
+ lat_deg += STEP_LATITUDE )
+ {
+ /*
+ * Color this polygon with red or white.
+ */
+ if ( colorToggle )
+ glColor3f( 0.8f, 0.1f, 0.1f );
+ else
+ glColor3f( 0.95f, 0.95f, 0.95f );
+#if 0
+ if ( lat_deg >= 180 )
+ if ( colorToggle )
+ glColor3f( 0.1f, 0.8f, 0.1f );
+ else
+ glColor3f( 0.5f, 0.5f, 0.95f );
+#endif
+ colorToggle = ! colorToggle;
+
+ /*
+ * Change color if drawing shadow.
+ */
+ if ( drawBallHow == DRAW_BALL_SHADOW )
+ glColor3f( 0.35f, 0.35f, 0.35f );
+
+ /*
+ * Assign each Y.
+ */
+ vert_ne.y = vert_nw.y = (float) cos_deg(long_hi) * RADIUS;
+ vert_sw.y = vert_se.y = (float) cos_deg(long_lo) * RADIUS;
+
+ /*
+ * Assign each X,Z with sin,cos values scaled by latitude radius indexed by longitude.
+ * Eg, long=0 and long=180 are at the poles, so zero scale is sin(longitude),
+ * while long=90 (sin(90)=1) is at equator.
+ */
+ vert_ne.x = (float) cos_deg( lat_deg ) * (RADIUS * (float) sin_deg( long_lo + STEP_LONGITUDE ));
+ vert_se.x = (float) cos_deg( lat_deg ) * (RADIUS * (float) sin_deg( long_lo ));
+ vert_nw.x = (float) cos_deg( lat_deg + STEP_LATITUDE ) * (RADIUS * (float) sin_deg( long_lo + STEP_LONGITUDE ));
+ vert_sw.x = (float) cos_deg( lat_deg + STEP_LATITUDE ) * (RADIUS * (float) sin_deg( long_lo ));
+
+ vert_ne.z = (float) sin_deg( lat_deg ) * (RADIUS * (float) sin_deg( long_lo + STEP_LONGITUDE ));
+ vert_se.z = (float) sin_deg( lat_deg ) * (RADIUS * (float) sin_deg( long_lo ));
+ vert_nw.z = (float) sin_deg( lat_deg + STEP_LATITUDE ) * (RADIUS * (float) sin_deg( long_lo + STEP_LONGITUDE ));
+ vert_sw.z = (float) sin_deg( lat_deg + STEP_LATITUDE ) * (RADIUS * (float) sin_deg( long_lo ));
+
+ /*
+ * Draw the facet.
+ */
+ glBegin( GL_POLYGON );
+
+ CrossProduct( vert_ne, vert_nw, vert_sw, &vert_norm );
+ glNormal3f( vert_norm.x, vert_norm.y, vert_norm.z );
+
+ glVertex3f( vert_ne.x, vert_ne.y, vert_ne.z );
+ glVertex3f( vert_nw.x, vert_nw.y, vert_nw.z );
+ glVertex3f( vert_sw.x, vert_sw.y, vert_sw.z );
+ glVertex3f( vert_se.x, vert_se.y, vert_se.z );
+
+ glEnd();
+
+#if BOING_DEBUG
+ printf( "----------------------------------------------------------- \n" );
+ printf( "lat = %f long_lo = %f long_hi = %f \n", lat_deg, long_lo, long_hi );
+ printf( "vert_ne x = %.8f y = %.8f z = %.8f \n", vert_ne.x, vert_ne.y, vert_ne.z );
+ printf( "vert_nw x = %.8f y = %.8f z = %.8f \n", vert_nw.x, vert_nw.y, vert_nw.z );
+ printf( "vert_se x = %.8f y = %.8f z = %.8f \n", vert_se.x, vert_se.y, vert_se.z );
+ printf( "vert_sw x = %.8f y = %.8f z = %.8f \n", vert_sw.x, vert_sw.y, vert_sw.z );
+#endif
+
+ }
+
+ /*
+ * Toggle color so that next band will opposite red/white colors than this one.
+ */
+ colorToggle = ! colorToggle;
+
+ /*
+ * This circular band is done.
+ */
+ return;
+}
+
+
+/*****************************************************************************
+ * Draw the purple grid of lines, behind the Boing ball.
+ * When the Workbench is dropped to the bottom, Boing shows 12 rows.
+ *****************************************************************************/
+void DrawGrid( void )
+{
+ int row, col;
+ const int rowTotal = 12; /* must be divisible by 2 */
+ const int colTotal = rowTotal; /* must be same as rowTotal */
+ const GLfloat widthLine = 2.0; /* should be divisible by 2 */
+ const GLfloat sizeCell = GRID_SIZE / rowTotal;
+ const GLfloat z_offset = -40.0;
+ GLfloat xl, xr;
+ GLfloat yt, yb;
+
+ glPushMatrix();
+ glDisable( GL_CULL_FACE );
+
+ /*
+ * Another relative Z translation to separate objects.
+ */
+ glTranslatef( 0.0, 0.0, DIST_BALL );
+
+ /*
+ * Draw vertical lines (as skinny 3D rectangles).
+ */
+ for ( col = 0; col <= colTotal; col++ )
+ {
+ /*
+ * Compute co-ords of line.
+ */
+ xl = -GRID_SIZE / 2 + col * sizeCell;
+ xr = xl + widthLine;
+
+ yt = GRID_SIZE / 2;
+ yb = -GRID_SIZE / 2 - widthLine;
+
+ glBegin( GL_POLYGON );
+
+ glColor3f( 0.6f, 0.1f, 0.6f ); /* purple */
+
+ glVertex3f( xr, yt, z_offset ); /* NE */
+ glVertex3f( xl, yt, z_offset ); /* NW */
+ glVertex3f( xl, yb, z_offset ); /* SW */
+ glVertex3f( xr, yb, z_offset ); /* SE */
+
+ glEnd();
+ }
+
+ /*
+ * Draw horizontal lines (as skinny 3D rectangles).
+ */
+ for ( row = 0; row <= rowTotal; row++ )
+ {
+ /*
+ * Compute co-ords of line.
+ */
+ yt = GRID_SIZE / 2 - row * sizeCell;
+ yb = yt - widthLine;
+
+ xl = -GRID_SIZE / 2;
+ xr = GRID_SIZE / 2 + widthLine;
+
+ glBegin( GL_POLYGON );
+
+ glColor3f( 0.6f, 0.1f, 0.6f ); /* purple */
+
+ glVertex3f( xr, yt, z_offset ); /* NE */
+ glVertex3f( xl, yt, z_offset ); /* NW */
+ glVertex3f( xl, yb, z_offset ); /* SW */
+ glVertex3f( xr, yb, z_offset ); /* SE */
+
+ glEnd();
+ }
+
+ glPopMatrix();
+
+ return;
+}
+
+
+/*======================================================================*
+ * main()
+ *======================================================================*/
+
+int main( void )
+{
+ int running;
+
+ /* Init GLFW */
+ if( !glfwInit() )
+ {
+ fprintf( stderr, "Failed to initialize GLFW\n" );
+ exit( EXIT_FAILURE );
+ }
+
+ if( !glfwOpenWindow( 400,400, 0,0,0,0, 16,0, GLFW_WINDOW ) )
+ {
+ fprintf( stderr, "Failed to open GLFW window\n" );
+ glfwTerminate();
+ exit( EXIT_FAILURE );
+ }
+
+ glfwSetWindowTitle( "Boing (classic Amiga demo)" );
+ glfwSetWindowSizeCallback( reshape );
+ glfwEnable( GLFW_STICKY_KEYS );
+ glfwSwapInterval( 1 );
+ glfwSetTime( 0.0 );
+
+ init();
+
+ /* Main loop */
+ do
+ {
+ /* Timing */
+ t = glfwGetTime();
+ dt = t - t_old;
+ t_old = t;
+
+ /* Draw one frame */
+ display();
+
+ /* Swap buffers */
+ glfwSwapBuffers();
+
+ /* Check if we are still running */
+ running = !glfwGetKey( GLFW_KEY_ESC ) &&
+ glfwGetWindowParam( GLFW_OPENED );
+ }
+ while( running );
+
+ glfwTerminate();
+ exit( EXIT_SUCCESS );
+}
+
diff --git a/tests/glfw/bundle.sh b/tests/glfw/bundle.sh
new file mode 100644
index 00000000..ee4d18dd
--- /dev/null
+++ b/tests/glfw/bundle.sh
@@ -0,0 +1,46 @@
+#!/bin/sh
+
+# Creates application bundles for use on Mac OS X.
+
+if [ -z "$1" ]; then
+ echo "usage: `basename $0` BUNDLE-NAME"
+ exit 1
+fi
+
+bundle_name="$1"
+
+if [ ! -d "${bundle_name}.app/Contents/MacOS" ]; then
+ mkdir -p "${bundle_name}.app/Contents/MacOS"
+fi
+
+if [ ! -d "${bundle_name}.app/Contents/Resources" ]; then
+ mkdir -p "${bundle_name}.app/Contents/Resources"
+fi
+
+if [ ! -f "${bundle_name}.app/Contents/PkgInfo" ]; then
+ echo -n "APPL????" > "${bundle_name}.app/Contents/PkgInfo"
+fi
+
+if [ ! -f "${bundle_name}.app/Contents/Info.plist" ]; then
+ cat > "${bundle_name}.app/Contents/Info.plist" <<EOF
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>${bundle_name}</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>0.1</string>
+</dict>
+</plist>
+EOF
+fi
+
diff --git a/tests/glfw/gears.c b/tests/glfw/gears.c
new file mode 100644
index 00000000..d9efdf9a
--- /dev/null
+++ b/tests/glfw/gears.c
@@ -0,0 +1,373 @@
+/*
+ * 3-D gear wheels. This program is in the public domain.
+ *
+ * Command line options:
+ * -info print GL implementation information
+ * -exit automatically exit after 30 seconds
+ *
+ *
+ * Brian Paul
+ *
+ *
+ * Marcus Geelnard:
+ * - Conversion to GLFW
+ * - Time based rendering (frame rate independent)
+ * - Slightly modified camera that should work better for stereo viewing
+ *
+ *
+ * Camilla Berglund:
+ * - Removed FPS counter (this is not a benchmark)
+ * - Added a few comments
+ * - Enabled vsync
+ */
+
+
+#include <math.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <GL/glfw.h>
+
+#ifndef M_PI
+#define M_PI 3.141592654
+#endif
+
+/* The program exits when this is zero.
+ */
+static int running = 1;
+
+/* If non-zero, the program exits after that many seconds
+ */
+static int autoexit = 0;
+
+/**
+
+ Draw a gear wheel. You'll probably want to call this function when
+ building a display list since we do a lot of trig here.
+
+ Input: inner_radius - radius of hole at center
+ outer_radius - radius at center of teeth
+ width - width of gear teeth - number of teeth
+ tooth_depth - depth of tooth
+
+ **/
+
+static void
+gear(GLfloat inner_radius, GLfloat outer_radius, GLfloat width,
+ GLint teeth, GLfloat tooth_depth)
+{
+ GLint i;
+ GLfloat r0, r1, r2;
+ GLfloat angle, da;
+ GLfloat u, v, len;
+
+ r0 = inner_radius;
+ r1 = outer_radius - tooth_depth / 2.f;
+ r2 = outer_radius + tooth_depth / 2.f;
+
+ da = 2.f * (float) M_PI / teeth / 4.f;
+
+ glShadeModel(GL_FLAT);
+
+ glNormal3f(0.f, 0.f, 1.f);
+
+ /* draw front face */
+ glBegin(GL_QUAD_STRIP);
+ for (i = 0; i <= teeth; i++) {
+ angle = i * 2.f * (float) M_PI / teeth;
+ glVertex3f(r0 * (float) cos(angle), r0 * (float) sin(angle), width * 0.5f);
+ glVertex3f(r1 * (float) cos(angle), r1 * (float) sin(angle), width * 0.5f);
+ if (i < teeth) {
+ glVertex3f(r0 * (float) cos(angle), r0 * (float) sin(angle), width * 0.5f);
+ glVertex3f(r1 * (float) cos(angle + 3 * da), r1 * (float) sin(angle + 3 * da), width * 0.5f);
+ }
+ }
+ glEnd();
+
+ /* draw front sides of teeth */
+ glBegin(GL_QUADS);
+ da = 2.f * (float) M_PI / teeth / 4.f;
+ for (i = 0; i < teeth; i++) {
+ angle = i * 2.f * (float) M_PI / teeth;
+
+ glVertex3f(r1 * (float) cos(angle), r1 * (float) sin(angle), width * 0.5f);
+ glVertex3f(r2 * (float) cos(angle + da), r2 * (float) sin(angle + da), width * 0.5f);
+ glVertex3f(r2 * (float) cos(angle + 2 * da), r2 * (float) sin(angle + 2 * da), width * 0.5f);
+ glVertex3f(r1 * (float) cos(angle + 3 * da), r1 * (float) sin(angle + 3 * da), width * 0.5f);
+ }
+ glEnd();
+
+ glNormal3f(0.0, 0.0, -1.0);
+
+ /* draw back face */
+ glBegin(GL_QUAD_STRIP);
+ for (i = 0; i <= teeth; i++) {
+ angle = i * 2.f * (float) M_PI / teeth;
+ glVertex3f(r1 * (float) cos(angle), r1 * (float) sin(angle), -width * 0.5f);
+ glVertex3f(r0 * (float) cos(angle), r0 * (float) sin(angle), -width * 0.5f);
+ if (i < teeth) {
+ glVertex3f(r1 * (float) cos(angle + 3 * da), r1 * (float) sin(angle + 3 * da), -width * 0.5f);
+ glVertex3f(r0 * (float) cos(angle), r0 * (float) sin(angle), -width * 0.5f);
+ }
+ }
+ glEnd();
+
+ /* draw back sides of teeth */
+ glBegin(GL_QUADS);
+ da = 2.f * (float) M_PI / teeth / 4.f;
+ for (i = 0; i < teeth; i++) {
+ angle = i * 2.f * (float) M_PI / teeth;
+
+ glVertex3f(r1 * (float) cos(angle + 3 * da), r1 * (float) sin(angle + 3 * da), -width * 0.5f);
+ glVertex3f(r2 * (float) cos(angle + 2 * da), r2 * (float) sin(angle + 2 * da), -width * 0.5f);
+ glVertex3f(r2 * (float) cos(angle + da), r2 * (float) sin(angle + da), -width * 0.5f);
+ glVertex3f(r1 * (float) cos(angle), r1 * (float) sin(angle), -width * 0.5f);
+ }
+ glEnd();
+
+ /* draw outward faces of teeth */
+ glBegin(GL_QUAD_STRIP);
+ for (i = 0; i < teeth; i++) {
+ angle = i * 2.f * (float) M_PI / teeth;
+
+ glVertex3f(r1 * (float) cos(angle), r1 * (float) sin(angle), width * 0.5f);
+ glVertex3f(r1 * (float) cos(angle), r1 * (float) sin(angle), -width * 0.5f);
+ u = r2 * (float) cos(angle + da) - r1 * (float) cos(angle);
+ v = r2 * (float) sin(angle + da) - r1 * (float) sin(angle);
+ len = (float) sqrt(u * u + v * v);
+ u /= len;
+ v /= len;
+ glNormal3f(v, -u, 0.0);
+ glVertex3f(r2 * (float) cos(angle + da), r2 * (float) sin(angle + da), width * 0.5f);
+ glVertex3f(r2 * (float) cos(angle + da), r2 * (float) sin(angle + da), -width * 0.5f);
+ glNormal3f((float) cos(angle), (float) sin(angle), 0.f);
+ glVertex3f(r2 * (float) cos(angle + 2 * da), r2 * (float) sin(angle + 2 * da), width * 0.5f);
+ glVertex3f(r2 * (float) cos(angle + 2 * da), r2 * (float) sin(angle + 2 * da), -width * 0.5f);
+ u = r1 * (float) cos(angle + 3 * da) - r2 * (float) cos(angle + 2 * da);
+ v = r1 * (float) sin(angle + 3 * da) - r2 * (float) sin(angle + 2 * da);
+ glNormal3f(v, -u, 0.f);
+ glVertex3f(r1 * (float) cos(angle + 3 * da), r1 * (float) sin(angle + 3 * da), width * 0.5f);
+ glVertex3f(r1 * (float) cos(angle + 3 * da), r1 * (float) sin(angle + 3 * da), -width * 0.5f);
+ glNormal3f((float) cos(angle), (float) sin(angle), 0.f);
+ }
+
+ glVertex3f(r1 * (float) cos(0), r1 * (float) sin(0), width * 0.5f);
+ glVertex3f(r1 * (float) cos(0), r1 * (float) sin(0), -width * 0.5f);
+
+ glEnd();
+
+ glShadeModel(GL_SMOOTH);
+
+ /* draw inside radius cylinder */
+ glBegin(GL_QUAD_STRIP);
+ for (i = 0; i <= teeth; i++) {
+ angle = i * 2.f * (float) M_PI / teeth;
+ glNormal3f(-(float) cos(angle), -(float) sin(angle), 0.f);
+ glVertex3f(r0 * (float) cos(angle), r0 * (float) sin(angle), -width * 0.5f);
+ glVertex3f(r0 * (float) cos(angle), r0 * (float) sin(angle), width * 0.5f);
+ }
+ glEnd();
+
+}
+
+
+static GLfloat view_rotx = 20.f, view_roty = 30.f, view_rotz = 0.f;
+static GLint gear1, gear2, gear3;
+static GLfloat angle = 0.f;
+
+/* OpenGL draw function & timing */
+static void draw(void)
+{
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ glPushMatrix();
+ glRotatef(view_rotx, 1.0, 0.0, 0.0);
+ glRotatef(view_roty, 0.0, 1.0, 0.0);
+ glRotatef(view_rotz, 0.0, 0.0, 1.0);
+
+ glPushMatrix();
+ glTranslatef(-3.0, -2.0, 0.0);
+ glRotatef(angle, 0.0, 0.0, 1.0);
+ glCallList(gear1);
+ glPopMatrix();
+
+ glPushMatrix();
+ glTranslatef(3.1f, -2.f, 0.f);
+ glRotatef(-2.f * angle - 9.f, 0.f, 0.f, 1.f);
+ glCallList(gear2);
+ glPopMatrix();
+
+ glPushMatrix();
+ glTranslatef(-3.1f, 4.2f, 0.f);
+ glRotatef(-2.f * angle - 25.f, 0.f, 0.f, 1.f);
+ glCallList(gear3);
+ glPopMatrix();
+
+ glPopMatrix();
+}
+
+
+/* update animation parameters */
+static void animate(void)
+{
+ angle = 100.f * (float) glfwGetTime();
+}
+
+
+/* change view angle, exit upon ESC */
+void GLFWCALL key( int k, int action )
+{
+ if( action != GLFW_PRESS ) return;
+
+ switch (k) {
+ case 'Z':
+ if( glfwGetKey( GLFW_KEY_LSHIFT ) )
+ view_rotz -= 5.0;
+ else
+ view_rotz += 5.0;
+ break;
+ case GLFW_KEY_ESC:
+ running = 0;
+ break;
+ case GLFW_KEY_UP:
+ view_rotx += 5.0;
+ break;
+ case GLFW_KEY_DOWN:
+ view_rotx -= 5.0;
+ break;
+ case GLFW_KEY_LEFT:
+ view_roty += 5.0;
+ break;
+ case GLFW_KEY_RIGHT:
+ view_roty -= 5.0;
+ break;
+ default:
+ return;
+ }
+}
+
+
+/* new window size */
+void GLFWCALL reshape( int width, int height )
+{
+ GLfloat h = (GLfloat) height / (GLfloat) width;
+ GLfloat xmax, znear, zfar;
+
+ znear = 5.0f;
+ zfar = 30.0f;
+ xmax = znear * 0.5f;
+
+ glViewport( 0, 0, (GLint) width, (GLint) height );
+ glMatrixMode( GL_PROJECTION );
+ glLoadIdentity();
+ glFrustum( -xmax, xmax, -xmax*h, xmax*h, znear, zfar );
+ glMatrixMode( GL_MODELVIEW );
+ glLoadIdentity();
+ glTranslatef( 0.0, 0.0, -20.0 );
+}
+
+
+/* program & OpenGL initialization */
+static void init(int argc, char *argv[])
+{
+ static GLfloat pos[4] = {5.f, 5.f, 10.f, 0.f};
+ static GLfloat red[4] = {0.8f, 0.1f, 0.f, 1.f};
+ static GLfloat green[4] = {0.f, 0.8f, 0.2f, 1.f};
+ static GLfloat blue[4] = {0.2f, 0.2f, 1.f, 1.f};
+ GLint i;
+
+ glLightfv(GL_LIGHT0, GL_POSITION, pos);
+ glEnable(GL_CULL_FACE);
+ glEnable(GL_LIGHTING);
+ glEnable(GL_LIGHT0);
+ glEnable(GL_DEPTH_TEST);
+
+ /* make the gears */
+ gear1 = glGenLists(1);
+ glNewList(gear1, GL_COMPILE);
+ glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, red);
+ gear(1.f, 4.f, 1.f, 20, 0.7f);
+ glEndList();
+
+ gear2 = glGenLists(1);
+ glNewList(gear2, GL_COMPILE);
+ glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, green);
+ gear(0.5f, 2.f, 2.f, 10, 0.7f);
+ glEndList();
+
+ gear3 = glGenLists(1);
+ glNewList(gear3, GL_COMPILE);
+ glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, blue);
+ gear(1.3f, 2.f, 0.5f, 10, 0.7f);
+ glEndList();
+
+ glEnable(GL_NORMALIZE);
+
+ for ( i=1; i<argc; i++ ) {
+ if (strcmp(argv[i], "-info")==0) {
+ printf("GL_RENDERER = %s\n", (char *) glGetString(GL_RENDERER));
+ printf("GL_VERSION = %s\n", (char *) glGetString(GL_VERSION));
+ printf("GL_VENDOR = %s\n", (char *) glGetString(GL_VENDOR));
+ printf("GL_EXTENSIONS = %s\n", (char *) glGetString(GL_EXTENSIONS));
+ }
+ else if ( strcmp(argv[i], "-exit")==0) {
+ autoexit = 30;
+ printf("Auto Exit after %i seconds.\n", autoexit );
+ }
+ }
+}
+
+
+/* program entry */
+int main(int argc, char *argv[])
+{
+ if( !glfwInit() )
+ {
+ fprintf( stderr, "Failed to initialize GLFW\n" );
+ exit( EXIT_FAILURE );
+ }
+
+ if( !glfwOpenWindow( 300,300, 0,0,0,0, 16,0, GLFW_WINDOW ) )
+ {
+ fprintf( stderr, "Failed to open GLFW window\n" );
+ glfwTerminate();
+ exit( EXIT_FAILURE );
+ }
+
+ glfwSetWindowTitle( "Gears" );
+ glfwEnable( GLFW_KEY_REPEAT );
+ glfwSwapInterval( 1 );
+
+ // Parse command-line options
+ init(argc, argv);
+
+ // Set callback functions
+ glfwSetWindowSizeCallback( reshape );
+ glfwSetKeyCallback( key );
+
+ // Main loop
+ while( running )
+ {
+ // Draw gears
+ draw();
+
+ // Update animation
+ animate();
+
+ // Swap buffers
+ glfwSwapBuffers();
+
+ // Was the window closed?
+ if( !glfwGetWindowParam( GLFW_OPENED ) )
+ {
+ running = 0;
+ }
+ }
+
+ // Terminate GLFW
+ glfwTerminate();
+
+ // Exit program
+ exit( EXIT_SUCCESS );
+}
+
diff --git a/tests/glfw/getopt.c b/tests/glfw/getopt.c
new file mode 100644
index 00000000..b891b0a5
--- /dev/null
+++ b/tests/glfw/getopt.c
@@ -0,0 +1,253 @@
+/*****************************************************************************
+* getopt.c - competent and free getopt library.
+* $Header: /cvsroot/freegetopt/freegetopt/getopt.c,v 1.2 2003/10/26 03:10:20 vindaci Exp $
+*
+* Copyright (c)2002-2003 Mark K. Kim
+* All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+*
+* * Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+*
+* * Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer in
+* the documentation and/or other materials provided with the
+* distribution.
+*
+* * Neither the original author of this software nor the names of its
+* contributors may be used to endorse or promote products derived
+* from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+* DAMAGE.
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "getopt.h"
+
+
+/* 2009-10-12 Camilla Berglund <elmindreda@elmindreda.org>
+ *
+ * Removed unused global static variable 'ID'.
+ */
+
+
+char* optarg = NULL;
+int optind = 0;
+int opterr = 1;
+int optopt = '?';
+
+
+static char** prev_argv = NULL; /* Keep a copy of argv and argc to */
+static int prev_argc = 0; /* tell if getopt params change */
+static int argv_index = 0; /* Option we're checking */
+static int argv_index2 = 0; /* Option argument we're checking */
+static int opt_offset = 0; /* Index into compounded "-option" */
+static int dashdash = 0; /* True if "--" option reached */
+static int nonopt = 0; /* How many nonopts we've found */
+
+static void increment_index()
+{
+ /* Move onto the next option */
+ if(argv_index < argv_index2)
+ {
+ while(prev_argv[++argv_index] && prev_argv[argv_index][0] != '-'
+ && argv_index < argv_index2+1);
+ }
+ else argv_index++;
+ opt_offset = 1;
+}
+
+
+/*
+* Permutes argv[] so that the argument currently being processed is moved
+* to the end.
+*/
+static int permute_argv_once()
+{
+ /* Movability check */
+ if(argv_index + nonopt >= prev_argc) return 1;
+ /* Move the current option to the end, bring the others to front */
+ else
+ {
+ char* tmp = prev_argv[argv_index];
+
+ /* Move the data */
+ memmove(&prev_argv[argv_index], &prev_argv[argv_index+1],
+ sizeof(char**) * (prev_argc - argv_index - 1));
+ prev_argv[prev_argc - 1] = tmp;
+
+ nonopt++;
+ return 0;
+ }
+}
+
+
+int getopt(int argc, char** argv, char* optstr)
+{
+ int c = 0;
+
+ /* If we have new argv, reinitialize */
+ if(prev_argv != argv || prev_argc != argc)
+ {
+ /* Initialize variables */
+ prev_argv = argv;
+ prev_argc = argc;
+ argv_index = 1;
+ argv_index2 = 1;
+ opt_offset = 1;
+ dashdash = 0;
+ nonopt = 0;
+ }
+
+ /* Jump point in case we want to ignore the current argv_index */
+ getopt_top:
+
+ /* Misc. initializations */
+ optarg = NULL;
+
+ /* Dash-dash check */
+ if(argv[argv_index] && !strcmp(argv[argv_index], "--"))
+ {
+ dashdash = 1;
+ increment_index();
+ }
+
+ /* If we're at the end of argv, that's it. */
+ if(argv[argv_index] == NULL)
+ {
+ c = -1;
+ }
+ /* Are we looking at a string? Single dash is also a string */
+ else if(dashdash || argv[argv_index][0] != '-' || !strcmp(argv[argv_index], "-"))
+ {
+ /* If we want a string... */
+ if(optstr[0] == '-')
+ {
+ c = 1;
+ optarg = argv[argv_index];
+ increment_index();
+ }
+ /* If we really don't want it (we're in POSIX mode), we're done */
+ else if(optstr[0] == '+' || getenv("POSIXLY_CORRECT"))
+ {
+ c = -1;
+
+ /* Everything else is a non-opt argument */
+ nonopt = argc - argv_index;
+ }
+ /* If we mildly don't want it, then move it back */
+ else
+ {
+ if(!permute_argv_once()) goto getopt_top;
+ else c = -1;
+ }
+ }
+ /* Otherwise we're looking at an option */
+ else
+ {
+ char* opt_ptr = NULL;
+
+ /* Grab the option */
+ c = argv[argv_index][opt_offset++];
+
+ /* Is the option in the optstr? */
+ if(optstr[0] == '-') opt_ptr = strchr(optstr+1, c);
+ else opt_ptr = strchr(optstr, c);
+ /* Invalid argument */
+ if(!opt_ptr)
+ {
+ if(opterr)
+ {
+ fprintf(stderr, "%s: invalid option -- %c\n", argv[0], c);
+ }
+
+ optopt = c;
+ c = '?';
+
+ /* Move onto the next option */
+ increment_index();
+ }
+ /* Option takes argument */
+ else if(opt_ptr[1] == ':')
+ {
+ /* ie, -oARGUMENT, -xxxoARGUMENT, etc. */
+ if(argv[argv_index][opt_offset] != '\0')
+ {
+ optarg = &argv[argv_index][opt_offset];
+ increment_index();
+ }
+ /* ie, -o ARGUMENT (only if it's a required argument) */
+ else if(opt_ptr[2] != ':')
+ {
+ /* One of those "you're not expected to understand this" moment */
+ if(argv_index2 < argv_index) argv_index2 = argv_index;
+ while(argv[++argv_index2] && argv[argv_index2][0] == '-');
+ optarg = argv[argv_index2];
+
+ /* Don't cross into the non-option argument list */
+ if(argv_index2 + nonopt >= prev_argc) optarg = NULL;
+
+ /* Move onto the next option */
+ increment_index();
+ }
+ else
+ {
+ /* Move onto the next option */
+ increment_index();
+ }
+
+ /* In case we got no argument for an option with required argument */
+ if(optarg == NULL && opt_ptr[2] != ':')
+ {
+ optopt = c;
+ c = '?';
+
+ if(opterr)
+ {
+ fprintf(stderr,"%s: option requires an argument -- %c\n",
+ argv[0], optopt);
+ }
+ }
+ }
+ /* Option does not take argument */
+ else
+ {
+ /* Next argv_index */
+ if(argv[argv_index][opt_offset] == '\0')
+ {
+ increment_index();
+ }
+ }
+ }
+
+ /* Calculate optind */
+ if(c == -1)
+ {
+ optind = argc - nonopt;
+ }
+ else
+ {
+ optind = argv_index;
+ }
+
+ return c;
+}
+
+
+/* vim:ts=3
+*/
diff --git a/tests/glfw/getopt.h b/tests/glfw/getopt.h
new file mode 100644
index 00000000..0b78650a
--- /dev/null
+++ b/tests/glfw/getopt.h
@@ -0,0 +1,63 @@
+/*****************************************************************************
+* getopt.h - competent and free getopt library.
+* $Header: /cvsroot/freegetopt/freegetopt/getopt.h,v 1.2 2003/10/26 03:10:20 vindaci Exp $
+*
+* Copyright (c)2002-2003 Mark K. Kim
+* All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+*
+* * Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+*
+* * Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer in
+* the documentation and/or other materials provided with the
+* distribution.
+*
+* * Neither the original author of this software nor the names of its
+* contributors may be used to endorse or promote products derived
+* from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+* DAMAGE.
+*/
+#ifndef GETOPT_H_
+#define GETOPT_H_
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+extern char* optarg;
+extern int optind;
+extern int opterr;
+extern int optopt;
+
+int getopt(int argc, char** argv, char* optstr);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* GETOPT_H_ */
+
+
+/* vim:ts=3
+*/
diff --git a/tests/glfw/heightmap.c b/tests/glfw/heightmap.c
new file mode 100644
index 00000000..7faa5d1f
--- /dev/null
+++ b/tests/glfw/heightmap.c
@@ -0,0 +1,850 @@
+//========================================================================
+// Heightmap example program using OpenGL 3 core profile
+// Copyright (c) 2010 Olivier Delannoy
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would
+// be appreciated but is not required.
+//
+// 2. Altered source versions must be plainly marked as such, and must not
+// be misrepresented as being the original software.
+//
+// 3. This notice may not be removed or altered from any source
+// distribution.
+//
+//========================================================================
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <assert.h>
+#include <stddef.h>
+#include "getopt.h"
+
+
+#define GLFW_NO_GLU 1
+#include <GL/glfw.h>
+
+/* OpenGL 3.3 support
+ * Functions are effectively mapped in init_opengl() */
+#ifndef GL_VERSION_3_0
+/* no defines */
+#endif
+
+#ifndef GL_VERSION_2_0
+
+typedef char GLchar;
+
+#define GL_BLEND_EQUATION_RGB 0x8009
+#define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622
+#define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623
+#define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624
+#define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625
+#define GL_CURRENT_VERTEX_ATTRIB 0x8626
+#define GL_VERTEX_PROGRAM_POINT_SIZE 0x8642
+#define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645
+#define GL_STENCIL_BACK_FUNC 0x8800
+#define GL_STENCIL_BACK_FAIL 0x8801
+#define GL_STENCIL_BACK_PASS_DEPTH_FAIL 0x8802
+#define GL_STENCIL_BACK_PASS_DEPTH_PASS 0x8803
+#define GL_MAX_DRAW_BUFFERS 0x8824
+#define GL_DRAW_BUFFER0 0x8825
+#define GL_DRAW_BUFFER1 0x8826
+#define GL_DRAW_BUFFER2 0x8827
+#define GL_DRAW_BUFFER3 0x8828
+#define GL_DRAW_BUFFER4 0x8829
+#define GL_DRAW_BUFFER5 0x882A
+#define GL_DRAW_BUFFER6 0x882B
+#define GL_DRAW_BUFFER7 0x882C
+#define GL_DRAW_BUFFER8 0x882D
+#define GL_DRAW_BUFFER9 0x882E
+#define GL_DRAW_BUFFER10 0x882F
+#define GL_DRAW_BUFFER11 0x8830
+#define GL_DRAW_BUFFER12 0x8831
+#define GL_DRAW_BUFFER13 0x8832
+#define GL_DRAW_BUFFER14 0x8833
+#define GL_DRAW_BUFFER15 0x8834
+#define GL_BLEND_EQUATION_ALPHA 0x883D
+#define GL_MAX_VERTEX_ATTRIBS 0x8869
+#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A
+#define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872
+#define GL_FRAGMENT_SHADER 0x8B30
+#define GL_VERTEX_SHADER 0x8B31
+#define GL_MAX_FRAGMENT_UNIFORM_COMPONENTS 0x8B49
+#define GL_MAX_VERTEX_UNIFORM_COMPONENTS 0x8B4A
+#define GL_MAX_VARYING_FLOATS 0x8B4B
+#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS 0x8B4C
+#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D
+#define GL_SHADER_TYPE 0x8B4F
+#define GL_FLOAT_VEC2 0x8B50
+#define GL_FLOAT_VEC3 0x8B51
+#define GL_FLOAT_VEC4 0x8B52
+#define GL_INT_VEC2 0x8B53
+#define GL_INT_VEC3 0x8B54
+#define GL_INT_VEC4 0x8B55
+#define GL_BOOL 0x8B56
+#define GL_BOOL_VEC2 0x8B57
+#define GL_BOOL_VEC3 0x8B58
+#define GL_BOOL_VEC4 0x8B59
+#define GL_FLOAT_MAT2 0x8B5A
+#define GL_FLOAT_MAT3 0x8B5B
+#define GL_FLOAT_MAT4 0x8B5C
+#define GL_SAMPLER_1D 0x8B5D
+#define GL_SAMPLER_2D 0x8B5E
+#define GL_SAMPLER_3D 0x8B5F
+#define GL_SAMPLER_CUBE 0x8B60
+#define GL_SAMPLER_1D_SHADOW 0x8B61
+#define GL_SAMPLER_2D_SHADOW 0x8B62
+#define GL_DELETE_STATUS 0x8B80
+#define GL_COMPILE_STATUS 0x8B81
+#define GL_LINK_STATUS 0x8B82
+#define GL_VALIDATE_STATUS 0x8B83
+#define GL_INFO_LOG_LENGTH 0x8B84
+#define GL_ATTACHED_SHADERS 0x8B85
+#define GL_ACTIVE_UNIFORMS 0x8B86
+#define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87
+#define GL_SHADER_SOURCE_LENGTH 0x8B88
+#define GL_ACTIVE_ATTRIBUTES 0x8B89
+#define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A
+#define GL_FRAGMENT_SHADER_DERIVATIVE_HINT 0x8B8B
+#define GL_SHADING_LANGUAGE_VERSION 0x8B8C
+#define GL_CURRENT_PROGRAM 0x8B8D
+#define GL_POINT_SPRITE_COORD_ORIGIN 0x8CA0
+#define GL_LOWER_LEFT 0x8CA1
+#define GL_UPPER_LEFT 0x8CA2
+#define GL_STENCIL_BACK_REF 0x8CA3
+#define GL_STENCIL_BACK_VALUE_MASK 0x8CA4
+#define GL_STENCIL_BACK_WRITEMASK 0x8CA5
+#endif
+
+
+
+#ifndef GL_VERSION_1_5
+
+typedef ptrdiff_t GLintptr;
+typedef ptrdiff_t GLsizeiptr;
+
+#define GL_BUFFER_SIZE 0x8764
+#define GL_BUFFER_USAGE 0x8765
+#define GL_QUERY_COUNTER_BITS 0x8864
+#define GL_CURRENT_QUERY 0x8865
+#define GL_QUERY_RESULT 0x8866
+#define GL_QUERY_RESULT_AVAILABLE 0x8867
+#define GL_ARRAY_BUFFER 0x8892
+#define GL_ELEMENT_ARRAY_BUFFER 0x8893
+#define GL_ARRAY_BUFFER_BINDING 0x8894
+#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895
+#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F
+#define GL_READ_ONLY 0x88B8
+#define GL_WRITE_ONLY 0x88B9
+#define GL_READ_WRITE 0x88BA
+#define GL_BUFFER_ACCESS 0x88BB
+#define GL_BUFFER_MAPPED 0x88BC
+#define GL_BUFFER_MAP_POINTER 0x88BD
+#define GL_STREAM_DRAW 0x88E0
+#define GL_STREAM_READ 0x88E1
+#define GL_STREAM_COPY 0x88E2
+#define GL_STATIC_DRAW 0x88E4
+#define GL_STATIC_READ 0x88E5
+#define GL_STATIC_COPY 0x88E6
+#define GL_DYNAMIC_DRAW 0x88E8
+#define GL_DYNAMIC_READ 0x88E9
+#define GL_DYNAMIC_COPY 0x88EA
+#define GL_SAMPLES_PASSED 0x8914
+#endif
+
+
+
+
+/* OpenGL 3.0 */
+typedef void (APIENTRY * PFN_glGenVertexArrays)(GLsizei n, GLuint *arrays);
+typedef void (APIENTRY * PFN_glBindVertexArray)(GLuint array);
+typedef void (APIENTRY * PFN_glDeleteVertexArrays)(GLsizei n, GLuint *arrays);
+/* OpenGL 2.0 */
+typedef GLuint (APIENTRY * PFN_glCreateShader)(GLenum type);
+typedef void (APIENTRY * PFN_glDeleteShader)(GLuint shader);
+typedef void (APIENTRY * PFN_glCompileShader)(GLuint shader);
+typedef void (APIENTRY * PFN_glShaderSource)(GLuint shader, GLsizei count, const GLchar* *string, const GLint *length);
+typedef void (APIENTRY * PFN_glGetShaderiv)(GLuint shader, GLenum pname, GLint *params);
+typedef void (APIENTRY * PFN_glGetShaderInfoLog)(GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog);
+typedef GLuint (APIENTRY * PFN_glCreateProgram)(void);
+typedef void (APIENTRY * PFN_glDeleteProgram)(GLuint program);
+typedef void (APIENTRY * PFN_glAttachShader)(GLuint program, GLuint shader);
+typedef void (APIENTRY * PFN_glLinkProgram)(GLuint program);
+typedef void (APIENTRY * PFN_glGetProgramiv)(GLuint program, GLenum pname, GLint *params);
+typedef void (APIENTRY * PFN_glGetProgramInfoLog)(GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog);
+typedef void (APIENTRY * PFN_glValidateProgram)(GLuint program);
+typedef void (APIENTRY * PFN_glUseProgram)(GLuint program);
+typedef GLint (APIENTRY * PFN_glGetUniformLocation)(GLuint program, const GLchar *name);
+typedef void (APIENTRY * PFN_glUniformMatrix4fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
+typedef GLint (APIENTRY * PFN_glGetAttribLocation)(GLuint program, const GLchar *name);
+typedef void (APIENTRY * PFN_glEnableVertexAttribArray)(GLuint index);
+typedef void (APIENTRY * PFN_glVertexAttrib1f)(GLuint index, GLfloat x);
+typedef void (APIENTRY * PFN_glVertexAttribPointer)(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *pointer);
+/* OpenGL 1.5 */
+typedef void (APIENTRY * PFN_glBindBuffer)(GLenum target, GLuint buffer);
+typedef void (APIENTRY * PFN_glDeleteBuffers)(GLsizei n, const GLuint *buffers);
+typedef void (APIENTRY * PFN_glGenBuffers)(GLsizei n, GLuint *buffers);
+typedef void (APIENTRY * PFN_glBufferData)(GLenum target, GLsizeiptr size, const GLvoid *data, GLenum usage);
+typedef void (APIENTRY * PFN_glBufferSubData)(GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid *data);
+
+/* OpenGL function pointers */
+static PFN_glGenBuffers pglGenBuffers = NULL;
+static PFN_glGenVertexArrays pglGenVertexArrays = NULL;
+static PFN_glDeleteVertexArrays pglDeleteVertexArrays = NULL;
+static PFN_glCreateShader pglCreateShader = NULL;
+static PFN_glShaderSource pglShaderSource = NULL;
+static PFN_glCompileShader pglCompileShader = NULL;
+static PFN_glGetShaderiv pglGetShaderiv = NULL;
+static PFN_glGetShaderInfoLog pglGetShaderInfoLog = NULL;
+static PFN_glDeleteShader pglDeleteShader = NULL;
+static PFN_glCreateProgram pglCreateProgram = NULL;
+static PFN_glAttachShader pglAttachShader = NULL;
+static PFN_glLinkProgram pglLinkProgram = NULL;
+static PFN_glUseProgram pglUseProgram = NULL;
+static PFN_glGetProgramiv pglGetProgramiv = NULL;
+static PFN_glGetProgramInfoLog pglGetProgramInfoLog = NULL;
+static PFN_glDeleteProgram pglDeleteProgram = NULL;
+static PFN_glGetUniformLocation pglGetUniformLocation = NULL;
+static PFN_glUniformMatrix4fv pglUniformMatrix4fv = NULL;
+static PFN_glGetAttribLocation pglGetAttribLocation = NULL;
+
+/* Map height updates */
+#define MAX_CIRCLE_SIZE (5.0f)
+#define MAX_DISPLACEMENT (1.0f)
+#define DISPLACEMENT_SIGN_LIMIT (0.3f)
+#define MAX_ITER (200)
+#define NUM_ITER_AT_A_TIME (1)
+
+/* Map general information */
+#define MAP_SIZE (10.0f)
+#define MAP_NUM_VERTICES (80)
+#define MAP_NUM_TOTAL_VERTICES (MAP_NUM_VERTICES*MAP_NUM_VERTICES)
+#define MAP_NUM_LINES (3* (MAP_NUM_VERTICES - 1) * (MAP_NUM_VERTICES - 1) + \
+ 2 * (MAP_NUM_VERTICES - 1))
+
+
+/* OpenGL function pointers */
+
+static PFN_glBindVertexArray pglBindVertexArray = NULL;
+static PFN_glBufferData pglBufferData = NULL;
+static PFN_glBindBuffer pglBindBuffer = NULL;
+static PFN_glBufferSubData pglBufferSubData = NULL;
+static PFN_glEnableVertexAttribArray pglEnableVertexAttribArray = NULL;
+static PFN_glVertexAttribPointer pglVertexAttribPointer = NULL;
+
+#define RESOLVE_GL_FCN(type, var, name) \
+ if (status == GL_TRUE) \
+ {\
+ var = glfwGetProcAddress((name));\
+ if ((var) == NULL)\
+ {\
+ status = GL_FALSE;\
+ }\
+ }
+
+
+static GLboolean init_opengl(void)
+{
+ GLboolean status = GL_TRUE;
+ RESOLVE_GL_FCN(PFN_glCreateShader, pglCreateShader, "glCreateShader");
+ RESOLVE_GL_FCN(PFN_glShaderSource, pglShaderSource, "glShaderSource");
+ RESOLVE_GL_FCN(PFN_glCompileShader, pglCompileShader, "glCompileShader");
+ RESOLVE_GL_FCN(PFN_glGetShaderiv, pglGetShaderiv, "glGetShaderiv");
+ RESOLVE_GL_FCN(PFN_glGetShaderInfoLog, pglGetShaderInfoLog, "glGetShaderInfoLog");
+ RESOLVE_GL_FCN(PFN_glDeleteShader, pglDeleteShader, "glDeleteShader");
+ RESOLVE_GL_FCN(PFN_glCreateProgram, pglCreateProgram, "glCreateProgram");
+ RESOLVE_GL_FCN(PFN_glAttachShader, pglAttachShader, "glAttachShader");
+ RESOLVE_GL_FCN(PFN_glLinkProgram, pglLinkProgram, "glLinkProgram");
+ RESOLVE_GL_FCN(PFN_glUseProgram, pglUseProgram, "glUseProgram");
+ RESOLVE_GL_FCN(PFN_glGetProgramiv, pglGetProgramiv, "glGetProgramiv");
+ RESOLVE_GL_FCN(PFN_glGetProgramInfoLog, pglGetProgramInfoLog, "glGetProgramInfoLog");
+ RESOLVE_GL_FCN(PFN_glDeleteProgram, pglDeleteProgram, "glDeleteProgram");
+ RESOLVE_GL_FCN(PFN_glGetUniformLocation, pglGetUniformLocation, "glGetUniformLocation");
+ RESOLVE_GL_FCN(PFN_glUniformMatrix4fv, pglUniformMatrix4fv, "glUniformMatrix4fv");
+ RESOLVE_GL_FCN(PFN_glGetAttribLocation, pglGetAttribLocation, "glGetAttribLocation");
+ RESOLVE_GL_FCN(PFN_glGenVertexArrays, pglGenVertexArrays, "glGenVertexArrays");
+ RESOLVE_GL_FCN(PFN_glDeleteVertexArrays, pglDeleteVertexArrays, "glDeleteVertexArrays");
+ RESOLVE_GL_FCN(PFN_glBindVertexArray, pglBindVertexArray, "glBindVertexArray");
+ RESOLVE_GL_FCN(PFN_glGenBuffers, pglGenBuffers, "glGenBuffers");
+ RESOLVE_GL_FCN(PFN_glBindBuffer, pglBindBuffer, "glBindBuffer");
+ RESOLVE_GL_FCN(PFN_glBufferData, pglBufferData, "glBufferData");
+ RESOLVE_GL_FCN(PFN_glBufferSubData, pglBufferSubData, "glBufferSubData");
+ RESOLVE_GL_FCN(PFN_glEnableVertexAttribArray, pglEnableVertexAttribArray, "glEnableVertexAttribArray");
+ RESOLVE_GL_FCN(PFN_glVertexAttribPointer, pglVertexAttribPointer, "glVertexAttribPointer");
+ return status;
+}
+/**********************************************************************
+ * Default shader programs
+ *********************************************************************/
+
+static const char* default_vertex_shader =
+"#version 150\n"
+"uniform mat4 project;\n"
+"uniform mat4 modelview;\n"
+"in float x;\n"
+"in float y;\n"
+"in float z;\n"
+"\n"
+"void main()\n"
+"{\n"
+" gl_Position = project * modelview * vec4(x, y, z, 1.0);\n"
+"}\n";
+
+static const char* default_fragment_shader =
+"#version 150\n"
+"out vec4 gl_FragColor;\n"
+"void main()\n"
+"{\n"
+" gl_FragColor = vec4(0.2, 1.0, 0.2, 1.0); \n"
+"}\n";
+
+/**********************************************************************
+ * Values for shader uniforms
+ *********************************************************************/
+
+/* Frustum configuration */
+static GLfloat view_angle = 45.0f;
+static GLfloat aspect_ratio = 4.0f/3.0f;
+static GLfloat z_near = 1.0f;
+static GLfloat z_far = 100.f;
+
+/* Projection matrix */
+static GLfloat projection_matrix[16] = {
+ 1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f, 0.0f,
+ 0.0f, 0.0f, 0.0f, 1.0f
+};
+
+/* Model view matrix */
+static GLfloat modelview_matrix[16] = {
+ 1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f, 0.0f,
+ 0.0f, 0.0f, 0.0f, 1.0f
+};
+
+/**********************************************************************
+ * Heightmap vertex and index data
+ *********************************************************************/
+
+static GLfloat map_vertices[3][MAP_NUM_TOTAL_VERTICES];
+static GLuint map_line_indices[2*MAP_NUM_LINES];
+
+/* Store uniform location for the shaders
+ * Those values are setup as part of the process of creating
+ * the shader program. They should not be used before creating
+ * the program.
+ */
+static GLuint mesh;
+static GLuint mesh_vbo[4];
+
+/**********************************************************************
+ * OpenGL helper functions
+ *********************************************************************/
+
+/* Load a (text) file into memory and return its contents
+ */
+static char* read_file_content(const char* filename)
+{
+ FILE* fd;
+ size_t size = 0;
+ char* result = NULL;
+
+ fd = fopen(filename, "r");
+ if (fd != NULL)
+ {
+ size = fseek(fd, 0, SEEK_END);
+ (void) fseek(fd, 0, SEEK_SET);
+
+ result = malloc(size + 1);
+ result[size] = '\0';
+ if (fread(result, size, 1, fd) != 1)
+ {
+ free(result);
+ result = NULL;
+ }
+ (void) fclose(fd);
+ }
+ return result;
+}
+
+/* Creates a shader object of the specified type using the specified text
+ */
+static GLuint make_shader(GLenum type, const char* shader_src)
+{
+ GLuint shader;
+ GLint shader_ok;
+ GLsizei log_length;
+ char info_log[8192];
+
+ shader = pglCreateShader(type);
+ if (shader != 0)
+ {
+ pglShaderSource(shader, 1, (const GLchar**)&shader_src, NULL);
+ pglCompileShader(shader);
+ pglGetShaderiv(shader, GL_COMPILE_STATUS, &shader_ok);
+ if (shader_ok != GL_TRUE)
+ {
+ fprintf(stderr, "ERROR: Failed to compile %s shader\n", (type == GL_FRAGMENT_SHADER) ? "fragment" : "vertex" );
+ pglGetShaderInfoLog(shader, 8192, &log_length,info_log);
+ fprintf(stderr, "ERROR: \n%s\n\n", info_log);
+ pglDeleteShader(shader);
+ shader = 0;
+ }
+ }
+ return shader;
+}
+
+/* Creates a program object using the specified vertex and fragment text
+ */
+static GLuint make_shader_program(const char* vertex_shader_src, const char* fragment_shader_src)
+{
+ GLuint program = 0u;
+ GLint program_ok;
+ GLuint vertex_shader = 0u;
+ GLuint fragment_shader = 0u;
+ GLsizei log_length;
+ char info_log[8192];
+
+ vertex_shader = make_shader(GL_VERTEX_SHADER, (vertex_shader_src == NULL) ? default_vertex_shader : vertex_shader_src);
+ if (vertex_shader != 0u)
+ {
+ fragment_shader = make_shader(GL_FRAGMENT_SHADER, (fragment_shader_src == NULL) ? default_fragment_shader : fragment_shader_src);
+ if (fragment_shader != 0u)
+ {
+ /* make the program that connect the two shader and link it */
+ program = pglCreateProgram();
+ if (program != 0u)
+ {
+ /* attach both shader and link */
+ pglAttachShader(program, vertex_shader);
+ pglAttachShader(program, fragment_shader);
+ pglLinkProgram(program);
+ pglGetProgramiv(program, GL_LINK_STATUS, &program_ok);
+
+ if (program_ok != GL_TRUE)
+ {
+ fprintf(stderr, "ERROR, failed to link shader program\n");
+ pglGetProgramInfoLog(program, 8192, &log_length, info_log);
+ fprintf(stderr, "ERROR: \n%s\n\n", info_log);
+ pglDeleteProgram(program);
+ pglDeleteShader(fragment_shader);
+ pglDeleteShader(vertex_shader);
+ program = 0u;
+ }
+ }
+ }
+ else
+ {
+ fprintf(stderr, "ERROR: Unable to load fragment shader\n");
+ pglDeleteShader(vertex_shader);
+ }
+ }
+ else
+ {
+ fprintf(stderr, "ERROR: Unable to load vertex shader\n");
+ }
+ return program;
+}
+
+/**********************************************************************
+ * Geometry creation functions
+ *********************************************************************/
+
+/* Generate vertices and indices for the heightmap
+ */
+static void init_map(void)
+{
+ int i;
+ int j;
+ int k;
+ GLfloat step = MAP_SIZE / (MAP_NUM_VERTICES - 1);
+ GLfloat x = 0.0f;
+ GLfloat z = 0.0f;
+ /* Create a flat grid */
+ k = 0;
+ for (i = 0 ; i < MAP_NUM_VERTICES ; ++i)
+ {
+ for (j = 0 ; j < MAP_NUM_VERTICES ; ++j)
+ {
+ map_vertices[0][k] = x;
+ map_vertices[1][k] = 0.0f;
+ map_vertices[2][k] = z;
+ z += step;
+ ++k;
+ }
+ x += step;
+ z = 0.0f;
+ }
+#if DEBUG_ENABLED
+ for (i = 0 ; i < MAP_NUM_TOTAL_VERTICES ; ++i)
+ {
+ printf ("Vertice %d (%f, %f, %f)\n",
+ i, map_vertices[0][i], map_vertices[1][i], map_vertices[2][i]);
+
+ }
+#endif
+ /* create indices */
+ /* line fan based on i
+ * i+1
+ * | / i + n + 1
+ * | /
+ * |/
+ * i --- i + n
+ */
+
+ /* close the top of the square */
+ k = 0;
+ for (i = 0 ; i < MAP_NUM_VERTICES -1 ; ++i)
+ {
+ map_line_indices[k++] = (i + 1) * MAP_NUM_VERTICES -1;
+ map_line_indices[k++] = (i + 2) * MAP_NUM_VERTICES -1;
+ }
+ /* close the right of the square */
+ for (i = 0 ; i < MAP_NUM_VERTICES -1 ; ++i)
+ {
+ map_line_indices[k++] = (MAP_NUM_VERTICES - 1) * MAP_NUM_VERTICES + i;
+ map_line_indices[k++] = (MAP_NUM_VERTICES - 1) * MAP_NUM_VERTICES + i + 1;
+ }
+
+ for (i = 0 ; i < (MAP_NUM_VERTICES - 1) ; ++i)
+ {
+ for (j = 0 ; j < (MAP_NUM_VERTICES - 1) ; ++j)
+ {
+ int ref = i * (MAP_NUM_VERTICES) + j;
+ map_line_indices[k++] = ref;
+ map_line_indices[k++] = ref + 1;
+
+ map_line_indices[k++] = ref;
+ map_line_indices[k++] = ref + MAP_NUM_VERTICES;
+
+ map_line_indices[k++] = ref;
+ map_line_indices[k++] = ref + MAP_NUM_VERTICES + 1;
+ }
+ }
+
+#ifdef DEBUG_ENABLED
+ for (k = 0 ; k < 2 * MAP_NUM_LINES ; k += 2)
+ {
+ int beg, end;
+ beg = map_line_indices[k];
+ end = map_line_indices[k+1];
+ printf ("Line %d: %d -> %d (%f, %f, %f) -> (%f, %f, %f)\n",
+ k / 2, beg, end,
+ map_vertices[0][beg], map_vertices[1][beg], map_vertices[2][beg],
+ map_vertices[0][end], map_vertices[1][end], map_vertices[2][end]);
+ }
+#endif
+}
+
+static void generate_heightmap__circle(float* center_x, float* center_y,
+ float* size, float* displacement)
+{
+ float sign;
+ /* random value for element in between [0-1.0] */
+ *center_x = (MAP_SIZE * rand()) / (1.0f * RAND_MAX);
+ *center_y = (MAP_SIZE * rand()) / (1.0f * RAND_MAX);
+ *size = (MAX_CIRCLE_SIZE * rand()) / (1.0f * RAND_MAX);
+ sign = (1.0f * rand()) / (1.0f * RAND_MAX);
+ sign = (sign < DISPLACEMENT_SIGN_LIMIT) ? -1.0f : 1.0f;
+ *displacement = (sign * (MAX_DISPLACEMENT * rand())) / (1.0f * RAND_MAX);
+}
+
+/* Run the specified number of iterations of the generation process for the
+ * heightmap
+ */
+static void update_map(int num_iter)
+{
+ assert(num_iter > 0);
+ while(num_iter)
+ {
+ /* center of the circle */
+ float center_x;
+ float center_z;
+ float circle_size;
+ float disp;
+ size_t ii;
+ generate_heightmap__circle(&center_x, &center_z, &circle_size, &disp);
+ disp = disp / 2.0f;
+ for (ii = 0u ; ii < MAP_NUM_TOTAL_VERTICES ; ++ii)
+ {
+ GLfloat dx = center_x - map_vertices[0][ii];
+ GLfloat dz = center_z - map_vertices[2][ii];
+ GLfloat pd = (2.0f * sqrtf((dx * dx) + (dz * dz))) / circle_size;
+ if (fabs(pd) <= 1.0f)
+ {
+ /* tx,tz is within the circle */
+ GLfloat new_height = disp + ((GLfloat) cos(pd*3.14f) * disp);
+ map_vertices[1][ii] += new_height;
+ }
+ }
+ --num_iter;
+ }
+}
+
+/**********************************************************************
+ * OpenGL helper functions
+ *********************************************************************/
+
+/* Create VBO, IBO and VAO objects for the heightmap geometry and bind them to
+ * the specified program object
+ */
+static void make_mesh(GLuint program)
+{
+ GLuint attrloc;
+
+ pglGenVertexArrays(1, &mesh);
+ pglGenBuffers(4, mesh_vbo);
+ pglBindVertexArray(mesh);
+ /* Prepare the data for drawing through a buffer inidices */
+ pglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh_vbo[3]);
+ pglBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint)* MAP_NUM_LINES * 2, map_line_indices, GL_STATIC_DRAW);
+
+ /* Prepare the attributes for rendering */
+ attrloc = pglGetAttribLocation(program, "x");
+ pglBindBuffer(GL_ARRAY_BUFFER, mesh_vbo[0]);
+ pglBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * MAP_NUM_TOTAL_VERTICES, &map_vertices[0][0], GL_STATIC_DRAW);
+ pglEnableVertexAttribArray(attrloc);
+ pglVertexAttribPointer(attrloc, 1, GL_FLOAT, GL_FALSE, 0, 0);
+
+ attrloc = pglGetAttribLocation(program, "z");
+ pglBindBuffer(GL_ARRAY_BUFFER, mesh_vbo[2]);
+ pglBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * MAP_NUM_TOTAL_VERTICES, &map_vertices[2][0], GL_STATIC_DRAW);
+ pglEnableVertexAttribArray(attrloc);
+ pglVertexAttribPointer(attrloc, 1, GL_FLOAT, GL_FALSE, 0, 0);
+
+ attrloc = pglGetAttribLocation(program, "y");
+ pglBindBuffer(GL_ARRAY_BUFFER, mesh_vbo[1]);
+ pglBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * MAP_NUM_TOTAL_VERTICES, &map_vertices[1][0], GL_DYNAMIC_DRAW);
+ pglEnableVertexAttribArray(attrloc);
+ pglVertexAttribPointer(attrloc, 1, GL_FLOAT, GL_FALSE, 0, 0);
+}
+
+/* Update VBO vertices from source data
+ */
+static void update_mesh(void)
+{
+ pglBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(GLfloat) * MAP_NUM_TOTAL_VERTICES, &map_vertices[1][0]);
+}
+
+/**********************************************************************
+ * GLFW callback functions
+ *********************************************************************/
+
+/* The program runs as long as this is GL_TRUE
+ */
+static GLboolean running = GL_TRUE;
+
+/* GLFW Window management functions */
+static int GLFWCALL close_window_callback(void)
+{
+ running = GL_FALSE;
+
+ /* Disallow window closing
+ * The window will be closed when the main loop terminates */
+ return GL_FALSE;
+}
+
+static void GLFWCALL key_callback(int key, int action)
+{
+ switch(key)
+ {
+ case GLFW_KEY_ESC:
+ /* Exit program on Escape */
+ running = GL_FALSE;
+ break;
+ }
+}
+
+/* Print usage information */
+static void usage(void)
+{
+ printf("Usage: heightmap [-v <vertex_shader_path>] [-f <fragment_shader_path>]\n");
+ printf(" heightmap [-h]\n");
+}
+
+int main(int argc, char** argv)
+{
+ int ch, iter;
+ double dt;
+ double last_update_time;
+ int frame;
+ float f;
+ GLint uloc_modelview;
+ GLint uloc_project;
+
+ char* vertex_shader_path = NULL;
+ char* fragment_shader_path = NULL;
+ char* vertex_shader_src = NULL;
+ char* fragment_shader_src = NULL;
+ GLuint shader_program;
+
+ while ((ch = getopt(argc, argv, "f:v:h")) != -1)
+ {
+ switch (ch)
+ {
+ case 'f':
+ fragment_shader_path = optarg;
+ break;
+ case 'v':
+ vertex_shader_path = optarg;
+ break;
+ case 'h':
+ usage();
+ exit(EXIT_SUCCESS);
+ default:
+ usage();
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (fragment_shader_path)
+ {
+ vertex_shader_src = read_file_content(fragment_shader_path);
+ if (!fragment_shader_src)
+ {
+ fprintf(stderr,
+ "ERROR: unable to load fragment shader from '%s'\n",
+ fragment_shader_path);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (vertex_shader_path)
+ {
+ vertex_shader_src = read_file_content(vertex_shader_path);
+ if (!vertex_shader_src)
+ {
+ fprintf(stderr,
+ "ERROR: unable to load vertex shader from '%s'\n",
+ fragment_shader_path);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (GL_TRUE != glfwInit())
+ {
+ fprintf(stderr, "ERROR: Unable to initialize GLFW\n");
+ usage();
+
+ free(vertex_shader_src);
+ free(fragment_shader_src);
+ exit(EXIT_FAILURE);
+ }
+
+ glfwEnable(GLFW_AUTO_POLL_EVENTS); /* No explicit call to glfwPollEvents() */
+
+ glfwOpenWindowHint(GLFW_WINDOW_NO_RESIZE, GL_TRUE);
+ glfwOpenWindowHint(GLFW_OPENGL_VERSION_MAJOR, 3);
+ glfwOpenWindowHint(GLFW_OPENGL_VERSION_MINOR, 2);
+ glfwOpenWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
+ glfwOpenWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_FALSE);
+
+ if (GL_TRUE != glfwOpenWindow(800, 600, 0, 0, 0, 0, 0, 0, GLFW_WINDOW))
+ {
+ fprintf(stderr, "ERROR: Unable to create the OpenGL context and associated window\n");
+ usage();
+
+ free(vertex_shader_src);
+ free(fragment_shader_src);
+ exit(EXIT_FAILURE);
+ }
+
+ glfwSetWindowTitle("GLFW OpenGL3 Heightmap demo");
+ /* Register events callback */
+ glfwSetWindowCloseCallback(close_window_callback);
+ glfwSetKeyCallback(key_callback);
+
+ if (GL_TRUE != init_opengl())
+ {
+ fprintf(stderr, "ERROR: unable to resolve OpenGL function pointers\n");
+ free(vertex_shader_src);
+ free(fragment_shader_src);
+ exit(EXIT_FAILURE);
+ }
+ /* Prepare opengl resources for rendering */
+ shader_program = make_shader_program(vertex_shader_src , fragment_shader_src);
+ free(vertex_shader_src);
+ free(fragment_shader_src);
+
+ if (shader_program == 0u)
+ {
+ fprintf(stderr, "ERROR: during creation of the shader program\n");
+ usage();
+ exit(EXIT_FAILURE);
+ }
+
+ pglUseProgram(shader_program);
+ uloc_project = pglGetUniformLocation(shader_program, "project");
+ uloc_modelview = pglGetUniformLocation(shader_program, "modelview");
+
+ /* Compute the projection matrix */
+ f = 1.0f / tanf(view_angle / 2.0f);
+ projection_matrix[0] = f / aspect_ratio;
+ projection_matrix[5] = f;
+ projection_matrix[10] = (z_far + z_near)/ (z_near - z_far);
+ projection_matrix[11] = -1.0f;
+ projection_matrix[14] = 2.0f * (z_far * z_near) / (z_near - z_far);
+ pglUniformMatrix4fv(uloc_project, 1, GL_FALSE, projection_matrix);
+
+ /* Set the camera position */
+ modelview_matrix[12] = -5.0f;
+ modelview_matrix[13] = -5.0f;
+ modelview_matrix[14] = -20.0f;
+ pglUniformMatrix4fv(uloc_modelview, 1, GL_FALSE, modelview_matrix);
+
+ /* Create mesh data */
+ init_map();
+ make_mesh(shader_program);
+
+ /* Create vao + vbo to store the mesh */
+ /* Create the vbo to store all the information for the grid and the height */
+
+ /* setup the scene ready for rendering */
+ glViewport(0, 0, 800, 600);
+ glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+
+ /* main loop */
+ frame = 0;
+ iter = 0;
+ dt = last_update_time = glfwGetTime();
+
+ while (running)
+ {
+ ++frame;
+ /* render the next frame */
+ glClear(GL_COLOR_BUFFER_BIT);
+ glDrawElements(GL_LINES, 2* MAP_NUM_LINES , GL_UNSIGNED_INT, 0);
+
+ /* display and process events through callbacks */
+ glfwSwapBuffers();
+ /* Check the frame rate and update the heightmap if needed */
+ dt = glfwGetTime();
+ if ((dt - last_update_time) > 0.2)
+ {
+ /* generate the next iteration of the heightmap */
+ if (iter < MAX_ITER)
+ {
+ update_map(NUM_ITER_AT_A_TIME);
+ update_mesh();
+ iter += NUM_ITER_AT_A_TIME;
+ }
+ last_update_time = dt;
+ frame = 0;
+ }
+ }
+
+ exit(EXIT_SUCCESS);
+}
+
diff --git a/tests/glfw/listmodes.c b/tests/glfw/listmodes.c
new file mode 100644
index 00000000..717cfde0
--- /dev/null
+++ b/tests/glfw/listmodes.c
@@ -0,0 +1,48 @@
+//========================================================================
+// This is a small test application for GLFW.
+// The program lists all available fullscreen video modes.
+//========================================================================
+
+#include <stdio.h>
+#include <GL/glfw.h>
+
+// Maximum number of modes that we want to list
+#define MAX_NUM_MODES 400
+
+
+//========================================================================
+// main()
+//========================================================================
+
+int main( void )
+{
+ GLFWvidmode dtmode, modes[ MAX_NUM_MODES ];
+ int modecount, i;
+
+ // Initialize GLFW
+ if( !glfwInit() )
+ {
+ return 0;
+ }
+
+ // Show desktop video mode
+ glfwGetDesktopMode( &dtmode );
+ printf( "Desktop mode: %d x %d x %d\n\n",
+ dtmode.Width, dtmode.Height, dtmode.RedBits +
+ dtmode.GreenBits + dtmode.BlueBits );
+
+ // List available video modes
+ modecount = glfwGetVideoModes( modes, MAX_NUM_MODES );
+ printf( "Available modes:\n" );
+ for( i = 0; i < modecount; i ++ )
+ {
+ printf( "%3d: %d x %d x %d\n", i,
+ modes[i].Width, modes[i].Height, modes[i].RedBits +
+ modes[i].GreenBits + modes[i].BlueBits );
+ }
+
+ // Terminate GLFW
+ glfwTerminate();
+
+ return 0;
+}
diff --git a/tests/glfw/mipmaps.c b/tests/glfw/mipmaps.c
new file mode 100644
index 00000000..59bbef2e
--- /dev/null
+++ b/tests/glfw/mipmaps.c
@@ -0,0 +1,122 @@
+//========================================================================
+// This is an example program for the GLFW library
+//
+// It shows texture loading with mipmap generation and rendering with
+// trilienar texture filtering
+//========================================================================
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <GL/glfw.h>
+
+int main( void )
+{
+ int width, height, x;
+ double time;
+ GLboolean running;
+ GLuint textureID;
+ char* texturePath = "mipmaps.tga";
+
+ // Initialise GLFW
+ if( !glfwInit() )
+ {
+ fprintf( stderr, "Failed to initialize GLFW\n" );
+ exit( EXIT_FAILURE );
+ }
+
+ // Open OpenGL window
+ if( !glfwOpenWindow( 640, 480, 0,0,0,0, 0,0, GLFW_WINDOW ) )
+ {
+ fprintf( stderr, "Failed to open GLFW window\n" );
+ glfwTerminate();
+ exit( EXIT_FAILURE );
+ }
+
+ glfwSetWindowTitle( "Trilinear interpolation" );
+
+ // Enable sticky keys
+ glfwEnable( GLFW_STICKY_KEYS );
+
+ // Enable vertical sync (on cards that support it)
+ glfwSwapInterval( 1 );
+
+ // Generate and bind our texture ID
+ glGenTextures( 1, &textureID );
+ glBindTexture( GL_TEXTURE_2D, textureID );
+
+ // Load texture from file into video memory, including mipmap levels
+ if( !glfwLoadTexture2D( texturePath, GLFW_BUILD_MIPMAPS_BIT ) )
+ {
+ fprintf( stderr, "Failed to load texture %s\n", texturePath );
+ glfwTerminate();
+ exit( EXIT_FAILURE );
+ }
+
+ // Use trilinear interpolation (GL_LINEAR_MIPMAP_LINEAR)
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
+ GL_LINEAR_MIPMAP_LINEAR );
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
+ GL_LINEAR );
+
+ // Enable plain 2D texturing
+ glEnable( GL_TEXTURE_2D );
+
+ running = GL_TRUE;
+ while( running )
+ {
+ // Get time and mouse position
+ time = glfwGetTime();
+ glfwGetMousePos( &x, NULL );
+
+ // Get window size (may be different than the requested size)
+ glfwGetWindowSize( &width, &height );
+ height = height > 0 ? height : 1;
+
+ // Set viewport
+ glViewport( 0, 0, width, height );
+
+ // Clear color buffer
+ glClearColor( 0.0f, 0.0f, 0.0f, 0.0f);
+ glClear( GL_COLOR_BUFFER_BIT );
+
+ // Select and setup the projection matrix
+ glMatrixMode( GL_PROJECTION );
+ glLoadIdentity();
+ gluPerspective( 65.0f, (GLfloat)width / (GLfloat)height, 1.0f,
+ 50.0f );
+
+ // Select and setup the modelview matrix
+ glMatrixMode( GL_MODELVIEW );
+ glLoadIdentity();
+ gluLookAt( 0.0f, 3.0f, -20.0f, // Eye-position
+ 0.0f, -4.0f, -11.0f, // View-point
+ 0.0f, 1.0f, 0.0f ); // Up-vector
+
+ // Draw a textured quad
+ glRotatef( 0.05f * (GLfloat)x + (GLfloat)time * 5.0f, 0.0f, 1.0f, 0.0f );
+ glBegin( GL_QUADS );
+ glTexCoord2f( -20.0f, 20.0f );
+ glVertex3f( -50.0f, 0.0f, -50.0f );
+ glTexCoord2f( 20.0f, 20.0f );
+ glVertex3f( 50.0f, 0.0f, -50.0f );
+ glTexCoord2f( 20.0f, -20.0f );
+ glVertex3f( 50.0f, 0.0f, 50.0f );
+ glTexCoord2f( -20.0f, -20.0f );
+ glVertex3f( -50.0f, 0.0f, 50.0f );
+ glEnd();
+
+ // Swap buffers
+ glfwSwapBuffers();
+
+ // Check if the ESC key was pressed or the window was closed
+ running = !glfwGetKey( GLFW_KEY_ESC ) &&
+ glfwGetWindowParam( GLFW_OPENED );
+ }
+
+ // Close OpenGL window and terminate GLFW
+ glfwTerminate();
+
+ exit( EXIT_SUCCESS );
+}
+
diff --git a/tests/glfw/mipmaps.tga b/tests/glfw/mipmaps.tga
new file mode 100644
index 00000000..55f913b0
--- /dev/null
+++ b/tests/glfw/mipmaps.tga
Binary files differ
diff --git a/tests/glfw/mtbench.c b/tests/glfw/mtbench.c
new file mode 100644
index 00000000..a5c63ed1
--- /dev/null
+++ b/tests/glfw/mtbench.c
@@ -0,0 +1,301 @@
+//========================================================================
+// Multithreading benchmark program, based on the GLFW multi threading
+// support.
+//
+// This program can be used to get an idea of what to expect in terms of
+// multithreading granularity performance.
+//
+// As a "bonus", this program demonstrates how to create a signal
+// primitive using the GLFW mutex and condition variable primitives.
+//
+// Here are some benchmark results:
+// (Note: these are not exact measurments, since they are subject to
+// varying CPU-loads etc. Some tested systems are multi-user systems
+// which were running under anything but optimal conditions)
+//
+// +------------+-------+-------------+-------------------+------------+
+// | Processor | CPUs | OS | Context switches | Mean sleep |
+// | | | | per second | time (ms) |
+// +------------+-------+-------------+-------------------+------------+
+// |Athlon | 1 | Linux | 161942 | 20.000 |
+// |710 MHz | | 2.4.3 | | |
+// +------------+-------+-------------+-------------------+------------+
+// |Athlon | 1 | MS Win2k | 525230 | 10.014 |
+// |710 MHz | | | | |
+// +------------+-------+-------------+-------------------+------------+
+// |Athlon | 1 | MS Win 98 | 23564 | 4.947 |
+// |710 MHz | | | | |
+// +------------+-------+-------------+-------------------+------------+
+// |Pentium III | 1 | MS NT 4.0 | 304694 | 10.014 |
+// |500 MHz | | | | |
+// +------------+-------+-------------+-------------------+------------+
+// |UltraSPARC2 | 6 | SunOS 5.6 | 120867 | 19.355 |
+// |400 MHz | | | | |
+// +------------+-------+-------------+-------------------+------------+
+// |Alpha 21264 | 1 | OSF1 | 131993 | 3.097 |
+// |500 MHz | | | | |
+// +------------+-------+-------------+-------------------+------------+
+// |Alpha 21264 | 2 | OSF1 | 40836 | 1.397 |
+// |500 MHz | | | | |
+// +------------+-------+-------------+-------------------+------------+
+// |68020 (emu) | 1 | AmigaOS 3.1 | 50425 | 40.060 |
+// |~200 MHz | | (WinUAE) | | |
+// +------------+-------+-------------+-------------------+------------+
+//
+//========================================================================
+
+#include <stdio.h>
+#include <GL/glfw.h>
+
+
+typedef struct {
+ GLFWcond cond;
+ GLFWmutex mutex;
+ int flag;
+} signal_t;
+
+
+signal_t gotoA, gotoB;
+
+GLFWcond threadDone;
+GLFWmutex doneMutex;
+int doneCount;
+int gotoACount, gotoBCount;
+
+#define MAX_COUNT 10000
+
+
+//------------------------------------------------------------------------
+// InitSignal()
+//------------------------------------------------------------------------
+
+void InitSignal( signal_t *s )
+{
+ s->cond = glfwCreateCond();
+ s->mutex = glfwCreateMutex();
+ s->flag = 0;
+}
+
+
+//------------------------------------------------------------------------
+// KillSignal()
+//------------------------------------------------------------------------
+
+void KillSignal( signal_t *s )
+{
+ glfwDestroyCond( s->cond );
+ glfwDestroyMutex( s->mutex );
+ s->flag = 0;
+}
+
+
+//------------------------------------------------------------------------
+// WaitSignal()
+//------------------------------------------------------------------------
+
+void WaitSignal( signal_t *s )
+{
+ glfwLockMutex( s->mutex );
+ while( !s->flag )
+ {
+ glfwWaitCond( s->cond, s->mutex, GLFW_INFINITY );
+ }
+ s->flag = 0;
+ glfwUnlockMutex( s->mutex );
+}
+
+
+//------------------------------------------------------------------------
+// SetSignal()
+//------------------------------------------------------------------------
+
+void SetSignal( signal_t *s )
+{
+ glfwLockMutex( s->mutex );
+ s->flag = 1;
+ glfwUnlockMutex( s->mutex );
+ glfwSignalCond( s->cond );
+}
+
+
+//------------------------------------------------------------------------
+// threadAfun()
+//------------------------------------------------------------------------
+
+void GLFWCALL threadAfun( void * arg )
+{
+ int done;
+
+ do
+ {
+ done = (gotoACount >= MAX_COUNT);
+ if( !done )
+ {
+ gotoACount ++;
+ SetSignal( &gotoB );
+ WaitSignal( &gotoA );
+ }
+ }
+ while( !done );
+
+ SetSignal( &gotoB );
+
+ glfwLockMutex( doneMutex );
+ doneCount ++;
+ glfwUnlockMutex( doneMutex );
+ glfwSignalCond( threadDone );
+}
+
+
+//------------------------------------------------------------------------
+// threadBfun()
+//------------------------------------------------------------------------
+
+void GLFWCALL threadBfun( void * arg )
+{
+ int done;
+
+ do
+ {
+ done = (gotoBCount >= MAX_COUNT);
+ if( !done )
+ {
+ gotoBCount ++;
+ SetSignal( &gotoA );
+ WaitSignal( &gotoB );
+ }
+ }
+ while( !done );
+
+ SetSignal( &gotoA );
+
+ glfwLockMutex( doneMutex );
+ doneCount ++;
+ glfwUnlockMutex( doneMutex );
+ glfwSignalCond( threadDone );
+}
+
+
+
+//------------------------------------------------------------------------
+// main()
+//------------------------------------------------------------------------
+
+int main( void )
+{
+ GLFWthread threadA, threadB;
+ double t1, t2, csps;
+ int done, count, i;
+
+ gotoACount = gotoBCount = doneCount = 0;
+
+ // Initialize GLFW
+ if( !glfwInit() )
+ {
+ return 0;
+ }
+
+ // Print some program information
+ printf( "\nMultithreading benchmarking program\n" );
+ printf( "-----------------------------------\n\n" );
+ printf( "This program consists of two tests. In the first test " );
+ printf( "two threads are created,\n" );
+ printf( "which continously signal/wait each other. This forces " );
+ printf( "the execution to\n" );
+ printf( "alternate between the two threads, and gives a measure " );
+ printf( "of the thread\n" );
+ printf( "synchronization granularity. In the second test, the " );
+ printf( "main thread is repeatedly\n" );
+ printf( "put to sleep for a very short interval using glfwSleep. " );
+ printf( "The average sleep time\n" );
+ printf( "is measured, which tells the minimum supported sleep " );
+ printf( "interval.\n\n" );
+ printf( "Results:\n" );
+ printf( "--------\n\n" );
+ printf( "Number of CPUs: %d\n\n", glfwGetNumberOfProcessors() );
+ fflush( stdout );
+
+
+//------------------------------------------------------------------------
+// 1) Benchmark thread synchronization granularity
+//------------------------------------------------------------------------
+
+ // Init mutexes and conditions
+ doneMutex = glfwCreateMutex();
+ threadDone = glfwCreateCond();
+ InitSignal( &gotoA );
+ InitSignal( &gotoB );
+
+ // Create threads A & B
+ threadA = glfwCreateThread( threadAfun, NULL );
+ threadB = glfwCreateThread( threadBfun, NULL );
+ if( threadA == -1 || threadB == -1 )
+ {
+ glfwLockMutex( doneMutex );
+ doneCount = 2;
+ glfwUnlockMutex( doneMutex );
+ }
+
+ // Wait for both threads to be done
+ t1 = glfwGetTime();
+ glfwLockMutex( doneMutex );
+ do
+ {
+ done = (doneCount == 2);
+ if( !done )
+ {
+ glfwWaitCond( threadDone, doneMutex, GLFW_INFINITY );
+ }
+ }
+ while( !done );
+ glfwUnlockMutex( doneMutex );
+ t2 = glfwGetTime();
+
+ // Display results
+ count = gotoACount + gotoBCount;
+ csps = (double)count / (t2-t1);
+ printf( "Test 1: %.0f context switches / second (%.3f us/switch)\n",
+ csps, 1e6/csps );
+ fflush( stdout );
+
+ // Wait for threads to die
+ glfwWaitThread( threadA, GLFW_WAIT );
+ glfwWaitThread( threadB, GLFW_WAIT );
+
+ // Destroy mutexes and conditions
+ glfwDestroyMutex( doneMutex );
+ glfwDestroyCond( threadDone );
+ KillSignal( &gotoA );
+ KillSignal( &gotoB );
+
+
+//------------------------------------------------------------------------
+// 2) Benchmark thread sleep granularity
+//------------------------------------------------------------------------
+
+ // Find an initial estimate
+ t1 = glfwGetTime();
+ for( i = 0; i < 10; i ++ )
+ {
+ glfwSleep( 0.0001 );
+ }
+ t2 = glfwGetTime();
+
+ // Sleep for roughly 1 s
+ count = (int)(1.0 / ((t2-t1)/10.0));
+ t1 = glfwGetTime();
+ for( i = 0; i < count; i ++ )
+ {
+ glfwSleep( 0.0001 );
+ }
+ t2 = glfwGetTime();
+
+ // Display results
+ printf( "Test 2: %.3f ms / sleep (mean)\n\n",
+ 1000.0 * (t2-t1) / (double)count );
+
+ // Terminate GLFW
+ glfwTerminate();
+
+ return 0;
+}
diff --git a/tests/glfw/mthello.c b/tests/glfw/mthello.c
new file mode 100644
index 00000000..e12dea52
--- /dev/null
+++ b/tests/glfw/mthello.c
@@ -0,0 +1,48 @@
+//========================================================================
+// This is a small test application for GLFW.
+// The program prints "Hello world!", using two threads.
+//========================================================================
+
+#include <stdio.h>
+#include <GL/glfw.h>
+
+
+//========================================================================
+// HelloFun() - Thread function
+//========================================================================
+
+void GLFWCALL HelloFun( void *arg )
+{
+ // Print the first part of the message
+ printf( "Hello " );
+}
+
+
+//========================================================================
+// main() - Main function (main thread)
+//========================================================================
+
+int main( void )
+{
+ GLFWthread thread;
+
+ // Initialise GLFW
+ if( !glfwInit() )
+ {
+ return 0;
+ }
+
+ // Create thread
+ thread = glfwCreateThread( HelloFun, NULL );
+
+ // Wait for thread to die
+ glfwWaitThread( thread, GLFW_WAIT );
+
+ // Print the rest of the message
+ printf( "world!\n" );
+
+ // Terminate GLFW
+ glfwTerminate();
+
+ return 0;
+}
diff --git a/tests/glfw/particles.c b/tests/glfw/particles.c
new file mode 100644
index 00000000..403a9997
--- /dev/null
+++ b/tests/glfw/particles.c
@@ -0,0 +1,1152 @@
+//========================================================================
+// 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <GL/glfw.h>
+
+// 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 <math.h>'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( !glfwOpenWindow( 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/tests/glfw/pong3d.c b/tests/glfw/pong3d.c
new file mode 100644
index 00000000..1d1afd1f
--- /dev/null
+++ b/tests/glfw/pong3d.c
@@ -0,0 +1,854 @@
+//========================================================================
+// 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 <GL/glfw.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+
+//========================================================================
+// 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 );
+}
+
diff --git a/tests/glfw/pong3d_field.tga b/tests/glfw/pong3d_field.tga
new file mode 100644
index 00000000..cc20bbdb
--- /dev/null
+++ b/tests/glfw/pong3d_field.tga
Binary files differ
diff --git a/tests/glfw/pong3d_instr.tga b/tests/glfw/pong3d_instr.tga
new file mode 100644
index 00000000..758eb447
--- /dev/null
+++ b/tests/glfw/pong3d_instr.tga
Binary files differ
diff --git a/tests/glfw/pong3d_menu.tga b/tests/glfw/pong3d_menu.tga
new file mode 100644
index 00000000..d0d6c5a4
--- /dev/null
+++ b/tests/glfw/pong3d_menu.tga
Binary files differ
diff --git a/tests/glfw/pong3d_title.tga b/tests/glfw/pong3d_title.tga
new file mode 100644
index 00000000..d0d8e36d
--- /dev/null
+++ b/tests/glfw/pong3d_title.tga
Binary files differ
diff --git a/tests/glfw/pong3d_winner1.tga b/tests/glfw/pong3d_winner1.tga
new file mode 100644
index 00000000..f963720c
--- /dev/null
+++ b/tests/glfw/pong3d_winner1.tga
Binary files differ
diff --git a/tests/glfw/pong3d_winner2.tga b/tests/glfw/pong3d_winner2.tga
new file mode 100644
index 00000000..ea8266de
--- /dev/null
+++ b/tests/glfw/pong3d_winner2.tga
Binary files differ
diff --git a/tests/glfw/splitview.c b/tests/glfw/splitview.c
new file mode 100644
index 00000000..932cd0d6
--- /dev/null
+++ b/tests/glfw/splitview.c
@@ -0,0 +1,514 @@
+//========================================================================
+// This is an example program for the GLFW library
+//
+// The program uses a "split window" view, rendering four views of the
+// same scene in one window (e.g. uesful for 3D modelling software). This
+// demo uses scissors to separete the four different rendering areas from
+// each other.
+//
+// (If the code seems a little bit strange here and there, it may be
+// because I am not a friend of orthogonal projections)
+//========================================================================
+
+#include <GL/glfw.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+
+//========================================================================
+// Global variables
+//========================================================================
+
+// Mouse position
+static int xpos = 0, ypos = 0;
+
+// Window size
+static int width, height;
+
+// Active view: 0 = none, 1 = upper left, 2 = upper right, 3 = lower left,
+// 4 = lower right
+static int active_view = 0;
+
+// Rotation around each axis
+static int rot_x = 0, rot_y = 0, rot_z = 0;
+
+// Do redraw?
+static int do_redraw = 1;
+
+
+//========================================================================
+// Draw a solid torus (use a display list for the model)
+//========================================================================
+
+#define TORUS_MAJOR 1.5
+#define TORUS_MINOR 0.5
+#define TORUS_MAJOR_RES 32
+#define TORUS_MINOR_RES 32
+
+static void drawTorus( void )
+{
+ static GLuint torus_list = 0;
+ int i, j, k;
+ double s, t, x, y, z, nx, ny, nz, scale, twopi;
+
+ if( !torus_list )
+ {
+ // Start recording displaylist
+ torus_list = glGenLists( 1 );
+ glNewList( torus_list, GL_COMPILE_AND_EXECUTE );
+
+ // Draw torus
+ twopi = 2.0 * M_PI;
+ for( i = 0; i < TORUS_MINOR_RES; i++ )
+ {
+ glBegin( GL_QUAD_STRIP );
+ for( j = 0; j <= TORUS_MAJOR_RES; j++ )
+ {
+ for( k = 1; k >= 0; k-- )
+ {
+ s = (i + k) % TORUS_MINOR_RES + 0.5;
+ t = j % TORUS_MAJOR_RES;
+
+ // Calculate point on surface
+ x = (TORUS_MAJOR+TORUS_MINOR*cos(s*twopi/TORUS_MINOR_RES))*cos(t*twopi/TORUS_MAJOR_RES);
+ y = TORUS_MINOR * sin(s * twopi / TORUS_MINOR_RES);
+ z = (TORUS_MAJOR+TORUS_MINOR*cos(s*twopi/TORUS_MINOR_RES))*sin(t*twopi/TORUS_MAJOR_RES);
+
+ // Calculate surface normal
+ nx = x - TORUS_MAJOR*cos(t*twopi/TORUS_MAJOR_RES);
+ ny = y;
+ nz = z - TORUS_MAJOR*sin(t*twopi/TORUS_MAJOR_RES);
+ scale = 1.0 / sqrt( nx*nx + ny*ny + nz*nz );
+ nx *= scale;
+ ny *= scale;
+ nz *= scale;
+
+ glNormal3f( (float)nx, (float)ny, (float)nz );
+ glVertex3f( (float)x, (float)y, (float)z );
+ }
+ }
+ glEnd();
+ }
+
+ // Stop recording displaylist
+ glEndList();
+ }
+ else
+ {
+ // Playback displaylist
+ glCallList( torus_list );
+ }
+}
+
+
+//========================================================================
+// Draw the scene (a rotating torus)
+//========================================================================
+
+static void drawScene( void )
+{
+ const GLfloat model_diffuse[4] = {1.0f, 0.8f, 0.8f, 1.0f};
+ const GLfloat model_specular[4] = {0.6f, 0.6f, 0.6f, 1.0f};
+ const GLfloat model_shininess = 20.0f;
+
+ glPushMatrix();
+
+ // Rotate the object
+ glRotatef( (GLfloat)rot_x*0.5f, 1.0f, 0.0f, 0.0f );
+ glRotatef( (GLfloat)rot_y*0.5f, 0.0f, 1.0f, 0.0f );
+ glRotatef( (GLfloat)rot_z*0.5f, 0.0f, 0.0f, 1.0f );
+
+ // Set model color (used for orthogonal views, lighting disabled)
+ glColor4fv( model_diffuse );
+
+ // Set model material (used for perspective view, lighting enabled)
+ glMaterialfv( GL_FRONT, GL_DIFFUSE, model_diffuse );
+ glMaterialfv( GL_FRONT, GL_SPECULAR, model_specular );
+ glMaterialf( GL_FRONT, GL_SHININESS, model_shininess );
+
+ // Draw torus
+ drawTorus();
+
+ glPopMatrix();
+}
+
+
+//========================================================================
+// Draw a 2D grid (used for orthogonal views)
+//========================================================================
+
+static void drawGrid( float scale, int steps )
+{
+ int i;
+ float x, y;
+
+ glPushMatrix();
+
+ // Set background to some dark bluish grey
+ glClearColor( 0.05f, 0.05f, 0.2f, 0.0f);
+ glClear( GL_COLOR_BUFFER_BIT );
+
+ // Setup modelview matrix (flat XY view)
+ glLoadIdentity();
+ gluLookAt( 0.0, 0.0, 1.0,
+ 0.0, 0.0, 0.0,
+ 0.0, 1.0, 0.0 );
+
+ // We don't want to update the Z-buffer
+ glDepthMask( GL_FALSE );
+
+ // Set grid color
+ glColor3f( 0.0f, 0.5f, 0.5f );
+
+ glBegin( GL_LINES );
+
+ // Horizontal lines
+ x = scale * 0.5f * (float)(steps-1);
+ y = -scale * 0.5f * (float)(steps-1);
+ for( i = 0; i < steps; i ++ )
+ {
+ glVertex3f( -x, y, 0.0f );
+ glVertex3f( x, y, 0.0f );
+ y += scale;
+ }
+
+ // Vertical lines
+ x = -scale * 0.5f * (float)(steps-1);
+ y = scale * 0.5f * (float)(steps-1);
+ for( i = 0; i < steps; i ++ )
+ {
+ glVertex3f( x, -y, 0.0f );
+ glVertex3f( x, y, 0.0f );
+ x += scale;
+ }
+
+ glEnd();
+
+ // Enable Z-buffer writing again
+ glDepthMask( GL_TRUE );
+
+ glPopMatrix();
+}
+
+
+//========================================================================
+// Draw all views
+//========================================================================
+
+static void drawAllViews( void )
+{
+ const GLfloat light_position[4] = {0.0f, 8.0f, 8.0f, 1.0f};
+ const GLfloat light_diffuse[4] = {1.0f, 1.0f, 1.0f, 1.0f};
+ const GLfloat light_specular[4] = {1.0f, 1.0f, 1.0f, 1.0f};
+ const GLfloat light_ambient[4] = {0.2f, 0.2f, 0.3f, 1.0f};
+ double aspect;
+
+ // Calculate aspect of window
+ if( height > 0 )
+ {
+ aspect = (double)width / (double)height;
+ }
+ else
+ {
+ aspect = 1.0;
+ }
+
+ // Clear screen
+ glClearColor( 0.0f, 0.0f, 0.0f, 0.0f);
+ glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
+
+ // Enable scissor test
+ glEnable( GL_SCISSOR_TEST );
+
+ // Enable depth test
+ glEnable( GL_DEPTH_TEST );
+ glDepthFunc( GL_LEQUAL );
+
+
+ // ** ORTHOGONAL VIEWS **
+
+ // For orthogonal views, use wireframe rendering
+ glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
+
+ // Enable line anti-aliasing
+ glEnable( GL_LINE_SMOOTH );
+ glEnable( GL_BLEND );
+ glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
+
+ // Setup orthogonal projection matrix
+ glMatrixMode( GL_PROJECTION );
+ glLoadIdentity();
+ glOrtho( -3.0*aspect, 3.0*aspect, -3.0, 3.0, 1.0, 50.0 );
+
+ // Upper left view (TOP VIEW)
+ glViewport( 0, height/2, width/2, height/2 );
+ glScissor( 0, height/2, width/2, height/2 );
+ glMatrixMode( GL_MODELVIEW );
+ glLoadIdentity();
+ gluLookAt( 0.0f, 10.0f, 1e-3f, // Eye-position (above)
+ 0.0f, 0.0f, 0.0f, // View-point
+ 0.0f, 1.0f, 0.0f ); // Up-vector
+ drawGrid( 0.5, 12 );
+ drawScene();
+
+ // Lower left view (FRONT VIEW)
+ glViewport( 0, 0, width/2, height/2 );
+ glScissor( 0, 0, width/2, height/2 );
+ glMatrixMode( GL_MODELVIEW );
+ glLoadIdentity();
+ gluLookAt( 0.0f, 0.0f, 10.0f, // Eye-position (in front of)
+ 0.0f, 0.0f, 0.0f, // View-point
+ 0.0f, 1.0f, 0.0f ); // Up-vector
+ drawGrid( 0.5, 12 );
+ drawScene();
+
+ // Lower right view (SIDE VIEW)
+ glViewport( width/2, 0, width/2, height/2 );
+ glScissor( width/2, 0, width/2, height/2 );
+ glMatrixMode( GL_MODELVIEW );
+ glLoadIdentity();
+ gluLookAt( 10.0f, 0.0f, 0.0f, // Eye-position (to the right)
+ 0.0f, 0.0f, 0.0f, // View-point
+ 0.0f, 1.0f, 0.0f ); // Up-vector
+ drawGrid( 0.5, 12 );
+ drawScene();
+
+ // Disable line anti-aliasing
+ glDisable( GL_LINE_SMOOTH );
+ glDisable( GL_BLEND );
+
+
+ // ** PERSPECTIVE VIEW **
+
+ // For perspective view, use solid rendering
+ glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
+
+ // Enable face culling (faster rendering)
+ glEnable( GL_CULL_FACE );
+ glCullFace( GL_BACK );
+ glFrontFace( GL_CW );
+
+ // Setup perspective projection matrix
+ glMatrixMode( GL_PROJECTION );
+ glLoadIdentity();
+ gluPerspective( 65.0f, aspect, 1.0f, 50.0f );
+
+ // Upper right view (PERSPECTIVE VIEW)
+ glViewport( width/2, height/2, width/2, height/2 );
+ glScissor( width/2, height/2, width/2, height/2 );
+ glMatrixMode( GL_MODELVIEW );
+ glLoadIdentity();
+ gluLookAt( 3.0f, 1.5f, 3.0f, // Eye-position
+ 0.0f, 0.0f, 0.0f, // View-point
+ 0.0f, 1.0f, 0.0f ); // Up-vector
+
+ // Configure and enable light source 1
+ glLightfv( GL_LIGHT1, GL_POSITION, light_position );
+ glLightfv( GL_LIGHT1, GL_AMBIENT, light_ambient );
+ glLightfv( GL_LIGHT1, GL_DIFFUSE, light_diffuse );
+ glLightfv( GL_LIGHT1, GL_SPECULAR, light_specular );
+ glEnable( GL_LIGHT1 );
+ glEnable( GL_LIGHTING );
+
+ // Draw scene
+ drawScene();
+
+ // Disable lighting
+ glDisable( GL_LIGHTING );
+
+ // Disable face culling
+ glDisable( GL_CULL_FACE );
+
+ // Disable depth test
+ glDisable( GL_DEPTH_TEST );
+
+ // Disable scissor test
+ glDisable( GL_SCISSOR_TEST );
+
+
+ // Draw a border around the active view
+ if( active_view > 0 && active_view != 2 )
+ {
+ glViewport( 0, 0, width, height );
+ glMatrixMode( GL_PROJECTION );
+ glLoadIdentity();
+ glOrtho( 0.0, 2.0, 0.0, 2.0, 0.0, 1.0 );
+ glMatrixMode( GL_MODELVIEW );
+ glLoadIdentity();
+ glColor3f( 1.0f, 1.0f, 0.6f );
+ glTranslatef( (GLfloat) ((active_view - 1) & 1), (GLfloat) (1 - (active_view - 1) / 2), 0.0f );
+ glBegin( GL_LINE_STRIP );
+ glVertex2i( 0, 0 );
+ glVertex2i( 1, 0 );
+ glVertex2i( 1, 1 );
+ glVertex2i( 0, 1 );
+ glVertex2i( 0, 0 );
+ glEnd();
+ }
+}
+
+
+//========================================================================
+// Window size callback function
+//========================================================================
+
+static void GLFWCALL windowSizeFun( int w, int h )
+{
+ width = w;
+ height = h > 0 ? h : 1;
+ do_redraw = 1;
+}
+
+
+//========================================================================
+// Window refresh callback function
+//========================================================================
+
+static void GLFWCALL windowRefreshFun( void )
+{
+ do_redraw = 1;
+}
+
+
+//========================================================================
+// Mouse position callback function
+//========================================================================
+
+static void GLFWCALL mousePosFun( int x, int y )
+{
+ // Depending on which view was selected, rotate around different axes
+ switch( active_view )
+ {
+ case 1:
+ rot_x += y - ypos;
+ rot_z += x - xpos;
+ do_redraw = 1;
+ break;
+ case 3:
+ rot_x += y - ypos;
+ rot_y += x - xpos;
+ do_redraw = 1;
+ break;
+ case 4:
+ rot_y += x - xpos;
+ rot_z += y - ypos;
+ do_redraw = 1;
+ break;
+ default:
+ // Do nothing for perspective view, or if no view is selected
+ break;
+ }
+
+ // Remember mouse position
+ xpos = x;
+ ypos = y;
+}
+
+
+//========================================================================
+// Mouse button callback function
+//========================================================================
+
+static void GLFWCALL mouseButtonFun( int button, int action )
+{
+ // Button clicked?
+ if( ( button == GLFW_MOUSE_BUTTON_LEFT ) && action == GLFW_PRESS )
+ {
+ // Detect which of the four views was clicked
+ active_view = 1;
+ if( xpos >= width/2 )
+ {
+ active_view += 1;
+ }
+ if( ypos >= height/2 )
+ {
+ active_view += 2;
+ }
+ }
+
+ // Button released?
+ else if( button == GLFW_MOUSE_BUTTON_LEFT )
+ {
+ // Deselect any previously selected view
+ active_view = 0;
+ }
+
+ do_redraw = 1;
+}
+
+
+//========================================================================
+// main()
+//========================================================================
+
+int main( void )
+{
+ // Initialise GLFW
+ if( !glfwInit() )
+ {
+ fprintf( stderr, "Failed to initialize GLFW\n" );
+ exit( EXIT_FAILURE );
+ }
+
+ // Open OpenGL window
+ if( !glfwOpenWindow( 500, 500, 0,0,0,0, 16,0, GLFW_WINDOW ) )
+ {
+ fprintf( stderr, "Failed to open GLFW window\n" );
+ glfwTerminate();
+ exit( EXIT_FAILURE );
+ }
+
+ // Enable vsync
+ glfwSwapInterval( 1 );
+
+ // Set window title
+ glfwSetWindowTitle( "Split view demo" );
+
+ // Enable sticky keys
+ glfwEnable( GLFW_STICKY_KEYS );
+
+ // Enable mouse cursor (only needed for fullscreen mode)
+ glfwEnable( GLFW_MOUSE_CURSOR );
+
+ // Disable automatic event polling
+ glfwDisable( GLFW_AUTO_POLL_EVENTS );
+
+ // Set callback functions
+ glfwSetWindowSizeCallback( windowSizeFun );
+ glfwSetWindowRefreshCallback( windowRefreshFun );
+ glfwSetMousePosCallback( mousePosFun );
+ glfwSetMouseButtonCallback( mouseButtonFun );
+
+ // Main loop
+ do
+ {
+ // Only redraw if we need to
+ if( do_redraw )
+ {
+ // Draw all views
+ drawAllViews();
+
+ // Swap buffers
+ glfwSwapBuffers();
+
+ do_redraw = 0;
+ }
+
+ // Wait for new events
+ glfwWaitEvents();
+
+ } // Check if the ESC key was pressed or the window was closed
+ while( glfwGetKey( GLFW_KEY_ESC ) != GLFW_PRESS &&
+ glfwGetWindowParam( GLFW_OPENED ) );
+
+ // Close OpenGL window and terminate GLFW
+ glfwTerminate();
+
+ exit( EXIT_SUCCESS );
+}
+
diff --git a/tests/glfw/triangle.c b/tests/glfw/triangle.c
new file mode 100644
index 00000000..a8b737be
--- /dev/null
+++ b/tests/glfw/triangle.c
@@ -0,0 +1,94 @@
+//========================================================================
+// This is a small test application for GLFW.
+// The program opens a window (640x480), and renders a spinning colored
+// triangle (it is controlled with both the GLFW timer and the mouse).
+//========================================================================
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <GL/glfw.h>
+
+
+int main( void )
+{
+ int width, height, x;
+ double t;
+
+ // Initialise GLFW
+ if( !glfwInit() )
+ {
+ fprintf( stderr, "Failed to initialize GLFW\n" );
+ exit( EXIT_FAILURE );
+ }
+
+ // Open a window and create its OpenGL context
+ if( !glfwOpenWindow( 640, 480, 0,0,0,0, 0,0, GLFW_WINDOW ) )
+ {
+ fprintf( stderr, "Failed to open GLFW window\n" );
+
+ glfwTerminate();
+ exit( EXIT_FAILURE );
+ }
+
+ glfwSetWindowTitle( "Spinning Triangle" );
+
+ // Ensure we can capture the escape key being pressed below
+ glfwEnable( GLFW_STICKY_KEYS );
+
+ // Enable vertical sync (on cards that support it)
+ glfwSwapInterval( 1 );
+
+ do
+ {
+ t = glfwGetTime();
+ glfwGetMousePos( &x, NULL );
+
+ // Get window size (may be different than the requested size)
+ glfwGetWindowSize( &width, &height );
+
+ // Special case: avoid division by zero below
+ height = height > 0 ? height : 1;
+
+ glViewport( 0, 0, width, height );
+
+ // Clear color buffer to black
+ glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
+ glClear( GL_COLOR_BUFFER_BIT );
+
+ // Select and setup the projection matrix
+ glMatrixMode( GL_PROJECTION );
+ glLoadIdentity();
+ gluPerspective( 65.0f, (GLfloat)width/(GLfloat)height, 1.0f, 100.0f );
+
+ // Select and setup the modelview matrix
+ glMatrixMode( GL_MODELVIEW );
+ glLoadIdentity();
+ gluLookAt( 0.0f, 1.0f, 0.0f, // Eye-position
+ 0.0f, 20.0f, 0.0f, // View-point
+ 0.0f, 0.0f, 1.0f ); // Up-vector
+
+ // Draw a rotating colorful triangle
+ glTranslatef( 0.0f, 14.0f, 0.0f );
+ glRotatef( 0.3f*(GLfloat)x + (GLfloat)t*100.0f, 0.0f, 0.0f, 1.0f );
+ glBegin( GL_TRIANGLES );
+ glColor3f( 1.0f, 0.0f, 0.0f );
+ glVertex3f( -5.0f, 0.0f, -4.0f );
+ glColor3f( 0.0f, 1.0f, 0.0f );
+ glVertex3f( 5.0f, 0.0f, -4.0f );
+ glColor3f( 0.0f, 0.0f, 1.0f );
+ glVertex3f( 0.0f, 0.0f, 6.0f );
+ glEnd();
+
+ // Swap buffers
+ glfwSwapBuffers();
+
+ } // Check if the ESC key was pressed or the window was closed
+ while( glfwGetKey( GLFW_KEY_ESC ) != GLFW_PRESS &&
+ glfwGetWindowParam( GLFW_OPENED ) );
+
+ // Close OpenGL window and terminate GLFW
+ glfwTerminate();
+
+ exit( EXIT_SUCCESS );
+}
+
diff --git a/tests/glfw/wave.c b/tests/glfw/wave.c
new file mode 100644
index 00000000..67f516cc
--- /dev/null
+++ b/tests/glfw/wave.c
@@ -0,0 +1,399 @@
+/*****************************************************************************
+ * Wave Simulation in OpenGL
+ * (C) 2002 Jakob Thomsen
+ * http://home.in.tum.de/~thomsen
+ * Modified for GLFW by Sylvain Hellegouarch - sh@programmationworld.com
+ * Modified for variable frame rate by Marcus Geelnard
+ * 2003-Jan-31: Minor cleanups and speedups / MG
+ *****************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <GL/glfw.h>
+
+#ifndef M_PI
+ #define M_PI 3.1415926535897932384626433832795
+#endif
+
+/* Maximum delta T to allow for differential calculations */
+#define MAX_DELTA_T 0.01
+
+/* Animation speed (10.0 looks good) */
+#define ANIMATION_SPEED 10.0
+
+
+GLfloat alpha = 210.0f, beta = -70.0f;
+GLfloat zoom = 2.0f;
+
+int running = 1;
+
+struct Vertex
+{
+ GLfloat x,y,z;
+ GLfloat r,g,b;
+};
+
+#define GRIDW 50
+#define GRIDH 50
+#define VERTEXNUM (GRIDW*GRIDH)
+
+#define QUADW (GRIDW-1)
+#define QUADH (GRIDH-1)
+#define QUADNUM (QUADW*QUADH)
+
+GLuint quad[4*QUADNUM];
+struct Vertex vertex[VERTEXNUM];
+
+/* The grid will look like this:
+ *
+ * 3 4 5
+ * *---*---*
+ * | | |
+ * | 0 | 1 |
+ * | | |
+ * *---*---*
+ * 0 1 2
+ */
+
+void initVertices( void )
+{
+ int x,y,p;
+
+ /* place the vertices in a grid */
+ for(y=0;y<GRIDH;y++)
+ for(x=0;x<GRIDW;x++)
+ {
+ p = y*GRIDW + x;
+
+ //vertex[p].x = (-GRIDW/2)+x+sin(2.0*M_PI*(double)y/(double)GRIDH);
+ //vertex[p].y = (-GRIDH/2)+y+cos(2.0*M_PI*(double)x/(double)GRIDW);
+ vertex[p].x = (GLfloat)(x-GRIDW/2)/(GLfloat)(GRIDW/2);
+ vertex[p].y = (GLfloat)(y-GRIDH/2)/(GLfloat)(GRIDH/2);
+ vertex[p].z = 0;//sin(d*M_PI);
+ //vertex[p].r = (GLfloat)x/(GLfloat)GRIDW;
+ //vertex[p].g = (GLfloat)y/(GLfloat)GRIDH;
+ //vertex[p].b = 1.0-((GLfloat)x/(GLfloat)GRIDW+(GLfloat)y/(GLfloat)GRIDH)/2.0;
+ if((x%4<2)^(y%4<2))
+ {
+ vertex[p].r = 0.0;
+ }
+ else
+ {
+ vertex[p].r=1.0;
+ }
+
+ vertex[p].g = (GLfloat)y/(GLfloat)GRIDH;
+ vertex[p].b = 1.f-((GLfloat)x/(GLfloat)GRIDW+(GLfloat)y/(GLfloat)GRIDH)/2.f;
+ }
+
+ for(y=0;y<QUADH;y++)
+ for(x=0;x<QUADW;x++)
+ {
+ p = 4*(y*QUADW + x);
+
+ /* first quad */
+ quad[p+0] = y *GRIDW+x; /* some point */
+ quad[p+1] = y *GRIDW+x+1; /* neighbor at the right side */
+ quad[p+2] = (y+1)*GRIDW+x+1; /* upper right neighbor */
+ quad[p+3] = (y+1)*GRIDW+x; /* upper neighbor */
+ }
+}
+
+double dt;
+double p[GRIDW][GRIDH];
+double vx[GRIDW][GRIDH], vy[GRIDW][GRIDH];
+double ax[GRIDW][GRIDH], ay[GRIDW][GRIDH];
+
+
+
+void initSurface( void )
+{
+ int x, y;
+ double dx, dy, d;
+
+ for(y = 0; y<GRIDH; y++)
+ {
+ for(x = 0; x<GRIDW; x++)
+ {
+ dx = (double)(x-GRIDW/2);
+ dy = (double)(y-GRIDH/2);
+ d = sqrt( dx*dx + dy*dy );
+ if(d < 0.1 * (double)(GRIDW/2))
+ {
+ d = d * 10.0;
+ p[x][y] = -cos(d * (M_PI / (double)(GRIDW * 4))) * 100.0;
+ }
+ else
+ {
+ p[x][y] = 0.0;
+ }
+ vx[x][y] = 0.0;
+ vy[x][y] = 0.0;
+ }
+ }
+}
+
+
+/* Draw view */
+void draw_screen( void )
+{
+ /* Clear the color and depth buffers. */
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ /* We don't want to modify the projection matrix. */
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+
+ /* Move back. */
+ glTranslatef(0.0, 0.0, -zoom);
+ /* Rotate the view */
+ glRotatef(beta, 1.0, 0.0, 0.0);
+ glRotatef(alpha, 0.0, 0.0, 1.0);
+
+ //glDrawArrays(GL_POINTS,0,VERTEXNUM); /* Points only */
+ glDrawElements(GL_QUADS, 4*QUADNUM, GL_UNSIGNED_INT, quad);
+ //glDrawElements(GL_LINES, QUADNUM, GL_UNSIGNED_INT, quad);
+
+ glfwSwapBuffers();
+}
+
+
+/* Initialize OpenGL */
+void setup_opengl( void )
+{
+ /* Our shading model--Gouraud (smooth). */
+ glShadeModel(GL_SMOOTH);
+
+ /* Culling. */
+ //glCullFace(GL_BACK);
+ //glFrontFace(GL_CCW);
+ //glEnable(GL_CULL_FACE);
+
+ /* Switch on the z-buffer. */
+ glEnable(GL_DEPTH_TEST);
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_COLOR_ARRAY);
+ glVertexPointer(3/*3 components per vertex (x,y,z)*/, GL_FLOAT, sizeof(struct Vertex), vertex);
+ glColorPointer(3/*3 components per vertex (r,g,b)*/, GL_FLOAT, sizeof(struct Vertex), &vertex[0].r); //Pointer to the first color
+ glPointSize(2.0);
+
+ /* Background color is black. */
+ glClearColor(0, 0, 0, 0);
+}
+
+
+/* Modify the height of each vertex according to the pressure. */
+void adjustGrid( void )
+{
+ int pos;
+ int x, y;
+
+ for(y = 0; y<GRIDH; y++)
+ {
+ for(x = 0; x<GRIDW; x++)
+ {
+ pos = y*GRIDW + x;
+ vertex[pos].z = (float) (p[x][y]*(1.0/50.0));
+ }
+ }
+}
+
+
+/* Calculate wave propagation */
+void calc( void )
+{
+ int x, y, x2, y2;
+ double time_step = dt * ANIMATION_SPEED;
+
+ /* compute accelerations */
+ for(x = 0; x < GRIDW; x++)
+ {
+ x2 = (x + 1) % GRIDW;
+ for(y = 0; y < GRIDH; y++)
+ {
+ ax[x][y] = p[x][y] - p[x2][y];
+ }
+ }
+
+ for(y = 0; y < GRIDH;y++)
+ {
+ y2 = (y + 1) % GRIDH;
+ for(x = 0; x < GRIDW; x++)
+ {
+ ay[x][y] = p[x][y] - p[x][y2];
+ }
+ }
+
+ /* compute speeds */
+ for(x = 0; x < GRIDW; x++)
+ {
+ for(y = 0; y < GRIDH; y++)
+ {
+ vx[x][y] = vx[x][y] + ax[x][y] * time_step;
+ vy[x][y] = vy[x][y] + ay[x][y] * time_step;
+ }
+ }
+
+ /* compute pressure */
+ for(x = 1; x < GRIDW; x++)
+ {
+ x2 = x - 1;
+ for(y = 1; y < GRIDH; y++)
+ {
+ y2 = y - 1;
+ p[x][y] = p[x][y] + (vx[x2][y] - vx[x][y] + vy[x][y2] - vy[x][y]) * time_step;
+ }
+ }
+}
+
+
+/* Handle key strokes */
+void GLFWCALL handle_key_down(int key, int action)
+{
+ if( action != GLFW_PRESS )
+ {
+ return;
+ }
+
+ switch(key) {
+ case GLFW_KEY_ESC:
+ running = 0;
+ break;
+ case GLFW_KEY_SPACE:
+ initSurface();
+ break;
+ case GLFW_KEY_LEFT:
+ alpha+=5;
+ break;
+ case GLFW_KEY_RIGHT:
+ alpha-=5;
+ break;
+ case GLFW_KEY_UP:
+ beta-=5;
+ break;
+ case GLFW_KEY_DOWN:
+ beta+=5;
+ break;
+ case GLFW_KEY_PAGEUP:
+ if(zoom>1) zoom-=1;
+ break;
+ case GLFW_KEY_PAGEDOWN:
+ zoom+=1;
+ break;
+ default:
+ break;
+ }
+}
+
+
+/* Callback function for window resize events */
+void GLFWCALL handle_resize( int width, int height )
+{
+ float ratio = 1.0f;
+
+ if( height > 0 )
+ {
+ ratio = (float) width / (float) height;
+ }
+
+ /* Setup viewport (Place where the stuff will appear in the main window). */
+ glViewport(0, 0, width, height);
+
+ /*
+ * Change to the projection matrix and set
+ * our viewing volume.
+ */
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ gluPerspective(60.0, ratio, 1.0, 1024.0);
+}
+
+
+/* Program entry point */
+int main(int argc, char* argv[])
+{
+ /* Dimensions of our window. */
+ int width, height;
+ /* Style of our window. */
+ int mode;
+ /* Frame time */
+ double t, t_old, dt_total;
+
+ /* Initialize GLFW */
+ if(glfwInit() == GL_FALSE)
+ {
+ fprintf(stderr, "GLFW initialization failed\n");
+ exit(-1);
+ }
+
+ /* Desired window properties */
+ width = 640;
+ height = 480;
+ mode = GLFW_WINDOW;
+
+ /* Open window */
+ if( glfwOpenWindow(width,height,0,0,0,0,16,0,mode) == GL_FALSE )
+ {
+ fprintf(stderr, "Could not open window\n");
+ glfwTerminate();
+ exit(-1);
+ }
+
+ /* Set title */
+ glfwSetWindowTitle( "Wave Simulation" );
+
+ glfwSwapInterval( 1 );
+
+ /* Keyboard handler */
+ glfwSetKeyCallback( handle_key_down );
+ glfwEnable( GLFW_KEY_REPEAT );
+
+ /* Window resize handler */
+ glfwSetWindowSizeCallback( handle_resize );
+
+ /* Initialize OpenGL */
+ setup_opengl();
+
+ /* Initialize simulation */
+ initVertices();
+ initSurface();
+ adjustGrid();
+
+ /* Initialize timer */
+ t_old = glfwGetTime() - 0.01;
+
+ /* Main loop */
+ while(running)
+ {
+ /* Timing */
+ t = glfwGetTime();
+ dt_total = t - t_old;
+ t_old = t;
+
+ /* Safety - iterate if dt_total is too large */
+ while( dt_total > 0.0f )
+ {
+ /* Select iteration time step */
+ dt = dt_total > MAX_DELTA_T ? MAX_DELTA_T : dt_total;
+ dt_total -= dt;
+
+ /* Calculate wave propagation */
+ calc();
+ }
+
+ /* Compute height of each vertex */
+ adjustGrid();
+
+ /* Draw wave grid to OpenGL display */
+ draw_screen();
+
+ /* Still running? */
+ running = running && glfwGetWindowParam( GLFW_OPENED );
+ }
+
+ glfwTerminate();
+
+ return 0;
+}