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