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