|
This Java tip illustrates the use of colored backgrounds in Java 3D scenes.
import java.applet.Applet;
import java.awt.AWTEvent;
import java.awt.BorderLayout;
import java.awt.CheckboxMenuItem;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Frame;
import java.awt.Menu;
import java.awt.MenuBar;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseEvent;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.io.File;
import java.util.Enumeration;
import java.util.EventListener;
import javax.media.j3d.AmbientLight;
import javax.media.j3d.Appearance;
import javax.media.j3d.Background;
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.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.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.Matrix3f;
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 ExBackgroundColor extends Java3DFrame {
//--------------------------------------------------------------
// SCENE CONTENT
//--------------------------------------------------------------
//
// Nodes (updated via menu)
//
private Background background = null;
//
// Build scene
//
public Group buildScene() {
// Get the current color
Color3f color = (Color3f) colors[currentColor].value;
// Turn off the example headlight
setHeadlightEnable(false);
// Default to walk navigation
setNavigationType(Walk);
// Create the scene group
Group scene = new Group();
// BEGIN EXAMPLE TOPIC
// Create application bounds
BoundingSphere worldBounds = new BoundingSphere(new Point3d(0.0, 0.0,
0.0), // Center
1000.0); // Extent
// Set the background color and its application bounds
background = new Background();
background.setColor(color);
background.setCapability(Background.ALLOW_COLOR_WRITE);
background.setApplicationBounds(worldBounds);
scene.addChild(background);
// END EXAMPLE TOPIC
// Build foreground geometry
scene.addChild(new TowerScene(this));
return scene;
}
//--------------------------------------------------------------
// USER INTERFACE
//--------------------------------------------------------------
//
// Main
//
public static void main(String[] args) {
ExBackgroundColor ex = new ExBackgroundColor();
ex.initialize(args);
ex.buildUniverse();
ex.showFrame();
}
// Color menu choices
private NameValue[] colors = { new NameValue("White", White),
new NameValue("Dark Gray", DarkGray),
new NameValue("Black", Black), new NameValue("Dark Red", DarkRed),
new NameValue("Dark Green", DarkGreen),
new NameValue("Dark Blue", DarkBlue), };
private int currentColor = 2;
private CheckboxMenu colorMenu = null;
//
// Initialize the GUI (application and applet)
//
public void initialize(String[] args) {
// Initialize the window, menubar, etc.
super.initialize(args);
exampleFrame.setTitle("Java 3D Background Color Example");
//
// Add a menubar menu to change node parameters
// Color -->
//
Menu m = new Menu("Background");
colorMenu = new CheckboxMenu("Color", colors, currentColor, this);
m.add(colorMenu);
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;
background.setColor(color);
return;
}
// Handle all other checkboxes
super.checkboxChanged(menu, check);
}
}
//
//CLASS
//TowerScene - shapes and lights for a scene with towers
//
//DESCRIPTION
//This class builds a scene containing a cratered surface, a set of
//stone towers, plus appropriate lighting. The scene is used in several
//of the examples to provide content to affect with lights, background
//colors and images, and so forth.
//
//SEE ALSO
//ExBackgroundColor
//ExBackgroundImage
//ExBackgroundGeometry
//
//AUTHOR
//David R. Nadeau / San Diego Supercomputer Center
//
class TowerScene extends Group {
private static final double[][] craters = {
// x,z,radius are in a normalized -1.0 to 1.0 space
// x z radius depth
{ 0.0, 0.0, 0.7, 0.20 }, { 0.3, 0.3, 0.5, 0.20 },
{ -0.3, 0.1, 0.6, 0.20 }, { -0.2, 0.4, 0.4, 0.20 },
{ -0.9, -0.9, 0.5, 0.20 }, { 0.4, 0.5, 0.3, 0.10 },
{ 0.9, -0.2, 0.4, 0.10 }, { -0.8, 0.1, 0.2, 0.10 },
{ 0.2, 0.7, 0.3, 0.20 }, { 0.5, -0.5, 0.21, 0.20 },
{ 0.8, -0.8, 0.16, 0.10 }, { -0.3, 0.7, 0.23, 0.20 },
{ 0.5, 0.5, 0.22, 0.10 }, { -0.7, 0.8, 0.15, 0.10 },
{ -0.5, -0.3, 0.22, 0.10 }, { 0.2, 0.2, 0.15, 0.10 },
{ 0.1, 0.8, 0.25, 0.20 }, { 0.4, 0.9, 0.28, 0.09 },
{ 0.9, -0.1, 0.23, 0.10 }, { 0.1, -0.0, 0.33, 0.08 },
{ 0.1, -0.9, 0.23, 0.20 }, { -1.0, 0.8, 0.13, 0.15 },
{ -0.9, 0.7, 0.10, 0.15 }, { -0.2, 0.1, 0.10, 0.16 },
{ 1.1, 1.0, 0.12, 0.15 }, { 0.9, 0.5, 0.13, 0.14 },
{ -0.1, -0.1, 0.14, 0.15 }, { -0.5, -0.5, 0.10, 0.13 },
{ 0.1, -0.4, 0.10, 0.15 }, { -0.4, -1.0, 0.25, 0.15 },
{ 0.4, 1.0, 0.25, 0.15 }, };
public TowerScene(Component observer) {
BoundingSphere worldBounds = new BoundingSphere(new Point3d(0.0, 0.0,
0.0), // Center
1000.0); // Extent
// Add a few lights
AmbientLight ambient = new AmbientLight();
ambient.setEnable(true);
ambient.setColor(new Color3f(0.2f, 0.2f, 0.2f));
ambient.setInfluencingBounds(worldBounds);
addChild(ambient);
DirectionalLight dir1 = new DirectionalLight();
dir1.setEnable(true);
dir1.setColor(new Color3f(1.0f, 0.15f, 0.15f));
dir1.setDirection(new Vector3f(0.8f, -0.35f, -0.5f));
dir1.setInfluencingBounds(worldBounds);
addChild(dir1);
DirectionalLight dir2 = new DirectionalLight();
dir2.setEnable(true);
dir2.setColor(new Color3f(0.15f, 0.15f, 1.0f));
dir2.setDirection(new Vector3f(-0.7f, -0.35f, 0.5f));
dir2.setInfluencingBounds(worldBounds);
addChild(dir2);
// Load textures
TextureLoader texLoader = new TextureLoader("moon5.jpg", observer);
Texture moon = texLoader.getTexture();
if (moon == null)
System.err.println("Cannot load moon5.jpg texture");
else {
moon.setBoundaryModeS(Texture.WRAP);
moon.setBoundaryModeT(Texture.WRAP);
moon.setMinFilter(Texture.NICEST);
moon.setMagFilter(Texture.NICEST);
moon.setMipMapMode(Texture.BASE_LEVEL);
moon.setEnable(true);
}
texLoader = new TextureLoader("stonebrk2.jpg", observer);
Texture stone = texLoader.getTexture();
if (stone == null)
System.err.println("Cannot load stonebrk2.jpg texture");
else {
stone.setBoundaryModeS(Texture.WRAP);
stone.setBoundaryModeT(Texture.WRAP);
stone.setMinFilter(Texture.NICEST);
stone.setMagFilter(Texture.NICEST);
stone.setMipMapMode(Texture.BASE_LEVEL);
stone.setEnable(true);
}
//
// Build a rough terrain
//
Appearance moonApp = new Appearance();
Material moonMat = new Material();
moonMat.setAmbientColor(0.5f, 0.5f, 0.5f);
moonMat.setDiffuseColor(1.0f, 1.0f, 1.0f);
moonMat.setSpecularColor(0.0f, 0.0f, 0.0f);
moonApp.setMaterial(moonMat);
TextureAttributes moonTexAtt = new TextureAttributes();
moonTexAtt.setTextureMode(TextureAttributes.MODULATE);
moonTexAtt.setPerspectiveCorrectionMode(TextureAttributes.NICEST);
moonApp.setTextureAttributes(moonTexAtt);
if (moon != null)
moonApp.setTexture(moon);
CraterGrid grid = new CraterGrid(50, 50, // grid dimensions
1.0, 1.0, // grid spacing
4.0, // height exageration factor
craters, // grid elevations
moonApp); // grid appearance
addChild(grid);
//
// Build several towers on the terrain
//
SharedGroup tower = new SharedGroup();
Appearance towerApp = new Appearance();
Material towerMat = new Material();
towerMat.setAmbientColor(0.6f, 0.6f, 0.6f);
towerMat.setDiffuseColor(1.0f, 1.0f, 1.0f);
towerMat.setSpecularColor(0.0f, 0.0f, 0.0f);
towerApp.setMaterial(towerMat);
Transform3D tr = new Transform3D();
tr.setScale(new Vector3d(4.0, 4.0, 1.0));
TextureAttributes towerTexAtt = new TextureAttributes();
towerTexAtt.setTextureMode(TextureAttributes.MODULATE);
towerTexAtt.setPerspectiveCorrectionMode(TextureAttributes.NICEST);
towerTexAtt.setTextureTransform(tr);
towerApp.setTextureAttributes(towerTexAtt);
if (stone != null)
towerApp.setTexture(stone);
Arch towerShape = new Arch(0.0, // start Phi
1.571, // end Phi
2, // nPhi
0.0, // start Theta
Math.PI * 2.0, // end Theta
5, // nTheta
3.0, // start radius
8.0, // end radius
0.0, // start phi thickness
0.0, // end phi thickness
towerApp); // appearance
tower.addChild(towerShape);
// Place towers
Matrix3f rot = new Matrix3f();
rot.setIdentity();
TransformGroup tg = new TransformGroup(new Transform3D(rot,
new Vector3d(2.0, -3.0, -8.0), 1.0));
tg.addChild(new Link(tower));
addChild(tg);
tg = new TransformGroup(new Transform3D(rot, new Vector3d(-1.0, -3.0,
-6.0), 0.5));
tg.addChild(new Link(tower));
addChild(tg);tg = new TransformGroup(new Transform3D(rot, new Vector3d(5.0, -3.0,
-6.0), 0.75));
tg.addChild(new Link(tower));
addChild(tg);
tg = new TransformGroup(new Transform3D(rot, new Vector3d(1.0, -3.0,
-3.0), 0.35));
tg.addChild(new Link(tower));
addChild(tg);
}
}
//
//CLASS
//Arch - generalized arch
//
//DESCRIPTION
//This class builds a generalized arch where incoming parameters
//specify the angle range in theta (around the equator of a sphere),
//the angle range in phi (north-south), the number of subdivisions
//in theta and phi, and optionally radii and outer-to-inner wall
//thickness variations as phi varies from its starting value to
//its ending value. If the thicknesses are 0.0, then only an outer
//surface is created.
//
//Using this class, you can create spheres with or without inner
//surfaces, hemisphers, quarter spheres, and arches stretched or
//compressed vertically.
//
//This is probably not as general as it could be, but it was enough
//for the purposes at hand.
//
//SEE ALSO
//ModernFire
//
//AUTHOR
//David R. Nadeau / San Diego Supercomputer Center
//
//
class Arch extends Group {
// The shape
private Shape3D arch = null;
// Construct an arch
public Arch() {
// Default to a sphere
this(0.0, Math.PI / 2.0, 9, 0.0, Math.PI, 17, 1.0, 1.0, 0.0, 0.0,
new Appearance());
}
public Arch(Appearance app) {
// Default to a sphere
this(0.0, Math.PI / 2.0, 9, 0.0, Math.PI, 17, 1.0, 1.0, 0.0, 0.0, app);
}
public Arch(double startPhi, double endPhi, int nPhi, double startTheta,
double endTheta, int nTheta, Appearance app) {
// Default to constant radius, no thickness
this(startPhi, endPhi, nPhi, startTheta, endTheta, nTheta, 1.0, 1.0,
0.0, 0.0, app);
}
public Arch(double startPhi, double endPhi, int nPhi, double startTheta,
double endTheta, int nTheta, double startPhiRadius,
double endPhiRadius, double startPhiThickness,
double endPhiThickness, Appearance app) {
double theta, phi, radius, radius2, thickness;
double x, y, z;
double[] xyz = new double[3];
float[] norm = new float[3];
float[] tex = new float[3];
// Compute some values for our looping
double deltaTheta = (endTheta - startTheta) / (double) (nTheta - 1);
double deltaPhi = (endPhi - startPhi) / (double) (nPhi - 1);
double deltaTexX = 1.0 / (double) (nTheta - 1);
double deltaTexY = 1.0 / (double) (nPhi - 1);
double deltaPhiRadius = (endPhiRadius - startPhiRadius)
/ (double) (nPhi - 1);
double deltaPhiThickness = (endPhiThickness - startPhiThickness)
/ (double) (nPhi - 1);
boolean doThickness = true;
if (startPhiThickness == 0.0 && endPhiThickness == 0.0)
doThickness = false;
// Create geometry
int vertexCount = nTheta * nPhi;
if (doThickness)
vertexCount *= 2;
int indexCount = (nTheta - 1) * (nPhi - 1) * 4; // Outer surface
if (doThickness) {
indexCount *= 2; // plus inner surface
indexCount += (nPhi - 1) * 4 * 2; // plus left & right edges
}
IndexedQuadArray polys = new IndexedQuadArray(vertexCount,
GeometryArray.COORDINATES | GeometryArray.NORMALS
| GeometryArray.TEXTURE_COORDINATE_2, indexCount);
//
// Compute coordinates, normals, and texture coordinates
//
theta = startTheta;
tex[0] = 0.0f;
int index = 0;
for (int i = 0; i < nTheta; i++) {
phi = startPhi;
radius = startPhiRadius;
thickness = startPhiThickness;
tex[1] = 0.0f;
for (int j = 0; j < nPhi; j++) {
norm[0] = (float) (Math.cos(phi) * Math.cos(theta));
norm[1] = (float) (Math.sin(phi));
norm[2] = (float) (-Math.cos(phi) * Math.sin(theta));
xyz[0] = radius * norm[0];
xyz[1] = radius * norm[1];
xyz[2] = radius * norm[2];
polys.setCoordinate(index, xyz);
polys.setNormal(index, norm);
polys.setTextureCoordinate(index, tex);
index++;
if (doThickness) {
radius2 = radius - thickness;
xyz[0] = radius2 * norm[0];
xyz[1] = radius2 * norm[1];
xyz[2] = radius2 * norm[2];
norm[0] *= -1.0f;
norm[1] *= -1.0f;
norm[2] *= -1.0f;
polys.setCoordinate(index, xyz);
polys.setNormal(index, norm);
polys.setTextureCoordinate(index, tex);
index++;
}
phi += deltaPhi;
radius += deltaPhiRadius;
thickness += deltaPhiThickness;
tex[1] += deltaTexY;
}
theta += deltaTheta;
tex[0] += deltaTexX;
}
//
// Compute coordinate indexes
// (also used as normal and texture indexes)
//
index = 0;
int phiRow = nPhi;
int phiCol = 1;
if (doThickness) {
phiRow += nPhi;
phiCol += 1;
}
int[] indices = new int[indexCount];
// Outer surface
int n;
for (int i = 0; i < nTheta - 1; i++) {
for (int j = 0; j < nPhi - 1; j++) {
n = i * phiRow + j * phiCol;
indices[index + 0] = n;
indices[index + 1] = n + phiRow;
indices[index + 2] = n + phiRow + phiCol;
indices[index + 3] = n + phiCol;
index += 4;
}
}
// Inner surface
if (doThickness) {
for (int i = 0; i < nTheta - 1; i++) {
for (int j = 0; j < nPhi - 1; j++) {
n = i * phiRow + j * phiCol;
indices[index + 0] = n + 1;
indices[index + 1] = n + phiCol + 1;
indices[index + 2] = n + phiRow + phiCol + 1;
indices[index + 3] = n + phiRow + 1;
index += 4;
}
}
}
// Edges
if (doThickness) {
for (int j = 0; j < nPhi - 1; j++) {
n = j * phiCol;
indices[index + 0] = n;
indices[index + 1] = n + phiCol;
indices[index + 2] = n + phiCol + 1;
indices[index + 3] = n + 1;
index += 4;
}
for (int j = 0; j < nPhi - 1; j++) {
n = (nTheta - 1) * phiRow + j * phiCol;
indices[index + 0 | |