////////////////////////////////////////////////////////////////// // Concept: Steven C. Dollins (Brown University) // Modified: Robert C. Duvall // Fall 2001 // #include // also includes glu and gl correctly #include // for atoi #include // for sprintf #include // for USHRT_MAX #include // for sin, cos, pow, etc. ////////////////////////////////////////////////////////////////// // Globals // // typedefs // typedef unsigned short ushort; // // Constants // const int INIT_POINT_SIZE = 3; const float NUM_POINTS_BASE = M_SQRT2; float * const COS = new float[USHRT_MAX + 1]; float * const SIN = new float[USHRT_MAX + 1]; // // Variables // // control how many points to display int theNumPoints; int theNumPointsFactor = 18; // control how fast animation moves float theSpeed = 0.01; float theLocalTime = 0.0; // control viewing angle of animation float theXAngle = 0; float theZAngle = 0; // allow user to turn axes on and off int theShouldDrawAxes = 0; // allow user to switch between full-screen and window mode int theShouldBeFullScreen = 0; int theWindowWidth = 300; int theWindowHeight = 300; ////////////////////////////////////////////////////////////////// // Utility functions // inline int min (int a, int b) { return (a < b) ? a : b; } inline int max (int a, int b) { return (a > b) ? a : b; } inline int clamp (int a, int minLimit, int maxLimit) { return max(minLimit, min(a, maxLimit)); } void initMath () // post: trig functions precomputed into table { for (int k = 0; k <= USHRT_MAX; k++) { float t = k * (2.0 * M_PI) / (USHRT_MAX + 1); COS[k] = cos(t); SIN[k] = sin(t); } } ////////////////////////////////////////////////////////////////// // User Functions // These are the functions you will mostly be changing. // void initModel () { initMath(); theNumPoints = int(pow(NUM_POINTS_BASE, theNumPointsFactor)); glPointSize(INIT_POINT_SIZE); } void drawAxes () { glLineWidth(5.0); glBegin(GL_LINES); glColor3f(1.0, 0.0, 0.0); glVertex3f(2.0, 0.0, 0.0); glVertex3f(-2.0, 0.0, 0.0); glColor3f(0.0, 1.0, 0.0); glVertex3f(0.0, 2.0, 0.0); glVertex3f(0.0, -2.0, 0.0); glColor3f(0.0, 0.0, 1.0); glVertex3f(0.0, 0.0, 2.0); glVertex3f(0.0, 0.0, -2.0); glEnd(); } void drawModel () { glBegin(GL_POINTS); theLocalTime += theSpeed; for (float k = theNumPoints; k >= 0; --k) { float s = k / theNumPoints; float t = theLocalTime * s; ushort angle_idx = ushort(13627.0 * (t - theLocalTime / 2.0)); ushort radius_idx = ushort(15646.0 * t); ushort height_idx = ushort(9970.0 * t); float radius = 1.0 + 0.75 * COS[radius_idx]; float x = COS[angle_idx] * radius; float y = SIN[angle_idx] * radius; float z = SIN[height_idx]; glColor3f(s, s * (z + 1.0) * 0.5, s * (-z + 1.0) * 0.375 + 0.25); glVertex3f(x, y, z); } glEnd(); } ////////////////////////////////////////////////////////////////// // Callback Functions // These functions are registered with the glut window and called // when certain events occur. // void onInit () { initModel(); glEnable(GL_DEPTH_TEST); } void onRedraw () { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glRotatef(theXAngle, 1, 0, 0); glRotatef(theZAngle, 0, 0, 1); if (theShouldDrawAxes) { drawAxes(); } drawModel(); glRotatef(-theZAngle, 0, 0, 1); glRotatef(-theXAngle, 1, 0, 0); glutSwapBuffers(); } void onKeyPressed (unsigned char key, int /* x */, int /* y */) { switch (key) { case 'q': case 27: /* Escape */ exit(0); break; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': glPointSize(key - '0'); break; case 'f': if (theShouldBeFullScreen ^= 1) glutFullScreen(); else glutReshapeWindow(theWindowWidth, theWindowHeight); break; case '[': theNumPointsFactor--; if (theNumPointsFactor > 0) theNumPoints = int(pow(NUM_POINTS_BASE, theNumPointsFactor)); break; case ']': theNumPointsFactor++; if (theNumPointsFactor < 32) theNumPoints = int(pow(NUM_POINTS_BASE, theNumPointsFactor)); break; case ',': theSpeed -= 0.01; break; case '.': theSpeed += 0.01; break; case '/': theLocalTime += theSpeed * -10; break; case 'm': theLocalTime += theSpeed * 10; break; case ' ': theShouldDrawAxes ^= 1; break; default: return; } glutPostRedisplay(); } void onSpecialKeyPressed (int key, int /* x */, int /* y */) { switch (key) { case GLUT_KEY_UP: theXAngle += 5; break; case GLUT_KEY_DOWN: theXAngle -= 5; break; case GLUT_KEY_LEFT: theZAngle -= 5; break; case GLUT_KEY_RIGHT: theZAngle += 5; break; default: return; } glutPostRedisplay(); } void onResize (int width, int height) { if (! theShouldBeFullScreen) { theWindowWidth = width; theWindowHeight = height; } glViewport(0, 0, GLint(width), GLint(height)); // reset size of viewing area GLfloat w = (GLfloat) width / (GLfloat) height; glMatrixMode(GL_PROJECTION); glLoadIdentity(); glFrustum(-w, w, -1.0, 1.0, 5.0, 25.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslatef(0.0, 0.0, -10.0); } void onVisible (int state) { if (state == GLUT_VISIBLE) { glutIdleFunc(glutPostRedisplay); } else { glutIdleFunc(NULL); } } ////////////////////////////////////////////////////////////////// // Main Function // int main (int argc, char *argv[]) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE); glutCreateWindow("\"Particle Man, Particle Man...\""); onInit(); glutDisplayFunc(onRedraw); glutReshapeFunc(onResize); glutKeyboardFunc(onKeyPressed); glutSpecialFunc(onSpecialKeyPressed); glutVisibilityFunc(onVisible); glutMainLoop(); return 0; }