From http://www.jwz.org/xscreensaver/xscreensaver-5.38.tar.gz
[xscreensaver] / hacks / glx / hexstrut.c
1 /* hexstrut, Copyright (c) 2016-2017 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                         "*count:        20          \n" \
16                         "*suppressRotationAnimation: True\n" \
17
18 # define release_hexstrut 0
19 #undef countof
20 #define countof(x) (sizeof((x))/sizeof((*x)))
21
22 #include "xlockmore.h"
23 #include "colors.h"
24 #include "normals.h"
25 #include "rotator.h"
26 #include "gltrackball.h"
27 #include <ctype.h>
28
29 #ifdef USE_GL /* whole file */
30
31
32 #define DEF_SPIN        "True"
33 #define DEF_WANDER      "True"
34 #define DEF_SPEED       "1.0"
35 #define DEF_THICKNESS   "0.2"
36
37 #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
38
39 typedef struct { double a, o; } LL;     /* latitude + longitude */
40
41 typedef struct triangle triangle;
42 struct triangle {
43   XYZ p[3];
44   triangle *next;
45   triangle *neighbors[6];
46   GLfloat orot, rot;
47   int delay, odelay;
48   int ccolor;
49 };
50
51 typedef struct {
52   GLXContext *glx_context;
53   rotator *rot;
54   trackball_state *trackball;
55   Bool button_down_p;
56
57   int count;
58   triangle *triangles;
59
60   int ncolors;
61   XColor *colors;
62
63 } hexstrut_configuration;
64
65 static hexstrut_configuration *bps = NULL;
66
67 static Bool do_spin;
68 static GLfloat speed;
69 static Bool do_wander;
70 static GLfloat thickness;
71
72 static XrmOptionDescRec opts[] = {
73   { "-spin",   ".spin",   XrmoptionNoArg, "True" },
74   { "+spin",   ".spin",   XrmoptionNoArg, "False" },
75   { "-speed",  ".speed",  XrmoptionSepArg, 0 },
76   { "-wander", ".wander", XrmoptionNoArg, "True" },
77   { "+wander", ".wander", XrmoptionNoArg, "False" },
78   { "-thickness", ".thickness", XrmoptionSepArg, 0 },
79 };
80
81 static argtype vars[] = {
82   {&do_spin,   "spin",   "Spin",   DEF_SPIN,   t_Bool},
83   {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
84   {&speed,     "speed",  "Speed",  DEF_SPEED,  t_Float},
85   {&thickness, "thickness", "Thickness", DEF_THICKNESS, t_Float},
86 };
87
88 ENTRYPOINT ModeSpecOpt hexstrut_opts = {countof(opts), opts, countof(vars), vars, NULL};
89
90
91
92 /* Add t1 to the neighbor list of t0. */
93 static void
94 link_neighbor (triangle *t0, triangle *t1)
95 {
96   int k;
97   if (t0 == t1)
98     return;
99   for (k = 0; k < countof(t0->neighbors); k++)
100     {
101       if (t0->neighbors[k] == t1 ||
102           t0->neighbors[k] == 0)
103         {
104           t0->neighbors[k] = t1;
105           return;
106         }
107     }
108   fprintf (stderr, "too many neighbors\n");
109   abort();
110 }
111
112
113 static void
114 make_plane (ModeInfo *mi)
115 {
116   hexstrut_configuration *bp = &bps[MI_SCREEN(mi)];
117   int n = MI_COUNT(mi) * 2;
118   GLfloat size = 2.0 / n;
119   int x, y;
120   GLfloat w = size;
121   GLfloat h = size * sqrt(3) / 2;
122   triangle **grid = (triangle **) calloc (n * n, sizeof(*grid));
123
124   for (y = 0; y < n; y++)
125     for (x = 0; x < n; x++)
126       {
127         triangle *t;
128
129         t = (triangle *) calloc (1, sizeof(*t));
130         t->p[0].x = (x - n/2) * w;
131         t->p[0].y = (y - n/2) * h;
132
133         if (y & 1)
134           t->p[0].x += w / 2;
135
136         t->p[1].x = t->p[0].x - w/2;
137         t->p[2].x = t->p[0].x + w/2;
138         t->p[1].y = t->p[0].y + h;
139         t->p[2].y = t->p[0].y + h;
140
141         if (x > 0)
142           {
143             triangle *t2 = grid[y * n + (x-1)];
144             link_neighbor (t, t2);
145             link_neighbor (t2, t);
146           }
147         if (y > 0)
148           {
149             triangle *t2 = grid[(y-1) * n + x];
150             link_neighbor (t, t2);
151             link_neighbor (t2, t);
152
153             if (x < n-1)
154               {
155                 t2 = grid[(y-1) * n + (x+1)];
156                 link_neighbor (t, t2);
157                 link_neighbor (t2, t);
158               }
159           }
160
161         t->next = bp->triangles;
162         bp->triangles = t;
163         grid[y * n + x] = t;
164         bp->count++;
165       }
166
167   free (grid);
168 }
169
170
171 static void
172 tick_triangles (ModeInfo *mi)
173 {
174   hexstrut_configuration *bp = &bps[MI_SCREEN(mi)];
175   triangle *t;
176   GLfloat step = 0.01 + (0.04 * speed);
177
178   if (! (random() % 80))
179     {
180       int n = random() % bp->count;
181       int i;
182       for (i = 0, t = bp->triangles; t && i < n; t = t->next, i++)
183         ;
184       if (! t->rot)
185         {
186           t->rot += step * ((random() & 1) ? 1 : -1);
187           t->odelay = t->delay = 4;
188         }
189     }
190
191   for (t = bp->triangles; t; t = t->next)
192     {
193       /* If this triangle is rotating, continue until done. */
194       if (t->rot)
195         {
196           t->rot += step * (t->rot > 0 ? 1 : -1);
197
198           t->ccolor++;
199           if (t->ccolor >= bp->ncolors)
200             t->ccolor = 0;
201
202           if (t->rot > 1 || t->rot < -1)
203             {
204               t->orot += (t->rot > 1 ? 1 : -1);
205               t->rot = 0;
206             }
207         }
208
209       /* If this triangle's propagation delay hasn't hit zero, decrement it.
210          When it does, start its neighbors rotating.
211        */
212       if (t->delay)
213         {
214           int i;
215           t->delay--;
216           if (t->delay == 0)
217             for (i = 0; i < countof(t->neighbors); i++)
218               {
219                 if (t->neighbors[i] &&
220                     t->neighbors[i]->rot == 0)
221                   {
222                     t->neighbors[i]->rot += step * (t->rot > 0 ? 1 : -1);
223                     t->neighbors[i]->delay = 
224                       t->neighbors[i]->odelay = t->odelay;
225                   }
226               }
227         }
228     }
229 }
230
231
232 static void
233 draw_triangles (ModeInfo *mi)
234 {
235   hexstrut_configuration *bp = &bps[MI_SCREEN(mi)];
236   int wire = MI_IS_WIREFRAME(mi);
237   triangle *t;
238   GLfloat length = sqrt(3) / 3;
239   GLfloat t2 = length * thickness / 2;
240   GLfloat scale;
241
242   {
243     triangle *t = bp->triangles;
244     GLfloat X = t->p[0].x - t->p[1].x;
245     GLfloat Y = t->p[0].y - t->p[1].y;
246     GLfloat Z = t->p[0].z - t->p[1].z;
247     scale = sqrt(X*X + Y*Y + Z*Z);
248   }
249
250   glFrontFace (GL_CCW);
251
252   glBegin (wire ? GL_LINES : GL_QUADS);
253
254   glNormal3f (0, 0, 1);
255   for (t = bp->triangles; t; t = t->next)
256     {
257       int i;
258       XYZ c;
259       GLfloat color[4];
260
261       GLfloat angle = (M_PI * 2 / 3) * t->rot;
262       GLfloat cr = cos(angle), sr = sin(angle);
263
264       c.x = (t->p[0].x + t->p[1].x + t->p[2].x) / 3;
265       c.y = (t->p[0].y + t->p[1].y + t->p[2].y) / 3;
266       c.z = (t->p[0].z + t->p[1].z + t->p[2].z) / 3;
267
268       /* Actually we don't need normals at all, since no lighting.
269       do_normal (t->p[0].x, t->p[0].y, t->p[0].z,
270                  t->p[1].x, t->p[1].y, t->p[1].z,
271                  t->p[2].x, t->p[2].y, t->p[2].z);
272       */
273
274       color[0] = bp->colors[t->ccolor].red   / 65535.0;
275       color[1] = bp->colors[t->ccolor].green / 65535.0;
276       color[2] = bp->colors[t->ccolor].blue  / 65535.0;
277       color[3] = 1;
278
279       /* Brighter */
280       color[0] = color[0] * 0.75 + 0.25;
281       color[1] = color[1] * 0.75 + 0.25;
282       color[2] = color[2] * 0.75 + 0.25;
283
284       glColor4fv (color);
285       glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
286
287       for (i = 0; i < 3; i++)
288         {
289           /* Orient to direction of corner. */
290           GLfloat x = t->p[i].x - c.x;
291           GLfloat y = t->p[i].y - c.y;
292           GLfloat z = t->p[i].z - c.z;
293
294           GLfloat smc = sr * y - cr * x;
295           GLfloat spc = cr * y + sr * x;
296
297           GLfloat st2 = t2 * scale / sqrt(x*x + y*y);
298           GLfloat slength = length * scale / sqrt(x*x + y*y + z*z);
299
300           GLfloat xt2 = spc * st2;
301           GLfloat yt2 = smc * st2;
302           GLfloat xlength = c.x - slength * smc;
303           GLfloat ylength = c.y + slength * spc;
304           GLfloat zlength = c.z + slength * z;
305
306           if (! wire)
307             glVertex3f (c.x - xt2, c.y - yt2, c.z);
308
309           glVertex3f (c.x + xt2, c.y + yt2, c.z);
310           if (wire)
311             glVertex3f (xlength + xt2, ylength + yt2, zlength);
312
313           if (! wire)
314             glVertex3f (xlength + xt2, ylength + yt2, zlength);
315
316           glVertex3f (xlength - xt2, ylength - yt2, zlength);
317
318           if (wire)
319             glVertex3f (c.x - xt2, c.y - yt2, c.z);
320
321           mi->polygon_count++;
322         }
323     }
324
325   glEnd();
326 }
327
328
329 /* Window management, etc
330  */
331 ENTRYPOINT void
332 reshape_hexstrut (ModeInfo *mi, int width, int height)
333 {
334   GLfloat h = (GLfloat) height / (GLfloat) width;
335   int y = 0;
336
337   if (width > height * 3) {   /* tiny window: show middle */
338     height = width * 9/16;
339     y = -height/2;
340     h = height / (GLfloat) width;
341   }
342
343   glViewport (0, y, (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 hexstrut_handle_event (ModeInfo *mi, XEvent *event)
369 {
370   hexstrut_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           bp->ncolors = 64;
384           make_smooth_colormap (0, 0, 0,
385                                 bp->colors, &bp->ncolors,
386                                 False, 0, False);
387           return True;
388         }
389     }
390
391   return False;
392 }
393
394
395 ENTRYPOINT void 
396 init_hexstrut (ModeInfo *mi)
397 {
398   hexstrut_configuration *bp;
399
400   MI_INIT (mi, bps);
401
402   bp = &bps[MI_SCREEN(mi)];
403
404   bp->glx_context = init_GL(mi);
405
406   reshape_hexstrut (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
407
408   {
409     double spin_speed   = 0.002;
410     double wander_speed = 0.003;
411     double spin_accel   = 1.0;
412
413     bp->rot = make_rotator (do_spin ? spin_speed : 0,
414                             do_spin ? spin_speed : 0,
415                             do_spin ? spin_speed : 0,
416                             spin_accel,
417                             do_wander ? wander_speed : 0,
418                             False);
419     bp->trackball = gltrackball_init (True);
420   }
421
422
423   /* Let's tilt the scene a little. */
424   gltrackball_reset (bp->trackball,
425                      -0.4 + frand(0.8),
426                      -0.4 + frand(0.8));
427
428   if (thickness < 0.05) thickness = 0.05;
429   if (thickness < 0.05) MI_IS_WIREFRAME(mi) = True;
430   if (thickness > 1.7) thickness = 1.7;
431   if (speed > 2) speed = 2;
432
433   bp->ncolors = 64;
434   bp->colors = (XColor *) calloc(bp->ncolors, sizeof(XColor));
435   make_smooth_colormap (0, 0, 0,
436                         bp->colors, &bp->ncolors,
437                         False, 0, False);
438
439   make_plane (mi);
440 }
441
442
443 ENTRYPOINT void
444 draw_hexstrut (ModeInfo *mi)
445 {
446   hexstrut_configuration *bp = &bps[MI_SCREEN(mi)];
447   Display *dpy = MI_DISPLAY(mi);
448   Window window = MI_WINDOW(mi);
449
450   if (!bp->glx_context)
451     return;
452
453   glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
454
455   glShadeModel(GL_SMOOTH);
456
457   glDisable(GL_DEPTH_TEST);
458   glEnable(GL_NORMALIZE);
459   glDisable(GL_CULL_FACE);
460
461   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
462
463   glPushMatrix ();
464
465   {
466     double x, y, z;
467     get_position (bp->rot, &x, &y, &z, !bp->button_down_p);
468     glTranslatef((x - 0.5) * 6,
469                  (y - 0.5) * 6,
470                  (z - 0.5) * 12);
471
472     gltrackball_rotate (bp->trackball);
473
474     get_rotation (bp->rot, &x, &y, &z, !bp->button_down_p);
475     glRotatef (z * 360, 0.0, 0.0, 1.0);
476   }
477
478   mi->polygon_count = 0;
479
480   glScalef (30, 30, 30);
481
482   if (! bp->button_down_p)
483     tick_triangles (mi);
484   draw_triangles (mi);
485
486   glPopMatrix ();
487
488   if (mi->fps_p) do_fps (mi);
489   glFinish();
490
491   glXSwapBuffers(dpy, window);
492 }
493
494
495 ENTRYPOINT void
496 free_hexstrut (ModeInfo *mi)
497 {
498   hexstrut_configuration *bp = &bps[MI_SCREEN(mi)];
499   while (bp->triangles)
500     {
501       triangle *t = bp->triangles->next;
502       free (bp->triangles);
503       bp->triangles = t;
504     }
505 }
506
507 XSCREENSAVER_MODULE ("Hexstrut", hexstrut)
508
509 #endif /* USE_GL */