#include #include #include #include /* #ifdef X11 #include extern "C" { #include }; #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() { TK_RGBImageRec *teximage = NULL; teximage = tkRGBImageLoad((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 = tkRGBImageLoad((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 = tkRGBImageLoad((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 = tkRGBImageLoad((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); }