2015-02-25 5 views
6

Alors que je créais mon premier jeu 3D en JavaFX - où vous seriez en mesure d'assembler des navires à partir de pièces en utilisant la souris. Cela pose un problème car JAVAFX ne semble pas avoir de métodes natives qui permettent de convertir les coordonnées 2D de l'écran PerspectiveCamera dans l'espace 3D de la scène.JavaFX Déplacement d'objets 3D avec la souris sur un plan virtuel

Voici une représentation de ce que j'essaie d'obtenir. Un bloc déplacé par la souris doit se déplacer sur un plan imaginaire toujours tourné de 90 par rapport à la caméra: Representation J'ai essayé de résoudre le problème avec la trigonométrie sans beaucoup de succès. Je n'ai pas joint d'extrait de code car je suis à la recherche d'une solution mathématique plus générique, mais je la fournirai si nécessaire.

Toute aide serait appréciée!

Résultat souhaité: Before

After

+0

Cela ne fait pas partie du publ IC api, mais vous pouvez essayer d'utiliser CameraHelper .. a 3 méthodes, celle que vous voulez est **.pickProjectionPlane (caméra, x, y) – jdub1581

Répondre

7

Comme @ jdub1581 souligne, la Camera est la clé pour lier le mouvement de la souris avec vos objets 3D sur la scène. Pour les starters, nous connaissons l'API publique PickResult, qui nous permet de sélectionner un objet 3D à l'aide de la souris, en fonction de techniques de tracé de rayons effectuées à partir de la position de la caméra.

Mais une fois que nous avons un objet, le déplacer est un problème différent.

En cherchant une solution à ce problème (en déplaçant des objets 3D avec une souris 2D dans l'espace 3D) il y a quelques temps, j'ai trouvé la classe Camera3D au projet Toys sur le référentiel OpenJFX.

Il a une méthode appelée unProjectDirection prometteuse:

/* 
* 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) { 
} 

Depuis que vous avez demandé explication mathématique, cette méthode utilise la trigonométrie que vous recherchez. Cela vous donnera un vecteur 3D à base de (x, y) les coordonnées de la souris, en utilisant une classe Vec3d privée (que l'on peut remplacer par Point3D public):

double tanOfHalfFOV = Math.tan(Math.toRadians(camera.getFieldOfView()) * 0.5f); 
Vec3d vMouse = new Vec3d(tanOfHalfFOV*(2*sceneX/sWidth-1), tanOfHalfFOV*(2*sceneY/sWidth-sHeight/sWidth), 1); 

Quelques autres transformations sont appliquées pour obtenir un vecteur normalisé en coordonnées de la scène. L'étape suivante va transformer ce vecteur normalisé en coordonnées réelles, en utilisant simplement la distance entre la caméra et l'objet, donnée sur le résultat de la sélection, et en transformant la position de l'objet.

Fondamentalement, ce bout de code décrit le processus de faire glisser un objet:

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(); 
    }); 

Et ceci est un exemple de base de travail complet:

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); 
    } 
} 

qui vous permettra la cueillette et en faisant glisser la sphère sur la scène:

dragging 3d

+0

Wow, bonne réponse! Ceci est exactement ce que je cherchais! : D Merci beaucoup. –

+0

Merci. Vous devrez sûrement trouver quelques petites choses pour vous, mais c'est un point de départ. Au cas où vous en auriez besoin, jetez un oeil à ce [référentiel] (https://github.com/FXyz/FXyz), nous travaillons sur des fonctionnalités avancées pour JavaFX 3D. –

+0

Génial, je vais être sûr de vérifier. –