Mof*_*ast 8 3d mouse raycasting javafx-8 javafx-3d
当我在JavaFX中创建我的第一个3D游戏时 - 您可以使用鼠标从零件组装船只.这提出了一个问题,因为JAVAFX似乎没有用于将PerspectiveCamera屏幕2D坐标转换为场景3D空间的原生元素.
这是我正在努力实现的目标.鼠标移动的块应在假想的平面上移动,该平面始终相对于摄像机旋转90°:
我试图用三角函数解决问题而没有太大的成功.我没有附加代码片段,因为我正在寻找更通用的数学解决方案,但如果需要,我会提供它.
所有帮助将不胜感激!
期望的结果:
Jos*_*eda 11
正如@ jdub1581所指出的,这Camera
是将鼠标移动与场景中的3D对象绑定的关键.
对于starterts,我们了解公共API PickResult
,它允许我们使用鼠标选择3D对象,基于从摄像机位置执行的一些光线跟踪技术.
但是一旦我们有了一个物体,移动它就是另一个问题.
寻找这个问题的解决方案(在3D空间中使用2D鼠标移动3D对象)不久前,我在OpenJFX存储库的Toys项目中找到了Camera3D类.
它有一个突发方法叫做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) {
}
Run Code Online (Sandbox Code Playgroud)
由于您要求数学解释,此方法使用您正在寻找的三角学.这将为您提供基于(x,y)鼠标坐标的3D矢量,使用私有Vec3d
类(我们可以替换为public 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);
Run Code Online (Sandbox Code Playgroud)
应用一些进一步的变换以获得场景坐标中的归一化矢量.
下一步将在实际坐标中转换此标准化矢量,仅使用从拾取器到对象的距离,在拾取结果上给出,并转换对象位置.
基本上,这段代码概述了拖动对象的整个过程:
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();
});
Run Code Online (Sandbox Code Playgroud)
这是一个完整的基本示例:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
这将允许您在场景中拾取和拖动球体: