From http://www.jwz.org/xscreensaver/xscreensaver-5.36.tar.gz
[xscreensaver] / hacks / glx / cubetwist.c
1 /* cubetwist, Copyright (c) 2016 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
12 #define DEFAULTS        "*delay:        30000       \n" \
13                         "*showFPS:      False       \n" \
14                         "*wireframe:    False       \n" \
15                         "*suppressRotationAnimation: True\n" \
16
17 # define refresh_cube 0
18 #undef countof
19 #define countof(x) (sizeof((x))/sizeof((*x)))
20
21 #include "xlockmore.h"
22 #include "normals.h"
23 #include "rotator.h"
24 #include "gltrackball.h"
25 #include <ctype.h>
26
27 #ifdef USE_GL /* whole file */
28
29
30 #define DEF_SPIN         "True"
31 #define DEF_WANDER       "True"
32 #define DEF_SPEED        "1.0"
33 #define DEF_FLAT         "True"
34 #define DEF_THICKNESS    "0.0"
35 #define DEF_DISPLACEMENT "0.0"
36
37 typedef struct cube cube;
38 struct cube {
39   GLfloat size, thickness;
40   XYZ pos, rot;
41   GLfloat color[4];
42   cube *next;
43 };
44
45 typedef struct oscillator oscillator;
46 struct oscillator {
47   double ratio, from, to, speed, *var;
48   int remaining;
49   oscillator *next;
50 };
51
52 typedef struct {
53   GLXContext *glx_context;
54   rotator *rot;
55   trackball_state *trackball;
56   Bool button_down_p;
57   cube *cubes;
58   oscillator *oscillators;
59 } cube_configuration;
60
61 static cube_configuration *bps = NULL;
62
63 static Bool do_flat;
64 static Bool do_spin;
65 static GLfloat speed;
66 static GLfloat thickness;
67 static GLfloat displacement;
68 static Bool do_wander;
69
70 static XrmOptionDescRec opts[] = {
71   { "-spin",   ".spin",   XrmoptionNoArg, "True" },
72   { "+spin",   ".spin",   XrmoptionNoArg, "False" },
73   { "-wander", ".wander", XrmoptionNoArg, "True" },
74   { "+wander", ".wander", XrmoptionNoArg, "False" },
75   { "-flat",   ".flat",   XrmoptionNoArg, "True" },
76   { "+flat",   ".flat",   XrmoptionNoArg, "False" },
77   { "-speed",  ".speed",  XrmoptionSepArg, 0 },
78   { "-thickness",  ".thickness",  XrmoptionSepArg, 0 },
79   { "-displacement",  ".displacement",  XrmoptionSepArg, 0 },
80 };
81
82 static argtype vars[] = {
83   {&do_flat,   "flat",   "flat",   DEF_FLAT,   t_Bool},
84   {&do_spin,   "spin",   "Spin",   DEF_SPIN,   t_Bool},
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   {&displacement, "displacement", "Displacement", DEF_DISPLACEMENT, t_Float},
89 };
90
91 ENTRYPOINT ModeSpecOpt cube_opts = {countof(opts), opts, countof(vars), vars, NULL};
92
93
94 static int
95 draw_strut (ModeInfo *mi, cube *c)
96 {
97   int wire = MI_IS_WIREFRAME(mi);
98   int polys = 0;
99
100   glPushMatrix();
101   glFrontFace (GL_CW);
102   glNormal3f (0, 0, -1);
103   glTranslatef (-c->size/2, -c->size/2, -c->size/2);
104
105   glBegin (wire ? GL_LINE_LOOP : GL_TRIANGLE_FAN);
106   glVertex3f (0, 0, 0);
107   glVertex3f (c->size, 0, 0);
108   glVertex3f (c->size - c->thickness, c->thickness, 0);
109   glVertex3f (c->thickness, c->thickness, 0);
110   glEnd();
111   polys += 2;
112
113   glNormal3f (0, 1, 0);
114   glBegin (wire ? GL_LINE_LOOP : GL_TRIANGLE_FAN);
115   glVertex3f (c->thickness, c->thickness, 0);
116   glVertex3f (c->size - c->thickness, c->thickness, 0);
117   glVertex3f (c->size - c->thickness, c->thickness, c->thickness);
118   glVertex3f (c->thickness, c->thickness, c->thickness);
119   glEnd();
120   polys += 2;
121   glPopMatrix();
122
123   return polys;
124 }
125
126
127 static int
128 draw_cubes (ModeInfo *mi, cube *c)
129 {
130   int polys = 0;
131   int i, j;
132
133   glPushMatrix();
134   glColor4fv (c->color);
135   glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, c->color);
136
137   glPushMatrix();
138   for (j = 0; j < 6; j++)
139     {
140       for (i = 0; i < 4; i++)
141         {
142           polys += draw_strut (mi, c);
143           glRotatef (90, 0, 0, 1);
144         }
145       if (j == 3)
146         glRotatef (90, 0, 0, 1);
147       if (j < 4)
148         glRotatef (90, 0, 1, 0);
149       else
150         glRotatef (180, 1, 0, 0);
151     }
152   glPopMatrix();
153
154   if (c->next)
155     {
156       glRotatef (c->rot.x, 1, 0, 0);
157       glRotatef (c->rot.y, 0, 1, 0);
158       glRotatef (c->rot.z, 0, 0, 1);
159       glTranslatef (c->pos.x, c->pos.y, c->pos.z);
160       c->next->pos = c->pos;
161       c->next->rot = c->rot;
162       polys += draw_cubes (mi, c->next);
163     }
164   glPopMatrix();
165
166   return polys;
167 }
168
169
170 static void
171 make_cubes (ModeInfo *mi)
172 {
173   cube_configuration *bp = &bps[MI_SCREEN(mi)];
174   GLfloat step = 2 * (thickness + displacement);
175   GLfloat size = 1.0;
176   cube *tail = 0;
177   GLfloat cc[4], cstep;
178   int depth = 0;
179   cube *c;
180
181   cc[0] = 0.3 + frand(0.7);
182   cc[1] = 0.3 + frand(0.7);
183   cc[2] = 0.3 + frand(0.7);
184   cc[3] = 1;
185
186   if (bp->cubes) abort();
187   while (1)
188     {
189       cube *c = (cube *) calloc (1, sizeof (*c));
190       c->size = size;
191       c->thickness = thickness;
192       if (tail)
193         tail->next = c;
194       else
195         bp->cubes = c;
196       tail = c;
197
198       depth++;
199       size -= step;
200       if (size <= step)
201         break;
202     }
203
204   cstep = 0.8 / depth;
205   for (c = bp->cubes; c; c = c->next)
206     {
207       memcpy (c->color, cc, sizeof(cc));
208       cc[0] -= cstep;
209       cc[1] -= cstep;
210       cc[2] -= cstep;
211     }
212 }
213
214
215 static GLfloat
216 ease_fn (GLfloat r)
217 {
218   return cos ((r/2 + 1) * M_PI) + 1; /* Smooth curve up, end at slope 1. */
219 }
220
221
222 static GLfloat
223 ease_ratio (GLfloat r)
224 {
225   GLfloat ease = 0.5;
226   if      (r <= 0)     return 0;
227   else if (r >= 1)     return 1;
228   else if (r <= ease)  return     ease * ease_fn (r / ease);
229   else if (r > 1-ease) return 1 - ease * ease_fn ((1 - r) / ease);
230   else                 return r;
231 }
232
233
234 static void
235 tick_oscillators (ModeInfo *mi)
236 {
237   cube_configuration *bp = &bps[MI_SCREEN(mi)];
238   oscillator *prev = 0;
239   oscillator *a = bp->oscillators;
240   GLfloat tick = 0.1 / speed;
241
242   while (a)
243     {
244       oscillator *next = a->next;
245       a->ratio += tick * a->speed;
246       if (a->ratio > 1)
247         a->ratio = 1;
248
249       *a->var = a->from + (a->to - a->from) * ease_ratio (a->ratio);
250
251       if (a->ratio < 1)                 /* mid cycle */
252         prev = a;
253       else if (--a->remaining <= 0)     /* ended, and expired */
254         {
255           if (prev)
256             prev->next = next;
257           else
258             bp->oscillators = next;
259           free (a);
260         }
261       else                              /* keep going the other way */
262         {
263           GLfloat swap = a->from;
264           a->from = a->to;
265           a->to = swap;
266           a->ratio = 0;
267           prev = a;
268         }
269
270       a = next;
271     }
272 }
273
274
275 static void
276 add_oscillator (ModeInfo *mi, double *var, GLfloat speed, GLfloat to,
277                 int repeat)
278 {
279   cube_configuration *bp = &bps[MI_SCREEN(mi)];
280   oscillator *a;
281
282   /* If an oscillator is already running on this variable, don't
283      add another. */
284   for (a = bp->oscillators; a && a->next; a = a->next)
285     if (a->var == var)
286       return;
287
288   a = (oscillator *) calloc (1, sizeof (*a));
289   if (repeat <= 0) abort();
290   a->ratio = 0;
291   a->from = *var;
292   a->to = to;
293   a->speed = speed;
294   a->var = var;
295   a->remaining = repeat;
296   a->next = bp->oscillators;
297   bp->oscillators = a;
298 # if 0
299   fprintf (stderr, "%s: %3d %6.2f -> %6.2f %s\n",
300            progname, repeat, *var, to,
301            (var == &bp->midpoint.z ? "z" :
302             var == &bp->tilt ? "tilt" :
303             var == &bp->axial_radius ? "r" :
304             var == &bp->speed ? "speed" : "?"));
305 # endif
306 }
307
308
309 #undef RANDSIGN
310 #define RANDSIGN() ((random() & 1) ? 1 : -1)
311
312 static void
313 add_random_oscillator (ModeInfo *mi)
314 {
315   cube_configuration *bp = &bps[MI_SCREEN(mi)];
316   cube *c = bp->cubes;
317   double s1 = speed * 0.07;
318   double s2 = speed * 0.3;
319   double disp = (thickness + displacement);
320   int c1 = 1 + ((random() % 4) ? 0 : (random() % 3));
321   int c2 = 2;
322   int n = random() % 6;
323
324   switch (n) {
325   case 0: add_oscillator (mi, &c->rot.x, s1, 90 * RANDSIGN(), c1); break;
326   case 1: add_oscillator (mi, &c->rot.y, s1, 90 * RANDSIGN(), c1); break;
327   case 2: add_oscillator (mi, &c->rot.z, s1, 90 * RANDSIGN(), c1); break;
328   case 3: add_oscillator (mi, &c->pos.x, s2, disp * RANDSIGN(), c2); break;
329   case 4: add_oscillator (mi, &c->pos.y, s2, disp * RANDSIGN(), c2); break;
330   case 5: add_oscillator (mi, &c->pos.z, s2, disp * RANDSIGN(), c2); break;
331   default: abort(); break;
332   }
333 }
334
335
336 /* Window management, etc
337  */
338 ENTRYPOINT void
339 reshape_cube (ModeInfo *mi, int width, int height)
340 {
341   GLfloat h = (GLfloat) height / (GLfloat) width;
342
343   glViewport (0, 0, (GLint) width, (GLint) height);
344
345   glMatrixMode(GL_PROJECTION);
346   glLoadIdentity();
347   gluPerspective (30.0, 1/h, 1.0, 100.0);
348
349   glMatrixMode(GL_MODELVIEW);
350   glLoadIdentity();
351   gluLookAt( 0.0, 0.0, 30.0,
352              0.0, 0.0, 0.0,
353              0.0, 1.0, 0.0);
354
355 # ifdef HAVE_MOBILE     /* Keep it the same relative size when rotated. */
356   {
357     int o = (int) current_device_rotation();
358     if (o != 0 && o != 180 && o != -180)
359       glScalef (1/h, 1/h, 1/h);
360   }
361 # endif
362
363   glClear(GL_COLOR_BUFFER_BIT);
364 }
365
366
367 ENTRYPOINT Bool
368 cube_handle_event (ModeInfo *mi, XEvent *event)
369 {
370   cube_configuration *bp = &bps[MI_SCREEN(mi)];
371
372   if (gltrackball_event_handler (event, bp->trackball,
373                                  MI_WIDTH (mi), MI_HEIGHT (mi),
374                                  &bp->button_down_p))
375     return True;
376   else if (event->xany.type == KeyPress)
377     {
378       KeySym keysym;
379       char c = 0;
380       XLookupString (&event->xkey, &c, 1, &keysym, 0);
381       if (c == ' ' || c == '\t')
382         {
383           while (bp->cubes)
384             {
385               cube *c = bp->cubes->next;
386               free (bp->cubes);
387               bp->cubes = c;
388             }
389
390           while (bp->oscillators)
391             {
392               oscillator *o = bp->oscillators->next;
393               free (bp->oscillators);
394               bp->oscillators = o;
395             }
396
397           if (random() & 1)
398             {
399               thickness = 0.03 + frand(0.02);
400               displacement = (random() & 1) ? 0 : (thickness / 3);
401             }
402           else
403             {
404               thickness = 0.001 + frand(0.02);
405               displacement = 0;
406             }
407
408           make_cubes (mi);
409
410           return True;
411         }
412     }
413
414   return False;
415 }
416
417
418 ENTRYPOINT void 
419 init_cube (ModeInfo *mi)
420 {
421   cube_configuration *bp;
422   int wire = MI_IS_WIREFRAME(mi);
423
424   if (!bps) {
425     bps = (cube_configuration *)
426       calloc (MI_NUM_SCREENS(mi), sizeof (cube_configuration));
427     if (!bps) {
428       fprintf(stderr, "%s: out of memory\n", progname);
429       exit(1);
430     }
431   }
432
433   bp = &bps[MI_SCREEN(mi)];
434
435   bp->glx_context = init_GL(mi);
436
437   reshape_cube (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
438
439   if (!wire && !do_flat)
440     {
441       GLfloat color[4] = {1, 1, 1, 1};
442       GLfloat cspec[4] = {1, 1, 0, 1};
443       static const GLfloat shiny = 30;
444
445       static GLfloat pos0[4] = { 0.5, -1, -0.5, 0};
446       static GLfloat pos1[4] = {-0.75, -1, 0, 0};
447       static GLfloat amb[4] = {0, 0, 0, 1};
448       static GLfloat dif[4] = {1, 1, 1, 1};
449       static GLfloat spc[4] = {1, 1, 1, 1};
450
451       glEnable(GL_LIGHTING);
452       glEnable(GL_LIGHT0);
453       glEnable(GL_LIGHT1);
454       glEnable(GL_DEPTH_TEST);
455       glEnable(GL_CULL_FACE);
456
457       glLightfv(GL_LIGHT0, GL_POSITION, pos0);
458       glLightfv(GL_LIGHT0, GL_AMBIENT,  amb);
459       glLightfv(GL_LIGHT0, GL_DIFFUSE,  dif);
460       glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
461
462       glLightfv(GL_LIGHT1, GL_POSITION, pos1);
463       glLightfv(GL_LIGHT1, GL_AMBIENT,  amb);
464       glLightfv(GL_LIGHT1, GL_DIFFUSE,  dif);
465       glLightfv(GL_LIGHT1, GL_SPECULAR, spc);
466
467       glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
468       glMaterialfv (GL_FRONT, GL_SPECULAR,  cspec);
469       glMateriali  (GL_FRONT, GL_SHININESS, shiny);
470     }
471
472   {
473     double spin_speed   = 0.05;
474     double wander_speed = 0.005;
475     double spin_accel   = 1.0;
476
477     bp->rot = make_rotator (do_spin ? spin_speed : 0,
478                             do_spin ? spin_speed : 0,
479                             do_spin ? spin_speed : 0,
480                             spin_accel,
481                             do_wander ? wander_speed : 0,
482                             True);
483     bp->trackball = gltrackball_init (True);
484   }
485
486   if (thickness > 0.5)
487     thickness = 0.5;
488   if (displacement > 0.5)
489     displacement = 0.5;
490
491   if (thickness <= 0.0001)
492     {
493       if (random() & 1)
494         {
495           thickness = 0.03 + frand(0.02);
496           displacement = (random() & 1) ? 0 : (thickness / 3);
497         }
498       else
499         {
500           thickness = 0.001 + frand(0.02);
501           displacement = 0;
502         }
503     }
504
505   make_cubes (mi);
506 }
507
508
509 ENTRYPOINT void
510 draw_cube (ModeInfo *mi)
511 {
512   cube_configuration *bp = &bps[MI_SCREEN(mi)];
513   Display *dpy = MI_DISPLAY(mi);
514   Window window = MI_WINDOW(mi);
515
516   if (!bp->glx_context)
517     return;
518
519   glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
520
521   glShadeModel(GL_SMOOTH);
522   glEnable(GL_DEPTH_TEST);
523   glEnable(GL_NORMALIZE);
524   glEnable(GL_CULL_FACE);
525
526   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
527
528   glPushMatrix ();
529
530   glScalef(1.1, 1.1, 1.1);
531
532   {
533     double x, y, z;
534     get_position (bp->rot, &x, &y, &z, !bp->button_down_p);
535     glTranslatef((x - 0.5) * 4,
536                  (y - 0.5) * 4,
537                  (z - 0.5) * 2);
538
539     gltrackball_rotate (bp->trackball);
540
541     get_rotation (bp->rot, &x, &y, &z, !bp->button_down_p);
542     glRotatef (x * 360, 1.0, 0.0, 0.0);
543     glRotatef (y * 360, 0.0, 1.0, 0.0);
544     glRotatef (z * 360, 0.0, 0.0, 1.0);
545   }
546
547   mi->polygon_count = 0;
548
549   glScalef (6, 6, 6);
550
551   mi->polygon_count = draw_cubes (mi, bp->cubes);
552   glPopMatrix ();
553
554   if (!bp->button_down_p)
555     tick_oscillators (mi);
556
557   if (! bp->oscillators &&
558       !bp->button_down_p &&
559       !(random() % 60))
560     {
561       bp->cubes->pos.x = bp->cubes->pos.y = bp->cubes->pos.z = 0;
562       bp->cubes->rot.x = bp->cubes->rot.y = bp->cubes->rot.z = 0;
563       add_random_oscillator (mi);
564     }
565
566   if (mi->fps_p) do_fps (mi);
567   glFinish();
568
569   glXSwapBuffers(dpy, window);
570 }
571
572 ENTRYPOINT void
573 release_cube (ModeInfo *mi)
574 {
575   cube_configuration *bp = &bps[MI_SCREEN(mi)];
576   while (bp->cubes)
577     {
578       cube *c = bp->cubes->next;
579       free (bp->cubes);
580       bp->cubes = c;
581     }
582
583   while (bp->oscillators)
584     {
585       oscillator *o = bp->oscillators->next;
586       free (bp->oscillators);
587       bp->oscillators = o;
588     }
589 }
590
591
592 XSCREENSAVER_MODULE_2 ("CubeTwist", cubetwist, cube)
593
594 #endif /* USE_GL */