Java tutorial
/* * %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.Component; 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); } } }