/**
* @file graphics.c
- * @author Sam Moore (20503628) 2012 - adapted from template program provided by UWA
+ * @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 "graphics.h" //Function declarations
+
+#include <stdlib.h>
+#include <stdio.h>
#include <time.h> // Needed for clock
#include <math.h> // Maths functions (sin, cos)
-/**
- * Variables
- */
+
+#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
/**
- * Initialise the graphics
+ * @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_Init(void)
+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)");
+ #elif defined BARNES_HUT
+ glutCreateWindow("N-Body : Barnes Hut Algorithm");
+ #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);
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
+ #ifndef BARNES_HUT
+ glutMainLoop(); // the mthread and single-thread versions can just start the loop here
+ // In OpenMP, because barriers have to be within the #pragma, need glutMainLoopEvent in the #pragma
+ #endif //BARNS_HUT
+ #endif //OMP_THREADED
}
/**
- * This function redraws the screen after the positions of particles
- * have been updated
+ * @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())
+ {
+ QuitProgram(false);
+ 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
}
+
+
+ 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
glFlush();
}
/**
- * This function is to manipulate with the image
+ * @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')
{
- exit(EXIT_SUCCESS);
+
+ QuitProgram(false);
+ glutLeaveMainLoop();
+ return;
}
+ #ifdef FLYING_CAMERA
+ float s = 10;
+ switch (theKey)
+ {
+ // Translate in direction of camera
+ case 'W':
+ case 'w':
+ for (unsigned i = 0; i < DIMENSIONS; ++i)
+ eye.p[i] += s * eye.x[i];
+ break;
+ // Translate backwards from camera direction
+ case 'S':
+ case 's':
+ for (unsigned i = 0; i < DIMENSIONS; ++i)
+ eye.p[i] -= s * eye.x[i];
+ break;
+ // Translate left from camera direction
+ case 'A':
+ case 'a':
+ for (unsigned i = 0; i < DIMENSIONS; ++i)
+ eye.p[i] -= s * eye.y[i];
+ break;
+ // Translate right from camera direction
+ case 'D':
+ case 'd':
+ for (unsigned i = 0; i < DIMENSIONS; ++i)
+ eye.p[i] += s * eye.y[i];
+ break;
+ // Translate up from camera direction
+ case 'Q':
+ case 'q':
+ for (unsigned i = 0; i < DIMENSIONS; ++i)
+ eye.p[i] += s * eye.z[i];
+ break;
+ // Translate down from camera direction
+ case 'E':
+ case 'e':
+ for (unsigned i = 0; i < DIMENSIONS; ++i)
+ eye.p[i] -= s * 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)
}
if (sin(eyePhi) > 0) upY = 1;
else upY = 1;
+ #endif //FLYING_CAMERA
}
/**
- * Resize the view window
+ * @function Graphics_Reshape
+ * @purpose Resize the view window
+ * @param width, height - New size of the window
*/
void Graphics_Reshape(int width, int height)
{
}
-/**
- * This function is called repeatedly by graphics library. You can consider
- * it as main loop in the program.
- */
-void Graphics_Animate(void)
-{
- System_Compute(&universe); //Compute and update new positions for the time step
- Graphics_Display(); // Display needs to be called to redraw the screen
-}
+