From http://www.jwz.org/xscreensaver/xscreensaver-5.37.tar.gz
[xscreensaver] / hacks / glx / cubestack.c
1 /* cubestack, 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 # define release_cube 0
19 #undef countof
20 #define countof(x) (sizeof((x))/sizeof((*x)))
21
22 #include "xlockmore.h"
23 #include "colors.h"
24 #include "rotator.h"
25 #include "gltrackball.h"
26 #include <ctype.h>
27
28 #ifdef USE_GL /* whole file */
29
30
31 #define DEF_WANDER    "True"
32 #define DEF_SPEED     "1.0"
33 #define DEF_THICKNESS "0.13"
34 #define DEF_OPACITY   "0.7"
35
36 typedef struct {
37   GLXContext *glx_context;
38   rotator *rot;
39   trackball_state *trackball;
40   Bool button_down_p;
41   GLfloat state;
42   GLfloat r;
43   int length;
44   int ncolors;
45   XColor *colors;
46   int ccolor;
47 } cube_configuration;
48
49 static cube_configuration *bps = NULL;
50
51 static GLfloat speed;
52 static GLfloat thickness;
53 static GLfloat opacity;
54 static Bool do_wander;
55
56 static XrmOptionDescRec opts[] = {
57   { "-wander", ".wander", XrmoptionNoArg, "True" },
58   { "+wander", ".wander", XrmoptionNoArg, "False" },
59   { "-speed",  ".speed",  XrmoptionSepArg, 0 },
60   { "-thickness",  ".thickness",  XrmoptionSepArg, 0 },
61   { "-opacity",  ".opacity",  XrmoptionSepArg, 0 },
62 };
63
64 static argtype vars[] = {
65   {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
66   {&speed,     "speed",  "Speed",  DEF_SPEED,  t_Float},
67   {&thickness, "thickness", "Thickness", DEF_THICKNESS, t_Float},
68   {&opacity, "opacity", "Opacity", DEF_OPACITY, t_Float},
69 };
70
71 ENTRYPOINT ModeSpecOpt cube_opts = {countof(opts), opts, countof(vars), vars, NULL};
72
73
74 static int
75 draw_strut (ModeInfo *mi)
76 {
77   int wire = MI_IS_WIREFRAME(mi);
78   int polys = 0;
79   GLfloat h;
80
81   glPushMatrix();
82   glFrontFace (GL_CCW);
83   glNormal3f (0, 0, -1);
84   glTranslatef (-0.5, -0.5, 0);
85   glBegin (wire ? GL_LINE_LOOP : GL_TRIANGLE_FAN);
86   glVertex3f (0, 0, 0);
87   glVertex3f (1, 0, 0);
88   glVertex3f (1 - thickness, thickness, 0);
89   glVertex3f (thickness, thickness, 0);
90   glEnd();
91   polys += 2;
92
93   h = 0.5 - thickness;
94   if (h >= 0.25)
95     {
96       glBegin (wire ? GL_LINE_LOOP : GL_TRIANGLE_FAN);
97       glVertex3f (0.5, 0.5, 0);
98       glVertex3f (0.5 - thickness/2, 0.5 - thickness/2, 0);
99       glVertex3f (0.5 - thickness/2, 0.5 - h/2, 0);
100       glVertex3f (0.5 + thickness/2, 0.5 - h/2, 0);
101       glVertex3f (0.5 + thickness/2, 0.5 - thickness/2, 0);
102       glEnd();
103       polys += 3;
104     }
105
106   glPopMatrix();
107
108   return polys;
109 }
110
111
112 static int
113 draw_face (ModeInfo *mi)
114 {
115   int i;
116   int polys = 0;
117   for (i = 0; i < 4; i++)
118     {
119       polys += draw_strut (mi);
120       glRotatef (90, 0, 0, 1);
121     }
122   return polys;
123 }
124
125
126 static GLfloat
127 ease_fn (GLfloat r)
128 {
129   return cos ((r/2 + 1) * M_PI) + 1; /* Smooth curve up, end at slope 1. */
130 }
131
132
133 static GLfloat
134 ease_ratio (GLfloat r)
135 {
136   GLfloat ease = 0.5;
137   if      (r <= 0)     return 0;
138   else if (r >= 1)     return 1;
139   else if (r <= ease)  return     ease * ease_fn (r / ease);
140   else if (r > 1-ease) return 1 - ease * ease_fn ((1 - r) / ease);
141   else                 return r;
142 }
143
144
145 static int
146 draw_cube_1 (ModeInfo *mi, GLfloat state, GLfloat color[4], Bool bottom_p)
147 {
148   int polys = 0;
149   int istate = state;
150   GLfloat r = state - istate;
151   GLfloat a = color[3];
152
153   r = ease_ratio (r);
154
155 # define COLORIZE(R) \
156       color[3] = a * R; \
157       glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color); \
158       glColor4fv (color)
159
160   if (bottom_p)
161     {
162       GLfloat r2 = (state < 0 ? 1 + state : 1);
163       COLORIZE(r2);
164       polys += draw_face (mi);          /* Bottom */
165     }
166
167   if (state >= 0)                       /* Left */
168     {
169       GLfloat r2 = (istate == 0 ? r : 1);
170       COLORIZE(r2);
171       glPushMatrix();
172       glTranslatef (-0.5, 0.5, 0);
173       glRotatef (-r2 * 90, 0, 1, 0);
174       glTranslatef (0.5, -0.5, 0);
175       polys += draw_face (mi);
176       glPopMatrix();
177     }
178
179   if (state >= 1)                       /* Back */
180     {
181       GLfloat r2 = (istate == 1 ? r : 1);
182       COLORIZE(r2);
183       glPushMatrix();
184       glTranslatef (-0.5, 0.5, 0);
185       glRotatef ( 90, 0, 1, 0);
186       glRotatef (-90, 0, 0, 1);
187       glRotatef (-r2 * 90, 0, 1, 0);
188       glTranslatef (0.5, -0.5, 0);
189       polys += draw_face (mi);
190       glPopMatrix();
191     }
192
193   if (state >= 2)                       /* Right */
194     {
195       GLfloat r2 = (istate == 2 ? r : 1);
196       COLORIZE(r2);
197       glPushMatrix();
198       glTranslatef (0.5, 0.5, 0);
199       glRotatef ( 90, 0, 1, 0);
200       glRotatef (-90, 0, 0, 1);
201       glRotatef (-90, 0, 1, 0);
202       glRotatef (-r2 * 90, 0, 1, 0);
203       glTranslatef (-0.5, -0.5, 0);
204       polys += draw_face (mi);
205       glPopMatrix();
206     }
207
208   if (state >= 3)                       /* Front */
209     {
210       GLfloat r2 = (istate == 3 ? r : 1);
211       COLORIZE(r2);
212       glPushMatrix();
213       glTranslatef (0.5, 0.5, 0);
214       glRotatef ( 90, 0, 1, 0);
215       glRotatef (-90, 0, 0, 1);
216       glRotatef (-180, 0, 1, 0);
217       glTranslatef (-1, 0, 0);
218       glRotatef (-r2 * 90, 0, 1, 0);
219       glTranslatef (0.5, -0.5, 0);
220       polys += draw_face (mi);
221       glPopMatrix();
222     }
223
224   if (state >= 4)                       /* Top */
225     {
226       GLfloat r2 = (istate == 4 ? r : 1);
227       COLORIZE(r2);
228       glPushMatrix();
229       glTranslatef (0, 0, 1);
230       glRotatef (-90, 0, 0, 1);
231       glTranslatef (0.5, 0.5, 0);
232       glRotatef (-90, 0, 1, 0);
233       glRotatef (r2 * 90, 0, 1, 0);
234       glTranslatef (-0.5, -0.5, 0);
235       polys += draw_face (mi);
236       glPopMatrix();
237     }
238
239   return polys;
240 }
241
242
243 static int
244 draw_cubes (ModeInfo *mi)
245 {
246   cube_configuration *bp = &bps[MI_SCREEN(mi)];
247   int polys = 0;
248   GLfloat z = bp->state / 6;
249   int i;
250   GLfloat c[4];
251   int c0 = bp->ccolor;
252   GLfloat alpha = opacity;
253
254   glPushMatrix();
255   glTranslatef (0, 0, -1.5 - z);
256
257   glTranslatef (0, 0, -bp->length);
258   for (i = bp->length-1; i >= 0; i--)
259     {
260       int c1 = c0 - i - 1;
261       if (c1 < 0) c1 += bp->ncolors;
262       c[0] = bp->colors[c1].red   / 65536.0;
263       c[1] = bp->colors[c1].green / 65536.0;
264       c[2] = bp->colors[c1].blue  / 65536.0;
265       c[3] = alpha;
266
267       glTranslatef (0, 0, 1);
268       polys += draw_cube_1 (mi, 5, c, i == bp->length - 1);
269     }
270
271   c[0] = bp->colors[c0].red   / 65536.0;
272   c[1] = bp->colors[c0].green / 65536.0;
273   c[2] = bp->colors[c0].blue  / 65536.0;
274   c[3] = alpha;
275   glTranslatef (0, 0, 1);
276   polys += draw_cube_1 (mi, bp->state, c, bp->length == 0);
277
278   glPopMatrix();
279
280   return polys;
281 }
282
283
284 /* Window management, etc
285  */
286 ENTRYPOINT void
287 reshape_cube (ModeInfo *mi, int width, int height)
288 {
289   GLfloat h = (GLfloat) height / (GLfloat) width;
290
291   glViewport (0, 0, (GLint) width, (GLint) height);
292
293   glMatrixMode(GL_PROJECTION);
294   glLoadIdentity();
295   gluPerspective (30.0, 1/h, 1.0, 100.0);
296
297   glMatrixMode(GL_MODELVIEW);
298   glLoadIdentity();
299   gluLookAt( 0.0, 0.0, 30.0,
300              0.0, 0.0, 0.0,
301              0.0, 1.0, 0.0);
302
303 # ifdef HAVE_MOBILE     /* Keep it the same relative size when rotated. */
304   {
305     int o = (int) current_device_rotation();
306     if (o != 0 && o != 180 && o != -180)
307       glScalef (1/h, 1/h, 1/h);
308   }
309 # endif
310
311   glClear(GL_COLOR_BUFFER_BIT);
312 }
313
314
315 ENTRYPOINT Bool
316 cube_handle_event (ModeInfo *mi, XEvent *event)
317 {
318   cube_configuration *bp = &bps[MI_SCREEN(mi)];
319
320   if (gltrackball_event_handler (event, bp->trackball,
321                                  MI_WIDTH (mi), MI_HEIGHT (mi),
322                                  &bp->button_down_p))
323     return True;
324   else if (event->xany.type == KeyPress)
325     {
326       KeySym keysym;
327       char c = 0;
328       XLookupString (&event->xkey, &c, 1, &keysym, 0);
329       if (c == ' ' || c == '\t')
330         {
331           bp->ncolors = 32;
332           make_smooth_colormap (0, 0, 0,
333                                 bp->colors, &bp->ncolors,
334                                 False, 0, False);
335           return True;
336         }
337     }
338
339
340   return False;
341 }
342
343
344 ENTRYPOINT void 
345 init_cube (ModeInfo *mi)
346 {
347   cube_configuration *bp;
348   int wire = MI_IS_WIREFRAME(mi);
349
350   MI_INIT (mi, bps, NULL);
351
352   bp = &bps[MI_SCREEN(mi)];
353
354   bp->glx_context = init_GL(mi);
355
356   reshape_cube (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
357
358   if (!wire)
359     {
360       glDisable (GL_LIGHTING);
361       glEnable(GL_DEPTH_TEST);
362       glShadeModel (GL_SMOOTH);
363       glEnable (GL_NORMALIZE);
364       glDisable (GL_CULL_FACE);
365       glEnable (GL_BLEND);
366       glDisable (GL_DEPTH_TEST);
367       glBlendFunc (GL_SRC_ALPHA, GL_ONE);
368     }
369
370   {
371     double wander_speed = 0.005;
372     bp->rot = make_rotator (0, 0, 0, 0,
373                             do_wander ? wander_speed : 0,
374                             False);
375     bp->trackball = gltrackball_init (True);
376   }
377
378   if (thickness > 0.5)
379     thickness = 0.5;
380   if (thickness < 0.001)
381     thickness = 0.001;
382
383   bp->ncolors = 32;
384   bp->colors = (XColor *) calloc(bp->ncolors, sizeof(XColor));
385   make_smooth_colormap (0, 0, 0,
386                         bp->colors, &bp->ncolors,
387                         False, 0, False);
388   bp->state = -1;
389 }
390
391
392 ENTRYPOINT void
393 draw_cube (ModeInfo *mi)
394 {
395   cube_configuration *bp = &bps[MI_SCREEN(mi)];
396   Display *dpy = MI_DISPLAY(mi);
397   Window window = MI_WINDOW(mi);
398
399   if (!bp->glx_context)
400     return;
401
402   glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
403   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
404
405   glPushMatrix ();
406
407   {
408     double x, y, z;
409     get_position (bp->rot, &x, &y, &z, !bp->button_down_p);
410     glTranslatef((x - 0.5) * 4,
411                  (y - 0.5) * 4,
412                  (z - 0.5) * 2);
413
414     gltrackball_rotate (bp->trackball);
415   }
416
417   mi->polygon_count = 0;
418
419   glScalef (6, 6, 6);
420   glRotatef (-45, 1, 0, 0);
421   glRotatef (20, 0, 0, 1);
422   glRotatef (bp->r, 0, 0, 1);
423
424   mi->polygon_count = draw_cubes (mi);
425   glPopMatrix ();
426
427   if (!bp->button_down_p)
428     {
429       int max = 6;
430       bp->state += speed * 0.015;
431       bp->r += speed * 0.05;
432       while (bp->r > 360)
433         bp->r -= 360;
434       while (bp->state > max)
435         {
436           bp->state -= max;
437           bp->length++;
438           bp->ccolor++;
439           if (bp->ccolor > bp->ncolors)
440             bp->ccolor = 0;
441         }
442
443       if (bp->length > 20)
444         bp->length = 20;
445     }
446
447   if (mi->fps_p) do_fps (mi);
448   glFinish();
449
450   glXSwapBuffers(dpy, window);
451 }
452
453 XSCREENSAVER_MODULE_2 ("CubeStack", cubestack, cube)
454
455 #endif /* USE_GL */