Although you've found a solution, I'm posting an answer with the hope of making things a little clearer with regards to why calling toBack()
on your SubScene
works.
Z-Order
Each Parent
, which all layouts inherit from, can have one or more children. If two (or more) children occupy the same space within the Parent
then one will be rendered on top of the other. Which child is drawn over the other is determined by two things:
- The order of the children in the
Parent
's children list.
- (Since JavaFX 9) The value of each child's
viewOrder
property relative to the other children in the same Parent
.
The Z-Order in JavaFX Q&A goes into more detail.
The Problem in Your Code
Your FXML file describes an AnchorPane
which has a StackPane
as its only child. This means that the following:
AnchorPane pane = loader.load();
Gives you an AnchorPane
with a StackPane
in its children list, at the zeroth index. Then you immediately add your SubScene
using:
pane.getChildren().add(subScene);
The add
method you use appends the element to the end of the list. So that means the AnchorPane
's children list's order is:
StackPane
SubScene
Since the SubScene
comes after the StackPane
the former is rendered on top of the latter.
Your Solution
The solution you chose is to call toBack()
on your SubScene
after adding it to the AnchorPane
. Here's the documentation of that method:
Moves this Node
to the back of its sibling nodes in terms of z-order. This is accomplished by moving this Node
to the first position in its parent's content ObservableList
. This function has no effect if this Node
is not part of a group.
In other words, after you call that method the AnchorPane
's children list's order becomes:
SubScene
StackPane
That's why the SubScene
is now rendered underneath the StackPane
, because you changed the order of the children.
Note that the toBack()
method is part of the Node
class, not SubScene
. The latter inherits from the former. I bring this up to point out that the problem you were having is not specific to SubScene
or even mixing 2D and 3D scene graphs together. Both your StackPane
and your SubScene
are part of a 2D Scene
(i.e. no depth buffering) which means their z-order is solely governed by what's discussed above*. The fact that the SubScene
is 3D (i.e. depth buffering enabled) is irrelevant to the problem at hand; that fact only affects the descendants of said SubScene
.
* In a 3D scene the z-coordinate of a node becomes relevant.
Other Solutions
Here are some other approaches that could be used to solve your problem:
Add the SubScene
to the start of the AnchorPane
's children list:
pane.getChildren().add(0, subScene);
If you're using JavaFX 9+, set the viewOrder
property of the SubScene
to something less than that of the AnchorPane
's property (by default, that property's value is 0
):
subScene.setViewOrder(-1);
Define your SubScene
in the FXML file before the StackPane
(note this approach requires the use of an FXML controller):
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.Group?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.SceneAntialiasing?>
<?import javafx.scene.SubScene?>
<?import javafx.scene.text.Font?>
<AnchorPane prefHeight="700.0" prefWidth="1200.0" xmlns="http://javafx.com/javafx/11.0.1"
xmlns:fx="http://javafx.com/fxml/1">
<children>
<!-- Define the SubScene before the StackPane -->
<SubScene fx:id="subScene" width="1280" height="700" depthBuffer="true">
<antiAliasing>
<SceneAntialiasing fx:constant="BALANCED"/>
</antiAliasing>
<!--
Unfortunately, as far as I can tell, you can't set your PerspectiveCamera in FXML because you
want 'fixedEyeAtCameraZero' to be true. That property can only be set during construction but
the constructor with that parameter does not annotate said parameter with @NamedArg, thus the
FXMLLoader can't see it. And the no-arg constructor sets the value to false, not true. This
means you have to inject the SubScene into your controller and add the PerspectiveCamera in
code.
-->
<root>
<!-- Inject the root into the controller in order to add your models to it in code -->
<Group fx:id="root3D"/>
</root>
</SubScene>
<StackPane prefHeight="150.0" prefWidth="200.0">
<children>
<ImageView fitHeight="214.0" fitWidth="169.0" pickOnBounds="true" preserveRatio="true">
<image>
<Image url="@../Sprite/Cards/Small/podium-characters-Poseidon.png"/>
</image>
</ImageView>
<TextField prefHeight="62.0" prefWidth="274.0" text="APOLLO">
<font>
<Font name="Trebuchet MS Bold Italic" size="22.0"/>
</font>
</TextField>
</children>
</StackPane>
</children>
</AnchorPane>