ad14d01f2c1827e3a9f95f5ac99c03c4d905738b
[xscreensaver] / hacks / glx / gflux.c
1 /* -*- Mode: C; tab-width: 4 -*- emacs friendly */
2 /* gflux - creates a fluctuating 3D grid 
3  * requires OpenGL or MesaGL
4  * 
5  * Copyright (c) Josiah Pease, 2000
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 
12  * implied warranty.
13  *
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
17  * linux usable  
18  * Personal thanks to Kevin Moss, Paul Sheahee and Jamie Zawinski
19  * 
20  * some xscreensaver code lifted from superquadrics.  Most other glx hacks 
21  * used as reference at some time.
22  *
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
26  * 
27  * Josiah Pease <gfluxcode@jpease.force9.co.uk> 21 July 2000
28  * 
29  * History 
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
39  */
40
41
42 /*-
43  * due to a Bug/feature in VMS X11/Intrinsic.h has to be placed before xlock.
44  * otherwise caddr_t is not defined correctly
45  */
46
47 #include <X11/Intrinsic.h>
48
49
50 #ifdef STANDALONE
51 # define PROGCLASS                                              "gflux"
52 # define HACK_INIT                                              init_gflux
53 # define HACK_DRAW                                              draw_gflux
54 # define HACK_RESHAPE                                   reshape_gflux
55 # define HACK_HANDLE_EVENT                              gflux_handle_event
56 # define EVENT_MASK                                             PointerMotionMask
57 # define gflux_opts                                             xlockmore_opts
58 #define DEFAULTS                        "*delay:                20000   \n" \
59                                                                                 "*showFPS:      False   \n" \
60                                         "*mode:         light" "\n" \
61                                         "*squares:      19      \n" \
62                                                                                 "*resolution:   0       \n" \
63                                         "*flat:         0       \n" \
64                                         "*speed:        0.05    \n" \
65                                         "*rotationx:    0.01    \n" \
66                                         "*rotationy:    0.0     \n" \
67                                         "*rotationz:    0.1     \n" \
68                                         "*waves:        3       \n" \
69                                         "*waveChange:   50      \n" \
70                                         "*waveHeight:  0.8      \n" \
71                                         "*waveFreq:    3.0      \n" \
72                                         "*zoom:        1.0      \n" 
73
74
75 # include "xlockmore.h"                         /* from the xscreensaver distribution */
76 #else /* !STANDALONE */
77 # include "xlock.h"                                     /* from the xlockmore distribution */
78 #endif /* !STANDALONE */
79
80 #ifdef USE_GL /* whole file */
81
82 #ifdef HAVE_XMU
83 # ifndef VMS
84 #  include <X11/Xmu/Drawing.h>
85 #else  /* VMS */
86 #  include <Xmu/Drawing.h>
87 # endif /* VMS */
88 #endif
89
90 #ifdef HAVE_PPM
91 #include <ppm.h>
92 #endif
93
94 #undef countof
95 #define countof(x) (sizeof((x))/sizeof((*x)))
96
97 #include <stdlib.h>
98 #include <stdio.h>
99
100 #include <GL/gl.h>
101 #include <GL/glu.h>
102 #include <GL/glx.h>
103
104 #include <math.h>
105
106 #include "grab-ximage.h"
107 #include "gltrackball.h"
108
109
110 static enum {wire=0,solid,light,checker,textured,grab} _draw; /* draw style */
111 static int _squares = 19;                                 /* grid size */
112 static int _resolution = 4;                    /* wireframe resolution */
113 static int _flat = 0;
114
115 static float _speed = 0.05;
116 static float _rotationx = 0.01;
117 static float _rotationy = 0.0;
118 static float _rotationz = 0.1;
119 static float _zoom = 1.0;
120
121 static int _waves = 3;
122 static int _waveChange = 50;
123 static float _waveHeight = 1.0;
124 static float _waveFreq = 3.0;
125
126 static trackball_state *trackball;
127 static Bool button_down_p = False;
128
129 #define WIDTH 320
130 #define HEIGHT 240
131
132 static XrmOptionDescRec opts[] = {
133     {"-squares", ".gflux.squares", XrmoptionSepArg, (caddr_t) NULL},
134     {"-resolution", ".gflux.resolution", XrmoptionSepArg, (caddr_t) NULL},
135 /*    {"-draw", ".gflux.draw", XrmoptionSepArg, (caddr_t) NULL},*/
136     {"-mode", ".gflux.mode", XrmoptionSepArg, (caddr_t) NULL},
137     {"-flat", ".gflux.flat", XrmoptionSepArg, (caddr_t) NULL},
138     {"-speed", ".gflux.speed", XrmoptionSepArg, (caddr_t) NULL},
139     {"-rotationx", ".gflux.rotationx", XrmoptionSepArg, (caddr_t) NULL},
140     {"-rotationy", ".gflux.rotationy", XrmoptionSepArg, (caddr_t) NULL},
141     {"-rotationz", ".gflux.rotationz", XrmoptionSepArg, (caddr_t) NULL},
142     {"-waves", ".gflux.waves", XrmoptionSepArg, (caddr_t) NULL},
143     {"-waveChange", ".gflux.waveChange", XrmoptionSepArg, (caddr_t) NULL},
144     {"-waveHeight", ".gflux.waveHeight", XrmoptionSepArg, (caddr_t) NULL},
145     {"-waveFreq", ".gflux.waveFreq", XrmoptionSepArg, (caddr_t) NULL},
146     {"-zoom", ".gflux.zoom", XrmoptionSepArg, (caddr_t) NULL},
147 };
148
149
150 static argtype vars[] = {
151     {(caddr_t *) & _squares, "squares", "Squares", "19", t_Int},
152     {(caddr_t *) & _resolution, "resolution", "Resolution", "4", t_Int},
153 /*    {(caddr_t *) & _draw, "draw", "Draw", "2", t_Int},*/
154     {(caddr_t *) & _flat, "flat", "Flat", "0", t_Int},
155     {(caddr_t *) & _speed, "speed", "Speed", "0.05", t_Float},
156     {(caddr_t *) & _rotationx, "rotationx", "Rotationx", "0.01", t_Float},
157     {(caddr_t *) & _rotationy, "rotationy", "Rotationy", "0.0", t_Float},
158     {(caddr_t *) & _rotationz, "rotationz", "Rotationz", "0.1", t_Float},
159     {(caddr_t *) & _waves, "waves", "Waves", "3", t_Int},
160     {(caddr_t *) & _waveChange, "waveChange", "WaveChange", "50", t_Int},
161     {(caddr_t *) & _waveHeight, "waveHeight", "WaveHeight", "1.0", t_Float},
162     {(caddr_t *) & _waveFreq, "waveFreq", "WaveFreq", "3.0", t_Float},
163     {(caddr_t *) & _zoom, "zoom", "Zoom", "1.0", t_Float},
164 };
165
166
167 static OptionStruct desc[] =
168 {
169     {"-squares num", "size of grid in squares (19)"},
170     {"-resolution num", "detail of lines making grid, wireframe only (4)"},
171 /*    {"-draw num", "draw method to use: 0=wireframe 1=solid 2=lit (0)"},*/
172     {"-flat num", "shading method, not wireframe: 0=smooth 1=flat (0)"},
173     {"-speed num", "speed of waves (0.05)"},
174     {"-rotationx num", "speed of xrotation (0.01)"},
175     {"-rotationy num", "speed of yrotation (0.00)"},
176     {"-rotationz num", "speed of zrotation (0.10)"},
177     {"-waves num", "number of simultanious waves (3)"},
178     {"-waveChange num", "number of cyles for a wave to change (50)"},
179     {"-waveHeight num", "height of waves (1.0)"},
180     {"-waveFreq num", "max frequency of a wave (3.0)"},
181     {"-zoom num", "camera control (1.0)"},
182 };
183
184 ModeSpecOpt gflux_opts = {countof(opts), opts, countof(vars), vars, desc};
185
186 #ifdef USE_MODULES
187 ModStruct   gflux_description =
188 {"gflux", "init_gflux", "draw_gflux", "release_gflux",
189  "draw_gflux", "init_gflux", NULL, &gflux_opts,
190  1000, 1, 2, 1, 4, 1.0, "",
191  "Gflux: an OpenGL gflux", 0, NULL};
192 #endif
193
194 /* structure for holding the gflux data */
195 typedef struct {
196     ModeInfo *modeinfo;
197     int screen_width, screen_height;
198     GLXContext *glx_context;
199     Window window;
200     XColor fg, bg;
201 #define MAXWAVES 10   /* should be dynamic    */
202     double wa[MAXWAVES];
203     double freq[MAXWAVES];
204     double dispy[MAXWAVES];
205     double dispx[MAXWAVES];
206     GLfloat colour[3];
207     int imageWidth;
208     int imageHeight;
209 #ifdef HAVE_PPM
210         pixval imageMax;
211     pixel **image;
212 #else
213         int imageMax;
214         GLubyte *image;
215 #endif
216     GLint texName;
217     GLfloat tex_xscale;
218     GLfloat tex_yscale;
219     void (*drawFunc)(void);
220 } gfluxstruct;
221 static gfluxstruct *gflux = NULL;
222
223 /* prototypes */
224 void initLighting(void);
225 void initTexture(void);
226 void loadTexture(void);
227 void grabTexture(void);
228 void createTexture(void);
229 void displaySolid(void);            /* drawFunc implementations */
230 void displayLight(void);
231 void displayTexture(void);
232 void displayWire(void);
233 void calcGrid(void);
234 double getGrid(double,double,double);
235
236 /* as macro for speed */
237 /* could do with colour cycling here */
238 /* void genColour(double);*/
239 #define genColour(X) \
240 {\
241     gflux->colour[0] = 0.0;\
242     gflux->colour[1] = 0.5+0.5*(X);\
243     gflux->colour[2] = 0.5-0.5*(X);\
244 }
245
246 /* BEGINNING OF FUNCTIONS */
247
248
249 Bool
250 gflux_handle_event (ModeInfo *mi, XEvent *event)
251 {
252   if (event->xany.type == ButtonPress &&
253       event->xbutton.button & Button1)
254     {
255       button_down_p = True;
256       gltrackball_start (trackball,
257                          event->xbutton.x, event->xbutton.y,
258                          MI_WIDTH (mi), MI_HEIGHT (mi));
259       return True;
260     }
261   else if (event->xany.type == ButtonRelease &&
262            event->xbutton.button & Button1)
263     {
264       button_down_p = False;
265       return True;
266     }
267   else if (event->xany.type == MotionNotify &&
268            button_down_p)
269     {
270       gltrackball_track (trackball,
271                          event->xmotion.x, event->xmotion.y,
272                          MI_WIDTH (mi), MI_HEIGHT (mi));
273       return True;
274     }
275
276   return False;
277 }
278
279
280 static void
281 userRot(void)
282 {
283   gltrackball_rotate (trackball);
284 }
285
286 /* draw the gflux once */
287 void draw_gflux(ModeInfo * mi)
288 {
289     gfluxstruct *gp = &gflux[MI_SCREEN(mi)];
290     Display    *display = MI_DISPLAY(mi);
291     Window      window = MI_WINDOW(mi);
292
293     if (!gp->glx_context) return;
294
295     glXMakeCurrent(display, window, *(gp->glx_context));
296
297     calcGrid();
298     gflux->drawFunc();
299     if (mi->fps_p) do_fps (mi);
300     glXSwapBuffers(display, window);
301 }
302
303
304 /* reset the projection matrix */
305 void resetProjection(void) {
306     glMatrixMode(GL_PROJECTION);
307     glLoadIdentity();
308     glFrustum(-_zoom,_zoom,-0.8*_zoom,0.8*_zoom,2,6);
309     glTranslatef(0.0,0.0,-4.0);
310     glMatrixMode(GL_MODELVIEW);
311     glLoadIdentity();
312 }
313
314 /* Standard reshape function */
315 void
316 reshape_gflux(ModeInfo *mi, int width, int height)
317 {
318     glViewport( 0, 0, width, height );
319     resetProjection();
320 }
321
322
323 /* main OpenGL initialization routine */
324 void initializeGL(ModeInfo *mi, GLsizei width, GLsizei height) 
325 {
326   reshape_gflux(mi, width, height);
327   glViewport( 0, 0, width, height ); 
328
329   gflux->tex_xscale = 1.0;  /* maybe changed later */
330   gflux->tex_yscale = 1.0;
331
332   switch(_draw) {
333     case solid :
334       gflux->drawFunc = (displaySolid);
335       glEnable(GL_DEPTH_TEST);
336     break;
337     case light :
338       gflux->drawFunc = (displayLight);
339       glEnable(GL_DEPTH_TEST);
340       initLighting();
341         break;
342         case checker :
343       gflux->drawFunc = (displayTexture);
344       glEnable(GL_DEPTH_TEST);
345       createTexture();
346       initTexture();
347       initLighting();
348     break;
349         case textured :
350       gflux->drawFunc = (displayTexture);
351       glEnable(GL_DEPTH_TEST);
352       loadTexture();
353       initTexture();
354       initLighting();
355     break;
356         case grab :
357       gflux->drawFunc = (displayTexture);
358       glEnable(GL_DEPTH_TEST);
359       grabTexture();
360       initTexture();
361       initLighting();
362     break;
363     case wire :
364         default :
365       gflux->drawFunc = (displayWire);
366       glDisable(GL_DEPTH_TEST);
367     break;
368   }
369
370   if(_flat) glShadeModel(GL_FLAT);
371   else glShadeModel(GL_SMOOTH);
372
373 }
374
375
376 /* xgflux initialization routine */
377 void init_gflux(ModeInfo * mi)
378 {
379     int screen = MI_SCREEN(mi);
380     gfluxstruct *gp;
381
382     if (gflux == NULL) {
383         if ((gflux = (gfluxstruct *) 
384                  calloc(MI_NUM_SCREENS(mi), sizeof (gfluxstruct))) == NULL)
385             return;
386     }
387     gp = &gflux[screen];
388
389     trackball = gltrackball_init ();
390
391     {
392       char *s = get_string_resource ("mode", "Mode");
393       if (!s || !*s)                       _draw = wire;
394       else if (!strcasecmp (s, "wire"))    _draw = wire;
395       else if (!strcasecmp (s, "solid"))   _draw = solid;
396       else if (!strcasecmp (s, "light"))   _draw = light;
397       else if (!strcasecmp (s, "checker")) _draw = checker;
398       else if (!strcasecmp (s, "stdin"))   _draw = textured;
399       else if (!strcasecmp (s, "grab"))    _draw = grab;
400       else
401         {
402           fprintf (stderr,
403                    "%s: mode must be one of: wire, solid, "
404                    "light, checker, or grab; not \"%s\"\n",
405                    progname, s);
406           exit (1);
407         }
408     }
409
410     gp->modeinfo = mi;
411     gp->window = MI_WINDOW(mi);
412     if ((gp->glx_context = init_GL(mi)) != NULL) {
413         reshape_gflux(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
414         initializeGL(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
415     } else {
416         MI_CLEARWINDOW(mi);
417     }
418 }
419
420 /* cleanup code */
421 void release_gflux(ModeInfo * mi)
422 {
423     if(gflux->image!=NULL) free(gflux->image);
424     if(gflux->glx_context!=NULL) free(gflux->glx_context);
425     if (gflux != NULL) {
426         (void) free((void *) gflux);
427         gflux = NULL;
428     }
429     FreeAllGL(mi);
430 }
431
432 #ifdef HAVE_PPM
433
434 /* load pnm from stdin using pnm libs */
435 void loadTexture(void)
436 {
437     FILE *file = stdin;
438         gflux->image = ppm_readppm( file, 
439                         &(gflux->imageHeight), &(gflux->imageWidth), &(gflux->imageMax) );
440 }
441
442 /* creates an image for texture mapping */
443 void createTexture(void)
444 {
445     int i,j,c;
446     pixel **result;
447
448         gflux->imageHeight = gflux->imageWidth = 8;
449
450         result = ppm_allocarray(gflux->imageHeight,gflux->imageWidth);
451     for(i=0;i<gflux->imageHeight;i++) {
452         for(j=0;j<gflux->imageWidth;j++) {
453             c = (((i)%2 ^ (j)%2) ? 100 : 200 );
454                         PPM_ASSIGN( result[i][j] , c, c, c );
455         }
456     }
457         gflux->image = result;
458 }
459
460 /* specifies image as texture */    
461 void initTexture(void)
462 {
463         glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
464         glGenTextures(1, &gflux->texName);
465         glBindTexture(GL_TEXTURE_2D, gflux->texName);
466         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
467         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
468         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
469         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
470
471     clear_gl_error();
472         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, gflux->imageWidth,
473                         gflux->imageHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, *(gflux->image));
474     check_gl_error("texture");
475 }
476
477 #else /* HAVE_PPM FALSE */
478
479 #define presult(A,B,C) (*(result+(A)*(gflux->imageWidth)*4+(B)*4+(C)))
480 void loadTexture(void)
481 {
482     int i, j, levels, width, height;
483     int red,green,blue;
484     char s[4];
485     int ppmType=0;
486     FILE *file = stdin;
487     GLubyte *result;
488
489     fgets(s,4,file);
490
491     if(!strncmp(s,"P6",2)) ppmType=6;
492     if(!strncmp(s,"P5",2)) ppmType=5;
493     if(!strncmp(s,"P3",2)) ppmType=3;
494     if(!strncmp(s,"P2",2)) ppmType=2;
495     if(!ppmType)exit(1);
496
497     while((i=getc(file))=='#')
498     {
499         while(getc(file)!='\n');
500     }
501     ungetc(i,file);
502
503     fscanf(file,"%d %d %d",&width,&height,&levels);
504
505     result = malloc(sizeof(GLubyte)*4*width*height);
506     gflux->imageWidth = width;
507     gflux->imageHeight = height;
508
509     switch(ppmType) {
510         case 2 :    /* ASCII grey */
511             for(i=0;i<height;i++) {
512                 for(j=0;j<width;j++) {
513                     fscanf(file,"%d",&red);
514                     presult(j,i,0) = red;
515                     presult(j,i,1) = red;
516                     presult(j,i,2) = red;
517                 }
518             }
519             break;
520         case 3 :    /* ASCII rgb */
521             for(i=0;i<height;i++) {
522                 for(j=0;j<width;j++) {
523                    fscanf(file,"%d %d %d",&red,&green,&blue);
524                     presult(j,i,0) = red;
525                     presult(j,i,1) = green;
526                     presult(j,i,2) = blue;
527                 }
528             }
529             break;
530         case 5 :    /* Binary grey */
531             getc(file); /* seems nessessary */
532             for(i=0;i<height;i++) {
533                 for(j=0;j<width;j++) {
534                     red = getc(file);
535                     presult(j,i,0) = red;
536                     presult(j,i,1) = red;
537                     presult(j,i,2) = red;
538                 }
539             }
540         break;
541         case 6 :    /* Binary rgb */
542             getc(file); /* seems nessessary */
543             for(i=0;i<height;i++) {
544                 for(j=0;j<width;j++) {
545                     red = getc(file);
546                     green = getc(file);
547                     blue = getc(file);
548                     presult(j,i,0) = red;
549                     presult(j,i,1) = green;
550                     presult(j,i,2) = blue;
551                 }
552             }
553         break;
554     }
555     gflux->image = result;
556 }
557
558 void createTexture(void)
559 {
560     int i,j,c;
561     GLubyte *result;
562
563     gflux->imageHeight = gflux->imageWidth = 8;
564
565     result = malloc(sizeof(GLubyte)*4*gflux->imageHeight*gflux->imageWidth);
566     for(i=0;i<gflux->imageHeight;i++) {
567         for(j=0;j<gflux->imageWidth;j++) {
568             c = (((i)%2 ^ (j)%2) ? 100 : 200 );
569             presult(i,j,0) = (GLubyte) c;
570             presult(i,j,1) = (GLubyte) c;
571             presult(i,j,2) = (GLubyte) c;
572             presult(i,j,3) = (GLubyte) 255;
573         }
574     }
575     gflux->image = result;
576 }
577
578 /* specifies image as texture */
579 void initTexture(void)
580 {
581     clear_gl_error();
582     glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
583     glGenTextures(1, &gflux->texName);
584     glBindTexture(GL_TEXTURE_2D, gflux->texName);
585     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
586     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
587     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
588     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
589     check_gl_error("texture parameter");
590
591     /* Bail out if the texture is too large. */
592     {
593       GLint width;
594       glTexImage2D(GL_PROXY_TEXTURE_2D, 0, GL_RGBA, gflux->imageWidth,
595                    gflux->imageHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
596       glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0,
597                                 GL_TEXTURE_WIDTH, &width);
598       if (width <= 0)
599         {
600           glGetIntegerv (GL_MAX_TEXTURE_SIZE, &width);
601           fprintf (stderr,
602                    "%s: texture too large (%dx%d -- probable max %dx%d)\n",
603                    progname, gflux->imageWidth, gflux->imageHeight,
604                    width, width);
605           return;
606         }
607     }
608
609     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, gflux->imageWidth,
610             gflux->imageHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, gflux->image);
611     check_gl_error("texture creation");
612 }
613
614 #undef presult
615 #endif
616
617
618 void
619 grabTexture(void)
620 {
621   int real_width  = gflux->modeinfo->xgwa.width;
622   int real_height = gflux->modeinfo->xgwa.height;
623   XImage *ximage = screen_to_ximage (gflux->modeinfo->xgwa.screen,
624                                      gflux->window);
625   Bool bigimage = False;
626   int size = 0;
627
628   if (ximage->width > 1280 ||   /* that's too damned big... */
629       ximage->height > 1280)
630     {
631       Display *dpy = gflux->modeinfo->dpy;
632       Visual *v = gflux->modeinfo->xgwa.visual;
633       int real_size = (ximage->width < ximage->height ?
634                        real_width : real_height);
635       XImage *x2;
636       int x, y, xoff, yoff;
637       size = (ximage->width < ximage->height ?
638               ximage->width : ximage->height);
639       bigimage = True;
640
641       if (size > 1024) size = 1024;
642
643       x2 = XCreateImage (dpy, v, 32, ZPixmap, 0, 0, size, size, 32, 0);
644       xoff = (real_width  > size ? (random() % (real_width  - size)) : 0);
645       yoff = (real_height > size ? (random() % (real_height - size)) : 0);
646
647 # if 0
648       fprintf(stderr, "%s: cropping texture from %dx%d to %dx%d @ %d,%d\n",
649               progname, ximage->width, ximage->height, x2->width, x2->height,
650               xoff, yoff);
651 # endif
652       x2->data = ximage->data;  /* we can reuse the same array */
653       for (y = 0; y < x2->height; y++)
654         for (x = 0; x < x2->width; x++)
655           XPutPixel (x2, x, y, XGetPixel (ximage, x+xoff, y+yoff));
656
657       real_width = real_height = real_size;
658       ximage->data = 0;
659       XDestroyImage (ximage);
660       ximage = x2;
661     }
662
663   /* Add a border. */
664   {
665     unsigned long gray = 0xAAAAAAAAL;  /* so shoot me */
666     int width  = (bigimage ? size : real_width);
667     int height = (bigimage ? size : real_height);
668     int i;
669     for (i = 0; i < real_height; i++)
670       {
671         XPutPixel (ximage, 0, i, gray);
672         XPutPixel (ximage, width-1, i, gray);
673       }
674     for (i = 0; i < real_width; i++)
675       {
676         XPutPixel (ximage, i, 0, gray);
677         XPutPixel (ximage, i, height-1, gray);
678       }
679   }
680
681   gflux->imageWidth  = ximage->width;
682   gflux->imageHeight = ximage->height;
683   gflux->image = ximage->data;
684
685   if (bigimage)  /* don't scale really large images */
686     {
687       gflux->tex_xscale = 1;
688       gflux->tex_yscale = 1;
689     }
690   else
691     {
692       gflux->tex_xscale = ((GLfloat) real_width  / (GLfloat) ximage->width);
693       gflux->tex_yscale = ((GLfloat) real_height / (GLfloat) ximage->height);
694     }
695
696   ximage->data = 0;
697   XDestroyImage (ximage);
698 }
699
700
701 void initLighting(void)
702 {
703     static float ambientA[] = {0.0, 0.0, 0.0, 1.0};
704     static float diffuseA[] = {1.0, 1.0, 1.0, 1.0};
705     static float positionA[] = {5.0, 5.0, 15.0, 1.0};
706
707     static float front_mat_shininess[] = {30.0};
708     static float front_mat_specular[] = {0.5, 0.5, 0.5, 1.0};
709
710     static float mat_diffuse[] = {0.5, 0.5, 0.5, 1.0};
711
712     glMaterialfv(GL_FRONT, GL_SHININESS, front_mat_shininess);
713     glMaterialfv(GL_FRONT, GL_SPECULAR, front_mat_specular);
714
715     glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, mat_diffuse);
716
717     glLightfv(GL_LIGHT0, GL_AMBIENT, ambientA);
718     glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseA);
719     glLightfv(GL_LIGHT0, GL_POSITION, positionA);
720     glEnable(GL_LIGHTING);
721     glEnable(GL_LIGHT0);
722     glLightModelf(GL_LIGHT_MODEL_TWO_SIDE,1);
723
724     glEnable(GL_NORMALIZE);         /* would it be faster ...   */
725     glEnable(GL_COLOR_MATERIAL);
726     glColorMaterial(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE);
727 }
728
729 /************************************/
730 /* draw implementations             */
731 /* somewhat inefficient since they  */
732 /* all calculate previously         */
733 /* calculated values again          */
734 /* storing the values in an array   */
735 /* is a posibility                  */
736 /************************************/
737 void displayTexture(void)
738 {
739     static double time = 0.0;
740     static double anglex = 0.0;
741     static double angley = 0.0;
742     static double anglez = 0.0;
743
744     double x,y,u,v;
745     double z;
746     double dx = 2.0/((double)_squares);
747     double dy = 2.0/((double)_squares);
748
749     double du = 2.0/((double)_squares);
750     double dv = 2.0/((double)_squares);
751
752     double xs = gflux->tex_xscale;
753     double ys = gflux->tex_yscale;
754
755         glMatrixMode (GL_TEXTURE);
756         glLoadIdentity ();
757         glTranslatef(-1,-1,0);
758         glScalef(0.5,0.5,1);
759         glMatrixMode (GL_MODELVIEW);
760
761     glLoadIdentity();
762     glRotatef(anglex,1,0,0);
763     glRotatef(angley,0,1,0);
764     glRotatef(anglez,0,0,1);
765     userRot();
766     glScalef(1,1,(GLfloat)_waveHeight);
767     glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
768         glEnable(GL_TEXTURE_2D);
769
770     clear_gl_error();
771         glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
772         glBindTexture(GL_TEXTURE_2D, gflux->texName);
773     check_gl_error("texture binding");
774
775         glColor3f(0.5,0.5,0.5);
776  
777     for(x=-1,u= 0;x<0.9999;x+=dx,u+=du) {
778         glBegin(GL_QUAD_STRIP);
779         for(y=-1,v= 0;y<=1;y+=dy,v+=dv) {
780             z = getGrid(x,y,time);
781         /*  genColour(z);
782             glColor3fv(gflux->colour);
783         */  glTexCoord2f(u*xs,v*ys);
784             glNormal3f(
785                 getGrid(x+dx,y,time)-getGrid(x-dx,y,time),
786                 getGrid(x,y+dy,time)-getGrid(x,y-dy,time),
787                 1
788             );
789             glVertex3f(x,y,z);
790
791             z = getGrid(x+dx,y,time);
792         /*  genColour(z);
793             glColor3fv(gflux->colour);
794         */  glTexCoord2f((u+du)*xs,v*ys);
795             glNormal3f(
796                 getGrid(x+dx+dx,y,time)-getGrid(x,y,time),
797                 getGrid(x+dx,y+dy,time)-getGrid(x+dx,y-dy,time),
798                 1
799             );
800             glVertex3f(x+dx,y,z);
801         }
802         glEnd();
803     }
804
805     if (! button_down_p) {
806       time -= _speed;
807       anglex -= _rotationx;
808       angley -= _rotationy;
809       anglez -= _rotationz;
810     }
811 }
812 void displaySolid(void)
813 {
814     static double time = 0.0;
815     static double anglex = 0.0;
816     static double angley = 0.0;
817     static double anglez = 0.0;
818
819     double x,y;
820     double z;
821     double dx = 2.0/((double)_squares);
822     double dy = 2.0/((double)_squares);
823
824     glLoadIdentity();
825     glRotatef(anglex,1,0,0);
826     glRotatef(angley,0,1,0);
827     glRotatef(anglez,0,0,1);
828     userRot();
829     glScalef(1,1,(GLfloat)_waveHeight);
830     glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
831
832     for(x=-1;x<0.9999;x+=dx) {
833         glBegin(GL_QUAD_STRIP);
834         for(y=-1;y<=1;y+=dy) {
835             z = getGrid(x,y,time);
836             genColour(z);
837             glColor3fv(gflux->colour);
838             glVertex3f(x,y,z);
839
840             z = getGrid(x+dx,y,time);
841             genColour(z);
842             glColor3fv(gflux->colour);
843             glVertex3f(x+dx,y,z);
844         }
845         glEnd();
846     }
847
848     if (! button_down_p) {
849       time -= _speed;
850       anglex -= _rotationx;
851       angley -= _rotationy;
852       anglez -= _rotationz;
853     }
854
855 }
856
857 void displayLight(void)
858 {
859     static double time = 0.0;
860     static double anglex = 0.0;
861     static double angley = 0.0;
862     static double anglez = 0.0;
863
864     double x,y;
865     double z;
866     double dx = 2.0/((double)_squares);
867     double dy = 2.0/((double)_squares);
868
869     glLoadIdentity();
870     glRotatef(anglex,1,0,0);
871     glRotatef(angley,0,1,0);
872     glRotatef(anglez,0,0,1);
873     userRot();
874     glScalef(1,1,(GLfloat)_waveHeight);
875     glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
876
877     for(x=-1;x<0.9999;x+=dx) {
878         glBegin(GL_QUAD_STRIP);
879         for(y=-1;y<=1;y+=dy) {
880             z = getGrid(x,y,time);
881             genColour(z);
882             glColor3fv(gflux->colour);
883             glNormal3f(
884                 getGrid(x+dx,y,time)-getGrid(x-dx,y,time),
885                 getGrid(x,y+dy,time)-getGrid(x,y-dy,time),
886                 1
887             );
888             glVertex3f(x,y,z);
889
890             z = getGrid(x+dx,y,time);
891             genColour(z);
892             glColor3fv(gflux->colour);
893             glNormal3f(
894                 getGrid(x+dx+dx,y,time)-getGrid(x,y,time),
895                 getGrid(x+dx,y+dy,time)-getGrid(x+dx,y-dy,time),
896                 1
897             );
898             glVertex3f(x+dx,y,z);
899         }
900         glEnd();
901     }
902
903     if (! button_down_p) {
904       time -= _speed;
905       anglex -= _rotationx;
906       angley -= _rotationy;
907       anglez -= _rotationz;
908     }
909 }
910
911 void displayWire(void)
912 {
913     static double time = 0.0;
914     static double anglex = 0.0;
915     static double angley = 0.0;
916     static double anglez = 0.0;
917
918     double x,y;
919     double z;
920     double dx1 = 2.0/((double)(_squares*_resolution)) - 0.00001;
921     double dy1 = 2.0/((double)(_squares*_resolution)) - 0.00001;
922     double dx2 = 2.0/((double)_squares) - 0.00001;
923     double dy2 = 2.0/((double)_squares) - 0.00001;
924
925     glLoadIdentity();
926     glRotatef(anglex,1,0,0);
927     glRotatef(angley,0,1,0);
928     glRotatef(anglez,0,0,1);
929     userRot();
930     glScalef(1,1,(GLfloat)_waveHeight);
931     glClear(GL_COLOR_BUFFER_BIT);
932
933     for(x=-1;x<=1;x+=dx2) {
934         glBegin(GL_LINE_STRIP);
935         for(y=-1;y<=1;y+=dy1) {
936             z = getGrid(x,y,time);
937             genColour(z);
938             glColor3fv(gflux->colour);
939             glVertex3f(x,y,z);
940         }
941         glEnd();
942     }
943     for(y=-1;y<=1;y+=dy2) {
944         glBegin(GL_LINE_STRIP);
945         for(x=-1;x<=1;x+=dx1) {
946             z = getGrid(x,y,time);
947             genColour(z);
948             glColor3fv(gflux->colour);
949             glVertex3f(x,y,z);
950         }
951         glEnd();
952     }
953
954     if (! button_down_p) {
955       time -= _speed;
956       anglex -= _rotationx;
957       angley -= _rotationy;
958       anglez -= _rotationz;
959     }
960 }
961
962 /* generates new ripples */
963 void calcGrid(void)
964 {
965     static int counter=0;
966     double tmp;
967     static int newWave;
968
969     if (button_down_p) return;
970
971     tmp = 1.0/((double)_waveChange);
972     if(!(counter%_waveChange)) {
973         newWave = ((int)(counter*tmp))%_waves;
974         gflux->dispx[newWave] = -frand(1.0);
975         gflux->dispy[newWave] = -frand(1.0);
976         gflux->freq[newWave] = _waveFreq * frand(1.0);
977         gflux->wa[newWave] = 0.0;
978     }
979     counter++;
980     gflux->wa[newWave] += tmp;
981     gflux->wa[(newWave+1)%_waves] -= tmp;
982 }
983
984 /* returns a height for the grid given time and x,y space co-ords */
985 double getGrid(double x, double y, double a)
986 {
987     register int i;
988     double retval=0.0;
989     double tmp;
990
991     tmp = 1.0/((float)_waves);
992     for(i=0;i<_waves;i++) {
993       retval += gflux->wa[i] * tmp * sin( gflux->freq[i]
994               * ( (x+gflux->dispx[i]) * (x+gflux->dispx[i]) 
995                 + (y+gflux->dispy[i]) * (y+gflux->dispy[i]) +a ) );
996     }
997     return(retval);
998 }
999
1000 #endif
1001