// import render.*; import java.awt.*; import java.net.*; import java.io.*; import java.applet.*; import java.util.StringTokenizer; import java.util.Vector; public class BeanGuy extends RenderApplet { Geometry beanGuyBase; GeometryLoader beanGuyLoader; AnimatedGeometry rightArmStill; AnimatedGeometry leftArmStill; AnimatedGeometry leftArmWave; AnimatedGeometry leftArmRaise; AnimatedGeometry beanGuyWalkLegs; AnimatedGeometry beanGuyWalkArms; Material shinyMetal = new Material(); Material blinkColor = null; Material eyeColor = null; java.util.Random randomizer = new java.util.Random(); public void initialize() { setFOV(1.0); setBgColor(0, 0, 0.1); shinyMetal.setColor(.6, .6, .6, .9, .9, .9, 10); URL b = getCodeBase(); // Load Base BeanGuy Model beanGuyLoader = new BeanGuyPoseLoader(); beanGuyLoader.calculateBoundingBoxes(true); beanGuyBase = beanGuyLoader.loadGeometry(b); // Obtain BeanGuy Body Color and EyeColor (used by blink() method) blinkColor = beanGuyBase.child[beanGuyLoader.getPartIndex("MainBody")].material; eyeColor = beanGuyBase.child[beanGuyLoader.getPartIndex("RightEye")].material; // Load A Single Frame Animation Geometry for BeanGuy's Right Arm rightArmStill = new AnimatedGeometry(1); rightArmStill.loadFrame(new BeanGuyRightArmLoader(), b); // Replace Base BeanGuy Model's Right Arm with rightArmStill replaceBeanGuyChild(rightArmStill.frameGeometry[0], rightArmStill.frameLoader[0]); // Load A Single Frame Animation Geometry for BeanGuy's Left Arm leftArmStill = new AnimatedGeometry(1); leftArmStill.loadFrame(new BeanGuyLeftArmDefaultLoader(), b); // Replace Base BeanGuy Model's Left Arm with leftArmStill replaceBeanGuyChild(leftArmStill.frameGeometry[0], leftArmStill.frameLoader[0]); // Create new AnimatedGeometry for Left Arm Waving, load animation // frams, and set animation speed and sequence leftArmWave = new AnimatedGeometry(3); leftArmWave.loadFrame(new BeanGuyWave1Loader(), b); leftArmWave.loadFrame(new BeanGuyWave2Loader(), b); leftArmWave.loadFrame(new BeanGuyWave3Loader(), b); leftArmWave.setAnimationSpeed(3); leftArmWave.setAnimationSequence(new int[] {0,1,2,1}); // Create new AnimatedGeometry for Left Arm Raising, load animation // frames, and set animation speed and sequence leftArmRaise = new AnimatedGeometry(5); leftArmRaise.loadFrame(leftArmStill.frameLoader[0], b); leftArmRaise.loadFrame(new BeanGuyLeftArmUp2Loader(), b); leftArmRaise.loadFrame(new BeanGuyLeftArmUp3Loader(), b); leftArmRaise.loadFrame(new BeanGuyLeftArmUp4Loader(), b); leftArmRaise.loadFrame(leftArmWave.frameLoader[0], b); leftArmRaise.setAnimationSpeed(3); leftArmRaise.setAnimationSequence(new int[] {0,1,2,3,4}); // Create new AnimatedGeometry for Bean Guy Walking, load animation // frames, and set animation speed and sequence beanGuyWalkLegs = new AnimatedGeometry(10); beanGuyWalkLegs.loadFrame(new LegWalk1Loader(), b); beanGuyWalkLegs.loadFrame(new LegWalk2Loader(), b); beanGuyWalkLegs.loadFrame(new LegWalk3Loader(), b); beanGuyWalkLegs.loadFrame(new LegWalk4Loader(), b); beanGuyWalkLegs.loadFrame(new LegWalk5Loader(), b); beanGuyWalkLegs.loadFrame(new LegWalk6Loader(), b); beanGuyWalkLegs.loadFrame(new LegWalk7Loader(), b); beanGuyWalkLegs.loadFrame(new LegWalk8Loader(), b); beanGuyWalkLegs.loadFrame(new LegWalk9Loader(), b); beanGuyWalkLegs.loadFrame(new LegWalk10Loader(), b); beanGuyWalkLegs.setAnimationSpeed(10); beanGuyWalkLegs.setAnimationSequence(new int[] {0,1,2,3,4,5,6,7,8,9}); // Create new AnimatedGeometry for Bean Guy Walking, load animation // frames, and set animation speed and sequence beanGuyWalkArms = new AnimatedGeometry(10); beanGuyWalkArms.loadFrame(new ArmWalk1Loader(), b); beanGuyWalkArms.loadFrame(new ArmWalk2Loader(), b); beanGuyWalkArms.loadFrame(new ArmWalk3Loader(), b); beanGuyWalkArms.loadFrame(new ArmWalk4Loader(), b); beanGuyWalkArms.loadFrame(new ArmWalk5Loader(), b); beanGuyWalkArms.loadFrame(new ArmWalk6Loader(), b); beanGuyWalkArms.loadFrame(new ArmWalk7Loader(), b); beanGuyWalkArms.loadFrame(new ArmWalk8Loader(), b); beanGuyWalkArms.loadFrame(new ArmWalk9Loader(), b); beanGuyWalkArms.loadFrame(new ArmWalk10Loader(), b); beanGuyWalkArms.setAnimationSpeed(10); beanGuyWalkArms.setAnimationSequence(new int[] {0,1,2,3,4,5,6,7,8,9}); // Scale Bean Guy down a bit beanGuyBase.scale(0.18, 0.18, 0.18); beanGuyBase.transform(); // Make sure to include him in the world world.add(beanGuyBase); // Lights... addLight(1, -1, 1, .6, .4, .4); addLight(-1, 1, 1, .4, .4, .6); addLight(-1, -1, 1, .35, .2, .2); addLight(-1, 1, -1, .2, .25, .3); addLight(1, -1, -1, .3, .2, .25); } // replaceBeanGuyChild() - replaces the BeanGuy child geometry corres- // ponding to the provided Geometry and associated GeometryLoader public void replaceBeanGuyChild(Geometry replacementGeometry, GeometryLoader replacementLoader) { AnimatedGeometry.replaceGeometryChildren(beanGuyBase, beanGuyLoader, replacementGeometry, replacementLoader); } // blink() - called every animation cycle to mimic blinking for BeanGuy. // (BeanGuy blinks every 2 + (0.0 - 3.0) seconds). public void blink(double time) { if(presence) { blinkElapsedTime += time - lastBlinkTime; if (eyesOpen && blinkElapsedTime > blinkWaitTime) { push(); translate(0.0, 0.0, -5.0); transform(beanGuyBase.child[beanGuyLoader.getPartIndex("RightPupil")]); transform(beanGuyBase.child[beanGuyLoader.getPartIndex("LeftPupil")]); pop(); beanGuyBase.child[beanGuyLoader.getPartIndex("RightEye")].child[0].setMaterial(blinkColor); beanGuyBase.child[beanGuyLoader.getPartIndex("LeftEye")].child[0].setMaterial(blinkColor); eyesOpen = false; blinkElapsedTime = blinkElapsedTime - blinkWaitTime; blinkWaitTime = (randomizer.nextDouble() * 3.0) + 2.0; } else if (!eyesOpen && blinkElapsedTime > 0.15) { push(); identity(); transform(beanGuyBase.child[beanGuyLoader.getPartIndex("RightPupil")]); transform(beanGuyBase.child[beanGuyLoader.getPartIndex("LeftPupil")]); pop(); beanGuyBase.child[beanGuyLoader.getPartIndex("RightEye")].child[0].setMaterial(eyeColor); beanGuyBase.child[beanGuyLoader.getPartIndex("LeftEye")].child[0].setMaterial(eyeColor); eyesOpen = true; blinkElapsedTime = blinkElapsedTime - .15; } lastBlinkTime = time; } } // establishPresence() - // This is really noise applied to the rotation of BeanGuy's facial features, i.e. // eyes, nose, and pupils, and BeanGuy's vertical arm postitioning (meant to look sort // of like breathing). BeanGuy's "head" isn't really suited for rotation about its X // axis, so noise is only applied to its Y axis roation for the eyes and nose. The pupils, // however rotate about both X and Y within the eyes. This use of noise to establish // presence is based on Ken Perlin's "The Clay Becomes Flesh" and "Responsive Face" // experiments. public void establishPresence(double time) { if(presence) { double theta = 0.0, phi = 0.0; push(); // Rotate "face", i.e. head, nose, and eyes, about head's Y axis varied by noise theta = Noise.noise(time); Matrix.copy(beanGuyBase.child[beanGuyLoader.getPartIndex("Nose")].m(),m()); rotateY(.075 * theta); transform(beanGuyBase.child[beanGuyLoader.getPartIndex("Nose")]); transform(beanGuyBase.child[beanGuyLoader.getPartIndex("LeftEye")]); transform(beanGuyBase.child[beanGuyLoader.getPartIndex("RightEye")]); transform(beanGuyBase.child[beanGuyLoader.getPartIndex("MainBody")]); if(eyesOpen) { // Also rotate pupils about head's X and Y axis identity(); rotateY(.075 * theta); push(); phi = Noise.noise(time + 50); rotateY(.05 * theta); rotateX(.05 * phi); transform(beanGuyBase.child[beanGuyLoader.getPartIndex("LeftPupil")]); transform(beanGuyBase.child[beanGuyLoader.getPartIndex("RightPupil")]); pop(); } pop(); push(); // Slightly lower or raise arms varied by noise Matrix.copy(beanGuyBase.child[beanGuyLoader.getPartIndex("RightSleeve")].m(),m()); translate(0.5 * Noise.noise(time + 25), 0.0, 0.0); transform(beanGuyBase.child[beanGuyLoader.getPartIndex("RightSleeve")]); Matrix.copy(beanGuyBase.child[beanGuyLoader.getPartIndex("RightHand")].m(),m()); translate(0.5 * Noise.noise(time + 25), 0.0, 0.0); transform(beanGuyBase.child[beanGuyLoader.getPartIndex("RightHand")]); Matrix.copy(beanGuyBase.child[beanGuyLoader.getPartIndex("LeftSleeve")].m(),m()); translate( -0.5 * Noise.noise(time + 25), 0.0, 0.0); transform(beanGuyBase.child[beanGuyLoader.getPartIndex("LeftSleeve")]); Matrix.copy(beanGuyBase.child[beanGuyLoader.getPartIndex("LeftHand")].m(),m()); translate( -0.5 * Noise.noise(time + 25), 0.0, 0.0); transform(beanGuyBase.child[beanGuyLoader.getPartIndex("LeftHand")]); pop(); } } // animate() - // Defined in RenderApplet class to be overidden by subclasses. // // Calls establishPresence() and blink() methods, defined above. Calls // appropriate animation methods for each AnimatedGeometry associated // with BeanGuy. Each AnimatedGeometry animation method call decides // for itself whether to actually animate anything. // // The refreshHighlight if-block pertains to the highlighting of // geometries selected by the middle mouse button. The material of the // highlighted geometry is set to shinyMetal and restored when the user // either selects another geometry to be highlighted or selects the // background with the middle mouse button. This code was initilly used // for debugging the pickability code I added to the existing Renderer // class and remains to illustrate that augmentation. // // The checkMotion if-block pertains to initation of various animation // sequences base on geometries selected by the right mouse button. // public void animate(double time) { establishPresence(time); blink(time); beanGuyWalkLegs.animateCycle(time); beanGuyWalkArms.animateCycle(time); leftArmWave.animateCycle(time); leftArmRaise.animateOnce(time); if (refreshHighlight == true) { if (refreshReady == true) { Geometry highlightedGeometry = renderer.getMouseGeometry(); if (lastGeometryHighLight != null) lastGeometryHighLight.setMaterial(lastGeometryMaterial); if (highlightedGeometry != null) { lastGeometryMaterial = highlightedGeometry.material; highlightedGeometry.setMaterial(shinyMetal); lastGeometryHighLight = highlightedGeometry; } else { lastGeometryHighLight = null; } refreshHighlight = false; refreshReady = false; } else { refreshReady = true; } } if (checkMotion == true) { if (motionReady == true) { selectedGeometry = renderer.getMouseGeometry(); for(int i = 0; i < beanGuyBase.numChildren(); i++) { if(selectedGeometry != beanGuyBase.child[i]) { for(int j = 0; j < beanGuyBase.child[i].numChildren(); j++) { if(selectedGeometry == beanGuyBase.child[i].child[j]) { selectedGeometry = beanGuyBase.child[i]; } } } } if (selectedGeometry == beanGuyBase.child[beanGuyLoader.getPartIndex("LeftSleeve")] || selectedGeometry == beanGuyBase.child[beanGuyLoader.getPartIndex("LeftHand")]) { if (leftArmRaise.animationCycle <= 0) { processLeftArmRaiseRequest(); } else if (leftArmRaise.animationCycle >= leftArmRaise.lastFrame() && leftArmWave.nowAnimating == false) { leftArmWave.resetAnimation(); processLeftArmWaveRequest(); } else if (leftArmRaise.animationCycle >= 4) { processLeftArmLowerRequest(); } } else if (selectedGeometry == beanGuyBase.child[beanGuyLoader.getPartIndex("LeftLeg")] || selectedGeometry == beanGuyBase.child[beanGuyLoader.getPartIndex("LeftShoe")] || selectedGeometry == beanGuyBase.child[beanGuyLoader.getPartIndex("RightLeg")] || selectedGeometry == beanGuyBase.child[beanGuyLoader.getPartIndex("RightShoe")] || selectedGeometry == beanGuyBase.child[beanGuyLoader.getPartIndex("Pants")]) { processWalkRequest(); } checkMotion = false; motionReady = false; } else { motionReady = true; } } } // MOUSEDOWN OVERRIDE public boolean mouseDown(Event event, int x, int y) { super.mouseDown(event, x, y); // a middle mouse button or CNTL+left mouse button press // resets the renderer's mouse location to the current // mouse x and y location, and sets refreshHighlight to // true. Refer to animate() method above for futher details. if (event.controlDown() == true) { renderer.setMouseLocation(x, y); refreshHighlight = true; // a right mouse button or ALT+left mouse button press // resets the renderer's mouse location to the current // mouse x and y location, and sets checkMotion to true. // Refer to animate() method above for futher details. } else if (event.metaDown() == true) { renderer.setMouseLocation(x, y); checkMotion = true; } else { } return true; } // KEYDOWN PROCESSING public boolean keyDown( Event e, int key) { if((char)key == 'r' || (char)key == 'R') { // Pressing the r key raises BeanGuy's Left Arm processLeftArmRaiseRequest(); } else if ((char)key == 'l' || (char)key == 'L') { // Pressing the l key lowers BeanGuy's Left Arm processLeftArmLowerRequest(); } else if ((char)key == 'h' || (char)key == 'H') { // Pressing the k key causes BeanGuy to wave hello processLeftArmWaveRequest(); } else if ((char)key == 'w' || (char)key == 'W') { // Pressing the w key cause BeanGuy to walk processWalkRequest(); } else if ((char)key == 'b' || (char)key == 'B') { processBoundingBoxDisplayRequest(); } else if ((char)key == 'p' || (char)key == 'P') { processPresenceChangeRequest(); } else if (key == Event.UP) { // Pressing the up arrow key cause BeanGuy to walk faster if(beanGuyWalkLegs.animationSpeed < 30) beanGuyWalkLegs.animationSpeed++; } else if (key == Event.DOWN) { // Pressing the down arrow key cause BeanGuy to walk slower if(beanGuyWalkLegs.animationSpeed > 0 ) beanGuyWalkLegs.animationSpeed--; } else if (key == Event.LEFT) { beanGuyBase.rotateY(0.1); beanGuyBase.transform(); } else if (key == Event.RIGHT) { beanGuyBase.rotateY(-0.1); beanGuyBase.transform(); } return true; } public void processPresenceChangeRequest() { presence = !presence; } public void processBoundingBoxDisplayRequest() { if(boundingBoxVisible) { for(int i = 0; i < beanGuyBase.numChildren(); i++) { beanGuyBase.child[i].setBoundingBoxVisible(false); } boundingBoxVisible = false; } else { for(int i = 0; i < beanGuyBase.numChildren(); i++) { beanGuyBase.child[i].setBoundingBoxVisible(true); } boundingBoxVisible = true; } } public void processLeftArmRaiseRequest() { if(leftArmRaise.nowAnimating) { leftArmRaise.setAnimating(false); } else { replaceBeanGuyChild(leftArmRaise.frameGeometry[0], leftArmRaise.frameLoader[0]); leftArmRaise.setAnimating(true); leftArmWave.setAnimating(false); } } public void processLeftArmLowerRequest() { if(leftArmRaise.nowReverseAnimating) { leftArmRaise.setReverseAnimating(false); } else { replaceBeanGuyChild(leftArmRaise.frameGeometry[0], leftArmRaise.frameLoader[0]); leftArmRaise.setReverseAnimating(true); if(leftArmWave.nowAnimating) { leftArmWave.setAnimating(false); leftArmRaise.resetAnimation(); } } } public void processLeftArmWaveRequest() { if (leftArmWave.nowAnimating) { leftArmWave.setAnimating(false); } else { if (leftArmRaise.animationCycle >= leftArmRaise.lastFrame()) { leftArmRaise.setAnimating(false); leftArmRaise.setReverseAnimating(false); replaceBeanGuyChild(leftArmWave.frameGeometry[0], leftArmWave.frameLoader[0]); leftArmWave.setAnimating(true); } } } public void processWalkRequest() { if (beanGuyWalkLegs.nowAnimating) { beanGuyWalkLegs.setAnimating(false); beanGuyWalkArms.setAnimating(false); } else { replaceBeanGuyChild(beanGuyWalkLegs.frameGeometry[0], beanGuyWalkLegs.frameLoader[0]); replaceBeanGuyChild(beanGuyWalkArms.frameGeometry[0], beanGuyWalkArms.frameLoader[0]); beanGuyWalkLegs.setAnimating(true); beanGuyWalkArms.setAnimating(true); } } public void processWalkFasterRequest() { if(beanGuyWalkLegs.animationSpeed < 30) beanGuyWalkLegs.animationSpeed++; } public void processWalkSlowerRequest() { if(beanGuyWalkLegs.animationSpeed > 0) beanGuyWalkLegs.animationSpeed--; } public void processTurnRightRequest() { beanGuyBase.rotateY(-0.1); beanGuyBase.transform(); } public void processTurnLeftRequest() { beanGuyBase.rotateY(0.1); beanGuyBase.transform(); } private double lastBlinkTime = 0.0; private double blinkElapsedTime = 0.0; private double blinkWaitTime = 3.0; private Geometry lastGeometryHighLight; private Material lastGeometryMaterial; private boolean refreshHighlight = false; private boolean refreshReady = false; private boolean presence = true; private boolean eyesOpen = true; private boolean boundingBoxVisible = false; private boolean checkMotion = false; private Geometry selectedGeometry = null; private boolean motionReady = false; }