Java/3D/3D Animation

Материал из Java эксперт
Перейти к: навигация, поиск

Animation and Interaction - a Bouncing Ball

   <source lang="java">

/* The Joy of Java 3D by Greg Hopkins Copyright Copyright 2001

  • /

/* To create animation you need to move the objects between each frame of animation. You can use a timer and move the 3D objects by a small amount each time. Also, you can modify the objects in other ways, the next example scales the ball so that it looks squashed at the bottom of each bounce. For interaction with the user, you can process keystrokes or clicks on buttons or other components. One thing to notice is that you have to tell Java3D you are going to move something by setting a capability. Otherwise, you will not be able to move anything once it has been drawn.

objTrans = new TransformGroup();
objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);

The following example combines these techniques. You start it by clicking on the button, then the ball bounces up and down, and you can press a or s to move the ball left or right.

  • /

import java.applet.Applet; import java.awt.BorderLayout; import java.awt.Button; import java.awt.GraphicsConfiguration; import java.awt.Panel; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import javax.media.j3d.AmbientLight; import javax.media.j3d.BoundingSphere; import javax.media.j3d.BranchGroup; import javax.media.j3d.Canvas3D; import javax.media.j3d.DirectionalLight; import javax.media.j3d.Transform3D; import javax.media.j3d.TransformGroup; import javax.swing.Timer; import javax.vecmath.Color3f; import javax.vecmath.Point3d; import javax.vecmath.Vector3d; import javax.vecmath.Vector3f; import com.sun.j3d.utils.applet.MainFrame; import com.sun.j3d.utils.geometry.Sphere; import com.sun.j3d.utils.universe.SimpleUniverse; public class BouncingBall extends Applet implements ActionListener, KeyListener {

 private Button go = new Button("Go");
 private TransformGroup objTrans;
 private Transform3D trans = new Transform3D();
 private float height = 0.0f;
 private float sign = 1.0f; // going up or down
 private Timer timer;
 private float xloc = 0.0f;
 public BranchGroup createSceneGraph() {
   // Create the root of the branch graph
   BranchGroup objRoot = new BranchGroup();
   objTrans = new TransformGroup();
   objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
   objRoot.addChild(objTrans);
   // Create a simple shape leaf node, add it to the scene graph.
   Sphere sphere = new Sphere(0.25f);
   objTrans = new TransformGroup();
   objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
   Transform3D pos1 = new Transform3D();
   pos1.setTranslation(new Vector3f(0.0f, 0.0f, 0.0f));
   objTrans.setTransform(pos1);
   objTrans.addChild(sphere);
   objRoot.addChild(objTrans);
   BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0),
       100.0);
   Color3f light1Color = new Color3f(1.0f, 0.0f, 0.2f);
   Vector3f light1Direction = new Vector3f(4.0f, -7.0f, -12.0f);
   DirectionalLight light1 = new DirectionalLight(light1Color,
       light1Direction);
   light1.setInfluencingBounds(bounds);
   objRoot.addChild(light1);
   // Set up the ambient light
   Color3f ambientColor = new Color3f(1.0f, 1.0f, 1.0f);
   AmbientLight ambientLightNode = new AmbientLight(ambientColor);
   ambientLightNode.setInfluencingBounds(bounds);
   objRoot.addChild(ambientLightNode);
   return objRoot;
 }
 public BouncingBall() {
   setLayout(new BorderLayout());
   GraphicsConfiguration config = SimpleUniverse
       .getPreferredConfiguration();
   Canvas3D c = new Canvas3D(config);
   add("Center", c);
   c.addKeyListener(this);
   timer = new Timer(100, this);
   //timer.start();
   Panel p = new Panel();
   p.add(go);
   add("North", p);
   go.addActionListener(this);
   go.addKeyListener(this);
   // Create a simple scene and attach it to the virtual universe
   BranchGroup scene = createSceneGraph();
   SimpleUniverse u = new SimpleUniverse(c);
   u.getViewingPlatform().setNominalViewingTransform();
   u.addBranchGraph(scene);
 }
 public void keyPressed(KeyEvent e) {
   //Invoked when a key has been pressed.
   if (e.getKeyChar() == "s") {
     xloc = xloc + .1f;
   }
   if (e.getKeyChar() == "a") {
     xloc = xloc - .1f;
   }
 }
 public void keyReleased(KeyEvent e) {
   // Invoked when a key has been released.
 }
 public void keyTyped(KeyEvent e) {
   //Invoked when a key has been typed.
 }
 public void actionPerformed(ActionEvent e) {
   // start timer when button is pressed
   if (e.getSource() == go) {
     if (!timer.isRunning()) {
       timer.start();
     }
   } else {
     height += .1 * sign;
     if (Math.abs(height * 2) >= 1)
       sign = -1.0f * sign;
     if (height < -0.4f) {
       trans.setScale(new Vector3d(1.0, .8, 1.0));
     } else {
       trans.setScale(new Vector3d(1.0, 1.0, 1.0));
     }
     trans.setTranslation(new Vector3f(xloc, height, 0.0f));
     objTrans.setTransform(trans);
   }
 }
 public static void main(String[] args) {
   System.out.println("Program Started");
   BouncingBall bb = new BouncingBall();
   bb.addKeyListener(bb);
   MainFrame mf = new MainFrame(bb, 256, 256);
 }

}

      </source>
   
  
 
  



HelloJava3Da renders a single, rotating cube

   <source lang="java">

/*

* @(#)HelloJava3Da.java 1.0 98/11/09 15:07:04
* 
* Copyright (c) 1996-1998 Sun Microsystems, Inc. All Rights Reserved.
* 
* Sun grants you ("Licensee") a non-exclusive, royalty free, license to use,
* modify and redistribute this software in source and binary code form,
* provided that i) this copyright notice and license appear on all copies of
* the software; and ii) Licensee does not utilize the software in a manner
* which is disparaging to Sun.
* 
* This software is provided "AS IS," without a warranty of any kind. ALL
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
* IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
* NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
* LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
* OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
* LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
* INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
* CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
* OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGES.
* 
* This software is not designed or intended for use in on-line control of
* aircraft, air traffic, aircraft navigation or aircraft communications; or in
* the design, construction, operation or maintenance of any nuclear facility.
* Licensee represents and warrants that it will not use or redistribute the
* Software for such purposes.
*/

import java.applet.Applet; import java.awt.BorderLayout; import java.awt.Frame; import javax.media.j3d.BranchGroup; import javax.media.j3d.Canvas3D; import com.sun.j3d.utils.applet.MainFrame; import com.sun.j3d.utils.geometry.ColorCube; import com.sun.j3d.utils.universe.SimpleUniverse; // HelloJava3Da renders a single, rotating cube. public class HelloJava3Da extends Applet {

 public HelloJava3Da() {
   setLayout(new BorderLayout());
   Canvas3D canvas3D = new Canvas3D(null);
   add("Center", canvas3D);
   BranchGroup scene = createSceneGraph();
   // SimpleUniverse is a Convenience Utility class
   SimpleUniverse simpleU = new SimpleUniverse(canvas3D);
   // This will move the ViewPlatform back a bit so the
   // objects in the scene can be viewed.
   simpleU.getViewingPlatform().setNominalViewingTransform();
   simpleU.addBranchGraph(scene);
 } // end of HelloJava3Da (constructor)
 public BranchGroup createSceneGraph() {
   // Create the root of the branch graph
   BranchGroup objRoot = new BranchGroup();
   objRoot.addChild(new ColorCube(0.4));
   return objRoot;
 } // end of CreateSceneGraph method of HelloJava3Da
 //  The following allows this to be run as an application
 //  as well as an applet
 public static void main(String[] args) {
   Frame frame = new MainFrame(new HelloJava3Da(), 256, 256);
 } // end of main (method of HelloJava3Da)

} // end of class HelloJava3Da


      </source>
   
  
 
  



Human Animation

   <source lang="java">

/*

* %Z%%M% %I% %E% %U%
* 
* ************************************************************** "Copyright (c)
* 2001 Sun Microsystems, Inc. All Rights Reserved.
* 
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 
* -Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 
* -Redistribution in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 
* Neither the name of Sun Microsystems, Inc. or the names of contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
* 
* This software is provided "AS IS," without a warranty of any kind. ALL
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
* IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
* NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
* LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
* OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
* LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
* INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
* CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
* OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGES.
* 
* You acknowledge that Software is not designed,licensed or intended for use in
* the design, construction, operation or maintenance of any nuclear facility."
* 
* ***************************************************************************
*/

import java.applet.Applet; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.GraphicsConfiguration; import java.awt.GridLayout; import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.image.BufferedImage; import java.io.BufferedOutputStream; import java.io.FileOutputStream; import java.text.NumberFormat; import javax.media.j3d.AmbientLight; import javax.media.j3d.Appearance; import javax.media.j3d.Background; import javax.media.j3d.BoundingSphere; import javax.media.j3d.BranchGroup; import javax.media.j3d.Canvas3D; import javax.media.j3d.DirectionalLight; import javax.media.j3d.ImageComponent; import javax.media.j3d.ImageComponent2D; import javax.media.j3d.Material; import javax.media.j3d.Screen3D; import javax.media.j3d.Transform3D; import javax.media.j3d.TransformGroup; import javax.media.j3d.View; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JSlider; import javax.swing.JTabbedPane; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.vecmath.AxisAngle4f; import javax.vecmath.Color3f; import javax.vecmath.Point3d; import javax.vecmath.Point3f; import javax.vecmath.Vector3f; import com.sun.image.codec.jpeg.JPEGCodec; import com.sun.image.codec.jpeg.JPEGEncodeParam; import com.sun.image.codec.jpeg.JPEGImageEncoder; import com.sun.j3d.utils.applet.MainFrame; import com.sun.j3d.utils.behaviors.mouse.MouseRotate; import com.sun.j3d.utils.geometry.Cylinder; import com.sun.j3d.utils.geometry.Sphere; import com.sun.j3d.utils.universe.SimpleUniverse; public class Human1 extends Applet implements ChangeListener, ActionListener {

 SimpleUniverse u;
 boolean isApplication;
 Canvas3D canvas;
 OffScreenCanvas3D offScreenCanvas;
 View view;
 // These names correspond to the H-Anim names
 TransformGroup Human_body;
 TransformGroup Human_r_shoulder;
 TransformGroup Human_r_elbow;
 TransformGroup Human_l_shoulder;
 TransformGroup Human_l_elbow;
 TransformGroup Human_skullbase;
 // these set up the transformations
 int rShoulderRot = 0;
 AxisAngle4f rShoulderAA = new AxisAngle4f(0.0f, 0.0f, -1.0f, 0.0f);
 JSlider rShoulderSlider;
 JLabel rShoulderSliderLabel;
 int rElbowRot = 0;
 AxisAngle4f rElbowAA = new AxisAngle4f(0.0f, 0.0f, -1.0f, 0.0f);
 JSlider rElbowSlider;
 JLabel rElbowSliderLabel;
 int lShoulderRot = 0;
 AxisAngle4f lShoulderAA = new AxisAngle4f(0.0f, 0.0f, 1.0f, 0.0f);
 JSlider lShoulderSlider;
 JLabel lShoulderSliderLabel;
 int lElbowRot = 0;
 AxisAngle4f lElbowAA = new AxisAngle4f(0.0f, 0.0f, 1.0f, 0.0f);
 JSlider lElbowSlider;
 JLabel lElbowSliderLabel;
 String snapImageString = "Snap Image";
 String outFileBase = "human";
 int outFileSeq = 0;
 float offScreenScale = 1.5f;
 // GUI elements
 JTabbedPane tabbedPane;
 // Temporaries that are reused
 Transform3D tmpTrans = new Transform3D();
 Vector3f tmpVector = new Vector3f();
 AxisAngle4f tmpAxisAngle = new AxisAngle4f();
 // These will be created, attached the scene graph and then the variable
 // will be reused to initialize other sections of the scene graph.
 Cylinder tmpCyl;
 Sphere tmpSphere;
 TransformGroup tmpTG;
 // colors for use in the cones
 Color3f red = new Color3f(1.0f, 0.0f, 0.0f);
 Color3f black = new Color3f(0.0f, 0.0f, 0.0f);
 Color3f white = new Color3f(1.0f, 1.0f, 1.0f);
 // geometric constant
 Point3f origin = new Point3f();
 Vector3f yAxis = new Vector3f(0.0f, 1.0f, 0.0f);
 // NumberFormat to print out floats with only two digits
 NumberFormat nf;
 // Returns the TransformGroup we will be editing to change the tranform
 // on the cone
 void createHuman() {
   Human_body = new TransformGroup();
   // center the body
   tmpVector.set(0.0f, -1.5f, 0.0f);
   tmpTrans.set(tmpVector);
   Human_body.setTransform(tmpTrans);
   // Set up an appearance to make the body with red ambient,
   // black emmissive, red diffuse and white specular coloring
   Material material = new Material(red, black, red, white, 64);
   Appearance appearance = new Appearance();
   appearance.setMaterial(material);
   // offset and place the cylinder for the body
   tmpTG = new TransformGroup();
   // offset the shape
   tmpVector.set(0.0f, 1.5f, 0.0f);
   tmpTrans.set(tmpVector);
   tmpTG.setTransform(tmpTrans);
   tmpCyl = new Cylinder(0.75f, 3.0f, appearance);
   tmpTG.addChild(tmpCyl);
   // add the shape to the body
   Human_body.addChild(tmpTG);
   // create the r_shoulder TransformGroup
   Human_r_shoulder = new TransformGroup();
   Human_r_shoulder.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
   Human_r_shoulder.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
   // translate from the waist
   tmpVector.set(-0.95f, 2.9f, -0.2f);
   tmpTrans.set(tmpVector);
   Human_r_shoulder.setTransform(tmpTrans);
   // place the sphere for the r_shoulder
   tmpSphere = new Sphere(0.22f, appearance);
   Human_r_shoulder.addChild(tmpSphere);
   // offset and place the cylinder for the r_shoulder
   tmpTG = new TransformGroup();
   // offset the shape
   tmpVector.set(0.0f, -0.5f, 0.0f);
   tmpTrans.set(tmpVector);
   tmpTG.setTransform(tmpTrans);
   tmpCyl = new Cylinder(0.2f, 1.0f, appearance);
   tmpTG.addChild(tmpCyl);
   // add the shape to the r_shoulder
   Human_r_shoulder.addChild(tmpTG);
   // add the shoulder to the body group
   Human_body.addChild(Human_r_shoulder);
   // create the r_elbow TransformGroup
   Human_r_elbow = new TransformGroup();
   Human_r_elbow.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
   Human_r_elbow.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
   tmpVector.set(0.0f, -1.054f, 0.0f);
   tmpTrans.set(tmpVector);
   Human_r_elbow.setTransform(tmpTrans);
   // place the sphere for the r_elbow
   tmpSphere = new Sphere(0.22f, appearance);
   Human_r_elbow.addChild(tmpSphere);
   // offset and place the cylinder for the r_shoulder
   tmpTG = new TransformGroup();
   // offset the shape
   tmpVector.set(0.0f, -0.5f, 0.0f);
   tmpTrans.set(tmpVector);
   tmpTG.setTransform(tmpTrans);
   tmpCyl = new Cylinder(0.2f, 1.0f, appearance);
   tmpTG.addChild(tmpCyl);
   // add the shape to the r_shoulder
   Human_r_elbow.addChild(tmpTG);
   // add the elbow to the shoulder group
   Human_r_shoulder.addChild(Human_r_elbow);
   // create the l_shoulder TransformGroup
   Human_l_shoulder = new TransformGroup();
   Human_l_shoulder.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
   Human_l_shoulder.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
   tmpVector.set(0.95f, 2.9f, -0.2f);
   tmpTrans.set(tmpVector);
   Human_l_shoulder.setTransform(tmpTrans);
   // place the sphere for the l_shoulder
   tmpSphere = new Sphere(0.22f, appearance);
   Human_l_shoulder.addChild(tmpSphere);
   // offset and place the cylinder for the l_shoulder
   tmpTG = new TransformGroup();
   // offset the shape
   tmpVector.set(0.0f, -0.5f, 0.0f);
   tmpTrans.set(tmpVector);
   tmpTG.setTransform(tmpTrans);
   tmpCyl = new Cylinder(0.2f, 1.0f, appearance);
   tmpTG.addChild(tmpCyl);
   // add the shape to the l_shoulder
   Human_l_shoulder.addChild(tmpTG);
   // add the shoulder to the body group
   Human_body.addChild(Human_l_shoulder);
   // create the r_elbow TransformGroup
   Human_l_elbow = new TransformGroup();
   Human_l_elbow.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
   Human_l_elbow.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
   tmpVector.set(0.0f, -1.054f, 0.0f);
   tmpTrans.set(tmpVector);
   Human_l_elbow.setTransform(tmpTrans);
   // place the sphere for the l_elbow
   tmpSphere = new Sphere(0.22f, appearance);
   Human_l_elbow.addChild(tmpSphere);
   // offset and place the cylinder for the l_elbow
   tmpTG = new TransformGroup();
   // offset the shape
   tmpVector.set(0.0f, -0.5f, 0.0f);
   tmpTrans.set(tmpVector);
   tmpTG.setTransform(tmpTrans);
   tmpCyl = new Cylinder(0.2f, 1.0f, appearance);
   tmpTG.addChild(tmpCyl);
   // add the shape to the l_elbow
   Human_l_elbow.addChild(tmpTG);
   // add the shoulder to the body group
   Human_l_shoulder.addChild(Human_l_elbow);
   // create the skullbase TransformGroup
   Human_skullbase = new TransformGroup();
   tmpVector.set(0.0f, 3.632f, 0.0f);
   tmpTrans.set(tmpVector);
   Human_skullbase.setTransform(tmpTrans);
   // offset and place the sphere for the skull
   tmpSphere = new Sphere(0.5f, appearance);
   // add the shape to the l_shoulder
   Human_skullbase.addChild(tmpSphere);
   // add the shoulder to the body group
   Human_body.addChild(Human_skullbase);
 }
 public void actionPerformed(ActionEvent e) {
   String action = e.getActionCommand();
   Object source = e.getSource();
   if (action == snapImageString) {
     Point loc = canvas.getLocationOnScreen();
     offScreenCanvas.setOffScreenLocation(loc);
     Dimension dim = canvas.getSize();
     dim.width *= offScreenScale;
     dim.height *= offScreenScale;
     nf.setMinimumIntegerDigits(3);
     offScreenCanvas.snapImageFile(
         outFileBase + nf.format(outFileSeq++), dim.width,
         dim.height);
     nf.setMinimumIntegerDigits(0);
   }
 }
 public void setRShoulderRot(int rotation) {
   rShoulderRot = rotation;
   rShoulderAA.angle = (float) Math.toRadians(rShoulderRot);
   Human_r_shoulder.getTransform(tmpTrans);
   tmpTrans.setRotation(rShoulderAA);
   Human_r_shoulder.setTransform(tmpTrans);
 }
 public void setRElbowRot(int rotation) {
   float angle = (float) Math.toRadians(rotation);
   rElbowRot = rotation;
   rElbowAA.angle = (float) Math.toRadians(rElbowRot);
   Human_r_elbow.getTransform(tmpTrans);
   tmpTrans.setRotation(rElbowAA);
   Human_r_elbow.setTransform(tmpTrans);
 }
 public void setLShoulderRot(int rotation) {
   lShoulderRot = rotation;
   lShoulderAA.angle = (float) Math.toRadians(lShoulderRot);
   Human_l_shoulder.getTransform(tmpTrans);
   tmpTrans.setRotation(lShoulderAA);
   Human_l_shoulder.setTransform(tmpTrans);
 }
 public void setLElbowRot(int rotation) {
   float angle = (float) Math.toRadians(rotation);
   lElbowRot = rotation;
   lElbowAA.angle = (float) Math.toRadians(lElbowRot);
   Human_l_elbow.getTransform(tmpTrans);
   tmpTrans.setRotation(lElbowAA);
   Human_l_elbow.setTransform(tmpTrans);
 }
 public void stateChanged(ChangeEvent e) {
   JSlider source = (JSlider) e.getSource();
   int value = source.getValue();
   if (source == rShoulderSlider) {
     setRShoulderRot(value);
     rShoulderSliderLabel.setText(Integer.toString(value));
   } else if (source == rElbowSlider) {
     setRElbowRot(value);
     rElbowSliderLabel.setText(Integer.toString(value));
   } else if (source == lShoulderSlider) {
     setLShoulderRot(value);
     lShoulderSliderLabel.setText(Integer.toString(value));
   } else if (source == lElbowSlider) {
     setLElbowRot(value);
     lElbowSliderLabel.setText(Integer.toString(value));
   }
 }
 BranchGroup createSceneGraph() {
   // Create the root of the branch graph
   BranchGroup objRoot = new BranchGroup();
   // Create a TransformGroup to scale the scene down by 3.5x
   // TODO: move view platform instead of scene using orbit behavior
   TransformGroup objScale = new TransformGroup();
   Transform3D scaleTrans = new Transform3D();
   scaleTrans.set(1 / 3.5f); // scale down by 3.5x
   objScale.setTransform(scaleTrans);
   objRoot.addChild(objScale);
   // Create a TransformGroup and initialize it to the
   // identity. Enable the TRANSFORM_WRITE capability so that
   // the mouse behaviors code can modify it at runtime. Add it to the
   // root of the subgraph.
   TransformGroup objTrans = new TransformGroup();
   objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
   objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
   objScale.addChild(objTrans);
   // Add the primitives to the scene
   createHuman(); // the human
   objTrans.addChild(Human_body);
   BoundingSphere bounds = new BoundingSphere(new Point3d(), 100.0);
   Background bg = new Background(new Color3f(1.0f, 1.0f, 1.0f));
   bg.setApplicationBounds(bounds);
   objTrans.addChild(bg);
   // set up the mouse rotation behavior
   MouseRotate mr = new MouseRotate();
   mr.setTransformGroup(objTrans);
   mr.setSchedulingBounds(bounds);
   mr.setFactor(0.007);
   objTrans.addChild(mr);
   // Set up the ambient light
   Color3f ambientColor = new Color3f(0.1f, 0.1f, 0.1f);
   AmbientLight ambientLightNode = new AmbientLight(ambientColor);
   ambientLightNode.setInfluencingBounds(bounds);
   objRoot.addChild(ambientLightNode);
   // Set up the directional lights
   Color3f light1Color = new Color3f(1.0f, 1.0f, 1.0f);
   Vector3f light1Direction = new Vector3f(0.0f, -0.2f, -1.0f);
   DirectionalLight light1 = new DirectionalLight(light1Color,
       light1Direction);
   light1.setInfluencingBounds(bounds);
   objRoot.addChild(light1);
   return objRoot;
 }
 public Human1() {
   this(false, 1.0f);
 }
 public Human1(boolean isApplication, float initOffScreenScale) {
   this.isApplication = isApplication;
   this.offScreenScale = initOffScreenScale;
 }
 public void init() {
   // set up a NumFormat object to print out float with only 3 fraction
   // digits
   nf = NumberFormat.getInstance();
   nf.setMaximumFractionDigits(3);
   setLayout(new BorderLayout());
   GraphicsConfiguration config = SimpleUniverse
       .getPreferredConfiguration();
   canvas = new Canvas3D(config);
   add("Center", canvas);
   u = new SimpleUniverse(canvas);
   if (isApplication) {
     offScreenCanvas = new OffScreenCanvas3D(config, true);
     // set the size of the off-screen canvas based on a scale
     // of the on-screen size
     Screen3D sOn = canvas.getScreen3D();
     Screen3D sOff = offScreenCanvas.getScreen3D();
     Dimension dim = sOn.getSize();
     dim.width *= offScreenScale;
     dim.height *= offScreenScale;
     sOff.setSize(dim);
     sOff.setPhysicalScreenWidth(sOn.getPhysicalScreenWidth()
         * offScreenScale);
     sOff.setPhysicalScreenHeight(sOn.getPhysicalScreenHeight()
         * offScreenScale);
     // attach the offscreen canvas to the view
     u.getViewer().getView().addCanvas3D(offScreenCanvas);
   }
   // Create a simple scene and attach it to the virtual universe
   BranchGroup scene = createSceneGraph();
   // This will move the ViewPlatform back a bit so the
   // objects in the scene can be viewed.
   u.getViewingPlatform().setNominalViewingTransform();
   u.addBranchGraph(scene);
   view = u.getViewer().getView();
   add("East", guiPanel());
 }
 // create a panel with a tabbed pane holding each of the edit panels
 JPanel guiPanel() {
   JPanel panel = new JPanel();
   panel.setLayout(new GridLayout(0, 1));
   // Human_r_shoulder rotation
   panel.add(new JLabel("Right Shoulder rotation"));
   rShoulderSlider = new JSlider(JSlider.HORIZONTAL, 0, 180, rShoulderRot);
   rShoulderSlider.addChangeListener(this);
   rShoulderSliderLabel = new JLabel(Integer.toString(rShoulderRot));
   panel.add(rShoulderSlider);
   panel.add(rShoulderSliderLabel);
   // Human_r_elbow rotation
   panel.add(new JLabel("Right Elbow rotation"));
   rElbowSlider = new JSlider(JSlider.HORIZONTAL, 0, 180, rElbowRot);
   rElbowSlider.addChangeListener(this);
   rElbowSliderLabel = new JLabel(Integer.toString(rElbowRot));
   panel.add(rElbowSlider);
   panel.add(rElbowSliderLabel);
   // Human_l_shoulder rotation
   panel.add(new JLabel("Left Shoulder rotation"));
   lShoulderSlider = new JSlider(JSlider.HORIZONTAL, 0, 180, lShoulderRot);
   lShoulderSlider.addChangeListener(this);
   lShoulderSliderLabel = new JLabel(Integer.toString(lShoulderRot));
   panel.add(lShoulderSlider);
   panel.add(lShoulderSliderLabel);
   // Human_l_elbow rotation
   panel.add(new JLabel("Left Elbow rotation"));
   lElbowSlider = new JSlider(JSlider.HORIZONTAL, 0, 180, lElbowRot);
   lElbowSlider.addChangeListener(this);
   lElbowSliderLabel = new JLabel(Integer.toString(lElbowRot));
   panel.add(lElbowSlider);
   panel.add(rElbowSliderLabel);
   if (isApplication) {
     JButton snapButton = new JButton(snapImageString);
     snapButton.setActionCommand(snapImageString);
     snapButton.addActionListener(this);
     panel.add(snapButton);
   }
   return panel;
 }
 public void destroy() {
   u.removeAllLocales();
 }
 // The following allows Human to be run as an application
 // as well as an applet
 //
 public static void main(String[] args) {
   float initOffScreenScale = 2.5f;
   for (int i = 0; i < args.length; i++) {
     if (args[i].equals("-s")) {
       if (args.length >= (i + 1)) {
         initOffScreenScale = Float.parseFloat(args[i + 1]);
         i++;
       }
     }
   }
   new MainFrame(new Human1(true, initOffScreenScale), 950, 600);
 }

} class OffScreenCanvas3D extends Canvas3D {

 OffScreenCanvas3D(GraphicsConfiguration graphicsConfiguration,
     boolean offScreen) {
   super(graphicsConfiguration, offScreen);
 }
 private BufferedImage doRender(int width, int height) {
   BufferedImage bImage = new BufferedImage(width, height,
       BufferedImage.TYPE_INT_RGB);
   ImageComponent2D buffer = new ImageComponent2D(
       ImageComponent.FORMAT_RGB, bImage);
   //buffer.setYUp(true);
   setOffScreenBuffer(buffer);
   renderOffScreenBuffer();
   waitForOffScreenRendering();
   bImage = getOffScreenBuffer().getImage();
   return bImage;
 }
 void snapImageFile(String filename, int width, int height) {
   BufferedImage bImage = doRender(width, height);
   /*
    * JAI: RenderedImage fImage = JAI.create("format", bImage,
    * DataBuffer.TYPE_BYTE); JAI.create("filestore", fImage, filename +
    * ".tif", "tiff", null);
    */
   /* No JAI: */
   try {
     FileOutputStream fos = new FileOutputStream(filename + ".jpg");
     BufferedOutputStream bos = new BufferedOutputStream(fos);
     JPEGImageEncoder jie = JPEGCodec.createJPEGEncoder(bos);
     JPEGEncodeParam param = jie.getDefaultJPEGEncodeParam(bImage);
     param.setQuality(1.0f, true);
     jie.setJPEGEncodeParam(param);
     jie.encode(bImage);
     bos.flush();
     fos.close();
   } catch (Exception e) {
     System.out.println(e);
   }
 }

}


      </source>
   
  
 
  



Simple DOOM style navigation of a 3D scene using Java 3D

   <source lang="java">

/**********************************************************

Copyright (C) 2001   Daniel Selman
First distributed with the book "Java 3D Programming"
by Daniel Selman and published by Manning Publications.
http://manning.ru/selman
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation, version 2.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
The license can be found on the WWW at:
http://www.fsf.org/copyleft/gpl.html
Or by writing to:
Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
Author can be contacted at:
Daniel Selman: daniel@selman.org
If you make changes you think others would like please
contact Daniel Selman.
**************************************************************/

import java.applet.Applet; import java.awt.AWTEvent; import java.awt.BorderLayout; import java.awt.Color; import java.awt.ruponent; import java.awt.GraphicsConfigTemplate; import java.awt.GraphicsDevice; import java.awt.GraphicsEnvironment; import java.awt.event.KeyEvent; import java.awt.image.BufferedImage; import java.io.File; import java.io.InputStream; import java.net.URL; import java.util.Enumeration; import java.util.Vector; import javax.media.j3d.Alpha; import javax.media.j3d.Appearance; import javax.media.j3d.AudioDevice; import javax.media.j3d.Background; import javax.media.j3d.BackgroundSound; import javax.media.j3d.Behavior; import javax.media.j3d.BoundingSphere; import javax.media.j3d.Bounds; import javax.media.j3d.BranchGroup; import javax.media.j3d.Canvas3D; import javax.media.j3d.ColoringAttributes; import javax.media.j3d.GeometryArray; import javax.media.j3d.GraphicsConfigTemplate3D; import javax.media.j3d.Group; import javax.media.j3d.ImageComponent2D; import javax.media.j3d.Locale; import javax.media.j3d.MediaContainer; import javax.media.j3d.Node; import javax.media.j3d.PhysicalBody; import javax.media.j3d.PhysicalEnvironment; import javax.media.j3d.PointSound; import javax.media.j3d.PolygonAttributes; import javax.media.j3d.QuadArray; import javax.media.j3d.Shape3D; import javax.media.j3d.Sound; import javax.media.j3d.Texture; import javax.media.j3d.TextureAttributes; import javax.media.j3d.Transform3D; import javax.media.j3d.TransformGroup; import javax.media.j3d.TransparencyAttributes; import javax.media.j3d.View; import javax.media.j3d.ViewPlatform; import javax.media.j3d.VirtualUniverse; import javax.media.j3d.WakeupCondition; import javax.media.j3d.WakeupCriterion; import javax.media.j3d.WakeupOnAWTEvent; import javax.media.j3d.WakeupOnCollisionEntry; import javax.media.j3d.WakeupOnCollisionExit; import javax.media.j3d.WakeupOnElapsedTime; import javax.media.j3d.WakeupOr; import javax.vecmath.Color3f; import javax.vecmath.Color4f; import javax.vecmath.Point2d; import javax.vecmath.Point2f; import javax.vecmath.Point3d; import javax.vecmath.Point3f; import javax.vecmath.Quat4f; import javax.vecmath.Vector3d; import javax.vecmath.Vector3f; import javax.vecmath.Vector4f; import com.sun.j3d.audioengines.javasound.JavaSoundMixer; import com.sun.j3d.loaders.Scene; import com.sun.j3d.loaders.objectfile.ObjectFile; import com.sun.j3d.utils.applet.MainFrame; import com.sun.j3d.utils.behaviors.interpolators.RotPosScaleTCBSplinePathInterpolator; import com.sun.j3d.utils.behaviors.interpolators.TCBKeyFrame; import com.sun.j3d.utils.geometry.Cone; import com.sun.j3d.utils.geometry.Primitive; import com.sun.j3d.utils.image.TextureLoader; /**

* Simple DOOM style navigation of a 3D scene using Java 3D. The scene
* description is loaded from a GIF file where different colors denote objects
* in the 3D scene. The example includes: simple (manual) collision detection,
* texture animation, keyboard navigation.
*/

public class KeyNavigateTest extends Java3dApplet implements CollisionDetector {

 private static int m_kWidth = 300;
 private static int m_kHeight = 300;
 private static final String m_szMapName = new String("small_map.gif");
 private float FLOOR_WIDTH = 256;
 private float FLOOR_LENGTH = 256;
 private final int m_ColorWall = new Color(0, 0, 0).getRGB();
 private final int m_ColorGuard = new Color(255, 0, 0).getRGB();
 private final int m_ColorLight = new Color(255, 255, 0).getRGB();
 private final int m_ColorBookcase = new Color(0, 255, 0).getRGB();
 private final int m_ColorWater = new Color(0, 0, 255).getRGB();
 private Vector3d m_MapSquareSize = null;
 private Appearance m_WallAppearance = null;
 private Appearance m_GuardAppearance = null;
 private Appearance m_BookcaseAppearance = null;
 private Appearance m_WaterAppearance = null;
 private BufferedImage m_MapImage = null;
 private final double m_kFloorLevel = -20;
 private final double m_kCeilingHeight = 50;
 private final double m_kCeilingLevel = m_kFloorLevel + m_kCeilingHeight;
 private Vector3d m_Translation = new Vector3d();
 public KeyNavigateTest() {
   initJava3d();
 }
 protected double getScale() {
   return 0.05;
 }
 protected int getCanvas3dWidth(Canvas3D c3d) {
   return m_kWidth - 10;
 }
 protected int getCanvas3dHeight(Canvas3D c3d) {
   return m_kHeight - 10;
 }
 // edit the positions of the clipping
 // planes so we don"t clip on the front
 // plane prematurely
 protected double getBackClipDistance() {
   return 20.0;
 }
 protected double getFrontClipDistance() {
   return 0.05;
 }
 // we create 2 TransformGroups above the ViewPlatform:
 // the first merely applies a scale, while the second is
 // manipulated using the KeyBehavior
 public TransformGroup[] getViewTransformGroupArray() {
   TransformGroup[] tgArray = new TransformGroup[2];
   tgArray[0] = new TransformGroup();
   tgArray[1] = new TransformGroup();
   Transform3D t3d = new Transform3D();
   t3d.setScale(getScale());
   t3d.invert();
   tgArray[0].setTransform(t3d);
   // ensure the Behavior can access the TG
   tgArray[1].setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
   // create the KeyBehavior and attach
   KeyCollisionBehavior keyBehavior = new KeyCollisionBehavior(tgArray[1],
       this);
   keyBehavior.setSchedulingBounds(getApplicationBounds());
   keyBehavior.setMovementRate(0.7);
   tgArray[1].addChild(keyBehavior);
   return tgArray;
 }
 protected BranchGroup createSceneBranchGroup() {
   BranchGroup objRoot = super.createSceneBranchGroup();
   TransformGroup objTrans = new TransformGroup();
   objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
   objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
   createMap(objTrans);
   createFloor(objTrans);
   createCeiling(objTrans);
   objRoot.addChild(objTrans);
   return objRoot;
 }
 public Group createFloor(Group g) {
   System.out.println("Creating floor");
   Group floorGroup = new Group();
   Land floorTile = null;
   // use a shared Appearance so we only store 1 copy of the texture
   Appearance app = new Appearance();
   g.addChild(floorGroup);
   final double kNumTiles = 6;
   for (double x = -FLOOR_WIDTH + FLOOR_WIDTH / (2 * kNumTiles); x < FLOOR_WIDTH; x = x
       + FLOOR_WIDTH / kNumTiles) {
     for (double z = -FLOOR_LENGTH + FLOOR_LENGTH / (2 * kNumTiles); z < FLOOR_LENGTH; z = z
         + FLOOR_LENGTH / kNumTiles) {
       floorTile = new Land(this, g, ComplexObject.GEOMETRY
           | ComplexObject.TEXTURE);
       floorTile.createObject(app, new Vector3d(x, m_kFloorLevel, z),
           new Vector3d(FLOOR_WIDTH / (2 * kNumTiles), 1,
               FLOOR_LENGTH / (2 * kNumTiles)), "floor.gif",
           null, null);
     }
   }
   return floorGroup;
 }
 public Group createCeiling(Group g) {
   System.out.println("Creating ceiling");
   Group ceilingGroup = new Group();
   Land ceilingTile = null;
   // because we are technically viewing the ceiling from below
   // we want to switch the normals using a PolygonAttributes.
   Appearance app = new Appearance();
   app.setPolygonAttributes(new PolygonAttributes(
       PolygonAttributes.POLYGON_FILL, PolygonAttributes.CULL_NONE, 0,
       false));
   g.addChild(ceilingGroup);
   final double kNumTiles = 6;
   for (double x = -FLOOR_WIDTH + FLOOR_WIDTH / (2 * kNumTiles); x < FLOOR_WIDTH; x = x
       + FLOOR_WIDTH / kNumTiles) {
     for (double z = -FLOOR_LENGTH + FLOOR_LENGTH / (2 * kNumTiles); z < FLOOR_LENGTH; z = z
         + FLOOR_LENGTH / kNumTiles) {
       ceilingTile = new Land(this, g, ComplexObject.GEOMETRY
           | ComplexObject.TEXTURE);
       ceilingTile.createObject(app, new Vector3d(x, m_kCeilingLevel,
           z), new Vector3d(FLOOR_WIDTH / (2 * kNumTiles), 1,
           FLOOR_LENGTH / (2 * kNumTiles)), "ceiling.gif", null,
           null);
     }
   }
   return ceilingGroup;
 }
 Point3d convertToWorldCoordinate(int nPixelX, int nPixelY) {
   Point3d point3d = new Point3d();
   Vector3d squareSize = getMapSquareSize();
   // range from 0 to 1
   point3d.x = nPixelX * squareSize.x;
   point3d.x -= FLOOR_WIDTH;
   point3d.z = nPixelY * squareSize.z;
   point3d.z -= FLOOR_LENGTH;
   point3d.y = 0;
   return point3d;
 }
 Point3d convertToWorldCoordinatesPixelCenter(int nPixelX, int nPixelY) {
   Point3d point3d = convertToWorldCoordinate(nPixelX, nPixelY);
   Vector3d squareSize = getMapSquareSize();
   point3d.x += squareSize.x / 2;
   point3d.z += squareSize.z / 2;
   return point3d;
 }
 Point2d convertToMapCoordinate(Vector3d worldCoord) {
   Point2d point2d = new Point2d();
   Vector3d squareSize = getMapSquareSize();
   point2d.x = (worldCoord.x + FLOOR_WIDTH) / squareSize.x;
   point2d.y = (worldCoord.z + FLOOR_LENGTH) / squareSize.z;
   return point2d;
 }
 public boolean isCollision(Transform3D t3d, boolean bViewSide) {
   // get the translation
   t3d.get(m_Translation);
   // we need to scale up by the scale that was
   // applied to the root TG on the view side of the scenegraph
   if (bViewSide != false)
     m_Translation.scale(1.0 / getScale());
   Vector3d mapSquareSize = getMapSquareSize();
   // first check that we are still inside the "world"
   if (m_Translation.x < -FLOOR_WIDTH + mapSquareSize.x
       || m_Translation.x > FLOOR_WIDTH - mapSquareSize.x
       || m_Translation.y < -FLOOR_LENGTH + mapSquareSize.y
       || m_Translation.y > FLOOR_LENGTH - mapSquareSize.y)
     return true;
   if (bViewSide != false)
     // then do a pixel based look up using the map
     return isCollision(m_Translation);
   return false;
 }
 // returns true if the given x,z location in the world corresponds to a wall
 // section
 protected boolean isCollision(Vector3d worldCoord) {
   Point2d point = convertToMapCoordinate(worldCoord);
   int nImageWidth = m_MapImage.getWidth();
   int nImageHeight = m_MapImage.getHeight();
   // outside of image
   if (point.x < 0 || point.x >= nImageWidth || point.y < 0
       || point.y >= nImageHeight)
     return true;
   int color = m_MapImage.getRGB((int) point.x, (int) point.y);
   // we can"t walk through walls or bookcases
   return (color == m_ColorWall || color == m_ColorBookcase);
 }
 public Group createMap(Group g) {
   System.out.println("Creating map items");
   Group mapGroup = new Group();
   g.addChild(mapGroup);
   Texture tex = new TextureLoader(m_szMapName, this).getTexture();
   m_MapImage = ((ImageComponent2D) tex.getImage(0)).getImage();
   float imageWidth = m_MapImage.getWidth();
   float imageHeight = m_MapImage.getHeight();
   FLOOR_WIDTH = imageWidth * 8;
   FLOOR_LENGTH = imageHeight * 8;
   for (int nPixelX = 1; nPixelX < imageWidth - 1; nPixelX++) {
     for (int nPixelY = 1; nPixelY < imageWidth - 1; nPixelY++)
       createMapItem(mapGroup, nPixelX, nPixelY);
     float percentDone = 100 * (float) nPixelX
         / (float) (imageWidth - 2);
     System.out.println("   " + (int) (percentDone) + "%");
   }
   createExternalWall(mapGroup);
   return mapGroup;
 }
 void createMapItem(Group mapGroup, int nPixelX, int nPixelY) {
   int color = m_MapImage.getRGB((int) nPixelX, (int) nPixelY);
   if (color == m_ColorWall)
     createWall(mapGroup, nPixelX, nPixelY);
   else if (color == m_ColorGuard)
     createGuard(mapGroup, nPixelX, nPixelY);
   else if (color == m_ColorLight)
     createLight(mapGroup, nPixelX, nPixelY);
   else if (color == m_ColorBookcase)
     createBookcase(mapGroup, nPixelX, nPixelY);
   else if (color == m_ColorWater)
     createWater(mapGroup, nPixelX, nPixelY);
 }
 Vector3d getMapSquareSize() {
   if (m_MapSquareSize == null) {
     double imageWidth = m_MapImage.getWidth();
     double imageHeight = m_MapImage.getHeight();
     m_MapSquareSize = new Vector3d(2 * FLOOR_WIDTH / imageWidth, 0, 2
         * FLOOR_LENGTH / imageHeight);
   }
   return m_MapSquareSize;
 }
 void createWall(Group mapGroup, int nPixelX, int nPixelY) {
   Point3d point = convertToWorldCoordinatesPixelCenter(nPixelX, nPixelY);
   if (m_WallAppearance == null)
     m_WallAppearance = new Appearance();
   Vector3d squareSize = getMapSquareSize();
   Cuboid wall = new Cuboid(this, mapGroup, ComplexObject.GEOMETRY
       | ComplexObject.TEXTURE);
   wall
       .createObject(m_WallAppearance, new Vector3d(point.x,
           m_kFloorLevel, point.z), new Vector3d(squareSize.x / 2,
           m_kCeilingHeight / 2, squareSize.z / 2), "wall.gif",
           null, null);
 }
 void createExternalWall(Group mapGroup) {
   Vector3d squareSize = getMapSquareSize();
   Appearance app = new Appearance();
   app.setColoringAttributes(new ColoringAttributes(new Color3f(
       132f / 255f, 0, 66f / 255f), ColoringAttributes.FASTEST));
   int imageWidth = m_MapImage.getWidth();
   int imageHeight = m_MapImage.getHeight();
   Point3d topLeft = convertToWorldCoordinatesPixelCenter(0, 0);
   Point3d bottomRight = convertToWorldCoordinatesPixelCenter(
       imageWidth - 1, imageHeight - 1);
   // top
   Cuboid wall = new Cuboid(this, mapGroup, ComplexObject.GEOMETRY);
   wall.createObject(app, new Vector3d(0, m_kFloorLevel, topLeft.z),
       new Vector3d(squareSize.x * imageWidth / 2,
           m_kCeilingHeight / 2, squareSize.z / 2), null, null,
       null);
   // bottom
   wall = new Cuboid(this, mapGroup, ComplexObject.GEOMETRY);
   wall.createObject(app, new Vector3d(0, m_kFloorLevel, bottomRight.z),
       new Vector3d(squareSize.x * imageWidth / 2,
           m_kCeilingHeight / 2, squareSize.z / 2), null, null,
       null);
   // left
   wall = new Cuboid(this, mapGroup, ComplexObject.GEOMETRY);
   wall.createObject(app, new Vector3d(topLeft.x, m_kFloorLevel, 0),
       new Vector3d(squareSize.x / 2, m_kCeilingHeight / 2,
           squareSize.z * imageHeight / 2), null, null, null);
   // right
   wall = new Cuboid(this, mapGroup, ComplexObject.GEOMETRY);
   wall.createObject(app, new Vector3d(bottomRight.x, m_kFloorLevel, 0),
       new Vector3d(squareSize.x / 2, m_kCeilingHeight / 2,
           squareSize.z * imageHeight / 2), null, null, null);
 }
 void createGuard(Group mapGroup, int nPixelX, int nPixelY) {
   Point3d point = convertToWorldCoordinatesPixelCenter(nPixelX, nPixelY);
   if (m_GuardAppearance == null)
     m_GuardAppearance = new Appearance();
   Vector3d squareSize = getMapSquareSize();
   Guard guard = new Guard(this, mapGroup, ComplexObject.GEOMETRY, this);
   guard.createObject(m_GuardAppearance, new Vector3d(point.x,
       (m_kFloorLevel + m_kCeilingLevel) / 2, point.z), new Vector3d(
       1, 1, 1), null, null, null);
 }
 void createLight(Group mapGroup, int nPixelX, int nPixelY) {
   Point3d point = convertToWorldCoordinatesPixelCenter(nPixelX, nPixelY);
   // we do not share an Appearance for lights
   // or they all animate in synch...
   final double lightHeight = m_kCeilingHeight / 1.5;
   Light light = new Light(this, mapGroup, ComplexObject.GEOMETRY
       | ComplexObject.TEXTURE);
   light.createObject(new Appearance(), new Vector3d(point.x,
       m_kFloorLevel + lightHeight / 2, point.z), new Vector3d(5,
       lightHeight, 5), "light.gif", null, null);
 }
 void createWater(Group mapGroup, int nPixelX, int nPixelY) {
   Point3d point = convertToWorldCoordinatesPixelCenter(nPixelX, nPixelY);
   if (m_WaterAppearance == null) {
     m_WaterAppearance = new Appearance();
     m_WaterAppearance.setPolygonAttributes(new PolygonAttributes(
         PolygonAttributes.POLYGON_FILL,
         PolygonAttributes.CULL_NONE, 0, false));
     m_WaterAppearance
         .setTransparencyAttributes(new TransparencyAttributes(
             TransparencyAttributes.BLENDED, 1.0f));
     m_WaterAppearance.setTextureAttributes(new TextureAttributes(
         TextureAttributes.REPLACE, new Transform3D(), new Color4f(
             0, 0, 0, 1), TextureAttributes.FASTEST));
   }
   Land water = new Land(this, mapGroup, ComplexObject.GEOMETRY
       | ComplexObject.TEXTURE);
   water.createObject(m_WaterAppearance, new Vector3d(point.x,
       m_kFloorLevel + 0.1, point.z), new Vector3d(40, 1, 40),
       "water.gif", null, null);
 }
 void createBookcase(Group mapGroup, int nPixelX, int nPixelY) {
   Point3d point = convertToWorldCoordinatesPixelCenter(nPixelX, nPixelY);
   if (m_BookcaseAppearance == null)
     m_BookcaseAppearance = new Appearance();
   Vector3d squareSize = getMapSquareSize();
   Cuboid bookcase = new Cuboid(this, mapGroup, ComplexObject.GEOMETRY
       | ComplexObject.TEXTURE);
   bookcase.createObject(m_BookcaseAppearance, new Vector3d(point.x,
       m_kFloorLevel, point.z), new Vector3d(squareSize.x / 2,
       m_kCeilingHeight / 2.7, squareSize.z / 2), "bookcase.gif",
       null, null);
 }
 public static void main(String[] args) {
   KeyNavigateTest keyTest = new KeyNavigateTest();
   keyTest.saveCommandLineArguments(args);
   new MainFrame(keyTest, m_kWidth, m_kHeight);
 }

} /*******************************************************************************

* Copyright (C) 2001 Daniel Selman
* 
* First distributed with the book "Java 3D Programming" by Daniel Selman and
* published by Manning Publications. http://manning.ru/selman
* 
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, version 2.
* 
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
* 
* The license can be found on the WWW at: http://www.fsf.org/copyleft/gpl.html
* 
* Or by writing to: Free Software Foundation, Inc., 59 Temple Place - Suite
* 330, Boston, MA 02111-1307, USA.
* 
* Authors can be contacted at: Daniel Selman: daniel@selman.org
* 
* If you make changes you think others would like, please contact one of the
* authors or someone at the www.j3d.org web site.
******************************************************************************/

//***************************************************************************** /**

* Java3dApplet
* 
* Base class for defining a Java 3D applet. Contains some useful methods for
* defining views and scenegraphs etc.
* 
* @author Daniel Selman
* @version 1.0
*/

//***************************************************************************** abstract class Java3dApplet extends Applet {

 public static int m_kWidth = 300;
 public static int m_kHeight = 300;
 protected String[] m_szCommandLineArray = null;
 protected VirtualUniverse m_Universe = null;
 protected BranchGroup m_SceneBranchGroup = null;
 protected Bounds m_ApplicationBounds = null;
 //  protected com.tornadolabs.j3dtree.Java3dTree m_Java3dTree = null;
 public Java3dApplet() {
 }
 public boolean isApplet() {
   try {
     System.getProperty("user.dir");
     System.out.println("Running as Application.");
     return false;
   } catch (Exception e) {
   }
   System.out.println("Running as Applet.");
   return true;
 }
 public URL getWorkingDirectory() throws java.net.MalformedURLException {
   URL url = null;
   try {
     File file = new File(System.getProperty("user.dir"));
     System.out.println("Running as Application:");
     System.out.println("   " + file.toURL());
     return file.toURL();
   } catch (Exception e) {
   }
   System.out.println("Running as Applet:");
   System.out.println("   " + getCodeBase());
   return getCodeBase();
 }
 public VirtualUniverse getVirtualUniverse() {
   return m_Universe;
 }
 //public com.tornadolabs.j3dtree.Java3dTree getJ3dTree() {
 //return m_Java3dTree;
 //  }
 public Locale getFirstLocale() {
   java.util.Enumeration e = m_Universe.getAllLocales();
   if (e.hasMoreElements() != false)
     return (Locale) e.nextElement();
   return null;
 }
 protected Bounds getApplicationBounds() {
   if (m_ApplicationBounds == null)
     m_ApplicationBounds = createApplicationBounds();
   return m_ApplicationBounds;
 }
 protected Bounds createApplicationBounds() {
   m_ApplicationBounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0),
       100.0);
   return m_ApplicationBounds;
 }
 protected Background createBackground() {
   Background back = new Background(new Color3f(0.9f, 0.9f, 0.9f));
   back.setApplicationBounds(createApplicationBounds());
   return back;
 }
 public void initJava3d() {
   //  m_Java3dTree = new com.tornadolabs.j3dtree.Java3dTree();
   m_Universe = createVirtualUniverse();
   Locale locale = createLocale(m_Universe);
   BranchGroup sceneBranchGroup = createSceneBranchGroup();
   ViewPlatform vp = createViewPlatform();
   BranchGroup viewBranchGroup = createViewBranchGroup(
       getViewTransformGroupArray(), vp);
   createView(vp);
   Background background = createBackground();
   if (background != null)
     sceneBranchGroup.addChild(background);
   //    m_Java3dTree.recursiveApplyCapability(sceneBranchGroup);
   //  m_Java3dTree.recursiveApplyCapability(viewBranchGroup);
   locale.addBranchGraph(sceneBranchGroup);
   addViewBranchGroup(locale, viewBranchGroup);
   onDoneInit();
 }
 protected void onDoneInit() {
   //  m_Java3dTree.updateNodes(m_Universe);
 }
 protected double getScale() {
   return 1.0;
 }
 public TransformGroup[] getViewTransformGroupArray() {
   TransformGroup[] tgArray = new TransformGroup[1];
   tgArray[0] = new TransformGroup();
   // move the camera BACK a little...
   // note that we have to invert the matrix as
   // we are moving the viewer
   Transform3D t3d = new Transform3D();
   t3d.setScale(getScale());
   t3d.setTranslation(new Vector3d(0.0, 0.0, -20.0));
   t3d.invert();
   tgArray[0].setTransform(t3d);
   return tgArray;
 }
 protected void addViewBranchGroup(Locale locale, BranchGroup bg) {
   locale.addBranchGraph(bg);
 }
 protected Locale createLocale(VirtualUniverse u) {
   return new Locale(u);
 }
 protected BranchGroup createSceneBranchGroup() {
   m_SceneBranchGroup = new BranchGroup();
   return m_SceneBranchGroup;
 }
 protected View createView(ViewPlatform vp) {
   View view = new View();
   PhysicalBody pb = createPhysicalBody();
   PhysicalEnvironment pe = createPhysicalEnvironment();
   AudioDevice audioDevice = createAudioDevice(pe);
   if (audioDevice != null) {
     pe.setAudioDevice(audioDevice);
     audioDevice.initialize();
   }
   view.setPhysicalEnvironment(pe);
   view.setPhysicalBody(pb);
   if (vp != null)
     view.attachViewPlatform(vp);
   view.setBackClipDistance(getBackClipDistance());
   view.setFrontClipDistance(getFrontClipDistance());
   Canvas3D c3d = createCanvas3D();
   view.addCanvas3D(c3d);
   addCanvas3D(c3d);
   return view;
 }
 protected PhysicalBody createPhysicalBody() {
   return new PhysicalBody();
 }
 protected AudioDevice createAudioDevice(PhysicalEnvironment pe) {
   JavaSoundMixer javaSoundMixer = new JavaSoundMixer(pe);
   if (javaSoundMixer == null)
     System.out.println("create of audiodevice failed");
   return javaSoundMixer;
 }
 protected PhysicalEnvironment createPhysicalEnvironment() {
   return new PhysicalEnvironment();
 }
 protected float getViewPlatformActivationRadius() {
   return 100;
 }
 protected ViewPlatform createViewPlatform() {
   ViewPlatform vp = new ViewPlatform();
   vp.setViewAttachPolicy(View.RELATIVE_TO_FIELD_OF_VIEW);
   vp.setActivationRadius(getViewPlatformActivationRadius());
   return vp;
 }
 protected Canvas3D createCanvas3D() {
   GraphicsConfigTemplate3D gc3D = new GraphicsConfigTemplate3D();
   gc3D.setSceneAntialiasing(GraphicsConfigTemplate.PREFERRED);
   GraphicsDevice gd[] = GraphicsEnvironment.getLocalGraphicsEnvironment()
       .getScreenDevices();
   Canvas3D c3d = new Canvas3D(gd[0].getBestConfiguration(gc3D));
   c3d.setSize(getCanvas3dWidth(c3d), getCanvas3dHeight(c3d));
   return c3d;
 }
 protected int getCanvas3dWidth(Canvas3D c3d) {
   return m_kWidth;
 }
 protected int getCanvas3dHeight(Canvas3D c3d) {
   return m_kHeight;
 }
 protected double getBackClipDistance() {
   return 100.0;
 }
 protected double getFrontClipDistance() {
   return 1.0;
 }
 protected BranchGroup createViewBranchGroup(TransformGroup[] tgArray,
     ViewPlatform vp) {
   BranchGroup vpBranchGroup = new BranchGroup();
   if (tgArray != null && tgArray.length > 0) {
     Group parentGroup = vpBranchGroup;
     TransformGroup curTg = null;
     for (int n = 0; n < tgArray.length; n++) {
       curTg = tgArray[n];
       parentGroup.addChild(curTg);
       parentGroup = curTg;
     }
     tgArray[tgArray.length - 1].addChild(vp);
   } else
     vpBranchGroup.addChild(vp);
   return vpBranchGroup;
 }
 protected void addCanvas3D(Canvas3D c3d) {
   setLayout(new BorderLayout());
   add(c3d, BorderLayout.CENTER);
   doLayout();
 }
 protected VirtualUniverse createVirtualUniverse() {
   return new VirtualUniverse();
 }
 protected void saveCommandLineArguments(String[] szArgs) {
   m_szCommandLineArray = szArgs;
 }
 protected String[] getCommandLineArguments() {
   return m_szCommandLineArray;
 }

} class KeyCollisionBehavior extends KeyBehavior {

 private CollisionChecker m_CollisionChecker = null;
 public KeyCollisionBehavior(TransformGroup tg,
     CollisionDetector collisionDetector) {
   super(tg);
   m_CollisionChecker = new CollisionChecker(tg, collisionDetector, true);
 }
 protected void updateTransform() {
   if (m_CollisionChecker.isCollision(transform3D) == false)
     transformGroup.setTransform(transform3D);
 }
 // dissallow rotation up or down
 protected void altMove(int keycode) {
 }
 // dissallow moving up or down
 protected void controlMove(int keycode) {
 }

} class Guard extends ComplexObject {

 private CollisionDetector m_CollisionDetector = null;
 public Guard(Component comp, Group g, int nFlags, CollisionDetector detector) {
   super(comp, g, nFlags);
   m_CollisionDetector = detector;
 }
 protected Group createGeometryGroup(Appearance app, Vector3d position,
     Vector3d scale, String szTextureFile, String szSoundFile) {
   TransformGroup tg = new TransformGroup();
   tg.addChild(new Cone(5, 30));
   attachBehavior(new RandomWalkBehavior(getBehaviorTransformGroup(),
       m_CollisionDetector));
   return tg;
 }

} interface CollisionDetector {

 public boolean isCollision(Transform3D t3d, boolean bViewSide);

} class CollisionChecker {

 CollisionDetector m_Detector = null;
 Transform3D m_ToWorld = null;
 Transform3D m_Transform3D = null;
 Node m_Node = null;
 boolean m_bViewSide = false;
 public CollisionChecker(Node node, CollisionDetector detector,
     boolean bViewSide) {
   m_Detector = detector;
   m_Node = node;
   m_bViewSide = bViewSide;
 }
 public boolean isCollision(Transform3D t3d) {
   return m_Detector.isCollision(t3d, m_bViewSide);
 }

} class Light extends ComplexObject {

 private TextureAttributes m_TextureAttributes = null;
 public Light(Component comp, Group g, int nFlags) {
   super(comp, g, nFlags);
 }
 protected Group createGeometryGroup(Appearance app, Vector3d position,
     Vector3d scale, String szTextureFile, String szSoundFile) {
   Group g = new Group();
   app.setPolygonAttributes(new PolygonAttributes(
       PolygonAttributes.POLYGON_FILL, PolygonAttributes.CULL_NONE, 0,
       false));
   app.setTransparencyAttributes(new TransparencyAttributes(
       TransparencyAttributes.BLENDED, 1.0f));
   m_TextureAttributes = new TextureAttributes(TextureAttributes.REPLACE,
       new Transform3D(), new Color4f(0, 0, 0, 1),
       TextureAttributes.FASTEST);
   app.setTextureAttributes(m_TextureAttributes);
   if ((m_nFlags & ComplexObject.TEXTURE) == ComplexObject.TEXTURE)
     setTexture(app, szTextureFile);
   Cone cone = new Cone(1, 1, Primitive.GENERATE_TEXTURE_COORDS, app);
   g.addChild(cone);
   attachBehavior(new TextureAnimationBehavior(m_TextureAttributes));
   return g;
 }

} class TextureAnimationBehavior extends Behavior {

 // the wake up condition for the behavior
 protected WakeupCondition m_WakeupCondition = null;
 protected Transform3D m_Transform3D = null;
 protected TextureAttributes m_TextureAttributes = null;
 protected double rotY = 0;
 public TextureAnimationBehavior(TextureAttributes texAttribs) {
   m_TextureAttributes = texAttribs;
   m_Transform3D = new Transform3D();
   m_TextureAttributes
       .setCapability(TextureAttributes.ALLOW_TRANSFORM_WRITE);
   // create the WakeupCriterion for the behavior
   WakeupCriterion criterionArray[] = new WakeupCriterion[1];
   criterionArray[0] = new WakeupOnElapsedTime(300);
   // save the WakeupCriterion for the behavior
   m_WakeupCondition = new WakeupOr(criterionArray);
 }
 public void initialize() {
   // apply the initial WakeupCriterion
   wakeupOn(m_WakeupCondition);
 }
 public void processStimulus(java.util.Enumeration criteria) {
   while (criteria.hasMoreElements()) {
     WakeupCriterion wakeUp = (WakeupCriterion) criteria.nextElement();
     if (wakeUp instanceof WakeupOnElapsedTime) {
       rotY += Utils.getRandomNumber(0.01, 0.01);
       m_Transform3D.rotY(rotY);
       m_TextureAttributes.setTextureTransform(m_Transform3D);
     }
   }
   // assign the next WakeUpCondition, so we are notified again
   wakeupOn(m_WakeupCondition);
 }

} // this class implements a simple behavior that // calculates and prints the size of an object // based on the vertices in its GeometryArray class RandomWalkBehavior extends Behavior {

 // the wake up condition for the behavior
 protected WakeupCondition m_WakeupCondition = null;
 protected TransformGroup m_TransformGroup = null;
 protected Transform3D m_Transform3D = null;
 protected Vector3d TargetVector3d = null;
 protected Vector3d CurrentVector3d = null;
 private final double m_MovementX = 2;
 private final double m_MovementY = 0;
 private final double m_MovementZ = 2;
 private int m_nFrameCount = 0;
 private CollisionChecker m_CollisionChecker = null;
 public RandomWalkBehavior(TransformGroup tg, CollisionDetector detector) {
   m_TransformGroup = tg;
   m_CollisionChecker = new CollisionChecker(tg, detector, false);
   m_Transform3D = new Transform3D();
   TargetVector3d = new Vector3d();
   CurrentVector3d = new Vector3d();
   // create the WakeupCriterion for the behavior
   WakeupCriterion criterionArray[] = new WakeupCriterion[1];
   criterionArray[0] = new WakeupOnElapsedTime(100);
   // save the WakeupCriterion for the behavior
   m_WakeupCondition = new WakeupOr(criterionArray);
 }
 public void initialize() {
   // apply the initial WakeupCriterion
   wakeupOn(m_WakeupCondition);
 }
 public void processStimulus(java.util.Enumeration criteria) {
   while (criteria.hasMoreElements()) {
     WakeupCriterion wakeUp = (WakeupCriterion) criteria.nextElement();
     if (wakeUp instanceof WakeupOnElapsedTime) {
       if (m_nFrameCount % 100 == 0) {
         // generate a random direction for movement
         TargetVector3d.x = m_MovementX
             * Utils.getRandomNumber(0, 1);
         TargetVector3d.y = m_MovementY
             * Utils.getRandomNumber(0, 1);
         TargetVector3d.z = m_MovementZ
             * Utils.getRandomNumber(0, 1);
       }
       CurrentVector3d.x += TargetVector3d.x
           * Utils.getRandomNumber(1, 0.1);
       CurrentVector3d.y += TargetVector3d.y
           * Utils.getRandomNumber(1, 0.1);
       CurrentVector3d.z += TargetVector3d.z
           * Utils.getRandomNumber(1, 0.1);
       m_Transform3D.setTranslation(CurrentVector3d);
       if (m_CollisionChecker.isCollision(m_Transform3D) == false)
         m_TransformGroup.setTransform(m_Transform3D);
       m_nFrameCount++;
     }
   }
   // assign the next WakeUpCondition, so we are notified again
   wakeupOn(m_WakeupCondition);
 }

} abstract class ComplexObject extends BranchGroup {

 protected Group m_ParentGroup = null;
 protected int m_nFlags = 0;
 protected BackgroundSound m_CollideSound = null;
 protected Component m_Component = null;
 protected TransformGroup m_TransformGroup = null;
 protected TransformGroup m_BehaviorTransformGroup = null;
 public static final int SOUND = 0x001;
 public static final int GEOMETRY = 0x002;
 public static final int TEXTURE = 0x004;
 public static final int COLLISION = 0x008;
 public static final int COLLISION_SOUND = 0x010;
 public ComplexObject(Component comp, Group group, int nFlags) {
   m_ParentGroup = group;
   m_nFlags = nFlags;
   m_Component = comp;
 }
 public Bounds getGeometryBounds() {
   return new BoundingSphere(new Point3d(0, 0, 0), 100);
 }
 private MediaContainer loadSoundFile(String szFile) {
   try {
     File file = new File(System.getProperty("user.dir"));
     URL url = file.toURL();
     URL soundUrl = new URL(url, szFile);
     return new MediaContainer(soundUrl);
   } catch (Exception e) {
     System.err.println("Error could not load sound file: " + e);
     System.exit(-1);
   }
   return null;
 }
 protected void setTexture(Appearance app, String szFile) {
   Texture tex = new TextureLoader(szFile, m_Component).getTexture();
   app.setTexture(tex);
 }
 abstract protected Group createGeometryGroup(Appearance app,
     Vector3d position, Vector3d scale, String szTextureFile,
     String szSoundFile);
 protected Group loadGeometryGroup(String szModel, Appearance app)
     throws java.io.FileNotFoundException {
   // load the object file
   Scene scene = null;
   Shape3D shape = null;
   // read in the geometry information from the data file
   ObjectFile objFileloader = new ObjectFile(ObjectFile.RESIZE);
   scene = objFileloader.load(szModel);
   // retrieve the Shape3D object from the scene
   BranchGroup branchGroup = scene.getSceneGroup();
   shape = (Shape3D) branchGroup.getChild(0);
   shape.setAppearance(app);
   return branchGroup;
 }
 protected int getSoundLoop(boolean bCollide) {
   return 1;
 }
 protected float getSoundPriority(boolean bCollide) {
   return 1.0f;
 }
 protected float getSoundInitialGain(boolean bCollide) {
   return 1.0f;
 }
 protected boolean getSoundInitialEnable(boolean bCollide) {
   return true;
 }
 protected boolean getSoundContinuousEnable(boolean bCollide) {
   return false;
 }
 protected Bounds getSoundSchedulingBounds(boolean bCollide) {
   return new BoundingSphere(new Point3d(0, 0, 0), 1.0);
 }
 protected boolean getSoundReleaseEnable(boolean bCollide) {
   return true;
 }
 protected Point2f[] getSoundDistanceGain(boolean bCollide) {
   return null;
 }
 protected void setSoundAttributes(Sound sound, boolean bCollide) {
   sound.setCapability(Sound.ALLOW_ENABLE_WRITE);
   sound.setCapability(Sound.ALLOW_ENABLE_READ);
   sound.setSchedulingBounds(getSoundSchedulingBounds(bCollide));
   sound.setEnable(getSoundInitialEnable(bCollide));
   sound.setLoop(getSoundLoop(bCollide));
   sound.setPriority(getSoundPriority(bCollide));
   sound.setInitialGain(getSoundInitialGain(bCollide));
   sound.setContinuousEnable(getSoundContinuousEnable(bCollide));
   sound.setReleaseEnable(bCollide);
   if (sound instanceof PointSound) {
     PointSound pointSound = (PointSound) sound;
     pointSound.setInitialGain(getSoundInitialGain(bCollide));
     Point2f[] gainArray = getSoundDistanceGain(bCollide);
     if (gainArray != null)
       pointSound.setDistanceGain(gainArray);
   }
 }
 public Group createObject(Appearance app, Vector3d position,
     Vector3d scale, String szTextureFile, String szSoundFile,
     String szCollisionSound) {
   m_TransformGroup = new TransformGroup();
   Transform3D t3d = new Transform3D();
   t3d.setScale(scale);
   t3d.setTranslation(position);
   m_TransformGroup.setTransform(t3d);
   m_BehaviorTransformGroup = new TransformGroup();
   if ((m_nFlags & GEOMETRY) == GEOMETRY)
     m_BehaviorTransformGroup.addChild(createGeometryGroup(app,
         position, scale, szTextureFile, szSoundFile));
   if ((m_nFlags & SOUND) == SOUND) {
     MediaContainer media = loadSoundFile(szSoundFile);
     PointSound pointSound = new PointSound(media,
         getSoundInitialGain(false), 0, 0, 0);
     setSoundAttributes(pointSound, false);
     m_BehaviorTransformGroup.addChild(pointSound);
   }
   if ((m_nFlags & COLLISION) == COLLISION) {
     m_BehaviorTransformGroup
         .setCapability(Node.ENABLE_COLLISION_REPORTING);
     m_BehaviorTransformGroup.setCollidable(true);
     m_BehaviorTransformGroup.setCollisionBounds(getGeometryBounds());
     if ((m_nFlags & COLLISION_SOUND) == COLLISION_SOUND) {
       MediaContainer collideMedia = loadSoundFile(szCollisionSound);
       m_CollideSound = new BackgroundSound(collideMedia, 1);
       setSoundAttributes(m_CollideSound, true);
       m_TransformGroup.addChild(m_CollideSound);
     }
     CollisionBehavior collision = new CollisionBehavior(
         m_BehaviorTransformGroup, this);
     collision.setSchedulingBounds(getGeometryBounds());
     m_BehaviorTransformGroup.addChild(collision);
   }
   m_TransformGroup.addChild(m_BehaviorTransformGroup);
   m_ParentGroup.addChild(m_TransformGroup);
   return m_BehaviorTransformGroup;
 }
 public void onCollide(boolean bCollide) {
   System.out.println("Collide: " + bCollide);
   if (m_CollideSound != null && bCollide == true)
     m_CollideSound.setEnable(true);
 }
 public void attachBehavior(Behavior beh) {
   m_BehaviorTransformGroup
       .setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
   beh.setSchedulingBounds(getGeometryBounds());
   m_BehaviorTransformGroup.addChild(beh);
 }
 public TransformGroup getBehaviorTransformGroup() {
   return m_BehaviorTransformGroup;
 }
 public void attachSplinePathInterpolator(Alpha alpha, Transform3D axis,
     URL urlKeyframes) {
   // read a spline path definition file and
   // add a Spline Path Interpolator to the TransformGroup for the object.
   m_BehaviorTransformGroup
       .setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
   RotPosScaleTCBSplinePathInterpolator splineInterpolator = Utils
       .createSplinePathInterpolator(alpha, m_BehaviorTransformGroup,
           axis, urlKeyframes);
   if (splineInterpolator != null) {
     splineInterpolator.setSchedulingBounds(getGeometryBounds());
     m_BehaviorTransformGroup.addChild(splineInterpolator);
   } else {
     System.out.println("attachSplinePathInterpolator failed for: "
         + urlKeyframes);
   }
 }

} class KeyBehavior extends Behavior {

 protected static final double FAST_SPEED = 20.0;
 protected static final double NORMAL_SPEED = 1.0;
 protected static final double SLOW_SPEED = 0.5;
 protected TransformGroup transformGroup;
 protected Transform3D transform3D;
 protected WakeupCondition keyCriterion;
 private final static double TWO_PI = (2.0 * Math.PI);
 private double rotateXAmount = Math.PI / 16.0;
 private double rotateYAmount = Math.PI / 16.0;
 private double rotateZAmount = Math.PI / 16.0;
 private double moveRate = 5;
 private double speed = NORMAL_SPEED;
 private final double kMoveForwardScale = 1.1;
 private final double kMoveBackwardScale = 0.9;
 private int forwardKey = KeyEvent.VK_UP;
 private int backKey = KeyEvent.VK_DOWN;
 private int leftKey = KeyEvent.VK_LEFT;
 private int rightKey = KeyEvent.VK_RIGHT;
 public KeyBehavior(TransformGroup tg) {
   super();
   transformGroup = tg;
   transform3D = new Transform3D();
 }
 public void initialize() {
   WakeupCriterion[] keyEvents = new WakeupCriterion[2];
   keyEvents[0] = new WakeupOnAWTEvent(KeyEvent.KEY_PRESSED);
   keyEvents[1] = new WakeupOnAWTEvent(KeyEvent.KEY_RELEASED);
   keyCriterion = new WakeupOr(keyEvents);
   wakeupOn(keyCriterion);
 }
 public void processStimulus(Enumeration criteria) {
   WakeupCriterion wakeup;
   AWTEvent[] event;
   while (criteria.hasMoreElements()) {
     wakeup = (WakeupCriterion) criteria.nextElement();
     if (!(wakeup instanceof WakeupOnAWTEvent))
       continue;
     event = ((WakeupOnAWTEvent) wakeup).getAWTEvent();
     for (int i = 0; i < event.length; i++) {
       if (event[i].getID() == KeyEvent.KEY_PRESSED) {
         processKeyEvent((KeyEvent) event[i]);
       }
     }
   }
   wakeupOn(keyCriterion);
 }
 protected void processKeyEvent(KeyEvent event) {
   int keycode = event.getKeyCode();
   if (event.isShiftDown())
     speed = FAST_SPEED;
   else
     speed = NORMAL_SPEED;
   if (event.isAltDown())
     altMove(keycode);
   else if (event.isControlDown())
     controlMove(keycode);
   else
     standardMove(keycode);
 }
 //moves forward backward or rotates left right
 private void standardMove(int keycode) {
   if (keycode == forwardKey)
     moveForward();
   else if (keycode == backKey)
     moveBackward();
   else if (keycode == leftKey)
     rotLeft();
   else if (keycode == rightKey)
     rotRight();
 }
 //moves left right, rotate up down
 protected void altMove(int keycode) {
   if (keycode == forwardKey)
     rotUp();
   else if (keycode == backKey)
     rotDown();
   else if (keycode == leftKey)
     rotLeft();
   else if (keycode == rightKey)
     rotRight();
   else if (keycode == leftKey)
     moveLeft();
   else if (keycode == rightKey)
     moveRight();
 }
 //move up down, rot left right
 protected void controlMove(int keycode) {
   if (keycode == forwardKey)
     moveUp();
   else if (keycode == backKey)
     moveDown();
   else if (keycode == leftKey)
     rollLeft();
   else if (keycode == rightKey)
     rollRight();
 }
 private void moveForward() {
   doMove(new Vector3d(0.0, 0.0, kMoveForwardScale * speed));
 }
 private void moveBackward() {
   doMove(new Vector3d(0.0, 0.0, -kMoveBackwardScale * speed));
 }
 private void moveLeft() {
   doMove(new Vector3d(-getMovementRate(), 0.0, 0.0));
 }
 private void moveRight() {
   doMove(new Vector3d(getMovementRate(), 0.0, 0.0));
 }
 private void moveUp() {
   doMove(new Vector3d(0.0, getMovementRate(), 0.0));
 }
 private void moveDown() {
   doMove(new Vector3d(0.0, -getMovementRate(), 0.0));
 }
 protected void rotRight() {
   doRotateY(getRotateRightAmount());
 }
 protected void rotUp() {
   doRotateX(getRotateUpAmount());
 }
 protected void rotLeft() {
   doRotateY(getRotateLeftAmount());
 }
 protected void rotDown() {
   doRotateX(getRotateDownAmount());
 }
 protected void rollLeft() {
   doRotateZ(getRollLeftAmount());
 }
 protected void rollRight() {
   doRotateZ(getRollRightAmount());
 }
 protected void updateTransform() {
   transformGroup.setTransform(transform3D);
 }
 protected void doRotateY(double radians) {
   transformGroup.getTransform(transform3D);
   Transform3D toMove = new Transform3D();
   toMove.rotY(radians);
   transform3D.mul(toMove);
   updateTransform();
 }
 protected void doRotateX(double radians) {
   transformGroup.getTransform(transform3D);
   Transform3D toMove = new Transform3D();
   toMove.rotX(radians);
   transform3D.mul(toMove);
   updateTransform();
 }
 protected void doRotateZ(double radians) {
   transformGroup.getTransform(transform3D);
   Transform3D toMove = new Transform3D();
   toMove.rotZ(radians);
   transform3D.mul(toMove);
   updateTransform();
 }
 protected void doMove(Vector3d theMove) {
   transformGroup.getTransform(transform3D);
   Transform3D toMove = new Transform3D();
   toMove.setTranslation(theMove);
   transform3D.mul(toMove);
   updateTransform();
 }
 protected double getMovementRate() {
   return moveRate * speed;
 }
 protected double getRollLeftAmount() {
   return rotateZAmount * speed;
 }
 protected double getRollRightAmount() {
   return -rotateZAmount * speed;
 }
 protected double getRotateUpAmount() {
   return rotateYAmount * speed;
 }
 protected double getRotateDownAmount() {
   return -rotateYAmount * speed;
 }
 protected double getRotateLeftAmount() {
   return rotateYAmount * speed;
 }
 protected double getRotateRightAmount() {
   return -rotateYAmount * speed;
 }
 public void setRotateXAmount(double radians) {
   rotateXAmount = radians;
 }
 public void setRotateYAmount(double radians) {
   rotateYAmount = radians;
 }
 public void setRotateZAmount(double radians) {
   rotateZAmount = radians;
 }
 public void setMovementRate(double meters) {
   moveRate = meters; // Travel rate in meters/frame
 }
 public void setForwardKey(int key) {
   forwardKey = key;
 }
 public void setBackKey(int key) {
   backKey = key;
 }
 public void setLeftKey(int key) {
   leftKey = key;
 }

} //creates a 2x2x2 cuboid with its base at y=0 class Cuboid extends ComplexObject {

 public Cuboid(Component comp, Group g, int nFlags) {
   super(comp, g, nFlags);
 }
 protected Group createGeometryGroup(Appearance app, Vector3d position,
     Vector3d scale, String szTextureFile, String szSoundFile) {
   int nFlags = GeometryArray.COORDINATES | GeometryArray.NORMALS;
   if ((m_nFlags & TEXTURE) == TEXTURE)
     nFlags |= GeometryArray.TEXTURE_COORDINATE_2;
   QuadArray quadArray = new QuadArray(24, nFlags);
   quadArray.setCoordinates(0, verts, 0, 24);
   for (int n = 0; n < 24; n++)
     quadArray.setNormal(n, normals[n / 4]);
   if ((m_nFlags & TEXTURE) == TEXTURE) {
     quadArray.setTextureCoordinates(0, 0, tcoords, 0, 24);
     setTexture(app, szTextureFile);
   }
   Shape3D shape = new Shape3D(quadArray, app);
   BranchGroup bg = new BranchGroup();
   bg.addChild(shape);
   return bg;
 }
 private static final float[] verts = {
 // front face
     1.0f, 0.0f, 1.0f, 1.0f, 2.0f, 1.0f, -1.0f, 2.0f, 1.0f, -1.0f, 0.0f,
     1.0f,
     // back face
     -1.0f, 0.0f, -1.0f, -1.0f, 2.0f, -1.0f, 1.0f, 2.0f, -1.0f, 1.0f,
     0.0f, -1.0f,
     // right face
     1.0f, 0.0f, -1.0f, 1.0f, 2.0f, -1.0f, 1.0f, 2.0f, 1.0f, 1.0f, 0.0f,
     1.0f,
     // left face
     -1.0f, 0.0f, 1.0f, -1.0f, 2.0f, 1.0f, -1.0f, 2.0f, -1.0f, -1.0f,
     0.0f, -1.0f,
     // top face
     1.0f, 2.0f, 1.0f, 1.0f, 2.0f, -1.0f, -1.0f, 2.0f, -1.0f, -1.0f,
     2.0f, 1.0f,
     // bottom face
     -1.0f, 0.0f, 1.0f, -1.0f, 0.0f, -1.0f, 1.0f, 0.0f, -1.0f, 1.0f,
     0.0f, 1.0f, };
 private static final float[] tcoords = {
 // front
     1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f,
     // back
     1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f,
     //right
     1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f,
     // left
     1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f,
     // top
     1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f,
     // bottom
     0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f };
 private static final Vector3f[] normals = { new Vector3f(0.0f, 0.0f, 1.0f), // front
     // face
     new Vector3f(0.0f, 0.0f, -1.0f), // back face
     new Vector3f(1.0f, 0.0f, 0.0f), // right face
     new Vector3f(-1.0f, 0.0f, 0.0f), // left face
     new Vector3f(0.0f, 1.0f, 0.0f), // top face
     new Vector3f(0.0f, -1.0f, 0.0f), // bottom face
 };

} //***************************************************************************** /**

* Utils
* 
* @author Daniel Selman
* @version 1.0
*/

//***************************************************************************** class Utils {

 // convert an angular rotation about an axis to a Quaternion
 static Quat4f createQuaternionFromAxisAndAngle(Vector3d axis, double angle) {
   double sin_a = Math.sin(angle / 2);
   double cos_a = Math.cos(angle / 2);
   // use a vector so we can call normalize
   Vector4f q = new Vector4f();
   q.x = (float) (axis.x * sin_a);
   q.y = (float) (axis.y * sin_a);
   q.z = (float) (axis.z * sin_a);
   q.w = (float) cos_a;
   // It is necessary to normalise the quaternion
   // in case any values are very close to zero.
   q.normalize();
   // convert to a Quat4f and return
   return new Quat4f(q);
 }
 // convert three rotations about the Euler axes to a Quaternion
 static Quat4f createQuaternionFromEuler(double angleX, double angleY,
     double angleZ) {
   // simply call createQuaternionFromAxisAndAngle
   // for each axis and multiply the results
   Quat4f qx = createQuaternionFromAxisAndAngle(new Vector3d(1, 0, 0),
       angleX);
   Quat4f qy = createQuaternionFromAxisAndAngle(new Vector3d(0, 1, 0),
       angleY);
   Quat4f qz = createQuaternionFromAxisAndAngle(new Vector3d(0, 0, 1),
       angleZ);
   // qx = qx * qy
   qx.mul(qy);
   // qx = qx * qz
   qx.mul(qz);
   return qx;
 }
 static public double getRandomNumber(double basis, double random) {
   return basis + ((float) Math.random() * random * 2f) - (random);
 }
 static public double getRandomNumber(double basis, double random,
     double scale) {
   double value = basis + ((float) Math.random() * random * 2f) - (random);
   return value * scale;
 }
 static public StringBuffer readFile(URL urlFile) {
   /*   allocate a temporary buffer to store the input file*/
   StringBuffer szBufferData = new StringBuffer();
   Vector keyFramesVector = new Vector();
   try {
     InputStream inputStream = urlFile.openStream();
     int nChar = 0;
     // read the entire file into the StringBuffer
     while (true) {
       nChar = inputStream.read();
       /* if we have not hit the end of file
        add the character to the StringBuffer
  • /
       if (nChar != -1)
         szBufferData.append((char) nChar);
       else
         // EOF
         break;
     }
     inputStream.close();
   } catch (Exception e) {
     System.err.println(e.toString());
     return null;
   }
   return szBufferData;
 }
 static public RotPosScaleTCBSplinePathInterpolator createSplinePathInterpolator(
     Alpha alpha, TransformGroup tg, Transform3D axis, URL urlKeyframes) {
   TCBKeyFrame[] keyFrames = readKeyFrames(urlKeyframes);
   if (keyFrames != null)
     return new RotPosScaleTCBSplinePathInterpolator(alpha, tg, axis,
         keyFrames);
   return null;
 }
 static public TCBKeyFrame[] readKeyFrames(URL urlKeyframes) {
   StringBuffer szBufferData = readFile(urlKeyframes);
   if (szBufferData == null)
     return null;
   Vector keyFramesVector = new Vector();
   // create a tokenizer to tokenize the input file at whitespace
   java.util.StringTokenizer tokenizer = new java.util.StringTokenizer(
       szBufferData.toString());
   // each keyframe is defined as follows
   // - knot (0 >= k <= 1)
   // - position (x,y,z)
   // - rotation (rx,ry,rz)
   // - scale (x,y,z)
   // - tension (-1 >= t <= 1)
   // - continuity (-1 >= c <= 1)
   // - bias (-1 >= b <= 1)
   // - linear (int - 0 or 1)
   while (true) {
     try {
       float knot = Float.parseFloat(tokenizer.nextToken());
       float posX = Float.parseFloat(tokenizer.nextToken());
       float posY = Float.parseFloat(tokenizer.nextToken());
       float posZ = Float.parseFloat(tokenizer.nextToken());
       float rotX = Float.parseFloat(tokenizer.nextToken());
       float rotY = Float.parseFloat(tokenizer.nextToken());
       float rotZ = Float.parseFloat(tokenizer.nextToken());
       float scaleX = Float.parseFloat(tokenizer.nextToken());
       float scaleY = Float.parseFloat(tokenizer.nextToken());
       float scaleZ = Float.parseFloat(tokenizer.nextToken());
       float tension = Float.parseFloat(tokenizer.nextToken());
       float continuity = Float.parseFloat(tokenizer.nextToken());
       float bias = Float.parseFloat(tokenizer.nextToken());
       int linear = Integer.parseInt(tokenizer.nextToken());
       TCBKeyFrame keyframe = new TCBKeyFrame(knot, linear,
           new Point3f(posX, posY, posZ),
           createQuaternionFromEuler(rotX, rotY, rotZ),
           new Point3f(scaleX, scaleY, scaleZ), tension,
           continuity, bias);
       keyFramesVector.add(keyframe);
     } catch (Exception e) {
       break;
     }
   }
   // create the return structure and populate
   TCBKeyFrame[] keysReturn = new TCBKeyFrame[keyFramesVector.size()];
   for (int n = 0; n < keysReturn.length; n++)
     keysReturn[n] = (TCBKeyFrame) keyFramesVector.get(n);
   // return the array
   return keysReturn;
 }

} class Land extends ComplexObject {

 public static final float WIDTH = 1.0f;
 public static final float LENGTH = 1.0f;
 public static final float HEIGHT = 0.0f;
 public Land(Component comp, Group g, int nFlags) {
   super(comp, g, nFlags);
 }
 protected Group createGeometryGroup(Appearance app, Vector3d position,
     Vector3d scale, String szTextureFile, String szSoundFile) {
   int nFlags = GeometryArray.COORDINATES | GeometryArray.NORMALS;
   if ((m_nFlags & TEXTURE) == TEXTURE)
     nFlags |= GeometryArray.TEXTURE_COORDINATE_2;
   QuadArray quadArray = new QuadArray(4, nFlags);
   float[] coordArray = { -WIDTH, HEIGHT, LENGTH, WIDTH, HEIGHT, LENGTH,
       WIDTH, HEIGHT, -LENGTH, -WIDTH, HEIGHT, -LENGTH };
   quadArray.setCoordinates(0, coordArray, 0, coordArray.length / 3);
   for (int n = 0; n < coordArray.length / 3; n++)
     quadArray.setNormal(n, new Vector3f(0, 1, 0));
   if ((m_nFlags & TEXTURE) == TEXTURE) {
     float[] texArray = { 0, 0, 1, 0, 1, 1, 0, 1 };
     quadArray.setTextureCoordinates(0, 0, texArray, 0,
         coordArray.length / 3);
     setTexture(app, szTextureFile);
   }
   BranchGroup bg = new BranchGroup();
   Shape3D shape = new Shape3D(quadArray, app);
   bg.addChild(shape);
   return bg;
 }

} /**

* This class is a simple behavior that invokes the KeyNavigator to modify the
* view platform transform.
*/

class CollisionBehavior extends Behavior {

 private WakeupOnCollisionEntry wakeupOne = null;
 private WakeupOnCollisionExit wakeupTwo = null;
 private WakeupCriterion[] wakeupArray = new WakeupCriterion[2];
 private WakeupCondition wakeupCondition = null;
 private ComplexObject m_Owner = null;
 public CollisionBehavior(Node node, ComplexObject owner) {
   wakeupOne = new WakeupOnCollisionEntry(node,
       WakeupOnCollisionEntry.USE_GEOMETRY);
   wakeupTwo = new WakeupOnCollisionExit(node,
       WakeupOnCollisionExit.USE_GEOMETRY);
   wakeupArray[0] = wakeupOne;
   wakeupArray[1] = wakeupTwo;
   wakeupCondition = new WakeupOr(wakeupArray);
   m_Owner = owner;
 }
 /**
  * Override Behavior"s initialize method to setup wakeup criteria.
  */
 public void initialize() {
   // Establish initial wakeup criteria
   wakeupOn(wakeupCondition);
 }
 /**
  * Override Behavior"s stimulus method to handle the event.
  */
 public void processStimulus(Enumeration criteria) {
   WakeupCriterion genericEvt;
   while (criteria.hasMoreElements()) {
     genericEvt = (WakeupCriterion) criteria.nextElement();
     if (genericEvt instanceof WakeupOnCollisionEntry) {
       m_Owner.onCollide(true);
     } else if (genericEvt instanceof WakeupOnCollisionExit) {
       m_Owner.onCollide(false);
     }
   }
   // Set wakeup criteria for next time
   wakeupOn(wakeupCondition);
 }

}


      </source>
   
  
 
  



Spline Animation

   <source lang="java">

/*

* @(#)SplineAnim.java 1.13 02/10/21 13:55:30
* 
* Copyright (c) 1996-2002 Sun Microsystems, Inc. All Rights Reserved.
* 
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*  - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*  - Redistribution in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 
* Neither the name of Sun Microsystems, Inc. or the names of contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
* 
* This software is provided "AS IS," without a warranty of any kind. ALL
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
* IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
* NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
* LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
* OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
* LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
* INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
* CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
* OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGES.
* 
* You acknowledge that Software is not designed,licensed or intended for use in
* the design, construction, operation or maintenance of any nuclear facility.
*/

import java.applet.Applet; import java.awt.Button; import java.awt.Choice; import java.awt.FlowLayout; import java.awt.Frame; import java.awt.GraphicsConfiguration; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.ItemSelectable; import java.awt.Label; import java.awt.Panel; import java.awt.Scrollbar; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.AdjustmentEvent; import java.awt.event.AdjustmentListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import javax.media.j3d.Alpha; import javax.media.j3d.AmbientLight; import javax.media.j3d.Appearance; import javax.media.j3d.Background; import javax.media.j3d.BoundingSphere; import javax.media.j3d.BranchGroup; import javax.media.j3d.Canvas3D; import javax.media.j3d.ColoringAttributes; import javax.media.j3d.DirectionalLight; import javax.media.j3d.Light; import javax.media.j3d.Material; import javax.media.j3d.Transform3D; import javax.media.j3d.TransformGroup; import javax.vecmath.Color3f; import javax.vecmath.Point3d; import javax.vecmath.Point3f; import javax.vecmath.Vector3d; import javax.vecmath.Vector3f; import com.sun.j3d.utils.applet.MainFrame; import com.sun.j3d.utils.behaviors.interpolators.KBKeyFrame; import com.sun.j3d.utils.behaviors.interpolators.KBRotPosScaleSplinePathInterpolator; import com.sun.j3d.utils.behaviors.vp.OrbitBehavior; import com.sun.j3d.utils.geometry.Cone; import com.sun.j3d.utils.geometry.Sphere; import com.sun.j3d.utils.universe.SimpleUniverse; import com.sun.j3d.utils.universe.ViewingPlatform; /*

* This program demonstrates the use of KBRotPosScaleSplinePathInterpolator in
* order to to do spline animation paths using Kochanek-Bartels (also known as
* TCB or Tension-Continuity-Bias ) splines. A cone red cone is animated along a
* spline path specified by 5 knot points, which are showns as cyan spheres.
* 
* Use the left mouse button to changes orientation of scene. Use the middle
* mouse button to zoom in/out Use the right mouse button to pan the scene
*/

public class SplineAnim extends Applet implements ActionListener,

   AdjustmentListener, ItemListener {
 // 3D Canvas
 Canvas3D canvas;
 // UI Components
 Panel controlPanel;
 Panel canvasPanel;
 Button animateButton;
 Choice interpChoice;
 Scrollbar speedSlider;
 Label speedLabel;
 Label interpLabel;
 // Scene Graph
 BoundingSphere bounds;
 BranchGroup root;
 BranchGroup behaviorBranch;
 Transform3D sceneTransform;
 TransformGroup sceneTransformGroup;
 Transform3D objTransform;
 TransformGroup objTransformGroup;
 Transform3D lightTransform1;
 Transform3D lightTransform2;
 TransformGroup light1TransformGroup;
 TransformGroup light2TransformGroup;
 // Key Frames & Interpolator
 int duration = 5000;
 Alpha animAlpha;
 Transform3D yAxis;
 KBKeyFrame[] linearKeyFrames = new KBKeyFrame[6];
 KBKeyFrame[] splineKeyFrames = new KBKeyFrame[6];
 KBRotPosScaleSplinePathInterpolator splineInterpolator;
 KBRotPosScaleSplinePathInterpolator linearInterpolator;
 // Data: Knot positions & transform groups
 Vector3f pos0 = new Vector3f(-5.0f, -5.0f, 0.0f);
 Vector3f pos1 = new Vector3f(-5.0f, 5.0f, 0.0f);
 Vector3f pos2 = new Vector3f(0.0f, 5.0f, 0.0f);
 Vector3f pos3 = new Vector3f(0.0f, -5.0f, 0.0f);
 Vector3f pos4 = new Vector3f(5.0f, -5.0f, 0.0f);
 Vector3f pos5 = new Vector3f(5.0f, 5.0f, 0.0f);
 TransformGroup k0TransformGroup;
 TransformGroup k1TransformGroup;
 TransformGroup k2TransformGroup;
 TransformGroup k3TransformGroup;
 TransformGroup k4TransformGroup;
 TransformGroup k5TransformGroup;
 // Flags
 boolean animationOn = true;
 boolean linear = false;
 private SimpleUniverse u = null;
 public SplineAnim() {
 }
 public void init() {
   this.setLayout(new FlowLayout());
   // Create the canvas and the UI
   canvasPanel = new Panel();
   controlPanel = new Panel();
   createCanvasPanel(canvasPanel);
   this.add(canvasPanel);
   createControlPanel(controlPanel);
   this.add(controlPanel);
   // Create the scene.
   BranchGroup scene = createSceneGraph();
   // Setup keyframe data for our animation
   setupSplineKeyFrames();
   setupLinearKeyFrames();
   // Setup alpha, create the interpolators and start them. We
   // create both a linear and a spline interpolator and turn on
   // one depending on user selection. The default is spline.
   setupAnimationData();
   createInterpolators();
   startInterpolator();
   // Add viewing platform
   u = new SimpleUniverse(canvas);
   // add mouse behaviors to ViewingPlatform
   ViewingPlatform viewingPlatform = u.getViewingPlatform();
   viewingPlatform.setNominalViewingTransform();
   // add orbit behavior to the ViewingPlatform
   OrbitBehavior orbit = new OrbitBehavior(canvas,
       OrbitBehavior.REVERSE_ALL);
   BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0),
       100.0);
   orbit.setSchedulingBounds(bounds);
   viewingPlatform.setViewPlatformBehavior(orbit);
   u.addBranchGraph(scene);
 }
 public void destroy() {
   u.cleanup();
 }
 /*
  * This creates the control panel which contains a choice menu to toggle
  * between spline and linear interpolation, a slider to adjust the speed of
  * the animation and a animation start/stop button.
  */
 private void createControlPanel(Panel p) {
   GridBagLayout gl = new GridBagLayout();
   GridBagConstraints gbc = new GridBagConstraints();
   p.setLayout(gl);
   gbc.weightx = 100;
   gbc.weighty = 100;
   gbc.fill = GridBagConstraints.BOTH;
   gbc.gridx = 0;
   gbc.gridy = 0;
   gbc.gridwidth = 1;
   gbc.gridheight = 1;
   interpLabel = new Label("Interpolation Type", Label.LEFT);
   p.add(interpLabel, gbc);
   gbc.gridx = 1;
   gbc.gridy = 0;
   gbc.gridwidth = 1;
   gbc.gridheight = 1;
   interpChoice = new Choice();
   interpChoice.add("Spline");
   interpChoice.add("Linear");
   p.add(interpChoice, gbc);
   interpChoice.addItemListener(this);
   gbc.gridx = 0;
   gbc.gridy = 2;
   gbc.gridwidth = 2;
   gbc.gridheight = 1;
   speedSlider = new Scrollbar(Scrollbar.HORIZONTAL, 2, 1, 0, 11);
   speedSlider.setUnitIncrement(1);
   p.add(speedSlider, gbc);
   speedSlider.addAdjustmentListener(this);
   gbc.gridx = 0;
   gbc.gridy = 3;
   gbc.gridwidth = 2;
   gbc.gridheight = 1;
   speedLabel = new Label(" - Animation Speed +", Label.CENTER);
   p.add(speedLabel, gbc);
   gbc.gridx = 0;
   gbc.gridy = 5;
   gbc.gridwidth = 2;
   gbc.gridheight = 1;
   animateButton = new Button("Stop Animation");
   p.add(animateButton, gbc);
   animateButton.addActionListener(this);
 }
 /*
  * This creates the Java3D canvas
  */
 private void createCanvasPanel(Panel p) {
   GridBagLayout gl = new GridBagLayout();
   GridBagConstraints gbc = new GridBagConstraints();
   p.setLayout(gl);
   gbc.gridx = 0;
   gbc.gridy = 0;
   gbc.gridwidth = 5;
   gbc.gridheight = 5;
   GraphicsConfiguration config = SimpleUniverse
       .getPreferredConfiguration();
   canvas = new Canvas3D(config);
   canvas.setSize(490, 490);
   p.add(canvas, gbc);
 }
 /*
  * This creates the scene with 5 knot points represented by cyan spheres, a
  * cone obejct that will be transformed, and two directional lights + and
  * ambient light.
  */
 public BranchGroup createSceneGraph() {
   // Colors for lights and objects
   Color3f aColor = new Color3f(0.2f, 0.2f, 0.2f);
   Color3f eColor = new Color3f(0.0f, 0.0f, 0.0f);
   Color3f sColor = new Color3f(1.0f, 1.0f, 1.0f);
   Color3f coneColor = new Color3f(0.9f, 0.1f, 0.1f);
   Color3f sphereColor = new Color3f(0.1f, 0.7f, 0.9f);
   Color3f bgColor = new Color3f(0.0f, 0.0f, 0.0f);
   Color3f lightColor = new Color3f(1.0f, 1.0f, 1.0f);
   // Root of the branch grsph
   BranchGroup root = new BranchGroup();
   // Create transforms such that all objects appears in the scene
   sceneTransform = new Transform3D();
   sceneTransform.setScale(0.14f);
   Transform3D yrot = new Transform3D();
   yrot.rotY(-Math.PI / 5.0d);
   sceneTransform.mul(yrot);
   sceneTransformGroup = new TransformGroup(sceneTransform);
   sceneTransformGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
   sceneTransformGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
   root.addChild(sceneTransformGroup);
   // Create bounds for the background and lights
   bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 100.0f);
   // Set up the background
   Background bg = new Background(bgColor);
   bg.setApplicationBounds(bounds);
   sceneTransformGroup.addChild(bg);
   // Create the transform group node for the lights
   lightTransform1 = new Transform3D();
   lightTransform2 = new Transform3D();
   Vector3d lightPos1 = new Vector3d(0.0, 0.0, 2.0);
   Vector3d lightPos2 = new Vector3d(1.0, 0.0, -2.0);
   lightTransform1.set(lightPos1);
   lightTransform2.set(lightPos2);
   light1TransformGroup = new TransformGroup(lightTransform1);
   light2TransformGroup = new TransformGroup(lightTransform2);
   sceneTransformGroup.addChild(light1TransformGroup);
   sceneTransformGroup.addChild(light2TransformGroup);
   // Create lights
   AmbientLight ambLight = new AmbientLight(aColor);
   Light dirLight1;
   Light dirLight2;
   Vector3f lightDir1 = new Vector3f(lightPos1);
   Vector3f lightDir2 = new Vector3f(lightPos2);
   lightDir1.negate();
   lightDir2.negate();
   dirLight1 = new DirectionalLight(lightColor, lightDir1);
   dirLight2 = new DirectionalLight(lightColor, lightDir2);
   // Set the influencing bounds
   ambLight.setInfluencingBounds(bounds);
   dirLight1.setInfluencingBounds(bounds);
   dirLight2.setInfluencingBounds(bounds);
   // Add the lights into the scene graph
   sceneTransformGroup.addChild(ambLight);
   sceneTransformGroup.addChild(dirLight1);
   sceneTransformGroup.addChild(dirLight2);
   // Create a cone and add it to the scene graph.
   objTransform = new Transform3D();
   objTransformGroup = new TransformGroup(objTransform);
   objTransformGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
   sceneTransformGroup.addChild(objTransformGroup);
   Material m = new Material(coneColor, eColor, coneColor, sColor, 100.0f);
   Appearance a = new Appearance();
   m.setLightingEnable(true);
   a.setMaterial(m);
   Cone cone = new Cone(0.4f, 1.0f);
   cone.setAppearance(a);
   objTransformGroup.addChild(cone);
   // Create transform groups for each knot point
   // knot point 0
   Transform3D t3dKnot = new Transform3D();
   t3dKnot.set(pos0);
   TransformGroup k0TransformGroup = new TransformGroup(t3dKnot);
   sceneTransformGroup.addChild(k0TransformGroup);
   // knot point 1
   t3dKnot = new Transform3D();
   t3dKnot.set(pos1);
   TransformGroup k1TransformGroup = new TransformGroup(t3dKnot);
   sceneTransformGroup.addChild(k1TransformGroup);
   // knot point 2
   t3dKnot = new Transform3D();
   t3dKnot.set(pos2);
   TransformGroup k2TransformGroup = new TransformGroup(t3dKnot);
   sceneTransformGroup.addChild(k2TransformGroup);
   // knot point 3
   t3dKnot = new Transform3D();
   t3dKnot.set(pos3);
   TransformGroup k3TransformGroup = new TransformGroup(t3dKnot);
   sceneTransformGroup.addChild(k3TransformGroup);
   // knot point 4
   t3dKnot = new Transform3D();
   t3dKnot.set(pos4);
   TransformGroup k4TransformGroup = new TransformGroup(t3dKnot);
   sceneTransformGroup.addChild(k4TransformGroup);
   // knot point 5
   t3dKnot = new Transform3D();
   t3dKnot.set(pos5);
   TransformGroup k5TransformGroup = new TransformGroup(t3dKnot);
   sceneTransformGroup.addChild(k5TransformGroup);
   // Create spheres for each knot point"s transform group
   ColoringAttributes sphereColorAttr = new ColoringAttributes();
   sphereColorAttr.setColor(sphereColor);
   Appearance sphereAppearance = new Appearance();
   sphereAppearance.setColoringAttributes(sphereColorAttr);
   k0TransformGroup.addChild(new Sphere(0.10f, sphereAppearance));
   k1TransformGroup.addChild(new Sphere(0.10f, sphereAppearance));
   k2TransformGroup.addChild(new Sphere(0.10f, sphereAppearance));
   k3TransformGroup.addChild(new Sphere(0.10f, sphereAppearance));
   k4TransformGroup.addChild(new Sphere(0.10f, sphereAppearance));
   k5TransformGroup.addChild(new Sphere(0.10f, sphereAppearance));
   return root;
 }
 /*
  * This sets up the key frame data for the spline interpolator. Each knot
  * point has a scale and rotation component specified. The second argument
  * to KBKeyFrame (in this case 0) tells the interpolator that this is to be
  * interpolated using splines. The last three arguments to KBKeyFrame are
  * Tension, Continuity, and Bias components for each key frame.
  */
 private void setupSplineKeyFrames() {
   // Prepare spline keyframe data
   Point3f p = new Point3f(pos0); // position
   float head = (float) Math.PI / 2.0f; // heading
   float pitch = 0.0f; // pitch
   float bank = 0.0f; // bank
   Point3f s = new Point3f(1.0f, 1.0f, 1.0f); // uniform scale
   splineKeyFrames[0] = new KBKeyFrame(0.0f, 0, p, head, pitch, bank, s,
       0.0f, 0.0f, 0.0f);
   p = new Point3f(pos1);
   head = 0.0f; // heading
   pitch = 0.0f; // pitch
   bank = (float) -Math.PI / 2.0f; // bank
   s = new Point3f(1.0f, 1.0f, 1.0f); // uniform scale
   splineKeyFrames[1] = new KBKeyFrame(0.2f, 0, p, head, pitch, bank, s,
       0.0f, 0.0f, 0.0f);
   p = new Point3f(pos2);
   head = 0.0f; // heading
   pitch = 0.0f; // pitch
   bank = 0.0f; // bank
   s = new Point3f(0.7f, 0.7f, 0.7f); // uniform scale
   splineKeyFrames[2] = new KBKeyFrame(0.4f, 0, p, head, pitch, bank, s,
       0.0f, 0.0f, 0.0f);
   p = new Point3f(pos3);
   head = (float) Math.PI / 2.0f; // heading
   pitch = 0.0f; // pitch
   bank = (float) Math.PI / 2.0f; // bank
   s = new Point3f(0.5f, 0.5f, 0.5f); // uniform scale
   splineKeyFrames[3] = new KBKeyFrame(0.6f, 0, p, head, pitch, bank, s,
       0.0f, 0.0f, 0.0f);
   p = new Point3f(pos4);
   head = (float) -Math.PI / 2.0f; // heading
   pitch = (float) -Math.PI / 2.0f; // pitch
   bank = (float) Math.PI / 2.0f; // bank
   s = new Point3f(0.4f, 0.4f, 0.4f); // uniform scale
   splineKeyFrames[4] = new KBKeyFrame(0.8f, 0, p, head, pitch, bank, s,
       0.0f, 0.0f, 0.0f);
   p = new Point3f(pos5);
   head = 0.0f; // heading
   pitch = 0.0f; // pitch
   bank = 0.0f; // bank
   s = new Point3f(1.0f, 1.0f, 1.0f); // uniform scale
   splineKeyFrames[5] = new KBKeyFrame(1.0f, 0, p, head, pitch, bank, s,
       0.0f, 0.0f, 0.0f);
 }
 /*
  * This sets up the key frame data for the linear interpolator. Each knot
  * point has a scale and rotation component specified. The second argument
  * to KBKeyFrame (in this case 1) tells the interpolator that this is to be
  * interpolated linearly. The last three arguments to TCBKeyFrame are
  * Tension, Continuity, and Bias components for each key frame.
  */
 private void setupLinearKeyFrames() {
   // Prepare linear keyframe data
   Point3f p = new Point3f(pos0);
   float head = 0.0f; // heading
   float pitch = 0.0f; // pitch
   float bank = 0.0f; // bank
   Point3f s = new Point3f(1.0f, 1.0f, 1.0f); // uniform scale
   linearKeyFrames[0] = new KBKeyFrame(0.0f, 1, p, head, pitch, bank, s,
       0.0f, 0.0f, 0.0f);
   p = new Point3f(pos1);
   linearKeyFrames[1] = new KBKeyFrame(0.2f, 1, p, head, pitch, bank, s,
       0.0f, 0.0f, 0.0f);
   p = new Point3f(pos2);
   linearKeyFrames[2] = new KBKeyFrame(0.4f, 1, p, head, pitch, bank, s,
       0.0f, 0.0f, 0.0f);
   p = new Point3f(pos3);
   linearKeyFrames[3] = new KBKeyFrame(0.6f, 1, p, head, pitch, bank, s,
       0.0f, 0.0f, 0.0f);
   p = new Point3f(pos4);
   linearKeyFrames[4] = new KBKeyFrame(0.8f, 1, p, head, pitch, bank, s,
       0.0f, 0.0f, 0.0f);
   p = new Point3f(pos5);
   linearKeyFrames[5] = new KBKeyFrame(1.0f, 1, p, head, pitch, bank, s,
       0.0f, 0.0f, 0.0f);
 }
 /*
  * This sets up alpha for the interpolator
  */
 private void setupAnimationData() {
   yAxis = new Transform3D();
   animAlpha = new Alpha(-1, Alpha.INCREASING_ENABLE, 0, 0, duration, 0,
       0, 0, 0, 0);
 }
 /*
  * create a spline and a linear interpolator, but we will activate only one
  * in startInterpolator()
  */
 private void createInterpolators() {
   behaviorBranch = new BranchGroup();
   // create spline interpolator
   splineInterpolator = new KBRotPosScaleSplinePathInterpolator(animAlpha,
       objTransformGroup, yAxis, splineKeyFrames);
   splineInterpolator.setSchedulingBounds(bounds);
   behaviorBranch.addChild(splineInterpolator);
   // create linear interpolator
   linearInterpolator = new KBRotPosScaleSplinePathInterpolator(animAlpha,
       objTransformGroup, yAxis, linearKeyFrames);
   linearInterpolator.setSchedulingBounds(bounds);
   behaviorBranch.addChild(linearInterpolator);
   objTransformGroup.addChild(behaviorBranch);
 }
 /*
  * This activates one of the interpolators depending on the state of the
  * linear boolean flag which may be toggled by the user using the choice
  * menu.
  */
 public void startInterpolator() {
   if (animationOn) {
     if (linear) {
       splineInterpolator.setEnable(false);
       linearInterpolator.setEnable(true);
     } else {
       linearInterpolator.setEnable(false);
       splineInterpolator.setEnable(true);
     }
   }
 }
 /*
  * Toggle animation
  */
 public void actionPerformed(ActionEvent event) {
   Object source = event.getSource();
   if (source == animateButton) {
     try {
       // toggle animation
       if (animationOn) {
         animationOn = false;
         splineInterpolator.setEnable(false);
         linearInterpolator.setEnable(false);
         animateButton.setLabel("Start Animation");
       } else {
         animationOn = true;
         startInterpolator();
         animateButton.setLabel("Stop Animation");
       }
     } catch (Exception e) {
       System.err.println("Exception " + e);
     }
   }
 }
 /*
  * Toggle the interpolators
  */
 public void itemStateChanged(ItemEvent event) {
   Object source = event.getSource();
   ItemSelectable ie = event.getItemSelectable();
   if (source == interpChoice) {
     try {
       if (ie.getSelectedObjects()[0] == "Spline") {
         linear = false;
       }
       if (ie.getSelectedObjects()[0] == "Linear") {
         linear = true;
       }
       startInterpolator();
     } catch (Exception e) {
       System.err.println("Exception " + e);
     }
   }
 }
 /*
  * Adjust the speed of the animations
  */
 public void adjustmentValueChanged(AdjustmentEvent e) {
   int value = e.getValue();
   duration = 6000 - (500 * value);
   animAlpha.setIncreasingAlphaDuration(duration);
 }
 public static void main(String[] args) {
   Frame frame = new MainFrame(new SplineAnim(), 500, 600);
 }

}


      </source>
   
  
 
  



This example creates a 3D fly-over of the city of Boston

   <source lang="java">

/**********************************************************

Copyright (C) 2001   Daniel Selman
First distributed with the book "Java 3D Programming"
by Daniel Selman and published by Manning Publications.
http://manning.ru/selman
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation, version 2.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
The license can be found on the WWW at:
http://www.fsf.org/copyleft/gpl.html
Or by writing to:
Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
Authors can be contacted at:
Daniel Selman: daniel@selman.org
If you make changes you think others would like, please 
contact one of the authors or someone at the 
www.j3d.org web site.
**************************************************************/

/**

* This example creates a 3D fly-over of the city of Boston. The viewer is
* animated using a RotPosScaleTCBSplinePathInterpolator as well as 3
* helicopters. The example uses PointSounds attached to the helicopters to
* generate 3D spatial audio.
*/

import java.applet.Applet; import java.awt.BorderLayout; import java.awt.Color; import java.awt.ruponent; import java.awt.Dimension; import java.awt.Frame; import java.awt.GraphicsConfigTemplate; import java.awt.GraphicsDevice; import java.awt.GraphicsEnvironment; import java.awt.image.BufferedImage; import java.io.File; import java.io.InputStream; import java.net.URL; import java.util.Enumeration; import java.util.Vector; import javax.media.j3d.Alpha; import javax.media.j3d.Appearance; import javax.media.j3d.AudioDevice; import javax.media.j3d.Background; import javax.media.j3d.BackgroundSound; import javax.media.j3d.Behavior; import javax.media.j3d.BoundingSphere; import javax.media.j3d.Bounds; import javax.media.j3d.BranchGroup; import javax.media.j3d.Canvas3D; import javax.media.j3d.ColoringAttributes; import javax.media.j3d.DistanceLOD; import javax.media.j3d.GeometryArray; import javax.media.j3d.GraphicsConfigTemplate3D; import javax.media.j3d.Group; import javax.media.j3d.ImageComponent2D; import javax.media.j3d.Locale; import javax.media.j3d.MediaContainer; import javax.media.j3d.Node; import javax.media.j3d.PhysicalBody; import javax.media.j3d.PhysicalEnvironment; import javax.media.j3d.PointSound; import javax.media.j3d.QuadArray; import javax.media.j3d.Shape3D; import javax.media.j3d.Sound; import javax.media.j3d.Switch; import javax.media.j3d.Texture; import javax.media.j3d.Transform3D; import javax.media.j3d.TransformGroup; import javax.media.j3d.View; import javax.media.j3d.ViewPlatform; import javax.media.j3d.VirtualUniverse; import javax.media.j3d.WakeupCondition; import javax.media.j3d.WakeupCriterion; import javax.media.j3d.WakeupOnCollisionEntry; import javax.media.j3d.WakeupOnCollisionExit; import javax.media.j3d.WakeupOr; import javax.swing.JButton; import javax.swing.JPanel; import javax.swing.JSlider; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.vecmath.Color3f; import javax.vecmath.Point2f; import javax.vecmath.Point3d; import javax.vecmath.Point3f; import javax.vecmath.Quat4f; import javax.vecmath.Vector3d; import javax.vecmath.Vector3f; import javax.vecmath.Vector4f; import com.sun.j3d.audioengines.javasound.JavaSoundMixer; import com.sun.j3d.loaders.Scene; import com.sun.j3d.loaders.objectfile.ObjectFile; import com.sun.j3d.utils.applet.MainFrame; import com.sun.j3d.utils.behaviors.interpolators.RotPosScaleTCBSplinePathInterpolator; import com.sun.j3d.utils.behaviors.interpolators.TCBKeyFrame; import com.sun.j3d.utils.geometry.Primitive; import com.sun.j3d.utils.geometry.Sphere; import com.sun.j3d.utils.image.TextureLoader; /*

* This example uses a Spline Interpolator to animate a fly-over the city of
* Boston. The city is rendered using satellite images with a Level of Detail
* behavior. The scene includes a number of moving helicopters, each with an
* associated sound.
*/

public class SplineInterpolatorTest extends Java3dApplet {

 // size of the 3D window - enlage on powerful systems
 private static int m_kWidth = 200;
 private static int m_kHeight = 200;
 // a shared appearance for the buildings we create
 private Appearance m_BuildingAppearance = null;
 // the size of the high resolution "world".
 // the world is centered at 0,0,0 and extends
 // to +- LAND_WIDTH in the x direction and
 //+- LAND_LENGTH in the z direction.
 // These dimensions are loosely based on pixel
 // coordinates from the texture images
 private static final float LAND_WIDTH = 180;
 private static final float LAND_LENGTH = 180;
 // the satellite images used as textures have
 // been manually edited so that the water in the
 // images corresponds to the following RGB values.
 // this allows the application to avoid creating
 // buildings in the water!
 private static final float WATER_COLOR_RED = 0;
 private static final float WATER_COLOR_GREEN = 57;
 private static final float WATER_COLOR_BLUE = 123;
 public SplineInterpolatorTest() {
   initJava3d();
 }
 // scale eveything so we can use pixel coordinates
 protected double getScale() {
   return 0.1;
 }
 protected int getCanvas3dWidth(Canvas3D c3d) {
   return m_kWidth;
 }
 protected int getCanvas3dHeight(Canvas3D c3d) {
   return m_kHeight;
 }
 protected Bounds createApplicationBounds() {
   m_ApplicationBounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0),
       10.0);
   return m_ApplicationBounds;
 }
 // we want a texture mapped background of a sky
 protected Background createBackground() {
   // add the sky backdrop
   Background back = new Background();
   back.setApplicationBounds(getApplicationBounds());
   BranchGroup bgGeometry = new BranchGroup();
   // create an appearance and assign the texture image
   Appearance app = new Appearance();
   Texture tex = new TextureLoader("sky.gif", this).getTexture();
   app.setTexture(tex);
   Sphere sphere = new Sphere(1.0f, Primitive.GENERATE_TEXTURE_COORDS
       | Primitive.GENERATE_NORMALS_INWARD, app);
   bgGeometry.addChild(sphere);
   back.setGeometry(bgGeometry);
   return back;
 }
 // this controls how close to a helicopter we can
 // be and still hear it. If the helicopters sound
 // scheduling bounds intersect our ViewPlatformActivationRadius
 // the sound of the helicopter is potentially audible.
 protected float getViewPlatformActivationRadius() {
   return 20;
 }
 // creates the objects within our world
 protected BranchGroup createSceneBranchGroup() {
   BranchGroup objRoot = super.createSceneBranchGroup();
   // create a root TG in case we need to scale the scene
   TransformGroup objTrans = new TransformGroup();
   objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
   objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
   Transform3D t3d = new Transform3D();
   objTrans.setTransform(t3d);
   Group hiResGroup = createLodLand(objTrans);
   createBuildings(objTrans);
   createHelicopters(objTrans);
   // connect
   objRoot.addChild(objTrans);
   return objRoot;
 }
 // we create 2 TransformGroups above the ViewPlatform:
 // the first merely applies a scale, while the second
 // has a RotPosScaleTCBSplinePathInterpolator attached
 // so that the viewer of the scene is animated along
 // a spline curve.
 public TransformGroup[] getViewTransformGroupArray() {
   TransformGroup[] tgArray = new TransformGroup[2];
   tgArray[0] = new TransformGroup();
   tgArray[1] = new TransformGroup();
   Transform3D t3d = new Transform3D();
   t3d.setScale(getScale());
   t3d.invert();
   tgArray[0].setTransform(t3d);
   // create an Alpha object for the Interpolator
   Alpha alpha = new Alpha(-1, Alpha.INCREASING_ENABLE
       | Alpha.DECREASING_ENABLE, 0, 0, 25000, 4000, 100, 20000, 5000,
       50);
   // ensure the Interpolator can access the TG
   tgArray[1].setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
   try {
     // create the Interpolator and load the keyframes from disk
     RotPosScaleTCBSplinePathInterpolator splineInterpolator = Utils
         .createSplinePathInterpolator(new UiAlpha(alpha),
             tgArray[1], new Transform3D(), new URL(
                 getWorkingDirectory(),
                 "rotate_viewer_spline.xls"));
     // set the scheduling bounds and attach to the scenegraph
     splineInterpolator.setSchedulingBounds(getApplicationBounds());
     tgArray[1].addChild(splineInterpolator);
   } catch (Exception e) {
     System.err.println(e.toString());
   }
   return tgArray;
 }
 // overidden so that the example can use audio
 protected AudioDevice createAudioDevice(PhysicalEnvironment pe) {
   return new JavaSoundMixer(pe);
 }
 // creates a Switch group that contains two versions
 // of the world - the first is a high resolution version,
 // the second if a lower resolution version.
 public Group createLodLand(Group g) {
   Switch switchNode = new Switch();
   switchNode.setCapability(Switch.ALLOW_SWITCH_WRITE);
   Group hiResGroup = createLand(switchNode);
   createEnvirons(switchNode);
   // create a DistanceLOD that will select the child of
   // the Switch node based on distance. Here we are selecting
   // child 0 (high res) if we are closer than 180 units to
   // 0,0,0 and child 1 (low res) otherwise.
   float[] distanceArray = { 180 };
   DistanceLOD distanceLod = new DistanceLOD(distanceArray);
   distanceLod.setSchedulingBounds(getApplicationBounds());
   distanceLod.addSwitch(switchNode);
   g.addChild(distanceLod);
   g.addChild(switchNode);
   return hiResGroup;
 }
 // creates a high resolution representation of the world.
 // a single texture mapped square and a larger (water colored)
 // square to act as a horizon.
 public Group createLand(Group g) {
   Land land = new Land(this, g, ComplexObject.GEOMETRY
       | ComplexObject.TEXTURE);
   Group hiResGroup = land.createObject(new Appearance(), new Vector3d(),
       new Vector3d(LAND_WIDTH, 1, LAND_LENGTH), "boston.gif", null,
       null);
   Appearance app = new Appearance();
   app.setColoringAttributes(new ColoringAttributes(
       WATER_COLOR_RED / 255f, WATER_COLOR_GREEN / 255f,
       WATER_COLOR_BLUE / 255f, ColoringAttributes.FASTEST));
   Land base = new Land(this, hiResGroup, ComplexObject.GEOMETRY);
   base.createObject(app, new Vector3d(0, -5, 0), new Vector3d(
       4 * LAND_WIDTH, 1, 4 * LAND_LENGTH), null, null, null);
   return hiResGroup;
 }
 // creates a low resolution version of the world and
 // applies the low resolution satellite image
 public Group createEnvirons(Group g) {
   Land environs = new Land(this, g, ComplexObject.GEOMETRY
       | ComplexObject.TEXTURE);
   return environs.createObject(new Appearance(), new Vector3d(),
       new Vector3d(2 * LAND_WIDTH, 1, 2 * LAND_LENGTH),
       "environs.gif", null, null);
 }
 // returns true if the given x,z location in the world
 // corresponds to water in the satellite image
 protected boolean isLocationWater(BufferedImage image, float posX,
     float posZ) {
   Color color = null;
   float imageWidth = image.getWidth();
   float imageHeight = image.getHeight();
   // range from 0 to 1
   float nPixelX = (posX + LAND_WIDTH) / (2 * LAND_WIDTH);
   float nPixelY = (posZ + LAND_LENGTH) / (2 * LAND_LENGTH);
   // rescale
   nPixelX *= imageWidth;
   nPixelY *= imageHeight;
   if (nPixelX >= 0 && nPixelX < imageWidth && nPixelY >= 0
       && nPixelY < imageHeight) {
     color = new Color(image.getRGB((int) nPixelX, (int) nPixelY));
     return (color.getBlue() >= WATER_COLOR_BLUE
         && color.getGreen() <= WATER_COLOR_GREEN && color.getRed() <= WATER_COLOR_RED);
   }
   return false;
 }
 // creates up to 120 building objects - ensures that
 // buildings are not positioned over water.
 public Group createBuildings(Group g) {
   m_BuildingAppearance = new Appearance();
   BranchGroup bg = new BranchGroup();
   Texture tex = new TextureLoader("boston.gif", this).getTexture();
   BufferedImage image = ((ImageComponent2D) tex.getImage(0)).getImage();
   final int nMaxBuildings = 120;
   for (int n = 0; n < nMaxBuildings; n++) {
     Cuboid building = new Cuboid(this, bg, ComplexObject.GEOMETRY
         | ComplexObject.TEXTURE);
     float posX = (int) Utils.getRandomNumber(0, LAND_WIDTH);
     float posZ = (int) Utils.getRandomNumber(0, LAND_LENGTH);
     if (isLocationWater(image, posX, posZ) == false) {
       building.createObject(m_BuildingAppearance, new Vector3d(posX,
           0, posZ), new Vector3d(Utils.getRandomNumber(3, 2),
           Utils.getRandomNumber(8, 7), Utils
               .getRandomNumber(3, 2)), "house.gif", null,
           null);
     }
   }
   g.addChild(bg);
   return bg;
 }
 // creates three helicopters
 public void createHelicopters(Group g) {
   for (int n = 0; n < 3; n++)
     createHelicopter(g);
 }
 // edit the positions of the clipping
 // planes so we don"t clip on the front
 // plane prematurely
 protected double getBackClipDistance() {
   return 50.0;
 }
 protected double getFrontClipDistance() {
   return 0.1;
 }
 // creates a single helicopter object
 public Group createHelicopter(Group g) {
   BranchGroup bg = new BranchGroup();
   Helicopter heli = new Helicopter(this, bg, ComplexObject.GEOMETRY
       | ComplexObject.SOUND);
   heli.createObject(new Appearance(), new Vector3d(Utils.getRandomNumber(
       0, LAND_WIDTH), Utils.getRandomNumber(15, 5), Utils
       .getRandomNumber(0, LAND_LENGTH)), new Vector3d(10, 10, 10),
       null, "heli.wav", null);
   g.addChild(bg);
   return bg;
 }
 public static void main(String[] args) {
   SplineInterpolatorTest splineInterpolatorTest = new SplineInterpolatorTest();
   splineInterpolatorTest.saveCommandLineArguments(args);
   new MainFrame(splineInterpolatorTest, m_kWidth, m_kHeight);
 }

} /*******************************************************************************

* Copyright (C) 2001 Daniel Selman
* 
* First distributed with the book "Java 3D Programming" by Daniel Selman and
* published by Manning Publications. http://manning.ru/selman
* 
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, version 2.
* 
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
* 
* The license can be found on the WWW at: http://www.fsf.org/copyleft/gpl.html
* 
* Or by writing to: Free Software Foundation, Inc., 59 Temple Place - Suite
* 330, Boston, MA 02111-1307, USA.
* 
* Authors can be contacted at: Daniel Selman: daniel@selman.org
* 
* If you make changes you think others would like, please contact one of the
* authors or someone at the www.j3d.org web site.
******************************************************************************/

//***************************************************************************** /**

* Java3dApplet
* 
* Base class for defining a Java 3D applet. Contains some useful methods for
* defining views and scenegraphs etc.
* 
* @author Daniel Selman
* @version 1.0
*/

//***************************************************************************** abstract class Java3dApplet extends Applet {

 public static int m_kWidth = 300;
 public static int m_kHeight = 300;
 protected String[] m_szCommandLineArray = null;
 protected VirtualUniverse m_Universe = null;
 protected BranchGroup m_SceneBranchGroup = null;
 protected Bounds m_ApplicationBounds = null;
 //  protected com.tornadolabs.j3dtree.Java3dTree m_Java3dTree = null;
 public Java3dApplet() {
 }
 public boolean isApplet() {
   try {
     System.getProperty("user.dir");
     System.out.println("Running as Application.");
     return false;
   } catch (Exception e) {
   }
   System.out.println("Running as Applet.");
   return true;
 }
 public URL getWorkingDirectory() throws java.net.MalformedURLException {
   URL url = null;
   try {
     File file = new File(System.getProperty("user.dir"));
     System.out.println("Running as Application:");
     System.out.println("   " + file.toURL());
     return file.toURL();
   } catch (Exception e) {
   }
   System.out.println("Running as Applet:");
   System.out.println("   " + getCodeBase());
   return getCodeBase();
 }
 public VirtualUniverse getVirtualUniverse() {
   return m_Universe;
 }
 //public com.tornadolabs.j3dtree.Java3dTree getJ3dTree() {
 //return m_Java3dTree;
 //  }
 public Locale getFirstLocale() {
   java.util.Enumeration e = m_Universe.getAllLocales();
   if (e.hasMoreElements() != false)
     return (Locale) e.nextElement();
   return null;
 }
 protected Bounds getApplicationBounds() {
   if (m_ApplicationBounds == null)
     m_ApplicationBounds = createApplicationBounds();
   return m_ApplicationBounds;
 }
 protected Bounds createApplicationBounds() {
   m_ApplicationBounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0),
       100.0);
   return m_ApplicationBounds;
 }
 protected Background createBackground() {
   Background back = new Background(new Color3f(0.9f, 0.9f, 0.9f));
   back.setApplicationBounds(createApplicationBounds());
   return back;
 }
 public void initJava3d() {
   //  m_Java3dTree = new com.tornadolabs.j3dtree.Java3dTree();
   m_Universe = createVirtualUniverse();
   Locale locale = createLocale(m_Universe);
   BranchGroup sceneBranchGroup = createSceneBranchGroup();
   ViewPlatform vp = createViewPlatform();
   BranchGroup viewBranchGroup = createViewBranchGroup(
       getViewTransformGroupArray(), vp);
   createView(vp);
   Background background = createBackground();
   if (background != null)
     sceneBranchGroup.addChild(background);
   //    m_Java3dTree.recursiveApplyCapability(sceneBranchGroup);
   //  m_Java3dTree.recursiveApplyCapability(viewBranchGroup);
   locale.addBranchGraph(sceneBranchGroup);
   addViewBranchGroup(locale, viewBranchGroup);
   onDoneInit();
 }
 protected void onDoneInit() {
   //  m_Java3dTree.updateNodes(m_Universe);
 }
 protected double getScale() {
   return 1.0;
 }
 public TransformGroup[] getViewTransformGroupArray() {
   TransformGroup[] tgArray = new TransformGroup[1];
   tgArray[0] = new TransformGroup();
   // move the camera BACK a little...
   // note that we have to invert the matrix as
   // we are moving the viewer
   Transform3D t3d = new Transform3D();
   t3d.setScale(getScale());
   t3d.setTranslation(new Vector3d(0.0, 0.0, -20.0));
   t3d.invert();
   tgArray[0].setTransform(t3d);
   return tgArray;
 }
 protected void addViewBranchGroup(Locale locale, BranchGroup bg) {
   locale.addBranchGraph(bg);
 }
 protected Locale createLocale(VirtualUniverse u) {
   return new Locale(u);
 }
 protected BranchGroup createSceneBranchGroup() {
   m_SceneBranchGroup = new BranchGroup();
   return m_SceneBranchGroup;
 }
 protected View createView(ViewPlatform vp) {
   View view = new View();
   PhysicalBody pb = createPhysicalBody();
   PhysicalEnvironment pe = createPhysicalEnvironment();
   AudioDevice audioDevice = createAudioDevice(pe);
   if (audioDevice != null) {
     pe.setAudioDevice(audioDevice);
     audioDevice.initialize();
   }
   view.setPhysicalEnvironment(pe);
   view.setPhysicalBody(pb);
   if (vp != null)
     view.attachViewPlatform(vp);
   view.setBackClipDistance(getBackClipDistance());
   view.setFrontClipDistance(getFrontClipDistance());
   Canvas3D c3d = createCanvas3D();
   view.addCanvas3D(c3d);
   addCanvas3D(c3d);
   return view;
 }
 protected PhysicalBody createPhysicalBody() {
   return new PhysicalBody();
 }
 protected AudioDevice createAudioDevice(PhysicalEnvironment pe) {
   JavaSoundMixer javaSoundMixer = new JavaSoundMixer(pe);
   if (javaSoundMixer == null)
     System.out.println("create of audiodevice failed");
   return javaSoundMixer;
 }
 protected PhysicalEnvironment createPhysicalEnvironment() {
   return new PhysicalEnvironment();
 }
 protected float getViewPlatformActivationRadius() {
   return 100;
 }
 protected ViewPlatform createViewPlatform() {
   ViewPlatform vp = new ViewPlatform();
   vp.setViewAttachPolicy(View.RELATIVE_TO_FIELD_OF_VIEW);
   vp.setActivationRadius(getViewPlatformActivationRadius());
   return vp;
 }
 protected Canvas3D createCanvas3D() {
   GraphicsConfigTemplate3D gc3D = new GraphicsConfigTemplate3D();
   gc3D.setSceneAntialiasing(GraphicsConfigTemplate.PREFERRED);
   GraphicsDevice gd[] = GraphicsEnvironment.getLocalGraphicsEnvironment()
       .getScreenDevices();
   Canvas3D c3d = new Canvas3D(gd[0].getBestConfiguration(gc3D));
   c3d.setSize(getCanvas3dWidth(c3d), getCanvas3dHeight(c3d));
   return c3d;
 }
 protected int getCanvas3dWidth(Canvas3D c3d) {
   return m_kWidth;
 }
 protected int getCanvas3dHeight(Canvas3D c3d) {
   return m_kHeight;
 }
 protected double getBackClipDistance() {
   return 100.0;
 }
 protected double getFrontClipDistance() {
   return 1.0;
 }
 protected BranchGroup createViewBranchGroup(TransformGroup[] tgArray,
     ViewPlatform vp) {
   BranchGroup vpBranchGroup = new BranchGroup();
   if (tgArray != null && tgArray.length > 0) {
     Group parentGroup = vpBranchGroup;
     TransformGroup curTg = null;
     for (int n = 0; n < tgArray.length; n++) {
       curTg = tgArray[n];
       parentGroup.addChild(curTg);
       parentGroup = curTg;
     }
     tgArray[tgArray.length - 1].addChild(vp);
   } else
     vpBranchGroup.addChild(vp);
   return vpBranchGroup;
 }
 protected void addCanvas3D(Canvas3D c3d) {
   setLayout(new BorderLayout());
   add(c3d, BorderLayout.CENTER);
   doLayout();
 }
 protected VirtualUniverse createVirtualUniverse() {
   return new VirtualUniverse();
 }
 protected void saveCommandLineArguments(String[] szArgs) {
   m_szCommandLineArray = szArgs;
 }
 protected String[] getCommandLineArguments() {
   return m_szCommandLineArray;
 }

} //***************************************************************************** /**

* Utils
* 
* @author Daniel Selman
* @version 1.0
*/

//***************************************************************************** class Utils {

 // convert an angular rotation about an axis to a Quaternion
 static Quat4f createQuaternionFromAxisAndAngle(Vector3d axis, double angle) {
   double sin_a = Math.sin(angle / 2);
   double cos_a = Math.cos(angle / 2);
   // use a vector so we can call normalize
   Vector4f q = new Vector4f();
   q.x = (float) (axis.x * sin_a);
   q.y = (float) (axis.y * sin_a);
   q.z = (float) (axis.z * sin_a);
   q.w = (float) cos_a;
   // It is necessary to normalise the quaternion
   // in case any values are very close to zero.
   q.normalize();
   // convert to a Quat4f and return
   return new Quat4f(q);
 }
 // convert three rotations about the Euler axes to a Quaternion
 static Quat4f createQuaternionFromEuler(double angleX, double angleY,
     double angleZ) {
   // simply call createQuaternionFromAxisAndAngle
   // for each axis and multiply the results
   Quat4f qx = createQuaternionFromAxisAndAngle(new Vector3d(1, 0, 0),
       angleX);
   Quat4f qy = createQuaternionFromAxisAndAngle(new Vector3d(0, 1, 0),
       angleY);
   Quat4f qz = createQuaternionFromAxisAndAngle(new Vector3d(0, 0, 1),
       angleZ);
   // qx = qx * qy
   qx.mul(qy);
   // qx = qx * qz
   qx.mul(qz);
   return qx;
 }
 static public double getRandomNumber(double basis, double random) {
   return basis + ((float) Math.random() * random * 2f) - (random);
 }
 static public double getRandomNumber(double basis, double random,
     double scale) {
   double value = basis + ((float) Math.random() * random * 2f) - (random);
   return value * scale;
 }
 static public StringBuffer readFile(URL urlFile) {
   // allocate a temporary buffer to store the input file
   StringBuffer szBufferData = new StringBuffer();
   Vector keyFramesVector = new Vector();
   try {
     InputStream inputStream = urlFile.openStream();
     int nChar = 0;
     // read the entire file into the StringBuffer
     while (true) {
       nChar = inputStream.read();
       // if we have not hit the end of file
       // add the character to the StringBuffer
       if (nChar != -1)
         szBufferData.append((char) nChar);
       else
         // EOF
         break;
     }
     inputStream.close();
   } catch (Exception e) {
     System.err.println(e.toString());
     return null;
   }
   return szBufferData;
 }
 static public RotPosScaleTCBSplinePathInterpolator createSplinePathInterpolator(
     Alpha alpha, TransformGroup tg, Transform3D axis, URL urlKeyframes) {
   TCBKeyFrame[] keyFrames = readKeyFrames(urlKeyframes);
   if (keyFrames != null)
     return new RotPosScaleTCBSplinePathInterpolator(alpha, tg, axis,
         keyFrames);
   return null;
 }
 static public TCBKeyFrame[] readKeyFrames(URL urlKeyframes) {
   StringBuffer szBufferData = readFile(urlKeyframes);
   if (szBufferData == null)
     return null;
   Vector keyFramesVector = new Vector();
   // create a tokenizer to tokenize the input file at whitespace
   java.util.StringTokenizer tokenizer = new java.util.StringTokenizer(
       szBufferData.toString());
   // each keyframe is defined as follows
   // - knot (0 >= k <= 1)
   // - position (x,y,z)
   // - rotation (rx,ry,rz)
   // - scale (x,y,z)
   // - tension (-1 >= t <= 1)
   // - continuity (-1 >= c <= 1)
   // - bias (-1 >= b <= 1)
   // - linear (int - 0 or 1)
   while (true) {
     try {
       float knot = Float.parseFloat(tokenizer.nextToken());
       float posX = Float.parseFloat(tokenizer.nextToken());
       float posY = Float.parseFloat(tokenizer.nextToken());
       float posZ = Float.parseFloat(tokenizer.nextToken());
       float rotX = Float.parseFloat(tokenizer.nextToken());
       float rotY = Float.parseFloat(tokenizer.nextToken());
       float rotZ = Float.parseFloat(tokenizer.nextToken());
       float scaleX = Float.parseFloat(tokenizer.nextToken());
       float scaleY = Float.parseFloat(tokenizer.nextToken());
       float scaleZ = Float.parseFloat(tokenizer.nextToken());
       float tension = Float.parseFloat(tokenizer.nextToken());
       float continuity = Float.parseFloat(tokenizer.nextToken());
       float bias = Float.parseFloat(tokenizer.nextToken());
       int linear = Integer.parseInt(tokenizer.nextToken());
       TCBKeyFrame keyframe = new TCBKeyFrame(knot, linear,
           new Point3f(posX, posY, posZ),
           createQuaternionFromEuler(rotX, rotY, rotZ),
           new Point3f(scaleX, scaleY, scaleZ), tension,
           continuity, bias);
       keyFramesVector.add(keyframe);
     } catch (Exception e) {
       break;
     }
   }
   // create the return structure and populate
   TCBKeyFrame[] keysReturn = new TCBKeyFrame[keyFramesVector.size()];
   for (int n = 0; n < keysReturn.length; n++)
     keysReturn[n] = (TCBKeyFrame) keyFramesVector.get(n);
   // return the array
   return keysReturn;
 }

} //this class defines an Alpha class that returns a random //value every N milliseconds. class UiAlpha extends Alpha implements ChangeListener {

 protected Alpha m_Alpha = null;
 protected float m_AlphaValue = 0.5f;
 JButton m_Button = null;
 boolean m_bAuto = true;
 public UiAlpha(Alpha alpha) {
   m_Alpha = alpha;
   Frame frame = new Frame("Alpha Control Panel");
   JPanel panel = new JPanel();
   frame.add(panel);
   addUiToPanel(panel);
   frame.pack();
   frame.setSize(new Dimension(400, 80));
   frame.validate();
   frame.setVisible(true);
 }
 protected void addUiToPanel(JPanel panel) {
   JSlider slider = new JSlider();
   slider.addChangeListener(this);
   panel.add(slider);
   m_Button = new JButton("Auto");
   m_Button.addChangeListener(this);
   panel.add(m_Button);
 }
 public void stateChanged(ChangeEvent e) {
   if (e.getSource() instanceof JSlider) {
     m_AlphaValue = ((JSlider) e.getSource()).getValue() / 100.0f;
     m_bAuto = false;
   } else {
     m_bAuto = true;
   }
 }
 // core method override
 // returns the Alpha value for a given time
 public float value(long time) {
   if (m_bAuto == true)
     return m_Alpha.value(time);
   return m_AlphaValue;
 }

} class Land extends ComplexObject {

 public static final float WIDTH = 1.0f;
 public static final float LENGTH = 1.0f;
 public static final float HEIGHT = 0.0f;
 public Land(Component comp, Group g, int nFlags) {
   super(comp, g, nFlags);
 }
 protected Group createGeometryGroup(Appearance app, Vector3d position,
     Vector3d scale, String szTextureFile, String szSoundFile) {
   int nFlags = GeometryArray.COORDINATES | GeometryArray.NORMALS;
   if ((m_nFlags & TEXTURE) == TEXTURE)
     nFlags |= GeometryArray.TEXTURE_COORDINATE_2;
   QuadArray quadArray = new QuadArray(4, nFlags);
   float[] coordArray = { -WIDTH, HEIGHT, LENGTH, WIDTH, HEIGHT, LENGTH,
       WIDTH, HEIGHT, -LENGTH, -WIDTH, HEIGHT, -LENGTH };
   quadArray.setCoordinates(0, coordArray, 0, coordArray.length / 3);
   for (int n = 0; n < coordArray.length / 3; n++)
     quadArray.setNormal(n, new Vector3f(0, 1, 0));
   if ((m_nFlags & TEXTURE) == TEXTURE) {
     float[] texArray = { 0, 0, 1, 0, 1, 1, 0, 1 };
     quadArray.setTextureCoordinates(0, 0, texArray, 0,
         coordArray.length / 3);
     setTexture(app, szTextureFile);
   }
   BranchGroup bg = new BranchGroup();
   Shape3D shape = new Shape3D(quadArray, app);
   bg.addChild(shape);
   return bg;
 }

} abstract class ComplexObject extends BranchGroup {

 protected Group m_ParentGroup = null;
 protected int m_nFlags = 0;
 protected BackgroundSound m_CollideSound = null;
 protected Component m_Component = null;
 protected TransformGroup m_TransformGroup = null;
 protected TransformGroup m_BehaviorTransformGroup = null;
 public static final int SOUND = 0x001;
 public static final int GEOMETRY = 0x002;
 public static final int TEXTURE = 0x004;
 public static final int COLLISION = 0x008;
 public static final int COLLISION_SOUND = 0x010;
 public ComplexObject(Component comp, Group group, int nFlags) {
   m_ParentGroup = group;
   m_nFlags = nFlags;
   m_Component = comp;
 }
 public Bounds getGeometryBounds() {
   return new BoundingSphere(new Point3d(0, 0, 0), 100);
 }
 private MediaContainer loadSoundFile(String szFile) {
   try {
     File file = new File(System.getProperty("user.dir"));
     URL url = file.toURL();
     URL soundUrl = new URL(url, szFile);
     return new MediaContainer(soundUrl);
   } catch (Exception e) {
     System.err.println("Error could not load sound file: " + e);
     System.exit(-1);
   }
   return null;
 }
 protected void setTexture(Appearance app, String szFile) {
   Texture tex = new TextureLoader(szFile, m_Component).getTexture();
   app.setTexture(tex);
 }
 abstract protected Group createGeometryGroup(Appearance app,
     Vector3d position, Vector3d scale, String szTextureFile,
     String szSoundFile);
 protected Group loadGeometryGroup(String szModel, Appearance app)
     throws java.io.FileNotFoundException {
   // load the object file
   Scene scene = null;
   Shape3D shape = null;
   // read in the geometry information from the data file
   ObjectFile objFileloader = new ObjectFile(ObjectFile.RESIZE);
   scene = objFileloader.load(szModel);
   // retrieve the Shape3D object from the scene
   BranchGroup branchGroup = scene.getSceneGroup();
   shape = (Shape3D) branchGroup.getChild(0);
   shape.setAppearance(app);
   return branchGroup;
 }
 protected int getSoundLoop(boolean bCollide) {
   return 1;
 }
 protected float getSoundPriority(boolean bCollide) {
   return 1.0f;
 }
 protected float getSoundInitialGain(boolean bCollide) {
   return 1.0f;
 }
 protected boolean getSoundInitialEnable(boolean bCollide) {
   return true;
 }
 protected boolean getSoundContinuousEnable(boolean bCollide) {
   return false;
 }
 protected Bounds getSoundSchedulingBounds(boolean bCollide) {
   return new BoundingSphere(new Point3d(0, 0, 0), 1.0);
 }
 protected boolean getSoundReleaseEnable(boolean bCollide) {
   return true;
 }
 protected Point2f[] getSoundDistanceGain(boolean bCollide) {
   return null;
 }
 protected void setSoundAttributes(Sound sound, boolean bCollide) {
   sound.setCapability(Sound.ALLOW_ENABLE_WRITE);
   sound.setCapability(Sound.ALLOW_ENABLE_READ);
   sound.setSchedulingBounds(getSoundSchedulingBounds(bCollide));
   sound.setEnable(getSoundInitialEnable(bCollide));
   sound.setLoop(getSoundLoop(bCollide));
   sound.setPriority(getSoundPriority(bCollide));
   sound.setInitialGain(getSoundInitialGain(bCollide));
   sound.setContinuousEnable(getSoundContinuousEnable(bCollide));
   sound.setReleaseEnable(bCollide);
   if (sound instanceof PointSound) {
     PointSound pointSound = (PointSound) sound;
     pointSound.setInitialGain(getSoundInitialGain(bCollide));
     Point2f[] gainArray = getSoundDistanceGain(bCollide);
     if (gainArray != null)
       pointSound.setDistanceGain(gainArray);
   }
 }
 public Group createObject(Appearance app, Vector3d position,
     Vector3d scale, String szTextureFile, String szSoundFile,
     String szCollisionSound) {
   m_TransformGroup = new TransformGroup();
   Transform3D t3d = new Transform3D();
   t3d.setScale(scale);
   t3d.setTranslation(position);
   m_TransformGroup.setTransform(t3d);
   m_BehaviorTransformGroup = new TransformGroup();
   if ((m_nFlags & GEOMETRY) == GEOMETRY)
     m_BehaviorTransformGroup.addChild(createGeometryGroup(app,
         position, scale, szTextureFile, szSoundFile));
   if ((m_nFlags & SOUND) == SOUND) {
     MediaContainer media = loadSoundFile(szSoundFile);
     PointSound pointSound = new PointSound(media,
         getSoundInitialGain(false), 0, 0, 0);
     setSoundAttributes(pointSound, false);
     m_BehaviorTransformGroup.addChild(pointSound);
   }
   if ((m_nFlags & COLLISION) == COLLISION) {
     m_BehaviorTransformGroup
         .setCapability(Node.ENABLE_COLLISION_REPORTING);
     m_BehaviorTransformGroup.setCollidable(true);
     m_BehaviorTransformGroup.setCollisionBounds(getGeometryBounds());
     if ((m_nFlags & COLLISION_SOUND) == COLLISION_SOUND) {
       MediaContainer collideMedia = loadSoundFile(szCollisionSound);
       m_CollideSound = new BackgroundSound(collideMedia, 1);
       setSoundAttributes(m_CollideSound, true);
       m_TransformGroup.addChild(m_CollideSound);
     }
     CollisionBehavior collision = new CollisionBehavior(
         m_BehaviorTransformGroup, this);
     collision.setSchedulingBounds(getGeometryBounds());
     m_BehaviorTransformGroup.addChild(collision);
   }
   m_TransformGroup.addChild(m_BehaviorTransformGroup);
   m_ParentGroup.addChild(m_TransformGroup);
   return m_BehaviorTransformGroup;
 }
 public void onCollide(boolean bCollide) {
   System.out.println("Collide: " + bCollide);
   if (m_CollideSound != null && bCollide == true)
     m_CollideSound.setEnable(true);
 }
 public void attachBehavior(Behavior beh) {
   m_BehaviorTransformGroup
       .setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
   beh.setSchedulingBounds(getGeometryBounds());
   m_BehaviorTransformGroup.addChild(beh);
 }
 public TransformGroup getBehaviorTransformGroup() {
   return m_BehaviorTransformGroup;
 }
 public void attachSplinePathInterpolator(Alpha alpha, Transform3D axis,
     URL urlKeyframes) {
   // read a spline path definition file and
   // add a Spline Path Interpolator to the TransformGroup for the object.
   m_BehaviorTransformGroup
       .setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
   RotPosScaleTCBSplinePathInterpolator splineInterpolator = Utils
       .createSplinePathInterpolator(alpha, m_BehaviorTransformGroup,
           axis, urlKeyframes);
   if (splineInterpolator != null) {
     splineInterpolator.setSchedulingBounds(getGeometryBounds());
     m_BehaviorTransformGroup.addChild(splineInterpolator);
   } else {
     System.out.println("attachSplinePathInterpolator failed for: "
         + urlKeyframes);
   }
 }

} /**

* This class is a simple behavior that invokes the KeyNavigator to modify the
* view platform transform.
*/

class CollisionBehavior extends Behavior {

 private WakeupOnCollisionEntry wakeupOne = null;
 private WakeupOnCollisionExit wakeupTwo = null;
 private WakeupCriterion[] wakeupArray = new WakeupCriterion[2];
 private WakeupCondition wakeupCondition = null;
 private ComplexObject m_Owner = null;
 public CollisionBehavior(Node node, ComplexObject owner) {
   wakeupOne = new WakeupOnCollisionEntry(node,
       WakeupOnCollisionEntry.USE_GEOMETRY);
   wakeupTwo = new WakeupOnCollisionExit(node,
       WakeupOnCollisionExit.USE_GEOMETRY);
   wakeupArray[0] = wakeupOne;
   wakeupArray[1] = wakeupTwo;
   wakeupCondition = new WakeupOr(wakeupArray);
   m_Owner = owner;
 }
 /**
  * Override Behavior"s initialize method to setup wakeup criteria.
  */
 public void initialize() {
   // Establish initial wakeup criteria
   wakeupOn(wakeupCondition);
 }
 /**
  * Override Behavior"s stimulus method to handle the event.
  */
 public void processStimulus(Enumeration criteria) {
   WakeupCriterion genericEvt;
   while (criteria.hasMoreElements()) {
     genericEvt = (WakeupCriterion) criteria.nextElement();
     if (genericEvt instanceof WakeupOnCollisionEntry) {
       m_Owner.onCollide(true);
     } else if (genericEvt instanceof WakeupOnCollisionExit) {
       m_Owner.onCollide(false);
     }
   }
   // Set wakeup criteria for next time
   wakeupOn(wakeupCondition);
 }

} //creates a 2x2x2 cuboid with its base at y=0 class Cuboid extends ComplexObject {

 public Cuboid(Component comp, Group g, int nFlags) {
   super(comp, g, nFlags);
 }
 protected Group createGeometryGroup(Appearance app, Vector3d position,
     Vector3d scale, String szTextureFile, String szSoundFile) {
   int nFlags = GeometryArray.COORDINATES | GeometryArray.NORMALS;
   if ((m_nFlags & TEXTURE) == TEXTURE)
     nFlags |= GeometryArray.TEXTURE_COORDINATE_2;
   QuadArray quadArray = new QuadArray(24, nFlags);
   quadArray.setCoordinates(0, verts, 0, 24);
   for (int n = 0; n < 24; n++)
     quadArray.setNormal(n, normals[n / 4]);
   if ((m_nFlags & TEXTURE) == TEXTURE) {
     quadArray.setTextureCoordinates(0, 0, tcoords, 0, 24);
     setTexture(app, szTextureFile);
   }
   Shape3D shape = new Shape3D(quadArray, app);
   BranchGroup bg = new BranchGroup();
   bg.addChild(shape);
   return bg;
 }
 private static final float[] verts = {
 // front face
     1.0f, 0.0f, 1.0f, 1.0f, 2.0f, 1.0f, -1.0f, 2.0f, 1.0f, -1.0f, 0.0f,
     1.0f,
     // back face
     -1.0f, 0.0f, -1.0f, -1.0f, 2.0f, -1.0f, 1.0f, 2.0f, -1.0f, 1.0f,
     0.0f, -1.0f,
     // right face
     1.0f, 0.0f, -1.0f, 1.0f, 2.0f, -1.0f, 1.0f, 2.0f, 1.0f, 1.0f, 0.0f,
     1.0f,
     // left face
     -1.0f, 0.0f, 1.0f, -1.0f, 2.0f, 1.0f, -1.0f, 2.0f, -1.0f, -1.0f,
     0.0f, -1.0f,
     // top face
     1.0f, 2.0f, 1.0f, 1.0f, 2.0f, -1.0f, -1.0f, 2.0f, -1.0f, -1.0f,
     2.0f, 1.0f,
     // bottom face
     -1.0f, 0.0f, 1.0f, -1.0f, 0.0f, -1.0f, 1.0f, 0.0f, -1.0f, 1.0f,
     0.0f, 1.0f, };
 private static final float[] tcoords = {
 // front
     1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f,
     // back
     1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f,
     //right
     1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f,
     // left
     1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f,
     // top
     1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f,
     // bottom
     0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f };
 private static final Vector3f[] normals = { new Vector3f(0.0f, 0.0f, 1.0f), // front
     // face
     new Vector3f(0.0f, 0.0f, -1.0f), // back face
     new Vector3f(1.0f, 0.0f, 0.0f), // right face
     new Vector3f(-1.0f, 0.0f, 0.0f), // left face
     new Vector3f(0.0f, 1.0f, 0.0f), // top face
     new Vector3f(0.0f, -1.0f, 0.0f), // bottom face
 };

} class Helicopter extends ComplexObject {

 public static final float    WIDTH = 2.0f;
 public static final float    HEIGHT = 2.0f;
 public static final float    LENGTH = 2.0f;
 public Helicopter( Component comp, Group g, int nFlags )
 {
   super( comp, g, nFlags );    
 }
 protected Group createGeometryGroup( Appearance app, Vector3d position, Vector3d scale, String szTextureFile, String szSoundFile )
 {            
   TransformGroup tg = new TransformGroup( );
   // we need to flip the helicopter model
   // 90 degrees about the X axis
   Transform3D t3d = new Transform3D( );
   t3d.rotX( Math.toRadians( -90 ) );
   tg.setTransform( t3d );
   try
   {
     tg.addChild( loadGeometryGroup( "heli.obj", app ) );
     // create an Alpha object for the Interpolator
     Alpha alpha = new Alpha( -1,
       Alpha.INCREASING_ENABLE | Alpha.DECREASING_ENABLE,
       (long) Utils.getRandomNumber( 0, 500 ),
       (long)Utils.getRandomNumber( 0, 500 ),
       (long)Utils.getRandomNumber( 20000, 5000 ),
       4000,
       100,
       (long) Utils.getRandomNumber( 20000, 5000 ),
       5000,
       50 );
     attachSplinePathInterpolator( alpha, 
       new Transform3D( ),
       new URL( ((Java3dApplet) m_Component).getWorkingDirectory( ), "heli_spline.xls" ) );
   }
   catch( Exception e )
   {
     System.err.println( e.toString( ) );
   }
   return tg;
 }
 protected int getSoundLoop( boolean bCollide )
 {
   return -1;
 }
 protected float getSoundPriority( boolean bCollide )
 {
   return 1.0f;
 }
 protected float getSoundInitialGain( boolean bCollide )
 {
   return 3.0f;
 }
 protected Point2f[] getSoundDistanceGain( boolean bCollide )
 {
   Point2f[] gainArray = new Point2f[2];
   gainArray[0] = new Point2f( 2, 0.2f );
   gainArray[1] = new Point2f( 20, 0.05f );
   return gainArray;
 }
 protected boolean getSoundInitialEnable( boolean bCollide )
 {
   return true;
 }
 protected boolean getSoundContinuousEnable( boolean bCollide )
 {
   return false;
 }
 protected Bounds getSoundSchedulingBounds( boolean bCollide )
 {
   return new BoundingSphere( new Point3d( 0,0,0 ), 20 );
 }
 protected boolean getSoundReleaseEnable( boolean bCollide )
 {
   return true;
 }

} //heli.obj /*

  1. Mon Jun 21 15:53:56 1993

g v -0.010000 13.150000 3.670000 v -0.620000 3.690000 3.670000 v -0.620000 -3.740000 3.670000 v 0.580000 -3.740000 3.670000 v 0.580000 3.690000 3.670000 v -0.010000 13.150000 0.000000 v -0.620000 3.690000 0.000000 v -0.620000 -3.740000 0.000000 v 0.580000 -3.740000 0.000000 v 0.580000 3.690000 0.000000 v 0.000000 -3.320000 6.130000 v 1.220000 -2.890000 6.130000 v 1.840000 -2.020000 6.130000 v 2.450000 -0.720000 6.130000 v 0.610000 10.060000 6.130000 v -0.590000 10.060000 6.130000 v -2.430000 -0.720000 6.130000 v -1.820000 -2.020000 6.130000 v -1.200000 -2.890000 6.130000 v 0.000000 -3.320000 3.820000 v 1.220000 -2.890000 3.820000 v 1.840000 -2.020000 3.820000 v 2.450000 -0.720000 3.820000 v 0.610000 10.060000 3.820000 v -0.590000 10.060000 3.820000 v -2.430000 -0.720000 3.820000 v -1.820000 -2.020000 3.820000 v -1.200000 -2.890000 3.820000 v 1.110000 0.000000 8.100000 v 1.890000 0.010000 7.730000 v 2.660000 0.020000 7.160000 v 1.040000 0.350000 8.100000 v 1.790000 0.600000 7.730000 v 2.530000 0.840000 7.160000 v 0.890000 0.660000 8.100000 v 1.520000 1.130000 7.730000 v 2.150000 1.580000 7.160000 v 0.640000 0.900000 8.100000 v 1.090000 1.540000 7.730000 v 1.540000 2.170000 7.160000 v 0.330000 1.070000 8.100000 v 0.570000 1.800000 7.730000 v 0.800000 2.540000 7.160000 v 0.000000 1.110000 8.100000 v -0.010000 1.890000 7.730000 v -0.010000 2.660000 7.160000 v -0.350000 1.040000 8.100000 v -0.580000 1.790000 7.730000 v -0.840000 2.530000 7.160000 v -0.660000 0.890000 8.100000 v -1.120000 1.520000 7.730000 v -1.580000 2.150000 7.160000 v -0.900000 0.640000 8.100000 v -1.540000 1.090000 7.730000 v -2.170000 1.550000 7.160000 v -1.040000 0.330000 8.100000 v -1.800000 0.570000 7.730000 v -2.540000 0.800000 7.160000 v -1.110000 0.000000 8.100000 v -1.890000 0.000000 7.730000 v -2.660000 -0.010000 7.160000 v -1.040000 -0.350000 8.100000 v -1.790000 -0.580000 7.730000 v -2.540000 -0.840000 7.160000 v -0.890000 -0.660000 8.100000 v -1.520000 -1.120000 7.730000 v -2.150000 -1.580000 7.160000 v -0.640000 -0.900000 8.100000 v -1.090000 -1.530000 7.730000 v -1.550000 -2.170000 7.160000 v -0.330000 -1.040000 8.100000 v -0.570000 -1.800000 7.730000 v -0.800000 -2.540000 7.160000 v 0.000000 -1.110000 8.100000 v 0.000000 -1.890000 7.730000 v 0.010000 -2.660000 7.160000 v 0.350000 -1.040000 8.100000 v 0.580000 -1.790000 7.730000 v 0.830000 -2.540000 7.160000 v 0.660000 -0.890000 8.100000 v 1.120000 -1.520000 7.730000 v 1.580000 -2.150000 7.160000 v 0.900000 -0.640000 8.100000 v 1.530000 -1.100000 7.730000 v 2.170000 -1.550000 7.160000 v 0.000000 0.000000 8.300000 v 1.040000 -0.330000 8.100000 v 1.800000 -0.570000 7.730000 v 2.540000 -0.800000 7.160000 v 0.000000 0.000000 7.160000 v -0.150000 -0.840000 6.860000 v 0.160000 -1.370000 6.860000 v 0.160000 -2.420000 6.860000 v 0.520000 -2.420000 6.860000 v 0.520000 -2.960000 6.860000 v 0.160000 -2.960000 6.860000 v 0.880000 -5.090000 6.860000 v 0.880000 -30.559999 6.860000 v 0.520000 -31.090000 6.860000 v -0.510000 -31.090000 6.860000 v -0.870000 -30.559999 6.860000 v -0.870000 -5.090000 6.860000 v -0.150000 -2.960000 6.860000 v -0.510000 -2.960000 6.860000 v -0.510000 -2.420000 6.860000 v -0.150000 -2.420000 6.860000 v -0.150000 -0.840000 6.890000 v 0.160000 -1.370000 6.890000 v 0.160000 -2.420000 6.890000 v 0.520000 -2.420000 6.890000 v 0.520000 -2.960000 6.890000 v 0.160000 -2.960000 6.890000 v 0.880000 -5.090000 6.890000 v 0.880000 -30.559999 6.890000 v 0.520000 -31.090000 6.890000 v -0.510000 -31.090000 6.890000 v -0.870000 -30.559999 6.890000 v -0.870000 -5.090000 6.890000 v -0.150000 -2.960000 6.890000 v -0.510000 -2.960000 6.890000 v -0.510000 -2.420000 6.890000 v -0.150000 -2.420000 6.890000 v 0.750000 -0.410000 6.860000 v 1.360000 -0.230000 6.860000 v 2.360000 -0.550000 6.860000 v 2.480000 -0.210000 6.860000 v 2.980000 -0.380000 6.860000 v 2.880000 -0.720000 6.860000 v 5.120000 -0.690000 6.860000 v 29.350000 -8.570000 6.860000 v 29.740000 -9.070000 6.860000 v 29.410000 -10.090000 6.860000 v 28.799999 -10.270000 6.860000 v 4.570000 -2.400000 6.860000 v 2.770000 -1.040000 6.860000 v 2.660000 -1.400000 6.860000 v 2.160000 -1.220000 6.860000 v 2.260000 -0.900000 6.860000 v 0.750000 -0.410000 6.900000 v 1.360000 -0.230000 6.900000 v 2.360000 -0.550000 6.900000 v 2.480000 -0.210000 6.900000 v 2.980000 -0.380000 6.900000 v 2.880000 -0.720000 6.900000 v 5.120000 -0.690000 6.900000 v 29.350000 -8.570000 6.900000 v 29.740000 -9.070000 6.900000 v 29.410000 -10.090000 6.900000 v 28.799999 -10.270000 6.900000 v 4.570000 -2.400000 6.900000 v 2.770000 -1.040000 6.900000 v 2.660000 -1.400000 6.900000 v 2.160000 -1.220000 6.900000 v 2.260000 -0.900000 6.900000 v 0.630000 0.580000 6.860000 v 0.670000 1.210000 6.860000 v 1.280000 2.060000 6.860000 v 0.990000 2.280000 6.860000 v 1.320000 2.710000 6.860000 v 1.600000 2.490000 6.860000 v 2.260000 4.640000 6.860000 v 17.240000 25.240000 6.860000 v 17.840000 25.459999 6.860000 v 18.700001 24.850000 6.860000 v 18.680000 24.200001 6.860000 v 3.700000 3.600000 6.860000 v 1.880000 2.300000 6.860000 v 2.170000 2.080000 6.860000 v 1.840000 1.650000 6.860000 v 1.560000 1.870000 6.860000 v 0.630000 0.580000 6.890000 v 0.670000 1.210000 6.890000 v 1.280000 2.060000 6.890000 v 0.990000 2.280000 6.890000 v 1.320000 2.710000 6.890000 v 1.600000 2.490000 6.890000 v 2.260000 4.640000 6.890000 v 17.240000 25.240000 6.890000 v 17.840000 25.459999 6.890000 v 18.700001 24.850000 6.890000 v 18.680000 24.200001 6.890000 v 3.700000 3.600000 6.890000 v 1.880000 2.300000 6.890000 v 2.170000 2.080000 6.890000 v 1.840000 1.650000 6.890000 v 1.560000 1.870000 6.890000 v -0.340000 0.780000 6.860000 v -0.920000 1.010000 6.860000 v -1.540000 1.870000 6.860000 v -1.830000 1.660000 6.860000 v -2.150000 2.080000 6.860000 v -1.860000 2.310000 6.860000 v -3.690000 3.600000 6.860000 v -18.660000 24.209999 6.860000 v -18.690001 24.850000 6.860000 v -17.820000 25.469999 6.860000 v -17.219999 25.250000 6.860000 v -2.250000 4.650000 6.860000 v -1.590000 2.500000 6.860000 v -1.300000 2.710000 6.860000 v -0.980000 2.280000 6.860000 v -1.260000 2.070000 6.860000 v -0.340000 0.780000 6.890000 v -0.920000 1.010000 6.890000 v -1.540000 1.870000 6.890000 v -1.830000 1.660000 6.890000 v -2.150000 2.080000 6.890000 v -1.860000 2.310000 6.890000 v -3.690000 3.600000 6.890000 v -18.660000 24.209999 6.890000 v -18.690001 24.850000 6.890000 v -17.820000 25.469999 6.890000 v -17.219999 25.250000 6.890000 v -2.250000 4.650000 6.890000 v -1.590000 2.500000 6.890000 v -1.300000 2.710000 6.890000 v -0.980000 2.280000 6.890000 v -1.260000 2.070000 6.890000 v -0.820000 -0.060000 6.860000 v -1.230000 -0.540000 6.860000 v -2.220000 -0.880000 6.860000 v -2.120000 -1.220000 6.860000 v -2.630000 -1.380000 6.860000 v -2.740000 -1.030000 6.860000 v -4.540000 -2.380000 6.860000 v -28.770000 -10.250000 6.860000 v -29.379999 -10.070000 6.860000 v -29.700001 -9.080000 6.860000 v -29.320000 -8.560000 6.860000 v -5.090000 -0.690000 6.860000 v -2.850000 -0.720000 6.860000 v -2.960000 -0.380000 6.860000 v -2.440000 -0.210000 6.860000 v -2.320000 -0.560000 6.860000 v -0.820000 -0.060000 6.890000 v -1.230000 -0.540000 6.890000 v -2.220000 -0.880000 6.890000 v -2.120000 -1.220000 6.890000 v -2.630000 -1.380000 6.890000 v -2.740000 -1.030000 6.890000 v -4.540000 -2.380000 6.890000 v -28.770000 -10.250000 6.890000 v -29.379999 -10.070000 6.890000 v -29.700001 -9.080000 6.890000 v -29.320000 -8.560000 6.890000 v -5.090000 -0.690000 6.890000 v -2.850000 -0.720000 6.890000 v -2.960000 -0.380000 6.890000 v -2.440000 -0.210000 6.890000 v -2.320000 -0.560000 6.890000 v 5.670000 -9.090000 -7.210000 v 5.670000 -7.990000 -8.530000 v 5.670000 6.200000 -8.530000 v 5.670000 6.200000 -8.750000 v 5.670000 -7.990000 -8.750000 v 5.670000 -9.180000 -7.210000 v 6.040000 -9.090000 -7.210000 v 6.040000 -7.990000 -8.530000 v 6.040000 6.200000 -8.530000 v 6.040000 6.200000 -8.750000 v 6.040000 -7.990000 -8.750000 v 6.040000 -9.180000 -7.210000 v -6.030000 -9.080000 -7.200000 v -6.030000 -7.980000 -8.520000 v -6.030000 6.200000 -8.520000 v -6.030000 6.200000 -8.740000 v -6.030000 -7.980000 -8.740000 v -6.030000 -9.170000 -7.200000 v -5.660000 -9.080000 -7.200000 v -5.660000 -7.980000 -8.520000 v -5.660000 6.200000 -8.520000 v -5.660000 6.200000 -8.740000 v -5.660000 -7.980000 -8.740000 v -5.660000 -9.170000 -7.200000 v 2.960000 -4.140000 -3.420000 v 3.130000 -4.140000 -3.320000 v 6.160000 -4.140000 -8.560000 v 5.980000 -4.140000 -8.660000 v 2.960000 -4.330000 -3.420000 v 3.130000 -4.330000 -3.320000 v 6.160000 -4.330000 -8.560000 v 5.980000 -4.330000 -8.660000 v 2.960000 2.330000 -3.400000 v 3.130000 2.330000 -3.300000 v 6.150000 2.330000 -8.540000 v 5.990000 2.330000 -8.640000 v 2.960000 2.150000 -3.400000 v 3.130000 2.150000 -3.300000 v 6.150000 2.150000 -8.540000 v 5.990000 2.150000 -8.640000 v -3.120000 -4.130000 -3.310000 v -2.950000 -4.130000 -3.410000 v -5.970000 -4.130000 -8.650000 v -6.150000 -4.130000 -8.550000 v -3.120000 -4.320000 -3.310000 v -2.950000 -4.320000 -3.410000 v -5.970000 -4.320000 -8.650000 v -6.150000 -4.320000 -8.550000 v -3.100000 2.330000 -3.300000 v -2.930000 2.330000 -3.400000 v -5.960000 2.330000 -8.630000 v -6.120000 2.330000 -8.540000 v -3.100000 2.150000 -3.300000 v -2.930000 2.150000 -3.400000 v -5.960000 2.150000 -8.630000 v -6.120000 2.150000 -8.540000 v -5.090000 33.450001 7.670000 v -5.090000 35.240002 10.180000 v -5.090000 36.660000 10.180000 v -5.090000 35.959999 6.090000 v -5.090000 34.520000 6.090000 v -4.910000 33.450001 7.670000 v -4.910000 35.240002 10.180000 v -4.910000 36.660000 10.180000 v -4.910000 35.959999 6.090000 v -4.910000 34.520000 6.090000 v 4.660000 33.450001 7.670000 v 4.660000 35.240002 10.180000 v 4.660000 36.660000 10.180000 v 4.660000 35.959999 6.090000 v 4.660000 34.520000 6.090000 v 4.840000 33.450001 7.670000 v 4.840000 35.240002 10.180000 v 4.840000 36.660000 10.180000 v 4.840000 35.959999 6.090000 v 4.840000 34.520000 6.090000 v -4.880000 36.480000 8.270000 v 4.580000 36.480000 8.270000 v 4.580000 36.480000 7.930000 v -4.880000 36.480000 7.930000 v -4.880000 34.180000 8.270000 v 4.580000 34.180000 8.270000 v 4.580000 34.180000 7.930000 v -4.880000 34.180000 7.930000 v 0.140000 36.180000 7.750000 v 0.140000 34.950001 7.750000 v 0.140000 31.260000 2.330000 v 0.140000 34.029999 -1.700000 v 0.140000 35.250000 -1.700000 v 0.140000 34.029999 2.330000 v -0.130000 36.180000 7.750000 v -0.130000 34.950001 7.750000 v -0.130000 31.260000 2.330000 v -0.130000 34.029999 -1.700000 v -0.130000 35.250000 -1.700000 v -0.130000 34.029999 2.330000 v 1.430000 -10.690000 0.000000 v 2.740000 -9.840000 0.010000 v 3.810000 -8.830000 0.020000 v 4.730000 -6.960000 0.040000 v 5.270000 -4.100000 0.040000 v 5.190000 -0.720000 0.040000 v 4.350000 2.820000 0.030000 v 1.820000 8.390000 0.010000 v 0.820000 11.770000 0.000000 v 0.660000 13.130000 0.000000 v 0.660000 10.250000 0.000000 v 1.390000 -10.690000 0.370000 v 2.640000 -9.840000 0.700000 v 3.690000 -8.830000 0.980000 v 4.570000 -6.960000 1.220000 v 5.090000 -4.100000 1.360000 v 5.020000 -0.720000 1.340000 v 4.190000 2.820000 1.120000 v 1.750000 8.390000 0.460000 v 0.790000 11.770000 0.210000 v 0.640000 13.130000 0.170000 v 0.640000 10.250000 0.170000 v 1.250000 -10.690000 0.710000 v 2.380000 -9.840000 1.340000 v 3.320000 -8.830000 1.870000 v 4.130000 -6.960000 2.320000 v 4.600000 -4.100000 2.590000 v 4.530000 -0.720000 2.550000 v 3.790000 2.820000 2.130000 v 1.580000 8.390000 0.890000 v 0.710000 11.770000 0.400000 v 0.580000 13.130000 0.330000 v 0.580000 10.250000 0.330000 v 1.030000 -10.690000 1.000000 v 1.980000 -9.840000 1.900000 v 2.750000 -8.830000 2.640000 v 3.410000 -6.960000 3.280000 v 3.800000 -4.100000 3.650000 v 3.750000 -0.720000 3.600000 v 3.140000 2.820000 3.020000 v 1.310000 8.390000 1.260000 v 0.580000 11.770000 0.570000 v 0.480000 13.130000 0.470000 v 0.480000 10.250000 0.470000 v 0.750000 -10.690000 1.230000 v 1.440000 -9.840000 2.330000 v 2.000000 -8.830000 3.250000 v 2.490000 -6.960000 4.030000 v 2.780000 -4.100000 4.480000 v 2.730000 -0.720000 4.420000 v 2.280000 2.820000 3.710000 v 0.950000 8.390000 1.550000 v 0.430000 11.770000 0.710000 v 0.340000 13.130000 0.570000 v 0.340000 10.250000 0.570000 v 0.420000 -10.690000 1.380000 v 0.810000 -9.840000 2.630000 v 1.130000 -8.830000 3.650000 v 1.410000 -6.960000 4.530000 v 1.570000 -4.100000 5.040000 v 1.550000 -0.720000 4.960000 v 1.290000 2.820000 4.160000 v 0.520000 8.390000 1.740000 v 0.230000 11.770000 0.790000 v 0.190000 13.130000 0.640000 v 0.190000 10.250000 0.640000 v 0.060000 -10.690000 1.440000 v 0.130000 -9.840000 2.750000 v 0.180000 -8.830000 3.820000 v 0.230000 -6.960000 4.750000 v 0.260000 -4.100000 5.280000 v 0.260000 -0.720000 5.210000 v 0.210000 2.820000 4.360000 v 0.070000 8.390000 1.830000 v 0.020000 11.770000 0.830000 v 0.010000 13.130000 0.670000 v 0.010000 10.250000 0.670000 v -0.290000 -10.690000 1.420000 v -0.550000 -9.840000 2.690000 v -0.760000 -8.830000 3.750000 v -0.940000 -6.960000 4.650000 v -1.040000 -4.100000 5.180000 v -1.030000 -0.720000 5.110000 v -0.860000 2.820000 4.280000 v -0.370000 8.390000 1.800000 v -0.170000 11.770000 0.810000 v -0.140000 13.130000 0.670000 v -0.140000 10.250000 0.670000 v -0.640000 -10.690000 1.300000 v -1.210000 -9.840000 2.480000 v -1.670000 -8.830000 3.450000 v -2.070000 -6.960000 4.280000 v -2.310000 -4.100000 4.760000 v -2.270000 -0.720000 4.690000 v -1.910000 2.820000 3.930000 v -0.810000 8.390000 1.650000 v -0.370000 11.770000 0.750000 v -0.310000 13.130000 0.610000 v -0.310000 10.250000 0.610000 v -0.950000 -10.690000 1.100000 v -1.780000 -9.840000 2.080000 v -2.480000 -8.830000 2.920000 v -3.070000 -6.960000 3.630000 v -3.420000 -4.100000 4.040000 v -3.370000 -0.720000 3.980000 v -2.830000 2.820000 3.330000 v -1.190000 8.390000 1.400000 v -0.550000 11.770000 0.640000 v -0.450000 13.130000 0.510000 v -0.450000 10.250000 0.510000 v -1.190000 -10.690000 0.840000 v -2.260000 -9.840000 1.600000 v -3.140000 -8.830000 2.220000 v -3.880000 -6.960000 2.760000 v -4.320000 -4.100000 3.060000 v -4.260000 -0.720000 3.020000 v -3.570000 2.820000 2.530000 v -1.510000 8.390000 1.040000 v -0.690000 11.770000 0.480000 v -0.570000 13.130000 0.390000 v -0.570000 10.250000 0.390000 v -1.370000 -10.690000 0.510000 v -2.580000 -9.840000 0.980000 v -3.590000 -8.830000 1.370000 v -4.440000 -6.960000 1.700000 v -4.940000 -4.100000 1.900000 v -4.880000 -0.720000 1.870000 v -4.090000 2.820000 1.570000 v -1.720000 8.390000 0.650000 v -0.790000 11.770000 0.290000 v -0.650000 13.130000 0.240000 v -0.650000 10.250000 0.240000 v -1.450000 -10.690000 0.160000 v -2.750000 -9.840000 0.310000 v -3.820000 -8.830000 0.440000 v -4.730000 -6.960000 0.540000 v -5.270000 -4.100000 0.610000 v -5.190000 -0.720000 0.600000 v -4.360000 2.820000 0.500000 v -1.840000 8.390000 0.210000 v -0.850000 11.770000 0.080000 v -0.690000 13.130000 0.070000 v -0.690000 10.250000 0.070000 v -1.450000 -10.690000 -0.190000 v -2.740000 -9.840000 -0.370000 v -3.810000 -8.830000 -0.510000 v -4.730000 -6.960000 -0.630000 v -5.260000 -4.100000 -0.710000 v -5.180000 -0.720000 -0.690000 v -4.340000 2.820000 -0.580000 v -1.830000 8.390000 -0.240000 v -0.850000 11.770000 -0.110000 v -0.690000 13.130000 -0.090000 v -0.690000 10.250000 -0.090000 v -1.360000 -10.690000 -0.540000 v -2.570000 -9.840000 -1.040000 v -3.570000 -8.830000 -1.440000 v -4.420000 -6.960000 -1.780000 v -4.920000 -4.100000 -1.990000 v -4.850000 -0.720000 -1.960000 v -4.070000 2.820000 -1.640000 v -1.720000 8.390000 -0.690000 v -0.790000 11.770000 -0.310000 v -0.650000 13.130000 -0.250000 v -0.650000 10.250000 -0.250000 v -1.160000 -10.690000 -0.860000 v -2.230000 -9.840000 -1.630000 v -3.100000 -8.830000 -2.280000 v -3.840000 -6.960000 -2.830000 v -4.270000 -4.100000 -3.140000 v -4.190000 -0.720000 -3.100000 v -3.530000 2.820000 -2.590000 v -1.490000 8.390000 -1.090000 v -0.690000 11.770000 -0.500000 v -0.560000 13.130000 -0.410000 v -0.560000 10.250000 -0.410000 v -0.920000 -10.690000 -1.130000 v -1.750000 -9.840000 -2.140000 v -2.430000 -8.830000 -2.970000 v -3.010000 -6.960000 -3.690000 v -3.350000 -4.100000 -4.110000 v -3.310000 -0.720000 -4.050000 v -2.770000 2.820000 -3.390000 v -1.160000 8.390000 -1.430000 v -0.540000 11.770000 -0.650000 v -0.440000 13.130000 -0.520000 v -0.440000 10.250000 -0.520000 v -0.620000 -10.690000 -1.320000 v -1.160000 -9.840000 -2.500000 v -1.620000 -8.830000 -3.490000 v -2.000000 -6.960000 -4.320000 v -2.220000 -4.100000 -4.810000 v -2.190000 -0.720000 -4.730000 v -1.840000 2.820000 -3.970000 v -0.780000 8.390000 -1.670000 v -0.360000 11.770000 -0.760000 v -0.290000 13.130000 -0.620000 v -0.290000 10.250000 -0.620000 v -0.270000 -10.690000 -1.430000 v -0.500000 -9.840000 -2.720000 v -0.690000 -8.830000 -3.770000 v -0.860000 -6.960000 -4.670000 v -0.960000 -4.100000 -5.210000 v -0.950000 -0.720000 -5.130000 v -0.790000 2.820000 -4.300000 v -0.340000 8.390000 -1.810000 v -0.160000 11.770000 -0.830000 v -0.130000 13.130000 -0.670000 v -0.130000 10.250000 -0.670000 v 0.080000 -10.690000 -1.450000 v 0.170000 -9.840000 -2.760000 v 0.250000 -8.830000 -3.820000 v 0.310000 -6.960000 -4.750000 v 0.350000 -4.100000 -5.280000 v 0.340000 -0.720000 -5.210000 v 0.280000 2.820000 -4.360000 v 0.100000 8.390000 -1.830000 v 0.030000 11.770000 -0.840000 v 0.030000 13.130000 -0.680000 v 0.030000 10.250000 -0.680000 v 0.440000 -10.690000 -1.380000 v 0.850000 -9.840000 -2.620000 v 1.190000 -8.830000 -3.640000 v 1.480000 -6.960000 -4.510000 v 1.650000 -4.100000 -5.020000 v 1.620000 -0.720000 -4.940000 v 1.360000 2.820000 -4.150000 v 0.560000 8.390000 -1.740000 v 0.240000 11.770000 -0.800000 v 0.190000 13.130000 -0.650000 v 0.190000 10.250000 -0.650000 v 0.760000 -10.690000 -1.220000 v 1.480000 -9.840000 -2.320000 v 2.050000 -8.830000 -3.220000 v 2.550000 -6.960000 -4.000000 v 2.840000 -4.100000 -4.440000 v 2.810000 -0.720000 -4.380000 v 2.330000 2.820000 -3.680000 v 0.980000 8.390000 -1.540000 v 0.430000 11.770000 -0.710000 v 0.350000 13.130000 -0.570000 v 0.350000 10.250000 -0.570000 v 1.040000 -10.690000 -0.990000 v 2.010000 -9.840000 -1.870000 v 2.790000 -8.830000 -2.600000 v 3.470000 -6.960000 -3.230000 v 3.870000 -4.100000 -3.590000 v 3.800000 -0.720000 -3.550000 v 3.190000 2.820000 -2.970000 v 1.330000 8.390000 -1.250000 v 0.580000 11.770000 -0.570000 v 0.490000 13.130000 -0.470000 v 0.490000 10.250000 -0.470000 v 1.260000 -10.690000 -0.690000 v 2.410000 -9.840000 -1.310000 v 3.350000 -8.830000 -1.820000 v 4.170000 -6.960000 -2.260000 v 4.630000 -4.100000 -2.520000 v 4.570000 -0.720000 -2.480000 v 3.830000 2.820000 -2.080000 v 1.600000 8.390000 -0.870000 v 0.720000 11.770000 -0.400000 v 0.580000 13.130000 -0.320000 v 0.580000 10.250000 -0.320000 v -0.010000 -11.200000 0.000000 v 1.390000 -10.690000 -0.350000 v 2.660000 -9.840000 -0.670000 v 3.700000 -8.830000 -0.930000 v 4.600000 -6.960000 -1.150000 v 5.120000 -4.100000 -1.280000 v 5.040000 -0.720000 -1.260000 v 4.230000 2.820000 -1.040000 v 1.760000 8.390000 -0.440000 v 0.790000 11.770000 -0.200000 v 0.650000 13.130000 -0.160000 v 0.650000 10.250000 -0.160000 v -0.010000 10.250000 0.000000 v 0.450000 31.430000 3.060000 v 0.990000 5.730000 3.050000 v 0.430000 31.430000 2.940000 v 0.960000 5.730000 2.810000 v 0.390000 31.430000 2.840000 v 0.860000 5.730000 2.580000 v 0.330000 31.430000 2.740000 v 0.710000 5.730000 2.380000 v 0.230000 31.430000 2.680000 v 0.520000 5.730000 2.210000 v 0.130000 31.430000 2.620000 v 0.290000 5.730000 2.100000 v 0.020000 31.430000 2.610000 v 0.040000 5.730000 2.070000 v -0.080000 31.430000 2.610000 v -0.190000 5.730000 2.080000 v -0.190000 31.430000 2.640000 v -0.430000 5.730000 2.160000 v -0.290000 31.430000 2.710000 v -0.630000 5.730000 2.300000 v -0.360000 31.430000 2.800000 v -0.800000 5.730000 2.480000 v -0.420000 31.430000 2.900000 v -0.920000 5.730000 2.710000 v -0.450000 31.430000 3.010000 v -0.990000 5.730000 2.940000 v -0.450000 31.430000 3.120000 v -0.980000 5.730000 3.190000 v -0.420000 31.430000 3.230000 v -0.920000 5.730000 3.430000 v -0.360000 31.430000 3.330000 v -0.790000 5.730000 3.650000 v -0.280000 31.430000 3.410000 v -0.620000 5.730000 3.830000 v -0.190000 31.430000 3.470000 v -0.410000 5.730000 3.970000 v -0.070000 31.430000 3.510000 v -0.170000 5.730000 4.030000 v 0.030000 31.430000 3.510000 v 0.060000 5.730000 4.050000 v 0.130000 31.430000 3.490000 v 0.310000 5.730000 4.000000 v 0.240000 31.430000 3.440000 v 0.520000 5.730000 3.900000 v 0.330000 31.430000 3.370000 v 0.720000 5.730000 3.740000 v 0.390000 31.430000 3.270000 v 0.870000 5.730000 3.530000 v 0.000000 31.620001 3.060000 v 0.440000 31.430000 3.170000 v 0.960000 5.730000 3.300000 v 0.000000 2.760000 3.060000

  1. 675 vertices
  2. 0 vertex parms
  3. 0 texture vertices
  4. 0 normals

g TURBINE f 1 6 7 f 1 7 2 f 2 7 8 f 2 8 3 f 3 8 9 f 3 9 4 f 4 9 10 f 4 10 5 f 5 10 6 f 5 6 1 f 1 2 3 f 6 8 7 f 1 3 4 f 6 9 8 f 1 4 5 f 6 10 9 f 11 20 21 f 11 21 12 f 12 21 22 f 12 22 13 f 13 22 23 f 13 23 14 f 14 23 24 f 14 24 15 f 15 24 25 f 15 25 16 f 16 25 26 f 16 26 17 f 17 26 27 f 17 27 18 f 18 27 28 f 18 28 19 f 19 28 20 f 19 20 11 f 11 12 13 f 20 22 21 f 11 13 14 f 20 23 22 f 11 14 15 f 20 24 23 f 11 15 16 f 20 25 24 f 11 16 17 f 20 26 25 f 11 17 18 f 20 27 26 f 11 18 19 f 20 28 27 g ROTOR f 86 29 32 f 29 30 33 f 29 33 32 f 30 31 34 f 30 34 33 f 31 90 34 f 86 32 35 f 32 33 36 f 32 36 35 f 33 34 37 f 33 37 36 f 34 90 37 f 86 35 38 f 35 36 39 f 35 39 38 f 36 37 40 f 36 40 39 f 37 90 40 f 86 38 41 f 38 39 42 f 38 42 41 f 39 40 43 f 39 43 42 f 40 90 43 f 86 41 44 f 41 42 45 f 41 45 44 f 42 43 46 f 42 46 45 f 43 90 46 f 86 44 47 f 44 45 48 f 44 48 47 f 45 46 49 f 45 49 48 f 46 90 49 f 86 47 50 f 47 48 51 f 47 51 50 f 48 49 52 f 48 52 51 f 49 90 52 f 86 50 53 f 50 51 54 f 50 54 53 f 51 52 55 f 51 55 54 f 52 90 55 f 86 53 56 f 53 54 57 f 53 57 56 f 54 55 58 f 54 58 57 f 55 90 58 f 86 56 59 f 56 57 60 f 56 60 59 f 57 58 61 f 57 61 60 f 58 90 61 f 86 59 62 f 59 60 63 f 59 63 62 f 60 61 64 f 60 64 63 f 61 90 64 f 86 62 65 f 62 63 66 f 62 66 65 f 63 64 67 f 63 67 66 f 64 90 67 f 86 65 68 f 65 66 69 f 65 69 68 f 66 67 70 f 66 70 69 f 67 90 70 f 86 68 71 f 68 69 72 f 68 72 71 f 69 70 73 f 69 73 72 f 70 90 73 f 86 71 74 f 71 72 75 f 71 75 74 f 72 73 76 f 72 76 75 f 73 90 76 f 86 74 77 f 74 75 78 f 74 78 77 f 75 76 79 f 75 79 78 f 76 90 79 f 86 77 80 f 77 78 81 f 77 81 80 f 78 79 82 f 78 82 81 f 79 90 82 f 86 80 83 f 80 81 84 f 80 84 83 f 81 82 85 f 81 85 84 f 82 90 85 f 86 83 87 f 83 84 88 f 83 88 87 f 84 85 89 f 84 89 88 f 85 90 89 f 86 87 29 f 87 88 30 f 87 30 29 f 88 89 31 f 88 31 30 f 89 90 31 f 91 107 108 f 91 108 92 f 92 108 109 f 92 109 93 f 93 109 110 f 93 110 94 f 94 110 111 f 94 111 95 f 95 111 112 f 95 112 96 f 96 112 113 f 96 113 97 f 97 113 114 f 97 114 98 f 98 114 115 f 98 115 99 f 99 115 116 f 99 116 100 f 100 116 117 f 100 117 101 f 101 117 118 f 101 118 102 f 102 118 119 f 102 119 103 f 103 119 120 f 103 120 104 f 104 120 121 f 104 121 105 f 105 121 122 f 105 122 106 f 106 122 107 f 106 107 91 f 93 94 95 f 109 111 110 f 93 95 96 f 109 112 111 f 96 97 98 f 112 114 113 f 96 98 99 f 112 115 114 f 96 99 100 f 112 116 115 f 93 96 100 f 109 116 112 f 103 104 105 f 119 121 120 f 103 105 106 f 119 122 121 f 106 91 92 f 122 108 107 f 103 106 92 f 119 108 122 f 103 92 93 f 119 109 108 f 93 100 101 f 109 117 116 f 93 101 102 f 109 118 117 f 93 102 103 f 109 119 118 f 123 139 140 f 123 140 124 f 124 140 141 f 124 141 125 f 125 141 142 f 125 142 126 f 126 142 143 f 126 143 127 f 127 143 144 f 127 144 128 f 128 144 145 f 128 145 129 f 129 145 146 f 129 146 130 f 130 146 147 f 130 147 131 f 131 147 148 f 131 148 132 f 132 148 149 f 132 149 133 f 133 149 150 f 133 150 134 f 134 150 151 f 134 151 135 f 135 151 152 f 135 152 136 f 136 152 153 f 136 153 137 f 137 153 154 f 137 154 138 f 138 154 139 f 138 139 123 f 125 126 127 f 141 143 142 f 125 127 128 f 141 144 143 f 128 129 130 f 144 146 145 f 128 130 131 f 144 147 146 f 128 131 132 f 144 148 147 f 125 128 132 f 141 148 144 f 135 136 137 f 151 153 152 f 135 137 138 f 151 154 153 f 138 123 124 f 154 140 139 f 135 138 124 f 151 140 154 f 135 124 125 f 151 141 140 f 125 132 133 f 141 149 148 f 125 133 134 f 141 150 149 f 125 134 135 f 141 151 150 f 155 171 172 f 155 172 156 f 156 172 173 f 156 173 157 f 157 173 174 f 157 174 158 f 158 174 175 f 158 175 159 f 159 175 176 f 159 176 160 f 160 176 177 f 160 177 161 f 161 177 178 f 161 178 162 f 162 178 179 f 162 179 163 f 163 179 180 f 163 180 164 f 164 180 181 f 164 181 165 f 165 181 182 f 165 182 166 f 166 182 183 f 166 183 167 f 167 183 184 f 167 184 168 f 168 184 185 f 168 185 169 f 169 185 186 f 169 186 170 f 170 186 171 f 170 171 155 f 157 158 159 f 173 175 174 f 157 159 160 f 173 176 175 f 160 161 162 f 176 178 177 f 160 162 163 f 176 179 178 f 160 163 164 f 176 180 179 f 157 160 164 f 173 180 176 f 167 168 169 f 183 185 184 f 167 169 170 f 183 186 185 f 170 155 156 f 186 172 171 f 167 170 156 f 183 172 186 f 167 156 157 f 183 173 172 f 157 164 165 f 173 181 180 f 157 165 166 f 173 182 181 f 157 166 167 f 173 183 182 f 187 203 204 f 187 204 188 f 188 204 205 f 188 205 189 f 189 205 206 f 189 206 190 f 190 206 207 f 190 207 191 f 191 207 208 f 191 208 192 f 192 208 209 f 192 209 193 f 193 209 210 f 193 210 194 f 194 210 211 f 194 211 195 f 195 211 212 f 195 212 196 f 196 212 213 f 196 213 197 f 197 213 214 f 197 214 198 f 198 214 215 f 198 215 199 f 199 215 216 f 199 216 200 f 200 216 217 f 200 217 201 f 201 217 218 f 201 218 202 f 202 218 203 f 202 203 187 f 189 190 191 f 205 207 206 f 189 191 192 f 205 208 207 f 192 193 194 f 208 210 209 f 192 194 195 f 208 211 210 f 192 195 196 f 208 212 211 f 189 192 196 f 205 212 208 f 199 200 201 f 215 217 216 f 199 201 202 f 215 218 217 f 202 187 188 f 218 204 203 f 199 202 188 f 215 204 218 f 199 188 189 f 215 205 204 f 189 196 197 f 205 213 212 f 189 197 198 f 205 214 213 f 189 198 199 f 205 215 214 f 219 235 236 f 219 236 220 f 220 236 237 f 220 237 221 f 221 237 238 f 221 238 222 f 222 238 239 f 222 239 223 f 223 239 240 f 223 240 224 f 224 240 241 f 224 241 225 f 225 241 242 f 225 242 226 f 226 242 243 f 226 243 227 f 227 243 244 f 227 244 228 f 228 244 245 f 228 245 229 f 229 245 246 f 229 246 230 f 230 246 247 f 230 247 231 f 231 247 248 f 231 248 232 f 232 248 249 f 232 249 233 f 233 249 250 f 233 250 234 f 234 250 235 f 234 235 219 f 221 222 223 f 237 239 238 f 221 223 224 f 237 240 239 f 224 225 226 f 240 242 241 f 224 226 227 f 240 243 242 f 224 227 228 f 240 244 243 f 221 224 228 f 237 244 240 f 231 232 233 f 247 249 248 f 231 233 234 f 247 250 249 f 234 219 220 f 250 236 235 f 231 234 220 f 247 236 250 f 231 220 221 f 247 237 236 f 221 228 229 f 237 245 244 f 221 229 230 f 237 246 245 f 221 230 231 f 237 247 246 g FEET f 251 257 258 f 251 258 252 f 252 258 259 f 252 259 253 f 253 259 260 f 253 260 254 f 254 260 261 f 254 261 255 f 255 261 262 f 255 262 256 f 256 262 257 f 256 257 251 f 252 253 254 f 258 260 259 f 252 254 255 f 258 261 260 f 251 252 255 f 257 261 258 f 251 255 256 f 257 262 261 f 263 269 270 f 263 270 264 f 264 270 271 f 264 271 265 f 265 271 272 f 265 272 266 f 266 272 273 f 266 273 267 f 267 273 274 f 267 274 268 f 268 274 269 f 268 269 263 f 264 265 266 f 270 272 271 f 264 266 267 f 270 273 272 f 263 264 267 f 269 273 270 f 263 267 268 f 269 274 273 g LEGS f 275 279 280 f 275 280 276 f 276 280 281 f 276 281 277 f 277 281 282 f 277 282 278 f 278 282 279 f 278 279 275 f 275 276 277 f 279 281 280 f 275 277 278 f 279 282 281 f 283 287 288 f 283 288 284 f 284 288 289 f 284 289 285 f 285 289 290 f 285 290 286 f 286 290 287 f 286 287 283 f 283 284 285 f 287 289 288 f 283 285 286 f 287 290 289 f 291 295 296 f 291 296 292 f 292 296 297 f 292 297 293 f 293 297 298 f 293 298 294 f 294 298 295 f 294 295 291 f 291 292 293 f 295 297 296 f 291 293 294 f 295 298 297 f 299 303 304 f 299 304 300 f 300 304 305 f 300 305 301 f 301 305 306 f 301 306 302 f 302 306 303 f 302 303 299 f 299 300 301 f 303 305 304 f 299 301 302 f 303 306 305 g TAIL f 307 312 313 f 307 313 308 f 308 313 314 f 308 314 309 f 309 314 315 f 309 315 310 f 310 315 316 f 310 316 311 f 311 316 312 f 311 312 307 f 307 308 309 f 312 314 313 f 307 309 310 f 312 315 314 f 307 310 311 f 312 316 315 f 317 322 323 f 317 323 318 f 318 323 324 f 318 324 319 f 319 324 325 f 319 325 320 f 320 325 326 f 320 326 321 f 321 326 322 f 321 322 317 f 317 318 319 f 322 324 323 f 317 319 320 f 322 325 324 f 317 320 321 f 322 326 325 f 327 331 332 f 327 332 328 f 328 332 333 f 328 333 329 f 329 333 334 f 329 334 330 f 330 334 331 f 330 331 327 f 327 328 329 f 331 333 332 f 327 329 330 f 331 334 333 f 335 341 342 f 335 342 336 f 336 342 343 f 336 343 337 f 337 343 344 f 337 344 338 f 338 344 345 f 338 345 339 f 339 345 346 f 339 346 340 f 340 346 341 f 340 341 335 f 340 335 336 f 346 342 341 f 340 336 337 f 346 343 342 f 337 338 339 f 343 345 344 f 337 339 340 f 343 346 345 g BODY f 611 347 358 f 347 348 359 f 347 359 358 f 348 349 360 f 348 360 359 f 349 350 361 f 349 361 360 f 350 351 362 f 350 362 361 f 351 352 363 f 351 363 362 f 352 353 364 f 352 364 363 f 353 354 365 f 353 365 364 f 354 355 366 f 354 366 365 f 355 356 367 f 355 367 366 f 356 357 368 f 356 368 367 f 357 623 368 f 611 358 369 f 358 359 370 f 358 370 369 f 359 360 371 f 359 371 370 f 360 361 372 f 360 372 371 f 361 362 373 f 361 373 372 f 362 363 374 f 362 374 373 f 363 364 375 f 363 375 374 f 364 365 376 f 364 376 375 f 365 366 377 f 365 377 376 f 366 367 378 f 366 378 377 f 367 368 379 f 367 379 378 f 368 623 379 f 611 369 380 f 369 370 381 f 369 381 380 f 370 371 382 f 370 382 381 f 371 372 383 f 371 383 382 f 372 373 384 f 372 384 383 f 373 374 385 f 373 385 384 f 374 375 386 f 374 386 385 f 375 376 387 f 375 387 386 f 376 377 388 f 376 388 387 f 377 378 389 f 377 389 388 f 378 379 390 f 378 390 389 f 379 623 390 f 611 380 391 f 380 381 392 f 380 392 391 f 381 382 393 f 381 393 392 f 382 383 394 f 382 394 393 f 383 384 395 f 383 395 394 f 384 385 396 f 384 396 395 f 385 386 397 f 385 397 396 f 386 387 398 f 386 398 397 f 387 388 399 f 387 399 398 f 388 389 400 f 388 400 399 f 389 390 401 f 389 401 400 f 390 623 401 f 611 391 402 f 391 392 403 f 391 403 402 f 392 393 404 f 392 404 403 f 393 394 405 f 393 405 404 f 394 395 406 f 394 406 405 f 395 396 407 f 395 407 406 f 396 397 408 f 396 408 407 f 397 398 409 f 397 409 408 f 398 399 410 f 398 410 409 f 399 400 411 f 399 411 410 f 400 401 412 f 400 412 411 f 401 623 412 f 611 402 413 f 402 403 414 f 402 414 413 f 403 404 415 f 403 415 414 f 404 405 416 f 404 416 415 f 405 406 417 f 405 417 416 f 406 407 418 f 406 418 417 f 407 408 419 f 407 419 418 f 408 409 420 f 408 420 419 f 409 410 421 f 409 421 420 f 410 411 422 f 410 422 421 f 411 412 423 f 411 423 422 f 412 623 423 f 611 413 424 f 413 414 425 f 413 425 424 f 414 415 426 f 414 426 425 f 415 416 427 f 415 427 426 f 416 417 428 f 416 428 427 f 417 418 429 f 417 429 428 f 418 419 430 f 418 430 429 f 419 420 431 f 419 431 430 f 420 421 432 f 420 432 431 f 421 422 433 f 421 433 432 f 422 423 434 f 422 434 433 f 423 623 434 f 611 424 435 f 424 425 436 f 424 436 435 f 425 426 437 f 425 437 436 f 426 427 438 f 426 438 437 f 427 428 439 f 427 439 438 f 428 429 440 f 428 440 439 f 429 430 441 f 429 441 440 f 430 431 442 f 430 442 441 f 431 432 443 f 431 443 442 f 432 433 444 f 432 444 443 f 433 434 445 f 433 445 444 f 434 623 445 f 611 435 446 f 435 436 447 f 435 447 446 f 436 437 448 f 436 448 447 f 437 438 449 f 437 449 448 f 438 439 450 f 438 450 449 f 439 440 451 f 439 451 450 f 440 441 452 f 440 452 451 f 441 442 453 f 441 453 452 f 442 443 454 f 442 454 453 f 443 444 455 f 443 455 454 f 444 445 456 f 444 456 455 f 445 623 456 f 611 446 457 f 446 447 458 f 446 458 457 f 447 448 459 f 447 459 458 f 448 449 460 f 448 460 459 f 449 450 461 f 449 461 460 f 450 451 462 f 450 462 461 f 451 452 463 f 451 463 462 f 452 453 464 f 452 464 463 f 453 454 465 f 453 465 464 f 454 455 466 f 454 466 465 f 455 456 467 f 455 467 466 f 456 623 467 f 611 457 468 f 457 458 469 f 457 469 468 f 458 459 470 f 458 470 469 f 459 460 471 f 459 471 470 f 460 461 472 f 460 472 471 f 461 462 473 f 461 473 472 f 462 463 474 f 462 474 473 f 463 464 475 f 463 475 474 f 464 465 476 f 464 476 475 f 465 466 477 f 465 477 476 f 466 467 478 f 466 478 477 f 467 623 478 f 611 468 479 f 468 469 480 f 468 480 479 f 469 470 481 f 469 481 480 f 470 471 482 f 470 482 481 f 471 472 483 f 471 483 482 f 472 473 484 f 472 484 483 f 473 474 485 f 473 485 484 f 474 475 486 f 474 486 485 f 475 476 487 f 475 487 486 f 476 477 488 f 476 488 487 f 477 478 489 f 477 489 488 f 478 623 489 f 611 479 490 f 479 480 491 f 479 491 490 f 480 481 492 f 480 492 491 f 481 482 493 f 481 493 492 f 482 483 494 f 482 494 493 f 483 484 495 f 483 495 494 f 484 485 496 f 484 496 495 f 485 486 497 f 485 497 496 f 486 487 498 f 486 498 497 f 487 488 499 f 487 499 498 f 488 489 500 f 488 500 499 f 489 623 500 f 611 490 501 f 490 491 502 f 490 502 501 f 491 492 503 f 491 503 502 f 492 493 504 f 492 504 503 f 493 494 505 f 493 505 504 f 494 495 506 f 494 506 505 f 495 496 507 f 495 507 506 f 496 497 508 f 496 508 507 f 497 498 509 f 497 509 508 f 498 499 510 f 498 510 509 f 499 500 511 f 499 511 510 f 500 623 511 f 611 501 512 f 501 502 513 f 501 513 512 f 502 503 514 f 502 514 513 f 503 504 515 f 503 515 514 f 504 505 516 f 504 516 515 f 505 506 517 f 505 517 516 f 506 507 518 f 506 518 517 f 507 508 519 f 507 519 518 f 508 509 520 f 508 520 519 f 509 510 521 f 509 521 520 f 510 511 522 f 510 522 521 f 511 623 522 f 611 512 523 f 512 513 524 f 512 524 523 f 513 514 525 f 513 525 524 f 514 515 526 f 514 526 525 f 515 516 527 f 515 527 526 f 516 517 528 f 516 528 527 f 517 518 529 f 517 529 528 f 518 519 530 f 518 530 529 f 519 520 531 f 519 531 530 f 520 521 532 f 520 532 531 f 521 522 533 f 521 533 532 f 522 623 533 f 611 523 534 f 523 524 535 f 523 535 534 f 524 525 536 f 524 536 535 f 525 526 537 f 525 537 536 f 526 527 538 f 526 538 537 f 527 528 539 f 527 539 538 f 528 529 540 f 528 540 539 f 529 530 541 f 529 541 540 f 530 531 542 f 530 542 541 f 531 532 543 f 531 543 542 f 532 533 544 f 532 544 543 f 533 623 544 f 611 534 545 f 534 535 546 f 534 546 545 f 535 536 547 f 535 547 546 f 536 537 548 f 536 548 547 f 537 538 549 f 537 549 548 f 538 539 550 f 538 550 549 f 539 540 551 f 539 551 550 f 540 541 552 f 540 552 551 f 541 542 553 f 541 553 552 f 542 543 554 f 542 554 553 f 543 544 555 f 543 555 554 f 544 623 555 f 611 545 556 f 545 546 557 f 545 557 556 f 546 547 558 f 546 558 557 f 547 548 559 f 547 559 558 f 548 549 560 f 548 560 559 f 549 550 561 f 549 561 560 f 550 551 562 f 550 562 561 f 551 552 563 f 551 563 562 f 552 553 564 f 552 564 563 f 553 554 565 f 553 565 564 f 554 555 566 f 554 566 565 f 555 623 566 f 611 556 567 f 556 557 568 f 556 568 567 f 557 558 569 f 557 569 568 f 558 559 570 f 558 570 569 f 559 560 571 f 559 571 570 f 560 561 572 f 560 572 571 f 561 562 573 f 561 573 572 f 562 563 574 f 562 574 573 f 563 564 575 f 563 575 574 f 564 565 576 f 564 576 575 f 565 566 577 f 565 577 576 f 566 623 577 f 611 567 578 f 567 568 579 f 567 579 578 f 568 569 580 f 568 580 579 f 569 570 581 f 569 581 580 f 570 571 582 f 570 582 581 f 571 572 583 f 571 583 582 f 572 573 584 f 572 584 583 f 573 574 585 f 573 585 584 f 574 575 586 f 574 586 585 f 575 576 587 f 575 587 586 f 576 577 588 f 576 588 587 f 577 623 588 f 611 578 589 f 578 579 590 f 578 590 589 f 579 580 591 f 579 591 590 f 580 581 592 f 580 592 591 f 581 582 593 f 581 593 592 f 582 583 594 f 582 594 593 f 583 584 595 f 583 595 594 f 584 585 596 f 584 596 595 f 585 586 597 f 585 597 596 f 586 587 598 f 586 598 597 f 587 588 599 f 587 599 598 f 588 623 599 f 611 589 600 f 589 590 601 f 589 601 600 f 590 591 602 f 590 602 601 f 591 592 603 f 591 603 602 f 592 593 604 f 592 604 603 f 593 594 605 f 593 605 604 f 594 595 606 f 594 606 605 f 595 596 607 f 595 607 606 f 596 597 608 f 596 608 607 f 597 598 609 f 597 609 608 f 598 599 610 f 598 610 609 f 599 623 610 f 611 600 612 f 600 601 613 f 600 613 612 f 601 602 614 f 601 614 613 f 602 603 615 f 602 615 614 f 603 604 616 f 603 616 615 f 604 605 617 f 604 617 616 f 605 606 618 f 605 618 617 f 606 607 619 f 606 619 618 f 607 608 620 f 607 620 619 f 608 609 621 f 608 621 620 f 609 610 622 f 609 622 621 f 610 623 622 f 611 612 347 f 612 613 348 f 612 348 347 f 613 614 349 f 613 349 348 f 614 615 350 f 614 350 349 f 615 616 351 f 615 351 350 f 616 617 352 f 616 352 351 f 617 618 353 f 617 353 352 f 618 619 354 f 618 354 353 f 619 620 355 f 619 355 354 f 620 621 356 f 620 356 355 f 621 622 357 f 621 357 356 f 622 623 357 g TUBE f 672 624 626 f 624 625 627 f 624 627 626 f 625 675 627 f 672 626 628 f 626 627 629 f 626 629 628 f 627 675 629 f 672 628 630 f 628 629 631 f 628 631 630 f 629 675 631 f 672 630 632 f 630 631 633 f 630 633 632 f 631 675 633 f 672 632 634 f 632 633 635 f 632 635 634 f 633 675 635 f 672 634 636 f 634 635 637 f 634 637 636 f 635 675 637 f 672 636 638 f 636 637 639 f 636 639 638 f 637 675 639 f 672 638 640 f 638 639 641 f 638 641 640 f 639 675 641 f 672 640 642 f 640 641 643 f 640 643 642 f 641 675 643 f 672 642 644 f 642 643 645 f 642 645 644 f 643 675 645 f 672 644 646 f 644 645 647 f 644 647 646 f 645 675 647 f 672 646 648 f 646 647 649 f 646 649 648 f 647 675 649 f 672 648 650 f 648 649 651 f 648 651 650 f 649 675 651 f 672 650 652 f 650 651 653 f 650 653 652 f 651 675 653 f 672 652 654 f 652 653 655 f 652 655 654 f 653 675 655 f 672 654 656 f 654 655 657 f 654 657 656 f 655 675 657 f 672 656 658 f 656 657 659 f 656 659 658 f 657 675 659 f 672 658 660 f 658 659 661 f 658 661 660 f 659 675 661 f 672 660 662 f 660 661 663 f 660 663 662 f 661 675 663 f 672 662 664 f 662 663 665 f 662 665 664 f 663 675 665 f 672 664 666 f 664 665 667 f 664 667 666 f 665 675 667 f 672 666 668 f 666 667 669 f 666 669 668 f 667 675 669 f 672 668 670 f 668 669 671 f 668 671 670 f 669 675 671 f 672 670 673 f 670 671 674 f 670 674 673 f 671 675 674 f 672 673 624 f 673 674 625 f 673 625 624 f 674 675 625

  1. 1270 elements
  • /


      </source>
   
  
 
  



This uses the class SimpleMorphBehaviour to animate

   <source lang="java">

/* Essential Java 3D Fast Ian Palmer Publisher: Springer-Verlag ISBN: 1-85233-394-4

  • /

import java.awt.BorderLayout; import java.awt.Button; import java.awt.Frame; import java.awt.Panel; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.util.Enumeration; import javax.media.j3d.Alpha; import javax.media.j3d.AmbientLight; import javax.media.j3d.Appearance; import javax.media.j3d.Behavior; import javax.media.j3d.BoundingSphere; import javax.media.j3d.BranchGroup; import javax.media.j3d.Canvas3D; import javax.media.j3d.DirectionalLight; import javax.media.j3d.GeometryArray; import javax.media.j3d.IndexedQuadArray; import javax.media.j3d.Locale; import javax.media.j3d.Material; import javax.media.j3d.Morph; import javax.media.j3d.PhysicalBody; import javax.media.j3d.PhysicalEnvironment; import javax.media.j3d.Transform3D; import javax.media.j3d.TransformGroup; import javax.media.j3d.View; import javax.media.j3d.ViewPlatform; import javax.media.j3d.VirtualUniverse; import javax.media.j3d.WakeupCriterion; import javax.media.j3d.WakeupOnAWTEvent; import javax.media.j3d.WakeupOnElapsedFrames; import javax.media.j3d.WakeupOr; import javax.vecmath.AxisAngle4d; import javax.vecmath.Color3f; import javax.vecmath.Point3d; import javax.vecmath.Point3f; import javax.vecmath.Vector3f; /**

* This uses the class SimpleMorphBehaviour to animate a shape between two key
* shapes: a cube and a pyramid. The Morph object is the same as that used in
* the program SimpleMorph, but this time we use an alpha generator to drive the
* animation.
* 
* @author I.J.Palmer
* @version 1.0
* @see SimpleMorphBehaviour
* @see SimpleMorph
*/

public class SimpleMorph2 extends Frame implements ActionListener {

 protected Canvas3D myCanvas3D = new Canvas3D(null);
 protected Button exitButton = new Button("Exit");
 /** This performs the animation */
 protected Morph myMorph;
 /** This drives the Morph object */
 protected SimpleMorphBehaviour myBehave;
 /** The active bounds for the behaviour */
 protected BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0,
     0.0), 100.0);
 /**
  * Build the view branch of the scene graph
  * 
  * @return BranchGroup that is the root of the view branch
  */
 protected BranchGroup buildViewBranch(Canvas3D c) {
   BranchGroup viewBranch = new BranchGroup();
   Transform3D viewXfm = new Transform3D();
   viewXfm.set(new Vector3f(0.0f, 0.0f, 5.0f));
   TransformGroup viewXfmGroup = new TransformGroup(viewXfm);
   ViewPlatform myViewPlatform = new ViewPlatform();
   PhysicalBody myBody = new PhysicalBody();
   PhysicalEnvironment myEnvironment = new PhysicalEnvironment();
   viewXfmGroup.addChild(myViewPlatform);
   viewBranch.addChild(viewXfmGroup);
   View myView = new View();
   myView.addCanvas3D(c);
   myView.attachViewPlatform(myViewPlatform);
   myView.setPhysicalBody(myBody);
   myView.setPhysicalEnvironment(myEnvironment);
   return viewBranch;
 }
 /**
  * Add some lights to the scene graph
  * 
  * @param b
  *            BranchGroup that the lights are added to
  */
 protected void addLights(BranchGroup b) {
   Color3f ambLightColour = new Color3f(0.5f, 0.5f, 0.5f);
   AmbientLight ambLight = new AmbientLight(ambLightColour);
   ambLight.setInfluencingBounds(bounds);
   Color3f dirLightColour = new Color3f(1.0f, 1.0f, 1.0f);
   Vector3f dirLightDir = new Vector3f(-1.0f, -1.0f, -1.0f);
   DirectionalLight dirLight = new DirectionalLight(dirLightColour,
       dirLightDir);
   dirLight.setInfluencingBounds(bounds);
   b.addChild(ambLight);
   b.addChild(dirLight);
 }
 /**
  * Create the Morph from the given shapes
  * 
  * @param theShapes
  *            GeometryArray that stores the shapes for the Morph
  * @param app
  *            Appearnce used for the shapes
  * @return Morph that uses the given shapes
  */
 protected Morph createMorph(GeometryArray[] theShapes, Appearance app) {
   double[] weights = { 1.0, 0.0 };
   Alpha morphAlpha = new Alpha(-1, Alpha.INCREASING_ENABLE
       | Alpha.DECREASING_ENABLE, 0, 0, 4000, 2000, 0, 4000, 2000, 0);
   myMorph = new Morph(theShapes, app);
   myMorph.setWeights(weights);
   myMorph.setCapability(Morph.ALLOW_WEIGHTS_WRITE);
   myBehave = new SimpleMorphBehaviour(morphAlpha, myMorph);
   myBehave.setSchedulingBounds(bounds);
   return myMorph;
 }
 /**
  * Build the content branch for the scene graph
  * 
  * @return BranchGroup that is the root of the content
  */
 protected BranchGroup buildContentBranch() {
   Appearance app = new Appearance();
   Color3f ambientColour = new Color3f(1.0f, 0.0f, 0.0f);
   Color3f emissiveColour = new Color3f(0.0f, 0.0f, 0.0f);
   Color3f specularColour = new Color3f(1.0f, 1.0f, 1.0f);
   Color3f diffuseColour = new Color3f(1.0f, 0.0f, 0.0f);
   float shininess = 20.0f;
   app.setMaterial(new Material(ambientColour, emissiveColour,
       diffuseColour, specularColour, shininess));
   //Make the cube key shape
   IndexedQuadArray indexedCube = new IndexedQuadArray(8,
       IndexedQuadArray.COORDINATES | IndexedQuadArray.NORMALS, 24);
   Point3f[] cubeCoordinates = { new Point3f(1.0f, 1.0f, 1.0f),
       new Point3f(-1.0f, 1.0f, 1.0f),
       new Point3f(-1.0f, -1.0f, 1.0f),
       new Point3f(1.0f, -1.0f, 1.0f), new Point3f(1.0f, 1.0f, -1.0f),
       new Point3f(-1.0f, 1.0f, -1.0f),
       new Point3f(-1.0f, -1.0f, -1.0f),
       new Point3f(1.0f, -1.0f, -1.0f) };
   Vector3f[] cubeNormals = { new Vector3f(0.0f, 0.0f, 1.0f),
       new Vector3f(0.0f, 0.0f, -1.0f),
       new Vector3f(1.0f, 0.0f, 0.0f),
       new Vector3f(-1.0f, 0.0f, 0.0f),
       new Vector3f(0.0f, 1.0f, 0.0f), new Vector3f(0.0f, -1.0f, 0.0f) };
   int cubeCoordIndices[] = { 0, 1, 2, 3, 7, 6, 5, 4, 0, 3, 7, 4, 5, 6, 2,
       1, 0, 4, 5, 1, 6, 7, 3, 2 };
   int cubeNormalIndices[] = { 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3,
       3, 3, 4, 4, 4, 4, 5, 5, 5, 5 };
   indexedCube.setCoordinates(0, cubeCoordinates);
   indexedCube.setNormals(0, cubeNormals);
   indexedCube.setCoordinateIndices(0, cubeCoordIndices);
   indexedCube.setNormalIndices(0, cubeNormalIndices);
   //Make the pyramid key shape. Although this needs
   //only five vertices to create the desired shape, we
   //need to use six vertices so that it has the same
   //number as the cube.
   IndexedQuadArray indexedPyramid = new IndexedQuadArray(8,
       IndexedQuadArray.COORDINATES | IndexedQuadArray.NORMALS, 24);
   Point3f[] pyramidCoordinates = { new Point3f(0.0f, 1.0f, 0.0f),
       new Point3f(0.0f, 1.0f, 0.0f), new Point3f(-1.0f, -1.0f, 1.0f),
       new Point3f(1.0f, -1.0f, 1.0f), new Point3f(0.0f, 1.0f, 0.0f),
       new Point3f(0.0f, 1.0f, 0.0f),
       new Point3f(-1.0f, -1.0f, -1.0f),
       new Point3f(1.0f, -1.0f, -1.0f) };
   Vector3f[] pyramidNormals = { new Vector3f(0.0f, 0.0f, 1.0f),
       new Vector3f(0.0f, 0.0f, -1.0f),
       new Vector3f(1.0f, 0.0f, 0.0f),
       new Vector3f(-1.0f, 0.0f, 0.0f),
       new Vector3f(0.0f, 1.0f, 0.0f), new Vector3f(0.0f, -1.0f, 0.0f) };
   int pyramidCoordIndices[] = { 0, 1, 2, 3, 7, 6, 5, 4, 0, 3, 7, 4, 5, 6,
       2, 1, 0, 4, 5, 1, 6, 7, 3, 2 };
   int pyramidNormalIndices[] = { 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3,
       3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5 };
   indexedPyramid.setCoordinates(0, pyramidCoordinates);
   indexedPyramid.setNormals(0, pyramidNormals);
   indexedPyramid.setCoordinateIndices(0, pyramidCoordIndices);
   indexedPyramid.setNormalIndices(0, pyramidNormalIndices);
   //Set the contents of the array to the two shapes
   GeometryArray[] theShapes = new GeometryArray[2];
   theShapes[0] = indexedCube;
   theShapes[1] = indexedPyramid;
   BranchGroup contentBranch = new BranchGroup();
   //Create a transform to rotate the shape slightly
   Transform3D rotateCube = new Transform3D();
   rotateCube.set(new AxisAngle4d(1.0, 1.0, 0.0, Math.PI / 4.0));
   TransformGroup rotationGroup = new TransformGroup(rotateCube);
   contentBranch.addChild(rotationGroup);
   addLights(contentBranch);
   //Call the function to build the morph
   rotationGroup.addChild(createMorph(theShapes, app));
   //Add the behaviour to the scene graph to activate it
   rotationGroup.addChild(myBehave);
   return contentBranch;
 }
 public void actionPerformed(ActionEvent e) {
   dispose();
   System.exit(0);
 }
 public SimpleMorph2() {
   VirtualUniverse myUniverse = new VirtualUniverse();
   Locale myLocale = new Locale(myUniverse);
   myLocale.addBranchGraph(buildViewBranch(myCanvas3D));
   myLocale.addBranchGraph(buildContentBranch());
   setTitle("SimpleMorph");
   setSize(400, 400);
   setLayout(new BorderLayout());
   Panel bottom = new Panel();
   bottom.add(exitButton);
   add(BorderLayout.CENTER, myCanvas3D);
   add(BorderLayout.SOUTH, bottom);
   exitButton.addActionListener(this);
   setVisible(true);
 }
 public static void main(String[] args) {
   SimpleMorph2 sw = new SimpleMorph2();
 }

} /**

* This class uses an alpha generator to change the weights of a Morph node. It
* morphs a shape between two key shapes repeatedly once a key has been pressed.
* Subsequent key presses toggle the running state of the animation.
* 
* @author I.J.Palmer
* @version 1.0
* @see SimpleMorph2
*/

class SimpleMorphBehaviour extends Behavior {

 /** Used to drive the animation */
 protected Alpha theAlpha;
 /** The weights of this are changed by the alpha values */
 protected Morph theMorph;
 /** Used to define the Morph weights */
 protected double theWeights[] = new double[2];
 /** Defines whether the animation is running or not */
 protected boolean running = false;
 /** The triggers for the animation to start */
 protected WakeupCriterion[] wakeConditions;
 /** The combined triggers */
 protected WakeupOr oredConditions;
 /** Set up the criteria to trigger after zero time or when a key is pressed */
 public void initialize() {
   wakeConditions = new WakeupCriterion[2];
   wakeConditions[0] = new WakeupOnAWTEvent(KeyEvent.KEY_PRESSED);
   wakeConditions[1] = new WakeupOnElapsedFrames(0);
   oredConditions = new WakeupOr(wakeConditions);
   wakeupOn(wakeConditions[0]);
 }
 /**
  * If the behaviour is not running and and a key has been pressed, start the
  * animation and vice-versa. Otherwise calculate a new set of weights.
  */
 public void processStimulus(Enumeration criteria) {
   WakeupCriterion theCriteria;
   theCriteria = (WakeupCriterion) criteria.nextElement();
   //If a key has been pressed, toggle the running state
   if (theCriteria instanceof WakeupOnAWTEvent) {
     running = !running;
   }
   if (running) {
     //Get the alpha value
     double alphaValue = theAlpha.value();
     //Set the two weights according to this value
     theWeights[0] = 1.0 - alphaValue;
     theWeights[1] = alphaValue;
     //Use the weights in the Morph
     theMorph.setWeights(theWeights);
   }
   //Set the trigger conditions again
   wakeupOn(oredConditions);
 }
 /**
  * Set up the data for the behaviour.
  * 
  * @param a
  *            Alpha that is used to drive the animation
  * @param m
  *            Morph that is affected by this behaviour
  */
 public SimpleMorphBehaviour(Alpha a, Morph m) {
   theAlpha = a;
   theMorph = m;
 }

}


      </source>