* 19-Oct-2003: jon.dowdall@bigpond.com Added texturing
* 21-Oct-2003: Renamed to mirrorblob
* 10-Feb-2004: jon.dowdall@bigpond.com Added motion blur
+ * 28-Jan-2006: jon.dowdall@bigpond.com Big clean up and bug fixes
*
* The mirrorblob screensaver draws a pulsing blob on the screen. Options
* include adding a background (via screen_to_texture), texturing the blob,
* The blob was inspired by a lavalamp is in no way a simulation. The code is
* just an attempt to generate some eye-candy.
*
- * Much of xscreensaver code framework is taken from the pulsar module by David
+ * Much of xmirrorblob code framework is taken from the pulsar module by David
* Konerding and the glslideshow by Mike Oliphant and Jamie Zawinski.
*
*/
#include <math.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/time.h>
#ifdef STANDALONE
-# define PROGCLASS "Screensaver"
-# define HACK_INIT init_screensaver
-# define HACK_DRAW draw_screensaver
-# define HACK_RESHAPE reshape_screensaver
-# define screensaver_opts xlockmore_opts
+#define DEFAULTS \
+ "*delay: " DEF_DELAY "\n" \
+ "*showFPS: " DEF_FPS "\n" \
+ "*useSHM: True \n"
+
+# define refresh_mirrorblob 0
+/*
+# define mirrorblob_handle_event 0
+*/
+# include "xlockmore.h" /* from the xmirrorblob distribution */
+#else /* !STANDALONE */
+# include "xlock.h" /* from the xlockmore distribution */
+#endif /* !STANDALONE */
+
+#ifdef USE_GL /* whole file */
+
#define DEF_DELAY "10000"
#define DEF_FPS "False"
#define DEF_WIRE "False"
-#define DEF_BLEND "False"
+#define DEF_BLEND "1.0"
#define DEF_FOG "False"
#define DEF_ANTIALIAS "False"
-#define DEF_WALLS "True"
+#define DEF_WALLS "False"
#define DEF_COLOUR "False"
+#define DEF_ASYNC "True"
#define DEF_TEXTURE "True"
#define DEF_OFFSET_TEXTURE "False"
#define DEF_PAINT_BACKGROUND "True"
-#define DEF_X_RES "60"
-#define DEF_Y_RES "32"
-#define DEF_FIELD_POINTS "5"
-#define DEF_MOTION_BLUR "0"
+#define DEF_RESOLUTION "30"
+#define DEF_BUMPS "10"
+#define DEF_MOTION_BLUR "0.0"
#define DEF_INCREMENTAL "0"
-#define DEF_HOLD_TIME "30"
-#define DEF_FADE_TIME "5"
-
-#define DEFAULTS \
- "*delay: " DEF_DELAY "\n" \
- "*showFPS: " DEF_FPS "\n" \
- "*wire: " DEF_WIRE "\n" \
- "*blend: " DEF_BLEND "\n" \
- "*fog: " DEF_FOG "\n" \
- "*antialias: " DEF_ANTIALIAS "\n" \
- "*walls: " DEF_WALLS "\n" \
- "*colour : " DEF_COLOUR "\n" \
- "*texture: " DEF_TEXTURE "\n" \
- "*offset_texture: " DEF_OFFSET_TEXTURE "\n" \
- "*paint_background: " DEF_PAINT_BACKGROUND "\n" \
- "*x_resolution: " DEF_X_RES "\n" \
- "*y_resolution: " DEF_Y_RES "\n" \
- "*field_points: " DEF_FIELD_POINTS "\n" \
- "*motion_blur: " DEF_MOTION_BLUR "\n" \
- "*incremental: " DEF_INCREMENTAL "\n" \
- "*hold_time: " DEF_HOLD_TIME "\n" \
- "*fade_time: " DEF_FADE_TIME "\n" \
- "*useSHM: True \n"
-
-# include "xlockmore.h" /* from the xscreensaver distribution */
-#else /* !STANDALONE */
-# include "xlock.h" /* from the xlockmore distribution */
-#endif /* !STANDALONE */
-
-#ifdef USE_GL /* whole file */
+#define DEF_HOLD_TIME "30.0"
+#define DEF_FADE_TIME "5.0"
+#define DEF_ZOOM "1.0"
#ifdef HAVE_XMU
# ifndef VMS
# endif /* VMS */
#endif
-#include <GL/gl.h>
-#include <GL/glu.h>
-#include "GL/glx.h"
-
-#include <stdlib.h>
-#include <stdio.h>
-/*#include <string.h>*/
+#include "gltrackball.h"
#include "grab-ximage.h"
#undef countof
#define PI 3.1415926535897
-/* */
-static int do_wire;
-static int do_blend;
-static int do_fog;
-static int do_antialias;
-static int do_walls;
-static int do_texture;
-static int do_paint_background;
-static int do_colour;
-static int offset_texture;
-static int x_resolution;
-static int y_resolution;
-static int field_points;
-static int motion_blur;
-static int incremental;
-static int fade_time;
-static int hold_time;
+/* Options from command line */
+static GLfloat blend;
+static Bool wireframe;
+static Bool do_fog;
+static Bool do_antialias;
+static Bool do_walls;
+static Bool do_texture;
+static Bool do_paint_background;
+static Bool do_colour;
+static Bool offset_texture;
+static int resolution;
+static int bumps;
+static float motion_blur;
+static float fade_time;
+static float hold_time;
+static float zoom;
+
+/* Internal parameters based on supplied options */
+static Bool culling;
+static Bool load_textures;
static XrmOptionDescRec opts[] = {
{"-wire", ".blob.wire", XrmoptionNoArg, "true" },
{"+wire", ".blob.wire", XrmoptionNoArg, "false" },
- {"-blend", ".blob.blend", XrmoptionNoArg, "true" },
- {"+blend", ".blob.blend", XrmoptionNoArg, "false" },
+ {"-blend", ".blob.blend", XrmoptionSepArg, 0 },
{"-fog", ".blob.fog", XrmoptionNoArg, "true" },
{"+fog", ".blob.fog", XrmoptionNoArg, "false" },
{"-antialias", ".blob.antialias", XrmoptionNoArg, "true" },
{"+texture", ".blob.texture", XrmoptionNoArg, "false" },
{"-colour", ".blob.colour", XrmoptionNoArg, "true" },
{"+colour", ".blob.colour", XrmoptionNoArg, "false" },
- {"-offset_texture", ".blob.offset_texture", XrmoptionNoArg, "true" },
- {"+offset_texture", ".blob.offset_texture", XrmoptionNoArg, "false" },
- {"-paint_background", ".blob.paint_background", XrmoptionNoArg, "true" },
- {"+paint_background", ".blob.paint_background", XrmoptionNoArg, "false" },
- {"-x_res", ".blob.x_res", XrmoptionSepArg, NULL },
- {"-y_res", ".blob.y_res", XrmoptionSepArg, NULL },
- {"-field_points", ".blob.field_points", XrmoptionSepArg, NULL },
- {"-motion_blur", ".blob.motion_blur", XrmoptionSepArg, NULL },
- {"-incremental", ".blob.incremental", XrmoptionSepArg, NULL },
- {"-fade_time", ".blob.fade_time", XrmoptionSepArg, NULL },
- {"-hold_time", ".blob.hold_time", XrmoptionSepArg, NULL },
+ {"-offset-texture", ".blob.offset_texture", XrmoptionNoArg, "true" },
+ {"+offset-texture", ".blob.offset_texture", XrmoptionNoArg, "false" },
+ {"-paint-background", ".blob.paint_background", XrmoptionNoArg, "true" },
+ {"+paint-background", ".blob.paint_background", XrmoptionNoArg, "false" },
+ {"-resolution", ".blob.resolution", XrmoptionSepArg, NULL },
+ {"-bumps", ".blob.bumps", XrmoptionSepArg, NULL },
+ {"-motion-blur", ".blob.motion_blur", XrmoptionSepArg, 0 },
+ {"-fade-time", ".blob.fade_time", XrmoptionSepArg, 0 },
+ {"-hold-time", ".blob.hold_time", XrmoptionSepArg, 0 },
+ {"-zoom", ".blob.zoom", XrmoptionSepArg, 0 },
};
static argtype vars[] = {
- {&do_wire, "wire", "Wire", DEF_WIRE, t_Bool},
- {&do_blend, "blend", "Blend", DEF_BLEND, t_Bool},
+ {&wireframe, "wire", "Wire", DEF_WIRE, t_Bool},
+ {&blend, "blend", "Blend", DEF_BLEND, t_Float},
{&do_fog, "fog", "Fog", DEF_FOG, t_Bool},
{&do_antialias, "antialias", "Antialias", DEF_ANTIALIAS, t_Bool},
{&do_walls, "walls", "Walls", DEF_WALLS, t_Bool},
{&do_colour, "colour", "Colour", DEF_COLOUR, t_Bool},
{&offset_texture, "offset_texture","Offset_Texture", DEF_OFFSET_TEXTURE, t_Bool},
{&do_paint_background,"paint_background","Paint_Background", DEF_PAINT_BACKGROUND, t_Bool},
- {&x_resolution, "x_res", "X_Res", DEF_X_RES, t_Int},
- {&y_resolution, "y_res", "Y_Res", DEF_Y_RES, t_Int},
- {&field_points, "field_points", "Field_Points", DEF_FIELD_POINTS, t_Int},
- {&motion_blur, "motion_blur", "Motion_Blur", DEF_MOTION_BLUR, t_Int},
- {&incremental, "incremental", "Incremental", DEF_INCREMENTAL, t_Int},
- {&fade_time, "fade_time", "Fade_Time", DEF_FADE_TIME, t_Int},
- {&hold_time, "hold_time", "Hold_Time", DEF_HOLD_TIME, t_Int},
+ {&resolution, "resolution", "Resolution", DEF_RESOLUTION, t_Int},
+ {&bumps, "bumps", "Bump", DEF_BUMPS, t_Int},
+ {&motion_blur, "motion_blur", "Motion_Blur", DEF_MOTION_BLUR, t_Float},
+ {&fade_time, "fade_time", "Fade_Time", DEF_FADE_TIME, t_Float},
+ {&hold_time, "hold_time", "Hold_Time", DEF_HOLD_TIME, t_Float},
+ {&zoom, "zoom", "Zoom", DEF_ZOOM, t_Float},
};
{"-/+ colour", "whether to colour the blob"},
{"-/+ offset_texture", "whether to offset texture co-ordinates"},
{"-/+ paint_background", "whether to display a background texture (slower)"},
- {"-x_res", "Blob resolution in x direction"},
- {"-y_res", "Blob resolution in y direction"},
- {"-field_points", "Number of field points used to disturb blob"},
+ {"-resolution", "Resolution of blob tesselation"},
+ {"-bumps", "Number of bumps used to disturb blob"},
{"-motion_blur", "Fade blob images (higher number = faster fade)"},
- {"-incremental", "Field summation method"},
{"-fade_time", "Number of frames to transistion to next image"},
{"-hold_time", "Number of frames before next image"},
};
-ModeSpecOpt screensaver_opts = {countof(opts), opts, countof(vars), vars, desc};
+ENTRYPOINT ModeSpecOpt mirrorblob_opts = {countof(opts), opts, countof(vars), vars, desc};
#ifdef USE_MODULES
-ModStruct screensaver_description =
-{"screensaver", "init_screensaver", "draw_screensaver", "release_screensaver",
- "draw_screensaver", "init_screensaver", NULL, &screensaver_opts,
+ModStruct mirrorblob_description =
+{"mirrorblob", "init_mirrorblob", "draw_mirrorblob", "release_mirrorblob",
+ "draw_mirrorblob", "init_mirrorblob", "handle_event", &mirrorblob_opts,
1000, 1, 2, 1, 4, 1.0, "",
- "OpenGL screensaver", 0, NULL};
+ "OpenGL mirrorblob", 0, NULL};
#endif
-/* structure for holding the screensaver data */
-typedef struct {
- int screen_width, screen_height;
- GLXContext *glx_context;
- Window window;
- XColor fg, bg;
-} screensaverstruct;
-
-static screensaverstruct *Screensaver = NULL;
/*****************************************************************************
* Types used in blob code
GLdouble x, y, z;
} Vector3D;
+typedef struct
+{
+ GLdouble w;
+ GLdouble x;
+ GLdouble y;
+ GLdouble z;
+} Quaternion;
+
typedef struct
{
GLubyte red, green, blue, alpha;
} Colour;
-/* Data used for sphere tessellation */
typedef struct
{
- double cosyd, sinyd;
+ Vector3D initial_position;
+ Vector3D position;
+ Vector3D normal;
+} Node_Data;
- /* Number of x points at each row of the blob */
- int num_x_points;
-} Row_Data;
+typedef struct
+{
+ int node1, node2, node3;
+ Vector3D normal;
+ double length1, length2, length3;
+} Face_Data;
-/* Structure to hold sphere distortion data */
+/* Structure to hold data about bumps used to distortion sphere */
typedef struct
{
- double cx, cy, cpower;
- double mx, my, mpower;
- double ax, ay, apower;
- double vx, vy, vpower;
+ double cx, cy, cpower, csize;
+ double ax, ay, power, size;
+ double mx, my, mpower, msize;
+ double vx, vy, vpower, vsize;
Vector3D pos;
-} Field_Data;
+} Bump_Data;
+
+/* Vertices of a tetrahedron */
+#define sqrt_3 0.5773502692
+/*#undef sqrt_3
+#define sqrt_3 1.0*/
+#define PPP { sqrt_3, sqrt_3, sqrt_3 } /* +X, +Y, +Z */
+#define MMP { -sqrt_3, -sqrt_3, sqrt_3 } /* -X, -Y, +Z */
+#define MPM { -sqrt_3, sqrt_3, -sqrt_3 } /* -X, +Y, -Z */
+#define PMM { sqrt_3, -sqrt_3, -sqrt_3 } /* +X, -Y, -Z */
+
+/* Structure describing a tetrahedron */
+Vector3D tetrahedron[4][3] = {
+ {PPP, MMP, MPM},
+ {PMM, MPM, MMP},
+ {PPP, MPM, PMM},
+ {PPP, PMM, MMP}
+};
/*****************************************************************************
* Static blob data
*****************************************************************************/
-static Row_Data *row_data;
-
-/* Parameters controlling the position of the blob as a whole */
-static Vector3D blob_center = {0.0, 0.0, 0.0};
-static Vector3D blob_anchor = {0.0, 0.0, 0.0};
-static Vector3D blob_velocity = {0.0, 0.0, 0.0};
-static Vector3D blob_force = {0.0, 0.0, 0.0};
-
-/* Count of the total number of points */
-static int num_points;
-
-static Vector3D *dots = NULL;
-static Vector3D *normals = NULL;
-static Colour *colours = NULL;
-static Vector2D *tex_coords = NULL;
-
-/* Pointer to the field function results */
-static double *field = 0, *wall_field = 0;
-
-Field_Data *field_data;
+const Vector3D zero_vector = { 0.0, 0.0, 0.0 };
/* Use 2 textures to allow a gradual fade between images */
#define NUM_TEXTURES 2
-static int current_texture;
-
-/* Ratio of used texture size to total texture size */
-GLfloat tex_width[NUM_TEXTURES], tex_height[NUM_TEXTURES];
-GLuint textures[NUM_TEXTURES];
+#define BUMP_ARRAY_SIZE 1024
typedef enum
{
HOLDING,
+ LOADING,
TRANSITIONING
} Frame_State;
-static Frame_State state = HOLDING;
-static double state_start_time = 0;
+/* structure for holding the mirrorblob data */
+typedef struct {
+ int screen_width, screen_height;
+ GLXContext *glx_context;
+ Window window;
+ XColor fg, bg;
+
+ /* Parameters controlling the position of the blob as a whole */
+ Vector3D blob_center;
+ Vector3D blob_anchor;
+ Vector3D blob_velocity;
+ Vector3D blob_force;
+
+ /* Count of the total number of nodes and faces used to tesselate the blob */
+ int num_nodes;
+ int num_faces;
+
+ Node_Data *nodes;
+ Face_Data *faces;
+
+ Vector3D *dots;
+ Vector3D *normals;
+ Colour *colours;
+ Vector2D *tex_coords;
+
+ /* Pointer to the bump function results */
+ double *bump_shape, *wall_shape;
+
+ Bump_Data *bump_data;
+
+ /* Use 2 textures to allow a gradual fade between images */
+ int current_texture;
+
+ /* Ratio of used texture size to total texture size */
+ GLfloat tex_width[NUM_TEXTURES], tex_height[NUM_TEXTURES];
+ GLuint textures[NUM_TEXTURES];
+
+ Frame_State state;
+ double state_start_time;
+
+ int colour_cycle;
+
+ Bool mipmap_p;
+ Bool waiting_for_image_p;
+ Bool first_image_p;
+
+ trackball_state *trackball;
+ int button_down;
-static int colour_cycle = 0;
+} mirrorblobstruct;
+
+static mirrorblobstruct *Mirrorblob = NULL;
/******************************************************************************
*
glLoadIdentity ();
}
+/******************************************************************************
+ *
+ * Calculate the dot product of two vectors u and v
+ * Dot product u.v = |u||v|cos(theta)
+ * Where theta = angle between u and v
+ */
+static inline double
+dot (const Vector3D u, const Vector3D v)
+{
+ return (u.x * v.x) + (u.y * v.y) + (u.z * v.z);
+}
+
+/******************************************************************************
+ *
+ * Calculate the cross product of two vectors.
+ * Gives a vector perpendicular to u and v with magnitude |u||v|sin(theta)
+ * Where theta = angle between u and v
+ */
+static inline Vector3D
+cross (const Vector3D u, const Vector3D v)
+{
+ Vector3D result;
+
+ result.x = (u.y * v.z - u.z * v.y);
+ result.y = (u.z * v.x - u.x * v.z);
+ result.z = (u.x * v.y - u.y * v.x);
+
+ return result;
+}
+
+/******************************************************************************
+ *
+ * Add vector v to vector u
+ */
+static inline void
+add (Vector3D *u, const Vector3D v)
+{
+ u->x = u->x + v.x;
+ u->y = u->y + v.y;
+ u->z = u->z + v.z;
+}
+
+/******************************************************************************
+ *
+ * Subtract vector v from vector u
+ */
+static inline Vector3D
+subtract (const Vector3D u, const Vector3D v)
+{
+ Vector3D result;
+
+ result.x = u.x - v.x;
+ result.y = u.y - v.y;
+ result.z = u.z - v.z;
+
+ return result;
+}
+
+/******************************************************************************
+ *
+ * multiply vector v by scalar s
+ */
+static inline Vector3D
+scale (const Vector3D v, const double s)
+{
+ Vector3D result;
+
+ result.x = v.x * s;
+ result.y = v.y * s;
+ result.z = v.z * s;
+ return result;
+}
+
+/******************************************************************************
+ *
+ * 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 using the screen_to_texture function.
+ * Load a texture.
*/
-void
-grab_texture(ModeInfo *mi, int texture_index)
+
+static void
+image_loaded_cb (const char *filename, XRectangle *geometry,
+ int image_width, int image_height,
+ int texture_width, int texture_height,
+ void *closure)
{
- Bool mipmap_p = True;
- int iw, ih, tw, th;
+ 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();
- glBindTexture (GL_TEXTURE_2D, textures[texture_index]);
+ mp->tex_width [texture_index] = (GLfloat) image_width / texture_width;
+ mp->tex_height[texture_index] = -(GLfloat) image_height / texture_height;
- if (! screen_to_texture (mi->xgwa.screen, mi->window, 0, 0, mipmap_p,
- NULL, NULL, &iw, &ih, &tw, &th))
- exit(1);
+ 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);
- tex_width [texture_index] = (GLfloat) iw / tw;
- tex_height[texture_index] = -(GLfloat) ih / th;
+ mp->waiting_for_image_p = False;
+ mp->first_image_p = True;
+}
- glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
- (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);
+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);
}
/******************************************************************************
*
- * Initialise the data used to calculate the blob shape.
+ * 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.
*/
static void
initialize_gl(ModeInfo *mi, GLsizei width, GLsizei height)
{
- GLfloat fogColor[4] = { 0.1, 0.1, 0.1, 0.1 };
+ mirrorblobstruct *gp = &Mirrorblob[MI_SCREEN(mi)];
+
/* Lighting values */
+ GLfloat ambientLight[] = { 0.2f, 0.2f, 0.2f, 1.0f };
+
GLfloat lightPos0[] = {500.0f, 100.0f, 200.0f, 1.0f };
- GLfloat whiteLight0[] = { 0.1f, 0.1f, 0.1f, 1.0f };
- GLfloat sourceLight0[] = { 1.0f, 1.0f, 1.0f, 1.0f };
- GLfloat specularLight0[] = { 0.7f, 0.6f, 0.3f, 1.0f };
+ GLfloat whiteLight0[] = { 0.0f, 0.0f, 0.0f, 1.0f };
+ GLfloat sourceLight0[] = { 0.6f, 0.6f, 0.6f, 1.0f };
+ GLfloat specularLight0[] = { 0.8f, 0.8f, 0.9f, 1.0f };
GLfloat lightPos1[] = {0.0f, -500.0f, 500.0f, 1.0f };
- GLfloat whiteLight1[] = { 0.1f, 0.1f, 0.1f, 1.0f };
- GLfloat sourceLight1[] = { 1.0f, 0.3f, 0.3f, 1.0f };
- GLfloat specularLight1[] = { 0.7f, 0.6f, 0.3f, 1.0f };
+ GLfloat whiteLight1[] = { 0.0f, 0.0f, 0.0f, 1.0f };
+ GLfloat sourceLight1[] = { 0.6f, 0.6f, 0.6f, 1.0f };
+ GLfloat specularLight1[] = { 0.7f, 0.7f, 0.7f, 1.0f };
GLfloat specref[] = { 1.0f, 1.0f, 1.0f, 1.0f };
- /* Setup our viewport. */
- glViewport (0, 0, width, height );
+ GLfloat fogColor[4] = { 0.4, 0.4, 0.5, 0.1 };
- glEnable(GL_DEPTH_TEST);
+ /* Set the internal parameters based on the configuration settings */
+ set_parameters();
+
+ /* Set the viewport to the width and heigh of the window */
+ glViewport (0, 0, width, height );
if (do_antialias)
{
- do_blend = 1;
+ blend = 1.0;
glEnable(GL_LINE_SMOOTH);
+ glEnable(GL_POLYGON_SMOOTH);
}
/* The blend function is used for trasitioning between two images even when
* blend is not selected.
*/
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-
- if (do_wire)
- {
- glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
- }
- else
- {
- glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
- }
-
+
if (do_fog)
{
glEnable(GL_FOG);
- glFogi(GL_FOG_MODE, GL_LINEAR);
glFogfv(GL_FOG_COLOR, fogColor);
- glFogf(GL_FOG_DENSITY, 0.35);
- glFogf(GL_FOG_START, 2.0);
- glFogf(GL_FOG_END, 10.0);
+ glFogf(GL_FOG_DENSITY, 0.50);
+ glFogf(GL_FOG_START, 15.0);
+ glFogf(GL_FOG_END, 30.0);
}
- /* Our shading model--Gouraud (smooth). */
+ /* Set the shading model to smooth (Gouraud shading). */
glShadeModel (GL_SMOOTH);
- /* Culling. */
- glCullFace (GL_BACK);
- glEnable (GL_CULL_FACE);
- glEnable (GL_DEPTH_TEST);
- glFrontFace (GL_CCW);
-
/* Set the clear color. */
glClearColor( 0, 0, 0, 0 );
- glViewport( 0, 0, width, height );
-
+ glLightModelfv (GL_LIGHT_MODEL_AMBIENT, ambientLight);
glLightfv (GL_LIGHT0, GL_AMBIENT, whiteLight0);
glLightfv (GL_LIGHT0, GL_DIFFUSE, sourceLight0);
glLightfv (GL_LIGHT0, GL_SPECULAR, specularLight0);
/* Set all materials to have specular reflectivity */
glMaterialfv (GL_FRONT, GL_SPECULAR, specref);
- glMateriali (GL_FRONT, GL_SHININESS, 64);
+ glMateriali (GL_FRONT, GL_SHININESS, 32);
+ /* Let GL implementation scale normal vectors. */
glEnable (GL_NORMALIZE);
/* Enable Arrays */
- if (do_texture)
+ if (load_textures)
{
glLightModeli (GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR);
-
glEnable (GL_TEXTURE_2D);
- current_texture = 0;
- glGenTextures (NUM_TEXTURES, textures);
- grab_texture (mi, current_texture);
- glEnableClientState (GL_TEXTURE_COORD_ARRAY);
+ gp->current_texture = 0;
+ glGenTextures (NUM_TEXTURES, gp->textures);
+ grab_texture (mi, gp->current_texture);
+
+ if (do_texture)
+ {
+ glEnableClientState (GL_TEXTURE_COORD_ARRAY);
+ }
+ glMatrixMode (GL_TEXTURE);
+ glRotated (180.0, 1.0, 0.0, 0.0);
+ glMatrixMode (GL_MODELVIEW);
}
if (do_colour)
/******************************************************************************
*
- * Calculate the normal vector for a plane given three points in the plane.
+ * Initialise the openGL state data.
*/
static void
-calculate_normal (Vector3D point1,
- Vector3D point2,
- Vector3D point3,
- Vector3D *normal)
+set_blob_gl_state(GLdouble alpha)
{
- Vector3D vector1, vector2;
- double magnitude;
-
- vector1.x = point2.x - point1.x;
- vector1.y = point2.y - point1.y;
- vector1.z = point2.z - point1.z;
-
- vector2.x = point3.x - point2.x;
- vector2.y = point3.y - point2.y;
- vector2.z = point3.z - point2.z;
-
- (*normal).x = vector1.y * vector2.z - vector1.z * vector2.y;
- (*normal).y = vector1.z * vector2.x - vector1.x * vector2.z;
- (*normal).z = vector1.x * vector2.y - vector1.y * vector2.x;
+ if (do_antialias)
+ {
+ glEnable(GL_LINE_SMOOTH);
+ glEnable(GL_POLYGON_SMOOTH);
+ }
- /* Adjust the normal to unit magnitude */
- magnitude = sqrt ((*normal).x * (*normal).x
- + (*normal).y * (*normal).y
- + (*normal).z * (*normal).z);
+ if (wireframe)
+ {
+ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+ }
+ else
+ {
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+ }
- /* Watch out for divide by zero/underflow */
- if (magnitude > 1e-300)
+ /* The blend function is used for trasitioning between two images even when
+ * blend is not selected.
+ */
+ glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ /* Culling. */
+ if (culling)
+ {
+ glCullFace (GL_BACK);
+ glEnable (GL_CULL_FACE);
+ glFrontFace (GL_CCW);
+ }
+ else
{
- (*normal).x /= magnitude;
- (*normal).y /= magnitude;
- (*normal).z /= magnitude;
+ glDisable (GL_CULL_FACE);
}
+
+ if (blend < 1.0)
+ {
+ glEnable (GL_BLEND);
+ glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ /* Set the default blob colour to off-white. */
+ glColor4d (0.9, 0.9, 1.0, alpha);
+ }
+ else
+ {
+ glDisable(GL_BLEND);
+ glColor4d (0.9, 0.9, 1.0, 1.0);
+ }
+
+ glEnable(GL_DEPTH_TEST);
+ glEnable(GL_LIGHTING);
}
/******************************************************************************
* Return 0 on success.
*/
static int
-initialise_blob(int width,
+initialise_blob(mirrorblobstruct *gp,
+ int width,
int height,
- int field_array_size)
+ int bump_array_size)
{
- /* Loop variables */
- int x, y, i;
- double xd;
+ /* Loop variables */
+ int i, u, v, node, side, face, base, base2 = 0;
+ int nodes_on_edge = resolution;
+ Vector3D node1, node2, result;
+
+ if (nodes_on_edge < 2)
+ return -1;
- colour_cycle = 0;
+ gp->num_nodes = 2 * nodes_on_edge * nodes_on_edge - 4 * nodes_on_edge + 4;
+ gp->num_faces = 4 * (nodes_on_edge - 1) * (nodes_on_edge - 1);
+
+ gp->nodes = (Node_Data *) malloc (gp->num_nodes * sizeof (Node_Data));
+ if (!gp->nodes)
+ {
+ fprintf (stderr, "Couldn't allocate gp->nodes buffer\n");
+ return -1;
+ }
- row_data = (Row_Data *) malloc (y_resolution * sizeof (Row_Data));
- if (!row_data)
+ gp->faces = (Face_Data *) malloc (gp->num_faces * sizeof (Face_Data));
+ if (!gp->faces)
{
- fprintf(stderr, "Couldn't allocate row data buffer\n");
+ fprintf (stderr, "Couldn't allocate faces data buffer\n");
return -1;
}
- field_data = (Field_Data *) malloc (field_points * sizeof (Field_Data));
- if (!field_data)
+ gp->bump_data = (Bump_Data *) malloc (bumps * sizeof (Bump_Data));
+ if (!gp->bump_data)
{
- fprintf(stderr, "Couldn't allocate field data buffer\n");
+ fprintf(stderr, "Couldn't allocate bump data buffer\n");
return -1;
}
- field = (double *)malloc(field_array_size * sizeof(double));
- if (!field)
+ gp->bump_shape = (double *)malloc(bump_array_size * sizeof(double));
+ if (!gp->bump_shape)
{
- fprintf(stderr, "Couldn't allocate field buffer\n");
+ fprintf(stderr, "Couldn't allocate bump buffer\n");
return -1;
}
- wall_field = (double *)malloc(field_array_size * sizeof(double));
- if (!wall_field)
+ gp->wall_shape = (double *)malloc(bump_array_size * sizeof(double));
+ if (!gp->wall_shape)
{
- fprintf(stderr, "Couldn't allocate wall field buffer\n");
+ fprintf(stderr, "Couldn't allocate wall bump buffer\n");
return -1;
}
- dots = (Vector3D *)malloc(x_resolution * y_resolution * sizeof(Vector3D));
- if (!dots)
+ gp->dots = (Vector3D *)malloc(gp->num_nodes * sizeof(Vector3D));
+ if (!gp->dots)
{
- fprintf(stderr, "Couldn't allocate points buffer\n");
+ fprintf(stderr, "Couldn't allocate nodes buffer\n");
return -1;
}
- glVertexPointer (3, GL_DOUBLE, 0, (GLvoid *) dots);
+ glVertexPointer (3, GL_DOUBLE, 0, (GLvoid *) gp->dots);
- normals = (Vector3D *)malloc(x_resolution * y_resolution * sizeof(Vector3D));
- if (!normals)
+ gp->normals = (Vector3D *)malloc(gp->num_nodes * sizeof(Vector3D));
+ if (!gp->normals)
{
fprintf(stderr, "Couldn't allocate normals buffer\n");
return -1;
}
- glNormalPointer (GL_DOUBLE, 0, (GLvoid *) normals);
+ glNormalPointer (GL_DOUBLE, 0, (GLvoid *) gp->normals);
if (do_colour)
{
- colours = (Colour *)malloc(x_resolution * y_resolution * sizeof(Colour));
- if (!colours)
+ gp->colours = (Colour *)malloc(gp->num_nodes * sizeof(Colour));
+ if (!gp->colours)
{
fprintf(stderr, "Couldn't allocate colours buffer\n");
return -1;
}
- glColorPointer (4, GL_UNSIGNED_BYTE, 0, (GLvoid *) colours);
+ glColorPointer (4, GL_UNSIGNED_BYTE, 0, (GLvoid *) gp->colours);
}
if (do_texture)
{
- tex_coords = (Vector2D *)malloc(x_resolution * y_resolution
- * sizeof(Vector2D));
- if (!tex_coords)
+ gp->tex_coords = (Vector2D *)malloc(gp->num_nodes * sizeof(Vector2D));
+ if (!gp->tex_coords)
{
- fprintf(stderr, "Couldn't allocate tex_coords buffer\n");
+ fprintf(stderr, "Couldn't allocate gp->tex_coords buffer\n");
return -1;
}
- glTexCoordPointer (2, GL_DOUBLE, 0, (GLvoid *) tex_coords);
- }
-
- num_points = 0;
- /* Generate constant row data and count of total number of points */
- for (y = 0; y < y_resolution; y++)
- {
- row_data[y].cosyd = cos(PI * (double)(y * (y_resolution + 1))
- / (double)(y_resolution * y_resolution));
- row_data[y].sinyd = sin(PI * (double)(y * (y_resolution + 1))
- / (double)(y_resolution * y_resolution));
- row_data[y].num_x_points = (int)(x_resolution * row_data[y].sinyd + 1.0);
- num_points += row_data[y].num_x_points;
+ glTexCoordPointer (2, GL_DOUBLE, 0, (GLvoid *) gp->tex_coords);
}
- /* Initialise field data */
- for (i = 0; i < field_points; i++)
+ /* Initialise bump data */
+ for (i = 0; i < bumps; i++)
{
- field_data[i].ax = 2.0 * (((double)random() / (double)RAND_MAX) - 0.5);
- field_data[i].ay = 2.0 * (((double)random() / (double)RAND_MAX) - 0.5);
- field_data[i].apower = (((double)random() / (double)RAND_MAX) - 0.5);
-
- field_data[i].pos.x = 1.5 * sin(PI * field_data[i].ay)
- * cos(PI * field_data[i].ax);
- field_data[i].pos.y = 1.5 * cos(PI * field_data[i].ay);
- field_data[i].pos.z = 1.5 * sin(PI * field_data[i].ay)
- * sin(PI * field_data[i].ax);
-
- field_data[i].cx = 2.0 * (((double)random() / (double)RAND_MAX) - 0.5);
- field_data[i].cy = 2.0 * (((double)random() / (double)RAND_MAX) - 0.5);
- field_data[i].cpower = (((double)random() / (double)RAND_MAX) - 0.5);
-
- field_data[i].vx = 0.0;
- field_data[i].vy = 0.0;
- field_data[i].vpower = 0.0;
-
- field_data[i].mx = 0.003 * ((double)random() / (double)RAND_MAX);
- field_data[i].my = 0.003 * ((double)random() / (double)RAND_MAX);
- field_data[i].mpower = 0.003 * ((double)random() / (double)RAND_MAX);
+ gp->bump_data[i].ax = 2.0 * (((double)random() / (double)RAND_MAX) - 0.5);
+ gp->bump_data[i].ay = 2.0 * (((double)random() / (double)RAND_MAX) - 0.5);
+ gp->bump_data[i].power = (5.0 / pow(bumps, 0.75)) * (((double)random() / (double)RAND_MAX) - 0.5);
+ gp->bump_data[i].size = 0.1 + 0.5 * (((double)random() / (double)RAND_MAX));
+
+ gp->bump_data[i].pos.x = 1.5 * sin(PI * gp->bump_data[i].ay)
+ * cos(PI * gp->bump_data[i].ax);
+ gp->bump_data[i].pos.y = 1.5 * cos(PI * gp->bump_data[i].ay);
+ gp->bump_data[i].pos.z = 1.5 * sin(PI * gp->bump_data[i].ay)
+ * sin(PI * gp->bump_data[i].ax);
+
+ gp->bump_data[i].cx = 2.0 * (((double)random() / (double)RAND_MAX) - 0.5);
+ gp->bump_data[i].cy = 2.0 * (((double)random() / (double)RAND_MAX) - 0.5);
+ gp->bump_data[i].cpower = (5.0 / pow(bumps, 0.75)) * (((double)random() / (double)RAND_MAX) - 0.5);
+ gp->bump_data[i].csize = 0.35; /*0.1 + 0.25 * (((double)random() / (double)RAND_MAX));*/
+
+ gp->bump_data[i].vx = 0.0;
+ gp->bump_data[i].vy = 0.0;
+ gp->bump_data[i].vpower = 0.0;
+ gp->bump_data[i].vsize = 0.0;
+
+ gp->bump_data[i].mx = 0.003 * ((double)random() / (double)RAND_MAX);
+ gp->bump_data[i].my = 0.003 * ((double)random() / (double)RAND_MAX);
+ gp->bump_data[i].mpower = 0.003 * ((double)random() / (double)RAND_MAX);
+ gp->bump_data[i].msize = 0.003 * ((double)random() / (double)RAND_MAX);
}
- /* Initialise lookup table of field strength */
- for (i = 0; i < field_array_size; i++)
+ /* Initialise lookup table of bump strength */
+ for (i = 0; i < bump_array_size; i++)
{
- xd = 2.0 * (((double)i / (double)field_array_size));
+ double xd, xd2;
+ xd = i / (double)bump_array_size;
- xd = 3.0 * xd * xd * xd * xd;
- field[i] = 0.4 / (field_points * (xd + 0.1));
+ xd2 = 48.0 * xd * xd;
+ gp->bump_shape[i] = 0.1 / (xd2 + 0.1);
- xd = 10.0 * (((double)i / (double)field_array_size));
- wall_field[i] = 0.4 / (xd * xd * xd * xd + 1.0);
+ xd2 = 40.0 * xd * xd * xd * xd;
+ gp->wall_shape[i] = 0.4 / (xd2 + 0.1);
}
- for (y = 0; y < y_resolution; y++)
+ node = 0;
+ face = 0;
+ for (side = 0; side < 4; side++)
{
- for (x = 0; x < row_data[y].num_x_points; x++)
+ base = node;
+ if (side == 2)
{
- i = x + y * x_resolution;
- xd = 2.0 * (((double)x / (double)row_data[y].num_x_points) - 0.5);
-
- dots[i].x = row_data[y].sinyd * cos(PI * xd);
- dots[i].y = row_data[y].cosyd;
- dots[i].z = row_data[y].sinyd * sin(PI * xd);
- normals[i].x = row_data[y].sinyd * cos(PI * xd);
- normals[i].y = row_data[y].cosyd;
- normals[i].z = row_data[y].sinyd * sin(PI * xd);
- if (do_texture)
+ base2 = node;
+ }
+ /*
+ * The start and end of the for loops below are modified based on the
+ * side of the tetrahedron that is being calculated to avoid duplication
+ * of the gp->nodes that are on the edges of the tetrahedron.
+ */
+ for (u = (side > 1); u < (nodes_on_edge - (side > 0)); u++)
+ {
+ node1 = partial (normalise (tetrahedron[side][0]),
+ normalise (tetrahedron[side][1]),
+ u / (double) (nodes_on_edge - 1));
+ node2 = partial (normalise (tetrahedron[side][0]),
+ normalise (tetrahedron[side][2]),
+ u / (double) (nodes_on_edge - 1));
+
+ for (v = (side > 1); v <= (u - (side > 2)); v++)
{
- tex_coords[i].x = 2.0 - 2.0 * x / (float) row_data[y].num_x_points;
- tex_coords[i].y = 1.0 - y / (float) y_resolution;
+ if (u > 0)
+ result = partial (node1, node2, v / (double) u);
+ else
+ result = node1;
+
+ gp->nodes[node].position = normalise (result);
+ gp->nodes[node].initial_position = gp->nodes[node].position;
+ gp->nodes[node].normal = zero_vector;
+ node++;
+ }
+ }
+
+ /*
+ * Determine which nodes make up each face. The complexity is caused
+ * by having to determine the correct nodes for the edges of the
+ * tetrahedron since the common nodes on the edges are only calculated
+ * once (see above).
+ */
+ for (u = 0; u < (nodes_on_edge - 1); u++)
+ {
+ for (v = 0; v <= u; v++)
+ {
+ {
+ if (side < 2)
+ {
+ gp->faces[face].node1 = base + ((u * (u + 1)) / 2) + v;
+ gp->faces[face].node2 =
+ base + ((u + 1) * (u + 2)) / 2 + v + 1;
+ gp->faces[face].node3 =
+ base + ((u + 1) * (u + 2)) / 2 + v;
+
+ if ((side == 1) && (u == (nodes_on_edge - 2)))
+ {
+ gp->faces[face].node3 =
+ ((u + 1) * (u + 2)) / 2 +
+ nodes_on_edge - v - 1;
+ gp->faces[face].node2 =
+ ((u + 1) * (u + 2)) / 2 +
+ nodes_on_edge - v - 2;
+ }
+ }
+ else if (side < 3)
+ {
+ gp->faces[face].node1 =
+ base + (((u - 1) * u) / 2) + v - 1;
+ gp->faces[face].node2 = base + ((u) * (u + 1)) / 2 + v;
+ gp->faces[face].node3 =
+ base + ((u) * (u + 1)) / 2 + v - 1;
+
+ if (u == (nodes_on_edge - 2))
+ {
+ int n = nodes_on_edge - v - 1;
+ gp->faces[face].node2 =
+ ((nodes_on_edge *
+ (nodes_on_edge + 1)) / 2) +
+ ((n - 1) * (n + 0)) / 2;
+ gp->faces[face].node3 =
+ ((nodes_on_edge *
+ (nodes_on_edge + 1)) / 2) +
+ ((n + 0) * (n + 1)) / 2;
+ }
+ if (v == 0)
+ {
+ gp->faces[face].node1 = (((u + 1) * (u + 2)) / 2) - 1;
+ gp->faces[face].node3 = (((u + 2) * (u + 3)) / 2) - 1;
+ }
+ }
+ else
+ {
+ gp->faces[face].node1 =
+ base + (((u - 2) * (u - 1)) / 2) + v - 1;
+ gp->faces[face].node2 = base + ((u - 1) * u) / 2 + v;
+ gp->faces[face].node3 = base + ((u - 1) * u) / 2 + v - 1;
+
+ if (v == 0)
+ {
+ gp->faces[face].node1 =
+ base2 + ((u * (u + 1)) / 2) - 1;
+ gp->faces[face].node3 =
+ base2 + ((u + 1) * (u + 2)) / 2 - 1;
+ }
+ if (u == (nodes_on_edge - 2))
+ {
+ gp->faces[face].node3 =
+ ((nodes_on_edge *
+ (nodes_on_edge + 1)) / 2) +
+ ((v + 1) * (v + 2)) / 2 - 1;
+ gp->faces[face].node2 =
+ ((nodes_on_edge *
+ (nodes_on_edge + 1)) / 2) +
+ ((v + 2) * (v + 3)) / 2 - 1;
+ }
+ if (v == u)
+ {
+ gp->faces[face].node1 = (u * (u + 1)) / 2;
+ gp->faces[face].node2 = ((u + 1) * (u + 2)) / 2;
+ }
+ }
+ face++;
+ }
+
+ if (v < u)
+ {
+ if (side < 2)
+ {
+ gp->faces[face].node1 = base + ((u * (u + 1)) / 2) + v;
+ gp->faces[face].node2 =
+ base + ((u * (u + 1)) / 2) + v + 1;
+ gp->faces[face].node3 =
+ base + (((u + 1) * (u + 2)) / 2) + v + 1;
+
+ if ((side == 1) && (u == (nodes_on_edge - 2)))
+ {
+ gp->faces[face].node3 =
+ ((u + 1) * (u + 2)) / 2 +
+ nodes_on_edge - v - 2;
+ }
+ }
+ else if (side < 3)
+ {
+ gp->faces[face].node1 =
+ base + ((u * (u - 1)) / 2) + v - 1;
+ gp->faces[face].node2 = base + ((u * (u - 1)) / 2) + v;
+ gp->faces[face].node3 = base + ((u * (u + 1)) / 2) + v;
+
+ if (u == (nodes_on_edge - 2))
+ {
+ int n = nodes_on_edge - v - 1;
+ gp->faces[face].node3 =
+ ((nodes_on_edge *
+ (nodes_on_edge + 1)) / 2) +
+ ((n + 0) * (n - 1)) / 2;
+ }
+ if (v == 0)
+ {
+ gp->faces[face].node1 = (((u + 1) * (u + 2)) / 2) - 1;
+ }
+ }
+ else
+ {
+ gp->faces[face].node1 =
+ base + (((u - 2) * (u - 1)) / 2) + v - 1;
+ gp->faces[face].node2 =
+ base + (((u - 2) * (u - 1)) / 2) + v;
+ gp->faces[face].node3 = base + (((u - 1) * u) / 2) + v;
+
+ if (v == 0)
+ {
+ gp->faces[face].node1 = base2 + (u * (u + 1)) / 2 - 1;
+ }
+ if (u == (nodes_on_edge - 2))
+ {
+ gp->faces[face].node3 =
+ ((nodes_on_edge * (nodes_on_edge + 1)) / 2) +
+ ((v + 2) * (v + 3)) / 2 - 1;
+ }
+ if (v == (u - 1))
+ {
+ gp->faces[face].node2 = (u * (u + 1)) / 2;
+ }
+ }
+ face++;
+ }
}
}
}
+
return 0;
}
+/******************************************************************************
+ *
+ * Return the magnitude of the given vector
+ */
+static inline double
+length (Vector3D u)
+{
+ return sqrt (u.x * u.x + u.y * u.y + u.z * u.z);
+}
/******************************************************************************
*
* Calculate the blob shape.
*/
static void
-calc_blob(int width,
+calc_blob(mirrorblobstruct *gp,
+ int width,
int height,
- int field_array_size,
+ int bump_array_size,
float limit,
double fade)
{
- static double freak = 0.0;
-
- static double v_freak = 0.0007;
-
/* Loop variables */
- int x, y, i, index, index1, index2, index3;
- /* position of a point */
- double xd, yd, zd, offset_x, offset_y, offset_z;
- double strength, radius;
- double xdist, ydist, zdist;
+ int i, index, face;
+ /* position of a node */
+ Vector3D node;
+ Vector3D offset;
+ Vector3D bump_vector;
int dist;
- /* Color components */
-
- colour_cycle++;
-
- /* Update position and strength of points used to distort the blob */
- for (i = 0; i < field_points; i++)
+ /* Update position and strength of bumps used to distort the blob */
+ for (i = 0; i < bumps; i++)
{
- field_data[i].vx += field_data[i].mx*(field_data[i].cx - field_data[i].ax);
- field_data[i].vy += field_data[i].my*(field_data[i].cy - field_data[i].ay);
- field_data[i].vpower += field_data[i].mpower
- * (field_data[i].cpower - field_data[i].apower);
-
- field_data[i].ax += 0.1 * field_data[i].vx;
- field_data[i].ay += 0.1 * field_data[i].vy;
- field_data[i].apower += 0.1 * field_data[i].vpower;
-
- field_data[i].pos.x = 1.0 * sin(PI * field_data[i].ay)
- * cos(PI * field_data[i].ax);
- field_data[i].pos.y = 1.0 * cos(PI * field_data[i].ay);
- field_data[i].pos.z = 1.0 * sin(PI * field_data[i].ay)
- * sin(PI * field_data[i].ax);
+ gp->bump_data[i].vx += gp->bump_data[i].mx*(gp->bump_data[i].cx - gp->bump_data[i].ax);
+ gp->bump_data[i].vy += gp->bump_data[i].my*(gp->bump_data[i].cy - gp->bump_data[i].ay);
+ gp->bump_data[i].vpower += gp->bump_data[i].mpower
+ * (gp->bump_data[i].cpower - gp->bump_data[i].power);
+ gp->bump_data[i].vsize += gp->bump_data[i].msize
+ * (gp->bump_data[i].csize - gp->bump_data[i].size);
+
+ gp->bump_data[i].ax += 0.1 * gp->bump_data[i].vx;
+ gp->bump_data[i].ay += 0.1 * gp->bump_data[i].vy;
+ gp->bump_data[i].power += 0.1 * gp->bump_data[i].vpower;
+ gp->bump_data[i].size += 0.1 * gp->bump_data[i].vsize;
+
+ gp->bump_data[i].pos.x = 1.0 * sin(PI * gp->bump_data[i].ay)
+ * cos(PI * gp->bump_data[i].ax);
+ gp->bump_data[i].pos.y = 1.0 * cos(PI * gp->bump_data[i].ay);
+ gp->bump_data[i].pos.z = 1.0 * sin(PI * gp->bump_data[i].ay)
+ * sin(PI * gp->bump_data[i].ax);
}
- blob_force.x = 0.0;
- blob_force.y = 0.0;
- blob_force.z = 0.0;
- for (y = 0; y < y_resolution; y++)
+ /* Update calculate new position for each vertex based on an offset from
+ * the initial position
+ */
+ gp->blob_force = zero_vector;
+ for (index = 0; index < gp->num_nodes; ++index)
{
- for (x = 0; x < row_data[y].num_x_points; x++)
- {
- index = x + y * x_resolution;
- xd = 2.0 * PI * (((double)x / (double)row_data[y].num_x_points) - 0.5);
-
- radius = 1.0 + 0.0 * sin (xd * 10);
+ node = gp->nodes[index].initial_position;
+ gp->nodes[index].normal = zero_vector;
- zd = radius * row_data[y].sinyd * sin(xd);
- xd = radius * row_data[y].sinyd * cos(xd);
- yd = radius * row_data[y].cosyd;
+ offset = zero_vector;
+ for ( i = 0; i < bumps; i++)
+ {
+ bump_vector = subtract(gp->bump_data[i].pos, node);
- normals[index].x = xd;
- normals[index].y = yd;
- normals[index].z = zd;
+ dist = bump_array_size * dot(bump_vector, bump_vector) * gp->bump_data[i].size;
- offset_x = 0.0;
- offset_y = 0.0;
- offset_z = 0.0;
- strength = 0.0;
- for ( i = 0; i < field_points; i++)
+ if (dist < bump_array_size)
{
- xdist = field_data[i].pos.x - xd;
- ydist = field_data[i].pos.y - yd;
- zdist = field_data[i].pos.z - zd;
- dist = field_array_size * (xdist * xdist + ydist * ydist
- + zdist * zdist) * 0.1;
-
- strength += PI * field_data[i].apower;
-
- if (dist < field_array_size)
- {
- offset_x += xd * field_data[i].apower * field[dist];
- offset_y += yd * field_data[i].apower * field[dist];
- offset_z += zd * field_data[i].apower * field[dist];
-
- blob_force.x += 1.0 * xd * field_data[i].apower * field[dist];
- blob_force.y += 1.0 * yd * field_data[i].apower * field[dist];
- blob_force.z += 1.0 * zd * field_data[i].apower * field[dist];
+ add(&offset, scale(node, gp->bump_data[i].power * gp->bump_shape[dist]));
+ add(&gp->blob_force, scale(node, gp->bump_data[i].power * gp->bump_shape[dist]));
+ }
+ }
- strength *= 2.0 * field[dist];
- }
+ add(&node, offset);
+ node = scale(node, zoom);
+ add(&node, gp->blob_center);
- if (incremental)
- {
- xd += offset_x * freak * freak;
- yd += offset_y * freak * freak;
- zd += offset_z * freak * freak;
- }
- if (incremental == 1)
- {
- offset_x = 0.0;
- offset_y = 0.0;
- offset_z = 0.0;
- }
- }
+ if (do_walls)
+ {
+ if (node.z < -limit) node.z = -limit;
+ if (node.z > limit) node.z = limit;
- if (incremental < 3)
+ dist = bump_array_size * (node.z + limit) * (node.z + limit) * 0.5;
+ if (dist < bump_array_size)
{
- xd += offset_x;
- yd += offset_y;
- zd += offset_z;
+ node.x += (node.x - gp->blob_center.x) * gp->wall_shape[dist];
+ node.y += (node.y - gp->blob_center.y) * gp->wall_shape[dist];
+ gp->blob_force.z += (node.z + limit);
}
- xd += blob_center.x;
- yd += blob_center.y;
- zd += blob_center.z;
-
- if (do_colour)
+ else
{
- colours[index].red = 128 + (int)(sin(strength + colour_cycle * 0.01 + 2.0 * PI * x / row_data[y].num_x_points) * 127.0);
- colours[index].green = 128 + (int)(cos(strength + colour_cycle * 0.025) * 127.0);
- colours[index].blue = 128 + (int)(sin(strength + colour_cycle * 0.03 + 2.0 * PI * y / y_resolution) * 127.0);
- colours[index].alpha = (int)(255.0 * fade);
- }
+ dist = bump_array_size * (node.z - limit) * (node.z - limit) * 0.5;
+ if (dist < bump_array_size)
+ {
+ node.x += (node.x - gp->blob_center.x) * gp->wall_shape[dist];
+ node.y += (node.y - gp->blob_center.y) * gp->wall_shape[dist];
+ gp->blob_force.z -= (node.z - limit);
+ }
- /* Add walls */
- if (do_walls)
- {
- if (zd < -limit) zd = -limit;
- if (zd > limit) zd = limit;
+ if (node.y < -limit) node.y = -limit;
+ if (node.y > limit) node.y = limit;
- dist = field_array_size * (zd + limit) * (zd + limit) * 0.5;
- if (dist < field_array_size)
+ dist = bump_array_size * (node.y + limit) * (node.y + limit) * 0.5;
+ if (dist < bump_array_size)
{
- xd += (xd - blob_center.x) * wall_field[dist];
- yd += (yd - blob_center.y) * wall_field[dist];
- blob_force.z += (zd + limit);
+ node.x += (node.x - gp->blob_center.x) * gp->wall_shape[dist];
+ node.z += (node.z - gp->blob_center.z) * gp->wall_shape[dist];
+ gp->blob_force.y += (node.y + limit);
}
else
{
- dist = field_array_size * (zd - limit) * (zd - limit) * 0.5;
- if (dist < field_array_size)
+ dist = bump_array_size * (node.y - limit) * (node.y - limit) * 0.5;
+ if (dist < bump_array_size)
{
- xd += (xd - blob_center.x) * wall_field[dist];
- yd += (yd - blob_center.y) * wall_field[dist];
- blob_force.z -= (zd - limit);
- }
-
- if (yd < -limit) yd = -limit;
- if (yd > limit) yd = limit;
-
- dist = field_array_size * (yd + limit) * (yd + limit) * 0.5;
- if (dist < field_array_size)
- {
- xd += (xd - blob_center.x) * wall_field[dist];
- zd += (zd - blob_center.z) * wall_field[dist];
- blob_force.y += (yd + limit);
- }
- else
- {
- dist = field_array_size * (yd - limit) * (yd - limit) * 0.5;
- if (dist < field_array_size)
- {
- xd += (xd - blob_center.x) * wall_field[dist];
- zd += (zd - blob_center.z) * wall_field[dist];
- blob_force.y -= (yd - limit);
- }
+ node.x += (node.x - gp->blob_center.x) * gp->wall_shape[dist];
+ node.z += (node.z - gp->blob_center.z) * gp->wall_shape[dist];
+ gp->blob_force.y -= (node.y - limit);
}
+ }
- if (xd < -limit) xd = -limit;
- if (xd > limit) xd = limit;
+ if (node.x < -limit) node.x = -limit;
+ if (node.x > limit) node.x = limit;
- dist = field_array_size * (xd + limit) * (xd + limit) * 0.5;
- if (dist < field_array_size)
- {
- yd += (yd - blob_center.y) * wall_field[dist];
- zd += (zd - blob_center.z) * wall_field[dist];
- blob_force.x += (xd + limit);
- }
- else
+ dist = bump_array_size * (node.x + limit) * (node.x + limit) * 0.5;
+ if (dist < bump_array_size)
+ {
+ node.y += (node.y - gp->blob_center.y) * gp->wall_shape[dist];
+ node.z += (node.z - gp->blob_center.z) * gp->wall_shape[dist];
+ gp->blob_force.x += (node.x + limit);
+ }
+ else
+ {
+ dist = bump_array_size * (node.x - limit) * (node.x - limit) * 0.5;
+ if (dist < bump_array_size)
{
- dist = field_array_size * (xd - limit) * (xd - limit) * 0.5;
- if (dist < field_array_size)
- {
- yd += (yd - blob_center.y) * wall_field[dist];
- zd += (zd - blob_center.z) * wall_field[dist];
- blob_force.x -= (xd - limit);
- }
+ node.y += (node.y - gp->blob_center.y) * gp->wall_shape[dist];
+ node.z += (node.z - gp->blob_center.z) * gp->wall_shape[dist];
+ gp->blob_force.x -= (node.x - limit);
}
-
- if (yd < -limit) yd = -limit;
- if (yd > limit) yd = limit;
}
- }
- dots[index].x = xd;
- dots[index].y = yd;
- dots[index].z = zd;
+ if (node.y < -limit) node.y = -limit;
+ if (node.y > limit) node.y = limit;
+ }
}
+ gp->dots[index] = node;
}
- /* Calculate the normals for each vertex and the texture mapping if required.
- * Although the code actually calculates the normal for one of the triangles
- * attached to a vertex rather than the vertex itself the results are not too
- * bad for with a reasonable number of verticies.
- */
-
- /* The first point is treated as a special case since the loop expects to use
- * points in the previous row to form the triangle.
- */
- index1 = 0;
- index2 = y * x_resolution;
- index3 = 1 + y * x_resolution;
- calculate_normal (dots[index1], dots[index2], dots[index3], &normals[index1]);
- if (do_texture)
+ /* Determine the normal for each face */
+ for (face = 0; face < gp->num_faces; face++)
{
- if (offset_texture)
- {
- tex_coords[index1].x = dots[index1].x * 0.125 + 0.5
- * (1.0 + 0.25 * asin(normals[index1].x) / (0.5 * PI));
- tex_coords[index1].y = dots[index1].y * 0.125 + 0.5
- * (1.0 + 0.25 * asin(normals[index1].y) / (0.5 * PI));
- }
- else
- {
- tex_coords[index1].x = 0.5 * (1.0 + (asin(normals[index1].x)
- / (0.5 * PI)));
- tex_coords[index1].y = 0.5 * (1.0 + (asin(normals[index1].y)
- / (0.5 * PI)));
- }
- tex_coords[index1].x *= tex_width[current_texture];
- tex_coords[index1].y *= tex_height[current_texture];
+ /* Use nodeers to indexed nodes to help readability */
+ Node_Data *node1 = &gp->nodes[gp->faces[face].node1];
+ Node_Data *node2 = &gp->nodes[gp->faces[face].node2];
+ Node_Data *node3 = &gp->nodes[gp->faces[face].node3];
+
+ gp->faces[face].normal = cross(subtract(node2->position, node1->position),
+ subtract(node3->position, node1->position));
+
+ /* Add the normal for the face onto the normal for the verticies of
+ the face */
+ add(&node1->normal, gp->faces[face].normal);
+ add(&node2->normal, gp->faces[face].normal);
+ add(&node3->normal, gp->faces[face].normal);
}
- for (y = 1; y < y_resolution - 1; y++)
+ /* Use the normal to set the colour and texture */
+ if (do_colour || do_texture)
{
- if (row_data[y - 1].num_x_points)
+ for (index = 0; index < gp->num_nodes; ++index)
{
- for (x = 0; x < row_data[y].num_x_points; x++)
+ gp->normals[index] = normalise(gp->nodes[index].normal);
+
+ if (do_colour)
+ {
+ gp->colours[index].red = (int)(255.0 * fabs(gp->normals[index].x));
+ gp->colours[index].green = (int)(255.0 * fabs(gp->normals[index].y));
+ gp->colours[index].blue = (int)(255.0 * fabs(gp->normals[index].z));
+ gp->colours[index].alpha = (int)(255.0 * fade);
+ }
+ if (do_texture)
{
- if (x == row_data[y].num_x_points - 1)
+ if (offset_texture)
{
- index1 = y * x_resolution;
+ gp->tex_coords[index].x = gp->dots[index].x * 0.125 + 0.5
+ * (1.0 + 0.25 * asin(gp->normals[index].x) / (0.5 * PI));
+ gp->tex_coords[index].y = -gp->dots[index].y * 0.125 - 0.5
+ * (1.0 + 0.25 * asin(gp->normals[index].y) / (0.5 * PI));
}
else
{
- index1 = x + 1 + y * x_resolution;
- }
- index2 = x + y * x_resolution;
- index3 = ((x + 0.5) * row_data[y - 1].num_x_points
- / row_data[y].num_x_points) + (y - 1) * x_resolution;
- calculate_normal (dots[index1], dots[index2], dots[index3],
- &normals[index1]);
- if (do_texture)
- {
- if (offset_texture)
- {
- tex_coords[index1].x = dots[index1].x * 0.125 + 0.5
- * (1.0 + 0.25 * asin(normals[index1].x) / (0.5 * PI));
- tex_coords[index1].y = dots[index1].y * 0.125 + 0.5
- * (1.0 + 0.25 * asin(normals[index1].y) / (0.5 * PI));
- }
- else
- {
- tex_coords[index1].x = 0.5 * (1.0 + (asin(normals[index1].x)
- / (0.5 * PI)));
- tex_coords[index1].y = 0.5 * (1.0 + (asin(normals[index1].y)
- / (0.5 * PI)));
- }
- tex_coords[index1].x *= tex_width[current_texture];
- tex_coords[index1].y *= tex_height[current_texture];
+ gp->tex_coords[index].x = 0.5
+ * (1.0 + asin(gp->normals[index].x) / (0.5 * PI));
+ gp->tex_coords[index].y = -0.5
+ * (1.0 + asin(gp->normals[index].y) / (0.5 * PI));
}
+ /* Adjust the texture co-ordinates to from range 0..1 to
+ * 0..width or 0..height as appropriate
+ */
+ gp->tex_coords[index].x *= gp->tex_width[gp->current_texture];
+ gp->tex_coords[index].y *= gp->tex_height[gp->current_texture];
}
}
}
- index1 = (y_resolution - 1) * x_resolution;
- index2 = (y_resolution - 2) * x_resolution;
- index3 = 1 + (y_resolution - 2) * x_resolution;
- calculate_normal (dots[index1], dots[index2], dots[index3], &normals[index1]);
- if (do_texture)
- {
- if (offset_texture)
- {
- tex_coords[index1].x = dots[index1].x * 0.125 + 0.5
- * (1.0 + 0.25 * asin(normals[index1].x) / (0.5 * PI));
- tex_coords[index1].y = dots[index1].y * 0.125 + 0.5
- * (1.0 + 0.25 * asin(normals[index1].y) / (0.5 * PI));
- }
- else
- {
- tex_coords[index1].x = 0.5 * (1.0 + (asin(normals[index1].x)
- / (0.5 * PI)));
- tex_coords[index1].y = 0.5 * (1.0 + (asin(normals[index1].y)
- / (0.5 * PI)));
- }
- tex_coords[index1].x *= tex_width[current_texture];
- tex_coords[index1].y *= tex_height[current_texture];
- }
-
+
+ /* Update the center of the whole blob */
+ add(&gp->blob_velocity, scale (subtract (gp->blob_anchor, gp->blob_center), 1.0 / 80.0));
+ add(&gp->blob_velocity, scale (gp->blob_force, 0.01 / gp->num_nodes));
- freak += v_freak;
- v_freak += -freak / 2000000.0;
+ add(&gp->blob_center, scale(gp->blob_velocity, 0.5));
- /* Update the center of the whole blob */
- blob_velocity.x += (blob_anchor.x - blob_center.x) / 80.0
- + 0.01 * blob_force.x / num_points;
- blob_velocity.y += (blob_anchor.y - blob_center.y) / 80.0
- + 0.01 * blob_force.y / num_points;
- blob_velocity.z += (blob_anchor.z - blob_center.z) / 80.0
- + 0.01 * blob_force.z / num_points;
-
- blob_center.x += blob_velocity.x * 0.5;
- blob_center.y += blob_velocity.y * 0.5;
- blob_center.z += blob_velocity.z * 0.5;
-
- blob_velocity.x *= 0.99;
- blob_velocity.y *= 0.99;
- blob_velocity.z *= 0.99;
+ gp->blob_velocity = scale(gp->blob_velocity, 0.999);
}
/******************************************************************************
*
* Draw the blob shape.
*
- * The horrendous indexing to calculate the verticies that form a particular
- * traiangle is the result of the conversion of my first non-openGL version of
- * blob to this openGL version. This may be tidied up when I finally playing
- * with the more interesting bits of the code.
*/
static void
-draw_blob (void)
+draw_blob (mirrorblobstruct *gp)
{
- int x, y, x2, x3;
- int index1, index2, index3;
- int lower, upper;
+ int face;
glMatrixMode (GL_MODELVIEW);
glLoadIdentity ();
/* Move down the z-axis. */
- glTranslatef (0.0, 0.0, -5.0 );
+ glTranslatef (0.0, 0.0, -4.0);
- for (y = 1; y < y_resolution; y++)
- {
- if (row_data[y - 1].num_x_points)
- {
- for (x = 0; x < row_data[y].num_x_points; x++)
- {
- glBegin (GL_TRIANGLES);
- if (x == row_data[y].num_x_points - 1)
- {
- index1 = y * x_resolution;
- }
- else
- {
- index1 = x + 1 + y * x_resolution;
- }
- index2 = x + y * x_resolution;
- index3 = ((x + 0.5) * row_data[y - 1].num_x_points
- / row_data[y].num_x_points) + (y - 1) * x_resolution;
- glArrayElement(index1);
- glArrayElement(index2);
- glArrayElement(index3);
- glEnd();
-
- lower = ((x - 0.5) * row_data[y - 1].num_x_points
- / (float)row_data[y].num_x_points);
- upper = ((x + 0.5) * row_data[y - 1].num_x_points
- / (float)row_data[y].num_x_points);
-
- if (upper > lower)
- {
- glBegin (GL_TRIANGLE_FAN);
- index1 = x + y * x_resolution;
+ gltrackball_rotate (gp->trackball);
- for (x2 = lower; x2 <= upper; x2++)
- {
- x3 = x2;
- while (x3 < 0) x3 += row_data[y - 1].num_x_points;
- while (x3 >= row_data[y - 1].num_x_points)
- x3 -= row_data[y - 1].num_x_points;
- index2 = x3 + (y - 1) * x_resolution;
-
- if (x2 < upper)
- {
- x3 = x2 + 1;
- while (x3 < 0) x3 += row_data[y - 1].num_x_points;
- while (x3 >= row_data[y - 1].num_x_points)
- x3 -= row_data[y - 1].num_x_points;
- index3 = x3 + (y - 1) * x_resolution;
- if (x2 == lower)
- {
- glArrayElement(index1);
- }
- }
- glArrayElement(index2);
- }
- glEnd ();
- }
- }
- }
+ /* glColor4ub (255, 0, 0, 128); */
+ glBegin (GL_TRIANGLES);
+ for (face = 0; face < gp->num_faces; face++)
+ {
+ glArrayElement (gp->faces[face].node1);
+ glArrayElement (gp->faces[face].node2);
+ glArrayElement (gp->faces[face].node3);
}
+ glEnd ();
+ glLoadIdentity ();
}
/******************************************************************************
static void
draw_background (ModeInfo *mi)
{
+ mirrorblobstruct *gp = &Mirrorblob[MI_SCREEN(mi)];
+
glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
glEnable (GL_TEXTURE_2D);
glDisable(GL_LIGHTING);
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
/* Reset the projection matrix to make it easier to get the size of the quad
* correct
glTexCoord2f (0.0, 0.0);
glVertex2i (0, 0);
- glTexCoord2f (0.0, -tex_height[current_texture]);
+ glTexCoord2f (0.0, gp->tex_height[gp->current_texture]);
glVertex2i (0, MI_HEIGHT(mi));
- glTexCoord2f (tex_width[current_texture], -tex_height[current_texture]);
+ glTexCoord2f (gp->tex_width[gp->current_texture], gp->tex_height[gp->current_texture]);
glVertex2i (MI_WIDTH(mi), MI_HEIGHT(mi));
- glTexCoord2f (tex_width[current_texture], 0.0);
+ glTexCoord2f (gp->tex_width[gp->current_texture], 0.0);
glVertex2i (MI_WIDTH(mi), 0);
glEnd();
static GLvoid
draw_scene(ModeInfo * mi)
{
+ mirrorblobstruct *gp = &Mirrorblob[MI_SCREEN(mi)];
+
double fade = 0.0;
double current_time;
check_gl_error ("draw_scene");
glColor4d(1.0, 1.0, 1.0, 1.0);
current_time = double_time();
- switch (state)
+ switch (gp->state)
{
case TRANSITIONING:
- fade = (current_time - state_start_time) / fade_time;
+ fade = 1.0 - (current_time - gp->state_start_time) / fade_time;
break;
+ case LOADING: /* FALL-THROUGH */
case HOLDING:
- fade = 0.0;
+ fade = 1.0;
break;
}
-
/* Set the correct texture, when transitioning this ensures that the first draw
* is the original texture (which has the new texture drawn over it with decreasing
* transparency)
*/
- if (do_texture)
+ if (load_textures)
{
- glBindTexture (GL_TEXTURE_2D, textures[current_texture]);
+ glBindTexture (GL_TEXTURE_2D, gp->textures[gp->current_texture]);
}
- if (do_paint_background && !do_wire)
+ glDisable (GL_DEPTH_TEST);
+ if (do_paint_background)
{
- glClear(GL_DEPTH_BUFFER_BIT);
- if (motion_blur)
+ glEnable (GL_TEXTURE_2D);
+ if (motion_blur > 0.0)
{
+ glClear(GL_DEPTH_BUFFER_BIT);
glEnable (GL_BLEND);
- glColor4ub (255, 255, 255, motion_blur);
+ glColor4d (1.0, 1.0, 1.0, motion_blur);
+ }
+ else
+ {
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
draw_background (mi);
/* When transitioning between two images paint the new image over the old
* image with a varying alpha value to get a smooth fade.
*/
- if (state == TRANSITIONING)
+ if (gp->state == TRANSITIONING)
{
- glDisable (GL_DEPTH_TEST);
glEnable (GL_BLEND);
/* Select the texture to transition to */
- glBindTexture (GL_TEXTURE_2D, textures[1 - current_texture]);
- glColor4d (1.0, 1.0, 1.0, fade);
+ glBindTexture (GL_TEXTURE_2D, gp->textures[1 - gp->current_texture]);
+ glColor4d (1.0, 1.0, 1.0, 1.0 - fade);
draw_background (mi);
/* Select the original texture to draw the blob */
- glBindTexture (GL_TEXTURE_2D, textures[current_texture]);
- glEnable (GL_DEPTH_TEST);
+ glBindTexture (GL_TEXTURE_2D, gp->textures[gp->current_texture]);
}
/* Clear the depth buffer bit so the backgound is behind the blob */
glClear(GL_DEPTH_BUFFER_BIT);
}
- else if (motion_blur)
+ else if (motion_blur > 0.0)
{
- glDisable (GL_DEPTH_TEST);
glEnable (GL_BLEND);
- glColor4ub (0, 0, 0, motion_blur);
+ glColor4d (0.0, 0.0, 0.0, motion_blur);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+ glTranslatef (0.0, 0.0, -4.0);
glRectd (-10.0, -10.0, 10.0, 10.0);
- if (do_wire)
+ if (wireframe)
{
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
}
- glEnable (GL_DEPTH_TEST);
glClear (GL_DEPTH_BUFFER_BIT);
}
else
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
- if (do_blend)
+ if (!do_texture)
{
- fade = fade * 0.5;
+ fade = 1.0;
+ glDisable (GL_TEXTURE_2D);
}
- calc_blob(MI_WIDTH(mi), MI_HEIGHT(mi), 1024, 2.5, fade);
+ calc_blob(gp, MI_WIDTH(mi), MI_HEIGHT(mi), BUMP_ARRAY_SIZE, 2.5, fade * blend);
- glEnable(GL_LIGHTING);
- glEnable(GL_LIGHT0);
- glEnable(GL_LIGHT1);
+ set_blob_gl_state(fade * blend);
- if (do_blend)
+ if (blend < 1.0)
{
- glEnable (GL_BLEND);
- if (do_colour)
- {
- glBlendFunc (GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA);
- }
- else
- {
- glColor4d (1.0, 1.0, 1.0, 0.5 - fade);
- }
+ /* Disable the three colour chanels so that only the depth buffer is updated */
+ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+ draw_blob(gp);
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ glDepthFunc(GL_LEQUAL);
}
- else
- {
- glDisable (GL_BLEND);
- glColor4d (1.0, 1.0, 1.0, 1.0);
- }
- draw_blob();
+ glDepthFunc(GL_LEQUAL);
+ draw_blob(gp);
- if (do_blend && do_colour)
- {
- glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- }
-
- /* While transitioning draw a second blob twice with a modified alpha channel.
- * The trasitioning state machine is very crude, it simply counts frames
- * rather than elapsed time but it works.
+ /* While transitioning between images draw a second blob with a modified
+ * alpha value.
*/
- if (do_texture && (hold_time > 0))
+ if (load_textures && (hold_time > 0))
{
- switch (state)
+ switch (gp->state)
{
- case TRANSITIONING:
- glClear(GL_DEPTH_BUFFER_BIT);
- glEnable (GL_BLEND);
- /* Select the texture to transition to */
- glBindTexture (GL_TEXTURE_2D, textures[1 - current_texture]);
- glColor4d (1.0, 1.0, 1.0, fade);
- draw_blob ();
-
- if ((current_time - state_start_time) > fade_time)
+ case HOLDING:
+ if ((current_time - gp->state_start_time) > hold_time)
{
- state = HOLDING;
- state_start_time = current_time;
- current_texture = 1 - current_texture;
+ grab_texture(mi, 1 - gp->current_texture);
+ gp->state = LOADING;
}
break;
- case HOLDING:
- if ((current_time - state_start_time) > hold_time)
+ case LOADING:
+ /* Once the image has loaded move to the TRANSITIONING STATE */
+ if (!gp->waiting_for_image_p)
{
- grab_texture (mi, 1 - current_texture);
- state = TRANSITIONING;
+ gp->state = TRANSITIONING;
/* Get the time again rather than using the current time so
* that the time taken by the grab_texture function is not part
* of the fade time
*/
- state_start_time = double_time();
+ gp->state_start_time = double_time();
+ }
+ break;
+
+ case TRANSITIONING:
+
+ /* If the blob is textured draw over existing blob to fade between
+ * images
+ */
+ if (do_texture)
+ {
+ /* Select the texture to transition to */
+ glBindTexture (GL_TEXTURE_2D, gp->textures[1 - gp->current_texture]);
+ glEnable (GL_BLEND);
+
+ /* If colour is enabled update the alpha data in the buffer and
+ * use that in the blending since the alpha of the incomming
+ * verticies will not be correct
+ */
+ if (do_colour)
+ {
+ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
+ glClearColor(0.0, 0.0, 0.0, (1.0 - fade) * blend);
+ glClear(GL_COLOR_BUFFER_BIT);
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ glBlendFunc(GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA);
+ }
+ else
+ {
+ glColor4d(0.9, 0.9, 1.0, (1.0 - fade) * blend);
+ }
+
+ draw_blob (gp);
+
+ if (do_colour)
+ {
+ /* Restore the 'standard' blend functions. */
+ glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ }
+ }
+
+ if ((current_time - gp->state_start_time) > fade_time)
+ {
+ gp->state = HOLDING;
+ gp->state_start_time = current_time;
+ gp->current_texture = 1 - gp->current_texture;
}
break;
+
}
}
}
/******************************************************************************
*
- * XScreensaver screen update entry
+ * XMirrorblob screen update entry
*/
-void
-draw_screensaver(ModeInfo * mi)
+ENTRYPOINT void
+draw_mirrorblob(ModeInfo * mi)
{
- screensaverstruct *gp = &Screensaver[MI_SCREEN(mi)];
+ mirrorblobstruct *gp = &Mirrorblob[MI_SCREEN(mi)];
Display *display = MI_DISPLAY(mi);
Window window = MI_WINDOW(mi);
if (!gp->glx_context)
return;
+ /* Wait for the first image; for subsequent images, load them in the
+ background while animating. */
+ if (gp->waiting_for_image_p && gp->first_image_p)
+ return;
+
glXMakeCurrent(display, window, *(gp->glx_context));
draw_scene(mi);
if (mi->fps_p) do_fps (mi);
/******************************************************************************
*
- * XScreensaver screen resize entry
+ * XMirrorblob screen resize entry
*/
-void
-reshape_screensaver(ModeInfo *mi, int width, int height)
+ENTRYPOINT void
+reshape_mirrorblob(ModeInfo *mi, int width, int height)
{
glViewport( 0, 0, MI_WIDTH(mi), MI_HEIGHT(mi) );
reset_projection(width, height);
}
+/****************************************************************************
+ *
+ * Handle Mouse events
+ */
+ENTRYPOINT Bool
+mirrorblob_handle_event (ModeInfo * mi, XEvent * event)
+{
+ mirrorblobstruct *gp = &Mirrorblob[MI_SCREEN (mi)];
+
+ if (event->xany.type == ButtonPress &&
+ event->xbutton.button == Button1)
+ {
+ gp->button_down = 1;
+ gltrackball_start (gp->trackball, event->xbutton.x,
+ event->xbutton.y, MI_WIDTH (mi), MI_HEIGHT (mi));
+ return True;
+ }
+ else if (event->xany.type == ButtonRelease &&
+ event->xbutton.button == Button1)
+ {
+ gp->button_down = 0;
+ return True;
+ }
+ else if (event->xany.type == ButtonPress &&
+ event->xbutton.button == Button4)
+ {
+ zoom *= 1.1;
+ return True;
+ }
+ else if (event->xany.type == ButtonPress &&
+ event->xbutton.button == Button5)
+ {
+
+ zoom *= 0.9;
+ return True;
+ }
+ else if (event->xany.type == MotionNotify && gp->button_down)
+ {
+ gltrackball_track (gp->trackball, event->xmotion.x,
+ event->xmotion.y, MI_WIDTH (mi), MI_HEIGHT (mi));
+ return True;
+ }
+ return False;
+}
+
/******************************************************************************
*
- * XScreensaver initialise entry
+ * XMirrorblob initialise entry
*/
-void
-init_screensaver(ModeInfo * mi)
+ENTRYPOINT void
+init_mirrorblob(ModeInfo * mi)
{
int screen = MI_SCREEN(mi);
- screensaverstruct *gp;
+ mirrorblobstruct *gp;
- if (Screensaver == NULL)
+ if (Mirrorblob == NULL)
{
- if ((Screensaver = (screensaverstruct *)
- calloc(MI_NUM_SCREENS(mi), sizeof (screensaverstruct))) == NULL)
+ if ((Mirrorblob = (mirrorblobstruct *)
+ calloc(MI_NUM_SCREENS(mi), sizeof (mirrorblobstruct))) == NULL)
{
return;
}
}
- gp = &Screensaver[screen];
+ gp = &Mirrorblob[screen];
gp->window = MI_WINDOW(mi);
if ((gp->glx_context = init_GL(mi)) != NULL)
{
- reshape_screensaver(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
+ reshape_mirrorblob(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
initialize_gl(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
}
else
{
MI_CLEARWINDOW(mi);
}
+ gp->trackball = gltrackball_init ();
+
+ initialise_blob(gp, MI_WIDTH(mi), MI_HEIGHT(mi), BUMP_ARRAY_SIZE);
+ gp->state_start_time = double_time();
- initialise_blob(MI_WIDTH(mi), MI_HEIGHT(mi), 1024);
- state_start_time = double_time();
+ gp->first_image_p = True;
}
/******************************************************************************
*
- * XScreensaver cleanup entry
+ * XMirrorblob cleanup entry
*/
-void
-release_screensaver(ModeInfo * mi)
+ENTRYPOINT void
+release_mirrorblob(ModeInfo * mi)
{
- if (row_data) free(row_data);
- if (field_data) free(field_data);
- if (colours) free(colours);
- if (tex_coords) free(tex_coords);
- if (dots) free(dots);
- if (wall_field) free(wall_field);
- if (field) free(field);
-
- if (Screensaver != NULL)
- {
- (void) free((void *) Screensaver);
- Screensaver = NULL;
+ if (Mirrorblob != NULL) {
+ int i;
+ for (i = 0; i < MI_NUM_SCREENS(mi); i++) {
+ mirrorblobstruct *gp = &Mirrorblob[i];
+ if (gp->nodes) free(gp->nodes);
+ if (gp->faces) free(gp->faces);
+ if (gp->bump_data) free(gp->bump_data);
+ if (gp->colours) free(gp->colours);
+ if (gp->tex_coords) free(gp->tex_coords);
+ if (gp->dots) free(gp->dots);
+ if (gp->wall_shape) free(gp->wall_shape);
+ if (gp->bump_shape) free(gp->bump_shape);
}
- FreeAllGL(mi);
+
+ free(Mirrorblob);
+ Mirrorblob = NULL;
+ }
+ FreeAllGL(mi);
}
+
+XSCREENSAVER_MODULE ("MirrorBlob", mirrorblob)
+
#endif