From http://www.jwz.org/xscreensaver/xscreensaver-5.30.tar.gz
[xscreensaver] / hacks / glx / hypertorus.c
1 /* hypertorus --- Shows a hypertorus that rotates in 4d */
2
3 #if 0
4 static const char sccsid[] = "@(#)hypertorus.c  1.2 05/09/28 xlockmore";
5 #endif
6
7 /* Copyright (c) 2003-2009 Carsten Steger <carsten@mirsanmir.org>. */
8
9 /*
10  * Permission to use, copy, modify, and distribute this software and its
11  * documentation for any purpose and without fee is hereby granted,
12  * provided that the above copyright notice appear in all copies and that
13  * both that copyright notice and this permission notice appear in
14  * supporting documentation.
15  *
16  * This file is provided AS IS with no warranties of any kind.  The author
17  * shall have no liability with respect to the infringement of copyrights,
18  * trade secrets or any patents by this file or any part thereof.  In no
19  * event will the author be liable for any lost revenue or profits or
20  * other special, indirect and consequential damages.
21  *
22  * REVISION HISTORY:
23  * C. Steger - 03/05/18: Initial version
24  * C. Steger - 05/09/28: Added the spirals appearance mode
25  *                       and trackball support
26  * C. Steger - 07/01/23: Improved 4d trackball support
27  * C. Steger - 09/08/22: Removed check-config.pl warnings
28  */
29
30 /*
31  * This program shows the Clifford torus as it rotates in 4d.  The Clifford
32  * torus is a torus lies on the "surface" of the hypersphere in 4d.  The
33  * program projects the 4d torus to 3d using either a perspective or an
34  * orthographic projection.  Of the two alternatives, the perspecitve
35  * projection looks much more appealing.  In orthographic projections the
36  * torus degenerates into a doubly covered cylinder for some angles.  The
37  * projected 3d torus can then be projected to the screen either perspectively
38  * or orthographically.  There are three display modes for the torus: mesh
39  * (wireframe), solid, or transparent.  Furthermore, the appearance of the
40  * torus can be as a solid object or as a set of see-through bands or
41  * see-through spirals.  Finally, the colors with with the torus is drawn can
42  * be set to either two-sided or to colorwheel.  In the first case, the torus
43  * is drawn with red on the outside and green on the inside.  This mode
44  * enables you to see that the torus turns inside-out as it rotates in 4d.
45  * The second mode draws the torus in a fully saturated color wheel.  This
46  * gives a very nice effect when combined with the see-through bands or
47  * see-through spirals mode.  The rotation speed for each of the six planes
48  * around which the torus rotates can be chosen.  This program is very much
49  * inspired by Thomas Banchoff's book "Beyond the Third Dimension: Geometry,
50  * Computer Graphics, and Higher Dimensions", Scientific American Library,
51  * 1990.
52  */
53
54 #ifndef M_PI
55 #define M_PI 3.14159265358979323846
56 #endif
57
58 #define DISP_WIREFRAME             0
59 #define DISP_SURFACE               1
60 #define DISP_TRANSPARENT           2
61
62 #define APPEARANCE_SOLID           0
63 #define APPEARANCE_BANDS           1
64 #define APPEARANCE_SPIRALS         2
65
66 #define COLORS_TWOSIDED            0
67 #define COLORS_COLORWHEEL          1
68
69 #define DISP_3D_PERSPECTIVE        0
70 #define DISP_3D_ORTHOGRAPHIC       1
71
72 #define DISP_4D_PERSPECTIVE        0
73 #define DISP_4D_ORTHOGRAPHIC       1
74
75 #define DEF_DISPLAY_MODE           "surface"
76 #define DEF_APPEARANCE             "bands"
77 #define DEF_COLORS                 "colorwheel"
78 #define DEF_PROJECTION_3D          "perspective"
79 #define DEF_PROJECTION_4D          "perspective"
80 #define DEF_SPEEDWX                "1.1"
81 #define DEF_SPEEDWY                "1.3"
82 #define DEF_SPEEDWZ                "1.5"
83 #define DEF_SPEEDXY                "1.7"
84 #define DEF_SPEEDXZ                "1.9"
85 #define DEF_SPEEDYZ                "2.1"
86
87 #ifdef STANDALONE
88 # define DEFAULTS           "*delay:      25000 \n" \
89                             "*showFPS:    False \n" \
90
91 # define refresh_hypertorus 0
92 # include "xlockmore.h"         /* from the xscreensaver distribution */
93 #else  /* !STANDALONE */
94 # include "xlock.h"             /* from the xlockmore distribution */
95 #endif /* !STANDALONE */
96
97 #ifdef USE_GL
98 #ifndef HAVE_COCOA
99 # include <X11/keysym.h>
100 #endif
101
102 #include "gltrackball.h"
103
104
105 #ifdef USE_MODULES
106 ModStruct   hypertorus_description =
107 {"hypertorus", "init_hypertorus", "draw_hypertorus", "release_hypertorus",
108  "draw_hypertorus", "change_hypertorus", NULL, &hypertorus_opts,
109  25000, 1, 1, 1, 1.0, 4, "",
110  "Shows a hypertorus rotating in 4d", 0, NULL};
111
112 #endif
113
114
115 static char *mode;
116 static int display_mode;
117 static char *appear;
118 static int appearance;
119 static int num_spirals;
120 static char *color_mode;
121 static int colors;
122 static char *proj_3d;
123 static int projection_3d;
124 static char *proj_4d;
125 static int projection_4d;
126 static float speed_wx;
127 static float speed_wy;
128 static float speed_wz;
129 static float speed_xy;
130 static float speed_xz;
131 static float speed_yz;
132
133 static const float offset4d[4] = {  0.0,  0.0,  0.0,  2.0 };
134 static const float offset3d[4] = {  0.0,  0.0, -2.0,  0.0 };
135
136
137 static XrmOptionDescRec opts[] =
138 {
139   {"-mode",            ".displayMode",  XrmoptionSepArg, 0 },
140   {"-wireframe",       ".displayMode",  XrmoptionNoArg,  "wireframe" },
141   {"-surface",         ".displayMode",  XrmoptionNoArg,  "surface" },
142   {"-transparent",     ".displayMode",  XrmoptionNoArg,  "transparent" },
143   {"-appearance",      ".appearance",   XrmoptionSepArg, 0 },
144   {"-solid",           ".appearance",   XrmoptionNoArg,  "solid" },
145   {"-bands",           ".appearance",   XrmoptionNoArg,  "bands" },
146   {"-spirals-1",       ".appearance",   XrmoptionNoArg,  "spirals-1" },
147   {"-spirals-2",       ".appearance",   XrmoptionNoArg,  "spirals-2" },
148   {"-spirals-4",       ".appearance",   XrmoptionNoArg,  "spirals-4" },
149   {"-spirals-8",       ".appearance",   XrmoptionNoArg,  "spirals-8" },
150   {"-spirals-16",      ".appearance",   XrmoptionNoArg,  "spirals-16" },
151   {"-twosided",        ".colors",       XrmoptionNoArg,  "twosided" },
152   {"-colorwheel",      ".colors",       XrmoptionNoArg,  "colorwheel" },
153   {"-perspective-3d",  ".projection3d", XrmoptionNoArg,  "perspective" },
154   {"-orthographic-3d", ".projection3d", XrmoptionNoArg,  "orthographic" },
155   {"-perspective-4d",  ".projection4d", XrmoptionNoArg,  "perspective" },
156   {"-orthographic-4d", ".projection4d", XrmoptionNoArg,  "orthographic" },
157   {"-speed-wx",        ".speedwx",      XrmoptionSepArg, 0 },
158   {"-speed-wy",        ".speedwy",      XrmoptionSepArg, 0 },
159   {"-speed-wz",        ".speedwz",      XrmoptionSepArg, 0 },
160   {"-speed-xy",        ".speedxy",      XrmoptionSepArg, 0 },
161   {"-speed-xz",        ".speedxz",      XrmoptionSepArg, 0 },
162   {"-speed-yz",        ".speedyz",      XrmoptionSepArg, 0 }
163 };
164
165 static argtype vars[] =
166 {
167   { &mode,       "displayMode",  "DisplayMode",  DEF_DISPLAY_MODE,  t_String },
168   { &appear,     "appearance",   "Appearance",   DEF_APPEARANCE,    t_String },
169   { &color_mode, "colors",       "Colors",       DEF_COLORS,        t_String },
170   { &proj_3d,    "projection3d", "Projection3d", DEF_PROJECTION_3D, t_String },
171   { &proj_4d,    "projection4d", "Projection4d", DEF_PROJECTION_4D, t_String },
172   { &speed_wx,   "speedwx",      "Speedwx",      DEF_SPEEDWX,       t_Float},
173   { &speed_wy,   "speedwy",      "Speedwy",      DEF_SPEEDWY,       t_Float},
174   { &speed_wz,   "speedwz",      "Speedwz",      DEF_SPEEDWZ,       t_Float},
175   { &speed_xy,   "speedxy",      "Speedxy",      DEF_SPEEDXY,       t_Float},
176   { &speed_xz,   "speedxz",      "Speedxz",      DEF_SPEEDXZ,       t_Float},
177   { &speed_yz,   "speedyz",      "Speedyz",      DEF_SPEEDYZ,       t_Float}
178 };
179
180 static OptionStruct desc[] =
181 {
182   { "-wireframe",       "display the torus as a wireframe mesh" },
183   { "-surface",         "display the torus as a solid surface" },
184   { "-transparent",     "display the torus as a transparent surface" },
185   { "-solid",           "display the torus as a solid object" },
186   { "-bands",           "display the torus as see-through bands" },
187   { "-spirals-{1,2,4,8,16}", "display the torus as see-through spirals" },
188   { "-twosided",        "display the torus with two colors" },
189   { "-colorwheel",      "display the torus with a smooth color wheel" },
190   { "-perspective-3d",  "project the torus perspectively from 3d to 2d" },
191   { "-orthographic-3d", "project the torus orthographically from 3d to 2d" },
192   { "-perspective-4d",  "project the torus perspectively from 4d to 3d" },
193   { "-orthographic-4d", "project the torus orthographically from 4d to 3d" },
194   { "-speed-wx <arg>",  "rotation speed around the wx plane" },
195   { "-speed-wy <arg>",  "rotation speed around the wy plane" },
196   { "-speed-wz <arg>",  "rotation speed around the wz plane" },
197   { "-speed-xy <arg>",  "rotation speed around the xy plane" },
198   { "-speed-xz <arg>",  "rotation speed around the xz plane" },
199   { "-speed-yz <arg>",  "rotation speed around the yz plane" }
200 };
201
202 ENTRYPOINT ModeSpecOpt hypertorus_opts =
203 {sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
204
205
206 typedef struct {
207   GLint      WindH, WindW;
208   GLXContext *glx_context;
209   /* 4D rotation angles */
210   float alpha, beta, delta, zeta, eta, theta;
211   /* Aspect ratio of the current window */
212   float aspect;
213   /* Trackball states */
214   trackball_state *trackballs[2];
215   int current_trackball;
216   Bool button_pressed;
217
218   float speed_scale;
219
220 } hypertorusstruct;
221
222 static hypertorusstruct *hyper = (hypertorusstruct *) NULL;
223
224
225 /* Add a rotation around the wx-plane to the matrix m. */
226 static void rotatewx(float m[4][4], float phi)
227 {
228   float c, s, u, v;
229   int i;
230
231   phi *= M_PI/180.0;
232   c = cos(phi);
233   s = sin(phi);
234   for (i=0; i<4; i++)
235   {
236     u = m[i][1];
237     v = m[i][2];
238     m[i][1] = c*u+s*v;
239     m[i][2] = -s*u+c*v;
240   }
241 }
242
243
244 /* Add a rotation around the wy-plane to the matrix m. */
245 static void rotatewy(float m[4][4], float phi)
246 {
247   float c, s, u, v;
248   int i;
249
250   phi *= M_PI/180.0;
251   c = cos(phi);
252   s = sin(phi);
253   for (i=0; i<4; i++)
254   {
255     u = m[i][0];
256     v = m[i][2];
257     m[i][0] = c*u-s*v;
258     m[i][2] = s*u+c*v;
259   }
260 }
261
262
263 /* Add a rotation around the wz-plane to the matrix m. */
264 static void rotatewz(float m[4][4], float phi)
265 {
266   float c, s, u, v;
267   int i;
268
269   phi *= M_PI/180.0;
270   c = cos(phi);
271   s = sin(phi);
272   for (i=0; i<4; i++)
273   {
274     u = m[i][0];
275     v = m[i][1];
276     m[i][0] = c*u+s*v;
277     m[i][1] = -s*u+c*v;
278   }
279 }
280
281
282 /* Add a rotation around the xy-plane to the matrix m. */
283 static void rotatexy(float m[4][4], float phi)
284 {
285   float c, s, u, v;
286   int i;
287
288   phi *= M_PI/180.0;
289   c = cos(phi);
290   s = sin(phi);
291   for (i=0; i<4; i++)
292   {
293     u = m[i][2];
294     v = m[i][3];
295     m[i][2] = c*u+s*v;
296     m[i][3] = -s*u+c*v;
297   }
298 }
299
300
301 /* Add a rotation around the xz-plane to the matrix m. */
302 static void rotatexz(float m[4][4], float phi)
303 {
304   float c, s, u, v;
305   int i;
306
307   phi *= M_PI/180.0;
308   c = cos(phi);
309   s = sin(phi);
310   for (i=0; i<4; i++)
311   {
312     u = m[i][1];
313     v = m[i][3];
314     m[i][1] = c*u-s*v;
315     m[i][3] = s*u+c*v;
316   }
317 }
318
319
320 /* Add a rotation around the yz-plane to the matrix m. */
321 static void rotateyz(float m[4][4], float phi)
322 {
323   float c, s, u, v;
324   int i;
325
326   phi *= M_PI/180.0;
327   c = cos(phi);
328   s = sin(phi);
329   for (i=0; i<4; i++)
330   {
331     u = m[i][0];
332     v = m[i][3];
333     m[i][0] = c*u-s*v;
334     m[i][3] = s*u+c*v;
335   }
336 }
337
338
339 /* Compute the rotation matrix m from the rotation angles. */
340 static void rotateall(float al, float be, float de, float ze, float et,
341                       float th, float m[4][4])
342 {
343   int i, j;
344
345   for (i=0; i<4; i++)
346     for (j=0; j<4; j++)
347       m[i][j] = (i==j);
348   rotatewx(m,al);
349   rotatewy(m,be);
350   rotatewz(m,de);
351   rotatexz(m,et);
352   rotatexy(m,ze);
353   rotateyz(m,th);
354 }
355
356
357 /* Multiply two rotation matrices: o=m*n. */
358 static void mult_rotmat(float m[4][4], float n[4][4], float o[4][4])
359 {
360   int i, j, k;
361
362   for (i=0; i<4; i++)
363   {
364     for (j=0; j<4; j++)
365     {
366       o[i][j] = 0.0;
367       for (k=0; k<4; k++)
368         o[i][j] += m[i][k]*n[k][j];
369     }
370   }
371 }
372
373
374 /* Compute a 4D rotation matrix from two unit quaternions. */
375 static void quats_to_rotmat(float p[4], float q[4], float m[4][4])
376 {
377   double al, be, de, ze, et, th;
378   double r00, r01, r02, r12, r22;
379
380   r00 = 1.0-2.0*(p[1]*p[1]+p[2]*p[2]);
381   r01 = 2.0*(p[0]*p[1]+p[2]*p[3]);
382   r02 = 2.0*(p[2]*p[0]-p[1]*p[3]);
383   r12 = 2.0*(p[1]*p[2]+p[0]*p[3]);
384   r22 = 1.0-2.0*(p[1]*p[1]+p[0]*p[0]);
385
386   al = atan2(-r12,r22)*180.0/M_PI;
387   be = atan2(r02,sqrt(r00*r00+r01*r01))*180.0/M_PI;
388   de = atan2(-r01,r00)*180.0/M_PI;
389
390   r00 = 1.0-2.0*(q[1]*q[1]+q[2]*q[2]);
391   r01 = 2.0*(q[0]*q[1]+q[2]*q[3]);
392   r02 = 2.0*(q[2]*q[0]-q[1]*q[3]);
393   r12 = 2.0*(q[1]*q[2]+q[0]*q[3]);
394   r22 = 1.0-2.0*(q[1]*q[1]+q[0]*q[0]);
395
396   et = atan2(-r12,r22)*180.0/M_PI;
397   th = atan2(r02,sqrt(r00*r00+r01*r01))*180.0/M_PI;
398   ze = atan2(-r01,r00)*180.0/M_PI;
399
400   rotateall(al,be,de,ze,et,-th,m);
401 }
402
403
404 /* Compute a fully saturated and bright color based on an angle. */
405 static void color(double angle)
406 {
407   int s;
408   double t;
409   float color[4];
410
411   if (colors != COLORS_COLORWHEEL)
412     return;
413
414   if (angle >= 0.0)
415     angle = fmod(angle,2*M_PI);
416   else
417     angle = fmod(angle,-2*M_PI);
418   s = floor(angle/(M_PI/3));
419   t = angle/(M_PI/3)-s;
420   if (s >= 6)
421     s = 0;
422   switch (s)
423   {
424     case 0:
425       color[0] = 1.0;
426       color[1] = t;
427       color[2] = 0.0;
428       break;
429     case 1:
430       color[0] = 1.0-t;
431       color[1] = 1.0;
432       color[2] = 0.0;
433       break;
434     case 2:
435       color[0] = 0.0;
436       color[1] = 1.0;
437       color[2] = t;
438       break;
439     case 3:
440       color[0] = 0.0;
441       color[1] = 1.0-t;
442       color[2] = 1.0;
443       break;
444     case 4:
445       color[0] = t;
446       color[1] = 0.0;
447       color[2] = 1.0;
448       break;
449     case 5:
450       color[0] = 1.0;
451       color[1] = 0.0;
452       color[2] = 1.0-t;
453       break;
454   }
455   if (display_mode == DISP_TRANSPARENT)
456     color[3] = 0.7;
457   else
458     color[3] = 1.0;
459   glColor3fv(color);
460   glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,color);
461 }
462
463
464 /* Draw a hypertorus projected into 3D.  Note that the spirals appearance
465    will only work correctly if numu and numv are set to 64 or any higher
466    power of 2.  Similarly, the banded appearance will only work correctly
467    if numu and numv are divisible by 4. */
468 static int hypertorus(ModeInfo *mi, double umin, double umax, double vmin,
469                        double vmax, int numu, int numv)
470 {
471   int polys = 0;
472   static const GLfloat mat_diff_red[]         = { 1.0, 0.0, 0.0, 1.0 };
473   static const GLfloat mat_diff_green[]       = { 0.0, 1.0, 0.0, 1.0 };
474   static const GLfloat mat_diff_trans_red[]   = { 1.0, 0.0, 0.0, 0.7 };
475   static const GLfloat mat_diff_trans_green[] = { 0.0, 1.0, 0.0, 0.7 };
476   float p[3], pu[3], pv[3], n[3], mat[4][4];
477   int i, j, k, l, m, b, skew;
478   double u, v, ur, vr;
479   double cu, su, cv, sv;
480   double xx[4], xxu[4], xxv[4], x[4], xu[4], xv[4];
481   double r, s, t;
482   float q1[4], q2[4], r1[4][4], r2[4][4];
483   hypertorusstruct *hp = &hyper[MI_SCREEN(mi)];
484
485   rotateall(hp->alpha,hp->beta,hp->delta,hp->zeta,hp->eta,hp->theta,r1);
486
487   gltrackball_get_quaternion(hp->trackballs[0],q1);
488   gltrackball_get_quaternion(hp->trackballs[1],q2);
489   quats_to_rotmat(q1,q2,r2);
490
491   mult_rotmat(r2,r1,mat);
492
493   if (colors != COLORS_COLORWHEEL)
494   {
495     glColor3fv(mat_diff_red);
496     if (display_mode == DISP_TRANSPARENT)
497     {
498       glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_diff_trans_red);
499       glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_trans_green);
500     }
501     else
502     {
503       glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_diff_red);
504       glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_green);
505     }
506   }
507
508   skew = num_spirals;
509   ur = umax-umin;
510   vr = vmax-vmin;
511   for (i=0; i<numu; i++)
512   {
513     if ((appearance == APPEARANCE_BANDS ||
514          appearance == APPEARANCE_SPIRALS) && ((i & 3) >= 2))
515       continue;
516     if (display_mode == DISP_WIREFRAME)
517       glBegin(GL_QUAD_STRIP);
518     else
519       glBegin(GL_TRIANGLE_STRIP);
520     for (j=0; j<=numv; j++)
521     {
522       for (k=0; k<=1; k++)
523       {
524         l = (i+k);
525         m = j;
526         u = ur*l/numu+umin;
527         v = vr*m/numv+vmin;
528         if (appearance == APPEARANCE_SPIRALS)
529         {
530           u += 4.0*skew/numv*v;
531           b = ((i/4)&(skew-1))*(numu/(4*skew));
532           color(ur*4*b/numu+umin);
533         }
534         else
535         {
536           color(u);
537         }
538         cu = cos(u);
539         su = sin(u);
540         cv = cos(v);
541         sv = sin(v);
542         xx[0] = cu;
543         xx[1] = su;
544         xx[2] = cv;
545         xx[3] = sv;
546         xxu[0] = -su;
547         xxu[1] = cu;
548         xxu[2] = 0.0;
549         xxu[3] = 0.0;
550         xxv[0] = 0.0;
551         xxv[1] = 0.0;
552         xxv[2] = -sv;
553         xxv[3] = cv;
554         for (l=0; l<4; l++)
555         {
556           r = 0.0;
557           s = 0.0;
558           t = 0.0;
559           for (m=0; m<4; m++)
560           {
561             r += mat[l][m]*xx[m];
562             s += mat[l][m]*xxu[m];
563             t += mat[l][m]*xxv[m];
564           }
565           x[l] = r;
566           xu[l] = s;
567           xv[l] = t;
568         }
569         if (projection_4d == DISP_4D_ORTHOGRAPHIC)
570         {
571           for (l=0; l<3; l++)
572           {
573             p[l] = (x[l]+offset4d[l])/1.5+offset3d[l];
574             pu[l] = xu[l];
575             pv[l] = xv[l];
576           }
577         }
578         else
579         {
580           s = x[3]+offset4d[3];
581           t = s*s;
582           for (l=0; l<3; l++)
583           {
584             r = x[l]+offset4d[l];
585             p[l] = r/s+offset3d[l];
586             pu[l] = (xu[l]*s-r*xu[3])/t;
587             pv[l] = (xv[l]*s-r*xv[3])/t;
588           }
589         }
590         n[0] = pu[1]*pv[2]-pu[2]*pv[1];
591         n[1] = pu[2]*pv[0]-pu[0]*pv[2];
592         n[2] = pu[0]*pv[1]-pu[1]*pv[0];
593         t = sqrt(n[0]*n[0]+n[1]*n[1]+n[2]*n[2]);
594         n[0] /= t;
595         n[1] /= t;
596         n[2] /= t;
597         glNormal3fv(n);
598         glVertex3fv(p);
599         polys++;
600       }
601     }
602     glEnd();
603   }
604   polys /= 2;
605   return polys;
606 }
607
608
609 static void init(ModeInfo *mi)
610 {
611   static const GLfloat light_ambient[]  = { 0.0, 0.0, 0.0, 1.0 };
612   static const GLfloat light_diffuse[]  = { 1.0, 1.0, 1.0, 1.0 };
613   static const GLfloat light_specular[] = { 1.0, 1.0, 1.0, 1.0 };
614   static const GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 };
615   static const GLfloat mat_specular[]   = { 1.0, 1.0, 1.0, 1.0 };
616   hypertorusstruct *hp = &hyper[MI_SCREEN(mi)];
617
618   hp->alpha = 0.0;
619   hp->beta = 0.0;
620   hp->delta = 0.0;
621   hp->zeta = 0.0;
622   hp->eta = 0.0;
623   hp->theta = 0.0;
624
625   glMatrixMode(GL_PROJECTION);
626   glLoadIdentity();
627   if (projection_3d == DISP_3D_PERSPECTIVE)
628     gluPerspective(60.0,1.0,0.1,100.0);
629   else
630     glOrtho(-1.0,1.0,-1.0,1.0,0.1,100.0);;
631   glMatrixMode(GL_MODELVIEW);
632   glLoadIdentity();
633
634 # ifdef HAVE_JWZGLES /* #### glPolygonMode other than GL_FILL unimplemented */
635   if (display_mode == DISP_WIREFRAME)
636     display_mode = DISP_SURFACE;
637 # endif
638
639   if (display_mode == DISP_SURFACE)
640   {
641     glEnable(GL_DEPTH_TEST);
642     glDepthFunc(GL_LESS);
643     glShadeModel(GL_SMOOTH);
644     glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
645     glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,GL_TRUE);
646     glEnable(GL_LIGHTING);
647     glEnable(GL_LIGHT0);
648     glLightfv(GL_LIGHT0,GL_AMBIENT,light_ambient);
649     glLightfv(GL_LIGHT0,GL_DIFFUSE,light_diffuse);
650     glLightfv(GL_LIGHT0,GL_SPECULAR,light_specular);
651     glLightfv(GL_LIGHT0,GL_POSITION,light_position);
652     glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,mat_specular);
653     glMaterialf(GL_FRONT_AND_BACK,GL_SHININESS,50.0);
654     glDepthMask(GL_TRUE);
655     glDisable(GL_BLEND);
656   }
657   else if (display_mode == DISP_TRANSPARENT)
658   {
659     glDisable(GL_DEPTH_TEST);
660     glShadeModel(GL_SMOOTH);
661     glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
662     glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,GL_TRUE);
663     glEnable(GL_LIGHTING);
664     glEnable(GL_LIGHT0);
665     glLightfv(GL_LIGHT0,GL_AMBIENT,light_ambient);
666     glLightfv(GL_LIGHT0,GL_DIFFUSE,light_diffuse);
667     glLightfv(GL_LIGHT0,GL_SPECULAR,light_specular);
668     glLightfv(GL_LIGHT0,GL_POSITION,light_position);
669     glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,mat_specular);
670     glMaterialf(GL_FRONT_AND_BACK,GL_SHININESS,50.0);
671     glDepthMask(GL_FALSE);
672     glEnable(GL_BLEND);
673     glBlendFunc(GL_SRC_ALPHA,GL_ONE);
674   }
675   else  /* display_mode == DISP_WIREFRAME */
676   {
677     glDisable(GL_DEPTH_TEST);
678     glShadeModel(GL_FLAT);
679     glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
680     glDisable(GL_LIGHTING);
681     glDisable(GL_LIGHT0);
682     glDisable(GL_BLEND);
683   }
684 }
685
686
687 /* Redisplay the hypertorus. */
688 static void display_hypertorus(ModeInfo *mi)
689 {
690   hypertorusstruct *hp = &hyper[MI_SCREEN(mi)];
691
692   if (!hp->button_pressed)
693   {
694     hp->alpha += speed_wx * hp->speed_scale;
695     if (hp->alpha >= 360.0)
696       hp->alpha -= 360.0;
697     hp->beta += speed_wy * hp->speed_scale;
698     if (hp->beta >= 360.0)
699       hp->beta -= 360.0;
700     hp->delta += speed_wz * hp->speed_scale;
701     if (hp->delta >= 360.0)
702       hp->delta -= 360.0;
703     hp->zeta += speed_xy * hp->speed_scale;
704     if (hp->zeta >= 360.0)
705       hp->zeta -= 360.0;
706     hp->eta += speed_xz * hp->speed_scale;
707     if (hp->eta >= 360.0)
708       hp->eta -= 360.0;
709     hp->theta += speed_yz * hp->speed_scale;
710     if (hp->theta >= 360.0)
711       hp->theta -= 360.0;
712   }
713
714   glMatrixMode(GL_PROJECTION);
715   glLoadIdentity();
716   if (projection_3d == DISP_3D_ORTHOGRAPHIC)
717   {
718     if (hp->aspect >= 1.0)
719       glOrtho(-hp->aspect,hp->aspect,-1.0,1.0,0.1,100.0);
720     else
721       glOrtho(-1.0,1.0,-1.0/hp->aspect,1.0/hp->aspect,0.1,100.0);
722   }
723   else
724   {
725     gluPerspective(60.0,hp->aspect,0.1,100.0);
726   }
727   glMatrixMode(GL_MODELVIEW);
728   glLoadIdentity();
729
730   mi->polygon_count = hypertorus(mi,0.0,2.0*M_PI,0.0,2.0*M_PI,64,64);
731 }
732
733
734 ENTRYPOINT void reshape_hypertorus(ModeInfo *mi, int width, int height)
735 {
736   hypertorusstruct *hp = &hyper[MI_SCREEN(mi)];
737
738   hp->WindW = (GLint)width;
739   hp->WindH = (GLint)height;
740   glViewport(0,0,width,height);
741   hp->aspect = (GLfloat)width/(GLfloat)height;
742 }
743
744
745 ENTRYPOINT Bool hypertorus_handle_event(ModeInfo *mi, XEvent *event)
746 {
747   hypertorusstruct *hp = &hyper[MI_SCREEN(mi)];
748   KeySym  sym = 0;
749   char c = 0;
750
751   if (event->xany.type == KeyPress || event->xany.type == KeyRelease)
752     XLookupString (&event->xkey, &c, 1, &sym, 0);
753
754   if (event->xany.type == ButtonPress &&
755       event->xbutton.button == Button1)
756   {
757     hp->button_pressed = True;
758     gltrackball_start(hp->trackballs[hp->current_trackball],
759                       event->xbutton.x, event->xbutton.y,
760                       MI_WIDTH(mi), MI_HEIGHT(mi));
761     return True;
762   }
763   else if (event->xany.type == ButtonRelease &&
764            event->xbutton.button == Button1)
765   {
766     hp->button_pressed = False;
767     return True;
768   }
769   else if (event->xany.type == KeyPress)
770   {
771     if (sym == XK_Shift_L || sym == XK_Shift_R)
772     {
773       hp->current_trackball = 1;
774       if (hp->button_pressed)
775         gltrackball_start(hp->trackballs[hp->current_trackball],
776                           event->xbutton.x, event->xbutton.y,
777                           MI_WIDTH(mi), MI_HEIGHT(mi));
778       return True;
779     }
780   }
781   else if (event->xany.type == KeyRelease)
782   {
783     if (sym == XK_Shift_L || sym == XK_Shift_R)
784     {
785       hp->current_trackball = 0;
786       if (hp->button_pressed)
787         gltrackball_start(hp->trackballs[hp->current_trackball],
788                           event->xbutton.x, event->xbutton.y,
789                           MI_WIDTH(mi), MI_HEIGHT(mi));
790       return True;
791     }
792   }
793   else if (event->xany.type == MotionNotify && hp->button_pressed)
794   {
795     gltrackball_track(hp->trackballs[hp->current_trackball],
796                       event->xmotion.x, event->xmotion.y,
797                       MI_WIDTH(mi), MI_HEIGHT(mi));
798     return True;
799   }
800
801   return False;
802 }
803
804
805 /*
806  *-----------------------------------------------------------------------------
807  *-----------------------------------------------------------------------------
808  *    Xlock hooks.
809  *-----------------------------------------------------------------------------
810  *-----------------------------------------------------------------------------
811  */
812
813 /*
814  *-----------------------------------------------------------------------------
815  *    Initialize hypertorus.  Called each time the window changes.
816  *-----------------------------------------------------------------------------
817  */
818
819 ENTRYPOINT void init_hypertorus(ModeInfo *mi)
820 {
821   hypertorusstruct *hp;
822
823   if (hyper == NULL)
824   {
825     hyper = (hypertorusstruct *)calloc(MI_NUM_SCREENS(mi),
826                                        sizeof(hypertorusstruct));
827     if (hyper == NULL)
828       return;
829   }
830   hp = &hyper[MI_SCREEN(mi)];
831
832   
833   hp->trackballs[0] = gltrackball_init(True);
834   hp->trackballs[1] = gltrackball_init(True);
835   hp->current_trackball = 0;
836   hp->button_pressed = False;
837
838   /* Set the display mode. */
839   if (!strcasecmp(mode,"wireframe") || !strcasecmp(mode,"0"))
840   {
841     display_mode = DISP_WIREFRAME;
842   }
843   else if (!strcasecmp(mode,"surface") || !strcasecmp(mode,"1"))
844   {
845     display_mode = DISP_SURFACE;
846   }
847   else if (!strcasecmp(mode,"transparent") || !strcasecmp(mode,"2"))
848   {
849     display_mode = DISP_TRANSPARENT;
850   }
851   else
852   {
853     display_mode = DISP_SURFACE;
854   }
855
856   /* Set the appearance. */
857   if (!strcasecmp(appear,"solid") || !strcasecmp(appear,"0"))
858   {
859     appearance = APPEARANCE_SOLID;
860   }
861   else if (!strcasecmp(appear,"bands") || !strcasecmp(appear,"1"))
862   {
863     appearance = APPEARANCE_BANDS;
864     num_spirals = 0;
865   }
866   else if (!strcasecmp(appear,"spirals-1") || !strcasecmp(appear,"3"))
867   {
868     appearance = APPEARANCE_SPIRALS;
869     num_spirals = 1;
870   }
871   else if (!strcasecmp(appear,"spirals-2") || !strcasecmp(appear,"4"))
872   {
873     appearance = APPEARANCE_SPIRALS;
874     num_spirals = 2;
875   }
876   else if (!strcasecmp(appear,"spirals-4") || !strcasecmp(appear,"5"))
877   {
878     appearance = APPEARANCE_SPIRALS;
879     num_spirals = 4;
880   }
881   else if (!strcasecmp(appear,"spirals-8") || !strcasecmp(appear,"6"))
882   {
883     appearance = APPEARANCE_SPIRALS;
884     num_spirals = 8;
885   }
886   else if (!strcasecmp(appear,"spirals-16") || !strcasecmp(appear,"7"))
887   {
888     appearance = APPEARANCE_SPIRALS;
889     num_spirals = 16;
890   }
891   else
892   {
893     appearance = APPEARANCE_BANDS;
894     num_spirals = 0;
895   }
896
897   /* Set the color mode. */
898   if (!strcasecmp(color_mode,"twosided"))
899   {
900     colors = COLORS_TWOSIDED;
901   }
902   else if (!strcasecmp(color_mode,"colorwheel"))
903   {
904     colors = COLORS_COLORWHEEL;
905   }
906   else
907   {
908     colors = COLORS_COLORWHEEL;
909   }
910
911   /* Set the 3d projection mode. */
912   if (!strcasecmp(proj_3d,"perspective") || !strcasecmp(proj_3d,"0"))
913   {
914     projection_3d = DISP_3D_PERSPECTIVE;
915   }
916   else if (!strcasecmp(proj_3d,"orthographic") || !strcasecmp(proj_3d,"1"))
917   {
918     projection_3d = DISP_3D_ORTHOGRAPHIC;
919   }
920   else
921   {
922     projection_3d = DISP_3D_PERSPECTIVE;
923   }
924
925   /* Set the 4d projection mode. */
926   if (!strcasecmp(proj_4d,"perspective") || !strcasecmp(proj_4d,"0"))
927   {
928     projection_4d = DISP_4D_PERSPECTIVE;
929   }
930   else if (!strcasecmp(proj_4d,"orthographic") || !strcasecmp(proj_4d,"1"))
931   {
932     projection_4d = DISP_4D_ORTHOGRAPHIC;
933   }
934   else
935   {
936     projection_4d = DISP_4D_PERSPECTIVE;
937   }
938
939   /* make multiple screens rotate at slightly different rates. */
940   hp->speed_scale = 0.9 + frand(0.3);
941
942   if ((hp->glx_context = init_GL(mi)) != NULL)
943   {
944     reshape_hypertorus(mi,MI_WIDTH(mi),MI_HEIGHT(mi));
945     glDrawBuffer(GL_BACK);
946     init(mi);
947   }
948   else
949   {
950     MI_CLEARWINDOW(mi);
951   }
952 }
953
954 /*
955  *-----------------------------------------------------------------------------
956  *    Called by the mainline code periodically to update the display.
957  *-----------------------------------------------------------------------------
958  */
959 ENTRYPOINT void draw_hypertorus(ModeInfo *mi)
960 {
961   Display          *display = MI_DISPLAY(mi);
962   Window           window = MI_WINDOW(mi);
963   hypertorusstruct *hp;
964
965   if (hyper == NULL)
966     return;
967   hp = &hyper[MI_SCREEN(mi)];
968
969   MI_IS_DRAWN(mi) = True;
970   if (!hp->glx_context)
971     return;
972
973   glXMakeCurrent(display,window,*(hp->glx_context));
974
975   glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
976   glLoadIdentity();
977
978   display_hypertorus(mi);
979
980   if (MI_IS_FPS(mi))
981     do_fps (mi);
982
983   glFlush();
984
985   glXSwapBuffers(display,window);
986 }
987
988
989 /*
990  *-----------------------------------------------------------------------------
991  *    The display is being taken away from us.  Free up malloc'ed 
992  *      memory and X resources that we've alloc'ed.  Only called
993  *      once, we must zap everything for every screen.
994  *-----------------------------------------------------------------------------
995  */
996
997 ENTRYPOINT void release_hypertorus(ModeInfo *mi)
998 {
999   if (hyper != NULL)
1000   {
1001     int screen;
1002
1003     for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
1004     {
1005       hypertorusstruct *hp = &hyper[screen];
1006
1007       if (hp->glx_context)
1008         hp->glx_context = (GLXContext *)NULL;
1009     }
1010     (void) free((void *)hyper);
1011     hyper = (hypertorusstruct *)NULL;
1012   }
1013   FreeAllGL(mi);
1014 }
1015
1016 #ifndef STANDALONE
1017 ENTRYPOINT void change_hypertorus(ModeInfo *mi)
1018 {
1019   hypertorusstruct *hp = &hyper[MI_SCREEN(mi)];
1020
1021   if (!hp->glx_context)
1022     return;
1023
1024   glXMakeCurrent(MI_DISPLAY(mi),MI_WINDOW(mi),*(hp->glx_context));
1025   init(mi);
1026 }
1027 #endif /* !STANDALONE */
1028
1029 XSCREENSAVER_MODULE ("Hypertorus", hypertorus)
1030
1031 #endif /* USE_GL */