As usual, I'll be posting completed projects here for your personal entertainment/education. Just be warned, these are actually really lame. =/
The assignment was to construct an icon as an X with a box around it using OpenGL primitives. Then we had to implement a few extremely simple motions (based on glut Timers). The extra credit options included scaling, rotation, and a built-in color swatch for selecting background colors...
Code: Select all
/*
Falco Girgis
2/10/11
CS445
There are two main portions of the program. The first is the class named "Icon" which encapsulates all data for the icon. One
is instantiated at global scope and manipulated throughout the event handlers.
The second is the event-handling functions and a few global helper functions. These are where the actual logic manipulating
the global icon instance is performed.
Extra Credit Attempts:
1) Icon can also scale (press 'q' or 'a')
2) Icon can also rotate (press 'w' or 's')
3) Different colored background for icon
4) Pop-up menu for changing the canvas background (press 'c' and click on a color)
*/
#include <GL/glut.h>
/* Note on my_setup.h:
I had to do my own initialization for the rotation (I needed to rotate about the z axis). I really wanted to attempt
the extra credit.
*/
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "math.h"
#define SCREEN_WIDTH 400
#define SCREEN_HEIGHT 400
//This is a simple vector structure used to group the components of a 2D vector (with simple addition and subtraction operators defined).
struct Vector2 {
float x, y;
Vector2(): x(0.0f), y(0.0f) {}
Vector2(const float p_x, const float p_y): x(p_x), y(p_y) {}
const Vector2 &operator+= (const Vector2 &rhs) { x += rhs.x; y += rhs.y; return *this; }
const Vector2 &operator-= (const Vector2 &rhs) { x -= rhs.x; y -= rhs.y; return *this; }
};
/*
This class encapsulates all data relating to the icon and instantiates in at global scope. The event handler functions set/get/operate
on this data depending on user-input.
*/
class Icon {
public:
//enumerated type for the current animation state of the icon.
enum ACTION { MOVE_UP, MOVE_DOWN, MOVE_LEFT, MOVE_RIGHT, NONE};
//enumerated type representing the different potential background colors of the icon
enum BG_COLOR { RED, BLUE, GREEN, YELLOW, BLACK, PURPLE, LAST_COLOR };
private:
//current action being performed
ACTION _currentAction;
//frame counter for number of frames the current action has executed
unsigned int _actionCount;
//current position of the icon
Vector2 _position;
//current size of the icon
Vector2 _scale;
//current rotation (in radians) of the icon
float _orientation;
//array of each vertex in local coordinates (transformed during render to construct actual icon).
Vector2 _localVertex[8];
//current color of the icon's background
BG_COLOR _curColor;
public:
Icon(): _currentAction(NONE), _actionCount(0), _position(200.0f, 200.0f), _scale(50.0f, 50.0f), _orientation(0.0f), _curColor(RED) {
//Initialize line segments
_localVertex[0] = Vector2(-0.5f, -0.5f);
_localVertex[1] = Vector2(0.5f, -0.5f);
_localVertex[2] = Vector2(0.5f, 0.5f);
_localVertex[3] = Vector2(-0.5f, 0.5f);
_localVertex[4] = Vector2(0.5f, -0.5f);
_localVertex[5] = Vector2(-0.5f, 0.5f);
_localVertex[6] = Vector2(-0.5f, -0.5f);
_localVertex[7] = Vector2(0.5f, 0.5f);
}
void Render() {
glLoadIdentity();
//move the icon into position
glTranslatef(_position.x, _position.y, 0.0f);
//resize the icon
glScalef(_scale.x, _scale.y, 0.0f);
//reorient the icon
glRotatef(_orientation, 0.0f, 0.0f, 1.0f);
//determine which background color to use
switch(_curColor) {
case RED:
glColor3f(1.0f, 0.0f, 0.0f);
break;
case BLUE:
glColor3f(0.0f, 0.0f, 1.0f);
break;
case GREEN:
glColor3f(0.0f, 1.0f, 0.0f);
break;
case YELLOW:
glColor3f(1.0f, 1.0f, 0.0f);
break;
case BLACK:
glColor3f(0.0f, 0.0f, 0.0f);
break;
case PURPLE:
glColor3f(1.0f, 0.0f, 1.0f);
break;
default:
break;
}
//render background
glBegin(GL_QUADS);
glVertex3f(-0.5f, -0.5f, 1.0f);
glVertex3f(0.5f, -0.5f, 1.0f);
glVertex3f(0.5f, 0.5f, 1.0f);
glVertex3f(-0.5f, 0.5f, 1.0f);
glEnd();
//render line-segments comprising icon
glColor3f(0, 0, 1.0f);
glBegin(GL_LINE_LOOP);
glVertex3f(_localVertex[0].x, _localVertex[0].y, 1.0f);
glVertex3f(_localVertex[1].x, _localVertex[1].y, 1.0f);
glVertex3f(_localVertex[2].x, _localVertex[2].y, 1.0f);
glVertex3f(_localVertex[3].x, _localVertex[3].y, 1.0f);
glEnd();
glBegin(GL_LINES);
glVertex2f(_localVertex[4].x, _localVertex[4].y);
glVertex2f(_localVertex[5].x, _localVertex[5].y);
glEnd();
glBegin(GL_LINES);
glVertex2f(_localVertex[6].x, _localVertex[6].y);
glVertex2f(_localVertex[7].x, _localVertex[7].y);
glEnd();
}
//tell icon to begin animation
void BeginAction(ACTION action) {
_currentAction = action;
_actionCount = 0;
}
//must be called from timer function to update vertical motion animation
void UpdateVertMotion() {
switch(_currentAction) {
case MOVE_UP:
case MOVE_DOWN:
_position += (_currentAction == MOVE_UP)? Vector2(0.0f, -2.0f) : Vector2(0.0f, 2.0f);
++_actionCount;
if(_actionCount >= 20) {
_actionCount = 0;
_currentAction = NONE;
}
break;
default:
return;
}
}
//must be called from the timer function to update horizontal motion animation
void UpdateHorizMotion() {
switch(_currentAction) {
case MOVE_LEFT:
_position -= Vector2(10.0f, 0.0f);
++_actionCount;
if(_actionCount >= 2) {
_actionCount = 0;
_currentAction = NONE;
}
break;
default:
return;
}
}
//scales the icon by the supplied vector
void Scale(const Vector2 &scale) {
_scale.x *= scale.x;
_scale.y *= scale.y;
}
//rotates the icon by the supplied float
void Rotate(const float delta) {
_orientation += delta;
}
//recolors the background image
void Recolor(BG_COLOR newColor) { _curColor = newColor; printf("NEW COLOR - %d\n", (int)newColor); }
} icon;
//global state variable for whether the color-selection swatch is active or not
bool colorSwatchActive = false;
//global functions/event handlers
void init(void);
void display(void);
void reshape(int, int);
void keypress(unsigned char, int, int);
void timer(int);
void mouse(int, int, int, int);
int main(int argc, char **argv) {
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize(SCREEN_WIDTH, SCREEN_HEIGHT);
glutInitWindowPosition(100, 100);
glutCreateWindow("CS445 Project #2");
//register event handlers
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutKeyboardFunc(keypress);
glutMouseFunc(mouse);
glutTimerFunc(1000/20, timer, 1); //1/20th of a second is the minimum update time of the two actions.
init();
glutMainLoop();
return 0;
}
void init() {
//initialize viewport and matrix modes
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClearDepth (1.0f);
glDepthFunc (GL_LEQUAL);
glEnable (GL_DEPTH_TEST);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
//gluOrtho2D(0.0f, SCREEN_WIDTH, SCREEN_HEIGHT, 0.0f);
//needed 3D orthographics project rather than 2D to rotate about z axis.
glOrtho(0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, -100, 10000);
glMatrixMode(GL_MODELVIEW);
glClear(GL_COLOR_BUFFER_BIT);
}
void RenderSwatch() {
if(!colorSwatchActive) return; //return if the swatch isn't toggled
glLoadIdentity();
//loop through each potential color
for(unsigned int i = 0; i < (unsigned int)Icon::LAST_COLOR; ++i) {
//determine color of current quad
switch((Icon::BG_COLOR)i) {
case Icon::RED:
glColor3f(1.0f, 0.0f, 0.0f);
break;
case Icon::BLUE:
glColor3f(0.0f, 0.0f, 1.0f);
break;
case Icon::GREEN:
glColor3f(0.0f, 1.0f, 0.0f);
break;
case Icon::YELLOW:
glColor3f(1.0f, 1.0f, 0.0f);
break;
case Icon::BLACK:
glColor3f(0.0f, 0.0f, 0.0f);
break;
case Icon::PURPLE:
glColor3f(1.0f, 0.0f, 1.0f);
break;
default:
break;
}
//render 20x20 quad in swatch
glBegin(GL_QUADS);
glVertex3f(0.0f + i*20.0f, SCREEN_HEIGHT-20.0f, 1.0f);
glVertex3f(20.0f + i*20.0f, SCREEN_HEIGHT-20.0f, 1.0f);
glVertex3f(20.0f + i*20.0f, SCREEN_HEIGHT, 1.0f);
glVertex3f(0.0f + i*20.0f, SCREEN_HEIGHT, 1.0f);
glEnd();
}
}
void display() {
glClear(GL_COLOR_BUFFER_BIT); //clear screen
icon.Render(); //render icon
RenderSwatch(); //render swatch
glFlush(); //request framebuffer flush
}
void reshape(int w, int h) {
//wasn't a requirement
}
void keypress(unsigned char key, int x, int y) { //exit on any keypress
if(key == ' ') {
icon.BeginAction(Icon::MOVE_DOWN); //begin downward animation
}
else if(key == 'u' || key == 'U') {
icon.BeginAction(Icon::MOVE_UP); //begin upward animation
}
else if(key == 'q' || key == 'Q') {
icon.Scale(Vector2(1.05f, 1.05f)); //scale the icon larger
}
else if(key == 'a' || key == 'A') {
icon.Scale(Vector2(0.95f, 0.95f)); //scale the icon smaller
}
else if(key == 'w' || key == 'W') {
icon.Rotate(1.2f); //rotate the icon counterclockwise
}
else if(key == 's' || key == 'S') {
icon.Rotate(-1.2f); //rotate the icon clockwise
}
else if(key == 'c' || key == 'C') {
colorSwatchActive = !colorSwatchActive; //toggle colorswatch
}
}
//Event handler registered to catch mouse events.
void mouse(int button, int state, int x, int y) {
if(button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) {
icon.BeginAction(Icon::MOVE_LEFT); //begin left animation
}
else if(button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN) {
exit(0); //quit program
}
if(!colorSwatchActive) return;
if(button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) { //choose background color
if(y < 380 || x > 120) return; //not within collision region
icon.Recolor((Icon::BG_COLOR)(x/20)); //calculate enumerated color type based on mouse position
colorSwatchActive = false; //close swatch
}
}
/*
Note on the timing:
I was a little uncertain based on what the handout said about the timing. I just wanted to explain the way I interpretted it.
1) U and Space keys cause the icon to begin moving at 20 units over 1 second, where each unit is 2 pixels. That means our timer must
update at 1sec/20 = .05 seconds.
2) The left mouse click causes the icon to move "immediately." The movement is done in 2 sets of 10 pixels each over a total time of
0.5 seconds. This means that each 10 pixel movement happens in half that time (0.25sec). That is 5 times the timer delay that is
required by the vertical motion, so we keep a static "counter" to know when this much time has ellapsed (since you can only have
one timer callback function).
*/
void timer(int v) {
static unsigned int count = 0;
display(); //called every timer event, since that's the most frequently anything can possible animate/change.
++count;
icon.UpdateVertMotion();
if(count == 5) {
icon.UpdateHorizMotion();
count = 0;
}
glutTimerFunc(1000/20, timer, 1);
}