/* flies - draws a swarm of hovering flies Copyright (C) 1998 James Bowman flies is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this software; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include /* for cos(), sin(), and sqrt() */ #include #ifdef _WIN32 #include #else #include #endif #include static int texWidth = 64; /* 64x64 is plenty */ static int paused = 0; /* If it's paused */ static int motion_blur = 1; /* Temporal antialiasing using supersampling */ /************************************************************************/ /* Generate a pleasing, fuzzy-dot-shaped texture. */ static void setTexture(void) { int texSize; void *textureBuf; GLubyte *p; int i,j; double radius; texSize = texWidth*texWidth; textureBuf = malloc(texSize); if (NULL == textureBuf) return; p = (GLubyte *)textureBuf; radius = (double)(texWidth / 2); for (i=0; i < texWidth; i++) { for (j=0; j < texWidth; j++) { double x, y, d; x = fabs((double)(i - (texWidth / 2))); y = fabs((double)(j - (texWidth / 2))); d = sqrt((x * x) + (y * y)); if (d < radius) { double t = 1.0 - (d / radius); /* t is 1.0 at center, 0.0 at edge */ *p = (int)((double)0xff * (t * t)); } else *p = 0x00; p++; } } gluBuild2DMipmaps(GL_TEXTURE_2D, 1, texWidth, texWidth, GL_LUMINANCE, GL_UNSIGNED_BYTE, textureBuf); free(textureBuf); } /************************************************************************/ static int W, H; /* Viewport dimensions */ #define NUM_FLIES 10 #define NUM_TIME_SAMPLES 20 #define frand() ((float)rand() / (float)RAND_MAX) /* 0.0 - 1.0 */ #define frand2() (1.0f - (2.0f * frand())) /* -1.0 - +1.0 */ #define FREQ 20 #define MAX_SPEED (0.09f / (float)NUM_TIME_SAMPLES) #define DELTA (0.0005f / (float)NUM_TIME_SAMPLES) void redraw_viewing(void) { static int cold = 1; static struct fly { float position[3]; float velocity[3]; } flies[NUM_FLIES]; GLfloat fb_buffer[NUM_TIME_SAMPLES][NUM_FLIES * 4]; int drawn_flies[NUM_TIME_SAMPLES]; /* How many flies visible in each pass */ int t, /* For iterating over passes */ i; /* For iterating over flies */ if (cold) { /* Position all the flies more-or-less randomly on the surface * of a unit sphere. */ for (i = 0; i < NUM_FLIES; i++) { flies[i].position[0] = frand2(); flies[i].position[1] = frand2(); flies[i].position[2] = frand2(); flies[i].velocity[0] = MAX_SPEED * frand2() / (float)NUM_TIME_SAMPLES; flies[i].velocity[1] = MAX_SPEED * frand2() / (float)NUM_TIME_SAMPLES; flies[i].velocity[2] = MAX_SPEED * frand2() / (float)NUM_TIME_SAMPLES; } cold = 0; } glClear(GL_COLOR_BUFFER_BIT); for (t = 0; t < NUM_TIME_SAMPLES; t++) { for (i = 0; i < NUM_FLIES; i++) { int j; /* j iterates over the axes */ for (j = 0; j < 3; j++) { if ((rand() % (FREQ * NUM_TIME_SAMPLES)) == 0) flies[i].velocity[j] = frand2() * MAX_SPEED; if (flies[i].position[j] < 0.0f) flies[i].velocity[j] += DELTA; else flies[i].velocity[j] -= DELTA; flies[i].position[j] += flies[i].velocity[j]; } } /* Use feedback to determine the screen positions of the flies. */ glFeedbackBuffer(NUM_FLIES * 4, GL_3D, fb_buffer[t]); glRenderMode(GL_FEEDBACK); glBegin(GL_POINTS); for (i = 0; i < NUM_FLIES; i++) glVertex3fv(flies[i].position); glEnd(); drawn_flies[t] = glRenderMode(GL_RENDER); } /* Now draw the flies as sprites. */ glEnable(GL_TEXTURE_2D); glEnable(GL_BLEND); /* Simple additive blend is fine */ glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); /* Set up projection and modelview matrix that gives us a 1:1 * mapping to screen coordinates. */ glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); glScalef(2.0f / (GLfloat)W, 2.0f / (GLfloat)H, 1.0f); glTranslatef(0.5f * -(GLfloat)W, 0.5f * -(GLfloat)H, 0.0f); { float width; float x, y, z; int samples; glBegin(GL_QUADS); if (motion_blur) { samples = NUM_TIME_SAMPLES; /* We could alter the color to achieve more sophisticated effects than the 'box' filter we get by having a constant color for every sample. e.g. could give more weight to later samples, or only sample part of the period to simulate a film camera's shutter. */ glColor3f(2.0f / NUM_TIME_SAMPLES, 2.0f / NUM_TIME_SAMPLES, 2.0f / NUM_TIME_SAMPLES); } else { samples = 1; glColor3f(1.0f, 1.0f, 1.0f); } for (t = 0; t < samples; t++) { for (i = 0; i < (drawn_flies[t] / 4); i++) { /* Skip the GL_POINT_TOKEN at [4 * i] */ x = fb_buffer[t][4 * i + 1]; y = fb_buffer[t][4 * i + 2]; z = fb_buffer[t][4 * i + 3]; width = 3.0 * (2.0 - z); /* Arbitrary distance attenuation */ glTexCoord2f(0.0f, 0.0f); glVertex2f(x - width, y - width); glTexCoord2f(1.0f, 0.0f); glVertex2f(x + width, y - width); glTexCoord2f(1.0f, 1.0f); glVertex2f(x + width, y + width); glTexCoord2f(0.0f, 1.0f); glVertex2f(x - width, y + width); } } glEnd(); } glMatrixMode(GL_MODELVIEW); glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); glutSwapBuffers(); } /************************************************************************/ void reshape_viewing(int w, int h) { glViewport(0, 0, w, h); W = w; H = h; } void animate(void) { if (!paused) { glutPostRedisplay(); } } void key(unsigned char key, int x, int y) { switch (key) { case 27: case 'q': exit(0); case 'm': motion_blur = !motion_blur; glutPostRedisplay(); break; case ' ': paused = 1; glutPostRedisplay(); break; case 'c': case 'g': paused = 0; break; } } int main(int argc, char **argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE); glutInitWindowSize(480, 480); glutCreateWindow("flies"); glutReshapeFunc(reshape_viewing); glutDisplayFunc(redraw_viewing); glutIdleFunc(animate); glutKeyboardFunc(key); glDisable(GL_DEPTH_TEST); glDisable(GL_LIGHTING); glClearColor(1.0f, 1.0f, 1.0f, 1.0f); glMatrixMode(GL_MODELVIEW); setTexture(); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glMatrixMode(GL_PROJECTION); gluPerspective( /* field of view in degree */ 40.0f, /* aspect ratio */ 1.0f, /* Z near */ 1.0f, /* Z far */ 10.0f); glMatrixMode(GL_MODELVIEW); gluLookAt(0.0f, 0.0f, 4.0f, /* eye position */ 0.0f, 0.0f, 0.0f, /* looking at */ 0.0f, 1.0f, 0.0f); /* up vector */ glutMainLoop(); return 0; /* ANSI C requires main to return int. */ }