1face865e3ff8477117f8b89e37d68bea29b9084
[xscreensaver] / hacks / glx / providence.c
1 /*
2  * Permission to use, copy, modify, and distribute this software and its
3  * documentation for any purpose and without fee is hereby granted,
4  * provided that the above copyright notice appear in all copies and that
5  * both that copyright notice and this permission notice appear in
6  * supporting documentation.
7  *
8  * This file is provided AS IS with no warranties of any kind.  The author
9  * shall have no liability with respect to the infringement of copyrights,
10  * trade secrets or any patents by this file or any part thereof.  In no
11  * event will the author be liable for any lost revenue or profits or
12  * other special, indirect and consequential damages.
13  *
14  * Copyright 2004 Blair Tennessy
15  */
16
17 #include <X11/Intrinsic.h>
18
19 #ifdef STANDALONE
20 #define PROGCLASS           "Providence"
21 #define HACK_INIT           init_providence
22 #define HACK_DRAW           draw_providence
23 #define HACK_RESHAPE        reshape_providence
24 #define HACK_HANDLE_EVENT   providence_handle_event
25 #define EVENT_MASK          PointerMotionMask
26 #define providence_opts     xlockmore_opts
27 #define DEFAULTS            "*delay:   20000   \n" \
28                             "*showFPS: False   \n" \
29                             "*wireframe: False \n"
30
31 #include "xlockmore.h"
32 #else
33 #include "xlock.h"
34 #endif
35
36 #include <GL/glu.h>
37 #include "gltrackball.h"
38
39 #define DEF_SOLIDPROVIDENCE  "False"
40 #define DEF_EYE  "True"
41
42 static int eye;
43
44 static XrmOptionDescRec opts[] = {
45   {(char *) "-eye", 
46    (char *) ".providence.eye", XrmoptionNoArg, (caddr_t) "on"},
47
48   {(char *) "+eye", 
49    (char *) ".providence.eye", XrmoptionNoArg, (caddr_t) "off"}
50 };
51
52 static argtype vars[] = {
53   {(caddr_t *) &eye, 
54    (char *) "eye", (char *) "Eye", (char *) DEF_EYE, t_Bool}
55 };
56
57 static OptionStruct desc[] = {
58   {(char *) "-/+eye", 
59    (char *) "turn on/off eye of providence"}
60 };
61
62 ModeSpecOpt providence_opts = {
63   sizeof opts / sizeof opts[0], opts, 
64   sizeof vars / sizeof vars[0], vars, desc
65 };
66
67 #ifdef USE_MODULES
68 ModStruct   providence_description = {
69   "providence", "init_providence", "draw_providence", 
70   "release_providence", "draw_providence", "change_providence", 
71   (char *) NULL, &providence_opts, 1000, 1, 1, 1, 4, 1.0, "",
72   "draws pyramid with glory", 0, NULL
73 };
74 #endif
75
76 #define Scale4Window               0.3
77 #define Scale4Iconic               0.4
78
79 #define sqr(A)                     ((A)*(A))
80
81 #ifndef Pi
82 #define Pi M_PI
83 #endif
84
85 int mono = 0, wire = 0;
86 double camera_velocity = 0.0;
87 double camera_z = -8.0;
88 int camera_mode = 0;
89
90 typedef struct {
91   GLint       WindH, WindW;
92   GLXContext *glx_context;
93   trackball_state *trackball;
94   Bool        button_down_p;
95 } providencestruct;
96
97 /* lighting variables */
98 GLfloat front_shininess[] = {60.0};
99 GLfloat front_specular[] = {0.2, 0.2, 0.2, 1.0};
100 GLfloat ambient[] = {0.8, 0.8, 0.8, 1.0};
101 GLfloat ambient2[] = {0.25, 0.25, 0.25, 1.0};
102 GLfloat diffuse[] = {1.0, 1.0, 1.0, 1.0};
103 GLfloat position0[] = {1.0, 5.0, 1.0, 1.0};
104 GLfloat position1[] = {-1.0, -5.0, 1.0, 1.0};
105 GLfloat lmodel_ambient[] = {0.5, 0.5, 0.5, 1.0};
106 GLfloat lmodel_twoside[] = {GL_TRUE};
107
108 /* gray-gray */
109
110 GLfloat MaterialGlory[] = {0.04, 0.30, 0.22, 0.7};
111 GLfloat MaterialGloryB[] = {0.07, 0.50, 0.36, 0.6};
112
113 GLfloat MaterialGloryF[] = {0.07, 0.50, 0.36, 1.0};
114 /* GLfloat MaterialGloryF[] = {0.06, 0.38, 0.27, 1.0}; */
115 GLfloat MaterialGloryE[] = {0.06, 0.38, 0.27, 0.3};
116 GLfloat MaterialGloryM[] = {0.5, 0.5, 0.5, 0.5};
117 GLfloat MaterialGloryMB[] = {0.36, 0.36, 0.36, 0.4};
118 GLfloat MaterialGreenback[4] = {0.04, 0.30, 0.22, 1.0};
119 GLfloat MaterialBlack[4] = {0.0, 0.0, 0.0, 1.0};
120
121 GLfloat MaterialGray5[] = {0.5, 0.5, 0.5, 1.0};
122 GLfloat MaterialGray6[] = {0.6, 0.6, 0.6, 1.0};
123
124 double currenttime;
125
126 static providencestruct *providence = (providencestruct *) NULL;
127
128 #define NUM_SCENES      2
129
130 /* brick texture */
131 #define checkImageWidth 64
132 #define checkImageHeight 64
133 GLubyte checkImage[checkImageWidth][checkImageHeight][3];
134 GLuint bricktexture;
135
136 /* build brick texture */
137 void make_brick(void) {
138   int i, j, c;
139
140   for (i = 0; i < checkImageWidth; i++) {
141     for (j = 0; j < checkImageHeight; j++) {
142       c = i % 16 == 15 ? 255 : (j + 48*(i / 16))%64 == 0 ? 255 : 
143         102 + random() % 102;
144       checkImage[i][j][0] = (GLubyte) c;
145       checkImage[i][j][1] = (GLubyte) c;
146       checkImage[i][j][2] = (GLubyte) c;
147     }
148   }
149
150   glGenTextures(1, &bricktexture);
151   glBindTexture(GL_TEXTURE_2D, bricktexture);
152
153   glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
154   glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
155   glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
156   glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
157   glTexImage2D(GL_TEXTURE_2D, 0, 3, checkImageWidth, 
158                checkImageHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, 
159                &checkImage[0][0]);
160   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
161 }
162
163 /* int min */
164 int mini(int a, int b) { return a < b ? a : b; }
165
166 /* eye particles */
167 #define EYE_PARTICLE_COUNT 2000
168 int eyeparticles[EYE_PARTICLE_COUNT][2];
169
170 /* lookup table for the eye */
171 #define LOOKUPSIZE 3600
172 #define EYELENGTH 300
173 double lookup[LOOKUPSIZE][EYELENGTH][2];
174 double lookup2[LOOKUPSIZE][EYELENGTH][2];
175
176 /* build eye lookup table */
177 void build_eye(void) {
178   int i, j;
179   double x;
180   double inc = 0.1 / EYELENGTH;
181   double inc2 = 2.4*Pi / EYELENGTH;
182
183   /* describe all values tangentially out from pupil */
184   for(i = 0; i < LOOKUPSIZE; ++i) {
185     double r = i * 2*Pi / LOOKUPSIZE;/*x + inc;*/
186     double sr = sin(r);
187     double cr = cos(r);
188     x = 0.07;
189
190     for(j = 0; j < EYELENGTH; ++j) {
191       lookup[i][j][0] = x*sr;
192       lookup[i][j][1] = x*cr;
193       x += inc;
194     }
195   }
196
197   /* lookup2: dollar sign */
198   for(i = 0; i < LOOKUPSIZE; ++i) {
199     double y = -1.2*Pi;
200     
201     for(j = 0; j < EYELENGTH; ++j) {
202       if(i % 2) {
203         lookup2[i][j][0] = sin(y)/6.0 + i/36000.0 - 0.05;
204         lookup2[i][j][1] = i%4 ? y/12.0 - 0.05 : 1.2*Pi-y/12.0 + 0.05;
205       }
206       else {
207         lookup2[i][j][0] = i/36000.0 - 0.05;
208         lookup2[i][j][1] = y/9.0 - 0.05;
209       }
210       y += inc2;
211     }
212   }
213 }
214
215 int pyramidlist;
216 #define EPSILON 0.0001
217
218 double min(double a, double b) {
219   return a < b ? a : b;
220 }
221
222 double max(double a, double b) {
223   return a > b ? a : b;
224 }
225
226 #define PARTICLE_COUNT 2000
227 double particles[PARTICLE_COUNT][5];
228
229 void init_particle(double particle[5]) {
230   /* position along glory */
231   double p = (random() % 485410) / 100000.0;
232
233   /* on a plane */
234   particle[2] = 0.0;
235   
236   if(p < 1.5) {
237     particle[0] = p - 0.75;
238     particle[1] = -0.75001;
239   }
240   else if(p < 1.5 + sqrt(45)/4.0) {
241     double d = p - 1.5;
242     particle[0] = 0.75 - d*cos(atan(2.0));
243     particle[1] = d*sin(atan(2.0)) - 0.75;
244   }
245   else {
246     double d = 4.8541 - p;
247     particle[0] = -0.75 + d*cos(atan(2.0));
248     particle[1] = d*sin(atan(2.0)) - 0.75;
249   }
250
251   particle[3] = currenttime;
252   particle[4] = 1.25 + (random()%10)/10.0;
253 }
254
255 /* init glory particles */
256 void init_particles(void) {
257   int i;
258   for(i = 0; i < PARTICLE_COUNT; ++i) {
259     init_particle(particles[i]);
260
261     /* set initial time */
262     particles[i][3] = currenttime - (random()%1250)/1000.0;
263   }
264
265   /* init eye particles */
266   for(i = 0; i < EYE_PARTICLE_COUNT; ++i) {
267     eyeparticles[i][0] = random()%LOOKUPSIZE;
268     eyeparticles[i][1] = random()%EYELENGTH;
269   }
270 }
271
272 #define FPS 50
273 double theta = 0.0;
274
275 /* ugg, should be a priority queue if next event times known */
276 void update_particles(void) {
277   int i;
278
279   for(i = 0; i < PARTICLE_COUNT; ++i) {
280     /* check for time elapse */
281     if(currenttime > particles[i][3] + particles[i][4])
282       init_particle(particles[i]);
283   }
284
285   /* now update eye particles */
286   for(i = 0; i < EYE_PARTICLE_COUNT; ++i) {
287 /*     int x = eyeparticles[i][1] + random()%16; */
288     int x = eyeparticles[i][1] + random()%(cos(theta) < 0.0 ? 8 : 16);
289
290     /* reset if dead */
291     if(x > EYELENGTH || random()%(cos(theta) < 0.0 ? 40 : 10) == 0) {
292
293 /*     if(x > EYELENGTH || (x > EYELENGTH/(2/3.0) && random()%7 == 0)) { */
294       eyeparticles[i][0] = random()%LOOKUPSIZE;
295       eyeparticles[i][1] = random()%40;
296     }
297     else {
298       eyeparticles[i][1] = x;
299     }    
300   }
301 }
302
303 /* draw the pyramid */
304 void draw_seal(void) {
305   int i;
306   double base = sqrt(2.0);
307   double top = 1.0 / sqrt(2.0);
308   double tmod = 7.0/6.0;
309
310   glPushMatrix();
311
312   /* set options for mono, wireframe */
313   if(wire) {
314     glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
315     glDisable(GL_LIGHTING);
316     glDisable(GL_TEXTURE_2D);
317   }
318   else {
319     glEnable(GL_TEXTURE_2D);
320     glBindTexture(GL_TEXTURE_2D, bricktexture);
321
322     glEnable(GL_LIGHTING);
323
324     glColor4fv(mono ? MaterialGray5 : MaterialGloryF);
325     glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, 
326                  mono ? MaterialGray5 : MaterialGloryF);
327   }
328
329   glRotatef(45.0, 0.0, 1.0, 0.0);
330   glTranslatef(0.0, -3.25, 0.0);
331
332   for(i = 0; i < 4; ++i) {
333     glRotatef(i*90.0, 0.0, 1.0, 0.0);
334
335     glBegin(GL_QUADS);
336     glNormal3f(1 / sqrt(6.0), 2 / sqrt(6.0), 1 / sqrt(6.0));
337     glTexCoord2f(-base, 0.0);
338     glVertex3f(-base, 0.0, base);
339     glTexCoord2f(base, 0.0);
340     glVertex3f(base, 0.0, base);
341     glTexCoord2f(top, 13.0/4.0);
342     glVertex3f(top, 2.0, top);
343     glTexCoord2f(-top, 13.0/4.0);
344     glVertex3f(-top, 2.0, top);
345     glEnd();
346   }
347
348   glBegin(GL_QUADS);
349
350   /* top */
351   glNormal3f(0.0, 1.0, 0.0);
352   glTexCoord2f(0.02, 0.0);
353   glVertex3f(-top, 2.0, top);
354   glTexCoord2f(2.0*top, 0.0);
355   glVertex3f(top, 2.0, top);
356   glTexCoord2f(2.0*top, tmod*2.1*top);
357   glVertex3f(top, 2.0, -top);
358   glTexCoord2f(0.02, tmod*2.1*top);
359   glVertex3f(-top, 2.0, -top);
360
361   /* base */
362   glNormal3f(0.0, -1.0, 0.0);
363   glTexCoord2f(-base, 0.0);
364   glVertex3f(-base, 0.0, -base);
365   glTexCoord2f(top, 0.0);
366   glVertex3f(base, 0.0, -base);
367   glTexCoord2f(top, top*13.0/4.0);
368   glVertex3f(base, 0.0, base);
369   glTexCoord2f(-top, top*13.0/4.0);
370   glVertex3f(-base, 0.0, base);
371
372   glEnd();
373
374   glPopMatrix();
375   glDisable(GL_TEXTURE_2D);
376 }
377
378 /* draw glory */
379 void draw_glory(void) {
380   int i;
381
382   if(wire) {
383     glBegin(GL_TRIANGLES);
384     glVertex3f(-0.75, -0.75, 0.0);
385     glVertex3f(0.75, -0.75, 0.0);
386     glVertex3f(0.0, 0.75, 0.0);
387     glEnd();
388     return;
389   }
390
391   /* draw particles */
392   glDisable(GL_LIGHTING);
393   glPushMatrix();
394   glEnable(GL_BLEND);
395
396   /* glory colour lines */
397   glColor4fv(mono ? MaterialGloryM : MaterialGlory);
398   glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, 
399                mono ? MaterialGloryM : MaterialGlory);
400
401   glBegin(GL_LINES);
402   for(i = 0; i < PARTICLE_COUNT/2; ++i) {
403     double t = currenttime - particles[i][3];
404     double th = atan(particles[i][1] / particles[i][0]);
405     if(particles[i][0] < 0.0)
406       th += Pi;
407
408     glVertex3f(particles[i][0], particles[i][1], particles[i][2]);
409     glVertex3f(particles[i][0] + 0.2*cos(th)*t,
410                particles[i][1] + 0.2*sin(th)*t,
411                particles[i][2]);
412   }
413   glEnd();
414   
415   /* gloryb colour lines */
416   glColor4fv(mono ? MaterialGloryMB : MaterialGloryB);
417   glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, 
418                mono ? MaterialGloryMB : MaterialGloryB);
419   glBegin(GL_LINES);
420   for(; i < PARTICLE_COUNT; ++i) {
421     double t = currenttime - particles[i][3];
422     double th = atan(particles[i][1] / particles[i][0]);
423     if(particles[i][0] < 0.0)
424       th += Pi;
425
426     glVertex3f(particles[i][0], particles[i][1], particles[i][2]);
427     glVertex3f(particles[i][0] + 0.2*cos(th)*t,
428                particles[i][1] + 0.2*sin(th)*t,
429                particles[i][2]);
430   }
431   glEnd();
432
433   glPopMatrix();
434   glEnable(GL_LIGHTING);
435 }
436
437 /* draw eye of providence */
438 void draw_eye(void) {
439   int i;
440
441   /* draw wireeye */
442   if(wire) {
443     glBegin(GL_TRIANGLES);
444     glVertex3f(-0.25, -0.25, 0.0);
445     glVertex3f(0.25, -0.25, 0.0);
446     glVertex3f(0.0, 0.25, 0.0);
447     glEnd();
448     return;
449   }
450
451   /* draw particles */
452   glDisable(GL_LIGHTING);
453   glPushMatrix();
454   glEnable(GL_BLEND);
455
456   /* eye */
457   glColor4fv(mono ? MaterialGloryM : MaterialGlory);
458   glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, 
459                mono ? MaterialGloryM : MaterialGlory);
460
461   /* draw eye particles on z = 0 plane */
462   glBegin(GL_POINTS);
463   for(i = 0; i < EYE_PARTICLE_COUNT/2; ++i) {
464     glVertex3f(lookup[eyeparticles[i][0]][eyeparticles[i][1]][0], 
465                lookup[eyeparticles[i][0]][eyeparticles[i][1]][1],
466                0.0);
467   }
468   glEnd();
469
470   /* eye */
471   glColor4fv(mono ? MaterialGloryMB : MaterialGloryB);
472   glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, 
473                mono ? MaterialGloryMB : MaterialGloryB);
474
475   /* draw eye particles on z = 0 plane */
476   glBegin(GL_POINTS);
477   for(; i < EYE_PARTICLE_COUNT; ++i) {
478     glVertex3f(lookup[eyeparticles[i][0]][eyeparticles[i][1]][0], 
479                lookup[eyeparticles[i][0]][eyeparticles[i][1]][1],
480                0.0);
481   }
482   glEnd();
483
484
485   /* draw scaled particles */
486   glPushMatrix();
487   glScalef(3.3, 2.2, 3.3);
488
489   /* eye */
490   glColor4fv(mono ? MaterialGloryMB : MaterialGloryB);
491   glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, 
492                mono ? MaterialGloryMB : MaterialGloryB);
493
494   /* draw eye particles on z = 0 plane */
495   glBegin(GL_POINTS);
496   for(i = 0; i < EYE_PARTICLE_COUNT/2; ++i) {
497     glVertex3f(lookup[eyeparticles[i][0]][eyeparticles[i][1]][0], 
498                lookup[eyeparticles[i][0]][eyeparticles[i][1]][1],
499                0.0);
500   }
501   glEnd();
502
503   glColor4fv(mono ? MaterialGloryM : MaterialGlory);
504   glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, 
505                mono ? MaterialGloryM : MaterialGlory);
506
507   /* draw eye particles on z = 0 plane */
508   glBegin(GL_POINTS);
509   for(; i < EYE_PARTICLE_COUNT; ++i) {
510     glVertex3f(lookup[eyeparticles[i][0]][eyeparticles[i][1]][0], 
511                lookup[eyeparticles[i][0]][eyeparticles[i][1]][1],
512                0.0);
513   }
514   glEnd();
515
516   glPopMatrix();
517
518   glPopMatrix();
519   glEnable(GL_LIGHTING);
520 }
521
522 /* draw eye of providence */
523 void draw_eye2(void) {
524   int i;
525
526   /* draw wireeye */
527   if(wire) {
528     glBegin(GL_TRIANGLES);
529     glVertex3f(-0.25, -0.25, 0.0);
530     glVertex3f(0.25, -0.25, 0.0);
531     glVertex3f(0.0, 0.25, 0.0);
532     glEnd();
533     return;
534   }
535
536   /* draw particles */
537   glDisable(GL_LIGHTING);
538   glPushMatrix();
539   glEnable(GL_BLEND);
540
541   /* eye */
542   glColor4fv(mono ? MaterialGloryM : MaterialGlory);
543   glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, 
544                mono ? MaterialGloryM : MaterialGlory);
545
546   /* draw eye particles on z = 0 plane */
547   glBegin(GL_POINTS);
548   for(i = 0; i < EYE_PARTICLE_COUNT/2; ++i) {
549     glVertex3f(lookup2[eyeparticles[i][0]][eyeparticles[i][1]][0], 
550                lookup2[eyeparticles[i][0]][eyeparticles[i][1]][1],
551                0.0);
552   }
553   glEnd();
554
555   /* eye */
556   glColor4fv(mono ? MaterialGloryMB : MaterialGloryB);
557   glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, 
558                mono ? MaterialGloryMB : MaterialGloryB);
559
560   /* draw eye particles on z = 0 plane */
561   glBegin(GL_POINTS);
562   for(; i < EYE_PARTICLE_COUNT; ++i) {
563     glVertex3f(lookup2[eyeparticles[i][0]][eyeparticles[i][1]][0], 
564                lookup2[eyeparticles[i][0]][eyeparticles[i][1]][1],
565                0.0);
566   }
567   glEnd();
568
569   glPopMatrix();
570   glEnable(GL_LIGHTING);
571 }
572
573 /* draw the scene */
574 void draw_providence_strip(ModeInfo *mi) {
575   glTranslatef(0.0, 1.414, 0.0);
576
577   position0[0] = 1.6*sin(theta);
578   position0[1] = 1.2;
579   position0[2] = 1.6*cos(theta);
580   position0[3] = 0.0;
581   glLightfv(GL_LIGHT0, GL_POSITION, position0);
582   glLightfv(GL_LIGHT0, GL_AMBIENT, ambient2);
583   glEnable(GL_LIGHTING);
584   glEnable(GL_LIGHT0);
585
586   /* draw pyramid, glory */
587   glDisable(GL_BLEND);
588   glCallList(pyramidlist);
589   draw_glory();
590   if(eye) {
591     if(cos(theta) < 0.0)
592       draw_eye2();
593     else
594       draw_eye();
595   }
596
597   return;
598 }
599
600 void reshape_providence(ModeInfo * mi, int width, int height) {
601   double h = (GLfloat) height / (GLfloat) width;  
602   providencestruct *mp = &providence[MI_SCREEN(mi)];
603
604   glViewport(0, 0, mp->WindW = (GLint) width, mp->WindH = (GLint) height);
605   glMatrixMode(GL_PROJECTION);
606   glLoadIdentity();
607
608   gluPerspective(45, 1/h, 0.001, 25.0);
609
610   glMatrixMode(GL_MODELVIEW);
611   glLineWidth(2.0);
612   glPointSize(2.0);
613 }
614
615 static void pinit(void) {
616   glClearDepth(1.0);
617   glClearColor(0.0, 0.0, 0.0, 1.0);
618   
619   /* setup twoside lighting */
620   glLightfv(GL_LIGHT0, GL_AMBIENT, ambient2);
621   glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
622   glLightfv(GL_LIGHT0, GL_POSITION, position0);
623
624   glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
625   glLightModelfv(GL_LIGHT_MODEL_TWO_SIDE, lmodel_twoside);
626   glEnable(GL_LIGHTING);
627   glEnable(GL_LIGHT0);
628
629   currenttime = 0.0;
630   init_particles();
631   make_brick();
632   build_eye();
633
634   glEnable(GL_NORMALIZE);
635   glFrontFace(GL_CCW);
636 /*   glDisable(GL_CULL_FACE); */
637   glEnable(GL_CULL_FACE);
638   glCullFace(GL_BACK);
639   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
640
641   glShadeModel(GL_SMOOTH);
642   glEnable(GL_DEPTH_TEST);
643   glDepthFunc(GL_LEQUAL);
644
645   /* build pyramid list */
646   pyramidlist = glGenLists(1);
647   glNewList(pyramidlist, GL_COMPILE);
648   draw_seal();
649   glEndList();
650 }
651
652 /* cleanup routine */
653 void release_providence(ModeInfo * mi) {
654
655   if(providence) {
656     free((void *) providence);
657     providence = (providencestruct *) NULL;
658   }
659
660   FreeAllGL(mi);
661 }
662
663 /* event handling */
664 Bool providence_handle_event(ModeInfo *mi, XEvent *event) {
665   providencestruct *mp = &providence[MI_SCREEN(mi)];
666
667   switch(event->xany.type) {
668   case ButtonPress:
669
670     switch(event->xbutton.button) {
671
672     case Button1:
673       mp->button_down_p = True;
674       gltrackball_start(mp->trackball, 
675                         event->xbutton.x, event->xbutton.y,
676                         MI_WIDTH (mi), MI_HEIGHT (mi));
677       break;
678       
679     case Button4:
680       camera_velocity += 1.0;
681       break;
682
683     case Button5:
684       camera_velocity -= 1.0;
685       break;
686     }
687
688     break;
689     
690   case ButtonRelease:
691
692     switch(event->xbutton.button) {
693     case Button1:
694       mp->button_down_p = False;
695       break;
696     }
697
698     break;
699
700   case MotionNotify:
701     if(mp->button_down_p)
702       gltrackball_track(mp->trackball,
703                         event->xmotion.x, event->xmotion.y,
704                         MI_WIDTH (mi), MI_HEIGHT (mi));
705     break;
706     
707   default:
708     return False;
709   }
710
711   return True;
712 }
713
714 void init_providence(ModeInfo *mi) {
715   providencestruct *mp;
716   
717   if(!providence) {
718     if((providence = (providencestruct *) 
719         calloc(MI_NUM_SCREENS(mi), sizeof (providencestruct))) == NULL)
720       return;
721   }
722   mp = &providence[MI_SCREEN(mi)];
723   mp->trackball = gltrackball_init ();
724
725   mono = MI_IS_MONO(mi);
726   wire = MI_IS_WIREFRAME(mi);
727
728   if((mp->glx_context = init_GL(mi)) != NULL) {
729     reshape_providence(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
730     glDrawBuffer(GL_BACK);
731     pinit();
732   }
733   else
734     MI_CLEARWINDOW(mi);
735 }
736
737 void draw_providence(ModeInfo * mi) {
738   providencestruct *mp;
739   
740   Display    *display = MI_DISPLAY(mi);
741   Window      window = MI_WINDOW(mi);
742   
743   if(!providence)
744     return;
745   mp = &providence[MI_SCREEN(mi)];
746   
747   MI_IS_DRAWN(mi) = True;
748   
749   if(!mp->glx_context)
750     return;
751   
752   glXMakeCurrent(display, window, *(mp->glx_context));
753   
754   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
755
756   glPushMatrix();
757
758   /* modify camera */
759   if(fabs(camera_velocity) > EPSILON) {
760     camera_z = max(min(camera_z + 0.1*camera_velocity, -4.0), -12.0);
761     camera_velocity = 0.95*camera_velocity;
762   }
763   
764   /* rotate providence */
765   glTranslatef(0.0, 0.0, camera_z + sin(theta/4.0));
766   glRotatef(10.0+20.0*sin(theta/2.0), 1.0, 0.0, 0.0);
767   gltrackball_rotate(mp->trackball);
768   glRotatef(theta * 180.0 / Pi, 0.0, -1.0, 0.0);
769
770   /* draw providence */
771   draw_providence_strip(mi);
772   glPopMatrix();
773   
774   if(MI_IS_FPS(mi)) do_fps (mi);
775   glFlush();
776   
777   glXSwapBuffers(display, window);
778
779   /* update */
780   currenttime += 1.0 / FPS;
781   theta = currenttime / 2.0;
782   update_particles();
783 }
784
785 void change_providence(ModeInfo * mi) {
786   providencestruct *mp = &providence[MI_SCREEN(mi)];
787   
788   if (!mp->glx_context)
789         return;
790   
791   glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(mp->glx_context));
792   pinit();
793 }