e905cae4a76c51f85be59de8d2c96979ed2e320d
[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 address is
37  * m-vianna@usa.net
38  *
39  * Marcelo F. Vianna (Jun-01-1997)
40  *
41  * Revision History:
42  * 07-Jan-98: This would be a scene for the escher mode, but now escher mode
43  *            was splitted in different modes for each scene. This is the
44  *            initial release and is not working yet.
45  *            Marcelo F. Vianna.
46  *
47  */
48
49 /*-
50  * Texture mapping is only available on RGBA contexts, Mono and color index
51  * visuals DO NOT support texture mapping in OpenGL.
52  *
53  * BUT Mesa do implements RGBA contexts in pseudo color visuals, so texture
54  * mapping shuld work on PseudoColor, DirectColor, TrueColor using Mesa. Mono
55  * is not officially supported for both OpenGL and Mesa, but seems to not crash
56  * Mesa.
57  *
58  * In real OpenGL, PseudoColor DO NOT support texture map (as far as I know).
59  */
60
61 #include <X11/Intrinsic.h>
62
63 #ifdef STANDALONE
64 # define PROGCLASS                      "Stairs"
65 # define HACK_INIT                      init_stairs
66 # define HACK_DRAW                      draw_stairs
67 # define HACK_RESHAPE           reshape_stairs
68 # define stairs_opts            xlockmore_opts
69 # define DEFAULTS                       "*delay:                20000   \n"                     \
70                                                         "*showFPS:      False   \n"                     \
71                                                         "*wireframe:    False   \n"
72 # include "xlockmore.h"         /* from the xscreensaver distribution */
73 #else /* !STANDALONE */
74 # include "xlock.h"                     /* from the xlockmore distribution */
75
76 #endif /* !STANDALONE */
77
78 #ifdef USE_GL
79
80 #include <GL/glu.h>
81 #include "e_textures.h"
82
83 ModeSpecOpt stairs_opts =
84 {0, NULL, 0, NULL, NULL};
85
86 #ifdef USE_MODULES
87 ModStruct   stairs_description =
88 {"stairs", "init_stairs", "draw_stairs", "release_stairs",
89  "draw_stairs", "change_stairs", NULL, &stairs_opts,
90  1000, 1, 1, 1, 4, 1.0, "",
91  "Shows Infinite Stairs, an Escher-like scene", 0, NULL};
92
93 #endif
94
95 #define Scale4Window               0.3
96 #define Scale4Iconic               0.4
97
98 #define sqr(A)                     ((A)*(A))
99
100 #ifndef Pi
101 #define Pi                         M_PI
102 #endif
103
104 /*************************************************************************/
105
106 typedef struct {
107         GLint       WindH, WindW;
108         GLfloat     step;
109         Bool        direction;
110         int         AreObjectsDefined[1];
111         int         sphere_position;
112         int         sphere_tick;
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
153 #endif
154 static float MaterialYellow[] =
155 {0.7, 0.7, 0.0, 1.0};
156 static float MaterialWhite[] =
157 {0.7, 0.7, 0.7, 1.0};
158
159 static float positions[] =
160 {
161         -2.5, 4.0, 0.0,         /* First one is FUDGED :) */
162         -3.0, 3.25, 1.0,
163         -3.0, 4.4, 1.5,
164         -3.0, 3.05, 2.0,
165         -3.0, 4.2, 2.5,
166
167         -3.0, 2.85, 3.0,
168         -2.5, 4.0, 3.0,
169         -2.0, 2.75, 3.0,
170         -1.5, 3.9, 3.0,
171         -1.0, 2.65, 3.0,
172         -0.5, 3.8, 3.0,
173         0.0, 2.55, 3.0,
174         0.5, 3.7, 3.0,
175         1.0, 2.45, 3.0,
176         1.5, 3.6, 3.0,
177         2.0, 2.35, 3.0,
178
179         2.0, 3.5, 2.5,
180         2.0, 2.25, 2.0,
181         2.0, 3.4, 1.5,
182         2.0, 2.15, 1.0,
183         2.0, 3.3, 0.5,
184         2.0, 2.05, 0.0,
185         2.0, 3.2, -0.5,
186         2.0, 1.95, -1.0,
187         2.0, 3.1, -1.5,
188         2.0, 1.85, -2.0,
189
190         1.5, 2.9, -2.0,
191         1.0, 1.65, -2.0,
192         0.5, 2.7, -2.0,
193         0.0, 1.55, -2.0,
194         -0.5, 2.5, -2.0,
195         -1.0, 1.45, -2.0,
196 };
197
198 #define NPOSITIONS ((sizeof positions) / (sizeof positions[0]))
199
200 #define SPHERE_TICKS 8
201
202 static stairsstruct *stairs = NULL;
203 static GLuint objects;
204
205 #define ObjSphere    0
206
207 #define PlankWidth      3.0
208 #define PlankHeight     0.35
209 #define PlankThickness  0.15
210
211 static void
212 mySphere(float radius)
213 {
214         GLUquadricObj *quadObj;
215
216         quadObj = gluNewQuadric();
217         gluQuadricDrawStyle(quadObj, (GLenum) GLU_FILL);
218         gluSphere(quadObj, radius, 16, 16);
219         gluDeleteQuadric(quadObj);
220 }
221
222 static void
223 draw_block(GLfloat width, GLfloat height, GLfloat thickness)
224 {
225         glBegin(GL_QUADS);
226         glNormal3f(0, 0, 1);
227         glTexCoord2f(0, 0);
228         glVertex3f(-width, -height, thickness);
229         glTexCoord2f(1, 0);
230         glVertex3f(width, -height, thickness);
231         glTexCoord2f(1, 1);
232         glVertex3f(width, height, thickness);
233         glTexCoord2f(0, 1);
234         glVertex3f(-width, height, thickness);
235         glNormal3f(0, 0, -1);
236         glTexCoord2f(0, 0);
237         glVertex3f(-width, height, -thickness);
238         glTexCoord2f(1, 0);
239         glVertex3f(width, height, -thickness);
240         glTexCoord2f(1, 1);
241         glVertex3f(width, -height, -thickness);
242         glTexCoord2f(0, 1);
243         glVertex3f(-width, -height, -thickness);
244         glNormal3f(0, 1, 0);
245         glTexCoord2f(0, 0);
246         glVertex3f(-width, height, thickness);
247         glTexCoord2f(1, 0);
248         glVertex3f(width, height, thickness);
249         glTexCoord2f(1, 1);
250         glVertex3f(width, height, -thickness);
251         glTexCoord2f(0, 1);
252         glVertex3f(-width, height, -thickness);
253         glNormal3f(0, -1, 0);
254         glTexCoord2f(0, 0);
255         glVertex3f(-width, -height, -thickness);
256         glTexCoord2f(1, 0);
257         glVertex3f(width, -height, -thickness);
258         glTexCoord2f(1, 1);
259         glVertex3f(width, -height, thickness);
260         glTexCoord2f(0, 1);
261         glVertex3f(-width, -height, thickness);
262         glNormal3f(1, 0, 0);
263         glTexCoord2f(0, 0);
264         glVertex3f(width, -height, thickness);
265         glTexCoord2f(1, 0);
266         glVertex3f(width, -height, -thickness);
267         glTexCoord2f(1, 1);
268         glVertex3f(width, height, -thickness);
269         glTexCoord2f(0, 1);
270         glVertex3f(width, height, thickness);
271         glNormal3f(-1, 0, 0);
272         glTexCoord2f(0, 0);
273         glVertex3f(-width, height, thickness);
274         glTexCoord2f(1, 0);
275         glVertex3f(-width, height, -thickness);
276         glTexCoord2f(1, 1);
277         glVertex3f(-width, -height, -thickness);
278         glTexCoord2f(0, 1);
279         glVertex3f(-width, -height, thickness);
280         glEnd();
281 }
282
283 static void
284 draw_stairs_internal(ModeInfo * mi)
285 {
286         stairsstruct *sp = &stairs[MI_SCREEN(mi)];
287         GLfloat     X;
288
289         glPushMatrix();
290         glPushMatrix();
291         glTranslatef(-3.0, 0.1, 2.0);
292         for (X = 0; X < 2; X++) {
293                 draw_block(0.5, 2.7 + 0.1 * X, 0.5);
294                 glTranslatef(0.0, 0.1, -1.0);
295         }
296         glPopMatrix();
297         glTranslatef(-3.0, 0.0, 3.0);
298         glPushMatrix();
299
300         for (X = 0; X < 6; X++) {
301                 draw_block(0.5, 2.6 - 0.1 * X, 0.5);
302                 glTranslatef(1.0, -0.1, 0.0);
303         }
304         glTranslatef(-1.0, -0.9, -1.0);
305         for (X = 0; X < 5; X++) {
306                 draw_block(0.5, 3.0 - 0.1 * X, 0.5);
307                 glTranslatef(0.0, 0.0, -1.0);
308         }
309         glTranslatef(-1.0, -1.1, 1.0);
310         for (X = 0; X < 3; X++) {
311                 draw_block(0.5, 3.5 - 0.1 * X, 0.5);
312                 glTranslatef(-1.0, -0.1, 0.0);
313         }
314         glPopMatrix();
315         glPopMatrix();
316
317         glPushMatrix();
318         glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialYellow);
319
320     {
321       int pos  = sp->sphere_position;
322       int ppos = sp->sphere_position - 3;
323       int npos = sp->sphere_position + 3;
324       GLfloat spx, spy, spz;
325       GLfloat dx, dy, dz;
326       int div;
327
328       if (ppos < 0) ppos += NPOSITIONS;
329       if (npos >= NPOSITIONS) npos -= NPOSITIONS;
330
331       if (sp->sphere_tick < 0)
332         {
333           dx = positions[ppos]   - positions[pos];
334           dy = positions[ppos+1] - positions[pos+1];
335           dz = positions[ppos+2] - positions[pos+2];
336           div = SPHERE_TICKS + sp->sphere_tick;
337         }
338       else
339         {
340           dx = positions[npos]   - positions[pos];
341           dy = positions[npos+1] - positions[pos+1];
342           dz = positions[npos+2] - positions[ppos+2];
343           div = SPHERE_TICKS - sp->sphere_tick;
344         }
345         
346       spx = positions[pos];
347       spy = positions[pos+1];
348       spz = positions[pos+2];
349       if (div != 0)
350         {
351           spx += dx / div;
352           spy += dy / div;
353           spz += dz / div;
354         }
355
356
357       spy -= 0.5;   /* move the bottom of the ball closer to the stairs */
358
359
360 #ifdef DEBUG
361       fprintf(stderr, "%3d %3d   %2.2f %2.2f %2.2f  %2.2f %2.2f %2.2f\n",
362               sp->sphere_position, sp->sphere_tick,
363               dx, dy, dz,
364               spx, spy, spz);
365
366       glBegin(GL_LINE_LOOP);   /* path 1 */
367       glVertex3f(positions[pos],  positions[pos+1],  positions[pos+2]);
368       glVertex3f(positions[npos], positions[npos+1], positions[npos+2]);
369       glEnd();
370
371       glBegin(GL_LINE_LOOP);   /* path 2 */
372       glVertex3f(positions[pos],  positions[pos+1],  positions[pos+2]);
373       glVertex3f(positions[ppos], positions[ppos+1], positions[ppos+2]);
374       glEnd();
375
376       glBegin(GL_LINE_LOOP);  /* base origin */
377       glVertex3f(positions[pos], positions[pos+1]-10, positions[pos+2]);
378       glVertex3f(positions[pos], positions[pos+1]+10, positions[pos+2]);
379       glEnd();
380
381       glBegin(GL_LINE_LOOP);  /* base origin */
382       glVertex3f(positions[pos]-10, positions[pos+1], positions[pos+2]);
383       glVertex3f(positions[pos]+10, positions[pos+1], positions[pos+2]);
384       glEnd();
385
386       glBegin(GL_LINE_LOOP);  /* base origin */
387       glVertex3f(positions[pos], positions[pos+1], positions[pos+2]-10);
388       glVertex3f(positions[pos], positions[pos+1], positions[pos+2]+10);
389       glEnd();
390 #endif /* DEBUG */
391
392       glTranslatef(spx, spy, spz);
393
394 #ifdef DEBUG  /* ball origin */
395       glBegin(GL_LINE_LOOP); glVertex3f(0,-2,0); glVertex3f(0,2,0); glEnd();
396       glBegin(GL_LINE_LOOP); glVertex3f(-2,0,0); glVertex3f(2,0,0); glEnd();
397       glBegin(GL_LINE_LOOP); glVertex3f(0,0,-2); glVertex3f(0,0,2); glEnd();
398 #endif /* DEBUG */
399     }
400
401         if (sp->sphere_position == 0)   /* FUDGE soo its not so obvious */
402                 mySphere(0.48);
403         else
404                 mySphere(0.5);
405         glPopMatrix();
406
407     if (++sp->sphere_tick >= SPHERE_TICKS-1)
408       {
409         sp->sphere_tick = -(SPHERE_TICKS-2);
410         sp->sphere_position += 3;
411         sp->sphere_position += 3;
412       }
413
414         if (sp->sphere_position >= NPOSITIONS)
415                 sp->sphere_position = 0;
416 }
417
418 void
419 reshape_stairs(ModeInfo * mi, int width, int height)
420 {
421         stairsstruct *sp = &stairs[MI_SCREEN(mi)];
422
423         glViewport(0, 0, sp->WindW = (GLint) width, sp->WindH = (GLint) height);
424         glMatrixMode(GL_PROJECTION);
425         glLoadIdentity();
426         glFrustum(-1.0, 1.0, -1.0, 1.0, 5.0, 15.0);
427         glMatrixMode(GL_MODELVIEW);
428         if (width >= 1024) {
429                 glLineWidth(3);
430                 glPointSize(3);
431         } else if (width >= 512) {
432                 glLineWidth(2);
433                 glPointSize(2);
434         } else {
435                 glLineWidth(1);
436                 glPointSize(1);
437         }
438 }
439
440 static void
441 pinit(void)
442 {
443         glClearDepth(1.0);
444         glClearColor(0.0, 0.0, 0.0, 1.0);
445
446         glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
447         glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
448         glLightfv(GL_LIGHT0, GL_POSITION, position0);
449         glLightfv(GL_LIGHT1, GL_AMBIENT, ambient);
450         glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse);
451         glLightfv(GL_LIGHT1, GL_POSITION, position1);
452         glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
453         glLightModelfv(GL_LIGHT_MODEL_TWO_SIDE, lmodel_twoside);
454         glEnable(GL_LIGHTING);
455         glEnable(GL_LIGHT0);
456         glEnable(GL_LIGHT1);
457         glEnable(GL_NORMALIZE);
458         glFrontFace(GL_CCW);
459         glCullFace(GL_BACK);
460
461         glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialWhite);
462         glShadeModel(GL_FLAT);
463         glEnable(GL_DEPTH_TEST);
464         glEnable(GL_TEXTURE_2D);
465         glEnable(GL_CULL_FACE);
466
467         glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
468         gluBuild2DMipmaps(GL_TEXTURE_2D, 3, WoodTextureWidth, WoodTextureHeight,
469                           GL_RGB, GL_UNSIGNED_BYTE, WoodTextureData);
470         glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
471         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
472         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
473         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
474         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
475
476         glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, front_shininess);
477         glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, front_specular);
478 }
479
480 void
481 init_stairs(ModeInfo * mi)
482 {
483         int         screen = MI_SCREEN(mi);
484         stairsstruct *sp;
485
486         if (stairs == NULL) {
487                 if ((stairs = (stairsstruct *) calloc(MI_NUM_SCREENS(mi),
488                                              sizeof (stairsstruct))) == NULL)
489                         return;
490         }
491         sp = &stairs[screen];
492         sp->step = 0.0;
493         sp->direction = LRAND() & 1;
494         sp->sphere_position = NRAND(NPOSITIONS / 3) * 3;
495         sp->sphere_tick = 0;
496
497         if ((sp->glx_context = init_GL(mi)) != NULL) {
498
499                 reshape_stairs(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
500                 glDrawBuffer(GL_BACK);
501                 if (!glIsList(objects))
502                         objects = glGenLists(1);
503                 pinit();
504         } else {
505                 MI_CLEARWINDOW(mi);
506         }
507 }
508
509 void
510 draw_stairs(ModeInfo * mi)
511 {
512         stairsstruct *sp = &stairs[MI_SCREEN(mi)];
513
514         Display    *display = MI_DISPLAY(mi);
515         Window      window = MI_WINDOW(mi);
516
517         if (!sp->glx_context)
518                 return;
519
520         glXMakeCurrent(display, window, *(sp->glx_context));
521
522         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
523
524         glPushMatrix();
525
526         glTranslatef(0.0, 0.0, -10.0);
527
528         if (!MI_IS_ICONIC(mi)) {
529                 glScalef(Scale4Window * sp->WindH / sp->WindW, Scale4Window, Scale4Window);
530         } else {
531                 glScalef(Scale4Iconic * sp->WindH / sp->WindW, Scale4Iconic, Scale4Iconic);
532         }
533
534         glRotatef(44.5, 1, 0, 0);
535         glRotatef(50 + ((sp->direction) ? 1 : -1) *
536                ((sp->step * 100 > 120) ? sp->step * 100 - 120 : 0), 0, 1, 0);
537         if (sp->step * 100 >= 360 + 120) {      /* stop showing secrets */
538                 sp->step = 0;
539                 sp->direction = LRAND() & 1;
540         }
541         draw_stairs_internal(mi);
542
543         glPopMatrix();
544
545     if (mi->fps_p) do_fps (mi);
546         glFlush();
547
548         glXSwapBuffers(display, window);
549
550         sp->step += 0.025;
551 }
552
553 void
554 change_stairs(ModeInfo * mi)
555 {
556         stairsstruct *sp = &stairs[MI_SCREEN(mi)];
557
558         if (!sp->glx_context)
559                 return;
560
561         glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(sp->glx_context));
562         pinit();
563 }
564
565 void
566 release_stairs(ModeInfo * mi)
567 {
568         if (stairs != NULL) {
569                 (void) free((void *) stairs);
570                 stairs = NULL;
571         }
572         if (glIsList(objects)) {
573                 glDeleteLists(objects, 1);
574         }
575         FreeAllGL(mi);
576 }
577
578 #endif