Java/3D/Appearance

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

Appearance Explorer

   <source lang="java">

/*

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

import java.awt.BorderLayout; import java.awt.Color; import java.awt.ruponent; import java.awt.Container; import java.awt.Dimension; import java.awt.Font; import java.awt.GraphicsConfiguration; import java.awt.GridLayout; import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.image.BufferedImage; import java.io.BufferedOutputStream; import java.io.FileOutputStream; import java.text.NumberFormat; import 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.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.Font3D; import javax.media.j3d.FontExtrusion; import javax.media.j3d.GeometryArray; import javax.media.j3d.Group; import javax.media.j3d.ImageComponent; import javax.media.j3d.ImageComponent2D; import javax.media.j3d.IndexedQuadArray; import javax.media.j3d.Light; import javax.media.j3d.LineArray; import javax.media.j3d.LineAttributes; import javax.media.j3d.LineStripArray; import javax.media.j3d.Material; import javax.media.j3d.PointArray; import javax.media.j3d.PointAttributes; import javax.media.j3d.PointLight; import javax.media.j3d.PolygonAttributes; import javax.media.j3d.QuadArray; import javax.media.j3d.RenderingAttributes; import javax.media.j3d.Screen3D; import javax.media.j3d.Shape3D; import javax.media.j3d.Switch; import javax.media.j3d.TexCoordGeneration; import javax.media.j3d.Text3D; import javax.media.j3d.Texture; import javax.media.j3d.Texture2D; import javax.media.j3d.TextureAttributes; import javax.media.j3d.Transform3D; import javax.media.j3d.TransparencyAttributes; import javax.media.j3d.TriangleArray; import javax.media.j3d.TriangleFanArray; import javax.media.j3d.TriangleStripArray; import javax.media.j3d.View; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.JApplet; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JColorChooser; 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.Color4f; import javax.vecmath.Point3d; import javax.vecmath.Point3f; import javax.vecmath.TexCoord2f; import javax.vecmath.Vector3f; import javax.vecmath.Vector4f; 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.loaders.Scene; import com.sun.j3d.loaders.objectfile.ObjectFile; import com.sun.j3d.utils.applet.MainFrame; import com.sun.j3d.utils.behaviors.vp.OrbitBehavior; import com.sun.j3d.utils.geometry.GeometryInfo; import com.sun.j3d.utils.geometry.NormalGenerator; import com.sun.j3d.utils.geometry.Sphere; import com.sun.j3d.utils.geometry.Triangulator; import com.sun.j3d.utils.image.TextureLoader; import com.sun.j3d.utils.universe.SimpleUniverse; import com.sun.j3d.utils.universe.ViewingPlatform; public class AppearanceExplorer extends JApplet implements

   Java3DExplorerConstants {
 // Scene graph items
 SimpleUniverse u;
 Switch sceneSwitch;
 Group beethoven = null;
 Group galleon = null;
 Switch bgSwitch;
 IntChooser bgChooser;
 Appearance appearance;
 // image grabber
 boolean isApplication;
 Canvas3D canvas;
 OffScreenCanvas3D offScreenCanvas;
 View view;
 // ColoringAttributes
 ColoringAttributes coloringAttr;
 ColoringAttributesEditor coloringAttrEditor;
 Color3f coloringColor;
 int coloringShadeModel = ColoringAttributes.SHADE_GOURAUD;
 // PointAttributes
 PointAttributes pointAttr;
 PointAttributesEditor pointAttrEditor;
 float pointSize = 4.0f;
 boolean pointAAEnable = false;
 // LineAttributes
 LineAttributes lineAttr;
 float lineWidth = 1.0f;
 LineAttributesEditor lineAttrEditor;
 boolean lineAAEnable = false;
 int linePattern = LineAttributes.PATTERN_SOLID;
 // PolygonAttributes
 PolygonAttributes polygonAttr;
 PolygonAttributesEditor polygonAttrEditor;
 int polygonMode = PolygonAttributes.POLYGON_FILL;
 int polygonCull = PolygonAttributes.CULL_NONE;
 float polygonOffsetBias = 1.0f;
 float polygonOffsetFactor = 1.0f;
 // RenderingAttributes
 RenderingAttributes renderAttr;
 RenderingAttributesEditor renderAttrEditor;
 boolean renderVisible = true;
 boolean renderDepthBuffer = true;
 boolean renderDepthBufferWrite = true;
 boolean renderIgnoreVertexColor = false;
 boolean renderRasterOpEnable = false;
 int renderRasterOp = RenderingAttributes.ROP_COPY;
 // TransparencyAttributes
 TransparencyAttributes transpAttr;
 TransparencyAttributesEditor transpAttrEditor;
 int transpMode = TransparencyAttributes.NONE;
 float transpValue = 0.5f;
 // Material
 Material material;
 MaterialEditor materialEditor;
 // Texture2D
 Texture2DEditor texture2DEditor;
 boolean texEnable;
 String texImageFile;
 int texBoundaryModeS;
 int texBoundaryModeT;
 Color4f texBoundaryColor;
 int texMinFilter;
 int texMagFilter;
 int texMipMapMode;
 // TextureAttributes
 TextureAttributes textureAttr;
 TextureAttributesEditor textureAttrEditor;
 int texMode;
 Color4f texBlendColor;
 Transform3D texTransform;
 int texPerspCorrect;
 // TexCoordGeneration
 TexCoordGeneration texGen;
 TexCoordGenerationEditor texGenEditor;
 boolean texGenEnable;
 int texGenMode;
 Vector4f texGenPlaneS;
 Vector4f texGenPlaneT;
 // GUI helpers to allow galleon and beethoven to be loaded as needed
 // to reduce the startup time
 int galleonIndex;
 int beethovenIndex;
 String galleonString = "Obj File: Galleon";
 String beethovenString = "Obj File: Beethoven";
 BranchGroup beethovenPlaceholder;
 BranchGroup galleonPlaceholder;
 // Config items
 Switch lightSwitch;
 String snapImageString = "Snap Image";
 String outFileBase = "appear";
 int outFileSeq = 0;
 float offScreenScale = 1.5f;
 // Temporaries that are reused
 Transform3D tmpTrans = new Transform3D();
 Vector3f tmpVector = new Vector3f();
 AxisAngle4f tmpAxisAngle = new AxisAngle4f();
 // 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();
   // ColoringAttributes
   coloringColor = new Color3f(red);
   coloringAttr = new ColoringAttributes(coloringColor, coloringShadeModel);
   coloringAttr.setCapability(ColoringAttributes.ALLOW_COLOR_WRITE);
   coloringAttr.setCapability(ColoringAttributes.ALLOW_SHADE_MODEL_WRITE);
   appearance.setColoringAttributes(coloringAttr);
   // set up the editor
   coloringAttrEditor = new ColoringAttributesEditor(coloringAttr);
   // PointAttributes
   pointAttr = new PointAttributes(pointSize, pointAAEnable);
   pointAttr.setCapability(PointAttributes.ALLOW_SIZE_WRITE);
   pointAttr.setCapability(PointAttributes.ALLOW_ANTIALIASING_WRITE);
   appearance.setPointAttributes(pointAttr);
   // set up the editor
   pointAttrEditor = new PointAttributesEditor(pointAttr);
   // LineAttributes
   lineAttr = new LineAttributes(lineWidth, linePattern, lineAAEnable);
   lineAttr.setCapability(LineAttributes.ALLOW_WIDTH_WRITE);
   lineAttr.setCapability(LineAttributes.ALLOW_PATTERN_WRITE);
   lineAttr.setCapability(LineAttributes.ALLOW_ANTIALIASING_WRITE);
   appearance.setLineAttributes(lineAttr);
   // set up the editor
   lineAttrEditor = new LineAttributesEditor(lineAttr);
   // PolygonAttributes
   polygonAttr = new PolygonAttributes(polygonMode, polygonCull, 0.0f);
   polygonAttr.setPolygonOffset(polygonOffsetBias);
   polygonAttr.setPolygonOffsetFactor(polygonOffsetFactor);
   polygonAttr.setCapability(PolygonAttributes.ALLOW_MODE_WRITE);
   polygonAttr.setCapability(PolygonAttributes.ALLOW_CULL_FACE_WRITE);
   polygonAttr.setCapability(PolygonAttributes.ALLOW_OFFSET_WRITE);
   appearance.setPolygonAttributes(polygonAttr);
   // set up the editor
   polygonAttrEditor = new PolygonAttributesEditor(polygonAttr);
   // Rendering attributes
   renderAttr = new RenderingAttributes(renderDepthBuffer,
       renderDepthBufferWrite, 0.0f, RenderingAttributes.ALWAYS,
       renderVisible, renderIgnoreVertexColor, renderRasterOpEnable,
       renderRasterOp);
   renderAttr
       .setCapability(RenderingAttributes.ALLOW_IGNORE_VERTEX_COLORS_WRITE);
   renderAttr.setCapability(RenderingAttributes.ALLOW_VISIBLE_WRITE);
   renderAttr.setCapability(RenderingAttributes.ALLOW_RASTER_OP_WRITE);
   renderAttr
       .setCapability(RenderingAttributes.ALLOW_ALPHA_TEST_FUNCTION_WRITE);
   renderAttr
       .setCapability(RenderingAttributes.ALLOW_ALPHA_TEST_VALUE_WRITE);
   appearance.setRenderingAttributes(renderAttr);
   appearance.setCapability(Appearance.ALLOW_RENDERING_ATTRIBUTES_WRITE);
   // set up the editor
   renderAttrEditor = new RenderingAttributesEditor(renderAttr);
   // TransparencyAttributes
   transpAttr = new TransparencyAttributes(transpMode, transpValue);
   transpAttr.setCapability(TransparencyAttributes.ALLOW_MODE_WRITE);
   transpAttr.setCapability(TransparencyAttributes.ALLOW_VALUE_WRITE);
   transpAttr
       .setCapability(TransparencyAttributes.ALLOW_BLEND_FUNCTION_WRITE);
   appearance.setTransparencyAttributes(transpAttr);
   // set up the editor
   transpAttrEditor = new TransparencyAttributesEditor(transpAttr);
   // Material
   material = new Material(red, black, red, white, 20.f);
   material.setLightingEnable(false);
   material.setCapability(Material.ALLOW_COMPONENT_WRITE);
   appearance.setMaterial(material);
   // material presets
   String[] materialNames = { "Red", "White", "Red Ambient",
       "Red Diffuse", "Grey Emissive", "White Specular", "Aluminium",
       "Blue Plastic", "Copper", "Gold", "Red Alloy", "Black Onyx" };
   Material[] materialPresets = new Material[materialNames.length];
   materialPresets[0] = new Material(red, black, red, white, 20.0f);
   materialPresets[1] = new Material(white, black, white, white, 20.0f);
   materialPresets[2] = new Material(red, black, black, black, 20.0f);
   materialPresets[3] = new Material(black, black, red, black, 20.0f);
   materialPresets[4] = new Material(black, grey, black, black, 20.0f);
   materialPresets[5] = new Material(black, black, black, white, 20.0f);
   Color3f alum = new Color3f(0.37f, 0.37f, 0.37f);
   Color3f alumSpec = new Color3f(0.89f, 0.89f, 0.89f);
   materialPresets[6] = new Material(alum, black, alum, alumSpec, 17);
   Color3f bluePlastic = new Color3f(0.20f, 0.20f, 0.70f);
   Color3f bluePlasticSpec = new Color3f(0.85f, 0.85f, 0.85f);
   materialPresets[7] = new Material(bluePlastic, black, bluePlastic,
       bluePlasticSpec, 22);
   Color3f copper = new Color3f(0.30f, 0.10f, 0.00f);
   ;
   Color3f copperSpec = new Color3f(0.75f, 0.30f, 0.00f);
   materialPresets[8] = new Material(copper, black, copper, copperSpec, 10);
   Color3f gold = new Color3f(0.49f, 0.34f, 0.00f);
   Color3f goldSpec = new Color3f(0.89f, 0.79f, 0.00f);
   materialPresets[9] = new Material(gold, black, gold, goldSpec, 15);
   Color3f redAlloy = new Color3f(0.34f, 0.00f, 0.34f);
   Color3f redAlloySpec = new Color3f(0.84f, 0.00f, 0.00f);
   materialPresets[10] = new Material(redAlloy, black, redAlloy,
       redAlloySpec, 15);
   Color3f blackOnyxSpec = new Color3f(0.72f, 0.72f, 0.72f);
   materialPresets[11] = new Material(black, black, black, blackOnyxSpec,
       23);
   // set up the editor
   materialEditor = new MaterialPresetEditor(material, materialNames,
       materialPresets);
   // 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(0.0f, 0.0f, 0.0f, 0.0f);
   // set up the image choices
   String[] texImageNames = { "Earth", "Fish", };
   String[] texImageFileNames = { "earth.jpg", "fish1.gif", };
   int texImageFileIndex = 0;
   // set up the appearance to allow the texture to be changed
   appearance.setCapability(Appearance.ALLOW_TEXTURE_WRITE);
   // set up the editor (this will create the initial Texture2D and
   // assign it to the appearance)
   texture2DEditor = new Texture2DEditor(appearance, codeBaseString,
       texImageNames, texImageFileNames, texImageFileIndex, texEnable,
       texBoundaryModeS, texBoundaryModeT, texMinFilter, texMagFilter,
       texMipMapMode, texBoundaryColor);
   // TextureAttributes
   texMode = TextureAttributes.REPLACE;
   texBlendColor = new Color4f(1.0f, 1.0f, 1.0f, 1.0f);
   texTransform = new Transform3D();
   texPerspCorrect = TextureAttributes.NICEST;
   textureAttr = new TextureAttributes(texMode, texTransform,
       texBlendColor, texPerspCorrect);
   // set the capabilities to allow run time changes
   textureAttr.setCapability(TextureAttributes.ALLOW_MODE_WRITE);
   textureAttr.setCapability(TextureAttributes.ALLOW_BLEND_COLOR_WRITE);
   textureAttr.setCapability(TextureAttributes.ALLOW_TRANSFORM_WRITE);
   // connect it to the appearance
   appearance.setTextureAttributes(textureAttr);
   // setup the editor
   textureAttrEditor = new TextureAttributesEditor(textureAttr);
   // set up the tex coordinate generation
   texGenEnable = false;
   texGenMode = TexCoordGeneration.OBJECT_LINEAR;
   texGenPlaneS = new Vector4f(1.0f, 0.0f, 0.0f, 0.0f);
   texGenPlaneT = new Vector4f(0.0f, 1.0f, 0.0f, 0.0f);
   // set the appearance so that we can replace the tex gen when live
   appearance.setCapability(Appearance.ALLOW_TEXGEN_WRITE);
   // setup the editor
   texGenEditor = new TexCoordGenerationEditor(appearance, texGenEnable,
       texGenMode, texGenPlaneS, texGenPlaneT);
 }
 int powerOfTwo(int value) {
   int retval = 2;
   while (retval < value) {
     retval *= 2;
   }
   return retval;
 }
 // Point Array with three points
 Shape3D createPointArray() {
   Point3f pnt[] = new Point3f[3];
   pnt[0] = new Point3f(-1.0f, -1.0f, 0.0f);
   pnt[1] = new Point3f(1.0f, -1.0f, 0.0f);
   pnt[2] = new Point3f(1.0f, 1.0f, 0.0f);
   PointArray pa = new PointArray(3, GeometryArray.COORDINATES);
   pa.setCoordinates(0, pnt);
   return new Shape3D(pa, appearance);
 }
 // Line Array with two lines with vertex colors
 Shape3D createLineArray() {
   Point3f pnt[] = new Point3f[4];
   pnt[0] = new Point3f(-1.0f, -1.0f, 0.0f);
   pnt[1] = new Point3f(1.0f, -1.0f, 0.0f);
   pnt[2] = new Point3f(1.0f, 1.0f, 0.0f);
   pnt[3] = new Point3f(-1.0f, 1.0f, 0.0f);
   Color3f colrs[] = new Color3f[4];
   colrs[0] = black;
   colrs[1] = white;
   colrs[2] = red;
   colrs[3] = green;
   LineArray la = new LineArray(4, GeometryArray.COORDINATES
       | GeometryArray.COLOR_3);
   la.setCoordinates(0, pnt);
   la.setColors(0, colrs);
   return new Shape3D(la, appearance);
 }
 // Triangle Array with one triangle with vertex colors and a facet normal
 Shape3D createTriangleArray() {
   Point3f pnt[] = new Point3f[3];
   pnt[0] = new Point3f(-1.0f, -1.0f, 0.0f);
   pnt[1] = new Point3f(1.0f, -1.0f, 0.0f);
   pnt[2] = new Point3f(1.0f, 1.0f, 0.0f);
   Color3f colrs[] = new Color3f[3];
   colrs[0] = red;
   colrs[1] = green;
   colrs[2] = blue;
   Vector3f norms[] = new Vector3f[3];
   Vector3f triNormal = new Vector3f(0.0f, 0.0f, 1.0f);
   norms[0] = triNormal;
   norms[1] = triNormal;
   norms[2] = triNormal;
   TriangleArray ta = new TriangleArray(3, GeometryArray.COORDINATES
       | GeometryArray.COLOR_3 | GeometryArray.NORMALS);
   ta.setCoordinates(0, pnt);
   ta.setColors(0, colrs);
   ta.setNormals(0, norms);
   return new Shape3D(ta, appearance);
 }
 // Line Strip Array with two lines with 3 and 2 vertices each making
 // a two segment line and a one segment line
 Shape3D createLineStripArray() {
   int[] stripLengths = new int[2];
   stripLengths[0] = 3;
   stripLengths[1] = 2;
   Point3f pnt[] = new Point3f[5];
   // first line
   pnt[0] = new Point3f(-1.0f, -1.0f, 0.0f);
   pnt[1] = new Point3f(1.0f, -1.0f, 0.0f);
   pnt[2] = new Point3f(1.0f, 1.0f, 0.0f);
   // second line
   pnt[3] = new Point3f(0.5f, 0.5f, 0.0f);
   pnt[4] = new Point3f(-0.5f, -0.5f, 0.0f);
   LineStripArray lsa = new LineStripArray(5, GeometryArray.COORDINATES,
       stripLengths);
   lsa.setCoordinates(0, pnt);
   return new Shape3D(lsa, appearance);
 }
 Shape3D createTriangleStripArray() {
   int[] stripLengths = new int[1];
   stripLengths[0] = 5;
   Point3f pnt[] = new Point3f[5];
   pnt[0] = new Point3f(-1.0f, -1.0f, 0.0f);
   pnt[1] = new Point3f(1.0f, -1.0f, 0.0f);
   pnt[2] = new Point3f(-1.0f, 0.0f, 0.0f);
   pnt[3] = new Point3f(1.0f, 0.0f, 0.0f);
   pnt[4] = new Point3f(1.0f, 1.0f, 0.0f);
   TriangleStripArray tsa = new TriangleStripArray(5,
       GeometryArray.COORDINATES, stripLengths);
   tsa.setCoordinates(0, pnt);
   return new Shape3D(tsa, appearance);
 }
 Shape3D createTriangleFanArray() {
   int[] stripLengths = new int[1];
   stripLengths[0] = 5;
   Point3f pnt[] = new Point3f[5];
   pnt[0] = new Point3f(-1.0f, -1.0f, 0.0f);
   pnt[1] = new Point3f(1.0f, -1.0f, 0.0f);
   pnt[2] = new Point3f(1.0f, 0.0f, 0.0f);
   pnt[3] = new Point3f(0.0f, 1.0f, 0.0f);
   pnt[4] = new Point3f(-1.0f, 1.0f, 0.0f);
   TriangleFanArray tfa = new TriangleFanArray(5,
       GeometryArray.COORDINATES, stripLengths);
   tfa.setCoordinates(0, pnt);
   return new Shape3D(tfa, appearance);
 }
 Shape3D createTexTris() {
   Point3f pnt[] = new Point3f[9];
   pnt[0] = new Point3f(-0.8f, -0.8f, 0.0f);
   pnt[1] = new Point3f(-0.5f, -0.7f, 0.0f);
   pnt[2] = new Point3f(-0.7f, 0.7f, 0.0f);
   pnt[3] = new Point3f(-0.4f, 0.7f, 0.0f);
   pnt[4] = new Point3f(0.0f, -0.7f, 0.0f);
   pnt[5] = new Point3f(0.4f, 0.7f, 0.0f);
   pnt[6] = new Point3f(0.5f, 0.7f, 0.0f);
   pnt[7] = new Point3f(0.5f, -0.7f, 0.0f);
   pnt[8] = new Point3f(0.9f, 0.0f, 0.0f);
   TexCoord2f texCoord[] = new TexCoord2f[9];
   texCoord[0] = new TexCoord2f(0.05f, 0.90f);
   texCoord[1] = new TexCoord2f(0.25f, 0.10f);
   texCoord[2] = new TexCoord2f(1.00f, 0.60f);
   texCoord[3] = texCoord[0];
   texCoord[4] = texCoord[1];
   texCoord[5] = texCoord[2];
   texCoord[6] = texCoord[0];
   texCoord[7] = texCoord[1];
   texCoord[8] = texCoord[2];
   TriangleArray ta = new TriangleArray(9, GeometryArray.COORDINATES
       | GeometryArray.TEXTURE_COORDINATE_2);
   ta.setCoordinates(0, pnt);
   ta.setTextureCoordinates(0, 0, texCoord);
   return new Shape3D(ta, appearance);
 }
 Shape3D createTexSquare() {
   // color cube
   Point3f pnt[] = new Point3f[4];
   pnt[0] = new Point3f(-1.0f, -1.0f, 0.0f);
   pnt[1] = new Point3f(1.0f, -1.0f, 0.0f);
   pnt[2] = new Point3f(1.0f, 1.0f, 0.0f);
   pnt[3] = new Point3f(-1.0f, 1.0f, 0.0f);
   TexCoord2f texCoord[] = new TexCoord2f[4];
   texCoord[0] = new TexCoord2f(0.0f, 0.0f);
   texCoord[1] = new TexCoord2f(1.0f, 0.0f);
   texCoord[2] = new TexCoord2f(1.0f, 1.0f);
   texCoord[3] = new TexCoord2f(0.0f, 1.0f);
   QuadArray qa = new QuadArray(4, GeometryArray.COORDINATES
       | GeometryArray.TEXTURE_COORDINATE_2);
   qa.setCoordinates(0, pnt);
   qa.setTextureCoordinates(0, 0, texCoord);
   return new Shape3D(qa, appearance);
 }
 Shape3D createLargeTexSquare() {
   // color cube
   Point3f pnt[] = new Point3f[4];
   pnt[0] = new Point3f(-1.0f, -1.0f, 0.0f);
   pnt[1] = new Point3f(1.0f, -1.0f, 0.0f);
   pnt[2] = new Point3f(1.0f, 1.0f, 0.0f);
   pnt[3] = new Point3f(-1.0f, 1.0f, 0.0f);
   TexCoord2f texCoord[] = new TexCoord2f[4];
   texCoord[0] = new TexCoord2f(-1.0f, -1.0f);
   texCoord[1] = new TexCoord2f(2.0f, -1.0f);
   texCoord[2] = new TexCoord2f(2.0f, 2.0f);
   texCoord[3] = new TexCoord2f(-1.0f, 2.0f);
   QuadArray qa = new QuadArray(4, GeometryArray.COORDINATES
       | GeometryArray.TEXTURE_COORDINATE_2);
   qa.setCoordinates(0, pnt);
   qa.setTextureCoordinates(0, 0, texCoord);
   return new Shape3D(qa, appearance);
 }
 Shape3D createColorCube() {
   // color cube
   int[] indices = { 0, 3, 4, 2, // left face x = -1
       0, 1, 5, 3, // bottom face y = -1
       0, 2, 6, 1, // back face z = -1
       7, 5, 1, 6, // right face x = 1
       7, 6, 2, 4, // top face y = 1
       7, 4, 3, 5 // front face z = 1
   };
   Point3f pts[] = new Point3f[8];
   pts[0] = new Point3f(-1.0f, -1.0f, -1.0f);
   pts[1] = new Point3f(1.0f, -1.0f, -1.0f);
   pts[2] = new Point3f(-1.0f, 1.0f, -1.0f);
   pts[3] = new Point3f(-1.0f, -1.0f, 1.0f);
   pts[4] = new Point3f(-1.0f, 1.0f, 1.0f);
   pts[5] = new Point3f(1.0f, -1.0f, 1.0f);
   pts[6] = new Point3f(1.0f, 1.0f, -1.0f);
   pts[7] = new Point3f(1.0f, 1.0f, 1.0f);
   Color3f colr[] = new Color3f[8];
   colr[0] = black;
   colr[1] = red;
   colr[2] = green;
   colr[3] = blue;
   colr[4] = cyan;
   colr[5] = magenta;
   colr[6] = yellow;
   colr[7] = white;
   // The normals point out from 0,0,0, through the verticies of the
   // cube. These can be calculated by copying the coordinates to
   // a Vector3f and normalizing.
   Vector3f norm[] = new Vector3f[8];
   for (int i = 0; i < 8; i++) {
     norm[i] = new Vector3f(pts[i]);
     norm[i].normalize();
   }
   IndexedQuadArray iqa = new IndexedQuadArray(8,
       GeometryArray.COORDINATES | GeometryArray.COLOR_3
           | GeometryArray.NORMALS, 24);
   iqa.setCoordinates(0, pts);
   iqa.setColors(0, colr);
   iqa.setNormals(0, norm);
   iqa.setCoordinateIndices(0, indices);
   iqa.setColorIndices(0, indices);
   iqa.setNormalIndices(0, indices);
   return new Shape3D(iqa, appearance);
 }
 Shape3D createNGCube(float creaseAngle) {
   // color cube
   int[] indices = { 0, 3, 4, 2, // left face x = -1
       0, 1, 5, 3, // bottom face y = -1
       0, 2, 6, 1, // back face z = -1
       7, 5, 1, 6, // right face x = 1
       7, 6, 2, 4, // top face y = 1
       7, 4, 3, 5 // front face z = 1
   };
   Point3f pts[] = new Point3f[8];
   pts[0] = new Point3f(-1.0f, -1.0f, -1.0f);
   pts[1] = new Point3f(1.0f, -1.0f, -1.0f);
   pts[2] = new Point3f(-1.0f, 1.0f, -1.0f);
   pts[3] = new Point3f(-1.0f, -1.0f, 1.0f);
   pts[4] = new Point3f(-1.0f, 1.0f, 1.0f);
   pts[5] = new Point3f(1.0f, -1.0f, 1.0f);
   pts[6] = new Point3f(1.0f, 1.0f, -1.0f);
   pts[7] = new Point3f(1.0f, 1.0f, 1.0f);
   GeometryInfo gi = new GeometryInfo(GeometryInfo.QUAD_ARRAY);
   gi.setCoordinates(pts);
   gi.setCoordinateIndices(indices);
   NormalGenerator ng = new NormalGenerator();
   ng.setCreaseAngle((float) Math.toRadians(creaseAngle));
   ng.generateNormals(gi);
   GeometryArray cube = gi.getGeometryArray();
   return new Shape3D(cube, appearance);
 }
 Shape3D createTriWithHole() {
   int[] stripCounts = new int[2];
   stripCounts[0] = 3;
   stripCounts[1] = 3;
   int[] contourCounts = new int[1];
   contourCounts[0] = 2;
   Point3f pnt[] = new Point3f[6];
   pnt[0] = new Point3f(-1.0f, -1.0f, 0.0f);
   pnt[1] = new Point3f(1.0f, -1.0f, 0.0f);
   pnt[2] = new Point3f(1.0f, 1.0f, 0.0f);
   pnt[3] = new Point3f(-0.6f, -0.8f, 0.0f);
   pnt[4] = new Point3f(0.8f, 0.6f, 0.0f);
   pnt[5] = new Point3f(0.8f, -0.8f, 0.0f);
   GeometryInfo gi = new GeometryInfo(GeometryInfo.POLYGON_ARRAY);
   gi.setCoordinates(pnt);
   gi.setStripCounts(stripCounts);
   gi.setContourCounts(contourCounts);
   Triangulator tr = new Triangulator();
   tr.triangulate(gi);
   GeometryArray triWithHole = gi.getGeometryArray();
   return new Shape3D(triWithHole, appearance);
 }
 Shape3D createText3D() {
   Font3D f3d = new Font3D(new Font(null, Font.PLAIN, 2),
       new FontExtrusion());
   Text3D t3d = new Text3D(f3d, "Text3D", new Point3f(-3.0f, -1.0f, 0.0f));
   Shape3D textShape = new Shape3D(t3d, appearance);
   return textShape;
 }
 BranchGroup createGalleon() {
   java.net.URL galleonURL = null;
   try {
     galleonURL = new java.net.URL(codeBaseString + "galleon.obj");
   } catch (Exception e) {
     System.err.println("Exception: " + e);
     System.exit(1);
   }
   int flags = ObjectFile.RESIZE;
   ObjectFile f = new ObjectFile(flags);
   Scene s = null;
   try {
     s = f.load(galleonURL);
   } catch (Exception e) {
     System.err.println(e);
     System.exit(1);
   }
   Group sceneGroup = s.getSceneGroup();
   Hashtable namedObjects = s.getNamedObjects();
   Enumeration e = namedObjects.keys();
   while (e.hasMoreElements()) {
     String name = (String) e.nextElement();
     //System.out.println("name = " + name);
     Shape3D shape = (Shape3D) namedObjects.get(name);
     shape.setAppearance(appearance);
   }
   BranchGroup retVal = new BranchGroup();
   retVal.addChild(s.getSceneGroup());
   return retVal;
 }
 BranchGroup createBeethoven() {
   java.net.URL beethovenURL = null;
   try {
     beethovenURL = new java.net.URL(codeBaseString + "beethoven.obj");
   } catch (Exception e) {
     System.err.println("Exception: " + e);
     System.exit(1);
   }
   int flags = ObjectFile.RESIZE;
   ObjectFile f = new ObjectFile(flags);
   Scene s = null;
   try {
     s = f.load(beethovenURL);
   } catch (Exception e) {
     System.err.println(e);
     System.exit(1);
   }
   Group sceneGroup = s.getSceneGroup();
   Hashtable namedObjects = s.getNamedObjects();
   Enumeration e = namedObjects.keys();
   while (e.hasMoreElements()) {
     String name = (String) e.nextElement();
     Shape3D shape = (Shape3D) namedObjects.get(name);
     shape.setAppearance(appearance);
   }
   BranchGroup retVal = new BranchGroup();
   retVal.addChild(s.getSceneGroup());
   return retVal;
 }
 // sets up the scene switch
 void setupSceneSwitch() {
   // create a Switch for the scene, allow switch changes
   sceneSwitch = new Switch();
   sceneSwitch.setCapability(Switch.ALLOW_SWITCH_WRITE);
   sceneSwitch.setCapability(Switch.ALLOW_CHILDREN_READ);
   sceneSwitch.setCapability(Switch.ALLOW_CHILDREN_WRITE);
   sceneSwitch.setCapability(Switch.ALLOW_CHILDREN_EXTEND);
   Shape3D pointArray = createPointArray();
   sceneSwitch.addChild(pointArray);
   Shape3D lineArray = createLineArray();
   sceneSwitch.addChild(lineArray);
   Shape3D triangleArray = createTriangleArray();
   sceneSwitch.addChild(triangleArray);
   Shape3D lineStripArray = createLineStripArray();
   sceneSwitch.addChild(lineStripArray);
   Shape3D triangleStripArray = createTriangleStripArray();
   sceneSwitch.addChild(triangleStripArray);
   Shape3D triangleFanArray = createTriangleFanArray();
   sceneSwitch.addChild(triangleFanArray);
   Shape3D texTris = createTexTris();
   sceneSwitch.addChild(texTris);
   Shape3D texSquare = createTexSquare();
   sceneSwitch.addChild(texSquare);
   Shape3D largeTexSquare = createLargeTexSquare();
   sceneSwitch.addChild(largeTexSquare);
   Shape3D colorCube = createColorCube();
   sceneSwitch.addChild(colorCube);
   Shape3D ngCreaseCube = createNGCube(45);
   sceneSwitch.addChild(ngCreaseCube);
   Shape3D ngSmoothCube = createNGCube(100);
   sceneSwitch.addChild(ngSmoothCube);
   Shape3D triWithHole = createTriWithHole();
   sceneSwitch.addChild(triWithHole);
   // create a sphere with the shared appearance
   Sphere sphere = new Sphere(1.0f, Sphere.GENERATE_NORMALS
       | Sphere.GENERATE_TEXTURE_COORDS, appearance);
   sceneSwitch.addChild(sphere);
   // create a sphere with the shared appearance
   Sphere lrSphere = new Sphere(1.0f, Sphere.GENERATE_NORMALS
       | Sphere.GENERATE_TEXTURE_COORDS, 10, appearance);
   sceneSwitch.addChild(lrSphere);
   // create a sphere with the shared appearance
   Sphere hrSphere = new Sphere(1.0f, Sphere.GENERATE_NORMALS
       | Sphere.GENERATE_TEXTURE_COORDS, 45, appearance);
   sceneSwitch.addChild(hrSphere);
   // Text3D
   Shape3D text3D = createText3D();
   sceneSwitch.addChild(text3D);
   // galleon -- use a placeholder to indicate it hasn"t been loaded yet
   // then load it the first time it gets asked for
   //was:
   //Group galleon = createGalleon();
   //sceneSwitch.addChild(galleon);
   galleonIndex = sceneSwitch.numChildren();
   galleonPlaceholder = new BranchGroup();
   galleonPlaceholder.setCapability(BranchGroup.ALLOW_DETACH);
   sceneSwitch.addChild(galleonPlaceholder);
   // beethoven -- use a placeholder to indicate it hasn"t been loaded yet
   // then load it the first time it gets asked for
   //was:
   //Group beethoven = createBeethoven();
   //sceneSwitch.addChild(beethoven);
   beethovenIndex = sceneSwitch.numChildren();
   beethovenPlaceholder = new BranchGroup();
   beethovenPlaceholder.setCapability(BranchGroup.ALLOW_DETACH);
   sceneSwitch.addChild(beethovenPlaceholder);
 }
 /*
  * 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.
  */
 Group setupLights() {
   Group group = new Group();
   // set up the BoundingSphere for all the lights
   BoundingSphere bounds = new BoundingSphere(new Point3d(), 100.0);
   // Set up the ambient light
   AmbientLight lightAmbient = new AmbientLight(medGrey);
   lightAmbient.setInfluencingBounds(bounds);
   lightAmbient.setCapability(Light.ALLOW_STATE_WRITE);
   group.addChild(lightAmbient);
   lightSwitch = new Switch();
   lightSwitch.setCapability(Switch.ALLOW_SWITCH_WRITE);
   group.addChild(lightSwitch);
   // Set up the directional light
   Vector3f lightDirection1 = new Vector3f(0.0f, 0.0f, -1.0f);
   DirectionalLight lightDirectional1 = new DirectionalLight(white,
       lightDirection1);
   lightDirectional1.setInfluencingBounds(bounds);
   lightDirectional1.setCapability(Light.ALLOW_STATE_WRITE);
   lightSwitch.addChild(lightDirectional1);
   Point3f lightPos1 = new Point3f(-4.0f, 8.0f, 16.0f);
   Point3f lightAttenuation1 = new Point3f(1.0f, 0.0f, 0.0f);
   PointLight pointLight1 = new PointLight(brightWhite, lightPos1,
       lightAttenuation1);
   pointLight1.setInfluencingBounds(bounds);
   lightSwitch.addChild(pointLight1);
   Point3f lightPos2 = new Point3f(-16.0f, 8.0f, 4.0f);
   //Point3f lightPos = new Point3f(-4.0f, 2.0f, 1.0f);
   Point3f lightAttenuation2 = new Point3f(1.0f, 0.0f, 0.0f);
   PointLight pointLight2 = new PointLight(white, lightPos2,
       lightAttenuation2);
   pointLight2.setInfluencingBounds(bounds);
   lightSwitch.addChild(pointLight2);
   return group;
 }
 BranchGroup createSceneGraph() {
   // Create the root of the branch graph
   BranchGroup objRoot = new BranchGroup();
   // Add the primitives to the scene
   setupAppearance();
   setupSceneSwitch();
   objRoot.addChild(sceneSwitch);
   objRoot.addChild(bgSwitch);
   Group lightGroup = setupLights();
   objRoot.addChild(lightGroup);
   return objRoot;
 }
 public AppearanceExplorer() {
   this(false, 1.0f);
 }
 public AppearanceExplorer(boolean isApplication, float initOffScreenScale) {
   this.isApplication = isApplication;
   this.offScreenScale = initOffScreenScale;
 }
 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);
   Container contentPane = getContentPane();
   contentPane.setLayout(new BorderLayout());
   GraphicsConfiguration config = SimpleUniverse
       .getPreferredConfiguration();
   canvas = new Canvas3D(config);
   canvas.setSize(600, 600);
   u = new SimpleUniverse(canvas);
   if (isApplication) {
     offScreenCanvas = new OffScreenCanvas3D(config, true);
     // set the size of the off-screen canvas based on a scale
     // of the on-screen size
     Screen3D sOn = canvas.getScreen3D();
     Screen3D sOff = offScreenCanvas.getScreen3D();
     Dimension dim = sOn.getSize();
     dim.width *= offScreenScale;
     dim.height *= offScreenScale;
     sOff.setSize(dim);
     sOff.setPhysicalScreenWidth(sOn.getPhysicalScreenWidth()
         * offScreenScale);
     sOff.setPhysicalScreenHeight(sOn.getPhysicalScreenHeight()
         * offScreenScale);
     // attach the offscreen canvas to the view
     u.getViewer().getView().addCanvas3D(offScreenCanvas);
   }
   contentPane.add("Center", canvas);
   BackgroundTool bgTool = new BackgroundTool(codeBaseString);
   bgSwitch = bgTool.getSwitch();
   bgChooser = bgTool.getChooser();
   // 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 -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,
       OrbitBehavior.PROPORTIONAL_ZOOM | OrbitBehavior.REVERSE_ROTATE
           | OrbitBehavior.REVERSE_TRANSLATE);
   orbit.setZoomFactor(0.25);
   BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0),
       100.0);
   orbit.setSchedulingBounds(bounds);
   viewingPlatform.setViewPlatformBehavior(orbit);
   u.addBranchGraph(scene);
   contentPane.add("East", 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());
   JTabbedPane tabbedPane = new JTabbedPane();
   tabbedPane.addTab("Setup", setupPanel());
   tabbedPane.addTab("ColoringAttributes", coloringAttrEditor);
   tabbedPane.addTab("PointAttributes", pointAttrEditor);
   tabbedPane.addTab("LineAttributes", lineAttrEditor);
   tabbedPane.addTab("PolygonAttributes", polygonAttrEditor);
   tabbedPane.addTab("RenderingAttributes", renderAttrEditor);
   tabbedPane.addTab("TransparencyAttributes", transpAttrEditor);
   tabbedPane.addTab("Material", materialEditor);
   tabbedPane.addTab("Texture2D", texture2DEditor);
   tabbedPane.addTab("TextureAttributes", textureAttrEditor);
   tabbedPane.addTab("TexCoordGeneration", texGenEditor);
   panel.add("Center", tabbedPane);
   return panel;
 }
 JPanel setupPanel() {
   JPanel panel = new JPanel();
   panel.setLayout(new GridLayout(0, 1));
   // This order of the names must match the cases in the Switch in
   // setupSceneSwitch
   String[] dataNames = { "Point Array", "Line Array", "Triangle Array",
       "Line Strip Array", "Triangle Strip Array",
       "Triangle Fan Array", "Textured Triangles", "Textured Square",
       "Large Texture Square", "Color Cube", "Norm Gen Cube - Crease",
       "Norm Gen Cube - Smooth", "Tri with hole", "Sphere",
       "Low-res Sphere", "High-res Sphere", "Text 3D", galleonString,
       beethovenString, };
   IntChooser dataChooser = new IntChooser("Data:", dataNames);
   dataChooser.addIntListener(new IntListener() {
     public void intChanged(IntEvent event) {
       int value = event.getValue();
       if (sceneSwitch.getChild(value) == beethovenPlaceholder) {
         beethoven = createBeethoven();
         sceneSwitch.setChild(beethoven, beethovenIndex);
       } else if (sceneSwitch.getChild(value) == galleonPlaceholder) {
         galleon = createGalleon();
         sceneSwitch.setChild(galleon, galleonIndex);
       }
       sceneSwitch.setWhichChild(value);
     }
   });
   dataChooser.setValueByName("Sphere");
   panel.add(dataChooser);
   panel.add(bgChooser);
   String[] lightNames = { "Ambient Only", "Directional", "Point Light 1",
       "Point Light 2", };
   int[] lightValues = { Switch.CHILD_NONE, 0, 1, 2 };
   IntChooser lightChooser = new IntChooser("Light:", lightNames,
       lightValues, 0);
   lightChooser.addIntListener(new IntListener() {
     public void intChanged(IntEvent event) {
       int value = event.getValue();
       lightSwitch.setWhichChild(value);
     }
   });
   lightChooser.setValueByName("Point Light 1");
   panel.add(lightChooser);
   panel.add(new JLabel(""));
   if (isApplication) {
     JButton snapButton = new JButton(snapImageString);
     snapButton.setActionCommand(snapImageString);
     snapButton.addActionListener(new ActionListener() {
       public void actionPerformed(ActionEvent e) {
         doSnapshot();
       }
     });
     panel.add(snapButton);
   }
   return panel;
 }
 void doSnapshot() {
   Point loc = canvas.getLocationOnScreen();
   offScreenCanvas.setOffScreenLocation(loc);
   Dimension dim = canvas.getSize();
   dim.width *= offScreenScale;
   dim.height *= offScreenScale;
   nf.setMinimumIntegerDigits(3);
   offScreenCanvas.snapImageFile(outFileBase + nf.format(outFileSeq++),
       dim.width, dim.height);
   nf.setMinimumIntegerDigits(0);
 }
 // The following allows AppearanceExplorer to be run as an application
 // as well as an applet
 //
 public static void main(String[] args) {
   float initOffScreenScale = 2.5f;
   for (int i = 0; i < args.length; i++) {
     if (args[i].equals("-s")) {
       if (args.length >= (i + 1)) {
         initOffScreenScale = Float.parseFloat(args[i + 1]);
         i++;
       }
     }
   }
   new MainFrame(new AppearanceExplorer(true, initOffScreenScale), 950,
       600);
 }

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

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

} class ColoringAttributesEditor extends Box implements Java3DExplorerConstants {

 ColoringAttributes coloringAttr;
 Color3f color = new Color3f();
 int coloringShadeModel;
 public ColoringAttributesEditor(ColoringAttributes init) {
   super(BoxLayout.Y_AXIS);
   coloringAttr = init;
   coloringAttr.getColor(color);
   coloringShadeModel = coloringAttr.getShadeModel();
   String[] shadeNames = { "SHADE_FLAT", "SHADE_GOURAUD", "NICEST",
       "FASTEST" };
   int[] shadeValues = { ColoringAttributes.SHADE_FLAT,
       ColoringAttributes.SHADE_GOURAUD, ColoringAttributes.NICEST,
       ColoringAttributes.FASTEST };
   IntChooser shadeChooser = new IntChooser("Shade model:", shadeNames,
       shadeValues, coloringShadeModel);
   shadeChooser.addIntListener(new IntListener() {
     public void intChanged(IntEvent event) {
       int value = event.getValue();
       coloringAttr.setShadeModel(value);
     }
   });
   add(shadeChooser);
   Color3fEditor colorEditor = new Color3fEditor("Color", color);
   colorEditor.addColor3fListener(new Color3fListener() {
     public void colorChanged(Color3fEvent event) {
       event.getValue(color);
       coloringAttr.setColor(color);
     }
   });
   add(colorEditor);
 }

} class Color3fEditor extends JPanel implements ActionListener,

   Java3DExplorerConstants {
 String name;
 Color3f color = new Color3f();
 JButton button;
 JPanel preview;
 Vector listeners = new Vector();
 public Color3fEditor(String initName, Color3f initColor) {
   name = initName;
   color.set(initColor);
   JLabel label = new JLabel(name);
   preview = new JPanel();
   preview.setPreferredSize(new Dimension(40, 40));
   preview.setBackground(color.get());
   preview.setBorder(BorderFactory.createRaisedBevelBorder());
   button = new JButton("Set");
   button.addActionListener(this);
   JPanel filler = new JPanel();
   filler.setPreferredSize(new Dimension(100, 20));
   setLayout(new BorderLayout());
   Box box = new Box(BoxLayout.X_AXIS);
   add(box, BorderLayout.WEST);
   box.add(label);
   box.add(preview);
   box.add(button);
   box.add(filler);
 }
 public void actionPerformed(ActionEvent e) {
   Color currentColor = color.get();
   Color newColor = JColorChooser.showDialog(this, name, currentColor);
   if (newColor != null) {
     color.set(newColor);
     valueChanged();
   }
 }
 public void setValue(Color3f newValue) {
   boolean changed = !color.equals(newValue);
   if (changed) {
     color.set(newValue);
     valueChanged();
   }
 }
 public void addColor3fListener(Color3fListener listener) {
   listeners.add(listener);
 }
 public void removeColor3fListener(Color3fListener listener) {
   listeners.remove(listener);
 }
 private void valueChanged() {
   // update the preview
   preview.setBackground(color.get());
   // notify the listeners
   Color3fEvent event = new Color3fEvent(this, color);
   for (Enumeration e = listeners.elements(); e.hasMoreElements();) {
     Color3fListener listener = (Color3fListener) e.nextElement();
     listener.colorChanged(event);
   }
 }

} class Color3fEvent extends EventObject {

 Color3f value = new Color3f();
 Color3fEvent(Object source, Color3f newValue) {
   super(source);
   value.set(newValue);
 }
 void getValue(Color3f getValue) {
   getValue.set(value);
 }

} interface Color3fListener extends EventListener {

 void colorChanged(Color3fEvent e);

} class PointAttributesEditor extends Box implements Java3DExplorerConstants {

 // PointAttributes
 PointAttributes pointAttr;
 float pointSize;
 boolean pointAAEnable;
 String pointAAString = "Point AA";
 PointAttributesEditor(PointAttributes init) {
   super(BoxLayout.Y_AXIS);
   pointAttr = init;
   pointSize = pointAttr.getPointSize();
   pointAAEnable = pointAttr.getPointAntialiasingEnable();
   LogFloatLabelJSlider pointSizeSlider = new LogFloatLabelJSlider("Size",
       0.1f, 100.0f, pointSize);
   pointSizeSlider.setMajorTickSpacing(1.0f);
   pointSizeSlider.setPaintTicks(true);
   pointSizeSlider.addFloatListener(new FloatListener() {
     public void floatChanged(FloatEvent e) {
       pointSize = e.getValue();
       pointAttr.setPointSize(pointSize);
     }
   });
   add(pointSizeSlider);
   JCheckBox pointAACheckBox = new JCheckBox(antiAliasString);
   pointAACheckBox.addActionListener(new ActionListener() {
     public void actionPerformed(ActionEvent e) {
       JCheckBox checkbox = (JCheckBox) e.getSource();
       pointAAEnable = checkbox.isSelected();
       pointAttr.setPointAntialiasingEnable(pointAAEnable);
     }
   });
   add(new LeftAlignComponent(pointAACheckBox));
 }

} class LineAttributesEditor extends Box implements Java3DExplorerConstants {

 // LineAttributes
 LineAttributes lineAttr;
 float lineWidth;
 int linePattern;
 boolean lineAAEnable;
 LineAttributesEditor(LineAttributes init) {
   super(BoxLayout.Y_AXIS);
   lineAttr = init;
   lineWidth = lineAttr.getLineWidth();
   linePattern = lineAttr.getLinePattern();
   lineAAEnable = lineAttr.getLineAntialiasingEnable();
   FloatLabelJSlider lineWidthSlider = new FloatLabelJSlider("Width",
       0.1f, 0.0f, 5.0f, lineWidth);
   lineWidthSlider.setMajorTickSpacing(1.0f);
   lineWidthSlider.setPaintTicks(true);
   lineWidthSlider.addFloatListener(new FloatListener() {
     public void floatChanged(FloatEvent e) {
       lineWidth = e.getValue();
       lineAttr.setLineWidth(lineWidth);
     }
   });
   lineWidthSlider.setAlignmentX(Component.LEFT_ALIGNMENT);
   add(lineWidthSlider);
   String[] patternNames = { "PATTERN_SOLID", "PATTERN_DASH",
       "PATTERN_DOT", "PATTERN_DASH_DOT" };
   int[] patternValues = { LineAttributes.PATTERN_SOLID,
       LineAttributes.PATTERN_DASH, LineAttributes.PATTERN_DOT,
       LineAttributes.PATTERN_DASH_DOT };
   IntChooser patternChooser = new IntChooser("Pattern:", patternNames,
       patternValues, linePattern);
   patternChooser.addIntListener(new IntListener() {
     public void intChanged(IntEvent event) {
       int value = event.getValue();
       lineAttr.setLinePattern(value);
     }
   });
   patternChooser.setAlignmentX(Component.LEFT_ALIGNMENT);
   add(patternChooser);
   JCheckBox lineAACheckBox = new JCheckBox(antiAliasString);
   lineAACheckBox.addActionListener(new ActionListener() {
     public void actionPerformed(ActionEvent e) {
       JCheckBox checkbox = (JCheckBox) e.getSource();
       lineAAEnable = checkbox.isSelected();
       lineAttr.setLineAntialiasingEnable(lineAAEnable);
     }
   });
   lineAACheckBox.setAlignmentX(Component.LEFT_ALIGNMENT);
   // add the checkbox to the panel
   add(lineAACheckBox);
 }

} class PolygonAttributesEditor extends Box implements Java3DExplorerConstants {

 // PolygonAttributes
 PolygonAttributes polygonAttr;
 int polygonMode;
 int cullFace;
 float polygonOffset;
 float polygonOffsetFactor;
 boolean backFaceNormalFlip;
 PolygonAttributesEditor(PolygonAttributes init) {
   super(BoxLayout.Y_AXIS);
   polygonAttr = init;
   polygonMode = polygonAttr.getPolygonMode();
   cullFace = polygonAttr.getCullFace();
   polygonOffset = polygonAttr.getPolygonOffset();
   polygonOffsetFactor = polygonAttr.getPolygonOffsetFactor();
   backFaceNormalFlip = polygonAttr.getBackFaceNormalFlip();
   String[] modeNames = { "POLYGON_POINT", "POLYGON_LINE", "POLYGON_FILL", };
   int[] modeValues = { PolygonAttributes.POLYGON_POINT,
       PolygonAttributes.POLYGON_LINE, PolygonAttributes.POLYGON_FILL, };
   IntChooser modeChooser = new IntChooser("Mode:", modeNames, modeValues,
       polygonMode);
   modeChooser.addIntListener(new IntListener() {
     public void intChanged(IntEvent event) {
       polygonMode = event.getValue();
       polygonAttr.setPolygonMode(polygonMode);
     }
   });
   add(modeChooser);
   String[] cullNames = { "CULL_NONE", "CULL_BACK", "CULL_FRONT", };
   int[] cullValues = { PolygonAttributes.CULL_NONE,
       PolygonAttributes.CULL_BACK, PolygonAttributes.CULL_FRONT, };
   IntChooser cullChooser = new IntChooser("Cull:", cullNames, cullValues,
       cullFace);
   cullChooser.addIntListener(new IntListener() {
     public void intChanged(IntEvent event) {
       cullFace = event.getValue();
       polygonAttr.setCullFace(cullFace);
     }
   });
   add(cullChooser);
   FloatLabelJSlider polygonOffsetSlider = new FloatLabelJSlider("Offset",
       0.1f, 0.0f, 2.0f, polygonOffset);
   polygonOffsetSlider.setMajorTickSpacing(1.0f);
   polygonOffsetSlider.setPaintTicks(true);
   polygonOffsetSlider.addFloatListener(new FloatListener() {
     public void floatChanged(FloatEvent e) {
       polygonOffset = e.getValue();
       polygonAttr.setPolygonOffset(polygonOffset);
     }
   });
   add(polygonOffsetSlider);
   LogFloatLabelJSlider polygonOffsetFactorSlider = new LogFloatLabelJSlider(
       "Offset Factor", 0.1f, 10000.0f, polygonOffsetFactor);
   polygonOffsetFactorSlider.addFloatListener(new FloatListener() {
     public void floatChanged(FloatEvent e) {
       polygonOffsetFactor = e.getValue();
       polygonAttr.setPolygonOffsetFactor(polygonOffsetFactor);
     }
   });
   add(polygonOffsetFactorSlider);
   JCheckBox backFaceNormalFlipCheckBox = new JCheckBox(
       "BackFaceNormalFlip", backFaceNormalFlip);
   backFaceNormalFlipCheckBox.addActionListener(new ActionListener() {
     public void actionPerformed(ActionEvent e) {
       JCheckBox checkbox = (JCheckBox) e.getSource();
       backFaceNormalFlip = checkbox.isSelected();
       polygonAttr.setBackFaceNormalFlip(backFaceNormalFlip);
     }
   });
   // no ablity to change without replcing polygon attributes
   backFaceNormalFlipCheckBox.setEnabled(false);
   add(new LeftAlignComponent(backFaceNormalFlipCheckBox));
 }

} class RenderingAttributesEditor extends JPanel implements

   Java3DExplorerConstants {
 // RenderingAttributes
 RenderingAttributes renderingAttr;
 boolean visible;
 boolean depthBufferEnable;
 boolean depthBufferWriteEnable;
 boolean ignoreVertexColors;
 boolean rasterOpEnable;
 int rasterOp;
 int alphaTestFunction;
 float alphaTestValue;
 RenderingAttributesEditor(RenderingAttributes init) {
   renderingAttr = init;
   visible = renderingAttr.getVisible();
   depthBufferEnable = renderingAttr.getDepthBufferEnable();
   depthBufferWriteEnable = renderingAttr.getDepthBufferWriteEnable();
   ignoreVertexColors = renderingAttr.getIgnoreVertexColors();
   rasterOpEnable = renderingAttr.getRasterOpEnable();
   rasterOp = renderingAttr.getRasterOp();
   alphaTestFunction = renderingAttr.getAlphaTestFunction();
   alphaTestValue = renderingAttr.getAlphaTestValue();
   setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
   JCheckBox visibleCheckBox = new JCheckBox("Visible", visible);
   visibleCheckBox.addActionListener(new ActionListener() {
     public void actionPerformed(ActionEvent e) {
       JCheckBox checkbox = (JCheckBox) e.getSource();
       visible = checkbox.isSelected();
       renderingAttr.setVisible(visible);
     }
   });
   // add the checkbox to the panel
   add(new LeftAlignComponent(visibleCheckBox));
   JCheckBox ignoreVertexColorsCheckBox = new JCheckBox(
       "Ignore Vertex Colors", ignoreVertexColors);
   ignoreVertexColorsCheckBox.addActionListener(new ActionListener() {
     public void actionPerformed(ActionEvent e) {
       JCheckBox checkbox = (JCheckBox) e.getSource();
       ignoreVertexColors = checkbox.isSelected();
       renderingAttr.setIgnoreVertexColors(ignoreVertexColors);
     }
   });
   // add the checkbox to the panel
   add(new LeftAlignComponent(ignoreVertexColorsCheckBox));
   JCheckBox depthBufferEnableCheckBox = new JCheckBox(
       "Depth Buffer Enable", depthBufferEnable);
   depthBufferEnableCheckBox.addActionListener(new ActionListener() {
     public void actionPerformed(ActionEvent e) {
       JCheckBox checkbox = (JCheckBox) e.getSource();
       depthBufferEnable = checkbox.isSelected();
       renderingAttr.setDepthBufferEnable(depthBufferEnable);
     }
   });
   // add the checkbox to the panel
   add(new LeftAlignComponent(depthBufferEnableCheckBox));
   // no cap bit for depth buffer enable
   depthBufferEnableCheckBox.setEnabled(false);
   JCheckBox depthBufferWriteEnableCheckBox = new JCheckBox(
       "Depth Buffer Write Enable", depthBufferWriteEnable);
   depthBufferWriteEnableCheckBox.addActionListener(new ActionListener() {
     public void actionPerformed(ActionEvent e) {
       JCheckBox checkbox = (JCheckBox) e.getSource();
       depthBufferWriteEnable = checkbox.isSelected();
       renderingAttr.setDepthBufferWriteEnable(depthBufferWriteEnable);
     }
   });
   // add the checkbox to the panel
   add(new LeftAlignComponent(depthBufferWriteEnableCheckBox));
   // no cap bit for depth buffer enable
   depthBufferWriteEnableCheckBox.setEnabled(false);
   JCheckBox rasterOpEnableCheckBox = new JCheckBox(
       "Raster Operation Enable", rasterOpEnable);
   rasterOpEnableCheckBox.addActionListener(new ActionListener() {
     public void actionPerformed(ActionEvent e) {
       JCheckBox checkbox = (JCheckBox) e.getSource();
       rasterOpEnable = checkbox.isSelected();
       renderingAttr.setRasterOpEnable(rasterOpEnable);
     }
   });
   // add the checkbox to the panel
   add(new LeftAlignComponent(rasterOpEnableCheckBox));
   String[] rasterOpNames = { "ROP_COPY", "ROP_XOR", };
   int[] rasterOpValues = { RenderingAttributes.ROP_COPY,
       RenderingAttributes.ROP_XOR, };
   IntChooser rasterOpChooser = new IntChooser("Raster Operation:",
       rasterOpNames, rasterOpValues, rasterOp);
   rasterOpChooser.addIntListener(new IntListener() {
     public void intChanged(IntEvent event) {
       rasterOp = event.getValue();
       renderingAttr.setRasterOp(rasterOp);
     }
   });
   add(rasterOpChooser);
   String[] alphaTestFunctionNames = { "ALWAYS", "NEVER", "EQUAL",
       "NOT_EQUAL", "LESS", "LESS_OR_EQUAL", "GREATER",
       "GREATER_OR_EQUAL", };
   int[] alphaTestFunctionValues = { RenderingAttributes.ALWAYS,
       RenderingAttributes.NEVER, RenderingAttributes.EQUAL,
       RenderingAttributes.NOT_EQUAL, RenderingAttributes.LESS,
       RenderingAttributes.LESS_OR_EQUAL, RenderingAttributes.GREATER,
       RenderingAttributes.GREATER_OR_EQUAL, };
   IntChooser alphaTestFunctionChooser = new IntChooser(
       "Alpha Test Function:", alphaTestFunctionNames,
       alphaTestFunctionValues, alphaTestFunction);
   alphaTestFunctionChooser.addIntListener(new IntListener() {
     public void intChanged(IntEvent event) {
       alphaTestFunction = event.getValue();
       renderingAttr.setAlphaTestFunction(alphaTestFunction);
     }
   });
   add(alphaTestFunctionChooser);
   FloatLabelJSlider alphaTestValueSlider = new FloatLabelJSlider(
       "Alpha Test Value: ", 0.1f, 0.0f, 1.0f, alphaTestValue);
   alphaTestValueSlider.setMajorTickSpacing(1.0f);
   alphaTestValueSlider.setPaintTicks(true);
   alphaTestValueSlider.addFloatListener(new FloatListener() {
     public void floatChanged(FloatEvent e) {
       alphaTestValue = e.getValue();
       renderingAttr.setAlphaTestValue(alphaTestValue);
     }
   });
   add(alphaTestValueSlider);
 }

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

} class MaterialEditor extends Box implements Java3DExplorerConstants {

 Material material;
 boolean lightingEnable;
 Color3f ambientColor = new Color3f();
 Color3f diffuseColor = new Color3f();
 Color3f emissiveColor = new Color3f();
 Color3f specularColor = new Color3f();
 float shininess;
 JCheckBox lightingEnableCheckBox;
 Color3fEditor ambientEditor;
 Color3fEditor diffuseEditor;
 Color3fEditor emissiveEditor;
 Color3fEditor specularEditor;
 FloatLabelJSlider shininessSlider;
 public MaterialEditor(Material init) {
   super(BoxLayout.Y_AXIS);
   material = init;
   lightingEnable = material.getLightingEnable();
   material.getAmbientColor(ambientColor);
   material.getDiffuseColor(diffuseColor);
   material.getEmissiveColor(emissiveColor);
   material.getSpecularColor(specularColor);
   shininess = material.getShininess();
   lightingEnableCheckBox = new JCheckBox("Lighting Enable",
       lightingEnable);
   lightingEnableCheckBox.addActionListener(new ActionListener() {
     public void actionPerformed(ActionEvent e) {
       JCheckBox checkbox = (JCheckBox) e.getSource();
       lightingEnable = checkbox.isSelected();
       material.setLightingEnable(lightingEnable);
     }
   });
   // add the checkbox to the panel
   add(new LeftAlignComponent(lightingEnableCheckBox));
   ambientEditor = new Color3fEditor("Ambient Color  ", ambientColor);
   ambientEditor.addColor3fListener(new Color3fListener() {
     public void colorChanged(Color3fEvent event) {
       event.getValue(ambientColor);
       material.setAmbientColor(ambientColor);
     }
   });
   add(ambientEditor);
   diffuseEditor = new Color3fEditor("Diffuse Color    ", diffuseColor);
   diffuseEditor.addColor3fListener(new Color3fListener() {
     public void colorChanged(Color3fEvent event) {
       event.getValue(diffuseColor);
       material.setDiffuseColor(diffuseColor);
     }
   });
   add(diffuseEditor);
   emissiveEditor = new Color3fEditor("Emissive Color", emissiveColor);
   emissiveEditor.addColor3fListener(new Color3fListener() {
     public void colorChanged(Color3fEvent event) {
       event.getValue(emissiveColor);
       material.setEmissiveColor(emissiveColor);
     }
   });
   add(emissiveEditor);
   specularEditor = new Color3fEditor("Specular Color ", specularColor);
   specularEditor.addColor3fListener(new Color3fListener() {
     public void colorChanged(Color3fEvent event) {
       event.getValue(specularColor);
       material.setSpecularColor(specularColor);
     }
   });
   add(specularEditor);
   shininessSlider = new FloatLabelJSlider("Shininess: ", 1.0f, 0.0f,
       128.0f, shininess);
   shininessSlider.setMajorTickSpacing(16.0f);
   shininessSlider.setPaintTicks(true);
   shininessSlider.addFloatListener(new FloatListener() {
     public void floatChanged(FloatEvent e) {
       shininess = e.getValue();
       material.setShininess(shininess);
     }
   });
   add(shininessSlider);
 }

} class TransparencyAttributesEditor extends JPanel implements

   Java3DExplorerConstants {
 // TransparencyAttributes
 TransparencyAttributes transpAttr;
 float transparency;
 int mode;
 int srcBlendFunction;
 int dstBlendFunction;
 TransparencyAttributesEditor(TransparencyAttributes init) {
   transpAttr = init;
   transparency = transpAttr.getTransparency();
   mode = transpAttr.getTransparencyMode();
   srcBlendFunction = transpAttr.getSrcBlendFunction();
   dstBlendFunction = transpAttr.getDstBlendFunction();
   setLayout(new GridLayout(4, 1));
   FloatLabelJSlider transparencySlider = new FloatLabelJSlider(
       "Transparency", 0.1f, 0.0f, 1.0f, transparency);
   transparencySlider.setMajorTickSpacing(0.1f);
   transparencySlider.setPaintTicks(true);
   transparencySlider.addFloatListener(new FloatListener() {
     public void floatChanged(FloatEvent e) {
       transparency = e.getValue();
       transpAttr.setTransparency(transparency);
     }
   });
   add(transparencySlider);
   String[] modeNames = { "NONE", "SCREEN_DOOR", "BLENDED", "NICEST",
       "FASTEST" };
   int[] modeValues = { TransparencyAttributes.NONE,
       TransparencyAttributes.SCREEN_DOOR,
       TransparencyAttributes.BLENDED, TransparencyAttributes.NICEST,
       TransparencyAttributes.FASTEST };
   IntChooser modeChooser = new IntChooser("Mode:", modeNames, modeValues,
       mode);
   modeChooser.addIntListener(new IntListener() {
     public void intChanged(IntEvent event) {
       mode = event.getValue();
       transpAttr.setTransparencyMode(mode);
     }
   });
   add(modeChooser);
   String[] blendNames = { "BLEND_ZERO", "BLEND_ONE", "BLEND_SRC_ALPHA",
       "BLEND_ONE_MINUS_SRC_ALPHA" };
   int[] blendValues = { TransparencyAttributes.BLEND_ZERO,
       TransparencyAttributes.BLEND_ONE,
       TransparencyAttributes.BLEND_SRC_ALPHA,
       TransparencyAttributes.BLEND_ONE_MINUS_SRC_ALPHA, };
   IntChooser srcBlendFunctionChooser = new IntChooser("Src Blend Func:",
       blendNames, blendValues, srcBlendFunction);
   srcBlendFunctionChooser.addIntListener(new IntListener() {
     public void intChanged(IntEvent event) {
       srcBlendFunction = event.getValue();
       transpAttr.setSrcBlendFunction(srcBlendFunction);
     }
   });
   add(srcBlendFunctionChooser);
   IntChooser dstBlendFunctionChooser = new IntChooser("Dst Blend Func:",
       blendNames, blendValues, dstBlendFunction);
   dstBlendFunctionChooser.addIntListener(new IntListener() {
     public void intChanged(IntEvent event) {
       dstBlendFunction = event.getValue();
       transpAttr.setDstBlendFunction(dstBlendFunction);
     }
   });
   add(dstBlendFunctionChooser);
 }

} class Texture2DEditor extends Box implements Java3DExplorerConstants {

 Appearance appearance;
 Texture2D texture;
 String codeBaseString;
 String imageFile;
 String[] imageNames;
 String[] imageFileNames;
 int imageIndex;
 TextureLoader texLoader;
 int width;
 int height;
 int format;
 boolean enable;
 Color4f boundaryColor = new Color4f();
 int boundaryModeS;
 int boundaryModeT;
 int minFilter;
 int magFilter;
 int mipMapMode;
 public Texture2DEditor(Appearance app, String codeBaseString,
     String[] texImageNames, String[] texImageFileNames,
     int texImageIndex, boolean texEnable, int texBoundaryModeS,
     int texBoundaryModeT, int texMinFilter, int texMagFilter,
     int texMipMapMode, Color4f texBoundaryColor) {
   super(BoxLayout.Y_AXIS);
   this.appearance = app;
   // TODO: make deep copies?
   this.imageNames = texImageNames;
   this.imageFileNames = texImageFileNames;
   this.imageIndex = texImageIndex;
   this.imageFile = texImageFileNames[texImageIndex];
   this.codeBaseString = codeBaseString;
   this.enable = texEnable;
   this.mipMapMode = texMipMapMode;
   this.boundaryModeS = texBoundaryModeS;
   this.boundaryModeT = texBoundaryModeT;
   this.minFilter = texMinFilter;
   this.magFilter = texMagFilter;
   this.boundaryColor.set(texBoundaryColor);
   // set up the initial texture
   setTexture();
   JCheckBox texEnableCheckBox = new JCheckBox("Enable Texture");
   texEnableCheckBox.addActionListener(new ActionListener() {
     public void actionPerformed(ActionEvent e) {
       enable = ((JCheckBox) e.getSource()).isSelected();
       // workaround for bug
       // should just be able to
       // texture.setEnable(texEnable);
       // instead we have to:
       setTexture();
     }
   });
   // add the checkbox to the panel
   add(new LeftAlignComponent(texEnableCheckBox));
   IntChooser imgChooser = new IntChooser("Image:", imageNames);
   imgChooser.setValue(imageIndex);
   imgChooser.addIntListener(new IntListener() {
     public void intChanged(IntEvent event) {
       imageIndex = event.getValue();
       imageFile = imageFileNames[imageIndex];
       setTexture();
     }
   });
   add(imgChooser);
   // texture boundaries
   String[] boundaryNames = { "WRAP", "CLAMP", };
   int[] boundaryValues = { Texture.WRAP, Texture.CLAMP, };
   // texture boundary S
   IntChooser bndSChooser = new IntChooser("Boundary S Mode:",
       boundaryNames, boundaryValues);
   bndSChooser.setValue(texBoundaryModeS);
   bndSChooser.addIntListener(new IntListener() {
     public void intChanged(IntEvent event) {
       int value = event.getValue();
       boundaryModeS = value;
       setTexture();
     }
   });
   add(bndSChooser);
   // texture boundary T
   IntChooser bndTChooser = new IntChooser("Boundary T Mode:",
       boundaryNames, boundaryValues);
   bndTChooser.setValue(texBoundaryModeT);
   bndTChooser.addIntListener(new IntListener() {
     public void intChanged(IntEvent event) {
       int value = event.getValue();
       boundaryModeT = value;
       setTexture();
     }
   });
   add(bndTChooser);
   // texture min filter
   String[] minFiltNames = { "FASTEST", "NICEST", "BASE_LEVEL_POINT",
       "BASE_LEVEL_LINEAR", "MULTI_LEVEL_POINT", "MULTI_LEVEL_LINEAR", };
   int[] minFiltValues = { Texture.FASTEST, Texture.NICEST,
       Texture.BASE_LEVEL_POINT, Texture.BASE_LEVEL_LINEAR,
       Texture.MULTI_LEVEL_POINT, Texture.MULTI_LEVEL_LINEAR, };
   // min filter
   IntChooser minFiltChooser = new IntChooser("Min Filter:", minFiltNames,
       minFiltValues);
   minFiltChooser.setValue(minFilter);
   minFiltChooser.addIntListener(new IntListener() {
     public void intChanged(IntEvent event) {
       int value = event.getValue();
       minFilter = value;
       setTexture();
     }
   });
   add(minFiltChooser);
   // texture mag filter
   String[] magFiltNames = { "FASTEST", "NICEST", "BASE_LEVEL_POINT",
       "BASE_LEVEL_LINEAR", };
   int[] magFiltValues = { Texture.FASTEST, Texture.NICEST,
       Texture.BASE_LEVEL_POINT, Texture.BASE_LEVEL_LINEAR, };
   // mag filter
   IntChooser magFiltChooser = new IntChooser("Mag Filter:", magFiltNames,
       magFiltValues);
   magFiltChooser.setValue(magFilter);
   magFiltChooser.addIntListener(new IntListener() {
     public void intChanged(IntEvent event) {
       int value = event.getValue();
       magFilter = value;
       setTexture();
     }
   });
   add(magFiltChooser);
   // texture mipmap mode
   String[] mipMapNames = { "BASE_LEVEL", "MULTI_LEVEL_MIPMAP", };
   int[] mipMapValues = { Texture.BASE_LEVEL, Texture.MULTI_LEVEL_MIPMAP, };
   // mipMap mode
   IntChooser mipMapChooser = new IntChooser("MipMap Mode:", mipMapNames,
       mipMapValues);
   mipMapChooser.setValue(mipMapMode);
   mipMapChooser.addIntListener(new IntListener() {
     public void intChanged(IntEvent event) {
       int value = event.getValue();
       mipMapMode = value;
       setTexture();
     }
   });
   add(mipMapChooser);
   Color4fEditor boundaryColorEditor = new Color4fEditor("Boundary Color",
       boundaryColor);
   boundaryColorEditor.addColor4fListener(new Color4fListener() {
     public void colorChanged(Color4fEvent event) {
       event.getValue(boundaryColor);
       setTexture();
     }
   });
   add(boundaryColorEditor);
 }
 // 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 + imageFile);
   } catch (Exception e) {
     System.err.println("Exception: " + e);
     System.exit(1);
   }
   int flags;
   if (mipMapMode == Texture.BASE_LEVEL) {
     flags = 0;
   } else {
     flags = TextureLoader.GENERATE_MIPMAP;
   }
   texLoader = new TextureLoader(imageURL, new String("RGBA"), 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(boundaryColor);
   texture.setBoundaryModeS(boundaryModeS);
   texture.setBoundaryModeT(boundaryModeT);
   texture.setEnable(enable);
   texture.setMinFilter(minFilter);
   texture.setMagFilter(magFilter);
   // 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
   appearance.setTexture(texture);
 }

} class TextureAttributesEditor extends Box implements Java3DExplorerConstants {

 // TextureAttributes
 TextureAttributes textureAttr;
 float transparency;
 int mode;
 int pcMode;
 Color4f blendColor = new Color4f();
 TextureAttributesEditor(TextureAttributes init) {
   super(BoxLayout.Y_AXIS);
   textureAttr = init;
   mode = textureAttr.getTextureMode();
   pcMode = textureAttr.getPerspectiveCorrectionMode();
   textureAttr.getTextureBlendColor(blendColor);
   String[] modeNames = { "REPLACE", "MODULATE", "DECAL", "BLEND", };
   int[] modeValues = { TextureAttributes.REPLACE,
       TextureAttributes.MODULATE, TextureAttributes.DECAL,
       TextureAttributes.BLEND, };
   IntChooser modeChooser = new IntChooser("Mode:", modeNames, modeValues,
       mode);
   modeChooser.addIntListener(new IntListener() {
     public void intChanged(IntEvent event) {
       mode = event.getValue();
       textureAttr.setTextureMode(mode);
     }
   });
   add(modeChooser);
   Color4fEditor blendColorEditor = new Color4fEditor("Blend Color",
       blendColor);
   blendColorEditor.addColor4fListener(new Color4fListener() {
     public void colorChanged(Color4fEvent event) {
       event.getValue(blendColor);
       textureAttr.setTextureBlendColor(blendColor);
     }
   });
   add(blendColorEditor);
   String[] pcModeNames = { "NICEST", "FASTEST", };
   int[] pcModeValues = { TextureAttributes.NICEST,
       TextureAttributes.FASTEST, };
   IntChooser pcModeChooser = new IntChooser("Perspective Correction:",
       pcModeNames, pcModeValues, pcMode);
   pcModeChooser.addIntListener(new IntListener() {
     public void intChanged(IntEvent event) {
       pcMode = event.getValue();
       textureAttr.setPerspectiveCorrectionMode(pcMode);
     }
   });
   add(pcModeChooser);
 }

} /**

* 
* Note: this editor only handles 2D tex gen
*/

class TexCoordGenerationEditor extends Box implements Java3DExplorerConstants {

 // TexCoordGeneration
 Appearance app;
 TexCoordGeneration texGen;
 boolean enable;
 int mode;
 Vector4f planeS = new Vector4f();
 Vector4f planeT = new Vector4f();
 TexCoordGenerationEditor(Appearance initApp, boolean initEnable,
     int initMode, Vector4f initPlaneS, Vector4f initPlaneT) {
   super(BoxLayout.Y_AXIS);
   app = initApp;
   enable = initEnable;
   mode = initMode;
   planeS.set(initPlaneS);
   planeT.set(initPlaneT);
   setTexGen(); // set up the initial texGen
   JCheckBox enableCheckBox = new JCheckBox("Enable Tex Coord Gen");
   enableCheckBox.addActionListener(new ActionListener() {
     public void actionPerformed(ActionEvent e) {
       enable = ((JCheckBox) e.getSource()).isSelected();
       texGen.setEnable(enable);
     }
   });
   add(new LeftAlignComponent(enableCheckBox));
   // texture boundaries
   String[] modeNames = { "OBJECT_LINEAR", "EYE_LINEAR", "SPHERE_MAP", };
   int[] modeValues = { TexCoordGeneration.OBJECT_LINEAR,
       TexCoordGeneration.EYE_LINEAR, TexCoordGeneration.SPHERE_MAP, };
   // tex gen modes
   IntChooser modeChooser = new IntChooser("Generation Mode:", modeNames,
       modeValues);
   modeChooser.setValue(mode);
   modeChooser.addIntListener(new IntListener() {
     public void intChanged(IntEvent event) {
       int value = event.getValue();
       mode = value;
       setTexGen();
     }
   });
   add(modeChooser);
   // make a panel for both sets of sliders and then two sub-panels,
   // one for each group of sliders
   Box sliderPanel = new Box(BoxLayout.Y_AXIS);
   add(sliderPanel);
   Box planeSPanel = new Box(BoxLayout.Y_AXIS);
   Box planeTPanel = new Box(BoxLayout.Y_AXIS);
   sliderPanel.add(planeSPanel);
   sliderPanel.add(planeTPanel);
   planeSPanel.add(new LeftAlignComponent(new JLabel("Plane S:")));
   FloatLabelJSlider planeSxSlider = new FloatLabelJSlider("X:", 0.1f,
       -10.0f, 10.0f, planeS.x);
   planeSxSlider.setMajorTickSpacing(0.1f);
   planeSxSlider.setPaintTicks(true);
   planeSxSlider.addFloatListener(new FloatListener() {
     public void floatChanged(FloatEvent e) {
       planeS.x = e.getValue();
       setTexGen();
     }
   });
   planeSPanel.add(planeSxSlider);
   FloatLabelJSlider planeSySlider = new FloatLabelJSlider("Y:", 0.1f,
       -10.0f, 10.0f, planeS.y);
   planeSySlider.setMajorTickSpacing(0.1f);
   planeSySlider.setPaintTicks(true);
   planeSySlider.addFloatListener(new FloatListener() {
     public void floatChanged(FloatEvent e) {
       planeS.y = e.getValue();
       setTexGen();
     }
   });
   planeSPanel.add(planeSySlider);
   FloatLabelJSlider planeSzSlider = new FloatLabelJSlider("Z:", 0.1f,
       -10.0f, 10.0f, planeS.z);
   planeSzSlider.setMajorTickSpacing(0.1f);
   planeSzSlider.setPaintTicks(true);
   planeSzSlider.addFloatListener(new FloatListener() {
     public void floatChanged(FloatEvent e) {
       planeS.z = e.getValue();
       setTexGen();
     }
   });
   planeSPanel.add(planeSzSlider);
   FloatLabelJSlider planeSwSlider = new FloatLabelJSlider("W:", 0.1f,
       -10.0f, 10.0f, planeS.w);
   planeSwSlider.setMajorTickSpacing(0.1f);
   planeSwSlider.setPaintTicks(true);
   planeSwSlider.addFloatListener(new FloatListener() {
     public void floatChanged(FloatEvent e) {
       planeS.w = e.getValue();
       setTexGen();
     }
   });
   planeSPanel.add(planeSwSlider);
   planeSPanel.add(new LeftAlignComponent(new JLabel("Plane T:")));
   FloatLabelJSlider planeTxSlider = new FloatLabelJSlider("X:", 0.1f,
       -10.0f, 10.0f, planeT.x);
   planeTxSlider.setMajorTickSpacing(0.1f);
   planeTxSlider.setPaintTicks(true);
   planeTxSlider.addFloatListener(new FloatListener() {
     public void floatChanged(FloatEvent e) {
       planeT.x = e.getValue();
       setTexGen();
     }
   });
   planeTPanel.add(planeTxSlider);
   FloatLabelJSlider planeTySlider = new FloatLabelJSlider("Y:", 0.1f,
       -10.0f, 10.0f, planeT.y);
   planeTySlider.setMajorTickSpacing(0.1f);
   planeTySlider.setPaintTicks(true);
   planeTySlider.addFloatListener(new FloatListener() {
     public void floatChanged(FloatEvent e) {
       planeT.y = e.getValue();
       setTexGen();
     }
   });
   planeTPanel.add(planeTySlider);
   FloatLabelJSlider planeTzSlider = new FloatLabelJSlider("Z:", 0.1f,
       -10.0f, 10.0f, planeT.z);
   planeTzSlider.setMajorTickSpacing(0.1f);
   planeTzSlider.setPaintTicks(true);
   planeTzSlider.addFloatListener(new FloatListener() {
     public void floatChanged(FloatEvent e) {
       planeT.z = e.getValue();
       setTexGen();
     }
   });
   planeTPanel.add(planeTzSlider);
   FloatLabelJSlider planeTwSlider = new FloatLabelJSlider("W:", 0.1f,
       -10.0f, 10.0f, planeT.w);
   planeTwSlider.setMajorTickSpacing(0.1f);
   planeTwSlider.setPaintTicks(true);
   planeTwSlider.addFloatListener(new FloatListener() {
     public void floatChanged(FloatEvent e) {
       planeT.w = e.getValue();
       setTexGen();
     }
   });
   planeTPanel.add(planeTwSlider);
 }
 void setTexGen() {
   texGen = new TexCoordGeneration(mode,
       TexCoordGeneration.TEXTURE_COORDINATE_2, planeS, planeT);
   texGen.setCapability(TexCoordGeneration.ALLOW_ENABLE_WRITE);
   texGen.setEnable(enable);
   app.setTexCoordGeneration(texGen);
 }

} class MaterialPresetEditor extends MaterialEditor implements ActionListener {

 String[] materialNames;
 Material[] materialPresets;
 IntChooser presetChooser;
 public MaterialPresetEditor(Material init, String[] presetNames,
     Material[] presets) {
   super(init);
   if ((presetNames.length != presets.length)) {
     throw new IllegalArgumentException(
         "Preset name and value arrays must have the same length");
   }
   materialNames = presetNames;
   materialPresets = presets;
   JPanel presetPanel = new JPanel();
   presetChooser = new IntChooser("Preset:", materialNames);
   presetPanel.add(presetChooser);
   JButton presetCopyButton = new JButton("Copy preset");
   presetCopyButton.addActionListener(this);
   presetPanel.add(presetCopyButton);
   add(new LeftAlignComponent(presetPanel));
 }
 // copy when button is pressed
 public void actionPerformed(ActionEvent e) {
   Material copyMaterial = materialPresets[presetChooser.getValue()];
   lightingEnable = copyMaterial.getLightingEnable();
   copyMaterial.getAmbientColor(ambientColor);
   copyMaterial.getDiffuseColor(diffuseColor);
   copyMaterial.getEmissiveColor(emissiveColor);
   copyMaterial.getSpecularColor(specularColor);
   shininess = copyMaterial.getShininess();
   // update the GUI
   lightingEnableCheckBox.setSelected(lightingEnable);
   material.setLightingEnable(lightingEnable);
   ambientEditor.setValue(ambientColor);
   diffuseEditor.setValue(diffuseColor);
   emissiveEditor.setValue(emissiveColor);
   specularEditor.setValue(specularColor);
   shininessSlider.setValue(shininess);
 }

} class LeftAlignComponent extends JPanel {

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

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

} class LogFloatLabelJSlider extends JPanel implements ChangeListener,

   Java3DExplorerConstants {
 JSlider slider;
 JLabel valueLabel;
 Vector listeners = new Vector();
 float min, max, resolution, current, scale;
 double minLog, maxLog, curLog;
 int minInt, maxInt, curInt;;
 int intDigits, fractDigits;
 NumberFormat nf = NumberFormat.getInstance();
 float minResolution = 0.001f;
 double logBase = Math.log(10);
 // default slider with name, resolution = 0.1, min = 0.0, max = 1.0 inital
 // 0.5
 LogFloatLabelJSlider(String name) {
   this(name, 0.1f, 100.0f, 10.0f);
 }
 LogFloatLabelJSlider(String name, float min, float max, float current) {
   this.resolution = resolution;
   this.min = min;
   this.max = max;
   this.current = current;
   if (resolution < minResolution) {
     resolution = minResolution;
   }
   minLog = log10(min);
   maxLog = log10(max);
   curLog = log10(current);
   // resolution is 100 steps from min to max
   scale = 100.0f;
   resolution = 1.0f / scale;
   // get the integer versions of max, min, current
   minInt = (int) Math.round(minLog * scale);
   maxInt = (int) Math.round(maxLog * scale);
   curInt = (int) Math.round(curLog * scale);
   slider = new JSlider(JSlider.HORIZONTAL, minInt, maxInt, curInt);
   slider.addChangeListener(this);
   valueLabel = new JLabel(" ");
   // Need to muck around to make sure that the width of the label
   // is wide enough for the largest value. Pad the initial string
   // be large enough to hold the largest value.
   int pad = 5; // fudge to make up for variable width fonts
   intDigits = (int) Math.ceil(maxLog) + pad;
   if (min < 0) {
     intDigits++; // add one for the "-"
   }
   if (minLog < 0) {
     fractDigits = (int) Math.ceil(-minLog);
   } else {
     fractDigits = 0;
   }
   nf.setMinimumFractionDigits(fractDigits);
   nf.setMaximumFractionDigits(fractDigits);
   String value = nf.format(current);
   while (value.length() < (intDigits + fractDigits)) {
     value = value + " ";
   }
   valueLabel.setText(value);
   // 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();
   curInt = source.getValue();
   curLog = curInt / scale;
   current = (float) exp10(curLog);
   valueChanged();
 }
 public void setValue(float newValue) {
   boolean changed = (newValue != current);
   current = newValue;
   if (changed) {
     valueChanged();
   }
 }
 private void valueChanged() {
   String value = nf.format(current);
   valueLabel.setText(value);
   // notify the listeners
   FloatEvent event = new FloatEvent(this, current);
   for (Enumeration e = listeners.elements(); e.hasMoreElements();) {
     FloatListener listener = (FloatListener) e.nextElement();
     listener.floatChanged(event);
   }
 }
 double log10(double value) {
   return Math.log(value) / logBase;
 }
 double exp10(double value) {
   return Math.exp(value * logBase);
 }

} interface Color4fListener extends EventListener {

 void colorChanged(Color4fEvent e);

} class Color4fEvent extends EventObject {

 Color4f value = new Color4f();
 Color4fEvent(Object source, Color4f newValue) {
   super(source);
   value.set(newValue);
 }
 void getValue(Color4f getValue) {
   getValue.set(value);
 }

} class Color4fEditor extends Box implements ActionListener,

   Java3DExplorerConstants {
 String name;
 Color4f color = new Color4f();
 Color3f color3f = new Color3f(); // just RGB of Color4f
 JButton button;
 JPanel preview;
 Vector listeners = new Vector();
 public Color4fEditor(String initName, Color4f initColor) {
   super(BoxLayout.Y_AXIS);
   name = initName;
   color.set(initColor);
   color3f.x = color.x;
   color3f.y = color.y;
   color3f.z = color.z;
   JPanel colorPanel = new JPanel();
   colorPanel.setLayout(new BorderLayout());
   add(colorPanel);
   JLabel label = new JLabel(name);
   preview = new JPanel();
   preview.setPreferredSize(new Dimension(40, 40));
   preview.setBackground(color3f.get());
   preview.setBorder(BorderFactory.createRaisedBevelBorder());
   button = new JButton("Set");
   button.addActionListener(this);
   JPanel filler = new JPanel();
   filler.setPreferredSize(new Dimension(100, 20));
   Box box = new Box(BoxLayout.X_AXIS);
   colorPanel.add(box, BorderLayout.WEST);
   box.add(label);
   box.add(preview);
   box.add(button);
   box.add(filler);
   FloatLabelJSlider alphaSlider = new FloatLabelJSlider("    Alpha");
   alphaSlider.setValue(color.w);
   alphaSlider.addFloatListener(new FloatListener() {
     public void floatChanged(FloatEvent event) {
       color.w = event.getValue();
       valueChanged();
     }
   });
   add(alphaSlider);
 }
 public void actionPerformed(ActionEvent e) {
   Color currentColor = color3f.get();
   Color newColor = JColorChooser.showDialog(this, name, currentColor);
   if (newColor != null) {
     color3f.set(newColor);
     color.x = color3f.x;
     color.y = color3f.y;
     color.z = color3f.z;
     valueChanged();
   }
 }
 public void setValue(Color4f newValue) {
   boolean changed = !color.equals(newValue);
   if (changed) {
     color.set(newValue);
     color3f.x = color.x;
     color3f.y = color.y;
     color3f.z = color.z;
     valueChanged();
   }
 }
 public void addColor4fListener(Color4fListener listener) {
   listeners.add(listener);
 }
 public void removeColor4fListener(Color4fListener listener) {
   listeners.remove(listener);
 }
 private void valueChanged() {
   // update the preview
   preview.setBackground(color3f.get());
   // notify the listeners
   Color4fEvent event = new Color4fEvent(this, color);
   for (Enumeration e = listeners.elements(); e.hasMoreElements();) {
     Color4fListener listener = (Color4fListener) e.nextElement();
     listener.colorChanged(event);
   }
 }

}


      </source>
   
  
 
  



PolygonOffset

   <source lang="java">

/*

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

import java.applet.Applet; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.GraphicsConfiguration; import java.awt.GridBagLayout; import java.awt.GridLayout; import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.image.BufferedImage; import java.io.BufferedOutputStream; import java.io.FileOutputStream; import java.text.NumberFormat; import java.util.Enumeration; import java.util.EventListener; import java.util.EventObject; import java.util.Hashtable; import java.util.Vector; import javax.media.j3d.Alpha; import javax.media.j3d.Appearance; import javax.media.j3d.Background; import javax.media.j3d.BoundingSphere; import javax.media.j3d.BranchGroup; import javax.media.j3d.Canvas3D; import javax.media.j3d.ColoringAttributes; import javax.media.j3d.ImageComponent; import javax.media.j3d.ImageComponent2D; import javax.media.j3d.PolygonAttributes; import javax.media.j3d.RotationInterpolator; import javax.media.j3d.Screen3D; import javax.media.j3d.Transform3D; import javax.media.j3d.TransformGroup; import javax.media.j3d.View; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JSlider; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.vecmath.AxisAngle4f; import javax.vecmath.Color3f; import javax.vecmath.Point3d; import javax.vecmath.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.geometry.Sphere; import com.sun.j3d.utils.universe.SimpleUniverse; import com.sun.j3d.utils.universe.ViewingPlatform; public class PolygonOffset extends Applet implements Java3DExplorerConstants {

 SimpleUniverse u;
 boolean isApplication;
 Canvas3D canvas;
 OffScreenCanvas3D offScreenCanvas;
 View view;
 PolygonAttributes solidPa;
 PolygonAttributes wirePa;
 float dynamicOffset = 1.0f;
 float staticOffset = 1.0f;
 ViewingPlatform viewingPlatform;
 float innerScale = 0.94f;
 TransformGroup innerTG;
 Transform3D scale;
 float sphereRadius = 0.9f;
 String outFileBase = "offset";
 int outFileSeq = 0;
 float offScreenScale = 1.0f;
 public BranchGroup createSceneGraph() {
   // Create the root of the branch graph
   BranchGroup objRoot = new BranchGroup();
   // Create the transform group node and initialize it to the
   // identity. Enable the TRANSFORM_WRITE capability so that
   // our behavior code can modify it at runtime. Add it to the
   // root of the subgraph.
   TransformGroup objTrans = new TransformGroup();
   objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
   objRoot.addChild(objTrans);
   // Create a Sphere. We will display this as both wireframe and
   // solid to make a hidden line display
   // wireframe
   Appearance wireApp = new Appearance();
   ColoringAttributes wireCa = new ColoringAttributes();
   wireCa.setColor(black);
   wireApp.setColoringAttributes(wireCa);
   wirePa = new PolygonAttributes(PolygonAttributes.POLYGON_LINE,
       PolygonAttributes.CULL_BACK, 0.0f);
   wireApp.setPolygonAttributes(wirePa);
   Sphere outWireSphere = new Sphere(sphereRadius, 0, 15, wireApp);
   objTrans.addChild(outWireSphere);
   // solid
   ColoringAttributes outCa = new ColoringAttributes(red,
       ColoringAttributes.SHADE_FLAT);
   Appearance outSolid = new Appearance();
   outSolid.setColoringAttributes(outCa);
   solidPa = new PolygonAttributes(PolygonAttributes.POLYGON_FILL,
       PolygonAttributes.CULL_BACK, 0.0f);
   solidPa.setPolygonOffsetFactor(dynamicOffset);
   solidPa.setPolygonOffset(staticOffset);
   solidPa.setCapability(PolygonAttributes.ALLOW_OFFSET_WRITE);
   outSolid.setPolygonAttributes(solidPa);
   Sphere outSolidSphere = new Sphere(sphereRadius, 0, 15, outSolid);
   objTrans.addChild(outSolidSphere);
   innerTG = new TransformGroup();
   innerTG.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
   scale = new Transform3D();
   updateInnerScale();
   objTrans.addChild(innerTG);
   // Create a smaller sphere to go inside. This sphere has a different
   // tesselation and color
   Sphere inWireSphere = new Sphere(sphereRadius, 0, 10, wireApp);
   innerTG.addChild(inWireSphere);
   // inside solid
   ColoringAttributes inCa = new ColoringAttributes(blue,
       ColoringAttributes.SHADE_FLAT);
   Appearance inSolid = new Appearance();
   inSolid.setColoringAttributes(inCa);
   inSolid.setPolygonAttributes(solidPa);
   Sphere inSolidSphere = new Sphere(sphereRadius, 0, 10, inSolid);
   innerTG.addChild(inSolidSphere);
   // Create a new Behavior object that will perform the desired
   // operation on the specified transform object and add it into
   // the scene graph.
   AxisAngle4f axisAngle = new AxisAngle4f(0.0f, 0.0f, 1.0f,
       -(float) Math.PI / 2.0f);
   Transform3D yAxis = new Transform3D();
   Alpha rotationAlpha = new Alpha(-1, Alpha.INCREASING_ENABLE, 0, 0,
       80000, 0, 0, 0, 0, 0);
   RotationInterpolator rotator = new RotationInterpolator(rotationAlpha,
       objTrans, yAxis, 0.0f, (float) Math.PI * 2.0f);
   BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0),
       100.0);
   rotator.setSchedulingBounds(bounds);
   objTrans.addChild(rotator);
   // set up a white background
   Background bgWhite = new Background(new Color3f(1.0f, 1.0f, 1.0f));
   bgWhite.setApplicationBounds(bounds);
   objTrans.addChild(bgWhite);
   // Have Java 3D perform optimizations on this scene graph.
   objRoot.rupile();
   return objRoot;
 }
 void updateInnerScale() {
   scale.set(innerScale);
   innerTG.setTransform(scale);
 }
 public PolygonOffset() {
   this(false);
 }
 public PolygonOffset(boolean isApplication) {
   this.isApplication = isApplication;
 }
 public void init() {
   setLayout(new BorderLayout());
   GraphicsConfiguration config = SimpleUniverse
       .getPreferredConfiguration();
   JPanel canvasPanel = new JPanel();
   GridBagLayout gridbag = new GridBagLayout();
   canvasPanel.setLayout(gridbag);
   canvas = new Canvas3D(config);
   canvas.setSize(600, 600);
   add(canvas, BorderLayout.CENTER);
   u = new SimpleUniverse(canvas);
   if (isApplication) {
     offScreenCanvas = new OffScreenCanvas3D(config, true);
     // set the size of the off-screen canvas based on a scale
     // of the on-screen size
     Screen3D sOn = canvas.getScreen3D();
     Screen3D sOff = offScreenCanvas.getScreen3D();
     Dimension dim = sOn.getSize();
     dim.width *= offScreenScale;
     dim.height *= offScreenScale;
     sOff.setSize(dim);
     sOff.setPhysicalScreenWidth(sOn.getPhysicalScreenWidth()
         * offScreenScale);
     sOff.setPhysicalScreenHeight(sOn.getPhysicalScreenHeight()
         * offScreenScale);
     // attach the offscreen canvas to the view
     u.getViewer().getView().addCanvas3D(offScreenCanvas);
   }
   // Create a simple scene and attach it to the virtual universe
   BranchGroup scene = createSceneGraph();
   // set the eye at z = 2.0
   viewingPlatform = u.getViewingPlatform();
   Transform3D vpTrans = new Transform3D();
   vpTrans.set(new Vector3f(0.0f, 0.0f, 2.0f));
   viewingPlatform.getViewPlatformTransform().setTransform(vpTrans);
   // set up a parallel projection with clip limits at 1 and -1
   view = u.getViewer().getView();
   view.setProjectionPolicy(View.PARALLEL_PROJECTION);
   view.setFrontClipPolicy(View.VIRTUAL_EYE);
   view.setBackClipPolicy(View.VIRTUAL_EYE);
   view.setFrontClipDistance(1.0f);
   view.setBackClipDistance(3.0f);
   u.addBranchGraph(scene);
   // set up the sliders
   JPanel guiPanel = new JPanel();
   guiPanel.setLayout(new GridLayout(0, 1));
   FloatLabelJSlider dynamicSlider = new FloatLabelJSlider(
       "Dynamic Offset", 0.1f, 0.0f, 2.0f, dynamicOffset);
   dynamicSlider.addFloatListener(new FloatListener() {
     public void floatChanged(FloatEvent e) {
       dynamicOffset = e.getValue();
       solidPa.setPolygonOffsetFactor(dynamicOffset);
     }
   });
   guiPanel.add(dynamicSlider);
   LogFloatLabelJSlider staticSlider = new LogFloatLabelJSlider(
       "Static Offset", 0.1f, 10000.0f, staticOffset);
   staticSlider.addFloatListener(new FloatListener() {
     public void floatChanged(FloatEvent e) {
       staticOffset = e.getValue();
       solidPa.setPolygonOffset(staticOffset);
     }
   });
   guiPanel.add(staticSlider);
   FloatLabelJSlider innerSphereSlider = new FloatLabelJSlider(
       "Inner Sphere Scale", 0.001f, 0.90f, 1.0f, innerScale);
   innerSphereSlider.addFloatListener(new FloatListener() {
     public void floatChanged(FloatEvent e) {
       innerScale = e.getValue();
       updateInnerScale();
     }
   });
   guiPanel.add(innerSphereSlider);
   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 *= offScreenScale;
         dim.height *= offScreenScale;
         nf.setMinimumIntegerDigits(3);
         offScreenCanvas.snapImageFile(outFileBase
             + nf.format(outFileSeq++), dim.width, dim.height);
         nf.setMinimumIntegerDigits(0);
       }
     });
     guiPanel.add(snapButton);
   }
   add(guiPanel, BorderLayout.EAST);
 }
 public void destroy() {
   u.removeAllLocales();
 }
 //
 // The following allows PolygonOffset to be run as an application
 // as well as an applet
 //
 public static void main(String[] args) {
   new MainFrame(new PolygonOffset(true), 950, 600);
 }

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

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

} class LogFloatLabelJSlider extends JPanel implements ChangeListener,

   Java3DExplorerConstants {
 JSlider slider;
 JLabel valueLabel;
 Vector listeners = new Vector();
 float min, max, resolution, current, scale;
 double minLog, maxLog, curLog;
 int minInt, maxInt, curInt;;
 int intDigits, fractDigits;
 NumberFormat nf = NumberFormat.getInstance();
 float minResolution = 0.001f;
 double logBase = Math.log(10);
 // default slider with name, resolution = 0.1, min = 0.0, max = 1.0 inital
 // 0.5
 LogFloatLabelJSlider(String name) {
   this(name, 0.1f, 100.0f, 10.0f);
 }
 LogFloatLabelJSlider(String name, float min, float max, float current) {
   this.resolution = resolution;
   this.min = min;
   this.max = max;
   this.current = current;
   if (resolution < minResolution) {
     resolution = minResolution;
   }
   minLog = log10(min);
   maxLog = log10(max);
   curLog = log10(current);
   // resolution is 100 steps from min to max
   scale = 100.0f;
   resolution = 1.0f / scale;
   // get the integer versions of max, min, current
   minInt = (int) Math.round(minLog * scale);
   maxInt = (int) Math.round(maxLog * scale);
   curInt = (int) Math.round(curLog * scale);
   slider = new JSlider(JSlider.HORIZONTAL, minInt, maxInt, curInt);
   slider.addChangeListener(this);
   valueLabel = new JLabel(" ");
   // Need to muck around to make sure that the width of the label
   // is wide enough for the largest value. Pad the initial string
   // be large enough to hold the largest value.
   int pad = 5; // fudge to make up for variable width fonts
   intDigits = (int) Math.ceil(maxLog) + pad;
   if (min < 0) {
     intDigits++; // add one for the "-"
   }
   if (minLog < 0) {
     fractDigits = (int) Math.ceil(-minLog);
   } else {
     fractDigits = 0;
   }
   nf.setMinimumFractionDigits(fractDigits);
   nf.setMaximumFractionDigits(fractDigits);
   String value = nf.format(current);
   while (value.length() < (intDigits + fractDigits)) {
     value = value + " ";
   }
   valueLabel.setText(value);
   // 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();
   curInt = source.getValue();
   curLog = curInt / scale;
   current = (float) exp10(curLog);
   valueChanged();
 }
 public void setValue(float newValue) {
   boolean changed = (newValue != current);
   current = newValue;
   if (changed) {
     valueChanged();
   }
 }
 private void valueChanged() {
   String value = nf.format(current);
   valueLabel.setText(value);
   // notify the listeners
   FloatEvent event = new FloatEvent(this, current);
   for (Enumeration e = listeners.elements(); e.hasMoreElements();) {
     FloatListener listener = (FloatListener) e.nextElement();
     listener.floatChanged(event);
   }
 }
 double log10(double value) {
   return Math.log(value) / logBase;
 }
 double exp10(double value) {
   return Math.exp(value * logBase);
 }

}


      </source>
   
  
 
  



Viewer

   <source lang="java">

/*

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

import java.applet.Applet; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.GraphicsConfiguration; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.GridLayout; import java.awt.Insets; import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.image.BufferedImage; import java.io.BufferedOutputStream; import java.io.FileOutputStream; import java.text.NumberFormat; import java.util.Enumeration; import java.util.EventListener; import java.util.EventObject; import java.util.Hashtable; import java.util.Vector; import javax.media.j3d.Alpha; import javax.media.j3d.Appearance; import javax.media.j3d.Background; import javax.media.j3d.BoundingSphere; import javax.media.j3d.BranchGroup; import javax.media.j3d.Canvas3D; import javax.media.j3d.ColoringAttributes; import javax.media.j3d.ImageComponent; import javax.media.j3d.ImageComponent2D; import javax.media.j3d.LineArray; import javax.media.j3d.LineAttributes; import javax.media.j3d.LineStripArray; import javax.media.j3d.PolygonAttributes; import javax.media.j3d.RotationInterpolator; import javax.media.j3d.Screen3D; import javax.media.j3d.Shape3D; import javax.media.j3d.Transform3D; import javax.media.j3d.TransformGroup; import javax.media.j3d.TriangleFanArray; import javax.media.j3d.View; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JSlider; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.vecmath.AxisAngle4f; import javax.vecmath.Color3f; import javax.vecmath.Matrix4d; import javax.vecmath.Point3d; import javax.vecmath.Point3f; import javax.vecmath.Vector3f; import javax.vecmath.Vector4d; 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.geometry.Sphere; import com.sun.j3d.utils.universe.SimpleUniverse; import com.sun.j3d.utils.universe.ViewingPlatform; public class ViewProj extends Applet implements Java3DExplorerConstants {

 PolygonAttributes solidPa;
 PolygonAttributes wirePa;
 JSlider dynamicOffsetSlider;
 JSlider staticOffsetSlider;
 JLabel dynamicSliderValueLabel;
 JLabel staticSliderValueLabel;
 float dynamicOffset = 1.0f;
 float staticOffset = 1.0f;
 float frontClipDist = 1.413f;
 float backClipDist = 3.309f;
 float backClipRatio = backClipDist / frontClipDist;
 View view;
 ViewingPlatform viewingPlatform;
 float innerScale = 0.94f;
 TransformGroup innerTG;
 Transform3D scale;
 Transform3D projTrans = new Transform3D();
 int numClipGridPts;
 int maxClipGridPts = 180;
 Point3f[] clipGridPtsVW = new Point3f[maxClipGridPts];
 Point3f[] clipGridPtsProj = new Point3f[maxClipGridPts];
 int numCirclePts = 36;
 Point3f[] circlePtsVW = new Point3f[numCirclePts];
 Point3f[] circlePtsProj = new Point3f[numCirclePts];
 Point3f eyePtVW = new Point3f();
 float fov;
 float sphereRadius = 0.85f;
 BranchGroup urScene;
 BranchGroup lrScene;
 SimpleUniverse urUniverse;
 SimpleUniverse lrUniverse;
 boolean isApplication;
 Canvas3D canvas;
 Canvas3D urCanvas;
 Canvas3D lrCanvas;
 OffScreenCanvas3D offScreenCanvas;
 OffScreenCanvas3D urOffScreenCanvas;
 OffScreenCanvas3D lrOffScreenCanvas;
 String snapImageString = "Snap Main";
 String urSnapImageString = "Snap UR";
 String lrSnapImageString = "Snap LR";
 String outFileBase = "vproj";
 int outFileSeq = 0;
 float offScreenScale = 1.0f;
 String urOutFileBase = "vprojur";
 int urOutFileSeq = 0;
 float urOffScreenScale = 1.0f;
 String lrOutFileBase = "vprojlr";
 int lrOutFileSeq = 0;
 float lrOffScreenScale = 1.0f;
 NumberFormat nf;
 Vector4d projPt = new Vector4d();
 public BranchGroup createSceneGraph() {
   // Create the root of the branch graph
   BranchGroup objRoot = new BranchGroup();
   // Create the transform group node and initialize it to the
   // identity. Enable the TRANSFORM_WRITE capability so that
   // our behavior code can modify it at runtime. Add it to the
   // root of the subgraph.
   TransformGroup objTrans = new TransformGroup();
   objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
   objRoot.addChild(objTrans);
   // Create a Sphere. We will display this as both wireframe and
   // solid to make a hidden line display
   // wireframe
   Appearance wireApp = new Appearance();
   ColoringAttributes ca = new ColoringAttributes(black,
       ColoringAttributes.SHADE_FLAT);
   wireApp.setColoringAttributes(ca);
   wirePa = new PolygonAttributes(PolygonAttributes.POLYGON_LINE,
       PolygonAttributes.CULL_BACK, 0.0f);
   wireApp.setPolygonAttributes(wirePa);
   Sphere outWireSphere = new Sphere(sphereRadius, 0, 10, wireApp);
   objTrans.addChild(outWireSphere);
   // solid
   ColoringAttributes outCa = new ColoringAttributes(red,
       ColoringAttributes.SHADE_FLAT);
   Appearance outSolid = new Appearance();
   outSolid.setColoringAttributes(outCa);
   solidPa = new PolygonAttributes(PolygonAttributes.POLYGON_FILL,
       PolygonAttributes.CULL_BACK, 0.0f);
   solidPa.setPolygonOffsetFactor(dynamicOffset);
   solidPa.setPolygonOffset(staticOffset);
   solidPa.setCapability(PolygonAttributes.ALLOW_OFFSET_WRITE);
   outSolid.setPolygonAttributes(solidPa);
   Sphere outSolidSphere = new Sphere(sphereRadius, 0, 10, outSolid);
   objTrans.addChild(outSolidSphere);
   innerTG = new TransformGroup();
   innerTG.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
   scale = new Transform3D();
   updateInnerScale();
   objTrans.addChild(innerTG);
   // Create a smaller sphere to go inside. This sphere has a different
   // tesselation and color
   Sphere inWireSphere = new Sphere(sphereRadius, 0, 15, wireApp);
   innerTG.addChild(inWireSphere);
   // inside solid
   ColoringAttributes inCa = new ColoringAttributes(blue,
       ColoringAttributes.SHADE_FLAT);
   Appearance inSolid = new Appearance();
   inSolid.setColoringAttributes(inCa);
   inSolid.setPolygonAttributes(solidPa);
   Sphere inSolidSphere = new Sphere(sphereRadius, 0, 15, inSolid);
   innerTG.addChild(inSolidSphere);
   // Create a new Behavior object that will perform the desired
   // operation on the specified transform object and add it into
   // the scene graph.
   AxisAngle4f axisAngle = new AxisAngle4f(0.0f, 0.0f, 1.0f,
       -(float) Math.PI / 2.0f);
   Transform3D yAxis = new Transform3D();
   Alpha rotationAlpha = new Alpha(-1, Alpha.INCREASING_ENABLE, 0, 0,
       80000, 0, 0, 0, 0, 0);
   RotationInterpolator rotator = new RotationInterpolator(rotationAlpha,
       objTrans, yAxis, 0.0f, (float) Math.PI * 2.0f);
   BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0),
       100.0);
   rotator.setSchedulingBounds(bounds);
   //objTrans.addChild(rotator);
   Background bgWhite = new Background(white);
   bgWhite.setApplicationBounds(bounds);
   objTrans.addChild(bgWhite);
   // Have Java 3D perform optimizations on this scene graph.
   objRoot.rupile();
   return objRoot;
 }
 void updateInnerScale() {
   scale.set(innerScale);
   innerTG.setTransform(scale);
 }
 public BranchGroup createVWorldViewSG() {
   // Create the root of the branch graph
   BranchGroup objRoot = new BranchGroup();
   objRoot.setCapability(BranchGroup.ALLOW_DETACH);
   // setup a transform group to hold the scaled scene
   TransformGroup objTrans = new TransformGroup();
   objRoot.addChild(objTrans);
   // get the eye point, field of view and clip distances
   float fov = (float) view.getFieldOfView();
   // figure out the angle factors to find points along the edges
   // of the FOV
   // X = fovSpreadX * (Y - eyeVW.y) + eyeVW.x;
   float fovSpreadX = (float) Math.tan(fov / 2);
   // Z = fovSpreadZ * (X - eyeVW.x) + eyeVW.z;
   float fovSpreadZ = 1.0f / fovSpreadX;
   //System.out.println("fovSpreadX = " + fovSpreadX);
   //System.out.println("fovSpreadZ = " + fovSpreadZ);
   Transform3D vpTransform = new Transform3D();
   viewingPlatform.getViewPlatformTransform().getTransform(vpTransform);
   Vector3f vpTranslation = new Vector3f();
   vpTransform.get(vpTranslation);
   eyePtVW.set(vpTranslation);
   eyePtVW.negate();
   // get the eye point in our 2D coord system.
   Point3f eyePt = new Point3f(0.0f, eyePtVW.z, 0.1f);
   float frontClipDist = (float) view.getFrontClipDistance();
   float backClipDist = (float) view.getBackClipDistance();
   // set up the clip plane lines
   Point3f[] cpPoints = new Point3f[5];
   cpPoints[0] = new Point3f(frontClipDist * fovSpreadX, eyePtVW.z
       + frontClipDist, 0.1f);
   cpPoints[1] = new Point3f(cpPoints[0]);
   cpPoints[1].x *= -1;
   Point3f backLeft = new Point3f(-backClipDist * fovSpreadX, eyePtVW.z
       + backClipDist, 0.1f);
   cpPoints[2] = backLeft;
   Point3f backRight = new Point3f(backLeft);
   backRight.x *= -1;
   cpPoints[3] = backRight;
   cpPoints[4] = cpPoints[0];
   //for (int i = 0; i < 4; i++) {
   //    System.out.println("cpPoints[" + i + "] = " + cpPoints[i]);
   //}
   int[] cpLength = new int[1];
   cpLength[0] = 5;
   LineStripArray cpLines = new LineStripArray(5, LineArray.COORDINATES,
       cpLength);
   cpLines.setCoordinates(0, cpPoints);
   Appearance cpApp = new Appearance();
   ColoringAttributes cpCa = new ColoringAttributes(blue,
       ColoringAttributes.SHADE_FLAT);
   cpApp.setColoringAttributes(cpCa);
   Shape3D cpShape = new Shape3D(cpLines, cpApp);
   objTrans.addChild(cpShape);
   // get the limits of the space
   float minY = eyePt.y;
   float maxY = backLeft.y;
   float minX = backLeft.x;
   float maxX = backRight.x;
   // figure out the X and Y extents and offsets
   float deltaX = maxX - minX;
   float deltaY = maxY - minY;
   float offsetX = -(maxX + minX) / 2.0f;
   float offsetY = -(maxY + minY) / 2.0f;
   float gridSize = Math.max(deltaX, deltaY);
   // scale the grid slightly to give a border around the edge
   gridSize *= 1.1f;
   //System.out.println("offsetX = " + offsetX);
   //System.out.println("offsetY = " + offsetY);
   // Scale the view to fit -1 to 1
   Transform3D trans = new Transform3D();
   trans.set(new Vector3f(offsetX, offsetY, 0.0f), 2.0f / gridSize);
   objTrans.setTransform(trans);
   // figure out a grid step that is a multiple of 10 which keeps the
   // number of steps less than 30.
   float gridStep = 1.0f;
   while ((gridSize / gridStep) > 30.0) {
     gridStep *= 10;
   }
   int gridNumSteps = (int) Math.ceil(gridSize / gridStep) + 1;
   // allocate the grid points array, four points for each step (x and y)
   // with a couple extra points for the extra grid points added
   // below
   int gridNumPoints = 4 * (gridNumSteps + 4);
   Point3f[] gridPts = new Point3f[gridNumPoints];
   for (int i = 0; i < gridNumPoints; i++) {
     gridPts[i] = new Point3f();
   }
   // find the grid limits. Add a step on each side to make sure
   // the grid is larger than the view
   float gridMinY = gridStepFloor(minY, gridStep) - gridStep;
   float gridMaxY = gridStepCeil(maxY, gridStep) + gridStep;
   float gridMinX = gridStepFloor(minX, gridStep) - gridStep;
   float gridMaxX = gridStepCeil(maxX, gridStep) + gridStep;
   //System.out.println("gridMinY = " + gridMinY);
   //System.out.println("gridMaxY = " + gridMaxY);
   //System.out.println("gridMinX = " + gridMinX);
   //System.out.println("gridMaxX = " + gridMaxX);
   // set up the background grid
   Appearance bgApp = new Appearance();
   ColoringAttributes bgCa = new ColoringAttributes();
   bgCa.setColor(grey);
   LineAttributes bgLa = new LineAttributes();
   bgApp.setColoringAttributes(bgCa);
   // clear out the clip grid point list
   numClipGridPts = 0;
   // set up the vertical lines
   int numPts = 0;
   for (float x = gridMinX; x <= gridMaxX; x += gridStep) {
     gridPts[numPts].x = x;
     gridPts[numPts].y = gridMinY;
     gridPts[numPts].z = -0.2f;
     gridPts[numPts + 1].x = x;
     gridPts[numPts + 1].y = gridMaxY;
     gridPts[numPts + 1].z = -0.2f;
     numPts += 2;
     // try to add a line to the clipped grid
     // find the intersection of the clipped line with the FOV sides
     // this is a distance relative to the eye
     float clipZ = fovSpreadZ * Math.abs(x - eyePtVW.x);
     if (clipZ < frontClipDist) { // clip to front clip plane
       clipZ = frontClipDist;
     }
     if (clipZ < backClipDist) { // clip to back clip plane
       // line is not clipped
       clipGridPtsVW[numClipGridPts].x = x;
       clipGridPtsVW[numClipGridPts].y = clipZ + eyePtVW.z;
       clipGridPtsVW[numClipGridPts].z = -0.1f;
       clipGridPtsVW[numClipGridPts + 1].x = x;
       clipGridPtsVW[numClipGridPts + 1].y = backClipDist + eyePtVW.z;
       clipGridPtsVW[numClipGridPts + 1].z = -0.1f;
       numClipGridPts += 2;
     }
   }
   LineArray vertLa = new LineArray(numPts, LineArray.COORDINATES);
   vertLa.setCoordinates(0, gridPts, 0, numPts);
   Shape3D vertShape = new Shape3D(vertLa, bgApp);
   objTrans.addChild(vertShape);
   // set up the horizontal lines
   numPts = 0;
   for (float y = gridMinY; y <= gridMaxY; y += gridStep) {
     gridPts[numPts].x = gridMinX;
     gridPts[numPts].y = y;
     gridPts[numPts++].z = -0.2f;
     gridPts[numPts].x = gridMaxX;
     gridPts[numPts].y = y;
     gridPts[numPts++].z = -0.2f;
     // try to add a line to the clipped grid
     // find the intersection of the clipped line with the FOV sides
     // this is a distance relative to the eye
     float clipDist = (y - eyePtVW.z);
     if ((clipDist > frontClipDist) && (clipDist < backClipDist)) {
       float clipX = fovSpreadX * clipDist;
       clipGridPtsVW[numClipGridPts].x = -clipX;
       clipGridPtsVW[numClipGridPts].y = y;
       clipGridPtsVW[numClipGridPts].z = -0.1f;
       clipGridPtsVW[numClipGridPts + 1].x = clipX;
       clipGridPtsVW[numClipGridPts + 1].y = y;
       clipGridPtsVW[numClipGridPts + 1].z = -0.1f;
       numClipGridPts += 2;
     }
   }
   LineArray horizLa = new LineArray(numPts, LineArray.COORDINATES);
   horizLa.setCoordinates(0, gridPts, 0, numPts);
   Shape3D horizShape = new Shape3D(horizLa, bgApp);
   objTrans.addChild(horizShape);
   // draw the clipped grid.
   if (numClipGridPts > 0) {
     LineArray clipLa = new LineArray(numClipGridPts,
         LineArray.COORDINATES);
     clipLa.setCoordinates(0, clipGridPtsVW, 0, numClipGridPts);
     Appearance clipGridApp = new Appearance();
     ColoringAttributes clipCa = new ColoringAttributes(black,
         ColoringAttributes.SHADE_FLAT);
     clipGridApp.setColoringAttributes(clipCa);
     LineAttributes clipGridLa = new LineAttributes();
     Shape3D clipShape = new Shape3D(clipLa, clipGridApp);
     objTrans.addChild(clipShape);
   }
   // set up the coordinate system
   Appearance coordSysApp = new Appearance();
   LineAttributes coordSysLa = new LineAttributes();
   coordSysLa.setLineWidth(3.0f);
   coordSysApp.setLineAttributes(coordSysLa);
   ColoringAttributes coordSysCa = new ColoringAttributes(grey,
       ColoringAttributes.SHADE_FLAT);
   coordSysApp.setColoringAttributes(coordSysCa);
   Point3f[] coordSysPts = new Point3f[4];
   coordSysPts[0] = new Point3f(gridMinX, 0, -0.5f);
   coordSysPts[1] = new Point3f(gridMaxX, 0, -0.5f);
   coordSysPts[2] = new Point3f(0, gridMinY, -0.5f);
   coordSysPts[3] = new Point3f(0, gridMaxY, -0.5f);
   LineArray coordSysLines = new LineArray(4, LineArray.COORDINATES);
   coordSysLines.setCoordinates(0, coordSysPts);
   Shape3D coordSysShape = new Shape3D(coordSysLines, coordSysApp);
   objTrans.addChild(coordSysShape);
   // set up the circle
   Appearance circleApp = new Appearance();
   ColoringAttributes circleCa = new ColoringAttributes();
   circleCa.setColor(red);
   circleApp.setColoringAttributes(circleCa);
   PolygonAttributes pa = new PolygonAttributes();
   pa.setCullFace(PolygonAttributes.CULL_NONE);
   circleApp.setPolygonAttributes(pa);
   int step = 360 / (numCirclePts - 1);
   for (int deg = 0; deg < 360; deg += step) {
     double angle = Math.toRadians(deg);
     circlePtsVW[deg / 10].x = sphereRadius * (float) Math.sin(angle);
     circlePtsVW[deg / 10].y = sphereRadius * (float) Math.cos(angle);
     circlePtsVW[deg / 10].z = -0.3f;
   }
   circlePtsVW[numCirclePts - 1].set(circlePtsVW[0]);
   int[] lineStripLength = new int[1];
   lineStripLength[0] = numCirclePts;
   //LineStripArray circleLineStrip = new LineStripArray(numCirclePts,
   //        LineArray.COORDINATES, lineStripLength);
   TriangleFanArray circleLineStrip = new TriangleFanArray(numCirclePts,
       LineArray.COORDINATES, lineStripLength);
   circleLineStrip.setCoordinates(0, circlePtsVW);
   Shape3D circleShape = new Shape3D(circleLineStrip, circleApp);
   objTrans.addChild(circleShape);
   return objRoot;
 }
 // return the closest multiple of step less than value
 float gridStepFloor(float value, float step) {
   return (float) (step * (Math.floor(value / step)));
 }
 // return the closest multiple of step greater than value
 float gridStepCeil(float value, float step) {
   return (float) (step * (Math.ceil(value / step)));
 }
 public BranchGroup createProjViewSG() {
   // Create the root of the branch graph
   BranchGroup objRoot = new BranchGroup();
   objRoot.setCapability(BranchGroup.ALLOW_DETACH);
   // setup a transform group to hold the scaled scene
   TransformGroup objTrans = new TransformGroup();
   Transform3D scale = new Transform3D();
   scale.set(0.9);
   objTrans.setTransform(scale);
   objRoot.addChild(objTrans);
   // create the clip limits line
   Point3f[] cpPoints = new Point3f[5];
   cpPoints[0] = new Point3f(-1, -1, 0.1f);
   cpPoints[1] = new Point3f(1, -1, 0.1f);
   cpPoints[2] = new Point3f(1, 1, 0.1f);
   cpPoints[3] = new Point3f(-1, 1, 0.1f);
   cpPoints[4] = cpPoints[0];
   int[] cpLength = new int[1];
   cpLength[0] = 5;
   LineStripArray cpLines = new LineStripArray(5, LineArray.COORDINATES,
       cpLength);
   cpLines.setCoordinates(0, cpPoints);
   Appearance cpApp = new Appearance();
   ColoringAttributes cpCa = new ColoringAttributes(blue,
       ColoringAttributes.SHADE_FLAT);
   cpApp.setColoringAttributes(cpCa);
   LineAttributes cpLa = new LineAttributes();
   Shape3D cpShape = new Shape3D(cpLines, cpApp);
   objTrans.addChild(cpShape);
   // transform and render the clip grid points
   updateProjTrans();
   if (numClipGridPts > 0) {
     // transform the clipGridPts
     for (int i = 0; i < numClipGridPts; i++) {
       projectPoint(clipGridPtsVW[i], clipGridPtsProj[i]);
     }
     LineArray clipLn = new LineArray(numClipGridPts,
         LineArray.COORDINATES);
     clipLn.setCoordinates(0, clipGridPtsProj, 0, numClipGridPts);
     Appearance clipGridApp = new Appearance();
     ColoringAttributes clipCa = new ColoringAttributes(black,
         ColoringAttributes.SHADE_FLAT);
     clipGridApp.setColoringAttributes(clipCa);
     LineAttributes clipLa = new LineAttributes();
     Shape3D clipShape = new Shape3D(clipLn, clipGridApp);
     objTrans.addChild(clipShape);
   }
   // set up the circle
   Appearance circleApp = new Appearance();
   ColoringAttributes circleCa = new ColoringAttributes();
   circleCa.setColor(red);
   circleApp.setColoringAttributes(circleCa);
   PolygonAttributes pa = new PolygonAttributes();
   pa.setCullFace(PolygonAttributes.CULL_NONE);
   circleApp.setPolygonAttributes(pa);
   // transform the circlePts
   for (int i = 0; i < numCirclePts; i++) {
     projectPoint(circlePtsVW[i], circlePtsProj[i]);
   }
   int[] lineStripLength = new int[1];
   lineStripLength[0] = numCirclePts;
   //LineStripArray circleLineStrip = new LineStripArray(numCirclePts,
   //        LineArray.COORDINATES, lineStripLength);
   TriangleFanArray circleLineStrip = new TriangleFanArray(numCirclePts,
       LineArray.COORDINATES, lineStripLength);
   circleLineStrip.setCoordinates(0, circlePtsProj);
   Shape3D circleShape = new Shape3D(circleLineStrip, circleApp);
   objTrans.addChild(circleShape);
   return objRoot;
 }
 void projectPoint(Point3f ptVW, Point3f ptProj) {
   // handle the VW having y and z switched
   // TODO: fix viewpoint for views
   projPt.x = ptVW.x;
   projPt.y = ptVW.z;
   projPt.z = -ptVW.y;
   projPt.w = 1.0f;
   projPt.z += eyePtVW.z; // TODO: move to projTrans
   //System.out.println("projPtVW = (" +
   //  projPt.x + ", " +
   //  projPt.y + ", " +
   //  projPt.z + ")");
   projTrans.transform(projPt);
   projPt.x /= projPt.w;
   projPt.y /= projPt.w;
   projPt.z /= projPt.w;
   //System.out.println("projPt = (" +
   //  projPt.x + ", " +
   //  projPt.y + ", " +
   //  projPt.z + ")");
   ptProj.x = (float) projPt.x;
   ptProj.y = (float) projPt.z;
   ptProj.z = (float) projPt.y;
 }
 /**
  * Calculates the projection transform specified by the field of view and
  * clip distances specified by the view.
  */
 public void updateProjTrans() {
   int projType = view.getProjectionPolicy();
   if (projType == View.PARALLEL_PROJECTION) {
     //System.out.println("PARALLEL_PROJECTION");
     projTrans.setIdentity();
     return;
   }
   //System.out.println("PERSPECTIVE_PROJECTION");
   // figure out the perspective transform from the view
   double fov = view.getFieldOfView();
   // n = near clip
   double n = frontClipDist;
   // f = far clip
   double f = backClipDist;
   //System.out.println("n = " + nf.format(n) + " f = " + nf.format(f));
   // Create a matrix using coefficents derived from the OpenGL
   // glFrustum() man page. This assumes the eye point is a 0,0,0,
   // the front clip plane is a z = -n, the back clip plane is at
   // z = -f and that the front clip plane intersects the FOV so that
   // -1 <= X,Y <= 1 at the front plane (the last assumption may not
   // be true, so we"ll scale later).
   Matrix4d matrix = new Matrix4d();
   matrix.m00 = n;
   matrix.m11 = n;
   matrix.m22 = -(f + n) / (f - n);
   matrix.m23 = -2 * f * n / (f - n);
   matrix.m32 = -1;
   //System.out.println("matrix = " + matrix);
   // This is the distance where the FOV maps to a -1 to 1 area in X and Y
   double d = 1 / Math.tan(fov / 2);
   //System.out.println("n = " + nf.format(n) + " f = " + nf.format(f) +
   //  " d = " + nf.format(d));
   // this is a scaling ratio to make the OpenGL glFrustum() matrix
   // elements work with with the J3D matrix. It compensates for the
   // front clip plane not being at the FOV distance (the OpenGL
   // matrix expects n == d).
   double scale = n / d;
   //System.out.println("scale = " + nf.format(scale));
   // scale the elements of the matrix
   //matrix.m00 *= 1.0/scale;
   //matrix.m11 *= 1.0/scale;
   matrix.m22 *= scale;
   matrix.m23 *= scale;
   matrix.m32 *= scale;
   // set the Transform3D
   projTrans.set(matrix);
   //System.out.println("projTrans = " + projTrans);
 }
 /* TODO: use a behavior post to avoid the flicker when these change */
 void updateViewWindows() {
   BranchGroup newUlScene = createVWorldViewSG();
   urScene.detach();
   urUniverse.addBranchGraph(newUlScene);
   urScene = newUlScene;
   BranchGroup newLlScene = createProjViewSG();
   lrScene.detach();
   lrUniverse.addBranchGraph(newLlScene);
   lrScene = newLlScene;
 }
 public ViewProj() {
   this(true);
 }
 public ViewProj(boolean isApplication) {
   this.isApplication = isApplication;
 }
 public void init() {
   setLayout(new BorderLayout());
   nf = NumberFormat.getInstance();
   nf.setMaximumFractionDigits(3);
   GraphicsConfiguration config = SimpleUniverse
       .getPreferredConfiguration();
   JPanel canvasPanel = new JPanel();
   GridBagLayout gridbag = new GridBagLayout();
   canvasPanel.setLayout(gridbag);
   canvas = new Canvas3D(config);
   canvas.setSize(400, 400);
   GridBagConstraints constraints = new GridBagConstraints();
   constraints.gridx = 0;
   constraints.gridy = 0;
   constraints.gridwidth = 2;
   constraints.gridheight = 2;
   constraints.insets = new Insets(5, 5, 5, 5);
   constraints.fill = GridBagConstraints.BOTH;
   gridbag.setConstraints(canvas, constraints);
   canvasPanel.add(canvas);
   constraints.fill = GridBagConstraints.REMAINDER;
   constraints.gridwidth = 1;
   constraints.gridheight = 1;
   constraints.gridx = 2;
   constraints.gridy = 0;
   urCanvas = new Canvas3D(config);
   urCanvas.setSize(200, 200);
   gridbag.setConstraints(urCanvas, constraints);
   canvasPanel.add(urCanvas);
   constraints.gridx = 2;
   constraints.gridy = 1;
   lrCanvas = new Canvas3D(config);
   lrCanvas.setSize(200, 200);
   gridbag.setConstraints(lrCanvas, constraints);
   canvasPanel.add(lrCanvas);
   add(canvasPanel, BorderLayout.NORTH);
   SimpleUniverse u = new SimpleUniverse(canvas);
   urUniverse = new SimpleUniverse(urCanvas);
   lrUniverse = new SimpleUniverse(lrCanvas);
   if (isApplication) {
     offScreenCanvas = new OffScreenCanvas3D(config, true);
     // set the size of the off-screen canvas based on a scale
     // of the on-screen size
     Screen3D sOn = canvas.getScreen3D();
     Screen3D sOff = offScreenCanvas.getScreen3D();
     Dimension dim = sOn.getSize();
     dim.width *= offScreenScale;
     dim.height *= offScreenScale;
     sOff.setSize(dim);
     sOff.setPhysicalScreenWidth(sOn.getPhysicalScreenWidth()
         * offScreenScale);
     sOff.setPhysicalScreenHeight(sOn.getPhysicalScreenHeight()
         * offScreenScale);
     // attach the offscreen canvas to the view
     u.getViewer().getView().addCanvas3D(offScreenCanvas);
     urOffScreenCanvas = new OffScreenCanvas3D(config, true);
     // set the size of the off-screen canvas based on a scale
     // of the on-screen size
     sOn = urCanvas.getScreen3D();
     sOff = urOffScreenCanvas.getScreen3D();
     dim = sOn.getSize();
     dim.width *= urOffScreenScale;
     dim.height *= urOffScreenScale;
     sOff.setSize(dim);
     sOff.setPhysicalScreenWidth(sOn.getPhysicalScreenWidth()
         * urOffScreenScale);
     sOff.setPhysicalScreenHeight(sOn.getPhysicalScreenHeight()
         * urOffScreenScale);
     // attach the offscreen canvas to the view
     urUniverse.getViewer().getView().addCanvas3D(urOffScreenCanvas);
     lrOffScreenCanvas = new OffScreenCanvas3D(config, true);
     // set the size of the off-screen canvas based on a scale
     // of the on-screen size
     sOn = lrCanvas.getScreen3D();
     sOff = lrOffScreenCanvas.getScreen3D();
     dim = sOn.getSize();
     dim.width *= lrOffScreenScale;
     dim.height *= lrOffScreenScale;
     sOff.setSize(dim);
     sOff.setPhysicalScreenWidth(sOn.getPhysicalScreenWidth()
         * lrOffScreenScale);
     sOff.setPhysicalScreenHeight(sOn.getPhysicalScreenHeight()
         * lrOffScreenScale);
     // attach the offscreen canvas to the view
     lrUniverse.getViewer().getView().addCanvas3D(lrOffScreenCanvas);
   }
   // Create a simple scene and attach it to the virtual universe
   BranchGroup scene = createSceneGraph();
   // This will move the ViewPlatform back a bit so the
   // objects in the scene can be viewed.
   viewingPlatform = u.getViewingPlatform();
   viewingPlatform.setNominalViewingTransform();
   view = u.getViewer().getView();
   view.setFrontClipPolicy(View.VIRTUAL_EYE);
   view.setBackClipPolicy(View.VIRTUAL_EYE);
   view.setFrontClipDistance(frontClipDist);
   view.setBackClipDistance(backClipDist);
   u.addBranchGraph(scene);
   // init the clipGridPts arrays
   for (int i = 0; i < maxClipGridPts; i++) {
     clipGridPtsVW[i] = new Point3f();
     clipGridPtsProj[i] = new Point3f();
   }
   // init the circlePts arrays
   for (int i = 0; i < numCirclePts; i++) {
     circlePtsVW[i] = new Point3f();
     circlePtsProj[i] = new Point3f();
   }
   // setup the ur canvas
   urScene = createVWorldViewSG();
   // This will move the ViewPlatform back a bit so the
   // objects in the scene can be viewed.
   urUniverse.getViewingPlatform().setNominalViewingTransform();
   View urView = urUniverse.getViewer().getView();
   urView.setProjectionPolicy(View.PARALLEL_PROJECTION);
   urUniverse.addBranchGraph(urScene);
   // set up the background on a separate BG so that it can stay there
   // when we replace the scene SG
   Background urBgWhite = new Background(white);
   urBgWhite.setApplicationBounds(infiniteBounds);
   BranchGroup urBackBG = new BranchGroup();
   urBackBG.addChild(urBgWhite);
   urUniverse.addBranchGraph(urBackBG);
   // setup the lr canvas
   lrScene = createProjViewSG();
   // This will move the ViewPlatform back a bit so the
   // objects in the scene can be viewed.
   lrUniverse.getViewingPlatform().setNominalViewingTransform();
   View lrView = lrUniverse.getViewer().getView();
   lrView.setProjectionPolicy(View.PARALLEL_PROJECTION);
   lrUniverse.addBranchGraph(lrScene);
   // set up the background on a separate BG so that it can stay there
   // when we replace the scene SG
   Background lrBgWhite = new Background(white);
   lrBgWhite.setApplicationBounds(infiniteBounds);
   BranchGroup lrBackBG = new BranchGroup();
   lrBackBG.addChild(lrBgWhite);
   lrUniverse.addBranchGraph(lrBackBG);
   // set up the sliders
   JPanel guiPanel = new JPanel();
   guiPanel.setLayout(new GridLayout(0, 2));
   FloatLabelJSlider dynamicSlider = new FloatLabelJSlider(
       "Dynamic Offset", 0.1f, 0.0f, 2.0f, dynamicOffset);
   dynamicSlider.addFloatListener(new FloatListener() {
     public void floatChanged(FloatEvent e) {
       dynamicOffset = e.getValue();
       solidPa.setPolygonOffsetFactor(dynamicOffset);
     }
   });
   guiPanel.add(dynamicSlider);
   LogFloatLabelJSlider staticSlider = new LogFloatLabelJSlider(
       "Static Offset", 0.1f, 10000.0f, staticOffset);
   staticSlider.addFloatListener(new FloatListener() {
     public void floatChanged(FloatEvent e) {
       staticOffset = e.getValue();
       solidPa.setPolygonOffset(staticOffset);
     }
   });
   guiPanel.add(staticSlider);
   // These are declared final here so they can be changed by the
   // listener routines below.
   LogFloatLabelJSlider frontClipSlider = new LogFloatLabelJSlider(
       "Front Clip Distance", 0.001f, 10.0f, frontClipDist);
   final LogFloatLabelJSlider backClipSlider = new LogFloatLabelJSlider(
       "Back Clip Distance", 1.0f, 10000.0f, backClipDist);
   final LogFloatLabelJSlider backClipRatioSlider = new LogFloatLabelJSlider(
       "Back Clip Ratio", 1.0f, 10000.0f, backClipRatio);
   frontClipSlider.addFloatListener(new FloatListener() {
     public void floatChanged(FloatEvent e) {
       frontClipDist = e.getValue();
       view.setFrontClipDistance(frontClipDist);
       backClipRatio = backClipDist / frontClipDist;
       backClipRatioSlider.setValue(backClipRatio);
       updateViewWindows();
     }
   });
   guiPanel.add(frontClipSlider);
   backClipSlider.addFloatListener(new FloatListener() {
     public void floatChanged(FloatEvent e) {
       backClipDist = e.getValue();
       backClipRatio = backClipDist / frontClipDist;
       backClipRatioSlider.setValue(backClipRatio);
       view.setBackClipDistance(backClipDist);
       updateViewWindows();
     }
   });
   guiPanel.add(backClipSlider);
   backClipRatioSlider.addFloatListener(new FloatListener() {
     public void floatChanged(FloatEvent e) {
       backClipRatio = e.getValue();
       backClipDist = backClipRatio * frontClipDist;
       backClipSlider.setValue(backClipDist);
       updateViewWindows();
     }
   });
   guiPanel.add(backClipRatioSlider);
   FloatLabelJSlider innerSphereSlider = new FloatLabelJSlider(
       "Inner Sphere Scale", 0.001f, 0.90f, 1.0f, innerScale);
   innerSphereSlider.addFloatListener(new FloatListener() {
     public void floatChanged(FloatEvent e) {
       innerScale = e.getValue();
       updateInnerScale();
     }
   });
   guiPanel.add(innerSphereSlider);
   JButton mainSnap = new JButton(snapImageString);
   mainSnap.addActionListener(new ActionListener() {
     public void actionPerformed(ActionEvent e) {
       Point loc = canvas.getLocationOnScreen();
       offScreenCanvas.setOffScreenLocation(loc);
       Dimension dim = canvas.getSize();
       dim.width *= offScreenScale;
       dim.height *= offScreenScale;
       nf.setMinimumIntegerDigits(3);
       offScreenCanvas.snapImageFile(outFileBase
           + nf.format(outFileSeq++), dim.width, dim.height);
       nf.setMinimumIntegerDigits(0);
     }
   });
   guiPanel.add(mainSnap);
   JButton urSnap = new JButton(urSnapImageString);
   urSnap.addActionListener(new ActionListener() {
     public void actionPerformed(ActionEvent e) {
       System.out.println("Snap UR");
       Point loc = urCanvas.getLocationOnScreen();
       urOffScreenCanvas.setOffScreenLocation(loc);
       Dimension dim = urCanvas.getSize();
       dim.width *= urOffScreenScale;
       dim.height *= urOffScreenScale;
       nf.setMinimumIntegerDigits(3);
       urOffScreenCanvas.snapImageFile(urOutFileBase
           + nf.format(urOutFileSeq++), dim.width, dim.height);
       nf.setMinimumIntegerDigits(0);
     }
   });
   guiPanel.add(urSnap);
   JButton lrSnap = new JButton(lrSnapImageString);
   lrSnap.addActionListener(new ActionListener() {
     public void actionPerformed(ActionEvent e) {
       System.out.println("Snap LR");
       Point loc = lrCanvas.getLocationOnScreen();
       lrOffScreenCanvas.setOffScreenLocation(loc);
       Dimension dim = lrCanvas.getSize();
       dim.width *= lrOffScreenScale;
       dim.height *= lrOffScreenScale;
       nf.setMinimumIntegerDigits(3);
       lrOffScreenCanvas.snapImageFile(lrOutFileBase
           + nf.format(lrOutFileSeq++), dim.width, dim.height);
       nf.setMinimumIntegerDigits(0);
     }
   });
   guiPanel.add(lrSnap);
   add(guiPanel, BorderLayout.SOUTH);
 }
 //
 // The following allows ViewProj to be run as an application
 // as well as an applet
 //
 public static void main(String[] args) {
   new MainFrame(new ViewProj(true), 700, 600);
 }

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

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

}

class LogFloatLabelJSlider extends JPanel implements ChangeListener,

   Java3DExplorerConstants {
 JSlider slider;
 JLabel valueLabel;
 Vector listeners = new Vector();
 float min, max, resolution, current, scale;
 double minLog, maxLog, curLog;
 int minInt, maxInt, curInt;;
 int intDigits, fractDigits;
 NumberFormat nf = NumberFormat.getInstance();
 float minResolution = 0.001f;
 double logBase = Math.log(10);
 // default slider with name, resolution = 0.1, min = 0.0, max = 1.0 inital
 // 0.5
 LogFloatLabelJSlider(String name) {
   this(name, 0.1f, 100.0f, 10.0f);
 }
 LogFloatLabelJSlider(String name, float min, float max, float current) {
   this.resolution = resolution;
   this.min = min;
   this.max = max;
   this.current = current;
   if (resolution < minResolution) {
     resolution = minResolution;
   }
   minLog = log10(min);
   maxLog = log10(max);
   curLog = log10(current);
   // resolution is 100 steps from min to max
   scale = 100.0f;
   resolution = 1.0f / scale;
   // get the integer versions of max, min, current
   minInt = (int) Math.round(minLog * scale);
   maxInt = (int) Math.round(maxLog * scale);
   curInt = (int) Math.round(curLog * scale);
   slider = new JSlider(JSlider.HORIZONTAL, minInt, maxInt, curInt);
   slider.addChangeListener(this);
   valueLabel = new JLabel(" ");
   // Need to muck around to make sure that the width of the label
   // is wide enough for the largest value. Pad the initial string
   // be large enough to hold the largest value.
   int pad = 5; // fudge to make up for variable width fonts
   intDigits = (int) Math.ceil(maxLog) + pad;
   if (min < 0) {
     intDigits++; // add one for the "-"
   }
   if (minLog < 0) {
     fractDigits = (int) Math.ceil(-minLog);
   } else {
     fractDigits = 0;
   }
   nf.setMinimumFractionDigits(fractDigits);
   nf.setMaximumFractionDigits(fractDigits);
   String value = nf.format(current);
   while (value.length() < (intDigits + fractDigits)) {
     value = value + " ";
   }
   valueLabel.setText(value);
   // 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();
   curInt = source.getValue();
   curLog = curInt / scale;
   current = (float) exp10(curLog);
   valueChanged();
 }
 public void setValue(float newValue) {
   boolean changed = (newValue != current);
   current = newValue;
   if (changed) {
     valueChanged();
   }
 }
 private void valueChanged() {
   String value = nf.format(current);
   valueLabel.setText(value);
   // notify the listeners
   FloatEvent event = new FloatEvent(this, current);
   for (Enumeration e = listeners.elements(); e.hasMoreElements();) {
     FloatListener listener = (FloatListener) e.nextElement();
     listener.floatChanged(event);
   }
 }
 double log10(double value) {
   return Math.log(value) / logBase;
 }
 double exp10(double value) {
   return Math.exp(value * logBase);
 }

}


      </source>