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