I'm going to create a small 2D/3D CAD tool using Java3D. One thing that a user should
be able to do with the tool is to draw 2D geometries (rectangle, ellipse, etc.) using
the mouse. My idea is that the user should be able to rotate the 3D scene to an
arbitrary view (in order to get a 2D work plane), then draw 2D geomtries using the
mouse, having the locked 3D view as background. I have tested a couple ways of doing
this but no way that looks good or is fast enough. Maybe someone can give me some help
here?
My 3D scene is quite large (300-400 Shape3D nodes (using LineArray and TriangleArray)
will be common) and I want to have rubberband effects when drawing the 2D geometries.
Here are the different approaches I have tested:
- Java2D on Canvas3D (see HelloUniverse.java below)
Very slow. The idea was to do rubberbanding effects etc. using Java2D which is fast if
Java3D is not present. Then when the user finishes the draw event the start and stop
coordinates would be used to create a Shape3D node. The rubberband effect is very
slow. Maybe I am doing something wrong?
- Start/Stop renderer and GraphicsContext3D (see MixedImmediateFront.java below)
The rubberband effects are fast but flickering. It is not easy to see the flicker with
the example below, but when you try to something more advanced like drawing a cirle
you can see the irritating flicker.
- MouseListeners on Canvas3D and Shape3D.setGeometry
Draw directly in to Java3D by changing the geometry of a Shape3D gives me something
that looks nice, but unfortunately the rubberband effects become too slow when having
large 3D scenes. If it is possible to speed this approach up I would really like to
use it (then I could just skip using Java2D in my CAD tool). Is there a way to tell
the renderer that most of the scene should not be rendered (most of the scene will not
change)? Are there any other ways to speed this up?
I have seen a lot of messages on this interest group about overlays etc. I have also
tried to figure out wheter the overlay library from Columbia University is something
that I should use for this? Any ideas?
/Anders
/* BEGIN: HelloUniverse ------------------------------------------ */
import com.sun.j3d.utils.behaviors.mouse.MouseTranslate;
import com.sun.j3d.utils.behaviors.mouse.MouseRotate;
import com.sun.j3d.utils.behaviors.mouse.MouseZoom;
import java.applet.Applet;
import java.awt.BorderLayout;
import java.awt.event.*;
import java.awt.GraphicsConfiguration;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.geometry.ColorCube;
import com.sun.j3d.utils.universe.*;
import javax.media.j3d.*;
import javax.vecmath.*;
import java.awt.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import java.awt.font.TextLayout;
import java.awt.font.FontRenderContext;
import java.io.*;
import java.util.*;
public class HelloUniverse extends Applet {
private HelloCanvas3D c;
public BranchGroup createSceneGraph(Canvas3D canvas) {
// Create the root of the branch graph
BranchGroup objRoot = new BranchGroup();
// Create the transform group node and initialize it to the
// identity. Enable the TRANSFORM_WRITE capability so that
// our behavior code can modify it at runtime. Add it to the
// root of the subgraph.
TransformGroup objTrans = new TransformGroup();
objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
objRoot.addChild(objTrans);
// Create a simple shape leaf node, add it to the scene graph.
objTrans.addChild(new ColorCube(0.4));
BoundingSphere bounds=new BoundingSphere(new Point3d(0.0,0.0,0.0),100.0);
// Create the rotate behavior node
MouseRotate behavior=new MouseRotate(objTrans);
objTrans.addChild(behavior);
behavior.setSchedulingBounds(bounds);
// Have Java 3D perform optimizations on this scene graph.
objRoot.compile();
return objRoot;
}
class HelloCanvas3D extends Canvas3D {
private GraphicsContext3D gc;
private J3DGraphics2D g2 = null;
private Dimension d = new Dimension(0, 0);
public HelloCanvas3D(GraphicsConfiguration gcfg) {
super(gcfg,false);
}
public void postRender() {
if (g2 == null) {
g2 = this.getGraphics2D();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
}
this.getSize(d);
drawDemo(d.width, d.height, g2);
g2.flush(false);
}
}
public void drawDemo(int w, int h, J3DGraphics2D g2) {
g2.setColor(new Color(1.0f,1.0f,1.0f));
g2.drawString("abc 123", 50, 50);
}
public HelloUniverse() {
setLayout(new BorderLayout());
GraphicsConfiguration config =
SimpleUniverse.getPreferredConfiguration();
c = new HelloCanvas3D(config);
add("Center", c);
// Create a simple scene and attach it to the virtual universe
BranchGroup scene = createSceneGraph(c);
SimpleUniverse u = new SimpleUniverse(c);
// This will move the ViewPlatform back a bit so the
// objects in the scene can be viewed.
u.getViewingPlatform().setNominalViewingTransform();
u.addBranchGraph(scene);
}
//
// The following allows HelloUniverse to be run as an application
// as well as an applet
//
public static void main(String[] args) {
new MainFrame(new HelloUniverse(), 256, 256);
}
}
/* END: HelloUniverse ------------------------------------------ */
/* BEGIN: MixedImmediateFront ----------------------------------- */
import java.applet.Applet;
import java.awt.BorderLayout;
import java.awt.GraphicsConfiguration;
import java.awt.event.*;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.geometry.ColorCube;
import com.sun.j3d.utils.universe.*;
import javax.media.j3d.*;
import javax.vecmath.*;
import java.awt.*;
import java.awt.image.DataBufferInt;
import java.awt.image.BufferedImage;
import java.util.Enumeration;
import java.text.NumberFormat;
/**
* An example of mixing immediate mode rendering with normal rendering. The
* "HelloUniverse" spinning cube is draw until there is a mouse button pressed.
* Then a rubber-band line is drawn in immediate mode in front of the existing
* image by making updates to the front buffer. The previous lines (if any)
* are "undrawn" by drawing them with the XOR raster op.
*/
public class MixedImmediateFront extends Applet {
Canvas3D canvas;
GraphicsContext3D gc = null;
LineArray line = null;
Transform3D sphereTransform = new Transform3D();
WakeupCriterion[] mouseEvents;
WakeupOr mouseCriterion;
boolean rendererRunning = true;
Point3d coords[] = new Point3d[2];
Point3d baseMouse = new Point3d();
Point3d curMouse = new Point3d();
Transform3D ipToVworld = new Transform3D();
Appearance whiteAppearance;
// only works in single buffer mode right now
static final boolean singleBuffer = false;
public void setBase(int x, int y) {
canvas.getPixelLocationInImagePlate(x, y, baseMouse);
canvas.getImagePlateToVworld(ipToVworld);
ipToVworld.transform(baseMouse);
}
public void render(int x, int y) {
if (line != null) {
gc.draw(line);
}
canvas.getPixelLocationInImagePlate(x, y, curMouse);
canvas.getImagePlateToVworld(ipToVworld);
ipToVworld.transform(curMouse);
line = new LineArray(2, LineArray.COORDINATES);
coords[0] = baseMouse;
coords[1] = curMouse;
line.setCoordinates(0, coords);
// Render the geometry for this frame
gc.draw(line);
// flush to make sure the display is correct
gc.flush(false);
}
//
//Applet init routine (called by browser or by MainFrame)
//
public void init() {
setLayout(new BorderLayout());
canvas = new Canvas3D(SimpleUniverse.getPreferredConfiguration());
if (singleBuffer) {
canvas.setDoubleBufferEnable(false);
}
add("Center", canvas);
// set up the graphics context
gc = canvas.getGraphicsContext3D();
gc.setBufferOverride(true);
// set up the appearances
RenderingAttributes ra = new RenderingAttributes();
ra.setDepthBufferEnable(false);
ra.setRasterOpEnable(true);
ra.setRasterOp(RenderingAttributes.ROP_XOR);
whiteAppearance = new Appearance();
whiteAppearance.setRenderingAttributes(ra);
gc.setAppearance(whiteAppearance);
// Create a simple scene and attach it to the virtual universe
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();
BranchGroup scene = createSceneGraph();
u.addBranchGraph(scene);
BranchGroup behaviorRoot = new BranchGroup();
behaviorRoot.addChild(new Callback());
u.addBranchGraph(behaviorRoot);
}
public class Callback extends Behavior {
public void initialize() {
mouseEvents = new WakeupCriterion[3];
mouseEvents[0] = new WakeupOnAWTEvent(MouseEvent.MOUSE_DRAGGED);
mouseEvents[1] = new WakeupOnAWTEvent(MouseEvent.MOUSE_PRESSED);
mouseEvents[2] = new WakeupOnAWTEvent(MouseEvent.MOUSE_RELEASED);
mouseCriterion = new WakeupOr(mouseEvents);
wakeupOn (mouseCriterion);
setSchedulingBounds(new BoundingSphere(new Point3d(),1000));
}
public void processStimulus (Enumeration criteria) {
WakeupCriterion wakeup;
AWTEvent[] event;
int id;
int dx, dy;
while (criteria.hasMoreElements()) {
wakeup = (WakeupCriterion) criteria.nextElement();
if (wakeup instanceof WakeupOnAWTEvent) {
event = ((WakeupOnAWTEvent)wakeup).getAWTEvent();
for (int i=0; i<event.length; i++) {
MouseEvent mevent = (MouseEvent) event[i];
if (!mevent.isMetaDown() &&
!mevent.isAltDown()){
id = event[i].getID();
switch (id) {
case MouseEvent.MOUSE_PRESSED:
setBase(mevent.getX(), mevent.getY());
// no break here on purpose, fall through to
// drag case
case MouseEvent.MOUSE_DRAGGED:
if (rendererRunning) {
canvas.stopRenderer();
rendererRunning = false;
gc.setFrontBufferRendering(true);
}
render(mevent.getX(), mevent.getY());
break;
case MouseEvent.MOUSE_RELEASED:
//System.out.println("start renderer");
if (!singleBuffer) {
gc.setFrontBufferRendering(false);
}
canvas.startRenderer();
rendererRunning = true;
// no need to undraw previous anymore
line = null;
break;
}
}
}
}
}
wakeupOn (mouseCriterion);
}
}
public BranchGroup createSceneGraph() {
// Create the root of the branch graph
BranchGroup objRoot = new BranchGroup();
// Create the transform group node and initialize it to the
// identity. Enable the TRANSFORM_WRITE capability so that
// our behavior code can modify it at runtime. Add it to the
// root of the subgraph.
TransformGroup objTrans = new TransformGroup();
objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
objRoot.addChild(objTrans);
// Create a simple shape leaf node, add it to the scene graph.
objTrans.addChild(new ColorCube(0.4));
// Create a new Behavior object that will perform the desired
// operation on the specified transform object and add it into
// the scene graph.
Transform3D yAxis = new Transform3D();
Alpha rotationAlpha = new Alpha(-1, Alpha.INCREASING_ENABLE,
0, 0,
4000, 0, 0,
0, 0, 0);
RotationInterpolator rotator =
new RotationInterpolator(rotationAlpha, objTrans, yAxis,
0.0f, (float) Math.PI*2.0f);
BoundingSphere bounds =
new BoundingSphere(new Point3d(0.0,0.0,0.0), 100.0);
rotator.setSchedulingBounds(bounds);
objTrans.addChild(rotator);
// Have Java 3D perform optimizations on this scene graph.
objRoot.compile();
return objRoot;
}
//
// The following allows MixedImmediateFront to be run as an application
// as well as an applet
//
public static void main(String[] args) {
new MainFrame(new MixedImmediateFront(), 600, 600);
}
}
/* END: MixedImmediateFront ----------------------------------- */
===========================================================================
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".