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