Eugene
Eugene

Reputation: 1067

JavaFX 3D - How to set different cameras for Group with 3D object and SubScene with UI Controls?

Due to new features in JavaFX 8, it became possible to combine 3D objects with 2D UI controls.

I used this documents as manuals: JavaFX Tutorial, Exploring JavaFX 3D.

So, I made this code:

public class CastAnalytics extends Application {

    final Group root = new Group();
    final Group axisGroup = new Group();
    final XForm world = new XForm();
    final PerspectiveCamera camera = new PerspectiveCamera(true);
    final PerspectiveCamera subSceneCamera = new PerspectiveCamera(false);
    final XForm cameraXForm = new XForm();
    final XForm cameraXForm2 = new XForm();
    final XForm cameraXForm3 = new XForm();
    final double cameraDistance = 450;
    final XForm moleculeGroup = new XForm();
    private Timeline timeline;
    boolean timelinePlaying = false;
    double CONTROL_MULTIPLIER = 0.1;
    double SHIFT_MULTIPLIER = 0.1;
    double ALT_MULTIPLIER = 0.5;
    double mousePosX;
    double mousePosY;
    double mouseOldX;
    double mouseOldY;
    double mouseDeltaX;
    double mouseDeltaY;

    @Override
    public void start(Stage primaryStage) throws Exception{
        buildScene();
        buildCamera();
        buildAxes();

        Scene scene = new Scene(root, 1024, 768, true);
        scene.setFill(Color.GREY);
        handleKeyboard(scene, world);
        handleMouse(scene, world);

        primaryStage.setTitle("Sample Application");
        primaryStage.setScene(scene);
        primaryStage.show();


        scene.setCamera(subSceneCamera);
        scene.setCamera(camera);
    }

    private void buildScene() {
        root.getChildren().add(world);

        Label label = new Label("123");
        HBox hBox = new HBox();
        hBox.getChildren().add(label);
        SubScene subScene =  new SubScene(hBox, 200, 200);
        subScene.setLayoutX(100);
        subScene.setLayoutY(100);

        root.getChildren().addAll(subScene);
    }

    private void buildCamera() {
        root.getChildren().addAll(cameraXForm);
        cameraXForm.getChildren().add(cameraXForm2);
        cameraXForm2.getChildren().add(cameraXForm3);
        cameraXForm3.getChildren().add(camera);
        cameraXForm3.setRotateZ(180.0);

        camera.setNearClip(0.1);
        camera.setFarClip(10000.0);
        camera.setTranslateZ(-cameraDistance);
        cameraXForm.ry.setAngle(320.0);
        cameraXForm.rx.setAngle(40);
    }

    private void buildAxes() {
        Box box = new Box(200,200,200);


        axisGroup.getChildren().addAll(box);
        world.getChildren().addAll(axisGroup);
    }

    private void handleMouse(Scene scene, final Node root) {
        scene.setOnMousePressed(new EventHandler<MouseEvent>() {
            @Override public void handle(MouseEvent me) {
                mousePosX = me.getSceneX();
                mousePosY = me.getSceneY();
                mouseOldX = me.getSceneX();
                mouseOldY = me.getSceneY();
            }
        });
        scene.setOnMouseDragged(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent me) {
                mouseOldX = mousePosX;
                mouseOldY = mousePosY;
                mousePosX = me.getSceneX();
                mousePosY = me.getSceneY();
                mouseDeltaX = (mousePosX - mouseOldX);
                mouseDeltaY = (mousePosY - mouseOldY);

                double modifier = 1.0;
                double modifierFactor = 0.1;

                if (me.isControlDown()) {
                    modifier = 0.1;
                }
                if (me.isShiftDown()) {
                    modifier = 10.0;
                }
                if (me.isPrimaryButtonDown()) {
                    cameraXForm.ry.setAngle(cameraXForm.ry.getAngle() - mouseDeltaX * modifierFactor * modifier * 2.0);  // +
                    cameraXForm.rx.setAngle(cameraXForm.rx.getAngle() + mouseDeltaY * modifierFactor * modifier * 2.0);  // -
                } else if (me.isSecondaryButtonDown()) {
                    double z = camera.getTranslateZ();
                    double newZ = z + mouseDeltaX * modifierFactor * modifier;
                    camera.setTranslateZ(newZ);
                } else if (me.isMiddleButtonDown()) {
                    cameraXForm2.t.setX(cameraXForm2.t.getX() + mouseDeltaX * modifierFactor * modifier * 0.3);  // -
                    cameraXForm2.t.setY(cameraXForm2.t.getY() + mouseDeltaY * modifierFactor * modifier * 0.3);  // -
                }
            }
        });
    }

    private void handleKeyboard(Scene scene, final Node root) {
        final boolean moveCamera = true;
        scene.setOnKeyPressed(new EventHandler<KeyEvent>() {
            @Override
            public void handle(KeyEvent event) {
                Duration currentTime;
                switch (event.getCode()) {
                    case Z:
                        if (event.isShiftDown()) {
                            cameraXForm.ry.setAngle(0.0);
                            cameraXForm.rx.setAngle(0.0);
                            camera.setTranslateZ(-300.0);
                        }
                        cameraXForm2.t.setX(0.0);
                        cameraXForm2.t.setY(0.0);
                        break;
                    case X:
                        if (event.isControlDown()) {
                            if (axisGroup.isVisible()) {
                                axisGroup.setVisible(false);
                            } else {
                                axisGroup.setVisible(true);
                            }
                        }
                        break;
                    case S:
                        if (event.isControlDown()) {
                            if (moleculeGroup.isVisible()) {
                                moleculeGroup.setVisible(false);
                            } else {
                                moleculeGroup.setVisible(true);
                            }
                        }
                        break;
                    case SPACE:
                        if (timelinePlaying) {
                            timeline.pause();
                            timelinePlaying = false;
                        } else {
                            timeline.play();
                            timelinePlaying = true;
                        }
                        break;
                    case UP:
                        if (event.isControlDown() && event.isShiftDown()) {
                            cameraXForm2.t.setY(cameraXForm2.t.getY() - 10.0 * CONTROL_MULTIPLIER);
                        } else if (event.isAltDown() && event.isShiftDown()) {
                            cameraXForm.rx.setAngle(cameraXForm.rx.getAngle() - 10.0 * ALT_MULTIPLIER);
                        } else if (event.isControlDown()) {
                            cameraXForm2.t.setY(cameraXForm2.t.getY() - 1.0 * CONTROL_MULTIPLIER);
                        } else if (event.isAltDown()) {
                            cameraXForm.rx.setAngle(cameraXForm.rx.getAngle() - 2.0 * ALT_MULTIPLIER);
                        } else if (event.isShiftDown()) {
                            double z = camera.getTranslateZ();
                            double newZ = z + 5.0 * SHIFT_MULTIPLIER;
                            camera.setTranslateZ(newZ);
                        }
                        break;
                    case DOWN:
                        if (event.isControlDown() && event.isShiftDown()) {
                            cameraXForm2.t.setY(cameraXForm2.t.getY() + 10.0 * CONTROL_MULTIPLIER);
                        } else if (event.isAltDown() && event.isShiftDown()) {
                            cameraXForm.rx.setAngle(cameraXForm.rx.getAngle() + 10.0 * ALT_MULTIPLIER);
                        } else if (event.isControlDown()) {
                            cameraXForm2.t.setY(cameraXForm2.t.getY() + 1.0 * CONTROL_MULTIPLIER);
                        } else if (event.isAltDown()) {
                            cameraXForm.rx.setAngle(cameraXForm.rx.getAngle() + 2.0 * ALT_MULTIPLIER);
                        } else if (event.isShiftDown()) {
                            double z = camera.getTranslateZ();
                            double newZ = z - 5.0 * SHIFT_MULTIPLIER;
                            camera.setTranslateZ(newZ);
                        }
                        break;
                    case RIGHT:
                        if (event.isControlDown() && event.isShiftDown()) {
                            cameraXForm2.t.setX(cameraXForm2.t.getX() + 10.0 * CONTROL_MULTIPLIER);
                        } else if (event.isAltDown() && event.isShiftDown()) {
                            cameraXForm.ry.setAngle(cameraXForm.ry.getAngle() - 10.0 * ALT_MULTIPLIER);
                        } else if (event.isControlDown()) {
                            cameraXForm2.t.setX(cameraXForm2.t.getX() + 1.0 * CONTROL_MULTIPLIER);
                        } else if (event.isAltDown()) {
                            cameraXForm.ry.setAngle(cameraXForm.ry.getAngle() - 2.0 * ALT_MULTIPLIER);
                        }
                        break;
                    case LEFT:
                        if (event.isControlDown() && event.isShiftDown()) {
                            cameraXForm2.t.setX(cameraXForm2.t.getX() - 10.0 * CONTROL_MULTIPLIER);
                        } else if (event.isAltDown() && event.isShiftDown()) {
                            cameraXForm.ry.setAngle(cameraXForm.ry.getAngle() + 10.0 * ALT_MULTIPLIER);  // -
                        } else if (event.isControlDown()) {
                            cameraXForm2.t.setX(cameraXForm2.t.getX() - 1.0 * CONTROL_MULTIPLIER);
                        } else if (event.isAltDown()) {
                            cameraXForm.ry.setAngle(cameraXForm.ry.getAngle() + 2.0 * ALT_MULTIPLIER);  // -
                        }
                        break;
                }
            }
        });
    }



    public static void main(String[] args) {
        launch(args);
    }
}

But te result isn't that what I expected. I wanted to have Pane for UI controls above the 3D object, but what I get is this:

enter image description here

What am I doing wrong?

Upvotes: 7

Views: 4275

Answers (3)

abdelrahman elattar
abdelrahman elattar

Reputation: 41

Here is the Solution

public class rotate3Dwithpanel extends Application
{
    private double mouseOldX, mouseOldY = 0;
    private Rotate rotateX = new Rotate(0, Rotate.X_AXIS);
    private Rotate rotateY = new Rotate(0, Rotate.Y_AXIS);
    private Rotate rotateZ = new Rotate(0, Rotate.Z_AXIS);

    @Override
    public void start(Stage stage) throws Exception 
    {
        final PhongMaterial redMaterial = new PhongMaterial();
        redMaterial.setSpecularColor(Color.ORANGE);
        redMaterial.setDiffuseColor(Color.RED);

        Box myBox = new Box(100, 100, 100);
        myBox.setTranslateX(400);
        myBox.setTranslateY(300);
        myBox.setTranslateZ(400);
        myBox.setMaterial(redMaterial);

        Rectangle rectangle = new Rectangle();
        rectangle.setX(200);
        rectangle.setY(600);
        rectangle.setWidth(200);
        rectangle.setHeight(100);
        rectangle.setFill(Color.GREY);

        // to Set pivot points
        rotateX.setPivotX(400);
        rotateX.setPivotY(300);
        rotateX.setPivotZ(400);

        rotateY.setPivotX(400);
        rotateY.setPivotY(300);
        rotateY.setPivotZ(400);

        rotateZ.setPivotX(400);
        rotateZ.setPivotY(300);
        rotateZ.setPivotZ(400);


        // initialize the camera
        PerspectiveCamera camera = new PerspectiveCamera(false);
        camera.getTransforms().addAll (rotateX, rotateY, new Translate(0, 0, 0));

        Group root = new Group();
        Group subRoot = new Group();

        root.getChildren().add(rectangle);

        Scene scene = new Scene(root, 1000, 1000, true);
        SubScene subScene = new SubScene(subRoot, 800, 800, true, SceneAntialiasing.BALANCED);

        subScene.setCamera(camera);
        subRoot.getChildren().add(myBox);
        root.getChildren().add(subScene);

        // events for rotation 
        rectangle.setOnMousePressed(event -> {
            mouseOldX = event.getSceneX();
            mouseOldY = event.getSceneY();
        });

        rectangle.setOnMouseDragged(event -> {
            if(event.isPrimaryButtonDown())
            {
                rotateX.setAngle(rotateX.getAngle() - (event.getSceneY() - mouseOldY));
                rotateY.setAngle(rotateY.getAngle() + (event.getSceneX() - mouseOldX));
                mouseOldX = event.getSceneX();
                mouseOldY = event.getSceneY();
            }
        });

        stage.setTitle("JavaFX 3D Object");
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) 
    {
        launch(args);
    }

}

Upvotes: 0

ajeh
ajeh

Reputation: 2784

From what I understand from the (limited) tests I have done, there are two options:

  1. Set a camera for a sub-scene and add that sub-scene to the root. You will be using only one camera. Your world will have to be a separate group and flying/pivoting camera view will have to be accomplished by transforming the world group.

  2. File a bug report with JavaFX jira.

I was not successful using a separate camera as a sub-scene camera. No transforms applied to the camera or a sub-scene itself ever rotated a sub-scene from the default position similar to the one in your screenshot. At this point with Oracle not releasing any sub-scene documentation, we can only wait till they come clean and fill the gaps. Until then we can consider subscene support in JavaFX 3D broken.

Upvotes: 2

Andrew Glushchenko
Andrew Glushchenko

Reputation: 1

The problem is here:

    scene.setCamera(subSceneCamera);
    scene.setCamera(camera);

You can set only one camera in Scene (or SubScene). You need to set second Camera in SubScene. Try something like this:

@Override
public void start(Stage primaryStage) throws Exception{
    ...
    primaryStage.show();
    scene.setCamera(camera);
}
private void buildScene() {
    ...
    subScene.setLayoutX(100);
    subScene.setLayoutY(100);
    subScene.setCamera(subSceneCamera);
    root.getChildren().addAll(subScene);
}

Upvotes: 0

Related Questions