/* -*- Mode: C; c-basic-offset: 4; tab-width: 4 -*- * speedmine, Copyright (C) 2001 Conrad Parker * * 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. */ /* * Written mostly over the Easter holiday, 2001. Psychedelic option due to * a night at Home nightclub, Sydney. Three all-nighters of solid partying * were involved in the week this hack was written. * * Happy Birthday to WierdArms (17 April) and Pat (18 April) */ /* * Hacking notes * * This program generates a rectangular terrain grid and maps this onto * a semi-circular tunnel. The terrain has length TERRAIN_LENGTH, which * corresponds to length along the tunnel, and breadth TERRAIN_BREADTH, * which corresponds to circumference around the tunnel. For each frame, * the tunnel is perspective mapped onto a set of X and Y screen values. * * Throughout this code the following temporary variable names are used: * * i iterates along the tunnel in the direction of travel * j iterates around the tunnel clockwise * t iterates along the length of the perspective mapped values * from the furthest to the nearest * * Thus, the buffers are used with these iterators: * * terrain[i][j] terrain map * worldx[i][j], worldy[i][j] world coordinates (after wrapping) * {x,y,z}curvature[i] tunnel curvature * wideness[i] tunnel wideness * bonuses[i] bonus values * * xvals[t][j], yvals[t][j] screen coordinates * {min,max}{x,y}[t] bounding boxes of screen coords */ /* Define or undefine NDEBUG to turn assert and abort debugging off or on */ /*#define NDEBUG*/ /*#include */ #define assert(X) #define DEBUG_FLAG 0 #include #include "screenhack.h" #include "erase.h" #define MIN(a,b) ((a)<(b)?(a):(b)) #define MAX(a,b) ((a)>(b)?(a):(b)) #define RAND(r) (int)(((r)>0)?(random() % (long)(r)): -(random() % (long)(-r))) #define SIGN3(a) ((a)>0?1:((a)<0?-1:0)) #define MODULO(a,b) while ((a)<0) (a)+=(b); (a) %= (b); /* No. of shades of each color (ground, walls, bonuses) */ #define MAX_COLORS 32 #define FORWARDS 1 #define BACKWARDS -1 /* Apparently AIX's math.h bogusly defines `nearest' as a function, in violation of the ANSI C spec. */ #undef nearest #define nearest n3arest #define wireframe (st->wire_flag||st->wire_bonus>8||st->wire_bonus%2==1) #define effective_speed (st->direction*(st->speed+st->speed_bonus)) /* No. of levels of interpolation, for perspective */ #define INTERP 32 /* These must be powers of 2 */ #define TERRAIN_LENGTH 256 #define TERRAIN_BREADTH 32 /* total "perspective distance" of terrain */ #define TERRAIN_PDIST (INTERP*TERRAIN_LENGTH) #define ROTS 1024 #define TB_MUL (ROTS/TERRAIN_BREADTH) #define random_elevation() (st->terrain_flag?(random() % 200):0) #define random_curvature() (st->curviness>0.0?((double)(random() % 40)-20)*st->curviness:0.0) #define random_twist() (st->twistiness>0.0?((double)(random() % 40)-20)*st->twistiness:0.0) #define random_wideness() (st->widening_flag?(int)(random() % 1200):0) #define STEEL_ELEVATION 300 struct state { Display *dpy; Window window; Pixmap dbuf, stars_mask; Colormap cmap; Visual *visual; Screen *screen; unsigned int default_fg_pixel; GC draw_gc, erase_gc, tunnelend_gc, stars_gc, stars_erase_gc; int ncolors, nr_ground_colors, nr_wall_colors, nr_bonus_colors; XColor ground_colors[MAX_COLORS], wall_colors[MAX_COLORS]; XColor bonus_colors[MAX_COLORS]; GC ground_gcs[MAX_COLORS], wall_gcs[MAX_COLORS], bonus_gcs[MAX_COLORS]; int be_wormy; int width, height; int delay; int smoothness; int verbose_flag; int wire_flag; int terrain_flag; int widening_flag; int bumps_flag; int bonuses_flag; int crosshair_flag; int psychedelic_flag; double maxspeed; double thrust, gravity; double vertigo; double curviness; double twistiness; double pos; double speed; double accel; double step; int direction; int pindex, nearest; int flipped_at; int xoffset, yoffset; int bonus_bright; int wire_bonus; double speed_bonus; int spin_bonus; int backwards_bonus; double sintab[ROTS], costab[ROTS]; int orientation; int terrain[TERRAIN_LENGTH][TERRAIN_BREADTH]; double xcurvature[TERRAIN_LENGTH]; double ycurvature[TERRAIN_LENGTH]; double zcurvature[TERRAIN_LENGTH]; int wideness[TERRAIN_LENGTH]; int bonuses[TERRAIN_LENGTH]; int xvals[TERRAIN_LENGTH][TERRAIN_BREADTH]; int yvals[TERRAIN_LENGTH][TERRAIN_BREADTH]; double worldx[TERRAIN_LENGTH][TERRAIN_BREADTH]; double worldy[TERRAIN_LENGTH][TERRAIN_BREADTH]; int minx[TERRAIN_LENGTH], maxx[TERRAIN_LENGTH]; int miny[TERRAIN_LENGTH], maxy[TERRAIN_LENGTH]; int total_nframes; int nframes; double fps; double fps_start, fps_end; struct timeval start_time; int rotation_offset; int jamming; }; /* a forward declaration ... */ static void change_colors(struct state *st); /* * get_time () * * returns the total time elapsed since the beginning of the demo */ static double get_time(struct state *st) { struct timeval t; float f; #if GETTIMEOFDAY_TWO_ARGS gettimeofday(&t, NULL); #else gettimeofday(&t); #endif t.tv_sec -= st->start_time.tv_sec; f = ((double)t.tv_sec) + t.tv_usec*1e-6; return f; } /* * init_time () * * initialises the timing structures */ static void init_time(struct state *st) { #if GETTIMEOFDAY_TWO_ARGS gettimeofday(&st->start_time, NULL); #else gettimeofday(&st->start_time); #endif st->fps_start = get_time(st); } /* * perspective() * * perspective map the world coordinates worldx[i][j], worldy[i][j] onto * screen coordinates xvals[t][j], yvals[t][j] */ static void perspective (struct state *st) { int i, j, jj, t=0, depth, view_pos; int rotation_bias, r; double xc=0.0, yc=0.0, zc=0.0; double xcc=0.0, ycc=0.0, zcc=0.0; double xx, yy; double zfactor, zf; zf = 8.0*28.0 / (double)(st->width*TERRAIN_LENGTH); if (st->be_wormy) zf *= 3.0; depth = TERRAIN_PDIST - INTERP + st->pindex; view_pos = (st->nearest+3*TERRAIN_LENGTH/4)%TERRAIN_LENGTH; st->xoffset += - st->xcurvature[view_pos]*st->curviness/8; st->xoffset /= 2; st->yoffset += - st->ycurvature[view_pos]*st->curviness/4; st->yoffset /= 2; st->rotation_offset += (int)((st->zcurvature[view_pos]-st->zcurvature[st->nearest])*ROTS/8); st->rotation_offset /= 2; rotation_bias = st->orientation + st->spin_bonus - st->rotation_offset; if (st->bumps_flag) { if (st->be_wormy) { st->yoffset -= ((st->terrain[view_pos][TERRAIN_BREADTH/4] * st->width /(8*1600))); rotation_bias += (st->terrain[view_pos][TERRAIN_BREADTH/4+2] - st->terrain[view_pos][TERRAIN_BREADTH/4-2])/8; } else { st->yoffset -= ((st->terrain[view_pos][TERRAIN_BREADTH/4] * st->width /(2*1600))); rotation_bias += (st->terrain[view_pos][TERRAIN_BREADTH/4+2] - st->terrain[view_pos][TERRAIN_BREADTH/4-2])/16; } } MODULO(rotation_bias, ROTS); for (t=0; t < TERRAIN_LENGTH; t++) { i = st->nearest + t; MODULO(i, TERRAIN_LENGTH); xc += st->xcurvature[i]; yc += st->ycurvature[i]; zc += st->zcurvature[i]; xcc += xc; ycc += yc; zcc += zc; st->maxx[i] = st->maxy[i] = 0; st->minx[i] = st->width; st->miny[i] = st->height; } for (t=0; t < TERRAIN_LENGTH; t++) { i = st->nearest - 1 - t; MODULO(i, TERRAIN_LENGTH); zfactor = (double)depth* (12.0 - TERRAIN_LENGTH/8.0) * zf; for (j=0; j < TERRAIN_BREADTH; j++) { jj = st->direction * j; MODULO(jj, TERRAIN_BREADTH); /* jwz: not totally sure if this is right, but it avoids div0 */ if (zfactor != 0) { xx = (st->worldx[i][jj]-(st->vertigo*xcc))/zfactor; yy = (st->worldy[i][j]-(st->vertigo*ycc))/zfactor; } else { xx = 0; yy = 0; } r = rotation_bias + (int)(st->vertigo*zcc); MODULO(r, ROTS); st->xvals[t][j] = st->xoffset + (st->width>>1) + (int)(xx * st->costab[r] - yy * st->sintab[r]); st->maxx[t] = MAX(st->maxx[t], st->xvals[t][j]); st->minx[t] = MIN(st->minx[t], st->xvals[t][j]); st->yvals[t][j] = st->yoffset + st->height/2 + (int)(xx * st->sintab[r] + yy * st->costab[r]); st->maxy[t] = MAX(st->maxy[t], st->yvals[t][j]); st->miny[t] = MIN(st->miny[t], st->yvals[t][j]); } xcc -= xc; ycc -= yc; zcc -= zc; xc -= st->xcurvature[i]; yc -= st->ycurvature[i]; zc -= st->zcurvature[i]; depth -= INTERP; } } /* * wrap_tunnel (start, end) * * wrap the terrain terrain[i][j] around the semi-circular tunnel function * * x' = x/2 * cos(theta) - (y-k) * x * sin(theta) * y' = x/4 * sin(theta) + y * cos(theta) * * between i=start and i=end inclusive, producing world coordinates * worldx[i][j], worldy[i][j] */ static void wrap_tunnel (struct state *st, int start, int end) { int i, j, v; double x, y; assert (start < end); for (i=start; i <= end; i++) { for (j=0; j < TERRAIN_BREADTH; j++) { x = j * (1.0/TERRAIN_BREADTH); v = st->terrain[i][j]; y = (double)(v==STEEL_ELEVATION?200:v) - st->wideness[i] - 1200; /* lower road */ if (j > TERRAIN_BREADTH/8 && j < 3*TERRAIN_BREADTH/8) y -= 300; st->worldx[i][j] = x/2 * st->costab[j*TB_MUL] - (y-st->height/4.0)*x*st->sintab[j*TB_MUL]; st->worldy[i][j] = x/4 * st->sintab[j*TB_MUL] + y * st->costab[j*TB_MUL]; } } } /* * flip_direction() * * perform the state transitions and terrain transformation for the * "look backwards/look forwards" bonus */ static void flip_direction (struct state *st) { int i, ip, in, j, t; st->direction = -st->direction; st->bonus_bright = 20; for (i=0; i < TERRAIN_LENGTH; i++) { in = st->nearest + i; MODULO(in, TERRAIN_BREADTH); ip = st->nearest - i; MODULO(ip, TERRAIN_BREADTH); for (j=0; j < TERRAIN_BREADTH; j++) { t = st->terrain[ip][j]; st->terrain[ip][j] = st->terrain[in][j]; st->terrain[in][j] = t; } } } /* * generate_smooth (start, end) * * generate smooth terrain between i=start and i=end inclusive */ static void generate_smooth (struct state *st, int start, int end) { int i,j, ii; assert (start < end); for (i=start; i <= end; i++) { ii = i; MODULO(ii, TERRAIN_LENGTH); for (j=0; j < TERRAIN_BREADTH; j++) { st->terrain[i][j] = STEEL_ELEVATION; } } } /* * generate_straight (start, end) * * zero the curvature and wideness between i=start and i=end inclusive */ static void generate_straight (struct state *st, int start, int end) { int i,j, ii; assert (start < end); for (i=start; i <= end; i++) { ii = i; MODULO(ii, TERRAIN_LENGTH); for (j=0; j < TERRAIN_BREADTH; j++) { st->xcurvature[ii] = 0; st->ycurvature[ii] = 0; st->zcurvature[ii] = 0; st->wideness[ii] = 0; } } } /* * int generate_terrain_value (v1, v2, v3, v4, w) * * generate terrain value near the average of v1, v2, v3, v4, with * perturbation proportional to w */ static int generate_terrain_value (struct state *st, int v1, int v2, int v3, int v4, int w) { int sum, ret; int rval; if (!st->terrain_flag) return 0; sum = v1 + v2 + v3 + v4; rval = w*sum/st->smoothness; if (rval == 0) rval = 2; ret = (sum/4 -(rval/2) + RAND(rval)); if (ret < -400 || ret > 400) { ret = sum/4; } return ret; } /* * generate_terrain (start, end, final) * * generate terrain[i][j] between i=start and i=end inclusive * * This is performed by successive subdivision of the terrain into * rectangles of decreasing size. Subdivision continues until the * the minimum width or height of these rectangles is 'final'; ie. * final=1 indicates to subdivide as far as possible, final=2 indicates * to stop one subdivision before that (leaving a checkerboard pattern * uncalculated) etc. */ static void generate_terrain (struct state *st, int start, int end, int final) { int i,j,w,l; int ip, jp, in, jn; /* prev, next values */ int diff; assert (start < end); assert (start >= 0 && start < TERRAIN_LENGTH); assert (end >= 0 && end < TERRAIN_LENGTH); diff = end - start + 1; st->terrain[end][0] = random_elevation(); st->terrain[end][TERRAIN_BREADTH/2] = random_elevation(); for (w= diff/2, l=TERRAIN_BREADTH/4; w >= final || l >= final; w /= 2, l /= 2) { if (w<1) w=1; if (l<1) l=1; for (i=start+w-1; i < end; i += (w*2)) { ip = i-w; MODULO(ip, TERRAIN_LENGTH); in = i+w; MODULO(in, TERRAIN_LENGTH); for (j=l-1; j < TERRAIN_BREADTH; j += (l*2)) { jp = j-1; MODULO(jp, TERRAIN_BREADTH); jn = j+1; MODULO(jn, TERRAIN_BREADTH); st->terrain[i][j] = generate_terrain_value (st, st->terrain[ip][jp], st->terrain[in][jp], st->terrain[ip][jn], st->terrain[in][jn], w); } } for (i=start+(w*2)-1; i < end; i += (w*2)) { ip = i-w; MODULO(ip, TERRAIN_LENGTH); in = i+w; MODULO(in, TERRAIN_LENGTH); for (j=l-1; j < TERRAIN_BREADTH; j += (l*2)) { jp = j-1; MODULO(jp, TERRAIN_BREADTH); jn = j+1; MODULO(jn, TERRAIN_BREADTH); st->terrain[i][j] = generate_terrain_value (st, st->terrain[ip][j], st->terrain[in][j], st->terrain[i][jp], st->terrain[i][jn], w); } } for (i=start+w-1; i < end; i += (w*2)) { ip = i-w; MODULO(ip, TERRAIN_LENGTH); in = i+w; MODULO(in, TERRAIN_LENGTH); for (j=2*l-1; j < TERRAIN_BREADTH; j += (l*2)) { jp = j-1; MODULO(jp, TERRAIN_BREADTH); jn = j+1; MODULO(jn, TERRAIN_BREADTH); st->terrain[i][j] = generate_terrain_value (st, st->terrain[ip][j], st->terrain[in][j], st->terrain[i][jp], st->terrain[i][jn], w); } } } } /* * double generate_curvature_value (v1, v2, w) * * generate curvature value near the average of v1 and v2, with perturbation * proportional to w */ static double generate_curvature_value (double v1, double v2, int w) { double sum, avg, diff, ret; int rval; assert (!isnan(v1) && !isnan(v2)); sum = v1+v2; avg = sum/2.0; diff = MIN(v1 - avg, v2 - avg); rval = (int)diff * w; if (rval == 0.0) return avg; ret = (avg -((double)rval)/500.0 + ((double)RAND(rval))/1000.0); assert (!isnan(ret)); return ret; } /* * generate_curves (start, end) * * generate xcurvature[i], ycurvature[i], zcurvature[i] and wideness[i] * between start and end inclusive */ static void generate_curves (struct state *st, int start, int end) { int i, diff, ii, in, ip, w; assert (start < end); diff = end - start + 1; MODULO (diff, TERRAIN_LENGTH); if (random() % 100 == 0) st->xcurvature[end] = 30 * random_curvature(); else if (random() % 10 == 0) st->xcurvature[end] = 20 * random_curvature(); else st->xcurvature[end] = 10 * random_curvature(); if (random() % 50 == 0) st->ycurvature[end] = 20 * random_curvature(); else if (random() % 25 == 0) st->ycurvature[end] = 30 * random_curvature(); else st->ycurvature[end] = 10 * random_curvature(); if (random() % 3 == 0) st->zcurvature[end] = random_twist(); else st->zcurvature[end] = generate_curvature_value (st->zcurvature[end], random_twist(), 1); if (st->be_wormy) st->wideness[end] = random_wideness(); else st->wideness[end] = generate_curvature_value (st->wideness[end], random_wideness(), 1); for (w=diff/2; w >= 1; w /= 2) { for (i=start+w-1; i < end; i+=(w*2)) { ii = i; MODULO (ii, TERRAIN_LENGTH); ip = i-w; MODULO (ip, TERRAIN_LENGTH); in = i+w; MODULO (in, TERRAIN_LENGTH); st->xcurvature[ii] = generate_curvature_value (st->xcurvature[ip], st->xcurvature[in], w); st->ycurvature[ii] = generate_curvature_value (st->ycurvature[ip], st->ycurvature[in], w); st->zcurvature[ii] = generate_curvature_value (st->zcurvature[ip], st->zcurvature[in], w); st->wideness[ii] = generate_curvature_value (st->wideness[ip], st->wideness[in], w); } } } /* * do_bonus () * * choose a random bonus and perform its state transition */ static void do_bonus (struct state *st) { st->bonus_bright = 20; if (st->jamming > 0) { st->jamming--; st->nearest -= 2; MODULO(st->nearest, TERRAIN_LENGTH); return; } if (st->psychedelic_flag) change_colors(st); switch (random() % 7) { case 0: /* switch to or from wireframe */ st->wire_bonus = (st->wire_bonus?0:300); break; case 1: /* speedup */ st->speed_bonus = 40.0; break; case 2: st->spin_bonus += ROTS; break; case 3: st->spin_bonus -= ROTS; break; case 4: /* look backwards / look forwards */ st->flipped_at = st->nearest; flip_direction (st); st->backwards_bonus = (st->backwards_bonus?0:10); break; case 5: change_colors(st); break; case 6: /* jam against the bonus a few times; deja vu! */ st->nearest -= 2; MODULO(st->nearest, TERRAIN_LENGTH); st->jamming = 3; break; default: assert(0); break; } } /* * check_bonus () * * check if a bonus has been passed in the last frame, and handle it */ static void check_bonuses (struct state *st) { int i, ii, start, end; if (!st->bonuses_flag) return; if (st->step >= 0.0) { start = st->nearest; end = st->nearest + (int)floor(st->step); } else { end = st->nearest; start = st->nearest + (int)floor(st->step); } if (st->be_wormy) { start += TERRAIN_LENGTH/4; end += TERRAIN_LENGTH/4; } for (i=start; i < end; i++) { ii = i; MODULO(ii, TERRAIN_LENGTH); if (st->bonuses[ii] == 1) do_bonus (st); } } /* * decrement_bonuses (double time_per_frame) * * decrement timers associated with bonuses */ static void decrement_bonuses (struct state *st, double time_per_frame) { if (!st->bonuses_flag) return; if (st->bonus_bright > 0) st->bonus_bright-=4; if (st->wire_bonus > 0) st->wire_bonus--; if (st->speed_bonus > 0) st->speed_bonus -= 2.0; if (st->spin_bonus > 10) st->spin_bonus -= (int)(st->step*13.7); else if (st->spin_bonus < -10) st->spin_bonus += (int)(st->step*11.3); if (st->backwards_bonus > 1) st->backwards_bonus--; else if (st->backwards_bonus == 1) { st->nearest += 2*(MAX(st->flipped_at, st->nearest) - MIN(st->flipped_at,st->nearest)); MODULO(st->nearest, TERRAIN_LENGTH); flip_direction (st); st->backwards_bonus = 0; } } /* * set_bonuses (start, end) * * choose if to and where to set a bonus between i=start and i=end inclusive */ static void set_bonuses (struct state *st, int start, int end) { int i, diff, ii; if (!st->bonuses_flag) return; assert (start < end); diff = end - start; for (i=start; i <= end; i++) { ii = i; if (ii>=TERRAIN_LENGTH) ii -= TERRAIN_LENGTH; st->bonuses[ii] = 0; } if (random() % 4 == 0) { i = start + RAND(diff-3); ii = i; if (ii>=TERRAIN_LENGTH) ii -= TERRAIN_LENGTH; st->bonuses[ii] = 2; /* marker */ ii = i+3; if (ii>=TERRAIN_LENGTH) ii -= TERRAIN_LENGTH; st->bonuses[ii] = 1; /* real thing */ } } /* * regenerate_terrain () * * regenerate a portion of the terrain map of length TERRAIN_LENGTH/4 iff * we just passed between two quarters of the terrain. * * choose the kind of terrain to produce, produce it and wrap the tunnel */ static void regenerate_terrain (struct state *st) { int start, end; int passed; passed = st->nearest % (TERRAIN_LENGTH/4); if (st->speed == 0.0 || (st->speed > 0.0 && passed > (int)st->step) || (st->speed < 0.0 && (TERRAIN_LENGTH/4)-passed > (int)fabs(st->step))) { return; } end = st->nearest - passed - 1; MODULO(end, TERRAIN_LENGTH); start = end - TERRAIN_LENGTH/4 + 1; MODULO(start, TERRAIN_LENGTH); if (DEBUG_FLAG) printf ("Regenerating [%d - %d]\n", start, end); set_bonuses (st, start, end); switch (random() % 64) { case 0: case 1: generate_terrain (st, start, end, 1); generate_smooth (st, start, start + TERRAIN_LENGTH/8 + (random() % TERRAIN_LENGTH/8)); break; case 2: generate_smooth (st, start, end); generate_terrain (st, start, end, 4); break; case 3: generate_smooth (st, start, end); generate_terrain (st, start, end, 2); break; default: generate_terrain (st, start, end, 1); } if (random() % 16 == 0) { generate_straight (st, start, end); } else { generate_curves (st, start, end); } wrap_tunnel (st, start, end); } /* * init_terrain () * * initialise the terrain map for the beginning of the demo */ static void init_terrain (struct state *st) { int i, j; for (i=0; i < TERRAIN_LENGTH; i++) { for (j=0; j < TERRAIN_BREADTH; j++) { st->terrain[i][j] = 0; } } st->terrain[TERRAIN_LENGTH-1][0] = - (random() % 300); st->terrain[TERRAIN_LENGTH-1][TERRAIN_BREADTH/2] = - (random() % 300); generate_smooth (st, 0, TERRAIN_LENGTH-1); generate_terrain (st, 0, TERRAIN_LENGTH/4 -1, 4); generate_terrain (st, TERRAIN_LENGTH/4, TERRAIN_LENGTH/2 -1, 2); generate_terrain (st, TERRAIN_LENGTH/2, 3*TERRAIN_LENGTH/4 -1, 1); generate_smooth (st, 3*TERRAIN_LENGTH/4, TERRAIN_LENGTH-1); } /* * init_curves () * * initialise the curvatures and wideness for the beginning of the demo. */ static void init_curves (struct state *st) { int i; for (i=0; i < TERRAIN_LENGTH-1; i++) { st->xcurvature[i] = 0.0; st->ycurvature[i] = 0.0; st->zcurvature[i] = 0.0; } st->xcurvature[TERRAIN_LENGTH-1] = random_curvature(); st->ycurvature[TERRAIN_LENGTH-1] = random_curvature(); st->zcurvature[TERRAIN_LENGTH-1] = random_twist(); generate_straight (st, 0, TERRAIN_LENGTH/4-1); generate_curves (st, TERRAIN_LENGTH/4, TERRAIN_LENGTH/2-1); generate_curves (st, TERRAIN_LENGTH/2, 3*TERRAIN_LENGTH/4-1); generate_straight (st, 3*TERRAIN_LENGTH/4, TERRAIN_LENGTH-1); } /* * render_quads (dpy, d, t, dt, i) * * renders the quadrilaterals from perspective depth t to t+dt. * i is passed as a hint, where i corresponds to t as asserted. */ static void render_quads (struct state *st, Drawable d, int t, int dt, int i) { int j, t2, j2, in; int index; XPoint points[4]; GC gc; assert (i == (st->nearest - (t + dt) + TERRAIN_LENGTH) % TERRAIN_LENGTH); in = i + 1; MODULO(in, TERRAIN_LENGTH); for (j=0; j < TERRAIN_BREADTH; j+=dt) { t2 = t+dt; if (t2 >= TERRAIN_LENGTH) t2 -= TERRAIN_LENGTH; j2 = j+dt; if (j2 >= TERRAIN_BREADTH) j2 -= TERRAIN_BREADTH; points[0].x = st->xvals[t][j]; points[0].y = st->yvals[t][j]; points[1].x = st->xvals[t2][j]; points[1].y = st->yvals[t2][j]; points[2].x = st->xvals[t2][j2]; points[2].y = st->yvals[t2][j2]; points[3].x = st->xvals[t][j2]; points[3].y = st->yvals[t][j2]; index = st->bonus_bright + st->ncolors/3 + t*(t*INTERP + st->pindex) * st->ncolors / (3*TERRAIN_LENGTH*TERRAIN_PDIST); if (!wireframe) { index += (int)((points[0].y - points[3].y) / 8); index += (int)((st->worldx[i][j] - st->worldx[in][j]) / 40); index += (int)((st->terrain[in][j] - st->terrain[i][j]) / 100); } if (st->be_wormy && st->psychedelic_flag) index += st->ncolors/4; if (st->ncolors > MAX_COLORS) abort(); index = MIN (index, st->ncolors-1); index = MAX (index, 0); if (st->bonuses[i]) { XSetClipMask (st->dpy, st->bonus_gcs[index], None); } if (wireframe) { if (st->bonuses[i]) gc = st->bonus_gcs[index]; else gc = st->ground_gcs[index]; XDrawLines (st->dpy, d, gc, points, 4, CoordModeOrigin); } else { if (st->bonuses[i]) gc = st->bonus_gcs[index]; else if ((st->direction>0 && j < TERRAIN_BREADTH/8) || (j > TERRAIN_BREADTH/8 && j < 3*TERRAIN_BREADTH/8-1) || (st->direction < 0 && j > 3*TERRAIN_BREADTH/8-1 && j < TERRAIN_BREADTH/2) || st->terrain[i][j] == STEEL_ELEVATION || st->wideness[in] - st->wideness[i] > 200) gc = st->ground_gcs[index]; else gc = st->wall_gcs[index]; XFillPolygon (st->dpy, d, gc, points, 4, Nonconvex, CoordModeOrigin); } } } /* * render_pentagons (dpy, d, t, dt, i) * * renders the pentagons from perspective depth t to t+dt. * i is passed as a hint, where i corresponds to t as asserted. */ static void render_pentagons (struct state *st, Drawable d, int t, int dt, int i) { int j, t2, j2, j3, in; int index; XPoint points[5]; GC gc; assert (i == (st->nearest -t + TERRAIN_LENGTH) % TERRAIN_LENGTH); in = i + 1; MODULO(in, TERRAIN_LENGTH); for (j=0; j < TERRAIN_BREADTH; j+=dt*2) { t2 = t+(dt*2); if (t2 >= TERRAIN_LENGTH) t2 -= TERRAIN_LENGTH; j2 = j+dt; if (j2 >= TERRAIN_BREADTH) j2 -= TERRAIN_BREADTH; j3 = j+dt+dt; if (j3 >= TERRAIN_BREADTH) j3 -= TERRAIN_BREADTH; points[0].x = st->xvals[t][j]; points[0].y = st->yvals[t][j]; points[1].x = st->xvals[t2][j]; points[1].y = st->yvals[t2][j]; points[2].x = st->xvals[t2][j2]; points[2].y = st->yvals[t2][j2]; points[3].x = st->xvals[t2][j3]; points[3].y = st->yvals[t2][j3]; points[4].x = st->xvals[t][j3]; points[4].y = st->yvals[t][j3]; index = st->bonus_bright + st->ncolors/3 + t*(t*INTERP + st->pindex) * st->ncolors / (3*TERRAIN_LENGTH*TERRAIN_PDIST); if (!wireframe) { index += (int)((points[0].y - points[3].y) / 8); index += (int)((st->worldx[i][j] - st->worldx[in][j]) / 40); index += (int)((st->terrain[in][j] - st->terrain[i][j]) / 100); } if (st->be_wormy && st->psychedelic_flag) index += st->ncolors/4; index = MIN (index, st->ncolors-1); index = MAX (index, 0); if (st->bonuses[i]) { XSetClipMask (st->dpy, st->bonus_gcs[index], None); } if (wireframe) { if (st->bonuses[i]) gc = st->bonus_gcs[index]; else gc = st->ground_gcs[index]; XDrawLines (st->dpy, d, gc, points, 5, CoordModeOrigin); } else { if (st->bonuses[i]) gc = st->bonus_gcs[index]; else if (j < TERRAIN_BREADTH/8 || (j > TERRAIN_BREADTH/8 && j < 3*TERRAIN_BREADTH/8-1) || st->terrain[i][j] == STEEL_ELEVATION || st->wideness[in] - st->wideness[i] > 200) gc = st->ground_gcs[index]; else gc = st->wall_gcs[index]; XFillPolygon (st->dpy, d, gc, points, 5, Complex, CoordModeOrigin); } } } /* * render_block (dpy, d, gc, t) * * render a filled polygon at perspective depth t using the given GC */ static void render_block (struct state *st, Drawable d, GC gc, int t) { int i; XPoint erase_points[TERRAIN_BREADTH/2]; for (i=0; i < TERRAIN_BREADTH/2; i++) { erase_points[i].x = st->xvals[t][i*2]; erase_points[i].y = st->yvals[t][i*2]; } XFillPolygon (st->dpy, d, gc, erase_points, TERRAIN_BREADTH/2, Complex, CoordModeOrigin); } /* * regenerate_stars_mask (dpy, t) * * regenerate the clip mask 'stars_mask' for drawing the bonus stars at * random positions within the bounding box at depth t */ static void regenerate_stars_mask (struct state *st, int t) { int i, w, h, a, b, l1, l2; const int lim = st->width*TERRAIN_LENGTH/(300*(TERRAIN_LENGTH-t)); w = st->maxx[t] - st->minx[t]; h = st->maxy[t] - st->miny[t]; if (w<6||h<6) return; XFillRectangle (st->dpy, st->stars_mask, st->stars_erase_gc, 0, 0, st->width, st->height); l1 = (t>3*TERRAIN_LENGTH/4?2:1); l2 = (t>7*TERRAIN_LENGTH/8?2:1); for (i=0; i < lim; i++) { a = RAND(w); b = RAND(h); XDrawLine (st->dpy, st->stars_mask, st->stars_gc, st->minx[t]+a-l1, st->miny[t]+b, st->minx[t]+a+l1, st->miny[t]+b); XDrawLine (st->dpy, st->stars_mask, st->stars_gc, st->minx[t]+a, st->miny[t]+b-l1, st->minx[t]+a, st->miny[t]+b+l1); } for (i=0; i < lim; i++) { a = RAND(w); b = RAND(h); XDrawLine (st->dpy, st->stars_mask, st->stars_gc, st->minx[t]+a-l2, st->miny[t]+b, st->minx[t]+a+l2, st->miny[t]+b); XDrawLine (st->dpy, st->stars_mask, st->stars_gc, st->minx[t]+a, st->miny[t]+b-l2, st->minx[t]+a, st->miny[t]+b+l2); } } /* * render_bonus_block (dpy, d, t, i) * * draw the bonus stars at depth t. * i is passed as a hint, where i corresponds to t as asserted. */ static void render_bonus_block (struct state *st, Drawable d, int t, int i) { int bt; assert (i == (st->nearest -t + TERRAIN_LENGTH) % TERRAIN_LENGTH); if (!st->bonuses[i] || wireframe) return; regenerate_stars_mask (st, t); bt = t * st->nr_bonus_colors / (2*TERRAIN_LENGTH); XSetClipMask (st->dpy, st->bonus_gcs[bt], st->stars_mask); render_block (st, d, st->bonus_gcs[bt], t); } static int begin_at (struct state *st) { int t; int max_minx=0, min_maxx=st->width, max_miny=0, min_maxy=st->height; for (t=TERRAIN_LENGTH-1; t > 0; t--) { max_minx = MAX (max_minx, st->minx[t]); min_maxx = MIN (min_maxx, st->maxx[t]); max_miny = MAX (max_miny, st->miny[t]); min_maxy = MIN (min_maxy, st->maxy[t]); if (max_miny >= min_maxy || max_minx >= min_maxx) break; } return t; } /* * render_speedmine (dpy, d) * * render the current frame. */ static void render_speedmine (struct state *st, Drawable d) { int t, i=st->nearest, dt=1; GC gc; assert (st->nearest >= 0 && st->nearest < TERRAIN_LENGTH); if (st->be_wormy || wireframe) { XFillRectangle (st->dpy, d, st->erase_gc, 0, 0, st->width, st->height); dt=4; for (t=0; t < TERRAIN_LENGTH/4; t+=dt) { render_bonus_block (st, d, t, i); i -= dt; MODULO(i, TERRAIN_LENGTH); render_quads (st, d, t, dt, i); } assert (t == TERRAIN_LENGTH/4); } else { t = MAX(begin_at(st), TERRAIN_LENGTH/4); /*t = TERRAIN_LENGTH/4; dt = 2; */ /*dt = (t >= 3*TERRAIN_LENGTH/4 ? 1 : 2);*/ i = (st->nearest -t + TERRAIN_LENGTH) % TERRAIN_LENGTH; render_block (st, d, st->tunnelend_gc, t); } dt=2; if (t == TERRAIN_LENGTH/4) render_pentagons (st, d, t, dt, i); for (; t < 3*TERRAIN_LENGTH/4; t+=dt) { render_bonus_block (st, d, t, i); i -= dt; MODULO(i, TERRAIN_LENGTH); render_quads (st, d, t, dt, i); } dt=1; if (st->be_wormy) { for (; t < TERRAIN_LENGTH-(1+(st->pindexpindexcrosshair_flag) { gc = (wireframe ? st->bonus_gcs[st->nr_bonus_colors/2] : st->erase_gc); XFillRectangle (st->dpy, d, gc, st->width/2+(st->xoffset)-8, st->height/2+(st->yoffset*2)-1, 16, 3); XFillRectangle (st->dpy, d, gc, st->width/2+(st->xoffset)-1, st->height/2+(st->yoffset*2)-8, 3, 16); } } /* * move (step) * * move to the position for the next frame, and modify the state variables * st->nearest, pindex, pos, speed */ static void move (struct state *st) { double dpos; st->pos += st->step; dpos = SIGN3(st->pos) * floor(fabs(st->pos)); st->pindex += SIGN3(effective_speed) + INTERP; while (st->pindex >= INTERP) { st->nearest --; st->pindex -= INTERP; } while (st->pindex < 0) { st->nearest ++; st->pindex += INTERP; } st->nearest += dpos; MODULO(st->nearest, TERRAIN_LENGTH); st->pos -= dpos; st->accel = st->thrust + st->ycurvature[st->nearest] * st->gravity; st->speed += st->accel; if (st->speed > st->maxspeed) st->speed = st->maxspeed; if (st->speed < -st->maxspeed) st->speed = -st->maxspeed; } /* * speedmine (dpy, window) * * do everything required for one frame of the demo */ static unsigned long speedmine_draw (Display *dpy, Window window, void *closure) { struct state *st = (struct state *) closure; double elapsed, time_per_frame = 0.04; regenerate_terrain (st); perspective (st); render_speedmine (st, st->dbuf); if (st->dbuf != st->window) XCopyArea (st->dpy, st->dbuf, st->window, st->draw_gc, 0, 0, st->width, st->height, 0, 0); st->fps_end = get_time(st); st->nframes++; st->total_nframes++; if (st->fps_end > st->fps_start + 0.5) { elapsed = st->fps_end - st->fps_start; st->fps_start = get_time(st); time_per_frame = elapsed / st->nframes - st->delay*1e-6; st->fps = st->nframes / elapsed; if (DEBUG_FLAG) { printf ("%f s elapsed\t%3f s/frame\t%.1f FPS\n", elapsed, time_per_frame, st->fps); } st->step = effective_speed * elapsed; st->nframes = 0; } move (st); decrement_bonuses (st, time_per_frame); check_bonuses (st); return st->delay; } /* * speedmine_color_ramp (dpy, gcs, colors, ncolors, s1, s2, v1, v2) * * generate a color ramp of up to *ncolors between randomly chosen hues, * varying from saturation s1 to s2 and value v1 to v2, placing the colors * in 'colors' and creating corresponding GCs in 'gcs'. * * The number of colors actually allocated is returned in ncolors. */ static void speedmine_color_ramp (struct state *st, GC *gcs, XColor * colors, int *ncolors, double s1, double s2, double v1, double v2) { XGCValues gcv; int h1, h2; unsigned long flags; int i; assert (*st->ncolors >= 0); assert (s1 >= 0.0 && s1 <= 1.0 && v1 >= 0.0 && v2 <= 1.0); if (st->psychedelic_flag) { h1 = RAND(360); h2 = (h1 + 180) % 360; } else { h1 = h2 = RAND(360); } make_color_ramp (st->screen, st->visual, st->cmap, h1, s1, v1, h2, s2, v2, colors, ncolors, False, True, False); flags = GCForeground; for (i=0; i < *ncolors; i++) { gcv.foreground = colors[i].pixel; if (gcs[i]) XFreeGC (st->dpy, gcs[i]); gcs[i] = XCreateGC (st->dpy, st->dbuf, flags, &gcv); } } /* * change_colors () * * perform the color changing bonus. New colors are allocated for the * walls and bonuses, and if the 'psychedelic' option is set then new * colors are also chosen for the ground. */ static void change_colors (struct state *st) { double s1, s2; if (st->psychedelic_flag) { free_colors (st->screen, st->cmap, st->bonus_colors, st->nr_bonus_colors); free_colors (st->screen, st->cmap, st->wall_colors, st->nr_wall_colors); free_colors (st->screen, st->cmap, st->ground_colors, st->nr_ground_colors); s1 = 0.4; s2 = 0.9; st->ncolors = MAX_COLORS; speedmine_color_ramp (st, st->ground_gcs, st->ground_colors, &st->ncolors, 0.0, 0.8, 0.0, 0.9); st->nr_ground_colors = st->ncolors; } else { free_colors (st->screen, st->cmap, st->bonus_colors, st->nr_bonus_colors); free_colors (st->screen, st->cmap, st->wall_colors, st->nr_wall_colors); st->ncolors = st->nr_ground_colors; s1 = 0.0; s2 = 0.6; } st->ncolors = MAX_COLORS; speedmine_color_ramp (st, st->wall_gcs, st->wall_colors, &st->ncolors, s1, s2, 0.0, 0.9); st->nr_wall_colors = st->ncolors; st->ncolors = MAX_COLORS; speedmine_color_ramp (st, st->bonus_gcs, st->bonus_colors, &st->ncolors, 0.6, 0.9, 0.4, 1.0); st->nr_bonus_colors = st->ncolors; } /* * init_psychedelic_colors (dpy, window, cmap) * * initialise a psychedelic colormap */ static void init_psychedelic_colors (struct state *st) { XGCValues gcv; gcv.foreground = get_pixel_resource (st->dpy, st->cmap, "tunnelend", "TunnelEnd"); st->tunnelend_gc = XCreateGC (st->dpy, st->window, GCForeground, &gcv); st->ncolors = MAX_COLORS; speedmine_color_ramp (st, st->ground_gcs, st->ground_colors, &st->ncolors, 0.0, 0.8, 0.0, 0.9); st->nr_ground_colors = st->ncolors; st->ncolors = MAX_COLORS; speedmine_color_ramp (st, st->wall_gcs, st->wall_colors, &st->ncolors, 0.0, 0.6, 0.0, 0.9); st->nr_wall_colors = st->ncolors; st->ncolors = MAX_COLORS; speedmine_color_ramp (st, st->bonus_gcs, st->bonus_colors, &st->ncolors, 0.6, 0.9, 0.4, 1.0); st->nr_bonus_colors = st->ncolors; } /* * init_colors (dpy, window, cmap) * * initialise a normal colormap */ static void init_colors (struct state *st) { XGCValues gcv; XColor dark, light; int h1, h2; double s1, s2, v1, v2; unsigned long flags; int i; gcv.foreground = get_pixel_resource (st->dpy, st->cmap, "tunnelend", "TunnelEnd"); st->tunnelend_gc = XCreateGC (st->dpy, st->window, GCForeground, &gcv); st->ncolors = MAX_COLORS; dark.pixel = get_pixel_resource (st->dpy, st->cmap, "darkground", "DarkGround"); XQueryColor (st->dpy, st->cmap, &dark); light.pixel = get_pixel_resource (st->dpy, st->cmap, "lightground", "LightGround"); XQueryColor (st->dpy, st->cmap, &light); rgb_to_hsv (dark.red, dark.green, dark.blue, &h1, &s1, &v1); rgb_to_hsv (light.red, light.green, light.blue, &h2, &s2, &v2); make_color_ramp (st->screen, st->visual, st->cmap, h1, s1, v1, h2, s2, v2, st->ground_colors, &st->ncolors, False, True, False); st->nr_ground_colors = st->ncolors; flags = GCForeground; for (i=0; i < st->ncolors; i++) { gcv.foreground = st->ground_colors[i].pixel; st->ground_gcs[i] = XCreateGC (st->dpy, st->dbuf, flags, &gcv); } st->ncolors = MAX_COLORS; speedmine_color_ramp (st, st->wall_gcs, st->wall_colors, &st->ncolors, 0.0, 0.6, 0.0, 0.9); st->nr_wall_colors = st->ncolors; st->ncolors = MAX_COLORS; speedmine_color_ramp (st, st->bonus_gcs, st->bonus_colors, &st->ncolors, 0.6, 0.9, 0.4, 1.0); st->nr_bonus_colors = st->ncolors; } /* * print_stats () * * print out average FPS stats for the demo */ #if 0 static void print_stats (struct state *st) { if (st->total_nframes >= 1) printf ("Rendered %d frames averaging %f FPS\n", st->total_nframes, st->total_nframes / get_time(st)); } #endif /* * init_speedmine (dpy, window) * * initialise the demo */ static void * speedmine_init (Display *dpy, Window window) { struct state *st = (struct state *) calloc (1, sizeof(*st)); XGCValues gcv; XWindowAttributes xgwa; int i; double th; int wide; st->dpy = dpy; st->window = window; st->speed = 1.1; st->accel = 0.00000001; st->direction = FORWARDS; st->orientation = (17*ROTS)/22; XGetWindowAttributes (st->dpy, st->window, &xgwa); st->cmap = xgwa.colormap; st->visual = xgwa.visual; st->screen = xgwa.screen; st->width = xgwa.width; st->height = xgwa.height; st->verbose_flag = get_boolean_resource (st->dpy, "verbose", "Boolean"); # ifdef HAVE_JWXYZ /* Don't second-guess Quartz's double-buffering */ st->dbuf = st->window; #else st->dbuf = XCreatePixmap (st->dpy, st->window, st->width, st->height, xgwa.depth); #endif st->stars_mask = XCreatePixmap (st->dpy, st->window, st->width, st->height, 1); gcv.foreground = st->default_fg_pixel = get_pixel_resource (st->dpy, st->cmap, "foreground", "Foreground"); st->draw_gc = XCreateGC (st->dpy, st->window, GCForeground, &gcv); gcv.foreground = 1; st->stars_gc = XCreateGC (st->dpy, st->stars_mask, GCForeground, &gcv); gcv.foreground = get_pixel_resource (st->dpy, st->cmap, "background", "Background"); st->erase_gc = XCreateGC (st->dpy, st->dbuf, GCForeground, &gcv); gcv.foreground = 0; st->stars_erase_gc = XCreateGC (st->dpy, st->stars_mask, GCForeground, &gcv); st->wire_flag = get_boolean_resource (st->dpy, "wire", "Boolean"); st->psychedelic_flag = get_boolean_resource (st->dpy, "psychedelic", "Boolean"); st->delay = get_integer_resource(st->dpy, "delay", "Integer"); st->smoothness = get_integer_resource(st->dpy, "smoothness", "Integer"); if (st->smoothness < 1) st->smoothness = 1; st->maxspeed = get_float_resource(st->dpy, "maxspeed", "Float"); st->maxspeed *= 0.01; st->maxspeed = fabs(st->maxspeed); st->thrust = get_float_resource(st->dpy, "thrust", "Float"); st->thrust *= 0.2; st->gravity = get_float_resource(st->dpy, "gravity", "Float"); st->gravity *= 0.002/9.8; st->vertigo = get_float_resource(st->dpy, "vertigo", "Float"); st->vertigo *= 0.2; st->curviness = get_float_resource(st->dpy, "curviness", "Float"); st->curviness *= 0.25; st->twistiness = get_float_resource(st->dpy, "twistiness", "Float"); st->twistiness *= 0.125; st->terrain_flag = get_boolean_resource (st->dpy, "terrain", "Boolean"); st->widening_flag = get_boolean_resource (st->dpy, "widening", "Boolean"); st->bumps_flag = get_boolean_resource (st->dpy, "bumps", "Boolean"); st->bonuses_flag = get_boolean_resource (st->dpy, "bonuses", "Boolean"); st->crosshair_flag = get_boolean_resource (st->dpy, "crosshair", "Boolean"); st->be_wormy = get_boolean_resource (st->dpy, "worm", "Boolean"); if (st->be_wormy) { st->maxspeed *= 1.43; st->thrust *= 10; st->gravity *= 3; st->vertigo *= 0.5; st->smoothness *= 2; st->curviness *= 2; st->twistiness *= 2; st->psychedelic_flag = True; st->crosshair_flag = False; } if (st->psychedelic_flag) init_psychedelic_colors (st); else init_colors (st); for (i=0; icostab[i] = cos(th); st->sintab[i] = sin(th); } wide = random_wideness(); for (i=0; i < TERRAIN_LENGTH; i++) { st->wideness[i] = wide; st->bonuses[i] = 0; } init_terrain (st); init_curves (st); wrap_tunnel (st, 0, TERRAIN_LENGTH-1); #if 0 if (DEBUG_FLAG || st->verbose_flag) atexit(print_stats); #endif st->step = effective_speed; init_time (st); return st; } static void speedmine_reshape (Display *dpy, Window window, void *closure, unsigned int w, unsigned int h) { struct state *st = (struct state *) closure; st->width = w; st->height = h; if (st->dbuf != st->window) { XWindowAttributes xgwa; XGetWindowAttributes (st->dpy, st->window, &xgwa); XFreePixmap (dpy, st->dbuf); st->dbuf = XCreatePixmap (st->dpy, st->window, st->width, st->height, xgwa.depth); } } static Bool speedmine_event (Display *dpy, Window window, void *closure, XEvent *event) { return False; } static void speedmine_free (Display *dpy, Window window, void *closure) { struct state *st = (struct state *) closure; int i; XFreeGC (dpy, st->draw_gc); XFreeGC (dpy, st->erase_gc); XFreeGC (dpy, st->tunnelend_gc); XFreeGC (dpy, st->stars_gc); XFreeGC (dpy, st->stars_erase_gc); for (i = 0; i < MAX_COLORS; i++) { if (st->ground_gcs[i]) XFreeGC (dpy, (st->ground_gcs[i])); if (st->wall_gcs[i]) XFreeGC (dpy, (st->wall_gcs[i])); if (st->bonus_gcs[i]) XFreeGC (dpy, (st->bonus_gcs[i])); } free (st); } /* * Down the speedmine, you'll find speed * to satisfy your moving needs; * So if you're looking for a blast * then hit the speedmine, really fast. */ /* * Speedworm likes to choke and spit * and chase his tail, and dance a bit * he really is a funky friend; * he's made of speed from end to end. */ static const char *speedmine_defaults [] = { ".verbose: False", "*worm: False", "*wire: False", ".background: black", ".foreground: white", "*darkground: #101010", "*lightground: #a0a0a0", "*tunnelend: #000000", "*delay: 30000", "*maxspeed: 700", "*thrust: 1.0", "*gravity: 9.8", "*vertigo: 1.0", "*terrain: True", "*smoothness: 6", "*curviness: 1.0", "*twistiness: 1.0", "*widening: True", "*bumps: True", "*bonuses: True", "*crosshair: True", "*psychedelic: False", 0 }; static XrmOptionDescRec speedmine_options [] = { { "-verbose", ".verbose", XrmoptionNoArg, "True"}, { "-worm", ".worm", XrmoptionNoArg, "True"}, { "-wireframe", ".wire", XrmoptionNoArg, "True"}, { "-no-wireframe", ".wire", XrmoptionNoArg, "False"}, { "-darkground", ".darkground", XrmoptionSepArg, 0 }, { "-lightground", ".lightground", XrmoptionSepArg, 0 }, { "-tunnelend", ".tunnelend", XrmoptionSepArg, 0 }, { "-delay", ".delay", XrmoptionSepArg, 0 }, { "-maxspeed", ".maxspeed", XrmoptionSepArg, 0 }, { "-thrust", ".thrust", XrmoptionSepArg, 0 }, { "-gravity", ".gravity", XrmoptionSepArg, 0 }, { "-vertigo", ".vertigo", XrmoptionSepArg, 0 }, { "-terrain", ".terrain", XrmoptionNoArg, "True"}, { "-no-terrain", ".terrain", XrmoptionNoArg, "False"}, { "-smoothness", ".smoothness", XrmoptionSepArg, 0 }, { "-curviness", ".curviness", XrmoptionSepArg, 0 }, { "-twistiness", ".twistiness", XrmoptionSepArg, 0 }, { "-widening", ".widening", XrmoptionNoArg, "True"}, { "-no-widening", ".widening", XrmoptionNoArg, "False"}, { "-bumps", ".bumps", XrmoptionNoArg, "True"}, { "-no-bumps", ".bumps", XrmoptionNoArg, "False"}, { "-bonuses", ".bonuses", XrmoptionNoArg, "True"}, { "-no-bonuses", ".bonuses", XrmoptionNoArg, "False"}, { "-crosshair", ".crosshair", XrmoptionNoArg, "True"}, { "-no-crosshair", ".crosshair", XrmoptionNoArg, "False"}, { "-psychedelic", ".psychedelic", XrmoptionNoArg, "True"}, { "-no-psychedelic", ".psychedelic", XrmoptionNoArg, "False"}, { 0, 0, 0, 0 } }; XSCREENSAVER_MODULE ("SpeedMine", speedmine) /* vim: ts=4 */