javafxfxmljavafx-3d

set perspective camera as subscene camera in fxml file


In this code I have a Subscene with 3d features enabled. I add camera and meshviews at runtime, but I want to put the camera in FXML because I don´t need to change it at runtime. How can I do that ?

<SubScene fx:id="viewer3d" depthBuffer="true" fill="#3389de" height="600.0" width="600.0">
    <root>
        <Region />
    </root>
    <antiAliasing>
    <SceneAntialiasing fx:constant="BALANCED" />

Solution

  • If you're okay with the fixedEyeAtCameraZero property being false then all you need is:

    <camera>
      <!-- make sure to import the class (or use fully qualified name) -->
      <PerspectiveCamera/>
    </camera>
    

    Nested in the SubScene element.

    However, if you want the aforementioned property to be true then unfortunately you have to write some code. Said property can only be set during construction, but the constructor does not annotate its parameter with @NamedArg. Meaning the following:

    <camera>
      <PerspectiveCamera fixedEyeAtCameraZero="true"/>
    </camera>
    

    Will fail because the FXMLLoader will not know what to do with the attribute. In this case, there are two options:

    1. Instead of defining the camera in the FXML file, create the camera and set it on the sub-scene in code (e.g., in the controllers initialize method).

      I find this option preferable, but that is just my opinion.

    2. Continue defining the camera in the FXML file and implement a Builder and BuilderFactory.

      Here is an example implementation. Note the example Builder is extremely simple. It is only implemented to allow setting the fixedEyeAtCameraZero property. Trying to set any other property, which would have otherwise worked without registering this builder, will now fail. You can add other getters and setters as needed. Or, perhaps easier, you could have the builder implement Map<String, Object> and appropriately implement the put method.

      Builder

      import javafx.scene.PerspectiveCamera;
      import javafx.util.Builder;
      
      public class PerspectiveCameraBuilder implements Builder<PerspectiveCamera> {
      
        private boolean fixedEyeAtCameraZero;
      
        public boolean isFixedEyeAtCameraZero() {
          return fixedEyeAtCameraZero;
        }
      
        public void setFixedEyeAtCameraZero(boolean fixedEyeAtCameraZero) {
          this.fixedEyeAtCameraZero = fixedEyeAtCameraZero;
        }
      
        @Override
        public PerspectiveCamera build() {
          return new PerspectiveCamera(fixedEyeAtCameraZero);
        }
      }
      

      Builder Factory

      import javafx.scene.PerspectiveCamera;
      import javafx.util.Builder;
      import javafx.util.BuilderFactory;
      
      public class MyBuilderFactory implements BuilderFactory {
      
        @Override
        public Builder<?> getBuilder(Class<?> type) {
          if (type == PerspectiveCamera.class) {
            return new PerspectiveCameraBuilder();
          }
          return null;
        }
      }
      

      Usage

      var loader = new FXMLLoader(...);
      loader.setBuilderFactory(new MyBuilderFactory());
      // ... load FXML file