1 /* mirrorblob Copyright (c) 2003 Jon Dowdall <jon.dowdall@bigpond.com> */
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.
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.
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
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
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.
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.
38 "*delay: " DEF_DELAY "\n" \
39 "*showFPS: " DEF_FPS "\n" \
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 */
49 #ifdef USE_GL /* whole file */
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"
73 # include <X11/Xmu/Drawing.h>
75 # include <Xmu/Drawing.h>
79 #include "grab-ximage.h"
82 #define countof(x) (sizeof((x)) / sizeof((*x)))
84 #define PI 3.1415926535897
90 static int do_antialias;
92 static int do_texture;
93 static int do_paint_background;
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;
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 },
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},
152 static OptionStruct desc[] =
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"},
172 ENTRYPOINT ModeSpecOpt mirrorblob_opts = {countof(opts), opts, countof(vars), vars, desc};
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};
182 #define NUM_TEXTURES 2
184 /*****************************************************************************
185 * Types used in blob code
186 *****************************************************************************/
200 GLubyte red, green, blue, alpha;
203 /* Data used for sphere tessellation */
208 /* Number of x points at each row of the blob */
212 /* Structure to hold sphere distortion data */
215 double cx, cy, cpower;
216 double mx, my, mpower;
217 double ax, ay, apower;
218 double vx, vy, vpower;
229 /* structure for holding the mirrorblob data */
231 int screen_width, screen_height;
232 GLXContext *glx_context;
235 double freak, v_freak;
239 /* Parameters controlling the position of the blob as a whole */
240 Vector3D blob_center;
241 Vector3D blob_anchor;
242 Vector3D blob_velocity;
245 /* Count of the total number of points */
251 Vector2D *tex_coords;
253 /* Pointer to the field function results */
254 double *field, *wall_field;
256 Field_Data *field_data;
258 /* Use 2 textures to allow a gradual fade between images */
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];
266 double state_start_time;
271 Bool waiting_for_image_p;
276 static mirrorblobstruct *Mirrorblob = NULL;
278 /******************************************************************************
280 * Returns the current time in seconds as a double. Shamelessly borrowed from
288 # ifdef GETTIMEOFDAY_TWO_ARGS
290 gettimeofday(&now, &tzp);
295 return (now.tv_sec + ((double) now.tv_usec * 0.000001));
298 /******************************************************************************
300 * Change to the projection matrix and set our viewing volume.
304 reset_projection(int width, int height)
306 glMatrixMode (GL_PROJECTION);
308 gluPerspective (60.0, 1.0, 1.0, 1024.0 );
309 glMatrixMode (GL_MODELVIEW);
313 /****************************************************************************
319 image_loaded_cb (const char *filename, XRectangle *geometry,
320 int image_width, int image_height,
321 int texture_width, int texture_height,
324 mirrorblobstruct *mp = (mirrorblobstruct *) closure;
326 int texture_index = -1;
329 glGetIntegerv (GL_TEXTURE_BINDING_2D, &texid);
330 if (texid < 0) abort();
332 for (i = 0; i < NUM_TEXTURES; i++) {
333 if (mp->textures[i] == texid) {
338 if (texture_index < 0) abort();
340 mp->tex_width [texture_index] = (GLfloat) image_width / texture_width;
341 mp->tex_height[texture_index] = -(GLfloat) image_height / texture_height;
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);
350 mp->waiting_for_image_p = False;
351 mp->first_image_p = True;
356 grab_texture(ModeInfo *mi, int texture_index)
358 mirrorblobstruct *mp = &Mirrorblob[MI_SCREEN(mi)];
360 mp->waiting_for_image_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);
368 /******************************************************************************
370 * Initialise the data used to calculate the blob shape.
373 initialize_gl(ModeInfo *mi, GLsizei width, GLsizei height)
375 mirrorblobstruct *gp = &Mirrorblob[MI_SCREEN(mi)];
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 };
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 };
389 GLfloat specref[] = { 1.0f, 1.0f, 1.0f, 1.0f };
391 /* Setup our viewport. */
392 glViewport (0, 0, width, height );
394 glEnable(GL_DEPTH_TEST);
399 glEnable(GL_LINE_SMOOTH);
402 /* The blend function is used for trasitioning between two images even when
403 * blend is not selected.
405 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
409 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
413 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
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);
426 /* Our shading model--Gouraud (smooth). */
427 glShadeModel (GL_SMOOTH);
430 glCullFace (GL_BACK);
431 glEnable (GL_CULL_FACE);
432 glEnable (GL_DEPTH_TEST);
433 glFrontFace (GL_CCW);
435 /* Set the clear color. */
436 glClearColor( 0, 0, 0, 0 );
438 glViewport( 0, 0, width, height );
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);
452 /* Enable color tracking */
453 glEnable (GL_COLOR_MATERIAL);
455 /* Set Material properties to follow glColor values */
456 glColorMaterial (GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
458 /* Set all materials to have specular reflectivity */
459 glMaterialfv (GL_FRONT, GL_SPECULAR, specref);
460 glMateriali (GL_FRONT, GL_SHININESS, 64);
462 glEnable (GL_NORMALIZE);
467 glLightModeli (GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR);
469 glEnable (GL_TEXTURE_2D);
470 gp->current_texture = 0;
471 glGenTextures (NUM_TEXTURES, gp->textures);
472 grab_texture (mi, gp->current_texture);
474 glEnableClientState (GL_TEXTURE_COORD_ARRAY);
479 glEnableClientState (GL_COLOR_ARRAY);
481 glEnableClientState (GL_NORMAL_ARRAY);
482 glEnableClientState (GL_VERTEX_ARRAY);
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);
488 /******************************************************************************
490 * Calculate the normal vector for a plane given three points in the plane.
493 calculate_normal (Vector3D point1,
498 Vector3D vector1, vector2;
501 vector1.x = point2.x - point1.x;
502 vector1.y = point2.y - point1.y;
503 vector1.z = point2.z - point1.z;
505 vector2.x = point3.x - point2.x;
506 vector2.y = point3.y - point2.y;
507 vector2.z = point3.z - point2.z;
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;
513 /* Adjust the normal to unit magnitude */
514 magnitude = sqrt ((*normal).x * (*normal).x
515 + (*normal).y * (*normal).y
516 + (*normal).z * (*normal).z);
518 /* Watch out for divide by zero/underflow */
519 if (magnitude > 1e-300)
521 (*normal).x /= magnitude;
522 (*normal).y /= magnitude;
523 (*normal).z /= magnitude;
527 /******************************************************************************
529 * Initialise the data required to draw the blob allocating the memory as
532 * Return 0 on success.
535 initialise_blob(mirrorblobstruct *gp,
536 int width, int height,
537 int field_array_size)
543 gp->colour_cycle = 0;
545 gp->row_data = (Row_Data *) malloc (y_resolution * sizeof (Row_Data));
548 fprintf(stderr, "Couldn't allocate row data buffer\n");
552 gp->field_data = (Field_Data *) malloc (field_points * sizeof (Field_Data));
555 fprintf(stderr, "Couldn't allocate field data buffer\n");
559 gp->field = (double *)malloc(field_array_size * sizeof(double));
562 fprintf(stderr, "Couldn't allocate field buffer\n");
566 gp->wall_field = (double *)malloc(field_array_size * sizeof(double));
569 fprintf(stderr, "Couldn't allocate wall field buffer\n");
573 gp->dots = (Vector3D *)malloc(x_resolution * y_resolution * sizeof(Vector3D));
576 fprintf(stderr, "Couldn't allocate points buffer\n");
579 glVertexPointer (3, GL_DOUBLE, 0, (GLvoid *) gp->dots);
581 gp->normals = (Vector3D *)malloc(x_resolution * y_resolution * sizeof(Vector3D));
584 fprintf(stderr, "Couldn't allocate normals buffer\n");
587 glNormalPointer (GL_DOUBLE, 0, (GLvoid *) gp->normals);
591 gp->colours = (Colour *)malloc(x_resolution * y_resolution * sizeof(Colour));
594 fprintf(stderr, "Couldn't allocate colours buffer\n");
597 glColorPointer (4, GL_UNSIGNED_BYTE, 0, (GLvoid *) gp->colours);
602 gp->tex_coords = (Vector2D *)malloc(x_resolution * y_resolution
606 fprintf(stderr, "Couldn't allocate tex_coords buffer\n");
609 glTexCoordPointer (2, GL_DOUBLE, 0, (GLvoid *) gp->tex_coords);
613 /* Generate constant row data and count of total number of points */
614 for (y = 0; y < y_resolution; y++)
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;
624 /* Initialise field data */
625 for (i = 0; i < field_points; i++)
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);
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);
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);
641 gp->field_data[i].vx = 0.0;
642 gp->field_data[i].vy = 0.0;
643 gp->field_data[i].vpower = 0.0;
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);
650 /* Initialise lookup table of field strength */
651 for (i = 0; i < field_array_size; i++)
653 xd = 2.0 * (((double)i / (double)field_array_size));
655 xd = 3.0 * xd * xd * xd * xd;
656 gp->field[i] = 0.4 / (field_points * (xd + 0.1));
658 xd = 10.0 * (((double)i / (double)field_array_size));
659 gp->wall_field[i] = 0.4 / (xd * xd * xd * xd + 1.0);
662 for (y = 0; y < y_resolution; y++)
664 for (x = 0; x < gp->row_data[y].num_x_points; x++)
666 i = x + y * x_resolution;
667 xd = 2.0 * (((double)x / (double)gp->row_data[y].num_x_points) - 0.5);
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);
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;
686 /******************************************************************************
688 * Calculate the blob shape.
691 calc_blob(mirrorblobstruct *gp,
692 int width, int height,
693 int field_array_size,
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;
705 /* Color components */
709 /* Update position and strength of points used to distort the blob */
710 for (i = 0; i < field_points; i++)
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);
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;
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);
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++)
733 for (x = 0; x < gp->row_data[y].num_x_points; x++)
735 index = x + y * x_resolution;
736 xd = 2.0 * PI * (((double)x / (double)gp->row_data[y].num_x_points) - 0.5);
738 radius = 1.0 + 0.0 * sin (xd * 10);
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;
744 gp->normals[index].x = xd;
745 gp->normals[index].y = yd;
746 gp->normals[index].z = zd;
752 for ( i = 0; i < field_points; i++)
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;
760 strength += PI * gp->field_data[i].apower;
762 if (dist < field_array_size)
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];
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];
772 strength *= 2.0 * gp->field[dist];
777 xd += offset_x * gp->freak * gp->freak;
778 yd += offset_y * gp->freak * gp->freak;
779 zd += offset_z * gp->freak * gp->freak;
781 if (incremental == 1)
795 xd += gp->blob_center.x;
796 yd += gp->blob_center.y;
797 zd += gp->blob_center.z;
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);
810 if (zd < -limit) zd = -limit;
811 if (zd > limit) zd = limit;
813 dist = field_array_size * (zd + limit) * (zd + limit) * 0.5;
814 if (dist < field_array_size)
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);
822 dist = field_array_size * (zd - limit) * (zd - limit) * 0.5;
823 if (dist < field_array_size)
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);
830 if (yd < -limit) yd = -limit;
831 if (yd > limit) yd = limit;
833 dist = field_array_size * (yd + limit) * (yd + limit) * 0.5;
834 if (dist < field_array_size)
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);
842 dist = field_array_size * (yd - limit) * (yd - limit) * 0.5;
843 if (dist < field_array_size)
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);
851 if (xd < -limit) xd = -limit;
852 if (xd > limit) xd = limit;
854 dist = field_array_size * (xd + limit) * (xd + limit) * 0.5;
855 if (dist < field_array_size)
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);
863 dist = field_array_size * (xd - limit) * (xd - limit) * 0.5;
864 if (dist < field_array_size)
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);
872 if (yd < -limit) yd = -limit;
873 if (yd > limit) yd = limit;
877 gp->dots[index].x = xd;
878 gp->dots[index].y = yd;
879 gp->dots[index].z = zd;
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.
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.
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]);
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));
907 gp->tex_coords[index1].x = 0.5 * (1.0 + (asin(gp->normals[index1].x)
909 gp->tex_coords[index1].y = 0.5 * (1.0 + (asin(gp->normals[index1].y)
912 gp->tex_coords[index1].x *= gp->tex_width[gp->current_texture];
913 gp->tex_coords[index1].y *= gp->tex_height[gp->current_texture];
916 for (y = 1; y < y_resolution - 1; y++)
918 if (gp->row_data[y - 1].num_x_points)
920 for (x = 0; x < gp->row_data[y].num_x_points; x++)
922 if (x == gp->row_data[y].num_x_points - 1)
924 index1 = y * x_resolution;
928 index1 = x + 1 + y * x_resolution;
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]);
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));
946 gp->tex_coords[index1].x = 0.5 * (1.0 + (asin(gp->normals[index1].x)
948 gp->tex_coords[index1].y = 0.5 * (1.0 + (asin(gp->normals[index1].y)
951 gp->tex_coords[index1].x *= gp->tex_width[gp->current_texture];
952 gp->tex_coords[index1].y *= gp->tex_height[gp->current_texture];
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]);
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));
972 gp->tex_coords[index1].x = 0.5 * (1.0 + (asin(gp->normals[index1].x)
974 gp->tex_coords[index1].y = 0.5 * (1.0 + (asin(gp->normals[index1].y)
977 gp->tex_coords[index1].x *= gp->tex_width[gp->current_texture];
978 gp->tex_coords[index1].y *= gp->tex_height[gp->current_texture];
982 gp->freak += gp->v_freak;
983 gp->v_freak += -gp->freak / 2000000.0;
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;
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;
997 gp->blob_velocity.x *= 0.99;
998 gp->blob_velocity.y *= 0.99;
999 gp->blob_velocity.z *= 0.99;
1002 /******************************************************************************
1004 * Draw the blob shape.
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.
1012 draw_blob (mirrorblobstruct *gp)
1015 int index1, index2, index3;
1018 glMatrixMode (GL_MODELVIEW);
1021 /* Move down the z-axis. */
1022 glTranslatef (0.0, 0.0, -5.0 );
1024 for (y = 1; y < y_resolution; y++)
1026 if (gp->row_data[y - 1].num_x_points)
1028 for (x = 0; x < gp->row_data[y].num_x_points; x++)
1030 glBegin (GL_TRIANGLES);
1031 if (x == gp->row_data[y].num_x_points - 1)
1033 index1 = y * x_resolution;
1037 index1 = x + 1 + y * x_resolution;
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);
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);
1054 glBegin (GL_TRIANGLE_FAN);
1055 index1 = x + y * x_resolution;
1057 for (x2 = lower; x2 <= upper; 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;
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;
1074 glArrayElement(index1);
1077 glArrayElement(index2);
1086 /******************************************************************************
1088 * Draw the background image simply map a texture onto a full screen quad.
1091 draw_background (ModeInfo *mi)
1093 mirrorblobstruct *gp = &Mirrorblob[MI_SCREEN(mi)];
1095 glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
1096 glEnable (GL_TEXTURE_2D);
1097 glDisable(GL_LIGHTING);
1099 /* Reset the projection matrix to make it easier to get the size of the quad
1102 glMatrixMode(GL_PROJECTION);
1106 glOrtho(0.0, MI_WIDTH(mi), MI_HEIGHT(mi), 0.0, -1000.0, 1000.0);
1110 glTexCoord2f (0.0, 0.0);
1113 glTexCoord2f (0.0, -gp->tex_height[gp->current_texture]);
1114 glVertex2i (0, MI_HEIGHT(mi));
1116 glTexCoord2f (gp->tex_width[gp->current_texture], -gp->tex_height[gp->current_texture]);
1117 glVertex2i (MI_WIDTH(mi), MI_HEIGHT(mi));
1119 glTexCoord2f (gp->tex_width[gp->current_texture], 0.0);
1120 glVertex2i (MI_WIDTH(mi), 0);
1124 glMatrixMode (GL_MODELVIEW);
1125 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
1128 /******************************************************************************
1133 draw_scene(ModeInfo * mi)
1135 mirrorblobstruct *gp = &Mirrorblob[MI_SCREEN(mi)];
1138 double current_time;
1139 check_gl_error ("draw_scene");
1141 glColor4d(1.0, 1.0, 1.0, 1.0);
1143 current_time = double_time();
1147 fade = (current_time - gp->state_start_time) / fade_time;
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
1162 glBindTexture (GL_TEXTURE_2D, gp->textures[gp->current_texture]);
1165 if (do_paint_background && !do_wire)
1167 glClear(GL_DEPTH_BUFFER_BIT);
1170 glEnable (GL_BLEND);
1171 glColor4ub (255, 255, 255, motion_blur);
1173 draw_background (mi);
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.
1178 if (gp->state == TRANSITIONING)
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);
1186 draw_background (mi);
1188 /* Select the original texture to draw the blob */
1189 glBindTexture (GL_TEXTURE_2D, gp->textures[gp->current_texture]);
1190 glEnable (GL_DEPTH_TEST);
1192 /* Clear the depth buffer bit so the backgound is behind the blob */
1193 glClear(GL_DEPTH_BUFFER_BIT);
1195 else if (motion_blur)
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);
1204 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
1206 glEnable (GL_DEPTH_TEST);
1207 glClear (GL_DEPTH_BUFFER_BIT);
1211 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1219 calc_blob(gp, MI_WIDTH(mi), MI_HEIGHT(mi), 1024, 2.5, fade);
1221 glEnable(GL_LIGHTING);
1222 glEnable(GL_LIGHT0);
1223 glEnable(GL_LIGHT1);
1227 glEnable (GL_BLEND);
1230 glBlendFunc (GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA);
1234 glColor4d (1.0, 1.0, 1.0, 0.5 - fade);
1239 glDisable (GL_BLEND);
1240 glColor4d (1.0, 1.0, 1.0, 1.0);
1244 if (do_blend && do_colour)
1246 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
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.
1253 if (do_texture && (hold_time > 0))
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);
1265 if ((current_time - gp->state_start_time) > fade_time)
1267 gp->state = HOLDING;
1268 gp->state_start_time = current_time;
1269 gp->current_texture = 1 - gp->current_texture;
1274 if ((current_time - gp->state_start_time) > hold_time)
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
1282 gp->state_start_time = double_time();
1289 /******************************************************************************
1291 * XMirrorblob screen update entry
1294 draw_mirrorblob(ModeInfo * mi)
1296 mirrorblobstruct *gp = &Mirrorblob[MI_SCREEN(mi)];
1297 Display *display = MI_DISPLAY(mi);
1298 Window window = MI_WINDOW(mi);
1300 if (!gp->glx_context)
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)
1308 glXMakeCurrent(display, window, *(gp->glx_context));
1310 if (mi->fps_p) do_fps (mi);
1311 glXSwapBuffers(display, window);
1314 /******************************************************************************
1316 * XMirrorblob screen resize entry
1319 reshape_mirrorblob(ModeInfo *mi, int width, int height)
1321 glViewport( 0, 0, MI_WIDTH(mi), MI_HEIGHT(mi) );
1322 reset_projection(width, height);
1325 /******************************************************************************
1327 * XMirrorblob initialise entry
1330 init_mirrorblob(ModeInfo * mi)
1332 int screen = MI_SCREEN(mi);
1334 mirrorblobstruct *gp;
1336 if (Mirrorblob == NULL)
1338 if ((Mirrorblob = (mirrorblobstruct *)
1339 calloc(MI_NUM_SCREENS(mi), sizeof (mirrorblobstruct))) == NULL)
1344 gp = &Mirrorblob[screen];
1346 gp->window = MI_WINDOW(mi);
1347 if ((gp->glx_context = init_GL(mi)) != NULL)
1349 reshape_mirrorblob(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
1350 initialize_gl(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
1357 initialise_blob(gp, MI_WIDTH(mi), MI_HEIGHT(mi), 1024);
1358 gp->state_start_time = double_time();
1361 gp->v_freak = 0.0007;
1362 gp->first_image_p = True;
1365 /******************************************************************************
1367 * XMirrorblob cleanup entry
1370 release_mirrorblob(ModeInfo * mi)
1372 if (Mirrorblob != NULL) {
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);
1391 XSCREENSAVER_MODULE ("MirrorBlob", mirrorblob)