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
20 * 28-Jan-2006: jon.dowdall@bigpond.com Big clean up and bug fixes
21 * 13-Apr-2009: jon.dowdall@gmail.com Fixed Mac version
23 * The mirrorblob screensaver draws a pulsing blob on the screen. Options
24 * include adding a background (via screen_to_texture), texturing the blob,
25 * making the blob semi-transparent and varying the resolution of the blob
28 * The blob was inspired by a lavalamp is in no way a simulation. The code is
29 * just an attempt to generate some eye-candy.
31 * Much of xmirrorblob code framework is taken from the pulsar module by David
32 * Konerding and the glslideshow by Mike Oliphant and Jamie Zawinski.
39 #define DEFAULTS "*delay: " DEF_DELAY "\n" \
40 "*showFPS: " DEF_FPS "\n" \
42 "*desktopGrabber: xscreensaver-getimage -no-desktop %s\n" \
43 "*grabDesktopImages: True \n" \
44 "*chooseRandomImages: True \n"
46 # define refresh_mirrorblob 0
48 # define mirrorblob_handle_event 0
50 # include "xlockmore.h"
51 #else /* !STANDALONE */
52 # include "xlock.h" /* from the xlockmore distribution */
53 #endif /* !STANDALONE */
55 #ifdef USE_GL /* whole file */
58 #define DEF_DELAY "10000"
59 #define DEF_FPS "False"
60 #define DEF_WIRE "False"
61 #define DEF_BLEND "1.0"
62 #define DEF_FOG "False"
63 #define DEF_ANTIALIAS "False"
64 #define DEF_WALLS "False"
65 #define DEF_COLOUR "False"
66 #define DEF_ASYNC "True"
67 #define DEF_TEXTURE "True"
68 #define DEF_OFFSET_TEXTURE "False"
69 #define DEF_PAINT_BACKGROUND "True"
70 #define DEF_RESOLUTION "30"
71 #define DEF_BUMPS "10"
72 #define DEF_MOTION_BLUR "0.0"
73 #define DEF_INCREMENTAL "0"
74 #define DEF_HOLD_TIME "30.0"
75 #define DEF_FADE_TIME "5.0"
76 #define DEF_ZOOM "1.0"
80 # include <X11/Xmu/Drawing.h>
82 # include <Xmu/Drawing.h>
86 #include "gltrackball.h"
87 #include "grab-ximage.h"
90 #define countof(x) (sizeof((x)) / sizeof((*x)))
92 #define PI 3.1415926535897
94 /* Options from command line */
96 static Bool wireframe;
98 static Bool do_antialias;
100 static Bool do_texture;
101 static Bool do_paint_background;
102 static Bool do_colour;
103 static Bool offset_texture;
104 static int resolution;
106 static float motion_blur;
107 static float fade_time;
108 static float hold_time;
111 /* Internal parameters based on supplied options */
113 static Bool load_textures;
115 static XrmOptionDescRec opts[] = {
116 {"-wire", ".blob.wire", XrmoptionNoArg, "true" },
117 {"+wire", ".blob.wire", XrmoptionNoArg, "false" },
118 {"-blend", ".blob.blend", XrmoptionSepArg, 0 },
119 {"-fog", ".blob.fog", XrmoptionNoArg, "true" },
120 {"+fog", ".blob.fog", XrmoptionNoArg, "false" },
121 {"-antialias", ".blob.antialias", XrmoptionNoArg, "true" },
122 {"+antialias", ".blob.antialias", XrmoptionNoArg, "false" },
123 {"-walls", ".blob.walls", XrmoptionNoArg, "true" },
124 {"+walls", ".blob.walls", XrmoptionNoArg, "false" },
125 {"-texture", ".blob.texture", XrmoptionNoArg, "true" },
126 {"+texture", ".blob.texture", XrmoptionNoArg, "false" },
127 {"-colour", ".blob.colour", XrmoptionNoArg, "true" },
128 {"+colour", ".blob.colour", XrmoptionNoArg, "false" },
129 {"-offset-texture", ".blob.offsetTexture", XrmoptionNoArg, "true" },
130 {"+offset-texture", ".blob.offsetTexture", XrmoptionNoArg, "false" },
131 {"-paint-background", ".blob.paintBackground", XrmoptionNoArg, "true" },
132 {"+paint-background", ".blob.paintBackground", XrmoptionNoArg, "false" },
133 {"-resolution", ".blob.resolution", XrmoptionSepArg, NULL },
134 {"-bumps", ".blob.bumps", XrmoptionSepArg, NULL },
135 {"-motion-blur", ".blob.motionBlur", XrmoptionSepArg, 0 },
136 {"-fade-time", ".blob.fadeTime", XrmoptionSepArg, 0 },
137 {"-hold-time", ".blob.holdTime", XrmoptionSepArg, 0 },
138 {"-zoom", ".blob.zoom", XrmoptionSepArg, 0 },
141 static argtype vars[] = {
142 {&wireframe, "wire", "Wire", DEF_WIRE, t_Bool},
143 {&blend, "blend", "Blend", DEF_BLEND, t_Float},
144 {&do_fog, "fog", "Fog", DEF_FOG, t_Bool},
145 {&do_antialias, "antialias", "Antialias", DEF_ANTIALIAS, t_Bool},
146 {&do_walls, "walls", "Walls", DEF_WALLS, t_Bool},
147 {&do_texture, "texture", "Texture", DEF_TEXTURE, t_Bool},
148 {&do_colour, "colour", "Colour", DEF_COLOUR, t_Bool},
149 {&offset_texture, "offsetTexture","OffsetTexture", DEF_OFFSET_TEXTURE, t_Bool},
150 {&do_paint_background,"paintBackground","PaintBackground", DEF_PAINT_BACKGROUND, t_Bool},
151 {&resolution, "resolution", "Resolution", DEF_RESOLUTION, t_Int},
152 {&bumps, "bumps", "Bump", DEF_BUMPS, t_Int},
153 {&motion_blur, "motionBlur", "MotionBlur", DEF_MOTION_BLUR, t_Float},
154 {&fade_time, "fadeTime", "FadeTime", DEF_FADE_TIME, t_Float},
155 {&hold_time, "holdTime", "HoldTime", DEF_HOLD_TIME, t_Float},
156 {&zoom, "zoom", "Zoom", DEF_ZOOM, t_Float},
160 static OptionStruct desc[] =
162 {"-/+ wire", "whether to do use wireframe instead of filled (faster)"},
163 {"-/+ blend", "whether to do enable blending (slower)"},
164 {"-/+ fog", "whether to do enable fog (slower)"},
165 {"-/+ antialias", "whether to do enable antialiased lines (slower)"},
166 {"-/+ walls", "whether to add walls to the blob space (slower)"},
167 {"-/+ texture", "whether to add a texture to the blob (slower)"},
168 {"-/+ colour", "whether to colour the blob"},
169 {"-/+ offset_texture", "whether to offset texture co-ordinates"},
170 {"-/+ paint_background", "whether to display a background texture (slower)"},
171 {"-resolution", "Resolution of blob tesselation"},
172 {"-bumps", "Number of bumps used to disturb blob"},
173 {"-motion_blur", "Fade blob images (higher number = faster fade)"},
174 {"-fade_time", "Number of frames to transistion to next image"},
175 {"-hold_time", "Number of frames before next image"},
178 ENTRYPOINT ModeSpecOpt mirrorblob_opts = {countof(opts), opts, countof(vars), vars, desc};
181 ModStruct mirrorblob_description =
182 {"mirrorblob", "init_mirrorblob", "draw_mirrorblob", "release_mirrorblob",
183 "draw_mirrorblob", "init_mirrorblob", "handle_event", &mirrorblob_opts,
184 1000, 1, 2, 1, 4, 1.0, "",
185 "OpenGL mirrorblob", 0, NULL};
188 /*****************************************************************************
189 * Types used in blob code
190 *****************************************************************************/
212 GLubyte red, green, blue, alpha;
217 Vector3D initial_position;
224 int node1, node2, node3;
226 double length1, length2, length3;
229 /* Structure to hold data about bumps used to distortion sphere */
232 double cx, cy, cpower, csize;
233 double ax, ay, power, size;
234 double mx, my, mpower, msize;
235 double vx, vy, vpower, vsize;
239 /* Vertices of a tetrahedron */
240 #define sqrt_3 0.5773502692
243 #define PPP { sqrt_3, sqrt_3, sqrt_3 } /* +X, +Y, +Z */
244 #define MMP { -sqrt_3, -sqrt_3, sqrt_3 } /* -X, -Y, +Z */
245 #define MPM { -sqrt_3, sqrt_3, -sqrt_3 } /* -X, +Y, -Z */
246 #define PMM { sqrt_3, -sqrt_3, -sqrt_3 } /* +X, -Y, -Z */
248 /* Structure describing a tetrahedron */
249 static Vector3D tetrahedron[4][3] = {
256 /*****************************************************************************
258 *****************************************************************************/
260 static const Vector3D zero_vector = { 0.0, 0.0, 0.0 };
262 /* Use 2 textures to allow a gradual fade between images */
263 #define NUM_TEXTURES 2
264 #define BUMP_ARRAY_SIZE 1024
274 /* structure for holding the mirrorblob data */
276 int screen_width, screen_height;
277 GLXContext *glx_context;
281 /* Parameters controlling the position of the blob as a whole */
282 Vector3D blob_center;
283 Vector3D blob_anchor;
284 Vector3D blob_velocity;
287 /* Count of the total number of nodes and faces used to tesselate the blob */
297 Vector2D *tex_coords;
299 /* Pointer to the bump function results */
300 double *bump_shape, *wall_shape;
302 Bump_Data *bump_data;
304 /* Use 2 textures to allow a gradual fade between images */
307 /* Ratio of used texture size to total texture size */
308 GLfloat tex_width[NUM_TEXTURES], tex_height[NUM_TEXTURES];
309 GLuint textures[NUM_TEXTURES];
312 double state_start_time;
317 Bool waiting_for_image_p;
320 trackball_state *trackball;
325 static mirrorblobstruct *Mirrorblob = NULL;
327 /******************************************************************************
329 * Returns the current time in seconds as a double. Shamelessly borrowed from
337 # ifdef GETTIMEOFDAY_TWO_ARGS
339 gettimeofday(&now, &tzp);
344 return (now.tv_sec + ((double) now.tv_usec * 0.000001));
347 /******************************************************************************
349 * Change to the projection matrix and set our viewing volume.
353 reset_projection(int width, int height)
355 glMatrixMode (GL_PROJECTION);
357 gluPerspective (60.0, 1.0, 1.0, 1024.0 );
358 glMatrixMode (GL_MODELVIEW);
362 /******************************************************************************
364 * Calculate the dot product of two vectors u and v
365 * Dot product u.v = |u||v|cos(theta)
366 * Where theta = angle between u and v
369 dot (const Vector3D u, const Vector3D v)
371 return (u.x * v.x) + (u.y * v.y) + (u.z * v.z);
374 /******************************************************************************
376 * Calculate the cross product of two vectors.
377 * Gives a vector perpendicular to u and v with magnitude |u||v|sin(theta)
378 * Where theta = angle between u and v
380 static inline Vector3D
381 cross (const Vector3D u, const Vector3D v)
385 result.x = (u.y * v.z - u.z * v.y);
386 result.y = (u.z * v.x - u.x * v.z);
387 result.z = (u.x * v.y - u.y * v.x);
392 /******************************************************************************
394 * Add vector v to vector u
397 add (Vector3D *u, const Vector3D v)
404 /******************************************************************************
406 * Subtract vector v from vector u
408 static inline Vector3D
409 subtract (const Vector3D u, const Vector3D v)
413 result.x = u.x - v.x;
414 result.y = u.y - v.y;
415 result.z = u.z - v.z;
420 /******************************************************************************
422 * multiply vector v by scalar s
424 static inline Vector3D
425 scale (const Vector3D v, const double s)
435 /******************************************************************************
439 static inline Vector3D
440 normalise (const Vector3D v)
445 magnitude = sqrt (dot(v, v));
447 if (magnitude > 1e-300)
449 result = scale (v, 1.0 / magnitude);
454 result = zero_vector;
459 /******************************************************************************
461 * Calculate the transform matrix for the given quaternion
464 quaternion_transform (Quaternion q, GLfloat * transform)
472 transform[0] = (w * w) + (x * x) - (y * y) - (z * z);
473 transform[1] = (2.0 * x * y) + (2.0 * w * z);
474 transform[2] = (2.0 * x * z) - (2.0 * w * y);
477 transform[4] = (2.0 * x * y) - (2.0 * w * z);
478 transform[5] = (w * w) - (x * x) + (y * y) - (z * z);
479 transform[6] = (2.0 * y * z) + (2.0 * w * x);
482 transform[8] = (2.0 * x * z) + (2.0 * w * y);
483 transform[9] = (2.0 * y * z) - (2.0 * w * x);
484 transform[10] = (w * w) - (x * x) - (y * y) + (z * z);
490 transform[15] = (w * w) + (x * x) + (y * y) + (z * z);
493 /******************************************************************************
495 * Apply a matrix transform to the given vector
497 static inline Vector3D
498 vector_transform (Vector3D u, GLfloat * t)
502 result.x = (u.x * t[0] + u.y * t[4] + u.z * t[8] + 1.0 * t[12]);
503 result.y = (u.x * t[1] + u.y * t[5] + u.z * t[9] + 1.0 * t[13]);
504 result.z = (u.x * t[2] + u.y * t[6] + u.z * t[10] + 1.0 * t[14]);
509 /******************************************************************************
511 * Return a node that is on an arc between node1 and node2, where distance
512 * is the proportion of the distance from node1 to the total arc.
515 partial (Vector3D node1, Vector3D node2, double distance)
518 Vector3D rotation_axis;
519 GLfloat transformation[16];
523 rotation_axis = normalise (cross (node1, node2));
524 angle = acos (dot (node1, node2)) * distance;
526 rotation.x = rotation_axis.x * sin (angle / 2.0);
527 rotation.y = rotation_axis.y * sin (angle / 2.0);
528 rotation.z = rotation_axis.z * sin (angle / 2.0);
529 rotation.w = cos (angle / 2.0);
531 quaternion_transform (rotation, transformation);
533 result = vector_transform (node1, transformation);
538 /****************************************************************************
540 * Callback indicating a texture has loaded
543 image_loaded_cb (const char *filename, XRectangle *geometry,
544 int image_width, int image_height,
545 int texture_width, int texture_height,
548 mirrorblobstruct *mp = (mirrorblobstruct *) closure;
550 int texture_index = -1;
553 glGetIntegerv (GL_TEXTURE_BINDING_2D, &texid);
554 if (texid < 0) abort();
556 for (i = 0; i < NUM_TEXTURES; i++) {
557 if (mp->textures[i] == texid) {
562 if (texture_index < 0) abort();
564 mp->tex_width [texture_index] = (GLfloat) image_width / texture_width;
565 mp->tex_height[texture_index] = -(GLfloat) image_height / texture_height;
567 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
568 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
569 (mp->mipmap_p ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR));
570 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
571 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
572 glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
574 mp->waiting_for_image_p = False;
575 mp->first_image_p = True;
578 /* Load a new file into a texture
581 grab_texture(ModeInfo *mi, int texture_index)
583 mirrorblobstruct *mp = &Mirrorblob[MI_SCREEN(mi)];
586 int w = (MI_WIDTH(mi) / 2) - 1;
587 int h = (MI_HEIGHT(mi) / 2) - 1;
591 mp->waiting_for_image_p = True;
593 load_texture_async (mi->xgwa.screen, mi->window,
594 *mp->glx_context, w, h, mp->mipmap_p,
595 mp->textures[texture_index],
596 image_loaded_cb, mp);
600 /******************************************************************************
602 * Generate internal parameters based on supplied options the parameters to
603 * ensure they are consistant.
608 # ifdef HAVE_JWZGLES /* #### glPolygonMode other than GL_FILL unimplemented */
612 /* In wire frame mode do not draw a texture */
619 /* Need to load textures if either the blob or the backgound has an image */
620 if (do_texture || do_paint_background)
622 load_textures = True;
626 load_textures = False;
629 /* If theres no texture don't calculate co-ordinates. */
632 offset_texture = False;
638 /******************************************************************************
640 * Initialise the openGL state data.
643 initialize_gl(ModeInfo *mi, GLsizei width, GLsizei height)
645 mirrorblobstruct *gp = &Mirrorblob[MI_SCREEN(mi)];
647 /* Lighting values */
648 GLfloat ambientLight[] = { 0.2f, 0.2f, 0.2f, 1.0f };
650 GLfloat lightPos0[] = {500.0f, 100.0f, 200.0f, 1.0f };
651 GLfloat whiteLight0[] = { 0.0f, 0.0f, 0.0f, 1.0f };
652 GLfloat sourceLight0[] = { 0.6f, 0.6f, 0.6f, 1.0f };
653 GLfloat specularLight0[] = { 0.8f, 0.8f, 0.9f, 1.0f };
655 GLfloat lightPos1[] = {-50.0f, -100.0f, 2500.0f, 1.0f };
656 GLfloat whiteLight1[] = { 0.0f, 0.0f, 0.0f, 1.0f };
657 GLfloat sourceLight1[] = { 0.6f, 0.6f, 0.6f, 1.0f };
658 GLfloat specularLight1[] = { 0.7f, 0.7f, 0.7f, 1.0f };
660 GLfloat specref[] = { 1.0f, 1.0f, 1.0f, 1.0f };
662 GLfloat fogColor[4] = { 0.4, 0.4, 0.5, 0.1 };
664 /* Set the internal parameters based on the configuration settings */
667 /* Set the viewport to the width and heigh of the window */
668 glViewport (0, 0, width, height );
673 glEnable(GL_LINE_SMOOTH);
674 glEnable(GL_POLYGON_SMOOTH);
677 /* The blend function is used for trasitioning between two images even when
678 * blend is not selected.
680 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
685 glFogfv(GL_FOG_COLOR, fogColor);
686 glFogf(GL_FOG_DENSITY, 0.50);
687 glFogf(GL_FOG_START, 15.0);
688 glFogf(GL_FOG_END, 30.0);
691 /* Set the shading model to smooth (Gouraud shading). */
692 glShadeModel (GL_SMOOTH);
694 glLightModelfv (GL_LIGHT_MODEL_AMBIENT, ambientLight);
695 glLightfv (GL_LIGHT0, GL_AMBIENT, whiteLight0);
696 glLightfv (GL_LIGHT0, GL_DIFFUSE, sourceLight0);
697 glLightfv (GL_LIGHT0, GL_SPECULAR, specularLight0);
698 glLightfv (GL_LIGHT0, GL_POSITION, lightPos0);
699 glEnable (GL_LIGHT0);
700 glLightfv (GL_LIGHT1, GL_AMBIENT, whiteLight1);
701 glLightfv (GL_LIGHT1, GL_DIFFUSE, sourceLight1);
702 glLightfv (GL_LIGHT1, GL_SPECULAR, specularLight1);
703 glLightfv (GL_LIGHT1, GL_POSITION, lightPos1);
704 glEnable (GL_LIGHT1);
705 glEnable (GL_LIGHTING);
707 /* Enable color tracking */
708 glEnable (GL_COLOR_MATERIAL);
710 /* Set Material properties to follow glColor values */
711 glColorMaterial (GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
713 /* Set all materials to have specular reflectivity */
714 glMaterialfv (GL_FRONT, GL_SPECULAR, specref);
715 glMateriali (GL_FRONT, GL_SHININESS, 32);
717 /* Let GL implementation scale normal vectors. */
718 glEnable (GL_NORMALIZE);
723 glLightModeli (GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR);
724 glEnable (GL_TEXTURE_2D);
726 gp->current_texture = 0;
727 glGenTextures(NUM_TEXTURES, gp->textures);
728 grab_texture(mi, gp->current_texture);
730 glMatrixMode (GL_TEXTURE);
731 glRotated (180.0, 1.0, 0.0, 0.0);
732 glMatrixMode (GL_MODELVIEW);
735 /* Clear the buffer since this is not done during a draw with motion blur */
736 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
739 /******************************************************************************
741 * Initialise the openGL state data.
744 set_blob_gl_state(GLfloat alpha)
748 glEnable(GL_LINE_SMOOTH);
749 glEnable(GL_POLYGON_SMOOTH);
754 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
758 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
761 /* The blend function is used for trasitioning between two images even when
762 * blend is not selected.
764 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
769 glCullFace (GL_BACK);
770 glEnable (GL_CULL_FACE);
771 glFrontFace (GL_CCW);
775 glDisable (GL_CULL_FACE);
781 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
782 /* Set the default blob colour to off-white. */
783 glColor4f (0.9, 0.9, 1.0, alpha);
788 glColor4f (0.9, 0.9, 1.0, 1.0);
791 glEnable(GL_DEPTH_TEST);
792 glEnable(GL_LIGHTING);
795 /******************************************************************************
797 * Initialise the data required to draw the blob allocating the memory as
800 * Return 0 on success.
803 initialise_blob(mirrorblobstruct *gp,
809 int i, u, v, node, side, face, base, base2 = 0;
810 int nodes_on_edge = resolution;
811 Vector3D node1, node2, result;
813 if (nodes_on_edge < 2)
816 gp->num_nodes = 2 * nodes_on_edge * nodes_on_edge - 4 * nodes_on_edge + 4;
817 gp->num_faces = 4 * (nodes_on_edge - 1) * (nodes_on_edge - 1);
819 gp->nodes = (Node_Data *) malloc (gp->num_nodes * sizeof (Node_Data));
822 fprintf (stderr, "Couldn't allocate gp->nodes buffer\n");
826 gp->faces = (Face_Data *) malloc (gp->num_faces * sizeof (Face_Data));
829 fprintf (stderr, "Couldn't allocate faces data buffer\n");
833 gp->bump_data = (Bump_Data *) malloc (bumps * sizeof (Bump_Data));
836 fprintf(stderr, "Couldn't allocate bump data buffer\n");
840 gp->bump_shape = (double *)malloc(bump_array_size * sizeof(double));
843 fprintf(stderr, "Couldn't allocate bump buffer\n");
847 gp->wall_shape = (double *)malloc(bump_array_size * sizeof(double));
850 fprintf(stderr, "Couldn't allocate wall bump buffer\n");
855 gp->dots = (Vector3D *)malloc(gp->num_nodes * sizeof(Vector3D));
858 fprintf(stderr, "Couldn't allocate nodes buffer\n");
862 gp->normals = (Vector3D *)malloc(gp->num_nodes * sizeof(Vector3D));
865 fprintf(stderr, "Couldn't allocate normals buffer\n");
869 gp->colours = (Colour *)malloc(gp->num_nodes * sizeof(Colour));
872 fprintf(stderr, "Couldn't allocate colours buffer\n");
876 gp->tex_coords = (Vector2D *)malloc(gp->num_nodes * sizeof(Vector2D));
879 fprintf(stderr, "Couldn't allocate gp->tex_coords buffer\n");
884 /* Initialise bump data */
885 for (i = 0; i < bumps; i++)
887 gp->bump_data[i].ax = 2.0 * (((double)random() / (double)RAND_MAX) - 0.5);
888 gp->bump_data[i].ay = 2.0 * (((double)random() / (double)RAND_MAX) - 0.5);
889 gp->bump_data[i].power = (5.0 / pow(bumps, 0.75)) * (((double)random() / (double)RAND_MAX) - 0.5);
890 gp->bump_data[i].size = 0.1 + 0.5 * (((double)random() / (double)RAND_MAX));
892 gp->bump_data[i].pos.x = 1.5 * sin(PI * gp->bump_data[i].ay)
893 * cos(PI * gp->bump_data[i].ax);
894 gp->bump_data[i].pos.y = 1.5 * cos(PI * gp->bump_data[i].ay);
895 gp->bump_data[i].pos.z = 1.5 * sin(PI * gp->bump_data[i].ay)
896 * sin(PI * gp->bump_data[i].ax);
898 gp->bump_data[i].cx = 2.0 * (((double)random() / (double)RAND_MAX) - 0.5);
899 gp->bump_data[i].cy = 2.0 * (((double)random() / (double)RAND_MAX) - 0.5);
900 gp->bump_data[i].cpower = (5.0 / pow(bumps, 0.75)) * (((double)random() / (double)RAND_MAX) - 0.5);
901 gp->bump_data[i].csize = 0.35; /*0.1 + 0.25 * (((double)random() / (double)RAND_MAX));*/
903 gp->bump_data[i].vx = 0.0;
904 gp->bump_data[i].vy = 0.0;
905 gp->bump_data[i].vpower = 0.0;
906 gp->bump_data[i].vsize = 0.0;
908 gp->bump_data[i].mx = 0.003 * ((double)random() / (double)RAND_MAX);
909 gp->bump_data[i].my = 0.003 * ((double)random() / (double)RAND_MAX);
910 gp->bump_data[i].mpower = 0.003 * ((double)random() / (double)RAND_MAX);
911 gp->bump_data[i].msize = 0.003 * ((double)random() / (double)RAND_MAX);
914 /* Initialise lookup table of bump strength */
915 for (i = 0; i < bump_array_size; i++)
918 xd = i / (double)bump_array_size;
920 xd2 = 48.0 * xd * xd;
921 gp->bump_shape[i] = 0.1 / (xd2 + 0.1);
923 xd2 = 40.0 * xd * xd * xd * xd;
924 gp->wall_shape[i] = 0.4 / (xd2 + 0.1);
929 for (side = 0; side < 4; side++)
937 * The start and end of the for loops below are modified based on the
938 * side of the tetrahedron that is being calculated to avoid duplication
939 * of the gp->nodes that are on the edges of the tetrahedron.
941 for (u = (side > 1); u < (nodes_on_edge - (side > 0)); u++)
943 node1 = partial (normalise (tetrahedron[side][0]),
944 normalise (tetrahedron[side][1]),
945 u / (double) (nodes_on_edge - 1));
946 node2 = partial (normalise (tetrahedron[side][0]),
947 normalise (tetrahedron[side][2]),
948 u / (double) (nodes_on_edge - 1));
950 for (v = (side > 1); v <= (u - (side > 2)); v++)
953 result = partial (node1, node2, v / (double) u);
957 gp->nodes[node].position = normalise (result);
958 gp->nodes[node].initial_position = gp->nodes[node].position;
959 gp->nodes[node].normal = zero_vector;
965 * Determine which nodes make up each face. The complexity is caused
966 * by having to determine the correct nodes for the edges of the
967 * tetrahedron since the common nodes on the edges are only calculated
970 for (u = 0; u < (nodes_on_edge - 1); u++)
972 for (v = 0; v <= u; v++)
977 gp->faces[face].node1 = base + ((u * (u + 1)) / 2) + v;
978 gp->faces[face].node2 =
979 base + ((u + 1) * (u + 2)) / 2 + v + 1;
980 gp->faces[face].node3 =
981 base + ((u + 1) * (u + 2)) / 2 + v;
983 if ((side == 1) && (u == (nodes_on_edge - 2)))
985 gp->faces[face].node3 =
986 ((u + 1) * (u + 2)) / 2 +
987 nodes_on_edge - v - 1;
988 gp->faces[face].node2 =
989 ((u + 1) * (u + 2)) / 2 +
990 nodes_on_edge - v - 2;
995 gp->faces[face].node1 =
996 base + (((u - 1) * u) / 2) + v - 1;
997 gp->faces[face].node2 = base + ((u) * (u + 1)) / 2 + v;
998 gp->faces[face].node3 =
999 base + ((u) * (u + 1)) / 2 + v - 1;
1001 if (u == (nodes_on_edge - 2))
1003 int n = nodes_on_edge - v - 1;
1004 gp->faces[face].node2 =
1006 (nodes_on_edge + 1)) / 2) +
1007 ((n - 1) * (n + 0)) / 2;
1008 gp->faces[face].node3 =
1010 (nodes_on_edge + 1)) / 2) +
1011 ((n + 0) * (n + 1)) / 2;
1015 gp->faces[face].node1 = (((u + 1) * (u + 2)) / 2) - 1;
1016 gp->faces[face].node3 = (((u + 2) * (u + 3)) / 2) - 1;
1021 gp->faces[face].node1 =
1022 base + (((u - 2) * (u - 1)) / 2) + v - 1;
1023 gp->faces[face].node2 = base + ((u - 1) * u) / 2 + v;
1024 gp->faces[face].node3 = base + ((u - 1) * u) / 2 + v - 1;
1028 gp->faces[face].node1 =
1029 base2 + ((u * (u + 1)) / 2) - 1;
1030 gp->faces[face].node3 =
1031 base2 + ((u + 1) * (u + 2)) / 2 - 1;
1033 if (u == (nodes_on_edge - 2))
1035 gp->faces[face].node3 =
1037 (nodes_on_edge + 1)) / 2) +
1038 ((v + 1) * (v + 2)) / 2 - 1;
1039 gp->faces[face].node2 =
1041 (nodes_on_edge + 1)) / 2) +
1042 ((v + 2) * (v + 3)) / 2 - 1;
1046 gp->faces[face].node1 = (u * (u + 1)) / 2;
1047 gp->faces[face].node2 = ((u + 1) * (u + 2)) / 2;
1057 gp->faces[face].node1 = base + ((u * (u + 1)) / 2) + v;
1058 gp->faces[face].node2 =
1059 base + ((u * (u + 1)) / 2) + v + 1;
1060 gp->faces[face].node3 =
1061 base + (((u + 1) * (u + 2)) / 2) + v + 1;
1063 if ((side == 1) && (u == (nodes_on_edge - 2)))
1065 gp->faces[face].node3 =
1066 ((u + 1) * (u + 2)) / 2 +
1067 nodes_on_edge - v - 2;
1072 gp->faces[face].node1 =
1073 base + ((u * (u - 1)) / 2) + v - 1;
1074 gp->faces[face].node2 = base + ((u * (u - 1)) / 2) + v;
1075 gp->faces[face].node3 = base + ((u * (u + 1)) / 2) + v;
1077 if (u == (nodes_on_edge - 2))
1079 int n = nodes_on_edge - v - 1;
1080 gp->faces[face].node3 =
1082 (nodes_on_edge + 1)) / 2) +
1083 ((n + 0) * (n - 1)) / 2;
1087 gp->faces[face].node1 = (((u + 1) * (u + 2)) / 2) - 1;
1092 gp->faces[face].node1 =
1093 base + (((u - 2) * (u - 1)) / 2) + v - 1;
1094 gp->faces[face].node2 =
1095 base + (((u - 2) * (u - 1)) / 2) + v;
1096 gp->faces[face].node3 = base + (((u - 1) * u) / 2) + v;
1100 gp->faces[face].node1 = base2 + (u * (u + 1)) / 2 - 1;
1102 if (u == (nodes_on_edge - 2))
1104 gp->faces[face].node3 =
1105 ((nodes_on_edge * (nodes_on_edge + 1)) / 2) +
1106 ((v + 2) * (v + 3)) / 2 - 1;
1110 gp->faces[face].node2 = (u * (u + 1)) / 2;
1122 /******************************************************************************
1124 * Return the magnitude of the given vector
1126 static inline double
1129 return sqrt (u.x * u.x + u.y * u.y + u.z * u.z);
1132 /******************************************************************************
1134 * Calculate the blob shape.
1137 calc_blob(mirrorblobstruct *gp,
1140 int bump_array_size,
1144 /* Loop variables */
1146 /* position of a node */
1149 Vector3D bump_vector;
1152 /* Update position and strength of bumps used to distort the blob */
1153 for (i = 0; i < bumps; i++)
1155 gp->bump_data[i].vx += gp->bump_data[i].mx*(gp->bump_data[i].cx - gp->bump_data[i].ax);
1156 gp->bump_data[i].vy += gp->bump_data[i].my*(gp->bump_data[i].cy - gp->bump_data[i].ay);
1157 gp->bump_data[i].vpower += gp->bump_data[i].mpower
1158 * (gp->bump_data[i].cpower - gp->bump_data[i].power);
1159 gp->bump_data[i].vsize += gp->bump_data[i].msize
1160 * (gp->bump_data[i].csize - gp->bump_data[i].size);
1162 gp->bump_data[i].ax += 0.1 * gp->bump_data[i].vx;
1163 gp->bump_data[i].ay += 0.1 * gp->bump_data[i].vy;
1164 gp->bump_data[i].power += 0.1 * gp->bump_data[i].vpower;
1165 gp->bump_data[i].size += 0.1 * gp->bump_data[i].vsize;
1167 gp->bump_data[i].pos.x = 1.0 * sin(PI * gp->bump_data[i].ay)
1168 * cos(PI * gp->bump_data[i].ax);
1169 gp->bump_data[i].pos.y = 1.0 * cos(PI * gp->bump_data[i].ay);
1170 gp->bump_data[i].pos.z = 1.0 * sin(PI * gp->bump_data[i].ay)
1171 * sin(PI * gp->bump_data[i].ax);
1174 /* Update calculate new position for each vertex based on an offset from
1175 * the initial position
1177 gp->blob_force = zero_vector;
1178 for (index = 0; index < gp->num_nodes; ++index)
1180 node = gp->nodes[index].initial_position;
1181 gp->nodes[index].normal = node;
1183 offset = zero_vector;
1184 for ( i = 0; i < bumps; i++)
1186 bump_vector = subtract(gp->bump_data[i].pos, node);
1188 dist = bump_array_size * dot(bump_vector, bump_vector) * gp->bump_data[i].size;
1190 if (dist < bump_array_size)
1192 add(&offset, scale(node, gp->bump_data[i].power * gp->bump_shape[dist]));
1193 add(&gp->blob_force, scale(node, gp->bump_data[i].power * gp->bump_shape[dist]));
1198 node = scale(node, zoom);
1199 add(&node, gp->blob_center);
1203 if (node.z < -limit) node.z = -limit;
1204 if (node.z > limit) node.z = limit;
1206 dist = bump_array_size * (node.z + limit) * (node.z + limit) * 0.5;
1207 if (dist < bump_array_size)
1209 node.x += (node.x - gp->blob_center.x) * gp->wall_shape[dist];
1210 node.y += (node.y - gp->blob_center.y) * gp->wall_shape[dist];
1211 gp->blob_force.z += (node.z + limit);
1215 dist = bump_array_size * (node.z - limit) * (node.z - limit) * 0.5;
1216 if (dist < bump_array_size)
1218 node.x += (node.x - gp->blob_center.x) * gp->wall_shape[dist];
1219 node.y += (node.y - gp->blob_center.y) * gp->wall_shape[dist];
1220 gp->blob_force.z -= (node.z - limit);
1223 if (node.y < -limit) node.y = -limit;
1224 if (node.y > limit) node.y = limit;
1226 dist = bump_array_size * (node.y + limit) * (node.y + limit) * 0.5;
1227 if (dist < bump_array_size)
1229 node.x += (node.x - gp->blob_center.x) * gp->wall_shape[dist];
1230 node.z += (node.z - gp->blob_center.z) * gp->wall_shape[dist];
1231 gp->blob_force.y += (node.y + limit);
1235 dist = bump_array_size * (node.y - limit) * (node.y - limit) * 0.5;
1236 if (dist < bump_array_size)
1238 node.x += (node.x - gp->blob_center.x) * gp->wall_shape[dist];
1239 node.z += (node.z - gp->blob_center.z) * gp->wall_shape[dist];
1240 gp->blob_force.y -= (node.y - limit);
1244 if (node.x < -limit) node.x = -limit;
1245 if (node.x > limit) node.x = limit;
1247 dist = bump_array_size * (node.x + limit) * (node.x + limit) * 0.5;
1248 if (dist < bump_array_size)
1250 node.y += (node.y - gp->blob_center.y) * gp->wall_shape[dist];
1251 node.z += (node.z - gp->blob_center.z) * gp->wall_shape[dist];
1252 gp->blob_force.x += (node.x + limit);
1256 dist = bump_array_size * (node.x - limit) * (node.x - limit) * 0.5;
1257 if (dist < bump_array_size)
1259 node.y += (node.y - gp->blob_center.y) * gp->wall_shape[dist];
1260 node.z += (node.z - gp->blob_center.z) * gp->wall_shape[dist];
1261 gp->blob_force.x -= (node.x - limit);
1265 if (node.y < -limit) node.y = -limit;
1266 if (node.y > limit) node.y = limit;
1269 gp->dots[index] = node;
1272 /* Determine the normal for each face */
1273 for (face = 0; face < gp->num_faces; face++)
1275 /* Use pointers to indexed nodes to help readability */
1276 int index1 = gp->faces[face].node1;
1277 int index2 = gp->faces[face].node2;
1278 int index3 = gp->faces[face].node3;
1280 gp->faces[face].normal = cross(subtract(gp->dots[index2], gp->dots[index1]),
1281 subtract(gp->dots[index3], gp->dots[index1]));
1283 /* Add the normal for the face onto the normal for the verticies of
1285 add(&gp->nodes[index1].normal, gp->faces[face].normal);
1286 add(&gp->nodes[index2].normal, gp->faces[face].normal);
1287 add(&gp->nodes[index3].normal, gp->faces[face].normal);
1290 /* Use the normal to set the colour and texture */
1291 if (do_colour || do_texture)
1293 for (index = 0; index < gp->num_nodes; ++index)
1295 gp->normals[index] = normalise(gp->nodes[index].normal);
1299 gp->colours[index].red = (int)(255.0 * fabs(gp->normals[index].x));
1300 gp->colours[index].green = (int)(255.0 * fabs(gp->normals[index].y));
1301 gp->colours[index].blue = (int)(255.0 * fabs(gp->normals[index].z));
1302 gp->colours[index].alpha = (int)(255.0 * fade);
1308 const float cube_size = 100.0;
1309 Vector3D eye = {0.0, 0.0, 50.0};
1310 Vector3D eye_r = normalise(subtract(gp->dots[index], eye));
1311 Vector3D reference = subtract(eye_r, scale(gp->normals[index], 2.0 * dot(eye_r, gp->normals[index])));
1314 double n, n_min = 10000.0, sign = 1.0;
1315 if (fabs(reference.z) > 1e-9)
1317 n = (cube_size - gp->dots[index].z) / reference.z;
1320 n = (-cube_size - gp->dots[index].z) / reference.z;
1325 x = sign * (gp->dots[index].x + n * reference.x);
1326 y = sign * (gp->dots[index].y + n * reference.y);
1330 if (fabs(reference.x) > 1e-9)
1332 n = (cube_size - gp->dots[index].x) / reference.x;
1336 n = (-cube_size - gp->dots[index].x) / reference.x;
1339 if ((n > 0.0) && (n < n_min))
1341 x = sign * (2.0 * cube_size - (gp->dots[index].z + n * reference.z));
1342 y = sign * x * (gp->dots[index].y + n * reference.y) / cube_size;
1346 if (fabs(reference.y) > 1e-9)
1348 n = (cube_size - gp->dots[index].y) / reference.y;
1352 n = (-cube_size - gp->dots[index].y) / reference.y;
1355 if ((n > 0.0) && (n < n_min))
1357 y = sign * (2.0 * cube_size -( gp->dots[index].z + n * reference.z));
1358 x = sign * y * (gp->dots[index].x + n * reference.x) / cube_size;
1362 gp->tex_coords[index].x = 0.5 + x / (cube_size * 6.0);
1363 gp->tex_coords[index].y = 0.5 - y / (cube_size * 6.0);
1367 gp->tex_coords[index].x = 0.5
1368 * (1.0 + asin(gp->normals[index].x) / (0.5 * PI));
1369 gp->tex_coords[index].y = -0.5
1370 * (1.0 + asin(gp->normals[index].y) / (0.5 * PI));
1372 /* Adjust the texture co-ordinates to from range 0..1 to
1373 * 0..width or 0..height as appropriate
1375 gp->tex_coords[index].x *= gp->tex_width[gp->current_texture];
1376 gp->tex_coords[index].y *= gp->tex_height[gp->current_texture];
1381 /* Update the center of the whole blob */
1382 add(&gp->blob_velocity, scale (subtract (gp->blob_anchor, gp->blob_center), 1.0 / 80.0));
1383 add(&gp->blob_velocity, scale (gp->blob_force, 0.01 / gp->num_nodes));
1385 add(&gp->blob_center, scale(gp->blob_velocity, 0.5));
1387 gp->blob_velocity = scale(gp->blob_velocity, 0.999);
1391 draw_vertex(mirrorblobstruct *gp, int index)
1395 glColor3ub(gp->colours[index].red,
1396 gp->colours[index].green,
1397 gp->colours[index].blue);
1401 glTexCoord2fv(&gp->tex_coords[index].x);
1403 glNormal3fv(&gp->normals[index].x);
1404 glVertex3fv(&gp->dots[index].x);
1407 /******************************************************************************
1409 * Draw the blob shape.
1413 draw_blob (mirrorblobstruct *gp)
1417 glMatrixMode(GL_MODELVIEW);
1419 glRotatef(current_device_rotation(), 0, 0, 1);
1421 /* Move down the z-axis. */
1422 glTranslatef (0.0, 0.0, -4.0);
1424 gltrackball_rotate (gp->trackball);
1426 /* glColor4ub (255, 0, 0, 128); */
1427 glBegin(GL_TRIANGLES);
1428 for (face = 0; face < gp->num_faces; face++)
1430 draw_vertex(gp, gp->faces[face].node1);
1431 draw_vertex(gp, gp->faces[face].node2);
1432 draw_vertex(gp, gp->faces[face].node3);
1438 for (face = 0; face < gp->num_faces; face++)
1440 if (gp->normals[gp->faces[face].node1].z > 0.0)
1442 Vector3D end = gp->dots[gp->faces[face].node1];
1444 add(&end, scale(gp->normals[gp->faces[face].node1], 0.25));
1454 /******************************************************************************
1456 * Draw the background image simply map a texture onto a full screen quad.
1459 draw_background (ModeInfo *mi)
1461 mirrorblobstruct *gp = &Mirrorblob[MI_SCREEN(mi)];
1462 GLfloat rot = current_device_rotation();
1464 glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
1465 glEnable (GL_TEXTURE_2D);
1466 glDisable(GL_LIGHTING);
1467 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
1469 /* Reset the projection matrix to make it easier to get the size of the quad
1472 glMatrixMode(GL_PROJECTION);
1476 glRotatef (rot, 0, 0, 1);
1477 if ((rot > 45 && rot < 135) ||
1478 (rot < -45 && rot > -135))
1480 GLfloat s = MI_WIDTH(mi) / (GLfloat) MI_HEIGHT(mi);
1481 glScalef (s, 1/s, 1);
1484 glOrtho(0.0, MI_WIDTH(mi), MI_HEIGHT(mi), 0.0, -1000.0, 1000.0);
1488 glTexCoord2f (0.0, 0.0);
1491 glTexCoord2f (0.0, gp->tex_height[gp->current_texture]);
1492 glVertex2i (0, MI_HEIGHT(mi));
1494 glTexCoord2f (gp->tex_width[gp->current_texture], gp->tex_height[gp->current_texture]);
1495 glVertex2i (MI_WIDTH(mi), MI_HEIGHT(mi));
1497 glTexCoord2f (gp->tex_width[gp->current_texture], 0.0);
1498 glVertex2i (MI_WIDTH(mi), 0);
1502 glMatrixMode (GL_MODELVIEW);
1503 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
1506 /******************************************************************************
1511 draw_scene(ModeInfo * mi)
1513 mirrorblobstruct *gp = &Mirrorblob[MI_SCREEN(mi)];
1516 double current_time;
1517 check_gl_error ("draw_scene");
1519 mi->polygon_count = 0;
1520 glColor4f (1.0, 1.0, 1.0, 1.0);
1522 current_time = double_time();
1526 glColor4f (0.0, 0.0, 0.0, 1.0);
1531 fade = 1.0 - (current_time - gp->state_start_time) / fade_time;
1534 case LOADING: /* FALL-THROUGH */
1540 /* Set the correct texture, when transitioning this ensures that the first draw
1541 * is the original texture (which has the new texture drawn over it with decreasing
1546 glBindTexture(GL_TEXTURE_2D, gp->textures[gp->current_texture]);
1549 glDisable (GL_DEPTH_TEST);
1550 if (do_paint_background)
1552 glEnable (GL_TEXTURE_2D);
1553 if (motion_blur > 0.0)
1555 glClear(GL_DEPTH_BUFFER_BIT);
1556 glEnable (GL_BLEND);
1557 glColor4f (1.0, 1.0, 1.0, motion_blur);
1561 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1563 draw_background (mi);
1564 mi->polygon_count++;
1566 /* When transitioning between two images paint the new image over the old
1567 * image with a varying alpha value to get a smooth fade.
1569 if (gp->state == TRANSITIONING)
1571 glEnable (GL_BLEND);
1572 /* Select the texture to transition to */
1573 glBindTexture (GL_TEXTURE_2D, gp->textures[1 - gp->current_texture]);
1574 glColor4f (1.0, 1.0, 1.0, 1.0 - fade);
1576 draw_background (mi);
1577 mi->polygon_count++;
1579 /* Select the original texture to draw the blob */
1580 glBindTexture (GL_TEXTURE_2D, gp->textures[gp->current_texture]);
1582 /* Clear the depth buffer bit so the backgound is behind the blob */
1583 glClear(GL_DEPTH_BUFFER_BIT);
1585 else if (motion_blur > 0.0)
1587 glEnable (GL_BLEND);
1588 glColor4f (0.0, 0.0, 0.0, motion_blur);
1589 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
1590 glTranslatef (0.0, 0.0, -4.0);
1591 glRectd (-10.0, -10.0, 10.0, 10.0);
1594 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
1596 glClear(GL_DEPTH_BUFFER_BIT);
1600 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1606 glDisable (GL_TEXTURE_2D);
1609 calc_blob(gp, MI_WIDTH(mi), MI_HEIGHT(mi), BUMP_ARRAY_SIZE, 2.5, fade * blend);
1611 set_blob_gl_state(fade * blend);
1615 /* Disable the colour chanels so that only the depth buffer is updated */
1616 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
1618 mi->polygon_count += gp->num_faces;
1619 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
1622 glDepthFunc(GL_LEQUAL);
1624 mi->polygon_count += gp->num_faces;
1626 /* While transitioning between images draw a second blob with a modified
1629 if (load_textures && (hold_time > 0))
1634 if (!gp->waiting_for_image_p)
1636 gp->state = HOLDING;
1641 if ((current_time - gp->state_start_time) > hold_time)
1643 grab_texture(mi, 1 - gp->current_texture);
1644 gp->state = LOADING;
1649 /* Once the image has loaded move to the TRANSITIONING STATE */
1650 if (!gp->waiting_for_image_p)
1652 gp->state = TRANSITIONING;
1653 /* Get the time again rather than using the current time so
1654 * that the time taken by the grab_texture function is not part
1657 gp->state_start_time = double_time();
1663 /* If the blob is textured draw over existing blob to fade between
1668 /* Select the texture to transition to */
1669 glBindTexture (GL_TEXTURE_2D, gp->textures[1 - gp->current_texture]);
1670 glEnable (GL_BLEND);
1672 /* If colour is enabled update the alpha data in the buffer and
1673 * use that in the blending since the alpha of the incomming
1674 * verticies will not be correct
1678 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
1679 glClearColor(0.0, 0.0, 0.0, (1.0 - fade) * blend);
1680 glClear(GL_COLOR_BUFFER_BIT);
1681 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
1682 glBlendFunc(GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA);
1686 glColor4f (0.9, 0.9, 1.0, (1.0 - fade) * blend);
1690 mi->polygon_count += gp->num_faces;
1694 /* Restore the 'standard' blend functions. */
1695 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1699 if ((current_time - gp->state_start_time) > fade_time)
1701 gp->state = HOLDING;
1702 gp->state_start_time = current_time;
1703 gp->current_texture = 1 - gp->current_texture;
1711 /******************************************************************************
1713 * XMirrorblob screen update entry
1716 draw_mirrorblob(ModeInfo * mi)
1718 mirrorblobstruct *gp = &Mirrorblob[MI_SCREEN(mi)];
1719 Display *display = MI_DISPLAY(mi);
1720 Window window = MI_WINDOW(mi);
1722 if (!gp->glx_context)
1725 /* Wait for the first image; for subsequent images, load them in the
1726 background while animating. */
1727 if (gp->waiting_for_image_p && gp->first_image_p)
1730 glXMakeCurrent(display, window, *(gp->glx_context));
1732 if (mi->fps_p) do_fps (mi);
1734 glXSwapBuffers(display, window);
1737 /******************************************************************************
1739 * XMirrorblob screen resize entry
1742 reshape_mirrorblob(ModeInfo *mi, int width, int height)
1744 glViewport( 0, 0, MI_WIDTH(mi), MI_HEIGHT(mi) );
1745 reset_projection(width, height);
1748 /****************************************************************************
1750 * Handle Mouse events
1753 mirrorblob_handle_event (ModeInfo * mi, XEvent * event)
1755 mirrorblobstruct *gp = &Mirrorblob[MI_SCREEN (mi)];
1757 if (event->xany.type == ButtonPress &&
1758 event->xbutton.button == Button1)
1760 gp->button_down = 1;
1761 gltrackball_start (gp->trackball, event->xbutton.x,
1762 event->xbutton.y, MI_WIDTH (mi), MI_HEIGHT (mi));
1765 else if (event->xany.type == ButtonRelease &&
1766 event->xbutton.button == Button1)
1768 gp->button_down = 0;
1771 else if (event->xany.type == ButtonPress &&
1772 event->xbutton.button == Button4)
1777 else if (event->xany.type == ButtonPress &&
1778 event->xbutton.button == Button5)
1784 else if (event->xany.type == MotionNotify && gp->button_down)
1786 gltrackball_track (gp->trackball, event->xmotion.x,
1787 event->xmotion.y, MI_WIDTH (mi), MI_HEIGHT (mi));
1793 /******************************************************************************
1795 * XMirrorblob initialise entry
1798 init_mirrorblob(ModeInfo * mi)
1800 int screen = MI_SCREEN(mi);
1802 mirrorblobstruct *gp;
1804 if (Mirrorblob == NULL)
1806 if ((Mirrorblob = (mirrorblobstruct *)
1807 calloc(MI_NUM_SCREENS(mi), sizeof (mirrorblobstruct))) == NULL)
1812 gp = &Mirrorblob[screen];
1814 gp->window = MI_WINDOW(mi);
1815 if ((gp->glx_context = init_GL(mi)) != NULL)
1817 reshape_mirrorblob(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
1818 initialize_gl(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
1824 gp->trackball = gltrackball_init();
1826 initialise_blob(gp, MI_WIDTH(mi), MI_HEIGHT(mi), BUMP_ARRAY_SIZE);
1827 gp->state = INITIALISING;
1828 gp->state_start_time = double_time();
1830 gp->first_image_p = True;
1833 /******************************************************************************
1835 * XMirrorblob cleanup entry
1838 release_mirrorblob(ModeInfo * mi)
1840 if (Mirrorblob != NULL) {
1842 for (i = 0; i < MI_NUM_SCREENS(mi); i++) {
1843 mirrorblobstruct *gp = &Mirrorblob[i];
1844 if (gp->nodes) free(gp->nodes);
1845 if (gp->faces) free(gp->faces);
1846 if (gp->bump_data) free(gp->bump_data);
1847 if (gp->colours) free(gp->colours);
1848 if (gp->tex_coords) free(gp->tex_coords);
1849 if (gp->dots) free(gp->dots);
1850 if (gp->wall_shape) free(gp->wall_shape);
1851 if (gp->bump_shape) free(gp->bump_shape);
1860 XSCREENSAVER_MODULE ("MirrorBlob", mirrorblob)