http://packetstormsecurity.org/UNIX/admin/xscreensaver-4.03.tar.gz
[xscreensaver] / hacks / glx / spheremonics.c
1 /* xscreensaver, Copyright (c) 2002 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  * With the hairier objects, some of the faces are inside out.  E.g.,
57  *     -parameters 01210111
58  * If we turn off GL_CULL_FACE, that object renders more solidly
59  * (indicating wrong winding) and the altered surfaces are too dark
60  * (indicating wrong normals.)
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 ccs_opts        xlockmore_opts
72
73 #define DEF_DURATION    "100"
74 #define DEF_SPIN        "XYZ"
75 #define DEF_WANDER      "False"
76 #define DEF_RESOLUTION  "64"
77 #define DEF_BBOX        "False"
78 #define DEF_GRID        "True"
79 #define DEF_SMOOTH      "True"
80 #define DEF_PARMS       "(default)"
81
82 #define DEFAULTS        "*delay:        30000       \n" \
83                         "*resolution: " DEF_RESOLUTION "\n" \
84                         "*showFPS:      False       \n" \
85                         "*wireframe:    False       \n" \
86                         "*duration:   " DEF_DURATION "\n" \
87                         "*spin:       " DEF_SPIN   "\n" \
88                         "*wander:     " DEF_WANDER "\n" \
89                         "*bbox:       " DEF_BBOX   "\n" \
90                         "*grid:       " DEF_GRID   "\n" \
91                         "*smooth:     " DEF_SMOOTH "\n" \
92                         "*parameters: " DEF_PARMS  "\n" \
93
94 #undef countof
95 #define countof(x) (sizeof((x))/sizeof((*x)))
96
97 #include "xlockmore.h"
98 #include "colors.h"
99 #include <ctype.h>
100
101 #ifdef USE_GL /* whole file */
102
103 #include <GL/glu.h>
104
105 typedef struct {
106    double x,y,z;
107 } XYZ;
108
109 typedef struct {
110   GLXContext *glx_context;
111
112   GLfloat rotx, roty, rotz;        /* current object rotation */
113   GLfloat dx, dy, dz;              /* current rotational velocity */
114   GLfloat ddx, ddy, ddz;           /* current rotational acceleration */
115   GLfloat d_max;                   /* max velocity */
116   Bool spin_x, spin_y, spin_z;
117
118   GLuint dlist, dlist2;
119   GLfloat scale;
120   XYZ bbox[2];
121
122   int resolution;
123   int ncolors;
124   XColor *colors;
125
126   int m[8];
127   int dm[8];
128   int m_max;
129
130   int tracer;
131   int mesher;
132
133   XFontStruct *font;
134   GLuint font_list;
135
136 } spheremonics_configuration;
137
138 static spheremonics_configuration *ccs = NULL;
139
140 static char *do_spin;
141 static Bool do_wander;
142 static Bool do_bbox;
143 static Bool do_grid;
144 static int smooth_p;
145 static char *static_parms;
146 static int res;
147 static int duration;
148
149 static XrmOptionDescRec opts[] = {
150   { "-spin",   ".spin",   XrmoptionSepArg, 0 },
151   { "+spin",   ".spin",   XrmoptionNoArg, "" },
152   { "-wander", ".wander", XrmoptionNoArg, "True" },
153   { "+wander", ".wander", XrmoptionNoArg, "False" },
154   { "-resolution", ".resolution", XrmoptionSepArg, 0 },
155   { "-duration",   ".duration",   XrmoptionSepArg, 0 },
156   { "-bbox",   ".bbox",  XrmoptionNoArg, "True" },
157   { "+bbox",   ".bbox",  XrmoptionNoArg, "False" },
158   { "-grid",   ".grid",  XrmoptionNoArg, "True" },
159   { "+grid",   ".grid",  XrmoptionNoArg, "False" },
160   {"-smooth",  ".smooth", XrmoptionNoArg, "True" },
161   {"+smooth",  ".smooth", XrmoptionNoArg, "False" },
162   { "-parameters", ".parameters", XrmoptionSepArg, 0 },
163 };
164
165 static argtype vars[] = {
166   {(caddr_t *) &do_spin,   "spin",   "Spin",   DEF_SPIN,   t_String},
167   {(caddr_t *) &do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
168   {(caddr_t *) &res,       "resolution", "Resolution", DEF_RESOLUTION, t_Int},
169   {(caddr_t *) &duration,  "duration",   "Duration",   DEF_DURATION,   t_Int},
170   {(caddr_t *) &do_bbox,   "bbox",   "BBox",   DEF_BBOX,   t_Bool},
171   {(caddr_t *) &do_grid,   "grid",   "Grid",   DEF_GRID,   t_Bool},
172   {(caddr_t *) &smooth_p,  "smooth", "Smooth", DEF_SMOOTH, t_Bool},
173   {(caddr_t *) &static_parms, "parameters", "Parameters", DEF_PARMS, t_String},
174 };
175
176 ModeSpecOpt ccs_opts = {countof(opts), opts, countof(vars), vars, NULL};
177
178
179 /* Window management, etc
180  */
181 void
182 reshape_spheremonics (ModeInfo *mi, int width, int height)
183 {
184   GLfloat h = (GLfloat) height / (GLfloat) width;
185
186   glViewport (0, 0, (GLint) width, (GLint) height);
187
188   glMatrixMode(GL_PROJECTION);
189   glLoadIdentity();
190
191   gluPerspective( 30.0, 1/h, 1.0, 100.0 );
192   gluLookAt( 0.0, 0.0, 15.0,
193              0.0, 0.0, 0.0,
194              0.0, 1.0, 0.0);
195   glMatrixMode(GL_MODELVIEW);
196   glLoadIdentity();
197   glTranslatef(0.0, 0.0, -15.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_CULL_FACE);
217       glEnable(GL_LIGHTING);
218       glEnable(GL_LIGHT0);
219       glEnable(GL_DEPTH_TEST);
220     }
221 }
222
223
224 /* lifted from lament.c */
225 #define RAND(n) ((long) ((random() & 0x7fffffff) % ((long) (n))))
226 #define RANDSIGN() ((random() & 1) ? 1 : -1)
227
228 static void
229 rotate(GLfloat *pos, GLfloat *v, GLfloat *dv, GLfloat max_v)
230 {
231   double ppos = *pos;
232
233   /* tick position */
234   if (ppos < 0)
235     ppos = -(ppos + *v);
236   else
237     ppos += *v;
238
239   if (ppos > 1.0)
240     ppos -= 1.0;
241   else if (ppos < 0)
242     ppos += 1.0;
243
244   if (ppos < 0) abort();
245   if (ppos > 1.0) abort();
246   *pos = (*pos > 0 ? ppos : -ppos);
247
248   /* accelerate */
249   *v += *dv;
250
251   /* clamp velocity */
252   if (*v > max_v || *v < -max_v)
253     {
254       *dv = -*dv;
255     }
256   /* If it stops, start it going in the other direction. */
257   else if (*v < 0)
258     {
259       if (random() % 4)
260         {
261           *v = 0;
262
263           /* keep going in the same direction */
264           if (random() % 2)
265             *dv = 0;
266           else if (*dv < 0)
267             *dv = -*dv;
268         }
269       else
270         {
271           /* reverse gears */
272           *v = -*v;
273           *dv = -*dv;
274           *pos = -*pos;
275         }
276     }
277
278   /* Alter direction of rotational acceleration randomly. */
279   if (! (random() % 120))
280     *dv = -*dv;
281
282   /* Change acceleration very occasionally. */
283   if (! (random() % 200))
284     {
285       if (*dv == 0)
286         *dv = 0.00001;
287       else if (random() & 1)
288         *dv *= 1.2;
289       else
290         *dv *= 0.8;
291     }
292 }
293
294 \f
295 /* generate the object */
296
297 static XYZ
298 sphere_eval (double theta, double phi, int *m)
299 {
300   double r = 0;
301   XYZ p;
302
303   r += pow (sin(m[0] * phi),  (double)m[1]);
304   r += pow (cos(m[2] * phi),  (double)m[3]);
305   r += pow (sin(m[4] * theta),(double)m[5]);
306   r += pow (cos(m[6] * theta),(double)m[7]);
307
308   p.x = r * sin(phi) * cos(theta);
309   p.y = r * cos(phi);
310   p.z = r * sin(phi) * sin(theta);
311
312   return (p);
313 }
314
315
316 /* Normalise a vector */
317 static void
318 normalize (XYZ *p)
319 {
320   double length;
321   length = sqrt(p->x * p->x + p->y * p->y + p->z * p->z);
322   if (length != 0) {
323     p->x /= length;
324     p->y /= length;
325     p->z /= length;
326   } else {
327     p->x = 0;
328     p->y = 0;
329     p->z = 0;
330   }       
331 }
332
333 /*-------------------------------------------------------------------------
334         Calculate the unit normal at p given two other points 
335         p1,p2 on the surface. The normal points in the direction 
336         of p1 crossproduct p2
337  */
338 static XYZ
339 calc_normal (XYZ p, XYZ p1, XYZ p2)
340 {
341   XYZ n, pa, pb;
342   pa.x = p1.x - p.x;
343   pa.y = p1.y - p.y;
344   pa.z = p1.z - p.z;
345   pb.x = p2.x - p.x;
346   pb.y = p2.y - p.y;
347   pb.z = p2.z - p.z;
348   n.x = pa.y * pb.z - pa.z * pb.y;
349   n.y = pa.z * pb.x - pa.x * pb.z;
350   n.z = pa.x * pb.y - pa.y * pb.x;
351   normalize (&n);
352   return (n);
353 }
354
355
356 static void
357 do_color (int i, XColor *colors)
358 {
359   GLfloat c[4];
360   c[0] = colors[i].red   / 65535.0;
361   c[1] = colors[i].green / 65535.0;
362   c[2] = colors[i].blue  / 65535.0;
363   c[3] = 1.0;
364   glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, c);
365   glColor3f (c[0], c[1], c[2]);
366 }
367
368
369 static void
370 draw_circle (ModeInfo *mi, Bool teeth_p)
371 {
372   GLfloat th;
373   int tick = 0;
374   GLfloat x, y;
375   GLfloat step = (M_PI / 180);
376
377   glBegin(GL_LINE_LOOP);
378   for (th = 0; th < M_PI*2; th += step*5)
379     {
380       GLfloat r1 = 0.5;
381       x = cos (th);
382       y = sin (th);
383       glVertex3f(x*r1, y*r1,  0);
384     }
385   glEnd();
386
387   if (!teeth_p) return;
388
389   glBegin(GL_LINES);
390   for (th = 0; th < M_PI*2; th += step)
391     {
392       GLfloat r1 = 0.5;
393       GLfloat r2 = r1 - 0.01;
394       if (! (tick % 10))
395         r2 -= 0.02;
396       else if (! (tick % 5))
397         r2 -= 0.01;
398       tick++;
399
400       x = cos (th);
401       y = sin (th);
402       glVertex3f(x*r1, y*r1,  0);
403       glVertex3f(x*r2, y*r2,  0);
404     }
405   glEnd();
406 }
407
408
409 static void
410 draw_bounding_box (ModeInfo *mi)
411 {
412   spheremonics_configuration *cc = &ccs[MI_SCREEN(mi)];
413
414   static GLfloat c1[4] = { 0.2, 0.2, 0.6, 1.0 };
415   static GLfloat c2[4] = { 1.0, 0.0, 0.0, 1.0 };
416   int wire = MI_IS_WIREFRAME(mi);
417
418   GLfloat x1 = cc->bbox[0].x;
419   GLfloat y1 = cc->bbox[0].y;
420   GLfloat z1 = cc->bbox[0].z;
421   GLfloat x2 = cc->bbox[1].x;
422   GLfloat y2 = cc->bbox[1].y;
423   GLfloat z2 = cc->bbox[1].z;
424
425 #if 1
426   x1 = y1 = z1 = -0.5;
427   x2 = y2 = z2 =  0.5;
428 #endif
429
430   if (do_bbox && !wire)
431     {
432       glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, c1);
433       glFrontFace(GL_CCW);
434
435       glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
436       glNormal3f(0, 1, 0);
437       glVertex3f(x1, y1, z1); glVertex3f(x1, y1, z2);
438       glVertex3f(x2, y1, z2); glVertex3f(x2, y1, z1);
439       glEnd();
440       glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
441       glNormal3f(0, -1, 0);
442       glVertex3f(x2, y2, z1); glVertex3f(x2, y2, z2);
443       glVertex3f(x1, y2, z2); glVertex3f(x1, y2, z1);
444       glEnd();
445       glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
446       glNormal3f(0, 0, 1);
447       glVertex3f(x1, y1, z1); glVertex3f(x2, y1, z1);
448       glVertex3f(x2, y2, z1); glVertex3f(x1, y2, z1);
449       glEnd();
450       glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
451       glNormal3f(0, 0, -1);
452       glVertex3f(x1, y2, z2); glVertex3f(x2, y2, z2);
453       glVertex3f(x2, y1, z2); glVertex3f(x1, y1, z2);
454       glEnd();
455       glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
456       glNormal3f(1, 0, 0);
457       glVertex3f(x1, y2, z1); glVertex3f(x1, y2, z2);
458       glVertex3f(x1, y1, z2); glVertex3f(x1, y1, z1);
459       glEnd();
460       glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
461       glNormal3f(-1, 0, 0);
462       glVertex3f(x2, y1, z1); glVertex3f(x2, y1, z2);
463       glVertex3f(x2, y2, z2); glVertex3f(x2, y2, z1);
464       glEnd();
465     }
466
467   glPushAttrib (GL_LIGHTING);
468   glDisable (GL_LIGHTING);
469
470   glColor3f (c2[0], c2[1], c2[2]);
471
472   if (do_grid)
473     {
474       glPushMatrix();
475       glBegin(GL_LINES);
476       glVertex3f(0, -0.66, 0);
477       glVertex3f(0,  0.66, 0); 
478       glEnd();
479       draw_circle (mi, True);
480       glRotatef(90, 1, 0, 0);
481       draw_circle (mi, True);
482       glRotatef(90, 0, 1, 0);
483       draw_circle (mi, True);
484       glPopMatrix();
485     }
486   else
487     {
488       glBegin(GL_LINES);
489       if (x1 > 0) x1 = 0; if (x2 < 0) x2 = 0;
490       if (y1 > 0) y1 = 0; if (y2 < 0) y2 = 0;
491       if (z1 > 0) z1 = 0; if (z2 < 0) z2 = 0;
492       glVertex3f(x1, 0,  0);  glVertex3f(x2, 0,  0); 
493       glVertex3f(0 , y1, 0);  glVertex3f(0,  y2, 0); 
494       glVertex3f(0,  0,  z1); glVertex3f(0,  0,  z2); 
495       glEnd();
496     }
497
498   glPopAttrib();
499 }
500
501
502 static void
503 do_tracer (ModeInfo *mi)
504 {
505   spheremonics_configuration *cc = &ccs[MI_SCREEN(mi)];
506
507   if (cc->tracer == -1 &&
508       cc->mesher == -1 &&
509       !(random() % (duration * 4)))
510     {
511       if (random() & 1)
512         cc->tracer = ((random() & 1) ? 0 : 180);
513       else
514         cc->mesher = ((random() % ((duration / 3) + 1)) +
515                       (random() % ((duration / 3) + 1)));
516     }
517
518   if (cc->tracer >= 0)
519     {
520       int d = (90 - cc->tracer);
521       GLfloat th = d * (M_PI / 180);
522       GLfloat x = cos (th);
523       GLfloat y = sin (th);
524       GLfloat s = 1.5 / cc->scale;
525
526       if (s > 0.001)
527         {
528           static GLfloat c[4] = { 0.6, 0.5, 1.0, 1.0 };
529
530           glPushAttrib (GL_LIGHTING);
531           glDisable (GL_LIGHTING);
532
533           glPushMatrix();
534           glRotatef (90, 1, 0, 0);
535           glTranslatef (0, 0, y*s/2);
536           s *= x;
537           glScalef(s, s, s);
538           glColor3f (c[0], c[1], c[2]);
539           draw_circle (mi, False);
540           glPopMatrix();
541
542           glPopAttrib();
543         }
544
545       cc->tracer += 5;
546       if (cc->tracer == 180 || cc->tracer == 360)
547         cc->tracer = -1;
548     }
549 }
550
551
552 static void
553 unit_spheremonics (ModeInfo *mi,
554                    int resolution, Bool wire, int *m, XColor *colors)
555 {
556   spheremonics_configuration *cc = &ccs[MI_SCREEN(mi)];
557
558   int i, j;
559   double du, dv;
560   XYZ q[4];
561   XYZ n[4];
562   int res = (wire == 2
563              ? resolution / 2
564              : resolution);
565
566   cc->bbox[0].x = cc->bbox[0].y = cc->bbox[0].z = 0;
567   cc->bbox[1].x = cc->bbox[1].y = cc->bbox[1].z = 0;
568
569   du = (M_PI+M_PI) / (double)res; /* Theta */
570   dv = M_PI        / (double)res; /* Phi   */
571
572   if (wire)
573     glColor3f (1, 1, 1);
574
575 /*  mi->polygon_count = 0; */
576
577   glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
578
579   for (i = 0; i < res; i++) {
580     double u = i * du;
581     for (j = 0; j < res; j++) {
582       double v = j * dv;
583       q[0] = sphere_eval (u, v, m);
584       n[0] = calc_normal(q[0],
585                          sphere_eval (u+du/10, v, m),
586                          sphere_eval (u, v+dv/10, m));
587       glNormal3f(n[0].x,n[0].y,n[0].z);
588       if (!wire) do_color (i, colors);
589       glVertex3f(q[0].x,q[0].y,q[0].z);
590
591       q[1] = sphere_eval (u+du, v, m);
592       n[1] = calc_normal(q[1],
593                          sphere_eval (u+du+du/10, v, m),
594                          sphere_eval (u+du, v+dv/10, m));
595       glNormal3f(n[1].x,n[1].y,n[1].z);
596       if (!wire) do_color ((i+1)%res, colors);
597       glVertex3f(q[1].x,q[1].y,q[1].z);
598
599       q[2] = sphere_eval (u+du, v+dv, m);
600       n[2] = calc_normal(q[2],
601                          sphere_eval (u+du+du/10, v+dv, m),
602                          sphere_eval (u+du, v+dv+dv/10, m));
603       glNormal3f(n[2].x,n[2].y,n[2].z);
604       if (!wire) do_color ((i+1)%res, colors);
605       glVertex3f(q[2].x,q[2].y,q[2].z);
606
607       q[3] = sphere_eval (u,v+dv, m);
608       n[3] = calc_normal(q[3],
609                          sphere_eval (u+du/10, v+dv, m),
610                          sphere_eval (u, v+dv+dv/10, m));
611       glNormal3f(n[3].x,n[3].y,n[3].z);
612       if (!wire) do_color (i, colors);
613       glVertex3f(q[3].x,q[3].y,q[3].z);
614
615 /*      mi->polygon_count++; */
616
617 # define CHECK_BBOX(N) \
618          if (q[(N)].x < cc->bbox[0].x) cc->bbox[0].x = q[(N)].x; \
619          if (q[(N)].y < cc->bbox[0].y) cc->bbox[0].y = q[(N)].y; \
620          if (q[(N)].z < cc->bbox[0].z) cc->bbox[0].z = q[(N)].z; \
621          if (q[(N)].x > cc->bbox[1].x) cc->bbox[1].x = q[(N)].x; \
622          if (q[(N)].y > cc->bbox[1].y) cc->bbox[1].y = q[(N)].y; \
623          if (q[(N)].z > cc->bbox[1].z) cc->bbox[1].z = q[(N)].z
624
625       CHECK_BBOX(0);
626       CHECK_BBOX(1);
627       CHECK_BBOX(2);
628       CHECK_BBOX(3);
629 # undef CHECK_BBOX
630     }
631   }
632   glEnd();
633
634   {
635     GLfloat w = cc->bbox[1].x - cc->bbox[0].x;
636     GLfloat h = cc->bbox[1].y - cc->bbox[0].y;
637     GLfloat d = cc->bbox[1].z - cc->bbox[0].z;
638     GLfloat wh = (w > h ? w : h);
639     GLfloat hd = (h > d ? h : d);
640     GLfloat scale = (wh > hd ? wh : hd);
641
642     cc->scale = 1/scale;
643
644     if (wire < 2 && (do_bbox || do_grid))
645       {
646         GLfloat s = scale * 1.5;
647         glPushMatrix();
648         glScalef(s, s, s);
649         draw_bounding_box (mi);
650         glPopMatrix();
651       }
652   }
653 }
654
655
656 static void
657 init_colors (ModeInfo *mi)
658 {
659   spheremonics_configuration *cc = &ccs[MI_SCREEN(mi)];
660   int i;
661   cc->ncolors = cc->resolution;
662   cc->colors = (XColor *) calloc(cc->ncolors, sizeof(XColor));
663   make_smooth_colormap (0, 0, 0,
664                         cc->colors, &cc->ncolors, 
665                         False, 0, False);
666
667   /* brighter colors, please... */
668   for (i = 0; i < cc->ncolors; i++)
669     {
670       cc->colors[i].red   = (cc->colors[i].red   / 2) + 32767;
671       cc->colors[i].green = (cc->colors[i].green / 2) + 32767;
672       cc->colors[i].blue  = (cc->colors[i].blue  / 2) + 32767;
673     }
674 }
675
676
677 /* Pick one of the parameters to the function and tweak it up or down.
678  */
679 static void
680 tweak_parameters (ModeInfo *mi)
681 {
682   spheremonics_configuration *cc = &ccs[MI_SCREEN(mi)];
683
684   /* If the -parameters command line option was specified, just use that
685      all the time.
686    */
687   if (static_parms &&
688       *static_parms &&
689       !!strcasecmp (static_parms, "(default)"))
690     {
691       unsigned long n;
692       char dummy;
693       if (8 == sscanf (static_parms, "%d %d %d %d %d %d %d %d %c",
694                        &cc->m[0], &cc->m[1], &cc->m[2], &cc->m[3],
695                        &cc->m[4], &cc->m[5], &cc->m[6], &cc->m[7],
696                        &dummy))
697         return;
698       else if (strlen (static_parms) == 8 &&
699                1 == sscanf (static_parms, "%lu %c", &n, &dummy))
700         {
701           const char *s = static_parms;
702           int i = 0;
703           while (*s)
704             cc->m[i++] = (*s++)-'0';
705           return;
706         }
707       fprintf (stderr,
708                "%s: -parameters must be a string of 8 ints (not \"%s\")\n",
709                progname, static_parms);
710       exit (1);
711     }
712
713   static_parms = 0;
714
715
716 # define SHIFT(N) do { \
717     int n = (N); \
718     cc->m[n] += cc->dm[n]; \
719     if (cc->m[n] <= 0) \
720       cc->m[n] = 0, cc->dm[n] = -cc->dm[n]; \
721     else if (cc->m[n] >= cc->m_max) \
722       cc->m[n] = cc->m_max, cc->dm[n] = -cc->dm[n]; \
723   } while(0)
724
725 /*    else if (cc->m[n] >= cc->m_max/2 && (! (random() % 3))) \
726       cc->m[n] = cc->m_max/2, cc->dm[n] = -cc->dm[n]; \
727 */
728
729   switch(random() % 8)
730     {
731     case 0: SHIFT(0); break;
732     case 1: SHIFT(1); break;
733     case 2: SHIFT(2); break;
734     case 3: SHIFT(3); break;
735     case 4: SHIFT(4); break;
736     case 5: SHIFT(5); break;
737     case 6: SHIFT(6); break;
738     case 7: SHIFT(7); break;
739     default: abort(); break;
740     }
741 # undef SHIFT
742
743 #if 0
744     printf ("%s: state: %d %d %d %d %d %d %d %d\n",
745             progname,
746             cc->m[0], cc->m[1], cc->m[2], cc->m[3],
747             cc->m[4], cc->m[5], cc->m[6], cc->m[7]);
748 #endif
749
750 }
751
752
753 static void
754 generate_spheremonics (ModeInfo *mi)
755 {
756   spheremonics_configuration *cc = &ccs[MI_SCREEN(mi)];
757   int wire = MI_IS_WIREFRAME(mi);
758
759   tweak_parameters (mi);
760
761   {
762     static Bool done = False;
763     if (!done || (0 == (random() % 20)))
764       {
765         init_colors (mi);
766         done = True;
767       }
768   }
769
770   {
771     glNewList(cc->dlist, GL_COMPILE);
772     unit_spheremonics (mi, cc->resolution, wire, cc->m, cc->colors);
773     glEndList();
774
775     glNewList(cc->dlist2, GL_COMPILE);
776     glPushAttrib (GL_LIGHTING);
777     glDisable (GL_LIGHTING);
778     glPushMatrix();
779     glScalef (1.05, 1.05, 1.05);
780     unit_spheremonics (mi, cc->resolution, 2, cc->m, cc->colors);
781     glPopMatrix();
782     glPopAttrib();
783     glEndList();
784   }
785 }
786
787
788 \f
789
790 static void
791 load_font (ModeInfo *mi, char *res, XFontStruct **fontP, GLuint *dlistP)
792 {
793   const char *font = get_string_resource (res, "Font");
794   XFontStruct *f;
795   Font id;
796   int first, last;
797
798   if (!font) font = "-*-times-bold-r-normal-*-140-*";
799
800   f = XLoadQueryFont(mi->dpy, font);
801   if (!f) f = XLoadQueryFont(mi->dpy, "fixed");
802
803   id = f->fid;
804   first = f->min_char_or_byte2;
805   last = f->max_char_or_byte2;
806   
807   clear_gl_error ();
808   *dlistP = glGenLists ((GLuint) last+1);
809   check_gl_error ("glGenLists");
810   glXUseXFont(id, first, last-first+1, *dlistP + first);
811   check_gl_error ("glXUseXFont");
812
813   *fontP = f;
814 }
815
816 static void
817 draw_label (ModeInfo *mi, const char *s)
818 {
819   spheremonics_configuration *cc = &ccs[MI_SCREEN(mi)];
820   int i;
821   
822   glPushAttrib(GL_TRANSFORM_BIT | GL_ENABLE_BIT);
823   glDisable(GL_LIGHTING);
824   glDisable(GL_DEPTH_TEST);
825   glMatrixMode(GL_PROJECTION);
826   glPushMatrix();
827   glLoadIdentity();
828   glMatrixMode(GL_MODELVIEW);
829   glPushMatrix();
830   glLoadIdentity();
831   gluOrtho2D(0, mi->xgwa.width, 0, mi->xgwa.height);
832   glColor3f(1.0, 1.0, 0.0);
833
834   glRasterPos2f (10,
835                  (mi->xgwa.height
836                   - 10
837                   - (cc->font->ascent + cc->font->descent)));
838   for (i = 0; i < strlen(s); i++)
839     glCallList (cc->font_list + (int)s[i]);
840
841   glPopMatrix();
842   glMatrixMode(GL_PROJECTION);
843   glPopMatrix();
844   glPopAttrib();
845 }
846
847
848 \f
849
850 void 
851 init_spheremonics (ModeInfo *mi)
852 {
853   spheremonics_configuration *cc;
854
855   if (!ccs) {
856     ccs = (spheremonics_configuration *)
857       calloc (MI_NUM_SCREENS(mi), sizeof (spheremonics_configuration));
858     if (!ccs) {
859       fprintf(stderr, "%s: out of memory\n", progname);
860       exit(1);
861     }
862
863     cc = &ccs[MI_SCREEN(mi)];
864   }
865
866   cc = &ccs[MI_SCREEN(mi)];
867
868   if ((cc->glx_context = init_GL(mi)) != NULL) {
869     gl_init(mi);
870     reshape_spheremonics (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
871   }
872
873   if (smooth_p) 
874     {
875       glEnable (GL_LINE_SMOOTH);
876       glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
877       glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 
878       glEnable (GL_BLEND);
879     }
880
881   {
882     char *s = do_spin;
883     while (*s)
884       {
885         if      (*s == 'x' || *s == 'X') cc->spin_x = 1;
886         else if (*s == 'y' || *s == 'Y') cc->spin_y = 1;
887         else if (*s == 'z' || *s == 'Z') cc->spin_z = 1;
888         else
889           {
890             fprintf (stderr,
891          "%s: spin must contain only the characters X, Y, or Z (not \"%s\")\n",
892                      progname, do_spin);
893             exit (1);
894           }
895         s++;
896       }
897   }
898
899   cc->rotx = frand(1.0) * RANDSIGN();
900   cc->roty = frand(1.0) * RANDSIGN();
901   cc->rotz = frand(1.0) * RANDSIGN();
902
903   /* bell curve from 0-6 degrees, avg 3 */
904   cc->dx = (frand(0.4) + frand(0.4) + frand(0.4)) / (360/2);
905   cc->dy = (frand(0.4) + frand(0.4) + frand(0.4)) / (360/2);
906   cc->dz = (frand(0.4) + frand(0.4) + frand(0.4)) / (360/2);
907
908   cc->d_max = cc->dx * 2;
909
910   cc->ddx = 0.00006 + frand(0.00003);
911   cc->ddy = 0.00006 + frand(0.00003);
912   cc->ddz = 0.00006 + frand(0.00003);
913   cc->tracer = -1;
914   cc->mesher = -1;
915
916   cc->resolution = res;
917
918   load_font (mi, "labelfont", &cc->font, &cc->font_list);
919
920   cc->dlist = glGenLists(1);
921   cc->dlist2 = glGenLists(1);
922
923   cc->m_max = 4; /* 9? */
924   {
925     int i;
926     for (i = 0; i < countof(cc->dm); i++)
927       cc->dm[i] = 1;  /* going up! */
928
929     /* Generate a few more times so we don't always start off with a sphere */
930     for (i = 0; i < 5; i++)
931       tweak_parameters (mi);
932   }
933
934   generate_spheremonics(mi);
935 }
936
937
938 static Bool
939 mouse_down_p (ModeInfo *mi)
940 {
941   Window root, child;
942   int rx, ry, wx, wy;
943   unsigned int mask;
944   if (!XQueryPointer (MI_DISPLAY(mi), MI_WINDOW(mi),
945                       &root, &child, &rx, &ry, &wx, &wy, &mask))
946     return False;
947   if (! (mask & Button1Mask))
948     return False;
949   return True;
950 }
951
952
953 void
954 draw_spheremonics (ModeInfo *mi)
955 {
956   spheremonics_configuration *cc = &ccs[MI_SCREEN(mi)];
957   Display *dpy = MI_DISPLAY(mi);
958   Window window = MI_WINDOW(mi);
959   Bool mouse_p;
960
961   if (!cc->glx_context)
962     return;
963
964   mouse_p = mouse_down_p (mi);
965
966   glShadeModel(GL_SMOOTH);
967
968   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
969
970   glPushMatrix ();
971
972   glScalef(1.1, 1.1, 1.1);
973
974   {
975     GLfloat x, y, z;
976
977     if (do_wander)
978       {
979         static int frame = 0;
980
981 #       define SINOID(SCALE,SIZE) \
982         ((((1 + sin((frame * (SCALE)) / 2 * M_PI)) / 2.0) * (SIZE)) - (SIZE)/2)
983
984         x = SINOID(0.0071, 8.0);
985         y = SINOID(0.0053, 6.0);
986         z = SINOID(0.0037, 15.0);
987         frame++;
988         glTranslatef(x, y, z);
989       }
990
991     if (cc->spin_x || cc->spin_y || cc->spin_z)
992       {
993         x = cc->rotx;
994         y = cc->roty;
995         z = cc->rotz;
996         if (x < 0) x = 1 - (x + 1);
997         if (y < 0) y = 1 - (y + 1);
998         if (z < 0) z = 1 - (z + 1);
999
1000         if (cc->spin_x) glRotatef(x * 360, 1.0, 0.0, 0.0);
1001         if (cc->spin_y) glRotatef(y * 360, 0.0, 1.0, 0.0);
1002         if (cc->spin_z) glRotatef(z * 360, 0.0, 0.0, 1.0);
1003
1004         rotate(&cc->rotx, &cc->dx, &cc->ddx, cc->d_max);
1005         rotate(&cc->roty, &cc->dy, &cc->ddy, cc->d_max);
1006         rotate(&cc->rotz, &cc->dz, &cc->ddz, cc->d_max);
1007       }
1008   }
1009
1010   glScalef(7,7,7);
1011
1012   glScalef (cc->scale, cc->scale, cc->scale);
1013   glCallList (cc->dlist);
1014   if (cc->mesher >= 0 /* || mouse_p */)
1015     {
1016       glCallList (cc->dlist2);
1017       if (cc->mesher >= 0)
1018         cc->mesher--;
1019     }
1020   do_tracer(mi);
1021
1022
1023   if (mouse_p)
1024     {
1025       char buf[200];
1026       sprintf (buf, "%d %d %d %d %d %d %d %d",
1027                cc->m[0], cc->m[1], cc->m[2], cc->m[3],
1028                cc->m[4], cc->m[5], cc->m[6], cc->m[7]);
1029       draw_label (mi, buf);
1030     }
1031
1032   if (!static_parms)
1033     {
1034       static int tick = 0;
1035       if (tick++ == duration)
1036         {
1037           generate_spheremonics(mi);
1038           tick = 0;
1039           cc->mesher = -1;  /* turn off the mesh when switching objects */
1040         }
1041     }
1042
1043   glPopMatrix();
1044
1045   if (mi->fps_p) do_fps (mi);
1046   glFinish();
1047
1048   glXSwapBuffers(dpy, window);
1049 }
1050
1051 #endif /* USE_GL */