Attached below are a utility class and a test program which show how to
transform between local coordinates for a Node and window coordinates for a
window associated with a Canvas3D. Use it like this:
// after the canvas and node are created
LocalToWindow locToWindow = LocalToWindow(node, canvas);
...
// when you need to transform (canvas location and node transforms may have
// changed)
locToWindow.update(); // make sure transforms are up to date
Point3d[] localPts = <some local coords to transform >
Point[] windowPts = <the area to put the tranformed pts >
for (int i = 0; i < localPts.length; i++) {
locToWindow.transformPt(localPts[i], windowPts[i]);
}
Hope this helps,
Doug Gehringer
Sun Microsystems
/**
* Utility class for doing local->window transformations for case where
* Canvas3D is a simple display such as a monitor. This won't work for the
* more complex cases (i.e. a multiple canvases, head tracking, etc).
*
* Usage:
* // after the canvas and node are created
* LocalToWindow locToWindow = LocalToWindow(node, canvas);
* ...
* // when we need to transform (canvas location and node transforms may have
* // changed)
* locToWindow.update(); // make sure transforms are up to date
*
* Point3d[] localPts = <some local coords to transform >
* Point[] windowPts = <the area to put the tranformed pts >
* for (int i = 0; i < localPts.length; i++) {
* locToWindow.transformPt(localPts[i], windowPts[i]);
* }
*/
// standard j3d packages
import javax.media.j3d.*;
import javax.vecmath.*;
import java.awt.Point;
import java.awt.Dimension;
public class LocalToWindow {
Canvas3D canvas = null;
Node node = null;
// inquired/derived data
Transform3D localToVworld = new Transform3D();
Transform3D vworldToImagePlate = new Transform3D();
Transform3D localToImagePlate = new Transform3D();
Point3d eyePos = new Point3d();
int projType;
Point canvasScr;
Dimension screenSize;
double metersPerPixelX;
double metersPerPixelY;
// Temporaries
Point3d imagePlatePt = new Point3d();
Vector3d projVec = new Vector3d();
Point2d screenPt = new Point2d();
/**
* Called with the Node which specifies the local coordinates for the
* points to be transformed and the Canvas3D where the points are
* displayed
*/
public LocalToWindow(Node node, Canvas3D canvas) {
this.canvas = canvas;
this.node = node;
update();
}
/**
* Either create LocalToWindow() just before transforming points or call
* this method to ensure that the transforms are up to date. Note: if
* you are transforming several points, you only need to call this method
* once.
*/
public void update() {
node.getLocalToVworld(localToVworld);
canvas.getVworldToImagePlate(vworldToImagePlate);
// Make a composite transform:
// vWorldPt = LocalToVworld * localPt;
// imagePlatePt = VworldToImagePlate * vWorldPt;
// imagePlatePt = VworldToImagePlate * LocalToVworld * localPt;
localToImagePlate.mul(vworldToImagePlate, localToVworld);
// we need these to project the point from Image Plate coords to
// the actual image plate (i.e. perpsective)
canvas.getCenterEyeInImagePlate(eyePos);
//System.out.println("eyePos = " + eyePos);
projType = canvas.getView().getProjectionPolicy();
// this stuff is to go from image plate coords to window coords
canvasScr = canvas.getLocationOnScreen();
//System.out.println("canvasScr = " + canvasScr);
screenSize = canvas.getScreen3D().getSize();
double physicalScreenWidth =
canvas.getScreen3D().getPhysicalScreenWidth();
double physicalScreenHeight =
canvas.getScreen3D().getPhysicalScreenHeight();
metersPerPixelX = physicalScreenWidth / (double) screenSize.width;
metersPerPixelY = physicalScreenHeight / (double) screenSize.height;
}
/**
* Transform the point from local coords to window coords
*/
public void transformPt(Point3d localPt, Point windowPt) {
// System.out.println("vWorld Pt = " + local);
localToImagePlate.transform(localPt, imagePlatePt);
//System.out.println("imagePlatePt = " + imagePlatePt);
double zScale = 1.0; // default, used for PARALELL_PROJECTION
if (projType == View.PERSPECTIVE_PROJECTION) {
// get the vector from eyePos to imagePlatePt
projVec.sub(imagePlatePt, eyePos);
// Scale this vector to make it end at the projection plane.
// Scale is ratio :
// eye->imagePlate Plane dist / eye->imagePlatePt dist
// eye dist to plane is eyePos.z (eye is in +z space)
// image->eye dist is -projVec.z (image->eye is in -z dir)
//System.out.println("eye dist = " + (eyePos.z));
//System.out.println("image dist = " + (-projVec.z));
zScale = eyePos.z / (-projVec.z);
screenPt.x = eyePos.x + projVec.x * zScale;
screenPt.y = eyePos.y + projVec.y * zScale;
}
//System.out.println("screenPt = " + screenPt);
// Note: screenPt is in image plate coords, at z=0
// Transform from image plate coords to screen coords
windowPt.x = (int)Math.round(screenPt.x / metersPerPixelX) -
canvasScr.x;
windowPt.y = screenSize.height - 1 -
(int)Math.round(screenPt.y / metersPerPixelY) - canvasScr.y;
//System.out.println("windowPt = " + windowPt);
}
}
/*
* Testing LocalToWindow utility. Rotate using left (or main) button,
* select line points using right (or meta + main) button. Compare coords
* of selected point with mouse coords.
*/
// standard j3d packages
import javax.media.j3d.*;
import javax.vecmath.*;
// sun utility classes
import com.sun.j3d.utils.universe.*;
import com.sun.j3d.utils.geometry.ColorCube;
import com.sun.j3d.utils.behaviors.mouse.MouseRotate;
import com.sun.j3d.utils.applet.MainFrame;
import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
import java.util.Enumeration;
public class LocalToWindowTest extends Applet {
Canvas3D canvas;
Shape3D shape;
Point3d[] coords;
public BranchGroup createSceneGraph() {
// create root of the branch graph
BranchGroup objRoot = new BranchGroup();
// create the TransformGroup
TransformGroup objTrans = new TransformGroup();
objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
objRoot.addChild(objTrans);
// create box so that we can compare it's reported window coords
// with what we can "measure" by picking the corners
coords = new Point3d[5];
coords[0] = new Point3d( 0.0, 0.0, 0.0);
coords[1] = new Point3d( 0.5,-0.5, 0.0);
coords[2] = new Point3d( 0.5, 0.5, 0.0);
coords[3] = new Point3d(-0.5, 0.5, 0.0);
coords[4] = new Point3d(-0.5,-0.5, 0.0);
int [] lineLength = new int[1];
lineLength[0] = 5;
LineStripArray lineArray = new LineStripArray(5, LineArray.COORDINATES,
lineLength);
lineArray.setCoordinates(0, coords);
Appearance a = new Appearance();
a.setColoringAttributes(new ColoringAttributes());
shape = new Shape3D(lineArray, a);
shape.setCapability(Shape3D.ALLOW_LOCAL_TO_VWORLD_READ);
objTrans.addChild(shape);
objRoot.addChild(new Callback());
MouseRotate mr = new MouseRotate();
mr.setTransformGroup(objTrans);
mr.setSchedulingBounds(new BoundingSphere(new Point3d(),1000));
objRoot.addChild(mr);
return objRoot;
}
public LocalToWindowTest(){
setLayout(new BorderLayout());
canvas = new Canvas3D(null);
add("Center", canvas);
// create a simple scene and attach it to virtual universe
Transform3D t = new Transform3D();
canvas.getImagePlateToVworld(t);
//System.out.println("imagePlateToVworld = ");
//System.out.println(t);
BranchGroup scene = createSceneGraph();
SimpleUniverse u = new SimpleUniverse(canvas);
// This will move the ViewPlatform back a bit so the
// objects in the scene can be viewed.
u.getViewingPlatform().setNominalViewingTransform();
u.addBranchGraph(scene);
//canvas.getView().setProjectionPolicy(View.PARALLEL_PROJECTION);
}
void printXformedCoords() {
LocalToWindow locToWindow = new LocalToWindow(shape, canvas);
Point windowPt = new Point();
for (int i = 0; i < coords.length; i++) {
locToWindow.transformPt(coords[i], windowPt);
System.out.println("windowPt[" + i + "] = (" + windowPt.x + ", " +
windowPt.y + ")");
}
}
// allows this to be run as application as well as applet
public static void main(String[] argv){
new MainFrame(new LocalToWindowTest(),256,256);
}
public class Callback extends Behavior {
public void initialize(){
setSchedulingBounds(new BoundingSphere(new Point3d(),1000));
wakeupOn(new WakeupOnAWTEvent(MouseEvent.MOUSE_PRESSED));
}
public void processStimulus(Enumeration criteria){
WakeupOnAWTEvent awt=(WakeupOnAWTEvent) criteria.nextElement();
MouseEvent e=(MouseEvent) awt.getAWTEvent()[0];
if (e.isMetaDown()) {
int x=e.getX();
int y=e.getY();
System.out.println("mouse ev=("+x+","+y+")");
printXformedCoords();
}
wakeupOn(new WakeupOnAWTEvent(MouseEvent.MOUSE_PRESSED));
}
}
}