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