问题
I'm using JavaFX to move 3D cubes around by mouse drag. The cube should stay on the plane spanned by x and z axis. My solution works fairly well, however if I move the cube too fast with my mouse or when it encounters an object with a certain depth (y-Axis), it is assumed, that the mouse is moving on the y-Axis and the cube starts jumping forward or backwards. Is there a way to restrict the mouse to the xz-plane? A more complicated solution would be projecting the y length back to the xz-plane, but I got no clue how. I looked at JavaFX Moving 3D Objects, but couldn't adapt it to my case.
My code so far:
private volatile double relMousePosX;
private volatile double relMousePosZ;
public void enableDragMove(PaneBox paneBox) {
Group paneBoxGroup = paneBox.get();
paneBoxGroup.setOnMousePressed((MouseEvent me) -> {
if(isSelected(paneBox) && MouseButton.PRIMARY.equals(me.getButton())) {
relMousePosX = me.getX();
relMousePosZ = me.getZ();
}
});
paneBoxGroup.setOnMouseDragged((MouseEvent me) -> {
if(paneBoxGroup.focusedProperty().get() && MouseButton.PRIMARY.equals(me.getButton())) {
setDragInProgress(paneBox, true);
System.out.println(me.getY()); // should stay small value, but jumps to higher values at times, creating the problem.
paneBoxGroup.setCursor(Cursor.MOVE);
paneBox.setTranslateX(paneBox.getTranslateX() + (me.getX() - relMousePosX));
paneBox.setTranslateZ(paneBox.getTranslateZ() + (me.getZ() - relMousePosZ));
}
});
paneBoxGroup.setOnMouseReleased((MouseEvent me) -> {
if(paneBoxGroup.focusedProperty().get() && MouseButton.PRIMARY.equals(me.getButton())) {
setDragInProgress(paneBox, false);
paneBoxGroup.setCursor(Cursor.DEFAULT);
}
});
}
回答1:
As you have mentioned, there two possible approachs for 3D dragging:
- Pure drag & drop, like you propose
- unprojecting directions, like in this question.
For drag and drop events, we can detect a drag event on the 3D shape, when the event starts and finishes.
The trick here is, instead of listening to the in between dragging events on the shape, listen to drag over events on a possible target.
By defining a target, you can restrict easily the shape movements, since you will only update its position when you are dragging it only over this target.
So if you want to restrict movements on a plane, you can use a Rectangle
as the target.
This snippet shows how this approach works:
@Override
public void start(Stage stage) {
final PerspectiveCamera cam = new PerspectiveCamera();
cam.setFieldOfView(20);
cam.setFarClip(10000);
cam.setNearClip(0.01);
cam.getTransforms().addAll(new Rotate(60,Rotate.X_AXIS),new Translate(-200,-200,300));
final Group root = new Group();
final Box floor = new Box(500, 500, 1);
floor.setTranslateX(200);
floor.setTranslateY(200);
floor.setTranslateZ(50);
floor.setMaterial(new PhongMaterial(Color.YELLOW));
root.getChildren().add(floor);
final Box box = new Box(50, 50, 50);
box.setMaterial(new PhongMaterial(Color.RED));
root.getChildren().add(box);
final Rectangle rectangle = new Rectangle(400, 400, Color.TRANSPARENT);
rectangle.setMouseTransparent(true);
rectangle.setDepthTest(DepthTest.DISABLE);
root.getChildren().add(rectangle);
// D&D starts
box.setOnDragDetected((MouseEvent event)-> {
box.setMouseTransparent(true);
rectangle.setMouseTransparent(false);
box.setCursor(Cursor.MOVE);
box.startFullDrag();
});
// D&D ends
box.setOnMouseReleased((MouseEvent event)-> {
box.setMouseTransparent(false);
rectangle.setMouseTransparent(true);
box.setCursor(Cursor.DEFAULT);
});
// While D&D, only confined to the rectangle
rectangle.setOnMouseDragOver((MouseDragEvent event)-> {
Point3D coords = event.getPickResult().getIntersectedPoint();
coords = rectangle.localToParent(coords);
box.setTranslateX(coords.getX());
box.setTranslateY(coords.getY());
box.setTranslateZ(coords.getZ());
});
final Scene scene = new Scene(root, 800, 600, true);
scene.setCamera(cam);
stage.setScene(scene);
stage.setTitle("JavaFX 3D Drag&Drop");
stage.show();
}
If you run it, you will see that you can pick the box and drag it all over the floor.
In this picture, I've added some color and stroke to the rectangle to see the limits of dragging.
Note also the changes in mouse transparent both in box and rectangle. The rectangle is not mouse transparent only during the dragging.
来源:https://stackoverflow.com/questions/29034080/restricting-a-3d-object-mouse-drag-movement-to-a-plane-in-javafx