I'm developing a GUI for a game and I want to mix a 3D SubScene with a 2D Pane in JavaFX. I have a group called root3D that contains all my 3d objects already set correctly, and then I'm creating a Pane using an FXML file set through JavaFX Scene Builder. But nothing shows up and I can only see my 3D Objects.
PerspectiveCamera camera = new PerspectiveCamera(true);
camera.setTranslateZ(-30);
Group root3D = new Group(model1,model2,model3); //various 3d models I imported
SubScene subScene = new SubScene(root3D, 1280, 700, true,SceneAntialiasing.BALANCED);
subScene.setCamera(camera);
FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/file.fxml"));
AnchorPane pane = loader.load();
pane.getChildren().add(subScene);
Scene scene = new Scene(pane);
primaryStage.setScene(scene);
primaryStage.setResizable(false);
primaryStage.show();
FXML file:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.StackPane?>
<?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>
<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>
EDIT: I found out that resizing the subscene, I can see the FXML elements. Apparently they are being "covered" by the subscene. Does anyone know how to lay the FXML elements on top of the subscene and not the opposite like it's happening now?
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.
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:
Parent
's children list . viewOrder
property relative to the other children in the same Parent
. The Z-Order in JavaFX Q&A goes into more detail.
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.
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 thisNode
to the first position in its parent's contentObservableList
. This function has no effect if thisNode
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
(ie no depth buffering) which means their z-order is solely governed by what's discussed above * . The fact that the SubScene
is 3D (ie 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.
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>
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.