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