diff options
Diffstat (limited to 'hacks/glx/gibson.c')
-rw-r--r-- | hacks/glx/gibson.c | 1335 |
1 files changed, 1335 insertions, 0 deletions
diff --git a/hacks/glx/gibson.c b/hacks/glx/gibson.c new file mode 100644 index 0000000..8f99040 --- /dev/null +++ b/hacks/glx/gibson.c @@ -0,0 +1,1335 @@ +/* gibson, Copyright (c) 2020 Jamie Zawinski <jwz@jwz.org> + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation. No representations are made about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + * + * Hacking the Gibson, as per the 1995 classic film, HACKERS. + * + * In the movie, this was primarily a practical effect: the towers were + * edge-lit etched perspex, each about four feet tall. + */ + +#define TOWER_FONT "-*-helvetica-bold-r-normal-*-*-480-*-*-*-*-*-*" + +#define DEFAULTS "*delay: 20000 \n" \ + "*groundColor: #8A2BE2" "\n" \ + "*towerColor: #4444FF" "\n" \ + "*towerText: #DDDDFF" "\n" \ + "*towerText2: #FF0000" "\n" \ + "*towerFont: " TOWER_FONT "\n" \ + "*showFPS: False \n" \ + "*wireframe: False \n" \ + +# define release_gibson 0 +#undef countof +#define countof(x) (sizeof((x))/sizeof((*x))) + +#include "xlockmore.h" +#include "colors.h" +#include "rotator.h" +#include "texfont.h" +#include <ctype.h> + +#ifdef USE_GL /* whole file */ + + +#define DEF_SPEED "1.0" +#define DEF_TEXTURE "True" +#define DEF_GRID_WIDTH "6" +#define DEF_GRID_HEIGHT "7" +#define DEF_GRID_DEPTH "6" +#define DEF_GRID_SPACING "2.0" +#define DEF_COLUMNS "5" + +#define GROUND_QUAD_SIZE 30 + +typedef struct { + GLfloat x, y, h; + GLuint fg_dlists[5], bg_dlists[5]; + int fg_polys, bg_polys; + unsigned int face_mode; /* 5 bit field */ +} tower; + +typedef struct { + GLXContext *glx_context; + Bool button_down_p; + rotator *rot, *rot2; + GLfloat xscroll, yscroll; + GLfloat oxscroll, oyscroll; + + GLuint ground_dlist; + GLuint tower_dlist; + int ground_polys, tower_polys; + GLfloat ground_y; + GLfloat billboard_y; + const char *billboard_text; + + int ntowers; + tower *towers; + GLfloat tower_color[4]; + GLfloat tower_color2[4]; + GLfloat edge_color[4]; + GLfloat bg_color[4]; + Bool startup_p; + + struct { + GLuint texid; + XCharStruct metrics; + int width, height; + texture_font_data *font_data; + int ascent, descent, em_width; + char *text; + } text[2]; + +} gibson_configuration; + +static gibson_configuration *ccs = NULL; + +static GLfloat speed; +static Bool do_tex; +static int grid_width; +static int grid_height; +static int grid_depth; +static GLfloat grid_spacing; +static int columns; + +static XrmOptionDescRec opts[] = { + { "-speed", ".speed", XrmoptionSepArg, 0 }, + { "-texture", ".texture", XrmoptionNoArg, "True" }, + { "+texture", ".texture", XrmoptionNoArg, "False" }, + { "-grid-width", ".gridWidth", XrmoptionSepArg, 0 }, + { "-grid-height", ".gridHeight", XrmoptionSepArg, 0 }, + { "-grid-depth", ".gridDepth", XrmoptionSepArg, 0 }, + { "-spacing", ".gridSpacing", XrmoptionSepArg, 0 }, + { "-columns", ".columns", XrmoptionSepArg, 0 }, +}; + +static argtype vars[] = { + {&speed, "speed", "Speed", DEF_SPEED, t_Float}, + {&do_tex, "texture", "Texture", DEF_TEXTURE, t_Bool}, + {&grid_width, "gridWidth", "GridWidth", DEF_GRID_WIDTH, t_Int}, + {&grid_height, "gridHeight", "GridHeight", DEF_GRID_HEIGHT, t_Int}, + {&grid_depth, "gridDepth", "GridDepth", DEF_GRID_DEPTH, t_Int}, + {&grid_spacing, "gridSpacing", "GridSpacing", DEF_GRID_SPACING, t_Float}, + {&columns, "columns", "Columns", DEF_COLUMNS, t_Int}, +}; + +ENTRYPOINT ModeSpecOpt gibson_opts = { + countof(opts), opts, countof(vars), vars, NULL}; + + +ENTRYPOINT void +reshape_gibson (ModeInfo *mi, int width, int height) +{ + GLfloat h = (GLfloat) height / (GLfloat) width; + int y = 0; + + glViewport (0, y, (GLint) width, (GLint) height); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); +# ifdef DEBUG + gluPerspective (30, 1/h, 1, 100); +# else + gluPerspective (100, 1/h/4, + 1.0, + 20 * grid_depth * 1.5 * (1 + grid_spacing)); +# endif + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + gluLookAt( 0, 0, 1, + 0, 0, 0, + 0, 1, 0); + + glClear(GL_COLOR_BUFFER_BIT); +} + + +/* Copied from gltrackball.c */ +static void +adjust_for_device_rotation (double *x, double *y, double *w, double *h) +{ + int rot = (int) current_device_rotation(); + int swap; + + while (rot <= -180) rot += 360; + while (rot > 180) rot -= 360; + + if (rot > 135 || rot < -135) /* 180 */ + { + *x = *w - *x; + *y = *h - *y; + } + else if (rot > 45) /* 90 */ + { + swap = *x; *x = *y; *y = swap; + swap = *w; *w = *h; *h = swap; + *x = *w - *x; + } + else if (rot < -45) /* 270 */ + { + swap = *x; *x = *y; *y = swap; + swap = *w; *w = *h; *h = swap; + *y = *h - *y; + } +} + + +ENTRYPOINT Bool +gibson_handle_event (ModeInfo *mi, XEvent *event) +{ + gibson_configuration *bp = &ccs[MI_SCREEN(mi)]; + double w = MI_WIDTH(mi); + double h = MI_HEIGHT(mi); + double xoff = 0, yoff = 0; + + if (event->xany.type == ButtonPress || + event->xany.type == ButtonRelease) + { + double x = event->xbutton.x; + double y = event->xbutton.y; + adjust_for_device_rotation (&x, &y, &w, &h); + xoff = (x / w) - 0.5; + yoff = (event->xbutton.y / h) - 0.5; + bp->button_down_p = (event->xany.type == ButtonPress); + bp->oxscroll = xoff; + bp->oyscroll = yoff; + + return True; + } + else if (event->xany.type == MotionNotify) + { + double x = event->xmotion.x; + double y = event->xmotion.y; + adjust_for_device_rotation (&x, &y, &w, &h); + xoff = (x / w) - 0.5; + yoff = (y / h) - 0.5; + if (bp->button_down_p) + { + bp->xscroll += xoff - bp->oxscroll; + bp->yscroll += yoff - bp->oyscroll; + bp->oxscroll = xoff; + bp->oyscroll = yoff; + } + return True; + } + + return False; +} + + +static void +parse_color (ModeInfo *mi, char *key, GLfloat color[4]) +{ + XColor xcolor; + char *string = get_string_resource (mi->dpy, key, "Color"); + if (!XParseColor (mi->dpy, mi->xgwa.colormap, string, &xcolor)) + { + fprintf (stderr, "%s: unparsable color in %s: %s\n", progname, + key, string); + exit (1); + } + free (string); + + color[0] = xcolor.red / 65536.0; + color[1] = xcolor.green / 65536.0; + color[2] = xcolor.blue / 65536.0; + color[3] = 1; +} + + +static int +draw_ground (ModeInfo *mi) +{ + int wire = MI_IS_WIREFRAME(mi); + int polys = 0; + int x, y; + int cells = 20; + GLfloat color[4]; + GLfloat color0[4]; + GLfloat cell_size = 1.0; + GLfloat z = -0.005; + + parse_color (mi, "groundColor", color); + parse_color (mi, "towerColor", color0); + color0[0] *= 0.05; + color0[1] *= 0.05; + color0[2] *= 0.3; + color0[3] = 1; + + if (!wire) + { + GLfloat fog_color[4] = { 0, 0, 0, 1 }; + + glFogi (GL_FOG_MODE, GL_EXP2); + glFogfv (GL_FOG_COLOR, fog_color); + glFogf (GL_FOG_DENSITY, 0.015); + glFogf (GL_FOG_START, -cells/2 * cell_size); + glEnable (GL_FOG); + } + + glPushMatrix(); + glScalef (1.0/cells, 1.0/cells, 1); + glTranslatef (-cells/2.0, -cells/2.0, 0); + glTranslatef (0.5, 0, 0); + + glBegin (GL_QUADS); /* clipping quad */ + glColor4fv (color0); + glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color0); + + glVertex3f (0, 0, z); + glVertex3f (cells * cell_size, 0, z); + glVertex3f (cells * cell_size, cells * cell_size, z); + glVertex3f (0, cells * cell_size, z); + glEnd(); + polys++; + + glColor4fv (color); + glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color); + + for (y = 0; y < cells; y++) + for (x = 0; x < cells; x++) + { + GLfloat a = 0; + GLfloat b = 1.0/3; + GLfloat c = 2.0/3; + GLfloat d = 1.0; + GLfloat w = 0.02; + + glPushMatrix(); + glTranslatef (x, y, 0); + + glNormal3f (0, 0, 1); + + switch (random() % 4) { + case 0: + glRotatef (90, 0, 0, 1); + glTranslatef (0, -1, 0); + break; + case 1: + glRotatef (-90, 0, 0, 1); + glTranslatef (-1, 0, 0); + break; + case 2: + glRotatef (180, 0, 0, 1); + glTranslatef (-1, -1, 0); + break; + default: break; + } + + switch (random() % 2) { + case 0: + glScalef (-1, -1, 1); + glTranslatef (-1, -1, 0); + break; + default: break; + } + + switch (random() % 2) { + case 0: + + glBegin (wire ? GL_LINE_LOOP : GL_QUAD_STRIP); + glVertex3f (a, b+w, 0); + glVertex3f (a, b-w, 0); polys++; + glVertex3f (b+w, a, 0); polys++; + glVertex3f (b-w, a, 0); polys++; + glEnd(); + + glBegin (wire ? GL_LINE_LOOP : GL_QUAD_STRIP); + glVertex3f (a, c+w, 0); + glVertex3f (a, c-w, 0); polys++; + glVertex3f (b+w, c+w, 0); polys++; + glVertex3f (b, c-w, 0); polys++; + glVertex3f (c+w, b+w, 0); polys++; + glVertex3f (c-w, b, 0); polys++; + glVertex3f (c+w, a, 0); polys++; + glVertex3f (c-w, a, 0); polys++; + glEnd(); + +/* + glBegin (wire ? GL_LINE_LOOP : GL_QUAD_STRIP); + glVertex3f (c+w, d, 0); + glVertex3f (c-w, d, 0); polys++; + glVertex3f (d, c+w, 0); polys++; + glVertex3f (d, c-w, 0); polys++; + glEnd(); +*/ + break; + + default: + glBegin (wire ? GL_LINE_LOOP : GL_QUAD_STRIP); + glVertex3f (a+w, d, 0); + glVertex3f (a, d, 0); polys++; + glVertex3f (a+w, d, 0); + glVertex3f (a, d-w, 0); polys++; + glVertex3f (b+w, c-w, 0); polys++; + glVertex3f (b-w, c-w, 0); polys++; + glVertex3f (b+w, a, 0); polys++; + glVertex3f (b-w, a, 0); polys++; + glEnd(); + + glBegin (wire ? GL_LINE_LOOP : GL_QUAD_STRIP); + glVertex3f (b+w, d, 0); + glVertex3f (b-w, d, 0); polys++; + glVertex3f (c+w, c-w, 0); polys++; + glVertex3f (c-w, c-w, 0); polys++; + glVertex3f (c+w, a, 0); polys++; + glVertex3f (c-w, a, 0); polys++; + glEnd(); + break; + } + + glPopMatrix(); + } + glPopMatrix(); + + if (!wire) + { + glDisable (GL_BLEND); + glDisable (GL_FOG); + } + + return polys; +} + + +/* qsort comparator for sorting towers by y position */ +static int +cmp_towers (const void *aa, const void *bb) +{ + const tower *a = (tower *) aa; + const tower *b = (tower *) bb; + return ((int) (b->y * 10000) - + (int) (a->y * 10000)); +} + + +static GLfloat +ease_fn (GLfloat r) +{ + return cos ((r/2 + 1) * M_PI) + 1; /* Smooth curve up, end at slope 1. */ +} + + +static GLfloat +ease_ratio (GLfloat r) +{ + GLfloat ease = 0.5; + if (r <= 0) return 0; + else if (r >= 1) return 1; + else if (r <= ease) return ease * ease_fn (r / ease); + else if (r > 1-ease) return 1 - ease * ease_fn ((1 - r) / ease); + else return r; +} + + +/* Draws the text quads on the face. + First pass is for the small background text, second is for the big block. + */ +static int +draw_tower_face_text (ModeInfo *mi, GLfloat height, Bool which) +{ + gibson_configuration *bp = &ccs[MI_SCREEN(mi)]; + int wire = MI_IS_WIREFRAME(mi); + int polys = 0; + Bool wire2 = False; /* Debugging quads */ + Bool bg_p = (which == 1 && do_tex && !wire); + + glColor4fv (which ? bp->tower_color2 : bp->tower_color); + glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, + which ? bp->tower_color2 : bp->tower_color); + + /* The texture is a tex_width x tex_height rectangle, of which we + only use the rbearing+lbearing x ascent+descent sub-rectangle. + Texture coordinates reference the tex_width x tex_height rectangle + as a 0.0 - 1.0 coordinate. + */ + int n = which ? 1 : 0; + GLfloat twratio = ((bp->text[n].metrics.rbearing + + bp->text[n].metrics.lbearing) / + (GLfloat) bp->text[n].width); + GLfloat thratio = ((bp->text[n].metrics.ascent + + bp->text[n].metrics.descent) / + (GLfloat) bp->text[n].height); + GLfloat aspect = ((bp->text[n].ascent + bp->text[n].descent) / + (GLfloat) bp->text[n].em_width); + + GLfloat sx = 1.0 / (which ? 1 : columns); + GLfloat sy = (which + ? height * 0.8 + : sx * 4); /* Tweaked to match gluPerspective */ + + GLfloat lines_in_tex = ((bp->text[n].metrics.ascent + + bp->text[n].metrics.descent) / + (GLfloat) + (bp->text[n].ascent + bp->text[n].descent)); + GLfloat tex_lines = (which ? 3 : 8); /* Put this many lines in each quad */ + + GLfloat tsx = sx * twratio; + GLfloat tsy = sy * thratio * tex_lines / lines_in_tex * aspect; + GLfloat x1, tx1; + GLfloat margin = 0.2; + GLfloat m2 = margin/2 / (which ? 1 : columns); + GLfloat m3 = m2 / (which ? 1 : height); + GLfloat h2 = height * (which ? 1-margin : 1); + + glBindTexture (GL_TEXTURE_2D, bp->text[n].texid); + + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + + if (!wire && !wire2 && !bg_p) glBegin (GL_QUADS); + for (x1 = 0, tx1 = 0; x1 < 1.0; x1 += sx, tx1 += tsx) + { + GLfloat x2 = x1 + sx; + GLfloat tx2 = tx1 + tsx; + GLfloat y2, ty2; + GLfloat z = (which ? 0.05 : 0); + + tx1 = 0; + tx2 = twratio; + + for (y2 = h2, ty2 = thratio; + y2 > 0; + y2 -= sy, ty2 -= tsy) + { + GLfloat y1 = y2 - sy * (1-margin); + GLfloat ty1 = ty2 - tsy; + GLfloat toff = frand ((bp->text[n].metrics.ascent + + bp->text[n].metrics.descent) + * 0.8); + + if (y1 < 0) /* Clip the panel to the bottom of the tower face */ + { + tsy = y2 / (y2 - y1); + y1 = 0; + } + + ty1 = toff; + ty2 = ty1 + tsy; + + if (wire2 && which) glColor3f (1,0,0); + if (wire || wire2 || bg_p) + glBegin (!wire && (wire2 || bg_p) ? GL_QUADS : GL_LINE_LOOP); + glTexCoord2f(tx1, ty2); glVertex3f (x1+m2, y1+m3, z); + glTexCoord2f(tx2, ty2); glVertex3f (x2-m2, y1+m3, z); + glTexCoord2f(tx2, ty1); glVertex3f (x2-m2, y2-m3, z); + glTexCoord2f(tx1, ty1); glVertex3f (x1+m2, y2-m3, z); + if (wire || wire2 || bg_p) + glEnd(); + polys++; + + if (bg_p) + { + GLfloat bg[4] = { 1, 1, 1, 0.2 }; + z -= 0.1; + m2 -= 0.03; + m3 -= 0.03; + glColor4fv (bg); + glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, bg); + } + + if ((wire2 || bg_p) && !wire) + { + if (do_tex) glDisable(GL_TEXTURE_2D); + glBegin (bg_p ? GL_QUADS : GL_LINE_LOOP); + glVertex3f (x1+m2, y1+m3, z); + glVertex3f (x2-m2, y1+m3, z); + glVertex3f (x2-m2, y2-m3, z); + glVertex3f (x1+m2, y2-m3, z); + glEnd(); + polys++; + if (do_tex) glEnable(GL_TEXTURE_2D); + } + + if (which) break; + } + } + if (!wire && !wire2 && !bg_p) glEnd(); + + return polys; +} + + +/* Draws the wall of the face, and the edges, then the text quads on it. + */ +static int +draw_tower_face (ModeInfo *mi, GLfloat height, int mode) +{ + gibson_configuration *bp = &ccs[MI_SCREEN(mi)]; + int wire = MI_IS_WIREFRAME(mi); + int polys = 0; + + switch (mode) { + case 0: + if (! wire) + { + GLfloat m = 0.015; + GLfloat z = -0.0005; + if (do_tex) glDisable (GL_TEXTURE_2D); + + glNormal3f (0, 0, 1); + + glColor4fv (bp->bg_color); + glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, + bp->bg_color); + + glBegin (GL_QUADS); + glVertex3f (0, 0, z*2); /* background */ + glVertex3f (1, 0, z*2); + glVertex3f (1, height, z*2); + glVertex3f (0, height, z*2); + polys++; + glEnd(); + + glColor4fv (bp->edge_color); + glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, + bp->edge_color); + + glBegin (GL_QUADS); + glVertex3f (0, 0, z); /* left */ + glVertex3f (m, 0, z); + glVertex3f (m, height, z); + glVertex3f (0, height, z); + polys++; + + glVertex3f (1-m, 0, 0); /* right */ + glVertex3f (1, 0, 0); + glVertex3f (1, height, z); + glVertex3f (1-m, height, z); + polys++; + + glVertex3f (m, 0, 0); /* bottom */ + glVertex3f (1-m, 0, 0); + glVertex3f (1-m, m, 0); + glVertex3f (m, m, 0); + polys++; + + glVertex3f (m, height-m, z); /* top */ + glVertex3f (1-m, height-m, z); + glVertex3f (1-m, height, z); + glVertex3f (m, height, z); + polys++; + glEnd(); + + if (do_tex) glEnable (GL_TEXTURE_2D); + } + break; + case 1: + polys += draw_tower_face_text (mi, height, 0); + break; + case 2: + polys += draw_tower_face_text (mi, height * 0.7, 1); + break; + default: + abort(); + break; + } + + return polys; +} + + +/* Mode 0: draws 5 sides of the box + Mode 1: just background text + Mode 2: just foreground text + */ +static int +draw_tower (ModeInfo *mi, tower *t, int mode, int face) +{ + GLfloat height = grid_height; + + int polys = 0; + + glPushMatrix(); + glTranslatef (-0.5, 0.5, 0); + + if (face == 0 || face == -1) + { + glPushMatrix(); /* top */ + glTranslatef (0, 0, height); + polys += draw_tower_face (mi, 1.0, mode); + glPopMatrix(); + } + + if (face == 1 || face == -1) + { + glPushMatrix(); /* left */ + glRotatef ( 90, 1, 0, 0); + glRotatef (-90, 0, 1, 0); + glTranslatef (-1, 0, 0); + polys += draw_tower_face (mi, height, mode); + glPopMatrix(); + } + + if (face == 2 || face == -1) + { + glPushMatrix(); /* back */ + glRotatef ( 90, 1, 0, 0); + glRotatef (180, 0, 1, 0); + glTranslatef (-1, 0, 1); + polys += draw_tower_face (mi, height, mode); + glPopMatrix(); + } + + if (face == 3 || face == -1) + { + glPushMatrix(); /* right */ + glRotatef ( 90, 1, 0, 0); + glRotatef ( 90, 0, 1, 0); + glTranslatef (0, 0, 1); + polys += draw_tower_face (mi, height, mode); + glPopMatrix(); + } + + if (face == 4 || face == -1) + { + glPushMatrix(); /* front */ + glRotatef ( 90, 1, 0, 0); + polys += draw_tower_face (mi, height, mode); + glPopMatrix(); + } + + if (face < -1 || face > 4) abort(); + + glPopMatrix(); + return polys; +} + + +static void +animate_towers (ModeInfo *mi) +{ + gibson_configuration *bp = &ccs[MI_SCREEN(mi)]; + int ii; + GLfloat min = -3; + GLfloat max = grid_depth * (1 + grid_spacing) - grid_spacing - 1; + GLfloat yspeed = speed * 0.05; + + for (ii = 0; ii < 20; ii++) + { + int jj, kk; + + /* randomly trade two towers' fg dlists */ + if (0 == (random() % 20)) + { + int i = random() % bp->ntowers; + int j = random() % bp->ntowers; + int k = random() % countof(bp->towers[i].fg_dlists); + GLuint d1 = bp->towers[i].bg_dlists[k]; + GLuint d2 = bp->towers[j].bg_dlists[k]; + bp->towers[i].bg_dlists[k] = d2; + bp->towers[j].bg_dlists[k] = d1; + } + + /* randomly trade two towers' bg dlists */ + if (1) /* (0 == (random() % 3)) */ + { + int i = random() % bp->ntowers; + int j = random() % bp->ntowers; + int k = random() % countof(bp->towers[i].fg_dlists); + GLuint d1 = bp->towers[i].fg_dlists[k]; + GLuint d2 = bp->towers[j].fg_dlists[k]; + bp->towers[i].fg_dlists[k] = d2; + bp->towers[j].fg_dlists[k] = d1; + } + + /* Randomize whether it's displaying fg text or bg text */ + for (jj = 0; jj < bp->ntowers; jj++) + for (kk = 0; kk < countof(bp->towers[jj].fg_dlists); kk++) + { + /* Re-choose every N frames. Display fg 1 in M. */ + int frames = 500; + int fg_chance = (kk == 0 ? 100000 : 10); + unsigned int o = !!(bp->towers[jj].face_mode & (1 << kk)); + unsigned int n = !!((random() % frames) ? o : + (0 == (random() % fg_chance))); + bp->towers[jj].face_mode = + ((bp->towers[jj].face_mode & ~(1 << kk)) | (n << kk)); + } + } + + for (ii = 0; ii < bp->ntowers; ii++) + { + tower *t = &bp->towers[ii]; + t->h += speed * 0.01; + if (t->h > 1) t->h = 1; + + t->y -= yspeed; + + if (t->y < min) + { + t->h = 0; + t->y = max; + } + } + + /* Sorting by depth improves frame rate slightly. */ + qsort (bp->towers, bp->ntowers, sizeof(*bp->towers), cmp_towers); + + bp->ground_y -= yspeed / GROUND_QUAD_SIZE; + if (bp->ground_y < 1) + bp->ground_y += 1; + + bp->billboard_y -= yspeed; + if (bp->billboard_y < min || !bp->billboard_text) + { + const char *const ss[] = { + "ACCESS GRANTED", + "ACCESS GRANTED", + "ACCESS DENIED", + "ACCESS DENIED", + "ACCESS DENIED", + "ACCESS DENIED", + "ACCESS DENIED", + "PASSWORD ACCEPTED", + " GIVE ME\nA COOKIE", + "MESS WITH THE BEST\n DIE LIKE THE REST", + }; + + bp->billboard_y = max * (1 + frand(8)); + bp->billboard_text = ss[random() % countof(ss)]; + } +} + + +static int +draw_billboard (ModeInfo *mi) +{ + gibson_configuration *bp = &ccs[MI_SCREEN(mi)]; + int polys = 0; + int wire = MI_IS_WIREFRAME(mi); + GLfloat w, h, s, margin, margin2; + XCharStruct metrics; + int ascent, descent; + texture_font_data *font = bp->text[1].font_data; + GLfloat color[4]; + GLfloat y = grid_height * 0.3; + + texture_string_metrics (font, bp->billboard_text, + &metrics, &ascent, &descent); + w = metrics.lbearing + metrics.rbearing; + h = metrics.ascent + metrics.descent; + s = 1.0 / w; + s *= 0.95; + + margin = w * 0.1; + margin2 = margin * 1.7; + + glPushMatrix(); + + glTranslatef (-0.5, bp->billboard_y, y); + glRotatef (90, 1, 0, 0); + glScalef (s, s * 1.5, s); + + memcpy (color, bp->tower_color2, sizeof(color)); + color[3] = 0.6; + glColor4fv (color); + glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color); + + if (do_tex && !wire) + glDisable (GL_TEXTURE_2D); + + glBegin (wire ? GL_LINE_LOOP : GL_QUADS); + glNormal3f (0, 0, 1); + glVertex3f (-margin, -margin2, 0); + glVertex3f (-margin, h+margin2, 0); + glVertex3f (w+margin, h+margin2, 0); + glVertex3f (w+margin, -margin2, 0); + glEnd(); + polys++; + + if (do_tex && !wire) + { + color[3] = 1; + glColor4fv (color); + glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color); + glEnable (GL_TEXTURE_2D); + glTranslatef (-metrics.lbearing, metrics.descent, 0); + print_texture_string (font, bp->billboard_text); + polys++; + } + + glPopMatrix(); + return polys; +} + + +static void +init_text (ModeInfo *mi) +{ + gibson_configuration *bp = &ccs[MI_SCREEN(mi)]; + int lines = 20; + int i; + char *s; + + const char *const ss[] = { + "\n" + "ACCESS TO THIS COMPUTER AND\n" + "ITS DATA IS RESTRICTED TO\n" + "AUTHORIZED PERSONNEL ONLY\n" + "\n", + "\n" + " PASSWORD ACCEPTED\n" + " GOD\n" + "\n", + "PERSONNEL >>>\n", + "SEA ROUTINGS >>>\n", + "GARBAGE >>>\n", + "COMP. SERVICING >>>\n", + "COMPANY BUDGETS >>>\n", + "SCIENTIFIC BUDGETS >>>\n", + "COMPANY POLICIES >>>\n", + "ANNUAL RETURNS >>>\n", + "MINE RESEARCH >>>\n", + "CENTRAL LIBRARY >>>\n", + "QUANTATIVE SPEC. >>>\n", + "PAYMENT LEVELS >>>\n", + "CENTRAL SERVER >>>\n", + "GARBAGE >>>\n", + "KNMTS. DVPNT. >>>\n", + "LICENSING >>>\n", + "RELATIONS >>>\n", + "TIME SHEET RECS. >>>\n", + "RD. PRT. ROUTINGS >>>\n", + "RECRUITMENT >>>\n", + "TNKR. EXPENDITURE >>>\n", + "MINE DEVELOPMENT >>>\n", + "GARBAGE >>>\n", + "ANNUAL BUDGETS >>>\n", + "OIL LOCATIONS >>>\n", + "TIME SHEET RECS. >>>\n", + "RD. PRT. ROUTINGS >>>\n", + "KINEMATICS >>>\n", + "TPS. REPORTS >>>\n", + "BLAST FRNC. STATUS >>>\n", + "ACCOUNTANTS >>>\n", + "SHIPPING FORCASTS >>>\n", + "INDST. REPORTS >>>\n", + "EXPLOR. DVLT. >>>\n", + "WRHSE. EXPEND. >>>\n", + "GARBAGE >>>\n", + "RELOCATIONS >>>\n", + "AIRFREIGHT STATUS >>>\n", + "TPGC. EXPEND. >>>\n", + "SEA-BOARD LAWS >>>\n", + "COMPOSITE PLANTS >>>\n", + "NUCLEAR RESEARCH >>>\n", + "BALLAST REPORTS >>>\n", + "\n" + "CONFIDENTAL\n" + "FILES\n" + "DO NOT DELETE\n" + "BEFORE FINAL\n" + "BACKUP IS COMPLETED\n" + "\n", + "\n" + "FILE 1\n" + "WAITING FOR BACK-UP\n" + "\n" + "FILE 2\n" + "WAITING FOR BACK-UP\n" + "\n" + "FILE 3\n" + "WAITING FOR BACK-UP\n" + "\n" + "FILE 4\n" + "WAITING FOR BACK-UP\n" + "\n" + }; + + + bp->text[1].text = s = calloc (countof(ss) * 2 * 40, 1); + for (i = 0; i < countof(ss); i++) + { + int n = random() % countof(ss); + strcat (s, ss[n]); + s += strlen(s); + } + + bp->text[0].text = s = calloc (lines * 40, 1); + for (i = 0; i < lines; i++) + { + switch (random() % 11) { + case 0: sprintf (s, "%X\n", random() % 0xFFFFFFFF); break; + case 1: sprintf (s, "%X\n", random() % 0xFFFFFF); break; + case 2: sprintf (s, "%X\n", random() % 0xFFFF); break; + case 3: sprintf (s, "%d\n", random() % 0xFFFFFF); break; + case 4: sprintf (s, "%d\n", random() % 0xFFFF); break; + case 5: sprintf (s, "%d\n", random() % 0xFFF); break; + case 6: strcat (s, "00000000\n"); break; + case 7: sprintf (s, "{{{{{{{{\n"); break; + case 8: sprintf (s, "[][][][][][]\n"); break; + case 9: sprintf (s, "DEFAULT\n"); break; + case 10: sprintf (s, "\n"); break; + } + s += strlen(s); + } +} + + +static void +init_textures (ModeInfo *mi) +{ + gibson_configuration *bp = &ccs[MI_SCREEN(mi)]; + int i; + for (i = 0; i < countof(bp->text); i++) + { + glGenTextures (1, &bp->text[i].texid); + glBindTexture (GL_TEXTURE_2D, bp->text[i].texid); + texture_string_metrics (bp->text[i].font_data, " ", + &bp->text[i].metrics, + &bp->text[i].ascent, + &bp->text[i].descent); + bp->text[i].em_width = bp->text[i].metrics.width; + string_to_texture (bp->text[i].font_data, bp->text[i].text, + &bp->text[i].metrics, + &bp->text[i].width, + &bp->text[i].height); + } + glBindTexture (GL_TEXTURE_2D, 0); +} + + +ENTRYPOINT void +init_gibson (ModeInfo *mi) +{ + gibson_configuration *bp; + + MI_INIT (mi, ccs); + + bp = &ccs[MI_SCREEN(mi)]; + + if ((bp->glx_context = init_GL(mi)) != NULL) { + reshape_gibson (mi, MI_WIDTH(mi), MI_HEIGHT(mi)); + } + + parse_color (mi, "towerText", bp->tower_color); + parse_color (mi, "towerText2", bp->tower_color2); + parse_color (mi, "towerColor", bp->bg_color); + memcpy (bp->edge_color, bp->bg_color, sizeof(bp->tower_color)); + bp->edge_color [3] = 0.7; + bp->bg_color [3] = 1.0; + bp->tower_color [3] = 1.0; + bp->tower_color2[3] = 1.0; + + if (grid_spacing < 1) grid_spacing = 1; + if (grid_width < 1) grid_width = 1; + if (grid_height < 1) grid_height = 1; + if (grid_depth < 1) grid_depth = 1; + if (columns < 1) columns = 1; + bp->ntowers = grid_width * grid_depth; + bp->towers = (tower *) calloc (sizeof(tower), bp->ntowers); + bp->startup_p = True; + + { + double wander_speed = 0.007 * speed; + double tilt_speed = 0.01 * speed; + bp->rot = make_rotator (0, 0, 0, 0, wander_speed, True); + bp->rot2 = make_rotator (0, 0, 0, 0, tilt_speed, True); + } + + bp->text[0].font_data = load_texture_font (mi->dpy, "towerFont"); + bp->text[1].font_data = load_texture_font (mi->dpy, "towerFont"); + init_text (mi); + init_textures (mi); + + bp->ground_dlist = glGenLists (1); + glNewList (bp->ground_dlist, GL_COMPILE); + bp->ground_polys = draw_ground (mi); + glEndList (); + + bp->tower_dlist = glGenLists (1); + glNewList (bp->tower_dlist, GL_COMPILE); + bp->tower_polys = draw_tower (mi, &bp->towers[0], 0, -1); + glEndList (); + + { + int x, y; + GLfloat ww = grid_width * (1 + grid_spacing) - grid_spacing; + GLfloat hh = grid_depth * (1 + grid_spacing) - grid_spacing; + for (y = 0; y < grid_depth; y++) + for (x = 0; x < grid_width; x++) + { + int i; + tower *t = &bp->towers[y * grid_width + x]; + t->x = (x * ww / (grid_width - 1)) - ww/2; + t->y = (y * hh / grid_depth) + 6; + t->h = 0 - y / (GLfloat) grid_depth / 2; + + for (i = 0; i < countof(t->fg_dlists); i++) + { + t->bg_dlists[i] = glGenLists (1); + glNewList (t->bg_dlists[i], GL_COMPILE); + t->bg_polys = draw_tower (mi, t, 1, i); + glEndList (); + + t->fg_dlists[i] = glGenLists (1); + glNewList (t->fg_dlists[i], GL_COMPILE); + t->fg_polys += draw_tower (mi, t, 2, i); + glEndList (); + } + } + } + + animate_towers (mi); +} + + +ENTRYPOINT void +draw_gibson (ModeInfo *mi) +{ + gibson_configuration *bp = &ccs[MI_SCREEN(mi)]; + Display *dpy = MI_DISPLAY(mi); + Window window = MI_WINDOW(mi); + int wire = MI_IS_WIREFRAME(mi); + GLfloat s; + int i; + + static const GLfloat bspec[4] = {1.0, 1.0, 1.0, 1.0}; + static const GLfloat bshiny = 128.0; + GLfloat bcolor[4] = { 0.7, 0.7, 1.0, 1.0 }; + + if (!bp->glx_context) + return; + + mi->polygon_count = 0; + glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *bp->glx_context); + + glShadeModel (GL_SMOOTH); + glEnable (GL_NORMALIZE); + glEnable (GL_CULL_FACE); + glDisable (GL_TEXTURE_2D); + glEnable (GL_DEPTH_TEST); + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + if (!wire) + { + GLfloat pos[4] = {0.4, 0.2, 0.4, 0.0}; + GLfloat amb[4] = {0.2, 0.2, 0.2, 1.0}; + GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0}; + GLfloat spc[4] = {1.0, 1.0, 1.0, 1.0}; + + glEnable(GL_LIGHTING); + glEnable(GL_LIGHT0); + glLightfv(GL_LIGHT0, GL_POSITION, pos); + glLightfv(GL_LIGHT0, GL_AMBIENT, amb); + glLightfv(GL_LIGHT0, GL_DIFFUSE, dif); + glLightfv(GL_LIGHT0, GL_SPECULAR, spc); + } + + glPushMatrix (); + + glRotatef(current_device_rotation(), 0, 0, 1); + +# ifdef DEBUG + s = 0.02; +# else + s = 10; +# endif + + glScalef (s, s, s); + + glTranslatef (0, -1, 0); + +# ifndef DEBUG + glRotatef (-82, 1, 0, 0); + + { + double maxx = 40; /* up/down */ + double maxy = 1.5; /* tilt */ + double maxz = 100; /* left/right */ + + double x, y, z; + double minh = -(grid_height / 2.0); + double maxh = -(grid_height / 20.0); + + get_position (bp->rot, &x, &y, &z, !bp->button_down_p); + x -= 0.5; + z = minh + (z * (maxh - minh)); + glTranslatef(x * grid_spacing * 0.005, 0, z); + + get_position (bp->rot2, &x, &y, &z, !bp->button_down_p); + + z += (bp->xscroll / 2.0); + x += (bp->yscroll / 2.0); + + glRotatef (maxx/2 - x*maxx, 1, 0, 0); + glRotatef (maxy/2 - y*maxy, 0, 1, 0); + glRotatef (maxz/2 - z*maxz, 0, 0, 1); + } +# endif /* DEBUG */ + + glPushMatrix(); + glScalef (GROUND_QUAD_SIZE, GROUND_QUAD_SIZE, 1); + + glTranslatef (0, bp->ground_y - 1.5, 0); + glCallList (bp->ground_dlist); + mi->polygon_count += bp->ground_polys; + + glTranslatef (0, 1, 0); + glCallList (bp->ground_dlist); + mi->polygon_count += bp->ground_polys; + glPopMatrix(); + + glMaterialfv (GL_FRONT, GL_SPECULAR, bspec); + glMateriali (GL_FRONT, GL_SHININESS, bshiny); + glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, bcolor); + + glPushMatrix(); + + if (grid_width & 1) /* Stay between towers */ + glTranslatef ((grid_spacing + 1) / 2.0, 0, 0); + + + if (!wire) + { + GLfloat fog_color[4] = { 0, 0, 0, 1 }; + glFogfv (GL_FOG_COLOR, fog_color); + /* I so don't understand how to choose the fog parameters. */ + glFogi (GL_FOG_MODE, GL_LINEAR); + glFogf (GL_FOG_START, 0); + glFogf (GL_FOG_END, 100); + glEnable (GL_FOG); + } + + /* Clear the floor under the tower bases */ + + { + GLfloat color0[4] = { 0, 0, 0, 1 }; + GLfloat z = 0.01; + + if (do_tex && !wire) glDisable (GL_TEXTURE_2D); + glColor4fv (color0); + glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color0); + glDisable (GL_BLEND); + glDisable (GL_DEPTH_TEST); + + for (i = 0; i < bp->ntowers; i++) + { + tower *t = &bp->towers[i]; + glPushMatrix(); + glTranslatef (t->x, t->y, 0); + + glNormal3f (0, 0, 1); + glBegin (wire ? GL_LINE_LOOP : GL_QUADS); /* clipping quad */ + glVertex3f (-0.5, -0.5, z); + glVertex3f ( 0.5, -0.5, z); + glVertex3f ( 0.5, 0.5, z); + glVertex3f (-0.5, 0.5, z); + glEnd(); + mi->polygon_count++; + glPopMatrix(); + } + } + + glEnable (GL_DEPTH_TEST); + + if (!wire) + { + if (do_tex) + { + glEnable (GL_TEXTURE_2D); + enable_texture_string_parameters(); + } + glEnable (GL_BLEND); + glBlendFunc (GL_SRC_ALPHA, GL_ONE); + glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); + glDisable (GL_CULL_FACE); + if (bp->startup_p) + glEnable (GL_DEPTH_TEST); + else + glDisable (GL_DEPTH_TEST); + } + + /* Draw the towers */ + + for (i = 0; i < bp->ntowers; i++) + { + tower *t = &bp->towers[i]; + glPushMatrix(); + glTranslatef (t->x, t->y-1, -grid_height * ease_ratio (1 - t->h)); + + glCallList (bp->tower_dlist); + mi->polygon_count += bp->tower_polys; + + if (wire || do_tex) + { + int j; + for (j = 0; j < countof(t->fg_dlists); j++) + { + if (! (t->face_mode & (1 << j))) + { + glCallList (t->bg_dlists[j]); + mi->polygon_count += t->bg_polys; + } + else + { + glCallList (t->fg_dlists[j]); + mi->polygon_count += t->fg_polys; + } + } + } + glPopMatrix(); + } + + glPopMatrix(); + + mi->polygon_count += draw_billboard (mi); + glPopMatrix(); + + if (!bp->button_down_p) + animate_towers (mi); + + if (bp->startup_p && bp->towers[bp->ntowers-1].h >= 1) + bp->startup_p = False; + + if (mi->fps_p) do_fps (mi); + glFinish(); + + glXSwapBuffers(dpy, window); +} + + +ENTRYPOINT void +free_gibson (ModeInfo *mi) +{ + int i, j; + gibson_configuration *bp = &ccs[MI_SCREEN(mi)]; + if (!bp->glx_context) return; + glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *bp->glx_context); + + if (bp->towers) free (bp->towers); + if (bp->rot) free_rotator (bp->rot); + if (bp->rot2) free_rotator (bp->rot2); + if (glIsList(bp->ground_dlist)) glDeleteLists(bp->ground_dlist, 1); + if (glIsList(bp->tower_dlist)) glDeleteLists(bp->tower_dlist, 1); + for (i = 0; i < countof(bp->text); i++) + { + if (bp->text[i].font_data) free_texture_font (bp->text[i].font_data); + if (bp->text[i].text) free (bp->text[i].text); + } + for (i = 0; i < bp->ntowers; i++) + { + for (j = 0; j < countof(bp->towers[i].fg_dlists); j++) + { + if (glIsList(bp->towers[i].fg_dlists[j])) + glDeleteLists(bp->towers[i].fg_dlists[j], 1); + if (glIsList(bp->towers[i].bg_dlists[j])) + glDeleteLists(bp->towers[i].bg_dlists[j], 1); + } + } +} + + +XSCREENSAVER_MODULE ("Gibson", gibson) +/* Greets to Crash Override, The Phantom Freak, and also Joey */ + +#endif /* USE_GL */ |