gpt4 book ai didi

javafx - 如何在javafx中获取3D对象窗口上的2D坐标

转载 作者:行者123 更新时间:2023-12-02 11:15:32 25 4
gpt4 key购买 nike

在javafx中,如果我们有2D HUD(由Pane组成,然后我们为2D Hud创建SubScene对象)和3D SubScene,并且在3D场景中我们有一些带有坐标(x,y,z)的对象 - 如何才能如果物体位于透视相机的视野中,我们会在 HUD 中获得 2D 坐标吗?

我尝试获取对象的第一个场景坐标,然后将其转换为 (sceneToScreen) 坐标,并且对于 Pane 的点 (0,0) 进行相同的转换,然后从第二点中减去第一个点,但我没有得到正确的结果。抱歉,我的英语不好。有人可以帮忙吗?

最佳答案

有一种方法可以将子场景中对象的 3D 坐标转换为 2D 场景坐标,但不幸的是它使用私有(private) API,因此建议不要依赖它。

这个想法基于相机投影的工作原理,并且基于通常用于输入事件的 com.sun.javafx.scene.input.InputEventUtils.recomputeCooperatives() 方法来自 PickResult

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

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

并且可以了解该节点的子场景:

SubScene subScene = NodeHelper.getSubScene(node);

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

Translates point from inner subScene coordinates to scene coordinates.

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

coordinates = SceneUtils.subSceneToScene(subScene, coordinates);

但这些仍然是 3D 坐标。

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

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

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

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

HUD

FXyz3D library还有另一个类似的sample .

编辑

这个答案的后期编辑,但值得一提的是,不需要私有(private) API。 Node::localToScene 方法中有公共(public) API,允许遍历子场景。

所以这有效(注意true参数):

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

根据 Node::localToScene 的 JavaDoc:

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,转换是在子场景内进行的,但是有了它,转换就会从当前子场景到场景。在本例中,此方法调用 SceneUtils::subSceneToScene,因此我们不需要再执行此操作。

这样,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 - 如何在javafx中获取3D对象窗口上的2D坐标,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52017893/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com