From ae69a754244c4e475c8d2591772ca8e005071d83 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Mon, 18 Feb 2019 11:55:41 +0100 Subject: Update to 5.42 --- hacks/glx/handsy.c | 1175 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1175 insertions(+) create mode 100644 hacks/glx/handsy.c (limited to 'hacks/glx/handsy.c') 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 + * + * 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 + +#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 */ -- cgit v1.2.3-55-g7522