1 /* glknots, Copyright (c) 2003, 2004 Jamie Zawinski <jwz@jwz.org>
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
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/
16 #include <X11/Intrinsic.h>
18 extern XtAppContext app;
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
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"
35 #define DEFAULTS "*delay: 30000 \n" \
36 "*showFPS: False \n" \
37 "*wireframe: False \n" \
38 "*speed: " DEF_SPEED " \n" \
39 "*spin: " DEF_SPIN "\n" \
40 "*wander: " DEF_WANDER "\n" \
41 "*thickness: " DEF_THICKNESS "\n" \
42 "*segments: " DEF_SEGMENTS "\n" \
43 "*duration: " DEF_DURATION "\n" \
46 #define countof(x) (sizeof((x))/sizeof((*x)))
48 #include "xlockmore.h"
52 #include "gltrackball.h"
55 #ifdef USE_GL /* whole file */
60 GLXContext *glx_context;
62 trackball_state *trackball;
71 int mode; /* 0 = normal, 1 = out, 2 = in */
77 static knot_configuration *bps = NULL;
81 static Bool do_wander;
82 static GLfloat thickness;
83 static unsigned int segments;
86 static XrmOptionDescRec opts[] = {
87 { "-spin", ".spin", XrmoptionSepArg, 0 },
88 { "+spin", ".spin", XrmoptionNoArg, "" },
89 { "-wander", ".wander", XrmoptionNoArg, "True" },
90 { "+wander", ".wander", XrmoptionNoArg, "False" },
91 { "-speed", ".speed", XrmoptionSepArg, 0 },
92 { "-thickness", ".thickness", XrmoptionSepArg, 0 },
93 { "-segments", ".segments", XrmoptionSepArg, 0 },
94 { "-duration", ".duration", XrmoptionSepArg, 0 },
97 static argtype vars[] = {
98 {&do_spin, "spin", "Spin", DEF_SPIN, t_String},
99 {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
100 {&speed, "speed", "Speed", DEF_SPEED, t_Float},
101 {&thickness, "thickness", "Thickness", DEF_THICKNESS, t_Float},
102 {&segments, "segments", "Segments", DEF_SEGMENTS, t_Int},
103 {&duration, "duration", "Duration", DEF_DURATION, t_Int},
106 ModeSpecOpt sws_opts = {countof(opts), opts, countof(vars), vars, NULL};
110 make_knot (ModeInfo *mi)
112 int wire = MI_IS_WIREFRAME(mi);
114 GLfloat diam = (4 * thickness);
115 int faces = (wire ? 3 : 6);
118 double x, y, z, ox=0, oy=0, oz=0;
123 Bool blobby_p = (0 == (random() % 5));
124 Bool type = (random() % 2);
126 for (i = 0; i < countof(p); i++)
128 p[i] = 1 + (random() % 4);
129 if (! (random() % 3))
130 p[i] += (random() % 5);
136 p[1] *= ((p[0] + p[0]) / 10);
140 mi->polygon_count = 0;
142 for (i = 0; i <= segments; i++)
146 mu = i * (M_PI * 2) / segments;
147 x = 10 * (cos(mu) + cos(p[0]*mu)) + cos(p[1]*mu) + cos(p[2]*mu);
148 y = 6 * sin(mu) + 10 * sin(p[3]*mu);
149 z = 16 * sin(p[4]*mu) * sin(p[5]*mu/2) + p[6]*sin(p[7]*mu) -
154 mu = i * (M_PI * 2) * p[0] / (double) segments;
155 x = 10 * cos(mu) * (1 + cos(p[1] * mu/ p[0]) / 2.0);
156 y = 25 * sin(p[1] * mu / p[0]) / 2.0;
157 z = 10 * sin(mu) * (1 + cos(p[1] * mu/ p[0]) / 2.0);
164 GLfloat dist = sqrt ((x-ox)*(x-ox) +
172 di = dist * (segments / 500.0);
179 faces, True, wire, wire);
181 mi->polygon_count += faces;
191 /* Window management, etc
194 reshape_knot (ModeInfo *mi, int width, int height)
196 GLfloat h = (GLfloat) height / (GLfloat) width;
198 glViewport (0, 0, (GLint) width, (GLint) height);
200 glMatrixMode(GL_PROJECTION);
202 gluPerspective (30.0, 1/h, 1.0, 100.0);
204 glMatrixMode(GL_MODELVIEW);
206 gluLookAt( 0.0, 0.0, 30.0,
210 glClear(GL_COLOR_BUFFER_BIT);
215 new_knot (ModeInfo *mi)
217 knot_configuration *bp = &bps[MI_SCREEN(mi)];
220 bp->clear_p = !!(random() % 15);
223 bp->colors = (XColor *) calloc(bp->ncolors, sizeof(XColor));
224 make_smooth_colormap (0, 0, 0,
225 bp->colors, &bp->ncolors,
228 for (i = 0; i < bp->ncolors; i++)
230 /* make colors twice as bright */
231 bp->colors[i].red = (bp->colors[i].red >> 2) + 0x7FFF;
232 bp->colors[i].green = (bp->colors[i].green >> 2) + 0x7FFF;
233 bp->colors[i].blue = (bp->colors[i].blue >> 2) + 0x7FFF;
236 glNewList (bp->knot_list, GL_COMPILE);
243 knot_handle_event (ModeInfo *mi, XEvent *event)
245 knot_configuration *bp = &bps[MI_SCREEN(mi)];
247 if (event->xany.type == ButtonPress &&
248 event->xbutton.button & Button1)
250 bp->button_down_p = True;
251 gltrackball_start (bp->trackball,
252 event->xbutton.x, event->xbutton.y,
253 MI_WIDTH (mi), MI_HEIGHT (mi));
256 else if (event->xany.type == ButtonRelease &&
257 event->xbutton.button & Button1)
259 bp->button_down_p = False;
262 else if (event->xany.type == MotionNotify &&
265 gltrackball_track (bp->trackball,
266 event->xmotion.x, event->xmotion.y,
267 MI_WIDTH (mi), MI_HEIGHT (mi));
277 init_knot (ModeInfo *mi)
279 knot_configuration *bp;
280 int wire = MI_IS_WIREFRAME(mi);
283 bps = (knot_configuration *)
284 calloc (MI_NUM_SCREENS(mi), sizeof (knot_configuration));
286 fprintf(stderr, "%s: out of memory\n", progname);
290 bp = &bps[MI_SCREEN(mi)];
293 bp = &bps[MI_SCREEN(mi)];
295 bp->glx_context = init_GL(mi);
297 if (thickness <= 0) thickness = 0.001;
298 else if (thickness > 1) thickness = 1;
300 if (segments < 10) segments = 10;
302 reshape_knot (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
306 GLfloat pos[4] = {1.0, 1.0, 1.0, 0.0};
307 GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0};
308 GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
309 GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
311 glEnable(GL_LIGHTING);
313 glEnable(GL_DEPTH_TEST);
314 glEnable(GL_CULL_FACE);
316 glLightfv(GL_LIGHT0, GL_POSITION, pos);
317 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
318 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
319 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
323 Bool spinx=False, spiny=False, spinz=False;
324 double spin_speed = 2.0;
325 double wander_speed = 0.05;
326 double spin_accel = 0.2;
331 if (*s == 'x' || *s == 'X') spinx = True;
332 else if (*s == 'y' || *s == 'Y') spiny = True;
333 else if (*s == 'z' || *s == 'Z') spinz = True;
337 "%s: spin must contain only the characters X, Y, or Z (not \"%s\")\n",
344 bp->rot = make_rotator (spinx ? spin_speed : 0,
345 spiny ? spin_speed : 0,
346 spinz ? spin_speed : 0,
348 do_wander ? wander_speed : 0,
349 (spinx && spiny && spinz));
350 bp->trackball = gltrackball_init ();
353 bp->knot_list = glGenLists (1);
356 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
361 draw_knot (ModeInfo *mi)
363 knot_configuration *bp = &bps[MI_SCREEN(mi)];
364 Display *dpy = MI_DISPLAY(mi);
365 Window window = MI_WINDOW(mi);
367 static GLfloat bcolor[4] = {0.0, 0.0, 0.0, 1.0};
368 static GLfloat bspec[4] = {1.0, 1.0, 1.0, 1.0};
369 static GLfloat bshiny = 128.0;
371 static time_t last_time = 0;
373 if (!bp->glx_context)
381 time_t now = time((time_t *) 0);
382 if (last_time == 0) last_time = now;
384 if (!bp->button_down_p &&
385 now - last_time >= duration)
387 bp->mode = 1; /* go out */
388 bp->mode_tick = 10 * speed;
393 else if (bp->mode == 1) /* out */
395 if (--bp->mode_tick <= 0)
398 bp->mode_tick = 10 * speed;
399 bp->mode = 2; /* go in */
402 else if (bp->mode == 2) /* in */
404 if (--bp->mode_tick <= 0)
405 bp->mode = 0; /* normal */
410 glShadeModel(GL_SMOOTH);
412 glEnable(GL_DEPTH_TEST);
413 glEnable(GL_NORMALIZE);
414 glEnable(GL_CULL_FACE);
417 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
423 get_position (bp->rot, &x, &y, &z, !bp->button_down_p);
424 glTranslatef((x - 0.5) * 8,
428 gltrackball_rotate (bp->trackball);
430 get_rotation (bp->rot, &x, &y, &z, !bp->button_down_p);
432 glRotatef (x * 360, 1.0, 0.0, 0.0);
433 glRotatef (y * 360, 0.0, 1.0, 0.0);
434 glRotatef (z * 360, 0.0, 0.0, 1.0);
437 bcolor[0] = bp->colors[bp->ccolor].red / 65536.0;
438 bcolor[1] = bp->colors[bp->ccolor].green / 65536.0;
439 bcolor[2] = bp->colors[bp->ccolor].blue / 65536.0;
441 if (bp->ccolor >= bp->ncolors) bp->ccolor = 0;
443 glMaterialfv (GL_FRONT, GL_SPECULAR, bspec);
444 glMateriali (GL_FRONT, GL_SHININESS, bshiny);
445 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, bcolor);
447 glScalef(0.25, 0.25, 0.25);
451 GLfloat s = (bp->mode == 1
452 ? bp->mode_tick / (10 * speed)
453 : ((10 * speed) - bp->mode_tick + 1) / (10 * speed));
457 glCallList (bp->knot_list);
461 if (mi->fps_p) do_fps (mi);
464 glXSwapBuffers(dpy, window);