//
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}; }