|
This Java tip ilustrates the use of light influencing bounds, and bounding leaves.
A DirectionalLight node is added to illuminate a scene.
Then its influencing bounds is adjusted.
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.AmbientLight;
import javax.media.j3d.Appearance;
import javax.media.j3d.Behavior;
import javax.media.j3d.BoundingLeaf;
import javax.media.j3d.BoundingSphere;
import javax.media.j3d.Bounds;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.Canvas3D;
import javax.media.j3d.DirectionalLight;
import javax.media.j3d.Group;
import javax.media.j3d.Light;
import javax.media.j3d.Material;
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.Point3d;
import javax.vecmath.Point3f;
import javax.vecmath.Vector3d;
import javax.vecmath.Vector3f;
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 ExLightBounds extends Java3DFrame {
//--------------------------------------------------------------
// SCENE CONTENT
//--------------------------------------------------------------
//
// Nodes (updated via menu)
//
private DirectionalLight light = null;
private Bounds worldBounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0), // Center
1000.0); // Extent
private Bounds smallBounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0), // Center
1.0); // Extent
private Bounds tinyBounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0), // Center
0.2); // Extent
private BoundingLeaf leafBounds = null;
private TransformGroup leafTransformGroup = null;
//
// Build scene
//
public Group buildScene() {
// Get the current bounding leaf position
Point3f pos = (Point3f) positions[currentPosition].value;
// Turn off the example headlight
setHeadlightEnable(false);
// Build the scene group
Group scene = new Group();
// BEGIN EXAMPLE TOPIC
// Create a bounding leaf we'll use or not use depending
// upon menu selections. Put it within a transform group
// so that we can move the leaf about.
leafTransformGroup = new TransformGroup();
leafTransformGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
Transform3D tr = new Transform3D();
tr.setTranslation(new Vector3f(pos));
leafTransformGroup.setTransform(tr);
leafBounds = new BoundingLeaf(worldBounds);
leafBounds.setCapability(BoundingLeaf.ALLOW_REGION_WRITE);
leafTransformGroup.addChild(leafBounds);
scene.addChild(leafTransformGroup);
// Add a directional light whose bounds we'll modify
// Set its color and aim direction
light = new DirectionalLight();
light.setEnable(true);
light.setColor(White);
light.setDirection(new Vector3f(1.0f, 0.0f, -1.0f));
light.setCapability(DirectionalLight.ALLOW_INFLUENCING_BOUNDS_WRITE);
// Set the bounds to be either from the leaf or from
// explicit bounds, depending upon the menu initial state
if (boundingLeafOnOff)
// Use bounding leaf
light.setInfluencingBoundingLeaf(leafBounds);
else
// Use bounds on the light
light.setInfluencingBounds(worldBounds);
// Set the scope list to include nothing initially.
// This defaults to "universal scope" which covers
// everything.
scene.addChild(light);
// Add an ambient light to dimly illuminate the rest of
// the shapes in the scene to help illustrate that the
// directional light is being bounded... otherwise it looks
// like we're just removing shapes from the scene
AmbientLight ambient = new AmbientLight();
ambient.setEnable(true);
ambient.setColor(White);
ambient.setInfluencingBounds(worldBounds);
scene.addChild(ambient);
// END EXAMPLE TOPIC
// Build foreground geometry
scene.addChild(new SphereGroup());
return scene;
}
//--------------------------------------------------------------
// USER INTERFACE
//--------------------------------------------------------------
//
// Main
//
public static void main(String[] args) {
ExLightBounds ex = new ExLightBounds();
ex.initialize(args);
ex.buildUniverse();
ex.showFrame();
}
// Bounds mode On/off choices
private boolean boundingLeafOnOff = true;
private CheckboxMenuItem boundingLeafOnOffMenu = null;
// Bounds menu choices
private NameValue[] bounds = { new NameValue("Tiny bounds", tinyBounds),
new NameValue("Small bounds", smallBounds),
new NameValue("Big bounds", worldBounds), };
private int currentBounds = 2;
private CheckboxMenu boundsMenu = 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;
//
// Initialize the GUI (application and applet)
//
public void initialize(String[] args) {
// Initialize the window, menubar, etc.
super.initialize(args);
exampleFrame.setTitle("Java 3D Light Bounds Example");
//
// Add a menubar menu to change node parameters
// Use bounding leaf
// Bounds size -->
// Bounding leaf position -->
//
Menu m = new Menu("DirectionalLight");
boundingLeafOnOffMenu = new CheckboxMenuItem("Use bounding leaf",
boundingLeafOnOff);
boundingLeafOnOffMenu.addItemListener(this);
m.add(boundingLeafOnOffMenu);
boundsMenu = new CheckboxMenu("Bounds size", bounds, currentBounds,
this);
m.add(boundsMenu);
positionMenu = new CheckboxMenu("Bounding leaf position", positions,
currentPosition, this);
if (boundingLeafOnOff)
// Bounding leaf on
positionMenu.setEnabled(true);
else
// Bounding leaf off
positionMenu.setEnabled(false);
m.add(positionMenu);
exampleMenuBar.add(m);
}
//
// Handle checkboxes and menu choices
//
public void checkboxChanged(CheckboxMenu menu, int check) {
if (menu == boundsMenu) {
// Change the light bounds
currentBounds = check;
Bounds bou = (Bounds) bounds[check].value;
if (boundingLeafOnOff) {
// Change the bounding leaf's bounds
leafBounds.setRegion(bou);
// Kick the light to get it to update
// its bounds now that the leaf has
// changed... (only necessary in the
// Alpha release of Java3D)
light.setInfluencingBoundingLeaf(leafBounds);
} else {
// Change the light's own bounds
light.setInfluencingBounds(bou);
}
return;
}
if (menu == positionMenu) {
// Change the bounding leaf position
currentPosition = check;
Point3f pos = (Point3f) positions[check].value;
Transform3D tr = new Transform3D();
tr.setTranslation(new Vector3f(pos));
leafTransformGroup.setTransform(tr);
// Kick the light to get it to update
// its bounds now that the leaf has
// changed... (only necessary in the
// Alpha release of Java3D)
light.setInfluencingBoundingLeaf(leafBounds);
return;
}
// Handle all other checkboxes
super.checkboxChanged(menu, check);
}
public void itemStateChanged(ItemEvent event) {
Object src = event.getSource();
if (src == boundingLeafOnOffMenu) {
boundingLeafOnOff = boundingLeafOnOffMenu.getState();
if (boundingLeafOnOff) {
// Use the bounding leaf
light.setInfluencingBoundingLeaf(leafBounds);
// A bounding leaf overrides bounds,
// but for neatness we can turn them off
// (doesn't work in Alpha release of Java3D)
light.setInfluencingBounds(null);
positionMenu.setEnabled(true);
} else {
// Use bounds on the light itself
Bounds bou = (Bounds) bounds[currentBounds].value;
light.setInfluencingBoundingLeaf(null);
light.setInfluencingBounds(bou);
positionMenu.setEnabled(false);
}
return;
}
// Handle all other checkboxes
super.itemStateChanged(event);
}
}
//
//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 o | |