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