javafx - 使用 JavaFX 3D 创建坐标网格的最实用方法是什么?

标签 javafx javafx-8 javafx-3d fxyz3d

我想用 JavaFX 创建一个 3D 演示应用程序来可视化点在 3D 空间中的移动,首先我需要设置一个坐标网格以供视觉引用。不幸的是,我无法找到如下图所示网格的示例代码:

this picture

有谁知道最实用的方法是什么?

最佳答案

已经有一些解决方案。

FXyz3D图书馆有一个CubeWorld class ,这为您提供了一个精确的引用网格。

CubeWorld

使用起来非常简单。只需从 JCenter 导入 'org.fxyz3d:fxyz3d:0.3.0' 依赖项并使用它:

CubeWorld cubeWorld = new CubeWorld(5000, 500, true);
Sphere sphere = new Sphere(100);
sphere.setMaterial(new PhongMaterial(Color.FIREBRICK));
sphere.getTransforms().add(new Translate(100, 200, 300));

Scene scene = new Scene(new Group(cubeWorld, sphere), 800, 800, true, SceneAntialiasing.BALANCED);

如您所见,该解决方案基于为每个面使用 2D 矩形,并使用 3D 圆柱体创建网格线。它具有非常好的功能(如自闪电或根据相机的正面不显示网格),但它的节点非常密集(上面的示例有 168 个节点)。

还有其他使用较少节点的解决方案。例如,对于这个 sample ,这也恰好与Leap Motion有关,我使用了一个TriangleMesh

LeapV2

这是一个简单的解决方案,只需要两个网格。但是,您看到的是三角形,而不是正方形。

所以让我们尝试摆脱三角形。为此,我将使用 PolygonMesh,就像在另一个 question 中一样。 ,基于在 OpenJFX 上可用的 3DViewer 项目存储库,已经包含一个 PolygonalMesh实现,允许每个面有任意数量的点,因此任何多边形都可以是面。

这将为您提供基于方形面的平面网格:

private PolygonMesh createQuadrilateralMesh(float width, float height, int subDivX, int subDivY) {
    final float minX = - width / 2f;
    final float minY = - height / 2f;
    final float maxX = width / 2f;
    final float maxY = height / 2f;

    final int pointSize = 3;
    final int texCoordSize = 2;
    // 4 point indices and 4 texCoord indices per face
    final int faceSize = 8;
    int numDivX = subDivX + 1;
    int numVerts = (subDivY + 1) * numDivX;
    float points[] = new float[numVerts * pointSize];
    float texCoords[] = new float[numVerts * texCoordSize];
    int faceCount = subDivX * subDivY;
    int faces[][] = new int[faceCount][faceSize];

    // Create points and texCoords
    for (int y = 0; y <= subDivY; y++) {
        float dy = (float) y / subDivY;
        double fy = (1 - dy) * minY + dy * maxY;

        for (int x = 0; x <= subDivX; x++) {
            float dx = (float) x / subDivX;
            double fx = (1 - dx) * minX + dx * maxX;

            int index = y * numDivX * pointSize + (x * pointSize);
            points[index] = (float) fx;
            points[index + 1] = (float) fy;
            points[index + 2] = 0.0f;

            index = y * numDivX * texCoordSize + (x * texCoordSize);
            texCoords[index] = dx;
            texCoords[index + 1] = dy;
        }
    }

    // Create faces
    int index = 0;
    for (int y = 0; y < subDivY; y++) {
        for (int x = 0; x < subDivX; x++) {
            int p00 = y * numDivX + x;
            int p01 = p00 + 1;
            int p10 = p00 + numDivX;
            int p11 = p10 + 1;
            int tc00 = y * numDivX + x;
            int tc01 = tc00 + 1;
            int tc10 = tc00 + numDivX;
            int tc11 = tc10 + 1;

            faces[index][0] = p00;
            faces[index][1] = tc00;
            faces[index][2] = p10;
            faces[index][3] = tc10;
            faces[index][4] = p11;
            faces[index][5] = tc11;
            faces[index][6] = p01;
            faces[index++][7] = tc01;
        }
    }

    int[] smooth = new int[faceCount];

    PolygonMesh mesh = new PolygonMesh(points, texCoords, faces);
    mesh.getFaceSmoothingGroups().addAll(smooth);
    return mesh;
}

因此您可以使用其中的 2 或 3 个来创建这样的坐标系:

public Group createGrid(float size, float delta) {
    if (delta < 1) {
        delta = 1;
    }
    final PolygonMesh plane = createQuadrilateralMesh(size, size, (int) (size / delta), (int) (size / delta));

    final PolygonMesh plane2 = createQuadrilateralMesh(size, size, (int) (size / delta / 5), (int) (size / delta / 5));

    PolygonMeshView meshViewXY = new PolygonMeshView(plane);
    meshViewXY.setDrawMode(DrawMode.LINE);
    meshViewXY.setCullFace(CullFace.NONE);

    PolygonMeshView meshViewXZ = new PolygonMeshView(plane);
    meshViewXZ.setDrawMode(DrawMode.LINE);
    meshViewXZ.setCullFace(CullFace.NONE);
    meshViewXZ.getTransforms().add(new Rotate(90, Rotate.X_AXIS));

    PolygonMeshView meshViewYZ = new PolygonMeshView(plane);
    meshViewYZ.setDrawMode(DrawMode.LINE);
    meshViewYZ.setCullFace(CullFace.NONE);
    meshViewYZ.getTransforms().add(new Rotate(90, Rotate.Y_AXIS));

    PolygonMeshView meshViewXY2 = new PolygonMeshView(plane2);
    meshViewXY2.setDrawMode(DrawMode.LINE);
    meshViewXY2.setCullFace(CullFace.NONE);
    meshViewXY2.getTransforms().add(new Translate(size / 1000f, size / 1000f, 0));

    PolygonMeshView meshViewXZ2 = new PolygonMeshView(plane2);
    meshViewXZ2.setDrawMode(DrawMode.LINE);
    meshViewXZ2.setCullFace(CullFace.NONE);
    meshViewXZ2.getTransforms().add(new Translate(size / 1000f, size / 1000f, 0));
    meshViewXZ2.getTransforms().add(new Rotate(90, Rotate.X_AXIS));

    PolygonMeshView meshViewYZ2 = new PolygonMeshView(plane2);
    meshViewYZ2.setDrawMode(DrawMode.LINE);
    meshViewYZ2.setCullFace(CullFace.NONE);
    meshViewYZ2.getTransforms().add(new Translate(size / 1000f, size / 1000f, 0));
    meshViewYZ2.getTransforms().add(new Rotate(90, Rotate.Y_AXIS));

    return new Group(meshViewXY, meshViewXY2, meshViewXZ, meshViewXZ2 /*, meshViewYZ, meshViewYZ2 */);
}

请注意,我已经复制了平面以每 5 行模拟一个更宽的笔画。

最后添加坐标轴:

public Group getAxes(double scale) {
    Cylinder axisX = new Cylinder(1, 200);
    axisX.getTransforms().addAll(new Rotate(90, Rotate.Z_AXIS), new Translate(0, -100, 0));
    axisX.setMaterial(new PhongMaterial(Color.RED));

    Cylinder axisY = new Cylinder(1, 200);
    axisY.getTransforms().add(new Translate(0, 100, 0));
    axisY.setMaterial(new PhongMaterial(Color.GREEN));

    Cylinder axisZ = new Cylinder(1, 200);
    axisZ.setMaterial(new PhongMaterial(Color.BLUE));
    axisZ.getTransforms().addAll(new Rotate(90, Rotate.X_AXIS), new Translate(0, 100, 0));

    Group group = new Group(axisX, axisY, axisZ);
    group.getTransforms().add(new Scale(scale, scale, scale));
    return group;
}

现在你有:

final Group axes = getAxes(0.5);
final Group grid = createGrid(200, 10);

final Sphere sphere = new Sphere(5);
sphere.getTransforms().add(new Translate(20, 15, 40));

Scene scene = new Scene(new Group(axes, grid, sphere), 800, 800, true, SceneAntialiasing.BALANCED);

Quadrilateral Grid

本样本的节点总数为14。

当然还可以改进,添加标签等诸多功能。

关于javafx - 使用 JavaFX 3D 创建坐标网格的最实用方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51895469/

相关文章:

javafx - 如何在初始化期间从 Controller 获取阶段?

3d - JavaFX 8 3D场景交点

JavaFX 未包含在 OpenJDK8 中?

css - 我将如何使用 css 在 javafx 中的 "menu"中制作文本

java - 无法在 JavaFX 中加载 Controller

java - 以百分比表示的 AnchorPane 约束

android - 在 Android 上同时使用 JavaFx + Activity

javascript - 如何确保加载所有 HTML

java - 如何在 JavaFX 中将 GUI 覆盖在 3D 场景上?

swing - JavaFX GUI,复杂 3D 图形的最简单方法