5896e195a1e1792e1ad906aba0a3df92f636aca8
[xscreensaver] / hacks / glx / stairs.c
1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* stairs --- Infinite Stairs, and Escher-like scene. */
3
4 #if !defined( lint ) && !defined( SABER )
5 static const char sccsid[] = "@(#)stairs.c      4.07 97/11/24 xlockmore";
6
7 #endif
8
9 #undef DEBUG_LISTS
10
11 /*-
12  * Permission to use, copy, modify, and distribute this software and its
13  * documentation for any purpose and without fee is hereby granted,
14  * provided that the above copyright notice appear in all copies and that
15  * both that copyright notice and this permission notice appear in
16  * supporting documentation.
17  *
18  * This file is provided AS IS with no warranties of any kind.  The author
19  * shall have no liability with respect to the infringement of copyrights,
20  * trade secrets or any patents by this file or any part thereof.  In no
21  * event will the author be liable for any lost revenue or profits or
22  * other special, indirect and consequential damages.
23  *
24  * This mode shows some interesting scenes that are impossible OR very
25  * weird to build in the real universe. Much of the scenes are inspirated
26  * on Mauritz Cornelis stairs's works which derivated the mode's name.
27  * M.C. Escher (1898-1972) was a dutch artist and many people prefer to
28  * say he was a mathematician.
29  *
30  * Thanks goes to Brian Paul for making it possible and inexpensive to use 
31  * OpenGL at home.
32  *
33  * Since I'm not a native English speaker, my apologies for any grammatical
34  * mistake.
35  *
36  * My e-mail addresses are
37  * vianna@cat.cbpf.br 
38  *         and
39  * m-vianna@usa.net
40  *
41  * Marcelo F. Vianna (Jun-01-1997)
42  *
43  * Revision History:
44  * 07-Jan-98: This would be a scene for the escher mode, but now escher mode
45  *            was splitted in different modes for each scene. This is the
46  *            initial release and is not working yet.
47  *            Marcelo F. Vianna.
48  *
49  */
50
51 /*-
52  * Texture mapping is only available on RGBA contexts, Mono and color index
53  * visuals DO NOT support texture mapping in OpenGL.
54  *
55  * BUT Mesa do implements RGBA contexts in pseudo color visuals, so texture
56  * mapping shuld work on PseudoColor, DirectColor, TrueColor using Mesa. Mono
57  * is not officially supported for both OpenGL and Mesa, but seems to not crash
58  * Mesa.
59 z *
60  * In real OpenGL, PseudoColor DO NOT support texture map (as far as I know).
61  */
62
63 #include <X11/Intrinsic.h>
64
65 #ifdef STANDALONE
66 # define PROGCLASS                      "Stairs"
67 # define HACK_INIT                      init_stairs
68 # define HACK_DRAW                      draw_stairs
69 # define stairs_opts            xlockmore_opts
70 # define DEFAULTS                       "*cycles:               1       \n"                     \
71                                                         "*delay:                200000  \n"                     \
72                                                         "*wireframe:    False   \n"
73 # include "xlockmore.h"         /* from the xscreensaver distribution */
74 #else /* !STANDALONE */
75 # include "xlock.h"                     /* from the xlockmore distribution */
76
77 #endif /* !STANDALONE */
78
79 #ifdef USE_GL
80
81 #include <GL/glu.h>
82 #include "e_textures.h"
83
84 ModeSpecOpt stairs_opts =
85 {0, NULL, 0, NULL, NULL};
86
87 #ifdef USE_MODULES
88 ModStruct   stairs_description =
89 {"stairs", "init_stairs", "draw_stairs", "release_stairs",
90  "draw_stairs", "change_stairs", NULL, &stairs_opts,
91  1000, 1, 1, 1, 1.0, "",
92  "Shows Infinite Stairs, an Escher-like scene", 0, NULL};
93
94 #endif
95
96 #define Scale4Window               0.3
97 #define Scale4Iconic               0.4
98
99 #define sqr(A)                     ((A)*(A))
100
101 #ifndef Pi
102 #define Pi                         M_PI
103 #endif
104
105 /*************************************************************************/
106
107 typedef struct {
108         GLint       WindH, WindW;
109         GLfloat     step;
110         Bool        direction;
111   int         AreObjectsDefined[1];
112         int     sphere_position;
113         GLXContext *glx_context;
114 } stairsstruct;
115
116 static float front_shininess[] =
117 {60.0};
118 static float front_specular[] =
119 {0.7, 0.7, 0.7, 1.0};
120 static float ambient[] =
121 {0.0, 0.0, 0.0, 1.0};
122 static float diffuse[] =
123 {1.0, 1.0, 1.0, 1.0};
124 static float position0[] =
125 {1.0, 1.0, 1.0, 0.0};
126 static float position1[] =
127 {-1.0, -1.0, 1.0, 0.0};
128 static float lmodel_ambient[] =
129 {0.5, 0.5, 0.5, 1.0};
130 static float lmodel_twoside[] =
131 {GL_TRUE};
132
133 #if 0
134 static float MaterialRed[] =
135 {0.7, 0.0, 0.0, 1.0};
136 static float MaterialGreen[] =
137 {0.1, 0.5, 0.2, 1.0};
138 static float MaterialBlue[] =
139 {0.0, 0.0, 0.7, 1.0};
140 static float MaterialCyan[] =
141 {0.2, 0.5, 0.7, 1.0};
142 static float MaterialMagenta[] =
143 {0.6, 0.2, 0.5, 1.0};
144 static float MaterialGray[] =
145 {0.2, 0.2, 0.2, 1.0};
146 static float MaterialGray5[] =
147 {0.5, 0.5, 0.5, 1.0};
148 static float MaterialGray6[] =
149 {0.6, 0.6, 0.6, 1.0};
150 static float MaterialGray8[] =
151 {0.8, 0.8, 0.8, 1.0};
152 #endif
153 static float MaterialYellow[] =
154 {0.7, 0.7, 0.0, 1.0};
155 static float MaterialWhite[] =
156 {0.7, 0.7, 0.7, 1.0};
157
158 static float positions[] =
159 {
160   -2.5, 4.0, 0.0, /* First one is FUDGED :) */
161   -3.0, 3.25, 1.0,
162   -3.0, 4.4, 1.5,
163   -3.0, 3.05, 2.0,
164   -3.0, 4.2, 2.5,
165
166   -3.0, 2.85, 3.0,
167   -2.5, 4.0, 3.0,
168   -2.0, 2.75, 3.0,
169   -1.5, 3.9, 3.0,
170   -1.0, 2.65, 3.0,
171   -0.5, 3.8, 3.0,
172   0.0, 2.55, 3.0,
173   0.5, 3.7, 3.0,
174   1.0, 2.45, 3.0,
175   1.5, 3.6, 3.0,
176   2.0, 2.35, 3.0,
177
178   2.0, 3.5, 2.5,
179   2.0, 2.25, 2.0,
180   2.0, 3.4, 1.5,
181   2.0, 2.15, 1.0,
182   2.0, 3.3, 0.5,
183   2.0, 2.05, 0.0,
184   2.0, 3.2, -0.5,
185   2.0, 1.95, -1.0,
186   2.0, 3.1, -1.5,
187   2.0, 1.85, -2.0,
188
189   1.5, 2.9, -2.0,
190   1.0, 1.65, -2.0,
191   0.5, 2.7, -2.0,
192   0.0, 1.55, -2.0,
193   -0.5, 2.5, -2.0,
194   -1.0, 1.45, -2.0,
195 };
196 #define NPOSITIONS ((sizeof positions) / (sizeof positions[0]))
197
198 static stairsstruct *stairs = NULL;
199 static GLuint objects;
200
201 #define ObjSphere    0
202
203 #define PlankWidth      3.0
204 #define PlankHeight     0.35
205 #define PlankThickness  0.15
206
207 static void
208 mySphere(float radius)
209 {
210         GLUquadricObj *quadObj;
211
212         quadObj = gluNewQuadric();
213         gluQuadricDrawStyle(quadObj, (GLenum) GLU_FILL);
214         gluSphere(quadObj, radius, 16, 16);
215         gluDeleteQuadric(quadObj);
216 }
217
218 static void
219 draw_block(stairsstruct * sp, GLfloat width, GLfloat height, GLfloat thickness)
220 {
221                 glBegin(GL_QUADS);
222                 glNormal3f(0, 0, 1);
223                 glTexCoord2f(0, 0);
224                 glVertex3f(-width, -height, thickness);
225                 glTexCoord2f(1, 0);
226                 glVertex3f(width, -height, thickness);
227                 glTexCoord2f(1, 1);
228                 glVertex3f(width, height, thickness);
229                 glTexCoord2f(0, 1);
230                 glVertex3f(-width, height, thickness);
231                 glNormal3f(0, 0, -1);
232                 glTexCoord2f(0, 0);
233                 glVertex3f(-width, height, -thickness);
234                 glTexCoord2f(1, 0);
235                 glVertex3f(width, height, -thickness);
236                 glTexCoord2f(1, 1);
237                 glVertex3f(width, -height, -thickness);
238                 glTexCoord2f(0, 1);
239                 glVertex3f(-width, -height, -thickness);
240                 glNormal3f(0, 1, 0);
241                 glTexCoord2f(0, 0);
242                 glVertex3f(-width, height, thickness);
243                 glTexCoord2f(1, 0);
244                 glVertex3f(width, height, thickness);
245                 glTexCoord2f(1, 1);
246                 glVertex3f(width, height, -thickness);
247                 glTexCoord2f(0, 1);
248                 glVertex3f(-width, height, -thickness);
249                 glNormal3f(0, -1, 0);
250                 glTexCoord2f(0, 0);
251                 glVertex3f(-width, -height, -thickness);
252                 glTexCoord2f(1, 0);
253                 glVertex3f(width, -height, -thickness);
254                 glTexCoord2f(1, 1);
255                 glVertex3f(width, -height, thickness);
256                 glTexCoord2f(0, 1);
257                 glVertex3f(-width, -height, thickness);
258                 glNormal3f(1, 0, 0);
259                 glTexCoord2f(0, 0);
260                 glVertex3f(width, -height, thickness);
261                 glTexCoord2f(1, 0);
262                 glVertex3f(width, -height, -thickness);
263                 glTexCoord2f(1, 1);
264                 glVertex3f(width, height, -thickness);
265                 glTexCoord2f(0, 1);
266                 glVertex3f(width, height, thickness);
267                 glNormal3f(-1, 0, 0);
268                 glTexCoord2f(0, 0);
269                 glVertex3f(-width, height, thickness);
270                 glTexCoord2f(1, 0);
271                 glVertex3f(-width, height, -thickness);
272                 glTexCoord2f(1, 1);
273                 glVertex3f(-width, -height, -thickness);
274                 glTexCoord2f(0, 1);
275                 glVertex3f(-width, -height, thickness);
276                 glEnd();
277 }
278
279 static void
280 draw_degree(stairsstruct * sp, GLfloat w, GLfloat h , GLfloat t)
281 {
282   draw_block(sp, w, h, t);
283 }
284
285 static void
286 draw_stairs_internal(ModeInfo *mi)
287 {
288         stairsstruct *sp = &stairs[MI_SCREEN(mi)];
289   GLfloat X;
290   
291   glPushMatrix();
292   glPushMatrix();
293   glTranslatef(-3.0, 0.1, 2.0);
294   for (X=0; X< 2; X++) {
295     draw_degree(sp, 0.5, 2.7+0.1*X, 0.5);
296     glTranslatef( 0.0, 0.1,-1.0);
297   }
298         glPopMatrix();
299   glTranslatef(-3.0, 0.0, 3.0);
300   glPushMatrix();
301
302   for (X=0; X< 6; X++) {
303     draw_degree(sp, 0.5, 2.6-0.1*X, 0.5);
304     glTranslatef( 1.0,-0.1, 0.0);
305   }
306   glTranslatef(-1.0,-0.9,-1.0);
307   for (X=0; X< 5; X++) {
308     draw_degree(sp, 0.5, 3.0-0.1*X, 0.5);
309     glTranslatef( 0.0, 0.0,-1.0);
310   }
311   glTranslatef(-1.0,-1.1, 1.0);
312   for (X=0; X< 3; X++) {
313     draw_degree(sp, 0.5, 3.5-0.1*X, 0.5);
314     glTranslatef(-1.0,-0.1, 0.0);
315   }
316         glPopMatrix();
317         glPopMatrix();
318
319   glPushMatrix();
320   glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialYellow);
321
322   glTranslatef((GLfloat) positions[sp->sphere_position],
323        (GLfloat) positions[sp->sphere_position + 1],
324        (GLfloat) positions[sp->sphere_position + 2]);
325   if (sp->sphere_position == 0) /* FUDGE soo its not so obvious */
326      mySphere(0.48);
327    else
328      mySphere(0.5);
329   glPopMatrix();
330   sp->sphere_position += 3;
331   if (sp->sphere_position >= NPOSITIONS)
332     sp->sphere_position = 0;
333 }
334
335 static void
336 reshape(ModeInfo * mi, int width, int height)
337 {
338         stairsstruct *sp = &stairs[MI_SCREEN(mi)];
339
340         glViewport(0, 0, sp->WindW = (GLint) width, sp->WindH = (GLint) height);
341         glMatrixMode(GL_PROJECTION);
342         glLoadIdentity();
343         glFrustum(-1.0, 1.0, -1.0, 1.0, 5.0, 15.0);
344         glMatrixMode(GL_MODELVIEW);
345         if (width >= 1024) {
346                 glLineWidth(3);
347                 glPointSize(3);
348         } else if (width >= 512) {
349                 glLineWidth(2);
350                 glPointSize(2);
351         } else {
352                 glLineWidth(1);
353                 glPointSize(1);
354         }
355 }
356
357 static void
358 pinit(ModeInfo * mi)
359 {
360 /*      stairsstruct *sp = &stairs[MI_SCREEN(mi)];*/
361
362         glClearDepth(1.0);
363         glClearColor(0.0, 0.0, 0.0, 1.0);
364
365         glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
366         glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
367         glLightfv(GL_LIGHT0, GL_POSITION, position0);
368         glLightfv(GL_LIGHT1, GL_AMBIENT, ambient);
369         glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse);
370         glLightfv(GL_LIGHT1, GL_POSITION, position1);
371         glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
372         glLightModelfv(GL_LIGHT_MODEL_TWO_SIDE, lmodel_twoside);
373         glEnable(GL_LIGHTING);
374         glEnable(GL_LIGHT0);
375         glEnable(GL_LIGHT1);
376         glEnable(GL_NORMALIZE);
377         glFrontFace(GL_CCW);
378         glCullFace(GL_BACK);
379
380   glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialWhite);
381         glShadeModel(GL_FLAT);
382         glEnable(GL_DEPTH_TEST);
383         glEnable(GL_TEXTURE_2D);
384         glEnable(GL_CULL_FACE);
385
386         glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
387         gluBuild2DMipmaps(GL_TEXTURE_2D, 3, WoodTextureWidth, WoodTextureHeight,
388                           GL_RGB, GL_UNSIGNED_BYTE, WoodTextureData);
389         glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
390         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
391         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
392         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
393         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
394
395         glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, front_shininess);
396         glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, front_specular);
397 }
398
399 void
400 init_stairs(ModeInfo * mi)
401 {
402         int         screen = MI_SCREEN(mi);
403         stairsstruct *sp;
404
405         if (stairs == NULL) {
406                 if ((stairs = (stairsstruct *) calloc(MI_NUM_SCREENS(mi),
407                                              sizeof (stairsstruct))) == NULL)
408                         return;
409         }
410         sp = &stairs[screen];
411         sp->step = 0.0;
412   sp->direction = LRAND() & 1;
413         sp->sphere_position = NRAND(NPOSITIONS / 3) * 3;
414
415         if ((sp->glx_context = init_GL(mi)) != NULL) {
416
417                 reshape(mi, MI_WIN_WIDTH(mi), MI_WIN_HEIGHT(mi));
418                 glDrawBuffer(GL_BACK);
419                 if (!glIsList(objects))
420                         objects = glGenLists(1);
421                 pinit(mi);
422         } else {
423     MI_CLEARWINDOW(mi);
424         }
425 }
426
427 void
428 draw_stairs(ModeInfo * mi)
429 {
430         stairsstruct *sp = &stairs[MI_SCREEN(mi)];
431
432         Display    *display = MI_DISPLAY(mi);
433         Window      window = MI_WINDOW(mi);
434
435         if (!sp->glx_context)
436                 return;
437
438         glXMakeCurrent(display, window, *(sp->glx_context));
439
440         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
441
442         glPushMatrix();
443
444         glTranslatef(0.0, 0.0, -10.0);
445
446         if (!MI_WIN_IS_ICONIC(mi)) {
447                 glScalef(Scale4Window * sp->WindH / sp->WindW, Scale4Window, Scale4Window);
448         } else {
449                 glScalef(Scale4Iconic * sp->WindH / sp->WindW, Scale4Iconic, Scale4Iconic);
450         }
451
452         glRotatef(44.5, 1, 0, 0);
453   glRotatef(50 + ((sp->direction) ? 1 : -1 ) *
454      ((sp->step * 100 > 120) ? sp->step * 100 - 120 : 0), 0, 1, 0);
455   if (sp->step * 100 >= 360 + 120) {  /* stop showing secrets */
456     sp->step = 0;
457     sp->direction = LRAND() & 1;
458   }
459   draw_stairs_internal(mi);
460
461         glPopMatrix();
462
463         glFlush();
464
465         glXSwapBuffers(display, window);
466
467         sp->step += 0.025;
468 }
469
470 void
471 change_stairs(ModeInfo * mi)
472 {
473         stairsstruct *sp = &stairs[MI_SCREEN(mi)];
474
475         if (!sp->glx_context)
476                 return;
477
478         glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(sp->glx_context));
479         pinit(mi);
480 }
481
482 void
483 release_stairs(ModeInfo * mi)
484 {
485         if (stairs != NULL) {
486                 (void) free((void *) stairs);
487                 stairs = NULL;
488         }
489         if (glIsList(objects)) {
490                 glDeleteLists(objects, 1);
491         }
492         FreeAllGL(mi);
493 }
494
495 #endif