Here is my code. You can copy-paste and follow what I write bellow to see the problem yourself.
public class MyApp extends Application {
@Override
public void start(Stage stage) throws Exception {
Scene scene = new Scene(new MyView(), 100, 150);
stage.setScene(scene);
stage.show();
}
private class MyView extends BorderPane {
MyView() {
GridPane board = new GridPane();
int size = 3;
for (int i = 0; i < size*size; i++) {
BorderPane pane = new BorderPane();
pane.setMinSize(30, 30);
pane.setBackground(new Background(new BackgroundFill(Color.RED, null, null)));
pane.setBorder(new Border(new BorderStroke(null, BorderStrokeStyle.SOLID,
null, null, null)));
pane.setOnMousePressed(e -> {
PickResult pick = e.getPickResult();
Pane selectedNode = (Pane) pick.getIntersectedNode();
selectedNode.setBackground(new Background(new BackgroundFill(Color.GREEN, null, null)));
});
board.add(pane, i / size, i % size);
}
Box box = new Box(20d, 20d, 20d);
BorderPane boardPane = new BorderPane(box, null, null, board, null);
Group root = new Group(boardPane);
SubScene scene = new SubScene(root, USE_PREF_SIZE, USE_PREF_SIZE, true, SceneAntialiasing.BALANCED);
scene.widthProperty().bind(widthProperty());
scene.heightProperty().bind(heightProperty());
setCenter(scene);
}
}
public static void main(String[] args) throws Exception {
launch(args);
}
}
I create a subscene with a grid of squares. When i press on a square I want its background to change color. This works in 2 situations:
boardPane
or both. But if i both add the box and set a depth buffer, the squares don't receive the event. instead the boardPane
receives it. i guess it's something to do with 2D nodes in a 3D scene.
I tried setting combinations of these methods :setPickOnBounds
, setDepthTest
, setMouseTransparent
but nothing worked.
What's the solution?
Once you have a 3D scene, there aren't really such things as 2D nodes, everything in the scene graph has an x, y and z co-ordinate; even things that were previously treated as 2D nodes when the depth buffer was set to false.
When you place a box in your root border pane, that root border pane assumes the 3D co-ordinates of the box. For picking purposes, the box you have defined is represented in 3D space from z co-ordinates -10 to 10, so the root border pane is defined as -10 for picking purposes. The border panes that you place inside the root border pane have no z co-ordinate defined for them, so they end up at a z co-ordinate of 0, which, from the viewers perspective is behind the root border pane.
So, the root border pane is receiving clicks, but because it is now on a different z plane than the rest of its contents, the other 2D square contents you have defined do not receive clicks. One could argue that the root border pane is not rendered at all as it has no color, so it should be treated as transparent and the clicks just go through to the child nodes, but it seems that is just not how the 3D picking algorithm for JavaFX works.
For your example, to get everything in the same Z plane and picking working correctly, add the following line inside your for loop: pane.setTranslateZ(-10);
.
Note: I debugged this by adding the following line to your source code (which reported to me the target of the mouse clicks and the x,y,z pick result co-ordinate for each click):
root.setOnMouseClicked(System.out::println);
My advice is to avoid using layout panes that are designed for 2D purposes (e.g. border panes) to attempt laying out elements in 3D space. The JavaFX layout panes are really only meant for 2D layout. To handle positioning in 3D space you should manage co-ordinates yourself, by just using Groups rather than anything that derives from Pane. At the very least don't try adding 3D elements into 2D layout panes as the results can be confusing (as you have discovered).
You can further separate 2D and 3D items by placing them in different sub scenes (that is really what the intention of the sub scene notion in JavaFX is I think). An example of an application with multiple sub scenes is shown in the following answer: How to create custom 3d model in JavaFX 8?