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