//-----------------------------------------------------------------------------
// Shogun3D's OpenGL Utility Toolkit framework.
//
// Disclamer: Use this code as you see fit, for both personal and/or commercial
// usages. You don't exactly have to give me credit for using this
// code in your own projects (although I would appreciate it if you
// did), but you could at let me know that this has benefited you
// in one way or another, because I didn't write this code just for
// myself you know ^^
//
// File: main.c
// Desc: This code will hopefully show you how easy it is to create a 3rd
// person game :)
// Date: August 20, 2010
//-----------------------------------------------------------------------------
// Include files
#ifdef WIN32 // For windows builds
#include <windows.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <gl\glut.h>
#include <gl\glext.h>
//-----------------------------------------------------------------------------
// 3D vector
//-----------------------------------------------------------------------------
struct Vector3
{
float x, y, z;
};
//-----------------------------------------------------------------------------
// Third Person Camera structure
//-----------------------------------------------------------------------------
struct ThirdPersonCamera_t
{
struct Vector3 vecPos;
struct Vector3 vecRot;
float fRadius; // Distance between the camera and the object.
float fLastX;
float fLastY;
};
//-----------------------------------------------------------------------------
// Sphere structure
//-----------------------------------------------------------------------------
struct Sphere_t
{
struct Vector3 position; // Position in 3D space
int selected; // Did OpenGL select this one?
int dead; // Is this sphere dead?
unsigned int death_time; // How long has this sphere been dead?
// Respawn after X milliseconds
float distance; // Distance between you and the sphere.
float size; // The size of the sphere. Decreases during
// death phase.
};
//-----------------------------------------------------------------------------
// Global variables
//-----------------------------------------------------------------------------
#define sphere_count 20 // Number of spheres in the game
#define respawn_time 5000 // After a sphere is shot dead, respawn after this
// amount of time.
struct ThirdPersonCamera_t camera;
struct Sphere_t spheres[sphere_count];
GLuint floor_tex = 0;
GLuint select_buffer[sphere_count*4] = {0};
GLint hits = 0;
float jump_height = 0.0f;
GLboolean sniper_mode = GL_FALSE;
GLint kills = 0;
//-----------------------------------------------------------------------------
// Name: glEnable2D
// Desc: Enabled 2D primitive rendering by setting up the appropriate orthographic
// perspectives and matrices.
//-----------------------------------------------------------------------------
void glEnable2D( void )
{
GLint iViewport[4];
// Get a copy of the viewport
glGetIntegerv( GL_VIEWPORT, iViewport );
// Save a copy of the projection matrix so that we can restore it
// when it's time to do 3D rendering again.
glMatrixMode( GL_PROJECTION );
glPushMatrix();
glLoadIdentity();
// Set up the orthographic projection
glOrtho( iViewport[0], iViewport[0]+iViewport[2],
iViewport[1]+iViewport[3], iViewport[1], -1, 1 );
glMatrixMode( GL_MODELVIEW );
glPushMatrix();
glLoadIdentity();
// Make sure depth testing and lighting are disabled for 2D rendering until
// we are finished rendering in 2D
glPushAttrib( GL_DEPTH_BUFFER_BIT | GL_LIGHTING_BIT );
glDisable( GL_DEPTH_TEST );
glDisable( GL_LIGHTING );
}
//-----------------------------------------------------------------------------
// Name: glDisable2D
// Desc: Disables 2D rendering and restores the previous matrix and render states
// before they were modified.
//-----------------------------------------------------------------------------
void glDisable2D( void )
{
glPopAttrib();
glMatrixMode( GL_PROJECTION );
glPopMatrix();
glMatrixMode( GL_MODELVIEW );
glPopMatrix();
}
//-----------------------------------------------------------------------------
// Name: glPrintf
// Desc: Prints a string of chars using GLUT's pre-defined char blitting API.
//-----------------------------------------------------------------------------
void glPrintf( int x, int y, void* font, char* string, ... )
{
char *c;
char temp[256];
va_list ap;
va_start( ap, string );
vsprintf( temp, string, ap );
va_end( ap );
glEnable2D();
glRasterPos2i(x,y);
for (c=temp; *c != '\0'; c++)
{
glutBitmapCharacter(font, *c);
}
glDisable2D();
}
//-----------------------------------------------------------------------------
// Name: distance
// Desc: Calculates the distance between two points.
//-----------------------------------------------------------------------------
float distance( const struct Vector3* v1, const struct Vector3* v2 )
{
float d = 0.0f;
float x = v2->x - v1->x;
float y = v2->y - v1->y;
float z = v2->z - v1->z;
x *= x;
y *= y;
z *= z;
d = sqrt(x+y+z);
return d;
}
//-----------------------------------------------------------------------------
// Name: calculate_distances
// Desc: Calulates the distance between each sphere and the camera.
//-----------------------------------------------------------------------------
void calculate_distances( void )
{
int i;
struct Vector3 neg_camera;
// In order to accurately calculate the distance between the spheres and
// the camera, the x and z values should be converted to negatives, and
// the y coordinate should be ignored. The camera updates the y value
// still, but in a 3rd person shooter/adventure style game, the is calc-
// lations are done seperately from the camera to compensate for jumping
// and gravity, etc.
neg_camera.x = camera.vecPos.x * -1.0f;
neg_camera.y = 0.0f;
neg_camera.z = camera.vecPos.z * -1.0f;
for( i = 0; i < sphere_count; i++ )
{
spheres[i].distance = distance( &neg_camera, &spheres[i].position );
}
}
//-----------------------------------------------------------------------------
// Name: texture_create
// Desc: Creates a simple black and white checker board style texture. Change it
// to whatever you like :)
//-----------------------------------------------------------------------------
void texture_create( void )
{
GLubyte buffer[64][64][4];
int s, t;
for( t = 0; t < 64; t++ )
{
for( s = 0; s < 64; s++ )
{
GLubyte c = ((((t&0x8)==0)^((s&0x8))==0))*255;
buffer[s][t][0] = c;
buffer[s][t][1] = c;
buffer[s][t][2] = c;
buffer[s][t][3] = 255;
}
}
// Set the pixel alignment to default
glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
// Generate 1 texture ID
glGenTextures( 1, &floor_tex );
// Bind this texture so that we can edit it
glBindTexture( GL_TEXTURE_2D, floor_tex );
// Create the actual texture with it's dimensions, format and texel data
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, 64, 64, 0,
GL_RGBA, GL_UNSIGNED_BYTE, buffer );
// Enable bilinear filtering
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
}
//-----------------------------------------------------------------------------
// Name: floor_render
// Desc: Renders the floor with the texture created above.
//-----------------------------------------------------------------------------
void floor_render( float size, float y )
{
// Render the floor at the given height on the Y axis.
glPushAttrib( GL_ALL_ATTRIB_BITS );
glPushMatrix();
glTranslatef( 0.0f, y, 0.0f );
glDisable( GL_LIGHTING );
glEnable( GL_TEXTURE_2D );
glBindTexture( GL_TEXTURE_2D, floor_tex );
glBegin( GL_QUADS );
glColor3f( 1.0f, 1.0f, 1.0f );
glTexCoord2f( 0.0f, 0.0f ); glVertex3f( -size, y, -size );
glTexCoord2f( 0.0f, 1.0f ); glVertex3f( -size, y, +size );
glTexCoord2f( 1.0f, 1.0f ); glVertex3f( +size, y, +size );
glTexCoord2f( 1.0f, 0.0f ); glVertex3f( +size, y, -size );
glEnd();
glPopMatrix();
glPopAttrib();
}
//-----------------------------------------------------------------------------
// Name: spheres_init
// Desc: Gives each sphere a random position in 3D space.
//-----------------------------------------------------------------------------
void spheres_init( void )
{
int i;
// Give each sphere a random position
for( i = 0; i < sphere_count; i++ )
{
spheres[i].position.x = (float) ( ( rand() % 100 ) + 1 ) - 50;
spheres[i].position.y = 0.0f;
spheres[i].position.z = (float) ( ( rand() % 100 ) + 1 ) - 50;
spheres[i].selected = 0;
spheres[i].dead = 0;
spheres[i].death_time = 0;
spheres[i].distance = 0.0f;
spheres[i].size = 2.0f;
}
}
//-----------------------------------------------------------------------------
// Name: spheres_render
// Desc: Renders each sphere in it's random position
//-----------------------------------------------------------------------------
void spheres_render( void )
{
int i;
// Render each sphere with a solid green colour
glColor3f( 0.0f, 1.0f, 0.0f );
for( i = 0; i < sphere_count; i++ )
{
// Is this sphere dead?
if( spheres[i].dead )
{
unsigned int current_time;
// Slowly decrease the size of the sphere when it's dying
if( spheres[i].size > 0.0f )
spheres[i].size -= 0.1f;
// When time expires, bring the sphere back into play (respawn).
// The sphere will fall from the sky after the respawn time expires.
current_time = GetTickCount();
if( (current_time - spheres[i].death_time) > respawn_time )
{
spheres[i].position.x = (float) ( ( rand() % 100 ) + 1 ) - 50;
spheres[i].position.y = 50.0f;
spheres[i].position.z = (float) ( ( rand() % 100 ) + 1 ) - 50;
spheres[i].dead = 0;
spheres[i].size = 2.0f;
}
}
//if( !spheres[i].dead )
if( spheres[i].size != 0.0f )
{
glPushMatrix();
glTranslatef( -spheres[i].position.x, spheres[i].position.y, -spheres[i].position.z );
glutSolidSphere( spheres[i].size, 20, 20 );
// Render a slightly larger purple transparent sphere around
// selected objects.
if( spheres[i].selected )
{
glPushAttrib( GL_ALL_ATTRIB_BITS );
glColor4ub( 128, 0, 255, 64 );
glDisable( GL_COLOR_MATERIAL );
glDisable( GL_LIGHTING );
// glDisable( GL_DEPTH_TEST );
glDepthMask( GL_FALSE );
glDisable( GL_TEXTURE_2D );
glEnable( GL_BLEND );
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
glutSolidSphere( spheres[i].size * 1.5f, 20, 20 );
glPopAttrib();
}
glPopMatrix();
}
// If the sphere is in the sky, let it fall back down to the ground
if( spheres[i].position.y > 0.0f )
{
spheres[i].position.y -= 0.5f;
}
}
}
//-----------------------------------------------------------------------------
// Name: draw_axes
// Desc: Draws each axis using a line in the center of 3D space. Red is X,
// green is Y, and blue is Z. This will help us make sure we are not
// upside down.
//-----------------------------------------------------------------------------
void draw_axes( void )
{
glPushAttrib( GL_ALL_ATTRIB_BITS );
glDisable( GL_LIGHTING );
glEnable( GL_LINE_SMOOTH );
glPushMatrix();
glTranslatef( 0.0f, 0.0f, 0.0f );
glBegin( GL_LINES );
// X Axis (Red)
glColor3f( 1.0f, 0.0f, 0.0f );
glVertex3f( 0.0f, 0.0f, 0.0f );
glVertex3f( 2.0f, 0.0f, 0.0f );
// Y Axis (Green)
glColor3f( 0.0f, 1.0f, 0.0f );
glVertex3f( 0.0f, 0.0f, 0.0f );
glVertex3f( 0.0f, 2.0f, 0.0f );
// Z Axis (Blue)
glColor3f( 0.0f, 0.0f, 1.0f );
glVertex3f( 0.0f, 0.0f, 0.0f );
glVertex3f( 0.0f, 0.0f, 2.0f );
glEnd();
glPopMatrix();
glPopAttrib();
}
//-----------------------------------------------------------------------------
// Name: draw_crosshair
// Desc:
//-----------------------------------------------------------------------------
void draw_crosshair( void )
{
float fViewport[4];
float w = 32.0f, h = 32.0f;
// Get the current viewport. We want to make sure the crosshair
// is in the center of the screen, even if the window is resized.
glGetFloatv( GL_VIEWPORT, fViewport );
// Enable 2D rendering
glEnable2D();
glBegin( GL_LINES );
glColor3f( 1.0f, 1.0f, 0.0f );
glVertex2f( (fViewport[2]/2.0f)-(w/2.0f), fViewport[3]/2.0f );
glVertex2f( (fViewport[2]/2.0f)+(w/2.0f), fViewport[3]/2.0f );
glVertex2f( fViewport[2]/2.0f, (fViewport[3]/2.0f)-(h/2.0f) );
glVertex2f( fViewport[2]/2.0f, (fViewport[3]/2.0f)+(h/2.0f) );
glEnd();
// Disable 2D rendering
glDisable2D();
}
//-----------------------------------------------------------------------------
// Name: show_camera_stats
// Desc:
//-----------------------------------------------------------------------------
void show_camera_stats( void )
{
char string[128];
sprintf( string, "Camera <%f,%f,%f>", camera.vecPos.x, camera.vecPos.y, camera.vecPos.z );
glPrintf( 30, 30, GLUT_BITMAP_9_BY_15, string );
}
//-----------------------------------------------------------------------------
// Name: show_stats
// Desc:
//-----------------------------------------------------------------------------
void show_stats( void )
{
char string[128];
glColor3f( 0.0f, 0.5f, 1.0f );
sprintf( string, "Kills: %d", kills );
glPrintf( 450, 30, GLUT_BITMAP_9_BY_15, string );
if( sniper_mode )
sprintf( string, "Sniper Mode: On" );
else
sprintf( string, "Sniper Mode: Off" );
glPrintf( 450, 45, GLUT_BITMAP_9_BY_15, string );
}
//-----------------------------------------------------------------------------
// Name: print_selected_objects
// Desc:
//-----------------------------------------------------------------------------
void print_selected_objects( void )
{
int i;
// List the number of hits
printf( "Number of hits: %d\n\n", hits );
// List each the attributes of each object selected
for( i = 0; i < hits; i++ )
{
GLubyte number = select_buffer[i*4];
GLubyte min_z = select_buffer[i*4+1];
GLubyte max_z = select_buffer[i*4+2];
GLubyte name = select_buffer[i*4+3];
printf( "Number: %d\nMin Z: %d\nMax Z: %d\nName: %d\n\n",
number, min_z, max_z, name );
}
}
//-----------------------------------------------------------------------------
// Name: kill_selected_objects
// Desc: Marks all selected objects as dead.
//-----------------------------------------------------------------------------
void kill_selected_objects( void )
{
int i;
// Loop through the selection buffer and pick out the selected objects.
// This effect should be similar to that of an armour piercing sniper rifle.
for( i = 0; i < hits; i++ )
{
GLubyte name = select_buffer[i*4+3];
spheres[name].dead = 1;
spheres[name].death_time = GetTickCount();
// Increment the kill counter
kills++;
}
}
//-----------------------------------------------------------------------------
// Name: kill_closest_selected_object
// Desc: Marks the closest selected object as dead.
//-----------------------------------------------------------------------------
void kill_closest_selected_object( void )
{
int i = 0;
GLubyte name;
// If we only have one object selected, just kill that one.
if( hits == 1 )
{
name = select_buffer[i*4+3];
spheres[name].dead = 1;
spheres[name].death_time = GetTickCount();
kills++;
}
else
{
float d = spheres[select_buffer[i*4+3]].distance;
int closest = 0;
// Find the object with the shortest distance
/*for( i = 1; i < hits; i++ )
{
name = select_buffer[i*4+3];
if( d > spheres[name].distance )
{
d = spheres[name].distance;
closest = name;
}
}
spheres[closest].dead = 1;
spheres[closest].death_time = GetTickCount();*/
for( i = 0; i < hits; i++ )
{
name = select_buffer[i*4+3];
d = min( d, spheres[name].distance );
}
// Now set the closest sphere as dead
for( i = 0; i < hits; i++ )
{
name = select_buffer[i*4+3];
if( spheres[name].distance == d )
{
spheres[name].dead = 1;
spheres[name].death_time = GetTickCount();
kills++;
break;
}
}
}
}
//-----------------------------------------------------------------------------
// Name: draw_lazer_from_player_to_nearest_target
// Desc: Draws an orange antialiased line from the player to the nearest targeted
// object.
//-----------------------------------------------------------------------------
void draw_lazer_from_player_to_nearest_target( void )
{
int i = 0;
GLubyte name;
struct Vector3 p1, p2;
// Don't do anything if there is no target in the crosshair
if( !hits )
return;
// Set the first point relative to the camera's position
p1.x = camera.vecPos.x;
p1.y = 0.0f; //camera.vecPos.y;
p1.z = camera.vecPos.z;
// If we only have one object selected, then we know where to aim
// the lazer.
if( hits == 1 )
{
name = select_buffer[i*4+3];
p2.x = -spheres[name].position.x;
p2.y = -spheres[name].position.y;
p2.z = -spheres[name].position.z;
}
else
{
float d = spheres[select_buffer[i*4+3]].distance;
for( i = 0; i < hits; i++ )
{
name = select_buffer[i*4+3];
d = min( d, spheres[name].distance );
}
// Now target the closest sphere with the lazer
for( i = 0; i < hits; i++ )
{
name = select_buffer[i*4+3];
if( spheres[name].distance == d )
{
name = select_buffer[i*4+3];
p2.x = -spheres[name].position.x;
p2.y = -spheres[name].position.y;
p2.z = -spheres[name].position.z;
break;
}
}
}
// Draw the lazer as an orange line
glPushAttrib( GL_ALL_ATTRIB_BITS );
glDisable( GL_LIGHTING );
glDisable( GL_BLEND );
//glEnable( GL_LINE_SMOOTH );
glLineWidth( 1.5f );
glBegin( GL_LINES );
glColor3f( 1.0f, 0.5f, 0.0f );
glVertex3fv( (float*) &p1 );
glVertex3fv( (float*) &p2 );
glEnd();
glPopAttrib();
}
//-----------------------------------------------------------------------------
// Name: do_selection
// Desc:
//-----------------------------------------------------------------------------
void do_selection( int select_x, int select_y, int preselect )
{
GLint iViewport[4];
float fViewport[4];
int i;
// Set the selection buffer and specify it's size
glSelectBuffer( sphere_count*4, select_buffer );
// Get a copy of the current viewport
glGetIntegerv( GL_VIEWPORT, iViewport );
glGetFloatv( GL_VIEWPORT, fViewport );
// Switch into selection mode
glRenderMode( GL_SELECT );
// Clear the name stack
glInitNames();
// Give the stack at least one name or else it will generate an error
glPushName(0);
// Save the projection matrix
glMatrixMode( GL_PROJECTION );
glPushMatrix();
glLoadIdentity();
// Restrict the rendering area to the given point using gluPickMatrix
gluPickMatrix( select_x, select_y, 1.0f, 1.0f, iViewport );
gluPerspective( 45.0f, (GLdouble) fViewport[2]/fViewport[3], 0.1f, 500.0f );
// Now render selectable objects to the screen
glMatrixMode( GL_MODELVIEW );
// glutSwapBuffers();
// Render each sphere with a solid green colour
glColor3f( 0.0f, 1.0f, 0.0f );
for( i = 0; i < sphere_count; i++ )
{
// if( !spheres[i].dead )
if( spheres[i].size != 0.0f )
{
glPushMatrix();
glLoadName(i);
glTranslatef( -spheres[i].position.x, spheres[i].position.y, -spheres[i].position.z );
glutSolidSphere( spheres[i].size, 20, 20 );
glPopMatrix();
}
}
glPopName();
// Restore the projection matrix
glMatrixMode( GL_PROJECTION );
glPopMatrix();
// Get the number of hits (the number of objects drawn within that
// specified area) and return to render mode
hits = glRenderMode( GL_RENDER );
// Don't do anything except mark objects as selected in preselect mode
if( preselect )
{
// Go through the list of objects and deselect them all
for( i = 0; i < sphere_count; i++ )
spheres[i].selected = 0;
// Now mark all selected objects
for( i = 0; i < hits; i++ )
{
GLubyte name = select_buffer[i*4+3];
char string[64];
spheres[name].selected = 1;
// For debugging purposes, display the distance between you and
// each selected object.
sprintf( string, "Name: %d Distance %f", name, spheres[name].distance );
glPrintf( 30, 45+(i*20), GLUT_BITMAP_9_BY_15, string );
}
draw_lazer_from_player_to_nearest_target();
}
else
{
// Show the number of selected objects in the console (optional)
print_selected_objects();
// Kill selected object(s)
if( sniper_mode )
kill_selected_objects();
else
kill_closest_selected_object();
}
// Return to the modelview matrix
glMatrixMode( GL_MODELVIEW );
}
//-----------------------------------------------------------------------------
// Name: glh_extension_supported
// Desc: Checks the list of OpenGL extensions supported by this videocard/driver
// to see if it's supported. This function was borrowed from the NVIDIA
// SDK (v9.5). I was too lazy to write my own extension checking function :)
// See glh_extensions.h
//-----------------------------------------------------------------------------
int glh_extension_supported( const char *extension )
{
static const GLubyte *extensions = NULL;
const GLubyte *start;
GLubyte *where, *terminator;
// Extension names should not have spaces.
where = (GLubyte *) strchr( extension, ' ');
if ( where || *extension == '\0')
return 0;
if ( !extensions )
extensions = glGetString( GL_EXTENSIONS );
// It takes a bit of care to be fool-proof about parsing the
// OpenGL extensions string. Don't be fooled by sub-strings,
// etc.
start = extensions;
for (;;)
{
where = (GLubyte *) strstr( (const char *) start, extension );
if ( !where )
break;
terminator = where + strlen( extension );
if ( where == start || *(where - 1) == ' ' )
{
if ( *terminator == ' ' || *terminator == '\0' )
{
return 1;
}
}
start = terminator;
}
return 0;
}
//-----------------------------------------------------------------------------
// Name: InitScene
// Desc: Initializes extensions, textures, render states, etc. before rendering
//-----------------------------------------------------------------------------
int InitScene( void )
{
//
// Set default render states
//
// Enable depth testing
glEnable( GL_DEPTH_TEST );
glDepthFunc( GL_LEQUAL );
glClearDepth( 1.0f );
// Enable lighting
glEnable( GL_LIGHTING );
glEnable( GL_LIGHT0 );
// Enable colour material
glEnable( GL_COLOR_MATERIAL );
// Disable texture mapping (for now)
glDisable( GL_TEXTURE_2D );
// Disable blending
glDisable( GL_BLEND );
// Disable dithering
glDisable( GL_DITHER );
// Enable culling (counter clock wise)
glEnable( GL_CULL_FACE );
glCullFace( GL_CCW );
// Enable fog
// glEnable( GL_FOG );
// Enable perspective correction and polygon smoothing
glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST );
glHint( GL_POLYGON_SMOOTH_HINT, GL_NICEST );
//
// Initialize scene objects and geometry
//
texture_create();
spheres_init();
memset( &camera, 0, sizeof( struct ThirdPersonCamera_t ) );
camera.fRadius = 10.0f;
//
// Misc
//
srand( time( NULL ) );
return GL_TRUE;
}
//-----------------------------------------------------------------------------
// Name: DisplayFunction
// Desc: Our display function used to render our graphics in the main loop of
// our GLUT application.
//-----------------------------------------------------------------------------
void DisplayFunction( void )
{
int iViewport[4];
// Clear the screen and depth buffer
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
// Reset matrices to default position, scale, and rotation
glLoadIdentity();
// Position the camera behind our character and render it
glTranslatef( 0.0f, -2.0f, -camera.fRadius );
glRotatef( camera.vecRot.x, 1.0f, 0.0f, 0.0f );
glColor3f( 1.0f, 0.0f, 0.0f );
glutSolidCube( 3.0f );
// Rotate the camera as necessary
glRotatef( camera.vecRot.y, 0.0f, 1.0f, 0.0f );
glTranslatef( -camera.vecPos.x, 0.0f, -camera.vecPos.z );
// Calculate distances
calculate_distances();
// Do pre-selection
glGetIntegerv( GL_VIEWPORT, iViewport );
do_selection( iViewport[2]/2, iViewport[3]/2, TRUE );
// Render the floor
floor_render( 50.0f, -0.75f );
// Render spheres
spheres_render();
// Draw each axis
draw_axes();
// Draw the crosshair
draw_crosshair();
// Show camera's statistics
glColor3f( 0.0f, 0.0f, 1.0f );
show_camera_stats();
// Show other stats
show_stats();
// Display graphics to the user
glutSwapBuffers();
}
//-----------------------------------------------------------------------------
// Name: IdleFunction
// Desc: The function that is executed whenever the program is idle
//-----------------------------------------------------------------------------
void IdleFunction( void )
{
// Continue to display graphics even when messages aren't
// being processed.
glutPostRedisplay();
}
//-----------------------------------------------------------------------------
// Name: KeyboardFunction
// Desc: Handles keyboard input
//-----------------------------------------------------------------------------
void KeyboardFunction( GLubyte k, int x, int y )
{
static float fRotSpeed = 1.0f;
if (k=='q')
{
camera.vecRot.x += fRotSpeed;
if (camera.vecRot.x > 360) camera.vecRot.x -= 360;
}
if (k=='z')
{
camera.vecRot.x -= fRotSpeed;
if (camera.vecRot.x < -360) camera.vecRot.x += 360;
}
if (k=='c')
{
camera.vecRot.y += fRotSpeed;
if (camera.vecRot.y > 360) camera.vecRot.y -= 360;
}
if (k=='v')
{
camera.vecRot.y -= fRotSpeed;
if (camera.vecRot.y < -360) camera.vecRot.y += 360;
}
if (k=='w')
{
float xrotrad, yrotrad;
yrotrad = (camera.vecRot.y / 180.0f * 3.141592654f);
xrotrad = (camera.vecRot.x / 180.0f * 3.141592654f);
camera.vecPos.x += (float)(sin(yrotrad));
camera.vecPos.z -= (float)(cos(yrotrad));
camera.vecPos.y -= (float)(sin(xrotrad));
}
if (k=='s')
{
float xrotrad, yrotrad;
yrotrad = (camera.vecRot.y / 180.0f * 3.141592654f);
xrotrad = (camera.vecRot.x / 180.0f * 3.141592654f);
camera.vecPos.x -= (float)(sin(yrotrad));
camera.vecPos.z += (float)(cos(yrotrad));
camera.vecPos.y += (float)(sin(xrotrad));
}
if (k=='d')
{
float yrotrad;
yrotrad = (camera.vecRot.y / 180.0f * 3.141592654f);
camera.vecPos.x += (float)(cos(yrotrad)) * 0.5f;
camera.vecPos.z += (float)(sin(yrotrad)) * 0.5f;
}
if (k =='a')
{
float yrotrad;
yrotrad = (camera.vecRot.y / 180.0f * 3.141592654f);
camera.vecPos.x -= (float)(cos(yrotrad)) * 0.5f;
camera.vecPos.z -= (float)(sin(yrotrad)) * 0.5f;
}
// Has escape been pressed?
if( k == 27 )
{
exit(0);
}
// If not, then continue rendering
glutPostRedisplay();
}
//-----------------------------------------------------------------------------
// Name: MouseFunction
// Desc: Handles mouse input (movement)
//-----------------------------------------------------------------------------
void MouseFunction( int x, int y )
{
int diffx = x - camera.fLastX;
int diffy = y - camera.fLastY;
camera.fLastX = x;
camera.fLastY = y;
camera.vecRot.x += (float) diffy;
camera.vecRot.y += (float) diffx;
if( camera.vecRot.x < -30.0f )
camera.vecRot.x = -30.0f;
if( camera.vecRot.x > 90.0f )
camera.vecRot.x = 90.0f;
}
//-----------------------------------------------------------------------------
// Name: MouseClickFunction
// Desc: Handles mouse input (clicks)
//-----------------------------------------------------------------------------
void MouseClickFunction( int button, int state, int x, int y )
{
int iViewport[4];
int center_x;
int center_y;
if( ( button == GLUT_LEFT_BUTTON ) && ( state == GLUT_DOWN ) )
{
glGetIntegerv( GL_VIEWPORT, iViewport );
center_x = iViewport[2]/2;
center_y = iViewport[3]/2;
printf( "\nCenter X: %d\nCenter Y: %d\n\n", center_x, center_y );
do_selection( center_x, center_y, FALSE );
}
if( ( button == GLUT_RIGHT_BUTTON ) && ( state == GLUT_DOWN ) )
sniper_mode = !sniper_mode;
}
//-----------------------------------------------------------------------------
// Name: ReshapeFunction
// Desc: Changes the viewport when the window is resized
//-----------------------------------------------------------------------------
void ReshapeFunction( GLsizei width, GLsizei height )
{
// Prevent divide by zero exception
if( !height ) height = 1;
// Reset viewport
glViewport( 0, 0, width, height );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
// Reset perspective
gluPerspective( 45.0f, (GLdouble) width/height, 0.1f, 500.0f );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
}
//-----------------------------------------------------------------------------
// Name: main
// Desc: Program entry point.
//-----------------------------------------------------------------------------
int main( int argc, char* argv[] )
{
// Initialize OpenGL via GLUT library
glutInit( &argc, argv );
glutInitWindowSize( 640, 480 );
glutInitDisplayMode( GLUT_DOUBLE | GLUT_DEPTH | GLUT_RGBA );
glutCreateWindow( "Meta-Balls!" );
// glutFullScreen();
// Initialize scene objects and properties
InitScene();
// Set our input/output functions and begin the main loop
glutIdleFunc( IdleFunction );
glutDisplayFunc( DisplayFunction );
glutKeyboardFunc( KeyboardFunction );
glutReshapeFunc( ReshapeFunction );
glutMouseFunc( MouseClickFunction );
glutPassiveMotionFunc( MouseFunction );
glutMainLoop();
return 0;
}