// // throw balls at other balls // Created by Bill 2008 // #include #include #include #include #include #include #include #include #include #include #include "vectors.h" using namespace std; #define kWindowWidth 800 #define kWindowHeight 600 typedef Vect GLvec; // ----- ball data structure ----- struct Ball { GLfloat mass, rad, bouncy; GLvec pos, vel, col; bool colliding; Ball(); }; // default constructor Ball::Ball() { mass = 0.0f; rad = 1.0f; bouncy = 0.9f; pos = GLvec(0.0f, 0.0f, 0.0f); vel = GLvec(0.0f, 0.0f, 0.0f); col = GLvec(1.0f, 1.0f, 1.0f); colliding = false; } // ----- box data structure ----- struct Box { GLvec c1, c2, pos, rad; Box(); Box(GLvec p, GLvec r); }; // default constructor Box::Box() { pos = GLvec(0.0f, 0.0f, 0.0f); rad = GLvec(1.0f, 1.0f, 1.0f); c1 = pos - rad; c2 = pos + rad; } // constructor by position and size Box::Box(GLvec p, GLvec r) { pos = p; rad = r; c1 = pos - rad; c2 = pos + rad; } // ----- level data structure ----- struct Level { int boxCount; vector levelBalls; vector levelBoxes; }; // ----- TARGA image data structure ----- struct tImage { unsigned iWidth, iHeight; GLubyte* imageData; tImage(); tImage(const char* tfname); }; // default constructor tImage::tImage() { iWidth = 0; iHeight = 0; imageData = NULL; } // constructor via filename tImage::tImage(const char* tfname) { char b; ifstream in(tfname); // no checks for successful file opening yet - move texture files at own risk! unsigned idfield = in.get(); unsigned cmaptype = in.get(); unsigned imgtype = in.get(); // skip color map spec for (int i = 0; i < 5; i++) in.get(b); // skip x origin in.get(b); in.get(b); // skip y origin in.get(b); in.get(b); unsigned one, two; one = in.get(); two = in.get(); iWidth = one + (two << 8); one = in.get(); two = in.get(); iHeight = one + (two << 8); unsigned pxlsize = in.get(); unsigned imgdescr = in.get(); // skip image id field for (unsigned i = 0; i < idfield; i++) in.get(b); // read image data imageData = new GLubyte[3*iWidth*iHeight]; for(unsigned i = 0; i < 3*iWidth*iHeight; i+=3) { imageData[i+2] = (GLubyte)in.get(); imageData[i+1] = (GLubyte)in.get(); imageData[i] = (GLubyte)in.get(); } } // global declarations int nx, ny, nf, nb; unsigned lev = 0; const int maxNumBalls = 40; clock_t ctim; vector balls; vector boxes; vector levels; vector pointer; tImage floorimage; tImage wallimage; GLvec gravity = GLvec(0.0f, -1.0f, 0.0f); GLvec defaultFireDir = GLvec(0.0f, 0.0f, -1.0f); GLvec fireDir = defaultFireDir; GLvec fireLoc = GLvec(0.0f, 5.0f, 9.5f); GLfloat* ballvertices; GLfloat* plotvertices; GLfloat* ballshadow; GLfloat* plotshadow; GLfloat* boxvertices; GLfloat* boxshadow; GLuint texture[2]; GLfloat stime, dt, cdt; const GLfloat pi = 3.1415926535f; // light source properties GLfloat lightAmbient[] = { 0.0f, 0.0f, 0.0f, 1.0f }; GLfloat lightDiffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f }; GLfloat lightSpecular[] = { 0.2f, 0.2f, 0.2f, 1.0f }; GLfloat lightPosition[] = { 5.0f, 10.0f, 10.0f, 1.0f }; // material properties GLfloat materialAmbient[] = { 0.2f, 0.2f, 0.2f, 1.0f }; GLfloat texMaterialDiffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f }; GLfloat ballMaterialDiffuse[] = { 0.5f, 0.6f, 1.0f, 1.0f }; GLfloat wallMaterialDiffuse[] = { 1.0f, 0.3f, 0.0f, 1.0f }; GLfloat floorMaterialDiffuse[] = { 1.0f, 0.8f, 0.3f, 1.0f }; GLfloat shadMaterialDiffuse[] = { 0.0f, 0.0f, 0.0f, 1.0f }; GLfloat materialSpecular[] = { 0.1f, 0.1f, 0.1f, 1.0f }; GLfloat pointerAmbient[] = { 1.0f, 1.0f, 1.0f, 1.0f }; GLfloat pointerDiffuse[] = { 1.0f, 1.0f, 0.0f, 1.0f }; // functions GLvoid checkPointer(int x, int y); GLvoid checkClicks(int b, int s, int x, int y); GLvoid InitGL(GLvoid); GLvoid DrawGLScene(GLvoid); GLvoid ReSizeGLScene(int Width, int Height); void initBallMesh(); void initLevels(); void nextLevel(int l); void drawBall(GLvec loc, GLfloat rad); void moveBalls(); void addBall(); void checkCollisions(); void collide(Ball& a, Ball& b); void collide(Ball& a, Box& b); void drawRoom(); void drawPointer(); bool insideBox(GLvec c1, GLvec c2, GLvec p); bool ballsIntersect(Ball& a, Ball& b); int main(int argc, char** argv) { // init GLUT and GL glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); glutInitWindowSize(kWindowWidth, kWindowHeight); glutInitWindowPosition(100, 100); glutCreateWindow(argv[0]); InitGL(); glutDisplayFunc(DrawGLScene); glutReshapeFunc(ReSizeGLScene); glutPassiveMotionFunc(checkPointer); glutMouseFunc(checkClicks); // init variables and data stime = 0.0f; dt = 0.1f; cdt = CLOCKS_PER_SEC/50; initBallMesh(); initLevels(); nextLevel(lev); // define pointer / gun pointer.push_back(GLvec(-0.1f, 0.0f, 0.5f)); pointer.push_back(GLvec(0.1f, 0.0f, 0.5f)); pointer.push_back(GLvec(0.1f, 0.0f, 0.0f)); pointer.push_back(GLvec(-0.1f, 0.0f, 0.5f)); pointer.push_back(GLvec(0.1f, 0.0f, 0.0f)); pointer.push_back(GLvec(-0.1f, 0.0f, 0.0f)); pointer.push_back(GLvec(-0.2f, 0.0f, 0.0f)); pointer.push_back(GLvec(0.2f, 0.0f, 0.0f)); pointer.push_back(GLvec(0.0f, 0.0f, -0.5f)); // enter main loop glutMainLoop(); // reclaim memory delete [] ballvertices; delete [] plotvertices; delete [] ballshadow; delete [] plotshadow; delete [] boxvertices; delete [] boxshadow; return 0; } // build ball mesh for later scaling and translation void initBallMesh() { nx = 16; ny = 16; nf = 18*(nx-1)*(ny-1); ballvertices = new GLfloat[nf]; plotvertices = new GLfloat[2*nf]; GLfloat vang = 0; GLfloat dvang = pi/(ny-1); GLfloat drang = 2*pi/(nx-1); GLfloat rang = 0; GLvec tn; for (int i = 0; i < nf; i += 18) { //1 ballvertices[i] = sin(vang)*sin(rang); ballvertices[i+1] = cos(vang); ballvertices[i+2] = sin(vang)*cos(rang); tn = GLvec(ballvertices[i], ballvertices[i+1], ballvertices[i+2]); tn = normalize(tn); plotvertices[2*i] = tn.x; plotvertices[2*i+1] = tn.y; plotvertices[2*i+2] = tn.z; //2 ballvertices[i+3] = sin(vang+dvang)*sin(rang); ballvertices[i+4] = cos(vang+dvang); ballvertices[i+5] = sin(vang+dvang)*cos(rang); tn = GLvec(ballvertices[i+3], ballvertices[i+4], ballvertices[i+5]); tn = normalize(tn); plotvertices[2*i+6] = tn.x; plotvertices[2*i+7] = tn.y; plotvertices[2*i+8] = tn.z; //3 ballvertices[i+6] = sin(vang+dvang)*sin(rang+drang); ballvertices[i+7] = cos(vang+dvang); ballvertices[i+8] = sin(vang+dvang)*cos(rang+drang); tn = GLvec(ballvertices[i+6], ballvertices[i+7], ballvertices[i+8]); tn = normalize(tn); plotvertices[2*i+12] = tn.x; plotvertices[2*i+13] = tn.y; plotvertices[2*i+14] = tn.z; //4 ballvertices[i+9] = sin(vang)*sin(rang); ballvertices[i+10] = cos(vang); ballvertices[i+11] = sin(vang)*cos(rang); tn = GLvec(ballvertices[i+9], ballvertices[i+10], ballvertices[i+11]); tn = normalize(tn); plotvertices[2*i+18] = tn.x; plotvertices[2*i+19] = tn.y; plotvertices[2*i+20] = tn.z; //5 ballvertices[i+12] = sin(vang+dvang)*sin(rang+drang); ballvertices[i+13] = cos(vang+dvang); ballvertices[i+14] = sin(vang+dvang)*cos(rang+drang); tn = GLvec(ballvertices[i+12], ballvertices[i+13], ballvertices[i+14]); tn = normalize(tn); plotvertices[2*i+24] = tn.x; plotvertices[2*i+25] = tn.y; plotvertices[2*i+26] = tn.z; //6 ballvertices[i+15] = sin(vang)*sin(rang+drang); ballvertices[i+16] = cos(vang); ballvertices[i+17] = sin(vang)*cos(rang+drang); tn = GLvec(ballvertices[i+15], ballvertices[i+16], ballvertices[i+17]); tn = normalize(tn); plotvertices[2*i+30] = tn.x; plotvertices[2*i+31] = tn.y; plotvertices[2*i+32] = tn.z; // increments rang += drang; if (rang >= 2*pi) { rang = 0; vang += dvang; } } // declare shadow mesh rang = 0; ballshadow = new GLfloat[nx*9]; plotshadow = new GLfloat[nx*9]; for (int i = 0; i < nx*9; i+=9) { ballshadow[i] = sin(rang); ballshadow[i+1] = 0.01f; ballshadow[i+2] = cos(rang); ballshadow[i+3] = sin(rang+drang); ballshadow[i+4] = 0.01f; ballshadow[i+5] = cos(rang+drang); ballshadow[i+6] = 0.0f; ballshadow[i+7] = 0.01f; ballshadow[i+8] = 0.0f; rang += drang; } } // make ball object by type; helper function Ball makeBall(int type, GLvec posit) { Ball b; b.pos = posit; b.vel = GLvec(0.0f, 0.0f, 0.0f); switch(type) { case 0: b.mass = 2.0f; b.rad = 0.7f; b.bouncy = 0.5f; b.col = GLvec(1.0f, 0.0f, 0.0f); break; case 1: b.mass = 1.0f; b.rad = 0.5f; b.bouncy = 0.85f; b.col = GLvec(0.0f, 1.0f, 0.5f); break; case 2: b.mass = 5.0f; b.rad = 0.8f; b.bouncy = 0.35f; b.col = GLvec(0.4f, 0.4f, 0.4f); break; default: break; } return b; } // fill levels array void initLevels() { Ball b; Box c; Level l; // level one c = Box(GLvec(-7.0f, 1.0f, -2.0f), GLvec(1.0f, 1.0f, 1.0f)); l.levelBoxes.push_back(c); c = Box(GLvec(7.0f, 1.0f, -2.0f), GLvec(1.0f, 1.0f, 1.0f)); l.levelBoxes.push_back(c); l.levelBalls.push_back(makeBall(0, GLvec(-7.0f, 4.2f, -2.0f))); l.levelBalls.push_back(makeBall(0, GLvec(7.0f, 4.2f, -2.0f))); l.boxCount = 2; levels.push_back(l); // level two l.levelBoxes.clear(); l.levelBalls.clear(); GLfloat xp = -2.0f; for (int i = 0; i < 5; i++) { c = Box(GLvec(xp+0.5f, 2.5f+xp/4, -11.5f-xp), GLvec(0.5f, 2.5f+xp/4, 0.5f)); l.levelBoxes.push_back(c); c = Box(GLvec(-xp+0.5f, 2.5f+xp/4, -11.5f-xp), GLvec(0.5f, 2.5f+xp/4, 0.5f)); l.levelBoxes.push_back(c); l.levelBalls.push_back(makeBall(1, GLvec(xp+0.5f, 5.0f+xp/2+0.75f, -11.5f-xp))); l.levelBalls.push_back(makeBall(1, GLvec(-xp+0.5f, 5.0f+xp/2+0.75f, -11.5f-xp))); xp -= 1.5f; } l.boxCount = 10; levels.push_back(l); // level three l.levelBoxes.clear(); l.levelBalls.clear(); c = Box(GLvec(3.0f, 1.0f, -2.0f), GLvec(1.0f, 1.0f, 1.0f)); l.levelBoxes.push_back(c); l.levelBalls.push_back(makeBall(2, GLvec(3.0f, 4.5f, -2.0f))); l.boxCount = 1; levels.push_back(l); // level four l.levelBoxes.clear(); l.levelBalls.clear(); c = Box(GLvec(0.0f, 2.5f, 0.0f), GLvec(5.0f, 0.5f, 1.0f)); l.levelBoxes.push_back(c); c = Box(GLvec(-4.5f, 1.0f, 0.0f), GLvec(0.5f, 1.0f, 1.0f)); l.levelBoxes.push_back(c); c = Box(GLvec(4.5f, 1.0f, 0.0f), GLvec(0.5f, 1.0f, 1.0f)); l.levelBoxes.push_back(c); l.levelBalls.push_back(makeBall(2, GLvec(4.5f, 3.5f, 0.0f))); l.levelBalls.push_back(makeBall(2, GLvec(-4.5f, 3.5f, 0.0f))); l.levelBalls.push_back(makeBall(1, GLvec(-3.0f, 3.5f, 0.0f))); l.levelBalls.push_back(makeBall(1, GLvec(-1.0f, 3.5f, 0.0f))); l.levelBalls.push_back(makeBall(1, GLvec(1.0f, 3.5f, 0.0f))); l.levelBalls.push_back(makeBall(1, GLvec(3.0f, 3.5f, 0.0f))); l.boxCount = 3; levels.push_back(l); // level five l.levelBoxes.clear(); l.levelBalls.clear(); c = Box(GLvec(8.0f, 0.5f, 0.0f), GLvec(2.0f, 0.5f, 5.0f)); l.levelBoxes.push_back(c); c = Box(GLvec(9.0f, 1.5f, 0.0f), GLvec(1.0f, 0.5f, 5.0f)); l.levelBoxes.push_back(c); c = Box(GLvec(-8.0f, 0.5f, 0.0f), GLvec(2.0f, 0.5f, 5.0f)); l.levelBoxes.push_back(c); c = Box(GLvec(-9.0f, 1.5f, 0.0f), GLvec(1.0f, 0.5f, 5.0f)); l.levelBoxes.push_back(c); l.levelBalls.push_back(makeBall(1, GLvec(9.0f, 2.6f, 2.5f))); l.levelBalls.push_back(makeBall(1, GLvec(9.0f, 2.6f, -2.5f))); l.levelBalls.push_back(makeBall(1, GLvec(9.0f, 2.6f, 0.0f))); l.levelBalls.push_back(makeBall(1, GLvec(-9.0f, 2.6f, 2.5f))); l.levelBalls.push_back(makeBall(1, GLvec(-9.0f, 2.6f, -2.5f))); l.levelBalls.push_back(makeBall(1, GLvec(-9.0f, 2.6f, 0.0f))); l.boxCount = 4; levels.push_back(l); // calculate maximum box vertex array size required int maxBoxes = 10; boxvertices = new GLfloat[2*3*16*maxBoxes]; boxshadow = new GLfloat[3*4*maxBoxes]; } // advance level; build box vertex arrays now rather than every frame void nextLevel(int l) { for (vector::iterator p = levels[l].levelBalls.begin(); p != levels[l].levelBalls.end(); p++) balls.push_back(*p); boxes.clear(); int bi = 0, bsi = 0; for (vector::iterator p = levels[l].levelBoxes.begin(); p != levels[l].levelBoxes.end(); p++) { boxes.push_back(*p); // left face boxvertices[bi] = -1.0f; boxvertices[bi+1] = 0.0f; boxvertices[bi+2] = 0.0f; boxvertices[bi+3] = p->c1.x; boxvertices[bi+4] = p->c1.y; boxvertices[bi+5] = p->c1.z; boxvertices[bi+6] = -1.0f; boxvertices[bi+7] = 0.0f; boxvertices[bi+8] = 0.0f; boxvertices[bi+9] = p->c1.x; boxvertices[bi+10] = p->c2.y; boxvertices[bi+11] = p->c1.z; boxvertices[bi+12] = -1.0f; boxvertices[bi+13] = 0.0f; boxvertices[bi+14] = 0.0f; boxvertices[bi+15] = p->c1.x; boxvertices[bi+16] = p->c2.y; boxvertices[bi+17] = p->c2.z; boxvertices[bi+18] = -1.0f; boxvertices[bi+19] = 0.0f; boxvertices[bi+20] = 0.0f; boxvertices[bi+21] = p->c1.x; boxvertices[bi+22] = p->c1.y; boxvertices[bi+23] = p->c2.z; bi += 24; // right face boxvertices[bi] = 1.0f; boxvertices[bi+1] = 0.0f; boxvertices[bi+2] = 0.0f; boxvertices[bi+3] = p->c2.x; boxvertices[bi+4] = p->c1.y; boxvertices[bi+5] = p->c1.z; boxvertices[bi+6] = 1.0f; boxvertices[bi+7] = 0.0f; boxvertices[bi+8] = 0.0f; boxvertices[bi+9] = p->c2.x; boxvertices[bi+10] = p->c2.y; boxvertices[bi+11] = p->c1.z; boxvertices[bi+12] = 1.0f; boxvertices[bi+13] = 0.0f; boxvertices[bi+14] = 0.0f; boxvertices[bi+15] = p->c2.x; boxvertices[bi+16] = p->c2.y; boxvertices[bi+17] = p->c2.z; boxvertices[bi+18] = 1.0f; boxvertices[bi+19] = 0.0f; boxvertices[bi+20] = 0.0f; boxvertices[bi+21] = p->c2.x; boxvertices[bi+22] = p->c1.y; boxvertices[bi+23] = p->c2.z; bi += 24; // front face boxvertices[bi] = 0.0f; boxvertices[bi+1] = 0.0f; boxvertices[bi+2] = 1.0f; boxvertices[bi+3] = p->c1.x; boxvertices[bi+4] = p->c1.y; boxvertices[bi+5] = p->c2.z; boxvertices[bi+6] = 0.0f; boxvertices[bi+7] = 0.0f; boxvertices[bi+8] = 1.0f; boxvertices[bi+9] = p->c2.x; boxvertices[bi+10] = p->c1.y; boxvertices[bi+11] = p->c2.z; boxvertices[bi+12] = 0.0f; boxvertices[bi+13] = 0.0f; boxvertices[bi+14] = 1.0f; boxvertices[bi+15] = p->c2.x; boxvertices[bi+16] = p->c2.y; boxvertices[bi+17] = p->c2.z; boxvertices[bi+18] = 0.0f; boxvertices[bi+19] = 0.0f; boxvertices[bi+20] = 1.0f; boxvertices[bi+21] = p->c1.x; boxvertices[bi+22] = p->c2.y; boxvertices[bi+23] = p->c2.z; bi += 24; // top face boxvertices[bi] = 0.0f; boxvertices[bi+1] = 1.0f; boxvertices[bi+2] = 0.0f; boxvertices[bi+3] = p->c1.x; boxvertices[bi+4] = p->c2.y; boxvertices[bi+5] = p->c1.z; boxvertices[bi+6] = 0.0f; boxvertices[bi+7] = 1.0f; boxvertices[bi+8] = 0.0f; boxvertices[bi+9] = p->c2.x; boxvertices[bi+10] = p->c2.y; boxvertices[bi+11] = p->c1.z; boxvertices[bi+12] = 0.0f; boxvertices[bi+13] = 1.0f; boxvertices[bi+14] = 0.0f; boxvertices[bi+15] = p->c2.x; boxvertices[bi+16] = p->c2.y; boxvertices[bi+17] = p->c2.z; boxvertices[bi+18] = 0.0f; boxvertices[bi+19] = 1.0f; boxvertices[bi+20] = 0.0f; boxvertices[bi+21] = p->c1.x; boxvertices[bi+22] = p->c2.y; boxvertices[bi+23] = p->c2.z; bi += 24; // shadows boxshadow[bsi] = p->c1.x; boxshadow[bsi+1] = 0.01f; boxshadow[bsi+2] = p->c1.z; boxshadow[bsi+3] = p->c2.x; boxshadow[bsi+4] = 0.01f; boxshadow[bsi+5] = p->c1.z; boxshadow[bsi+6] = p->c2.x; boxshadow[bsi+7] = 0.01f; boxshadow[bsi+8] = p->c2.z; boxshadow[bsi+9] = p->c1.x; boxshadow[bsi+10] = 0.01f; boxshadow[bsi+11] = p->c2.z; bsi += 12; } } // increment ball positions inline void moveBalls() { for (vector::iterator p = balls.begin(); p < balls.end() && !balls.empty(); p++) { p->vel = p->vel + gravity*dt; p->pos = p->pos + p->vel*dt; } } // check for ball collisions void checkCollisions() { for (vector::iterator p = balls.begin(); p < balls.end() && !balls.empty(); p++) { // left-right walls if (p->pos.x < -10.0f + p->rad) { p->pos.x = -10.0f + p->rad; p->vel.x = -p->vel.x; p->vel = p->vel*p->bouncy; } if (p->pos.x > 10.0f - p->rad) { p->pos.x = 10.0f - p->rad; p->vel.x = -p->vel.x; p->vel = p->vel*p->bouncy; } // front-back walls if (p->pos.z > 10.0f - p->rad) { p->pos.z = 10.0f - p->rad; p->vel.z = - p->vel.z; p->vel = p->vel*p->bouncy; } if (p->pos.z < -10.0f + p->rad) { p->pos.z = -10.0f + p->rad; p->vel.z = - p->vel.z; p->vel = p->vel*p->bouncy; } // floor if (p->pos.y < p->rad) { p->pos.y = p->rad; p->vel.y = - p->vel.y; p->vel = p->vel*p->bouncy; } // check for ball-box collisions for (vector::iterator q = boxes.begin(); q < boxes.end(); q++) if (insideBox(q->c1 - p->rad, q->c2 + p->rad, p->pos)) { collide(*p, *q); p->colliding = true; } // check for ball-ball collisions for (vector::iterator q = p+1; q < balls.end() && !balls.empty(); q++) if (ballsIntersect(*p, *q)) { collide(*p, *q); p->colliding = true; q->colliding = true; } } for (vector::iterator p = balls.begin(); p < balls.end() && !balls.empty(); p++) for (vector::iterator q = p+1; q < balls.end() && !balls.empty(); q++) if (p->colliding && q->colliding && ballsIntersect(*p, *q)) { collide(*p, *q); p->colliding = false; q->colliding = false; } } // inside box helper function inline bool insideBox(GLvec c1, GLvec c2, GLvec p) { if (p > c1 && p < c2) return true; return false; } // check for ball-ball intersection bool ballsIntersect(Ball& a, Ball& b) { GLvec dp = b.pos - a.pos; GLvec dv = b.vel - a.vel; GLfloat r = a.rad + b.rad; GLfloat pdotv = dot(dp,dv); GLfloat vdotv = dot(dv,dv); GLfloat pdotp = dot(dp,dp); GLfloat det = 4*(pow(pdotv,2) - vdotv*(pdotp-(r*r))); if (det < 0) return false; GLfloat t1 = (-pdotv - sqrt(det/4)) / vdotv; GLfloat t2 = (-pdotv + sqrt(det/4)) / vdotv; if (t1 < dt && t2 > 0) return true; return false; } // perform collision between balls void collide(Ball& a, Ball& b) { GLfloat bounce = min(a.bouncy, b.bouncy); GLvec sep = a.pos - b.pos; GLvec dr = normalize(sep); GLvec vcm = (a.vel*a.mass + b.vel*b.mass) / (a.mass + b.mass); GLvec p1 = (b.vel - vcm)*b.mass; GLvec p1dir = normalize(p1); GLfloat p1len = p1.length(); GLvec newp1 = (p1dir - (dr*2.0f*dot(p1dir, dr))) * p1len * bounce; if (sep.length() < a.rad+b.rad) { sep = dr*(a.rad+b.rad-sep.length()); a.pos = a.pos + sep; b.pos = b.pos - sep; } a.vel = (-newp1 / a.mass) + vcm; b.vel = (newp1 / b.mass) + vcm; a.pos = a.pos + a.vel*dt; b.pos = b.pos + b.vel*dt; } // perform collision between a ball and a box void collide(Ball& a, Box& b) { if (insideBox(b.c1, b.c2, a.pos)) { GLfloat l1, l2, lx, ly, lz, l; GLvec v = normalize(a.vel); l1 = (b.c1.x - a.pos.x)/v.x; l2 = (b.c2.x - a.pos.x)/v.x; lx = min(l1, l2); l1 = (b.c1.y - a.pos.y)/v.y; l2 = (b.c2.y - a.pos.y)/v.y; ly = min(l1, l2); l1 = (b.c1.z - a.pos.z)/v.z; l2 = (b.c2.z - a.pos.z)/v.z; lz = min(l1, l2); l = max(lx, max(ly, lz)); a.pos = a.pos + v*(l-a.rad/2); } GLvec bnorm = GLvec(0.0f, 0.0f, 0.0f); GLvec ratio = GLvec(0.0f, 0.0f, 0.0f); GLvec ra = a.pos - b.pos; GLvec m = GLvec(ra.x/fabs(ra.x), ra.y/fabs(ra.y), ra.z/fabs(ra.z)); GLvec nsep = GLvec(0.0f, 0.0f, 0.0f); int nc = 0; if (ra.x != 0.0f) { ratio.x = fabs(ra.x/b.rad.x); if (ratio.x > 1.0f) { bnorm.x = (ratio.x-1.0f) * m.x; nc++; } } if (ra.y != 0.0f) { ratio.y = fabs(ra.y/b.rad.y); if (ratio.y > 1.0f) { bnorm.y = (ratio.y-1.0f) * m.y; nc++; } } if (ra.z != 0.0f) { ratio.z = fabs(ra.z/b.rad.z); if (ratio.z > 1.0f) { bnorm.z = (ratio.z-1.0f) * m.z; nc++; } } bnorm = normalize(bnorm); nsep.x = bnorm.x*(fabs(a.pos.x - b.pos.x)-b.rad.x); nsep.y = bnorm.y*(fabs(a.pos.y - b.pos.y)-b.rad.y); nsep.z = bnorm.z*(fabs(a.pos.z - b.pos.z)-b.rad.z); if (nc > 1) { if (ratio.x > 1.0f) ratio.x = 1.0f; if (ratio.y > 1.0f) ratio.y = 1.0f; if (ratio.z > 1.0f) ratio.z = 1.0f; GLvec nearestPoint = b.pos + GLvec(b.rad.x*ratio.x*m.x, b.rad.y*ratio.y*m.y, b.rad.z*ratio.z*m.z); nsep = a.pos - nearestPoint; if (nsep.length() > a.rad) return; } a.pos = a.pos + bnorm*(a.rad-nsep.length()); GLvec adir = normalize(a.vel); GLfloat aspeed = a.vel.length(); a.vel = (adir - (bnorm*2.0f*dot(adir, bnorm))) * aspeed * a.bouncy; a.pos = a.pos + a.vel*dt; } // add new ball at pointer location and direction void addBall() { GLvec dir = normalize(fireDir); GLvec tempdir2 = GLvec(dir.x, dir.y+1.0f, dir.z); GLvec xdir = normalize(cross(dir, tempdir2)); GLvec ydir = normalize(cross(dir, xdir)); xdir = xdir*(GLfloat(rand())/RAND_MAX - 0.5f)*0.05f; ydir = ydir*(GLfloat(rand())/RAND_MAX - 0.5f)*0.05f; dir = dir + xdir + ydir; Ball newb; newb.mass = 0.5f; newb.rad = 0.3f; newb.bouncy = 0.8f; newb.pos = fireLoc; newb.vel = dir*5.0f; newb.col = GLvec(1.0f, 1.0f, 0.0f); balls.push_back(newb); } // plot room walls and floor void drawRoom() { glMaterialfv(GL_FRONT, GL_DIFFUSE, texMaterialDiffuse); glBindTexture(GL_TEXTURE_2D, texture[1]); glBegin(GL_TRIANGLES); // left wall glNormal3f(1.0f, 0.0f, 0.0f); glTexCoord2f(0.0f, 0.0f); glVertex3f(-10.0f, 0.0f, -10.0f); glNormal3f(1.0f, 0.0f, 0.0f); glTexCoord2f(1.0f, 0.0f); glVertex3f(-10.0f, 0.0f, 10.0f); glNormal3f(1.0f, 0.0f, 0.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f(-10.0f, 20.0f, 10.0f); glNormal3f(1.0f, 0.0f, 0.0f); glTexCoord2f(0.0f, 0.0f); glVertex3f(-10.0f, 0.0f, -10.0f); glNormal3f(1.0f, 0.0f, 0.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f(-10.0f, 20.0f, 10.0f); glNormal3f(1.0f, 0.0f, 0.0f); glTexCoord2f(0.0f, 1.0f); glVertex3f(-10.0f, 20.0f, -10.0f); // right wall glNormal3f(-1.0f, 0.0f, 0.0f); glTexCoord2f(0.0f, 0.0f); glVertex3f(10.0f, 0.0f, 10.0f); glNormal3f(-1.0f, 0.0f, 0.0f); glTexCoord2f(1.0f, 0.0f); glVertex3f(10.0f, 0.0f, -10.0f); glNormal3f(-1.0f, 0.0f, 0.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f(10.0f, 20.0f, -10.0f); glNormal3f(-1.0f, 0.0f, 0.0f); glTexCoord2f(0.0f, 0.0f); glVertex3f(10.0f, 0.0f, 10.0f); glNormal3f(-1.0f, 0.0f, 0.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f(10.0f, 20.0f, -10.0f); glNormal3f(-1.0f, 0.0f, 0.0f); glTexCoord2f(0.0f, 1.0f); glVertex3f(10.0f, 20.0f, 10.0f); // back wall glNormal3f(0.0f, 0.0f, 1.0f); glTexCoord2f(0.0f, 0.0f); glVertex3f(-10.0f, 0.0f, -10.0f); glNormal3f(0.0f, 0.0f, 1.0f); glTexCoord2f(1.0f, 0.0f); glVertex3f(10.0f, 0.0f, -10.0f); glNormal3f(0.0f, 0.0f, 1.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f(10.0f, 20.0f, -10.0f); glNormal3f(0.0f, 0.0f, 1.0f); glTexCoord2f(0.0f, 0.0f); glVertex3f(-10.0f, 0.0f, -10.0f); glNormal3f(0.0f, 0.0f, 1.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f(10.0f, 20.0f, -10.0f); glNormal3f(0.0f, 0.0f, 1.0f); glTexCoord2f(0.0f, 1.0f); glVertex3f(-10.0f, 20.0f, -10.0f); glEnd(); // floor glBindTexture(GL_TEXTURE_2D, texture[0]); glBegin(GL_QUADS); glNormal3f(0.0f, 1.0f, 0.0f); glTexCoord2f(1.0f, 0.0f); glVertex3f(-10.0f, 0.0f, -10.0f); glNormal3f(0.0f, 1.0f, 0.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f(10.0f, 0.0f, -10.0f); glNormal3f(0.0f, 1.0f, 0.0f); glTexCoord2f(0.0f, 1.0f); glVertex3f(10.0f, 0.0f, 10.0f); glNormal3f(0.0f, 1.0f, 0.0f); glTexCoord2f(0.0f, 0.0f); glVertex3f(-10.0f, 0.0f, 10.0f); glEnd(); } // plot boxes and shadows void drawBoxes() { // draw boxes glMaterialfv(GL_FRONT, GL_DIFFUSE, floorMaterialDiffuse); glInterleavedArrays(GL_N3F_V3F, 0, boxvertices); glDrawArrays(GL_QUADS, 0, levels[lev].boxCount*16); // draw shadows GLfloat boxCol[4]; boxCol[0] = 0.0f; boxCol[1] = 0.0f; boxCol[2] = 0.0f; glMaterialfv(GL_FRONT, GL_DIFFUSE, boxCol); glInterleavedArrays(GL_V3F, 0, boxshadow); glDrawArrays(GL_QUADS, 0, levels[lev].boxCount*4); } // plot single ball void drawBall(const Ball& b) { // select ball colour GLfloat ballCol[4]; ballCol[0] = b.col.x; ballCol[1] = b.col.y; ballCol[2] = b.col.z; ballCol[3] = 1.0f; for (int i = 0; i < nf; i += 3) { plotvertices[2*i+3] = b.pos.x + b.rad*ballvertices[i]; plotvertices[2*i+4] = b.pos.y + b.rad*ballvertices[i+1]; plotvertices[2*i+5] = b.pos.z + b.rad*ballvertices[i+2]; } glMaterialfv(GL_FRONT, GL_DIFFUSE, ballCol); glInterleavedArrays(GL_N3F_V3F, 0, plotvertices); glDrawArrays(GL_TRIANGLES, 0, nf/3); // draw shadow GLfloat br = b.rad*(1 - b.pos.y/20); ballCol[0] = 0.0f; ballCol[1] = 0.0f; ballCol[2] = 0.0f; for (int i = 0; i < nx*9; i += 3) { plotshadow[i] = b.pos.x + br*ballshadow[i]; plotshadow[i+1] = ballshadow[i+1]; plotshadow[i+2] = b.pos.z + br*ballshadow[i+2]; } glMaterialfv(GL_FRONT, GL_DIFFUSE, ballCol); glInterleavedArrays(GL_V3F, 0, plotshadow); glDrawArrays(GL_TRIANGLES, 0, nx*3); } // plot pointer GLvoid drawPointer() { GLvec pointy = GLvec(0.0f, 1.0f, 0.0f); GLvec pointx = normalize(cross(pointy, fireDir)); GLvec pointup = -normalize(cross(pointx, fireDir)); glBegin(GL_TRIANGLES); glMaterialfv(GL_FRONT, GL_AMBIENT, pointerAmbient); glMaterialfv(GL_FRONT, GL_DIFFUSE, pointerDiffuse); for (vector::iterator p = pointer.begin(); p != pointer.end(); p++) { GLvec vert = fireLoc + pointx*p->x - fireDir*p->z; glNormal3f(pointup.x, pointup.y, pointup.z); glVertex3f(vert.x, vert.y, vert.z); } glMaterialfv(GL_FRONT, GL_AMBIENT, materialAmbient); glEnd(); } // update pointer position based on mouse movements GLvoid checkPointer(int x, int y) { int winx = glutGet(GLUT_WINDOW_WIDTH); int winy = glutGet(GLUT_WINDOW_HEIGHT); GLfloat rotx = -(x-winx/2)*winx/180; GLfloat roty = -(y-winy/2)*winy/180; fireDir = defaultFireDir.rotate_y(rotx/1000); fireDir = fireDir.rotate_x(roty/1000); } // check for fire button pressed GLvoid checkClicks(int b, int s, int x, int y) { if (b == GLUT_LEFT_BUTTON && s == GLUT_UP) if (balls.size() < maxNumBalls) addBall(); } // init GL (duh) GLvoid InitGL(GLvoid) { glClearColor(0.1f, 0.2f, 0.5f, 0.0f); glClearDepth(1.0); glDepthFunc(GL_LESS); glEnable(GL_DEPTH_TEST); glEnable(GL_NORMALIZE); glEnable(GL_TEXTURE_2D); glShadeModel(GL_SMOOTH); glLightfv(GL_LIGHT1, GL_AMBIENT, lightAmbient); glLightfv(GL_LIGHT1, GL_DIFFUSE, lightDiffuse); glLightfv(GL_LIGHT1, GL_SPECULAR, lightSpecular); glLightfv(GL_LIGHT1, GL_POSITION, lightPosition); glEnable(GL_LIGHT1); glEnable(GL_LIGHTING); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45.0f,(GLfloat)kWindowWidth/(GLfloat)kWindowHeight,0.1f,100.0f); glMatrixMode(GL_MODELVIEW); glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); floorimage = tImage("floor.tga"); wallimage = tImage("wall.tga"); glGenTextures(2, &texture[0]); glBindTexture(GL_TEXTURE_2D, texture[0]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, floorimage.iWidth, floorimage.iHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, floorimage.imageData); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glBindTexture(GL_TEXTURE_2D, texture[1]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, wallimage.iWidth, wallimage.iHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, wallimage.imageData); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } // main every-frame GL function GLvoid DrawGLScene(GLvoid) { // start frame timer ctim = clock(); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); glTranslatef(0.0f, -5.0f, -25.0f); glRotatef(15.0f, 1.0f, 0.0f, 0.0f); moveBalls(); checkCollisions(); vector::iterator p = balls.begin(); while (p < balls.end() && !balls.empty()) { if ((p->pos.y - p->rad) < 0.01f && fabs(p->vel.y) < 0.1f) p = balls.erase(p); if (p < balls.end()) p++; } for (vector::iterator p = balls.begin(); p != balls.end() && !balls.empty(); p++) drawBall(*p); drawRoom(); drawBoxes(); drawPointer(); // see if next level is required yet if (balls.empty() && lev+1 < levels.size()) nextLevel(++lev); if (balls.empty() && lev+1 == levels.size()) nextLevel(lev); // increment time stime += dt; glFlush(); glutPostRedisplay(); glutSwapBuffers(); // wait for end of frame time while (GLfloat(clock()-ctim) < cdt); } // resize GL window GLvoid ReSizeGLScene(int Width, int Height) { glViewport (0, 0, (GLsizei) Width, (GLsizei) Height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45.0, (GLfloat) Width / (GLfloat) Height, 0.1, 100.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); }