From http://www.jwz.org/xscreensaver/xscreensaver-5.37.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                             "*suppressRotationAnimation: True\n" \
91
92 # define refresh_hypertorus 0
93 # define release_hypertorus 0
94 # include "xlockmore.h"         /* from the xscreensaver distribution */
95 #else  /* !STANDALONE */
96 # include "xlock.h"             /* from the xlockmore distribution */
97 #endif /* !STANDALONE */
98
99 #ifdef USE_GL
100
101 #include "gltrackball.h"
102
103
104 #ifdef USE_MODULES
105 ModStruct   hypertorus_description =
106 {"hypertorus", "init_hypertorus", "draw_hypertorus", NULL,
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 #if 0 /* #### not working */
508 # ifdef HAVE_MOBILE     /* Keep it the same relative size when rotated. */
509   {
510     GLfloat h = MI_HEIGHT(mi) / (GLfloat) MI_WIDTH(mi);
511     int o = (int) current_device_rotation();
512     if (o != 0 && o != 180 && o != -180)
513       glScalef (1/h, 1/h, 1/h);
514   }
515 # endif
516 #endif
517
518   skew = num_spirals;
519   ur = umax-umin;
520   vr = vmax-vmin;
521   for (i=0; i<numu; i++)
522   {
523     if ((appearance == APPEARANCE_BANDS ||
524          appearance == APPEARANCE_SPIRALS) && ((i & 3) >= 2))
525       continue;
526     if (display_mode == DISP_WIREFRAME)
527       glBegin(GL_QUAD_STRIP);
528     else
529       glBegin(GL_TRIANGLE_STRIP);
530     for (j=0; j<=numv; j++)
531     {
532       for (k=0; k<=1; k++)
533       {
534         l = (i+k);
535         m = j;
536         u = ur*l/numu+umin;
537         v = vr*m/numv+vmin;
538         if (appearance == APPEARANCE_SPIRALS)
539         {
540           u += 4.0*skew/numv*v;
541           b = ((i/4)&(skew-1))*(numu/(4*skew));
542           color(ur*4*b/numu+umin);
543         }
544         else
545         {
546           color(u);
547         }
548         cu = cos(u);
549         su = sin(u);
550         cv = cos(v);
551         sv = sin(v);
552         xx[0] = cu;
553         xx[1] = su;
554         xx[2] = cv;
555         xx[3] = sv;
556         xxu[0] = -su;
557         xxu[1] = cu;
558         xxu[2] = 0.0;
559         xxu[3] = 0.0;
560         xxv[0] = 0.0;
561         xxv[1] = 0.0;
562         xxv[2] = -sv;
563         xxv[3] = cv;
564         for (l=0; l<4; l++)
565         {
566           r = 0.0;
567           s = 0.0;
568           t = 0.0;
569           for (m=0; m<4; m++)
570           {
571             r += mat[l][m]*xx[m];
572             s += mat[l][m]*xxu[m];
573             t += mat[l][m]*xxv[m];
574           }
575           x[l] = r;
576           xu[l] = s;
577           xv[l] = t;
578         }
579         if (projection_4d == DISP_4D_ORTHOGRAPHIC)
580         {
581           for (l=0; l<3; l++)
582           {
583             p[l] = (x[l]+offset4d[l])/1.5+offset3d[l];
584             pu[l] = xu[l];
585             pv[l] = xv[l];
586           }
587         }
588         else
589         {
590           s = x[3]+offset4d[3];
591           t = s*s;
592           for (l=0; l<3; l++)
593           {
594             r = x[l]+offset4d[l];
595             p[l] = r/s+offset3d[l];
596             pu[l] = (xu[l]*s-r*xu[3])/t;
597             pv[l] = (xv[l]*s-r*xv[3])/t;
598           }
599         }
600         n[0] = pu[1]*pv[2]-pu[2]*pv[1];
601         n[1] = pu[2]*pv[0]-pu[0]*pv[2];
602         n[2] = pu[0]*pv[1]-pu[1]*pv[0];
603         t = sqrt(n[0]*n[0]+n[1]*n[1]+n[2]*n[2]);
604         n[0] /= t;
605         n[1] /= t;
606         n[2] /= t;
607         glNormal3fv(n);
608         glVertex3fv(p);
609         polys++;
610       }
611     }
612     glEnd();
613   }
614   polys /= 2;
615   return polys;
616 }
617
618
619 static void init(ModeInfo *mi)
620 {
621   static const GLfloat light_ambient[]  = { 0.0, 0.0, 0.0, 1.0 };
622   static const GLfloat light_diffuse[]  = { 1.0, 1.0, 1.0, 1.0 };
623   static const GLfloat light_specular[] = { 1.0, 1.0, 1.0, 1.0 };
624   static const GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 };
625   static const GLfloat mat_specular[]   = { 1.0, 1.0, 1.0, 1.0 };
626   hypertorusstruct *hp = &hyper[MI_SCREEN(mi)];
627
628   hp->alpha = 0.0;
629   hp->beta = 0.0;
630   hp->delta = 0.0;
631   hp->zeta = 0.0;
632   hp->eta = 0.0;
633   hp->theta = 0.0;
634
635   glMatrixMode(GL_PROJECTION);
636   glLoadIdentity();
637   if (projection_3d == DISP_3D_PERSPECTIVE)
638     gluPerspective(60.0,1.0,0.1,100.0);
639   else
640     glOrtho(-1.0,1.0,-1.0,1.0,0.1,100.0);;
641   glMatrixMode(GL_MODELVIEW);
642   glLoadIdentity();
643
644 # ifdef HAVE_JWZGLES /* #### glPolygonMode other than GL_FILL unimplemented */
645   if (display_mode == DISP_WIREFRAME)
646     display_mode = DISP_SURFACE;
647 # endif
648
649   if (display_mode == DISP_SURFACE)
650   {
651     glEnable(GL_DEPTH_TEST);
652     glDepthFunc(GL_LESS);
653     glShadeModel(GL_SMOOTH);
654     glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
655     glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,GL_TRUE);
656     glEnable(GL_LIGHTING);
657     glEnable(GL_LIGHT0);
658     glLightfv(GL_LIGHT0,GL_AMBIENT,light_ambient);
659     glLightfv(GL_LIGHT0,GL_DIFFUSE,light_diffuse);
660     glLightfv(GL_LIGHT0,GL_SPECULAR,light_specular);
661     glLightfv(GL_LIGHT0,GL_POSITION,light_position);
662     glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,mat_specular);
663     glMaterialf(GL_FRONT_AND_BACK,GL_SHININESS,50.0);
664     glDepthMask(GL_TRUE);
665     glDisable(GL_BLEND);
666   }
667   else if (display_mode == DISP_TRANSPARENT)
668   {
669     glDisable(GL_DEPTH_TEST);
670     glShadeModel(GL_SMOOTH);
671     glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
672     glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,GL_TRUE);
673     glEnable(GL_LIGHTING);
674     glEnable(GL_LIGHT0);
675     glLightfv(GL_LIGHT0,GL_AMBIENT,light_ambient);
676     glLightfv(GL_LIGHT0,GL_DIFFUSE,light_diffuse);
677     glLightfv(GL_LIGHT0,GL_SPECULAR,light_specular);
678     glLightfv(GL_LIGHT0,GL_POSITION,light_position);
679     glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,mat_specular);
680     glMaterialf(GL_FRONT_AND_BACK,GL_SHININESS,50.0);
681     glDepthMask(GL_FALSE);
682     glEnable(GL_BLEND);
683     glBlendFunc(GL_SRC_ALPHA,GL_ONE);
684   }
685   else  /* display_mode == DISP_WIREFRAME */
686   {
687     glDisable(GL_DEPTH_TEST);
688     glShadeModel(GL_FLAT);
689     glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
690     glDisable(GL_LIGHTING);
691     glDisable(GL_LIGHT0);
692     glDisable(GL_BLEND);
693   }
694 }
695
696
697 /* Redisplay the hypertorus. */
698 static void display_hypertorus(ModeInfo *mi)
699 {
700   hypertorusstruct *hp = &hyper[MI_SCREEN(mi)];
701
702   if (!hp->button_pressed)
703   {
704     hp->alpha += speed_wx * hp->speed_scale;
705     if (hp->alpha >= 360.0)
706       hp->alpha -= 360.0;
707     hp->beta += speed_wy * hp->speed_scale;
708     if (hp->beta >= 360.0)
709       hp->beta -= 360.0;
710     hp->delta += speed_wz * hp->speed_scale;
711     if (hp->delta >= 360.0)
712       hp->delta -= 360.0;
713     hp->zeta += speed_xy * hp->speed_scale;
714     if (hp->zeta >= 360.0)
715       hp->zeta -= 360.0;
716     hp->eta += speed_xz * hp->speed_scale;
717     if (hp->eta >= 360.0)
718       hp->eta -= 360.0;
719     hp->theta += speed_yz * hp->speed_scale;
720     if (hp->theta >= 360.0)
721       hp->theta -= 360.0;
722   }
723
724   glMatrixMode(GL_PROJECTION);
725   glLoadIdentity();
726   if (projection_3d == DISP_3D_ORTHOGRAPHIC)
727   {
728     if (hp->aspect >= 1.0)
729       glOrtho(-hp->aspect,hp->aspect,-1.0,1.0,0.1,100.0);
730     else
731       glOrtho(-1.0,1.0,-1.0/hp->aspect,1.0/hp->aspect,0.1,100.0);
732   }
733   else
734   {
735     gluPerspective(60.0,hp->aspect,0.1,100.0);
736   }
737   glMatrixMode(GL_MODELVIEW);
738   glLoadIdentity();
739
740   mi->polygon_count = hypertorus(mi,0.0,2.0*M_PI,0.0,2.0*M_PI,64,64);
741 }
742
743
744 ENTRYPOINT void reshape_hypertorus(ModeInfo *mi, int width, int height)
745 {
746   hypertorusstruct *hp = &hyper[MI_SCREEN(mi)];
747
748   hp->WindW = (GLint)width;
749   hp->WindH = (GLint)height;
750   glViewport(0,0,width,height);
751   hp->aspect = (GLfloat)width/(GLfloat)height;
752 }
753
754
755 ENTRYPOINT Bool hypertorus_handle_event(ModeInfo *mi, XEvent *event)
756 {
757   hypertorusstruct *hp = &hyper[MI_SCREEN(mi)];
758   KeySym  sym = 0;
759   char c = 0;
760
761   if (event->xany.type == KeyPress || event->xany.type == KeyRelease)
762     XLookupString (&event->xkey, &c, 1, &sym, 0);
763
764   if (event->xany.type == ButtonPress &&
765       event->xbutton.button == Button1)
766   {
767     hp->button_pressed = True;
768     gltrackball_start(hp->trackballs[hp->current_trackball],
769                       event->xbutton.x, event->xbutton.y,
770                       MI_WIDTH(mi), MI_HEIGHT(mi));
771     return True;
772   }
773   else if (event->xany.type == ButtonRelease &&
774            event->xbutton.button == Button1)
775   {
776     hp->button_pressed = False;
777     return True;
778   }
779   else if (event->xany.type == KeyPress)
780   {
781     if (sym == XK_Shift_L || sym == XK_Shift_R)
782     {
783       hp->current_trackball = 1;
784       if (hp->button_pressed)
785         gltrackball_start(hp->trackballs[hp->current_trackball],
786                           event->xbutton.x, event->xbutton.y,
787                           MI_WIDTH(mi), MI_HEIGHT(mi));
788       return True;
789     }
790   }
791   else if (event->xany.type == KeyRelease)
792   {
793     if (sym == XK_Shift_L || sym == XK_Shift_R)
794     {
795       hp->current_trackball = 0;
796       if (hp->button_pressed)
797         gltrackball_start(hp->trackballs[hp->current_trackball],
798                           event->xbutton.x, event->xbutton.y,
799                           MI_WIDTH(mi), MI_HEIGHT(mi));
800       return True;
801     }
802   }
803   else if (event->xany.type == MotionNotify && hp->button_pressed)
804   {
805     gltrackball_track(hp->trackballs[hp->current_trackball],
806                       event->xmotion.x, event->xmotion.y,
807                       MI_WIDTH(mi), MI_HEIGHT(mi));
808     return True;
809   }
810
811   return False;
812 }
813
814
815 /*
816  *-----------------------------------------------------------------------------
817  *-----------------------------------------------------------------------------
818  *    Xlock hooks.
819  *-----------------------------------------------------------------------------
820  *-----------------------------------------------------------------------------
821  */
822
823 /*
824  *-----------------------------------------------------------------------------
825  *    Initialize hypertorus.  Called each time the window changes.
826  *-----------------------------------------------------------------------------
827  */
828
829 ENTRYPOINT void init_hypertorus(ModeInfo *mi)
830 {
831   hypertorusstruct *hp;
832
833   MI_INIT(mi, hyper, NULL);
834   hp = &hyper[MI_SCREEN(mi)];
835
836   
837   hp->trackballs[0] = gltrackball_init(True);
838   hp->trackballs[1] = gltrackball_init(True);
839   hp->current_trackball = 0;
840   hp->button_pressed = False;
841
842   /* Set the display mode. */
843   if (!strcasecmp(mode,"wireframe") || !strcasecmp(mode,"0"))
844   {
845     display_mode = DISP_WIREFRAME;
846   }
847   else if (!strcasecmp(mode,"surface") || !strcasecmp(mode,"1"))
848   {
849     display_mode = DISP_SURFACE;
850   }
851   else if (!strcasecmp(mode,"transparent") || !strcasecmp(mode,"2"))
852   {
853     display_mode = DISP_TRANSPARENT;
854   }
855   else
856   {
857     display_mode = DISP_SURFACE;
858   }
859
860   /* Set the appearance. */
861   if (!strcasecmp(appear,"solid") || !strcasecmp(appear,"0"))
862   {
863     appearance = APPEARANCE_SOLID;
864   }
865   else if (!strcasecmp(appear,"bands") || !strcasecmp(appear,"1"))
866   {
867     appearance = APPEARANCE_BANDS;
868     num_spirals = 0;
869   }
870   else if (!strcasecmp(appear,"spirals-1") || !strcasecmp(appear,"3"))
871   {
872     appearance = APPEARANCE_SPIRALS;
873     num_spirals = 1;
874   }
875   else if (!strcasecmp(appear,"spirals-2") || !strcasecmp(appear,"4"))
876   {
877     appearance = APPEARANCE_SPIRALS;
878     num_spirals = 2;
879   }
880   else if (!strcasecmp(appear,"spirals-4") || !strcasecmp(appear,"5"))
881   {
882     appearance = APPEARANCE_SPIRALS;
883     num_spirals = 4;
884   }
885   else if (!strcasecmp(appear,"spirals-8") || !strcasecmp(appear,"6"))
886   {
887     appearance = APPEARANCE_SPIRALS;
888     num_spirals = 8;
889   }
890   else if (!strcasecmp(appear,"spirals-16") || !strcasecmp(appear,"7"))
891   {
892     appearance = APPEARANCE_SPIRALS;
893     num_spirals = 16;
894   }
895   else
896   {
897     appearance = APPEARANCE_BANDS;
898     num_spirals = 0;
899   }
900
901   /* Set the color mode. */
902   if (!strcasecmp(color_mode,"twosided"))
903   {
904     colors = COLORS_TWOSIDED;
905   }
906   else if (!strcasecmp(color_mode,"colorwheel"))
907   {
908     colors = COLORS_COLORWHEEL;
909   }
910   else
911   {
912     colors = COLORS_COLORWHEEL;
913   }
914
915   /* Set the 3d projection mode. */
916   if (!strcasecmp(proj_3d,"perspective") || !strcasecmp(proj_3d,"0"))
917   {
918     projection_3d = DISP_3D_PERSPECTIVE;
919   }
920   else if (!strcasecmp(proj_3d,"orthographic") || !strcasecmp(proj_3d,"1"))
921   {
922     projection_3d = DISP_3D_ORTHOGRAPHIC;
923   }
924   else
925   {
926     projection_3d = DISP_3D_PERSPECTIVE;
927   }
928
929   /* Set the 4d projection mode. */
930   if (!strcasecmp(proj_4d,"perspective") || !strcasecmp(proj_4d,"0"))
931   {
932     projection_4d = DISP_4D_PERSPECTIVE;
933   }
934   else if (!strcasecmp(proj_4d,"orthographic") || !strcasecmp(proj_4d,"1"))
935   {
936     projection_4d = DISP_4D_ORTHOGRAPHIC;
937   }
938   else
939   {
940     projection_4d = DISP_4D_PERSPECTIVE;
941   }
942
943   /* make multiple screens rotate at slightly different rates. */
944   hp->speed_scale = 0.9 + frand(0.3);
945
946   if ((hp->glx_context = init_GL(mi)) != NULL)
947   {
948     reshape_hypertorus(mi,MI_WIDTH(mi),MI_HEIGHT(mi));
949     glDrawBuffer(GL_BACK);
950     init(mi);
951   }
952   else
953   {
954     MI_CLEARWINDOW(mi);
955   }
956 }
957
958 /*
959  *-----------------------------------------------------------------------------
960  *    Called by the mainline code periodically to update the display.
961  *-----------------------------------------------------------------------------
962  */
963 ENTRYPOINT void draw_hypertorus(ModeInfo *mi)
964 {
965   Display          *display = MI_DISPLAY(mi);
966   Window           window = MI_WINDOW(mi);
967   hypertorusstruct *hp;
968
969   if (hyper == NULL)
970     return;
971   hp = &hyper[MI_SCREEN(mi)];
972
973   MI_IS_DRAWN(mi) = True;
974   if (!hp->glx_context)
975     return;
976
977   glXMakeCurrent(display,window,*(hp->glx_context));
978
979   glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
980   glLoadIdentity();
981
982   display_hypertorus(mi);
983
984   if (MI_IS_FPS(mi))
985     do_fps (mi);
986
987   glFlush();
988
989   glXSwapBuffers(display,window);
990 }
991
992
993 #ifndef STANDALONE
994 ENTRYPOINT void change_hypertorus(ModeInfo *mi)
995 {
996   hypertorusstruct *hp = &hyper[MI_SCREEN(mi)];
997
998   if (!hp->glx_context)
999     return;
1000
1001   glXMakeCurrent(MI_DISPLAY(mi),MI_WINDOW(mi),*(hp->glx_context));
1002   init(mi);
1003 }
1004 #endif /* !STANDALONE */
1005
1006 XSCREENSAVER_MODULE ("Hypertorus", hypertorus)
1007
1008 #endif /* USE_GL */