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