1 /* cubetwist, Copyright (c) 2016-2017 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
12 #define DEFAULTS "*delay: 30000 \n" \
13 "*showFPS: False \n" \
14 "*wireframe: False \n" \
15 "*suppressRotationAnimation: True\n" \
17 # define refresh_cube 0
18 # define release_cube 0
20 #define countof(x) (sizeof((x))/sizeof((*x)))
22 #include "xlockmore.h"
25 #include "gltrackball.h"
28 #ifdef USE_GL /* whole file */
31 #define DEF_SPIN "True"
32 #define DEF_WANDER "True"
33 #define DEF_SPEED "1.0"
34 #define DEF_FLAT "True"
35 #define DEF_THICKNESS "0.0"
36 #define DEF_DISPLACEMENT "0.0"
38 typedef struct cube cube;
40 GLfloat size, thickness;
46 typedef struct oscillator oscillator;
48 double ratio, from, to, speed, *var;
54 GLXContext *glx_context;
56 trackball_state *trackball;
59 oscillator *oscillators;
62 static cube_configuration *bps = NULL;
67 static GLfloat thickness;
68 static GLfloat displacement;
69 static Bool do_wander;
71 static XrmOptionDescRec opts[] = {
72 { "-spin", ".spin", XrmoptionNoArg, "True" },
73 { "+spin", ".spin", XrmoptionNoArg, "False" },
74 { "-wander", ".wander", XrmoptionNoArg, "True" },
75 { "+wander", ".wander", XrmoptionNoArg, "False" },
76 { "-flat", ".flat", XrmoptionNoArg, "True" },
77 { "+flat", ".flat", XrmoptionNoArg, "False" },
78 { "-speed", ".speed", XrmoptionSepArg, 0 },
79 { "-thickness", ".thickness", XrmoptionSepArg, 0 },
80 { "-displacement", ".displacement", XrmoptionSepArg, 0 },
83 static argtype vars[] = {
84 {&do_flat, "flat", "flat", DEF_FLAT, t_Bool},
85 {&do_spin, "spin", "Spin", DEF_SPIN, t_Bool},
86 {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
87 {&speed, "speed", "Speed", DEF_SPEED, t_Float},
88 {&thickness, "thickness", "Thickness", DEF_THICKNESS, t_Float},
89 {&displacement, "displacement", "Displacement", DEF_DISPLACEMENT, t_Float},
92 ENTRYPOINT ModeSpecOpt cube_opts = {countof(opts), opts, countof(vars), vars, NULL};
96 draw_strut (ModeInfo *mi, cube *c)
98 int wire = MI_IS_WIREFRAME(mi);
103 glNormal3f (0, 0, -1);
104 glTranslatef (-c->size/2, -c->size/2, -c->size/2);
106 glBegin (wire ? GL_LINE_LOOP : GL_TRIANGLE_FAN);
107 glVertex3f (0, 0, 0);
108 glVertex3f (c->size, 0, 0);
109 glVertex3f (c->size - c->thickness, c->thickness, 0);
110 glVertex3f (c->thickness, c->thickness, 0);
114 glNormal3f (0, 1, 0);
115 glBegin (wire ? GL_LINE_LOOP : GL_TRIANGLE_FAN);
116 glVertex3f (c->thickness, c->thickness, 0);
117 glVertex3f (c->size - c->thickness, c->thickness, 0);
118 glVertex3f (c->size - c->thickness, c->thickness, c->thickness);
119 glVertex3f (c->thickness, c->thickness, c->thickness);
129 draw_cubes (ModeInfo *mi, cube *c)
134 glColor4fv (c->color);
135 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, c->color);
138 for (j = 0; j < 6; j++)
140 for (i = 0; i < 4; i++)
142 polys += draw_strut (mi, c);
143 glRotatef (90, 0, 0, 1);
146 glRotatef (90, 0, 0, 1);
148 glRotatef (90, 0, 1, 0);
150 glRotatef (180, 1, 0, 0);
156 /* This leaves rotations on the prevailing matrix stack, but since
157 this is a tail-call, that's fine. Don't blow the matrix stack. */
158 glRotatef (c->rot.x, 1, 0, 0);
159 glRotatef (c->rot.y, 0, 1, 0);
160 glRotatef (c->rot.z, 0, 0, 1);
161 glTranslatef (c->pos.x, c->pos.y, c->pos.z);
162 c->next->pos = c->pos;
163 c->next->rot = c->rot;
164 polys += draw_cubes (mi, c->next);
167 check_gl_error("cubetwist");
173 make_cubes (ModeInfo *mi)
175 cube_configuration *bp = &bps[MI_SCREEN(mi)];
176 GLfloat step = 2 * (thickness + displacement);
179 GLfloat cc[4], cstep;
183 cc[0] = 0.3 + frand(0.7);
184 cc[1] = 0.3 + frand(0.7);
185 cc[2] = 0.3 + frand(0.7);
188 if (bp->cubes) abort();
191 cube *c = (cube *) calloc (1, sizeof (*c));
193 c->thickness = thickness;
207 for (c = bp->cubes; c; c = c->next)
209 memcpy (c->color, cc, sizeof(cc));
220 return cos ((r/2 + 1) * M_PI) + 1; /* Smooth curve up, end at slope 1. */
225 ease_ratio (GLfloat r)
228 if (r <= 0) return 0;
229 else if (r >= 1) return 1;
230 else if (r <= ease) return ease * ease_fn (r / ease);
231 else if (r > 1-ease) return 1 - ease * ease_fn ((1 - r) / ease);
237 tick_oscillators (ModeInfo *mi)
239 cube_configuration *bp = &bps[MI_SCREEN(mi)];
240 oscillator *prev = 0;
241 oscillator *a = bp->oscillators;
242 GLfloat tick = 0.1 / speed;
246 oscillator *next = a->next;
247 a->ratio += tick * a->speed;
251 *a->var = a->from + (a->to - a->from) * ease_ratio (a->ratio);
253 if (a->ratio < 1) /* mid cycle */
255 else if (--a->remaining <= 0) /* ended, and expired */
260 bp->oscillators = next;
263 else /* keep going the other way */
265 GLfloat swap = a->from;
278 add_oscillator (ModeInfo *mi, double *var, GLfloat speed, GLfloat to,
281 cube_configuration *bp = &bps[MI_SCREEN(mi)];
284 /* If an oscillator is already running on this variable, don't
286 for (a = bp->oscillators; a && a->next; a = a->next)
290 a = (oscillator *) calloc (1, sizeof (*a));
291 if (repeat <= 0) abort();
297 a->remaining = repeat;
298 a->next = bp->oscillators;
301 fprintf (stderr, "%s: %3d %6.2f -> %6.2f %s\n",
302 progname, repeat, *var, to,
303 (var == &bp->midpoint.z ? "z" :
304 var == &bp->tilt ? "tilt" :
305 var == &bp->axial_radius ? "r" :
306 var == &bp->speed ? "speed" : "?"));
312 #define RANDSIGN() ((random() & 1) ? 1 : -1)
315 add_random_oscillator (ModeInfo *mi)
317 cube_configuration *bp = &bps[MI_SCREEN(mi)];
319 double s1 = speed * 0.07;
320 double s2 = speed * 0.3;
321 double disp = (thickness + displacement);
322 int c1 = 1 + ((random() % 4) ? 0 : (random() % 3));
324 int n = random() % 6;
327 case 0: add_oscillator (mi, &c->rot.x, s1, 90 * RANDSIGN(), c1); break;
328 case 1: add_oscillator (mi, &c->rot.y, s1, 90 * RANDSIGN(), c1); break;
329 case 2: add_oscillator (mi, &c->rot.z, s1, 90 * RANDSIGN(), c1); break;
330 case 3: add_oscillator (mi, &c->pos.x, s2, disp * RANDSIGN(), c2); break;
331 case 4: add_oscillator (mi, &c->pos.y, s2, disp * RANDSIGN(), c2); break;
332 case 5: add_oscillator (mi, &c->pos.z, s2, disp * RANDSIGN(), c2); break;
333 default: abort(); break;
338 /* Window management, etc
341 reshape_cube (ModeInfo *mi, int width, int height)
343 GLfloat h = (GLfloat) height / (GLfloat) width;
345 glViewport (0, 0, (GLint) width, (GLint) height);
347 glMatrixMode(GL_PROJECTION);
349 gluPerspective (30.0, 1/h, 1.0, 100.0);
351 glMatrixMode(GL_MODELVIEW);
353 gluLookAt( 0.0, 0.0, 30.0,
357 # ifdef HAVE_MOBILE /* Keep it the same relative size when rotated. */
359 int o = (int) current_device_rotation();
360 if (o != 0 && o != 180 && o != -180)
361 glScalef (1/h, 1/h, 1/h);
365 glClear(GL_COLOR_BUFFER_BIT);
370 cube_handle_event (ModeInfo *mi, XEvent *event)
372 cube_configuration *bp = &bps[MI_SCREEN(mi)];
374 if (gltrackball_event_handler (event, bp->trackball,
375 MI_WIDTH (mi), MI_HEIGHT (mi),
378 else if (event->xany.type == KeyPress)
382 XLookupString (&event->xkey, &c, 1, &keysym, 0);
383 if (c == ' ' || c == '\t')
387 cube *c = bp->cubes->next;
392 while (bp->oscillators)
394 oscillator *o = bp->oscillators->next;
395 free (bp->oscillators);
401 thickness = 0.03 + frand(0.02);
402 displacement = (random() & 1) ? 0 : (thickness / 3);
406 thickness = 0.001 + frand(0.02);
420 static void free_cube (ModeInfo *mi);
423 init_cube (ModeInfo *mi)
425 cube_configuration *bp;
426 int wire = MI_IS_WIREFRAME(mi);
428 MI_INIT (mi, bps, free_cube);
430 bp = &bps[MI_SCREEN(mi)];
432 bp->glx_context = init_GL(mi);
434 reshape_cube (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
436 if (!wire && !do_flat)
438 GLfloat color[4] = {1, 1, 1, 1};
439 GLfloat cspec[4] = {1, 1, 0, 1};
440 static const GLfloat shiny = 30;
442 static GLfloat pos0[4] = { 0.5, -1, -0.5, 0};
443 static GLfloat pos1[4] = {-0.75, -1, 0, 0};
444 static GLfloat amb[4] = {0, 0, 0, 1};
445 static GLfloat dif[4] = {1, 1, 1, 1};
446 static GLfloat spc[4] = {1, 1, 1, 1};
448 glEnable(GL_LIGHTING);
451 glEnable(GL_DEPTH_TEST);
452 glEnable(GL_CULL_FACE);
454 glLightfv(GL_LIGHT0, GL_POSITION, pos0);
455 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
456 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
457 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
459 glLightfv(GL_LIGHT1, GL_POSITION, pos1);
460 glLightfv(GL_LIGHT1, GL_AMBIENT, amb);
461 glLightfv(GL_LIGHT1, GL_DIFFUSE, dif);
462 glLightfv(GL_LIGHT1, GL_SPECULAR, spc);
464 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
465 glMaterialfv (GL_FRONT, GL_SPECULAR, cspec);
466 glMateriali (GL_FRONT, GL_SHININESS, shiny);
470 double spin_speed = 0.05;
471 double wander_speed = 0.005;
472 double spin_accel = 1.0;
474 bp->rot = make_rotator (do_spin ? spin_speed : 0,
475 do_spin ? spin_speed : 0,
476 do_spin ? spin_speed : 0,
478 do_wander ? wander_speed : 0,
480 bp->trackball = gltrackball_init (True);
485 if (displacement > 0.5)
488 if (thickness <= 0.0001)
492 thickness = 0.03 + frand(0.02);
493 displacement = (random() & 1) ? 0 : (thickness / 3);
497 thickness = 0.001 + frand(0.02);
507 draw_cube (ModeInfo *mi)
509 cube_configuration *bp = &bps[MI_SCREEN(mi)];
510 Display *dpy = MI_DISPLAY(mi);
511 Window window = MI_WINDOW(mi);
513 if (!bp->glx_context)
516 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
518 glShadeModel(GL_SMOOTH);
519 glEnable(GL_DEPTH_TEST);
520 glEnable(GL_NORMALIZE);
521 glEnable(GL_CULL_FACE);
523 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
527 glScalef(1.1, 1.1, 1.1);
531 get_position (bp->rot, &x, &y, &z, !bp->button_down_p);
532 glTranslatef((x - 0.5) * 4,
536 gltrackball_rotate (bp->trackball);
538 get_rotation (bp->rot, &x, &y, &z, !bp->button_down_p);
539 glRotatef (x * 360, 1.0, 0.0, 0.0);
540 glRotatef (y * 360, 0.0, 1.0, 0.0);
541 glRotatef (z * 360, 0.0, 0.0, 1.0);
544 mi->polygon_count = 0;
548 mi->polygon_count = draw_cubes (mi, bp->cubes);
551 if (!bp->button_down_p)
552 tick_oscillators (mi);
554 if (! bp->oscillators &&
555 !bp->button_down_p &&
558 bp->cubes->pos.x = bp->cubes->pos.y = bp->cubes->pos.z = 0;
559 bp->cubes->rot.x = bp->cubes->rot.y = bp->cubes->rot.z = 0;
560 add_random_oscillator (mi);
563 if (mi->fps_p) do_fps (mi);
566 glXSwapBuffers(dpy, window);
570 free_cube (ModeInfo *mi)
572 cube_configuration *bp = &bps[MI_SCREEN(mi)];
575 cube *c = bp->cubes->next;
580 while (bp->oscillators)
582 oscillator *o = bp->oscillators->next;
583 free (bp->oscillators);
589 XSCREENSAVER_MODULE_2 ("CubeTwist", cubetwist, cube)