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