http://www.tienza.es/crux/src/www.jwz.org/xscreensaver/xscreensaver-5.05.tar.gz
[xscreensaver] / hacks / glx / glknots.c
1 /* glknots, Copyright (c) 2003-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  * Generates some 3D knots (closed loops).
12  * Inspired by Paul Bourke <pbourke@swin.edu.au> at
13  * http://astronomy.swin.edu.au/~pbourke/curves/knot/
14  */
15
16 #define DEFAULTS        "*delay:        30000       \n" \
17                         "*showFPS:      False       \n" \
18                         "*wireframe:    False       \n" \
19
20 # define refresh_knot 0
21 # define release_knot 0
22 #undef countof
23 #define countof(x) (sizeof((x))/sizeof((*x)))
24
25 #include "xlockmore.h"
26 #include "colors.h"
27 #include "tube.h"
28 #include "rotator.h"
29 #include "gltrackball.h"
30 #include <ctype.h>
31
32 #ifdef USE_GL /* whole file */
33
34 #define DEF_SPIN        "XYZ"
35 #define DEF_WANDER      "True"
36 #define DEF_SPEED       "1.0"
37 #define DEF_THICKNESS   "0.3"
38 #define DEF_SEGMENTS    "800"
39 #define DEF_DURATION    "8"
40
41 typedef struct {
42   GLXContext *glx_context;
43   rotator *rot;
44   trackball_state *trackball;
45   Bool button_down_p;
46
47   GLuint knot_list;
48
49   int ncolors;
50   XColor *colors;
51   int ccolor;
52
53   int mode;  /* 0 = normal, 1 = out, 2 = in */
54   int mode_tick;
55   Bool clear_p;
56
57   time_t last_time;
58   int draw_tick;
59
60 } knot_configuration;
61
62 static knot_configuration *bps = NULL;
63
64 static char *do_spin;
65 static GLfloat speed;
66 static Bool do_wander;
67 static GLfloat thickness;
68 static unsigned int segments;
69 static int duration;
70
71 static XrmOptionDescRec opts[] = {
72   { "-spin",   ".spin",   XrmoptionSepArg, 0 },
73   { "+spin",   ".spin",   XrmoptionNoArg, "" },
74   { "-wander", ".wander", XrmoptionNoArg, "True" },
75   { "+wander", ".wander", XrmoptionNoArg, "False" },
76   { "-speed",  ".speed",  XrmoptionSepArg, 0 },
77   { "-thickness", ".thickness",  XrmoptionSepArg, 0 },
78   { "-segments",  ".segments",   XrmoptionSepArg, 0 },
79   { "-duration",  ".duration",   XrmoptionSepArg, 0 },
80 };
81
82 static argtype vars[] = {
83   {&do_spin,   "spin",   "Spin",   DEF_SPIN,   t_String},
84   {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
85   {&speed,     "speed",  "Speed",  DEF_SPEED,  t_Float},
86   {&thickness, "thickness", "Thickness",  DEF_THICKNESS, t_Float},
87   {&segments,  "segments",  "Segments",   DEF_SEGMENTS,  t_Int},
88   {&duration,  "duration",  "Duration",   DEF_DURATION,  t_Int},
89 };
90
91 ENTRYPOINT ModeSpecOpt knot_opts = {countof(opts), opts, countof(vars), vars, NULL};
92
93
94 static void
95 make_knot (ModeInfo *mi)
96 {
97   int wire = MI_IS_WIREFRAME(mi);
98
99   GLfloat diam = (4 * thickness);
100   int faces = (wire ? 3 : 6);
101
102   unsigned int i;
103   double x, y, z, ox=0, oy=0, oz=0;
104   double mu;
105
106   double p[9];
107
108   Bool blobby_p = (0 == (random() % 5));
109   Bool type = (random() % 2);
110
111   for (i = 0; i < countof(p); i++)
112     {
113       p[i] = 1 + (random() % 4);
114       if (! (random() % 3))
115         p[i] += (random() % 5);
116     }
117
118   if (type == 1)
119     {
120       p[0] += 4;
121       p[1] *= ((p[0] + p[0]) / 10);
122       blobby_p = False;
123     }
124
125   mi->polygon_count = 0;
126
127   for (i = 0; i <= segments; i++)
128     {
129       if (type == 0)
130         {
131           mu = i * (M_PI * 2) / segments;
132           x = 10 * (cos(mu) + cos(p[0]*mu)) + cos(p[1]*mu) + cos(p[2]*mu);
133           y = 6 * sin(mu) + 10 * sin(p[3]*mu);
134           z = 16 * sin(p[4]*mu) * sin(p[5]*mu/2) + p[6]*sin(p[7]*mu) -
135             2 * sin(p[8]*mu);
136         }
137       else if (type == 1)
138         {
139           mu = i * (M_PI * 2) * p[0] / (double) segments;
140           x = 10 * cos(mu) * (1 + cos(p[1] * mu/ p[0]) / 2.0);
141           y = 25 * sin(p[1] * mu / p[0]) / 2.0;
142           z = 10 * sin(mu) * (1 + cos(p[1] * mu/ p[0]) / 2.0);
143         }
144       else
145         abort();
146
147       if (i != 0)
148         {
149           GLfloat dist = sqrt ((x-ox)*(x-ox) +
150                                (y-oy)*(y-oy) +
151                                (z-oz)*(z-oz));
152           GLfloat di;
153           if (!blobby_p)
154             di = diam;
155           else
156             {
157               di = dist * (segments / 500.0);
158               di = (di * di * 3);
159             }
160
161           mi->polygon_count += tube (ox, oy, oz,
162                                      x, y, z,
163                                      di, dist/3,
164                                      faces, True, wire, wire);
165         }
166
167       ox = x;
168       oy = y;
169       oz = z;
170    }
171 }
172
173
174 /* Window management, etc
175  */
176 ENTRYPOINT void
177 reshape_knot (ModeInfo *mi, int width, int height)
178 {
179   GLfloat h = (GLfloat) height / (GLfloat) width;
180
181   glViewport (0, 0, (GLint) width, (GLint) height);
182
183   glMatrixMode(GL_PROJECTION);
184   glLoadIdentity();
185   gluPerspective (30.0, 1/h, 1.0, 100.0);
186
187   glMatrixMode(GL_MODELVIEW);
188   glLoadIdentity();
189   gluLookAt( 0.0, 0.0, 30.0,
190              0.0, 0.0, 0.0,
191              0.0, 1.0, 0.0);
192
193   glClear(GL_COLOR_BUFFER_BIT);
194 }
195
196
197 static void
198 new_knot (ModeInfo *mi)
199 {
200   knot_configuration *bp = &bps[MI_SCREEN(mi)];
201   int i;
202
203   bp->clear_p = !!(random() % 15);
204
205   bp->ncolors = 128;
206   bp->colors = (XColor *) calloc(bp->ncolors, sizeof(XColor));
207   make_smooth_colormap (0, 0, 0,
208                         bp->colors, &bp->ncolors,
209                         False, 0, False);
210
211   for (i = 0; i < bp->ncolors; i++)
212     {
213       /* make colors twice as bright */
214       bp->colors[i].red   = (bp->colors[i].red   >> 2) + 0x7FFF;
215       bp->colors[i].green = (bp->colors[i].green >> 2) + 0x7FFF;
216       bp->colors[i].blue  = (bp->colors[i].blue  >> 2) + 0x7FFF;
217     }
218
219   glNewList (bp->knot_list, GL_COMPILE);
220   make_knot (mi);
221   glEndList ();
222 }
223
224
225 ENTRYPOINT Bool
226 knot_handle_event (ModeInfo *mi, XEvent *event)
227 {
228   knot_configuration *bp = &bps[MI_SCREEN(mi)];
229
230   if (event->xany.type == ButtonPress &&
231       event->xbutton.button == Button1)
232     {
233       bp->button_down_p = True;
234       gltrackball_start (bp->trackball,
235                          event->xbutton.x, event->xbutton.y,
236                          MI_WIDTH (mi), MI_HEIGHT (mi));
237       return True;
238     }
239   else if (event->xany.type == ButtonRelease &&
240            event->xbutton.button == Button1)
241     {
242       bp->button_down_p = False;
243       return True;
244     }
245   else if (event->xany.type == ButtonPress &&
246            (event->xbutton.button == Button4 ||
247             event->xbutton.button == Button5 ||
248             event->xbutton.button == Button6 ||
249             event->xbutton.button == Button7))
250     {
251       gltrackball_mousewheel (bp->trackball, event->xbutton.button, 5,
252                               !!event->xbutton.state);
253       return True;
254     }
255   else if (event->xany.type == MotionNotify &&
256            bp->button_down_p)
257     {
258       gltrackball_track (bp->trackball,
259                          event->xmotion.x, event->xmotion.y,
260                          MI_WIDTH (mi), MI_HEIGHT (mi));
261       return True;
262     }
263
264   return False;
265 }
266
267
268
269 ENTRYPOINT void 
270 init_knot (ModeInfo *mi)
271 {
272   knot_configuration *bp;
273   int wire = MI_IS_WIREFRAME(mi);
274
275   if (!bps) {
276     bps = (knot_configuration *)
277       calloc (MI_NUM_SCREENS(mi), sizeof (knot_configuration));
278     if (!bps) {
279       fprintf(stderr, "%s: out of memory\n", progname);
280       exit(1);
281     }
282
283     bp = &bps[MI_SCREEN(mi)];
284   }
285
286   bp = &bps[MI_SCREEN(mi)];
287
288   bp->glx_context = init_GL(mi);
289
290   if (thickness <= 0) thickness = 0.001;
291   else if (thickness > 1) thickness = 1;
292
293   if (segments < 10) segments = 10;
294
295   reshape_knot (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
296
297   if (!wire)
298     {
299       GLfloat pos[4] = {1.0, 1.0, 1.0, 0.0};
300       GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0};
301       GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
302       GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
303
304       glEnable(GL_LIGHTING);
305       glEnable(GL_LIGHT0);
306       glEnable(GL_DEPTH_TEST);
307       glEnable(GL_CULL_FACE);
308
309       glLightfv(GL_LIGHT0, GL_POSITION, pos);
310       glLightfv(GL_LIGHT0, GL_AMBIENT,  amb);
311       glLightfv(GL_LIGHT0, GL_DIFFUSE,  dif);
312       glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
313     }
314
315   {
316     Bool spinx=False, spiny=False, spinz=False;
317     double spin_speed   = 2.0;
318     double wander_speed = 0.05;
319     double spin_accel   = 0.2;
320
321     char *s = do_spin;
322     while (*s)
323       {
324         if      (*s == 'x' || *s == 'X') spinx = True;
325         else if (*s == 'y' || *s == 'Y') spiny = True;
326         else if (*s == 'z' || *s == 'Z') spinz = True;
327         else if (*s == '0') ;
328         else
329           {
330             fprintf (stderr,
331          "%s: spin must contain only the characters X, Y, or Z (not \"%s\")\n",
332                      progname, do_spin);
333             exit (1);
334           }
335         s++;
336       }
337
338     bp->rot = make_rotator (spinx ? spin_speed : 0,
339                             spiny ? spin_speed : 0,
340                             spinz ? spin_speed : 0,
341                             spin_accel,
342                             do_wander ? wander_speed : 0,
343                             (spinx && spiny && spinz));
344     bp->trackball = gltrackball_init ();
345   }
346
347   bp->knot_list = glGenLists (1);
348   new_knot(mi);
349
350   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
351 }
352
353
354 ENTRYPOINT void
355 draw_knot (ModeInfo *mi)
356 {
357   knot_configuration *bp = &bps[MI_SCREEN(mi)];
358   Display *dpy = MI_DISPLAY(mi);
359   Window window = MI_WINDOW(mi);
360
361   GLfloat bcolor[4] = {0.0, 0.0, 0.0, 1.0};
362   GLfloat bspec[4]  = {1.0, 1.0, 1.0, 1.0};
363   GLfloat bshiny    = 128.0;
364
365   if (!bp->glx_context)
366     return;
367
368   glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
369
370   if (bp->mode == 0)
371     {
372       if (bp->draw_tick++ > 10)
373         {
374           time_t now = time((time_t *) 0);
375           if (bp->last_time == 0) bp->last_time = now;
376           bp->draw_tick = 0;
377           if (!bp->button_down_p &&
378               now - bp->last_time >= duration)
379             {
380               bp->mode = 1;    /* go out */
381               bp->mode_tick = 10 * speed;
382               bp->last_time = now;
383             }
384         }
385     }
386   else if (bp->mode == 1)   /* out */
387     {
388       if (--bp->mode_tick <= 0)
389         {
390           new_knot (mi);
391           bp->mode_tick = 10 * speed;
392           bp->mode = 2;  /* go in */
393         }
394     }
395   else if (bp->mode == 2)   /* in */
396     {
397       if (--bp->mode_tick <= 0)
398         bp->mode = 0;  /* normal */
399     }
400   else
401     abort();
402
403   glShadeModel(GL_SMOOTH);
404
405   glEnable(GL_DEPTH_TEST);
406   glEnable(GL_NORMALIZE);
407   glEnable(GL_CULL_FACE);
408
409   if (bp->clear_p)
410     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
411
412   glPushMatrix ();
413
414   {
415     double x, y, z;
416     get_position (bp->rot, &x, &y, &z, !bp->button_down_p);
417     glTranslatef((x - 0.5) * 8,
418                  (y - 0.5) * 8,
419                  (z - 0.5) * 15);
420
421     gltrackball_rotate (bp->trackball);
422
423     get_rotation (bp->rot, &x, &y, &z, !bp->button_down_p);
424
425     glRotatef (x * 360, 1.0, 0.0, 0.0);
426     glRotatef (y * 360, 0.0, 1.0, 0.0);
427     glRotatef (z * 360, 0.0, 0.0, 1.0);
428   }
429
430   bcolor[0] = bp->colors[bp->ccolor].red   / 65536.0;
431   bcolor[1] = bp->colors[bp->ccolor].green / 65536.0;
432   bcolor[2] = bp->colors[bp->ccolor].blue  / 65536.0;
433   bp->ccolor++;
434   if (bp->ccolor >= bp->ncolors) bp->ccolor = 0;
435
436   glMaterialfv (GL_FRONT, GL_SPECULAR,            bspec);
437   glMateriali  (GL_FRONT, GL_SHININESS,           bshiny);
438   glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, bcolor);
439
440   glScalef(0.25, 0.25, 0.25);
441
442   if (bp->mode != 0)
443     {
444       GLfloat s = (bp->mode == 1
445                    ? bp->mode_tick / (10 * speed)
446                    : ((10 * speed) - bp->mode_tick + 1) / (10 * speed));
447       glScalef (s, s, s);
448     }
449
450   glCallList (bp->knot_list);
451
452   glPopMatrix ();
453
454   if (mi->fps_p) do_fps (mi);
455   glFinish();
456
457   glXSwapBuffers(dpy, window);
458 }
459
460 XSCREENSAVER_MODULE_2 ("GLKnots", glknots, knot)
461
462 #endif /* USE_GL */