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