Search code examples
javajavafx3dzoomingjavafx-3d

Zooming moves the camera on the y-axis instead of z-axis


I have a 3D box at the origin. I have a camera as well that is moved to the box so that I get to move the camera around the box instead of rotating the box. However, moving the camera using the mouse is working properly (I think), but the zooming function is not working as intended. For example, if I moved the camera on y-axis, then tried to zoom in, the box will move on the y-axis instead of z-axis (pictures attached below).

Initial Box Initial Box

Box after mouse dragging up and down Box after mouse dragging up and down

Box after zoom-in Box after zoom-in

As you may see the box gets far down as I zoom-in, and it will goes up as I zoom-out. Now, I think I need to fix the camera point when I zoom-in/-out. Any ideas how to do so? Or anything else that might fix the issue.

public Main3D() {
    root = new HBox();
    root.setAlignment(Pos.CENTER);
    xRotate = new Rotate(0, Rotate.X_AXIS);
    yRotate = new Rotate(0, Rotate.Y_AXIS);
    currAngleX = new SimpleDoubleProperty(0);
    currAngleY = new SimpleDoubleProperty(0);
    currAngleZ = new SimpleDoubleProperty(0);
    truckMaterial = new PhongMaterial();
}

private void initiate3dSphereScene() {
    // Create the container/truck
    truck = new Box(Truck.WIDTH, Truck.HEIGHT, Truck.DEPTH);
    truckMaterial.setDiffuseColor(Color.SILVER.deriveColor(.3, .3, .4, .3));
    truck.setMaterial(truckMaterial);
    
    group = new Group();
    group.getChildren().add(truck);

    mainScene = new SubScene(group, SCREEN_WIDTH * .75, SCREEN_HEIGHT, true, SceneAntialiasing.BALANCED);
    mainScene.setFill(Color.SILVER);
        
    // Set up the camera
    camera = new PerspectiveCamera(true);
    camera.getTransforms().addAll(xRotate, yRotate, new Translate(0, 0, -40));
    camera.setNearClip(1);
    camera.setFarClip(100000);
    mainScene.setCamera(camera);
    
    initiateMouseControl();
}

private void initiateMouseControl() {
    xRotate.angleProperty().bind(currAngleX);
    yRotate.angleProperty().bind(currAngleY);
    
    mainScene.setOnMousePressed(mouseEvent -> {
        currX = mouseEvent.getSceneX();
        currY = mouseEvent.getSceneY();
        
        newX = currAngleX.get();
        newY = currAngleY.get();
    });
    
    mainScene.setOnMouseDragged(mouseEvent -> {
        currAngleX.set(newX + (currY - mouseEvent.getSceneY()));
        currAngleY.set(newY - (currX - mouseEvent.getSceneX()));
    });
    
    mainScene.addEventHandler(ScrollEvent.SCROLL, event -> {
        double zoom = event.getDeltaY();
        camera.translateZProperty().set(camera.getTranslateZ() + zoom / 10);
        camera.translateYProperty().set(currAngleY.get());
    });
        
    mainScene.setOnKeyPressed(KeyEvent -> {
        if (KeyEvent.getCode() == KeyCode.C) {
            camera.translateXProperty().set(0);
            camera.translateXProperty().set(0);
            camera.translateZProperty().set(-40);
        }
    });
}

Solution

  • Changing the FieldOfView property will do the trick after initializing a PerspectiveCamera instead of a Camera object. This will give the impression of zooming in/out.

    mainScene.addEventHandler(ScrollEvent.SCROLL, event -> {
        double zoomFactor = 1.05;
        double deltaY = event.getDeltaY();
        if (deltaY < 0) {
            zoomFactor = 2.0 - zoomFactor;
        }
    
        camera.setFieldOfView(camera.getFieldOfView() / zoomFactor);
    });