diff options
author | Roland Scheidegger <rscheidegger@gmx.ch> | 2004-06-25 13:49:46 +0000 |
---|---|---|
committer | Roland Scheidegger <rscheidegger@gmx.ch> | 2004-06-25 13:49:46 +0000 |
commit | a09db0a7fa38c8161374ae4df3aa375efd5acbb0 (patch) | |
tree | 3c8a583aae114adf14236d4086a1434497b2a8d7 /progs | |
parent | 9a47d2b7cfd18dc86f2b12eb4ce7249cb5a10f9d (diff) |
new test which uses mixed texgen/non-texgen texture coordinates to exhibit potential bugs in hardware drivers
Diffstat (limited to 'progs')
-rw-r--r-- | progs/tests/texgenmix.c | 640 |
1 files changed, 640 insertions, 0 deletions
diff --git a/progs/tests/texgenmix.c b/progs/tests/texgenmix.c new file mode 100644 index 0000000000..f8942f1d19 --- /dev/null +++ b/progs/tests/texgenmix.c @@ -0,0 +1,640 @@ + +/* + * Demonstrates mixed texgen/non-texgen texture coordinates. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <GL/glut.h> + +#undef max +#undef min +#define max( a, b ) ((a) >= (b) ? (a) : (b)) +#define min( a, b ) ((a) <= (b) ? (a) : (b)) + +GLfloat labelColor0[4] = { 1.0, 1.0, 1.0, 1.0 }; +GLfloat labelColor1[4] = { 1.0, 1.0, 0.4, 1.0 }; +GLfloat *labelInfoColor = labelColor0; + +GLboolean doubleBuffered = GL_TRUE; +GLboolean drawTextured = GL_TRUE; + +int textureWidth = 64; +int textureHeight = 64; + +int winWidth = 580, winHeight = 720; + +const GLfloat texmat_swap_rq[16] = { 1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 1.0, + 0.0, 0.0, 1.0, 0.0}; + +const GLfloat nullPlane[4] = { 0.0, 0.0, 0.0, 0.0 }; +const GLfloat ObjPlaneS1[4] = { 1.0, 0.0, 1.0, 0.0 }; +const GLfloat ObjPlaneS2[4] = { 0.5, 0.0, 0.0, 0.0 }; +const GLfloat ObjPlaneS3[4] = { 1.0, 0.0, 0.0, 0.0 }; +const GLfloat ObjPlaneT[4] = { 0.0, 1.0, 0.0, 0.0 }; +const GLfloat ObjPlaneT2[4] = { 0.0, 0.5, 0.0, 0.0 }; +const GLfloat ObjPlaneT3[4] = { 0.0, 1.0, 0.0, 0.0 }; +const GLfloat ObjPlaneR[4] = { 0.0, 0.0, 1.0, 0.0 }; +const GLfloat ObjPlaneQ[4] = { 0.0, 0.0, 0.0, 0.5 }; + + +static void checkErrors( void ) +{ + GLenum error; + + while ( (error = glGetError()) != GL_NO_ERROR ) { + fprintf( stderr, "Error: %s\n", (char *) gluErrorString( error ) ); + } +} + +static void drawString( const char *string, GLfloat x, GLfloat y, + const GLfloat color[4] ) +{ + glColor4fv( color ); + glRasterPos2f( x, y ); + + while ( *string ) { + glutBitmapCharacter( GLUT_BITMAP_TIMES_ROMAN_10, *string ); + string++; + } +} + +static void begin2D( int width, int height ) +{ + glMatrixMode( GL_PROJECTION ); + + glPushMatrix(); + glLoadIdentity(); + + glOrtho( 0, width, 0, height, -1, 1 ); + glMatrixMode( GL_MODELVIEW ); + + glPushMatrix(); + glLoadIdentity(); +} + +static void end2D( void ) +{ + glMatrixMode( GL_PROJECTION ); + glPopMatrix(); + glMatrixMode( GL_MODELVIEW ); + glPopMatrix(); +} + +static void initialize( void ) +{ + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + + glOrtho( -1.5, 1.5, -1.5, 1.5, -1.5, 1.5 ); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + glShadeModel( GL_FLAT ); +} + +/* ARGSUSED1 */ +static void keyboard( unsigned char c, int x, int y ) +{ + switch ( c ) { + case 't': + drawTextured = !drawTextured; + break; + case 27: /* Escape key should force exit. */ + exit(0); + break; + default: + break; + } + glutPostRedisplay(); +} + +/* ARGSUSED1 */ +static void special( int key, int x, int y ) +{ + switch ( key ) { + case GLUT_KEY_DOWN: + break; + case GLUT_KEY_UP: + break; + case GLUT_KEY_LEFT: + break; + case GLUT_KEY_RIGHT: + break; + default: + break; + } + glutPostRedisplay(); +} + +static void +reshape( int w, int h ) +{ + winWidth = w; + winHeight = h; + /* No need to call glViewPort here since "draw" calls it! */ +} + +static void loadTexture( int width, int height ) +{ + int alphaSize = 1; + int rgbSize = 3; + GLubyte *texImage, *p; + int elementsPerGroup, elementSize, groupSize, rowSize; + int i, j; + + + elementsPerGroup = alphaSize + rgbSize; + elementSize = sizeof(GLubyte); + groupSize = elementsPerGroup * elementSize; + rowSize = width * groupSize; + + if ( (texImage = (GLubyte *) malloc( height * rowSize ) ) == NULL ) { + fprintf( stderr, "texture malloc failed\n" ); + return; + } + + for ( i = 0 ; i < height ; i++ ) + { + p = texImage + i * rowSize; + + for ( j = 0 ; j < width ; j++ ) + { + if ( rgbSize > 0 ) + { + /** + ** +-----+-----+ + ** | | | + ** | R | G | + ** | | | + ** +-----+-----+ + ** | | | + ** | Y | B | + ** | | | + ** +-----+-----+ + **/ + if ( i > height / 2 ) { + if ( j < width / 2 ) { + p[0] = 0xff; + p[1] = 0x00; + p[2] = 0x00; + } else { + p[0] = 0x00; + p[1] = 0xff; + p[2] = 0x00; + } + } else { + if ( j < width / 2 ) { + p[0] = 0xff; + p[1] = 0xff; + p[2] = 0x00; + } else { + p[0] = 0x00; + p[1] = 0x00; + p[2] = 0xff; + } + } + p += 3 * elementSize; + } + + if ( alphaSize > 0 ) + { + /** + ** +-----------+ + ** | W | + ** | +-----+ | + ** | | | | + ** | | B | | + ** | | | | + ** | +-----+ | + ** | | + ** +-----------+ + **/ + int i2 = i - height / 2; + int j2 = j - width / 2; + int h8 = height / 8; + int w8 = width / 8; + if ( -h8 <= i2 && i2 <= h8 && -w8 <= j2 && j2 <= w8 ) { + p[0] = 0x00; + } else if ( -2 * h8 <= i2 && i2 <= 2 * h8 && -2 * w8 <= j2 && j2 <= 2 * w8 ) { + p[0] = 0x55; + } else if ( -3 * h8 <= i2 && i2 <= 3 * h8 && -3 * w8 <= j2 && j2 <= 3 * w8 ) { + p[0] = 0xaa; + } else { + p[0] = 0xff; + } + p += elementSize; + } + } + } + + glTexImage2D( GL_TEXTURE_2D, 0, + GL_RGBA, width, height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, texImage ); + + free( texImage ); +} + + +static void drawSample( int x, int y, int w, int h, + int texgenenabled, int coordnr ) +{ + char buf[255]; + + glViewport( x, y, w, h ); + glScissor( x, y, w, h ); + + glClearColor( 0.1, 0.1, 0.1, 1.0 ); + glClear( GL_COLOR_BUFFER_BIT ); + + begin2D( w, h ); + if (texgenenabled == 2) { + sprintf( buf, "TexCoord%df", coordnr); + drawString( buf, 10, h - 15, labelInfoColor ); + sprintf( buf, "texgen enabled for %s coordinate(s)", coordnr == 2 ? "S" : "S/T"); + drawString( buf, 10, 5, labelInfoColor ); + } + else if (texgenenabled == 0) { + sprintf( buf, "TexCoord%df", coordnr); + drawString( buf, 10, h - 15, labelInfoColor ); + drawString( "no texgen", 10, 5, labelInfoColor ); + } + else if (texgenenabled == 1) { + drawString( "no TexCoord", 10, h - 15, labelInfoColor ); + sprintf( buf, "texgen enabled for %s coordinate(s)", + coordnr == 2 ? "S/T" : (coordnr == 3 ? "S/T/R" : "S/T/R/Q")); + drawString( buf, 10, 5, labelInfoColor ); + } + + end2D(); + + glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); + + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP ); + + loadTexture( textureWidth, textureHeight ); + + if ( drawTextured ) { + glEnable( GL_TEXTURE_2D ); + } + + glDisable( GL_TEXTURE_GEN_S ); + glDisable( GL_TEXTURE_GEN_T ); + glDisable( GL_TEXTURE_GEN_R ); + glDisable( GL_TEXTURE_GEN_Q ); + + glMatrixMode( GL_TEXTURE ); + glLoadIdentity(); + glMatrixMode( GL_MODELVIEW ); + glPushMatrix(); + + switch (coordnr) { + case 2: + switch (texgenenabled) { + case 0: + glBegin( GL_QUADS ); + glTexCoord2f( 0.0, 0.0 ); + glVertex2f( -0.8, -0.8 ); + + glTexCoord2f( 1.0, 0.0 ); + glVertex2f( 0.8, -0.8 ); + + glTexCoord2f( 1.0, 1.0 ); + glVertex2f( 0.8, 0.8 ); + + glTexCoord2f( 0.0, 1.0 ); + glVertex2f( -0.8, 0.8 ); + glEnd(); + break; + case 1: + /* why doesn't this case work with software mesa? The S/T tex coords + should turn out as 0 and 1 if dot product of vertex coordinates with + ObjPlaneS3/T3 is calculated, and Q coord should be default value (1.0), no? + Submitting one dummy tex coordinate fixes this? */ + glTranslatef( -0.8, -0.8, 0.0 ); + glScalef( 1.6, 1.6, 1.0 ); + glTexGeni( GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR ); + glTexGeni( GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR ); + glTexGeni( GL_R, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR ); + glTexGeni( GL_Q, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR ); + glTexGenfv(GL_S, GL_OBJECT_PLANE, ObjPlaneS3); + glTexGenfv(GL_T, GL_OBJECT_PLANE, ObjPlaneT3); + glTexGenfv(GL_R, GL_OBJECT_PLANE, nullPlane); + glTexGenfv(GL_Q, GL_OBJECT_PLANE, nullPlane); + + glEnable( GL_TEXTURE_GEN_S ); + glEnable( GL_TEXTURE_GEN_T ); + + glBegin( GL_QUADS ); +/* glTexCoord1f( 0.0 );*/ + glVertex2f( 0.0, 0.0 ); + glVertex2f( 1.0, 0.0 ); + glVertex2f( 1.0, 1.0 ); + glVertex2f( 0.0, 1.0 ); + glEnd(); + break; + case 2: + /* make sure that texgen T and non-texgen S coordinate are wrong */ + glTexGeni( GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR ); + glTexGeni( GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR ); + glTexGeni( GL_R, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR ); + glTexGeni( GL_Q, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR ); + glTexGenfv(GL_S, GL_OBJECT_PLANE, ObjPlaneS1); + glTexGenfv(GL_T, GL_OBJECT_PLANE, nullPlane); + glTexGenfv(GL_R, GL_OBJECT_PLANE, nullPlane); + glTexGenfv(GL_Q, GL_OBJECT_PLANE, nullPlane); + + glEnable( GL_TEXTURE_GEN_S ); + + glBegin( GL_QUADS ); + /* use z coordinate to get correct texgen values... */ + glTexCoord2f( 0.0, 0.0 ); + glVertex3f( -0.8, -0.8, 0.8 ); + + glTexCoord2f( 0.0, 0.0 ); + glVertex3f( 0.8, -0.8, 0.2 ); + + glTexCoord2f( 0.0, 1.0 ); + glVertex3f( 0.8, 0.8, 0.2 ); + + glTexCoord2f( 0.0, 1.0 ); + glVertex3f( -0.8, 0.8, 0.8 ); + glEnd(); + break; + } + break; + case 3: + glMatrixMode( GL_TEXTURE ); + glLoadMatrixf( texmat_swap_rq ); + glMatrixMode( GL_MODELVIEW ); + glTranslatef( -0.8, -0.8, 0.0 ); + glScalef( 1.6, 1.6, 1.0 ); + switch (texgenenabled) { + case 0: + glBegin( GL_QUADS ); + glTexCoord3f( 0.0, 0.0, 0.5 ); + glVertex2f( 0.0, 0.0 ); + + glTexCoord3f( 0.5, 0.0, 0.5 ); + glVertex2f( 1.0, 0.0 ); + + glTexCoord3f( 0.5, 0.5, 0.5 ); + glVertex2f( 1.0, 1.0 ); + + glTexCoord3f( 0.0, 0.5, 0.5 ); + glVertex2f( 0.0, 1.0 ); + glEnd(); + break; + case 1: + glTexGeni( GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR ); + glTexGeni( GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR ); + glTexGeni( GL_R, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR ); + glTexGeni( GL_Q, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR ); + glTexGenfv(GL_S, GL_OBJECT_PLANE, ObjPlaneS2); + glTexGenfv(GL_T, GL_OBJECT_PLANE, ObjPlaneT2); + glTexGenfv(GL_R, GL_OBJECT_PLANE, ObjPlaneR); + glTexGenfv(GL_Q, GL_OBJECT_PLANE, nullPlane); + + glEnable( GL_TEXTURE_GEN_S ); + glEnable( GL_TEXTURE_GEN_T ); + glEnable( GL_TEXTURE_GEN_R ); + + glBegin( GL_QUADS ); + glVertex3f( 0.0, 0.0, 0.5 ); + glVertex3f( 1.0, 0.0, 0.5 ); + glVertex3f( 1.0, 1.0, 0.5 ); + glVertex3f( 0.0, 1.0, 0.5 ); + glEnd(); + break; + case 2: + /* make sure that texgen R/Q and non-texgen S/T coordinates are wrong */ + glTexGeni( GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR ); + glTexGeni( GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR ); + glTexGeni( GL_R, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR ); + glTexGeni( GL_Q, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR ); + glTexGenfv(GL_S, GL_OBJECT_PLANE, ObjPlaneS2); + glTexGenfv(GL_T, GL_OBJECT_PLANE, ObjPlaneT2); + glTexGenfv(GL_R, GL_OBJECT_PLANE, nullPlane); + glTexGenfv(GL_Q, GL_OBJECT_PLANE, nullPlane); + + glEnable( GL_TEXTURE_GEN_S ); + glEnable( GL_TEXTURE_GEN_T ); + + glBegin( GL_QUADS ); + glTexCoord3f( 0.0, 0.0, 0.5 ); + glVertex2f( 0.0, 0.0); + + glTexCoord3f( 0.0, 0.0, 0.5 ); + glVertex2f( 1.0, 0.0); + + glTexCoord3f( 0.0, 0.0, 0.5 ); + glVertex2f( 1.0, 1.0); + + glTexCoord3f( 0.0, 0.0, 0.5 ); + glVertex2f( 0.0, 1.0); + glEnd(); + break; + } + break; + case 4: + switch (texgenenabled) { + case 0: + glBegin( GL_QUADS ); + /* don't need r coordinate but still setting it I'm mean */ + glTexCoord4f( 0.0, 0.0, 0.0, 0.5 ); + glVertex2f( -0.8, -0.8 ); + + glTexCoord4f( 0.5, 0.0, 0.2, 0.5 ); + glVertex2f( 0.8, -0.8 ); + + glTexCoord4f( 0.5, 0.5, 0.5, 0.5 ); + glVertex2f( 0.8, 0.8 ); + + glTexCoord4f( 0.0, 0.5, 0.5, 0.5 ); + glVertex2f( -0.8, 0.8 ); + glEnd(); + break; + case 1: + glTranslatef( -0.8, -0.8, 0.0 ); + glScalef( 1.6, 1.6, 1.0 ); + /* make sure that texgen R/Q and non-texgen S/T coordinates are wrong */ + glTexGeni( GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR ); + glTexGeni( GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR ); + glTexGeni( GL_R, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR ); + glTexGeni( GL_Q, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR ); + glTexGenfv(GL_S, GL_OBJECT_PLANE, ObjPlaneS2); + glTexGenfv(GL_T, GL_OBJECT_PLANE, ObjPlaneT2); + glTexGenfv(GL_R, GL_OBJECT_PLANE, ObjPlaneR); + glTexGenfv(GL_Q, GL_OBJECT_PLANE, ObjPlaneQ); + + glEnable( GL_TEXTURE_GEN_S ); + glEnable( GL_TEXTURE_GEN_T ); + glEnable( GL_TEXTURE_GEN_R ); + glEnable( GL_TEXTURE_GEN_Q ); + + glBegin( GL_QUADS ); + glVertex2f( 0.0, 0.0 ); + glVertex2f( 1.0, 0.0 ); + glVertex2f( 1.0, 1.0 ); + glVertex2f( 0.0, 1.0 ); + glEnd(); + break; + case 2: + glTranslatef( -0.8, -0.8, 0.0 ); + glScalef( 1.6, 1.6, 1.0 ); + /* make sure that texgen R/Q and non-texgen S/T coordinates are wrong */ + glTexGeni( GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR ); + glTexGeni( GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR ); + glTexGeni( GL_R, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR ); + glTexGeni( GL_Q, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR ); + glTexGenfv(GL_S, GL_OBJECT_PLANE, ObjPlaneS2); + glTexGenfv(GL_T, GL_OBJECT_PLANE, ObjPlaneT2); + glTexGenfv(GL_R, GL_OBJECT_PLANE, nullPlane); + glTexGenfv(GL_Q, GL_OBJECT_PLANE, nullPlane); + + glEnable( GL_TEXTURE_GEN_S ); + glEnable( GL_TEXTURE_GEN_T ); + + glBegin( GL_QUADS ); + glTexCoord4f( 0.0, 0.0, 0.0, 0.5 ); + glVertex2f( 0.0, 0.0 ); + + glTexCoord4f( 0.0, 0.0, 0.2, 0.5 ); + glVertex2f( 1.0, 0.0 ); + + glTexCoord4f( 0.0, 0.0, 0.5, 0.5 ); + glVertex2f( 1.0, 1.0 ); + + glTexCoord4f( 0.0, 0.0, 0.75, 0.5 ); + glVertex2f( 0.0, 1.0 ); + glEnd(); + break; + } + break; + } + + glPopMatrix(); + glDisable( GL_TEXTURE_2D ); + +} + +static void display( void ) +{ + int numX = 3, numY = 3; + float xBase = (float) winWidth * 0.01; + float xOffset = (winWidth - xBase) / numX; + float xSize = max( xOffset - xBase, 1 ); + float yBase = (float) winHeight * 0.01; + float yOffset = (winHeight - yBase) / numY; + float ySize = max( yOffset - yBase, 1 ); + float x, y; + int i, j; + + glViewport( 0, 0, winWidth, winHeight ); + glDisable( GL_SCISSOR_TEST ); + glClearColor( 0.0, 0.0, 0.0, 0.0 ); + glClear( GL_COLOR_BUFFER_BIT ); + glEnable( GL_SCISSOR_TEST ); + + x = xBase; + y = (winHeight - 1) - yOffset; + + for ( i = 0 ; i < numY ; i++ ) + { + + labelInfoColor = labelColor1; + + + for ( j = 0 ; j < numX ; j++ ) { + drawSample( x, y, xSize, ySize, i, j+2 ); + x += xOffset; + } + + x = xBase; + y -= yOffset; + } + + if ( doubleBuffered ) { + glutSwapBuffers(); + } else { + glFlush(); + } + + checkErrors(); +} + +static void usage( char *name ) +{ + fprintf( stderr, "usage: %s [ options ]\n", name ); + fprintf( stderr, "\n" ); + fprintf( stderr, "options:\n" ); + fprintf( stderr, " -sb single buffered\n" ); + fprintf( stderr, " -db double buffered\n" ); + fprintf( stderr, " -info print OpenGL driver info\n" ); +} + +static void instructions( void ) +{ + fprintf( stderr, "texgenmix - mixed texgen/non-texgen texture coordinate test\n" ); + fprintf( stderr, "all quads should look the same!\n" ); + fprintf( stderr, "\n" ); + fprintf( stderr, " [t] - toggle texturing\n" ); +} + +int main( int argc, char *argv[] ) +{ + GLboolean info = GL_FALSE; + int i; + + glutInit( &argc, argv ); + + for ( i = 1 ; i < argc ; i++ ) { + if ( !strcmp( "-sb", argv[i] ) ) { + doubleBuffered = GL_FALSE; + } else if ( !strcmp( "-db", argv[i] ) ) { + doubleBuffered = GL_TRUE; + } else if ( !strcmp( "-info", argv[i] ) ) { + info = GL_TRUE; + } else { + usage( argv[0] ); + exit( 1 ); + } + } + + if ( doubleBuffered ) { + glutInitDisplayMode( GLUT_RGB | GLUT_DOUBLE ); + } else { + glutInitDisplayMode( GLUT_RGB | GLUT_SINGLE ); + } + + glutInitWindowSize( winWidth, winHeight ); + glutInitWindowPosition( 0, 0 ); + glutCreateWindow( "Mixed texgen/non-texgen texture coordinate test" ); + + initialize(); + instructions(); + + if ( info ) { + printf( "\n" ); + 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 ) ); + } + + glutDisplayFunc( display ); + glutReshapeFunc( reshape ); + glutKeyboardFunc( keyboard ); + glutSpecialFunc( special ); + glutMainLoop(); + + return 0; +} |