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