JavaFX 3d sous-scène en 2d Scène
Je développe une interface graphique pour un jeu et je veux mélanger un sous-scène 3D avec un volet 2D dans JavaFX. J'ai un groupe appelé root3D qui contient tous mes objets 3d déjà définis correctement, puis je crée un volet à l'aide d'un fichier FXML défini via JavaFX Scene Builder. Mais rien n'apparaît et je ne peux voir que mes objets 3D.
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();
Fichier FXML:
<?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: J'ai découvert que le redimensionnement du sous-domaine, je peux voir les éléments FXML. Apparemment ils sont "couverts" par la subscene. Est-ce que quelqu'un sait comment poser les éléments FXML au-dessus du sous-domaine et non le contraire comme cela se passe maintenant?
2 answers
Bien que vous ayez trouvé une solution, je poste une réponse dans l'espoir de rendre les choses un peu plus claires en ce qui concerne pourquoi appeler toBack()
sur vos SubScene
fonctionne.
Z-Ordre
Chaque Parent
, dont toutes les mises en page héritent, peut avoir un ou plusieurs enfants. Si deux (ou plus) enfants occupent le même espace dans le Parent
alors l'un sera rendu sur l'autre. Quel enfant est tiré sur l'autre est déterminé par deux choses:
- L'ordre de les enfants dans le
Parent
's liste des enfants. - (Depuis JavaFX 9) La valeur de chaque enfant
viewOrder
propriété par rapport aux autres enfants dans la mêmeParent
.
Le Z-Order dans JavaFX Q&A va plus en détail.
Le problème dans votre code
Votre fichier FXML décrit un AnchorPane
qui a un StackPane
comme seul enfant. Cela signifie que ce qui suit:
AnchorPane pane = loader.load();
Vous Donne un AnchorPane
avec StackPane
dans sa liste d'enfants, à l'indice zéro. Ensuite, vous ajoutez immédiatement votre SubScene
en utilisant:
pane.getChildren().add(subScene);
Le add
la méthode que vous utilisez ajoute l'élément à la fin de la liste. Cela signifie donc que l'ordre de la liste des enfants de AnchorPane
est:
StackPane
SubScene
Puisque le SubScene
vient après le StackPane
le premier est rendu au-dessus du second.
Votre solution
La solution que vous avez choisie est d'appeler toBack()
sur votre SubScene
après l'avoir ajouté au AnchorPane
. Voici la documentation de cette méthode:
Déplace ce
Node
à l'arrière de ses nœuds frères en termes d'ordre z. Ceci est accompli en déplaçant ceNode
à la première position dans le contenu de son parentObservableList
. Cette fonction n'a aucun effet si ceNode
ne fait pas partie d'un groupe.
En d'autres termes, après avoir appelé cette méthode, l'ordre de la liste des enfants de AnchorPane
devient:
SubScene
StackPane
C'est pourquoi le SubScene
est maintenant rendu sous le StackPane
, parce que vous avez changé l'ordre des enfants.
Notez que la méthode toBack()
fait partie de la classe Node
, pas SubScene
. Ce dernier hérite du premier. J'apporte cela pour souligner que le problème que vous aviez n'est pas spécifique à SubScene
ou même mélanger des graphiques de scène 2D et 3D ensemble. Votre StackPane
et votre SubScene
font partie d'un Scene
2D (c'est-à-dire pas de tampon de profondeur), ce qui signifie que leur ordre z est uniquement régi par ce qui est discuté ci-dessus*. Le fait que le SubScene
soit en 3D (c'est-à-dire que la mise en mémoire tampon de profondeur est activée) n'est pas pertinent pour le problème en question; ce fait n'affecte que les descendants dudit SubScene
.
* Dans une scène 3D à la coordonnée z d'un nœud devient pertinente.
Autres solutions
Voici quelques autres approches qui pourraient être utilisées pour résoudre votre problème:
-
Ajouter le
SubScene
au début des enfants duAnchorPane
liste:pane.getChildren().add(0, subScene);
-
Si vous utilisez JavaFX 9+, définissez la propriété
viewOrder
du {[6] } sur quelque chose de moins que celle de la propriétéAnchorPane
(par défaut, la valeur de cette propriété est0
):subScene.setViewOrder(-1);
-
Définir votre
SubScene
dans le fichier FXML avant leStackPane
(notez que cette approche nécessite l'utilisation de un contrôleur FXML):<?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>
Comme @Slaw l'a indirectement souligné, ce dont j'avais besoin était de définir le sous-domaine.toBack () pour définir un ordre Z correct dans ma scène. Merci beaucoup! En lisant sur les sous-scènes, je n'ai rien trouvé à ce sujet, donc j'espère que cela pourra aider certains débutants 3d à l'avenir.