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