http://slackware.bholcomb.com/slackware/slackware-11.0/source/xap/xscreensaver/xscree...
[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 DEFAULTS           "*delay:      25000 \n" \
122                             "*showFPS:    False \n" \
123
124 # define refresh_hypertorus 0
125 # include "xlockmore.h"         /* from the xscreensaver distribution */
126 #else  /* !STANDALONE */
127 # include "xlock.h"             /* from the xlockmore distribution */
128 #endif /* !STANDALONE */
129
130 #ifdef USE_GL
131
132 #include <X11/keysym.h>
133
134 #include "gltrackball.h"
135
136
137 #ifdef USE_MODULES
138 ModStruct   hypertorus_description =
139 {"hypertorus", "init_hypertorus", "draw_hypertorus", "release_hypertorus",
140  "draw_hypertorus", "change_hypertorus", NULL, &hypertorus_opts,
141  25000, 1, 1, 1, 1.0, 4, "",
142  "Shows a hypertorus rotating in 4d", 0, NULL};
143
144 #endif
145
146
147 static int display_mode;
148 static int appearance;
149 static int num_spirals;
150 static int colors;
151 static int projection_3d;
152 static int projection_4d;
153 static float speed_wx;
154 static float speed_wy;
155 static float speed_wz;
156 static float speed_xy;
157 static float speed_xz;
158 static float speed_yz;
159
160 static const float offset4d[4] = {  0.0,  0.0,  0.0,  2.0 };
161 static const float offset3d[4] = {  0.0,  0.0, -2.0,  0.0 };
162
163
164 static XrmOptionDescRec opts[] =
165 {
166   {"-mode",            ".hypertorus.displayMode",  XrmoptionSepArg, 0 },
167   {"-wireframe",       ".hypertorus.displayMode",  XrmoptionNoArg,
168                        DISP_WIREFRAME_STR },
169   {"-surface",         ".hypertorus.displayMode",  XrmoptionNoArg,
170                        DISP_SURFACE_STR },
171   {"-transparent",     ".hypertorus.displayMode",  XrmoptionNoArg,
172                        DISP_TRANSPARENT_STR },
173
174   {"-appearance",      ".hypertorus.appearance",   XrmoptionSepArg, 0 },
175   {"-solid",           ".hypertorus.appearance",   XrmoptionNoArg,
176                        APPEARANCE_SOLID_STR },
177   {"-bands",           ".hypertorus.appearance",   XrmoptionNoArg,
178                        APPEARANCE_BANDS_STR },
179   {"-spirals-1",       ".hypertorus.appearance",   XrmoptionNoArg,
180                        APPEARANCE_SPIRALS_1_STR },
181   {"-spirals-2",       ".hypertorus.appearance",   XrmoptionNoArg,
182                        APPEARANCE_SPIRALS_2_STR },
183   {"-spirals-4",       ".hypertorus.appearance",   XrmoptionNoArg,
184                        APPEARANCE_SPIRALS_4_STR },
185   {"-spirals-8",       ".hypertorus.appearance",   XrmoptionNoArg,
186                        APPEARANCE_SPIRALS_8_STR },
187   {"-spirals-16",      ".hypertorus.appearance",   XrmoptionNoArg,
188                        APPEARANCE_SPIRALS_16_STR },
189   {"-twosided",        ".hypertorus.colors",       XrmoptionNoArg,
190                        COLORS_TWOSIDED_STR },
191   {"-colorwheel",      ".hypertorus.colors",       XrmoptionNoArg,
192                        COLORS_COLORWHEEL_STR },
193   {"-perspective-3d",  ".hypertorus.projection3d", XrmoptionNoArg,
194                        DISP_3D_PERSPECTIVE_STR },
195   {"-orthographic-3d", ".hypertorus.projection3d", XrmoptionNoArg,
196                        DISP_3D_ORTHOGRAPHIC_STR },
197   {"-perspective-4d",  ".hypertorus.projection4d", XrmoptionNoArg,
198                        DISP_4D_PERSPECTIVE_STR },
199   {"-orthographic-4d", ".hypertorus.projection4d", XrmoptionNoArg,
200                        DISP_4D_ORTHOGRAPHIC_STR },
201   {"-speed-wx",        ".hypertorus.speedwx",      XrmoptionSepArg, 0 },
202   {"-speed-wy",        ".hypertorus.speedwy",      XrmoptionSepArg, 0 },
203   {"-speed-wz",        ".hypertorus.speedwz",      XrmoptionSepArg, 0 },
204   {"-speed-xy",        ".hypertorus.speedxy",      XrmoptionSepArg, 0 },
205   {"-speed-xz",        ".hypertorus.speedxz",      XrmoptionSepArg, 0 },
206   {"-speed-yz",        ".hypertorus.speedyz",      XrmoptionSepArg, 0 }
207 };
208
209 static argtype vars[] =
210 {
211   { &display_mode,  "displayMode",  "DisplayMode",
212     DEF_DISPLAY_MODE,  t_Int },
213   { &appearance,    "appearance",   "Appearance",
214     DEF_APPEARANCE,    t_Int },
215   { &colors,        "colors",       "Colors",
216     DEF_COLORS,        t_Int },
217   { &projection_3d, "projection3d", "Projection3d",
218     DEF_3D_PROJECTION, t_Int },
219   { &projection_4d, "projection4d", "Projection4d",
220     DEF_4D_PROJECTION, t_Int },
221   { &speed_wx,      "speedwx",      "Speedwx",
222     DEF_DALPHA,        t_Float},
223   { &speed_wy,      "speedwy",      "Speedwy",
224     DEF_DBETA,         t_Float},
225   { &speed_wz,      "speedwz",      "Speedwz",
226     DEF_DDELTA,        t_Float},
227   { &speed_xy,      "speedxy",      "Speedxy",
228     DEF_DZETA,         t_Float},
229   { &speed_xz,      "speedxz",      "Speedxz",
230     DEF_DETA,          t_Float},
231   { &speed_yz,      "speedyz",      "Speedyz",
232     DEF_DTHETA,        t_Float}
233 };
234
235 static OptionStruct desc[] =
236 {
237   { "-wireframe",       "display the torus as a wireframe mesh" },
238   { "-surface",         "display the torus as a solid surface" },
239   { "-transparent",     "display the torus as a transparent surface" },
240   { "-solid",           "display the torus as a solid object" },
241   { "-bands",           "display the torus as see-through bands" },
242   { "-spirals-{1,2,4,8,16}", "display the torus as see-through spirals" },
243   { "-twosided",        "display the torus with two colors" },
244   { "-colorwheel",      "display the torus with a smooth color wheel" },
245   { "-perspective-3d",  "project the torus perspectively from 3d to 2d" },
246   { "-orthographic-3d", "project the torus orthographically from 3d to 2d" },
247   { "-perspective-4d",  "project the torus perspectively from 4d to 3d" },
248   { "-orthographic-4d", "project the torus orthographically from 4d to 3d" },
249   { "-speed-wx <arg>",  "rotation speed around the wx plane" },
250   { "-speed-wy <arg>",  "rotation speed around the wy plane" },
251   { "-speed-wz <arg>",  "rotation speed around the wz plane" },
252   { "-speed-xy <arg>",  "rotation speed around the xy plane" },
253   { "-speed-xz <arg>",  "rotation speed around the xz plane" },
254   { "-speed-yz <arg>",  "rotation speed around the yz plane" }
255 };
256
257 ENTRYPOINT ModeSpecOpt hypertorus_opts =
258 {sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
259
260
261 typedef struct {
262   GLint      WindH, WindW;
263   GLXContext *glx_context;
264   /* 4D rotation angles */
265   float alpha, beta, delta, zeta, eta, theta;
266   /* Aspect ratio of the current window */
267   float aspect;
268   /* Trackball states */
269   trackball_state *trackballs[2];
270   int current_trackball;
271   Bool button_pressed;
272
273   float speed_scale;
274
275 } hypertorusstruct;
276
277 static hypertorusstruct *hyper = (hypertorusstruct *) NULL;
278
279
280 /* Add a rotation around the wx-plane to the matrix m. */
281 static void rotatewx(float m[4][4], float phi)
282 {
283   float c, s, u, v;
284   int i;
285
286   phi *= M_PI/180.0;
287   c = cos(phi);
288   s = sin(phi);
289   for (i=0; i<4; i++)
290   {
291     u = m[i][1];
292     v = m[i][2];
293     m[i][1] = c*u+s*v;
294     m[i][2] = -s*u+c*v;
295   }
296 }
297
298
299 /* Add a rotation around the wy-plane to the matrix m. */
300 static void rotatewy(float m[4][4], float phi)
301 {
302   float c, s, u, v;
303   int i;
304
305   phi *= M_PI/180.0;
306   c = cos(phi);
307   s = sin(phi);
308   for (i=0; i<4; i++)
309   {
310     u = m[i][0];
311     v = m[i][2];
312     m[i][0] = c*u-s*v;
313     m[i][2] = s*u+c*v;
314   }
315 }
316
317
318 /* Add a rotation around the wz-plane to the matrix m. */
319 static void rotatewz(float m[4][4], float phi)
320 {
321   float c, s, u, v;
322   int i;
323
324   phi *= M_PI/180.0;
325   c = cos(phi);
326   s = sin(phi);
327   for (i=0; i<4; i++)
328   {
329     u = m[i][0];
330     v = m[i][1];
331     m[i][0] = c*u+s*v;
332     m[i][1] = -s*u+c*v;
333   }
334 }
335
336
337 /* Add a rotation around the xy-plane to the matrix m. */
338 static void rotatexy(float m[4][4], float phi)
339 {
340   float c, s, u, v;
341   int i;
342
343   phi *= M_PI/180.0;
344   c = cos(phi);
345   s = sin(phi);
346   for (i=0; i<4; i++)
347   {
348     u = m[i][2];
349     v = m[i][3];
350     m[i][2] = c*u+s*v;
351     m[i][3] = -s*u+c*v;
352   }
353 }
354
355
356 /* Add a rotation around the xz-plane to the matrix m. */
357 static void rotatexz(float m[4][4], float phi)
358 {
359   float c, s, u, v;
360   int i;
361
362   phi *= M_PI/180.0;
363   c = cos(phi);
364   s = sin(phi);
365   for (i=0; i<4; i++)
366   {
367     u = m[i][1];
368     v = m[i][3];
369     m[i][1] = c*u-s*v;
370     m[i][3] = s*u+c*v;
371   }
372 }
373
374
375 /* Add a rotation around the yz-plane to the matrix m. */
376 static void rotateyz(float m[4][4], float phi)
377 {
378   float c, s, u, v;
379   int i;
380
381   phi *= M_PI/180.0;
382   c = cos(phi);
383   s = sin(phi);
384   for (i=0; i<4; i++)
385   {
386     u = m[i][0];
387     v = m[i][3];
388     m[i][0] = c*u-s*v;
389     m[i][3] = s*u+c*v;
390   }
391 }
392
393
394 /* Compute the rotation matrix m from the rotation angles. */
395 static void rotateall(float al, float be, float de, float ze, float et,
396                       float th, float m[4][4])
397 {
398   int i, j;
399
400   for (i=0; i<4; i++)
401     for (j=0; j<4; j++)
402       m[i][j] = (i==j);
403   rotatewx(m,al);
404   rotatewy(m,be);
405   rotatewz(m,de);
406   rotatexy(m,ze);
407   rotatexz(m,et);
408   rotateyz(m,th);
409 }
410
411
412 /* Multiply two rotation matrices: o=m*n. */
413 static void mult_rotmat(float m[4][4], float n[4][4], float o[4][4])
414 {
415   int i, j, k;
416
417   for (i=0; i<4; i++)
418   {
419     for (j=0; j<4; j++)
420     {
421       o[i][j] = 0.0;
422       for (k=0; k<4; k++)
423         o[i][j] += m[i][k]*n[k][j];
424     }
425   }
426 }
427
428
429 /* Compute a 4D rotation matrix from two unit quaternions. */
430 static void quats_to_rotmat(float p[4], float q[4], float m[4][4])
431 {
432   double al, be, de, ze, et, th;
433   double r00, r01, r02, r12, r22;
434
435   r00 = 1.0-2.0*(p[1]*p[1]+p[2]*p[2]);
436   r01 = 2.0*(p[0]*p[1]+p[2]*p[3]);
437   r02 = 2.0*(p[2]*p[0]-p[1]*p[3]);
438   r12 = 2.0*(p[1]*p[2]+p[0]*p[3]);
439   r22 = 1.0-2.0*(p[1]*p[1]+p[0]*p[0]);
440
441   al = atan2(-r12,r22)*180.0/M_PI;
442   be = atan2(r02,sqrt(r00*r00+r01*r01))*180.0/M_PI;
443   de = atan2(-r01,r00)*180.0/M_PI;
444
445   r00 = 1.0-2.0*(q[1]*q[1]+q[2]*q[2]);
446   r01 = 2.0*(q[0]*q[1]+q[2]*q[3]);
447   r02 = 2.0*(q[2]*q[0]-q[1]*q[3]);
448   r12 = 2.0*(q[1]*q[2]+q[0]*q[3]);
449   r22 = 1.0-2.0*(q[1]*q[1]+q[0]*q[0]);
450
451   ze = atan2(-r12,r22)*180.0/M_PI;
452   et = atan2(r02,sqrt(r00*r00+r01*r01))*180.0/M_PI;
453   th = atan2(-r01,r00)*180.0/M_PI;
454
455   rotateall(al,be,de,ze,et,th,m);
456 }
457
458
459 /* Compute a fully saturated and bright color based on an angle. */
460 static void color(double angle)
461 {
462   int s;
463   double t;
464   float color[4];
465
466   if (colors != COLORS_COLORWHEEL)
467     return;
468
469   if (angle >= 0.0)
470     angle = fmod(angle,2*M_PI);
471   else
472     angle = fmod(angle,-2*M_PI);
473   s = floor(angle/(M_PI/3));
474   t = angle/(M_PI/3)-s;
475   if (s >= 6)
476     s = 0;
477   switch (s)
478   {
479     case 0:
480       color[0] = 1.0;
481       color[1] = t;
482       color[2] = 0.0;
483       break;
484     case 1:
485       color[0] = 1.0-t;
486       color[1] = 1.0;
487       color[2] = 0.0;
488       break;
489     case 2:
490       color[0] = 0.0;
491       color[1] = 1.0;
492       color[2] = t;
493       break;
494     case 3:
495       color[0] = 0.0;
496       color[1] = 1.0-t;
497       color[2] = 1.0;
498       break;
499     case 4:
500       color[0] = t;
501       color[1] = 0.0;
502       color[2] = 1.0;
503       break;
504     case 5:
505       color[0] = 1.0;
506       color[1] = 0.0;
507       color[2] = 1.0-t;
508       break;
509   }
510   if (display_mode == DISP_TRANSPARENT)
511     color[3] = 0.7;
512   else
513     color[3] = 1.0;
514   glColor3fv(color);
515   glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,color);
516 }
517
518
519 /* Draw a hypertorus projected into 3D.  Note that the spirals appearance
520    will only work correctly if numu and numv are set to 64 or any higher
521    power of 2.  Similarly, the banded appearance will only work correctly
522    if numu and numv are divisible by 4. */
523 static void hypertorus(ModeInfo *mi, double umin, double umax, double vmin,
524                        double vmax, int numu, int numv)
525 {
526   static const GLfloat mat_diff_red[]         = { 1.0, 0.0, 0.0, 1.0 };
527   static const GLfloat mat_diff_green[]       = { 0.0, 1.0, 0.0, 1.0 };
528   static const GLfloat mat_diff_trans_red[]   = { 1.0, 0.0, 0.0, 0.7 };
529   static const GLfloat mat_diff_trans_green[] = { 0.0, 1.0, 0.0, 0.7 };
530   float p[3], pu[3], pv[3], n[3], mat[4][4];
531   int i, j, k, l, m, b, skew;
532   double u, v, ur, vr;
533   double cu, su, cv, sv;
534   double xx[4], xxu[4], xxv[4], x[4], xu[4], xv[4];
535   double r, s, t;
536   float q1[4], q2[4], r1[4][4], r2[4][4];
537   hypertorusstruct *hp = &hyper[MI_SCREEN(mi)];
538
539   rotateall(hp->alpha,hp->beta,hp->delta,hp->zeta,hp->eta,hp->theta,r1);
540
541   gltrackball_get_quaternion(hp->trackballs[0],q1);
542   gltrackball_get_quaternion(hp->trackballs[1],q2);
543   quats_to_rotmat(q1,q2,r2);
544
545   mult_rotmat(r2,r1,mat);
546
547   if (colors != COLORS_COLORWHEEL)
548   {
549     glColor3fv(mat_diff_red);
550     if (display_mode == DISP_TRANSPARENT)
551     {
552       glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_diff_trans_red);
553       glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_trans_green);
554     }
555     else
556     {
557       glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,mat_diff_red);
558       glMaterialfv(GL_BACK,GL_AMBIENT_AND_DIFFUSE,mat_diff_green);
559     }
560   }
561
562   skew = num_spirals;
563   ur = umax-umin;
564   vr = vmax-vmin;
565   for (i=0; i<numu; i++)
566   {
567     if ((appearance == APPEARANCE_BANDS ||
568          appearance == APPEARANCE_SPIRALS) && ((i & 3) >= 2))
569       continue;
570     if (display_mode == DISP_WIREFRAME)
571       glBegin(GL_QUAD_STRIP);
572     else
573       glBegin(GL_TRIANGLE_STRIP);
574     for (j=0; j<=numv; j++)
575     {
576       for (k=0; k<=1; k++)
577       {
578         l = (i+k);
579         m = j;
580         u = ur*l/numu+umin;
581         v = vr*m/numv+vmin;
582         if (appearance == APPEARANCE_SPIRALS)
583         {
584           u += 4.0*skew/numv*v;
585           b = ((i/4)&(skew-1))*(numu/(4*skew));
586           color(ur*4*b/numu+umin);
587         }
588         else
589         {
590           color(u);
591         }
592         cu = cos(u);
593         su = sin(u);
594         cv = cos(v);
595         sv = sin(v);
596         xx[0] = cu;
597         xx[1] = su;
598         xx[2] = cv;
599         xx[3] = sv;
600         xxu[0] = -su;
601         xxu[1] = cu;
602         xxu[2] = 0.0;
603         xxu[3] = 0.0;
604         xxv[0] = 0.0;
605         xxv[1] = 0.0;
606         xxv[2] = -sv;
607         xxv[3] = cv;
608         for (l=0; l<4; l++)
609         {
610           r = 0.0;
611           s = 0.0;
612           t = 0.0;
613           for (m=0; m<4; m++)
614           {
615             r += mat[l][m]*xx[m];
616             s += mat[l][m]*xxu[m];
617             t += mat[l][m]*xxv[m];
618           }
619           x[l] = r;
620           xu[l] = s;
621           xv[l] = t;
622         }
623         if (projection_4d == DISP_4D_ORTHOGRAPHIC)
624         {
625           for (l=0; l<3; l++)
626           {
627             p[l] = (x[l]+offset4d[l])/1.5+offset3d[l];
628             pu[l] = xu[l];
629             pv[l] = xv[l];
630           }
631         }
632         else
633         {
634           s = x[3]+offset4d[3];
635           t = s*s;
636           for (l=0; l<3; l++)
637           {
638             r = x[l]+offset4d[l];
639             p[l] = r/s+offset3d[l];
640             pu[l] = (xu[l]*s-r*xu[3])/t;
641             pv[l] = (xv[l]*s-r*xv[3])/t;
642           }
643         }
644         n[0] = pu[1]*pv[2]-pu[2]*pv[1];
645         n[1] = pu[2]*pv[0]-pu[0]*pv[2];
646         n[2] = pu[0]*pv[1]-pu[1]*pv[0];
647         t = sqrt(n[0]*n[0]+n[1]*n[1]+n[2]*n[2]);
648         n[0] /= t;
649         n[1] /= t;
650         n[2] /= t;
651         glNormal3fv(n);
652         glVertex3fv(p);
653       }
654     }
655     glEnd();
656   }
657 }
658
659
660 static void init(ModeInfo *mi)
661 {
662   static const GLfloat light_ambient[]  = { 0.0, 0.0, 0.0, 1.0 };
663   static const GLfloat light_diffuse[]  = { 1.0, 1.0, 1.0, 1.0 };
664   static const GLfloat light_specular[] = { 1.0, 1.0, 1.0, 1.0 };
665   static const GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 };
666   static const GLfloat mat_specular[]   = { 1.0, 1.0, 1.0, 1.0 };
667   hypertorusstruct *hp = &hyper[MI_SCREEN(mi)];
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   hp->alpha = 0.0;
680   hp->beta = 0.0;
681   hp->delta = 0.0;
682   hp->zeta = 0.0;
683   hp->eta = 0.0;
684   hp->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(ModeInfo *mi)
754 {
755   hypertorusstruct *hp = &hyper[MI_SCREEN(mi)];
756
757   if (!hp->button_pressed)
758   {
759     hp->alpha += speed_wx * hp->speed_scale;
760     if (hp->alpha >= 360.0)
761       hp->alpha -= 360.0;
762     hp->beta += speed_wy * hp->speed_scale;
763     if (hp->beta >= 360.0)
764       hp->beta -= 360.0;
765     hp->delta += speed_wz * hp->speed_scale;
766     if (hp->delta >= 360.0)
767       hp->delta -= 360.0;
768     hp->zeta += speed_xy * hp->speed_scale;
769     if (hp->zeta >= 360.0)
770       hp->zeta -= 360.0;
771     hp->eta += speed_xz * hp->speed_scale;
772     if (hp->eta >= 360.0)
773       hp->eta -= 360.0;
774     hp->theta += speed_yz * hp->speed_scale;
775     if (hp->theta >= 360.0)
776       hp->theta -= 360.0;
777   }
778
779   glMatrixMode(GL_PROJECTION);
780   glLoadIdentity();
781   if (projection_3d == DISP_3D_ORTHOGRAPHIC)
782   {
783     if (hp->aspect >= 1.0)
784       glOrtho(-hp->aspect,hp->aspect,-1.0,1.0,0.1,100.0);
785     else
786       glOrtho(-1.0,1.0,-1.0/hp->aspect,1.0/hp->aspect,0.1,100.0);
787   }
788   else
789   {
790     gluPerspective(60.0,hp->aspect,0.1,100.0);
791   }
792   glMatrixMode(GL_MODELVIEW);
793   glLoadIdentity();
794
795   hypertorus(mi,0.0,2.0*M_PI,0.0,2.0*M_PI,64,64);
796 }
797
798
799 ENTRYPOINT void reshape_hypertorus(ModeInfo *mi, int width, int height)
800 {
801   hypertorusstruct *hp = &hyper[MI_SCREEN(mi)];
802
803   hp->WindW = (GLint)width;
804   hp->WindH = (GLint)height;
805   glViewport(0,0,width,height);
806   hp->aspect = (GLfloat)width/(GLfloat)height;
807 }
808
809
810 ENTRYPOINT Bool hypertorus_handle_event(ModeInfo *mi, XEvent *event)
811 {
812   Display *display = MI_DISPLAY(mi);
813   hypertorusstruct *hp = &hyper[MI_SCREEN(mi)];
814   KeySym  sym;
815
816   if (event->xany.type == ButtonPress &&
817       event->xbutton.button == Button1)
818   {
819     hp->button_pressed = True;
820     gltrackball_start(hp->trackballs[hp->current_trackball],
821                       event->xbutton.x, event->xbutton.y,
822                       MI_WIDTH(mi), MI_HEIGHT(mi));
823     return True;
824   }
825   else if (event->xany.type == ButtonRelease &&
826            event->xbutton.button == Button1)
827   {
828     hp->button_pressed = False;
829     return True;
830   }
831   else if (event->xany.type == KeyPress)
832   {
833     sym = XKeycodeToKeysym(display,event->xkey.keycode,0);
834     if (sym == XK_Shift_L || sym == XK_Shift_R)
835     {
836       hp->current_trackball = 1;
837       if (hp->button_pressed)
838         gltrackball_start(hp->trackballs[hp->current_trackball],
839                           event->xbutton.x, event->xbutton.y,
840                           MI_WIDTH(mi), MI_HEIGHT(mi));
841       return True;
842     }
843   }
844   else if (event->xany.type == KeyRelease)
845   {
846     sym = XKeycodeToKeysym(display,event->xkey.keycode,0);
847     if (sym == XK_Shift_L || sym == XK_Shift_R)
848     {
849       hp->current_trackball = 0;
850       if (hp->button_pressed)
851         gltrackball_start(hp->trackballs[hp->current_trackball],
852                           event->xbutton.x, event->xbutton.y,
853                           MI_WIDTH(mi), MI_HEIGHT(mi));
854       return True;
855     }
856   }
857   else if (event->xany.type == MotionNotify && hp->button_pressed)
858   {
859     gltrackball_track(hp->trackballs[hp->current_trackball],
860                       event->xmotion.x, event->xmotion.y,
861                       MI_WIDTH(mi), MI_HEIGHT(mi));
862     return True;
863   }
864
865   return False;
866 }
867
868
869 /*
870  *-----------------------------------------------------------------------------
871  *-----------------------------------------------------------------------------
872  *    Xlock hooks.
873  *-----------------------------------------------------------------------------
874  *-----------------------------------------------------------------------------
875  */
876
877 /*
878  *-----------------------------------------------------------------------------
879  *    Initialize hypertorus.  Called each time the window changes.
880  *-----------------------------------------------------------------------------
881  */
882
883 ENTRYPOINT void init_hypertorus(ModeInfo *mi)
884 {
885   hypertorusstruct *hp;
886
887   if (hyper == NULL)
888   {
889     hyper = (hypertorusstruct *)calloc(MI_NUM_SCREENS(mi),
890                                        sizeof(hypertorusstruct));
891     if (hyper == NULL)
892       return;
893   }
894   hp = &hyper[MI_SCREEN(mi)];
895
896   
897   hp->trackballs[0] = gltrackball_init();
898   hp->trackballs[1] = gltrackball_init();
899   hp->current_trackball = 0;
900   hp->button_pressed = False;
901
902   /* make multiple screens rotate at slightly different rates. */
903   hp->speed_scale = 0.9 + frand(0.3);
904
905   if ((hp->glx_context = init_GL(mi)) != NULL)
906   {
907     reshape_hypertorus(mi,MI_WIDTH(mi),MI_HEIGHT(mi));
908     glDrawBuffer(GL_BACK);
909     init(mi);
910   }
911   else
912   {
913     MI_CLEARWINDOW(mi);
914   }
915 }
916
917 /*
918  *-----------------------------------------------------------------------------
919  *    Called by the mainline code periodically to update the display.
920  *-----------------------------------------------------------------------------
921  */
922 ENTRYPOINT void draw_hypertorus(ModeInfo *mi)
923 {
924   Display          *display = MI_DISPLAY(mi);
925   Window           window = MI_WINDOW(mi);
926   hypertorusstruct *hp;
927
928   if (hyper == NULL)
929     return;
930   hp = &hyper[MI_SCREEN(mi)];
931
932   MI_IS_DRAWN(mi) = True;
933   if (!hp->glx_context)
934     return;
935
936   glXMakeCurrent(display,window,*(hp->glx_context));
937
938   glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
939   glLoadIdentity();
940
941   display_hypertorus(mi);
942
943   if (MI_IS_FPS(mi))
944     do_fps (mi);
945
946   glFlush();
947
948   glXSwapBuffers(display,window);
949 }
950
951
952 /*
953  *-----------------------------------------------------------------------------
954  *    The display is being taken away from us.  Free up malloc'ed 
955  *      memory and X resources that we've alloc'ed.  Only called
956  *      once, we must zap everything for every screen.
957  *-----------------------------------------------------------------------------
958  */
959
960 ENTRYPOINT void release_hypertorus(ModeInfo *mi)
961 {
962   if (hyper != NULL)
963   {
964     int screen;
965
966     for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
967     {
968       hypertorusstruct *hp = &hyper[screen];
969
970       if (hp->glx_context)
971         hp->glx_context = (GLXContext *)NULL;
972     }
973     (void) free((void *)hyper);
974     hyper = (hypertorusstruct *)NULL;
975   }
976   FreeAllGL(mi);
977 }
978
979 #ifndef STANDALONE
980 ENTRYPOINT void change_hypertorus(ModeInfo *mi)
981 {
982   hypertorusstruct *hp = &hyper[MI_SCREEN(mi)];
983
984   if (!hp->glx_context)
985     return;
986
987   glXMakeCurrent(MI_DISPLAY(mi),MI_WINDOW(mi),*(hp->glx_context));
988   init(mi);
989 }
990 #endif /* !STANDALONE */
991
992 XSCREENSAVER_MODULE ("Hypertorus", hypertorus)
993
994 #endif /* USE_GL */