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