/* stars - draws a twisting sphere of eerie lights Copyright (C) 1998 James Bowman stars 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 phi; /* Global clock */ static int texWidth = 64; /* 64x64 is plenty */ /************************************************************************/ /* Generate a pleasing, light-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 */ /* inverse square looks nice */ *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; #define NUM_STARS 100 #define frand() ((float)(rand() & 0xfff) / (float)0x1000) /* 0.0 - 1.0 */ #define frand2() (1.0f - (2.0f * frand())) /* -1 - +1 */ /* Normalise the input vector */ static void vecNormalise(float *vv) { double mag; mag = sqrt((vv[0] * vv[0]) + (vv[1] * vv[1]) + (vv[2] * vv[2])); vv[0] /= mag; vv[1] /= mag; vv[2] /= mag; } void redraw_viewing(void) { static int cold = 1; static float stars[NUM_STARS * 3]; GLfloat fb_buffer[NUM_STARS * 4]; int i; if (cold) { /* Position all the stars more-or-less randomly on the surface * of a unit sphere. */ for (i = 0; i < NUM_STARS; i++) { stars[3 * i + 0] = frand2(); stars[3 * i + 1] = frand2(); stars[3 * i + 2] = frand2(); vecNormalise(&stars[3 * i]); } cold = 0; } glClear(GL_COLOR_BUFFER_BIT); /* First use feedback to determine the screen positions of the stars. */ glFeedbackBuffer(NUM_STARS * 4, GL_3D, fb_buffer); glRenderMode(GL_FEEDBACK); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glRotatef(phi % 360, 0.0f, 1.0f, 0.0f); /* Constant spin */ glRotatef(sin((double)phi / 300.0) * 150.0, /* Periodic twist */ 0.1f, 0.6f, -0.5f); glBegin(GL_POINTS); for (i = 0; i < NUM_STARS; i++) glVertex3fv(&stars[3 * i]); glEnd(); glPopMatrix(); glRenderMode(GL_RENDER); /* No more feedback */ /* Now draw the stars as sprites. */ glEnable(GL_TEXTURE_2D); glEnable(GL_BLEND); /* Simple additive blend is fine */ glBlendFunc(GL_ONE, GL_ONE); /* Choose a color triple like this so that when the sprites overlap * and the color saturates, red will clamp first, then green, then * blue. */ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glColor3f(1.0f, 0.75f, 0.5f); /* 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 i; glBegin(GL_QUADS); for (i = 0; i < NUM_STARS; i++) { /* Skip the GL_POINT_TOKEN */ x = fb_buffer[4 * i + 1]; y = fb_buffer[4 * i + 2]; z = fb_buffer[4 * i + 3]; width = 10.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) { phi++; redraw_viewing(); } void key(unsigned char key, int x, int y) { switch (key) { case 27: case 'q': exit(0); } } int main(int argc, char **argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE); glutInitWindowSize(480, 480); glutCreateWindow("stars"); glutReshapeFunc(reshape_viewing); glutDisplayFunc(redraw_viewing); glutIdleFunc(animate); glutKeyboardFunc(key); glDisable(GL_DEPTH_TEST); glDisable(GL_LIGHTING); 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 */ 3.0f, /* Z far */ 5.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. */ }