ace7eef3839960c7d454e3e5e7444cbe1c6354b3
[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  *
21  * The mirrorblob screensaver draws a pulsing blob on the screen.  Options
22  * include adding a background (via screen_to_ximage), texturing the blob,
23  * making the blob semi-transparent and varying the resolution of the blob
24  * tessellation.
25  *
26  * The blob was inspired by a lavalamp is in no way a simulation.  The code is
27  * just an attempt to generate some eye-candy.
28  *
29  * Much of xscreensaver code framework is taken from the pulsar module by David
30  * Konerding and the glslideshow by Mike Oliphant and Jamie Zawinski.
31  *
32  */
33
34 #include <math.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <sys/time.h>
38
39 /*-
40  * due to a Bug/feature in VMS X11/Intrinsic.h has to be placed before xlock.
41  * otherwise caddr_t is not defined correctly
42  */
43
44 #include <X11/Intrinsic.h>
45
46 #ifdef STANDALONE
47 # define PROGCLASS         "Screensaver"
48 # define HACK_INIT         init_screensaver
49 # define HACK_DRAW         draw_screensaver
50 # define HACK_RESHAPE      reshape_screensaver
51 # define screensaver_opts  xlockmore_opts
52
53 #define DEF_DELAY            "10000"
54 #define DEF_FPS              "False"
55 #define DEF_WIRE             "False"
56 #define DEF_BLEND            "False"
57 #define DEF_FOG              "False"
58 #define DEF_ANTIALIAS        "False"
59 #define DEF_WALLS            "True"
60 #define DEF_COLOUR           "False"
61 #define DEF_TEXTURE          "True"
62 #define DEF_OFFSET_TEXTURE   "False"
63 #define DEF_PAINT_BACKGROUND "True"
64 #define DEF_X_RES            "60"
65 #define DEF_Y_RES            "32"
66 #define DEF_FIELD_POINTS     "5"
67 #define DEF_MOTION_BLUR      "0"
68 #define DEF_INCREMENTAL      "0"
69 #define DEF_HOLD_TIME        "30"
70 #define DEF_FADE_TIME        "5"
71
72 #define DEFAULTS \
73     "*delay:             " DEF_DELAY                "\n" \
74     "*showFPS:           " DEF_FPS                  "\n" \
75     "*wire:              " DEF_WIRE                 "\n" \
76     "*blend:             " DEF_BLEND                "\n" \
77     "*fog:               " DEF_FOG                  "\n" \
78     "*antialias:         " DEF_ANTIALIAS            "\n" \
79     "*walls:             " DEF_WALLS                "\n" \
80     "*colour :           " DEF_COLOUR               "\n" \
81     "*texture:           " DEF_TEXTURE              "\n" \
82     "*offset_texture:    " DEF_OFFSET_TEXTURE       "\n" \
83     "*paint_background:  " DEF_PAINT_BACKGROUND     "\n" \
84     "*x_resolution:      " DEF_X_RES                "\n" \
85     "*y_resolution:      " DEF_Y_RES                "\n" \
86     "*field_points:      " DEF_FIELD_POINTS         "\n" \
87     "*motion_blur:       " DEF_MOTION_BLUR          "\n" \
88     "*incremental:       " DEF_INCREMENTAL          "\n" \
89     "*hold_time:         " DEF_HOLD_TIME            "\n" \
90     "*fade_time:         " DEF_FADE_TIME            "\n"
91
92 # include "xlockmore.h"    /* from the xscreensaver distribution */
93 #else /* !STANDALONE */
94 # include "xlock.h"        /* from the xlockmore distribution */
95 #endif /* !STANDALONE */
96
97 #ifdef USE_GL /* whole file */
98
99 #ifdef HAVE_XMU
100 # ifndef VMS
101 #  include <X11/Xmu/Drawing.h>
102 #else  /* VMS */
103 #  include <Xmu/Drawing.h>
104 # endif /* VMS */
105 #endif
106
107 #include <GL/gl.h>
108 #include <GL/glu.h>
109 #include "GL/glx.h"
110
111 #include <stdlib.h>
112 #include <stdio.h>
113 /*#include <string.h>*/
114 #include "grab-ximage.h"
115
116 #undef countof
117 #define countof(x) (sizeof((x)) / sizeof((*x)))
118
119 #define PI  3.1415926535897
120
121 /* */
122 static int do_wire;
123 static int do_blend;
124 static int do_fog;
125 static int do_antialias;
126 static int do_walls;
127 static int do_texture;
128 static int do_paint_background;
129 static int do_colour;
130 static int offset_texture;
131 static int x_resolution;
132 static int y_resolution;
133 static int field_points;
134 static int motion_blur;
135 static int incremental;
136 static int fade_time;
137 static int hold_time;
138
139 static XrmOptionDescRec opts[] = {
140     {"-wire",             ".blob.wire",             XrmoptionNoArg, "true" },
141     {"+wire",             ".blob.wire",             XrmoptionNoArg, "false" },
142     {"-blend",            ".blob.blend",            XrmoptionNoArg, "true" },
143     {"+blend",            ".blob.blend",            XrmoptionNoArg, "false" },
144     {"-fog",              ".blob.fog",              XrmoptionNoArg, "true" },
145     {"+fog",              ".blob.fog",              XrmoptionNoArg, "false" },
146     {"-antialias",        ".blob.antialias",        XrmoptionNoArg, "true" },
147     {"+antialias",        ".blob.antialias",        XrmoptionNoArg, "false" },
148     {"-walls",            ".blob.walls",            XrmoptionNoArg, "true" },
149     {"+walls",            ".blob.walls",            XrmoptionNoArg, "false" },
150     {"-texture",          ".blob.texture",          XrmoptionNoArg, "true" },
151     {"+texture",          ".blob.texture",          XrmoptionNoArg, "false" },
152     {"-colour",           ".blob.colour",           XrmoptionNoArg, "true" },
153     {"+colour",           ".blob.colour",           XrmoptionNoArg, "false" },
154     {"-offset_texture",   ".blob.offset_texture",   XrmoptionNoArg, "true" },
155     {"+offset_texture",   ".blob.offset_texture",   XrmoptionNoArg, "false" },
156     {"-paint_background", ".blob.paint_background", XrmoptionNoArg, "true" },
157     {"+paint_background", ".blob.paint_background", XrmoptionNoArg, "false" },
158     {"-x_res",            ".blob.x_res",            XrmoptionSepArg, NULL },
159     {"-y_res",            ".blob.y_res",            XrmoptionSepArg, NULL },
160     {"-field_points",     ".blob.field_points",     XrmoptionSepArg, NULL },
161     {"-motion_blur",      ".blob.motion_blur",      XrmoptionSepArg, NULL },
162     {"-incremental",      ".blob.incremental",      XrmoptionSepArg, NULL },
163     {"-fade_time",        ".blob.fade_time",        XrmoptionSepArg, NULL },
164     {"-hold_time",        ".blob.hold_time",        XrmoptionSepArg, NULL },
165 };
166
167 static argtype vars[] = {
168     {&do_wire,      "wire",         "Wire",      DEF_WIRE,      t_Bool},
169     {&do_blend,     "blend",        "Blend",     DEF_BLEND,     t_Bool},
170     {&do_fog,       "fog",          "Fog",       DEF_FOG,       t_Bool},
171     {&do_antialias, "antialias",    "Antialias", DEF_ANTIALIAS, t_Bool},
172     {&do_walls,     "walls",        "Walls",     DEF_WALLS,     t_Bool},
173     {&do_texture,   "texture",      "Texture",   DEF_TEXTURE,   t_Bool},
174     {&do_colour,    "colour",       "Colour",    DEF_COLOUR,   t_Bool},
175     {&offset_texture, "offset_texture","Offset_Texture", DEF_OFFSET_TEXTURE, t_Bool},
176     {&do_paint_background,"paint_background","Paint_Background", DEF_PAINT_BACKGROUND, t_Bool},
177     {&x_resolution, "x_res",        "X_Res",        DEF_X_RES,        t_Int},
178     {&y_resolution, "y_res",        "Y_Res",        DEF_Y_RES,        t_Int},
179     {&field_points, "field_points", "Field_Points", DEF_FIELD_POINTS, t_Int},
180     {&motion_blur,  "motion_blur",  "Motion_Blur",  DEF_MOTION_BLUR,  t_Int},
181     {&incremental,  "incremental",  "Incremental",  DEF_INCREMENTAL,  t_Int},
182     {&fade_time,    "fade_time",    "Fade_Time",    DEF_FADE_TIME,    t_Int},
183     {&hold_time,    "hold_time",    "Hold_Time",    DEF_HOLD_TIME,    t_Int},
184 };
185
186
187 static OptionStruct desc[] =
188 {
189     {"-/+ wire", "whether to do use wireframe instead of filled (faster)"},
190     {"-/+ blend", "whether to do enable blending (slower)"},
191     {"-/+ fog", "whether to do enable fog (slower)"},
192     {"-/+ antialias", "whether to do enable antialiased lines (slower)"},
193     {"-/+ walls", "whether to add walls to the blob space (slower)"},
194     {"-/+ texture", "whether to add a texture to the blob (slower)"},
195     {"-/+ colour", "whether to colour the blob"},
196     {"-/+ offset_texture", "whether to offset texture co-ordinates"},
197     {"-/+ paint_background", "whether to display a background texture (slower)"},
198     {"-x_res", "Blob resolution in x direction"},
199     {"-y_res", "Blob resolution in y direction"},
200     {"-field_points", "Number of field points used to disturb blob"},
201     {"-motion_blur", "Fade blob images (higher number = faster fade)"},
202     {"-incremental", "Field summation method"},
203     {"-fade_time", "Number of frames to transistion to next image"},
204     {"-hold_time", "Number of frames before next image"},
205 };
206
207 ModeSpecOpt screensaver_opts = {countof(opts), opts, countof(vars), vars, desc};
208
209 #ifdef USE_MODULES
210 ModStruct   screensaver_description =
211 {"screensaver", "init_screensaver", "draw_screensaver", "release_screensaver",
212  "draw_screensaver", "init_screensaver", NULL, &screensaver_opts,
213  1000, 1, 2, 1, 4, 1.0, "",
214  "OpenGL screensaver", 0, NULL};
215 #endif
216
217 /* structure for holding the screensaver data */
218 typedef struct {
219     int screen_width, screen_height;
220     GLXContext *glx_context;
221     Window window;
222     XColor fg, bg;
223 } screensaverstruct;
224
225 static screensaverstruct *Screensaver = NULL;
226
227 /*****************************************************************************
228  * Types used in blob code
229  *****************************************************************************/
230
231 typedef struct
232 {
233     GLdouble x, y;
234 } Vector2D;
235
236 typedef struct
237 {
238     GLdouble x, y, z;
239 } Vector3D;
240
241 typedef struct
242 {
243     GLubyte red, green, blue, alpha;
244 } Colour;
245
246 /* Data used for sphere tessellation */
247 typedef struct
248 {
249     double cosyd, sinyd;
250
251     /* Number of x points at each row of the blob */
252     int num_x_points;
253 } Row_Data;
254
255 /* Structure to hold sphere distortion data */
256 typedef struct
257 {
258     double cx, cy, cpower;
259     double mx, my, mpower;
260     double ax, ay, apower;
261     double vx, vy, vpower;
262     Vector3D pos;
263 } Field_Data;
264
265 /*****************************************************************************
266  * Static blob data
267  *****************************************************************************/
268
269 static Row_Data *row_data;
270
271 /* Parameters controlling the position of the blob as a whole */
272 static Vector3D blob_center = {0.0, 0.0, 0.0};
273 static Vector3D blob_anchor = {0.0, 0.0, 0.0};
274 static Vector3D blob_velocity = {0.0, 0.0, 0.0};
275 static Vector3D blob_force = {0.0, 0.0, 0.0};
276
277 /* Count of the total number of points */
278 static int num_points;
279
280 static Vector3D *dots = NULL;
281 static Vector3D *normals = NULL;
282 static Colour   *colours = NULL;
283 static Vector2D *tex_coords = NULL;
284
285 /* Pointer to the field function results */
286 static double *field = 0, *wall_field = 0;
287
288 Field_Data *field_data;
289
290 /* Use 2 textures to allow a gradual fade between images */
291 #define NUM_TEXTURES 2
292 static int current_texture;
293
294 /* Ratio of used texture size to total texture size */
295 GLfloat tex_width[NUM_TEXTURES], tex_height[NUM_TEXTURES];
296 GLuint textures[NUM_TEXTURES];
297
298 typedef enum
299 {
300   HOLDING,
301   TRANSITIONING
302 } Frame_State;
303
304 static Frame_State state = HOLDING;
305 static double state_start_time = 0;
306
307 static int colour_cycle = 0;
308
309 /******************************************************************************
310  *
311  * Returns the current time in seconds as a double.  Shamelessly borrowed from
312  * glslideshow.
313  *
314  */
315 static double
316 double_time (void)
317 {
318   struct timeval now;
319 # ifdef GETTIMEOFDAY_TWO_ARGS
320   struct timezone tzp;
321   gettimeofday(&now, &tzp);
322 # else
323   gettimeofday(&now);
324 # endif
325
326   return (now.tv_sec + ((double) now.tv_usec * 0.000001));
327 }
328
329 /******************************************************************************
330  *
331  * Change to the projection matrix and set our viewing volume.
332  *
333  */
334 static void
335 reset_projection(int width, int height)
336 {
337     glMatrixMode (GL_PROJECTION);
338     glLoadIdentity ();
339     gluPerspective (60.0, 1.0, 1.0, 1024.0 );
340     glMatrixMode (GL_MODELVIEW);
341     glLoadIdentity ();
342 }
343
344 /****************************************************************************
345  *
346  * Load a texture using the screen_to_ximage function.
347  */
348 void
349 grab_texture(ModeInfo *mi, int texture_index)
350 {
351     XImage *ximage;
352
353     ximage = screen_to_ximage (mi->xgwa.screen, mi->window, 0);
354
355     glBindTexture (GL_TEXTURE_2D, textures[texture_index]);
356     glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
357
358     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, ximage->width, ximage->height,
359                  0, GL_RGBA, GL_UNSIGNED_BYTE, ximage->data);
360
361     tex_width[texture_index] = (mi->xgwa.width - 1) / (GLfloat)ximage->width;
362     tex_height[texture_index] = (mi->xgwa.height - 1)  / (GLfloat)ximage->height;
363
364     glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
365     glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
366
367     glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
368     glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
369
370     glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
371
372     free(ximage->data);
373     ximage->data = 0;
374     XDestroyImage (ximage);
375 }
376
377 /******************************************************************************
378  *
379  * Initialise the data used to calculate the blob shape.
380  */
381 static void
382 initialize_gl(ModeInfo *mi, GLsizei width, GLsizei height)
383 {
384     GLfloat fogColor[4] = { 0.1, 0.1, 0.1, 0.1 };
385     /* Lighting values */
386     GLfloat lightPos0[] = {500.0f, 100.0f, 200.0f, 1.0f };
387     GLfloat whiteLight0[] = { 0.1f, 0.1f, 0.1f, 1.0f };
388     GLfloat sourceLight0[] = { 1.0f, 1.0f, 1.0f, 1.0f };
389     GLfloat specularLight0[] = { 0.7f, 0.6f, 0.3f, 1.0f };
390
391     GLfloat lightPos1[] = {0.0f, -500.0f, 500.0f, 1.0f };
392     GLfloat whiteLight1[] = { 0.1f, 0.1f, 0.1f, 1.0f };
393     GLfloat sourceLight1[] = { 1.0f, 0.3f, 0.3f, 1.0f };
394     GLfloat specularLight1[] = { 0.7f, 0.6f, 0.3f, 1.0f };
395
396     GLfloat specref[] = { 1.0f, 1.0f, 1.0f, 1.0f };
397
398     /* Setup our viewport. */
399     glViewport (0, 0, width, height ); 
400
401     glEnable(GL_DEPTH_TEST);
402
403     if (do_antialias)
404     {
405         do_blend = 1;
406         glEnable(GL_LINE_SMOOTH);
407     }
408
409     /* The blend function is used for trasitioning between two images even when
410      * blend is not selected.
411      */
412     glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
413
414     if (do_wire)
415     {
416         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
417     }
418     else
419     {
420         glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
421     }
422
423     if (do_fog)
424     {
425         glEnable(GL_FOG);
426         glFogi(GL_FOG_MODE, GL_LINEAR);
427         glFogfv(GL_FOG_COLOR, fogColor);
428         glFogf(GL_FOG_DENSITY, 0.35);
429         glFogf(GL_FOG_START, 2.0);
430         glFogf(GL_FOG_END, 10.0);
431     }
432
433     /* Our shading model--Gouraud (smooth). */
434     glShadeModel (GL_SMOOTH);
435
436     /* Culling. */
437     glCullFace (GL_BACK);
438     glEnable (GL_CULL_FACE);
439     glEnable (GL_DEPTH_TEST);
440     glFrontFace (GL_CCW);
441
442     /* Set the clear color. */
443     glClearColor( 0, 0, 0, 0 );
444
445     glViewport( 0, 0, width, height );
446
447     glLightfv (GL_LIGHT0, GL_AMBIENT, whiteLight0);
448     glLightfv (GL_LIGHT0, GL_DIFFUSE, sourceLight0);
449     glLightfv (GL_LIGHT0, GL_SPECULAR, specularLight0);
450     glLightfv (GL_LIGHT0, GL_POSITION, lightPos0);
451     glEnable (GL_LIGHT0);
452     glLightfv (GL_LIGHT1, GL_AMBIENT, whiteLight1);
453     glLightfv (GL_LIGHT1, GL_DIFFUSE, sourceLight1);
454     glLightfv (GL_LIGHT1, GL_SPECULAR, specularLight1);
455     glLightfv (GL_LIGHT1, GL_POSITION, lightPos1);
456     glEnable (GL_LIGHT1);
457     glEnable (GL_LIGHTING);
458
459     /* Enable color tracking */
460     glEnable (GL_COLOR_MATERIAL);
461
462     /* Set Material properties to follow glColor values */
463     glColorMaterial (GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
464
465     /* Set all materials to have specular reflectivity */
466     glMaterialfv (GL_FRONT, GL_SPECULAR, specref);
467     glMateriali (GL_FRONT, GL_SHININESS, 64);
468
469     glEnable (GL_NORMALIZE);
470
471     /* Enable Arrays */
472     if (do_texture)
473     {
474         glLightModeli (GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR);
475
476         glEnable (GL_TEXTURE_2D);
477         current_texture = 0;
478         glGenTextures (NUM_TEXTURES, textures);
479         grab_texture (mi, current_texture);
480
481         glEnableClientState (GL_TEXTURE_COORD_ARRAY);
482     }
483
484     if (do_colour)
485     {
486         glEnableClientState (GL_COLOR_ARRAY);
487     }
488     glEnableClientState (GL_NORMAL_ARRAY);
489     glEnableClientState (GL_VERTEX_ARRAY);
490
491     /* Clear the buffer since this is not done during a draw with motion blur */
492     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
493 }
494
495 /******************************************************************************
496  *
497  * Calculate the normal vector for a plane given three points in the plane.
498  */
499 static void
500 calculate_normal (Vector3D point1,
501                   Vector3D point2,
502                   Vector3D point3,
503                   Vector3D *normal)
504 {
505     Vector3D vector1, vector2;
506     double magnitude;
507
508     vector1.x = point2.x - point1.x;
509     vector1.y = point2.y - point1.y;
510     vector1.z = point2.z - point1.z;
511
512     vector2.x = point3.x - point2.x;
513     vector2.y = point3.y - point2.y;
514     vector2.z = point3.z - point2.z;
515
516     (*normal).x = vector1.y * vector2.z - vector1.z * vector2.y;
517     (*normal).y = vector1.z * vector2.x - vector1.x * vector2.z;
518     (*normal).z = vector1.x * vector2.y - vector1.y * vector2.x;
519
520     /* Adjust the normal to unit magnitude */
521     magnitude = sqrt ((*normal).x * (*normal).x
522                       + (*normal).y * (*normal).y
523                       + (*normal).z * (*normal).z);
524
525     /* Watch out for divide by zero/underflow */
526     if (magnitude > 1e-300)
527     {
528         (*normal).x /= magnitude;
529         (*normal).y /= magnitude;
530         (*normal).z /= magnitude;
531     }
532 }
533
534 /******************************************************************************
535  *
536  * Initialise the data required to draw the blob allocating the memory as
537  * necessary.
538  *
539  * Return 0 on success.
540  */
541 static int
542 initialise_blob(int width,
543                 int height,
544                 int field_array_size)
545 {
546     /* Loop variables */
547     int x, y, i;
548     double xd;
549
550     colour_cycle = 0;
551
552     row_data = (Row_Data *) malloc (y_resolution * sizeof (Row_Data));
553     if (!row_data)
554     {
555         fprintf(stderr, "Couldn't allocate row data buffer\n");
556         return -1;
557     }
558
559     field_data = (Field_Data *) malloc (field_points * sizeof (Field_Data));
560     if (!field_data)
561     {
562         fprintf(stderr, "Couldn't allocate field data buffer\n");
563         return -1;
564     }
565
566     field = (double *)malloc(field_array_size * sizeof(double));
567     if (!field)
568     {
569         fprintf(stderr, "Couldn't allocate field buffer\n");
570         return -1;
571     }
572
573     wall_field = (double *)malloc(field_array_size * sizeof(double));
574     if (!wall_field)
575     {
576         fprintf(stderr, "Couldn't allocate wall field buffer\n");
577         return -1;
578     }
579
580     dots = (Vector3D *)malloc(x_resolution * y_resolution * sizeof(Vector3D));
581     if (!dots)
582     {
583         fprintf(stderr, "Couldn't allocate points buffer\n");
584         return -1;
585     }
586     glVertexPointer (3, GL_DOUBLE, 0, (GLvoid *) dots);
587
588     normals = (Vector3D *)malloc(x_resolution * y_resolution * sizeof(Vector3D));
589     if (!normals)
590     {
591         fprintf(stderr, "Couldn't allocate normals buffer\n");
592         return -1;
593     }
594     glNormalPointer (GL_DOUBLE, 0, (GLvoid *) normals);
595
596     if (do_colour)
597     {
598         colours = (Colour *)malloc(x_resolution * y_resolution * sizeof(Colour));
599         if (!colours)
600         {
601             fprintf(stderr, "Couldn't allocate colours buffer\n");
602             return -1;
603         }
604         glColorPointer (4, GL_UNSIGNED_BYTE, 0, (GLvoid *) colours);
605     }
606
607     if (do_texture)
608     {
609         tex_coords = (Vector2D *)malloc(x_resolution * y_resolution
610                                         * sizeof(Vector2D));
611         if (!tex_coords)
612         {
613             fprintf(stderr, "Couldn't allocate tex_coords buffer\n");
614             return -1;
615         }
616         glTexCoordPointer (2, GL_DOUBLE, 0, (GLvoid *) tex_coords);
617     }
618
619     num_points = 0;
620     /* Generate constant row data and count of total number of points */
621     for (y = 0; y < y_resolution; y++)
622     {
623         row_data[y].cosyd = cos(PI * (double)(y * (y_resolution + 1))
624                                 / (double)(y_resolution * y_resolution));
625         row_data[y].sinyd = sin(PI * (double)(y * (y_resolution + 1))
626                                 / (double)(y_resolution * y_resolution));
627         row_data[y].num_x_points = (int)(x_resolution * row_data[y].sinyd + 1.0);
628         num_points += row_data[y].num_x_points;
629     }
630
631     /* Initialise field data */
632     for (i = 0; i < field_points; i++)
633     {
634         field_data[i].ax = 2.0 * (((double)random() / (double)RAND_MAX) - 0.5);
635         field_data[i].ay = 2.0 * (((double)random() / (double)RAND_MAX) - 0.5);
636         field_data[i].apower = (((double)random() / (double)RAND_MAX) - 0.5);
637
638         field_data[i].pos.x = 1.5 * sin(PI * field_data[i].ay)
639             * cos(PI *  field_data[i].ax);
640         field_data[i].pos.y = 1.5 * cos(PI * field_data[i].ay);
641         field_data[i].pos.z = 1.5 * sin(PI * field_data[i].ay)
642             * sin(PI *  field_data[i].ax);
643
644         field_data[i].cx = 2.0 * (((double)random() / (double)RAND_MAX) - 0.5);
645         field_data[i].cy = 2.0 * (((double)random() / (double)RAND_MAX) - 0.5);
646         field_data[i].cpower = (((double)random() / (double)RAND_MAX) - 0.5);
647
648         field_data[i].vx = 0.0;
649         field_data[i].vy = 0.0;
650         field_data[i].vpower = 0.0;
651
652         field_data[i].mx = 0.003 * ((double)random() / (double)RAND_MAX);
653         field_data[i].my = 0.003 * ((double)random() / (double)RAND_MAX);
654         field_data[i].mpower = 0.003 * ((double)random() / (double)RAND_MAX);
655     }
656
657     /* Initialise lookup table of field strength */
658     for (i = 0; i < field_array_size; i++)
659     {
660         xd = 2.0 * (((double)i / (double)field_array_size));
661
662         xd = 3.0 * xd * xd * xd * xd;
663         field[i] = 0.4 / (field_points * (xd + 0.1));
664
665         xd = 10.0 * (((double)i / (double)field_array_size));
666         wall_field[i] = 0.4 / (xd * xd * xd * xd + 1.0);
667     }
668
669     for (y = 0; y < y_resolution; y++)
670     {
671         for (x = 0; x < row_data[y].num_x_points; x++)
672         {
673             i = x + y * x_resolution;
674             xd = 2.0 * (((double)x / (double)row_data[y].num_x_points) - 0.5);
675
676             dots[i].x = row_data[y].sinyd * cos(PI * xd);
677             dots[i].y = row_data[y].cosyd;
678             dots[i].z = row_data[y].sinyd * sin(PI * xd);
679             normals[i].x = row_data[y].sinyd * cos(PI * xd);
680             normals[i].y = row_data[y].cosyd;
681             normals[i].z = row_data[y].sinyd * sin(PI * xd);
682             if (do_texture)
683             {
684                 tex_coords[i].x = 2.0 - 2.0 * x / (float) row_data[y].num_x_points;
685                 tex_coords[i].y = 1.0 - y / (float) y_resolution;
686             }
687         }
688     }
689     return 0;
690 }
691
692
693 /******************************************************************************
694  *
695  * Calculate the blob shape.
696  */
697 static void
698 calc_blob(int width,
699           int height,
700           int field_array_size,
701           float limit,
702           double fade)
703 {
704     static double freak = 0.0;
705
706     static double v_freak = 0.0007;
707
708     /* Loop variables */
709     int x, y, i, index, index1, index2, index3;
710     /* position of a point */
711     double xd, yd, zd, offset_x, offset_y, offset_z;
712     double strength, radius;
713     double xdist, ydist, zdist;
714     int dist;
715
716     /* Color components */
717
718     colour_cycle++;
719
720     /* Update position and strength of points used to distort the blob */
721     for (i = 0; i < field_points; i++)
722     {
723         field_data[i].vx += field_data[i].mx*(field_data[i].cx - field_data[i].ax);
724         field_data[i].vy += field_data[i].my*(field_data[i].cy - field_data[i].ay);
725         field_data[i].vpower += field_data[i].mpower
726             * (field_data[i].cpower - field_data[i].apower);
727
728         field_data[i].ax += 0.1 * field_data[i].vx;
729         field_data[i].ay += 0.1 * field_data[i].vy;
730         field_data[i].apower += 0.1 * field_data[i].vpower;
731
732         field_data[i].pos.x = 1.0 * sin(PI * field_data[i].ay)
733             * cos(PI * field_data[i].ax);
734         field_data[i].pos.y = 1.0 * cos(PI * field_data[i].ay);
735         field_data[i].pos.z = 1.0 * sin(PI * field_data[i].ay)
736             * sin(PI * field_data[i].ax);
737     }
738
739     blob_force.x = 0.0;
740     blob_force.y = 0.0;
741     blob_force.z = 0.0;
742     for (y = 0; y < y_resolution; y++)
743     {
744         for (x = 0; x < row_data[y].num_x_points; x++)
745         {
746             index = x + y * x_resolution;
747             xd = 2.0 * PI * (((double)x / (double)row_data[y].num_x_points) - 0.5);
748
749             radius = 1.0 + 0.0 * sin (xd * 10);
750
751             zd = radius * row_data[y].sinyd * sin(xd);
752             xd = radius * row_data[y].sinyd * cos(xd);
753             yd = radius * row_data[y].cosyd;
754
755             normals[index].x = xd;
756             normals[index].y = yd;
757             normals[index].z = zd;
758
759             offset_x = 0.0;
760             offset_y = 0.0;
761             offset_z = 0.0;
762             strength = 0.0;
763             for ( i = 0; i < field_points; i++)
764             {
765                 xdist = field_data[i].pos.x - xd;
766                 ydist = field_data[i].pos.y - yd;
767                 zdist = field_data[i].pos.z - zd;
768                 dist = field_array_size * (xdist * xdist + ydist * ydist
769                                            + zdist * zdist) * 0.1;
770
771                 strength += PI * field_data[i].apower;
772
773                 if (dist < field_array_size)
774                 {
775                     offset_x += xd * field_data[i].apower * field[dist];
776                     offset_y += yd * field_data[i].apower * field[dist];
777                     offset_z += zd * field_data[i].apower * field[dist];
778
779                     blob_force.x += 1.0 * xd * field_data[i].apower * field[dist];
780                     blob_force.y += 1.0 * yd * field_data[i].apower * field[dist];
781                     blob_force.z += 1.0 * zd * field_data[i].apower * field[dist];
782
783                     strength *= 2.0 * field[dist];
784                 }
785
786                 if (incremental)
787                 {
788                     xd += offset_x * freak * freak;
789                     yd += offset_y * freak * freak;
790                     zd += offset_z * freak * freak;
791                 }
792                 if (incremental == 1)
793                 {
794                     offset_x = 0.0;
795                     offset_y = 0.0;
796                     offset_z = 0.0;
797                 }
798             }
799
800             if (incremental < 3)
801             {
802                 xd += offset_x;
803                 yd += offset_y;
804                 zd += offset_z;
805             }
806             xd += blob_center.x;
807             yd += blob_center.y;
808             zd += blob_center.z;
809
810             if (do_colour)
811             {
812                 colours[index].red = 128 + (int)(sin(strength + colour_cycle * 0.01 + 2.0 * PI * x / row_data[y].num_x_points) * 127.0);
813                 colours[index].green = 128 + (int)(cos(strength + colour_cycle * 0.025) * 127.0);
814                 colours[index].blue = 128 + (int)(sin(strength + colour_cycle * 0.03 + 2.0 * PI * y / y_resolution) * 127.0);
815                 colours[index].alpha = (int)(255.0 * fade);
816             }
817
818             /* Add walls */
819             if (do_walls)
820             {
821                 if (zd < -limit) zd = -limit;
822                 if (zd > limit) zd = limit;
823
824                 dist = field_array_size * (zd + limit) * (zd + limit) * 0.5;
825                 if (dist < field_array_size)
826                 {
827                     xd += (xd - blob_center.x) * wall_field[dist];
828                     yd += (yd - blob_center.y) * wall_field[dist];
829                     blob_force.z += (zd + limit);
830                 }
831                 else
832                 {
833                     dist = field_array_size * (zd - limit) * (zd - limit) * 0.5;
834                     if (dist < field_array_size)
835                     {
836                         xd += (xd - blob_center.x) * wall_field[dist];
837                         yd += (yd - blob_center.y) * wall_field[dist];
838                         blob_force.z -= (zd - limit);
839                     }
840
841                     if (yd < -limit) yd = -limit;
842                     if (yd > limit) yd = limit;
843
844                     dist = field_array_size * (yd + limit) * (yd + limit) * 0.5;
845                     if (dist < field_array_size)
846                     {
847                         xd += (xd - blob_center.x) * wall_field[dist];
848                         zd += (zd - blob_center.z) * wall_field[dist];
849                         blob_force.y += (yd + limit);
850                     }
851                     else
852                     {
853                         dist = field_array_size * (yd - limit) * (yd - limit) * 0.5;
854                         if (dist < field_array_size)
855                         {
856                             xd += (xd - blob_center.x) * wall_field[dist];
857                             zd += (zd - blob_center.z) * wall_field[dist];
858                             blob_force.y -= (yd - limit);
859                         }
860                     }
861
862                     if (xd < -limit) xd = -limit;
863                     if (xd > limit) xd = limit;
864
865                     dist = field_array_size * (xd + limit) * (xd + limit) * 0.5;
866                     if (dist < field_array_size)
867                     {
868                         yd += (yd - blob_center.y) * wall_field[dist];
869                         zd += (zd - blob_center.z) * wall_field[dist];
870                         blob_force.x += (xd + limit);
871                     }
872                     else
873                     {
874                         dist = field_array_size * (xd - limit) * (xd - limit) * 0.5;
875                         if (dist < field_array_size)
876                         {
877                             yd += (yd - blob_center.y) * wall_field[dist];
878                             zd += (zd - blob_center.z) * wall_field[dist];
879                             blob_force.x -= (xd - limit);
880                         }
881                     }
882
883                     if (yd < -limit) yd = -limit;
884                     if (yd > limit) yd = limit;
885                 }
886             }
887
888             dots[index].x = xd;
889             dots[index].y = yd;
890             dots[index].z = zd;
891         }
892     }
893
894     /* Calculate the normals for each vertex and the texture mapping if required.
895      * Although the code actually calculates the normal for one of the triangles
896      * attached to a vertex rather than the vertex itself the results are not too
897      * bad for with a reasonable number of verticies.
898      */
899
900     /* The first point is treated as a special case since the loop expects to use
901      * points in the previous row to form the triangle.
902      */
903     index1 = 0;
904     index2 = y * x_resolution;
905     index3 = 1 + y * x_resolution;
906     calculate_normal (dots[index1], dots[index2], dots[index3], &normals[index1]);
907     if (do_texture)
908     {
909         if (offset_texture)
910         {
911             tex_coords[index1].x = dots[index1].x * 0.125 + 0.5
912                 * (1.0 + 0.25 * asin(normals[index1].x) / (0.5 * PI));
913             tex_coords[index1].y = dots[index1].y * 0.125 + 0.5
914                 * (1.0 + 0.25 * asin(normals[index1].y) / (0.5 * PI));
915         }
916         else
917         {
918             tex_coords[index1].x = 0.5 * (1.0 + (asin(normals[index1].x)
919                                                  / (0.5 * PI)));
920             tex_coords[index1].y = 0.5 * (1.0 + (asin(normals[index1].y)
921                                                  / (0.5 * PI)));
922         }
923         tex_coords[index1].x *= tex_width[current_texture];
924         tex_coords[index1].y *= tex_height[current_texture];
925     }
926
927     for (y = 1; y < y_resolution - 1; y++)
928     {
929         if (row_data[y - 1].num_x_points)
930         {
931             for (x = 0; x < row_data[y].num_x_points; x++)
932             {
933                 if (x == row_data[y].num_x_points - 1)
934                 {
935                     index1 = y * x_resolution;
936                 }
937                 else
938                 {
939                     index1 = x + 1 + y * x_resolution;
940                 }
941                 index2 = x + y * x_resolution;
942                 index3 = ((x + 0.5) * row_data[y - 1].num_x_points
943                           / row_data[y].num_x_points) + (y - 1) * x_resolution;
944                 calculate_normal (dots[index1], dots[index2], dots[index3],
945                                   &normals[index1]);
946                 if (do_texture)
947                 {
948                     if (offset_texture)
949                     {
950                         tex_coords[index1].x = dots[index1].x * 0.125 + 0.5
951                             * (1.0 + 0.25 * asin(normals[index1].x) / (0.5 * PI));
952                         tex_coords[index1].y = dots[index1].y * 0.125 + 0.5
953                             * (1.0 + 0.25 * asin(normals[index1].y) / (0.5 * PI));
954                     }
955                     else
956                     {
957                         tex_coords[index1].x = 0.5 * (1.0 + (asin(normals[index1].x)
958                                                              / (0.5 * PI)));
959                         tex_coords[index1].y = 0.5 * (1.0 + (asin(normals[index1].y)
960                                                              / (0.5 * PI)));
961                     }
962                     tex_coords[index1].x *= tex_width[current_texture];
963                     tex_coords[index1].y *= tex_height[current_texture];
964                 }
965             }
966         }
967     }
968     index1 = (y_resolution - 1) * x_resolution;
969     index2 = (y_resolution - 2) * x_resolution;
970     index3 = 1 + (y_resolution - 2) * x_resolution;
971     calculate_normal (dots[index1], dots[index2], dots[index3], &normals[index1]);
972     if (do_texture)
973     {
974         if (offset_texture)
975         {
976             tex_coords[index1].x = dots[index1].x * 0.125 + 0.5
977                 * (1.0 + 0.25 * asin(normals[index1].x) / (0.5 * PI));
978             tex_coords[index1].y = dots[index1].y * 0.125 + 0.5
979                 * (1.0 + 0.25 * asin(normals[index1].y) / (0.5 * PI));
980         }
981         else
982         {
983             tex_coords[index1].x = 0.5 * (1.0 + (asin(normals[index1].x)
984                                                  / (0.5 * PI)));
985             tex_coords[index1].y = 0.5 * (1.0 + (asin(normals[index1].y)
986                                                  / (0.5 * PI)));
987         }
988         tex_coords[index1].x *= tex_width[current_texture];
989         tex_coords[index1].y *= tex_height[current_texture];
990     }
991
992
993     freak += v_freak;
994     v_freak += -freak / 2000000.0;
995
996     /* Update the center of the whole blob */
997     blob_velocity.x += (blob_anchor.x - blob_center.x) / 80.0
998         + 0.01 * blob_force.x / num_points;
999     blob_velocity.y += (blob_anchor.y - blob_center.y) / 80.0
1000         + 0.01 * blob_force.y / num_points;
1001     blob_velocity.z += (blob_anchor.z - blob_center.z) / 80.0
1002         + 0.01 * blob_force.z / num_points;
1003
1004     blob_center.x += blob_velocity.x * 0.5;
1005     blob_center.y += blob_velocity.y * 0.5;
1006     blob_center.z += blob_velocity.z * 0.5;
1007
1008     blob_velocity.x *= 0.99;
1009     blob_velocity.y *= 0.99;
1010     blob_velocity.z *= 0.99;
1011 }
1012
1013 /******************************************************************************
1014  *
1015  * Draw the blob shape.
1016  *
1017  * The horrendous indexing to calculate the verticies that form a particular
1018  * traiangle is the result of the conversion of my first non-openGL version of
1019  * blob to this openGL version.  This may be tidied up when I finally playing
1020  * with the more interesting bits of the code.
1021  */
1022 static void
1023 draw_blob (void)
1024 {
1025     int x, y, x2, x3;
1026     int index1, index2, index3;
1027     int lower, upper;
1028
1029     glMatrixMode (GL_MODELVIEW);
1030     glLoadIdentity ();
1031
1032     /* Move down the z-axis. */
1033     glTranslatef (0.0, 0.0, -5.0 );
1034
1035     for (y = 1; y < y_resolution; y++)
1036     {
1037         if (row_data[y - 1].num_x_points)
1038         {
1039             for (x = 0; x < row_data[y].num_x_points; x++)
1040             {
1041                 glBegin (GL_TRIANGLES);
1042                 if (x == row_data[y].num_x_points - 1)
1043                 {
1044                     index1 = y * x_resolution;
1045                 }
1046                 else
1047                 {
1048                     index1 = x + 1 + y * x_resolution;
1049                 }
1050                 index2 = x + y * x_resolution;
1051                 index3 = ((x + 0.5) * row_data[y - 1].num_x_points
1052                           / row_data[y].num_x_points) + (y - 1) * x_resolution;
1053                 glArrayElement(index1);
1054                 glArrayElement(index2);
1055                 glArrayElement(index3);
1056                 glEnd();
1057
1058                 lower = ((x - 0.5) * row_data[y - 1].num_x_points
1059                          / (float)row_data[y].num_x_points);
1060                 upper = ((x + 0.5) * row_data[y - 1].num_x_points
1061                          / (float)row_data[y].num_x_points);
1062
1063                 if (upper > lower)
1064                 {
1065                     glBegin (GL_TRIANGLE_FAN);
1066                     index1 = x + y * x_resolution;
1067
1068                     for (x2 = lower; x2 <= upper; x2++)
1069                     {
1070                         x3 = x2;
1071                         while (x3 < 0) x3 += row_data[y - 1].num_x_points;
1072                         while (x3 >= row_data[y - 1].num_x_points)
1073                             x3 -= row_data[y - 1].num_x_points;
1074                         index2 = x3 + (y - 1) * x_resolution;
1075
1076                         if (x2 < upper)
1077                         {
1078                             x3 = x2 + 1;
1079                             while (x3 < 0) x3 += row_data[y - 1].num_x_points;
1080                             while (x3 >= row_data[y - 1].num_x_points)
1081                                 x3 -= row_data[y - 1].num_x_points;
1082                             index3 = x3 + (y - 1) * x_resolution;
1083                             if (x2 == lower)
1084                             {
1085                                 glArrayElement(index1);
1086                             }
1087                         }
1088                         glArrayElement(index2);
1089                     }
1090                     glEnd ();
1091                 }
1092             }
1093         }
1094     }
1095 }
1096
1097 /******************************************************************************
1098  *
1099  * Draw the background image simply map a texture onto a full screen quad.
1100  */
1101 static void
1102 draw_background (ModeInfo *mi)
1103 {
1104     glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
1105     glEnable (GL_TEXTURE_2D);
1106     glDisable(GL_LIGHTING);
1107
1108     /* Reset the projection matrix to make it easier to get the size of the quad
1109      * correct
1110      */
1111     glMatrixMode(GL_PROJECTION);
1112     glPushMatrix();
1113     glLoadIdentity();
1114
1115     glOrtho(0.0, MI_WIDTH(mi), MI_HEIGHT(mi), 0.0, -1000.0, 1000.0);
1116
1117     glBegin (GL_QUADS);
1118     
1119     glTexCoord2f (0.0, tex_height[current_texture]);
1120     glVertex2i (0, 0);
1121     
1122     glTexCoord2f (0.0, 0.0);
1123     glVertex2i (0, MI_HEIGHT(mi));
1124
1125     glTexCoord2f (tex_width[current_texture], 0.0);
1126     glVertex2i (MI_WIDTH(mi), MI_HEIGHT(mi));
1127
1128     glTexCoord2f (tex_width[current_texture], tex_height[current_texture]);
1129     glVertex2i (MI_WIDTH(mi), 0);
1130     glEnd();
1131
1132     glPopMatrix ();
1133     glMatrixMode (GL_MODELVIEW);
1134     glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
1135 }
1136
1137 /******************************************************************************
1138  *
1139  * Update the scene.
1140  */
1141 static GLvoid
1142 draw_scene(ModeInfo * mi)
1143 {
1144     double fade = 0.0;
1145     double current_time;
1146     check_gl_error ("draw_scene");
1147
1148     glColor4d(1.0, 1.0, 1.0, 1.0);
1149
1150     current_time = double_time();
1151     switch (state)
1152     {
1153     case TRANSITIONING:
1154         fade = (current_time - state_start_time) / fade_time;
1155         break;
1156
1157     case HOLDING:
1158         fade = 0.0;
1159         break;
1160     }
1161
1162
1163     /* Set the correct texture, when transitioning this ensures that the first draw
1164      * is the original texture (which has the new texture drawn over it with decreasing
1165      * transparency)
1166      */
1167     if (do_texture)
1168     {
1169       glBindTexture (GL_TEXTURE_2D, textures[current_texture]);
1170     }
1171
1172     if (do_paint_background && !do_wire)
1173     {
1174         glClear(GL_DEPTH_BUFFER_BIT);
1175         if (motion_blur)
1176         {
1177             glEnable (GL_BLEND);
1178             glColor4ub (255, 255, 255, motion_blur);
1179         }
1180         draw_background (mi);
1181
1182         /* When transitioning between two images paint the new image over the old
1183          * image with a varying alpha value to get a smooth fade.
1184          */
1185         if (state == TRANSITIONING)
1186         {
1187             glDisable (GL_DEPTH_TEST);
1188             glEnable (GL_BLEND);
1189             /* Select the texture to transition to */
1190             glBindTexture (GL_TEXTURE_2D, textures[1 - current_texture]);
1191             glColor4d (1.0, 1.0, 1.0, fade);
1192
1193             draw_background (mi);
1194
1195             /* Select the original texture to draw the blob */
1196             glBindTexture (GL_TEXTURE_2D, textures[current_texture]);
1197             glEnable (GL_DEPTH_TEST);
1198         }
1199         /* Clear the depth buffer bit so the backgound is behind the blob */
1200         glClear(GL_DEPTH_BUFFER_BIT);
1201     }
1202     else if (motion_blur)
1203     {
1204         glDisable (GL_DEPTH_TEST);
1205         glEnable (GL_BLEND);
1206         glColor4ub (0, 0, 0, motion_blur);
1207         glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
1208         glRectd (-10.0, -10.0, 10.0, 10.0);
1209         if (do_wire)
1210         {
1211             glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
1212         }
1213         glEnable (GL_DEPTH_TEST);
1214         glClear (GL_DEPTH_BUFFER_BIT);
1215     }
1216     else
1217     {
1218         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1219     }
1220
1221     if (do_blend)
1222     {
1223         fade = fade * 0.5;
1224     }
1225
1226     calc_blob(MI_WIDTH(mi), MI_HEIGHT(mi), 1024, 2.5, fade);
1227
1228     glEnable(GL_LIGHTING);
1229     glEnable(GL_LIGHT0);
1230     glEnable(GL_LIGHT1);
1231
1232     if (do_blend)
1233     {
1234         glEnable (GL_BLEND);
1235         if (do_colour)
1236         {
1237             glBlendFunc (GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA);
1238         }
1239         else
1240         {
1241             glColor4d (1.0, 1.0, 1.0, 0.5 - fade);
1242         }
1243     }
1244     else
1245     {
1246         glDisable (GL_BLEND);
1247         glColor4d (1.0, 1.0, 1.0, 1.0);
1248     }
1249     draw_blob();
1250
1251     if (do_blend && do_colour)
1252     {
1253         glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1254     }
1255
1256     /* While transitioning draw a second blob twice with a modified alpha channel.
1257      * The trasitioning state machine is very crude, it simply counts frames
1258      * rather than elapsed time but it works.
1259      */
1260     if (do_texture && (hold_time > 0))
1261     {
1262         switch (state)
1263         {
1264         case TRANSITIONING:
1265             glClear(GL_DEPTH_BUFFER_BIT);
1266             glEnable (GL_BLEND);
1267             /* Select the texture to transition to */
1268             glBindTexture (GL_TEXTURE_2D, textures[1 - current_texture]);
1269             glColor4d (1.0, 1.0, 1.0, fade);
1270             draw_blob ();
1271
1272             if ((current_time - state_start_time) > fade_time)
1273             {
1274                 state = HOLDING;
1275                 state_start_time = current_time;
1276                 current_texture = 1 - current_texture;
1277             }
1278             break;
1279
1280         case HOLDING:
1281             if ((current_time - state_start_time) > hold_time)
1282             {
1283                 grab_texture (mi, 1 - current_texture);
1284                 state = TRANSITIONING;
1285                 /* Get the time again rather than using the current time so
1286                  * that the time taken by the grab_texture function is not part
1287                  * of the fade time
1288                  */
1289                 state_start_time = double_time();
1290             }
1291             break;
1292         }
1293     }
1294 }
1295
1296 /******************************************************************************
1297  *
1298  * XScreensaver screen update entry
1299  */
1300 void
1301 draw_screensaver(ModeInfo * mi)
1302 {
1303     screensaverstruct *gp = &Screensaver[MI_SCREEN(mi)];
1304     Display    *display = MI_DISPLAY(mi);
1305     Window      window = MI_WINDOW(mi);
1306
1307     if (!gp->glx_context)
1308         return;
1309
1310     glXMakeCurrent(display, window, *(gp->glx_context));
1311     draw_scene(mi);
1312     if (mi->fps_p) do_fps (mi);
1313     glXSwapBuffers(display, window);
1314 }
1315
1316 /******************************************************************************
1317  *
1318  * XScreensaver screen resize entry
1319  */
1320 void
1321 reshape_screensaver(ModeInfo *mi, int width, int height)
1322 {
1323     glViewport( 0, 0, MI_WIDTH(mi), MI_HEIGHT(mi) );
1324     reset_projection(width, height);
1325 }
1326
1327 /******************************************************************************
1328  *
1329  * XScreensaver initialise entry
1330  */
1331 void
1332 init_screensaver(ModeInfo * mi)
1333 {
1334     int screen = MI_SCREEN(mi);
1335
1336     screensaverstruct *gp;
1337
1338     if (Screensaver == NULL)
1339     {
1340         if ((Screensaver = (screensaverstruct *)
1341              calloc(MI_NUM_SCREENS(mi), sizeof (screensaverstruct))) == NULL)
1342         {
1343             return;
1344         }
1345     }
1346     gp = &Screensaver[screen];
1347
1348     gp->window = MI_WINDOW(mi);
1349     if ((gp->glx_context = init_GL(mi)) != NULL)
1350     {
1351         reshape_screensaver(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
1352         initialize_gl(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
1353     }
1354     else
1355     {
1356         MI_CLEARWINDOW(mi);
1357     }
1358
1359     initialise_blob(MI_WIDTH(mi), MI_HEIGHT(mi), 1024);
1360     state_start_time = double_time();
1361 }
1362
1363 /******************************************************************************
1364  *
1365  * XScreensaver cleanup entry
1366  */
1367 void
1368 release_screensaver(ModeInfo * mi)
1369 {
1370     if (row_data) free(row_data);
1371     if (field_data) free(field_data);
1372     if (colours) free(colours);
1373     if (tex_coords) free(tex_coords);
1374     if (dots) free(dots);
1375     if (wall_field) free(wall_field);
1376     if (field) free(field);
1377
1378     if (Screensaver != NULL)
1379     {
1380         (void) free((void *) Screensaver);
1381         Screensaver = NULL;
1382     }
1383     FreeAllGL(mi);
1384 }
1385 #endif