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