2

I am writing a program which uses simple 3D graphics and I have made it to a point where I need to drag objects with mouse. I have figured out how to do so here, but unfortunately PickResult is acting weirdly with SubScene. I tested this with the code provided in the answers of the question (link above).

If you put root into Scene, the distance is picked correctly. If used with SubScene, the getIntersectedDistance() method will return some kind of number based on window size - window height, specifically.

If you use the getIntersectedPoint() - the coordinates are correct.

I wrote my own methods of getting the camera to intersected point distance, but the calculations might not be very correct, so the dragging is still not on point.

Why does this happen and how do I bypass it? Thanks in advance

Below is the code I used to test this issue:

    public class Drag extends Application {

    private final Group root = new Group();
    private PerspectiveCamera camera;
    private final double sceneWidth = 800;
    private final double sceneHeight = 600;

    private double mousePosX;
    private double mousePosY;
    private double mouseOldX;
    private double mouseOldY;
    private final Rotate rotateX = new Rotate(-20, Rotate.X_AXIS);
    private final Rotate rotateY = new Rotate(-20, Rotate.Y_AXIS);

    private volatile boolean isPicking = false;
    private Point3D vecIni, vecPos;
    private double distance;
    private Sphere s;

    @Override
    public void start(Stage stage) {
        Box floor = new Box(1500, 10, 1500);
        floor.setMaterial(new PhongMaterial(Color.GRAY));
        floor.setTranslateY(150);
        root.getChildren().add(floor);

        Sphere sphere = new Sphere(40);
        sphere.setMaterial(new PhongMaterial(Color.RED));
        sphere.setTranslateY(-5);
        root.getChildren().add(sphere);

        camera = new PerspectiveCamera(true);
        camera.setVerticalFieldOfView(false);

        camera.setNearClip(0.1);
        camera.setFarClip(100000.0);
        camera.getTransforms().addAll(rotateX, rotateY, new Translate(0, 0, -3000));

        PointLight light = new PointLight(Color.GAINSBORO);
        root.getChildren().add(light);
        root.getChildren().add(new AmbientLight(Color.WHITE));
   /*Uncomment the section below to test subscene*/
   //SubScene subscene = new SubScene(root, sceneWidth, sceneHeight, true, SceneAntialiasing.BALANCED);
   //subscene.setFill(Color.web("3d3d3d"));
   //Pane pane = new Pane();
   //pane.getChildren().add(subscene);
   //Scene scene = new Scene(pane, 800,600);
   //stage.setScene(scene);
   //subscene.setCamera(camera);
   //System.out.println("SubScene built");
   /*Comment the section below to test subscene*/
        Scene scene = new Scene(root, sceneWidth, sceneHeight);
        scene.setFill(Color.web("3d3d3d"));
        stage.setScene(scene);
        scene.setCamera(camera);
        System.out.println("Scene built");
/*                                          */
        scene.setOnMousePressed((MouseEvent me) -> {
            mousePosX = me.getSceneX();
            mousePosY = me.getSceneY();
            PickResult pr = me.getPickResult();
            if (pr != null && pr.getIntersectedNode() != null && pr.getIntersectedNode() instanceof Sphere) {
                System.out.println("getIntersectedDistance: " + distance);
                System.out.println("getIntersectedPoint: " + pr.getIntersectedPoint().toString());
                distance = pr.getIntersectedDistance();

                s = (Sphere) pr.getIntersectedNode();
                isPicking = true;
                vecIni = unProjectDirection(mousePosX, mousePosY, scene.getWidth(), scene.getHeight());
            }
        });
        scene.setOnMouseDragged((MouseEvent me) -> {
            mousePosX = me.getSceneX();
            mousePosY = me.getSceneY();

            if (isPicking) {
                vecPos = unProjectDirection(mousePosX, mousePosY, scene.getWidth(), scene.getHeight());

                Point3D p = vecPos.subtract(vecIni).multiply(distance);
                s.getTransforms().add(new Translate(p.getX(), p.getY(), p.getZ()));
                vecIni = vecPos;
                PickResult pr = me.getPickResult();
            } else {
                rotateX.setAngle(rotateX.getAngle() - (mousePosY - mouseOldY));
                rotateY.setAngle(rotateY.getAngle() + (mousePosX - mouseOldX));
                mouseOldX = mousePosX;
                mouseOldY = mousePosY;
            }
        });
        scene.setOnMouseReleased((MouseEvent me) -> {
            if (isPicking) {
                isPicking = false;
            }
        });
        scene.setOnScroll((ScrollEvent event) -> {
            double zoomFactor = camera.getTranslateZ();
            double deltaY = event.getDeltaY();
            if (deltaY < 0) {
                camera.setTranslateZ(zoomFactor + deltaY * 0.2);
            }
            if (deltaY > 0) {
                camera.setTranslateZ(zoomFactor + deltaY * 0.2);
            }
        });

        stage.setTitle("3D Dragging");

        stage.show();
    }
    /*
     From fx83dfeatures.Camera3D
     http://hg.openjdk.java.net/openjfx/8u-dev/rt/file/5d371a34ddf1/apps/toys/FX8-3DFeatures/src/fx83dfeatures/Camera3D.java
     */
    public Point3D unProjectDirection(double sceneX, double sceneY, double sWidth, double sHeight) {
        double tanHFov = Math.tan(Math.toRadians(camera.getFieldOfView()) * 0.5f);
        Point3D vMouse = new Point3D(tanHFov * (2 * sceneX / sWidth - 1), tanHFov * (2 * sceneY / sWidth - sHeight / sWidth), 1);

        Point3D result = localToSceneDirection(vMouse);
        return result.normalize();
    }

    public Point3D localToScene(Point3D pt) {
        Point3D res = camera.localToParentTransformProperty().get().transform(pt);
        if (camera.getParent() != null) {
            res = camera.getParent().localToSceneTransformProperty().get().transform(res);
        }
        return res;
    }

    public Point3D localToSceneDirection(Point3D dir) {
        Point3D res = localToScene(dir);
        return res.subtract(localToScene(new Point3D(0, 0, 0)));
    }

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

}

Edit: I've figured out a way to get the camera distance to the intersection point without using getIntersectedDistance method. If anyone is interested, feel free to ask.

Community
  • 1
  • 1
  • This is not a trivial change. From scene to subScene there are also transformations that need to be taken into account to get the right result. I suggest you have a look at this class: `com.sun.javafx.scene.SceneUtils`. It has precisely two methods to go back and forward from one to the other. – José Pereda Jun 23 '16 at 16:28
  • Thank you very much for a quick response! I'll check that right away and hopefuly find a solution. – Arūnas Rimkus Jun 23 '16 at 16:35
  • I still do not understand why the getIntersectedDistance method fails to record the distance from the camera, while the getIntersectedPoint works fine with SubScene. – Arūnas Rimkus Jun 23 '16 at 21:04
  • i am interested to know how you figured it out – MitchBroadhead Mar 23 '18 at 14:47
  • @MitchBroadhead for my case it was enough to just track the camera's global x,y,z coordinates and calculate the distance to the pickresult's intersection point. It kind of works, but I am yet to find a better solution since I haven't worked with this project for while now. – Arūnas Rimkus Mar 27 '18 at 07:48

0 Answers0