97808a0c86d14509c7688b6b89c3c2b696351faf
[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 stairs_opts            xlockmore_opts
68 # define DEFAULTS                       "*cycles:               1       \n"                     \
69                                                         "*delay:                20000   \n"                     \
70                                                         "*wireframe:    False   \n"
71 # include "xlockmore.h"         /* from the xscreensaver distribution */
72 #else /* !STANDALONE */
73 # include "xlock.h"                     /* from the xlockmore distribution */
74
75 #endif /* !STANDALONE */
76
77 #ifdef USE_GL
78
79 #include <GL/glu.h>
80 #include "e_textures.h"
81
82 ModeSpecOpt stairs_opts =
83 {0, NULL, 0, NULL, NULL};
84
85 #ifdef USE_MODULES
86 ModStruct   stairs_description =
87 {"stairs", "init_stairs", "draw_stairs", "release_stairs",
88  "draw_stairs", "change_stairs", NULL, &stairs_opts,
89  1000, 1, 1, 1, 4, 1.0, "",
90  "Shows Infinite Stairs, an Escher-like scene", 0, NULL};
91
92 #endif
93
94 #define Scale4Window               0.3
95 #define Scale4Iconic               0.4
96
97 #define sqr(A)                     ((A)*(A))
98
99 #ifndef Pi
100 #define Pi                         M_PI
101 #endif
102
103 /*************************************************************************/
104
105 typedef struct {
106         GLint       WindH, WindW;
107         GLfloat     step;
108         Bool        direction;
109         int         AreObjectsDefined[1];
110         int         sphere_position;
111         int         sphere_tick;
112         GLXContext *glx_context;
113 } stairsstruct;
114
115 static float front_shininess[] =
116 {60.0};
117 static float front_specular[] =
118 {0.7, 0.7, 0.7, 1.0};
119 static float ambient[] =
120 {0.0, 0.0, 0.0, 1.0};
121 static float diffuse[] =
122 {1.0, 1.0, 1.0, 1.0};
123 static float position0[] =
124 {1.0, 1.0, 1.0, 0.0};
125 static float position1[] =
126 {-1.0, -1.0, 1.0, 0.0};
127 static float lmodel_ambient[] =
128 {0.5, 0.5, 0.5, 1.0};
129 static float lmodel_twoside[] =
130 {GL_TRUE};
131
132 #if 0
133 static float MaterialRed[] =
134 {0.7, 0.0, 0.0, 1.0};
135 static float MaterialGreen[] =
136 {0.1, 0.5, 0.2, 1.0};
137 static float MaterialBlue[] =
138 {0.0, 0.0, 0.7, 1.0};
139 static float MaterialCyan[] =
140 {0.2, 0.5, 0.7, 1.0};
141 static float MaterialMagenta[] =
142 {0.6, 0.2, 0.5, 1.0};
143 static float MaterialGray[] =
144 {0.2, 0.2, 0.2, 1.0};
145 static float MaterialGray5[] =
146 {0.5, 0.5, 0.5, 1.0};
147 static float MaterialGray6[] =
148 {0.6, 0.6, 0.6, 1.0};
149 static float MaterialGray8[] =
150 {0.8, 0.8, 0.8, 1.0};
151
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
197 #define NPOSITIONS ((sizeof positions) / (sizeof positions[0]))
198
199 #define SPHERE_TICKS 8
200
201 static stairsstruct *stairs = NULL;
202 static GLuint objects;
203
204 #define ObjSphere    0
205
206 #define PlankWidth      3.0
207 #define PlankHeight     0.35
208 #define PlankThickness  0.15
209
210 static void
211 mySphere(float radius)
212 {
213         GLUquadricObj *quadObj;
214
215         quadObj = gluNewQuadric();
216         gluQuadricDrawStyle(quadObj, (GLenum) GLU_FILL);
217         gluSphere(quadObj, radius, 16, 16);
218         gluDeleteQuadric(quadObj);
219 }
220
221 static void
222 draw_block(GLfloat width, GLfloat height, GLfloat thickness)
223 {
224         glBegin(GL_QUADS);
225         glNormal3f(0, 0, 1);
226         glTexCoord2f(0, 0);
227         glVertex3f(-width, -height, thickness);
228         glTexCoord2f(1, 0);
229         glVertex3f(width, -height, thickness);
230         glTexCoord2f(1, 1);
231         glVertex3f(width, height, thickness);
232         glTexCoord2f(0, 1);
233         glVertex3f(-width, height, thickness);
234         glNormal3f(0, 0, -1);
235         glTexCoord2f(0, 0);
236         glVertex3f(-width, height, -thickness);
237         glTexCoord2f(1, 0);
238         glVertex3f(width, height, -thickness);
239         glTexCoord2f(1, 1);
240         glVertex3f(width, -height, -thickness);
241         glTexCoord2f(0, 1);
242         glVertex3f(-width, -height, -thickness);
243         glNormal3f(0, 1, 0);
244         glTexCoord2f(0, 0);
245         glVertex3f(-width, height, thickness);
246         glTexCoord2f(1, 0);
247         glVertex3f(width, height, thickness);
248         glTexCoord2f(1, 1);
249         glVertex3f(width, height, -thickness);
250         glTexCoord2f(0, 1);
251         glVertex3f(-width, height, -thickness);
252         glNormal3f(0, -1, 0);
253         glTexCoord2f(0, 0);
254         glVertex3f(-width, -height, -thickness);
255         glTexCoord2f(1, 0);
256         glVertex3f(width, -height, -thickness);
257         glTexCoord2f(1, 1);
258         glVertex3f(width, -height, thickness);
259         glTexCoord2f(0, 1);
260         glVertex3f(-width, -height, thickness);
261         glNormal3f(1, 0, 0);
262         glTexCoord2f(0, 0);
263         glVertex3f(width, -height, thickness);
264         glTexCoord2f(1, 0);
265         glVertex3f(width, -height, -thickness);
266         glTexCoord2f(1, 1);
267         glVertex3f(width, height, -thickness);
268         glTexCoord2f(0, 1);
269         glVertex3f(width, height, thickness);
270         glNormal3f(-1, 0, 0);
271         glTexCoord2f(0, 0);
272         glVertex3f(-width, height, thickness);
273         glTexCoord2f(1, 0);
274         glVertex3f(-width, height, -thickness);
275         glTexCoord2f(1, 1);
276         glVertex3f(-width, -height, -thickness);
277         glTexCoord2f(0, 1);
278         glVertex3f(-width, -height, thickness);
279         glEnd();
280 }
281
282 static void
283 draw_stairs_internal(ModeInfo * mi)
284 {
285         stairsstruct *sp = &stairs[MI_SCREEN(mi)];
286         GLfloat     X;
287
288         glPushMatrix();
289         glPushMatrix();
290         glTranslatef(-3.0, 0.1, 2.0);
291         for (X = 0; X < 2; X++) {
292                 draw_block(0.5, 2.7 + 0.1 * X, 0.5);
293                 glTranslatef(0.0, 0.1, -1.0);
294         }
295         glPopMatrix();
296         glTranslatef(-3.0, 0.0, 3.0);
297         glPushMatrix();
298
299         for (X = 0; X < 6; X++) {
300                 draw_block(0.5, 2.6 - 0.1 * X, 0.5);
301                 glTranslatef(1.0, -0.1, 0.0);
302         }
303         glTranslatef(-1.0, -0.9, -1.0);
304         for (X = 0; X < 5; X++) {
305                 draw_block(0.5, 3.0 - 0.1 * X, 0.5);
306                 glTranslatef(0.0, 0.0, -1.0);
307         }
308         glTranslatef(-1.0, -1.1, 1.0);
309         for (X = 0; X < 3; X++) {
310                 draw_block(0.5, 3.5 - 0.1 * X, 0.5);
311                 glTranslatef(-1.0, -0.1, 0.0);
312         }
313         glPopMatrix();
314         glPopMatrix();
315
316         glPushMatrix();
317         glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialYellow);
318
319     {
320       int pos  = sp->sphere_position;
321       int ppos = sp->sphere_position - 3;
322       int npos = sp->sphere_position + 3;
323       GLfloat spx, spy, spz;
324       GLfloat dx, dy, dz;
325       int div;
326
327       if (ppos < 0) ppos += NPOSITIONS;
328       if (npos >= NPOSITIONS) npos -= NPOSITIONS;
329
330       if (sp->sphere_tick < 0)
331         {
332           dx = positions[ppos]   - positions[pos];
333           dy = positions[ppos+1] - positions[pos+1];
334           dz = positions[ppos+2] - positions[pos+2];
335           div = SPHERE_TICKS + sp->sphere_tick;
336         }
337       else
338         {
339           dx = positions[npos]   - positions[pos];
340           dy = positions[npos+1] - positions[pos+1];
341           dz = positions[npos+2] - positions[ppos+2];
342           div = SPHERE_TICKS - sp->sphere_tick;
343         }
344         
345       spx = positions[pos];
346       spy = positions[pos+1];
347       spz = positions[pos+2];
348       if (div != 0)
349         {
350           spx += dx / div;
351           spy += dy / div;
352           spz += dz / div;
353         }
354
355
356       spy -= 0.5;   /* move the bottom of the ball closer to the stairs */
357
358
359 #ifdef DEBUG
360       fprintf(stderr, "%3d %3d   %2.2f %2.2f %2.2f  %2.2f %2.2f %2.2f\n",
361               sp->sphere_position, sp->sphere_tick,
362               dx, dy, dz,
363               spx, spy, spz);
364
365       glBegin(GL_LINE_LOOP);   /* path 1 */
366       glVertex3f(positions[pos],  positions[pos+1],  positions[pos+2]);
367       glVertex3f(positions[npos], positions[npos+1], positions[npos+2]);
368       glEnd();
369
370       glBegin(GL_LINE_LOOP);   /* path 2 */
371       glVertex3f(positions[pos],  positions[pos+1],  positions[pos+2]);
372       glVertex3f(positions[ppos], positions[ppos+1], positions[ppos+2]);
373       glEnd();
374
375       glBegin(GL_LINE_LOOP);  /* base origin */
376       glVertex3f(positions[pos], positions[pos+1]-10, positions[pos+2]);
377       glVertex3f(positions[pos], positions[pos+1]+10, positions[pos+2]);
378       glEnd();
379
380       glBegin(GL_LINE_LOOP);  /* base origin */
381       glVertex3f(positions[pos]-10, positions[pos+1], positions[pos+2]);
382       glVertex3f(positions[pos]+10, positions[pos+1], positions[pos+2]);
383       glEnd();
384
385       glBegin(GL_LINE_LOOP);  /* base origin */
386       glVertex3f(positions[pos], positions[pos+1], positions[pos+2]-10);
387       glVertex3f(positions[pos], positions[pos+1], positions[pos+2]+10);
388       glEnd();
389 #endif /* DEBUG */
390
391       glTranslatef(spx, spy, spz);
392
393 #ifdef DEBUG  /* ball origin */
394       glBegin(GL_LINE_LOOP); glVertex3f(0,-2,0); glVertex3f(0,2,0); glEnd();
395       glBegin(GL_LINE_LOOP); glVertex3f(-2,0,0); glVertex3f(2,0,0); glEnd();
396       glBegin(GL_LINE_LOOP); glVertex3f(0,0,-2); glVertex3f(0,0,2); glEnd();
397 #endif /* DEBUG */
398     }
399
400         if (sp->sphere_position == 0)   /* FUDGE soo its not so obvious */
401                 mySphere(0.48);
402         else
403                 mySphere(0.5);
404         glPopMatrix();
405
406     if (++sp->sphere_tick >= SPHERE_TICKS-1)
407       {
408         sp->sphere_tick = -(SPHERE_TICKS-2);
409         sp->sphere_position += 3;
410         sp->sphere_position += 3;
411       }
412
413         if (sp->sphere_position >= NPOSITIONS)
414                 sp->sphere_position = 0;
415 }
416
417 static void
418 reshape(ModeInfo * mi, int width, int height)
419 {
420         stairsstruct *sp = &stairs[MI_SCREEN(mi)];
421
422         glViewport(0, 0, sp->WindW = (GLint) width, sp->WindH = (GLint) height);
423         glMatrixMode(GL_PROJECTION);
424         glLoadIdentity();
425         glFrustum(-1.0, 1.0, -1.0, 1.0, 5.0, 15.0);
426         glMatrixMode(GL_MODELVIEW);
427         if (width >= 1024) {
428                 glLineWidth(3);
429                 glPointSize(3);
430         } else if (width >= 512) {
431                 glLineWidth(2);
432                 glPointSize(2);
433         } else {
434                 glLineWidth(1);
435                 glPointSize(1);
436         }
437 }
438
439 static void
440 pinit(void)
441 {
442         glClearDepth(1.0);
443         glClearColor(0.0, 0.0, 0.0, 1.0);
444
445         glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
446         glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
447         glLightfv(GL_LIGHT0, GL_POSITION, position0);
448         glLightfv(GL_LIGHT1, GL_AMBIENT, ambient);
449         glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse);
450         glLightfv(GL_LIGHT1, GL_POSITION, position1);
451         glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
452         glLightModelfv(GL_LIGHT_MODEL_TWO_SIDE, lmodel_twoside);
453         glEnable(GL_LIGHTING);
454         glEnable(GL_LIGHT0);
455         glEnable(GL_LIGHT1);
456         glEnable(GL_NORMALIZE);
457         glFrontFace(GL_CCW);
458         glCullFace(GL_BACK);
459
460         glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialWhite);
461         glShadeModel(GL_FLAT);
462         glEnable(GL_DEPTH_TEST);
463         glEnable(GL_TEXTURE_2D);
464         glEnable(GL_CULL_FACE);
465
466         glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
467         gluBuild2DMipmaps(GL_TEXTURE_2D, 3, WoodTextureWidth, WoodTextureHeight,
468                           GL_RGB, GL_UNSIGNED_BYTE, WoodTextureData);
469         glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
470         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
471         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
472         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
473         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
474
475         glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, front_shininess);
476         glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, front_specular);
477 }
478
479 void
480 init_stairs(ModeInfo * mi)
481 {
482         int         screen = MI_SCREEN(mi);
483         stairsstruct *sp;
484
485         if (stairs == NULL) {
486                 if ((stairs = (stairsstruct *) calloc(MI_NUM_SCREENS(mi),
487                                              sizeof (stairsstruct))) == NULL)
488                         return;
489         }
490         sp = &stairs[screen];
491         sp->step = 0.0;
492         sp->direction = LRAND() & 1;
493         sp->sphere_position = NRAND(NPOSITIONS / 3) * 3;
494         sp->sphere_tick = 0;
495
496         if ((sp->glx_context = init_GL(mi)) != NULL) {
497
498                 reshape(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
499                 glDrawBuffer(GL_BACK);
500                 if (!glIsList(objects))
501                         objects = glGenLists(1);
502                 pinit();
503         } else {
504                 MI_CLEARWINDOW(mi);
505         }
506 }
507
508 void
509 draw_stairs(ModeInfo * mi)
510 {
511         stairsstruct *sp = &stairs[MI_SCREEN(mi)];
512
513         Display    *display = MI_DISPLAY(mi);
514         Window      window = MI_WINDOW(mi);
515
516         if (!sp->glx_context)
517                 return;
518
519         glXMakeCurrent(display, window, *(sp->glx_context));
520
521         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
522
523         glPushMatrix();
524
525         glTranslatef(0.0, 0.0, -10.0);
526
527         if (!MI_IS_ICONIC(mi)) {
528                 glScalef(Scale4Window * sp->WindH / sp->WindW, Scale4Window, Scale4Window);
529         } else {
530                 glScalef(Scale4Iconic * sp->WindH / sp->WindW, Scale4Iconic, Scale4Iconic);
531         }
532
533         glRotatef(44.5, 1, 0, 0);
534         glRotatef(50 + ((sp->direction) ? 1 : -1) *
535                ((sp->step * 100 > 120) ? sp->step * 100 - 120 : 0), 0, 1, 0);
536         if (sp->step * 100 >= 360 + 120) {      /* stop showing secrets */
537                 sp->step = 0;
538                 sp->direction = LRAND() & 1;
539         }
540         draw_stairs_internal(mi);
541
542         glPopMatrix();
543
544         glFlush();
545
546         glXSwapBuffers(display, window);
547
548         sp->step += 0.025;
549 }
550
551 void
552 change_stairs(ModeInfo * mi)
553 {
554         stairsstruct *sp = &stairs[MI_SCREEN(mi)];
555
556         if (!sp->glx_context)
557                 return;
558
559         glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(sp->glx_context));
560         pinit();
561 }
562
563 void
564 release_stairs(ModeInfo * mi)
565 {
566         if (stairs != NULL) {
567                 (void) free((void *) stairs);
568                 stairs = NULL;
569         }
570         if (glIsList(objects)) {
571                 glDeleteLists(objects, 1);
572         }
573         FreeAllGL(mi);
574 }
575
576 #endif