From http://www.jwz.org/xscreensaver/xscreensaver-5.35.tar.gz
[xscreensaver] / hacks / glx / moebius.c
1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* moebius --- Moebius Strip II, an Escher-like GL scene with ants. */
3
4 #if 0
5 static const char sccsid[] = "@(#)moebius.c     5.01 2001/03/01 xlockmore";
6 #endif
7
8 /*-
9  * Permission to use, copy, modify, and distribute this software and its
10  * documentation for any purpose and without fee is hereby granted,
11  * provided that the above copyright notice appear in all copies and that
12  * both that copyright notice and this permission notice appear in
13  * supporting documentation.
14  *
15  * This file is provided AS IS with no warranties of any kind.  The author
16  * shall have no liability with respect to the infringement of copyrights,
17  * trade secrets or any patents by this file or any part thereof.  In no
18  * event will the author be liable for any lost revenue or profits or
19  * other special, indirect and consequential damages.
20  *
21  * The RotateAroundU() routine was adapted from the book
22  *    "Computer Graphics Principles and Practice
23  *     Foley - vanDam - Feiner - Hughes
24  *     Second Edition" Pag. 227, exercise 5.15.
25  *
26  * This mode shows some interesting scenes that are impossible OR very
27  * wierd to build in the real universe. Much of the scenes are inspirated
28  * on Mauritz Cornelis Escher's works which derivated the mode's name.
29  * M.C. Escher (1898-1972) was a dutch artist and many people prefer to
30  * say he was a mathematician.
31  *
32  * Thanks goes to Brian Paul for making it possible and inexpensive to use
33  * OpenGL at home.
34  *
35  * Since I'm not a native English speaker, my apologies for any grammatical
36  * mistakes.
37  *
38  * My e-mail address is
39  * mfvianna@centroin.com.br
40  *
41  * Marcelo F. Vianna (Jun-01-1997)
42  *
43  * Revision History:
44  * 05-Apr-2002: Removed all gllist uses (fix some bug with nvidia driver)
45  * 01-Mar-2001: backported from xscreensaver by lassauge@mail.dotcom.fr
46  *    Feb-2001: Made motion and rotation be smoother Jamie Zawinski
47  *              <jwz@jwz.org>
48  * 01-Nov-2000: Allocation checks
49  * 01-Jan-1998: Mode separated from escher and renamed
50  * 08-Jun-1997: New scene implemented: "Impossible Cage" based in a M.C.
51  *              Escher's painting with the same name (quite similar). The
52  *              first GL mode to use texture mapping.
53  *              The "Impossible Cage" scene doesn't use DEPTH BUFFER, the
54  *              wood planks are drawn consistently using GL_CULL_FACE, and
55  *              the painter's algorithm is used to sort the planks.
56  *              Marcelo F. Vianna.
57  * 07-Jun-1997: Speed ups in Moebius Strip using GL_CULL_FACE.
58  *              Marcelo F. Vianna.
59  * 03-Jun-1997: Initial Release (Only one scene: "Moebius Strip")
60  *              The Moebius Strip scene was inspirated in a M.C. Escher's
61  *              painting named Moebius Strip II in wich ants walk across a
62  *              Moebius Strip path, sometimes meeting each other and sometimes
63  *              being in "opposite faces" (note that the moebius strip has
64  *              only one face and one edge).
65  *              Marcelo F. Vianna.
66  */
67
68 /*-
69  * Texture mapping is only available on RGBA contexts, Mono and color index
70  * visuals DO NOT support texture mapping in OpenGL.
71  *
72  * BUT Mesa do implements RGBA contexts in pseudo color visuals, so texture
73  * mapping shuld work on PseudoColor, DirectColor, TrueColor using Mesa. Mono
74  * is not officially supported for both OpenGL and Mesa, but seems to not crash
75  * Mesa.
76  *
77  * In real OpenGL, PseudoColor DO NOT support texture map (as far as I know).
78  */
79
80 #ifdef STANDALONE
81 # define MODE_moebius
82 # define refresh_moebius 0
83 # define DEFAULTS                       "*delay:                20000   \n"                     \
84                                                         "*showFPS:      False   \n"                     \
85                                                         "*suppressRotationAnimation: True\n" \
86
87 # include "xlockmore.h"         /* from the xscreensaver distribution */
88 #else /* !STANDALONE */
89 # include "xlock.h"             /* from the xlockmore distribution */
90 #endif /* !STANDALONE */
91
92 #ifdef HAVE_JWXYZ
93 # include "jwxyz.h"
94 #else
95 # include <X11/Xlib.h>
96 # include <GL/gl.h>
97 # include <GL/glu.h>
98 #endif
99
100 #ifdef HAVE_JWZGLES
101 # include "jwzgles.h"
102 #endif /* HAVE_JWZGLES */
103
104 #ifdef MODE_moebius
105
106 #if 0 /* Hey, this never actually used the texture at all! */
107 #if 0
108 #include "e_textures.h"
109 #else
110 #include "xpm-ximage.h"
111 #include "../images/wood.xpm"
112 #endif
113 #endif /* 0 */
114
115 #include "sphere.h"
116 #include "tube.h"
117
118 #include "rotator.h"
119 #include "gltrackball.h"
120
121 #define DEF_SOLIDMOEBIUS  "False"
122 #define DEF_DRAWANTS  "True"
123
124 static int  solidmoebius;
125 static int  drawants;
126
127 static XrmOptionDescRec opts[] =
128 {
129   {"-solidmoebius", ".moebius.solidmoebius", XrmoptionNoArg, "on"},
130   {"+solidmoebius", ".moebius.solidmoebius", XrmoptionNoArg, "off"},
131   {"-ants", ".moebius.drawants", XrmoptionNoArg, "on"},
132   {"+ants", ".moebius.drawants", XrmoptionNoArg, "off"}
133 };
134 static argtype vars[] =
135 {
136   {&solidmoebius, "solidmoebius", "Solidmoebius", DEF_SOLIDMOEBIUS, t_Bool},
137   {&drawants, "drawants", "Drawants", DEF_DRAWANTS, t_Bool}
138
139 };
140 static OptionStruct desc[] =
141 {
142         {"-/+solidmoebius", "select between a SOLID or a NET Moebius Strip"},
143         {"-/+drawants", "turn on/off walking ants"}
144 };
145
146 ENTRYPOINT ModeSpecOpt moebius_opts =
147 {sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
148
149 #ifdef USE_MODULES
150 ModStruct   moebius_description =
151 {"moebius", "init_moebius", "draw_moebius", "release_moebius",
152  "draw_moebius", "change_moebius", (char *) NULL, &moebius_opts,
153  1000, 1, 1, 1, 4, 1.0, "",
154  "Shows Moebius Strip II, an Escher-like GL scene with ants", 0, NULL};
155
156 #endif
157
158 #define Scale4Window               0.3
159 #define Scale4Iconic               0.4
160
161 #define sqr(A)                     ((A)*(A))
162
163 #ifndef Pi
164 #define Pi                         M_PI
165 #endif
166
167 #define ObjMoebiusStrip 0
168 #define ObjAntBody      1
169 #define MaxObj          2
170
171 /*************************************************************************/
172
173 typedef struct {
174         GLint       WindH, WindW;
175         GLfloat     step;
176         GLfloat     ant_position;
177         float       ant_step;
178         GLXContext *glx_context;
179     rotator    *rot;
180     trackball_state *trackball;
181     Bool        button_down_p;
182 } moebiusstruct;
183
184 static const float front_shininess[] = {60.0};
185 static const float front_specular[] = {0.7, 0.7, 0.7, 1.0};
186 static const float ambient[] = {0.0, 0.0, 0.0, 1.0};
187 static const float diffuse[] = {1.0, 1.0, 1.0, 1.0};
188 static const float position0[] = {1.0, 1.0, 1.0, 0.0};
189 static const float position1[] = {-1.0, -1.0, 1.0, 0.0};
190 static const float lmodel_ambient[] = {0.5, 0.5, 0.5, 1.0};
191 static const float lmodel_twoside[] = {GL_TRUE};
192
193 static const float MaterialRed[] = {0.7, 0.0, 0.0, 1.0};
194 static const float MaterialGreen[] = {0.1, 0.5, 0.2, 1.0};
195 static const float MaterialBlue[] = {0.0, 0.0, 0.7, 1.0};
196 static const float MaterialCyan[] = {0.2, 0.5, 0.7, 1.0};
197 static const float MaterialYellow[] = {0.7, 0.7, 0.0, 1.0};
198 static const float MaterialMagenta[] = {0.6, 0.2, 0.5, 1.0};
199 static const float MaterialWhite[] = {0.7, 0.7, 0.7, 1.0};
200 static const float MaterialGray[] = {0.2, 0.2, 0.2, 1.0};
201 static const float MaterialGray5[] = {0.5, 0.5, 0.5, 1.0};
202 static const float MaterialGray6[] = {0.6, 0.6, 0.6, 1.0};
203 static const float MaterialGray8[] = {0.8, 0.8, 0.8, 1.0};
204
205 static moebiusstruct *moebius = (moebiusstruct *) NULL;
206
207 #define NUM_SCENES      2
208
209 static Bool
210 mySphere(float radius)
211 {
212 #if 0
213         GLUquadricObj *quadObj;
214
215         if ((quadObj = gluNewQuadric()) == 0)
216                 return False;
217         gluQuadricDrawStyle(quadObj, (GLenum) GLU_FILL);
218         gluSphere(quadObj, radius, 16, 16);
219         gluDeleteQuadric(quadObj);
220 #else
221     glPushMatrix();
222     glScalef (radius, radius, radius);
223     unit_sphere (16, 16, False);
224     glPopMatrix();
225 #endif
226         return True;
227 }
228
229 static Bool
230 myCone(float radius)
231 {
232 #if 0
233         GLUquadricObj *quadObj;
234
235         if ((quadObj = gluNewQuadric()) == 0)
236                 return False;
237         gluQuadricDrawStyle(quadObj, (GLenum) GLU_FILL);
238         gluCylinder(quadObj, radius, 0, radius * 3, 8, 1);
239         gluDeleteQuadric(quadObj);
240 #else
241     cone (0, 0, 0,
242           0, 0, radius * 3,
243           radius, 0,
244           8, True, True, False);
245 #endif
246         return True;
247 }
248
249 static Bool
250 draw_moebius_ant(moebiusstruct * mp, const float *Material, int mono)
251 {
252         float       cos1 = cos(mp->ant_step);
253         float       cos2 = cos(mp->ant_step + 2 * Pi / 3);
254         float       cos3 = cos(mp->ant_step + 4 * Pi / 3);
255         float       sin1 = sin(mp->ant_step);
256         float       sin2 = sin(mp->ant_step + 2 * Pi / 3);
257         float       sin3 = sin(mp->ant_step + 4 * Pi / 3);
258
259         if (mono)
260                 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialGray5);
261         else
262                 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, Material);
263         glEnable(GL_CULL_FACE);
264         glPushMatrix();
265         glScalef(1, 1.3, 1);
266         if (!mySphere(0.18))
267                 return False;
268         glScalef(1, 1 / 1.3, 1);
269         glTranslatef(0.00, 0.30, 0.00);
270         if (!mySphere(0.2))
271                 return False;
272
273         glTranslatef(-0.05, 0.17, 0.05);
274         glRotatef(-90, 1, 0, 0);
275         glRotatef(-25, 0, 1, 0);
276         if (!myCone(0.05))
277                 return False;
278         glTranslatef(0.00, 0.10, 0.00);
279         if (!myCone(0.05))
280                 return False;
281         glRotatef(25, 0, 1, 0);
282         glRotatef(90, 1, 0, 0);
283
284         glScalef(1, 1.3, 1);
285         glTranslatef(0.15, -0.65, 0.05);
286         if (!mySphere(0.25))
287                 return False;
288         glScalef(1, 1 / 1.3, 1);
289         glPopMatrix();
290         glDisable(GL_CULL_FACE);
291
292         glDisable(GL_LIGHTING);
293         /* ANTENNAS */
294         glBegin(GL_LINES);
295         if (mono)
296                 glColor3fv(MaterialGray5);
297         else
298                 glColor3fv(Material);
299         glVertex3f(0.00, 0.30, 0.00);
300         glColor3fv(MaterialGray);
301         glVertex3f(0.40, 0.70, 0.40);
302         if (mono)
303                 glColor3fv(MaterialGray5);
304         else
305                 glColor3fv(Material);
306         glVertex3f(0.00, 0.30, 0.00);
307         glColor3fv(MaterialGray);
308         glVertex3f(0.40, 0.70, -0.40);
309         glEnd();
310         glBegin(GL_POINTS);
311         if (mono)
312                 glColor3fv(MaterialGray6);
313         else
314                 glColor3fv(MaterialRed);
315         glVertex3f(0.40, 0.70, 0.40);
316         glVertex3f(0.40, 0.70, -0.40);
317         glEnd();
318
319         /* LEFT-FRONT ARM */
320         glBegin(GL_LINE_STRIP);
321         if (mono)
322                 glColor3fv(MaterialGray5);
323         else
324                 glColor3fv(Material);
325         glVertex3f(0.00, 0.05, 0.18);
326         glVertex3f(0.35 + 0.05 * cos1, 0.15, 0.25);
327         glColor3fv(MaterialGray);
328         glVertex3f(-0.20 + 0.05 * cos1, 0.25 + 0.1 * sin1, 0.45);
329         glEnd();
330
331         /* LEFT-CENTER ARM */
332         glBegin(GL_LINE_STRIP);
333         if (mono)
334                 glColor3fv(MaterialGray5);
335         else
336                 glColor3fv(Material);
337         glVertex3f(0.00, 0.00, 0.18);
338         glVertex3f(0.35 + 0.05 * cos2, 0.00, 0.25);
339         glColor3fv(MaterialGray);
340         glVertex3f(-0.20 + 0.05 * cos2, 0.00 + 0.1 * sin2, 0.45);
341         glEnd();
342
343         /* LEFT-BACK ARM */
344         glBegin(GL_LINE_STRIP);
345         if (mono)
346                 glColor3fv(MaterialGray5);
347         else
348                 glColor3fv(Material);
349         glVertex3f(0.00, -0.05, 0.18);
350         glVertex3f(0.35 + 0.05 * cos3, -0.15, 0.25);
351         glColor3fv(MaterialGray);
352         glVertex3f(-0.20 + 0.05 * cos3, -0.25 + 0.1 * sin3, 0.45);
353         glEnd();
354
355         /* RIGHT-FRONT ARM */
356         glBegin(GL_LINE_STRIP);
357         if (mono)
358                 glColor3fv(MaterialGray5);
359         else
360                 glColor3fv(Material);
361         glVertex3f(0.00, 0.05, -0.18);
362         glVertex3f(0.35 - 0.05 * sin1, 0.15, -0.25);
363         glColor3fv(MaterialGray);
364         glVertex3f(-0.20 - 0.05 * sin1, 0.25 + 0.1 * cos1, -0.45);
365         glEnd();
366
367         /* RIGHT-CENTER ARM */
368         glBegin(GL_LINE_STRIP);
369         if (mono)
370                 glColor3fv(MaterialGray5);
371         else
372                 glColor3fv(Material);
373         glVertex3f(0.00, 0.00, -0.18);
374         glVertex3f(0.35 - 0.05 * sin2, 0.00, -0.25);
375         glColor3fv(MaterialGray);
376         glVertex3f(-0.20 - 0.05 * sin2, 0.00 + 0.1 * cos2, -0.45);
377         glEnd();
378
379         /* RIGHT-BACK ARM */
380         glBegin(GL_LINE_STRIP);
381         if (mono)
382                 glColor3fv(MaterialGray5);
383         else
384                 glColor3fv(Material);
385         glVertex3f(0.00, -0.05, -0.18);
386         glVertex3f(0.35 - 0.05 * sin3, -0.15, -0.25);
387         glColor3fv(MaterialGray);
388         glVertex3f(-0.20 - 0.05 * sin3, -0.25 + 0.1 * cos3, -0.45);
389         glEnd();
390
391         glBegin(GL_POINTS);
392         if (mono)
393                 glColor3fv(MaterialGray8);
394         else
395                 glColor3fv(MaterialMagenta);
396         glVertex3f(-0.20 + 0.05 * cos1, 0.25 + 0.1 * sin1, 0.45);
397         glVertex3f(-0.20 + 0.05 * cos2, 0.00 + 0.1 * sin2, 0.45);
398         glVertex3f(-0.20 + 0.05 * cos3, -0.25 + 0.1 * sin3, 0.45);
399         glVertex3f(-0.20 - 0.05 * sin1, 0.25 + 0.1 * cos1, -0.45);
400         glVertex3f(-0.20 - 0.05 * sin2, 0.00 + 0.1 * cos2, -0.45);
401         glVertex3f(-0.20 - 0.05 * sin3, -0.25 + 0.1 * cos3, -0.45);
402         glEnd();
403
404         glEnable(GL_LIGHTING);
405
406         mp->ant_step += 0.3;
407         return True;
408 }
409
410 static void
411 RotateAaroundU(float Ax, float Ay, float Az,
412                float Ux, float Uy, float Uz,
413                float *Cx, float *Cy, float *Cz,
414                float Theta)
415 {
416         float       cosO = cos(Theta);
417         float       sinO = sin(Theta);
418         float       one_cosO = 1 - cosO;
419         float       Ux2 = sqr(Ux);
420         float       Uy2 = sqr(Uy);
421         float       Uz2 = sqr(Uz);
422         float       UxUy = Ux * Uy;
423         float       UxUz = Ux * Uz;
424         float       UyUz = Uy * Uz;
425
426         *Cx = (Ux2 + cosO * (1 - Ux2)) * Ax + (UxUy * one_cosO - Uz * sinO) * Ay + (UxUz * one_cosO + Uy * sinO) * Az;
427         *Cy = (UxUy * one_cosO + Uz * sinO) * Ax + (Uy2 + cosO * (1 - Uy2)) * Ay + (UyUz * one_cosO - Ux * sinO) * Az;
428         *Cz = (UxUz * one_cosO - Uy * sinO) * Ax + (UyUz * one_cosO + Ux * sinO) * Ay + (Uz2 + cosO * (1 - Uz2)) * Az;
429 }
430
431 #define MoebiusDivisions 40
432 #define MoebiusTransversals 4
433 static Bool
434 draw_moebius_strip(ModeInfo * mi)
435 {
436         GLfloat     Phi, Theta;
437         GLfloat     cPhi, sPhi;
438         moebiusstruct *mp = &moebius[MI_SCREEN(mi)];
439         int         i, j;
440         int         mono = MI_IS_MONO(mi);
441
442         float       Cx, Cy, Cz;
443
444 #ifdef HAVE_JWZGLES /* #### glPolygonMode other than GL_FILL unimplemented */
445     solidmoebius = True;
446 #endif
447
448         if (solidmoebius) {
449                 glBegin(GL_QUAD_STRIP);
450                 Phi = 0;
451                 i = 0;
452                 while (i < (MoebiusDivisions * 2 + 1)) {
453                         Theta = Phi / 2;
454                         cPhi = cos(Phi);
455                         sPhi = sin(Phi);
456
457                         i++;
458                         if (mono)
459                                 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialWhite);
460                         else if (i % 2)
461                                 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialRed);
462                         else
463                                 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialGray);
464
465                         RotateAaroundU(cPhi, sPhi, 0, -sPhi, cPhi, 0, &Cx, &Cy, &Cz, Theta);
466                         glNormal3f(Cx, Cy, Cz);
467                         RotateAaroundU(0, 0, 1, -sPhi, cPhi, 0, &Cx, &Cy, &Cz, Theta);
468                         glVertex3f(cPhi * 3 + Cx, sPhi * 3 + Cy, +Cz);
469                         glVertex3f(cPhi * 3 - Cx, sPhi * 3 - Cy, -Cz);
470
471                         Phi += Pi / MoebiusDivisions;
472                 }
473                 glEnd();
474         } else {
475                 for (j = -MoebiusTransversals; j < MoebiusTransversals; j++) {
476                         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
477                         glBegin(GL_QUAD_STRIP);
478                         Phi = 0;
479                         i = 0;
480                         while (i < (MoebiusDivisions * 2 + 1)) {
481                                 Theta = Phi / 2;
482                                 cPhi = cos(Phi);
483                                 sPhi = sin(Phi);
484
485                                 RotateAaroundU(cPhi, sPhi, 0, -sPhi, cPhi, 0, &Cx, &Cy, &Cz, Theta);
486                                 glNormal3f(Cx, Cy, Cz);
487                                 RotateAaroundU(0, 0, 1, -sPhi, cPhi, 0, &Cx, &Cy, &Cz, Theta);
488                                 j++;
489                                 if (j == MoebiusTransversals || mono)
490                                         glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialWhite);
491                                 else if (i % 2)
492                                         glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialRed);
493                                 else
494                                         glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialGray);
495                                 glVertex3f(cPhi * 3 + Cx / MoebiusTransversals * j, sPhi * 3 + Cy / MoebiusTransversals * j, +Cz / MoebiusTransversals * j);
496                                 j--;
497                                 if (j == -MoebiusTransversals || mono)
498                                         glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialWhite);
499                                 else if (i % 2)
500                                         glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialRed);
501                                 else
502                                         glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialGray);
503                                 glVertex3f(cPhi * 3 + Cx / MoebiusTransversals * j, sPhi * 3 + Cy / MoebiusTransversals * j, +Cz / MoebiusTransversals * j);
504
505                                 Phi += Pi / MoebiusDivisions;
506                                 i++;
507                         }
508                         glEnd();
509                 }
510                 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
511         }
512
513         if (drawants) {
514                 /* DRAW BLUE ANT */
515                 glPushMatrix();
516                 glRotatef(mp->ant_position + 180, 0, 0, 1);
517                 glTranslatef(3, 0, 0);
518                 glRotatef(mp->ant_position / 2 + 90, 0, 1, 0);
519                 glTranslatef(0.28, 0, -0.45);
520                 if (!draw_moebius_ant(mp, MaterialYellow, mono))
521                         return False;
522                 glPopMatrix();
523
524                 /* DRAW YELLOW ANT */
525                 glPushMatrix();
526                 glRotatef(mp->ant_position, 0, 0, 1);
527                 glTranslatef(3, 0, 0);
528                 glRotatef(mp->ant_position / 2, 0, 1, 0);
529                 glTranslatef(0.28, 0, -0.45);
530                 if (!draw_moebius_ant(mp, MaterialBlue, mono))
531                         return False;
532                 glPopMatrix();
533
534                 /* DRAW GREEN ANT */
535                 glPushMatrix();
536                 glRotatef(-mp->ant_position, 0, 0, 1);
537                 glTranslatef(3, 0, 0);
538                 glRotatef(-mp->ant_position / 2, 0, 1, 0);
539                 glTranslatef(0.28, 0, 0.45);
540                 glRotatef(180, 1, 0, 0);
541                 if (!draw_moebius_ant(mp, MaterialGreen, mono))
542                         return False;
543                 glPopMatrix();
544
545                 /* DRAW CYAN ANT */
546                 glPushMatrix();
547                 glRotatef(-mp->ant_position + 180, 0, 0, 1);
548                 glTranslatef(3, 0, 0);
549                 glRotatef(-mp->ant_position / 2 + 90, 0, 1, 0);
550                 glTranslatef(0.28, 0, 0.45);
551                 glRotatef(180, 1, 0, 0);
552                 if (!draw_moebius_ant(mp, MaterialCyan, mono))
553                         return False;
554                 glPopMatrix();
555         }
556         mp->ant_position += 1;
557         return True;
558 }
559 #undef MoebiusDivisions
560 #undef MoebiusTransversals
561
562 ENTRYPOINT void
563 reshape_moebius (ModeInfo * mi, int width, int height)
564 {
565         moebiusstruct *mp = &moebius[MI_SCREEN(mi)];
566
567         glViewport(0, 0, mp->WindW = (GLint) width, mp->WindH = (GLint) height);
568         glMatrixMode(GL_PROJECTION);
569         glLoadIdentity();
570         glFrustum(-1.0, 1.0, -1.0, 1.0, 5.0, 15.0);
571         glMatrixMode(GL_MODELVIEW);
572         if (width >= 1024) {
573                 glLineWidth(3);
574                 glPointSize(3);
575         } else if (width >= 512) {
576                 glLineWidth(2);
577                 glPointSize(2);
578         } else {
579                 glLineWidth(1);
580                 glPointSize(1);
581         }
582 }
583
584 static void
585 pinit(ModeInfo *mi)
586 {
587   /* int status; */
588         glClearDepth(1.0);
589         glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
590         glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
591         glLightfv(GL_LIGHT0, GL_POSITION, position0);
592         glLightfv(GL_LIGHT1, GL_AMBIENT, ambient);
593         glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse);
594         glLightfv(GL_LIGHT1, GL_POSITION, position1);
595         glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
596         glLightModelfv(GL_LIGHT_MODEL_TWO_SIDE, lmodel_twoside);
597         glEnable(GL_LIGHTING);
598         glEnable(GL_LIGHT0);
599         glEnable(GL_LIGHT1);
600         glEnable(GL_NORMALIZE);
601         glFrontFace(GL_CCW);
602         glCullFace(GL_BACK);
603
604         /* moebius */
605         glShadeModel(GL_SMOOTH);
606         glEnable(GL_DEPTH_TEST);
607         glDisable(GL_CULL_FACE);
608
609 #if 0
610         glEnable(GL_TEXTURE_2D);
611         glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
612
613 #if 0
614     clear_gl_error();
615         status = gluBuild2DMipmaps(GL_TEXTURE_2D, 3,
616                                WoodTextureWidth, WoodTextureHeight,
617                                GL_RGB, GL_UNSIGNED_BYTE, WoodTextureData);
618     if (status)
619       {
620         const char *s = (char *) gluErrorString (status);
621         fprintf (stderr, "%s: error mipmapping %dx%d texture: %s\n",
622                  progname, WoodTextureWidth, WoodTextureHeight,
623                  (s ? s : "(unknown)"));
624         exit (1);
625       }
626     check_gl_error("mipmapping");
627 #else
628     {
629       XImage *img = xpm_to_ximage (mi->dpy,
630                                    mi->xgwa.visual,
631                                    mi->xgwa.colormap,
632                                    wood_texture);
633           glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA,
634                     img->width, img->height, 0,
635                     GL_RGBA,
636                     /* GL_UNSIGNED_BYTE, */
637                     GL_UNSIGNED_INT_8_8_8_8_REV,
638                     img->data);
639       check_gl_error("texture");
640       XDestroyImage (img);
641     }
642 #endif
643
644         glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
645         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
646         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
647         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
648         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
649 #endif
650
651         glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, front_shininess);
652         glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, front_specular);
653 }
654
655
656
657 ENTRYPOINT void
658 release_moebius (ModeInfo * mi)
659 {
660         if (moebius != NULL) {
661                 (void) free((void *) moebius);
662                 moebius = (moebiusstruct *) NULL;
663         }
664         FreeAllGL(mi);
665 }
666
667 ENTRYPOINT Bool
668 moebius_handle_event (ModeInfo *mi, XEvent *event)
669 {
670   moebiusstruct *mp = &moebius[MI_SCREEN(mi)];
671
672   if (gltrackball_event_handler (event, mp->trackball,
673                                  MI_WIDTH (mi), MI_HEIGHT (mi),
674                                  &mp->button_down_p))
675     return True;
676
677   return False;
678 }
679
680
681 ENTRYPOINT void
682 init_moebius (ModeInfo * mi)
683 {
684         moebiusstruct *mp;
685
686         if (moebius == NULL) {
687                 if ((moebius = (moebiusstruct *) calloc(MI_NUM_SCREENS(mi),
688                                             sizeof (moebiusstruct))) == NULL)
689                         return;
690         }
691         mp = &moebius[MI_SCREEN(mi)];
692         mp->step = NRAND(90);
693         mp->ant_position = NRAND(90);
694
695     {
696       double rot_speed = 0.3;
697       mp->rot = make_rotator (rot_speed, rot_speed, rot_speed, 1, 0, True);
698       mp->trackball = gltrackball_init (True);
699     }
700
701         if ((mp->glx_context = init_GL(mi)) != NULL) {
702
703                 reshape_moebius(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
704                 glDrawBuffer(GL_BACK);
705                 pinit(mi);
706         } else {
707                 MI_CLEARWINDOW(mi);
708         }
709 }
710
711 ENTRYPOINT void
712 draw_moebius (ModeInfo * mi)
713 {
714         moebiusstruct *mp;
715
716         Display    *display = MI_DISPLAY(mi);
717         Window      window = MI_WINDOW(mi);
718
719         if (moebius == NULL)
720             return;
721         mp = &moebius[MI_SCREEN(mi)];
722
723         MI_IS_DRAWN(mi) = True;
724
725         if (!mp->glx_context)
726                 return;
727
728         glXMakeCurrent(display, window, *(mp->glx_context));
729
730         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
731
732         glPushMatrix();
733
734
735         glTranslatef(0.0, 0.0, -10.0);
736
737     gltrackball_rotate (mp->trackball);
738
739         if (!MI_IS_ICONIC(mi)) {
740                 glScalef(Scale4Window * mp->WindH / mp->WindW, Scale4Window, Scale4Window);
741         } else {
742                 glScalef(Scale4Iconic * mp->WindH / mp->WindW, Scale4Iconic, Scale4Iconic);
743         }
744
745 # ifdef HAVE_MOBILE     /* Keep it the same relative size when rotated. */
746     {
747       GLfloat h = MI_HEIGHT(mi) / (GLfloat) MI_WIDTH(mi);
748       int o = (int) current_device_rotation();
749       if (o != 0 && o != 180 && o != -180) {
750         glScalef (1/h, h, 1);  /* #### not quite right */
751         h = 1.7;
752         glScalef (h, h, h);
753       }
754     }
755 # endif
756
757     {
758       double x, y, z;
759       get_rotation (mp->rot, &x, &y, &z, !mp->button_down_p);
760       glRotatef (x * 360, 1.0, 0.0, 0.0);
761       glRotatef (y * 360, 0.0, 1.0, 0.0);
762       glRotatef (z * 360, 0.0, 0.0, 1.0);
763     }
764
765         /* moebius */
766         if (!draw_moebius_strip(mi)) {
767                 release_moebius(mi);
768                 return;
769         }
770
771         glPopMatrix();
772
773     if (MI_IS_FPS(mi)) do_fps (mi);
774         glFlush();
775
776         glXSwapBuffers(display, window);
777
778         mp->step += 0.025;
779 }
780
781 #ifndef STANDALONE
782 ENTRYPOINT void
783 change_moebius (ModeInfo * mi)
784 {
785         moebiusstruct *mp = &moebius[MI_SCREEN(mi)];
786
787         if (!mp->glx_context)
788                 return;
789
790         glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(mp->glx_context));
791         pinit();
792 }
793 #endif /* !STANDALONE */
794
795
796 XSCREENSAVER_MODULE ("Moebius", moebius)
797
798 #endif