如何在javafx中获取3D对象窗口的2D坐标 [英] How to get 2D coordinates on window for 3D object in javafx

查看:225
本文介绍了如何在javafx中获取3D对象窗口的2D坐标的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在javafx中,如果我们有2D HUD(由Pane制作然后我们创建了用于2D Hud的SubScene对象)和3D SubScene以及3D场景内部我们有一些带坐标的对象(x,y,z) - 怎么能如果在我们的透视相机的视野中,我们在对象的HUD中获得2D坐标?

In javafx if we have 2D HUD (made of Pane and then out of it we create SubScene object for 2D Hud) and 3D SubScene and inside 3D scene we have some object with coordinates (x,y,z) - how can we get 2D coordinates in our HUD of the object if it is in visual field of our perspective camera?

我试图得到对象的第一个场景坐标,然后将其转换为(sceneToScreen)坐标,对于Pane的点(0,0)进行转换,然后先减去从第二点开始,但我得不到正确的结果。很抱歉,因为我的英语不好。可以帮忙吗?

I tried to get first Scene coordinates of the object and then convert it (sceneToScreen) coordinates and the same for point (0,0) of Pane and then to subtract first point from second point but i don't get right result. Sorry because of my bad English.Can Someone help with this?

推荐答案

有一种方法可以转换对象的3D坐标在subScene到2D场景坐标,但遗憾的是它使用私有API,因此建议不要依赖它。

There is a way to convert the 3D coordinates of an object in a subScene to a 2D scene coordinates, but unfortunately it uses private API, so it is advised not to rely on it.

这个想法是基于相机投影的方式工作,它基于 com.sun.javafx.scene.input.InputEventUtils.recomputeCoordinates()方法,该方法通常用于来自<$ c $的输入事件c> PickResult 。

The idea is based on how the camera projection works, and it is based on the com.sun.javafx.scene.input.InputEventUtils.recomputeCoordinates() method that is used typically for input events from a PickResult.

假设您在子场景中有一个节点。对于该节点的给定点,您可以获得其坐标,如:

Let's say you have a node in a sub scene. For a given point of that node, you can obtain its coordinates like:

Point3D coordinates = node.localToScene(Point3D.ZERO);

您可以了解节点的子场景:

and you can find out about the sub scene of the node:

SubScene subScene = NodeHelper.getSubScene(node);

现在你可以使用 SceneUtils :: subSceneToScene 方法


将点从内部subScene坐标转换为场景坐标。

Translates point from inner subScene coordinates to scene coordinates.

获取一组新的坐标,引用场景:

to get a new set of coordinates, referenced to the scene:

coordinates = SceneUtils.subSceneToScene(subScene, coordinates);

但这些仍然是3D坐标。

But these are still 3D coordinates.

将这些转换为2D的最后一步是使用 CameraHelper :: project

The final step to convert those to 2D is with the use of CameraHelper::project:

final Camera effectiveCamera = SceneHelper.getEffectiveCamera(node.getScene());        
Point2D p2 = CameraHelper.project(effectiveCamera, coordinates);

以下示例将2D标签放置在场景中,位于8个顶点的完全相同位置subScene中的3D框。

The following sample places 2D labels in the scene, at the exact same position of the 8 vertices of a 3D box in a subScene.

private final Rotate rotateX = new Rotate(0, Rotate.X_AXIS);
private final Rotate rotateY = new Rotate(0, Rotate.Y_AXIS);

private double mousePosX;
private double mousePosY;
private double mouseOldX;
private double mouseOldY;

private Group root;

@Override
public void start(Stage primaryStage) {

    Box box = new Box(150, 100, 50);
    box.setDrawMode(DrawMode.LINE);
    box.setCullFace(CullFace.NONE);

    Group group = new Group(box);

    PerspectiveCamera camera = new PerspectiveCamera(true);
    camera.setNearClip(0.1);
    camera.setFarClip(10000.0);
    camera.setFieldOfView(20);
    camera.getTransforms().addAll (rotateX, rotateY, new Translate(0, 0, -500));
    SubScene subScene = new SubScene(group, 500, 400, true, SceneAntialiasing.BALANCED);
    subScene.setCamera(camera);
    root = new Group(subScene);

    Scene scene = new Scene(root, 500, 400);

    primaryStage.setTitle("HUD: 2D Labels over 3D SubScene");
    primaryStage.setScene(scene);
    primaryStage.show();

    updateLabels(box);

    scene.setOnMousePressed(event -> {
        mousePosX = event.getSceneX();
        mousePosY = event.getSceneY();
    });

    scene.setOnMouseDragged(event -> {
        mousePosX = event.getSceneX();
        mousePosY = event.getSceneY();
        rotateX.setAngle(rotateX.getAngle() - (mousePosY - mouseOldY));
        rotateY.setAngle(rotateY.getAngle() + (mousePosX - mouseOldX));
        mouseOldX = mousePosX;
        mouseOldY = mousePosY;
        updateLabels(box);
    });
}

private List<Point3D> generateDots(Node box) {
    List<Point3D> vertices = new ArrayList<>();
    Bounds bounds = box.getBoundsInLocal();
    vertices.add(box.localToScene(new Point3D(bounds.getMinX(), bounds.getMinY(), bounds.getMinZ())));
    vertices.add(box.localToScene(new Point3D(bounds.getMinX(), bounds.getMinY(), bounds.getMaxZ())));
    vertices.add(box.localToScene(new Point3D(bounds.getMinX(), bounds.getMaxY(), bounds.getMinZ())));
    vertices.add(box.localToScene(new Point3D(bounds.getMinX(), bounds.getMaxY(), bounds.getMaxZ())));
    vertices.add(box.localToScene(new Point3D(bounds.getMaxX(), bounds.getMinY(), bounds.getMinZ())));
    vertices.add(box.localToScene(new Point3D(bounds.getMaxX(), bounds.getMinY(), bounds.getMaxZ())));
    vertices.add(box.localToScene(new Point3D(bounds.getMaxX(), bounds.getMaxY(), bounds.getMinZ())));
    vertices.add(box.localToScene(new Point3D(bounds.getMaxX(), bounds.getMaxY(), bounds.getMaxZ())));
    return vertices;
}

private void updateLabels(Node box) {
    root.getChildren().removeIf(Label.class::isInstance);
    SubScene oldSubScene = NodeHelper.getSubScene(box);
    AtomicInteger counter = new AtomicInteger(1);
    generateDots(box).stream()
        .forEach(dot -> {
            Point3D coordinates = SceneUtils.subSceneToScene(oldSubScene, dot);
            Point2D p2 = CameraHelper.project(SceneHelper.getEffectiveCamera(box.getScene()), coordinates);
            Label label = new Label("" + counter.getAndIncrement() + String.format(" (%.1f,%.1f)", p2.getX(), p2.getY()));
            label.setStyle("-fx-font-size:1.3em; -fx-text-fill: blue;");
            label.getTransforms().setAll(new Translate(p2.getX(), p2.getY()));
            root.getChildren().add(label);
        });
}

FXyz3D 还有另一个类似的示例

The FXyz3D library has another similar sample.

编辑

这个答案的后期编辑,但值得一提的是那里不需要私有API。 Node :: localToScene 方法中有公共API允许遍历subScene。

A late edit of this answer, but it is worthwhile mentioning that there is no need for private API. There is public API in the Node::localToScene methods that allows traversing the subScene.

所以这只是作品(注意 true 参数):

So this just works (note the true argument):

Point3D p2 = box.localToScene(dot, true);

根据JavaDoc for Node :: localToScene

According to the JavaDoc for Node::localToScene:


将一个点从此Node的局部坐标空间转换为其场景的坐标空间。如果Node没有任何SubScene或rootScene设置为true,则结果点位于getScene()返回的Node的Scene坐标中。否则,使用子场景坐标,相当于调用localToScene(Point3D)。

Transforms a point from the local coordinate space of this Node into the coordinate space of its scene. If the Node does not have any SubScene or rootScene is set to true, the result point is in Scene coordinates of the Node returned by getScene(). Otherwise, the subscene coordinates are used, which is equivalent to calling localToScene(Point3D).

没有 true 转换在subScene中,但是使用它,转换从当前的subScene转到场景。在这种情况下,此方法调用 SceneUtils :: subSceneToScene ,因此我们不再需要这样做。

Without true the conversion is within the subScene, but with it, the conversion goes from the current subScene to the scene. In this case, this methods calls SceneUtils::subSceneToScene, so we don't need to do it anymore.

有了这个, updateLabels 被简化为:

private void updateLabels(Node box) {
    root.getChildren().removeIf(Label.class::isInstance);
    AtomicInteger counter = new AtomicInteger(1);
    generateDots(box).stream()
            .forEach(dot -> {
                Point3D p2 = box.localToScene(dot, true);
                Label label = new Label("" + counter.getAndIncrement() + String.format(" (%.1f,%.1f)", p2.getX(), p2.getY()));
                label.setStyle("-fx-font-size:1.3em; -fx-text-fill: blue;");
                label.getTransforms().setAll(new Translate(p2.getX(), p2.getY()));
                root.getChildren().add(label);
            });
}

这篇关于如何在javafx中获取3D对象窗口的2D坐标的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆