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