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