/* darkspot - shows use of darklights to produce a shadow-like effect. Copyright (C) 1999 James Bowman This 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 #include static int phi; /* Global clock */ static int move_cube = 1; static int move_light = 1; static int move_spheres = 1; /************************************************************************/ #if !defined(M_PI) #define M_PI 3.141593 #endif #define LAT_DIVISIONS 8 /* How many lines of latitude in the sphere */ #define LON_DIVISIONS 16 /* longitude */ static void spherePoint(float *p, double lat, double lon) { double lat_angle, lon_angle; double r; lat_angle = lat * M_PI; lon_angle = lon * 2 * M_PI; p[1] = cos(lat_angle); r = sin(lat_angle); p[0] = sin(lon_angle) * r; p[2] = cos(lon_angle) * r; } static void drawSphere(double radius) { int i, j; static int cold = 1; static float surface[LAT_DIVISIONS + 1][LON_DIVISIONS][3]; if (cold) { double lat, lon; double lat_step = (1.0 / (double)LAT_DIVISIONS); double lon_step = (1.0 / (double)LON_DIVISIONS); for (i = 0; i <= LAT_DIVISIONS; i++) { for (j = 0; j < LON_DIVISIONS; j++) { lat = ((float)i / (float)LAT_DIVISIONS); lon = ((float)j / (float)LON_DIVISIONS); spherePoint(surface[i][j], lat, lon); } } cold = 0; } for (i = 0; i < LAT_DIVISIONS; i++) { glBegin(GL_TRIANGLE_STRIP); for (j = 0; j < LON_DIVISIONS; j++) { glNormal3fv(surface[i][j]); glVertex3f(surface[i][j][0] * radius, surface[i][j][1] * radius, surface[i][j][2] * radius); glNormal3fv(surface[i + 1][j]); glVertex3f(surface[i + 1][j][0] * radius, surface[i + 1][j][1] * radius, surface[i + 1][j][2] * radius); } glNormal3fv(surface[i][0]); glVertex3f(surface[i][0][0] * radius, surface[i][0][1] * radius, surface[i][0][2] * radius); glNormal3fv(surface[i + 1][0]); glVertex3f(surface[i + 1][0][0] * radius, surface[i + 1][0][1] * radius, surface[i + 1][0][2] * radius); glEnd(); } } /************************************************************************/ #define SHEET_SUBDIVISIONS 20 void drawSheet(void) { int i, j; glNormal3f(0.0f, 0.0f, 1.0f); for (i = 0; i < SHEET_SUBDIVISIONS; i++) { glBegin(GL_TRIANGLE_STRIP); for (j = 0; j <= SHEET_SUBDIVISIONS; j++) { GLfloat x, y; x = (2.0f * (GLfloat)i / ((GLfloat)SHEET_SUBDIVISIONS)) - 1.0f; y = (2.0f * (GLfloat)j / ((GLfloat)SHEET_SUBDIVISIONS)) - 1.0f; glVertex2f(x, y); glVertex2f(x + (2.0f / (GLfloat)SHEET_SUBDIVISIONS), y); } glEnd(); } } static void drawTwoSheets(void) { glPushMatrix(); glTranslatef(0.0f, 0.0f, -1.0f); drawSheet(); glPopMatrix(); glPushMatrix(); glTranslatef(0.0f, 0.0f, 1.0f); glRotatef(180.0f, 0.0f, 1.0f, 0.0f); drawSheet(); glPopMatrix(); } void drawCube(void) { drawTwoSheets(); glPushMatrix(); glRotatef(90.0, 0.0f, 1.0f, 0.0f); drawTwoSheets(); glPopMatrix(); glPushMatrix(); glRotatef(90.0, 1.0f, 0.0f, 0.0f); drawTwoSheets(); glPopMatrix(); } /************************************************************************/ static int W, H; #define NUM_SPHERES 4 /* How many spheres to draw */ #define frand() ((float)(rand() & 0xfff) / (float)0x1000) /* 0.0 - 1.0 */ #define frand2() (1.0f - (2.0f * frand())) /* -1 - +1 */ /* Parameters affecting the motion of lights and spheres */ #define FREQ 400 #define DELTA 0.001f #define MAX_SPEED (DELTA * 20.0f) struct sphere { float position[3]; float velocity[3]; float distance; }; /* Used for distance-sorting the spheres */ static int sphere_nearest_compare(const void *p0, const void *p1) { const struct sphere *f0, *f1; f0 = p0; f1 = p1; if (f0->distance < f1->distance) return -1; else if (f0->distance > f1->distance) return 1; else return 0; } void redraw_viewing(void) { static int cold = 1; int i, j; static struct sphere spheres[NUM_SPHERES]; GLfloat source_pos[4]; if ((W == 0) || (H == 0)) return; if (cold) { /* Position all the spheres more or less at random. */ for (i = 0; i < NUM_SPHERES; i++) { spheres[i].position[0] = frand2(); spheres[i].position[1] = frand2(); spheres[i].position[2] = frand2(); spheres[i].velocity[0] = MAX_SPEED * frand2(); spheres[i].velocity[1] = MAX_SPEED * frand2(); spheres[i].velocity[2] = MAX_SPEED * frand2(); } cold = 0; } for (i = 0; i < NUM_SPHERES; i++) { if (((i == 0) && move_light) || ((i != 0) && move_spheres)) { /* j iterates over the axes */ for (j = 0; j < 3; j++) { if ((rand() % (FREQ)) == 0) spheres[i].velocity[j] = frand2() * MAX_SPEED; if (spheres[i].position[j] < 0.0f) spheres[i].velocity[j] += DELTA; else spheres[i].velocity[j] -= DELTA; spheres[i].position[j] += spheres[i].velocity[j]; } } } glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); glPushMatrix(); /* sphere[0] is the light source; draw it as a white globe */ glDisable(GL_LIGHTING); glPushMatrix(); glTranslatef(spheres[0].position[0], spheres[0].position[1], spheres[0].position[2]); drawSphere(0.1); glPopMatrix(); glEnable(GL_LIGHTING); /* There is one "normal" light in the scene. Have to copy here because GL needs us to specify w==1 for local light position. */ source_pos[0] = spheres[0].position[0]; source_pos[1] = spheres[0].position[1]; source_pos[2] = spheres[0].position[2]; source_pos[3] = 1.0f; /* Means local light. */ glLightfv(GL_LIGHT0, GL_POSITION, source_pos); /* All the other lights are darklights. Disable them all initially */ for (i = 1; i < NUM_SPHERES; i++) glDisable(GL_LIGHT0 + i); /* Calculate distance^2 from the source for each light */ for (i = 1; i < NUM_SPHERES; i++) { GLfloat toSource[3]; for (j = 0; j < 3; j++) toSource[j] = spheres[i].position[j] - source_pos[j]; spheres[i].distance = (toSource[0] * toSource[0]) + (toSource[1] * toSource[1]) + (toSource[2] * toSource[2]); } /* Order the spheres from nearest to furthest */ qsort(&spheres[1], NUM_SPHERES - 1, sizeof(struct sphere), sphere_nearest_compare); /* OK, now draw them in that order */ for (i = 1; i < NUM_SPHERES; i++) { glPushMatrix(); glTranslatef(spheres[i].position[0], spheres[i].position[1], spheres[i].position[2]); drawSphere(0.1); glPopMatrix(); /* This "antiwhite" spot is the shadow of that sphere */ { static const GLfloat antiwhite[4] = { -1.0f, -1.0f, -1.0f, 1.0f }; GLfloat spotDirection[3]; /* Spotlight parameters */ glEnable(GL_LIGHT0 + i); glLightfv(GL_LIGHT0 + i, GL_DIFFUSE, antiwhite); glLightf(GL_LIGHT0 + i, GL_SPOT_CUTOFF, 60.0f); glLightf(GL_LIGHT0 + i, GL_SPOT_EXPONENT, 20.0f); /* The spotlight's position is the same as the light source. */ glLightfv(GL_LIGHT0 + i, GL_POSITION, source_pos); /* Direction is set up so that it shines 'through' the sphere we've just drawn */ for (j = 0; j < 3; j++) spotDirection[j] = spheres[i].position[j] - spheres[0].position[j]; glLightfv(GL_LIGHT0 + i, GL_SPOT_DIRECTION, spotDirection); } } /* Finally draw the surrounding cube */ 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); drawCube(); glPopMatrix(); glutSwapBuffers(); } void reshape_viewing(int w, int h) { glViewport(0, 0, w, h); W = w; H = h; } /************************************************************************/ void animate(void) { if (move_cube) phi++; redraw_viewing(); } void key(unsigned char key, int x, int y) { switch (key) { case 'c': move_cube ^=1 ; break; case 'l': move_light ^= 1; break; case 's': move_spheres ^= 1; break; case 27: case 'q': exit(0); } } int main(int argc, char **argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH); glutInitWindowSize(480, 480); glutCreateWindow("darkspot"); glutReshapeFunc(reshape_viewing); glutDisplayFunc(redraw_viewing); glutIdleFunc(animate); glutKeyboardFunc(key); glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); glEnable(GL_LIGHTING); { static const GLfloat lightPosition[4] = { 0.0f, 1.0f, 0.0f, 0.0f }; static const GLfloat black[4] = { 0.0f, 0.0f, 0.0f, 1.0f }; static const GLfloat white[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; static const GLfloat brown[4] = { 1.0f, 0.8f, 0.7f, 1.0f }; glLightfv(GL_LIGHT0, GL_DIFFUSE, white); glLightfv(GL_LIGHT0, GL_SPECULAR, black); glEnable(GL_LIGHT0); glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, brown); glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, white); glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 20.0f); } glMatrixMode(GL_MODELVIEW); glMatrixMode(GL_PROJECTION); gluPerspective( /* field of view in degree */ 20.0f, /* aspect ratio */ 1.0f, /* Z near */ 3.0f, /* Z far */ 10.0f); glMatrixMode(GL_MODELVIEW); gluLookAt(0.0f, 0.0f, 8.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. */ }