1 /* -*- Mode: C; tab-width: 4 -*- emacs friendly */
2 /* gflux - creates a fluctuating 3D grid
3 * requires OpenGL or MesaGL
5 * Copyright (c) Josiah Pease, 2000, 2003
6 * Permission to use, copy, modify, distribute, and sell this software and its
7 * documentation for any purpose is hereby granted without fee, provided that
8 * the above copyright notice appear in all copies and that both that
9 * copyright notice and this permission notice appear in supporting
10 * documentation. No representations are made about the suitability of this
11 * software for any purpose. It is provided "as is" without express or
14 * Thanks go to all those who worked on...
15 * MesaGL, OpenGL, UtahGLX, XFree86, gcc, vim, rxvt, the PNM (anymap) format
16 * xscreensaver and the thousands of other tools, apps and daemons that make
18 * Personal thanks to Kevin Moss, Paul Sheahee and Jamie Zawinski
20 * some xscreensaver code lifted from superquadrics. Most other glx hacks
21 * used as reference at some time.
23 * This hack and others can cause UtahGLX to crash my X server
24 * wireframe looks good with software only rendering anyway
25 * If anyone can work out why and supply a fix I'd love to hear from them
27 * Josiah Pease <gfluxcode@jpease.force9.co.uk> 21 July 2000
30 * 10 June 2000 : wireframe rippling grid standalone written
31 * 18 June 2000 : xscreensaver code added
32 * 25 June 2000 : solid and light added
33 * 04 July 2000 : majour bug hunt, xscreensaver code rewritten
34 * 08 July 2000 : texture mapping, rotation and zoom added
35 * 21 July 2000 : cleaned up code from bug hunts, manpage written
36 * 24 November 2000 : fixed x co-ord calculation in solid - textured
37 * 05 March 2001 : put back non pnmlib code with #ifdefs
38 * 11 May 2002 : fixed image problems with large images
43 # define PROGCLASS "gflux"
44 # define HACK_INIT init_gflux
45 # define HACK_DRAW draw_gflux
46 # define HACK_RESHAPE reshape_gflux
47 # define HACK_HANDLE_EVENT gflux_handle_event
48 # define EVENT_MASK PointerMotionMask
49 # define gflux_opts xlockmore_opts
50 #define DEFAULTS "*delay: 20000 \n" \
51 "*showFPS: False \n" \
57 "*rotationx: 0.01 \n" \
58 "*rotationy: 0.0 \n" \
59 "*rotationz: 0.1 \n" \
61 "*waveChange: 50 \n" \
62 "*waveHeight: 0.8 \n" \
68 # include "xlockmore.h" /* from the xscreensaver distribution */
69 #else /* !STANDALONE */
70 # include "xlock.h" /* from the xlockmore distribution */
71 #endif /* !STANDALONE */
73 #ifdef USE_GL /* whole file */
77 # include <X11/Xmu/Drawing.h>
79 # include <Xmu/Drawing.h>
84 #define countof(x) (sizeof((x))/sizeof((*x)))
95 #include "grab-ximage.h"
96 #include "gltrackball.h"
99 static enum {wire=0,solid,light,checker,grab} _draw; /* draw style */
100 static int _squares = 19; /* grid size */
101 static int _resolution = 4; /* wireframe resolution */
102 static int _flat = 0;
104 static float _speed = 0.05;
105 static float _rotationx = 0.01;
106 static float _rotationy = 0.0;
107 static float _rotationz = 0.1;
108 static float _zoom = 1.0;
110 static int _waves = 3;
111 static int _waveChange = 50;
112 static float _waveHeight = 1.0;
113 static float _waveFreq = 3.0;
115 static trackball_state *trackball;
116 static Bool button_down_p = False;
121 static XrmOptionDescRec opts[] = {
122 {"-squares", ".gflux.squares", XrmoptionSepArg, 0},
123 {"-resolution", ".gflux.resolution", XrmoptionSepArg, 0},
124 /* {"-draw", ".gflux.draw", XrmoptionSepArg, 0},*/
125 {"-mode", ".gflux.mode", XrmoptionSepArg, 0},
126 {"-flat", ".gflux.flat", XrmoptionSepArg, 0},
127 {"-speed", ".gflux.speed", XrmoptionSepArg, 0},
128 {"-rotationx", ".gflux.rotationx", XrmoptionSepArg, 0},
129 {"-rotationy", ".gflux.rotationy", XrmoptionSepArg, 0},
130 {"-rotationz", ".gflux.rotationz", XrmoptionSepArg, 0},
131 {"-waves", ".gflux.waves", XrmoptionSepArg, 0},
132 {"-waveChange", ".gflux.waveChange", XrmoptionSepArg, 0},
133 {"-waveHeight", ".gflux.waveHeight", XrmoptionSepArg, 0},
134 {"-waveFreq", ".gflux.waveFreq", XrmoptionSepArg, 0},
135 {"-zoom", ".gflux.zoom", XrmoptionSepArg, 0},
139 static argtype vars[] = {
140 {&_squares, "squares", "Squares", "19", t_Int},
141 {&_resolution, "resolution", "Resolution", "4", t_Int},
142 /* {&_draw, "draw", "Draw", "2", t_Int},*/
143 {&_flat, "flat", "Flat", "0", t_Int},
144 {&_speed, "speed", "Speed", "0.05", t_Float},
145 {&_rotationx, "rotationx", "Rotationx", "0.01", t_Float},
146 {&_rotationy, "rotationy", "Rotationy", "0.0", t_Float},
147 {&_rotationz, "rotationz", "Rotationz", "0.1", t_Float},
148 {&_waves, "waves", "Waves", "3", t_Int},
149 {&_waveChange, "waveChange", "WaveChange", "50", t_Int},
150 {&_waveHeight, "waveHeight", "WaveHeight", "1.0", t_Float},
151 {&_waveFreq, "waveFreq", "WaveFreq", "3.0", t_Float},
152 {&_zoom, "zoom", "Zoom", "1.0", t_Float},
156 static OptionStruct desc[] =
158 {"-squares num", "size of grid in squares (19)"},
159 {"-resolution num", "detail of lines making grid, wireframe only (4)"},
160 /* {"-draw num", "draw method to use: 0=wireframe 1=solid 2=lit (0)"},*/
161 {"-flat num", "shading method, not wireframe: 0=smooth 1=flat (0)"},
162 {"-speed num", "speed of waves (0.05)"},
163 {"-rotationx num", "speed of xrotation (0.01)"},
164 {"-rotationy num", "speed of yrotation (0.00)"},
165 {"-rotationz num", "speed of zrotation (0.10)"},
166 {"-waves num", "number of simultanious waves (3)"},
167 {"-waveChange num", "number of cyles for a wave to change (50)"},
168 {"-waveHeight num", "height of waves (1.0)"},
169 {"-waveFreq num", "max frequency of a wave (3.0)"},
170 {"-zoom num", "camera control (1.0)"},
173 ModeSpecOpt gflux_opts = {countof(opts), opts, countof(vars), vars, desc};
176 ModStruct gflux_description =
177 {"gflux", "init_gflux", "draw_gflux", "release_gflux",
178 "draw_gflux", "init_gflux", NULL, &gflux_opts,
179 1000, 1, 2, 1, 4, 1.0, "",
180 "Gflux: an OpenGL gflux", 0, NULL};
183 /* structure for holding the gflux data */
186 int screen_width, screen_height;
187 GLXContext *glx_context;
190 #define MAXWAVES 10 /* should be dynamic */
192 double freq[MAXWAVES];
193 double dispy[MAXWAVES];
194 double dispx[MAXWAVES];
200 int img_width, img_height;
201 void (*drawFunc)(void);
203 static gfluxstruct *gflux = NULL;
206 void initLighting(void);
207 void grabTexture(void);
208 void createTexture(void);
209 void displaySolid(void); /* drawFunc implementations */
210 void displayLight(void);
211 void displayTexture(void);
212 void displayWire(void);
214 double getGrid(double,double,double);
216 /* as macro for speed */
217 /* could do with colour cycling here */
218 /* void genColour(double);*/
219 #define genColour(X) \
221 gflux->colour[0] = 0.0;\
222 gflux->colour[1] = 0.5+0.5*(X);\
223 gflux->colour[2] = 0.5-0.5*(X);\
226 /* BEGINNING OF FUNCTIONS */
230 gflux_handle_event (ModeInfo *mi, XEvent *event)
232 if (event->xany.type == ButtonPress &&
233 event->xbutton.button == Button1)
235 button_down_p = True;
236 gltrackball_start (trackball,
237 event->xbutton.x, event->xbutton.y,
238 MI_WIDTH (mi), MI_HEIGHT (mi));
241 else if (event->xany.type == ButtonRelease &&
242 event->xbutton.button == Button1)
244 button_down_p = False;
247 else if (event->xany.type == ButtonPress &&
248 (event->xbutton.button == Button4 ||
249 event->xbutton.button == Button5))
251 gltrackball_mousewheel (trackball, event->xbutton.button, 10,
252 !!event->xbutton.state);
255 else if (event->xany.type == MotionNotify &&
258 gltrackball_track (trackball,
259 event->xmotion.x, event->xmotion.y,
260 MI_WIDTH (mi), MI_HEIGHT (mi));
271 gltrackball_rotate (trackball);
274 /* draw the gflux once */
275 void draw_gflux(ModeInfo * mi)
277 gfluxstruct *gp = &gflux[MI_SCREEN(mi)];
278 Display *display = MI_DISPLAY(mi);
279 Window window = MI_WINDOW(mi);
281 if (!gp->glx_context) return;
283 glXMakeCurrent(display, window, *(gp->glx_context));
287 if (mi->fps_p) do_fps (mi);
288 glXSwapBuffers(display, window);
292 /* reset the projection matrix */
293 void resetProjection(void) {
294 glMatrixMode(GL_PROJECTION);
296 glFrustum(-_zoom,_zoom,-0.8*_zoom,0.8*_zoom,2,6);
297 glTranslatef(0.0,0.0,-4.0);
298 glMatrixMode(GL_MODELVIEW);
302 /* Standard reshape function */
304 reshape_gflux(ModeInfo *mi, int width, int height)
306 glViewport( 0, 0, width, height );
311 /* main OpenGL initialization routine */
312 void initializeGL(ModeInfo *mi, GLsizei width, GLsizei height)
314 reshape_gflux(mi, width, height);
315 glViewport( 0, 0, width, height );
317 gflux->tex_xscale = 1.0; /* maybe changed later */
318 gflux->tex_yscale = 1.0;
322 gflux->drawFunc = (displaySolid);
323 glEnable(GL_DEPTH_TEST);
326 gflux->drawFunc = (displayLight);
327 glEnable(GL_DEPTH_TEST);
331 gflux->drawFunc = (displayTexture);
332 glEnable(GL_DEPTH_TEST);
337 gflux->drawFunc = (displayTexture);
338 glEnable(GL_DEPTH_TEST);
344 gflux->drawFunc = (displayWire);
345 glDisable(GL_DEPTH_TEST);
349 if(_flat) glShadeModel(GL_FLAT);
350 else glShadeModel(GL_SMOOTH);
355 /* xgflux initialization routine */
356 void init_gflux(ModeInfo * mi)
358 int screen = MI_SCREEN(mi);
362 if ((gflux = (gfluxstruct *)
363 calloc(MI_NUM_SCREENS(mi), sizeof (gfluxstruct))) == NULL)
368 trackball = gltrackball_init ();
371 char *s = get_string_resource ("mode", "Mode");
372 if (!s || !*s) _draw = wire;
373 else if (!strcasecmp (s, "wire")) _draw = wire;
374 else if (!strcasecmp (s, "solid")) _draw = solid;
375 else if (!strcasecmp (s, "light")) _draw = light;
376 else if (!strcasecmp (s, "checker")) _draw = checker;
377 else if (!strcasecmp (s, "grab")) _draw = grab;
381 "%s: mode must be one of: wire, solid, "
382 "light, checker, or grab; not \"%s\"\n",
389 gp->window = MI_WINDOW(mi);
390 if ((gp->glx_context = init_GL(mi)) != NULL) {
391 reshape_gflux(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
392 initializeGL(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
399 void release_gflux(ModeInfo * mi)
401 if(gflux->glx_context!=NULL) free(gflux->glx_context);
403 (void) free((void *) gflux);
411 void createTexture(void)
414 unsigned int data[] = { 0xFFFFFFFF, 0xAAAAAAAA, 0xFFFFFFFF, 0xAAAAAAAA,
415 0xAAAAAAAA, 0xFFFFFFFF, 0xAAAAAAAA, 0xFFFFFFFF,
416 0xFFFFFFFF, 0xAAAAAAAA, 0xFFFFFFFF, 0xAAAAAAAA,
417 0xAAAAAAAA, 0xFFFFFFFF, 0xAAAAAAAA, 0xFFFFFFFF };
419 gflux->tex_xscale = size;
420 gflux->tex_yscale = size;
422 glGenTextures (1, &gflux->texName);
423 glBindTexture (GL_TEXTURE_2D, gflux->texName);
425 glTexImage2D (GL_TEXTURE_2D, 0, 3, size, size, 0,
426 GL_RGBA, GL_UNSIGNED_BYTE, data);
428 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
429 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
431 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
432 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
439 Bool mipmap_p = True;
442 if (MI_IS_WIREFRAME(gflux->modeinfo))
445 if (! screen_to_texture (gflux->modeinfo->xgwa.screen,
446 gflux->modeinfo->window, 0, 0, mipmap_p,
447 NULL, &gflux->img_geom, &iw, &ih, &tw, &th))
450 gflux->tex_xscale = (GLfloat) iw / tw;
451 gflux->tex_yscale = -(GLfloat) ih / th;
452 gflux->img_width = iw;
453 gflux->img_height = ih;
455 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
456 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
457 (mipmap_p ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR));
461 void initLighting(void)
463 static float ambientA[] = {0.0, 0.0, 0.0, 1.0};
464 static float diffuseA[] = {1.0, 1.0, 1.0, 1.0};
465 static float positionA[] = {5.0, 5.0, 15.0, 1.0};
467 static float front_mat_shininess[] = {30.0};
468 static float front_mat_specular[] = {0.5, 0.5, 0.5, 1.0};
470 static float mat_diffuse[] = {0.5, 0.5, 0.5, 1.0};
472 glMaterialfv(GL_FRONT, GL_SHININESS, front_mat_shininess);
473 glMaterialfv(GL_FRONT, GL_SPECULAR, front_mat_specular);
475 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, mat_diffuse);
477 glLightfv(GL_LIGHT0, GL_AMBIENT, ambientA);
478 glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseA);
479 glLightfv(GL_LIGHT0, GL_POSITION, positionA);
480 glEnable(GL_LIGHTING);
482 glLightModelf(GL_LIGHT_MODEL_TWO_SIDE,1);
484 glEnable(GL_NORMALIZE); /* would it be faster ... */
485 glEnable(GL_COLOR_MATERIAL);
486 glColorMaterial(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE);
489 /************************************/
490 /* draw implementations */
491 /* somewhat inefficient since they */
492 /* all calculate previously */
493 /* calculated values again */
494 /* storing the values in an array */
495 /* is a posibility */
496 /************************************/
497 void displayTexture(void)
499 static double time = 0.0;
500 static double anglex = 0.0;
501 static double angley = 0.0;
502 static double anglez = 0.0;
506 double dx = 2.0/((double)_squares);
507 double dy = 2.0/((double)_squares);
509 double du = 2.0/((double)_squares);
510 double dv = 2.0/((double)_squares);
512 double xs = gflux->tex_xscale;
513 double ys = gflux->tex_yscale;
515 double minx, miny, maxx, maxy;
519 minx = (GLfloat) gflux->img_geom.x / gflux->img_width;
520 miny = (GLfloat) gflux->img_geom.y / gflux->img_height;
521 maxx = ((GLfloat) (gflux->img_geom.x + gflux->img_geom.width) /
523 maxy = ((GLfloat) (gflux->img_geom.y + gflux->img_geom.height) /
527 minx = (minx * 2) - 1;
528 miny = (miny * 2) - 1;
529 maxx = (maxx * 2) - 1;
530 maxy = (maxy * 2) - 1;
540 glMatrixMode (GL_TEXTURE);
542 glTranslatef(-1,-1,0);
544 glMatrixMode (GL_MODELVIEW);
548 glRotatef(anglex,1,0,0);
549 glRotatef(angley,0,1,0);
550 glRotatef(anglez,0,0,1);
551 glScalef(1,1,(GLfloat)_waveHeight);
552 glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
553 glEnable(GL_TEXTURE_2D);
556 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
557 glBindTexture(GL_TEXTURE_2D, gflux->texName);
558 check_gl_error("texture binding");
560 glColor3f(0.5,0.5,0.5);
562 for(x = minx, u = minu; x < maxx - 0.01; x += dx, u += du) {
563 glBegin(GL_QUAD_STRIP);
564 for (y = miny, v = minv; y <= maxy + 0.01; y += dy, v += dv) {
565 z = getGrid(x,y,time);
566 glTexCoord2f(u*xs,v*ys);
568 getGrid(x+dx,y,time)-getGrid(x-dx,y,time),
569 getGrid(x,y+dy,time)-getGrid(x,y-dy,time),
574 z = getGrid(x+dx,y,time);
575 glTexCoord2f((u+du)*xs,v*ys);
577 getGrid(x+dx+dx,y,time)-getGrid(x,y,time),
578 getGrid(x+dx,y+dy,time)-getGrid(x+dx,y-dy,time),
581 glVertex3f(x+dx,y,z);
586 /* Draw a border around the grid.
588 glColor3f(0.4, 0.4, 0.4);
589 glDisable(GL_TEXTURE_2D);
590 glEnable (GL_LINE_SMOOTH);
592 glBegin(GL_LINE_LOOP);
594 for (x = minx; x <= maxx; x += dx)
595 glVertex3f (x, y, getGrid (x, y, time));
597 for (y = miny; y <= maxy; y += dy)
598 glVertex3f (x, y, getGrid (x, y, time));
600 for (x = maxx; x >= minx; x -= dx)
601 glVertex3f (x, y, getGrid (x, y, time));
603 for (y = maxy; y >= miny; y -= dy)
604 glVertex3f (x, y, getGrid (x, y, time));
607 glEnable(GL_TEXTURE_2D);
609 if (! button_down_p) {
611 anglex -= _rotationx;
612 angley -= _rotationy;
613 anglez -= _rotationz;
616 void displaySolid(void)
618 static double time = 0.0;
619 static double anglex = 0.0;
620 static double angley = 0.0;
621 static double anglez = 0.0;
625 double dx = 2.0/((double)_squares);
626 double dy = 2.0/((double)_squares);
629 glRotatef(anglex,1,0,0);
630 glRotatef(angley,0,1,0);
631 glRotatef(anglez,0,0,1);
633 glScalef(1,1,(GLfloat)_waveHeight);
634 glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
636 for(x=-1;x<0.9999;x+=dx) {
637 glBegin(GL_QUAD_STRIP);
638 for(y=-1;y<=1;y+=dy) {
639 z = getGrid(x,y,time);
641 glColor3fv(gflux->colour);
644 z = getGrid(x+dx,y,time);
646 glColor3fv(gflux->colour);
647 glVertex3f(x+dx,y,z);
652 if (! button_down_p) {
654 anglex -= _rotationx;
655 angley -= _rotationy;
656 anglez -= _rotationz;
661 void displayLight(void)
663 static double time = 0.0;
664 static double anglex = 0.0;
665 static double angley = 0.0;
666 static double anglez = 0.0;
670 double dx = 2.0/((double)_squares);
671 double dy = 2.0/((double)_squares);
674 glRotatef(anglex,1,0,0);
675 glRotatef(angley,0,1,0);
676 glRotatef(anglez,0,0,1);
678 glScalef(1,1,(GLfloat)_waveHeight);
679 glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
681 for(x=-1;x<0.9999;x+=dx) {
682 glBegin(GL_QUAD_STRIP);
683 for(y=-1;y<=1;y+=dy) {
684 z = getGrid(x,y,time);
686 glColor3fv(gflux->colour);
688 getGrid(x+dx,y,time)-getGrid(x-dx,y,time),
689 getGrid(x,y+dy,time)-getGrid(x,y-dy,time),
694 z = getGrid(x+dx,y,time);
696 glColor3fv(gflux->colour);
698 getGrid(x+dx+dx,y,time)-getGrid(x,y,time),
699 getGrid(x+dx,y+dy,time)-getGrid(x+dx,y-dy,time),
702 glVertex3f(x+dx,y,z);
707 if (! button_down_p) {
709 anglex -= _rotationx;
710 angley -= _rotationy;
711 anglez -= _rotationz;
715 void displayWire(void)
717 static double time = 0.0;
718 static double anglex = 0.0;
719 static double angley = 0.0;
720 static double anglez = 0.0;
724 double dx1 = 2.0/((double)(_squares*_resolution)) - 0.00001;
725 double dy1 = 2.0/((double)(_squares*_resolution)) - 0.00001;
726 double dx2 = 2.0/((double)_squares) - 0.00001;
727 double dy2 = 2.0/((double)_squares) - 0.00001;
730 glRotatef(anglex,1,0,0);
731 glRotatef(angley,0,1,0);
732 glRotatef(anglez,0,0,1);
734 glScalef(1,1,(GLfloat)_waveHeight);
735 glClear(GL_COLOR_BUFFER_BIT);
737 for(x=-1;x<=1;x+=dx2) {
738 glBegin(GL_LINE_STRIP);
739 for(y=-1;y<=1;y+=dy1) {
740 z = getGrid(x,y,time);
742 glColor3fv(gflux->colour);
747 for(y=-1;y<=1;y+=dy2) {
748 glBegin(GL_LINE_STRIP);
749 for(x=-1;x<=1;x+=dx1) {
750 z = getGrid(x,y,time);
752 glColor3fv(gflux->colour);
758 if (! button_down_p) {
760 anglex -= _rotationx;
761 angley -= _rotationy;
762 anglez -= _rotationz;
766 /* generates new ripples */
769 static int counter=0;
773 if (button_down_p) return;
775 tmp = 1.0/((double)_waveChange);
776 if(!(counter%_waveChange)) {
777 newWave = ((int)(counter*tmp))%_waves;
778 gflux->dispx[newWave] = -frand(1.0);
779 gflux->dispy[newWave] = -frand(1.0);
780 gflux->freq[newWave] = _waveFreq * frand(1.0);
781 gflux->wa[newWave] = 0.0;
784 gflux->wa[newWave] += tmp;
785 gflux->wa[(newWave+1)%_waves] -= tmp;
788 /* returns a height for the grid given time and x,y space co-ords */
789 double getGrid(double x, double y, double a)
795 tmp = 1.0/((float)_waves);
796 for(i=0;i<_waves;i++) {
797 retval += gflux->wa[i] * tmp * sin( gflux->freq[i]
798 * ( (x+gflux->dispx[i]) * (x+gflux->dispx[i])
799 + (y+gflux->dispy[i]) * (y+gflux->dispy[i]) +a ) );