From http://www.jwz.org/xscreensaver/xscreensaver-5.35.tar.gz
[xscreensaver] / hacks / glx / mirrorblob.c
1 /* mirrorblob  Copyright (c) 2003 Jon Dowdall <jon.dowdall@bigpond.com> */
2 /*
3  * Permission to use, copy, modify, and distribute this software and its
4  * documentation for any purpose and without fee is hereby granted,
5  * provided that the above copyright notice appear in all copies and that
6  * both that copyright notice and this permission notice appear in
7  * supporting documentation.
8  *
9  * This file is provided AS IS with no warranties of any kind.  The author
10  * shall have no liability with respect to the infringement of copyrights,
11  * trade secrets or any patents by this file or any part thereof.  In no
12  * event will the author be liable for any lost revenue or profits or
13  * other special, indirect and consequential damages.
14  *
15  * Revision History:
16  * 23-Sep-2003:  jon.dowdall@bigpond.com  Created module "blob"
17  * 19-Oct-2003:  jon.dowdall@bigpond.com  Added texturing
18  * 21-Oct-2003:                           Renamed to mirrorblob
19  * 10-Feb-2004:  jon.dowdall@bigpond.com  Added motion blur
20  * 28-Jan-2006:  jon.dowdall@bigpond.com  Big clean up and bug fixes
21  * 13-Apr-2009:  jon.dowdall@gmail.com    Fixed Mac version
22  *
23  * The mirrorblob screensaver draws a pulsing blob on the screen.  Options
24  * include adding a background (via screen_to_texture), texturing the blob,
25  * making the blob semi-transparent and varying the resolution of the blob
26  * tessellation.
27  *
28  * The blob was inspired by a lavalamp is in no way a simulation.  The code is
29  * just an attempt to generate some eye-candy.
30  *
31  * Much of xmirrorblob code framework is taken from the pulsar module by David
32  * Konerding and the glslideshow by Mike Oliphant and Jamie Zawinski.
33  *
34  */
35
36 #include <math.h>
37
38 #ifdef STANDALONE
39 #define DEFAULTS "*delay:             " DEF_DELAY "\n"                      \
40                  "*showFPS:           " DEF_FPS   "\n"                      \
41                  "*useSHM:              True       \n"                      \
42                  "*desktopGrabber:  xscreensaver-getimage -no-desktop %s\n" \
43                  "*grabDesktopImages:   True  \n"                           \
44                  "*chooseRandomImages:  True  \n"                           \
45                  "*suppressRotationAnimation: True\n"                       \
46
47 # define refresh_mirrorblob 0
48 # include "xlockmore.h"
49 #else /* !STANDALONE */
50 # include "xlock.h"        /* from the xlockmore distribution */
51 #endif /* !STANDALONE */
52
53 #ifdef USE_GL /* whole file */
54
55
56 #define DEF_DELAY            "10000"
57 #define DEF_FPS              "False"
58 #define DEF_WIRE             "False"
59 #define DEF_BLEND            "1.0"
60 #define DEF_FOG              "False"
61 #define DEF_ANTIALIAS        "False"
62 #define DEF_WALLS            "False"
63 #define DEF_COLOUR           "False"
64 #define DEF_ASYNC            "True"
65 #define DEF_TEXTURE          "True"
66 #define DEF_OFFSET_TEXTURE   "False"
67 #define DEF_PAINT_BACKGROUND "True"
68 #define DEF_RESOLUTION       "30"
69 #define DEF_BUMPS            "10"
70 #define DEF_MOTION_BLUR      "0.0"
71 #define DEF_INCREMENTAL      "0"
72 #define DEF_HOLD_TIME        "30.0"
73 #define DEF_FADE_TIME        "5.0"
74 #define DEF_ZOOM             "1.0"
75
76 #ifdef HAVE_XMU
77 # ifndef VMS
78 #  include <X11/Xmu/Drawing.h>
79 #else  /* VMS */
80 #  include <Xmu/Drawing.h>
81 # endif /* VMS */
82 #endif
83
84 #include "gltrackball.h"
85 #include "grab-ximage.h"
86
87 #undef countof
88 #define countof(x) (sizeof((x)) / sizeof((*x)))
89
90 #define PI  3.1415926535897
91
92 /* Options from command line */
93 static GLfloat blend;
94 static Bool wireframe;
95 static Bool do_fog;
96 static Bool do_antialias;
97 static Bool do_walls;
98 static Bool do_texture;
99 static Bool do_paint_background;
100 static Bool do_colour;
101 static Bool offset_texture;
102 static int resolution;
103 static int bumps;
104 static float motion_blur;
105 static float fade_time;
106 static float hold_time;
107 static float zoom;
108
109 /* Internal parameters based on supplied options */
110 static Bool culling;
111 static Bool load_textures;
112
113 static XrmOptionDescRec opts[] = {
114     {"-wire",             ".blob.wire",             XrmoptionNoArg, "true" },
115     {"+wire",             ".blob.wire",             XrmoptionNoArg, "false" },
116     {"-blend",            ".blob.blend",            XrmoptionSepArg, 0 },
117     {"-fog",              ".blob.fog",              XrmoptionNoArg, "true" },
118     {"+fog",              ".blob.fog",              XrmoptionNoArg, "false" },
119     {"-antialias",        ".blob.antialias",        XrmoptionNoArg, "true" },
120     {"+antialias",        ".blob.antialias",        XrmoptionNoArg, "false" },
121     {"-walls",            ".blob.walls",            XrmoptionNoArg, "true" },
122     {"+walls",            ".blob.walls",            XrmoptionNoArg, "false" },
123     {"-texture",          ".blob.texture",          XrmoptionNoArg, "true" },
124     {"+texture",          ".blob.texture",          XrmoptionNoArg, "false" },
125     {"-colour",           ".blob.colour",           XrmoptionNoArg, "true" },
126     {"+colour",           ".blob.colour",           XrmoptionNoArg, "false" },
127     {"-offset-texture",   ".blob.offsetTexture",    XrmoptionNoArg, "true" },
128     {"+offset-texture",   ".blob.offsetTexture",    XrmoptionNoArg, "false" },
129     {"-paint-background", ".blob.paintBackground",  XrmoptionNoArg, "true" },
130     {"+paint-background", ".blob.paintBackground",  XrmoptionNoArg, "false" },
131     {"-resolution",       ".blob.resolution",       XrmoptionSepArg, NULL },
132     {"-bumps",            ".blob.bumps",            XrmoptionSepArg, NULL },
133     {"-motion-blur",      ".blob.motionBlur",       XrmoptionSepArg, 0 },
134     {"-fade-time",        ".blob.fadeTime",         XrmoptionSepArg, 0 },
135     {"-hold-time",        ".blob.holdTime",         XrmoptionSepArg, 0 },
136     {"-zoom",             ".blob.zoom",             XrmoptionSepArg, 0 },
137 };
138
139 static argtype vars[] = {
140     {&wireframe,    "wire",         "Wire",      DEF_WIRE,      t_Bool},
141     {&blend,        "blend",        "Blend",     DEF_BLEND,     t_Float},
142     {&do_fog,       "fog",          "Fog",       DEF_FOG,       t_Bool},
143     {&do_antialias, "antialias",    "Antialias", DEF_ANTIALIAS, t_Bool},
144     {&do_walls,     "walls",        "Walls",     DEF_WALLS,     t_Bool},
145     {&do_texture,   "texture",      "Texture",   DEF_TEXTURE,   t_Bool},
146     {&do_colour,    "colour",       "Colour",    DEF_COLOUR,   t_Bool},
147     {&offset_texture, "offsetTexture","OffsetTexture", DEF_OFFSET_TEXTURE, t_Bool},
148     {&do_paint_background,"paintBackground","PaintBackground", DEF_PAINT_BACKGROUND, t_Bool},
149     {&resolution,   "resolution",   "Resolution",   DEF_RESOLUTION,   t_Int},
150     {&bumps,        "bumps",        "Bump",         DEF_BUMPS, t_Int},
151     {&motion_blur,  "motionBlur",   "MotionBlur",   DEF_MOTION_BLUR,  t_Float},
152     {&fade_time,    "fadeTime",     "FadeTime",     DEF_FADE_TIME,    t_Float},
153     {&hold_time,    "holdTime",     "HoldTime",     DEF_HOLD_TIME,    t_Float},
154     {&zoom,         "zoom",         "Zoom",         DEF_ZOOM,         t_Float},
155 };
156
157
158 static OptionStruct desc[] =
159 {
160     {"-/+ wire", "whether to do use wireframe instead of filled (faster)"},
161     {"-/+ blend", "whether to do enable blending (slower)"},
162     {"-/+ fog", "whether to do enable fog (slower)"},
163     {"-/+ antialias", "whether to do enable antialiased lines (slower)"},
164     {"-/+ walls", "whether to add walls to the blob space (slower)"},
165     {"-/+ texture", "whether to add a texture to the blob (slower)"},
166     {"-/+ colour", "whether to colour the blob"},
167     {"-/+ offset_texture", "whether to offset texture co-ordinates"},
168     {"-/+ paint_background", "whether to display a background texture (slower)"},
169     {"-resolution", "Resolution of blob tesselation"},
170     {"-bumps", "Number of bumps used to disturb blob"},
171     {"-motion_blur", "Fade blob images (higher number = faster fade)"},
172     {"-fade_time", "Number of frames to transistion to next image"},
173     {"-hold_time", "Number of frames before next image"},
174 };
175
176 ENTRYPOINT ModeSpecOpt mirrorblob_opts = {countof(opts), opts, countof(vars), vars, desc};
177
178 #ifdef USE_MODULES
179 ModStruct   mirrorblob_description =
180 {"mirrorblob", "init_mirrorblob", "draw_mirrorblob", "release_mirrorblob",
181  "draw_mirrorblob", "init_mirrorblob", "handle_event", &mirrorblob_opts,
182  1000, 1, 2, 1, 4, 1.0, "",
183  "OpenGL mirrorblob", 0, NULL};
184 #endif
185
186 /*****************************************************************************
187  * Types used in blob code
188  *****************************************************************************/
189
190 typedef struct
191 {
192   GLfloat x, y;
193 } Vector2D;
194
195 typedef struct
196 {
197   GLfloat x, y, z;
198 } Vector3D;
199
200 typedef struct
201 {
202   GLfloat w;
203   GLfloat x;
204   GLfloat y;
205   GLfloat z;
206 } Quaternion;
207
208 typedef struct
209 {
210   GLubyte red, green, blue, alpha;
211 } Colour;
212
213 typedef struct
214 {
215   Vector3D initial_position;
216   Vector3D position;
217   Vector3D normal;
218 } Node_Data;
219
220 typedef struct
221 {
222   int node1, node2, node3;
223   Vector3D normal;
224   double length1, length2, length3;
225 } Face_Data;
226
227 /* Structure to hold data about bumps used to distortion sphere */
228 typedef struct
229 {
230   double cx, cy, cpower, csize;
231   double ax, ay, power, size;
232   double mx, my, mpower, msize;
233   double vx, vy, vpower, vsize;
234   Vector3D pos;
235 } Bump_Data;
236
237 /* Vertices of a tetrahedron */
238 #define sqrt_3 0.5773502692
239 /*#undef sqrt_3
240 #define sqrt_3 1.0*/
241 #define PPP {  sqrt_3,  sqrt_3,  sqrt_3 }       /* +X, +Y, +Z */
242 #define MMP { -sqrt_3, -sqrt_3,  sqrt_3 }       /* -X, -Y, +Z */
243 #define MPM { -sqrt_3,  sqrt_3, -sqrt_3 }       /* -X, +Y, -Z */
244 #define PMM {  sqrt_3, -sqrt_3, -sqrt_3 }       /* +X, -Y, -Z */
245
246 /* Structure describing a tetrahedron */
247 static Vector3D tetrahedron[4][3] = {
248     {PPP, MMP, MPM},
249     {PMM, MPM, MMP},
250     {PPP, MPM, PMM},
251     {PPP, PMM, MMP}
252 };
253
254 /*****************************************************************************
255  * Static blob data
256  *****************************************************************************/
257
258 static const Vector3D zero_vector = { 0.0, 0.0, 0.0 };
259
260 /* Use 2 textures to allow a gradual fade between images */
261 #define NUM_TEXTURES 2
262 #define BUMP_ARRAY_SIZE 1024
263
264 typedef enum
265 {
266   INITIALISING,
267   HOLDING,
268   LOADING,
269   TRANSITIONING
270 } Frame_State;
271
272 /* structure for holding the mirrorblob data */
273 typedef struct {
274   int screen_width, screen_height;
275   GLXContext *glx_context;
276   Window window;
277   XColor fg, bg;
278
279   /* Parameters controlling the position of the blob as a whole */
280   Vector3D blob_center;
281   Vector3D blob_anchor;
282   Vector3D blob_velocity;
283   Vector3D blob_force;
284
285   /* Count of the total number of nodes and faces used to tesselate the blob */
286   int num_nodes;
287   int num_faces;
288
289   Node_Data *nodes;
290   Face_Data *faces;
291   
292   Vector3D *dots;
293   Vector3D *normals;
294   Colour   *colours;
295   Vector2D *tex_coords;
296
297   /* Pointer to the bump function results */
298   double *bump_shape, *wall_shape;
299
300   Bump_Data *bump_data;
301
302   /* Use 2 textures to allow a gradual fade between images */
303   int current_texture;
304
305   /* Ratio of used texture size to total texture size */
306   GLfloat tex_width[NUM_TEXTURES], tex_height[NUM_TEXTURES];
307   GLuint textures[NUM_TEXTURES];
308
309   Frame_State state;
310   double state_start_time;
311
312   int colour_cycle;
313
314   Bool mipmap_p;
315   Bool waiting_for_image_p;
316   Bool first_image_p;
317
318   trackball_state *trackball;
319   int button_down;
320
321 } mirrorblobstruct;
322
323 static mirrorblobstruct *Mirrorblob = NULL;
324
325 /******************************************************************************
326  *
327  * Returns the current time in seconds as a double.  Shamelessly borrowed from
328  * glslideshow.
329  *
330  */
331 static double
332 double_time (void)
333 {
334   struct timeval now;
335 # ifdef GETTIMEOFDAY_TWO_ARGS
336   struct timezone tzp;
337   gettimeofday(&now, &tzp);
338 # else
339   gettimeofday(&now);
340 # endif
341
342   return (now.tv_sec + ((double) now.tv_usec * 0.000001));
343 }
344
345 /******************************************************************************
346  *
347  * Change to the projection matrix and set our viewing volume.
348  *
349  */
350 static void
351 reset_projection(int width, int height)
352 {
353   glMatrixMode (GL_PROJECTION);
354   glLoadIdentity ();
355   gluPerspective (60.0, 1.0, 1.0, 1024.0 );
356   glMatrixMode (GL_MODELVIEW);
357   glLoadIdentity ();
358 }
359
360 /******************************************************************************
361  *
362  * Calculate the dot product of two vectors u and v
363  * Dot product u.v = |u||v|cos(theta)
364  * Where theta = angle between u and v
365  */
366 static inline double
367 dot (const Vector3D u, const Vector3D v)
368 {
369   return (u.x * v.x) + (u.y * v.y) + (u.z * v.z);
370 }
371
372 /******************************************************************************
373  *
374  * Calculate the cross product of two vectors.
375  * Gives a vector perpendicular to u and v with magnitude |u||v|sin(theta)
376  * Where theta = angle between u and v
377  */
378 static inline Vector3D
379 cross (const Vector3D u, const Vector3D v)
380 {
381   Vector3D result;
382
383   result.x = (u.y * v.z - u.z * v.y);
384   result.y = (u.z * v.x - u.x * v.z);
385   result.z = (u.x * v.y - u.y * v.x);
386
387   return result;
388 }
389
390 /******************************************************************************
391  *
392  * Add vector v to vector u
393  */
394 static inline void
395 add (Vector3D *u, const Vector3D v)
396 {
397   u->x = u->x + v.x;
398   u->y = u->y + v.y;
399   u->z = u->z + v.z;
400 }
401
402 /******************************************************************************
403  *
404  * Subtract vector v from vector u
405  */
406 static inline Vector3D
407 subtract (const Vector3D u, const Vector3D v)
408 {
409   Vector3D result;
410
411   result.x = u.x - v.x;
412   result.y = u.y - v.y;
413   result.z = u.z - v.z;
414
415   return result;
416 }
417
418 /******************************************************************************
419  *
420  * multiply vector v by scalar s
421  */
422 static inline Vector3D
423 scale (const Vector3D v, const double s)
424 {
425   Vector3D result;
426     
427   result.x = v.x * s;
428   result.y = v.y * s;
429   result.z = v.z * s;
430   return result;
431 }
432
433 /******************************************************************************
434  *
435  * normalise vector v
436  */
437 static inline Vector3D
438 normalise (const Vector3D v)
439 {
440   Vector3D result;
441   double magnitude;
442
443   magnitude = sqrt (dot(v, v));
444
445   if (magnitude > 1e-300)
446     {
447       result = scale (v, 1.0 / magnitude);
448     }
449   else
450     {
451       printf("zero\n");
452       result = zero_vector;
453     }
454   return result;
455 }
456
457 /******************************************************************************
458  *
459  * Calculate the transform matrix for the given quaternion
460  */
461 static void
462 quaternion_transform (Quaternion q, GLfloat * transform)
463 {
464   GLfloat x, y, z, w;
465   x = q.x;
466   y = q.y;
467   z = q.z;
468   w = q.w;
469
470   transform[0] = (w * w) + (x * x) - (y * y) - (z * z);
471   transform[1] = (2.0 * x * y) + (2.0 * w * z);
472   transform[2] = (2.0 * x * z) - (2.0 * w * y);
473   transform[3] = 0.0;
474
475   transform[4] = (2.0 * x * y) - (2.0 * w * z);
476   transform[5] = (w * w) - (x * x) + (y * y) - (z * z);
477   transform[6] = (2.0 * y * z) + (2.0 * w * x);
478   transform[7] = 0.0;
479
480   transform[8] = (2.0 * x * z) + (2.0 * w * y);
481   transform[9] = (2.0 * y * z) - (2.0 * w * x);
482   transform[10] = (w * w) - (x * x) - (y * y) + (z * z);
483   transform[11] = 0.0;
484
485   transform[12] = 0.0;
486   transform[13] = 0.0;
487   transform[14] = 0.0;
488   transform[15] = (w * w) + (x * x) + (y * y) + (z * z);
489 }
490
491 /******************************************************************************
492  *
493  * Apply a matrix transform to the given vector
494  */
495 static inline Vector3D
496 vector_transform (Vector3D u, GLfloat * t)
497 {
498   Vector3D result;
499
500   result.x = (u.x * t[0] + u.y * t[4] + u.z * t[8] + 1.0 * t[12]);
501   result.y = (u.x * t[1] + u.y * t[5] + u.z * t[9] + 1.0 * t[13]);
502   result.z = (u.x * t[2] + u.y * t[6] + u.z * t[10] + 1.0 * t[14]);
503
504   return result;
505 }
506
507 /******************************************************************************
508  *
509  * Return a node that is on an arc between node1 and node2, where distance
510  * is the proportion of the distance from node1 to the total arc.
511  */
512 static Vector3D
513 partial (Vector3D node1, Vector3D node2, double distance)
514 {
515   Vector3D result;
516   Vector3D rotation_axis;
517   GLfloat transformation[16];
518   double angle;
519   Quaternion rotation;
520
521   rotation_axis = normalise (cross (node1, node2));
522   angle = acos (dot (node1, node2)) * distance;
523
524   rotation.x = rotation_axis.x * sin (angle / 2.0);
525   rotation.y = rotation_axis.y * sin (angle / 2.0);
526   rotation.z = rotation_axis.z * sin (angle / 2.0);
527   rotation.w = cos (angle / 2.0);
528
529   quaternion_transform (rotation, transformation);
530
531   result = vector_transform (node1, transformation);
532
533   return result;
534 }
535
536 /****************************************************************************
537  *
538  * Callback indicating a texture has loaded
539  */
540 static void
541 image_loaded_cb (const char *filename, XRectangle *geometry,
542                  int image_width, int image_height, 
543                  int texture_width, int texture_height,
544                  void *closure)
545 {
546   mirrorblobstruct *mp = (mirrorblobstruct *) closure;
547   GLint texid = -1;
548   int texture_index = -1;
549   int i;
550
551   glGetIntegerv (GL_TEXTURE_BINDING_2D, &texid);
552   if (texid < 0) abort();
553
554   for (i = 0; i < NUM_TEXTURES; i++) {
555     if (mp->textures[i] == texid) {
556       texture_index = i;
557       break;
558     }
559   }
560   if (texture_index < 0) abort();
561
562   mp->tex_width [texture_index] =  (GLfloat) image_width  / texture_width;
563   mp->tex_height[texture_index] = -(GLfloat) image_height / texture_height;
564
565   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
566   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
567                    (mp->mipmap_p ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR));
568   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
569   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
570   glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
571
572   mp->waiting_for_image_p = False;
573   mp->first_image_p = True;
574 }
575
576 /* Load a new file into a texture
577  */
578 static void
579 grab_texture(ModeInfo *mi, int texture_index)
580 {
581   mirrorblobstruct *mp = &Mirrorblob[MI_SCREEN(mi)];
582         
583   {
584     int w = (MI_WIDTH(mi)  / 2) - 1;
585     int h = (MI_HEIGHT(mi) / 2) - 1;
586     if (w <= 10) w = 10;
587     if (h <= 10) h = 10;
588         
589     mp->waiting_for_image_p = True;
590     mp->mipmap_p = True;
591     load_texture_async (mi->xgwa.screen, mi->window,
592                         *mp->glx_context, w, h, mp->mipmap_p, 
593                         mp->textures[texture_index],
594                         image_loaded_cb, mp);
595   }
596 }
597
598 /******************************************************************************
599  *
600  * Generate internal parameters based on supplied options the parameters to
601  * ensure they are consistant.
602  */
603 static void
604 set_parameters(void)
605 {
606 # ifdef HAVE_JWZGLES /* #### glPolygonMode other than GL_FILL unimplemented */
607   wireframe = 0;
608 # endif
609
610   /* In wire frame mode do not draw a texture */
611   if (wireframe)
612     {
613       do_texture = False;
614       blend = 1.0;
615     }
616     
617   /* Need to load textures if either the blob or the backgound has an image */
618   if (do_texture || do_paint_background)
619     {
620       load_textures = True;
621     }
622   else
623     {
624       load_textures = False;
625     }
626     
627   /* If theres no texture don't calculate co-ordinates. */
628   if (!do_texture)
629     {
630       offset_texture = False;
631     }
632     
633   culling = True;
634 }
635
636 /******************************************************************************
637  *
638  * Initialise the openGL state data.
639  */
640 static void
641 initialize_gl(ModeInfo *mi, GLsizei width, GLsizei height)
642 {
643   mirrorblobstruct *gp = &Mirrorblob[MI_SCREEN(mi)];
644     
645   /* Lighting values */
646   GLfloat ambientLight[] = { 0.2f, 0.2f, 0.2f, 1.0f };
647
648   GLfloat lightPos0[] = {500.0f, 100.0f, 200.0f, 1.0f };
649   GLfloat whiteLight0[] = { 0.0f, 0.0f, 0.0f, 1.0f };
650   GLfloat sourceLight0[] = { 0.6f, 0.6f, 0.6f, 1.0f };
651   GLfloat specularLight0[] = { 0.8f, 0.8f, 0.9f, 1.0f };
652
653   GLfloat lightPos1[] = {-50.0f, -100.0f, 2500.0f, 1.0f };
654   GLfloat whiteLight1[] = { 0.0f, 0.0f, 0.0f, 1.0f };
655   GLfloat sourceLight1[] = { 0.6f, 0.6f, 0.6f, 1.0f };
656   GLfloat specularLight1[] = { 0.7f, 0.7f, 0.7f, 1.0f };
657
658   GLfloat specref[] = { 1.0f, 1.0f, 1.0f, 1.0f };
659
660   GLfloat fogColor[4] = { 0.4, 0.4, 0.5, 0.1 };
661
662   /* Set the internal parameters based on the configuration settings */
663   set_parameters();
664
665   /* Set the viewport to the width and heigh of the window */
666   glViewport (0, 0, width, height ); 
667
668   if (do_antialias)
669     {
670       blend = 1.0;
671       glEnable(GL_LINE_SMOOTH);
672       glEnable(GL_POLYGON_SMOOTH);
673     }
674
675   /* The blend function is used for trasitioning between two images even when
676    * blend is not selected.
677    */
678   glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
679  
680   if (do_fog)
681     {
682       glEnable(GL_FOG);
683       glFogfv(GL_FOG_COLOR, fogColor);
684       glFogf(GL_FOG_DENSITY, 0.50);
685       glFogf(GL_FOG_START, 15.0);
686       glFogf(GL_FOG_END, 30.0);
687     }
688
689   /* Set the shading model to smooth (Gouraud shading). */
690   glShadeModel (GL_SMOOTH);
691
692   glLightModelfv (GL_LIGHT_MODEL_AMBIENT, ambientLight);
693   glLightfv (GL_LIGHT0, GL_AMBIENT, whiteLight0);
694   glLightfv (GL_LIGHT0, GL_DIFFUSE, sourceLight0);
695   glLightfv (GL_LIGHT0, GL_SPECULAR, specularLight0);
696   glLightfv (GL_LIGHT0, GL_POSITION, lightPos0);
697   glEnable (GL_LIGHT0);
698   glLightfv (GL_LIGHT1, GL_AMBIENT, whiteLight1);
699   glLightfv (GL_LIGHT1, GL_DIFFUSE, sourceLight1);
700   glLightfv (GL_LIGHT1, GL_SPECULAR, specularLight1);
701   glLightfv (GL_LIGHT1, GL_POSITION, lightPos1);
702   glEnable (GL_LIGHT1);
703   glEnable (GL_LIGHTING);
704
705   /* Enable color tracking */
706   glEnable (GL_COLOR_MATERIAL);
707
708   /* Set Material properties to follow glColor values */
709   glColorMaterial (GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
710
711   /* Set all materials to have specular reflectivity */
712   glMaterialfv (GL_FRONT, GL_SPECULAR, specref);
713   glMateriali (GL_FRONT, GL_SHININESS, 32);
714
715   /* Let GL implementation scale normal vectors. */
716   glEnable (GL_NORMALIZE);
717
718   /* Enable Arrays */
719   if (load_textures)
720     {
721       glLightModeli (GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR);
722       glEnable (GL_TEXTURE_2D);
723
724       gp->current_texture = 0;
725       glGenTextures(NUM_TEXTURES, gp->textures);
726       grab_texture(mi, gp->current_texture);
727
728       glMatrixMode (GL_TEXTURE);
729       glRotated (180.0, 1.0, 0.0, 0.0);
730       glMatrixMode (GL_MODELVIEW);
731     }
732
733   /* Clear the buffer since this is not done during a draw with motion blur */
734   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
735 }
736
737 /******************************************************************************
738  *
739  * Initialise the openGL state data.
740  */
741 static void
742 set_blob_gl_state(GLfloat alpha)
743 {
744   if (do_antialias)
745     {
746       glEnable(GL_LINE_SMOOTH);
747       glEnable(GL_POLYGON_SMOOTH);
748     }
749
750   if (wireframe)
751     {
752       glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
753     }
754   else
755     {
756       glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
757     }
758
759   /* The blend function is used for trasitioning between two images even when
760    * blend is not selected.
761    */
762   glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
763  
764   /* Culling. */
765   if (culling)
766     {
767       glCullFace (GL_BACK);
768       glEnable (GL_CULL_FACE);
769       glFrontFace (GL_CCW);
770     }
771   else
772     {
773       glDisable (GL_CULL_FACE);
774     }
775     
776   if (blend < 1.0)
777     {
778       glEnable (GL_BLEND);
779       glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
780       /* Set the default blob colour to off-white. */
781       glColor4f (0.9, 0.9, 1.0, alpha);
782     }
783   else
784     {
785       glDisable(GL_BLEND);
786       glColor4f (0.9, 0.9, 1.0, 1.0);
787     }
788     
789   glEnable(GL_DEPTH_TEST);
790   glEnable(GL_LIGHTING);
791 }
792
793 /******************************************************************************
794  *
795  * Initialise the data required to draw the blob allocating the memory as
796  * necessary.
797  *
798  * Return 0 on success.
799  */
800 static int
801 initialise_blob(mirrorblobstruct *gp,
802                 int width,
803                 int height,
804                 int bump_array_size)
805 {
806   /* Loop variables */    
807   int i, u, v, node, side, face, base, base2 = 0;
808   int nodes_on_edge = resolution;
809   Vector3D node1, node2, result;
810
811   if (nodes_on_edge < 2)
812     return -1;
813
814   gp->num_nodes = 2 * nodes_on_edge * nodes_on_edge - 4 * nodes_on_edge + 4;
815   gp->num_faces = 4 * (nodes_on_edge - 1) * (nodes_on_edge - 1);
816  
817   gp->nodes = (Node_Data *) malloc (gp->num_nodes * sizeof (Node_Data));
818   if (!gp->nodes)
819     {
820       fprintf (stderr, "Couldn't allocate gp->nodes buffer\n");
821       return -1;
822     }
823
824   gp->faces = (Face_Data *) malloc (gp->num_faces * sizeof (Face_Data));
825   if (!gp->faces)
826     {
827       fprintf (stderr, "Couldn't allocate faces data buffer\n");
828       return -1;
829     }
830
831   gp->bump_data = (Bump_Data *) malloc (bumps * sizeof (Bump_Data));
832   if (!gp->bump_data)
833     {
834       fprintf(stderr, "Couldn't allocate bump data buffer\n");
835       return -1;
836     }
837
838   gp->bump_shape = (double *)malloc(bump_array_size * sizeof(double));
839   if (!gp->bump_shape)
840     {
841       fprintf(stderr, "Couldn't allocate bump buffer\n");
842       return -1;
843     }
844
845   gp->wall_shape = (double *)malloc(bump_array_size * sizeof(double));
846   if (!gp->wall_shape)
847     {
848       fprintf(stderr, "Couldn't allocate wall bump buffer\n");
849       return -1;
850     }
851
852         
853   gp->dots = (Vector3D *)malloc(gp->num_nodes * sizeof(Vector3D));
854   if (!gp->dots)
855     {
856       fprintf(stderr, "Couldn't allocate nodes buffer\n");
857       return -1;
858     }
859
860   gp->normals = (Vector3D *)malloc(gp->num_nodes * sizeof(Vector3D));
861   if (!gp->normals)
862     {
863       fprintf(stderr, "Couldn't allocate normals buffer\n");
864       return -1;
865     }
866
867   gp->colours = (Colour *)malloc(gp->num_nodes * sizeof(Colour));
868   if (!gp->colours)
869     {
870       fprintf(stderr, "Couldn't allocate colours buffer\n");
871       return -1;
872     }
873
874   gp->tex_coords = (Vector2D *)malloc(gp->num_nodes * sizeof(Vector2D));
875   if (!gp->tex_coords)
876     {
877       fprintf(stderr, "Couldn't allocate gp->tex_coords buffer\n");
878       return -1;
879     }
880
881         
882   /* Initialise bump data */
883   for (i = 0; i < bumps; i++)
884     {
885       gp->bump_data[i].ax = 2.0 * (((double)random() / (double)RAND_MAX) - 0.5);
886       gp->bump_data[i].ay = 2.0 * (((double)random() / (double)RAND_MAX) - 0.5);
887       gp->bump_data[i].power = (5.0 / pow(bumps, 0.75)) * (((double)random() / (double)RAND_MAX) - 0.5);
888       gp->bump_data[i].size = 0.1 + 0.5 * (((double)random() / (double)RAND_MAX));
889
890       gp->bump_data[i].pos.x = 1.5 * sin(PI * gp->bump_data[i].ay)
891         * cos(PI *  gp->bump_data[i].ax);
892       gp->bump_data[i].pos.y = 1.5 * cos(PI * gp->bump_data[i].ay);
893       gp->bump_data[i].pos.z = 1.5 * sin(PI * gp->bump_data[i].ay)
894         * sin(PI *  gp->bump_data[i].ax);
895
896       gp->bump_data[i].cx = 2.0 * (((double)random() / (double)RAND_MAX) - 0.5);
897       gp->bump_data[i].cy = 2.0 * (((double)random() / (double)RAND_MAX) - 0.5);
898       gp->bump_data[i].cpower = (5.0 / pow(bumps, 0.75)) * (((double)random() / (double)RAND_MAX) - 0.5);
899       gp->bump_data[i].csize = 0.35; /*0.1 + 0.25 * (((double)random() / (double)RAND_MAX));*/
900
901       gp->bump_data[i].vx = 0.0;
902       gp->bump_data[i].vy = 0.0;
903       gp->bump_data[i].vpower = 0.0;
904       gp->bump_data[i].vsize = 0.0;
905
906       gp->bump_data[i].mx = 0.003 * ((double)random() / (double)RAND_MAX);
907       gp->bump_data[i].my = 0.003 * ((double)random() / (double)RAND_MAX);
908       gp->bump_data[i].mpower = 0.003 * ((double)random() / (double)RAND_MAX);
909       gp->bump_data[i].msize = 0.003 * ((double)random() / (double)RAND_MAX);
910     }
911
912   /* Initialise lookup table of bump strength */
913   for (i = 0; i < bump_array_size; i++)
914     {
915       double xd, xd2;
916       xd = i / (double)bump_array_size;
917
918       xd2 = 48.0 * xd * xd;
919       gp->bump_shape[i] = 0.1 / (xd2 + 0.1);
920
921       xd2 = 40.0 * xd * xd * xd * xd;
922       gp->wall_shape[i] = 0.4 / (xd2 + 0.1);
923     }
924
925   node = 0;
926   face = 0;
927   for (side = 0; side < 4; side++)
928     {
929       base = node;
930       if (side == 2) 
931         {
932           base2 = node;
933         }
934       /*
935        * The start and end of the for loops below are modified based on the 
936        * side of the tetrahedron that is being calculated to avoid duplication
937        * of the gp->nodes that are on the edges of the tetrahedron. 
938        */
939       for (u = (side > 1); u < (nodes_on_edge - (side > 0)); u++)
940         {
941           node1 = partial (normalise (tetrahedron[side][0]),
942                            normalise (tetrahedron[side][1]),
943                            u / (double) (nodes_on_edge - 1));
944           node2 = partial (normalise (tetrahedron[side][0]),
945                            normalise (tetrahedron[side][2]),
946                            u / (double) (nodes_on_edge - 1));
947
948           for (v = (side > 1); v <= (u - (side > 2)); v++)
949             {
950               if (u > 0)
951                 result = partial (node1, node2, v / (double) u);
952               else
953                 result = node1;
954
955               gp->nodes[node].position = normalise (result);
956               gp->nodes[node].initial_position = gp->nodes[node].position;
957               gp->nodes[node].normal = zero_vector;
958               node++;
959             }
960         }
961  
962       /*
963        * Determine which nodes make up each face.  The complexity is caused 
964        * by having to determine the correct nodes for the edges of the
965        * tetrahedron since the common nodes on the edges are only calculated
966        * once (see above).
967        */
968       for (u = 0; u < (nodes_on_edge - 1); u++)
969         {
970           for (v = 0; v <= u; v++)
971             {
972               {
973                 if (side < 2)
974                   {
975                     gp->faces[face].node1 = base + ((u * (u + 1)) / 2) + v;
976                     gp->faces[face].node2 =
977                       base + ((u + 1) * (u + 2)) / 2 + v + 1;
978                     gp->faces[face].node3 =
979                       base + ((u + 1) * (u + 2)) / 2 + v;
980
981                     if ((side == 1) && (u == (nodes_on_edge - 2)))
982                       {
983                         gp->faces[face].node3 =
984                           ((u + 1) * (u + 2)) / 2 +
985                           nodes_on_edge - v - 1;
986                         gp->faces[face].node2 =
987                           ((u + 1) * (u + 2)) / 2 +
988                           nodes_on_edge - v - 2;
989                       }
990                   }
991                 else if (side < 3)
992                   {
993                     gp->faces[face].node1 =
994                       base + (((u - 1) * u) / 2) + v - 1;
995                     gp->faces[face].node2 = base + ((u) * (u + 1)) / 2 + v;
996                     gp->faces[face].node3 =
997                       base + ((u) * (u + 1)) / 2 + v - 1;
998
999                     if (u == (nodes_on_edge - 2))
1000                       {
1001                         int n = nodes_on_edge - v - 1;
1002                         gp->faces[face].node2 =
1003                           ((nodes_on_edge *
1004                             (nodes_on_edge + 1)) / 2) +
1005                           ((n - 1) * (n + 0)) / 2;
1006                         gp->faces[face].node3 =
1007                           ((nodes_on_edge *
1008                             (nodes_on_edge + 1)) / 2) +
1009                           ((n + 0) * (n + 1)) / 2;
1010                       }
1011                     if (v == 0)
1012                       {
1013                         gp->faces[face].node1 = (((u + 1) * (u + 2)) / 2) - 1;
1014                         gp->faces[face].node3 = (((u + 2) * (u + 3)) / 2) - 1;
1015                       }
1016                   }
1017                 else
1018                   {
1019                     gp->faces[face].node1 =
1020                       base + (((u - 2) * (u - 1)) / 2) + v - 1;
1021                     gp->faces[face].node2 = base + ((u - 1) * u) / 2 + v;
1022                     gp->faces[face].node3 = base + ((u - 1) * u) / 2 + v - 1;
1023
1024                     if (v == 0)
1025                       {
1026                         gp->faces[face].node1 =
1027                           base2 + ((u * (u + 1)) / 2) - 1;
1028                         gp->faces[face].node3 =
1029                           base2 + ((u + 1) * (u + 2)) / 2 - 1;
1030                       }
1031                     if (u == (nodes_on_edge - 2))
1032                       {
1033                         gp->faces[face].node3 =
1034                           ((nodes_on_edge *
1035                             (nodes_on_edge + 1)) / 2) +
1036                           ((v + 1) * (v + 2)) / 2 - 1;
1037                         gp->faces[face].node2 =
1038                           ((nodes_on_edge *
1039                             (nodes_on_edge + 1)) / 2) +
1040                           ((v + 2) * (v + 3)) / 2 - 1;
1041                       }
1042                     if (v == u)
1043                       {
1044                         gp->faces[face].node1 = (u * (u + 1)) / 2;
1045                         gp->faces[face].node2 = ((u + 1) * (u + 2)) / 2;
1046                       }
1047                   }
1048                 face++;
1049               }
1050
1051               if (v < u)
1052                 {
1053                   if (side < 2)
1054                     {
1055                       gp->faces[face].node1 = base + ((u * (u + 1)) / 2) + v;
1056                       gp->faces[face].node2 =
1057                         base + ((u * (u + 1)) / 2) + v + 1;
1058                       gp->faces[face].node3 =
1059                         base + (((u + 1) * (u + 2)) / 2) + v + 1;
1060
1061                       if ((side == 1) && (u == (nodes_on_edge - 2)))
1062                         {
1063                           gp->faces[face].node3 =
1064                             ((u + 1) * (u + 2)) / 2 +
1065                             nodes_on_edge - v - 2;
1066                         }
1067                     }
1068                   else if (side < 3)
1069                     {
1070                       gp->faces[face].node1 =
1071                         base + ((u * (u - 1)) / 2) + v - 1;
1072                       gp->faces[face].node2 = base + ((u * (u - 1)) / 2) + v;
1073                       gp->faces[face].node3 = base + ((u * (u + 1)) / 2) + v;
1074
1075                       if (u == (nodes_on_edge - 2))
1076                         {
1077                           int n = nodes_on_edge - v - 1;
1078                           gp->faces[face].node3 =
1079                             ((nodes_on_edge *
1080                               (nodes_on_edge + 1)) / 2) +
1081                             ((n + 0) * (n - 1)) / 2;
1082                         }
1083                       if (v == 0)
1084                         {
1085                           gp->faces[face].node1 = (((u + 1) * (u + 2)) / 2) - 1;
1086                         }
1087                     }
1088                   else
1089                     {
1090                       gp->faces[face].node1 =
1091                         base + (((u - 2) * (u - 1)) / 2) + v - 1;
1092                       gp->faces[face].node2 =
1093                         base + (((u - 2) * (u - 1)) / 2) + v;
1094                       gp->faces[face].node3 = base + (((u - 1) * u) / 2) + v;
1095
1096                       if (v == 0)
1097                         {
1098                           gp->faces[face].node1 = base2 + (u * (u + 1)) / 2 - 1;
1099                         }
1100                       if (u == (nodes_on_edge - 2))
1101                         {
1102                           gp->faces[face].node3 =
1103                             ((nodes_on_edge * (nodes_on_edge + 1)) / 2) +
1104                             ((v + 2) * (v + 3)) / 2 - 1;
1105                         }
1106                       if (v == (u - 1))
1107                         {
1108                           gp->faces[face].node2 = (u * (u + 1)) / 2;
1109                         }
1110                     }
1111                   face++;
1112                 }
1113             }
1114         }
1115     }
1116
1117   return 0;
1118 }
1119
1120 /******************************************************************************
1121  *
1122  * Return the magnitude of the given vector
1123  */
1124 #if 0
1125 static inline double
1126 length (Vector3D u)
1127 {
1128   return sqrt (u.x * u.x + u.y * u.y + u.z * u.z);
1129 }
1130 #endif
1131
1132 /******************************************************************************
1133  *
1134  * Calculate the blob shape.
1135  */
1136 static void
1137 calc_blob(mirrorblobstruct *gp,
1138           int width,
1139           int height,
1140           int bump_array_size,
1141           float limit,
1142           double fade)
1143 {
1144   /* Loop variables */
1145   int i, index, face;
1146   /* position of a node */
1147   Vector3D node;
1148   Vector3D offset;
1149   Vector3D bump_vector;
1150   int dist;
1151
1152   /* Update position and strength of bumps used to distort the blob */
1153   for (i = 0; i < bumps; i++)
1154     {
1155       gp->bump_data[i].vx += gp->bump_data[i].mx*(gp->bump_data[i].cx - gp->bump_data[i].ax);
1156       gp->bump_data[i].vy += gp->bump_data[i].my*(gp->bump_data[i].cy - gp->bump_data[i].ay);
1157       gp->bump_data[i].vpower += gp->bump_data[i].mpower
1158         * (gp->bump_data[i].cpower - gp->bump_data[i].power);
1159       gp->bump_data[i].vsize += gp->bump_data[i].msize
1160         * (gp->bump_data[i].csize - gp->bump_data[i].size);
1161
1162       gp->bump_data[i].ax += 0.1 * gp->bump_data[i].vx;
1163       gp->bump_data[i].ay += 0.1 * gp->bump_data[i].vy;
1164       gp->bump_data[i].power += 0.1 * gp->bump_data[i].vpower;
1165       gp->bump_data[i].size += 0.1 * gp->bump_data[i].vsize;
1166
1167       gp->bump_data[i].pos.x = 1.0 * sin(PI * gp->bump_data[i].ay)
1168         * cos(PI * gp->bump_data[i].ax);
1169       gp->bump_data[i].pos.y = 1.0 * cos(PI * gp->bump_data[i].ay);
1170       gp->bump_data[i].pos.z = 1.0 * sin(PI * gp->bump_data[i].ay)
1171         * sin(PI * gp->bump_data[i].ax);
1172     }
1173
1174   /* Update calculate new position for each vertex based on an offset from
1175    * the initial position
1176    */
1177   gp->blob_force = zero_vector;
1178   for (index = 0; index < gp->num_nodes; ++index)
1179     {
1180       node = gp->nodes[index].initial_position;
1181       gp->nodes[index].normal = node;
1182
1183       offset = zero_vector;
1184       for ( i = 0; i < bumps; i++)
1185         {
1186           bump_vector = subtract(gp->bump_data[i].pos, node);
1187
1188           dist = bump_array_size * dot(bump_vector, bump_vector) * gp->bump_data[i].size;
1189
1190           if (dist < bump_array_size)
1191             {
1192               add(&offset, scale(node, gp->bump_data[i].power * gp->bump_shape[dist]));
1193               add(&gp->blob_force, scale(node, gp->bump_data[i].power * gp->bump_shape[dist]));
1194             }
1195         }
1196
1197       add(&node, offset);
1198       node = scale(node, zoom);
1199       add(&node, gp->blob_center);
1200
1201       if (do_walls)
1202         {
1203           if (node.z < -limit) node.z = -limit;
1204           if (node.z > limit) node.z = limit;
1205
1206           dist = bump_array_size * (node.z + limit) * (node.z + limit) * 0.5;
1207           if (dist < bump_array_size)
1208             {
1209               node.x += (node.x - gp->blob_center.x) * gp->wall_shape[dist];
1210               node.y += (node.y - gp->blob_center.y) * gp->wall_shape[dist];
1211               gp->blob_force.z += (node.z + limit);
1212             }
1213           else
1214             {
1215               dist = bump_array_size * (node.z - limit) * (node.z - limit) * 0.5;
1216               if (dist < bump_array_size)
1217                 {
1218                   node.x += (node.x - gp->blob_center.x) * gp->wall_shape[dist];
1219                   node.y += (node.y - gp->blob_center.y) * gp->wall_shape[dist];
1220                   gp->blob_force.z -= (node.z - limit);
1221                 }
1222
1223               if (node.y < -limit) node.y = -limit;
1224               if (node.y > limit) node.y = limit;
1225
1226               dist = bump_array_size * (node.y + limit) * (node.y + limit) * 0.5;
1227               if (dist < bump_array_size)
1228                 {
1229                   node.x += (node.x - gp->blob_center.x) * gp->wall_shape[dist];
1230                   node.z += (node.z - gp->blob_center.z) * gp->wall_shape[dist];
1231                   gp->blob_force.y += (node.y + limit);
1232                 }
1233               else
1234                 {
1235                   dist = bump_array_size * (node.y - limit) * (node.y - limit) * 0.5;
1236                   if (dist < bump_array_size)
1237                     {
1238                       node.x += (node.x - gp->blob_center.x) * gp->wall_shape[dist];
1239                       node.z += (node.z - gp->blob_center.z) * gp->wall_shape[dist];
1240                       gp->blob_force.y -= (node.y - limit);
1241                     }
1242                 }
1243
1244               if (node.x < -limit) node.x = -limit;
1245               if (node.x > limit) node.x = limit;
1246
1247               dist = bump_array_size * (node.x + limit) * (node.x + limit) * 0.5;
1248               if (dist < bump_array_size)
1249                 {
1250                   node.y += (node.y - gp->blob_center.y) * gp->wall_shape[dist];
1251                   node.z += (node.z - gp->blob_center.z) * gp->wall_shape[dist];
1252                   gp->blob_force.x += (node.x + limit);
1253                 }
1254               else
1255                 {
1256                   dist = bump_array_size * (node.x - limit) * (node.x - limit) * 0.5;
1257                   if (dist < bump_array_size)
1258                     {
1259                       node.y += (node.y - gp->blob_center.y) * gp->wall_shape[dist];
1260                       node.z += (node.z - gp->blob_center.z) * gp->wall_shape[dist];
1261                       gp->blob_force.x -= (node.x - limit);
1262                     }
1263                 }
1264
1265               if (node.y < -limit) node.y = -limit;
1266               if (node.y > limit) node.y = limit;
1267             }
1268         }
1269       gp->dots[index] = node;
1270     }
1271
1272   /* Determine the normal for each face */
1273   for (face = 0; face < gp->num_faces; face++)
1274     {
1275       /* Use pointers to indexed nodes to help readability */
1276       int index1 = gp->faces[face].node1;
1277       int index2 = gp->faces[face].node2;
1278       int index3 = gp->faces[face].node3;
1279
1280       gp->faces[face].normal = cross(subtract(gp->dots[index2], gp->dots[index1]),
1281                                      subtract(gp->dots[index3], gp->dots[index1]));
1282             
1283       /* Add the normal for the face onto the normal for the verticies of
1284          the face */
1285       add(&gp->nodes[index1].normal, gp->faces[face].normal);
1286       add(&gp->nodes[index2].normal, gp->faces[face].normal);
1287       add(&gp->nodes[index3].normal, gp->faces[face].normal);
1288     }
1289
1290   /* Use the normal to set the colour and texture */
1291   if (do_colour || do_texture)
1292     {
1293       for (index = 0; index < gp->num_nodes; ++index)
1294         {
1295           gp->normals[index] = normalise(gp->nodes[index].normal);
1296    
1297           if (do_colour)
1298             {
1299               gp->colours[index].red = (int)(255.0 * fabs(gp->normals[index].x));
1300               gp->colours[index].green = (int)(255.0 * fabs(gp->normals[index].y));
1301               gp->colours[index].blue = (int)(255.0 * fabs(gp->normals[index].z));
1302               gp->colours[index].alpha = (int)(255.0 * fade);
1303             }
1304           if (do_texture)
1305             {
1306               if (offset_texture)
1307                 {
1308                   const float cube_size = 100.0;
1309                   Vector3D eye = {0.0, 0.0, 50.0};
1310                   Vector3D eye_r = normalise(subtract(gp->dots[index], eye));
1311                   Vector3D reference = subtract(eye_r, scale(gp->normals[index], 2.0 * dot(eye_r, gp->normals[index])));
1312                   double x = 0.0;
1313                   double y = 0.0;
1314                   double n, n_min = 10000.0, sign = 1.0;
1315                   if (fabs(reference.z) > 1e-9)
1316                     {
1317                       n = (cube_size - gp->dots[index].z) / reference.z;
1318                       if (n < 0.0)
1319                         {
1320                           n = (-cube_size - gp->dots[index].z) / reference.z;
1321                           sign = 3.0;
1322                         }
1323                       if (n > 0.0)
1324                         {
1325                           x = sign * (gp->dots[index].x + n * reference.x);
1326                           y = sign * (gp->dots[index].y + n * reference.y);
1327                           n_min = n;
1328                         }
1329                     }
1330                   if (fabs(reference.x) > 1e-9)
1331                     {
1332                       n = (cube_size - gp->dots[index].x) / reference.x;
1333                       sign = 1.0;
1334                       if (n < 0.0)
1335                         {
1336                           n = (-cube_size - gp->dots[index].x) / reference.x;
1337                           sign = -1.0;
1338                         }
1339                       if ((n > 0.0) && (n < n_min))
1340                         {
1341                           x = sign * (2.0 * cube_size - (gp->dots[index].z + n * reference.z));
1342                           y = sign * x * (gp->dots[index].y + n * reference.y) / cube_size;
1343                           n_min = n;
1344                         }
1345                     }
1346                   if (fabs(reference.y) > 1e-9)
1347                     {
1348                       n = (cube_size - gp->dots[index].y) / reference.y;
1349                       sign = 1.0;
1350                       if (n < 0.0)
1351                         {
1352                           n = (-cube_size - gp->dots[index].y) / reference.y;
1353                           sign = -1.0;
1354                         }
1355                       if ((n > 0.0) && (n < n_min))
1356                         {
1357                           y = sign * (2.0 * cube_size -( gp->dots[index].z + n * reference.z));
1358                           x = sign * y * (gp->dots[index].x + n * reference.x) / cube_size;
1359                         }
1360                     }
1361                                         
1362                   gp->tex_coords[index].x = 0.5 + x / (cube_size * 6.0);
1363                   gp->tex_coords[index].y = 0.5 - y / (cube_size * 6.0);
1364                 }
1365               else
1366                 {
1367                   gp->tex_coords[index].x = 0.5
1368                     * (1.0 + asin(gp->normals[index].x) / (0.5 * PI));
1369                   gp->tex_coords[index].y = -0.5
1370                     * (1.0 + asin(gp->normals[index].y) / (0.5 * PI));
1371                 }
1372               /* Adjust the texture co-ordinates to from range 0..1 to
1373                * 0..width or 0..height as appropriate
1374                */
1375               gp->tex_coords[index].x *= gp->tex_width[gp->current_texture];
1376               gp->tex_coords[index].y *= gp->tex_height[gp->current_texture];
1377             }
1378         }
1379     }
1380     
1381   /* Update the center of the whole blob */
1382   add(&gp->blob_velocity, scale (subtract (gp->blob_anchor, gp->blob_center), 1.0 / 80.0));
1383   add(&gp->blob_velocity, scale (gp->blob_force, 0.01 / gp->num_nodes));
1384
1385   add(&gp->blob_center, scale(gp->blob_velocity, 0.5));
1386
1387   gp->blob_velocity = scale(gp->blob_velocity, 0.999);
1388 }
1389
1390 static void
1391 draw_vertex(mirrorblobstruct *gp, int index)
1392 {
1393   if (do_colour)
1394     {
1395       glColor3ub(gp->colours[index].red,
1396                  gp->colours[index].green,
1397                  gp->colours[index].blue);
1398     }
1399   if (load_textures)
1400     {
1401       glTexCoord2fv(&gp->tex_coords[index].x);
1402     }
1403   glNormal3fv(&gp->normals[index].x);
1404   glVertex3fv(&gp->dots[index].x);
1405 }
1406
1407 /******************************************************************************
1408  *
1409  * Draw the blob shape.
1410  *
1411  */
1412 static void
1413 draw_blob (mirrorblobstruct *gp)
1414 {
1415   int face;
1416
1417   glMatrixMode(GL_MODELVIEW);
1418   glLoadIdentity();
1419 /*  glRotatef(current_device_rotation(), 0, 0, 1); */
1420
1421   /* Move down the z-axis. */
1422   glTranslatef (0.0, 0.0, -4.0);
1423
1424   gltrackball_rotate (gp->trackball);
1425
1426   /* glColor4ub (255, 0, 0, 128); */
1427   glBegin(GL_TRIANGLES);
1428   for (face = 0; face < gp->num_faces; face++)
1429     {
1430       draw_vertex(gp, gp->faces[face].node1);
1431       draw_vertex(gp, gp->faces[face].node2);
1432       draw_vertex(gp, gp->faces[face].node3);
1433     }
1434   glEnd();
1435
1436 #if 0
1437   glBegin(GL_LINES);
1438   for (face = 0; face < gp->num_faces; face++)
1439     {
1440       if (gp->normals[gp->faces[face].node1].z > 0.0)
1441         {
1442           Vector3D end = gp->dots[gp->faces[face].node1];
1443           glVertex3dv(&end);
1444           add(&end, scale(gp->normals[gp->faces[face].node1], 0.25));
1445           glVertex3dv(&end);
1446         }
1447     }
1448   glEnd();
1449 #endif
1450         
1451   glLoadIdentity();
1452 }
1453
1454 /******************************************************************************
1455  *
1456  * Draw the background image simply map a texture onto a full screen quad.
1457  */
1458 static void
1459 draw_background (ModeInfo *mi)
1460 {
1461   mirrorblobstruct *gp = &Mirrorblob[MI_SCREEN(mi)];
1462   GLfloat rot = current_device_rotation();
1463     
1464   glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
1465   glEnable (GL_TEXTURE_2D);
1466   glDisable(GL_LIGHTING);
1467   glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
1468
1469   /* Reset the projection matrix to make it easier to get the size of the quad
1470    * correct
1471    */
1472   glMatrixMode(GL_PROJECTION);
1473   glPushMatrix();
1474   glLoadIdentity();
1475
1476   glRotatef (-rot, 0, 0, 1);
1477 /*
1478   if ((rot >  45 && rot <  135) ||
1479       (rot < -45 && rot > -135))
1480     {
1481       GLfloat s = MI_WIDTH(mi) / (GLfloat) MI_HEIGHT(mi);
1482       glScalef (s, 1/s, 1);
1483     }
1484 */
1485
1486   glOrtho(0.0, MI_WIDTH(mi), MI_HEIGHT(mi), 0.0, -1000.0, 1000.0);
1487
1488   glBegin (GL_QUADS);
1489     
1490   glTexCoord2f (0.0, 0.0);
1491   glVertex2i (0, 0);
1492     
1493   glTexCoord2f (0.0, gp->tex_height[gp->current_texture]);
1494   glVertex2i (0, MI_HEIGHT(mi));
1495
1496   glTexCoord2f (gp->tex_width[gp->current_texture], gp->tex_height[gp->current_texture]);
1497   glVertex2i (MI_WIDTH(mi), MI_HEIGHT(mi));
1498
1499   glTexCoord2f (gp->tex_width[gp->current_texture], 0.0);
1500   glVertex2i (MI_WIDTH(mi), 0);
1501   glEnd();
1502
1503   glPopMatrix ();
1504   glMatrixMode (GL_MODELVIEW);
1505   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
1506 }
1507
1508 /******************************************************************************
1509  *
1510  * Update the scene.
1511  */
1512 static GLvoid
1513 draw_scene(ModeInfo * mi)
1514 {
1515   mirrorblobstruct *gp = &Mirrorblob[MI_SCREEN(mi)];
1516     
1517   double fade = 0.0;
1518   double current_time;
1519   check_gl_error ("draw_scene");
1520
1521   mi->polygon_count = 0;
1522   glColor4f (1.0, 1.0, 1.0, 1.0);
1523
1524   current_time = double_time();
1525   switch (gp->state)
1526     {
1527     case INITIALISING:
1528       glColor4f (0.0, 0.0, 0.0, 1.0);
1529       fade = 1.0;
1530       break;
1531
1532     case TRANSITIONING:
1533       fade = 1.0 - (current_time - gp->state_start_time) / fade_time;
1534       break;
1535
1536     case LOADING: /* FALL-THROUGH */
1537     case HOLDING:
1538       fade = 1.0;
1539       break;
1540     }
1541
1542   /* Set the correct texture, when transitioning this ensures that the first draw
1543    * is the original texture (which has the new texture drawn over it with decreasing
1544    * transparency)
1545    */
1546   if (load_textures)
1547     {
1548       glBindTexture(GL_TEXTURE_2D, gp->textures[gp->current_texture]);
1549     }
1550
1551   glDisable (GL_DEPTH_TEST);
1552   if (do_paint_background)
1553     {
1554       glEnable (GL_TEXTURE_2D);
1555       if (motion_blur > 0.0)
1556         {
1557           glClear(GL_DEPTH_BUFFER_BIT);
1558           glEnable (GL_BLEND);
1559           glColor4f (1.0, 1.0, 1.0, motion_blur);
1560         }
1561       else
1562         {
1563           glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1564         }
1565       draw_background (mi);
1566       mi->polygon_count++;
1567
1568       /* When transitioning between two images paint the new image over the old
1569        * image with a varying alpha value to get a smooth fade.
1570        */
1571       if (gp->state == TRANSITIONING)
1572         {
1573           glEnable (GL_BLEND);
1574           /* Select the texture to transition to */
1575           glBindTexture (GL_TEXTURE_2D, gp->textures[1 - gp->current_texture]);
1576           glColor4f (1.0, 1.0, 1.0, 1.0 - fade);
1577
1578           draw_background (mi);
1579           mi->polygon_count++;
1580
1581           /* Select the original texture to draw the blob */
1582           glBindTexture (GL_TEXTURE_2D, gp->textures[gp->current_texture]);
1583         }
1584       /* Clear the depth buffer bit so the backgound is behind the blob */
1585       glClear(GL_DEPTH_BUFFER_BIT);
1586     }
1587   else if (motion_blur > 0.0)
1588     {
1589       glEnable (GL_BLEND);
1590       glColor4f (0.0, 0.0, 0.0, motion_blur);
1591       glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
1592       glTranslatef (0.0, 0.0, -4.0);
1593       glRectd (-10.0, -10.0, 10.0, 10.0);
1594       if (wireframe)
1595         {
1596           glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
1597         }
1598       glClear(GL_DEPTH_BUFFER_BIT);
1599     }
1600   else
1601     {
1602       glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1603     }
1604
1605   if (!do_texture)
1606     {
1607       fade = 1.0;
1608       glDisable (GL_TEXTURE_2D);
1609     }
1610
1611   calc_blob(gp, MI_WIDTH(mi), MI_HEIGHT(mi), BUMP_ARRAY_SIZE, 2.5, fade * blend);
1612
1613   set_blob_gl_state(fade * blend);
1614
1615   if (blend < 1.0)
1616     {
1617       /* Disable the colour chanels so that only the depth buffer is updated */
1618       glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
1619       draw_blob(gp);
1620       mi->polygon_count += gp->num_faces;
1621       glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
1622     }
1623         
1624   glDepthFunc(GL_LEQUAL);
1625   draw_blob(gp);
1626   mi->polygon_count += gp->num_faces;
1627
1628   /* While transitioning between images draw a second blob with a modified
1629    * alpha value.
1630    */
1631   if (load_textures && (hold_time > 0))
1632     {
1633       switch (gp->state)
1634         {
1635         case INITIALISING:
1636           if (!gp->waiting_for_image_p)
1637             {
1638               gp->state = HOLDING;
1639             }
1640           break;
1641                 
1642         case HOLDING:
1643           if ((current_time - gp->state_start_time) > hold_time)
1644             {
1645               grab_texture(mi, 1 - gp->current_texture);
1646               gp->state = LOADING;
1647             }
1648           break;
1649
1650         case LOADING:
1651           /* Once the image has loaded move to the TRANSITIONING STATE */
1652           if (!gp->waiting_for_image_p)
1653             {
1654               gp->state = TRANSITIONING;
1655               /* Get the time again rather than using the current time so
1656                * that the time taken by the grab_texture function is not part
1657                * of the fade time
1658                */
1659               gp->state_start_time = double_time();
1660             }
1661           break;        
1662
1663         case TRANSITIONING:
1664
1665           /* If the blob is textured draw over existing blob to fade between
1666            * images
1667            */
1668           if (do_texture)
1669             {
1670               /* Select the texture to transition to */
1671               glBindTexture (GL_TEXTURE_2D, gp->textures[1 - gp->current_texture]);
1672               glEnable (GL_BLEND);
1673                 
1674               /* If colour is enabled update the alpha data in the buffer and
1675                * use that in the blending since the alpha of the incomming
1676                * verticies will not be correct
1677                */
1678               if (do_colour)
1679                 {
1680                   glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
1681                   glClearColor(0.0, 0.0, 0.0, (1.0 - fade) * blend);
1682                   glClear(GL_COLOR_BUFFER_BIT);
1683                   glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);                    
1684                   glBlendFunc(GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA);
1685                 }
1686               else
1687                 {
1688                   glColor4f (0.9, 0.9, 1.0, (1.0 - fade) * blend);
1689                 }
1690
1691               draw_blob (gp);
1692               mi->polygon_count += gp->num_faces;
1693
1694               if (do_colour)
1695                 {
1696                   /* Restore the 'standard' blend functions. */
1697                   glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1698                 }
1699             }
1700             
1701           if ((current_time - gp->state_start_time) > fade_time)
1702             {
1703               gp->state = HOLDING;
1704               gp->state_start_time = current_time;
1705               gp->current_texture = 1 - gp->current_texture;
1706             }
1707           break;
1708
1709         }
1710     }
1711 }
1712
1713 /******************************************************************************
1714  *
1715  * XMirrorblob screen update entry
1716  */
1717 ENTRYPOINT void
1718 draw_mirrorblob(ModeInfo * mi)
1719 {
1720   mirrorblobstruct *gp = &Mirrorblob[MI_SCREEN(mi)];
1721   Display    *display = MI_DISPLAY(mi);
1722   Window      window = MI_WINDOW(mi);
1723
1724   if (!gp->glx_context)
1725     return;
1726
1727   /* Wait for the first image; for subsequent images, load them in the
1728      background while animating. */
1729   if (gp->waiting_for_image_p && gp->first_image_p)
1730     return;
1731
1732   glXMakeCurrent(display, window, *(gp->glx_context));
1733   draw_scene(mi);
1734   if (mi->fps_p) do_fps (mi);
1735   glFinish();
1736   glXSwapBuffers(display, window);
1737 }
1738
1739 /******************************************************************************
1740  *
1741  * XMirrorblob screen resize entry
1742  */
1743 ENTRYPOINT void
1744 reshape_mirrorblob(ModeInfo *mi, int width, int height)
1745 {
1746   glViewport( 0, 0, MI_WIDTH(mi), MI_HEIGHT(mi) );
1747   reset_projection(width, height);
1748 }
1749
1750 /****************************************************************************
1751  *
1752  * Handle Mouse events
1753  */
1754 ENTRYPOINT Bool
1755 mirrorblob_handle_event (ModeInfo * mi, XEvent * event)
1756 {
1757   mirrorblobstruct *gp = &Mirrorblob[MI_SCREEN (mi)];
1758
1759   if (event->xany.type == ButtonPress &&
1760       event->xbutton.button == Button4)
1761     {
1762       zoom *= 1.1;
1763       return True;
1764     }
1765   else if (event->xany.type == ButtonPress &&
1766            event->xbutton.button == Button5)
1767     {
1768
1769       zoom *= 0.9;
1770       return True;
1771     }
1772   else if (gltrackball_event_handler (event, gp->trackball,
1773                                  MI_WIDTH (mi), MI_HEIGHT (mi),
1774                                  &gp->button_down))
1775     {
1776       return True;
1777     }
1778   else if (screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event))
1779     {
1780       gp->state_start_time = 0;
1781       gp->state = HOLDING;
1782       return True;
1783     }
1784
1785   return False;
1786 }
1787
1788 /******************************************************************************
1789  *
1790  * XMirrorblob initialise entry
1791  */
1792 ENTRYPOINT void
1793 init_mirrorblob(ModeInfo * mi)
1794 {
1795   int screen = MI_SCREEN(mi);
1796
1797   mirrorblobstruct *gp;
1798
1799   if (Mirrorblob == NULL)
1800     {
1801       if ((Mirrorblob = (mirrorblobstruct *)
1802            calloc(MI_NUM_SCREENS(mi), sizeof (mirrorblobstruct))) == NULL)
1803         {
1804           return;
1805         }
1806     }
1807   gp = &Mirrorblob[screen];
1808
1809   gp->window = MI_WINDOW(mi);
1810   if ((gp->glx_context = init_GL(mi)) != NULL)
1811     {
1812       reshape_mirrorblob(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
1813       initialize_gl(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
1814     }
1815   else
1816     {
1817       MI_CLEARWINDOW(mi);
1818     }
1819   gp->trackball = gltrackball_init(False);
1820     
1821   initialise_blob(gp, MI_WIDTH(mi), MI_HEIGHT(mi), BUMP_ARRAY_SIZE);
1822   gp->state = INITIALISING;
1823   gp->state_start_time = double_time();
1824
1825   gp->first_image_p = True;
1826 }
1827
1828 /******************************************************************************
1829  *
1830  * XMirrorblob cleanup entry
1831  */
1832 ENTRYPOINT void
1833 release_mirrorblob(ModeInfo * mi)
1834 {
1835   if (Mirrorblob != NULL) {
1836     int i;
1837     for (i = 0; i < MI_NUM_SCREENS(mi); i++) {
1838       mirrorblobstruct *gp = &Mirrorblob[i];
1839       if (gp->nodes) free(gp->nodes);
1840       if (gp->faces) free(gp->faces);
1841       if (gp->bump_data) free(gp->bump_data);
1842       if (gp->colours) free(gp->colours);
1843       if (gp->tex_coords) free(gp->tex_coords);
1844       if (gp->dots) free(gp->dots);
1845       if (gp->wall_shape) free(gp->wall_shape);
1846       if (gp->bump_shape) free(gp->bump_shape);
1847     }
1848
1849     free(Mirrorblob);
1850     Mirrorblob = NULL;
1851   }
1852   FreeAllGL(mi);
1853 }
1854
1855 XSCREENSAVER_MODULE ("MirrorBlob", mirrorblob)
1856
1857 #endif