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