This OpenGL program lays the groundwork for a basic asteroid-type game where users can aim a spaceship and fire missiles with the mouse and use menus to modify the type of missile.
spaceship.cc:
// Peter Bui
// CSE 40166 Computer Graphics (Fall 2010)
// Example 5: spaceship
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <list>
#ifdef __APPLE__
#include <GLUT/glut.h>
#else
#include <GL/glut.h>
#endif
using namespace std;
// Missile data structure ------------------------------------------------------
typedef struct {
double x;
double y;
double angle;
} missile;
// Constants -------------------------------------------------------------------
enum DISPLAY_LISTS {
DL_SPACESHIP,
DL_CROSSHAIR,
DL_MISSILE_TRIANGLE,
DL_MISSILE_DIAMOND,
DL_MISSILE_CIRCLE,
DL_MAX
};
// Global variables ------------------------------------------------------------
static GLuint DisplayListsBase;
static size_t WindowWidth = 640;
static size_t WindowHeight = 480;
static GLuint CursorX, CursorY;
static GLuint SpaceshipX, SpaceshipY;
static GLuint FrameRate = 60;
static GLuint MissileType = DL_MISSILE_TRIANGLE;
static int MissileTypeMenu;
static int MainMenu;
static list<missile> Missiles;
// Draw spaceship --------------------------------------------------------------
void
draw_spaceship()
{
glBegin(GL_TRIANGLE_FAN); {
double ox = 0.0, oy = 0.0, radius = 0.5;
int triangles = 32;
glVertex2f(ox, oy);
for (int i = 0; i <= triangles; i++) {
double angle = i * 2.0 * M_PI / triangles;
glVertex2f(ox + radius * cos(angle), oy + radius * sin(angle));
}
} glEnd();
glBegin(GL_TRIANGLES); {
glVertex2f( 0.55, 0.4);
glVertex2f( 0.55, -0.4);
glVertex2f( 0.95, 0.0);
} glEnd();
}
// Draw crosshair --------------------------------------------------------------
void
draw_crosshair()
{
glBegin(GL_LINE_LOOP); {
glVertex2f(-0.5, -0.5);
glVertex2f(-0.5, 0.5);
glVertex2f( 0.5, 0.5);
glVertex2f( 0.5, -0.5);
} glEnd();
glBegin(GL_LINES); {
glVertex2f(-0.5, 0.0);
glVertex2f( 0.5, 0.0);
glVertex2f( 0.0, 0.5);
glVertex2f( 0.0, -0.5);
} glEnd();
}
// Draw missile triangle -------------------------------------------------------
void
draw_missile_triangle()
{
glBegin(GL_TRIANGLES); {
glVertex2f(-0.5, 0.5);
glVertex2f(-0.5, -0.5);
glVertex2f( 0.5, 0.0);
} glEnd();
}
// Draw missile diamond --------------------------------------------------------
void
draw_missile_diamond()
{
glBegin(GL_QUADS); {
glVertex2f(-0.5, 0.0);
glVertex2f( 0.0, -0.5);
glVertex2f( 0.5, 0.0);
glVertex2f( 0.0, 0.5);
} glEnd();
}
// Draw missile circle ---------------------------------------------------------
void
draw_missile_circle()
{
glBegin(GL_TRIANGLE_FAN); {
double ox = 0.0, oy = 0.0, radius = 1.0;
int triangles = 32;
glVertex2f(ox, oy);
for (int i = 0; i <= triangles; i++) {
double angle = i * 2.0 * M_PI / triangles;
glVertex2f(ox + radius * cos(angle), oy + radius * sin(angle));
}
} glEnd();
}
// Compute angle ---------------------------------------------------------------
double
compute_angle(double a_x, double a_y, double b_x, double b_y)
{
double angle = 0.0;
if (b_x > a_x && b_y > a_y)
angle = atan2(b_y - a_y, b_x - a_x) * 180.0 / M_PI;
else if (b_x < a_x && b_y > a_y)
angle = 180.0 - (atan2(b_y - a_y, a_x - b_x) * 180.0 / M_PI);
else if (b_x < a_x && b_y < a_y)
angle = 180.0 + (atan2(a_y - b_y, a_x - b_x) * 180.0 / M_PI);
else if (b_x > a_x && b_y < a_y)
angle = 360.0 - (atan2(a_y - b_y, b_x - a_x) * 180.0 / M_PI);
return angle;
}
// Draw scene ------------------------------------------------------------------
void
draw_scene()
{
double angle = compute_angle(SpaceshipX, SpaceshipY, CursorX, CursorY);
glColor3f(0.0, 1.0, 0.0);
glPushMatrix();
glTranslatef(SpaceshipX, SpaceshipY, 0.0);
glRotatef(angle, 0.0, 0.0, 1.0);
glScalef(20.0, 20.0, 1.0);
glCallList(DisplayListsBase + DL_SPACESHIP);
glPopMatrix();
glPushMatrix();
glColor3f(1.0, 0.0, 0.0);
glTranslatef(CursorX, CursorY, 0.0);
glScalef(10.0, 10.0, 1.0);
glCallList(DisplayListsBase + DL_CROSSHAIR);
glPopMatrix();
for (list<missile>::iterator i = Missiles.begin(); i != Missiles.end(); i++) {
glColor3f(0.0, 0.0, 1.0);
i->x += 5.0*cos(i->angle*M_PI/180.0);
i->y += 5.0*sin(i->angle*M_PI/180.0);
if (i->x < 0 || i->x > WindowWidth || i->y < 0 || i->y > WindowHeight) {
i = Missiles.erase(i);
} else {
glPushMatrix();
glTranslatef(i->x, i->y, 0.0);
glRotatef(i->angle, 0.0, 0.0, 1.0);
glScalef(10.0, 10.0, 1.0);
glCallList(DisplayListsBase + MissileType);
glPopMatrix();
}
}
}
// Initialize scene ------------------------------------------------------------
void
init_scene()
{
glClearColor(1.0, 1.0, 1.0, 1.0);
glutSetCursor(GLUT_CURSOR_NONE);
SpaceshipX = WindowWidth / 2;
SpaceshipY = WindowHeight / 2;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0, WindowWidth, 0, WindowHeight);
glMatrixMode(GL_MODELVIEW);
glViewport(0, 0, WindowWidth, WindowHeight);
}
// Menu functions --------------------------------------------------------------
void
missile_type_menu(int id)
{
glutSetMenu(MissileTypeMenu);
switch (MissileType) {
case DL_MISSILE_TRIANGLE:
glutChangeToMenuEntry(1, "Triangle", DL_MISSILE_TRIANGLE);
break;
case DL_MISSILE_DIAMOND:
glutChangeToMenuEntry(2, "Diamond", DL_MISSILE_DIAMOND);
break;
case DL_MISSILE_CIRCLE:
glutChangeToMenuEntry(3, "Circle", DL_MISSILE_CIRCLE);
break;
}
MissileType = id;
switch (MissileType) {
case DL_MISSILE_TRIANGLE:
glutChangeToMenuEntry(1, "Triangle <", DL_MISSILE_TRIANGLE);
break;
case DL_MISSILE_DIAMOND:
glutChangeToMenuEntry(2, "Diamond <", DL_MISSILE_DIAMOND);
break;
case DL_MISSILE_CIRCLE:
glutChangeToMenuEntry(3, "Circle <", DL_MISSILE_CIRCLE);
break;
}
}
void
main_menu(int id)
{
switch (id) {
case 1: exit(EXIT_SUCCESS);
}
}
void
setup_menus()
{
MissileTypeMenu = glutCreateMenu(missile_type_menu);
glutAddMenuEntry("Triangle <", DL_MISSILE_TRIANGLE);
glutAddMenuEntry("Diamond", DL_MISSILE_DIAMOND);
glutAddMenuEntry("Circle", DL_MISSILE_CIRCLE);
MainMenu = glutCreateMenu(main_menu);
glutAddSubMenu("Missile Type", MissileTypeMenu);
glutAddMenuEntry("Quit", 1);
glutAttachMenu(GLUT_RIGHT_BUTTON);
}
// Make display lists ----------------------------------------------------------
void
make_display_lists()
{
DisplayListsBase = glGenLists(DL_MAX);
glNewList(DisplayListsBase + DL_SPACESHIP, GL_COMPILE); {
glPushMatrix();
draw_spaceship();
glPopMatrix();
} glEndList();
glNewList(DisplayListsBase + DL_CROSSHAIR, GL_COMPILE); {
glPushMatrix();
draw_crosshair();
glPopMatrix();
} glEndList();
glNewList(DisplayListsBase + DL_MISSILE_TRIANGLE, GL_COMPILE); {
glPushMatrix();
draw_missile_triangle();
glPopMatrix();
} glEndList();
glNewList(DisplayListsBase + DL_MISSILE_DIAMOND, GL_COMPILE); {
glPushMatrix();
draw_missile_diamond();
glPopMatrix();
} glEndList();
glNewList(DisplayListsBase + DL_MISSILE_CIRCLE, GL_COMPILE); {
glPushMatrix();
draw_missile_circle();
glPopMatrix();
} glEndList();
}
// Display callback ------------------------------------------------------------
void
display()
{
glClear(GL_COLOR_BUFFER_BIT);
draw_scene();
glutSwapBuffers();
}
// Reshape callback ------------------------------------------------------------
void
reshape(GLsizei nw, GLsizei nh)
{
WindowWidth = nw;
WindowHeight = nh;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0, WindowWidth, 0, WindowHeight);
glMatrixMode(GL_MODELVIEW);
glViewport(0, 0, WindowWidth, WindowHeight);
}
// Mouse callback --------------------------------------------------------------
void
mouse(int button, int state, int x, int y)
{
if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) {
missile m;
CursorX = x;
CursorY = WindowHeight - y;
m.x = SpaceshipX;
m.y = SpaceshipY;
m.angle = compute_angle(SpaceshipX, SpaceshipY, CursorX, CursorY);
Missiles.push_back(m);
}
}
// Passive Motion callback ------------------------------------------------------
void
passive_motion(int x, int y)
{
CursorX = x;
CursorY = WindowHeight - y;
}
// Keyboard callback ------------------------------------------------------------
void
keyboard(unsigned char key, int x, int y)
{
if (key == 'q' || key == 'Q')
exit(EXIT_SUCCESS);
}
// Timer callback ---------------------------------------------------------------
void
timer(int value)
{
glutPostRedisplay();
glutTimerFunc(1000/FrameRate, timer, value);
}
// Main execution ---------------------------------------------------------------
int
main(int argc, char *argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
glutInitWindowSize(WindowWidth, WindowHeight);
glutCreateWindow("board");
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutMouseFunc(mouse);
glutPassiveMotionFunc(passive_motion);
glutKeyboardFunc(keyboard);
glutTimerFunc(1000/FrameRate, timer, 0);
init_scene();
make_display_lists();
setup_menus();
glutMainLoop();
return (EXIT_SUCCESS);
}
// vim: sts=4 sw=4 ts=8 ft=cpp