From http://www.jwz.org/xscreensaver/xscreensaver-5.16.tar.gz
[xscreensaver] / hacks / glx / crackberg.c
1 /***************************
2  ** crackberg; Matus Telgarsky [ catachresis@cmu.edu ] 2005 
3  ** */
4 #ifndef HAVE_COCOA
5 # define XK_MISCELLANY
6 # include <X11/keysymdef.h>
7 #endif
8
9 #define DEFAULTS    "*delay:        20000       \n" \
10                     "*showFPS:      False       \n" \
11                     "*wireframe:    False       \n" \
12
13 # define refresh_crackberg 0
14 #undef countof
15 #define countof(x) (sizeof((x))/sizeof((*x)))
16
17 #include "xlockmore.h"
18 #ifdef USE_GL /* whole file */
19
20 #define DEF_NSUBDIVS   "4"
21 #define DEF_BORING     "False"
22 #define DEF_CRACK      "True"
23 #define DEF_WATER      "True"
24 #define DEF_FLAT       "True"
25 #define DEF_COLOR      "random"
26 #define DEF_LIT        "True"
27 #define DEF_VISIBILITY "0.6"
28 #define DEF_LETTERBOX  "False"
29
30
31 /***************************
32  ** macros
33  ** */
34
35 #define M_RAD7_4        0.661437827766148
36 #define M_SQRT3_2       0.866025403784439
37 #define M_PI_180        0.0174532925199433
38 #define M_180_PI        57.2957795130823
39 #define MSPEED_SCALE    1.1
40 #define AVE3(a,b,c)     ( ((a) + (b) + (c)) / 3.0 )
41 #define MAX_ZDELTA      0.35
42 #define DISPLACE(h,d)   (h+(random()/(double)RAND_MAX-0.5)*2*MAX_ZDELTA/(1<<d))
43 #define MEAN(x,y)       ( ((x) + (y)) / 2.0 )
44 #define TCOORD(x,y)     (cberg->heights[(cberg->epoints * (y) - ((y)-1)*(y)/2 + (x))])
45 #define sNCOORD(x,y,p)  (cberg->norms[3 * (cberg->epoints * (y) - ((y)-1)*(y)/2 + (x)) + (p)])
46 #define SET_sNCOORD(x,y, down, a,b,c,d,e,f)   \
47     sNCOORD(x,y,0) = AVE3(a-d, 0.5 * (b-e), -0.5 * (c-f)); \
48     sNCOORD(x,y,1) = ((down) ? -1 : +1) * AVE3(0.0, M_SQRT3_2 * (b-e), M_SQRT3_2 * (c-f)); \
49     sNCOORD(x,y,2) = (2*dx)
50 #define fNCOORD(x,y,w,p)  \
51     (cberg->norms[3 * (2*(y)*cberg->epoints-((y)+1)*((y)+1) + 1 + 2 * ((x)-1) + (w)) + (p)])
52 #define SET_fNCOORDa(x,y, down, dz00,dz01) \
53     fNCOORD(x,y,0,0) = (down) * (dy) * (dz01); \
54     fNCOORD(x,y,0,1) = (down) * ((dz01) * (dx) / 2 - (dx) * (dz00)); \
55     fNCOORD(x,y,0,2) = (down) * (dx) * (dy)
56 #define SET_fNCOORDb(x,y, down, dz10,dz11) \
57     fNCOORD(x,y,1,0) = (down) * (dy) * (dz10); \
58     fNCOORD(x,y,1,1) = (down) * ((dz11) * (dx) - (dx) * (dz10) / 2); \
59     fNCOORD(x,y,1,2) = (down) * (dx) * (dy)
60
61
62 /***************************
63  ** types
64  ** */
65
66
67 typedef struct _cberg_state cberg_state;
68 typedef struct _Trile Trile;
69
70 typedef struct {
71     void (*init)(Trile *);
72     void (*free)(Trile *);
73     void (*draw)(Trile *);
74     void (*init_iter)(Trile *, cberg_state *);
75     void (*dying_iter)(Trile *, cberg_state *);
76 } Morph;
77
78 typedef struct {
79     char *id;
80     void (*land)(cberg_state *, double);
81     void (*water)(cberg_state *, double);
82     double bg[4];
83 } Color;
84
85 enum { TRILE_NEW, TRILE_INIT, TRILE_STABLE, TRILE_DYING, TRILE_DELETE };
86
87 struct _Trile {
88     int x,y; /*center coords; points up if (x+y)%2 == 0, else down*/
89     short state;
90     short visible;
91     double *l,*r,*v; /*only edges need saving*/
92     GLuint call_list;
93
94     void *morph_data;
95     const Morph *morph;
96
97     struct _Trile *left, *right, *parent; /* for bst, NOT spatial */
98     struct _Trile *next_free; /* for memory allocation */
99 };
100
101 enum { MOTION_AUTO = 0, MOTION_MANUAL = 1, MOTION_LROT= 2,    MOTION_RROT = 4,
102        MOTION_FORW = 8,   MOTION_BACK = 16,  MOTION_DEC = 32, MOTION_INC = 64,
103        MOTION_LEFT = 128, MOTION_RIGHT = 256 };
104
105 struct _cberg_state {
106     GLXContext *glx_context;
107     Trile *trile_head;
108     
109     double x,y,z, yaw,roll,pitch, dx,dy,dz, dyaw,droll,dpitch, elapsed;
110     double prev_frame;
111     int motion_state;
112     double mspeed;
113
114     double fovy, aspect, zNear, zFar;
115
116     const Color *color;
117
118     int count;
119
120     unsigned int epoints, /*number of points to one edge*/
121                  tpoints, /*number points total*/
122                  ntris,   /*number triangles per trile*/
123                  tnorms;  /*number of normals*/
124
125     double *heights, *norms;
126     Trile *free_head; /* for trile_[alloc|free] */
127
128     double draw_elapsed;
129
130     double dx0;
131
132     double vs0r,vs0g,vs0b, vs1r, vs1g, vs1b,
133            vf0r,vf0g,vf0b, vf1r, vf1g, vf1b;
134 };
135
136
137
138 /***************************
139  ** globals
140  ** */
141
142 static unsigned int nsubdivs;
143 static Bool crack, boring, do_water, flat, lit, letterbox;
144 static float visibility;
145 static char *color;
146
147 static cberg_state *cbergs = NULL;
148
149 static XrmOptionDescRec opts[] = {
150   {"-nsubdivs",   ".nsubdivs",   XrmoptionSepArg, 0},
151   {"-boring",     ".boring",     XrmoptionNoArg,  "True"},
152   {"-crack",      ".crack",      XrmoptionNoArg,  "True"},
153   {"-no-crack",   ".crack",      XrmoptionNoArg,  "False"},
154   {"-water",      ".water",      XrmoptionNoArg,  "True"},
155   {"-no-water",   ".water",      XrmoptionNoArg,  "False"},
156   {"-flat",       ".flat",       XrmoptionNoArg,  "True"},
157   {"-no-flat",    ".flat",       XrmoptionNoArg,  "False"},
158   {"-color",      ".color",      XrmoptionSepArg, 0},
159   {"-lit",        ".lit",        XrmoptionNoArg,  "True"},
160   {"-no-lit",     ".lit",        XrmoptionNoArg,  "False"},
161   {"-visibility", ".visibility", XrmoptionSepArg, 0},
162   {"-letterbox",  ".letterbox",  XrmoptionNoArg,  "True"}
163 };
164
165 static argtype vars[] = {
166   {&nsubdivs,   "nsubdivs",   "nsubdivs",   DEF_NSUBDIVS,   t_Int},
167   {&boring,     "boring",     "boring",     DEF_BORING,     t_Bool},
168   {&crack,      "crack",      "crack",      DEF_CRACK,      t_Bool},
169   {&do_water,   "water",      "water",      DEF_WATER,      t_Bool},
170   {&flat,       "flat",       "flat",       DEF_FLAT,       t_Bool},
171   {&color,      "color",      "color",      DEF_COLOR,      t_String},
172   {&lit,        "lit",        "lit",        DEF_LIT,        t_Bool},
173   {&visibility, "visibility", "visibility", DEF_VISIBILITY, t_Float},
174   {&letterbox,  "letterbox",  "letterbox",  DEF_LETTERBOX,  t_Bool}
175 };
176
177 ENTRYPOINT ModeSpecOpt crackberg_opts = {countof(opts), opts, countof(vars), vars, NULL};
178
179
180 /***************************
181  ** Trile functions. 
182  **  first come all are regular trile functions 
183  ** */
184
185
186 /* forward decls for trile_new */
187 static Trile *triles_find(Trile *tr, int x, int y);
188 static Trile *trile_alloc(cberg_state *cberg);
189 static const Morph *select_morph(void);
190 static const Color *select_color(cberg_state *);
191
192 static void trile_calc_sides(cberg_state *cberg, 
193                              Trile *new, int x, int y, Trile *root)
194 {
195     unsigned int i,j,k; 
196     int dv = ( (x + y) % 2 ? +1 : -1); /* we are pointing down or up*/
197     Trile *l, *r, *v; /* v_ertical */
198
199
200     if (root) {
201         l = triles_find(root, x-1, y);
202         r = triles_find(root, x+1, y);  
203         v = triles_find(root, x,y+dv); 
204     } else
205         l = r = v = NULL;
206
207     if (v) {
208         for (i = 0; i != cberg->epoints; ++i)
209             new->v[i] = v->v[i];
210     } else {
211         if (l)          new->v[0] = l->l[0];
212         else if (!root) new->v[0] = DISPLACE(0,0);
213         else { 
214             Trile *tr; /* all of these tests needed.. */
215             if ( (tr = triles_find(root, x-1, y + dv)) )
216                 new->v[0] = tr->l[0];
217             else if ( (tr = triles_find(root, x-2, y)) )
218                 new->v[0] = tr->r[0];
219             else if ( (tr = triles_find(root, x-2, y + dv)) )
220                 new->v[0] = tr->r[0];
221             else
222                 new->v[0] = DISPLACE(0,0);
223         }
224
225         if (r)          new->v[cberg->epoints-1] = r->l[0];
226         else if (!root) new->v[cberg->epoints-1] = DISPLACE(0,0);
227         else {
228             Trile *tr;
229             if ( (tr = triles_find(root, x+1, y + dv)) )
230                 new->v[cberg->epoints-1] = tr->l[0];
231             else if ( (tr = triles_find(root, x+2, y)) )
232                 new->v[cberg->epoints-1] = tr->v[0];
233             else if ( (tr = triles_find(root, x+2, y + dv)) )
234                 new->v[cberg->epoints-1] = tr->v[0];
235             else
236                 new->v[cberg->epoints-1] = DISPLACE(0,0);
237         }
238
239         for (i = ((1 << nsubdivs) >> 1), k =1; i; i >>= 1, ++k)
240             for (j = i; j < cberg->epoints; j += i * 2)
241                 new->v[j] = DISPLACE(MEAN(new->v[j-i], new->v[j+i]), k);
242     }
243         
244     if (l) {
245         for (i = 0; i != cberg->epoints; ++i)
246             new->l[i] = l->r[i];
247     } else {
248         if (r)          new->l[0] = r->v[0];
249         else if (!root) new->l[0] = DISPLACE(0,0);
250         else {
251             Trile *tr;
252             if ( (tr = triles_find(root, x-1, y-dv)) )
253                 new->l[0] = tr->r[0];
254             else if ( (tr = triles_find(root, x+1, y-dv)) )
255                 new->l[0] = tr->v[0];
256             else if ( (tr = triles_find(root, x, y-dv)) )
257                 new->l[0] = tr->l[0];
258             else 
259                 new->l[0] = DISPLACE(0,0);
260         }
261
262         new->l[cberg->epoints - 1] = new->v[0];
263
264         for (i = ((1 << nsubdivs) >> 1), k =1; i; i >>= 1, ++k)
265             for (j = i; j < cberg->epoints; j += i * 2)
266                 new->l[j] = DISPLACE(MEAN(new->l[j-i], new->l[j+i]), k);
267     }
268
269     if (r) {
270         for (i = 0; i != cberg->epoints; ++i)
271             new->r[i] = r->l[i];
272     } else {
273         new->r[0] = new->v[cberg->epoints - 1];
274         new->r[cberg->epoints - 1] = new->l[0];
275
276         for (i = ((1 << nsubdivs) >> 1), k =1; i; i >>= 1, ++k)
277             for (j = i; j < cberg->epoints; j += i * 2)
278                 new->r[j] = DISPLACE(MEAN(new->r[j-i], new->r[j+i]), k);
279     }
280 }
281
282 static void trile_calc_heights(cberg_state *cberg, Trile *new)
283 {
284     unsigned int i, j, k, h;
285
286     for (i = 0; i < cberg->epoints - 1; ++i) { /* copy in sides */
287         TCOORD(i,0) = new->v[i];
288         TCOORD(cberg->epoints - 1 - i, i) = new->r[i];
289         TCOORD(0, cberg->epoints - 1 - i) = new->l[i];
290     }
291
292     for (i = ((1 << nsubdivs) >> 2), k =1; i; i >>= 1, ++k)
293         for (j = 1; j < (1 << k); ++j)
294             for (h = 1; h <= (1<<k) - j; ++h) {
295                 TCOORD( i*(2*h - 1), i*(2*j - 1) ) = /*rights*/
296                   DISPLACE(MEAN(TCOORD( i*(2*h - 2), i*(2*j + 0) ),
297                                 TCOORD( i*(2*h + 0), i*(2*j - 2) )), k);
298
299                 TCOORD( i*(2*h + 0), i*(2*j - 1) ) = /*lefts*/
300                   DISPLACE(MEAN(TCOORD( i*(2*h + 0), i*(2*j - 2) ),
301                                 TCOORD( i*(2*h + 0), i*(2*j + 0) )), k);
302
303                 TCOORD( i*(2*h - 1), i*(2*j + 0) ) = /*verts*/
304                   DISPLACE(MEAN(TCOORD( i*(2*h - 2), i*(2*j + 0) ),
305                                 TCOORD( i*(2*h + 0), i*(2*j + 0) )), k);
306             }
307 }
308
309 static void trile_calc_flat_norms(cberg_state *cberg, Trile *new)
310 {
311     unsigned int x, y;
312     int down = (((new->x + new->y) % 2) ? -1 : +1);
313     double dz00,dz01,dz10,dz11, a,b,c,d;
314     double dy = down * M_SQRT3_2 / (1 << nsubdivs);
315     double dx = cberg->dx0;
316
317     for (y = 0; y < cberg->epoints - 1; ++y) {
318         a = TCOORD(0,y);
319         b = TCOORD(0,y+1);
320         for (x = 1; x < cberg->epoints - 1 - y; ++x) {
321             c = TCOORD(x,y);
322             d = TCOORD(x,y+1);
323
324             dz00 = b-c;
325             dz01 = a-c;
326             dz10 = b-d;
327             dz11 = c-d;
328             
329             SET_fNCOORDa(x,y, down, dz00,dz01);
330             SET_fNCOORDb(x,y, down, dz10,dz11);
331
332             a = c;
333             b = d;
334         }
335
336         c = TCOORD(x,y);
337         dz00 = b-c;
338         dz01 = a-c;
339         SET_fNCOORDa(x,y, down, dz00, dz01);
340     }
341 }
342
343 static void trile_calc_smooth_norms(cberg_state *cberg, Trile *new)
344 {
345     unsigned int i,j, down = (new->x + new->y) % 2;
346     double prev, cur, next;
347     double dx = cberg->dx0;
348
349     /** corners -- assume level (bah) **/
350     cur = TCOORD(0,0);
351     SET_sNCOORD(0,0, down,
352         cur,cur,TCOORD(0,1),TCOORD(1,0),cur,cur);
353     cur = TCOORD(cberg->epoints-1,0);
354     SET_sNCOORD(cberg->epoints-1,0, down,
355         TCOORD(cberg->epoints-2,0),TCOORD(cberg->epoints-2,1),cur,cur,cur,cur);
356     cur = TCOORD(0,cberg->epoints-1);
357     SET_sNCOORD(0,cberg->epoints-1, down,
358         cur,cur,cur,cur,TCOORD(1,cberg->epoints-2),TCOORD(0,cberg->epoints-2));
359
360
361     /** sides **/
362     /* vert */
363     prev = TCOORD(0,0);
364     cur = TCOORD(1,0);
365     for (i = 1; i < cberg->epoints - 1; ++i) {
366         next = TCOORD(i+1,0);
367         SET_sNCOORD(i,0, down, prev,TCOORD(i-1,1),TCOORD(i,1), next,cur,cur);
368         prev = cur;
369         cur = next;
370     }
371
372     /* right */
373     prev = TCOORD(cberg->epoints-1,0);
374     cur = TCOORD(cberg->epoints-2,0);
375     for (i = 1; i < cberg->epoints - 1; ++i) {
376         next = TCOORD(cberg->epoints-i-2,i+1);
377         SET_sNCOORD(cberg->epoints-i-1,i, down, TCOORD(cberg->epoints-i-2,i),next,cur,
378                                         cur,prev,TCOORD(cberg->epoints-i-1,i-1));
379         prev = cur;
380         cur = next;
381     }
382         
383     /* left */
384     prev = TCOORD(0,0);
385     cur = TCOORD(0,1);
386     for (i = 1; i < cberg->epoints - 1; ++i) {
387         next = TCOORD(0,i+1);
388         SET_sNCOORD(0,i, down, cur,cur,next,TCOORD(1,i),TCOORD(1,i-1),prev);
389         prev = cur;
390         cur = next;
391     }
392
393
394     /** fill in **/
395     for (i = 1; i < cberg->epoints - 2; ++i) {
396         prev = TCOORD(0,i);
397         cur = TCOORD(1,i);
398         for (j = 1; j < cberg->epoints - i - 1; ++j) {
399             next = TCOORD(j+1,i);
400             SET_sNCOORD(j,i, down, prev,TCOORD(j-1,i+1),TCOORD(j,i+1),
401                             next,TCOORD(j+1,i-1),TCOORD(j,i-1));
402             prev = cur;
403             cur = next;
404         }
405     }
406 }
407
408 static inline void trile_light(cberg_state *cberg, 
409                                unsigned int x, unsigned int y, 
410                                unsigned int which)
411 {
412     if (flat) {
413         if (x) {
414             glNormal3d(fNCOORD(x,y,which,0),
415                        fNCOORD(x,y,which,1),
416                        fNCOORD(x,y,which,2));
417         } else { /* I get mesa errors and bizarre glitches without this!! */
418             glNormal3d(fNCOORD(1,y,0,0),
419                        fNCOORD(1,y,0,1),
420                        fNCOORD(1,y,0,2));
421         }
422     } else {
423         glNormal3d(sNCOORD(x,y+which,0),
424                    sNCOORD(x,y+which,1),
425                    sNCOORD(x,y+which,2));
426     }
427 }
428
429 static inline void trile_draw_vertex(cberg_state *cberg, unsigned int ix,
430     unsigned int iy, unsigned int which, double x,double y,
431     double zcur, double z1, double z2)
432 {
433     glColor3d(0.0, 0.0, 0.0); /* don't ask. my card breaks otherwise. */
434     
435     if (do_water && zcur <= 0.0) {
436         cberg->color->water(cberg, zcur); /* XXX use average-of-3 for color when flat?*/
437         if (lit) glNormal3d(0.0,0.0,1.0);
438         glVertex3d(x, y, 0.0); 
439     } else {
440         cberg->color->land(cberg, zcur);
441         if (lit) trile_light(cberg,ix,iy,which);
442         glVertex3d(x, y, zcur);
443     }
444 }
445
446 static void trile_render(cberg_state *cberg, Trile *new)
447 {
448     double cornerx = 0.5 * new->x - 0.5, cornery;
449     double dy = M_SQRT3_2 / (1 << nsubdivs);
450     double z0,z1,z2;
451     int x,y;
452
453     new->call_list = glGenLists(1);
454     glNewList(new->call_list, GL_COMPILE);
455
456         if ((new->x + new->y) % 2) { /*point down*/
457             cornery = (new->y + 0.5)*M_SQRT3_2;
458             glFrontFace(GL_CW);
459             dy = -dy;
460         } else
461             cornery = (new->y - 0.5) * M_SQRT3_2;
462
463         for (y = 0; y < cberg->epoints - 1; ++y) {
464             double dx = cberg->dx0;
465             glBegin(GL_TRIANGLE_STRIP);
466                 /* first three points all part of the same triangle.. */
467                 z0 = TCOORD(0,y);
468                 z1 = TCOORD(0,y+1);
469                 z2 = TCOORD(1,y);
470                 trile_draw_vertex(cberg, 0,y,0,
471                   cornerx,cornery, z0, z1, z2);
472                 trile_draw_vertex(cberg, 0,y,1,
473                   cornerx+0.5*dx,cornery+dy, z1, z0, z2);
474
475                 for (x = 1; x < cberg->epoints - 1 - y; ++x) {
476                     trile_draw_vertex(cberg, x,y,0,
477                       cornerx+x*dx,cornery, z2, z1, z0);
478
479                     z0 = TCOORD(x, y+1);
480
481                     trile_draw_vertex(cberg, x,y,1,
482                       cornerx+(x+0.5)*dx,cornery+dy, z0, z2, z1);
483
484                     z1 = z0;
485                     z0 = z2;
486                     z2 = TCOORD(x+1,y);
487                 }
488                 trile_draw_vertex(cberg, x,y,0,
489                   cornerx + x*dx, cornery, z2, z1, z0);
490             glEnd();
491
492             cornerx += dx/2;
493             cornery += dy;
494         }
495
496         if ((new->x + new->y) % 2) /*point down*/
497             glFrontFace(GL_CCW);
498     glEndList();
499 }
500
501 static Trile *trile_new(cberg_state *cberg, int x,int y,Trile *parent,Trile *root)
502 {
503     Trile *new;
504
505     new = trile_alloc(cberg);
506
507     new->x = x;
508     new->y = y;
509     new->state = TRILE_NEW;
510     new->parent = parent;
511     new->left = new->right = NULL;
512     new->visible = 1;
513
514     new->morph = select_morph();
515     new->morph->init(new);
516
517     trile_calc_sides(cberg, new, x, y, root);
518     trile_calc_heights(cberg, new);
519
520     if (lit) {
521         if (flat)   trile_calc_flat_norms(cberg, new);
522         else        trile_calc_smooth_norms(cberg, new);
523     }
524
525     trile_render(cberg, new);
526     return new;
527 }
528
529 static Trile *trile_alloc(cberg_state *cberg)
530 {
531     Trile *new;
532
533     if (cberg->free_head) {
534         new = cberg->free_head;
535         cberg->free_head = cberg->free_head->next_free;
536     } else {
537         ++cberg->count;
538         if (!(new = malloc(sizeof(Trile)))
539          || !(new->l = (double *) malloc(sizeof(double) * cberg->epoints * 3))) {
540             perror(progname);
541             exit(1);
542         }
543         new->r = new->l + cberg->epoints;
544         new->v = new->r + cberg->epoints;
545 #ifdef DEBUG
546         printf("needed to alloc; [%d]\n", cberg->count);
547 #endif
548     }
549     return new;
550 }
551
552 static void trile_free(cberg_state *cberg, Trile *tr)
553 {
554     glDeleteLists(tr->call_list, 1);
555     tr->morph->free(tr);
556     tr->next_free = cberg->free_head;
557     cberg->free_head = tr;
558 }
559
560
561 static void trile_draw_vanilla(Trile *tr)
562 { glCallList(tr->call_list); }
563
564 static void trile_draw(Trile *tr, void *ignore)
565 {
566     if (tr->state == TRILE_STABLE)
567         trile_draw_vanilla(tr);
568     else
569         tr->morph->draw(tr);
570 }
571
572
573 /***************************
574  ** Trile morph functions. 
575  **  select function at bottom (forward decls sucls) 
576  ** */
577
578
579 /*** first the basic growing morph */
580
581 static void grow_init(Trile *tr)
582 {
583     tr->morph_data = (void *) malloc(sizeof(double));
584     *((double *)tr->morph_data) = 0.02; /* not 0; avoid normals crapping */
585 }
586
587 static void grow_free(Trile *tr)
588 {
589     free(tr->morph_data);
590 }
591
592 static void grow_draw(Trile *tr)
593 {
594     glPushMatrix();
595         glScaled(1.0,1.0, *((double *)tr->morph_data));
596         trile_draw_vanilla(tr);
597     glPopMatrix();
598 }
599
600 static void grow_init_iter(Trile *tr, cberg_state *cberg)
601 {
602     *((double *)(tr->morph_data)) = *((double *)tr->morph_data) + cberg->elapsed;
603     if (*((double *)tr->morph_data) >= 1.0)
604         tr->state = TRILE_STABLE;
605 }
606
607 static void grow_dying_iter(Trile *tr, cberg_state *cberg)
608 {
609     *((double *)tr->morph_data) = *((double *)tr->morph_data) - cberg->elapsed;
610     if (*((double *)tr->morph_data) <= 0.02) /* XXX avoid fast del/cons? */
611         tr->state = TRILE_DELETE;
612 }
613
614 /**** falling morph ****/
615
616 static void fall_init(Trile *tr)
617 {
618     tr->morph_data = (void *) malloc(sizeof(double));
619     *((double *)tr->morph_data) = 0.0;
620 }
621
622 static void fall_free(Trile *tr)
623 {
624     free(tr->morph_data);
625 }
626
627 static void fall_draw(Trile *tr)
628 {
629     glPushMatrix();
630         glTranslated(0.0,0.0,(0.5 - *((double *)tr->morph_data)) * 8);
631         trile_draw_vanilla(tr);
632     glPopMatrix();
633 }
634
635 static void fall_init_iter(Trile *tr, cberg_state *cberg)
636 {
637     *((double *)(tr->morph_data)) = *((double *)tr->morph_data) + cberg->elapsed;
638     if (*((double *)tr->morph_data) >= 0.5)
639         tr->state = TRILE_STABLE;
640 }
641
642 static void fall_dying_iter(Trile *tr, cberg_state *cberg)
643 {
644     *((double *)tr->morph_data) = *((double *)tr->morph_data) - cberg->elapsed;
645     if (*((double *)tr->morph_data) <= 0.0) /* XXX avoid fast del/cons? */
646         tr->state = TRILE_DELETE;
647 }
648
649 /**** yeast morph ****/
650
651 static void yeast_init(Trile *tr)
652 {
653     tr->morph_data = (void *) malloc(sizeof(double));
654     *((double *)tr->morph_data) = 0.02;
655 }
656
657 static void yeast_free(Trile *tr)
658 {
659     free(tr->morph_data);
660 }
661
662 static void yeast_draw(Trile *tr)
663 {
664     double x = tr->x * 0.5,
665            y = tr->y * M_SQRT3_2,
666            z = *((double *)tr->morph_data);
667
668     glPushMatrix();
669         glTranslated(x, y, 0);
670         glRotated(z*360, 0,0,1);
671         glScaled(z,z,z);
672         glTranslated(-x, -y, 0);
673         trile_draw_vanilla(tr);
674     glPopMatrix();
675 }
676
677 static void yeast_init_iter(Trile *tr, cberg_state *cberg)
678 {
679     *((double *)(tr->morph_data)) = *((double *)tr->morph_data) + cberg->elapsed;
680     if (*((double *)tr->morph_data) >= 1.0)
681         tr->state = TRILE_STABLE;
682 }
683
684 static void yeast_dying_iter(Trile *tr, cberg_state *cberg)
685 {
686     *((double *)tr->morph_data) = *((double *)tr->morph_data) - cberg->elapsed;
687     if (*((double *)tr->morph_data) <= 0.02) /* XXX avoid fast del/cons? */
688         tr->state = TRILE_DELETE;
689 }
690
691 /**** identity morph ****/
692
693 static void identity_init(Trile *tr)
694 { tr->state = TRILE_STABLE; }
695
696 static void identity_free(Trile *tr)
697 {}
698
699 static void identity_draw(Trile *tr)
700 { trile_draw_vanilla(tr); }
701
702 static void identity_init_iter(Trile *tr, cberg_state *cberg)
703 {}
704
705 static void identity_dying_iter(Trile *tr, cberg_state *cberg)
706 { tr->state = TRILE_DELETE; }
707
708 /** now to handle selection **/
709
710 static const Morph morphs[] = {
711     {grow_init, grow_free, grow_draw, grow_init_iter, grow_dying_iter},
712     {fall_init, fall_free, fall_draw, fall_init_iter, fall_dying_iter},
713     {yeast_init, yeast_free, yeast_draw, yeast_init_iter, yeast_dying_iter},
714     {identity_init,  /*always put identity last to skip it..*/
715         identity_free, identity_draw, identity_init_iter, identity_dying_iter}
716 };    
717
718 static const Morph *select_morph()
719
720     int nmorphs = countof(morphs);
721     if (crack)
722         return &morphs[random() % (nmorphs-1)]; 
723     else if (boring)
724         return &morphs[nmorphs-1]; 
725     else
726         return morphs;
727 }
728
729
730 /***************************
731  ** Trile superstructure functions. 
732  **  */
733
734
735 static void triles_set_visible(cberg_state *cberg, Trile **root, int x, int y)
736 {
737     Trile *parent = NULL, 
738           *iter = *root;
739     int goleft=0;
740
741     while (iter != NULL) {
742         parent = iter;
743         goleft = (iter->x > x || (iter->x == x && iter->y > y));
744         if (goleft)
745             iter = iter->left;
746         else if (iter->x == x && iter->y == y) {
747             iter->visible = 1;
748             return;
749         } else
750             iter = iter->right;
751     }
752
753     if (parent == NULL)
754         *root = trile_new(cberg, x,y, NULL, NULL);
755     else if (goleft)
756         parent->left = trile_new(cberg, x,y, parent, *root);
757     else
758         parent->right = trile_new(cberg, x,y, parent, *root);
759 }
760
761 static unsigned int triles_foreach(Trile *root, void (*f)(Trile *, void *), 
762   void *data)
763 {
764     if (root == NULL) 
765         return 0;
766     
767     f(root, data);
768     return 1 + triles_foreach(root->left, f, data) 
769       + triles_foreach(root->right, f, data);
770 }
771
772 static void triles_update_state(Trile **root, cberg_state *cberg)
773 {
774     int process_current = 1;
775     if (*root == NULL)
776         return;
777
778     while (process_current) {
779         if ( (*root)->visible ) {
780             if ( (*root)->state == TRILE_INIT )
781                 (*root)->morph->init_iter(*root, cberg);
782             else if ( (*root)->state == TRILE_DYING ) {
783                 (*root)->state = TRILE_INIT;
784                 (*root)->morph->init_iter(*root, cberg);
785             } else if ( (*root)->state == TRILE_NEW ) 
786                 (*root)->state = TRILE_INIT;
787
788             (*root)->visible = 0;
789         } else {
790             if ( (*root)->state == TRILE_STABLE )
791                 (*root)->state = TRILE_DYING;
792             else if ( (*root)->state == TRILE_INIT ) {
793                 (*root)->state = TRILE_DYING;
794                 (*root)->morph->dying_iter(*root, cberg);
795             } else if ( (*root)->state == TRILE_DYING )
796                 (*root)->morph->dying_iter(*root, cberg);
797         }
798
799         if ( (*root)->state == TRILE_DELETE ) {
800             Trile *splice_me;
801             process_current = 1;
802
803             if ((*root)->left == NULL) {
804                 splice_me = (*root)->right;
805                 if (splice_me)
806                     splice_me->parent = (*root)->parent;
807                 else 
808                     process_current = 0;
809             } else if ((*root)->right == NULL) {
810                 splice_me = (*root)->left;
811                 splice_me->parent = (*root)->parent;
812             } else {
813                 Trile *tmp;
814                 for (splice_me = (*root)->right; splice_me->left != NULL; )
815                     splice_me = splice_me->left;
816                 tmp = splice_me->right;
817
818                 if (tmp) tmp->parent = splice_me->parent;
819
820                 if (splice_me == splice_me->parent->left)
821                     splice_me->parent->left = tmp;
822                 else
823                     splice_me->parent->right = tmp;
824
825                 splice_me->parent = (*root)->parent;
826                 splice_me->left = (*root)->left;
827                 (*root)->left->parent = splice_me;
828                 splice_me->right = (*root)->right;
829                 if ((*root)->right)
830                     (*root)->right->parent = splice_me;
831             }
832             trile_free(cberg, *root);
833             *root = splice_me;
834         } else
835             process_current = 0;
836     }
837
838     if (*root) {
839         triles_update_state(&((*root)->left), cberg);
840         triles_update_state(&((*root)->right), cberg);
841     } 
842 }
843
844 static Trile *triles_find(Trile *tr, int x, int y)
845 {
846     while (tr && !(tr->x == x && tr->y == y))
847         if (x < tr->x || (x == tr->x && y < tr->y))
848             tr = tr->left;
849         else
850             tr = tr->right;
851     return tr;
852 }
853
854
855 /***************************
856  ** Trile superstructure visibility functions. 
857  **  strategy fine, implementation lazy&retarded =/
858  **  */
859
860 #ifdef DEBUG
861 static double x_shit, y_shit;
862 #endif
863
864 static void calc_points(cberg_state *cberg, double *x1,double *y1, 
865         double *x2,double *y2, double *x3,double *y3, double *x4,double *y4)
866 {
867     double zNear, x_nearcenter, y_nearcenter, nhalfwidth, x_center, y_center;
868
869
870     /* could cache these.. bahhhhhhhhhhhhhh */
871     double halfheight = tan(cberg->fovy / 2 * M_PI_180) * cberg->zNear;
872     double fovx_2 = atan(halfheight * cberg->aspect / cberg->zNear) * M_180_PI; 
873     double zFar = cberg->zFar + M_RAD7_4;
874     double fhalfwidth = zFar * tan(fovx_2 * M_PI_180)
875                       + M_RAD7_4 / cos(fovx_2 * M_PI_180);
876     double x_farcenter = cberg->x + zFar * cos(cberg->yaw * M_PI_180);
877     double y_farcenter = cberg->y + zFar * sin(cberg->yaw * M_PI_180);
878     *x1 = x_farcenter + fhalfwidth * cos((cberg->yaw - 90) * M_PI_180);
879     *y1 = y_farcenter + fhalfwidth * sin((cberg->yaw - 90) * M_PI_180);
880     *x2 = x_farcenter - fhalfwidth * cos((cberg->yaw - 90) * M_PI_180);
881     *y2 = y_farcenter - fhalfwidth * sin((cberg->yaw - 90) * M_PI_180);
882
883 #ifdef DEBUG
884     printf("pos (%.3f,%.3f) @ %.3f || fovx: %f || fovy: %f\n", 
885             cberg->x, cberg->y, cberg->yaw, fovx_2 * 2, cberg->fovy);
886     printf("\tfarcenter: (%.3f,%.3f) || fhalfwidth: %.3f \n"
887            "\tp1: (%.3f,%.3f) || p2: (%.3f,%.3f)\n",
888             x_farcenter, y_farcenter, fhalfwidth, *x1, *y1, *x2, *y2);
889 #endif
890
891     if (cberg->z - halfheight <= 0) /* near view plane hits xy */
892         zNear = cberg->zNear - M_RAD7_4;
893     else /* use bottom of frustum */
894         zNear = cberg->z / tan(cberg->fovy / 2 * M_PI_180) - M_RAD7_4;
895     nhalfwidth = zNear * tan(fovx_2 * M_PI_180)
896                + M_RAD7_4 / cos(fovx_2 * M_PI_180);
897     x_nearcenter = cberg->x + zNear * cos(cberg->yaw * M_PI_180);
898     y_nearcenter = cberg->y + zNear * sin(cberg->yaw * M_PI_180);
899     *x3 = x_nearcenter - nhalfwidth * cos((cberg->yaw - 90) * M_PI_180);
900     *y3 = y_nearcenter - nhalfwidth * sin((cberg->yaw - 90) * M_PI_180);
901     *x4 = x_nearcenter + nhalfwidth * cos((cberg->yaw - 90) * M_PI_180);
902     *y4 = y_nearcenter + nhalfwidth * sin((cberg->yaw - 90) * M_PI_180);
903
904 #ifdef DEBUG
905     printf("\tnearcenter: (%.3f,%.3f) || nhalfwidth: %.3f\n"
906            "\tp3: (%.3f,%.3f) || p4: (%.3f,%.3f)\n",
907             x_nearcenter, y_nearcenter, nhalfwidth, *x3, *y3, *x4, *y4);
908 #endif
909
910
911     /* center can be average or the intersection of diagonals.. */
912 #if 0
913     {
914         double c = nhalfwidth * (zFar -zNear) / (fhalfwidth + nhalfwidth);
915         x_center = x_nearcenter + c * cos(cberg->yaw * M_PI_180);
916         y_center = y_nearcenter + c * sin(cberg->yaw * M_PI_180);
917     }
918 #else
919     x_center = (x_nearcenter + x_farcenter) / 2;
920     y_center = (y_nearcenter + y_farcenter) / 2;
921 #endif
922 #ifdef DEBUG
923     x_shit = x_center;
924     y_shit = y_center;
925 #endif
926     
927 #define VSCALE(p)   *x##p = visibility * *x##p + (1-visibility) * x_center; \
928                     *y##p = visibility * *y##p + (1-visibility) * y_center
929
930     VSCALE(1);
931     VSCALE(2);
932     VSCALE(3);
933     VSCALE(4);
934 #undef VSCALE
935 }
936
937 /* this is pretty stupid.. */
938 static inline void minmax4(double a, double b, double c, double d, 
939   double *min, double *max)
940 {
941     *min = *max = a;
942
943     if (b > *max)       *max = b;
944     else if (b < *min)  *min = b;
945     if (c > *max)       *max = c;
946     else if (c < *min)  *min = c;
947     if (d > *max)       *max = d;
948     else if (d < *min)  *min = d;
949 }
950
951 typedef struct {
952     double min, max, start, dx;
953 } LS;
954
955 #define check_line(a, b)                     \
956     if (fabs(y##a-y##b) > 0.001) {                    \
957         ls[count].dx = (x##b-x##a)/(y##b-y##a);               \
958         if (y##b > y##a) {                            \
959             ls[count].start = x##a;                     \
960             ls[count].min = y##a;                       \
961             ls[count].max = y##b;                       \
962         } else {                                  \
963             ls[count].start = x##b;                     \
964             ls[count].min = y##b;                       \
965             ls[count].max = y##a;                       \
966         }                                         \
967         ++count;                                    \
968     }
969
970 static unsigned int build_ls(cberg_state *cberg, 
971                       double x1, double y1, double x2, double y2, 
972                       double x3, double y3, double x4, double y4, LS *ls,
973                       double *trough, double *peak)
974 {
975     unsigned int count = 0;
976
977     check_line(1, 2);
978     check_line(2, 3);
979     check_line(3, 4);
980     check_line(4, 1);
981
982     minmax4(y1, y2, y3, y4, trough, peak);
983     return count;
984 }
985
986 #undef check_line
987
988 /*needs bullshit to avoid double counts on corners.*/
989 static void find_bounds(double y, double *left, double *right, LS *ls,
990         unsigned int nls)
991 {
992     double x;
993     unsigned int i, set = 0;
994
995     for (i = 0; i != nls; ++i)
996         if (ls[i].min <= y && ls[i].max >= y) {
997             x = (y - ls[i].min) * ls[i].dx + ls[i].start;
998             if (!set) {
999                 *left = x;
1000                 ++set;
1001             } else if (fabs(x - *left) > 0.001) {
1002                 if (*left < x)
1003                     *right = x;
1004                 else {
1005                     *right = *left;
1006                     *left = x;
1007                 }
1008                 return;
1009             }
1010         }
1011
1012     /* just in case we somehow blew up */
1013     *left = 3.0;
1014     *right = -3.0;
1015 }
1016
1017 static void mark_visible(cberg_state *cberg)
1018 {
1019     double trough, peak, yval, left=0, right=0;
1020     double x1,y1, x2,y2, x3,y3, x4,y4;
1021     int start, stop, x, y;
1022     LS ls[4];
1023     unsigned int nls;
1024
1025     calc_points(cberg, &x1,&y1, &x2,&y2, &x3,&y3, &x4,&y4);
1026     nls = build_ls(cberg, x1,y1, x2,y2, x3,y3, x4,y4, ls, &trough, &peak);
1027
1028     start = (int) ceil(trough / M_SQRT3_2);
1029     stop = (int) floor(peak / M_SQRT3_2);
1030     
1031     for (y = start; y <= stop; ++y) {
1032         yval = y * M_SQRT3_2;
1033         find_bounds(yval, &left, &right, ls, nls);
1034         for (x = (int) ceil(left*2-1); x <= (int) floor(right*2); ++x) 
1035             triles_set_visible(cberg, &(cberg->trile_head), x, y);
1036     }
1037 }
1038
1039
1040 /***************************
1041  ** color schemes
1042  ** */
1043
1044 static void plain_land(cberg_state *cberg, double z)
1045 { glColor3f(pow((z/0.35),4),  z/0.35, pow((z/0.35),4)); }
1046 static void plain_water(cberg_state *cberg, double z)
1047 { glColor3f(0.0, (z+0.35)*1.6, 0.8); }
1048
1049 static void ice_land(cberg_state *cberg, double z)
1050 { glColor3f((0.35 - z)/0.35, (0.35 - z)/0.35, 1.0); }
1051 static void ice_water(cberg_state *cberg, double z)
1052 { glColor3f(0.0, (z+0.35)*1.6, 0.8); }
1053
1054
1055 static void magma_land(cberg_state *cberg, double z)
1056 { glColor3f(z/0.35, z/0.2,0); }
1057 static void magma_lava(cberg_state *cberg, double z)
1058 { glColor3f((z+0.35)*1.6, (z+0.35), 0.0); }
1059
1060 static void vomit_solid(cberg_state *cberg, double z)
1061 {
1062     double norm = fabs(z) / 0.35;
1063     glColor3f( 
1064       (1-norm) * cberg->vs0r + norm * cberg->vs1r, 
1065       (1-norm) * cberg->vs0g + norm * cberg->vs1g, 
1066       (1-norm) * cberg->vs0b + norm * cberg->vs1b 
1067     );
1068 }
1069 static void vomit_fluid(cberg_state *cberg, double z)
1070 {
1071     double norm = z / -0.35;
1072     glColor3f( 
1073       (1-norm) * cberg->vf0r + norm * cberg->vf1r, 
1074       (1-norm) * cberg->vf0g + norm * cberg->vf1g, 
1075       (1-norm) * cberg->vf0b + norm * cberg->vf1b 
1076     );
1077 }
1078
1079
1080 static const Color colors[] = {
1081     {"plain", plain_land, plain_water, {0.0, 0.0, 0.0, 1.0}},
1082     {"ice", ice_land, ice_water, {0.0, 0.0, 0.0, 1.0}},
1083     {"magma", magma_land, magma_lava, {0.3, 0.3, 0.0, 1.0}},
1084     {"vomit", vomit_solid, vomit_fluid, {0.3, 0.3, 0.0, 1.0}}, /* no error! */
1085 };
1086
1087 static const Color *select_color(cberg_state *cberg)
1088 {
1089     unsigned int ncolors = countof(colors);
1090     int idx = -1;
1091     if ( ! strcmp(color, "random") )
1092         idx = random() % ncolors;
1093     else {
1094         unsigned int i;
1095         for (i = 0; i != ncolors; ++i)
1096             if ( ! strcmp(colors[i].id, color) ) {
1097                 idx = i;
1098                 break;
1099             }
1100
1101         if (idx == -1) {
1102             printf("invalid color scheme selected; valid choices are:\n");
1103             for (i = 0; i != ncolors; ++i)
1104                 printf("\t%s\n", colors[i].id);
1105             printf("\t%s\n", "random");
1106             idx = 0;
1107         }
1108     }
1109
1110     if ( ! strcmp(colors[idx].id, "vomit") ) { /* need to create it (ghetto) */
1111         cberg->vs0r = random()/(double)RAND_MAX;
1112         cberg->vs0g = random()/(double)RAND_MAX;
1113         cberg->vs0b = random()/(double)RAND_MAX; 
1114         cberg->vs1r = random()/(double)RAND_MAX; 
1115         cberg->vs1g = random()/(double)RAND_MAX;
1116         cberg->vs1b = random()/(double)RAND_MAX;
1117         cberg->vf0r = random()/(double)RAND_MAX;
1118         cberg->vf0g = random()/(double)RAND_MAX;
1119         cberg->vf0b = random()/(double)RAND_MAX; 
1120         cberg->vf1r = random()/(double)RAND_MAX; 
1121         cberg->vf1g = random()/(double)RAND_MAX; 
1122         cberg->vf1b = random()/(double)RAND_MAX; 
1123  
1124         glClearColor(random()/(double)RAND_MAX,
1125                      random()/(double)RAND_MAX,
1126                      random()/(double)RAND_MAX,
1127                      1.0);
1128     } else {
1129         glClearColor(colors[idx].bg[0],
1130                      colors[idx].bg[1],
1131                      colors[idx].bg[2],
1132                      colors[idx].bg[3]);
1133     }
1134     return colors + idx;
1135 }
1136
1137
1138 /***************************
1139  ** misc helper functions
1140  ** */
1141
1142
1143 /* simple one for now.. */
1144 static inline double drunken_rando(double cur_val, double max, double width)
1145 {
1146     double r = random() / (double) RAND_MAX * 2;
1147     if (cur_val > 0)
1148         if (r >= 1)
1149             return cur_val + (r-1) * width * (1-cur_val/max);
1150         else
1151             return cur_val - r * width; 
1152     else
1153         if (r >= 1)
1154             return cur_val - (r-1) * width * (1+cur_val/max);
1155         else
1156             return cur_val + r * width; 
1157 }
1158
1159
1160 /***************************
1161  ** core crackberg routines
1162  ** */
1163
1164 ENTRYPOINT void reshape_crackberg (ModeInfo *mi, int w, int h);
1165
1166 ENTRYPOINT void init_crackberg (ModeInfo *mi)
1167 {
1168     cberg_state *cberg;
1169
1170     if (!cbergs) {
1171         nsubdivs %= 16; /* just in case.. */
1172
1173         if ( !(cbergs = calloc(MI_NUM_SCREENS(mi), sizeof(cberg_state)))) {
1174             perror(progname);
1175             exit(1);
1176         }
1177
1178         if (visibility > 1.0 || visibility < 0.2) {
1179             printf("visibility must be in range [0.2 .. 1.0]\n");
1180             visibility = 1.0;
1181         }
1182     }
1183
1184     cberg = &cbergs[MI_SCREEN(mi)];
1185     
1186     cberg->epoints = 1 + (1 << nsubdivs);
1187     cberg->tpoints = cberg->epoints * (cberg->epoints + 1) / 2;
1188     cberg->ntris = (1 << (nsubdivs << 1));
1189     cberg->tnorms = ( (flat) ? cberg->ntris : cberg->tpoints);
1190     cberg->dx0 = 1.0 / (1 << nsubdivs);
1191
1192     cberg->heights = malloc(cberg->tpoints * sizeof(double));
1193     cberg->norms = malloc(3 * cberg->tnorms * sizeof(double));
1194
1195     cberg->glx_context = init_GL(mi);
1196     cberg->motion_state = MOTION_AUTO;
1197     cberg->mspeed = 1.0;
1198     cberg->z = 0.5;
1199
1200     cberg->fovy = 60.0;
1201     cberg->zNear = 0.5;
1202     cberg->zFar = 5.0;
1203
1204     cberg->draw_elapsed = 1.0;
1205
1206     glEnable(GL_DEPTH_TEST);
1207     glEnable(GL_BLEND);
1208     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1209     glShadeModel((flat) ? GL_FLAT : GL_SMOOTH);
1210     glPolygonMode(GL_FRONT_AND_BACK, (MI_IS_WIREFRAME(mi)) ? GL_LINE : GL_FILL);
1211
1212     if (lit) {
1213         glEnable(GL_LIGHTING);
1214         glEnable(GL_LIGHT0);
1215         glEnable(GL_COLOR_MATERIAL);
1216         glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
1217         glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
1218         glEnable(GL_NORMALIZE);
1219         glEnable(GL_RESCALE_NORMAL); 
1220     }
1221
1222     cberg->color = select_color(cberg);
1223
1224     reshape_crackberg(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
1225 }
1226
1227 ENTRYPOINT void reshape_crackberg (ModeInfo *mi, int w, int h)
1228 {
1229     int h2;
1230     cberg_state *cberg = &cbergs[MI_SCREEN(mi)];
1231
1232     if (letterbox && (h2 = w * 9 / 16) < h) {
1233         glViewport(0, (h-h2)/2, w, h2);
1234         cberg->aspect = w/(double)h2;
1235     } else {
1236         glViewport (0, 0, w, h);
1237         cberg->aspect = w/(double)h;
1238     }
1239
1240     glMatrixMode(GL_PROJECTION);
1241     glLoadIdentity();
1242     gluPerspective(cberg->fovy, cberg->aspect, cberg->zNear, cberg->zFar);
1243     glMatrixMode(GL_MODELVIEW);
1244 }
1245
1246 ENTRYPOINT Bool crackberg_handle_event (ModeInfo *mi, XEvent *ev)
1247 {
1248     cberg_state *cberg = &cbergs[MI_SCREEN(mi)];
1249     KeySym keysym = 0;
1250     char c = 0;
1251     if (ev->xany.type == KeyPress || ev->xany.type == KeyRelease)
1252       XLookupString (&ev->xkey, &c, 1, &keysym, 0);
1253
1254     if (ev->xany.type == KeyPress) {
1255         switch (keysym) {
1256             case XK_Left:   cberg->motion_state |= MOTION_LROT;  break;
1257             case XK_Right:  cberg->motion_state |= MOTION_RROT;  break;
1258             case XK_Down:   cberg->motion_state |= MOTION_BACK;  break;
1259             case XK_Up:     cberg->motion_state |= MOTION_FORW;  break;
1260             case '1':       cberg->motion_state |= MOTION_DEC;   break; 
1261             case '2':       cberg->motion_state |= MOTION_INC;   break;
1262             case 'a':       cberg->motion_state |= MOTION_LEFT;  break;
1263             case 'd':       cberg->motion_state |= MOTION_RIGHT; break;
1264             case 's':       cberg->motion_state |= MOTION_BACK;  break;
1265             case 'w':       cberg->motion_state |= MOTION_FORW;  break;
1266             default:        return False;
1267         }
1268         cberg->motion_state |= MOTION_MANUAL;
1269     } else if (ev->xany.type == KeyRelease) { 
1270 #if 0
1271         XEvent peek_ev;
1272         if (XPending(mi->dpy)) {
1273             XPeekEvent(mi->dpy, &peek_ev);
1274             if (peek_ev.type == KeyPress
1275              && peek_ev.xkey.keycode == ev->xkey.keycode       
1276              && peek_ev.xkey.time - ev->xkey.time < 2) {
1277                 XNextEvent(mi->dpy, &peek_ev); /* drop bullshit repeat events */
1278                 return False;
1279             }
1280         }
1281 #endif
1282
1283         switch (keysym) {
1284             case XK_Left:   cberg->motion_state &= ~MOTION_LROT;  break;
1285             case XK_Right:  cberg->motion_state &= ~MOTION_RROT;  break;
1286             case XK_Down:   cberg->motion_state &= ~MOTION_BACK;  break;
1287             case XK_Up:     cberg->motion_state &= ~MOTION_FORW;  break;
1288             case '1':       cberg->motion_state &= ~MOTION_DEC;   break; 
1289             case '2':       cberg->motion_state &= ~MOTION_INC;   break;
1290             case 'a':       cberg->motion_state &= ~MOTION_LEFT;  break;
1291             case 'd':       cberg->motion_state &= ~MOTION_RIGHT; break;
1292             case 's':       cberg->motion_state &= ~MOTION_BACK;  break;
1293             case 'w':       cberg->motion_state &= ~MOTION_FORW;  break;
1294             case ' ':       
1295                 if (cberg->motion_state == MOTION_MANUAL)
1296                     cberg->motion_state = MOTION_AUTO;     
1297                 break;
1298             default:            return False;
1299         }
1300     } else
1301         return False;
1302     return True;
1303 }   
1304  
1305 ENTRYPOINT void draw_crackberg (ModeInfo *mi)
1306 {
1307     cberg_state *cberg = &cbergs[MI_SCREEN(mi)];
1308     struct timeval cur_frame_t;
1309     double cur_frame;
1310     static const float lpos[] = {2.0,0.0,-0.3,0.0};
1311
1312     if (!cberg->glx_context) /*XXX does this get externally tweaked? it kinda*/
1313         return;               /*XXX can't.. check it in crackberg_init*/
1314
1315     glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(cberg->glx_context));
1316
1317     gettimeofday(&cur_frame_t, NULL);
1318     cur_frame = cur_frame_t.tv_sec + cur_frame_t.tv_usec / 1.0E6;
1319     if ( cberg->prev_frame ) { /*not first run */
1320
1321         cberg->elapsed = cur_frame - cberg->prev_frame;
1322
1323         if (cberg->motion_state == MOTION_AUTO) {
1324             cberg->x += cberg->dx * cberg->elapsed;
1325             cberg->y += cberg->dy * cberg->elapsed;
1326             /* cberg->z */
1327             /* cberg->pitch */
1328             /* cberg->roll */
1329             cberg->yaw += cberg->dyaw * cberg->elapsed;
1330
1331             cberg->draw_elapsed += cberg->elapsed;
1332             if (cberg->draw_elapsed >= 0.8) {
1333                 cberg->draw_elapsed = 0.0;
1334                 cberg->dx = drunken_rando(cberg->dx, 2.5, 0.8);
1335                 cberg->dy = drunken_rando(cberg->dy, 2.5, 0.8);
1336                 /* cberg->dz */
1337                 /* cberg->dpitch */
1338                 /* cberg->droll */
1339                 cberg->dyaw = drunken_rando(cberg->dyaw, 40.0,  8.0);
1340             }
1341         } else {
1342             double scale = cberg->elapsed * cberg->mspeed;
1343             if (cberg->motion_state & MOTION_BACK) {
1344                 cberg->x -= cos(cberg->yaw * M_PI_180) * scale;
1345                 cberg->y -= sin(cberg->yaw * M_PI_180) * scale;
1346             }
1347             if (cberg->motion_state & MOTION_FORW) {
1348                 cberg->x += cos(cberg->yaw * M_PI_180) * scale;
1349                 cberg->y += sin(cberg->yaw * M_PI_180) * scale;
1350             }
1351
1352             if (cberg->motion_state & MOTION_LEFT) {
1353                 cberg->x -= sin(cberg->yaw * M_PI_180) * scale;
1354                 cberg->y += cos(cberg->yaw * M_PI_180) * scale;
1355             }
1356             if (cberg->motion_state & MOTION_RIGHT) {
1357                 cberg->x += sin(cberg->yaw * M_PI_180) * scale;
1358                 cberg->y -= cos(cberg->yaw * M_PI_180) * scale;
1359             }
1360
1361             if (cberg->motion_state & MOTION_LROT)
1362                 cberg->yaw += 45 * scale;
1363             if (cberg->motion_state & MOTION_RROT)
1364                 cberg->yaw -= 45 * scale;
1365
1366             if (cberg->motion_state & MOTION_DEC)
1367                 cberg->mspeed /= pow(MSPEED_SCALE, cberg->draw_elapsed);
1368             if (cberg->motion_state & MOTION_INC)
1369                 cberg->mspeed *= pow(MSPEED_SCALE, cberg->draw_elapsed);
1370
1371         }
1372     }
1373     cberg->prev_frame = cur_frame;
1374
1375     mark_visible(cberg);
1376     triles_update_state(&(cberg->trile_head), cberg);
1377         
1378     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1379     glLoadIdentity();
1380     glRotatef(current_device_rotation(), 0, 0, 1);
1381     gluLookAt(0,0,0, 1,0,0, 0,0,1);
1382     glLightfv(GL_LIGHT0, GL_POSITION, lpos);
1383     /*glRotated(cberg->roll, 1,0,0); / * XXX blah broken and unused for now..* /
1384     glRotated(cberg->pitch, 0,1,0); */
1385     glRotated(-cberg->yaw, 0,0,1); /* camera sees ->yaw over */
1386     glTranslated(-cberg->x, -cberg->y, -cberg->z);
1387
1388     mi->polygon_count = cberg->ntris * 
1389       triles_foreach(cberg->trile_head, trile_draw,(void *) cberg);
1390     
1391     if (mi->fps_p)  
1392         do_fps(mi);
1393
1394 #ifdef DEBUG
1395     glBegin(GL_LINES);
1396         glColor3f(1.0,0.0,0.0);
1397         glVertex3d(x_shit, y_shit, 0.0);
1398         glVertex3d(x_shit, y_shit, 1.0);
1399     glEnd();
1400 #endif
1401
1402     glFinish();
1403     glXSwapBuffers(MI_DISPLAY(mi), MI_WINDOW(mi));
1404 }
1405
1406 /* uh */
1407 ENTRYPOINT void release_crackberg (ModeInfo *mi)
1408 {
1409   if (cbergs) {
1410     int screen;
1411     for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
1412       cberg_state *cberg = &cbergs[screen];
1413       if (cberg->norms)
1414         free(cberg->norms);
1415       free(cberg->heights);
1416     }
1417     free (cbergs);
1418     cbergs = 0;
1419   }
1420 }
1421
1422 XSCREENSAVER_MODULE ("Crackberg", crackberg)
1423
1424 #endif /* USE_GL */