|
This Java tip illustrates the use of point lights in Java 3D scenes.
import java.applet.Applet;
import java.awt.AWTEvent;
import java.awt.BorderLayout;
import java.awt.CheckboxMenuItem;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Frame;
import java.awt.Menu;
import java.awt.MenuBar;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseEvent;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.io.File;
import java.util.Enumeration;
import java.util.EventListener;
import javax.media.j3d.Appearance;
import javax.media.j3d.Behavior;
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.GeometryArray;
import javax.media.j3d.Group;
import javax.media.j3d.Light;
import javax.media.j3d.LineArray;
import javax.media.j3d.LineAttributes;
import javax.media.j3d.Material;
import javax.media.j3d.PointLight;
import javax.media.j3d.Shape3D;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.media.j3d.WakeupCriterion;
import javax.media.j3d.WakeupOnAWTEvent;
import javax.media.j3d.WakeupOnElapsedFrames;
import javax.media.j3d.WakeupOr;
import javax.vecmath.Color3f;
import javax.vecmath.Matrix4d;
import javax.vecmath.Matrix4f;
import javax.vecmath.Point3d;
import javax.vecmath.Point3f;
import javax.vecmath.Vector3d;
import javax.vecmath.Vector3f;
import com.sun.j3d.utils.geometry.Cone;
import com.sun.j3d.utils.geometry.Primitive;
import com.sun.j3d.utils.geometry.Sphere;
import com.sun.j3d.utils.universe.PlatformGeometry;
import com.sun.j3d.utils.universe.SimpleUniverse;
import com.sun.j3d.utils.universe.Viewer;
import com.sun.j3d.utils.universe.ViewingPlatform;
public class ExPointLight extends Java3DFrame {
//--------------------------------------------------------------
// SCENE CONTENT
//--------------------------------------------------------------
//
// Nodes (updated via menu)
//
private PointLight light = null;
//
// Build scene
//
public Group buildScene() {
// Get the current color, position, and attenuation
Color3f color = (Color3f) colors[currentColor].value;
Point3f pos = (Point3f) positions[currentPosition].value;
Point3f atten = (Point3f) attenuations[currentAttenuation].value;
// Turn off the example headlight
setHeadlightEnable(false);
// Build the scene root
Group scene = new Group();
// BEGIN EXAMPLE TOPIC
// Create influencing bounds
BoundingSphere worldBounds = new BoundingSphere(new Point3d(0.0, 0.0,
0.0), // Center
1000.0); // Extent
// Set the light color and its influencing bounds
light = new PointLight();
light.setEnable(lightOnOff);
light.setColor(color);
light.setPosition(pos);
light.setAttenuation(atten);
light.setCapability(PointLight.ALLOW_STATE_WRITE);
light.setCapability(PointLight.ALLOW_COLOR_WRITE);
light.setCapability(PointLight.ALLOW_POSITION_WRITE);
light.setCapability(PointLight.ALLOW_ATTENUATION_WRITE);
light.setInfluencingBounds(worldBounds);
scene.addChild(light);
// END EXAMPLE TOPIC
// Build foreground geometry
scene.addChild(new SphereGroup());
// Add arrows in a fan to show light ray directions
scene.addChild(buildArrows());
return scene;
}
//--------------------------------------------------------------
// FOREGROUND AND ANNOTATION CONTENT
//--------------------------------------------------------------
//
// Create a fan of annotation arrows aiming in all directions,
// but in the XY plane. Next, build an array of Transform3D's,
// one for each of the light positions shown on the positions
// menu. Save these Transform3Ds and a top-level TransformGroup
// surrounding the arrows. Later, when the user selects a new
// light position, we poke the corresponding Transform3D into
// the TransformGroup to cause the arrows to move to a new
// position.
//
private Transform3D[] arrowPositionTransforms = null;
private TransformGroup arrowPositionTransformGroup = null;
private Group buildArrows() {
// Create a transform group surrounding the arrows.
// Enable writing of its transform.
arrowPositionTransformGroup = new TransformGroup();
arrowPositionTransformGroup
.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
// Create a group of arrows in a fan and add that group
// to the transform group.
AnnotationArrowFan af = new AnnotationArrowFan(0.0f, 0.0f, 0.0f, // center
// position
2.5f, // arrow length
0.0f, // start angle
(float) (Math.PI * 2.0 * 7.0 / 8.0),// end angle
8); // number of arrows
arrowPositionTransformGroup.addChild(af);
// Create a set of Transform3Ds for the different
// arrow positions.
arrowPositionTransforms = new Transform3D[positions.length];
Point3f pos;
Vector3f v = new Vector3f();
for (int i = 0; i < positions.length; i++) {
// Create a Transform3D, setting its translation.
arrowPositionTransforms[i] = new Transform3D();
pos = (Point3f) positions[i].value;
v.set(pos);
arrowPositionTransforms[i].setTranslation(v);
}
// Set the initial transform to be the current position
arrowPositionTransformGroup
.setTransform(arrowPositionTransforms[currentPosition]);
return arrowPositionTransformGroup;
}
//--------------------------------------------------------------
// USER INTERFACE
//--------------------------------------------------------------
//
// Main
//
public static void main(String[] args) {
ExPointLight ex = new ExPointLight();
ex.initialize(args);
ex.buildUniverse();
ex.showFrame();
}
// On/off choices
private boolean lightOnOff = true;
private CheckboxMenuItem lightOnOffMenu;
// Color menu choices
private NameValue[] colors = { new NameValue("White", White),
new NameValue("Gray", Gray), new NameValue("Black", Black),
new NameValue("Red", Red), new NameValue("Yellow", Yellow),
new NameValue("Green", Green), new NameValue("Cyan", Cyan),
new NameValue("Blue", Blue), new NameValue("Magenta", Magenta), };
private int currentColor = 0;
private CheckboxMenu colorMenu = null;
// Position menu choices
private NameValue[] positions = { new NameValue("Origin", Origin),
new NameValue("+X", PlusX), new NameValue("-X", MinusX),
new NameValue("+Y", PlusY), new NameValue("-Y", MinusY),
new NameValue("+Z", PlusZ), new NameValue("-Z", MinusZ), };
private int currentPosition = 0;
private CheckboxMenu positionMenu = null;
// Attenuation menu choices
private NameValue[] attenuations = {
new NameValue("Constant", new Point3f(1.0f, 0.0f, 0.0f)),
new NameValue("Linear", new Point3f(0.0f, 1.0f, 0.0f)),
new NameValue("Quadratic", new Point3f(0.0f, 0.0f, 1.0f)), };
private int currentAttenuation = 0;
private CheckboxMenu attenuationMenu = null;
//
// Initialize the GUI (application and applet)
//
public void initialize(String[] args) {
// Initialize the window, menubar, etc.
super.initialize(args);
exampleFrame.setTitle("Java 3D Point Light Example");
//
// Add a menubar menu to change node parameters
// Light on/off
// Color -->
// Position -->
// Attenuation -->
//
Menu m = new Menu("PointLight");
lightOnOffMenu = new CheckboxMenuItem("Light on/off", lightOnOff);
lightOnOffMenu.addItemListener(this);
m.add(lightOnOffMenu);
colorMenu = new CheckboxMenu("Color", colors, currentColor, this);
m.add(colorMenu);
positionMenu = new CheckboxMenu("Position", positions, currentPosition,
this);
m.add(positionMenu);
attenuationMenu = new CheckboxMenu("Attenuation", attenuations,
currentAttenuation, this);
m.add(attenuationMenu);
exampleMenuBar.add(m);
}
//
// Handle checkboxes and menu choices
//
public void checkboxChanged(CheckboxMenu menu, int check) {
if (menu == colorMenu) {
// Change the light color
currentColor = check;
Color3f color = (Color3f) colors[check].value;
light.setColor(color);
return;
}
if (menu == positionMenu) {
// Change the light position
currentPosition = check;
Point3f pos = (Point3f) positions[check].value;
light.setPosition(pos);
// Change the arrow group position
arrowPositionTransformGroup
.setTransform(arrowPositionTransforms[check]);
return;
}
if (menu == attenuationMenu) {
// Change the light attenuation
currentAttenuation = check;
Point3f atten = (Point3f) attenuations[check].value;
light.setAttenuation(atten);
return;
}
// Handle all other checkboxes
super.checkboxChanged(menu, check);
}
public void itemStateChanged(ItemEvent event) {
Object src = event.getSource();
if (src == lightOnOffMenu) {
// Turn the light on or off
lightOnOff = lightOnOffMenu.getState();
light.setEnable(lightOnOff);
return;
}
// Handle all other checkboxes
super.itemStateChanged(event);
}
}
//
//CLASS
//AnnotationArrowFan - A group of arrows in a fan
//
//DESCRIPTION
//This class creates one or more 3D, unlighted arrows arranged in a
//fan around the xyz position. Such arrow fans can be used to indicate
//point light directions, and so forth.
//
//The arrow fan is drawn in the XY plane, pointing right (middle arrow).
//The fan origin, arrow length, start and end angles, and number of
//arrows all may be controlled.
//
//SEE ALSO
//AnnotationArrow
//AnnotationArrowGroup
//
//AUTHOR
//David R. Nadeau / San Diego Supercomputer Center
//
//
class AnnotationArrowFan extends Group {
// 3D nodes
AnnotationArrow[] arrows;
// Constructors
public AnnotationArrowFan() {
// xyz length start/end angles count
this(0.0f, 0.0f, 0.0f, 1.0f, 1.571f, -1.571f, 5);
}
public AnnotationArrowFan(float x, float y, float z, float length,
float startAngle, float endAngle, int count) {
arrows = new AnnotationArrow[count];
float x2, y2;
float angle = startAngle;
float deltaAngle = (endAngle - startAngle) / (float) (count - 1);
for (int i = 0; i < count; i++) {
x2 = (float) (length * Math.cos(angle));
y2 = (float) (length * Math.sin(angle));
arrows[i] = new AnnotationArrow(x, y, z, x2, y2, z);
addChild(arrows[i]);
angle += deltaAngle;
}
}
}
//
//CLASS
//AnnotationArrowGroup - A group of parallel arrows
//
//DESCRIPTION
//This class creates one or more parallel 3D, unlighted arrows.
//Such arrow groups can be used to indicate directional light
//directions, and so forth.
//
//The arrow group is drawn in the XY plane, pointing right.
//The X start and end values, and the Y start and end values
//can be set, along with the count of the number of arrows to
//build.
//
//SEE ALSO
//AnnotationArrow
//AnnotationArrowFan
//
//AUTHOR
//David R. Nadeau / San Diego Supercomputer Center
//
//
class AnnotationArrowGroup extends Group {
// 3D nodes
AnnotationArrow[] arrows;
// Constructors
public AnnotationArrowGroup() {
// xStart xEnd yStart yEnd count
this(-1.0f, 1.0f, 1.0f, -1.0f, 3);
}
public AnnotationArrowGroup(float xStart, float xEnd, float yStart,
float yEnd, int count) {
arrows = new AnnotationArrow[count];
float y = yStart;
float deltaY = (yEnd - yStart) / (float) (count - 1);
for (int i = 0; i < count; i++) {
arrows[i] = new AnnotationArrow(xStart, y, 0.0f, xEnd, y, 0.0f);
addChild(arrows[i]);
y += deltaY;
}
}
}
//
//CLASS
//AnnotationArrow - 3D arrow used for annotation & diagrams
//
//DESCRIPTION
//This class creates a 3D, unlighted line between two 3D coordinates
//plus a cone-shaped arrow at the line's endpoint. The line's width
//and color can be controlled. The arrow head's width and length
//can be controlled.
//
//SEE ALSO
//AnnotationLine
//AnnotationAxes
//AnnotationArrowFan
//AnnotationArrowGroup
//
//AUTHOR
//David R. Nadeau / San Diego Supercomputer Center
//
class AnnotationArrow extends AnnotationLine {
// Parameters
private Color3f arrowColor = new Color3f(1.0f, 1.0f, 1.0f);
private float arrowRadius = 0.1f;
private float arrowLength = 0.20f;
private float lineWidth = 3.0f;
private int radialDivisions = 8;
private int sideDivisions = 1;
// 3D Nodes
private Cone arrowHead = null;
private Appearance arrowAppearance = null;
private TransformGroup arrowTrans = null;
private ColoringAttributes coloringAttributes = null;
//
// Construct a straight line
//
public AnnotationArrow(float x2, float y2, float z2) {
// origin to given coordinate
this(0.0f, 0.0f, 0.0f, x2, y2, z2);
}
public AnnotationArrow(float x, float y, float z, float x2, float y2,
float z2) {
super(x, y, z, x2, y2, z2);
setLineWidth(lineWidth);
// Compute the length and direction of the line
float deltaX = x2 - x;
float deltaY = y2 - y;
float deltaZ = z2 - z;
float theta = -(float) Math.atan2(deltaZ, deltaX);
float phi = (float) Math.atan2(deltaY, deltaX);
if (deltaX < 0.0f) {
phi = (float) Math.PI - phi;
}
// Compute a matrix to rotate a cone to point in the line's
// direction, then place the cone at the line's endpoint.
Matrix4f mat = new Matrix4f();
Matrix4f mat2 = new Matrix4f();
mat.setIdentity();
// Move to the endpoint of the line
mat2.setIdentity();
mat2.setTranslation(new Vector3f(x2, y2, z2));
mat.mul(mat2);
// Spin around Y
mat2.setIdentity();
mat2.rotY(theta);
mat.mul(mat2);
// Tilt up or down around Z
mat2.setIdentity();
mat2.rotZ(phi);
mat.mul(mat2);
// Tilt cone to point right
mat2.setIdentity();
mat2.rotZ(-1.571f);
mat.mul(mat2);
arrowTrans = new TransformGroup();
arrowTrans.setCapability(Group.ALLOW_CHILDREN_WRITE);
Transform3D trans = new Transform3D(mat);
arrowTrans.setTransform(trans);
// Create an appearance
arrowAppearance = new Appearance();
arrowAppearance
.setCapability(Appearance.ALLOW_COLORING_ATTRIBUTES_WRITE);
getLineColor(arrowColor);
coloringAttributes = new ColoringAttributes();
coloringAttributes.setColor(arrowColor);
coloringAttributes.setShadeModel(ColoringAttributes.SHADE_FLAT);
arrowAppearance.setColoringAttributes(coloringAttributes);
// Build a cone for the arrow head
arrowHead = new Cone(arrowRadius, // base radius
arrowLength, // height
0, // don't generate normals
radialDivisions, // divisions radially
sideDivisions, // divisions vertically
arrowAppearance); // appearance
arrowTrans.addChild(arrowHead);
addChild(arrowTrans);
}
//
// Control the arrow head size
//
public void setArrowHeadRadius(float radius) {
arrowRadius = radius;
arrowTrans.removeChild(0);
arrowHead = new Cone(arrowRadius, // base radius
arrowLength, // height
0, // don't generate normals
radialDivisions, // divisions radially
sideDivisions, // divisions vertically
arrowAppearance); // appearance
arrowTrans.addChild(arrowHead);
}
public void setArrowHeadLength(float length) {
arrowLength = length;
arrowTrans.removeChild(0);
arrowHead = new Cone(arrowRadius, // base radius
arrowLength, // height
0, // don't generate normals
radialDivisions, // divisions radially
sideDivisions, // divisions vertically
arrowAppearance); // appearance
arrowTrans.addChild(arrowHead);
}
public float getArrowHeadRadius() {
return arrowRadius;
}
public float getArrowHeadLength() {
return arrowLength;
}
//
// Control the line color
//
public void setLineColor(Color3f color) {
super.setLineColor(color);
getLineColor(arrowColor);
coloringAttributes.setColor(arrowColor);
arrowAppearance.setColoringAttributes(coloringAttributes);
arrowHead.setAppearance(arrowAppearance);
}
public void setLineColor(float r, float g, float b) {
super.setLineColor(r, g, b);
getLineColor(arrowColor);
coloringAttributes.setColor(arrowColor);
arrowAppearance.setColoringAttributes(coloringAttributes);
arrowHead.setAppearance(arrowAppearance);
}
public void setLineColor(float[] color) {
super.setLineColor(color);
getLineColor(arrowColor);
coloringAttributes.setColor(arrowColor);
arrowAppearance.setColoringAttributes(coloringAttributes);
arrowHead.setAppearance(arrowAppearance);
}
//
// Control the appearance
//
public void setAppearance(Appearance app) {
super.setAppearance(app);
arrowAppearance = app;
arrowAppearance
.setCapability(Appearance.ALLOW_COLORING_ATTRIBUTES_WRITE);
arrowAppearance.setColoringAttributes(coloringAttributes);
arrowHead.setAppearance(arrowAppearance);
}
//
// Provide info on the shape and geometry
//
public Shape3D getShape(int partid) {
if (partid == Cone.BODY)
return arrowHead.getShape(Cone.BODY);
else if (partid == Cone.CAP)
return arrowHead.getShape(Cone.CAP);
else
return super.getShape(partid);
}
public int getNumTriangles() {
return arrowHead.getNumTriangles();
}
public int getNumVertices() {
return arrowHead.getNumVertices() + super.getNumVertices();
}
}
//
//CLASS
//AnnotationLine - 3D line used for annotation & diagrams
//
//DESCRIPTION
//This class creates a 3D, unlighted line between two 3D coordinates.
//The line's width and color can be controlled.
//
//SEE ALSO
//AnnotationArrow
//
//AUTHOR
//David R. Nadeau / San Diego Supercomputer Center
//
//
class AnnotationLine extends Primitive {
// Parameters
private float lineWidth = 1;
private Color3f lineColor = new Color3f(1.0f, 1.0f, 1.0f);
// 3D nodes
private Shape3D shape = null;
private LineAttributes lineAttributes = null;
private ColoringAttributes coloringAttributes = null;
private LineArray line = null;
protected Appearance mainAppearance = null;
//
// Construct a straight line
//
public AnnotationLine(float x2, float y2, float z2) {
// origin to given coordinate
this(0.0f, 0.0f, 0.0f, x2, y2, z2);
}
public AnnotationLine(float x, float y, float z, float x2, float y2,
float z2) {
float[] coord = new float[3];
float[] texcoord = new float[2];
// Build a shape
shape = new Shape3D();
shape.setCapability(Shape3D.ALLOW_APPEARANCE_WRITE);
// Create geometry for a 2-vertex straight line
line = new LineArray(2, GeometryArray.COORDINATES
| GeometryArray.TEXTURE_COORDINATE_2);
line.setCapability(GeometryArray.ALLOW_COLOR_WRITE);
// Starting point
coord[0] = x;
coord[1] = y;
coord[2] = z;
texcoord[0] = 0.0f;
& | |