/*
* %Z%%M% %I% %E% %U%
*
* ************************************************************** "Copyright (c)
* 2001 Sun Microsystems, Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* -Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* -Redistribution in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any kind. ALL
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
* IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
* NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
* LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
* OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
* LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
* INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
* CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
* OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGES.
*
* You acknowledge that Software is not designed,licensed or intended for use in
* the design, construction, operation or maintenance of any nuclear facility."
*
* ***************************************************************************
*/
import java.awt.AWTEvent;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.GraphicsConfiguration;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.text.NumberFormat;
import java.util.Enumeration;
import java.util.EventListener;
import java.util.EventObject;
import java.util.Hashtable;
import java.util.Vector;
import javax.media.j3d.AmbientLight;
import javax.media.j3d.Appearance;
import javax.media.j3d.Background;
import javax.media.j3d.BackgroundSound;
import javax.media.j3d.Behavior;
import javax.media.j3d.BoundingSphere;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.Canvas3D;
import javax.media.j3d.DirectionalLight;
import javax.media.j3d.ExponentialFog;
import javax.media.j3d.Group;
import javax.media.j3d.ImageComponent;
import javax.media.j3d.ImageComponent2D;
import javax.media.j3d.Light;
import javax.media.j3d.LinearFog;
import javax.media.j3d.Link;
import javax.media.j3d.Material;
import javax.media.j3d.MediaContainer;
import javax.media.j3d.PointLight;
import javax.media.j3d.PointSound;
import javax.media.j3d.QuadArray;
import javax.media.j3d.Screen3D;
import javax.media.j3d.Shape3D;
import javax.media.j3d.SharedGroup;
import javax.media.j3d.Sound;
import javax.media.j3d.SpotLight;
import javax.media.j3d.Switch;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.media.j3d.View;
import javax.media.j3d.WakeupCondition;
import javax.media.j3d.WakeupCriterion;
import javax.media.j3d.WakeupOnAWTEvent;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JApplet;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.JTabbedPane;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.vecmath.AxisAngle4f;
import javax.vecmath.Color3f;
import javax.vecmath.Point2f;
import javax.vecmath.Point3d;
import javax.vecmath.Point3f;
import javax.vecmath.Vector3f;
import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGEncodeParam;
import com.sun.image.codec.jpeg.JPEGImageEncoder;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.behaviors.vp.OrbitBehavior;
import com.sun.j3d.utils.geometry.Sphere;
import com.sun.j3d.utils.image.TextureLoader;
import com.sun.j3d.utils.universe.SimpleUniverse;
import com.sun.j3d.utils.universe.ViewingPlatform;
public class EnvironmentExplorer extends JApplet implements
Java3DExplorerConstants {
// Scene graph items
SimpleUniverse u;
// Light items
Group lightGroup;
AmbientLight lightAmbient;
DirectionalLight lightDirectional;
PointLight lightPoint;
SpotLight lightSpot;
Point3f attenuation = new Point3f(1.0f, 0.0f, 0.0f);
float spotSpreadAngle = 60; // degrees
float spotConcentration = 5.0f;
// Fog items
Switch fogSwitch;
IntChooser fogChooser;
// Background items
Switch bgSwitch;
IntChooser bgChooser;
// Sound items
Switch soundSwitch;
IntChooser soundChooser;
BackgroundSound soundBackground;
PointSound soundPoint;
// Display object
Switch spheresSwitch;
Switch gridSwitch;
// image grabber
boolean isApplication;
Canvas3D canvas;
OffScreenCanvas3D offScreenCanvas;
View view;
// GUI elements
JTabbedPane tabbedPane;
// Config items
String codeBaseString;
String outFileBase = "env";
int outFileSeq = 0;
static final float OFF_SCREEN_SCALE = 1.0f;
int colorMode = USE_COLOR;
// Temporaries that are reused
Transform3D tmpTrans = new Transform3D();
Vector3f tmpVector = new Vector3f();
AxisAngle4f tmpAxisAngle = new AxisAngle4f();
// configurable colors. These get set based on the rendering
// mode. By default they use color. B&W is set up for print
// file output: white background with B&W coloring.
Color3f objColor;
// geometric constants
Point3f origin = new Point3f();
/*
* Set up the lights. This is a group which contains the ambient light and a
* switch for the other lights. directional : white light pointing along Z
* axis point : white light near upper left corner of spheres spot : white
* light near upper left corner of spheres, pointing towards center.
*/
void setupLights() {
lightGroup = new Group();
// Set up the ambient light
lightAmbient = new AmbientLight(darkGrey);
lightAmbient.setInfluencingBounds(infiniteBounds);
lightAmbient.setCapability(Light.ALLOW_STATE_WRITE);
lightAmbient.setEnable(true);
lightGroup.addChild(lightAmbient);
// Set up the directional light
Vector3f lightDirection = new Vector3f(0.65f, -0.65f, -0.40f);
lightDirectional = new DirectionalLight(white, lightDirection);
lightDirectional.setInfluencingBounds(infiniteBounds);
lightDirectional.setEnable(true);
lightDirectional.setCapability(Light.ALLOW_STATE_WRITE);
lightGroup.addChild(lightDirectional);
// Set up the point light
Point3f lightPosition = new Point3f(-1.0f, 1.0f, 0.6f);
lightPoint = new PointLight(white, lightPosition, attenuation);
lightPoint.setInfluencingBounds(infiniteBounds);
lightPoint.setEnable(false);
lightPoint.setCapability(Light.ALLOW_STATE_WRITE);
lightPoint.setCapability(PointLight.ALLOW_ATTENUATION_WRITE);
lightGroup.addChild(lightPoint);
// Set up the spot light
// Point the light back at the origin
lightSpot = new SpotLight(white, lightPosition, attenuation,
lightDirection, (float) Math.toRadians(spotSpreadAngle),
spotConcentration);
lightSpot.setInfluencingBounds(infiniteBounds);
lightSpot.setEnable(false);
lightSpot.setCapability(Light.ALLOW_STATE_WRITE);
lightSpot.setCapability(PointLight.ALLOW_ATTENUATION_WRITE);
lightSpot.setCapability(SpotLight.ALLOW_CONCENTRATION_WRITE);
lightSpot.setCapability(SpotLight.ALLOW_SPREAD_ANGLE_WRITE);
lightGroup.addChild(lightSpot);
}
/*
* Setup the backgrounds. The bg tool creates a Switch and a GUI component
* for the backgrounds
*/
void setupBackgrounds() {
// initialize the background tool
BackgroundTool bgTool = new BackgroundTool(codeBaseString);
bgSwitch = bgTool.getSwitch();
bgChooser = bgTool.getChooser();
}
/*
* Setup the fog Switch and Chooser. Child values are: CHILD_NONE: Don't use
* a fog 0: The linear Fog node 1: The exponential Fog node
*/
void setupFogs() {
fogSwitch = new Switch(Switch.CHILD_NONE);
fogSwitch.setCapability(Switch.ALLOW_SWITCH_WRITE);
// set up the linear fog
LinearFog fogLinear = new LinearFog(skyBlue, 6.0f, 12.0f);
fogLinear.setInfluencingBounds(infiniteBounds);
fogSwitch.addChild(fogLinear);
// set up the exponential fog
ExponentialFog fogExp = new ExponentialFog(skyBlue, 0.3f);
fogExp.setInfluencingBounds(infiniteBounds);
fogSwitch.addChild(fogExp);
// Create the chooser GUI
String[] fogNames = { "None", "Linear", "Exponential", };
int[] fogValues = { Switch.CHILD_NONE, 0, 1 };
fogChooser = new IntChooser("Fog:", fogNames, fogValues, 0);
fogChooser.addIntListener(new IntListener() {
public void intChanged(IntEvent event) {
int value = event.getValue();
fogSwitch.setWhichChild(value);
}
});
fogChooser.setValue(Switch.CHILD_NONE);
}
/*
* Set up the sound switch. The child values are: CHILD_NONE: 1No sound 0:
* BackgroundSound 1: PointSound 2: ConeSound
*/
void setupSounds() {
soundSwitch = new Switch(Switch.CHILD_NONE);
soundSwitch.setCapability(Switch.ALLOW_SWITCH_WRITE);
// Set up the sound media container
java.net.URL soundURL = null;
String soundFile = "techno_machine.au";
try {
soundURL = new java.net.URL(codeBaseString + soundFile);
} catch (java.net.MalformedURLException ex) {
System.out.println(ex.getMessage());
System.exit(1);
}
if (soundURL == null) { // application, try file URL
try {
soundURL = new java.net.URL("file:./" + soundFile);
} catch (java.net.MalformedURLException ex) {
System.out.println(ex.getMessage());
System.exit(1);
}
}
//System.out.println("soundURL = " + soundURL);
MediaContainer soundMC = new MediaContainer(soundURL);
// set up the Background Sound
soundBackground = new BackgroundSound();
soundBackground.setCapability(Sound.ALLOW_ENABLE_WRITE);
soundBackground.setSoundData(soundMC);
soundBackground.setSchedulingBounds(infiniteBounds);
soundBackground.setEnable(false);
soundBackground.setLoop(Sound.INFINITE_LOOPS);
soundSwitch.addChild(soundBackground);
// set up the point sound
soundPoint = new PointSound();
soundPoint.setCapability(Sound.ALLOW_ENABLE_WRITE);
soundPoint.setSoundData(soundMC);
soundPoint.setSchedulingBounds(infiniteBounds);
soundPoint.setEnable(false);
soundPoint.setLoop(Sound.INFINITE_LOOPS);
soundPoint.setPosition(-5.0f, 5.0f, 0.0f);
Point2f[] distGain = new Point2f[2];
// set the attenuation to linearly decrease volume from max at
// source to 0 at a distance of 15m
distGain[0] = new Point2f(0.0f, 1.0f);
distGain[1] = new Point2f(15.0f, 0.0f);
soundPoint.setDistanceGain(distGain);
soundSwitch.addChild(soundPoint);
// Create the chooser GUI
String[] soundNames = { "None", "Background", "Point", };
soundChooser = new IntChooser("Sound:", soundNames);
soundChooser.addIntListener(new IntListener() {
public void intChanged(IntEvent event) {
int value = event.getValue();
// Should just be able to use setWhichChild on
// soundSwitch, have to explictly enable/disable due to
// bug.
switch (value) {
case 0:
soundSwitch.setWhichChild(Switch.CHILD_NONE);
soundBackground.setEnable(false);
soundPoint.setEnable(false);
break;
case 1:
soundSwitch.setWhichChild(0);
soundBackground.setEnable(true);
soundPoint.setEnable(false);
break;
case 2:
soundSwitch.setWhichChild(1);
soundBackground.setEnable(false);
soundPoint.setEnable(true);
break;
}
}
});
soundChooser.setValue(Switch.CHILD_NONE);
}
// sets up a grid of spheres
void setupSpheres() {
// create a Switch for the spheres, allow switch changes
spheresSwitch = new Switch(Switch.CHILD_ALL);
spheresSwitch.setCapability(Switch.ALLOW_SWITCH_WRITE);
// Set up an appearance to make the Sphere with objColor ambient,
// black emmissive, objColor diffuse and white specular coloring
Material material = new Material(objColor, black, objColor, white, 32);
Appearance appearance = new Appearance();
appearance.setMaterial(material);
// create a sphere and put it into a shared group
Sphere sphere = new Sphere(0.5f, appearance);
SharedGroup sphereSG = new SharedGroup();
sphereSG.addChild(sphere);
// create a grid of spheres in the z=0 plane
// each has a TransformGroup to position the sphere which contains
// a link to the shared group for the sphere
for (int y = -2; y <= 2; y++) {
for (int x = -2; x <= 2; x++) {
TransformGroup tg = new TransformGroup();
tmpVector.set(x * 1.2f, y * 1.2f, -0.1f);
tmpTrans.set(tmpVector);
tg.setTransform(tmpTrans);
tg.addChild(new Link(sphereSG));
spheresSwitch.addChild(tg);
}
}
}
// sets up a grid of squares
void setupGrid() {
// create a Switch for the spheres, allow switch changes
gridSwitch = new Switch(Switch.CHILD_NONE);
gridSwitch.setCapability(Switch.ALLOW_SWITCH_WRITE);
// Set up an appearance to make the square3s with red ambient,
// black emmissive, red diffuse and black specular coloring
Material material = new Material(red, black, red, black, 64);
Appearance appearance = new Appearance();
appearance.setMaterial(material);
// create a grid of quads
int gridSize = 20; // grid is gridSize quads along each side
int numQuads = gridSize * gridSize;
int numVerts = numQuads * 4; // 4 verts per quad
// there will be 3 floats per coord and 4 coords per quad
float[] coords = new float[3 * numVerts];
// All the quads will use the same normal at each vertex, so
// allocate an array to hold references to the same normal
Vector3f[] normals = new Vector3f[numVerts];
Vector3f vertNormal = new Vector3f(0.0f, 0.0f, 1.0f);
float edgeLength = 5.0f; // length of each edge of the grid
float gridGap = 0.03f; // the gap between each quad
// length of each quad is (total length - sum of the gaps) / gridSize
float quadLength = (edgeLength - gridGap * (gridSize - 1)) / gridSize;
// create a grid of quads in the z=0 plane
// each has a TransformGroup to position the sphere which contains
// a link to the shared group for the sphere
float curX, curY;
for (int y = 0; y < gridSize; y++) {
curY = y * (quadLength + gridGap); // offset to lower left corner
curY -= edgeLength / 2; // center on 0,0
for (int x = 0; x < gridSize; x++) {
// this is the offset into the vertex array for the first
// vertex of the quad
int vertexOffset = (y * gridSize + x) * 4;
// this is the offset into the coord array for the first
// vertex of the quad, where there are 3 floats per vertex
int coordOffset = vertexOffset * 3;
curX = x * (quadLength + gridGap); // offset to ll corner
curX -= edgeLength / 2; // center on 0,0
// lower left corner
coords[coordOffset + 0] = curX;
coords[coordOffset + 1] = curY;
coords[coordOffset + 2] = 0.0f; // z
// lower right corner
coords[coordOffset + 3] = curX + quadLength;
coords[coordOffset + 4] = curY;
coords[coordOffset + 5] = 0.0f; // z
// upper right corner
coords[coordOffset + 6] = curX + quadLength;
coords[coordOffset + 7] = curY + quadLength;
coords[coordOffset + 8] = 0.0f; // z
// upper left corner
coords[coordOffset + 9] = curX;
coords[coordOffset + 10] = curY + quadLength;
coords[coordOffset + 11] = 0.0f; // z
for (int i = 0; i < 4; i++) {
normals[vertexOffset + i] = vertNormal;
}
}
}
// now that we have the data, create the QuadArray
QuadArray quads = new QuadArray(numVerts, QuadArray.COORDINATES
| QuadArray.NORMALS);
quads.setCoordinates(0, coords);
quads.setNormals(0, normals);
// create the shape
Shape3D shape = new Shape3D(quads, appearance);
// add it to the switch
gridSwitch.addChild(shape);
}
BranchGroup createSceneGraph() {
// Create the root of the branch graph
BranchGroup objRoot = new BranchGroup();
// Add the primitives to the scene
setupSpheres();
objRoot.addChild(spheresSwitch);
setupGrid();
objRoot.addChild(gridSwitch);
objRoot.addChild(lightGroup);
objRoot.addChild(bgSwitch);
objRoot.addChild(fogSwitch);
objRoot.addChild(soundSwitch);
KeyPrintBehavior key = new KeyPrintBehavior();
key.setSchedulingBounds(infiniteBounds);
objRoot.addChild(key);
return objRoot;
}
public EnvironmentExplorer(boolean isApplication, boolean blackAndWhite) {
if (blackAndWhite) {
colorMode = USE_BLACK_AND_WHITE;
}
this.isApplication = isApplication;
}
public EnvironmentExplorer(boolean isApplication) {
this(isApplication, false);
}
public EnvironmentExplorer() {
this(false, false);
}
public void init() {
// initialize the code base
try {
java.net.URL codeBase = getCodeBase();
codeBaseString = codeBase.toString();
} catch (Exception e) {
// probably running as an application, try the application
// code base
codeBaseString = "file:./";
}
if (colorMode == USE_COLOR) {
objColor = red;
} else {
objColor = white;
}
Container contentPane = getContentPane();
contentPane.setLayout(new BorderLayout());
GraphicsConfiguration config = SimpleUniverse
.getPreferredConfiguration();
canvas = new Canvas3D(config);
u = new SimpleUniverse(canvas);
if (isApplication) {
offScreenCanvas = new OffScreenCanvas3D(config, true);
// set the size of the off-screen canvas based on a scale
// of the on-screen size
Screen3D sOn = canvas.getScreen3D();
Screen3D sOff = offScreenCanvas.getScreen3D();
Dimension dim = sOn.getSize();
dim.width *= OFF_SCREEN_SCALE;
dim.height *= OFF_SCREEN_SCALE;
sOff.setSize(dim);
sOff.setPhysicalScreenWidth(sOn.getPhysicalScreenWidth()
* OFF_SCREEN_SCALE);
sOff.setPhysicalScreenHeight(sOn.getPhysicalScreenHeight()
* OFF_SCREEN_SCALE);
// attach the offscreen canvas to the view
u.getViewer().getView().addCanvas3D(offScreenCanvas);
}
contentPane.add("Center", canvas);
// setup the env nodes and their GUI elements
setupLights();
setupBackgrounds();
setupFogs();
setupSounds();
// Create a simple scene and attach it to the virtual universe
BranchGroup scene = createSceneGraph();
// set up sound
u.getViewer().createAudioDevice();
// get the view
view = u.getViewer().getView();
// Get the viewing platform
ViewingPlatform viewingPlatform = u.getViewingPlatform();
// Move the viewing platform back to enclose the -4 -> 4 range
double viewRadius = 4.0; // want to be able to see circle
// of viewRadius size around origin
// get the field of view
double fov = u.getViewer().getView().getFieldOfView();
// calc view distance to make circle view in fov
float viewDistance = (float) (viewRadius / Math.tan(fov / 2.0));
tmpVector.set(0.0f, 0.0f, viewDistance);// setup offset
tmpTrans.set(tmpVector); // set trans to translate
// move the view platform
viewingPlatform.getViewPlatformTransform().setTransform(tmpTrans);
// add an orbit behavior to move the viewing platform
OrbitBehavior orbit = new OrbitBehavior(canvas, OrbitBehavior.STOP_ZOOM);
orbit.setSchedulingBounds(infiniteBounds);
viewingPlatform.setViewPlatformBehavior(orbit);
u.addBranchGraph(scene);
contentPane.add("East", guiPanel());
}
// create a panel with a tabbed pane holding each of the edit panels
JPanel guiPanel() {
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
tabbedPane = new JTabbedPane();
tabbedPane.addTab("Light", lightPanel());
tabbedPane.addTab("Background", backgroundPanel());
tabbedPane.addTab("Fog", fogPanel());
tabbedPane.addTab("Sound", soundPanel());
panel.add("Center", tabbedPane);
panel.add("South", configPanel());
return panel;
}
Box lightPanel() {
Box panel = new Box(BoxLayout.Y_AXIS);
// add the ambient light checkbox to the panel
JCheckBox ambientCheckBox = new JCheckBox("Ambient Light");
ambientCheckBox.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JCheckBox checkbox = (JCheckBox) e.getSource();
lightAmbient.setEnable(checkbox.isSelected());
}
});
ambientCheckBox.setSelected(true);
panel.add(new LeftAlignComponent(ambientCheckBox));
String[] lightTypeValues = { "None", "Directional", "Positional",
"Spot" };
IntChooser lightTypeChooser = new IntChooser("Light Type:",
lightTypeValues);
lightTypeChooser.addIntListener(new IntListener() {
public void intChanged(IntEvent event) {
int value = event.getValue();
switch (value) {
case 0:
lightDirectional.setEnable(false);
lightPoint.setEnable(false);
lightSpot.setEnable(false);
break;
case 1:
lightDirectional.setEnable(true);
lightPoint.setEnable(false);
lightSpot.setEnable(false);
break;
case 2:
lightDirectional.setEnable(false);
lightPoint.setEnable(true);
lightSpot.setEnable(false);
break;
case 3:
lightDirectional.setEnable(false);
lightPoint.setEnable(false);
lightSpot.setEnable(true);
break;
}
}
});
lightTypeChooser.setValueByName("Directional");
panel.add(lightTypeChooser);
// Set up the sliders for the attenuation
// top row
panel.add(new LeftAlignComponent(new JLabel("Light attenuation:")));
FloatLabelJSlider constantSlider = new FloatLabelJSlider("Constant ",
0.1f, 0.0f, 3.0f, attenuation.x);
constantSlider.setMajorTickSpacing(1.0f);
constantSlider.setPaintTicks(true);
constantSlider.addFloatListener(new FloatListener() {
public void floatChanged(FloatEvent e) {
attenuation.x = e.getValue();
lightPoint.setAttenuation(attenuation);
lightSpot.setAttenuation(attenuation);
}
});
panel.add(constantSlider);
FloatLabelJSlider linearSlider = new FloatLabelJSlider("Linear ",
0.1f, 0.0f, 3.0f, attenuation.y);
linearSlider.setMajorTickSpacing(1.0f);
linearSlider.setPaintTicks(true);
linearSlider.addFloatListener(new FloatListener() {
public void floatChanged(FloatEvent e) {
attenuation.y = e.getValue();
lightPoint.setAttenuation(attenuation);
lightSpot.setAttenuation(attenuation);
}
});
panel.add(linearSlider);
FloatLabelJSlider quadradicSlider = new FloatLabelJSlider("Quadradic",
0.1f, 0.0f, 3.0f, attenuation.z);
quadradicSlider.setMajorTickSpacing(1.0f);
quadradicSlider.setPaintTicks(true);
quadradicSlider.addFloatListener(new FloatListener() {
public void floatChanged(FloatEvent e) {
attenuation.z = e.getValue();
lightPoint.setAttenuation(attenuation);
lightSpot.setAttenuation(attenuation);
}
});
panel.add(quadradicSlider);
// Set up the sliders for the attenuation
// top row
panel.add(new LeftAlignComponent(new JLabel("Spot light:")));
// spread angle is 0-180 degrees, no slider scaling
FloatLabelJSlider spotSpreadSlider = new FloatLabelJSlider(
"Spread Angle ", 1.0f, 0.0f, 180.0f, spotSpreadAngle);
spotSpreadSlider.addFloatListener(new FloatListener() {
public void floatChanged(FloatEvent e) {
spotSpreadAngle = e.getValue();
lightSpot.setSpreadAngle((float) Math
.toRadians(spotSpreadAngle));
}
});
panel.add(spotSpreadSlider);
// concentration angle is 0-128 degrees
FloatLabelJSlider spotConcentrationSlider = new FloatLabelJSlider(
"Concentration", 1.0f, 0.0f, 128.0f, spotConcentration);
spotConcentrationSlider.addFloatListener(new FloatListener() {
public void floatChanged(FloatEvent e) {
spotConcentration = e.getValue();
lightSpot.setConcentration(spotConcentration);
}
});
panel.add(spotConcentrationSlider);
return panel;
}
JPanel backgroundPanel() {
JPanel panel = new JPanel();
panel.add(bgChooser);
return panel;
}
JPanel fogPanel() {
JPanel panel = new JPanel();
panel.add(fogChooser);
return panel;
}
JPanel soundPanel() {
JPanel panel = new JPanel();
panel.add(soundChooser);
return panel;
}
JPanel configPanel() {
JPanel panel = new JPanel();
panel.setLayout(new GridLayout(1, 0));
String[] dataTypeValues = { "Spheres", "Grid", };
IntChooser dataTypeChooser = new IntChooser("Data:", dataTypeValues);
dataTypeChooser.addIntListener(new IntListener() {
public void intChanged(IntEvent event) {
int value = event.getValue();
switch (value) {
case 0:
spheresSwitch.setWhichChild(Switch.CHILD_ALL);
gridSwitch.setWhichChild(Switch.CHILD_NONE);
break;
case 1:
gridSwitch.setWhichChild(Switch.CHILD_ALL);
spheresSwitch.setWhichChild(Switch.CHILD_NONE);
break;
}
}
});
panel.add(dataTypeChooser);
if (isApplication) {
JButton snapButton = new JButton("Snap Image");
snapButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Point loc = canvas.getLocationOnScreen();
offScreenCanvas.setOffScreenLocation(loc);
Dimension dim = canvas.getSize();
dim.width *= OFF_SCREEN_SCALE;
dim.height *= OFF_SCREEN_SCALE;
nf.setMinimumIntegerDigits(3);
offScreenCanvas.snapImageFile(outFileBase
+ nf.format(outFileSeq++), dim.width, dim.height);
nf.setMinimumIntegerDigits(0);
}
});
panel.add(snapButton);
}
return panel;
}
public void destroy() {
u.removeAllLocales();
}
// The following allows EnvironmentExplorer to be run as an application
// as well as an applet
//
public static void main(String[] args) {
boolean useBlackAndWhite = false;
for (int i = 0; i < args.length; i++) {
//System.out.println("args[" + i + "] = " + args[i]);
if (args[i].equals("-b")) {
System.out.println("Use Black And White");
useBlackAndWhite = true;
}
}
new MainFrame(new EnvironmentExplorer(true, useBlackAndWhite), 950, 600);
}
}
class BackgroundTool implements Java3DExplorerConstants {
Switch bgSwitch;
IntChooser bgChooser;
BackgroundTool(String codeBaseString) {
bgSwitch = new Switch(Switch.CHILD_NONE);
bgSwitch.setCapability(Switch.ALLOW_SWITCH_WRITE);
// set up the dark grey BG color node
Background bgDarkGrey = new Background(darkGrey);
bgDarkGrey.setApplicationBounds(infiniteBounds);
bgSwitch.addChild(bgDarkGrey);
// set up the grey BG color node
Background bgGrey = new Background(grey);
bgGrey.setApplicationBounds(infiniteBounds);
bgSwitch.addChild(bgGrey);
// set up the light grey BG color node
Background bgLightGrey = new Background(lightGrey);
bgLightGrey.setApplicationBounds(infiniteBounds);
bgSwitch.addChild(bgLightGrey);
// set up the white BG color node
Background bgWhite = new Background(white);
bgWhite.setApplicationBounds(infiniteBounds);
bgSwitch.addChild(bgWhite);
// set up the blue BG color node
Background bgBlue = new Background(skyBlue);
bgBlue.setApplicationBounds(infiniteBounds);
bgSwitch.addChild(bgBlue);
// set up the image
java.net.URL bgImageURL = null;
try {
bgImageURL = new java.net.URL(codeBaseString + "bg.jpg");
} catch (java.net.MalformedURLException ex) {
System.out.println(ex.getMessage());
System.exit(1);
}
if (bgImageURL == null) { // application, try file URL
try {
bgImageURL = new java.net.URL("file:./bg.jpg");
} catch (java.net.MalformedURLException ex) {
System.out.println(ex.getMessage());
System.exit(1);
}
}
TextureLoader bgTexture = new TextureLoader(bgImageURL, null);
// Create a background with the static image
Background bgImage = new Background(bgTexture.getImage());
bgImage.setApplicationBounds(infiniteBounds);
bgSwitch.addChild(bgImage);
// create a background with the image mapped onto a sphere which
// will enclose the world
Background bgGeo = new Background();
bgGeo.setApplicationBounds(infiniteBounds);
BranchGroup bgGeoBG = new BranchGroup();
Appearance bgGeoApp = new Appearance();
bgGeoApp.setTexture(bgTexture.getTexture());
Sphere sphereObj = new Sphere(1.0f, Sphere.GENERATE_NORMALS
| Sphere.GENERATE_NORMALS_INWARD
| Sphere.GENERATE_TEXTURE_COORDS, 45, bgGeoApp);
bgGeoBG.addChild(sphereObj);
bgGeo.setGeometry(bgGeoBG);
bgSwitch.addChild(bgGeo);
// Create the chooser GUI
String[] bgNames = { "No Background (Black)", "Dark Grey", "Grey",
"Light Grey", "White", "Blue", "Sky Image", "Sky Geometry", };
int[] bgValues = { Switch.CHILD_NONE, 0, 1, 2, 3, 4, 5, 6 };
bgChooser = new IntChooser("Background:", bgNames, bgValues, 0);
bgChooser.addIntListener(new IntListener() {
public void intChanged(IntEvent event) {
int value = event.getValue();
bgSwitch.setWhichChild(value);
}
});
bgChooser.setValue(Switch.CHILD_NONE);
}
Switch getSwitch() {
return bgSwitch;
}
IntChooser getChooser() {
return bgChooser;
}
}
interface Java3DExplorerConstants {
// colors
static Color3f black = new Color3f(0.0f, 0.0f, 0.0f);
static Color3f red = new Color3f(1.0f, 0.0f, 0.0f);
static Color3f green = new Color3f(0.0f, 1.0f, 0.0f);
static Color3f blue = new Color3f(0.0f, 0.0f, 1.0f);
static Color3f skyBlue = new Color3f(0.6f, 0.7f, 0.9f);
static Color3f cyan = new Color3f(0.0f, 1.0f, 1.0f);
static Color3f magenta = new Color3f(1.0f, 0.0f, 1.0f);
static Color3f yellow = new Color3f(1.0f, 1.0f, 0.0f);
static Color3f brightWhite = new Color3f(1.0f, 1.5f, 1.5f);
static Color3f white = new Color3f(1.0f, 1.0f, 1.0f);
static Color3f darkGrey = new Color3f(0.15f, 0.15f, 0.15f);
static Color3f medGrey = new Color3f(0.3f, 0.3f, 0.3f);
static Color3f grey = new Color3f(0.5f, 0.5f, 0.5f);
static Color3f lightGrey = new Color3f(0.75f, 0.75f, 0.75f);
// infinite bounding region, used to make env nodes active everywhere
BoundingSphere infiniteBounds = new BoundingSphere(new Point3d(),
Double.MAX_VALUE);
// common values
static final String nicestString = "NICEST";
static final String fastestString = "FASTEST";
static final String antiAliasString = "Anti-Aliasing";
static final String noneString = "NONE";
// light type constants
static int LIGHT_AMBIENT = 1;
static int LIGHT_DIRECTIONAL = 2;
static int LIGHT_POSITIONAL = 3;
static int LIGHT_SPOT = 4;
// screen capture constants
static final int USE_COLOR = 1;
static final int USE_BLACK_AND_WHITE = 2;
// number formatter
NumberFormat nf = NumberFormat.getInstance();
}
class IntChooser extends JPanel implements Java3DExplorerConstants {
JComboBox combo;
String[] choiceNames;
int[] choiceValues;
int current;
Vector listeners = new Vector();
IntChooser(String name, String[] initChoiceNames, int[] initChoiceValues,
int initValue) {
if ((initChoiceValues != null)
&& (initChoiceNames.length != initChoiceValues.length)) {
throw new IllegalArgumentException(
"Name and Value arrays must have the same length");
}
choiceNames = new String[initChoiceNames.length];
choiceValues = new int[initChoiceNames.length];
System
.arraycopy(initChoiceNames, 0, choiceNames, 0,
choiceNames.length);
if (initChoiceValues != null) {
System.arraycopy(initChoiceValues, 0, choiceValues, 0,
choiceNames.length);
} else {
for (int i = 0; i < initChoiceNames.length; i++) {
choiceValues[i] = i;
}
}
// Create the combo box, select the init value
combo = new JComboBox(choiceNames);
combo.setSelectedIndex(current);
combo.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JComboBox cb = (JComboBox) e.getSource();
int index = cb.getSelectedIndex();
setValueIndex(index);
}
});
// set the initial value
current = 0;
setValue(initValue);
// layout to align left
setLayout(new BorderLayout());
Box box = new Box(BoxLayout.X_AXIS);
add(box, BorderLayout.WEST);
box.add(new JLabel(name));
box.add(combo);
}
IntChooser(String name, String[] initChoiceNames, int[] initChoiceValues) {
this(name, initChoiceNames, initChoiceValues, initChoiceValues[0]);
}
IntChooser(String name, String[] initChoiceNames, int initValue) {
this(name, initChoiceNames, null, initValue);
}
IntChooser(String name, String[] initChoiceNames) {
this(name, initChoiceNames, null, 0);
}
public void addIntListener(IntListener listener) {
listeners.add(listener);
}
public void removeIntListener(IntListener listener) {
listeners.remove(listener);
}
public void setValueByName(String newName) {
boolean found = false;
int newIndex = 0;
for (int i = 0; (!found) && (i < choiceNames.length); i++) {
if (newName.equals(choiceNames[i])) {
newIndex = i;
found = true;
}
}
if (found) {
setValueIndex(newIndex);
}
}
public void setValue(int newValue) {
boolean found = false;
int newIndex = 0;
for (int i = 0; (!found) && (i < choiceValues.length); i++) {
if (newValue == choiceValues[i]) {
newIndex = i;
found = true;
}
}
if (found) {
setValueIndex(newIndex);
}
}
public int getValue() {
return choiceValues[current];
}
public String getValueName() {
return choiceNames[current];
}
public void setValueIndex(int newIndex) {
boolean changed = (newIndex != current);
current = newIndex;
if (changed) {
combo.setSelectedIndex(current);
valueChanged();
}
}
private void valueChanged() {
// notify the listeners
IntEvent event = new IntEvent(this, choiceValues[current]);
for (Enumeration e = listeners.elements(); e.hasMoreElements();) {
IntListener listener = (IntListener) e.nextElement();
listener.intChanged(event);
}
}
}
class OffScreenCanvas3D extends Canvas3D {
OffScreenCanvas3D(GraphicsConfiguration graphicsConfiguration,
boolean offScreen) {
super(graphicsConfiguration, offScreen);
}
private BufferedImage doRender(int width, int height) {
BufferedImage bImage = new BufferedImage(width, height,
BufferedImage.TYPE_INT_RGB);
ImageComponent2D buffer = new ImageComponent2D(
ImageComponent.FORMAT_RGB, bImage);
//buffer.setYUp(true);
setOffScreenBuffer(buffer);
renderOffScreenBuffer();
waitForOffScreenRendering();
bImage = getOffScreenBuffer().getImage();
return bImage;
}
void snapImageFile(String filename, int width, int height) {
BufferedImage bImage = doRender(width, height);
/*
* JAI: RenderedImage fImage = JAI.create("format", bImage,
* DataBuffer.TYPE_BYTE); JAI.create("filestore", fImage, filename +
* ".tif", "tiff", null);
*/
/* No JAI: */
try {
FileOutputStream fos = new FileOutputStream(filename + ".jpg");
BufferedOutputStream bos = new BufferedOutputStream(fos);
JPEGImageEncoder jie = JPEGCodec.createJPEGEncoder(bos);
JPEGEncodeParam param = jie.getDefaultJPEGEncodeParam(bImage);
param.setQuality(1.0f, true);
jie.setJPEGEncodeParam(param);
jie.encode(bImage);
bos.flush();
fos.close();
} catch (Exception e) {
System.out.println(e);
}
}
}
interface IntListener extends EventListener {
void intChanged(IntEvent e);
}
class IntEvent extends EventObject {
int value;
IntEvent(Object source, int newValue) {
super(source);
value = newValue;
}
int getValue() {
return value;
}
}
class KeyPrintBehavior extends Behavior {
WakeupCondition wakeup = new WakeupOnAWTEvent(KeyEvent.KEY_PRESSED);
public void initialize() {
wakeupOn(wakeup);
}
public void processStimulus(Enumeration criteria) {
while (criteria.hasMoreElements()) {
wakeup = (WakeupCriterion) criteria.nextElement();
if (wakeup instanceof WakeupOnAWTEvent) {
AWTEvent[] evt = ((WakeupOnAWTEvent) wakeup).getAWTEvent();
for (int i = 0; i < evt.length; i++) {
if (evt[i] instanceof KeyEvent) {
KeyEvent keyEvt = (KeyEvent) evt[i];
System.out.println("Key pressed: '"
+ keyEvt.getKeyChar() + "'");
}
}
}
}
// set the wakeup so we'll get the next event
wakeupOn(wakeup);
}
}
class LeftAlignComponent extends JPanel {
LeftAlignComponent(Component c) {
setLayout(new BorderLayout());
add(c, BorderLayout.WEST);
}
}
class FloatLabelJSlider extends JPanel implements ChangeListener,
Java3DExplorerConstants {
JSlider slider;
JLabel valueLabel;
Vector listeners = new Vector();
float min, max, resolution, current, scale;
int minInt, maxInt, curInt;;
int intDigits, fractDigits;
float minResolution = 0.001f;
// default slider with name, resolution = 0.1, min = 0.0, max = 1.0 inital
// 0.5
FloatLabelJSlider(String name) {
this(name, 0.1f, 0.0f, 1.0f, 0.5f);
}
FloatLabelJSlider(String name, float resolution, float min, float max,
float current) {
this.resolution = resolution;
this.min = min;
this.max = max;
this.current = current;
if (resolution < minResolution) {
resolution = minResolution;
}
// round scale to nearest integer fraction. i.e. 0.3 => 1/3 = 0.33
scale = (float) Math.round(1.0f / resolution);
resolution = 1.0f / scale;
// get the integer versions of max, min, current
minInt = Math.round(min * scale);
maxInt = Math.round(max * scale);
curInt = Math.round(current * scale);
// sliders use integers, so scale our floating point value by "scale"
// to make each slider "notch" be "resolution". We will scale the
// value down by "scale" when we get the event.
slider = new JSlider(JSlider.HORIZONTAL, minInt, maxInt, curInt);
slider.addChangeListener(this);
valueLabel = new JLabel(" ");
// set the initial value label
setLabelString();
// add min and max labels to the slider
Hashtable labelTable = new Hashtable();
labelTable.put(new Integer(minInt), new JLabel(nf.format(min)));
labelTable.put(new Integer(maxInt), new JLabel(nf.format(max)));
slider.setLabelTable(labelTable);
slider.setPaintLabels(true);
/* layout to align left */
setLayout(new BorderLayout());
Box box = new Box(BoxLayout.X_AXIS);
add(box, BorderLayout.WEST);
box.add(new JLabel(name));
box.add(slider);
box.add(valueLabel);
}
public void setMinorTickSpacing(float spacing) {
int intSpacing = Math.round(spacing * scale);
slider.setMinorTickSpacing(intSpacing);
}
public void setMajorTickSpacing(float spacing) {
int intSpacing = Math.round(spacing * scale);
slider.setMajorTickSpacing(intSpacing);
}
public void setPaintTicks(boolean paint) {
slider.setPaintTicks(paint);
}
public void addFloatListener(FloatListener listener) {
listeners.add(listener);
}
public void removeFloatListener(FloatListener listener) {
listeners.remove(listener);
}
public void stateChanged(ChangeEvent e) {
JSlider source = (JSlider) e.getSource();
// get the event type, set the corresponding value.
// Sliders use integers, handle floating point values by scaling the
// values by "scale" to allow settings at "resolution" intervals.
// Divide by "scale" to get back to the real value.
curInt = source.getValue();
current = curInt / scale;
valueChanged();
}
public void setValue(float newValue) {
boolean changed = (newValue != current);
current = newValue;
if (changed) {
valueChanged();
}
}
private void valueChanged() {
// update the label
setLabelString();
// notify the listeners
FloatEvent event = new FloatEvent(this, current);
for (Enumeration e = listeners.elements(); e.hasMoreElements();) {
FloatListener listener = (FloatListener) e.nextElement();
listener.floatChanged(event);
}
}
void setLabelString() {
// Need to muck around to try to make sure that the width of the label
// is wide enough for the largest value. Pad the string
// be large enough to hold the largest value.
int pad = 5; // fudge to make up for variable width fonts
float maxVal = Math.max(Math.abs(min), Math.abs(max));
intDigits = Math.round((float) (Math.log(maxVal) / Math.log(10))) + pad;
if (min < 0) {
intDigits++; // add one for the '-'
}
// fractDigits is num digits of resolution for fraction. Use base 10 log
// of scale, rounded up, + 2.
fractDigits = (int) Math.ceil((Math.log(scale) / Math.log(10)));
nf.setMinimumFractionDigits(fractDigits);
nf.setMaximumFractionDigits(fractDigits);
String value = nf.format(current);
while (value.length() < (intDigits + fractDigits)) {
value = value + " ";
}
valueLabel.setText(value);
}
}
class FloatEvent extends EventObject {
float value;
FloatEvent(Object source, float newValue) {
super(source);
value = newValue;
}
float getValue() {
return value;
}
}
interface FloatListener extends EventListener {
void floatChanged(FloatEvent e);
}
|