> Date: Tue, 12 Mar 2002 12:51:38 -0800 (PST)
> From: Enrique Dumas <[EMAIL PROTECTED]>
>
> Hello Mark,
>
> Well, in a previous post you wrote that you willl write a little sample to
> help me... Had you find the time to do it ? I tryed lots of things with the
> policies of J3d but I am again a little bit lost ;) Thanks !!!
Hi Enrique,
I did find some time since you posted your question to write an example program
for displaying skewed projection transforms, which is what I think you need.
I'm CC'ing to the interest list since it demonstrates the difference between
moving the view platform vs. manually moving the eyepoint relative to the view
platform.
I've appended the example app to the end of this message. See the comments
after the disclaimer for a description.
-- Mark Hood
> Date: Mon, 28 Jan 2002 15:58:41 -0700
> From: SUBSCRIBE JAVA3D-INTEREST Anonymous <[EMAIL PROTECTED]>
> Subject: [JAVA3D] change the origin of coordinate system on the screen
> To: [EMAIL PROTECTED]
>
> I made an app with a ground.
>
> I'd like that the horizon line of the ground to be on the high of the canvas
> but is near from the center when the camera is near the ground
>
> To change this, I changed the field of view and the rotx angle of the camera
> (as if I look my feet)
>
> my problem is that the rotx "modify" the geometry of objects, as lines which
> are no more vertical drawen (due to the perspective)
>
> So I looked the api and I read that there were some policies to change the
> origin of coordinate system
>
> I use the Nominal screen policy but it places the view platform origin at the
> screen center
>
> My question is : is it possible to set the origin of the coordinate system at
> a different position on the screen
/*
* @(#)ManualEyeDemo.java 1.3 02/03/12 19:54:20
*
* Copyright (c) 1996-2002 Sun Microsystems, Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES
* SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
* DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN
* OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR
* FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR
* PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF
* LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE SOFTWARE,
* EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that Software is not designed,licensed or intended
* for use in the design, construction, operation or maintenance of
* any nuclear facility.
*/
import java.awt.* ;
import java.awt.event.* ;
import javax.media.j3d.* ;
import javax.swing.JApplet ;
import javax.swing.JButton ;
import javax.swing.JLabel ;
import javax.swing.JSlider ;
import javax.swing.event.* ;
import javax.vecmath.* ;
import com.sun.j3d.utils.applet.MainFrame ;
import com.sun.j3d.utils.behaviors.vp.* ;
import com.sun.j3d.utils.geometry.* ;
import com.sun.j3d.utils.universe.* ;
/**
* This program demonstrates the use of the RELATIVE_TO_SCREEN eyepoint
* policy and how to set the eyepoint using the Canvas3D methods
* setLeftManualEyeInImagePlate() and setRightManualEyeInImagePlate(). These
* methods allow you position the eyepoint anywhere relative to the screen or
* window, as opposed to the default RELATIVE_TO_FIELD_OF_VIEW eyepoint
* policy, which always centers the eye in the center of the screen or window
* at the appropriate distance to effect the desired field of view.
* <p>
* Setting the eyepoint at an offset from the center of the window or screen
* produces skewed projection transforms. These have the effect of
* allowing one to see to the sides or the top and bottom of an object
* without altering the orientation of the projection plane relative to the
* object being viewed. Portions of the objects which intersect the
* projection plane will not change shape when moving the eyepoint in this
* manner, and lines which are parallel to the projection plane will not
* converge due to the effect of perspective.
* <p>
* This is useful in architectural renderings where it is often desirable to
* view a building from different eyepoints without having the sides of the
* building converge in the projected image.
* <p>
* Note that it is not possible to achieve the proper effect while using the
* RELATIVE_TO_WINDOW eyepoint policy. With that policy the eyepoint is
* still always centered about the window, and only the Z components of the
* LeftManualEyeInImagePlate and RightManualEyeInImagePlate values are
* used. Instead, we have to use RELATIVE_TO_SCREEN and track the location
* of the window on the screen to keep the eyepoint in the desired location
* relative to the window.
* <p>
* The nested class ManualEyeUpdater encapsulates the code needed to track
* the position of the window and update the manual eye positions based on
* offsets from the center of the window and should be usable by other
* applications. The rest of this example program mainly justs sets up the
* content scenegraph and the GUI.
* <p>
* This program needs Java 3D beta 1 or later to run. A window will come up
* with sliders to allow you to adjust the X, Y, and Z positions of the
* eyepoint relative to the center of the window. The reset button will set
* them back to their nominal values. An OrbitBehavior is also attached to
* the view platform so that you can contrast the effect of moving the
* view platform with the effect of moving the eyepoint relative to the view
* platform. (Note that due to bugs in OrbitBehavior the reset button will
* not correctly reset the view platform transform -- this is fixed in Java
* 3D beta 2 and later).
*/
public class ManualEyeDemo extends JApplet
implements ChangeListener, ActionListener {
float initialEyeOffsetX = 0.00f ;
float initialEyeOffsetY = 0.00f ;
float initialEyeOffsetZ = 0.25f ;
SimpleUniverse universe ;
OrbitBehavior orbit ;
ManualEyeUpdater eyeUpdater ;
JButton resetButton ;
JSlider eyeOffsetXSlider, eyeOffsetYSlider, eyeOffsetZSlider ;
Bounds infiniteBounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0),
Double.POSITIVE_INFINITY) ;
public void init() {
// Create a Canvas3D and add it to the applet.
Container pane = getContentPane() ;
Canvas3D c = new Canvas3D(SimpleUniverse.getPreferredConfiguration()) ;
pane.add(c, BorderLayout.CENTER) ;
// Create a simple universe.
universe = new SimpleUniverse(c) ;
// Set the clip distances appropriate for the view.
View v = universe.getViewer().getView() ;
v.setFrontClipDistance(0.01) ;
v.setBackClipDistance(5.0) ;
// Create a simple scene and attach it to the universe.
universe.addBranchGraph(createSceneGraph()) ;
// Create the ManualEyeUpdater to manage the manual eye position.
eyeUpdater = new ManualEyeUpdater(universe, c,
initialEyeOffsetX,
initialEyeOffsetY,
initialEyeOffsetZ) ;
// Create the GUI for setting the manual eye position.
pane.add(createEyeUpdaterUI(), BorderLayout.SOUTH) ;
// Create an OrbitBehavior to move the view platform.
orbit = new OrbitBehavior(c, OrbitBehavior.REVERSE_ALL) ;
orbit.setSchedulingBounds(infiniteBounds) ;
orbit.setHomeTransform(new Transform3D()) ;
universe.getViewingPlatform().setViewPlatformBehavior(orbit) ;
}
public void start() {
// If the applet is visible, set the initial canvas center. This is
// done here instead of init() to ensure that the Canvas3D has been
// fully sized within the applet.
if (isShowing())
eyeUpdater.updateCanvasCenter() ;
}
public void destroy() {
universe.removeAllLocales() ;
}
BranchGroup createSceneGraph() {
// Create the root of the branch graph
BranchGroup objRoot = new BranchGroup() ;
// Create some Appearances.
Appearance app1 = new Appearance() ;
Material m1 = new Material() ;
m1.setLightingEnable(true) ;
app1.setMaterial(m1) ;
Appearance app2 = new Appearance() ;
Material m2 = new Material() ;
m2.setDiffuseColor(new Color3f(0.6f, 0.7f, 0.8f)) ;
m2.setLightingEnable(true) ;
app2.setMaterial(m2) ;
Appearance app3 = new Appearance() ;
Material m3 = new Material() ;
m3.setDiffuseColor(new Color3f(0.5f, 0.6f, 0.7f)) ;
m3.setLightingEnable(true) ;
app3.setMaterial(m3) ;
// Create some boxes to resemble buildings. Put them on a common
// ground level of -0.60 in Y and put the front faces of two of them
// directly on the projection plane (Z = 0.0) so that remain invariant
// when subjected to manual eye movements.
Box b1 = new Box(0.15f, 0.60f, 0.15f, Box.GENERATE_NORMALS, app1) ;
Transform3D b1Xfm = new Transform3D() ;
b1Xfm.set(new Vector3f(-0.40f, 0.00f, -0.15f)) ;
TransformGroup b1Grp = new TransformGroup(b1Xfm);
b1Grp.addChild(b1) ;
objRoot.addChild(b1Grp) ;
Box b2 = new Box(0.15f, 0.50f, 0.15f, Box.GENERATE_NORMALS, app1) ;
Transform3D b2Xfm = new Transform3D() ;
b2Xfm.set(new Vector3f(0.45f, -0.10f, -0.15f)) ;
TransformGroup b2Grp = new TransformGroup(b2Xfm);
b2Grp.addChild(b2) ;
objRoot.addChild(b2Grp) ;
Box b3 = new Box(0.35f, 0.25f, 0.20f, Box.GENERATE_NORMALS, app2) ;
Transform3D b3Xfm = new Transform3D() ;
b3Xfm.set(new Vector3f(-0.50f, -0.35f, -0.60f)) ;
TransformGroup b3Grp = new TransformGroup(b3Xfm);
b3Grp.addChild(b3) ;
objRoot.addChild(b3Grp) ;
Box b4 = new Box(0.45f, 0.30f, 0.25f, Box.GENERATE_NORMALS, app2) ;
Transform3D b4Xfm = new Transform3D() ;
b4Xfm.set(new Vector3f(0.55f, -0.30f, -0.65f)) ;
TransformGroup b4Grp = new TransformGroup(b4Xfm);
b4Grp.addChild(b4) ;
objRoot.addChild(b4Grp) ;
Box b5 = new Box(0.20f, 0.55f, 0.15f, Box.GENERATE_NORMALS, app3) ;
Transform3D b5Xfm = new Transform3D() ;
b5Xfm.set(new Vector3f(-0.05f, -0.05f, -1.2f)) ;
TransformGroup b5Grp = new TransformGroup(b5Xfm);
b5Grp.addChild(b5) ;
objRoot.addChild(b5Grp) ;
// Set up the ambient light
Color3f ambientColor = new Color3f(0.2f, 0.2f, 0.2f);
AmbientLight ambientLight = new AmbientLight(ambientColor);
ambientLight.setInfluencingBounds(infiniteBounds);
objRoot.addChild(ambientLight);
// Set up the directional lights.
Color3f lColor1 = new Color3f(0.7f, 0.7f, 0.7f) ;
Color3f lColor2 = new Color3f(0.3f, 0.3f, 0.3f) ;
Vector3f lDir1 = new Vector3f(-0.7f, -0.8f, -0.9f) ;
Vector3f lDir2 = new Vector3f(0.1f, 0.1f, -1.0f) ;
DirectionalLight lgt1 = new DirectionalLight(lColor1, lDir1) ;
DirectionalLight lgt2 = new DirectionalLight(lColor2, lDir2) ;
lgt1.setInfluencingBounds(infiniteBounds);
lgt2.setInfluencingBounds(infiniteBounds);
objRoot.addChild(lgt1) ;
objRoot.addChild(lgt2) ;
// Set up the background
Color3f bgColor = new Color3f(0.05f, 0.05f, 0.5f) ;
Background bgNode = new Background(bgColor) ;
bgNode.setApplicationBounds(infiniteBounds) ;
objRoot.addChild(bgNode) ;
return objRoot ;
}
javax.swing.Box createEyeUpdaterUI() {
// Create sliders for setting the eyepoint offsets relative to the
// middle of the Canvas3D.
JLabel xLabel = new JLabel("Eye X Offset") ;
xLabel.setAlignmentX(Component.CENTER_ALIGNMENT) ;
eyeOffsetXSlider = new JSlider(-50, 50, (int)(initialEyeOffsetX*100)) ;
eyeOffsetXSlider.addChangeListener(this) ;
JLabel yLabel = new JLabel("Eye Y Offset") ;
yLabel.setAlignmentX(Component.CENTER_ALIGNMENT) ;
eyeOffsetYSlider = new JSlider(-50, 50, (int)(initialEyeOffsetY*100)) ;
eyeOffsetYSlider.addChangeListener(this) ;
JLabel zLabel = new JLabel("Eye Z Offset") ;
zLabel.setAlignmentX(Component.CENTER_ALIGNMENT) ;
eyeOffsetZSlider = new JSlider(2, 150, (int)(initialEyeOffsetZ*100)) ;
eyeOffsetZSlider.addChangeListener(this) ;
// Create a button to reset the eye and viewplatform position.
final JButton resetButton = new JButton("Reset") ;
resetButton.setAlignmentX(Component.CENTER_ALIGNMENT) ;
resetButton.addActionListener(this) ;
// Create a Swing Box to hold the UI elements.
javax.swing.Box box = javax.swing.Box.createVerticalBox() ;
box.add(xLabel) ;
box.add(eyeOffsetXSlider) ;
box.add(yLabel) ;
box.add(eyeOffsetYSlider) ;
box.add(zLabel) ;
box.add(eyeOffsetZSlider) ;
box.add(resetButton) ;
return box ;
}
public void stateChanged(ChangeEvent e) {
// Update eye offset from slider values.
eyeUpdater.updateEyeOffset(eyeOffsetXSlider.getValue() / 100f,
eyeOffsetYSlider.getValue() / 100f,
eyeOffsetZSlider.getValue() / 100f) ;
}
public void actionPerformed(ActionEvent e) {
// Reset eye offset and view platform position.
eyeUpdater.updateEyeOffset(initialEyeOffsetX,
initialEyeOffsetY,
initialEyeOffsetZ) ;
eyeOffsetXSlider.setValue((int)(initialEyeOffsetX*100f)) ;
eyeOffsetYSlider.setValue((int)(initialEyeOffsetY*100f)) ;
eyeOffsetZSlider.setValue((int)(initialEyeOffsetZ*100f)) ;
orbit.goHome() ;
}
public class ManualEyeUpdater
implements ComponentListener, HierarchyBoundsListener {
private Canvas3D canvas ;
private Point3d eye = new Point3d() ;
private Rectangle screenBounds ;
private double metersPerPixelX ;
private double metersPerPixelY ;
private double canvasCenterX, canvasCenterY ;
private float eyeOffsetX, eyeOffsetY, eyeOffsetZ ;
public ManualEyeUpdater(SimpleUniverse u, Canvas3D canvas,
float eyeOffsetX, float eyeOffsetY,
float eyeOffsetZ) {
this.canvas = canvas ;
this.eyeOffsetX = eyeOffsetX ;
this.eyeOffsetY = eyeOffsetY ;
this.eyeOffsetZ = eyeOffsetZ ;
// Use the RELATIVE_TO_SCREEN eyepoint policy and listen for
// canvas moves and resizes to manually maintain the eyepoint in
// the desired position relative to the canvas.
//
// RELATIVE_TO_WINDOW can't be used since that policy only uses
// the Z component of the manual eyepoint and always centers it in
// the canvas.
View v = u.getViewer().getView() ;
v.setWindowEyepointPolicy(View.RELATIVE_TO_SCREEN) ;
// To simplify eye placement, use the NOMINAL_SCREEN view attach
// policy to set the view platform origin in physical coordinates
// to the center of coexistence. The center of coexistence
// follows the center of the canvas when using the default window
// movement policy of PHYSICAL_WORLD.
ViewingPlatform vp = u.getViewingPlatform();
vp.getViewPlatform().setViewAttachPolicy(View.NOMINAL_SCREEN) ;
// Derive information needed to compute the middle of the Canvas3D
// relative to its position and size on the image plate.
Screen3D screen = canvas.getScreen3D() ;
screenBounds = canvas.getGraphicsConfiguration().getBounds() ;
double physicalScreenWidth = screen.getPhysicalScreenWidth() ;
double physicalScreenHeight = screen.getPhysicalScreenHeight() ;
metersPerPixelX =
physicalScreenWidth / (double)screenBounds.width ;
metersPerPixelY =
physicalScreenHeight / (double)screenBounds.height ;
// Add listeners.
canvas.addComponentListener(this) ;
canvas.addHierarchyBoundsListener(this) ;
}
public void componentShown(ComponentEvent e) {
updateCanvasCenter() ;
}
public void componentMoved(ComponentEvent e) {
// Normally the canvas never moves relative to content pane.
// We have to listen for ancestorMoved events instead.
updateCanvasCenter() ;
}
public void componentResized(ComponentEvent e) {
updateCanvasCenter() ;
}
public void componentHidden(ComponentEvent e) {
// No need to do anything here.
}
public void ancestorMoved(HierarchyEvent e) {
// Normally the canvas never moves relative to content pane.
// We have to listen for ancestorMoved events instead.
updateCanvasCenter() ;
}
public void ancestorResized(HierarchyEvent e) {
// This will always trigger a componentResized on the canvas.
// No need to update the canvas center for each ancestor resize.
}
/**
* Compute the center of the Canvas3D in physical image plate
* coordinates.
*/
public void updateCanvasCenter() {
int w = canvas.getWidth() ;
int h = canvas.getHeight() ;
// Java 3D problem: the getPixelLocationInImagePlate() method of
// Canvas3D should be able to take pixel values relative to the
// upper left corner of the canvas and convert them to their
// corresponding physical image plate coordinates here, but they
// instead always reflect the previous canvas position.
//
// The workaround is to get the location of the canvas center
// relative to the screen and convert it explicitly to physical
// image plate coordinates based on the size of a pixel in
// meters.
double physicalWidth = w * metersPerPixelX ;
double physicalHeight = h * metersPerPixelY ;
// AWT pixel locations are expressed in terms of a contiguous
// virtual screen area which encompasses all physical screens.
// Subtract the screen origin to get pixels relative to the
// physical screen on which the Canvas3D was created.
Point awtLocation = canvas.getLocationOnScreen() ;
int x = awtLocation.x - screenBounds.x;
int y = awtLocation.y - screenBounds.y;
double physicalXLeft = x * metersPerPixelX ;
double physicalYBottom =
(screenBounds.height - (y + h)) * metersPerPixelY ;
canvasCenterX = physicalXLeft + physicalWidth / 2.0 ;
canvasCenterY = physicalYBottom + physicalHeight / 2.0 ;
updateEye() ;
}
public void updateEyeOffset(float eyeOffsetX,
float eyeOffsetY, float eyeOffsetZ) {
this.eyeOffsetX = eyeOffsetX ;
this.eyeOffsetY = eyeOffsetY ;
this.eyeOffsetZ = eyeOffsetZ ;
updateEye() ;
}
private void updateEye() {
eye.x = canvasCenterX + eyeOffsetX ;
eye.y = canvasCenterY + eyeOffsetY ;
eye.z = eyeOffsetZ ;
canvas.setLeftManualEyeInImagePlate(eye) ;
canvas.setRightManualEyeInImagePlate(eye) ;
}
}
public static void main(String[] args) {
new MainFrame(new ManualEyeDemo(), 700, 700);
}
}
===========================================================================
To unsubscribe, send email to [EMAIL PROTECTED] and include in the body
of the message "signoff JAVA3D-INTEREST". For general help, send email to
[EMAIL PROTECTED] and include in the body of the message "help".