2015-02-25 23 views
6

Kiedy tworzyłem swoją pierwszą grę 3D w JavaFX - gdzie można było składać statki z części za pomocą myszy. Stanowi to problem, ponieważ wydaje się, że JAVAFX nie ma natywnych metod, które działają w celu konwersji współrzędnych 2D ekranu PerspectiveCamera na przestrzeń 3D sceny.JavaFX Przenoszenie obiektów 3D za pomocą myszy na wirtualnym samolocie

Oto przedstawienie tego, co próbuję osiągnąć. Blok poruszany myszą powinien poruszać się na wyimaginowanej płaszczyźnie, która jest zawsze obrócona o 90 w stosunku do kamery: Representation Próbowałem rozwiązać problem z trygonometrią bez większego powodzenia. Nie załączam fragmentu kodu, ponieważ szukam bardziej ogólnego rozwiązania matematycznego, ale udostępnię je na żądanie.

Cała pomoc będzie doceniona!

Pożądany wynik: Before

After

+0

To nie jest część publikacji ic api, ale możesz spróbować użyć CameraHelper .. ma 3 metody, jedna, której chcesz, to **.pickProjectionPlane (camera, x, y) – jdub1581

Odpowiedz

7

Jako @ jdub1581 wskazuje obecnie, Camera jest kluczem do związania ruch myszy ze swoimi 3D objets na scenie.

Dla początkujących wiemy o publicznym API PickResult, który pozwala nam wybrać obiekt 3D za pomocą myszy, na podstawie niektórych technik śledzenia promieni wykonywanych z pozycji kamery.

Ale gdy już mamy obiekt, przenoszenie go to inny problem.

Poszukując rozwiązania tego problemu (przenoszenie obiektów 3D za pomocą myszy 2D w przestrzeni 3D) jakiś czas temu znalazłem klasę Camera3D w projekcie Toys w repozytorium OpenJFX.

Ma obiecującą metodę zwaną 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) { 
} 

Ponieważ poprosiłeś o matematycznego wyjaśnienia, ta metoda wykorzystuje trygonometrii, czego szukasz. To daje wektor 3D na podstawie (x, y) współrzędne myszy, używając prywatnego Vec3d klasę (które możemy zastąpić z publicznym Point3D):

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

Niektóre dalsze transformacje są stosowane, aby uzyskać znormalizowany wektor w współrzędne sceny.

Następnym krokiem będzie transformacja tego znormalizowanego wektora na rzeczywiste współrzędne, używając jedynie odległości od kamery do obiektu, podanej w wyniku wybrania i transformacji położenia obiektu.

Zasadniczo, ten fragment kodu przedstawia cały proces przeciągania obiektu:

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

I to jest pełne pracy prosty przykład:

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

który pozwoli Ci zbieranie i przeciągając Kula na scenie:

dragging 3d

+0

Wow, świetna odpowiedź! Właśnie tego szukałem! : D Wielkie dzięki. –

+0

Dziękuję. Na pewno będziesz musiał wymyślić kilka rzeczy dla siebie, ale to jest punkt wyjścia. Jeśli potrzebujesz, spójrz na to [repozytorium] (https://github.com/FXyz/FXyz), pracujemy nad zaawansowanymi funkcjami dla JavaFX 3D. –

+0

Niesamowite, na pewno to sprawdzę. –

Powiązane problemy