Sprite Animation in OpenGL
|
Prof. David Bernstein |
Computer Science Department |
bernstdh@jmu.edu |
#ifndef edu_jmu_cs_Sprite_h #define edu_jmu_cs_Sprite_h #include "GL/glut.h" /** * A Sprite is a geometric object that "plays" on a Stage * * @author Prof. David Bernstein, James Madison University */ class Sprite { public: /** * Destructor. */ virtual ~Sprite(); /** * Handle clock tick events * * A Cube rotates itself in response to clock tick events */ virtual void handleTick() = 0; // A pure virtual (i.e., abstract) method /** * Handle "key pressed" events * * By default, a Sprite does nothing in response to mouse clicks * * @param key The key * @param x The x-position of the mouse * @param y The y-position of the mouse */ virtual void keyPressed(unsigned char key, int x, int y); /** * Handle "mouse clicked" events * * By default, a Sprite does nothing in response to mouse clicks * * @param button The button (e.g., GLUT_LEFT_BUTTON) * @param state The state (e.g., GLUT_UP or GLUT_DOWN) * @param x The x-position of the mouse * @param y The y-position of the mouse */ virtual void mouseClicked(int button, int state, int x, int y); /** * Handle timer events */ void timerTicked(); }; #endif
#include "Sprite.h" Sprite::~Sprite() { } void Sprite::keyPressed(unsigned char key, int x, int y) { // Do nothing } void Sprite::mouseClicked(int button, int state, int x, int y) { // Do nothing } void Sprite::timerTicked() { // Save the current transformation glPushMatrix(); // Handle the tick handleTick(); // Restore the transformation glPopMatrix(); }
#ifndef edu_jmu_cs_Stage_h #define edu_jmu_cs_Stage_h #include "GL/glut.h" #include "Sprite.h" #define MAX_SPRITES 10 /** * A Stage is a "window" that contains animated Sprite objects * * @author Prof. David Bernstein, James Madison University */ class Stage { public: /** * Default Constructor */ Stage(); /** * Destructor. */ ~Stage(); /** * Add a Sprite to this Stage. * * @param s The Sprite to add */ void addSprite(Sprite* s); /** * Forwards "key pressed" events to a particular Sprite. * * @param key The key * @param x The x-position of the mouse * @param y The y-position of the mouse * @param id The ID of the sprite to forward to */ void keyPressed(unsigned char key, int x, int y, int id); /** * Forwards "mouse clicked" events to a particular Sprite object. * * @param button The button (e.g., GLUT_LEFT_BUTTON) * @param state The state (e.g., GLUT_UP or GLUT_DOWN) * @param x The x-position of the mouse * @param y The y-position of the mouse * @param id The ID of the sprite to forward to */ void mouseClicked(int button, int state, int x, int y, int id); /** * Forwards timer events to Sprite objects. */ void onTimer(); /** * Forces the Stage to be painted/rendered. */ void paint(); /** * Called to reshape the Stage. * * @param width The new width * @param height The new height */ void reshape(int width, int height); /** * Start the Stage */ void start(); private: int size; GLfloat light0Position[4]; GLfloat colorWhite[4]; Sprite* sprite[MAX_SPRITES]; /** * Perform OpenGL initializations. */ void init(); }; #endif
#include "Stage.h" Stage::Stage() { light0Position[0] = 0.0; light0Position[1] = 0.0; light0Position[2] = 1.0; light0Position[3] = 1.0; colorWhite[0] = 1.0; colorWhite[1] = 1.0; colorWhite[2] = 1.0; colorWhite[3] = 1.0; size = 0; } Stage::~Stage() { // Someone else is responsible for deleting the Sprite } void Stage::addSprite(Sprite* s) { if (size < MAX_SPRITES) { sprite[size] = s; ++size; } } void Stage::init() { glClearColor(1, 1, 1, 1); // Shading glShadeModel(GL_SMOOTH); // Lighting GLfloat lightingmodelAmbient[] = { 0.2, 0.2, 0.2, 1.0 }; glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lightingmodelAmbient); glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_FALSE); glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE); // Lights glLightfv(GL_LIGHT0, GL_POSITION, light0Position); glLightfv(GL_LIGHT0, GL_DIFFUSE, colorWhite); glLightfv(GL_LIGHT0, GL_SPECULAR, colorWhite); // Enable glEnable(GL_LIGHTING); // Enable lighting glEnable(GL_LIGHT0); // Enable light number 0 glEnable(GL_DEPTH_TEST); // Enable depth buffering } void Stage::keyPressed(unsigned char key, int x, int y, int id) { if ((id >= 0) && (id < size)) sprite[id]->keyPressed(key, x, y); } void Stage::mouseClicked(int button, int state, int x, int y, int id) { if ((id >= 0) && (id < size)) sprite[id]->mouseClicked(button, state, x, y); } void Stage::onTimer() { // Clear the screen glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Tell the sprites for (int i = 0; i < size; i++) sprite[i]->timerTicked(); // Force a display callback glutPostRedisplay(); } void Stage::paint() { // Force the rendering (off-screen) glFlush(); // Handle the double buffering glutSwapBuffers(); } void Stage::reshape(int width, int height) { GLfloat aspect; glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); if (width <= height) { aspect = (GLfloat) height / (GLfloat) width; glOrtho(-5.0, 5.0, -5.0 * aspect, 5.0 * aspect, -5.0, 5.0); } else { aspect = (GLfloat) width / (GLfloat) height; glOrtho(-5.0 * aspect, 5.0 * aspect, -5.0, 5.0, -5.0, 5.0); } glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); } void Stage::start() { init(); glutMainLoop(); }
#ifndef edu_jmu_cs_Cube_h #define edu_jmu_cs_Cube_h #include "Sprite.h" /** * A Cube is a cube-shaped Sprite that rotates * * @author Prof. David Bernstein, James Madison University */ class Cube : public Sprite { public: /** * Constructor */ explicit Cube(GLfloat offset); /** * Destructor. */ ~Cube(); void handleTick(); /** * Handle "mouse clicked" events * * A Cube changes its axis of rotation in response to * "mouse clicked" events * * @param button The button (e.g., GLUT_LEFT_BUTTON) * @param state The state (e.g., GLUT_UP or GLUT_DOWN) * @param x The x-position of the mouse * @param y The y-position of the mouse */ void mouseClicked(int button, int state, int x, int y); private: GLfloat offset; GLfloat theta[3]; GLint axis; }; #endif
#include "Cube.h" Cube::Cube(GLfloat offset) { this->offset = offset; theta[0] = 0.0; theta[1] = 0.0; theta[2] = 0.0; axis = 2; } Cube::~Cube() { } void Cube::handleTick() { // Asjust the rotation angle theta[axis] += 2.0; if (theta[axis] > 360.0) theta[axis] = 0.0; // Reset the transformation matrix glLoadIdentity(); // Offset glTranslatef(offset, 0.0, 0.0); // Build the rotation matrix glRotatef(theta[0], 1.0, 0.0, 0.0); glRotatef(theta[1], 0.0, 1.0, 0.0); glRotatef(theta[2], 0.0, 0.0, 1.0); // Build the cube glutSolidCube(1.0); } void Cube::mouseClicked(int button, int state, int x, int y) { if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) axis = 0; else if (button == GLUT_MIDDLE_BUTTON && state == GLUT_DOWN) axis = 1; else if (button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN) axis = 2; }
#include "Stage.h" #include "Cube.h" /** * \file * The driver for a simple animation * * To avoid having to deal with the complexities of C++ method pointers, * this driver is written in C * * @author Prof. David Bernstein, James Madison University */ static int selectedSprite = 0; static GLint delay; static Stage stage; /** * The display callback */ void display() { stage.paint(); } /** * The keyboard callback */ void keyboard(unsigned char key, int x, int y) { // Don't forward these events. In this application they are used to // select a Sprite. selectedSprite = key - 48; if ((selectedSprite < 0) || (selectedSprite > 9)) selectedSprite = 0; } /** * The mouse callback * * @param button The button (e.g., GLUT_LEFT_BUTTON) * @param state The state (e.g., GLUT_UP or GLUT_DOWN) * @param x The x-position of the mouse * @param y The y-position of the mouse */ void mouse(int button, int state, int x, int y) { stage.mouseClicked(button, state, x, y, selectedSprite); } /** * The reshape callback * * @param width The new width * @param height The new height */ void reshape(int width, int height) { stage.reshape(width, height); } /** * The timer callback * * @param value The ID */ void timer(int value) { stage.onTimer(); // Re-start the timer glutTimerFunc(delay, timer, 1); } /** * The entry point of the application. * * @param argc The number of command line arguments * @param argv The array of command line arguments * @return A status code */ int main(int argc, char **argv) { Cube a(-0.75), b(0.75); delay = 20; stage.addSprite(&a); stage.addSprite(&b); glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); glutInitWindowSize(500, 500); glutCreateWindow("Stage"); glutReshapeFunc(reshape); glutDisplayFunc(display); glutMouseFunc(mouse); glutKeyboardFunc(keyboard); glutTimerFunc(delay, timer, 0); stage.start(); return 0; }
#ifndef edu_jmu_cs_Sun_h #define edu_jmu_cs_Sun_h #include "Sprite.h" #include "Earth.h" /** * A simple encpsulation of the sun. * * @author Prof. David Bernstein, James Madison University * @version 1.0 */ class Sun : public Sprite { public: /** * Default Constructor. */ Sun(); /** * Destructor. */ ~Sun(); /** * Handle clock tick events. */ void handleTick(); private: Earth earth; }; #endif
#include "Sun.h" Sun::Sun() { } Sun::~Sun() { } void Sun::handleTick() { GLfloat color[] = { 1.0, 1.0, 0.0 }; // Make space black glClearColor(0, 0, 0, 1); // Inform the Earth earth.handleTick(); // Render the Sun glMaterialfv(GL_FRONT, GL_AMBIENT, color); glMaterialfv(GL_FRONT, GL_DIFFUSE, color); glMaterialfv(GL_FRONT, GL_SPECULAR, color); glMaterialf(GL_FRONT, GL_SHININESS, 80); glutSolidSphere(1.0, 16, 16); }
#ifndef edu_jmu_cs_Earth_h #define edu_jmu_cs_Earth_h #include "Sprite.h" #include "Moon.h" /** * A simple encapsulation of the Earth. * * @author Prof. David Bernstein, James Madison University */ class Earth : public Sprite { public: /** * Default Constructor. */ Earth(); /** * Destructor. */ ~Earth(); /** * Handle clock tick events. */ void handleTick(); private: int day, hour; Moon moon; }; #endif
#include "Earth.h" Earth::Earth() { day = 0; hour = 0; } Earth::~Earth() { } void Earth::handleTick() { GLfloat color[] = { 0.0, 0.0, 1.0 }; ++hour; if (hour == 24) { hour = 0; day = (day + 1) % 365; } // Save the state glPushMatrix(); // Position the Earth in its orbit glRotatef(((GLfloat) day) / 365.0f * 360.0f, 0.0, 0.0, 1.0); glTranslatef(4.0, 0.0, 0.0); // Inform the Moon moon.handleTick(); // Rotate the Earth around its axis glRotatef(((GLfloat) hour) / 24.0f * 360.0f, 0.0, 0.0, 1.0); // Render the Earth glMaterialfv(GL_FRONT, GL_AMBIENT, color); glMaterialfv(GL_FRONT, GL_DIFFUSE, color); glMaterialfv(GL_FRONT, GL_SPECULAR, color); glMaterialf(GL_FRONT, GL_SHININESS, 80); glutSolidSphere(0.5, 8, 8); // Restore the state glPopMatrix(); }
#ifndef edu_jmu_cs_Moon_h #define edu_jmu_cs_Moon_h #include "Sprite.h" /** * A simple encapsulation of the moon. * * @author Prof. David Bernstein, James Madison University */ class Moon : public Sprite { public: /** * Default Constructor. */ Moon(); /** * Destructor. */ ~Moon(); /** * Handle clock tick events. */ void handleTick(); private: int dom, hour; }; #endif
#include "Moon.h" Moon::Moon() { dom = 0; hour = 0; } Moon::~Moon() { } void Moon::handleTick() { GLfloat color[] = { 1.0, 1.0, 1.0 }; ++hour; if (hour == 24) { hour = 0; dom = (dom + 1) % 28; } // Save the state glPushMatrix(); // Position the Moon in its orbit glRotatef(((GLfloat) dom) / 28.0f * 360.0f, 0.0, 0.0, 1.0); glTranslatef(1.0, 0.0, 0.0); // Render the Moon glMaterialfv(GL_FRONT, GL_AMBIENT, color); glMaterialfv(GL_FRONT, GL_DIFFUSE, color); glMaterialfv(GL_FRONT, GL_SPECULAR, color); glMaterialf(GL_FRONT, GL_SHININESS, 80); glutSolidSphere(0.15, 16, 16); // Restore the state glPopMatrix(); }
#include "Stage.h" #include "Sun.h" /** * \file * A simple "solar system". * * To avoid having to deal with the complexities of C++ method pointers, * this driver is written in C * * @author Prof. David Bernstein, James Madison University */ static GLint delay; static Stage stage; static Sun sun; /** * The display callback */ void display() { stage.paint(); } /** * The reshape callback * * @param width The new width * @param height The new height */ void reshape(int width, int height) { stage.reshape(width, height); } /** * The timer callback * * @param value The ID */ void timer(int value) { stage.onTimer(); // Re-start the timer glutTimerFunc(delay, timer, 1); } /** * The entry point of the application. * * @param argc The number of command line arguments * @param argv The array of command line arguments * @return A status code */ int main(int argc, char **argv) { delay = 20; stage.addSprite(&sun); glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB); glutInitWindowSize(500, 500); glutCreateWindow("Solar System"); glutReshapeFunc(reshape); glutDisplayFunc(display); glutTimerFunc(delay, timer, 0); stage.start(); return 0; }