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