Environment Explorer : 3D Environment « 3D « Java






Environment Explorer

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

import java.awt.AWTEvent;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.GraphicsConfiguration;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.text.NumberFormat;
import java.util.Enumeration;
import java.util.EventListener;
import java.util.EventObject;
import java.util.Hashtable;
import java.util.Vector;

import javax.media.j3d.AmbientLight;
import javax.media.j3d.Appearance;
import javax.media.j3d.Background;
import javax.media.j3d.BackgroundSound;
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.ExponentialFog;
import javax.media.j3d.Group;
import javax.media.j3d.ImageComponent;
import javax.media.j3d.ImageComponent2D;
import javax.media.j3d.Light;
import javax.media.j3d.LinearFog;
import javax.media.j3d.Link;
import javax.media.j3d.Material;
import javax.media.j3d.MediaContainer;
import javax.media.j3d.PointLight;
import javax.media.j3d.PointSound;
import javax.media.j3d.QuadArray;
import javax.media.j3d.Screen3D;
import javax.media.j3d.Shape3D;
import javax.media.j3d.SharedGroup;
import javax.media.j3d.Sound;
import javax.media.j3d.SpotLight;
import javax.media.j3d.Switch;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.media.j3d.View;
import javax.media.j3d.WakeupCondition;
import javax.media.j3d.WakeupCriterion;
import javax.media.j3d.WakeupOnAWTEvent;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JApplet;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.JTabbedPane;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.vecmath.AxisAngle4f;
import javax.vecmath.Color3f;
import javax.vecmath.Point2f;
import javax.vecmath.Point3d;
import javax.vecmath.Point3f;
import javax.vecmath.Vector3f;

import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGEncodeParam;
import com.sun.image.codec.jpeg.JPEGImageEncoder;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.behaviors.vp.OrbitBehavior;
import com.sun.j3d.utils.geometry.Sphere;
import com.sun.j3d.utils.image.TextureLoader;
import com.sun.j3d.utils.universe.SimpleUniverse;
import com.sun.j3d.utils.universe.ViewingPlatform;

public class EnvironmentExplorer extends JApplet implements
    Java3DExplorerConstants {

  // Scene graph items
  SimpleUniverse u;

  // Light items
  Group lightGroup;

  AmbientLight lightAmbient;

  DirectionalLight lightDirectional;

  PointLight lightPoint;

  SpotLight lightSpot;

  Point3f attenuation = new Point3f(1.0f, 0.0f, 0.0f);

  float spotSpreadAngle = 60; // degrees

  float spotConcentration = 5.0f;

  // Fog items
  Switch fogSwitch;

  IntChooser fogChooser;

  // Background items
  Switch bgSwitch;

  IntChooser bgChooser;

  // Sound items
  Switch soundSwitch;

  IntChooser soundChooser;

  BackgroundSound soundBackground;

  PointSound soundPoint;

  // Display object
  Switch spheresSwitch;

  Switch gridSwitch;

  // image grabber
  boolean isApplication;

  Canvas3D canvas;

  OffScreenCanvas3D offScreenCanvas;

  View view;

  // GUI elements
  JTabbedPane tabbedPane;

  // Config items
  String codeBaseString;

  String outFileBase = "env";

  int outFileSeq = 0;

  static final float OFF_SCREEN_SCALE = 1.0f;

  int colorMode = USE_COLOR;

  // Temporaries that are reused
  Transform3D tmpTrans = new Transform3D();

  Vector3f tmpVector = new Vector3f();

  AxisAngle4f tmpAxisAngle = new AxisAngle4f();

  // configurable colors. These get set based on the rendering
  // mode. By default they use color. B&W is set up for print
  // file output: white background with B&W coloring.
  Color3f objColor;

  // geometric constants
  Point3f origin = new Point3f();

  /*
   * Set up the lights. This is a group which contains the ambient light and a
   * switch for the other lights. directional : white light pointing along Z
   * axis point : white light near upper left corner of spheres spot : white
   * light near upper left corner of spheres, pointing towards center.
   */
  void setupLights() {

    lightGroup = new Group();

    // Set up the ambient light
    lightAmbient = new AmbientLight(darkGrey);
    lightAmbient.setInfluencingBounds(infiniteBounds);
    lightAmbient.setCapability(Light.ALLOW_STATE_WRITE);
    lightAmbient.setEnable(true);
    lightGroup.addChild(lightAmbient);

    // Set up the directional light
    Vector3f lightDirection = new Vector3f(0.65f, -0.65f, -0.40f);
    lightDirectional = new DirectionalLight(white, lightDirection);
    lightDirectional.setInfluencingBounds(infiniteBounds);
    lightDirectional.setEnable(true);
    lightDirectional.setCapability(Light.ALLOW_STATE_WRITE);
    lightGroup.addChild(lightDirectional);

    // Set up the point light
    Point3f lightPosition = new Point3f(-1.0f, 1.0f, 0.6f);
    lightPoint = new PointLight(white, lightPosition, attenuation);
    lightPoint.setInfluencingBounds(infiniteBounds);
    lightPoint.setEnable(false);
    lightPoint.setCapability(Light.ALLOW_STATE_WRITE);
    lightPoint.setCapability(PointLight.ALLOW_ATTENUATION_WRITE);
    lightGroup.addChild(lightPoint);

    // Set up the spot light
    // Point the light back at the origin
    lightSpot = new SpotLight(white, lightPosition, attenuation,
        lightDirection, (float) Math.toRadians(spotSpreadAngle),
        spotConcentration);
    lightSpot.setInfluencingBounds(infiniteBounds);
    lightSpot.setEnable(false);
    lightSpot.setCapability(Light.ALLOW_STATE_WRITE);
    lightSpot.setCapability(PointLight.ALLOW_ATTENUATION_WRITE);
    lightSpot.setCapability(SpotLight.ALLOW_CONCENTRATION_WRITE);
    lightSpot.setCapability(SpotLight.ALLOW_SPREAD_ANGLE_WRITE);
    lightGroup.addChild(lightSpot);
  }

  /*
   * Setup the backgrounds. The bg tool creates a Switch and a GUI component
   * for the backgrounds
   */
  void setupBackgrounds() {
    // initialize the background tool
    BackgroundTool bgTool = new BackgroundTool(codeBaseString);
    bgSwitch = bgTool.getSwitch();
    bgChooser = bgTool.getChooser();
  }

  /*
   * Setup the fog Switch and Chooser. Child values are: CHILD_NONE: Don't use
   * a fog 0: The linear Fog node 1: The exponential Fog node
   */
  void setupFogs() {
    fogSwitch = new Switch(Switch.CHILD_NONE);
    fogSwitch.setCapability(Switch.ALLOW_SWITCH_WRITE);

    // set up the linear fog
    LinearFog fogLinear = new LinearFog(skyBlue, 6.0f, 12.0f);
    fogLinear.setInfluencingBounds(infiniteBounds);
    fogSwitch.addChild(fogLinear);

    // set up the exponential fog
    ExponentialFog fogExp = new ExponentialFog(skyBlue, 0.3f);
    fogExp.setInfluencingBounds(infiniteBounds);
    fogSwitch.addChild(fogExp);

    // Create the chooser GUI
    String[] fogNames = { "None", "Linear", "Exponential", };
    int[] fogValues = { Switch.CHILD_NONE, 0, 1 };

    fogChooser = new IntChooser("Fog:", fogNames, fogValues, 0);
    fogChooser.addIntListener(new IntListener() {
      public void intChanged(IntEvent event) {
        int value = event.getValue();
        fogSwitch.setWhichChild(value);
      }
    });
    fogChooser.setValue(Switch.CHILD_NONE);
  }

  /*
   * Set up the sound switch. The child values are: CHILD_NONE: 1No sound 0:
   * BackgroundSound 1: PointSound 2: ConeSound
   */
  void setupSounds() {
    soundSwitch = new Switch(Switch.CHILD_NONE);
    soundSwitch.setCapability(Switch.ALLOW_SWITCH_WRITE);

    // Set up the sound media container
    java.net.URL soundURL = null;
    String soundFile = "techno_machine.au";
    try {
      soundURL = new java.net.URL(codeBaseString + soundFile);
    } catch (java.net.MalformedURLException ex) {
      System.out.println(ex.getMessage());
      System.exit(1);
    }
    if (soundURL == null) { // application, try file URL
      try {
        soundURL = new java.net.URL("file:./" + soundFile);
      } catch (java.net.MalformedURLException ex) {
        System.out.println(ex.getMessage());
        System.exit(1);
      }
    }
    //System.out.println("soundURL = " + soundURL);
    MediaContainer soundMC = new MediaContainer(soundURL);

    // set up the Background Sound
    soundBackground = new BackgroundSound();
    soundBackground.setCapability(Sound.ALLOW_ENABLE_WRITE);
    soundBackground.setSoundData(soundMC);
    soundBackground.setSchedulingBounds(infiniteBounds);
    soundBackground.setEnable(false);
    soundBackground.setLoop(Sound.INFINITE_LOOPS);
    soundSwitch.addChild(soundBackground);

    // set up the point sound
    soundPoint = new PointSound();
    soundPoint.setCapability(Sound.ALLOW_ENABLE_WRITE);
    soundPoint.setSoundData(soundMC);
    soundPoint.setSchedulingBounds(infiniteBounds);
    soundPoint.setEnable(false);
    soundPoint.setLoop(Sound.INFINITE_LOOPS);
    soundPoint.setPosition(-5.0f, 5.0f, 0.0f);
    Point2f[] distGain = new Point2f[2];
    // set the attenuation to linearly decrease volume from max at
    // source to 0 at a distance of 15m
    distGain[0] = new Point2f(0.0f, 1.0f);
    distGain[1] = new Point2f(15.0f, 0.0f);
    soundPoint.setDistanceGain(distGain);
    soundSwitch.addChild(soundPoint);

    // Create the chooser GUI
    String[] soundNames = { "None", "Background", "Point", };

    soundChooser = new IntChooser("Sound:", soundNames);
    soundChooser.addIntListener(new IntListener() {
      public void intChanged(IntEvent event) {
        int value = event.getValue();
        // Should just be able to use setWhichChild on
        // soundSwitch, have to explictly enable/disable due to
        // bug.
        switch (value) {
        case 0:
          soundSwitch.setWhichChild(Switch.CHILD_NONE);
          soundBackground.setEnable(false);
          soundPoint.setEnable(false);
          break;
        case 1:
          soundSwitch.setWhichChild(0);
          soundBackground.setEnable(true);
          soundPoint.setEnable(false);
          break;
        case 2:
          soundSwitch.setWhichChild(1);
          soundBackground.setEnable(false);
          soundPoint.setEnable(true);
          break;
        }
      }
    });
    soundChooser.setValue(Switch.CHILD_NONE);

  }

  // 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 objColor ambient,
    // black emmissive, objColor diffuse and white specular coloring
    Material material = new Material(objColor, black, objColor, white, 32);
    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.1f);
        tmpTrans.set(tmpVector);
        tg.setTransform(tmpTrans);
        tg.addChild(new Link(sphereSG));
        spheresSwitch.addChild(tg);
      }
    }
  }

  // sets up a grid of squares
  void setupGrid() {

    // create a Switch for the spheres, allow switch changes
    gridSwitch = new Switch(Switch.CHILD_NONE);
    gridSwitch.setCapability(Switch.ALLOW_SWITCH_WRITE);

    // Set up an appearance to make the square3s with red ambient,
    // black emmissive, red diffuse and black specular coloring
    Material material = new Material(red, black, red, black, 64);
    Appearance appearance = new Appearance();
    appearance.setMaterial(material);

    // create a grid of quads
    int gridSize = 20; // grid is gridSize quads along each side
    int numQuads = gridSize * gridSize;
    int numVerts = numQuads * 4; // 4 verts per quad
    // there will be 3 floats per coord and 4 coords per quad
    float[] coords = new float[3 * numVerts];
    // All the quads will use the same normal at each vertex, so
    // allocate an array to hold references to the same normal
    Vector3f[] normals = new Vector3f[numVerts];
    Vector3f vertNormal = new Vector3f(0.0f, 0.0f, 1.0f);
    float edgeLength = 5.0f; // length of each edge of the grid
    float gridGap = 0.03f; // the gap between each quad
    // length of each quad is (total length - sum of the gaps) / gridSize
    float quadLength = (edgeLength - gridGap * (gridSize - 1)) / gridSize;

    // create a grid of quads in the z=0 plane
    // each has a TransformGroup to position the sphere which contains
    // a link to the shared group for the sphere
    float curX, curY;
    for (int y = 0; y < gridSize; y++) {
      curY = y * (quadLength + gridGap); // offset to lower left corner
      curY -= edgeLength / 2; // center on 0,0
      for (int x = 0; x < gridSize; x++) {
        // this is the offset into the vertex array for the first
        // vertex of the quad
        int vertexOffset = (y * gridSize + x) * 4;
        // this is the offset into the coord array for the first
        // vertex of the quad, where there are 3 floats per vertex
        int coordOffset = vertexOffset * 3;
        curX = x * (quadLength + gridGap); // offset to ll corner
        curX -= edgeLength / 2; // center on 0,0
        // lower left corner
        coords[coordOffset + 0] = curX;
        coords[coordOffset + 1] = curY;
        coords[coordOffset + 2] = 0.0f; // z
        // lower right corner
        coords[coordOffset + 3] = curX + quadLength;
        coords[coordOffset + 4] = curY;
        coords[coordOffset + 5] = 0.0f; // z
        // upper right corner
        coords[coordOffset + 6] = curX + quadLength;
        coords[coordOffset + 7] = curY + quadLength;
        coords[coordOffset + 8] = 0.0f; // z
        // upper left corner
        coords[coordOffset + 9] = curX;
        coords[coordOffset + 10] = curY + quadLength;
        coords[coordOffset + 11] = 0.0f; // z
        for (int i = 0; i < 4; i++) {
          normals[vertexOffset + i] = vertNormal;
        }
      }
    }
    // now that we have the data, create the QuadArray
    QuadArray quads = new QuadArray(numVerts, QuadArray.COORDINATES
        | QuadArray.NORMALS);
    quads.setCoordinates(0, coords);
    quads.setNormals(0, normals);

    // create the shape
    Shape3D shape = new Shape3D(quads, appearance);

    // add it to the switch
    gridSwitch.addChild(shape);
  }

  BranchGroup createSceneGraph() {
    // Create the root of the branch graph
    BranchGroup objRoot = new BranchGroup();

    // Add the primitives to the scene
    setupSpheres();
    objRoot.addChild(spheresSwitch);
    setupGrid();
    objRoot.addChild(gridSwitch);
    objRoot.addChild(lightGroup);
    objRoot.addChild(bgSwitch);
    objRoot.addChild(fogSwitch);
    objRoot.addChild(soundSwitch);

    KeyPrintBehavior key = new KeyPrintBehavior();
    key.setSchedulingBounds(infiniteBounds);
    objRoot.addChild(key);
    return objRoot;
  }

  public EnvironmentExplorer(boolean isApplication, boolean blackAndWhite) {
    if (blackAndWhite) {
      colorMode = USE_BLACK_AND_WHITE;
    }
    this.isApplication = isApplication;
  }

  public EnvironmentExplorer(boolean isApplication) {
    this(isApplication, false);
  }

  public EnvironmentExplorer() {
    this(false, false);
  }

  public void init() {
    // initialize the code base
    try {
      java.net.URL codeBase = getCodeBase();
      codeBaseString = codeBase.toString();
    } catch (Exception e) {
      // probably running as an application, try the application
      // code base
      codeBaseString = "file:./";
    }

    if (colorMode == USE_COLOR) {
      objColor = red;
    } else {
      objColor = white;
    }

    Container contentPane = getContentPane();

    contentPane.setLayout(new BorderLayout());

    GraphicsConfiguration config = SimpleUniverse
        .getPreferredConfiguration();

    canvas = new Canvas3D(config);

    u = new SimpleUniverse(canvas);

    if (isApplication) {
      offScreenCanvas = new OffScreenCanvas3D(config, true);
      // set the size of the off-screen canvas based on a scale
      // of the on-screen size
      Screen3D sOn = canvas.getScreen3D();
      Screen3D sOff = offScreenCanvas.getScreen3D();
      Dimension dim = sOn.getSize();
      dim.width *= OFF_SCREEN_SCALE;
      dim.height *= OFF_SCREEN_SCALE;
      sOff.setSize(dim);
      sOff.setPhysicalScreenWidth(sOn.getPhysicalScreenWidth()
          * OFF_SCREEN_SCALE);
      sOff.setPhysicalScreenHeight(sOn.getPhysicalScreenHeight()
          * OFF_SCREEN_SCALE);

      // attach the offscreen canvas to the view
      u.getViewer().getView().addCanvas3D(offScreenCanvas);

    }
    contentPane.add("Center", canvas);

    // setup the env nodes and their GUI elements
    setupLights();
    setupBackgrounds();
    setupFogs();
    setupSounds();

    // Create a simple scene and attach it to the virtual universe
    BranchGroup scene = createSceneGraph();

    // set up sound
    u.getViewer().createAudioDevice();

    // get the view
    view = u.getViewer().getView();

    // 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(canvas, OrbitBehavior.STOP_ZOOM);
    orbit.setSchedulingBounds(infiniteBounds);
    viewingPlatform.setViewPlatformBehavior(orbit);

    u.addBranchGraph(scene);

    contentPane.add("East", guiPanel());
  }

  // create a panel with a tabbed pane holding each of the edit panels
  JPanel guiPanel() {
    JPanel panel = new JPanel();
    panel.setLayout(new BorderLayout());
    tabbedPane = new JTabbedPane();
    tabbedPane.addTab("Light", lightPanel());
    tabbedPane.addTab("Background", backgroundPanel());
    tabbedPane.addTab("Fog", fogPanel());
    tabbedPane.addTab("Sound", soundPanel());
    panel.add("Center", tabbedPane);

    panel.add("South", configPanel());
    return panel;
  }

  Box lightPanel() {
    Box panel = new Box(BoxLayout.Y_AXIS);

    // add the ambient light checkbox to the panel
    JCheckBox ambientCheckBox = new JCheckBox("Ambient Light");
    ambientCheckBox.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        JCheckBox checkbox = (JCheckBox) e.getSource();
        lightAmbient.setEnable(checkbox.isSelected());
      }
    });
    ambientCheckBox.setSelected(true);
    panel.add(new LeftAlignComponent(ambientCheckBox));

    String[] lightTypeValues = { "None", "Directional", "Positional",
        "Spot" };
    IntChooser lightTypeChooser = new IntChooser("Light Type:",
        lightTypeValues);
    lightTypeChooser.addIntListener(new IntListener() {
      public void intChanged(IntEvent event) {
        int value = event.getValue();
        switch (value) {
        case 0:
          lightDirectional.setEnable(false);
          lightPoint.setEnable(false);
          lightSpot.setEnable(false);
          break;
        case 1:
          lightDirectional.setEnable(true);
          lightPoint.setEnable(false);
          lightSpot.setEnable(false);
          break;
        case 2:
          lightDirectional.setEnable(false);
          lightPoint.setEnable(true);
          lightSpot.setEnable(false);
          break;
        case 3:
          lightDirectional.setEnable(false);
          lightPoint.setEnable(false);
          lightSpot.setEnable(true);
          break;
        }
      }
    });
    lightTypeChooser.setValueByName("Directional");
    panel.add(lightTypeChooser);

    // Set up the sliders for the attenuation

    // top row
    panel.add(new LeftAlignComponent(new JLabel("Light attenuation:")));

    FloatLabelJSlider constantSlider = new FloatLabelJSlider("Constant ",
        0.1f, 0.0f, 3.0f, attenuation.x);
    constantSlider.setMajorTickSpacing(1.0f);
    constantSlider.setPaintTicks(true);
    constantSlider.addFloatListener(new FloatListener() {
      public void floatChanged(FloatEvent e) {
        attenuation.x = e.getValue();
        lightPoint.setAttenuation(attenuation);
        lightSpot.setAttenuation(attenuation);
      }
    });
    panel.add(constantSlider);

    FloatLabelJSlider linearSlider = new FloatLabelJSlider("Linear   ",
        0.1f, 0.0f, 3.0f, attenuation.y);
    linearSlider.setMajorTickSpacing(1.0f);
    linearSlider.setPaintTicks(true);
    linearSlider.addFloatListener(new FloatListener() {
      public void floatChanged(FloatEvent e) {
        attenuation.y = e.getValue();
        lightPoint.setAttenuation(attenuation);
        lightSpot.setAttenuation(attenuation);
      }
    });
    panel.add(linearSlider);

    FloatLabelJSlider quadradicSlider = new FloatLabelJSlider("Quadradic",
        0.1f, 0.0f, 3.0f, attenuation.z);
    quadradicSlider.setMajorTickSpacing(1.0f);
    quadradicSlider.setPaintTicks(true);
    quadradicSlider.addFloatListener(new FloatListener() {
      public void floatChanged(FloatEvent e) {
        attenuation.z = e.getValue();
        lightPoint.setAttenuation(attenuation);
        lightSpot.setAttenuation(attenuation);
      }
    });
    panel.add(quadradicSlider);

    // Set up the sliders for the attenuation
    // top row
    panel.add(new LeftAlignComponent(new JLabel("Spot light:")));

    // spread angle is 0-180 degrees, no slider scaling
    FloatLabelJSlider spotSpreadSlider = new FloatLabelJSlider(
        "Spread Angle ", 1.0f, 0.0f, 180.0f, spotSpreadAngle);
    spotSpreadSlider.addFloatListener(new FloatListener() {
      public void floatChanged(FloatEvent e) {
        spotSpreadAngle = e.getValue();
        lightSpot.setSpreadAngle((float) Math
            .toRadians(spotSpreadAngle));
      }
    });
    panel.add(spotSpreadSlider);

    // concentration angle is 0-128 degrees
    FloatLabelJSlider spotConcentrationSlider = new FloatLabelJSlider(
        "Concentration", 1.0f, 0.0f, 128.0f, spotConcentration);
    spotConcentrationSlider.addFloatListener(new FloatListener() {
      public void floatChanged(FloatEvent e) {
        spotConcentration = e.getValue();
        lightSpot.setConcentration(spotConcentration);
      }
    });
    panel.add(spotConcentrationSlider);

    return panel;
  }

  JPanel backgroundPanel() {
    JPanel panel = new JPanel();
    panel.add(bgChooser);
    return panel;
  }

  JPanel fogPanel() {
    JPanel panel = new JPanel();
    panel.add(fogChooser);
    return panel;
  }

  JPanel soundPanel() {
    JPanel panel = new JPanel();
    panel.add(soundChooser);
    return panel;
  }

  JPanel configPanel() {
    JPanel panel = new JPanel();
    panel.setLayout(new GridLayout(1, 0));

    String[] dataTypeValues = { "Spheres", "Grid", };
    IntChooser dataTypeChooser = new IntChooser("Data:", dataTypeValues);
    dataTypeChooser.addIntListener(new IntListener() {
      public void intChanged(IntEvent event) {
        int value = event.getValue();
        switch (value) {
        case 0:
          spheresSwitch.setWhichChild(Switch.CHILD_ALL);
          gridSwitch.setWhichChild(Switch.CHILD_NONE);
          break;
        case 1:
          gridSwitch.setWhichChild(Switch.CHILD_ALL);
          spheresSwitch.setWhichChild(Switch.CHILD_NONE);
          break;
        }
      }
    });
    panel.add(dataTypeChooser);

    if (isApplication) {
      JButton snapButton = new JButton("Snap Image");
      snapButton.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
          Point loc = canvas.getLocationOnScreen();
          offScreenCanvas.setOffScreenLocation(loc);
          Dimension dim = canvas.getSize();
          dim.width *= OFF_SCREEN_SCALE;
          dim.height *= OFF_SCREEN_SCALE;
          nf.setMinimumIntegerDigits(3);
          offScreenCanvas.snapImageFile(outFileBase
              + nf.format(outFileSeq++), dim.width, dim.height);
          nf.setMinimumIntegerDigits(0);
        }
      });
      panel.add(snapButton);
    }

    return panel;
  }

  public void destroy() {
    u.removeAllLocales();
  }

  // The following allows EnvironmentExplorer to be run as an application
  // as well as an applet
  //
  public static void main(String[] args) {
    boolean useBlackAndWhite = false;
    for (int i = 0; i < args.length; i++) {
      //System.out.println("args[" + i + "] = " + args[i]);
      if (args[i].equals("-b")) {
        System.out.println("Use Black And White");
        useBlackAndWhite = true;
      }
    }
    new MainFrame(new EnvironmentExplorer(true, useBlackAndWhite), 950, 600);
  }
}

class BackgroundTool implements Java3DExplorerConstants {

  Switch bgSwitch;

  IntChooser bgChooser;

  BackgroundTool(String codeBaseString) {

    bgSwitch = new Switch(Switch.CHILD_NONE);
    bgSwitch.setCapability(Switch.ALLOW_SWITCH_WRITE);

    // set up the dark grey BG color node
    Background bgDarkGrey = new Background(darkGrey);
    bgDarkGrey.setApplicationBounds(infiniteBounds);
    bgSwitch.addChild(bgDarkGrey);

    // set up the grey BG color node
    Background bgGrey = new Background(grey);
    bgGrey.setApplicationBounds(infiniteBounds);
    bgSwitch.addChild(bgGrey);

    // set up the light grey BG color node
    Background bgLightGrey = new Background(lightGrey);
    bgLightGrey.setApplicationBounds(infiniteBounds);
    bgSwitch.addChild(bgLightGrey);

    // set up the white BG color node
    Background bgWhite = new Background(white);
    bgWhite.setApplicationBounds(infiniteBounds);
    bgSwitch.addChild(bgWhite);

    // set up the blue BG color node
    Background bgBlue = new Background(skyBlue);
    bgBlue.setApplicationBounds(infiniteBounds);
    bgSwitch.addChild(bgBlue);

    // set up the image
    java.net.URL bgImageURL = null;
    try {
      bgImageURL = new java.net.URL(codeBaseString + "bg.jpg");
    } catch (java.net.MalformedURLException ex) {
      System.out.println(ex.getMessage());
      System.exit(1);
    }
    if (bgImageURL == null) { // application, try file URL
      try {
        bgImageURL = new java.net.URL("file:./bg.jpg");
      } catch (java.net.MalformedURLException ex) {
        System.out.println(ex.getMessage());
        System.exit(1);
      }
    }
    TextureLoader bgTexture = new TextureLoader(bgImageURL, null);

    // Create a background with the static image
    Background bgImage = new Background(bgTexture.getImage());
    bgImage.setApplicationBounds(infiniteBounds);
    bgSwitch.addChild(bgImage);

    // create a background with the image mapped onto a sphere which
    // will enclose the world
    Background bgGeo = new Background();
    bgGeo.setApplicationBounds(infiniteBounds);
    BranchGroup bgGeoBG = new BranchGroup();
    Appearance bgGeoApp = new Appearance();
    bgGeoApp.setTexture(bgTexture.getTexture());
    Sphere sphereObj = new Sphere(1.0f, Sphere.GENERATE_NORMALS
        | Sphere.GENERATE_NORMALS_INWARD
        | Sphere.GENERATE_TEXTURE_COORDS, 45, bgGeoApp);
    bgGeoBG.addChild(sphereObj);
    bgGeo.setGeometry(bgGeoBG);
    bgSwitch.addChild(bgGeo);

    // Create the chooser GUI
    String[] bgNames = { "No Background (Black)", "Dark Grey", "Grey",
        "Light Grey", "White", "Blue", "Sky Image", "Sky Geometry", };
    int[] bgValues = { Switch.CHILD_NONE, 0, 1, 2, 3, 4, 5, 6 };

    bgChooser = new IntChooser("Background:", bgNames, bgValues, 0);
    bgChooser.addIntListener(new IntListener() {
      public void intChanged(IntEvent event) {
        int value = event.getValue();
        bgSwitch.setWhichChild(value);
      }
    });
    bgChooser.setValue(Switch.CHILD_NONE);
  }

  Switch getSwitch() {
    return bgSwitch;
  }

  IntChooser getChooser() {
    return bgChooser;
  }

}

interface Java3DExplorerConstants {

  // colors
  static Color3f black = new Color3f(0.0f, 0.0f, 0.0f);

  static Color3f red = new Color3f(1.0f, 0.0f, 0.0f);

  static Color3f green = new Color3f(0.0f, 1.0f, 0.0f);

  static Color3f blue = new Color3f(0.0f, 0.0f, 1.0f);

  static Color3f skyBlue = new Color3f(0.6f, 0.7f, 0.9f);

  static Color3f cyan = new Color3f(0.0f, 1.0f, 1.0f);

  static Color3f magenta = new Color3f(1.0f, 0.0f, 1.0f);

  static Color3f yellow = new Color3f(1.0f, 1.0f, 0.0f);

  static Color3f brightWhite = new Color3f(1.0f, 1.5f, 1.5f);

  static Color3f white = new Color3f(1.0f, 1.0f, 1.0f);

  static Color3f darkGrey = new Color3f(0.15f, 0.15f, 0.15f);

  static Color3f medGrey = new Color3f(0.3f, 0.3f, 0.3f);

  static Color3f grey = new Color3f(0.5f, 0.5f, 0.5f);

  static Color3f lightGrey = new Color3f(0.75f, 0.75f, 0.75f);

  // infinite bounding region, used to make env nodes active everywhere
  BoundingSphere infiniteBounds = new BoundingSphere(new Point3d(),
      Double.MAX_VALUE);

  // common values
  static final String nicestString = "NICEST";

  static final String fastestString = "FASTEST";

  static final String antiAliasString = "Anti-Aliasing";

  static final String noneString = "NONE";

  // light type constants
  static int LIGHT_AMBIENT = 1;

  static int LIGHT_DIRECTIONAL = 2;

  static int LIGHT_POSITIONAL = 3;

  static int LIGHT_SPOT = 4;

  // screen capture constants
  static final int USE_COLOR = 1;

  static final int USE_BLACK_AND_WHITE = 2;

  // number formatter
  NumberFormat nf = NumberFormat.getInstance();

}

class IntChooser extends JPanel implements Java3DExplorerConstants {

  JComboBox combo;

  String[] choiceNames;

  int[] choiceValues;

  int current;

  Vector listeners = new Vector();

  IntChooser(String name, String[] initChoiceNames, int[] initChoiceValues,
      int initValue) {
    if ((initChoiceValues != null)
        && (initChoiceNames.length != initChoiceValues.length)) {
      throw new IllegalArgumentException(
          "Name and Value arrays must have the same length");
    }
    choiceNames = new String[initChoiceNames.length];
    choiceValues = new int[initChoiceNames.length];
    System
        .arraycopy(initChoiceNames, 0, choiceNames, 0,
            choiceNames.length);
    if (initChoiceValues != null) {
      System.arraycopy(initChoiceValues, 0, choiceValues, 0,
          choiceNames.length);
    } else {
      for (int i = 0; i < initChoiceNames.length; i++) {
        choiceValues[i] = i;
      }
    }

    // Create the combo box, select the init value
    combo = new JComboBox(choiceNames);
    combo.setSelectedIndex(current);
    combo.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        JComboBox cb = (JComboBox) e.getSource();
        int index = cb.getSelectedIndex();
        setValueIndex(index);
      }
    });

    // set the initial value
    current = 0;
    setValue(initValue);

    // layout to align left
    setLayout(new BorderLayout());
    Box box = new Box(BoxLayout.X_AXIS);
    add(box, BorderLayout.WEST);

    box.add(new JLabel(name));
    box.add(combo);
  }

  IntChooser(String name, String[] initChoiceNames, int[] initChoiceValues) {
    this(name, initChoiceNames, initChoiceValues, initChoiceValues[0]);
  }

  IntChooser(String name, String[] initChoiceNames, int initValue) {
    this(name, initChoiceNames, null, initValue);
  }

  IntChooser(String name, String[] initChoiceNames) {
    this(name, initChoiceNames, null, 0);
  }

  public void addIntListener(IntListener listener) {
    listeners.add(listener);
  }

  public void removeIntListener(IntListener listener) {
    listeners.remove(listener);
  }

  public void setValueByName(String newName) {
    boolean found = false;
    int newIndex = 0;
    for (int i = 0; (!found) && (i < choiceNames.length); i++) {
      if (newName.equals(choiceNames[i])) {
        newIndex = i;
        found = true;
      }
    }
    if (found) {
      setValueIndex(newIndex);
    }
  }

  public void setValue(int newValue) {
    boolean found = false;
    int newIndex = 0;
    for (int i = 0; (!found) && (i < choiceValues.length); i++) {
      if (newValue == choiceValues[i]) {
        newIndex = i;
        found = true;
      }
    }
    if (found) {
      setValueIndex(newIndex);
    }
  }

  public int getValue() {
    return choiceValues[current];
  }

  public String getValueName() {
    return choiceNames[current];
  }

  public void setValueIndex(int newIndex) {
    boolean changed = (newIndex != current);
    current = newIndex;
    if (changed) {
      combo.setSelectedIndex(current);
      valueChanged();
    }
  }

  private void valueChanged() {
    // notify the listeners
    IntEvent event = new IntEvent(this, choiceValues[current]);
    for (Enumeration e = listeners.elements(); e.hasMoreElements();) {
      IntListener listener = (IntListener) e.nextElement();
      listener.intChanged(event);
    }
  }

}

class OffScreenCanvas3D extends Canvas3D {

  OffScreenCanvas3D(GraphicsConfiguration graphicsConfiguration,
      boolean offScreen) {

    super(graphicsConfiguration, offScreen);
  }

  private BufferedImage doRender(int width, int height) {

    BufferedImage bImage = new BufferedImage(width, height,
        BufferedImage.TYPE_INT_RGB);

    ImageComponent2D buffer = new ImageComponent2D(
        ImageComponent.FORMAT_RGB, bImage);
    //buffer.setYUp(true);

    setOffScreenBuffer(buffer);
    renderOffScreenBuffer();
    waitForOffScreenRendering();
    bImage = getOffScreenBuffer().getImage();
    return bImage;
  }

  void snapImageFile(String filename, int width, int height) {
    BufferedImage bImage = doRender(width, height);

    /*
     * JAI: RenderedImage fImage = JAI.create("format", bImage,
     * DataBuffer.TYPE_BYTE); JAI.create("filestore", fImage, filename +
     * ".tif", "tiff", null);
     */

    /* No JAI: */
    try {
      FileOutputStream fos = new FileOutputStream(filename + ".jpg");
      BufferedOutputStream bos = new BufferedOutputStream(fos);

      JPEGImageEncoder jie = JPEGCodec.createJPEGEncoder(bos);
      JPEGEncodeParam param = jie.getDefaultJPEGEncodeParam(bImage);
      param.setQuality(1.0f, true);
      jie.setJPEGEncodeParam(param);
      jie.encode(bImage);

      bos.flush();
      fos.close();
    } catch (Exception e) {
      System.out.println(e);
    }
  }
}

interface IntListener extends EventListener {
  void intChanged(IntEvent e);
}

class IntEvent extends EventObject {

  int value;

  IntEvent(Object source, int newValue) {
    super(source);
    value = newValue;
  }

  int getValue() {
    return value;
  }
}

class KeyPrintBehavior extends Behavior {

  WakeupCondition wakeup = new WakeupOnAWTEvent(KeyEvent.KEY_PRESSED);

  public void initialize() {
    wakeupOn(wakeup);
  }

  public void processStimulus(Enumeration criteria) {
    while (criteria.hasMoreElements()) {
      wakeup = (WakeupCriterion) criteria.nextElement();
      if (wakeup instanceof WakeupOnAWTEvent) {
        AWTEvent[] evt = ((WakeupOnAWTEvent) wakeup).getAWTEvent();
        for (int i = 0; i < evt.length; i++) {
          if (evt[i] instanceof KeyEvent) {
            KeyEvent keyEvt = (KeyEvent) evt[i];
            System.out.println("Key pressed: '"
                + keyEvt.getKeyChar() + "'");
          }
        }
      }
    }
    // set the wakeup so we'll get the next event
    wakeupOn(wakeup);
  }
}

class LeftAlignComponent extends JPanel {
  LeftAlignComponent(Component c) {
    setLayout(new BorderLayout());
    add(c, BorderLayout.WEST);
  }
}

class FloatLabelJSlider extends JPanel implements ChangeListener,
    Java3DExplorerConstants {

  JSlider slider;

  JLabel valueLabel;

  Vector listeners = new Vector();

  float min, max, resolution, current, scale;

  int minInt, maxInt, curInt;;

  int intDigits, fractDigits;

  float minResolution = 0.001f;

  // default slider with name, resolution = 0.1, min = 0.0, max = 1.0 inital
  // 0.5
  FloatLabelJSlider(String name) {
    this(name, 0.1f, 0.0f, 1.0f, 0.5f);
  }

  FloatLabelJSlider(String name, float resolution, float min, float max,
      float current) {

    this.resolution = resolution;
    this.min = min;
    this.max = max;
    this.current = current;

    if (resolution < minResolution) {
      resolution = minResolution;
    }

    // round scale to nearest integer fraction. i.e. 0.3 => 1/3 = 0.33
    scale = (float) Math.round(1.0f / resolution);
    resolution = 1.0f / scale;

    // get the integer versions of max, min, current
    minInt = Math.round(min * scale);
    maxInt = Math.round(max * scale);
    curInt = Math.round(current * scale);

    // sliders use integers, so scale our floating point value by "scale"
    // to make each slider "notch" be "resolution". We will scale the
    // value down by "scale" when we get the event.
    slider = new JSlider(JSlider.HORIZONTAL, minInt, maxInt, curInt);
    slider.addChangeListener(this);

    valueLabel = new JLabel(" ");

    // set the initial value label
    setLabelString();

    // add min and max labels to the slider
    Hashtable labelTable = new Hashtable();
    labelTable.put(new Integer(minInt), new JLabel(nf.format(min)));
    labelTable.put(new Integer(maxInt), new JLabel(nf.format(max)));
    slider.setLabelTable(labelTable);
    slider.setPaintLabels(true);

    /* layout to align left */
    setLayout(new BorderLayout());
    Box box = new Box(BoxLayout.X_AXIS);
    add(box, BorderLayout.WEST);

    box.add(new JLabel(name));
    box.add(slider);
    box.add(valueLabel);
  }

  public void setMinorTickSpacing(float spacing) {
    int intSpacing = Math.round(spacing * scale);
    slider.setMinorTickSpacing(intSpacing);
  }

  public void setMajorTickSpacing(float spacing) {
    int intSpacing = Math.round(spacing * scale);
    slider.setMajorTickSpacing(intSpacing);
  }

  public void setPaintTicks(boolean paint) {
    slider.setPaintTicks(paint);
  }

  public void addFloatListener(FloatListener listener) {
    listeners.add(listener);
  }

  public void removeFloatListener(FloatListener listener) {
    listeners.remove(listener);
  }

  public void stateChanged(ChangeEvent e) {
    JSlider source = (JSlider) e.getSource();
    // get the event type, set the corresponding value.
    // Sliders use integers, handle floating point values by scaling the
    // values by "scale" to allow settings at "resolution" intervals.
    // Divide by "scale" to get back to the real value.
    curInt = source.getValue();
    current = curInt / scale;

    valueChanged();
  }

  public void setValue(float newValue) {
    boolean changed = (newValue != current);
    current = newValue;
    if (changed) {
      valueChanged();
    }
  }

  private void valueChanged() {
    // update the label
    setLabelString();

    // notify the listeners
    FloatEvent event = new FloatEvent(this, current);
    for (Enumeration e = listeners.elements(); e.hasMoreElements();) {
      FloatListener listener = (FloatListener) e.nextElement();
      listener.floatChanged(event);
    }
  }

  void setLabelString() {
    // Need to muck around to try to make sure that the width of the label
    // is wide enough for the largest value. Pad the string
    // be large enough to hold the largest value.
    int pad = 5; // fudge to make up for variable width fonts
    float maxVal = Math.max(Math.abs(min), Math.abs(max));
    intDigits = Math.round((float) (Math.log(maxVal) / Math.log(10))) + pad;
    if (min < 0) {
      intDigits++; // add one for the '-'
    }
    // fractDigits is num digits of resolution for fraction. Use base 10 log
    // of scale, rounded up, + 2.
    fractDigits = (int) Math.ceil((Math.log(scale) / Math.log(10)));
    nf.setMinimumFractionDigits(fractDigits);
    nf.setMaximumFractionDigits(fractDigits);
    String value = nf.format(current);
    while (value.length() < (intDigits + fractDigits)) {
      value = value + "  ";
    }
    valueLabel.setText(value);
  }

}

class FloatEvent extends EventObject {

  float value;

  FloatEvent(Object source, float newValue) {
    super(source);
    value = newValue;
  }

  float getValue() {
    return value;
  }
}

interface FloatListener extends EventListener {
  void floatChanged(FloatEvent e);
}





           
       








Related examples in the same category

1.Java 3D and VRML Package Tree Printer
2.Query PropertiesQuery Properties
3.Package InfoPackage Info
4.Frames per second counterFrames per second counter
5.Timer TestTimer Test