简体   繁体   中英

JavaFX 3d SubScene in 2d Scene

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>

没有 FXML 布局的迹象

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? 子场景涵盖了我创建的 FXML 布局

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:

  1. The order of the children in the Parent 's children list .
  2. (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:

  1. StackPane
  2. 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:

  1. SubScene
  2. 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.

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>

As @Slaw indirectly pointed out, what I needed was to set Subscene.toBack() to set a correct Z-order in my scene. Thank you very much. While reading about Subscenes I didn't find anything about it so I hope this can help some 3d beginner in the future. 最后结果

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM