began for maemo
[xscreensaver] / xscreensaver / hacks / glx / cage.c
1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* cage --- the Impossible Cage, an Escher like scene. */
3
4 #if 0
5 static const char sccsid[] = "@(#)cage.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: Added FPS stuff E.Lassauge <lassauge@mail.dotcom.fr>
46  * 01-Nov-2000: Allocation checks
47  * 01-Jan-1998: Mode separated from escher and renamed
48  * 08-Jun-1997: New scene implemented: "Impossible Cage" based in a M.C.
49  *              Escher's painting with the same name (quite similar). The
50  *              first GL mode to use texture mapping.
51  *              The "Impossible Cage" scene doesn't use DEPTH BUFFER, the
52  *              wood planks are drawn consistently using GL_CULL_FACE, and
53  *              the painter's algorithm is used to sort the planks.
54  *              Marcelo F. Vianna.
55  * 07-Jun-1997: Speed ups in Moebius Strip using GL_CULL_FACE.
56  *              Marcelo F. Vianna.
57  * 03-Jun-1997: Initial Release (Only one scene: "Moebius Strip")
58  *              The Moebius Strip scene was inspirated in a M.C. Escher's
59  *              painting named Moebius Strip II in wich ants walk across a
60  *              Moebius Strip path, sometimes meeting each other and sometimes
61  *              being in "opposite faces" (note that the moebius strip has
62  *              only one face and one edge).
63  *              Marcelo F. Vianna.
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 #ifdef STANDALONE
79 # define MODE_cage
80 # define DEFAULTS                       "*delay:                25000   \n"                     \
81                                                         "*showFPS:      False   \n"                     \
82                                                         "*wireframe:    False   \n"
83 # define refresh_cage 0
84 # define reshape_cage 0
85 # define cage_handle_event 0
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_cage
92
93 #include "e_textures.h"
94
95 ENTRYPOINT ModeSpecOpt cage_opts =
96 {0, (XrmOptionDescRec *) NULL, 0, (argtype *) NULL, (OptionStruct *) NULL};
97
98 #ifdef USE_MODULES
99 ModStruct   cage_description =
100 {"cage", "init_cage", "draw_cage", "release_cage",
101  "draw_cage", "change_cage", (char *) NULL, &cage_opts,
102  25000, 1, 1, 1, 1.0, 4, "",
103  "Shows the Impossible Cage, an Escher-like GL scene", 0, NULL};
104
105 #endif
106
107 #define Scale4Window               0.3
108 #define Scale4Iconic               0.4
109
110 #define sqr(A)                     ((A)*(A))
111
112 #ifndef Pi
113 #define Pi                         M_PI
114 #endif
115
116 #define ObjWoodPlank    0
117 #define MaxObj          1
118
119 /*************************************************************************/
120
121 typedef struct {
122         GLint       WindH, WindW;
123         GLfloat     step;
124         GLXContext *glx_context;
125 } cagestruct;
126
127 static const float front_shininess[] = {60.0};
128 static const float front_specular[] = {0.7, 0.7, 0.7, 1.0};
129 static const float ambient[] = {0.0, 0.0, 0.0, 1.0};
130 static const float diffuse[] = {1.0, 1.0, 1.0, 1.0};
131 static const float position0[] = {1.0, 1.0, 1.0, 0.0};
132 static const float position1[] = {-1.0, -1.0, 1.0, 0.0};
133 static const float lmodel_ambient[] = {0.5, 0.5, 0.5, 1.0};
134 static const float lmodel_twoside[] = {GL_TRUE};
135
136 static const float MaterialWhite[] = {0.7, 0.7, 0.7, 1.0};
137
138 static cagestruct *cage = (cagestruct *) NULL;
139
140 #define PlankWidth      3.0
141 #define PlankHeight     0.35
142 #define PlankThickness  0.15
143
144 static Bool 
145 draw_woodplank(cagestruct * cp, int wire)
146 {
147         glBegin(wire ? GL_LINES : GL_QUADS);
148         glNormal3f(0, 0, 1);
149         glTexCoord2f(0, 0);
150         glVertex3f(-PlankWidth, -PlankHeight, PlankThickness);
151         glTexCoord2f(1, 0);
152         glVertex3f(PlankWidth, -PlankHeight, PlankThickness);
153         glTexCoord2f(1, 1);
154         glVertex3f(PlankWidth, PlankHeight, PlankThickness);
155         glTexCoord2f(0, 1);
156         glVertex3f(-PlankWidth, PlankHeight, PlankThickness);
157         glNormal3f(0, 0, -1);
158         glTexCoord2f(0, 0);
159         glVertex3f(-PlankWidth, PlankHeight, -PlankThickness);
160         glTexCoord2f(1, 0);
161         glVertex3f(PlankWidth, PlankHeight, -PlankThickness);
162         glTexCoord2f(1, 1);
163         glVertex3f(PlankWidth, -PlankHeight, -PlankThickness);
164         glTexCoord2f(0, 1);
165         glVertex3f(-PlankWidth, -PlankHeight, -PlankThickness);
166         glNormal3f(0, 1, 0);
167         glTexCoord2f(0, 0);
168         glVertex3f(-PlankWidth, PlankHeight, PlankThickness);
169         glTexCoord2f(1, 0);
170         glVertex3f(PlankWidth, PlankHeight, PlankThickness);
171         glTexCoord2f(1, 1);
172         glVertex3f(PlankWidth, PlankHeight, -PlankThickness);
173         glTexCoord2f(0, 1);
174         glVertex3f(-PlankWidth, PlankHeight, -PlankThickness);
175         glNormal3f(0, -1, 0);
176         glTexCoord2f(0, 0);
177         glVertex3f(-PlankWidth, -PlankHeight, -PlankThickness);
178         glTexCoord2f(1, 0);
179         glVertex3f(PlankWidth, -PlankHeight, -PlankThickness);
180         glTexCoord2f(1, 1);
181         glVertex3f(PlankWidth, -PlankHeight, PlankThickness);
182         glTexCoord2f(0, 1);
183         glVertex3f(-PlankWidth, -PlankHeight, PlankThickness);
184         glNormal3f(1, 0, 0);
185         glTexCoord2f(0, 0);
186         glVertex3f(PlankWidth, -PlankHeight, PlankThickness);
187         glTexCoord2f(1, 0);
188         glVertex3f(PlankWidth, -PlankHeight, -PlankThickness);
189         glTexCoord2f(1, 1);
190         glVertex3f(PlankWidth, PlankHeight, -PlankThickness);
191         glTexCoord2f(0, 1);
192         glVertex3f(PlankWidth, PlankHeight, PlankThickness);
193         glNormal3f(-1, 0, 0);
194         glTexCoord2f(0, 0);
195         glVertex3f(-PlankWidth, PlankHeight, PlankThickness);
196         glTexCoord2f(1, 0);
197         glVertex3f(-PlankWidth, PlankHeight, -PlankThickness);
198         glTexCoord2f(1, 1);
199         glVertex3f(-PlankWidth, -PlankHeight, -PlankThickness);
200         glTexCoord2f(0, 1);
201         glVertex3f(-PlankWidth, -PlankHeight, PlankThickness);
202         glEnd();
203
204         return True;
205 }
206
207 static Bool
208 draw_impossiblecage(cagestruct * cp, int wire)
209 {
210         glPushMatrix();
211         glRotatef(90, 0, 1, 0);
212         glTranslatef(0.0, PlankHeight - PlankWidth, -PlankThickness - PlankWidth);
213         if (!draw_woodplank(cp, wire))
214                 return False;
215         glPopMatrix();
216         glPushMatrix();
217         glRotatef(90, 0, 0, 1);
218         glTranslatef(0.0, PlankHeight - PlankWidth, PlankWidth - PlankThickness);
219         if (!draw_woodplank(cp, wire))
220                 return False;
221         glPopMatrix();
222         glPushMatrix();
223         glRotatef(90, 0, 1, 0);
224         glTranslatef(0.0, PlankWidth - PlankHeight, -PlankThickness - PlankWidth);
225         if (!draw_woodplank(cp, wire))
226                 return False;
227         glPopMatrix();
228         glPushMatrix();
229         glTranslatef(0.0, PlankWidth - PlankHeight, 3 * PlankThickness - PlankWidth);
230         if (!draw_woodplank(cp, wire))
231                 return False;
232         glPopMatrix();
233         glPushMatrix();
234         glRotatef(90, 0, 0, 1);
235         glTranslatef(0.0, PlankWidth - PlankHeight, PlankWidth - PlankThickness);
236         if (!draw_woodplank(cp, wire))
237                 return False;
238         glPopMatrix();
239         glPushMatrix();
240         glTranslatef(0.0, PlankWidth - PlankHeight, PlankWidth - 3 * PlankThickness);
241         if (!draw_woodplank(cp, wire))
242                 return False;
243         glPopMatrix();
244         glPushMatrix();
245         glTranslatef(0.0, PlankHeight - PlankWidth, 3 * PlankThickness - PlankWidth);
246         if (!draw_woodplank(cp, wire))
247                 return False;
248         glPopMatrix();
249         glPushMatrix();
250         glRotatef(90, 0, 0, 1);
251         glTranslatef(0.0, PlankHeight - PlankWidth, PlankThickness - PlankWidth);
252         if (!draw_woodplank(cp, wire))
253                 return False;
254         glPopMatrix();
255         glPushMatrix();
256         glTranslatef(0.0, PlankHeight - PlankWidth, PlankWidth - 3 * PlankThickness);
257         if (!draw_woodplank(cp, wire))
258                 return False;
259         glPopMatrix();
260         glPushMatrix();
261         glRotatef(90, 0, 1, 0);
262         glTranslatef(0.0, PlankHeight - PlankWidth, PlankWidth + PlankThickness);
263         if (!draw_woodplank(cp, wire))
264                 return False;
265         glPopMatrix();
266         glPushMatrix();
267         glRotatef(90, 0, 0, 1);
268         glTranslatef(0.0, PlankWidth - PlankHeight, PlankThickness - PlankWidth);
269         if (!draw_woodplank(cp, wire))
270                 return False;
271         glPopMatrix();
272         glPushMatrix();
273         glRotatef(90, 0, 1, 0);
274         glTranslatef(0.0, PlankWidth - PlankHeight, PlankWidth + PlankThickness);
275         if (!draw_woodplank(cp, wire))
276                 return False;
277         glPopMatrix();
278         return True;
279 }
280
281 static void
282 reshape(ModeInfo * mi, int width, int height)
283 {
284         cagestruct *cp = &cage[MI_SCREEN(mi)];
285         int i;
286
287         glViewport(0, 0, cp->WindW = (GLint) width, cp->WindH = (GLint) height);
288         glMatrixMode(GL_PROJECTION);
289         glLoadIdentity();
290         glFrustum(-1.0, 1.0, -1.0, 1.0, 5.0, 15.0);
291         glMatrixMode(GL_MODELVIEW);
292         i = width / 512 + 1;
293         glLineWidth(i);
294         glPointSize(i);
295 }
296
297 static void
298 pinit(ModeInfo *mi)
299 {
300         int status;
301
302         glClearDepth(1.0);
303         glClearColor(0.0, 0.0, 0.0, 1.0);
304
305     if (MI_IS_WIREFRAME(mi))
306       return;
307
308         glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
309         glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
310         glLightfv(GL_LIGHT0, GL_POSITION, position0);
311         glLightfv(GL_LIGHT1, GL_AMBIENT, ambient);
312         glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse);
313         glLightfv(GL_LIGHT1, GL_POSITION, position1);
314         glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
315         glLightModelfv(GL_LIGHT_MODEL_TWO_SIDE, lmodel_twoside);
316         glEnable(GL_LIGHTING);
317         glEnable(GL_LIGHT0);
318         glEnable(GL_LIGHT1);
319         glEnable(GL_NORMALIZE);
320         glFrontFace(GL_CCW);
321         glCullFace(GL_BACK);
322
323         /* cage */
324         glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialWhite);
325         glShadeModel(GL_FLAT);
326         glDisable(GL_DEPTH_TEST);
327         glEnable(GL_TEXTURE_2D);
328         glEnable(GL_CULL_FACE);
329
330         glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
331
332         clear_gl_error();
333         if (MI_IS_MONO(mi))
334       status = 0;
335     else
336       status = gluBuild2DMipmaps(GL_TEXTURE_2D, 3,
337                                  WoodTextureWidth, WoodTextureHeight,
338                                  GL_RGB, GL_UNSIGNED_BYTE, WoodTextureData);
339         if (status)
340           {
341                 const char *s = (char *) gluErrorString (status);
342                 fprintf (stderr, "%s: error mipmapping texture: %s\n",
343                                  progname, (s ? s : "(unknown)"));
344                 exit (1);
345           }
346         check_gl_error("mipmapping");
347
348         glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
349         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
350         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
351         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
352         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
353
354         glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, front_shininess);
355         glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, front_specular);
356 }
357
358 ENTRYPOINT void
359 release_cage (ModeInfo * mi)
360 {
361         if (cage != NULL) {
362                 int screen;
363
364                 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
365                         cagestruct *cp = &cage[screen];
366
367                         if (cp->glx_context) {
368                                 cp->glx_context = (GLXContext *) NULL;
369                         }
370                 }
371                 (void) free((void *) cage);
372                 cage = (cagestruct *) NULL;
373         }
374         FreeAllGL(mi);
375 }
376
377 ENTRYPOINT void
378 init_cage (ModeInfo * mi)
379 {
380         cagestruct *cp;
381
382         if (cage == NULL) {
383                 if ((cage = (cagestruct *) calloc(MI_NUM_SCREENS(mi),
384                                                sizeof (cagestruct))) == NULL)
385                         return;
386         }
387         cp = &cage[MI_SCREEN(mi)];
388
389         cp->step = NRAND(90);
390         if ((cp->glx_context = init_GL(mi)) != NULL) {
391
392                 reshape(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
393                 glDrawBuffer(GL_BACK);
394                 pinit(mi);
395         } else {
396                 MI_CLEARWINDOW(mi);
397         }
398 }
399
400 ENTRYPOINT void
401 draw_cage (ModeInfo * mi)
402 {
403         Display    *display = MI_DISPLAY(mi);
404         Window      window = MI_WINDOW(mi);
405         cagestruct *cp;
406
407         if (cage == NULL)
408                 return;
409         cp = &cage[MI_SCREEN(mi)];
410
411         MI_IS_DRAWN(mi) = True;
412         if (!cp->glx_context)
413                 return;
414
415         glXMakeCurrent(display, window, *(cp->glx_context));
416
417         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
418
419         glPushMatrix();
420
421         glTranslatef(0.0, 0.0, -10.0);
422
423         if (!MI_IS_ICONIC(mi)) {
424                 glScalef(Scale4Window * cp->WindH / cp->WindW, Scale4Window, Scale4Window);
425         } else {
426                 glScalef(Scale4Iconic * cp->WindH / cp->WindW, Scale4Iconic, Scale4Iconic);
427         }
428
429         /* cage */
430         glRotatef(cp->step * 100, 0, 0, 1);
431         glRotatef(25 + cos(cp->step * 5) * 6, 1, 0, 0);
432         glRotatef(204.5 - sin(cp->step * 5) * 8, 0, 1, 0);
433         if (!draw_impossiblecage(cp, MI_IS_WIREFRAME(mi))) {
434                 release_cage(mi);
435                 return;
436         }
437
438         glPopMatrix();
439         if (MI_IS_FPS(mi)) do_fps (mi);
440         glFlush();
441
442         glXSwapBuffers(display, window);
443
444         cp->step += 0.025;
445 }
446
447 #ifndef STANDALONE
448 ENTRYPOINT void
449 change_cage (ModeInfo * mi)
450 {
451         cagestruct *cp = &cage[MI_SCREEN(mi)];
452
453         if (!cp->glx_context)
454                 return;
455
456         glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(cp->glx_context));
457         pinit(mi);
458 }
459 #endif /* !STANDALONE */
460
461 XSCREENSAVER_MODULE ("Cage", cage)
462
463 #endif