d189602aaa7d8ffe3bda68a875e978f009f8f930
[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 SPIKE_FACES   12  /* how densely to render spikes */
36 #define SMOOTH_SPIKES True
37 #define SPHERE_SLICES 32  /* how densely to render spheres */
38 #define SPHERE_STACKS 16
39
40
41 #undef countof
42 #define countof(x) (sizeof((x))/sizeof((*x)))
43
44 #include "xlockmore.h"
45 #include "colors.h"
46 #include "sphere.h"
47 #include "tube.h"
48 #include <ctype.h>
49
50 #ifdef USE_GL /* whole file */
51
52 #include <GL/glu.h>
53
54 typedef struct {
55   GLXContext *glx_context;
56
57   GLfloat rotx, roty, rotz;        /* current object rotation */
58   GLfloat dx, dy, dz;              /* current rotational velocity */
59   GLfloat ddx, ddy, ddz;           /* current rotational acceleration */
60   GLfloat d_max;                   /* max velocity */
61
62   GLuint ball_list;
63   GLuint spike_list;
64
65   GLfloat pos;
66   int *spikes;
67
68   int ncolors;
69   XColor *colors;
70   int ccolor;
71   int color_shift;
72
73 } ball_configuration;
74
75 static ball_configuration *bps = NULL;
76
77 static char *do_spin;
78 static GLfloat speed;
79 static Bool do_wander;
80
81 static XrmOptionDescRec opts[] = {
82   { "-spin",   ".spin",   XrmoptionNoArg, "True" },
83   { "+spin",   ".spin",   XrmoptionNoArg, "False" },
84   { "-speed",  ".speed",  XrmoptionSepArg, 0 },
85   { "-wander", ".wander", XrmoptionNoArg, "True" },
86   { "+wander", ".wander", XrmoptionNoArg, "False" }
87 };
88
89 static argtype vars[] = {
90   {(caddr_t *) &do_spin,   "spin",   "Spin",   DEF_SPIN,   t_Bool},
91   {(caddr_t *) &do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
92   {(caddr_t *) &speed,     "speed",  "Speed",  DEF_SPEED,  t_Float},
93 };
94
95 ModeSpecOpt sws_opts = {countof(opts), opts, countof(vars), vars, NULL};
96
97
98 /* Window management, etc
99  */
100 void
101 reshape_ball (ModeInfo *mi, int width, int height)
102 {
103   GLfloat h = (GLfloat) height / (GLfloat) width;
104
105   glViewport (0, 0, (GLint) width, (GLint) height);
106
107   glMatrixMode(GL_PROJECTION);
108   glLoadIdentity();
109
110   gluPerspective( 30.0, 1/h, 1.0, 100.0 );
111   gluLookAt( 0.0, 0.0, 15.0,
112              0.0, 0.0, 0.0,
113              0.0, 1.0, 0.0);
114   glMatrixMode(GL_MODELVIEW);
115   glLoadIdentity();
116   glTranslatef(0.0, 0.0, -15.0);
117
118   glClear(GL_COLOR_BUFFER_BIT);
119 }
120
121
122 static void
123 gl_init (ModeInfo *mi)
124 {
125 /*  ball_configuration *bp = &bps[MI_SCREEN(mi)]; */
126   int wire = MI_IS_WIREFRAME(mi);
127
128   static GLfloat pos[4] = {5.0, 5.0, 10.0, 1.0};
129
130   if (!wire)
131     {
132       glLightfv(GL_LIGHT0, GL_POSITION, pos);
133       glEnable(GL_CULL_FACE);
134       glEnable(GL_LIGHTING);
135       glEnable(GL_LIGHT0);
136       glEnable(GL_DEPTH_TEST);
137     }
138 }
139
140
141 /* lifted from lament.c */
142 #define RAND(n) ((long) ((random() & 0x7fffffff) % ((long) (n))))
143 #define RANDSIGN() ((random() & 1) ? 1 : -1)
144
145 static void
146 rotate(GLfloat *pos, GLfloat *v, GLfloat *dv, GLfloat max_v)
147 {
148   double ppos = *pos;
149
150   /* tick position */
151   if (ppos < 0)
152     ppos = -(ppos + *v);
153   else
154     ppos += *v;
155
156   if (ppos > 1.0)
157     ppos -= 1.0;
158   else if (ppos < 0)
159     ppos += 1.0;
160
161   if (ppos < 0) abort();
162   if (ppos > 1.0) abort();
163   *pos = (*pos > 0 ? ppos : -ppos);
164
165   /* accelerate */
166   *v += *dv;
167
168   /* clamp velocity */
169   if (*v > max_v || *v < -max_v)
170     {
171       *dv = -*dv;
172     }
173   /* If it stops, start it going in the other direction. */
174   else if (*v < 0)
175     {
176       if (random() % 4)
177         {
178           *v = 0;
179
180           /* keep going in the same direction */
181           if (random() % 2)
182             *dv = 0;
183           else if (*dv < 0)
184             *dv = -*dv;
185         }
186       else
187         {
188           /* reverse gears */
189           *v = -*v;
190           *dv = -*dv;
191           *pos = -*pos;
192         }
193     }
194
195   /* Alter direction of rotational acceleration randomly. */
196   if (! (random() % 120))
197     *dv = -*dv;
198
199   /* Change acceleration very occasionally. */
200   if (! (random() % 200))
201     {
202       if (*dv == 0)
203         *dv = 0.00001;
204       else if (random() & 1)
205         *dv *= 1.2;
206       else
207         *dv *= 0.8;
208     }
209 }
210
211
212 static void
213 randomize_spikes (ModeInfo *mi)
214 {
215   ball_configuration *bp = &bps[MI_SCREEN(mi)];
216   int i;
217   bp->pos = 0;
218   for (i = 0; i < MI_COUNT(mi); i++)
219     {
220       bp->spikes[i*2]   = (random() % 360) - 180;
221       bp->spikes[i*2+1] = (random() % 180) - 90;
222     }
223
224 # define ROT_SCALE 22
225   for (i = 0; i < MI_COUNT(mi) * 2; i++)
226     bp->spikes[i] = (bp->spikes[i] / ROT_SCALE) * ROT_SCALE;
227
228   if ((random() % 3) == 0)
229     bp->color_shift = random() % (bp->ncolors / 2);
230   else
231     bp->color_shift = 0;
232 }
233
234 static void
235 draw_spikes (ModeInfo *mi)
236 {
237   ball_configuration *bp = &bps[MI_SCREEN(mi)];
238   GLfloat diam = 0.2;
239   GLfloat pos = bp->pos;
240   int i;
241
242   if (pos < 0) pos = -pos;
243
244   pos = (asin (0.5 + pos/2) - 0.5) * 2;
245
246   for (i = 0; i < MI_COUNT(mi); i++)
247     {
248       glPushMatrix();
249       glRotatef(bp->spikes[i*2],   0, 1, 0);
250       glRotatef(bp->spikes[i*2+1], 0, 0, 1);
251       glTranslatef(0.7, 0, 0);
252       glRotatef(-90, 0, 0, 1);
253       glScalef (diam, pos, diam);
254       glCallList (bp->spike_list);
255       glPopMatrix();
256     }
257 }
258
259
260 static void
261 move_spikes (ModeInfo *mi)
262 {
263   ball_configuration *bp = &bps[MI_SCREEN(mi)];
264
265   if (bp->pos >= 0)             /* moving outward */
266     {
267       bp->pos += speed;
268       if (bp->pos >= 1)         /*  reverse gears at apex */
269         bp->pos = -1;
270     }
271   else                          /* moving inward */
272     {
273       bp->pos += speed;
274       if (bp->pos >= 0)         /*  stop at end */
275         randomize_spikes (mi);
276     }
277 }
278
279
280 void 
281 init_ball (ModeInfo *mi)
282 {
283   ball_configuration *bp;
284   int wire = MI_IS_WIREFRAME(mi);
285
286   if (!bps) {
287     bps = (ball_configuration *)
288       calloc (MI_NUM_SCREENS(mi), sizeof (ball_configuration));
289     if (!bps) {
290       fprintf(stderr, "%s: out of memory\n", progname);
291       exit(1);
292     }
293
294     bp = &bps[MI_SCREEN(mi)];
295   }
296
297   bp = &bps[MI_SCREEN(mi)];
298
299   if ((bp->glx_context = init_GL(mi)) != NULL) {
300     gl_init(mi);
301     reshape_ball (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
302   }
303
304   bp->rotx = frand(1.0) * RANDSIGN();
305   bp->roty = frand(1.0) * RANDSIGN();
306   bp->rotz = frand(1.0) * RANDSIGN();
307
308   /* bell curve from 0-6 degrees, avg 3 */
309   bp->dx = (frand(2) + frand(2) + frand(2)) / (360/2);
310   bp->dy = (frand(2) + frand(2) + frand(2)) / (360/2);
311   bp->dz = (frand(2) + frand(2) + frand(2)) / (360/2);
312
313   bp->d_max = bp->dx * 2;
314
315   bp->ddx = 0.00006 + frand(0.00003);
316   bp->ddy = 0.00006 + frand(0.00003);
317   bp->ddz = 0.00006 + frand(0.00003);
318
319   bp->ncolors = 128;
320   bp->colors = (XColor *) calloc(bp->ncolors, sizeof(XColor));
321   make_smooth_colormap (0, 0, 0,
322                         bp->colors, &bp->ncolors,
323                         False, 0, False);
324
325   bp->spikes = (int *) calloc(MI_COUNT(mi), sizeof(*bp->spikes) * 2);
326
327   bp->ball_list = glGenLists (1);
328   bp->spike_list = glGenLists (1);
329
330   glNewList (bp->ball_list, GL_COMPILE);
331   unit_sphere (SPHERE_STACKS, SPHERE_SLICES, wire);
332   glEndList ();
333
334   glNewList (bp->spike_list, GL_COMPILE);
335   cone (0, 0, 0,
336         0, 1, 0,
337         1, 0, SPIKE_FACES, SMOOTH_SPIKES, wire);
338   glEndList ();
339
340   randomize_spikes (mi);
341 }
342
343
344 void
345 draw_ball (ModeInfo *mi)
346 {
347   ball_configuration *bp = &bps[MI_SCREEN(mi)];
348   Display *dpy = MI_DISPLAY(mi);
349   Window window = MI_WINDOW(mi);
350   int c2;
351
352   static GLfloat color1[4] = {0.0, 0.0, 0.0, 1.0};
353   static GLfloat color2[4] = {0.0, 0.0, 0.0, 1.0};
354
355   if (!bp->glx_context)
356     return;
357
358   glShadeModel(GL_SMOOTH);
359
360   glEnable(GL_DEPTH_TEST);
361   glEnable(GL_NORMALIZE);
362   glEnable(GL_CULL_FACE);
363
364   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
365
366   glPushMatrix ();
367
368   glScalef(1.1, 1.1, 1.1);
369
370   {
371     GLfloat x, y, z;
372
373     if (do_wander)
374       {
375         static int frame = 0;
376
377 #       define SINOID(SCALE,SIZE) \
378         ((((1 + sin((frame * (SCALE)) / 2 * M_PI)) / 2.0) * (SIZE)) - (SIZE)/2)
379
380         x = SINOID(0.051, 8.0);
381         y = SINOID(0.037, 8.0);
382         z = SINOID(0.131, 13.0);
383         frame++;
384         glTranslatef(x, y, z);
385       }
386
387     if (do_spin)
388       {
389         x = bp->rotx;
390         y = bp->roty;
391         z = bp->rotz;
392         if (x < 0) x = 1 - (x + 1);
393         if (y < 0) y = 1 - (y + 1);
394         if (z < 0) z = 1 - (z + 1);
395
396         glRotatef(x * 360, 1.0, 0.0, 0.0);
397         glRotatef(y * 360, 0.0, 1.0, 0.0);
398         glRotatef(z * 360, 0.0, 0.0, 1.0);
399
400         rotate(&bp->rotx, &bp->dx, &bp->ddx, bp->d_max);
401         rotate(&bp->roty, &bp->dy, &bp->ddy, bp->d_max);
402         rotate(&bp->rotz, &bp->dz, &bp->ddz, bp->d_max);
403       }
404   }
405
406   color1[0] = bp->colors[bp->ccolor].red   / 65536.0;
407   color1[1] = bp->colors[bp->ccolor].green / 65536.0;
408   color1[2] = bp->colors[bp->ccolor].blue  / 65536.0;
409
410   c2 = (bp->ccolor + bp->color_shift) % bp->ncolors;
411   color2[0] = bp->colors[c2].red   / 65536.0;
412   color2[1] = bp->colors[c2].green / 65536.0;
413   color2[2] = bp->colors[c2].blue  / 65536.0;
414
415   bp->ccolor++;
416   if (bp->ccolor >= bp->ncolors) bp->ccolor = 0;
417
418   glScalef (2.0, 2.0, 2.0);
419
420   move_spikes (mi);
421
422   glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color1);
423   glCallList (bp->ball_list);
424
425   glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color2);
426   draw_spikes (mi);
427   glPopMatrix ();
428
429   if (mi->fps_p) do_fps (mi);
430   glFinish();
431
432   glXSwapBuffers(dpy, window);
433 }
434
435 #endif /* USE_GL */