📄 billiards.cpp
字号:
// Billiard ball simulator// Created by Nelis Franken// -----------------------------------------------------------// Main implementation file// -----------------------------------------------------------#include "billiards.h"// Loads 24-bit bitmap files with 1 plane only.// (Disclaimer: This function originally obtained from http://nehe.gamedev.net)int ImageLoad(const char *filename, Image *image) { FILE *file; unsigned long size; unsigned long i; unsigned short int planes; unsigned short int bpp; char temp, finalName[80]; strcpy(finalName, "textures/" ); strcat(finalName, filename ); if ((file = fopen(finalName, "rb"))==NULL) { printf("File Not Found : %s\n",finalName); return 0; } fseek(file, 18, SEEK_CUR); if ((i = fread(&image->sizeX, 4, 1, file)) != 1) { printf("Error reading width from %s.\n", finalName); return 0; } if ((i = fread(&image->sizeY, 4, 1, file)) != 1) { printf("Error reading height from %s.\n", finalName); return 0; } size = image->sizeX * image->sizeY * 3; if ((fread(&planes, 2, 1, file)) != 1) { printf("Error reading planes from %s.\n", finalName); return 0; } if (planes != 1) { printf("Planes from %s is not 1: %u\n", finalName, planes); return 0; } if ((i = fread(&bpp, 2, 1, file)) != 1) { printf("Error reading bpp from %s.\n", finalName); return 0; } if (bpp != 24) { printf("Bpp from %s is not 24: %u\n", finalName, bpp); return 0; } fseek(file, 24, SEEK_CUR); image->data = (char *) malloc(size); if (image->data == NULL) { printf("Error allocating memory for color-corrected image data"); return 0; } if ((i = fread(image->data, size, 1, file)) != 1) { printf("Error reading image data from %s.\n", finalName); return 0; } for (i=0;i<size;i+=3) { temp = image->data[i]; image->data[i] = image->data[i+2]; image->data[i+2] = temp; } return 1;}// Determines the normal from any three points on a plane.vector3 getNormal(GLfloat point1[3], GLfloat point3[3], GLfloat point4[3]) { vector3 theNormal = vector3(0.0,0.0,0.0); theNormal.x = (point1[1] - point4[1])*(point3[2] - point4[2]) - (point3[1] - point4[1])*(point1[2] - point4[2]); theNormal.y = (point3[0] - point4[0])*(point1[2] - point4[2]) - (point1[0] - point4[0])*(point3[2] - point4[2]); theNormal.z = (point1[0] - point4[0])*(point3[1] - point4[1]) - (point3[0] - point4[0])*(point1[1] - point4[1]); return theNormal;}// Renders the billiard balls to screen (with dynamic shadows)void renderBalls() { GLfloat m[16]; for (int i=0; i < 15; i++) m[i] = 0.0; m[0] = m[5] = m[10] = 1.0; m[7] = (-1.0)/(light0Pos[1] + 2.0); for (int p=0; p < ballCount; p++) { glPushMatrix(); glTranslatef(ballList[p].position.x,ballList[p].position.y,ballList[p].position.z); // Determine shadows. glPushMatrix(); glTranslatef(light0Pos[0], light0Pos[1]+0.65, light0Pos[2]); glMultMatrixf(m); glTranslatef(-1.0*light0Pos[0], -1.0*light0Pos[1], -1.0*light0Pos[2]); glColor3f(0.0,0.0,0.0); glBindTexture(GL_TEXTURE_2D, theTexture[BLACK]); gluSphere(ballQuadric, ballList[p].radius, 32, 12); glPopMatrix(); vector3 tempSpeed = vector3(0.0, 0.0, 0.0); tempSpeed = ballList[p].speed; tempSpeed.normalize(); vector3 speedNormal = vector3(0.0,0.0,0.0); speedNormal.x = tempSpeed.z; speedNormal.y = 0.0; speedNormal.z = (-1.0)*tempSpeed.x; if (ballList[p].rotation > 360.0) ballList[p].rotation = ballList[p].rotation - 360.0; ballList[p].rotation += ballList[p].speedSize/ballList[p].radius*(180.0/M_PI); glBindTexture(GL_TEXTURE_2D, theTexture[p]); glRotatef(ballList[p].rotation, speedNormal.x, speedNormal.y, speedNormal.z); gluSphere(ballQuadric, ballList[p].radius, 32, 32); glPopMatrix(); }}// Renders a flat surface to screen with predefined width/height, segments and texturevoid renderSurface(GLfloat width, GLfloat length, GLfloat widthSegments, GLfloat lengthSegments, GLfloat elevation, GLfloat texXTile, GLfloat texYTile, GLfloat normalX, GLfloat normalY, GLfloat normalZ, GLuint &surfaceTexture) { glPushMatrix(); glBindTexture(GL_TEXTURE_2D, surfaceTexture); for (GLfloat k=(width/(-2.0)); k < (width/(2.0)); k += (width/widthSegments)) { for (GLfloat r=(length/(-2.0)); r < (length/(2.0)); r += (length/lengthSegments)) { glBegin(GL_QUADS); glNormal3f(normalX, normalY, normalZ); glTexCoord2f(0.0, texYTile); glVertex3f(k, elevation, r+length/lengthSegments); glTexCoord2f(texXTile, texYTile); glVertex3f(k+width/widthSegments, elevation, r+length/lengthSegments); glTexCoord2f(texXTile, 0.0); glVertex3f(k+width/widthSegments, elevation, r); glTexCoord2f(0.0, 0.0); glVertex3f(k, elevation, r); glEnd(); } } glPopMatrix();}// Renders a quad represented by any 4 points to screen.void renderQuad(GLfloat point1[3], GLfloat point2[3], GLfloat point3[3], GLfloat point4[3], GLfloat orientation, GLfloat texXTile, GLfloat texYTile) { glBegin(GL_QUADS); vector3 theNormal = vector3(0.0, 0.0, 0.0); theNormal = getNormal(point1, point3, point4); theNormal = theNormal*orientation; glNormal3f(theNormal.x, theNormal.y, theNormal.z); glTexCoord2f(0.0, texYTile); glVertex3fv(point1); glNormal3f(theNormal.x, theNormal.y, theNormal.z); glTexCoord2f(texXTile, texYTile); glVertex3fv(point2); glNormal3f(theNormal.x, theNormal.y, theNormal.z); glTexCoord2f(texXTile, 0.0); glVertex3fv(point3); glNormal3f(theNormal.x, theNormal.y, theNormal.z); glTexCoord2f(0.0, 0.0); glVertex3fv(point4); glEnd();}// Renders a curve (partial cylinder of any height and sweep) to screen.void renderCurve(GLfloat radius, GLfloat height, GLfloat sweep, GLuint segments, GLfloat orientation, GLuint &aTexture) { glPushMatrix(); glBindTexture(GL_TEXTURE_2D, aTexture); for (float t=0.0; t <= sweep-(sweep/segments); t += sweep/segments) { GLfloat x = 0.0, y = 0.0, z = 0.0; GLfloat point1[3] = {radius*cos(t), height, radius*sin(t)}; GLfloat point2[3] = {radius*cos(t+sweep/segments), height, radius*sin(t+sweep/segments)}; GLfloat point3[3] = {radius*cos(t+sweep/segments), 0.0, radius*sin(t+sweep/segments)}; GLfloat point4[3] = {radius*cos(t), 0.0, radius*sin(t)}; renderQuad(point1, point2, point3, point4, orientation, 1.0, 1.0); } glPopMatrix();}// Renders a disc (cap for a cylinder) of any sweep to screen.void renderCap(GLfloat inner_radius, GLfloat outer_radius, GLfloat inner_sweep, GLfloat outer_sweep, GLuint segments, GLuint &myTexture) { glPushMatrix(); GLfloat ciX = 0.0, coX = 0.0, ciZ = 0.0, coZ = 0.0; GLfloat angle = -1.0*outer_sweep/segments; glBindTexture(GL_TEXTURE_2D, myTexture); for (int k=0; k < segments; k++) { ciX = inner_radius*cos(angle + inner_sweep/segments); ciZ = inner_radius*sin(angle + inner_sweep/segments); coX = outer_radius*cos(angle + outer_sweep/segments); coZ = outer_radius*sin(angle + outer_sweep/segments); angle += inner_sweep/segments; glBegin(GL_QUADS); glNormal3f(0.0,1.0,0.0); glTexCoord2f(coX/(2.0*outer_radius), coZ/(2.0*outer_radius)); glVertex3f(coX, 0.0, coZ); glNormal3f(0.0,1.0,0.0); glTexCoord2f(outer_radius*cos(angle + outer_sweep/segments)/(2.0*outer_radius), outer_radius*sin(angle + outer_sweep/segments)/(2.0*outer_radius)); glVertex3f(outer_radius*cos(angle + outer_sweep/segments), 0.0, outer_radius*sin(angle + outer_sweep/segments)); glNormal3f(0.0,1.0,0.0); glTexCoord2f(inner_radius*cos(angle + inner_sweep/segments)/(2.0*inner_radius), inner_radius*sin(angle + inner_sweep/segments)/(2.0*inner_radius)); glVertex3f(inner_radius*cos(angle + inner_sweep/segments), 0.0, inner_radius*sin(angle + inner_sweep/segments)); glNormal3f(0.0,1.0,0.0); glTexCoord2f(ciX/(2.0*inner_radius), ciZ/(2.0*inner_radius)); glVertex3f(ciX, 0.0, ciZ); glEnd(); } glPopMatrix();}// Renders a pocket of generic alignment and predefined sweep to screen.void renderPocket(GLfloat sweep) { glPushMatrix(); glTranslatef(0.0, -1.55, 0.0); renderCurve(5.5, 2.55, sweep, 16, 1, theTexture[DARK_WOOD]); renderCurve(3.0, 2.55, sweep, 16, -1, theTexture[GREEN_CARPET]); renderCap(0.0001, 5.5, 6.3, 6.3, 16, theTexture[BLACK]); glPopMatrix(); glPushMatrix(); glTranslatef(0.0, 1.0, 0.0); renderCap(3.0, 5.5, sweep, sweep, 16, theTexture[DARK_WOOD]); glPopMatrix(); glPushMatrix(); glTranslatef(0.0, -1.4, 0.0); if (sweep < M_PI+0.001) renderCap(0.0001, 3.0, M_PI, M_PI, 16, theTexture[BLACK]); else renderCap(0.0001, 3.0, 6.3, 6.3, 16, theTexture[BLACK]); glPopMatrix();}// Renders the curved table legs to screen.void renderTableLegs() { glNewList(displayList[4], GL_COMPILE); glBindTexture(GL_TEXTURE_2D, theTexture[DARK_WOOD]); glPushMatrix(); for (double k=0; k < 3.0*M_PI; k+=1.4) { for (double g=0; g < M_PI*2.0; g+=0.5) { GLfloat point1[3] = {3.0*sin((k+1.4)/3.0)*cos(g), k*2, 3.0*sin((k+1.4)/3.0)*sin(g)}; GLfloat point2[3] = {3.0*sin((k+1.4)/3.0)*cos(g+0.5), k*2, 3.0*sin((k+1.4)/3.0)*sin(g+0.5)}; GLfloat point3[3] = {3.0*sin(k/3.0)*cos(g+0.5), k*2 - 2.8, 3.0*sin(k/3.0)*sin(g+0.5)}; GLfloat point4[3] = {3.0*sin(k/3.0)*cos(g), k*2 - 2.8, 3.0*sin(k/3.0)*sin(g)}; renderQuad(point1, point2, point3, point4, 1.0, 1.0, 1.0); } } glRotatef(90.0, 1.0, 0.0 ,0.0); glutSolidTorus(1.0, 1.8, 5, 8); gluCylinder(pillarCylinder, 1.0, 3.0, 4.0, 8, 2); glPopMatrix(); glEndList();}// Draws one side of the table (generically aligned)void drawSide() { // Wooden side glPushMatrix(); glTranslatef(0, -0.3, 0); glScalef(1.0, 1.0, 2.0); glRotatef(270.0, 0.0, 1.0, 0.0); glRotatef(45.0, 0.0, 0.0, 1.0); glBindTexture(GL_TEXTURE_2D, theTexture[WOOD]); gluCylinder(pillarCylinder, 1.7, 1.7, tableWidth - 6, 4, 4); glPopMatrix(); // Green carpeted side glPushMatrix(); glTranslatef(-1.0*tableWidth + 6, 0.0, -2.35); glRotatef(180.0, 0.0, 0.0, 1.0); glRotatef(270.0, 0.0, 1.0, 0.0); glScalef(0.5, 1.0, 1.0); glBindTexture(GL_TEXTURE_2D, theTexture[GREEN_CARPET]); gluCylinder(pillarCylinder, 1.7, 1.7, tableWidth - 6, 3, 3); glPopMatrix();}// Draws the entire table to screenvoid renderTable() { glNewList(displayList[1], GL_COMPILE); glColor3f(1.0,1.0,1.0); // Table carpeted area renderSurface(tableWidth, tableLength, 10.0, 15.0, -1.5, 1.0, 1.0, 0.0, 1.0, 0.0, theTexture[GREEN_CARPET]); // Anti-aliased game-lines on carpeted area glPushMatrix(); glDisable(GL_TEXTURE_2D); glDisable(GL_LIGHTING); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_LINE_SMOOTH); glColor4f(0.5, 0.5, 0.5, 0.4); glLineWidth(2.5f); // Horisontal line glTranslatef(0.0, -1.45, 0.0); glBegin(GL_LINES); glVertex3f(minX - pocketRadius, 0.0, minZ - minZ/3); glVertex3f(maxX + pocketRadius, 0.0, minZ - minZ/3); glEnd(); // Half-circle glTranslatef(0.0, 0.0, minZ - minZ/3); glRotatef(90.0, 0.0, 1.0, 0.0); glBegin(GL_LINES); for (GLfloat i = 0.0; i < M_PI-(M_PI/12.0); i+=M_PI/12.0) { glVertex3f((maxZ/6.0)*sin(i), 0.0, (maxZ/6.0)*cos(i)); glVertex3f((maxZ/6.0)*sin(i+M_PI/12.0), 0.0, (maxZ/6.0)*cos(i+M_PI/12.0)); } glEnd(); glLineWidth(1.0f); // Start-point on line glRotatef(90.0, 1.0, 0.0, 0.0); gluDisk(pillarCylinder, 0, 0.3, 9, 1); glDisable(GL_LINE_SMOOTH); glDisable(GL_BLEND); glEnable(GL_LIGHTING); glEnable(GL_TEXTURE_2D); glPopMatrix(); // Table sides GLint divCount = -5, angleDirection = 1; GLfloat offA = 2.4, offB = 3.0, offTemp = 0.0, sideAngle = 270.0; for(GLint f=1; f >= -1; f--) { for (GLfloat r=-1.0; r <= 1.0; r+=2.0) { glPushMatrix(); glTranslatef(tableWidth/(r*2.0) + (abs(divCount)/divCount)*offA, 0.0, f*tableWidth + r*offB); glRotatef(sideAngle, 0.0, 1.0, 0.0); drawSide(); glPopMatrix(); divCount += 2; if ((f == 0) && (r == -1.0)) { offA = 2.4; offB = 3.0; offTemp = 0.0; sideAngle = 90.0; angleDirection = 1; } else { offTemp = offA; offA = offB; offB = offTemp; sideAngle = sideAngle + angleDirection*90.0; angleDirection *= -1; } } } // Pockets GLfloat direction = -1.0, angle = 360.0, sweep = M_PI + M_PI/2.0; bool sidePocket = false; for (int g=-1; g <= 1; g+=2) { for (int d=1; d >= -1; d-=1) { glPushMatrix(); glTranslatef(g*tableWidth/(2.0), 0.0, d*tableWidth); glRotatef(angle,0.0,1.0,0.0); renderPocket(sweep); glPopMatrix(); if (d == 1) { sweep = M_PI; if (g == -1) { angle = angle + direction*90; sidePocket = true; } } else { if (sidePocket == false) angle = angle + direction*90; else sidePocket = false; sweep = M_PI + M_PI/2.0; } } direction = -1*direction; angle = 90; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -