//
import render.*;
public class Polly extends Geometry
{
//----- DATA DEFINING ALL THE KEY ACTIONS
// BEATS PER SECOND FOR EACH ACTION
static double rate[] = {4, 8, 6, 7, 2, 2, 2, 10, 12, 10, 9, 10, 4};
// HOW MUCH DISTANCE EACH ACTION TRAVELS PER BEAT
static double travel[] = {0, .5, .5, .45, .45, .5, .3, 0, 0, 0, .7, 0, -.5};
// AN ACTION HAS FOUR KEY POSES. EACH POSE HAS SIX (X,Y,Z) VERTICES.
static double actions[][] = {
{ //idle
.01,1.0,.00, 0.,0.,0.5, .02,1.00,1.01, 1.02,1.,.03, 1.,0.,.5, 1.01,1.00,1.03,
.03,1.0,.02, 0.,0.,0.5, .00,1.03,1.03, 1.01,1.,.02, 1.,0.,.5, 1.00,1.03,1.00,
.01,1.0,.01, 0.,0.,0.5, .01,1.00,1.00, 1.03,1.,.00, 1.,0.,.5, 1.03,1.00,1.01,
.02,1.0,.01, 0.,0.,0.5, .00,0.97,1.02, 1.00,1.,.01, 1.,0.,.5, 1.02,0.97,1.02,
},
{ //scamper
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,
},
{ //broadjump
0.0,0.4,0.1, 0.0,0.0,0.9, 0.0,0.7,1.0, 1.0,0.4,0.1, 1.0,0.0,0.9, 1.0,0.7,1.0,
0.0,1.9,0.0, 0.1,0.7,0.5, 0.0,1.8,1.0, 1.0,1.9,0.0, 0.9,0.7,0.5, 1.0,1.8,1.0,
0.0,1.9,0.0, 0.1,0.5,0.0, 0.0,1.6,1.0, 1.0,1.9,0.0, 0.9,0.5,0.0, 1.0,1.6,1.0,
0.0,1.0,0.0, 0.0,0.0,0.3, 0.0,1.0,1.0, 1.0,1.0,0.0, 1.0,0.0,0.3, 1.0,1.0,1.0,
},
{ //prowl
0.0,0.6,-.1, 0.2,0.0,0.9, 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.1, 0.1,0.8,1.0, 1.1,1.0,0.0, 0.8,0.0,.45, 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,0.9, 1.0,1.0,1.0,
-.1,1.0,0.0, 0.2,0.0,.45, -.1,0.8,1.0, 0.9,1.0,0.0, 1.0,0.3,0.1, 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,
},
{ //dejected
0.0,0.5,0.0, 0.1,0.0,0.8, 0.,1.00,1.0, 1.0,0.7,-.1, 0.9,0.0,0.2, 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.2, 0.,1.00,0.9, 1.0,0.5,0.0, 0.9,0.0,0.8, 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,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,
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.5, 0.1,1.1,1.1, 0.9,0.9,-.1, 1.0,0.0,0.5, 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,0.5, 0.,1.05,1.0, 1.,0.90,-.1, 1.0,0.0,0.5, 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.5, 0.,0.93,1.0, 1.,1.15,-.1, 1.0,0.0,0.5, 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,
},
{ //hotfeet
0.0,1.2,0.1, 0.0,0.3,0.5, 0.0,1.2,1.0, 1.0,1.2,0.0, 1.0,0.3,0.5, 1.0,1.2,1.0,
0.0,1.3,0.0, 0.2,0.5,0.5, 0.0,1.3,1.0, 1.0,1.1,0.0, 1.0,-.1,0.5, 1.0,1.1,1.0,
0.0,1.2,0.0, 0.0,0.3,0.5, 0.0,1.2,1.0, 1.0,1.2,0.1, 1.0,0.3,0.5, 1.0,1.2,1.0,
0.0,1.1,0.0, 0.0,-.1,0.5, 0.0,1.1,1.0, 1.0,1.3,0.0, 0.8,0.5,0.5, 1.0,1.3,1.0,
},
{ //sprint
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.2, 1.0,1.2,1.0,
0.0,1.0,0.0, -.1,0.5,0.6, 0.0,1.1,1.0, 1.0,1.2,0.0, 0.8,-.1,0.6, 1.0,1.3,1.0,
0.0,1.1,0.0, 0.2,0.3,0.2, 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.6, 0.0,1.3,1.0, 1.0,1.0,0.0, 1.1,0.5,0.6, 1.0,1.1,1.0,
},
{ //hop
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.5, 0.7,1.4,1.0, 1.5,0.9,0.0, 1.0,0.0,0.5, 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.2,0.5, 1.6,1.0,0.8,
0.7,1.4,0.0, 0.2,0.5,0.5, 0.7,1.4,1.0, 1.5,0.9,0.0, 1.0,0.0,0.5, 1.5,1.0,0.9,
},
{ //scamper backward
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.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,
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.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,
},
};
// HOW THE VERTICES OF THE POLYHEDRON ARE CONNECTED BY FACES
static int faces[][]={{0,1,2},{3,4,5},{6,7,8,9},{10,11,12,13},{14,15,16,17}};
static int map[] = { 0,1,2, 5,4,3, 1,0,3,4, 2, 1, 4, 5, 0, 2, 5, 3 };
static Material red = (new Material()).setColor(1,0,0, 1,1,1,10, .2,0,0);
static public void clearPollys() { nPollys = 0; }
//----- STUFF THAT'S UNIQUE TO EACH INDIVIDUAL POLLY
// CONSTRUCTOR
public Polly() {
setMaterial(red);
Geometry body = add();
body.faces = faces;
body.vertices = new double[18][6];
body.matrix.translate(-.5,0,-.5);
//Matrix.translate(body.matrix,-.5,0,-.5);
Geometry shadow = add();
shadow.setMaterial((new Material()).setColor(0,0,0).setTransparency(.75));
shadow.faces = body.faces;
shadow.vertices = body.vertices;
shadow.matrix.translate(-.5,.03,-.5);
shadow.matrix.scale(1.1,.02,1.1);
//Matrix.translate(shadow.matrix,-.5,.03,-.5);
//Matrix.scale(shadow.matrix,1.1,.02,1.1);
index = nPollys++;
setP();
}
// ALLOW APPLICATION TO SET VARIOUS PARAMETERS
public Polly setColor(double r, double g, double b) {
child[0].setMaterial(
(new Material()).setColor(r,g,b, 1,1,1,10, .2*r,.2*g,.2*b));
return this;
}
public Polly setSize(double Size) {
size = Size; setP(); return this;
}
public Polly setPosition(double X, double Z) {
x = X; z = Z; setP(); return this;
}
public Polly setDirection(double Theta) {
theta = Theta; return this;
}
void setP() {
p[index][AX] = p[index][BX] = x;
p[index][AZ] = p[index][BZ] = z;
p[index][SIZE] = size;
}
// ALLOW APPLICATION TO CHOOSE THE NEXT ACTION
public void action(int action) {
if (action >= 0 && action < actions.length && action != action1) {
action0 = action1;
action1 = action;
transition = 0;
}
}
// ANIMATE ONE FRAME
public void animate(double time) {
// TRAVEL FORWARD
if (prevTime == 0) prevTime = time - 1;
double elapsed = time - prevTime;
prevTime = time;
double travel = size * getTravel(elapsed);
double tz = travel * Math.cos(theta);
double tx = travel * Math.sin(theta);
z += tz;
x += tx;
setP();
p[index][BX] = x + tx;
p[index][BZ] = z + tz;
// TURN AWAY FROM OBSTACLES
double R = repulse(x,z,size,index);
if (R > 0) {
double Rx = (repulse(x+.01,z,size,index) - R) / .01;
double Rz = (repulse(x,z+.01,size,index) - R) / .01;
if (Rx*tx > -Rz*tz) // IF FACING WALL
theta += Rx*tz < Rz*tx ? R : -R; // TURN MORE AWAY
}
// SET POLLY'S SHAPE ACCORDING TO CURRENT ACTION AND KEY POSES
transition = Math.min(1, transition + elapsed);
beat = (beat + elapsed*lerp(transition,rate[action0],rate[action1])) % 4;
int key = (int)beat;
for (int v = 0 ; v < child[0].vertices.length ; v++)
for (int i = 0 ; i < 3 ; i++)
child[0].vertices[v][i] =
coord(transition, beat%1, action0,action1, key,(key+1)%4, v,i);
child[0].computePolyhedronNormals();
// SET TRANSLATION, ROTATION AND SCALE
//Matrix.identity(matrix);
//Matrix.translate(matrix,x,0,z);
//Matrix.rotateY(matrix,Math.PI + theta);
//Matrix.scale(matrix,size,size,size);
matrix.identity();
matrix.translate(x,0,z);
matrix.rotateY(Math.PI + theta);
matrix.scale(size,size,size);
}
// COMPUTE HOW FAR FORWARD POLLY TRAVELS IN A SMALL TIME INTERVAL
public double getTravel(double elapsed) {
return elapsed * lerp(transition, travel[action0], travel[action1])
* lerp(transition, rate [action0], rate [action1]);
}
// 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][18*key + 3*map[v] + i];
}
// LINEAR INTERPOLATOR
double lerp(double t, double a, double b) { return a + t * (b - a); }
// INSTANCE DATA
int index, action0 = 0, action1 = 0;
double transition=0, beat=0, prevTime=0, x=0, z=0, theta=0, size=1;
//----- DEFINING STATIC WALLS IN THE SCENE FOR ALL POLLYS TO AVOID
// ADD A WALL, OR CLEAR ALL WALLS
static public void addWall(double wall[]) { w[nWalls++] = wall; }
static public void clearWalls() { nWalls = 0; }
static double repulse(double x, double z, double size, int index) {
double R = 0;
// REPULSION FROM ALL WALLS
for (int i = 0 ; i < nWalls ; i++) {
int n = w[i].length/2 - 1;
for (int j = 0 ; j < n ; j++)
R += repulse(w[i][2*j ]-x, w[i][2*j+1]-z,
w[i][2*j+2]-x, w[i][2*j+3]-z, j==0, j==n-1, size);
}
// REPULSION FROM OTHER POLLYS
for (int i = 0 ; i < nPollys ; i++)
if (i != index)
R += repulse(p[i][AX]-x,p[i][AZ]-z,
p[i][BX]-x,p[i][BZ]-z,true,true,.6*(size+p[i][SIZE]));
return R;
}
// COMPUTE REPULSION FORCE FROM ONE WALL SECTION
static double repulse(double ax,double az,double bx,double bz,
boolean aE,boolean bE, double size) {
// COMPUTE UNIT VECTOR BETWEEN THE WALL'S TWO END POINTS
double dx = bx - ax, dz = bz - az;
if (dx == 0 && dz == 0) dz = .1;
double len = Math.sqrt(dx*dx + dz*dz);
dx /= len;
dz /= len;
// REPULSION DROPS OFF WITH DISTANCE AWAY FROM PLANE OF WALL
double t = ax * dz - az * dx;
t = Math.max(0, 2.2*size - Math.abs(t)) / (2.2*size);
// REPULSION ALSO DROPS OFF AT THE TWO ENDS OF THE WALL
return t*t * Math.max(0,Math.min(1, (aE?1:.5) - .5*(ax*dx + az*dz))) *
Math.max(0,Math.min(1, (bE?1:.5) + .5*(bx*dx + bz*dz))) ;
}
// STATIC DATA
static int nWalls = 0;
static double w[][] = new double[100][];
final static int AX=0,AZ=1,BX=2,BZ=3,SIZE=4;
static int nPollys = 0;
static double p[][] = new double[100][5];
}