http://packetstormsecurity.org/UNIX/admin/xscreensaver-3.30.tar.gz
[xscreensaver] / hacks / glx / dangerball.c
1 /* dangerball, Copyright (c) 2001 Jamie Zawinski <jwz@jwz.org>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or 
9  * implied warranty.
10  */
11
12 #include <X11/Intrinsic.h>
13
14 extern XtAppContext app;
15
16 #define PROGCLASS       "DangerBall"
17 #define HACK_INIT       init_ball
18 #define HACK_DRAW       draw_ball
19 #define HACK_RESHAPE    reshape_ball
20 #define sws_opts        xlockmore_opts
21
22 #define DEF_SPIN        "True"
23 #define DEF_WANDER      "True"
24 #define DEF_SPEED       "0.05"
25
26 #define DEFAULTS        "*delay:        30000       \n" \
27                         "*count:        30          \n" \
28                         "*showFPS:      False       \n" \
29                         "*wireframe:    False       \n" \
30                         "*speed:      " DEF_SPEED " \n" \
31                         "*spin:       " DEF_SPIN   "\n" \
32                         "*wander:     " DEF_WANDER "\n" \
33
34
35 #define SPHERE_SLICES 32  /* how densely to render spheres */
36 #define SPHERE_STACKS 16
37
38
39 #define SPIKE_FACES  12   /* how densely to render spikes */
40
41
42 #undef countof
43 #define countof(x) (sizeof((x))/sizeof((*x)))
44
45 #include "xlockmore.h"
46 #include "colors.h"
47 #include <ctype.h>
48
49 #ifdef USE_GL /* whole file */
50
51 #ifdef HAVE_UNAME
52 # include <sys/utsname.h>
53 #endif /* HAVE_UNAME */
54
55
56 #include <GL/glu.h>
57 #include "glutstroke.h"
58 #include "glut_roman.h"
59 #define GLUT_FONT (&glutStrokeRoman)
60
61
62 typedef struct {
63   GLXContext *glx_context;
64
65   GLfloat rotx, roty, rotz;        /* current object rotation */
66   GLfloat dx, dy, dz;              /* current rotational velocity */
67   GLfloat ddx, ddy, ddz;           /* current rotational acceleration */
68   GLfloat d_max;                   /* max velocity */
69
70   GLuint ball_list;
71   GLuint spike_list;
72
73   GLfloat pos;
74   int *spikes;
75
76   int ncolors;
77   XColor *colors;
78   int ccolor;
79   int color_shift;
80
81 } ball_configuration;
82
83 static ball_configuration *bps = NULL;
84
85 static char *do_spin;
86 static GLfloat speed;
87 static Bool do_wander;
88
89 static XrmOptionDescRec opts[] = {
90   { "-spin",   ".spin",   XrmoptionNoArg, "True" },
91   { "+spin",   ".spin",   XrmoptionNoArg, "False" },
92   { "-speed",  ".speed",  XrmoptionSepArg, 0 },
93   { "-wander", ".wander", XrmoptionNoArg, "True" },
94   { "+wander", ".wander", XrmoptionNoArg, "False" }
95 };
96
97 static argtype vars[] = {
98   {(caddr_t *) &do_spin,   "spin",   "Spin",   DEF_SPIN,   t_Bool},
99   {(caddr_t *) &do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
100   {(caddr_t *) &speed,     "speed",  "Speed",  DEF_SPEED,  t_Float},
101 };
102
103 ModeSpecOpt sws_opts = {countof(opts), opts, countof(vars), vars, NULL};
104
105
106
107 static void
108 unit_spike (Bool wire)
109 {
110   int i;
111   int faces = SPIKE_FACES;
112   GLfloat step = M_PI * 2 / faces;
113   GLfloat th;
114
115   glFrontFace(GL_CW);
116   glBegin(wire ? GL_LINE_STRIP : GL_TRIANGLE_FAN);
117
118   glNormal3f(0, 1, 0);
119   glVertex3f(0, 1, 0);
120   for (i = 0, th = 0; i <= faces; i++)
121     {
122       GLfloat x = cos (th);
123       GLfloat y = sin (th);
124       glNormal3f(x, 0, y);
125       glVertex3f(x, 0, y);
126       if (wire) glVertex3f(0, 1, 0);
127       th += step;
128     }
129   glEnd();
130 }
131
132
133 /* lifted from glplanet */
134 /* Function for determining points on the surface of the sphere */
135 static void
136 parametric_sphere (float theta, float rho, GLfloat *vector)
137 {
138   vector[0] = -sin(theta) * sin(rho);
139   vector[1] = cos(theta) * sin(rho);
140   vector[2] = cos(rho);
141 }
142
143 /* lifted from glplanet */
144 static void
145 unit_sphere (Bool wire)
146 {
147   int stacks = SPHERE_STACKS;
148   int slices = SPHERE_SLICES;
149
150   int i, j;
151   float drho, dtheta;
152   float rho, theta;
153   GLfloat vector[3];
154   GLfloat ds, dt, t, s;
155
156   if (!wire)
157     glShadeModel(GL_SMOOTH);
158
159   /* Generate a sphere with quadrilaterals.
160    * Quad vertices are determined using a parametric sphere function.
161    * For fun, you could generate practically any parameteric surface and
162    * map an image onto it. 
163    */
164   drho = M_PI / stacks;
165   dtheta = 2.0 * M_PI / slices;
166   ds = 1.0 / slices;
167   dt = 1.0 / stacks;
168
169   glFrontFace(GL_CCW);
170   glBegin( wire ? GL_LINE_LOOP : GL_QUADS );
171
172   t = 0.0;
173   for (i=0; i < stacks; i++) {
174     rho = i * drho;
175     s = 0.0;
176     for (j=0; j < slices; j++) {
177       theta = j * dtheta;
178
179       glTexCoord2f (s,t);
180       parametric_sphere (theta, rho, vector);
181       glNormal3fv (vector);
182       parametric_sphere (theta, rho, vector);
183       glVertex3f (vector[0], vector[1], vector[2]);
184
185       glTexCoord2f (s,t+dt);
186       parametric_sphere (theta, rho+drho, vector);
187       glNormal3fv (vector);
188       parametric_sphere (theta, rho+drho, vector);
189       glVertex3f (vector[0], vector[1], vector[2]);
190
191       glTexCoord2f (s+ds,t+dt);
192       parametric_sphere (theta + dtheta, rho+drho, vector);
193       glNormal3fv (vector);
194       parametric_sphere (theta + dtheta, rho+drho, vector);
195       glVertex3f (vector[0], vector[1], vector[2]);
196
197       glTexCoord2f (s+ds, t);
198       parametric_sphere (theta + dtheta, rho, vector);
199       glNormal3fv (vector);
200       parametric_sphere (theta + dtheta, rho, vector);
201       glVertex3f (vector[0], vector[1], vector[2]);
202
203       s = s + ds;
204     }
205     t = t + dt;
206   }
207   glEnd();
208 }
209
210
211
212 /* Window management, etc
213  */
214 void
215 reshape_ball (ModeInfo *mi, int width, int height)
216 {
217   GLfloat h = (GLfloat) height / (GLfloat) width;
218
219   glViewport (0, 0, (GLint) width, (GLint) height);
220
221   glMatrixMode(GL_PROJECTION);
222   glLoadIdentity();
223
224   gluPerspective( 30.0, 1/h, 1.0, 100.0 );
225   gluLookAt( 0.0, 0.0, 15.0,
226              0.0, 0.0, 0.0,
227              0.0, 1.0, 0.0);
228   glMatrixMode(GL_MODELVIEW);
229   glLoadIdentity();
230   glTranslatef(0.0, 0.0, -15.0);
231
232   glClear(GL_COLOR_BUFFER_BIT);
233 }
234
235
236 static void
237 gl_init (ModeInfo *mi)
238 {
239 /*  ball_configuration *bp = &bps[MI_SCREEN(mi)]; */
240   int wire = MI_IS_WIREFRAME(mi);
241
242   static GLfloat pos[4] = {5.0, 5.0, 10.0, 1.0};
243
244   if (!wire)
245     {
246       glLightfv(GL_LIGHT0, GL_POSITION, pos);
247       glEnable(GL_CULL_FACE);
248       glEnable(GL_LIGHTING);
249       glEnable(GL_LIGHT0);
250       glEnable(GL_DEPTH_TEST);
251     }
252 }
253
254
255 /* lifted from lament.c */
256 #define RAND(n) ((long) ((random() & 0x7fffffff) % ((long) (n))))
257 #define RANDSIGN() ((random() & 1) ? 1 : -1)
258
259 static void
260 rotate(GLfloat *pos, GLfloat *v, GLfloat *dv, GLfloat max_v)
261 {
262   double ppos = *pos;
263
264   /* tick position */
265   if (ppos < 0)
266     ppos = -(ppos + *v);
267   else
268     ppos += *v;
269
270   if (ppos > 1.0)
271     ppos -= 1.0;
272   else if (ppos < 0)
273     ppos += 1.0;
274
275   if (ppos < 0) abort();
276   if (ppos > 1.0) abort();
277   *pos = (*pos > 0 ? ppos : -ppos);
278
279   /* accelerate */
280   *v += *dv;
281
282   /* clamp velocity */
283   if (*v > max_v || *v < -max_v)
284     {
285       *dv = -*dv;
286     }
287   /* If it stops, start it going in the other direction. */
288   else if (*v < 0)
289     {
290       if (random() % 4)
291         {
292           *v = 0;
293
294           /* keep going in the same direction */
295           if (random() % 2)
296             *dv = 0;
297           else if (*dv < 0)
298             *dv = -*dv;
299         }
300       else
301         {
302           /* reverse gears */
303           *v = -*v;
304           *dv = -*dv;
305           *pos = -*pos;
306         }
307     }
308
309   /* Alter direction of rotational acceleration randomly. */
310   if (! (random() % 120))
311     *dv = -*dv;
312
313   /* Change acceleration very occasionally. */
314   if (! (random() % 200))
315     {
316       if (*dv == 0)
317         *dv = 0.00001;
318       else if (random() & 1)
319         *dv *= 1.2;
320       else
321         *dv *= 0.8;
322     }
323 }
324
325
326 static void
327 randomize_spikes (ModeInfo *mi)
328 {
329   ball_configuration *bp = &bps[MI_SCREEN(mi)];
330   int i;
331   bp->pos = 0;
332   for (i = 0; i < MI_COUNT(mi); i++)
333     {
334       bp->spikes[i*2]   = (random() % 360) - 180;
335       bp->spikes[i*2+1] = (random() % 180) - 90;
336     }
337
338 # define ROT_SCALE 22
339   for (i = 0; i < MI_COUNT(mi) * 2; i++)
340     bp->spikes[i] = (bp->spikes[i] / ROT_SCALE) * ROT_SCALE;
341
342   if ((random() % 3) == 0)
343     bp->color_shift = random() % (bp->ncolors / 2);
344   else
345     bp->color_shift = 0;
346 }
347
348 static void
349 draw_spikes (ModeInfo *mi)
350 {
351   ball_configuration *bp = &bps[MI_SCREEN(mi)];
352   GLfloat diam = 0.2;
353   GLfloat pos = bp->pos;
354   int i;
355
356   if (pos < 0) pos = -pos;
357
358   pos = (asin (0.5 + pos/2) - 0.5) * 2;
359
360   for (i = 0; i < MI_COUNT(mi); i++)
361     {
362       glPushMatrix();
363       glRotatef(bp->spikes[i*2],   0, 1, 0);
364       glRotatef(bp->spikes[i*2+1], 0, 0, 1);
365       glTranslatef(0.7, 0, 0);
366       glRotatef(-90, 0, 0, 1);
367       glScalef (diam, pos, diam);
368       glCallList (bp->spike_list);
369       glPopMatrix();
370     }
371 }
372
373
374 static void
375 move_spikes (ModeInfo *mi)
376 {
377   ball_configuration *bp = &bps[MI_SCREEN(mi)];
378
379   if (bp->pos >= 0)             /* moving outward */
380     {
381       bp->pos += speed;
382       if (bp->pos >= 1)         /*  reverse gears at apex */
383         bp->pos = -1;
384     }
385   else                          /* moving inward */
386     {
387       bp->pos += speed;
388       if (bp->pos >= 0)         /*  stop at end */
389         randomize_spikes (mi);
390     }
391 }
392
393
394 void 
395 init_ball (ModeInfo *mi)
396 {
397   ball_configuration *bp;
398   int wire = MI_IS_WIREFRAME(mi);
399
400   if (!bps) {
401     bps = (ball_configuration *)
402       calloc (MI_NUM_SCREENS(mi), sizeof (ball_configuration));
403     if (!bps) {
404       fprintf(stderr, "%s: out of memory\n", progname);
405       exit(1);
406     }
407
408     bp = &bps[MI_SCREEN(mi)];
409   }
410
411   bp = &bps[MI_SCREEN(mi)];
412
413   if ((bp->glx_context = init_GL(mi)) != NULL) {
414     gl_init(mi);
415     reshape_ball (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
416   }
417
418   bp->rotx = frand(1.0) * RANDSIGN();
419   bp->roty = frand(1.0) * RANDSIGN();
420   bp->rotz = frand(1.0) * RANDSIGN();
421
422   /* bell curve from 0-6 degrees, avg 3 */
423   bp->dx = (frand(2) + frand(2) + frand(2)) / (360/2);
424   bp->dy = (frand(2) + frand(2) + frand(2)) / (360/2);
425   bp->dz = (frand(2) + frand(2) + frand(2)) / (360/2);
426
427   bp->d_max = bp->dx * 2;
428
429   bp->ddx = 0.00006 + frand(0.00003);
430   bp->ddy = 0.00006 + frand(0.00003);
431   bp->ddz = 0.00006 + frand(0.00003);
432
433   bp->ncolors = 128;
434   bp->colors = (XColor *) calloc(bp->ncolors, sizeof(XColor));
435   make_smooth_colormap (0, 0, 0,
436                         bp->colors, &bp->ncolors,
437                         False, 0, False);
438
439   bp->spikes = (int *) calloc(MI_COUNT(mi), sizeof(*bp->spikes) * 2);
440
441   bp->ball_list = glGenLists (1);
442   bp->spike_list = glGenLists (1);
443
444   glNewList (bp->ball_list, GL_COMPILE);
445   unit_sphere (wire);
446   glEndList ();
447
448   glNewList (bp->spike_list, GL_COMPILE);
449   unit_spike (wire);
450   glEndList ();
451
452   randomize_spikes (mi);
453 }
454
455
456 void
457 draw_ball (ModeInfo *mi)
458 {
459   ball_configuration *bp = &bps[MI_SCREEN(mi)];
460   Display *dpy = MI_DISPLAY(mi);
461   Window window = MI_WINDOW(mi);
462   int c2;
463
464   static GLfloat color1[4] = {0.0, 0.0, 0.0, 1.0};
465   static GLfloat color2[4] = {0.0, 0.0, 0.0, 1.0};
466
467   if (!bp->glx_context)
468     return;
469
470   glShadeModel(GL_SMOOTH);
471
472   glEnable(GL_DEPTH_TEST);
473   glEnable(GL_NORMALIZE);
474   glEnable(GL_CULL_FACE);
475
476   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
477
478   glPushMatrix ();
479
480   glScalef(1.1, 1.1, 1.1);
481
482   {
483     GLfloat x, y, z;
484
485     if (do_wander)
486       {
487         static int frame = 0;
488
489 #       define SINOID(SCALE,SIZE) \
490         ((((1 + sin((frame * (SCALE)) / 2 * M_PI)) / 2.0) * (SIZE)) - (SIZE)/2)
491
492         x = SINOID(0.051, 8.0);
493         y = SINOID(0.037, 8.0);
494         z = SINOID(0.131, 13.0);
495         frame++;
496         glTranslatef(x, y, z);
497       }
498
499     if (do_spin)
500       {
501         x = bp->rotx;
502         y = bp->roty;
503         z = bp->rotz;
504         if (x < 0) x = 1 - (x + 1);
505         if (y < 0) y = 1 - (y + 1);
506         if (z < 0) z = 1 - (z + 1);
507
508         glRotatef(x * 360, 1.0, 0.0, 0.0);
509         glRotatef(y * 360, 0.0, 1.0, 0.0);
510         glRotatef(z * 360, 0.0, 0.0, 1.0);
511
512         rotate(&bp->rotx, &bp->dx, &bp->ddx, bp->d_max);
513         rotate(&bp->roty, &bp->dy, &bp->ddy, bp->d_max);
514         rotate(&bp->rotz, &bp->dz, &bp->ddz, bp->d_max);
515       }
516   }
517
518   color1[0] = bp->colors[bp->ccolor].red   / 65536.0;
519   color1[1] = bp->colors[bp->ccolor].green / 65536.0;
520   color1[2] = bp->colors[bp->ccolor].blue  / 65536.0;
521
522   c2 = (bp->ccolor + bp->color_shift) % bp->ncolors;
523   color2[0] = bp->colors[c2].red   / 65536.0;
524   color2[1] = bp->colors[c2].green / 65536.0;
525   color2[2] = bp->colors[c2].blue  / 65536.0;
526
527   bp->ccolor++;
528   if (bp->ccolor >= bp->ncolors) bp->ccolor = 0;
529
530   glScalef (2.0, 2.0, 2.0);
531
532   move_spikes (mi);
533
534   glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color1);
535   glCallList (bp->ball_list);
536
537   glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color2);
538   draw_spikes (mi);
539   glPopMatrix ();
540
541   if (mi->fps_p) do_fps (mi);
542   glFinish();
543
544   glXSwapBuffers(dpy, window);
545 }
546
547 #endif /* USE_GL */