#include "main.h" #include "glwrap.h" #include "glut.h" #include "myext.h" #include #include #include //Global variables //Time double time = 0; double Taccel = .00020; // tornado's upward y accel double Gaccel = -.00000098; //gravity downward y accel //Tornado double Tsize = 200; //height of Tornado double zpos = -1000; double xpos = 0.0; double ypos = Tsize-300; double zvel = 0.1; double xvel = 0.0; double yvel = 0.0; double angle = 0; double speed = 1; //Object 1 = Square double zpos1 = -700; double xpos1 = 0.0; double ypos1 = -300.0; double zvel1 = 0.0; double xvel1 = 0.0; double yvel1 = 0.0; int inT1 = 0; int fall1 = 0; int grounded1 = 1; double timeV1 = 0; //timer for velocity double timeA1 = 0; //timer for gravity's negative acceleration //Object 2 = Sphere double zpos2 = -500; double xpos2 = 0.0; double ypos2 = -300.0; double zvel2 = 0.0; double xvel2 = 0.0; double yvel2 = 0.0; int inT2 = 0; int fall2 = 0; int grounded2 = 1; double timeV2 = 0; //timer for velocity double timeA2 = 0; //timer for gravity's negative acceleration //Object 3 = Teapot double zpos3 = -300; double xpos3 = 0.0; double ypos3 = -300.0; double zvel3 = 0.0; double xvel3 = 0.0; double yvel3 = 0.0; int inT3 = 0; int fall3 = 0; int grounded3 = 1; double timeV3 = 0; //timer for velocity double timeA3 = 0; //timer for gravity's negative acceleration //fixme: // could put Skybox in its own header/source file. // ==hplus== class Skybox { public: Skybox(); void release(); bool load( char const * name ); void draw(); private: ~Skybox(); GWState sides_[ 6 ]; static float coords[ 6 ][ 4 ][ 3 ]; static float uvs[ 6 ][ 4 ][ 2 ]; static char const * suffices[ 6 ]; }; // Typically, you'd have artists create geometry as models. // However, a skybox is simple enough that I don't want to code // up a model loader just for this example. Thus, here's the // geometry, all inline. I'm putting it 9 units from the origin // to make sure it doesn't clip through our near clipping plane. // Other than that, it's really quite arbitrary where we put it // as long as it's centered on the origin. float Skybox::coords[ 6 ][ 4 ][ 3 ] = { { // Z { -9, -9, 9, }, { -9, 9, 9, }, { 9, 9, 9, }, { 9, -9, 9, }, }, { // X { 9, -9, 9, }, { 9, 9, 9, }, { 9, 9, -9, }, { 9, -9, -9, }, }, { // -Z { 9, -9, -9, }, { 9, 9, -9, }, { -9, 9, -9, }, { -9, -9, -9, }, }, { // -X { -9, -9, -9, }, { -9, 9, -9, }, { -9, 9, 9, }, { -9, -9, 9, }, }, { // Y { -9, 9, 9, }, { -9, 9, -9, }, { 9, 9, -9, }, { 9, 9, 9, }, }, { // -Y { -9, -9, -9, }, { -9, -9, 9, }, { 9, -9, 9, }, { 9, -9, -9, }, }, }; // It's important to get the u/v coordinates right for each // vertex for each texture, so the cube matches up once it's // drawn. float Skybox::uvs[ 6 ][ 4 ][ 2 ] = { { // Z { 1, 0, }, { 1, 1, }, { 0, 1, }, { 0, 0, }, }, { // X { 1, 0, }, { 1, 1, }, { 0, 1, }, { 0, 0, }, }, { // -Z { 1, 0, }, { 1, 1, }, { 0, 1, }, { 0, 0, }, }, { // -X { 1, 0, }, { 1, 1, }, { 0, 1, }, { 0, 0, }, }, { // Y { 0, 0, }, { 1, 0, }, { 1, 1, }, { 0, 1, }, }, { // -Y { 1, 1, }, { 0, 1, }, { 0, 0, }, { 1, 0, }, }, }; char const * Skybox::suffices[ 6 ] = { "_pz", "_px", "_nz", "_nx", "_py", "_ny", }; Skybox::Skybox() { } void Skybox::release() { delete this; } bool Skybox::load( char const * name ) { char path[ PathLengthMax ]; bool ok = true; for( unsigned int ix = 0; ix < 6; ++ix ) { sprintf( path, "%s%s.tga", name, suffices[ ix ] ); sides_[ ix ].texture = TextureRef( path, true ); if( !sides_[ ix ].texture.consistent() ) { error( "cannot load texture: %s", path ); ok = false; } } return ok; } void Skybox::draw() { // The skybox needs to be drawn centered on the origin of the // eye, no matter what the "world" transform you're using may // be. This assert makes sure that such is the case. It also // makes sure there's no skew. It could be tightened up to // also make sure there's no non-uniform scale... #if !defined( NDEBUG ) float mx[ 16 ]; glGetFloatv( GL_MODELVIEW_MATRIX, mx ); //assert( mx[12] == 0 && mx[13] == 0 && mx[14] == 0 ); //assert( mx[3] == 0 && mx[7] == 0 && mx[11] == 0 ); //assert( mx[15] == 1 ); #endif for( unsigned int ix = 0; ix < 6; ++ix ) { sides_[ ix ].draw( GL_QUADS, NULL, 4, (float *)coords[ ix ], (float *)uvs[ ix ], NULL, NULL ); } } Skybox::~Skybox() { } Skybox * gSkybox; void initSkybox() { gSkybox = new Skybox; gSkybox->load( "skybox" ); } float gYaw; float gPitch; struct IntOptionValue { char const * name; int val; }; static IntOptionValue intValues[] = { { "winSizeX", WINWIDTH }, { "winSizeY", WINHEIGHT }, }; int intOption( char const * o ) { for( int ix = 0; ix < sizeof(intValues)/sizeof(intValues[0]); ++ix ) { if( !_stricmp( intValues[ix].name, o ) ) { return intValues[ix].val; } } assert( 0 ); return -1; } static void vanillaGl( ) { // make sure there's no leakage between frames gwReset( ); //gluLookAt (10, 10, 10, 0, 10, 0, 0, 1, 0); // set up our basic projection glMatrixMode( GL_PROJECTION ); glLoadIdentity(); float fw = float(intValues[1].val)/float(intValues[0].val); //glOrtho( -1, 1, -fw, fw, 1, 1000 ); glFrustum( -1, 1, -fw, fw, 1, 1000 ); glMatrixMode( GL_MODELVIEW ); assert( !glGetError( ) ); } //draws the ground on which the tornado shall ravage objects void drawGround(){ double z,x; glColor3f(0.0,1.0,1.0); glBegin(GL_POLYGON); glVertex3f(-1000.0,-300.0,-1000.0); glVertex3f(-1000.0,-300.0,1000.0); glVertex3f(1000.0,-300.0,1000.0); glVertex3f(1000.0,-300.0,-1000.0); glEnd(); glColor3f(1.0,1.0,1.0); for(z = -1000; z < 1000; z = z + 10){ glBegin(GL_LINES); glVertex3f(-1000.0,-300.0,z); glVertex3f(1000.0,-300.0,z); glEnd(); } for(x = -1000; x < 1000; x = x + 10){ glBegin(GL_LINES); glVertex3f(x,-300.0,-1000.0); glVertex3f(x,-300.0,1000.0); glEnd(); } } //Main draw method void display() { vanillaGl(); glClear( GL_DEPTH_BUFFER_BIT ); // we don't need to clear color, because we will draw // over the entire screen glRotatef( gPitch * 180 / M_PI, 1, 0, 0 ); glRotatef( gYaw * 180 / M_PI, 0, -1, 0 ); gSkybox->draw(); drawGround(); ypos = Tsize-300; glColor3f(0.0,1.0,0.0); glBegin(GL_LINES); glVertex3f(0.0,0.0,0.0); //y glVertex3f(0.0,100.0,0.0); glEnd(); //All objects start at -300 for their y value //TORNADO? glColor3f(0.0,0.0,0.0); glPushMatrix(); glTranslatef(xpos,ypos,zpos); glTranslatef(0,0,0); glRotatef(angle,0,1,0); glRotatef(90,1,0,0); glutWireCone(50.0,Tsize,20,20); //Base , height, slices, stacks glPopMatrix(); //CUBE glColor3f(0.5,0.5,0.0); glPushMatrix(); glTranslatef(xpos1,ypos1,zpos1); if(inT1){ glRotatef(angle,0,1,0); glTranslatef((.2 * 200/Tsize)*(ypos1+300),0,0); } glutSolidCube(10); glPopMatrix(); //SPHERE glColor3f(1.0,0.0,0.0); glPushMatrix(); glTranslatef(xpos2,ypos2,zpos2); if(inT2){ glRotatef(angle,0,1,0); glTranslatef((.2 * 200/Tsize)*(ypos2+300),0,0); } glutSolidSphere(5,20,20); glPopMatrix(); //TEAPOT glColor3f(0.0,0.0,1.0); glPushMatrix(); glTranslatef(xpos3,ypos3,zpos3); if(inT3){ glRotatef(angle,0,1,0); glTranslatef((.2 * 200/Tsize)*(ypos3+300),0,0); } glutSolidTeapot(10); glPopMatrix(); gwSwapBuffers( ); } bool options[ 128 ]; static void putOptions( int argc, char ** argv ) { for( int ix = 0; ix < argc; ++ix ) { if( argv[ix][0] == '-' && argv[ix][1] != '-' ) { char * p = &argv[ix][1]; while( *p ) { options[*p & 0x7f] = true; ++p; } } else { char * eq = strchr( argv[ ix ], '=' ); if( eq ) { *eq = 0; ++eq; for( int o = 0; o < sizeof(intValues)/sizeof(intValues[0]); ++o ) { if( !stricmp( intValues[o].name, argv[ix] ) ) { intValues[o].val = atoi(eq); debug( "setting %s to %d\n", intValues[o].name, intValues[o].val ); goto foundit; } } } warning( "unknown option: %s\n", argv[ix] ); foundit: ; } } } bool hasOption( char ch ) { return options[ ch & 0x7f ]; } void keyboard( unsigned char key, int x, int y ) { if( key == 'q' || key == 'Q' || key == 27 ) { exit(0); } if(key == 'i'){ //Use i to make tornado taller Tsize++; } if(key == 'k'){ Tsize--; // Use k to make tornado smaller } if(key == 'u'){ if(speed < 2){ speed = speed + 0.1; Taccel = Taccel + .00001; } } //WASD controls for moving the tornado if(key == 'w'){ zvel = -0.1; xvel = 0.0; } if(key == 'a'){ zvel = 0.0; xvel = -0.1; } if(key == 's'){ zvel = 0.1; xvel = 0.0; } if(key == 'd'){ zvel = 0.0; xvel = 0.1; } if(key == 'j'){ if(speed > 0){ speed = speed - 0.1; Taccel = Taccel - .00001; } } if(key == 'r'){ Taccel = .00020; // tornado's upward y accel Gaccel = -.00000098; //gravity downward y accel //Tornado Tsize = 200; //height of Tornado zpos = -1000; xpos = 0.0; ypos = Tsize-300; zvel = 0.1; xvel = 0.0; yvel = 0.0; angle = 0; speed = 1; //Object 1 = Square zpos1 = -700; xpos1 = 0.0; ypos1 = -300.0; zvel1 = 0.0; xvel1 = 0.0; yvel1 = 0.0; inT1 = 0; fall1 = 0; grounded1 = 1; timeV1 = 0; //timer for velocity timeA1 = 0; //timer for gravity's negative acceleration //Object 2 = Sphere zpos2 = -500; xpos2 = 0.0; ypos2 = -300.0; zvel2 = 0.0; xvel2 = 0.0; yvel2 = 0.0; inT2 = 0; fall2 = 0; grounded2 = 1; timeV2 = 0; //timer for velocity timeA2 = 0; //timer for gravity's negative acceleration //Object 3 = Teapot zpos3 = -300; xpos3 = 0.0; ypos3 = -300.0; zvel3 = 0.0; xvel3 = 0.0; yvel3 = 0.0; inT3 = 0; fall3 = 0; grounded3 = 1; timeV3 = 0; //timer for velocity timeA3 = 0; //timer for gravity's negative acceleration } } void special( int key, int x, int y ) { if( key == GLUT_KEY_RIGHT ) { gYaw -= 0.1f; } else if( key == GLUT_KEY_LEFT ) { gYaw += 0.1f; } else if( key == GLUT_KEY_UP ) { gPitch -= 0.05f; } else if( key == GLUT_KEY_DOWN ) { gPitch += 0.05f; } if( gPitch > M_PI ) { gPitch -= 2*M_PI; } else if( gPitch < -M_PI ) { gPitch += 2*M_PI; } if( gYaw > M_PI ) { gYaw -= 2*M_PI; } else if( gYaw < -M_PI ) { gYaw += 2*M_PI; } } void timer( ) { //if Object is not in tornado, and the tornado is directely above, go in tornado and move up zpos += zvel; xpos += xvel; if(zpos > zpos1 - 5 && zpos < zpos1 + 5 && inT1 == 0 && fall1 == 0 && xpos > xpos1 - 5 && xpos < xpos1 + 5){ timeV1 = 0; inT1 = 1; } if(zpos > zpos2 - 5 && zpos < zpos2 + 5 && inT2 == 0 && fall2 == 0 && xpos > xpos2 - 5 && xpos < xpos2 + 5){ timeV2 = 0; inT2 = 1; } if(zpos > zpos3 - 5 && zpos < zpos3 + 5 && inT3 == 0 && fall3 == 0 && xpos > xpos3 - 5 && xpos < xpos3 + 5){ timeV3 = 0; inT3 = 1; } //if objects are caught in tornado and are not falling, move with tornado along z axis if(inT1 == 1 && fall1 == 0){ zpos1 = zpos; xpos1 = xpos; } if(inT2 == 1 && fall2 == 0){ zpos2 = zpos; xpos2 = xpos; } if(inT3 == 1 && fall3 == 0){ zpos3 = zpos; xpos3 = xpos; } //if objects exceed tornado height while in tornado, start falling // else if already falling, commence de-acceleration if(ypos1 >= (-300 + Tsize) && inT1 == 1){ timeA1 = 0; yvel1 = Taccel*timeV1; inT1 = 0; fall1 = 1; //Based on the angle of rotation at top of tornado, calculate x and z velocities zvel1 = -cos(angle*M_PI/180)*(.1+ (0.05*speed)); xvel1 = -sin(angle*M_PI/180)*(.1+ (0.05*speed)); } else if(fall1 == 1){ yvel1 = yvel1 + Gaccel*timeA1; } if(ypos2 >= (-300 + Tsize) && inT2 == 1){ timeA2 = 0; yvel2 = Taccel*timeV2; inT2 = 0; fall2 = 1; //Based on the angle of rotation at top of tornado, calculate x and z velocities zvel2 = -cos(angle*M_PI/180)*(.1+ (0.05*speed)); xvel2 = -sin(angle*M_PI/180)*(.1+ (0.05*speed)); } else if(fall2 == 1){ yvel2 = yvel2 + Gaccel*timeA2; } if(ypos3 >= (-300 + Tsize) && inT3 == 1){ timeA3 = 0; yvel3 = Taccel*timeV3; inT3 = 0; fall3 = 1; //Based on the angle of rotation at top of tornado, calculate x and z velocities zvel3 = -cos(angle*M_PI/180)*(.1+ (0.05*speed)); xvel3 = -sin(angle*M_PI/180)*(.1+ (0.05*speed)); } else if(fall3 == 1){ yvel3 = yvel3 + Gaccel*timeA3; } //if object hits ground, stop moving if(ypos1 < -300){ yvel1 = 0; xvel1 = 0; zvel1 = 0; ypos1 = -300; fall1 = 0; } if(ypos2 < -300){ yvel2 = 0; xvel2 = 0; zvel2 = 0; ypos2 = -300; fall2 = 0; } if(ypos3 < -300){ yvel3 = 0; xvel3 = 0; zvel3 = 0; ypos3 = -300; fall3 = 0; } //Calculate position if(inT1 == 1){ ypos1 = -300 + (.5)*(Taccel)*(timeV1*timeV1); xpos1 += xvel1; zpos1 += zvel1; } else if(fall1 == 1){ ypos1 += yvel1; xpos1 += xvel1; zpos1 += zvel1; } if(inT2 == 1){ ypos2 = -300 + (.5)*(Taccel)*(timeV2*timeV2); xpos2 += xvel2; zpos2 += zvel2; } else if(fall2 == 1){ ypos2 += yvel2; xpos2 += xvel2; zpos2 += zvel2; } if(inT3 == 1){ ypos3 = -300 + (.5)*(Taccel)*(timeV3*timeV3); xpos3 += xvel3; zpos3 += zvel3; } else if(fall3 == 1){ ypos3 += yvel3; xpos3 += xvel3; zpos3 += zvel3; } //ypos2 += yvel2; if(angle <360){ angle = angle + speed; } else{ angle = 0; } timeV1++; timeV2++; timeV3++; timeA1++; timeA2++; timeA3++; glutPostRedisplay(); // this is maybe not the most efficient... } void mouse( int button, int state, int x, int y ) { // state == GLUT_DOWN ? true : false } void activeMotion( int x, int y ) { } void passiveMotion( int x, int y ) { } void goBackToNormalScreen( ) { #if defined( WIN32 ) ChangeDisplaySettings( 0, 0 ); #endif } int main(int argc, char** argv) { #if !defined( NDEBUG ) initLog( "log.txt", LogFilterNone ); #else initLog( "log.txt", LogFilterDebug ); #endif #if defined( WIN32 ) RECT r = { 0, 0, 0, 0 }; AdjustWindowRectEx( &r, WS_OVERLAPPEDWINDOW, false, WS_EX_OVERLAPPEDWINDOW ); #endif putOptions( argc, argv ); glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH ); glutInitWindowSize(intOption("winSizeX"),intOption("winSizeY")); if( !options['w'] ) { // this doesn't seem to work -- can't find documentation anywhere // glutGameModeString( "800x600@60" ); // glutEnterGameMode( ); glutInitWindowPosition(0, 0); } else { glutInitWindowPosition(100, 0); } glutCreateWindow("Main Window"); glutSetKeyRepeat( GLUT_KEY_REPEAT_OFF ); if( !options[ 'w' ] ) { #if defined( WIN32 ) // not windowed -- go fullscreen DEVMODE dm; memset( &dm, 0, sizeof( dm ) ); dm.dmSize = sizeof( DEVMODE ); dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT; dm.dmBitsPerPel = 32; dm.dmPelsWidth = intOption("winSizeX"); dm.dmPelsHeight = intOption("winSizeY"); dm.dmDisplayFlags = 0; dm.dmDisplayFrequency = 60; BOOL b = (ChangeDisplaySettings( &dm, CDS_FULLSCREEN ) == DISP_CHANGE_SUCCESSFUL); if( !b ) { error( "ChangeDisplaySettings(): cannot go fullscreen!\n" ); } #endif atexit( goBackToNormalScreen ); glutPositionWindow( 0, 0 ); glutReshapeWindow( intOption("winSizeX"), intOption("winSizeY") ); } vanillaGl( ); gwFlushState( ); glutDisplayFunc( display ); glutKeyboardFunc( keyboard ); glutSpecialFunc( special ); glutIdleFunc( timer ); glutMouseFunc( mouse ); glutMotionFunc( activeMotion ); glutPassiveMotionFunc( passiveMotion ); initSkybox(); glutMainLoop(); return 0; } #if defined( WIN32 ) static int splitCmdLine( char const * cmdLine, char ** argv, int n ) { int args = 0; while( n > 1 ) { while( *cmdLine && isspace( *cmdLine ) ) { ++cmdLine; } if( !*cmdLine ) break; char const * lStart; char const * lEnd; if( *cmdLine == '\"' ) { lStart = cmdLine+1; lEnd = strchr( lStart, '\"' ); } else { lStart = cmdLine; lEnd = strchr( lStart, ' ' ); } if( !lEnd ) { lEnd = lStart + strlen( lStart ); } *argv = new char[ lEnd-lStart+1 ]; memcpy( *argv, lStart, lEnd-lStart ); (*argv)[ lEnd-lStart ] = 0; --n; ++args; ++argv; if( *lEnd ) { cmdLine = lEnd+1; } else { cmdLine = lEnd; } } *argv = 0; return args; } int __stdcall WinMain( HINSTANCE cur, HINSTANCE prev, LPSTR cmdLine, int cmdShow ) { char * argv[ 100 ]; char progName[ 32 ] = "skybox"; argv[ 0 ] = progName; int argc = 1 + splitCmdLine( cmdLine, &argv[ 1 ], 98 ); return main( argc, argv ); } int strcasecmp( char const * a, char const * b ) { while( *a && *b && (tolower(*a) == tolower(*b)) ) { ++a; ++b; } return tolower(*a) - tolower(*b); } #endif // FIXME: move log into its own file // ==hplus== static unsigned int s_logFilterLevel; static FILE * s_logFile; void initLog( char const * fileName, unsigned int level ) { if( s_logFile ) { fclose( s_logFile ); } s_logFile = fopen( fileName, "wb" ); assert( s_logFile ); // crappy error reporting } static void logOutput( unsigned int level, char const * fmt, va_list l ) { if( !s_logFile ) { return; } if( level <= s_logFilterLevel ) { return; } vfprintf( s_logFile, fmt, l ); fflush( s_logFile ); } void debug( char const * fmt, ... ) { va_list vl; va_start( vl, fmt ); logOutput( LogFilterDebug, fmt, vl ); va_end( vl ); } void warning( char const * fmt, ... ) { va_list vl; va_start( vl, fmt ); logOutput( LogFilterWarning, fmt, vl ); va_end( vl ); } void error( char const * fmt, ... ) { assert( 0 ); // catch errors as they happen! va_list vl; va_start( vl, fmt ); logOutput( LogFilterError, fmt, vl ); va_end( vl ); }