From http://www.jwz.org/xscreensaver/xscreensaver-5.16.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_JWZGLES
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   if (display_mode == DISP_WIREFRAME)
635   {
636     glDisable(GL_DEPTH_TEST);
637     glShadeModel(GL_FLAT);
638     glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
639     glDisable(GL_LIGHTING);
640     glDisable(GL_LIGHT0);
641     glDisable(GL_BLEND);
642   }
643   else if (display_mode == DISP_SURFACE)
644   {
645     glEnable(GL_DEPTH_TEST);
646     glDepthFunc(GL_LESS);
647     glShadeModel(GL_SMOOTH);
648     glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
649     glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,GL_TRUE);
650     glEnable(GL_LIGHTING);
651     glEnable(GL_LIGHT0);
652     glLightfv(GL_LIGHT0,GL_AMBIENT,light_ambient);
653     glLightfv(GL_LIGHT0,GL_DIFFUSE,light_diffuse);
654     glLightfv(GL_LIGHT0,GL_SPECULAR,light_specular);
655     glLightfv(GL_LIGHT0,GL_POSITION,light_position);
656     glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,mat_specular);
657     glMaterialf(GL_FRONT_AND_BACK,GL_SHININESS,50.0);
658     glDepthMask(GL_TRUE);
659     glDisable(GL_BLEND);
660   }
661   else if (display_mode == DISP_TRANSPARENT)
662   {
663     glDisable(GL_DEPTH_TEST);
664     glShadeModel(GL_SMOOTH);
665     glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
666     glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,GL_TRUE);
667     glEnable(GL_LIGHTING);
668     glEnable(GL_LIGHT0);
669     glLightfv(GL_LIGHT0,GL_AMBIENT,light_ambient);
670     glLightfv(GL_LIGHT0,GL_DIFFUSE,light_diffuse);
671     glLightfv(GL_LIGHT0,GL_SPECULAR,light_specular);
672     glLightfv(GL_LIGHT0,GL_POSITION,light_position);
673     glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,mat_specular);
674     glMaterialf(GL_FRONT_AND_BACK,GL_SHININESS,50.0);
675     glDepthMask(GL_FALSE);
676     glEnable(GL_BLEND);
677     glBlendFunc(GL_SRC_ALPHA,GL_ONE);
678   }
679   else
680   {
681     glDisable(GL_DEPTH_TEST);
682     glShadeModel(GL_FLAT);
683     glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
684     glDisable(GL_LIGHTING);
685     glDisable(GL_LIGHT0);
686     glDisable(GL_BLEND);
687   }
688 }
689
690
691 /* Redisplay the hypertorus. */
692 static void display_hypertorus(ModeInfo *mi)
693 {
694   hypertorusstruct *hp = &hyper[MI_SCREEN(mi)];
695
696   if (!hp->button_pressed)
697   {
698     hp->alpha += speed_wx * hp->speed_scale;
699     if (hp->alpha >= 360.0)
700       hp->alpha -= 360.0;
701     hp->beta += speed_wy * hp->speed_scale;
702     if (hp->beta >= 360.0)
703       hp->beta -= 360.0;
704     hp->delta += speed_wz * hp->speed_scale;
705     if (hp->delta >= 360.0)
706       hp->delta -= 360.0;
707     hp->zeta += speed_xy * hp->speed_scale;
708     if (hp->zeta >= 360.0)
709       hp->zeta -= 360.0;
710     hp->eta += speed_xz * hp->speed_scale;
711     if (hp->eta >= 360.0)
712       hp->eta -= 360.0;
713     hp->theta += speed_yz * hp->speed_scale;
714     if (hp->theta >= 360.0)
715       hp->theta -= 360.0;
716   }
717
718   glMatrixMode(GL_PROJECTION);
719   glLoadIdentity();
720   if (projection_3d == DISP_3D_ORTHOGRAPHIC)
721   {
722     if (hp->aspect >= 1.0)
723       glOrtho(-hp->aspect,hp->aspect,-1.0,1.0,0.1,100.0);
724     else
725       glOrtho(-1.0,1.0,-1.0/hp->aspect,1.0/hp->aspect,0.1,100.0);
726   }
727   else
728   {
729     gluPerspective(60.0,hp->aspect,0.1,100.0);
730   }
731   glMatrixMode(GL_MODELVIEW);
732   glLoadIdentity();
733
734   mi->polygon_count = hypertorus(mi,0.0,2.0*M_PI,0.0,2.0*M_PI,64,64);
735 }
736
737
738 ENTRYPOINT void reshape_hypertorus(ModeInfo *mi, int width, int height)
739 {
740   hypertorusstruct *hp = &hyper[MI_SCREEN(mi)];
741
742   hp->WindW = (GLint)width;
743   hp->WindH = (GLint)height;
744   glViewport(0,0,width,height);
745   hp->aspect = (GLfloat)width/(GLfloat)height;
746 }
747
748
749 ENTRYPOINT Bool hypertorus_handle_event(ModeInfo *mi, XEvent *event)
750 {
751   hypertorusstruct *hp = &hyper[MI_SCREEN(mi)];
752   KeySym  sym = 0;
753   char c = 0;
754
755   if (event->xany.type == KeyPress || event->xany.type == KeyRelease)
756     XLookupString (&event->xkey, &c, 1, &sym, 0);
757
758   if (event->xany.type == ButtonPress &&
759       event->xbutton.button == Button1)
760   {
761     hp->button_pressed = True;
762     gltrackball_start(hp->trackballs[hp->current_trackball],
763                       event->xbutton.x, event->xbutton.y,
764                       MI_WIDTH(mi), MI_HEIGHT(mi));
765     return True;
766   }
767   else if (event->xany.type == ButtonRelease &&
768            event->xbutton.button == Button1)
769   {
770     hp->button_pressed = False;
771     return True;
772   }
773   else if (event->xany.type == KeyPress)
774   {
775     if (sym == XK_Shift_L || sym == XK_Shift_R)
776     {
777       hp->current_trackball = 1;
778       if (hp->button_pressed)
779         gltrackball_start(hp->trackballs[hp->current_trackball],
780                           event->xbutton.x, event->xbutton.y,
781                           MI_WIDTH(mi), MI_HEIGHT(mi));
782       return True;
783     }
784   }
785   else if (event->xany.type == KeyRelease)
786   {
787     if (sym == XK_Shift_L || sym == XK_Shift_R)
788     {
789       hp->current_trackball = 0;
790       if (hp->button_pressed)
791         gltrackball_start(hp->trackballs[hp->current_trackball],
792                           event->xbutton.x, event->xbutton.y,
793                           MI_WIDTH(mi), MI_HEIGHT(mi));
794       return True;
795     }
796   }
797   else if (event->xany.type == MotionNotify && hp->button_pressed)
798   {
799     gltrackball_track(hp->trackballs[hp->current_trackball],
800                       event->xmotion.x, event->xmotion.y,
801                       MI_WIDTH(mi), MI_HEIGHT(mi));
802     return True;
803   }
804
805   return False;
806 }
807
808
809 /*
810  *-----------------------------------------------------------------------------
811  *-----------------------------------------------------------------------------
812  *    Xlock hooks.
813  *-----------------------------------------------------------------------------
814  *-----------------------------------------------------------------------------
815  */
816
817 /*
818  *-----------------------------------------------------------------------------
819  *    Initialize hypertorus.  Called each time the window changes.
820  *-----------------------------------------------------------------------------
821  */
822
823 ENTRYPOINT void init_hypertorus(ModeInfo *mi)
824 {
825   hypertorusstruct *hp;
826
827   if (hyper == NULL)
828   {
829     hyper = (hypertorusstruct *)calloc(MI_NUM_SCREENS(mi),
830                                        sizeof(hypertorusstruct));
831     if (hyper == NULL)
832       return;
833   }
834   hp = &hyper[MI_SCREEN(mi)];
835
836   
837   hp->trackballs[0] = gltrackball_init();
838   hp->trackballs[1] = gltrackball_init();
839   hp->current_trackball = 0;
840   hp->button_pressed = False;
841
842   /* Set the display mode. */
843   if (!strcasecmp(mode,"wireframe") || !strcasecmp(mode,"0"))
844   {
845     display_mode = DISP_WIREFRAME;
846   }
847   else if (!strcasecmp(mode,"surface") || !strcasecmp(mode,"1"))
848   {
849     display_mode = DISP_SURFACE;
850   }
851   else if (!strcasecmp(mode,"transparent") || !strcasecmp(mode,"2"))
852   {
853     display_mode = DISP_TRANSPARENT;
854   }
855   else
856   {
857     display_mode = DISP_SURFACE;
858   }
859
860   /* Set the appearance. */
861   if (!strcasecmp(appear,"solid") || !strcasecmp(appear,"0"))
862   {
863     appearance = APPEARANCE_SOLID;
864   }
865   else if (!strcasecmp(appear,"bands") || !strcasecmp(appear,"1"))
866   {
867     appearance = APPEARANCE_BANDS;
868     num_spirals = 0;
869   }
870   else if (!strcasecmp(appear,"spirals-1") || !strcasecmp(appear,"3"))
871   {
872     appearance = APPEARANCE_SPIRALS;
873     num_spirals = 1;
874   }
875   else if (!strcasecmp(appear,"spirals-2") || !strcasecmp(appear,"4"))
876   {
877     appearance = APPEARANCE_SPIRALS;
878     num_spirals = 2;
879   }
880   else if (!strcasecmp(appear,"spirals-4") || !strcasecmp(appear,"5"))
881   {
882     appearance = APPEARANCE_SPIRALS;
883     num_spirals = 4;
884   }
885   else if (!strcasecmp(appear,"spirals-8") || !strcasecmp(appear,"6"))
886   {
887     appearance = APPEARANCE_SPIRALS;
888     num_spirals = 8;
889   }
890   else if (!strcasecmp(appear,"spirals-16") || !strcasecmp(appear,"7"))
891   {
892     appearance = APPEARANCE_SPIRALS;
893     num_spirals = 16;
894   }
895   else
896   {
897     appearance = APPEARANCE_BANDS;
898     num_spirals = 0;
899   }
900
901   /* Set the color mode. */
902   if (!strcasecmp(color_mode,"twosided"))
903   {
904     colors = COLORS_TWOSIDED;
905   }
906   else if (!strcasecmp(color_mode,"colorwheel"))
907   {
908     colors = COLORS_COLORWHEEL;
909   }
910   else
911   {
912     colors = COLORS_COLORWHEEL;
913   }
914
915   /* Set the 3d projection mode. */
916   if (!strcasecmp(proj_3d,"perspective") || !strcasecmp(proj_3d,"0"))
917   {
918     projection_3d = DISP_3D_PERSPECTIVE;
919   }
920   else if (!strcasecmp(proj_3d,"orthographic") || !strcasecmp(proj_3d,"1"))
921   {
922     projection_3d = DISP_3D_ORTHOGRAPHIC;
923   }
924   else
925   {
926     projection_3d = DISP_3D_PERSPECTIVE;
927   }
928
929   /* Set the 4d projection mode. */
930   if (!strcasecmp(proj_4d,"perspective") || !strcasecmp(proj_4d,"0"))
931   {
932     projection_4d = DISP_4D_PERSPECTIVE;
933   }
934   else if (!strcasecmp(proj_4d,"orthographic") || !strcasecmp(proj_4d,"1"))
935   {
936     projection_4d = DISP_4D_ORTHOGRAPHIC;
937   }
938   else
939   {
940     projection_4d = DISP_4D_PERSPECTIVE;
941   }
942
943   /* make multiple screens rotate at slightly different rates. */
944   hp->speed_scale = 0.9 + frand(0.3);
945
946   if ((hp->glx_context = init_GL(mi)) != NULL)
947   {
948     reshape_hypertorus(mi,MI_WIDTH(mi),MI_HEIGHT(mi));
949     glDrawBuffer(GL_BACK);
950     init(mi);
951   }
952   else
953   {
954     MI_CLEARWINDOW(mi);
955   }
956 }
957
958 /*
959  *-----------------------------------------------------------------------------
960  *    Called by the mainline code periodically to update the display.
961  *-----------------------------------------------------------------------------
962  */
963 ENTRYPOINT void draw_hypertorus(ModeInfo *mi)
964 {
965   Display          *display = MI_DISPLAY(mi);
966   Window           window = MI_WINDOW(mi);
967   hypertorusstruct *hp;
968
969   if (hyper == NULL)
970     return;
971   hp = &hyper[MI_SCREEN(mi)];
972
973   MI_IS_DRAWN(mi) = True;
974   if (!hp->glx_context)
975     return;
976
977   glXMakeCurrent(display,window,*(hp->glx_context));
978
979   glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
980   glLoadIdentity();
981
982   display_hypertorus(mi);
983
984   if (MI_IS_FPS(mi))
985     do_fps (mi);
986
987   glFlush();
988
989   glXSwapBuffers(display,window);
990 }
991
992
993 /*
994  *-----------------------------------------------------------------------------
995  *    The display is being taken away from us.  Free up malloc'ed 
996  *      memory and X resources that we've alloc'ed.  Only called
997  *      once, we must zap everything for every screen.
998  *-----------------------------------------------------------------------------
999  */
1000
1001 ENTRYPOINT void release_hypertorus(ModeInfo *mi)
1002 {
1003   if (hyper != NULL)
1004   {
1005     int screen;
1006
1007     for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
1008     {
1009       hypertorusstruct *hp = &hyper[screen];
1010
1011       if (hp->glx_context)
1012         hp->glx_context = (GLXContext *)NULL;
1013     }
1014     (void) free((void *)hyper);
1015     hyper = (hypertorusstruct *)NULL;
1016   }
1017   FreeAllGL(mi);
1018 }
1019
1020 #ifndef STANDALONE
1021 ENTRYPOINT void change_hypertorus(ModeInfo *mi)
1022 {
1023   hypertorusstruct *hp = &hyper[MI_SCREEN(mi)];
1024
1025   if (!hp->glx_context)
1026     return;
1027
1028   glXMakeCurrent(MI_DISPLAY(mi),MI_WINDOW(mi),*(hp->glx_context));
1029   init(mi);
1030 }
1031 #endif /* !STANDALONE */
1032
1033 XSCREENSAVER_MODULE ("Hypertorus", hypertorus)
1034
1035 #endif /* USE_GL */