2c9fb3de2e517fc22c10ea19697878dcc053147c
[xscreensaver] / hacks / glx / gleidescope.c
1 /* -*- Mode: C; tab-width: 4 -*- */
2
3 #if !defined( lint ) && !defined( SABER )
4 static const char sccsid[] = "@(#)gleidescope.c 1.0 03/06/27 xlockmore";
5 #endif
6
7 /* enable -grab switch */
8 /*#define       GRAB*/
9
10 /*-
11  * Permission to use, copy, modify, and distribute this software and its
12  * documentation for any purpose and without fee is hereby granted,
13  * provided that the above copyright notice appear in all copies and that
14  * both that copyright notice and this permission notice appear in
15  * supporting documentation.
16  *
17  * This file is provided AS IS with no warranties of any kind.  The author
18  * shall have no liability with respect to the infringement of copyrights,
19  * trade secrets or any patents by this file or any part thereof.  In no
20  * event will the author be liable for any lost revenue or profits or
21  * other special, indirect and consequential damages.
22  *
23  *      Revision History:
24  *
25  *      20030627        1.0             acd             First Release.
26  *                                                              Texture loading code from 'glplanet'
27  *                                                                      by Jamie Zawinski <jwz@jwz.org>
28  *      20030810        1.1             acd             Added size flag.
29  *                                                              Now grabs screen / video / picture
30  *                                                                      (uses code from 'glslideshow' by
31  *                                                                      Mike Oliphant, Ben Buxton, Jamie Zawinski).
32  *                                                              Added -duration.
33  *                                                              Added mouse code.
34  *                                                              Added fade code (also from glslideshow).
35  *      20031013        1.2             acd             Migrated to compile without warnings under
36  *                                                                      xscreensaver 4.13.
37  *      20031023        1.3             acd             Better code to limit twisting speeds.
38  *                                                              Tweaked initial rotation values.
39  *                                                              Move, Rotate, Zoom now chosen at random if
40  *                                                                      no preference is given.
41  *                                                              Made grid slightly bigger so you can't see
42  *                                                                      the edge when zooming and moving.
43  */
44
45 #include <X11/Intrinsic.h>
46 #include "colors.h"
47
48 #include "xpm-ximage.h"
49
50 /*
51 **----------------------------------------------------------------------------
52 ** Defines
53 **----------------------------------------------------------------------------
54 */
55
56 #ifdef STANDALONE
57 # define PROGCLASS                              "gleidescope"
58 # define HACK_INIT                              init_gleidescope
59 # define HACK_DRAW                              draw_gleidescope
60 # define HACK_RESHAPE                   reshape_gleidescope
61 # define HACK_HANDLE_EVENT              gleidescope_handle_event
62 # define EVENT_MASK                             PointerMotionMask
63 # define gleidescope_opts               xlockmore_opts
64 # define DEFAULTS \
65                 "*delay:                20000           \n"     \
66                 "*showFPS:              False           \n"     \
67                 "*move:                 False           \n"     \
68                 "*rotate:               False           \n"     \
69                 "*zoom:                 False           \n"     \
70                 "*image:                DEFAULT         \n"     \
71                 "*size:                 -1                      \n"     \
72                 "*duration:             30                      \n" \
73
74 # include "xlockmore.h"                         /* from the xscreensaver distribution */
75 #else  /* !STANDALONE */
76 # include "xlock.h"                                     /* from the xlockmore distribution */
77 #endif /* !STANDALONE */
78
79 #ifdef USE_GL
80
81 #include <GL/glu.h>
82
83 /* acd TODO should all these be in gleidestruct? */
84 #ifdef GRAB
85 static Bool             grab;                   /* grab images */
86 #endif
87 static Bool             move;                   /* moving camera */
88 static Bool             nomove;                 /* no moving camera */
89 static Bool             rotate;                 /* rotate in place */
90 static Bool             norotate;               /* no rotate in place */
91 static int              size = -1;              /* size */
92 static Bool             zoom;                   /* zooming camera */
93 static Bool             nozoom;                 /* no zooming camera */
94 static char             *image;                 /* name of texture to load */
95 static int              duration;               /* length of time to display grabbed image */
96
97 #define MAX_TANGLE_VEL  2.0
98
99 static float    tangle = 0;                     /* texture angle */
100 static float    tangle_vel = 0.0;       /* texture velocity */
101 static float    tangle_acc = 0.0;       /* texture acceleration */
102
103 #define MAX_RANGLE_VEL  1.5
104
105 static float    rangle = 0;                     /* rotate angle */
106 static float    rangle_vel = 0.0;       /* rotate velocity */
107 static float    rangle_acc = 0.0;       /* rotate acceleration */
108
109 static XrmOptionDescRec opts[] =
110 {
111 #ifdef GRAB
112         {"-grab",               ".gleidescope.grab",            XrmoptionNoArg,         "true"},
113 #endif
114         {"-move",               ".gleidescope.move",            XrmoptionNoArg,         "true"},
115         {"-no-move",    ".gleidescope.nomove",          XrmoptionNoArg,         "true"},
116         {"-rotate",             ".gleidescope.rotate",          XrmoptionNoArg,         "true"},
117         {"-no-rotate",  ".gleidescope.norotate",        XrmoptionNoArg,         "true"},
118         /*{"-size",             ".gleidescope.size",            XrmoptionNoArg,         "-1"},*/
119         {"-zoom",               ".gleidescope.zoom",            XrmoptionNoArg,         "true"},
120         {"-no-zoom",    ".gleidescope.nozoom",          XrmoptionNoArg,         "true"},
121         {"-image",              ".gleidescope.image",           XrmoptionSepArg,        "DEFAULT"},
122         {"-duration",   ".gleidescope.duration",        XrmoptionSepArg,        "30"},
123 };
124
125
126 static argtype vars[] = {
127 #ifdef GRAB
128         {&grab,                 "grab",         "Grab",         "False",        t_Bool},
129 #endif
130         {&move,                 "move",         "Move",         "False",        t_Bool},
131         {&nomove,               "nomove",       "noMove",       "False",        t_Bool},
132         {&rotate,               "rotate",       "Rotate",       "False",        t_Bool},
133         {&norotate,             "norotate",     "noRotate",     "False",        t_Bool},
134         /*{&size,               "size",         "Size",         "-1",           t_Int},*/
135         {&zoom,                 "zoom",         "Zoom",         "False",        t_Bool},
136         {&nozoom,               "nozoom",       "noZoom",       "False",        t_Bool},
137         {&image,                "image",        "Image",        "DEFAULT",      t_String},
138         {&duration,             "duration",     "Duration",     "30",           t_Int},
139 };
140
141 static OptionStruct desc[] = {
142 #ifdef GRAB
143         {"-grab",               "grab images to create animation"},
144 #endif
145         {"-move",               "camera will move"},
146         {"-no-move",    "camera won't move"},
147         {"-rotate",             "camera will rotate"},
148         {"-no-rotate",  "camera won't rotate"},
149         /*{"-size",             "size of the hexagons (1-10)"},*/
150         {"-zoom",               "camera will zoom"},
151         {"-no-zoom",    "camera won't zoom"},
152         {"-image",              "xpm / xbm image file to use for texture"},
153         {"-duration",   "length of time texture will be used"},
154 };
155
156 ModeSpecOpt gleidescope_opts = {
157         sizeof opts / sizeof opts[0], opts,
158         sizeof vars / sizeof vars[0], vars,
159         desc
160 };
161
162 #ifdef USE_MODULES
163 ModStruct   gleidescope_description = { 
164      "gleidescope", "init_gleidescope", "draw_gleidescope", "release_gleidescope",
165      "draw_gleidescope", "init_gleidescope", NULL, &gleidescope_opts,
166      1000, 1, 2, 1, 4, 1.0, "",
167      "GL Kaleidescope", 0, NULL};
168 #endif
169
170 /*
171 **-----------------------------------------------------------------------------
172 **      Typedefs
173 **-----------------------------------------------------------------------------
174 */
175
176 typedef struct hex_s {
177         GLfloat x, y, z;                /* position */
178 } hex_t;
179
180 typedef struct {
181         GLfloat x;
182         GLfloat y;
183         GLfloat z;
184 } vectorf;
185
186 #define MAX_FADE        500     /* number of fade cycles */
187
188 typedef struct {
189         float                   cam_x_speed, cam_z_speed, cam_y_speed;
190         int                             cam_x_phase, cam_z_phase, cam_y_phase;
191         float                   tic;
192         GLXContext              *glx_context;
193         Window                  window;
194         GLfloat                 max_tx, max_ty; /* maximum texture sizes */
195         GLuint                  textures[2];    /* texture handles */
196         GLuint                  visible;                /* texture handle for new texture */
197         GLint                   fade;
198         time_t                  start_time;
199         Bool                    button_down_p;
200 } gleidestruct;
201
202 #define XOFFSET (0.8660254f)    /* sin 60' */
203 #define YOFFSET (1.5000000f)    /* cos 60' + 1 */
204
205 #if 0
206
207 #define SIZE    3
208
209 /* generates a grid with edges of given size */
210 /* acd TODO - replace hex[] with this and allow size and distance as parameters */
211
212 int
213 generate_grid(int size)
214
215         int     i, x, y;
216
217         size--;
218
219         i = size;
220         for (y = -size ; y <= size ; y++) {
221                 for (x = -i ; x <= i ; x += 2) {
222                         printf("{XOFFSET * %d, YOFFSET * %d, 0},\n", x, y);
223                 }
224                 printf("\n");
225                 if (y < 0) {
226                         i++;
227                 } else {
228                         i--;
229                 }
230         }
231         return 0;
232 }
233 #endif
234
235 hex_t hex[] = {
236         /* edges of size 7 */
237         /* number of hexagons required to cover screen depends on camera distance */
238         /* at a distance of 10 this is just about enough. */
239         {XOFFSET * -6, YOFFSET * -6, 0},
240         {XOFFSET * -4, YOFFSET * -6, 0},
241         {XOFFSET * -2, YOFFSET * -6, 0},
242         {XOFFSET * 0, YOFFSET * -6, 0},
243         {XOFFSET * 2, YOFFSET * -6, 0},
244         {XOFFSET * 4, YOFFSET * -6, 0},
245         {XOFFSET * 6, YOFFSET * -6, 0},
246
247         {XOFFSET * -7, YOFFSET * -5, 0},
248         {XOFFSET * -5, YOFFSET * -5, 0},
249         {XOFFSET * -3, YOFFSET * -5, 0},
250         {XOFFSET * -1, YOFFSET * -5, 0},
251         {XOFFSET * 1, YOFFSET * -5, 0},
252         {XOFFSET * 3, YOFFSET * -5, 0},
253         {XOFFSET * 5, YOFFSET * -5, 0},
254         {XOFFSET * 7, YOFFSET * -5, 0},
255
256         {XOFFSET * -8, YOFFSET * -4, 0},
257         {XOFFSET * -6, YOFFSET * -4, 0},
258         {XOFFSET * -4, YOFFSET * -4, 0},
259         {XOFFSET * -2, YOFFSET * -4, 0},
260         {XOFFSET * 0, YOFFSET * -4, 0},
261         {XOFFSET * 2, YOFFSET * -4, 0},
262         {XOFFSET * 4, YOFFSET * -4, 0},
263         {XOFFSET * 6, YOFFSET * -4, 0},
264         {XOFFSET * 8, YOFFSET * -4, 0},
265
266         {XOFFSET * -9, YOFFSET * -3, 0},
267         {XOFFSET * -7, YOFFSET * -3, 0},
268         {XOFFSET * -5, YOFFSET * -3, 0},
269         {XOFFSET * -3, YOFFSET * -3, 0},
270         {XOFFSET * -1, YOFFSET * -3, 0},
271         {XOFFSET * 1, YOFFSET * -3, 0},
272         {XOFFSET * 3, YOFFSET * -3, 0},
273         {XOFFSET * 5, YOFFSET * -3, 0},
274         {XOFFSET * 7, YOFFSET * -3, 0},
275         {XOFFSET * 9, YOFFSET * -3, 0},
276
277         {XOFFSET * -10, YOFFSET * -2, 0},
278         {XOFFSET * -8, YOFFSET * -2, 0},
279         {XOFFSET * -6, YOFFSET * -2, 0},
280         {XOFFSET * -4, YOFFSET * -2, 0},
281         {XOFFSET * -2, YOFFSET * -2, 0},
282         {XOFFSET * 0, YOFFSET * -2, 0},
283         {XOFFSET * 2, YOFFSET * -2, 0},
284         {XOFFSET * 4, YOFFSET * -2, 0},
285         {XOFFSET * 6, YOFFSET * -2, 0},
286         {XOFFSET * 8, YOFFSET * -2, 0},
287         {XOFFSET * 10, YOFFSET * -2, 0},
288
289         {XOFFSET * -11, YOFFSET * -1, 0},
290         {XOFFSET * -9, YOFFSET * -1, 0},
291         {XOFFSET * -7, YOFFSET * -1, 0},
292         {XOFFSET * -5, YOFFSET * -1, 0},
293         {XOFFSET * -3, YOFFSET * -1, 0},
294         {XOFFSET * -1, YOFFSET * -1, 0},
295         {XOFFSET * 1, YOFFSET * -1, 0},
296         {XOFFSET * 3, YOFFSET * -1, 0},
297         {XOFFSET * 5, YOFFSET * -1, 0},
298         {XOFFSET * 7, YOFFSET * -1, 0},
299         {XOFFSET * 9, YOFFSET * -1, 0},
300         {XOFFSET * 11, YOFFSET * -1, 0},
301
302         {XOFFSET * -12, YOFFSET * 0, 0},
303         {XOFFSET * -10, YOFFSET * 0, 0},
304         {XOFFSET * -8, YOFFSET * 0, 0},
305         {XOFFSET * -6, YOFFSET * 0, 0},
306         {XOFFSET * -4, YOFFSET * 0, 0},
307         {XOFFSET * -2, YOFFSET * 0, 0},
308         {XOFFSET * 0, YOFFSET * 0, 0},
309         {XOFFSET * 2, YOFFSET * 0, 0},
310         {XOFFSET * 4, YOFFSET * 0, 0},
311         {XOFFSET * 6, YOFFSET * 0, 0},
312         {XOFFSET * 8, YOFFSET * 0, 0},
313         {XOFFSET * 10, YOFFSET * 0, 0},
314         {XOFFSET * 12, YOFFSET * 0, 0},
315
316         {XOFFSET * -11, YOFFSET * 1, 0},
317         {XOFFSET * -9, YOFFSET * 1, 0},
318         {XOFFSET * -7, YOFFSET * 1, 0},
319         {XOFFSET * -5, YOFFSET * 1, 0},
320         {XOFFSET * -3, YOFFSET * 1, 0},
321         {XOFFSET * -1, YOFFSET * 1, 0},
322         {XOFFSET * 1, YOFFSET * 1, 0},
323         {XOFFSET * 3, YOFFSET * 1, 0},
324         {XOFFSET * 5, YOFFSET * 1, 0},
325         {XOFFSET * 7, YOFFSET * 1, 0},
326         {XOFFSET * 9, YOFFSET * 1, 0},
327         {XOFFSET * 11, YOFFSET * 1, 0},
328
329         {XOFFSET * -10, YOFFSET * 2, 0},
330         {XOFFSET * -8, YOFFSET * 2, 0},
331         {XOFFSET * -6, YOFFSET * 2, 0},
332         {XOFFSET * -4, YOFFSET * 2, 0},
333         {XOFFSET * -2, YOFFSET * 2, 0},
334         {XOFFSET * 0, YOFFSET * 2, 0},
335         {XOFFSET * 2, YOFFSET * 2, 0},
336         {XOFFSET * 4, YOFFSET * 2, 0},
337         {XOFFSET * 6, YOFFSET * 2, 0},
338         {XOFFSET * 8, YOFFSET * 2, 0},
339         {XOFFSET * 10, YOFFSET * 2, 0},
340
341         {XOFFSET * -9, YOFFSET * 3, 0},
342         {XOFFSET * -7, YOFFSET * 3, 0},
343         {XOFFSET * -5, YOFFSET * 3, 0},
344         {XOFFSET * -3, YOFFSET * 3, 0},
345         {XOFFSET * -1, YOFFSET * 3, 0},
346         {XOFFSET * 1, YOFFSET * 3, 0},
347         {XOFFSET * 3, YOFFSET * 3, 0},
348         {XOFFSET * 5, YOFFSET * 3, 0},
349         {XOFFSET * 7, YOFFSET * 3, 0},
350         {XOFFSET * 9, YOFFSET * 3, 0},
351
352         {XOFFSET * -8, YOFFSET * 4, 0},
353         {XOFFSET * -6, YOFFSET * 4, 0},
354         {XOFFSET * -4, YOFFSET * 4, 0},
355         {XOFFSET * -2, YOFFSET * 4, 0},
356         {XOFFSET * 0, YOFFSET * 4, 0},
357         {XOFFSET * 2, YOFFSET * 4, 0},
358         {XOFFSET * 4, YOFFSET * 4, 0},
359         {XOFFSET * 6, YOFFSET * 4, 0},
360         {XOFFSET * 8, YOFFSET * 4, 0},
361
362         {XOFFSET * -7, YOFFSET * 5, 0},
363         {XOFFSET * -5, YOFFSET * 5, 0},
364         {XOFFSET * -3, YOFFSET * 5, 0},
365         {XOFFSET * -1, YOFFSET * 5, 0},
366         {XOFFSET * 1, YOFFSET * 5, 0},
367         {XOFFSET * 3, YOFFSET * 5, 0},
368         {XOFFSET * 5, YOFFSET * 5, 0},
369         {XOFFSET * 7, YOFFSET * 5, 0},
370
371         {XOFFSET * -6, YOFFSET * 6, 0},
372         {XOFFSET * -4, YOFFSET * 6, 0},
373         {XOFFSET * -2, YOFFSET * 6, 0},
374         {XOFFSET * 0, YOFFSET * 6, 0},
375         {XOFFSET * 2, YOFFSET * 6, 0},
376         {XOFFSET * 4, YOFFSET * 6, 0},
377         {XOFFSET * 6, YOFFSET * 6, 0},
378 };
379
380 /*
381 **----------------------------------------------------------------------------
382 ** Local Variables
383 **----------------------------------------------------------------------------
384 */
385
386 static  gleidestruct *gleidescope = NULL;
387
388 /*
389  *load defaults in config structure
390  */
391 void setdefaultconfig(void)
392 {
393 #ifdef GRAB
394         grab = False;
395 #endif
396         move = False;
397         rotate = False;
398         zoom = False;
399         image = NULL;
400 }
401
402 static int xstart;
403 static int ystart;
404 static double xmouse = 0.0;
405 static double ymouse = 0.0;
406
407 Bool
408 gleidescope_handle_event(ModeInfo *mi, XEvent *event)
409 {
410         gleidestruct *gp = &gleidescope[MI_SCREEN(mi)];
411
412         /*
413         printf("event:%d\n", event->xany.type);
414         printf("button:%d\n", event->xbutton.button);
415         */
416         switch(event->xany.type)
417         {
418                 case ButtonPress:
419
420                         if (event->xbutton.button == Button1 || event->xbutton.button == Button3)
421                         {
422                                 /* store initial values of mouse */
423                                 xstart = event->xbutton.x;
424                                 ystart = event->xbutton.y;
425
426                                 /* button is down */
427                                 gp->button_down_p = True;
428                                 return True;
429                         }
430 #if 0   /* TODO */
431                         else if (event->xbutton.button == Button4)
432                         {
433                                 /* zoom in */
434                                 return True;
435                         }
436                         else if (event->xbutton.button == Button5)
437                         {
438                                 /* zoom out */
439                                 return True;
440                         }
441 #endif
442                         break;
443
444                 case ButtonRelease:
445
446                         if (event->xbutton.button == Button1 || event->xbutton.button == Button3)
447                         {
448                                 /* button is up */
449                                 gp->button_down_p = False;
450                                 return True;
451                         }
452                         break;
453
454                 case MotionNotify:
455
456                         if (gp->button_down_p)
457                         {
458                                 /* update mouse position */
459                                 xmouse += (double)(event->xmotion.x - xstart) / MI_WIDTH(mi);
460                                 ymouse += (double)(event->xmotion.y - ystart) / MI_HEIGHT(mi);
461                                 xstart = event->xmotion.x;
462                                 ystart = event->xmotion.y;
463
464                                 return True;
465                         }
466                         break;
467         }
468
469         return False;
470 }
471
472 #include "grab-ximage.h"
473
474 static void
475 getSnapshot(ModeInfo *mi, GLuint name)
476 {
477         XImage  *ximage;
478         int     status;
479         int     tw, th;
480         gleidestruct *gp = &gleidescope[MI_SCREEN(mi)];
481
482         if (MI_IS_WIREFRAME(mi))
483                 return;
484
485         ximage = screen_to_ximage(mi->xgwa.screen, mi->window, 0);
486
487         tw = mi->xgwa.width;
488         th = mi->xgwa.height;
489
490         gp->max_tx = (GLfloat) tw / (GLfloat) ximage->width;
491         gp->max_ty = (GLfloat) th / (GLfloat) ximage->height;
492
493         glBindTexture (GL_TEXTURE_2D, name);
494
495         glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
496         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
497                         GL_LINEAR_MIPMAP_LINEAR);
498
499         clear_gl_error();
500         status = gluBuild2DMipmaps(GL_TEXTURE_2D, 3,
501                         ximage->width, ximage->height,
502                         GL_RGBA, GL_UNSIGNED_BYTE, ximage->data);
503
504         if (!status && glGetError())
505                 /* Some implementations of gluBuild2DMipmaps(), but set a GL error anyway.
506                 **            We could just call check_gl_error(), but that would exit. */
507                 status = -1;
508
509         if (status)
510         {
511                 const GLubyte *s = gluErrorString (status);
512                 if (s)
513                 {
514                         fprintf (stderr, "%s: error mipmapping %dx%d texture: %s\n",
515                                         progname, ximage->width, ximage->height, s);
516                 }
517                 else
518                 {
519                         fprintf (stderr, "%s: error mipmapping %dx%d texture: (unknown)\n",
520                                         progname, ximage->width, ximage->height);
521                 }
522                 clear_gl_error();
523         }
524         check_gl_error("mipmapping");  /* should get a return code instead of a
525                                                                           GL error, but just in case... */
526
527         free(ximage->data);
528         ximage->data = 0;
529         XDestroyImage (ximage);
530
531         /* remember time of last image change */
532         gp->start_time = time ((time_t *) 0);
533 }
534
535 static void
536 setup_file_texture (ModeInfo *mi, char *filename, GLuint name)
537 {
538         Display *dpy = mi->dpy;
539         Visual *visual = mi->xgwa.visual;
540         char buf[1024];
541
542         Colormap cmap = mi->xgwa.colormap;
543         XImage *image = xpm_file_to_ximage (dpy, visual, cmap, filename);
544
545         /* use this texture */
546         glBindTexture(GL_TEXTURE_2D, name);
547
548         clear_gl_error();
549         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
550                         image->width, image->height, 0,
551                         GL_RGBA, GL_UNSIGNED_BYTE, image->data);
552         sprintf (buf, "texture: %.100s (%dx%d)",
553                         filename, image->width, image->height);
554         check_gl_error(buf);
555
556         /* setup parameters for texturing */
557         glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
558         glPixelStorei(GL_UNPACK_ROW_LENGTH, image->width);
559
560         glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
561         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
562         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
563         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
564         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
565 }
566
567 static void
568 setup_texture(ModeInfo * mi, GLuint id)
569 {
570         gleidestruct *gp = &gleidescope[MI_SCREEN(mi)];
571
572         if (!image || !*image || !strcmp(image, "DEFAULT")) {
573                 /* no image specified - grab screen */
574                 getSnapshot(mi, id);
575                 /* max_tx and max_ty set in getSnapshot() */
576         } else {
577                 /* use supplied image file */
578                 setup_file_texture(mi, image, id);
579
580                 /* set tx params to use whole image */
581                 gp->max_tx = 1.0f;
582                 gp->max_ty = 1.0f;
583         }
584
585         check_gl_error("texture initialization");
586
587         /* Need to flip the texture top for bottom for some reason. */
588         glMatrixMode (GL_TEXTURE);
589         glScalef (1, -1, 1);
590         glMatrixMode (GL_MODELVIEW);
591 }
592
593 #define VERTEX0 glVertex3f( 0.0000f,  0.000f, 0.0f);
594 #define VERTEX1 glVertex3f( 0.0000f,  1.000f, 0.0f);
595 #define VERTEX2 glVertex3f( XOFFSET,  0.500f, 0.0f);
596 #define VERTEX3 glVertex3f( XOFFSET, -0.500f, 0.0f);
597 #define VERTEX4 glVertex3f( 0.0000f, -1.000f, 0.0f);
598 #define VERTEX5 glVertex3f(-XOFFSET, -0.500f, 0.0f);
599 #define VERTEX6 glVertex3f(-XOFFSET,  0.500f, 0.0f);
600
601 static void
602 draw_hexagons(ModeInfo *mi, int translucency, GLuint texture)
603 {
604         int             i;
605         GLfloat col[4];
606         GLfloat t1x, t1y, t2x, t2y, t3x, t3y;
607         gleidestruct *gp = &gleidescope[MI_SCREEN(mi)];
608         GLfloat tangle2;
609
610         col[0] = 1.0;
611         col[1] = 1.0;
612         col[2] = 1.0;
613         col[3] = (float)translucency / MAX_FADE;
614
615         /* calculate vertices of equilateral triangle within image. */
616         /* t1 is always in centre */
617         t1x = gp->max_tx / 2;
618         t1y = gp->max_ty / 2;
619         /* t2 rotates */
620         t2x = (gp->max_tx / 2) * (1 + cos((ymouse * 2 * M_PI) + (tangle * M_PI / 180)));
621         t2y = (gp->max_ty / 2) * (1 + sin((ymouse * 2 * M_PI) + (tangle * M_PI / 180)));
622         /* t3 is always 60' further around than t2 */
623         tangle2 = (ymouse * 2 * M_PI) + (tangle * M_PI / 180) + (M_PI * 2 / 6);
624         t3x = (gp->max_tx / 2) * (1 + (cos(tangle2)));
625         t3y = (gp->max_ty / 2) * (1 + (sin(tangle2)));
626         /* NB image is flipped vertically hence: */
627         t1y = 1 - t1y;
628         t2y = 1 - t2y;
629         t3y = 1 - t3y;
630         /*printf("texcoords:[%f,%f]->[%f,%f](%f,%f)\n", t1x, t1y, t2x, t2y, gp->max_tx, gp->max_ty);*/
631
632         glColor4f(1.0, 1.0, 1.0, (float)translucency / MAX_FADE);
633         glEnable(GL_TEXTURE_2D);
634         glEnable(GL_BLEND);
635         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
636         glDepthMask(GL_FALSE);
637         glBindTexture(GL_TEXTURE_2D, gp->textures[texture]);
638
639         for (i = 0 ; i < sizeof(hex) / sizeof(hex[0]) ; i++) {
640
641                 glPushMatrix();
642
643                 glTranslatef(hex[i].x, hex[i].y, 0.0);
644
645                 glBegin(GL_TRIANGLES);
646
647                 /*
648                 ** six triangles to each hexagon
649                 */
650
651                 glTexCoord2f(t1x, t1y);
652                 VERTEX0;
653                 glTexCoord2f(t2x, t2y);
654                 VERTEX1;
655                 glTexCoord2f(t3x, t3y);
656                 VERTEX6;
657
658                 glTexCoord2f(t1x, t1y);
659                 VERTEX0;
660                 glTexCoord2f(t3x, t3y);
661                 VERTEX6;
662                 glTexCoord2f(t2x, t2y);
663                 VERTEX5;
664
665                 glTexCoord2f(t1x, t1y);
666                 VERTEX0;
667                 glTexCoord2f(t2x, t2y);
668                 VERTEX5;
669                 glTexCoord2f(t3x, t3y);
670                 VERTEX4;
671
672                 glTexCoord2f(t1x, t1y);
673                 VERTEX0;
674                 glTexCoord2f(t3x, t3y);
675                 VERTEX4;
676                 glTexCoord2f(t2x, t2y);
677                 VERTEX3;
678
679                 glTexCoord2f(t1x, t1y);
680                 VERTEX0;
681                 glTexCoord2f(t2x, t2y);
682                 VERTEX3;
683                 glTexCoord2f(t3x, t3y);
684                 VERTEX2;
685
686                 glTexCoord2f(t1x, t1y);
687                 VERTEX0;
688                 glTexCoord2f(t3x, t3y);
689                 VERTEX2;
690                 glTexCoord2f(t2x, t2y);
691                 VERTEX1;
692
693                 glEnd();
694
695                 glPopMatrix();
696         }
697         
698 #ifdef DISPLAY_TEXTURE
699         glPushMatrix();
700         /* acd debug - display (bigger, centred) texture */
701         glScalef(2.0, 2.0, 2.0);
702         glTranslatef(-0.5, -0.5, 0.0);
703         glBegin(GL_QUADS);
704         glTexCoord2f(0.0, 0.0);
705         glVertex3f(0.0, 0.0, -0.1);
706         glTexCoord2f(1.0, 0.0);
707         glVertex3f(1.0, 0.0, -0.1);
708         glTexCoord2f(1.0, 1.0);
709         glVertex3f(1.0, 1.0, -0.1);
710         glTexCoord2f(0.0, 1.0);
711         glVertex3f(0.0, 1.0, -0.1);
712         glEnd();
713         /* acd debug - display texture triangle */
714         glColor4f(1.0, 1.0, 1.0, 1.0);
715         glBegin(GL_LINE_LOOP);
716         glVertex3f(t1x, t1y, -0.11);
717         glVertex3f(t2x, t2y, -0.11);
718         glVertex3f(t3x, t3y, -0.11);
719         glEnd();
720         glPopMatrix();
721 #endif
722
723         glDisable(GL_TEXTURE_2D);
724         glDepthMask(GL_TRUE);
725         glDisable(GL_BLEND);
726 }
727
728 /*
729  * main rendering loop
730  */
731 static void
732 draw(ModeInfo * mi)
733 {
734         GLfloat x_angle, y_angle, z_angle;
735         gleidestruct *gp = &gleidescope[MI_SCREEN(mi)];
736         vectorf v1;
737         GLfloat pos[4];
738
739         glClearColor(0.5, 0.5, 0.5, 1.0);
740         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
741         glLoadIdentity();
742
743         gp->tic += 0.005f;
744
745         x_angle = gp->cam_x_phase + gp->tic * gp->cam_x_speed;
746         y_angle = gp->cam_y_phase + gp->tic * gp->cam_y_speed;
747         z_angle = gp->cam_z_phase + gp->tic * gp->cam_z_speed;
748
749         if (move) {
750                 v1.x = 2 * sin(x_angle);
751                 v1.y = 2 * sin(y_angle);
752         } else {
753                 v1.x = 0;
754                 v1.y = 0;
755         }
756
757         /* size is changed in pinit() to be distance from plane */
758         size = MI_SIZE(mi);
759         if (size > 10) {
760                 size = 10;
761         }
762         if (size < -1) {
763                 size = -1;
764         }
765         if (size != -1) {
766                 /* user defined size */
767                 v1.z = size;
768         } else if (zoom) {
769                 /* max distance given by adding the constant and the multiplier */
770                 v1.z = 5.0 + 4.0 * sin(z_angle);
771         } else {
772                 /* default */
773                 v1.z = 7.0;
774         }
775
776         /* update rotation angle (but not if mouse button down) */
777         if (rotate && !gp->button_down_p)
778         {
779                 float   new_rangle_vel = 0.0;
780
781                 /* update camera rotation angle and velocity */
782                 rangle += rangle_vel;
783                 new_rangle_vel = rangle_vel + rangle_acc;
784                 if (new_rangle_vel > -MAX_RANGLE_VEL && new_rangle_vel < MAX_RANGLE_VEL)
785                 {
786                         /* new velocity is within limits */
787                         rangle_vel = new_rangle_vel;
788                 }
789
790                 /* randomly change twisting speed */
791                 if ((random() % 1000) < 1)
792                 {
793                         rangle_acc = frand(0.002) - 0.001;
794                 }
795         }
796
797 #ifdef WOBBLE
798         /* this makes the image wobble - requires -move and a larger grid */
799         gluLookAt(0, 0, v1.z, v1.x, v1.y, 0.0, 0.0, 1.0, 0.0);
800 #else
801         /* no wobble - camera always perpendicular to grid */
802
803         /* rotating camera rather than entire space - smoother */
804         gluLookAt(
805                         v1.x, v1.y, v1.z,
806                         v1.x, v1.y, 0.0,
807                         sin((xmouse * M_PI * 2) + rangle * M_PI / 180),
808                         cos((xmouse * M_PI * 2) + rangle * M_PI / 180),
809                         0.0);
810 #endif
811
812         /* light position same as camera */
813         pos[0] = v1.x;
814         pos[1] = v1.y;
815         pos[2] = v1.z;
816         pos[3] = 0;
817
818         if (gp->fade == 0)
819         {
820                 /* not fading */
821                 draw_hexagons(mi, MAX_FADE, gp->visible);
822         }
823         else
824         {
825                 /* fading - show both textures with alpha */
826                 draw_hexagons(mi, MAX_FADE - gp->fade, gp->visible);
827                 draw_hexagons(mi, gp->fade, 1 - gp->visible);
828
829                 /* fade some more */
830                 gp->fade++;
831
832                 /* have we faded enough? */
833                 if (gp->fade > MAX_FADE)
834                 {
835                         /* stop fading */
836                         gp->fade = 0;
837                         gp->visible = 1 - gp->visible;
838                 }
839         }
840
841         /* increment texture angle based on time, velocity etc */
842         /* but only if button is not down */
843         if (!gp->button_down_p)
844         {
845                 float           new_tangle_vel = 0.0;
846
847                 tangle += tangle_vel;
848
849                 /* work out new texture angle velocity */
850                 new_tangle_vel = tangle_vel + tangle_acc;
851                 if (new_tangle_vel > -MAX_TANGLE_VEL && new_tangle_vel < MAX_TANGLE_VEL)
852                 {
853                         /* new velocity is inside limits */
854                         tangle_vel = new_tangle_vel;
855                 }
856
857                 /* randomly change texture angle acceleration */
858                 if ((random() % 1000) < 1)
859                 {
860                         tangle_acc = frand(0.002) - 0.001;
861                 }
862         }
863
864         glFlush();
865 }
866
867 /* 
868  * new window size or exposure 
869  */
870 void reshape_gleidescope(ModeInfo *mi, int width, int height)
871 {
872         GLfloat         h = (GLfloat) height / (GLfloat) width;
873
874         glViewport(0, 0, (GLint) width, (GLint) height);
875         glMatrixMode(GL_PROJECTION);
876         glLoadIdentity();
877         gluPerspective(50.0, 1/h, 0.1, 2000.0);
878         glMatrixMode (GL_MODELVIEW);
879
880         glLineWidth(1);
881         glPointSize(1);   
882 }
883
884 static void
885 pinit(ModeInfo * mi)
886 {
887         gleidestruct    *gp = &gleidescope[MI_SCREEN(mi)];
888
889         /* set start time - star_time = 0 implies non-dynamic texture */
890         gp->start_time = (time_t)0;
891
892         /* set the texture size to default */
893         gp->max_tx = 1.0;
894         gp->max_ty = 1.0;
895
896         /* no fading */
897         gp->fade = 0;
898
899         glShadeModel(GL_SMOOTH);
900         glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
901         glEnable(GL_DEPTH_TEST);
902         glEnable(GL_CULL_FACE);
903         glDisable(GL_LIGHTING);
904
905         /* space for textures */
906         glGenTextures(1, &gp->textures[0]);
907         glGenTextures(1, &gp->textures[1]);
908         gp->visible = 0;
909
910         setup_texture(mi, gp->textures[gp->visible]);
911
912         /*
913         **      want to choose a value for arg randomly if neither -arg nor -no-arg
914         **      is specified. xscreensaver libraries don't seem to let you do this -
915         **      if something isn't true then it is false (pesky two-state boolean values).
916         **      so, i've defined both -arg and -no-arg to arguments and added the 
917         **      following logic.
918         **      (btw if both -arg and -no-arg are defined then arg is set to False)
919         */
920         if (zoom == False && nozoom == False)
921         {
922                 /* no zoom preference - randomise */
923                 zoom = (((random() & 0x1) == 0x1) ? True : False);
924         }
925         else if (nozoom == True)
926         {
927                 /* definately no zoom */
928                 zoom = False;
929         }
930
931         if (move == False && nomove == False)
932         {
933                 /* no move preference - randomise */
934                 move = (((random() & 0x1) == 0x1) ? True : False);
935         }
936         else if (nomove == True)
937         {
938                 /* definately no move */
939                 move = False;
940         }
941
942         if (rotate == False && norotate == False)
943         {
944                 /* no rotate preference - randomise */
945                 rotate = (((random() & 0x1) == 0x1) ? True : False);
946         }
947         else if (norotate == True)
948         {
949                 /* definately no rotate */
950                 rotate = False;
951         }
952
953         /* define cam variables */
954         gp->cam_x_speed = frand(3.0) - 1.5;
955         gp->cam_x_phase = random() % 360;
956         gp->cam_y_speed = frand(3.0) - 1.5;
957         gp->cam_y_phase = random() % 360;
958         gp->cam_z_speed = frand(3.0) - 1.5;
959         gp->cam_z_phase = random() % 360;
960
961         /* initial angular speeds */
962         rangle_vel = frand(0.2) - 0.1;
963         tangle_vel = frand(0.2) - 0.1;
964         rangle_acc = frand(0.002) - 0.001;
965         tangle_acc = frand(0.002) - 0.001;
966
967     /* jwz */
968     {
969       GLfloat speed = 15;
970       rangle_vel *= speed;
971       tangle_vel *= speed;
972       rangle_acc *= speed;
973       tangle_acc *= speed;
974     }
975
976         /* distance is 11 - size */
977         if (size != -1) {
978                 if (zoom) {
979                         fprintf(stderr, "-size given. ignoring -zoom.\n");
980                         zoom = False;
981                 }
982                 if (size < 1) {
983                         size = 1;
984                 } else if (size >= 10) {
985                         size = 10;
986                 }
987                 size = 11 - size;
988         }
989
990 #ifdef DEBUG
991 printf("phases [%d, %d, %d]\n", gp->cam_x_phase, gp->cam_y_phase, gp->cam_z_phase);
992 #endif
993 }
994
995 void
996 init_gleidescope(ModeInfo * mi)
997 {
998         gleidestruct *gp;
999         int screen = MI_SCREEN(mi);
1000
1001
1002         if (gleidescope == NULL) {
1003                 gleidescope = (gleidestruct *) calloc(MI_NUM_SCREENS(mi), sizeof (gleidestruct));
1004                 if (gleidescope == NULL) {
1005                         return;
1006                 }
1007         }
1008         gp = &gleidescope[screen];
1009         gp->window = MI_WINDOW(mi);
1010
1011         if ((gp->glx_context = init_GL(mi)) != NULL) {
1012
1013                 reshape_gleidescope(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
1014
1015                 glDrawBuffer(GL_BACK);
1016
1017                 /* do initialisation */
1018                 pinit(mi);
1019
1020         } else {
1021                 MI_CLEARWINDOW(mi);
1022         }
1023 }
1024
1025 void
1026 draw_gleidescope(ModeInfo * mi)
1027 {
1028         gleidestruct    *gp = &gleidescope[MI_SCREEN(mi)];
1029         Display         *display = MI_DISPLAY(mi);
1030         Window          window = MI_WINDOW(mi);
1031
1032
1033         if (!gp->glx_context)
1034                 return;
1035
1036         glDrawBuffer(GL_BACK);
1037
1038         glXMakeCurrent(display, window, *(gp->glx_context));
1039         draw(mi);
1040
1041         if (mi->fps_p) {
1042                 do_fps (mi);
1043         }
1044
1045         glFinish();
1046         glXSwapBuffers(display, window);
1047
1048 #ifdef GRAB
1049         if (grab) {
1050                 grab_frame(mi);
1051         }
1052 #endif
1053
1054         /* need to change texture? */
1055         if ((gp->start_time != 0) && (duration != -1) && gp->fade == 0) {
1056                 if (gp->start_time + duration <= time((time_t *)0)) {
1057                         /* get new snapshot (into back buffer) and start fade count */
1058                         getSnapshot(mi, gp->textures[1 - gp->visible]);
1059                         gp->fade = 1;
1060                 }
1061         }
1062 }
1063
1064 void
1065 release_gleidescope(ModeInfo * mi)
1066 {
1067         if (gleidescope != NULL) {
1068                 int screen;
1069
1070                 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
1071                         gleidestruct *gp = &gleidescope[screen];
1072
1073                         /* acd -  is this needed? */
1074                         if (gp->glx_context) {
1075                                 /* Display lists MUST be freed while their glXContext is current. */
1076                                 glXMakeCurrent(MI_DISPLAY(mi), gp->window, *(gp->glx_context));
1077
1078                                 /* acd - was code here for freeing things that are no longer in struct */
1079                         }
1080                 }
1081                 (void) free((void *) gleidescope);
1082                 gleidescope = NULL;
1083         }
1084         
1085         FreeAllGL(mi);
1086 }
1087
1088 #endif