http://www.tienza.es/crux/src/www.jwz.org/xscreensaver/xscreensaver-5.05.tar.gz
[xscreensaver] / hacks / glx / spheremonics.c
1 /* xscreensaver, Copyright (c) 2002-2008 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:   -*-times-bold-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   XFontStruct *font;
115   GLuint font_list;
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
226
227 \f
228 /* generate the object */
229
230 static XYZ
231 sphere_eval (double theta, double phi, int *m)
232 {
233   double r = 0;
234   XYZ p;
235
236   r += pow (sin(m[0] * phi),  (double)m[1]);
237   r += pow (cos(m[2] * phi),  (double)m[3]);
238   r += pow (sin(m[4] * theta),(double)m[5]);
239   r += pow (cos(m[6] * theta),(double)m[7]);
240
241   p.x = r * sin(phi) * cos(theta);
242   p.y = r * cos(phi);
243   p.z = r * sin(phi) * sin(theta);
244
245   return (p);
246 }
247
248
249 static void
250 do_color (int i, XColor *colors)
251 {
252   GLfloat c[4];
253   c[0] = colors[i].red   / 65535.0;
254   c[1] = colors[i].green / 65535.0;
255   c[2] = colors[i].blue  / 65535.0;
256   c[3] = 1.0;
257   glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, c);
258   glColor3f (c[0], c[1], c[2]);
259 }
260
261
262 static void
263 draw_circle (ModeInfo *mi, Bool teeth_p)
264 {
265   GLfloat th;
266   int tick = 0;
267   GLfloat x, y;
268   GLfloat step = (M_PI / 180);
269
270   glBegin(GL_LINE_LOOP);
271   for (th = 0; th < M_PI*2; th += step*5)
272     {
273       GLfloat r1 = 0.5;
274       x = cos (th);
275       y = sin (th);
276       glVertex3f(x*r1, y*r1,  0);
277     }
278   glEnd();
279
280   if (!teeth_p) return;
281
282   glBegin(GL_LINES);
283   for (th = 0; th < M_PI*2; th += step)
284     {
285       GLfloat r1 = 0.5;
286       GLfloat r2 = r1 - 0.01;
287       if (! (tick % 10))
288         r2 -= 0.02;
289       else if (! (tick % 5))
290         r2 -= 0.01;
291       tick++;
292
293       x = cos (th);
294       y = sin (th);
295       glVertex3f(x*r1, y*r1,  0);
296       glVertex3f(x*r2, y*r2,  0);
297     }
298   glEnd();
299 }
300
301
302 static void
303 draw_bounding_box (ModeInfo *mi)
304 {
305   spheremonics_configuration *cc = &ccs[MI_SCREEN(mi)];
306
307   static const GLfloat c1[4] = { 0.2, 0.2, 0.6, 1.0 };
308   static const GLfloat c2[4] = { 1.0, 0.0, 0.0, 1.0 };
309   int wire = MI_IS_WIREFRAME(mi);
310
311   GLfloat x1 = cc->bbox[0].x;
312   GLfloat y1 = cc->bbox[0].y;
313   GLfloat z1 = cc->bbox[0].z;
314   GLfloat x2 = cc->bbox[1].x;
315   GLfloat y2 = cc->bbox[1].y;
316   GLfloat z2 = cc->bbox[1].z;
317
318 #if 1
319   x1 = y1 = z1 = -0.5;
320   x2 = y2 = z2 =  0.5;
321 #endif
322
323   if (do_bbox && !wire)
324     {
325       glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, c1);
326       glFrontFace(GL_CCW);
327       glEnable(GL_CULL_FACE);
328
329       glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
330       glNormal3f(0, 1, 0);
331       glVertex3f(x1, y1, z1); glVertex3f(x1, y1, z2);
332       glVertex3f(x2, y1, z2); glVertex3f(x2, y1, z1);
333       glEnd();
334       glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
335       glNormal3f(0, -1, 0);
336       glVertex3f(x2, y2, z1); glVertex3f(x2, y2, z2);
337       glVertex3f(x1, y2, z2); glVertex3f(x1, y2, z1);
338       glEnd();
339       glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
340       glNormal3f(0, 0, 1);
341       glVertex3f(x1, y1, z1); glVertex3f(x2, y1, z1);
342       glVertex3f(x2, y2, z1); glVertex3f(x1, y2, z1);
343       glEnd();
344       glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
345       glNormal3f(0, 0, -1);
346       glVertex3f(x1, y2, z2); glVertex3f(x2, y2, z2);
347       glVertex3f(x2, y1, z2); glVertex3f(x1, y1, z2);
348       glEnd();
349       glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
350       glNormal3f(1, 0, 0);
351       glVertex3f(x1, y2, z1); glVertex3f(x1, y2, z2);
352       glVertex3f(x1, y1, z2); glVertex3f(x1, y1, z1);
353       glEnd();
354       glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
355       glNormal3f(-1, 0, 0);
356       glVertex3f(x2, y1, z1); glVertex3f(x2, y1, z2);
357       glVertex3f(x2, y2, z2); glVertex3f(x2, y2, z1);
358       glEnd();
359       glDisable(GL_CULL_FACE);
360     }
361
362   glPushAttrib (GL_LIGHTING);
363   glDisable (GL_LIGHTING);
364
365   glColor3f (c2[0], c2[1], c2[2]);
366
367   if (do_grid)
368     {
369       glPushMatrix();
370       glBegin(GL_LINES);
371       glVertex3f(0, -0.66, 0);
372       glVertex3f(0,  0.66, 0); 
373       glEnd();
374       draw_circle (mi, True);
375       glRotatef(90, 1, 0, 0);
376       draw_circle (mi, True);
377       glRotatef(90, 0, 1, 0);
378       draw_circle (mi, True);
379       glPopMatrix();
380     }
381   else
382     {
383       glBegin(GL_LINES);
384       if (x1 > 0) x1 = 0; if (x2 < 0) x2 = 0;
385       if (y1 > 0) y1 = 0; if (y2 < 0) y2 = 0;
386       if (z1 > 0) z1 = 0; if (z2 < 0) z2 = 0;
387       glVertex3f(x1, 0,  0);  glVertex3f(x2, 0,  0); 
388       glVertex3f(0 , y1, 0);  glVertex3f(0,  y2, 0); 
389       glVertex3f(0,  0,  z1); glVertex3f(0,  0,  z2); 
390       glEnd();
391     }
392
393   glPopAttrib();
394 }
395
396
397 static void
398 do_tracer (ModeInfo *mi)
399 {
400   spheremonics_configuration *cc = &ccs[MI_SCREEN(mi)];
401
402   if (cc->tracer == -1 &&
403       cc->mesher == -1 &&
404       !(random() % (duration * 4)))
405     {
406       if (random() & 1)
407         cc->tracer = ((random() & 1) ? 0 : 180);
408       else
409         cc->mesher = ((random() % ((duration / 3) + 1)) +
410                       (random() % ((duration / 3) + 1)));
411     }
412
413   if (cc->tracer >= 0)
414     {
415       int d = (90 - cc->tracer);
416       GLfloat th = d * (M_PI / 180);
417       GLfloat x = cos (th);
418       GLfloat y = sin (th);
419       GLfloat s = 1.5 / cc->scale;
420
421       if (s > 0.001)
422         {
423           static const GLfloat c[4] = { 0.6, 0.5, 1.0, 1.0 };
424
425           glPushAttrib (GL_LIGHTING);
426           glDisable (GL_LIGHTING);
427
428           glPushMatrix();
429           glRotatef (90, 1, 0, 0);
430           glTranslatef (0, 0, y*s/2);
431           s *= x;
432           glScalef(s, s, s);
433           glColor3f (c[0], c[1], c[2]);
434           draw_circle (mi, False);
435           glPopMatrix();
436
437           glPopAttrib();
438         }
439
440       cc->tracer += 5;
441       if (cc->tracer == 180 || cc->tracer == 360)
442         cc->tracer = -1;
443     }
444 }
445
446
447 static int
448 unit_spheremonics (ModeInfo *mi,
449                    int resolution, Bool wire, int *m, XColor *colors)
450 {
451   spheremonics_configuration *cc = &ccs[MI_SCREEN(mi)];
452   int polys = 0;
453   int i, j;
454   double du, dv;
455   XYZ q[4];
456   XYZ n[4];
457   int res = (wire == 2
458              ? resolution / 2
459              : resolution);
460
461   cc->bbox[0].x = cc->bbox[0].y = cc->bbox[0].z = 0;
462   cc->bbox[1].x = cc->bbox[1].y = cc->bbox[1].z = 0;
463
464   du = (M_PI+M_PI) / (double)res; /* Theta */
465   dv = M_PI        / (double)res; /* Phi   */
466
467   if (wire)
468     glColor3f (1, 1, 1);
469
470   glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
471
472   for (i = 0; i < res; i++) {
473     double u = i * du;
474     for (j = 0; j < res; j++) {
475       double v = j * dv;
476       q[0] = sphere_eval (u, v, m);
477       n[0] = calc_normal(q[0],
478                          sphere_eval (u+du/10, v, m),
479                          sphere_eval (u, v+dv/10, m));
480       glNormal3f(n[0].x,n[0].y,n[0].z);
481       if (!wire) do_color (i, colors);
482       glVertex3f(q[0].x,q[0].y,q[0].z);
483
484       q[1] = sphere_eval (u+du, v, m);
485       n[1] = calc_normal(q[1],
486                          sphere_eval (u+du+du/10, v, m),
487                          sphere_eval (u+du, v+dv/10, m));
488       glNormal3f(n[1].x,n[1].y,n[1].z);
489       if (!wire) do_color ((i+1)%res, colors);
490       glVertex3f(q[1].x,q[1].y,q[1].z);
491
492       q[2] = sphere_eval (u+du, v+dv, m);
493       n[2] = calc_normal(q[2],
494                          sphere_eval (u+du+du/10, v+dv, m),
495                          sphere_eval (u+du, v+dv+dv/10, m));
496       glNormal3f(n[2].x,n[2].y,n[2].z);
497       if (!wire) do_color ((i+1)%res, colors);
498       glVertex3f(q[2].x,q[2].y,q[2].z);
499
500       q[3] = sphere_eval (u,v+dv, m);
501       n[3] = calc_normal(q[3],
502                          sphere_eval (u+du/10, v+dv, m),
503                          sphere_eval (u, v+dv+dv/10, m));
504       glNormal3f(n[3].x,n[3].y,n[3].z);
505       if (!wire) do_color (i, colors);
506       glVertex3f(q[3].x,q[3].y,q[3].z);
507
508       polys++;
509
510 # define CHECK_BBOX(N) \
511          if (q[(N)].x < cc->bbox[0].x) cc->bbox[0].x = q[(N)].x; \
512          if (q[(N)].y < cc->bbox[0].y) cc->bbox[0].y = q[(N)].y; \
513          if (q[(N)].z < cc->bbox[0].z) cc->bbox[0].z = q[(N)].z; \
514          if (q[(N)].x > cc->bbox[1].x) cc->bbox[1].x = q[(N)].x; \
515          if (q[(N)].y > cc->bbox[1].y) cc->bbox[1].y = q[(N)].y; \
516          if (q[(N)].z > cc->bbox[1].z) cc->bbox[1].z = q[(N)].z
517
518       CHECK_BBOX(0);
519       CHECK_BBOX(1);
520       CHECK_BBOX(2);
521       CHECK_BBOX(3);
522 # undef CHECK_BBOX
523     }
524   }
525   glEnd();
526
527   {
528     GLfloat w = cc->bbox[1].x - cc->bbox[0].x;
529     GLfloat h = cc->bbox[1].y - cc->bbox[0].y;
530     GLfloat d = cc->bbox[1].z - cc->bbox[0].z;
531     GLfloat wh = (w > h ? w : h);
532     GLfloat hd = (h > d ? h : d);
533     GLfloat scale = (wh > hd ? wh : hd);
534
535     cc->scale = 1/scale;
536
537     if (wire < 2 && (do_bbox || do_grid))
538       {
539         GLfloat s = scale * 1.5;
540         glPushMatrix();
541         glScalef(s, s, s);
542         draw_bounding_box (mi);
543         glPopMatrix();
544       }
545   }
546   return polys;
547 }
548
549
550 static void
551 init_colors (ModeInfo *mi)
552 {
553   spheremonics_configuration *cc = &ccs[MI_SCREEN(mi)];
554   int i;
555   cc->ncolors = cc->resolution;
556   cc->colors = (XColor *) calloc(cc->ncolors, sizeof(XColor));
557   make_smooth_colormap (0, 0, 0,
558                         cc->colors, &cc->ncolors, 
559                         False, 0, False);
560
561   /* brighter colors, please... */
562   for (i = 0; i < cc->ncolors; i++)
563     {
564       cc->colors[i].red   = (cc->colors[i].red   / 2) + 32767;
565       cc->colors[i].green = (cc->colors[i].green / 2) + 32767;
566       cc->colors[i].blue  = (cc->colors[i].blue  / 2) + 32767;
567     }
568 }
569
570
571 /* Pick one of the parameters to the function and tweak it up or down.
572  */
573 static void
574 tweak_parameters (ModeInfo *mi)
575 {
576   spheremonics_configuration *cc = &ccs[MI_SCREEN(mi)];
577
578   /* If the -parameters command line option was specified, just use that
579      all the time.
580    */
581   if (static_parms &&
582       *static_parms &&
583       !!strcasecmp (static_parms, "(default)"))
584     {
585       unsigned long n;
586       char dummy;
587       if (8 == sscanf (static_parms, "%d %d %d %d %d %d %d %d %c",
588                        &cc->m[0], &cc->m[1], &cc->m[2], &cc->m[3],
589                        &cc->m[4], &cc->m[5], &cc->m[6], &cc->m[7],
590                        &dummy))
591         return;
592       else if (strlen (static_parms) == 8 &&
593                1 == sscanf (static_parms, "%lu %c", &n, &dummy))
594         {
595           const char *s = static_parms;
596           int i = 0;
597           while (*s)
598             cc->m[i++] = (*s++)-'0';
599           return;
600         }
601       fprintf (stderr,
602                "%s: -parameters must be a string of 8 ints (not \"%s\")\n",
603                progname, static_parms);
604       exit (1);
605     }
606
607   static_parms = 0;
608
609
610 # define SHIFT(N) do { \
611     int n = (N); \
612     cc->m[n] += cc->dm[n]; \
613     if (cc->m[n] <= 0) \
614       cc->m[n] = 0, cc->dm[n] = -cc->dm[n]; \
615     else if (cc->m[n] >= cc->m_max) \
616       cc->m[n] = cc->m_max, cc->dm[n] = -cc->dm[n]; \
617   } while(0)
618
619 /*    else if (cc->m[n] >= cc->m_max/2 && (! (random() % 3))) \
620       cc->m[n] = cc->m_max/2, cc->dm[n] = -cc->dm[n]; \
621 */
622
623   switch(random() % 8)
624     {
625     case 0: SHIFT(0); break;
626     case 1: SHIFT(1); break;
627     case 2: SHIFT(2); break;
628     case 3: SHIFT(3); break;
629     case 4: SHIFT(4); break;
630     case 5: SHIFT(5); break;
631     case 6: SHIFT(6); break;
632     case 7: SHIFT(7); break;
633     default: abort(); break;
634     }
635 # undef SHIFT
636
637 #if 0
638     printf ("%s: state: %d %d %d %d %d %d %d %d\n",
639             progname,
640             cc->m[0], cc->m[1], cc->m[2], cc->m[3],
641             cc->m[4], cc->m[5], cc->m[6], cc->m[7]);
642 #endif
643
644 }
645
646
647 static void
648 generate_spheremonics (ModeInfo *mi)
649 {
650   spheremonics_configuration *cc = &ccs[MI_SCREEN(mi)];
651   int wire = MI_IS_WIREFRAME(mi);
652
653   tweak_parameters (mi);
654
655   if (!cc->done_once || (0 == (random() % 20)))
656     {
657       init_colors (mi);
658       cc->done_once = True;
659     }
660
661   {
662     glNewList(cc->dlist, GL_COMPILE);
663     cc->polys1 = unit_spheremonics (mi, cc->resolution, wire,cc->m,cc->colors);
664     glEndList();
665
666     glNewList(cc->dlist2, GL_COMPILE);
667     glPushAttrib (GL_LIGHTING);
668     glDisable (GL_LIGHTING);
669     glPushMatrix();
670     glScalef (1.05, 1.05, 1.05);
671     cc->polys2 = unit_spheremonics (mi, cc->resolution, 2, cc->m, cc->colors);
672     glPopMatrix();
673     glPopAttrib();
674     glEndList();
675   }
676 }
677
678
679 \f
680
681 ENTRYPOINT void 
682 init_spheremonics (ModeInfo *mi)
683 {
684   spheremonics_configuration *cc;
685
686   if (!ccs) {
687     ccs = (spheremonics_configuration *)
688       calloc (MI_NUM_SCREENS(mi), sizeof (spheremonics_configuration));
689     if (!ccs) {
690       fprintf(stderr, "%s: out of memory\n", progname);
691       exit(1);
692     }
693
694     cc = &ccs[MI_SCREEN(mi)];
695   }
696
697   cc = &ccs[MI_SCREEN(mi)];
698
699   if ((cc->glx_context = init_GL(mi)) != NULL) {
700     gl_init(mi);
701     reshape_spheremonics (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
702   }
703
704   if (smooth_p) 
705     {
706       glEnable (GL_LINE_SMOOTH);
707       glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
708       glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 
709       glEnable (GL_BLEND);
710     }
711
712   {
713     Bool spinx=False, spiny=False, spinz=False;
714     double spin_speed   = 1.0;
715     double wander_speed = 0.03;
716
717     char *s = do_spin;
718     while (*s)
719       {
720         if      (*s == 'x' || *s == 'X') spinx = True;
721         else if (*s == 'y' || *s == 'Y') spiny = True;
722         else if (*s == 'z' || *s == 'Z') spinz = True;
723         else if (*s == '0') ;
724         else
725           {
726             fprintf (stderr,
727          "%s: spin must contain only the characters X, Y, or Z (not \"%s\")\n",
728                      progname, do_spin);
729             exit (1);
730           }
731         s++;
732       }
733
734     cc->rot = make_rotator (spinx ? spin_speed : 0,
735                             spinz ? spin_speed : 0,
736                             spiny ? spin_speed : 0,
737                             1.0,
738                             do_wander ? wander_speed : 0,
739                             (spinx && spiny && spinz));
740     cc->trackball = gltrackball_init ();
741   }
742
743   cc->tracer = -1;
744   cc->mesher = -1;
745
746   cc->resolution = res;
747
748   load_font (mi->dpy, "labelfont", &cc->font, &cc->font_list);
749
750   cc->dlist = glGenLists(1);
751   cc->dlist2 = glGenLists(1);
752
753   cc->m_max = 4; /* 9? */
754   {
755     unsigned int i;
756     for (i = 0; i < countof(cc->dm); i++)
757       cc->dm[i] = 1;  /* going up! */
758
759     /* Generate a few more times so we don't always start off with a sphere */
760     for (i = 0; i < 5; i++)
761       tweak_parameters (mi);
762   }
763
764   generate_spheremonics(mi);
765 }
766
767
768 ENTRYPOINT Bool
769 spheremonics_handle_event (ModeInfo *mi, XEvent *event)
770 {
771   spheremonics_configuration *cc = &ccs[MI_SCREEN(mi)];
772
773   if (event->xany.type == ButtonPress &&
774       event->xbutton.button == Button1)
775     {
776       cc->button_down_p = True;
777       gltrackball_start (cc->trackball,
778                          event->xbutton.x, event->xbutton.y,
779                          MI_WIDTH (mi), MI_HEIGHT (mi));
780       return True;
781     }
782   else if (event->xany.type == ButtonRelease &&
783            event->xbutton.button == Button1)
784     {
785       cc->button_down_p = False;
786       return True;
787     }
788   else if (event->xany.type == ButtonPress &&
789            (event->xbutton.button == Button4 ||
790             event->xbutton.button == Button5 ||
791             event->xbutton.button == Button6 ||
792             event->xbutton.button == Button7))
793     {
794       gltrackball_mousewheel (cc->trackball, event->xbutton.button, 10,
795                               !!event->xbutton.state);
796       return True;
797     }
798   else if (event->xany.type == KeyPress)
799     {
800       KeySym keysym;
801       char c = 0;
802       XLookupString (&event->xkey, &c, 1, &keysym, 0);
803
804       if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
805         {
806           cc->change_tick = duration;
807           return True;
808         }
809     }
810   else if (event->xany.type == MotionNotify &&
811            cc->button_down_p)
812     {
813       gltrackball_track (cc->trackball,
814                          event->xmotion.x, event->xmotion.y,
815                          MI_WIDTH (mi), MI_HEIGHT (mi));
816       return True;
817     }
818
819   return False;
820 }
821
822
823 ENTRYPOINT void
824 draw_spheremonics (ModeInfo *mi)
825 {
826   spheremonics_configuration *cc = &ccs[MI_SCREEN(mi)];
827   Display *dpy = MI_DISPLAY(mi);
828   Window window = MI_WINDOW(mi);
829
830   if (!cc->glx_context)
831     return;
832
833   glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(cc->glx_context));
834
835   glShadeModel(GL_SMOOTH);
836
837   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
838
839   glPushMatrix ();
840
841   glScalef(1.1, 1.1, 1.1);
842
843   {
844     double x, y, z;
845     get_position (cc->rot, &x, &y, &z, !cc->button_down_p);
846     glTranslatef((x - 0.5) * 8,
847                  (y - 0.5) * 6,
848                  (z - 0.5) * 8);
849
850     gltrackball_rotate (cc->trackball);
851
852     get_rotation (cc->rot, &x, &y, &z, !cc->button_down_p);
853     glRotatef (x * 360, 1.0, 0.0, 0.0);
854     glRotatef (y * 360, 0.0, 1.0, 0.0);
855     glRotatef (z * 360, 0.0, 0.0, 1.0);
856   }
857
858   glScalef(7,7,7);
859
860   mi->polygon_count = 0;
861
862   glScalef (cc->scale, cc->scale, cc->scale);
863   glCallList (cc->dlist);
864   mi->polygon_count += cc->polys1;
865
866   if (cc->mesher >= 0 /* || mouse_p */)
867     {
868       glCallList (cc->dlist2);
869       mi->polygon_count += cc->polys2;
870       if (cc->mesher >= 0)
871         cc->mesher--;
872     }
873   do_tracer(mi);
874
875
876   if (cc->button_down_p)
877     {
878       char buf[200];
879       sprintf (buf,
880                ((cc->m[0]<10 && cc->m[1]<10 && cc->m[2]<10 && cc->m[3]<10 &&
881                  cc->m[4]<10 && cc->m[5]<10 && cc->m[6]<10 && cc->m[7]<10)
882                 ? "%d%d%d%d%d%d%d%d"
883                 : "%d %d %d %d %d %d %d %d"),
884                cc->m[0], cc->m[1], cc->m[2], cc->m[3],
885                cc->m[4], cc->m[5], cc->m[6], cc->m[7]);
886
887       glColor3f(1.0, 1.0, 0.0);
888       print_gl_string (mi->dpy, cc->font, cc->font_list,
889                        mi->xgwa.width, mi->xgwa.height,
890                        10, mi->xgwa.height - 10,
891                        buf);
892     }
893
894   if (!static_parms)
895     {
896       if (cc->change_tick++ >= duration && !cc->button_down_p)
897         {
898           generate_spheremonics(mi);
899           cc->change_tick = 0;
900           cc->mesher = -1;  /* turn off the mesh when switching objects */
901         }
902     }
903
904   glPopMatrix();
905
906   if (mi->fps_p) do_fps (mi);
907   glFinish();
908
909   glXSwapBuffers(dpy, window);
910 }
911
912 XSCREENSAVER_MODULE ("Spheremonics", spheremonics)
913
914 #endif /* USE_GL */