/** * @file graphics.c * @author Sam Moore (20503628) 2012 * - Adapted from template program provided by UWA * @purpose Definition of graphics related functions * NOTE: This file is identical for both the single-threaded and multi-threaded versions of the program */ #include #include #include // Needed for clock #include // Maths functions (sin, cos) #include "graphics.h" //Function declarations #include "nbody.h" //The simulation // --- Variable definitions --- // double previousTime, eyeTheta, eyePhi, eyeRho; float look[3]; int windowWidth, windowHeight, upY; double scale = 1.0; #ifdef FLYING_CAMERA Camera eye; #endif //FLYING_CAMERA /** * @function Graphics_Run * @purpose Setup and start the graphics engine * @param argc - Number of arguments to main function, passed to glutInit * @param argv - Argument strings for main function, passed to glutInit */ void Graphics_Run(int argc, char ** argv) { if (options.draw_graphics == false) { // This message is left here for when I inevitably accidentally call the function fprintf(stderr, "Graphics_Run should not be called if graphics are disabled!\n"); exit(EXIT_FAILURE); } glutInit(&argc, argv); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); glutInitWindowSize(WIDTH, HEIGHT); glutInitWindowPosition(POSITION_X, POSITION_Y); //Set window title based on version of program #ifdef SINGLE_THREADED glutCreateWindow("N-Body : Single-Threaded"); #elif defined PTHREADED glutCreateWindow("N-Body : Multi-Threaded (pthread)"); #elif defined OMP_THREADED glutCreateWindow("N-Body : Multi-Threaded (OpenMP)"); #else glutCreateWindow("N-Body"); #endif glutDisplayFunc(Graphics_Display); glutIdleFunc(Graphics_Display); glutKeyboardFunc(Graphics_Keyboard); glutReshapeFunc(Graphics_Reshape); glClearColor(1.0,1.0,1.0,0.0); glColor3f(0.0f, 0.0f, 0.0f); glPointSize(POINT_SIZE); glMatrixMode(GL_PROJECTION); glLoadIdentity(); /*init lighting */ GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 }; GLfloat mat_shininess[] = { 50.0 }; GLfloat light_position[] = { 1.0, 1.0, 0.0, 0.0 }; glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular); glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess); glLightfv(GL_LIGHT0, GL_POSITION, light_position); glColorMaterial(GL_FRONT,GL_DIFFUSE); // Set Color Capability glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GL_DEPTH_TEST); glEnable(GL_COLOR_MATERIAL); // Enable color windowWidth = WIDTH; windowHeight = HEIGHT; previousTime = clock(); eyeTheta = 0; eyePhi = 0.5 * M_PI; eyeRho = RHO; upY = 1; look[0] = 0; look[1] = 0; look[2] = 0; gluPerspective(VIEW_ANGLE, (double)WIDTH/(double)HEIGHT, WORLD_NEAR, WORLD_FAR); #ifdef FLYING_CAMERA eye.x[0] = 0; eye.x[1] = 0; eye.x[2] = 1; eye.y[0] = 0; eye.y[1] = 1; eye.y[2] = 0; eye.z[0] = 1; eye.z[1] = 0; eye.z[2] = 0; eye.p[0] = 0; eye.p[1] = 0; eye.p[2] = -50; #endif //FLYING_CAMERA //This option must be set, or when glut leaves the main loop. the exit(3) function is called... annoying glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_CONTINUE_EXECUTION); #ifndef OMP_THREADED glutMainLoop(); #endif //OMP_THREADED } /** * @function Graphics_Display * @purpose This function redraws the screen after the positions of particles * have been updated. * It also calls System_Compute, and checks for exit conditions, in the single-threaded version only */ void Graphics_Display() { if (options.draw_graphics == false) { // This message is left here for when I inevitably accidentally call the function fprintf(stderr, "Graphics_Display should not be called if graphics are disabled!\n"); exit(EXIT_FAILURE); } //Check whether the graphics thread should exit for any reason if (ExitCondition()) { glutLeaveMainLoop(); // original glut does not have this, which makes "nicely" exiting a bit annoying return; } glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); #ifdef FLYING_CAMERA gluLookAt(eye.p[0], eye.p[1], eye.p[2], eye.p[0] + eye.x[0], eye.p[1] + eye.x[1], eye.p[2] + eye.x[2], eye.z[0], eye.z[1], eye.z[2]); #else gluLookAt(eyeRho * sin(eyePhi) * sin(eyeTheta), eyeRho * cos(eyePhi), eyeRho * sin(eyePhi) * cos(eyeTheta),look[0], look[1], look[2], 0, upY, 0); #endif //FLYING_CAMERA BeforeDraw(); // Stuff to do before graphics is allowed to draw // Single-thread - perform a computation step, obviously! It's not done anywhere else // Multiple threads - ensure that all body positions are updated (ie: not halfway through step). // (We don't care about the force and velocity variables though) for (int i = 0; i < universe.N; ++i) { Body * b = universe.body+i; glColor3f(0.0f, b->mass/1e11*100, 0.0f); //glColor3f(1.0f, 0.0f, 0.0f); glPushMatrix(); // to save the current matrix glTranslated(scale*b->x[0], scale*b->x[1], scale*b->x[2]); glutSolidSphere (BALL_SIZE, 10, 10); glPopMatrix(); // restore the previous matrix } glFlush(); AfterDraw(); // Stuff to do after graphics is done drawing // Single-thread - Nothing at all // Multiple threads - signal threads it is safe to change position variables } /** * @function Graphics_Keyboard * @purpose This function is to manipulate with the image * @param theKey key pressed * @param mouseX, mouseY position of the mouse in the window */ void Graphics_Keyboard(unsigned char theKey, int mouseX, int mouseY) { if (theKey == 'x' || theKey == 'X') { QuitProgram(false); return; } #ifdef FLYING_CAMERA switch (theKey) { // Translate in direction of camera case 'W': case 'w': for (unsigned i = 0; i < DIMENSIONS; ++i) eye.p[i] += eye.x[i]; break; // Translate backwards from camera direction case 'S': case 's': for (unsigned i = 0; i < DIMENSIONS; ++i) eye.p[i] -= eye.x[i]; break; // Translate left from camera direction case 'A': case 'a': for (unsigned i = 0; i < DIMENSIONS; ++i) eye.p[i] -= eye.y[i]; break; // Translate right from camera direction case 'D': case 'd': for (unsigned i = 0; i < DIMENSIONS; ++i) eye.p[i] += eye.y[i]; break; // Translate up from camera direction case 'Q': case 'q': for (unsigned i = 0; i < DIMENSIONS; ++i) eye.p[i] += eye.z[i]; break; // Translate down from camera direction case 'E': case 'e': for (unsigned i = 0; i < DIMENSIONS; ++i) eye.p[i] -= eye.z[i]; break; // Rotate camera direction "down" // (pitch control) case 'I': case 'i': { float theta = M_PI/80.0; Camera old = eye; for (unsigned i = 0; i < DIMENSIONS; ++i) { eye.z[i] = old.z[i] * cos(theta) + old.x[i] * sin(theta); eye.x[i] = old.x[i] * cos(theta) - old.z[i] * sin(theta); } break; } // Rotate camera direction "up" // (pitch control) case 'K': case 'k': { float theta = -M_PI/80.0; Camera old = eye; for (unsigned i = 0; i < DIMENSIONS; ++i) { eye.z[i] = old.z[i] * cos(theta) + old.x[i] * sin(theta); eye.x[i] = old.x[i] * cos(theta) - old.z[i] * sin(theta); } break; } // Rotate camera direction "left" // (yaw control) case 'J': case 'j': { float theta = +M_PI/80.0; Camera old = eye; for (unsigned i = 0; i < DIMENSIONS; ++i) { eye.y[i] = old.y[i] * cos(theta) + old.x[i] * sin(theta); eye.x[i] = old.x[i] * cos(theta) - old.y[i] * sin(theta); } break; } // Rotate camera direction "right" // (yaw control) case 'L': case 'l': { float theta = -M_PI/80.0; Camera old = eye; for (unsigned i = 0; i < DIMENSIONS; ++i) { eye.y[i] = old.y[i] * cos(theta) + old.x[i] * sin(theta); eye.x[i] = old.x[i] * cos(theta) - old.y[i] * sin(theta); } break; } // Rotate camera direction CCW about its axis // (roll control) case 'U': case 'u': { float theta = -M_PI/80.0; Camera old = eye; for (unsigned i = 0; i < DIMENSIONS; ++i) { eye.z[i] = old.z[i] * cos(theta) + old.y[i] * sin(theta); eye.y[i] = old.y[i] * cos(theta) - old.z[i] * sin(theta); } break; } // Rotate camera direction CW about its axis // (roll control) case 'O': case 'o': { float theta = +M_PI/80.0; Camera old = eye; for (unsigned i = 0; i < DIMENSIONS; ++i) { eye.z[i] = old.z[i] * cos(theta) + old.y[i] * sin(theta); eye.y[i] = old.y[i] * cos(theta) - old.z[i] * sin(theta); } break; } } /* Code used for debugging the camera system("clear"); printf("Camera status:\n"); printf("Position: %f %f %f\n", eye.p[0], eye.p[1], eye.p[2]); printf("x: %f %f %f (%f)\n", eye.x[0], eye.x[1], eye.x[2], sqrt(eye.x[0]*eye.x[0] + eye.x[1]*eye.x[1] + eye.x[2]*eye.x[2])); printf("y: %f %f %f (%f)\n", eye.y[0], eye.y[1], eye.y[2], sqrt(eye.y[0]*eye.y[0] + eye.y[1]*eye.y[1] + eye.y[2]*eye.y[2])); printf("z: %f %f %f (%f)\n", eye.z[0], eye.z[1], eye.z[2], sqrt(eye.z[0]*eye.z[0] + eye.z[1]*eye.z[1] + eye.z[2]*eye.z[2])); */ #else // The original view code // I like how 'I' is used three times. if (theKey == 'i' || theKey == 'I') { eyePhi -= M_PI / 20; if (eyePhi == 0) eyePhi = 2 * M_PI; } else if (theKey == 'm' || theKey == 'I') { eyePhi += M_PI / 20; } else if (theKey == 'j' || theKey == 'J') { eyeTheta -= M_PI / 20; } else if (theKey == 'k' || theKey == 'K') { eyeTheta += M_PI / 20; } else if (theKey == ',') { eyeRho += 0.5; } else if (theKey == '.' || theKey == 'I') { eyeRho -= 0.5; } else if (theKey == 'w' || theKey == 'W') { look[1] += 0.5; } else if (theKey == 'z' || theKey == 'Z') { look[1] -= 0.5; } else if (theKey == 'a' || theKey == 'A') { look[0] -= 0.5; } else if (theKey == 's' || theKey == 'S') { look[0] += 0.5; } else if (theKey == '+') { scale *= 1.1; } else if (theKey == '-') { scale *= 0.9; } if (sin(eyePhi) > 0) upY = 1; else upY = 1; #endif //FLYING_CAMERA } /** * @function Graphics_Reshape * @purpose Resize the view window * @param width, height - New size of the window */ void Graphics_Reshape(int width, int height) { double displayRatio = 1.0 * width / height; windowWidth = width; windowHeight = height; glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(VIEW_ANGLE, displayRatio, WORLD_NEAR, WORLD_FAR); glViewport(0, 0, windowWidth, windowHeight); }