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