|
//
//CLASS
//ExDirectionalLight - illustrate use of directional lights
//
//LESSON
//Add a DirectionalLight node to illuminate a scene.
//
//SEE ALSO
//ExAmbientLight
//ExPointLight
//ExSpotLight
//
//AUTHOR
//David R. Nadeau / San Diego Supercomputer Center
//
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.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.AxisAngle4f;
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 ExDirectionalLight extends Java3DFrame {
//--------------------------------------------------------------
// SCENE CONTENT
//--------------------------------------------------------------
//
// Nodes (updated via menu)
//
private DirectionalLight light = null;
//
// Build scene
//
public Group buildScene() {
// Get the current color and direction
Color3f color = (Color3f) colors[currentColor].value;
Vector3f dir = (Vector3f) directions[currentDirection].value;
// Turn off the example headlight
setHeadlightEnable(false);
// Build the scene group
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 DirectionalLight();
light.setEnable(lightOnOff);
light.setColor(color);
light.setDirection(dir);
light.setCapability(DirectionalLight.ALLOW_STATE_WRITE);
light.setCapability(DirectionalLight.ALLOW_COLOR_WRITE);
light.setCapability(DirectionalLight.ALLOW_DIRECTION_WRITE);
light.setInfluencingBounds(worldBounds);
scene.addChild(light);
// END EXAMPLE TOPIC
// Build foreground geometry
scene.addChild(new SphereGroup());
// Add anotation arrows pointing in +-X, +-Y, +-Z to
// illustrate aim direction
scene.addChild(buildArrows());
return scene;
}
//--------------------------------------------------------------
// FOREGROUND AND ANNOTATION CONTENT
//--------------------------------------------------------------
//
// Create a set of annotation arrows initially pointing in
// the +X direciton. Next, build an array of Transform3D's,
// one for each of the aim directions shown on the directions
// menu. Save these Transform3Ds and a top-level TransformGroup
// surrounding the arrows. Later, when the user selects a new
// light direction, we poke the corresponding Transform3D into
// the TransformGroup to cause the arrows to change direction.
//
private TransformGroup arrowDirectionTransformGroup = null;
private Transform3D[] arrowDirectionTransforms = null;
private Group buildArrows() {
// Create a transform group surrounding the arrows.
// Enable writing of its transform.
arrowDirectionTransformGroup = new TransformGroup();
arrowDirectionTransformGroup
.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
// Create a group of arrows and add the group to the
// transform group. The arrows point in the +X direction.
AnnotationArrowGroup ag = new AnnotationArrowGroup(-2.0f, 2.0f, // X
// start
// and
// end
1.5f, -1.5f, // Y start and end
5); // Number of arrows
arrowDirectionTransformGroup.addChild(ag);
// Create a set of Transform3Ds for the different
// arrow directions.
arrowDirectionTransforms = new Transform3D[directions.length];
Vector3f dir = new Vector3f();
Vector3f positiveX = new Vector3f(1.0f, 0.0f, 0.0f);
Vector3f axis = new Vector3f();
float angle;
float dot;
for (int i = 0; i < directions.length; i++) {
// Normalize the direction vector
dir.normalize((Vector3f) directions[i].value);
// Cross the direction vector with the arrow's
// +X aim direction to get a vector orthogonal
// to both. This is the rotation axis.
axis.cross(positiveX, dir);
if (axis.x == 0.0f && axis.y == 0.0f && axis.z == 0.0f) {
// New direction is parallel to current
// arrow direction. Default to a Y axis.
axis.y = 1.0f;
}
// Compute the angle between the direction and +X
// vectors, where:
//
// cos(angle) = (dir dot positiveX)
// -------------------------------
// (positiveX.length * dir.length)
//
// but since positiveX is normalized (as created
// above and dir has been normalized, both have a
// length of 1. So, the angle between the
// vectors is:
//
// angle = arccos(dir dot positiveX)
//
dot = dir.dot(positiveX);
angle = (float) Math.acos(dot);
// Create a Transform3D, setting its rotation using
// an AxisAngle4f, which takes an XYZ rotation vector
// and an angle to rotate by around that vector.
arrowDirectionTransforms[i] = new Transform3D();
arrowDirectionTransforms[i].setRotation(new AxisAngle4f(axis.x,
axis.y, axis.z, angle));
}
// Set the initial transform to be the current aim direction.
arrowDirectionTransformGroup
.setTransform(arrowDirectionTransforms[currentDirection]);
return arrowDirectionTransformGroup;
}
//--------------------------------------------------------------
// USER INTERFACE
//--------------------------------------------------------------
//
// Main
//
public static void main(String[] args) {
ExDirectionalLight ex = new ExDirectionalLight();
ex.initialize(args);
ex.buildUniverse();
ex.showFrame();
}
// On/off choices
private boolean lightOnOff = true;
private CheckboxMenuItem lightOnOffMenu = null;
// 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;
// Direction menu choices
private NameValue[] directions = { new NameValue("Positive X", PosX),
new NameValue("Negative X", NegX),
new NameValue("Positive Y", PosY),
new NameValue("Negative Y", NegY),
new NameValue("Positive Z", PosZ),
new NameValue("Negative Z", NegZ), };
private int currentDirection = 0;
private CheckboxMenu directionMenu = null;
//
// Initialize the GUI (application and applet)
//
public void initialize(String[] args) {
// Initialize the window, menubar, etc.
super.initialize(args);
exampleFrame.setTitle("Java 3D Directional Light Example");
//
// Add a menubar menu to change node parameters
// Light on/off
// Color -->
// Direction -->
//
Menu m = new Menu("DirectionalLight");
lightOnOffMenu = new CheckboxMenuItem("Light on/off", lightOnOff);
lightOnOffMenu.addItemListener(this);
m.add(lightOnOffMenu);
colorMenu = new CheckboxMenu("Color", colors, currentColor, this);
m.add(colorMenu);
directionMenu = new CheckboxMenu("Direction", directions,
currentDirection, this);
m.add(directionMenu);
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 == directionMenu) {
// Change the light direction
currentDirection = check;
Vector3f dir = (Vector3f) directions[check].value;
light.setDirection(dir);
// Change the arrow group direction
arrowDirectionTransformGroup
.setTransform(arrowDirectionTransforms[check]);
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
//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;
texcoord[1] = 0.0f;
line.setCoordinate(0, coord);
line.setTextureCoordinate(0, texcoord);
// Ending point
coord[0] = x2;
coord[1] = y2;
coord[2] = z2;
texcoord[0] = 1.0f;
texcoord[1] = 0.0f;
line.setCoordinate(1, coord);
line.setTextureCoordinate(1, texcoord);
shape.setGeometry(line);
// Create an appearance
mainAppearance = new Appearance();
mainAppearance.setCapability(Appearance.ALLOW_LINE_ATTRIBUTES_WRITE);
mainAppearance
.setCapability(Appearance.ALLOW_COLORING_ATTRIBUTES_WRITE);
lineAttributes = new LineAttributes();
lineAttributes.setLineWidth(lineWidth);
mainAppearance.setLineAttributes(lineAttributes);
coloringAttributes = new ColoringAttributes();
coloringAttributes.setColor(lineColor);
coloringAttributes.setShadeModel(ColoringAttributes.SHADE_FLAT);
mainAppearance.setColoringAttributes(coloringAttributes);
addChild(shape);
}
//
// Control the line width
//
public float getLineWidth() {
return lineWidth;
}
public void setLineWidth(float width) {
lineWidth = width;
lineAttributes.setLineWidth(lineWidth);
mainAppearance.setLineAttributes(lineAttributes);
shape.setAppearance(mainAppearance);
}
//
// Control the line color
//
public void getLineColor(Color3f color) {
lineColor.get(color);
}
public void getLineColor(float[] color) {
lineColor.get(color);
}
public
|