From http://www.jwz.org/xscreensaver/xscreensaver-5.30.tar.gz
[xscreensaver] / hacks / glx / dangerball.c
1 /* dangerball, Copyright (c) 2001-2014 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 #define DEFAULTS        "*delay:        30000       \n" \
13                         "*count:        30          \n" \
14                         "*showFPS:      False       \n" \
15                         "*wireframe:    False       \n" \
16
17 # define refresh_ball 0
18 # define release_ball 0
19 #undef countof
20 #define countof(x) (sizeof((x))/sizeof((*x)))
21
22 #include "xlockmore.h"
23 #include "colors.h"
24 #include "sphere.h"
25 #include "tube.h"
26 #include "rotator.h"
27 #include "gltrackball.h"
28 #include <ctype.h>
29
30 #ifdef USE_GL /* whole file */
31
32
33 #define DEF_SPIN        "True"
34 #define DEF_WANDER      "True"
35 #define DEF_SPEED       "0.05"
36
37 #define SPIKE_FACES   12  /* how densely to render spikes */
38 #define SMOOTH_SPIKES True
39 #define SPHERE_SLICES 32  /* how densely to render spheres */
40 #define SPHERE_STACKS 16
41
42
43 typedef struct {
44   GLXContext *glx_context;
45   rotator *rot;
46   trackball_state *trackball;
47   Bool button_down_p;
48
49   GLuint ball_list;
50   GLuint spike_list;
51
52   GLfloat pos;
53   int *spikes;
54
55   int ncolors;
56   XColor *colors;
57   int ccolor;
58   int color_shift;
59
60 } ball_configuration;
61
62 static ball_configuration *bps = NULL;
63
64 static Bool do_spin;
65 static GLfloat speed;
66 static Bool do_wander;
67
68 static XrmOptionDescRec opts[] = {
69   { "-spin",   ".spin",   XrmoptionNoArg, "True" },
70   { "+spin",   ".spin",   XrmoptionNoArg, "False" },
71   { "-speed",  ".speed",  XrmoptionSepArg, 0 },
72   { "-wander", ".wander", XrmoptionNoArg, "True" },
73   { "+wander", ".wander", XrmoptionNoArg, "False" }
74 };
75
76 static argtype vars[] = {
77   {&do_spin,   "spin",   "Spin",   DEF_SPIN,   t_Bool},
78   {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
79   {&speed,     "speed",  "Speed",  DEF_SPEED,  t_Float},
80 };
81
82 ENTRYPOINT ModeSpecOpt ball_opts = {countof(opts), opts, countof(vars), vars, NULL};
83
84
85 /* Window management, etc
86  */
87 ENTRYPOINT void
88 reshape_ball (ModeInfo *mi, int width, int height)
89 {
90   GLfloat h = (GLfloat) height / (GLfloat) width;
91
92   glViewport (0, 0, (GLint) width, (GLint) height);
93
94   glMatrixMode(GL_PROJECTION);
95   glLoadIdentity();
96   gluPerspective (30.0, 1/h, 1.0, 100.0);
97
98   glMatrixMode(GL_MODELVIEW);
99   glLoadIdentity();
100   gluLookAt( 0.0, 0.0, 30.0,
101              0.0, 0.0, 0.0,
102              0.0, 1.0, 0.0);
103
104   glClear(GL_COLOR_BUFFER_BIT);
105 }
106
107
108 static void
109 randomize_spikes (ModeInfo *mi)
110 {
111   ball_configuration *bp = &bps[MI_SCREEN(mi)];
112   int i;
113   bp->pos = 0;
114   for (i = 0; i < MI_COUNT(mi); i++)
115     {
116       bp->spikes[i*2]   = (random() % 360) - 180;
117       bp->spikes[i*2+1] = (random() % 180) - 90;
118     }
119
120 # define ROT_SCALE 22
121   for (i = 0; i < MI_COUNT(mi) * 2; i++)
122     bp->spikes[i] = (bp->spikes[i] / ROT_SCALE) * ROT_SCALE;
123
124   if ((random() % 3) == 0)
125     bp->color_shift = random() % (bp->ncolors / 2);
126   else
127     bp->color_shift = 0;
128 }
129
130 static void
131 draw_spikes (ModeInfo *mi)
132 {
133   ball_configuration *bp = &bps[MI_SCREEN(mi)];
134   GLfloat diam = 0.2;
135   GLfloat pos = bp->pos;
136   int i;
137
138   if (pos < 0) pos = -pos;
139
140   pos = (asin (0.5 + pos/2) - 0.5) * 2;
141
142   for (i = 0; i < MI_COUNT(mi); i++)
143     {
144       glPushMatrix();
145       glRotatef(bp->spikes[i*2],   0, 1, 0);
146       glRotatef(bp->spikes[i*2+1], 0, 0, 1);
147       glTranslatef(0.7, 0, 0);
148       glRotatef(-90, 0, 0, 1);
149       glScalef (diam, pos, diam);
150       glCallList (bp->spike_list);
151       glPopMatrix();
152
153       mi->polygon_count += (SPIKE_FACES + 1);
154     }
155 }
156
157
158 static void
159 move_spikes (ModeInfo *mi)
160 {
161   ball_configuration *bp = &bps[MI_SCREEN(mi)];
162
163   if (bp->pos >= 0)             /* moving outward */
164     {
165       bp->pos += speed;
166       if (bp->pos >= 1)         /*  reverse gears at apex */
167         bp->pos = -1;
168     }
169   else                          /* moving inward */
170     {
171       bp->pos += speed;
172       if (bp->pos >= 0)         /*  stop at end */
173         randomize_spikes (mi);
174     }
175 }
176
177
178 ENTRYPOINT Bool
179 ball_handle_event (ModeInfo *mi, XEvent *event)
180 {
181   ball_configuration *bp = &bps[MI_SCREEN(mi)];
182
183   if (gltrackball_event_handler (event, bp->trackball,
184                                  MI_WIDTH (mi), MI_HEIGHT (mi),
185                                  &bp->button_down_p))
186     return True;
187
188   return False;
189 }
190
191
192 ENTRYPOINT void 
193 init_ball (ModeInfo *mi)
194 {
195   ball_configuration *bp;
196   int wire = MI_IS_WIREFRAME(mi);
197
198   if (!bps) {
199     bps = (ball_configuration *)
200       calloc (MI_NUM_SCREENS(mi), sizeof (ball_configuration));
201     if (!bps) {
202       fprintf(stderr, "%s: out of memory\n", progname);
203       exit(1);
204     }
205   }
206
207   bp = &bps[MI_SCREEN(mi)];
208
209   bp->glx_context = init_GL(mi);
210
211   reshape_ball (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
212
213   if (!wire)
214     {
215       GLfloat pos[4] = {1.0, 1.0, 1.0, 0.0};
216       GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0};
217       GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
218       GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
219
220       glEnable(GL_LIGHTING);
221       glEnable(GL_LIGHT0);
222       glEnable(GL_DEPTH_TEST);
223       glEnable(GL_CULL_FACE);
224
225       glLightfv(GL_LIGHT0, GL_POSITION, pos);
226       glLightfv(GL_LIGHT0, GL_AMBIENT,  amb);
227       glLightfv(GL_LIGHT0, GL_DIFFUSE,  dif);
228       glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
229     }
230
231   {
232     double spin_speed   = 10.0;
233     double wander_speed = 0.12;
234     double spin_accel   = 2.0;
235
236     bp->rot = make_rotator (do_spin ? spin_speed : 0,
237                             do_spin ? spin_speed : 0,
238                             do_spin ? spin_speed : 0,
239                             spin_accel,
240                             do_wander ? wander_speed : 0,
241                             True);
242     bp->trackball = gltrackball_init (True);
243   }
244
245   bp->ncolors = 128;
246   bp->colors = (XColor *) calloc(bp->ncolors, sizeof(XColor));
247   make_smooth_colormap (0, 0, 0,
248                         bp->colors, &bp->ncolors,
249                         False, 0, False);
250
251   bp->spikes = (int *) calloc(MI_COUNT(mi), sizeof(*bp->spikes) * 2);
252
253   bp->ball_list = glGenLists (1);
254   bp->spike_list = glGenLists (1);
255
256   glNewList (bp->ball_list, GL_COMPILE);
257   unit_sphere (SPHERE_STACKS, SPHERE_SLICES, wire);
258   glEndList ();
259
260   glNewList (bp->spike_list, GL_COMPILE);
261   cone (0, 0, 0,
262         0, 1, 0,
263         1, 0, SPIKE_FACES, SMOOTH_SPIKES, False, wire);
264   glEndList ();
265
266   randomize_spikes (mi);
267 }
268
269
270 ENTRYPOINT void
271 draw_ball (ModeInfo *mi)
272 {
273   ball_configuration *bp = &bps[MI_SCREEN(mi)];
274   Display *dpy = MI_DISPLAY(mi);
275   Window window = MI_WINDOW(mi);
276   int c2;
277
278   static const GLfloat bspec[4]  = {1.0, 1.0, 1.0, 1.0};
279   static const GLfloat sspec[4]  = {0.0, 0.0, 0.0, 1.0};
280   static const GLfloat bshiny    = 128.0;
281   static const GLfloat sshiny    = 0.0;
282
283   GLfloat bcolor[4] = {0.0, 0.0, 0.0, 1.0};
284   GLfloat scolor[4] = {0.0, 0.0, 0.0, 1.0};
285
286   if (!bp->glx_context)
287     return;
288
289   glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
290
291   glShadeModel(GL_SMOOTH);
292
293   glEnable(GL_DEPTH_TEST);
294   glEnable(GL_NORMALIZE);
295   glEnable(GL_CULL_FACE);
296
297   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
298
299   glPushMatrix ();
300
301   glScalef(1.1, 1.1, 1.1);
302
303   {
304     double x, y, z;
305     get_position (bp->rot, &x, &y, &z, !bp->button_down_p);
306     glTranslatef((x - 0.5) * 8,
307                  (y - 0.5) * 8,
308                  (z - 0.5) * 15);
309
310     gltrackball_rotate (bp->trackball);
311
312     get_rotation (bp->rot, &x, &y, &z, !bp->button_down_p);
313     glRotatef (x * 360, 1.0, 0.0, 0.0);
314     glRotatef (y * 360, 0.0, 1.0, 0.0);
315     glRotatef (z * 360, 0.0, 0.0, 1.0);
316   }
317
318   bcolor[0] = bp->colors[bp->ccolor].red   / 65536.0;
319   bcolor[1] = bp->colors[bp->ccolor].green / 65536.0;
320   bcolor[2] = bp->colors[bp->ccolor].blue  / 65536.0;
321
322   c2 = (bp->ccolor + bp->color_shift) % bp->ncolors;
323   scolor[0] = bp->colors[c2].red   / 65536.0;
324   scolor[1] = bp->colors[c2].green / 65536.0;
325   scolor[2] = bp->colors[c2].blue  / 65536.0;
326
327   bp->ccolor++;
328   if (bp->ccolor >= bp->ncolors) bp->ccolor = 0;
329
330   mi->polygon_count = 0;
331
332   glScalef (2.0, 2.0, 2.0);
333
334   move_spikes (mi);
335
336   glMaterialfv (GL_FRONT, GL_SPECULAR,            bspec);
337   glMateriali  (GL_FRONT, GL_SHININESS,           bshiny);
338   glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, bcolor);
339   glCallList (bp->ball_list);
340   mi->polygon_count += (SPHERE_SLICES * SPHERE_STACKS);
341
342   glMaterialfv (GL_FRONT, GL_SPECULAR,            sspec);
343   glMaterialf  (GL_FRONT, GL_SHININESS,           sshiny);
344   glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, scolor);
345   draw_spikes (mi);
346   glPopMatrix ();
347
348   if (mi->fps_p) do_fps (mi);
349   glFinish();
350
351   glXSwapBuffers(dpy, window);
352 }
353
354 XSCREENSAVER_MODULE_2 ("DangerBall", dangerball, ball)
355
356 #endif /* USE_GL */