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