source: trunk/third/xscreensaver/hacks/speedmine.c @ 20148

Revision 20148, 39.7 KB checked in by ghudson, 21 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r20147, which included commits to RCS files with non-trunk default branches.
Line 
1/* -*- Mode: C; c-basic-offset: 4; tab-width: 4 -*-
2 * speedmine, Copyright (C) 2001 Conrad Parker <conrad@deephackmode.org>
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation.  No representations are made about the suitability of this
9 * software for any purpose.  It is provided "as is" without express or
10 * implied warranty.
11 */
12
13/*
14 * Written mostly over the Easter holiday, 2001. Psychedelic option due to
15 * a night at Home nightclub, Sydney. Three all-nighters of solid partying
16 * were involved in the week this hack was written.
17 *
18 * Happy Birthday to WierdArms (17 April) and Pat (18 April)
19 */
20
21/*
22 * Hacking notes
23 *
24 * This program generates a rectangular terrain grid and maps this onto
25 * a semi-circular tunnel. The terrain has length TERRAIN_LENGTH, which
26 * corresponds to length along the tunnel, and breadth TERRAIN_BREADTH,
27 * which corresponds to circumference around the tunnel. For each frame,
28 * the tunnel is perspective mapped onto a set of X and Y screen values.
29 *
30 * Throughout this code the following temporary variable names are used:
31 *
32 *                      i       iterates along the tunnel in the direction of travel
33 *                      j       iterates around the tunnel clockwise
34 *                      t       iterates along the length of the perspective mapped values
35 *                              from the furthest to the nearest
36 *
37 * Thus, the buffers are used with these iterators:
38 *
39 *                      terrain[i][j]                                   terrain map
40 *                      worldx[i][j], worldy[i][j]              world coordinates (after wrapping)
41 *                      {x,y,z}curvature[i]                             tunnel curvature
42 *                      wideness[i]                                             tunnel wideness
43 *                      bonuses[i]                                              bonus values
44 *
45 *                      xvals[t][j], yvals[t][j]                screen coordinates
46 *                      {min,max}{x,y}[t]                               bounding boxes of screen coords
47 */
48
49/* Define or undefine NDEBUG to turn assert and abort debugging off or on */
50#define NDEBUG
51#include <assert.h>
52
53#include <math.h>
54
55#include "screenhack.h"
56#include "erase.h"
57
58#define MIN(a,b) ((a)<(b)?(a):(b))
59#define MAX(a,b) ((a)>(b)?(a):(b))
60
61#define RAND(r) (int)(((r)>0)?(random() % (long)(r)): -(random() % (long)(-r)))
62
63#define SIGN3(a) ((a)>0?1:((a)<0?-1:0))
64
65#define MODULO(a,b) while ((a)<0) (a)+=(b); (a) %= (b);
66
67static Display * display;
68static Pixmap dbuf, stars_mask;
69static Colormap cmap;
70static unsigned int default_fg_pixel;
71static GC draw_gc, erase_gc, tunnelend_gc, stars_gc, stars_erase_gc;
72
73/* No. of shades of each color (ground, walls, bonuses) */
74#define MAX_COLORS 32
75static int ncolors, nr_ground_colors, nr_wall_colors, nr_bonus_colors;
76static XColor ground_colors[MAX_COLORS], wall_colors[MAX_COLORS];
77static XColor bonus_colors[MAX_COLORS];
78static GC ground_gcs[MAX_COLORS], wall_gcs[MAX_COLORS], bonus_gcs[MAX_COLORS];
79
80static int be_wormy;
81
82static int width, height;
83static int delay;
84
85static int smoothness;
86static int verbose_flag;
87static int wire_flag;
88static int terrain_flag;
89static int widening_flag;
90static int bumps_flag;
91static int bonuses_flag;
92static int crosshair_flag;
93static int psychedelic_flag;
94
95#ifdef NDEBUG
96#define DEBUG_FLAG 0
97#else
98#define DEBUG_FLAG 1
99#endif
100
101static double maxspeed;
102
103static double thrust, gravity;
104
105static double vertigo;
106static double curviness;
107static double twistiness;
108
109static double pos=0.0;
110static double speed=-1.1;
111static double accel=0.00000001;
112static double step=0.0;
113
114
115#define FORWARDS 1
116#define BACKWARDS -1
117static int direction = FORWARDS;
118
119/* Apparently AIX's math.h bogusly defines `nearest' as a function,
120   in violation of the ANSI C spec. */
121#undef nearest
122#define nearest n3arest
123
124static int pindex=0, nearest=0;
125static int flipped_at=0;
126static int xoffset=0, yoffset=0;
127
128static int bonus_bright = 0;
129static int wire_bonus=0;
130#define wireframe (wire_flag||wire_bonus>8||wire_bonus%2==1)
131
132static double speed_bonus=0.0;
133#define effective_speed (direction*(speed+speed_bonus))
134
135static int spin_bonus = 0;
136static int backwards_bonus = 0;
137
138/* No. of levels of interpolation, for perspective */
139#define INTERP 32
140
141/* These must be powers of 2 */
142#define TERRAIN_LENGTH 256
143#define TERRAIN_BREADTH 32
144
145/* total "perspective distance" of terrain */
146#define TERRAIN_PDIST (INTERP*TERRAIN_LENGTH)
147
148#define ROTS 1024
149#define TB_MUL (ROTS/TERRAIN_BREADTH)
150static double sintab[ROTS], costab[ROTS];
151
152static int orientation = (17*ROTS)/22;
153
154static int terrain[TERRAIN_LENGTH][TERRAIN_BREADTH];
155static double xcurvature[TERRAIN_LENGTH];
156static double ycurvature[TERRAIN_LENGTH];
157static double zcurvature[TERRAIN_LENGTH];
158static int wideness[TERRAIN_LENGTH];
159static int bonuses[TERRAIN_LENGTH];
160static int xvals[TERRAIN_LENGTH][TERRAIN_BREADTH];
161static int yvals[TERRAIN_LENGTH][TERRAIN_BREADTH];
162static double worldx[TERRAIN_LENGTH][TERRAIN_BREADTH];
163static double worldy[TERRAIN_LENGTH][TERRAIN_BREADTH];
164static int minx[TERRAIN_LENGTH], maxx[TERRAIN_LENGTH];
165static int miny[TERRAIN_LENGTH], maxy[TERRAIN_LENGTH];
166
167#define random_elevation() (terrain_flag?(random() % 200):0)
168#define random_curvature() (curviness>0.0?((double)(random() % 40)-20)*curviness:0.0)
169#define random_twist() (twistiness>0.0?((double)(random() % 40)-20)*twistiness:0.0)
170#define random_wideness() (widening_flag?(int)(random() % 1200):0)
171
172#define STEEL_ELEVATION 300
173
174/* a forward declaration ... */
175static void change_colors(void);
176
177#if HAVE_GETTIMEOFDAY
178static int total_nframes = 0;
179static int nframes = 0;
180static double fps = 0.0;
181static double fps_start, fps_end;
182static struct timeval start_time;
183
184/*
185 * get_time ()
186 *
187 * returns the total time elapsed since the beginning of the demo
188 */
189static double get_time(void) {
190  struct timeval t;
191  float f;
192#if GETTIMEOFDAY_TWO_ARGS
193  gettimeofday(&t, NULL);
194#else
195  gettimeofday(&t);
196#endif
197  t.tv_sec -= start_time.tv_sec;
198  f = ((double)t.tv_sec) + t.tv_usec*1e-6;
199  return f;
200}
201
202/*
203 * init_time ()
204 *
205 * initialises the timing structures
206 */
207static void init_time(void) {
208#if GETTIMEOFDAY_TWO_ARGS
209  gettimeofday(&start_time, NULL);
210#else
211  gettimeofday(&start_time);
212#endif
213  fps_start = get_time();
214}
215#endif
216
217/*
218 * perspective()
219 *
220 * perspective map the world coordinates worldx[i][j], worldy[i][j] onto
221 * screen coordinates xvals[t][j], yvals[t][j]
222 */
223static void
224perspective (void)
225{
226        static int rotation_offset=0;
227
228        int i, j, jj, t=0, depth, view_pos;
229        int rotation_bias, r;
230        double xc=0.0, yc=0.0, zc=0.0;
231        double xcc=0.0, ycc=0.0, zcc=0.0;
232        double xx, yy;
233        double zfactor, zf;
234
235        zf = 8.0*28.0 / (double)(width*TERRAIN_LENGTH);
236        if (be_wormy) zf *= 3.0;
237
238        depth = TERRAIN_PDIST - INTERP + pindex;
239
240        view_pos = (nearest+3*TERRAIN_LENGTH/4)%TERRAIN_LENGTH;
241       
242        xoffset += - xcurvature[view_pos]*curviness/8;
243        xoffset /= 2;
244
245        yoffset += - ycurvature[view_pos]*curviness/4;
246        yoffset /= 2;
247
248        rotation_offset += (int)((zcurvature[view_pos]-zcurvature[nearest])*ROTS/8);
249        rotation_offset /= 2;
250        rotation_bias = orientation + spin_bonus - rotation_offset;
251
252        if (bumps_flag) {
253                if (be_wormy) {
254                        yoffset -= ((terrain[view_pos][TERRAIN_BREADTH/4] * width /(8*1600)));
255                        rotation_bias += (terrain[view_pos][TERRAIN_BREADTH/4+2] -
256                                                         terrain[view_pos][TERRAIN_BREADTH/4-2])/8;
257                } else {
258                        yoffset -= ((terrain[view_pos][TERRAIN_BREADTH/4] * width /(2*1600)));
259                        rotation_bias += (terrain[view_pos][TERRAIN_BREADTH/4+2] -
260                                                         terrain[view_pos][TERRAIN_BREADTH/4-2])/16;
261                }
262        }
263
264        MODULO(rotation_bias, ROTS);
265
266        for (t=0; t < TERRAIN_LENGTH; t++) {
267                i = nearest + t; MODULO(i, TERRAIN_LENGTH);
268                xc += xcurvature[i]; yc += ycurvature[i]; zc += zcurvature[i];
269                xcc += xc; ycc += yc; zcc += zc;
270                maxx[i] = maxy[i] = 0;
271                minx[i] = width; miny[i] = height;
272        }
273
274        for (t=0; t < TERRAIN_LENGTH; t++) {
275                i = nearest - 1 - t; MODULO(i, TERRAIN_LENGTH);
276
277                zfactor = (double)depth* (12.0 - TERRAIN_LENGTH/8.0) * zf;
278                for (j=0; j < TERRAIN_BREADTH; j++) {
279                        jj = direction * j; MODULO(jj, TERRAIN_BREADTH);
280            /* jwz: not totally sure if this is right, but it avoids div0 */
281            if (zfactor != 0) {
282                xx = (worldx[i][jj]-(vertigo*xcc))/zfactor;
283                yy = (worldy[i][j]-(vertigo*ycc))/zfactor;
284            } else {
285                xx = 0;
286                yy = 0;
287            }
288                        r = rotation_bias + (int)(vertigo*zcc); MODULO(r, ROTS);
289
290                        xvals[t][j] = xoffset + width/2 +
291                                          (int)(xx * costab[r] - yy * sintab[r]);
292                        maxx[t] = MAX(maxx[t], xvals[t][j]);
293                        minx[t] = MIN(minx[t], xvals[t][j]);
294
295                        yvals[t][j] = yoffset + height/2 +
296                                                  (int)(xx * sintab[r] + yy * costab[r]);
297                        maxy[t] = MAX(maxy[t], yvals[t][j]);
298                        miny[t] = MIN(miny[t], yvals[t][j]);
299                }
300                xcc -= xc; ycc -= yc; zcc -= zc;
301                xc -= xcurvature[i]; yc -= ycurvature[i]; zc -= zcurvature[i];
302                depth -= INTERP;
303        }
304}
305
306/*
307 * wrap_tunnel (start, end)
308 *
309 * wrap the terrain terrain[i][j] around the semi-circular tunnel function
310 *
311 *                      x' = x/2 * cos(theta) - (y-k) * x * sin(theta)
312 *                      y' = x/4 * sin(theta) + y * cos(theta)
313 *
314 * between i=start and i=end inclusive, producing world coordinates
315 * worldx[i][j], worldy[i][j]
316 */
317static void
318wrap_tunnel (int start, int end)
319{
320        int i, j, v;
321        double x, y;
322
323        assert (start < end);
324
325        for (i=start; i <= end; i++) {
326                for (j=0; j < TERRAIN_BREADTH; j++) {
327                        x = j * (1.0/TERRAIN_BREADTH);
328                        v = terrain[i][j];
329                        y = (double)(v==STEEL_ELEVATION?200:v) - wideness[i] - 1200;
330
331                        /* lower road */
332                        if (j > TERRAIN_BREADTH/8 && j < 3*TERRAIN_BREADTH/8) y -= 300;
333
334                    worldx[i][j] = x/2 * costab[j*TB_MUL] -
335                                                        (y-height/4.0)*x*sintab[j*TB_MUL];
336                        worldy[i][j] = x/4 * sintab[j*TB_MUL] +
337                                                        y * costab[j*TB_MUL];
338                }
339        }
340}
341
342/*
343 * flip_direction()
344 *
345 * perform the state transitions and terrain transformation for the
346 * "look backwards/look forwards" bonus
347 */
348static void
349flip_direction (void)
350{
351        int i, ip, in, j, t;
352
353        direction = -direction;
354
355        bonus_bright = 20;
356
357        for (i=0; i < TERRAIN_LENGTH; i++) {
358                in = nearest + i; MODULO(in, TERRAIN_BREADTH);
359                ip = nearest - i; MODULO(ip, TERRAIN_BREADTH);
360                for (j=0; j < TERRAIN_BREADTH; j++) {
361                        t = terrain[ip][j];
362                        terrain[ip][j] = terrain[in][j];
363                        terrain[in][j] = t;
364                }
365        }
366}
367
368/*
369 * generate_smooth (start, end)
370 *
371 * generate smooth terrain between i=start and i=end inclusive
372 */
373static void
374generate_smooth (int start, int end)
375{
376        int i,j, ii;
377
378        assert (start < end);
379
380        for (i=start; i <= end; i++) {
381                ii = i; MODULO(ii, TERRAIN_LENGTH);
382                for (j=0; j < TERRAIN_BREADTH; j++) {
383                        terrain[i][j] = STEEL_ELEVATION;
384                }
385        }
386}
387
388/*
389 * generate_straight (start, end)
390 *
391 * zero the curvature and wideness between i=start and i=end inclusive
392 */
393static void
394generate_straight (int start, int end)
395{
396        int i,j, ii;
397
398        assert (start < end);
399
400        for (i=start; i <= end; i++) {
401                ii = i; MODULO(ii, TERRAIN_LENGTH);
402                for (j=0; j < TERRAIN_BREADTH; j++) {
403                        xcurvature[ii] = 0;
404                        ycurvature[ii] = 0;
405                        zcurvature[ii] = 0;
406                        wideness[ii] = 0;
407                }
408        }
409}
410
411/*
412 * int generate_terrain_value (v1, v2, v3, v4, w)
413 *
414 * generate terrain value near the average of v1, v2, v3, v4, with
415 * perturbation proportional to w
416 */
417static int
418generate_terrain_value (int v1, int v2, int v3, int v4, int w)
419{
420        int sum, ret;
421        int rval;
422
423        if (!terrain_flag) return 0;
424
425        sum = v1 + v2 + v3 + v4;
426
427        rval = w*sum/smoothness;
428        if (rval == 0) rval = 2;
429
430        ret = (sum/4 -(rval/2) + RAND(rval));
431
432        if (ret < -400 || ret > 400) {
433                ret = sum/4;
434        }
435
436        return ret;
437}
438
439/*
440 * generate_terrain (start, end, final)
441 *
442 * generate terrain[i][j] between i=start and i=end inclusive
443 *
444 * This is performed by successive subdivision of the terrain into
445 * rectangles of decreasing size. Subdivision continues until the
446 * the minimum width or height of these rectangles is 'final'; ie.
447 * final=1 indicates to subdivide as far as possible, final=2 indicates
448 * to stop one subdivision before that (leaving a checkerboard pattern
449 * uncalculated) etc.
450 */
451static void
452generate_terrain (int start, int end, int final)
453{
454        int i,j,w,l;
455        int ip, jp, in, jn; /* prev, next values */
456        int diff;
457
458        assert (start < end);
459        assert (start >= 0 && start < TERRAIN_LENGTH);
460        assert (end >= 0 && end < TERRAIN_LENGTH);
461
462        diff = end - start + 1;
463
464        terrain[end][0] = random_elevation();
465        terrain[end][TERRAIN_BREADTH/2] = random_elevation();
466
467        for (w= diff/2, l=TERRAIN_BREADTH/4;
468             w >= final || l >= final; w /= 2, l /= 2) {
469
470                if (w<1) w=1; if (l<1) l=1;
471
472                for (i=start+w-1; i < end; i += (w*2)) {
473                        ip = i-w; MODULO(ip, TERRAIN_LENGTH);
474                        in = i+w; MODULO(in, TERRAIN_LENGTH);
475                        for (j=l-1; j < TERRAIN_BREADTH; j += (l*2)) {
476                                jp = j-1; MODULO(jp, TERRAIN_BREADTH);
477                                jn = j+1; MODULO(jn, TERRAIN_BREADTH);
478                                terrain[i][j] =
479                                        generate_terrain_value (terrain[ip][jp], terrain[in][jp],
480                                            terrain[ip][jn], terrain[in][jn], w);
481                        }
482                }
483
484                for (i=start+(w*2)-1; i < end; i += (w*2)) {
485                        ip = i-w; MODULO(ip, TERRAIN_LENGTH);
486                        in = i+w; MODULO(in, TERRAIN_LENGTH);
487                        for (j=l-1; j < TERRAIN_BREADTH; j += (l*2)) {
488                                jp = j-1; MODULO(jp, TERRAIN_BREADTH);
489                                jn = j+1; MODULO(jn, TERRAIN_BREADTH);
490                                terrain[i][j] =
491                                        generate_terrain_value (terrain[ip][j], terrain[in][j],
492                                            terrain[i][jp], terrain[i][jn], w);
493                        }
494                }
495
496                for (i=start+w-1; i < end; i += (w*2)) {
497                        ip = i-w; MODULO(ip, TERRAIN_LENGTH);
498                        in = i+w; MODULO(in, TERRAIN_LENGTH);
499                        for (j=2*l-1; j < TERRAIN_BREADTH; j += (l*2)) {
500                                jp = j-1; MODULO(jp, TERRAIN_BREADTH);
501                                jn = j+1; MODULO(jn, TERRAIN_BREADTH);
502                                terrain[i][j] =
503                                        generate_terrain_value (terrain[ip][j], terrain[in][j],
504                                            terrain[i][jp], terrain[i][jn], w);
505                        }
506                }
507        }
508}
509
510/*
511 * double generate_curvature_value (v1, v2, w)
512 *
513 * generate curvature value near the average of v1 and v2, with perturbation
514 * proportional to w
515 */
516static double
517generate_curvature_value (double v1, double v2, int w)
518{
519        double sum, avg, diff, ret;
520        int rval;
521
522        assert (!isnan(v1) && !isnan(v2));
523
524        sum = v1+v2;
525        avg = sum/2.0;
526
527        diff = MIN(v1 - avg, v2 - avg);
528
529        rval = (int)diff * w;
530        if (rval == 0.0) return avg;
531
532        ret = (avg -((double)rval)/500.0 + ((double)RAND(rval))/1000.0);
533
534        assert (!isnan(ret));
535
536        return ret;
537}
538
539/*
540 * generate_curves (start, end)
541 *
542 * generate xcurvature[i], ycurvature[i], zcurvature[i] and wideness[i]
543 * between start and end inclusive
544 */
545static void
546generate_curves (int start, int end)
547{
548        int i, diff, ii, in, ip, w;
549
550        assert (start < end);
551
552        diff = end - start + 1; MODULO (diff, TERRAIN_LENGTH);
553
554        if (random() % 100 == 0)
555          xcurvature[end] = 30 * random_curvature();
556        else if (random() % 10 == 0)
557          xcurvature[end] = 20 * random_curvature();
558        else
559          xcurvature[end] = 10 * random_curvature();
560
561        if (random() % 50 == 0)
562          ycurvature[end] = 20 * random_curvature();
563        else if (random() % 25 == 0)
564          ycurvature[end] = 30 * random_curvature();
565        else
566          ycurvature[end] = 10 * random_curvature();
567
568        if (random() % 3 == 0)
569          zcurvature[end] = random_twist();
570        else
571          zcurvature[end] =
572                          generate_curvature_value (zcurvature[end], random_twist(), 1);
573
574        if (be_wormy)
575                        wideness[end] = random_wideness();
576        else
577                wideness[end] =
578                        generate_curvature_value (wideness[end], random_wideness(), 1);
579
580    for (w=diff/2; w >= 1; w /= 2) {
581      for (i=start+w-1; i < end; i+=(w*2)) {
582        ii = i; MODULO (ii, TERRAIN_LENGTH);
583                ip = i-w; MODULO (ip, TERRAIN_LENGTH);
584            in = i+w; MODULO (in, TERRAIN_LENGTH);
585            xcurvature[ii] =
586                                generate_curvature_value (xcurvature[ip], xcurvature[in], w);
587            ycurvature[ii] =
588                                generate_curvature_value (ycurvature[ip], ycurvature[in], w);
589            zcurvature[ii] =
590                                generate_curvature_value (zcurvature[ip], zcurvature[in], w);
591            wideness[ii] =
592                                generate_curvature_value (wideness[ip], wideness[in], w);
593      }
594    }
595}
596
597/*
598 * do_bonus ()
599 *
600 * choose a random bonus and perform its state transition
601 */
602static void
603do_bonus (void)
604{
605        static int jamming=0;
606
607        bonus_bright = 20;
608
609        if (jamming > 0) {
610                jamming--;
611                nearest -= 2; MODULO(nearest, TERRAIN_LENGTH);
612                return;
613        }
614
615        if (psychedelic_flag) change_colors();
616
617        switch (random() % 7) {
618        case 0: /* switch to or from wireframe */
619                wire_bonus = (wire_bonus?0:300);
620                break;
621        case 1: /* speedup */
622                speed_bonus = 40.0;
623                break;
624        case 2:
625                spin_bonus += ROTS;
626                break;
627        case 3:
628                spin_bonus -= ROTS;
629                break;
630        case 4: /* look backwards / look forwards */
631                flipped_at = nearest;
632                flip_direction ();
633                backwards_bonus = (backwards_bonus?0:10);
634                break;
635        case 5:
636                change_colors();
637                break;
638        case 6: /* jam against the bonus a few times; deja vu! */
639                nearest -= 2; MODULO(nearest, TERRAIN_LENGTH);
640                jamming = 3;
641                break;
642        default:
643                assert(0);
644                break;
645        }
646}
647
648/*
649 * check_bonus ()
650 *
651 * check if a bonus has been passed in the last frame, and handle it
652 */
653static void
654check_bonuses (void)
655{
656        int i, ii, start, end;
657
658        if (!bonuses_flag) return;
659
660        if (step >= 0.0) {
661                start = nearest; end = nearest + (int)floor(step);
662        } else {
663                end = nearest; start = nearest + (int)floor(step);
664        }
665
666        if (be_wormy) {
667                start += TERRAIN_LENGTH/4;
668                end += TERRAIN_LENGTH/4;
669        }
670
671        for (i=start; i < end; i++) {
672                ii = i; MODULO(ii, TERRAIN_LENGTH);
673                if (bonuses[ii] == 1) do_bonus ();
674        }
675}
676
677/*
678 * decrement_bonuses (double time_per_frame)
679 *
680 * decrement timers associated with bonuses
681 */
682static void
683decrement_bonuses (double time_per_frame)
684{
685        if (!bonuses_flag) return;
686
687        if (bonus_bright > 0) bonus_bright-=4;
688        if (wire_bonus > 0) wire_bonus--;
689        if (speed_bonus > 0) speed_bonus -= 2.0;
690
691        if (spin_bonus > 10) spin_bonus -= (int)(step*13.7);
692        else if (spin_bonus < -10) spin_bonus += (int)(step*11.3);
693
694        if (backwards_bonus > 1) backwards_bonus--;
695        else if (backwards_bonus == 1) {
696                nearest += 2*(MAX(flipped_at, nearest) - MIN(flipped_at,nearest));
697                MODULO(nearest, TERRAIN_LENGTH);
698                flip_direction ();
699                backwards_bonus = 0;
700        }
701}
702
703/*
704 * set_bonuses (start, end)
705 *
706 * choose if to and where to set a bonus between i=start and i=end inclusive
707 */
708static void
709set_bonuses (int start, int end)
710{
711        int i, diff, ii;
712
713        if (!bonuses_flag) return;
714
715        assert (start < end);
716
717        diff = end - start;
718
719        for (i=start; i <= end; i++) {
720                ii = i; if (ii>=TERRAIN_LENGTH) ii -= TERRAIN_LENGTH;
721                bonuses[ii] = 0;
722        }
723        if (random() % 4 == 0) {
724                i = start + RAND(diff-3);
725                ii = i; if (ii>=TERRAIN_LENGTH) ii -= TERRAIN_LENGTH;
726                bonuses[ii] = 2; /* marker */
727                ii = i+3; if (ii>=TERRAIN_LENGTH) ii -= TERRAIN_LENGTH;
728                bonuses[ii] = 1; /* real thing */
729        }
730}
731
732/*
733 * regenerate_terrain ()
734 *
735 * regenerate a portion of the terrain map of length TERRAIN_LENGTH/4 iff
736 * we just passed between two quarters of the terrain.
737 *
738 * choose the kind of terrain to produce, produce it and wrap the tunnel
739 */
740static void
741regenerate_terrain (void)
742{
743        int start, end;
744        int passed;
745
746        passed = nearest % (TERRAIN_LENGTH/4);
747
748        if (speed == 0.0 ||
749                (speed > 0.0 && passed > (int)step) ||
750                (speed < 0.0 && (TERRAIN_LENGTH/4)-passed > (int)fabs(step))) {
751
752                return;
753        }
754
755        end = nearest - passed - 1; MODULO(end, TERRAIN_LENGTH);
756        start = end - TERRAIN_LENGTH/4 + 1; MODULO(start, TERRAIN_LENGTH);
757
758        if (DEBUG_FLAG) printf ("Regenerating [%d - %d]\n", start, end);
759
760        set_bonuses (start, end);
761
762        switch (random() % 64) {
763        case 0:
764        case 1:
765                generate_terrain (start, end, 1);
766                generate_smooth (start,
767                        start + TERRAIN_LENGTH/8 + (random() % TERRAIN_LENGTH/8));
768                break;
769        case 2:
770                generate_smooth (start, end);
771                generate_terrain (start, end, 4); break;
772        case 3:
773                generate_smooth (start, end);
774                generate_terrain (start, end, 2); break;
775        default:
776                generate_terrain (start, end, 1);
777        }
778
779        if (random() % 16 == 0) {
780                generate_straight (start, end);
781        } else {
782                generate_curves (start, end);
783        }
784
785        wrap_tunnel (start, end);
786}
787
788/*
789 * init_terrain ()
790 *
791 * initialise the terrain map for the beginning of the demo
792 */
793static void
794init_terrain (void)
795{
796        int i, j;
797
798        for (i=0; i < TERRAIN_LENGTH; i++) {
799                for (j=0; j < TERRAIN_BREADTH; j++) {
800                        terrain[i][j] = 0;
801                }
802        }
803
804        terrain[TERRAIN_LENGTH-1][0] =  - (random() % 300);
805        terrain[TERRAIN_LENGTH-1][TERRAIN_BREADTH/2] =  - (random() % 300);
806
807        generate_smooth (0, TERRAIN_LENGTH-1);
808        generate_terrain (0, TERRAIN_LENGTH/4 -1, 4);
809        generate_terrain (TERRAIN_LENGTH/4, TERRAIN_LENGTH/2 -1, 2);
810        generate_terrain (TERRAIN_LENGTH/2, 3*TERRAIN_LENGTH/4 -1, 1);
811        generate_smooth (3*TERRAIN_LENGTH/4, TERRAIN_LENGTH-1);
812}
813
814/*
815 * init_curves ()
816 *
817 * initialise the curvatures and wideness for the beginning of the demo.
818 */
819static void
820init_curves (void)
821{
822        int i;
823
824        for (i=0; i < TERRAIN_LENGTH-1; i++) {
825        xcurvature[i] = 0.0;
826            ycurvature[i] = 0.0;
827                zcurvature[i] = 0.0;
828        }
829
830    xcurvature[TERRAIN_LENGTH-1] = random_curvature();
831    ycurvature[TERRAIN_LENGTH-1] = random_curvature();
832    zcurvature[TERRAIN_LENGTH-1] = random_twist();
833
834        generate_straight (0, TERRAIN_LENGTH/4-1);
835        generate_curves (TERRAIN_LENGTH/4, TERRAIN_LENGTH/2-1);
836        generate_curves (TERRAIN_LENGTH/2, 3*TERRAIN_LENGTH/4-1);
837        generate_straight (3*TERRAIN_LENGTH/4, TERRAIN_LENGTH-1);
838
839}
840
841/*
842 * render_quads (dpy, d, t, dt, i)
843 *
844 * renders the quadrilaterals from perspective depth t to t+dt.
845 * i is passed as a hint, where i corresponds to t as asserted.
846 */
847static void
848render_quads (Display * dpy, Drawable d, int t, int dt, int i)
849{
850        int j, t2, j2, in;
851        int index;
852        XPoint points[4];
853        GC gc;
854
855        assert (i == (nearest - (t + dt) + TERRAIN_LENGTH) % TERRAIN_LENGTH);
856
857        in = i + 1; MODULO(in, TERRAIN_LENGTH);
858
859        for (j=0; j < TERRAIN_BREADTH; j+=dt) {
860                t2 = t+dt; if (t2 >= TERRAIN_LENGTH) t2 -= TERRAIN_LENGTH;
861                j2 = j+dt; if (j2 >= TERRAIN_BREADTH) j2 -= TERRAIN_BREADTH;
862                points[0].x = xvals[t][j]; points[0].y = yvals[t][j];
863                points[1].x = xvals[t2][j]; points[1].y = yvals[t2][j];
864                points[2].x = xvals[t2][j2]; points[2].y = yvals[t2][j2];
865                points[3].x = xvals[t][j2]; points[3].y = yvals[t][j2];
866
867            index = bonus_bright + ncolors/3 +
868                                t*(t*INTERP + pindex) * ncolors /
869                            (3*TERRAIN_LENGTH*TERRAIN_PDIST);
870                if (!wireframe) {
871                        index += (int)((points[0].y - points[3].y) / 8);
872                        index += (int)((worldx[i][j] - worldx[in][j]) / 40);
873                        index += (int)((terrain[in][j] - terrain[i][j]) / 100);
874                }
875                if (be_wormy && psychedelic_flag) index += ncolors/4;
876
877                index = MIN (index, ncolors-1);
878                index = MAX (index, 0);
879
880                if (bonuses[i]) {
881                        XSetClipMask (dpy, bonus_gcs[index], None);
882                }
883
884                if (wireframe) {
885                        if (bonuses[i]) gc = bonus_gcs[index];
886                        else gc = ground_gcs[index];
887                        XDrawLines (dpy, d, gc, points, 4, CoordModeOrigin);
888                } else {
889                        if (bonuses[i])
890                                gc = bonus_gcs[index];
891                        else if ((direction>0 && j < TERRAIN_BREADTH/8) ||
892                                (j > TERRAIN_BREADTH/8 && j < 3*TERRAIN_BREADTH/8-1) ||
893                                (direction < 0 && j > 3*TERRAIN_BREADTH/8-1 &&
894                                        j < TERRAIN_BREADTH/2) ||
895                                terrain[i][j] == STEEL_ELEVATION ||
896                                wideness[in] - wideness[i] > 200)
897                                gc = ground_gcs[index];
898                        else
899                                gc = wall_gcs[index];
900
901                        XFillPolygon (dpy, d, gc, points, 4, Nonconvex, CoordModeOrigin);
902                }
903        }
904}
905
906/*
907 * render_pentagons (dpy, d, t, dt, i)
908 *
909 * renders the pentagons from perspective depth t to t+dt.
910 * i is passed as a hint, where i corresponds to t as asserted.
911 */
912static void
913render_pentagons (Display *dpy, Drawable d, int t, int dt, int i)
914{
915        int j, t2, j2, j3, in;
916        int index;
917        XPoint points[5];
918        GC gc;
919
920        assert (i == (nearest -t + TERRAIN_LENGTH) % TERRAIN_LENGTH);
921
922        in = i + 1; MODULO(in, TERRAIN_LENGTH);
923
924        for (j=0; j < TERRAIN_BREADTH; j+=dt*2) {
925                t2 = t+(dt*2); if (t2 >= TERRAIN_LENGTH) t2 -= TERRAIN_LENGTH;
926                j2 = j+dt; if (j2 >= TERRAIN_BREADTH) j2 -= TERRAIN_BREADTH;
927                j3 = j+dt+dt; if (j3 >= TERRAIN_BREADTH) j3 -= TERRAIN_BREADTH;
928                points[0].x = xvals[t][j]; points[0].y = yvals[t][j];
929                points[1].x = xvals[t2][j]; points[1].y = yvals[t2][j];
930                points[2].x = xvals[t2][j2]; points[2].y = yvals[t2][j2];
931                points[3].x = xvals[t2][j3]; points[3].y = yvals[t2][j3];
932                points[4].x = xvals[t][j3]; points[4].y = yvals[t][j3];
933
934            index = bonus_bright + ncolors/3 +
935                                t*(t*INTERP + pindex) * ncolors /
936                            (3*TERRAIN_LENGTH*TERRAIN_PDIST);
937                if (!wireframe) {
938                        index += (int)((points[0].y - points[3].y) / 8);
939                        index += (int)((worldx[i][j] - worldx[in][j]) / 40);
940                        index += (int)((terrain[in][j] - terrain[i][j]) / 100);
941                }
942                if (be_wormy && psychedelic_flag) index += ncolors/4;
943
944                index = MIN (index, ncolors-1);
945                index = MAX (index, 0);
946
947                if (bonuses[i]) {
948                        XSetClipMask (dpy, bonus_gcs[index], None);
949                }
950
951                if (wireframe) {
952                        if (bonuses[i]) gc = bonus_gcs[index];
953                        else gc = ground_gcs[index];
954                        XDrawLines (dpy, d, gc, points, 5, CoordModeOrigin);
955                } else {
956                        if (bonuses[i])
957                                gc = bonus_gcs[index];
958                        else if (j < TERRAIN_BREADTH/8 ||
959                                (j > TERRAIN_BREADTH/8 && j < 3*TERRAIN_BREADTH/8-1) ||
960                                terrain[i][j] == STEEL_ELEVATION ||
961                                wideness[in] - wideness[i] > 200)
962                                gc = ground_gcs[index];
963                        else
964                                gc = wall_gcs[index];
965
966                        XFillPolygon (dpy, d, gc, points, 5, Complex, CoordModeOrigin);
967                }
968        }
969}
970
971/*
972 * render_block (dpy, d, gc, t)
973 *
974 * render a filled polygon at perspective depth t using the given GC
975 */
976static void
977render_block (Display * dpy, Drawable d, GC gc, int t)
978{
979        int i;
980
981        XPoint erase_points[TERRAIN_BREADTH/2];
982
983        for (i=0; i < TERRAIN_BREADTH/2; i++) {
984                erase_points[i].x = xvals[t][i*2];
985                erase_points[i].y = yvals[t][i*2];
986        }
987
988        XFillPolygon (dpy, d, gc, erase_points,
989                                  TERRAIN_BREADTH/2, Complex, CoordModeOrigin);
990}
991
992/*
993 * regenerate_stars_mask (dpy, t)
994 *
995 * regenerate the clip mask 'stars_mask' for drawing the bonus stars at
996 * random positions within the bounding box at depth t
997 */
998static void
999regenerate_stars_mask (Display * dpy, int t)
1000{
1001        int i, w, h, a, b, l1, l2;
1002        const int lim = width*TERRAIN_LENGTH/(300*(TERRAIN_LENGTH-t));
1003
1004        w = maxx[t] - minx[t];
1005        h = maxy[t] - miny[t];
1006
1007        if (w<6||h<6) return;
1008
1009        XFillRectangle (dpy, stars_mask, stars_erase_gc,
1010                                        0, 0, width, height);
1011
1012        l1 = (t>3*TERRAIN_LENGTH/4?2:1);
1013        l2 = (t>7*TERRAIN_LENGTH/8?2:1);
1014
1015        for (i=0; i < lim; i++) {
1016                a = RAND(w); b = RAND(h);
1017                XDrawLine (dpy, stars_mask, stars_gc,
1018                                        minx[t]+a-l1, miny[t]+b, minx[t]+a+l1, miny[t]+b);
1019                XDrawLine (dpy, stars_mask, stars_gc,
1020                                        minx[t]+a, miny[t]+b-l1, minx[t]+a, miny[t]+b+l1);
1021        }
1022        for (i=0; i < lim; i++) {
1023                a = RAND(w); b = RAND(h);
1024                XDrawLine (dpy, stars_mask, stars_gc,
1025                                        minx[t]+a-l2, miny[t]+b, minx[t]+a+l2, miny[t]+b);
1026                XDrawLine (dpy, stars_mask, stars_gc,
1027                                        minx[t]+a, miny[t]+b-l2, minx[t]+a, miny[t]+b+l2);
1028        }
1029}
1030
1031/*
1032 * render_bonus_block (dpy, d, t, i)
1033 *
1034 * draw the bonus stars at depth t.
1035 * i is passed as a hint, where i corresponds to t as asserted.
1036 */
1037static void
1038render_bonus_block (Display * dpy, Drawable d, int t, int i)
1039{
1040        int bt;
1041
1042        assert (i == (nearest -t + TERRAIN_LENGTH) % TERRAIN_LENGTH);
1043
1044        if (!bonuses[i] || wireframe) return;
1045
1046        regenerate_stars_mask (dpy, t);
1047
1048        bt = t * nr_bonus_colors / (2*TERRAIN_LENGTH);
1049
1050        XSetClipMask (dpy, bonus_gcs[bt], stars_mask);
1051
1052        render_block (dpy, d, bonus_gcs[bt], t);
1053}
1054
1055static int
1056begin_at (void)
1057{
1058        int t;
1059        int max_minx=0, min_maxx=width, max_miny=0, min_maxy=height;
1060
1061        for (t=TERRAIN_LENGTH-1; t > 0; t--) {
1062                max_minx = MAX (max_minx, minx[t]);
1063                min_maxx = MIN (min_maxx, maxx[t]);
1064                max_miny = MAX (max_miny, miny[t]);
1065                min_maxy = MIN (min_maxy, maxy[t]);
1066
1067                if (max_miny >= min_maxy || max_minx >= min_maxx) break;
1068        }
1069
1070        return t;
1071}
1072
1073/*
1074 * render_speedmine (dpy, d)
1075 *
1076 * render the current frame.
1077 */
1078static void
1079render_speedmine (Display * dpy, Drawable d)
1080{
1081        int t, i=nearest, dt=1;
1082        GC gc;
1083
1084        assert (nearest >= 0 && nearest < TERRAIN_LENGTH);
1085
1086        if (be_wormy || wireframe) {
1087                XFillRectangle (dpy, d, erase_gc, 0, 0, width, height);
1088
1089                dt=4;
1090                for (t=0; t < TERRAIN_LENGTH/4; t+=dt) {
1091                        render_bonus_block (dpy, d, t, i);
1092                        i -= dt; MODULO(i, TERRAIN_LENGTH);
1093                        render_quads (dpy, d, t, dt, i);
1094                }
1095
1096                assert (t == TERRAIN_LENGTH/4);
1097        } else {
1098                t = MAX(begin_at(), TERRAIN_LENGTH/4);
1099                /*t = TERRAIN_LENGTH/4; dt = 2; */
1100                dt = (t >= 3*TERRAIN_LENGTH/4 ? 1 : 2);
1101                i = (nearest -t + TERRAIN_LENGTH) % TERRAIN_LENGTH;
1102                render_block (dpy, d, tunnelend_gc, t);
1103        }
1104
1105        dt=2;
1106
1107        if (t == TERRAIN_LENGTH/4)
1108                render_pentagons (dpy, d, t, dt, i);
1109
1110        for (; t < 3*TERRAIN_LENGTH/4; t+=dt) {
1111                render_bonus_block (dpy, d, t, i);
1112                i -= dt; MODULO(i, TERRAIN_LENGTH);
1113                render_quads (dpy, d, t, dt, i);
1114        }
1115
1116        dt=1;
1117        if (be_wormy) {
1118                for (; t < TERRAIN_LENGTH-(1+(pindex<INTERP/2)); t+=dt) {
1119                        render_bonus_block (dpy, d, t, i);
1120                        i -= dt; MODULO(i, TERRAIN_LENGTH);
1121                }
1122        } else {
1123                if (wireframe) {assert (t == 3*TERRAIN_LENGTH/4);}
1124
1125                if (t == 3*TERRAIN_LENGTH/4)
1126                        render_pentagons (dpy, d, t, dt, i);
1127
1128                for (; t < TERRAIN_LENGTH-(1+(pindex<INTERP/2)); t+=dt) {
1129                        render_bonus_block (dpy, d, t, i);
1130                        i -= dt; MODULO(i, TERRAIN_LENGTH);
1131                        render_quads (dpy, d, t, dt, i);
1132                }
1133        }
1134
1135        /* Draw crosshair */
1136        if (crosshair_flag) {
1137                gc = (wireframe ? bonus_gcs[nr_bonus_colors/2] : erase_gc);
1138                XFillRectangle (dpy, d, gc,
1139                                                width/2+(xoffset)-8, height/2+(yoffset*2)-1, 16, 3);
1140                XFillRectangle (dpy, d, gc,
1141                                                width/2+(xoffset)-1, height/2+(yoffset*2)-8, 3, 16);
1142        }
1143
1144}
1145
1146/*
1147 * move (step)
1148 *
1149 * move to the position for the next frame, and modify the state variables
1150 * nearest, pindex, pos, speed
1151 */
1152static void
1153move (double step)
1154{
1155        double dpos;
1156
1157        pos += step;
1158        dpos = SIGN3(pos) * floor(fabs(pos));
1159
1160        pindex += SIGN3(effective_speed) + INTERP;
1161        while (pindex >= INTERP) {
1162                nearest --;
1163                pindex -= INTERP;
1164        }
1165        while (pindex < 0) {
1166                nearest ++;
1167                pindex += INTERP;
1168        }
1169
1170    nearest += dpos; MODULO(nearest, TERRAIN_LENGTH);
1171
1172        pos -= dpos;
1173
1174        accel = thrust + ycurvature[nearest] * gravity;
1175        speed += accel;
1176        if (speed > maxspeed) speed = maxspeed;
1177        if (speed < -maxspeed) speed = -maxspeed;
1178}
1179
1180/*
1181 * speedmine (dpy, window)
1182 *
1183 * do everything required for one frame of the demo
1184 */
1185static void
1186speedmine (Display *dpy, Window window)
1187{
1188        double elapsed, time_per_frame = 0.04;
1189
1190        regenerate_terrain ();
1191
1192        perspective ();
1193
1194        render_speedmine (dpy, dbuf);
1195        XCopyArea (dpy, dbuf, window, draw_gc, 0, 0, width, height, 0, 0);
1196
1197#if HAVE_GETTIMEOFDAY
1198        fps_end = get_time();
1199        nframes++;
1200        total_nframes++;
1201
1202        if (fps_end > fps_start + 0.5) {
1203                elapsed = fps_end - fps_start;
1204                fps_start = get_time();
1205
1206                time_per_frame = elapsed / nframes - delay*1e-6;
1207                fps = nframes / elapsed;
1208                if (DEBUG_FLAG) {
1209                        printf ("%f s elapsed\t%3f s/frame\t%.1f FPS\n", elapsed,
1210                                        time_per_frame, fps);
1211                }
1212                step = effective_speed * elapsed;
1213
1214                nframes = 0;
1215        }
1216#else
1217        time_per_frame = 0.04;
1218        step = effective_speed;
1219#endif
1220
1221        move (step);
1222
1223        decrement_bonuses (time_per_frame);
1224
1225        check_bonuses ();
1226}
1227
1228/*
1229 * speedmine_color_ramp (dpy, cmap, gcs, colors, ncolors, s1, s2, v1, v2)
1230 *
1231 * generate a color ramp of up to *ncolors between randomly chosen hues,
1232 * varying from saturation s1 to s2 and value v1 to v2, placing the colors
1233 * in 'colors' and creating corresponding GCs in 'gcs'.
1234 *
1235 * The number of colors actually allocated is returned in ncolors.
1236 */
1237static void
1238speedmine_color_ramp (Display * dpy, Colormap cmap, GC *gcs, XColor * colors,
1239                                         int *ncolors, double s1, double s2, double v1, double v2)
1240{
1241        XGCValues gcv;
1242        int h1, h2;
1243        unsigned long flags;
1244        int i;
1245
1246        assert (*ncolors >= 0);
1247        assert (s1 >= 0.0 && s1 <= 1.0 && v1 >= 0.0 && v2 <= 1.0);
1248
1249        if (psychedelic_flag) {
1250                h1 = RAND(360); h2 = (h1 + 180) % 360;
1251        } else {
1252                h1 = h2 = RAND(360);
1253        }
1254
1255        make_color_ramp (dpy, cmap, h1, s1, v1, h2, s2, v2,
1256                                     colors, ncolors, False, True, False);
1257
1258        flags = GCForeground;
1259        for (i=0; i < *ncolors; i++) {
1260                gcv.foreground = colors[i].pixel;
1261                gcs[i] = XCreateGC (dpy, dbuf, flags, &gcv);
1262        }
1263
1264}
1265
1266/*
1267 * change_colors ()
1268 *
1269 * perform the color changing bonus. New colors are allocated for the
1270 * walls and bonuses, and if the 'psychedelic' option is set then new
1271 * colors are also chosen for the ground.
1272 */
1273static void
1274change_colors (void)
1275{
1276        double s1, s2;
1277
1278        if (psychedelic_flag) {
1279                free_colors (display, cmap, bonus_colors, nr_bonus_colors);
1280                free_colors (display, cmap, wall_colors, nr_wall_colors);
1281                free_colors (display, cmap, ground_colors, nr_ground_colors);
1282                ncolors = MAX_COLORS;
1283
1284                s1 = 0.4; s2 = 0.9;
1285
1286                speedmine_color_ramp (display, cmap, ground_gcs, ground_colors,
1287                                                          &ncolors, 0.0, 0.8, 0.0, 0.9);
1288                nr_ground_colors = ncolors;
1289        } else {
1290                free_colors (display, cmap, bonus_colors, nr_bonus_colors);
1291                free_colors (display, cmap, wall_colors, nr_wall_colors);
1292                ncolors = nr_ground_colors;
1293
1294                s1 = 0.0; s2 = 0.6;
1295        }
1296
1297    speedmine_color_ramp (display, cmap, wall_gcs, wall_colors, &ncolors,
1298                                                  s1, s2, 0.0, 0.9);
1299    nr_wall_colors = ncolors;
1300
1301    speedmine_color_ramp (display, cmap, bonus_gcs, bonus_colors, &ncolors,
1302                                                  0.6, 0.9, 0.4, 1.0);
1303        nr_bonus_colors = ncolors;
1304}
1305
1306/*
1307 * init_psychedelic_colors (dpy, window, cmap)
1308 *
1309 * initialise a psychedelic colormap
1310 */
1311static void
1312init_psychedelic_colors (Display * dpy, Window window, Colormap cmap)
1313{
1314  XGCValues gcv;
1315
1316  gcv.foreground = get_pixel_resource ("tunnelend", "TunnelEnd", dpy, cmap);
1317  tunnelend_gc = XCreateGC (dpy, window, GCForeground, &gcv);
1318
1319  ncolors = MAX_COLORS;
1320
1321  speedmine_color_ramp (dpy, cmap, ground_gcs, ground_colors, &ncolors,
1322                                                0.0, 0.8, 0.0, 0.9);
1323  nr_ground_colors = ncolors;
1324
1325  speedmine_color_ramp (dpy, cmap, wall_gcs, wall_colors, &ncolors,
1326                                                0.0, 0.6, 0.0, 0.9);
1327  nr_wall_colors = ncolors;
1328
1329  speedmine_color_ramp (dpy, cmap, bonus_gcs, bonus_colors, &ncolors,
1330                                                0.6, 0.9, 0.4, 1.0);
1331  nr_bonus_colors = ncolors;
1332}
1333
1334/*
1335 * init_colors (dpy, window, cmap)
1336 *
1337 * initialise a normal colormap
1338 */
1339static void
1340init_colors (Display * dpy, Window window, Colormap cmap)
1341{
1342  XGCValues gcv;
1343  XColor dark, light;
1344  int h1, h2;
1345  double s1, s2, v1, v2;
1346  unsigned long flags;
1347  int i;
1348
1349  gcv.foreground = get_pixel_resource ("tunnelend", "TunnelEnd", dpy, cmap);
1350  tunnelend_gc = XCreateGC (dpy, window, GCForeground, &gcv);
1351
1352  ncolors = MAX_COLORS;
1353
1354  dark.pixel = get_pixel_resource ("darkground", "DarkGround", dpy, cmap);
1355  XQueryColor (dpy, cmap, &dark);
1356
1357  light.pixel = get_pixel_resource ("lightground", "LightGround", dpy, cmap);
1358  XQueryColor (dpy, cmap, &light);
1359
1360  rgb_to_hsv (dark.red, dark.green, dark.blue, &h1, &s1, &v1);
1361  rgb_to_hsv (light.red, light.green, light.blue, &h2, &s2, &v2);
1362  make_color_ramp (dpy, cmap, h1, s1, v1, h2, s2, v2,
1363                                  ground_colors, &ncolors, False, True, False);
1364  nr_ground_colors = ncolors;
1365
1366  flags = GCForeground;
1367  for (i=0; i < ncolors; i++) {
1368        gcv.foreground = ground_colors[i].pixel;
1369        ground_gcs[i] = XCreateGC (dpy, dbuf, flags, &gcv);
1370  }
1371
1372  speedmine_color_ramp (dpy, cmap, wall_gcs, wall_colors, &ncolors,
1373                                                0.0, 0.6, 0.0, 0.9);
1374  nr_wall_colors = ncolors;
1375
1376  speedmine_color_ramp (dpy, cmap, bonus_gcs, bonus_colors, &ncolors,
1377                                                0.6, 0.9, 0.4, 1.0);
1378  nr_bonus_colors = ncolors;
1379}
1380
1381/*
1382 * print_stats ()
1383 *
1384 * print out average FPS stats for the demo
1385 */
1386static void
1387print_stats (void)
1388{
1389        if (total_nframes >= 1)
1390                printf ("Rendered %d frames averaging %f FPS\n", total_nframes,
1391                                total_nframes / get_time());
1392}
1393
1394/*
1395 * init_speedmine (dpy, window)
1396 *
1397 * initialise the demo
1398 */
1399static void
1400init_speedmine (Display *dpy, Window window)
1401{
1402  XGCValues gcv;
1403  XWindowAttributes xgwa;
1404  int i;
1405  double th;
1406  int wide;
1407
1408  display = dpy;
1409
1410  XGetWindowAttributes (dpy, window, &xgwa);
1411  cmap = xgwa.colormap;
1412  width = xgwa.width;
1413  height = xgwa.height;
1414
1415  verbose_flag = get_boolean_resource ("verbose", "Boolean");
1416
1417  dbuf = XCreatePixmap (dpy, window, width, height, xgwa.depth);
1418  stars_mask = XCreatePixmap (dpy, window, width, height, 1);
1419
1420  gcv.foreground = default_fg_pixel =
1421    get_pixel_resource ("foreground", "Foreground", dpy, cmap);
1422  draw_gc = XCreateGC (dpy, window, GCForeground, &gcv);
1423  stars_gc = XCreateGC (dpy, stars_mask, GCForeground, &gcv);
1424
1425  gcv.foreground = get_pixel_resource ("background", "Background", dpy, cmap);
1426  erase_gc = XCreateGC (dpy, dbuf, GCForeground, &gcv);
1427  stars_erase_gc = XCreateGC (dpy, stars_mask, GCForeground, &gcv);
1428
1429  wire_flag = get_boolean_resource ("wire", "Boolean");
1430
1431  psychedelic_flag = get_boolean_resource ("psychedelic", "Boolean");
1432
1433  delay = get_integer_resource("delay", "Integer");
1434
1435  smoothness = get_integer_resource("smoothness", "Integer");
1436  if (smoothness < 1) smoothness = 1;
1437
1438  maxspeed = get_float_resource("maxspeed", "Float");
1439  maxspeed *= 0.01;
1440  maxspeed = fabs(maxspeed);
1441
1442  thrust = get_float_resource("thrust", "Float");
1443  thrust *= 0.2;
1444
1445  gravity = get_float_resource("gravity", "Float");
1446  gravity *= 0.002/9.8;
1447
1448  vertigo = get_float_resource("vertigo", "Float");
1449  vertigo *= 0.2;
1450
1451  curviness = get_float_resource("curviness", "Float");
1452  curviness *= 0.25;
1453
1454  twistiness = get_float_resource("twistiness", "Float");
1455  twistiness *= 0.125;
1456
1457  terrain_flag = get_boolean_resource ("terrain", "Boolean");
1458  widening_flag = get_boolean_resource ("widening", "Boolean");
1459  bumps_flag = get_boolean_resource ("bumps", "Boolean");
1460  bonuses_flag = get_boolean_resource ("bonuses", "Boolean");
1461  crosshair_flag = get_boolean_resource ("crosshair", "Boolean");
1462
1463  be_wormy = get_boolean_resource ("worm", "Boolean");
1464  if (be_wormy) {
1465      maxspeed   *= 1.43;
1466      thrust     *= 10;
1467      gravity    *= 3;
1468      vertigo    *= 0.5;
1469      smoothness *= 2;
1470      curviness  *= 2;
1471      twistiness *= 2;
1472      psychedelic_flag = True;
1473      crosshair_flag = False;
1474  }
1475
1476  if (psychedelic_flag) init_psychedelic_colors (dpy, window, cmap);
1477  else init_colors (dpy, window, cmap);
1478
1479  for (i=0; i<ROTS; i++) {
1480        th = M_PI * 2.0 * i / ROTS;
1481        costab[i] = cos(th);
1482        sintab[i] = sin(th);
1483  }
1484
1485  wide = random_wideness();
1486
1487  for (i=0; i < TERRAIN_LENGTH; i++) {
1488        wideness[i] = wide;
1489        bonuses[i] = 0;
1490  }
1491
1492  init_terrain ();
1493  init_curves ();
1494  wrap_tunnel (0, TERRAIN_LENGTH-1);
1495
1496  if (DEBUG_FLAG || verbose_flag) atexit(print_stats);
1497
1498  step = effective_speed;
1499
1500#ifdef HAVE_GETTIMEOFDAY
1501  init_time ();
1502#endif
1503
1504}
1505
1506
1507/*
1508 * Down the speedmine, you'll find speed
1509 * to satisfy your moving needs;
1510 * So if you're looking for a blast
1511 * then hit the speedmine, really fast.
1512 */
1513
1514/*
1515 * Speedworm likes to choke and spit
1516 * and chase his tail, and dance a bit
1517 * he really is a funky friend;
1518 * he's made of speed from end to end.
1519 */
1520
1521char *progclass = "Speedmine";
1522
1523char *defaults [] = {
1524  ".verbose: False",
1525  "*worm: False",
1526  "*wire: False",
1527  ".background: black",
1528  ".foreground: white",
1529  "*darkground: #101010",
1530  "*lightground: #a0a0a0",
1531  "*tunnelend: #000000",
1532  "*delay:      30000",
1533  "*maxspeed: 700",
1534  "*thrust: 1.0",
1535  "*gravity: 9.8",
1536  "*vertigo: 1.0",
1537  "*terrain: True",
1538  "*smoothness: 6",
1539  "*curviness: 1.0",
1540  "*twistiness: 1.0",
1541  "*widening: True",
1542  "*bumps: True",
1543  "*bonuses: True",
1544  "*crosshair: True",
1545  "*psychedelic: False",
1546  0
1547};
1548
1549XrmOptionDescRec options [] = {
1550  { "-verbose",                 ".verbose",                             XrmoptionNoArg, "True"},
1551  { "-worm",                    ".worm",                                XrmoptionNoArg, "True"},
1552  { "-wire",                    ".wire",                                XrmoptionNoArg, "True"},
1553  { "-nowire",                  ".wire",                                XrmoptionNoArg, "False"},
1554  { "-darkground",              ".darkground",                  XrmoptionSepArg, 0 },
1555  { "-lightground",             ".lightground",                 XrmoptionSepArg, 0 },
1556  { "-tunnelend",               ".tunnelend",                   XrmoptionSepArg, 0 },
1557  { "-delay",           ".delay",               XrmoptionSepArg, 0 },
1558  { "-maxspeed",                ".maxspeed",                    XrmoptionSepArg, 0 },
1559  { "-thrust",                  ".thrust",                              XrmoptionSepArg, 0 },
1560  { "-gravity",                 ".gravity",                             XrmoptionSepArg, 0 },
1561  { "-vertigo",                 ".vertigo",                             XrmoptionSepArg, 0 },
1562  { "-terrain",                 ".terrain",                             XrmoptionNoArg, "True"},
1563  { "-noterrain",               ".terrain",                             XrmoptionNoArg, "False"},
1564  { "-smoothness",      ".smoothness",                  XrmoptionSepArg, 0 },
1565  { "-curviness",               ".curviness",                   XrmoptionSepArg, 0 },
1566  { "-twistiness",              ".twistiness",                  XrmoptionSepArg, 0 },
1567  { "-widening",                ".widening",                    XrmoptionNoArg, "True"},
1568  { "-nowidening",              ".widening",                    XrmoptionNoArg, "False"},
1569  { "-bumps",                   ".bumps",                               XrmoptionNoArg, "True"},
1570  { "-nobumps",                 ".bumps",                               XrmoptionNoArg, "False"},
1571  { "-bonuses",                 ".bonuses",                             XrmoptionNoArg, "True"},
1572  { "-nobonuses",               ".bonuses",                             XrmoptionNoArg, "False"},
1573  { "-crosshair",               ".crosshair",                   XrmoptionNoArg, "True"},
1574  { "-nocrosshair",             ".crosshair",                   XrmoptionNoArg, "False"},
1575  { "-psychedelic",             ".psychedelic",                 XrmoptionNoArg, "True"},
1576  { "-nopsychedelic",   ".psychedelic",                 XrmoptionNoArg, "False"},
1577  { 0, 0, 0, 0 }
1578};
1579
1580
1581void
1582screenhack (Display *dpy, Window window)
1583{
1584#ifndef NDEBUG
1585        atexit (abort);
1586#endif
1587
1588        init_speedmine (dpy, window);
1589
1590        while (1) {
1591                speedmine (dpy, window);
1592                XSync (dpy, False);
1593                screenhack_handle_events (dpy);
1594                if (delay) usleep(delay);
1595        }
1596}
1597
1598/* vim: ts=4
1599 */
Note: See TracBrowser for help on using the repository browser.