+ * normalise vector v
+ */
+static inline Vector3D
+normalise (const Vector3D v)
+{
+ Vector3D result;
+ double magnitude;
+
+ magnitude = sqrt (dot(v, v));
+
+ if (magnitude > 1e-300)
+ {
+ result = scale (v, 1.0 / magnitude);
+ }
+ else
+ {
+ printf("zero\n");
+ result = zero_vector;
+ }
+ return result;
+}
+
+/******************************************************************************
+ *
+ * Calculate the transform matrix for the given quaternion
+ */
+static void
+quaternion_transform (Quaternion q, GLdouble * transform)
+{
+ GLdouble x, y, z, w;
+ x = q.x;
+ y = q.y;
+ z = q.z;
+ w = q.w;
+
+ transform[0] = (w * w) + (x * x) - (y * y) - (z * z);
+ transform[1] = (2.0 * x * y) + (2.0 * w * z);
+ transform[2] = (2.0 * x * z) - (2.0 * w * y);
+ transform[3] = 0.0;
+
+ transform[4] = (2.0 * x * y) - (2.0 * w * z);
+ transform[5] = (w * w) - (x * x) + (y * y) - (z * z);
+ transform[6] = (2.0 * y * z) + (2.0 * w * x);
+ transform[7] = 0.0;
+
+ transform[8] = (2.0 * x * z) + (2.0 * w * y);
+ transform[9] = (2.0 * y * z) - (2.0 * w * x);
+ transform[10] = (w * w) - (x * x) - (y * y) + (z * z);
+ transform[11] = 0.0;
+
+ transform[12] = 0.0;
+ transform[13] = 0.0;
+ transform[14] = 0.0;
+ transform[15] = (w * w) + (x * x) + (y * y) + (z * z);
+}
+
+/******************************************************************************
+ *
+ * Apply a matrix transform to the given vector
+ */
+static inline Vector3D
+vector_transform (Vector3D u, GLdouble * t)
+{
+ Vector3D result;
+
+ result.x = (u.x * t[0] + u.y * t[4] + u.z * t[8] + 1.0 * t[12]);
+ result.y = (u.x * t[1] + u.y * t[5] + u.z * t[9] + 1.0 * t[13]);
+ result.z = (u.x * t[2] + u.y * t[6] + u.z * t[10] + 1.0 * t[14]);
+
+ return result;
+}
+
+/******************************************************************************
+ *
+ * Return a node that is on an arc between node1 and node2, where distance
+ * is the proportion of the distance from node1 to the total arc.
+ */
+static Vector3D
+partial (Vector3D node1, Vector3D node2, double distance)
+{
+ Vector3D result;
+ Vector3D rotation_axis;
+ GLdouble transformation[16];
+ double angle;
+ Quaternion rotation;
+
+ rotation_axis = normalise (cross (node1, node2));
+ angle = acos (dot (node1, node2)) * distance;
+
+ rotation.x = rotation_axis.x * sin (angle / 2.0);
+ rotation.y = rotation_axis.y * sin (angle / 2.0);
+ rotation.z = rotation_axis.z * sin (angle / 2.0);
+ rotation.w = cos (angle / 2.0);
+
+ quaternion_transform (rotation, transformation);
+
+ result = vector_transform (node1, transformation);
+
+ return result;
+}
+
+/****************************************************************************
+ *
+ * Load a texture.
+ */
+
+static void
+image_loaded_cb (const char *filename, XRectangle *geometry,
+ int image_width, int image_height,
+ int texture_width, int texture_height,
+ void *closure)
+{
+ mirrorblobstruct *mp = (mirrorblobstruct *) closure;
+ GLint texid = -1;
+ int texture_index = -1;
+ int i;
+
+ glGetIntegerv (GL_TEXTURE_BINDING_2D, &texid);
+ if (texid < 0) abort();
+
+ for (i = 0; i < NUM_TEXTURES; i++) {
+ if (mp->textures[i] == texid) {
+ texture_index = i;
+ break;
+ }
+ }
+ if (texture_index < 0) abort();
+
+ mp->tex_width [texture_index] = (GLfloat) image_width / texture_width;
+ mp->tex_height[texture_index] = -(GLfloat) image_height / texture_height;
+
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
+ (mp->mipmap_p ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR));
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+ mp->waiting_for_image_p = False;
+ mp->first_image_p = True;
+}
+
+
+static void
+grab_texture(ModeInfo *mi, int texture_index)
+{
+ mirrorblobstruct *mp = &Mirrorblob[MI_SCREEN(mi)];
+
+ mp->waiting_for_image_p = True;
+ mp->mipmap_p = True;
+ load_texture_async (mi->xgwa.screen, mi->window,
+ *mp->glx_context, 0, 0, mp->mipmap_p,
+ mp->textures[texture_index],
+ image_loaded_cb, mp);
+}
+
+/******************************************************************************
+ *
+ * Generate internal parameters based on supplied options the parameters to
+ * ensure they are consistant.
+ */
+static void
+set_parameters(void)
+{
+ /* In wire frame mode do not draw a texture */
+ if (wireframe)
+ {
+ do_texture = False;
+ blend = 1.0;
+ }
+
+ /* Need to load textures if either the blob or the backgound has an image */
+ if (do_texture || do_paint_background)
+ {
+ load_textures = True;
+ }
+ else
+ {
+ load_textures = False;
+ }
+
+ /* If theres no texture don't calculate co-ordinates. */
+ if (!do_texture)
+ {
+ offset_texture = False;
+ }
+
+ culling = True;
+}
+
+/******************************************************************************
+ *
+ * Initialise the openGL state data.