//
import render.*;
public class Pollymorph
{
//----- DATA DEFINING ALL THE KEY ACTIONS
// BEATS PER SECOND FOR EACH ACTION
static double rate[] = {4, 6, 6, 6, 2, 2, 2, 10, 12, 8, 9, 6};
// AN ACTION HAS FOUR KEY POSES. EACH POSE HAS SIX (X,Y,Z) VERTICES.
static double actions[][] = {
{ //idle
0,1,0, 0,0,1., 0,1,1, 1,1,0, 1,0,0., 1,1,1,
0,1,0, 0,0,.5, 0,1,1, 1,1,0, 1,0,.5, 1,1,1,
0,1,0, 0,0,0., 0,1,1, 1,1,0, 1,0,1., 1,1,1,
0,1,0, 0,0,.5, 0,1,1, 1,1,0, 1,0,.5, 1,1,1,
},
{ //trot
0.,1.03,0.0, 0.1,0.0,1.0, 0.,1.03,1.0, 1.,1.17,0.0, 0.9,0.0,0.0, 1.,1.17,1.0,
0.,1.17,0.0, 0.0,0.2,0.5, 0.,1.17,1.0, 1.,1.03,0.0, 0.9,0.0,0.5, 1.,1.03,1.0,
0.,1.17,0.0, 0.1,0.0,0.0, 0.,1.17,1.0, 1.,1.03,0.0, 0.9,0.0,1.0, 1.,1.03,1.0,
0.,1.03,0.0, 0.1,0.0,0.5, 0.,1.03,1.0, 1.,1.17,0.0, 1.0,0.2,0.5, 1.,1.17,1.0,
},
{ //swagger
0.0,1.0,0.0, 0.1,0.0,1.0, 0.0,1.0,1.0, 1.0,1.0,-.1, 0.9,0.0,0.0, 1.0,0.9,1.0,
0.2,1.5,0.0, 0.0,0.5,0.3, 0.2,1.5,1.0, 1.2,1.0,0.0, 0.9,0.0,0.3, 1.2,1.0,1.0,
0.0,1.0,-.1, 0.1,0.0,0.0, 0.0,1.0,0.9, 1.0,1.0,0.0, 0.9,0.0,1.0, 1.0,1.0,1.0,
-.2,1.0,0.0, 0.1,0.0,0.3, -.2,1.0,1.0, 0.8,1.5,0.0, 1.0,0.5,0.3, 0.8,1.5,1.0,
},
{ //jump
0.0,0.7,0.1, 0.0,0.0,1.0, 0.0,0.7,1.0, 1.0,0.7,0.1, 1.0,0.0,1.0, 1.0,0.7,1.0,
0.0,1.8,0.0, 0.1,0.6,0.5, 0.0,1.8,1.0, 1.0,1.8,0.0, 0.9,0.6,0.5, 1.0,1.8,1.0,
0.0,1.6,0.0, 0.1,0.4,0.0, 0.0,1.6,1.0, 1.0,1.6,0.0, 0.9,0.4,0.0, 1.0,1.6,1.0,
0.0,1.0,0.0, 0.0,0.0,0.5, 0.0,1.0,1.0, 1.0,1.0,0.0, 1.0,0.0,0.5, 1.0,1.0,1.0,
},
{ //prowl
0.0,0.6,-.1, 0.2,0.0,1.0, 0.0,1.0,1.0, 1.0,0.8,-.1, 0.8,0.0,0.0, 1.0,1.0,0.8,
0.1,1.0,0.0, 0.0,0.3,0.2, 0.1,0.8,1.0, 1.1,1.0,0.0, 0.8,0.0,0.5, 1.1,0.8,1.0,
0.0,0.8,-.1, 0.2,0.0,0.0, 0.0,1.0,0.8, 1.0,0.6,-.1, 0.8,0.0,1.0, 1.0,1.0,1.0,
-.1,1.0,0.0, 0.2,0.0,0.5, -.1,0.8,1.0, 0.9,1.0,0.0, 1.0,0.3,0.2, 0.9,0.8,1.0,
},
{ //lumber
0.0,1.0,0.0, 0.1,0.0,1.0, 0.0,1.0,1.0, 1.0,1.0,-.1, 0.9,0.0,0.0, 1.0,0.9,1.0,
0.0,1.2,0.0, 0.0,0.1,0.5, 0.0,1.2,1.0, 1.0,1.0,0.0, 0.9,0.0,0.5, 1.0,1.0,1.0,
0.0,1.0,-.1, 0.1,0.0,0.0, 0.0,1.0,0.9, 1.0,1.0,0.0, 0.9,0.0,1.0, 1.0,1.0,1.0,
0.0,1.0,0.0, 0.1,0.0,0.5, 0.0,1.0,1.0, 1.0,1.2,0.0, 1.0,0.1,0.5, 1.0,1.2,1.0,
},
{ //mope
0.0,0.5,0.0, 0.1,0.0,1.0, 0.,1.00,1.0, 1.0,0.7,-.1, 0.9,0.0,0.0, 1.,0.95,1.0,
0.0,0.8,0.0, 0.0,0.1,0.5, 0.,1.05,1.0, 1.0,0.7,0.0, 0.9,0.0,0.5, 1.,1.00,1.0,
0.0,0.7,-.1, 0.1,0.0,0.0, 0.,1.00,0.9, 1.0,0.5,0.0, 0.9,0.0,1.0, 1.,1.00,1.0,
0.0,0.7,0.0, 0.1,0.0,0.5, 0.,0.95,1.0, 1.0,0.8,0.0, 1.0,0.1,0.5, 1.,1.05,1.0,
},
{ //no
0.1,0.9,-.1, 0.0,0.0,1.0, -.1,1.1,0.9, 1.1,0.9,0.1, 1.0,0.0,0.0, 0.9,1.1,1.1,
0.1,0.9,-.1, 0.0,0.0,0.5, -.1,1.1,0.9, 1.1,0.9,0.1, 1.0,0.0,0.5, 0.9,1.1,1.1,
-.1,0.9,0.1, 0.0,0.0,0.0, 0.1,1.1,1.1, 0.9,0.9,-.1, 1.0,0.0,1.0, 1.1,1.1,0.9,
-.1,0.9,0.1, 0.0,0.0,0.5, 0.1,1.1,1.1, 0.9,0.9,-.1, 1.0,0.0,0.5, 1.1,1.1,0.9,
},
{ //yes
0.,0.90,-.1, 0.0,0.0,1.0, 0.,1.05,1.0, 1.,0.90,-.1, 1.0,0.0,0.0, 1.,1.05,1.0,
0.,1.15,-.1, 0.0,0.0,0.5, 0.,0.93,1.0, 1.,1.15,-.1, 1.0,0.0,0.5, 1.,0.93,1.0,
0.,1.15,-.1, 0.0,0.0,0.0, 0.,0.93,1.0, 1.,1.15,-.1, 1.0,0.0,1.0, 1.,0.93,1.0,
0.,0.90,-.1, 0.0,0.0,0.5, 0.,1.05,1.0, 1.,0.90,-.1, 1.0,0.0,0.5, 1.,1.05,1.0,
},
{ //dance
0.0,1.2,0.1, 0.0,0.3,1.0, 0.0,1.2,1.0, 1.0,1.2,0.0, 1.0,0.3,0.0, 1.0,1.2,1.0,
0.1,1.1,0.0, 0.1,0.4,0.5, 0.1,1.1,1.0, 1.1,1.1,0.0, 1.0,0.0,0.5, 1.1,1.1,1.0,
0.0,1.2,0.0, 0.0,0.3,0.0, 0.0,1.2,1.0, 1.0,1.2,0.1, 1.0,0.3,1.0, 1.0,1.2,1.0,
-.1,1.1,-.1, 0.0,0.0,0.5, -.1,1.1,0.9, 0.9,1.1,-.1, 0.9,0.4,0.5, 0.9,1.1,0.9,
},
{ //run
0.0,1.1,0.1, 0.2,0.3,1.0, 0.0,1.2,1.0, 1.0,1.1,0.0, 0.8,0.3,0.0, 1.0,1.2,1.0,
0.0,1.0,0.0, -.1,0.5,0.5, 0.0,1.1,1.0, 1.0,1.2,0.0, 0.8,-.1,0.5, 1.0,1.3,1.0,
0.0,1.1,0.0, 0.2,0.3,0.0, 0.0,1.2,1.0, 1.0,1.1,0.1, 0.8,0.3,1.0, 1.0,1.2,1.0,
0.0,1.2,0.0, 0.2,-.1,0.5, 0.0,1.3,1.0, 1.0,1.0,0.0, 1.1,0.5,0.5, 1.0,1.1,1.0,
},
{ //hop
0.7,1.4,0.0, 0.2,0.5,1.0, 0.7,1.4,1.0, 1.5,0.9,0.0, 1.0,0.0,0.0, 1.5,1.0,0.9,
0.7,1.3,0.0, 0.2,0.4,0.5, 0.7,1.3,1.0, 1.4,1.0,0.0, 1.0,0.0,0.5, 1.4,1.0,1.0,
0.7,1.4,0.0, 0.2,0.5,0.0, 0.7,1.4,1.0, 1.5,0.9,0.0, 1.0,0.0,1.0, 1.5,1.0,0.9,
0.7,1.5,0.0, 0.2,0.6,0.5, 0.7,1.5,1.0, 1.6,0.8,0.0, 1.0,0.3,0.5, 1.6,1.0,0.8,
},
};
// CURRENT STATE OF POLLY
double vertices[][] = new double[6][3];
double transition=0, beat=0, time=0, x=0, y=0, z=0, theta=0, size=1;
double speedTarget=0, speed=0;
int index, action0 = 0, action1 = 0;
Matrix M = new Matrix();
double sX = 0, sZ = 0;
// PUBLIC QUERIES
public double getDirection() { return direction; }
public double getPhase() { return Math.PI * beat / 2; }
public double getSize() { return size; }
public double getSpeed() { return speed; }
public double getX() { return x + sX; }
public double getY() { return y ; }
public double getZ() { return z + sZ; }
public double getX(int v) { return size * (vertices[v][0] - .5); }
public double getY(int v) { return size * vertices[v][1]; }
public double getZ(int v) { return size * (.5 - vertices[v][2]); }
// PARAMETERS THAT CAN BE SET PUBLICLY
public void changeTurning(double delta) { turningTarget += delta; }
public void changeDirection(double delta) { direction += delta; }
public void changeSpeed(double delta) { speedTarget += delta; }
public void setAction(int action) {
if (action >= 0 && action < actions.length && action != action1) {
action0 = action1;
action1 = action;
transition = 0;
}
}
public void setTurning(double Turning) { turningTarget = Turning; }
public void setDirection(double Direction) { direction = Direction; }
public void setSize(double Size) { size = Size; }
public void setSpeed(double S) { speedTarget = S; }
public void setX(double X) { x = X; }
public void setY(double Y) { y = Y; }
public void setZ(double Z) { z = Z; }
public void setNod(double n, double b) { nodTarget = n; nodBoldnessTarget = b; }
public void setTilt(double t, double b) { tiltTarget = t; tiltBoldnessTarget = b; }
public void setTurn(double t, double b) { turnTarget = t; turnBoldnessTarget = b; }
public void setLift(double t) { liftTarget = t; }
double gx = 0, gy = 0, gz = 0, gw = 0;
double nodToObject = 0, turnToObject = 0;
double turning = 0, turningTarget = 0;
double direction = 0;
double nod = 0, nodTarget = 0;
double tilt = 0, tiltTarget = 0;
double turn = 0, turnTarget = 0;
double lift = 0, liftTarget = 0;
double nodBoldness = 0, nodBoldnessTarget = 0;
double tiltBoldness = 0, tiltBoldnessTarget = 0;
double turnBoldness = 0, turnBoldnessTarget = 0;
public void setGaze(double x,double y,double z,double w) {
gx = x;
gy = y;
gz = z;
gw = w;
}
double leftTwist = 0, rightTwist = 0;
public double getLeftFootTwist(){ return leftTwist; }
public double getRightFootTwist(){ return rightTwist; }
// ANIMATE ONE FRAME
public void animate(double time) {
// TRAVEL FORWARD
if (this.time == 0) this.time = time - 1;
double elapsedTime = time - this.time;
this.time = time;
speed = lerp(elapsedTime / 0.5, speed, speedTarget);
double travel = elapsedTime * size * speed;
direction += elapsedTime * turning;
theta = (direction + 100 * Math.PI) % (2 * Math.PI);
double sin = Math.sin(theta), cos = Math.cos(theta);
double tx = travel * sin;
double tz = travel * cos;
x += tx;
z += tz;
// SET SHAPE ACCORDING TO CURRENT ACTION AND KEY POSES
transition = Math.min(1, transition + elapsedTime);
double stepsPerSecond = lerp(transition,rate[action0],rate[action1]);
beat += stepsPerSecond * elapsedTime;
int key = (int)(beat % 4);
for (int v = 0 ; v < vertices.length ; v++)
for (int i = 0 ; i < 3 ; i++)
vertices[v][i] = coord(transition, beat%1, action0,action1, key,(key+1)%4, v,i);
// MAKE THE FEET TRAVEL THE RIGHT AMOUNT
double stepLength = 2 * speed / stepsPerSecond;
double LX = (vertices[1][0] -= .5);
double LZ = vertices[1][2] - .5;
vertices[1][2] = 0;
M.identity();
M.rotateY(-turning * LZ / 2);
M.translate(-.2 * LZ * stepLength * turning, 0, LZ * (stepLength - 2 * turning * LX));
M.rotateY(-turning * LZ / 2);
misc.transform(vertices[1], M);
vertices[1][0] += .5;
vertices[1][2] += .5;
leftTwist = -turning * LZ;
double RX = (vertices[4][0] -= .5);
double RZ = vertices[4][2] - .5;
vertices[4][2] = 0;
M.identity();
M.rotateY(-turning * RZ / 2);
M.translate(-.2 * RZ * stepLength * turning, 0, RZ * (stepLength - 2 * turning * RX));
M.rotateY(-turning * RZ / 2);
misc.transform(vertices[4], M);
vertices[4][0] += .5;
vertices[4][2] += .5;
rightTwist = -turning * RZ;
//---------------- TURNING THE HEAD -----------------
double v[][] = vertices;
for (int i = 0 ; i < 3 ; i++)
head[i] = (v[0][i] + v[2][i] + v[3][i] + v[5][i]) / 4;
// COMPUTE ANGLES FOR GAZING AT AN OBJECT
if (gw != 0 || nodToObject != 0 || turnToObject != 0) { // COMPUTE CENTROID OF HEAD
double dx = gx - x, dz = gz - z; // COMPUTE TURN AND NOD ANGLES
double t = Math.atan2(dx, dz) - theta;
t = ((t+3*Math.PI)%(2*Math.PI)) - Math.PI;
double n = Math.atan2(gy - size * head[1], Math.sqrt(dx*dx + dz*dz));
double limit = gw > 0 ? Math.PI/3 : Math.PI/4;
double f = Math.abs(t) / limit; // DON'T GAZE AT THINGS BEHIND ME
if (f < 1 && gw < 0) // IF GAZE IS NEGATIVE (AVOIDING)
t = Math.pow(10,1-f) * t; // TRY NOT TO LOOK STRAIGHT AHEAD
double w = f<1 ? 1 : f>2 ? 0 : 2-f;
double speed = 0.14;
turnToObject = lerp(elapsedTime / speed, turnToObject, (gw < 0 ? t : -t) * w);
nodToObject = lerp(elapsedTime / speed, nodToObject , gw <= 0 ? 0 : n * w);
}
// BUILD THE MATRIX TO ANIMATE THE HEAD
M.identity();
M.translate(head[0],head[1],head[2]);
// LIFT OR LOWER THE HEAD
M.translate(0, .25 * lift, 0);
// ANGLES TO TURN THE HEAD
M.translate(0, 0, .7*turnBoldness);
M.rotateY(.5 * turn);
M.translate(0, 0,-.7*turnBoldness);
M.translate(0,-tiltBoldness, 0);
M.rotateZ(.3 * tilt);
M.translate(0, tiltBoldness, 0);
M.translate(0, (nod < 0 ? -.7 : .7) * nodBoldness + .3 * nod, 0);
M.rotateX(.3 * nod);
M.translate(0, (nod < 0 ? .7 : -.7) * nodBoldness - .3 * nod, 0);
// ANGLES TO GAZE AT AN OBJECT
M.rotateY(turnToObject + .1 * Noise.noise(2 * time));
M.rotateX(nodToObject + .1 * Noise.noise(2 * time + 10));
M.translate(-head[0],-head[1],-head[2]);
// APPLY TURNS TO HEAD VERTICES
for (int i = 0 ; i < v.length ; i++)
if (i != 1 && i != 4)
misc.transform(v[i], M);
/*
//---------------- FLIP EXPERIMENTS ------------------
if (action1 == 11) {
double t = 1 - (beat % 4) / 4;
t = misc.gain(t, .95);
t *= 2 * Math.PI;
double p[] = {0,0,0};
for (int i = 0 ; i < v.length ; i++)
for (int j = 0 ; j < 3 ; j++)
p[j] += v[i][j] / v.length;
for (int i = 0 ; i < v.length ; i++) {
for (int j = 0 ; j < 3 ; j++) v[i][j] -= p[j];
Vec.rotate(v[i], 0, t);
for (int j = 0 ; j < 3 ; j++) v[i][j] += p[j];
}
}
*/
//----------------- TIME-VARY ALL PARAMETERS --------------
double t = Math.min(.5, 10 * elapsedTime);
turning = lerp(t, turning, turningTarget);
nod = lerp(t, nod, nodTarget);
tilt = lerp(t, tilt, tiltTarget);
turn = lerp(t, turn, turnTarget);
lift = lerp(t, lift, liftTarget);
nodBoldness = lerp(t, nodBoldness, nodBoldnessTarget);
tiltBoldness = lerp(t, tiltBoldness, tiltBoldnessTarget);
turnBoldness = lerp(t, turnBoldness, turnBoldnessTarget);
}
// GET ONE COORDINATE, INTERPOLATING BETWEEN TWO ACTIONS
double coord(double s,double t,int a0,int a1,int k0,int k1,int v, int i) {
return lerp(s, coord(t, a0, k0, k1, v, i), coord(t, a1, k0, k1, v, i));
}
// GET ONE COORDINATE, INTERPOLATING BETWEEN TWO KEY POSES IN AN ACTION
double coord(double t, int action, int key0,int key1, int v,int i) {
return lerp(t, coord(action, key0, v, i), coord(action, key1, v, i));
}
// GET ONE COORDINATE FROM DATA
double coord(int action, int key, int v, int i) {
return actions[action][3 * (6*key + v) + i];
}
double lerp(double t, double a, double b) { return a + t * (b - a); }
double head[] = {0,0,0};
}