Search code examples
javafxfxmlscenebuilder

(JavaFX) Trouble Grouping 3D Objects and rotating them around a set pivot


In JavaFX, I want to group a few 3D objects that I created in scenebuilder and use this group as an object to rotate and transform as appropriate. However, grouping an object or translating it causes whatever objects that where grouped, to disappear.

Here is the application class where I access the objects from the 'main' controller class and attempt grouping...

public class MusicGeneratorGUI extends Application { // implements ActionListener

    @FXML
    private void initialize(){
    }

    @Override
    public void start(Stage mainStage) throws IOException {

        //Loading controller
        FXMLLoader loader = new FXMLLoader(getClass().getResource("main.fxml"));
        Parent root = loader.load();
        Main main = loader.getController();

        //Creating application window...
        Scene scene = new Scene(root);
        mainStage.setScene(scene);
        mainStage.setTitle("APPLICATION_NAME");
        mainStage.show();

        //Attempting an object grouping...
        Group iconGroup = new Group();
        iconGroup.getChildren().add(main.icon);
        iconGroup.getChildren().add(main.icon1);
        iconGroup.getChildren().add(main.icon2);
        iconGroup.getChildren().add(main.icon3);
        iconGroup.getChildren().add(main.icon4);
        iconGroup.getChildren().add(main.icon5);

        //Even attempting rotation on a single object causes this disappearing...
        //main.icon.translateXProperty().set(250);
        //main.icon.translateYProperty().set(300);
        //main.icon.translateZProperty().set(-1200);                  
        //Rotate rotate = new Rotate(65, new Point3D(1,0,0));
        //main.icon.getTransforms().add(rotate);
    }

    public static void main(String[] args)
    {
        launch(args);
    }
}

This is how these objects where instantiated in the 'main' controller class...

public class Main implements Initializable {
    @FXML public Sphere icon;
    @FXML public Cylinder icon1;
    @FXML public Cylinder icon2;
    @FXML public Cylinder icon3;
    @FXML public Cylinder icon4;
    @FXML public Cylinder icon5;
}

Here is the FXML file...

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.Insets?>
<?import javafx.scene.PerspectiveCamera?>
<?import javafx.scene.control.Menu?>
<?import javafx.scene.control.MenuBar?>
<?import javafx.scene.control.MenuItem?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.shape.Cylinder?>
<?import javafx.scene.shape.Sphere?>

<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="500.0" prefWidth="500.0" style="-fx-background-color: #feccff;" xmlns="http://javafx.com/javafx/15.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="Main">
   <children>
      <PerspectiveCamera fx:id="camera" layoutX="249.0" layoutY="274.0" />
      <MenuBar maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="70.0" prefWidth="500.0" style="-fx-background-color: #a9e8d2; -fx-use-system-menu-bar: #cba9e8;">
        <menus>
          <Menu mnemonicParsing="false" text="Help">
            <items>
              <MenuItem mnemonicParsing="false" text="Welcome Guide" />
                  <MenuItem mnemonicParsing="false" text="Contact Us" />
            </items>
          </Menu>
          <Menu mnemonicParsing="false" text="Customise">
            <items>
              <MenuItem mnemonicParsing="false" text="Key" />
                  <MenuItem mnemonicParsing="false" text="Time Signature" />
                  <MenuItem mnemonicParsing="false" text="Instruments" />
            </items>
          </Menu>
          <Menu disable="true" mnemonicParsing="false" text="Key">
            <items>
              <MenuItem mnemonicParsing="false" text="About" />
            </items>
          </Menu>
        </menus>
         <opaqueInsets>
            <Insets />
         </opaqueInsets>
         <padding>
            <Insets left="20.0" top="25.0" />
         </padding>
      </MenuBar>
      <Sphere fx:id="icon" layoutX="250.0" layoutY="300.0" radius="150.0" AnchorPane.leftAnchor="100.0" AnchorPane.rightAnchor="100.0" AnchorPane.topAnchor="125.0" />
      <Cylinder fx:id="icon2" height="100.0" layoutX="190.0" layoutY="225.0" radius="10.0" rotate="45.0" />
      <Cylinder fx:id="icon4" height="100.0" layoutX="310.0" layoutY="225.0" radius="10.0" rotate="45.0" />
      <Cylinder fx:id="icon5" height="100" layoutX="250.0" layoutY="345.0" radius="10.0" rotate="90.0" />
      <Cylinder fx:id="icon3" height="100.0" layoutX="310.0" layoutY="225.0" radius="10.0" rotate="-45.0" />
      <Cylinder fx:id="icon1" height="100.0" layoutX="190.0" layoutY="225.0" radius="10.0" rotate="-45.0" />
   </children>
</AnchorPane>


Solution

  • What you have implemented is almost correct, except that you are moving the icons to a Group node and not rendering the Group node any where. Below are the changes to make it work.

    Give an fx:id to the AnchorPane in main.fxml

    <AnchorPane fx:id="root" maxHeight="-Infinity"....
    

    Include the instance variable in Main.java

    @FXML public AnchorPane root;
    

    Include the iconGroup in the root node in MusicGeneratorGUI.java

    Group iconGroup = new Group();
    iconGroup.getChildren().add(main.icon);
    iconGroup.getChildren().add(main.icon1);
    iconGroup.getChildren().add(main.icon2);
    iconGroup.getChildren().add(main.icon3);
    iconGroup.getChildren().add(main.icon4);
    iconGroup.getChildren().add(main.icon5);
    
    main.root.getChildren().add(iconGroup); // You are missing this line
    

    Or alternately you can group them in the fxml itself and deal with Group id, and can get rid of all the above code.

    <Group fx:id="iconGroup">
            <Sphere fx:id="icon" layoutX="250.0" layoutY="300.0" radius="150.0" AnchorPane.leftAnchor="100.0" AnchorPane.rightAnchor="100.0" AnchorPane.topAnchor="125.0" />
            <Cylinder fx:id="icon2" height="100.0" layoutX="190.0" layoutY="225.0" radius="10.0" rotate="45.0" />
            <Cylinder fx:id="icon4" height="100.0" layoutX="310.0" layoutY="225.0" radius="10.0" rotate="45.0" />
            <Cylinder fx:id="icon5" height="100" layoutX="250.0" layoutY="345.0" radius="10.0" rotate="90.0" />
            <Cylinder fx:id="icon3" height="100.0" layoutX="310.0" layoutY="225.0" radius="10.0" rotate="-45.0" />
            <Cylinder fx:id="icon1" height="100.0" layoutX="190.0" layoutY="225.0" radius="10.0" rotate="-45.0" />
        </Group>