summaryrefslogtreecommitdiffstats
path: root/hacks/glx/handsy.c
diff options
context:
space:
mode:
Diffstat (limited to 'hacks/glx/handsy.c')
-rw-r--r--hacks/glx/handsy.c1175
1 files changed, 1175 insertions, 0 deletions
diff --git a/hacks/glx/handsy.c b/hacks/glx/handsy.c
new file mode 100644
index 0000000..077df76
--- /dev/null
+++ b/hacks/glx/handsy.c
@@ -0,0 +1,1175 @@
+/* handsy, Copyright (c) 2018 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+#define DEFAULTS "*delay: 30000 \n" \
+ "*count: 2 \n" \
+ ".foreground: #8888CC" "\n" \
+ "*groundColor: #0000FF" "\n" \
+ "*showFPS: False \n" \
+ "*wireframe: False \n"
+
+# define release_hands 0
+#undef countof
+#define countof(x) (sizeof((x))/sizeof((*x)))
+
+#include "xlockmore.h"
+#include "sphere.h"
+#include "tube.h"
+#include "rotator.h"
+#include "gltrackball.h"
+#include "gllist.h"
+#include <ctype.h>
+
+#ifdef USE_GL /* whole file */
+
+
+#define DEF_SPEED "1.0"
+#define DEF_SPIN "XY"
+#define DEF_WANDER "True"
+#define DEF_FACE_FRONT "True"
+#define DEF_DEBUG "False"
+
+extern const struct gllist
+ *handsy_model_finger_distal, *handsy_model_finger_intermediate,
+ *handsy_model_finger_proximal, *handsy_model_finger_metacarpal,
+ *handsy_model_thumb_distal, *handsy_model_thumb_proximal,
+ *handsy_model_thumb_metacarpal, *handsy_model_palm;
+static struct gllist *ground = 0;
+
+static const struct gllist * const *all_objs[] = {
+ &handsy_model_finger_distal, &handsy_model_finger_intermediate,
+ &handsy_model_finger_proximal, &handsy_model_finger_metacarpal,
+ &handsy_model_thumb_distal, &handsy_model_thumb_proximal,
+ &handsy_model_thumb_metacarpal, &handsy_model_palm,
+ (const struct gllist * const *) &ground
+};
+
+#define FINGER_DISTAL 0
+#define FINGER_INTERMEDIATE 1
+#define FINGER_PROXIMAL 2
+#define FINGER_METACARPAL 3
+#define THUMB_DISTAL 4
+#define THUMB_PROXIMAL 5
+#define THUMB_METACARPAL 6
+#define PALM 7
+#define GROUND 8
+
+
+/* 'hand_geom' describes the position and extent of the various joints.
+ 'hand' describes the current flexion of the joints in that model.
+ 'hand_anim' is a list of positions and timings describing an animation.
+ 'hands_configuration' is the usual global state structure.
+ */
+
+typedef struct {
+ double min, max; /* +- pi */
+} joint;
+
+typedef struct {
+ joint bones[4];
+ joint base;
+} finger;
+
+typedef struct {
+ finger fingers[5];
+ joint palm;
+ joint wrist1;
+ joint wrist2;
+} hand_geom;
+
+static const hand_geom human_hand = {
+ {{{{ 0.0, 1.6 }, /* thumb distal */
+ { 0.0, 1.6 }, /* thumb proximal */
+ { 0.0, 1.6 }, /* thumb metacarpal */
+ { 0.0, 0.0 }}, /* none */
+ { -1.70, 0.00 }},
+ {{{ -0.2, 1.6 }, /* index distal */
+ { -0.2, 1.6 }, /* index intermediate */
+ { -0.2, 1.6 }, /* index proximal */
+ { 0.0, 0.0 }}, /* index metacarpal */
+ { -0.25, 0.25 }},
+ {{{ -0.2, 1.6 }, /* middle distal */
+ { -0.2, 1.6 }, /* middle intermediate */
+ { -0.2, 1.6 }, /* middle proximal */
+ { 0.0, 0.0 }}, /* middle metacarpal */
+ { -0.25, 0.25 }},
+ {{{ -0.2, 1.6 }, /* ring distal */
+ { -0.2, 1.6 }, /* ring intermediate */
+ { -0.2, 1.6 }, /* ring proximal */
+ { 0.0, 0.0 }}, /* ring metacarpal */
+ { -0.25, 0.25 }},
+ {{{ -0.2, 1.6 }, /* pinky distal */
+ { -0.2, 1.6 }, /* pinky intermediate */
+ { -0.2, 1.6 }, /* pinky proximal */
+ { 0.0, 0.0 }}, /* pinky metacarpal */
+ { -0.25, 0.25 }}},
+ { -0.7, 1.5 }, /* palm (wrist up/down) */
+ { -M_PI, M_PI }, /* wrist left/right */
+ { -M_PI, M_PI }, /* wrist rotate */
+};
+
+typedef struct {
+ double joint[countof(human_hand.fingers)] /* +- pi */
+ [countof(human_hand.fingers[0].bones)];
+ double base[countof(human_hand.fingers)];
+ double wrist[3]; /* up/down, left/right, rotate */
+ double pos[3]; /* XYZ */
+ Bool sinister;
+ double alpha;
+} hand;
+
+typedef struct {
+ const hand * const dest;
+ double duration, pause;
+ double pos[3], rot[3]; /* XYZ */
+} hand_anim;
+
+typedef struct {
+ const hand_anim * const pair[2]; /* L/R */
+ double delay; /* Delay added to R */
+} hand_anim_pair;
+
+typedef struct {
+ GLXContext *glx_context;
+ rotator *rot, *rot2;
+ trackball_state *trackball;
+ Bool spinx, spiny, spinz;
+ Bool button_down_p;
+
+ const hand_geom *geom;
+ GLuint *dlists;
+ int nhands;
+
+ struct {
+ const hand_anim *anim;
+ int anim_hands; /* frames in animation */
+ int anim_hand; /* pos in anim, L/R */
+ double anim_start, anim_time;
+ double tick;
+ double delay;
+ } pair[2];
+
+ struct { hand from, to, current; } *hands;
+ GLfloat color[4];
+ Bool ringp;
+
+} hands_configuration;
+
+#include "handsy_anim.h"
+
+
+static hands_configuration *bps = NULL;
+
+static GLfloat speed;
+static char *do_spin;
+static Bool do_wander;
+static Bool face_front_p;
+static Bool debug_p;
+
+static XrmOptionDescRec opts[] = {
+ { "-speed", ".speed", XrmoptionSepArg, 0 },
+ { "-spin", ".spin", XrmoptionSepArg, 0 },
+ { "+spin", ".spin", XrmoptionNoArg, "" },
+ { "-wander", ".wander", XrmoptionNoArg, "True" },
+ { "+wander", ".wander", XrmoptionNoArg, "False" },
+ { "-front", ".faceFront", XrmoptionNoArg, "True" },
+ { "+front", ".faceFront", XrmoptionNoArg, "False" },
+ { "-debug", ".debug", XrmoptionNoArg, "True" },
+ { "+debug", ".debug", XrmoptionNoArg, "False" },
+};
+
+static argtype vars[] = {
+ {&do_spin, "spin", "Spin", DEF_SPIN, t_String},
+ {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
+ {&face_front_p, "faceFront", "FaceFront", DEF_FACE_FRONT, t_Bool},
+ {&speed, "speed", "Speed", DEF_SPEED, t_Float},
+ {&debug_p, "debug", "Debug", DEF_DEBUG, t_Bool},
+};
+
+ENTRYPOINT ModeSpecOpt hands_opts = {countof(opts), opts, countof(vars), vars, NULL};
+
+
+/* Returns the current time in seconds as a double.
+ */
+static double
+double_time (void)
+{
+ struct timeval now;
+# ifdef GETTIMEOFDAY_TWO_ARGS
+ struct timezone tzp;
+ gettimeofday(&now, &tzp);
+# else
+ gettimeofday(&now);
+# endif
+
+ return (now.tv_sec + ((double) now.tv_usec * 0.000001));
+}
+
+
+static double
+constrain_joint (double v, double min, double max)
+{
+ if (v < min) v = min;
+ else if (v > max) v = max;
+ return v;
+}
+
+
+static void
+draw_hand (ModeInfo *mi, hand *h)
+{
+ hands_configuration *bp = &bps[MI_SCREEN(mi)];
+ int wire = MI_IS_WIREFRAME(mi);
+ int finger;
+ int off = h->sinister ? -1 : 1;
+ int nfingers = countof (bp->geom->fingers);
+ int nbones = countof (bp->geom->fingers[0].bones);
+
+ glLineWidth (1);
+ glPushMatrix();
+
+ glTranslatef (off * h->pos[0], h->pos[1], h->pos[2]);
+ glRotatef (h->wrist[1] * 180 / M_PI * -off, 0, 1, 0);
+ glRotatef (h->wrist[2] * 180 / M_PI * -off, 0, 0, 1);
+ glRotatef (h->wrist[0] * 180 / M_PI, 1, 0, 0);
+
+ bp->color[3] = h->alpha;
+ glColor4fv (bp->color);
+ glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, bp->color);
+
+ if (!wire) glEnable (GL_BLEND);
+
+ glPushMatrix();
+ if (h->sinister)
+ {
+ glScalef (-1, 1, 1);
+ glFrontFace (GL_CW);
+ }
+ else
+ glFrontFace (GL_CCW);
+ glCallList (bp->dlists[PALM]);
+ glPopMatrix();
+
+ for (finger = 0; finger < nfingers; finger++)
+ {
+ int bone = nbones - 2;
+ glPushMatrix();
+ if (finger == 0) /* thumb */
+ {
+ glTranslatef (off * 0.113, -0.033, 0.093);
+ glRotatef (off * 45, 0, 1, 0);
+
+ if (h->sinister)
+ glRotatef (180, 0, 0, 1);
+
+ glRotatef (off * h->base[finger] * -180 / M_PI, 1, 0, 0);
+ bone--;
+
+ glFrontFace (GL_CCW);
+ glCallList (bp->dlists[THUMB_METACARPAL]);
+ glPushMatrix();
+ glScalef (1, -1, 1);
+ glFrontFace (GL_CW);
+ glCallList (bp->dlists[THUMB_METACARPAL]);
+ glPopMatrix();
+
+ glTranslatef (0, 0, 0.1497);
+ glRotatef (h->joint[finger][bone] * -180 / M_PI, 0, 1, 0);
+ bone--;
+
+ glFrontFace (GL_CCW);
+ glCallList (bp->dlists[THUMB_PROXIMAL]);
+ glPushMatrix();
+ glScalef (1, -1, 1);
+ glFrontFace (GL_CW);
+ glCallList (bp->dlists[THUMB_PROXIMAL]);
+ glPopMatrix();
+
+ glTranslatef (0, 0, 0.1212);
+ glRotatef (h->joint[finger][bone] * -180 / M_PI, 0, 1, 0);
+ bone--;
+
+ glFrontFace (GL_CCW);
+ glCallList (bp->dlists[THUMB_DISTAL]);
+ glPushMatrix();
+ glScalef (1, -1, 1);
+ glFrontFace (GL_CW);
+ glCallList (bp->dlists[THUMB_DISTAL]);
+ glPopMatrix();
+ }
+ else
+ {
+ switch (finger) {
+ case 1: /* index */
+ glTranslatef (off * 0.135, 0.004, 0.26835);
+ glRotatef (off * 4, 0, 1, 0);
+ break;
+ case 2: /* middle */
+ glTranslatef (off * 0.046, 0.004, 0.27152);
+ glRotatef (off * 1, 0, 1, 0);
+ break;
+ case 3: /* ring */
+ glTranslatef (off * -0.046, 0.004, 0.25577);
+ glRotatef (off * -1, 0, 1, 0);
+ break;
+ case 4: /* pinky */
+ glTranslatef (off * -0.135, 0.004, 0.22204);
+ glRotatef (off * -4, 0, 1, 0);
+ break;
+ default: abort(); break;
+ }
+
+ glRotatef (90, 0, 0, 1);
+
+ glFrontFace (GL_CCW);
+ glCallList (bp->dlists[FINGER_METACARPAL]);
+ glPushMatrix();
+ glScalef (1, -1, 1);
+ glFrontFace (GL_CW);
+ glCallList (bp->dlists[FINGER_METACARPAL]);
+ glPopMatrix();
+
+ glTranslatef (0, 0, 0.1155);
+ glRotatef (off * h->base[finger] * -180 / M_PI, 1, 0, 0);
+ glRotatef (h->joint[finger][bone] * -180 / M_PI, 0, 1, 0);
+ bone--;
+
+ glFrontFace (GL_CCW);
+ glCallList (bp->dlists[FINGER_PROXIMAL]);
+ glPushMatrix();
+ glScalef (1, -1, 1);
+ glFrontFace (GL_CW);
+ glCallList (bp->dlists[FINGER_PROXIMAL]);
+ glPopMatrix();
+
+ glTranslatef (0, 0, 0.1815);
+ glRotatef (h->joint[finger][bone] * -180 / M_PI, 0, 1, 0);
+ bone--;
+
+ glFrontFace (GL_CCW);
+ glCallList (bp->dlists[FINGER_INTERMEDIATE]);
+ glPushMatrix();
+ glScalef (1, -1, 1);
+ glFrontFace (GL_CW);
+ glCallList (bp->dlists[FINGER_INTERMEDIATE]);
+ glPopMatrix();
+
+ glTranslatef (0, 0, 0.1003);
+ glRotatef (h->joint[finger][bone] * -180 / M_PI, 0, 1, 0);
+ bone--;
+
+ glFrontFace (GL_CCW);
+ glCallList (bp->dlists[FINGER_DISTAL]);
+ glPushMatrix();
+ glScalef (1, -1, 1);
+ glFrontFace (GL_CW);
+ glCallList (bp->dlists[FINGER_DISTAL]);
+ glPopMatrix();
+ }
+ glPopMatrix();
+ }
+ glPopMatrix();
+
+ if (h->sinister && bp->ringp)
+ {
+ GLfloat color[] = { 1.0, 0.4, 0.4, 1 };
+ GLfloat center = 0.4;
+ GLfloat th;
+ GLfloat r = center - h->pos[0] + 0.1;
+ GLfloat min = 0.22;
+ if (r < min) r = min;
+ glPushMatrix();
+ glTranslatef (-center, -0.28, 0.5);
+ glRotatef (h->wrist[2] * 180 / M_PI * -off, 0, 0, 1);
+ glRotatef (h->wrist[0] * 180 / M_PI, 1, 0, 0);
+
+ glColor4fv (color);
+ glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
+ glDisable (GL_LIGHTING);
+ glLineWidth (8);
+ glBegin (GL_LINE_LOOP);
+ for (th = 0; th < M_PI * 2; th += M_PI / 180)
+ glVertex3f (r * cos(th), r * sin(th), 0);
+ glEnd();
+ if (! wire) glEnable (GL_LIGHTING);
+ glPopMatrix();
+ }
+
+ glDisable (GL_BLEND);
+}
+
+
+static void
+parse_color (ModeInfo *mi, char *key, GLfloat color[4])
+{
+ XColor xcolor;
+ char *string = get_string_resource (mi->dpy, key, "Color");
+ if (!XParseColor (mi->dpy, mi->xgwa.colormap, string, &xcolor))
+ {
+ fprintf (stderr, "%s: unparsable color in %s: %s\n", progname,
+ key, string);
+ exit (1);
+ }
+ free (string);
+
+ color[0] = xcolor.red / 65536.0;
+ color[1] = xcolor.green / 65536.0;
+ color[2] = xcolor.blue / 65536.0;
+ color[3] = 1;
+}
+
+
+static int
+draw_ground (ModeInfo *mi)
+{
+ int wire = MI_IS_WIREFRAME(mi);
+ GLfloat i, j, k;
+
+ /* When using fog, iOS apparently doesn't like lines or quads that are
+ really long, and extend very far outside of the scene. Maybe? If the
+ length of the line (cells * cell_size) is greater than 25 or so, lines
+ that are oriented steeply away from the viewer tend to disappear
+ (whether implemented as GL_LINES or as GL_QUADS).
+
+ So we do a bunch of smaller grids instead of one big one.
+ */
+ int cells = 30;
+ GLfloat cell_size = 0.8;
+ int points = 0;
+ int grids = 12;
+ GLfloat color[4];
+
+ if (wire) glLineWidth (1);
+
+ parse_color (mi, "groundColor", color);
+
+ glPushMatrix();
+
+ glScalef (0.2, 0.2, 0.2);
+
+ glRotatef (frand(90), 0, 0, 1);
+
+ if (!wire)
+ {
+ GLfloat fog_color[4] = { 0, 0, 0, 1 };
+
+ glLineWidth (4);
+ glEnable (GL_LINE_SMOOTH);
+ glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
+ glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable (GL_BLEND);
+
+ glFogi (GL_FOG_MODE, GL_EXP2);
+ glFogfv (GL_FOG_COLOR, fog_color);
+ glFogf (GL_FOG_DENSITY, 0.015);
+ glFogf (GL_FOG_START, -cells/2 * cell_size * grids);
+ glEnable (GL_FOG);
+ }
+
+ glColor4fv (color);
+ glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
+
+ glTranslatef (-cells * grids * cell_size / 2,
+ -cells * grids * cell_size / 2, 0);
+
+ for (j = 0; j < grids; j++)
+ {
+ glPushMatrix();
+ for (k = 0; k < grids; k++)
+ {
+ glBegin (GL_LINES);
+ for (i = -cells/2; i < cells/2; i++)
+ {
+ GLfloat a = i * cell_size;
+ GLfloat b = cells/2 * cell_size;
+ glVertex3f (a, -b, 0); glVertex3f (a, b, 0); points++;
+ glVertex3f (-b, a, 0); glVertex3f (b, a, 0); points++;
+ }
+ glEnd();
+ glTranslatef (cells * cell_size, 0, 0);
+ }
+ glPopMatrix();
+ glTranslatef (0, cells * cell_size, 0);
+ }
+
+ if (!wire)
+ {
+ glDisable (GL_LINE_SMOOTH);
+ glDisable (GL_BLEND);
+ glDisable (GL_FOG);
+ }
+
+ glPopMatrix();
+
+ return points;
+}
+
+
+ENTRYPOINT void
+reshape_hands (ModeInfo *mi, int width, int height)
+{
+ GLfloat h = (GLfloat) height / (GLfloat) width;
+ int y = 0;
+
+ glViewport (0, y, (GLint) width, (GLint) height);
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ gluPerspective (30.0, 1/h, 1.0, 100.0);
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ gluLookAt( 0.0, 0.0, 30.0,
+ 0.0, 0.0, 0.0,
+ 0.0, 1.0, 0.0);
+
+ glClear(GL_COLOR_BUFFER_BIT);
+}
+
+
+ENTRYPOINT Bool
+hands_handle_event (ModeInfo *mi, XEvent *event)
+{
+ hands_configuration *bp = &bps[MI_SCREEN(mi)];
+ Bool ok = False;
+
+ if (gltrackball_event_handler (event, bp->trackball,
+ MI_WIDTH (mi), MI_HEIGHT (mi),
+ &bp->button_down_p))
+ ok = True;
+ else if (debug_p && event->type == KeyPress)
+ {
+ int nfingers = countof (bp->geom->fingers);
+ int nbones = countof (bp->geom->fingers[0].bones);
+ KeySym keysym;
+ char c = 0;
+ Bool tiltp = False;
+ double delta = 0.02;
+ double sign = 1;
+ int i, j, k;
+
+ XLookupString (&event->xkey, &c, 1, &keysym, 0);
+
+ for (i = 0; i < bp->nhands; i++)
+ {
+ hand *h = &bp->hands[i].current;
+ Bool modp = !!event->xkey.state;
+
+ switch (keysym) {
+ case XK_Up: ok = True; h->pos[(modp ? 1 : 2)] += delta; break;
+ case XK_Down: ok = True; h->pos[(modp ? 1 : 2)] -= delta; break;
+ case XK_Right: ok = True; h->pos[0] += delta; break;
+ case XK_Left: ok = True; h->pos[0] -= delta; break;
+ default: break;
+ }
+
+ switch (c) {
+ case '?': case '/':
+ ok = True;
+ fprintf (stderr, "\n"
+ " Open fingers: 12345 (1 = pinky, 5 = thumb)\n"
+ " Close fingers: QWERT\n"
+ " Tilt left: ASDFG\n"
+ " Tilt right: ZXCVB\n"
+ " Bend wrist: UJ (up/down)\n"
+ " Bend wrist: IK (left/right)\n"
+ " Rotate wrist: OL\n"
+ " Move origin: arrow keys: XZ plane; shift: XY plane\n"
+ " Tab: print current state to stdout\n"
+ " 0: Reset\n"
+ " ?: This\n\n");
+ return ok;
+ break;
+
+
+ case '1': case '!': j = 4; sign = -1; goto FINGER;
+ case '2': case '@': j = 3; sign = -1; goto FINGER;
+ case '3': case '#': j = 2; sign = -1; goto FINGER;
+ case '4': case '$': j = 1; sign = -1; goto FINGER;
+ case '5': case '%': j = 0; sign = -1; goto FINGER;
+
+ case 'q': case 'Q': j = 4; sign = 1; goto FINGER;
+ case 'w': case 'W': j = 3; sign = 1; goto FINGER;
+ case 'e': case 'E': j = 2; sign = 1; goto FINGER;
+ case 'r': case 'R': j = 1; sign = 1; goto FINGER;
+ case 't': case 'T': j = 0; sign = 1; goto FINGER;
+
+ case 'a': case 'A': tiltp = True; j = 4; sign = 1; goto FINGER;
+ case 's': case 'S': tiltp = True; j = 3; sign = 1; goto FINGER;
+ case 'd': case 'D': tiltp = True; j = 2; sign = 1; goto FINGER;
+ case 'f': case 'F': tiltp = True; j = 1; sign = 1; goto FINGER;
+ case 'g': case 'G': tiltp = True; j = 0; sign = 1; goto FINGER;
+
+ case 'z': case 'Z': tiltp = True; j = 4; sign = -1; goto FINGER;
+ case 'x': case 'X': tiltp = True; j = 3; sign = -1; goto FINGER;
+ case 'c': case 'C': tiltp = True; j = 2; sign = -1; goto FINGER;
+ case 'v': case 'V': tiltp = True; j = 1; sign = -1; goto FINGER;
+ case 'b': case 'B': tiltp = True; j = 0; sign = -1; goto FINGER;
+ FINGER:
+ ok = True;
+ if (tiltp)
+ h->base[j] = constrain_joint (h->base[j] + sign * delta,
+ bp->geom->fingers[j].base.min,
+ bp->geom->fingers[j].base.max);
+ else
+ for (k = 0; k < nbones; k++)
+ h->joint[j][k] =
+ constrain_joint (h->joint[j][k] + sign * delta,
+ bp->geom->fingers[j].bones[k].min,
+ bp->geom->fingers[j].bones[k].max);
+ break;
+
+ case 'u': case 'U': ok = True; h->wrist[0] -= delta; break;
+ case 'j': case 'J': ok = True; h->wrist[0] += delta; break;
+ case 'i': case 'I': ok = True; h->wrist[1] += delta; break;
+ case 'k': case 'K': ok = True; h->wrist[1] -= delta; break;
+ case 'o': case 'O': ok = True; h->wrist[2] -= delta; break;
+ case 'l': case 'L': ok = True; h->wrist[2] += delta; break;
+
+ case '0': case ')':
+ ok = True;
+ for (j = 0; j < nfingers; j++)
+ {
+ h->base[j] = 0;
+ for (k = 0; k < nbones; k++)
+ h->joint[j][k] = 0;
+ }
+ for (j = 0; j < 3; j++)
+ h->wrist[j] = 0;
+ for (j = 0; j < 3; j++)
+ h->pos[j] = 0;
+ break;
+
+ case '\t':
+ ok = True;
+ fprintf (stdout, "\nstatic const hand H = {\n {");
+ for (i = 0; i < nfingers; i++)
+ {
+ if (i > 0) fprintf (stdout, " ");
+ fprintf (stdout, "{");
+ for (j = 0; j < nbones; j++)
+ {
+ double v = h->joint[i][j];
+ if (i == 0 && j == 3) v = 0; /* no thumb intermediate */
+ if (j == 0) fprintf (stdout, " ");
+ fprintf (stdout, "%.2f", v);
+ if (j < nbones-1) fprintf (stdout, ", ");
+ }
+ fprintf (stdout, " }");
+ if (i < nfingers-1) fprintf (stdout, ",\n");
+ }
+ fprintf (stdout, "},\n { ");
+ for (i = 0; i < nfingers; i++)
+ {
+ fprintf (stdout, "%.2f", h->base[i]);
+ if (i < nfingers-1) fprintf (stdout, ", ");
+ }
+ fprintf (stdout, " },\n");
+ fprintf (stdout, " { %.2f, %.2f, %.2f },\n",
+ h->wrist[0], h->wrist[1], h->wrist[2]);
+ fprintf (stdout, " { %.2f, %.2f, %.2f },\n",
+ h->pos[0], h->pos[1], h->pos[2]);
+ fprintf (stdout, " True\n};\n\n");
+ fflush (stdout);
+ return ok;
+ break;
+
+ default: break;
+ }
+ }
+ }
+ return ok;
+}
+
+
+ENTRYPOINT void
+init_hands (ModeInfo *mi)
+{
+ hands_configuration *bp;
+ int wire = MI_IS_WIREFRAME(mi);
+ hand def[2];
+ int i;
+
+ MI_INIT (mi, bps);
+ bp = &bps[MI_SCREEN(mi)];
+
+ bp->glx_context = init_GL(mi);
+
+ reshape_hands (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
+
+ bp->pair[0].tick = bp->pair[1].tick = 1.0;
+ bp->geom = &human_hand;
+ bp->nhands = MI_COUNT(mi);
+ if (bp->nhands <= 0) bp->nhands = 1;
+
+ if (bp->nhands & 1) bp->nhands++; /* Even number */
+
+ if (debug_p)
+ {
+ bp->nhands = 1;
+ do_spin = "";
+ do_wander = False;
+ }
+
+ bp->hands = calloc (bp->nhands, sizeof(*bp->hands));
+
+ {
+ double spin_speed = 0.5 * speed;
+ double wander_speed = 0.005 * speed;
+ double tilt_speed = 0.001 * speed;
+ double spin_accel = 0.5;
+
+ char *s = do_spin;
+ while (*s)
+ {
+ if (*s == 'x' || *s == 'X') bp->spinx = True;
+ else if (*s == 'y' || *s == 'Y') bp->spiny = True;
+ else if (*s == 'z' || *s == 'Z') bp->spinz = True;
+ else if (*s == '0') ;
+ else
+ {
+ fprintf (stderr,
+ "%s: spin must contain only the characters X, Y, or Z (not \"%s\")\n",
+ progname, do_spin);
+ exit (1);
+ }
+ s++;
+ }
+
+ bp->rot = make_rotator (bp->spinx ? spin_speed : 0,
+ bp->spiny ? spin_speed : 0,
+ bp->spinz ? spin_speed : 0,
+ spin_accel,
+ do_wander ? wander_speed : 0,
+ False);
+ bp->rot2 = (face_front_p
+ ? make_rotator (0, 0, 0, 0, tilt_speed, True)
+ : 0);
+ bp->trackball = gltrackball_init (False);
+ }
+
+ /* Set default hand to the last hand in the animation list. */
+ for (i = 0; i <= 1; i++)
+ {
+ int j;
+ for (j = 0; ; j++)
+ if (!all_hand_anims[j+1].pair[i])
+ {
+ if (! all_hand_anims[j].pair[i]) abort();
+ def[i] = *all_hand_anims[j].pair[i]->dest;
+ if (debug_p)
+ def[i].alpha = 1;
+ else
+ {
+ def[i].pos[1] = 5; /* off screen */
+ def[i].pos[2] = 5;
+ }
+ break;
+ }
+ }
+
+ for (i = 0; i < bp->nhands; i++)
+ {
+ int sinister = (i & 1);
+ bp->hands[i].to = def[sinister];
+ bp->hands[i].to.sinister = sinister;
+ bp->hands[i].from = bp->hands[i].to;
+ bp->hands[i].current = bp->hands[i].to;
+ }
+
+ glFrontFace(GL_CW);
+ bp->dlists = (GLuint *) calloc (countof(all_objs)+1, sizeof(GLuint));
+ for (i = 0; i < countof(all_objs); i++)
+ bp->dlists[i] = glGenLists (1);
+
+ for (i = 0; i < countof(all_objs); i++)
+ {
+ const struct gllist *gll = *all_objs[i];
+ GLfloat s = 0.1;
+ glNewList (bp->dlists[i], GL_COMPILE);
+ switch (i) {
+ case GROUND:
+ if (! ground)
+ ground = (struct gllist *) calloc (1, sizeof(*ground));
+ ground->points = draw_ground (mi);
+ break;
+ default:
+ glPushMatrix();
+ glScalef (s, s, s);
+ renderList (gll, wire);
+ glPopMatrix();
+ break;
+ }
+ glEndList ();
+ }
+
+ if (!wire)
+ {
+ GLfloat pos[4] = {0.4, 0.2, 0.4, 0.0};
+ GLfloat amb[4] = {0.2, 0.2, 0.2, 1.0};
+ GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
+ GLfloat spc[4] = {1.0, 1.0, 1.0, 1.0};
+
+ glEnable(GL_LIGHTING);
+ glEnable(GL_LIGHT0);
+ glEnable(GL_DEPTH_TEST);
+ glEnable(GL_CULL_FACE);
+
+ glLightfv(GL_LIGHT0, GL_POSITION, pos);
+ glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
+ glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
+ glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
+ }
+
+ parse_color (mi, "foreground", bp->color);
+}
+
+
+static void
+tick_hands (ModeInfo *mi)
+{
+ hands_configuration *bp = &bps[MI_SCREEN(mi)];
+ int i, j, k, nfingers, nbones, sinister;
+ double now = double_time();
+
+ if (debug_p) return;
+
+ if (!bp->pair[0].anim && /* Both hands finished. */
+ !bp->pair[1].anim) /* Pick a new animation. */
+ {
+ int nanims = 0;
+ while (all_hand_anims[nanims].pair[0] ||
+ all_hand_anims[nanims].pair[1])
+ nanims++;
+ i = random() % nanims;
+ for (sinister = 0; sinister <= 1; sinister++)
+ {
+ bp->pair[sinister].anim = all_hand_anims[i].pair[sinister];
+ bp->pair[sinister].anim_hand = 0;
+ bp->pair[sinister].anim_hands = 0;
+ while (bp->pair[sinister].anim[bp->pair[sinister].anim_hands].dest)
+ bp->pair[sinister].anim_hands++;
+ bp->pair[sinister].anim_start = now;
+ bp->pair[sinister].tick = 0;
+
+ if (sinister == 1)
+ bp->pair[sinister].delay = all_hand_anims[i].delay;
+ }
+
+ bp->ringp = (all_hand_anims[i].pair[0] == goatse_anim);
+
+ for (i = 0; i < bp->nhands; i++)
+ {
+ sinister = bp->hands[i].from.sinister;
+ bp->hands[i].from = bp->hands[i].current;
+ bp->hands[i].to = *bp->pair[sinister].anim->dest;
+ bp->hands[i].to.sinister = sinister;
+
+ j = bp->pair[sinister].anim_hand;
+
+ bp->hands[i].to.alpha =
+ (bp->pair[sinister].anim == hidden_anim ? 0 : 1);
+
+ /* Anim keyframes can adjust position and rotation */
+ for (k = 0; k < 3; k++)
+ {
+ bp->hands[i].to.wrist[k] +=
+ bp->pair[sinister].anim[j].rot[k];
+ bp->hands[i].to.pos[k] +=
+ bp->pair[sinister].anim[j].pos[k];
+ }
+ }
+ }
+
+ for (sinister = 0; sinister <= 1; sinister++)
+ {
+ const hand_anim *h;
+ double elapsed, duration, duration2;
+
+ if (! bp->pair[sinister].anim) /* Done with this hand, not the other. */
+ continue;
+
+ h = &bp->pair[sinister].anim[bp->pair[sinister].anim_hand];
+
+ elapsed = now - bp->pair[sinister].anim_start;
+ duration = h->duration / speed;
+ duration2 = duration + (bp->pair[sinister].delay + h->pause) / speed;
+
+ if (elapsed > duration2 && /* Done animating and pausing this hand. */
+ bp->pair[sinister].tick >= 1) /* ...and painted final frame. */
+ {
+ bp->pair[sinister].anim_hand++;
+ bp->pair[sinister].tick = 1;
+ if (bp->pair[sinister].anim_hand >= bp->pair[sinister].anim_hands)
+ {
+ /* Done with all steps of this hand's animation. */
+ bp->pair[sinister].anim = 0;
+ for (i = 0; i < bp->nhands; i++)
+ if (bp->hands[i].from.sinister == sinister)
+ bp->hands[i].from = bp->hands[i].to = bp->hands[i].current;
+ }
+ else
+ {
+ /* Move to next step of animation. */
+ for (i = 0; i < bp->nhands; i++)
+ {
+ if (sinister != bp->hands[i].current.sinister)
+ continue;
+
+ j = bp->pair[sinister].anim_hand;
+ bp->hands[i].from = bp->hands[i].current;
+ bp->hands[i].to = *bp->pair[sinister].anim[j].dest;
+ bp->hands[i].to.alpha =
+ (bp->pair[sinister].anim == hidden_anim ? 0 : 1);
+
+ /* Anim keyframes can adjust position and rotation */
+ for (k = 0; k < 3; k++)
+ {
+ bp->hands[i].to.wrist[k] +=
+ bp->pair[sinister].anim[j].rot[k];
+ bp->hands[i].to.pos[k] +=
+ bp->pair[sinister].anim[j].pos[k];
+ }
+ }
+ bp->pair[sinister].anim_start = now;
+ bp->pair[sinister].tick = 0;
+ bp->pair[sinister].delay = 0;
+ }
+ }
+ else if (elapsed > duration) /* Done animating, still pausing. */
+ bp->pair[sinister].tick = 1;
+ else /* Still animating. */
+ bp->pair[sinister].tick = elapsed / duration;
+
+ if (bp->pair[sinister].tick > 1)
+ bp->pair[sinister].tick = 1;
+
+ /* Move the joints into position:
+ compute 'current' between 'from' and 'to' by ratio 'tick'. */
+
+ nfingers = countof (bp->geom->fingers);
+ nbones = countof (bp->geom->fingers[0].bones);
+ for (i = 0; i < bp->nhands; i++)
+ {
+ if (bp->hands[i].current.sinister != sinister)
+ continue;
+ for (j = 0; j < nfingers; j++)
+ {
+ for (k = 0; k < nbones; k++)
+ bp->hands[i].current.joint[j][k] =
+ constrain_joint (bp->hands[i].from.joint[j][k] +
+ bp->pair[sinister].tick
+ * (bp->hands[i].to.joint[j][k] -
+ bp->hands[i].from.joint[j][k]),
+ bp->geom->fingers[j].bones[k].min,
+ bp->geom->fingers[j].bones[k].max);
+ bp->hands[i].current.base[j] =
+ constrain_joint (bp->hands[i].from.base[j] +
+ bp->pair[sinister].tick
+ * (bp->hands[i].to.base[j] -
+ bp->hands[i].from.base[j]),
+ bp->geom->fingers[j].base.min,
+ bp->geom->fingers[j].base.max);
+ }
+ j = 0;
+ bp->hands[i].current.wrist[j] =
+ constrain_joint (bp->hands[i].from.wrist[j] +
+ bp->pair[sinister].tick
+ * (bp->hands[i].to.wrist[j] -
+ bp->hands[i].from.wrist[j]),
+ bp->geom->palm.min,
+ bp->geom->palm.max);
+ j = 1;
+ bp->hands[i].current.wrist[j] =
+ constrain_joint (bp->hands[i].from.wrist[j] +
+ bp->pair[sinister].tick
+ * (bp->hands[i].to.wrist[j] -
+ bp->hands[i].from.wrist[j]),
+ bp->geom->wrist1.min,
+ bp->geom->wrist1.max);
+ j = 2;
+ bp->hands[i].current.wrist[j] =
+ constrain_joint (bp->hands[i].from.wrist[j] +
+ bp->pair[sinister].tick
+ * (bp->hands[i].to.wrist[j] -
+ bp->hands[i].from.wrist[j]),
+ bp->geom->wrist2.min,
+ bp->geom->wrist2.max);
+ for (j = 0; j < 3; j++)
+ bp->hands[i].current.pos[j] =
+ constrain_joint (bp->hands[i].from.pos[j] +
+ bp->pair[sinister].tick
+ * (bp->hands[i].to.pos[j] -
+ bp->hands[i].from.pos[j]),
+ -999, 999);
+ bp->hands[i].current.alpha =
+ bp->hands[i].from.alpha +
+ bp->pair[sinister].tick *
+ (bp->hands[i].to.alpha - bp->hands[i].from.alpha);
+ }
+ }
+}
+
+
+ENTRYPOINT void
+draw_hands (ModeInfo *mi)
+{
+ hands_configuration *bp = &bps[MI_SCREEN(mi)];
+ Display *dpy = MI_DISPLAY(mi);
+ Window window = MI_WINDOW(mi);
+ GLfloat s;
+ int i;
+
+ static const GLfloat bspec[4] = {1.0, 1.0, 1.0, 1.0};
+ static const GLfloat bshiny = 128.0;
+ GLfloat bcolor[4] = { 0.7, 0.7, 1.0, 1.0 };
+
+ if (!bp->glx_context)
+ return;
+
+ glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *bp->glx_context);
+
+ glShadeModel(GL_SMOOTH);
+
+ glEnable(GL_DEPTH_TEST);
+ glEnable(GL_NORMALIZE);
+ glEnable(GL_CULL_FACE);
+
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ glPushMatrix ();
+
+ glRotatef(current_device_rotation(), 0, 0, 1);
+
+ s = 10;
+ glScalef (s, s, s);
+
+ {
+ double x, y, z;
+
+ gltrackball_rotate (bp->trackball);
+
+ if (face_front_p)
+ {
+ double maxx = 120 / 10.0;
+ double maxy = 55 / 10.0;
+ double maxz = 40 / 10.0;
+ get_position (bp->rot2, &x, &y, &z, !bp->button_down_p);
+ if (bp->spinx) glRotatef (maxx/2 - x*maxx, 0, 1, 0);
+ if (bp->spiny) glRotatef (maxy/2 - y*maxy, 1, 0, 0);
+ if (bp->spinz) glRotatef (maxz/2 - z*maxz, 0, 0, 1);
+ }
+ else
+ {
+ get_rotation (bp->rot, &x, &y, &z, !bp->button_down_p);
+ glRotatef (x * 360, 1, 0, 0);
+ glRotatef (y * 360, 0, 1, 0);
+ glRotatef (z * 360, 0, 0, 1);
+ }
+
+ glRotatef (-70, 1, 0, 0);
+
+ glTranslatef (0, 0, -0.5);
+
+ glPushMatrix();
+ glRotatef ((bp->spiny ? y : bp->spinx ? x : z) * 90, 0, 0, 1);
+ glCallList (bp->dlists[GROUND]);
+ glPopMatrix();
+
+ get_position (bp->rot, &x, &y, &z, !bp->button_down_p);
+ z += 1; /* Origin of hands is 1.0 above floor. */
+ glTranslatef((x - 0.5) * 0.8,
+ (y - 0.5) * 1.1,
+ (z - 0.5) * 0.2);
+ }
+
+ glMaterialfv (GL_FRONT, GL_SPECULAR, bspec);
+ glMateriali (GL_FRONT, GL_SHININESS, bshiny);
+ glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, bcolor);
+
+ {
+ /* Lay the pairs out in a square-ish grid, keeping pairs together. */
+ int rows = sqrt (bp->nhands / 2);
+ int cols;
+ int x, y;
+
+ cols = ceil (bp->nhands / 2.0 / rows);
+
+ if (bp->nhands <= 2)
+ rows = cols = 1;
+
+ if (MI_WIDTH(mi) < MI_HEIGHT(mi)) /* Portrait */
+ {
+ s = 0.5;
+ glScalef (s, s, s);
+ }
+
+ if (bp->nhands == 1)
+ glScalef (2, 2, 2);
+
+ if (cols > 1)
+ {
+ s = 1.0 / rows;
+ glScalef (s, s, s);
+ }
+
+ if (bp->nhands > 1)
+ {
+ s = 0.8;
+ glTranslatef (-s * rows * 1.5, -s * cols, 0);
+ glTranslatef (s, s, 0);
+ }
+
+ i = 0;
+ for (y = 0; y < cols; y++)
+ for (x = 0; x < rows; x++)
+ {
+ glPushMatrix();
+ glTranslatef (x * s * 3, y * s * 2, y * s);
+ if (i < bp->nhands)
+ draw_hand (mi, &bp->hands[i++].current);
+ glTranslatef (s, 0, 0);
+ if (i < bp->nhands)
+ draw_hand (mi, &bp->hands[i++].current);
+ glPopMatrix();
+ }
+ }
+ glPopMatrix();
+
+ tick_hands (mi);
+
+ if (mi->fps_p) do_fps (mi);
+ glFinish();
+
+ glXSwapBuffers(dpy, window);
+}
+
+
+ENTRYPOINT void
+free_hands (ModeInfo *mi)
+{
+ hands_configuration *bp = &bps[MI_SCREEN(mi)];
+ int i;
+ if (!bp->glx_context) return;
+ glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *bp->glx_context);
+ if (bp->rot) free_rotator (bp->rot);
+ if (bp->trackball) gltrackball_free (bp->trackball);
+
+ if (bp->dlists) {
+ for (i = 0; i < countof(all_objs); i++)
+ if (glIsList(bp->dlists[i])) glDeleteLists(bp->dlists[i], 1);
+ free (bp->dlists);
+ }
+}
+
+XSCREENSAVER_MODULE_2 ("Handsy", handsy, hands)
+
+#endif /* USE_GL */