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