SCENE.CXX

/* 
* (c) Copyright 1993, Silicon Graphics, Inc.
* 1993-1995 Microsoft Corporation
*
* ALL RIGHTS RESERVED
*
* Please refer to OpenGL/readme.txt for additional information
*
*/

#include "glos.h"

extern "C" {
#include <GL/glaux.h>
}
#include <GL/glu.h>
#include <GL/gl.h>

#ifdef GLX_MOTIF
#include <GL/glx.h>
#endif

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>

#include "Unitdisk.hxx"
#include "scene.hxx"

const GLfloat I[16] = {
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
};

Color white;
Color black;

const double M_2PI = 2.0 * M_PI;
const float scene_fudge = .000001;


// Lights are native to the xz plane and are rotated into position -
// shadows and refraction will not have to be changed if lights are
// just rotating about the z axis

light lights[] = {
{{1, 0, 0, 1}, {0, 0, 0, 0}, {1, 0, 0, 0},
{1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1},
"Red", 1},
{{0, 1, 0, 1}, {0, 0, 0, 0}, {0, 1, 0, 0},
{1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1},
"Green", 1},
{{0, 0, 1, 1}, {0, 0, 0, 0}, {0, 0, 1, 0},
{1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1},
"Blue", 1}
};

GLfloat light_init_position[nlights][4] = {
{1.5, 0, 2.5, 1},
{1, 0, 3, 1},
{2, 0, 3, 1}
};

GLfloat light_init_rotation[nlights] = {135, 0, 90};
GLfloat light_rotation[nlights];

Color world_ambient(.25, .25, .25);

GLfloat index = indices[def_refraction_index].index;

GLfloat square_ambient[4] = {.25, .25, .25, 1};
GLfloat square_diffuse[4] = {1, 1, 1, 1};
GLfloat square_specular[4] = {0, 0, 0, 1};

const GLfloat fov = 45.0;
GLfloat aspect = 1.0;
GLfloat eyep[3] = {-6, 0, 6};
GLfloat lookp[3] = {0, 0, 1};


#ifdef GLX_MOTIF
static GLXContext glx_context;
#endif

const int max_args = 20;

static int list_square;
static int lists_shadows;
static int lists_refraction;
static int lists_lights = 5;
static int list_sphere = 4;
static int list_spheredisk = 9;
static int list_lights_on = 6;
static int list_lights_off = 7;
static int list_light_draw = 8;

int draw_square = 1;
int draw_shadows = 1;
int draw_refraction = 1;
int draw_sphere = 1;
int draw_lights = 1;
int draw_texture = 0;

int possible_divisions[] = {10, 20, 30, 40};

// Sphere is stored as floats - more efficient
GLfloat *spherepts = NULL;
int nspherepts = 0;
int spherediv = 0;
Point sphere_position = {0, 0, 1, 1};
GLfloat sphere_size = .5;
const GLfloat sphere_ambient[4] = {0, 0, 0, 0};
const GLfloat sphere_specular[4] = {0, 0, 0, 0};
Unitdisk sphere_disk;
static void sphere_build();
static void sphere_list_init();
static void sphere_draw();

static void square_list_init();

static void lights_init_onoff();
static void lights_init_position();
static void lights_init_position(int i);
static void lights_list_init();
static void lights_list_init(int n);

static void light_draw_list_init();

Unitdisk disks[nlights];
int diskdiv = possible_divisions[def_divisions_index];
static void disk_build();
static void disk_build(int disk);


Unitdisk shadows[nlights];
static void shadow_list_init();
static void shadow_list_init(int n);
static void shadow_draw(int n);

Unitdisk refraction[nlights];
static void refraction_list_init();
static void refraction_list_init(int n);

static void shadow_refraction_full_build();
static void shadow_refraction_full_build(int n);

void scene_init();



#ifdef MYDEBUG
void lists_init();
void lights_init();
int lights_move(int light, float dr, float dphi, float dtheta,
int update);

void lights_move_update(int light, int dr, int dphi, int dtheta);
#else
static void lists_init();
static void lights_init();
static int lights_move(int light, float dr, float dphi, float dtheta,
int update);
static void lights_move_update(int light, int dr, int dphi, int dtheta);
#endif


void scene_draw();

void texture_init();
AUX_RGBImageRec *teximage = NULL;

inline float sign(float a)
{
// This is badly written so let's not call it too often, 'k?
return (a > 0) ? (float)1 : (a < 0) ? (float) -1 : (float) 0;
}

inline double degrees(double a)
{
return (a * 180.0 / M_PI);
}

inline double radians(double a)
{
return (a * M_PI / 180.0);
}

inline double degrees_clamp(double a)
{
while (a < 0.0) a += 360.0;
while (a > 360.0) a -= 360.0;
return a;
}

inline double radians_clamp(double a)
{
while (a < 0.0) a += M_2PI;
while (a > M_2PI) a -= M_2PI;
return a;
}

void scene_init()
{
int i;

white.c[0] = white.c[1] = white.c[2] = white.c[3] = 1;
black.c[0] = black.c[1] = black.c[2] = 0;
black.c[3] = 1;

lists_init();


for (i = 0; i < nlights; i++) {
lights[i].pos = light_init_position[i];
light_rotation[i] = light_init_rotation[i];
lights_init_position(i);
}

divisions_change(possible_divisions[def_divisions_index]);

lights_init_onoff();
lights_init();
lights_init_position();

texture_init();

glClearStencil(0);

// This is for profiling
// exit(0);
}

static void scene_project()
{
glMatrixMode(GL_PROJECTION);
gluPerspective(fov, aspect, 0.01, 20.0);
gluLookAt(eyep[0], eyep[1], eyep[2], lookp[0], lookp[1], lookp[2],
1, 0, 0);
}

static void scene_rasterize()
{
int i;

glLoadName(name_square);
if (draw_square) {
if (draw_texture) glEnable(GL_TEXTURE_2D);
glCallList(list_square);
glDisable(GL_TEXTURE_2D);
}
if (draw_shadows)
for (i = 0; i < nlights; i++)
if (lights[i].on) {
glPushMatrix();
glRotatef(-light_rotation[i], 0, 0, 1);
glCallList(lists_shadows + i);
glPopMatrix();
}
if (draw_refraction)
for (i = 0; i < nlights; i++)
if (lights[i].on) {
glPushMatrix();
glRotatef(-light_rotation[i], 0, 0, 1);
glCallList(lists_refraction + i);
glPopMatrix();
}

glLoadName(name_sphere);
/* Drawing the sphere here makes the sphere visible through itself when we
* do the refraction redraw hack -- for now, just don't draw it */
// if (draw_sphere) glCallList(list_sphere);

for (i = 0; i < nlights; i++)
if (draw_lights) glCallList(lists_lights + i);
}

/* This draws an image of the scene seen through the sphere */
static void scene_draw_refracted()
{
int i;

if (!draw_sphere) return;


/* Draw an image of the sphere into the stencil plane -
* must do this every time in case the lights have moved in front
* of it */
glEnable(GL_STENCIL_TEST);
glClearStencil(0);
glClear(GL_STENCIL_BUFFER_BIT);
glStencilFunc(GL_ALWAYS, 0x1, 0x1);
glStencilOp(GL_REPLACE, GL_KEEP, GL_REPLACE);

glColorMask(0, 0, 0, 0);

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
scene_project();

glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glCallList(list_sphere);
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);

glColorMask(1, 1, 1, 1);


/* Set up a transform with a wider field of view - this is inaccurate
* but I don't have time to do it right */
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(fov * index, aspect, 0.01, 20.0);
gluLookAt(eyep[0], eyep[1], eyep[2], lookp[0], lookp[1], lookp[2],
1, 0, 0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();


/* Set up the stencil stuff which will be used to draw the image */
glStencilFunc(GL_NOTEQUAL, 0x0, 0xffffffff);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);


/* Draw the image, gambling that we'll never see anything but the
* floor through the table */
glLoadName(name_sphere);
if (draw_texture) glEnable(GL_TEXTURE_2D);
if (draw_square) glCallList(list_square);
if (draw_shadows)
for (i = 0; i < nlights; i++)
if (lights[i].on) {
glPushMatrix();
glRotatef(-light_rotation[i], 0, 0, 1);
glCallList(lists_shadows + i);
glPopMatrix();
}
if (draw_refraction)
for (i = 0; i < nlights; i++)
if (lights[i].on) {
glPushMatrix();
glRotatef(-light_rotation[i], 0, 0, 1);
glCallList(lists_refraction + i);
glPopMatrix();
}
glDisable(GL_TEXTURE_2D);


/* Draw the sphere to make it look like it
* has some substance */
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
scene_project();
glCallList(list_spheredisk);

glDisable(GL_STENCIL_TEST);
}


void scene_draw()
{
glMatrixMode(GL_PROJECTION);
glLoadIdentity();

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

scene_project();

/* Should draw an image of the square into the stencil buffer
* to make sure that refractions which are not on the square do not get
* drawn, but it can wait. */

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
scene_rasterize();

scene_draw_refracted();

}

const int pick_maxz = 0xffffffff;

int scene_pick(GLdouble x, GLdouble y)
{
GLuint buffer[128];
GLint vp[4], nhits, nnames;
GLuint minz, hit = name_background;
GLint i, j;

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

glMatrixMode(GL_PROJECTION);
glLoadIdentity();

glGetIntegerv(GL_VIEWPORT, vp);

glSelectBuffer(128, buffer);
glRenderMode(GL_SELECT);

// Where is this supposed to go, anyway?
gluPickMatrix(x, vp[3] - y, 1, 1, vp);

scene_project();

glMatrixMode(GL_MODELVIEW);

glInitNames();
glPushName(name_background);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
scene_rasterize();
glFlush();
nhits = glRenderMode(GL_RENDER);

minz = pick_maxz;
for (i = j = 0; j < nhits; j++) {
nnames = buffer[i];
i++;
if (buffer[i] < minz) {
minz = buffer[i];
hit = buffer[i + 1 + nnames];
}
i++;
i += nnames + 1;
}
if (minz == pick_maxz) return name_background;
else return hit;
}

void scene_reset_lights()
{
int i;
for (i = 0; i < nlights; i++) {
lights[i].pos = light_init_position[i];
light_rotation[i] = light_init_rotation[i];
}
lights_init_position();
lights_list_init();
}

static void square_list_init()
{
GLfloat x, y, inc;
int i, j;
glNewList(list_square, GL_COMPILE);
glLoadName(name_square);
glNormal3f(0, 0, 1);
glEnable(GL_LIGHTING);
glCallList(list_lights_on);
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, square_ambient);
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, square_diffuse);
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, square_specular);
inc = (GLfloat) (10.0 / diskdiv);
glEnable(GL_CULL_FACE);

for (i = 0, y = -5.0; i < diskdiv; i++, y += inc) {
glBegin(GL_TRIANGLE_STRIP);
for (j = 0, x = -5.0; j <= diskdiv; j++, x += inc) {
glTexCoord2f(x, y + inc);
glVertex2f(x, y + inc);
glTexCoord2f(x, y);
glVertex2f(x, y);
}
glEnd();
}

glDisable(GL_CULL_FACE);
glCallList(list_lights_off);
glDisable(GL_LIGHTING);
glEndList();
}

static void spheredisk_list_init()
{
glNewList(list_spheredisk, GL_COMPILE);
glEnable(GL_BLEND);
glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA);
glEnable(GL_LIGHTING);
glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);
glEnable(GL_COLOR_MATERIAL);
glMaterialfv(GL_AMBIENT, GL_FRONT_AND_BACK, sphere_ambient);
glMaterialfv(GL_SPECULAR, GL_FRONT_AND_BACK, sphere_specular);
glCallList(list_lights_on);
sphere_disk.draw();
glCallList(list_lights_off);
glDisable(GL_COLOR_MATERIAL);
glDisable(GL_LIGHTING);
glDisable(GL_BLEND);
glEndList();
}

void lights_onoff(int light, int val)
{
lights[light].on = val;
lights_init_onoff();
lights_list_init(light);
square_list_init();
}

void refraction_change(GLfloat refraction)
{
if (refraction == index) return;
index = refraction;
shadow_refraction_full_build();
refraction_list_init();
}

void divisions_change(int divisions)
{
Point eye, look;

if (divisions != spherediv) {
spherediv = divisions;

light_draw_list_init();
lights_list_init();

sphere_disk.set_divisions(spherediv, spherediv);
sphere_disk.fill_points();
sphere_disk.set_colors(white);
sphere_disk.scale_alpha_by_z();
eye = eyep;
look = lookp;
sphere_disk.face_direction((eye - look).unit());
sphere_disk.copy_normals_from_points();
sphere_disk.scale_translate(sphere_size, sphere_position);
sphere_build();
sphere_list_init();

diskdiv = divisions;
disk_build();
shadow_refraction_full_build();
square_list_init();
spheredisk_list_init();
shadow_list_init();
refraction_list_init();
}
}

int scene_move(int name, float dr, float dphi, float dtheta, int update)
{
switch(name) {
case name_background:
return 0;
case name_square:
return 0;
case name_sphere:
return 0;
default:
if (name < name_lights || name > name_lights + nlights) return 0;
return lights_move(name - name_lights, dr, dphi, dtheta, update);
}
}

void scene_move_update(int name, int dr, int dphi, int dtheta)
{
switch(name) {
case name_background:
break;
case name_square:
break;
case name_sphere:
break;
default:
if (name < name_lights || name > name_lights + nlights) break;
lights_move_update(name - name_lights, dr, dphi, dtheta);
break;
}
}

#ifdef MYDEBUG
void lights_init_onoff()
#else
static void lights_init_onoff()
#endif
{
int i;

glNewList(list_lights_on, GL_COMPILE);
for (i = 0; i < nlights; i++)
if (lights[i].on) glEnable(GL_LIGHT0 + i);
else glDisable(GL_LIGHT0 + i);
glEndList();

glNewList(list_lights_off, GL_COMPILE);
for (i = 0; i < nlights; i++) glDisable(GL_LIGHT0 + i);
glEndList();
}


#ifdef MYDEBUG
void lights_init_position()
#else
static void lights_init_position()
#endif
{
int i;

for (i = 0; i < nlights; i++) lights_init_position(i);

}

static void lights_init_position(int i)
{
Point l, d;

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glMatrixMode(GL_PROJECTION);
glLoadIdentity();

l = lights[i].pos;
l.pt[0] = (GLfloat)(lights[i].pos.pt[0] * cos((double)radians(light_rotation[i])));
l.pt[1] = (GLfloat)(lights[i].pos.pt[0] * -sin((double)radians(light_rotation[i])));
d = (sphere_position - l).unit();
glLightfv(GL_LIGHT0 + i, GL_POSITION, l.pt);
glLightfv(GL_LIGHT0 + i, GL_SPOT_DIRECTION, d.pt);
}

static void lights_list_init()
{
int i;
for (i = 0; i < nlights; i++) lights_list_init(i);
}

static void lights_list_init(int n)
{
Color c;

glNewList(lists_lights + n, GL_COMPILE);
if (lights[n].on) {
glLoadName(name_lights + n);

glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 1);
glCallList(list_lights_on);

c = lights[n].diffuse;
glMaterialfv(GL_BACK, GL_AMBIENT, c.c);
glMaterialfv(GL_BACK, GL_DIFFUSE, black.c);
glMaterialfv(GL_BACK, GL_SPECULAR, black.c);

glMaterialfv(GL_FRONT, GL_AMBIENT, (c * .75).c);
glMaterialfv(GL_FRONT, GL_DIFFUSE, white.c);
glMaterialfv(GL_FRONT, GL_SPECULAR, white.c);

glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glRotatef(-light_rotation[n], 0, 0, 1);
glTranslatef(lights[n].pos.pt[0], lights[n].pos.pt[1],
lights[n].pos.pt[2]);
glRotatef((GLfloat)-degrees(atan2((double)(lights[n].pos.pt[2] - sphere_position.pt[2]),
(double)(lights[n].pos.pt[0]))), 0, 1, 0);
glCallList(list_light_draw);
glPopMatrix();

glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 0);
glDisable(GL_LIGHTING);
glCallList(list_lights_off);
glDisable(GL_DEPTH_TEST);
} else {
/* 5.0.1 for Elans seems to object strongly to replacing
* empty display lists, so will put some stupid command
* in here */
glColor3f(0, 0, 0);
}
glEndList();
}

static void light_draw_list_init()
{
float c, s;
int t;

glNewList(list_light_draw, GL_COMPILE);
glEnable(GL_NORMALIZE);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glScalef(.25, .15, .15);
glBegin(GL_QUAD_STRIP);
for (t = 0; t <= spherediv; t++) {
c = (float) cos((double)(M_2PI * (float)t / (float)spherediv));
s = (float) sin((double)(M_2PI * (float)t / (float)spherediv));
glNormal3f((GLfloat).25, (GLfloat)(.968*s), (GLfloat)(.968*c));
glVertex3f((GLfloat)0, (GLfloat)s, (GLfloat)c);
glVertex3f((GLfloat)1, (GLfloat)(.75*s), (GLfloat)(.75*c));
}
glEnd();
glNormal3f(1, 0, 0);
glBegin(GL_TRIANGLE_STRIP);
for (t = 0; t <= spherediv; t++) {
c = (float)cos((double)(M_2PI * (float)t / (float)spherediv));
s = (float)sin((double)(M_2PI * (float)t / (float)spherediv));
glVertex3f((GLfloat)1, (GLfloat)(.75*s), (GLfloat)(.75*c));
glVertex3f(1, 0, 0);
}
glEnd();
glPopMatrix();
glDisable(GL_NORMALIZE);
glEndList();
}


#ifdef MYDEBUG
void lights_init()
#else
static void lights_init()
#endif
{
int i;

for (i = 0; i < nlights; i++) {
glLightfv(GL_LIGHT0 + i, GL_DIFFUSE, lights[i].diffuse);
glLightfv(GL_LIGHT0 + i, GL_SPECULAR, black.c);
glLightfv(GL_LIGHT0 + i, GL_AMBIENT, black.c);
glLightf(GL_LIGHT0 + i, GL_SPOT_EXPONENT, 4);
glLightf(GL_LIGHT0 + i, GL_SPOT_CUTOFF, 90);
}

glLightfv(GL_LIGHT0 + nlights, GL_DIFFUSE, black.c);
glLightfv(GL_LIGHT0 + nlights, GL_SPECULAR, black.c);
glLightfv(GL_LIGHT0 + nlights, GL_AMBIENT, world_ambient.c);
glEnable(GL_LIGHT0 + nlights);

/* GL_LIGHT0 + nlights + 1 willl eventually be used to draw the
* refractions - stay tuned. */
glLightfv(GL_LIGHT0 + nlights + 1, GL_DIFFUSE, black.c);
glLightfv(GL_LIGHT0 + nlights + 1, GL_SPECULAR, black.c);
glLightfv(GL_LIGHT0 + nlights + 1, GL_AMBIENT, white.c);
}

#ifdef MYDEBUG
int lights_move(int light, float dr, float dphi, float dtheta,
#else
static int lights_move(int light, float dr, float dphi, float dtheta,

#endif
int update)
{
float cphi, sphi, x, y;
Point l, dl;

if (!(dr || dphi || dtheta)) return 0;

l = lights[light].pos - sphere_position;

if (dr) {
dl = l + l*dr;
if (dl.mag() > sphere_size) l = dl;
}

if (dphi) {
cphi = (float)cos((double)dphi);
sphi = (float)sin((double)dphi);
y = -l.pt[0]*sphi + l.pt[2]*cphi;

/* This hack keeps with light from getting below the sphere -
* the projection sections would completely freak if this ever
* happened - sphere_size is multiplied by two as a fudge factor*/
if (y < 2.0*sphere_size) {
dphi = (float)atan2((double)(l.pt[2] - 2.0*sphere_size), (double)l.pt[0]);
cphi = (float)cos((double)dphi);
sphi = (float)sin((double)dphi);

}
x = l.pt[0];
l.pt[0] = x*cphi + l.pt[2]*sphi;
l.pt[2] = -x*sphi + l.pt[2]*cphi;
}

if (dtheta) {
light_rotation[light] += (GLfloat)degrees((double)dtheta);
light_rotation[light] = (GLfloat)degrees_clamp((double)light_rotation[light]);
}

lights[light].pos = l + sphere_position;
lights[light].pos.pt[3] = 1;

lights_init_position(light);
lights_list_init(light);

if (update) lights_move_update(light, dr ? 1 : 0, dphi ? 1 : 0,
dtheta ? 1 : 0);
return 1;
}

#ifdef MYDEBUG
void lights_move_update(int light, int dr, int dphi,
#else
static void lights_move_update(int light, int dr, int dphi,
#endif
int dtheta)
{
if (dr) {
disk_build(light);
shadow_refraction_full_build(light);
shadow_list_init(light);
refraction_list_init(light);
} else if (dphi) {
shadow_refraction_full_build(light);
shadow_list_init(light);
refraction_list_init(light);
} else if (dtheta) {
}

}



#ifdef MYDEBUG
int get_lists(int size)
#else
static int get_lists(int size)
#endif
{
int i;
i = glGenLists(size);
if (size && !i) {
//fprintf(stderr, "Unable to allocate %d display lists.\n");
exit(1);
}
return i;
}

#ifdef MYDEBUG
void lists_init()
#else
static void lists_init()
#endif
{
list_square = get_lists(1);
lists_shadows = get_lists(nlights);
lists_refraction = get_lists(nlights);
lists_lights = get_lists(nlights);
list_sphere = get_lists(1);
list_spheredisk = get_lists(1);
list_lights_on = get_lists(1);
list_lights_off = get_lists(1);
list_light_draw = get_lists(1);
// sphere_build();
}

static inline int sphere_npoints()
{
return (spherediv+1)*spherediv*3;
}

void sphere_build()
{
int nspherepts;
int r, t, index;
float c, s;

delete spherepts;
nspherepts = sphere_npoints();
if (nspherepts == 0) return;
spherepts = new GLfloat[nspherepts];

index = 0;
for (r = 0; r <= spherediv; r++) {
spherepts[index++] = (GLfloat)sin((double)(M_PI * (float)r / (float)spherediv));
spherepts[index++] = 0;
spherepts[index++] = (GLfloat)-cos((double)(M_PI * (float)r / (float)spherediv));
}
for (t = 1; t < spherediv; t++) {
c = (float)cos((double)(2.0 * M_PI * (float)t / (float)spherediv));
s = (float)sin((double)(2.0 * M_PI * (float)t / (float)spherediv));
for (r = 0; r <= spherediv; r++) {
spherepts[index++] = c*spherepts[r*3];
spherepts[index++] = s*spherepts[r*3];
spherepts[index++] = spherepts[r*3 + 2];
}
}

}

void sphere_list_init()
{
glNewList(list_sphere, GL_COMPILE);
sphere_disk.draw_by_perimeter();
glEndList();
}

void sphere_draw()
{
int r, t, p1, p2;

for (t = 1; t < spherediv; t++) {
glBegin(GL_QUAD_STRIP);
p1 = (t - 1) * (spherediv + 1);
p2 = t * (spherediv + 1);
for (r = 0; r <= spherediv; r++, p1++, p2++) {
glNormal3fv(&spherepts[p1*3]);
glVertex3fv(&spherepts[p1*3]);
glNormal3fv(&spherepts[p2*3]);
glVertex3fv(&spherepts[p2*3]);
}
glEnd();
}

glBegin(GL_QUAD_STRIP);
p1 = (spherediv + 1) * (spherediv - 1);
p2 = 0;
for (r = 0; r <= spherediv; r++, p1++, p2++) {
glNormal3fv(&spherepts[p1*3]);
glVertex3fv(&spherepts[p1*3]);
glNormal3fv(&spherepts[p2*3]);
glVertex3fv(&spherepts[p2*3]);
}
glEnd();
}

static void disk_build()
{
int i;
for (i = 0; i < nlights; i++) disk_build(i);
}

static void disk_build(int disk)
{
Point light;
light = lights[disk].pos;

disks[disk].free_points_normals();
disks[disk].free_colors();

disks[disk].set_divisions(diskdiv, diskdiv);
disks[disk].set_angle((float)(2.0 *
acos((double)(sphere_size / light.dist(sphere_position)))));
disks[disk].fill_points();
}

static void shadow_list_init()
{
int i;
for (i = 0; i < nlights; i++) shadow_list_init(i);
}

static void shadow_list_init(int n)
{
Color c(square_ambient[0], square_ambient[1], square_ambient[2]);

c *= world_ambient;

glNewList(lists_shadows + n, GL_COMPILE);
glColorMask(lights[n].shadow_mask[0], lights[n].shadow_mask[1],
lights[n].shadow_mask[2], lights[n].shadow_mask[3]);
glDisable(GL_DEPTH_TEST);
glColor3fv(c.c);
shadows[n].draw_by_perimeter(0, 0, 1);
glColorMask(1, 1, 1, 1);
glEndList();
}

static void refraction_list_init()
{
int i;
for (i = 0; i < nlights; i++) refraction_list_init(i);
}

static void refraction_list_init(int n) {
/* This could be loads simpler if it weren't for the texture mapping -
* that's where all this weirdness with GL_LIGHT0 + nlights + 1 comes
* in */
glNewList(lists_refraction + n, GL_COMPILE);

glEnable(GL_LIGHTING);
glCallList(list_lights_off);
/* This is white ambient light */
glEnable(GL_LIGHT0 + nlights + 1);
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, black.c);
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, black.c);
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT);
glEnable(GL_COLOR_MATERIAL);

glBlendFunc(GL_ONE, GL_ONE);
glEnable(GL_BLEND);

glDisable(GL_DEPTH_TEST);
refraction[n].draw();

glDisable(GL_BLEND);

glDisable(GL_COLOR_MATERIAL);
glDisable(GL_LIGHT0 + nlights + 1);
glDisable(GL_LIGHTING);

glEndList();
}

static void shadow_refraction_full_build()
{
int i;
for (i = 0; i < nlights; i++) shadow_refraction_full_build(i);
}

/* This entire function is written a bit oddly... */
static void shadow_refraction_full_build(int n)
{
Color c;
float dist_light;
Point dlight, zaxis;

/* Make sure that we're starting over from scratch */
shadows[n].free_points_normals();
shadows[n].free_colors();
refraction[n].free_points_normals();
refraction[n].free_colors();

dlight = lights[n].pos - sphere_position;
dist_light = dlight.mag();
dlight.unitize();
zaxis.pt[0] = 0;
zaxis.pt[1] = 0;
zaxis.pt[2] = 1;

shadows[n].set_divisions(disks[n].get_rdivisions(),
disks[n].get_tdivisions());
refraction[n].set_divisions(disks[n].get_rdivisions(),
disks[n].get_tdivisions());

shadows[n].alloc_points();
shadows[n].face_direction(dlight, disks[n]);
shadows[n].scale_translate(sphere_size, sphere_position);

c = square_diffuse;
c *= lights[n].diffuse;

refraction[n].copy_points(disks[n]);
refraction[n].set_colors(c);
refraction[n].scale_colors_by_z();

refraction[n].scale(sphere_size);
refraction[n].refract_normals(zaxis * dist_light, index);
refraction[n].face_direction(dlight);

refraction[n].project_borrow_points(shadows[n]);
refraction[n].free_normals();
shadows[n].project(lights[n].pos);
if (index != 1.0) refraction[n].scale_colors_by_darea(shadows[n]);
}

int scene_load_texture(char *texfile)
{
#ifdef TEXTURE
teximage = auxRGBImageLoad(texfile);
#else
teximage = NULL;
#endif

if (teximage == NULL) return 0;
else return 1;
}

void texture_init()
{
if (teximage == NULL) return;

gluBuild2DMipmaps(GL_TEXTURE_2D, 3, teximage->sizeX, teximage->sizeY,
GL_RGB, GL_UNSIGNED_BYTE, teximage->data);

glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glRotatef(90, 0, 0, 1);
/* This scales the texture so that it fits on the square */
glTranslatef(.5, .5, 0);
glScalef(2, 2, 1);
glMatrixMode(GL_MODELVIEW);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
GL_NEAREST_MIPMAP_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
GL_LINEAR);
}