http://www.archive.org/download/tucows_10294_XScreenSaver/xscreensaver-4.10.tar.gz
[xscreensaver] / hacks / glx / jigglypuff.c
index ba36ecf893d5ef27d085ce2a9b5fdce2ddcfd6cf..bc20c44279c57bca3af7e2bdb2d6a25dc3507f12 100644 (file)
@@ -1,4 +1,4 @@
-/* jigglypuff - the least colorful screensaver you'll ever see
+/* jigglypuff - a most, most, unfortunate screensaver.
  *
  * Copyright (c) 2003 Keith Macleod (kmacleod@primus.ca)
  *
  * orbiting lazily about the screen. More of an accident
  * than anything else.
  *
- * This could either use more options, or less options and
- * more randomness.
- *
  * Apologies to anyone who thought they were getting a Pokemon
  * out of this.
  *
  * Of course, if you modify it to do something interesting and/or
  * funny, I'd appreciate receiving a copy.
+ *
+ * 04/06/2003 - Oops, figured out what was wrong with the sphere
+ *              mapping. I had assumed it was done in model space,
+ *              but of course I was totally wrong... Eye space you
+ *              say? Yup. km
+ *
+ * 03/31/2003 - Added chrome to the color options. The mapping
+ *              is anything but 'correct', but it's a pretty good
+ *              effect anyways, as long as the surface is jiggling
+ *              enough that you can't tell. Sure, it seems  kind of odd
+ *              that it's reflecting a sky that's obviously not there,
+ *              but who has time to worry about silly details like
+ *              that? Not me, ah rekkin'. km
+ *
  */
 
 #include <X11/Intrinsic.h>
 
 #ifdef STANDALONE
-# define PROGCLASS        "Jigglypuff"
-# define HACK_INIT        init_jigglypuff
-# define HACK_DRAW        draw_jigglypuff
-# define HACK_RESHAPE     reshape_jigglypuff
-# define jigglypuff_opts  xlockmore_opts
-# define DEFAULTS         "*random: True\n" \
-                          "*delay: 20000\n" \
-                          "*showFPS: False\n" \
-                          "wireframe: False\n"
+# define PROGCLASS          "Jigglypuff"
+# define HACK_INIT          init_jigglypuff
+# define HACK_DRAW          draw_jigglypuff
+# define HACK_RESHAPE       reshape_jigglypuff
+# define HACK_HANDLE_EVENT  jigglypuff_handle_event
+# define EVENT_MASK         PointerMotionMask 
+# define jigglypuff_opts    xlockmore_opts
+
+#define DEF_COLOR           "cycle"
+#define DEF_SHININESS       "100"
+#define DEF_COMPLEXITY      "2"
+#define DEF_SPEED           "500"
+#define DEF_DISTANCE        "100"
+#define DEF_HOLD            "800"
+#define DEF_SPHERISM        "75"
+#define DEF_DAMPING         "500"
+
+# define DEFAULTS           "*delay: 20000\n" \
+                            "*showFPS: False\n" \
+                            "*wireframe: False\n" \
+                            "*color: cycle\n" \
+                            "*shininess: 100\n" \
+                            "*complexity: 2\n" \
+                            "*speed: 500\n" \
+                            "*distance: 100\n" \
+                            "*hold: 800\n" \
+                            "*spherism: 200\n" \
+                            "*damping: 500\n"
+
 
 # include "xlockmore.h"
 #else
 # include "config.h"
 #endif
 
+#include "xpm-ximage.h"
+#include "gltrackball.h"
+#include "../images/jigglymap.xpm"
+
 #ifdef USE_GL
 #include <GL/gl.h>
 
 #define min(a,b) (((a)<(b))?(a):(b))
 #endif
 
+/* Why isn't RAND_MAX correct in the first place? */
+#define REAL_RAND_MAX (2.0*(float)RAND_MAX)
+
 static int spherism;
 static int hold;
 static int distance;
 static int damping;
 
-static int do_wireframe;
+static int complexity;
+static int speed;
+
 static int do_tetrahedron;
-static int do_spooky;
+static int spooky;
+static char *color;
+static int shininess;
+
 static int random_parms;
 
 typedef struct solid solid;
@@ -74,14 +117,23 @@ typedef struct {
     float damping_factor;
 
     int do_wireframe;
-    int do_spooky;
+    int spooky;
+    int color_style;
+    GLint shininess;
+    GLfloat jiggly_color[4];
+    GLfloat color_dir[3];
 
     solid *shape;
+
+    trackball_state *trackball;
+    int button_down;
+
     float angle;
     float axis;
+    float speed;
 
     GLXContext *glx_context;
-}jigglystruct;
+} jigglystruct;
 
 static jigglystruct *jss = NULL;
 
@@ -90,22 +142,29 @@ static XrmOptionDescRec opts[] = {
     {"+random", ".Jigglypuff.random", XrmoptionNoArg, (caddr_t)"false"},
     {"-tetra", ".Jigglypuff.tetra", XrmoptionNoArg, (caddr_t)"true"},
     {"+tetra", ".Jigglypuff.tetra", XrmoptionNoArg, (caddr_t)"false"},
-    {"-spooky", ".Jigglypuff.spooky", XrmoptionNoArg, (caddr_t)"true"},
-    {"+spooky", ".Jigglypuff.spooky", XrmoptionNoArg, (caddr_t)"false"},
-    {"-spherism", ".Jigglypuff.spherism", XrmoptionSepArg, "500"},
-    {"-hold", ".Jigglypuff.hold", XrmoptionSepArg, "500"},
-    {"-distance", "Jigglypuff.distance", XrmoptionSepArg, "500"},
-    {"-damping", "Jigglypuff.damping", XrmoptionSepArg, "50"}
+    {"-spooky", ".Jigglypuff.spooky", XrmoptionSepArg, (caddr_t)"0"},
+    {"-color", ".Jigglypuff.color", XrmoptionSepArg, (caddr_t)DEF_COLOR},
+    {"-shininess", ".Jigglypuff.shininess", XrmoptionSepArg, (caddr_t)DEF_SHININESS},
+    {"-complexity", ".Jigglypuff.complexity", XrmoptionSepArg, (caddr_t)DEF_COMPLEXITY},
+    {"-speed", ".Jigglypuff.speed", XrmoptionSepArg, (caddr_t)DEF_SPEED},
+    {"-spherism", ".Jigglypuff.spherism", XrmoptionSepArg, (caddr_t)DEF_SPHERISM},
+    {"-hold", ".Jigglypuff.hold", XrmoptionSepArg, (caddr_t)DEF_HOLD},
+    {"-distance", "Jigglypuff.distance", XrmoptionSepArg, (caddr_t)DEF_DISTANCE},
+    {"-damping", "Jigglypuff.damping", XrmoptionSepArg, (caddr_t)DEF_DAMPING}
 };
 
 static argtype vars[] = {
     {(caddr_t*)&random_parms, "random", "Random", "False", t_Bool},
-    {(caddr_t*)&do_tetrahedron, "tetra", "Tetra", "True", t_Bool},
-    {(caddr_t*)&do_spooky, "spooky", "Spooky", "False", t_Bool},
-    {(caddr_t*)&spherism, "spherism", "Spherism", "100", t_Int},
-    {(caddr_t*)&hold, "hold", "Hold", "600", t_Int},
-    {(caddr_t*)&distance, "distance", "Distance", "500", t_Int},
-    {(caddr_t*)&damping, "damping", "Damping", "50", t_Int}
+    {(caddr_t*)&do_tetrahedron, "tetra", "Tetra", "False", t_Bool},
+    {(caddr_t*)&spooky, "spooky", "Spooky", "0", t_Int},
+    {(caddr_t*)&color, "color", "Color", DEF_COLOR, t_String},
+    {(caddr_t*)&shininess, "shininess", "Shininess", DEF_SHININESS, t_Int},
+    {(caddr_t*)&complexity, "complexity", "Complexity", DEF_COMPLEXITY, t_Int},
+    {(caddr_t*)&speed, "speed", "Speed", DEF_SPEED, t_Int},
+    {(caddr_t*)&spherism, "spherism", "Spherism", DEF_SPHERISM, t_Int},
+    {(caddr_t*)&hold, "hold", "Hold", DEF_HOLD, t_Int},
+    {(caddr_t*)&distance, "distance", "Distance", DEF_DISTANCE, t_Int},
+    {(caddr_t*)&damping, "damping", "Damping", DEF_DAMPING, t_Int}
 };
 
 #undef countof
@@ -113,6 +172,29 @@ static argtype vars[] = {
 
 ModeSpecOpt jigglypuff_opts = {countof(opts), opts, countof(vars), vars, NULL};
 
+#define COLOR_STYLE_NORMAL    0
+#define COLOR_STYLE_CYCLE     1
+#define COLOR_STYLE_CLOWNBARF 2
+#define COLOR_STYLE_FLOWERBOX 3
+#define COLOR_STYLE_CHROME    4
+
+#define CLOWNBARF_NCOLORS 5
+
+static GLfloat clownbarf_colors[CLOWNBARF_NCOLORS][4] = {
+    {0.7, 0.7, 0.0, 1.0},
+    {0.8, 0.1, 0.1, 1.0},
+    {0.1, 0.1, 0.8, 1.0},
+    {0.9, 0.9, 0.9, 1.0},
+    {0.0, 0.0, 0.0, 1.0}
+};
+
+static GLfloat flowerbox_colors[4][4] = {
+    {0.7, 0.7, 0.0, 1.0},
+    {0.9, 0.0, 0.0, 1.0},
+    {0.0, 0.9, 0.0, 1.0},
+    {0.0, 0.0, 0.9, 1.0},
+};
+
 #ifdef DEBUG
 #define _DEBUG(msg, args...) do { \
     fprintf(stderr, "%s : %d : " msg ,__FILE__,__LINE__ ,##args); \
@@ -121,6 +203,7 @@ ModeSpecOpt jigglypuff_opts = {countof(opts), opts, countof(vars), vars, NULL};
 #define _DEBUG(msg, args...)
 #endif
 
+/* This is all the half-edge b-rep code (as well as basic geometry) */
 typedef struct face face;
 typedef struct edge edge;
 typedef struct hedge hedge;
@@ -137,6 +220,7 @@ struct solid {
 struct face {
     solid *s;
     hedge *start;
+    GLfloat *color;
     face *next;
 };
 
@@ -165,54 +249,50 @@ struct vertex {
     vertex *next;
 };
 
-static void vector_init(vector v, coord x, coord y, coord z)
+static inline void vector_init(vector v, coord x, coord y, coord z)
 {
     v[0] = x;
     v[1] = y;
     v[2] = z;
 }    
 
-static void vector_copy(vector d, vector s)
+static inline void vector_copy(vector d, vector s)
 {
     d[0] = s[0];
     d[1] = s[1];
     d[2] = s[2];
 }
 
-#if 0
-static void vector_add(vector v1, vector v2, vector v)
+static inline void vector_add(vector v1, vector v2, vector v)
 {
     vector_init(v, v1[0]+v2[0], v1[1]+v2[1], v1[2]+v2[2]);
 }
-#endif
 
-static void vector_add_to(vector v1, vector v2)
+static inline void vector_add_to(vector v1, vector v2)
 {
     v1[0] += v2[0];
     v1[1] += v2[1];
     v1[2] += v2[2];
 }
 
-static void vector_sub(vector v1, vector v2, vector v)
+static inline void vector_sub(vector v1, vector v2, vector v)
 {
     vector_init(v, v1[0]-v2[0], v1[1]-v2[1], v1[2]-v2[2]);
 }
 
-static void vector_scale(vector v, coord s)
+static inline void vector_scale(vector v, coord s)
 {
     v[0] *= s;
     v[1] *= s;
     v[2] *= s;
 }
 
-#if 0
-static coord dot(vector v1, vector v2)
+static inline coord dot(vector v1, vector v2)
 {
     return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2];
 }
-#endif
 
-static void cross(vector v1, vector v2, vector v)
+static inline void cross(vector v1, vector v2, vector v)
 {
     vector_init(v,
                v1[1]*v2[2] - v2[1]*v1[2],
@@ -220,17 +300,17 @@ static void cross(vector v1, vector v2, vector v)
                v1[0]*v2[1] - v2[0]*v1[1]);
 }
 
-static coord magnitude2(vector v)
+static inline coord magnitude2(vector v)
 {
     return v[0]*v[0] + v[1]*v[1] + v[2]*v[2];
 }
 
-static coord magnitude(vector v)
+static inline coord magnitude(vector v)
 {
     return sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);
 }
 
-static void normalize(vector v)
+static inline void normalize(vector v)
 {
     coord mag = 1.0/sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2]);
 
@@ -239,7 +319,7 @@ static void normalize(vector v)
     v[2] *= mag;
 }
 
-static void normalize_to(vector v, coord m)
+static inline void normalize_to(vector v, coord m)
 {
     coord mag = 1.0/sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2])/m;
 
@@ -248,7 +328,7 @@ static void normalize_to(vector v, coord m)
     v[2] *= mag;
 }
 
-static void midpoint(vector v1, vector v2, vector v)
+static inline void midpoint(vector v1, vector v2, vector v)
 {
     vector_init(v,
                v1[0] + 0.5 * (v2[0] - v1[0]),
@@ -256,7 +336,7 @@ static void midpoint(vector v1, vector v2, vector v)
                v1[2] + 0.5 * (v2[2] - v1[2]));
 }
 
-static hedge *partner(hedge *h) {
+static inline hedge *partner(hedge *h) {
     if(!h->e)
        return NULL;
     if(h == h->e->left) {
@@ -266,12 +346,12 @@ static hedge *partner(hedge *h) {
        return h->e->left;
     }
     else {
-       _DEBUG("Holy shit Batman! this edge is fucked up!\n");
+       _DEBUG("Inconsistent edge detected. Presumably, this is a bug. Exiting.\n");
        exit(-1);
     }
 }
 
-static vertex *vertex_new(solid *s, vector v)
+vertex *vertex_new(solid *s, vector v)
 {
     vertex *vtx = (vertex*)malloc(sizeof(vertex));
 
@@ -290,7 +370,7 @@ static vertex *vertex_new(solid *s, vector v)
  * i.e. it is a helper for the split_* functions, which
  * maintain the consistency of the solid.
  */
-static hedge *hedge_new(hedge *hafter, vertex *vtx)
+hedge *hedge_new(hedge *hafter, vertex *vtx)
 {
     hedge *h = (hedge*)malloc(sizeof(hedge));
     
@@ -308,7 +388,7 @@ static hedge *hedge_new(hedge *hafter, vertex *vtx)
     return h;
 }
 
-static edge *edge_new(solid *s)
+edge *edge_new(solid *s)
 {
     edge *e = (edge*)malloc(sizeof(edge));
     if(!e) {
@@ -322,7 +402,7 @@ static edge *edge_new(solid *s)
     return e;
 }
 
-static face *face_new(solid *s, hedge *h)
+face *face_new(solid *s, hedge *h)
 {
     face *f = (face*)malloc(sizeof(face));
     if(!f) {
@@ -344,17 +424,13 @@ static face *face_new(solid *s, hedge *h)
  *    there are at least 2 faces.
  *    partner(h)->next->vtx == vtx
  * Post-assumptions:
- *    the new halfedge will be inserted _before_ the
- *    halfedge owning vtx in f.
- *    THIS IS WRONG. FIX ME!!!
- * New Deal - the Invariants
  *    the new halfedge will be inserted AFTER the indicated
  *    halfedge. This means that f->start is guaranteed not to
  *    change.
- * Also, the vertex returned will have h==<the new halfedge>.
+ *    the vertex returned will have h==<the new halfedge>.
  */
 
-static vertex *vertex_split(hedge *h, vector v)
+vertex *vertex_split(hedge *h, vector v)
 {
     hedge *h2, *hn1, *hn2;
     vertex *vtxn;
@@ -364,7 +440,7 @@ static vertex *vertex_split(hedge *h, vector v)
     f1 = h->f;
     h2 = partner(h);
     f2 = h2->f;
-
+    
     vtxn = vertex_new(f1->s, v);
     hn1 = hedge_new(h, vtxn);
     vtxn->h = hn1;
@@ -384,7 +460,7 @@ static vertex *vertex_split(hedge *h, vector v)
     return vtxn;
 }
 
-static face *face_split(face *f, hedge *h1, hedge *h2)
+face *face_split(face *f, hedge *h1, hedge *h2)
 {
     hedge *hn1, *hn2, *tmp;
     edge *en;
@@ -424,10 +500,11 @@ static face *face_split(face *f, hedge *h1, hedge *h2)
        tmp->f = fn;
        tmp = tmp->next;
     } while(tmp != fn->start);
+    fn->color = f->color;
     return fn;
 }
 
-static solid *solid_new(vector where) 
+solid *solid_new(vector where) 
 {
     solid *s = (solid*)malloc(sizeof(solid));
     face *f1, *f2;
@@ -463,33 +540,8 @@ static solid *solid_new(vector where)
     return s;
 }
 
-static solid *tetra(void) 
-{
-    solid *s;
-    vertex *vtx;
-    vector v;
-    hedge *h;
-    face *f;
-
-    vector_init(v, 1, 1, 1);
-    s = solid_new(v);
-    vector_init(v, -1, -1, 1);
-    h = s->faces->start;
-    vtx = vertex_split(h, v);
-    vector_init(v, -1, 1, -1);
-    vtx = vertex_split(vtx->h, v);
-    h = vtx->h;
-    f = face_split(s->faces, h, h->prev);
-    vector_init(v, 1, -1, -1);
-    vertex_split(f->start, v);
-    f = s->faces->next->next;
-    h = f->start;
-    face_split(f, h, h->next->next);
-
-    return s;
-}
-
-static void face_tessel2(face *f)
+/* This is all the code directly related to constructing the jigglypuff */
+void face_tessel2(face *f)
 {
     hedge *h1=f->start->prev, *h2=f->start->next;
     
@@ -510,7 +562,7 @@ static void face_tessel2(face *f)
  * added at the head of the list. If that ever changes,
  * this is borked. 
  */
-static void solid_tesselate(solid *s) 
+void solid_tesselate(solid *s) 
 {
     edge *e = s->edges;
     face *f = s->faces;
@@ -527,7 +579,7 @@ static void solid_tesselate(solid *s)
     }
 }
                
-static void solid_spherify(solid * s, coord size) 
+void solid_spherify(solid * s, coord size) 
 {
     vertex *vtx = s->vertices;
 
@@ -537,9 +589,43 @@ static void solid_spherify(solid * s, coord size)
     }
 }
 
-static solid *tesselated_tetrahedron(coord size, int iter
+solid *tetrahedron(jigglystruct *js
 {
-    solid *s = tetra();
+    solid *s;
+    vertex *vtx;
+    vector v;
+    hedge *h;
+    face *f;
+    int i;
+
+    vector_init(v, 1, 1, 1);
+    s = solid_new(v);
+    vector_init(v, -1, -1, 1);
+    h = s->faces->start;
+    vtx = vertex_split(h, v);
+    vector_init(v, -1, 1, -1);
+    vtx = vertex_split(vtx->h, v);
+    h = vtx->h;
+    f = face_split(s->faces, h, h->prev);
+    vector_init(v, 1, -1, -1);
+    vertex_split(f->start, v);
+    f = s->faces->next->next;
+    h = f->start;
+    face_split(f, h, h->next->next);
+
+    if(js->color_style == COLOR_STYLE_FLOWERBOX) {
+       f = s->faces;
+       for(i=0; i<4; i++) {
+           f->color = flowerbox_colors[i];
+           f = f->next;
+       }
+    }      
+
+    return s;
+}
+
+solid *tesselated_tetrahedron(coord size, int iter, jigglystruct *js) {
+    solid *s = tetrahedron(js);
     int i;
 
     for(i=0; i<iter; i++) {
@@ -548,7 +634,17 @@ static solid *tesselated_tetrahedron(coord size, int iter)
     return s;
 }
 
-static void vertex_calcnormal(vertex *vtx, int spooky)
+static void clownbarf_colorize(solid *s) {
+    face *f = s->faces;
+    while(f) {
+       f->color = clownbarf_colors[random() % CLOWNBARF_NCOLORS];
+       f = f->next;
+    }
+}
+
+/* Here be the rendering code */
+
+static inline void vertex_calcnormal(vertex *vtx, jigglystruct *js)
 {
     hedge *start = vtx->h, *h=start;
     
@@ -561,19 +657,24 @@ static void vertex_calcnormal(vertex *vtx, int spooky)
        vector_add_to(vtx->n, norm);
        h = partner(h)->next;
     } while(h != start);
-    if(!spooky)
+    if(!js->spooky)
        normalize(vtx->n);
     else
-       vector_scale(vtx->n, 15);
+       vector_scale(vtx->n, js->spooky);
 }
 
-static void vertex_render(vertex *vtx)
+static inline void vertex_render(vertex *vtx, jigglystruct *js)
 {
     glNormal3fv(vtx->n);
     glVertex3fv(vtx->v);
 }
 
-static void face_render(face *f)
+/* This can be optimized somewhat due to the fact that all
+ * the faces are triangles. I haven't actually tested to
+ * see what the cost is of calling glBegin/glEnd for each
+ * triangle.
+ */
+static inline void face_render(face *f, jigglystruct *js)
 {
     hedge *h1, *h2, *hend;
 
@@ -581,58 +682,49 @@ static void face_render(face *f)
     hend = h1->prev;
     h2 = h1->next;
     
+    if(js->color_style == COLOR_STYLE_FLOWERBOX ||
+       js->color_style == COLOR_STYLE_CLOWNBARF)
+       glColor4fv(f->color);
     glBegin(GL_TRIANGLES);
     while(h1 != hend && h2 !=hend) {
-       vertex_render(h1->vtx);
-       vertex_render(h2->vtx);
-       vertex_render(hend->vtx);
+       vertex_render(h1->vtx, js);
+       vertex_render(h2->vtx, js);
+       vertex_render(hend->vtx, js);
        h1 = h2;
        h2 = h1->next;
     }
     glEnd();
 }
 
-static void jigglypuff_render(jigglystruct *js) 
+void jigglypuff_render(jigglystruct *js) 
 {
     face *f = js->shape->faces;
     vertex *vtx = js->shape->vertices;
 
     while(vtx) {
-       vertex_calcnormal(vtx, js->do_spooky);
+       vertex_calcnormal(vtx, js);
        vtx = vtx->next;
     }
     while(f) {
-       face_render(f);
+       face_render(f, js);
        f=f->next;
     }
 }
 
-void calculate_parameters(jigglystruct *js) {
-    js->stable_distance = (float)distance / 10000.0;
-    js->hold_strength = (float)hold / 1000.0;
-    js->spherify_strength = (float)spherism / 10000.0;
-    js->damping_velocity = (float)damping / 100000.0;
-    js->damping_factor = 
-       min(0.1, 1.0/max(js->hold_strength, js->spherify_strength));
-}
+/* This is the jiggling code */
 
-void randomize_parameters(void) {
-    do_tetrahedron = !(random() & 1);
-    do_spooky = !(random() & 3);
-    do_wireframe = !(random() & 3);
-    spherism = random() % 1000;
-    hold = random() % 1000;
-    distance = random() % 1000;
-    damping = random() % 1000;
-}    
+/* stable distance when subdivs == 4 */
+#define STABLE_DISTANCE 0.088388347648
 
-void update_shape(jigglystruct *js) {
+void update_shape(jigglystruct *js)
+{
     vertex *vtx = js->shape->vertices;
     edge *e = js->shape->edges;
     vector zero;
 
     vector_init(zero, 0, 0, 0);
 
+    /* sum all the vertex-vertex forces */
     while(e) {
        vector f;
        coord mag;
@@ -644,6 +736,10 @@ void update_shape(jigglystruct *js) {
        vector_add_to(e->right->vtx->f, f);
        e = e->next;
     }
+
+    /* scale back the v-v force and add the spherical force
+     * then add the result to the vertex velocity, damping
+     * if necessary. Finally, move the vertex */
     while(vtx) {
        coord mag;
        vector to_sphere;
@@ -662,56 +758,32 @@ void update_shape(jigglystruct *js) {
     }
 }
 
-void draw_jigglypuff(ModeInfo *mi)
-{
-    jigglystruct *js = &jss[MI_SCREEN(mi)];
-
-    glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(js->glx_context));
-    
-    glDrawBuffer(GL_BACK);
-    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-
-    glMatrixMode(GL_MODELVIEW);
-    glLoadIdentity();
+/* These are the various initialization routines */
 
-    glRotatef(js->angle, sin(js->axis), cos(js->axis), -sin(js->axis));
-    glTranslatef(0,0,5);
-
-    if((js->angle+=0.1) >= 360.0f ) {
-       js->angle -= 360.0f;
-    }
-    if((js->axis+=0.01f) >= 2*M_PI ) {
-       js->axis -= 2*M_PI;
-    }
-    jigglypuff_render(js);
-    if(mi->fps_p)
-       do_fps(mi);
-    glFinish();
-    update_shape(js);
-    glXSwapBuffers(MI_DISPLAY(mi), MI_WINDOW(mi));
-}
-
-void reshape_jigglypuff(ModeInfo *mi, int width, int height)
+void init_texture(ModeInfo *mi)
 {
-    GLfloat aspect = (GLfloat)width / (GLfloat)height;
+    XImage *img = xpm_to_ximage(mi->dpy, mi->xgwa.visual,
+                              mi->xgwa.colormap, jigglymap_xpm);
 
-    glViewport(0, 0, width, height);
-    glMatrixMode(GL_PROJECTION);
-    glLoadIdentity();
-    glFrustum(-0.5*aspect, 0.5*aspect, -0.5, 0.5, 1, 20);
-    glTranslatef(0,0,-10);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,
+                img->width, img->height, 0, GL_RGBA,
+                GL_UNSIGNED_BYTE, img->data);
+
+    XDestroyImage(img);
 }
 
-static void setup_opengl(ModeInfo *mi, jigglystruct *js)
+void setup_opengl(ModeInfo *mi, jigglystruct *js)
 {
     const GLfloat lpos0[4] = {-12, 8, 12, 0};
     const GLfloat lpos1[4] = {7, -5, 0, 0};
     const GLfloat lcol0[4] = {0.7, 0.7, 0.65, 1};
     const GLfloat lcol1[4] = {0.3, 0.2, 0.1, 1};
-    const GLfloat color1[4]={1, 1, 1, 0.5};
-    const GLfloat color2[4]={0.9, 0.9, 0.9, 0.5};
+    const GLfloat scolor[4]= {0.9, 0.9, 0.9, 0.5};
 
+    glDrawBuffer(GL_BACK);
+    glClearColor(0, 0, 0, 0);
     glShadeModel(GL_SMOOTH);
+    glEnable(GL_DEPTH_TEST);
 
     if(js->do_wireframe) {
        glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
@@ -722,27 +794,209 @@ static void setup_opengl(ModeInfo *mi, jigglystruct *js)
        glEnable(GL_CULL_FACE);
     }
 
-    glEnable(GL_DEPTH_TEST);
+    if(js->color_style != COLOR_STYLE_CHROME) {
+       glEnable(GL_LIGHTING);
+       glEnable(GL_LIGHT0);
+       glEnable(GL_LIGHT1);
+       
+       glLightfv(GL_LIGHT0, GL_POSITION, lpos0);
+       glLightfv(GL_LIGHT1, GL_POSITION, lpos1);
+       glLightfv(GL_LIGHT0, GL_DIFFUSE, lcol0);
+       glLightfv(GL_LIGHT1, GL_DIFFUSE, lcol1);
+
+       glEnable(GL_COLOR_MATERIAL);
+       glColor4fv(js->jiggly_color);
+
+       glMaterialfv(GL_FRONT, GL_SPECULAR, scolor);
+       glMateriali(GL_FRONT, GL_SHININESS, js->shininess);
+    }
+    else { /* chrome */
+       init_texture(mi);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+       glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
+       glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
+       glEnable(GL_TEXTURE_GEN_S);
+       glEnable(GL_TEXTURE_GEN_T);
+       glEnable(GL_TEXTURE_2D);
+       glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+    }
+}
 
-    glEnable(GL_LIGHTING);
-    glEnable(GL_LIGHT0);
-    glEnable(GL_LIGHT1);
+int parse_color(jigglystruct *js)
+{
+    int r, g, b;
+    if(!strcmp(color, "clownbarf")) {
+       js->color_style = COLOR_STYLE_CLOWNBARF;
+       return 1;
+    }
+    else if(!strcmp(color, "flowerbox")) {
+       js->color_style = COLOR_STYLE_FLOWERBOX;
+       return 1;
+    }
+    else if(!strcmp(color, "chrome")) {
+       js->color_style = COLOR_STYLE_CHROME;
+       return 1;
+    }
+    else if(!strcmp(color, "cycle")) {
+       js->color_style = COLOR_STYLE_CYCLE;
+       js->jiggly_color[0] = ((float)random()) / REAL_RAND_MAX * 0.7 + 0.3;
+       js->jiggly_color[1] = ((float)random()) / REAL_RAND_MAX * 0.7 + 0.3;
+       js->jiggly_color[2] = ((float)random()) / REAL_RAND_MAX * 0.7 + 0.3;
+       js->jiggly_color[3] = 1.0f;
+       js->color_dir[0] = ((float)random()) / REAL_RAND_MAX / 100.0;
+       js->color_dir[1] = ((float)random()) / REAL_RAND_MAX / 100.0;
+       js->color_dir[2] = ((float)random()) / REAL_RAND_MAX / 100.0;
+       return 1;
+    }
+    else
+       js->color_style = 0;
+    if(strlen(color) != 7)
+       return 0;
+    if(sscanf(color,"#%02x%02x%02x", &r, &g, &b) != 3) {
+       return 0;
+    }
+    js->jiggly_color[0] = ((float)r)/255;
+    js->jiggly_color[1] = ((float)g)/255;
+    js->jiggly_color[2] = ((float)b)/255;
+    js->jiggly_color[3] = 1.0f;
 
-    glLightfv(GL_LIGHT0, GL_POSITION, lpos0);
-    glLightfv(GL_LIGHT1, GL_POSITION, lpos1);
-    glLightfv(GL_LIGHT0, GL_DIFFUSE, lcol0);
-    glLightfv(GL_LIGHT1, GL_DIFFUSE, lcol1);
+    return 1;
+}
 
-    glClearColor(0, 0, 0, 0);
+void randomize_parameters(jigglystruct *js) {
+    do_tetrahedron = random() & 1;
+    js->do_wireframe = !(random() & 3);
+    js->color_style = random() % 5;
+    if(js->color_style == COLOR_STYLE_NORMAL
+       || js->color_style == COLOR_STYLE_CYCLE) {
+       js->jiggly_color[0] = ((float)random()) / REAL_RAND_MAX * 0.5 + 0.5;
+       js->jiggly_color[1] = ((float)random()) / REAL_RAND_MAX * 0.5 + 0.5;
+       js->jiggly_color[2] = ((float)random()) / REAL_RAND_MAX * 0.5 + 0.5;
+       js->jiggly_color[3] = 1.0f;
+       if(js->color_style == COLOR_STYLE_CYCLE) {
+           js->color_dir[0] = ((float)random()) / REAL_RAND_MAX / 100.0;
+           js->color_dir[1] = ((float)random()) / REAL_RAND_MAX / 100.0;
+           js->color_dir[2] = ((float)random()) / REAL_RAND_MAX / 100.0;
+       }
+    }
+    if((js->color_style != COLOR_STYLE_CHROME) && (random() & 1))
+       js->spooky = (random() % 6) + 4;
+    else 
+       js->spooky = 0;
+    js->shininess = random() % 200;
+    speed = (random() % 700) + 50;
+    /* It' kind of dull if this is too high when it starts as a sphere */
+    spherism = do_tetrahedron ? (random() % 500) + 20 : (random() % 100) + 10;
+    hold = (random() % 800) + 100;
+    distance = (random() % 500) + 100;
+    damping = (random() % 800) + 50;
+}    
+
+void calculate_parameters(jigglystruct *js, int subdivs) {
+    /* try to compensate for the inherent instability at
+     * low complexity. */
+    float dist_factor = (subdivs == 6) ? 2 : (subdivs == 5) ? 1 : 0.5;
+
+    js->stable_distance = ((float)distance / 500.0) 
+       * (STABLE_DISTANCE / dist_factor);
+    js->hold_strength = (float)hold / 10000.0;
+    js->spherify_strength = (float)spherism / 10000.0;
+    js->damping_velocity = (float)damping / 100000.0;
+    js->damping_factor = 
+       0.001/max(js->hold_strength, js->spherify_strength);
+
+    js->speed = (float)speed / 1000.0;
+}
+
+/* The screenhack related functions begin here */
+
+Bool jigglypuff_handle_event(ModeInfo *mi, XEvent *event)
+{
+    jigglystruct *js = &jss[MI_SCREEN(mi)];
+    
+    if(event->xany.type == ButtonPress &&
+       event->xbutton.button & Button1) {
+       js->button_down = 1;
+       gltrackball_start(js->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) {
+       js->button_down = 0;
+       return True;
+    }
+    else if(event->xany.type == MotionNotify && js->button_down) {
+       gltrackball_track(js->trackball, event->xmotion.x, event->xmotion.y,
+                         MI_WIDTH(mi), MI_HEIGHT(mi));
+       return True;
+    }
+    return False;
+}
+
+void reshape_jigglypuff(ModeInfo *mi, int width, int height)
+{
+    GLfloat aspect = (GLfloat)width / (GLfloat)height;
+
+    glViewport(0, 0, width, height);
+    glMatrixMode(GL_PROJECTION);
+    glLoadIdentity();
+    glFrustum(-0.5*aspect, 0.5*aspect, -0.5, 0.5, 1, 20);
+/*    glTranslatef(0, 0, -10);*/
+}
+
+void draw_jigglypuff(ModeInfo *mi)
+{
+    jigglystruct *js = &jss[MI_SCREEN(mi)];
+    
+    glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(js->glx_context));
+    
+    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+    glMatrixMode(GL_MODELVIEW);
+    glLoadIdentity();
+    glTranslatef(0,0,-10);
 
-    glMaterialfv(GL_FRONT, GL_DIFFUSE, color1);
-    glMaterialfv(GL_FRONT, GL_SPECULAR, color2);
-    glMateriali(GL_FRONT, GL_SHININESS, 100);
+    glRotatef(js->angle, sin(js->axis), cos(js->axis), -sin(js->axis));
+    glTranslatef(0, 0, 5);
+    if(!(js->button_down)) {
+       if((js->angle += js->speed) >= 360.0f ) {
+           js->angle -= 360.0f;
+       }
+       if((js->axis+=0.01f) >= 2*M_PI ) {
+           js->axis -= 2*M_PI;
+       }
+    }
+    gltrackball_rotate(js->trackball);
+
+    if(js->color_style == COLOR_STYLE_CYCLE) {
+       int i;
+       vector_add(js->jiggly_color, js->color_dir, js->jiggly_color);
+       
+       for(i=0; i<3; i++) {
+           if(js->jiggly_color[i] > 1.0 || js->jiggly_color[i] < 0.3) {
+               js->color_dir[i] = (-js->color_dir[i]);
+               js->jiggly_color[i] += js->color_dir[i];
+           }
+       }
+       glColor4fv(js->jiggly_color);
+    }
+    
+    jigglypuff_render(js);
+    if(MI_IS_FPS(mi))
+       do_fps(mi);
+    glFinish();
+    update_shape(js);
+    glXSwapBuffers(MI_DISPLAY(mi), MI_WINDOW(mi));
 }
 
 void init_jigglypuff(ModeInfo *mi)
 {
     jigglystruct *js;
+    int subdivs;
 
     if(!jss) {
        jss = (jigglystruct*)
@@ -752,20 +1006,59 @@ void init_jigglypuff(ModeInfo *mi)
            exit(1);
        }
     }
+
     js = &jss[MI_SCREEN(mi)];
+
     js->do_wireframe = MI_IS_WIREFRAME(mi);
+
+    js->shininess = shininess;
+
+    subdivs = (complexity==1) ? 4 : (complexity==2) ? 5
+       : (complexity==3) ? 6 : 5;
+
+    js->spooky = spooky << (subdivs-3);
+
+    if(!parse_color(js)) {
+       fprintf(stderr, "%s: Bad color specification: '%s'.\n", progname, color);
+       exit(-1);
+    }
     
     if(random_parms)
-       randomize_parameters();
+       randomize_parameters(js);
+
+    js->shape = tesselated_tetrahedron(1, subdivs, js);
+
+    if(!do_tetrahedron)
+       solid_spherify(js->shape, 1);
+
+    if(js->color_style == COLOR_STYLE_CLOWNBARF)
+       clownbarf_colorize(js->shape);
+
+    calculate_parameters(js, subdivs);
+
     if((js->glx_context = init_GL(mi)) != NULL) {
        glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(js->glx_context));
        setup_opengl(mi, js);
+       reshape_jigglypuff(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
     }
-    js->shape = tesselated_tetrahedron(1, 5);
-    if(!do_tetrahedron)
-       solid_spherify(js->shape, 1);
-    calculate_parameters(js);
-    js->do_spooky = do_spooky;
+    else {
+       MI_CLEARWINDOW(mi);
+    }
+    js->trackball = gltrackball_init();
+    _DEBUG("distance : %f\nhold : %f\nspherify : %f\ndamping : %f\ndfact : %f\n",
+          js->stable_distance, js->hold_strength, js->spherify_strength,
+          js->damping_velocity, js->damping_factor);
+    _DEBUG("wire : %d\nspooky : %d\nstyle : %d\nshininess : %d\n",
+          js->do_wireframe, js->spooky, js->color_style, js->shininess);
 }
 
+/* This is the end of the file */
+
 #endif /* USE_GL */
+
+
+
+
+
+
+