// MAIN: // All glut functions are here; // draws the grid of cells to the screen; // provides choice of rules; // provides choice of initial configurations; // evolves the ca given an initial configuration // and a rule #include // graphics tools #include #include #include #include "TimeStep.h" #include "cell.h" #include // don't need in final version #include // don't need in final version // find useful glut info at... // http://piglet.uccs.edu/~semwal/geometric.html // http://www.fatech.com/tech/opengl/glut #define CLEAR_SCREEN 0 #define NEXT_TIME_STEP 1 #define START_OVER_SAME 2 #define START_OVER_NEW 3 #define CONWAYS_LIFE 51 #define LONG_LIFE 52 #define RULE3 53 #define EXIT 99 float angle = 0.0; // the angle of rotation in the y-axis; // will allow us to rotate the camera. // We will be moving the xz-plane and // rotating around the y-axis. float lineOfSight_x = -0.6f, lineOfSight_y = 0.0f, lineOfSight_z = -1.0f; // a vector defining our line of sight float camera_x = 2.0f, camera_y = 0.0f, camera_z = 1.0f; // controls the position of the camera int viewport[4]; // keeps the width and height of the window //float rotationAngle = 0.0; // the call list storage GLuint WhiteCell; GLuint BlueCell; GLfloat LightAmbient[]= { 0.8f, 0.8f, 0.8f, 1.0f }; GLfloat LightDiffuse[]= { 0.5f, 0.5f, 0.5f, 1.0f }; GLfloat BLightAmbient[]= { 0.0f, 0.0f, 0.8f, 1.0f }; GLfloat BLightDiffuse[]= { 0.0f, 0.0f, 0.5f, 1.0f }; GLfloat Specular[] = {0.1f,0.1f,0.1f,1.0f}; GLfloat BSpecular[] = {0,0,0.1f,1.0f}; GLfloat LightPosition[]= { -20.0f, 20.0f, 0.0f, 1.0f }; // for showTimeStep() int const numSteps = 50; TimeStep timeStepArray[numSteps]; int nextStepNumber = 0; int initialConfig = 0; int rule = CONWAYS_LIFE; // draws the grid of cells to the screen void showGrid(TimeStep theTimeStep) { for(int q=1; q<=10; q++) for(int r=1; r<=10; r++) { glPushMatrix(); // Reset The Current Modelview Matrix double ymove = q*0.21; double xmove = r*0.21; glTranslatef(-1.0+ xmove, -1.0 + ymove, -3.0); if ((theTimeStep.TheCell(q,r)).status == 1) { glCallList(BlueCell); // executes the specified (BlueCell) call list } else { glCallList(WhiteCell); } glPopMatrix(); // discard the modelling transformations; // after this the matrix will have only // the camera settings // (any changes in the current color and // current matrix made during the execution // of the display list remain in effect after // it has been called) } } void showTimeSteps(int step, int config, int rule) { // DEBUGGING cout << "in showTimeSteps(); the step is " << step << endl; //glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_ACCUM_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); // if this is the initial step, the // switch looks at the config variable // to determine which initial config // the user wants if (step == 0) { switch(config) { case 0: { // initial configuration: // a group of four alive cells in the center timeStepArray[0] = TimeStep(0); (timeStepArray[0].TheCell(4,4)).status = 1; (timeStepArray[0].TheCell(5,4)).status = 1; (timeStepArray[0].TheCell(5,5)).status = 1; (timeStepArray[0].TheCell(6,3)).status = 1; //FYI, alternatively, you could do this... //Cell* testCell1 = &timeStep0.TheCell(4,4); //testCell1->status = 1; //timeStep0.TheCell(4,4) returns the cell at // position (4,4) in the grid; // the next line sets its status to "alive" showGrid(timeStepArray[0]); // display the current configuration break; } case 1: { // initial configuration: // a line of five alive cells in the center timeStepArray[0] = TimeStep(0); (timeStepArray[0].TheCell(5,4)).status = 1; (timeStepArray[0].TheCell(5,5)).status = 1; (timeStepArray[0].TheCell(5,6)).status = 1; (timeStepArray[0].TheCell(5,7)).status = 1; (timeStepArray[0].TheCell(5,8)).status = 1; showGrid(timeStepArray[0]); // display the current configuration break; } case 2: { // initial configuration: // a group of nine alive cells in the center timeStepArray[0] = TimeStep(0); (timeStepArray[0].TheCell(4,5)).status = 1; (timeStepArray[0].TheCell(5,4)).status = 1; (timeStepArray[0].TheCell(5,5)).status = 1; (timeStepArray[0].TheCell(5,6)).status = 1; (timeStepArray[0].TheCell(6,4)).status = 1; (timeStepArray[0].TheCell(6,5)).status = 1; (timeStepArray[0].TheCell(6,6)).status = 1; (timeStepArray[0].TheCell(6,7)).status = 1; (timeStepArray[0].TheCell(7,6)).status = 1; showGrid(timeStepArray[0]); // display the current configuration break; } } } // if this is not the initial step, the // switch looks at the rule variable // to determine which rule // the user wants else { //create new time step now timeStepArray[step] = TimeStep(step); switch(rule) { case(CONWAYS_LIFE): { timeStepArray[step].EvolutionConwaysLife(timeStepArray[step-1]); //now timeStep1 holds the correct configuration break; } case(LONG_LIFE): { timeStepArray[step].EvolutionLongLife(timeStepArray[step-1]); //now timeStep1 holds the correct configuration break; } } showGrid(timeStepArray[step]); } glFlush(); glutSwapBuffers(); } void lightingSetup(void) // setup the lighting { GLfloat light_ambient[] = {1.0, .2, .8, 1.0}; GLfloat light_diffuse[] = {1.0, .2, .8, 1.0}; glLightfv(GL_LIGHT0, GL_AMBIENT, LightAmbient); glLightfv(GL_LIGHT0, GL_DIFFUSE, LightDiffuse); glLightfv(GL_LIGHT0, GL_POSITION, LightPosition); glEnable(GL_LIGHT0); glEnable(GL_LIGHTING); } // maintains perspective when window // is resized void applyCamera(int w, int h) // position the camera { glMatrixMode(GL_PROJECTION); // sets the current matrix to the projection matrix, // which is the matrix that defines the viewing // volume //glLoadIdentity(); // the identity matrix is loaded to initialize the // current matrix glViewport(0, 0, (GLsizei) w, (GLsizei) h); // Sets the viewport to be the entire window // first two parameters are the top left corner; // last two parameters are the bottom left. // The coordinates are relative to the client // area of the window, not the screen gluPerspective(45, (GLfloat)w/(GLfloat)h, 0.1, 100.0); // establishes the perspective parameters; // first parameter: defines the field of view angle // in the yz-plane // second parameter: the ratio that defines the relation // between the width and height of the viewport // third parameter: defines the near clipping plane // fourth parameter: defines the far clipping plane // anything closer than the near value or further away // than the far value will be clipped from the scene glMatrixMode(GL_MODELVIEW); // set the modelview matrix as the current matrix, // which is where the camera settings and the // model transformations are defined glLoadIdentity(); lightingSetup(); gluLookAt(camera_x, camera_y, camera_z, camera_x+lineOfSight_x, camera_y+lineOfSight_y, camera_z+lineOfSight_z, 0,1,0); // sets the camera and position // first set of three values: indicate the camera position // second set of three values: defines the point you are // looking at; can be any point in our line of sight; // the look at point is computer by adding the vector // which defines our line of sight to the camera position // third set of three values: the up vector; (0,1,0) means // that the camera is not tilted [an upside down view // would be (0, -1, 0) ] } void drawingSetup(void) //initialize a few things { glClearColor(0.0, 0.0, 0.0, 1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the depth buffer glEnable(GL_DEPTH_TEST); // Enables Depth Testing glDepthFunc(GL_LEQUAL); // The Type Of Depth Testing To Do glShadeModel(GL_SMOOTH); // a list for a white cell WhiteCell = glGenLists(1); glNewList(WhiteCell,GL_COMPILE); // GL_COMPILE indicates that this // should not be executed immediately // (the alternative is // GL_COMPILE_AND_EXECUTE) //glRotatef(rotationAngle, 0.0, 1.0, 0.0); glBegin(GL_QUADS); // Draw A Quad glMaterialfv(GL_FRONT,GL_AMBIENT,LightAmbient); glMaterialfv(GL_FRONT,GL_DIFFUSE,LightDiffuse); glMaterialfv(GL_FRONT,GL_SPECULAR,Specular); glVertex3f( 0.1f, 0.1f,-0.1f); // Top Right Of The Quad (Top) glVertex3f(-0.1f, 0.1f,-0.1f); // Top Left Of The Quad (Top) glVertex3f(-0.1f, 0.1f, 0.1f); // Bottom Left Of The Quad (Top) glVertex3f( 0.1f, 0.1f, 0.1f); // Bottom Right Of The Quad (Top) glNormal3f(0,-1,0); glVertex3f( 0.1f,-0.1f, 0.1f); // Top Right Of The Quad (Bottom) glVertex3f(-0.1f,-0.1f, 0.1f); // Top Left Of The Quad (Bottom) glVertex3f(-0.1f,-0.1f,-0.1f); // Bottom Left Of The Quad (Bottom) glVertex3f( 0.1f,-0.1f,-0.1f); // Bottom Right Of The Quad (Bottom) glNormal3f(0,0,1); glVertex3f( 0.1f, 0.1f, 0.1f); // Top Right Of The Quad (Front) glVertex3f(-0.1f, 0.1f, 0.1f); // Top Left Of The Quad (Front) glVertex3f(-0.1f,-0.1f, 0.1f); // Bottom Left Of The Quad (Front) glVertex3f( 0.1f,-0.1f, 0.1f); // Bottom Right Of The Quad (Front) glNormal3f(0,0,-1); glVertex3f( 0.1f,-0.1f,-0.1f); // Top Right Of The Quad (Back) glVertex3f(-0.1f,-0.1f,-0.1f); // Top Left Of The Quad (Back) glVertex3f(-0.1f, 0.1f,-0.1f); // Bottom Left Of The Quad (Back) glVertex3f( 0.1f, 0.1f,-0.1f); // Bottom Right Of The Quad (Back) glNormal3f(-1,0,0); glVertex3f(-0.1f, 0.1f, 0.1f); // Top Right Of The Quad (Left) glVertex3f(-0.1f, 0.1f,-0.1f); // Top Left Of The Quad (Left) glVertex3f(-0.1f,-0.1f,-0.1f); // Bottom Left Of The Quad (Left) glVertex3f(-0.1f,-0.1f, 0.1f); // Bottom Right Of The Quad (Left) glNormal3f(1,0,0); glVertex3f(0.1f, 0.1f,-0.1f); // Top Right Of The Quad (Right) glVertex3f(0.1f, 0.1f, 0.1f); // Top Left Of The Quad (Right) glVertex3f(0.1f,-0.1f, 0.1f); // Bottom Left Of The Quad (Right) glVertex3f(0.1f,-0.1f,-0.1f); // Bottom Right Of The Quad (Right) glEnd(); // Done Drawing The Quad glEndList(); // a list for a blue cell BlueCell = glGenLists(1); glNewList(BlueCell,GL_COMPILE); // GL_COMPILE indicates that this // should not be executed immediately // (the alternative is // GL_COMPILE_AND_EXECUTE) //glRotatef(rotationAngle, 0.0, 1.0, 0.0); glBegin(GL_QUADS); // Draw A Quad glMaterialfv(GL_FRONT,GL_AMBIENT,BLightAmbient); glMaterialfv(GL_FRONT,GL_DIFFUSE,BLightDiffuse); glMaterialfv(GL_FRONT,GL_SPECULAR,BSpecular); glNormal3f(0,1,0); glVertex3f( 0.1f, 0.1f,-0.1f); // Top Right Of The Quad (Top) glVertex3f(-0.1f, 0.1f,-0.1f); // Top Left Of The Quad (Top) glVertex3f(-0.1f, 0.1f, 0.1f); // Bottom Left Of The Quad (Top) glVertex3f( 0.1f, 0.1f, 0.1f); // Bottom Right Of The Quad (Top) glNormal3f(0,-1,0); glVertex3f( 0.1f,-0.1f, 0.1f); // Top Right Of The Quad (Bottom) glVertex3f(-0.1f,-0.1f, 0.1f); // Top Left Of The Quad (Bottom) glVertex3f(-0.1f,-0.1f,-0.1f); // Bottom Left Of The Quad (Bottom) glVertex3f( 0.1f,-0.1f,-0.1f); // Bottom Right Of The Quad (Bottom) glNormal3f(0,0,1); glVertex3f( 0.1f, 0.1f, 0.1f); // Top Right Of The Quad (Front) glVertex3f(-0.1f, 0.1f, 0.1f); // Top Left Of The Quad (Front) glVertex3f(-0.1f,-0.1f, 0.1f); // Bottom Left Of The Quad (Front) glVertex3f( 0.1f,-0.1f, 0.1f); // Bottom Right Of The Quad (Front) glNormal3f(0,0,-1);// ?? glVertex3f( 0.1f,-0.1f,-0.1f); // Top Right Of The Quad (Back) glVertex3f(-0.1f,-0.1f,-0.1f); // Top Left Of The Quad (Back) glVertex3f(-0.1f, 0.1f,-0.1f); // Bottom Left Of The Quad (Back) glVertex3f( 0.1f, 0.1f,-0.1f); // Bottom Right Of The Quad (Back) glNormal3f(-1,0,0); glVertex3f(-0.1f, 0.1f, 0.1f); // Top Right Of The Quad (Left) glVertex3f(-0.1f, 0.1f,-0.1f); // Top Left Of The Quad (Left) glVertex3f(-0.1f,-0.1f,-0.1f); // Bottom Left Of The Quad (Left) glVertex3f(-0.1f,-0.1f, 0.1f); // Bottom Right Of The Quad (Left) glNormal3f(1,0,0); glVertex3f(0.1f, 0.1f,-0.1f); // Top Right Of The Quad (Right) glVertex3f(0.1f, 0.1f, 0.1f); // Top Left Of The Quad (Right) glVertex3f(0.1f,-0.1f, 0.1f); // Bottom Left Of The Quad (Right) glVertex3f(0.1f,-0.1f,-0.1f); // Bottom Right Of The Quad (Right) glEnd(); // Done Drawing The Quad glEndList(); } void draw(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the depth buffer glColor3f(1.0, 0.0, 0.0); glutSwapBuffers(); // draw to the screen; // swapping the back and front // buffer causes the rendering // to be shown (remember that // there are two buffers and // drawing happens in the // hidden buffer) } void idle(void) // do this stuff when there is nothing else to do // this rendering functin is continuously called, enabling animation { //glutPostRedisplay(); // call the drawing routine // should NOT be commented out, but does not work if it does // (7/6/01) showGrid(timeStepArray[nextStepNumber]); } // rotation does not work right now!! /*void rotateCam(float ang) { //DEBUGGING cout << "in rotateCam" << endl; // This is responsible for rotating the camera in the // xz-plane. It receives an angle and computes the // appropriate values for the new x and z components // of the line-of-sight vector. // Afterwards we set the new camera orientation. In order to do that // we first speicify that we're working with the projection matrix. // Then we reset the matrix to avoid accumulation with previous // transformations. The gluPerspective sets the perspective parameters, // and finally gluLookAt sets the camera orientation. Note that // the camera doesn't move, the camera position remains the same. lineOfSight_x = sin(ang); lineOfSight_z = -cos(ang); // The new lineOfSight_x and lineOfSight_z // are mapped onto a circle on the xz-plane //glLoadIdentity(); gluLookAt(camera_x, camera_y, camera_z, camera_x+lineOfSight_x, camera_y+lineOfSight_y, camera_z+lineOfSight_z, 0,1,0); glutSwapBuffers(); cout << "(" << camera_x << "," << camera_y << "," << camera_z << "); ( " << camera_x+lineOfSight_x << "," << camera_y+lineOfSight_y << "," << camera_z+lineOfSight_z << "); (0,1,0)" << endl << endl; } void moveAlongSight(int direction) { //DEBUGGING cout << "in moveAlongSight" << endl; // This is responsible for the camera movement along // the line of sight -- the next camera position must // be along the line of sight vector. camera_x = camera_x + direction*(lineOfSight_x)*0.9; camera_z = camera_z + direction*(lineOfSight_z)*0.9; // The new camera positions are calculated by adding // a fraction of the line-of-sight vector to the // current position/ // Direction is either -1 or 1 depending on whether // we want to move forward or backward. // The fraction is a possible speed implementation. // Since the line-of-sight coordinates are points on // the unit circle, if the fraction is kept constant // then the speed will be kept constant as well. The // higher the fraction, the faster you move (the // farther you are moving in each frame). //glLoadIdentity(); gluLookAt(camera_x, camera_y, camera_z, camera_x+lineOfSight_x, camera_y+lineOfSight_y, camera_z+lineOfSight_z, 0,1,0); cout << "(" << camera_x << "," << camera_y << "," << camera_z << "); ( " << camera_x+lineOfSight_x << "," << camera_y+lineOfSight_y << "," << camera_z+lineOfSight_z << "); (0,1,0)" << endl << endl; }*/ void processMenuEvents(int option) { switch (option) { // below: could be useful, but commented out for now /* case CLEAR_SCREEN: { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_ACCUM_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); glutSwapBuffers(); break; }*/ case NEXT_TIME_STEP: { showTimeSteps(nextStepNumber, initialConfig, rule); nextStepNumber++; break; } case START_OVER_SAME: // start over; same initial configuration { nextStepNumber = 0; showTimeSteps(nextStepNumber, initialConfig, rule); nextStepNumber++; break; } case START_OVER_NEW: // start over; new initial configuration { nextStepNumber = 0; if (initialConfig == 2) // there are only 2 right now initialConfig = 0; else initialConfig++; showTimeSteps(nextStepNumber, initialConfig, rule); nextStepNumber++; break; } case CONWAYS_LIFE: { rule = CONWAYS_LIFE; break; } case LONG_LIFE: { rule = LONG_LIFE; break; } case EXIT: { exit(0); break; } } } void createGLUTMenus() { int menu, submenu; // create the submenu and // tell glut that "processMenuEvents" will // handle the events submenu = glutCreateMenu(processMenuEvents); glutAddMenuEntry("Conway's Game of Life (S23/B3)", CONWAYS_LIFE); glutAddMenuEntry("Long Life (S5/B345)",LONG_LIFE); // create the menu and // tell glut that "processMenuEvents" will // handle the events menu = glutCreateMenu(processMenuEvents); //add entries to our menu //glutAddMenuEntry("clear screen",CLEAR_SCREEN); glutAddSubMenu("rules", submenu); glutAddMenuEntry("next step",NEXT_TIME_STEP); glutAddMenuEntry("start over (same initial config)",START_OVER_SAME); glutAddMenuEntry("start over (new initial config)",START_OVER_NEW); glutAddMenuEntry("exit",EXIT); // attach the menu to the right button glutAttachMenu(GLUT_RIGHT_BUTTON); } void normalKeys(unsigned char key, int x, int y) { // key: the ASCII code of the key pressed // x and y: the mouse position when // the key is pressed // (relative to the top left // corner of the client area // of window) switch(key) { case 27: // exit program; 27 is the ESCAPE key { exit(0); break; } case 110: // n; go to the next time step { showTimeSteps(nextStepNumber, initialConfig, rule); nextStepNumber++; break; } case 78: // N; go to the next time step { showTimeSteps(nextStepNumber, initialConfig, rule); nextStepNumber++; break; } case 99: // c; clear { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_ACCUM_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); glutSwapBuffers(); break; } case 67: // C; clear { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_ACCUM_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); glutSwapBuffers(); break; } } } /*void specialKeys(int key, int x, int y) { // key: key pressed -- an integer // value this time, as the // GLUT_KEY_* are predefined // constants in glut.h. // x and y: the mouse position when // the key is pressed // (relative to the top left // corner of the client area // of window) // The left and right keys are used to // rotate the camera -- to change the // vector that defines the line of sight. // The up and down keys are used to move // along the current line of sight. switch(key) { case GLUT_KEY_LEFT: { angle -= 0.1f; rotateCam(angle); // rotates the camera break; } case GLUT_KEY_RIGHT: { angle +=0.1f; rotateCam(angle); // rotates the camera break; } case GLUT_KEY_UP: { moveAlongSight(1); // moves camera along line of sight break; } case GLUT_KEY_DOWN: { moveAlongSight(-1); // moves camera along line of sight break; } } }*/ int main(int argc, char* argv[]) { // Main performs the required initializations and // starts the event processing loop. // All functions in GLUT have the prefix "glut"; // the functions that perform an initialization // have the prefix "glutInit." glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE|GLUT_DEPTH|GLUT_RGB); // defines the display mode // The parameter is a boolean combination of // predefined values in the GLUT library. // GLUT_DOUBLE selects a double buffer window, // required to have smooth animation; // double buffering allows for smooth // animation by keeping the drawing in a // back buffer and swapping the back with // the front buffer (the visible one) when // the rendering is complete; prevents // flickering // GLUT_DEPTH selects the depth buffer. // GLUT_RGB selects a RGBA window, the default // color mode. // So this is a RGB window with double buffering // and a depth buffer. glutInitWindowSize(640, 480); // sets the window size // The window will be 640 pixels wide // and 480 pixels tall. // These are actually just a suggestion to the // window manager and the returned window // may be a different size. glutInitWindowPosition(100,100); // defines the window // establishes the window's position as // 100 pixels from the left of the screen // (first parameter) and 480 pixels from the // top of the screen (second parameter). // These are actually just a suggestion to the // window manager and the returned window // may be in a different position. glutCreateWindow("<* Ca in C/OpenGL/GLUT *>"); // creates the window, setting the window title // the return value is the window identifier //glutFullScreen(); glEnable(GL_DEPTH_TEST); drawingSetup(); glutKeyboardFunc(normalKeys); // This is one of two GLUT functions that // registers callbacks for keyboard events. // Here, keyboard is passed in -- this is // the function that should process normal // key events, meaning any key that has an // an ASCII code. // glutSpecialFunc(specialKeys); // This is the other GLUT function that // registers callbacks for keyboard events. // The passed-in function processes special // keyboard events, like the arrows. glutDisplayFunc(draw); // This tells GLUT that the function // draw should be used whenever the // window is reported by the window // system to be damaged (this redraws); // this also tells GLUT that draw // should be called the first time the // window is created glutIdleFunc(idle); // means that when the application is idle // the idle function should be called; // this allows glut to keep calling this // rendering function, enabling animation glutReshapeFunc(applyCamera); // reshaped is the name of the function that // will be responsible for setting the // correct perspective when the window // changes size createGLUTMenus(); glutMainLoop(); return 1; }