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:
From the angle that doesn't work properly it looks like this:
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.
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.