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