http://svn.poeml.de/viewvc/ppc/src-unpacked/xscreensaver/xscreensaver-4.12.tar.bz2...
[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     int status;
444         glClearDepth(1.0);
445         glClearColor(0.0, 0.0, 0.0, 1.0);
446
447         glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
448         glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
449         glLightfv(GL_LIGHT0, GL_POSITION, position0);
450         glLightfv(GL_LIGHT1, GL_AMBIENT, ambient);
451         glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse);
452         glLightfv(GL_LIGHT1, GL_POSITION, position1);
453         glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
454         glLightModelfv(GL_LIGHT_MODEL_TWO_SIDE, lmodel_twoside);
455         glEnable(GL_LIGHTING);
456         glEnable(GL_LIGHT0);
457         glEnable(GL_LIGHT1);
458         glEnable(GL_NORMALIZE);
459         glFrontFace(GL_CCW);
460         glCullFace(GL_BACK);
461
462         glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialWhite);
463         glShadeModel(GL_FLAT);
464         glEnable(GL_DEPTH_TEST);
465         glEnable(GL_TEXTURE_2D);
466         glEnable(GL_CULL_FACE);
467
468         glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
469
470     clear_gl_error();
471     status = gluBuild2DMipmaps(GL_TEXTURE_2D, 3,
472                                WoodTextureWidth, WoodTextureHeight,
473                                GL_RGB, GL_UNSIGNED_BYTE, WoodTextureData);
474     if (status)
475       {
476         const char *s = (char *) gluErrorString (status);
477         fprintf (stderr, "%s: error mipmapping %dx%d texture: %s\n",
478                  progname, WoodTextureWidth, WoodTextureHeight,
479                  (s ? s : "(unknown)"));
480         exit (1);
481       }
482     check_gl_error("mipmapping");
483
484         glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
485         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
486         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
487         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
488         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
489
490         glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, front_shininess);
491         glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, front_specular);
492 }
493
494 void
495 init_stairs(ModeInfo * mi)
496 {
497         int         screen = MI_SCREEN(mi);
498         stairsstruct *sp;
499
500         if (stairs == NULL) {
501                 if ((stairs = (stairsstruct *) calloc(MI_NUM_SCREENS(mi),
502                                              sizeof (stairsstruct))) == NULL)
503                         return;
504         }
505         sp = &stairs[screen];
506         sp->step = 0.0;
507         sp->direction = LRAND() & 1;
508         sp->sphere_position = NRAND(NPOSITIONS / 3) * 3;
509         sp->sphere_tick = 0;
510
511         if ((sp->glx_context = init_GL(mi)) != NULL) {
512
513                 reshape_stairs(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
514                 glDrawBuffer(GL_BACK);
515                 if (!glIsList(objects))
516                         objects = glGenLists(1);
517                 pinit();
518         } else {
519                 MI_CLEARWINDOW(mi);
520         }
521 }
522
523 void
524 draw_stairs(ModeInfo * mi)
525 {
526         stairsstruct *sp = &stairs[MI_SCREEN(mi)];
527
528         Display    *display = MI_DISPLAY(mi);
529         Window      window = MI_WINDOW(mi);
530
531         if (!sp->glx_context)
532                 return;
533
534         glXMakeCurrent(display, window, *(sp->glx_context));
535
536         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
537
538         glPushMatrix();
539
540         glTranslatef(0.0, 0.0, -10.0);
541
542         if (!MI_IS_ICONIC(mi)) {
543                 glScalef(Scale4Window * sp->WindH / sp->WindW, Scale4Window, Scale4Window);
544         } else {
545                 glScalef(Scale4Iconic * sp->WindH / sp->WindW, Scale4Iconic, Scale4Iconic);
546         }
547
548         glRotatef(44.5, 1, 0, 0);
549         glRotatef(50 + ((sp->direction) ? 1 : -1) *
550                ((sp->step * 100 > 120) ? sp->step * 100 - 120 : 0), 0, 1, 0);
551         if (sp->step * 100 >= 360 + 120) {      /* stop showing secrets */
552                 sp->step = 0;
553                 sp->direction = LRAND() & 1;
554         }
555         draw_stairs_internal(mi);
556
557         glPopMatrix();
558
559     if (mi->fps_p) do_fps (mi);
560         glFlush();
561
562         glXSwapBuffers(display, window);
563
564         sp->step += 0.025;
565 }
566
567 void
568 change_stairs(ModeInfo * mi)
569 {
570         stairsstruct *sp = &stairs[MI_SCREEN(mi)];
571
572         if (!sp->glx_context)
573                 return;
574
575         glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(sp->glx_context));
576         pinit();
577 }
578
579 void
580 release_stairs(ModeInfo * mi)
581 {
582         if (stairs != NULL) {
583                 (void) free((void *) stairs);
584                 stairs = NULL;
585         }
586         if (glIsList(objects)) {
587                 glDeleteLists(objects, 1);
588         }
589         FreeAllGL(mi);
590 }
591
592 #endif