javajavafxdepth-buffer

JavaFX z-buffer issues


I'm filling a container with a lot of small boxes and the problem is that you can see trough them from some angles. I've already enabled the depth buffer, but that doesn't work.

The code that handles this part is split into 3. setupUIPreElements sets everything up, setupUIElements adds the boxes and setupUIPostElements adds the container and some camera stuff.

public static void setupUIPreElements(Stage stage){
    //Setup grids, groups, scenes, camera and such so that the scene is made from scratch
    topGrid = new GridPane();
    twoDGroup = new Group();
    threeDGroup = new SmartGroup();
    root = new HBox();
    mainScene = new Scene(root, SCREEN_WIDTH, SCREEN_HEIGHT, true, SceneAntialiasing.BALANCED);
    twoD = new SubScene(twoDGroup, SCREEN_WIDTH*.2, SCREEN_HEIGHT);
    threeD = new SubScene(threeDGroup, SCREEN_WIDTH*.8, SCREEN_HEIGHT);
    anchorAngleX = 0;
    anchorAngleY = 0;
    angleX = new SimpleDoubleProperty(0);
    angleY = new SimpleDoubleProperty(0);
    camera = new PerspectiveCamera();
    pins = new ProgressIndicator[1];
    pin = pins[0] = new ProgressIndicator();
    parcels = new ArrayList<UIParcel>();

    //add subscenes to scene
    root.getChildren().addAll(twoD, threeD);
    root.setSpacing(10);
    root.setPadding(new Insets(20, 20, 20, 20));

    /*START Setup top menu*/
    //Setup grid
    topGrid.setHgap(10);
    topGrid.setVgap(10);

    //Setup items
    //Add scoring label
    scoringLabel = new Label("Score: " + Wrapper.score);
    startButton = new Button("Start");

    modeSelection = new ChoiceBox(FXCollections.observableArrayList(
            "Parcels", "Pentominoes"
    ));

    modeSelection.setValue("");

    //Parcel selection UI
    ParcelAAmountLabel = new Label("Amount of parcel A: ");
    ParcelBAmountLabel = new Label("Amount of parcel B: ");
    ParcelCAmountLabel = new Label("Amount of parcel C: ");
    ParcelAAmountTextField = new TextField();
    ParcelBAmountTextField = new TextField();
    ParcelCAmountTextField = new TextField();

    ParcelAValueLabel = new Label("Value of parcel A: ");
    ParcelBValueLabel = new Label("Value of parcel B: ");
    ParcelCValueLabel = new Label("Value of parcel C: ");
    ParcelAValueTextField = new TextField();
    ParcelBValueTextField = new TextField();
    ParcelCValueTextField = new TextField();

    //Pentominoe selection UI
    LPentominoAmountLabel = new Label("Amount of L pentominoes: ");
    PPentominoAmountLabel = new Label("Amount of P pentominoes: ");
    TPentominoAmountLabel = new Label("Amount of T pentominoes: ");
    LPentominoAmountTextField = new TextField();
    PPentominoAmountTextField = new TextField();
    TPentominoAmountTextField = new TextField();

    LPentominoValueLabel = new Label("Value of L pentominoes: ");
    PPentominoValueLabel = new Label("Value of P pentominoes: ");
    TPentominoValueLabel = new Label("Value of T pentominoes: ");
    LPentominoValueTextField = new TextField();
    PPentominoValueTextField = new TextField();
    TPentominoValueTextField = new TextField();

    //-1 will make it display an animated disk, set to 1 to show that it's done
    //pin is the progress indicator
    pin.setProgress(-1);

    topGrid.add(scoringLabel, 0, 0);
    topGrid.add(modeSelection, 0, 1);
    topGrid.add(startButton, 0, 8);
    twoDGroup.getChildren().add(topGrid);
    /*END*/

    //Set materials
    container_material.setDiffuseColor(CONTAINER_COLOR);
    edge_material.setDiffuseColor(EDGE_COLOR);
}

public static void setupUIElements(Stage stage, int[][][] resultBoxesArray){
    //TODO check if I can assume the IDs to be either 1, 2 or 3 if filled in or 0 if not
    int colorStart = 0;
    int colorEnd = 0;

    //give every filled in field a box representation and keep color in mind
    //create all the boxes
    for(int x=0; x<resultBoxesArray.length; x++){
        for(int y=0; y<resultBoxesArray[x].length; y++){
            for(int z=0; z<resultBoxesArray[x][y].length; z++){
                int currentValue = resultBoxesArray[x][y][z];

                //if this field is filled
                if(currentValue!=0){
                    //update color range
                    if(currentValue==1){
                        colorStart = 0;
                        colorEnd = 70;
                    } else if (currentValue==2){
                        colorStart = 85;
                        colorEnd = 155;
                    } else {
                        colorStart = 170;
                        colorEnd = 255;
                    }

                    //50 is used because that is the size that is given for each cell in the array
                    UIParcel cellBox = new UIParcel(x*50, y*50, z*50, 50, 50, 50, colorStart, colorEnd);
                    parcels.add(cellBox);
                }
            }
        }
    }

    //show them
    threeDGroup.getChildren().addAll(parcels);
}

public static void setupUIPostElements(Stage stage){
    //Create container (note: Has to be created after adding all the other objects in order to use transparency (I know, javaFX can be crappy))
    Box container = new Box(Wrapper.CONTAINER_WIDTH, Wrapper.CONTAINER_HEIGHT, Wrapper.CONTAINER_DEPTH);
    container.setTranslateX(Wrapper.CONTAINER_WIDTH/2);
    container.setTranslateY(Wrapper.CONTAINER_HEIGHT/2);
    container.setTranslateZ(Wrapper.CONTAINER_DEPTH/2);
    container.setMaterial(container_material);
    threeDGroup.getChildren().add(container);

    //Setup camera (so that you can have the container at the origin and can still see it well
    //The +threeDOffsetLeft comes from the compensation for the 2D subscene on the left
    camera.setTranslateX(-SCREEN_WIDTH/2+Wrapper.CONTAINER_WIDTH/2+threeDOffsetLeft);
    camera.setTranslateY(-SCREEN_HEIGHT/2+Wrapper.CONTAINER_HEIGHT/2);
    camera.setTranslateZ(-Wrapper.CONTAINER_DEPTH/0.5);

    //Setup mouse rotation
    initMouseControl(threeDGroup, mainScene, stage);

    //Set eventListener for mode selection
    modeSelection.getSelectionModel().selectedItemProperty().addListener((v, oldValue, newValue) -> {
        //check what mode was selected and show the corresponding options
        if(newValue.equals("Parcels")){
            //remove other option
            if(oldValue.equals("Pentominoes")){
                topGrid.getChildren().removeAll(LPentominoAmountLabel, PPentominoAmountLabel, TPentominoAmountLabel, LPentominoAmountTextField, PPentominoAmountTextField, TPentominoAmountTextField, LPentominoValueLabel, PPentominoValueLabel, TPentominoValueLabel, LPentominoValueTextField, PPentominoValueTextField, TPentominoValueTextField);
            }

            //add labels
            topGrid.add(ParcelAAmountLabel, 0, 2);
            topGrid.add(ParcelBAmountLabel, 0, 4);
            topGrid.add(ParcelCAmountLabel, 0, 6);

            topGrid.add(ParcelAValueLabel, 0, 3);
            topGrid.add(ParcelBValueLabel, 0, 5);
            topGrid.add(ParcelCValueLabel, 0, 7);

            //add text fields
            topGrid.add(ParcelAAmountTextField, 1, 2);
            topGrid.add(ParcelBAmountTextField, 1, 4);
            topGrid.add(ParcelCAmountTextField, 1, 6);

            topGrid.add(ParcelAValueTextField, 1, 3);
            topGrid.add(ParcelBValueTextField, 1, 5);
            topGrid.add(ParcelCValueTextField, 1, 7);

        } else if (newValue.equals("Pentominoes")){
            //remove other option
            if(oldValue.equals("Parcels")){
                topGrid.getChildren().removeAll(ParcelAAmountLabel, ParcelBAmountLabel, ParcelCAmountLabel, ParcelAAmountTextField, ParcelBAmountTextField, ParcelCAmountTextField, ParcelAValueLabel, ParcelBValueLabel, ParcelCValueLabel, ParcelAValueTextField, ParcelBValueTextField, ParcelCValueTextField);
            }

            //add labels
            topGrid.add(LPentominoAmountLabel, 0, 2);
            topGrid.add(PPentominoAmountLabel, 0, 4);
            topGrid.add(TPentominoAmountLabel, 0, 6);

            topGrid.add(LPentominoValueLabel, 0, 3);
            topGrid.add(PPentominoValueLabel, 0, 5);
            topGrid.add(TPentominoValueLabel, 0, 7);

            //add text fields
            topGrid.add(LPentominoAmountTextField, 1, 2);
            topGrid.add(PPentominoAmountTextField, 1, 4);
            topGrid.add(TPentominoAmountTextField, 1, 6);

            topGrid.add(LPentominoValueTextField, 1, 3);
            topGrid.add(PPentominoValueTextField, 1, 5);
            topGrid.add(TPentominoValueTextField, 1, 7);
        }
    });

    //Set evenListener for start button
    startButton.addEventHandler(MouseEvent.MOUSE_CLICKED, event-> {
        //Show loading circle (that was created at the start)
        topGrid.add(pin, 0, 9);

        //TODO use values from the textFields as input

        //TODO start calculations

        //TODO remove after testing
        test.giveInput();

    });

    threeD.setCamera(camera);
    stage.setTitle("Filling 3D objects");
    threeD.setFill(BACKGROUND_COLOR);
    stage.setScene(mainScene);
    stage.show();
}

From the angle that works as desired is looks like this: enter image description here

From the angle that doesn't work properly it looks like this: enter image description here

Note that the boxes are added as UIParcel, this is just a class that extends the regular Box with some extra info, it doesn't effect any 3D stuff.


Solution

  • The solution was to also enable depth buffering for the SubScene that contained the 3D elements, it doesn't just follow the settings from the mainScene.

    threeD = new SubScene(threeDGroup, SCREEN_WIDTH*.8, SCREEN_HEIGHT);
    

    becomes

    threeD = new SubScene(threeDGroup, SCREEN_WIDTH*.8, SCREEN_HEIGHT, true, SceneAntialiasing.BALANCED);
    

    Setting the SceneAntialiasing is also required by the SubScene constructor.