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