diff options
-rw-r--r-- | src/library_glfw.js | 1 | ||||
-rw-r--r-- | tests/glfw/Makefile | 56 | ||||
-rw-r--r-- | tests/glfw/boing.c | 615 | ||||
-rw-r--r-- | tests/glfw/bundle.sh | 46 | ||||
-rw-r--r-- | tests/glfw/gears.c | 373 | ||||
-rw-r--r-- | tests/glfw/getopt.c | 253 | ||||
-rw-r--r-- | tests/glfw/getopt.h | 63 | ||||
-rw-r--r-- | tests/glfw/heightmap.c | 850 | ||||
-rw-r--r-- | tests/glfw/listmodes.c | 48 | ||||
-rw-r--r-- | tests/glfw/mipmaps.c | 122 | ||||
-rw-r--r-- | tests/glfw/mipmaps.tga | bin | 0 -> 66322 bytes | |||
-rw-r--r-- | tests/glfw/mtbench.c | 301 | ||||
-rw-r--r-- | tests/glfw/mthello.c | 48 | ||||
-rw-r--r-- | tests/glfw/particles.c | 1152 | ||||
-rw-r--r-- | tests/glfw/pong3d.c | 854 | ||||
-rw-r--r-- | tests/glfw/pong3d_field.tga | bin | 0 -> 17816 bytes | |||
-rw-r--r-- | tests/glfw/pong3d_instr.tga | bin | 0 -> 21279 bytes | |||
-rw-r--r-- | tests/glfw/pong3d_menu.tga | bin | 0 -> 1835 bytes | |||
-rw-r--r-- | tests/glfw/pong3d_title.tga | bin | 0 -> 106516 bytes | |||
-rw-r--r-- | tests/glfw/pong3d_winner1.tga | bin | 0 -> 861 bytes | |||
-rw-r--r-- | tests/glfw/pong3d_winner2.tga | bin | 0 -> 891 bytes | |||
-rw-r--r-- | tests/glfw/splitview.c | 514 | ||||
-rw-r--r-- | tests/glfw/triangle.c | 94 | ||||
-rw-r--r-- | tests/glfw/wave.c | 399 |
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(¢er_x, ¢er_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 Binary files differnew file mode 100644 index 00000000..55f913b0 --- /dev/null +++ b/tests/glfw/mipmaps.tga 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 Binary files differnew file mode 100644 index 00000000..cc20bbdb --- /dev/null +++ b/tests/glfw/pong3d_field.tga diff --git a/tests/glfw/pong3d_instr.tga b/tests/glfw/pong3d_instr.tga Binary files differnew file mode 100644 index 00000000..758eb447 --- /dev/null +++ b/tests/glfw/pong3d_instr.tga diff --git a/tests/glfw/pong3d_menu.tga b/tests/glfw/pong3d_menu.tga Binary files differnew file mode 100644 index 00000000..d0d6c5a4 --- /dev/null +++ b/tests/glfw/pong3d_menu.tga diff --git a/tests/glfw/pong3d_title.tga b/tests/glfw/pong3d_title.tga Binary files differnew file mode 100644 index 00000000..d0d8e36d --- /dev/null +++ b/tests/glfw/pong3d_title.tga diff --git a/tests/glfw/pong3d_winner1.tga b/tests/glfw/pong3d_winner1.tga Binary files differnew file mode 100644 index 00000000..f963720c --- /dev/null +++ b/tests/glfw/pong3d_winner1.tga diff --git a/tests/glfw/pong3d_winner2.tga b/tests/glfw/pong3d_winner2.tga Binary files differnew file mode 100644 index 00000000..ea8266de --- /dev/null +++ b/tests/glfw/pong3d_winner2.tga 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; +} |