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