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