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" \
56 # include "xlockmore.h" /* from the xscreensaver distribution */
57 #else /* !STANDALONE */
58 # include "xlock.h" /* from the xlockmore distribution */
59 #endif /* !STANDALONE */
61 #ifdef USE_GL /* whole file */
65 # include <X11/Xmu/Drawing.h>
67 # include <Xmu/Drawing.h>
72 #define countof(x) (sizeof((x))/sizeof((*x)))
83 #include "grab-ximage.h"
84 #include "gltrackball.h"
87 static enum {wire=0,solid,light,checker,grab} _draw; /* draw style */
88 static int _squares = 19; /* grid size */
89 static int _resolution = 4; /* wireframe resolution */
92 static float _speed = 0.05;
93 static float _rotationx = 0.01;
94 static float _rotationy = 0.0;
95 static float _rotationz = 0.1;
96 static float _zoom = 1.0;
98 static int _waves = 3;
99 static int _waveChange = 50;
100 static float _waveHeight = 1.0;
101 static float _waveFreq = 3.0;
103 static trackball_state *trackball;
104 static Bool button_down_p = False;
109 static XrmOptionDescRec opts[] = {
110 {"-squares", ".gflux.squares", XrmoptionSepArg, 0},
111 {"-resolution", ".gflux.resolution", XrmoptionSepArg, 0},
112 /* {"-draw", ".gflux.draw", XrmoptionSepArg, 0},*/
113 {"-mode", ".gflux.mode", XrmoptionSepArg, 0},
114 {"-wireframe", ".gflux.mode", XrmoptionNoArg, "wire"},
115 {"-flat", ".gflux.flat", XrmoptionSepArg, 0},
116 {"-speed", ".gflux.speed", XrmoptionSepArg, 0},
117 {"-rotationx", ".gflux.rotationx", XrmoptionSepArg, 0},
118 {"-rotationy", ".gflux.rotationy", XrmoptionSepArg, 0},
119 {"-rotationz", ".gflux.rotationz", XrmoptionSepArg, 0},
120 {"-waves", ".gflux.waves", XrmoptionSepArg, 0},
121 {"-waveChange", ".gflux.waveChange", XrmoptionSepArg, 0},
122 {"-waveHeight", ".gflux.waveHeight", XrmoptionSepArg, 0},
123 {"-waveFreq", ".gflux.waveFreq", XrmoptionSepArg, 0},
124 {"-zoom", ".gflux.zoom", XrmoptionSepArg, 0},
128 static argtype vars[] = {
129 {&_squares, "squares", "Squares", "19", t_Int},
130 {&_resolution, "resolution", "Resolution", "4", t_Int},
131 /* {&_draw, "draw", "Draw", "2", t_Int},*/
132 {&_flat, "flat", "Flat", "0", t_Int},
133 {&_speed, "speed", "Speed", "0.05", t_Float},
134 {&_rotationx, "rotationx", "Rotationx", "0.01", t_Float},
135 {&_rotationy, "rotationy", "Rotationy", "0.0", t_Float},
136 {&_rotationz, "rotationz", "Rotationz", "0.1", t_Float},
137 {&_waves, "waves", "Waves", "3", t_Int},
138 {&_waveChange, "waveChange", "WaveChange", "50", t_Int},
139 {&_waveHeight, "waveHeight", "WaveHeight", "1.0", t_Float},
140 {&_waveFreq, "waveFreq", "WaveFreq", "3.0", t_Float},
141 {&_zoom, "zoom", "Zoom", "1.0", t_Float},
145 static OptionStruct desc[] =
147 {"-squares num", "size of grid in squares (19)"},
148 {"-resolution num", "detail of lines making grid, wireframe only (4)"},
149 /* {"-draw num", "draw method to use: 0=wireframe 1=solid 2=lit (0)"},*/
150 {"-flat num", "shading method, not wireframe: 0=smooth 1=flat (0)"},
151 {"-speed num", "speed of waves (0.05)"},
152 {"-rotationx num", "speed of xrotation (0.01)"},
153 {"-rotationy num", "speed of yrotation (0.00)"},
154 {"-rotationz num", "speed of zrotation (0.10)"},
155 {"-waves num", "number of simultanious waves (3)"},
156 {"-waveChange num", "number of cyles for a wave to change (50)"},
157 {"-waveHeight num", "height of waves (1.0)"},
158 {"-waveFreq num", "max frequency of a wave (3.0)"},
159 {"-zoom num", "camera control (1.0)"},
162 ModeSpecOpt gflux_opts = {countof(opts), opts, countof(vars), vars, desc};
165 ModStruct gflux_description =
166 {"gflux", "init_gflux", "draw_gflux", "release_gflux",
167 "draw_gflux", "init_gflux", NULL, &gflux_opts,
168 1000, 1, 2, 1, 4, 1.0, "",
169 "Gflux: an OpenGL gflux", 0, NULL};
172 /* structure for holding the gflux data */
175 int screen_width, screen_height;
176 GLXContext *glx_context;
179 #define MAXWAVES 10 /* should be dynamic */
181 double freq[MAXWAVES];
182 double dispy[MAXWAVES];
183 double dispx[MAXWAVES];
189 int img_width, img_height;
190 void (*drawFunc)(void);
192 static gfluxstruct *gflux = NULL;
195 void initLighting(void);
196 void grabTexture(void);
197 void createTexture(void);
198 void displaySolid(void); /* drawFunc implementations */
199 void displayLight(void);
200 void displayTexture(void);
201 void displayWire(void);
203 double getGrid(double,double,double);
205 /* as macro for speed */
206 /* could do with colour cycling here */
207 /* void genColour(double);*/
208 #define genColour(X) \
210 gflux->colour[0] = 0.0;\
211 gflux->colour[1] = 0.5+0.5*(X);\
212 gflux->colour[2] = 0.5-0.5*(X);\
215 /* BEGINNING OF FUNCTIONS */
219 gflux_handle_event (ModeInfo *mi, XEvent *event)
221 if (event->xany.type == ButtonPress &&
222 event->xbutton.button == Button1)
224 button_down_p = True;
225 gltrackball_start (trackball,
226 event->xbutton.x, event->xbutton.y,
227 MI_WIDTH (mi), MI_HEIGHT (mi));
230 else if (event->xany.type == ButtonRelease &&
231 event->xbutton.button == Button1)
233 button_down_p = False;
236 else if (event->xany.type == ButtonPress &&
237 (event->xbutton.button == Button4 ||
238 event->xbutton.button == Button5))
240 gltrackball_mousewheel (trackball, event->xbutton.button, 10,
241 !!event->xbutton.state);
244 else if (event->xany.type == MotionNotify &&
247 gltrackball_track (trackball,
248 event->xmotion.x, event->xmotion.y,
249 MI_WIDTH (mi), MI_HEIGHT (mi));
260 gltrackball_rotate (trackball);
263 /* draw the gflux once */
264 void draw_gflux(ModeInfo * mi)
266 gfluxstruct *gp = &gflux[MI_SCREEN(mi)];
267 Display *display = MI_DISPLAY(mi);
268 Window window = MI_WINDOW(mi);
270 if (!gp->glx_context) return;
272 glXMakeCurrent(display, window, *(gp->glx_context));
276 if (mi->fps_p) do_fps (mi);
277 glXSwapBuffers(display, window);
281 /* reset the projection matrix */
282 void resetProjection(void) {
283 glMatrixMode(GL_PROJECTION);
285 glFrustum(-_zoom,_zoom,-0.8*_zoom,0.8*_zoom,2,6);
286 glTranslatef(0.0,0.0,-4.0);
287 glMatrixMode(GL_MODELVIEW);
291 /* Standard reshape function */
293 reshape_gflux(ModeInfo *mi, int width, int height)
295 glViewport( 0, 0, width, height );
300 /* main OpenGL initialization routine */
301 void initializeGL(ModeInfo *mi, GLsizei width, GLsizei height)
303 reshape_gflux(mi, width, height);
304 glViewport( 0, 0, width, height );
306 gflux->tex_xscale = 1.0; /* maybe changed later */
307 gflux->tex_yscale = 1.0;
311 gflux->drawFunc = (displaySolid);
312 glEnable(GL_DEPTH_TEST);
315 gflux->drawFunc = (displayLight);
316 glEnable(GL_DEPTH_TEST);
320 gflux->drawFunc = (displayTexture);
321 glEnable(GL_DEPTH_TEST);
326 gflux->drawFunc = (displayTexture);
327 glEnable(GL_DEPTH_TEST);
333 gflux->drawFunc = (displayWire);
334 glDisable(GL_DEPTH_TEST);
338 if(_flat) glShadeModel(GL_FLAT);
339 else glShadeModel(GL_SMOOTH);
344 /* xgflux initialization routine */
345 void init_gflux(ModeInfo * mi)
347 int screen = MI_SCREEN(mi);
351 if ((gflux = (gfluxstruct *)
352 calloc(MI_NUM_SCREENS(mi), sizeof (gfluxstruct))) == NULL)
357 trackball = gltrackball_init ();
360 char *s = get_string_resource ("mode", "Mode");
361 if (!s || !*s) _draw = wire;
362 else if (!strcasecmp (s, "wire")) _draw = wire;
363 else if (!strcasecmp (s, "solid")) _draw = solid;
364 else if (!strcasecmp (s, "light")) _draw = light;
365 else if (!strcasecmp (s, "checker")) _draw = checker;
366 else if (!strcasecmp (s, "grab")) _draw = grab;
370 "%s: mode must be one of: wire, solid, "
371 "light, checker, or grab; not \"%s\"\n",
378 gp->window = MI_WINDOW(mi);
379 if ((gp->glx_context = init_GL(mi)) != NULL) {
380 reshape_gflux(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
381 initializeGL(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
388 void release_gflux(ModeInfo * mi)
390 if(gflux->glx_context!=NULL) free(gflux->glx_context);
392 (void) free((void *) gflux);
400 void createTexture(void)
403 unsigned int data[] = { 0xFFFFFFFF, 0xAAAAAAAA, 0xFFFFFFFF, 0xAAAAAAAA,
404 0xAAAAAAAA, 0xFFFFFFFF, 0xAAAAAAAA, 0xFFFFFFFF,
405 0xFFFFFFFF, 0xAAAAAAAA, 0xFFFFFFFF, 0xAAAAAAAA,
406 0xAAAAAAAA, 0xFFFFFFFF, 0xAAAAAAAA, 0xFFFFFFFF };
408 gflux->tex_xscale = size;
409 gflux->tex_yscale = size;
411 glGenTextures (1, &gflux->texName);
412 glBindTexture (GL_TEXTURE_2D, gflux->texName);
414 glTexImage2D (GL_TEXTURE_2D, 0, 3, size, size, 0,
415 GL_RGBA, GL_UNSIGNED_BYTE, data);
417 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
418 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
420 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
421 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
428 Bool mipmap_p = True;
431 if (MI_IS_WIREFRAME(gflux->modeinfo))
434 if (! screen_to_texture (gflux->modeinfo->xgwa.screen,
435 gflux->modeinfo->window, 0, 0, mipmap_p,
436 NULL, &gflux->img_geom, &iw, &ih, &tw, &th))
439 gflux->tex_xscale = (GLfloat) iw / tw;
440 gflux->tex_yscale = -(GLfloat) ih / th;
441 gflux->img_width = iw;
442 gflux->img_height = ih;
444 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
445 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
446 (mipmap_p ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR));
450 void initLighting(void)
452 static float ambientA[] = {0.0, 0.0, 0.0, 1.0};
453 static float diffuseA[] = {1.0, 1.0, 1.0, 1.0};
454 static float positionA[] = {5.0, 5.0, 15.0, 1.0};
456 static float front_mat_shininess[] = {30.0};
457 static float front_mat_specular[] = {0.5, 0.5, 0.5, 1.0};
459 static float mat_diffuse[] = {0.5, 0.5, 0.5, 1.0};
461 glMaterialfv(GL_FRONT, GL_SHININESS, front_mat_shininess);
462 glMaterialfv(GL_FRONT, GL_SPECULAR, front_mat_specular);
464 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, mat_diffuse);
466 glLightfv(GL_LIGHT0, GL_AMBIENT, ambientA);
467 glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseA);
468 glLightfv(GL_LIGHT0, GL_POSITION, positionA);
469 glEnable(GL_LIGHTING);
471 glLightModelf(GL_LIGHT_MODEL_TWO_SIDE,1);
473 glEnable(GL_NORMALIZE); /* would it be faster ... */
474 glEnable(GL_COLOR_MATERIAL);
475 glColorMaterial(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE);
478 /************************************/
479 /* draw implementations */
480 /* somewhat inefficient since they */
481 /* all calculate previously */
482 /* calculated values again */
483 /* storing the values in an array */
484 /* is a posibility */
485 /************************************/
486 void displayTexture(void)
488 static double time = 0.0;
489 static double anglex = 0.0;
490 static double angley = 0.0;
491 static double anglez = 0.0;
495 double dx = 2.0/((double)_squares);
496 double dy = 2.0/((double)_squares);
498 double du = 2.0/((double)_squares);
499 double dv = 2.0/((double)_squares);
501 double xs = gflux->tex_xscale;
502 double ys = gflux->tex_yscale;
504 double minx, miny, maxx, maxy;
508 minx = (GLfloat) gflux->img_geom.x / gflux->img_width;
509 miny = (GLfloat) gflux->img_geom.y / gflux->img_height;
510 maxx = ((GLfloat) (gflux->img_geom.x + gflux->img_geom.width) /
512 maxy = ((GLfloat) (gflux->img_geom.y + gflux->img_geom.height) /
516 minx = (minx * 2) - 1;
517 miny = (miny * 2) - 1;
518 maxx = (maxx * 2) - 1;
519 maxy = (maxy * 2) - 1;
529 glMatrixMode (GL_TEXTURE);
531 glTranslatef(-1,-1,0);
533 glMatrixMode (GL_MODELVIEW);
537 glRotatef(anglex,1,0,0);
538 glRotatef(angley,0,1,0);
539 glRotatef(anglez,0,0,1);
540 glScalef(1,1,(GLfloat)_waveHeight);
541 glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
542 glEnable(GL_TEXTURE_2D);
545 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
546 glBindTexture(GL_TEXTURE_2D, gflux->texName);
547 check_gl_error("texture binding");
549 glColor3f(0.5,0.5,0.5);
551 for(x = minx, u = minu; x < maxx - 0.01; x += dx, u += du) {
552 glBegin(GL_QUAD_STRIP);
553 for (y = miny, v = minv; y <= maxy + 0.01; y += dy, v += dv) {
554 z = getGrid(x,y,time);
555 glTexCoord2f(u*xs,v*ys);
557 getGrid(x+dx,y,time)-getGrid(x-dx,y,time),
558 getGrid(x,y+dy,time)-getGrid(x,y-dy,time),
563 z = getGrid(x+dx,y,time);
564 glTexCoord2f((u+du)*xs,v*ys);
566 getGrid(x+dx+dx,y,time)-getGrid(x,y,time),
567 getGrid(x+dx,y+dy,time)-getGrid(x+dx,y-dy,time),
570 glVertex3f(x+dx,y,z);
575 /* Draw a border around the grid.
577 glColor3f(0.4, 0.4, 0.4);
578 glDisable(GL_TEXTURE_2D);
579 glEnable (GL_LINE_SMOOTH);
581 glBegin(GL_LINE_LOOP);
583 for (x = minx; x <= maxx; x += dx)
584 glVertex3f (x, y, getGrid (x, y, time));
586 for (y = miny; y <= maxy; y += dy)
587 glVertex3f (x, y, getGrid (x, y, time));
589 for (x = maxx; x >= minx; x -= dx)
590 glVertex3f (x, y, getGrid (x, y, time));
592 for (y = maxy; y >= miny; y -= dy)
593 glVertex3f (x, y, getGrid (x, y, time));
596 glEnable(GL_TEXTURE_2D);
598 if (! button_down_p) {
600 anglex -= _rotationx;
601 angley -= _rotationy;
602 anglez -= _rotationz;
605 void displaySolid(void)
607 static double time = 0.0;
608 static double anglex = 0.0;
609 static double angley = 0.0;
610 static double anglez = 0.0;
614 double dx = 2.0/((double)_squares);
615 double dy = 2.0/((double)_squares);
618 glRotatef(anglex,1,0,0);
619 glRotatef(angley,0,1,0);
620 glRotatef(anglez,0,0,1);
622 glScalef(1,1,(GLfloat)_waveHeight);
623 glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
625 for(x=-1;x<0.9999;x+=dx) {
626 glBegin(GL_QUAD_STRIP);
627 for(y=-1;y<=1;y+=dy) {
628 z = getGrid(x,y,time);
630 glColor3fv(gflux->colour);
633 z = getGrid(x+dx,y,time);
635 glColor3fv(gflux->colour);
636 glVertex3f(x+dx,y,z);
641 if (! button_down_p) {
643 anglex -= _rotationx;
644 angley -= _rotationy;
645 anglez -= _rotationz;
650 void displayLight(void)
652 static double time = 0.0;
653 static double anglex = 0.0;
654 static double angley = 0.0;
655 static double anglez = 0.0;
659 double dx = 2.0/((double)_squares);
660 double dy = 2.0/((double)_squares);
663 glRotatef(anglex,1,0,0);
664 glRotatef(angley,0,1,0);
665 glRotatef(anglez,0,0,1);
667 glScalef(1,1,(GLfloat)_waveHeight);
668 glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
670 for(x=-1;x<0.9999;x+=dx) {
671 glBegin(GL_QUAD_STRIP);
672 for(y=-1;y<=1;y+=dy) {
673 z = getGrid(x,y,time);
675 glColor3fv(gflux->colour);
677 getGrid(x+dx,y,time)-getGrid(x-dx,y,time),
678 getGrid(x,y+dy,time)-getGrid(x,y-dy,time),
683 z = getGrid(x+dx,y,time);
685 glColor3fv(gflux->colour);
687 getGrid(x+dx+dx,y,time)-getGrid(x,y,time),
688 getGrid(x+dx,y+dy,time)-getGrid(x+dx,y-dy,time),
691 glVertex3f(x+dx,y,z);
696 if (! button_down_p) {
698 anglex -= _rotationx;
699 angley -= _rotationy;
700 anglez -= _rotationz;
704 void displayWire(void)
706 static double time = 0.0;
707 static double anglex = 0.0;
708 static double angley = 0.0;
709 static double anglez = 0.0;
713 double dx1 = 2.0/((double)(_squares*_resolution)) - 0.00001;
714 double dy1 = 2.0/((double)(_squares*_resolution)) - 0.00001;
715 double dx2 = 2.0/((double)_squares) - 0.00001;
716 double dy2 = 2.0/((double)_squares) - 0.00001;
719 glRotatef(anglex,1,0,0);
720 glRotatef(angley,0,1,0);
721 glRotatef(anglez,0,0,1);
723 glScalef(1,1,(GLfloat)_waveHeight);
724 glClear(GL_COLOR_BUFFER_BIT);
726 for(x=-1;x<=1;x+=dx2) {
727 glBegin(GL_LINE_STRIP);
728 for(y=-1;y<=1;y+=dy1) {
729 z = getGrid(x,y,time);
731 glColor3fv(gflux->colour);
736 for(y=-1;y<=1;y+=dy2) {
737 glBegin(GL_LINE_STRIP);
738 for(x=-1;x<=1;x+=dx1) {
739 z = getGrid(x,y,time);
741 glColor3fv(gflux->colour);
747 if (! button_down_p) {
749 anglex -= _rotationx;
750 angley -= _rotationy;
751 anglez -= _rotationz;
755 /* generates new ripples */
758 static int counter=0;
762 if (button_down_p) return;
764 tmp = 1.0/((double)_waveChange);
765 if(!(counter%_waveChange)) {
766 newWave = ((int)(counter*tmp))%_waves;
767 gflux->dispx[newWave] = -frand(1.0);
768 gflux->dispy[newWave] = -frand(1.0);
769 gflux->freq[newWave] = _waveFreq * frand(1.0);
770 gflux->wa[newWave] = 0.0;
773 gflux->wa[newWave] += tmp;
774 gflux->wa[(newWave+1)%_waves] -= tmp;
777 /* returns a height for the grid given time and x,y space co-ords */
778 double getGrid(double x, double y, double a)
784 tmp = 1.0/((float)_waves);
785 for(i=0;i<_waves;i++) {
786 retval += gflux->wa[i] * tmp * sin( gflux->freq[i]
787 * ( (x+gflux->dispx[i]) * (x+gflux->dispx[i])
788 + (y+gflux->dispy[i]) * (y+gflux->dispy[i]) +a ) );