/*********************************************************************** * Source File: * User Interface Draw : put pixels on the screen * Author: * Br. Helfrich * Summary: * This is the code necessary to draw on the screen. We have a collection * of procedural functions here because each draw function does not * retain state. In other words, they are verbs (functions), not nouns * (variables) or a mixture (objects) ************************************************************************/ #include // need you ask? #include // convert an integer into text #include // I feel the need... the need for asserts #include // for clock #ifdef __APPLE__ #include // Main OpenGL library #include // Second OpenGL library #endif // __APPLE__ #ifdef __linux__ #include // Main OpenGL library #include // Second OpenGL library #endif // __linux__ #ifdef _WIN32 #include #include #include // OpenGL library we copied #define _USE_MATH_DEFINES #include #endif // _WIN32 #include "point.h" #include "uiDraw.h" using namespace std; #define deg2rad(value) ((M_PI / 180) * (value)) /********************************************* * NUMBER OUTLINES * We are drawing the text for score and things * like that by hand to make it look "old school." * These are how we render each individual charactger. * Note how -1 indicates "done". These are paired * coordinates where the even are the x and the odd * are the y and every 2 pairs represents a point ********************************************/ const char NUMBER_OUTLINES[10][20] = { {0, 0, 7, 0, 7, 0, 7,10, 7,10, 0,10, 0,10, 0, 0, -1,-1, -1,-1},//0 {7, 0, 7,10, -1,-1, -1,-1, -1,-1, -1,-1, -1,-1, -1,-1, -1,-1, -1,-1},//1 {0, 0, 7, 0, 7, 0, 7, 5, 7, 5, 0, 5, 0, 5, 0,10, 0,10, 7,10},//2 {0, 0, 7, 0, 7, 0, 7,10, 7,10, 0,10, 4, 5, 7, 5, -1,-1, -1,-1},//3 {0, 0, 0, 5, 0, 5, 7, 5, 7, 0, 7,10, -1,-1, -1,-1, -1,-1, -1,-1},//4 {7, 0, 0, 0, 0, 0, 0, 5, 0, 5, 7, 5, 7, 5, 7,10, 7,10, 0,10},//5 {7, 0, 0, 0, 0, 0, 0,10, 0,10, 7,10, 7,10, 7, 5, 7, 5, 0, 5},//6 {0, 0, 7, 0, 7, 0, 7,10, -1,-1, -1,-1, -1,-1, -1,-1, -1,-1, -1,-1},//7 {0, 0, 7, 0, 0, 5, 7, 5, 0,10, 7,10, 0, 0, 0,10, 7, 0, 7,10},//8 {0, 0, 7, 0, 7, 0, 7,10, 0, 0, 0, 5, 0, 5, 7, 5, -1,-1, -1,-1} //9 }; /************************************************************************ * DRAW DIGIT * Draw a single digit in the old school line drawing style. The * size of the glyph is 8x11 or x+(0..7), y+(0..10) * INPUT topLeft The top left corner of the character * digit The digit we are rendering: '0' .. '9' *************************************************************************/ void drawDigit(const Point & topLeft, char digit) { // we better be only drawing digits assert(isdigit(digit)); if (!isdigit(digit)) return; // compute the row as specified by the digit int r = digit - '0'; assert(r >= 0 && r <= 9); // go through each segment. for (int c = 0; c < 20 && NUMBER_OUTLINES[r][c] != -1; c += 4) { assert(NUMBER_OUTLINES[r][c ] != -1 && NUMBER_OUTLINES[r][c + 1] != -1 && NUMBER_OUTLINES[r][c + 2] != -1 && NUMBER_OUTLINES[r][c + 3] != -1); //Draw a line based off of the num structure for each number Point start; start.setX(topLeft.getX() + NUMBER_OUTLINES[r][c]); start.setY(topLeft.getY() - NUMBER_OUTLINES[r][c + 1]); Point end; end.setX(topLeft.getX() + NUMBER_OUTLINES[r][c + 2]); end.setY(topLeft.getY() - NUMBER_OUTLINES[r][c + 3]); drawLine(start, end); } } /************************************************************************* * DRAW NUMBER * Display an integer on the screen using the 7-segment method * INPUT topLeft The top left corner of the character * digit The digit we are rendering: '0' .. '9' *************************************************************************/ void drawNumber(const Point & topLeft, int number) { // our cursor, if you will. It will advance as we output digits Point point = topLeft; // is this negative bool isNegative = (number < 0); number *= (isNegative ? -1 : 1); // render the number as text ostringstream sout; sout << number; string text = sout.str(); // handle the negative if (isNegative) { glBegin(GL_LINES); glVertex2f(point.getX() + 1, point.getY() - 5); glVertex2f(point.getX() + 5, point.getY() - 5); glEnd(); point.addX(11); } // walk through the text one digit at a time for (const char *p = text.c_str(); *p; p++) { assert(isdigit(*p)); drawDigit(point, *p); point.addX(11); } } /************************************************************************* * DRAW TEXT * Draw text using a simple bitmap font * INPUT topLeft The top left corner of the text * text The text to be displayed ************************************************************************/ void drawText(const Point & topLeft, const char * text) { void *pFont = GLUT_BITMAP_HELVETICA_12; // also try _18 // prepare to draw the text from the top-left corner glRasterPos2f(topLeft.getX(), topLeft.getY()); // loop through the text for (const char *p = text; *p; p++) glutBitmapCharacter(pFont, *p); } /************************************************************************ * DRAW POLYGON * Draw a POLYGON from a given location (center) of a given size (radius). * INPUT center Center of the polygon * radius Size of the polygon * points How many points will we draw it. Larger the number, * the more line segments we will use * rotation True circles are rotation independent. However, if you * are drawing a 3-sided polygon (triangle), this matters! *************************************************************************/ void drawPolygon(const Point & center, int radius, int points, int rotation) { // begin drawing glBegin(GL_LINE_LOOP); //loop around a circle the given number of times drawing a line from //one point to the next for (double i = 0; i < 2 * M_PI; i += (2 * M_PI) / points) { Point temp(false /*check*/); temp.setX(center.getX() + (radius * cos(i))); temp.setY(center.getY() + (radius * sin(i))); rotate(temp, center, rotation); glVertex2f(temp.getX(), temp.getY()); } // complete drawing glEnd(); } /************************************************************************ * ROTATE * Rotate a given point (point) around a given origin (center) by a given * number of degrees (angle). * INPUT point The point to be moved * center The center point we will rotate around * rotation Rotation in degrees * OUTPUT point The new position *************************************************************************/ void rotate(Point & point, const Point & origin, int rotation) { // because sine and cosine are expensive, we want to call them only once double cosA = cos(deg2rad(rotation)); double sinA = sin(deg2rad(rotation)); // remember our original point Point tmp(false /*check*/); tmp.setX(point.getX() - origin.getX()); tmp.setY(point.getY() - origin.getY()); // find the new values point.setX(static_cast (tmp.getX() * cosA - tmp.getY() * sinA) + origin.getX()); point.setY(static_cast (tmp.getX() * sinA + tmp.getY() * cosA) + origin.getY()); } /************************************************************************ * DRAW LINE * Draw a line on the screen from the beginning to the end. * INPUT begin The position of the beginning of the line * end The position of the end of the line *************************************************************************/ void drawLine(const Point & begin, const Point & end, float red, float green, float blue) { // Get ready... glBegin(GL_LINES); glColor3f(red, green, blue); // Draw the actual line glVertex2f(begin.getX(), begin.getY()); glVertex2f( end.getX(), end.getY()); // Complete drawing glColor3f(1.0 /* red % */, 1.0 /* green % */, 1.0 /* blue % */); glEnd(); } /*********************************************************************** * DRAW Lander * Draw a moon-lander spaceship on the screen at a given point ***********************************************************************/ void drawLander(const Point & point) { // ultra simple point struct PT { int x; int y; } points[] = { {-6, 0}, {-10,0}, {-8, 0}, {-8, 3}, // left foot {-5, 4}, {-5, 7}, {-8, 3}, {-5, 4}, // left leg {-1, 4}, {-3, 2}, { 3, 2}, { 1, 4}, {-1, 4}, // bottom { 5, 4}, { 5, 7}, {-5, 7}, {-3, 7}, // engine square {-6,10}, {-6,13}, {-3,16}, { 3,16}, // left of habitat { 6,13}, { 6,10}, { 3, 7}, { 5, 7}, // right of habitat { 5, 4}, { 8, 3}, { 5, 7}, { 5, 4}, // right leg { 8, 3}, { 8, 0}, {10, 0}, { 6, 0} // right foot }; // draw it glBegin(GL_LINE_STRIP); for (int i = 0; i < sizeof(points) / sizeof(points[0]); i++) glVertex2f(point.getX() + points[i].x, point.getY() + points[i].y); // complete drawing glEnd(); } /*********************************************************************** * DRAW Lander Flame * Draw the flames coming out of a moonlander for thrust ***********************************************************************/ void drawLanderFlames(const Point & point, bool bottom, bool left, bool right) { // simple point struct PT { int x; int y; }; int iFlame = random(0, 3); // so the flame flickers // draw it glBegin(GL_LINE_LOOP); glColor3f(1.0 /* red % */, 0.0 /* green % */, 0.0 /* blue % */); // bottom thrust if (bottom) { PT points[3][3] = { { {-5, -6}, { 0, -1}, { 3, -10} }, { {-3, -6}, {-1, -2}, { 0, -15} }, { { 2, -12}, { 1, 0}, { 6, -4} } }; glVertex2f(point.getX() - 2, point.getY() + 2); for (int i = 0; i < 3; i++) glVertex2f(point.getX() + points[iFlame][i].x, point.getY() + points[iFlame][i].y); glVertex2f(point.getX() + 2, point.getY() + 2); } // right thrust if (right) { PT points[3][3] = { { {10, 14}, { 8, 12}, {12, 12} }, { {12, 10}, { 8, 10}, {10, 8} }, { {14, 11}, {14, 11}, {14, 11} } }; glVertex2f(point.getX() + 6, point.getY() + 12); for (int i = 0; i < 3; i++) glVertex2f(point.getX() + points[iFlame][i].x, point.getY() + points[iFlame][i].y); glVertex2f(point.getX() + 6, point.getY() + 10); } // left thrust if (left) { PT points[3][3] = { { {-10, 14}, { -8, 12}, {-12, 12} }, { {-12, 10}, { -8, 10}, {-10, 8} }, { {-14, 11}, {-14, 11}, {-14, 11} } }; glVertex2f(point.getX() - 6, point.getY() + 12); for (int i = 0; i < 3; i++) glVertex2f(point.getX() + points[iFlame][i].x, point.getY() + points[iFlame][i].y); glVertex2f(point.getX() - 6, point.getY() + 10); } glColor3f(1.0 /* red % */, 1.0 /* green % */, 1.0 /* blue % */); glEnd(); } /****************************************************************** * RANDOM * This function generates a random number. * * INPUT: min, max : The number of values (min <= num <= max) * OUTPUT : Return the integer ****************************************************************/ int random(int min, int max) { assert(min < max); int num = (rand() % (max - min)) + min; assert(min <= num && num <= max); return num; } /****************************************************************** * RANDOM * This function generates a random number. * * INPUT: min, max : The number of values (min <= num <= max) * OUTPUT : Return the double ****************************************************************/ double random(double min, double max) { assert(min <= max); double num = min + ((double)rand() / (double)RAND_MAX * (max - min)); assert(min <= num && num <= max); return num; } /************************************************************************ * DRAW RECTANGLE * Draw a rectangle on the screen centered on a given point (center) of * a given size (width, height), and at a given orientation (rotation) * INPUT center Center of the rectangle * width Horizontal size * height Vertical size * rotation Orientation *************************************************************************/ void drawRect(const Point & center, int width, int height, int rotation) { Point tl(false /*check*/); // top left Point tr(false /*check*/); // top right Point bl(false /*check*/); // bottom left Point br(false /*check*/); // bottom right //Top Left point tl.setX(center.getX() - (width / 2)); tl.setY(center.getY() + (height / 2)); //Top right point tr.setX(center.getX() + (width / 2)); tr.setY(center.getY() + (height / 2)); //Bottom left point bl.setX(center.getX() - (width / 2)); bl.setY(center.getY() - (height / 2)); //Bottom right point br.setX(center.getX() + (width / 2)); br.setY(center.getY() - (height / 2)); //Rotate all points the given degrees rotate(tl, center, rotation); rotate(tr, center, rotation); rotate(bl, center, rotation); rotate(br, center, rotation); //Finally draw the rectangle glBegin(GL_LINE_STRIP); glVertex2f(tl.getX(), tl.getY()); glVertex2f(tr.getX(), tr.getY()); glVertex2f(br.getX(), br.getY()); glVertex2f(bl.getX(), bl.getY()); glVertex2f(tl.getX(), tl.getY()); glEnd(); } /************************************************************************ * DRAW CIRCLE * Draw a circle from a given location (center) of a given size (radius). * INPUT center Center of the circle * radius Size of the circle *************************************************************************/ void drawCircle(const Point & center, int radius) { assert(radius > 1.0); const double increment = 1.0 / (double)radius; // begin drawing glBegin(GL_LINE_LOOP); // go around the circle for (double radians = 0; radians < M_PI * 2.0; radians += increment) glVertex2f(center.getX() + (radius * cos(radians)), center.getY() + (radius * sin(radians))); // complete drawing glEnd(); } /************************************************************************ * DRAW DOT * Draw a single point on the screen, 2 pixels by 2 pixels * INPUT point The position of the dow *************************************************************************/ void drawDot(const Point & point) { // Get ready, get set... glBegin(GL_POINTS); // Go... glVertex2f(point.getX(), point.getY() ); glVertex2f(point.getX() + 1, point.getY() ); glVertex2f(point.getX() + 1, point.getY() + 1); glVertex2f(point.getX(), point.getY() + 1); // Done! OK, that was a bit too dramatic glEnd(); } /************************************************************************ * DRAW Tough Bird * Draw a tough bird on the screen * INPUT point The position of the sacred * radius The size of the bird * hits How many its remaining to kill the bird *************************************************************************/ void drawToughBird(const Point & center, float radius, int hits) { assert(radius > 1.0); const double increment = M_PI / 6.0; // begin drawing glBegin(GL_TRIANGLES); // three points: center, pt1, pt2 Point pt1(false /*check*/); pt1.setX(center.getX() + (radius * cos(0.0))); pt1.setY(center.getY() + (radius * sin(0.0))); Point pt2(pt1); // go around the circle for (double radians = increment; radians <= M_PI * 2.0 + .5; radians += increment) { pt2.setX(center.getX() + (radius * cos(radians))); pt2.setY(center.getY() + (radius * sin(radians))); glVertex2f(center.getX(), center.getY()); glVertex2f(pt1.getX(), pt1.getY() ); glVertex2f(pt2.getX(), pt2.getY() ); pt1 = pt2; } // complete drawing glEnd(); // draw the score in the center if (hits > 0 && hits < 10) { glColor3f(0.0 /* red % */, 0.0 /* green % */, 0.0 /* blue % */); glRasterPos2f(center.getX() - 4, center.getY() - 3); glutBitmapCharacter(GLUT_BITMAP_8_BY_13, (char)(hits + '0')); glColor3f(1.0, 1.0, 1.0); // reset to white } } /************************************************************************ * DRAW Sacred Bird * Draw a sacred bird on the screen * INPUT point The position of the sacred * radius The size of the bird *************************************************************************/ void drawSacredBird(const Point & center, float radius) { // handle auto-rotation static float rotation = 0.0; rotation += 5.0; // begin drawing glBegin(GL_LINE_LOOP); glColor3f(1.0 /* red % */, 0.0 /* green % */, 0.0 /* blue % */); //loop around a circle the given number of times drawing a line from //one point to the next for (int i = 0; i < 5; i++) { Point temp(false /*check*/); float radian = (float)i * (M_PI * 2.0) * 0.4; temp.setX(center.getX() + (radius * cos(radian))); temp.setY(center.getY() + (radius * sin(radian))); rotate(temp, center, rotation); glVertex2f(temp.getX(), temp.getY()); } // complete drawing glColor3f(1.0, 1.0, 1.0); // reset to white glEnd(); } /********************************************************************** * DRAW SMALL ASTEROID **********************************************************************/ void drawSmallAsteroid( const Point & center, int rotation) { // ultra simple point struct PT { int x; int y; } points[] = { {-5, 9}, {4, 8}, {8, 4}, {8, -5}, {-2, -8}, {-2, -3}, {-8, -4}, {-8, 4}, {-5, 10} }; glBegin(GL_LINE_STRIP); glColor3f(1.0 /* red % */, 0.0/* green % */, 1.0/* blue % */); for (int i = 0; i < sizeof(points)/sizeof(PT); i++) { Point pt(center.getX() + points[i].x, center.getY() + points[i].y); rotate(pt, center, rotation); glVertex2f(pt.getX(), pt.getY()); } glEnd(); } /********************************************************************** * DRAW MEDIUM ASTEROID **********************************************************************/ void drawMediumAsteroid( const Point & center, int rotation) { // ultra simple point struct PT { int x; int y; } points[] = { {2, 8}, {8, 15}, {12, 8}, {6, 2}, {12, -6}, {2, -15}, {-6, -15}, {-14, -10}, {-15, 0}, {-4, 15}, {2, 8} }; glBegin(GL_LINE_STRIP); glColor3f(1.0 /* red % */, 0.0/* green % */, 1.0/* blue % */); for (int i = 0; i < sizeof(points)/sizeof(PT); i++) { Point pt(center.getX() + points[i].x, center.getY() + points[i].y); rotate(pt, center, rotation); glVertex2f(pt.getX(), pt.getY()); } glEnd(); } /********************************************************************** * DRAW LARGE ASTEROID **********************************************************************/ void drawLargeAsteroid( const Point & center, int rotation) { // ultra simple point struct PT { int x; int y; } points[] = { {0, 12}, {8, 20}, {16, 14}, {10, 12}, {20, 0}, {0, -20}, {-18, -10}, {-20, -2}, {-20, 14}, {-10, 20}, {0, 12} }; glBegin(GL_LINE_STRIP); glColor3f(1.0 /* red % */, 0.0/* green % */, 0.0/* blue % */); for (int i = 0; i < sizeof(points)/sizeof(PT); i++) { Point pt(center.getX() + points[i].x, center.getY() + points[i].y); rotate(pt, center, rotation); glVertex2f(pt.getX(), pt.getY()); } glEnd(); } /************************************************************************ * DRAW Ship * Draw a spaceship on the screen * INPUT point The position of the ship * angle Which direction it is ponted *************************************************************************/ void drawShip(const Point & center, int rotation, bool thrust) { // ultra simple point struct PT { int x; int y; }; // draw the ship const PT pointsShip[] = { // top r.wing r.engine l.engine l.wing top {0, 6}, {6, -6}, {2, -3}, {-2, -3}, {-6, -6}, {0, 6} }; glBegin(GL_LINE_STRIP); glColor3f(0.0 /* red % */, 0.0/* green % */, 1.0/* blue % */); for (int i = 0; i < sizeof(pointsShip)/sizeof(PT); i++) { Point pt(center.getX() + pointsShip[i].x, center.getY() + pointsShip[i].y); rotate(pt, center, rotation); glVertex2f(pt.getX(), pt.getY()); } glEnd(); // draw the flame if necessary if (thrust) { const PT pointsFlame[3][5] = { { {-2, -3}, {-2, -13}, { 0, -6}, { 2, -13}, {2, -3} }, { {-2, -3}, {-4, -9}, {-1, -7}, { 1, -14}, {2, -3} }, { {-2, -3}, {-1, -14}, { 1, -7}, { 4, -9}, {2, -3} } }; glBegin(GL_LINE_STRIP); glColor3f(1.0 /* red % */, 0.0 /* green % */, 0.0 /* blue % */); int iFlame = random(0, 3); for (int i = 0; i < 5; i++) { Point pt(center.getX() + pointsFlame[iFlame][i].x, center.getY() + pointsFlame[iFlame][i].y); rotate(pt, center, rotation); glVertex2f(pt.getX(), pt.getY()); } glColor3f(1.0, 1.0, 1.0); // reset to white glEnd(); } }