Here is the Overlay Library that I have implemented. I have tried
minimizing this implementation so it can be as flexible as possible.
Two classes exist, both in the package edu.columbia.cs.cgui.j3d:
FixedCanvas3D and OverlayGroup.
FixedCanvas3D is interchangeable with Canvas3D, and NEEDS to be used if
you want to use OverlayGroup. OverlayGroup allows you to place overlayed
objects by having a Node that is in pixel coordinates (origin is in the
bottom left hand corner of the Canvas), and gives you the
flexibility to create a scenegraph below this group node to place the
overlay objects.
Some functionality that you might find useful:
* Supports all View Model Parameters that are not part of head tracking.
As noted in the documentation, you cannot currently do Overlays in a head
tracked environment (Java3D limitation, hoping to be fixed in the near
future).
* Supports changing Z values for Overlays. This gets very interesting,
especially in stereo since 2D overlays cannot do this. You do not need to
use this functionality, you can also place it directly in front of the
front clipping plane.
* Works around the 'lag' problems introduced in the WakeUpOnElapsedFrame
behavior because Canvas3D caches values from previous frame. Also, I
provide new functions in FixedCanvas3D that should be in Canvas3D, such as
transformations relative to the ViewPlatform.
Please send be comments/suggestions. I know that this is a hot topic on
this newsgroup, and I hope this helps everyone.
Please let me know what you think!
Soon coming: A View Model Demo Program to explain this crazy View Model!
Blaine
On Mon, 11 Jun 2001, Artur Biesiadowski wrote:
> "Yazel, David J." wrote:
>
> > 17. No plans to implement resize because of the issus involved in rebuilding
> > the sub-textures. Would be easier to just make a new one. We might support
> > use of model-clips to clip in from a maximum size.
>
> I was trying to do overlay system some time ago and failed. Anyway, I've
> come up with following interface - it might be an overkill if you don't
> plan resize, but maybe you will like to reuse some idea.
>
> Overlay have specified xoffset, yoffset, width and height at creation
> time. These are double values, with following meanings:
>
> xoffset -
> if positive > 1, means distance of left edge of overlay from left side
> of window in pixels
> if negative < -1, means distance of right edge of overlay from right
> side of window in pixels
> if positive < 1, means how much free space should be used for the left
> side (fraction)
> if negative > -1, means how much free space should be used for the
> right side (same as 1+negative)
> yoffset - the same, just top and bottom
>
> width -
> if positive > 1 means width of overlay in pixels
> if negative < -1 means how much free width should be left on screen
> after substracting overlay width (in pixels)
> if positive < 1 means width of overlay as fraction of entire window
> width
> if negative > -1 means fraction of width which should be unoccupied by
> overlay (same as 1+negative)
> height - same as width, just for height
>
> This would allow for very dynamic system, but unfortunately size of
> texture would have to change with window resizes. It might be too much
> for your needs, then please implement at least negative offsets - to
> allow placing overlay relative to right and bottom edges (not only top
> and left). Fractional offsets would be also nice (allows for example
> centering with 0.5, 0.5 offsets).
>
> Artur
>
> ===========================================================================
> 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".
>
/*
* @(#)FixedCanvas3D.java
*
* Date: 6/11/2001
*
* Copyright 2001 Columbia University, All Rights Reserved.
*
* This software is the proprietary information of Columbia University.
* Please contact Blaine Bell ([EMAIL PROTECTED]) for any bugs or enhancements.
*
*/
package edu.columbia.cs.cgui.j3d;
import javax.media.j3d.*;
import javax.vecmath.*;
import java.awt.*;
import java.awt.event.*;
/** This class provides extra functionality that Canvas3D does not have. Also, the
functions
* that overides Canvas3D methods are not cached so that there is not a frame lag when
used
* in WakeUpOnElapsedFrame as long as the View Model and TransformGroups above the
ViewPlatform
* are changed before these functions are used.
*
* This class assumes:
* The ViewPlatform has capabilities set to ALLOW_LOCAL_TO_VWORLD_READ
* <p>
* @author Blaine Bell
* @since JDK1.1
* @version 1.0
*
**/
public class FixedCanvas3D extends Canvas3D implements ComponentListener{
double _fclipdist=0.,_bclipdist=0.;
public void refreshAllVariables(){
if (getView()!=null){
computeFrontClipValueInImagePlate();
computeBackClipValueInImagePlate();
}
}
public void componentHidden(ComponentEvent e){
refreshAllVariables();
}
public void componentShown(ComponentEvent e){
refreshAllVariables();
}
public void componentMoved(ComponentEvent e){
refreshAllVariables();
}
public void componentResized(ComponentEvent e){
refreshAllVariables();
}
/* Constructors */
public FixedCanvas3D(java.awt.GraphicsConfiguration graphicsConfiguration){
super(graphicsConfiguration);
addComponentListener(this);
refreshAllVariables();
}
public FixedCanvas3D(java.awt.GraphicsConfiguration graphicsConfiguration,
boolean offScreen){
super(graphicsConfiguration,offScreen);
addComponentListener(this);
refreshAllVariables();
}
public double getFrontClipValueInImagePlate(){
return (_fclipdist);
}
public double computeFrontClipValueInImagePlate(){
int fviewpolicy=getView().getFrontClipPolicy();
_fclipdist = -getView().getFrontClipDistance();
if (fviewpolicy==View.VIRTUAL_EYE || fviewpolicy==View.VIRTUAL_SCREEN){
/** need to multiply it by virtual-phyiscal scale **/
if (getView().getWindowResizePolicy()==View.PHYSICAL_WORLD){
/* Resize policy has effect on clipping planes */
_fclipdist *= getPhysicalWidth()/2.;
} else {
_fclipdist *= getScreen3D().getPhysicalScreenWidth()/2.;
}
} else if (getView().getWindowResizePolicy()==View.PHYSICAL_WORLD){
_fclipdist *= getPhysicalWidth()/getScreen3D().getPhysicalScreenWidth();
}
if (fviewpolicy==View.PHYSICAL_EYE || fviewpolicy==View.VIRTUAL_EYE){
Point3d eyepoint = new Point3d();
getCenterEyeInImagePlate(eyepoint);
_fclipdist += eyepoint.z;
}
return (_fclipdist);
}
public double getBackClipValueInImagePlate(){
return (_bclipdist);
}
public double computeBackClipValueInImagePlate(){
int bviewpolicy=getView().getBackClipPolicy();
_bclipdist = -getView().getBackClipDistance();
if (bviewpolicy==View.VIRTUAL_EYE || bviewpolicy==View.VIRTUAL_SCREEN){
/** need to multiply it by virtual-phyiscal scale **/
if (getView().getWindowResizePolicy()==View.PHYSICAL_WORLD){
/* Resize policy has effect on clipping planes */
_bclipdist *= getPhysicalWidth()/2.;
} else {
_bclipdist *= getScreen3D().getPhysicalScreenWidth()/2.;
}
} else if (getView().getWindowResizePolicy()==View.PHYSICAL_WORLD){
_bclipdist *= getPhysicalWidth()/getScreen3D().getPhysicalScreenWidth();
}
if (bviewpolicy==View.PHYSICAL_EYE || bviewpolicy==View.VIRTUAL_EYE){
Point3d eyepoint = new Point3d();
getCenterEyeInImagePlate(eyepoint);
_bclipdist += eyepoint.z;
}
return (_bclipdist);
}
public void getImagePlateToVworld(Transform3D t){
ViewPlatform viewplatform=getView().getViewPlatform();
Transform3D vp2vw = new Transform3D();
viewplatform.getLocalToVworld(vp2vw);
getViewPlatformToImagePlate (t);
t.mul(vp2vw,t);
}
public void getViewPlatformToImagePlate (Transform3D t){
View view=getView();
ViewPlatform viewplatform=getView().getViewPlatform();
int resizepolicy = view.getWindowResizePolicy(),
movementpolicy = view.getWindowMovementPolicy(),
viewattachmentpolicy = viewplatform.getViewAttachPolicy();
Screen3D screen = getScreen3D();
Vector2d canvasPhysDimensions = null,
screenPhysDimensions = new Vector2d(screen.getPhysicalScreenWidth(),
screen.getPhysicalScreenHeight());
Point canvasLocation = null;
Dimension screenPixSize = null,canvasPixSize = null;
/** Need to compute canvas coordinates in imageplate using location on screen */
screenPixSize = screen.getSize();
try {
canvasLocation = getLocationOnScreen();
} catch (Exception e){
t.setIdentity();
return;
}
canvasPixSize = getSize();
/** getLocationOnScreen returns from upper left, we want lower left */
canvasLocation.y = screenPixSize.height-canvasLocation.y-canvasPixSize.height;
canvasPhysDimensions = new
Vector2d(screenPhysDimensions.x*canvasPixSize.width/screenPixSize.width,
screenPhysDimensions.y*canvasPixSize.height/screenPixSize.height);
t.setIdentity();
Vector3d translation=new Vector3d();
double scale=1;
if (resizepolicy==View.VIRTUAL_WORLD){
/** View Platform in center of screen */
scale = .5*screenPhysDimensions.x;
} else {
/* The ratio of the widths between the canvas and screen */
scale = .5*canvasPhysDimensions.x;
}
t.setScale(scale);
if (movementpolicy==View.VIRTUAL_WORLD){
/** Middle of screen */
translation.x = .5*screenPhysDimensions.x;
translation.y = .5*screenPhysDimensions.y;
} else {
/** Size of Screen in VWorld is 2 wide
* since scale is */
translation.x = screenPhysDimensions.x*(canvasLocation.x +
canvasPixSize.width/2.)/screenPixSize.width;
translation.y = screenPhysDimensions.y*(canvasLocation.y +
canvasPixSize.height/2.)/(double)screenPixSize.height;
}
if (view.getWindowEyepointPolicy()==View.RELATIVE_TO_FIELD_OF_VIEW){
if (viewattachmentpolicy==View.NOMINAL_SCREEN){
translation.z = 0.;
} else if (viewattachmentpolicy==View.NOMINAL_HEAD){
translation.z =
.5*canvasPhysDimensions.x/Math.tan(view.getFieldOfView()/2.);
} else if (viewattachmentpolicy==View.NOMINAL_FEET){
translation.y -=
view.getPhysicalBody().getNominalEyeHeightFromGround()*scale/(.5*screenPhysDimensions.x);;
translation.z =
.5*canvasPhysDimensions.x/Math.tan(view.getFieldOfView()/2.);
}
} else {
if (viewattachmentpolicy==View.NOMINAL_SCREEN){
translation.z = 0.;
} else if (viewattachmentpolicy==View.NOMINAL_HEAD){
translation.z =
view.getPhysicalBody().getNominalEyeOffsetFromNominalScreen();
} else if (viewattachmentpolicy==View.NOMINAL_FEET){
translation.y -= view.getPhysicalBody().getNominalEyeHeightFromGround();
translation.z =
view.getPhysicalBody().getNominalEyeOffsetFromNominalScreen();
}
}
t.setTranslation(translation);
try {
t.invert();
} catch (Exception e){
System.out.println("t=" + t);
e.printStackTrace();
System.out.println("canvasLocation=" + canvasLocation + " canvasPixSize=" +
canvasPixSize + " screenPhysDimensions=" + screenPhysDimensions + "
canvasPhysDimensions=" + canvasPhysDimensions);
}
}
public void getImagePlateToViewPlatform(Transform3D t){
getViewPlatformToImagePlate (t);
t.invert();
}
public void getVworldToImagePlate(Transform3D t){
getImagePlateToVworld(t);
t.invert();
}
public void getPixelLocationInImagePlate(int x, int y, Point3d imagePlatePoint){
double dx=x, dy=y;
Screen3D screen = getScreen3D();
Dimension screenPixSize = screen.getSize(),canvasPixSize = getSize();
Point canvasLocation = getLocationOnScreen();
Vector2d canvasPhysDimensions = new Vector2d(getPhysicalWidth(),
getPhysicalHeight()),
screenPhysDimensions = new Vector2d(screen.getPhysicalScreenWidth(),
screen.getPhysicalScreenHeight());
imagePlatePoint.x = screenPhysDimensions.x*((canvasLocation.x +
dx)/(double)screenPixSize.width);
imagePlatePoint.y = screenPhysDimensions.y*((screenPixSize.height-canvasLocation.y
- dy-1)/(double)screenPixSize.height);
imagePlatePoint.z = 0.;
}
public void getPixelLocationInImagePlate(Point2d pixelLocation, Point3d
imagePlatePoint){
getPixelLocationInImagePlate((int)Math.round(pixelLocation.x),(int)Math.round(pixelLocation.y),
imagePlatePoint);
}
}
/*
* @(#)OverlayGroup.java
*
* Date: 6/11/2001
*
* Copyright 2001 Columbia University, All Rights Reserved.
*
* This software is the proprietary information of Columbia University.
* Please contact Blaine Bell ([EMAIL PROTECTED]) for any bugs or possible
enhancements.
*
*/
package edu.columbia.cs.cgui.j3d;
import javax.media.j3d.*;
import java.awt.*;
import javax.vecmath.*;
import java.util.*;
/** This class provides the ability to place scenegraph objects relative to the Java3D
Canvas3D.
* The implementation places a TransformGroup below the parent of the ViewPlatform,
and
* reads all of the parameters in the View Model to determine the transformation from
the
* ViewPlatform coordinate system to the pixel coordinate system. The origin of the
Overlay Group
* is placed in the bottom left hand corner of the Canvas3D. It also allows you to
place
* objects in specified Z.
* <p>
* This supports most (if not all) of the non-headtracking parts of the ViewModel.
Currently,
* Java3D does not support Overlaying in a head-tracked View Model settings because
the
* values of the Sensors get read AFTER the WakeUpOnElapsedFrame behaviors are run,
but
* before the next frame is rendered. This is currently under inquiry with the Sun
Java3D team,
* and hopefully will be fixed in the near future.
* <p>
* Also, when the Canvas3D is moved on the screen, the ImagePlate changes with respect
to the
* ViewPlatform. These events (Window Move and Resize) are captured by AWT events,
and therefore
* since their is a lag time/syncronization error between Java3D and AWT, this
produces nasty
* jitter when these events happen. Better to not move/resize the window when
possible.
*
* @author Blaine Bell
* @since JDK1.1
* @version 1.0
*
**/
public class OverlayGroup {
FixedCanvas3D _canvas=null;
View _view=null;
Group _group=null;
static Vector allOverlayGroups=new Vector();
TransformGroup tg = new TransformGroup();
boolean setup=false;
boolean onNearClippingPlane=false, onNearSet=false;
/** determines if you want to place the overlaygroup on the Near Clipping plane **/
public void setOnNearClippingPlane(boolean b){
onNearClippingPlane=b;
if (b){
onNearSet=false;
}
}
public OverlayGroup(){
allOverlayGroups.add(this);
}
/** Setups this OverlayGroup. Note: This needs to be called in order for the
overlaygroup to
* work properly. Should only be called once.
* @param c The FixedCanvas3D that needs to be used in order for this to work
properly
* @param groupAboveViewPlatform node directly above the ViewPlatform
* @param overlayAnchor the group node that is placed in the overlayplane/pixel
coordinates
*/
public void setupOverlayGroup(FixedCanvas3D c, Group groupAboveViewPlatform,Group
overlayAnchor) {
_canvas = c;
_view = _canvas.getView();
_group = groupAboveViewPlatform;
setup=true;
if (!_group.isLive()){
_group.setCapability(Group.ALLOW_CHILDREN_WRITE);
_group.setCapability(Group.ALLOW_CHILDREN_EXTEND);
}
if (!tg.isLive()){
tg.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
tg.setCapability(Group.ALLOW_CHILDREN_WRITE);
}
tg.addChild(overlayAnchor);
BranchGroup bg = new BranchGroup();
bg.addChild(tg);
_group.addChild(bg);
}
Dimension screenSize = new Dimension(),canvSize = new Dimension();
/* from imageplane */
double zdist=0.;
static public void updateAllZValues(){
for (Enumeration e=allOverlayGroups.elements();e.hasMoreElements();){
((OverlayGroup)e.nextElement()).updatePlacement();
}
}
/** Allows the Z value to change.
* @param z z-value in image-plate coordinates. Negative Z is typically further away
* from the eyepoint
*/
public void updateZValue(double z){
zdist = z;
if (!onNearSet){
if (setup){
updatePlacement();
}
}
}
/** This function must be called once the View Model is set up properly
* in order for the overlaygroup to work. If any parameters of the View Model
Changes,
* or the Z value is changed, this needs to be called again.
*/
public void updatePlacement(){
_canvas.refreshAllVariables();
if (onNearClippingPlane){
/** This value is dependent on hardware/difference in clipping values
between front and back clipping planes **/
zdist = _canvas.computeFrontClipValueInImagePlate()-.0001;
onNearSet=true;
}
Transform3D t=new Transform3D(),t2=new Transform3D(),t3=new Transform3D();
Screen3D screen = _canvas.getScreen3D();
Point loconscreen = _canvas.getLocationOnScreen();
Point3d eyepoint= new Point3d();
screen.getSize(screenSize);
_canvas.getSize(canvSize);
_canvas.getCenterEyeInImagePlate(eyepoint);
_canvas.getViewPlatformToImagePlate(t);
double imagePlateScale=t.getScale();
Point3d screenOrigin = new Point3d();
/* Translating eyepoint to origin */
t2.set(new Vector3d(eyepoint));
t.mul(t2);
t3.setScale(( eyepoint.z-zdist)/eyepoint.z);
t.mul(t3);
t2.set(new Vector3d(-eyepoint.x,-eyepoint.y,-eyepoint.z));
t.mul(t2);
_canvas.getPixelLocationInImagePlate(0,canvSize.height-1,screenOrigin);
t2.set(new Vector3d(screenOrigin));
t.mul(t2);
t3.setScale(new Vector3d(_canvas.getPhysicalWidth()/canvSize.width,
_canvas.getPhysicalHeight()/canvSize.height,
_canvas.getPhysicalWidth()/canvSize.width));
t.mul(t3);
tg.setTransform(t);
}
}