3d JavaFX在虚拟平面上使用鼠标移动三维对象

3d JavaFX在虚拟平面上使用鼠标移动三维对象,3d,mouse,javafx-8,raycasting,javafx-3d,3d,Mouse,Javafx 8,Raycasting,Javafx 3d,当我在JavaFX中创建我的第一个3D游戏时,你可以使用鼠标从零件组装船只。这带来了一个问题,因为JAVAFX似乎没有用于将透视摄影机屏幕2D坐标转换为场景3D空间的本地方法 这是我想表达的意思。鼠标移动的块应在始终相对于相机旋转90度的假想平面上移动: 我试图用三角法解决这个问题,但没有多大成功。我没有附上一个代码片段,因为我正在寻找一个更通用的数学解决方案,但如果需要,我会提供它 所有的帮助将不胜感激 预期结果: 正如@jdub1581所指出的,相机是将鼠标移动与场景中的3D对象绑定在一起

当我在JavaFX中创建我的第一个3D游戏时,你可以使用鼠标从零件组装船只。这带来了一个问题,因为JAVAFX似乎没有用于将透视摄影机屏幕2D坐标转换为场景3D空间的本地方法

这是我想表达的意思。鼠标移动的块应在始终相对于相机旋转90度的假想平面上移动: 我试图用三角法解决这个问题,但没有多大成功。我没有附上一个代码片段,因为我正在寻找一个更通用的数学解决方案,但如果需要,我会提供它

所有的帮助将不胜感激

预期结果:


正如@jdub1581所指出的,
相机是将鼠标移动与场景中的3D对象绑定在一起的关键

首先,我们了解公共API
PickResult
,它允许我们基于从相机位置执行的一些光线跟踪技术,使用鼠标选择3D对象

但是一旦我们有了一个物体,移动它是另一个问题

不久前,我在OpenJFX存储库的Toys项目中找到了这个类,以寻找解决这个问题的方法(在3D空间中使用2D鼠标移动3D对象)

它有一个promissing方法,名为
unProjectDirection

/*
 * returns 3D direction from the Camera position to the mouse
 * in the Scene space 
 */

public Vec3d unProjectDirection(double sceneX, double sceneY, 
                                double sWidth, double sHeight) {
}
因为你要求数学解释,这个方法使用了你想要的三角学。这将使用一个私有的
Vec3d
类(我们可以用public
Point3D
替换),基于(x,y)鼠标坐标为您提供一个三维矢量:

应用一些进一步的变换以获得场景坐标中的规范化向量

下一步将在实坐标中变换此规范化向量,只需使用拾取结果中给定的从摄影机到对象的距离,并变换对象位置

基本上,这段代码概述了拖动对象的整个过程:

    scene.setOnMousePressed((MouseEvent me) -> {
        vecIni = unProjectDirection(me.getSceneX(), me.getSceneY(), 
                     scene.getWidth(),scene.getHeight()); 
        distance=me.getPickResult().getIntersectedDistance();           
    });

    scene.setOnMouseDragged((MouseEvent me) -> {
        vecPos = unProjectDirection(mousePosX, mousePosY,
                 scene.getWidth(),scene.getHeight());
        Point3D p=vecPos.subtract(vecIni).multiply(distance);
        node.getTransforms().add(new Translate(p.getX(),p.getY(),p.getZ()));
        vecIni=vecPos;
        distance=me.getPickResult().getIntersectedDistance();
    });
这是一个完整的基本示例:

    public class Drag3DObject 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(150);
        sphere.setMaterial(new PhongMaterial(Color.RED));
        sphere.setTranslateY(-5);
        root.getChildren().add(sphere);

        Scene scene = new Scene(root, sceneWidth, sceneHeight, true, SceneAntialiasing.BALANCED);
        scene.setFill(Color.web("3d3d3d"));

        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));
        scene.setCamera(camera);

        scene.setOnMousePressed((MouseEvent me) -> {
            mousePosX = me.getSceneX();
            mousePosY = me.getSceneY();
            PickResult pr = me.getPickResult();
            if(pr!=null && pr.getIntersectedNode() != null && pr.getIntersectedNode() instanceof Sphere){
                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();
                if(pr!=null && pr.getIntersectedNode() != null && pr.getIntersectedNode()==s){
                    distance=pr.getIntersectedDistance();
                } else {
                    isPicking=false;
                }
            } 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;
            }
        });

        stage.setTitle("3D Dragging");
        stage.setScene(scene);
        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);
    }
}
这将允许您在场景中拾取和拖动球体:


它不是公共api的一部分,但您可以尝试使用CameraHelper。。有3种方法,你想要的是**。选择ProjectionPlane(camera,x,y)哇,很好的答案!这正是我想要的!:D非常感谢。谢谢。当然,你必须为自己找出一些事情,但这是一个起点。如果你需要它,看看这个,我们正在为JavaFX3D开发高级功能。太棒了,我一定会去看看。
    public class Drag3DObject 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(150);
        sphere.setMaterial(new PhongMaterial(Color.RED));
        sphere.setTranslateY(-5);
        root.getChildren().add(sphere);

        Scene scene = new Scene(root, sceneWidth, sceneHeight, true, SceneAntialiasing.BALANCED);
        scene.setFill(Color.web("3d3d3d"));

        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));
        scene.setCamera(camera);

        scene.setOnMousePressed((MouseEvent me) -> {
            mousePosX = me.getSceneX();
            mousePosY = me.getSceneY();
            PickResult pr = me.getPickResult();
            if(pr!=null && pr.getIntersectedNode() != null && pr.getIntersectedNode() instanceof Sphere){
                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();
                if(pr!=null && pr.getIntersectedNode() != null && pr.getIntersectedNode()==s){
                    distance=pr.getIntersectedDistance();
                } else {
                    isPicking=false;
                }
            } 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;
            }
        });

        stage.setTitle("3D Dragging");
        stage.setScene(scene);
        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);
    }
}