// Copyright 2001 Ken Perlin
import render.*; public class Face extends RenderApplet { Geometry s, eyes, rightEye, mouth, teeth, insideNose; Material skin; boolean transparentSkin = false; double aahTarget, oohTarget, lidsTarget, browTarget, smileTarget; double transition(double aTarget, double a) { return .5 * a + .5 * aTarget; } public void set(int i, double v) { switch (i) { case -1: state = 0; break; case 0: v = 2 * (v - .5); aahTarget = 1.1 * (v > 0 ? Math.pow(v,.2) : -Math.pow(-v,.2)); state = 1; break; case 1: oohTarget = v - .2; state = 1; break; case 2: lidsTarget = 1.5 * v - 1.2; state = 1; break; case 3: browTarget = .1 * (v - .5); state = 1; break; case 4: smileTarget = .16 * (v - .5) + .02; state = 1; break; } } public void initialize() { setBgColor(.2,.2,.2); skin = new Material(); skin.setColor(.58,.35,.15,.15,.15,.2,4).setGlow(.2,.15,.1); s = world.add(); s.add().globe( 5,40, .94,1.01, 0.0,1.0); s.add().globe(20,20, .00,.50, 0.0,1.0); s.add().globe( 5,40, .49,.56, 0.0,1.0); s.add().globe(20,10, .55,.95, 0.0,.2); s.add().globe(20,20, .55,.95, 0.2,.4); s.add().globe(20,15, .55,.95, 0.4,.473); s.add().globe(10,10, .55 ,.69 , .471,.565); s.add().globe(25,35, .686,.814, .471,.565); s.add().globe(10,10, .81 ,.95 , .471,.565); s.add().globe(40,30, .55,.95, .564,.67); s.add().globe(20,20, .55,.95, .66,1.0); push(); rotateX(-Math.PI/2); for (int i = 0 ; i < s.child.length ; i++) if (s.child[i] != null) transform(s.child[i]); pop(); s.setMaterial(skin); Material darkRed = new Material(); darkRed.setColor(0,0,0).setGlow(.2,0,0).setTransparency(0); mouth = world.add().globe(10,5, 0,1, .5,1); mouth.setMaterial(darkRed); Material white = new Material(); white.setColor(.1,.1,.1, .5,.5,.5,30).setGlow(.6,.6,.6); Material iris = new Material(); iris.setColor(.4,.5,.3).setGlow(.4,.5,.3); Material cornea = new Material(); cornea.setColor(0,0,0, 10,10,10,30).setTransparency(.5); eyes = world.add(); for (int i = 0 ; i < 2 ; i++) { eyes.add(); eyes.child[i].add().ball(10).setMaterial(white); eyes.child[i].add().globe(10,3, 0,1, .9,1); // PUPIL eyes.child[i].add().globe(10,3, 0,1, .85,.91).setMaterial(iris); eyes.child[i].add().globe(10,6, 0,1, .85,1).setMaterial(cornea); push(); rotateY(i==0?-.2:.2); translate(0,0,.02); transform(eyes.child[i].child[1]); translate(0,0,.02); transform(eyes.child[i].child[2]); scale(1,1,2); translate(0,0,-.44); transform(eyes.child[i].child[3]); pop(); } Material dark = new Material(); dark.setColor(0,0,0).setTransparency(.8); insideNose = world.add().disk(8); insideNose.setMaterial(dark); teeth = world.add().tube(10); teeth.setMaterial(white); addLight(1,1,1, 1,1,1); addLight(-1,-1,0, .6,.6,1); setFOV(.55); } private double time = 0; private int frameCount = 0; private int state = 0; double aah, ooh, lids, brow, smile; public void animate(double time) { skin.setTransparency(transparentSkin ? .5 : 0); pullCount = 0; this.time = time; boolean T = (int)time % 2 == 0; if (state == 0) { aah = Math.min(1,Math.max(-1,1.8*Noise.noise(.5*time))); aah = 1.1 * (aah > 0 ? Math.pow(aah, .2) : -Math.pow(-aah,.2)); ooh = .5 + Noise.noise(time+100); lids = Noise.noise(time+200); lids = (lids < 0 ? lids : .5*Math.pow(2*lids,.2)); lids = 1.2 * Math.pow(.5+lids,8) - .7; if (lids > 0) lids = .5; brow = .05*Noise.noise(time+300); smile = .2*Noise.noise(.3*time+400) + .02; } else { aah = transition(aahTarget, aah); ooh = transition(oohTarget, ooh); lids = transition(lidsTarget, lids); brow = transition(browTarget, brow); smile = transition(smileTarget, smile); } double x=0, xm=0, y=0, z=0, t=0, l=(.5-lids)/1.3; double theta = .02 * Noise.noise(time); double phi = .02 * Noise.noise(time + 100); push(); if (frameCount < 2) scale(0,0,0); rotateY(theta); rotateX(phi); push(); translate(0,.79,.92); double lookX = .007*((int)(1000*Noise.noise(.2*time + 1000))%10); double lookY = .01 + .05*Noise.noise(5*time + 1100); push(); translate(-.59,0,0); rotateY(lookX); rotateX(lookY); scale(.3,.3,.3); transform(eyes.child[0]); pop(); push(); translate(.59,0,0); rotateY(lookX); rotateX(lookY); scale(.3,.3,.3); transform(eyes.child[1]); pop(); pop(); push(); translate(0,-.19,1.45 + .04*ooh); scale(.34-.25*ooh,.2,.1); transform(mouth); pop(); push(); translate(0,-.03+.01*aah,1.25); rotateX(-Math.PI/2); scale(.36,.4,.1); transform(teeth); pop(); push(); translate(0,.3,1.73); rotateX(Math.PI/4); scale(.2,.2,1); transform(insideNose); pop(); pop(); push(); if (frameCount < 1) scale(0,0,0); scale(2.05,3,2.2); transform(s); push(); // CRANIAL BULGE IN BACK scale(1,1,1.1); pull(s, 0,0,0, -.4,.4,1, 0,-1,-1); pop(); push(); // LATERAL CRANIAL BULGE scale(1.1,1,1); pull(s, 0,0,0, .5,.85,1, 0,0,0); pop(); push(); // ADJUST SHAPE OF TOP OF HEAD scale(1,.975,1); translate(0,0,-.05); pull(s, -.9,0,.9, .5,1,1, 0,0,0); pop(); push(); // NECK scale(.4,1,.35); pull(s, 0,0,0, 1,-.7,-.7, 0,0,0); scale(5,1,7); pull(s, 0,0,0, -.4,-1,-1, 0,0,0); pop(); push(); // TEMPLES scale(.8,.9,1); pull(s, 0,1,1, 1,1,-.2, 0,0,0); pull(s, 0,-1,-1, 1,1,-.2, 0,0,0); pop(); push(); // JAW rotateX(.21+.04*aah); scale(1.18,1,1.3); translate(0,.03,.13); pull(s, -.9,0,.9, .3,-.2,-.4, .0,.2,.2); pop(); push(); // CHIN translate(0,-.03-.02*aah,.05-.03*aah); pull(s, -.4,0,.4, -.38,-.26,-.2-.03*aah, 0,1,1); pop(); push(); // EYE SOCKETS push(); translate(0,0,-.2); pull(s, -.01,.2,.2, .45,.3,.1+.5*smile, 0,1,1); pull(s, .01,-.2,-.2, .45,.3,.1+.5*smile, 0,1,1); pop(); push(); translate(0,brow>0?1.2*brow:2*brow,-.03); pull(s, -.45,0,.45, .2,.45,.6, 0,1,1); pop(); push(); translate(0,0,-.03); pull(s, 0,0,0, .3,.1,.1, 0,1,1); pop(); pop(); push(); // EYE LIDS y = -.015 + .1*lids; z = 0; push(); // TO HELP EYES TO SHUT translate(0,0,.2 * (.5+.5*lids)); pull(s, -.10,-.36,-.53, .29,.27,.25, .2,.8,.8); pull(s, .10, .36, .53, .29,.27,.25, .2,.8,.8); pop(); translate(0,0,.2); push(); // UPPER LID translate(0,-.37,-z); rotateX(y); translate(0,.37,z+.01); pull(s, .10, .36, .53, .39,.285,.27, .2,.8,.8); pull(s, -.10,-.36,-.53, .39,.285,.27, .2,.8,.8); pop(); push(); // LOWER LID t = Math.abs(smile) * l * .5; x = smile > 0 ? 0 : .4*t; translate(0,-.37,-z); rotateX(-y); translate(0,.37+t,z+y+.7*x); pull(s, .10-x/2, .36-x, .53, .27,.255,.17, .2,.9,.9); pull(s, -.10+x/2,-.36+x,-.53, .27,.255,.17, .2,.9,.9); pop(); pop(); push(); // CHEEKS push(); translate(.5*smile,-.04+.03*lids+.5*smile,.25); pull(s, .1,.7,.7, .32,.12,-.2, 0,1,1); pop(); push(); translate(-.5*smile,-.04+.03*lids+.5*smile,.25); pull(s, -.1,-.7,-.7, .32,.12,-.2, 0,1,1); pop(); pop(); push(); // BROW FURROWS translate(0,0,-.05); pull(s, -.6,.4,.4, .4+brow,.6,.6, 0,1,1); pull(s, .6,-.4,-.4, .4+brow,.6,.6, 0,1,1); pop(); push(); // NOSE push(); translate(0,-.01,.20); pull(s, -.25,0,.25, .36,.15,.015, 0,1,1); translate(0,.037 + .003*aah,-.18); pull(s, -.15,0,.15, .24,.15,.033, 0,1,1); pop(); push(); translate(0,.02+.006*aah,0); // NOSTRILS push(); push(); translate(-.04,0,0); pull(s, -.2,-.1,0, .2,.09,.06, .73,.81,.88); pop(); translate(0,.2,-.15); pull(s, -.085,-.045,-.01, .12,.06,.05, 0,1,1); pop(); push(); push(); translate(.04,0,0); pull(s, .2,.1,0, .2,.09,.06, .73,.81,.88); pop(); translate(0,.2,-.15); pull(s, .085, .045, .01, .12,.06,.05, 0,1,1); pop(); pop(); pop(); push(); // LIPS x = .35-.2*ooh; push(); // MOUTH CAVITY translate(0,0,-.01-.3*Math.pow(Math.max(.01,.3+.7*aah), .2)); pull(s, -x,0,x, -.035,-.055,-.08, 0,1,1); pop(); push(); // UPPER LIP xm = Math.max(x,.3); translate(0,-.003+.012*aah,.065+.01*(ooh-aah)); translate(0,.13,.63); rotateX(-.65); // CURL UP translate(0,-.13,-.63); pull(s, -x,0,x, -.08,-.05,.18, 0,1,1); pop(); push(); // LOWER LIP y = .03*aah; translate(0,-y,.03+.005*(ooh-aah)); pull(s, -x,0,x, -.20-.03*ooh,-.09,-.06, 0,1,1); translate(0,-.16,.75); rotateX(.3); // CURL DOWN translate(0,.16,-.75); pull(s, -x,0,x, -.21,-.12,-.06, .7,.75,.75); pop(); translate(0,0,.04*ooh); // PUSH MOUTH FWD WHEN PUCKERING pull(s, -x-.1,0,x+.1, -.20,-.05,.01, 0,1,1); push(); // PULL UP OR DOWN SIDES OF FACE AROUND MOUTH x = .45-.2*ooh; translate(0,-.02-.02*aah + smile,0); pull(s, 0, x, 2*x, -.8,-.02,.2, 0,1,1); pull(s, 0,-x,-2*x, -.8,-.02,.2, 0,1,1); pop(); pop(); push(); // TURN HEAD rotateY(theta); rotateX(phi); pull(s, 0,0,0, -1,-.2,-.2, 0,0,0); pop(); pop(); frameCount++; } public void setTransparent() { transparentSkin = !transparentSkin; renderer.updateTransparency = true; } public void setVisibleMesh() { if (transparentSkin) setTransparent(); renderer.showMesh = ! renderer.showMesh; } private int pullCount = 0; private double pStartTime; private boolean progression = false; public void setProgression() { progression = true; pStartTime = time; maskTime = time; for (int i = 0 ; i < mask.length ; i++) mask[i] = 0xffffffff; pullCount = 0; } private final int nPulls = 37; private int mask[] = new int[nPulls]; private double maskTime = 0; public int pull(Geometry s, double x0,double x1,double x2, double y0,double y1,double y2, double z0,double z1,double z2) { Geometry.pullWeight = 1; Geometry.pullMask = 0xffffffff; if (progression) { double t = time - pStartTime; if (t >= nPulls) progression = false; else { t = t % nPulls; if (t < pullCount) return 0; if (t < pullCount+1) Geometry.pullWeight = t - pullCount; } } if (time - maskTime > 0 && mask[pullCount] != 0) Geometry.pullMask = mask[pullCount]; int msk = super.pull(s, x0,x1,x2, y0,y1,y2, z0,z1,z2); if (time - maskTime > 1) mask[pullCount] |= msk; pullCount++; return 0; } }