2

How Can I add 3D surface to the view.fxml, there is not "thing" on the Scene Builder panel like surface. My scene builder hierarchy looks like :

Hierarchy

And ss of app - like we can see got something on the left top corner, the surface should be on the middle. enter image description here

I would like to add first just some samples of 3D surface : My controller code :

    package sample.packet3D;

import org.fxyz.cameras.CameraTransformer;

import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.fxml.FXML;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.PointLight;
import javafx.scene.layout.AnchorPane;


public class Window3DController {

    @FXML
    private AnchorPane anchorPane;
    @FXML
    private Group group;

    private Window3DBuilder window3dBuilder;
    private PerspectiveCamera perspectiveCamera;



    @FXML
    public void initialize() {
        perspectiveCamera = new PerspectiveCamera(true);

        window3dBuilder = new Window3DBuilder( group, perspectiveCamera );
        window3dBuilder.createScene();



        group.sceneProperty().addListener(new InvalidationListener() {

            @Override
            public void invalidated(Observable observable) {
                group.getScene().setCamera(perspectiveCamera);
                group.sceneProperty().removeListener(this);
            }
        });
    }
}

Logic class :

   package sample.packet3D;


import org.fxyz.cameras.CameraTransformer;
import org.fxyz.shapes.primitives.SurfacePlotMesh;

import javafx.geometry.Point3D;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.PointLight;
import javafx.scene.SceneAntialiasing;
import javafx.scene.SubScene;
import javafx.scene.layout.AnchorPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.CullFace;
import javafx.scene.shape.TriangleMesh;
import javafx.scene.transform.Rotate;

public class Window3DBuilder {

    private Group group;
    private SurfacePlotMesh surface;
    private PerspectiveCamera perspectiveCamera;
    private CameraTransformer cameraTransformer;
    private PointLight light;

    public Window3DBuilder( Group group, PerspectiveCamera perspectiveCamera ) {
        this.group = group;
        this.perspectiveCamera = perspectiveCamera;
        cameraTransformer = new CameraTransformer();
    }

    public void createScene() {

        createSurface();
        createLight(); 
        group.getChildren().addAll(surface);

        cameraTransformer.setTranslate(0, 0, 0);
        cameraTransformer.getChildren().addAll(perspectiveCamera);

        perspectiveCamera.setNearClip(0.1);
        perspectiveCamera.setFarClip(100000.0);
        perspectiveCamera.setTranslateX((group.getBoundsInLocal().getMaxX() + group.getBoundsInLocal().getMinX()) / 2d);
        perspectiveCamera.setTranslateY((group.getBoundsInLocal().getMaxY() + group.getBoundsInLocal().getMinY()) / 2d);
        double max = Math.max(group.getBoundsInLocal().getWidth(), group.getBoundsInLocal().getHeight());
        perspectiveCamera.setTranslateZ(-2 * max);

    }

    public void createLight() {
        light = new PointLight(Color.WHITE);
        cameraTransformer.getChildren().add(light);
        light.setTranslateX(perspectiveCamera.getTranslateX());
        light.setTranslateY(perspectiveCamera.getTranslateY());
        light.setTranslateZ(perspectiveCamera.getTranslateZ());
    }

    private void createSurface() {
        surface = new SurfacePlotMesh(
                p-> Math.sin(p.magnitude() + 1e-10) / (p.magnitude() + 1e-10),
                20, 20, 100, 100, 4);
        surface.setCullFace(CullFace.NONE);
        surface.setTextureModeVertices3D(1530, p -> p.magnitude());
        surface.getTransforms().addAll(new Rotate(200, Rotate.X_AXIS), new Rotate(-20, Rotate.Y_AXIS));
    }



}

And view :

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

<?import javafx.scene.effect.*?>
<?import javafx.scene.canvas.*?>
<?import javafx.scene.*?>
<?import javafx.scene.shape.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>

<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.packet3D.Window3DController">
   <children>
      <Group fx:id="group">
         <effect>
            <Lighting>
               <bumpInput>
                  <Shadow />
               </bumpInput>
               <light>
                  <Light.Distant />
               </light>
            </Lighting>
         </effect>
      </Group>
      <PerspectiveCamera fx:id="perspectiveCamera" visible="false" />
   </children>
</AnchorPane>

What Am I doing wrong ? Could someone help me ? Also this is one of the window, to which I am entering by pressing the button.

@FXML
    public void moveTo3DScene(ActionEvent event) throws IOException {
        Stage stage3D = (Stage) ((Node) event.getSource()).getScene().getWindow();
        Parent parent3D = FXMLLoader.load(getClass().getResource("packet3D/Window3DSceneView.fxml"));
        stage3D.setTitle("Animation 3D");
        stage3D.setScene(new Scene(parent3D, 1200, 800));
        stage3D.show();

    }

enter image description here

yerpy
  • 1,366
  • 3
  • 17
  • 44

2 Answers2

2

You have a problem with PerspectiveCamera. It has a boolean parameter called fixedEyeAtCameraZero that by default is false, and a very small surface is shown at the top left corner of your scene.

We need to set it to true, so:

If fixedEyeAtCameraZero is true, the eye position is fixed at (0, 0, 0) in the local coordinates of the camera

But unfortunately you can't set the parameter, there is no setFixedEyeAtCameraZero() method. The only way to change it is with the camera constructor.

This means that you have to remove the PerspectiveCamera from the FXML file, and add it by code on the controller

public class Window3DController {

    @FXML
    private AnchorPane anchorPane;
    @FXML
    private Group group;

    private Window3DBuilder window3dBuilder;
    private PerspectiveCamera perspectiveCamera;

    @FXML
    public void initialize() {
        perspectiveCamera = new PerspectiveCamera(true);

        window3dBuilder = new Window3DBuilder(group, perspectiveCamera);
        window3dBuilder.createScene();

        group.sceneProperty().addListener(new InvalidationListener() {
            @Override
            public void invalidated(Observable observable) {
                group.getScene().setCamera(perspectiveCamera);
                group.sceneProperty().removeListener(this);
            }
        });
    } 

}

One last step: you need to set some parameters to the camera, basically its z coordinate based on the size of the mesh:

public void createScene() {
    createSurface();
    group.getChildren().addAll(surface);

    perspectiveCamera.setNearClip(0.1);
    perspectiveCamera.setFarClip(100000.0);
    perspectiveCamera.setTranslateX((group.getBoundsInLocal().getMaxX() + group.getBoundsInLocal().getMinX()) / 2d);
    perspectiveCamera.setTranslateY((group.getBoundsInLocal().getMaxY() + group.getBoundsInLocal().getMinY()) / 2d);
    double max = Math.max(group.getBoundsInLocal().getWidth(), group.getBoundsInLocal().getHeight());
    perspectiveCamera.setTranslateZ(-2 * max);
}

This will show your surface, but not as you will expect: the effects you are applying are intended for 2D:

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

<?import javafx.scene.Group?>
<?import javafx.scene.layout.AnchorPane?>

<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.65" xmlns:fx="http://javafx.com/fxml/1" fx:controller="Window3DController">
   <children>
      <Group fx:id="group" />
   </children>
</AnchorPane>

Remove those effects, and add them by code using PointLight:

public class Window3DBuilder {

    private final Group group;
    private SurfacePlotMesh surface;
    private final CameraTransformer cameraTransformer;
    private final PerspectiveCamera perspectiveCamera;
    private PointLight light;

    public Window3DBuilder( Group group, PerspectiveCamera perspectiveCamera ) {
        this.group = group;
        this.perspectiveCamera = perspectiveCamera;
        cameraTransformer = new CameraTransformer();
    }

    public void createScene() {
        createSurface();
        group.getChildren().addAll(surface, cameraTransformer);

        cameraTransformer.setTranslate(0, 0, 0);
        cameraTransformer.getChildren().addAll(perspectiveCamera);

        perspectiveCamera.setNearClip(0.1);
        perspectiveCamera.setFarClip(100000.0);
        perspectiveCamera.setTranslateX((group.getBoundsInLocal().getMaxX() + group.getBoundsInLocal().getMinX()) / 2d);
        perspectiveCamera.setTranslateY((group.getBoundsInLocal().getMaxY() + group.getBoundsInLocal().getMinY()) / 2d);
        double max = Math.max(group.getBoundsInLocal().getWidth(), group.getBoundsInLocal().getHeight());
        perspectiveCamera.setTranslateZ(-2 * max);
        createLight();
    }

    public void createLight() {
        light = new PointLight(Color.WHITE);
        cameraTransformer.getChildren().add(light);
        light.setTranslateX(perspectiveCamera.getTranslateX());
        light.setTranslateY(perspectiveCamera.getTranslateY());
        light.setTranslateZ(perspectiveCamera.getTranslateZ() / 10);
    }

    private void createSurface() {
        surface = new SurfacePlotMesh(
                p-> Math.sin(p.magnitude() + 1e-10) / (p.magnitude() + 1e-10),
                20, 20, 100, 100, 4);
        surface.setCullFace(CullFace.NONE);
        surface.setTextureModeVertices3D(1530, p -> p.magnitude());
        surface.getTransforms().addAll(new Rotate(200, Rotate.X_AXIS), new Rotate(-20, Rotate.Y_AXIS));
    }

}

Surface

José Pereda
  • 44,311
  • 7
  • 104
  • 132
  • I created the method public void createLight() { light = new PointLight(Color.WHITE); cameraTransformer.getChildren().add(light); light.setTranslateX(perspectiveCamera.getTranslateX()); light.setTranslateY(perspectiveCamera.getTranslateY()); light.setTranslateZ(perspectiveCamera.getTranslateZ()); } and call inside the crateScene() but it doesnt work, what else do i need ? – yerpy May 15 '16 at 19:15
  • Do you have a `cameraTransformer`? Otherwise, `light` is a node, you have to add it to the scenegraph, so add it to the group – José Pereda May 15 '16 at 19:23
  • Yes, I do have : createSurface(); createLight(); group.getChildren().addAll(surface, light); – yerpy May 15 '16 at 19:27
  • Add the `cameraTransformer` to the group, instead the `light` node. – José Pereda May 15 '16 at 19:42
  • If I add it in the method `createLight()` where i have it, still not working, only in this place i am adding the `light` node to the `cameraTransformer`. Should not it be like first light node added to the cameraTransformer then `cameraTransformer` to the group ? – yerpy May 15 '16 at 19:51
  • You need to call `createLight()` after you set the camera, or bind the light translate properties to the camera ones. – José Pereda May 15 '16 at 19:53
  • I did it : `light = window3dBuilder.createLight(); group.getChildren().add(light);`in the `Window3DControlller` after the `group.sceneProperty()...`but it didint change anything ;/ – yerpy May 15 '16 at 19:59
  • Have same code, but result different, all the time i have this, added ss @up – yerpy May 15 '16 at 20:07
  • Did you delete the effects from the FXML file as I told you? – José Pereda May 15 '16 at 20:08
  • I found the problem, all the time one stuff didint save properly, Thank you for your help! – yerpy May 15 '16 at 20:23
1

I wanted to keep the camera in the .fxml file. I created a custom control based on PerspectiveCamera.

Start with a very simple class...

import javafx.scene.PerspectiveCamera;


public class PerspectiveCamera3D extends PerspectiveCamera {

// force 3D
public PerspectiveCamera3D() {
    super(true);
}

// toss the parameter, force 3D
public PerspectiveCamera3D(final boolean fixedEyeAtCameraZero) {
    this();
}
}

Export to a .jar file. Launch Scene Builder and open the .fxml file for where you want the camera to be.

Open the 'gear' menu on the Library header. Then Import FXML/Jar. Import your newly created .jar file. A dialog will pop up with your control listed. Once confirmed, the control will appear in the custom menu. Your control is now ready for use, just like any other one.

The 'Fixed Eye...' check box will still be read-only, but it will be checked. All of the other properties can be set as desired. To set the fx:id just add the following into your controller code...

@FXML
public PerspectiveCamera3D cambot;

Here is a bit more detailed of an example ... https://rterp.wordpress.com/2014/07/28/adding-custom-javafx-component-to-scene-builder-2-0-part-2/

The only problem I've had doing it this way, is when launching Scene Builder by clicking on the .fxml file in Eclipse causes an exception (I believe it is a local problem on my computer due to the way I'm launching things and their working directories). It works fine if I open Scene Builder, and then open the .fxml file from the Scene Builder File menu.

GeekToast
  • 11
  • 3