http://www.jwz.org/xscreensaver/xscreensaver-5.13.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
86 # include "xlockmore.h"         /* from the xscreensaver distribution */
87 #else /* !STANDALONE */
88 # include "xlock.h"             /* from the xlockmore distribution */
89 #endif /* !STANDALONE */
90
91 #ifdef MODE_moebius
92
93 #include "e_textures.h"
94 #include "rotator.h"
95 #include "gltrackball.h"
96
97 #define DEF_SOLIDMOEBIUS  "False"
98 #define DEF_DRAWANTS  "True"
99
100 static int  solidmoebius;
101 static int  drawants;
102
103 static XrmOptionDescRec opts[] =
104 {
105   {"-solidmoebius", ".moebius.solidmoebius", XrmoptionNoArg, "on"},
106   {"+solidmoebius", ".moebius.solidmoebius", XrmoptionNoArg, "off"},
107   {"-ants", ".moebius.drawants", XrmoptionNoArg, "on"},
108   {"+ants", ".moebius.drawants", XrmoptionNoArg, "off"}
109 };
110 static argtype vars[] =
111 {
112   {&solidmoebius, "solidmoebius", "Solidmoebius", DEF_SOLIDMOEBIUS, t_Bool},
113   {&drawants, "drawants", "Drawants", DEF_DRAWANTS, t_Bool}
114
115 };
116 static OptionStruct desc[] =
117 {
118         {"-/+solidmoebius", "select between a SOLID or a NET Moebius Strip"},
119         {"-/+drawants", "turn on/off walking ants"}
120 };
121
122 ENTRYPOINT ModeSpecOpt moebius_opts =
123 {sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
124
125 #ifdef USE_MODULES
126 ModStruct   moebius_description =
127 {"moebius", "init_moebius", "draw_moebius", "release_moebius",
128  "draw_moebius", "change_moebius", (char *) NULL, &moebius_opts,
129  1000, 1, 1, 1, 4, 1.0, "",
130  "Shows Moebius Strip II, an Escher-like GL scene with ants", 0, NULL};
131
132 #endif
133
134 #define Scale4Window               0.3
135 #define Scale4Iconic               0.4
136
137 #define sqr(A)                     ((A)*(A))
138
139 #ifndef Pi
140 #define Pi                         M_PI
141 #endif
142
143 #define ObjMoebiusStrip 0
144 #define ObjAntBody      1
145 #define MaxObj          2
146
147 /*************************************************************************/
148
149 typedef struct {
150         GLint       WindH, WindW;
151         GLfloat     step;
152         GLfloat     ant_position;
153         float       ant_step;
154         GLXContext *glx_context;
155     rotator    *rot;
156     trackball_state *trackball;
157     Bool        button_down_p;
158 } moebiusstruct;
159
160 static const float front_shininess[] = {60.0};
161 static const float front_specular[] = {0.7, 0.7, 0.7, 1.0};
162 static const float ambient[] = {0.0, 0.0, 0.0, 1.0};
163 static const float diffuse[] = {1.0, 1.0, 1.0, 1.0};
164 static const float position0[] = {1.0, 1.0, 1.0, 0.0};
165 static const float position1[] = {-1.0, -1.0, 1.0, 0.0};
166 static const float lmodel_ambient[] = {0.5, 0.5, 0.5, 1.0};
167 static const float lmodel_twoside[] = {GL_TRUE};
168
169 static const float MaterialRed[] = {0.7, 0.0, 0.0, 1.0};
170 static const float MaterialGreen[] = {0.1, 0.5, 0.2, 1.0};
171 static const float MaterialBlue[] = {0.0, 0.0, 0.7, 1.0};
172 static const float MaterialCyan[] = {0.2, 0.5, 0.7, 1.0};
173 static const float MaterialYellow[] = {0.7, 0.7, 0.0, 1.0};
174 static const float MaterialMagenta[] = {0.6, 0.2, 0.5, 1.0};
175 static const float MaterialWhite[] = {0.7, 0.7, 0.7, 1.0};
176 static const float MaterialGray[] = {0.2, 0.2, 0.2, 1.0};
177 static const float MaterialGray5[] = {0.5, 0.5, 0.5, 1.0};
178 static const float MaterialGray6[] = {0.6, 0.6, 0.6, 1.0};
179 static const float MaterialGray8[] = {0.8, 0.8, 0.8, 1.0};
180
181 static moebiusstruct *moebius = (moebiusstruct *) NULL;
182
183 #define NUM_SCENES      2
184
185 static Bool
186 mySphere(float radius)
187 {
188         GLUquadricObj *quadObj;
189
190         if ((quadObj = gluNewQuadric()) == 0)
191                 return False;
192         gluQuadricDrawStyle(quadObj, (GLenum) GLU_FILL);
193         gluSphere(quadObj, radius, 16, 16);
194         gluDeleteQuadric(quadObj);
195         return True;
196 }
197
198 static Bool
199 myCone(float radius)
200 {
201         GLUquadricObj *quadObj;
202
203         if ((quadObj = gluNewQuadric()) == 0)
204                 return False;
205         gluQuadricDrawStyle(quadObj, (GLenum) GLU_FILL);
206         gluCylinder(quadObj, radius, 0, radius * 3, 8, 1);
207         gluDeleteQuadric(quadObj);
208         return True;
209 }
210
211 static Bool
212 draw_moebius_ant(moebiusstruct * mp, const float *Material, int mono)
213 {
214         float       cos1 = cos(mp->ant_step);
215         float       cos2 = cos(mp->ant_step + 2 * Pi / 3);
216         float       cos3 = cos(mp->ant_step + 4 * Pi / 3);
217         float       sin1 = sin(mp->ant_step);
218         float       sin2 = sin(mp->ant_step + 2 * Pi / 3);
219         float       sin3 = sin(mp->ant_step + 4 * Pi / 3);
220
221         if (mono)
222                 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialGray5);
223         else
224                 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, Material);
225         glEnable(GL_CULL_FACE);
226         glPushMatrix();
227         glScalef(1, 1.3, 1);
228         if (!mySphere(0.18))
229                 return False;
230         glScalef(1, 1 / 1.3, 1);
231         glTranslatef(0.00, 0.30, 0.00);
232         if (!mySphere(0.2))
233                 return False;
234
235         glTranslatef(-0.05, 0.17, 0.05);
236         glRotatef(-90, 1, 0, 0);
237         glRotatef(-25, 0, 1, 0);
238         if (!myCone(0.05))
239                 return False;
240         glTranslatef(0.00, 0.10, 0.00);
241         if (!myCone(0.05))
242                 return False;
243         glRotatef(25, 0, 1, 0);
244         glRotatef(90, 1, 0, 0);
245
246         glScalef(1, 1.3, 1);
247         glTranslatef(0.15, -0.65, 0.05);
248         if (!mySphere(0.25))
249                 return False;
250         glScalef(1, 1 / 1.3, 1);
251         glPopMatrix();
252         glDisable(GL_CULL_FACE);
253
254         glDisable(GL_LIGHTING);
255         /* ANTENNAS */
256         glBegin(GL_LINES);
257         if (mono)
258                 glColor3fv(MaterialGray5);
259         else
260                 glColor3fv(Material);
261         glVertex3f(0.00, 0.30, 0.00);
262         glColor3fv(MaterialGray);
263         glVertex3f(0.40, 0.70, 0.40);
264         if (mono)
265                 glColor3fv(MaterialGray5);
266         else
267                 glColor3fv(Material);
268         glVertex3f(0.00, 0.30, 0.00);
269         glColor3fv(MaterialGray);
270         glVertex3f(0.40, 0.70, -0.40);
271         glEnd();
272         glBegin(GL_POINTS);
273         if (mono)
274                 glColor3fv(MaterialGray6);
275         else
276                 glColor3fv(MaterialRed);
277         glVertex3f(0.40, 0.70, 0.40);
278         glVertex3f(0.40, 0.70, -0.40);
279         glEnd();
280
281         /* LEFT-FRONT ARM */
282         glBegin(GL_LINE_STRIP);
283         if (mono)
284                 glColor3fv(MaterialGray5);
285         else
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         if (mono)
296                 glColor3fv(MaterialGray5);
297         else
298                 glColor3fv(Material);
299         glVertex3f(0.00, 0.00, 0.18);
300         glVertex3f(0.35 + 0.05 * cos2, 0.00, 0.25);
301         glColor3fv(MaterialGray);
302         glVertex3f(-0.20 + 0.05 * cos2, 0.00 + 0.1 * sin2, 0.45);
303         glEnd();
304
305         /* LEFT-BACK ARM */
306         glBegin(GL_LINE_STRIP);
307         if (mono)
308                 glColor3fv(MaterialGray5);
309         else
310                 glColor3fv(Material);
311         glVertex3f(0.00, -0.05, 0.18);
312         glVertex3f(0.35 + 0.05 * cos3, -0.15, 0.25);
313         glColor3fv(MaterialGray);
314         glVertex3f(-0.20 + 0.05 * cos3, -0.25 + 0.1 * sin3, 0.45);
315         glEnd();
316
317         /* RIGHT-FRONT ARM */
318         glBegin(GL_LINE_STRIP);
319         if (mono)
320                 glColor3fv(MaterialGray5);
321         else
322                 glColor3fv(Material);
323         glVertex3f(0.00, 0.05, -0.18);
324         glVertex3f(0.35 - 0.05 * sin1, 0.15, -0.25);
325         glColor3fv(MaterialGray);
326         glVertex3f(-0.20 - 0.05 * sin1, 0.25 + 0.1 * cos1, -0.45);
327         glEnd();
328
329         /* RIGHT-CENTER ARM */
330         glBegin(GL_LINE_STRIP);
331         if (mono)
332                 glColor3fv(MaterialGray5);
333         else
334                 glColor3fv(Material);
335         glVertex3f(0.00, 0.00, -0.18);
336         glVertex3f(0.35 - 0.05 * sin2, 0.00, -0.25);
337         glColor3fv(MaterialGray);
338         glVertex3f(-0.20 - 0.05 * sin2, 0.00 + 0.1 * cos2, -0.45);
339         glEnd();
340
341         /* RIGHT-BACK ARM */
342         glBegin(GL_LINE_STRIP);
343         if (mono)
344                 glColor3fv(MaterialGray5);
345         else
346                 glColor3fv(Material);
347         glVertex3f(0.00, -0.05, -0.18);
348         glVertex3f(0.35 - 0.05 * sin3, -0.15, -0.25);
349         glColor3fv(MaterialGray);
350         glVertex3f(-0.20 - 0.05 * sin3, -0.25 + 0.1 * cos3, -0.45);
351         glEnd();
352
353         glBegin(GL_POINTS);
354         if (mono)
355                 glColor3fv(MaterialGray8);
356         else
357                 glColor3fv(MaterialMagenta);
358         glVertex3f(-0.20 + 0.05 * cos1, 0.25 + 0.1 * sin1, 0.45);
359         glVertex3f(-0.20 + 0.05 * cos2, 0.00 + 0.1 * sin2, 0.45);
360         glVertex3f(-0.20 + 0.05 * cos3, -0.25 + 0.1 * sin3, 0.45);
361         glVertex3f(-0.20 - 0.05 * sin1, 0.25 + 0.1 * cos1, -0.45);
362         glVertex3f(-0.20 - 0.05 * sin2, 0.00 + 0.1 * cos2, -0.45);
363         glVertex3f(-0.20 - 0.05 * sin3, -0.25 + 0.1 * cos3, -0.45);
364         glEnd();
365
366         glEnable(GL_LIGHTING);
367
368         mp->ant_step += 0.3;
369         return True;
370 }
371
372 static void
373 RotateAaroundU(float Ax, float Ay, float Az,
374                float Ux, float Uy, float Uz,
375                float *Cx, float *Cy, float *Cz,
376                float Theta)
377 {
378         float       cosO = cos(Theta);
379         float       sinO = sin(Theta);
380         float       one_cosO = 1 - cosO;
381         float       Ux2 = sqr(Ux);
382         float       Uy2 = sqr(Uy);
383         float       Uz2 = sqr(Uz);
384         float       UxUy = Ux * Uy;
385         float       UxUz = Ux * Uz;
386         float       UyUz = Uy * Uz;
387
388         *Cx = (Ux2 + cosO * (1 - Ux2)) * Ax + (UxUy * one_cosO - Uz * sinO) * Ay + (UxUz * one_cosO + Uy * sinO) * Az;
389         *Cy = (UxUy * one_cosO + Uz * sinO) * Ax + (Uy2 + cosO * (1 - Uy2)) * Ay + (UyUz * one_cosO - Ux * sinO) * Az;
390         *Cz = (UxUz * one_cosO - Uy * sinO) * Ax + (UyUz * one_cosO + Ux * sinO) * Ay + (Uz2 + cosO * (1 - Uz2)) * Az;
391 }
392
393 #define MoebiusDivisions 40
394 #define MoebiusTransversals 4
395 static Bool
396 draw_moebius_strip(ModeInfo * mi)
397 {
398         GLfloat     Phi, Theta;
399         GLfloat     cPhi, sPhi;
400         moebiusstruct *mp = &moebius[MI_SCREEN(mi)];
401         int         i, j;
402         int         mono = MI_IS_MONO(mi);
403
404         float       Cx, Cy, Cz;
405
406         if (solidmoebius) {
407                 glBegin(GL_QUAD_STRIP);
408                 Phi = 0;
409                 i = 0;
410                 while (i < (MoebiusDivisions * 2 + 1)) {
411                         Theta = Phi / 2;
412                         cPhi = cos(Phi);
413                         sPhi = sin(Phi);
414
415                         i++;
416                         if (mono)
417                                 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialWhite);
418                         else if (i % 2)
419                                 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialRed);
420                         else
421                                 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialGray);
422
423                         RotateAaroundU(cPhi, sPhi, 0, -sPhi, cPhi, 0, &Cx, &Cy, &Cz, Theta);
424                         glNormal3f(Cx, Cy, Cz);
425                         RotateAaroundU(0, 0, 1, -sPhi, cPhi, 0, &Cx, &Cy, &Cz, Theta);
426                         glVertex3f(cPhi * 3 + Cx, sPhi * 3 + Cy, +Cz);
427                         glVertex3f(cPhi * 3 - Cx, sPhi * 3 - Cy, -Cz);
428
429                         Phi += Pi / MoebiusDivisions;
430                 }
431                 glEnd();
432         } else {
433                 for (j = -MoebiusTransversals; j < MoebiusTransversals; j++) {
434                         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
435                         glBegin(GL_QUAD_STRIP);
436                         Phi = 0;
437                         i = 0;
438                         while (i < (MoebiusDivisions * 2 + 1)) {
439                                 Theta = Phi / 2;
440                                 cPhi = cos(Phi);
441                                 sPhi = sin(Phi);
442
443                                 RotateAaroundU(cPhi, sPhi, 0, -sPhi, cPhi, 0, &Cx, &Cy, &Cz, Theta);
444                                 glNormal3f(Cx, Cy, Cz);
445                                 RotateAaroundU(0, 0, 1, -sPhi, cPhi, 0, &Cx, &Cy, &Cz, Theta);
446                                 j++;
447                                 if (j == MoebiusTransversals || mono)
448                                         glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialWhite);
449                                 else if (i % 2)
450                                         glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialRed);
451                                 else
452                                         glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialGray);
453                                 glVertex3f(cPhi * 3 + Cx / MoebiusTransversals * j, sPhi * 3 + Cy / MoebiusTransversals * j, +Cz / MoebiusTransversals * j);
454                                 j--;
455                                 if (j == -MoebiusTransversals || mono)
456                                         glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialWhite);
457                                 else if (i % 2)
458                                         glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialRed);
459                                 else
460                                         glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialGray);
461                                 glVertex3f(cPhi * 3 + Cx / MoebiusTransversals * j, sPhi * 3 + Cy / MoebiusTransversals * j, +Cz / MoebiusTransversals * j);
462
463                                 Phi += Pi / MoebiusDivisions;
464                                 i++;
465                         }
466                         glEnd();
467                 }
468                 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
469         }
470
471         if (drawants) {
472                 /* DRAW BLUE ANT */
473                 glPushMatrix();
474                 glRotatef(mp->ant_position + 180, 0, 0, 1);
475                 glTranslatef(3, 0, 0);
476                 glRotatef(mp->ant_position / 2 + 90, 0, 1, 0);
477                 glTranslatef(0.28, 0, -0.45);
478                 if (!draw_moebius_ant(mp, MaterialYellow, mono))
479                         return False;
480                 glPopMatrix();
481
482                 /* DRAW YELLOW ANT */
483                 glPushMatrix();
484                 glRotatef(mp->ant_position, 0, 0, 1);
485                 glTranslatef(3, 0, 0);
486                 glRotatef(mp->ant_position / 2, 0, 1, 0);
487                 glTranslatef(0.28, 0, -0.45);
488                 if (!draw_moebius_ant(mp, MaterialBlue, mono))
489                         return False;
490                 glPopMatrix();
491
492                 /* DRAW GREEN ANT */
493                 glPushMatrix();
494                 glRotatef(-mp->ant_position, 0, 0, 1);
495                 glTranslatef(3, 0, 0);
496                 glRotatef(-mp->ant_position / 2, 0, 1, 0);
497                 glTranslatef(0.28, 0, 0.45);
498                 glRotatef(180, 1, 0, 0);
499                 if (!draw_moebius_ant(mp, MaterialGreen, mono))
500                         return False;
501                 glPopMatrix();
502
503                 /* DRAW CYAN ANT */
504                 glPushMatrix();
505                 glRotatef(-mp->ant_position + 180, 0, 0, 1);
506                 glTranslatef(3, 0, 0);
507                 glRotatef(-mp->ant_position / 2 + 90, 0, 1, 0);
508                 glTranslatef(0.28, 0, 0.45);
509                 glRotatef(180, 1, 0, 0);
510                 if (!draw_moebius_ant(mp, MaterialCyan, mono))
511                         return False;
512                 glPopMatrix();
513         }
514         mp->ant_position += 1;
515         return True;
516 }
517 #undef MoebiusDivisions
518 #undef MoebiusTransversals
519
520 ENTRYPOINT void
521 reshape_moebius (ModeInfo * mi, int width, int height)
522 {
523         moebiusstruct *mp = &moebius[MI_SCREEN(mi)];
524
525         glViewport(0, 0, mp->WindW = (GLint) width, mp->WindH = (GLint) height);
526         glMatrixMode(GL_PROJECTION);
527         glLoadIdentity();
528         glFrustum(-1.0, 1.0, -1.0, 1.0, 5.0, 15.0);
529         glMatrixMode(GL_MODELVIEW);
530         if (width >= 1024) {
531                 glLineWidth(3);
532                 glPointSize(3);
533         } else if (width >= 512) {
534                 glLineWidth(2);
535                 glPointSize(2);
536         } else {
537                 glLineWidth(1);
538                 glPointSize(1);
539         }
540 }
541
542 static void
543 pinit(void)
544 {
545     int status;
546         glClearDepth(1.0);
547         glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
548         glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
549         glLightfv(GL_LIGHT0, GL_POSITION, position0);
550         glLightfv(GL_LIGHT1, GL_AMBIENT, ambient);
551         glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse);
552         glLightfv(GL_LIGHT1, GL_POSITION, position1);
553         glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
554         glLightModelfv(GL_LIGHT_MODEL_TWO_SIDE, lmodel_twoside);
555         glEnable(GL_LIGHTING);
556         glEnable(GL_LIGHT0);
557         glEnable(GL_LIGHT1);
558         glEnable(GL_NORMALIZE);
559         glFrontFace(GL_CCW);
560         glCullFace(GL_BACK);
561
562         /* moebius */
563         glShadeModel(GL_SMOOTH);
564         glEnable(GL_DEPTH_TEST);
565         glDisable(GL_TEXTURE_2D);
566         glDisable(GL_CULL_FACE);
567
568         glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
569
570     clear_gl_error();
571         status = gluBuild2DMipmaps(GL_TEXTURE_2D, 3,
572                                WoodTextureWidth, WoodTextureHeight,
573                                GL_RGB, GL_UNSIGNED_BYTE, WoodTextureData);
574     if (status)
575       {
576         const char *s = (char *) gluErrorString (status);
577         fprintf (stderr, "%s: error mipmapping %dx%d texture: %s\n",
578                  progname, WoodTextureWidth, WoodTextureHeight,
579                  (s ? s : "(unknown)"));
580         exit (1);
581       }
582     check_gl_error("mipmapping");
583
584         glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
585         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
586         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
587         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
588         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
589
590         glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, front_shininess);
591         glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, front_specular);
592 }
593
594
595
596 ENTRYPOINT void
597 release_moebius (ModeInfo * mi)
598 {
599         if (moebius != NULL) {
600                 (void) free((void *) moebius);
601                 moebius = (moebiusstruct *) NULL;
602         }
603         FreeAllGL(mi);
604 }
605
606 ENTRYPOINT Bool
607 moebius_handle_event (ModeInfo *mi, XEvent *event)
608 {
609   moebiusstruct *mp = &moebius[MI_SCREEN(mi)];
610
611   if (event->xany.type == ButtonPress &&
612       event->xbutton.button == Button1)
613     {
614       mp->button_down_p = True;
615       gltrackball_start (mp->trackball,
616                          event->xbutton.x, event->xbutton.y,
617                          MI_WIDTH (mi), MI_HEIGHT (mi));
618       return True;
619     }
620   else if (event->xany.type == ButtonRelease &&
621            event->xbutton.button == Button1)
622     {
623       mp->button_down_p = False;
624       return True;
625     }
626   else if (event->xany.type == ButtonPress &&
627            (event->xbutton.button == Button4 ||
628             event->xbutton.button == Button5 ||
629             event->xbutton.button == Button6 ||
630             event->xbutton.button == Button7))
631     {
632       gltrackball_mousewheel (mp->trackball, event->xbutton.button, 10,
633                               !!event->xbutton.state);
634       return True;
635     }
636   else if (event->xany.type == MotionNotify &&
637            mp->button_down_p)
638     {
639       gltrackball_track (mp->trackball,
640                          event->xmotion.x, event->xmotion.y,
641                          MI_WIDTH (mi), MI_HEIGHT (mi));
642       return True;
643     }
644
645   return False;
646 }
647
648
649 ENTRYPOINT void
650 init_moebius (ModeInfo * mi)
651 {
652         moebiusstruct *mp;
653
654         if (moebius == NULL) {
655                 if ((moebius = (moebiusstruct *) calloc(MI_NUM_SCREENS(mi),
656                                             sizeof (moebiusstruct))) == NULL)
657                         return;
658         }
659         mp = &moebius[MI_SCREEN(mi)];
660         mp->step = NRAND(90);
661         mp->ant_position = NRAND(90);
662
663     {
664       double rot_speed = 0.3;
665       mp->rot = make_rotator (rot_speed, rot_speed, rot_speed, 1, 0, True);
666       mp->trackball = gltrackball_init ();
667     }
668
669         if ((mp->glx_context = init_GL(mi)) != NULL) {
670
671                 reshape_moebius(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
672                 glDrawBuffer(GL_BACK);
673                 pinit();
674         } else {
675                 MI_CLEARWINDOW(mi);
676         }
677 }
678
679 ENTRYPOINT void
680 draw_moebius (ModeInfo * mi)
681 {
682         moebiusstruct *mp;
683
684         Display    *display = MI_DISPLAY(mi);
685         Window      window = MI_WINDOW(mi);
686
687         if (moebius == NULL)
688             return;
689         mp = &moebius[MI_SCREEN(mi)];
690
691         MI_IS_DRAWN(mi) = True;
692
693         if (!mp->glx_context)
694                 return;
695
696         glXMakeCurrent(display, window, *(mp->glx_context));
697
698         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
699
700         glPushMatrix();
701
702         glTranslatef(0.0, 0.0, -10.0);
703
704     gltrackball_rotate (mp->trackball);
705
706         if (!MI_IS_ICONIC(mi)) {
707                 glScalef(Scale4Window * mp->WindH / mp->WindW, Scale4Window, Scale4Window);
708         } else {
709                 glScalef(Scale4Iconic * mp->WindH / mp->WindW, Scale4Iconic, Scale4Iconic);
710         }
711
712     {
713       double x, y, z;
714       get_rotation (mp->rot, &x, &y, &z, !mp->button_down_p);
715       glRotatef (x * 360, 1.0, 0.0, 0.0);
716       glRotatef (y * 360, 0.0, 1.0, 0.0);
717       glRotatef (z * 360, 0.0, 0.0, 1.0);
718     }
719
720         /* moebius */
721         if (!draw_moebius_strip(mi)) {
722                 release_moebius(mi);
723                 return;
724         }
725
726         glPopMatrix();
727
728     if (MI_IS_FPS(mi)) do_fps (mi);
729         glFlush();
730
731         glXSwapBuffers(display, window);
732
733         mp->step += 0.025;
734 }
735
736 #ifndef STANDALONE
737 ENTRYPOINT void
738 change_moebius (ModeInfo * mi)
739 {
740         moebiusstruct *mp = &moebius[MI_SCREEN(mi)];
741
742         if (!mp->glx_context)
743                 return;
744
745         glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(mp->glx_context));
746         pinit();
747 }
748 #endif /* !STANDALONE */
749
750
751 XSCREENSAVER_MODULE ("Moebius", moebius)
752
753 #endif