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