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"

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

#include <GL/glu.h>
#include <GL/glaux.h>

/*
#ifdef X11
#include <GL/glx.h>
extern "C" {
#include <tk.h>
};
#endif
*/

#ifdef WIN32
#include "stonehen.h"
#endif

#include "Point.h"
#include "Ring.h"
#include "Roundwal.h"
#include "Ellipse.h"
#include "Telescop.h"

#define SCENE_EXTERN
#include "scene.h"

GLfloat mat_view[16];
GLfloat view_rotx = 0;
GLfloat fov = 45.0, aspect = 1.0;
static Point eyep = {0, 0, .5};
static Point lookp = {0.05, 1, .25};

TimeDate current_time;

static int list_ground;
static int list_texture_ground;
static int list_trees;
static int list_texture_trees;
static int list_ring;
static int list_ellipse;
static int list_texture_stones;
static int list_shadows;
static int list_telescope;
static int list_texture_telescope;
int draw_ground = 1;
int draw_trees = 0;
int draw_ring = 1;
int draw_ellipse = 1;
int draw_shadows = 0;

int use_lighting = 1;
int use_textures = 0;
int texture_hack = 0;//HACK HACK HACK - only texture map the stone
int use_normal_fog = 0;
int use_fancy_fog = 0;
int use_telescope = 0;
int use_antialias = 0;

static void scene_identity();
static void scene_project(GLfloat f = fov, float dx = 0, float dy = 0);
static void scene_draw(int rend = 1);
static void scene_render_telescope();

static void draw_background();

Point sun_position = {0., .707, .707, 0.};
Color ambient(.25, .25, .25, 1.);
static void lights_init();

static void lists_init();

static void ground_list_init();
static void ground_draw();

Roundwall trees;
static void trees_list_init();
static void trees_draw();

Ring ring;
static void ring_list_init();
static void ring_draw();

EllipseSt ellipse;
static void ellipse_list_init();
static void ellipse_draw();

static void shadows_list_init();
static void shadows_draw();

Weather weather;

Telescope telescope;
GLfloat magnif = .5;
static void telescope_list_init();
static void telescope_draw();

/* Read the back buffer into the accum buffer, adding in the appropriate
* alpha values */
static void fog_read_image();
/* Add the accum buffer to the back buffer */
static void fog_write_image();


inline float clamp(float x, float min, float max)
{
if (x < min) return min;
else if (x > max) return max;
else return x;
}

void scene_init()
{

scene_identity();
scene_viewer_center();

glEnable(GL_CULL_FACE);
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);


glEnable(GL_NORMALIZE);

/* Initial time will be four in the afternoon */
scene_set_time(TimeDate(16, 0));
scene_set_weather(weathers[def_weather_index]);

lights_init();
lists_init();
}


inline double time_minutes(int hour, int minute, int second)
{
return (double)hour*60.0 + (double)minute + (double)second / 60.0;
}

static void scene_time_changed()
{
sun_position = current_time.sun_direction();

weather.apply(sun_position);

lights_init();
shadows_list_init();
}

void scene_set_time(TimeDate t)
{
current_time = t;
scene_time_changed();
}

void scene_inc_time(TimeDate t)
{
current_time += t;
scene_time_changed();
}

/* This is a hack -- has to be called several times to get the antialiasing
* to work */
static void scene_inner_render(GLfloat dx = 0, GLfloat dy = 0)
{
/* This draws layered fog if the use_fancy_fog flag is on --
* it's going to be slow on anything but high-end stuff */
if (use_fancy_fog && weather.fog_density != 0.) {
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

glEnable(GL_FOG);
draw_background();
scene_project(fov, dx, dy);
glClear(GL_DEPTH_BUFFER_BIT);
if (use_lighting) glEnable(GL_LIGHTING);
scene_draw();
if (use_lighting) glDisable(GL_LIGHTING);
fog_read_image();
glDisable(GL_FOG);
} else
if (use_normal_fog && weather.fog_density != 0.) glEnable(GL_FOG);
else glDisable(GL_FOG);

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

/* This is the part where we actually draw the image */
glClear(GL_DEPTH_BUFFER_BIT);
draw_background();
scene_project(fov, dx, dy);
glClear(GL_DEPTH_BUFFER_BIT);
if (use_lighting) glEnable(GL_LIGHTING);
scene_draw();
if (use_lighting) glDisable(GL_LIGHTING);

if (use_fancy_fog && weather.fog_density != 0.) {
fog_write_image();
}

if (use_telescope) scene_render_telescope();
}

void scene_render()
{
GLint vp[4];

scene_inner_render();

if (!use_antialias) return;

if (use_fancy_fog) {
//fprintf(stderr, "Cannot antialias while using fancy fog.\n");
return;
}


glGetIntegerv(GL_VIEWPORT, vp);
glAccum(GL_LOAD, .5);

scene_inner_render(2. / (float)vp[2], 2. / (float)vp[3]);
glAccum(GL_ACCUM, .5);

/*
scene_inner_render(-2. / (float)vp[2], -2. / (float)vp[3]);
glAccum(GL_ACCUM, .25);
*/
glAccum(GL_RETURN, 1);
/*
glDrawBuffer(GL_BACK);
glFlush();
*/
}

static void scene_render_telescope()
{
telescope.draw_setup(fov, aspect);

/* Don't fog the telescope - moisture makes it rust.
* Seriously, it's in a strange coordinate system and fog will look
* bad on it. */
glPushAttrib(GL_ENABLE_BIT);
glDisable(GL_FOG);
if (use_textures) {
glCallList(list_texture_telescope);
glEnable(GL_TEXTURE_2D);
}
glCallList(list_telescope);
glPopAttrib();

if (use_lighting) glEnable(GL_LIGHTING);
glEnable(GL_STENCIL_TEST);
glClearStencil(0);
glClear(GL_STENCIL_BUFFER_BIT);
glStencilFunc(GL_ALWAYS, 0x1, 0x1);
glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);

glDisable(GL_CULL_FACE);
telescope.draw_lens();
glEnable(GL_CULL_FACE);

telescope.draw_takedown();

if (use_lighting) glDisable(GL_LIGHTING);

glStencilFunc(GL_NOTEQUAL, 0x0, 0xffffffff);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);

scene_identity();

draw_background();

glMatrixMode(GL_PROJECTION);
glTranslatef(telescope.xpos / magnif, -telescope.ypos / magnif, 0);
scene_project(fov * magnif);

glClear(GL_DEPTH_BUFFER_BIT);

/* Pushing the lighting bit used to do really bad things, but
* hopefully they've all gone away */
glPushAttrib(GL_LIGHTING_BIT);
lights_init();
if (use_lighting) glEnable(GL_LIGHTING);
scene_draw();
if (use_lighting) glDisable(GL_LIGHTING);
glPopAttrib();

glDisable(GL_STENCIL_TEST);
}

static void scene_identity()
{
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}

static void scene_project(GLfloat f, float dx, float dy)
{
glMatrixMode(GL_PROJECTION);
glOrtho(-1 - dx, 1, -1 - dy, 1, 0, -1);
gluPerspective(f, aspect, 0.01, 40.0);
glMatrixMode(GL_MODELVIEW);
glRotatef(view_rotx, 1, 0, 0);
gluLookAt(eyep.pt[0], eyep.pt[1], eyep.pt[2],
lookp.pt[0], lookp.pt[1], lookp.pt[2],
0, 0, 1);
glMultMatrixf(mat_view);
lights_init();
}

/* scene_draw() just draws the geometry - it's used for rendering and
* picking. */
static void scene_draw(int rend)
{
if (draw_ground) {
if (rend) {
if (use_textures) {
glCallList(list_texture_ground);
glEnable(GL_TEXTURE_2D);
} else glEnable(GL_COLOR_MATERIAL);
}
glCallList(list_ground);

if (rend) {
glDisable(GL_TEXTURE_2D);
glDisable(GL_COLOR_MATERIAL);
}
}

if (draw_shadows) {
if (use_textures && rend && !draw_ground) {
glCallList(list_texture_ground);
glEnable(GL_TEXTURE_2D);
}
glCallList(list_shadows);
if (use_textures && rend) glDisable(GL_TEXTURE_2D);
}

if (draw_trees) {
if (use_textures && rend) {
glCallList(list_texture_trees);
glEnable(GL_TEXTURE_2D);
}
glCallList(list_trees);
if (use_textures && rend) glDisable(GL_TEXTURE_2D);
}

glClear(GL_DEPTH_BUFFER_BIT);

if (draw_ring) {
if (rend) {
if (use_textures || texture_hack) {
glCallList(list_texture_stones);
glEnable(GL_TEXTURE_2D);
}
glEnable(GL_COLOR_MATERIAL);
glColor3f(.5, .5, .5);
}
glCallList(list_ring);
if (rend) {
if (use_textures || texture_hack) glDisable(GL_TEXTURE_2D);
glDisable(GL_COLOR_MATERIAL);
}
}

if (draw_ellipse) {
if (use_textures && rend) {
// Hack to avoid doing something expensive twice in a row
if (!draw_ring) glCallList(list_texture_stones);
glEnable(GL_TEXTURE_2D);
}
glCallList(list_ellipse);
if (use_textures && rend) glDisable(GL_TEXTURE_2D);
}
}

static void draw_background()
{
weather.draw_sky(sun_position);
}

void scene_viewer_center()
{
glPushMatrix();
glLoadIdentity();
glGetFloatv(GL_MODELVIEW_MATRIX, mat_view);
glPopMatrix();
view_rotx = 0;
}

void scene_viewer_rotate_worldz(GLfloat degrees)
{
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glRotatef(degrees, 0, 0, 1);
glMultMatrixf(mat_view);
glGetFloatv(GL_MODELVIEW_MATRIX, mat_view);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
}

void scene_viewer_rotatez(GLfloat degrees)
{
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glRotatef(degrees, 0, 0, 1);
glMultMatrixf(mat_view);
glGetFloatv(GL_PROJECTION_MATRIX, mat_view);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
}

void scene_viewer_rotatex(GLfloat degrees)
{
view_rotx += degrees;
view_rotx = clamp(view_rotx, -60, 60);
scene_identity();
scene_project();
lights_init();
}

void scene_viewer_translate(GLfloat dist)
{
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glTranslatef(0, dist, 0);
glMultMatrixf(mat_view);
glGetFloatv(GL_PROJECTION_MATRIX, mat_view);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
scene_identity();
scene_project();
lights_init();
}

void scene_position_telescope(GLfloat x, GLfloat y)
{
telescope.xpos = x;
telescope.ypos = y;
}

void scene_get_position_telescope(GLfloat *x, GLfloat *y)
{
*x = telescope.xpos;
*y = telescope.ypos;
}

void scene_get_radius_telescope(GLfloat *r)
{
*r = telescope.get_radius();
}

void scene_set_weather(Weather w)
{
weather = w;
weather.apply(sun_position);
shadows_list_init();
}

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

static void lights_init()
{
glLightfv(GL_LIGHT0, GL_POSITION, sun_position.pt);

/* This light gives a diffuse coefficient when the sun is off -
* it's used for drawing shadows */
glLightfv(GL_LIGHT1, GL_AMBIENT, black.c);
glLightfv(GL_LIGHT1, GL_DIFFUSE, black.c);
glLightfv(GL_LIGHT1, GL_SPECULAR, black.c);
}

#ifdef TEXTURE
static void textures_list_init()
{
AUX_RGBImageRec *teximage = NULL;

teximage = auxRGBImageLoad((char *)texfile_stones);
glNewList(list_texture_stones, GL_COMPILE);
gluBuild2DMipmaps(GL_TEXTURE_2D, 3, teximage->sizeX, teximage->sizeY,
GL_RGB, GL_UNSIGNED_BYTE, teximage->data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
GL_NEAREST_MIPMAP_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
GL_LINEAR);
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glDisable(GL_TEXTURE_GEN_S);
glDisable(GL_TEXTURE_GEN_T);
glEndList();

/* tk is obnoxious and doesn't seem to provide any mechanism for this */
free(teximage->data);
free(teximage);

teximage = auxRGBImageLoad((char *)texfile_ground);
glNewList(list_texture_ground, GL_COMPILE);
gluBuild2DMipmaps(GL_TEXTURE_2D, 3, teximage->sizeX, teximage->sizeY,
GL_RGB, GL_UNSIGNED_BYTE, teximage->data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
GL_NEAREST_MIPMAP_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
GL_LINEAR);
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glScalef(100, 100, 1);
glMatrixMode(GL_MODELVIEW);
glDisable(GL_TEXTURE_GEN_S);
glDisable(GL_TEXTURE_GEN_T);
glEndList();

free(teximage->data);
free(teximage);

/* Figure out some way to get an alpha component out of the tk --
* otherwise we're really hosed */
teximage = auxRGBImageLoad((char *)texfile_trees);
glNewList(list_texture_trees, GL_COMPILE);
/* In the final scenerio we probably won't want to mipmap this, but it's
* not square and I don't feel like bothering to scale it */
gluBuild2DMipmaps(GL_TEXTURE_2D, 3, teximage->sizeX, teximage->sizeY,
GL_RGB, GL_UNSIGNED_BYTE, teximage->data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
GL_NEAREST_MIPMAP_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
GL_LINEAR);
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glDisable(GL_TEXTURE_GEN_S);
glDisable(GL_TEXTURE_GEN_T);
glEndList();

free(teximage->data);
free(teximage);

teximage = auxRGBImageLoad((char *)texfile_telescope);
glNewList(list_texture_telescope, GL_COMPILE);
glTexImage2D(GL_TEXTURE_2D, 0, 3, teximage->sizeX, teximage->sizeY,
0, GL_RGB, GL_UNSIGNED_BYTE, teximage->data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
glEnable(GL_TEXTURE_GEN_S);
glEnable(GL_TEXTURE_GEN_T);
glEndList();

free(teximage->data);
free(teximage);
}
#endif

static void lists_init()
{
list_ground = get_lists(1);
list_texture_ground = get_lists(1);
list_trees = get_lists(1);
list_texture_trees = get_lists(1);
list_ring = get_lists(1);
list_ellipse = get_lists(1);
list_texture_stones = get_lists(1);
list_shadows = get_lists(1);
list_telescope = get_lists(1);
list_texture_telescope = get_lists(1);

ground_list_init();
trees_list_init();
shadows_list_init();
ring_list_init();
ellipse_list_init();
#ifdef TEXTURE
textures_list_init();
#endif
telescope_list_init();
}

static void ground_list_init()
{
glNewList(list_ground, GL_COMPILE);
ground_draw();
glEndList();
}

static void ground_draw()
{
glColor3f(0, .75, 0);

glLoadName(name_ground);

glNormal3f(0, 0, 1);

glPushMatrix();
/* Making something this big would confuse the zbuffer, but we're
* clearing that AFTER drawing this, so it's ok */
glScalef(100, 100, 1);
glBegin(GL_QUADS);
glTexCoord2f(0, 0);
glVertex2f(-1, -1);
glTexCoord2f(1, 0);
glVertex2f(1, -1);
glTexCoord2f(1, 1);
glVertex2f(1, 1);
glTexCoord2f(0, 1);
glVertex2f(-1, 1);
glEnd();
glPopMatrix();
}

static void trees_list_init()
{
glNewList(list_trees, GL_COMPILE);
trees_draw();
glEndList();
}

static void trees_draw()
{
glEnable(GL_COLOR_MATERIAL);
glColor3f(0, .5, 0);

glLoadName(name_trees);

glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
trees.draw();
glDisable(GL_BLEND);

glDisable(GL_COLOR_MATERIAL);
}

static void ring_list_init()
{
glNewList(list_ring, GL_COMPILE);
ring_draw();
glEndList();
}

static void ring_draw()
{
glLoadName(name_ring);

glEnable(GL_DEPTH_TEST);
ring.erode(.1);
ring.draw();
glDisable(GL_DEPTH_TEST);
}

static void ellipse_list_init()
{
glNewList(list_ellipse, GL_COMPILE);
ellipse_draw();
glEndList();
}

static void ellipse_draw()
{
glEnable(GL_COLOR_MATERIAL);
glColor3f(.5, .5, .5);

glEnable(GL_DEPTH_TEST);

glLoadName(name_ellipse);

ellipse.erode(.1);
ellipse.draw();

glDisable(GL_DEPTH_TEST);

glDisable(GL_COLOR_MATERIAL);
}

static void shadows_list_init()
{
glNewList(list_shadows, GL_COMPILE);
shadows_draw();
glEndList();
}

static void shadows_draw()
{
Color grass(0, .75, 0);

glPushAttrib(GL_ENABLE_BIT);

/* Turn the sun off */
glDisable(GL_LIGHT0);
glEnable(GL_LIGHT1);
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, grass.c);
glColor3fv((grass * .5).c);

glDisable(GL_CULL_FACE);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
ring.draw_shadow(sun_position, weather.shadow_blur(),
grass * .5, grass);
ellipse.draw_shadow(sun_position, weather.shadow_blur(),
grass * .5, grass);
glPopAttrib();
}

static void telescope_list_init()
{
glNewList(list_telescope, GL_COMPILE);
telescope_draw();
glEndList();
}

static void telescope_draw()
{
glLoadName(name_telescope);
glEnable(GL_COLOR_MATERIAL);
glDisable(GL_CULL_FACE);
glClear(GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
telescope.draw_body();
glDisable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glDisable(GL_COLOR_MATERIAL);
}

static void fog_read_image()
{
glPushMatrix();
glLoadIdentity();

/* This creates an alpha gradient across the image */
/* glColorMask(0, 0, 0, 1);
glBegin(GL_QUADS);
glColor4f(1, 1, 1, 1);
glVertex2f(-1, -1);
glVertex2f(1, -1);
glColor4f(1, 1, 1, 0);
glVertex2f(1, 1);
glVertex2f(-1, 1);
glEnd();
glColorMask(1, 1, 1, 1);
*/
glDrawBuffer(GL_BACK);
glReadBuffer(GL_BACK);
glAccum(GL_LOAD, 1);

glPopMatrix();
}

static void fog_write_image()
{
glDrawBuffer(GL_BACK);

/* Put this back in once we're done testing */
// glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glAccum(GL_RETURN, 1);
glDisable(GL_BLEND);
}