ftp://ftp.krokus.ru/pub/OpenBSD/distfiles/xscreensaver-4.21.tar.gz
[xscreensaver] / hacks / glx / blocktube.c
1 /* blocktube, Copyright (c) 2003 Lars Damerow <lars@oddment.org>
2  *
3  * Based on Jamie Zawinski's original dangerball code.
4  *
5  * Permission to use, copy, modify, distribute, and sell this software and its
6  * documentation for any purpose is hereby granted without fee, provided that
7  * the above copyright notice appear in all copies and that both that
8  * copyright notice and this permission notice appear in supporting
9  * documentation.  No representations are made about the suitability of this
10  * software for any purpose.  It is provided "as is" without express or 
11  * implied warranty.
12  */
13
14 #include <X11/Intrinsic.h>
15
16 #define DEBUG 1
17
18 extern XtAppContext app;
19
20 #define PROGCLASS       "Blocktube"
21 #define HACK_INIT       init_blocktube
22 #define HACK_DRAW       draw_blocktube
23 #define HACK_RESHAPE    reshape_blocktube
24 #define EVENT_MASK      PointerMotionMask
25 #define blocktube_opts  xlockmore_opts
26
27 #define DEF_HOLDTIME    "1000"
28 #define DEF_CHANGETIME  "200"
29 #define MAX_ENTITIES     1000
30 #define DEF_TEXTURE     "True"
31 #define DEF_FOG         "True"
32
33 #define DEFAULTS        "*delay:        40000           \n" \
34                         "*wireframe:    False           \n" \
35                         "*showFPS:      False           \n" \
36
37 #undef countof
38 #define countof(x) (sizeof((x))/sizeof((*x)))
39
40 #include "xlockmore.h"
41 #include "colors.h"
42 #include <math.h>
43 #include <ctype.h>
44
45 #ifdef USE_GL /* whole file */
46
47 #include <GL/glu.h>
48
49 #if defined(USE_XPM) || defined(USE_XPMINC) || defined(HAVE_XPM) || defined (HAVE_GDK_PIXBUF)
50 /* USE_XPM & USE_XPMINC in xlock mode ; HAVE_XPM in xscreensaver mode */
51 #include "xpm-ximage.h"
52 #define I_HAVE_XPM
53
54 #include "../images/blocktube.xpm"
55 #endif /* HAVE_XPM */
56
57 typedef struct {
58     GLXContext *glx_context;
59     GLuint  block_dlist;
60 } blocktube_configuration;
61
62 static int nextID = 1;
63 static blocktube_configuration *lps = NULL;
64
65 typedef struct {
66     int id, r, g, b;
67     GLfloat tVal;
68     int age;
69     int lifetime;
70     GLfloat position[3];
71     GLfloat angle;
72     GLfloat angularVelocity;
73 } entity;
74
75 static entity entities[MAX_ENTITIES];
76 static float targetR, targetG, targetB,
77              currentR, currentG, currentB,
78              deltaR, deltaG, deltaB;
79 static GLint holdtime;
80 static GLint changetime;
81 static int counter = 0;
82 static int changing = 0;
83 static GLfloat zoom = 30.0f;
84 static GLfloat tilt = 4.5f;
85 static GLuint loop;
86 static GLuint envTexture;
87 static XImage *texti;
88 static int do_texture;
89 static int do_fog;
90
91 GLfloat tunnelLength=200;
92 GLfloat tunnelWidth=5;
93
94 static XrmOptionDescRec opts[] = {
95     { "-holdtime",  ".holdtime",  XrmoptionSepArg, 0 },
96     { "-changetime",  ".changetime",  XrmoptionSepArg, 0 },
97     {"-texture",     ".texture",   XrmoptionNoArg, "True" },
98     {"+texture",     ".texture",   XrmoptionNoArg, "False" },
99     {"-fog",         ".fog",       XrmoptionNoArg, "True" },
100     {"+fog",         ".fog",       XrmoptionNoArg, "False" },
101 };
102
103 static argtype vars[] = {
104     {&holdtime, "holdtime",  "Hold Time",  DEF_HOLDTIME,  t_Int},
105     {&changetime, "changetime",  "Change Time",  DEF_CHANGETIME, \
106      t_Int},
107     {&do_texture, "texture",    "Texture", DEF_TEXTURE,   t_Bool},
108     {&do_fog,     "fog",        "Fog",     DEF_FOG,       t_Bool},
109 };
110
111 static OptionStruct desc[] = {
112     {"-holdtime", "how long to stay on the same color"},
113     {"-changetime", "how long it takes to fade to a new color"},
114 };
115
116 ModeSpecOpt blocktube_opts = {countof(opts), opts, countof(vars), vars, desc};
117
118 #ifdef USE_MODULES
119 ModStruct blocktube_description =
120     {"blocktube", "init_blocktube", "draw_blocktube", "release_blocktube",
121      "draw_blocktube", "init_blocktube", (char *)NULL, &blocktube_opts,
122      40000, 30, 1, 1, 64, 1.0, "",
123      "A shifting tunnel of reflective blocks", 0, NULL};
124 #endif /* USE_MODULES */
125
126 #if defined( I_HAVE_XPM )
127 static Bool LoadGLTextures(ModeInfo *mi)
128 {
129     Bool status;
130
131     status = True;
132     glGenTextures(1, &envTexture);
133     glBindTexture(GL_TEXTURE_2D, envTexture);
134     texti = xpm_to_ximage(MI_DISPLAY(mi), MI_VISUAL(mi), MI_COLORMAP(mi),
135                           blocktube_xpm);
136     if (!texti) {
137         status = False;
138     } else {
139         glPixelStorei(GL_UNPACK_ALIGNMENT,1);
140         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texti->width, texti->height, 0,
141             GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, texti->data);
142         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
143         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
144         glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
145         glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
146     }
147     return status;
148 }
149 #endif
150
151 static void newTargetColor(void)
152 {
153     int luminance = 0;
154
155     while (luminance <= 150) {
156         targetR = random() % 256;
157         targetG = random() % 256;
158         targetB = random() % 256;
159         deltaR = (targetR - currentR) / changetime;
160         deltaG = (targetG - currentG) / changetime;
161         deltaB = (targetB - currentB) / changetime;
162         luminance = 0.3 * targetR + 0.59 * targetG + 0.11 * targetB;
163     }
164 }
165
166 static void randomize_entity (entity *ent)
167 {
168     ent->id = nextID++;
169     ent->tVal = 1 - ((float)random() / RAND_MAX / 1.5);
170     ent->age = 0;
171     ent->lifetime = 100;
172     ent->angle = random() % 360;
173     ent->angularVelocity = 0.5-((float)(random()) / RAND_MAX);
174     ent->position[0] = (float)(random()) / RAND_MAX + tunnelWidth;
175     ent->position[1] = (float)(random()) / RAND_MAX * 2;
176     ent->position[2] = -(float)(random()) / RAND_MAX * tunnelLength;
177 }
178
179 static void entityTick(entity *ent)
180 {
181     ent->angle += ent->angularVelocity;
182     ent->position[2] += 0.1;
183     if (ent->position[2] > zoom) {
184         ent->position[2] = -tunnelLength + ((float)(random()) / RAND_MAX) * 20;
185     }
186     ent->age += 0.1;
187 }
188
189 static void tick(void)
190 {
191     counter--;
192     if (!counter) {
193         if (!changing) {
194             newTargetColor();
195             counter = changetime;
196         } else {
197             counter = holdtime;
198         }
199         changing = (!changing);
200     } else {
201         if (changing) {
202             currentR += deltaR;
203             currentG += deltaG;
204             currentB += deltaB;
205         }
206     }
207 }
208
209 static void cube_vertices(float x, float y, float z, int wire);
210
211 void init_blocktube (ModeInfo *mi)
212 {
213     GLfloat fogColor[4] = {0,0,0,1};
214     blocktube_configuration *lp;
215     int wire = MI_IS_WIREFRAME(mi);
216
217     if (!lps) {
218       lps = (blocktube_configuration *)
219         calloc (MI_NUM_SCREENS(mi), sizeof (blocktube_configuration));
220       if (!lps) {
221         fprintf(stderr, "%s: out of memory\n", progname);
222         exit(1);
223       }
224       lp = &lps[MI_SCREEN(mi)];
225     }
226
227     lp = &lps[MI_SCREEN(mi)];
228     lp->glx_context = init_GL(mi);
229
230     if (wire) {
231       do_fog = False;
232       do_texture = False;
233       glLineWidth(2);
234     }
235
236     lp->block_dlist = glGenLists (1);
237     glNewList (lp->block_dlist, GL_COMPILE);
238     cube_vertices(0.15, 1.2, 5.25, wire);
239     glEndList ();
240
241 #if defined( I_HAVE_XPM )
242     if (do_texture) {
243       if (!LoadGLTextures(mi)) {
244         fprintf(stderr, "%s: can't load textures!\n", progname);
245         exit(1);
246       }
247       glEnable(GL_TEXTURE_2D);
248     }
249 #endif
250
251     /* kick on the fog machine */
252     if (do_fog) {
253       glEnable(GL_FOG);
254       glFogi(GL_FOG_MODE, GL_LINEAR);
255       glHint(GL_FOG_HINT, GL_NICEST);
256       glFogf(GL_FOG_START, 0);
257       glFogf(GL_FOG_END, tunnelLength/1.8);
258       glFogfv(GL_FOG_COLOR, fogColor);
259       glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
260     }
261     glShadeModel(GL_SMOOTH);
262     glEnable(GL_DEPTH_TEST);
263     glEnable(GL_CULL_FACE);
264     glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
265     glClearDepth(1.0f);
266
267     if (!do_texture && !wire) {
268       /* If there is no texture, the boxes don't show up without a light.
269          Though I don't understand why all the blocks come out gray.
270        */
271       GLfloat pos[4] = {0.0, 1.0, 1.0, 0.0};
272       GLfloat amb[4] = {0.2, 0.2, 0.2, 1.0};
273       GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
274       GLfloat spc[4] = {1.0, 1.0, 1.0, 1.0};
275       glLightfv(GL_LIGHT0, GL_POSITION, pos);
276       glLightfv(GL_LIGHT0, GL_AMBIENT,  amb);
277       glLightfv(GL_LIGHT0, GL_DIFFUSE,  dif);
278       glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
279       glEnable(GL_LIGHTING);
280       glEnable(GL_LIGHT0);
281     }
282
283     counter = holdtime;
284     currentR = random() % 256;
285     currentG = random() % 256;
286     currentB = random() % 256;
287     newTargetColor();
288     for (loop = 0; loop < MAX_ENTITIES; loop++)
289     {
290         randomize_entity(&entities[loop]);
291     }
292     reshape_blocktube(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
293     glFlush();
294 }
295
296 void release_blocktube (ModeInfo *mi)
297 {
298 #if defined ( I_HAVE_XPM )
299     glDeleteTextures(1, &envTexture);
300     XDestroyImage(texti);
301 #endif
302 }
303
304 void reshape_blocktube (ModeInfo *mi, int width, int height)
305 {
306     GLfloat h = (GLfloat) height / (GLfloat) width;
307
308     glViewport(0, 0, (GLint) width, (GLint) height);
309     glMatrixMode(GL_PROJECTION);
310     glLoadIdentity();
311     gluPerspective(45.0, 1/h, 1.0, 100.0);
312     glMatrixMode(GL_MODELVIEW);
313 }
314
315 static void cube_vertices(float x, float y, float z, int wire)
316 {
317     float x2, y2, z2, nv = 0.7;
318     x2 = x/2;
319     y2 = y/2;
320     z2 = z/2;
321
322     glFrontFace(GL_CW);
323
324     glNormal3f(0, 0, nv);
325     glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
326     glTexCoord2f(0.0, 0.0); glVertex3f(-x2,  y2,  z2);
327     glTexCoord2f(1.0, 0.0); glVertex3f( x2,  y2,  z2);
328     glTexCoord2f(1.0, 1.0); glVertex3f( x2, -y2,  z2);
329     glTexCoord2f(0.0, 1.0); glVertex3f(-x2, -y2,  z2);
330     glEnd();
331
332     glNormal3f(0, 0, -nv);
333     glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
334     glTexCoord2f(1.0, 0.0); glVertex3f(-x2, -y2, -z2);
335     glTexCoord2f(1.0, 1.0); glVertex3f( x2, -y2, -z2);
336     glTexCoord2f(0.0, 1.0); glVertex3f( x2,  y2, -z2);
337     glTexCoord2f(0.0, 0.0); glVertex3f(-x2,  y2, -z2);
338     glEnd();
339
340     glNormal3f(0, nv, 0);
341     glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
342     glTexCoord2f(0.0, 1.0); glVertex3f(-x2,  y2, -z2);
343     glTexCoord2f(0.0, 0.0); glVertex3f( x2,  y2, -z2);
344     glTexCoord2f(1.0, 0.0); glVertex3f( x2,  y2,  z2);
345     glTexCoord2f(1.0, 1.0); glVertex3f(-x2,  y2,  z2);
346     glEnd();
347
348     glNormal3f(0, -nv, 0);
349     glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
350     glTexCoord2f(1.0, 1.0); glVertex3f(-x2, -y2, -z2);
351     glTexCoord2f(0.0, 1.0); glVertex3f(-x2, -y2,  z2);
352     glTexCoord2f(0.0, 0.0); glVertex3f( x2, -y2,  z2);
353     glTexCoord2f(1.0, 0.0); glVertex3f( x2, -y2, -z2);
354     glEnd();
355
356     if (wire) return;
357
358     glNormal3f(nv, 0, 0);
359     glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
360     glTexCoord2f(1.0, 0.0); glVertex3f( x2, -y2, -z2);
361     glTexCoord2f(1.0, 1.0); glVertex3f( x2, -y2,  z2);
362     glTexCoord2f(0.0, 1.0); glVertex3f( x2,  y2,  z2);
363     glTexCoord2f(0.0, 0.0); glVertex3f( x2,  y2, -z2);
364     glEnd();
365
366     glNormal3f(-nv, 0, 0);
367     glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
368     glTexCoord2f(0.0, 0.0); glVertex3f(-x2, -y2, -z2);
369     glTexCoord2f(1.0, 0.0); glVertex3f(-x2,  y2, -z2);
370     glTexCoord2f(1.0, 1.0); glVertex3f(-x2,  y2,  z2);
371     glTexCoord2f(0.0, 1.0); glVertex3f(-x2, -y2,  z2);
372     glEnd();
373 }
374
375 static void draw_block(ModeInfo *mi, entity *ent)
376 {
377     blocktube_configuration *lp = &lps[MI_SCREEN(mi)];
378     glCallList (lp->block_dlist);
379 }
380
381 void
382 draw_blocktube (ModeInfo *mi)
383 {
384     blocktube_configuration *lp = &lps[MI_SCREEN(mi)];
385     Display *dpy = MI_DISPLAY(mi);
386     Window window = MI_WINDOW(mi);
387     entity *cEnt = NULL;
388     int loop = 0;
389
390     if (!lp->glx_context)
391       return;
392
393     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
394
395     if (do_texture) {
396       glEnable(GL_TEXTURE_GEN_S);
397       glEnable(GL_TEXTURE_GEN_T);
398       glBindTexture(GL_TEXTURE_2D, envTexture);
399     }
400
401     for (loop = 0; loop < MAX_ENTITIES; loop++) {
402         cEnt = &entities[loop];
403
404         glLoadIdentity();
405         glTranslatef(0.0f, 0.0f, zoom);
406         glRotatef(tilt, 1.0f, 0.0f, 0.0f);
407         glRotatef(cEnt->angle, 0.0f, 0.0f, 1.0f);
408         glTranslatef(cEnt->position[0], cEnt->position[1], cEnt->position[2]);
409         glColor4ub((int)(currentR * cEnt->tVal),
410                    (int)(currentG * cEnt->tVal),
411                    (int)(currentB * cEnt->tVal), 255);
412         draw_block(mi, cEnt);
413         entityTick(cEnt);
414     }
415     tick();
416
417     if (mi->fps_p) do_fps (mi);
418     glFinish();
419     glXSwapBuffers(dpy, window);
420 }
421
422 #endif /* USE_GL */