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