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