I have been experiencing some interesting behavior. I have
several BranchGroups representing individual levels in a
"give 2D objects 3D properties" style application. I use
LineArrays to represent the 2D objects.
The behavior arises when I zoom in close to one of the objects,
and don't touch it for any extended period of time (around
10 seconds). When I zoom back out, several of the LineArrays
are non longer being drawn. I've tried setting
setVisibilityPolicy(View.VISIBILITY_DRAW_ALL);
on the View, but that didn't help.
Any ideas regarding this matter would be greatly appreciated.
I have attached a small program that demonstrates this behavior.
Michael Rutter
import java.awt.*;
import java.awt.event.*;
import javax.swing.JFrame;
import java.util.Vector;
import javax.vecmath.*;
import javax.media.j3d.*;
import com.sun.j3d.utils.geometry.Text2D;
import com.sun.j3d.utils.picking.*;
import com.sun.j3d.utils.universe.SimpleUniverse;
/**
* This is a simple class ment to demonstrate an undesired behavior of Java3D.
* There are four ways to move the scene graph around by draggging the mouse:
* 1) Tilt: The first mouse button will cause each individual "Level" to
* tilt about the x-axis, along the middle of the level.
* 2) Pan: CRTL+First mouse button will cause the levels to pan about.
* 3) Zoom: The second (middle) mouse button will cause the scene to zoom
* smaller when dragged up, and zoom larger when dragged down.
* 4) Spin: The third mouse button will cause each level will spin about the y-axis.
*
* Using these tools, tilt and spin the drawing just for fun. Then zoom
* in upon one of the individual levels (scale should equal 45 or higher),
* and let it sit for about ten seconds (moving it about a little is fine).
* Finally zoom back out to where it is possible to see all of the levels
* (scale ~= 1). At this point, there will most likely be only one or two
* of the seven levels still visible (but all of the text). This disappearance
* of shapes is the undesired behavior. An interesting bit to note is that
* while the LineArrays disappear, the Text objects remain. The LineArrays
* are still there, in the application it is still possible to pick them,
* they just aren't being drawn.
*
* The organazation of the scene graph comes decently close to mimicking that
* of the project where the problem originally popped up. What the project does
* is complex, hence the complexity of the scene graph.
*/
public class TestBlank extends JFrame implements MouseMotionListener{
protected static final int NUM_LEVELS = 7;
// Point I keep around to get good delta values when dragging the mouse.
protected Point lastMousePos = new Point(0, 0);
// Transform group used for panning, and for holding the levels.
protected TransformGroup objScale = null;
// Outer group used to zoom
protected TransformGroup scaleGroup = null;
// Transform3D used for panning
protected Transform3D t3d;
// Transform3D used for zooming
protected Transform3D scaleT3D;
// The seven individual levels
protected Block[] levels;
// Overall scale of the drawing
protected double scale;
// Initial scale value based upon the size of the elements
// put into the scene graph.
protected double initialScale;
// Vector3d used to keep track of the drawing's position
protected Vector3d position;
// Temp variables I keep around to make panning/scaling more efficient.
protected Vector3d tempVector3d;
protected Matrix3d tempMatrix3d;
public TestBlank() {
super("Show that the evil shapes disappear");
Canvas3D canvas = new
Canvas3D(SimpleUniverse.getPreferredConfiguration());
// Create a scene and attach it to the virtual universe
BranchGroup scene = createSceneGraph(canvas);
SimpleUniverse u = new SimpleUniverse(canvas);
Background background = new Background(scene);
background.setColor(1.0f, 1.0f, 1.0f);
// This will move the ViewPlatform back a bit so the
// objects in the scene can be viewed.
u.getViewingPlatform().setNominalViewingTransform();
u.addBranchGraph(scene);
PickCanvas pickCanvas = new PickCanvas(canvas, scene);
pickCanvas.setMode(PickTool.BOUNDS);
pickCanvas.setTolerance(1.0f);
// We are looking at ship drawings usually, so we don't like the
// perspective projection.
canvas.getView().setProjectionPolicy(View.PARALLEL_PROJECTION);
// This is an attemp to get Java3D to quit stopping drawing objects
// that aren't currently displayed. When a user zooms in close on
// a drawing, it's possible that Java3D will drop a lot of the objects
// that are beyond the visual scope. This wouldn't be a bad thing if
// Java3D picked them back up once they became visible again, but it
// doesn't. This doesn't seem to solve the problem
canvas.getView().setVisibilityPolicy(View.VISIBILITY_DRAW_ALL);
this.getContentPane().setLayout(new BorderLayout());
this.getContentPane().add(canvas, BorderLayout.CENTER);
this.addWindowListener(new WindowAdapter() {
public void windowClosing() {
System.exit(0);
}
});
}
protected BranchGroup createSceneGraph(Canvas3D canvas) {
BranchGroup objRoot = new BranchGroup();
// Set up the group that we're going to be using for zooming.
scaleT3D = new Transform3D();
scaleGroup = new TransformGroup();
Matrix3f tempRot = new Matrix3f();
tempRot.rotX(0.0f);
scaleT3D.set(tempRot, position = new Vector3d(0.0, 0.0, 0.0), scale =
1.0);
scaleGroup.setTransform(scaleT3D);
scaleGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
// Set up the initial scale so that we can see everything.
initialScale = Math.min(1.0 / Block.X_DEVIATION, 1.0 /
(Block.Y_DEVIATION * NUM_LEVELS));
position.set(0.0, 0.0, 0.0);
t3d = new Transform3D();
t3d.set(tempRot, position, initialScale);
objScale = new TransformGroup();
objScale.setTransform(t3d);
objScale.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
// Create some BS levels and plop them in.
levels = new Block[NUM_LEVELS];
for (int index = 0; index < levels.length; index++) {
levels[index] = new Block(new Point3d(0.0, 100.0*index, 0.0));
objScale.addChild(levels[index]);
}
scaleGroup.addChild(objScale);
objRoot.addChild(scaleGroup);
// Grabbed from some example code
BoundingSphere bounds =
new BoundingSphere(new Point3d(0.0,0.0,0.0), 10.0);
// Add a light.
Color3f lColor = new Color3f(1.0f, 1.0f, 1.0f) ;
Vector3f lDir = new Vector3f(0.0f, 0.0f, -1.0f) ;
DirectionalLight lgt = new DirectionalLight(lColor, lDir) ;
lgt.setInfluencingBounds(bounds) ;
objRoot.addChild(lgt);
// For happy happy mouseDragged events
canvas.addMouseMotionListener(this);
// Let's make some optimizations, yea.
objRoot.compile();
return objRoot;
}
/**
* This method is used to PAN, TILT, ZOOM, and SPIN the scene.
*/
public void mouseDragged(MouseEvent evt) {
int deltaX = evt.getX() - lastMousePos.x;
int deltaY = evt.getY() - lastMousePos.y;
// The delta values are just too big to work with, so knock them
// down a bit to something more usable.
double dX = (double)deltaX / 200.0;
double dY = (double)deltaY / 200.0;
if ((evt.getModifiers() & InputEvent.BUTTON1_MASK) != 0) {
if ((evt.getModifiers() & InputEvent.CTRL_MASK) != 0) {
pan(dX, dY);
}
else {
for (int cnt = 0; cnt < levels.length; cnt++) {
levels[cnt].adjustTilt(dY);
}
}
}
else if ((evt.getModifiers() & InputEvent.BUTTON2_MASK) != 0) {
zoom(dY);
}
else if ((evt.getModifiers() & InputEvent.BUTTON3_MASK) != 0) {
for (int cnt = 0; cnt < levels.length; cnt++) {
levels[cnt].adjustSpin(dX);
}
}
lastMousePos.x = evt.getX();
lastMousePos.y = evt.getY();
}
/**
* Stay updated on the last position that the mouse was in so that we
* can have good delta position values on mouseDragged events.
*/
public void mouseMoved(MouseEvent evt) {
lastMousePos.x = evt.getX();
lastMousePos.y = evt.getY();
}
/**
* Make the scene bigger or smaller. Honey, I Shrunk the Drawing!
*/
protected void zoom(double delta) {
// This is an arbitrary equation that I came up with. It
// seems to work pretty darn good, though. Nice and smooth.
scale += delta * ((scale / 2.0) + 1.0);
scaleT3D.setScale(Math.max(scale, 0.1));
scaleGroup.setTransform(scaleT3D);
System.out.println("scale=" + scale);
} // End of zoom
/**
* Move the scene around. A little to the left, yeah, that's greeeat.
*/
protected void pan(double dx, double dy) {
if (tempVector3d == null) tempVector3d = new Vector3d();
if (tempMatrix3d == null) tempMatrix3d = new Matrix3d();
// Adjust the deltas according to the current scale so that we can have
// similar results for different zoom factors
dx /= scale;
dy /= -scale;
tempVector3d.set(dx, dy, 0.0);
position.add(tempVector3d);
tempMatrix3d.setZero();
tempMatrix3d.rotX(0.0);
t3d.set(tempMatrix3d, position, initialScale);
objScale.setTransform(t3d);
} // End of pan
/**
* A class to simulate an individual level. In the actual application,
* the equivalent class represents a deck on a ship. Lots of objects.
*/
public class Block extends BranchGroup {
// Number of points used for the LineArray
protected static final int NUM_POINTS = 50;
// Basically the max X-Value
protected static final double X_DEVIATION = 1000.0;
// Basically the max Y-Value
protected static final double Y_DEVIATION = 100.0;
// The root TransformGroup
protected TransformGroup objRoot = null;
protected Transform3D t3d = null;
// The inner transform group used so that we can do the nifty tilting.
protected TransformGroup innerGroup = null;
// And the corresponding Transform3D
protected Transform3D innerTrans = null;
// Again with the persistant temporary variables for efficiency
protected Matrix3d tempMatrix = null;
protected Matrix3d rotMatrix = null;
public Block(Point3d base) {
// The points used for the LineArray
Point3f points[] = new Point3f[NUM_POINTS];
// The colors used for the LineArray
Color3b clrs[] = new Color3b[NUM_POINTS];
// The color used to fill the clrs array
Color3b color = new Color3b((byte)150, (byte)150, (byte)150);
// Create a bunch of random points so we can have a nice
mish-mash of lines
for (int cnt = 0; cnt < NUM_POINTS; cnt++) {
clrs[cnt] = color;
points[cnt] = new Point3f((float)((Math.random()-0.5)
* X_DEVIATION + base.x),
(float)((Math.random()-0.5)
* Y_DEVIATION + base.y), 0.0f);
}
objRoot = new TransformGroup();
t3d = new Transform3D();
tempMatrix = new Matrix3d();
rotMatrix = new Matrix3d();
rotMatrix.rotX(0.0);
// Create the nifty LineArray
LineArray elementArray = new LineArray(NUM_POINTS ,
GeometryArray.COORDINATES | GeometryArray.COLOR_3);
elementArray.setCoordinates(0, points);
elementArray.setColors(0, clrs);
elementArray.setCapability(GeometryArray.ALLOW_COLOR_WRITE);
Appearance appearance = new Appearance();
Material m = new Material();
m.setLightingEnable(false);
appearance.setMaterial(m);
// Make it pickable because, well, that's what is needed in the
// application.
Geometry geom = elementArray;
Shape3D level = new Shape3D(geom, appearance);
level.setPickable(true);
PickTool.setCapabilities(level, PickTool.INTERSECT_FULL);
// Shift everything so that the center is at (0, 0), and then
add
// the objects for this level.
innerGroup = new TransformGroup();
innerTrans = new Transform3D();
innerTrans.set(rotMatrix, new Vector3d(-base.x -
X_DEVIATION/2.0, -base.y - Y_DEVIATION/2.0, 0.0), 1.0);
innerGroup.setTransform(innerTrans);
try {
innerGroup.addChild(level);
innerGroup.addChild(new Text("Bork", Y_DEVIATION/5.0,
new Point3d(0.0, 0.0, 0.0)));
}
catch (javax.media.j3d.MultipleParentException exp) {}
// Now move stuff back to the original position.
t3d.set(rotMatrix, new Vector3d(base.x + X_DEVIATION/2.0,
base.y + Y_DEVIATION/2.0, 0.0), 1.0);
objRoot.setTransform(t3d);
objRoot.addChild(innerGroup);
objRoot.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
try {
this.addChild(objRoot);
this.compile();
}
catch (Exception exp) {exp.printStackTrace();}
} // End of Block
/**
* Tilt the level about the x-axis
*/
protected void adjustTilt(double delta) {
rotMatrix.setZero();
rotMatrix.rotX(delta);
t3d.get(tempMatrix);
rotMatrix.mul(tempMatrix);
t3d.setRotation(rotMatrix);
objRoot.setTransform(t3d);
} // End of Block.adjustTilt
/**
* Spin the level about the y-axis. Kind of like it's doing a pole
dance!
*/
protected void adjustSpin(double delta) {
rotMatrix.setZero();
rotMatrix.rotY(delta);
t3d.get(tempMatrix);
rotMatrix.mul(tempMatrix);
t3d.setRotation(rotMatrix);
objRoot.setTransform(t3d);
} // End of Block.adjustSpin
} // End of class Block
/**
* Quick class to add Text to the level. The Text is interesting because
* when the LineArray goes away, the text remains. Very interesting, indeed.
*/
public class Text extends TransformGroup {
protected Text2D textObject = null;
protected static final Color3f color = new Color3f(0.2f, 0.4f, 0.2f);
public Text(String textString, double height, Point3d location) {
double scale = (300.0 / 50.0) * height;
textObject = new Text2D(textString, color, "SansSerif", 50,
java.awt.Font.PLAIN);
TransformGroup spinTg = new TransformGroup();
spinTg.setPickable(true);
spinTg.addChild(textObject);
Transform3D t3d = new Transform3D();
Matrix3f tempRot = new Matrix3f();
tempRot.rotX(0.0f);
t3d.set(tempRot, new Vector3d(location.x, location.y, 0.0),
scale);
this.setTransform(t3d);
this.addChild(spinTg);
}// End of Text
} // End of class Text
/**
* Create the test, make it big enough to see, and plop it on the screen.
*/
public static void main(String[] args) {
TestBlank youCantSeeMe = new TestBlank();
youCantSeeMe.setBounds(0, 0, 1024, 780);
youCantSeeMe.setVisible(true);
} // End of main
} // End of class TestBlank