|
This example creates a simple scene composed of several spheres and a
QuadArray. The scene is lit using 4 lights: AmbientLight, DirectionalLight,
PointLight and SpotLight. Some UI is created for each light to allow the user
to interactively modify the lights' parameters and view the resulting scene.
import java.applet.Applet;
import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Checkbox;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.GraphicsConfigTemplate;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Label;
import java.awt.Panel;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.io.File;
import java.net.URL;
import javax.media.j3d.Alpha;
import javax.media.j3d.AmbientLight;
import javax.media.j3d.Appearance;
import javax.media.j3d.AudioDevice;
import javax.media.j3d.Background;
import javax.media.j3d.BoundingSphere;
import javax.media.j3d.Bounds;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.Canvas3D;
import javax.media.j3d.DirectionalLight;
import javax.media.j3d.GraphicsConfigTemplate3D;
import javax.media.j3d.Group;
import javax.media.j3d.Light;
import javax.media.j3d.Locale;
import javax.media.j3d.Material;
import javax.media.j3d.PhysicalBody;
import javax.media.j3d.PhysicalEnvironment;
import javax.media.j3d.PointLight;
import javax.media.j3d.RotationInterpolator;
import javax.media.j3d.Shape3D;
import javax.media.j3d.SpotLight;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.media.j3d.View;
import javax.media.j3d.ViewPlatform;
import javax.media.j3d.VirtualUniverse;
import javax.swing.JColorChooser;
import javax.vecmath.AxisAngle4d;
import javax.vecmath.Color3f;
import javax.vecmath.Point3d;
import javax.vecmath.Point3f;
import javax.vecmath.Vector3d;
import javax.vecmath.Vector3f;
import com.sun.j3d.audioengines.javasound.JavaSoundMixer;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.geometry.Cone;
import com.sun.j3d.utils.geometry.GeometryInfo;
import com.sun.j3d.utils.geometry.NormalGenerator;
import com.sun.j3d.utils.geometry.Primitive;
import com.sun.j3d.utils.geometry.Sphere;
/**
* This example creates a simple scene composed of several spheres and a
* QuadArray. The scene is lit using 4 lights: AmbientLight, DirectionalLight,
* PointLight and SpotLight. Some UI is created for each light to allow the user
* to interactively modify the lights' parameters and view the resulting scene.
*/
public class LightTest extends Java3dApplet {
private static final int m_kWidth = 400;
private static final int m_kHeight = 400;
public LightTest() {
initJava3d();
}
// create a pop-up Frame to contain the
// UI to control each light.
protected void createLight(LightObject light, BranchGroup objRoot) {
Frame frame = new Frame(light.getName());
Panel aPanel = new Panel();
frame.add(aPanel);
light.addUiToPanel(aPanel);
frame.pack();
frame.setSize(new Dimension(400, 250));
frame.validate();
frame.setVisible(true);
// add the geometry that depicts the light
// to the scenegraph
objRoot.addChild(light.createGeometry());
// finally add the light itself to the scenegraph
objRoot.addChild(light.getLight());
}
// overridden to use a black background
// so we can see the lights better
protected Background createBackground() {
return null;
}
protected BranchGroup createSceneBranchGroup() {
BranchGroup objRoot = super.createSceneBranchGroup();
// create the 4 lights - the actual creation
// and UI managment is delegated to an object
// that "shadows" (no pun intended) the functionality
// of the particular light
createLight(new AmbientLightObject(), objRoot);
createLight(new PointLightObject(), objRoot);
createLight(new DirectionalLightObject(), objRoot);
createLight(new SpotLightObject(), objRoot);
// rotate some of the spheres in the scene
TransformGroup objTrans = new TransformGroup();
objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
Transform3D yAxis = new Transform3D();
Alpha rotationAlpha = new Alpha(-1, Alpha.INCREASING_ENABLE, 0, 0,
4000, 0, 0, 0, 0, 0);
RotationInterpolator rotator = new RotationInterpolator(rotationAlpha,
objTrans, yAxis, 0.0f, (float) Math.PI * 2.0f);
rotator.setSchedulingBounds(getApplicationBounds());
objTrans.addChild(rotator);
// create a large sphere in the center of the
// scene and the floor as staionary objects
objRoot.addChild(createSphere(0, 0, 0, 2));
objRoot.addChild(createFloor());
// create a smaller sphere at the corners of a cube
final int nCubeSize = 3;
objTrans.addChild(createSphere(nCubeSize, nCubeSize, nCubeSize, 1));
objTrans.addChild(createSphere(nCubeSize, nCubeSize, -nCubeSize, 1));
objTrans.addChild(createSphere(nCubeSize, -nCubeSize, nCubeSize, 1));
objTrans.addChild(createSphere(nCubeSize, -nCubeSize, -nCubeSize, 1));
objTrans.addChild(createSphere(-nCubeSize, nCubeSize, nCubeSize, 1));
objTrans.addChild(createSphere(-nCubeSize, nCubeSize, -nCubeSize, 1));
objTrans.addChild(createSphere(-nCubeSize, -nCubeSize, nCubeSize, 1));
objTrans.addChild(createSphere(-nCubeSize, -nCubeSize, -nCubeSize, 1));
// add some small spheres here and there to
// make things interesting
objRoot.addChild(createSphere(-6, -6, 2, 1));
objRoot.addChild(createSphere(8, -5, 3, 1));
objRoot.addChild(createSphere(6, 7, -1, 1));
objRoot.addChild(createSphere(-5, 6, -3.5f, 0.5f));
objRoot.addChild(objTrans);
return objRoot;
}
// creates a QuadArray and uses per-vertex
// colors to make a black and white pattern
protected BranchGroup createFloor() {
final int LAND_WIDTH = 12;
final float LAND_HEIGHT = -4.0f;
final int LAND_LENGTH = 12;
final int nTileSize = 2;
// calculate how many vertices we need to store all the "tiles"
// that compose the QuadArray.
final int nNumTiles = ((LAND_LENGTH / nTileSize) * 2)
* ((LAND_WIDTH / nTileSize) * 2);
final int nVertexCount = 4 * nNumTiles;
Point3f[] coordArray = new Point3f[nVertexCount];
Color3f[] colorArray = new Color3f[nVertexCount];
// create an Appearance
Appearance app = new Appearance();
// create the parent BranchGroup
BranchGroup bg = new BranchGroup();
int nItem = 0;
Color3f whiteColor = new Color3f(1, 1, 1);
Color3f blackColor = new Color3f(0, 0, 0);
// loop over all the tiles in the environment
for (int x = -LAND_WIDTH; x <= LAND_WIDTH; x += nTileSize) {
for (int z = -LAND_LENGTH; z <= LAND_LENGTH; z += nTileSize) {
// if we are not on the last row or column create a "tile"
// and add to the QuadArray. Use CCW winding and assign texture
// coordinates.
if (z < LAND_LENGTH && x < LAND_WIDTH) {
coordArray[nItem] = new Point3f(x, LAND_HEIGHT, z);
colorArray[nItem++] = blackColor;
coordArray[nItem] = new Point3f(x, LAND_HEIGHT, z
+ nTileSize);
colorArray[nItem++] = whiteColor;
coordArray[nItem] = new Point3f(x + nTileSize, LAND_HEIGHT,
z + nTileSize);
colorArray[nItem++] = blackColor;
coordArray[nItem] = new Point3f(x + nTileSize, LAND_HEIGHT,
z);
colorArray[nItem++] = whiteColor;
}
}
}
// create a GeometryInfo and generate Normal vectors
// for the QuadArray that was populated.
GeometryInfo gi = new GeometryInfo(GeometryInfo.QUAD_ARRAY);
gi.setCoordinates(coordArray);
gi.setColors(colorArray);
NormalGenerator normalGenerator = new NormalGenerator();
normalGenerator.generateNormals(gi);
// wrap the GeometryArray in a Shape3D
Shape3D shape = new Shape3D(gi.getGeometryArray(), app);
// add the Shape3D to the parent BranchGroup
bg.addChild(shape);
return bg;
}
// helper method to create and position a sphere of a give size
protected Group createSphere(float x, float y, float z, float radius) {
TransformGroup tg = new TransformGroup();
Transform3D t3d = new Transform3D();
t3d.setTranslation(new Vector3d(x, y, z));
tg.setTransform(t3d);
// create an Appearance and Material
Appearance app = new Appearance();
Color3f objColor = new Color3f(1.0f, 0.7f, 0.8f);
Color3f black = new Color3f(0.0f, 0.0f, 0.0f);
app.setMaterial(new Material(objColor, black, objColor, black, 80.0f));
tg.addChild(new Sphere(radius, Primitive.GENERATE_NORMALS, app));
return tg;
}
public static void main(String[] args) {
LightTest lightTest = new LightTest();
lightTest.saveCommandLineArguments(args);
new MainFrame(lightTest, m_kWidth, m_kHeight);
}
}
//abstract base class that implements the
//basic "Light" class UI functionality
abstract class LightObject implements ActionListener, ItemListener {
protected Panel m_Panel = null;
protected JColorChooser m_ColorChooser = null;
TextField m_XTextField = null;
TextField m_YTextField = null;
TextField m_ZTextField = null;
TextField m_RadiusTextField = null;
Checkbox m_EnableCheck = null;
protected Light m_Light = null;
public LightObject() {
m_Light = createLight();
m_Light.setInfluencingBounds(new BoundingSphere(new Point3d(0, 0, 0),
100));
m_ColorChooser = new JColorChooser();
int[] caps = getCapabilities();
if (caps != null) {
for (int n = 0; n < caps.length; n++)
m_Light.setCapability(caps[n]);
}
}
protected Light createLight() {
return null;
}
public Light getLight() {
return m_Light;
}
public String getName() {
return "Light";
}
protected int[] getCapabilities() {
int[] caps = new int[8];
int nIndex = 0;
caps[nIndex++] = Light.ALLOW_COLOR_READ;
caps[nIndex++] = Light.ALLOW_COLOR_WRITE;
caps[nIndex++] = Light.ALLOW_INFLUENCING_BOUNDS_READ;
caps[nIndex++] = Light.ALLOW_INFLUENCING_BOUNDS_WRITE;
caps[nIndex++] = Light.ALLOW_SCOPE_READ;
caps[nIndex++] = Light.ALLOW_SCOPE_WRITE;
caps[nIndex++] = Light.ALLOW_STATE_READ;
caps[nIndex++] = Light.ALLOW_STATE_WRITE;
return caps;
}
public Group createGeometry() {
Group g = new Group();
m_Light.setUserData(this);
return g;
}
public void addUiToPanel(Panel panel) {
Button colorButton = new Button("Color");
colorButton.addActionListener(this);
panel.add(colorButton);
m_EnableCheck = new Checkbox("Enable", true);
m_EnableCheck.addItemListener(this);
panel.add(m_EnableCheck);
panel.add(new Label("Bounds:"));
panel.add(new Label("X:"));
m_XTextField = new TextField(3);
panel.add(m_XTextField);
panel.add(new Label("Y:"));
m_YTextField = new TextField(3);
panel.add(m_YTextField);
panel.add(new Label("Z:"));
m_ZTextField = new TextField(3);
panel.add(m_ZTextField);
panel.add(new Label("Radius:"));
m_RadiusTextField = new TextField(4);
panel.add(m_RadiusTextField);
Button updateButton = new Button("Update");
updateButton.addActionListener(this);
panel.add(updateButton);
synchLightToUi();
}
public void actionPerformed(ActionEvent event) {
if (event.getActionCommand().equals("Color") != false)
OnColor();
else if (event.getActionCommand().equals("Update") != false)
synchLightToUi();
}
public void itemStateChanged(ItemEvent event) {
m_Light.setEnable(event.getStateChange() == ItemEvent.SELECTED);
}
protected void OnColor() {
Color rgb = m_ColorChooser.showDialog(m_Panel, "Set Light Color", null);
if (rgb != null) {
m_Light
.setColor(new Color3f((float) rgb.getRed() / 255f,
(float) rgb.getGreen() / 255f, (float) rgb
.getBlue() / 255f));
}
}
public void synchLightToUi() {
m_Light.setEnable(m_EnableCheck.getState());
// set some defaults if things go wrong...
double x = 0;
double y = 0;
double z = 0;
double radius = 100;
try {
x = Double.valueOf(m_XTextField.getText()).doubleValue();
y = Double.valueOf(m_YTextField.getText()).doubleValue();
z = Double.valueOf(m_ZTextField.getText()).doubleValue();
radius = Double.valueOf(m_RadiusTextField.getText()).doubleValue();
} catch (java.lang.NumberFormatException e) {
// invalid numeric input - just ignore.
}
m_Light.setInfluencingBounds(new BoundingSphere(new Point3d(x, y, z),
radius));
}
// this method is a placeholder for some
// code that would synchronize the UI with the
// state of a light. This would allow the user
// to move a light around using a MouseBehavior
// and update the UI to display the new position.
// An exercise for the reader... ;-)
public void synchUiToLight() {
}
protected int[] createCompoundArray(int[] a1, int[] a2) {
int[] aRet = null;
int nTotalLen = 0;
int nLen1 = 0;
int nLen2 = 0;
if (a1 != null) {
nTotalLen += a1.length;
nLen1 = a1.length;
}
if (a2 != null) {
nTotalLen += a2.length;
nLen2 = a2.length;
}
aRet = new int[nTotalLen];
if (a1 != null)
System.arraycopy(a1, 0, aRet, 0, nLen1);
if (a2 != null)
System.arraycopy(a2, 0, aRet, nLen1, a2.length);
return aRet;
}
}
class PointLightObject extends LightObject {
protected TextField m_XPositionTextField = null;
protected TextField m_YPositionTextField = null;
protected TextField m_ZPositionTextField = null;
protected TextField m_ConstantAttenuationTextField = null;
protected TextField m_LinearAttenuationTextField = null;
protected TextField m_QuadraticAttenuationTextField = null;
protected TransformGroup m_TransformGroup = null;
protected Sphere m_Sphere = null;
public PointLightObject() {
}
protected Light createLight() {
return (Light) new PointLight();
}
public String getName() {
return "PointLight";
}
protected int[] getCapabilities() {
int[] superCaps = super.getCapabilities();
int[] caps = { PointLight.ALLOW_ATTENUATION_READ,
PointLight.ALLOW_ATTENUATION_WRITE,
PointLight.ALLOW_POSITION_READ, PointLight.ALLOW_POSITION_WRITE };
return createCompoundArray(superCaps, caps);
}
public Group createGeometry() {
Point3f pos = new Point3f();
((PointLight) m_Light).getPosition(pos);
m_TransformGroup = new TransformGroup();
m_TransformGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
m_TransformGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
Transform3D t3d = new Transform3D();
t3d.setTranslation(new Vector3f(pos.x, pos.y, pos.z));
m_TransformGroup.setTransform(t3d);
m_Sphere = new Sphere(0.2f, Primitive.ENABLE_APPEARANCE_MODIFY
| Primitive.GENERATE_NORMALS, 16);
m_TransformGroup.addChild(m_Sphere);
m_TransformGroup.addChild(super.createGeometry());
return (Group) m_TransformGroup;
}
public void addUiToPanel(Panel panel) {
m_XPositionTextField = new TextField(3);
m_YPositionTextField = new TextField(3);
m_ZPositionTextField = new TextField(3);
m_ConstantAttenuationTextField = new TextField(3);
m_LinearAttenuationTextField = new TextField(3);
m_QuadraticAttenuationTextField = new TextField(3);
panel.add(new Label("Position:"));
panel.add(new Label("X:"));
panel.add(m_XPositionTextField);
panel.add(new Label("Y:"));
panel.add(m_YPositionTextField);
panel.add(new Label("Z:"));
panel.add(m_ZPositionTextField);
panel.add(new Label("Attenuation:"));
panel.add(new Label("Constant:"));
panel.add(m_ConstantAttenuationTextField);
panel.add(new Label("Linear:"));
panel.add(m_LinearAttenuationTextField);
panel.add(new Label("Quadratic:"));
panel.add(m_QuadraticAttenuationTextField);
super.addUiToPanel(panel);
}
public void synchLightToUi() {
super.synchLightToUi();
// set some defaults if things go wrong...
double x = 0;
double y = 0;
double z = 0;
double constant = 0.01;
double linear = 0;
double quadratic = 0;
try {
x = Double.valueOf(m_XPositionTextField.getText()).doubleValue();
y = Double.valueOf(m_YPositionTextField.getText()).doubleValue();
z = Double.valueOf(m_ZPositionTextField.getText()).doubleValue();
constant = Double.valueOf(m_ConstantAttenuationTextField.getText())
.doubleValue();
linear = Double.valueOf(m_LinearAttenuationTextField.getText())
.doubleValue();
quadratic = Double.valueOf(
m_QuadraticAttenuationTextField.getText()).doubleValue();
} catch (java.lang.NumberFormatException e) {
// invalid numeric input - just ignore.
}
((PointLight) m_Light).setPosition((float) x, (float) y, (float) z);
((PointLight) m_Light).setAttenuation((float) constant, (float) linear,
(float) quadratic);
if (m_TransformGroup != null) {
Transform3D t3d = new Transform3D();
m_TransformGroup.getTransform(t3d);
t3d.setTranslation(new Vector3d(x, y, z));
m_TransformGroup.setTransform(t3d);
}
if (m_Sphere != null) {
Appearance app = new Appearance();
Color3f objColor = new Color3f();
m_Light.getColor(objColor);
Color3f black = new Color3f(0.0f, 0.0f, 0.0f);
app.setMaterial(new Material(objColor, black, objColor, black,
80.0f));
m_Sphere.getShape(Sphere.BODY).setAppearance(app);
}
}
public void synchUiToLight() {
super.synchUiToLight();
}
}
class SpotLightObject extends PointLightObject {
protected TextField m_ConcentrationTextField = null;
protected TextField m_SpreadAngleTextField = null;
protected TextField m_XDirectionTextField = null;
protected TextField m_YDirectionTextField = null;
protected TextField m_ZDirectionTextField = null;
protected TransformGroup m_DirectionTransformGroup = null;
protected Cone m_Cone = null;
public SpotLightObject() {
}
protected Light createLight() {
return (Light) new SpotLight();
| |