I have been playing around with OpenGL and created a fairly basic 2D animation was just hoping someone could take a look and give me some pointers.
Main.cpp:
#include "glut.h"
#include <math.h>
#include <stdio.h>
int bc = 0.4; //building color
int frame=0;
class points{
public:
float x, y;
};
points startLine[2] = {{300,800},{300,800}};
points endLine[2] = {{250,170},{250,170}};
points tweenLine[4];
int numpoints=2;
float proportion = 0.0; //helicopter scene
float lScale = 0.0; //line scale
float fScale = 0.5;
float bScale = 0.5;
float bTrans = 0.0;
int flag = 0;
void tween(points source[10], points destination[10], double proportion, points tweenLine[10])
{
for( int i = 0; i < numpoints; i++)
{
// get the source point
double sourceX = source[i].x;
double sourceY = source[i].y;
// get the destination point
double destinationX = destination[i].x;
double destinationY = destination[i].y;
// get the difference between source and destination
double differenceX = (destinationX - sourceX);
double differenceY = (destinationY - sourceY);
// tween point is source position + proportion
// of distance between source and destination
double tweenX = sourceX + ( differenceX * proportion );
double tweenY = sourceY + ( differenceY * proportion );
// create point with tween co-ordinates in tween array
tweenLine[i].x = tweenX;
tweenLine[i].y = tweenY;
}
}
void printText(float x, float y, int spacing, void *font, char *string)
{
char *c;
int x1=x;
for (c=string; *c != '0円'; c++)
{
glRasterPos2f(x1,y);
glutBitmapCharacter(font, *c);
x1 = x1 + glutBitmapWidth(font,*c) + spacing;
}
}
void init(void)
{
// Set display window color to as glClearColor(R,G,B,Alpha)
glClearColor(0.2, 0.2, 0.4, 0.0);
// Set projection parameters.
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// Set 2D Transformation as gluOrtho2D(Min Width, Max Width, Min Height, Max Height)
gluOrtho2D(0.0, 800, 0.0, 600);
}
void background(){
glBegin(GL_QUADS);
//floor
glColor3f(0.0, 0.0, 0.0);
glVertex2f(-1.0,-1.0);
glVertex2f(1.0,-1.0);
//sky
glColor3f(0.2, 0.2, 0.4);
glVertex2f(1.0, 1.0);
glVertex2f(-1.0, 1.0);
glEnd();
}
//function to draw text
void man()
{
//body
glColor3f(0.0, 0.0, 0.0);
glBegin(GL_POLYGON);
glVertex2i(332, 150);
glVertex2i(340, 150);
glVertex2i(340, 160);
glVertex2i(332, 160);
glEnd();
//head
glColor3f(0.9, 0.2, 0.2);
glBegin(GL_POLYGON);
glVertex2i(334, 160);
glVertex2i(338, 160);
glVertex2i(338, 163);
glVertex2i(334, 163);
glEnd();
}
void morphSpotlight()
{
//morph to white
glColor3f(1, 1, 1);
tween(startLine, endLine, proportion, tweenLine);
glBegin(GL_LINE_LOOP);
for(int i=0; i<numpoints; i++)
{
glVertex2i(tweenLine[i].x,tweenLine[i].y);
}
proportion += 0.005;
if(proportion >0.5) proportion +=0.01;
if(proportion >0.8) proportion =0.8;
}
void window()
{
glColor3f(0.9, 0.9, 0.0);
glBegin(GL_POLYGON);
glVertex2i(330, 170);
glVertex2i(340, 170);
glVertex2i(340, 150);
glVertex2i(330, 150);
glEnd();
}
void backdrop()
{
// Back strip
glColor3f(bc, bc, bc);
glBegin(GL_POLYGON);
glVertex2i(0, 35);
glVertex2i(900, 35);
glVertex2i(900, 0);
glVertex2i(0, 0);
glEnd();
//
glColor3f(bc, bc, bc);
glBegin(GL_POLYGON);
glVertex2i(0, 60);
glVertex2i(40, 70);
glVertex2i(40, 0);
glVertex2i(0, 0);
glEnd();
//building
glColor3f(bc, bc, bc);
glBegin(GL_POLYGON);
glVertex2i(70, 90);
glVertex2i(100, 90);
glVertex2i(100, 0);
glVertex2i(70, 0);
glEnd();
//building 1
glColor3f(bc, bc, bc);
glBegin(GL_POLYGON);
glVertex2i(50, 110);
glVertex2i(70, 110);
glVertex2i(70, 0);
glVertex2i(50, 0);
glEnd();
//building 3
glColor3f(bc, bc, bc);
glBegin(GL_POLYGON);
glVertex2i(110, 160);
glVertex2i(180, 160);
glVertex2i(180, 0);
glVertex2i(110, 0);
glEnd();
//building 3 roof
glColor3f(bc, bc, bc);
glBegin(GL_POLYGON);
glVertex2i(110, 160);
glVertex2i(180, 160);
glVertex2i(145, 180);
glEnd();
//lip
glColor3f(bc, bc, bc);
glBegin(GL_POLYGON);
glVertex2i(180, 80);
glVertex2i(190, 80);
glVertex2i(190, 0);
glVertex2i(180, 0);
glEnd();
//angled building
glColor3f(bc, bc, bc);
glBegin(GL_POLYGON);
glVertex2i(190, 120);
glVertex2i(230, 105);
glVertex2i(230, 0);
glVertex2i(190, 0);
glEnd();
//reverse-angled building
glColor3f(bc, bc, bc);
glBegin(GL_POLYGON);
glVertex2i(230, 50);
glVertex2i(250, 60);
glVertex2i(250, 0);
glVertex2i(230, 0);
glEnd();
//rectangle building
glColor3f(bc, bc, bc);
glBegin(GL_POLYGON);
glVertex2i(265, 100);
glVertex2i(300, 100);
glVertex2i(300, 0);
glVertex2i(265, 0);
glEnd();
//lip
glColor3f(bc, bc, bc);
glBegin(GL_POLYGON);
glVertex2i(300, 60);
glVertex2i(310, 70);
glVertex2i(310, 0);
glVertex2i(300, 0);
glEnd();
//spire
glColor3f(bc, bc, bc);
glBegin(GL_POLYGON);
glVertex2i(310, 200);
glVertex2i(360, 200);
glVertex2i(360, 0);
glVertex2i(310, 0);
glEnd();
//spire top
glColor3f(bc, bc, bc);
glBegin(GL_POLYGON);
glVertex2i(310, 200);
glVertex2i(360, 200);
glVertex2i(335, 270);
glVertex2i(310, 0);
glEnd();
//
glColor3f(bc, bc, bc);
glBegin(GL_POLYGON);
glVertex2i(370, 130);
glVertex2i(400, 140);
glVertex2i(400, 0);
glVertex2i(370, 0);
glEnd();
//lip high
glColor3f(bc, bc, bc);
glBegin(GL_POLYGON);
glVertex2i(400, 140);
glVertex2i(405, 130);
glVertex2i(405, 0);
glVertex2i(400, 0);
glEnd();
//lip
glColor3f(bc, bc, bc);
glBegin(GL_POLYGON);
glVertex2i(405, 50);
glVertex2i(420, 60);
glVertex2i(420, 0);
glVertex2i(405, 0);
glEnd();
//small building
glColor3f(bc, bc, bc);
glBegin(GL_POLYGON);
glVertex2i(420, 60);
glVertex2i(440, 60);
glVertex2i(440, 0);
glVertex2i(420, 0);
glEnd();
//small adj building
glColor3f(bc, bc, bc);
glBegin(GL_POLYGON);
glVertex2i(440, 140);
glVertex2i(480, 135);
glVertex2i(480, 0);
glVertex2i(440, 0);
glEnd();
//mid size
glColor3f(bc, bc, bc);
glBegin(GL_POLYGON);
glVertex2i(485, 160);
glVertex2i(500, 170);
glVertex2i(500, 0);
glVertex2i(485, 0);
glEnd();
//lip
glColor3f(bc, bc, bc);
glBegin(GL_POLYGON);
glVertex2i(500, 170);
glVertex2i(530, 170);
glVertex2i(530, 0);
glVertex2i(500, 0);
glEnd();
//b
glColor3f(bc, bc, bc);
glBegin(GL_POLYGON);
glVertex2i(500, 200);
glVertex2i(550, 200);
glVertex2i(550, 0);
glVertex2i(500, 0);
glEnd();
//b
glColor3f(bc, bc, bc);
glBegin(GL_POLYGON);
glVertex2i(550, 55);
glVertex2i(560, 55);
glVertex2i(560, 0);
glVertex2i(500, 0);
glEnd();
//
glColor3f(bc, bc, bc);
glBegin(GL_POLYGON);
glVertex2i(560, 70);
glVertex2i(580, 73);
glVertex2i(580, 0);
glVertex2i(560, 0);
glEnd();
//
glColor3f(bc, bc, bc);
glBegin(GL_POLYGON);
glVertex2i(580, 73);
glVertex2i(595, 73);
glVertex2i(595, 0);
glVertex2i(580, 0);
glEnd();
//
glColor3f(bc, bc, bc);
glBegin(GL_POLYGON);
glVertex2i(595, 73);
glVertex2i(615, 70);
glVertex2i(615, 0);
glVertex2i(595, 0);
glEnd();
//
glColor3f(bc, bc, bc);
glBegin(GL_POLYGON);
glVertex2i(620, 80);
glVertex2i(650, 80);
glVertex2i(650, 0);
glVertex2i(620, 0);
glEnd();
//
glColor3f(bc, bc, bc);
glBegin(GL_POLYGON);
glVertex2i(650, 190);
glVertex2i(690, 195);
glVertex2i(690, 0);
glVertex2i(650, 0);
glEnd();
//
glColor3f(bc, bc, bc);
glBegin(GL_POLYGON);
glVertex2i(650, 190);
glVertex2i(690, 195);
glVertex2i(690, 0);
glVertex2i(650, 0);
glEnd();
//
glColor3f(bc, bc, bc);
glBegin(GL_POLYGON);
glVertex2i(690, 195);
glVertex2i(710, 195);
glVertex2i(710, 0);
glVertex2i(690, 0);
glEnd();
//
glColor3f(bc, bc, bc);
glBegin(GL_POLYGON);
glVertex2i(720, 60);
glVertex2i(760, 60);
glVertex2i(760, 0);
glVertex2i(720, 0);
glEnd();
//
glColor3f(bc, bc, bc);
glBegin(GL_POLYGON);
glVertex2i(762, 90);
glVertex2i(790, 95);
glVertex2i(790, 0);
glVertex2i(762, 0);
glEnd();
//
//
glColor3f(bc, bc, bc);
glBegin(GL_POLYGON);
glVertex2i(790, 60);
glVertex2i(800, 60);
glVertex2i(800, 0);
glVertex2i(790, 0);
glEnd();
}
void citySkyLine()
{
//Roof
//glClear(GL_COLOR_BUFFER_BIT); // Clear display window
// Set line segment color as glColor3f(R,G,B)
// Building 1
glColor3f(0.25, 0.25, 0.25);
glBegin(GL_POLYGON);
glVertex2i(80, 250);
glVertex2i(170, 250);
glVertex2i(170, 0);
glVertex2i(80, 0);
glEnd();
//building 2
glColor3f(0.25, 0.25, 0.25);
glBegin(GL_POLYGON);
glVertex2i(300, 230);
glVertex2i(420, 280);
glVertex2i(420, 0);
glVertex2i(300, 0);
glEnd();
//building 3
glColor3f(0.25, 0.25, 0.25);
glBegin(GL_POLYGON);
glVertex2i(500, 260);
glVertex2i(620, 260);
glVertex2i(620, 0);
glVertex2i(500, 0);
glEnd();
//building 3 roof
glColor3f(0.25, 0.25, 0.25);
glBegin(GL_POLYGON);
glVertex2i(500, 260);
glVertex2i(620, 260);
glVertex2i(560, 290);
glEnd();
//building 4
glColor3f(0.25, 0.25, 0.25);
glBegin(GL_POLYGON);
glVertex2i(750, 460);
glVertex2i(880, 460);
glVertex2i(750, 0);
glVertex2i(880, 0);
glEnd();
//building 4 lip
glColor3f(0.25, 0.25, 0.25);
glBegin(GL_POLYGON);
glVertex2i(735, 470);
glVertex2i(755, 470);
glVertex2i(735, 0);
glVertex2i(790, 0);
glEnd();
}
void drawBitmapText(char *string,float x,float y,float z)
{
char *c;
glRasterPos3f(x, y, z);
for (c=string; *c != '0円'; c++)
{
glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_10, *c);
}
}
void Timer( int value )
{
if( value ) glutPostRedisplay();
glutTimerFunc(40,Timer,value);
}
void visibility(int state)
{
switch (state)
{
case GLUT_VISIBLE:
Timer(1);
break;
case GLUT_NOT_VISIBLE:
Timer(0);
break;
default:
break;
}
}
void display(void)
{
fScale += 0.005f;
int bSpeed=-frame/10;//background speed
int sSpeed =0;
glClear(GL_COLOR_BUFFER_BIT);
frame++;
if((frame>=0) && (frame<200))
{
background();
glPushMatrix();
glTranslatef(bSpeed,0,0);
backdrop();
glPopMatrix();
citySkyLine();
if ((frame>50))
{
window();
}
if ((frame>100) && (frame <= 200))
{
glClear(GL_COLOR_BUFFER_BIT);
glPushMatrix();
glScalef(fScale, fScale,1);
backdrop();
citySkyLine();
window();
glPopMatrix();
}
}
if ((frame>=200) && (frame <500))
{
glClear(GL_COLOR_BUFFER_BIT);
glPushMatrix();
glScalef(1.5, 1.5,1); //maintain zoom
backdrop();
citySkyLine();
if ((frame>=330) && (frame <500))
{
morphSpotlight(); //morph window to create spotlight
}
window();
man();
glPopMatrix();
glDisable(GL_LIGHTING); //change text color
if ((frame>=210) && (frame <250))
{
glColor3f(1,1,1);
printText(300,310,3,GLUT_BITMAP_HELVETICA_18,"Is that someone at the door?");
}
if ((frame>=260) && (frame <300))
{
glColor3f(1,1,1);
printText(280,310,3,GLUT_BITMAP_TIMES_ROMAN_24,"KNOCK KNOCK!");
printText(250,290,3,GLUT_BITMAP_TIMES_ROMAN_24,"Open up its the police!");
}
if ((frame>=310) && (frame <360))
{
glColor3f(1,1,1);
printText(300,310,3,GLUT_BITMAP_HELVETICA_18,"Oh no! I'm done for!");
}
}
glFlush();
}
int main(int argc, char ** argv)
{
// Initialize GLUT
glutInit(&argc, argv);
// Set display mode
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
// Set top - left display window position.
glutInitWindowPosition(100, 100);
// Set display window width and height
glutInitWindowSize(1200, 600);
// Create display window with the given title
glutCreateWindow("Animation in OpenGL ");
// Execute initialization procedure
init();
// Send graphics to display window
glutDisplayFunc(display);
glutVisibilityFunc(visibility);
// Display everything and wait.
glutMainLoop();
}
1 Answer 1
Was just hoping someone could take a look and give me some pointers.
Sure, there you go:
0x7fff52ce196c
0x7fff52ce1960
0x7fff52ce195c
0x7fff52ce1950
:P
If you got past the lame joke, here are some suggestions that can improve your code:
Avoid global mutable data
It is tempting to just drop a few globals on such a small single-source-file program, but that's a weak escuse for doing so. Passing your data as function arguments is no trouble at all and has so many benefits over global variables.
Function parameters give you explicit dependencies, and that makes it so much easier reasoning about the code. Consider making those globals at the top level local to each function that uses them, passing as parameters where appropriate.
You can also group related data to make things simpler, for instance, you could have a Line
structure:
struct Line {
Point start;
Point end;
};
Speaking of structures...
Use struct
for "dumb" data aggregates
The usual convention in C++ (note that this is not a language requirement, just a convention for programmers) is to use class
for "smart" types, things with methods and private data, while struct
is used for simple data aggregates with public fields.
My suggestion is to make your point
a simple struct for that reason. I'd also suggest using a capitalized first letter for Point
, that's a very common naming convention for user-defined types, so you can for example declare Point point{};
.
Don't Repeat Yourself!
This block of code is repeated dozens of times, only changing the parameters:
glColor3f(bc, bc, bc); glBegin(GL_POLYGON); glVertex2i(0, 35); glVertex2i(900, 35); glVertex2i(900, 0); glVertex2i(0, 0); glEnd();
Code duplication is one of your worst enemies, fight it by defining reusable functions:
struct Color {
float r, g, b;
};
struct Rectangle {
int x, y;
int width, height;
};
void drawGLRectangle(const Color& color, const Rectangle& rect)
{
glColor3f(color.r, color.g, color.b);
glBegin(GL_QUADS);
glVertex2i(rect.x, rect.y);
glVertex2i(rect.x, rect.y + rect.height);
glVertex2i(rect.x + rect.width, rect.y + rect.height);
glVertex2i(rect.x + rect.width, rect.y);
glEnd();
}
Also note how I use structures to group related variables. Passing a Rectangle
and a Color
conveys a lot more meaning than passing a bunch of floats and ints, plus it makes a lot harder to mismatch the parameters, since a Color
doesn't convert to a Rectangle
.
Side Note: I think you can use GL_QUADS
for glBegin()
, since you're drawing a simple rectangle.
Use more named constants
You have a bunch of magic numbers in your code, which I have no idea of the meaning. You will probably also forget yourself why a particular value was used here-and-there six months from now, so replace as many of them as you can with named constants that describe what that value is meant to represent and why it was chosen. Where you think a constant is not necessary, at least provide a comment.
Use const pointer where appropriate
When taking a read-only parameter by pointer or reference, always use const
to clearly indicate that and disallow modification:
// V---- here
void drawBitmapText(const char* string, float x, float y, float z)
{
glRasterPos3f(x, y, z);
// V---- and here
for (const char* c = string; *c != '0円'; c++)
{
glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_10, *c);
}
}
Fix your indentation
Your code is not very consistently indented and this hurts readability. Manually indenting code is a thing of the past, install a formatting tool like Clang-Format in your editor, it works wonders!
Classes can help you better organize your program
Lastly a suggestion: Take a look at classes and some concepts of Object Oriented Programming (OOP). It might help you better organize your code in the future. You can start from here.