Java/3D/Light

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

AmbientLight, DirectionalLight, PointLight and SpotLight

   <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.
**************************************************************/

import java.applet.Applet; import java.awt.BorderLayout; import java.awt.Button; import java.awt.Checkbox; import java.awt.Color; import java.awt.Dimension; import java.awt.Frame; import java.awt.GraphicsConfigTemplate; import java.awt.GraphicsDevice; import java.awt.GraphicsEnvironment; import java.awt.Label; import java.awt.Panel; import java.awt.TextField; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.io.File; import java.net.URL; import javax.media.j3d.Alpha; import javax.media.j3d.AmbientLight; import javax.media.j3d.Appearance; import javax.media.j3d.AudioDevice; import javax.media.j3d.Background; import javax.media.j3d.BoundingSphere; import javax.media.j3d.Bounds; import javax.media.j3d.BranchGroup; import javax.media.j3d.Canvas3D; import javax.media.j3d.DirectionalLight; import javax.media.j3d.GraphicsConfigTemplate3D; import javax.media.j3d.Group; import javax.media.j3d.Light; import javax.media.j3d.Locale; import javax.media.j3d.Material; import javax.media.j3d.PhysicalBody; import javax.media.j3d.PhysicalEnvironment; import javax.media.j3d.PointLight; import javax.media.j3d.RotationInterpolator; import javax.media.j3d.Shape3D; import javax.media.j3d.SpotLight; 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.swing.JColorChooser; import javax.vecmath.AxisAngle4d; import javax.vecmath.Color3f; import javax.vecmath.Point3d; import javax.vecmath.Point3f; import javax.vecmath.Vector3d; import javax.vecmath.Vector3f; import com.sun.j3d.audioengines.javasound.JavaSoundMixer; import com.sun.j3d.utils.applet.MainFrame; import com.sun.j3d.utils.geometry.Cone; import com.sun.j3d.utils.geometry.GeometryInfo; import com.sun.j3d.utils.geometry.NormalGenerator; import com.sun.j3d.utils.geometry.Primitive; import com.sun.j3d.utils.geometry.Sphere; /**

* This example creates a simple scene composed of several spheres and a
* QuadArray. The scene is lit using 4 lights: AmbientLight, DirectionalLight,
* PointLight and SpotLight. Some UI is created for each light to allow the user
* to interactively modify the lights" parameters and view the resulting scene.
*/

public class LightTest extends Java3dApplet {

 private static final int m_kWidth = 400;
 private static final int m_kHeight = 400;
 public LightTest() {
   initJava3d();
 }
 // create a pop-up Frame to contain the
 // UI to control each light.
 protected void createLight(LightObject light, BranchGroup objRoot) {
   Frame frame = new Frame(light.getName());
   Panel aPanel = new Panel();
   frame.add(aPanel);
   light.addUiToPanel(aPanel);
   frame.pack();
   frame.setSize(new Dimension(400, 250));
   frame.validate();
   frame.setVisible(true);
   // add the geometry that depicts the light
   // to the scenegraph
   objRoot.addChild(light.createGeometry());
   // finally add the light itself to the scenegraph
   objRoot.addChild(light.getLight());
 }
 // overridden to use a black background
 // so we can see the lights better
 protected Background createBackground() {
   return null;
 }
 protected BranchGroup createSceneBranchGroup() {
   BranchGroup objRoot = super.createSceneBranchGroup();
   // create the 4 lights - the actual creation
   // and UI managment is delegated to an object
   // that "shadows" (no pun intended) the functionality
   // of the particular light
   createLight(new AmbientLightObject(), objRoot);
   createLight(new PointLightObject(), objRoot);
   createLight(new DirectionalLightObject(), objRoot);
   createLight(new SpotLightObject(), objRoot);
   // rotate some of the spheres in the scene
   TransformGroup objTrans = new TransformGroup();
   objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
   objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
   Transform3D yAxis = new Transform3D();
   Alpha rotationAlpha = new Alpha(-1, Alpha.INCREASING_ENABLE, 0, 0,
       4000, 0, 0, 0, 0, 0);
   RotationInterpolator rotator = new RotationInterpolator(rotationAlpha,
       objTrans, yAxis, 0.0f, (float) Math.PI * 2.0f);
   rotator.setSchedulingBounds(getApplicationBounds());
   objTrans.addChild(rotator);
   // create a large sphere in the center of the
   // scene and the floor as staionary objects
   objRoot.addChild(createSphere(0, 0, 0, 2));
   objRoot.addChild(createFloor());
   // create a smaller sphere at the corners of a cube
   final int nCubeSize = 3;
   objTrans.addChild(createSphere(nCubeSize, nCubeSize, nCubeSize, 1));
   objTrans.addChild(createSphere(nCubeSize, nCubeSize, -nCubeSize, 1));
   objTrans.addChild(createSphere(nCubeSize, -nCubeSize, nCubeSize, 1));
   objTrans.addChild(createSphere(nCubeSize, -nCubeSize, -nCubeSize, 1));
   objTrans.addChild(createSphere(-nCubeSize, nCubeSize, nCubeSize, 1));
   objTrans.addChild(createSphere(-nCubeSize, nCubeSize, -nCubeSize, 1));
   objTrans.addChild(createSphere(-nCubeSize, -nCubeSize, nCubeSize, 1));
   objTrans.addChild(createSphere(-nCubeSize, -nCubeSize, -nCubeSize, 1));
   // add some small spheres here and there to
   // make things interesting
   objRoot.addChild(createSphere(-6, -6, 2, 1));
   objRoot.addChild(createSphere(8, -5, 3, 1));
   objRoot.addChild(createSphere(6, 7, -1, 1));
   objRoot.addChild(createSphere(-5, 6, -3.5f, 0.5f));
   objRoot.addChild(objTrans);
   return objRoot;
 }
 // creates a QuadArray and uses per-vertex
 // colors to make a black and white pattern
 protected BranchGroup createFloor() {
   final int LAND_WIDTH = 12;
   final float LAND_HEIGHT = -4.0f;
   final int LAND_LENGTH = 12;
   final int nTileSize = 2;
   // calculate how many vertices we need to store all the "tiles"
   // that compose the QuadArray.
   final int nNumTiles = ((LAND_LENGTH / nTileSize) * 2)
       * ((LAND_WIDTH / nTileSize) * 2);
   final int nVertexCount = 4 * nNumTiles;
   Point3f[] coordArray = new Point3f[nVertexCount];
   Color3f[] colorArray = new Color3f[nVertexCount];
   // create an Appearance
   Appearance app = new Appearance();
   // create the parent BranchGroup
   BranchGroup bg = new BranchGroup();
   int nItem = 0;
   Color3f whiteColor = new Color3f(1, 1, 1);
   Color3f blackColor = new Color3f(0, 0, 0);
   // loop over all the tiles in the environment
   for (int x = -LAND_WIDTH; x <= LAND_WIDTH; x += nTileSize) {
     for (int z = -LAND_LENGTH; z <= LAND_LENGTH; z += nTileSize) {
       // if we are not on the last row or column create a "tile"
       // and add to the QuadArray. Use CCW winding and assign texture
       // coordinates.
       if (z < LAND_LENGTH && x < LAND_WIDTH) {
         coordArray[nItem] = new Point3f(x, LAND_HEIGHT, z);
         colorArray[nItem++] = blackColor;
         coordArray[nItem] = new Point3f(x, LAND_HEIGHT, z
             + nTileSize);
         colorArray[nItem++] = whiteColor;
         coordArray[nItem] = new Point3f(x + nTileSize, LAND_HEIGHT,
             z + nTileSize);
         colorArray[nItem++] = blackColor;
         coordArray[nItem] = new Point3f(x + nTileSize, LAND_HEIGHT,
             z);
         colorArray[nItem++] = whiteColor;
       }
     }
   }
   // create a GeometryInfo and generate Normal vectors
   // for the QuadArray that was populated.
   GeometryInfo gi = new GeometryInfo(GeometryInfo.QUAD_ARRAY);
   gi.setCoordinates(coordArray);
   gi.setColors(colorArray);
   NormalGenerator normalGenerator = new NormalGenerator();
   normalGenerator.generateNormals(gi);
   // wrap the GeometryArray in a Shape3D
   Shape3D shape = new Shape3D(gi.getGeometryArray(), app);
   // add the Shape3D to the parent BranchGroup
   bg.addChild(shape);
   return bg;
 }
 // helper method to create and position a sphere of a give size
 protected Group createSphere(float x, float y, float z, float radius) {
   TransformGroup tg = new TransformGroup();
   Transform3D t3d = new Transform3D();
   t3d.setTranslation(new Vector3d(x, y, z));
   tg.setTransform(t3d);
   // create an Appearance and Material
   Appearance app = new Appearance();
   Color3f objColor = new Color3f(1.0f, 0.7f, 0.8f);
   Color3f black = new Color3f(0.0f, 0.0f, 0.0f);
   app.setMaterial(new Material(objColor, black, objColor, black, 80.0f));
   tg.addChild(new Sphere(radius, Primitive.GENERATE_NORMALS, app));
   return tg;
 }
 public static void main(String[] args) {
   LightTest lightTest = new LightTest();
   lightTest.saveCommandLineArguments(args);
   new MainFrame(lightTest, m_kWidth, m_kHeight);
 }

} //abstract base class that implements the //basic "Light" class UI functionality abstract class LightObject implements ActionListener, ItemListener {

 protected Panel m_Panel = null;
 protected JColorChooser m_ColorChooser = null;
 TextField m_XTextField = null;
 TextField m_YTextField = null;
 TextField m_ZTextField = null;
 TextField m_RadiusTextField = null;
 Checkbox m_EnableCheck = null;
 protected Light m_Light = null;
 public LightObject() {
   m_Light = createLight();
   m_Light.setInfluencingBounds(new BoundingSphere(new Point3d(0, 0, 0),
       100));
   m_ColorChooser = new JColorChooser();
   int[] caps = getCapabilities();
   if (caps != null) {
     for (int n = 0; n < caps.length; n++)
       m_Light.setCapability(caps[n]);
   }
 }
 protected Light createLight() {
   return null;
 }
 public Light getLight() {
   return m_Light;
 }
 public String getName() {
   return "Light";
 }
 protected int[] getCapabilities() {
   int[] caps = new int[8];
   int nIndex = 0;
   caps[nIndex++] = Light.ALLOW_COLOR_READ;
   caps[nIndex++] = Light.ALLOW_COLOR_WRITE;
   caps[nIndex++] = Light.ALLOW_INFLUENCING_BOUNDS_READ;
   caps[nIndex++] = Light.ALLOW_INFLUENCING_BOUNDS_WRITE;
   caps[nIndex++] = Light.ALLOW_SCOPE_READ;
   caps[nIndex++] = Light.ALLOW_SCOPE_WRITE;
   caps[nIndex++] = Light.ALLOW_STATE_READ;
   caps[nIndex++] = Light.ALLOW_STATE_WRITE;
   return caps;
 }
 public Group createGeometry() {
   Group g = new Group();
   m_Light.setUserData(this);
   return g;
 }
 public void addUiToPanel(Panel panel) {
   Button colorButton = new Button("Color");
   colorButton.addActionListener(this);
   panel.add(colorButton);
   m_EnableCheck = new Checkbox("Enable", true);
   m_EnableCheck.addItemListener(this);
   panel.add(m_EnableCheck);
   panel.add(new Label("Bounds:"));
   panel.add(new Label("X:"));
   m_XTextField = new TextField(3);
   panel.add(m_XTextField);
   panel.add(new Label("Y:"));
   m_YTextField = new TextField(3);
   panel.add(m_YTextField);
   panel.add(new Label("Z:"));
   m_ZTextField = new TextField(3);
   panel.add(m_ZTextField);
   panel.add(new Label("Radius:"));
   m_RadiusTextField = new TextField(4);
   panel.add(m_RadiusTextField);
   Button updateButton = new Button("Update");
   updateButton.addActionListener(this);
   panel.add(updateButton);
   synchLightToUi();
 }
 public void actionPerformed(ActionEvent event) {
   if (event.getActionCommand().equals("Color") != false)
     OnColor();
   else if (event.getActionCommand().equals("Update") != false)
     synchLightToUi();
 }
 public void itemStateChanged(ItemEvent event) {
   m_Light.setEnable(event.getStateChange() == ItemEvent.SELECTED);
 }
 protected void OnColor() {
   Color rgb = m_ColorChooser.showDialog(m_Panel, "Set Light Color", null);
   if (rgb != null) {
     m_Light
         .setColor(new Color3f((float) rgb.getRed() / 255f,
             (float) rgb.getGreen() / 255f, (float) rgb
                 .getBlue() / 255f));
   }
 }
 public void synchLightToUi() {
   m_Light.setEnable(m_EnableCheck.getState());
   // set some defaults if things go wrong...
   double x = 0;
   double y = 0;
   double z = 0;
   double radius = 100;
   try {
     x = Double.valueOf(m_XTextField.getText()).doubleValue();
     y = Double.valueOf(m_YTextField.getText()).doubleValue();
     z = Double.valueOf(m_ZTextField.getText()).doubleValue();
     radius = Double.valueOf(m_RadiusTextField.getText()).doubleValue();
   } catch (java.lang.NumberFormatException e) {
     // invalid numeric input - just ignore.
   }
   m_Light.setInfluencingBounds(new BoundingSphere(new Point3d(x, y, z),
       radius));
 }
 // this method is a placeholder for some
 // code that would synchronize the UI with the
 // state of a light. This would allow the user
 // to move a light around using a MouseBehavior
 // and update the UI to display the new position.
 // An exercise for the reader... ;-)
 public void synchUiToLight() {
 }
 protected int[] createCompoundArray(int[] a1, int[] a2) {
   int[] aRet = null;
   int nTotalLen = 0;
   int nLen1 = 0;
   int nLen2 = 0;
   if (a1 != null) {
     nTotalLen += a1.length;
     nLen1 = a1.length;
   }
   if (a2 != null) {
     nTotalLen += a2.length;
     nLen2 = a2.length;
   }
   aRet = new int[nTotalLen];
   if (a1 != null)
     System.arraycopy(a1, 0, aRet, 0, nLen1);
   if (a2 != null)
     System.arraycopy(a2, 0, aRet, nLen1, a2.length);
   return aRet;
 }

} class PointLightObject extends LightObject {

 protected TextField m_XPositionTextField = null;
 protected TextField m_YPositionTextField = null;
 protected TextField m_ZPositionTextField = null;
 protected TextField m_ConstantAttenuationTextField = null;
 protected TextField m_LinearAttenuationTextField = null;
 protected TextField m_QuadraticAttenuationTextField = null;
 protected TransformGroup m_TransformGroup = null;
 protected Sphere m_Sphere = null;
 public PointLightObject() {
 }
 protected Light createLight() {
   return (Light) new PointLight();
 }
 public String getName() {
   return "PointLight";
 }
 protected int[] getCapabilities() {
   int[] superCaps = super.getCapabilities();
   int[] caps = { PointLight.ALLOW_ATTENUATION_READ,
       PointLight.ALLOW_ATTENUATION_WRITE,
       PointLight.ALLOW_POSITION_READ, PointLight.ALLOW_POSITION_WRITE };
   return createCompoundArray(superCaps, caps);
 }
 public Group createGeometry() {
   Point3f pos = new Point3f();
   ((PointLight) m_Light).getPosition(pos);
   m_TransformGroup = new TransformGroup();
   m_TransformGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
   m_TransformGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
   Transform3D t3d = new Transform3D();
   t3d.setTranslation(new Vector3f(pos.x, pos.y, pos.z));
   m_TransformGroup.setTransform(t3d);
   m_Sphere = new Sphere(0.2f, Primitive.ENABLE_APPEARANCE_MODIFY
       | Primitive.GENERATE_NORMALS, 16);
   m_TransformGroup.addChild(m_Sphere);
   m_TransformGroup.addChild(super.createGeometry());
   return (Group) m_TransformGroup;
 }
 public void addUiToPanel(Panel panel) {
   m_XPositionTextField = new TextField(3);
   m_YPositionTextField = new TextField(3);
   m_ZPositionTextField = new TextField(3);
   m_ConstantAttenuationTextField = new TextField(3);
   m_LinearAttenuationTextField = new TextField(3);
   m_QuadraticAttenuationTextField = new TextField(3);
   panel.add(new Label("Position:"));
   panel.add(new Label("X:"));
   panel.add(m_XPositionTextField);
   panel.add(new Label("Y:"));
   panel.add(m_YPositionTextField);
   panel.add(new Label("Z:"));
   panel.add(m_ZPositionTextField);
   panel.add(new Label("Attenuation:"));
   panel.add(new Label("Constant:"));
   panel.add(m_ConstantAttenuationTextField);
   panel.add(new Label("Linear:"));
   panel.add(m_LinearAttenuationTextField);
   panel.add(new Label("Quadratic:"));
   panel.add(m_QuadraticAttenuationTextField);
   super.addUiToPanel(panel);
 }
 public void synchLightToUi() {
   super.synchLightToUi();
   // set some defaults if things go wrong...
   double x = 0;
   double y = 0;
   double z = 0;
   double constant = 0.01;
   double linear = 0;
   double quadratic = 0;
   try {
     x = Double.valueOf(m_XPositionTextField.getText()).doubleValue();
     y = Double.valueOf(m_YPositionTextField.getText()).doubleValue();
     z = Double.valueOf(m_ZPositionTextField.getText()).doubleValue();
     constant = Double.valueOf(m_ConstantAttenuationTextField.getText())
         .doubleValue();
     linear = Double.valueOf(m_LinearAttenuationTextField.getText())
         .doubleValue();
     quadratic = Double.valueOf(
         m_QuadraticAttenuationTextField.getText()).doubleValue();
   } catch (java.lang.NumberFormatException e) {
     // invalid numeric input - just ignore.
   }
   ((PointLight) m_Light).setPosition((float) x, (float) y, (float) z);
   ((PointLight) m_Light).setAttenuation((float) constant, (float) linear,
       (float) quadratic);
   if (m_TransformGroup != null) {
     Transform3D t3d = new Transform3D();
     m_TransformGroup.getTransform(t3d);
     t3d.setTranslation(new Vector3d(x, y, z));
     m_TransformGroup.setTransform(t3d);
   }
   if (m_Sphere != null) {
     Appearance app = new Appearance();
     Color3f objColor = new Color3f();
     m_Light.getColor(objColor);
     Color3f black = new Color3f(0.0f, 0.0f, 0.0f);
     app.setMaterial(new Material(objColor, black, objColor, black,
         80.0f));
     m_Sphere.getShape(Sphere.BODY).setAppearance(app);
   }
 }
 public void synchUiToLight() {
   super.synchUiToLight();
 }

} class SpotLightObject extends PointLightObject {

 protected TextField m_ConcentrationTextField = null;
 protected TextField m_SpreadAngleTextField = null;
 protected TextField m_XDirectionTextField = null;
 protected TextField m_YDirectionTextField = null;
 protected TextField m_ZDirectionTextField = null;
 protected TransformGroup m_DirectionTransformGroup = null;
 protected Cone m_Cone = null;
 public SpotLightObject() {
 }
 protected Light createLight() {
   return (Light) new SpotLight();
 }
 public String getName() {
   return "SpotLight";
 }
 protected int[] getCapabilities() {
   int[] superCaps = super.getCapabilities();
   int[] caps = { SpotLight.ALLOW_CONCENTRATION_READ,
       SpotLight.ALLOW_CONCENTRATION_WRITE,
       SpotLight.ALLOW_DIRECTION_READ,
       SpotLight.ALLOW_DIRECTION_WRITE,
       SpotLight.ALLOW_SPREAD_ANGLE_READ,
       SpotLight.ALLOW_SPREAD_ANGLE_WRITE };
   return createCompoundArray(superCaps, caps);
 }
 public Group createGeometry() {
   m_DirectionTransformGroup = new TransformGroup();
   m_DirectionTransformGroup
       .setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
   m_DirectionTransformGroup
       .setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
   // create appearance and material for the Cone
   Appearance app = new Appearance();
   // create the Primitive and add to the parent BranchGroup
   m_Cone = new Cone(0.5f, 2, Primitive.ENABLE_APPEARANCE_MODIFY
       | Primitive.GENERATE_NORMALS, app);
   m_DirectionTransformGroup.addChild(m_Cone);
   Group superGroup = super.createGeometry();
   superGroup.addChild(m_DirectionTransformGroup);
   return superGroup;
 }
 public void addUiToPanel(Panel panel) {
   m_XDirectionTextField = new TextField(3);
   m_YDirectionTextField = new TextField(3);
   m_ZDirectionTextField = new TextField(3);
   m_ConcentrationTextField = new TextField(3);
   m_SpreadAngleTextField = new TextField(3);
   panel.add(new Label("Direction:"));
   panel.add(new Label("X:"));
   panel.add(m_XDirectionTextField);
   panel.add(new Label("Y:"));
   panel.add(m_YDirectionTextField);
   panel.add(new Label("Z:"));
   panel.add(m_ZDirectionTextField);
   panel.add(new Label("Concentration:"));
   panel.add(m_ConcentrationTextField);
   panel.add(new Label("Spread Angle:"));
   panel.add(m_SpreadAngleTextField);
   super.addUiToPanel(panel);
 }
 public void synchLightToUi() {
   super.synchLightToUi();
   // set some defaults if things go wrong...
   double x = 0;
   double y = 0;
   double z = 0;
   double conc = 1;
   double spread = 2;
   try {
     x = Double.valueOf(m_XDirectionTextField.getText()).doubleValue();
     y = Double.valueOf(m_YDirectionTextField.getText()).doubleValue();
     z = Double.valueOf(m_ZDirectionTextField.getText()).doubleValue();
     conc = Double.valueOf(m_ConcentrationTextField.getText())
         .doubleValue();
     spread = Double.valueOf(m_SpreadAngleTextField.getText())
         .doubleValue();
   } catch (java.lang.NumberFormatException e) {
     // invalid numeric input - just ignore.
   }
   ((SpotLight) m_Light).setDirection((float) x, (float) y, (float) z);
   ((SpotLight) m_Light).setConcentration((float) conc);
   ((SpotLight) m_Light).setSpreadAngle((float) spread);
   if (m_DirectionTransformGroup != null) {
     Vector3d coneVector = new Vector3d(0, -1, 0);
     Vector3d lightVector = new Vector3d(x, y, z);
     coneVector.normalize();
     lightVector.normalize();
     Vector3d axisVector = new Vector3d();
     axisVector.cross(coneVector, lightVector);
     double angle = java.lang.Math.acos(coneVector.dot(lightVector));
     AxisAngle4d rotAxis = new AxisAngle4d(axisVector.x, axisVector.y,
         axisVector.z, angle);
     Transform3D t3d = new Transform3D();
     t3d.setRotation(rotAxis);
     m_DirectionTransformGroup.setTransform(t3d);
   }
   if (m_Cone != null) {
     Appearance app = new Appearance();
     Color3f objColor = new Color3f();
     m_Light.getColor(objColor);
     Color3f black = new Color3f(0.0f, 0.0f, 0.0f);
     app.setMaterial(new Material(objColor, black, objColor, black,
         80.0f));
     m_Cone.getShape(Cone.CAP).setAppearance(app);
   }
 }
 public void synchUiToLight() {
   super.synchUiToLight();
 }

} class AmbientLightObject extends LightObject {

 public AmbientLightObject() {
 }
 protected Light createLight() {
   return (Light) new AmbientLight();
 }
 public String getName() {
   return "AmbientLight";
 }
 protected int[] getCapabilities() {
   return super.getCapabilities();
 }
 public Group createGeometry() {
   return super.createGeometry();
 }
 public void addUiToPanel(Panel panel) {
   panel.add(new Label("AmbientLight"));
   super.addUiToPanel(panel);
 }
 public void synchLightToUi() {
   super.synchLightToUi();
 }
 public void synchUiToLight() {
   super.synchUiToLight();
 }

} class DirectionalLightObject extends LightObject {

 protected TextField m_XDirectionTextField = null;
 protected TextField m_YDirectionTextField = null;
 protected TextField m_ZDirectionTextField = null;
 protected TransformGroup m_TransformGroup = null;
 protected Cone m_Cone = null;
 public DirectionalLightObject() {
 }
 protected Light createLight() {
   return (Light) new DirectionalLight();
 }
 public String getName() {
   return "DirectionalLight";
 }
 protected int[] getCapabilities() {
   int[] superCaps = super.getCapabilities();
   int[] caps = { DirectionalLight.ALLOW_DIRECTION_READ,
       DirectionalLight.ALLOW_DIRECTION_WRITE, };
   return createCompoundArray(superCaps, caps);
 }
 public Group createGeometry() {
   m_TransformGroup = new TransformGroup();
   m_TransformGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
   m_TransformGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
   // create appearance and material for the Cone
   Appearance app = new Appearance();
   // create the Primitive and add to the parent BranchGroup
   m_Cone = new Cone(1, 10, Primitive.ENABLE_APPEARANCE_MODIFY
       | Primitive.GENERATE_NORMALS, app);
   m_TransformGroup.addChild(m_Cone);
   Group superGroup = super.createGeometry();
   superGroup.addChild(m_TransformGroup);
   return superGroup;
 }
 public void addUiToPanel(Panel panel) {
   m_XDirectionTextField = new TextField(3);
   m_YDirectionTextField = new TextField(3);
   m_ZDirectionTextField = new TextField(3);
   panel.add(new Label("Direction:"));
   panel.add(new Label("X:"));
   panel.add(m_XDirectionTextField);
   panel.add(new Label("Y:"));
   panel.add(m_YDirectionTextField);
   panel.add(new Label("Z:"));
   panel.add(m_ZDirectionTextField);
   super.addUiToPanel(panel);
 }
 public void synchLightToUi() {
   super.synchLightToUi();
   // set some defaults if things go wrong...
   double x = 0;
   double y = 0;
   double z = 0;
   try {
     x = Double.valueOf(m_XDirectionTextField.getText()).doubleValue();
     y = Double.valueOf(m_YDirectionTextField.getText()).doubleValue();
     z = Double.valueOf(m_ZDirectionTextField.getText()).doubleValue();
   } catch (java.lang.NumberFormatException e) {
     // invalid numeric input - just ignore.
   }
   ((DirectionalLight) m_Light).setDirection((float) x, (float) y,
       (float) z);
   if (m_TransformGroup != null) {
     Vector3d coneVector = new Vector3d(0, 1, 0);
     Vector3d lightVector = new Vector3d(x, y, z);
     coneVector.normalize();
     lightVector.normalize();
     Vector3d axisVector = new Vector3d();
     axisVector.cross(coneVector, lightVector);
     double angle = java.lang.Math.acos(coneVector.dot(lightVector));
     AxisAngle4d rotAxis = new AxisAngle4d(axisVector.x, axisVector.y,
         axisVector.z, angle);
     Transform3D t3d = new Transform3D();
     t3d.setRotation(rotAxis);
     m_TransformGroup.setTransform(t3d);
   }
   if (m_Cone != null) {
     Appearance app = new Appearance();
     Color3f objColor = new Color3f();
     m_Light.getColor(objColor);
     Color3f black = new Color3f(0.0f, 0.0f, 0.0f);
     app.setMaterial(new Material(objColor, black, objColor, black,
         80.0f));
     m_Cone.setAppearance(app);
   }
 }
 public void synchUiToLight() {
   super.synchUiToLight();
 }

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

* 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;
 }

}


      </source>
   
  
 
  



Creates an ambient light and a one directional light

   <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.event.ActionEvent; import java.awt.event.ActionListener; import javax.media.j3d.AmbientLight; import javax.media.j3d.Appearance; import javax.media.j3d.BoundingSphere; import javax.media.j3d.BranchGroup; import javax.media.j3d.Canvas3D; import javax.media.j3d.DirectionalLight; import javax.media.j3d.Locale; import javax.media.j3d.Material; 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.vecmath.AxisAngle4d; import javax.vecmath.Color3f; import javax.vecmath.Point3d; import javax.vecmath.Vector3f; import com.sun.j3d.utils.geometry.Sphere; /**

* This builds a red sphere using the Sphere utility class and adds lights so
* that you can see it shape. It creates a material for the sphere, creates an
* ambient light and a one directional light.
* 
* @author I.J.Palmer
* @version 1.0
*/

public class SimpleDirLight extends Frame implements ActionListener {

 protected Canvas3D myCanvas3D = new Canvas3D(null);
 protected Button myButton = new Button("Exit");
 /**
  * This function builds the view branch of the scene graph. It creates a
  * branch group and then creates the necessary view elements to give a
  * useful view of our content.
  * 
  * @param c
  *            Canvas3D that will display the view
  * @return BranchGroup that is the root of the view elements
  */
 protected BranchGroup buildViewBranch(Canvas3D c) {
   BranchGroup viewBranch = new BranchGroup();
   Transform3D viewXfm = new Transform3D();
   viewXfm.set(new Vector3f(0.0f, 0.0f, 10.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;
 }
 /**
  * This creates some lights and adds them to the BranchGroup.
  * 
  * @param b
  *            BranchGroup that the lights are added to.
  */
 protected void addLights(BranchGroup b) {
   // Create a bounds for the lights
   BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0),
       100.0);
   //Set up the ambient light
   Color3f ambientColour = new Color3f(0.2f, 0.2f, 0.2f);
   AmbientLight ambientLight = new AmbientLight(ambientColour);
   ambientLight.setInfluencingBounds(bounds);
   //Set up the directional light
   Color3f lightColour = new Color3f(1.0f, 1.0f, 1.0f);
   Vector3f lightDir = new Vector3f(-1.0f, -1.0f, -1.0f);
   DirectionalLight light = new DirectionalLight(lightColour, lightDir);
   light.setInfluencingBounds(bounds);
   //Add the lights to the BranchGroup
   b.addChild(ambientLight);
   b.addChild(light);
 }
 /**
  * This build the content branch of our scene graph. It creates a transform
  * group so that the shape is slightly tilted to reveal its 3D shape.
  * 
  * @param shape
  *            Node that represents the geometry for the content
  * @return BranchGroup that is the root of the content branch
  */
 protected BranchGroup buildContentBranch() {
   BranchGroup contentBranch = new BranchGroup();
   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);
   //Create a new appearance
   Appearance app = new Appearance();
   //Create the colours for the material
   Color3f ambientColour = new Color3f(1.0f, 0.0f, 0.0f);
   Color3f diffuseColour = new Color3f(1.0f, 0.0f, 0.0f);
   Color3f specularColour = new Color3f(1.0f, 1.0f, 1.0f);
   Color3f emissiveColour = new Color3f(0.0f, 0.0f, 0.0f);
   //Define the shininess
   float shininess = 20.0f;
   //Set the material of the appearance
   app.setMaterial(new Material(ambientColour, emissiveColour,
       diffuseColour, specularColour, shininess));
   //Create and add a new sphere using the appearance
   rotationGroup.addChild(new Sphere(2.0f, Sphere.GENERATE_NORMALS, 120,
       app));
   //Use the addLights function to add the lights to the branch
   addLights(contentBranch);
   //Return the root of the content branch
   return contentBranch;
 }
 /**
  * Handles the exit button action to quit the program.
  */
 public void actionPerformed(ActionEvent e) {
   dispose();
   System.exit(0);
 }
 /**
  * This creates a default universe and locale, creates a window and uses the
  * functions defined in this class to build the view and content branches of
  * the scene graph.
  */
 public SimpleDirLight() {
   VirtualUniverse myUniverse = new VirtualUniverse();
   Locale myLocale = new Locale(myUniverse);
   myLocale.addBranchGraph(buildViewBranch(myCanvas3D));
   myLocale.addBranchGraph(buildContentBranch());
   setTitle("SimpleDirLight");
   setSize(400, 400);
   setLayout(new BorderLayout());
   add("Center", myCanvas3D);
   add("South", myButton);
   myButton.addActionListener(this);
   setVisible(true);
 }
 public static void main(String[] args) {
   SimpleDirLight sdl = new SimpleDirLight();
 }

}

      </source>
   
  
 
  



ExAmbientLight - illustrate use of ambient lights

   <source lang="java">

// //CLASS //ExAmbientLight - illustrate use of ambient lights // //LESSON //Add an AmbientLight node to illuminate a scene. // //SEE ALSO //ExDirectionalLight //ExPointLight //ExSpotLight // //AUTHOR //David R. Nadeau / San Diego Supercomputer Center // import java.applet.Applet; import java.awt.AWTEvent; import java.awt.BorderLayout; import java.awt.CheckboxMenuItem; import java.awt.ruponent; import java.awt.Cursor; import java.awt.Frame; import java.awt.Menu; import java.awt.MenuBar; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.InputEvent; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.event.MouseEvent; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.io.File; import java.util.Enumeration; import java.util.EventListener; 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.Group; import javax.media.j3d.Light; import javax.media.j3d.Material; import javax.media.j3d.Transform3D; import javax.media.j3d.TransformGroup; import javax.media.j3d.WakeupCriterion; import javax.media.j3d.WakeupOnAWTEvent; import javax.media.j3d.WakeupOnElapsedFrames; import javax.media.j3d.WakeupOr; import javax.vecmath.Color3f; import javax.vecmath.Matrix4d; import javax.vecmath.Point3d; import javax.vecmath.Point3f; import javax.vecmath.Vector3d; import javax.vecmath.Vector3f; import com.sun.j3d.utils.geometry.Primitive; import com.sun.j3d.utils.geometry.Sphere; import com.sun.j3d.utils.universe.PlatformGeometry; import com.sun.j3d.utils.universe.SimpleUniverse; import com.sun.j3d.utils.universe.Viewer; import com.sun.j3d.utils.universe.ViewingPlatform; public class ExAmbientLight extends Java3DFrame {

 //--------------------------------------------------------------
 //  SCENE CONTENT
 //--------------------------------------------------------------
 //
 //  Nodes (updated via menu)
 //
 private AmbientLight light = null;
 //
 //  Build scene
 //
 public Group buildScene() {
   // Get the current color
   Color3f color = (Color3f) colors[currentColor].value;
   // Turn off the example headlight
   setHeadlightEnable(false);
   // Build the scene group
   Group scene = new Group();
   // BEGIN EXAMPLE TOPIC
   // Create influencing bounds
   BoundingSphere worldBounds = new BoundingSphere(new Point3d(0.0, 0.0,
       0.0), // Center
       1000.0); // Extent
   // Set the light color and its influencing bounds
   light = new AmbientLight();
   light.setEnable(lightOnOff);
   light.setColor(color);
   light.setCapability(AmbientLight.ALLOW_STATE_WRITE);
   light.setCapability(AmbientLight.ALLOW_COLOR_WRITE);
   light.setInfluencingBounds(worldBounds);
   scene.addChild(light);
   // END EXAMPLE TOPIC
   // Build foreground geometry
   scene.addChild(new SphereGroup());
   return scene;
 }
 //--------------------------------------------------------------
 //  USER INTERFACE
 //--------------------------------------------------------------
 //
 //  Main
 //
 public static void main(String[] args) {
   ExAmbientLight ex = new ExAmbientLight();
   ex.initialize(args);
   ex.buildUniverse();
   ex.showFrame();
 }
 //  On/off choices
 private boolean lightOnOff = true;
 private CheckboxMenuItem lightOnOffMenu;
 //  Color menu choices
 private NameValue[] colors = { new NameValue("White", White),
     new NameValue("Gray", Gray), new NameValue("Black", Black),
     new NameValue("Red", Red), new NameValue("Yellow", Yellow),
     new NameValue("Green", Green), new NameValue("Cyan", Cyan),
     new NameValue("Blue", Blue), new NameValue("Magenta", Magenta), };
 private int currentColor = 0;
 private CheckboxMenu colorMenu = null;
 //
 //  Initialize the GUI (application and applet)
 //
 public void initialize(String[] args) {
   // Initialize the window, menubar, etc.
   super.initialize(args);
   exampleFrame.setTitle("Java 3D Ambient Light Example");
   //
   //  Add a menubar menu to change node parameters
   //    Light on/off
   //    Color -->
   //
   Menu m = new Menu("AmbientLight");
   lightOnOffMenu = new CheckboxMenuItem("Light on/off", lightOnOff);
   lightOnOffMenu.addItemListener(this);
   m.add(lightOnOffMenu);
   colorMenu = new CheckboxMenu("Color", colors, currentColor, this);
   m.add(colorMenu);
   exampleMenuBar.add(m);
 }
 //
 //  Handle checkboxes and menu choices
 //
 public void checkboxChanged(CheckboxMenu menu, int check) {
   if (menu == colorMenu) {
     // Change the light color
     currentColor = check;
     Color3f color = (Color3f) colors[check].value;
     light.setColor(color);
     return;
   }
   // Handle all other checkboxes
   super.checkboxChanged(menu, check);
 }
 public void itemStateChanged(ItemEvent event) {
   Object src = event.getSource();
   if (src == lightOnOffMenu) {
     // Turn the light on or off
     lightOnOff = lightOnOffMenu.getState();
     light.setEnable(lightOnOff);
     return;
   }
   // Handle all other checkboxes
   super.itemStateChanged(event);
 }

} // //CLASS //SphereGroup - create a group of spheres on the XY plane // //DESCRIPTION //An XY grid of spheres is created. The number of spheres in X and Y, //the spacing in X and Y, the sphere radius, and the appearance can //all be set. // //This grid of spheres is used by several of the examples as a generic //bit of foreground geometry. // //SEE ALSO //Ex*Light //ExBackground* // //AUTHOR //David R. Nadeau / San Diego Supercomputer Center // class SphereGroup extends Group {

 //  Constructors
 public SphereGroup() {
   //    radius x,y spacing x,y count appearance
   this(0.25f, 0.75f, 0.75f, 5, 5, null);
 }
 public SphereGroup(Appearance app) {
   //    radius x,y spacing x,y count appearance
   this(0.25f, 0.75f, 0.75f, 5, 5, app);
 }
 public SphereGroup(float radius, float xSpacing, float ySpacing,
     int xCount, int yCount) {
   this(radius, xSpacing, ySpacing, xCount, yCount, null);
 }
 public SphereGroup(float radius, float xSpacing, float ySpacing,
     int xCount, int yCount, Appearance app) {
   if (app == null) {
     app = new Appearance();
     Material material = new Material();
     material.setDiffuseColor(new Color3f(0.8f, 0.8f, 0.8f));
     material.setSpecularColor(new Color3f(0.0f, 0.0f, 0.0f));
     material.setShininess(0.0f);
     app.setMaterial(material);
   }
   double xStart = -xSpacing * (double) (xCount - 1) / 2.0;
   double yStart = -ySpacing * (double) (yCount - 1) / 2.0;
   Sphere sphere = null;
   TransformGroup trans = null;
   Transform3D t3d = new Transform3D();
   Vector3d vec = new Vector3d();
   double x, y = yStart, z = 0.0;
   for (int i = 0; i < yCount; i++) {
     x = xStart;
     for (int j = 0; j < xCount; j++) {
       vec.set(x, y, z);
       t3d.setTranslation(vec);
       trans = new TransformGroup(t3d);
       addChild(trans);
       sphere = new Sphere(radius, // sphere radius
           Primitive.GENERATE_NORMALS, // generate normals
           16, // 16 divisions radially
           app); // it"s appearance
       trans.addChild(sphere);
       x += xSpacing;
     }
     y += ySpacing;
   }
 }

} /**

* The Example class is a base class extended by example applications. The class
* provides basic features to create a top-level frame, add a menubar and
* Canvas3D, build the universe, set up "examine" and "walk" style navigation
* behaviors, and provide hooks so that subclasses can add 3D content to the
* example"s universe.
*

* Using this Example class simplifies the construction of example applications, * enabling the author to focus upon 3D content and not the busywork of creating * windows, menus, and universes. * * @version 1.0, 98/04/16 * @author David R. Nadeau, San Diego Supercomputer Center */ class Java3DFrame extends Applet implements WindowListener, ActionListener, ItemListener, CheckboxMenuListener { // Navigation types public final static int Walk = 0; public final static int Examine = 1; // Should the scene be compiled? private boolean shouldCompile = true; // GUI objects for our subclasses protected Java3DFrame example = null; protected Frame exampleFrame = null; protected MenuBar exampleMenuBar = null; protected Canvas3D exampleCanvas = null; protected TransformGroup exampleViewTransform = null; protected TransformGroup exampleSceneTransform = null; protected boolean debug = false; // Private GUI objects and state private boolean headlightOnOff = true; private int navigationType = Examine; private CheckboxMenuItem headlightMenuItem = null; private CheckboxMenuItem walkMenuItem = null; private CheckboxMenuItem examineMenuItem = null; private DirectionalLight headlight = null; private ExamineViewerBehavior examineBehavior = null; private WalkViewerBehavior walkBehavior = null; //-------------------------------------------------------------- // ADMINISTRATION //-------------------------------------------------------------- /** * The main program entry point when invoked as an application. Each example * application that extends this class must define their own main. * * @param args * a String array of command-line arguments */ public static void main(String[] args) { Java3DFrame ex = new Java3DFrame(); ex.initialize(args); ex.buildUniverse(); ex.showFrame(); } /** * Constructs a new Example object. * * @return a new Example that draws no 3D content */ public Java3DFrame() { // Do nothing } /** * Initializes the application when invoked as an applet. */ public void init() { // Collect properties into String array String[] args = new String[2]; // NOTE: to be done still... this.initialize(args); this.buildUniverse(); this.showFrame(); // NOTE: add something to the browser page? } /** * Initializes the Example by parsing command-line arguments, building an * AWT Frame, constructing a menubar, and creating the 3D canvas. * * @param args * a String array of command-line arguments */ protected void initialize(String[] args) { example = this; // Parse incoming arguments parseArgs(args); // Build the frame if (debug) System.err.println("Building GUI..."); exampleFrame = new Frame(); exampleFrame.setSize(640, 480); exampleFrame.setTitle("Java 3D Example"); exampleFrame.setLayout(new BorderLayout()); // Set up a close behavior exampleFrame.addWindowListener(this); // Create a canvas exampleCanvas = new Canvas3D(null); exampleCanvas.setSize(630, 460); exampleFrame.add("Center", exampleCanvas); // Build the menubar exampleMenuBar = this.buildMenuBar(); exampleFrame.setMenuBar(exampleMenuBar); // Pack exampleFrame.pack(); exampleFrame.validate(); // exampleFrame.setVisible( true ); } /** * Parses incoming command-line arguments. Applications that subclass this * class may override this method to support their own command-line * arguments. * * @param args * a String array of command-line arguments */ protected void parseArgs(String[] args) { for (int i = 0; i < args.length; i++) { if (args[i].equals("-d")) debug = true; } } //-------------------------------------------------------------- // SCENE CONTENT //-------------------------------------------------------------- /** * Builds the 3D universe by constructing a virtual universe (via * SimpleUniverse), a view platform (via SimpleUniverse), and a view (via * SimpleUniverse). A headlight is added and a set of behaviors initialized * to handle navigation types. */ protected void buildUniverse() { // // Create a SimpleUniverse object, which builds: // // - a Locale using the given hi-res coordinate origin // // - a ViewingPlatform which in turn builds: // - a MultiTransformGroup with which to move the // the ViewPlatform about // // - a ViewPlatform to hold the view // // - a BranchGroup to hold avatar geometry (if any) // // - a BranchGroup to hold view platform // geometry (if any) // // - a Viewer which in turn builds: // - a PhysicalBody which characterizes the user"s // viewing preferences and abilities // // - a PhysicalEnvironment which characterizes the // user"s rendering hardware and software // // - a JavaSoundMixer which initializes sound // support within the 3D environment // // - a View which renders the scene into a Canvas3D // // All of these actions could be done explicitly, but // using the SimpleUniverse utilities simplifies the code. // if (debug) System.err.println("Building scene graph..."); SimpleUniverse universe = new SimpleUniverse(null, // Hi-res coordinate // for the origin - // use default 1, // Number of transforms in MultiTransformGroup exampleCanvas, // Canvas3D into which to draw null); // URL for user configuration file - use defaults // // Get the viewer and create an audio device so that // sound will be enabled in this content. // Viewer viewer = universe.getViewer(); viewer.createAudioDevice(); // // Get the viewing platform created by SimpleUniverse. // From that platform, get the inner-most TransformGroup // in the MultiTransformGroup. That inner-most group // contains the ViewPlatform. It is this inner-most // TransformGroup we need in order to: // // - add a "headlight" that always aims forward from // the viewer // // - change the viewing direction in a "walk" style // // The inner-most TransformGroup"s transform will be // changed by the walk behavior (when enabled). // ViewingPlatform viewingPlatform = universe.getViewingPlatform(); exampleViewTransform = viewingPlatform.getViewPlatformTransform(); // // Create a "headlight" as a forward-facing directional light. // Set the light"s bounds to huge. Since we want the light // on the viewer"s "head", we need the light within the // TransformGroup containing the ViewPlatform. The // ViewingPlatform class creates a handy hook to do this // called "platform geometry". The PlatformGeometry class is // subclassed off of BranchGroup, and is intended to contain // a description of the 3D platform itself... PLUS a headlight! // So, to add the headlight, create a new PlatformGeometry group, // add the light to it, then add that platform geometry to the // ViewingPlatform. // BoundingSphere allBounds = new BoundingSphere( new Point3d(0.0, 0.0, 0.0), 100000.0); PlatformGeometry pg = new PlatformGeometry(); headlight = new DirectionalLight(); headlight.setColor(White); headlight.setDirection(new Vector3f(0.0f, 0.0f, -1.0f)); headlight.setInfluencingBounds(allBounds); headlight.setCapability(Light.ALLOW_STATE_WRITE); pg.addChild(headlight); viewingPlatform.setPlatformGeometry(pg); // // Create the 3D content BranchGroup, containing: // // - a TransformGroup who"s transform the examine behavior // will change (when enabled). // // - 3D geometry to view // // Build the scene root BranchGroup sceneRoot = new BranchGroup(); // Build a transform that we can modify exampleSceneTransform = new TransformGroup(); exampleSceneTransform .setCapability(TransformGroup.ALLOW_TRANSFORM_READ); exampleSceneTransform .setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE); exampleSceneTransform.setCapability(Group.ALLOW_CHILDREN_EXTEND); // // Build the scene, add it to the transform, and add // the transform to the scene root // if (debug) System.err.println(" scene..."); Group scene = this.buildScene(); exampleSceneTransform.addChild(scene); sceneRoot.addChild(exampleSceneTransform); // // Create a pair of behaviors to implement two navigation // types: // // - "examine": a style where mouse drags rotate about // the scene"s origin as if it is an object under // examination. This is similar to the "Examine" // navigation type used by VRML browsers. // // - "walk": a style where mouse drags rotate about // the viewer"s center as if the viewer is turning // about to look at a scene they are in. This is // similar to the "Walk" navigation type used by // VRML browsers. // // Aim the examine behavior at the scene"s TransformGroup // and add the behavior to the scene root. // // Aim the walk behavior at the viewing platform"s // TransformGroup and add the behavior to the scene root. // // Enable one (and only one!) of the two behaviors // depending upon the current navigation type. // examineBehavior = new ExamineViewerBehavior(exampleSceneTransform, // Transform // gorup // to // modify exampleFrame); // Parent frame for cusor changes examineBehavior.setSchedulingBounds(allBounds); sceneRoot.addChild(examineBehavior); walkBehavior = new WalkViewerBehavior(exampleViewTransform, // Transform // group to // modify exampleFrame); // Parent frame for cusor changes walkBehavior.setSchedulingBounds(allBounds); sceneRoot.addChild(walkBehavior); if (navigationType == Walk) { examineBehavior.setEnable(false); walkBehavior.setEnable(true); } else { examineBehavior.setEnable(true); walkBehavior.setEnable(false); } // // Compile the scene branch group and add it to the // SimpleUniverse. // if (shouldCompile) sceneRoot.rupile(); universe.addBranchGraph(sceneRoot); reset(); } /** * Builds the scene. Example application subclasses should replace this * method with their own method to build 3D content. * * @return a Group containing 3D content to display */ public Group buildScene() { // Build the scene group containing nothing Group scene = new Group(); return scene; } //-------------------------------------------------------------- // SET/GET METHODS //-------------------------------------------------------------- /** * Sets the headlight on/off state. The headlight faces forward in the * direction the viewer is facing. Example applications that add their own * lights will typically turn the headlight off. A standard menu item * enables the headlight to be turned on and off via user control. * * @param onOff * a boolean turning the light on (true) or off (false) */ public void setHeadlightEnable(boolean onOff) { headlightOnOff = onOff; if (headlight != null) headlight.setEnable(headlightOnOff); if (headlightMenuItem != null) headlightMenuItem.setState(headlightOnOff); } /** * Gets the headlight on/off state. * * @return a boolean indicating if the headlight is on or off */ public boolean getHeadlightEnable() { return headlightOnOff; } /** * Sets the navigation type to be either Examine or Walk. The Examine * navigation type sets up behaviors that use mouse drags to rotate and * translate scene content as if it is an object held at arm"s length and * under examination. The Walk navigation type uses mouse drags to rotate * and translate the viewer as if they are walking through the content. The * Examine type is the default. * * @param nav * either Walk or Examine */ public void setNavigationType(int nav) { if (nav == Walk) { navigationType = Walk; if (walkMenuItem != null) walkMenuItem.setState(true); if (examineMenuItem != null) examineMenuItem.setState(false); if (walkBehavior != null) walkBehavior.setEnable(true); if (examineBehavior != null) examineBehavior.setEnable(false); } else { navigationType = Examine; if (walkMenuItem != null) walkMenuItem.setState(false); if (examineMenuItem != null) examineMenuItem.setState(true); if (walkBehavior != null) walkBehavior.setEnable(false); if (examineBehavior != null) examineBehavior.setEnable(true); } } /** * Gets the current navigation type, returning either Walk or Examine. * * @return either Walk or Examine */ public int getNavigationType() { return navigationType; } /** * Sets whether the scene graph should be compiled or not. Normally this is * always a good idea. For some example applications that use this Example * framework, it is useful to disable compilation - particularly when nodes * and node components will need to be made un-live in order to make * changes. Once compiled, such components can be made un-live, but they are * still unchangable unless appropriate capabilities have been set. * * @param onOff * a boolean turning compilation on (true) or off (false) */ public void setCompilable(boolean onOff) { shouldCompile = onOff; } /** * Gets whether the scene graph will be compiled or not. * * @return a boolean indicating if scene graph compilation is on or off */ public boolean getCompilable() { return shouldCompile; } //These methods will be replaced // Set the view position and direction public void setViewpoint(Point3f position, Vector3f direction) { Transform3D t = new Transform3D(); t.set(new Vector3f(position)); exampleViewTransform.setTransform(t); // how to set direction? } // Reset transforms public void reset() { Transform3D trans = new Transform3D(); exampleSceneTransform.setTransform(trans); trans.set(new Vector3f(0.0f, 0.0f, 10.0f)); exampleViewTransform.setTransform(trans); setNavigationType(navigationType); } // // Gets the URL (with file: prepended) for the current directory. // This is a terrible hack needed in the Alpha release of Java3D // in order to build a full path URL for loading sounds with // MediaContainer. When MediaContainer is fully implemented, // it should handle relative path names, but not yet. // public String getCurrentDirectory() { // Create a bogus file so that we can query it"s path File dummy = new File("dummy.tmp"); String dummyPath = dummy.getAbsolutePath(); // strip "/dummy.tmp" from end of dummyPath and put into "path" if (dummyPath.endsWith(File.separator + "dummy.tmp")) { int index = dummyPath.lastIndexOf(File.separator + "dummy.tmp"); if (index >= 0) { int pathLength = index + 5; // pre-pend "file:" char[] charPath = new char[pathLength]; dummyPath.getChars(0, index, charPath, 5); String path = new String(charPath, 0, pathLength); path = "file:" + path.substring(5, pathLength); return path + File.separator; } } return dummyPath + File.separator; } //-------------------------------------------------------------- // USER INTERFACE //-------------------------------------------------------------- /** * Builds the example AWT Frame menubar. Standard menus and their options * are added. Applications that subclass this class should build their * menubar additions within their initialize method. * * @return a MenuBar for the AWT Frame */ private MenuBar buildMenuBar() { // Build the menubar MenuBar menuBar = new MenuBar(); // File menu Menu m = new Menu("File"); m.addActionListener(this); m.add("Exit"); menuBar.add(m); // View menu m = new Menu("View"); m.addActionListener(this); m.add("Reset view"); m.addSeparator(); walkMenuItem = new CheckboxMenuItem("Walk"); walkMenuItem.addItemListener(this); m.add(walkMenuItem); examineMenuItem = new CheckboxMenuItem("Examine"); examineMenuItem.addItemListener(this); m.add(examineMenuItem); if (navigationType == Walk) { walkMenuItem.setState(true); examineMenuItem.setState(false); } else { walkMenuItem.setState(false); examineMenuItem.setState(true); } m.addSeparator(); headlightMenuItem = new CheckboxMenuItem("Headlight on/off"); headlightMenuItem.addItemListener(this); headlightMenuItem.setState(headlightOnOff); m.add(headlightMenuItem); menuBar.add(m); return menuBar; } /** * Shows the application"s frame, making it and its menubar, 3D canvas, and * 3D content visible. */ public void showFrame() { exampleFrame.show(); } /** * Quits the application. */ public void quit() { System.exit(0); } /** * Handles menu selections. * * @param event * an ActionEvent indicating what menu action requires handling */ public void actionPerformed(ActionEvent event) { String arg = event.getActionCommand(); if (arg.equals("Reset view")) reset(); else if (arg.equals("Exit")) quit(); } /** * Handles checkbox items on a CheckboxMenu. The Example class has none of * its own, but subclasses may have some. * * @param menu * which CheckboxMenu needs action * @param check * which CheckboxMenu item has changed */ public void checkboxChanged(CheckboxMenu menu, int check) { // None for us } /** * Handles on/off checkbox items on a standard menu. * * @param event * an ItemEvent indicating what requires handling */ public void itemStateChanged(ItemEvent event) { Object src = event.getSource(); boolean state; if (src == headlightMenuItem) { state = headlightMenuItem.getState(); headlight.setEnable(state); } else if (src == walkMenuItem) setNavigationType(Walk); else if (src == examineMenuItem) setNavigationType(Examine); } /** * Handles a window closing event notifying the application that the user * has chosen to close the application without selecting the "Exit" menu * item. * * @param event * a WindowEvent indicating the window is closing */ public void windowClosing(WindowEvent event) { quit(); } public void windowClosed(WindowEvent event) { } public void windowOpened(WindowEvent event) { } public void windowIconified(WindowEvent event) { } public void windowDeiconified(WindowEvent event) { } public void windowActivated(WindowEvent event) { } public void windowDeactivated(WindowEvent event) { } // Well known colors, positions, and directions public final static Color3f White = new Color3f(1.0f, 1.0f, 1.0f); public final static Color3f Gray = new Color3f(0.7f, 0.7f, 0.7f); public final static Color3f DarkGray = new Color3f(0.2f, 0.2f, 0.2f); public final static Color3f Black = new Color3f(0.0f, 0.0f, 0.0f); public final static Color3f Red = new Color3f(1.0f, 0.0f, 0.0f); public final static Color3f DarkRed = new Color3f(0.3f, 0.0f, 0.0f); public final static Color3f Yellow = new Color3f(1.0f, 1.0f, 0.0f); public final static Color3f DarkYellow = new Color3f(0.3f, 0.3f, 0.0f); public final static Color3f Green = new Color3f(0.0f, 1.0f, 0.0f); public final static Color3f DarkGreen = new Color3f(0.0f, 0.3f, 0.0f); public final static Color3f Cyan = new Color3f(0.0f, 1.0f, 1.0f); public final static Color3f Blue = new Color3f(0.0f, 0.0f, 1.0f); public final static Color3f DarkBlue = new Color3f(0.0f, 0.0f, 0.3f); public final static Color3f Magenta = new Color3f(1.0f, 0.0f, 1.0f); public final static Vector3f PosX = new Vector3f(1.0f, 0.0f, 0.0f); public final static Vector3f NegX = new Vector3f(-1.0f, 0.0f, 0.0f); public final static Vector3f PosY = new Vector3f(0.0f, 1.0f, 0.0f); public final static Vector3f NegY = new Vector3f(0.0f, -1.0f, 0.0f); public final static Vector3f PosZ = new Vector3f(0.0f, 0.0f, 1.0f); public final static Vector3f NegZ = new Vector3f(0.0f, 0.0f, -1.0f); public final static Point3f Origin = new Point3f(0.0f, 0.0f, 0.0f); public final static Point3f PlusX = new Point3f(0.75f, 0.0f, 0.0f); public final static Point3f MinusX = new Point3f(-0.75f, 0.0f, 0.0f); public final static Point3f PlusY = new Point3f(0.0f, 0.75f, 0.0f); public final static Point3f MinusY = new Point3f(0.0f, -0.75f, 0.0f); public final static Point3f PlusZ = new Point3f(0.0f, 0.0f, 0.75f); public final static Point3f MinusZ = new Point3f(0.0f, 0.0f, -0.75f); } // //INTERFACE //CheckboxMenuListener - listen for checkbox change events // //DESCRIPTION //The checkboxChanged method is called by users of this class //to notify the listener when a checkbox choice has changed on //a CheckboxMenu class menu. // interface CheckboxMenuListener extends EventListener { public void checkboxChanged(CheckboxMenu menu, int check); } /** * ExamineViewerBehavior * * @version 1.0, 98/04/16 */ /** * Wakeup on mouse button presses, releases, and mouse movements and generate * transforms in an "examination style" that enables the user to rotate, * translation, and zoom an object as if it is held at arm"s length. Such an * examination style is similar to the "Examine" navigation type used by VRML * browsers. * * The behavior maps mouse drags to different transforms depending upon the * mosue button held down: * * Button 1 (left) Horizontal movement --> Y-axis rotation Vertical movement --> * X-axis rotation * * Button 2 (middle) Horizontal movement --> nothing Vertical movement --> * Z-axis translation * * Button 3 (right) Horizontal movement --> X-axis translation Vertical movement * --> Y-axis translation * * To support systems with 2 or 1 mouse buttons, the following alternate * mappings are supported while dragging with any mouse button held down and * zero or more keyboard modifiers held down: * * No modifiers = Button 1 ALT = Button 2 Meta = Button 3 Control = Button 3 * * The behavior automatically modifies a TransformGroup provided to the * constructor. The TransformGroup"s transform can be set at any time by the * application or other behaviors to cause the examine rotation and translation * to be reset. */ // This class is inspired by the MouseBehavior, MouseRotate, // MouseTranslate, and MouseZoom utility behaviors provided with // Java 3D. This class differs from those utilities in that it: // // (a) encapsulates all three behaviors into one in order to // enforce a specific "Examine" symantic // // (b) supports set/get of the rotation and translation factors // that control the speed of movement. // // (c) supports the "Control" modifier as an alternative to the // "Meta" modifier not present on PC, Mac, and most non-Sun // keyboards. This makes button3 behavior usable on PCs, // Macs, and other systems with fewer than 3 mouse buttons. class ExamineViewerBehavior extends ViewerBehavior { // Previous cursor location protected int previousX = 0; protected int previousY = 0; // Saved standard cursor protected Cursor savedCursor = null; /** * Construct an examine behavior that listens to mouse movement and button * presses to generate rotation and translation transforms written into a * transform group given later with the setTransformGroup( ) method. */ public ExamineViewerBehavior() { super(); } /** * Construct an examine behavior that listens to mouse movement and button * presses to generate rotation and translation transforms written into a * transform group given later with the setTransformGroup( ) method. * * @param parent * The AWT Component that contains the area generating mouse * events. */ public ExamineViewerBehavior(Component parent) { super(parent); } /** * Construct an examine behavior that listens to mouse movement and button * presses to generate rotation and translation transforms written into the * given transform group. * * @param transformGroup * The transform group to be modified by the behavior. */ public ExamineViewerBehavior(TransformGroup transformGroup) { super(); subjectTransformGroup = transformGroup; } /** * Construct an examine behavior that listens to mouse movement and button * presses to generate rotation and translation transforms written into the * given transform group. * * @param transformGroup * The transform group to be modified by the behavior. * @param parent * The AWT Component that contains the area generating mouse * events. */ public ExamineViewerBehavior(TransformGroup transformGroup, Component parent) { super(parent); subjectTransformGroup = transformGroup; } /** * Respond to a button1 event (press, release, or drag). * * @param mouseEvent * A MouseEvent to respond to. */ public void onButton1(MouseEvent mev) { if (subjectTransformGroup == null) return; int x = mev.getX(); int y = mev.getY(); if (mev.getID() == MouseEvent.MOUSE_PRESSED) { // Mouse button pressed: record position previousX = x; previousY = y; // Change to a "move" cursor if (parentComponent != null) { savedCursor = parentComponent.getCursor(); parentComponent.setCursor(Cursor .getPredefinedCursor(Cursor.HAND_CURSOR)); } return; } if (mev.getID() == MouseEvent.MOUSE_RELEASED) { // Mouse button released: do nothing // Switch the cursor back if (parentComponent != null) parentComponent.setCursor(savedCursor); return; } // // Mouse moved while button down: create a rotation // // Compute the delta in X and Y from the previous // position. Use the delta to compute rotation // angles with the mapping: // // positive X mouse delta --> positive Y-axis rotation // positive Y mouse delta --> positive X-axis rotation // // where positive X mouse movement is to the right, and // positive Y mouse movement is **down** the screen. // int deltaX = x - previousX; int deltaY = y - previousY; if (deltaX > UNUSUAL_XDELTA || deltaX < -UNUSUAL_XDELTA || deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) { // Deltas are too huge to be believable. Probably a glitch. // Don"t record the new XY location, or do anything. return; } double xRotationAngle = deltaY * XRotationFactor; double yRotationAngle = deltaX * YRotationFactor; // // Build transforms // transform1.rotX(xRotationAngle); transform2.rotY(yRotationAngle); // Get and save the current transform matrix subjectTransformGroup.getTransform(currentTransform); currentTransform.get(matrix); translate.set(matrix.m03, matrix.m13, matrix.m23); // Translate to the origin, rotate, then translate back currentTransform.setTranslation(origin); currentTransform.mul(transform1, currentTransform); currentTransform.mul(transform2, currentTransform); currentTransform.setTranslation(translate); // Update the transform group subjectTransformGroup.setTransform(currentTransform); previousX = x; previousY = y; } /** * Respond to a button2 event (press, release, or drag). * * @param mouseEvent * A MouseEvent to respond to. */ public void onButton2(MouseEvent mev) { if (subjectTransformGroup == null) return; int x = mev.getX(); int y = mev.getY(); if (mev.getID() == MouseEvent.MOUSE_PRESSED) { // Mouse button pressed: record position previousX = x; previousY = y; // Change to a "move" cursor if (parentComponent != null) { savedCursor = parentComponent.getCursor(); parentComponent.setCursor(Cursor .getPredefinedCursor(Cursor.MOVE_CURSOR)); } return; } if (mev.getID() == MouseEvent.MOUSE_RELEASED) { // Mouse button released: do nothing // Switch the cursor back if (parentComponent != null) parentComponent.setCursor(savedCursor); return; } // // Mouse moved while button down: create a translation // // Compute the delta in Y from the previous // position. Use the delta to compute translation // distances with the mapping: // // positive Y mouse delta --> positive Y-axis translation // // where positive X mouse movement is to the right, and // positive Y mouse movement is **down** the screen. // int deltaY = y - previousY; if (deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) { // Deltas are too huge to be believable. Probably a glitch. // Don"t record the new XY location, or do anything. return; } double zTranslationDistance = deltaY * ZTranslationFactor; // // Build transforms // translate.set(0.0, 0.0, zTranslationDistance); transform1.set(translate); // Get and save the current transform subjectTransformGroup.getTransform(currentTransform); // Translate as needed currentTransform.mul(transform1, currentTransform); // Update the transform group subjectTransformGroup.setTransform(currentTransform); previousX = x; previousY = y; } /** * Respond to a button3 event (press, release, or drag). * * @param mouseEvent * A MouseEvent to respond to. */ public void onButton3(MouseEvent mev) { if (subjectTransformGroup == null) return; int x = mev.getX(); int y = mev.getY(); if (mev.getID() == MouseEvent.MOUSE_PRESSED) { // Mouse button pressed: record position previousX = x; previousY = y; // Change to a "move" cursor if (parentComponent != null) { savedCursor = parentComponent.getCursor(); parentComponent.setCursor(Cursor .getPredefinedCursor(Cursor.MOVE_CURSOR)); } return; } if (mev.getID() == MouseEvent.MOUSE_RELEASED) { // Mouse button released: do nothing // Switch the cursor back if (parentComponent != null) parentComponent.setCursor(savedCursor); return; } // // Mouse moved while button down: create a translation // // Compute the delta in X and Y from the previous // position. Use the delta to compute translation // distances with the mapping: // // positive X mouse delta --> positive X-axis translation // positive Y mouse delta --> negative Y-axis translation // // where positive X mouse movement is to the right, and // positive Y mouse movement is **down** the screen. // int deltaX = x - previousX; int deltaY = y - previousY; if (deltaX > UNUSUAL_XDELTA || deltaX < -UNUSUAL_XDELTA || deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) { // Deltas are too huge to be believable. Probably a glitch. // Don"t record the new XY location, or do anything. return; } double xTranslationDistance = deltaX * XTranslationFactor; double yTranslationDistance = -deltaY * YTranslationFactor; // // Build transforms // translate.set(xTranslationDistance, yTranslationDistance, 0.0); transform1.set(translate); // Get and save the current transform subjectTransformGroup.getTransform(currentTransform); // Translate as needed currentTransform.mul(transform1, currentTransform); // Update the transform group subjectTransformGroup.setTransform(currentTransform); previousX = x; previousY = y; } /** * Respond to an elapsed frames event (assuming subclass has set up a wakeup * criterion for it). * * @param time * A WakeupOnElapsedFrames criterion to respond to. */ public void onElapsedFrames(WakeupOnElapsedFrames timeEvent) { // Can"t happen } } /* * * Copyright (c) 1998 David R. Nadeau * */ /** * WalkViewerBehavior is a utility class that creates a "walking style" * navigation symantic. * * The behavior wakes up on mouse button presses, releases, and mouse movements * and generates transforms in a "walk style" that enables the user to walk * through a scene, translating and turning about as if they are within the * scene. Such a walk style is similar to the "Walk" navigation type used by * VRML browsers. * <P> * The behavior maps mouse drags to different transforms depending upon the * mouse button held down: *

*
Button 1 (left) *
Horizontal movement --> Y-axis rotation *
Vertical movement --> Z-axis translation * *
Button 2 (middle) *
Horizontal movement --> Y-axis rotation *
Vertical movement --> X-axis rotation * *
Button 3 (right) *
Horizontal movement --> X-axis translation *
Vertical movement --> Y-axis translation *
* 
* To support systems with 2 or 1 mouse buttons, the following alternate
* mappings are supported while dragging with any mouse button held down and
* zero or more keyboard modifiers held down:
*
    *
  • No modifiers = Button 1 *
  • ALT = Button 2 *
  • Meta = Button 3 *
  • Control = Button 3 *
* The behavior automatically modifies a TransformGroup provided to the
* constructor. The TransformGroup"s transform can be set at any time by the
* application or other behaviors to cause the walk rotation and translation to
* be reset.
* <P>
* While a mouse button is down, the behavior automatically changes the cursor
* in a given parent AWT Component. If no parent Component is given, no cursor
* changes are attempted.
* 
* @version 1.0, 98/04/16
* @author David R. Nadeau, San Diego Supercomputer Center
*/

class WalkViewerBehavior extends ViewerBehavior {

 // This class is inspired by the MouseBehavior, MouseRotate,
 // MouseTranslate, and MouseZoom utility behaviors provided with
 // Java 3D. This class differs from those utilities in that it:
 //
 //    (a) encapsulates all three behaviors into one in order to
 //        enforce a specific "Walk" symantic
 //
 //    (b) supports set/get of the rotation and translation factors
 //        that control the speed of movement.
 //
 //    (c) supports the "Control" modifier as an alternative to the
 //        "Meta" modifier not present on PC, Mac, and most non-Sun
 //        keyboards. This makes button3 behavior usable on PCs,
 //        Macs, and other systems with fewer than 3 mouse buttons.
 // Previous and initial cursor locations
 protected int previousX = 0;
 protected int previousY = 0;
 protected int initialX = 0;
 protected int initialY = 0;
 // Deadzone size (delta from initial XY for which no
 // translate or rotate action is taken
 protected static final int DELTAX_DEADZONE = 10;
 protected static final int DELTAY_DEADZONE = 10;
 // Keep a set of wakeup criterion for animation-generated
 // event types.
 protected WakeupCriterion[] mouseAndAnimationEvents = null;
 protected WakeupOr mouseAndAnimationCriterion = null;
 protected WakeupOr savedMouseCriterion = null;
 // Saved standard cursor
 protected Cursor savedCursor = null;
 /**
  * Default Rotation and translation scaling factors for animated movements
  * (Button 1 press).
  */
 public static final double DEFAULT_YROTATION_ANIMATION_FACTOR = 0.0002;
 public static final double DEFAULT_ZTRANSLATION_ANIMATION_FACTOR = 0.01;
 protected double YRotationAnimationFactor = DEFAULT_YROTATION_ANIMATION_FACTOR;
 protected double ZTranslationAnimationFactor = DEFAULT_ZTRANSLATION_ANIMATION_FACTOR;
 /**
  * Constructs a new walk behavior that converts mouse actions into rotations
  * and translations. Rotations and translations are written into a
  * TransformGroup that must be set using the setTransformGroup method. The
  * cursor will be changed during mouse actions if the parent frame is set
  * using the setParentComponent method.
  * 
  * @return a new WalkViewerBehavior that needs its TransformGroup and parent
  *         Component set
  */
 public WalkViewerBehavior() {
   super();
 }
 /**
  * Constructs a new walk behavior that converts mouse actions into rotations
  * and translations. Rotations and translations are written into a
  * TransformGroup that must be set using the setTransformGroup method. The
  * cursor will be changed within the given AWT parent Component during mouse
  * drags.
  * 
  * @param parent
  *            a parent AWT Component within which the cursor will change
  *            during mouse drags
  * 
  * @return a new WalkViewerBehavior that needs its TransformGroup and parent
  *         Component set
  */
 public WalkViewerBehavior(Component parent) {
   super(parent);
 }
 /**
  * Constructs a new walk behavior that converts mouse actions into rotations
  * and translations. Rotations and translations are written into the given
  * TransformGroup. The cursor will be changed during mouse actions if the
  * parent frame is set using the setParentComponent method.
  * 
  * @param transformGroup
  *            a TransformGroup whos transform is read and written by the
  *            behavior
  * 
  * @return a new WalkViewerBehavior that needs its TransformGroup and parent
  *         Component set
  */
 public WalkViewerBehavior(TransformGroup transformGroup) {
   super();
   subjectTransformGroup = transformGroup;
 }
 /**
  * Constructs a new walk behavior that converts mouse actions into rotations
  * and translations. Rotations and translations are written into the given
  * TransformGroup. The cursor will be changed within the given AWT parent
  * Component during mouse drags.
  * 
  * @param transformGroup
  *            a TransformGroup whos transform is read and written by the
  *            behavior
  * 
  * @param parent
  *            a parent AWT Component within which the cursor will change
  *            during mouse drags
  * 
  * @return a new WalkViewerBehavior that needs its TransformGroup and parent
  *         Component set
  */
 public WalkViewerBehavior(TransformGroup transformGroup, Component parent) {
   super(parent);
   subjectTransformGroup = transformGroup;
 }
 /**
  * Initializes the behavior.
  */
 public void initialize() {
   super.initialize();
   savedMouseCriterion = mouseCriterion; // from parent class
   mouseAndAnimationEvents = new WakeupCriterion[4];
   mouseAndAnimationEvents[0] = new WakeupOnAWTEvent(
       MouseEvent.MOUSE_DRAGGED);
   mouseAndAnimationEvents[1] = new WakeupOnAWTEvent(
       MouseEvent.MOUSE_PRESSED);
   mouseAndAnimationEvents[2] = new WakeupOnAWTEvent(
       MouseEvent.MOUSE_RELEASED);
   mouseAndAnimationEvents[3] = new WakeupOnElapsedFrames(0);
   mouseAndAnimationCriterion = new WakeupOr(mouseAndAnimationEvents);
   // Don"t use the above criterion until a button 1 down event
 }
 /**
  * Sets the Y rotation animation scaling factor for Y-axis rotations. This
  * scaling factor is used to control the speed of Y rotation when button 1
  * is pressed and dragged.
  * 
  * @param factor
  *            the double Y rotation scaling factor
  */
 public void setYRotationAnimationFactor(double factor) {
   YRotationAnimationFactor = factor;
 }
 /**
  * Gets the current Y animation rotation scaling factor for Y-axis
  * rotations.
  * 
  * @return the double Y rotation scaling factor
  */
 public double getYRotationAnimationFactor() {
   return YRotationAnimationFactor;
 }
 /**
  * Sets the Z animation translation scaling factor for Z-axis translations.
  * This scaling factor is used to control the speed of Z translation when
  * button 1 is pressed and dragged.
  * 
  * @param factor
  *            the double Z translation scaling factor
  */
 public void setZTranslationAnimationFactor(double factor) {
   ZTranslationAnimationFactor = factor;
 }
 /**
  * Gets the current Z animation translation scaling factor for Z-axis
  * translations.
  * 
  * @return the double Z translation scaling factor
  */
 public double getZTranslationAnimationFactor() {
   return ZTranslationAnimationFactor;
 }
 /**
  * Responds to an elapsed frames event. Such an event is generated on every
  * frame while button 1 is held down. On each call, this method computes new
  * Y-axis rotation and Z-axis translation values and writes them to the
  * behavior"s TransformGroup. The translation and rotation amounts are
  * computed based upon the distance between the current cursor location and
  * the cursor location when button 1 was pressed. As this distance
  * increases, the translation or rotation amount increases.
  * 
  * @param time
  *            the WakeupOnElapsedFrames criterion to respond to
  */
 public void onElapsedFrames(WakeupOnElapsedFrames timeEvent) {
   //
   // Time elapsed while button down: create a rotation and
   // a translation.
   //
   // Compute the delta in X and Y from the initial position to
   // the previous position. Multiply the delta times a scaling
   // factor to compute an offset to add to the current translation
   // and rotation. Use the mapping:
   //
   //   positive X mouse delta --> negative Y-axis rotation
   //   positive Y mouse delta --> positive Z-axis translation
   //
   // where positive X mouse movement is to the right, and
   // positive Y mouse movement is **down** the screen.
   //
   if (buttonPressed != BUTTON1)
     return;
   int deltaX = previousX - initialX;
   int deltaY = previousY - initialY;
   double yRotationAngle = -deltaX * YRotationAnimationFactor;
   double zTranslationDistance = deltaY * ZTranslationAnimationFactor;
   //
   // Build transforms
   //
   transform1.rotY(yRotationAngle);
   translate.set(0.0, 0.0, zTranslationDistance);
   // Get and save the current transform matrix
   subjectTransformGroup.getTransform(currentTransform);
   currentTransform.get(matrix);
   // Translate to the origin, rotate, then translate back
   currentTransform.setTranslation(origin);
   currentTransform.mul(transform1, currentTransform);
   // Translate back from the origin by the original translation
   // distance, plus the new walk translation... but force walk
   // to travel on a plane by ignoring the Y component of a
   // transformed translation vector.
   currentTransform.transform(translate);
   translate.x += matrix.m03; // add in existing X translation
   translate.y = matrix.m13; // use Y translation
   translate.z += matrix.m23; // add in existing Z translation
   currentTransform.setTranslation(translate);
   // Update the transform group
   subjectTransformGroup.setTransform(currentTransform);
 }
 /**
  * Responds to a button1 event (press, release, or drag). On a press, the
  * method adds a wakeup criterion to the behavior"s set, callling for the
  * behavior to be awoken on each frame. On a button prelease, this criterion
  * is removed from the set.
  * 
  * @param mouseEvent
  *            the MouseEvent to respond to
  */
 public void onButton1(MouseEvent mev) {
   if (subjectTransformGroup == null)
     return;
   int x = mev.getX();
   int y = mev.getY();
   if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
     // Mouse button pressed: record position and change
     // the wakeup criterion to include elapsed time wakeups
     // so we can animate.
     previousX = x;
     previousY = y;
     initialX = x;
     initialY = y;
     // Swap criterion... parent class will not reschedule us
     mouseCriterion = mouseAndAnimationCriterion;
     // Change to a "move" cursor
     if (parentComponent != null) {
       savedCursor = parentComponent.getCursor();
       parentComponent.setCursor(Cursor
           .getPredefinedCursor(Cursor.HAND_CURSOR));
     }
     return;
   }
   if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
     // Mouse button released: restore original wakeup
     // criterion which only includes mouse activity, not
     // elapsed time
     mouseCriterion = savedMouseCriterion;
     // Switch the cursor back
     if (parentComponent != null)
       parentComponent.setCursor(savedCursor);
     return;
   }
   previousX = x;
   previousY = y;
 }
 /**
  * Responds to a button2 event (press, release, or drag). On a press, the
  * method records the initial cursor location. On a drag, the difference
  * between the current and previous cursor location provides a delta that
  * controls the amount by which to rotate in X and Y.
  * 
  * @param mouseEvent
  *            the MouseEvent to respond to
  */
 public void onButton2(MouseEvent mev) {
   if (subjectTransformGroup == null)
     return;
   int x = mev.getX();
   int y = mev.getY();
   if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
     // Mouse button pressed: record position
     previousX = x;
     previousY = y;
     initialX = x;
     initialY = y;
     // Change to a "rotate" cursor
     if (parentComponent != null) {
       savedCursor = parentComponent.getCursor();
       parentComponent.setCursor(Cursor
           .getPredefinedCursor(Cursor.MOVE_CURSOR));
     }
     return;
   }
   if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
     // Mouse button released: do nothing
     // Switch the cursor back
     if (parentComponent != null)
       parentComponent.setCursor(savedCursor);
     return;
   }
   //
   // Mouse moved while button down: create a rotation
   //
   // Compute the delta in X and Y from the previous
   // position. Use the delta to compute rotation
   // angles with the mapping:
   //
   //   positive X mouse delta --> negative Y-axis rotation
   //   positive Y mouse delta --> negative X-axis rotation
   //
   // where positive X mouse movement is to the right, and
   // positive Y mouse movement is **down** the screen.
   //
   int deltaX = x - previousX;
   int deltaY = 0;
   if (Math.abs(y - initialY) > DELTAY_DEADZONE) {
     // Cursor has moved far enough vertically to consider
     // it intentional, so get it"s delta.
     deltaY = y - previousY;
   }
   if (deltaX > UNUSUAL_XDELTA || deltaX < -UNUSUAL_XDELTA
       || deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {
     // Deltas are too huge to be believable. Probably a glitch.
     // Don"t record the new XY location, or do anything.
     return;
   }
   double xRotationAngle = -deltaY * XRotationFactor;
   double yRotationAngle = -deltaX * YRotationFactor;
   //
   // Build transforms
   //
   transform1.rotX(xRotationAngle);
   transform2.rotY(yRotationAngle);
   // Get and save the current transform matrix
   subjectTransformGroup.getTransform(currentTransform);
   currentTransform.get(matrix);
   translate.set(matrix.m03, matrix.m13, matrix.m23);
   // Translate to the origin, rotate, then translate back
   currentTransform.setTranslation(origin);
   currentTransform.mul(transform2, currentTransform);
   currentTransform.mul(transform1);
   currentTransform.setTranslation(translate);
   // Update the transform group
   subjectTransformGroup.setTransform(currentTransform);
   previousX = x;
   previousY = y;
 }
 /**
  * Responds to a button3 event (press, release, or drag). On a drag, the
  * difference between the current and previous cursor location provides a
  * delta that controls the amount by which to translate in X and Y.
  * 
  * @param mouseEvent
  *            the MouseEvent to respond to
  */
 public void onButton3(MouseEvent mev) {
   if (subjectTransformGroup == null)
     return;
   int x = mev.getX();
   int y = mev.getY();
   if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
     // Mouse button pressed: record position
     previousX = x;
     previousY = y;
     // Change to a "move" cursor
     if (parentComponent != null) {
       savedCursor = parentComponent.getCursor();
       parentComponent.setCursor(Cursor
           .getPredefinedCursor(Cursor.MOVE_CURSOR));
     }
     return;
   }
   if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
     // Mouse button released: do nothing
     // Switch the cursor back
     if (parentComponent != null)
       parentComponent.setCursor(savedCursor);
     return;
   }
   //
   // Mouse moved while button down: create a translation
   //
   // Compute the delta in X and Y from the previous
   // position. Use the delta to compute translation
   // distances with the mapping:
   //
   //   positive X mouse delta --> positive X-axis translation
   //   positive Y mouse delta --> negative Y-axis translation
   //
   // where positive X mouse movement is to the right, and
   // positive Y mouse movement is **down** the screen.
   //
   int deltaX = x - previousX;
   int deltaY = y - previousY;
   if (deltaX > UNUSUAL_XDELTA || deltaX < -UNUSUAL_XDELTA
       || deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {
     // Deltas are too huge to be believable. Probably a glitch.
     // Don"t record the new XY location, or do anything.
     return;
   }
   double xTranslationDistance = deltaX * XTranslationFactor;
   double yTranslationDistance = -deltaY * YTranslationFactor;
   //
   // Build transforms
   //
   translate.set(xTranslationDistance, yTranslationDistance, 0.0);
   transform1.set(translate);
   // Get and save the current transform
   subjectTransformGroup.getTransform(currentTransform);
   // Translate as needed
   currentTransform.mul(transform1);
   // Update the transform group
   subjectTransformGroup.setTransform(currentTransform);
   previousX = x;
   previousY = y;
 }

} // //CLASS //CheckboxMenu - build a menu of grouped checkboxes // //DESCRIPTION //The class creates a menu with one or more CheckboxMenuItem"s //and monitors that menu. When a menu checkbox is picked, the //previous one is turned off (in radio-button style). Then, //a given listener"s checkboxChanged method is called, passing it //the menu and the item checked. // class CheckboxMenu extends Menu implements ItemListener {

 // State
 protected CheckboxMenuItem[] checks = null;
 protected int current = 0;
 protected CheckboxMenuListener listener = null;
 //  Construct
 public CheckboxMenu(String name, NameValue[] items,
     CheckboxMenuListener listen) {
   this(name, items, 0, listen);
 }
 public CheckboxMenu(String name, NameValue[] items, int cur,
     CheckboxMenuListener listen) {
   super(name);
   current = cur;
   listener = listen;
   if (items == null)
     return;
   checks = new CheckboxMenuItem[items.length];
   for (int i = 0; i < items.length; i++) {
     checks[i] = new CheckboxMenuItem(items[i].name, false);
     checks[i].addItemListener(this);
     add(checks[i]);
   }
   checks[cur].setState(true);
 }
 //  Handle checkbox changed events
 public void itemStateChanged(ItemEvent event) {
   Object src = event.getSource();
   for (int i = 0; i < checks.length; i++) {
     if (src == checks[i]) {
       // Update the checkboxes
       checks[current].setState(false);
       current = i;
       checks[current].setState(true);
       if (listener != null)
         listener.checkboxChanged(this, i);
       return;
     }
   }
 }
 // Methods to get and set state
 public int getCurrent() {
   return current;
 }
 public void setCurrent(int cur) {
   if (cur < 0 || cur >= checks.length)
     return; // ignore out of range choices
   if (checks == null)
     return;
   checks[current].setState(false);
   current = cur;
   checks[current].setState(true);
 }
 public CheckboxMenuItem getSelectedCheckbox() {
   if (checks == null)
     return null;
   return checks[current];
 }
 public void setSelectedCheckbox(CheckboxMenuItem item) {
   if (checks == null)
     return;
   for (int i = 0; i < checks.length; i++) {
     if (item == checks[i]) {
       checks[i].setState(false);
       current = i;
       checks[i].setState(true);
     }
   }
 }

} /**

* ViewerBehavior
* 
* @version 1.0, 98/04/16
*/

/**

* Wakeup on mouse button presses, releases, and mouse movements and generate
* transforms for a transform group. Classes that extend this class impose
* specific symantics, such as "Examine" or "Walk" viewing, similar to the
* navigation types used by VRML browsers.
* 
* To support systems with 2 or 1 mouse buttons, the following alternate
* mappings are supported while dragging with any mouse button held down and
* zero or more keyboard modifiers held down:
* 
* No modifiers = Button 1 ALT = Button 2 Meta = Button 3 Control = Button 3
* 
* The behavior automatically modifies a TransformGroup provided to the
* constructor. The TransformGroup"s transform can be set at any time by the
* application or other behaviors to cause the viewer"s rotation and translation
* to be reset.
*/

// This class is inspired by the MouseBehavior, MouseRotate, // MouseTranslate, and MouseZoom utility behaviors provided with // Java 3D. This class differs from those utilities in that it: // // (a) encapsulates all three behaviors into one in order to // enforce a specific viewing symantic // // (b) supports set/get of the rotation and translation factors // that control the speed of movement. // // (c) supports the "Control" modifier as an alternative to the // "Meta" modifier not present on PC, Mac, and most non-Sun // keyboards. This makes button3 behavior usable on PCs, // Macs, and other systems with fewer than 3 mouse buttons. abstract class ViewerBehavior extends Behavior {

 // Keep track of the transform group who"s transform we modify
 // during mouse motion.
 protected TransformGroup subjectTransformGroup = null;
 // Keep a set of wakeup criterion for different mouse-generated
 // event types.
 protected WakeupCriterion[] mouseEvents = null;
 protected WakeupOr mouseCriterion = null;
 // Track which button was last pressed
 protected static final int BUTTONNONE = -1;
 protected static final int BUTTON1 = 0;
 protected static final int BUTTON2 = 1;
 protected static final int BUTTON3 = 2;
 protected int buttonPressed = BUTTONNONE;
 // Keep a few Transform3Ds for use during event processing. This
 // avoids having to allocate new ones on each event.
 protected Transform3D currentTransform = new Transform3D();
 protected Transform3D transform1 = new Transform3D();
 protected Transform3D transform2 = new Transform3D();
 protected Matrix4d matrix = new Matrix4d();
 protected Vector3d origin = new Vector3d(0.0, 0.0, 0.0);
 protected Vector3d translate = new Vector3d(0.0, 0.0, 0.0);
 // Unusual X and Y delta limits.
 protected static final int UNUSUAL_XDELTA = 400;
 protected static final int UNUSUAL_YDELTA = 400;
 protected Component parentComponent = null;
 /**
  * Construct a viewer behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into a
  * transform group given later with the setTransformGroup( ) method.
  */
 public ViewerBehavior() {
   super();
 }
 /**
  * Construct a viewer behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into a
  * transform group given later with the setTransformGroup( ) method.
  * 
  * @param parent
  *            The AWT Component that contains the area generating mouse
  *            events.
  */
 public ViewerBehavior(Component parent) {
   super();
   parentComponent = parent;
 }
 /**
  * Construct a viewer behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into the
  * given transform group.
  * 
  * @param transformGroup
  *            The transform group to be modified by the behavior.
  */
 public ViewerBehavior(TransformGroup transformGroup) {
   super();
   subjectTransformGroup = transformGroup;
 }
 /**
  * Construct a viewer behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into the
  * given transform group.
  * 
  * @param transformGroup
  *            The transform group to be modified by the behavior.
  * @param parent
  *            The AWT Component that contains the area generating mouse
  *            events.
  */
 public ViewerBehavior(TransformGroup transformGroup, Component parent) {
   super();
   subjectTransformGroup = transformGroup;
   parentComponent = parent;
 }
 /**
  * Set the transform group modified by the viewer behavior. Setting the
  * transform group to null disables the behavior until the transform group
  * is again set to an existing group.
  * 
  * @param transformGroup
  *            The new transform group to be modified by the behavior.
  */
 public void setTransformGroup(TransformGroup transformGroup) {
   subjectTransformGroup = transformGroup;
 }
 /**
  * Get the transform group modified by the viewer behavior.
  */
 public TransformGroup getTransformGroup() {
   return subjectTransformGroup;
 }
 /**
  * Sets the parent component who"s cursor will be changed during mouse
  * drags. If no component is given is given to the constructor, or set via
  * this method, no cursor changes will be done.
  * 
  * @param parent
  *            the AWT Component, such as a Frame, within which cursor
  *            changes should take place during mouse drags
  */
 public void setParentComponent(Component parent) {
   parentComponent = parent;
 }
 /*
  * Gets the parent frame within which the cursor changes during mouse drags.
  * 
  * @return the AWT Component, such as a Frame, within which cursor changes
  * should take place during mouse drags. Returns null if no parent is set.
  */
 public Component getParentComponent() {
   return parentComponent;
 }
 /**
  * Initialize the behavior.
  */
 public void initialize() {
   // Wakeup when the mouse is dragged or when a mouse button
   // is pressed or released.
   mouseEvents = new WakeupCriterion[3];
   mouseEvents[0] = new WakeupOnAWTEvent(MouseEvent.MOUSE_DRAGGED);
   mouseEvents[1] = new WakeupOnAWTEvent(MouseEvent.MOUSE_PRESSED);
   mouseEvents[2] = new WakeupOnAWTEvent(MouseEvent.MOUSE_RELEASED);
   mouseCriterion = new WakeupOr(mouseEvents);
   wakeupOn(mouseCriterion);
 }
 /**
  * Process a new wakeup. Interpret mouse button presses, releases, and mouse
  * drags.
  * 
  * @param criteria
  *            The wakeup criteria causing the behavior wakeup.
  */
 public void processStimulus(Enumeration criteria) {
   WakeupCriterion wakeup = null;
   AWTEvent[] event = null;
   int whichButton = BUTTONNONE;
   // Process all pending wakeups
   while (criteria.hasMoreElements()) {
     wakeup = (WakeupCriterion) criteria.nextElement();
     if (wakeup instanceof WakeupOnAWTEvent) {
       event = ((WakeupOnAWTEvent) wakeup).getAWTEvent();
       // Process all pending events
       for (int i = 0; i < event.length; i++) {
         if (event[i].getID() != MouseEvent.MOUSE_PRESSED
             && event[i].getID() != MouseEvent.MOUSE_RELEASED
             && event[i].getID() != MouseEvent.MOUSE_DRAGGED)
           // Ignore uninteresting mouse events
           continue;
         //
         // Regretably, Java event handling (or perhaps
         // underlying OS event handling) doesn"t always
         // catch button bounces (redundant presses and
         // releases), or order events so that the last
         // drag event is delivered before a release.
         // This means we can get stray events that we
         // filter out here.
         //
         if (event[i].getID() == MouseEvent.MOUSE_PRESSED
             && buttonPressed != BUTTONNONE)
           // Ignore additional button presses until a release
           continue;
         if (event[i].getID() == MouseEvent.MOUSE_RELEASED
             && buttonPressed == BUTTONNONE)
           // Ignore additional button releases until a press
           continue;
         if (event[i].getID() == MouseEvent.MOUSE_DRAGGED
             && buttonPressed == BUTTONNONE)
           // Ignore drags until a press
           continue;
         MouseEvent mev = (MouseEvent) event[i];
         int modifiers = mev.getModifiers();
         //
         // Unfortunately, the underlying event handling
         // doesn"t do a "grab" operation when a mouse button
         // is pressed. This means that once a button is
         // pressed, if another mouse button or a keyboard
         // modifier key is pressed, the delivered mouse event
         // will show that a different button is being held
         // down. For instance:
         //
         // Action Event
         //  Button 1 press Button 1 press
         //  Drag with button 1 down Button 1 drag
         //  ALT press -
         //  Drag with ALT & button 1 down Button 2 drag
         //  Button 1 release Button 2 release
         //
         // The upshot is that we can get a button press
         // without a matching release, and the button
         // associated with a drag can change mid-drag.
         //
         // To fix this, we watch for an initial button
         // press, and thenceforth consider that button
         // to be the one held down, even if additional
         // buttons get pressed, and despite what is
         // reported in the event. Only when a button is
         // released, do we end such a grab.
         //
         if (buttonPressed == BUTTONNONE) {
           // No button is pressed yet, figure out which
           // button is down now and how to direct events
           if (((modifiers & InputEvent.BUTTON3_MASK) != 0)
               || (((modifiers & InputEvent.BUTTON1_MASK) != 0) && ((modifiers & InputEvent.CTRL_MASK) == InputEvent.CTRL_MASK))) {
             // Button 3 activity (META or CTRL down)
             whichButton = BUTTON3;
           } else if ((modifiers & InputEvent.BUTTON2_MASK) != 0) {
             // Button 2 activity (ALT down)
             whichButton = BUTTON2;
           } else {
             // Button 1 activity (no modifiers down)
             whichButton = BUTTON1;
           }
           // If the event is to press a button, then
           // record that that button is now down
           if (event[i].getID() == MouseEvent.MOUSE_PRESSED)
             buttonPressed = whichButton;
         } else {
           // Otherwise a button was pressed earlier and
           // hasn"t been released yet. Assign all further
           // events to it, even if ALT, META, CTRL, or
           // another button has been pressed as well.
           whichButton = buttonPressed;
         }
         // Distribute the event
         switch (whichButton) {
         case BUTTON1:
           onButton1(mev);
           break;
         case BUTTON2:
           onButton2(mev);
           break;
         case BUTTON3:
           onButton3(mev);
           break;
         default:
           break;
         }
         // If the event is to release a button, then
         // record that that button is now up
         if (event[i].getID() == MouseEvent.MOUSE_RELEASED)
           buttonPressed = BUTTONNONE;
       }
       continue;
     }
     if (wakeup instanceof WakeupOnElapsedFrames) {
       onElapsedFrames((WakeupOnElapsedFrames) wakeup);
       continue;
     }
   }
   // Reschedule us for another wakeup
   wakeupOn(mouseCriterion);
 }
 /**
  * Default X and Y rotation factors, and XYZ translation factors.
  */
 public static final double DEFAULT_XROTATION_FACTOR = 0.02;
 public static final double DEFAULT_YROTATION_FACTOR = 0.005;
 public static final double DEFAULT_XTRANSLATION_FACTOR = 0.02;
 public static final double DEFAULT_YTRANSLATION_FACTOR = 0.02;
 public static final double DEFAULT_ZTRANSLATION_FACTOR = 0.04;
 protected double XRotationFactor = DEFAULT_XROTATION_FACTOR;
 protected double YRotationFactor = DEFAULT_YROTATION_FACTOR;
 protected double XTranslationFactor = DEFAULT_XTRANSLATION_FACTOR;
 protected double YTranslationFactor = DEFAULT_YTRANSLATION_FACTOR;
 protected double ZTranslationFactor = DEFAULT_ZTRANSLATION_FACTOR;
 /**
  * Set the X rotation scaling factor for X-axis rotations.
  * 
  * @param factor
  *            The new scaling factor.
  */
 public void setXRotationFactor(double factor) {
   XRotationFactor = factor;
 }
 /**
  * Get the current X rotation scaling factor for X-axis rotations.
  */
 public double getXRotationFactor() {
   return XRotationFactor;
 }
 /**
  * Set the Y rotation scaling factor for Y-axis rotations.
  * 
  * @param factor
  *            The new scaling factor.
  */
 public void setYRotationFactor(double factor) {
   YRotationFactor = factor;
 }
 /**
  * Get the current Y rotation scaling factor for Y-axis rotations.
  */
 public double getYRotationFactor() {
   return YRotationFactor;
 }
 /**
  * Set the X translation scaling factor for X-axis translations.
  * 
  * @param factor
  *            The new scaling factor.
  */
 public void setXTranslationFactor(double factor) {
   XTranslationFactor = factor;
 }
 /**
  * Get the current X translation scaling factor for X-axis translations.
  */
 public double getXTranslationFactor() {
   return XTranslationFactor;
 }
 /**
  * Set the Y translation scaling factor for Y-axis translations.
  * 
  * @param factor
  *            The new scaling factor.
  */
 public void setYTranslationFactor(double factor) {
   YTranslationFactor = factor;
 }
 /**
  * Get the current Y translation scaling factor for Y-axis translations.
  */
 public double getYTranslationFactor() {
   return YTranslationFactor;
 }
 /**
  * Set the Z translation scaling factor for Z-axis translations.
  * 
  * @param factor
  *            The new scaling factor.
  */
 public void setZTranslationFactor(double factor) {
   ZTranslationFactor = factor;
 }
 /**
  * Get the current Z translation scaling factor for Z-axis translations.
  */
 public double getZTranslationFactor() {
   return ZTranslationFactor;
 }
 /**
  * Respond to a button1 event (press, release, or drag).
  * 
  * @param mouseEvent
  *            A MouseEvent to respond to.
  */
 public abstract void onButton1(MouseEvent mouseEvent);
 /**
  * Respond to a button2 event (press, release, or drag).
  * 
  * @param mouseEvent
  *            A MouseEvent to respond to.
  */
 public abstract void onButton2(MouseEvent mouseEvent);
 /**
  * Responed to a button3 event (press, release, or drag).
  * 
  * @param mouseEvent
  *            A MouseEvent to respond to.
  */
 public abstract void onButton3(MouseEvent mouseEvent);
 /**
  * Respond to an elapsed frames event (assuming subclass has set up a wakeup
  * criterion for it).
  * 
  * @param time
  *            A WakeupOnElapsedFrames criterion to respond to.
  */
 public abstract void onElapsedFrames(WakeupOnElapsedFrames timeEvent);

} // //CLASS //NameValue - create a handy name-value pair // //DESCRIPTION //It is frequently handy to have one or more name-value pairs //with which to store named colors, named positions, named textures, //and so forth. Several of the examples use this class. // //AUTHOR //David R. Nadeau / San Diego Supercomputer Center // class NameValue {

 public String name;
 public Object value;
 public NameValue(String n, Object v) {
   name = n;
   value = v;
 }

}


      </source>
   
  
 
  



ExDirectionalLight - illustrate use of directional lights

   <source lang="java">

// //CLASS //ExDirectionalLight - illustrate use of directional lights // //LESSON //Add a DirectionalLight node to illuminate a scene. // //SEE ALSO //ExAmbientLight //ExPointLight //ExSpotLight // //AUTHOR //David R. Nadeau / San Diego Supercomputer Center // import java.applet.Applet; import java.awt.AWTEvent; import java.awt.BorderLayout; import java.awt.CheckboxMenuItem; import java.awt.ruponent; import java.awt.Cursor; import java.awt.Frame; import java.awt.Menu; import java.awt.MenuBar; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.InputEvent; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.event.MouseEvent; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.io.File; import java.util.Enumeration; import java.util.EventListener; 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.ColoringAttributes; import javax.media.j3d.DirectionalLight; import javax.media.j3d.GeometryArray; import javax.media.j3d.Group; import javax.media.j3d.Light; import javax.media.j3d.LineArray; import javax.media.j3d.LineAttributes; import javax.media.j3d.Material; import javax.media.j3d.Shape3D; import javax.media.j3d.Transform3D; import javax.media.j3d.TransformGroup; import javax.media.j3d.WakeupCriterion; import javax.media.j3d.WakeupOnAWTEvent; import javax.media.j3d.WakeupOnElapsedFrames; import javax.media.j3d.WakeupOr; import javax.vecmath.AxisAngle4f; import javax.vecmath.Color3f; import javax.vecmath.Matrix4d; import javax.vecmath.Matrix4f; import javax.vecmath.Point3d; import javax.vecmath.Point3f; import javax.vecmath.Vector3d; import javax.vecmath.Vector3f; import com.sun.j3d.utils.geometry.Cone; import com.sun.j3d.utils.geometry.Primitive; import com.sun.j3d.utils.geometry.Sphere; import com.sun.j3d.utils.universe.PlatformGeometry; import com.sun.j3d.utils.universe.SimpleUniverse; import com.sun.j3d.utils.universe.Viewer; import com.sun.j3d.utils.universe.ViewingPlatform; public class ExDirectionalLight extends Java3DFrame {

 //--------------------------------------------------------------
 //  SCENE CONTENT
 //--------------------------------------------------------------
 //
 //  Nodes (updated via menu)
 //
 private DirectionalLight light = null;
 //
 //  Build scene
 //
 public Group buildScene() {
   // Get the current color and direction
   Color3f color = (Color3f) colors[currentColor].value;
   Vector3f dir = (Vector3f) directions[currentDirection].value;
   // Turn off the example headlight
   setHeadlightEnable(false);
   // Build the scene group
   Group scene = new Group();
   // BEGIN EXAMPLE TOPIC
   // Create influencing bounds
   BoundingSphere worldBounds = new BoundingSphere(new Point3d(0.0, 0.0,
       0.0), // Center
       1000.0); // Extent
   // Set the light color and its influencing bounds
   light = new DirectionalLight();
   light.setEnable(lightOnOff);
   light.setColor(color);
   light.setDirection(dir);
   light.setCapability(DirectionalLight.ALLOW_STATE_WRITE);
   light.setCapability(DirectionalLight.ALLOW_COLOR_WRITE);
   light.setCapability(DirectionalLight.ALLOW_DIRECTION_WRITE);
   light.setInfluencingBounds(worldBounds);
   scene.addChild(light);
   // END EXAMPLE TOPIC
   // Build foreground geometry
   scene.addChild(new SphereGroup());
   // Add anotation arrows pointing in +-X, +-Y, +-Z to
   // illustrate aim direction
   scene.addChild(buildArrows());
   return scene;
 }
 //--------------------------------------------------------------
 //  FOREGROUND AND ANNOTATION CONTENT
 //--------------------------------------------------------------
 //
 //  Create a set of annotation arrows initially pointing in
 //  the +X direciton. Next, build an array of Transform3D"s,
 //  one for each of the aim directions shown on the directions
 //  menu. Save these Transform3Ds and a top-level TransformGroup
 //  surrounding the arrows. Later, when the user selects a new
 //  light direction, we poke the corresponding Transform3D into
 //  the TransformGroup to cause the arrows to change direction.
 //
 private TransformGroup arrowDirectionTransformGroup = null;
 private Transform3D[] arrowDirectionTransforms = null;
 private Group buildArrows() {
   // Create a transform group surrounding the arrows.
   // Enable writing of its transform.
   arrowDirectionTransformGroup = new TransformGroup();
   arrowDirectionTransformGroup
       .setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
   // Create a group of arrows and add the group to the
   // transform group. The arrows point in the +X direction.
   AnnotationArrowGroup ag = new AnnotationArrowGroup(-2.0f, 2.0f, // X
       // start
       // and
       // end
       1.5f, -1.5f, // Y start and end
       5); // Number of arrows
   arrowDirectionTransformGroup.addChild(ag);
   // Create a set of Transform3Ds for the different
   // arrow directions.
   arrowDirectionTransforms = new Transform3D[directions.length];
   Vector3f dir = new Vector3f();
   Vector3f positiveX = new Vector3f(1.0f, 0.0f, 0.0f);
   Vector3f axis = new Vector3f();
   float angle;
   float dot;
   for (int i = 0; i < directions.length; i++) {
     // Normalize the direction vector
     dir.normalize((Vector3f) directions[i].value);
     // Cross the direction vector with the arrow"s
     // +X aim direction to get a vector orthogonal
     // to both. This is the rotation axis.
     axis.cross(positiveX, dir);
     if (axis.x == 0.0f && axis.y == 0.0f && axis.z == 0.0f) {
       // New direction is parallel to current
       // arrow direction. Default to a Y axis.
       axis.y = 1.0f;
     }
     // Compute the angle between the direction and +X
     // vectors, where:
     //
     //   cos(angle) = (dir dot positiveX)
     //                -------------------------------
     //                (positiveX.length * dir.length)
     //
     // but since positiveX is normalized (as created
     // above and dir has been normalized, both have a
     // length of 1. So, the angle between the
     // vectors is:
     //
     //   angle = arccos(dir dot positiveX)
     //
     dot = dir.dot(positiveX);
     angle = (float) Math.acos(dot);
     // Create a Transform3D, setting its rotation using
     // an AxisAngle4f, which takes an XYZ rotation vector
     // and an angle to rotate by around that vector.
     arrowDirectionTransforms[i] = new Transform3D();
     arrowDirectionTransforms[i].setRotation(new AxisAngle4f(axis.x,
         axis.y, axis.z, angle));
   }
   // Set the initial transform to be the current aim direction.
   arrowDirectionTransformGroup
       .setTransform(arrowDirectionTransforms[currentDirection]);
   return arrowDirectionTransformGroup;
 }
 //--------------------------------------------------------------
 //  USER INTERFACE
 //--------------------------------------------------------------
 //
 //  Main
 //
 public static void main(String[] args) {
   ExDirectionalLight ex = new ExDirectionalLight();
   ex.initialize(args);
   ex.buildUniverse();
   ex.showFrame();
 }
 //  On/off choices
 private boolean lightOnOff = true;
 private CheckboxMenuItem lightOnOffMenu = null;
 //  Color menu choices
 private NameValue[] colors = { new NameValue("White", White),
     new NameValue("Gray", Gray), new NameValue("Black", Black),
     new NameValue("Red", Red), new NameValue("Yellow", Yellow),
     new NameValue("Green", Green), new NameValue("Cyan", Cyan),
     new NameValue("Blue", Blue), new NameValue("Magenta", Magenta), };
 private int currentColor = 0;
 private CheckboxMenu colorMenu = null;
 //  Direction menu choices
 private NameValue[] directions = { new NameValue("Positive X", PosX),
     new NameValue("Negative X", NegX),
     new NameValue("Positive Y", PosY),
     new NameValue("Negative Y", NegY),
     new NameValue("Positive Z", PosZ),
     new NameValue("Negative Z", NegZ), };
 private int currentDirection = 0;
 private CheckboxMenu directionMenu = null;
 //
 //  Initialize the GUI (application and applet)
 //
 public void initialize(String[] args) {
   // Initialize the window, menubar, etc.
   super.initialize(args);
   exampleFrame.setTitle("Java 3D Directional Light Example");
   //
   //  Add a menubar menu to change node parameters
   //    Light on/off
   //    Color -->
   //    Direction -->
   //
   Menu m = new Menu("DirectionalLight");
   lightOnOffMenu = new CheckboxMenuItem("Light on/off", lightOnOff);
   lightOnOffMenu.addItemListener(this);
   m.add(lightOnOffMenu);
   colorMenu = new CheckboxMenu("Color", colors, currentColor, this);
   m.add(colorMenu);
   directionMenu = new CheckboxMenu("Direction", directions,
       currentDirection, this);
   m.add(directionMenu);
   exampleMenuBar.add(m);
 }
 //
 //  Handle checkboxes and menu choices
 //
 public void checkboxChanged(CheckboxMenu menu, int check) {
   if (menu == colorMenu) {
     // Change the light color
     currentColor = check;
     Color3f color = (Color3f) colors[check].value;
     light.setColor(color);
     return;
   }
   if (menu == directionMenu) {
     // Change the light direction
     currentDirection = check;
     Vector3f dir = (Vector3f) directions[check].value;
     light.setDirection(dir);
     // Change the arrow group direction
     arrowDirectionTransformGroup
         .setTransform(arrowDirectionTransforms[check]);
     return;
   }
   // Handle all other checkboxes
   super.checkboxChanged(menu, check);
 }
 public void itemStateChanged(ItemEvent event) {
   Object src = event.getSource();
   if (src == lightOnOffMenu) {
     // Turn the light on or off
     lightOnOff = lightOnOffMenu.getState();
     light.setEnable(lightOnOff);
     return;
   }
   // Handle all other checkboxes
   super.itemStateChanged(event);
 }

} // //CLASS //AnnotationArrowGroup - A group of parallel arrows // //DESCRIPTION //This class creates one or more parallel 3D, unlighted arrows. //Such arrow groups can be used to indicate directional light //directions, and so forth. // //The arrow group is drawn in the XY plane, pointing right. //The X start and end values, and the Y start and end values //can be set, along with the count of the number of arrows to //build. // //SEE ALSO //AnnotationArrow //AnnotationArrowFan // //AUTHOR //David R. Nadeau / San Diego Supercomputer Center // // class AnnotationArrowGroup extends Group {

 // 3D nodes
 AnnotationArrow[] arrows;
 //  Constructors
 public AnnotationArrowGroup() {
   //    xStart xEnd yStart yEnd count
   this(-1.0f, 1.0f, 1.0f, -1.0f, 3);
 }
 public AnnotationArrowGroup(float xStart, float xEnd, float yStart,
     float yEnd, int count) {
   arrows = new AnnotationArrow[count];
   float y = yStart;
   float deltaY = (yEnd - yStart) / (float) (count - 1);
   for (int i = 0; i < count; i++) {
     arrows[i] = new AnnotationArrow(xStart, y, 0.0f, xEnd, y, 0.0f);
     addChild(arrows[i]);
     y += deltaY;
   }
 }

} // //CLASS //AnnotationArrow - 3D arrow used for annotation & diagrams // //DESCRIPTION //This class creates a 3D, unlighted line between two 3D coordinates //plus a cone-shaped arrow at the line"s endpoint. The line"s width //and color can be controlled. The arrow head"s width and length //can be controlled. // //SEE ALSO //AnnotationLine //AnnotationAxes //AnnotationArrowFan //AnnotationArrowGroup // //AUTHOR //David R. Nadeau / San Diego Supercomputer Center // class AnnotationArrow extends AnnotationLine {

 // Parameters
 private Color3f arrowColor = new Color3f(1.0f, 1.0f, 1.0f);
 private float arrowRadius = 0.1f;
 private float arrowLength = 0.20f;
 private float lineWidth = 3.0f;
 private int radialDivisions = 8;
 private int sideDivisions = 1;
 // 3D Nodes
 private Cone arrowHead = null;
 private Appearance arrowAppearance = null;
 private TransformGroup arrowTrans = null;
 private ColoringAttributes coloringAttributes = null;
 //
 //  Construct a straight line
 //
 public AnnotationArrow(float x2, float y2, float z2) {
   //    origin to given coordinate
   this(0.0f, 0.0f, 0.0f, x2, y2, z2);
 }
 public AnnotationArrow(float x, float y, float z, float x2, float y2,
     float z2) {
   super(x, y, z, x2, y2, z2);
   setLineWidth(lineWidth);
   // Compute the length and direction of the line
   float deltaX = x2 - x;
   float deltaY = y2 - y;
   float deltaZ = z2 - z;
   float theta = -(float) Math.atan2(deltaZ, deltaX);
   float phi = (float) Math.atan2(deltaY, deltaX);
   if (deltaX < 0.0f) {
     phi = (float) Math.PI - phi;
   }
   // Compute a matrix to rotate a cone to point in the line"s
   // direction, then place the cone at the line"s endpoint.
   Matrix4f mat = new Matrix4f();
   Matrix4f mat2 = new Matrix4f();
   mat.setIdentity();
   // Move to the endpoint of the line
   mat2.setIdentity();
   mat2.setTranslation(new Vector3f(x2, y2, z2));
   mat.mul(mat2);
   // Spin around Y
   mat2.setIdentity();
   mat2.rotY(theta);
   mat.mul(mat2);
   // Tilt up or down around Z
   mat2.setIdentity();
   mat2.rotZ(phi);
   mat.mul(mat2);
   // Tilt cone to point right
   mat2.setIdentity();
   mat2.rotZ(-1.571f);
   mat.mul(mat2);
   arrowTrans = new TransformGroup();
   arrowTrans.setCapability(Group.ALLOW_CHILDREN_WRITE);
   Transform3D trans = new Transform3D(mat);
   arrowTrans.setTransform(trans);
   // Create an appearance
   arrowAppearance = new Appearance();
   arrowAppearance
       .setCapability(Appearance.ALLOW_COLORING_ATTRIBUTES_WRITE);
   getLineColor(arrowColor);
   coloringAttributes = new ColoringAttributes();
   coloringAttributes.setColor(arrowColor);
   coloringAttributes.setShadeModel(ColoringAttributes.SHADE_FLAT);
   arrowAppearance.setColoringAttributes(coloringAttributes);
   // Build a cone for the arrow head
   arrowHead = new Cone(arrowRadius, // base radius
       arrowLength, // height
       0, // don"t generate normals
       radialDivisions, // divisions radially
       sideDivisions, // divisions vertically
       arrowAppearance); // appearance
   arrowTrans.addChild(arrowHead);
   addChild(arrowTrans);
 }
 //
 //  Control the arrow head size
 //
 public void setArrowHeadRadius(float radius) {
   arrowRadius = radius;
   arrowTrans.removeChild(0);
   arrowHead = new Cone(arrowRadius, // base radius
       arrowLength, // height
       0, // don"t generate normals
       radialDivisions, // divisions radially
       sideDivisions, // divisions vertically
       arrowAppearance); // appearance
   arrowTrans.addChild(arrowHead);
 }
 public void setArrowHeadLength(float length) {
   arrowLength = length;
   arrowTrans.removeChild(0);
   arrowHead = new Cone(arrowRadius, // base radius
       arrowLength, // height
       0, // don"t generate normals
       radialDivisions, // divisions radially
       sideDivisions, // divisions vertically
       arrowAppearance); // appearance
   arrowTrans.addChild(arrowHead);
 }
 public float getArrowHeadRadius() {
   return arrowRadius;
 }
 public float getArrowHeadLength() {
   return arrowLength;
 }
 //
 //  Control the line color
 //
 public void setLineColor(Color3f color) {
   super.setLineColor(color);
   getLineColor(arrowColor);
   coloringAttributes.setColor(arrowColor);
   arrowAppearance.setColoringAttributes(coloringAttributes);
   arrowHead.setAppearance(arrowAppearance);
 }
 public void setLineColor(float r, float g, float b) {
   super.setLineColor(r, g, b);
   getLineColor(arrowColor);
   coloringAttributes.setColor(arrowColor);
   arrowAppearance.setColoringAttributes(coloringAttributes);
   arrowHead.setAppearance(arrowAppearance);
 }
 public void setLineColor(float[] color) {
   super.setLineColor(color);
   getLineColor(arrowColor);
   coloringAttributes.setColor(arrowColor);
   arrowAppearance.setColoringAttributes(coloringAttributes);
   arrowHead.setAppearance(arrowAppearance);
 }
 //
 //  Control the appearance
 //
 public void setAppearance(Appearance app) {
   super.setAppearance(app);
   arrowAppearance = app;
   arrowAppearance
       .setCapability(Appearance.ALLOW_COLORING_ATTRIBUTES_WRITE);
   arrowAppearance.setColoringAttributes(coloringAttributes);
   arrowHead.setAppearance(arrowAppearance);
 }
 //
 //  Provide info on the shape and geometry
 //
 public Shape3D getShape(int partid) {
   if (partid == Cone.BODY)
     return arrowHead.getShape(Cone.BODY);
   else if (partid == Cone.CAP)
     return arrowHead.getShape(Cone.CAP);
   else
     return super.getShape(partid);
 }
 public int getNumTriangles() {
   return arrowHead.getNumTriangles();
 }
 public int getNumVertices() {
   return arrowHead.getNumVertices() + super.getNumVertices();
 }

} // //CLASS //AnnotationLine - 3D line used for annotation & diagrams // //DESCRIPTION //This class creates a 3D, unlighted line between two 3D coordinates. //The line"s width and color can be controlled. // //SEE ALSO //AnnotationArrow // //AUTHOR //David R. Nadeau / San Diego Supercomputer Center // // class AnnotationLine extends Primitive {

 // Parameters
 private float lineWidth = 1;
 private Color3f lineColor = new Color3f(1.0f, 1.0f, 1.0f);
 // 3D nodes
 private Shape3D shape = null;
 private LineAttributes lineAttributes = null;
 private ColoringAttributes coloringAttributes = null;
 private LineArray line = null;
 protected Appearance mainAppearance = null;
 //
 //  Construct a straight line
 //
 public AnnotationLine(float x2, float y2, float z2) {
   //    origin to given coordinate
   this(0.0f, 0.0f, 0.0f, x2, y2, z2);
 }
 public AnnotationLine(float x, float y, float z, float x2, float y2,
     float z2) {
   float[] coord = new float[3];
   float[] texcoord = new float[2];
   // Build a shape
   shape = new Shape3D();
   shape.setCapability(Shape3D.ALLOW_APPEARANCE_WRITE);
   // Create geometry for a 2-vertex straight line
   line = new LineArray(2, GeometryArray.COORDINATES
       | GeometryArray.TEXTURE_COORDINATE_2);
   line.setCapability(GeometryArray.ALLOW_COLOR_WRITE);
   // Starting point
   coord[0] = x;
   coord[1] = y;
   coord[2] = z;
   texcoord[0] = 0.0f;
   texcoord[1] = 0.0f;
   line.setCoordinate(0, coord);
   line.setTextureCoordinate(0, texcoord);
   // Ending point
   coord[0] = x2;
   coord[1] = y2;
   coord[2] = z2;
   texcoord[0] = 1.0f;
   texcoord[1] = 0.0f;
   line.setCoordinate(1, coord);
   line.setTextureCoordinate(1, texcoord);
   shape.setGeometry(line);
   // Create an appearance
   mainAppearance = new Appearance();
   mainAppearance.setCapability(Appearance.ALLOW_LINE_ATTRIBUTES_WRITE);
   mainAppearance
       .setCapability(Appearance.ALLOW_COLORING_ATTRIBUTES_WRITE);
   lineAttributes = new LineAttributes();
   lineAttributes.setLineWidth(lineWidth);
   mainAppearance.setLineAttributes(lineAttributes);
   coloringAttributes = new ColoringAttributes();
   coloringAttributes.setColor(lineColor);
   coloringAttributes.setShadeModel(ColoringAttributes.SHADE_FLAT);
   mainAppearance.setColoringAttributes(coloringAttributes);
   addChild(shape);
 }
 //
 //  Control the line width
 //
 public float getLineWidth() {
   return lineWidth;
 }
 public void setLineWidth(float width) {
   lineWidth = width;
   lineAttributes.setLineWidth(lineWidth);
   mainAppearance.setLineAttributes(lineAttributes);
   shape.setAppearance(mainAppearance);
 }
 //
 //  Control the line color
 //
 public void getLineColor(Color3f color) {
   lineColor.get(color);
 }
 public void getLineColor(float[] color) {
   lineColor.get(color);
 }
 public void setLineColor(Color3f color) {
   lineColor.set(color);
   coloringAttributes.setColor(lineColor);
   mainAppearance.setColoringAttributes(coloringAttributes);
   shape.setAppearance(mainAppearance);
 }
 public void setLineColor(float r, float g, float b) {
   lineColor.set(r, g, b);
   coloringAttributes.setColor(lineColor);
   mainAppearance.setColoringAttributes(coloringAttributes);
   shape.setAppearance(mainAppearance);
 }
 public void setLineColor(float[] color) {
   lineColor.set(color);
   coloringAttributes.setColor(lineColor);
   mainAppearance.setColoringAttributes(coloringAttributes);
   shape.setAppearance(mainAppearance);
 }
 //
 //  Control the appearance
 //
 public void setAppearance(Appearance app) {
   mainAppearance = app;
   mainAppearance.setCapability(Appearance.ALLOW_LINE_ATTRIBUTES_WRITE);
   mainAppearance
       .setCapability(Appearance.ALLOW_COLORING_ATTRIBUTES_WRITE);
   mainAppearance.setLineAttributes(lineAttributes);
   mainAppearance.setColoringAttributes(coloringAttributes);
   shape.setAppearance(mainAppearance);
 }
 //
 //  Provide info on the shape and geometry
 //
 public Shape3D getShape(int partid) {
   return shape;
 }
 public int getNumTriangles() {
   return 0;
 }
 public int getNumVertices() {
   return 2;
 }
 /* (non-Javadoc)
  * @see com.sun.j3d.utils.geometry.Primitive#getAppearance(int)
  */
 public Appearance getAppearance(int arg0) {
   // TODO Auto-generated method stub
   return null;
 }

} // //CLASS //SphereGroup - create a group of spheres on the XY plane // //DESCRIPTION //An XY grid of spheres is created. The number of spheres in X and Y, //the spacing in X and Y, the sphere radius, and the appearance can //all be set. // //This grid of spheres is used by several of the examples as a generic //bit of foreground geometry. // //SEE ALSO //Ex*Light //ExBackground* // //AUTHOR //David R. Nadeau / San Diego Supercomputer Center // class SphereGroup extends Group {

 //  Constructors
 public SphereGroup() {
   //    radius x,y spacing x,y count appearance
   this(0.25f, 0.75f, 0.75f, 5, 5, null);
 }
 public SphereGroup(Appearance app) {
   //    radius x,y spacing x,y count appearance
   this(0.25f, 0.75f, 0.75f, 5, 5, app);
 }
 public SphereGroup(float radius, float xSpacing, float ySpacing,
     int xCount, int yCount) {
   this(radius, xSpacing, ySpacing, xCount, yCount, null);
 }
 public SphereGroup(float radius, float xSpacing, float ySpacing,
     int xCount, int yCount, Appearance app) {
   if (app == null) {
     app = new Appearance();
     Material material = new Material();
     material.setDiffuseColor(new Color3f(0.8f, 0.8f, 0.8f));
     material.setSpecularColor(new Color3f(0.0f, 0.0f, 0.0f));
     material.setShininess(0.0f);
     app.setMaterial(material);
   }
   double xStart = -xSpacing * (double) (xCount - 1) / 2.0;
   double yStart = -ySpacing * (double) (yCount - 1) / 2.0;
   Sphere sphere = null;
   TransformGroup trans = null;
   Transform3D t3d = new Transform3D();
   Vector3d vec = new Vector3d();
   double x, y = yStart, z = 0.0;
   for (int i = 0; i < yCount; i++) {
     x = xStart;
     for (int j = 0; j < xCount; j++) {
       vec.set(x, y, z);
       t3d.setTranslation(vec);
       trans = new TransformGroup(t3d);
       addChild(trans);
       sphere = new Sphere(radius, // sphere radius
           Primitive.GENERATE_NORMALS, // generate normals
           16, // 16 divisions radially
           app); // it"s appearance
       trans.addChild(sphere);
       x += xSpacing;
     }
     y += ySpacing;
   }
 }

} /**

* The Example class is a base class extended by example applications. The class
* provides basic features to create a top-level frame, add a menubar and
* Canvas3D, build the universe, set up "examine" and "walk" style navigation
* behaviors, and provide hooks so that subclasses can add 3D content to the
* example"s universe.
* <P>
* Using this Example class simplifies the construction of example applications,
* enabling the author to focus upon 3D content and not the busywork of creating
* windows, menus, and universes.
* 
* @version 1.0, 98/04/16
* @author David R. Nadeau, San Diego Supercomputer Center
*/

class Java3DFrame extends Applet implements WindowListener, ActionListener,

   ItemListener, CheckboxMenuListener {
 //  Navigation types
 public final static int Walk = 0;
 public final static int Examine = 1;
 //  Should the scene be compiled?
 private boolean shouldCompile = true;
 //  GUI objects for our subclasses
 protected Java3DFrame example = null;
 protected Frame exampleFrame = null;
 protected MenuBar exampleMenuBar = null;
 protected Canvas3D exampleCanvas = null;
 protected TransformGroup exampleViewTransform = null;
 protected TransformGroup exampleSceneTransform = null;
 protected boolean debug = false;
 //  Private GUI objects and state
 private boolean headlightOnOff = true;
 private int navigationType = Examine;
 private CheckboxMenuItem headlightMenuItem = null;
 private CheckboxMenuItem walkMenuItem = null;
 private CheckboxMenuItem examineMenuItem = null;
 private DirectionalLight headlight = null;
 private ExamineViewerBehavior examineBehavior = null;
 private WalkViewerBehavior walkBehavior = null;
 //--------------------------------------------------------------
 //  ADMINISTRATION
 //--------------------------------------------------------------
 /**
  * The main program entry point when invoked as an application. Each example
  * application that extends this class must define their own main.
  * 
  * @param args
  *            a String array of command-line arguments
  */
 public static void main(String[] args) {
   Java3DFrame ex = new Java3DFrame();
   ex.initialize(args);
   ex.buildUniverse();
   ex.showFrame();
 }
 /**
  * Constructs a new Example object.
  * 
  * @return a new Example that draws no 3D content
  */
 public Java3DFrame() {
   // Do nothing
 }
 /**
  * Initializes the application when invoked as an applet.
  */
 public void init() {
   // Collect properties into String array
   String[] args = new String[2];
   // NOTE: to be done still...
   this.initialize(args);
   this.buildUniverse();
   this.showFrame();
   // NOTE: add something to the browser page?
 }
 /**
  * Initializes the Example by parsing command-line arguments, building an
  * AWT Frame, constructing a menubar, and creating the 3D canvas.
  * 
  * @param args
  *            a String array of command-line arguments
  */
 protected void initialize(String[] args) {
   example = this;
   // Parse incoming arguments
   parseArgs(args);
   // Build the frame
   if (debug)
     System.err.println("Building GUI...");
   exampleFrame = new Frame();
   exampleFrame.setSize(640, 480);
   exampleFrame.setTitle("Java 3D Example");
   exampleFrame.setLayout(new BorderLayout());
   // Set up a close behavior
   exampleFrame.addWindowListener(this);
   // Create a canvas
   exampleCanvas = new Canvas3D(null);
   exampleCanvas.setSize(630, 460);
   exampleFrame.add("Center", exampleCanvas);
   // Build the menubar
   exampleMenuBar = this.buildMenuBar();
   exampleFrame.setMenuBar(exampleMenuBar);
   // Pack
   exampleFrame.pack();
   exampleFrame.validate();
   //    exampleFrame.setVisible( true );
 }
 /**
  * Parses incoming command-line arguments. Applications that subclass this
  * class may override this method to support their own command-line
  * arguments.
  * 
  * @param args
  *            a String array of command-line arguments
  */
 protected void parseArgs(String[] args) {
   for (int i = 0; i < args.length; i++) {
     if (args[i].equals("-d"))
       debug = true;
   }
 }
 //--------------------------------------------------------------
 //  SCENE CONTENT
 //--------------------------------------------------------------
 /**
  * Builds the 3D universe by constructing a virtual universe (via
  * SimpleUniverse), a view platform (via SimpleUniverse), and a view (via
  * SimpleUniverse). A headlight is added and a set of behaviors initialized
  * to handle navigation types.
  */
 protected void buildUniverse() {
   //
   //  Create a SimpleUniverse object, which builds:
   //
   //    - a Locale using the given hi-res coordinate origin
   //
   //    - a ViewingPlatform which in turn builds:
   //          - a MultiTransformGroup with which to move the
   //            the ViewPlatform about
   //
   //          - a ViewPlatform to hold the view
   //
   //          - a BranchGroup to hold avatar geometry (if any)
   //
   //          - a BranchGroup to hold view platform
   //            geometry (if any)
   //
   //    - a Viewer which in turn builds:
   //          - a PhysicalBody which characterizes the user"s
   //            viewing preferences and abilities
   //
   //          - a PhysicalEnvironment which characterizes the
   //            user"s rendering hardware and software
   //
   //          - a JavaSoundMixer which initializes sound
   //            support within the 3D environment
   //
   //          - a View which renders the scene into a Canvas3D
   //
   //  All of these actions could be done explicitly, but
   //  using the SimpleUniverse utilities simplifies the code.
   //
   if (debug)
     System.err.println("Building scene graph...");
   SimpleUniverse universe = new SimpleUniverse(null, // Hi-res coordinate
       // for the origin -
       // use default
       1, // Number of transforms in MultiTransformGroup
       exampleCanvas, // Canvas3D into which to draw
       null); // URL for user configuration file - use defaults
   //
   //  Get the viewer and create an audio device so that
   //  sound will be enabled in this content.
   //
   Viewer viewer = universe.getViewer();
   viewer.createAudioDevice();
   //
   //  Get the viewing platform created by SimpleUniverse.
   //  From that platform, get the inner-most TransformGroup
   //  in the MultiTransformGroup. That inner-most group
   //  contains the ViewPlatform. It is this inner-most
   //  TransformGroup we need in order to:
   //
   //    - add a "headlight" that always aims forward from
   //       the viewer
   //
   //    - change the viewing direction in a "walk" style
   //
   //  The inner-most TransformGroup"s transform will be
   //  changed by the walk behavior (when enabled).
   //
   ViewingPlatform viewingPlatform = universe.getViewingPlatform();
   exampleViewTransform = viewingPlatform.getViewPlatformTransform();
   //
   //  Create a "headlight" as a forward-facing directional light.
   //  Set the light"s bounds to huge. Since we want the light
   //  on the viewer"s "head", we need the light within the
   //  TransformGroup containing the ViewPlatform. The
   //  ViewingPlatform class creates a handy hook to do this
   //  called "platform geometry". The PlatformGeometry class is
   //  subclassed off of BranchGroup, and is intended to contain
   //  a description of the 3D platform itself... PLUS a headlight!
   //  So, to add the headlight, create a new PlatformGeometry group,
   //  add the light to it, then add that platform geometry to the
   //  ViewingPlatform.
   //
   BoundingSphere allBounds = new BoundingSphere(
       new Point3d(0.0, 0.0, 0.0), 100000.0);
   PlatformGeometry pg = new PlatformGeometry();
   headlight = new DirectionalLight();
   headlight.setColor(White);
   headlight.setDirection(new Vector3f(0.0f, 0.0f, -1.0f));
   headlight.setInfluencingBounds(allBounds);
   headlight.setCapability(Light.ALLOW_STATE_WRITE);
   pg.addChild(headlight);
   viewingPlatform.setPlatformGeometry(pg);
   //
   //  Create the 3D content BranchGroup, containing:
   //
   //    - a TransformGroup who"s transform the examine behavior
   //      will change (when enabled).
   //
   //    - 3D geometry to view
   //
   // Build the scene root
   BranchGroup sceneRoot = new BranchGroup();
   // Build a transform that we can modify
   exampleSceneTransform = new TransformGroup();
   exampleSceneTransform
       .setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
   exampleSceneTransform
       .setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
   exampleSceneTransform.setCapability(Group.ALLOW_CHILDREN_EXTEND);
   //
   //  Build the scene, add it to the transform, and add
   //  the transform to the scene root
   //
   if (debug)
     System.err.println("  scene...");
   Group scene = this.buildScene();
   exampleSceneTransform.addChild(scene);
   sceneRoot.addChild(exampleSceneTransform);
   //
   //  Create a pair of behaviors to implement two navigation
   //  types:
   //
   //    - "examine": a style where mouse drags rotate about
   //      the scene"s origin as if it is an object under
   //      examination. This is similar to the "Examine"
   //      navigation type used by VRML browsers.
   //
   //    - "walk": a style where mouse drags rotate about
   //      the viewer"s center as if the viewer is turning
   //      about to look at a scene they are in. This is
   //      similar to the "Walk" navigation type used by
   //      VRML browsers.
   //
   //  Aim the examine behavior at the scene"s TransformGroup
   //  and add the behavior to the scene root.
   //
   //  Aim the walk behavior at the viewing platform"s
   //  TransformGroup and add the behavior to the scene root.
   //
   //  Enable one (and only one!) of the two behaviors
   //  depending upon the current navigation type.
   //
   examineBehavior = new ExamineViewerBehavior(exampleSceneTransform, // Transform
       // gorup
       // to
       // modify
       exampleFrame); // Parent frame for cusor changes
   examineBehavior.setSchedulingBounds(allBounds);
   sceneRoot.addChild(examineBehavior);
   walkBehavior = new WalkViewerBehavior(exampleViewTransform, // Transform
       // group to
       // modify
       exampleFrame); // Parent frame for cusor changes
   walkBehavior.setSchedulingBounds(allBounds);
   sceneRoot.addChild(walkBehavior);
   if (navigationType == Walk) {
     examineBehavior.setEnable(false);
     walkBehavior.setEnable(true);
   } else {
     examineBehavior.setEnable(true);
     walkBehavior.setEnable(false);
   }
   //
   //  Compile the scene branch group and add it to the
   //  SimpleUniverse.
   //
   if (shouldCompile)
     sceneRoot.rupile();
   universe.addBranchGraph(sceneRoot);
   reset();
 }
 /**
  * Builds the scene. Example application subclasses should replace this
  * method with their own method to build 3D content.
  * 
  * @return a Group containing 3D content to display
  */
 public Group buildScene() {
   // Build the scene group containing nothing
   Group scene = new Group();
   return scene;
 }
 //--------------------------------------------------------------
 //  SET/GET METHODS
 //--------------------------------------------------------------
 /**
  * Sets the headlight on/off state. The headlight faces forward in the
  * direction the viewer is facing. Example applications that add their own
  * lights will typically turn the headlight off. A standard menu item
  * enables the headlight to be turned on and off via user control.
  * 
  * @param onOff
  *            a boolean turning the light on (true) or off (false)
  */
 public void setHeadlightEnable(boolean onOff) {
   headlightOnOff = onOff;
   if (headlight != null)
     headlight.setEnable(headlightOnOff);
   if (headlightMenuItem != null)
     headlightMenuItem.setState(headlightOnOff);
 }
 /**
  * Gets the headlight on/off state.
  * 
  * @return a boolean indicating if the headlight is on or off
  */
 public boolean getHeadlightEnable() {
   return headlightOnOff;
 }
 /**
  * Sets the navigation type to be either Examine or Walk. The Examine
  * navigation type sets up behaviors that use mouse drags to rotate and
  * translate scene content as if it is an object held at arm"s length and
  * under examination. The Walk navigation type uses mouse drags to rotate
  * and translate the viewer as if they are walking through the content. The
  * Examine type is the default.
  * 
  * @param nav
  *            either Walk or Examine
  */
 public void setNavigationType(int nav) {
   if (nav == Walk) {
     navigationType = Walk;
     if (walkMenuItem != null)
       walkMenuItem.setState(true);
     if (examineMenuItem != null)
       examineMenuItem.setState(false);
     if (walkBehavior != null)
       walkBehavior.setEnable(true);
     if (examineBehavior != null)
       examineBehavior.setEnable(false);
   } else {
     navigationType = Examine;
     if (walkMenuItem != null)
       walkMenuItem.setState(false);
     if (examineMenuItem != null)
       examineMenuItem.setState(true);
     if (walkBehavior != null)
       walkBehavior.setEnable(false);
     if (examineBehavior != null)
       examineBehavior.setEnable(true);
   }
 }
 /**
  * Gets the current navigation type, returning either Walk or Examine.
  * 
  * @return either Walk or Examine
  */
 public int getNavigationType() {
   return navigationType;
 }
 /**
  * Sets whether the scene graph should be compiled or not. Normally this is
  * always a good idea. For some example applications that use this Example
  * framework, it is useful to disable compilation - particularly when nodes
  * and node components will need to be made un-live in order to make
  * changes. Once compiled, such components can be made un-live, but they are
  * still unchangable unless appropriate capabilities have been set.
  * 
  * @param onOff
  *            a boolean turning compilation on (true) or off (false)
  */
 public void setCompilable(boolean onOff) {
   shouldCompile = onOff;
 }
 /**
  * Gets whether the scene graph will be compiled or not.
  * 
  * @return a boolean indicating if scene graph compilation is on or off
  */
 public boolean getCompilable() {
   return shouldCompile;
 }
 //These methods will be replaced
 //  Set the view position and direction
 public void setViewpoint(Point3f position, Vector3f direction) {
   Transform3D t = new Transform3D();
   t.set(new Vector3f(position));
   exampleViewTransform.setTransform(t);
   // how to set direction?
 }
 //  Reset transforms
 public void reset() {
   Transform3D trans = new Transform3D();
   exampleSceneTransform.setTransform(trans);
   trans.set(new Vector3f(0.0f, 0.0f, 10.0f));
   exampleViewTransform.setTransform(trans);
   setNavigationType(navigationType);
 }
 //
 //  Gets the URL (with file: prepended) for the current directory.
 //  This is a terrible hack needed in the Alpha release of Java3D
 //  in order to build a full path URL for loading sounds with
 //  MediaContainer. When MediaContainer is fully implemented,
 //  it should handle relative path names, but not yet.
 //
 public String getCurrentDirectory() {
   // Create a bogus file so that we can query it"s path
   File dummy = new File("dummy.tmp");
   String dummyPath = dummy.getAbsolutePath();
   // strip "/dummy.tmp" from end of dummyPath and put into "path"
   if (dummyPath.endsWith(File.separator + "dummy.tmp")) {
     int index = dummyPath.lastIndexOf(File.separator + "dummy.tmp");
     if (index >= 0) {
       int pathLength = index + 5; // pre-pend "file:"
       char[] charPath = new char[pathLength];
       dummyPath.getChars(0, index, charPath, 5);
       String path = new String(charPath, 0, pathLength);
       path = "file:" + path.substring(5, pathLength);
       return path + File.separator;
     }
   }
   return dummyPath + File.separator;
 }
 //--------------------------------------------------------------
 //  USER INTERFACE
 //--------------------------------------------------------------
 /**
  * Builds the example AWT Frame menubar. Standard menus and their options
  * are added. Applications that subclass this class should build their
  * menubar additions within their initialize method.
  * 
  * @return a MenuBar for the AWT Frame
  */
 private MenuBar buildMenuBar() {
   // Build the menubar
   MenuBar menuBar = new MenuBar();
   // File menu
   Menu m = new Menu("File");
   m.addActionListener(this);
   m.add("Exit");
   menuBar.add(m);
   // View menu
   m = new Menu("View");
   m.addActionListener(this);
   m.add("Reset view");
   m.addSeparator();
   walkMenuItem = new CheckboxMenuItem("Walk");
   walkMenuItem.addItemListener(this);
   m.add(walkMenuItem);
   examineMenuItem = new CheckboxMenuItem("Examine");
   examineMenuItem.addItemListener(this);
   m.add(examineMenuItem);
   if (navigationType == Walk) {
     walkMenuItem.setState(true);
     examineMenuItem.setState(false);
   } else {
     walkMenuItem.setState(false);
     examineMenuItem.setState(true);
   }
   m.addSeparator();
   headlightMenuItem = new CheckboxMenuItem("Headlight on/off");
   headlightMenuItem.addItemListener(this);
   headlightMenuItem.setState(headlightOnOff);
   m.add(headlightMenuItem);
   menuBar.add(m);
   return menuBar;
 }
 /**
  * Shows the application"s frame, making it and its menubar, 3D canvas, and
  * 3D content visible.
  */
 public void showFrame() {
   exampleFrame.show();
 }
 /**
  * Quits the application.
  */
 public void quit() {
   System.exit(0);
 }
 /**
  * Handles menu selections.
  * 
  * @param event
  *            an ActionEvent indicating what menu action requires handling
  */
 public void actionPerformed(ActionEvent event) {
   String arg = event.getActionCommand();
   if (arg.equals("Reset view"))
     reset();
   else if (arg.equals("Exit"))
     quit();
 }
 /**
  * Handles checkbox items on a CheckboxMenu. The Example class has none of
  * its own, but subclasses may have some.
  * 
  * @param menu
  *            which CheckboxMenu needs action
  * @param check
  *            which CheckboxMenu item has changed
  */
 public void checkboxChanged(CheckboxMenu menu, int check) {
   // None for us
 }
 /**
  * Handles on/off checkbox items on a standard menu.
  * 
  * @param event
  *            an ItemEvent indicating what requires handling
  */
 public void itemStateChanged(ItemEvent event) {
   Object src = event.getSource();
   boolean state;
   if (src == headlightMenuItem) {
     state = headlightMenuItem.getState();
     headlight.setEnable(state);
   } else if (src == walkMenuItem)
     setNavigationType(Walk);
   else if (src == examineMenuItem)
     setNavigationType(Examine);
 }
 /**
  * Handles a window closing event notifying the application that the user
  * has chosen to close the application without selecting the "Exit" menu
  * item.
  * 
  * @param event
  *            a WindowEvent indicating the window is closing
  */
 public void windowClosing(WindowEvent event) {
   quit();
 }
 public void windowClosed(WindowEvent event) {
 }
 public void windowOpened(WindowEvent event) {
 }
 public void windowIconified(WindowEvent event) {
 }
 public void windowDeiconified(WindowEvent event) {
 }
 public void windowActivated(WindowEvent event) {
 }
 public void windowDeactivated(WindowEvent event) {
 }
 //  Well known colors, positions, and directions
 public final static Color3f White = new Color3f(1.0f, 1.0f, 1.0f);
 public final static Color3f Gray = new Color3f(0.7f, 0.7f, 0.7f);
 public final static Color3f DarkGray = new Color3f(0.2f, 0.2f, 0.2f);
 public final static Color3f Black = new Color3f(0.0f, 0.0f, 0.0f);
 public final static Color3f Red = new Color3f(1.0f, 0.0f, 0.0f);
 public final static Color3f DarkRed = new Color3f(0.3f, 0.0f, 0.0f);
 public final static Color3f Yellow = new Color3f(1.0f, 1.0f, 0.0f);
 public final static Color3f DarkYellow = new Color3f(0.3f, 0.3f, 0.0f);
 public final static Color3f Green = new Color3f(0.0f, 1.0f, 0.0f);
 public final static Color3f DarkGreen = new Color3f(0.0f, 0.3f, 0.0f);
 public final static Color3f Cyan = new Color3f(0.0f, 1.0f, 1.0f);
 public final static Color3f Blue = new Color3f(0.0f, 0.0f, 1.0f);
 public final static Color3f DarkBlue = new Color3f(0.0f, 0.0f, 0.3f);
 public final static Color3f Magenta = new Color3f(1.0f, 0.0f, 1.0f);
 public final static Vector3f PosX = new Vector3f(1.0f, 0.0f, 0.0f);
 public final static Vector3f NegX = new Vector3f(-1.0f, 0.0f, 0.0f);
 public final static Vector3f PosY = new Vector3f(0.0f, 1.0f, 0.0f);
 public final static Vector3f NegY = new Vector3f(0.0f, -1.0f, 0.0f);
 public final static Vector3f PosZ = new Vector3f(0.0f, 0.0f, 1.0f);
 public final static Vector3f NegZ = new Vector3f(0.0f, 0.0f, -1.0f);
 public final static Point3f Origin = new Point3f(0.0f, 0.0f, 0.0f);
 public final static Point3f PlusX = new Point3f(0.75f, 0.0f, 0.0f);
 public final static Point3f MinusX = new Point3f(-0.75f, 0.0f, 0.0f);
 public final static Point3f PlusY = new Point3f(0.0f, 0.75f, 0.0f);
 public final static Point3f MinusY = new Point3f(0.0f, -0.75f, 0.0f);
 public final static Point3f PlusZ = new Point3f(0.0f, 0.0f, 0.75f);
 public final static Point3f MinusZ = new Point3f(0.0f, 0.0f, -0.75f);

} // //INTERFACE //CheckboxMenuListener - listen for checkbox change events // //DESCRIPTION //The checkboxChanged method is called by users of this class //to notify the listener when a checkbox choice has changed on //a CheckboxMenu class menu. // interface CheckboxMenuListener extends EventListener {

 public void checkboxChanged(CheckboxMenu menu, int check);

} /**

* ExamineViewerBehavior
* 
* @version 1.0, 98/04/16
*/

/**

* Wakeup on mouse button presses, releases, and mouse movements and generate
* transforms in an "examination style" that enables the user to rotate,
* translation, and zoom an object as if it is held at arm"s length. Such an
* examination style is similar to the "Examine" navigation type used by VRML
* browsers.
* 
* The behavior maps mouse drags to different transforms depending upon the
* mosue button held down:
* 
* Button 1 (left) Horizontal movement --> Y-axis rotation Vertical movement -->
* X-axis rotation
* 
* Button 2 (middle) Horizontal movement --> nothing Vertical movement -->
* Z-axis translation
* 
* Button 3 (right) Horizontal movement --> X-axis translation Vertical movement
* --> Y-axis translation
* 
* To support systems with 2 or 1 mouse buttons, the following alternate
* mappings are supported while dragging with any mouse button held down and
* zero or more keyboard modifiers held down:
* 
* No modifiers = Button 1 ALT = Button 2 Meta = Button 3 Control = Button 3
* 
* The behavior automatically modifies a TransformGroup provided to the
* constructor. The TransformGroup"s transform can be set at any time by the
* application or other behaviors to cause the examine rotation and translation
* to be reset.
*/

// This class is inspired by the MouseBehavior, MouseRotate, // MouseTranslate, and MouseZoom utility behaviors provided with // Java 3D. This class differs from those utilities in that it: // // (a) encapsulates all three behaviors into one in order to // enforce a specific "Examine" symantic // // (b) supports set/get of the rotation and translation factors // that control the speed of movement. // // (c) supports the "Control" modifier as an alternative to the // "Meta" modifier not present on PC, Mac, and most non-Sun // keyboards. This makes button3 behavior usable on PCs, // Macs, and other systems with fewer than 3 mouse buttons. class ExamineViewerBehavior extends ViewerBehavior {

 // Previous cursor location
 protected int previousX = 0;
 protected int previousY = 0;
 // Saved standard cursor
 protected Cursor savedCursor = null;
 /**
  * Construct an examine behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into a
  * transform group given later with the setTransformGroup( ) method.
  */
 public ExamineViewerBehavior() {
   super();
 }
 /**
  * Construct an examine behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into a
  * transform group given later with the setTransformGroup( ) method.
  * 
  * @param parent
  *            The AWT Component that contains the area generating mouse
  *            events.
  */
 public ExamineViewerBehavior(Component parent) {
   super(parent);
 }
 /**
  * Construct an examine behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into the
  * given transform group.
  * 
  * @param transformGroup
  *            The transform group to be modified by the behavior.
  */
 public ExamineViewerBehavior(TransformGroup transformGroup) {
   super();
   subjectTransformGroup = transformGroup;
 }
 /**
  * Construct an examine behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into the
  * given transform group.
  * 
  * @param transformGroup
  *            The transform group to be modified by the behavior.
  * @param parent
  *            The AWT Component that contains the area generating mouse
  *            events.
  */
 public ExamineViewerBehavior(TransformGroup transformGroup, Component parent) {
   super(parent);
   subjectTransformGroup = transformGroup;
 }
 /**
  * Respond to a button1 event (press, release, or drag).
  * 
  * @param mouseEvent
  *            A MouseEvent to respond to.
  */
 public void onButton1(MouseEvent mev) {
   if (subjectTransformGroup == null)
     return;
   int x = mev.getX();
   int y = mev.getY();
   if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
     // Mouse button pressed: record position
     previousX = x;
     previousY = y;
     // Change to a "move" cursor
     if (parentComponent != null) {
       savedCursor = parentComponent.getCursor();
       parentComponent.setCursor(Cursor
           .getPredefinedCursor(Cursor.HAND_CURSOR));
     }
     return;
   }
   if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
     // Mouse button released: do nothing
     // Switch the cursor back
     if (parentComponent != null)
       parentComponent.setCursor(savedCursor);
     return;
   }
   //
   // Mouse moved while button down: create a rotation
   //
   // Compute the delta in X and Y from the previous
   // position. Use the delta to compute rotation
   // angles with the mapping:
   //
   //   positive X mouse delta --> positive Y-axis rotation
   //   positive Y mouse delta --> positive X-axis rotation
   //
   // where positive X mouse movement is to the right, and
   // positive Y mouse movement is **down** the screen.
   //
   int deltaX = x - previousX;
   int deltaY = y - previousY;
   if (deltaX > UNUSUAL_XDELTA || deltaX < -UNUSUAL_XDELTA
       || deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {
     // Deltas are too huge to be believable. Probably a glitch.
     // Don"t record the new XY location, or do anything.
     return;
   }
   double xRotationAngle = deltaY * XRotationFactor;
   double yRotationAngle = deltaX * YRotationFactor;
   //
   // Build transforms
   //
   transform1.rotX(xRotationAngle);
   transform2.rotY(yRotationAngle);
   // Get and save the current transform matrix
   subjectTransformGroup.getTransform(currentTransform);
   currentTransform.get(matrix);
   translate.set(matrix.m03, matrix.m13, matrix.m23);
   // Translate to the origin, rotate, then translate back
   currentTransform.setTranslation(origin);
   currentTransform.mul(transform1, currentTransform);
   currentTransform.mul(transform2, currentTransform);
   currentTransform.setTranslation(translate);
   // Update the transform group
   subjectTransformGroup.setTransform(currentTransform);
   previousX = x;
   previousY = y;
 }
 /**
  * Respond to a button2 event (press, release, or drag).
  * 
  * @param mouseEvent
  *            A MouseEvent to respond to.
  */
 public void onButton2(MouseEvent mev) {
   if (subjectTransformGroup == null)
     return;
   int x = mev.getX();
   int y = mev.getY();
   if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
     // Mouse button pressed: record position
     previousX = x;
     previousY = y;
     // Change to a "move" cursor
     if (parentComponent != null) {
       savedCursor = parentComponent.getCursor();
       parentComponent.setCursor(Cursor
           .getPredefinedCursor(Cursor.MOVE_CURSOR));
     }
     return;
   }
   if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
     // Mouse button released: do nothing
     // Switch the cursor back
     if (parentComponent != null)
       parentComponent.setCursor(savedCursor);
     return;
   }
   //
   // Mouse moved while button down: create a translation
   //
   // Compute the delta in Y from the previous
   // position. Use the delta to compute translation
   // distances with the mapping:
   //
   //   positive Y mouse delta --> positive Y-axis translation
   //
   // where positive X mouse movement is to the right, and
   // positive Y mouse movement is **down** the screen.
   //
   int deltaY = y - previousY;
   if (deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {
     // Deltas are too huge to be believable. Probably a glitch.
     // Don"t record the new XY location, or do anything.
     return;
   }
   double zTranslationDistance = deltaY * ZTranslationFactor;
   //
   // Build transforms
   //
   translate.set(0.0, 0.0, zTranslationDistance);
   transform1.set(translate);
   // Get and save the current transform
   subjectTransformGroup.getTransform(currentTransform);
   // Translate as needed
   currentTransform.mul(transform1, currentTransform);
   // Update the transform group
   subjectTransformGroup.setTransform(currentTransform);
   previousX = x;
   previousY = y;
 }
 /**
  * Respond to a button3 event (press, release, or drag).
  * 
  * @param mouseEvent
  *            A MouseEvent to respond to.
  */
 public void onButton3(MouseEvent mev) {
   if (subjectTransformGroup == null)
     return;
   int x = mev.getX();
   int y = mev.getY();
   if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
     // Mouse button pressed: record position
     previousX = x;
     previousY = y;
     // Change to a "move" cursor
     if (parentComponent != null) {
       savedCursor = parentComponent.getCursor();
       parentComponent.setCursor(Cursor
           .getPredefinedCursor(Cursor.MOVE_CURSOR));
     }
     return;
   }
   if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
     // Mouse button released: do nothing
     // Switch the cursor back
     if (parentComponent != null)
       parentComponent.setCursor(savedCursor);
     return;
   }
   //
   // Mouse moved while button down: create a translation
   //
   // Compute the delta in X and Y from the previous
   // position. Use the delta to compute translation
   // distances with the mapping:
   //
   //   positive X mouse delta --> positive X-axis translation
   //   positive Y mouse delta --> negative Y-axis translation
   //
   // where positive X mouse movement is to the right, and
   // positive Y mouse movement is **down** the screen.
   //
   int deltaX = x - previousX;
   int deltaY = y - previousY;
   if (deltaX > UNUSUAL_XDELTA || deltaX < -UNUSUAL_XDELTA
       || deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {
     // Deltas are too huge to be believable. Probably a glitch.
     // Don"t record the new XY location, or do anything.
     return;
   }
   double xTranslationDistance = deltaX * XTranslationFactor;
   double yTranslationDistance = -deltaY * YTranslationFactor;
   //
   // Build transforms
   //
   translate.set(xTranslationDistance, yTranslationDistance, 0.0);
   transform1.set(translate);
   // Get and save the current transform
   subjectTransformGroup.getTransform(currentTransform);
   // Translate as needed
   currentTransform.mul(transform1, currentTransform);
   // Update the transform group
   subjectTransformGroup.setTransform(currentTransform);
   previousX = x;
   previousY = y;
 }
 /**
  * Respond to an elapsed frames event (assuming subclass has set up a wakeup
  * criterion for it).
  * 
  * @param time
  *            A WakeupOnElapsedFrames criterion to respond to.
  */
 public void onElapsedFrames(WakeupOnElapsedFrames timeEvent) {
   // Can"t happen
 }

} /*

* 
* Copyright (c) 1998 David R. Nadeau
*  
*/

/**

* WalkViewerBehavior is a utility class that creates a "walking style"
* navigation symantic.
* 
* The behavior wakes up on mouse button presses, releases, and mouse movements
* and generates transforms in a "walk style" that enables the user to walk
* through a scene, translating and turning about as if they are within the
* scene. Such a walk style is similar to the "Walk" navigation type used by
* VRML browsers.
* <P>
* The behavior maps mouse drags to different transforms depending upon the
* mouse button held down:
*
*
Button 1 (left) *
Horizontal movement --> Y-axis rotation *
Vertical movement --> Z-axis translation * *
Button 2 (middle) *
Horizontal movement --> Y-axis rotation *
Vertical movement --> X-axis rotation * *
Button 3 (right) *
Horizontal movement --> X-axis translation *
Vertical movement --> Y-axis translation *
* 
* To support systems with 2 or 1 mouse buttons, the following alternate
* mappings are supported while dragging with any mouse button held down and
* zero or more keyboard modifiers held down:
*
    *
  • No modifiers = Button 1 *
  • ALT = Button 2 *
  • Meta = Button 3 *
  • Control = Button 3 *
* The behavior automatically modifies a TransformGroup provided to the
* constructor. The TransformGroup"s transform can be set at any time by the
* application or other behaviors to cause the walk rotation and translation to
* be reset.
* <P>
* While a mouse button is down, the behavior automatically changes the cursor
* in a given parent AWT Component. If no parent Component is given, no cursor
* changes are attempted.
* 
* @version 1.0, 98/04/16
* @author David R. Nadeau, San Diego Supercomputer Center
*/

class WalkViewerBehavior extends ViewerBehavior {

 // This class is inspired by the MouseBehavior, MouseRotate,
 // MouseTranslate, and MouseZoom utility behaviors provided with
 // Java 3D. This class differs from those utilities in that it:
 //
 //    (a) encapsulates all three behaviors into one in order to
 //        enforce a specific "Walk" symantic
 //
 //    (b) supports set/get of the rotation and translation factors
 //        that control the speed of movement.
 //
 //    (c) supports the "Control" modifier as an alternative to the
 //        "Meta" modifier not present on PC, Mac, and most non-Sun
 //        keyboards. This makes button3 behavior usable on PCs,
 //        Macs, and other systems with fewer than 3 mouse buttons.
 // Previous and initial cursor locations
 protected int previousX = 0;
 protected int previousY = 0;
 protected int initialX = 0;
 protected int initialY = 0;
 // Deadzone size (delta from initial XY for which no
 // translate or rotate action is taken
 protected static final int DELTAX_DEADZONE = 10;
 protected static final int DELTAY_DEADZONE = 10;
 // Keep a set of wakeup criterion for animation-generated
 // event types.
 protected WakeupCriterion[] mouseAndAnimationEvents = null;
 protected WakeupOr mouseAndAnimationCriterion = null;
 protected WakeupOr savedMouseCriterion = null;
 // Saved standard cursor
 protected Cursor savedCursor = null;
 /**
  * Default Rotation and translation scaling factors for animated movements
  * (Button 1 press).
  */
 public static final double DEFAULT_YROTATION_ANIMATION_FACTOR = 0.0002;
 public static final double DEFAULT_ZTRANSLATION_ANIMATION_FACTOR = 0.01;
 protected double YRotationAnimationFactor = DEFAULT_YROTATION_ANIMATION_FACTOR;
 protected double ZTranslationAnimationFactor = DEFAULT_ZTRANSLATION_ANIMATION_FACTOR;
 /**
  * Constructs a new walk behavior that converts mouse actions into rotations
  * and translations. Rotations and translations are written into a
  * TransformGroup that must be set using the setTransformGroup method. The
  * cursor will be changed during mouse actions if the parent frame is set
  * using the setParentComponent method.
  * 
  * @return a new WalkViewerBehavior that needs its TransformGroup and parent
  *         Component set
  */
 public WalkViewerBehavior() {
   super();
 }
 /**
  * Constructs a new walk behavior that converts mouse actions into rotations
  * and translations. Rotations and translations are written into a
  * TransformGroup that must be set using the setTransformGroup method. The
  * cursor will be changed within the given AWT parent Component during mouse
  * drags.
  * 
  * @param parent
  *            a parent AWT Component within which the cursor will change
  *            during mouse drags
  * 
  * @return a new WalkViewerBehavior that needs its TransformGroup and parent
  *         Component set
  */
 public WalkViewerBehavior(Component parent) {
   super(parent);
 }
 /**
  * Constructs a new walk behavior that converts mouse actions into rotations
  * and translations. Rotations and translations are written into the given
  * TransformGroup. The cursor will be changed during mouse actions if the
  * parent frame is set using the setParentComponent method.
  * 
  * @param transformGroup
  *            a TransformGroup whos transform is read and written by the
  *            behavior
  * 
  * @return a new WalkViewerBehavior that needs its TransformGroup and parent
  *         Component set
  */
 public WalkViewerBehavior(TransformGroup transformGroup) {
   super();
   subjectTransformGroup = transformGroup;
 }
 /**
  * Constructs a new walk behavior that converts mouse actions into rotations
  * and translations. Rotations and translations are written into the given
  * TransformGroup. The cursor will be changed within the given AWT parent
  * Component during mouse drags.
  * 
  * @param transformGroup
  *            a TransformGroup whos transform is read and written by the
  *            behavior
  * 
  * @param parent
  *            a parent AWT Component within which the cursor will change
  *            during mouse drags
  * 
  * @return a new WalkViewerBehavior that needs its TransformGroup and parent
  *         Component set
  */
 public WalkViewerBehavior(TransformGroup transformGroup, Component parent) {
   super(parent);
   subjectTransformGroup = transformGroup;
 }
 /**
  * Initializes the behavior.
  */
 public void initialize() {
   super.initialize();
   savedMouseCriterion = mouseCriterion; // from parent class
   mouseAndAnimationEvents = new WakeupCriterion[4];
   mouseAndAnimationEvents[0] = new WakeupOnAWTEvent(
       MouseEvent.MOUSE_DRAGGED);
   mouseAndAnimationEvents[1] = new WakeupOnAWTEvent(
       MouseEvent.MOUSE_PRESSED);
   mouseAndAnimationEvents[2] = new WakeupOnAWTEvent(
       MouseEvent.MOUSE_RELEASED);
   mouseAndAnimationEvents[3] = new WakeupOnElapsedFrames(0);
   mouseAndAnimationCriterion = new WakeupOr(mouseAndAnimationEvents);
   // Don"t use the above criterion until a button 1 down event
 }
 /**
  * Sets the Y rotation animation scaling factor for Y-axis rotations. This
  * scaling factor is used to control the speed of Y rotation when button 1
  * is pressed and dragged.
  * 
  * @param factor
  *            the double Y rotation scaling factor
  */
 public void setYRotationAnimationFactor(double factor) {
   YRotationAnimationFactor = factor;
 }
 /**
  * Gets the current Y animation rotation scaling factor for Y-axis
  * rotations.
  * 
  * @return the double Y rotation scaling factor
  */
 public double getYRotationAnimationFactor() {
   return YRotationAnimationFactor;
 }
 /**
  * Sets the Z animation translation scaling factor for Z-axis translations.
  * This scaling factor is used to control the speed of Z translation when
  * button 1 is pressed and dragged.
  * 
  * @param factor
  *            the double Z translation scaling factor
  */
 public void setZTranslationAnimationFactor(double factor) {
   ZTranslationAnimationFactor = factor;
 }
 /**
  * Gets the current Z animation translation scaling factor for Z-axis
  * translations.
  * 
  * @return the double Z translation scaling factor
  */
 public double getZTranslationAnimationFactor() {
   return ZTranslationAnimationFactor;
 }
 /**
  * Responds to an elapsed frames event. Such an event is generated on every
  * frame while button 1 is held down. On each call, this method computes new
  * Y-axis rotation and Z-axis translation values and writes them to the
  * behavior"s TransformGroup. The translation and rotation amounts are
  * computed based upon the distance between the current cursor location and
  * the cursor location when button 1 was pressed. As this distance
  * increases, the translation or rotation amount increases.
  * 
  * @param time
  *            the WakeupOnElapsedFrames criterion to respond to
  */
 public void onElapsedFrames(WakeupOnElapsedFrames timeEvent) {
   //
   // Time elapsed while button down: create a rotation and
   // a translation.
   //
   // Compute the delta in X and Y from the initial position to
   // the previous position. Multiply the delta times a scaling
   // factor to compute an offset to add to the current translation
   // and rotation. Use the mapping:
   //
   //   positive X mouse delta --> negative Y-axis rotation
   //   positive Y mouse delta --> positive Z-axis translation
   //
   // where positive X mouse movement is to the right, and
   // positive Y mouse movement is **down** the screen.
   //
   if (buttonPressed != BUTTON1)
     return;
   int deltaX = previousX - initialX;
   int deltaY = previousY - initialY;
   double yRotationAngle = -deltaX * YRotationAnimationFactor;
   double zTranslationDistance = deltaY * ZTranslationAnimationFactor;
   //
   // Build transforms
   //
   transform1.rotY(yRotationAngle);
   translate.set(0.0, 0.0, zTranslationDistance);
   // Get and save the current transform matrix
   subjectTransformGroup.getTransform(currentTransform);
   currentTransform.get(matrix);
   // Translate to the origin, rotate, then translate back
   currentTransform.setTranslation(origin);
   currentTransform.mul(transform1, currentTransform);
   // Translate back from the origin by the original translation
   // distance, plus the new walk translation... but force walk
   // to travel on a plane by ignoring the Y component of a
   // transformed translation vector.
   currentTransform.transform(translate);
   translate.x += matrix.m03; // add in existing X translation
   translate.y = matrix.m13; // use Y translation
   translate.z += matrix.m23; // add in existing Z translation
   currentTransform.setTranslation(translate);
   // Update the transform group
   subjectTransformGroup.setTransform(currentTransform);
 }
 /**
  * Responds to a button1 event (press, release, or drag). On a press, the
  * method adds a wakeup criterion to the behavior"s set, callling for the
  * behavior to be awoken on each frame. On a button prelease, this criterion
  * is removed from the set.
  * 
  * @param mouseEvent
  *            the MouseEvent to respond to
  */
 public void onButton1(MouseEvent mev) {
   if (subjectTransformGroup == null)
     return;
   int x = mev.getX();
   int y = mev.getY();
   if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
     // Mouse button pressed: record position and change
     // the wakeup criterion to include elapsed time wakeups
     // so we can animate.
     previousX = x;
     previousY = y;
     initialX = x;
     initialY = y;
     // Swap criterion... parent class will not reschedule us
     mouseCriterion = mouseAndAnimationCriterion;
     // Change to a "move" cursor
     if (parentComponent != null) {
       savedCursor = parentComponent.getCursor();
       parentComponent.setCursor(Cursor
           .getPredefinedCursor(Cursor.HAND_CURSOR));
     }
     return;
   }
   if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
     // Mouse button released: restore original wakeup
     // criterion which only includes mouse activity, not
     // elapsed time
     mouseCriterion = savedMouseCriterion;
     // Switch the cursor back
     if (parentComponent != null)
       parentComponent.setCursor(savedCursor);
     return;
   }
   previousX = x;
   previousY = y;
 }
 /**
  * Responds to a button2 event (press, release, or drag). On a press, the
  * method records the initial cursor location. On a drag, the difference
  * between the current and previous cursor location provides a delta that
  * controls the amount by which to rotate in X and Y.
  * 
  * @param mouseEvent
  *            the MouseEvent to respond to
  */
 public void onButton2(MouseEvent mev) {
   if (subjectTransformGroup == null)
     return;
   int x = mev.getX();
   int y = mev.getY();
   if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
     // Mouse button pressed: record position
     previousX = x;
     previousY = y;
     initialX = x;
     initialY = y;
     // Change to a "rotate" cursor
     if (parentComponent != null) {
       savedCursor = parentComponent.getCursor();
       parentComponent.setCursor(Cursor
           .getPredefinedCursor(Cursor.MOVE_CURSOR));
     }
     return;
   }
   if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
     // Mouse button released: do nothing
     // Switch the cursor back
     if (parentComponent != null)
       parentComponent.setCursor(savedCursor);
     return;
   }
   //
   // Mouse moved while button down: create a rotation
   //
   // Compute the delta in X and Y from the previous
   // position. Use the delta to compute rotation
   // angles with the mapping:
   //
   //   positive X mouse delta --> negative Y-axis rotation
   //   positive Y mouse delta --> negative X-axis rotation
   //
   // where positive X mouse movement is to the right, and
   // positive Y mouse movement is **down** the screen.
   //
   int deltaX = x - previousX;
   int deltaY = 0;
   if (Math.abs(y - initialY) > DELTAY_DEADZONE) {
     // Cursor has moved far enough vertically to consider
     // it intentional, so get it"s delta.
     deltaY = y - previousY;
   }
   if (deltaX > UNUSUAL_XDELTA || deltaX < -UNUSUAL_XDELTA
       || deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {
     // Deltas are too huge to be believable. Probably a glitch.
     // Don"t record the new XY location, or do anything.
     return;
   }
   double xRotationAngle = -deltaY * XRotationFactor;
   double yRotationAngle = -deltaX * YRotationFactor;
   //
   // Build transforms
   //
   transform1.rotX(xRotationAngle);
   transform2.rotY(yRotationAngle);
   // Get and save the current transform matrix
   subjectTransformGroup.getTransform(currentTransform);
   currentTransform.get(matrix);
   translate.set(matrix.m03, matrix.m13, matrix.m23);
   // Translate to the origin, rotate, then translate back
   currentTransform.setTranslation(origin);
   currentTransform.mul(transform2, currentTransform);
   currentTransform.mul(transform1);
   currentTransform.setTranslation(translate);
   // Update the transform group
   subjectTransformGroup.setTransform(currentTransform);
   previousX = x;
   previousY = y;
 }
 /**
  * Responds to a button3 event (press, release, or drag). On a drag, the
  * difference between the current and previous cursor location provides a
  * delta that controls the amount by which to translate in X and Y.
  * 
  * @param mouseEvent
  *            the MouseEvent to respond to
  */
 public void onButton3(MouseEvent mev) {
   if (subjectTransformGroup == null)
     return;
   int x = mev.getX();
   int y = mev.getY();
   if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
     // Mouse button pressed: record position
     previousX = x;
     previousY = y;
     // Change to a "move" cursor
     if (parentComponent != null) {
       savedCursor = parentComponent.getCursor();
       parentComponent.setCursor(Cursor
           .getPredefinedCursor(Cursor.MOVE_CURSOR));
     }
     return;
   }
   if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
     // Mouse button released: do nothing
     // Switch the cursor back
     if (parentComponent != null)
       parentComponent.setCursor(savedCursor);
     return;
   }
   //
   // Mouse moved while button down: create a translation
   //
   // Compute the delta in X and Y from the previous
   // position. Use the delta to compute translation
   // distances with the mapping:
   //
   //   positive X mouse delta --> positive X-axis translation
   //   positive Y mouse delta --> negative Y-axis translation
   //
   // where positive X mouse movement is to the right, and
   // positive Y mouse movement is **down** the screen.
   //
   int deltaX = x - previousX;
   int deltaY = y - previousY;
   if (deltaX > UNUSUAL_XDELTA || deltaX < -UNUSUAL_XDELTA
       || deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {
     // Deltas are too huge to be believable. Probably a glitch.
     // Don"t record the new XY location, or do anything.
     return;
   }
   double xTranslationDistance = deltaX * XTranslationFactor;
   double yTranslationDistance = -deltaY * YTranslationFactor;
   //
   // Build transforms
   //
   translate.set(xTranslationDistance, yTranslationDistance, 0.0);
   transform1.set(translate);
   // Get and save the current transform
   subjectTransformGroup.getTransform(currentTransform);
   // Translate as needed
   currentTransform.mul(transform1);
   // Update the transform group
   subjectTransformGroup.setTransform(currentTransform);
   previousX = x;
   previousY = y;
 }

} // //CLASS //CheckboxMenu - build a menu of grouped checkboxes // //DESCRIPTION //The class creates a menu with one or more CheckboxMenuItem"s //and monitors that menu. When a menu checkbox is picked, the //previous one is turned off (in radio-button style). Then, //a given listener"s checkboxChanged method is called, passing it //the menu and the item checked. // class CheckboxMenu extends Menu implements ItemListener {

 // State
 protected CheckboxMenuItem[] checks = null;
 protected int current = 0;
 protected CheckboxMenuListener listener = null;
 //  Construct
 public CheckboxMenu(String name, NameValue[] items,
     CheckboxMenuListener listen) {
   this(name, items, 0, listen);
 }
 public CheckboxMenu(String name, NameValue[] items, int cur,
     CheckboxMenuListener listen) {
   super(name);
   current = cur;
   listener = listen;
   if (items == null)
     return;
   checks = new CheckboxMenuItem[items.length];
   for (int i = 0; i < items.length; i++) {
     checks[i] = new CheckboxMenuItem(items[i].name, false);
     checks[i].addItemListener(this);
     add(checks[i]);
   }
   checks[cur].setState(true);
 }
 //  Handle checkbox changed events
 public void itemStateChanged(ItemEvent event) {
   Object src = event.getSource();
   for (int i = 0; i < checks.length; i++) {
     if (src == checks[i]) {
       // Update the checkboxes
       checks[current].setState(false);
       current = i;
       checks[current].setState(true);
       if (listener != null)
         listener.checkboxChanged(this, i);
       return;
     }
   }
 }
 // Methods to get and set state
 public int getCurrent() {
   return current;
 }
 public void setCurrent(int cur) {
   if (cur < 0 || cur >= checks.length)
     return; // ignore out of range choices
   if (checks == null)
     return;
   checks[current].setState(false);
   current = cur;
   checks[current].setState(true);
 }
 public CheckboxMenuItem getSelectedCheckbox() {
   if (checks == null)
     return null;
   return checks[current];
 }
 public void setSelectedCheckbox(CheckboxMenuItem item) {
   if (checks == null)
     return;
   for (int i = 0; i < checks.length; i++) {
     if (item == checks[i]) {
       checks[i].setState(false);
       current = i;
       checks[i].setState(true);
     }
   }
 }

} /**

* ViewerBehavior
* 
* @version 1.0, 98/04/16
*/

/**

* Wakeup on mouse button presses, releases, and mouse movements and generate
* transforms for a transform group. Classes that extend this class impose
* specific symantics, such as "Examine" or "Walk" viewing, similar to the
* navigation types used by VRML browsers.
* 
* To support systems with 2 or 1 mouse buttons, the following alternate
* mappings are supported while dragging with any mouse button held down and
* zero or more keyboard modifiers held down:
* 
* No modifiers = Button 1 ALT = Button 2 Meta = Button 3 Control = Button 3
* 
* The behavior automatically modifies a TransformGroup provided to the
* constructor. The TransformGroup"s transform can be set at any time by the
* application or other behaviors to cause the viewer"s rotation and translation
* to be reset.
*/

// This class is inspired by the MouseBehavior, MouseRotate, // MouseTranslate, and MouseZoom utility behaviors provided with // Java 3D. This class differs from those utilities in that it: // // (a) encapsulates all three behaviors into one in order to // enforce a specific viewing symantic // // (b) supports set/get of the rotation and translation factors // that control the speed of movement. // // (c) supports the "Control" modifier as an alternative to the // "Meta" modifier not present on PC, Mac, and most non-Sun // keyboards. This makes button3 behavior usable on PCs, // Macs, and other systems with fewer than 3 mouse buttons. abstract class ViewerBehavior extends Behavior {

 // Keep track of the transform group who"s transform we modify
 // during mouse motion.
 protected TransformGroup subjectTransformGroup = null;
 // Keep a set of wakeup criterion for different mouse-generated
 // event types.
 protected WakeupCriterion[] mouseEvents = null;
 protected WakeupOr mouseCriterion = null;
 // Track which button was last pressed
 protected static final int BUTTONNONE = -1;
 protected static final int BUTTON1 = 0;
 protected static final int BUTTON2 = 1;
 protected static final int BUTTON3 = 2;
 protected int buttonPressed = BUTTONNONE;
 // Keep a few Transform3Ds for use during event processing. This
 // avoids having to allocate new ones on each event.
 protected Transform3D currentTransform = new Transform3D();
 protected Transform3D transform1 = new Transform3D();
 protected Transform3D transform2 = new Transform3D();
 protected Matrix4d matrix = new Matrix4d();
 protected Vector3d origin = new Vector3d(0.0, 0.0, 0.0);
 protected Vector3d translate = new Vector3d(0.0, 0.0, 0.0);
 // Unusual X and Y delta limits.
 protected static final int UNUSUAL_XDELTA = 400;
 protected static final int UNUSUAL_YDELTA = 400;
 protected Component parentComponent = null;
 /**
  * Construct a viewer behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into a
  * transform group given later with the setTransformGroup( ) method.
  */
 public ViewerBehavior() {
   super();
 }
 /**
  * Construct a viewer behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into a
  * transform group given later with the setTransformGroup( ) method.
  * 
  * @param parent
  *            The AWT Component that contains the area generating mouse
  *            events.
  */
 public ViewerBehavior(Component parent) {
   super();
   parentComponent = parent;
 }
 /**
  * Construct a viewer behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into the
  * given transform group.
  * 
  * @param transformGroup
  *            The transform group to be modified by the behavior.
  */
 public ViewerBehavior(TransformGroup transformGroup) {
   super();
   subjectTransformGroup = transformGroup;
 }
 /**
  * Construct a viewer behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into the
  * given transform group.
  * 
  * @param transformGroup
  *            The transform group to be modified by the behavior.
  * @param parent
  *            The AWT Component that contains the area generating mouse
  *            events.
  */
 public ViewerBehavior(TransformGroup transformGroup, Component parent) {
   super();
   subjectTransformGroup = transformGroup;
   parentComponent = parent;
 }
 /**
  * Set the transform group modified by the viewer behavior. Setting the
  * transform group to null disables the behavior until the transform group
  * is again set to an existing group.
  * 
  * @param transformGroup
  *            The new transform group to be modified by the behavior.
  */
 public void setTransformGroup(TransformGroup transformGroup) {
   subjectTransformGroup = transformGroup;
 }
 /**
  * Get the transform group modified by the viewer behavior.
  */
 public TransformGroup getTransformGroup() {
   return subjectTransformGroup;
 }
 /**
  * Sets the parent component who"s cursor will be changed during mouse
  * drags. If no component is given is given to the constructor, or set via
  * this method, no cursor changes will be done.
  * 
  * @param parent
  *            the AWT Component, such as a Frame, within which cursor
  *            changes should take place during mouse drags
  */
 public void setParentComponent(Component parent) {
   parentComponent = parent;
 }
 /*
  * Gets the parent frame within which the cursor changes during mouse drags.
  * 
  * @return the AWT Component, such as a Frame, within which cursor changes
  * should take place during mouse drags. Returns null if no parent is set.
  */
 public Component getParentComponent() {
   return parentComponent;
 }
 /**
  * Initialize the behavior.
  */
 public void initialize() {
   // Wakeup when the mouse is dragged or when a mouse button
   // is pressed or released.
   mouseEvents = new WakeupCriterion[3];
   mouseEvents[0] = new WakeupOnAWTEvent(MouseEvent.MOUSE_DRAGGED);
   mouseEvents[1] = new WakeupOnAWTEvent(MouseEvent.MOUSE_PRESSED);
   mouseEvents[2] = new WakeupOnAWTEvent(MouseEvent.MOUSE_RELEASED);
   mouseCriterion = new WakeupOr(mouseEvents);
   wakeupOn(mouseCriterion);
 }
 /**
  * Process a new wakeup. Interpret mouse button presses, releases, and mouse
  * drags.
  * 
  * @param criteria
  *            The wakeup criteria causing the behavior wakeup.
  */
 public void processStimulus(Enumeration criteria) {
   WakeupCriterion wakeup = null;
   AWTEvent[] event = null;
   int whichButton = BUTTONNONE;
   // Process all pending wakeups
   while (criteria.hasMoreElements()) {
     wakeup = (WakeupCriterion) criteria.nextElement();
     if (wakeup instanceof WakeupOnAWTEvent) {
       event = ((WakeupOnAWTEvent) wakeup).getAWTEvent();
       // Process all pending events
       for (int i = 0; i < event.length; i++) {
         if (event[i].getID() != MouseEvent.MOUSE_PRESSED
             && event[i].getID() != MouseEvent.MOUSE_RELEASED
             && event[i].getID() != MouseEvent.MOUSE_DRAGGED)
           // Ignore uninteresting mouse events
           continue;
         //
         // Regretably, Java event handling (or perhaps
         // underlying OS event handling) doesn"t always
         // catch button bounces (redundant presses and
         // releases), or order events so that the last
         // drag event is delivered before a release.
         // This means we can get stray events that we
         // filter out here.
         //
         if (event[i].getID() == MouseEvent.MOUSE_PRESSED
             && buttonPressed != BUTTONNONE)
           // Ignore additional button presses until a release
           continue;
         if (event[i].getID() == MouseEvent.MOUSE_RELEASED
             && buttonPressed == BUTTONNONE)
           // Ignore additional button releases until a press
           continue;
         if (event[i].getID() == MouseEvent.MOUSE_DRAGGED
             && buttonPressed == BUTTONNONE)
           // Ignore drags until a press
           continue;
         MouseEvent mev = (MouseEvent) event[i];
         int modifiers = mev.getModifiers();
         //
         // Unfortunately, the underlying event handling
         // doesn"t do a "grab" operation when a mouse button
         // is pressed. This means that once a button is
         // pressed, if another mouse button or a keyboard
         // modifier key is pressed, the delivered mouse event
         // will show that a different button is being held
         // down. For instance:
         //
         // Action Event
         //  Button 1 press Button 1 press
         //  Drag with button 1 down Button 1 drag
         //  ALT press -
         //  Drag with ALT & button 1 down Button 2 drag
         //  Button 1 release Button 2 release
         //
         // The upshot is that we can get a button press
         // without a matching release, and the button
         // associated with a drag can change mid-drag.
         //
         // To fix this, we watch for an initial button
         // press, and thenceforth consider that button
         // to be the one held down, even if additional
         // buttons get pressed, and despite what is
         // reported in the event. Only when a button is
         // released, do we end such a grab.
         //
         if (buttonPressed == BUTTONNONE) {
           // No button is pressed yet, figure out which
           // button is down now and how to direct events
           if (((modifiers & InputEvent.BUTTON3_MASK) != 0)
               || (((modifiers & InputEvent.BUTTON1_MASK) != 0) && ((modifiers & InputEvent.CTRL_MASK) == InputEvent.CTRL_MASK))) {
             // Button 3 activity (META or CTRL down)
             whichButton = BUTTON3;
           } else if ((modifiers & InputEvent.BUTTON2_MASK) != 0) {
             // Button 2 activity (ALT down)
             whichButton = BUTTON2;
           } else {
             // Button 1 activity (no modifiers down)
             whichButton = BUTTON1;
           }
           // If the event is to press a button, then
           // record that that button is now down
           if (event[i].getID() == MouseEvent.MOUSE_PRESSED)
             buttonPressed = whichButton;
         } else {
           // Otherwise a button was pressed earlier and
           // hasn"t been released yet. Assign all further
           // events to it, even if ALT, META, CTRL, or
           // another button has been pressed as well.
           whichButton = buttonPressed;
         }
         // Distribute the event
         switch (whichButton) {
         case BUTTON1:
           onButton1(mev);
           break;
         case BUTTON2:
           onButton2(mev);
           break;
         case BUTTON3:
           onButton3(mev);
           break;
         default:
           break;
         }
         // If the event is to release a button, then
         // record that that button is now up
         if (event[i].getID() == MouseEvent.MOUSE_RELEASED)
           buttonPressed = BUTTONNONE;
       }
       continue;
     }
     if (wakeup instanceof WakeupOnElapsedFrames) {
       onElapsedFrames((WakeupOnElapsedFrames) wakeup);
       continue;
     }
   }
   // Reschedule us for another wakeup
   wakeupOn(mouseCriterion);
 }
 /**
  * Default X and Y rotation factors, and XYZ translation factors.
  */
 public static final double DEFAULT_XROTATION_FACTOR = 0.02;
 public static final double DEFAULT_YROTATION_FACTOR = 0.005;
 public static final double DEFAULT_XTRANSLATION_FACTOR = 0.02;
 public static final double DEFAULT_YTRANSLATION_FACTOR = 0.02;
 public static final double DEFAULT_ZTRANSLATION_FACTOR = 0.04;
 protected double XRotationFactor = DEFAULT_XROTATION_FACTOR;
 protected double YRotationFactor = DEFAULT_YROTATION_FACTOR;
 protected double XTranslationFactor = DEFAULT_XTRANSLATION_FACTOR;
 protected double YTranslationFactor = DEFAULT_YTRANSLATION_FACTOR;
 protected double ZTranslationFactor = DEFAULT_ZTRANSLATION_FACTOR;
 /**
  * Set the X rotation scaling factor for X-axis rotations.
  * 
  * @param factor
  *            The new scaling factor.
  */
 public void setXRotationFactor(double factor) {
   XRotationFactor = factor;
 }
 /**
  * Get the current X rotation scaling factor for X-axis rotations.
  */
 public double getXRotationFactor() {
   return XRotationFactor;
 }
 /**
  * Set the Y rotation scaling factor for Y-axis rotations.
  * 
  * @param factor
  *            The new scaling factor.
  */
 public void setYRotationFactor(double factor) {
   YRotationFactor = factor;
 }
 /**
  * Get the current Y rotation scaling factor for Y-axis rotations.
  */
 public double getYRotationFactor() {
   return YRotationFactor;
 }
 /**
  * Set the X translation scaling factor for X-axis translations.
  * 
  * @param factor
  *            The new scaling factor.
  */
 public void setXTranslationFactor(double factor) {
   XTranslationFactor = factor;
 }
 /**
  * Get the current X translation scaling factor for X-axis translations.
  */
 public double getXTranslationFactor() {
   return XTranslationFactor;
 }
 /**
  * Set the Y translation scaling factor for Y-axis translations.
  * 
  * @param factor
  *            The new scaling factor.
  */
 public void setYTranslationFactor(double factor) {
   YTranslationFactor = factor;
 }
 /**
  * Get the current Y translation scaling factor for Y-axis translations.
  */
 public double getYTranslationFactor() {
   return YTranslationFactor;
 }
 /**
  * Set the Z translation scaling factor for Z-axis translations.
  * 
  * @param factor
  *            The new scaling factor.
  */
 public void setZTranslationFactor(double factor) {
   ZTranslationFactor = factor;
 }
 /**
  * Get the current Z translation scaling factor for Z-axis translations.
  */
 public double getZTranslationFactor() {
   return ZTranslationFactor;
 }
 /**
  * Respond to a button1 event (press, release, or drag).
  * 
  * @param mouseEvent
  *            A MouseEvent to respond to.
  */
 public abstract void onButton1(MouseEvent mouseEvent);
 /**
  * Respond to a button2 event (press, release, or drag).
  * 
  * @param mouseEvent
  *            A MouseEvent to respond to.
  */
 public abstract void onButton2(MouseEvent mouseEvent);
 /**
  * Responed to a button3 event (press, release, or drag).
  * 
  * @param mouseEvent
  *            A MouseEvent to respond to.
  */
 public abstract void onButton3(MouseEvent mouseEvent);
 /**
  * Respond to an elapsed frames event (assuming subclass has set up a wakeup
  * criterion for it).
  * 
  * @param time
  *            A WakeupOnElapsedFrames criterion to respond to.
  */
 public abstract void onElapsedFrames(WakeupOnElapsedFrames timeEvent);

} // //CLASS //NameValue - create a handy name-value pair // //DESCRIPTION //It is frequently handy to have one or more name-value pairs //with which to store named colors, named positions, named textures, //and so forth. Several of the examples use this class. // //AUTHOR //David R. Nadeau / San Diego Supercomputer Center // class NameValue {

 public String name;
 public Object value;
 public NameValue(String n, Object v) {
   name = n;
   value = v;
 }

}


      </source>
   
  
 
  



ExLightScope - illustrate use of light scope groups

   <source lang="java">

// //CLASS //ExLightScope - illustrate use of light scope groups // //LESSON //Add three DirectionalLight nodes to illuminate a scene, selecting //one of them to use at a time. Each light has a different scope //group list, and thus illuminates different parts of the scene. // //SEE ALSO //ExAmbientLight //ExPointLight //ExSpotLight //ExLightBounds // //AUTHOR //David R. Nadeau / San Diego Supercomputer Center // import java.applet.Applet; import java.awt.AWTEvent; import java.awt.BorderLayout; import java.awt.CheckboxMenuItem; import java.awt.ruponent; import java.awt.Cursor; import java.awt.Frame; import java.awt.Menu; import java.awt.MenuBar; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.InputEvent; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.event.MouseEvent; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.io.File; import java.util.Enumeration; import java.util.EventListener; 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.Group; import javax.media.j3d.Light; import javax.media.j3d.Material; import javax.media.j3d.Transform3D; import javax.media.j3d.TransformGroup; import javax.media.j3d.WakeupCriterion; import javax.media.j3d.WakeupOnAWTEvent; import javax.media.j3d.WakeupOnElapsedFrames; import javax.media.j3d.WakeupOr; import javax.vecmath.Color3f; import javax.vecmath.Matrix4d; import javax.vecmath.Point3d; import javax.vecmath.Point3f; import javax.vecmath.Vector3d; import javax.vecmath.Vector3f; import com.sun.j3d.utils.geometry.Primitive; import com.sun.j3d.utils.geometry.Sphere; import com.sun.j3d.utils.universe.PlatformGeometry; import com.sun.j3d.utils.universe.SimpleUniverse; import com.sun.j3d.utils.universe.Viewer; import com.sun.j3d.utils.universe.ViewingPlatform; public class ExLightScope extends Java3DFrame {

 //--------------------------------------------------------------
 //  SCENE CONTENT
 //--------------------------------------------------------------
 //
 //  Nodes (updated via menu)
 //
 private DirectionalLight light1 = null;
 private DirectionalLight light2 = null;
 private DirectionalLight light3 = null;
 private Group content1 = null;
 private Group content2 = null;
 //
 //  Build scene
 //
 public Group buildScene() {
   // Turn off the example headlight
   setHeadlightEnable(false);
   // Build the scene group
   Group scene = new Group();
   // Build foreground geometry into two groups. We"ll
   // create three directional lights below, one each with
   // scope to cover the first geometry group only, the
   // second geometry group only, or both geometry groups.
   content1 = new SphereGroup(0.25f, // radius of spheres
       1.5f, // x spacing
       0.75f, // y spacing
       3, // number of spheres in X
       5, // number of spheres in Y
       null); // appearance
   scene.addChild(content1);
   content2 = new SphereGroup(0.25f, // radius of spheres
       1.5f, // x spacing
       0.75f, // y spacing
       2, // number of spheres in X
       5, // number of spheres in Y
       null); // appearance
   scene.addChild(content2);
   // BEGIN EXAMPLE TOPIC
   // Create influencing bounds
   BoundingSphere worldBounds = new BoundingSphere(new Point3d(0.0, 0.0,
       0.0), // Center
       1000.0); // Extent
   // Add three directional lights whose scopes are set
   // to cover one, the other, or both of the shape groups
   // above. Also set the lights" color and aim direction.
   // Light #1 with content1 scope
   light1 = new DirectionalLight();
   light1.setEnable(light1OnOff);
   light1.setColor(Red);
   light1.setDirection(new Vector3f(1.0f, 0.0f, -1.0f));
   light1.setInfluencingBounds(worldBounds);
   light1.addScope(content1);
   light1.setCapability(Light.ALLOW_STATE_WRITE);
   scene.addChild(light1);
   // Light #2 with content2 scope
   light2 = new DirectionalLight();
   light2.setEnable(light2OnOff);
   light2.setColor(Blue);
   light2.setDirection(new Vector3f(1.0f, 0.0f, -1.0f));
   light2.setInfluencingBounds(worldBounds);
   light2.addScope(content2);
   light2.setCapability(Light.ALLOW_STATE_WRITE);
   scene.addChild(light2);
   // Light #3 with universal scope (the default)
   light3 = new DirectionalLight();
   light3.setEnable(light3OnOff);
   light3.setColor(White);
   light3.setDirection(new Vector3f(1.0f, 0.0f, -1.0f));
   light3.setInfluencingBounds(worldBounds);
   light3.setCapability(Light.ALLOW_STATE_WRITE);
   scene.addChild(light3);
   // Add an ambient light to dimly illuminate the rest of
   // the shapes in the scene to help illustrate that the
   // directional lights are being scoped... otherwise it looks
   // like we"re just removing shapes from the scene
   AmbientLight ambient = new AmbientLight();
   ambient.setEnable(true);
   ambient.setColor(White);
   ambient.setInfluencingBounds(worldBounds);
   scene.addChild(ambient);
   // END EXAMPLE TOPIC
   return scene;
 }
 //--------------------------------------------------------------
 //  USER INTERFACE
 //--------------------------------------------------------------
 //
 //  Main
 //
 public static void main(String[] args) {
   ExLightScope ex = new ExLightScope();
   ex.initialize(args);
   ex.buildUniverse();
   ex.showFrame();
 }
 //  Menu choices
 private boolean light1OnOff = true;
 private CheckboxMenuItem light1OnOffMenu;
 private boolean light2OnOff = true;
 private CheckboxMenuItem light2OnOffMenu;
 private boolean light3OnOff = false;
 private CheckboxMenuItem light3OnOffMenu;
 //
 //  Initialize the GUI (application and applet)
 //
 public void initialize(String[] args) {
   // Initialize the window, menubar, etc.
   super.initialize(args);
   exampleFrame.setTitle("Java 3D Light Scoping Example");
   //
   //  Add a menubar menu to change node parameters
   //    Use bounding leaf
   //
   Menu m = new Menu("DirectionalLights");
   light1OnOffMenu = new CheckboxMenuItem(
       "Red light with sphere set 1 scope", light1OnOff);
   light1OnOffMenu.addItemListener(this);
   m.add(light1OnOffMenu);
   light2OnOffMenu = new CheckboxMenuItem(
       "Blue light with sphere set 2 scope", light2OnOff);
   light2OnOffMenu.addItemListener(this);
   m.add(light2OnOffMenu);
   light3OnOffMenu = new CheckboxMenuItem(
       "White light with universal scope", light3OnOff);
   light3OnOffMenu.addItemListener(this);
   m.add(light3OnOffMenu);
   exampleMenuBar.add(m);
 }
 //
 //  Handle checkboxes and menu choices
 //
 public void itemStateChanged(ItemEvent event) {
   Object src = event.getSource();
   if (src == light1OnOffMenu) {
     light1OnOff = light1OnOffMenu.getState();
     light1.setEnable(light1OnOff);
     return;
   }
   if (src == light2OnOffMenu) {
     light2OnOff = light2OnOffMenu.getState();
     light2.setEnable(light2OnOff);
     return;
   }
   if (src == light3OnOffMenu) {
     light3OnOff = light3OnOffMenu.getState();
     light3.setEnable(light3OnOff);
     return;
   }
   // Handle all other checkboxes
   super.itemStateChanged(event);
 }

} // //CLASS //SphereGroup - create a group of spheres on the XY plane // //DESCRIPTION //An XY grid of spheres is created. The number of spheres in X and Y, //the spacing in X and Y, the sphere radius, and the appearance can //all be set. // //This grid of spheres is used by several of the examples as a generic //bit of foreground geometry. // //SEE ALSO //Ex*Light //ExBackground* // //AUTHOR //David R. Nadeau / San Diego Supercomputer Center // class SphereGroup extends Group {

 //  Constructors
 public SphereGroup() {
   //    radius x,y spacing x,y count appearance
   this(0.25f, 0.75f, 0.75f, 5, 5, null);
 }
 public SphereGroup(Appearance app) {
   //    radius x,y spacing x,y count appearance
   this(0.25f, 0.75f, 0.75f, 5, 5, app);
 }
 public SphereGroup(float radius, float xSpacing, float ySpacing,
     int xCount, int yCount) {
   this(radius, xSpacing, ySpacing, xCount, yCount, null);
 }
 public SphereGroup(float radius, float xSpacing, float ySpacing,
     int xCount, int yCount, Appearance app) {
   if (app == null) {
     app = new Appearance();
     Material material = new Material();
     material.setDiffuseColor(new Color3f(0.8f, 0.8f, 0.8f));
     material.setSpecularColor(new Color3f(0.0f, 0.0f, 0.0f));
     material.setShininess(0.0f);
     app.setMaterial(material);
   }
   double xStart = -xSpacing * (double) (xCount - 1) / 2.0;
   double yStart = -ySpacing * (double) (yCount - 1) / 2.0;
   Sphere sphere = null;
   TransformGroup trans = null;
   Transform3D t3d = new Transform3D();
   Vector3d vec = new Vector3d();
   double x, y = yStart, z = 0.0;
   for (int i = 0; i < yCount; i++) {
     x = xStart;
     for (int j = 0; j < xCount; j++) {
       vec.set(x, y, z);
       t3d.setTranslation(vec);
       trans = new TransformGroup(t3d);
       addChild(trans);
       sphere = new Sphere(radius, // sphere radius
           Primitive.GENERATE_NORMALS, // generate normals
           16, // 16 divisions radially
           app); // it"s appearance
       trans.addChild(sphere);
       x += xSpacing;
     }
     y += ySpacing;
   }
 }

} /**

* The Example class is a base class extended by example applications. The class
* provides basic features to create a top-level frame, add a menubar and
* Canvas3D, build the universe, set up "examine" and "walk" style navigation
* behaviors, and provide hooks so that subclasses can add 3D content to the
* example"s universe.
* <P>
* Using this Example class simplifies the construction of example applications,
* enabling the author to focus upon 3D content and not the busywork of creating
* windows, menus, and universes.
* 
* @version 1.0, 98/04/16
* @author David R. Nadeau, San Diego Supercomputer Center
*/

class Java3DFrame extends Applet implements WindowListener, ActionListener,

   ItemListener, CheckboxMenuListener {
 //  Navigation types
 public final static int Walk = 0;
 public final static int Examine = 1;
 //  Should the scene be compiled?
 private boolean shouldCompile = true;
 //  GUI objects for our subclasses
 protected Java3DFrame example = null;
 protected Frame exampleFrame = null;
 protected MenuBar exampleMenuBar = null;
 protected Canvas3D exampleCanvas = null;
 protected TransformGroup exampleViewTransform = null;
 protected TransformGroup exampleSceneTransform = null;
 protected boolean debug = false;
 //  Private GUI objects and state
 private boolean headlightOnOff = true;
 private int navigationType = Examine;
 private CheckboxMenuItem headlightMenuItem = null;
 private CheckboxMenuItem walkMenuItem = null;
 private CheckboxMenuItem examineMenuItem = null;
 private DirectionalLight headlight = null;
 private ExamineViewerBehavior examineBehavior = null;
 private WalkViewerBehavior walkBehavior = null;
 //--------------------------------------------------------------
 //  ADMINISTRATION
 //--------------------------------------------------------------
 /**
  * The main program entry point when invoked as an application. Each example
  * application that extends this class must define their own main.
  * 
  * @param args
  *            a String array of command-line arguments
  */
 public static void main(String[] args) {
   Java3DFrame ex = new Java3DFrame();
   ex.initialize(args);
   ex.buildUniverse();
   ex.showFrame();
 }
 /**
  * Constructs a new Example object.
  * 
  * @return a new Example that draws no 3D content
  */
 public Java3DFrame() {
   // Do nothing
 }
 /**
  * Initializes the application when invoked as an applet.
  */
 public void init() {
   // Collect properties into String array
   String[] args = new String[2];
   // NOTE: to be done still...
   this.initialize(args);
   this.buildUniverse();
   this.showFrame();
   // NOTE: add something to the browser page?
 }
 /**
  * Initializes the Example by parsing command-line arguments, building an
  * AWT Frame, constructing a menubar, and creating the 3D canvas.
  * 
  * @param args
  *            a String array of command-line arguments
  */
 protected void initialize(String[] args) {
   example = this;
   // Parse incoming arguments
   parseArgs(args);
   // Build the frame
   if (debug)
     System.err.println("Building GUI...");
   exampleFrame = new Frame();
   exampleFrame.setSize(640, 480);
   exampleFrame.setTitle("Java 3D Example");
   exampleFrame.setLayout(new BorderLayout());
   // Set up a close behavior
   exampleFrame.addWindowListener(this);
   // Create a canvas
   exampleCanvas = new Canvas3D(null);
   exampleCanvas.setSize(630, 460);
   exampleFrame.add("Center", exampleCanvas);
   // Build the menubar
   exampleMenuBar = this.buildMenuBar();
   exampleFrame.setMenuBar(exampleMenuBar);
   // Pack
   exampleFrame.pack();
   exampleFrame.validate();
   //    exampleFrame.setVisible( true );
 }
 /**
  * Parses incoming command-line arguments. Applications that subclass this
  * class may override this method to support their own command-line
  * arguments.
  * 
  * @param args
  *            a String array of command-line arguments
  */
 protected void parseArgs(String[] args) {
   for (int i = 0; i < args.length; i++) {
     if (args[i].equals("-d"))
       debug = true;
   }
 }
 //--------------------------------------------------------------
 //  SCENE CONTENT
 //--------------------------------------------------------------
 /**
  * Builds the 3D universe by constructing a virtual universe (via
  * SimpleUniverse), a view platform (via SimpleUniverse), and a view (via
  * SimpleUniverse). A headlight is added and a set of behaviors initialized
  * to handle navigation types.
  */
 protected void buildUniverse() {
   //
   //  Create a SimpleUniverse object, which builds:
   //
   //    - a Locale using the given hi-res coordinate origin
   //
   //    - a ViewingPlatform which in turn builds:
   //          - a MultiTransformGroup with which to move the
   //            the ViewPlatform about
   //
   //          - a ViewPlatform to hold the view
   //
   //          - a BranchGroup to hold avatar geometry (if any)
   //
   //          - a BranchGroup to hold view platform
   //            geometry (if any)
   //
   //    - a Viewer which in turn builds:
   //          - a PhysicalBody which characterizes the user"s
   //            viewing preferences and abilities
   //
   //          - a PhysicalEnvironment which characterizes the
   //            user"s rendering hardware and software
   //
   //          - a JavaSoundMixer which initializes sound
   //            support within the 3D environment
   //
   //          - a View which renders the scene into a Canvas3D
   //
   //  All of these actions could be done explicitly, but
   //  using the SimpleUniverse utilities simplifies the code.
   //
   if (debug)
     System.err.println("Building scene graph...");
   SimpleUniverse universe = new SimpleUniverse(null, // Hi-res coordinate
       // for the origin -
       // use default
       1, // Number of transforms in MultiTransformGroup
       exampleCanvas, // Canvas3D into which to draw
       null); // URL for user configuration file - use defaults
   //
   //  Get the viewer and create an audio device so that
   //  sound will be enabled in this content.
   //
   Viewer viewer = universe.getViewer();
   viewer.createAudioDevice();
   //
   //  Get the viewing platform created by SimpleUniverse.
   //  From that platform, get the inner-most TransformGroup
   //  in the MultiTransformGroup. That inner-most group
   //  contains the ViewPlatform. It is this inner-most
   //  TransformGroup we need in order to:
   //
   //    - add a "headlight" that always aims forward from
   //       the viewer
   //
   //    - change the viewing direction in a "walk" style
   //
   //  The inner-most TransformGroup"s transform will be
   //  changed by the walk behavior (when enabled).
   //
   ViewingPlatform viewingPlatform = universe.getViewingPlatform();
   exampleViewTransform = viewingPlatform.getViewPlatformTransform();
   //
   //  Create a "headlight" as a forward-facing directional light.
   //  Set the light"s bounds to huge. Since we want the light
   //  on the viewer"s "head", we need the light within the
   //  TransformGroup containing the ViewPlatform. The
   //  ViewingPlatform class creates a handy hook to do this
   //  called "platform geometry". The PlatformGeometry class is
   //  subclassed off of BranchGroup, and is intended to contain
   //  a description of the 3D platform itself... PLUS a headlight!
   //  So, to add the headlight, create a new PlatformGeometry group,
   //  add the light to it, then add that platform geometry to the
   //  ViewingPlatform.
   //
   BoundingSphere allBounds = new BoundingSphere(
       new Point3d(0.0, 0.0, 0.0), 100000.0);
   PlatformGeometry pg = new PlatformGeometry();
   headlight = new DirectionalLight();
   headlight.setColor(White);
   headlight.setDirection(new Vector3f(0.0f, 0.0f, -1.0f));
   headlight.setInfluencingBounds(allBounds);
   headlight.setCapability(Light.ALLOW_STATE_WRITE);
   pg.addChild(headlight);
   viewingPlatform.setPlatformGeometry(pg);
   //
   //  Create the 3D content BranchGroup, containing:
   //
   //    - a TransformGroup who"s transform the examine behavior
   //      will change (when enabled).
   //
   //    - 3D geometry to view
   //
   // Build the scene root
   BranchGroup sceneRoot = new BranchGroup();
   // Build a transform that we can modify
   exampleSceneTransform = new TransformGroup();
   exampleSceneTransform
       .setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
   exampleSceneTransform
       .setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
   exampleSceneTransform.setCapability(Group.ALLOW_CHILDREN_EXTEND);
   //
   //  Build the scene, add it to the transform, and add
   //  the transform to the scene root
   //
   if (debug)
     System.err.println("  scene...");
   Group scene = this.buildScene();
   exampleSceneTransform.addChild(scene);
   sceneRoot.addChild(exampleSceneTransform);
   //
   //  Create a pair of behaviors to implement two navigation
   //  types:
   //
   //    - "examine": a style where mouse drags rotate about
   //      the scene"s origin as if it is an object under
   //      examination. This is similar to the "Examine"
   //      navigation type used by VRML browsers.
   //
   //    - "walk": a style where mouse drags rotate about
   //      the viewer"s center as if the viewer is turning
   //      about to look at a scene they are in. This is
   //      similar to the "Walk" navigation type used by
   //      VRML browsers.
   //
   //  Aim the examine behavior at the scene"s TransformGroup
   //  and add the behavior to the scene root.
   //
   //  Aim the walk behavior at the viewing platform"s
   //  TransformGroup and add the behavior to the scene root.
   //
   //  Enable one (and only one!) of the two behaviors
   //  depending upon the current navigation type.
   //
   examineBehavior = new ExamineViewerBehavior(exampleSceneTransform, // Transform
       // gorup
       // to
       // modify
       exampleFrame); // Parent frame for cusor changes
   examineBehavior.setSchedulingBounds(allBounds);
   sceneRoot.addChild(examineBehavior);
   walkBehavior = new WalkViewerBehavior(exampleViewTransform, // Transform
       // group to
       // modify
       exampleFrame); // Parent frame for cusor changes
   walkBehavior.setSchedulingBounds(allBounds);
   sceneRoot.addChild(walkBehavior);
   if (navigationType == Walk) {
     examineBehavior.setEnable(false);
     walkBehavior.setEnable(true);
   } else {
     examineBehavior.setEnable(true);
     walkBehavior.setEnable(false);
   }
   //
   //  Compile the scene branch group and add it to the
   //  SimpleUniverse.
   //
   if (shouldCompile)
     sceneRoot.rupile();
   universe.addBranchGraph(sceneRoot);
   reset();
 }
 /**
  * Builds the scene. Example application subclasses should replace this
  * method with their own method to build 3D content.
  * 
  * @return a Group containing 3D content to display
  */
 public Group buildScene() {
   // Build the scene group containing nothing
   Group scene = new Group();
   return scene;
 }
 //--------------------------------------------------------------
 //  SET/GET METHODS
 //--------------------------------------------------------------
 /**
  * Sets the headlight on/off state. The headlight faces forward in the
  * direction the viewer is facing. Example applications that add their own
  * lights will typically turn the headlight off. A standard menu item
  * enables the headlight to be turned on and off via user control.
  * 
  * @param onOff
  *            a boolean turning the light on (true) or off (false)
  */
 public void setHeadlightEnable(boolean onOff) {
   headlightOnOff = onOff;
   if (headlight != null)
     headlight.setEnable(headlightOnOff);
   if (headlightMenuItem != null)
     headlightMenuItem.setState(headlightOnOff);
 }
 /**
  * Gets the headlight on/off state.
  * 
  * @return a boolean indicating if the headlight is on or off
  */
 public boolean getHeadlightEnable() {
   return headlightOnOff;
 }
 /**
  * Sets the navigation type to be either Examine or Walk. The Examine
  * navigation type sets up behaviors that use mouse drags to rotate and
  * translate scene content as if it is an object held at arm"s length and
  * under examination. The Walk navigation type uses mouse drags to rotate
  * and translate the viewer as if they are walking through the content. The
  * Examine type is the default.
  * 
  * @param nav
  *            either Walk or Examine
  */
 public void setNavigationType(int nav) {
   if (nav == Walk) {
     navigationType = Walk;
     if (walkMenuItem != null)
       walkMenuItem.setState(true);
     if (examineMenuItem != null)
       examineMenuItem.setState(false);
     if (walkBehavior != null)
       walkBehavior.setEnable(true);
     if (examineBehavior != null)
       examineBehavior.setEnable(false);
   } else {
     navigationType = Examine;
     if (walkMenuItem != null)
       walkMenuItem.setState(false);
     if (examineMenuItem != null)
       examineMenuItem.setState(true);
     if (walkBehavior != null)
       walkBehavior.setEnable(false);
     if (examineBehavior != null)
       examineBehavior.setEnable(true);
   }
 }
 /**
  * Gets the current navigation type, returning either Walk or Examine.
  * 
  * @return either Walk or Examine
  */
 public int getNavigationType() {
   return navigationType;
 }
 /**
  * Sets whether the scene graph should be compiled or not. Normally this is
  * always a good idea. For some example applications that use this Example
  * framework, it is useful to disable compilation - particularly when nodes
  * and node components will need to be made un-live in order to make
  * changes. Once compiled, such components can be made un-live, but they are
  * still unchangable unless appropriate capabilities have been set.
  * 
  * @param onOff
  *            a boolean turning compilation on (true) or off (false)
  */
 public void setCompilable(boolean onOff) {
   shouldCompile = onOff;
 }
 /**
  * Gets whether the scene graph will be compiled or not.
  * 
  * @return a boolean indicating if scene graph compilation is on or off
  */
 public boolean getCompilable() {
   return shouldCompile;
 }
 //These methods will be replaced
 //  Set the view position and direction
 public void setViewpoint(Point3f position, Vector3f direction) {
   Transform3D t = new Transform3D();
   t.set(new Vector3f(position));
   exampleViewTransform.setTransform(t);
   // how to set direction?
 }
 //  Reset transforms
 public void reset() {
   Transform3D trans = new Transform3D();
   exampleSceneTransform.setTransform(trans);
   trans.set(new Vector3f(0.0f, 0.0f, 10.0f));
   exampleViewTransform.setTransform(trans);
   setNavigationType(navigationType);
 }
 //
 //  Gets the URL (with file: prepended) for the current directory.
 //  This is a terrible hack needed in the Alpha release of Java3D
 //  in order to build a full path URL for loading sounds with
 //  MediaContainer. When MediaContainer is fully implemented,
 //  it should handle relative path names, but not yet.
 //
 public String getCurrentDirectory() {
   // Create a bogus file so that we can query it"s path
   File dummy = new File("dummy.tmp");
   String dummyPath = dummy.getAbsolutePath();
   // strip "/dummy.tmp" from end of dummyPath and put into "path"
   if (dummyPath.endsWith(File.separator + "dummy.tmp")) {
     int index = dummyPath.lastIndexOf(File.separator + "dummy.tmp");
     if (index >= 0) {
       int pathLength = index + 5; // pre-pend "file:"
       char[] charPath = new char[pathLength];
       dummyPath.getChars(0, index, charPath, 5);
       String path = new String(charPath, 0, pathLength);
       path = "file:" + path.substring(5, pathLength);
       return path + File.separator;
     }
   }
   return dummyPath + File.separator;
 }
 //--------------------------------------------------------------
 //  USER INTERFACE
 //--------------------------------------------------------------
 /**
  * Builds the example AWT Frame menubar. Standard menus and their options
  * are added. Applications that subclass this class should build their
  * menubar additions within their initialize method.
  * 
  * @return a MenuBar for the AWT Frame
  */
 private MenuBar buildMenuBar() {
   // Build the menubar
   MenuBar menuBar = new MenuBar();
   // File menu
   Menu m = new Menu("File");
   m.addActionListener(this);
   m.add("Exit");
   menuBar.add(m);
   // View menu
   m = new Menu("View");
   m.addActionListener(this);
   m.add("Reset view");
   m.addSeparator();
   walkMenuItem = new CheckboxMenuItem("Walk");
   walkMenuItem.addItemListener(this);
   m.add(walkMenuItem);
   examineMenuItem = new CheckboxMenuItem("Examine");
   examineMenuItem.addItemListener(this);
   m.add(examineMenuItem);
   if (navigationType == Walk) {
     walkMenuItem.setState(true);
     examineMenuItem.setState(false);
   } else {
     walkMenuItem.setState(false);
     examineMenuItem.setState(true);
   }
   m.addSeparator();
   headlightMenuItem = new CheckboxMenuItem("Headlight on/off");
   headlightMenuItem.addItemListener(this);
   headlightMenuItem.setState(headlightOnOff);
   m.add(headlightMenuItem);
   menuBar.add(m);
   return menuBar;
 }
 /**
  * Shows the application"s frame, making it and its menubar, 3D canvas, and
  * 3D content visible.
  */
 public void showFrame() {
   exampleFrame.show();
 }
 /**
  * Quits the application.
  */
 public void quit() {
   System.exit(0);
 }
 /**
  * Handles menu selections.
  * 
  * @param event
  *            an ActionEvent indicating what menu action requires handling
  */
 public void actionPerformed(ActionEvent event) {
   String arg = event.getActionCommand();
   if (arg.equals("Reset view"))
     reset();
   else if (arg.equals("Exit"))
     quit();
 }
 /**
  * Handles checkbox items on a CheckboxMenu. The Example class has none of
  * its own, but subclasses may have some.
  * 
  * @param menu
  *            which CheckboxMenu needs action
  * @param check
  *            which CheckboxMenu item has changed
  */
 public void checkboxChanged(CheckboxMenu menu, int check) {
   // None for us
 }
 /**
  * Handles on/off checkbox items on a standard menu.
  * 
  * @param event
  *            an ItemEvent indicating what requires handling
  */
 public void itemStateChanged(ItemEvent event) {
   Object src = event.getSource();
   boolean state;
   if (src == headlightMenuItem) {
     state = headlightMenuItem.getState();
     headlight.setEnable(state);
   } else if (src == walkMenuItem)
     setNavigationType(Walk);
   else if (src == examineMenuItem)
     setNavigationType(Examine);
 }
 /**
  * Handles a window closing event notifying the application that the user
  * has chosen to close the application without selecting the "Exit" menu
  * item.
  * 
  * @param event
  *            a WindowEvent indicating the window is closing
  */
 public void windowClosing(WindowEvent event) {
   quit();
 }
 public void windowClosed(WindowEvent event) {
 }
 public void windowOpened(WindowEvent event) {
 }
 public void windowIconified(WindowEvent event) {
 }
 public void windowDeiconified(WindowEvent event) {
 }
 public void windowActivated(WindowEvent event) {
 }
 public void windowDeactivated(WindowEvent event) {
 }
 //  Well known colors, positions, and directions
 public final static Color3f White = new Color3f(1.0f, 1.0f, 1.0f);
 public final static Color3f Gray = new Color3f(0.7f, 0.7f, 0.7f);
 public final static Color3f DarkGray = new Color3f(0.2f, 0.2f, 0.2f);
 public final static Color3f Black = new Color3f(0.0f, 0.0f, 0.0f);
 public final static Color3f Red = new Color3f(1.0f, 0.0f, 0.0f);
 public final static Color3f DarkRed = new Color3f(0.3f, 0.0f, 0.0f);
 public final static Color3f Yellow = new Color3f(1.0f, 1.0f, 0.0f);
 public final static Color3f DarkYellow = new Color3f(0.3f, 0.3f, 0.0f);
 public final static Color3f Green = new Color3f(0.0f, 1.0f, 0.0f);
 public final static Color3f DarkGreen = new Color3f(0.0f, 0.3f, 0.0f);
 public final static Color3f Cyan = new Color3f(0.0f, 1.0f, 1.0f);
 public final static Color3f Blue = new Color3f(0.0f, 0.0f, 1.0f);
 public final static Color3f DarkBlue = new Color3f(0.0f, 0.0f, 0.3f);
 public final static Color3f Magenta = new Color3f(1.0f, 0.0f, 1.0f);
 public final static Vector3f PosX = new Vector3f(1.0f, 0.0f, 0.0f);
 public final static Vector3f NegX = new Vector3f(-1.0f, 0.0f, 0.0f);
 public final static Vector3f PosY = new Vector3f(0.0f, 1.0f, 0.0f);
 public final static Vector3f NegY = new Vector3f(0.0f, -1.0f, 0.0f);
 public final static Vector3f PosZ = new Vector3f(0.0f, 0.0f, 1.0f);
 public final static Vector3f NegZ = new Vector3f(0.0f, 0.0f, -1.0f);
 public final static Point3f Origin = new Point3f(0.0f, 0.0f, 0.0f);
 public final static Point3f PlusX = new Point3f(0.75f, 0.0f, 0.0f);
 public final static Point3f MinusX = new Point3f(-0.75f, 0.0f, 0.0f);
 public final static Point3f PlusY = new Point3f(0.0f, 0.75f, 0.0f);
 public final static Point3f MinusY = new Point3f(0.0f, -0.75f, 0.0f);
 public final static Point3f PlusZ = new Point3f(0.0f, 0.0f, 0.75f);
 public final static Point3f MinusZ = new Point3f(0.0f, 0.0f, -0.75f);

} // //INTERFACE //CheckboxMenuListener - listen for checkbox change events // //DESCRIPTION //The checkboxChanged method is called by users of this class //to notify the listener when a checkbox choice has changed on //a CheckboxMenu class menu. // interface CheckboxMenuListener extends EventListener {

 public void checkboxChanged(CheckboxMenu menu, int check);

} /**

* ExamineViewerBehavior
* 
* @version 1.0, 98/04/16
*/

/**

* Wakeup on mouse button presses, releases, and mouse movements and generate
* transforms in an "examination style" that enables the user to rotate,
* translation, and zoom an object as if it is held at arm"s length. Such an
* examination style is similar to the "Examine" navigation type used by VRML
* browsers.
* 
* The behavior maps mouse drags to different transforms depending upon the
* mosue button held down:
* 
* Button 1 (left) Horizontal movement --> Y-axis rotation Vertical movement -->
* X-axis rotation
* 
* Button 2 (middle) Horizontal movement --> nothing Vertical movement -->
* Z-axis translation
* 
* Button 3 (right) Horizontal movement --> X-axis translation Vertical movement
* --> Y-axis translation
* 
* To support systems with 2 or 1 mouse buttons, the following alternate
* mappings are supported while dragging with any mouse button held down and
* zero or more keyboard modifiers held down:
* 
* No modifiers = Button 1 ALT = Button 2 Meta = Button 3 Control = Button 3
* 
* The behavior automatically modifies a TransformGroup provided to the
* constructor. The TransformGroup"s transform can be set at any time by the
* application or other behaviors to cause the examine rotation and translation
* to be reset.
*/

// This class is inspired by the MouseBehavior, MouseRotate, // MouseTranslate, and MouseZoom utility behaviors provided with // Java 3D. This class differs from those utilities in that it: // // (a) encapsulates all three behaviors into one in order to // enforce a specific "Examine" symantic // // (b) supports set/get of the rotation and translation factors // that control the speed of movement. // // (c) supports the "Control" modifier as an alternative to the // "Meta" modifier not present on PC, Mac, and most non-Sun // keyboards. This makes button3 behavior usable on PCs, // Macs, and other systems with fewer than 3 mouse buttons. class ExamineViewerBehavior extends ViewerBehavior {

 // Previous cursor location
 protected int previousX = 0;
 protected int previousY = 0;
 // Saved standard cursor
 protected Cursor savedCursor = null;
 /**
  * Construct an examine behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into a
  * transform group given later with the setTransformGroup( ) method.
  */
 public ExamineViewerBehavior() {
   super();
 }
 /**
  * Construct an examine behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into a
  * transform group given later with the setTransformGroup( ) method.
  * 
  * @param parent
  *            The AWT Component that contains the area generating mouse
  *            events.
  */
 public ExamineViewerBehavior(Component parent) {
   super(parent);
 }
 /**
  * Construct an examine behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into the
  * given transform group.
  * 
  * @param transformGroup
  *            The transform group to be modified by the behavior.
  */
 public ExamineViewerBehavior(TransformGroup transformGroup) {
   super();
   subjectTransformGroup = transformGroup;
 }
 /**
  * Construct an examine behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into the
  * given transform group.
  * 
  * @param transformGroup
  *            The transform group to be modified by the behavior.
  * @param parent
  *            The AWT Component that contains the area generating mouse
  *            events.
  */
 public ExamineViewerBehavior(TransformGroup transformGroup, Component parent) {
   super(parent);
   subjectTransformGroup = transformGroup;
 }
 /**
  * Respond to a button1 event (press, release, or drag).
  * 
  * @param mouseEvent
  *            A MouseEvent to respond to.
  */
 public void onButton1(MouseEvent mev) {
   if (subjectTransformGroup == null)
     return;
   int x = mev.getX();
   int y = mev.getY();
   if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
     // Mouse button pressed: record position
     previousX = x;
     previousY = y;
     // Change to a "move" cursor
     if (parentComponent != null) {
       savedCursor = parentComponent.getCursor();
       parentComponent.setCursor(Cursor
           .getPredefinedCursor(Cursor.HAND_CURSOR));
     }
     return;
   }
   if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
     // Mouse button released: do nothing
     // Switch the cursor back
     if (parentComponent != null)
       parentComponent.setCursor(savedCursor);
     return;
   }
   //
   // Mouse moved while button down: create a rotation
   //
   // Compute the delta in X and Y from the previous
   // position. Use the delta to compute rotation
   // angles with the mapping:
   //
   //   positive X mouse delta --> positive Y-axis rotation
   //   positive Y mouse delta --> positive X-axis rotation
   //
   // where positive X mouse movement is to the right, and
   // positive Y mouse movement is **down** the screen.
   //
   int deltaX = x - previousX;
   int deltaY = y - previousY;
   if (deltaX > UNUSUAL_XDELTA || deltaX < -UNUSUAL_XDELTA
       || deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {
     // Deltas are too huge to be believable. Probably a glitch.
     // Don"t record the new XY location, or do anything.
     return;
   }
   double xRotationAngle = deltaY * XRotationFactor;
   double yRotationAngle = deltaX * YRotationFactor;
   //
   // Build transforms
   //
   transform1.rotX(xRotationAngle);
   transform2.rotY(yRotationAngle);
   // Get and save the current transform matrix
   subjectTransformGroup.getTransform(currentTransform);
   currentTransform.get(matrix);
   translate.set(matrix.m03, matrix.m13, matrix.m23);
   // Translate to the origin, rotate, then translate back
   currentTransform.setTranslation(origin);
   currentTransform.mul(transform1, currentTransform);
   currentTransform.mul(transform2, currentTransform);
   currentTransform.setTranslation(translate);
   // Update the transform group
   subjectTransformGroup.setTransform(currentTransform);
   previousX = x;
   previousY = y;
 }
 /**
  * Respond to a button2 event (press, release, or drag).
  * 
  * @param mouseEvent
  *            A MouseEvent to respond to.
  */
 public void onButton2(MouseEvent mev) {
   if (subjectTransformGroup == null)
     return;
   int x = mev.getX();
   int y = mev.getY();
   if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
     // Mouse button pressed: record position
     previousX = x;
     previousY = y;
     // Change to a "move" cursor
     if (parentComponent != null) {
       savedCursor = parentComponent.getCursor();
       parentComponent.setCursor(Cursor
           .getPredefinedCursor(Cursor.MOVE_CURSOR));
     }
     return;
   }
   if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
     // Mouse button released: do nothing
     // Switch the cursor back
     if (parentComponent != null)
       parentComponent.setCursor(savedCursor);
     return;
   }
   //
   // Mouse moved while button down: create a translation
   //
   // Compute the delta in Y from the previous
   // position. Use the delta to compute translation
   // distances with the mapping:
   //
   //   positive Y mouse delta --> positive Y-axis translation
   //
   // where positive X mouse movement is to the right, and
   // positive Y mouse movement is **down** the screen.
   //
   int deltaY = y - previousY;
   if (deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {
     // Deltas are too huge to be believable. Probably a glitch.
     // Don"t record the new XY location, or do anything.
     return;
   }
   double zTranslationDistance = deltaY * ZTranslationFactor;
   //
   // Build transforms
   //
   translate.set(0.0, 0.0, zTranslationDistance);
   transform1.set(translate);
   // Get and save the current transform
   subjectTransformGroup.getTransform(currentTransform);
   // Translate as needed
   currentTransform.mul(transform1, currentTransform);
   // Update the transform group
   subjectTransformGroup.setTransform(currentTransform);
   previousX = x;
   previousY = y;
 }
 /**
  * Respond to a button3 event (press, release, or drag).
  * 
  * @param mouseEvent
  *            A MouseEvent to respond to.
  */
 public void onButton3(MouseEvent mev) {
   if (subjectTransformGroup == null)
     return;
   int x = mev.getX();
   int y = mev.getY();
   if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
     // Mouse button pressed: record position
     previousX = x;
     previousY = y;
     // Change to a "move" cursor
     if (parentComponent != null) {
       savedCursor = parentComponent.getCursor();
       parentComponent.setCursor(Cursor
           .getPredefinedCursor(Cursor.MOVE_CURSOR));
     }
     return;
   }
   if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
     // Mouse button released: do nothing
     // Switch the cursor back
     if (parentComponent != null)
       parentComponent.setCursor(savedCursor);
     return;
   }
   //
   // Mouse moved while button down: create a translation
   //
   // Compute the delta in X and Y from the previous
   // position. Use the delta to compute translation
   // distances with the mapping:
   //
   //   positive X mouse delta --> positive X-axis translation
   //   positive Y mouse delta --> negative Y-axis translation
   //
   // where positive X mouse movement is to the right, and
   // positive Y mouse movement is **down** the screen.
   //
   int deltaX = x - previousX;
   int deltaY = y - previousY;
   if (deltaX > UNUSUAL_XDELTA || deltaX < -UNUSUAL_XDELTA
       || deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {
     // Deltas are too huge to be believable. Probably a glitch.
     // Don"t record the new XY location, or do anything.
     return;
   }
   double xTranslationDistance = deltaX * XTranslationFactor;
   double yTranslationDistance = -deltaY * YTranslationFactor;
   //
   // Build transforms
   //
   translate.set(xTranslationDistance, yTranslationDistance, 0.0);
   transform1.set(translate);
   // Get and save the current transform
   subjectTransformGroup.getTransform(currentTransform);
   // Translate as needed
   currentTransform.mul(transform1, currentTransform);
   // Update the transform group
   subjectTransformGroup.setTransform(currentTransform);
   previousX = x;
   previousY = y;
 }
 /**
  * Respond to an elapsed frames event (assuming subclass has set up a wakeup
  * criterion for it).
  * 
  * @param time
  *            A WakeupOnElapsedFrames criterion to respond to.
  */
 public void onElapsedFrames(WakeupOnElapsedFrames timeEvent) {
   // Can"t happen
 }

} /*

* 
* Copyright (c) 1998 David R. Nadeau
*  
*/

/**

* WalkViewerBehavior is a utility class that creates a "walking style"
* navigation symantic.
* 
* The behavior wakes up on mouse button presses, releases, and mouse movements
* and generates transforms in a "walk style" that enables the user to walk
* through a scene, translating and turning about as if they are within the
* scene. Such a walk style is similar to the "Walk" navigation type used by
* VRML browsers.
* <P>
* The behavior maps mouse drags to different transforms depending upon the
* mouse button held down:
*
*
Button 1 (left) *
Horizontal movement --> Y-axis rotation *
Vertical movement --> Z-axis translation * *
Button 2 (middle) *
Horizontal movement --> Y-axis rotation *
Vertical movement --> X-axis rotation * *
Button 3 (right) *
Horizontal movement --> X-axis translation *
Vertical movement --> Y-axis translation *
* 
* To support systems with 2 or 1 mouse buttons, the following alternate
* mappings are supported while dragging with any mouse button held down and
* zero or more keyboard modifiers held down:
*
    *
  • No modifiers = Button 1 *
  • ALT = Button 2 *
  • Meta = Button 3 *
  • Control = Button 3 *
* The behavior automatically modifies a TransformGroup provided to the
* constructor. The TransformGroup"s transform can be set at any time by the
* application or other behaviors to cause the walk rotation and translation to
* be reset.
* <P>
* While a mouse button is down, the behavior automatically changes the cursor
* in a given parent AWT Component. If no parent Component is given, no cursor
* changes are attempted.
* 
* @version 1.0, 98/04/16
* @author David R. Nadeau, San Diego Supercomputer Center
*/

class WalkViewerBehavior extends ViewerBehavior {

 // This class is inspired by the MouseBehavior, MouseRotate,
 // MouseTranslate, and MouseZoom utility behaviors provided with
 // Java 3D. This class differs from those utilities in that it:
 //
 //    (a) encapsulates all three behaviors into one in order to
 //        enforce a specific "Walk" symantic
 //
 //    (b) supports set/get of the rotation and translation factors
 //        that control the speed of movement.
 //
 //    (c) supports the "Control" modifier as an alternative to the
 //        "Meta" modifier not present on PC, Mac, and most non-Sun
 //        keyboards. This makes button3 behavior usable on PCs,
 //        Macs, and other systems with fewer than 3 mouse buttons.
 // Previous and initial cursor locations
 protected int previousX = 0;
 protected int previousY = 0;
 protected int initialX = 0;
 protected int initialY = 0;
 // Deadzone size (delta from initial XY for which no
 // translate or rotate action is taken
 protected static final int DELTAX_DEADZONE = 10;
 protected static final int DELTAY_DEADZONE = 10;
 // Keep a set of wakeup criterion for animation-generated
 // event types.
 protected WakeupCriterion[] mouseAndAnimationEvents = null;
 protected WakeupOr mouseAndAnimationCriterion = null;
 protected WakeupOr savedMouseCriterion = null;
 // Saved standard cursor
 protected Cursor savedCursor = null;
 /**
  * Default Rotation and translation scaling factors for animated movements
  * (Button 1 press).
  */
 public static final double DEFAULT_YROTATION_ANIMATION_FACTOR = 0.0002;
 public static final double DEFAULT_ZTRANSLATION_ANIMATION_FACTOR = 0.01;
 protected double YRotationAnimationFactor = DEFAULT_YROTATION_ANIMATION_FACTOR;
 protected double ZTranslationAnimationFactor = DEFAULT_ZTRANSLATION_ANIMATION_FACTOR;
 /**
  * Constructs a new walk behavior that converts mouse actions into rotations
  * and translations. Rotations and translations are written into a
  * TransformGroup that must be set using the setTransformGroup method. The
  * cursor will be changed during mouse actions if the parent frame is set
  * using the setParentComponent method.
  * 
  * @return a new WalkViewerBehavior that needs its TransformGroup and parent
  *         Component set
  */
 public WalkViewerBehavior() {
   super();
 }
 /**
  * Constructs a new walk behavior that converts mouse actions into rotations
  * and translations. Rotations and translations are written into a
  * TransformGroup that must be set using the setTransformGroup method. The
  * cursor will be changed within the given AWT parent Component during mouse
  * drags.
  * 
  * @param parent
  *            a parent AWT Component within which the cursor will change
  *            during mouse drags
  * 
  * @return a new WalkViewerBehavior that needs its TransformGroup and parent
  *         Component set
  */
 public WalkViewerBehavior(Component parent) {
   super(parent);
 }
 /**
  * Constructs a new walk behavior that converts mouse actions into rotations
  * and translations. Rotations and translations are written into the given
  * TransformGroup. The cursor will be changed during mouse actions if the
  * parent frame is set using the setParentComponent method.
  * 
  * @param transformGroup
  *            a TransformGroup whos transform is read and written by the
  *            behavior
  * 
  * @return a new WalkViewerBehavior that needs its TransformGroup and parent
  *         Component set
  */
 public WalkViewerBehavior(TransformGroup transformGroup) {
   super();
   subjectTransformGroup = transformGroup;
 }
 /**
  * Constructs a new walk behavior that converts mouse actions into rotations
  * and translations. Rotations and translations are written into the given
  * TransformGroup. The cursor will be changed within the given AWT parent
  * Component during mouse drags.
  * 
  * @param transformGroup
  *            a TransformGroup whos transform is read and written by the
  *            behavior
  * 
  * @param parent
  *            a parent AWT Component within which the cursor will change
  *            during mouse drags
  * 
  * @return a new WalkViewerBehavior that needs its TransformGroup and parent
  *         Component set
  */
 public WalkViewerBehavior(TransformGroup transformGroup, Component parent) {
   super(parent);
   subjectTransformGroup = transformGroup;
 }
 /**
  * Initializes the behavior.
  */
 public void initialize() {
   super.initialize();
   savedMouseCriterion = mouseCriterion; // from parent class
   mouseAndAnimationEvents = new WakeupCriterion[4];
   mouseAndAnimationEvents[0] = new WakeupOnAWTEvent(
       MouseEvent.MOUSE_DRAGGED);
   mouseAndAnimationEvents[1] = new WakeupOnAWTEvent(
       MouseEvent.MOUSE_PRESSED);
   mouseAndAnimationEvents[2] = new WakeupOnAWTEvent(
       MouseEvent.MOUSE_RELEASED);
   mouseAndAnimationEvents[3] = new WakeupOnElapsedFrames(0);
   mouseAndAnimationCriterion = new WakeupOr(mouseAndAnimationEvents);
   // Don"t use the above criterion until a button 1 down event
 }
 /**
  * Sets the Y rotation animation scaling factor for Y-axis rotations. This
  * scaling factor is used to control the speed of Y rotation when button 1
  * is pressed and dragged.
  * 
  * @param factor
  *            the double Y rotation scaling factor
  */
 public void setYRotationAnimationFactor(double factor) {
   YRotationAnimationFactor = factor;
 }
 /**
  * Gets the current Y animation rotation scaling factor for Y-axis
  * rotations.
  * 
  * @return the double Y rotation scaling factor
  */
 public double getYRotationAnimationFactor() {
   return YRotationAnimationFactor;
 }
 /**
  * Sets the Z animation translation scaling factor for Z-axis translations.
  * This scaling factor is used to control the speed of Z translation when
  * button 1 is pressed and dragged.
  * 
  * @param factor
  *            the double Z translation scaling factor
  */
 public void setZTranslationAnimationFactor(double factor) {
   ZTranslationAnimationFactor = factor;
 }
 /**
  * Gets the current Z animation translation scaling factor for Z-axis
  * translations.
  * 
  * @return the double Z translation scaling factor
  */
 public double getZTranslationAnimationFactor() {
   return ZTranslationAnimationFactor;
 }
 /**
  * Responds to an elapsed frames event. Such an event is generated on every
  * frame while button 1 is held down. On each call, this method computes new
  * Y-axis rotation and Z-axis translation values and writes them to the
  * behavior"s TransformGroup. The translation and rotation amounts are
  * computed based upon the distance between the current cursor location and
  * the cursor location when button 1 was pressed. As this distance
  * increases, the translation or rotation amount increases.
  * 
  * @param time
  *            the WakeupOnElapsedFrames criterion to respond to
  */
 public void onElapsedFrames(WakeupOnElapsedFrames timeEvent) {
   //
   // Time elapsed while button down: create a rotation and
   // a translation.
   //
   // Compute the delta in X and Y from the initial position to
   // the previous position. Multiply the delta times a scaling
   // factor to compute an offset to add to the current translation
   // and rotation. Use the mapping:
   //
   //   positive X mouse delta --> negative Y-axis rotation
   //   positive Y mouse delta --> positive Z-axis translation
   //
   // where positive X mouse movement is to the right, and
   // positive Y mouse movement is **down** the screen.
   //
   if (buttonPressed != BUTTON1)
     return;
   int deltaX = previousX - initialX;
   int deltaY = previousY - initialY;
   double yRotationAngle = -deltaX * YRotationAnimationFactor;
   double zTranslationDistance = deltaY * ZTranslationAnimationFactor;
   //
   // Build transforms
   //
   transform1.rotY(yRotationAngle);
   translate.set(0.0, 0.0, zTranslationDistance);
   // Get and save the current transform matrix
   subjectTransformGroup.getTransform(currentTransform);
   currentTransform.get(matrix);
   // Translate to the origin, rotate, then translate back
   currentTransform.setTranslation(origin);
   currentTransform.mul(transform1, currentTransform);
   // Translate back from the origin by the original translation
   // distance, plus the new walk translation... but force walk
   // to travel on a plane by ignoring the Y component of a
   // transformed translation vector.
   currentTransform.transform(translate);
   translate.x += matrix.m03; // add in existing X translation
   translate.y = matrix.m13; // use Y translation
   translate.z += matrix.m23; // add in existing Z translation
   currentTransform.setTranslation(translate);
   // Update the transform group
   subjectTransformGroup.setTransform(currentTransform);
 }
 /**
  * Responds to a button1 event (press, release, or drag). On a press, the
  * method adds a wakeup criterion to the behavior"s set, callling for the
  * behavior to be awoken on each frame. On a button prelease, this criterion
  * is removed from the set.
  * 
  * @param mouseEvent
  *            the MouseEvent to respond to
  */
 public void onButton1(MouseEvent mev) {
   if (subjectTransformGroup == null)
     return;
   int x = mev.getX();
   int y = mev.getY();
   if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
     // Mouse button pressed: record position and change
     // the wakeup criterion to include elapsed time wakeups
     // so we can animate.
     previousX = x;
     previousY = y;
     initialX = x;
     initialY = y;
     // Swap criterion... parent class will not reschedule us
     mouseCriterion = mouseAndAnimationCriterion;
     // Change to a "move" cursor
     if (parentComponent != null) {
       savedCursor = parentComponent.getCursor();
       parentComponent.setCursor(Cursor
           .getPredefinedCursor(Cursor.HAND_CURSOR));
     }
     return;
   }
   if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
     // Mouse button released: restore original wakeup
     // criterion which only includes mouse activity, not
     // elapsed time
     mouseCriterion = savedMouseCriterion;
     // Switch the cursor back
     if (parentComponent != null)
       parentComponent.setCursor(savedCursor);
     return;
   }
   previousX = x;
   previousY = y;
 }
 /**
  * Responds to a button2 event (press, release, or drag). On a press, the
  * method records the initial cursor location. On a drag, the difference
  * between the current and previous cursor location provides a delta that
  * controls the amount by which to rotate in X and Y.
  * 
  * @param mouseEvent
  *            the MouseEvent to respond to
  */
 public void onButton2(MouseEvent mev) {
   if (subjectTransformGroup == null)
     return;
   int x = mev.getX();
   int y = mev.getY();
   if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
     // Mouse button pressed: record position
     previousX = x;
     previousY = y;
     initialX = x;
     initialY = y;
     // Change to a "rotate" cursor
     if (parentComponent != null) {
       savedCursor = parentComponent.getCursor();
       parentComponent.setCursor(Cursor
           .getPredefinedCursor(Cursor.MOVE_CURSOR));
     }
     return;
   }
   if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
     // Mouse button released: do nothing
     // Switch the cursor back
     if (parentComponent != null)
       parentComponent.setCursor(savedCursor);
     return;
   }
   //
   // Mouse moved while button down: create a rotation
   //
   // Compute the delta in X and Y from the previous
   // position. Use the delta to compute rotation
   // angles with the mapping:
   //
   //   positive X mouse delta --> negative Y-axis rotation
   //   positive Y mouse delta --> negative X-axis rotation
   //
   // where positive X mouse movement is to the right, and
   // positive Y mouse movement is **down** the screen.
   //
   int deltaX = x - previousX;
   int deltaY = 0;
   if (Math.abs(y - initialY) > DELTAY_DEADZONE) {
     // Cursor has moved far enough vertically to consider
     // it intentional, so get it"s delta.
     deltaY = y - previousY;
   }
   if (deltaX > UNUSUAL_XDELTA || deltaX < -UNUSUAL_XDELTA
       || deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {
     // Deltas are too huge to be believable. Probably a glitch.
     // Don"t record the new XY location, or do anything.
     return;
   }
   double xRotationAngle = -deltaY * XRotationFactor;
   double yRotationAngle = -deltaX * YRotationFactor;
   //
   // Build transforms
   //
   transform1.rotX(xRotationAngle);
   transform2.rotY(yRotationAngle);
   // Get and save the current transform matrix
   subjectTransformGroup.getTransform(currentTransform);
   currentTransform.get(matrix);
   translate.set(matrix.m03, matrix.m13, matrix.m23);
   // Translate to the origin, rotate, then translate back
   currentTransform.setTranslation(origin);
   currentTransform.mul(transform2, currentTransform);
   currentTransform.mul(transform1);
   currentTransform.setTranslation(translate);
   // Update the transform group
   subjectTransformGroup.setTransform(currentTransform);
   previousX = x;
   previousY = y;
 }
 /**
  * Responds to a button3 event (press, release, or drag). On a drag, the
  * difference between the current and previous cursor location provides a
  * delta that controls the amount by which to translate in X and Y.
  * 
  * @param mouseEvent
  *            the MouseEvent to respond to
  */
 public void onButton3(MouseEvent mev) {
   if (subjectTransformGroup == null)
     return;
   int x = mev.getX();
   int y = mev.getY();
   if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
     // Mouse button pressed: record position
     previousX = x;
     previousY = y;
     // Change to a "move" cursor
     if (parentComponent != null) {
       savedCursor = parentComponent.getCursor();
       parentComponent.setCursor(Cursor
           .getPredefinedCursor(Cursor.MOVE_CURSOR));
     }
     return;
   }
   if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
     // Mouse button released: do nothing
     // Switch the cursor back
     if (parentComponent != null)
       parentComponent.setCursor(savedCursor);
     return;
   }
   //
   // Mouse moved while button down: create a translation
   //
   // Compute the delta in X and Y from the previous
   // position. Use the delta to compute translation
   // distances with the mapping:
   //
   //   positive X mouse delta --> positive X-axis translation
   //   positive Y mouse delta --> negative Y-axis translation
   //
   // where positive X mouse movement is to the right, and
   // positive Y mouse movement is **down** the screen.
   //
   int deltaX = x - previousX;
   int deltaY = y - previousY;
   if (deltaX > UNUSUAL_XDELTA || deltaX < -UNUSUAL_XDELTA
       || deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {
     // Deltas are too huge to be believable. Probably a glitch.
     // Don"t record the new XY location, or do anything.
     return;
   }
   double xTranslationDistance = deltaX * XTranslationFactor;
   double yTranslationDistance = -deltaY * YTranslationFactor;
   //
   // Build transforms
   //
   translate.set(xTranslationDistance, yTranslationDistance, 0.0);
   transform1.set(translate);
   // Get and save the current transform
   subjectTransformGroup.getTransform(currentTransform);
   // Translate as needed
   currentTransform.mul(transform1);
   // Update the transform group
   subjectTransformGroup.setTransform(currentTransform);
   previousX = x;
   previousY = y;
 }

} // //CLASS //CheckboxMenu - build a menu of grouped checkboxes // //DESCRIPTION //The class creates a menu with one or more CheckboxMenuItem"s //and monitors that menu. When a menu checkbox is picked, the //previous one is turned off (in radio-button style). Then, //a given listener"s checkboxChanged method is called, passing it //the menu and the item checked. // class CheckboxMenu extends Menu implements ItemListener {

 // State
 protected CheckboxMenuItem[] checks = null;
 protected int current = 0;
 protected CheckboxMenuListener listener = null;
 //  Construct
 public CheckboxMenu(String name, NameValue[] items,
     CheckboxMenuListener listen) {
   this(name, items, 0, listen);
 }
 public CheckboxMenu(String name, NameValue[] items, int cur,
     CheckboxMenuListener listen) {
   super(name);
   current = cur;
   listener = listen;
   if (items == null)
     return;
   checks = new CheckboxMenuItem[items.length];
   for (int i = 0; i < items.length; i++) {
     checks[i] = new CheckboxMenuItem(items[i].name, false);
     checks[i].addItemListener(this);
     add(checks[i]);
   }
   checks[cur].setState(true);
 }
 //  Handle checkbox changed events
 public void itemStateChanged(ItemEvent event) {
   Object src = event.getSource();
   for (int i = 0; i < checks.length; i++) {
     if (src == checks[i]) {
       // Update the checkboxes
       checks[current].setState(false);
       current = i;
       checks[current].setState(true);
       if (listener != null)
         listener.checkboxChanged(this, i);
       return;
     }
   }
 }
 // Methods to get and set state
 public int getCurrent() {
   return current;
 }
 public void setCurrent(int cur) {
   if (cur < 0 || cur >= checks.length)
     return; // ignore out of range choices
   if (checks == null)
     return;
   checks[current].setState(false);
   current = cur;
   checks[current].setState(true);
 }
 public CheckboxMenuItem getSelectedCheckbox() {
   if (checks == null)
     return null;
   return checks[current];
 }
 public void setSelectedCheckbox(CheckboxMenuItem item) {
   if (checks == null)
     return;
   for (int i = 0; i < checks.length; i++) {
     if (item == checks[i]) {
       checks[i].setState(false);
       current = i;
       checks[i].setState(true);
     }
   }
 }

} /**

* ViewerBehavior
* 
* @version 1.0, 98/04/16
*/

/**

* Wakeup on mouse button presses, releases, and mouse movements and generate
* transforms for a transform group. Classes that extend this class impose
* specific symantics, such as "Examine" or "Walk" viewing, similar to the
* navigation types used by VRML browsers.
* 
* To support systems with 2 or 1 mouse buttons, the following alternate
* mappings are supported while dragging with any mouse button held down and
* zero or more keyboard modifiers held down:
* 
* No modifiers = Button 1 ALT = Button 2 Meta = Button 3 Control = Button 3
* 
* The behavior automatically modifies a TransformGroup provided to the
* constructor. The TransformGroup"s transform can be set at any time by the
* application or other behaviors to cause the viewer"s rotation and translation
* to be reset.
*/

// This class is inspired by the MouseBehavior, MouseRotate, // MouseTranslate, and MouseZoom utility behaviors provided with // Java 3D. This class differs from those utilities in that it: // // (a) encapsulates all three behaviors into one in order to // enforce a specific viewing symantic // // (b) supports set/get of the rotation and translation factors // that control the speed of movement. // // (c) supports the "Control" modifier as an alternative to the // "Meta" modifier not present on PC, Mac, and most non-Sun // keyboards. This makes button3 behavior usable on PCs, // Macs, and other systems with fewer than 3 mouse buttons. abstract class ViewerBehavior extends Behavior {

 // Keep track of the transform group who"s transform we modify
 // during mouse motion.
 protected TransformGroup subjectTransformGroup = null;
 // Keep a set of wakeup criterion for different mouse-generated
 // event types.
 protected WakeupCriterion[] mouseEvents = null;
 protected WakeupOr mouseCriterion = null;
 // Track which button was last pressed
 protected static final int BUTTONNONE = -1;
 protected static final int BUTTON1 = 0;
 protected static final int BUTTON2 = 1;
 protected static final int BUTTON3 = 2;
 protected int buttonPressed = BUTTONNONE;
 // Keep a few Transform3Ds for use during event processing. This
 // avoids having to allocate new ones on each event.
 protected Transform3D currentTransform = new Transform3D();
 protected Transform3D transform1 = new Transform3D();
 protected Transform3D transform2 = new Transform3D();
 protected Matrix4d matrix = new Matrix4d();
 protected Vector3d origin = new Vector3d(0.0, 0.0, 0.0);
 protected Vector3d translate = new Vector3d(0.0, 0.0, 0.0);
 // Unusual X and Y delta limits.
 protected static final int UNUSUAL_XDELTA = 400;
 protected static final int UNUSUAL_YDELTA = 400;
 protected Component parentComponent = null;
 /**
  * Construct a viewer behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into a
  * transform group given later with the setTransformGroup( ) method.
  */
 public ViewerBehavior() {
   super();
 }
 /**
  * Construct a viewer behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into a
  * transform group given later with the setTransformGroup( ) method.
  * 
  * @param parent
  *            The AWT Component that contains the area generating mouse
  *            events.
  */
 public ViewerBehavior(Component parent) {
   super();
   parentComponent = parent;
 }
 /**
  * Construct a viewer behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into the
  * given transform group.
  * 
  * @param transformGroup
  *            The transform group to be modified by the behavior.
  */
 public ViewerBehavior(TransformGroup transformGroup) {
   super();
   subjectTransformGroup = transformGroup;
 }
 /**
  * Construct a viewer behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into the
  * given transform group.
  * 
  * @param transformGroup
  *            The transform group to be modified by the behavior.
  * @param parent
  *            The AWT Component that contains the area generating mouse
  *            events.
  */
 public ViewerBehavior(TransformGroup transformGroup, Component parent) {
   super();
   subjectTransformGroup = transformGroup;
   parentComponent = parent;
 }
 /**
  * Set the transform group modified by the viewer behavior. Setting the
  * transform group to null disables the behavior until the transform group
  * is again set to an existing group.
  * 
  * @param transformGroup
  *            The new transform group to be modified by the behavior.
  */
 public void setTransformGroup(TransformGroup transformGroup) {
   subjectTransformGroup = transformGroup;
 }
 /**
  * Get the transform group modified by the viewer behavior.
  */
 public TransformGroup getTransformGroup() {
   return subjectTransformGroup;
 }
 /**
  * Sets the parent component who"s cursor will be changed during mouse
  * drags. If no component is given is given to the constructor, or set via
  * this method, no cursor changes will be done.
  * 
  * @param parent
  *            the AWT Component, such as a Frame, within which cursor
  *            changes should take place during mouse drags
  */
 public void setParentComponent(Component parent) {
   parentComponent = parent;
 }
 /*
  * Gets the parent frame within which the cursor changes during mouse drags.
  * 
  * @return the AWT Component, such as a Frame, within which cursor changes
  * should take place during mouse drags. Returns null if no parent is set.
  */
 public Component getParentComponent() {
   return parentComponent;
 }
 /**
  * Initialize the behavior.
  */
 public void initialize() {
   // Wakeup when the mouse is dragged or when a mouse button
   // is pressed or released.
   mouseEvents = new WakeupCriterion[3];
   mouseEvents[0] = new WakeupOnAWTEvent(MouseEvent.MOUSE_DRAGGED);
   mouseEvents[1] = new WakeupOnAWTEvent(MouseEvent.MOUSE_PRESSED);
   mouseEvents[2] = new WakeupOnAWTEvent(MouseEvent.MOUSE_RELEASED);
   mouseCriterion = new WakeupOr(mouseEvents);
   wakeupOn(mouseCriterion);
 }
 /**
  * Process a new wakeup. Interpret mouse button presses, releases, and mouse
  * drags.
  * 
  * @param criteria
  *            The wakeup criteria causing the behavior wakeup.
  */
 public void processStimulus(Enumeration criteria) {
   WakeupCriterion wakeup = null;
   AWTEvent[] event = null;
   int whichButton = BUTTONNONE;
   // Process all pending wakeups
   while (criteria.hasMoreElements()) {
     wakeup = (WakeupCriterion) criteria.nextElement();
     if (wakeup instanceof WakeupOnAWTEvent) {
       event = ((WakeupOnAWTEvent) wakeup).getAWTEvent();
       // Process all pending events
       for (int i = 0; i < event.length; i++) {
         if (event[i].getID() != MouseEvent.MOUSE_PRESSED
             && event[i].getID() != MouseEvent.MOUSE_RELEASED
             && event[i].getID() != MouseEvent.MOUSE_DRAGGED)
           // Ignore uninteresting mouse events
           continue;
         //
         // Regretably, Java event handling (or perhaps
         // underlying OS event handling) doesn"t always
         // catch button bounces (redundant presses and
         // releases), or order events so that the last
         // drag event is delivered before a release.
         // This means we can get stray events that we
         // filter out here.
         //
         if (event[i].getID() == MouseEvent.MOUSE_PRESSED
             && buttonPressed != BUTTONNONE)
           // Ignore additional button presses until a release
           continue;
         if (event[i].getID() == MouseEvent.MOUSE_RELEASED
             && buttonPressed == BUTTONNONE)
           // Ignore additional button releases until a press
           continue;
         if (event[i].getID() == MouseEvent.MOUSE_DRAGGED
             && buttonPressed == BUTTONNONE)
           // Ignore drags until a press
           continue;
         MouseEvent mev = (MouseEvent) event[i];
         int modifiers = mev.getModifiers();
         //
         // Unfortunately, the underlying event handling
         // doesn"t do a "grab" operation when a mouse button
         // is pressed. This means that once a button is
         // pressed, if another mouse button or a keyboard
         // modifier key is pressed, the delivered mouse event
         // will show that a different button is being held
         // down. For instance:
         //
         // Action Event
         //  Button 1 press Button 1 press
         //  Drag with button 1 down Button 1 drag
         //  ALT press -
         //  Drag with ALT & button 1 down Button 2 drag
         //  Button 1 release Button 2 release
         //
         // The upshot is that we can get a button press
         // without a matching release, and the button
         // associated with a drag can change mid-drag.
         //
         // To fix this, we watch for an initial button
         // press, and thenceforth consider that button
         // to be the one held down, even if additional
         // buttons get pressed, and despite what is
         // reported in the event. Only when a button is
         // released, do we end such a grab.
         //
         if (buttonPressed == BUTTONNONE) {
           // No button is pressed yet, figure out which
           // button is down now and how to direct events
           if (((modifiers & InputEvent.BUTTON3_MASK) != 0)
               || (((modifiers & InputEvent.BUTTON1_MASK) != 0) && ((modifiers & InputEvent.CTRL_MASK) == InputEvent.CTRL_MASK))) {
             // Button 3 activity (META or CTRL down)
             whichButton = BUTTON3;
           } else if ((modifiers & InputEvent.BUTTON2_MASK) != 0) {
             // Button 2 activity (ALT down)
             whichButton = BUTTON2;
           } else {
             // Button 1 activity (no modifiers down)
             whichButton = BUTTON1;
           }
           // If the event is to press a button, then
           // record that that button is now down
           if (event[i].getID() == MouseEvent.MOUSE_PRESSED)
             buttonPressed = whichButton;
         } else {
           // Otherwise a button was pressed earlier and
           // hasn"t been released yet. Assign all further
           // events to it, even if ALT, META, CTRL, or
           // another button has been pressed as well.
           whichButton = buttonPressed;
         }
         // Distribute the event
         switch (whichButton) {
         case BUTTON1:
           onButton1(mev);
           break;
         case BUTTON2:
           onButton2(mev);
           break;
         case BUTTON3:
           onButton3(mev);
           break;
         default:
           break;
         }
         // If the event is to release a button, then
         // record that that button is now up
         if (event[i].getID() == MouseEvent.MOUSE_RELEASED)
           buttonPressed = BUTTONNONE;
       }
       continue;
     }
     if (wakeup instanceof WakeupOnElapsedFrames) {
       onElapsedFrames((WakeupOnElapsedFrames) wakeup);
       continue;
     }
   }
   // Reschedule us for another wakeup
   wakeupOn(mouseCriterion);
 }
 /**
  * Default X and Y rotation factors, and XYZ translation factors.
  */
 public static final double DEFAULT_XROTATION_FACTOR = 0.02;
 public static final double DEFAULT_YROTATION_FACTOR = 0.005;
 public static final double DEFAULT_XTRANSLATION_FACTOR = 0.02;
 public static final double DEFAULT_YTRANSLATION_FACTOR = 0.02;
 public static final double DEFAULT_ZTRANSLATION_FACTOR = 0.04;
 protected double XRotationFactor = DEFAULT_XROTATION_FACTOR;
 protected double YRotationFactor = DEFAULT_YROTATION_FACTOR;
 protected double XTranslationFactor = DEFAULT_XTRANSLATION_FACTOR;
 protected double YTranslationFactor = DEFAULT_YTRANSLATION_FACTOR;
 protected double ZTranslationFactor = DEFAULT_ZTRANSLATION_FACTOR;
 /**
  * Set the X rotation scaling factor for X-axis rotations.
  * 
  * @param factor
  *            The new scaling factor.
  */
 public void setXRotationFactor(double factor) {
   XRotationFactor = factor;
 }
 /**
  * Get the current X rotation scaling factor for X-axis rotations.
  */
 public double getXRotationFactor() {
   return XRotationFactor;
 }
 /**
  * Set the Y rotation scaling factor for Y-axis rotations.
  * 
  * @param factor
  *            The new scaling factor.
  */
 public void setYRotationFactor(double factor) {
   YRotationFactor = factor;
 }
 /**
  * Get the current Y rotation scaling factor for Y-axis rotations.
  */
 public double getYRotationFactor() {
   return YRotationFactor;
 }
 /**
  * Set the X translation scaling factor for X-axis translations.
  * 
  * @param factor
  *            The new scaling factor.
  */
 public void setXTranslationFactor(double factor) {
   XTranslationFactor = factor;
 }
 /**
  * Get the current X translation scaling factor for X-axis translations.
  */
 public double getXTranslationFactor() {
   return XTranslationFactor;
 }
 /**
  * Set the Y translation scaling factor for Y-axis translations.
  * 
  * @param factor
  *            The new scaling factor.
  */
 public void setYTranslationFactor(double factor) {
   YTranslationFactor = factor;
 }
 /**
  * Get the current Y translation scaling factor for Y-axis translations.
  */
 public double getYTranslationFactor() {
   return YTranslationFactor;
 }
 /**
  * Set the Z translation scaling factor for Z-axis translations.
  * 
  * @param factor
  *            The new scaling factor.
  */
 public void setZTranslationFactor(double factor) {
   ZTranslationFactor = factor;
 }
 /**
  * Get the current Z translation scaling factor for Z-axis translations.
  */
 public double getZTranslationFactor() {
   return ZTranslationFactor;
 }
 /**
  * Respond to a button1 event (press, release, or drag).
  * 
  * @param mouseEvent
  *            A MouseEvent to respond to.
  */
 public abstract void onButton1(MouseEvent mouseEvent);
 /**
  * Respond to a button2 event (press, release, or drag).
  * 
  * @param mouseEvent
  *            A MouseEvent to respond to.
  */
 public abstract void onButton2(MouseEvent mouseEvent);
 /**
  * Responed to a button3 event (press, release, or drag).
  * 
  * @param mouseEvent
  *            A MouseEvent to respond to.
  */
 public abstract void onButton3(MouseEvent mouseEvent);
 /**
  * Respond to an elapsed frames event (assuming subclass has set up a wakeup
  * criterion for it).
  * 
  * @param time
  *            A WakeupOnElapsedFrames criterion to respond to.
  */
 public abstract void onElapsedFrames(WakeupOnElapsedFrames timeEvent);

} // //CLASS //NameValue - create a handy name-value pair // //DESCRIPTION //It is frequently handy to have one or more name-value pairs //with which to store named colors, named positions, named textures, //and so forth. Several of the examples use this class. // //AUTHOR //David R. Nadeau / San Diego Supercomputer Center // class NameValue {

 public String name;
 public Object value;
 public NameValue(String n, Object v) {
   name = n;
   value = v;
 }

}


      </source>
   
  
 
  



ExPointLight - illustrate use of point lights

   <source lang="java">

// //CLASS //ExPointLight - illustrate use of point lights // //LESSON //Add a PointLight node to illuminate a scene. // //SEE ALSO //ExAmbientLight //ExDirectionalLight //ExSpotLight // //AUTHOR //David R. Nadeau / San Diego Supercomputer Center // import java.applet.Applet; import java.awt.AWTEvent; import java.awt.BorderLayout; import java.awt.CheckboxMenuItem; import java.awt.ruponent; import java.awt.Cursor; import java.awt.Frame; import java.awt.Menu; import java.awt.MenuBar; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.InputEvent; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.event.MouseEvent; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.io.File; import java.util.Enumeration; import java.util.EventListener; 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.ColoringAttributes; import javax.media.j3d.DirectionalLight; import javax.media.j3d.GeometryArray; import javax.media.j3d.Group; import javax.media.j3d.Light; import javax.media.j3d.LineArray; import javax.media.j3d.LineAttributes; import javax.media.j3d.Material; import javax.media.j3d.PointLight; import javax.media.j3d.Shape3D; import javax.media.j3d.Transform3D; import javax.media.j3d.TransformGroup; import javax.media.j3d.WakeupCriterion; import javax.media.j3d.WakeupOnAWTEvent; import javax.media.j3d.WakeupOnElapsedFrames; import javax.media.j3d.WakeupOr; import javax.vecmath.Color3f; import javax.vecmath.Matrix4d; import javax.vecmath.Matrix4f; import javax.vecmath.Point3d; import javax.vecmath.Point3f; import javax.vecmath.Vector3d; import javax.vecmath.Vector3f; import com.sun.j3d.utils.geometry.Cone; import com.sun.j3d.utils.geometry.Primitive; import com.sun.j3d.utils.geometry.Sphere; import com.sun.j3d.utils.universe.PlatformGeometry; import com.sun.j3d.utils.universe.SimpleUniverse; import com.sun.j3d.utils.universe.Viewer; import com.sun.j3d.utils.universe.ViewingPlatform; public class ExPointLight extends Java3DFrame {

 //--------------------------------------------------------------
 //  SCENE CONTENT
 //--------------------------------------------------------------
 //
 //  Nodes (updated via menu)
 //
 private PointLight light = null;
 //
 //  Build scene
 //
 public Group buildScene() {
   // Get the current color, position, and attenuation
   Color3f color = (Color3f) colors[currentColor].value;
   Point3f pos = (Point3f) positions[currentPosition].value;
   Point3f atten = (Point3f) attenuations[currentAttenuation].value;
   // Turn off the example headlight
   setHeadlightEnable(false);
   // Build the scene root
   Group scene = new Group();
   // BEGIN EXAMPLE TOPIC
   // Create influencing bounds
   BoundingSphere worldBounds = new BoundingSphere(new Point3d(0.0, 0.0,
       0.0), // Center
       1000.0); // Extent
   // Set the light color and its influencing bounds
   light = new PointLight();
   light.setEnable(lightOnOff);
   light.setColor(color);
   light.setPosition(pos);
   light.setAttenuation(atten);
   light.setCapability(PointLight.ALLOW_STATE_WRITE);
   light.setCapability(PointLight.ALLOW_COLOR_WRITE);
   light.setCapability(PointLight.ALLOW_POSITION_WRITE);
   light.setCapability(PointLight.ALLOW_ATTENUATION_WRITE);
   light.setInfluencingBounds(worldBounds);
   scene.addChild(light);
   // END EXAMPLE TOPIC
   // Build foreground geometry
   scene.addChild(new SphereGroup());
   // Add arrows in a fan to show light ray directions
   scene.addChild(buildArrows());
   return scene;
 }
 //--------------------------------------------------------------
 //  FOREGROUND AND ANNOTATION CONTENT
 //--------------------------------------------------------------
 //
 //  Create a fan of annotation arrows aiming in all directions,
 //  but in the XY plane. Next, build an array of Transform3D"s,
 //  one for each of the light positions shown on the positions
 //  menu. Save these Transform3Ds and a top-level TransformGroup
 //  surrounding the arrows. Later, when the user selects a new
 //  light position, we poke the corresponding Transform3D into
 //  the TransformGroup to cause the arrows to move to a new
 //  position.
 //
 private Transform3D[] arrowPositionTransforms = null;
 private TransformGroup arrowPositionTransformGroup = null;
 private Group buildArrows() {
   // Create a transform group surrounding the arrows.
   // Enable writing of its transform.
   arrowPositionTransformGroup = new TransformGroup();
   arrowPositionTransformGroup
       .setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
   // Create a group of arrows in a fan and add that group
   // to the transform group.
   AnnotationArrowFan af = new AnnotationArrowFan(0.0f, 0.0f, 0.0f, // center
       // position
       2.5f, // arrow length
       0.0f, // start angle
       (float) (Math.PI * 2.0 * 7.0 / 8.0),// end angle
       8); // number of arrows
   arrowPositionTransformGroup.addChild(af);
   // Create a set of Transform3Ds for the different
   // arrow positions.
   arrowPositionTransforms = new Transform3D[positions.length];
   Point3f pos;
   Vector3f v = new Vector3f();
   for (int i = 0; i < positions.length; i++) {
     // Create a Transform3D, setting its translation.
     arrowPositionTransforms[i] = new Transform3D();
     pos = (Point3f) positions[i].value;
     v.set(pos);
     arrowPositionTransforms[i].setTranslation(v);
   }
   // Set the initial transform to be the current position
   arrowPositionTransformGroup
       .setTransform(arrowPositionTransforms[currentPosition]);
   return arrowPositionTransformGroup;
 }
 //--------------------------------------------------------------
 //  USER INTERFACE
 //--------------------------------------------------------------
 //
 //  Main
 //
 public static void main(String[] args) {
   ExPointLight ex = new ExPointLight();
   ex.initialize(args);
   ex.buildUniverse();
   ex.showFrame();
 }
 //  On/off choices
 private boolean lightOnOff = true;
 private CheckboxMenuItem lightOnOffMenu;
 //  Color menu choices
 private NameValue[] colors = { new NameValue("White", White),
     new NameValue("Gray", Gray), new NameValue("Black", Black),
     new NameValue("Red", Red), new NameValue("Yellow", Yellow),
     new NameValue("Green", Green), new NameValue("Cyan", Cyan),
     new NameValue("Blue", Blue), new NameValue("Magenta", Magenta), };
 private int currentColor = 0;
 private CheckboxMenu colorMenu = null;
 //  Position menu choices
 private NameValue[] positions = { new NameValue("Origin", Origin),
     new NameValue("+X", PlusX), new NameValue("-X", MinusX),
     new NameValue("+Y", PlusY), new NameValue("-Y", MinusY),
     new NameValue("+Z", PlusZ), new NameValue("-Z", MinusZ), };
 private int currentPosition = 0;
 private CheckboxMenu positionMenu = null;
 //  Attenuation menu choices
 private NameValue[] attenuations = {
     new NameValue("Constant", new Point3f(1.0f, 0.0f, 0.0f)),
     new NameValue("Linear", new Point3f(0.0f, 1.0f, 0.0f)),
     new NameValue("Quadratic", new Point3f(0.0f, 0.0f, 1.0f)), };
 private int currentAttenuation = 0;
 private CheckboxMenu attenuationMenu = null;
 //
 //  Initialize the GUI (application and applet)
 //
 public void initialize(String[] args) {
   // Initialize the window, menubar, etc.
   super.initialize(args);
   exampleFrame.setTitle("Java 3D Point Light Example");
   //
   //  Add a menubar menu to change node parameters
   //    Light on/off
   //    Color -->
   //    Position -->
   //    Attenuation -->
   //
   Menu m = new Menu("PointLight");
   lightOnOffMenu = new CheckboxMenuItem("Light on/off", lightOnOff);
   lightOnOffMenu.addItemListener(this);
   m.add(lightOnOffMenu);
   colorMenu = new CheckboxMenu("Color", colors, currentColor, this);
   m.add(colorMenu);
   positionMenu = new CheckboxMenu("Position", positions, currentPosition,
       this);
   m.add(positionMenu);
   attenuationMenu = new CheckboxMenu("Attenuation", attenuations,
       currentAttenuation, this);
   m.add(attenuationMenu);
   exampleMenuBar.add(m);
 }
 //
 //  Handle checkboxes and menu choices
 //
 public void checkboxChanged(CheckboxMenu menu, int check) {
   if (menu == colorMenu) {
     // Change the light color
     currentColor = check;
     Color3f color = (Color3f) colors[check].value;
     light.setColor(color);
     return;
   }
   if (menu == positionMenu) {
     // Change the light position
     currentPosition = check;
     Point3f pos = (Point3f) positions[check].value;
     light.setPosition(pos);
     // Change the arrow group position
     arrowPositionTransformGroup
         .setTransform(arrowPositionTransforms[check]);
     return;
   }
   if (menu == attenuationMenu) {
     // Change the light attenuation
     currentAttenuation = check;
     Point3f atten = (Point3f) attenuations[check].value;
     light.setAttenuation(atten);
     return;
   }
   // Handle all other checkboxes
   super.checkboxChanged(menu, check);
 }
 public void itemStateChanged(ItemEvent event) {
   Object src = event.getSource();
   if (src == lightOnOffMenu) {
     // Turn the light on or off
     lightOnOff = lightOnOffMenu.getState();
     light.setEnable(lightOnOff);
     return;
   }
   // Handle all other checkboxes
   super.itemStateChanged(event);
 }

} // //CLASS //AnnotationArrowFan - A group of arrows in a fan // //DESCRIPTION //This class creates one or more 3D, unlighted arrows arranged in a //fan around the xyz position. Such arrow fans can be used to indicate //point light directions, and so forth. // //The arrow fan is drawn in the XY plane, pointing right (middle arrow). //The fan origin, arrow length, start and end angles, and number of //arrows all may be controlled. // //SEE ALSO //AnnotationArrow //AnnotationArrowGroup // //AUTHOR //David R. Nadeau / San Diego Supercomputer Center // // class AnnotationArrowFan extends Group {

 // 3D nodes
 AnnotationArrow[] arrows;
 //  Constructors
 public AnnotationArrowFan() {
   //    xyz length start/end angles count
   this(0.0f, 0.0f, 0.0f, 1.0f, 1.571f, -1.571f, 5);
 }
 public AnnotationArrowFan(float x, float y, float z, float length,
     float startAngle, float endAngle, int count) {
   arrows = new AnnotationArrow[count];
   float x2, y2;
   float angle = startAngle;
   float deltaAngle = (endAngle - startAngle) / (float) (count - 1);
   for (int i = 0; i < count; i++) {
     x2 = (float) (length * Math.cos(angle));
     y2 = (float) (length * Math.sin(angle));
     arrows[i] = new AnnotationArrow(x, y, z, x2, y2, z);
     addChild(arrows[i]);
     angle += deltaAngle;
   }
 }

} // //CLASS //AnnotationArrowGroup - A group of parallel arrows // //DESCRIPTION //This class creates one or more parallel 3D, unlighted arrows. //Such arrow groups can be used to indicate directional light //directions, and so forth. // //The arrow group is drawn in the XY plane, pointing right. //The X start and end values, and the Y start and end values //can be set, along with the count of the number of arrows to //build. // //SEE ALSO //AnnotationArrow //AnnotationArrowFan // //AUTHOR //David R. Nadeau / San Diego Supercomputer Center // // class AnnotationArrowGroup extends Group {

 // 3D nodes
 AnnotationArrow[] arrows;
 //  Constructors
 public AnnotationArrowGroup() {
   //    xStart xEnd yStart yEnd count
   this(-1.0f, 1.0f, 1.0f, -1.0f, 3);
 }
 public AnnotationArrowGroup(float xStart, float xEnd, float yStart,
     float yEnd, int count) {
   arrows = new AnnotationArrow[count];
   float y = yStart;
   float deltaY = (yEnd - yStart) / (float) (count - 1);
   for (int i = 0; i < count; i++) {
     arrows[i] = new AnnotationArrow(xStart, y, 0.0f, xEnd, y, 0.0f);
     addChild(arrows[i]);
     y += deltaY;
   }
 }

} // //CLASS //AnnotationArrow - 3D arrow used for annotation & diagrams // //DESCRIPTION //This class creates a 3D, unlighted line between two 3D coordinates //plus a cone-shaped arrow at the line"s endpoint. The line"s width //and color can be controlled. The arrow head"s width and length //can be controlled. // //SEE ALSO //AnnotationLine //AnnotationAxes //AnnotationArrowFan //AnnotationArrowGroup // //AUTHOR //David R. Nadeau / San Diego Supercomputer Center // class AnnotationArrow extends AnnotationLine {

 // Parameters
 private Color3f arrowColor = new Color3f(1.0f, 1.0f, 1.0f);
 private float arrowRadius = 0.1f;
 private float arrowLength = 0.20f;
 private float lineWidth = 3.0f;
 private int radialDivisions = 8;
 private int sideDivisions = 1;
 // 3D Nodes
 private Cone arrowHead = null;
 private Appearance arrowAppearance = null;
 private TransformGroup arrowTrans = null;
 private ColoringAttributes coloringAttributes = null;
 //
 //  Construct a straight line
 //
 public AnnotationArrow(float x2, float y2, float z2) {
   //    origin to given coordinate
   this(0.0f, 0.0f, 0.0f, x2, y2, z2);
 }
 public AnnotationArrow(float x, float y, float z, float x2, float y2,
     float z2) {
   super(x, y, z, x2, y2, z2);
   setLineWidth(lineWidth);
   // Compute the length and direction of the line
   float deltaX = x2 - x;
   float deltaY = y2 - y;
   float deltaZ = z2 - z;
   float theta = -(float) Math.atan2(deltaZ, deltaX);
   float phi = (float) Math.atan2(deltaY, deltaX);
   if (deltaX < 0.0f) {
     phi = (float) Math.PI - phi;
   }
   // Compute a matrix to rotate a cone to point in the line"s
   // direction, then place the cone at the line"s endpoint.
   Matrix4f mat = new Matrix4f();
   Matrix4f mat2 = new Matrix4f();
   mat.setIdentity();
   // Move to the endpoint of the line
   mat2.setIdentity();
   mat2.setTranslation(new Vector3f(x2, y2, z2));
   mat.mul(mat2);
   // Spin around Y
   mat2.setIdentity();
   mat2.rotY(theta);
   mat.mul(mat2);
   // Tilt up or down around Z
   mat2.setIdentity();
   mat2.rotZ(phi);
   mat.mul(mat2);
   // Tilt cone to point right
   mat2.setIdentity();
   mat2.rotZ(-1.571f);
   mat.mul(mat2);
   arrowTrans = new TransformGroup();
   arrowTrans.setCapability(Group.ALLOW_CHILDREN_WRITE);
   Transform3D trans = new Transform3D(mat);
   arrowTrans.setTransform(trans);
   // Create an appearance
   arrowAppearance = new Appearance();
   arrowAppearance
       .setCapability(Appearance.ALLOW_COLORING_ATTRIBUTES_WRITE);
   getLineColor(arrowColor);
   coloringAttributes = new ColoringAttributes();
   coloringAttributes.setColor(arrowColor);
   coloringAttributes.setShadeModel(ColoringAttributes.SHADE_FLAT);
   arrowAppearance.setColoringAttributes(coloringAttributes);
   // Build a cone for the arrow head
   arrowHead = new Cone(arrowRadius, // base radius
       arrowLength, // height
       0, // don"t generate normals
       radialDivisions, // divisions radially
       sideDivisions, // divisions vertically
       arrowAppearance); // appearance
   arrowTrans.addChild(arrowHead);
   addChild(arrowTrans);
 }
 //
 //  Control the arrow head size
 //
 public void setArrowHeadRadius(float radius) {
   arrowRadius = radius;
   arrowTrans.removeChild(0);
   arrowHead = new Cone(arrowRadius, // base radius
       arrowLength, // height
       0, // don"t generate normals
       radialDivisions, // divisions radially
       sideDivisions, // divisions vertically
       arrowAppearance); // appearance
   arrowTrans.addChild(arrowHead);
 }
 public void setArrowHeadLength(float length) {
   arrowLength = length;
   arrowTrans.removeChild(0);
   arrowHead = new Cone(arrowRadius, // base radius
       arrowLength, // height
       0, // don"t generate normals
       radialDivisions, // divisions radially
       sideDivisions, // divisions vertically
       arrowAppearance); // appearance
   arrowTrans.addChild(arrowHead);
 }
 public float getArrowHeadRadius() {
   return arrowRadius;
 }
 public float getArrowHeadLength() {
   return arrowLength;
 }
 //
 //  Control the line color
 //
 public void setLineColor(Color3f color) {
   super.setLineColor(color);
   getLineColor(arrowColor);
   coloringAttributes.setColor(arrowColor);
   arrowAppearance.setColoringAttributes(coloringAttributes);
   arrowHead.setAppearance(arrowAppearance);
 }
 public void setLineColor(float r, float g, float b) {
   super.setLineColor(r, g, b);
   getLineColor(arrowColor);
   coloringAttributes.setColor(arrowColor);
   arrowAppearance.setColoringAttributes(coloringAttributes);
   arrowHead.setAppearance(arrowAppearance);
 }
 public void setLineColor(float[] color) {
   super.setLineColor(color);
   getLineColor(arrowColor);
   coloringAttributes.setColor(arrowColor);
   arrowAppearance.setColoringAttributes(coloringAttributes);
   arrowHead.setAppearance(arrowAppearance);
 }
 //
 //  Control the appearance
 //
 public void setAppearance(Appearance app) {
   super.setAppearance(app);
   arrowAppearance = app;
   arrowAppearance
       .setCapability(Appearance.ALLOW_COLORING_ATTRIBUTES_WRITE);
   arrowAppearance.setColoringAttributes(coloringAttributes);
   arrowHead.setAppearance(arrowAppearance);
 }
 //
 //  Provide info on the shape and geometry
 //
 public Shape3D getShape(int partid) {
   if (partid == Cone.BODY)
     return arrowHead.getShape(Cone.BODY);
   else if (partid == Cone.CAP)
     return arrowHead.getShape(Cone.CAP);
   else
     return super.getShape(partid);
 }
 public int getNumTriangles() {
   return arrowHead.getNumTriangles();
 }
 public int getNumVertices() {
   return arrowHead.getNumVertices() + super.getNumVertices();
 }

} // //CLASS //AnnotationLine - 3D line used for annotation & diagrams // //DESCRIPTION //This class creates a 3D, unlighted line between two 3D coordinates. //The line"s width and color can be controlled. // //SEE ALSO //AnnotationArrow // //AUTHOR //David R. Nadeau / San Diego Supercomputer Center // // class AnnotationLine extends Primitive {

 // Parameters
 private float lineWidth = 1;
 private Color3f lineColor = new Color3f(1.0f, 1.0f, 1.0f);
 // 3D nodes
 private Shape3D shape = null;
 private LineAttributes lineAttributes = null;
 private ColoringAttributes coloringAttributes = null;
 private LineArray line = null;
 protected Appearance mainAppearance = null;
 //
 //  Construct a straight line
 //
 public AnnotationLine(float x2, float y2, float z2) {
   //    origin to given coordinate
   this(0.0f, 0.0f, 0.0f, x2, y2, z2);
 }
 public AnnotationLine(float x, float y, float z, float x2, float y2,
     float z2) {
   float[] coord = new float[3];
   float[] texcoord = new float[2];
   // Build a shape
   shape = new Shape3D();
   shape.setCapability(Shape3D.ALLOW_APPEARANCE_WRITE);
   // Create geometry for a 2-vertex straight line
   line = new LineArray(2, GeometryArray.COORDINATES
       | GeometryArray.TEXTURE_COORDINATE_2);
   line.setCapability(GeometryArray.ALLOW_COLOR_WRITE);
   // Starting point
   coord[0] = x;
   coord[1] = y;
   coord[2] = z;
   texcoord[0] = 0.0f;
   texcoord[1] = 0.0f;
   line.setCoordinate(0, coord);
   line.setTextureCoordinate(0, texcoord);
   // Ending point
   coord[0] = x2;
   coord[1] = y2;
   coord[2] = z2;
   texcoord[0] = 1.0f;
   texcoord[1] = 0.0f;
   line.setCoordinate(1, coord);
   line.setTextureCoordinate(1, texcoord);
   shape.setGeometry(line);
   // Create an appearance
   mainAppearance = new Appearance();
   mainAppearance.setCapability(Appearance.ALLOW_LINE_ATTRIBUTES_WRITE);
   mainAppearance
       .setCapability(Appearance.ALLOW_COLORING_ATTRIBUTES_WRITE);
   lineAttributes = new LineAttributes();
   lineAttributes.setLineWidth(lineWidth);
   mainAppearance.setLineAttributes(lineAttributes);
   coloringAttributes = new ColoringAttributes();
   coloringAttributes.setColor(lineColor);
   coloringAttributes.setShadeModel(ColoringAttributes.SHADE_FLAT);
   mainAppearance.setColoringAttributes(coloringAttributes);
   addChild(shape);
 }
 //
 //  Control the line width
 //
 public float getLineWidth() {
   return lineWidth;
 }
 public void setLineWidth(float width) {
   lineWidth = width;
   lineAttributes.setLineWidth(lineWidth);
   mainAppearance.setLineAttributes(lineAttributes);
   shape.setAppearance(mainAppearance);
 }
 //
 //  Control the line color
 //
 public void getLineColor(Color3f color) {
   lineColor.get(color);
 }
 public void getLineColor(float[] color) {
   lineColor.get(color);
 }
 public void setLineColor(Color3f color) {
   lineColor.set(color);
   coloringAttributes.setColor(lineColor);
   mainAppearance.setColoringAttributes(coloringAttributes);
   shape.setAppearance(mainAppearance);
 }
 public void setLineColor(float r, float g, float b) {
   lineColor.set(r, g, b);
   coloringAttributes.setColor(lineColor);
   mainAppearance.setColoringAttributes(coloringAttributes);
   shape.setAppearance(mainAppearance);
 }
 public void setLineColor(float[] color) {
   lineColor.set(color);
   coloringAttributes.setColor(lineColor);
   mainAppearance.setColoringAttributes(coloringAttributes);
   shape.setAppearance(mainAppearance);
 }
 //
 //  Control the appearance
 //
 public void setAppearance(Appearance app) {
   mainAppearance = app;
   mainAppearance.setCapability(Appearance.ALLOW_LINE_ATTRIBUTES_WRITE);
   mainAppearance
       .setCapability(Appearance.ALLOW_COLORING_ATTRIBUTES_WRITE);
   mainAppearance.setLineAttributes(lineAttributes);
   mainAppearance.setColoringAttributes(coloringAttributes);
   shape.setAppearance(mainAppearance);
 }
 //
 //  Provide info on the shape and geometry
 //
 public Shape3D getShape(int partid) {
   return shape;
 }
 public int getNumTriangles() {
   return 0;
 }
 public int getNumVertices() {
   return 2;
 }
 /*
  * (non-Javadoc)
  * 
  * @see com.sun.j3d.utils.geometry.Primitive#getAppearance(int)
  */
 public Appearance getAppearance(int arg0) {
   // TODO Auto-generated method stub
   return null;
 }

} /*

* @(#)SphereGroup.java 1.7 02/04/01 15:04:02
* 
* 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.
*/

class SphereGroup extends Group {

 Shape3D[] shapes;
 int numShapes = 0;
 //  Constructors
 public SphereGroup() {
   //    radius x,y spacing x,y count appearance
   this(0.25f, 0.75f, 0.75f, 5, 5, null, false);
 }
 public SphereGroup(Appearance app) {
   //    radius x,y spacing x,y count appearance
   this(0.25f, 0.75f, 0.75f, 5, 5, app, false);
 }
 public SphereGroup(float radius, float xSpacing, float ySpacing,
     int xCount, int yCount, boolean overrideflag) {
   this(radius, xSpacing, ySpacing, xCount, yCount, null, overrideflag);
 }
 public SphereGroup(float radius, float xSpacing, float ySpacing,
     int xCount, int yCount, Appearance app, boolean overrideflag) {
   if (app == null) {
     app = new Appearance();
     Material material = new Material();
     material.setDiffuseColor(new Color3f(0.8f, 0.8f, 0.8f));
     material.setSpecularColor(new Color3f(0.0f, 0.0f, 0.0f));
     material.setShininess(0.0f);
     app.setMaterial(material);
   }
   double xStart = -xSpacing * (double) (xCount - 1) / 2.0;
   double yStart = -ySpacing * (double) (yCount - 1) / 2.0;
   Sphere sphere = null;
   TransformGroup trans = null;
   Transform3D t3d = new Transform3D();
   Vector3d vec = new Vector3d();
   double x, y = yStart, z = 0.0;
   shapes = new Shape3D[xCount * yCount];
   for (int i = 0; i < yCount; i++) {
     x = xStart;
     for (int j = 0; j < xCount; j++) {
       vec.set(x, y, z);
       t3d.setTranslation(vec);
       trans = new TransformGroup(t3d);
       addChild(trans);
       sphere = new Sphere(radius, // sphere radius
           Primitive.GENERATE_NORMALS, // generate normals
           16, // 16 divisions radially
           app); // it"s appearance
       trans.addChild(sphere);
       x += xSpacing;
       shapes[numShapes] = sphere.getShape();
       if (overrideflag)
         shapes[numShapes]
             .setCapability(Shape3D.ALLOW_APPEARANCE_OVERRIDE_WRITE);
       numShapes++;
     }
     y += ySpacing;
   }
 }
 Shape3D[] getShapes() {
   return shapes;
 }

} /**

* The Example class is a base class extended by example applications. The class
* provides basic features to create a top-level frame, add a menubar and
* Canvas3D, build the universe, set up "examine" and "walk" style navigation
* behaviors, and provide hooks so that subclasses can add 3D content to the
* example"s universe.
* <P>
* Using this Example class simplifies the construction of example applications,
* enabling the author to focus upon 3D content and not the busywork of creating
* windows, menus, and universes.
* 
* @version 1.0, 98/04/16
* @author David R. Nadeau, San Diego Supercomputer Center
*/

class Java3DFrame extends Applet implements WindowListener, ActionListener,

   ItemListener, CheckboxMenuListener {
 //  Navigation types
 public final static int Walk = 0;
 public final static int Examine = 1;
 //  Should the scene be compiled?
 private boolean shouldCompile = true;
 //  GUI objects for our subclasses
 protected Java3DFrame example = null;
 protected Frame exampleFrame = null;
 protected MenuBar exampleMenuBar = null;
 protected Canvas3D exampleCanvas = null;
 protected TransformGroup exampleViewTransform = null;
 protected TransformGroup exampleSceneTransform = null;
 protected boolean debug = false;
 //  Private GUI objects and state
 private boolean headlightOnOff = true;
 private int navigationType = Examine;
 private CheckboxMenuItem headlightMenuItem = null;
 private CheckboxMenuItem walkMenuItem = null;
 private CheckboxMenuItem examineMenuItem = null;
 private DirectionalLight headlight = null;
 private ExamineViewerBehavior examineBehavior = null;
 private WalkViewerBehavior walkBehavior = null;
 //--------------------------------------------------------------
 //  ADMINISTRATION
 //--------------------------------------------------------------
 /**
  * The main program entry point when invoked as an application. Each example
  * application that extends this class must define their own main.
  * 
  * @param args
  *            a String array of command-line arguments
  */
 public static void main(String[] args) {
   Java3DFrame ex = new Java3DFrame();
   ex.initialize(args);
   ex.buildUniverse();
   ex.showFrame();
 }
 /**
  * Constructs a new Example object.
  * 
  * @return a new Example that draws no 3D content
  */
 public Java3DFrame() {
   // Do nothing
 }
 /**
  * Initializes the application when invoked as an applet.
  */
 public void init() {
   // Collect properties into String array
   String[] args = new String[2];
   // NOTE: to be done still...
   this.initialize(args);
   this.buildUniverse();
   this.showFrame();
   // NOTE: add something to the browser page?
 }
 /**
  * Initializes the Example by parsing command-line arguments, building an
  * AWT Frame, constructing a menubar, and creating the 3D canvas.
  * 
  * @param args
  *            a String array of command-line arguments
  */
 protected void initialize(String[] args) {
   example = this;
   // Parse incoming arguments
   parseArgs(args);
   // Build the frame
   if (debug)
     System.err.println("Building GUI...");
   exampleFrame = new Frame();
   exampleFrame.setSize(640, 480);
   exampleFrame.setTitle("Java 3D Example");
   exampleFrame.setLayout(new BorderLayout());
   // Set up a close behavior
   exampleFrame.addWindowListener(this);
   // Create a canvas
   exampleCanvas = new Canvas3D(null);
   exampleCanvas.setSize(630, 460);
   exampleFrame.add("Center", exampleCanvas);
   // Build the menubar
   exampleMenuBar = this.buildMenuBar();
   exampleFrame.setMenuBar(exampleMenuBar);
   // Pack
   exampleFrame.pack();
   exampleFrame.validate();
   //    exampleFrame.setVisible( true );
 }
 /**
  * Parses incoming command-line arguments. Applications that subclass this
  * class may override this method to support their own command-line
  * arguments.
  * 
  * @param args
  *            a String array of command-line arguments
  */
 protected void parseArgs(String[] args) {
   for (int i = 0; i < args.length; i++) {
     if (args[i].equals("-d"))
       debug = true;
   }
 }
 //--------------------------------------------------------------
 //  SCENE CONTENT
 //--------------------------------------------------------------
 /**
  * Builds the 3D universe by constructing a virtual universe (via
  * SimpleUniverse), a view platform (via SimpleUniverse), and a view (via
  * SimpleUniverse). A headlight is added and a set of behaviors initialized
  * to handle navigation types.
  */
 protected void buildUniverse() {
   //
   //  Create a SimpleUniverse object, which builds:
   //
   //    - a Locale using the given hi-res coordinate origin
   //
   //    - a ViewingPlatform which in turn builds:
   //          - a MultiTransformGroup with which to move the
   //            the ViewPlatform about
   //
   //          - a ViewPlatform to hold the view
   //
   //          - a BranchGroup to hold avatar geometry (if any)
   //
   //          - a BranchGroup to hold view platform
   //            geometry (if any)
   //
   //    - a Viewer which in turn builds:
   //          - a PhysicalBody which characterizes the user"s
   //            viewing preferences and abilities
   //
   //          - a PhysicalEnvironment which characterizes the
   //            user"s rendering hardware and software
   //
   //          - a JavaSoundMixer which initializes sound
   //            support within the 3D environment
   //
   //          - a View which renders the scene into a Canvas3D
   //
   //  All of these actions could be done explicitly, but
   //  using the SimpleUniverse utilities simplifies the code.
   //
   if (debug)
     System.err.println("Building scene graph...");
   SimpleUniverse universe = new SimpleUniverse(null, // Hi-res coordinate
       // for the origin -
       // use default
       1, // Number of transforms in MultiTransformGroup
       exampleCanvas, // Canvas3D into which to draw
       null); // URL for user configuration file - use defaults
   //
   //  Get the viewer and create an audio device so that
   //  sound will be enabled in this content.
   //
   Viewer viewer = universe.getViewer();
   viewer.createAudioDevice();
   //
   //  Get the viewing platform created by SimpleUniverse.
   //  From that platform, get the inner-most TransformGroup
   //  in the MultiTransformGroup. That inner-most group
   //  contains the ViewPlatform. It is this inner-most
   //  TransformGroup we need in order to:
   //
   //    - add a "headlight" that always aims forward from
   //       the viewer
   //
   //    - change the viewing direction in a "walk" style
   //
   //  The inner-most TransformGroup"s transform will be
   //  changed by the walk behavior (when enabled).
   //
   ViewingPlatform viewingPlatform = universe.getViewingPlatform();
   exampleViewTransform = viewingPlatform.getViewPlatformTransform();
   //
   //  Create a "headlight" as a forward-facing directional light.
   //  Set the light"s bounds to huge. Since we want the light
   //  on the viewer"s "head", we need the light within the
   //  TransformGroup containing the ViewPlatform. The
   //  ViewingPlatform class creates a handy hook to do this
   //  called "platform geometry". The PlatformGeometry class is
   //  subclassed off of BranchGroup, and is intended to contain
   //  a description of the 3D platform itself... PLUS a headlight!
   //  So, to add the headlight, create a new PlatformGeometry group,
   //  add the light to it, then add that platform geometry to the
   //  ViewingPlatform.
   //
   BoundingSphere allBounds = new BoundingSphere(
       new Point3d(0.0, 0.0, 0.0), 100000.0);
   PlatformGeometry pg = new PlatformGeometry();
   headlight = new DirectionalLight();
   headlight.setColor(White);
   headlight.setDirection(new Vector3f(0.0f, 0.0f, -1.0f));
   headlight.setInfluencingBounds(allBounds);
   headlight.setCapability(Light.ALLOW_STATE_WRITE);
   pg.addChild(headlight);
   viewingPlatform.setPlatformGeometry(pg);
   //
   //  Create the 3D content BranchGroup, containing:
   //
   //    - a TransformGroup who"s transform the examine behavior
   //      will change (when enabled).
   //
   //    - 3D geometry to view
   //
   // Build the scene root
   BranchGroup sceneRoot = new BranchGroup();
   // Build a transform that we can modify
   exampleSceneTransform = new TransformGroup();
   exampleSceneTransform
       .setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
   exampleSceneTransform
       .setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
   exampleSceneTransform.setCapability(Group.ALLOW_CHILDREN_EXTEND);
   //
   //  Build the scene, add it to the transform, and add
   //  the transform to the scene root
   //
   if (debug)
     System.err.println("  scene...");
   Group scene = this.buildScene();
   exampleSceneTransform.addChild(scene);
   sceneRoot.addChild(exampleSceneTransform);
   //
   //  Create a pair of behaviors to implement two navigation
   //  types:
   //
   //    - "examine": a style where mouse drags rotate about
   //      the scene"s origin as if it is an object under
   //      examination. This is similar to the "Examine"
   //      navigation type used by VRML browsers.
   //
   //    - "walk": a style where mouse drags rotate about
   //      the viewer"s center as if the viewer is turning
   //      about to look at a scene they are in. This is
   //      similar to the "Walk" navigation type used by
   //      VRML browsers.
   //
   //  Aim the examine behavior at the scene"s TransformGroup
   //  and add the behavior to the scene root.
   //
   //  Aim the walk behavior at the viewing platform"s
   //  TransformGroup and add the behavior to the scene root.
   //
   //  Enable one (and only one!) of the two behaviors
   //  depending upon the current navigation type.
   //
   examineBehavior = new ExamineViewerBehavior(exampleSceneTransform, // Transform
       // gorup
       // to
       // modify
       exampleFrame); // Parent frame for cusor changes
   examineBehavior.setSchedulingBounds(allBounds);
   sceneRoot.addChild(examineBehavior);
   walkBehavior = new WalkViewerBehavior(exampleViewTransform, // Transform
       // group to
       // modify
       exampleFrame); // Parent frame for cusor changes
   walkBehavior.setSchedulingBounds(allBounds);
   sceneRoot.addChild(walkBehavior);
   if (navigationType == Walk) {
     examineBehavior.setEnable(false);
     walkBehavior.setEnable(true);
   } else {
     examineBehavior.setEnable(true);
     walkBehavior.setEnable(false);
   }
   //
   //  Compile the scene branch group and add it to the
   //  SimpleUniverse.
   //
   if (shouldCompile)
     sceneRoot.rupile();
   universe.addBranchGraph(sceneRoot);
   reset();
 }
 /**
  * Builds the scene. Example application subclasses should replace this
  * method with their own method to build 3D content.
  * 
  * @return a Group containing 3D content to display
  */
 public Group buildScene() {
   // Build the scene group containing nothing
   Group scene = new Group();
   return scene;
 }
 //--------------------------------------------------------------
 //  SET/GET METHODS
 //--------------------------------------------------------------
 /**
  * Sets the headlight on/off state. The headlight faces forward in the
  * direction the viewer is facing. Example applications that add their own
  * lights will typically turn the headlight off. A standard menu item
  * enables the headlight to be turned on and off via user control.
  * 
  * @param onOff
  *            a boolean turning the light on (true) or off (false)
  */
 public void setHeadlightEnable(boolean onOff) {
   headlightOnOff = onOff;
   if (headlight != null)
     headlight.setEnable(headlightOnOff);
   if (headlightMenuItem != null)
     headlightMenuItem.setState(headlightOnOff);
 }
 /**
  * Gets the headlight on/off state.
  * 
  * @return a boolean indicating if the headlight is on or off
  */
 public boolean getHeadlightEnable() {
   return headlightOnOff;
 }
 /**
  * Sets the navigation type to be either Examine or Walk. The Examine
  * navigation type sets up behaviors that use mouse drags to rotate and
  * translate scene content as if it is an object held at arm"s length and
  * under examination. The Walk navigation type uses mouse drags to rotate
  * and translate the viewer as if they are walking through the content. The
  * Examine type is the default.
  * 
  * @param nav
  *            either Walk or Examine
  */
 public void setNavigationType(int nav) {
   if (nav == Walk) {
     navigationType = Walk;
     if (walkMenuItem != null)
       walkMenuItem.setState(true);
     if (examineMenuItem != null)
       examineMenuItem.setState(false);
     if (walkBehavior != null)
       walkBehavior.setEnable(true);
     if (examineBehavior != null)
       examineBehavior.setEnable(false);
   } else {
     navigationType = Examine;
     if (walkMenuItem != null)
       walkMenuItem.setState(false);
     if (examineMenuItem != null)
       examineMenuItem.setState(true);
     if (walkBehavior != null)
       walkBehavior.setEnable(false);
     if (examineBehavior != null)
       examineBehavior.setEnable(true);
   }
 }
 /**
  * Gets the current navigation type, returning either Walk or Examine.
  * 
  * @return either Walk or Examine
  */
 public int getNavigationType() {
   return navigationType;
 }
 /**
  * Sets whether the scene graph should be compiled or not. Normally this is
  * always a good idea. For some example applications that use this Example
  * framework, it is useful to disable compilation - particularly when nodes
  * and node components will need to be made un-live in order to make
  * changes. Once compiled, such components can be made un-live, but they are
  * still unchangable unless appropriate capabilities have been set.
  * 
  * @param onOff
  *            a boolean turning compilation on (true) or off (false)
  */
 public void setCompilable(boolean onOff) {
   shouldCompile = onOff;
 }
 /**
  * Gets whether the scene graph will be compiled or not.
  * 
  * @return a boolean indicating if scene graph compilation is on or off
  */
 public boolean getCompilable() {
   return shouldCompile;
 }
 //These methods will be replaced
 //  Set the view position and direction
 public void setViewpoint(Point3f position, Vector3f direction) {
   Transform3D t = new Transform3D();
   t.set(new Vector3f(position));
   exampleViewTransform.setTransform(t);
   // how to set direction?
 }
 //  Reset transforms
 public void reset() {
   Transform3D trans = new Transform3D();
   exampleSceneTransform.setTransform(trans);
   trans.set(new Vector3f(0.0f, 0.0f, 10.0f));
   exampleViewTransform.setTransform(trans);
   setNavigationType(navigationType);
 }
 //
 //  Gets the URL (with file: prepended) for the current directory.
 //  This is a terrible hack needed in the Alpha release of Java3D
 //  in order to build a full path URL for loading sounds with
 //  MediaContainer. When MediaContainer is fully implemented,
 //  it should handle relative path names, but not yet.
 //
 public String getCurrentDirectory() {
   // Create a bogus file so that we can query it"s path
   File dummy = new File("dummy.tmp");
   String dummyPath = dummy.getAbsolutePath();
   // strip "/dummy.tmp" from end of dummyPath and put into "path"
   if (dummyPath.endsWith(File.separator + "dummy.tmp")) {
     int index = dummyPath.lastIndexOf(File.separator + "dummy.tmp");
     if (index >= 0) {
       int pathLength = index + 5; // pre-pend "file:"
       char[] charPath = new char[pathLength];
       dummyPath.getChars(0, index, charPath, 5);
       String path = new String(charPath, 0, pathLength);
       path = "file:" + path.substring(5, pathLength);
       return path + File.separator;
     }
   }
   return dummyPath + File.separator;
 }
 //--------------------------------------------------------------
 //  USER INTERFACE
 //--------------------------------------------------------------
 /**
  * Builds the example AWT Frame menubar. Standard menus and their options
  * are added. Applications that subclass this class should build their
  * menubar additions within their initialize method.
  * 
  * @return a MenuBar for the AWT Frame
  */
 private MenuBar buildMenuBar() {
   // Build the menubar
   MenuBar menuBar = new MenuBar();
   // File menu
   Menu m = new Menu("File");
   m.addActionListener(this);
   m.add("Exit");
   menuBar.add(m);
   // View menu
   m = new Menu("View");
   m.addActionListener(this);
   m.add("Reset view");
   m.addSeparator();
   walkMenuItem = new CheckboxMenuItem("Walk");
   walkMenuItem.addItemListener(this);
   m.add(walkMenuItem);
   examineMenuItem = new CheckboxMenuItem("Examine");
   examineMenuItem.addItemListener(this);
   m.add(examineMenuItem);
   if (navigationType == Walk) {
     walkMenuItem.setState(true);
     examineMenuItem.setState(false);
   } else {
     walkMenuItem.setState(false);
     examineMenuItem.setState(true);
   }
   m.addSeparator();
   headlightMenuItem = new CheckboxMenuItem("Headlight on/off");
   headlightMenuItem.addItemListener(this);
   headlightMenuItem.setState(headlightOnOff);
   m.add(headlightMenuItem);
   menuBar.add(m);
   return menuBar;
 }
 /**
  * Shows the application"s frame, making it and its menubar, 3D canvas, and
  * 3D content visible.
  */
 public void showFrame() {
   exampleFrame.show();
 }
 /**
  * Quits the application.
  */
 public void quit() {
   System.exit(0);
 }
 /**
  * Handles menu selections.
  * 
  * @param event
  *            an ActionEvent indicating what menu action requires handling
  */
 public void actionPerformed(ActionEvent event) {
   String arg = event.getActionCommand();
   if (arg.equals("Reset view"))
     reset();
   else if (arg.equals("Exit"))
     quit();
 }
 /**
  * Handles checkbox items on a CheckboxMenu. The Example class has none of
  * its own, but subclasses may have some.
  * 
  * @param menu
  *            which CheckboxMenu needs action
  * @param check
  *            which CheckboxMenu item has changed
  */
 public void checkboxChanged(CheckboxMenu menu, int check) {
   // None for us
 }
 /**
  * Handles on/off checkbox items on a standard menu.
  * 
  * @param event
  *            an ItemEvent indicating what requires handling
  */
 public void itemStateChanged(ItemEvent event) {
   Object src = event.getSource();
   boolean state;
   if (src == headlightMenuItem) {
     state = headlightMenuItem.getState();
     headlight.setEnable(state);
   } else if (src == walkMenuItem)
     setNavigationType(Walk);
   else if (src == examineMenuItem)
     setNavigationType(Examine);
 }
 /**
  * Handles a window closing event notifying the application that the user
  * has chosen to close the application without selecting the "Exit" menu
  * item.
  * 
  * @param event
  *            a WindowEvent indicating the window is closing
  */
 public void windowClosing(WindowEvent event) {
   quit();
 }
 public void windowClosed(WindowEvent event) {
 }
 public void windowOpened(WindowEvent event) {
 }
 public void windowIconified(WindowEvent event) {
 }
 public void windowDeiconified(WindowEvent event) {
 }
 public void windowActivated(WindowEvent event) {
 }
 public void windowDeactivated(WindowEvent event) {
 }
 //  Well known colors, positions, and directions
 public final static Color3f White = new Color3f(1.0f, 1.0f, 1.0f);
 public final static Color3f Gray = new Color3f(0.7f, 0.7f, 0.7f);
 public final static Color3f DarkGray = new Color3f(0.2f, 0.2f, 0.2f);
 public final static Color3f Black = new Color3f(0.0f, 0.0f, 0.0f);
 public final static Color3f Red = new Color3f(1.0f, 0.0f, 0.0f);
 public final static Color3f DarkRed = new Color3f(0.3f, 0.0f, 0.0f);
 public final static Color3f Yellow = new Color3f(1.0f, 1.0f, 0.0f);
 public final static Color3f DarkYellow = new Color3f(0.3f, 0.3f, 0.0f);
 public final static Color3f Green = new Color3f(0.0f, 1.0f, 0.0f);
 public final static Color3f DarkGreen = new Color3f(0.0f, 0.3f, 0.0f);
 public final static Color3f Cyan = new Color3f(0.0f, 1.0f, 1.0f);
 public final static Color3f Blue = new Color3f(0.0f, 0.0f, 1.0f);
 public final static Color3f DarkBlue = new Color3f(0.0f, 0.0f, 0.3f);
 public final static Color3f Magenta = new Color3f(1.0f, 0.0f, 1.0f);
 public final static Vector3f PosX = new Vector3f(1.0f, 0.0f, 0.0f);
 public final static Vector3f NegX = new Vector3f(-1.0f, 0.0f, 0.0f);
 public final static Vector3f PosY = new Vector3f(0.0f, 1.0f, 0.0f);
 public final static Vector3f NegY = new Vector3f(0.0f, -1.0f, 0.0f);
 public final static Vector3f PosZ = new Vector3f(0.0f, 0.0f, 1.0f);
 public final static Vector3f NegZ = new Vector3f(0.0f, 0.0f, -1.0f);
 public final static Point3f Origin = new Point3f(0.0f, 0.0f, 0.0f);
 public final static Point3f PlusX = new Point3f(0.75f, 0.0f, 0.0f);
 public final static Point3f MinusX = new Point3f(-0.75f, 0.0f, 0.0f);
 public final static Point3f PlusY = new Point3f(0.0f, 0.75f, 0.0f);
 public final static Point3f MinusY = new Point3f(0.0f, -0.75f, 0.0f);
 public final static Point3f PlusZ = new Point3f(0.0f, 0.0f, 0.75f);
 public final static Point3f MinusZ = new Point3f(0.0f, 0.0f, -0.75f);

} // //INTERFACE //CheckboxMenuListener - listen for checkbox change events // //DESCRIPTION //The checkboxChanged method is called by users of this class //to notify the listener when a checkbox choice has changed on //a CheckboxMenu class menu. // interface CheckboxMenuListener extends EventListener {

 public void checkboxChanged(CheckboxMenu menu, int check);

} /**

* ExamineViewerBehavior
* 
* @version 1.0, 98/04/16
*/

/**

* Wakeup on mouse button presses, releases, and mouse movements and generate
* transforms in an "examination style" that enables the user to rotate,
* translation, and zoom an object as if it is held at arm"s length. Such an
* examination style is similar to the "Examine" navigation type used by VRML
* browsers.
* 
* The behavior maps mouse drags to different transforms depending upon the
* mosue button held down:
* 
* Button 1 (left) Horizontal movement --> Y-axis rotation Vertical movement -->
* X-axis rotation
* 
* Button 2 (middle) Horizontal movement --> nothing Vertical movement -->
* Z-axis translation
* 
* Button 3 (right) Horizontal movement --> X-axis translation Vertical movement
* --> Y-axis translation
* 
* To support systems with 2 or 1 mouse buttons, the following alternate
* mappings are supported while dragging with any mouse button held down and
* zero or more keyboard modifiers held down:
* 
* No modifiers = Button 1 ALT = Button 2 Meta = Button 3 Control = Button 3
* 
* The behavior automatically modifies a TransformGroup provided to the
* constructor. The TransformGroup"s transform can be set at any time by the
* application or other behaviors to cause the examine rotation and translation
* to be reset.
*/

// This class is inspired by the MouseBehavior, MouseRotate, // MouseTranslate, and MouseZoom utility behaviors provided with // Java 3D. This class differs from those utilities in that it: // // (a) encapsulates all three behaviors into one in order to // enforce a specific "Examine" symantic // // (b) supports set/get of the rotation and translation factors // that control the speed of movement. // // (c) supports the "Control" modifier as an alternative to the // "Meta" modifier not present on PC, Mac, and most non-Sun // keyboards. This makes button3 behavior usable on PCs, // Macs, and other systems with fewer than 3 mouse buttons. class ExamineViewerBehavior extends ViewerBehavior {

 // Previous cursor location
 protected int previousX = 0;
 protected int previousY = 0;
 // Saved standard cursor
 protected Cursor savedCursor = null;
 /**
  * Construct an examine behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into a
  * transform group given later with the setTransformGroup( ) method.
  */
 public ExamineViewerBehavior() {
   super();
 }
 /**
  * Construct an examine behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into a
  * transform group given later with the setTransformGroup( ) method.
  * 
  * @param parent
  *            The AWT Component that contains the area generating mouse
  *            events.
  */
 public ExamineViewerBehavior(Component parent) {
   super(parent);
 }
 /**
  * Construct an examine behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into the
  * given transform group.
  * 
  * @param transformGroup
  *            The transform group to be modified by the behavior.
  */
 public ExamineViewerBehavior(TransformGroup transformGroup) {
   super();
   subjectTransformGroup = transformGroup;
 }
 /**
  * Construct an examine behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into the
  * given transform group.
  * 
  * @param transformGroup
  *            The transform group to be modified by the behavior.
  * @param parent
  *            The AWT Component that contains the area generating mouse
  *            events.
  */
 public ExamineViewerBehavior(TransformGroup transformGroup, Component parent) {
   super(parent);
   subjectTransformGroup = transformGroup;
 }
 /**
  * Respond to a button1 event (press, release, or drag).
  * 
  * @param mouseEvent
  *            A MouseEvent to respond to.
  */
 public void onButton1(MouseEvent mev) {
   if (subjectTransformGroup == null)
     return;
   int x = mev.getX();
   int y = mev.getY();
   if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
     // Mouse button pressed: record position
     previousX = x;
     previousY = y;
     // Change to a "move" cursor
     if (parentComponent != null) {
       savedCursor = parentComponent.getCursor();
       parentComponent.setCursor(Cursor
           .getPredefinedCursor(Cursor.HAND_CURSOR));
     }
     return;
   }
   if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
     // Mouse button released: do nothing
     // Switch the cursor back
     if (parentComponent != null)
       parentComponent.setCursor(savedCursor);
     return;
   }
   //
   // Mouse moved while button down: create a rotation
   //
   // Compute the delta in X and Y from the previous
   // position. Use the delta to compute rotation
   // angles with the mapping:
   //
   //   positive X mouse delta --> positive Y-axis rotation
   //   positive Y mouse delta --> positive X-axis rotation
   //
   // where positive X mouse movement is to the right, and
   // positive Y mouse movement is **down** the screen.
   //
   int deltaX = x - previousX;
   int deltaY = y - previousY;
   if (deltaX > UNUSUAL_XDELTA || deltaX < -UNUSUAL_XDELTA
       || deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {
     // Deltas are too huge to be believable. Probably a glitch.
     // Don"t record the new XY location, or do anything.
     return;
   }
   double xRotationAngle = deltaY * XRotationFactor;
   double yRotationAngle = deltaX * YRotationFactor;
   //
   // Build transforms
   //
   transform1.rotX(xRotationAngle);
   transform2.rotY(yRotationAngle);
   // Get and save the current transform matrix
   subjectTransformGroup.getTransform(currentTransform);
   currentTransform.get(matrix);
   translate.set(matrix.m03, matrix.m13, matrix.m23);
   // Translate to the origin, rotate, then translate back
   currentTransform.setTranslation(origin);
   currentTransform.mul(transform1, currentTransform);
   currentTransform.mul(transform2, currentTransform);
   currentTransform.setTranslation(translate);
   // Update the transform group
   subjectTransformGroup.setTransform(currentTransform);
   previousX = x;
   previousY = y;
 }
 /**
  * Respond to a button2 event (press, release, or drag).
  * 
  * @param mouseEvent
  *            A MouseEvent to respond to.
  */
 public void onButton2(MouseEvent mev) {
   if (subjectTransformGroup == null)
     return;
   int x = mev.getX();
   int y = mev.getY();
   if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
     // Mouse button pressed: record position
     previousX = x;
     previousY = y;
     // Change to a "move" cursor
     if (parentComponent != null) {
       savedCursor = parentComponent.getCursor();
       parentComponent.setCursor(Cursor
           .getPredefinedCursor(Cursor.MOVE_CURSOR));
     }
     return;
   }
   if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
     // Mouse button released: do nothing
     // Switch the cursor back
     if (parentComponent != null)
       parentComponent.setCursor(savedCursor);
     return;
   }
   //
   // Mouse moved while button down: create a translation
   //
   // Compute the delta in Y from the previous
   // position. Use the delta to compute translation
   // distances with the mapping:
   //
   //   positive Y mouse delta --> positive Y-axis translation
   //
   // where positive X mouse movement is to the right, and
   // positive Y mouse movement is **down** the screen.
   //
   int deltaY = y - previousY;
   if (deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {
     // Deltas are too huge to be believable. Probably a glitch.
     // Don"t record the new XY location, or do anything.
     return;
   }
   double zTranslationDistance = deltaY * ZTranslationFactor;
   //
   // Build transforms
   //
   translate.set(0.0, 0.0, zTranslationDistance);
   transform1.set(translate);
   // Get and save the current transform
   subjectTransformGroup.getTransform(currentTransform);
   // Translate as needed
   currentTransform.mul(transform1, currentTransform);
   // Update the transform group
   subjectTransformGroup.setTransform(currentTransform);
   previousX = x;
   previousY = y;
 }
 /**
  * Respond to a button3 event (press, release, or drag).
  * 
  * @param mouseEvent
  *            A MouseEvent to respond to.
  */
 public void onButton3(MouseEvent mev) {
   if (subjectTransformGroup == null)
     return;
   int x = mev.getX();
   int y = mev.getY();
   if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
     // Mouse button pressed: record position
     previousX = x;
     previousY = y;
     // Change to a "move" cursor
     if (parentComponent != null) {
       savedCursor = parentComponent.getCursor();
       parentComponent.setCursor(Cursor
           .getPredefinedCursor(Cursor.MOVE_CURSOR));
     }
     return;
   }
   if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
     // Mouse button released: do nothing
     // Switch the cursor back
     if (parentComponent != null)
       parentComponent.setCursor(savedCursor);
     return;
   }
   //
   // Mouse moved while button down: create a translation
   //
   // Compute the delta in X and Y from the previous
   // position. Use the delta to compute translation
   // distances with the mapping:
   //
   //   positive X mouse delta --> positive X-axis translation
   //   positive Y mouse delta --> negative Y-axis translation
   //
   // where positive X mouse movement is to the right, and
   // positive Y mouse movement is **down** the screen.
   //
   int deltaX = x - previousX;
   int deltaY = y - previousY;
   if (deltaX > UNUSUAL_XDELTA || deltaX < -UNUSUAL_XDELTA
       || deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {
     // Deltas are too huge to be believable. Probably a glitch.
     // Don"t record the new XY location, or do anything.
     return;
   }
   double xTranslationDistance = deltaX * XTranslationFactor;
   double yTranslationDistance = -deltaY * YTranslationFactor;
   //
   // Build transforms
   //
   translate.set(xTranslationDistance, yTranslationDistance, 0.0);
   transform1.set(translate);
   // Get and save the current transform
   subjectTransformGroup.getTransform(currentTransform);
   // Translate as needed
   currentTransform.mul(transform1, currentTransform);
   // Update the transform group
   subjectTransformGroup.setTransform(currentTransform);
   previousX = x;
   previousY = y;
 }
 /**
  * Respond to an elapsed frames event (assuming subclass has set up a wakeup
  * criterion for it).
  * 
  * @param time
  *            A WakeupOnElapsedFrames criterion to respond to.
  */
 public void onElapsedFrames(WakeupOnElapsedFrames timeEvent) {
   // Can"t happen
 }

} /*

* 
* Copyright (c) 1998 David R. Nadeau
*  
*/

/**

* WalkViewerBehavior is a utility class that creates a "walking style"
* navigation symantic.
* 
* The behavior wakes up on mouse button presses, releases, and mouse movements
* and generates transforms in a "walk style" that enables the user to walk
* through a scene, translating and turning about as if they are within the
* scene. Such a walk style is similar to the "Walk" navigation type used by
* VRML browsers.
* <P>
* The behavior maps mouse drags to different transforms depending upon the
* mouse button held down:
*
*
Button 1 (left) *
Horizontal movement --> Y-axis rotation *
Vertical movement --> Z-axis translation * *
Button 2 (middle) *
Horizontal movement --> Y-axis rotation *
Vertical movement --> X-axis rotation * *
Button 3 (right) *
Horizontal movement --> X-axis translation *
Vertical movement --> Y-axis translation *
* 
* To support systems with 2 or 1 mouse buttons, the following alternate
* mappings are supported while dragging with any mouse button held down and
* zero or more keyboard modifiers held down:
*
    *
  • No modifiers = Button 1 *
  • ALT = Button 2 *
  • Meta = Button 3 *
  • Control = Button 3 *
* The behavior automatically modifies a TransformGroup provided to the
* constructor. The TransformGroup"s transform can be set at any time by the
* application or other behaviors to cause the walk rotation and translation to
* be reset.
* <P>
* While a mouse button is down, the behavior automatically changes the cursor
* in a given parent AWT Component. If no parent Component is given, no cursor
* changes are attempted.
* 
* @version 1.0, 98/04/16
* @author David R. Nadeau, San Diego Supercomputer Center
*/

class WalkViewerBehavior extends ViewerBehavior {

 // This class is inspired by the MouseBehavior, MouseRotate,
 // MouseTranslate, and MouseZoom utility behaviors provided with
 // Java 3D. This class differs from those utilities in that it:
 //
 //    (a) encapsulates all three behaviors into one in order to
 //        enforce a specific "Walk" symantic
 //
 //    (b) supports set/get of the rotation and translation factors
 //        that control the speed of movement.
 //
 //    (c) supports the "Control" modifier as an alternative to the
 //        "Meta" modifier not present on PC, Mac, and most non-Sun
 //        keyboards. This makes button3 behavior usable on PCs,
 //        Macs, and other systems with fewer than 3 mouse buttons.
 // Previous and initial cursor locations
 protected int previousX = 0;
 protected int previousY = 0;
 protected int initialX = 0;
 protected int initialY = 0;
 // Deadzone size (delta from initial XY for which no
 // translate or rotate action is taken
 protected static final int DELTAX_DEADZONE = 10;
 protected static final int DELTAY_DEADZONE = 10;
 // Keep a set of wakeup criterion for animation-generated
 // event types.
 protected WakeupCriterion[] mouseAndAnimationEvents = null;
 protected WakeupOr mouseAndAnimationCriterion = null;
 protected WakeupOr savedMouseCriterion = null;
 // Saved standard cursor
 protected Cursor savedCursor = null;
 /**
  * Default Rotation and translation scaling factors for animated movements
  * (Button 1 press).
  */
 public static final double DEFAULT_YROTATION_ANIMATION_FACTOR = 0.0002;
 public static final double DEFAULT_ZTRANSLATION_ANIMATION_FACTOR = 0.01;
 protected double YRotationAnimationFactor = DEFAULT_YROTATION_ANIMATION_FACTOR;
 protected double ZTranslationAnimationFactor = DEFAULT_ZTRANSLATION_ANIMATION_FACTOR;
 /**
  * Constructs a new walk behavior that converts mouse actions into rotations
  * and translations. Rotations and translations are written into a
  * TransformGroup that must be set using the setTransformGroup method. The
  * cursor will be changed during mouse actions if the parent frame is set
  * using the setParentComponent method.
  * 
  * @return a new WalkViewerBehavior that needs its TransformGroup and parent
  *         Component set
  */
 public WalkViewerBehavior() {
   super();
 }
 /**
  * Constructs a new walk behavior that converts mouse actions into rotations
  * and translations. Rotations and translations are written into a
  * TransformGroup that must be set using the setTransformGroup method. The
  * cursor will be changed within the given AWT parent Component during mouse
  * drags.
  * 
  * @param parent
  *            a parent AWT Component within which the cursor will change
  *            during mouse drags
  * 
  * @return a new WalkViewerBehavior that needs its TransformGroup and parent
  *         Component set
  */
 public WalkViewerBehavior(Component parent) {
   super(parent);
 }
 /**
  * Constructs a new walk behavior that converts mouse actions into rotations
  * and translations. Rotations and translations are written into the given
  * TransformGroup. The cursor will be changed during mouse actions if the
  * parent frame is set using the setParentComponent method.
  * 
  * @param transformGroup
  *            a TransformGroup whos transform is read and written by the
  *            behavior
  * 
  * @return a new WalkViewerBehavior that needs its TransformGroup and parent
  *         Component set
  */
 public WalkViewerBehavior(TransformGroup transformGroup) {
   super();
   subjectTransformGroup = transformGroup;
 }
 /**
  * Constructs a new walk behavior that converts mouse actions into rotations
  * and translations. Rotations and translations are written into the given
  * TransformGroup. The cursor will be changed within the given AWT parent
  * Component during mouse drags.
  * 
  * @param transformGroup
  *            a TransformGroup whos transform is read and written by the
  *            behavior
  * 
  * @param parent
  *            a parent AWT Component within which the cursor will change
  *            during mouse drags
  * 
  * @return a new WalkViewerBehavior that needs its TransformGroup and parent
  *         Component set
  */
 public WalkViewerBehavior(TransformGroup transformGroup, Component parent) {
   super(parent);
   subjectTransformGroup = transformGroup;
 }
 /**
  * Initializes the behavior.
  */
 public void initialize() {
   super.initialize();
   savedMouseCriterion = mouseCriterion; // from parent class
   mouseAndAnimationEvents = new WakeupCriterion[4];
   mouseAndAnimationEvents[0] = new WakeupOnAWTEvent(
       MouseEvent.MOUSE_DRAGGED);
   mouseAndAnimationEvents[1] = new WakeupOnAWTEvent(
       MouseEvent.MOUSE_PRESSED);
   mouseAndAnimationEvents[2] = new WakeupOnAWTEvent(
       MouseEvent.MOUSE_RELEASED);
   mouseAndAnimationEvents[3] = new WakeupOnElapsedFrames(0);
   mouseAndAnimationCriterion = new WakeupOr(mouseAndAnimationEvents);
   // Don"t use the above criterion until a button 1 down event
 }
 /**
  * Sets the Y rotation animation scaling factor for Y-axis rotations. This
  * scaling factor is used to control the speed of Y rotation when button 1
  * is pressed and dragged.
  * 
  * @param factor
  *            the double Y rotation scaling factor
  */
 public void setYRotationAnimationFactor(double factor) {
   YRotationAnimationFactor = factor;
 }
 /**
  * Gets the current Y animation rotation scaling factor for Y-axis
  * rotations.
  * 
  * @return the double Y rotation scaling factor
  */
 public double getYRotationAnimationFactor() {
   return YRotationAnimationFactor;
 }
 /**
  * Sets the Z animation translation scaling factor for Z-axis translations.
  * This scaling factor is used to control the speed of Z translation when
  * button 1 is pressed and dragged.
  * 
  * @param factor
  *            the double Z translation scaling factor
  */
 public void setZTranslationAnimationFactor(double factor) {
   ZTranslationAnimationFactor = factor;
 }
 /**
  * Gets the current Z animation translation scaling factor for Z-axis
  * translations.
  * 
  * @return the double Z translation scaling factor
  */
 public double getZTranslationAnimationFactor() {
   return ZTranslationAnimationFactor;
 }
 /**
  * Responds to an elapsed frames event. Such an event is generated on every
  * frame while button 1 is held down. On each call, this method computes new
  * Y-axis rotation and Z-axis translation values and writes them to the
  * behavior"s TransformGroup. The translation and rotation amounts are
  * computed based upon the distance between the current cursor location and
  * the cursor location when button 1 was pressed. As this distance
  * increases, the translation or rotation amount increases.
  * 
  * @param time
  *            the WakeupOnElapsedFrames criterion to respond to
  */
 public void onElapsedFrames(WakeupOnElapsedFrames timeEvent) {
   //
   // Time elapsed while button down: create a rotation and
   // a translation.
   //
   // Compute the delta in X and Y from the initial position to
   // the previous position. Multiply the delta times a scaling
   // factor to compute an offset to add to the current translation
   // and rotation. Use the mapping:
   //
   //   positive X mouse delta --> negative Y-axis rotation
   //   positive Y mouse delta --> positive Z-axis translation
   //
   // where positive X mouse movement is to the right, and
   // positive Y mouse movement is **down** the screen.
   //
   if (buttonPressed != BUTTON1)
     return;
   int deltaX = previousX - initialX;
   int deltaY = previousY - initialY;
   double yRotationAngle = -deltaX * YRotationAnimationFactor;
   double zTranslationDistance = deltaY * ZTranslationAnimationFactor;
   //
   // Build transforms
   //
   transform1.rotY(yRotationAngle);
   translate.set(0.0, 0.0, zTranslationDistance);
   // Get and save the current transform matrix
   subjectTransformGroup.getTransform(currentTransform);
   currentTransform.get(matrix);
   // Translate to the origin, rotate, then translate back
   currentTransform.setTranslation(origin);
   currentTransform.mul(transform1, currentTransform);
   // Translate back from the origin by the original translation
   // distance, plus the new walk translation... but force walk
   // to travel on a plane by ignoring the Y component of a
   // transformed translation vector.
   currentTransform.transform(translate);
   translate.x += matrix.m03; // add in existing X translation
   translate.y = matrix.m13; // use Y translation
   translate.z += matrix.m23; // add in existing Z translation
   currentTransform.setTranslation(translate);
   // Update the transform group
   subjectTransformGroup.setTransform(currentTransform);
 }
 /**
  * Responds to a button1 event (press, release, or drag). On a press, the
  * method adds a wakeup criterion to the behavior"s set, callling for the
  * behavior to be awoken on each frame. On a button prelease, this criterion
  * is removed from the set.
  * 
  * @param mouseEvent
  *            the MouseEvent to respond to
  */
 public void onButton1(MouseEvent mev) {
   if (subjectTransformGroup == null)
     return;
   int x = mev.getX();
   int y = mev.getY();
   if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
     // Mouse button pressed: record position and change
     // the wakeup criterion to include elapsed time wakeups
     // so we can animate.
     previousX = x;
     previousY = y;
     initialX = x;
     initialY = y;
     // Swap criterion... parent class will not reschedule us
     mouseCriterion = mouseAndAnimationCriterion;
     // Change to a "move" cursor
     if (parentComponent != null) {
       savedCursor = parentComponent.getCursor();
       parentComponent.setCursor(Cursor
           .getPredefinedCursor(Cursor.HAND_CURSOR));
     }
     return;
   }
   if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
     // Mouse button released: restore original wakeup
     // criterion which only includes mouse activity, not
     // elapsed time
     mouseCriterion = savedMouseCriterion;
     // Switch the cursor back
     if (parentComponent != null)
       parentComponent.setCursor(savedCursor);
     return;
   }
   previousX = x;
   previousY = y;
 }
 /**
  * Responds to a button2 event (press, release, or drag). On a press, the
  * method records the initial cursor location. On a drag, the difference
  * between the current and previous cursor location provides a delta that
  * controls the amount by which to rotate in X and Y.
  * 
  * @param mouseEvent
  *            the MouseEvent to respond to
  */
 public void onButton2(MouseEvent mev) {
   if (subjectTransformGroup == null)
     return;
   int x = mev.getX();
   int y = mev.getY();
   if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
     // Mouse button pressed: record position
     previousX = x;
     previousY = y;
     initialX = x;
     initialY = y;
     // Change to a "rotate" cursor
     if (parentComponent != null) {
       savedCursor = parentComponent.getCursor();
       parentComponent.setCursor(Cursor
           .getPredefinedCursor(Cursor.MOVE_CURSOR));
     }
     return;
   }
   if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
     // Mouse button released: do nothing
     // Switch the cursor back
     if (parentComponent != null)
       parentComponent.setCursor(savedCursor);
     return;
   }
   //
   // Mouse moved while button down: create a rotation
   //
   // Compute the delta in X and Y from the previous
   // position. Use the delta to compute rotation
   // angles with the mapping:
   //
   //   positive X mouse delta --> negative Y-axis rotation
   //   positive Y mouse delta --> negative X-axis rotation
   //
   // where positive X mouse movement is to the right, and
   // positive Y mouse movement is **down** the screen.
   //
   int deltaX = x - previousX;
   int deltaY = 0;
   if (Math.abs(y - initialY) > DELTAY_DEADZONE) {
     // Cursor has moved far enough vertically to consider
     // it intentional, so get it"s delta.
     deltaY = y - previousY;
   }
   if (deltaX > UNUSUAL_XDELTA || deltaX < -UNUSUAL_XDELTA
       || deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {
     // Deltas are too huge to be believable. Probably a glitch.
     // Don"t record the new XY location, or do anything.
     return;
   }
   double xRotationAngle = -deltaY * XRotationFactor;
   double yRotationAngle = -deltaX * YRotationFactor;
   //
   // Build transforms
   //
   transform1.rotX(xRotationAngle);
   transform2.rotY(yRotationAngle);
   // Get and save the current transform matrix
   subjectTransformGroup.getTransform(currentTransform);
   currentTransform.get(matrix);
   translate.set(matrix.m03, matrix.m13, matrix.m23);
   // Translate to the origin, rotate, then translate back
   currentTransform.setTranslation(origin);
   currentTransform.mul(transform2, currentTransform);
   currentTransform.mul(transform1);
   currentTransform.setTranslation(translate);
   // Update the transform group
   subjectTransformGroup.setTransform(currentTransform);
   previousX = x;
   previousY = y;
 }
 /**
  * Responds to a button3 event (press, release, or drag). On a drag, the
  * difference between the current and previous cursor location provides a
  * delta that controls the amount by which to translate in X and Y.
  * 
  * @param mouseEvent
  *            the MouseEvent to respond to
  */
 public void onButton3(MouseEvent mev) {
   if (subjectTransformGroup == null)
     return;
   int x = mev.getX();
   int y = mev.getY();
   if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
     // Mouse button pressed: record position
     previousX = x;
     previousY = y;
     // Change to a "move" cursor
     if (parentComponent != null) {
       savedCursor = parentComponent.getCursor();
       parentComponent.setCursor(Cursor
           .getPredefinedCursor(Cursor.MOVE_CURSOR));
     }
     return;
   }
   if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
     // Mouse button released: do nothing
     // Switch the cursor back
     if (parentComponent != null)
       parentComponent.setCursor(savedCursor);
     return;
   }
   //
   // Mouse moved while button down: create a translation
   //
   // Compute the delta in X and Y from the previous
   // position. Use the delta to compute translation
   // distances with the mapping:
   //
   //   positive X mouse delta --> positive X-axis translation
   //   positive Y mouse delta --> negative Y-axis translation
   //
   // where positive X mouse movement is to the right, and
   // positive Y mouse movement is **down** the screen.
   //
   int deltaX = x - previousX;
   int deltaY = y - previousY;
   if (deltaX > UNUSUAL_XDELTA || deltaX < -UNUSUAL_XDELTA
       || deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {
     // Deltas are too huge to be believable. Probably a glitch.
     // Don"t record the new XY location, or do anything.
     return;
   }
   double xTranslationDistance = deltaX * XTranslationFactor;
   double yTranslationDistance = -deltaY * YTranslationFactor;
   //
   // Build transforms
   //
   translate.set(xTranslationDistance, yTranslationDistance, 0.0);
   transform1.set(translate);
   // Get and save the current transform
   subjectTransformGroup.getTransform(currentTransform);
   // Translate as needed
   currentTransform.mul(transform1);
   // Update the transform group
   subjectTransformGroup.setTransform(currentTransform);
   previousX = x;
   previousY = y;
 }

} // //CLASS //CheckboxMenu - build a menu of grouped checkboxes // //DESCRIPTION //The class creates a menu with one or more CheckboxMenuItem"s //and monitors that menu. When a menu checkbox is picked, the //previous one is turned off (in radio-button style). Then, //a given listener"s checkboxChanged method is called, passing it //the menu and the item checked. // class CheckboxMenu extends Menu implements ItemListener {

 // State
 protected CheckboxMenuItem[] checks = null;
 protected int current = 0;
 protected CheckboxMenuListener listener = null;
 //  Construct
 public CheckboxMenu(String name, NameValue[] items,
     CheckboxMenuListener listen) {
   this(name, items, 0, listen);
 }
 public CheckboxMenu(String name, NameValue[] items, int cur,
     CheckboxMenuListener listen) {
   super(name);
   current = cur;
   listener = listen;
   if (items == null)
     return;
   checks = new CheckboxMenuItem[items.length];
   for (int i = 0; i < items.length; i++) {
     checks[i] = new CheckboxMenuItem(items[i].name, false);
     checks[i].addItemListener(this);
     add(checks[i]);
   }
   checks[cur].setState(true);
 }
 //  Handle checkbox changed events
 public void itemStateChanged(ItemEvent event) {
   Object src = event.getSource();
   for (int i = 0; i < checks.length; i++) {
     if (src == checks[i]) {
       // Update the checkboxes
       checks[current].setState(false);
       current = i;
       checks[current].setState(true);
       if (listener != null)
         listener.checkboxChanged(this, i);
       return;
     }
   }
 }
 // Methods to get and set state
 public int getCurrent() {
   return current;
 }
 public void setCurrent(int cur) {
   if (cur < 0 || cur >= checks.length)
     return; // ignore out of range choices
   if (checks == null)
     return;
   checks[current].setState(false);
   current = cur;
   checks[current].setState(true);
 }
 public CheckboxMenuItem getSelectedCheckbox() {
   if (checks == null)
     return null;
   return checks[current];
 }
 public void setSelectedCheckbox(CheckboxMenuItem item) {
   if (checks == null)
     return;
   for (int i = 0; i < checks.length; i++) {
     if (item == checks[i]) {
       checks[i].setState(false);
       current = i;
       checks[i].setState(true);
     }
   }
 }

} /**

* ViewerBehavior
* 
* @version 1.0, 98/04/16
*/

/**

* Wakeup on mouse button presses, releases, and mouse movements and generate
* transforms for a transform group. Classes that extend this class impose
* specific symantics, such as "Examine" or "Walk" viewing, similar to the
* navigation types used by VRML browsers.
* 
* To support systems with 2 or 1 mouse buttons, the following alternate
* mappings are supported while dragging with any mouse button held down and
* zero or more keyboard modifiers held down:
* 
* No modifiers = Button 1 ALT = Button 2 Meta = Button 3 Control = Button 3
* 
* The behavior automatically modifies a TransformGroup provided to the
* constructor. The TransformGroup"s transform can be set at any time by the
* application or other behaviors to cause the viewer"s rotation and translation
* to be reset.
*/

// This class is inspired by the MouseBehavior, MouseRotate, // MouseTranslate, and MouseZoom utility behaviors provided with // Java 3D. This class differs from those utilities in that it: // // (a) encapsulates all three behaviors into one in order to // enforce a specific viewing symantic // // (b) supports set/get of the rotation and translation factors // that control the speed of movement. // // (c) supports the "Control" modifier as an alternative to the // "Meta" modifier not present on PC, Mac, and most non-Sun // keyboards. This makes button3 behavior usable on PCs, // Macs, and other systems with fewer than 3 mouse buttons. abstract class ViewerBehavior extends Behavior {

 // Keep track of the transform group who"s transform we modify
 // during mouse motion.
 protected TransformGroup subjectTransformGroup = null;
 // Keep a set of wakeup criterion for different mouse-generated
 // event types.
 protected WakeupCriterion[] mouseEvents = null;
 protected WakeupOr mouseCriterion = null;
 // Track which button was last pressed
 protected static final int BUTTONNONE = -1;
 protected static final int BUTTON1 = 0;
 protected static final int BUTTON2 = 1;
 protected static final int BUTTON3 = 2;
 protected int buttonPressed = BUTTONNONE;
 // Keep a few Transform3Ds for use during event processing. This
 // avoids having to allocate new ones on each event.
 protected Transform3D currentTransform = new Transform3D();
 protected Transform3D transform1 = new Transform3D();
 protected Transform3D transform2 = new Transform3D();
 protected Matrix4d matrix = new Matrix4d();
 protected Vector3d origin = new Vector3d(0.0, 0.0, 0.0);
 protected Vector3d translate = new Vector3d(0.0, 0.0, 0.0);
 // Unusual X and Y delta limits.
 protected static final int UNUSUAL_XDELTA = 400;
 protected static final int UNUSUAL_YDELTA = 400;
 protected Component parentComponent = null;
 /**
  * Construct a viewer behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into a
  * transform group given later with the setTransformGroup( ) method.
  */
 public ViewerBehavior() {
   super();
 }
 /**
  * Construct a viewer behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into a
  * transform group given later with the setTransformGroup( ) method.
  * 
  * @param parent
  *            The AWT Component that contains the area generating mouse
  *            events.
  */
 public ViewerBehavior(Component parent) {
   super();
   parentComponent = parent;
 }
 /**
  * Construct a viewer behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into the
  * given transform group.
  * 
  * @param transformGroup
  *            The transform group to be modified by the behavior.
  */
 public ViewerBehavior(TransformGroup transformGroup) {
   super();
   subjectTransformGroup = transformGroup;
 }
 /**
  * Construct a viewer behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into the
  * given transform group.
  * 
  * @param transformGroup
  *            The transform group to be modified by the behavior.
  * @param parent
  *            The AWT Component that contains the area generating mouse
  *            events.
  */
 public ViewerBehavior(TransformGroup transformGroup, Component parent) {
   super();
   subjectTransformGroup = transformGroup;
   parentComponent = parent;
 }
 /**
  * Set the transform group modified by the viewer behavior. Setting the
  * transform group to null disables the behavior until the transform group
  * is again set to an existing group.
  * 
  * @param transformGroup
  *            The new transform group to be modified by the behavior.
  */
 public void setTransformGroup(TransformGroup transformGroup) {
   subjectTransformGroup = transformGroup;
 }
 /**
  * Get the transform group modified by the viewer behavior.
  */
 public TransformGroup getTransformGroup() {
   return subjectTransformGroup;
 }
 /**
  * Sets the parent component who"s cursor will be changed during mouse
  * drags. If no component is given is given to the constructor, or set via
  * this method, no cursor changes will be done.
  * 
  * @param parent
  *            the AWT Component, such as a Frame, within which cursor
  *            changes should take place during mouse drags
  */
 public void setParentComponent(Component parent) {
   parentComponent = parent;
 }
 /*
  * Gets the parent frame within which the cursor changes during mouse drags.
  * 
  * @return the AWT Component, such as a Frame, within which cursor changes
  * should take place during mouse drags. Returns null if no parent is set.
  */
 public Component getParentComponent() {
   return parentComponent;
 }
 /**
  * Initialize the behavior.
  */
 public void initialize() {
   // Wakeup when the mouse is dragged or when a mouse button
   // is pressed or released.
   mouseEvents = new WakeupCriterion[3];
   mouseEvents[0] = new WakeupOnAWTEvent(MouseEvent.MOUSE_DRAGGED);
   mouseEvents[1] = new WakeupOnAWTEvent(MouseEvent.MOUSE_PRESSED);
   mouseEvents[2] = new WakeupOnAWTEvent(MouseEvent.MOUSE_RELEASED);
   mouseCriterion = new WakeupOr(mouseEvents);
   wakeupOn(mouseCriterion);
 }
 /**
  * Process a new wakeup. Interpret mouse button presses, releases, and mouse
  * drags.
  * 
  * @param criteria
  *            The wakeup criteria causing the behavior wakeup.
  */
 public void processStimulus(Enumeration criteria) {
   WakeupCriterion wakeup = null;
   AWTEvent[] event = null;
   int whichButton = BUTTONNONE;
   // Process all pending wakeups
   while (criteria.hasMoreElements()) {
     wakeup = (WakeupCriterion) criteria.nextElement();
     if (wakeup instanceof WakeupOnAWTEvent) {
       event = ((WakeupOnAWTEvent) wakeup).getAWTEvent();
       // Process all pending events
       for (int i = 0; i < event.length; i++) {
         if (event[i].getID() != MouseEvent.MOUSE_PRESSED
             && event[i].getID() != MouseEvent.MOUSE_RELEASED
             && event[i].getID() != MouseEvent.MOUSE_DRAGGED)
           // Ignore uninteresting mouse events
           continue;
         //
         // Regretably, Java event handling (or perhaps
         // underlying OS event handling) doesn"t always
         // catch button bounces (redundant presses and
         // releases), or order events so that the last
         // drag event is delivered before a release.
         // This means we can get stray events that we
         // filter out here.
         //
         if (event[i].getID() == MouseEvent.MOUSE_PRESSED
             && buttonPressed != BUTTONNONE)
           // Ignore additional button presses until a release
           continue;
         if (event[i].getID() == MouseEvent.MOUSE_RELEASED
             && buttonPressed == BUTTONNONE)
           // Ignore additional button releases until a press
           continue;
         if (event[i].getID() == MouseEvent.MOUSE_DRAGGED
             && buttonPressed == BUTTONNONE)
           // Ignore drags until a press
           continue;
         MouseEvent mev = (MouseEvent) event[i];
         int modifiers = mev.getModifiers();
         //
         // Unfortunately, the underlying event handling
         // doesn"t do a "grab" operation when a mouse button
         // is pressed. This means that once a button is
         // pressed, if another mouse button or a keyboard
         // modifier key is pressed, the delivered mouse event
         // will show that a different button is being held
         // down. For instance:
         //
         // Action Event
         //  Button 1 press Button 1 press
         //  Drag with button 1 down Button 1 drag
         //  ALT press -
         //  Drag with ALT & button 1 down Button 2 drag
         //  Button 1 release Button 2 release
         //
         // The upshot is that we can get a button press
         // without a matching release, and the button
         // associated with a drag can change mid-drag.
         //
         // To fix this, we watch for an initial button
         // press, and thenceforth consider that button
         // to be the one held down, even if additional
         // buttons get pressed, and despite what is
         // reported in the event. Only when a button is
         // released, do we end such a grab.
         //
         if (buttonPressed == BUTTONNONE) {
           // No button is pressed yet, figure out which
           // button is down now and how to direct events
           if (((modifiers & InputEvent.BUTTON3_MASK) != 0)
               || (((modifiers & InputEvent.BUTTON1_MASK) != 0) && ((modifiers & InputEvent.CTRL_MASK) == InputEvent.CTRL_MASK))) {
             // Button 3 activity (META or CTRL down)
             whichButton = BUTTON3;
           } else if ((modifiers & InputEvent.BUTTON2_MASK) != 0) {
             // Button 2 activity (ALT down)
             whichButton = BUTTON2;
           } else {
             // Button 1 activity (no modifiers down)
             whichButton = BUTTON1;
           }
           // If the event is to press a button, then
           // record that that button is now down
           if (event[i].getID() == MouseEvent.MOUSE_PRESSED)
             buttonPressed = whichButton;
         } else {
           // Otherwise a button was pressed earlier and
           // hasn"t been released yet. Assign all further
           // events to it, even if ALT, META, CTRL, or
           // another button has been pressed as well.
           whichButton = buttonPressed;
         }
         // Distribute the event
         switch (whichButton) {
         case BUTTON1:
           onButton1(mev);
           break;
         case BUTTON2:
           onButton2(mev);
           break;
         case BUTTON3:
           onButton3(mev);
           break;
         default:
           break;
         }
         // If the event is to release a button, then
         // record that that button is now up
         if (event[i].getID() == MouseEvent.MOUSE_RELEASED)
           buttonPressed = BUTTONNONE;
       }
       continue;
     }
     if (wakeup instanceof WakeupOnElapsedFrames) {
       onElapsedFrames((WakeupOnElapsedFrames) wakeup);
       continue;
     }
   }
   // Reschedule us for another wakeup
   wakeupOn(mouseCriterion);
 }
 /**
  * Default X and Y rotation factors, and XYZ translation factors.
  */
 public static final double DEFAULT_XROTATION_FACTOR = 0.02;
 public static final double DEFAULT_YROTATION_FACTOR = 0.005;
 public static final double DEFAULT_XTRANSLATION_FACTOR = 0.02;
 public static final double DEFAULT_YTRANSLATION_FACTOR = 0.02;
 public static final double DEFAULT_ZTRANSLATION_FACTOR = 0.04;
 protected double XRotationFactor = DEFAULT_XROTATION_FACTOR;
 protected double YRotationFactor = DEFAULT_YROTATION_FACTOR;
 protected double XTranslationFactor = DEFAULT_XTRANSLATION_FACTOR;
 protected double YTranslationFactor = DEFAULT_YTRANSLATION_FACTOR;
 protected double ZTranslationFactor = DEFAULT_ZTRANSLATION_FACTOR;
 /**
  * Set the X rotation scaling factor for X-axis rotations.
  * 
  * @param factor
  *            The new scaling factor.
  */
 public void setXRotationFactor(double factor) {
   XRotationFactor = factor;
 }
 /**
  * Get the current X rotation scaling factor for X-axis rotations.
  */
 public double getXRotationFactor() {
   return XRotationFactor;
 }
 /**
  * Set the Y rotation scaling factor for Y-axis rotations.
  * 
  * @param factor
  *            The new scaling factor.
  */
 public void setYRotationFactor(double factor) {
   YRotationFactor = factor;
 }
 /**
  * Get the current Y rotation scaling factor for Y-axis rotations.
  */
 public double getYRotationFactor() {
   return YRotationFactor;
 }
 /**
  * Set the X translation scaling factor for X-axis translations.
  * 
  * @param factor
  *            The new scaling factor.
  */
 public void setXTranslationFactor(double factor) {
   XTranslationFactor = factor;
 }
 /**
  * Get the current X translation scaling factor for X-axis translations.
  */
 public double getXTranslationFactor() {
   return XTranslationFactor;
 }
 /**
  * Set the Y translation scaling factor for Y-axis translations.
  * 
  * @param factor
  *            The new scaling factor.
  */
 public void setYTranslationFactor(double factor) {
   YTranslationFactor = factor;
 }
 /**
  * Get the current Y translation scaling factor for Y-axis translations.
  */
 public double getYTranslationFactor() {
   return YTranslationFactor;
 }
 /**
  * Set the Z translation scaling factor for Z-axis translations.
  * 
  * @param factor
  *            The new scaling factor.
  */
 public void setZTranslationFactor(double factor) {
   ZTranslationFactor = factor;
 }
 /**
  * Get the current Z translation scaling factor for Z-axis translations.
  */
 public double getZTranslationFactor() {
   return ZTranslationFactor;
 }
 /**
  * Respond to a button1 event (press, release, or drag).
  * 
  * @param mouseEvent
  *            A MouseEvent to respond to.
  */
 public abstract void onButton1(MouseEvent mouseEvent);
 /**
  * Respond to a button2 event (press, release, or drag).
  * 
  * @param mouseEvent
  *            A MouseEvent to respond to.
  */
 public abstract void onButton2(MouseEvent mouseEvent);
 /**
  * Responed to a button3 event (press, release, or drag).
  * 
  * @param mouseEvent
  *            A MouseEvent to respond to.
  */
 public abstract void onButton3(MouseEvent mouseEvent);
 /**
  * Respond to an elapsed frames event (assuming subclass has set up a wakeup
  * criterion for it).
  * 
  * @param time
  *            A WakeupOnElapsedFrames criterion to respond to.
  */
 public abstract void onElapsedFrames(WakeupOnElapsedFrames timeEvent);

} // //CLASS //NameValue - create a handy name-value pair // //DESCRIPTION //It is frequently handy to have one or more name-value pairs //with which to store named colors, named positions, named textures, //and so forth. Several of the examples use this class. // //AUTHOR //David R. Nadeau / San Diego Supercomputer Center // class NameValue {

 public String name;
 public Object value;
 public NameValue(String n, Object v) {
   name = n;
   value = v;
 }

}

      </source>
   
  
 
  



ExSpotLight - illustrate use of spot lights

   <source lang="java">

// //CLASS //ExSpotLight - illustrate use of spot lights // //LESSON //Add a SpotLight node to illuminate a scene. // //SEE ALSO //ExAmbientLight //ExDirectionalLight //ExPointLight // //AUTHOR //David R. Nadeau / San Diego Supercomputer Center // import java.applet.Applet; import java.awt.AWTEvent; import java.awt.BorderLayout; import java.awt.CheckboxMenuItem; import java.awt.ruponent; import java.awt.Cursor; import java.awt.Frame; import java.awt.Menu; import java.awt.MenuBar; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.InputEvent; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.event.MouseEvent; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.io.File; import java.util.Enumeration; import java.util.EventListener; 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.ColoringAttributes; import javax.media.j3d.DirectionalLight; import javax.media.j3d.GeometryArray; import javax.media.j3d.Group; import javax.media.j3d.Light; import javax.media.j3d.LineArray; import javax.media.j3d.LineAttributes; import javax.media.j3d.Material; import javax.media.j3d.Shape3D; import javax.media.j3d.SpotLight; import javax.media.j3d.Switch; import javax.media.j3d.Transform3D; import javax.media.j3d.TransformGroup; import javax.media.j3d.WakeupCriterion; import javax.media.j3d.WakeupOnAWTEvent; import javax.media.j3d.WakeupOnElapsedFrames; import javax.media.j3d.WakeupOr; import javax.vecmath.AxisAngle4f; import javax.vecmath.Color3f; import javax.vecmath.Matrix4d; import javax.vecmath.Matrix4f; import javax.vecmath.Point3d; import javax.vecmath.Point3f; import javax.vecmath.Vector3d; import javax.vecmath.Vector3f; import com.sun.j3d.utils.geometry.Cone; import com.sun.j3d.utils.geometry.Primitive; import com.sun.j3d.utils.geometry.Sphere; import com.sun.j3d.utils.universe.PlatformGeometry; import com.sun.j3d.utils.universe.SimpleUniverse; import com.sun.j3d.utils.universe.Viewer; import com.sun.j3d.utils.universe.ViewingPlatform; public class ExSpotLight extends Java3DFrame {

 //--------------------------------------------------------------
 //  SCENE CONTENT
 //--------------------------------------------------------------
 //
 //  Nodes (updated via menu)
 //
 private SpotLight light = null;
 //
 //  Build scene
 //
 public Group buildScene() {
   // Get the current color, position, attenuation,
   // spread angle, and concentration
   Color3f color = (Color3f) colors[currentColor].value;
   Point3f pos = (Point3f) positions[currentPosition].value;
   Vector3f dir = (Vector3f) directions[currentDirection].value;
   Point3f atten = (Point3f) attenuations[currentAttenuation].value;
   float spread = ((Double) spreads[currentSpread].value).floatValue();
   float concen = ((Double) concentrations[currentConcentration].value)
       .floatValue();
   // Turn off the example headlight
   setHeadlightEnable(false);
   // Build the scene root
   Group scene = new Group();
   // BEGIN EXAMPLE TOPIC
   // Create influencing bounds
   BoundingSphere worldBounds = new BoundingSphere(new Point3d(0.0, 0.0,
       0.0), // Center
       1000.0); // Extent
   // Set the light color and its influencing bounds
   light = new SpotLight();
   light.setEnable(lightOnOff);
   light.setColor(color);
   light.setPosition(pos);
   light.setAttenuation(atten);
   light.setDirection(dir);
   light.setSpreadAngle(spread);
   light.setConcentration(concen);
   light.setCapability(SpotLight.ALLOW_STATE_WRITE);
   light.setCapability(SpotLight.ALLOW_COLOR_WRITE);
   light.setCapability(SpotLight.ALLOW_POSITION_WRITE);
   light.setCapability(SpotLight.ALLOW_ATTENUATION_WRITE);
   light.setCapability(SpotLight.ALLOW_DIRECTION_WRITE);
   light.setCapability(SpotLight.ALLOW_SPREAD_ANGLE_WRITE);
   light.setCapability(SpotLight.ALLOW_CONCENTRATION_WRITE);
   light.setInfluencingBounds(worldBounds);
   scene.addChild(light);
   // END EXAMPLE TOPIC
   // Build foreground geometry
   scene.addChild(new SphereGroup());
   // Add annotation arrows in a fan to show light ray directions,
   // positions, and the spread angle
   scene.addChild(buildArrows());
   return scene;
 }
 //--------------------------------------------------------------
 //  FOREGROUND AND ANNOTATION CONTENT
 //--------------------------------------------------------------
 //
 //  Create a set of fans of annotation arrows initially pointing in
 //  the +X direction. Each fan in the set illustrates a different
 //  light spread angle listed in the user interface light control
 //  menu. Next, build two TransformGroups, one nested within
 //  the other, and place the fan within the innermost TransformGroup.
 //  The outer TransformGroup will be used to position the fan,
 //  and the inner one will be used to rotate the fan. The position
 //  and orientation are both selected via menu items. To do the
 //  position and orientation change, we compute a bunch of Transform3Ds
 //  for each menu choice position or orientation and save them.
 //  Later, when the user selects a new light direction or position,
 //  we poke the corresponding Transform3D into the appropriate
 //  TransformGroup, causing the arrows to change position or direction.
 //
 private TransformGroup arrowDirectionTransformGroup = null;
 private Transform3D[] arrowDirectionTransforms = null;
 private TransformGroup arrowPositionTransformGroup = null;
 private Transform3D[] arrowPositionTransforms = null;
 private Switch arrowSpreadAngleSwitch = null;
 private Group buildArrows() {
   // Create a switch group to hold the different arrow fan
   // spread angle choices. Enable child choice writing.
   arrowSpreadAngleSwitch = new Switch();
   arrowSpreadAngleSwitch.setCapability(Switch.ALLOW_SWITCH_WRITE);
   // Create a set of arrow fans, one per spread angle
   // shown on the menu.
   AnnotationArrowFan af = null;
   float spread = 0.0f;
   for (int i = 0; i < spreads.length; i++) {
     spread = ((Double) spreads[i].value).floatValue();
     af = new AnnotationArrowFan(0.0f, 0.0f, 0.0f, // center position
         2.5f, // arrow length
         -spread, // start angle
         spread, // end angle
         5); // number of arrows
     arrowSpreadAngleSwitch.addChild(af);
   }
   // Select the current fan.
   arrowSpreadAngleSwitch.setWhichChild(currentSpread);
   // Create an outer transform group used to change the fan
   // position. Enable writing of its transform.
   arrowPositionTransformGroup = new TransformGroup();
   arrowPositionTransformGroup
       .setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
   // Create a set of Transform3Ds for the different arrow positions.
   arrowPositionTransforms = new Transform3D[positions.length];
   Point3f pos;
   Vector3f v = new Vector3f();
   for (int i = 0; i < positions.length; i++) {
     // Create a Transform3D, setting its translation.
     arrowPositionTransforms[i] = new Transform3D();
     pos = (Point3f) positions[i].value;
     v.set(pos);
     arrowPositionTransforms[i].setTranslation(v);
   }
   // Set the initial transform to be the current position
   arrowPositionTransformGroup
       .setTransform(arrowPositionTransforms[currentPosition]);
   // Create an inner transform group surrounding the arrows,
   // used to set the aim direction. Enable writing of its transform.
   arrowDirectionTransformGroup = new TransformGroup();
   arrowDirectionTransformGroup
       .setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
   // Add the switch group to the direction-change transform group,
   // and add the direction-change transform group to the
   // position-change transform gorup.
   arrowDirectionTransformGroup.addChild(arrowSpreadAngleSwitch);
   arrowPositionTransformGroup.addChild(arrowDirectionTransformGroup);
   // Create a set of Transform3Ds for the different
   // arrow directions.
   arrowDirectionTransforms = new Transform3D[directions.length];
   Vector3f dir = new Vector3f();
   Vector3f positiveX = new Vector3f(1.0f, 0.0f, 0.0f);
   Vector3f axis = new Vector3f();
   float angle;
   float dot;
   for (int i = 0; i < directions.length; i++) {
     // Normalize the direction vector
     dir.normalize((Vector3f) directions[i].value);
     // Cross the direction vector with the arrow"s
     // +X aim direction to get a vector orthogonal
     // to both. This is the rotation axis.
     axis.cross(positiveX, dir);
     if (axis.x == 0.0f && axis.y == 0.0f && axis.z == 0.0f) {
       // New direction is parallel to current
       // arrow direction. Default to a Y axis.
       axis.y = 1.0f;
     }
     // Compute the angle between the direction and +X
     // vectors, where:
     //
     //   cos(angle) = (dir dot positiveX)
     //                -------------------------------
     //                (positiveX.length * dir.length)
     //
     // but since positiveX is normalized (as created
     // above) and dir has been normalized, both have
     // a length of 1. So, the angle between the
     // vectors is:
     //
     //   angle = arccos(dir dot positiveX)
     dot = dir.dot(positiveX);
     angle = (float) Math.acos(dot);
     // Create a Transform3D, setting its rotation using
     // an AxisAngle4f, which takes an XYZ rotation vector
     // and an angle to rotate by around that vector.
     arrowDirectionTransforms[i] = new Transform3D();
     arrowDirectionTransforms[i].setRotation(new AxisAngle4f(axis.x,
         axis.y, axis.z, angle));
   }
   // Set the initial transform to be the current aim direction.
   arrowDirectionTransformGroup
       .setTransform(arrowDirectionTransforms[currentDirection]);
   return arrowPositionTransformGroup;
 }
 //--------------------------------------------------------------
 //  USER INTERFACE
 //--------------------------------------------------------------
 //
 //  Main
 //
 public static void main(String[] args) {
   ExSpotLight ex = new ExSpotLight();
   ex.initialize(args);
   ex.buildUniverse();
   ex.showFrame();
 }
 //  On/off choices
 private boolean lightOnOff = true;
 private CheckboxMenuItem lightOnOffMenu;
 //  Color menu choices
 private NameValue[] colors = { new NameValue("White", White),
     new NameValue("Gray", Gray), new NameValue("Black", Black),
     new NameValue("Red", Red), new NameValue("Yellow", Yellow),
     new NameValue("Green", Green), new NameValue("Cyan", Cyan),
     new NameValue("Blue", Blue), new NameValue("Magenta", Magenta), };
 private int currentColor = 0;
 private CheckboxMenu colorMenu = null;
 //  Position menu choices
 private NameValue[] positions = { new NameValue("Origin", Origin),
     new NameValue("+X", PlusX), new NameValue("-X", MinusX),
     new NameValue("+Y", PlusY), new NameValue("-Y", MinusY),
     new NameValue("+Z", PlusZ), new NameValue("-Z", MinusZ), };
 private int currentPosition = 0;
 private CheckboxMenu positionMenu = null;
 //  Attenuation menu choices
 private NameValue[] attenuations = {
     new NameValue("Constant", new Point3f(1.0f, 0.0f, 0.0f)),
     new NameValue("Linear", new Point3f(0.0f, 1.0f, 0.0f)),
     new NameValue("Quadratic", new Point3f(0.0f, 0.0f, 1.0f)), };
 private int currentAttenuation = 0;
 private CheckboxMenu attenuationMenu = null;
 //  Direction menu choices
 private NameValue[] directions = { new NameValue("Positive X", PosX),
     new NameValue("Negative X", NegX),
     new NameValue("Positive Y", PosY),
     new NameValue("Negative Y", NegY),
     new NameValue("Positive Z", PosZ),
     new NameValue("Negative Z", NegZ), };
 private int currentDirection = 0;
 private CheckboxMenu directionMenu = null;
 //  Spread angle choices
 private NameValue[] spreads = {
     new NameValue("22.5 degrees", new Double(Math.PI / 8.0)),
     new NameValue("45.0 degrees", new Double(Math.PI / 4.0)),
     new NameValue("90.0 degrees", new Double(Math.PI / 2.0)), };
 private int currentSpread = 1;
 private CheckboxMenu spreadMenu = null;
 //  Concentration choices
 private NameValue[] concentrations = {
     new NameValue("0.0", new Double(0.0)),
     new NameValue("5.0", new Double(5.0)),
     new NameValue("10.0", new Double(10.0)),
     new NameValue("50.0", new Double(50.0)),
     new NameValue("100.0", new Double(100.0)), };
 private int currentConcentration = 0;
 private CheckboxMenu concentrationMenu = null;
 //
 //  Initialize the GUI (application and applet)
 //
 public void initialize(String[] args) {
   // Initialize the window, menubar, etc.
   super.initialize(args);
   exampleFrame.setTitle("Java 3D Spot Light Example");
   //
   //  Add a menubar menu to change node parameters
   //    Light on/off
   //    Color -->
   //    Position -->
   //    Direction -->
   //    Attenuation -->
   //    Spread Angle -->
   //    Concentration -->
   //
   Menu m = new Menu("SpotLight");
   lightOnOffMenu = new CheckboxMenuItem("Light on/off");
   lightOnOffMenu.addItemListener(this);
   lightOnOffMenu.setState(lightOnOff);
   m.add(lightOnOffMenu);
   colorMenu = new CheckboxMenu("Color", colors, currentColor, this);
   m.add(colorMenu);
   positionMenu = new CheckboxMenu("Position", positions, currentPosition,
       this);
   m.add(positionMenu);
   directionMenu = new CheckboxMenu("Direction", directions,
       currentDirection, this);
   m.add(directionMenu);
   attenuationMenu = new CheckboxMenu("Attenuation", attenuations,
       currentAttenuation, this);
   m.add(attenuationMenu);
   spreadMenu = new CheckboxMenu("Spread Angle", spreads, currentSpread,
       this);
   m.add(spreadMenu);
   concentrationMenu = new CheckboxMenu("Concentration", concentrations,
       currentConcentration, this);
   m.add(concentrationMenu);
   exampleMenuBar.add(m);
 }
 //
 //  Handle checkboxes and menu choices
 //
 public void checkboxChanged(CheckboxMenu menu, int check) {
   if (menu == colorMenu) {
     // Change the light color
     currentColor = check;
     Color3f color = (Color3f) colors[check].value;
     light.setColor(color);
     return;
   }
   if (menu == positionMenu) {
     // Change the light position
     currentPosition = check;
     Point3f pos = (Point3f) positions[check].value;
     light.setPosition(pos);
     // Change the arrow group position
     arrowPositionTransformGroup
         .setTransform(arrowPositionTransforms[check]);
     return;
   }
   if (menu == directionMenu) {
     // Change the light direction
     currentDirection = check;
     Vector3f dir = (Vector3f) directions[check].value;
     light.setDirection(dir);
     // Change the arrow group direction
     arrowDirectionTransformGroup
         .setTransform(arrowDirectionTransforms[check]);
     return;
   }
   if (menu == attenuationMenu) {
     // Change the light attenuation
     currentAttenuation = check;
     Point3f atten = (Point3f) attenuations[check].value;
     light.setAttenuation(atten);
     return;
   }
   if (menu == spreadMenu) {
     // Change the light spread angle
     currentSpread = check;
     arrowSpreadAngleSwitch.setWhichChild(check);
     float spread = ((Double) spreads[check].value).floatValue();
     light.setSpreadAngle(spread);
     return;
   }
   if (menu == concentrationMenu) {
     // Change the light concentration
     currentConcentration = check;
     float concen = ((Double) concentrations[check].value).floatValue();
     light.setConcentration(concen);
     return;
   }
   // Handle all other checkboxes
   super.checkboxChanged(menu, check);
 }
 public void itemStateChanged(ItemEvent event) {
   Object src = event.getSource();
   if (src == lightOnOffMenu) {
     // Turn the light on or off
     lightOnOff = lightOnOffMenu.getState();
     light.setEnable(lightOnOff);
     return;
   }
   // Handle all other checkboxes
   super.itemStateChanged(event);
 }

} // //CLASS //AnnotationArrowFan - A group of arrows in a fan // //DESCRIPTION //This class creates one or more 3D, unlighted arrows arranged in a //fan around the xyz position. Such arrow fans can be used to indicate //point light directions, and so forth. // //The arrow fan is drawn in the XY plane, pointing right (middle arrow). //The fan origin, arrow length, start and end angles, and number of //arrows all may be controlled. // //SEE ALSO //AnnotationArrow //AnnotationArrowGroup // //AUTHOR //David R. Nadeau / San Diego Supercomputer Center // // class AnnotationArrowFan extends Group {

 // 3D nodes
 AnnotationArrow[] arrows;
 //  Constructors
 public AnnotationArrowFan() {
   //    xyz length start/end angles count
   this(0.0f, 0.0f, 0.0f, 1.0f, 1.571f, -1.571f, 5);
 }
 public AnnotationArrowFan(float x, float y, float z, float length,
     float startAngle, float endAngle, int count) {
   arrows = new AnnotationArrow[count];
   float x2, y2;
   float angle = startAngle;
   float deltaAngle = (endAngle - startAngle) / (float) (count - 1);
   for (int i = 0; i < count; i++) {
     x2 = (float) (length * Math.cos(angle));
     y2 = (float) (length * Math.sin(angle));
     arrows[i] = new AnnotationArrow(x, y, z, x2, y2, z);
     addChild(arrows[i]);
     angle += deltaAngle;
   }
 }

} // //CLASS //AnnotationArrowGroup - A group of parallel arrows // //DESCRIPTION //This class creates one or more parallel 3D, unlighted arrows. //Such arrow groups can be used to indicate directional light //directions, and so forth. // //The arrow group is drawn in the XY plane, pointing right. //The X start and end values, and the Y start and end values //can be set, along with the count of the number of arrows to //build. // //SEE ALSO //AnnotationArrow //AnnotationArrowFan // //AUTHOR //David R. Nadeau / San Diego Supercomputer Center // // class AnnotationArrowGroup extends Group {

 // 3D nodes
 AnnotationArrow[] arrows;
 //  Constructors
 public AnnotationArrowGroup() {
   //    xStart xEnd yStart yEnd count
   this(-1.0f, 1.0f, 1.0f, -1.0f, 3);
 }
 public AnnotationArrowGroup(float xStart, float xEnd, float yStart,
     float yEnd, int count) {
   arrows = new AnnotationArrow[count];
   float y = yStart;
   float deltaY = (yEnd - yStart) / (float) (count - 1);
   for (int i = 0; i < count; i++) {
     arrows[i] = new AnnotationArrow(xStart, y, 0.0f, xEnd, y, 0.0f);
     addChild(arrows[i]);
     y += deltaY;
   }
 }

} // //CLASS //AnnotationArrow - 3D arrow used for annotation & diagrams // //DESCRIPTION //This class creates a 3D, unlighted line between two 3D coordinates //plus a cone-shaped arrow at the line"s endpoint. The line"s width //and color can be controlled. The arrow head"s width and length //can be controlled. // //SEE ALSO //AnnotationLine //AnnotationAxes //AnnotationArrowFan //AnnotationArrowGroup // //AUTHOR //David R. Nadeau / San Diego Supercomputer Center // class AnnotationArrow extends AnnotationLine {

 // Parameters
 private Color3f arrowColor = new Color3f(1.0f, 1.0f, 1.0f);
 private float arrowRadius = 0.1f;
 private float arrowLength = 0.20f;
 private float lineWidth = 3.0f;
 private int radialDivisions = 8;
 private int sideDivisions = 1;
 // 3D Nodes
 private Cone arrowHead = null;
 private Appearance arrowAppearance = null;
 private TransformGroup arrowTrans = null;
 private ColoringAttributes coloringAttributes = null;
 //
 //  Construct a straight line
 //
 public AnnotationArrow(float x2, float y2, float z2) {
   //    origin to given coordinate
   this(0.0f, 0.0f, 0.0f, x2, y2, z2);
 }
 public AnnotationArrow(float x, float y, float z, float x2, float y2,
     float z2) {
   super(x, y, z, x2, y2, z2);
   setLineWidth(lineWidth);
   // Compute the length and direction of the line
   float deltaX = x2 - x;
   float deltaY = y2 - y;
   float deltaZ = z2 - z;
   float theta = -(float) Math.atan2(deltaZ, deltaX);
   float phi = (float) Math.atan2(deltaY, deltaX);
   if (deltaX < 0.0f) {
     phi = (float) Math.PI - phi;
   }
   // Compute a matrix to rotate a cone to point in the line"s
   // direction, then place the cone at the line"s endpoint.
   Matrix4f mat = new Matrix4f();
   Matrix4f mat2 = new Matrix4f();
   mat.setIdentity();
   // Move to the endpoint of the line
   mat2.setIdentity();
   mat2.setTranslation(new Vector3f(x2, y2, z2));
   mat.mul(mat2);
   // Spin around Y
   mat2.setIdentity();
   mat2.rotY(theta);
   mat.mul(mat2);
   // Tilt up or down around Z
   mat2.setIdentity();
   mat2.rotZ(phi);
   mat.mul(mat2);
   // Tilt cone to point right
   mat2.setIdentity();
   mat2.rotZ(-1.571f);
   mat.mul(mat2);
   arrowTrans = new TransformGroup();
   arrowTrans.setCapability(Group.ALLOW_CHILDREN_WRITE);
   Transform3D trans = new Transform3D(mat);
   arrowTrans.setTransform(trans);
   // Create an appearance
   arrowAppearance = new Appearance();
   arrowAppearance
       .setCapability(Appearance.ALLOW_COLORING_ATTRIBUTES_WRITE);
   getLineColor(arrowColor);
   coloringAttributes = new ColoringAttributes();
   coloringAttributes.setColor(arrowColor);
   coloringAttributes.setShadeModel(ColoringAttributes.SHADE_FLAT);
   arrowAppearance.setColoringAttributes(coloringAttributes);
   // Build a cone for the arrow head
   arrowHead = new Cone(arrowRadius, // base radius
       arrowLength, // height
       0, // don"t generate normals
       radialDivisions, // divisions radially
       sideDivisions, // divisions vertically
       arrowAppearance); // appearance
   arrowTrans.addChild(arrowHead);
   addChild(arrowTrans);
 }
 //
 //  Control the arrow head size
 //
 public void setArrowHeadRadius(float radius) {
   arrowRadius = radius;
   arrowTrans.removeChild(0);
   arrowHead = new Cone(arrowRadius, // base radius
       arrowLength, // height
       0, // don"t generate normals
       radialDivisions, // divisions radially
       sideDivisions, // divisions vertically
       arrowAppearance); // appearance
   arrowTrans.addChild(arrowHead);
 }
 public void setArrowHeadLength(float length) {
   arrowLength = length;
   arrowTrans.removeChild(0);
   arrowHead = new Cone(arrowRadius, // base radius
       arrowLength, // height
       0, // don"t generate normals
       radialDivisions, // divisions radially
       sideDivisions, // divisions vertically
       arrowAppearance); // appearance
   arrowTrans.addChild(arrowHead);
 }
 public float getArrowHeadRadius() {
   return arrowRadius;
 }
 public float getArrowHeadLength() {
   return arrowLength;
 }
 //
 //  Control the line color
 //
 public void setLineColor(Color3f color) {
   super.setLineColor(color);
   getLineColor(arrowColor);
   coloringAttributes.setColor(arrowColor);
   arrowAppearance.setColoringAttributes(coloringAttributes);
   arrowHead.setAppearance(arrowAppearance);
 }
 public void setLineColor(float r, float g, float b) {
   super.setLineColor(r, g, b);
   getLineColor(arrowColor);
   coloringAttributes.setColor(arrowColor);
   arrowAppearance.setColoringAttributes(coloringAttributes);
   arrowHead.setAppearance(arrowAppearance);
 }
 public void setLineColor(float[] color) {
   super.setLineColor(color);
   getLineColor(arrowColor);
   coloringAttributes.setColor(arrowColor);
   arrowAppearance.setColoringAttributes(coloringAttributes);
   arrowHead.setAppearance(arrowAppearance);
 }
 //
 //  Control the appearance
 //
 public void setAppearance(Appearance app) {
   super.setAppearance(app);
   arrowAppearance = app;
   arrowAppearance
       .setCapability(Appearance.ALLOW_COLORING_ATTRIBUTES_WRITE);
   arrowAppearance.setColoringAttributes(coloringAttributes);
   arrowHead.setAppearance(arrowAppearance);
 }
 //
 //  Provide info on the shape and geometry
 //
 public Shape3D getShape(int partid) {
   if (partid == Cone.BODY)
     return arrowHead.getShape(Cone.BODY);
   else if (partid == Cone.CAP)
     return arrowHead.getShape(Cone.CAP);
   else
     return super.getShape(partid);
 }
 public int getNumTriangles() {
   return arrowHead.getNumTriangles();
 }
 public int getNumVertices() {
   return arrowHead.getNumVertices() + super.getNumVertices();
 }

} // //CLASS //AnnotationLine - 3D line used for annotation & diagrams // //DESCRIPTION //This class creates a 3D, unlighted line between two 3D coordinates. //The line"s width and color can be controlled. // //SEE ALSO //AnnotationArrow // //AUTHOR //David R. Nadeau / San Diego Supercomputer Center // // class AnnotationLine extends Primitive {

 // Parameters
 private float lineWidth = 1;
 private Color3f lineColor = new Color3f(1.0f, 1.0f, 1.0f);
 // 3D nodes
 private Shape3D shape = null;
 private LineAttributes lineAttributes = null;
 private ColoringAttributes coloringAttributes = null;
 private LineArray line = null;
 protected Appearance mainAppearance = null;
 //
 //  Construct a straight line
 //
 public AnnotationLine(float x2, float y2, float z2) {
   //    origin to given coordinate
   this(0.0f, 0.0f, 0.0f, x2, y2, z2);
 }
 public AnnotationLine(float x, float y, float z, float x2, float y2,
     float z2) {
   float[] coord = new float[3];
   float[] texcoord = new float[2];
   // Build a shape
   shape = new Shape3D();
   shape.setCapability(Shape3D.ALLOW_APPEARANCE_WRITE);
   // Create geometry for a 2-vertex straight line
   line = new LineArray(2, GeometryArray.COORDINATES
       | GeometryArray.TEXTURE_COORDINATE_2);
   line.setCapability(GeometryArray.ALLOW_COLOR_WRITE);
   // Starting point
   coord[0] = x;
   coord[1] = y;
   coord[2] = z;
   texcoord[0] = 0.0f;
   texcoord[1] = 0.0f;
   line.setCoordinate(0, coord);
   line.setTextureCoordinate(0, texcoord);
   // Ending point
   coord[0] = x2;
   coord[1] = y2;
   coord[2] = z2;
   texcoord[0] = 1.0f;
   texcoord[1] = 0.0f;
   line.setCoordinate(1, coord);
   line.setTextureCoordinate(1, texcoord);
   shape.setGeometry(line);
   // Create an appearance
   mainAppearance = new Appearance();
   mainAppearance.setCapability(Appearance.ALLOW_LINE_ATTRIBUTES_WRITE);
   mainAppearance
       .setCapability(Appearance.ALLOW_COLORING_ATTRIBUTES_WRITE);
   lineAttributes = new LineAttributes();
   lineAttributes.setLineWidth(lineWidth);
   mainAppearance.setLineAttributes(lineAttributes);
   coloringAttributes = new ColoringAttributes();
   coloringAttributes.setColor(lineColor);
   coloringAttributes.setShadeModel(ColoringAttributes.SHADE_FLAT);
   mainAppearance.setColoringAttributes(coloringAttributes);
   addChild(shape);
 }
 //
 //  Control the line width
 //
 public float getLineWidth() {
   return lineWidth;
 }
 public void setLineWidth(float width) {
   lineWidth = width;
   lineAttributes.setLineWidth(lineWidth);
   mainAppearance.setLineAttributes(lineAttributes);
   shape.setAppearance(mainAppearance);
 }
 //
 //  Control the line color
 //
 public void getLineColor(Color3f color) {
   lineColor.get(color);
 }
 public void getLineColor(float[] color) {
   lineColor.get(color);
 }
 public void setLineColor(Color3f color) {
   lineColor.set(color);
   coloringAttributes.setColor(lineColor);
   mainAppearance.setColoringAttributes(coloringAttributes);
   shape.setAppearance(mainAppearance);
 }
 public void setLineColor(float r, float g, float b) {
   lineColor.set(r, g, b);
   coloringAttributes.setColor(lineColor);
   mainAppearance.setColoringAttributes(coloringAttributes);
   shape.setAppearance(mainAppearance);
 }
 public void setLineColor(float[] color) {
   lineColor.set(color);
   coloringAttributes.setColor(lineColor);
   mainAppearance.setColoringAttributes(coloringAttributes);
   shape.setAppearance(mainAppearance);
 }
 //
 //  Control the appearance
 //
 public void setAppearance(Appearance app) {
   mainAppearance = app;
   mainAppearance.setCapability(Appearance.ALLOW_LINE_ATTRIBUTES_WRITE);
   mainAppearance
       .setCapability(Appearance.ALLOW_COLORING_ATTRIBUTES_WRITE);
   mainAppearance.setLineAttributes(lineAttributes);
   mainAppearance.setColoringAttributes(coloringAttributes);
   shape.setAppearance(mainAppearance);
 }
 //
 //  Provide info on the shape and geometry
 //
 public Shape3D getShape(int partid) {
   return shape;
 }
 public int getNumTriangles() {
   return 0;
 }
 public int getNumVertices() {
   return 2;
 }
 /*
  * (non-Javadoc)
  * 
  * @see com.sun.j3d.utils.geometry.Primitive#getAppearance(int)
  */
 public Appearance getAppearance(int arg0) {
   // TODO Auto-generated method stub
   return null;
 }

} // //CLASS //SphereGroup - create a group of spheres on the XY plane // //DESCRIPTION //An XY grid of spheres is created. The number of spheres in X and Y, //the spacing in X and Y, the sphere radius, and the appearance can //all be set. // //This grid of spheres is used by several of the examples as a generic //bit of foreground geometry. // //SEE ALSO //Ex*Light //ExBackground* // //AUTHOR //David R. Nadeau / San Diego Supercomputer Center // class SphereGroup extends Group {

 //  Constructors
 public SphereGroup() {
   //    radius x,y spacing x,y count appearance
   this(0.25f, 0.75f, 0.75f, 5, 5, null);
 }
 public SphereGroup(Appearance app) {
   //    radius x,y spacing x,y count appearance
   this(0.25f, 0.75f, 0.75f, 5, 5, app);
 }
 public SphereGroup(float radius, float xSpacing, float ySpacing,
     int xCount, int yCount) {
   this(radius, xSpacing, ySpacing, xCount, yCount, null);
 }
 public SphereGroup(float radius, float xSpacing, float ySpacing,
     int xCount, int yCount, Appearance app) {
   if (app == null) {
     app = new Appearance();
     Material material = new Material();
     material.setDiffuseColor(new Color3f(0.8f, 0.8f, 0.8f));
     material.setSpecularColor(new Color3f(0.0f, 0.0f, 0.0f));
     material.setShininess(0.0f);
     app.setMaterial(material);
   }
   double xStart = -xSpacing * (double) (xCount - 1) / 2.0;
   double yStart = -ySpacing * (double) (yCount - 1) / 2.0;
   Sphere sphere = null;
   TransformGroup trans = null;
   Transform3D t3d = new Transform3D();
   Vector3d vec = new Vector3d();
   double x, y = yStart, z = 0.0;
   for (int i = 0; i < yCount; i++) {
     x = xStart;
     for (int j = 0; j < xCount; j++) {
       vec.set(x, y, z);
       t3d.setTranslation(vec);
       trans = new TransformGroup(t3d);
       addChild(trans);
       sphere = new Sphere(radius, // sphere radius
           Primitive.GENERATE_NORMALS, // generate normals
           16, // 16 divisions radially
           app); // it"s appearance
       trans.addChild(sphere);
       x += xSpacing;
     }
     y += ySpacing;
   }
 }

} /**

* The Example class is a base class extended by example applications. The class
* provides basic features to create a top-level frame, add a menubar and
* Canvas3D, build the universe, set up "examine" and "walk" style navigation
* behaviors, and provide hooks so that subclasses can add 3D content to the
* example"s universe.
* <P>
* Using this Example class simplifies the construction of example applications,
* enabling the author to focus upon 3D content and not the busywork of creating
* windows, menus, and universes.
* 
* @version 1.0, 98/04/16
* @author David R. Nadeau, San Diego Supercomputer Center
*/

class Java3DFrame extends Applet implements WindowListener, ActionListener,

   ItemListener, CheckboxMenuListener {
 //  Navigation types
 public final static int Walk = 0;
 public final static int Examine = 1;
 //  Should the scene be compiled?
 private boolean shouldCompile = true;
 //  GUI objects for our subclasses
 protected Java3DFrame example = null;
 protected Frame exampleFrame = null;
 protected MenuBar exampleMenuBar = null;
 protected Canvas3D exampleCanvas = null;
 protected TransformGroup exampleViewTransform = null;
 protected TransformGroup exampleSceneTransform = null;
 protected boolean debug = false;
 //  Private GUI objects and state
 private boolean headlightOnOff = true;
 private int navigationType = Examine;
 private CheckboxMenuItem headlightMenuItem = null;
 private CheckboxMenuItem walkMenuItem = null;
 private CheckboxMenuItem examineMenuItem = null;
 private DirectionalLight headlight = null;
 private ExamineViewerBehavior examineBehavior = null;
 private WalkViewerBehavior walkBehavior = null;
 //--------------------------------------------------------------
 //  ADMINISTRATION
 //--------------------------------------------------------------
 /**
  * The main program entry point when invoked as an application. Each example
  * application that extends this class must define their own main.
  * 
  * @param args
  *            a String array of command-line arguments
  */
 public static void main(String[] args) {
   Java3DFrame ex = new Java3DFrame();
   ex.initialize(args);
   ex.buildUniverse();
   ex.showFrame();
 }
 /**
  * Constructs a new Example object.
  * 
  * @return a new Example that draws no 3D content
  */
 public Java3DFrame() {
   // Do nothing
 }
 /**
  * Initializes the application when invoked as an applet.
  */
 public void init() {
   // Collect properties into String array
   String[] args = new String[2];
   // NOTE: to be done still...
   this.initialize(args);
   this.buildUniverse();
   this.showFrame();
   // NOTE: add something to the browser page?
 }
 /**
  * Initializes the Example by parsing command-line arguments, building an
  * AWT Frame, constructing a menubar, and creating the 3D canvas.
  * 
  * @param args
  *            a String array of command-line arguments
  */
 protected void initialize(String[] args) {
   example = this;
   // Parse incoming arguments
   parseArgs(args);
   // Build the frame
   if (debug)
     System.err.println("Building GUI...");
   exampleFrame = new Frame();
   exampleFrame.setSize(640, 480);
   exampleFrame.setTitle("Java 3D Example");
   exampleFrame.setLayout(new BorderLayout());
   // Set up a close behavior
   exampleFrame.addWindowListener(this);
   // Create a canvas
   exampleCanvas = new Canvas3D(null);
   exampleCanvas.setSize(630, 460);
   exampleFrame.add("Center", exampleCanvas);
   // Build the menubar
   exampleMenuBar = this.buildMenuBar();
   exampleFrame.setMenuBar(exampleMenuBar);
   // Pack
   exampleFrame.pack();
   exampleFrame.validate();
   //    exampleFrame.setVisible( true );
 }
 /**
  * Parses incoming command-line arguments. Applications that subclass this
  * class may override this method to support their own command-line
  * arguments.
  * 
  * @param args
  *            a String array of command-line arguments
  */
 protected void parseArgs(String[] args) {
   for (int i = 0; i < args.length; i++) {
     if (args[i].equals("-d"))
       debug = true;
   }
 }
 //--------------------------------------------------------------
 //  SCENE CONTENT
 //--------------------------------------------------------------
 /**
  * Builds the 3D universe by constructing a virtual universe (via
  * SimpleUniverse), a view platform (via SimpleUniverse), and a view (via
  * SimpleUniverse). A headlight is added and a set of behaviors initialized
  * to handle navigation types.
  */
 protected void buildUniverse() {
   //
   //  Create a SimpleUniverse object, which builds:
   //
   //    - a Locale using the given hi-res coordinate origin
   //
   //    - a ViewingPlatform which in turn builds:
   //          - a MultiTransformGroup with which to move the
   //            the ViewPlatform about
   //
   //          - a ViewPlatform to hold the view
   //
   //          - a BranchGroup to hold avatar geometry (if any)
   //
   //          - a BranchGroup to hold view platform
   //            geometry (if any)
   //
   //    - a Viewer which in turn builds:
   //          - a PhysicalBody which characterizes the user"s
   //            viewing preferences and abilities
   //
   //          - a PhysicalEnvironment which characterizes the
   //            user"s rendering hardware and software
   //
   //          - a JavaSoundMixer which initializes sound
   //            support within the 3D environment
   //
   //          - a View which renders the scene into a Canvas3D
   //
   //  All of these actions could be done explicitly, but
   //  using the SimpleUniverse utilities simplifies the code.
   //
   if (debug)
     System.err.println("Building scene graph...");
   SimpleUniverse universe = new SimpleUniverse(null, // Hi-res coordinate
       // for the origin -
       // use default
       1, // Number of transforms in MultiTransformGroup
       exampleCanvas, // Canvas3D into which to draw
       null); // URL for user configuration file - use defaults
   //
   //  Get the viewer and create an audio device so that
   //  sound will be enabled in this content.
   //
   Viewer viewer = universe.getViewer();
   viewer.createAudioDevice();
   //
   //  Get the viewing platform created by SimpleUniverse.
   //  From that platform, get the inner-most TransformGroup
   //  in the MultiTransformGroup. That inner-most group
   //  contains the ViewPlatform. It is this inner-most
   //  TransformGroup we need in order to:
   //
   //    - add a "headlight" that always aims forward from
   //       the viewer
   //
   //    - change the viewing direction in a "walk" style
   //
   //  The inner-most TransformGroup"s transform will be
   //  changed by the walk behavior (when enabled).
   //
   ViewingPlatform viewingPlatform = universe.getViewingPlatform();
   exampleViewTransform = viewingPlatform.getViewPlatformTransform();
   //
   //  Create a "headlight" as a forward-facing directional light.
   //  Set the light"s bounds to huge. Since we want the light
   //  on the viewer"s "head", we need the light within the
   //  TransformGroup containing the ViewPlatform. The
   //  ViewingPlatform class creates a handy hook to do this
   //  called "platform geometry". The PlatformGeometry class is
   //  subclassed off of BranchGroup, and is intended to contain
   //  a description of the 3D platform itself... PLUS a headlight!
   //  So, to add the headlight, create a new PlatformGeometry group,
   //  add the light to it, then add that platform geometry to the
   //  ViewingPlatform.
   //
   BoundingSphere allBounds = new BoundingSphere(
       new Point3d(0.0, 0.0, 0.0), 100000.0);
   PlatformGeometry pg = new PlatformGeometry();
   headlight = new DirectionalLight();
   headlight.setColor(White);
   headlight.setDirection(new Vector3f(0.0f, 0.0f, -1.0f));
   headlight.setInfluencingBounds(allBounds);
   headlight.setCapability(Light.ALLOW_STATE_WRITE);
   pg.addChild(headlight);
   viewingPlatform.setPlatformGeometry(pg);
   //
   //  Create the 3D content BranchGroup, containing:
   //
   //    - a TransformGroup who"s transform the examine behavior
   //      will change (when enabled).
   //
   //    - 3D geometry to view
   //
   // Build the scene root
   BranchGroup sceneRoot = new BranchGroup();
   // Build a transform that we can modify
   exampleSceneTransform = new TransformGroup();
   exampleSceneTransform
       .setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
   exampleSceneTransform
       .setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
   exampleSceneTransform.setCapability(Group.ALLOW_CHILDREN_EXTEND);
   //
   //  Build the scene, add it to the transform, and add
   //  the transform to the scene root
   //
   if (debug)
     System.err.println("  scene...");
   Group scene = this.buildScene();
   exampleSceneTransform.addChild(scene);
   sceneRoot.addChild(exampleSceneTransform);
   //
   //  Create a pair of behaviors to implement two navigation
   //  types:
   //
   //    - "examine": a style where mouse drags rotate about
   //      the scene"s origin as if it is an object under
   //      examination. This is similar to the "Examine"
   //      navigation type used by VRML browsers.
   //
   //    - "walk": a style where mouse drags rotate about
   //      the viewer"s center as if the viewer is turning
   //      about to look at a scene they are in. This is
   //      similar to the "Walk" navigation type used by
   //      VRML browsers.
   //
   //  Aim the examine behavior at the scene"s TransformGroup
   //  and add the behavior to the scene root.
   //
   //  Aim the walk behavior at the viewing platform"s
   //  TransformGroup and add the behavior to the scene root.
   //
   //  Enable one (and only one!) of the two behaviors
   //  depending upon the current navigation type.
   //
   examineBehavior = new ExamineViewerBehavior(exampleSceneTransform, // Transform
       // gorup
       // to
       // modify
       exampleFrame); // Parent frame for cusor changes
   examineBehavior.setSchedulingBounds(allBounds);
   sceneRoot.addChild(examineBehavior);
   walkBehavior = new WalkViewerBehavior(exampleViewTransform, // Transform
       // group to
       // modify
       exampleFrame); // Parent frame for cusor changes
   walkBehavior.setSchedulingBounds(allBounds);
   sceneRoot.addChild(walkBehavior);
   if (navigationType == Walk) {
     examineBehavior.setEnable(false);
     walkBehavior.setEnable(true);
   } else {
     examineBehavior.setEnable(true);
     walkBehavior.setEnable(false);
   }
   //
   //  Compile the scene branch group and add it to the
   //  SimpleUniverse.
   //
   if (shouldCompile)
     sceneRoot.rupile();
   universe.addBranchGraph(sceneRoot);
   reset();
 }
 /**
  * Builds the scene. Example application subclasses should replace this
  * method with their own method to build 3D content.
  * 
  * @return a Group containing 3D content to display
  */
 public Group buildScene() {
   // Build the scene group containing nothing
   Group scene = new Group();
   return scene;
 }
 //--------------------------------------------------------------
 //  SET/GET METHODS
 //--------------------------------------------------------------
 /**
  * Sets the headlight on/off state. The headlight faces forward in the
  * direction the viewer is facing. Example applications that add their own
  * lights will typically turn the headlight off. A standard menu item
  * enables the headlight to be turned on and off via user control.
  * 
  * @param onOff
  *            a boolean turning the light on (true) or off (false)
  */
 public void setHeadlightEnable(boolean onOff) {
   headlightOnOff = onOff;
   if (headlight != null)
     headlight.setEnable(headlightOnOff);
   if (headlightMenuItem != null)
     headlightMenuItem.setState(headlightOnOff);
 }
 /**
  * Gets the headlight on/off state.
  * 
  * @return a boolean indicating if the headlight is on or off
  */
 public boolean getHeadlightEnable() {
   return headlightOnOff;
 }
 /**
  * Sets the navigation type to be either Examine or Walk. The Examine
  * navigation type sets up behaviors that use mouse drags to rotate and
  * translate scene content as if it is an object held at arm"s length and
  * under examination. The Walk navigation type uses mouse drags to rotate
  * and translate the viewer as if they are walking through the content. The
  * Examine type is the default.
  * 
  * @param nav
  *            either Walk or Examine
  */
 public void setNavigationType(int nav) {
   if (nav == Walk) {
     navigationType = Walk;
     if (walkMenuItem != null)
       walkMenuItem.setState(true);
     if (examineMenuItem != null)
       examineMenuItem.setState(false);
     if (walkBehavior != null)
       walkBehavior.setEnable(true);
     if (examineBehavior != null)
       examineBehavior.setEnable(false);
   } else {
     navigationType = Examine;
     if (walkMenuItem != null)
       walkMenuItem.setState(false);
     if (examineMenuItem != null)
       examineMenuItem.setState(true);
     if (walkBehavior != null)
       walkBehavior.setEnable(false);
     if (examineBehavior != null)
       examineBehavior.setEnable(true);
   }
 }
 /**
  * Gets the current navigation type, returning either Walk or Examine.
  * 
  * @return either Walk or Examine
  */
 public int getNavigationType() {
   return navigationType;
 }
 /**
  * Sets whether the scene graph should be compiled or not. Normally this is
  * always a good idea. For some example applications that use this Example
  * framework, it is useful to disable compilation - particularly when nodes
  * and node components will need to be made un-live in order to make
  * changes. Once compiled, such components can be made un-live, but they are
  * still unchangable unless appropriate capabilities have been set.
  * 
  * @param onOff
  *            a boolean turning compilation on (true) or off (false)
  */
 public void setCompilable(boolean onOff) {
   shouldCompile = onOff;
 }
 /**
  * Gets whether the scene graph will be compiled or not.
  * 
  * @return a boolean indicating if scene graph compilation is on or off
  */
 public boolean getCompilable() {
   return shouldCompile;
 }
 //These methods will be replaced
 //  Set the view position and direction
 public void setViewpoint(Point3f position, Vector3f direction) {
   Transform3D t = new Transform3D();
   t.set(new Vector3f(position));
   exampleViewTransform.setTransform(t);
   // how to set direction?
 }
 //  Reset transforms
 public void reset() {
   Transform3D trans = new Transform3D();
   exampleSceneTransform.setTransform(trans);
   trans.set(new Vector3f(0.0f, 0.0f, 10.0f));
   exampleViewTransform.setTransform(trans);
   setNavigationType(navigationType);
 }
 //
 //  Gets the URL (with file: prepended) for the current directory.
 //  This is a terrible hack needed in the Alpha release of Java3D
 //  in order to build a full path URL for loading sounds with
 //  MediaContainer. When MediaContainer is fully implemented,
 //  it should handle relative path names, but not yet.
 //
 public String getCurrentDirectory() {
   // Create a bogus file so that we can query it"s path
   File dummy = new File("dummy.tmp");
   String dummyPath = dummy.getAbsolutePath();
   // strip "/dummy.tmp" from end of dummyPath and put into "path"
   if (dummyPath.endsWith(File.separator + "dummy.tmp")) {
     int index = dummyPath.lastIndexOf(File.separator + "dummy.tmp");
     if (index >= 0) {
       int pathLength = index + 5; // pre-pend "file:"
       char[] charPath = new char[pathLength];
       dummyPath.getChars(0, index, charPath, 5);
       String path = new String(charPath, 0, pathLength);
       path = "file:" + path.substring(5, pathLength);
       return path + File.separator;
     }
   }
   return dummyPath + File.separator;
 }
 //--------------------------------------------------------------
 //  USER INTERFACE
 //--------------------------------------------------------------
 /**
  * Builds the example AWT Frame menubar. Standard menus and their options
  * are added. Applications that subclass this class should build their
  * menubar additions within their initialize method.
  * 
  * @return a MenuBar for the AWT Frame
  */
 private MenuBar buildMenuBar() {
   // Build the menubar
   MenuBar menuBar = new MenuBar();
   // File menu
   Menu m = new Menu("File");
   m.addActionListener(this);
   m.add("Exit");
   menuBar.add(m);
   // View menu
   m = new Menu("View");
   m.addActionListener(this);
   m.add("Reset view");
   m.addSeparator();
   walkMenuItem = new CheckboxMenuItem("Walk");
   walkMenuItem.addItemListener(this);
   m.add(walkMenuItem);
   examineMenuItem = new CheckboxMenuItem("Examine");
   examineMenuItem.addItemListener(this);
   m.add(examineMenuItem);
   if (navigationType == Walk) {
     walkMenuItem.setState(true);
     examineMenuItem.setState(false);
   } else {
     walkMenuItem.setState(false);
     examineMenuItem.setState(true);
   }
   m.addSeparator();
   headlightMenuItem = new CheckboxMenuItem("Headlight on/off");
   headlightMenuItem.addItemListener(this);
   headlightMenuItem.setState(headlightOnOff);
   m.add(headlightMenuItem);
   menuBar.add(m);
   return menuBar;
 }
 /**
  * Shows the application"s frame, making it and its menubar, 3D canvas, and
  * 3D content visible.
  */
 public void showFrame() {
   exampleFrame.show();
 }
 /**
  * Quits the application.
  */
 public void quit() {
   System.exit(0);
 }
 /**
  * Handles menu selections.
  * 
  * @param event
  *            an ActionEvent indicating what menu action requires handling
  */
 public void actionPerformed(ActionEvent event) {
   String arg = event.getActionCommand();
   if (arg.equals("Reset view"))
     reset();
   else if (arg.equals("Exit"))
     quit();
 }
 /**
  * Handles checkbox items on a CheckboxMenu. The Example class has none of
  * its own, but subclasses may have some.
  * 
  * @param menu
  *            which CheckboxMenu needs action
  * @param check
  *            which CheckboxMenu item has changed
  */
 public void checkboxChanged(CheckboxMenu menu, int check) {
   // None for us
 }
 /**
  * Handles on/off checkbox items on a standard menu.
  * 
  * @param event
  *            an ItemEvent indicating what requires handling
  */
 public void itemStateChanged(ItemEvent event) {
   Object src = event.getSource();
   boolean state;
   if (src == headlightMenuItem) {
     state = headlightMenuItem.getState();
     headlight.setEnable(state);
   } else if (src == walkMenuItem)
     setNavigationType(Walk);
   else if (src == examineMenuItem)
     setNavigationType(Examine);
 }
 /**
  * Handles a window closing event notifying the application that the user
  * has chosen to close the application without selecting the "Exit" menu
  * item.
  * 
  * @param event
  *            a WindowEvent indicating the window is closing
  */
 public void windowClosing(WindowEvent event) {
   quit();
 }
 public void windowClosed(WindowEvent event) {
 }
 public void windowOpened(WindowEvent event) {
 }
 public void windowIconified(WindowEvent event) {
 }
 public void windowDeiconified(WindowEvent event) {
 }
 public void windowActivated(WindowEvent event) {
 }
 public void windowDeactivated(WindowEvent event) {
 }
 //  Well known colors, positions, and directions
 public final static Color3f White = new Color3f(1.0f, 1.0f, 1.0f);
 public final static Color3f Gray = new Color3f(0.7f, 0.7f, 0.7f);
 public final static Color3f DarkGray = new Color3f(0.2f, 0.2f, 0.2f);
 public final static Color3f Black = new Color3f(0.0f, 0.0f, 0.0f);
 public final static Color3f Red = new Color3f(1.0f, 0.0f, 0.0f);
 public final static Color3f DarkRed = new Color3f(0.3f, 0.0f, 0.0f);
 public final static Color3f Yellow = new Color3f(1.0f, 1.0f, 0.0f);
 public final static Color3f DarkYellow = new Color3f(0.3f, 0.3f, 0.0f);
 public final static Color3f Green = new Color3f(0.0f, 1.0f, 0.0f);
 public final static Color3f DarkGreen = new Color3f(0.0f, 0.3f, 0.0f);
 public final static Color3f Cyan = new Color3f(0.0f, 1.0f, 1.0f);
 public final static Color3f Blue = new Color3f(0.0f, 0.0f, 1.0f);
 public final static Color3f DarkBlue = new Color3f(0.0f, 0.0f, 0.3f);
 public final static Color3f Magenta = new Color3f(1.0f, 0.0f, 1.0f);
 public final static Vector3f PosX = new Vector3f(1.0f, 0.0f, 0.0f);
 public final static Vector3f NegX = new Vector3f(-1.0f, 0.0f, 0.0f);
 public final static Vector3f PosY = new Vector3f(0.0f, 1.0f, 0.0f);
 public final static Vector3f NegY = new Vector3f(0.0f, -1.0f, 0.0f);
 public final static Vector3f PosZ = new Vector3f(0.0f, 0.0f, 1.0f);
 public final static Vector3f NegZ = new Vector3f(0.0f, 0.0f, -1.0f);
 public final static Point3f Origin = new Point3f(0.0f, 0.0f, 0.0f);
 public final static Point3f PlusX = new Point3f(0.75f, 0.0f, 0.0f);
 public final static Point3f MinusX = new Point3f(-0.75f, 0.0f, 0.0f);
 public final static Point3f PlusY = new Point3f(0.0f, 0.75f, 0.0f);
 public final static Point3f MinusY = new Point3f(0.0f, -0.75f, 0.0f);
 public final static Point3f PlusZ = new Point3f(0.0f, 0.0f, 0.75f);
 public final static Point3f MinusZ = new Point3f(0.0f, 0.0f, -0.75f);

} // //INTERFACE //CheckboxMenuListener - listen for checkbox change events // //DESCRIPTION //The checkboxChanged method is called by users of this class //to notify the listener when a checkbox choice has changed on //a CheckboxMenu class menu. // interface CheckboxMenuListener extends EventListener {

 public void checkboxChanged(CheckboxMenu menu, int check);

} /**

* ExamineViewerBehavior
* 
* @version 1.0, 98/04/16
*/

/**

* Wakeup on mouse button presses, releases, and mouse movements and generate
* transforms in an "examination style" that enables the user to rotate,
* translation, and zoom an object as if it is held at arm"s length. Such an
* examination style is similar to the "Examine" navigation type used by VRML
* browsers.
* 
* The behavior maps mouse drags to different transforms depending upon the
* mosue button held down:
* 
* Button 1 (left) Horizontal movement --> Y-axis rotation Vertical movement -->
* X-axis rotation
* 
* Button 2 (middle) Horizontal movement --> nothing Vertical movement -->
* Z-axis translation
* 
* Button 3 (right) Horizontal movement --> X-axis translation Vertical movement
* --> Y-axis translation
* 
* To support systems with 2 or 1 mouse buttons, the following alternate
* mappings are supported while dragging with any mouse button held down and
* zero or more keyboard modifiers held down:
* 
* No modifiers = Button 1 ALT = Button 2 Meta = Button 3 Control = Button 3
* 
* The behavior automatically modifies a TransformGroup provided to the
* constructor. The TransformGroup"s transform can be set at any time by the
* application or other behaviors to cause the examine rotation and translation
* to be reset.
*/

// This class is inspired by the MouseBehavior, MouseRotate, // MouseTranslate, and MouseZoom utility behaviors provided with // Java 3D. This class differs from those utilities in that it: // // (a) encapsulates all three behaviors into one in order to // enforce a specific "Examine" symantic // // (b) supports set/get of the rotation and translation factors // that control the speed of movement. // // (c) supports the "Control" modifier as an alternative to the // "Meta" modifier not present on PC, Mac, and most non-Sun // keyboards. This makes button3 behavior usable on PCs, // Macs, and other systems with fewer than 3 mouse buttons. class ExamineViewerBehavior extends ViewerBehavior {

 // Previous cursor location
 protected int previousX = 0;
 protected int previousY = 0;
 // Saved standard cursor
 protected Cursor savedCursor = null;
 /**
  * Construct an examine behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into a
  * transform group given later with the setTransformGroup( ) method.
  */
 public ExamineViewerBehavior() {
   super();
 }
 /**
  * Construct an examine behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into a
  * transform group given later with the setTransformGroup( ) method.
  * 
  * @param parent
  *            The AWT Component that contains the area generating mouse
  *            events.
  */
 public ExamineViewerBehavior(Component parent) {
   super(parent);
 }
 /**
  * Construct an examine behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into the
  * given transform group.
  * 
  * @param transformGroup
  *            The transform group to be modified by the behavior.
  */
 public ExamineViewerBehavior(TransformGroup transformGroup) {
   super();
   subjectTransformGroup = transformGroup;
 }
 /**
  * Construct an examine behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into the
  * given transform group.
  * 
  * @param transformGroup
  *            The transform group to be modified by the behavior.
  * @param parent
  *            The AWT Component that contains the area generating mouse
  *            events.
  */
 public ExamineViewerBehavior(TransformGroup transformGroup, Component parent) {
   super(parent);
   subjectTransformGroup = transformGroup;
 }
 /**
  * Respond to a button1 event (press, release, or drag).
  * 
  * @param mouseEvent
  *            A MouseEvent to respond to.
  */
 public void onButton1(MouseEvent mev) {
   if (subjectTransformGroup == null)
     return;
   int x = mev.getX();
   int y = mev.getY();
   if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
     // Mouse button pressed: record position
     previousX = x;
     previousY = y;
     // Change to a "move" cursor
     if (parentComponent != null) {
       savedCursor = parentComponent.getCursor();
       parentComponent.setCursor(Cursor
           .getPredefinedCursor(Cursor.HAND_CURSOR));
     }
     return;
   }
   if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
     // Mouse button released: do nothing
     // Switch the cursor back
     if (parentComponent != null)
       parentComponent.setCursor(savedCursor);
     return;
   }
   //
   // Mouse moved while button down: create a rotation
   //
   // Compute the delta in X and Y from the previous
   // position. Use the delta to compute rotation
   // angles with the mapping:
   //
   //   positive X mouse delta --> positive Y-axis rotation
   //   positive Y mouse delta --> positive X-axis rotation
   //
   // where positive X mouse movement is to the right, and
   // positive Y mouse movement is **down** the screen.
   //
   int deltaX = x - previousX;
   int deltaY = y - previousY;
   if (deltaX > UNUSUAL_XDELTA || deltaX < -UNUSUAL_XDELTA
       || deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {
     // Deltas are too huge to be believable. Probably a glitch.
     // Don"t record the new XY location, or do anything.
     return;
   }
   double xRotationAngle = deltaY * XRotationFactor;
   double yRotationAngle = deltaX * YRotationFactor;
   //
   // Build transforms
   //
   transform1.rotX(xRotationAngle);
   transform2.rotY(yRotationAngle);
   // Get and save the current transform matrix
   subjectTransformGroup.getTransform(currentTransform);
   currentTransform.get(matrix);
   translate.set(matrix.m03, matrix.m13, matrix.m23);
   // Translate to the origin, rotate, then translate back
   currentTransform.setTranslation(origin);
   currentTransform.mul(transform1, currentTransform);
   currentTransform.mul(transform2, currentTransform);
   currentTransform.setTranslation(translate);
   // Update the transform group
   subjectTransformGroup.setTransform(currentTransform);
   previousX = x;
   previousY = y;
 }
 /**
  * Respond to a button2 event (press, release, or drag).
  * 
  * @param mouseEvent
  *            A MouseEvent to respond to.
  */
 public void onButton2(MouseEvent mev) {
   if (subjectTransformGroup == null)
     return;
   int x = mev.getX();
   int y = mev.getY();
   if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
     // Mouse button pressed: record position
     previousX = x;
     previousY = y;
     // Change to a "move" cursor
     if (parentComponent != null) {
       savedCursor = parentComponent.getCursor();
       parentComponent.setCursor(Cursor
           .getPredefinedCursor(Cursor.MOVE_CURSOR));
     }
     return;
   }
   if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
     // Mouse button released: do nothing
     // Switch the cursor back
     if (parentComponent != null)
       parentComponent.setCursor(savedCursor);
     return;
   }
   //
   // Mouse moved while button down: create a translation
   //
   // Compute the delta in Y from the previous
   // position. Use the delta to compute translation
   // distances with the mapping:
   //
   //   positive Y mouse delta --> positive Y-axis translation
   //
   // where positive X mouse movement is to the right, and
   // positive Y mouse movement is **down** the screen.
   //
   int deltaY = y - previousY;
   if (deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {
     // Deltas are too huge to be believable. Probably a glitch.
     // Don"t record the new XY location, or do anything.
     return;
   }
   double zTranslationDistance = deltaY * ZTranslationFactor;
   //
   // Build transforms
   //
   translate.set(0.0, 0.0, zTranslationDistance);
   transform1.set(translate);
   // Get and save the current transform
   subjectTransformGroup.getTransform(currentTransform);
   // Translate as needed
   currentTransform.mul(transform1, currentTransform);
   // Update the transform group
   subjectTransformGroup.setTransform(currentTransform);
   previousX = x;
   previousY = y;
 }
 /**
  * Respond to a button3 event (press, release, or drag).
  * 
  * @param mouseEvent
  *            A MouseEvent to respond to.
  */
 public void onButton3(MouseEvent mev) {
   if (subjectTransformGroup == null)
     return;
   int x = mev.getX();
   int y = mev.getY();
   if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
     // Mouse button pressed: record position
     previousX = x;
     previousY = y;
     // Change to a "move" cursor
     if (parentComponent != null) {
       savedCursor = parentComponent.getCursor();
       parentComponent.setCursor(Cursor
           .getPredefinedCursor(Cursor.MOVE_CURSOR));
     }
     return;
   }
   if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
     // Mouse button released: do nothing
     // Switch the cursor back
     if (parentComponent != null)
       parentComponent.setCursor(savedCursor);
     return;
   }
   //
   // Mouse moved while button down: create a translation
   //
   // Compute the delta in X and Y from the previous
   // position. Use the delta to compute translation
   // distances with the mapping:
   //
   //   positive X mouse delta --> positive X-axis translation
   //   positive Y mouse delta --> negative Y-axis translation
   //
   // where positive X mouse movement is to the right, and
   // positive Y mouse movement is **down** the screen.
   //
   int deltaX = x - previousX;
   int deltaY = y - previousY;
   if (deltaX > UNUSUAL_XDELTA || deltaX < -UNUSUAL_XDELTA
       || deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {
     // Deltas are too huge to be believable. Probably a glitch.
     // Don"t record the new XY location, or do anything.
     return;
   }
   double xTranslationDistance = deltaX * XTranslationFactor;
   double yTranslationDistance = -deltaY * YTranslationFactor;
   //
   // Build transforms
   //
   translate.set(xTranslationDistance, yTranslationDistance, 0.0);
   transform1.set(translate);
   // Get and save the current transform
   subjectTransformGroup.getTransform(currentTransform);
   // Translate as needed
   currentTransform.mul(transform1, currentTransform);
   // Update the transform group
   subjectTransformGroup.setTransform(currentTransform);
   previousX = x;
   previousY = y;
 }
 /**
  * Respond to an elapsed frames event (assuming subclass has set up a wakeup
  * criterion for it).
  * 
  * @param time
  *            A WakeupOnElapsedFrames criterion to respond to.
  */
 public void onElapsedFrames(WakeupOnElapsedFrames timeEvent) {
   // Can"t happen
 }

} /*

* 
* Copyright (c) 1998 David R. Nadeau
*  
*/

/**

* WalkViewerBehavior is a utility class that creates a "walking style"
* navigation symantic.
* 
* The behavior wakes up on mouse button presses, releases, and mouse movements
* and generates transforms in a "walk style" that enables the user to walk
* through a scene, translating and turning about as if they are within the
* scene. Such a walk style is similar to the "Walk" navigation type used by
* VRML browsers.
* <P>
* The behavior maps mouse drags to different transforms depending upon the
* mouse button held down:
*
*
Button 1 (left) *
Horizontal movement --> Y-axis rotation *
Vertical movement --> Z-axis translation * *
Button 2 (middle) *
Horizontal movement --> Y-axis rotation *
Vertical movement --> X-axis rotation * *
Button 3 (right) *
Horizontal movement --> X-axis translation *
Vertical movement --> Y-axis translation *
* 
* To support systems with 2 or 1 mouse buttons, the following alternate
* mappings are supported while dragging with any mouse button held down and
* zero or more keyboard modifiers held down:
*
    *
  • No modifiers = Button 1 *
  • ALT = Button 2 *
  • Meta = Button 3 *
  • Control = Button 3 *
* The behavior automatically modifies a TransformGroup provided to the
* constructor. The TransformGroup"s transform can be set at any time by the
* application or other behaviors to cause the walk rotation and translation to
* be reset.
* <P>
* While a mouse button is down, the behavior automatically changes the cursor
* in a given parent AWT Component. If no parent Component is given, no cursor
* changes are attempted.
* 
* @version 1.0, 98/04/16
* @author David R. Nadeau, San Diego Supercomputer Center
*/

class WalkViewerBehavior extends ViewerBehavior {

 // This class is inspired by the MouseBehavior, MouseRotate,
 // MouseTranslate, and MouseZoom utility behaviors provided with
 // Java 3D. This class differs from those utilities in that it:
 //
 //    (a) encapsulates all three behaviors into one in order to
 //        enforce a specific "Walk" symantic
 //
 //    (b) supports set/get of the rotation and translation factors
 //        that control the speed of movement.
 //
 //    (c) supports the "Control" modifier as an alternative to the
 //        "Meta" modifier not present on PC, Mac, and most non-Sun
 //        keyboards. This makes button3 behavior usable on PCs,
 //        Macs, and other systems with fewer than 3 mouse buttons.
 // Previous and initial cursor locations
 protected int previousX = 0;
 protected int previousY = 0;
 protected int initialX = 0;
 protected int initialY = 0;
 // Deadzone size (delta from initial XY for which no
 // translate or rotate action is taken
 protected static final int DELTAX_DEADZONE = 10;
 protected static final int DELTAY_DEADZONE = 10;
 // Keep a set of wakeup criterion for animation-generated
 // event types.
 protected WakeupCriterion[] mouseAndAnimationEvents = null;
 protected WakeupOr mouseAndAnimationCriterion = null;
 protected WakeupOr savedMouseCriterion = null;
 // Saved standard cursor
 protected Cursor savedCursor = null;
 /**
  * Default Rotation and translation scaling factors for animated movements
  * (Button 1 press).
  */
 public static final double DEFAULT_YROTATION_ANIMATION_FACTOR = 0.0002;
 public static final double DEFAULT_ZTRANSLATION_ANIMATION_FACTOR = 0.01;
 protected double YRotationAnimationFactor = DEFAULT_YROTATION_ANIMATION_FACTOR;
 protected double ZTranslationAnimationFactor = DEFAULT_ZTRANSLATION_ANIMATION_FACTOR;
 /**
  * Constructs a new walk behavior that converts mouse actions into rotations
  * and translations. Rotations and translations are written into a
  * TransformGroup that must be set using the setTransformGroup method. The
  * cursor will be changed during mouse actions if the parent frame is set
  * using the setParentComponent method.
  * 
  * @return a new WalkViewerBehavior that needs its TransformGroup and parent
  *         Component set
  */
 public WalkViewerBehavior() {
   super();
 }
 /**
  * Constructs a new walk behavior that converts mouse actions into rotations
  * and translations. Rotations and translations are written into a
  * TransformGroup that must be set using the setTransformGroup method. The
  * cursor will be changed within the given AWT parent Component during mouse
  * drags.
  * 
  * @param parent
  *            a parent AWT Component within which the cursor will change
  *            during mouse drags
  * 
  * @return a new WalkViewerBehavior that needs its TransformGroup and parent
  *         Component set
  */
 public WalkViewerBehavior(Component parent) {
   super(parent);
 }
 /**
  * Constructs a new walk behavior that converts mouse actions into rotations
  * and translations. Rotations and translations are written into the given
  * TransformGroup. The cursor will be changed during mouse actions if the
  * parent frame is set using the setParentComponent method.
  * 
  * @param transformGroup
  *            a TransformGroup whos transform is read and written by the
  *            behavior
  * 
  * @return a new WalkViewerBehavior that needs its TransformGroup and parent
  *         Component set
  */
 public WalkViewerBehavior(TransformGroup transformGroup) {
   super();
   subjectTransformGroup = transformGroup;
 }
 /**
  * Constructs a new walk behavior that converts mouse actions into rotations
  * and translations. Rotations and translations are written into the given
  * TransformGroup. The cursor will be changed within the given AWT parent
  * Component during mouse drags.
  * 
  * @param transformGroup
  *            a TransformGroup whos transform is read and written by the
  *            behavior
  * 
  * @param parent
  *            a parent AWT Component within which the cursor will change
  *            during mouse drags
  * 
  * @return a new WalkViewerBehavior that needs its TransformGroup and parent
  *         Component set
  */
 public WalkViewerBehavior(TransformGroup transformGroup, Component parent) {
   super(parent);
   subjectTransformGroup = transformGroup;
 }
 /**
  * Initializes the behavior.
  */
 public void initialize() {
   super.initialize();
   savedMouseCriterion = mouseCriterion; // from parent class
   mouseAndAnimationEvents = new WakeupCriterion[4];
   mouseAndAnimationEvents[0] = new WakeupOnAWTEvent(
       MouseEvent.MOUSE_DRAGGED);
   mouseAndAnimationEvents[1] = new WakeupOnAWTEvent(
       MouseEvent.MOUSE_PRESSED);
   mouseAndAnimationEvents[2] = new WakeupOnAWTEvent(
       MouseEvent.MOUSE_RELEASED);
   mouseAndAnimationEvents[3] = new WakeupOnElapsedFrames(0);
   mouseAndAnimationCriterion = new WakeupOr(mouseAndAnimationEvents);
   // Don"t use the above criterion until a button 1 down event
 }
 /**
  * Sets the Y rotation animation scaling factor for Y-axis rotations. This
  * scaling factor is used to control the speed of Y rotation when button 1
  * is pressed and dragged.
  * 
  * @param factor
  *            the double Y rotation scaling factor
  */
 public void setYRotationAnimationFactor(double factor) {
   YRotationAnimationFactor = factor;
 }
 /**
  * Gets the current Y animation rotation scaling factor for Y-axis
  * rotations.
  * 
  * @return the double Y rotation scaling factor
  */
 public double getYRotationAnimationFactor() {
   return YRotationAnimationFactor;
 }
 /**
  * Sets the Z animation translation scaling factor for Z-axis translations.
  * This scaling factor is used to control the speed of Z translation when
  * button 1 is pressed and dragged.
  * 
  * @param factor
  *            the double Z translation scaling factor
  */
 public void setZTranslationAnimationFactor(double factor) {
   ZTranslationAnimationFactor = factor;
 }
 /**
  * Gets the current Z animation translation scaling factor for Z-axis
  * translations.
  * 
  * @return the double Z translation scaling factor
  */
 public double getZTranslationAnimationFactor() {
   return ZTranslationAnimationFactor;
 }
 /**
  * Responds to an elapsed frames event. Such an event is generated on every
  * frame while button 1 is held down. On each call, this method computes new
  * Y-axis rotation and Z-axis translation values and writes them to the
  * behavior"s TransformGroup. The translation and rotation amounts are
  * computed based upon the distance between the current cursor location and
  * the cursor location when button 1 was pressed. As this distance
  * increases, the translation or rotation amount increases.
  * 
  * @param time
  *            the WakeupOnElapsedFrames criterion to respond to
  */
 public void onElapsedFrames(WakeupOnElapsedFrames timeEvent) {
   //
   // Time elapsed while button down: create a rotation and
   // a translation.
   //
   // Compute the delta in X and Y from the initial position to
   // the previous position. Multiply the delta times a scaling
   // factor to compute an offset to add to the current translation
   // and rotation. Use the mapping:
   //
   //   positive X mouse delta --> negative Y-axis rotation
   //   positive Y mouse delta --> positive Z-axis translation
   //
   // where positive X mouse movement is to the right, and
   // positive Y mouse movement is **down** the screen.
   //
   if (buttonPressed != BUTTON1)
     return;
   int deltaX = previousX - initialX;
   int deltaY = previousY - initialY;
   double yRotationAngle = -deltaX * YRotationAnimationFactor;
   double zTranslationDistance = deltaY * ZTranslationAnimationFactor;
   //
   // Build transforms
   //
   transform1.rotY(yRotationAngle);
   translate.set(0.0, 0.0, zTranslationDistance);
   // Get and save the current transform matrix
   subjectTransformGroup.getTransform(currentTransform);
   currentTransform.get(matrix);
   // Translate to the origin, rotate, then translate back
   currentTransform.setTranslation(origin);
   currentTransform.mul(transform1, currentTransform);
   // Translate back from the origin by the original translation
   // distance, plus the new walk translation... but force walk
   // to travel on a plane by ignoring the Y component of a
   // transformed translation vector.
   currentTransform.transform(translate);
   translate.x += matrix.m03; // add in existing X translation
   translate.y = matrix.m13; // use Y translation
   translate.z += matrix.m23; // add in existing Z translation
   currentTransform.setTranslation(translate);
   // Update the transform group
   subjectTransformGroup.setTransform(currentTransform);
 }
 /**
  * Responds to a button1 event (press, release, or drag). On a press, the
  * method adds a wakeup criterion to the behavior"s set, callling for the
  * behavior to be awoken on each frame. On a button prelease, this criterion
  * is removed from the set.
  * 
  * @param mouseEvent
  *            the MouseEvent to respond to
  */
 public void onButton1(MouseEvent mev) {
   if (subjectTransformGroup == null)
     return;
   int x = mev.getX();
   int y = mev.getY();
   if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
     // Mouse button pressed: record position and change
     // the wakeup criterion to include elapsed time wakeups
     // so we can animate.
     previousX = x;
     previousY = y;
     initialX = x;
     initialY = y;
     // Swap criterion... parent class will not reschedule us
     mouseCriterion = mouseAndAnimationCriterion;
     // Change to a "move" cursor
     if (parentComponent != null) {
       savedCursor = parentComponent.getCursor();
       parentComponent.setCursor(Cursor
           .getPredefinedCursor(Cursor.HAND_CURSOR));
     }
     return;
   }
   if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
     // Mouse button released: restore original wakeup
     // criterion which only includes mouse activity, not
     // elapsed time
     mouseCriterion = savedMouseCriterion;
     // Switch the cursor back
     if (parentComponent != null)
       parentComponent.setCursor(savedCursor);
     return;
   }
   previousX = x;
   previousY = y;
 }
 /**
  * Responds to a button2 event (press, release, or drag). On a press, the
  * method records the initial cursor location. On a drag, the difference
  * between the current and previous cursor location provides a delta that
  * controls the amount by which to rotate in X and Y.
  * 
  * @param mouseEvent
  *            the MouseEvent to respond to
  */
 public void onButton2(MouseEvent mev) {
   if (subjectTransformGroup == null)
     return;
   int x = mev.getX();
   int y = mev.getY();
   if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
     // Mouse button pressed: record position
     previousX = x;
     previousY = y;
     initialX = x;
     initialY = y;
     // Change to a "rotate" cursor
     if (parentComponent != null) {
       savedCursor = parentComponent.getCursor();
       parentComponent.setCursor(Cursor
           .getPredefinedCursor(Cursor.MOVE_CURSOR));
     }
     return;
   }
   if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
     // Mouse button released: do nothing
     // Switch the cursor back
     if (parentComponent != null)
       parentComponent.setCursor(savedCursor);
     return;
   }
   //
   // Mouse moved while button down: create a rotation
   //
   // Compute the delta in X and Y from the previous
   // position. Use the delta to compute rotation
   // angles with the mapping:
   //
   //   positive X mouse delta --> negative Y-axis rotation
   //   positive Y mouse delta --> negative X-axis rotation
   //
   // where positive X mouse movement is to the right, and
   // positive Y mouse movement is **down** the screen.
   //
   int deltaX = x - previousX;
   int deltaY = 0;
   if (Math.abs(y - initialY) > DELTAY_DEADZONE) {
     // Cursor has moved far enough vertically to consider
     // it intentional, so get it"s delta.
     deltaY = y - previousY;
   }
   if (deltaX > UNUSUAL_XDELTA || deltaX < -UNUSUAL_XDELTA
       || deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {
     // Deltas are too huge to be believable. Probably a glitch.
     // Don"t record the new XY location, or do anything.
     return;
   }
   double xRotationAngle = -deltaY * XRotationFactor;
   double yRotationAngle = -deltaX * YRotationFactor;
   //
   // Build transforms
   //
   transform1.rotX(xRotationAngle);
   transform2.rotY(yRotationAngle);
   // Get and save the current transform matrix
   subjectTransformGroup.getTransform(currentTransform);
   currentTransform.get(matrix);
   translate.set(matrix.m03, matrix.m13, matrix.m23);
   // Translate to the origin, rotate, then translate back
   currentTransform.setTranslation(origin);
   currentTransform.mul(transform2, currentTransform);
   currentTransform.mul(transform1);
   currentTransform.setTranslation(translate);
   // Update the transform group
   subjectTransformGroup.setTransform(currentTransform);
   previousX = x;
   previousY = y;
 }
 /**
  * Responds to a button3 event (press, release, or drag). On a drag, the
  * difference between the current and previous cursor location provides a
  * delta that controls the amount by which to translate in X and Y.
  * 
  * @param mouseEvent
  *            the MouseEvent to respond to
  */
 public void onButton3(MouseEvent mev) {
   if (subjectTransformGroup == null)
     return;
   int x = mev.getX();
   int y = mev.getY();
   if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
     // Mouse button pressed: record position
     previousX = x;
     previousY = y;
     // Change to a "move" cursor
     if (parentComponent != null) {
       savedCursor = parentComponent.getCursor();
       parentComponent.setCursor(Cursor
           .getPredefinedCursor(Cursor.MOVE_CURSOR));
     }
     return;
   }
   if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
     // Mouse button released: do nothing
     // Switch the cursor back
     if (parentComponent != null)
       parentComponent.setCursor(savedCursor);
     return;
   }
   //
   // Mouse moved while button down: create a translation
   //
   // Compute the delta in X and Y from the previous
   // position. Use the delta to compute translation
   // distances with the mapping:
   //
   //   positive X mouse delta --> positive X-axis translation
   //   positive Y mouse delta --> negative Y-axis translation
   //
   // where positive X mouse movement is to the right, and
   // positive Y mouse movement is **down** the screen.
   //
   int deltaX = x - previousX;
   int deltaY = y - previousY;
   if (deltaX > UNUSUAL_XDELTA || deltaX < -UNUSUAL_XDELTA
       || deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {
     // Deltas are too huge to be believable. Probably a glitch.
     // Don"t record the new XY location, or do anything.
     return;
   }
   double xTranslationDistance = deltaX * XTranslationFactor;
   double yTranslationDistance = -deltaY * YTranslationFactor;
   //
   // Build transforms
   //
   translate.set(xTranslationDistance, yTranslationDistance, 0.0);
   transform1.set(translate);
   // Get and save the current transform
   subjectTransformGroup.getTransform(currentTransform);
   // Translate as needed
   currentTransform.mul(transform1);
   // Update the transform group
   subjectTransformGroup.setTransform(currentTransform);
   previousX = x;
   previousY = y;
 }

} // //CLASS //CheckboxMenu - build a menu of grouped checkboxes // //DESCRIPTION //The class creates a menu with one or more CheckboxMenuItem"s //and monitors that menu. When a menu checkbox is picked, the //previous one is turned off (in radio-button style). Then, //a given listener"s checkboxChanged method is called, passing it //the menu and the item checked. // class CheckboxMenu extends Menu implements ItemListener {

 // State
 protected CheckboxMenuItem[] checks = null;
 protected int current = 0;
 protected CheckboxMenuListener listener = null;
 //  Construct
 public CheckboxMenu(String name, NameValue[] items,
     CheckboxMenuListener listen) {
   this(name, items, 0, listen);
 }
 public CheckboxMenu(String name, NameValue[] items, int cur,
     CheckboxMenuListener listen) {
   super(name);
   current = cur;
   listener = listen;
   if (items == null)
     return;
   checks = new CheckboxMenuItem[items.length];
   for (int i = 0; i < items.length; i++) {
     checks[i] = new CheckboxMenuItem(items[i].name, false);
     checks[i].addItemListener(this);
     add(checks[i]);
   }
   checks[cur].setState(true);
 }
 //  Handle checkbox changed events
 public void itemStateChanged(ItemEvent event) {
   Object src = event.getSource();
   for (int i = 0; i < checks.length; i++) {
     if (src == checks[i]) {
       // Update the checkboxes
       checks[current].setState(false);
       current = i;
       checks[current].setState(true);
       if (listener != null)
         listener.checkboxChanged(this, i);
       return;
     }
   }
 }
 // Methods to get and set state
 public int getCurrent() {
   return current;
 }
 public void setCurrent(int cur) {
   if (cur < 0 || cur >= checks.length)
     return; // ignore out of range choices
   if (checks == null)
     return;
   checks[current].setState(false);
   current = cur;
   checks[current].setState(true);
 }
 public CheckboxMenuItem getSelectedCheckbox() {
   if (checks == null)
     return null;
   return checks[current];
 }
 public void setSelectedCheckbox(CheckboxMenuItem item) {
   if (checks == null)
     return;
   for (int i = 0; i < checks.length; i++) {
     if (item == checks[i]) {
       checks[i].setState(false);
       current = i;
       checks[i].setState(true);
     }
   }
 }

} /**

* ViewerBehavior
* 
* @version 1.0, 98/04/16
*/

/**

* Wakeup on mouse button presses, releases, and mouse movements and generate
* transforms for a transform group. Classes that extend this class impose
* specific symantics, such as "Examine" or "Walk" viewing, similar to the
* navigation types used by VRML browsers.
* 
* To support systems with 2 or 1 mouse buttons, the following alternate
* mappings are supported while dragging with any mouse button held down and
* zero or more keyboard modifiers held down:
* 
* No modifiers = Button 1 ALT = Button 2 Meta = Button 3 Control = Button 3
* 
* The behavior automatically modifies a TransformGroup provided to the
* constructor. The TransformGroup"s transform can be set at any time by the
* application or other behaviors to cause the viewer"s rotation and translation
* to be reset.
*/

// This class is inspired by the MouseBehavior, MouseRotate, // MouseTranslate, and MouseZoom utility behaviors provided with // Java 3D. This class differs from those utilities in that it: // // (a) encapsulates all three behaviors into one in order to // enforce a specific viewing symantic // // (b) supports set/get of the rotation and translation factors // that control the speed of movement. // // (c) supports the "Control" modifier as an alternative to the // "Meta" modifier not present on PC, Mac, and most non-Sun // keyboards. This makes button3 behavior usable on PCs, // Macs, and other systems with fewer than 3 mouse buttons. abstract class ViewerBehavior extends Behavior {

 // Keep track of the transform group who"s transform we modify
 // during mouse motion.
 protected TransformGroup subjectTransformGroup = null;
 // Keep a set of wakeup criterion for different mouse-generated
 // event types.
 protected WakeupCriterion[] mouseEvents = null;
 protected WakeupOr mouseCriterion = null;
 // Track which button was last pressed
 protected static final int BUTTONNONE = -1;
 protected static final int BUTTON1 = 0;
 protected static final int BUTTON2 = 1;
 protected static final int BUTTON3 = 2;
 protected int buttonPressed = BUTTONNONE;
 // Keep a few Transform3Ds for use during event processing. This
 // avoids having to allocate new ones on each event.
 protected Transform3D currentTransform = new Transform3D();
 protected Transform3D transform1 = new Transform3D();
 protected Transform3D transform2 = new Transform3D();
 protected Matrix4d matrix = new Matrix4d();
 protected Vector3d origin = new Vector3d(0.0, 0.0, 0.0);
 protected Vector3d translate = new Vector3d(0.0, 0.0, 0.0);
 // Unusual X and Y delta limits.
 protected static final int UNUSUAL_XDELTA = 400;
 protected static final int UNUSUAL_YDELTA = 400;
 protected Component parentComponent = null;
 /**
  * Construct a viewer behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into a
  * transform group given later with the setTransformGroup( ) method.
  */
 public ViewerBehavior() {
   super();
 }
 /**
  * Construct a viewer behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into a
  * transform group given later with the setTransformGroup( ) method.
  * 
  * @param parent
  *            The AWT Component that contains the area generating mouse
  *            events.
  */
 public ViewerBehavior(Component parent) {
   super();
   parentComponent = parent;
 }
 /**
  * Construct a viewer behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into the
  * given transform group.
  * 
  * @param transformGroup
  *            The transform group to be modified by the behavior.
  */
 public ViewerBehavior(TransformGroup transformGroup) {
   super();
   subjectTransformGroup = transformGroup;
 }
 /**
  * Construct a viewer behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into the
  * given transform group.
  * 
  * @param transformGroup
  *            The transform group to be modified by the behavior.
  * @param parent
  *            The AWT Component that contains the area generating mouse
  *            events.
  */
 public ViewerBehavior(TransformGroup transformGroup, Component parent) {
   super();
   subjectTransformGroup = transformGroup;
   parentComponent = parent;
 }
 /**
  * Set the transform group modified by the viewer behavior. Setting the
  * transform group to null disables the behavior until the transform group
  * is again set to an existing group.
  * 
  * @param transformGroup
  *            The new transform group to be modified by the behavior.
  */
 public void setTransformGroup(TransformGroup transformGroup) {
   subjectTransformGroup = transformGroup;
 }
 /**
  * Get the transform group modified by the viewer behavior.
  */
 public TransformGroup getTransformGroup() {
   return subjectTransformGroup;
 }
 /**
  * Sets the parent component who"s cursor will be changed during mouse
  * drags. If no component is given is given to the constructor, or set via
  * this method, no cursor changes will be done.
  * 
  * @param parent
  *            the AWT Component, such as a Frame, within which cursor
  *            changes should take place during mouse drags
  */
 public void setParentComponent(Component parent) {
   parentComponent = parent;
 }
 /*
  * Gets the parent frame within which the cursor changes during mouse drags.
  * 
  * @return the AWT Component, such as a Frame, within which cursor changes
  * should take place during mouse drags. Returns null if no parent is set.
  */
 public Component getParentComponent() {
   return parentComponent;
 }
 /**
  * Initialize the behavior.
  */
 public void initialize() {
   // Wakeup when the mouse is dragged or when a mouse button
   // is pressed or released.
   mouseEvents = new WakeupCriterion[3];
   mouseEvents[0] = new WakeupOnAWTEvent(MouseEvent.MOUSE_DRAGGED);
   mouseEvents[1] = new WakeupOnAWTEvent(MouseEvent.MOUSE_PRESSED);
   mouseEvents[2] = new WakeupOnAWTEvent(MouseEvent.MOUSE_RELEASED);
   mouseCriterion = new WakeupOr(mouseEvents);
   wakeupOn(mouseCriterion);
 }
 /**
  * Process a new wakeup. Interpret mouse button presses, releases, and mouse
  * drags.
  * 
  * @param criteria
  *            The wakeup criteria causing the behavior wakeup.
  */
 public void processStimulus(Enumeration criteria) {
   WakeupCriterion wakeup = null;
   AWTEvent[] event = null;
   int whichButton = BUTTONNONE;
   // Process all pending wakeups
   while (criteria.hasMoreElements()) {
     wakeup = (WakeupCriterion) criteria.nextElement();
     if (wakeup instanceof WakeupOnAWTEvent) {
       event = ((WakeupOnAWTEvent) wakeup).getAWTEvent();
       // Process all pending events
       for (int i = 0; i < event.length; i++) {
         if (event[i].getID() != MouseEvent.MOUSE_PRESSED
             && event[i].getID() != MouseEvent.MOUSE_RELEASED
             && event[i].getID() != MouseEvent.MOUSE_DRAGGED)
           // Ignore uninteresting mouse events
           continue;
         //
         // Regretably, Java event handling (or perhaps
         // underlying OS event handling) doesn"t always
         // catch button bounces (redundant presses and
         // releases), or order events so that the last
         // drag event is delivered before a release.
         // This means we can get stray events that we
         // filter out here.
         //
         if (event[i].getID() == MouseEvent.MOUSE_PRESSED
             && buttonPressed != BUTTONNONE)
           // Ignore additional button presses until a release
           continue;
         if (event[i].getID() == MouseEvent.MOUSE_RELEASED
             && buttonPressed == BUTTONNONE)
           // Ignore additional button releases until a press
           continue;
         if (event[i].getID() == MouseEvent.MOUSE_DRAGGED
             && buttonPressed == BUTTONNONE)
           // Ignore drags until a press
           continue;
         MouseEvent mev = (MouseEvent) event[i];
         int modifiers = mev.getModifiers();
         //
         // Unfortunately, the underlying event handling
         // doesn"t do a "grab" operation when a mouse button
         // is pressed. This means that once a button is
         // pressed, if another mouse button or a keyboard
         // modifier key is pressed, the delivered mouse event
         // will show that a different button is being held
         // down. For instance:
         //
         // Action Event
         //  Button 1 press Button 1 press
         //  Drag with button 1 down Button 1 drag
         //  ALT press -
         //  Drag with ALT & button 1 down Button 2 drag
         //  Button 1 release Button 2 release
         //
         // The upshot is that we can get a button press
         // without a matching release, and the button
         // associated with a drag can change mid-drag.
         //
         // To fix this, we watch for an initial button
         // press, and thenceforth consider that button
         // to be the one held down, even if additional
         // buttons get pressed, and despite what is
         // reported in the event. Only when a button is
         // released, do we end such a grab.
         //
         if (buttonPressed == BUTTONNONE) {
           // No button is pressed yet, figure out which
           // button is down now and how to direct events
           if (((modifiers & InputEvent.BUTTON3_MASK) != 0)
               || (((modifiers & InputEvent.BUTTON1_MASK) != 0) && ((modifiers & InputEvent.CTRL_MASK) == InputEvent.CTRL_MASK))) {
             // Button 3 activity (META or CTRL down)
             whichButton = BUTTON3;
           } else if ((modifiers & InputEvent.BUTTON2_MASK) != 0) {
             // Button 2 activity (ALT down)
             whichButton = BUTTON2;
           } else {
             // Button 1 activity (no modifiers down)
             whichButton = BUTTON1;
           }
           // If the event is to press a button, then
           // record that that button is now down
           if (event[i].getID() == MouseEvent.MOUSE_PRESSED)
             buttonPressed = whichButton;
         } else {
           // Otherwise a button was pressed earlier and
           // hasn"t been released yet. Assign all further
           // events to it, even if ALT, META, CTRL, or
           // another button has been pressed as well.
           whichButton = buttonPressed;
         }
         // Distribute the event
         switch (whichButton) {
         case BUTTON1:
           onButton1(mev);
           break;
         case BUTTON2:
           onButton2(mev);
           break;
         case BUTTON3:
           onButton3(mev);
           break;
         default:
           break;
         }
         // If the event is to release a button, then
         // record that that button is now up
         if (event[i].getID() == MouseEvent.MOUSE_RELEASED)
           buttonPressed = BUTTONNONE;
       }
       continue;
     }
     if (wakeup instanceof WakeupOnElapsedFrames) {
       onElapsedFrames((WakeupOnElapsedFrames) wakeup);
       continue;
     }
   }
   // Reschedule us for another wakeup
   wakeupOn(mouseCriterion);
 }
 /**
  * Default X and Y rotation factors, and XYZ translation factors.
  */
 public static final double DEFAULT_XROTATION_FACTOR = 0.02;
 public static final double DEFAULT_YROTATION_FACTOR = 0.005;
 public static final double DEFAULT_XTRANSLATION_FACTOR = 0.02;
 public static final double DEFAULT_YTRANSLATION_FACTOR = 0.02;
 public static final double DEFAULT_ZTRANSLATION_FACTOR = 0.04;
 protected double XRotationFactor = DEFAULT_XROTATION_FACTOR;
 protected double YRotationFactor = DEFAULT_YROTATION_FACTOR;
 protected double XTranslationFactor = DEFAULT_XTRANSLATION_FACTOR;
 protected double YTranslationFactor = DEFAULT_YTRANSLATION_FACTOR;
 protected double ZTranslationFactor = DEFAULT_ZTRANSLATION_FACTOR;
 /**
  * Set the X rotation scaling factor for X-axis rotations.
  * 
  * @param factor
  *            The new scaling factor.
  */
 public void setXRotationFactor(double factor) {
   XRotationFactor = factor;
 }
 /**
  * Get the current X rotation scaling factor for X-axis rotations.
  */
 public double getXRotationFactor() {
   return XRotationFactor;
 }
 /**
  * Set the Y rotation scaling factor for Y-axis rotations.
  * 
  * @param factor
  *            The new scaling factor.
  */
 public void setYRotationFactor(double factor) {
   YRotationFactor = factor;
 }
 /**
  * Get the current Y rotation scaling factor for Y-axis rotations.
  */
 public double getYRotationFactor() {
   return YRotationFactor;
 }
 /**
  * Set the X translation scaling factor for X-axis translations.
  * 
  * @param factor
  *            The new scaling factor.
  */
 public void setXTranslationFactor(double factor) {
   XTranslationFactor = factor;
 }
 /**
  * Get the current X translation scaling factor for X-axis translations.
  */
 public double getXTranslationFactor() {
   return XTranslationFactor;
 }
 /**
  * Set the Y translation scaling factor for Y-axis translations.
  * 
  * @param factor
  *            The new scaling factor.
  */
 public void setYTranslationFactor(double factor) {
   YTranslationFactor = factor;
 }
 /**
  * Get the current Y translation scaling factor for Y-axis translations.
  */
 public double getYTranslationFactor() {
   return YTranslationFactor;
 }
 /**
  * Set the Z translation scaling factor for Z-axis translations.
  * 
  * @param factor
  *            The new scaling factor.
  */
 public void setZTranslationFactor(double factor) {
   ZTranslationFactor = factor;
 }
 /**
  * Get the current Z translation scaling factor for Z-axis translations.
  */
 public double getZTranslationFactor() {
   return ZTranslationFactor;
 }
 /**
  * Respond to a button1 event (press, release, or drag).
  * 
  * @param mouseEvent
  *            A MouseEvent to respond to.
  */
 public abstract void onButton1(MouseEvent mouseEvent);
 /**
  * Respond to a button2 event (press, release, or drag).
  * 
  * @param mouseEvent
  *            A MouseEvent to respond to.
  */
 public abstract void onButton2(MouseEvent mouseEvent);
 /**
  * Responed to a button3 event (press, release, or drag).
  * 
  * @param mouseEvent
  *            A MouseEvent to respond to.
  */
 public abstract void onButton3(MouseEvent mouseEvent);
 /**
  * Respond to an elapsed frames event (assuming subclass has set up a wakeup
  * criterion for it).
  * 
  * @param time
  *            A WakeupOnElapsedFrames criterion to respond to.
  */
 public abstract void onElapsedFrames(WakeupOnElapsedFrames timeEvent);

} // //CLASS //NameValue - create a handy name-value pair // //DESCRIPTION //It is frequently handy to have one or more name-value pairs //with which to store named colors, named positions, named textures, //and so forth. Several of the examples use this class. // //AUTHOR //David R. Nadeau / San Diego Supercomputer Center // class NameValue {

 public String name;
 public Object value;
 public NameValue(String n, Object v) {
   name = n;
   value = v;
 }

}


      </source>
   
  
 
  



Illustrate use of light influencing bounds, and bounding leaves

   <source lang="java">

// //CLASS //ExLightBounds - illustrate use of light influencing bounds, and // bounding leaves // //LESSON //Add a DirectionalLight node to illuminate a scene, then adjust //its influencing bounds // //SEE ALSO //ExAmbientLight //ExPointLight //ExSpotLight //ExLightScope // //AUTHOR //David R. Nadeau / San Diego Supercomputer Center // import java.applet.Applet; import java.awt.AWTEvent; import java.awt.BorderLayout; import java.awt.CheckboxMenuItem; import java.awt.ruponent; import java.awt.Cursor; import java.awt.Frame; import java.awt.Menu; import java.awt.MenuBar; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.InputEvent; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.event.MouseEvent; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.io.File; import java.util.Enumeration; import java.util.EventListener; import javax.media.j3d.AmbientLight; import javax.media.j3d.Appearance; import javax.media.j3d.Behavior; import javax.media.j3d.BoundingLeaf; import javax.media.j3d.BoundingSphere; import javax.media.j3d.Bounds; import javax.media.j3d.BranchGroup; import javax.media.j3d.Canvas3D; import javax.media.j3d.DirectionalLight; import javax.media.j3d.Group; import javax.media.j3d.Light; import javax.media.j3d.Material; import javax.media.j3d.Transform3D; import javax.media.j3d.TransformGroup; import javax.media.j3d.WakeupCriterion; import javax.media.j3d.WakeupOnAWTEvent; import javax.media.j3d.WakeupOnElapsedFrames; import javax.media.j3d.WakeupOr; import javax.vecmath.Color3f; import javax.vecmath.Matrix4d; import javax.vecmath.Point3d; import javax.vecmath.Point3f; import javax.vecmath.Vector3d; import javax.vecmath.Vector3f; import com.sun.j3d.utils.geometry.Primitive; import com.sun.j3d.utils.geometry.Sphere; import com.sun.j3d.utils.universe.PlatformGeometry; import com.sun.j3d.utils.universe.SimpleUniverse; import com.sun.j3d.utils.universe.Viewer; import com.sun.j3d.utils.universe.ViewingPlatform; public class ExLightBounds extends Java3DFrame {

 //--------------------------------------------------------------
 //  SCENE CONTENT
 //--------------------------------------------------------------
 //
 //  Nodes (updated via menu)
 //
 private DirectionalLight light = null;
 private Bounds worldBounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0), // Center
     1000.0); // Extent
 private Bounds smallBounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0), // Center
     1.0); // Extent
 private Bounds tinyBounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0), // Center
     0.2); // Extent
 private BoundingLeaf leafBounds = null;
 private TransformGroup leafTransformGroup = null;
 //
 //  Build scene
 //
 public Group buildScene() {
   // Get the current bounding leaf position
   Point3f pos = (Point3f) positions[currentPosition].value;
   // Turn off the example headlight
   setHeadlightEnable(false);
   // Build the scene group
   Group scene = new Group();
   // BEGIN EXAMPLE TOPIC
   // Create a bounding leaf we"ll use or not use depending
   // upon menu selections. Put it within a transform group
   // so that we can move the leaf about.
   leafTransformGroup = new TransformGroup();
   leafTransformGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
   Transform3D tr = new Transform3D();
   tr.setTranslation(new Vector3f(pos));
   leafTransformGroup.setTransform(tr);
   leafBounds = new BoundingLeaf(worldBounds);
   leafBounds.setCapability(BoundingLeaf.ALLOW_REGION_WRITE);
   leafTransformGroup.addChild(leafBounds);
   scene.addChild(leafTransformGroup);
   // Add a directional light whose bounds we"ll modify
   // Set its color and aim direction
   light = new DirectionalLight();
   light.setEnable(true);
   light.setColor(White);
   light.setDirection(new Vector3f(1.0f, 0.0f, -1.0f));
   light.setCapability(DirectionalLight.ALLOW_INFLUENCING_BOUNDS_WRITE);
   // Set the bounds to be either from the leaf or from
   // explicit bounds, depending upon the menu initial state
   if (boundingLeafOnOff)
     // Use bounding leaf
     light.setInfluencingBoundingLeaf(leafBounds);
   else
     // Use bounds on the light
     light.setInfluencingBounds(worldBounds);
   // Set the scope list to include nothing initially.
   // This defaults to "universal scope" which covers
   // everything.
   scene.addChild(light);
   // Add an ambient light to dimly illuminate the rest of
   // the shapes in the scene to help illustrate that the
   // directional light is being bounded... otherwise it looks
   // like we"re just removing shapes from the scene
   AmbientLight ambient = new AmbientLight();
   ambient.setEnable(true);
   ambient.setColor(White);
   ambient.setInfluencingBounds(worldBounds);
   scene.addChild(ambient);
   // END EXAMPLE TOPIC
   // Build foreground geometry
   scene.addChild(new SphereGroup());
   return scene;
 }
 //--------------------------------------------------------------
 //  USER INTERFACE
 //--------------------------------------------------------------
 //
 //  Main
 //
 public static void main(String[] args) {
   ExLightBounds ex = new ExLightBounds();
   ex.initialize(args);
   ex.buildUniverse();
   ex.showFrame();
 }
 //  Bounds mode On/off choices
 private boolean boundingLeafOnOff = true;
 private CheckboxMenuItem boundingLeafOnOffMenu = null;
 //  Bounds menu choices
 private NameValue[] bounds = { new NameValue("Tiny bounds", tinyBounds),
     new NameValue("Small bounds", smallBounds),
     new NameValue("Big bounds", worldBounds), };
 private int currentBounds = 2;
 private CheckboxMenu boundsMenu = null;
 //  Position menu choices
 private NameValue[] positions = { new NameValue("Origin", Origin),
     new NameValue("+X", PlusX), new NameValue("-X", MinusX),
     new NameValue("+Y", PlusY), new NameValue("-Y", MinusY),
     new NameValue("+Z", PlusZ), new NameValue("-Z", MinusZ), };
 private int currentPosition = 0;
 private CheckboxMenu positionMenu = null;
 //
 //  Initialize the GUI (application and applet)
 //
 public void initialize(String[] args) {
   // Initialize the window, menubar, etc.
   super.initialize(args);
   exampleFrame.setTitle("Java 3D Light Bounds Example");
   //
   //  Add a menubar menu to change node parameters
   //    Use bounding leaf
   //    Bounds size -->
   //    Bounding leaf position -->
   //
   Menu m = new Menu("DirectionalLight");
   boundingLeafOnOffMenu = new CheckboxMenuItem("Use bounding leaf",
       boundingLeafOnOff);
   boundingLeafOnOffMenu.addItemListener(this);
   m.add(boundingLeafOnOffMenu);
   boundsMenu = new CheckboxMenu("Bounds size", bounds, currentBounds,
       this);
   m.add(boundsMenu);
   positionMenu = new CheckboxMenu("Bounding leaf position", positions,
       currentPosition, this);
   if (boundingLeafOnOff)
     // Bounding leaf on
     positionMenu.setEnabled(true);
   else
     // Bounding leaf off
     positionMenu.setEnabled(false);
   m.add(positionMenu);
   exampleMenuBar.add(m);
 }
 //
 //  Handle checkboxes and menu choices
 //
 public void checkboxChanged(CheckboxMenu menu, int check) {
   if (menu == boundsMenu) {
     // Change the light bounds
     currentBounds = check;
     Bounds bou = (Bounds) bounds[check].value;
     if (boundingLeafOnOff) {
       // Change the bounding leaf"s bounds
       leafBounds.setRegion(bou);
       // Kick the light to get it to update
       // its bounds now that the leaf has
       // changed... (only necessary in the
       // Alpha release of Java3D)
       light.setInfluencingBoundingLeaf(leafBounds);
     } else {
       // Change the light"s own bounds
       light.setInfluencingBounds(bou);
     }
     return;
   }
   if (menu == positionMenu) {
     // Change the bounding leaf position
     currentPosition = check;
     Point3f pos = (Point3f) positions[check].value;
     Transform3D tr = new Transform3D();
     tr.setTranslation(new Vector3f(pos));
     leafTransformGroup.setTransform(tr);
     // Kick the light to get it to update
     // its bounds now that the leaf has
     // changed... (only necessary in the
     // Alpha release of Java3D)
     light.setInfluencingBoundingLeaf(leafBounds);
     return;
   }
   // Handle all other checkboxes
   super.checkboxChanged(menu, check);
 }
 public void itemStateChanged(ItemEvent event) {
   Object src = event.getSource();
   if (src == boundingLeafOnOffMenu) {
     boundingLeafOnOff = boundingLeafOnOffMenu.getState();
     if (boundingLeafOnOff) {
       // Use the bounding leaf
       light.setInfluencingBoundingLeaf(leafBounds);
       // A bounding leaf overrides bounds,
       // but for neatness we can turn them off
       // (doesn"t work in Alpha release of Java3D)
       light.setInfluencingBounds(null);
       positionMenu.setEnabled(true);
     } else {
       // Use bounds on the light itself
       Bounds bou = (Bounds) bounds[currentBounds].value;
       light.setInfluencingBoundingLeaf(null);
       light.setInfluencingBounds(bou);
       positionMenu.setEnabled(false);
     }
     return;
   }
   // Handle all other checkboxes
   super.itemStateChanged(event);
 }

} // //CLASS //SphereGroup - create a group of spheres on the XY plane // //DESCRIPTION //An XY grid of spheres is created. The number of spheres in X and Y, //the spacing in X and Y, the sphere radius, and the appearance can //all be set. // //This grid of spheres is used by several of the examples as a generic //bit of foreground geometry. // //SEE ALSO //Ex*Light //ExBackground* // //AUTHOR //David R. Nadeau / San Diego Supercomputer Center // class SphereGroup extends Group {

 //  Constructors
 public SphereGroup() {
   //    radius x,y spacing x,y count appearance
   this(0.25f, 0.75f, 0.75f, 5, 5, null);
 }
 public SphereGroup(Appearance app) {
   //    radius x,y spacing x,y count appearance
   this(0.25f, 0.75f, 0.75f, 5, 5, app);
 }
 public SphereGroup(float radius, float xSpacing, float ySpacing,
     int xCount, int yCount) {
   this(radius, xSpacing, ySpacing, xCount, yCount, null);
 }
 public SphereGroup(float radius, float xSpacing, float ySpacing,
     int xCount, int yCount, Appearance app) {
   if (app == null) {
     app = new Appearance();
     Material material = new Material();
     material.setDiffuseColor(new Color3f(0.8f, 0.8f, 0.8f));
     material.setSpecularColor(new Color3f(0.0f, 0.0f, 0.0f));
     material.setShininess(0.0f);
     app.setMaterial(material);
   }
   double xStart = -xSpacing * (double) (xCount - 1) / 2.0;
   double yStart = -ySpacing * (double) (yCount - 1) / 2.0;
   Sphere sphere = null;
   TransformGroup trans = null;
   Transform3D t3d = new Transform3D();
   Vector3d vec = new Vector3d();
   double x, y = yStart, z = 0.0;
   for (int i = 0; i < yCount; i++) {
     x = xStart;
     for (int j = 0; j < xCount; j++) {
       vec.set(x, y, z);
       t3d.setTranslation(vec);
       trans = new TransformGroup(t3d);
       addChild(trans);
       sphere = new Sphere(radius, // sphere radius
           Primitive.GENERATE_NORMALS, // generate normals
           16, // 16 divisions radially
           app); // it"s appearance
       trans.addChild(sphere);
       x += xSpacing;
     }
     y += ySpacing;
   }
 }

} /**

* The Example class is a base class extended by example applications. The class
* provides basic features to create a top-level frame, add a menubar and
* Canvas3D, build the universe, set up "examine" and "walk" style navigation
* behaviors, and provide hooks so that subclasses can add 3D content to the
* example"s universe.
* <P>
* Using this Example class simplifies the construction of example applications,
* enabling the author to focus upon 3D content and not the busywork of creating
* windows, menus, and universes.
* 
* @version 1.0, 98/04/16
* @author David R. Nadeau, San Diego Supercomputer Center
*/

class Java3DFrame extends Applet implements WindowListener, ActionListener,

   ItemListener, CheckboxMenuListener {
 //  Navigation types
 public final static int Walk = 0;
 public final static int Examine = 1;
 //  Should the scene be compiled?
 private boolean shouldCompile = true;
 //  GUI objects for our subclasses
 protected Java3DFrame example = null;
 protected Frame exampleFrame = null;
 protected MenuBar exampleMenuBar = null;
 protected Canvas3D exampleCanvas = null;
 protected TransformGroup exampleViewTransform = null;
 protected TransformGroup exampleSceneTransform = null;
 protected boolean debug = false;
 //  Private GUI objects and state
 private boolean headlightOnOff = true;
 private int navigationType = Examine;
 private CheckboxMenuItem headlightMenuItem = null;
 private CheckboxMenuItem walkMenuItem = null;
 private CheckboxMenuItem examineMenuItem = null;
 private DirectionalLight headlight = null;
 private ExamineViewerBehavior examineBehavior = null;
 private WalkViewerBehavior walkBehavior = null;
 //--------------------------------------------------------------
 //  ADMINISTRATION
 //--------------------------------------------------------------
 /**
  * The main program entry point when invoked as an application. Each example
  * application that extends this class must define their own main.
  * 
  * @param args
  *            a String array of command-line arguments
  */
 public static void main(String[] args) {
   Java3DFrame ex = new Java3DFrame();
   ex.initialize(args);
   ex.buildUniverse();
   ex.showFrame();
 }
 /**
  * Constructs a new Example object.
  * 
  * @return a new Example that draws no 3D content
  */
 public Java3DFrame() {
   // Do nothing
 }
 /**
  * Initializes the application when invoked as an applet.
  */
 public void init() {
   // Collect properties into String array
   String[] args = new String[2];
   // NOTE: to be done still...
   this.initialize(args);
   this.buildUniverse();
   this.showFrame();
   // NOTE: add something to the browser page?
 }
 /**
  * Initializes the Example by parsing command-line arguments, building an
  * AWT Frame, constructing a menubar, and creating the 3D canvas.
  * 
  * @param args
  *            a String array of command-line arguments
  */
 protected void initialize(String[] args) {
   example = this;
   // Parse incoming arguments
   parseArgs(args);
   // Build the frame
   if (debug)
     System.err.println("Building GUI...");
   exampleFrame = new Frame();
   exampleFrame.setSize(640, 480);
   exampleFrame.setTitle("Java 3D Example");
   exampleFrame.setLayout(new BorderLayout());
   // Set up a close behavior
   exampleFrame.addWindowListener(this);
   // Create a canvas
   exampleCanvas = new Canvas3D(null);
   exampleCanvas.setSize(630, 460);
   exampleFrame.add("Center", exampleCanvas);
   // Build the menubar
   exampleMenuBar = this.buildMenuBar();
   exampleFrame.setMenuBar(exampleMenuBar);
   // Pack
   exampleFrame.pack();
   exampleFrame.validate();
   //    exampleFrame.setVisible( true );
 }
 /**
  * Parses incoming command-line arguments. Applications that subclass this
  * class may override this method to support their own command-line
  * arguments.
  * 
  * @param args
  *            a String array of command-line arguments
  */
 protected void parseArgs(String[] args) {
   for (int i = 0; i < args.length; i++) {
     if (args[i].equals("-d"))
       debug = true;
   }
 }
 //--------------------------------------------------------------
 //  SCENE CONTENT
 //--------------------------------------------------------------
 /**
  * Builds the 3D universe by constructing a virtual universe (via
  * SimpleUniverse), a view platform (via SimpleUniverse), and a view (via
  * SimpleUniverse). A headlight is added and a set of behaviors initialized
  * to handle navigation types.
  */
 protected void buildUniverse() {
   //
   //  Create a SimpleUniverse object, which builds:
   //
   //    - a Locale using the given hi-res coordinate origin
   //
   //    - a ViewingPlatform which in turn builds:
   //          - a MultiTransformGroup with which to move the
   //            the ViewPlatform about
   //
   //          - a ViewPlatform to hold the view
   //
   //          - a BranchGroup to hold avatar geometry (if any)
   //
   //          - a BranchGroup to hold view platform
   //            geometry (if any)
   //
   //    - a Viewer which in turn builds:
   //          - a PhysicalBody which characterizes the user"s
   //            viewing preferences and abilities
   //
   //          - a PhysicalEnvironment which characterizes the
   //            user"s rendering hardware and software
   //
   //          - a JavaSoundMixer which initializes sound
   //            support within the 3D environment
   //
   //          - a View which renders the scene into a Canvas3D
   //
   //  All of these actions could be done explicitly, but
   //  using the SimpleUniverse utilities simplifies the code.
   //
   if (debug)
     System.err.println("Building scene graph...");
   SimpleUniverse universe = new SimpleUniverse(null, // Hi-res coordinate
       // for the origin -
       // use default
       1, // Number of transforms in MultiTransformGroup
       exampleCanvas, // Canvas3D into which to draw
       null); // URL for user configuration file - use defaults
   //
   //  Get the viewer and create an audio device so that
   //  sound will be enabled in this content.
   //
   Viewer viewer = universe.getViewer();
   viewer.createAudioDevice();
   //
   //  Get the viewing platform created by SimpleUniverse.
   //  From that platform, get the inner-most TransformGroup
   //  in the MultiTransformGroup. That inner-most group
   //  contains the ViewPlatform. It is this inner-most
   //  TransformGroup we need in order to:
   //
   //    - add a "headlight" that always aims forward from
   //       the viewer
   //
   //    - change the viewing direction in a "walk" style
   //
   //  The inner-most TransformGroup"s transform will be
   //  changed by the walk behavior (when enabled).
   //
   ViewingPlatform viewingPlatform = universe.getViewingPlatform();
   exampleViewTransform = viewingPlatform.getViewPlatformTransform();
   //
   //  Create a "headlight" as a forward-facing directional light.
   //  Set the light"s bounds to huge. Since we want the light
   //  on the viewer"s "head", we need the light within the
   //  TransformGroup containing the ViewPlatform. The
   //  ViewingPlatform class creates a handy hook to do this
   //  called "platform geometry". The PlatformGeometry class is
   //  subclassed off of BranchGroup, and is intended to contain
   //  a description of the 3D platform itself... PLUS a headlight!
   //  So, to add the headlight, create a new PlatformGeometry group,
   //  add the light to it, then add that platform geometry to the
   //  ViewingPlatform.
   //
   BoundingSphere allBounds = new BoundingSphere(
       new Point3d(0.0, 0.0, 0.0), 100000.0);
   PlatformGeometry pg = new PlatformGeometry();
   headlight = new DirectionalLight();
   headlight.setColor(White);
   headlight.setDirection(new Vector3f(0.0f, 0.0f, -1.0f));
   headlight.setInfluencingBounds(allBounds);
   headlight.setCapability(Light.ALLOW_STATE_WRITE);
   pg.addChild(headlight);
   viewingPlatform.setPlatformGeometry(pg);
   //
   //  Create the 3D content BranchGroup, containing:
   //
   //    - a TransformGroup who"s transform the examine behavior
   //      will change (when enabled).
   //
   //    - 3D geometry to view
   //
   // Build the scene root
   BranchGroup sceneRoot = new BranchGroup();
   // Build a transform that we can modify
   exampleSceneTransform = new TransformGroup();
   exampleSceneTransform
       .setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
   exampleSceneTransform
       .setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
   exampleSceneTransform.setCapability(Group.ALLOW_CHILDREN_EXTEND);
   //
   //  Build the scene, add it to the transform, and add
   //  the transform to the scene root
   //
   if (debug)
     System.err.println("  scene...");
   Group scene = this.buildScene();
   exampleSceneTransform.addChild(scene);
   sceneRoot.addChild(exampleSceneTransform);
   //
   //  Create a pair of behaviors to implement two navigation
   //  types:
   //
   //    - "examine": a style where mouse drags rotate about
   //      the scene"s origin as if it is an object under
   //      examination. This is similar to the "Examine"
   //      navigation type used by VRML browsers.
   //
   //    - "walk": a style where mouse drags rotate about
   //      the viewer"s center as if the viewer is turning
   //      about to look at a scene they are in. This is
   //      similar to the "Walk" navigation type used by
   //      VRML browsers.
   //
   //  Aim the examine behavior at the scene"s TransformGroup
   //  and add the behavior to the scene root.
   //
   //  Aim the walk behavior at the viewing platform"s
   //  TransformGroup and add the behavior to the scene root.
   //
   //  Enable one (and only one!) of the two behaviors
   //  depending upon the current navigation type.
   //
   examineBehavior = new ExamineViewerBehavior(exampleSceneTransform, // Transform
       // gorup
       // to
       // modify
       exampleFrame); // Parent frame for cusor changes
   examineBehavior.setSchedulingBounds(allBounds);
   sceneRoot.addChild(examineBehavior);
   walkBehavior = new WalkViewerBehavior(exampleViewTransform, // Transform
       // group to
       // modify
       exampleFrame); // Parent frame for cusor changes
   walkBehavior.setSchedulingBounds(allBounds);
   sceneRoot.addChild(walkBehavior);
   if (navigationType == Walk) {
     examineBehavior.setEnable(false);
     walkBehavior.setEnable(true);
   } else {
     examineBehavior.setEnable(true);
     walkBehavior.setEnable(false);
   }
   //
   //  Compile the scene branch group and add it to the
   //  SimpleUniverse.
   //
   if (shouldCompile)
     sceneRoot.rupile();
   universe.addBranchGraph(sceneRoot);
   reset();
 }
 /**
  * Builds the scene. Example application subclasses should replace this
  * method with their own method to build 3D content.
  * 
  * @return a Group containing 3D content to display
  */
 public Group buildScene() {
   // Build the scene group containing nothing
   Group scene = new Group();
   return scene;
 }
 //--------------------------------------------------------------
 //  SET/GET METHODS
 //--------------------------------------------------------------
 /**
  * Sets the headlight on/off state. The headlight faces forward in the
  * direction the viewer is facing. Example applications that add their own
  * lights will typically turn the headlight off. A standard menu item
  * enables the headlight to be turned on and off via user control.
  * 
  * @param onOff
  *            a boolean turning the light on (true) or off (false)
  */
 public void setHeadlightEnable(boolean onOff) {
   headlightOnOff = onOff;
   if (headlight != null)
     headlight.setEnable(headlightOnOff);
   if (headlightMenuItem != null)
     headlightMenuItem.setState(headlightOnOff);
 }
 /**
  * Gets the headlight on/off state.
  * 
  * @return a boolean indicating if the headlight is on or off
  */
 public boolean getHeadlightEnable() {
   return headlightOnOff;
 }
 /**
  * Sets the navigation type to be either Examine or Walk. The Examine
  * navigation type sets up behaviors that use mouse drags to rotate and
  * translate scene content as if it is an object held at arm"s length and
  * under examination. The Walk navigation type uses mouse drags to rotate
  * and translate the viewer as if they are walking through the content. The
  * Examine type is the default.
  * 
  * @param nav
  *            either Walk or Examine
  */
 public void setNavigationType(int nav) {
   if (nav == Walk) {
     navigationType = Walk;
     if (walkMenuItem != null)
       walkMenuItem.setState(true);
     if (examineMenuItem != null)
       examineMenuItem.setState(false);
     if (walkBehavior != null)
       walkBehavior.setEnable(true);
     if (examineBehavior != null)
       examineBehavior.setEnable(false);
   } else {
     navigationType = Examine;
     if (walkMenuItem != null)
       walkMenuItem.setState(false);
     if (examineMenuItem != null)
       examineMenuItem.setState(true);
     if (walkBehavior != null)
       walkBehavior.setEnable(false);
     if (examineBehavior != null)
       examineBehavior.setEnable(true);
   }
 }
 /**
  * Gets the current navigation type, returning either Walk or Examine.
  * 
  * @return either Walk or Examine
  */
 public int getNavigationType() {
   return navigationType;
 }
 /**
  * Sets whether the scene graph should be compiled or not. Normally this is
  * always a good idea. For some example applications that use this Example
  * framework, it is useful to disable compilation - particularly when nodes
  * and node components will need to be made un-live in order to make
  * changes. Once compiled, such components can be made un-live, but they are
  * still unchangable unless appropriate capabilities have been set.
  * 
  * @param onOff
  *            a boolean turning compilation on (true) or off (false)
  */
 public void setCompilable(boolean onOff) {
   shouldCompile = onOff;
 }
 /**
  * Gets whether the scene graph will be compiled or not.
  * 
  * @return a boolean indicating if scene graph compilation is on or off
  */
 public boolean getCompilable() {
   return shouldCompile;
 }
 //These methods will be replaced
 //  Set the view position and direction
 public void setViewpoint(Point3f position, Vector3f direction) {
   Transform3D t = new Transform3D();
   t.set(new Vector3f(position));
   exampleViewTransform.setTransform(t);
   // how to set direction?
 }
 //  Reset transforms
 public void reset() {
   Transform3D trans = new Transform3D();
   exampleSceneTransform.setTransform(trans);
   trans.set(new Vector3f(0.0f, 0.0f, 10.0f));
   exampleViewTransform.setTransform(trans);
   setNavigationType(navigationType);
 }
 //
 //  Gets the URL (with file: prepended) for the current directory.
 //  This is a terrible hack needed in the Alpha release of Java3D
 //  in order to build a full path URL for loading sounds with
 //  MediaContainer. When MediaContainer is fully implemented,
 //  it should handle relative path names, but not yet.
 //
 public String getCurrentDirectory() {
   // Create a bogus file so that we can query it"s path
   File dummy = new File("dummy.tmp");
   String dummyPath = dummy.getAbsolutePath();
   // strip "/dummy.tmp" from end of dummyPath and put into "path"
   if (dummyPath.endsWith(File.separator + "dummy.tmp")) {
     int index = dummyPath.lastIndexOf(File.separator + "dummy.tmp");
     if (index >= 0) {
       int pathLength = index + 5; // pre-pend "file:"
       char[] charPath = new char[pathLength];
       dummyPath.getChars(0, index, charPath, 5);
       String path = new String(charPath, 0, pathLength);
       path = "file:" + path.substring(5, pathLength);
       return path + File.separator;
     }
   }
   return dummyPath + File.separator;
 }
 //--------------------------------------------------------------
 //  USER INTERFACE
 //--------------------------------------------------------------
 /**
  * Builds the example AWT Frame menubar. Standard menus and their options
  * are added. Applications that subclass this class should build their
  * menubar additions within their initialize method.
  * 
  * @return a MenuBar for the AWT Frame
  */
 private MenuBar buildMenuBar() {
   // Build the menubar
   MenuBar menuBar = new MenuBar();
   // File menu
   Menu m = new Menu("File");
   m.addActionListener(this);
   m.add("Exit");
   menuBar.add(m);
   // View menu
   m = new Menu("View");
   m.addActionListener(this);
   m.add("Reset view");
   m.addSeparator();
   walkMenuItem = new CheckboxMenuItem("Walk");
   walkMenuItem.addItemListener(this);
   m.add(walkMenuItem);
   examineMenuItem = new CheckboxMenuItem("Examine");
   examineMenuItem.addItemListener(this);
   m.add(examineMenuItem);
   if (navigationType == Walk) {
     walkMenuItem.setState(true);
     examineMenuItem.setState(false);
   } else {
     walkMenuItem.setState(false);
     examineMenuItem.setState(true);
   }
   m.addSeparator();
   headlightMenuItem = new CheckboxMenuItem("Headlight on/off");
   headlightMenuItem.addItemListener(this);
   headlightMenuItem.setState(headlightOnOff);
   m.add(headlightMenuItem);
   menuBar.add(m);
   return menuBar;
 }
 /**
  * Shows the application"s frame, making it and its menubar, 3D canvas, and
  * 3D content visible.
  */
 public void showFrame() {
   exampleFrame.show();
 }
 /**
  * Quits the application.
  */
 public void quit() {
   System.exit(0);
 }
 /**
  * Handles menu selections.
  * 
  * @param event
  *            an ActionEvent indicating what menu action requires handling
  */
 public void actionPerformed(ActionEvent event) {
   String arg = event.getActionCommand();
   if (arg.equals("Reset view"))
     reset();
   else if (arg.equals("Exit"))
     quit();
 }
 /**
  * Handles checkbox items on a CheckboxMenu. The Example class has none of
  * its own, but subclasses may have some.
  * 
  * @param menu
  *            which CheckboxMenu needs action
  * @param check
  *            which CheckboxMenu item has changed
  */
 public void checkboxChanged(CheckboxMenu menu, int check) {
   // None for us
 }
 /**
  * Handles on/off checkbox items on a standard menu.
  * 
  * @param event
  *            an ItemEvent indicating what requires handling
  */
 public void itemStateChanged(ItemEvent event) {
   Object src = event.getSource();
   boolean state;
   if (src == headlightMenuItem) {
     state = headlightMenuItem.getState();
     headlight.setEnable(state);
   } else if (src == walkMenuItem)
     setNavigationType(Walk);
   else if (src == examineMenuItem)
     setNavigationType(Examine);
 }
 /**
  * Handles a window closing event notifying the application that the user
  * has chosen to close the application without selecting the "Exit" menu
  * item.
  * 
  * @param event
  *            a WindowEvent indicating the window is closing
  */
 public void windowClosing(WindowEvent event) {
   quit();
 }
 public void windowClosed(WindowEvent event) {
 }
 public void windowOpened(WindowEvent event) {
 }
 public void windowIconified(WindowEvent event) {
 }
 public void windowDeiconified(WindowEvent event) {
 }
 public void windowActivated(WindowEvent event) {
 }
 public void windowDeactivated(WindowEvent event) {
 }
 //  Well known colors, positions, and directions
 public final static Color3f White = new Color3f(1.0f, 1.0f, 1.0f);
 public final static Color3f Gray = new Color3f(0.7f, 0.7f, 0.7f);
 public final static Color3f DarkGray = new Color3f(0.2f, 0.2f, 0.2f);
 public final static Color3f Black = new Color3f(0.0f, 0.0f, 0.0f);
 public final static Color3f Red = new Color3f(1.0f, 0.0f, 0.0f);
 public final static Color3f DarkRed = new Color3f(0.3f, 0.0f, 0.0f);
 public final static Color3f Yellow = new Color3f(1.0f, 1.0f, 0.0f);
 public final static Color3f DarkYellow = new Color3f(0.3f, 0.3f, 0.0f);
 public final static Color3f Green = new Color3f(0.0f, 1.0f, 0.0f);
 public final static Color3f DarkGreen = new Color3f(0.0f, 0.3f, 0.0f);
 public final static Color3f Cyan = new Color3f(0.0f, 1.0f, 1.0f);
 public final static Color3f Blue = new Color3f(0.0f, 0.0f, 1.0f);
 public final static Color3f DarkBlue = new Color3f(0.0f, 0.0f, 0.3f);
 public final static Color3f Magenta = new Color3f(1.0f, 0.0f, 1.0f);
 public final static Vector3f PosX = new Vector3f(1.0f, 0.0f, 0.0f);
 public final static Vector3f NegX = new Vector3f(-1.0f, 0.0f, 0.0f);
 public final static Vector3f PosY = new Vector3f(0.0f, 1.0f, 0.0f);
 public final static Vector3f NegY = new Vector3f(0.0f, -1.0f, 0.0f);
 public final static Vector3f PosZ = new Vector3f(0.0f, 0.0f, 1.0f);
 public final static Vector3f NegZ = new Vector3f(0.0f, 0.0f, -1.0f);
 public final static Point3f Origin = new Point3f(0.0f, 0.0f, 0.0f);
 public final static Point3f PlusX = new Point3f(0.75f, 0.0f, 0.0f);
 public final static Point3f MinusX = new Point3f(-0.75f, 0.0f, 0.0f);
 public final static Point3f PlusY = new Point3f(0.0f, 0.75f, 0.0f);
 public final static Point3f MinusY = new Point3f(0.0f, -0.75f, 0.0f);
 public final static Point3f PlusZ = new Point3f(0.0f, 0.0f, 0.75f);
 public final static Point3f MinusZ = new Point3f(0.0f, 0.0f, -0.75f);

} // //INTERFACE //CheckboxMenuListener - listen for checkbox change events // //DESCRIPTION //The checkboxChanged method is called by users of this class //to notify the listener when a checkbox choice has changed on //a CheckboxMenu class menu. // interface CheckboxMenuListener extends EventListener {

 public void checkboxChanged(CheckboxMenu menu, int check);

} /**

* ExamineViewerBehavior
* 
* @version 1.0, 98/04/16
*/

/**

* Wakeup on mouse button presses, releases, and mouse movements and generate
* transforms in an "examination style" that enables the user to rotate,
* translation, and zoom an object as if it is held at arm"s length. Such an
* examination style is similar to the "Examine" navigation type used by VRML
* browsers.
* 
* The behavior maps mouse drags to different transforms depending upon the
* mosue button held down:
* 
* Button 1 (left) Horizontal movement --> Y-axis rotation Vertical movement -->
* X-axis rotation
* 
* Button 2 (middle) Horizontal movement --> nothing Vertical movement -->
* Z-axis translation
* 
* Button 3 (right) Horizontal movement --> X-axis translation Vertical movement
* --> Y-axis translation
* 
* To support systems with 2 or 1 mouse buttons, the following alternate
* mappings are supported while dragging with any mouse button held down and
* zero or more keyboard modifiers held down:
* 
* No modifiers = Button 1 ALT = Button 2 Meta = Button 3 Control = Button 3
* 
* The behavior automatically modifies a TransformGroup provided to the
* constructor. The TransformGroup"s transform can be set at any time by the
* application or other behaviors to cause the examine rotation and translation
* to be reset.
*/

// This class is inspired by the MouseBehavior, MouseRotate, // MouseTranslate, and MouseZoom utility behaviors provided with // Java 3D. This class differs from those utilities in that it: // // (a) encapsulates all three behaviors into one in order to // enforce a specific "Examine" symantic // // (b) supports set/get of the rotation and translation factors // that control the speed of movement. // // (c) supports the "Control" modifier as an alternative to the // "Meta" modifier not present on PC, Mac, and most non-Sun // keyboards. This makes button3 behavior usable on PCs, // Macs, and other systems with fewer than 3 mouse buttons. class ExamineViewerBehavior extends ViewerBehavior {

 // Previous cursor location
 protected int previousX = 0;
 protected int previousY = 0;
 // Saved standard cursor
 protected Cursor savedCursor = null;
 /**
  * Construct an examine behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into a
  * transform group given later with the setTransformGroup( ) method.
  */
 public ExamineViewerBehavior() {
   super();
 }
 /**
  * Construct an examine behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into a
  * transform group given later with the setTransformGroup( ) method.
  * 
  * @param parent
  *            The AWT Component that contains the area generating mouse
  *            events.
  */
 public ExamineViewerBehavior(Component parent) {
   super(parent);
 }
 /**
  * Construct an examine behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into the
  * given transform group.
  * 
  * @param transformGroup
  *            The transform group to be modified by the behavior.
  */
 public ExamineViewerBehavior(TransformGroup transformGroup) {
   super();
   subjectTransformGroup = transformGroup;
 }
 /**
  * Construct an examine behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into the
  * given transform group.
  * 
  * @param transformGroup
  *            The transform group to be modified by the behavior.
  * @param parent
  *            The AWT Component that contains the area generating mouse
  *            events.
  */
 public ExamineViewerBehavior(TransformGroup transformGroup, Component parent) {
   super(parent);
   subjectTransformGroup = transformGroup;
 }
 /**
  * Respond to a button1 event (press, release, or drag).
  * 
  * @param mouseEvent
  *            A MouseEvent to respond to.
  */
 public void onButton1(MouseEvent mev) {
   if (subjectTransformGroup == null)
     return;
   int x = mev.getX();
   int y = mev.getY();
   if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
     // Mouse button pressed: record position
     previousX = x;
     previousY = y;
     // Change to a "move" cursor
     if (parentComponent != null) {
       savedCursor = parentComponent.getCursor();
       parentComponent.setCursor(Cursor
           .getPredefinedCursor(Cursor.HAND_CURSOR));
     }
     return;
   }
   if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
     // Mouse button released: do nothing
     // Switch the cursor back
     if (parentComponent != null)
       parentComponent.setCursor(savedCursor);
     return;
   }
   //
   // Mouse moved while button down: create a rotation
   //
   // Compute the delta in X and Y from the previous
   // position. Use the delta to compute rotation
   // angles with the mapping:
   //
   //   positive X mouse delta --> positive Y-axis rotation
   //   positive Y mouse delta --> positive X-axis rotation
   //
   // where positive X mouse movement is to the right, and
   // positive Y mouse movement is **down** the screen.
   //
   int deltaX = x - previousX;
   int deltaY = y - previousY;
   if (deltaX > UNUSUAL_XDELTA || deltaX < -UNUSUAL_XDELTA
       || deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {
     // Deltas are too huge to be believable. Probably a glitch.
     // Don"t record the new XY location, or do anything.
     return;
   }
   double xRotationAngle = deltaY * XRotationFactor;
   double yRotationAngle = deltaX * YRotationFactor;
   //
   // Build transforms
   //
   transform1.rotX(xRotationAngle);
   transform2.rotY(yRotationAngle);
   // Get and save the current transform matrix
   subjectTransformGroup.getTransform(currentTransform);
   currentTransform.get(matrix);
   translate.set(matrix.m03, matrix.m13, matrix.m23);
   // Translate to the origin, rotate, then translate back
   currentTransform.setTranslation(origin);
   currentTransform.mul(transform1, currentTransform);
   currentTransform.mul(transform2, currentTransform);
   currentTransform.setTranslation(translate);
   // Update the transform group
   subjectTransformGroup.setTransform(currentTransform);
   previousX = x;
   previousY = y;
 }
 /**
  * Respond to a button2 event (press, release, or drag).
  * 
  * @param mouseEvent
  *            A MouseEvent to respond to.
  */
 public void onButton2(MouseEvent mev) {
   if (subjectTransformGroup == null)
     return;
   int x = mev.getX();
   int y = mev.getY();
   if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
     // Mouse button pressed: record position
     previousX = x;
     previousY = y;
     // Change to a "move" cursor
     if (parentComponent != null) {
       savedCursor = parentComponent.getCursor();
       parentComponent.setCursor(Cursor
           .getPredefinedCursor(Cursor.MOVE_CURSOR));
     }
     return;
   }
   if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
     // Mouse button released: do nothing
     // Switch the cursor back
     if (parentComponent != null)
       parentComponent.setCursor(savedCursor);
     return;
   }
   //
   // Mouse moved while button down: create a translation
   //
   // Compute the delta in Y from the previous
   // position. Use the delta to compute translation
   // distances with the mapping:
   //
   //   positive Y mouse delta --> positive Y-axis translation
   //
   // where positive X mouse movement is to the right, and
   // positive Y mouse movement is **down** the screen.
   //
   int deltaY = y - previousY;
   if (deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {
     // Deltas are too huge to be believable. Probably a glitch.
     // Don"t record the new XY location, or do anything.
     return;
   }
   double zTranslationDistance = deltaY * ZTranslationFactor;
   //
   // Build transforms
   //
   translate.set(0.0, 0.0, zTranslationDistance);
   transform1.set(translate);
   // Get and save the current transform
   subjectTransformGroup.getTransform(currentTransform);
   // Translate as needed
   currentTransform.mul(transform1, currentTransform);
   // Update the transform group
   subjectTransformGroup.setTransform(currentTransform);
   previousX = x;
   previousY = y;
 }
 /**
  * Respond to a button3 event (press, release, or drag).
  * 
  * @param mouseEvent
  *            A MouseEvent to respond to.
  */
 public void onButton3(MouseEvent mev) {
   if (subjectTransformGroup == null)
     return;
   int x = mev.getX();
   int y = mev.getY();
   if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
     // Mouse button pressed: record position
     previousX = x;
     previousY = y;
     // Change to a "move" cursor
     if (parentComponent != null) {
       savedCursor = parentComponent.getCursor();
       parentComponent.setCursor(Cursor
           .getPredefinedCursor(Cursor.MOVE_CURSOR));
     }
     return;
   }
   if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
     // Mouse button released: do nothing
     // Switch the cursor back
     if (parentComponent != null)
       parentComponent.setCursor(savedCursor);
     return;
   }
   //
   // Mouse moved while button down: create a translation
   //
   // Compute the delta in X and Y from the previous
   // position. Use the delta to compute translation
   // distances with the mapping:
   //
   //   positive X mouse delta --> positive X-axis translation
   //   positive Y mouse delta --> negative Y-axis translation
   //
   // where positive X mouse movement is to the right, and
   // positive Y mouse movement is **down** the screen.
   //
   int deltaX = x - previousX;
   int deltaY = y - previousY;
   if (deltaX > UNUSUAL_XDELTA || deltaX < -UNUSUAL_XDELTA
       || deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {
     // Deltas are too huge to be believable. Probably a glitch.
     // Don"t record the new XY location, or do anything.
     return;
   }
   double xTranslationDistance = deltaX * XTranslationFactor;
   double yTranslationDistance = -deltaY * YTranslationFactor;
   //
   // Build transforms
   //
   translate.set(xTranslationDistance, yTranslationDistance, 0.0);
   transform1.set(translate);
   // Get and save the current transform
   subjectTransformGroup.getTransform(currentTransform);
   // Translate as needed
   currentTransform.mul(transform1, currentTransform);
   // Update the transform group
   subjectTransformGroup.setTransform(currentTransform);
   previousX = x;
   previousY = y;
 }
 /**
  * Respond to an elapsed frames event (assuming subclass has set up a wakeup
  * criterion for it).
  * 
  * @param time
  *            A WakeupOnElapsedFrames criterion to respond to.
  */
 public void onElapsedFrames(WakeupOnElapsedFrames timeEvent) {
   // Can"t happen
 }

} /*

* 
* Copyright (c) 1998 David R. Nadeau
*  
*/

/**

* WalkViewerBehavior is a utility class that creates a "walking style"
* navigation symantic.
* 
* The behavior wakes up on mouse button presses, releases, and mouse movements
* and generates transforms in a "walk style" that enables the user to walk
* through a scene, translating and turning about as if they are within the
* scene. Such a walk style is similar to the "Walk" navigation type used by
* VRML browsers.
* <P>
* The behavior maps mouse drags to different transforms depending upon the
* mouse button held down:
*
*
Button 1 (left) *
Horizontal movement --> Y-axis rotation *
Vertical movement --> Z-axis translation * *
Button 2 (middle) *
Horizontal movement --> Y-axis rotation *
Vertical movement --> X-axis rotation * *
Button 3 (right) *
Horizontal movement --> X-axis translation *
Vertical movement --> Y-axis translation *
* 
* To support systems with 2 or 1 mouse buttons, the following alternate
* mappings are supported while dragging with any mouse button held down and
* zero or more keyboard modifiers held down:
*
    *
  • No modifiers = Button 1 *
  • ALT = Button 2 *
  • Meta = Button 3 *
  • Control = Button 3 *
* The behavior automatically modifies a TransformGroup provided to the
* constructor. The TransformGroup"s transform can be set at any time by the
* application or other behaviors to cause the walk rotation and translation to
* be reset.
* <P>
* While a mouse button is down, the behavior automatically changes the cursor
* in a given parent AWT Component. If no parent Component is given, no cursor
* changes are attempted.
* 
* @version 1.0, 98/04/16
* @author David R. Nadeau, San Diego Supercomputer Center
*/

class WalkViewerBehavior extends ViewerBehavior {

 // This class is inspired by the MouseBehavior, MouseRotate,
 // MouseTranslate, and MouseZoom utility behaviors provided with
 // Java 3D. This class differs from those utilities in that it:
 //
 //    (a) encapsulates all three behaviors into one in order to
 //        enforce a specific "Walk" symantic
 //
 //    (b) supports set/get of the rotation and translation factors
 //        that control the speed of movement.
 //
 //    (c) supports the "Control" modifier as an alternative to the
 //        "Meta" modifier not present on PC, Mac, and most non-Sun
 //        keyboards. This makes button3 behavior usable on PCs,
 //        Macs, and other systems with fewer than 3 mouse buttons.
 // Previous and initial cursor locations
 protected int previousX = 0;
 protected int previousY = 0;
 protected int initialX = 0;
 protected int initialY = 0;
 // Deadzone size (delta from initial XY for which no
 // translate or rotate action is taken
 protected static final int DELTAX_DEADZONE = 10;
 protected static final int DELTAY_DEADZONE = 10;
 // Keep a set of wakeup criterion for animation-generated
 // event types.
 protected WakeupCriterion[] mouseAndAnimationEvents = null;
 protected WakeupOr mouseAndAnimationCriterion = null;
 protected WakeupOr savedMouseCriterion = null;
 // Saved standard cursor
 protected Cursor savedCursor = null;
 /**
  * Default Rotation and translation scaling factors for animated movements
  * (Button 1 press).
  */
 public static final double DEFAULT_YROTATION_ANIMATION_FACTOR = 0.0002;
 public static final double DEFAULT_ZTRANSLATION_ANIMATION_FACTOR = 0.01;
 protected double YRotationAnimationFactor = DEFAULT_YROTATION_ANIMATION_FACTOR;
 protected double ZTranslationAnimationFactor = DEFAULT_ZTRANSLATION_ANIMATION_FACTOR;
 /**
  * Constructs a new walk behavior that converts mouse actions into rotations
  * and translations. Rotations and translations are written into a
  * TransformGroup that must be set using the setTransformGroup method. The
  * cursor will be changed during mouse actions if the parent frame is set
  * using the setParentComponent method.
  * 
  * @return a new WalkViewerBehavior that needs its TransformGroup and parent
  *         Component set
  */
 public WalkViewerBehavior() {
   super();
 }
 /**
  * Constructs a new walk behavior that converts mouse actions into rotations
  * and translations. Rotations and translations are written into a
  * TransformGroup that must be set using the setTransformGroup method. The
  * cursor will be changed within the given AWT parent Component during mouse
  * drags.
  * 
  * @param parent
  *            a parent AWT Component within which the cursor will change
  *            during mouse drags
  * 
  * @return a new WalkViewerBehavior that needs its TransformGroup and parent
  *         Component set
  */
 public WalkViewerBehavior(Component parent) {
   super(parent);
 }
 /**
  * Constructs a new walk behavior that converts mouse actions into rotations
  * and translations. Rotations and translations are written into the given
  * TransformGroup. The cursor will be changed during mouse actions if the
  * parent frame is set using the setParentComponent method.
  * 
  * @param transformGroup
  *            a TransformGroup whos transform is read and written by the
  *            behavior
  * 
  * @return a new WalkViewerBehavior that needs its TransformGroup and parent
  *         Component set
  */
 public WalkViewerBehavior(TransformGroup transformGroup) {
   super();
   subjectTransformGroup = transformGroup;
 }
 /**
  * Constructs a new walk behavior that converts mouse actions into rotations
  * and translations. Rotations and translations are written into the given
  * TransformGroup. The cursor will be changed within the given AWT parent
  * Component during mouse drags.
  * 
  * @param transformGroup
  *            a TransformGroup whos transform is read and written by the
  *            behavior
  * 
  * @param parent
  *            a parent AWT Component within which the cursor will change
  *            during mouse drags
  * 
  * @return a new WalkViewerBehavior that needs its TransformGroup and parent
  *         Component set
  */
 public WalkViewerBehavior(TransformGroup transformGroup, Component parent) {
   super(parent);
   subjectTransformGroup = transformGroup;
 }
 /**
  * Initializes the behavior.
  */
 public void initialize() {
   super.initialize();
   savedMouseCriterion = mouseCriterion; // from parent class
   mouseAndAnimationEvents = new WakeupCriterion[4];
   mouseAndAnimationEvents[0] = new WakeupOnAWTEvent(
       MouseEvent.MOUSE_DRAGGED);
   mouseAndAnimationEvents[1] = new WakeupOnAWTEvent(
       MouseEvent.MOUSE_PRESSED);
   mouseAndAnimationEvents[2] = new WakeupOnAWTEvent(
       MouseEvent.MOUSE_RELEASED);
   mouseAndAnimationEvents[3] = new WakeupOnElapsedFrames(0);
   mouseAndAnimationCriterion = new WakeupOr(mouseAndAnimationEvents);
   // Don"t use the above criterion until a button 1 down event
 }
 /**
  * Sets the Y rotation animation scaling factor for Y-axis rotations. This
  * scaling factor is used to control the speed of Y rotation when button 1
  * is pressed and dragged.
  * 
  * @param factor
  *            the double Y rotation scaling factor
  */
 public void setYRotationAnimationFactor(double factor) {
   YRotationAnimationFactor = factor;
 }
 /**
  * Gets the current Y animation rotation scaling factor for Y-axis
  * rotations.
  * 
  * @return the double Y rotation scaling factor
  */
 public double getYRotationAnimationFactor() {
   return YRotationAnimationFactor;
 }
 /**
  * Sets the Z animation translation scaling factor for Z-axis translations.
  * This scaling factor is used to control the speed of Z translation when
  * button 1 is pressed and dragged.
  * 
  * @param factor
  *            the double Z translation scaling factor
  */
 public void setZTranslationAnimationFactor(double factor) {
   ZTranslationAnimationFactor = factor;
 }
 /**
  * Gets the current Z animation translation scaling factor for Z-axis
  * translations.
  * 
  * @return the double Z translation scaling factor
  */
 public double getZTranslationAnimationFactor() {
   return ZTranslationAnimationFactor;
 }
 /**
  * Responds to an elapsed frames event. Such an event is generated on every
  * frame while button 1 is held down. On each call, this method computes new
  * Y-axis rotation and Z-axis translation values and writes them to the
  * behavior"s TransformGroup. The translation and rotation amounts are
  * computed based upon the distance between the current cursor location and
  * the cursor location when button 1 was pressed. As this distance
  * increases, the translation or rotation amount increases.
  * 
  * @param time
  *            the WakeupOnElapsedFrames criterion to respond to
  */
 public void onElapsedFrames(WakeupOnElapsedFrames timeEvent) {
   //
   // Time elapsed while button down: create a rotation and
   // a translation.
   //
   // Compute the delta in X and Y from the initial position to
   // the previous position. Multiply the delta times a scaling
   // factor to compute an offset to add to the current translation
   // and rotation. Use the mapping:
   //
   //   positive X mouse delta --> negative Y-axis rotation
   //   positive Y mouse delta --> positive Z-axis translation
   //
   // where positive X mouse movement is to the right, and
   // positive Y mouse movement is **down** the screen.
   //
   if (buttonPressed != BUTTON1)
     return;
   int deltaX = previousX - initialX;
   int deltaY = previousY - initialY;
   double yRotationAngle = -deltaX * YRotationAnimationFactor;
   double zTranslationDistance = deltaY * ZTranslationAnimationFactor;
   //
   // Build transforms
   //
   transform1.rotY(yRotationAngle);
   translate.set(0.0, 0.0, zTranslationDistance);
   // Get and save the current transform matrix
   subjectTransformGroup.getTransform(currentTransform);
   currentTransform.get(matrix);
   // Translate to the origin, rotate, then translate back
   currentTransform.setTranslation(origin);
   currentTransform.mul(transform1, currentTransform);
   // Translate back from the origin by the original translation
   // distance, plus the new walk translation... but force walk
   // to travel on a plane by ignoring the Y component of a
   // transformed translation vector.
   currentTransform.transform(translate);
   translate.x += matrix.m03; // add in existing X translation
   translate.y = matrix.m13; // use Y translation
   translate.z += matrix.m23; // add in existing Z translation
   currentTransform.setTranslation(translate);
   // Update the transform group
   subjectTransformGroup.setTransform(currentTransform);
 }
 /**
  * Responds to a button1 event (press, release, or drag). On a press, the
  * method adds a wakeup criterion to the behavior"s set, callling for the
  * behavior to be awoken on each frame. On a button prelease, this criterion
  * is removed from the set.
  * 
  * @param mouseEvent
  *            the MouseEvent to respond to
  */
 public void onButton1(MouseEvent mev) {
   if (subjectTransformGroup == null)
     return;
   int x = mev.getX();
   int y = mev.getY();
   if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
     // Mouse button pressed: record position and change
     // the wakeup criterion to include elapsed time wakeups
     // so we can animate.
     previousX = x;
     previousY = y;
     initialX = x;
     initialY = y;
     // Swap criterion... parent class will not reschedule us
     mouseCriterion = mouseAndAnimationCriterion;
     // Change to a "move" cursor
     if (parentComponent != null) {
       savedCursor = parentComponent.getCursor();
       parentComponent.setCursor(Cursor
           .getPredefinedCursor(Cursor.HAND_CURSOR));
     }
     return;
   }
   if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
     // Mouse button released: restore original wakeup
     // criterion which only includes mouse activity, not
     // elapsed time
     mouseCriterion = savedMouseCriterion;
     // Switch the cursor back
     if (parentComponent != null)
       parentComponent.setCursor(savedCursor);
     return;
   }
   previousX = x;
   previousY = y;
 }
 /**
  * Responds to a button2 event (press, release, or drag). On a press, the
  * method records the initial cursor location. On a drag, the difference
  * between the current and previous cursor location provides a delta that
  * controls the amount by which to rotate in X and Y.
  * 
  * @param mouseEvent
  *            the MouseEvent to respond to
  */
 public void onButton2(MouseEvent mev) {
   if (subjectTransformGroup == null)
     return;
   int x = mev.getX();
   int y = mev.getY();
   if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
     // Mouse button pressed: record position
     previousX = x;
     previousY = y;
     initialX = x;
     initialY = y;
     // Change to a "rotate" cursor
     if (parentComponent != null) {
       savedCursor = parentComponent.getCursor();
       parentComponent.setCursor(Cursor
           .getPredefinedCursor(Cursor.MOVE_CURSOR));
     }
     return;
   }
   if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
     // Mouse button released: do nothing
     // Switch the cursor back
     if (parentComponent != null)
       parentComponent.setCursor(savedCursor);
     return;
   }
   //
   // Mouse moved while button down: create a rotation
   //
   // Compute the delta in X and Y from the previous
   // position. Use the delta to compute rotation
   // angles with the mapping:
   //
   //   positive X mouse delta --> negative Y-axis rotation
   //   positive Y mouse delta --> negative X-axis rotation
   //
   // where positive X mouse movement is to the right, and
   // positive Y mouse movement is **down** the screen.
   //
   int deltaX = x - previousX;
   int deltaY = 0;
   if (Math.abs(y - initialY) > DELTAY_DEADZONE) {
     // Cursor has moved far enough vertically to consider
     // it intentional, so get it"s delta.
     deltaY = y - previousY;
   }
   if (deltaX > UNUSUAL_XDELTA || deltaX < -UNUSUAL_XDELTA
       || deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {
     // Deltas are too huge to be believable. Probably a glitch.
     // Don"t record the new XY location, or do anything.
     return;
   }
   double xRotationAngle = -deltaY * XRotationFactor;
   double yRotationAngle = -deltaX * YRotationFactor;
   //
   // Build transforms
   //
   transform1.rotX(xRotationAngle);
   transform2.rotY(yRotationAngle);
   // Get and save the current transform matrix
   subjectTransformGroup.getTransform(currentTransform);
   currentTransform.get(matrix);
   translate.set(matrix.m03, matrix.m13, matrix.m23);
   // Translate to the origin, rotate, then translate back
   currentTransform.setTranslation(origin);
   currentTransform.mul(transform2, currentTransform);
   currentTransform.mul(transform1);
   currentTransform.setTranslation(translate);
   // Update the transform group
   subjectTransformGroup.setTransform(currentTransform);
   previousX = x;
   previousY = y;
 }
 /**
  * Responds to a button3 event (press, release, or drag). On a drag, the
  * difference between the current and previous cursor location provides a
  * delta that controls the amount by which to translate in X and Y.
  * 
  * @param mouseEvent
  *            the MouseEvent to respond to
  */
 public void onButton3(MouseEvent mev) {
   if (subjectTransformGroup == null)
     return;
   int x = mev.getX();
   int y = mev.getY();
   if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
     // Mouse button pressed: record position
     previousX = x;
     previousY = y;
     // Change to a "move" cursor
     if (parentComponent != null) {
       savedCursor = parentComponent.getCursor();
       parentComponent.setCursor(Cursor
           .getPredefinedCursor(Cursor.MOVE_CURSOR));
     }
     return;
   }
   if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
     // Mouse button released: do nothing
     // Switch the cursor back
     if (parentComponent != null)
       parentComponent.setCursor(savedCursor);
     return;
   }
   //
   // Mouse moved while button down: create a translation
   //
   // Compute the delta in X and Y from the previous
   // position. Use the delta to compute translation
   // distances with the mapping:
   //
   //   positive X mouse delta --> positive X-axis translation
   //   positive Y mouse delta --> negative Y-axis translation
   //
   // where positive X mouse movement is to the right, and
   // positive Y mouse movement is **down** the screen.
   //
   int deltaX = x - previousX;
   int deltaY = y - previousY;
   if (deltaX > UNUSUAL_XDELTA || deltaX < -UNUSUAL_XDELTA
       || deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {
     // Deltas are too huge to be believable. Probably a glitch.
     // Don"t record the new XY location, or do anything.
     return;
   }
   double xTranslationDistance = deltaX * XTranslationFactor;
   double yTranslationDistance = -deltaY * YTranslationFactor;
   //
   // Build transforms
   //
   translate.set(xTranslationDistance, yTranslationDistance, 0.0);
   transform1.set(translate);
   // Get and save the current transform
   subjectTransformGroup.getTransform(currentTransform);
   // Translate as needed
   currentTransform.mul(transform1);
   // Update the transform group
   subjectTransformGroup.setTransform(currentTransform);
   previousX = x;
   previousY = y;
 }

} // //CLASS //CheckboxMenu - build a menu of grouped checkboxes // //DESCRIPTION //The class creates a menu with one or more CheckboxMenuItem"s //and monitors that menu. When a menu checkbox is picked, the //previous one is turned off (in radio-button style). Then, //a given listener"s checkboxChanged method is called, passing it //the menu and the item checked. // class CheckboxMenu extends Menu implements ItemListener {

 // State
 protected CheckboxMenuItem[] checks = null;
 protected int current = 0;
 protected CheckboxMenuListener listener = null;
 //  Construct
 public CheckboxMenu(String name, NameValue[] items,
     CheckboxMenuListener listen) {
   this(name, items, 0, listen);
 }
 public CheckboxMenu(String name, NameValue[] items, int cur,
     CheckboxMenuListener listen) {
   super(name);
   current = cur;
   listener = listen;
   if (items == null)
     return;
   checks = new CheckboxMenuItem[items.length];
   for (int i = 0; i < items.length; i++) {
     checks[i] = new CheckboxMenuItem(items[i].name, false);
     checks[i].addItemListener(this);
     add(checks[i]);
   }
   checks[cur].setState(true);
 }
 //  Handle checkbox changed events
 public void itemStateChanged(ItemEvent event) {
   Object src = event.getSource();
   for (int i = 0; i < checks.length; i++) {
     if (src == checks[i]) {
       // Update the checkboxes
       checks[current].setState(false);
       current = i;
       checks[current].setState(true);
       if (listener != null)
         listener.checkboxChanged(this, i);
       return;
     }
   }
 }
 // Methods to get and set state
 public int getCurrent() {
   return current;
 }
 public void setCurrent(int cur) {
   if (cur < 0 || cur >= checks.length)
     return; // ignore out of range choices
   if (checks == null)
     return;
   checks[current].setState(false);
   current = cur;
   checks[current].setState(true);
 }
 public CheckboxMenuItem getSelectedCheckbox() {
   if (checks == null)
     return null;
   return checks[current];
 }
 public void setSelectedCheckbox(CheckboxMenuItem item) {
   if (checks == null)
     return;
   for (int i = 0; i < checks.length; i++) {
     if (item == checks[i]) {
       checks[i].setState(false);
       current = i;
       checks[i].setState(true);
     }
   }
 }

} /**

* ViewerBehavior
* 
* @version 1.0, 98/04/16
*/

/**

* Wakeup on mouse button presses, releases, and mouse movements and generate
* transforms for a transform group. Classes that extend this class impose
* specific symantics, such as "Examine" or "Walk" viewing, similar to the
* navigation types used by VRML browsers.
* 
* To support systems with 2 or 1 mouse buttons, the following alternate
* mappings are supported while dragging with any mouse button held down and
* zero or more keyboard modifiers held down:
* 
* No modifiers = Button 1 ALT = Button 2 Meta = Button 3 Control = Button 3
* 
* The behavior automatically modifies a TransformGroup provided to the
* constructor. The TransformGroup"s transform can be set at any time by the
* application or other behaviors to cause the viewer"s rotation and translation
* to be reset.
*/

// This class is inspired by the MouseBehavior, MouseRotate, // MouseTranslate, and MouseZoom utility behaviors provided with // Java 3D. This class differs from those utilities in that it: // // (a) encapsulates all three behaviors into one in order to // enforce a specific viewing symantic // // (b) supports set/get of the rotation and translation factors // that control the speed of movement. // // (c) supports the "Control" modifier as an alternative to the // "Meta" modifier not present on PC, Mac, and most non-Sun // keyboards. This makes button3 behavior usable on PCs, // Macs, and other systems with fewer than 3 mouse buttons. abstract class ViewerBehavior extends Behavior {

 // Keep track of the transform group who"s transform we modify
 // during mouse motion.
 protected TransformGroup subjectTransformGroup = null;
 // Keep a set of wakeup criterion for different mouse-generated
 // event types.
 protected WakeupCriterion[] mouseEvents = null;
 protected WakeupOr mouseCriterion = null;
 // Track which button was last pressed
 protected static final int BUTTONNONE = -1;
 protected static final int BUTTON1 = 0;
 protected static final int BUTTON2 = 1;
 protected static final int BUTTON3 = 2;
 protected int buttonPressed = BUTTONNONE;
 // Keep a few Transform3Ds for use during event processing. This
 // avoids having to allocate new ones on each event.
 protected Transform3D currentTransform = new Transform3D();
 protected Transform3D transform1 = new Transform3D();
 protected Transform3D transform2 = new Transform3D();
 protected Matrix4d matrix = new Matrix4d();
 protected Vector3d origin = new Vector3d(0.0, 0.0, 0.0);
 protected Vector3d translate = new Vector3d(0.0, 0.0, 0.0);
 // Unusual X and Y delta limits.
 protected static final int UNUSUAL_XDELTA = 400;
 protected static final int UNUSUAL_YDELTA = 400;
 protected Component parentComponent = null;
 /**
  * Construct a viewer behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into a
  * transform group given later with the setTransformGroup( ) method.
  */
 public ViewerBehavior() {
   super();
 }
 /**
  * Construct a viewer behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into a
  * transform group given later with the setTransformGroup( ) method.
  * 
  * @param parent
  *            The AWT Component that contains the area generating mouse
  *            events.
  */
 public ViewerBehavior(Component parent) {
   super();
   parentComponent = parent;
 }
 /**
  * Construct a viewer behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into the
  * given transform group.
  * 
  * @param transformGroup
  *            The transform group to be modified by the behavior.
  */
 public ViewerBehavior(TransformGroup transformGroup) {
   super();
   subjectTransformGroup = transformGroup;
 }
 /**
  * Construct a viewer behavior that listens to mouse movement and button
  * presses to generate rotation and translation transforms written into the
  * given transform group.
  * 
  * @param transformGroup
  *            The transform group to be modified by the behavior.
  * @param parent
  *            The AWT Component that contains the area generating mouse
  *            events.
  */
 public ViewerBehavior(TransformGroup transformGroup, Component parent) {
   super();
   subjectTransformGroup = transformGroup;
   parentComponent = parent;
 }
 /**
  * Set the transform group modified by the viewer behavior. Setting the
  * transform group to null disables the behavior until the transform group
  * is again set to an existing group.
  * 
  * @param transformGroup
  *            The new transform group to be modified by the behavior.
  */
 public void setTransformGroup(TransformGroup transformGroup) {
   subjectTransformGroup = transformGroup;
 }
 /**
  * Get the transform group modified by the viewer behavior.
  */
 public TransformGroup getTransformGroup() {
   return subjectTransformGroup;
 }
 /**
  * Sets the parent component who"s cursor will be changed during mouse
  * drags. If no component is given is given to the constructor, or set via
  * this method, no cursor changes will be done.
  * 
  * @param parent
  *            the AWT Component, such as a Frame, within which cursor
  *            changes should take place during mouse drags
  */
 public void setParentComponent(Component parent) {
   parentComponent = parent;
 }
 /*
  * Gets the parent frame within which the cursor changes during mouse drags.
  * 
  * @return the AWT Component, such as a Frame, within which cursor changes
  * should take place during mouse drags. Returns null if no parent is set.
  */
 public Component getParentComponent() {
   return parentComponent;
 }
 /**
  * Initialize the behavior.
  */
 public void initialize() {
   // Wakeup when the mouse is dragged or when a mouse button
   // is pressed or released.
   mouseEvents = new WakeupCriterion[3];
   mouseEvents[0] = new WakeupOnAWTEvent(MouseEvent.MOUSE_DRAGGED);
   mouseEvents[1] = new WakeupOnAWTEvent(MouseEvent.MOUSE_PRESSED);
   mouseEvents[2] = new WakeupOnAWTEvent(MouseEvent.MOUSE_RELEASED);
   mouseCriterion = new WakeupOr(mouseEvents);
   wakeupOn(mouseCriterion);
 }
 /**
  * Process a new wakeup. Interpret mouse button presses, releases, and mouse
  * drags.
  * 
  * @param criteria
  *            The wakeup criteria causing the behavior wakeup.
  */
 public void processStimulus(Enumeration criteria) {
   WakeupCriterion wakeup = null;
   AWTEvent[] event = null;
   int whichButton = BUTTONNONE;
   // Process all pending wakeups
   while (criteria.hasMoreElements()) {
     wakeup = (WakeupCriterion) criteria.nextElement();
     if (wakeup instanceof WakeupOnAWTEvent) {
       event = ((WakeupOnAWTEvent) wakeup).getAWTEvent();
       // Process all pending events
       for (int i = 0; i < event.length; i++) {
         if (event[i].getID() != MouseEvent.MOUSE_PRESSED
             && event[i].getID() != MouseEvent.MOUSE_RELEASED
             && event[i].getID() != MouseEvent.MOUSE_DRAGGED)
           // Ignore uninteresting mouse events
           continue;
         //
         // Regretably, Java event handling (or perhaps
         // underlying OS event handling) doesn"t always
         // catch button bounces (redundant presses and
         // releases), or order events so that the last
         // drag event is delivered before a release.
         // This means we can get stray events that we
         // filter out here.
         //
         if (event[i].getID() == MouseEvent.MOUSE_PRESSED
             && buttonPressed != BUTTONNONE)
           // Ignore additional button presses until a release
           continue;
         if (event[i].getID() == MouseEvent.MOUSE_RELEASED
             && buttonPressed == BUTTONNONE)
           // Ignore additional button releases until a press
           continue;
         if (event[i].getID() == MouseEvent.MOUSE_DRAGGED
             && buttonPressed == BUTTONNONE)
           // Ignore drags until a press
           continue;
         MouseEvent mev = (MouseEvent) event[i];
         int modifiers = mev.getModifiers();
         //
         // Unfortunately, the underlying event handling
         // doesn"t do a "grab" operation when a mouse button
         // is pressed. This means that once a button is
         // pressed, if another mouse button or a keyboard
         // modifier key is pressed, the delivered mouse event
         // will show that a different button is being held
         // down. For instance:
         //
         // Action Event
         //  Button 1 press Button 1 press
         //  Drag with button 1 down Button 1 drag
         //  ALT press -
         //  Drag with ALT & button 1 down Button 2 drag
         //  Button 1 release Button 2 release
         //
         // The upshot is that we can get a button press
         // without a matching release, and the button
         // associated with a drag can change mid-drag.
         //
         // To fix this, we watch for an initial button
         // press, and thenceforth consider that button
         // to be the one held down, even if additional
         // buttons get pressed, and despite what is
         // reported in the event. Only when a button is
         // released, do we end such a grab.
         //
         if (buttonPressed == BUTTONNONE) {
           // No button is pressed yet, figure out which
           // button is down now and how to direct events
           if (((modifiers & InputEvent.BUTTON3_MASK) != 0)
               || (((modifiers & InputEvent.BUTTON1_MASK) != 0) && ((modifiers & InputEvent.CTRL_MASK) == InputEvent.CTRL_MASK))) {
             // Button 3 activity (META or CTRL down)
             whichButton = BUTTON3;
           } else if ((modifiers & InputEvent.BUTTON2_MASK) != 0) {
             // Button 2 activity (ALT down)
             whichButton = BUTTON2;
           } else {
             // Button 1 activity (no modifiers down)
             whichButton = BUTTON1;
           }
           // If the event is to press a button, then
           // record that that button is now down
           if (event[i].getID() == MouseEvent.MOUSE_PRESSED)
             buttonPressed = whichButton;
         } else {
           // Otherwise a button was pressed earlier and
           // hasn"t been released yet. Assign all further
           // events to it, even if ALT, META, CTRL, or
           // another button has been pressed as well.
           whichButton = buttonPressed;
         }
         // Distribute the event
         switch (whichButton) {
         case BUTTON1:
           onButton1(mev);
           break;
         case BUTTON2:
           onButton2(mev);
           break;
         case BUTTON3:
           onButton3(mev);
           break;
         default:
           break;
         }
         // If the event is to release a button, then
         // record that that button is now up
         if (event[i].getID() == MouseEvent.MOUSE_RELEASED)
           buttonPressed = BUTTONNONE;
       }
       continue;
     }
     if (wakeup instanceof WakeupOnElapsedFrames) {
       onElapsedFrames((WakeupOnElapsedFrames) wakeup);
       continue;
     }
   }
   // Reschedule us for another wakeup
   wakeupOn(mouseCriterion);
 }
 /**
  * Default X and Y rotation factors, and XYZ translation factors.
  */
 public static final double DEFAULT_XROTATION_FACTOR = 0.02;
 public static final double DEFAULT_YROTATION_FACTOR = 0.005;
 public static final double DEFAULT_XTRANSLATION_FACTOR = 0.02;
 public static final double DEFAULT_YTRANSLATION_FACTOR = 0.02;
 public static final double DEFAULT_ZTRANSLATION_FACTOR = 0.04;
 protected double XRotationFactor = DEFAULT_XROTATION_FACTOR;
 protected double YRotationFactor = DEFAULT_YROTATION_FACTOR;
 protected double XTranslationFactor = DEFAULT_XTRANSLATION_FACTOR;
 protected double YTranslationFactor = DEFAULT_YTRANSLATION_FACTOR;
 protected double ZTranslationFactor = DEFAULT_ZTRANSLATION_FACTOR;
 /**
  * Set the X rotation scaling factor for X-axis rotations.
  * 
  * @param factor
  *            The new scaling factor.
  */
 public void setXRotationFactor(double factor) {
   XRotationFactor = factor;
 }
 /**
  * Get the current X rotation scaling factor for X-axis rotations.
  */
 public double getXRotationFactor() {
   return XRotationFactor;
 }
 /**
  * Set the Y rotation scaling factor for Y-axis rotations.
  * 
  * @param factor
  *            The new scaling factor.
  */
 public void setYRotationFactor(double factor) {
   YRotationFactor = factor;
 }
 /**
  * Get the current Y rotation scaling factor for Y-axis rotations.
  */
 public double getYRotationFactor() {
   return YRotationFactor;
 }
 /**
  * Set the X translation scaling factor for X-axis translations.
  * 
  * @param factor
  *            The new scaling factor.
  */
 public void setXTranslationFactor(double factor) {
   XTranslationFactor = factor;
 }
 /**
  * Get the current X translation scaling factor for X-axis translations.
  */
 public double getXTranslationFactor() {
   return XTranslationFactor;
 }
 /**
  * Set the Y translation scaling factor for Y-axis translations.
  * 
  * @param factor
  *            The new scaling factor.
  */
 public void setYTranslationFactor(double factor) {
   YTranslationFactor = factor;
 }
 /**
  * Get the current Y translation scaling factor for Y-axis translations.
  */
 public double getYTranslationFactor() {
   return YTranslationFactor;
 }
 /**
  * Set the Z translation scaling factor for Z-axis translations.
  * 
  * @param factor
  *            The new scaling factor.
  */
 public void setZTranslationFactor(double factor) {
   ZTranslationFactor = factor;
 }
 /**
  * Get the current Z translation scaling factor for Z-axis translations.
  */
 public double getZTranslationFactor() {
   return ZTranslationFactor;
 }
 /**
  * Respond to a button1 event (press, release, or drag).
  * 
  * @param mouseEvent
  *            A MouseEvent to respond to.
  */
 public abstract void onButton1(MouseEvent mouseEvent);
 /**
  * Respond to a button2 event (press, release, or drag).
  * 
  * @param mouseEvent
  *            A MouseEvent to respond to.
  */
 public abstract void onButton2(MouseEvent mouseEvent);
 /**
  * Responed to a button3 event (press, release, or drag).
  * 
  * @param mouseEvent
  *            A MouseEvent to respond to.
  */
 public abstract void onButton3(MouseEvent mouseEvent);
 /**
  * Respond to an elapsed frames event (assuming subclass has set up a wakeup
  * criterion for it).
  * 
  * @param time
  *            A WakeupOnElapsedFrames criterion to respond to.
  */
 public abstract void onElapsedFrames(WakeupOnElapsedFrames timeEvent);

} // //CLASS //NameValue - create a handy name-value pair // //DESCRIPTION //It is frequently handy to have one or more name-value pairs //with which to store named colors, named positions, named textures, //and so forth. Several of the examples use this class. // //AUTHOR //David R. Nadeau / San Diego Supercomputer Center // class NameValue {

 public String name;
 public Object value;
 public NameValue(String n, Object v) {
   name = n;
   value = v;
 }

}


      </source>
   
  
 
  



Light Bug

   <source lang="java">

/*

* @(#)LightBug.java 1.2 01/08/01 11:02:18
* 
* ************************************************************** "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.GraphicsConfiguration; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.text.NumberFormat; import javax.media.j3d.Appearance; import javax.media.j3d.BoundingSphere; import javax.media.j3d.BranchGroup; import javax.media.j3d.Canvas3D; import javax.media.j3d.DirectionalLight; import javax.media.j3d.Link; import javax.media.j3d.Material; import javax.media.j3d.PointLight; import javax.media.j3d.SharedGroup; import javax.media.j3d.SpotLight; import javax.media.j3d.Switch; import javax.media.j3d.Transform3D; import javax.media.j3d.TransformGroup; import javax.swing.ButtonGroup; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JRadioButton; import javax.vecmath.AxisAngle4f; import javax.vecmath.Color3f; import javax.vecmath.Point3d; import javax.vecmath.Point3f; import javax.vecmath.Vector3f; import com.sun.j3d.utils.applet.MainFrame; import com.sun.j3d.utils.behaviors.vp.OrbitBehavior; import com.sun.j3d.utils.geometry.Sphere; import com.sun.j3d.utils.universe.SimpleUniverse; import com.sun.j3d.utils.universe.ViewingPlatform; public class LightBug extends Applet implements ActionListener {

 // Scene graph items
 SimpleUniverse u;
 Switch spheresSwitch;
 Switch lightSwitch;
 // Light items
 DirectionalLight lightDirectional;
 PointLight lightPoint;
 SpotLight lightSpot;
 String lightNoneString = "None";
 String lightDirectionalString = "Directional";
 String lightPointString = "Point";
 String lightSpotString = "Spot";
 JRadioButton lightNoneButton;
 JRadioButton lightDirectionalButton;
 JRadioButton lightPointButton;
 JRadioButton lightSpotButton;
 static final int LIGHT_NONE = Switch.CHILD_NONE;
 static final int LIGHT_DIRECTIONAL = 0;
 static final int LIGHT_POINT = 1;
 static final int LIGHT_SPOT = 2;
 // Temporaries that are reused
 Transform3D tmpTrans = new Transform3D();
 Vector3f tmpVector = new Vector3f();
 AxisAngle4f tmpAxisAngle = new AxisAngle4f();
 // colors
 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);
 Color3f grey = new Color3f(0.1f, 0.1f, 0.1f);
 Color3f skyBlue = new Color3f(0.6f, 0.7f, 0.9f);
 // 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;
 // sets up a grid of spheres
 void setupSpheres() {
   // create a Switch for the spheres, allow switch changes
   spheresSwitch = new Switch(Switch.CHILD_ALL);
   spheresSwitch.setCapability(Switch.ALLOW_SWITCH_WRITE);
   // Set up an appearance to make the Sphere 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);
   // create a sphere and put it into a shared group
   Sphere sphere = new Sphere(0.5f, appearance);
   SharedGroup sphereSG = new SharedGroup();
   sphereSG.addChild(sphere);
   // create a grid of spheres in the z=0 plane
   // each has a TransformGroup to position the sphere which contains
   // a link to the shared group for the sphere
   for (int y = -2; y <= 2; y++) {
     for (int x = -2; x <= 2; x++) {
       TransformGroup tg = new TransformGroup();
       tmpVector.set(x * 1.2f, y * 1.2f, 0.0f);
       tmpTrans.set(tmpVector);
       tg.setTransform(tmpTrans);
       tg.addChild(new Link(sphereSG));
       spheresSwitch.addChild(tg);
     }
   }
 }
 /*
  * Set up the lights. This is a group which contains the ambient light and a
  * switch for the other lights. LIGHT_DIRECTIONAL : white light pointing
  * along Z axis LIGHT_POINT : white light near upper left corner of spheres
  * LIGHT_SPOT : white light near upper left corner of spheres, pointing
  * towards center.
  */
 void setupLights() {
   // set up the BoundingSphere for all the lights
   BoundingSphere bounds = new BoundingSphere(new Point3d(), 100.0);
   // create the switch and set the default
   lightSwitch = new Switch();
   lightSwitch.setCapability(Switch.ALLOW_SWITCH_WRITE);
   lightSwitch.setWhichChild(LIGHT_DIRECTIONAL);
   // Set up the directional light
   Vector3f lightDirection = new Vector3f(0.0f, 0.0f, -1.0f);
   lightDirectional = new DirectionalLight(white, lightDirection);
   lightDirectional.setInfluencingBounds(bounds);
   lightSwitch.addChild(lightDirectional);
   // Set up the point light
   Point3f lightPosition = new Point3f(-1.0f, 1.0f, 0.6f);
   Point3f lightAttenuation = new Point3f(0.0f, 1.0f, 0.0f);
   lightPoint = new PointLight(white, lightPosition, lightAttenuation);
   lightPoint.setInfluencingBounds(bounds);
   lightSwitch.addChild(lightPoint);
   // Set up the spot light
   // Point the light back at the origin
   Vector3f lightSpotDirection = new Vector3f(lightPosition);
   lightSpotDirection.negate(); // point back
   lightSpotDirection.normalize(); // make unit length
   float spreadAngle = 60; // degrees
   float concentration = 5.0f;
   lightSpot = new SpotLight(white, lightPosition, lightAttenuation,
       lightSpotDirection, (float) Math.toRadians(spreadAngle),
       concentration);
   lightSpot.setInfluencingBounds(bounds);
   lightSwitch.addChild(lightSpot);
 }
 public void actionPerformed(ActionEvent e) {
   String action = e.getActionCommand();
   Object source = e.getSource();
   if (action == lightNoneString) {
     System.out.println("light_none");
     lightSwitch.setWhichChild(LIGHT_NONE);
   } else if (action == lightDirectionalString) {
     System.out.println("light_directional");
     lightSwitch.setWhichChild(LIGHT_DIRECTIONAL);
   } else if (action == lightPointString) {
     System.out.println("light_point");
     lightSwitch.setWhichChild(LIGHT_POINT);
   } else if (action == lightSpotString) {
     System.out.println("light_spot");
     lightSwitch.setWhichChild(LIGHT_SPOT);
   }
 }
 BranchGroup createSceneGraph() {
   // Create the root of the branch graph
   BranchGroup objRoot = new BranchGroup();
   // Add the primitives to the scene
   setupSpheres();
   objRoot.addChild(spheresSwitch);
   setupLights();
   objRoot.addChild(lightSwitch);
   return objRoot;
 }
 public LightBug() {
   setLayout(new BorderLayout());
   GraphicsConfiguration config = SimpleUniverse
       .getPreferredConfiguration();
   Canvas3D c = new Canvas3D(config);
   add("Center", c);
   // Create a simple scene and attach it to the virtual universe
   BranchGroup scene = createSceneGraph();
   u = new SimpleUniverse(c);
   // Get the viewing platform
   ViewingPlatform viewingPlatform = u.getViewingPlatform();
   // Move the viewing platform back to enclose the -4 -> 4 range
   double viewRadius = 4.0; // want to be able to see circle
   // of viewRadius size around origin
   // get the field of view
   double fov = u.getViewer().getView().getFieldOfView();
   // calc view distance to make circle view in fov
   float viewDistance = (float) (viewRadius / Math.tan(fov / 2.0));
   tmpVector.set(0.0f, 0.0f, viewDistance);// setup offset
   tmpTrans.set(tmpVector); // set trans to translate
   // move the view platform
   viewingPlatform.getViewPlatformTransform().setTransform(tmpTrans);
   // add an orbit behavior to move the viewing platform
   OrbitBehavior orbit = new OrbitBehavior(c, OrbitBehavior.STOP_ZOOM);
   orbit.setMinRadius(0.5);
   BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0),
       100.0);
   orbit.setSchedulingBounds(bounds);
   viewingPlatform.setViewPlatformBehavior(orbit);
   u.addBranchGraph(scene);
   add("South", lightPanel());
 }
 JPanel lightPanel() {
   JPanel panel = new JPanel();
   panel.setLayout(new GridLayout(0, 1));
   panel.add(new JLabel("Light type"));
   // create the buttons
   JRadioButton lightNoneButton = new JRadioButton(lightNoneString);
   JRadioButton lightDirectionalButton = new JRadioButton(
       lightDirectionalString);
   JRadioButton lightPointButton = new JRadioButton(lightPointString);
   JRadioButton lightSpotButton = new JRadioButton(lightSpotString);
   // set up the action commands
   lightNoneButton.setActionCommand(lightNoneString);
   lightDirectionalButton.setActionCommand(lightDirectionalString);
   lightPointButton.setActionCommand(lightPointString);
   lightSpotButton.setActionCommand(lightSpotString);
   // add the buttons to a group so that only one can be selected
   ButtonGroup buttonGroup = new ButtonGroup();
   buttonGroup.add(lightNoneButton);
   buttonGroup.add(lightDirectionalButton);
   buttonGroup.add(lightPointButton);
   buttonGroup.add(lightSpotButton);
   // register the applet as the listener for the buttons
   lightNoneButton.addActionListener(this);
   lightDirectionalButton.addActionListener(this);
   lightPointButton.addActionListener(this);
   lightSpotButton.addActionListener(this);
   // add the buttons to the panel
   panel.add(lightNoneButton);
   panel.add(lightDirectionalButton);
   panel.add(lightPointButton);
   panel.add(lightSpotButton);
   // set the default
   lightDirectionalButton.setSelected(true);
   return panel;
 }
 public void destroy() {
   u.removeAllLocales();
 }
 // The following allows LightBug to be run as an application
 // as well as an applet
 //
 public static void main(String[] args) {
   new MainFrame(new LightBug(), 600, 800);
 }

}


      </source>
   
  
 
  



Light Demo

   <source lang="java">

// From: http://www.micg.et.fh-stralsund.de/Java3D/java3D.htm#Bild1

import java.applet.Applet; import java.awt.BorderLayout; import java.awt.Frame; import java.awt.GraphicsConfiguration; import javax.media.j3d.AmbientLight; import javax.media.j3d.Appearance; import javax.media.j3d.BoundingSphere; import javax.media.j3d.BranchGroup; import javax.media.j3d.Canvas3D; import javax.media.j3d.DirectionalLight; 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.Vector3f; import com.sun.j3d.utils.applet.MainFrame; import com.sun.j3d.utils.geometry.Sphere; import com.sun.j3d.utils.universe.SimpleUniverse; public class Licht extends Applet {

 /**
  * init Methoden fur die Darstellung als Applet
  */
 public void init() {
   setLayout(new BorderLayout());
   GraphicsConfiguration config = SimpleUniverse
       .getPreferredConfiguration();
   canvas3D = new Canvas3D(config);
   add("Center", canvas3D);
   BranchGroup szene = macheSzene();
   szene.rupile();
   universe = new SimpleUniverse(canvas3D);
   universe.getViewingPlatform().setNominalViewingTransform();
   universe.addBranchGraph(szene);
 }
 /**
  * Erstellt den Szenegraphen
  * 
  * @return BranchGroup
  */
 public BranchGroup macheSzene() {
   BranchGroup objWurzel = new BranchGroup();
   // Transformation, 2 Rotationen:
   Transform3D drehung = new Transform3D();
   Transform3D drehung2 = new Transform3D();
   drehung.rotX(Math.PI / 4.0d);
   drehung2.rotY(Math.PI / 5.0d);
   drehung.mul(drehung2);
   TransformGroup objDreh = new TransformGroup(drehung);
   Sphere kugel = new Sphere(0.5f, Sphere.GENERATE_NORMALS, 50,
       makeAppearance());
   objWurzel.addChild(kugel);
   objWurzel.addChild(objDreh);
   //directes Licht
   DirectionalLight d_Licht = new DirectionalLight();
   d_Licht.setInfluencingBounds(new BoundingSphere(new Point3d(0.0d, 0.0d,
       0.0d), Double.MAX_VALUE));
   d_Licht.setColor(new Color3f(1.0f, 0.0f, 0.0f));
   Vector3f dir = new Vector3f(1.0f, 2.0f, -1.0f);
   dir.normalize();
   d_Licht.setDirection(dir);
   objWurzel.addChild(d_Licht);
   // ambient Licht
   AmbientLight a_licht = new AmbientLight();
   a_licht.setInfluencingBounds(new BoundingSphere(new Point3d(0.0f, 0.0f,
       0.0f), Double.MAX_VALUE));
   a_licht.setColor(new Color3f(1.0f, 0.0f, 0.0f));
   objWurzel.addChild(a_licht);
   return objWurzel;
 }
 /**
  * Wurfeldarstellung
  * 
  * @return Appearance
  */
 private Appearance makeAppearance() {
   Appearance a = new Appearance();
   Material mat = new Material();
   mat.setShininess(50.0f);
   mat.setDiffuseColor(new Color3f(1.0f, 0.0f, 0.0f));
   mat.setSpecularColor(new Color3f(0.0f, 0.0f, 0.0f));
   a.setMaterial(mat);
   return a;
 }
 /**
  * gibt speicher frei
  */
 public void destroy() {
   universe.removeAllLocales();
 }
 public static void main(String[] args) {
   frame = new MainFrame(new Licht(), 500, 500);
   frame.setTitle("Licht");
 }
 //---- Attribute -----------------------
 private SimpleUniverse universe;
 private Canvas3D canvas3D;
 private static Frame frame;

}

      </source>
   
  
 
  



Lighting Plane

   <source lang="java">

/*

*      LightsNPlanesApp.java 1.0 99/04/12
*
* Copyright (c) 1999 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.
*/

/*

*
* This application (or a version of it) generated one or more
* of the images in Chapter 6 of Getting Started with the Java 3D API.
* The Java 3D Turtorial.
*
* See http://www.sun.ru/desktop/java3d/collateral for more information.
*
*/

import java.applet.Applet; import java.awt.BorderLayout; import javax.media.j3d.AmbientLight; import javax.media.j3d.Appearance; import javax.media.j3d.Background; import javax.media.j3d.BoundingBox; import javax.media.j3d.BoundingSphere; import javax.media.j3d.BranchGroup; import javax.media.j3d.Canvas3D; import javax.media.j3d.DirectionalLight; import javax.media.j3d.IndexedQuadArray; import javax.media.j3d.Light; import javax.media.j3d.Material; import javax.media.j3d.Node; import javax.media.j3d.PointLight; import javax.media.j3d.QuadArray; import javax.media.j3d.Shape3D; import javax.media.j3d.SpotLight; 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.Vector3f; import com.sun.j3d.utils.applet.MainFrame; import com.sun.j3d.utils.universe.SimpleUniverse; /**

* LightsNPlanesApp creates
*/

public class LightsNPlanesApp extends Applet {

 TransformGroup createTG(float x, float y, float z) {
   Vector3f position = new Vector3f(x, y, z);
   Transform3D translate = new Transform3D();
   translate.set(position);
   TransformGroup trans1 = new TransformGroup(translate);
   return trans1;
 }
 Appearance createMatAppear(Color3f dColor, Color3f sColor, float shine) {
   Appearance appear = new Appearance();
   Material material = new Material();
   material.setDiffuseColor(dColor);
   material.setSpecularColor(sColor);
   material.setShininess(shine);
   appear.setMaterial(material);
   return appear;
 }
 public LightsNPlanesApp() {
   setLayout(new BorderLayout());
   Canvas3D c = new Canvas3D(null);
   add("Center", c);
   BoundingSphere bounds = new BoundingSphere(new Point3d(), 0.1);
   Vector3f direction = new Vector3f(0.0f, -1.0f, 0.0f);
   Point3f position = new Point3f(0.0f, 0.5f, -0.3f);
   Color3f white = new Color3f(1.0f, 1.0f, 1.0f);
   Color3f red = new Color3f(1.0f, 0.0f, 0.0f);
   Color3f green = new Color3f(0.0f, 1.0f, 0.0f);
   Color3f blue = new Color3f(0.0f, 0.0f, 1.0f);
   BranchGroup scene = new BranchGroup();
   IndexedQuadArray qa = new IndexedQuadArray(9, QuadArray.COORDINATES
       | QuadArray.NORMALS, 16);
   qa.setCoordinate(0, new Point3f(-0.3f, 0.3f, -0.3f));
   qa.setCoordinate(1, new Point3f(0.0f, 0.3f, -0.3f));
   qa.setCoordinate(2, new Point3f(0.3f, 0.3f, -0.3f));
   qa.setCoordinate(3, new Point3f(-0.3f, 0.0f, 0.0f));
   qa.setCoordinate(4, new Point3f(0.0f, 0.0f, 0.0f));
   qa.setCoordinate(5, new Point3f(0.3f, 0.0f, 0.0f));
   qa.setCoordinate(6, new Point3f(-0.3f, -0.3f, 0.3f));
   qa.setCoordinate(7, new Point3f(0.0f, -0.3f, 0.3f));
   qa.setCoordinate(8, new Point3f(0.3f, -0.3f, 0.3f));
   Vector3f n = new Vector3f(0.0f, 0.6f, 0.8f);
   n.normalize();
   qa.setNormal(0, n);
   qa.setNormal(1, n);
   qa.setNormal(2, n);
   qa.setNormal(3, n);
   qa.setNormal(4, n);
   qa.setNormal(5, n);
   qa.setNormal(6, n);
   qa.setNormal(7, n);
   qa.setNormal(8, n);
   qa.setCoordinateIndex(0, 0);
   qa.setCoordinateIndex(1, 3);
   qa.setCoordinateIndex(2, 4);
   qa.setCoordinateIndex(3, 1);
   qa.setCoordinateIndex(4, 1);
   qa.setCoordinateIndex(5, 4);
   qa.setCoordinateIndex(6, 5);
   qa.setCoordinateIndex(7, 2);
   qa.setCoordinateIndex(8, 3);
   qa.setCoordinateIndex(9, 6);
   qa.setCoordinateIndex(10, 7);
   qa.setCoordinateIndex(11, 4);
   qa.setCoordinateIndex(12, 4);
   qa.setCoordinateIndex(13, 7);
   qa.setCoordinateIndex(14, 8);
   qa.setCoordinateIndex(15, 5);
   TransformGroup trans1 = createTG(-0.7f, 0.0f, -0.5f);
   scene.addChild(trans1);
   TransformGroup trans2 = createTG(0.0f, 0.0f, -0.5f);
   scene.addChild(trans2);
   TransformGroup trans3 = createTG(0.7f, 0.0f, -0.5f);
   scene.addChild(trans3);
   Appearance qAppear = createMatAppear(white, white, 5.0f);
   Shape3D p1 = new Shape3D(qa, qAppear);
   //    p1.setBoundsAutoCompute(false);
   p1.setBounds(bounds);
   p1.setCapability(Node.ALLOW_BOUNDS_READ);
   trans1.addChild(p1);
   Shape3D p2 = new Shape3D(qa, qAppear);
   p2.setBounds(bounds);
   p2.setCapability(Node.ALLOW_BOUNDS_READ);
   trans2.addChild(p2);
   Shape3D p3 = new Shape3D(qa, qAppear);
   p3.setBounds(bounds);
   p3.setCapability(Node.ALLOW_BOUNDS_READ);
   trans3.addChild(p3);
   AmbientLight lightA = new AmbientLight();
   lightA.setInfluencingBounds(new BoundingSphere());
   lightA.setCapability(Light.ALLOW_INFLUENCING_BOUNDS_READ);
   scene.addChild(lightA);
   DirectionalLight lightD = new DirectionalLight();
   lightD.setInfluencingBounds(bounds);
   lightD.setBoundsAutoCompute(false);
   lightD.setCapability(Light.ALLOW_INFLUENCING_BOUNDS_READ);
   lightD.setDirection(direction);
   lightD.setColor(red);
   trans1.addChild(lightD);
   PointLight lightP = new PointLight();
   lightP.setInfluencingBounds(bounds);
   lightP.setCapability(Light.ALLOW_INFLUENCING_BOUNDS_READ);
   lightP.setPosition(position);
   lightP.setColor(green);
   trans2.addChild(lightP);
   SpotLight lightS = new SpotLight();
   lightS.setInfluencingBounds(bounds);
   lightS.setCapability(Light.ALLOW_INFLUENCING_BOUNDS_READ);
   lightS.setPosition(position);
   lightS.setDirection(direction);
   lightS.setSpreadAngle(0.3f);
   lightS.setConcentration(1.0f);
   lightS.setColor(blue);
   trans3.addChild(lightS);
   Background background = new Background();
   background.setApplicationBounds(new BoundingSphere());
   background.setColor(1.0f, 1.0f, 1.0f);
   scene.addChild(background);
   scene.rupile();
   System.out.print("bounds object: ");
   System.out.println(bounds);
   System.out.print("influencing bounds for lightA: ");
   System.out.println(lightA.getInfluencingBounds());
   System.out.print("influencing bounds for lightD: ");
   System.out.println(lightD.getInfluencingBounds());
   System.out.print("influencing bounds for lightP: ");
   System.out.println(lightP.getInfluencingBounds());
   System.out.print("influencing bounds for lightS: ");
   System.out.println(lightS.getInfluencingBounds());
   System.out.print("bounds for plane1: ");
   System.out.println(p1.getBounds());
   System.out.print("bounds for plane2: ");
   System.out.println(p2.getBounds());
   System.out.print("bounds for plane3: ");
   System.out.println(p3.getBounds());
   BoundingSphere bs0 = new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 0.2);
   BoundingSphere bs1 = new BoundingSphere(new Point3d(-0.1, 0.0, 0.0),
       0.2);
   BoundingSphere bs2 = new BoundingSphere(new Point3d(-0.2, 0.0, 0.0),
       0.2);
   BoundingSphere bs3 = new BoundingSphere(new Point3d(-0.3, 0.0, 0.0),
       0.2);
   BoundingSphere bs4 = new BoundingSphere(new Point3d(-0.4, 0.0, 0.0),
       0.2);
   BoundingSphere bs5 = new BoundingSphere(new Point3d(-0.5, 0.0, 0.0),
       0.2);
   BoundingSphere bs6 = new BoundingSphere(new Point3d(-0.6, 0.0, 0.0),
       0.2);
   BoundingSphere bs7 = new BoundingSphere(new Point3d(-0.7, 0.0, 0.0),
       0.2);
   BoundingSphere bs8 = new BoundingSphere(new Point3d(-0.8, 0.0, 0.0),
       0.2);
   BoundingBox bb1 = new BoundingBox(bs1);
   BoundingBox bb2 = new BoundingBox(bs2);
   BoundingBox bb3 = new BoundingBox(bs3);
   BoundingBox bb4 = new BoundingBox(bs4);
   BoundingBox bb5 = new BoundingBox(bs5);
   BoundingBox bb6 = new BoundingBox(bs6);
   BoundingBox bb7 = new BoundingBox(bs7);
   BoundingBox bb8 = new BoundingBox(bs8);
   if (bs0.intersect(bs1))
     System.out.println("bs0 intersects bs1");
   if (bs0.intersect(bs2))
     System.out.println("bs0 intersects bs2");
   if (bs0.intersect(bs3))
     System.out.println("bs0 intersects bs3");
   if (bs0.intersect(bs4))
     System.out.println("bs0 intersects bs4");
   if (bs0.intersect(bs5))
     System.out.println("bs0 intersects bs5");
   if (bs0.intersect(bs6))
     System.out.println("bs0 intersects bs6");
   if (bs0.intersect(bs7))
     System.out.println("bs0 intersects bs7");
   if (bs0.intersect(bs8))
     System.out.println("bs0 intersects bs8");
   if (bs0.intersect(bb1))
     System.out.println("bs0 intersects bb1");
   if (bs0.intersect(bb2))
     System.out.println("bs0 intersects bb2");
   if (bs0.intersect(bb3))
     System.out.println("bs0 intersects bb3");
   if (bs0.intersect(bb4))
     System.out.println("bs0 intersects bb4");
   if (bs0.intersect(bb5))
     System.out.println("bs0 intersects bb5");
   if (bs0.intersect(bb6))
     System.out.println("bs0 intersects bb6");
   if (bs0.intersect(bb7))
     System.out.println("bs0 intersects bb7");
   if (bs0.intersect(bb8))
     System.out.println("bs0 intersects bb8");
   SimpleUniverse u = new SimpleUniverse(c);
   // This will move the ViewPlatform back a bit so the
   // objects in the scene can be viewed.
   u.getViewingPlatform().setNominalViewingTransform();
   u.addBranchGraph(scene);
 }
 public static void main(String argv[]) {
   new MainFrame(new LightsNPlanesApp(), 256, 128);
 }

}


      </source>
   
  
 
  



LightScopeApp creates a scene that is paritally light

   <source lang="java">

/*

*      LightScopeApp.java 1.0 99/04/12
*
* Copyright (c) 1999 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.
*/

/*

* This application demonstrates the use of scoping for controlling
* the influence of lights.
*
* This applicaion is not necessarilly a demonstration of good
* program design or coding.
*
* This application (or a version of it) generated one or more
* of the images in Chapter 6 of Getting Started with the Java 3D API.
* The Java 3D Turtorial.
*
* See http://www.sun.ru/desktop/java3d/collateral for more information.
*
*/

import java.applet.Applet; import java.awt.BorderLayout; 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.GeometryArray; import javax.media.j3d.Material; import javax.media.j3d.PointLight; import javax.media.j3d.PolygonAttributes; import javax.media.j3d.QuadArray; import javax.media.j3d.Shape3D; import javax.media.j3d.Transform3D; import javax.media.j3d.TransformGroup; import javax.media.j3d.TriangleStripArray; import javax.vecmath.Color3f; import javax.vecmath.Point3f; import javax.vecmath.Vector3f; import com.sun.j3d.utils.applet.MainFrame; import com.sun.j3d.utils.geometry.Box; import com.sun.j3d.utils.universe.SimpleUniverse; /**

* LightScopeApp creates a scene that is paritally light by a light using the
* scoping feature.
*/

public class LightScopeApp extends Applet {

 Appearance createMaterialAppearance(Color3f color) {
   Appearance appear = new Appearance();
   Material material = new Material();
   material.setDiffuseColor(color);
   material.setShininess(50.0f);
   appear.setMaterial(material);
   return appear;
 }
 Shape3D createXZPlane(Point3f p0, Point3f p1, Point3f p2, Point3f p3) {
   Shape3D plane = new Shape3D();
   QuadArray planeGeom = new QuadArray(4, QuadArray.COORDINATES
       | QuadArray.NORMALS);
   planeGeom.setCoordinate(0, p0);
   planeGeom.setCoordinate(1, p1);
   planeGeom.setCoordinate(2, p2);
   planeGeom.setCoordinate(3, p3);
   Vector3f norm = new Vector3f(0.0f, 1.0f, 0.0f);
   planeGeom.setNormal(0, norm);
   planeGeom.setNormal(1, norm);
   planeGeom.setNormal(2, norm);
   planeGeom.setNormal(3, norm);
   plane.setGeometry(planeGeom);
   return plane;
 }
 Shape3D createLampShape() {
   Shape3D lamp = new Shape3D();
   int stripCounts[] = { 10, 10 };
   TriangleStripArray lampGeom = new TriangleStripArray(20,
       GeometryArray.COORDINATES | GeometryArray.NORMALS, stripCounts);
   lampGeom.setCoordinate(0, new Point3f(-0.01f, 0.9f, 0.01f));
   lampGeom.setCoordinate(1, new Point3f(-0.01f, 0.0f, 0.01f));
   lampGeom.setCoordinate(2, new Point3f(0.01f, 0.9f, 0.01f));
   lampGeom.setCoordinate(3, new Point3f(0.01f, 0.0f, 0.01f));
   lampGeom.setCoordinate(4, new Point3f(0.01f, 0.9f, -0.01f));
   lampGeom.setCoordinate(5, new Point3f(0.01f, 0.0f, -0.01f));
   lampGeom.setCoordinate(6, new Point3f(-0.01f, 0.9f, -0.01f));
   lampGeom.setCoordinate(7, new Point3f(-0.01f, 0.0f, -0.01f));
   lampGeom.setCoordinate(8, new Point3f(-0.01f, 0.9f, 0.01f));
   lampGeom.setCoordinate(9, new Point3f(-0.01f, 0.0f, 0.01f));
   lampGeom.setCoordinate(10, new Point3f(-0.1f, 0.9f, 0.1f));
   lampGeom.setCoordinate(11, new Point3f(-0.2f, 0.5f, 0.2f));
   lampGeom.setCoordinate(12, new Point3f(0.1f, 0.9f, 0.1f));
   lampGeom.setCoordinate(13, new Point3f(0.2f, 0.5f, 0.2f));
   lampGeom.setCoordinate(14, new Point3f(0.1f, 0.9f, -0.1f));
   lampGeom.setCoordinate(15, new Point3f(0.2f, 0.5f, -0.2f));
   lampGeom.setCoordinate(16, new Point3f(-0.1f, 0.9f, -0.1f));
   lampGeom.setCoordinate(17, new Point3f(-0.2f, 0.5f, -0.2f));
   lampGeom.setCoordinate(18, new Point3f(-0.1f, 0.9f, 0.1f));
   lampGeom.setCoordinate(19, new Point3f(-0.2f, 0.5f, 0.2f));
   Vector3f norm = new Vector3f(-0.7f, 0.0f, 0.7f);
   lampGeom.setNormal(0, norm);
   lampGeom.setNormal(1, norm);
   norm.set(0.7f, 0.0f, 0.7f);
   lampGeom.setNormal(2, norm);
   lampGeom.setNormal(3, norm);
   norm.set(0.7f, 0.0f, -0.7f);
   lampGeom.setNormal(4, norm);
   lampGeom.setNormal(5, norm);
   norm.set(-0.7f, 0.0f, -0.7f);
   lampGeom.setNormal(6, norm);
   lampGeom.setNormal(7, norm);
   norm.set(-0.7f, 0.0f, 0.7f);
   lampGeom.setNormal(8, norm);
   lampGeom.setNormal(9, norm);
   norm.set(-0.7f, 0.0f, 0.7f);
   lampGeom.setNormal(10, norm);
   lampGeom.setNormal(11, norm);
   norm.set(0.7f, 0.0f, 0.7f);
   lampGeom.setNormal(12, norm);
   lampGeom.setNormal(13, norm);
   norm.set(0.7f, 0.0f, -0.7f);
   lampGeom.setNormal(14, norm);
   lampGeom.setNormal(15, norm);
   norm.set(-0.7f, 0.0f, -0.7f);
   lampGeom.setNormal(16, norm);
   lampGeom.setNormal(17, norm);
   norm.set(-0.7f, 0.0f, 0.7f);
   lampGeom.setNormal(18, norm);
   lampGeom.setNormal(19, norm);
   lamp.setGeometry(lampGeom);
   return lamp;
 }
 BranchGroup createScene() {
   BranchGroup scene = new BranchGroup();
   TransformGroup tableTG = new TransformGroup();
   TransformGroup lampTG = new TransformGroup();
   TransformGroup litBoxTG = new TransformGroup();
   TransformGroup unLitBoxTG = new TransformGroup();
   scene.addChild(tableTG);
   tableTG.addChild(lampTG);
   tableTG.addChild(litBoxTG);
   tableTG.addChild(unLitBoxTG);
   Color3f white = new Color3f(1.0f, 1.0f, 1.0f);
   Color3f red = new Color3f(1.0f, 0.0f, 0.0f);
   Color3f blue = new Color3f(0.0f, 1.0f, 0.0f);
   Color3f green = new Color3f(0.0f, 0.0f, 1.0f);
   Color3f black = new Color3f(0.0f, 0.0f, 0.0f);
   Vector3f transVector = new Vector3f();
   Transform3D transTransform = new Transform3D();
   transVector.set(0.0f, -0.4f, 0.5f);
   transTransform.setTranslation(transVector);
   tableTG.setTransform(transTransform);
   transVector.set(-0.4f, 0.001f, 0.1f);
   transTransform.setTranslation(transVector);
   lampTG.setTransform(transTransform);
   transVector.set(-0.2f, 0.1f, 0.2f);
   transTransform.setTranslation(transVector);
   litBoxTG.setTransform(transTransform);
   transVector.set(0.3f, 0.1f, -0.4f);
   transTransform.setTranslation(transVector);
   unLitBoxTG.setTransform(transTransform);
   Shape3D tablePlane = createXZPlane(new Point3f(-1.0f, 0.0f, -1.0f),
       new Point3f(-1.0f, 0.0f, 1.0f), new Point3f(1.0f, 0.0f, 1.0f),
       new Point3f(1.0f, 0.0f, -1.0f));
   tablePlane.setAppearance(createMaterialAppearance(white));
   tableTG.addChild(tablePlane);
   litBoxTG.addChild(new Box(0.1f, 0.1f, 0.1f, Box.GENERATE_NORMALS,
       createMaterialAppearance(red)));
   Shape3D shadowPlane = createXZPlane(new Point3f(0.1f, -0.095f, -0.1f),
       new Point3f(0.1f, -0.095f, 0.1f), new Point3f(0.2f, -0.095f,
           0.15f), new Point3f(0.2f, -0.095f, -0.15f));
   shadowPlane.setAppearance(createMaterialAppearance(black));
   litBoxTG.addChild(shadowPlane);
   Appearance redGlowMat = createMaterialAppearance(red);
   //    redGlowMat.getMaterial().setEmissiveColor(0.5f, 0.5f, 0.5f);
   unLitBoxTG.addChild(new Box(0.1f, 0.1f, 0.1f, Box.GENERATE_NORMALS,
       redGlowMat));
   Shape3D lamp = createLampShape();
   Appearance lampAppearance = createMaterialAppearance(blue);
   PolygonAttributes polyAttrib = new PolygonAttributes();
   polyAttrib.setCullFace(PolygonAttributes.CULL_NONE);
   polyAttrib.setBackFaceNormalFlip(true);
   lampAppearance.setPolygonAttributes(polyAttrib);
   lamp.setAppearance(lampAppearance);
   lampTG.addChild(lamp);
   PointLight lampLight = new PointLight();
   lampLight.setPosition(0.1f, 0.5f, -0.1f);
   lampLight.setInfluencingBounds(new BoundingSphere());
   lampTG.addChild(lampLight);
   Shape3D litPlane = createXZPlane(new Point3f(-0.4f, 0.0f, -0.4f),
       new Point3f(-0.4f, 0.0f, 0.4f), new Point3f(0.4f, 0.0f, 0.4f),
       new Point3f(0.4f, 0.0f, -0.4f));
   litPlane.setAppearance(createMaterialAppearance(white));
   lampTG.addChild(litPlane);
   lampLight.addScope(lampTG);
   lampLight.addScope(litBoxTG);
   AmbientLight lightA = new AmbientLight();
   lightA.setInfluencingBounds(new BoundingSphere());
   scene.addChild(lightA);
   DirectionalLight lightD1 = new DirectionalLight();
   lightD1.setInfluencingBounds(new BoundingSphere());
   lightD1.setColor(new Color3f(0.4f, 0.4f, 0.4f));
   Vector3f lightDir = new Vector3f(-1.0f, -1.0f, -1.0f);
   lightDir.normalize();
   lightD1.setDirection(lightDir);
   scene.addChild(lightD1);
   DirectionalLight lightD2 = new DirectionalLight();
   lightD2.setInfluencingBounds(new BoundingSphere());
   lightD2.setColor(new Color3f(0.2f, 0.2f, 0.2f));
   lightDir.set(1.0f, -1.0f, -1.0f);
   lightDir.normalize();
   lightD2.setDirection(lightDir);
   scene.addChild(lightD2);
   Background bg = new Background();
   bg.setColor(1.0f, 1.0f, 1.0f);
   bg.setApplicationBounds(new BoundingSphere());
   scene.addChild(bg);
   return scene;
 }
 public LightScopeApp() {
   setLayout(new BorderLayout());
   Canvas3D c = new Canvas3D(null);
   add("Center", c);
   BranchGroup scene = createScene();
   scene.rupile();
   SimpleUniverse u = new SimpleUniverse(c);
   // This will move the ViewPlatform back a bit so the
   // objects in the scene can be viewed.
   u.getViewingPlatform().setNominalViewingTransform();
   u.addBranchGraph(scene);
 }
 public static void main(String argv[]) {
   new MainFrame(new LightScopeApp(), 256, 256);
 }

}


      </source>
   
  
 
  



Light Viewer

   <source lang="java">

/*

*  @(#)Viewer.java 1.14 02/10/21 13:44: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.BorderLayout; import java.awt.GraphicsConfiguration; import javax.media.j3d.BranchGroup; import javax.media.j3d.Canvas3D; import javax.media.j3d.Transform3D; import javax.media.j3d.TransformGroup; import javax.media.j3d.View; import javax.vecmath.Matrix4d; import com.sun.j3d.loaders.Loader; import com.sun.j3d.loaders.Scene; import com.sun.j3d.loaders.lw3d.Lw3dLoader; import com.sun.j3d.utils.applet.MainFrame; import com.sun.j3d.utils.universe.SimpleUniverse; /**

* This class loads in a Lightwave3D file and displays it in an applet window.
* The application is fairly basic; a more complete version of a Lightwave 3D
* loader might incorporate features such as settable clip plane distances and
* animated views (these are both possible with the current Lightwave 3D loader,
* they just need to be implemented in the application).
*/

public class Viewer3D extends Applet {

 private java.net.URL filename;
 private SimpleUniverse u;
 public Viewer3D(java.net.URL url) {
   filename = url;
 }
 public Viewer3D() {
 }
 public void init() {
   if (filename == null) {
     // the path to the file for an applet
     try {
       java.net.URL path = getCodeBase();
       filename = new java.net.URL(path.toString() + "./ballcone.lws");
     } catch (java.net.MalformedURLException ex) {
       System.err.println(ex.getMessage());
       ex.printStackTrace();
       System.exit(1);
     }
   }
   // Construct the Lw3d loader and load the file
   Loader lw3dLoader = new Lw3dLoader(Loader.LOAD_ALL);
   Scene loaderScene = null;
   try {
     loaderScene = lw3dLoader.load(filename);
   } catch (Exception e) {
     e.printStackTrace();
     System.exit(1);
   }
   // Construct the applet canvas
   setLayout(new BorderLayout());
   GraphicsConfiguration config = SimpleUniverse
       .getPreferredConfiguration();
   Canvas3D c = new Canvas3D(config);
   add("Center", c);
   // Create a basic universe setup and the root of our scene
   u = new SimpleUniverse(c);
   BranchGroup sceneRoot = new BranchGroup();
   // Change the back clip distance; the default is small for
   // some lw3d worlds
   View theView = u.getViewer().getView();
   theView.setBackClipDistance(50000f);
   // Now add the scene graph defined in the lw3d file
   if (loaderScene.getSceneGroup() != null) {
     // Instead of using the default view location (which may be
     // completely bogus for the particular file you"re loading),
     // let"s use the initial view from the file. We can get
     // this by getting the view groups from the scene (there"s
     // only one for Lightwave 3D), then using the inverse of the
     // transform on that view as the transform for the entire scene.
     // First, get the view groups (shouldn"t be null unless there
     // was something wrong in the load
     TransformGroup viewGroups[] = loaderScene.getViewGroups();
     // Get the Transform3D from the view and invert it
     Transform3D t = new Transform3D();
     viewGroups[0].getTransform(t);
     Matrix4d m = new Matrix4d();
     t.get(m);
     m.invert();
     t.set(m);
     // Now we"ve got the transform we want. Create an
     // appropriate TransformGroup and parent the scene to it.
     // Then insert the new group into the main BranchGroup.
     TransformGroup sceneTransform = new TransformGroup(t);
     sceneTransform.addChild(loaderScene.getSceneGroup());
     sceneRoot.addChild(sceneTransform);
   }
   // Make the scene graph live by inserting the root into the universe
   u.addBranchGraph(sceneRoot);
 }
 public void destroy() {
   u.cleanup();
 }
 /**
  * The main method of the application takes one argument in the args array;
  * the filname that you want to load. Note that the file must be reachable
  * from the directory in which you"re running this application.
  */
 public static void main(String args[]) {
   java.net.URL url = null;
   java.net.URL pathUrl = null;
   if (args.length > 0) {
     try {
       if ((args[0].indexOf("file:") == 0)
           || (args[0].indexOf("http") == 0)) {
         url = new java.net.URL(args[0]);
       } else if (args[0].charAt(0) != "/") {
         url = new java.net.URL("file:./" + args[0]);
       } else {
         url = new java.net.URL("file:" + args[0]);
       }
     } catch (java.net.MalformedURLException ex) {
       System.err.println(ex.getMessage());
       ex.printStackTrace();
       System.exit(1);
     }
   } else {
     // the path to the image for an application
     try {
       url = new java.net.URL("file:./ballcone.lws");
     } catch (java.net.MalformedURLException ex) {
       System.err.println(ex.getMessage());
       ex.printStackTrace();
       System.exit(1);
     }
   }
   new MainFrame(new Viewer3D(url), 500, 500);
 }

}


      </source>
   
  
 
  



Spot Light

   <source lang="java">

/*

*      SpotLightApp.java 1.0 99/04/12
*
* Copyright (c) 1999 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.
*/

/*

* This application (or a version of it) generated one or more
* of the images in Chapter 6 of Getting Started with the Java 3D API.
* The Java 3D Turtorial.
*
* See http://www.sun.ru/desktop/java3d/collateral for more information.
*
*/

import java.applet.Applet; import java.awt.BorderLayout; import javax.media.j3d.AmbientLight; import javax.media.j3d.Appearance; import javax.media.j3d.Background; import javax.media.j3d.BoundingSphere; import javax.media.j3d.Bounds; import javax.media.j3d.BranchGroup; import javax.media.j3d.Canvas3D; import javax.media.j3d.IndexedQuadArray; import javax.media.j3d.Material; import javax.media.j3d.QuadArray; import javax.media.j3d.Shape3D; import javax.media.j3d.SpotLight; 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.Vector3f; import com.sun.j3d.utils.applet.MainFrame; import com.sun.j3d.utils.universe.SimpleUniverse; /**

* SpotLightApp creates
*/

public class SpotLightApp extends Applet {

 Appearance createMatAppear(Color3f dColor, Color3f sColor, float shine) {
   Appearance appear = new Appearance();
   Material material = new Material();
   material.setDiffuseColor(dColor);
   material.setSpecularColor(sColor);
   material.setShininess(shine);
   appear.setMaterial(material);
   return appear;
 }
 SpotLight newSpotLight(Bounds bounds, Point3f pos, float spread,
     float concentration) {
   SpotLight sl = new SpotLight();
   sl.setInfluencingBounds(bounds);
   sl.setPosition(pos);
   sl.setSpreadAngle(spread);
   sl.setConcentration(concentration);
   return sl;
 }
 public SpotLightApp() {
   BoundingSphere bound1 = new BoundingSphere(new Point3d(0.0, 0.9, 0.0),
       0.5);
   BoundingSphere bound2 = new BoundingSphere(new Point3d(0.0, 0.0, 0.0),
       0.5);
   BoundingSphere bound3 = new BoundingSphere(new Point3d(0.0, -0.9, 0.0),
       0.5);
   Vector3f direction = new Vector3f(0.0f, 0.0f, -1.0f);
   Color3f white = new Color3f(1.0f, 1.0f, 1.0f);
   Color3f red = new Color3f(1.0f, 0.0f, 0.0f);
   Color3f green = new Color3f(0.0f, 1.0f, 0.0f);
   Color3f blue = new Color3f(0.0f, 0.0f, 1.0f);
   BranchGroup scene = new BranchGroup();
   final int X = 101, Y = 102;
   final float dx = 0.01f, dy = -0.01f;
   final float epx = dx / 2.0f, epy = dy / 2.0f;
   final float top = 0.5f, left = -0.5f;
   final float right = left + dx * (X - 1);
   final float bottom = top + dy * (Y - 1);
   IndexedQuadArray qa = new IndexedQuadArray(X * Y, QuadArray.COORDINATES
       | QuadArray.NORMALS, (X - 1) * (Y - 1) * 4);
   float x, y;
   int i = 0;
   System.out.print("set " + X * Y + " coordiantes.....  ");
   for (y = top; y >= bottom - epx; y += dy)
     for (x = left; x <= right + epx; x += dx)
       qa.setCoordinate(i++, new Point3f(x, y, 0.0f));
   System.out.println(i + " coordiantes done");
   int row, col;
   i = 0;
   Vector3f n = new Vector3f(0.0f, 0.0f, 1.0f);
   System.out.print("set " + (X - 1) * (Y - 1) * 4
       + " coordinate indices.....  ");
   for (row = 0; row < (Y - 1); row++) {
     for (col = 0; col < (X - 1); col++) {
       qa.setNormal(row * X + col, n);
       qa.setCoordinateIndex(i++, row * X + col);
       qa.setCoordinateIndex(i++, (row + 1) * X + col);
       qa.setCoordinateIndex(i++, (row + 1) * X + col + 1);
       qa.setCoordinateIndex(i++, row * X + col + 1);
     }
     qa.setNormal(row * X + col + 1, n);
   }
   System.out.println(i + " coordinate indices done");
   for (col = 0; col < (X - 1); col++) {
     qa.setNormal(X * (Y - 1) + 1 + col, n);
   }
   System.out.println("coordinate normals done");
   Appearance qAppear = createMatAppear(blue, white, 5.0f);
   Shape3D plane = new Shape3D(qa, qAppear);
   Transform3D translate = new Transform3D();
   translate.set(new Vector3f(-0.5f, 0.5f, 0.0f));
   TransformGroup tg1 = new TransformGroup(translate);
   scene.addChild(tg1);
   Shape3D plane1 = new Shape3D(qa, qAppear);
   plane1.setBounds(bound1);
   tg1.addChild(plane1);
   translate.set(new Vector3f(0.5f, 0.5f, 0.0f));
   TransformGroup tg2 = new TransformGroup(translate);
   scene.addChild(tg2);
   Shape3D plane2 = new Shape3D(qa, qAppear);
   plane2.setBounds(bound1);
   tg2.addChild(plane2);
   translate.set(new Vector3f(-0.5f, -0.5f, 0.0f));
   TransformGroup tg3 = new TransformGroup(translate);
   scene.addChild(tg3);
   Shape3D plane3 = new Shape3D(qa, qAppear);
   plane3.setBounds(bound3);
   tg3.addChild(plane3);
   translate.set(new Vector3f(0.5f, -0.5f, 0.0f));
   TransformGroup tg4 = new TransformGroup(translate);
   scene.addChild(tg4);
   Shape3D plane4 = new Shape3D(qa, qAppear);
   plane4.setBounds(bound3);
   tg4.addChild(plane4);
   AmbientLight lightA = new AmbientLight();
   lightA.setInfluencingBounds(new BoundingSphere());
   scene.addChild(lightA);
   scene.addChild(newSpotLight(bound1, new Point3f(-0.7f, 0.7f, 0.5f),
       0.1f, 5.0f));
   scene.addChild(newSpotLight(bound1, new Point3f(0.0f, 0.7f, 0.5f),
       0.1f, 50.0f));
   scene.addChild(newSpotLight(bound1, new Point3f(0.7f, 0.7f, 0.5f),
       0.1f, 100.0f));
   scene.addChild(newSpotLight(bound2, new Point3f(-0.7f, 0.0f, 0.5f),
       0.3f, 5.0f));
   scene.addChild(newSpotLight(bound2, new Point3f(0.0f, 0.0f, 0.5f),
       0.3f, 50.0f));
   scene.addChild(newSpotLight(bound2, new Point3f(0.7f, 0.0f, 0.5f),
       0.3f, 100.0f));
   scene.addChild(newSpotLight(bound3, new Point3f(-0.7f, -0.7f, 0.5f),
       0.5f, 5.0f));
   scene.addChild(newSpotLight(bound3, new Point3f(0.0f, -0.7f, 0.5f),
       0.5f, 50.0f));
   scene.addChild(newSpotLight(bound3, new Point3f(0.7f, -0.7f, 0.5f),
       0.5f, 100.0f));
   Background background = new Background();
   background.setApplicationBounds(new BoundingSphere());
   background.setColor(1.0f, 1.0f, 1.0f);
   scene.addChild(background);
   scene.rupile();
   setLayout(new BorderLayout());
   Canvas3D c = new Canvas3D(null);
   add("Center", c);
   SimpleUniverse u = new SimpleUniverse(c);
   // This will move the ViewPlatform back a bit so the
   // objects in the scene can be viewed.
   u.getViewingPlatform().setNominalViewingTransform();
   u.addBranchGraph(scene);
 }
 public static void main(String argv[]) {
   System.out.println("SpotLightApp");
   System.out
       .println("illustrates the differences between spot light paramters");
   System.out
       .println("depending on your system.... this may take a few minutes");
   new MainFrame(new SpotLightApp(), 256, 256);
 }

}


      </source>
   
  
 
  



This builds a red sphere using the Sphere utility class and adds lights

   <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.event.ActionEvent; import java.awt.event.ActionListener; import javax.media.j3d.AmbientLight; import javax.media.j3d.Appearance; import javax.media.j3d.BoundingSphere; import javax.media.j3d.BranchGroup; import javax.media.j3d.Canvas3D; import javax.media.j3d.Locale; import javax.media.j3d.Material; import javax.media.j3d.PhysicalBody; import javax.media.j3d.PhysicalEnvironment; import javax.media.j3d.SpotLight; 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.vecmath.AxisAngle4d; import javax.vecmath.Color3f; import javax.vecmath.Point3d; import javax.vecmath.Point3f; import javax.vecmath.Vector3f; import com.sun.j3d.utils.geometry.Sphere; /**

* This builds a red sphere using the Sphere utility class and adds lights so
* that you can see it shape. It creates a material for the sphere, creates an
* ambient light and a two spot lights of different colours.
* 
* @author I.J.Palmer
* @version 1.0
*/

public class SimpleSpotLights extends Frame implements ActionListener {

 protected Canvas3D myCanvas3D = new Canvas3D(null);
 protected Button myButton = new Button("Exit");
 /**
  * This function builds the view branch of the scene graph. It creates a
  * branch group and then creates the necessary view elements to give a
  * useful view of our content.
  * 
  * @param c
  *            Canvas3D that will display the view
  * @return BranchGroup that is the root of the view elements
  */
 protected BranchGroup buildViewBranch(Canvas3D c) {
   BranchGroup viewBranch = new BranchGroup();
   Transform3D viewXfm = new Transform3D();
   viewXfm.set(new Vector3f(0.0f, 0.0f, 10.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;
 }
 /**
  * This creates some lights and adds them to the BranchGroup.
  * 
  * @param b
  *            BranchGroup that the lights are added to.
  */
 protected void addLights(BranchGroup b) {
   // Create a bounds for the lights
   BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0),
       100.0);
   //Set up the ambient light
   Color3f ambientColour = new Color3f(0.2f, 0.2f, 0.2f);
   AmbientLight ambientLight = new AmbientLight(ambientColour);
   ambientLight.setInfluencingBounds(bounds);
   Color3f blueColour = new Color3f(0.0f, 0.0f, 1.0f);
   Point3f bluePosition = new Point3f(1.8f, 1.8f, 1.8f);
   Point3f blueAtten = new Point3f(1.0f, 0.0f, 0.0f);
   Vector3f blueDir = new Vector3f(-1.0f, -1.0f, -1.0f);
   SpotLight blueLight = new SpotLight(blueColour, bluePosition,
       blueAtten, blueDir, (float) (Math.PI / 2.0), 0.0f);
   blueLight.setInfluencingBounds(bounds);
   Color3f greenColour = new Color3f(0.0f, 1.0f, 0.0f);
   Point3f greenPosition = new Point3f(0.0f, 0.0f, 3.0f);
   Point3f greenAtten = new Point3f(1.0f, 0.0f, 0.0f);
   Vector3f greenDir = new Vector3f(0.0f, 0.0f, -1.0f);
   SpotLight greenLight = new SpotLight(greenColour, greenPosition,
       greenAtten, greenDir, (float) (Math.PI / 2.0), 0.0f);
   greenLight.setInfluencingBounds(bounds);
   //Add the lights to the BranchGroup
   b.addChild(ambientLight);
   b.addChild(greenLight);
   b.addChild(blueLight);
 }
 /**
  * This build the content branch of our scene graph. It creates a transform
  * group so that the shape is slightly tilted to reveal its 3D shape.
  * 
  * @param shape
  *            Node that represents the geometry for the content
  * @return BranchGroup that is the root of the content branch
  */
 protected BranchGroup buildContentBranch() {
   BranchGroup contentBranch = new BranchGroup();
   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);
   //Create a new appearance
   Appearance app = new Appearance();
   //Create the colours for the material
   Color3f ambientColour = new Color3f(1.0f, 1.0f, 1.0f);
   Color3f diffuseColour = new Color3f(1.0f, 1.0f, 1.0f);
   Color3f specularColour = new Color3f(1.0f, 1.0f, 1.0f);
   Color3f emissiveColour = new Color3f(0.0f, 0.0f, 0.0f);
   //Define the shininess
   float shininess = 20.0f;
   //Set the material of the appearance
   app.setMaterial(new Material(ambientColour, emissiveColour,
       diffuseColour, specularColour, shininess));
   //Create and add a new sphere using the appearance
   rotationGroup.addChild(new Sphere(2.0f, Sphere.GENERATE_NORMALS, 120,
       app));
   //Use the addLights function to add the lights to the branch
   addLights(contentBranch);
   //Return the root of the content branch
   return contentBranch;
 }
 /**
  * Handles the exit button action to quit the program.
  */
 public void actionPerformed(ActionEvent e) {
   dispose();
   System.exit(0);
 }
 /**
  * This creates a default universe and locale, creates a window and uses the
  * functions defined in this class to build the view and content branches of
  * the scene graph.
  */
 public SimpleSpotLights() {
   VirtualUniverse myUniverse = new VirtualUniverse();
   Locale myLocale = new Locale(myUniverse);
   myLocale.addBranchGraph(buildViewBranch(myCanvas3D));
   myLocale.addBranchGraph(buildContentBranch());
   setTitle("SimpleDirLight");
   setSize(400, 400);
   setLayout(new BorderLayout());
   add("Center", myCanvas3D);
   add("South", myButton);
   myButton.addActionListener(this);
   setVisible(true);
 }
 public static void main(String[] args) {
   SimpleSpotLights spl = new SimpleSpotLights();
 }

}

      </source>