c6a5c84d2f9d96870de85d644520ef9c71341e0b
[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 load_texture_async), 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 xmirrorblob 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
36 #ifdef STANDALONE
37 #define DEFAULTS \
38     "*delay:             " DEF_DELAY "\n" \
39     "*showFPS:           " DEF_FPS   "\n" \
40     "*useSHM:              True      \n"
41
42 # define refresh_mirrorblob 0
43 # define mirrorblob_handle_event 0
44 # include "xlockmore.h"    /* from the xmirrorblob distribution */
45 #else /* !STANDALONE */
46 # include "xlock.h"        /* from the xlockmore distribution */
47 #endif /* !STANDALONE */
48
49 #ifdef USE_GL /* whole file */
50
51
52 #define DEF_DELAY            "10000"
53 #define DEF_FPS              "False"
54 #define DEF_WIRE             "False"
55 #define DEF_BLEND            "False"
56 #define DEF_FOG              "False"
57 #define DEF_ANTIALIAS        "False"
58 #define DEF_WALLS            "True"
59 #define DEF_COLOUR           "False"
60 #define DEF_TEXTURE          "True"
61 #define DEF_OFFSET_TEXTURE   "False"
62 #define DEF_PAINT_BACKGROUND "True"
63 #define DEF_X_RES            "60"
64 #define DEF_Y_RES            "32"
65 #define DEF_FIELD_POINTS     "5"
66 #define DEF_MOTION_BLUR      "0"
67 #define DEF_INCREMENTAL      "0"
68 #define DEF_HOLD_TIME        "30"
69 #define DEF_FADE_TIME        "5"
70
71 #ifdef HAVE_XMU
72 # ifndef VMS
73 #  include <X11/Xmu/Drawing.h>
74 #else  /* VMS */
75 #  include <Xmu/Drawing.h>
76 # endif /* VMS */
77 #endif
78
79 #include "grab-ximage.h"
80
81 #undef countof
82 #define countof(x) (sizeof((x)) / sizeof((*x)))
83
84 #define PI  3.1415926535897
85
86 /* */
87 static int do_wire;
88 static int do_blend;
89 static int do_fog;
90 static int do_antialias;
91 static int do_walls;
92 static int do_texture;
93 static int do_paint_background;
94 static int do_colour;
95 static int offset_texture;
96 static int x_resolution;
97 static int y_resolution;
98 static int field_points;
99 static int motion_blur;
100 static int incremental;
101 static int fade_time;
102 static int hold_time;
103
104 static XrmOptionDescRec opts[] = {
105     {"-wireframe",        ".blob.wire",             XrmoptionNoArg, "true" },
106     {"+wireframe",        ".blob.wire",             XrmoptionNoArg, "false" },
107     {"-blend",            ".blob.blend",            XrmoptionNoArg, "true" },
108     {"+blend",            ".blob.blend",            XrmoptionNoArg, "false" },
109     {"-fog",              ".blob.fog",              XrmoptionNoArg, "true" },
110     {"+fog",              ".blob.fog",              XrmoptionNoArg, "false" },
111     {"-antialias",        ".blob.antialias",        XrmoptionNoArg, "true" },
112     {"+antialias",        ".blob.antialias",        XrmoptionNoArg, "false" },
113     {"-walls",            ".blob.walls",            XrmoptionNoArg, "true" },
114     {"+walls",            ".blob.walls",            XrmoptionNoArg, "false" },
115     {"-texture",          ".blob.texture",          XrmoptionNoArg, "true" },
116     {"+texture",          ".blob.texture",          XrmoptionNoArg, "false" },
117     {"-colour",           ".blob.colour",           XrmoptionNoArg, "true" },
118     {"+colour",           ".blob.colour",           XrmoptionNoArg, "false" },
119     {"-offset_texture",   ".blob.offset_texture",   XrmoptionNoArg, "true" },
120     {"+offset_texture",   ".blob.offset_texture",   XrmoptionNoArg, "false" },
121     {"-paint_background", ".blob.paint_background", XrmoptionNoArg, "true" },
122     {"+paint_background", ".blob.paint_background", XrmoptionNoArg, "false" },
123     {"-x_res",            ".blob.x_res",            XrmoptionSepArg, NULL },
124     {"-y_res",            ".blob.y_res",            XrmoptionSepArg, NULL },
125     {"-field_points",     ".blob.field_points",     XrmoptionSepArg, NULL },
126     {"-motion_blur",      ".blob.motion_blur",      XrmoptionSepArg, NULL },
127     {"-incremental",      ".blob.incremental",      XrmoptionSepArg, NULL },
128     {"-fade_time",        ".blob.fade_time",        XrmoptionSepArg, NULL },
129     {"-hold_time",        ".blob.hold_time",        XrmoptionSepArg, NULL },
130 };
131
132 static argtype vars[] = {
133     {&do_wire,      "wire",         "Wire",      DEF_WIRE,      t_Bool},
134     {&do_blend,     "blend",        "Blend",     DEF_BLEND,     t_Bool},
135     {&do_fog,       "fog",          "Fog",       DEF_FOG,       t_Bool},
136     {&do_antialias, "antialias",    "Antialias", DEF_ANTIALIAS, t_Bool},
137     {&do_walls,     "walls",        "Walls",     DEF_WALLS,     t_Bool},
138     {&do_texture,   "texture",      "Texture",   DEF_TEXTURE,   t_Bool},
139     {&do_colour,    "colour",       "Colour",    DEF_COLOUR,   t_Bool},
140     {&offset_texture, "offset_texture","Offset_Texture", DEF_OFFSET_TEXTURE, t_Bool},
141     {&do_paint_background,"paint_background","Paint_Background", DEF_PAINT_BACKGROUND, t_Bool},
142     {&x_resolution, "x_res",        "X_Res",        DEF_X_RES,        t_Int},
143     {&y_resolution, "y_res",        "Y_Res",        DEF_Y_RES,        t_Int},
144     {&field_points, "field_points", "Field_Points", DEF_FIELD_POINTS, t_Int},
145     {&motion_blur,  "motion_blur",  "Motion_Blur",  DEF_MOTION_BLUR,  t_Int},
146     {&incremental,  "incremental",  "Incremental",  DEF_INCREMENTAL,  t_Int},
147     {&fade_time,    "fade_time",    "Fade_Time",    DEF_FADE_TIME,    t_Int},
148     {&hold_time,    "hold_time",    "Hold_Time",    DEF_HOLD_TIME,    t_Int},
149 };
150
151
152 static OptionStruct desc[] =
153 {
154     {"-/+ wire", "whether to do use wireframe instead of filled (faster)"},
155     {"-/+ blend", "whether to do enable blending (slower)"},
156     {"-/+ fog", "whether to do enable fog (slower)"},
157     {"-/+ antialias", "whether to do enable antialiased lines (slower)"},
158     {"-/+ walls", "whether to add walls to the blob space (slower)"},
159     {"-/+ texture", "whether to add a texture to the blob (slower)"},
160     {"-/+ colour", "whether to colour the blob"},
161     {"-/+ offset_texture", "whether to offset texture co-ordinates"},
162     {"-/+ paint_background", "whether to display a background texture (slower)"},
163     {"-x_res", "Blob resolution in x direction"},
164     {"-y_res", "Blob resolution in y direction"},
165     {"-field_points", "Number of field points used to disturb blob"},
166     {"-motion_blur", "Fade blob images (higher number = faster fade)"},
167     {"-incremental", "Field summation method"},
168     {"-fade_time", "Number of frames to transistion to next image"},
169     {"-hold_time", "Number of frames before next image"},
170 };
171
172 ENTRYPOINT ModeSpecOpt mirrorblob_opts = {countof(opts), opts, countof(vars), vars, desc};
173
174 #ifdef USE_MODULES
175 ModStruct   mirrorblob_description =
176 {"mirrorblob", "init_mirrorblob", "draw_mirrorblob", "release_mirrorblob",
177  "draw_mirrorblob", "init_mirrorblob", NULL, &mirrorblob_opts,
178  1000, 1, 2, 1, 4, 1.0, "",
179  "OpenGL mirrorblob", 0, NULL};
180 #endif
181
182 #define NUM_TEXTURES 2
183
184 /*****************************************************************************
185  * Types used in blob code
186  *****************************************************************************/
187
188 typedef struct
189 {
190     GLdouble x, y;
191 } Vector2D;
192
193 typedef struct
194 {
195     GLdouble x, y, z;
196 } Vector3D;
197
198 typedef struct
199 {
200     GLubyte red, green, blue, alpha;
201 } Colour;
202
203 /* Data used for sphere tessellation */
204 typedef struct
205 {
206     double cosyd, sinyd;
207
208     /* Number of x points at each row of the blob */
209     int num_x_points;
210 } Row_Data;
211
212 /* Structure to hold sphere distortion data */
213 typedef struct
214 {
215     double cx, cy, cpower;
216     double mx, my, mpower;
217     double ax, ay, apower;
218     double vx, vy, vpower;
219     Vector3D pos;
220 } Field_Data;
221
222 typedef enum
223 {
224   HOLDING=0,
225   TRANSITIONING
226 } Frame_State;
227
228
229 /* structure for holding the mirrorblob data */
230 typedef struct {
231   int screen_width, screen_height;
232   GLXContext *glx_context;
233   Window window;
234   XColor fg, bg;
235   double freak, v_freak;
236
237   Row_Data *row_data;
238
239   /* Parameters controlling the position of the blob as a whole */
240   Vector3D blob_center;
241   Vector3D blob_anchor;
242   Vector3D blob_velocity;
243   Vector3D blob_force;
244
245   /* Count of the total number of points */
246   int num_points;
247
248   Vector3D *dots;
249   Vector3D *normals;
250   Colour   *colours;
251   Vector2D *tex_coords;
252
253   /* Pointer to the field function results */
254   double *field, *wall_field;
255
256   Field_Data *field_data;
257
258   /* Use 2 textures to allow a gradual fade between images */
259   int current_texture;
260
261   /* Ratio of used texture size to total texture size */
262   GLfloat tex_width[NUM_TEXTURES], tex_height[NUM_TEXTURES];
263   GLuint textures[NUM_TEXTURES];
264
265   Frame_State state;
266   double state_start_time;
267
268   int colour_cycle;
269
270   Bool mipmap_p;
271   Bool waiting_for_image_p;
272   Bool first_image_p;
273
274 } mirrorblobstruct;
275
276 static mirrorblobstruct *Mirrorblob = NULL;
277
278 /******************************************************************************
279  *
280  * Returns the current time in seconds as a double.  Shamelessly borrowed from
281  * glslideshow.
282  *
283  */
284 static double
285 double_time (void)
286 {
287   struct timeval now;
288 # ifdef GETTIMEOFDAY_TWO_ARGS
289   struct timezone tzp;
290   gettimeofday(&now, &tzp);
291 # else
292   gettimeofday(&now);
293 # endif
294
295   return (now.tv_sec + ((double) now.tv_usec * 0.000001));
296 }
297
298 /******************************************************************************
299  *
300  * Change to the projection matrix and set our viewing volume.
301  *
302  */
303 static void
304 reset_projection(int width, int height)
305 {
306     glMatrixMode (GL_PROJECTION);
307     glLoadIdentity ();
308     gluPerspective (60.0, 1.0, 1.0, 1024.0 );
309     glMatrixMode (GL_MODELVIEW);
310     glLoadIdentity ();
311 }
312
313 /****************************************************************************
314  *
315  * Load a texture.
316  */
317
318 static void
319 image_loaded_cb (const char *filename, XRectangle *geometry,
320                  int image_width, int image_height, 
321                  int texture_width, int texture_height,
322                  void *closure)
323 {
324   mirrorblobstruct *mp = (mirrorblobstruct *) closure;
325   GLint texid = -1;
326   int texture_index = -1;
327   int i;
328
329   glGetIntegerv (GL_TEXTURE_BINDING_2D, &texid);
330   if (texid < 0) abort();
331
332   for (i = 0; i < NUM_TEXTURES; i++) {
333     if (mp->textures[i] == texid) {
334       texture_index = i;
335       break;
336     }
337   }
338   if (texture_index < 0) abort();
339
340   mp->tex_width [texture_index] =  (GLfloat) image_width  / texture_width;
341   mp->tex_height[texture_index] = -(GLfloat) image_height / texture_height;
342
343   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
344   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
345                    (mp->mipmap_p ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR));
346   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
347   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
348   glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
349
350   mp->waiting_for_image_p = False;
351   mp->first_image_p = True;
352 }
353
354
355 static void
356 grab_texture(ModeInfo *mi, int texture_index)
357 {
358   mirrorblobstruct *mp = &Mirrorblob[MI_SCREEN(mi)];
359
360   mp->waiting_for_image_p = True;
361   mp->mipmap_p = True;
362   load_texture_async (mi->xgwa.screen, mi->window,
363                       *mp->glx_context, 0, 0, mp->mipmap_p, 
364                       mp->textures[texture_index],
365                       image_loaded_cb, mp);
366 }
367
368 /******************************************************************************
369  *
370  * Initialise the data used to calculate the blob shape.
371  */
372 static void
373 initialize_gl(ModeInfo *mi, GLsizei width, GLsizei height)
374 {
375     mirrorblobstruct *gp = &Mirrorblob[MI_SCREEN(mi)];
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         gp->current_texture = 0;
471         glGenTextures (NUM_TEXTURES, gp->textures);
472         grab_texture (mi, gp->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(mirrorblobstruct *gp,
536                 int width, int height,
537                 int field_array_size)
538 {
539     /* Loop variables */
540     int x, y, i;
541     double xd;
542
543     gp->colour_cycle = 0;
544
545     gp->row_data = (Row_Data *) malloc (y_resolution * sizeof (Row_Data));
546     if (!gp->row_data)
547     {
548         fprintf(stderr, "Couldn't allocate row data buffer\n");
549         return -1;
550     }
551
552     gp->field_data = (Field_Data *) malloc (field_points * sizeof (Field_Data));
553     if (!gp->field_data)
554     {
555         fprintf(stderr, "Couldn't allocate field data buffer\n");
556         return -1;
557     }
558
559     gp->field = (double *)malloc(field_array_size * sizeof(double));
560     if (!gp->field)
561     {
562         fprintf(stderr, "Couldn't allocate field buffer\n");
563         return -1;
564     }
565
566     gp->wall_field = (double *)malloc(field_array_size * sizeof(double));
567     if (!gp->wall_field)
568     {
569         fprintf(stderr, "Couldn't allocate wall field buffer\n");
570         return -1;
571     }
572
573     gp->dots = (Vector3D *)malloc(x_resolution * y_resolution * sizeof(Vector3D));
574     if (!gp->dots)
575     {
576         fprintf(stderr, "Couldn't allocate points buffer\n");
577         return -1;
578     }
579     glVertexPointer (3, GL_DOUBLE, 0, (GLvoid *) gp->dots);
580
581     gp->normals = (Vector3D *)malloc(x_resolution * y_resolution * sizeof(Vector3D));
582     if (!gp->normals)
583     {
584         fprintf(stderr, "Couldn't allocate normals buffer\n");
585         return -1;
586     }
587     glNormalPointer (GL_DOUBLE, 0, (GLvoid *) gp->normals);
588
589     if (do_colour)
590     {
591         gp->colours = (Colour *)malloc(x_resolution * y_resolution * sizeof(Colour));
592         if (!gp->colours)
593         {
594             fprintf(stderr, "Couldn't allocate colours buffer\n");
595             return -1;
596         }
597         glColorPointer (4, GL_UNSIGNED_BYTE, 0, (GLvoid *) gp->colours);
598     }
599
600     if (do_texture)
601     {
602         gp->tex_coords = (Vector2D *)malloc(x_resolution * y_resolution
603                                         * sizeof(Vector2D));
604         if (!gp->tex_coords)
605         {
606             fprintf(stderr, "Couldn't allocate tex_coords buffer\n");
607             return -1;
608         }
609         glTexCoordPointer (2, GL_DOUBLE, 0, (GLvoid *) gp->tex_coords);
610     }
611
612     gp->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         gp->row_data[y].cosyd = cos(PI * (double)(y * (y_resolution + 1))
617                                 / (double)(y_resolution * y_resolution));
618         gp->row_data[y].sinyd = sin(PI * (double)(y * (y_resolution + 1))
619                                 / (double)(y_resolution * y_resolution));
620         gp->row_data[y].num_x_points = (int)(x_resolution * gp->row_data[y].sinyd + 1.0);
621         gp->num_points += gp->row_data[y].num_x_points;
622     }
623
624     /* Initialise field data */
625     for (i = 0; i < field_points; i++)
626     {
627         gp->field_data[i].ax = 2.0 * (((double)random() / (double)RAND_MAX) - 0.5);
628         gp->field_data[i].ay = 2.0 * (((double)random() / (double)RAND_MAX) - 0.5);
629         gp->field_data[i].apower = (((double)random() / (double)RAND_MAX) - 0.5);
630
631         gp->field_data[i].pos.x = 1.5 * sin(PI * gp->field_data[i].ay)
632             * cos(PI *  gp->field_data[i].ax);
633         gp->field_data[i].pos.y = 1.5 * cos(PI * gp->field_data[i].ay);
634         gp->field_data[i].pos.z = 1.5 * sin(PI * gp->field_data[i].ay)
635             * sin(PI *  gp->field_data[i].ax);
636
637         gp->field_data[i].cx = 2.0 * (((double)random() / (double)RAND_MAX) - 0.5);
638         gp->field_data[i].cy = 2.0 * (((double)random() / (double)RAND_MAX) - 0.5);
639         gp->field_data[i].cpower = (((double)random() / (double)RAND_MAX) - 0.5);
640
641         gp->field_data[i].vx = 0.0;
642         gp->field_data[i].vy = 0.0;
643         gp->field_data[i].vpower = 0.0;
644
645         gp->field_data[i].mx = 0.003 * ((double)random() / (double)RAND_MAX);
646         gp->field_data[i].my = 0.003 * ((double)random() / (double)RAND_MAX);
647         gp->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         gp->field[i] = 0.4 / (field_points * (xd + 0.1));
657
658         xd = 10.0 * (((double)i / (double)field_array_size));
659         gp->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 < gp->row_data[y].num_x_points; x++)
665         {
666             i = x + y * x_resolution;
667             xd = 2.0 * (((double)x / (double)gp->row_data[y].num_x_points) - 0.5);
668
669             gp->dots[i].x = gp->row_data[y].sinyd * cos(PI * xd);
670             gp->dots[i].y = gp->row_data[y].cosyd;
671             gp->dots[i].z = gp->row_data[y].sinyd * sin(PI * xd);
672             gp->normals[i].x = gp->row_data[y].sinyd * cos(PI * xd);
673             gp->normals[i].y = gp->row_data[y].cosyd;
674             gp->normals[i].z = gp->row_data[y].sinyd * sin(PI * xd);
675             if (do_texture)
676             {
677                 gp->tex_coords[i].x = 2.0 - 2.0 * x / (float) gp->row_data[y].num_x_points;
678                 gp->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(mirrorblobstruct *gp,
692           int width, int height,
693           int field_array_size,
694           float limit,
695           double fade)
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     gp->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         gp->field_data[i].vx += gp->field_data[i].mx*(gp->field_data[i].cx - gp->field_data[i].ax);
713         gp->field_data[i].vy += gp->field_data[i].my*(gp->field_data[i].cy - gp->field_data[i].ay);
714         gp->field_data[i].vpower += gp->field_data[i].mpower
715             * (gp->field_data[i].cpower - gp->field_data[i].apower);
716
717         gp->field_data[i].ax += 0.1 * gp->field_data[i].vx;
718         gp->field_data[i].ay += 0.1 * gp->field_data[i].vy;
719         gp->field_data[i].apower += 0.1 * gp->field_data[i].vpower;
720
721         gp->field_data[i].pos.x = 1.0 * sin(PI * gp->field_data[i].ay)
722             * cos(PI * gp->field_data[i].ax);
723         gp->field_data[i].pos.y = 1.0 * cos(PI * gp->field_data[i].ay);
724         gp->field_data[i].pos.z = 1.0 * sin(PI * gp->field_data[i].ay)
725             * sin(PI * gp->field_data[i].ax);
726     }
727
728     gp->blob_force.x = 0.0;
729     gp->blob_force.y = 0.0;
730     gp->blob_force.z = 0.0;
731     for (y = 0; y < y_resolution; y++)
732     {
733         for (x = 0; x < gp->row_data[y].num_x_points; x++)
734         {
735             index = x + y * x_resolution;
736             xd = 2.0 * PI * (((double)x / (double)gp->row_data[y].num_x_points) - 0.5);
737
738             radius = 1.0 + 0.0 * sin (xd * 10);
739
740             zd = radius * gp->row_data[y].sinyd * sin(xd);
741             xd = radius * gp->row_data[y].sinyd * cos(xd);
742             yd = radius * gp->row_data[y].cosyd;
743
744             gp->normals[index].x = xd;
745             gp->normals[index].y = yd;
746             gp->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 = gp->field_data[i].pos.x - xd;
755                 ydist = gp->field_data[i].pos.y - yd;
756                 zdist = gp->field_data[i].pos.z - zd;
757                 dist = field_array_size * (xdist * xdist + ydist * ydist
758                                            + zdist * zdist) * 0.1;
759
760                 strength += PI * gp->field_data[i].apower;
761
762                 if (dist < field_array_size)
763                 {
764                     offset_x += xd * gp->field_data[i].apower * gp->field[dist];
765                     offset_y += yd * gp->field_data[i].apower * gp->field[dist];
766                     offset_z += zd * gp->field_data[i].apower * gp->field[dist];
767
768                     gp->blob_force.x += 1.0 * xd * gp->field_data[i].apower * gp->field[dist];
769                     gp->blob_force.y += 1.0 * yd * gp->field_data[i].apower * gp->field[dist];
770                     gp->blob_force.z += 1.0 * zd * gp->field_data[i].apower * gp->field[dist];
771
772                     strength *= 2.0 * gp->field[dist];
773                 }
774
775                 if (incremental)
776                 {
777                     xd += offset_x * gp->freak * gp->freak;
778                     yd += offset_y * gp->freak * gp->freak;
779                     zd += offset_z * gp->freak * gp->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 += gp->blob_center.x;
796             yd += gp->blob_center.y;
797             zd += gp->blob_center.z;
798
799             if (do_colour)
800             {
801                 gp->colours[index].red = 128 + (int)(sin(strength + gp->colour_cycle * 0.01 + 2.0 * PI * x / gp->row_data[y].num_x_points) * 127.0);
802                 gp->colours[index].green = 128 + (int)(cos(strength + gp->colour_cycle * 0.025) * 127.0);
803                 gp->colours[index].blue = 128 + (int)(sin(strength + gp->colour_cycle * 0.03 + 2.0 * PI * y / y_resolution) * 127.0);
804                 gp->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 - gp->blob_center.x) * gp->wall_field[dist];
817                     yd += (yd - gp->blob_center.y) * gp->wall_field[dist];
818                     gp->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 - gp->blob_center.x) * gp->wall_field[dist];
826                         yd += (yd - gp->blob_center.y) * gp->wall_field[dist];
827                         gp->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 - gp->blob_center.x) * gp->wall_field[dist];
837                         zd += (zd - gp->blob_center.z) * gp->wall_field[dist];
838                         gp->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 - gp->blob_center.x) * gp->wall_field[dist];
846                             zd += (zd - gp->blob_center.z) * gp->wall_field[dist];
847                             gp->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 - gp->blob_center.y) * gp->wall_field[dist];
858                         zd += (zd - gp->blob_center.z) * gp->wall_field[dist];
859                         gp->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 - gp->blob_center.y) * gp->wall_field[dist];
867                             zd += (zd - gp->blob_center.z) * gp->wall_field[dist];
868                             gp->blob_force.x -= (xd - limit);
869                         }
870                     }
871
872                     if (yd < -limit) yd = -limit;
873                     if (yd > limit) yd = limit;
874                 }
875             }
876
877             gp->dots[index].x = xd;
878             gp->dots[index].y = yd;
879             gp->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 (gp->dots[index1], gp->dots[index2], gp->dots[index3], &gp->normals[index1]);
896     if (do_texture)
897     {
898         if (offset_texture)
899         {
900             gp->tex_coords[index1].x = gp->dots[index1].x * 0.125 + 0.5
901                 * (1.0 + 0.25 * asin(gp->normals[index1].x) / (0.5 * PI));
902             gp->tex_coords[index1].y = gp->dots[index1].y * 0.125 + 0.5
903                 * (1.0 + 0.25 * asin(gp->normals[index1].y) / (0.5 * PI));
904         }
905         else
906         {
907             gp->tex_coords[index1].x = 0.5 * (1.0 + (asin(gp->normals[index1].x)
908                                                  / (0.5 * PI)));
909             gp->tex_coords[index1].y = 0.5 * (1.0 + (asin(gp->normals[index1].y)
910                                                  / (0.5 * PI)));
911         }
912         gp->tex_coords[index1].x *= gp->tex_width[gp->current_texture];
913         gp->tex_coords[index1].y *= gp->tex_height[gp->current_texture];
914     }
915
916     for (y = 1; y < y_resolution - 1; y++)
917     {
918         if (gp->row_data[y - 1].num_x_points)
919         {
920             for (x = 0; x < gp->row_data[y].num_x_points; x++)
921             {
922                 if (x == gp->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) * gp->row_data[y - 1].num_x_points
932                           / gp->row_data[y].num_x_points) + (y - 1) * x_resolution;
933                 calculate_normal (gp->dots[index1], gp->dots[index2], gp->dots[index3],
934                                   &gp->normals[index1]);
935                 if (do_texture)
936                 {
937                     if (offset_texture)
938                     {
939                         gp->tex_coords[index1].x = gp->dots[index1].x * 0.125 + 0.5
940                             * (1.0 + 0.25 * asin(gp->normals[index1].x) / (0.5 * PI));
941                         gp->tex_coords[index1].y = gp->dots[index1].y * 0.125 + 0.5
942                             * (1.0 + 0.25 * asin(gp->normals[index1].y) / (0.5 * PI));
943                     }
944                     else
945                     {
946                         gp->tex_coords[index1].x = 0.5 * (1.0 + (asin(gp->normals[index1].x)
947                                                              / (0.5 * PI)));
948                         gp->tex_coords[index1].y = 0.5 * (1.0 + (asin(gp->normals[index1].y)
949                                                              / (0.5 * PI)));
950                     }
951                     gp->tex_coords[index1].x *= gp->tex_width[gp->current_texture];
952                     gp->tex_coords[index1].y *= gp->tex_height[gp->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 (gp->dots[index1], gp->dots[index2], gp->dots[index3], &gp->normals[index1]);
961     if (do_texture)
962     {
963         if (offset_texture)
964         {
965             gp->tex_coords[index1].x = gp->dots[index1].x * 0.125 + 0.5
966                 * (1.0 + 0.25 * asin(gp->normals[index1].x) / (0.5 * PI));
967             gp->tex_coords[index1].y = gp->dots[index1].y * 0.125 + 0.5
968                 * (1.0 + 0.25 * asin(gp->normals[index1].y) / (0.5 * PI));
969         }
970         else
971         {
972             gp->tex_coords[index1].x = 0.5 * (1.0 + (asin(gp->normals[index1].x)
973                                                  / (0.5 * PI)));
974             gp->tex_coords[index1].y = 0.5 * (1.0 + (asin(gp->normals[index1].y)
975                                                  / (0.5 * PI)));
976         }
977         gp->tex_coords[index1].x *= gp->tex_width[gp->current_texture];
978         gp->tex_coords[index1].y *= gp->tex_height[gp->current_texture];
979     }
980
981
982     gp->freak += gp->v_freak;
983     gp->v_freak += -gp->freak / 2000000.0;
984
985     /* Update the center of the whole blob */
986     gp->blob_velocity.x += (gp->blob_anchor.x - gp->blob_center.x) / 80.0
987         + 0.01 * gp->blob_force.x / gp->num_points;
988     gp->blob_velocity.y += (gp->blob_anchor.y - gp->blob_center.y) / 80.0
989         + 0.01 * gp->blob_force.y / gp->num_points;
990     gp->blob_velocity.z += (gp->blob_anchor.z - gp->blob_center.z) / 80.0
991         + 0.01 * gp->blob_force.z / gp->num_points;
992
993     gp->blob_center.x += gp->blob_velocity.x * 0.5;
994     gp->blob_center.y += gp->blob_velocity.y * 0.5;
995     gp->blob_center.z += gp->blob_velocity.z * 0.5;
996
997     gp->blob_velocity.x *= 0.99;
998     gp->blob_velocity.y *= 0.99;
999     gp->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 (mirrorblobstruct *gp)
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 (gp->row_data[y - 1].num_x_points)
1027         {
1028             for (x = 0; x < gp->row_data[y].num_x_points; x++)
1029             {
1030                 glBegin (GL_TRIANGLES);
1031                 if (x == gp->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) * gp->row_data[y - 1].num_x_points
1041                           / gp->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) * gp->row_data[y - 1].num_x_points
1048                          / (float)gp->row_data[y].num_x_points);
1049                 upper = ((x + 0.5) * gp->row_data[y - 1].num_x_points
1050                          / (float)gp->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 += gp->row_data[y - 1].num_x_points;
1061                         while (x3 >= gp->row_data[y - 1].num_x_points)
1062                             x3 -= gp->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 += gp->row_data[y - 1].num_x_points;
1069                             while (x3 >= gp->row_data[y - 1].num_x_points)
1070                                 x3 -= gp->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     mirrorblobstruct *gp = &Mirrorblob[MI_SCREEN(mi)];
1094
1095     glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
1096     glEnable (GL_TEXTURE_2D);
1097     glDisable(GL_LIGHTING);
1098
1099     /* Reset the projection matrix to make it easier to get the size of the quad
1100      * correct
1101      */
1102     glMatrixMode(GL_PROJECTION);
1103     glPushMatrix();
1104     glLoadIdentity();
1105
1106     glOrtho(0.0, MI_WIDTH(mi), MI_HEIGHT(mi), 0.0, -1000.0, 1000.0);
1107
1108     glBegin (GL_QUADS);
1109     
1110     glTexCoord2f (0.0, 0.0);
1111     glVertex2i (0, 0);
1112     
1113     glTexCoord2f (0.0, -gp->tex_height[gp->current_texture]);
1114     glVertex2i (0, MI_HEIGHT(mi));
1115
1116     glTexCoord2f (gp->tex_width[gp->current_texture], -gp->tex_height[gp->current_texture]);
1117     glVertex2i (MI_WIDTH(mi), MI_HEIGHT(mi));
1118
1119     glTexCoord2f (gp->tex_width[gp->current_texture], 0.0);
1120     glVertex2i (MI_WIDTH(mi), 0);
1121     glEnd();
1122
1123     glPopMatrix ();
1124     glMatrixMode (GL_MODELVIEW);
1125     glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
1126 }
1127
1128 /******************************************************************************
1129  *
1130  * Update the scene.
1131  */
1132 static GLvoid
1133 draw_scene(ModeInfo * mi)
1134 {
1135     mirrorblobstruct *gp = &Mirrorblob[MI_SCREEN(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 (gp->state)
1145     {
1146     case TRANSITIONING:
1147         fade = (current_time - gp->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, gp->textures[gp->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 (gp->state == TRANSITIONING)
1179         {
1180             glDisable (GL_DEPTH_TEST);
1181             glEnable (GL_BLEND);
1182             /* Select the texture to transition to */
1183             glBindTexture (GL_TEXTURE_2D, gp->textures[1 - gp->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, gp->textures[gp->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(gp, 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(gp);
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 (gp->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, gp->textures[1 - gp->current_texture]);
1262             glColor4d (1.0, 1.0, 1.0, fade);
1263             draw_blob (gp);
1264
1265             if ((current_time - gp->state_start_time) > fade_time)
1266             {
1267                 gp->state = HOLDING;
1268                 gp->state_start_time = current_time;
1269                 gp->current_texture = 1 - gp->current_texture;
1270             }
1271             break;
1272
1273         case HOLDING:
1274             if ((current_time - gp->state_start_time) > hold_time)
1275             {
1276                 grab_texture (mi, 1 - gp->current_texture);
1277                 gp->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                 gp->state_start_time = double_time();
1283             }
1284             break;
1285         }
1286     }
1287 }
1288
1289 /******************************************************************************
1290  *
1291  * XMirrorblob screen update entry
1292  */
1293 ENTRYPOINT void
1294 draw_mirrorblob(ModeInfo * mi)
1295 {
1296     mirrorblobstruct *gp = &Mirrorblob[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     /* Wait for the first image; for subsequent images, load them in the
1304        background while animating. */
1305     if (gp->waiting_for_image_p && gp->first_image_p)
1306       return;
1307
1308     glXMakeCurrent(display, window, *(gp->glx_context));
1309     draw_scene(mi);
1310     if (mi->fps_p) do_fps (mi);
1311     glXSwapBuffers(display, window);
1312 }
1313
1314 /******************************************************************************
1315  *
1316  * XMirrorblob screen resize entry
1317  */
1318 ENTRYPOINT void
1319 reshape_mirrorblob(ModeInfo *mi, int width, int height)
1320 {
1321     glViewport( 0, 0, MI_WIDTH(mi), MI_HEIGHT(mi) );
1322     reset_projection(width, height);
1323 }
1324
1325 /******************************************************************************
1326  *
1327  * XMirrorblob initialise entry
1328  */
1329 ENTRYPOINT void
1330 init_mirrorblob(ModeInfo * mi)
1331 {
1332     int screen = MI_SCREEN(mi);
1333
1334     mirrorblobstruct *gp;
1335
1336     if (Mirrorblob == NULL)
1337     {
1338         if ((Mirrorblob = (mirrorblobstruct *)
1339              calloc(MI_NUM_SCREENS(mi), sizeof (mirrorblobstruct))) == NULL)
1340         {
1341             return;
1342         }
1343     }
1344     gp = &Mirrorblob[screen];
1345
1346     gp->window = MI_WINDOW(mi);
1347     if ((gp->glx_context = init_GL(mi)) != NULL)
1348     {
1349         reshape_mirrorblob(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
1350         initialize_gl(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
1351     }
1352     else
1353     {
1354         MI_CLEARWINDOW(mi);
1355     }
1356
1357     initialise_blob(gp, MI_WIDTH(mi), MI_HEIGHT(mi), 1024);
1358     gp->state_start_time = double_time();
1359
1360     gp->freak = 0.0;
1361     gp->v_freak = 0.0007;
1362     gp->first_image_p = True;
1363 }
1364
1365 /******************************************************************************
1366  *
1367  * XMirrorblob cleanup entry
1368  */
1369 ENTRYPOINT void
1370 release_mirrorblob(ModeInfo * mi)
1371 {
1372   if (Mirrorblob != NULL) {
1373     int i;
1374     for (i = 0; i < MI_NUM_SCREENS(mi); i++) {
1375       mirrorblobstruct *gp = &Mirrorblob[i];
1376       if (gp->row_data) free(gp->row_data);
1377       if (gp->field_data) free(gp->field_data);
1378       if (gp->colours) free(gp->colours);
1379       if (gp->tex_coords) free(gp->tex_coords);
1380       if (gp->dots) free(gp->dots);
1381       if (gp->wall_field) free(gp->wall_field);
1382       if (gp->field) free(gp->field);
1383     }
1384
1385     free(Mirrorblob);
1386     Mirrorblob = NULL;
1387   }
1388   FreeAllGL(mi);
1389 }
1390
1391 XSCREENSAVER_MODULE ("MirrorBlob", mirrorblob)
1392
1393 #endif