http://packetstormsecurity.org/UNIX/admin/xscreensaver-4.14.tar.gz
[xscreensaver] / hacks / glx / stairs.c
1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* stairs --- Infinite Stairs, and Escher-like scene. */
3
4 #if 0
5 static const char sccsid[] = "@(#)stairs.c      4.07 97/11/24 xlockmore";
6 #endif
7
8 #undef DEBUG_LISTS
9
10 /*-
11  * Permission to use, copy, modify, and distribute this software and its
12  * documentation for any purpose and without fee is hereby granted,
13  * provided that the above copyright notice appear in all copies and that
14  * both that copyright notice and this permission notice appear in
15  * supporting documentation.
16  *
17  * This file is provided AS IS with no warranties of any kind.  The author
18  * shall have no liability with respect to the infringement of copyrights,
19  * trade secrets or any patents by this file or any part thereof.  In no
20  * event will the author be liable for any lost revenue or profits or
21  * other special, indirect and consequential damages.
22  *
23  * This mode shows some interesting scenes that are impossible OR very
24  * weird to build in the real universe. Much of the scenes are inspirated
25  * on Mauritz Cornelis stairs's works which derivated the mode's name.
26  * M.C. Escher (1898-1972) was a dutch artist and many people prefer to
27  * say he was a mathematician.
28  *
29  * Thanks goes to Brian Paul for making it possible and inexpensive to use 
30  * OpenGL at home.
31  *
32  * Since I'm not a native English speaker, my apologies for any grammatical
33  * mistake.
34  *
35  * My e-mail address is
36  * m-vianna@usa.net
37  *
38  * Marcelo F. Vianna (Jun-01-1997)
39  *
40  * Revision History:
41  * 07-Jan-98: This would be a scene for the escher mode, but now escher mode
42  *            was splitted in different modes for each scene. This is the
43  *            initial release and is not working yet.
44  *            Marcelo F. Vianna.
45  *
46  */
47
48 /*-
49  * Texture mapping is only available on RGBA contexts, Mono and color index
50  * visuals DO NOT support texture mapping in OpenGL.
51  *
52  * BUT Mesa do implements RGBA contexts in pseudo color visuals, so texture
53  * mapping shuld work on PseudoColor, DirectColor, TrueColor using Mesa. Mono
54  * is not officially supported for both OpenGL and Mesa, but seems to not crash
55  * Mesa.
56  *
57  * In real OpenGL, PseudoColor DO NOT support texture map (as far as I know).
58  */
59
60 #include <X11/Intrinsic.h>
61
62 #ifdef STANDALONE
63 # define PROGCLASS                      "Stairs"
64 # define HACK_INIT                      init_stairs
65 # define HACK_DRAW                      draw_stairs
66 # define HACK_RESHAPE           reshape_stairs
67 # define stairs_opts            xlockmore_opts
68 # define DEFAULTS                       "*delay:                20000   \n"                     \
69                                                         "*showFPS:      False   \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 void
418 reshape_stairs(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     int status;
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
469     clear_gl_error();
470     status = gluBuild2DMipmaps(GL_TEXTURE_2D, 3,
471                                WoodTextureWidth, WoodTextureHeight,
472                                GL_RGB, GL_UNSIGNED_BYTE, WoodTextureData);
473     if (status)
474       {
475         const char *s = (char *) gluErrorString (status);
476         fprintf (stderr, "%s: error mipmapping %dx%d texture: %s\n",
477                  progname, WoodTextureWidth, WoodTextureHeight,
478                  (s ? s : "(unknown)"));
479         exit (1);
480       }
481     check_gl_error("mipmapping");
482
483         glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
484         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
485         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
486         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
487         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
488
489         glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, front_shininess);
490         glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, front_specular);
491 }
492
493 void
494 init_stairs(ModeInfo * mi)
495 {
496         int         screen = MI_SCREEN(mi);
497         stairsstruct *sp;
498
499         if (stairs == NULL) {
500                 if ((stairs = (stairsstruct *) calloc(MI_NUM_SCREENS(mi),
501                                              sizeof (stairsstruct))) == NULL)
502                         return;
503         }
504         sp = &stairs[screen];
505         sp->step = 0.0;
506         sp->direction = LRAND() & 1;
507         sp->sphere_position = NRAND(NPOSITIONS / 3) * 3;
508         sp->sphere_tick = 0;
509
510         if ((sp->glx_context = init_GL(mi)) != NULL) {
511
512                 reshape_stairs(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
513                 glDrawBuffer(GL_BACK);
514                 if (!glIsList(objects))
515                         objects = glGenLists(1);
516                 pinit();
517         } else {
518                 MI_CLEARWINDOW(mi);
519         }
520 }
521
522 void
523 draw_stairs(ModeInfo * mi)
524 {
525         stairsstruct *sp = &stairs[MI_SCREEN(mi)];
526
527         Display    *display = MI_DISPLAY(mi);
528         Window      window = MI_WINDOW(mi);
529
530         if (!sp->glx_context)
531                 return;
532
533         glXMakeCurrent(display, window, *(sp->glx_context));
534
535         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
536
537         glPushMatrix();
538
539         glTranslatef(0.0, 0.0, -10.0);
540
541         if (!MI_IS_ICONIC(mi)) {
542                 glScalef(Scale4Window * sp->WindH / sp->WindW, Scale4Window, Scale4Window);
543         } else {
544                 glScalef(Scale4Iconic * sp->WindH / sp->WindW, Scale4Iconic, Scale4Iconic);
545         }
546
547         glRotatef(44.5, 1, 0, 0);
548         glRotatef(50 + ((sp->direction) ? 1 : -1) *
549                ((sp->step * 100 > 120) ? sp->step * 100 - 120 : 0), 0, 1, 0);
550         if (sp->step * 100 >= 360 + 120) {      /* stop showing secrets */
551                 sp->step = 0;
552                 sp->direction = LRAND() & 1;
553         }
554         draw_stairs_internal(mi);
555
556         glPopMatrix();
557
558     if (mi->fps_p) do_fps (mi);
559         glFlush();
560
561         glXSwapBuffers(display, window);
562
563         sp->step += 0.025;
564 }
565
566 void
567 change_stairs(ModeInfo * mi)
568 {
569         stairsstruct *sp = &stairs[MI_SCREEN(mi)];
570
571         if (!sp->glx_context)
572                 return;
573
574         glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(sp->glx_context));
575         pinit();
576 }
577
578 void
579 release_stairs(ModeInfo * mi)
580 {
581         if (stairs != NULL) {
582                 (void) free((void *) stairs);
583                 stairs = NULL;
584         }
585         if (glIsList(objects)) {
586                 glDeleteLists(objects, 1);
587         }
588         FreeAllGL(mi);
589 }
590
591 #endif