//
//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 void setLineColor(Color3f color) {
lineColor.set(color);
coloringAttributes.setColor(lineColor);
mainAppearance.setColoringAttributes(coloringAttributes);
shape.setAppearance(mainAppearance);
}
public void setLineColor(float r, float g, float b) {
lineColor.set(r, g, b);
coloringAttributes.setColor(lineColor);
mainAppearance.setColoringAttributes(coloringAttributes);
shape.setAppearance(mainAppearance);
}
public void setLineColor(float[] color) {
lineColor.set(color);
coloringAttributes.setColor(lineColor);
mainAppearance.setColoringAttributes(coloringAttributes);
shape.setAppearance(mainAppearance);
}
//
// Control the appearance
//
public void setAppearance(Appearance app) {
mainAppearance = app;
mainAppearance.setCapability(Appearance.ALLOW_LINE_ATTRIBUTES_WRITE);
mainAppearance
.setCapability(Appearance.ALLOW_COLORING_ATTRIBUTES_WRITE);
mainAppearance.setLineAttributes(lineAttributes);
mainAppearance.setColoringAttributes(coloringAttributes);
shape.setAppearance(mainAppearance);
}
//
// Provide info on the shape and geometry
//
public Shape3D getShape(int partid) {
return shape;
}
public int getNumTriangles() {
return 0;
}
public int getNumVertices() {
return 2;
}
/* (non-Javadoc)
* @see com.sun.j3d.utils.geometry.Primitive#getAppearance(int)
*/
public Appearance getAppearance(int arg0) {
// TODO Auto-generated method stub
return null;
}
}
//
//CLASS
//SphereGroup - create a group of spheres on the XY plane
//
//DESCRIPTION
//An XY grid of spheres is created. The number of spheres in X and Y,
//the spacing in X and Y, the sphere radius, and the appearance can
//all be set.
//
//This grid of spheres is used by several of the examples as a generic
//bit of foreground geometry.
//
//SEE ALSO
//Ex*Light
//ExBackground*
//
//AUTHOR
//David R. Nadeau / San Diego Supercomputer Center
//
class SphereGroup extends Group {
// Constructors
public SphereGroup() {
// radius x,y spacing x,y count appearance
this(0.25f, 0.75f, 0.75f, 5, 5, null);
}
public SphereGroup(Appearance app) {
// radius x,y spacing x,y count appearance
this(0.25f, 0.75f, 0.75f, 5, 5, app);
}
public SphereGroup(float radius, float xSpacing, float ySpacing,
int xCount, int yCount) {
this(radius, xSpacing, ySpacing, xCount, yCount, null);
}
public SphereGroup(float radius, float xSpacing, float ySpacing,
int xCount, int yCount, Appearance app) {
if (app == null) {
app = new Appearance();
Material material = new Material();
material.setDiffuseColor(new Color3f(0.8f, 0.8f, 0.8f));
material.setSpecularColor(new Color3f(0.0f, 0.0f, 0.0f));
material.setShininess(0.0f);
app.setMaterial(material);
}
double xStart = -xSpacing * (double) (xCount - 1) / 2.0;
double yStart = -ySpacing * (double) (yCount - 1) / 2.0;
Sphere sphere = null;
TransformGroup trans = null;
Transform3D t3d = new Transform3D();
Vector3d vec = new Vector3d();
double x, y = yStart, z = 0.0;
for (int i = 0; i < yCount; i++) {
x = xStart;
for (int j = 0; j < xCount; j++) {
vec.set(x, y, z);
t3d.setTranslation(vec);
trans = new TransformGroup(t3d);
addChild(trans);
sphere = new Sphere(radius, // sphere radius
Primitive.GENERATE_NORMALS, // generate normals
16, // 16 divisions radially
app); // it's appearance
trans.addChild(sphere);
x += xSpacing;
}
y += ySpacing;
}
}
}
/**
* The Example class is a base class extended by example applications. The class
* provides basic features to create a top-level frame, add a menubar and
* Canvas3D, build the universe, set up "examine" and "walk" style navigation
* behaviors, and provide hooks so that subclasses can add 3D content to the
* example's universe.
* <P>
* Using this Example class simplifies the construction of example applications,
* enabling the author to focus upon 3D content and not the busywork of creating
* windows, menus, and universes.
*
* @version 1.0, 98/04/16
* @author David R. Nadeau, San Diego Supercomputer Center
*/
class Java3DFrame extends Applet implements WindowListener, ActionListener,
ItemListener, CheckboxMenuListener {
// Navigation types
public final static int Walk = 0;
public final static int Examine = 1;
// Should the scene be compiled?
private boolean shouldCompile = true;
// GUI objects for our subclasses
protected Java3DFrame example = null;
protected Frame exampleFrame = null;
protected MenuBar exampleMenuBar = null;
protected Canvas3D exampleCanvas = null;
protected TransformGroup exampleViewTransform = null;
protected TransformGroup exampleSceneTransform = null;
protected boolean debug = false;
// Private GUI objects and state
private boolean headlightOnOff = true;
private int navigationType = Examine;
private CheckboxMenuItem headlightMenuItem = null;
private CheckboxMenuItem walkMenuItem = null;
private CheckboxMenuItem examineMenuItem = null;
private DirectionalLight headlight = null;
private ExamineViewerBehavior examineBehavior = null;
private WalkViewerBehavior walkBehavior = null;
//--------------------------------------------------------------
// ADMINISTRATION
//--------------------------------------------------------------
/**
* The main program entry point when invoked as an application. Each example
* application that extends this class must define their own main.
*
* @param args
* a String array of command-line arguments
*/
public static void main(String[] args) {
Java3DFrame ex = new Java3DFrame();
ex.initialize(args);
ex.buildUniverse();
ex.showFrame();
}
/**
* Constructs a new Example object.
*
* @return a new Example that draws no 3D content
*/
public Java3DFrame() {
// Do nothing
}
/**
* Initializes the application when invoked as an applet.
*/
public void init() {
// Collect properties into String array
String[] args = new String[2];
// NOTE: to be done still...
this.initialize(args);
this.buildUniverse();
this.showFrame();
// NOTE: add something to the browser page?
}
/**
* Initializes the Example by parsing command-line arguments, building an
* AWT Frame, constructing a menubar, and creating the 3D canvas.
*
* @param args
* a String array of command-line arguments
*/
protected void initialize(String[] args) {
example = this;
// Parse incoming arguments
parseArgs(args);
// Build the frame
if (debug)
System.err.println("Building GUI...");
exampleFrame = new Frame();
exampleFrame.setSize(640, 480);
exampleFrame.setTitle("Java 3D Example");
exampleFrame.setLayout(new BorderLayout());
// Set up a close behavior
exampleFrame.addWindowListener(this);
// Create a canvas
exampleCanvas = new Canvas3D(null);
exampleCanvas.setSize(630, 460);
exampleFrame.add("Center", exampleCanvas);
// Build the menubar
exampleMenuBar = this.buildMenuBar();
exampleFrame.setMenuBar(exampleMenuBar);
// Pack
exampleFrame.pack();
exampleFrame.validate();
// exampleFrame.setVisible( true );
}
/**
* Parses incoming command-line arguments. Applications that subclass this
* class may override this method to support their own command-line
* arguments.
*
* @param args
* a String array of command-line arguments
*/
protected void parseArgs(String[] args) {
for (int i = 0; i < args.length; i++) {
if (args[i].equals("-d"))
debug = true;
}
}
//--------------------------------------------------------------
// SCENE CONTENT
//--------------------------------------------------------------
/**
* Builds the 3D universe by constructing a virtual universe (via
* SimpleUniverse), a view platform (via SimpleUniverse), and a view (via
* SimpleUniverse). A headlight is added and a set of behaviors initialized
* to handle navigation types.
*/
protected void buildUniverse() {
//
// Create a SimpleUniverse object, which builds:
//
// - a Locale using the given hi-res coordinate origin
//
// - a ViewingPlatform which in turn builds:
// - a MultiTransformGroup with which to move the
// the ViewPlatform about
//
// - a ViewPlatform to hold the view
//
// - a BranchGroup to hold avatar geometry (if any)
//
// - a BranchGroup to hold view platform
// geometry (if any)
//
// - a Viewer which in turn builds:
// - a PhysicalBody which characterizes the user's
// viewing preferences and abilities
//
// - a PhysicalEnvironment which characterizes the
// user's rendering hardware and software
//
// - a JavaSoundMixer which initializes sound
// support within the 3D environment
//
// - a View which renders the scene into a Canvas3D
//
// All of these actions could be done explicitly, but
// using the SimpleUniverse utilities simplifies the code.
//
if (debug)
System.err.println("Building scene graph...");
SimpleUniverse universe = new SimpleUniverse(null, // Hi-res coordinate
// for the origin -
// use default
1, // Number of transforms in MultiTransformGroup
exampleCanvas, // Canvas3D into which to draw
null); // URL for user configuration file - use defaults
//
// Get the viewer and create an audio device so that
// sound will be enabled in this content.
//
Viewer viewer = universe.getViewer();
viewer.createAudioDevice();
//
// Get the viewing platform created by SimpleUniverse.
// From that platform, get the inner-most TransformGroup
// in the MultiTransformGroup. That inner-most group
// contains the ViewPlatform. It is this inner-most
// TransformGroup we need in order to:
//
// - add a "headlight" that always aims forward from
// the viewer
//
// - change the viewing direction in a "walk" style
//
// The inner-most TransformGroup's transform will be
// changed by the walk behavior (when enabled).
//
ViewingPlatform viewingPlatform = universe.getViewingPlatform();
exampleViewTransform = viewingPlatform.getViewPlatformTransform();
//
// Create a "headlight" as a forward-facing directional light.
// Set the light's bounds to huge. Since we want the light
// on the viewer's "head", we need the light within the
// TransformGroup containing the ViewPlatform. The
// ViewingPlatform class creates a handy hook to do this
// called "platform geometry". The PlatformGeometry class is
// subclassed off of BranchGroup, and is intended to contain
// a description of the 3D platform itself... PLUS a headlight!
// So, to add the headlight, create a new PlatformGeometry group,
// add the light to it, then add that platform geometry to the
// ViewingPlatform.
//
BoundingSphere allBounds = new BoundingSphere(
new Point3d(0.0, 0.0, 0.0), 100000.0);
PlatformGeometry pg = new PlatformGeometry();
headlight = new DirectionalLight();
headlight.setColor(White);
headlight.setDirection(new Vector3f(0.0f, 0.0f, -1.0f));
headlight.setInfluencingBounds(allBounds);
headlight.setCapability(Light.ALLOW_STATE_WRITE);
pg.addChild(headlight);
viewingPlatform.setPlatformGeometry(pg);
//
// Create the 3D content BranchGroup, containing:
//
// - a TransformGroup who's transform the examine behavior
// will change (when enabled).
//
// - 3D geometry to view
//
// Build the scene root
BranchGroup sceneRoot = new BranchGroup();
// Build a transform that we can modify
exampleSceneTransform = new TransformGroup();
exampleSceneTransform
.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
exampleSceneTransform
.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
exampleSceneTransform.setCapability(Group.ALLOW_CHILDREN_EXTEND);
//
// Build the scene, add it to the transform, and add
// the transform to the scene root
//
if (debug)
System.err.println(" scene...");
Group scene = this.buildScene();
exampleSceneTransform.addChild(scene);
sceneRoot.addChild(exampleSceneTransform);
//
// Create a pair of behaviors to implement two navigation
// types:
//
// - "examine": a style where mouse drags rotate about
// the scene's origin as if it is an object under
// examination. This is similar to the "Examine"
// navigation type used by VRML browsers.
//
// - "walk": a style where mouse drags rotate about
// the viewer's center as if the viewer is turning
// about to look at a scene they are in. This is
// similar to the "Walk" navigation type used by
// VRML browsers.
//
// Aim the examine behavior at the scene's TransformGroup
// and add the behavior to the scene root.
//
// Aim the walk behavior at the viewing platform's
// TransformGroup and add the behavior to the scene root.
//
// Enable one (and only one!) of the two behaviors
// depending upon the current navigation type.
//
examineBehavior = new ExamineViewerBehavior(exampleSceneTransform, // Transform
// gorup
// to
// modify
exampleFrame); // Parent frame for cusor changes
examineBehavior.setSchedulingBounds(allBounds);
sceneRoot.addChild(examineBehavior);
walkBehavior = new WalkViewerBehavior(exampleViewTransform, // Transform
// group to
// modify
exampleFrame); // Parent frame for cusor changes
walkBehavior.setSchedulingBounds(allBounds);
sceneRoot.addChild(walkBehavior);
if (navigationType == Walk) {
examineBehavior.setEnable(false);
walkBehavior.setEnable(true);
} else {
examineBehavior.setEnable(true);
walkBehavior.setEnable(false);
}
//
// Compile the scene branch group and add it to the
// SimpleUniverse.
//
if (shouldCompile)
sceneRoot.compile();
universe.addBranchGraph(sceneRoot);
reset();
}
/**
* Builds the scene. Example application subclasses should replace this
* method with their own method to build 3D content.
*
* @return a Group containing 3D content to display
*/
public Group buildScene() {
// Build the scene group containing nothing
Group scene = new Group();
return scene;
}
//--------------------------------------------------------------
// SET/GET METHODS
//--------------------------------------------------------------
/**
* Sets the headlight on/off state. The headlight faces forward in the
* direction the viewer is facing. Example applications that add their own
* lights will typically turn the headlight off. A standard menu item
* enables the headlight to be turned on and off via user control.
*
* @param onOff
* a boolean turning the light on (true) or off (false)
*/
public void setHeadlightEnable(boolean onOff) {
headlightOnOff = onOff;
if (headlight != null)
headlight.setEnable(headlightOnOff);
if (headlightMenuItem != null)
headlightMenuItem.setState(headlightOnOff);
}
/**
* Gets the headlight on/off state.
*
* @return a boolean indicating if the headlight is on or off
*/
public boolean getHeadlightEnable() {
return headlightOnOff;
}
/**
* Sets the navigation type to be either Examine or Walk. The Examine
* navigation type sets up behaviors that use mouse drags to rotate and
* translate scene content as if it is an object held at arm's length and
* under examination. The Walk navigation type uses mouse drags to rotate
* and translate the viewer as if they are walking through the content. The
* Examine type is the default.
*
* @param nav
* either Walk or Examine
*/
public void setNavigationType(int nav) {
if (nav == Walk) {
navigationType = Walk;
if (walkMenuItem != null)
walkMenuItem.setState(true);
if (examineMenuItem != null)
examineMenuItem.setState(false);
if (walkBehavior != null)
walkBehavior.setEnable(true);
if (examineBehavior != null)
examineBehavior.setEnable(false);
} else {
navigationType = Examine;
if (walkMenuItem != null)
walkMenuItem.setState(false);
if (examineMenuItem != null)
examineMenuItem.setState(true);
if (walkBehavior != null)
walkBehavior.setEnable(false);
if (examineBehavior != null)
examineBehavior.setEnable(true);
}
}
/**
* Gets the current navigation type, returning either Walk or Examine.
*
* @return either Walk or Examine
*/
public int getNavigationType() {
return navigationType;
}
/**
* Sets whether the scene graph should be compiled or not. Normally this is
* always a good idea. For some example applications that use this Example
* framework, it is useful to disable compilation - particularly when nodes
* and node components will need to be made un-live in order to make
* changes. Once compiled, such components can be made un-live, but they are
* still unchangable unless appropriate capabilities have been set.
*
* @param onOff
* a boolean turning compilation on (true) or off (false)
*/
public void setCompilable(boolean onOff) {
shouldCompile = onOff;
}
/**
* Gets whether the scene graph will be compiled or not.
*
* @return a boolean indicating if scene graph compilation is on or off
*/
public boolean getCompilable() {
return shouldCompile;
}
//These methods will be replaced
// Set the view position and direction
public void setViewpoint(Point3f position, Vector3f direction) {
Transform3D t = new Transform3D();
t.set(new Vector3f(position));
exampleViewTransform.setTransform(t);
// how to set direction?
}
// Reset transforms
public void reset() {
Transform3D trans = new Transform3D();
exampleSceneTransform.setTransform(trans);
trans.set(new Vector3f(0.0f, 0.0f, 10.0f));
exampleViewTransform.setTransform(trans);
setNavigationType(navigationType);
}
//
// Gets the URL (with file: prepended) for the current directory.
// This is a terrible hack needed in the Alpha release of Java3D
// in order to build a full path URL for loading sounds with
// MediaContainer. When MediaContainer is fully implemented,
// it should handle relative path names, but not yet.
//
public String getCurrentDirectory() {
// Create a bogus file so that we can query it's path
File dummy = new File("dummy.tmp");
String dummyPath = dummy.getAbsolutePath();
// strip "/dummy.tmp" from end of dummyPath and put into 'path'
if (dummyPath.endsWith(File.separator + "dummy.tmp")) {
int index = dummyPath.lastIndexOf(File.separator + "dummy.tmp");
if (index >= 0) {
int pathLength = index + 5; // pre-pend 'file:'
char[] charPath = new char[pathLength];
dummyPath.getChars(0, index, charPath, 5);
String path = new String(charPath, 0, pathLength);
path = "file:" + path.substring(5, pathLength);
return path + File.separator;
}
}
return dummyPath + File.separator;
}
//--------------------------------------------------------------
// USER INTERFACE
//--------------------------------------------------------------
/**
* Builds the example AWT Frame menubar. Standard menus and their options
* are added. Applications that subclass this class should build their
* menubar additions within their initialize method.
*
* @return a MenuBar for the AWT Frame
*/
private MenuBar buildMenuBar() {
// Build the menubar
MenuBar menuBar = new MenuBar();
// File menu
Menu m = new Menu("File");
m.addActionListener(this);
m.add("Exit");
menuBar.add(m);
// View menu
m = new Menu("View");
m.addActionListener(this);
m.add("Reset view");
m.addSeparator();
walkMenuItem = new CheckboxMenuItem("Walk");
walkMenuItem.addItemListener(this);
m.add(walkMenuItem);
examineMenuItem = new CheckboxMenuItem("Examine");
examineMenuItem.addItemListener(this);
m.add(examineMenuItem);
if (navigationType == Walk) {
walkMenuItem.setState(true);
examineMenuItem.setState(false);
} else {
walkMenuItem.setState(false);
examineMenuItem.setState(true);
}
m.addSeparator();
headlightMenuItem = new CheckboxMenuItem("Headlight on/off");
headlightMenuItem.addItemListener(this);
headlightMenuItem.setState(headlightOnOff);
m.add(headlightMenuItem);
menuBar.add(m);
return menuBar;
}
/**
* Shows the application's frame, making it and its menubar, 3D canvas, and
* 3D content visible.
*/
public void showFrame() {
exampleFrame.show();
}
/**
* Quits the application.
*/
public void quit() {
System.exit(0);
}
/**
* Handles menu selections.
*
* @param event
* an ActionEvent indicating what menu action requires handling
*/
public void actionPerformed(ActionEvent event) {
String arg = event.getActionCommand();
if (arg.equals("Reset view"))
reset();
else if (arg.equals("Exit"))
quit();
}
/**
* Handles checkbox items on a CheckboxMenu. The Example class has none of
* its own, but subclasses may have some.
*
* @param menu
* which CheckboxMenu needs action
* @param check
* which CheckboxMenu item has changed
*/
public void checkboxChanged(CheckboxMenu menu, int check) {
// None for us
}
/**
* Handles on/off checkbox items on a standard menu.
*
* @param event
* an ItemEvent indicating what requires handling
*/
public void itemStateChanged(ItemEvent event) {
Object src = event.getSource();
boolean state;
if (src == headlightMenuItem) {
state = headlightMenuItem.getState();
headlight.setEnable(state);
} else if (src == walkMenuItem)
setNavigationType(Walk);
else if (src == examineMenuItem)
setNavigationType(Examine);
}
/**
* Handles a window closing event notifying the application that the user
* has chosen to close the application without selecting the "Exit" menu
* item.
*
* @param event
* a WindowEvent indicating the window is closing
*/
public void windowClosing(WindowEvent event) {
quit();
}
public void windowClosed(WindowEvent event) {
}
public void windowOpened(WindowEvent event) {
}
public void windowIconified(WindowEvent event) {
}
public void windowDeiconified(WindowEvent event) {
}
public void windowActivated(WindowEvent event) {
}
public void windowDeactivated(WindowEvent event) {
}
// Well known colors, positions, and directions
public final static Color3f White = new Color3f(1.0f, 1.0f, 1.0f);
public final static Color3f Gray = new Color3f(0.7f, 0.7f, 0.7f);
public final static Color3f DarkGray = new Color3f(0.2f, 0.2f, 0.2f);
public final static Color3f Black = new Color3f(0.0f, 0.0f, 0.0f);
public final static Color3f Red = new Color3f(1.0f, 0.0f, 0.0f);
public final static Color3f DarkRed = new Color3f(0.3f, 0.0f, 0.0f);
public final static Color3f Yellow = new Color3f(1.0f, 1.0f, 0.0f);
public final static Color3f DarkYellow = new Color3f(0.3f, 0.3f, 0.0f);
public final static Color3f Green = new Color3f(0.0f, 1.0f, 0.0f);
public final static Color3f DarkGreen = new Color3f(0.0f, 0.3f, 0.0f);
public final static Color3f Cyan = new Color3f(0.0f, 1.0f, 1.0f);
public final static Color3f Blue = new Color3f(0.0f, 0.0f, 1.0f);
public final static Color3f DarkBlue = new Color3f(0.0f, 0.0f, 0.3f);
public final static Color3f Magenta = new Color3f(1.0f, 0.0f, 1.0f);
public final static Vector3f PosX = new Vector3f(1.0f, 0.0f, 0.0f);
public final static Vector3f NegX = new Vector3f(-1.0f, 0.0f, 0.0f);
public final static Vector3f PosY = new Vector3f(0.0f, 1.0f, 0.0f);
public final static Vector3f NegY = new Vector3f(0.0f, -1.0f, 0.0f);
public final static Vector3f PosZ = new Vector3f(0.0f, 0.0f, 1.0f);
public final static Vector3f NegZ = new Vector3f(0.0f, 0.0f, -1.0f);
public final static Point3f Origin = new Point3f(0.0f, 0.0f, 0.0f);
public final static Point3f PlusX = new Point3f(0.75f, 0.0f, 0.0f);
public final static Point3f MinusX = new Point3f(-0.75f, 0.0f, 0.0f);
public final static Point3f PlusY = new Point3f(0.0f, 0.75f, 0.0f);
public final static Point3f MinusY = new Point3f(0.0f, -0.75f, 0.0f);
public final static Point3f PlusZ = new Point3f(0.0f, 0.0f, 0.75f);
public final static Point3f MinusZ = new Point3f(0.0f, 0.0f, -0.75f);
}
//
//INTERFACE
//CheckboxMenuListener - listen for checkbox change events
//
//DESCRIPTION
//The checkboxChanged method is called by users of this class
//to notify the listener when a checkbox choice has changed on
//a CheckboxMenu class menu.
//
interface CheckboxMenuListener extends EventListener {
public void checkboxChanged(CheckboxMenu menu, int check);
}
/**
* ExamineViewerBehavior
*
* @version 1.0, 98/04/16
*/
/**
* Wakeup on mouse button presses, releases, and mouse movements and generate
* transforms in an "examination style" that enables the user to rotate,
* translation, and zoom an object as if it is held at arm's length. Such an
* examination style is similar to the "Examine" navigation type used by VRML
* browsers.
*
* The behavior maps mouse drags to different transforms depending upon the
* mosue button held down:
*
* Button 1 (left) Horizontal movement --> Y-axis rotation Vertical movement -->
* X-axis rotation
*
* Button 2 (middle) Horizontal movement --> nothing Vertical movement -->
* Z-axis translation
*
* Button 3 (right) Horizontal movement --> X-axis translation Vertical movement
* --> Y-axis translation
*
* To support systems with 2 or 1 mouse buttons, the following alternate
* mappings are supported while dragging with any mouse button held down and
* zero or more keyboard modifiers held down:
*
* No modifiers = Button 1 ALT = Button 2 Meta = Button 3 Control = Button 3
*
* The behavior automatically modifies a TransformGroup provided to the
* constructor. The TransformGroup's transform can be set at any time by the
* application or other behaviors to cause the examine rotation and translation
* to be reset.
*/
// This class is inspired by the MouseBehavior, MouseRotate,
// MouseTranslate, and MouseZoom utility behaviors provided with
// Java 3D. This class differs from those utilities in that it:
//
// (a) encapsulates all three behaviors into one in order to
// enforce a specific "Examine" symantic
//
// (b) supports set/get of the rotation and translation factors
// that control the speed of movement.
//
// (c) supports the "Control" modifier as an alternative to the
// "Meta" modifier not present on PC, Mac, and most non-Sun
// keyboards. This makes button3 behavior usable on PCs,
// Macs, and other systems with fewer than 3 mouse buttons.
class ExamineViewerBehavior extends ViewerBehavior {
// Previous cursor location
protected int previousX = 0;
protected int previousY = 0;
// Saved standard cursor
protected Cursor savedCursor = null;
/**
* Construct an examine behavior that listens to mouse movement and button
* presses to generate rotation and translation transforms written into a
* transform group given later with the setTransformGroup( ) method.
*/
public ExamineViewerBehavior() {
super();
}
/**
* Construct an examine behavior that listens to mouse movement and button
* presses to generate rotation and translation transforms written into a
* transform group given later with the setTransformGroup( ) method.
*
* @param parent
* The AWT Component that contains the area generating mouse
* events.
*/
public ExamineViewerBehavior(Component parent) {
super(parent);
}
/**
* Construct an examine behavior that listens to mouse movement and button
* presses to generate rotation and translation transforms written into the
* given transform group.
*
* @param transformGroup
* The transform group to be modified by the behavior.
*/
public ExamineViewerBehavior(TransformGroup transformGroup) {
super();
subjectTransformGroup = transformGroup;
}
/**
* Construct an examine behavior that listens to mouse movement and button
* presses to generate rotation and translation transforms written into the
* given transform group.
*
* @param transformGroup
* The transform group to be modified by the behavior.
* @param parent
* The AWT Component that contains the area generating mouse
* events.
*/
public ExamineViewerBehavior(TransformGroup transformGroup, Component parent) {
super(parent);
subjectTransformGroup = transformGroup;
}
/**
* Respond to a button1 event (press, release, or drag).
*
* @param mouseEvent
* A MouseEvent to respond to.
*/
public void onButton1(MouseEvent mev) {
if (subjectTransformGroup == null)
return;
int x = mev.getX();
int y = mev.getY();
if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
// Mouse button pressed: record position
previousX = x;
previousY = y;
// Change to a "move" cursor
if (parentComponent != null) {
savedCursor = parentComponent.getCursor();
parentComponent.setCursor(Cursor
.getPredefinedCursor(Cursor.HAND_CURSOR));
}
return;
}
if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
// Mouse button released: do nothing
// Switch the cursor back
if (parentComponent != null)
parentComponent.setCursor(savedCursor);
return;
}
//
// Mouse moved while button down: create a rotation
//
// Compute the delta in X and Y from the previous
// position. Use the delta to compute rotation
// angles with the mapping:
//
// positive X mouse delta --> positive Y-axis rotation
// positive Y mouse delta --> positive X-axis rotation
//
// where positive X mouse movement is to the right, and
// positive Y mouse movement is **down** the screen.
//
int deltaX = x - previousX;
int deltaY = y - previousY;
if (deltaX > UNUSUAL_XDELTA || deltaX < -UNUSUAL_XDELTA
|| deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {
// Deltas are too huge to be believable. Probably a glitch.
// Don't record the new XY location, or do anything.
return;
}
double xRotationAngle = deltaY * XRotationFactor;
double yRotationAngle = deltaX * YRotationFactor;
//
// Build transforms
//
transform1.rotX(xRotationAngle);
transform2.rotY(yRotationAngle);
// Get and save the current transform matrix
subjectTransformGroup.getTransform(currentTransform);
currentTransform.get(matrix);
translate.set(matrix.m03, matrix.m13, matrix.m23);
// Translate to the origin, rotate, then translate back
currentTransform.setTranslation(origin);
currentTransform.mul(transform1, currentTransform);
currentTransform.mul(transform2, currentTransform);
currentTransform.setTranslation(translate);
// Update the transform group
subjectTransformGroup.setTransform(currentTransform);
previousX = x;
previousY = y;
}
/**
* Respond to a button2 event (press, release, or drag).
*
* @param mouseEvent
* A MouseEvent to respond to.
*/
public void onButton2(MouseEvent mev) {
if (subjectTransformGroup == null)
return;
int x = mev.getX();
int y = mev.getY();
if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
// Mouse button pressed: record position
previousX = x;
previousY = y;
// Change to a "move" cursor
if (parentComponent != null) {
savedCursor = parentComponent.getCursor();
parentComponent.setCursor(Cursor
.getPredefinedCursor(Cursor.MOVE_CURSOR));
}
return;
}
if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
// Mouse button released: do nothing
// Switch the cursor back
if (parentComponent != null)
parentComponent.setCursor(savedCursor);
return;
}
//
// Mouse moved while button down: create a translation
//
// Compute the delta in Y from the previous
// position. Use the delta to compute translation
// distances with the mapping:
//
// positive Y mouse delta --> positive Y-axis translation
//
// where positive X mouse movement is to the right, and
// positive Y mouse movement is **down** the screen.
//
int deltaY = y - previousY;
if (deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {
// Deltas are too huge to be believable. Probably a glitch.
// Don't record the new XY location, or do anything.
return;
}
double zTranslationDistance = deltaY * ZTranslationFactor;
//
// Build transforms
//
translate.set(0.0, 0.0, zTranslationDistance);
transform1.set(translate);
// Get and save the current transform
subjectTransformGroup.getTransform(currentTransform);
// Translate as needed
currentTransform.mul(transform1, currentTransform);
// Update the transform group
subjectTransformGroup.setTransform(currentTransform);
previousX = x;
previousY = y;
}
/**
* Respond to a button3 event (press, release, or drag).
*
* @param mouseEvent
* A MouseEvent to respond to.
*/
public void onButton3(MouseEvent mev) {
if (subjectTransformGroup == null)
return;
int x = mev.getX();
int y = mev.getY();
if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
// Mouse button pressed: record position
previousX = x;
previousY = y;
// Change to a "move" cursor
if (parentComponent != null) {
savedCursor = parentComponent.getCursor();
parentComponent.setCursor(Cursor
.getPredefinedCursor(Cursor.MOVE_CURSOR));
}
return;
}
if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
// Mouse button released: do nothing
// Switch the cursor back
if (parentComponent != null)
parentComponent.setCursor(savedCursor);
return;
}
//
// Mouse moved while button down: create a translation
//
// Compute the delta in X and Y from the previous
// position. Use the delta to compute translation
// distances with the mapping:
//
// positive X mouse delta --> positive X-axis translation
// positive Y mouse delta --> negative Y-axis translation
//
// where positive X mouse movement is to the right, and
// positive Y mouse movement is **down** the screen.
//
int deltaX = x - previousX;
int deltaY = y - previousY;
if (deltaX > UNUSUAL_XDELTA || deltaX < -UNUSUAL_XDELTA
|| deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {
// Deltas are too huge to be believable. Probably a glitch.
// Don't record the new XY location, or do anything.
return;
}
double xTranslationDistance = deltaX * XTranslationFactor;
double yTranslationDistance = -deltaY * YTranslationFactor;
//
// Build transforms
//
translate.set(xTranslationDistance, yTranslationDistance, 0.0);
transform1.set(translate);
// Get and save the current transform
subjectTransformGroup.getTransform(currentTransform);
// Translate as needed
currentTransform.mul(transform1, currentTransform);
// Update the transform group
subjectTransformGroup.setTransform(currentTransform);
previousX = x;
previousY = y;
}
/**
* Respond to an elapsed frames event (assuming subclass has set up a wakeup
* criterion for it).
*
* @param time
* A WakeupOnElapsedFrames criterion to respond to.
*/
public void onElapsedFrames(WakeupOnElapsedFrames timeEvent) {
// Can't happen
}
}
/*
*
* Copyright (c) 1998 David R. Nadeau
*
*/
/**
* WalkViewerBehavior is a utility class that creates a "walking style"
* navigation symantic.
*
* The behavior wakes up on mouse button presses, releases, and mouse movements
* and generates transforms in a "walk style" that enables the user to walk
* through a scene, translating and turning about as if they are within the
* scene. Such a walk style is similar to the "Walk" navigation type used by
* VRML browsers.
* <P>
* The behavior maps mouse drags to different transforms depending upon the
* mouse button held down:
* <DL>
* <DT>Button 1 (left)
* <DD>Horizontal movement --> Y-axis rotation
* <DD>Vertical movement --> Z-axis translation
*
* <DT>Button 2 (middle)
* <DD>Horizontal movement --> Y-axis rotation
* <DD>Vertical movement --> X-axis rotation
*
* <DT>Button 3 (right)
* <DD>Horizontal movement --> X-axis translation
* <DD>Vertical movement --> Y-axis translation
* </DL>
*
* To support systems with 2 or 1 mouse buttons, the following alternate
* mappings are supported while dragging with any mouse button held down and
* zero or more keyboard modifiers held down:
* <UL>
* <LI>No modifiers = Button 1
* <LI>ALT = Button 2
* <LI>Meta = Button 3
* <LI>Control = Button 3
* </UL>
* The behavior automatically modifies a TransformGroup provided to the
* constructor. The TransformGroup's transform can be set at any time by the
* application or other behaviors to cause the walk rotation and translation to
* be reset.
* <P>
* While a mouse button is down, the behavior automatically changes the cursor
* in a given parent AWT Component. If no parent Component is given, no cursor
* changes are attempted.
*
* @version 1.0, 98/04/16
* @author David R. Nadeau, San Diego Supercomputer Center
*/
class WalkViewerBehavior extends ViewerBehavior {
// This class is inspired by the MouseBehavior, MouseRotate,
// MouseTranslate, and MouseZoom utility behaviors provided with
// Java 3D. This class differs from those utilities in that it:
//
// (a) encapsulates all three behaviors into one in order to
// enforce a specific "Walk" symantic
//
// (b) supports set/get of the rotation and translation factors
// that control the speed of movement.
//
// (c) supports the "Control" modifier as an alternative to the
// "Meta" modifier not present on PC, Mac, and most non-Sun
// keyboards. This makes button3 behavior usable on PCs,
// Macs, and other systems with fewer than 3 mouse buttons.
// Previous and initial cursor locations
protected int previousX = 0;
protected int previousY = 0;
protected int initialX = 0;
protected int initialY = 0;
// Deadzone size (delta from initial XY for which no
// translate or rotate action is taken
protected static final int DELTAX_DEADZONE = 10;
protected static final int DELTAY_DEADZONE = 10;
// Keep a set of wakeup criterion for animation-generated
// event types.
protected WakeupCriterion[] mouseAndAnimationEvents = null;
protected WakeupOr mouseAndAnimationCriterion = null;
protected WakeupOr savedMouseCriterion = null;
// Saved standard cursor
protected Cursor savedCursor = null;
/**
* Default Rotation and translation scaling factors for animated movements
* (Button 1 press).
*/
public static final double DEFAULT_YROTATION_ANIMATION_FACTOR = 0.0002;
public static final double DEFAULT_ZTRANSLATION_ANIMATION_FACTOR = 0.01;
protected double YRotationAnimationFactor = DEFAULT_YROTATION_ANIMATION_FACTOR;
protected double ZTranslationAnimationFactor = DEFAULT_ZTRANSLATION_ANIMATION_FACTOR;
/**
* Constructs a new walk behavior that converts mouse actions into rotations
* and translations. Rotations and translations are written into a
* TransformGroup that must be set using the setTransformGroup method. The
* cursor will be changed during mouse actions if the parent frame is set
* using the setParentComponent method.
*
* @return a new WalkViewerBehavior that needs its TransformGroup and parent
* Component set
*/
public WalkViewerBehavior() {
super();
}
/**
* Constructs a new walk behavior that converts mouse actions into rotations
* and translations. Rotations and translations are written into a
* TransformGroup that must be set using the setTransformGroup method. The
* cursor will be changed within the given AWT parent Component during mouse
* drags.
*
* @param parent
* a parent AWT Component within which the cursor will change
* during mouse drags
*
* @return a new WalkViewerBehavior that needs its TransformGroup and parent
* Component set
*/
public WalkViewerBehavior(Component parent) {
super(parent);
}
/**
* Constructs a new walk behavior that converts mouse actions into rotations
* and translations. Rotations and translations are written into the given
* TransformGroup. The cursor will be changed during mouse actions if the
* parent frame is set using the setParentComponent method.
*
* @param transformGroup
* a TransformGroup whos transform is read and written by the
* behavior
*
* @return a new WalkViewerBehavior that needs its TransformGroup and parent
* Component set
*/
public WalkViewerBehavior(TransformGroup transformGroup) {
super();
subjectTransformGroup = transformGroup;
}
/**
* Constructs a new walk behavior that converts mouse actions into rotations
* and translations. Rotations and translations are written into the given
* TransformGroup. The cursor will be changed within the given AWT parent
* Component during mouse drags.
*
* @param transformGroup
* a TransformGroup whos
分享到:
相关推荐
tcp-ip-illustrate-中文版-三卷,steven先生的经典著作,1-3卷完整版。
标题中的"welection-illustrate-directory.rar_6Y8_加工_数控_零件加工"表明这是一个关于数控加工的实例教程,特别关注6Y8型号的零件。"rar"是压缩文件格式,通常用于存储多个相关文件。从描述来看,这个教程着重于...
RSpec-illustrate RSpec和YARD的插件,使您可以在示例中定义说明性对象,这些对象将转发到输出格式化程序。 结果可以导入YARD,从而使您生成的规格和文档更具可读性,说明性和解释性。 源代码位于 。 文档。安装将...
Illustrate! is the market-leading cel and illustration renderer. This latest version includes a completely new Flash rendering engine that provides many advanced rendering features including shading ...
This revised fourth edition of "Drugs in Use" presents a series of clinical case studies to illustrate how pharmacists can optimise drug therapy in response to the needs of individual patients....
《Illustrate! 5.8 for 3DS MAX (64-bit) 插件详解及应用》 在3D建模和动画制作领域,3DS MAX是一款广泛应用的专业软件,其强大的功能和广泛的兼容性深受设计师们的喜爱。而Illustrate! 5.8 for 3DS MAX(64-bit)...
PTC Creo Illustrate是一款3D技术插图软件,该款软件在功能上将卓越的 3D 插图功能与相关的 CAD 数据结合起来,然后在搭配上特定于配置的图形化信息,来让工程师们实现准确反映了当前的产品设计的服务,并且设计师们...
Two demos illustrate use of SimPowerSystems for modeling a PV array connected to a utility grid. PVarray_Grid_IncCondReg_det.mdl is a detailed model of a 100-kW array connected to a 25-kV grid via a ...
This study is aimed at eliminating the influence of the higher-order modes on the ... Finally, a two-stage floating raft isolation system is used to illustrate the effectiveness of the derived results.
《Illustrate 5.6 32位版详解》 Illustrate是一款强大的矢量图形设计软件,主要用于创建高质量的插图、图标、图形以及复杂的艺术作品。本次我们关注的是其5.6版本的32位版本,即"Illustrate 5.6 (32-bit).msi"安装...
Numerical examples illustrate the use of each table and explain the computation of function values which lie outside its range, while the editors' introduction describes higher-order interpolation ...
《3ds Max Illustrate 5.3:提升三维创作的艺术表现力》 3ds Max Illustrate 5.3是一款专为3ds Max 7设计的插件,它为用户提供了强大的2D绘画和渲染功能,旨在增强3D场景的艺术表现力。通过这款插件,设计师能够将...
- **High-Level Bi-Directional Interaction with Python**: Python for Delphi provides a higher level of abstraction, allowing for seamless integration between Python and Delphi code. - **Access to ...
such as the use of function boxes in the first two chapters to visually distinguish functions from data, use of evaltrace notation in later chapters to illustrate the operation of evaluation rules, ...
such as the use of function boxes in the first two chapters to visually distinguish functions from data, use of evaltrace notation in later chapters to illustrate the operation of evaluation rules, ...
《Illustrate! 5.6 for 3dsmax2010-2011:探索卡通渲染的魅力》 在三维动画与设计领域,渲染器是至关重要的工具,它能够将模型、材质、灯光和摄像机设置转化为令人惊叹的视觉效果。其中,Illustrate! 5.6 特别针对3...
《Illustrate 5.4卡通渲染器:探索创新的视觉艺术世界》 Illustrate!作为市场上领先的CEL(Cellular)和插图渲染软件,一直以来都备受业界赞誉。其最新推出的5.4版本更是将卡通渲染技术提升到了新的高度,特别引入...