From http://www.jwz.org/xscreensaver/xscreensaver-5.36.tar.gz
[xscreensaver] / hacks / glx / hexstrut.c
1 /* hexstrut, 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                         "*count:        20          \n" \
16                         "*suppressRotationAnimation: True\n" \
17
18 # define refresh_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
336   glViewport (0, 0, (GLint) width, (GLint) height);
337
338   glMatrixMode(GL_PROJECTION);
339   glLoadIdentity();
340   gluPerspective (30.0, 1/h, 1.0, 100.0);
341
342   glMatrixMode(GL_MODELVIEW);
343   glLoadIdentity();
344   gluLookAt( 0.0, 0.0, 30.0,
345              0.0, 0.0, 0.0,
346              0.0, 1.0, 0.0);
347
348 # ifdef HAVE_MOBILE     /* Keep it the same relative size when rotated. */
349   {
350     int o = (int) current_device_rotation();
351     if (o != 0 && o != 180 && o != -180)
352       glScalef (1/h, 1/h, 1/h);
353   }
354 # endif
355
356   glClear(GL_COLOR_BUFFER_BIT);
357 }
358
359
360 ENTRYPOINT Bool
361 hexstrut_handle_event (ModeInfo *mi, XEvent *event)
362 {
363   hexstrut_configuration *bp = &bps[MI_SCREEN(mi)];
364
365   if (gltrackball_event_handler (event, bp->trackball,
366                                  MI_WIDTH (mi), MI_HEIGHT (mi),
367                                  &bp->button_down_p))
368     return True;
369   else if (event->xany.type == KeyPress)
370     {
371       KeySym keysym;
372       char c = 0;
373       XLookupString (&event->xkey, &c, 1, &keysym, 0);
374       if (c == ' ' || c == '\t')
375         {
376           bp->ncolors = 64;
377           make_smooth_colormap (0, 0, 0,
378                                 bp->colors, &bp->ncolors,
379                                 False, 0, False);
380           return True;
381         }
382     }
383
384   return False;
385 }
386
387
388 ENTRYPOINT void 
389 init_hexstrut (ModeInfo *mi)
390 {
391   hexstrut_configuration *bp;
392
393   if (!bps) {
394     bps = (hexstrut_configuration *)
395       calloc (MI_NUM_SCREENS(mi), sizeof (hexstrut_configuration));
396     if (!bps) {
397       fprintf(stderr, "%s: out of memory\n", progname);
398       exit(1);
399     }
400   }
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_start (bp->trackball, 500, 500, 1000, 1000);
425   gltrackball_track (bp->trackball,
426                      350 + (random() % 300),
427                      350 + (random() % 300),
428                      1000, 1000);
429
430
431   if (thickness < 0.05) thickness = 0.05;
432   if (thickness < 0.05) MI_IS_WIREFRAME(mi) = True;
433   if (thickness > 1.7) thickness = 1.7;
434   if (speed > 2) speed = 2;
435
436   bp->ncolors = 64;
437   bp->colors = (XColor *) calloc(bp->ncolors, sizeof(XColor));
438   make_smooth_colormap (0, 0, 0,
439                         bp->colors, &bp->ncolors,
440                         False, 0, False);
441
442   make_plane (mi);
443 }
444
445
446 ENTRYPOINT void
447 draw_hexstrut (ModeInfo *mi)
448 {
449   hexstrut_configuration *bp = &bps[MI_SCREEN(mi)];
450   Display *dpy = MI_DISPLAY(mi);
451   Window window = MI_WINDOW(mi);
452
453   if (!bp->glx_context)
454     return;
455
456   glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
457
458   glShadeModel(GL_SMOOTH);
459
460   glDisable(GL_DEPTH_TEST);
461   glEnable(GL_NORMALIZE);
462   glDisable(GL_CULL_FACE);
463
464   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
465
466   glPushMatrix ();
467
468   {
469     double x, y, z;
470     get_position (bp->rot, &x, &y, &z, !bp->button_down_p);
471     glTranslatef((x - 0.5) * 6,
472                  (y - 0.5) * 6,
473                  (z - 0.5) * 12);
474
475     gltrackball_rotate (bp->trackball);
476
477     get_rotation (bp->rot, &x, &y, &z, !bp->button_down_p);
478     glRotatef (z * 360, 0.0, 0.0, 1.0);
479   }
480
481   mi->polygon_count = 0;
482
483   glScalef (30, 30, 30);
484
485   if (! bp->button_down_p)
486     tick_triangles (mi);
487   draw_triangles (mi);
488
489   glPopMatrix ();
490
491   if (mi->fps_p) do_fps (mi);
492   glFinish();
493
494   glXSwapBuffers(dpy, window);
495 }
496
497
498 ENTRYPOINT void
499 release_hexstrut (ModeInfo *mi)
500 {
501   hexstrut_configuration *bp = &bps[MI_SCREEN(mi)];
502   while (bp->triangles)
503     {
504       triangle *t = bp->triangles->next;
505       free (bp->triangles);
506       bp->triangles = t;
507     }
508 }
509
510 XSCREENSAVER_MODULE ("Hexstrut", hexstrut)
511
512 #endif /* USE_GL */