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