I'm developing a JavaFX desktop application. In it I want to display some java 3D objects.
The way to do this, as far as I know, is using JavaFX's SwingNode. Java 3d has to be integrated in a Swing component too. So it's a two-step process.
Integrating Java 3D objects with Java Swing
Complete and minimal example composed of the panel I intend to reuse in the FX app and a JFrame that shows it works:
import java.awt.BorderLayout;
import javax.swing.JFrame;
public class JFrameWithCanvas3D extends JFrame {
public JFrameWithCanvas3D() {
super("Swing JFrame Wraps Canvas3D");
setLayout(new BorderLayout());
BallPanel panel = new BallPanel();
add(panel, BorderLayout.CENTER);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setVisible(true);
}
public static void main(String[] args) {
System.setProperty("sun.awt.noerasebackground", "true");
javax.swing.SwingUtilities.invokeLater(new Runnable() {
@SuppressWarnings("unused")
@Override
public void run() {
new JFrameWithCanvas3D();
}
});
}
}
public class BallPanel extends JPanel {
public BallPanel() {
setLayout(new BorderLayout());
setPreferredSize(new Dimension(500, 500));
add(makeCanvas());
}
private static Canvas3D makeCanvas() {
BranchGroup group = new BranchGroup();
group.addChild(makeLight());
group.addChild(new Sphere(5));
Canvas3D canvas3D = new Canvas3D(SimpleUniverse.getPreferredConfiguration());
SimpleUniverse universe = new SimpleUniverse(canvas3D);
Transform3D viewTransform = new Transform3D();
viewTransform.setTranslation(new Vector3d(0, 0, 20)); //move "back" a little
universe.getViewingPlatform().getViewPlatformTransform().setTransform(viewTransform);
universe.addBranchGraph(group);
return canvas3D;
}
private static DirectionalLight makeLight() {
DirectionalLight light = new DirectionalLight(new Color3f(Color.WHITE), new Vector3f(-1.0f, -1.0f, -1.0f));
light.setInfluencingBounds(new BoundingSphere(new Point3d(0, 0, 0), 100));
return light;
}
}
Integrating the BallPanel in the JavaFX app
public class FXAppWithSwingPanel extends Application {
@Override
public void start(Stage stage) {
final SwingNode swingNode = new SwingNode();
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
panel.setPreferredSize(new Dimension(450, 450));
panel.add(new BallPanel(), BorderLayout.CENTER);
swingNode.setContent(panel);
Pane pane = new Pane();
pane.getChildren().add(swingNode);
stage.setScene(new Scene(pane, 500, 500));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
All I see is a gray background where the sphere should be. I've tried a few different variations with no luck. Couldn't find anything online about this particular integration between Java 3D classes and JavaFX.
I know JavaFX has 3D graphics but I don't like them (at all).
Related, unresolved, question:
I finally managed to make it work using JCanvas3D. Credit for the suggestion goes to @gouessej. Many thanks.
Here is the new makeCanvas
method:
private void makeCanvas() {
GraphicsConfigTemplate3D gCT = new GraphicsConfigTemplate3D();
JCanvas3D jCanvas3D = new JCanvas3D(gCT);
Dimension canvasDim = new Dimension(400, 400);
jCanvas3D.setPreferredSize(canvasDim);
jCanvas3D.setSize(canvasDim);
add(jCanvas3D, BorderLayout.CENTER);
Canvas3D canvas3D = jCanvas3D.getOffscreenCanvas3D();
View view = new View();
view.setPhysicalBody(new PhysicalBody());
view.setPhysicalEnvironment(new PhysicalEnvironment());
view.addCanvas3D(canvas3D);
ViewPlatform vp = new ViewPlatform();
view.attachViewPlatform(vp);
Transform3D viewTransform = new Transform3D();
viewTransform.setTranslation(new Vector3d(0, 0, 20)); //move "back" a little
TransformGroup viewTG = new TransformGroup();
viewTG.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
viewTG.setTransform(viewTransform);
viewTG.addChild(vp);
viewTG.addChild(makeLight());
viewTG.addChild(new Sphere(5));
BranchGroup group = new BranchGroup();
group.addChild(viewTG);
group.addChild(makeLight());
group.addChild(new Sphere(5));
VirtualUniverse vu = new VirtualUniverse();
Locale locale = new Locale(vu);
locale.addBranchGraph(group);
}
Also, as James_D points out in the comments, Canvas3D
was not the way to go as it cannot be correctly rendered in a JavaFX
SwingNode
because it is a heavyweight component.
The SwingNode javadoc confirms it:
The hierarchy of components contained in the JComponent instance should not contain any heavyweight components, otherwise SwingNode may fail to paint it.