ftp://ftp.uni-heidelberg.de/pub/X11/contrib/applications/xscreensaver-2.07.tar.gz
[xscreensaver] / hacks / glx / escher.c
1 /* -*- Mode: C; tab-width: 4 -*-
2  * escher.c - Shows some Escher like scenes
3  */
4 #if !defined( lint ) && !defined( SABER )
5 static const char sccsid[] = "@(#)escher.c      4.04 97/07/28 xlockmore";
6 #endif
7
8 #undef DEBUG_LISTS
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  * The RotateAroundU() routine was adapted from the book
23  *    "Computer Graphics Principles and Practice 
24  *     Foley - vanDam - Feiner - Hughes
25  *     Second Edition" Pag. 227, exercise 5.15.
26  * 
27  * This mode shows some interesting scenes that are impossible OR very
28  * wierd to build in the real universe. Much of the scenes are inspirated
29  * on Mauritz Cornelis Escher's works which derivated the mode's name.
30  * M.C. Escher (1898-1972) was a dutch artist and many people prefer to
31  * say he was a mathematician.
32  *
33  * Thanks goes to Brian Paul for making it possible and inexpensive to use 
34  * OpenGL at home.
35  *
36  * Since I'm not a native english speaker, my apologies for any gramatical
37  * mistake.
38  *
39  * My e-mail addresses are
40  * vianna@cat.cbpf.br 
41  *         and
42  * marcelo@venus.rdc.puc-rio.br
43  *
44  * Marcelo F. Vianna (Jun-01-1997)
45  *
46  * Revision History:
47  * 08-Jun-97: New scene implemented: "Impossible Cage" based in a M.C. Escher's
48  *            painting with the same name (quite similar). The first GL mode
49  *            to use texture mapping.
50  *            The "Impossible Cage" scene doesn't use DEPTH BUFFER, the 
51  *            wood planks are drawn consistently using GL_CULL_FACE, and
52  *            the painter's algorithm is used to sort the planks.
53  *            Marcelo F. Vianna.
54  * 07-Jun-97: Speed ups in Moebius Strip using GL_CULL_FACE.
55  *            Marcelo F. Vianna.
56  * 03-Jun-97: Initial Release (Only one scene: "Moebius Strip")
57  *            The Moebious Strip scene was inspirated in a M.C. Escher's
58  *            painting named Moebius Strip II in wich ants walk across a
59  *            Moebius Strip path, sometimes meeting each other and sometimes
60  *            being in "opposite faces" (note that the moebius strip has
61  *            only one face and one edge).
62  *            Marcelo F. Vianna.
63  *
64  */
65
66 /*-
67  * Texture mapping is only available on RGBA contexts, Mono and color index
68  * visuals DO NOT support texture mapping in OpenGL.
69  *
70  * BUT Mesa do implements RGBA contexts in pseudo color visuals, so texture
71  * mapping shuld work on PseudoColor, DirectColor, TrueColor using Mesa. Mono
72  * is not officially supported for both OpenGL and Mesa, but seems to not crash
73  * Mesa.
74  *
75  * In real OpenGL, PseudoColor DO NOT support texture map (as far as I know).
76  */
77
78 #include <X11/Intrinsic.h>
79
80 #ifdef STANDALONE
81 # define PROGCLASS                                      "Escher"
82 # define HACK_INIT                                      init_escher
83 # define HACK_DRAW                                      draw_escher
84 # define escher_opts                            xlockmore_opts
85 # define DEFAULTS       "*count:                0       \n"                     \
86                                         "*cycles:               1       \n"                     \
87                                         "*delay:                100     \n"                     \
88                                         "*wireframe:    False   \n"
89 # include "xlockmore.h"                         /* from the xscreensaver distribution */
90 #else  /* !STANDALONE */
91 # include "xlock.h"                                     /* from the xlockmore distribution */
92 #endif /* !STANDALONE */
93
94
95 #ifdef USE_GL
96
97
98 #include <GL/glu.h>
99 #include "e_textures.h"
100
101 #define DEF_SOLIDMOEBIUS  "False"
102 #define DEF_NOANTS  "False"
103
104 static int  solidmoebius;
105 static int  noants;
106
107 static XrmOptionDescRec opts[] =
108 {
109    {"-solidmoebius", ".escher.solidmoebius", XrmoptionNoArg, (caddr_t) "on"},
110   {"+solidmoebius", ".escher.solidmoebius", XrmoptionNoArg, (caddr_t) "off"},
111         {"-noants", ".escher.noants", XrmoptionNoArg, (caddr_t) "on"},
112         {"+noants", ".escher.noants", XrmoptionNoArg, (caddr_t) "off"}
113 };
114 static argtype vars[] =
115 {
116         {(caddr_t *) & solidmoebius, "solidmoebius", "Solidmoebius", DEF_SOLIDMOEBIUS, t_Bool},
117         {(caddr_t *) & noants, "noants", "Noants", DEF_NOANTS, t_Bool}
118 };
119 static OptionStruct desc[] =
120 {
121         {"-/+solidmoebius", "select between a SOLID or a NET Moebius Strip"},
122         {"-/+noants", "turn on/off walking ants"}
123 };
124
125 ModeSpecOpt escher_opts =
126 {4, opts, 2, vars, desc};
127
128 #define Scale4Window               0.3
129 #define Scale4Iconic               0.4
130
131 #define sqr(A)                     ((A)*(A))
132
133 #ifndef Pi
134 #define Pi                         M_PI
135 #endif
136
137 /*************************************************************************/
138
139 typedef struct {
140         GLint       WindH, WindW;
141         GLfloat     step;
142         GLfloat     ant_position;
143         int         scene;
144         int         AreObjectsDefined[3];
145         GLXContext  glx_context;
146 } escherstruct;
147
148 static float front_shininess[] =
149 {60.0};
150 static float front_specular[] =
151 {0.7, 0.7, 0.7, 1.0};
152 static float ambient[] =
153 {0.0, 0.0, 0.0, 1.0};
154 static float diffuse[] =
155 {1.0, 1.0, 1.0, 1.0};
156 static float position0[] =
157 {1.0, 1.0, 1.0, 0.0};
158 static float position1[] =
159 {-1.0, -1.0, 1.0, 0.0};
160 static float lmodel_ambient[] =
161 {0.5, 0.5, 0.5, 1.0};
162 static float lmodel_twoside[] =
163 {GL_TRUE};
164
165 static float MaterialRed[] =
166 {0.7, 0.0, 0.0, 1.0};
167 static float MaterialGreen[] =
168 {0.1, 0.5, 0.2, 1.0};
169 static float MaterialBlue[] =
170 {0.0, 0.0, 0.7, 1.0};
171 static float MaterialCyan[] =
172 {0.2, 0.5, 0.7, 1.0};
173 static float MaterialYellow[] =
174 {0.7, 0.7, 0.0, 1.0};
175 static float MaterialMagenta[] =
176 {0.6, 0.2, 0.5, 1.0};
177 static float MaterialWhite[] =
178 {0.7, 0.7, 0.7, 1.0};
179 static float MaterialGray[] =
180 {0.2, 0.2, 0.2, 1.0};
181
182 static escherstruct *escher = NULL;
183 static GLuint objects;
184
185 #define NUM_SCENES      2
186
187 #define ObjMoebiusStrip 0
188 #define ObjAntBody      1
189 #define ObjWoodPlank    2
190
191 #define PlankWidth      3.0
192 #define PlankHeight     0.35
193 #define PlankThickness  0.15
194
195 static void
196 mySphere(float radius)
197 {
198         GLUquadricObj *quadObj;
199
200         quadObj = gluNewQuadric();
201         gluQuadricDrawStyle(quadObj, (GLenum) GLU_FILL);
202         gluSphere(quadObj, radius, 16, 16);
203         gluDeleteQuadric(quadObj);
204 }
205
206 static void
207 myCone(float radius)
208 {
209         GLUquadricObj *quadObj;
210
211         quadObj = gluNewQuadric();
212         gluQuadricDrawStyle(quadObj, (GLenum) GLU_FILL);
213         gluCylinder(quadObj, radius, 0, radius * 3, 8, 1);
214         gluDeleteQuadric(quadObj);
215 }
216
217 static void
218 draw_escher_ant(escherstruct * ep, float *Material)
219 {
220         static float ant_step = 0;
221         float       cos1 = cos(ant_step);
222         float       cos2 = cos(ant_step + 2 * Pi / 3);
223         float       cos3 = cos(ant_step + 4 * Pi / 3);
224         float       sin1 = sin(ant_step);
225         float       sin2 = sin(ant_step + 2 * Pi / 3);
226         float       sin3 = sin(ant_step + 4 * Pi / 3);
227
228         glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, Material);
229         if (!ep->AreObjectsDefined[ObjAntBody]) {
230                 glNewList(objects + ObjAntBody, GL_COMPILE_AND_EXECUTE);
231                 glEnable(GL_CULL_FACE);
232                 glPushMatrix();
233                 glScalef(1, 1.3, 1);
234                 mySphere(0.18);
235                 glScalef(1, 1 / 1.3, 1);
236                 glTranslatef(0.00, 0.30, 0.00);
237                 mySphere(0.2);
238
239                 glTranslatef(-0.05, 0.17, 0.05);
240                 glRotatef(-90, 1, 0, 0);
241                 glRotatef(-25, 0, 1, 0);
242                 myCone(0.05);
243                 glTranslatef(0.00, 0.10, 0.00);
244                 myCone(0.05);
245                 glRotatef(25, 0, 1, 0);
246                 glRotatef(90, 1, 0, 0);
247
248                 glScalef(1, 1.3, 1);
249                 glTranslatef(0.15, -0.65, 0.05);
250                 mySphere(0.25);
251                 glScalef(1, 1 / 1.3, 1);
252                 glPopMatrix();
253                 glDisable(GL_CULL_FACE);
254                 glEndList();
255                 ep->AreObjectsDefined[ObjAntBody] = 1;
256 #ifdef DEBUG_LISTS
257                 (void) printf("Ant drawn SLOWLY\n");
258 #endif
259         } else {
260                 glCallList(objects + ObjAntBody);
261 #ifdef DEBUG_LISTS
262                 (void) printf("Ant drawn quickly\n");
263 #endif
264         }
265
266         glDisable(GL_LIGHTING);
267         /* ANTENNAS */
268         glBegin(GL_LINES);
269         glColor3fv(Material);
270         glVertex3f(0.00, 0.30, 0.00);
271         glColor3fv(MaterialGray);
272         glVertex3f(0.40, 0.70, 0.40);
273         glColor3fv(Material);
274         glVertex3f(0.00, 0.30, 0.00);
275         glColor3fv(MaterialGray);
276         glVertex3f(0.40, 0.70, -0.40);
277         glEnd();
278         glBegin(GL_POINTS);
279         glColor3fv(MaterialRed);
280         glVertex3f(0.40, 0.70, 0.40);
281         glVertex3f(0.40, 0.70, -0.40);
282         glEnd();
283
284         /* LEFT-FRONT ARM */
285         glBegin(GL_LINE_STRIP);
286         glColor3fv(Material);
287         glVertex3f(0.00, 0.05, 0.18);
288         glVertex3f(0.35 + 0.05 * cos1, 0.15, 0.25);
289         glColor3fv(MaterialGray);
290         glVertex3f(-0.20 + 0.05 * cos1, 0.25 + 0.1 * sin1, 0.45);
291         glEnd();
292
293         /* LEFT-CENTER ARM */
294         glBegin(GL_LINE_STRIP);
295         glColor3fv(Material);
296         glVertex3f(0.00, 0.00, 0.18);
297         glVertex3f(0.35 + 0.05 * cos2, 0.00, 0.25);
298         glColor3fv(MaterialGray);
299         glVertex3f(-0.20 + 0.05 * cos2, 0.00 + 0.1 * sin2, 0.45);
300         glEnd();
301
302         /* LEFT-BACK ARM */
303         glBegin(GL_LINE_STRIP);
304         glColor3fv(Material);
305         glVertex3f(0.00, -0.05, 0.18);
306         glVertex3f(0.35 + 0.05 * cos3, -0.15, 0.25);
307         glColor3fv(MaterialGray);
308         glVertex3f(-0.20 + 0.05 * cos3, -0.25 + 0.1 * sin3, 0.45);
309         glEnd();
310
311         /* RIGHT-FRONT ARM */
312         glBegin(GL_LINE_STRIP);
313         glColor3fv(Material);
314         glVertex3f(0.00, 0.05, -0.18);
315         glVertex3f(0.35 - 0.05 * sin1, 0.15, -0.25);
316         glColor3fv(MaterialGray);
317         glVertex3f(-0.20 - 0.05 * sin1, 0.25 + 0.1 * cos1, -0.45);
318         glEnd();
319
320         /* RIGHT-CENTER ARM */
321         glBegin(GL_LINE_STRIP);
322         glColor3fv(Material);
323         glVertex3f(0.00, 0.00, -0.18);
324         glVertex3f(0.35 - 0.05 * sin2, 0.00, -0.25);
325         glColor3fv(MaterialGray);
326         glVertex3f(-0.20 - 0.05 * sin2, 0.00 + 0.1 * cos2, -0.45);
327         glEnd();
328
329         /* RIGHT-BACK ARM */
330         glBegin(GL_LINE_STRIP);
331         glColor3fv(Material);
332         glVertex3f(0.00, -0.05, -0.18);
333         glVertex3f(0.35 - 0.05 * sin3, -0.15, -0.25);
334         glColor3fv(MaterialGray);
335         glVertex3f(-0.20 - 0.05 * sin3, -0.25 + 0.1 * cos3, -0.45);
336         glEnd();
337
338         glBegin(GL_POINTS);
339         glColor3fv(MaterialMagenta);
340         glVertex3f(-0.20 + 0.05 * cos1, 0.25 + 0.1 * sin1, 0.45);
341         glVertex3f(-0.20 + 0.05 * cos2, 0.00 + 0.1 * sin2, 0.45);
342         glVertex3f(-0.20 + 0.05 * cos3, -0.25 + 0.1 * sin3, 0.45);
343         glVertex3f(-0.20 - 0.05 * sin1, 0.25 + 0.1 * cos1, -0.45);
344         glVertex3f(-0.20 - 0.05 * sin2, 0.00 + 0.1 * cos2, -0.45);
345         glVertex3f(-0.20 - 0.05 * sin3, -0.25 + 0.1 * cos3, -0.45);
346         glEnd();
347
348         glEnable(GL_LIGHTING);
349
350         ant_step += 0.3;
351 }
352
353 static void
354 RotateAaroundU(float Ax, float Ay, float Az,
355                float Ux, float Uy, float Uz,
356                float *Cx, float *Cy, float *Cz,
357                float Theta)
358 {
359         float       cosO = cos(Theta);
360         float       sinO = sin(Theta);
361         float       one_cosO = 1 - cosO;
362         float       Ux2 = sqr(Ux);
363         float       Uy2 = sqr(Uy);
364         float       Uz2 = sqr(Uz);
365         float       UxUy = Ux * Uy;
366         float       UxUz = Ux * Uz;
367         float       UyUz = Uy * Uz;
368
369         *Cx = (Ux2 + cosO * (1 - Ux2)) * Ax + (UxUy * one_cosO - Uz * sinO) * Ay + (UxUz * one_cosO + Uy * sinO) * Az;
370         *Cy = (UxUy * one_cosO + Uz * sinO) * Ax + (Uy2 + cosO * (1 - Uy2)) * Ay + (UyUz * one_cosO - Ux * sinO) * Az;
371         *Cz = (UxUz * one_cosO - Uy * sinO) * Ax + (UyUz * one_cosO + Ux * sinO) * Ay + (Uz2 + cosO * (1 - Uz2)) * Az;
372 }
373
374 #define MoebiusDivisions 40
375 #define MoebiusTransversals 4
376 static void
377 draw_moebius(ModeInfo * mi)
378 {
379         GLfloat     Phi, Theta;
380         GLfloat     cPhi, sPhi;
381         escherstruct *ep = &escher[MI_SCREEN(mi)];
382         int         i, j;
383
384         float       Cx, Cy, Cz;
385
386         if (!ep->AreObjectsDefined[ObjMoebiusStrip]) {
387                 glNewList(objects + ObjMoebiusStrip, GL_COMPILE_AND_EXECUTE);
388
389                 if (solidmoebius) {
390                         glBegin(GL_QUAD_STRIP);
391                         Phi = 0;
392                         i = 0;
393                         while (i < (MoebiusDivisions * 2 + 1)) {
394                                 Theta = Phi / 2;
395                                 cPhi = cos(Phi);
396                                 sPhi = sin(Phi);
397
398                                 i++;
399                                 if (i % 2)
400                                         glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialRed);
401                                 else
402                                         glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialGray);
403
404                                 RotateAaroundU(cPhi, sPhi, 0, -sPhi, cPhi, 0, &Cx, &Cy, &Cz, Theta);
405                                 glNormal3f(Cx, Cy, Cz);
406                                 RotateAaroundU(0, 0, 1, -sPhi, cPhi, 0, &Cx, &Cy, &Cz, Theta);
407                                 glVertex3f(cPhi * 3 + Cx, sPhi * 3 + Cy, +Cz);
408                                 glVertex3f(cPhi * 3 - Cx, sPhi * 3 - Cy, -Cz);
409
410                                 Phi += Pi / MoebiusDivisions;
411                         }
412                         glEnd();
413                 } else {
414                         for (j = -MoebiusTransversals; j < MoebiusTransversals; j++) {
415                                 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
416                                 glBegin(GL_QUAD_STRIP);
417                                 Phi = 0;
418                                 i = 0;
419                                 while (i < (MoebiusDivisions * 2 + 1)) {
420                                         Theta = Phi / 2;
421                                         cPhi = cos(Phi);
422                                         sPhi = sin(Phi);
423
424                                         RotateAaroundU(cPhi, sPhi, 0, -sPhi, cPhi, 0, &Cx, &Cy, &Cz, Theta);
425                                         glNormal3f(Cx, Cy, Cz);
426                                         RotateAaroundU(0, 0, 1, -sPhi, cPhi, 0, &Cx, &Cy, &Cz, Theta);
427                                         j++;
428                                         if (j == MoebiusTransversals)
429                                                 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialWhite);
430                                         else if (i % 2)
431                                                 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialRed);
432                                         else
433                                                 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialGray);
434                                         glVertex3f(cPhi * 3 + Cx / MoebiusTransversals * j, sPhi * 3 + Cy / MoebiusTransversals * j, +Cz / MoebiusTransversals * j);
435                                         j--;
436                                         if (j == -MoebiusTransversals)
437                                                 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialWhite);
438                                         else if (i % 2)
439                                                 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialRed);
440                                         else
441                                                 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialGray);
442                                         glVertex3f(cPhi * 3 + Cx / MoebiusTransversals * j, sPhi * 3 + Cy / MoebiusTransversals * j, +Cz / MoebiusTransversals * j);
443
444                                         Phi += Pi / MoebiusDivisions;
445                                         i++;
446                                 }
447                                 glEnd();
448                         }
449                         glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
450                 }
451
452                 glEndList();
453                 ep->AreObjectsDefined[ObjMoebiusStrip] = 1;
454 #ifdef DEBUG_LISTS
455                 (void) printf("Strip drawn SLOWLY\n");
456 #endif
457         } else {
458                 glCallList(objects + ObjMoebiusStrip);
459 #ifdef DEBUG_LISTS
460                 (void) printf("Strip drawn quickly\n");
461 #endif
462         }
463
464         if (!noants) {
465                 /* DRAW BLUE ANT */
466                 glPushMatrix();
467                 glRotatef(ep->ant_position + 180, 0, 0, 1);
468                 glTranslatef(3, 0, 0);
469                 glRotatef(ep->ant_position / 2 + 90, 0, 1, 0);
470                 glTranslatef(0.28, 0, -0.45);
471                 draw_escher_ant(ep, MaterialYellow);
472                 glPopMatrix();
473
474                 /* DRAW YELLOW ANT */
475                 glPushMatrix();
476                 glRotatef(ep->ant_position, 0, 0, 1);
477                 glTranslatef(3, 0, 0);
478                 glRotatef(ep->ant_position / 2, 0, 1, 0);
479                 glTranslatef(0.28, 0, -0.45);
480                 draw_escher_ant(ep, MaterialBlue);
481                 glPopMatrix();
482
483                 /* DRAW GREEN ANT */
484                 glPushMatrix();
485                 glRotatef(-ep->ant_position, 0, 0, 1);
486                 glTranslatef(3, 0, 0);
487                 glRotatef(-ep->ant_position / 2, 0, 1, 0);
488                 glTranslatef(0.28, 0, 0.45);
489                 glRotatef(180, 1, 0, 0);
490                 draw_escher_ant(ep, MaterialGreen);
491                 glPopMatrix();
492
493                 /* DRAW CYAN ANT */
494                 glPushMatrix();
495                 glRotatef(-ep->ant_position + 180, 0, 0, 1);
496                 glTranslatef(3, 0, 0);
497                 glRotatef(-ep->ant_position / 2 + 90, 0, 1, 0);
498                 glTranslatef(0.28, 0, 0.45);
499                 glRotatef(180, 1, 0, 0);
500                 draw_escher_ant(ep, MaterialCyan);
501                 glPopMatrix();
502         }
503         ep->ant_position += 1;
504 }
505 #undef MoebiusDivisions
506 #undef MoebiusTransversals
507
508 static void
509 draw_woodplank(escherstruct * ep)
510 {
511         if (!ep->AreObjectsDefined[ObjWoodPlank]) {
512                 glNewList(objects + ObjWoodPlank, GL_COMPILE_AND_EXECUTE);
513                 glBegin(GL_QUADS);
514                 glNormal3f(0, 0, 1);
515                 glTexCoord2f(0, 0);
516                 glVertex3f(-PlankWidth, -PlankHeight, PlankThickness);
517                 glTexCoord2f(1, 0);
518                 glVertex3f(PlankWidth, -PlankHeight, PlankThickness);
519                 glTexCoord2f(1, 1);
520                 glVertex3f(PlankWidth, PlankHeight, PlankThickness);
521                 glTexCoord2f(0, 1);
522                 glVertex3f(-PlankWidth, PlankHeight, PlankThickness);
523                 glNormal3f(0, 0, -1);
524                 glTexCoord2f(0, 0);
525                 glVertex3f(-PlankWidth, PlankHeight, -PlankThickness);
526                 glTexCoord2f(1, 0);
527                 glVertex3f(PlankWidth, PlankHeight, -PlankThickness);
528                 glTexCoord2f(1, 1);
529                 glVertex3f(PlankWidth, -PlankHeight, -PlankThickness);
530                 glTexCoord2f(0, 1);
531                 glVertex3f(-PlankWidth, -PlankHeight, -PlankThickness);
532                 glNormal3f(0, 1, 0);
533                 glTexCoord2f(0, 0);
534                 glVertex3f(-PlankWidth, PlankHeight, PlankThickness);
535                 glTexCoord2f(1, 0);
536                 glVertex3f(PlankWidth, PlankHeight, PlankThickness);
537                 glTexCoord2f(1, 1);
538                 glVertex3f(PlankWidth, PlankHeight, -PlankThickness);
539                 glTexCoord2f(0, 1);
540                 glVertex3f(-PlankWidth, PlankHeight, -PlankThickness);
541                 glNormal3f(0, -1, 0);
542                 glTexCoord2f(0, 0);
543                 glVertex3f(-PlankWidth, -PlankHeight, -PlankThickness);
544                 glTexCoord2f(1, 0);
545                 glVertex3f(PlankWidth, -PlankHeight, -PlankThickness);
546                 glTexCoord2f(1, 1);
547                 glVertex3f(PlankWidth, -PlankHeight, PlankThickness);
548                 glTexCoord2f(0, 1);
549                 glVertex3f(-PlankWidth, -PlankHeight, PlankThickness);
550                 glNormal3f(1, 0, 0);
551                 glTexCoord2f(0, 0);
552                 glVertex3f(PlankWidth, -PlankHeight, PlankThickness);
553                 glTexCoord2f(1, 0);
554                 glVertex3f(PlankWidth, -PlankHeight, -PlankThickness);
555                 glTexCoord2f(1, 1);
556                 glVertex3f(PlankWidth, PlankHeight, -PlankThickness);
557                 glTexCoord2f(0, 1);
558                 glVertex3f(PlankWidth, PlankHeight, PlankThickness);
559                 glNormal3f(-1, 0, 0);
560                 glTexCoord2f(0, 0);
561                 glVertex3f(-PlankWidth, PlankHeight, PlankThickness);
562                 glTexCoord2f(1, 0);
563                 glVertex3f(-PlankWidth, PlankHeight, -PlankThickness);
564                 glTexCoord2f(1, 1);
565                 glVertex3f(-PlankWidth, -PlankHeight, -PlankThickness);
566                 glTexCoord2f(0, 1);
567                 glVertex3f(-PlankWidth, -PlankHeight, PlankThickness);
568                 glEnd();
569                 glEndList();
570                 ep->AreObjectsDefined[ObjWoodPlank] = 1;
571 #ifdef DEBUG_LISTS
572                 (void) printf("WoodPlank drawn SLOWLY\n");
573 #endif
574         } else {
575                 glCallList(objects + ObjWoodPlank);
576 #ifdef DEBUG_LISTS
577                 (void) printf("WoodPlank drawn quickly\n");
578 #endif
579         }
580 }
581
582 static void
583 draw_impossiblecage(escherstruct * ep)
584 {
585         glPushMatrix();
586         glRotatef(90, 0, 1, 0);
587         glTranslatef(0.0, PlankHeight - PlankWidth, -PlankThickness - PlankWidth);
588         draw_woodplank(ep);
589         glPopMatrix();
590         glPushMatrix();
591         glRotatef(90, 0, 0, 1);
592         glTranslatef(0.0, PlankHeight - PlankWidth, PlankWidth - PlankThickness);
593         draw_woodplank(ep);
594         glPopMatrix();
595         glPushMatrix();
596         glRotatef(90, 0, 1, 0);
597         glTranslatef(0.0, PlankWidth - PlankHeight, -PlankThickness - PlankWidth);
598         draw_woodplank(ep);
599         glPopMatrix();
600         glPushMatrix();
601         glTranslatef(0.0, PlankWidth - PlankHeight, 3 * PlankThickness - PlankWidth);
602         draw_woodplank(ep);
603         glPopMatrix();
604         glPushMatrix();
605         glRotatef(90, 0, 0, 1);
606         glTranslatef(0.0, PlankWidth - PlankHeight, PlankWidth - PlankThickness);
607         draw_woodplank(ep);
608         glPopMatrix();
609         glPushMatrix();
610         glTranslatef(0.0, PlankWidth - PlankHeight, PlankWidth - 3 * PlankThickness);
611         draw_woodplank(ep);
612         glPopMatrix();
613         glPushMatrix();
614         glTranslatef(0.0, PlankHeight - PlankWidth, 3 * PlankThickness - PlankWidth);
615         draw_woodplank(ep);
616         glPopMatrix();
617         glPushMatrix();
618         glRotatef(90, 0, 0, 1);
619         glTranslatef(0.0, PlankHeight - PlankWidth, PlankThickness - PlankWidth);
620         draw_woodplank(ep);
621         glPopMatrix();
622         glPushMatrix();
623         glTranslatef(0.0, PlankHeight - PlankWidth, PlankWidth - 3 * PlankThickness);
624         draw_woodplank(ep);
625         glPopMatrix();
626         glPushMatrix();
627         glRotatef(90, 0, 1, 0);
628         glTranslatef(0.0, PlankHeight - PlankWidth, PlankWidth + PlankThickness);
629         draw_woodplank(ep);
630         glPopMatrix();
631         glPushMatrix();
632         glRotatef(90, 0, 0, 1);
633         glTranslatef(0.0, PlankWidth - PlankHeight, PlankThickness - PlankWidth);
634         draw_woodplank(ep);
635         glPopMatrix();
636         glPushMatrix();
637         glRotatef(90, 0, 1, 0);
638         glTranslatef(0.0, PlankWidth - PlankHeight, PlankWidth + PlankThickness);
639         draw_woodplank(ep);
640         glPopMatrix();
641 }
642
643 void
644 draw_escher(ModeInfo * mi)
645 {
646         escherstruct *ep = &escher[MI_SCREEN(mi)];
647
648         Display    *display = MI_DISPLAY(mi);
649         Window      window = MI_WINDOW(mi);
650
651         glXMakeCurrent(display, window, ep->glx_context);
652
653         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
654
655         glPushMatrix();
656
657         glTranslatef(0.0, 0.0, -10.0);
658
659         if (!MI_WIN_IS_ICONIC(mi)) {
660                 glScalef(Scale4Window * ep->WindH / ep->WindW, Scale4Window, Scale4Window);
661         } else {
662                 glScalef(Scale4Iconic * ep->WindH / ep->WindW, Scale4Iconic, Scale4Iconic);
663         }
664
665
666         switch (ep->scene) {
667                 case 1:
668                         glRotatef(ep->step * 100, 1, 0, 0);
669                         glRotatef(ep->step * 95, 0, 1, 0);
670                         glRotatef(ep->step * 90, 0, 0, 1);
671                         draw_moebius(mi);
672                         break;
673                 case 2: /* 196 - 213 */
674                         glRotatef(ep->step * 100, 0, 0, 1);
675                         glRotatef(25 + cos(ep->step * 5) * 6, 1, 0, 0);
676                         glRotatef(204.5 - sin(ep->step * 5) * 8, 0, 1, 0);
677                         draw_impossiblecage(ep);
678                         break;
679         }
680
681         glPopMatrix();
682
683         glFlush();
684
685         glXSwapBuffers(display, window);
686
687         ep->step += 0.025;
688 }
689
690 static void
691 reshape(ModeInfo * mi, int width, int height)
692 {
693         escherstruct *ep = &escher[MI_SCREEN(mi)];
694
695         glViewport(0, 0, ep->WindW = (GLint) width, ep->WindH = (GLint) height);
696         glMatrixMode(GL_PROJECTION);
697         glLoadIdentity();
698         glFrustum(-1.0, 1.0, -1.0, 1.0, 5.0, 15.0);
699         glMatrixMode(GL_MODELVIEW);
700         if (width >= 1024) {
701                 glLineWidth(3);
702                 glPointSize(3);
703         } else if (width >= 512) {
704                 glLineWidth(2);
705                 glPointSize(2);
706         } else {
707                 glLineWidth(1);
708                 glPointSize(1);
709         }
710         ep->AreObjectsDefined[ObjMoebiusStrip] = 0;
711         ep->AreObjectsDefined[ObjAntBody] = 0;
712         ep->AreObjectsDefined[ObjWoodPlank] = 0;
713 }
714
715 static void
716 pinit(ModeInfo * mi)
717 {
718         escherstruct *ep = &escher[MI_SCREEN(mi)];
719
720         glClearDepth(1.0);
721         glClearColor(0.0, 0.0, 0.0, 1.0);
722
723         glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
724         glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
725         glLightfv(GL_LIGHT0, GL_POSITION, position0);
726         glLightfv(GL_LIGHT1, GL_AMBIENT, ambient);
727         glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse);
728         glLightfv(GL_LIGHT1, GL_POSITION, position1);
729         glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
730         glLightModelfv(GL_LIGHT_MODEL_TWO_SIDE, lmodel_twoside);
731         glEnable(GL_LIGHTING);
732         glEnable(GL_LIGHT0);
733         glEnable(GL_LIGHT1);
734         glEnable(GL_NORMALIZE);
735         glFrontFace(GL_CCW);
736         glCullFace(GL_BACK);
737
738         switch (ep->scene) {
739                 case 1:
740                         glShadeModel(GL_SMOOTH);
741                         glEnable(GL_DEPTH_TEST);
742                         glDisable(GL_TEXTURE_2D);
743                         glDisable(GL_CULL_FACE);
744                         break;
745                 case 2:
746                         glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialWhite);
747                         glShadeModel(GL_FLAT);
748                         glDisable(GL_DEPTH_TEST);
749                         glEnable(GL_TEXTURE_2D);
750                         glEnable(GL_CULL_FACE);
751                         break;
752         }
753
754         glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
755         gluBuild2DMipmaps(GL_TEXTURE_2D, 3, WoodTextureWidth, WoodTextureHeight,
756                           GL_RGB, GL_UNSIGNED_BYTE, WoodTextureData);
757         glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
758         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
759         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
760         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
761         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
762
763         glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, front_shininess);
764         glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, front_specular);
765 }
766
767 void
768 init_escher(ModeInfo * mi)
769 {
770         int         screen = MI_SCREEN(mi);
771         escherstruct *ep;
772
773         if (escher == NULL) {
774                 if ((escher = (escherstruct *) calloc(MI_NUM_SCREENS(mi),
775                                              sizeof (escherstruct))) == NULL)
776                         return;
777         }
778         ep = &escher[screen];
779         ep->step = NRAND(90);
780         ep->ant_position = NRAND(90);
781
782         ep->glx_context = init_GL(mi);
783
784         reshape(mi, MI_WIN_WIDTH(mi), MI_WIN_HEIGHT(mi));
785         ep->scene = MI_BATCHCOUNT(mi);
786         if (ep->scene <= 0 || ep->scene > NUM_SCENES)
787                 ep->scene = NRAND(NUM_SCENES) + 1;
788         glDrawBuffer(GL_BACK);
789         objects = glGenLists(3);
790         pinit(mi);
791
792 }
793
794 void
795 change_escher(ModeInfo * mi)
796 {
797         escherstruct *ep = &escher[MI_SCREEN(mi)];
798
799         ep->scene = (ep->scene) % NUM_SCENES + 1;
800         glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), ep->glx_context);
801         pinit(mi);
802 }
803
804 void
805 release_escher(ModeInfo * mi)
806 {
807         if (escher != NULL) {
808                 (void) free((void *) escher);
809                 escher = NULL;
810         }
811         glDeleteLists(objects, 3);
812 }
813
814 #endif