From http://www.jwz.org/xscreensaver/xscreensaver-5.37.tar.gz
[xscreensaver] / hacks / glx / spheremonics.c
1 /* xscreensaver, Copyright (c) 2002-2014 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  * Algorithm by Paul Bourke <pbourke@swin.edu.au>
12  * http://astronomy.swin.edu.au/~pbourke/geometry/sphericalh/
13  * Screensaver veneer and parameter selection by jwz.
14  *
15  *  Paul says:
16  *
17  * These closed objects are commonly called spherical harmonics,
18  * although they are only remotely related to the mathematical
19  * definition found in the solution to certain wave functions, most
20  * notable the eigenfunctions of angular momentum operators.
21  *
22  * The formula is quite simple: the form used here is based upon
23  * spherical (polar) coordinates (radius, theta, phi).
24  *
25  *    r = sin(m0 phi)   ^ m1 + 
26  *        cos(m2 phi)   ^ m3 + 
27  *        sin(m4 theta) ^ m5 + 
28  *        cos(m6 theta) ^ m7 
29  *
30  * Where phi ranges from 0 to pi (lines of latitude), and theta ranges
31  * from 0 to 2 pi (lines of longitude), and r is the radius.  The
32  * parameters m0, m1, m2, m3, m4, m5, m6, and m7 are all integers
33  * greater than or equal to 0.
34  *
35  * As the degree increases, the objects become increasingly "pointed"
36  * and a large number of polygons are required to represent the surface
37  * faithfully.
38  *
39  * jwz adds:
40  * 
41  * The eight parameters live in the `cc->m' array.
42  * Each time we permute the image, we alter *one* of those eight parameters.
43  * Each time we alter a parameter, we move it in the same direction (either
44  * toward larger or smaller values) in the range [0, 3].
45  *
46  * By altering only one parameter at a time, and only by small amounts,
47  * we tend to produce successive objects that are pretty similar to each
48  * other, so you can see a progression.
49  *
50  * It'd be nice if they were even closer together, so that it looked more
51  * like a morph, but, well, that's not how it works.
52  *
53  * There tends to be a dark stripe in the colormaps.  I don't know why.
54  * Perhaps utils/colors.c is at fault?
55  *
56  * Note that this equation sometimes generates faces that are inside out:
57  *     -parameters 01210111
58  * To make this work, we need to render back-faces with two-sided lighting:
59  * figuring out how to correct the winding and normals on those inside out
60  * surfaces would be too hard.
61  */
62
63 #define DEFAULTS "*delay:       30000       \n" \
64                  "*showFPS:     False       \n" \
65                  "*wireframe:   False       \n" \
66                  "*suppressRotationAnimation: True\n" \
67          "*labelfont:   -*-helvetica-medium-r-normal-*-*-180-*-*-*-*-*-*\n"
68
69 # define refresh_spheremonics 0
70 # define release_spheremonics 0
71 #undef countof
72 #define countof(x) (sizeof((x))/sizeof((*x)))
73
74 #include "xlockmore.h"
75 #include "texfont.h"
76 #include "normals.h"
77 #include "colors.h"
78 #include "rotator.h"
79 #include "gltrackball.h"
80 #include <ctype.h>
81
82 #ifdef USE_GL /* whole file */
83
84 #define DEF_DURATION    "100"
85 #define DEF_SPIN        "XYZ"
86 #define DEF_WANDER      "False"
87 #define DEF_RESOLUTION  "64"
88 #define DEF_BBOX        "False"
89 #define DEF_GRID        "True"
90 #define DEF_SMOOTH      "True"
91 #define DEF_PARMS       "(default)"
92
93 typedef struct {
94   GLXContext *glx_context;
95   rotator *rot;
96   trackball_state *trackball;
97   Bool button_down_p;
98
99   GLuint dlist, dlist2;
100   GLfloat scale;
101   XYZ bbox[2];
102
103   int resolution;
104   int ncolors;
105   XColor *colors;
106
107   int m[8];
108   int dm[8];
109   int m_max;
110
111   int tracer;
112   int mesher;
113   int polys1, polys2;  /* polygon counts */
114
115   texture_font_data *font_data;
116
117   int change_tick;
118   int done_once;
119
120 } spheremonics_configuration;
121
122 static spheremonics_configuration *ccs = NULL;
123
124 static char *do_spin;
125 static Bool do_wander;
126 static Bool do_bbox;
127 static Bool do_grid;
128 static int smooth_p;
129 static char *static_parms;
130 static int res;
131 static int duration;
132
133 static XrmOptionDescRec opts[] = {
134   { "-spin",   ".spin",   XrmoptionSepArg, 0 },
135   { "+spin",   ".spin",   XrmoptionNoArg, "" },
136   { "-wander", ".wander", XrmoptionNoArg, "True" },
137   { "+wander", ".wander", XrmoptionNoArg, "False" },
138   { "-resolution", ".resolution", XrmoptionSepArg, 0 },
139   { "-duration",   ".duration",   XrmoptionSepArg, 0 },
140   { "-bbox",   ".bbox",  XrmoptionNoArg, "True" },
141   { "+bbox",   ".bbox",  XrmoptionNoArg, "False" },
142   { "-grid",   ".grid",  XrmoptionNoArg, "True" },
143   { "+grid",   ".grid",  XrmoptionNoArg, "False" },
144   {"-smooth",  ".smooth", XrmoptionNoArg, "True" },
145   {"+smooth",  ".smooth", XrmoptionNoArg, "False" },
146   { "-parameters", ".parameters", XrmoptionSepArg, 0 },
147 };
148
149 static argtype vars[] = {
150   {&do_spin,      "spin",       "Spin",       DEF_SPIN,       t_String},
151   {&do_wander,    "wander",     "Wander",     DEF_WANDER,     t_Bool},
152   {&res,          "resolution", "Resolution", DEF_RESOLUTION, t_Int},
153   {&duration,     "duration",   "Duration",   DEF_DURATION,   t_Int},
154   {&do_bbox,      "bbox",       "BBox",       DEF_BBOX,       t_Bool},
155   {&do_grid,      "grid",       "Grid",       DEF_GRID,       t_Bool},
156   {&smooth_p,     "smooth",     "Smooth",     DEF_SMOOTH,     t_Bool},
157   {&static_parms, "parameters", "Parameters", DEF_PARMS,      t_String},
158 };
159
160 ENTRYPOINT ModeSpecOpt spheremonics_opts = {countof(opts), opts, countof(vars), vars, NULL};
161
162
163 /* Window management, etc
164  */
165 ENTRYPOINT void
166 reshape_spheremonics (ModeInfo *mi, int width, int height)
167 {
168   GLfloat h = (GLfloat) height / (GLfloat) width;
169
170   glViewport (0, 0, (GLint) width, (GLint) height);
171
172   glMatrixMode(GL_PROJECTION);
173   glLoadIdentity();
174   gluPerspective (30.0, 1/h, 1.0, 100.0);
175
176   glMatrixMode(GL_MODELVIEW);
177   glLoadIdentity();
178   gluLookAt( 0.0, 0.0, 30.0,
179              0.0, 0.0, 0.0,
180              0.0, 1.0, 0.0);
181
182 # ifdef HAVE_MOBILE     /* Keep it the same relative size when rotated. */
183   {
184     int o = (int) current_device_rotation();
185     if (o != 0 && o != 180 && o != -180)
186       glScalef (1/h, 1/h, 1/h);
187   }
188
189 # endif
190   glClear(GL_COLOR_BUFFER_BIT);
191 }
192
193
194 static void
195 gl_init (ModeInfo *mi)
196 {
197 /*  spheremonics_configuration *cc = &ccs[MI_SCREEN(mi)]; */
198   int wire = MI_IS_WIREFRAME(mi);
199
200   static const GLfloat pos[4] = {5.0, 5.0, 10.0, 1.0};
201
202   glEnable(GL_NORMALIZE);
203
204   if (!wire)
205     {
206       glLightfv(GL_LIGHT0, GL_POSITION, pos);
207       glEnable(GL_LIGHTING);
208       glEnable(GL_LIGHT0);
209       glEnable(GL_DEPTH_TEST);
210
211       /* With objects that have proper winding and normals set up on all
212          their faces, one can cull back-faces; however, these equations
213          generate objects that are sometimes "inside out", and determining
214          whether a facet has been inverted like that is really hard.
215          So we render both front and back faces, at a probable performance
216          penalty on non-accelerated systems.
217
218          When rendering back faces, we also need to do two-sided lighting,
219          or the fact that the normals are flipped gives us too-dark surfaces
220          on the inside-out surfaces.
221
222          This isn't generally something you'd want, because you end up
223          with half the lighting dynamic range (kind of.)  So if you had
224          a sphere with correctly pointing normals, and a single light
225          source, it would be illuminated from two sides.  In this case,
226          though, it saves us from a difficult and time consuming
227          inside/outside test.  And we don't really care about a precise
228          lighting effect.
229        */
230       glDisable(GL_CULL_FACE);
231       glLightModeli (GL_LIGHT_MODEL_TWO_SIDE, True);
232     }
233
234   if (smooth_p) 
235     {
236       glEnable (GL_LINE_SMOOTH);
237       glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
238       glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 
239       glEnable (GL_BLEND);
240     }
241 }
242
243
244 \f
245 /* generate the object */
246
247 static XYZ
248 sphere_eval (double theta, double phi, int *m)
249 {
250   double r = 0;
251   XYZ p;
252
253   r += pow (sin(m[0] * phi),  (double)m[1]);
254   r += pow (cos(m[2] * phi),  (double)m[3]);
255   r += pow (sin(m[4] * theta),(double)m[5]);
256   r += pow (cos(m[6] * theta),(double)m[7]);
257
258   p.x = r * sin(phi) * cos(theta);
259   p.y = r * cos(phi);
260   p.z = r * sin(phi) * sin(theta);
261
262   return (p);
263 }
264
265
266 static void
267 do_color (int i, XColor *colors)
268 {
269   GLfloat c[4];
270   c[0] = colors[i].red   / 65535.0;
271   c[1] = colors[i].green / 65535.0;
272   c[2] = colors[i].blue  / 65535.0;
273   c[3] = 1.0;
274   glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, c);
275   glColor3f (c[0], c[1], c[2]);
276 }
277
278
279 static void
280 draw_circle (ModeInfo *mi, Bool teeth_p)
281 {
282   GLfloat th;
283   int tick = 0;
284   GLfloat x, y;
285   GLfloat step = (M_PI / 180);
286
287   glBegin(GL_LINE_LOOP);
288   for (th = 0; th < M_PI*2; th += step*5)
289     {
290       GLfloat r1 = 0.5;
291       x = cos (th);
292       y = sin (th);
293       glVertex3f(x*r1, y*r1,  0);
294     }
295   glEnd();
296
297   if (!teeth_p) return;
298
299   glBegin(GL_LINES);
300   for (th = 0; th < M_PI*2; th += step)
301     {
302       GLfloat r1 = 0.5;
303       GLfloat r2 = r1 - 0.01;
304       if (! (tick % 10))
305         r2 -= 0.02;
306       else if (! (tick % 5))
307         r2 -= 0.01;
308       tick++;
309
310       x = cos (th);
311       y = sin (th);
312       glVertex3f(x*r1, y*r1,  0);
313       glVertex3f(x*r2, y*r2,  0);
314     }
315   glEnd();
316 }
317
318
319 static void
320 draw_bounding_box (ModeInfo *mi)
321 {
322   /* spheremonics_configuration *cc = &ccs[MI_SCREEN(mi)]; */
323
324   static const GLfloat c1[4] = { 0.2, 0.2, 0.6, 1.0 };
325   static const GLfloat c2[4] = { 1.0, 0.0, 0.0, 1.0 };
326   int wire = MI_IS_WIREFRAME(mi);
327
328   GLfloat x1,y1,z1,x2,y2,z2;
329
330 # if 0
331   x1 = cc->bbox[0].x;
332   y1 = cc->bbox[0].y;
333   z1 = cc->bbox[0].z;
334   x2 = cc->bbox[1].x;
335   y2 = cc->bbox[1].y;
336   z2 = cc->bbox[1].z;
337 # else
338   x1 = y1 = z1 = -0.5;
339   x2 = y2 = z2 =  0.5;
340 # endif
341
342   if (do_bbox && !wire)
343     {
344       glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, c1);
345       glFrontFace(GL_CCW);
346       glEnable(GL_CULL_FACE);
347
348       glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
349       glNormal3f(0, 1, 0);
350       glVertex3f(x1, y1, z1); glVertex3f(x1, y1, z2);
351       glVertex3f(x2, y1, z2); glVertex3f(x2, y1, z1);
352       glEnd();
353       glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
354       glNormal3f(0, -1, 0);
355       glVertex3f(x2, y2, z1); glVertex3f(x2, y2, z2);
356       glVertex3f(x1, y2, z2); glVertex3f(x1, y2, z1);
357       glEnd();
358       glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
359       glNormal3f(0, 0, 1);
360       glVertex3f(x1, y1, z1); glVertex3f(x2, y1, z1);
361       glVertex3f(x2, y2, z1); glVertex3f(x1, y2, z1);
362       glEnd();
363       glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
364       glNormal3f(0, 0, -1);
365       glVertex3f(x1, y2, z2); glVertex3f(x2, y2, z2);
366       glVertex3f(x2, y1, z2); glVertex3f(x1, y1, z2);
367       glEnd();
368       glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
369       glNormal3f(1, 0, 0);
370       glVertex3f(x1, y2, z1); glVertex3f(x1, y2, z2);
371       glVertex3f(x1, y1, z2); glVertex3f(x1, y1, z1);
372       glEnd();
373       glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
374       glNormal3f(-1, 0, 0);
375       glVertex3f(x2, y1, z1); glVertex3f(x2, y1, z2);
376       glVertex3f(x2, y2, z2); glVertex3f(x2, y2, z1);
377       glEnd();
378       glDisable(GL_CULL_FACE);
379     }
380
381   if (do_grid)
382     {
383       glDisable (GL_LIGHTING);
384       glColor3f (c2[0], c2[1], c2[2]);
385       glPushMatrix();
386       glBegin(GL_LINES);
387       glVertex3f(0, -0.66, 0);
388       glVertex3f(0,  0.66, 0); 
389       glEnd();
390       draw_circle (mi, True);
391       glRotatef(90, 1, 0, 0);
392       draw_circle (mi, True);
393       glRotatef(90, 0, 1, 0);
394       draw_circle (mi, True);
395       glPopMatrix();
396     }
397   else
398     {
399 #if 0
400       glBegin(GL_LINES);
401       if (x1 > 0) x1 = 0; if (x2 < 0) x2 = 0;
402       if (y1 > 0) y1 = 0; if (y2 < 0) y2 = 0;
403       if (z1 > 0) z1 = 0; if (z2 < 0) z2 = 0;
404       glVertex3f(x1, 0,  0);  glVertex3f(x2, 0,  0); 
405       glVertex3f(0 , y1, 0);  glVertex3f(0,  y2, 0); 
406       glVertex3f(0,  0,  z1); glVertex3f(0,  0,  z2); 
407       glEnd();
408 #endif
409     }
410 }
411
412
413 static void
414 do_tracer (ModeInfo *mi)
415 {
416   spheremonics_configuration *cc = &ccs[MI_SCREEN(mi)];
417
418   if (cc->tracer == -1 &&
419       cc->mesher == -1 &&
420       !(random() % (duration * 4)))
421     {
422       if (random() & 1)
423         cc->tracer = ((random() & 1) ? 0 : 180);
424       else
425         cc->mesher = ((random() % ((duration / 3) + 1)) +
426                       (random() % ((duration / 3) + 1)));
427     }
428
429   if (cc->tracer >= 0)
430     {
431       int d = (90 - cc->tracer);
432       GLfloat th = d * (M_PI / 180);
433       GLfloat x = cos (th);
434       GLfloat y = sin (th);
435       GLfloat s = 1.5 / cc->scale;
436
437       if (s > 0.001)
438         {
439           static const GLfloat c[4] = { 0.6, 0.5, 1.0, 1.0 };
440
441           glDisable (GL_LIGHTING);
442
443           glPushMatrix();
444           glRotatef (90, 1, 0, 0);
445           glTranslatef (0, 0, y*s/2);
446           s *= x;
447           glScalef(s, s, s);
448           glColor3f (c[0], c[1], c[2]);
449           draw_circle (mi, False);
450           glPopMatrix();
451
452           if (! MI_IS_WIREFRAME(mi)) glEnable (GL_LIGHTING);
453         }
454
455       cc->tracer += 5;
456       if (cc->tracer == 180 || cc->tracer == 360)
457         cc->tracer = -1;
458     }
459 }
460
461
462 static int
463 unit_spheremonics (ModeInfo *mi,
464                    int resolution, Bool wire, int *m, XColor *colors)
465 {
466   spheremonics_configuration *cc = &ccs[MI_SCREEN(mi)];
467   int polys = 0;
468   int i, j;
469   double du, dv;
470   XYZ q[4];
471   XYZ n[4];
472   int res = (wire == 2
473              ? resolution / 2
474              : resolution);
475
476   cc->bbox[0].x = cc->bbox[0].y = cc->bbox[0].z = 0;
477   cc->bbox[1].x = cc->bbox[1].y = cc->bbox[1].z = 0;
478
479   du = (M_PI+M_PI) / (double)res; /* Theta */
480   dv = M_PI        / (double)res; /* Phi   */
481
482   if (wire)
483     glColor3f (1, 1, 1);
484
485   glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
486
487   for (i = 0; i < res; i++) {
488     double u = i * du;
489     for (j = 0; j < res; j++) {
490       double v = j * dv;
491       q[0] = sphere_eval (u, v, m);
492       n[0] = calc_normal(q[0],
493                          sphere_eval (u+du/10, v, m),
494                          sphere_eval (u, v+dv/10, m));
495       glNormal3f(n[0].x,n[0].y,n[0].z);
496       if (!wire) do_color (i, colors);
497       glVertex3f(q[0].x,q[0].y,q[0].z);
498
499       q[1] = sphere_eval (u+du, v, m);
500       n[1] = calc_normal(q[1],
501                          sphere_eval (u+du+du/10, v, m),
502                          sphere_eval (u+du, v+dv/10, m));
503       glNormal3f(n[1].x,n[1].y,n[1].z);
504       if (!wire) do_color ((i+1)%res, colors);
505       glVertex3f(q[1].x,q[1].y,q[1].z);
506
507       q[2] = sphere_eval (u+du, v+dv, m);
508       n[2] = calc_normal(q[2],
509                          sphere_eval (u+du+du/10, v+dv, m),
510                          sphere_eval (u+du, v+dv+dv/10, m));
511       glNormal3f(n[2].x,n[2].y,n[2].z);
512       if (!wire) do_color ((i+1)%res, colors);
513       glVertex3f(q[2].x,q[2].y,q[2].z);
514
515       q[3] = sphere_eval (u,v+dv, m);
516       n[3] = calc_normal(q[3],
517                          sphere_eval (u+du/10, v+dv, m),
518                          sphere_eval (u, v+dv+dv/10, m));
519       glNormal3f(n[3].x,n[3].y,n[3].z);
520       if (!wire) do_color (i, colors);
521       glVertex3f(q[3].x,q[3].y,q[3].z);
522
523       polys++;
524
525 # define CHECK_BBOX(N) \
526          if (q[(N)].x < cc->bbox[0].x) cc->bbox[0].x = q[(N)].x; \
527          if (q[(N)].y < cc->bbox[0].y) cc->bbox[0].y = q[(N)].y; \
528          if (q[(N)].z < cc->bbox[0].z) cc->bbox[0].z = q[(N)].z; \
529          if (q[(N)].x > cc->bbox[1].x) cc->bbox[1].x = q[(N)].x; \
530          if (q[(N)].y > cc->bbox[1].y) cc->bbox[1].y = q[(N)].y; \
531          if (q[(N)].z > cc->bbox[1].z) cc->bbox[1].z = q[(N)].z
532
533       CHECK_BBOX(0);
534       CHECK_BBOX(1);
535       CHECK_BBOX(2);
536       CHECK_BBOX(3);
537 # undef CHECK_BBOX
538     }
539   }
540   glEnd();
541
542   {
543     GLfloat w = cc->bbox[1].x - cc->bbox[0].x;
544     GLfloat h = cc->bbox[1].y - cc->bbox[0].y;
545     GLfloat d = cc->bbox[1].z - cc->bbox[0].z;
546     GLfloat wh = (w > h ? w : h);
547     GLfloat hd = (h > d ? h : d);
548     GLfloat scale = (wh > hd ? wh : hd);
549
550     cc->scale = 1/scale;
551
552     if (wire < 2 && (do_bbox || do_grid))
553       {
554         GLfloat s = scale * 1.5;
555         glPushMatrix();
556         glScalef(s, s, s);
557         draw_bounding_box (mi);
558         glPopMatrix();
559       }
560   }
561   return polys;
562 }
563
564
565 static void
566 init_colors (ModeInfo *mi)
567 {
568   spheremonics_configuration *cc = &ccs[MI_SCREEN(mi)];
569   int i;
570   cc->ncolors = cc->resolution;
571   cc->colors = (XColor *) calloc(cc->ncolors, sizeof(XColor));
572   make_smooth_colormap (0, 0, 0,
573                         cc->colors, &cc->ncolors, 
574                         False, 0, False);
575
576   /* brighter colors, please... */
577   for (i = 0; i < cc->ncolors; i++)
578     {
579       cc->colors[i].red   = (cc->colors[i].red   / 2) + 32767;
580       cc->colors[i].green = (cc->colors[i].green / 2) + 32767;
581       cc->colors[i].blue  = (cc->colors[i].blue  / 2) + 32767;
582     }
583 }
584
585
586 /* Pick one of the parameters to the function and tweak it up or down.
587  */
588 static void
589 tweak_parameters (ModeInfo *mi)
590 {
591   spheremonics_configuration *cc = &ccs[MI_SCREEN(mi)];
592
593   /* If the -parameters command line option was specified, just use that
594      all the time.
595    */
596   if (static_parms &&
597       *static_parms &&
598       !!strcasecmp (static_parms, "(default)"))
599     {
600       unsigned long n;
601       char dummy;
602       if (8 == sscanf (static_parms, "%d %d %d %d %d %d %d %d %c",
603                        &cc->m[0], &cc->m[1], &cc->m[2], &cc->m[3],
604                        &cc->m[4], &cc->m[5], &cc->m[6], &cc->m[7],
605                        &dummy))
606         return;
607       else if (strlen (static_parms) == 8 &&
608                1 == sscanf (static_parms, "%lu %c", &n, &dummy))
609         {
610           const char *s = static_parms;
611           int i = 0;
612           while (*s)
613             cc->m[i++] = (*s++)-'0';
614           return;
615         }
616       fprintf (stderr,
617                "%s: -parameters must be a string of 8 ints (not \"%s\")\n",
618                progname, static_parms);
619       exit (1);
620     }
621
622   static_parms = 0;
623
624
625 # define SHIFT(N) do { \
626     int n = (N); \
627     cc->m[n] += cc->dm[n]; \
628     if (cc->m[n] <= 0) \
629       cc->m[n] = 0, cc->dm[n] = -cc->dm[n]; \
630     else if (cc->m[n] >= cc->m_max) \
631       cc->m[n] = cc->m_max, cc->dm[n] = -cc->dm[n]; \
632   } while(0)
633
634 /*    else if (cc->m[n] >= cc->m_max/2 && (! (random() % 3))) \
635       cc->m[n] = cc->m_max/2, cc->dm[n] = -cc->dm[n]; \
636 */
637
638   switch(random() % 8)
639     {
640     case 0: SHIFT(0); break;
641     case 1: SHIFT(1); break;
642     case 2: SHIFT(2); break;
643     case 3: SHIFT(3); break;
644     case 4: SHIFT(4); break;
645     case 5: SHIFT(5); break;
646     case 6: SHIFT(6); break;
647     case 7: SHIFT(7); break;
648     default: abort(); break;
649     }
650 # undef SHIFT
651
652 #if 0
653     printf ("%s: state: %d %d %d %d %d %d %d %d\n",
654             progname,
655             cc->m[0], cc->m[1], cc->m[2], cc->m[3],
656             cc->m[4], cc->m[5], cc->m[6], cc->m[7]);
657 #endif
658
659 }
660
661
662 static void
663 generate_spheremonics (ModeInfo *mi)
664 {
665   spheremonics_configuration *cc = &ccs[MI_SCREEN(mi)];
666   int wire = MI_IS_WIREFRAME(mi);
667
668   tweak_parameters (mi);
669
670   if (!cc->done_once || (0 == (random() % 20)))
671     {
672       init_colors (mi);
673       cc->done_once = True;
674     }
675
676   {
677     glNewList(cc->dlist, GL_COMPILE);
678     cc->polys1 = unit_spheremonics (mi, cc->resolution, wire,cc->m,cc->colors);
679     glEndList();
680
681     glNewList(cc->dlist2, GL_COMPILE);
682     glPushMatrix();
683     glScalef (1.05, 1.05, 1.05);
684     cc->polys2 = unit_spheremonics (mi, cc->resolution, 2, cc->m, cc->colors);
685     glPopMatrix();
686     glEndList();
687   }
688 }
689
690
691 \f
692
693 ENTRYPOINT void 
694 init_spheremonics (ModeInfo *mi)
695 {
696   spheremonics_configuration *cc;
697
698   MI_INIT (mi, ccs, NULL);
699
700   cc = &ccs[MI_SCREEN(mi)];
701
702   if ((cc->glx_context = init_GL(mi)) != NULL) {
703     gl_init(mi);
704     reshape_spheremonics (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
705   }
706
707   {
708     Bool spinx=False, spiny=False, spinz=False;
709     double spin_speed   = 1.0;
710     double wander_speed = 0.03;
711
712     char *s = do_spin;
713     while (*s)
714       {
715         if      (*s == 'x' || *s == 'X') spinx = True;
716         else if (*s == 'y' || *s == 'Y') spiny = True;
717         else if (*s == 'z' || *s == 'Z') spinz = True;
718         else if (*s == '0') ;
719         else
720           {
721             fprintf (stderr,
722          "%s: spin must contain only the characters X, Y, or Z (not \"%s\")\n",
723                      progname, do_spin);
724             exit (1);
725           }
726         s++;
727       }
728
729     cc->rot = make_rotator (spinx ? spin_speed : 0,
730                             spinz ? spin_speed : 0,
731                             spiny ? spin_speed : 0,
732                             1.0,
733                             do_wander ? wander_speed : 0,
734                             (spinx && spiny && spinz));
735     cc->trackball = gltrackball_init (True);
736   }
737
738   cc->tracer = -1;
739   cc->mesher = -1;
740
741   cc->resolution = res;
742
743   cc->font_data = load_texture_font (mi->dpy, "labelfont");
744
745   cc->dlist = glGenLists(1);
746   cc->dlist2 = glGenLists(1);
747
748   cc->m_max = 4; /* 9? */
749   {
750     unsigned int i;
751     for (i = 0; i < countof(cc->dm); i++)
752       cc->dm[i] = 1;  /* going up! */
753
754     /* Generate a few more times so we don't always start off with a sphere */
755     for (i = 0; i < 5; i++)
756       tweak_parameters (mi);
757   }
758
759   generate_spheremonics(mi);
760 }
761
762
763 ENTRYPOINT Bool
764 spheremonics_handle_event (ModeInfo *mi, XEvent *event)
765 {
766   spheremonics_configuration *cc = &ccs[MI_SCREEN(mi)];
767
768   if (gltrackball_event_handler (event, cc->trackball,
769                                  MI_WIDTH (mi), MI_HEIGHT (mi),
770                                  &cc->button_down_p))
771     return True;
772   else if (screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event))
773     {
774       cc->change_tick = duration;
775       return True;
776     }
777
778   return False;
779 }
780
781
782 ENTRYPOINT void
783 draw_spheremonics (ModeInfo *mi)
784 {
785   spheremonics_configuration *cc = &ccs[MI_SCREEN(mi)];
786   Display *dpy = MI_DISPLAY(mi);
787   Window window = MI_WINDOW(mi);
788
789   if (!cc->glx_context)
790     return;
791
792   glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(cc->glx_context));
793
794   gl_init(mi);
795
796   glShadeModel(GL_SMOOTH);
797
798   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
799
800   glPushMatrix ();
801
802   glScalef(1.1, 1.1, 1.1);
803
804   {
805     double x, y, z;
806     get_position (cc->rot, &x, &y, &z, !cc->button_down_p);
807     glTranslatef((x - 0.5) * 8,
808                  (y - 0.5) * 6,
809                  (z - 0.5) * 8);
810
811     gltrackball_rotate (cc->trackball);
812
813     get_rotation (cc->rot, &x, &y, &z, !cc->button_down_p);
814     glRotatef (x * 360, 1.0, 0.0, 0.0);
815     glRotatef (y * 360, 0.0, 1.0, 0.0);
816     glRotatef (z * 360, 0.0, 0.0, 1.0);
817   }
818
819   glScalef(7,7,7);
820
821   mi->polygon_count = 0;
822
823   glScalef (cc->scale, cc->scale, cc->scale);
824   glCallList (cc->dlist);
825   mi->polygon_count += cc->polys1;
826
827   if (cc->mesher >= 0 /* || cc->button_down_p */)
828     {
829       glDisable (GL_LIGHTING);
830       glCallList (cc->dlist2);
831       mi->polygon_count += cc->polys2;
832       if (cc->mesher >= 0)
833         cc->mesher--;
834     }
835   do_tracer(mi);
836
837
838   if (cc->button_down_p)
839     {
840       char buf[200];
841       sprintf (buf,
842                ((cc->m[0]<10 && cc->m[1]<10 && cc->m[2]<10 && cc->m[3]<10 &&
843                  cc->m[4]<10 && cc->m[5]<10 && cc->m[6]<10 && cc->m[7]<10)
844                 ? "%d%d%d%d%d%d%d%d"
845                 : "%d %d %d %d %d %d %d %d"),
846                cc->m[0], cc->m[1], cc->m[2], cc->m[3],
847                cc->m[4], cc->m[5], cc->m[6], cc->m[7]);
848
849       glColor3f(1.0, 1.0, 0.0);
850       print_texture_label (mi->dpy, cc->font_data,
851                            mi->xgwa.width, mi->xgwa.height,
852                            1, buf);
853     }
854
855   if (!static_parms)
856     {
857       if (cc->change_tick++ >= duration && !cc->button_down_p)
858         {
859           generate_spheremonics(mi);
860           cc->change_tick = 0;
861           cc->mesher = -1;  /* turn off the mesh when switching objects */
862         }
863     }
864
865   glPopMatrix();
866
867   if (mi->fps_p) do_fps (mi);
868   glFinish();
869
870   glXSwapBuffers(dpy, window);
871 }
872
873 XSCREENSAVER_MODULE ("Spheremonics", spheremonics)
874
875 #endif /* USE_GL */