|
//
//CLASS
//ExHenge - create a stone-henge like (vaguely) mysterious temple thing
//
//DESCRIPTION
//This example illustrates the use of a few of Java 3D's lighting
//types to create atmospheric lighting to make a structure look
//like it is glowing. In particular, we build a central emissive
//dome, unaffected by any lighting. Surrounding that dome are a
//series of arches that are lit by a one or more of a point
//light in the center, directional lights at front-left and
//back-right, and two ambient lights. Each of these lights can be
//turned on and off via menu items.
//
//SEE ALSO
//Arch
//ExAmbientLight
//ExDirectionalLight
//ExPointLight
//
//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.AmbientLight;
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.DirectionalLight;
import javax.media.j3d.GeometryArray;
import javax.media.j3d.Group;
import javax.media.j3d.ImageComponent;
import javax.media.j3d.IndexedQuadArray;
import javax.media.j3d.IndexedTriangleStripArray;
import javax.media.j3d.Light;
import javax.media.j3d.Link;
import javax.media.j3d.Material;
import javax.media.j3d.PointLight;
import javax.media.j3d.Shape3D;
import javax.media.j3d.SharedGroup;
import javax.media.j3d.Texture;
import javax.media.j3d.TextureAttributes;
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.image.TextureLoader;
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 ExHenge extends Java3DFrame {
//--------------------------------------------------------------
// SCENE CONTENT
//--------------------------------------------------------------
//
// Nodes (updated via menu)
//
private AmbientLight ambient = null;
private AmbientLight brightAmbient = null;
private DirectionalLight redDirectional = null;
private DirectionalLight yellowDirectional = null;
private PointLight orangePoint = null;
//
// Build scene
//
public Group buildScene() {
// Turn off the example headlight
setHeadlightEnable(false);
// Default to walk navigation
setNavigationType(Walk);
//
// Preload the texture images
//
if (debug)
System.err.println(" textures...");
Texture groundTex = null;
Texture spurTex = null;
Texture domeTex = null;
TextureLoader texLoader = null;
ImageComponent image = null;
texLoader = new TextureLoader("mud01.jpg", this);
image = texLoader.getImage();
if (image == null)
System.err.println("Cannot load mud01.jpg texture");
else {
groundTex = texLoader.getTexture();
groundTex.setBoundaryModeS(Texture.WRAP);
groundTex.setBoundaryModeT(Texture.WRAP);
groundTex.setMinFilter(Texture.NICEST);
groundTex.setMagFilter(Texture.NICEST);
groundTex.setMipMapMode(Texture.BASE_LEVEL);
groundTex.setEnable(true);
}
texLoader = new TextureLoader("stonebrk2.jpg", this);
image = texLoader.getImage();
if (image == null)
System.err.println("Cannot load stonebrk2.jpg texture");
else {
spurTex = texLoader.getTexture();
spurTex.setBoundaryModeS(Texture.WRAP);
spurTex.setBoundaryModeT(Texture.WRAP);
spurTex.setMinFilter(Texture.NICEST);
spurTex.setMagFilter(Texture.NICEST);
spurTex.setMipMapMode(Texture.BASE_LEVEL);
spurTex.setEnable(true);
}
texLoader = new TextureLoader("fire.jpg", this);
image = texLoader.getImage();
if (image == null)
System.err.println("Cannot load fire.jpg texture");
else {
domeTex = texLoader.getTexture();
domeTex.setBoundaryModeS(Texture.WRAP);
domeTex.setBoundaryModeT(Texture.WRAP);
domeTex.setMinFilter(Texture.NICEST);
domeTex.setMagFilter(Texture.NICEST);
domeTex.setMipMapMode(Texture.BASE_LEVEL);
domeTex.setEnable(true);
}
//
// Build some shapes we'll need
//
if (debug)
System.err.println(" flying buttresses...");
// Build three types of spurs (flying buttresses)
Appearance spurApp = new Appearance();
Material spurMat = new Material();
spurMat.setAmbientColor(0.6f, 0.6f, 0.6f);
spurMat.setDiffuseColor(1.0f, 1.0f, 1.0f);
spurMat.setSpecularColor(0.0f, 0.0f, 0.0f);
spurApp.setMaterial(spurMat);
Transform3D tr = new Transform3D();
tr.setIdentity();
tr.setScale(new Vector3d(1.0, 4.0, 1.0));
TextureAttributes spurTexAtt = new TextureAttributes();
spurTexAtt.setTextureMode(TextureAttributes.MODULATE);
spurTexAtt.setPerspectiveCorrectionMode(TextureAttributes.NICEST);
spurTexAtt.setTextureTransform(tr);
spurApp.setTextureAttributes(spurTexAtt);
if (spurTex != null)
spurApp.setTexture(spurTex);
Arch spur1 = new Arch(0.0, // start Phi
1.571, // end Phi
9, // nPhi
-0.0982, // start Theta
0.0982, // end Theta (11.25 degrees)
2, // nTheta
2.5, // start radius
1.0, // end radius
0.05, // start phi thickness
0.025, // end phi thickness
spurApp); // appearance
Arch spur2 = new Arch(0.0, // start Phi
1.571, // end Phi
9, // nPhi
-0.0982, // start Theta
0.0982, // end Theta (11.25 degrees)
2, // nTheta
1.5, // start radius
2.0, // end radius
0.05, // start phi thickness
0.025, // end phi thickness
spurApp); // appearance
Arch spur3 = new Arch(0.0, // start Phi
1.571, // end Phi
9, // nPhi
-0.0982, // start Theta
0.0982, // end Theta (11.25 degrees)
2, // nTheta
1.5, // start radius
1.0, // end radius
0.05, // start phi thickness
0.025, // end phi thickness
spurApp); // appearance
Arch spur4 = new Arch(0.0, // start Phi
1.178, // end Phi
9, // nPhi
-0.0982, // start Theta
0.0982, // end Theta (11.25 degrees)
2, // nTheta
4.0, // start radius
4.0, // end radius
0.05, // start phi thickness
0.025, // end phi thickness
spurApp); // appearance
// Put each spur into a shared group so we can instance
// the spurs multiple times
SharedGroup spur1Group = new SharedGroup();
spur1Group.addChild(spur1);
spur1Group.compile();
SharedGroup spur2Group = new SharedGroup();
spur2Group.addChild(spur2);
spur2Group.compile();
SharedGroup spur3Group = new SharedGroup();
spur3Group.addChild(spur3);
spur3Group.compile();
SharedGroup spur4Group = new SharedGroup();
spur4Group.addChild(spur4);
spur4Group.compile();
// Build a central dome
if (debug)
System.err.println(" central dome...");
Appearance domeApp = new Appearance();
// No material needed - we want the dome to glow,
// so use a REPLACE mode texture only
TextureAttributes domeTexAtt = new TextureAttributes();
domeTexAtt.setTextureMode(TextureAttributes.REPLACE);
domeTexAtt.setPerspectiveCorrectionMode(TextureAttributes.NICEST);
domeApp.setTextureAttributes(domeTexAtt);
if (domeTex != null)
domeApp.setTexture(domeTex);
Arch dome = new Arch(0.0, // start Phi
1.571, // end Phi
5, // nPhi
0.0, // start Theta
2.0 * Math.PI, // end Theta (360 degrees)
17, // nTheta
1.0, // start radius
1.0, // end radius
0.0, // start phi thickness
0.0, // end phi thickness
domeApp); // appearance
// Build the ground. Use a trick to get better lighting
// effects by using an elevation grid. The idea is this:
// for interactive graphics systems, such as those
// controlled by Java3D, lighting effects are computed only
// at triangle vertexes. Imagine a big rectangular ground
// underneath a PointLight (added below). If the
// PointLight is above the center of the square, in the real
// world we'd expect a bright spot below it, fading to
// darkness at the edges of the square. Not so in
// interactive graphics. Since lighting is only computed
// at vertexes, and the square's vertexes are each
// equidistant from a centered PointLight, all four square
// coordinates get the same brightness. That brightness
// is interpolated across the square, giving a *constant*
// brightness for the entire square! There is no bright
// spot under the PointLight. So, here's the trick: use
// more triangles. Pretty simple. Split the ground under
// the PointLight into a grid of smaller squares. Each
// smaller square is shaded using light brightness computed
// at the square's vertexes. Squares directly under the
// PointLight get brighter lighting at their vertexes, and
// thus they are bright. This gives the desired bright
// spot under the PointLight. The more squares we use
// (a denser grid), the more accurate the bright spot and
// the smoother the lighting gradation from bright directly
// under the PointLight, to dark at the distant edges. Of
// course, with more squares, we also get more polygons to
// draw and a performance slow-down. So there is a
// tradeoff between lighting quality and drawing speed.
// For this example, we'll use a coarse mesh of triangles
// created using an ElevationGrid shape.
if (debug)
System.err.println(" ground...");
Appearance groundApp = new Appearance();
Material groundMat = new Material();
groundMat.setAmbientColor(0.3f, 0.3f, 0.3f);
groundMat.setDiffuseColor(0.7f, 0.7f, 0.7f);
groundMat.setSpecularColor(0.0f, 0.0f, 0.0f);
groundApp.setMaterial(groundMat);
tr = new Transform3D();
tr.setScale(new Vector3d(8.0, 8.0, 1.0));
TextureAttributes groundTexAtt = new TextureAttributes();
groundTexAtt.setTextureMode(TextureAttributes.MODULATE);
groundTexAtt.setPerspectiveCorrectionMode(TextureAttributes.NICEST);
groundTexAtt.setTextureTransform(tr);
groundApp.setTextureAttributes(groundTexAtt);
if (groundTex != null)
groundApp.setTexture(groundTex);
ElevationGrid ground = new ElevationGrid(11, // X dimension
11, // Z dimension
2.0f, // X spacing
2.0f, // Z spacing
// Automatically use zero heights
groundApp); // Appearance
//
// Build the scene using the shapes above. Place everything
// withing a TransformGroup.
//
// Build the scene root
TransformGroup scene = new TransformGroup();
tr = new Transform3D();
tr.setTranslation(new Vector3f(0.0f, -1.6f, 0.0f));
scene.setTransform(tr);
// Create influencing bounds
BoundingSphere worldBounds = new BoundingSphere(new Point3d(0.0, 0.0,
0.0), // Center
1000.0); // Extent
// General Ambient light
ambient = new AmbientLight();
ambient.setEnable(ambientOnOff);
ambient.setColor(new Color3f(0.3f, 0.3f, 0.3f));
ambient.setCapability(AmbientLight.ALLOW_STATE_WRITE);
ambient.setInfluencingBounds(worldBounds);
scene.addChild(ambient);
// Bright Ambient light
brightAmbient = new AmbientLight();
brightAmbient.setEnable(brightAmbientOnOff);
brightAmbient.setColor(new Color3f(1.0f, 1.0f, 1.0f));
brightAmbient.setCapability(AmbientLight.ALLOW_STATE_WRITE);
brightAmbient.setInfluencingBounds(worldBounds);
scene.addChild(brightAmbient);
// Red directional light
redDirectional = new DirectionalLight();
redDirectional.setEnable(redDirectionalOnOff);
redDirectional.setColor(new Color3f(1.0f, 0.0f, 0.0f));
redDirectional.setDirection(new Vector3f(1.0f, -0.5f, -0.5f));
redDirectional.setCapability(AmbientLight.ALLOW_STATE_WRITE);
redDirectional.setInfluencingBounds(worldBounds);
scene.addChild(redDirectional);
// Yellow directional light
yellowDirectional = new DirectionalLight();
yellowDirectional.setEnable(yellowDirectionalOnOff);
yellowDirectional.setColor(new Color3f(1.0f, 0.8f, 0.0f));
yellowDirectional.setDirection(new Vector3f(-1.0f, 0.5f, 1.0f));
yellowDirectional.setCapability(AmbientLight.ALLOW_STATE_WRITE);
yellowDirectional.setInfluencingBounds(worldBounds);
scene.addChild(yellowDirectional);
// Orange point light
orangePoint = new PointLight();
orangePoint.setEnable(orangePointOnOff);
orangePoint.setColor(new Color3f(1.0f, 0.5f, 0.0f));
orangePoint.setPosition(new Point3f(0.0f, 0.5f, 0.0f));
orangePoint.setCapability(AmbientLight.ALLOW_STATE_WRITE);
orangePoint.setInfluencingBounds(worldBounds);
scene.addChild(orangePoint);
// Ground
scene.addChild(ground);
// Dome
scene.addChild(dome);
// Spur 1's
Group g = buildRing(spur1Group);
scene.addChild(g);
// Spur 2's
TransformGroup tg = new TransformGroup();
tr = new Transform3D();
tr.rotY(0.3927);
tg.setTransform(tr);
g = buildRing(spur2Group);
tg.addChild(g);
scene.addChild(tg);
// Spur 3's
g = buildRing(spur3Group);
scene.addChild(g);
// Spur 4's
tg = new TransformGroup();
tg.setTransform(tr);
g = buildRing(spur4Group);
tg.addChild(g);
scene.addChild(tg);
return scene;
}
//
// Build a ring of shapes, each shape contained in a given
// shared group
//
public Group buildRing(SharedGroup sg) {
Group g = new Group();
g.addChild(new Link(sg)); // 0 degrees
TransformGroup tg = new TransformGroup();
Transform3D tr = new Transform3D();
tr.rotY(0.785); // 45 degrees
tg.setTransform(tr);
tg.addChild(new Link(sg));
g.addChild(tg);
tg = new TransformGroup();
tr = new Transform3D();
tr.rotY(-0.785); // -45 degrees
tg.setTransform(tr);
tg.addChild(new Link(sg));
g.addChild(tg);
tg = new TransformGroup();
tr = new Transform3D();
tr.rotY(1.571); // 90 degrees
tg.setTransform(tr);
tg.addChild(new Link(sg));
g.addChild(tg);
tg = new TransformGroup();
tr = new Transform3D();
tr.rotY(-1.571); // -90 degrees
tg.setTransform(tr);
tg.addChild(new Link(sg));
g.addChild(tg);
tg = new TransformGroup();
tr = new Transform3D();
tr.rotY(2.356); // 135 degrees
tg.setTransform(tr);
tg.addChild(new Link(sg));
g.addChild(tg);
tg = new TransformGroup();
tr = new Transform3D();
tr.rotY(-2.356); // -135 degrees
tg.setTransform(tr);
tg.addChild(new Link(sg));
g.addChild(tg);
tg = new TransformGroup();
tr = new Transform3D();
tr.rotY(Math.PI); // 180 degrees
tg.setTransform(tr);
tg.addChild(new Link(sg));
g.addChild(tg);
return g;
}
//--------------------------------------------------------------
// USER INTERFACE
//--------------------------------------------------------------
//
// Main
//
public static void main(String[] args) {
ExHenge ex = new ExHenge();
ex.initialize(args);
ex.buildUniverse();
ex.showFrame();
}
// On/off choices
private boolean ambientOnOff = true;
private boolean brightAmbientOnOff = false;
private boolean redDirectionalOnOff = false;
private boolean yellowDirectionalOnOff = false;
private boolean orangePointOnOff = true;
private CheckboxMenuItem ambientOnOffMenu;
private CheckboxMenuItem brightAmbientOnOffMenu;
private CheckboxMenuItem redDirectionalOnOffMenu;
private CheckboxMenuItem yellowDirectionalOnOffMenu;
private CheckboxMenuItem orangePointOnOffMenu;
//
// Initialize the GUI (application and applet)
//
public void initialize(String[] args) {
// Initialize the window, menubar, etc.
super.initialize(args);
exampleFrame.setTitle("Java 3D ExHenge Example");
//
// Add a menubar menu to change parameters
// Dim ambient light
// Bright ambient light
// Red directional light
// Yellow directional light
// Orange point light
//
Menu m = new Menu("Lights");
ambientOnOffMenu = new CheckboxMenuItem("Dim ambient light",
ambientOnOff);
ambientOnOffMenu.addItemListener(this);
m.add(ambientOnOffMenu);
brightAmbientOnOffMenu = new CheckboxMenuItem("Bright ambient light",
brightAmbientOnOff);
brightAmbientOnOffMenu.addItemListener(this);
m.add(brightAmbientOnOffMenu);
redDirectionalOnOffMenu = new CheckboxMenuItem("Red directional light",
redDirectionalOnOff);
redDirectionalOnOffMenu.addItemListener(this);
m.add(redDirectionalOnOffMenu);
yellowDirectionalOnOffMenu = new CheckboxMenuItem(
"Yellow directional light", yellowDirectionalOnOff);
yellowDirectionalOnOffMenu.addItemListener(this);
m.add(yellowDirectionalOnOffMenu);
orangePointOnOffMenu = new CheckboxMenuItem("Orange point light",
orangePointOnOff);
orangePointOnOffMenu.addItemListener(this);
m.add(orangePointOnOffMenu);
exampleMenuBar.add(m);
}
//
// Handle checkboxes
//
public void itemStateChanged(ItemEvent event) {
Object src = event.getSource();
if (src == ambientOnOffMenu) {
ambientOnOff = ambientOnOffMenu.getState();
ambient.setEnable(ambientOnOff);
return;
}
if (src == brightAmbientOnOffMenu) {
brightAmbientOnOff = brightAmbientOnOffMenu.getState();
brightAmbient.setEnable(brightAmbientOnOff);
return;
}
if (src == redDirectionalOnOffMenu) {
redDirectionalOnOff = redDirectionalOnOffMenu.getState();
redDirectional.setEnable(redDirectionalOnOff);
return;
}
if (src == yellowDirectionalOnOffMenu) {
yellowDirectionalOnOff = yellowDirectionalOnOffMenu.getState();
yellowDirectional.setEnable(yellowDirectionalOnOff);
return;
}
if (src == orangePointOnOffMenu) {
orangePointOnOff = orangePointOnOffMenu.getState();
orangePoint.setEnable(orangePointOnOff);
return;
}
// Handle all other checkboxes
super.itemStateChanged(event);
}
}
//
//CLASS
//ElevationGrid - a 3D terrain grid built from a list of heights
//
//DESCRIPTION
//This class creates a 3D terrain on a grid whose X and Z dimensions,
//and row/column spacing are parameters, along with a list of heights
//(elevations), one per grid row/column pair.
//
class ElevationGrid extends Primitive {
// Parameters
protected int xDimension = 0, zDimension = 0;
protected double xSpacing = 0.0, zSpacing = 0.0;
protected double[] heights = null;
// 3D nodes
private Appearance mainAppearance = null;
private Shape3D shape = null;
private IndexedTriangleStripArray tristrip = null;
//
// Construct an elevation grid
//
public ElevationGrid() {
xDimension = 2;
zDimension = 2;
xSpacing = 1.0;
zSpacing = 1.0;
mainAppearance = null;
zeroHeights();
rebuild();
}
public ElevationGrid(int xDim, int
|