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