1 /* glblur --- radial blur using GL textures
2 * Copyright (c) 2002-2014 Jamie Zawinski <jwz@jwz.org>
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation. No representations are made about the suitability of this
9 * software for any purpose. It is provided "as is" without express or
12 * This program draws a box and a few line segments, and generates a flowing
13 * radial blur outward from it -- this causes flowing field effects.
14 * It does this by rendering the scene into a small texture, then repeatedly
15 * rendering increasingly-enlarged and increasingly-transparent versions of
16 * that texture onto the frame buffer.
18 * As such, it's quite graphics intensive -- don't bother trying to run this
19 * if you don't have hardware-accelerated texture support.
21 * Inspired by Dario Corno's Radial Blur tutorial:
22 * http://nehe.gamedev.net/tutorials/lesson.asp?l=36
25 #define DEFAULTS "*delay: 10000 \n" \
26 "*showFPS: False \n" \
27 "*fpsSolid: True \n" \
28 "*suppressRotationAnimation: True\n" \
30 # define free_glblur 0
31 # define release_glblur 0
33 #define countof(x) (sizeof((x))/sizeof((*x)))
36 #define ABS(n) ((n)<0?-(n):(n))
38 #define SIGNOF(n) ((n)<0?-1:1)
40 #include "xlockmore.h"
43 #include "gltrackball.h"
46 #ifdef USE_GL /* whole file */
48 #define DEF_SPIN "XYZ"
49 #define DEF_WANDER "True"
50 #define DEF_BLUR_SIZE "15"
52 typedef struct metaball metaball;
56 GLXContext *glx_context;
58 trackball_state *trackball;
61 GLuint obj_dlist0; /* east-west cube faces */
62 GLuint obj_dlist1; /* north-south cube faces */
63 GLuint obj_dlist2; /* up-down cube faces */
64 GLuint obj_dlist3; /* spikes coming out of the cube's corners */
65 GLuint scene_dlist1; /* the cube, rotated and translated */
66 GLuint scene_dlist2; /* the spikes, rotated and translated */
67 int scene_polys1; /* polygons in scene, not counting texture overlay */
68 int scene_polys2; /* polygons in scene, not counting texture overlay */
71 unsigned int *tex_data;
84 } glblur_configuration;
86 static glblur_configuration *bps = NULL;
89 static Bool do_wander;
92 static XrmOptionDescRec opts[] = {
93 { "-spin", ".spin", XrmoptionSepArg, 0 },
94 { "+spin", ".spin", XrmoptionNoArg, "" },
95 { "-blursize", ".blurSize", XrmoptionSepArg, 0 },
96 { "-wander", ".wander", XrmoptionNoArg, "True" },
97 { "+wander", ".wander", XrmoptionNoArg, "False" },
100 static argtype vars[] = {
101 {&do_spin, "spin", "Spin", DEF_SPIN, t_String},
102 {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
103 {&blursize, "blurSize","BlurSize", DEF_BLUR_SIZE, t_Int},
106 ENTRYPOINT ModeSpecOpt glblur_opts = {countof(opts), opts, countof(vars), vars, NULL};
109 /* Window management, etc
112 reshape_glblur (ModeInfo *mi, int width, int height)
114 GLfloat h = (GLfloat) height / (GLfloat) width;
116 glViewport (0, 0, (GLint) width, (GLint) height);
118 glMatrixMode(GL_PROJECTION);
120 gluPerspective (30.0, 1/h, 1.0, 100.0);
122 glMatrixMode(GL_MODELVIEW);
124 gluLookAt( 0.0, 0.0, 8.0,
128 # ifdef HAVE_MOBILE /* Keep it the same relative size when rotated. */
130 int o = (int) current_device_rotation();
131 if (o != 0 && o != 180 && o != -180)
132 glScalef (1/h, 1/h, 1/h);
136 glClear(GL_COLOR_BUFFER_BIT);
141 /* Objects in the scene
145 generate_object (ModeInfo *mi)
147 glblur_configuration *bp = &bps[MI_SCREEN(mi)];
148 Bool wire = MI_IS_WIREFRAME (mi);
151 bp->scene_polys1 = 0;
152 bp->scene_polys2 = 0;
154 glNewList (bp->obj_dlist0, GL_COMPILE);
155 glBegin (wire ? GL_LINE_LOOP : GL_QUADS); /* front */
156 glNormal3f (0, 0, 1);
157 glTexCoord2f(1, 0); glVertex3f ( 0.5, -0.5, 0.5);
158 glTexCoord2f(0, 0); glVertex3f ( 0.5, 0.5, 0.5);
159 glTexCoord2f(0, 1); glVertex3f (-0.5, 0.5, 0.5);
160 glTexCoord2f(1, 1); glVertex3f (-0.5, -0.5, 0.5);
164 glBegin (wire ? GL_LINE_LOOP : GL_QUADS); /* back */
165 glNormal3f (0, 0, -1);
166 glTexCoord2f(0, 0); glVertex3f (-0.5, -0.5, -0.5);
167 glTexCoord2f(0, 1); glVertex3f (-0.5, 0.5, -0.5);
168 glTexCoord2f(1, 1); glVertex3f ( 0.5, 0.5, -0.5);
169 glTexCoord2f(1, 0); glVertex3f ( 0.5, -0.5, -0.5);
174 glNewList (bp->obj_dlist1, GL_COMPILE);
175 glBegin (wire ? GL_LINE_LOOP : GL_QUADS); /* left */
176 glNormal3f (-1, 0, 0);
177 glTexCoord2f(1, 1); glVertex3f (-0.5, 0.5, 0.5);
178 glTexCoord2f(1, 0); glVertex3f (-0.5, 0.5, -0.5);
179 glTexCoord2f(0, 0); glVertex3f (-0.5, -0.5, -0.5);
180 glTexCoord2f(0, 1); glVertex3f (-0.5, -0.5, 0.5);
184 glBegin (wire ? GL_LINE_LOOP : GL_QUADS); /* right */
185 glNormal3f (1, 0, 0);
186 glTexCoord2f(1, 1); glVertex3f ( 0.5, -0.5, -0.5);
187 glTexCoord2f(1, 0); glVertex3f ( 0.5, 0.5, -0.5);
188 glTexCoord2f(0, 0); glVertex3f ( 0.5, 0.5, 0.5);
189 glTexCoord2f(0, 1); glVertex3f ( 0.5, -0.5, 0.5);
194 glNewList (bp->obj_dlist2, GL_COMPILE);
195 glBegin (wire ? GL_LINE_LOOP : GL_QUADS); /* top */
196 glNormal3f (0, 1, 0);
197 glTexCoord2f(0, 0); glVertex3f ( 0.5, 0.5, 0.5);
198 glTexCoord2f(0, 1); glVertex3f ( 0.5, 0.5, -0.5);
199 glTexCoord2f(1, 1); glVertex3f (-0.5, 0.5, -0.5);
200 glTexCoord2f(1, 0); glVertex3f (-0.5, 0.5, 0.5);
204 glBegin (wire ? GL_LINE_LOOP : GL_QUADS); /* bottom */
205 glNormal3f (0, -1, 0);
206 glTexCoord2f(1, 0); glVertex3f (-0.5, -0.5, 0.5);
207 glTexCoord2f(0, 0); glVertex3f (-0.5, -0.5, -0.5);
208 glTexCoord2f(0, 1); glVertex3f ( 0.5, -0.5, -0.5);
209 glTexCoord2f(1, 1); glVertex3f ( 0.5, -0.5, 0.5);
214 glNewList (bp->obj_dlist3, GL_COMPILE);
217 glVertex3f(-s, 0, 0); glVertex3f(s, 0, 0); /* face spikes */
218 glVertex3f(0, -s, 0); glVertex3f(0, s, 0);
219 glVertex3f(0, 0, -s); glVertex3f(0, 0, s);
220 bp->scene_polys2 += 3;
225 glVertex3f(-s, -s, -s); glVertex3f( s, s, s); /* corner spikes */
226 glVertex3f(-s, -s, s); glVertex3f( s, s, -s);
227 glVertex3f(-s, s, -s); glVertex3f( s, -s, s);
228 glVertex3f( s, -s, -s); glVertex3f(-s, s, s);
229 bp->scene_polys2 += 4;
233 check_gl_error ("object generation");
238 init_texture (ModeInfo *mi)
240 glblur_configuration *bp = &bps[MI_SCREEN(mi)];
242 if (bp->tex_data) free (bp->tex_data);
246 bp->tex_data = (unsigned int *)
247 malloc (bp->tex_w * bp->tex_h * 4 * sizeof (unsigned int));
249 glGenTextures (1, &bp->texture);
250 glBindTexture (GL_TEXTURE_2D, bp->texture);
251 glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA,
252 bp->tex_w, bp->tex_h, 0,
253 GL_RGBA, GL_UNSIGNED_BYTE, bp->tex_data);
254 check_gl_error ("texture generation");
255 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
256 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
261 render_scene_to_texture (ModeInfo *mi)
263 glblur_configuration *bp = &bps[MI_SCREEN(mi)];
265 glViewport (0, 0, bp->tex_w, bp->tex_h);
267 glCallList (bp->scene_dlist1);
268 glCallList (bp->scene_dlist2);
270 glBindTexture (GL_TEXTURE_2D, bp->texture);
271 glCopyTexImage2D (GL_TEXTURE_2D, 0, GL_LUMINANCE, 0, 0,
272 bp->tex_w, bp->tex_h, 0);
273 check_gl_error ("texture2D");
275 glViewport (0, 0, MI_WIDTH(mi), MI_HEIGHT(mi));
279 overlay_blur_texture (ModeInfo *mi)
281 glblur_configuration *bp = &bps[MI_SCREEN(mi)];
282 int w = MI_WIDTH (mi);
283 int h = MI_HEIGHT (mi);
284 int times = blursize;
286 GLfloat inc = 0.02 * (25.0 / times);
288 GLfloat spost = 0; /* starting texture coordinate offset */
289 GLfloat alpha_inc; /* transparency fade factor */
290 GLfloat alpha = 0.2; /* initial transparency */
292 glEnable (GL_TEXTURE_2D);
293 glDisable (GL_DEPTH_TEST);
294 glBlendFunc (GL_SRC_ALPHA,GL_ONE);
296 glBindTexture (GL_TEXTURE_2D, bp->texture);
299 /* switch to orthographic projection, saving both previous matrixes
300 on their respective stacks.
302 glMatrixMode (GL_PROJECTION);
305 glOrtho (0, w, h, 0, -1, 1);
306 glMatrixMode (GL_MODELVIEW);
311 alpha_inc = alpha / times;
313 mi->polygon_count = bp->scene_polys1 + bp->scene_polys2;
316 for (i = 0; i < times; i++)
318 glColor4f (1, 1, 1, alpha);
319 glTexCoord2f (0+spost, 1-spost); glVertex2f (0, 0);
320 glTexCoord2f (0+spost, 0+spost); glVertex2f (0, h);
321 glTexCoord2f (1-spost, 0+spost); glVertex2f (w, h);
322 glTexCoord2f (1-spost, 1-spost); glVertex2f (w, 0);
329 /* Switch back to perspective projection, restoring the saved matrixes
331 glMatrixMode (GL_PROJECTION);
333 glMatrixMode (GL_MODELVIEW);
336 glEnable (GL_DEPTH_TEST);
337 glDisable (GL_BLEND);
338 glDisable (GL_TEXTURE_2D);
339 glBindTexture (GL_TEXTURE_2D, 0);
344 /* Startup initialization
348 glblur_handle_event (ModeInfo *mi, XEvent *event)
350 glblur_configuration *bp = &bps[MI_SCREEN(mi)];
352 if (gltrackball_event_handler (event, bp->trackball,
353 MI_WIDTH (mi), MI_HEIGHT (mi),
362 init_glblur (ModeInfo *mi)
364 glblur_configuration *bp;
365 int wire = MI_IS_WIREFRAME(mi);
369 bp = &bps[MI_SCREEN(mi)];
371 bp->glx_context = init_GL(mi);
373 reshape_glblur (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
374 clear_gl_error(); /* WTF? sometimes "invalid op" from glViewport! */
378 GLfloat gamb[4]= {0.2, 0.2, 0.2, 1.0};
379 GLfloat pos[4] = {0.0, 5.0, 10.0, 1.0};
380 GLfloat amb[4] = {0.2, 0.2, 0.2, 1.0};
381 GLfloat dif[4] = {0.3, 0.3, 0.3, 1.0};
382 GLfloat spc[4] = {0.8, 0.8, 0.8, 1.0};
385 glEnable(GL_LIGHTING);
388 glEnable(GL_DEPTH_TEST);
389 glEnable(GL_CULL_FACE);
390 glEnable(GL_NORMALIZE);
391 glShadeModel(GL_SMOOTH);
393 glLightModelfv (GL_LIGHT_MODEL_AMBIENT, gamb);
395 glLightfv(GL_LIGHT0, GL_POSITION, pos);
396 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
397 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
398 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
400 glEnable(GL_LIGHTING);
403 glMaterialf(GL_FRONT, GL_SHININESS, shiny);
407 Bool spinx=False, spiny=False, spinz=False;
408 double spin_speed = 0.9;
409 double wander_speed = 0.06;
414 if (*s == 'x' || *s == 'X') spinx = True;
415 else if (*s == 'y' || *s == 'Y') spiny = True;
416 else if (*s == 'z' || *s == 'Z') spinz = True;
417 else if (*s == '0') ;
421 "%s: spin must contain only the characters X, Y, or Z (not \"%s\")\n",
428 bp->rot = make_rotator (spinx ? spin_speed : 0,
429 spiny ? spin_speed : 0,
430 spinz ? spin_speed : 0,
432 do_wander ? wander_speed : 0,
434 bp->trackball = gltrackball_init (True);
437 if (blursize < 0) blursize = 0;
438 if (blursize > 200) blursize = 200;
441 bp->colors0 = (XColor *) calloc(bp->ncolors, sizeof(XColor));
442 bp->colors1 = (XColor *) calloc(bp->ncolors, sizeof(XColor));
443 bp->colors2 = (XColor *) calloc(bp->ncolors, sizeof(XColor));
444 bp->colors3 = (XColor *) calloc(bp->ncolors, sizeof(XColor));
445 make_smooth_colormap (0, 0, 0, bp->colors0, &bp->ncolors, False, 0, False);
446 make_smooth_colormap (0, 0, 0, bp->colors1, &bp->ncolors, False, 0, False);
447 make_smooth_colormap (0, 0, 0, bp->colors2, &bp->ncolors, False, 0, False);
448 make_smooth_colormap (0, 0, 0, bp->colors3, &bp->ncolors, False, 0, False);
451 bp->obj_dlist0 = glGenLists (1);
452 bp->obj_dlist1 = glGenLists (1);
453 bp->obj_dlist2 = glGenLists (1);
454 bp->obj_dlist3 = glGenLists (1);
455 bp->scene_dlist1 = glGenLists (1);
456 bp->scene_dlist2 = glGenLists (1);
460 generate_object (mi);
467 draw_glblur (ModeInfo *mi)
469 glblur_configuration *bp = &bps[MI_SCREEN(mi)];
470 Display *dpy = MI_DISPLAY(mi);
471 Window window = MI_WINDOW(mi);
473 GLfloat color0[4] = {0.0, 0.0, 0.0, 1.0};
474 GLfloat color1[4] = {0.0, 0.0, 0.0, 1.0};
475 GLfloat color2[4] = {0.0, 0.0, 0.0, 1.0};
476 GLfloat color3[4] = {0.0, 0.0, 0.0, 1.0};
477 GLfloat spec[4] = {1.0, 1.0, 1.0, 1.0};
483 if (!bp->glx_context)
486 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
488 /* Decide what we're drawing
490 if (0 == (random() % 30))
492 bp->show_cube_p = (0 == (random() % 10));
493 bp->show_spikes_p = (0 == (random() % 20));
496 /* Select new colors for the various objects
498 color0[0] = bp->colors0[bp->ccolor].red / 65536.0;
499 color0[1] = bp->colors0[bp->ccolor].green / 65536.0;
500 color0[2] = bp->colors0[bp->ccolor].blue / 65536.0;
502 color1[0] = bp->colors1[bp->ccolor].red / 65536.0;
503 color1[1] = bp->colors1[bp->ccolor].green / 65536.0;
504 color1[2] = bp->colors1[bp->ccolor].blue / 65536.0;
506 color2[0] = bp->colors2[bp->ccolor].red / 65536.0;
507 color2[1] = bp->colors2[bp->ccolor].green / 65536.0;
508 color2[2] = bp->colors2[bp->ccolor].blue / 65536.0;
510 color3[0] = bp->colors3[bp->ccolor].red / 65536.0;
511 color3[1] = bp->colors3[bp->ccolor].green / 65536.0;
512 color3[2] = bp->colors3[bp->ccolor].blue / 65536.0;
515 if (bp->ccolor >= bp->ncolors) bp->ccolor = 0;
518 get_position (bp->rot, &px, &py, &pz, !bp->button_down_p);
519 get_rotation (bp->rot, &rx, &ry, &rz, !bp->button_down_p);
528 /* Generate scene_dlist1, which contains the box (not spikes),
529 rotated into position.
531 glNewList (bp->scene_dlist1, GL_COMPILE);
533 glMatrixMode (GL_MODELVIEW);
535 glTranslatef (px, py, pz);
536 gltrackball_rotate (bp->trackball);
537 glRotatef (rx, 1.0, 0.0, 0.0);
538 glRotatef (ry, 0.0, 1.0, 0.0);
539 glRotatef (rz, 0.0, 0.0, 1.0);
541 glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, spec);
543 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color0);
544 glCallList (bp->obj_dlist0);
546 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color1);
547 glCallList (bp->obj_dlist1);
549 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color2);
550 glCallList (bp->obj_dlist2);
552 glMatrixMode (GL_MODELVIEW);
558 /* Generate scene_dlist2, which contains the spikes (not box),
559 rotated into position.
561 glNewList (bp->scene_dlist2, GL_COMPILE);
563 glMatrixMode (GL_MODELVIEW);
565 glTranslatef (px, py, pz);
566 gltrackball_rotate (bp->trackball);
567 glRotatef (rx, 1.0, 0.0, 0.0);
568 glRotatef (ry, 0.0, 1.0, 0.0);
569 glRotatef (rz, 0.0, 0.0, 1.0);
571 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color3);
572 glCallList (bp->obj_dlist3);
574 glMatrixMode (GL_MODELVIEW);
580 glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
582 render_scene_to_texture (mi);
584 glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
586 if (bp->show_cube_p || bp->button_down_p)
588 glCallList (bp->scene_dlist1);
589 extra_polys += bp->scene_polys1;
591 if (bp->show_spikes_p || bp->button_down_p)
593 glCallList (bp->scene_dlist2);
594 extra_polys += bp->scene_polys2;
597 overlay_blur_texture (mi);
598 mi->polygon_count += extra_polys;
602 if (mi->fps_p) do_fps (mi);
605 glXSwapBuffers(dpy, window);
608 XSCREENSAVER_MODULE ("GLBlur", glblur)