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.