3D Graphics Java

/*
 * %Z%%M% %I% %E% %U%
 * 
 * ************************************************************** "Copyright (c)
 * 2001 Sun Microsystems, Inc. All Rights Reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * -Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 * 
 * -Redistribution in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 * 
 * Neither the name of Sun Microsystems, Inc. or the names of contributors may
 * be used to endorse or promote products derived from this software without
 * specific prior written permission.
 * 
 * This software is provided "AS IS," without a warranty of any kind. ALL
 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
 * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
 * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
 * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
 * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
 * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
 * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
 * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
 * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY
 * OF SUCH DAMAGES.
 * 
 * You acknowledge that Software is not designed,licensed or intended for use in
 * the design, construction, operation or maintenance of any nuclear facility."
 * 
 * ***************************************************************************
 */
import java.applet.Applet;
import java.awt.BorderLayout;
import java.awt.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.Group;
import javax.media.j3d.Texture;
import javax.media.j3d.Texture2D;
import javax.media.j3d.Transform3D;
import javax.media.j3d.View;
import javax.swing.ButtonGroup;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.vecmath.AxisAngle4f;
import javax.vecmath.Color3f;
import javax.vecmath.Color4f;
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.image.TextureLoader;
import com.sun.j3d.utils.universe.SimpleUniverse;
import com.sun.j3d.utils.universe.ViewingPlatform;
public class TexBug extends Applet implements ActionListener,
    Java3DExplorerConstants {
  // Scene graph items
  SimpleUniverse u;
  Group scene;
  Appearance appearance;
  // temp image grabber
  boolean isApplication;
  Canvas3D canvas;
  View view;
  // Texture2D
  Texture2D texture;
  TextureLoader texLoader;
  int texWidth;
  int texHeight;
  int texFormat;
  boolean texEnable;
  String texEnableString = "Enable Texture";
  String texMipEnableString = "Enable MipMap";
  JCheckBox texEnableCheckBox;
  int texBoundaryModeS;
  int texBoundaryModeT;
  String wrapString = "WRAP";
  String clampString = "CLAMP";
  String texBoundarySWrapString = "S WRAP";
  String texBoundarySClampString = "S CLAMP";
  String texBoundaryTWrapString = "T WRAP";
  String texBoundaryTClampString = "T CLAMP";
  Color4f texBoundaryColor;
  int texMinFilter;
  int texMagFilter;
  String texFilterBasePointString = "BASE_LEVEL_POINT";
  String texFilterBaseLinearString = "BASE_LEVEL_LINEAR";
  String texFilterMultiPointString = "MULTI_LEVEL_POINT";
  String texFilterMultiLinearString = "MULTI_LEVEL_LINEAR";
  String texMinFilterBasePointString = "min base point";
  String texMinFilterBaseLinearString = "min base linear";
  String texMinFilterMultiPointString = "mag multi point";
  String texMinFilterMultiLinearString = "mag multi linear";
  String texMinFilterFastestString = "min fastest";
  String texMinFilterNicestString = "min nicest";
  String texMagFilterBasePointString = "mag base point";
  String texMagFilterBaseLinearString = "mag base linear";
  String texMagFilterNicestString = "mag nicest";
  String texMagFilterFastestString = "mag fastest";
  int texMipMapMode;
  String texMipMapBaseString = "BASE_LEVEL";
  String texMipMapMultiString = "MULTI_LEVEL_MIPMAP";
  // Temporaries that are reused
  Transform3D tmpTrans = new Transform3D();
  Vector3f tmpVector = new Vector3f();
  AxisAngle4f tmpAxisAngle = new AxisAngle4f();
  // colors
  Color3f black = new Color3f(0.0f, 0.0f, 0.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);
  Color3f cyan = new Color3f(0.0f, 1.0f, 1.0f);
  Color3f magenta = new Color3f(1.0f, 0.0f, 1.0f);
  Color3f yellow = new Color3f(1.0f, 1.0f, 0.0f);
  Color3f white = new Color3f(1.0f, 1.0f, 1.0f);
  Color3f darkGrey = new Color3f(0.2f, 0.2f, 0.2f);
  Color3f grey = new Color3f(0.5f, 0.5f, 0.5f);
  // 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;
  // Base for URLs, used to handle application/applet split
  String codeBaseString = null;
  // create the appearance and it's components
  void setupAppearance() {
    appearance = new Appearance();
    // Texture2D
    // set the values to the defaults
    texEnable = false;
    texMipMapMode = Texture.BASE_LEVEL;
    texBoundaryModeS = Texture.WRAP;
    texBoundaryModeT = Texture.WRAP;
    texMinFilter = Texture.BASE_LEVEL_POINT;
    texMagFilter = Texture.BASE_LEVEL_POINT;
    texBoundaryColor = new Color4f(1.0f, 1.0f, 1.0f, 1.0f);
    // set up the appearance to allow the texture to be changed
    appearance.setCapability(Appearance.ALLOW_TEXTURE_WRITE);
    // set the texture
    setTexture();
  }
  int powerOfTwo(int value) {
    int retval = 2;
    while (retval < value) {
      retval *= 2;
    }
    return retval;
  }
  // create a Texture2D using the current values from the GUI
  // and attach it to the appearance
  void setTexture() {
    // set up the image using the TextureLoader
    java.net.URL imageURL = null;
    try {
      imageURL = new java.net.URL(codeBaseString + "earth.jpg");
      //imageURL = new java.net.URL(codeBaseString +
      //  "decal_textures/fish1.gif");
    } catch (Exception e) {
      System.err.println("Exception: " + e);
      System.exit(1);
    }
    int flags;
    if (texMipMapMode == Texture.BASE_LEVEL) {
      flags = 0;
    } else {
      flags = TextureLoader.GENERATE_MIPMAP;
    }
    texLoader = new TextureLoader(imageURL, new String("RGBA"),
    //new String("LUMINANCE"),
        flags, this);
    // We could create texture from image
    //
    // Get the image from the loader. We need an image which
    // has power of two dimensions, so we'll get the unscaled image,
    // figure out what the scaled size should be and then get a scale
    // image
    //ImageComponent2D unscaledImage = texLoader.getImage();
    //int width = unscaledImage.getWidth();
    //int height = unscaledImage.getWidth();
    //
    // scaled values are next power of two greater than or equal to
    // value
    //texWidth = powerOfTwo(width);
    //texHeight = powerOfTwo(height);
    //
    // rescale the image if necessary
    //ImageComponent2D texImage;
    //if ((texWidth == width) && (texHeight == height)) {
    //    texImage = unscaledImage;
    //} else {
    //    texImage = texLoader.getScaledImage(texWidth, texHeight);
    //}
    //texFormat = Texture.RGB;
    //texture = new Texture2D(texMipMapMode, texFormat, texWidth,
    //  texHeight);
    //texture.setImage(0, texImage);
    // instead we'll just get get the texture from loader
    texture = (Texture2D) texLoader.getTexture();
    //texture.setBoundaryColor(texBoundaryColor);
    texture.setBoundaryColor(1.0f, 1.0f, 1.0f, 1.0f);
    texture.setBoundaryModeS(texBoundaryModeS);
    texture.setBoundaryModeT(texBoundaryModeT);
    texture.setEnable(texEnable);
    texture.setMinFilter(texMinFilter);
    texture.setMagFilter(texMagFilter);
    // Set the capabilities to enable the changable attrs
    texture.setCapability(Texture.ALLOW_ENABLE_WRITE);
    texture.setCapability(Texture.ALLOW_IMAGE_WRITE);
    // connect the new texture to the appearance
    System.out.println("Appearance.setTexture(" + texture + ")");
    appearance.setTexture(texture);
  }
  // sets up the scene switch
  void setupScene() {
    scene = new Group();
    // create a sphere with the shared appearance
    Sphere sphere = new Sphere(1.0f, Sphere.GENERATE_NORMALS
        | Sphere.GENERATE_TEXTURE_COORDS, appearance);
    scene.addChild(sphere);
  }
  public void actionPerformed(ActionEvent e) {
    String action = e.getActionCommand();
    Object source = e.getSource();
    if (action == texEnableString) {
      texEnable = texEnableCheckBox.isSelected();
      System.out.println("texture.setEnable(" + texEnable + ")");
      texture.setEnable(texEnable);
    } else if (action == texBoundarySWrapString) {
      texBoundaryModeS = Texture.WRAP;
      setTexture();
    } else if (action == texBoundarySClampString) {
      texBoundaryModeS = Texture.CLAMP;
      setTexture();
    } else if (action == texBoundaryTWrapString) {
      texBoundaryModeT = Texture.WRAP;
      setTexture();
    } else if (action == texBoundaryTClampString) {
      texBoundaryModeT = Texture.CLAMP;
      setTexture();
    } else if (action == texMinFilterBasePointString) {
      texMinFilter = Texture.BASE_LEVEL_POINT;
      setTexture();
    } else if (action == texMinFilterBaseLinearString) {
      texMinFilter = Texture.BASE_LEVEL_LINEAR;
      setTexture();
    } else if (action == texMinFilterMultiPointString) {
      texMinFilter = Texture.MULTI_LEVEL_POINT;
      setTexture();
    } else if (action == texMinFilterMultiLinearString) {
      texMinFilter = Texture.MULTI_LEVEL_LINEAR;
      setTexture();
    } else if (action == texMinFilterFastestString) {
      texMinFilter = Texture.FASTEST;
      setTexture();
    } else if (action == texMinFilterNicestString) {
      texMinFilter = Texture.NICEST;
      setTexture();
    } else if (action == texMagFilterBasePointString) {
      texMagFilter = Texture.BASE_LEVEL_POINT;
      setTexture();
    } else if (action == texMagFilterBaseLinearString) {
      texMagFilter = Texture.BASE_LEVEL_LINEAR;
      setTexture();
    } else if (action == texMagFilterNicestString) {
      texMagFilter = Texture.NICEST;
      setTexture();
    } else if (action == texMagFilterFastestString) {
      texMagFilter = Texture.FASTEST;
      setTexture();
    } else if (action == texMipMapBaseString) {
      texMipMapMode = Texture.BASE_LEVEL;
      setTexture();
    } else if (action == texMipMapMultiString) {
      texMipMapMode = Texture.MULTI_LEVEL_MIPMAP;
      setTexture();
    }
  }
  BranchGroup createSceneGraph() {
    // Create the root of the branch graph
    BranchGroup objRoot = new BranchGroup();
    // Add the primitives to the scene
    setupAppearance();
    setupScene();
    objRoot.addChild(scene);
    return objRoot;
  }
  public TexBug() {
    this(false);
  }
  public TexBug(boolean isApplication) {
    this.isApplication = isApplication;
  }
  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:./";
    }
    // set up a NumFormat object to print out float with only 3 fraction
    // digits
    nf = NumberFormat.getInstance();
    nf.setMaximumFractionDigits(3);
    setLayout(new BorderLayout());
    GraphicsConfiguration config = SimpleUniverse
        .getPreferredConfiguration();
    canvas = new Canvas3D(config);
    add("Center", canvas);
    // Create a simple scene and attach it to the virtual universe
    BranchGroup scene = createSceneGraph();
    u = new SimpleUniverse(canvas);
    // 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 -2 -> 2 range
    double viewRadius = 2.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);
    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", guiPanel());
  }
  public void destroy() {
    u.removeAllLocales();
  }
  // create a panel with a tabbed pane holding each of the edit panels
  JPanel guiPanel() {
    JPanel panel = new JPanel();
    panel.setLayout(new BorderLayout());
    panel.add("Center", texture2DPanel());
    return panel;
  }
  JPanel texture2DPanel() {
    JPanel panel = new JPanel();
    panel.setLayout(new GridLayout(1, 0)); // horizontal
    JPanel leftPanel = new JPanel();
    leftPanel.setLayout(new GridLayout(0, 1)); // vertical
    panel.add(leftPanel);
    JPanel rightPanel = new JPanel();
    rightPanel.setLayout(new GridLayout(0, 1)); // vertical
    panel.add(rightPanel);
    texEnableCheckBox = new JCheckBox(texEnableString);
    // set up the action commands
    texEnableCheckBox.setActionCommand(texEnableString);
    // register the applet as the listener for the buttons
    texEnableCheckBox.addActionListener(this);
    // set the initial value
    texEnableCheckBox.setSelected(texEnable);
    // add the checkbox to the panel
    leftPanel.add(texEnableCheckBox);
    // texture boundary S
    leftPanel.add(new JLabel("Boundary S Mode:"));
    // create the buttons
    JRadioButton texBoundarySWrapButton = new JRadioButton(wrapString);
    JRadioButton texBoundarySClampButton = new JRadioButton(clampString);
    // set up the action commands
    texBoundarySWrapButton.setActionCommand(texBoundarySWrapString);
    texBoundarySClampButton.setActionCommand(texBoundarySClampString);
    // add the buttons to a group so that only one can be selected
    ButtonGroup boundSButtonGroup = new ButtonGroup();
    boundSButtonGroup.add(texBoundarySWrapButton);
    boundSButtonGroup.add(texBoundarySClampButton);
    // register the applet as the listener for the buttons
    texBoundarySWrapButton.addActionListener(this);
    texBoundarySClampButton.addActionListener(this);
    // add the buttons to the panel
    leftPanel.add(texBoundarySWrapButton);
    leftPanel.add(texBoundarySClampButton);
    // set the default
    texBoundarySWrapButton.setSelected(true);
    // texture boundary T
    leftPanel.add(new JLabel("Boundary T Mode:"));
    // create the buttons
    JRadioButton texBoundaryTWrapButton = new JRadioButton(wrapString);
    JRadioButton texBoundaryTClampButton = new JRadioButton(clampString);
    // set up the action commands
    texBoundaryTWrapButton.setActionCommand(texBoundaryTWrapString);
    texBoundaryTClampButton.setActionCommand(texBoundaryTClampString);
    // add the buttons to a group so that only one can be selected
    ButtonGroup boundTButtonGroup = new ButtonGroup();
    boundTButtonGroup.add(texBoundaryTWrapButton);
    boundTButtonGroup.add(texBoundaryTClampButton);
    // register the applet as the listener for the buttons
    texBoundaryTWrapButton.addActionListener(this);
    texBoundaryTClampButton.addActionListener(this);
    // add the buttons to the panel
    leftPanel.add(texBoundaryTWrapButton);
    leftPanel.add(texBoundaryTClampButton);
    // set the default
    texBoundaryTWrapButton.setSelected(true);
    // texture min filter
    rightPanel.add(new JLabel("Min Filter:"));
    // create the buttons
    JRadioButton texMinFilterBasePointButton = new JRadioButton(
        texFilterBasePointString);
    JRadioButton texMinFilterBaseLinearButton = new JRadioButton(
        texFilterBaseLinearString);
    JRadioButton texMinFilterMultiPointButton = new JRadioButton(
        texFilterMultiPointString);
    JRadioButton texMinFilterMultiLinearButton = new JRadioButton(
        texFilterMultiLinearString);
    JRadioButton texMinFilterNicestButton = new JRadioButton(nicestString);
    JRadioButton texMinFilterFastestButton = new JRadioButton(fastestString);
    // set up the action commands
    texMinFilterBasePointButton
        .setActionCommand(texMinFilterBasePointString);
    texMinFilterBaseLinearButton
        .setActionCommand(texMinFilterBaseLinearString);
    texMinFilterMultiPointButton
        .setActionCommand(texMinFilterMultiPointString);
    texMinFilterMultiLinearButton
        .setActionCommand(texMinFilterMultiLinearString);
    texMinFilterNicestButton.setActionCommand(texMinFilterNicestString);
    texMinFilterFastestButton.setActionCommand(texMinFilterFastestString);
    // add the buttons to a group so that only one can be selected
    ButtonGroup minFilterButtonGroup = new ButtonGroup();
    minFilterButtonGroup.add(texMinFilterBasePointButton);
    minFilterButtonGroup.add(texMinFilterBaseLinearButton);
    minFilterButtonGroup.add(texMinFilterMultiPointButton);
    minFilterButtonGroup.add(texMinFilterMultiLinearButton);
    minFilterButtonGroup.add(texMinFilterNicestButton);
    minFilterButtonGroup.add(texMinFilterFastestButton);
    // register the applet as the listener for the buttons
    texMinFilterBasePointButton.addActionListener(this);
    texMinFilterBaseLinearButton.addActionListener(this);
    texMinFilterMultiPointButton.addActionListener(this);
    texMinFilterMultiLinearButton.addActionListener(this);
    texMinFilterNicestButton.addActionListener(this);
    texMinFilterFastestButton.addActionListener(this);
    // add the buttons to the panel
    rightPanel.add(texMinFilterBasePointButton);
    rightPanel.add(texMinFilterBaseLinearButton);
    rightPanel.add(texMinFilterMultiPointButton);
    rightPanel.add(texMinFilterMultiLinearButton);
    rightPanel.add(texMinFilterNicestButton);
    rightPanel.add(texMinFilterFastestButton);
    // set the default
    texMinFilterBasePointButton.setSelected(true);
    // texture max filter
    rightPanel.add(new JLabel("Mag Filter:"));
    // create the buttons
    JRadioButton texMagFilterBasePointButton = new JRadioButton(
        texFilterBasePointString);
    JRadioButton texMagFilterBaseLinearButton = new JRadioButton(
        texFilterBaseLinearString);
    JRadioButton texMagFilterNicestButton = new JRadioButton(nicestString);
    JRadioButton texMagFilterFastestButton = new JRadioButton(fastestString);
    // set up the action commands
    texMagFilterBasePointButton
        .setActionCommand(texMagFilterBasePointString);
    texMagFilterBaseLinearButton
        .setActionCommand(texMagFilterBaseLinearString);
    texMagFilterNicestButton.setActionCommand(texMagFilterNicestString);
    texMagFilterFastestButton.setActionCommand(texMagFilterFastestString);
    // add the buttons to a group so that only one can be selected
    ButtonGroup magFilterButtonGroup = new ButtonGroup();
    magFilterButtonGroup.add(texMagFilterBasePointButton);
    magFilterButtonGroup.add(texMagFilterBaseLinearButton);
    magFilterButtonGroup.add(texMagFilterNicestButton);
    magFilterButtonGroup.add(texMagFilterFastestButton);
    // register the applet as the listener for the buttons
    texMagFilterBasePointButton.addActionListener(this);
    texMagFilterBaseLinearButton.addActionListener(this);
    texMagFilterNicestButton.addActionListener(this);
    texMagFilterFastestButton.addActionListener(this);
    // add the buttons to the panel
    rightPanel.add(texMagFilterBasePointButton);
    rightPanel.add(texMagFilterBaseLinearButton);
    rightPanel.add(texMagFilterNicestButton);
    rightPanel.add(texMagFilterFastestButton);
    // set the default
    texMagFilterBasePointButton.setSelected(true);
    // MipMap Mode
    leftPanel.add(new JLabel("MipMap Mode:"));
    // create the buttons
    JRadioButton texMipMapBaseButton = new JRadioButton(texMipMapBaseString);
    JRadioButton texMipMapMultiButton = new JRadioButton(
        texMipMapMultiString);
    // set up the action commands
    texMipMapBaseButton.setActionCommand(texMipMapBaseString);
    texMipMapMultiButton.setActionCommand(texMipMapMultiString);
    // add the buttons to a group so that only one can be selected
    ButtonGroup texMipMapButtonGroup = new ButtonGroup();
    texMipMapButtonGroup.add(texMipMapBaseButton);
    texMipMapButtonGroup.add(texMipMapMultiButton);
    // register the applet as the listener for the buttons
    texMipMapBaseButton.addActionListener(this);
    texMipMapMultiButton.addActionListener(this);
    // add the buttons to the panel
    leftPanel.add(texMipMapBaseButton);
    leftPanel.add(texMipMapMultiButton);
    // set the default
    texMipMapBaseButton.setSelected(true);
    return panel;
  }
  // The following allows TexBug to be run as an application
  // as well as an applet
  //
  public static void main(String[] args) {
    new MainFrame(new TexBug(true), 650, 800);
  }
}
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();
}