Каков наиболее практичный способ создания сетки координат с помощью JavaFX 3D?

Я хотел бы создать демонстрационное 3D-приложение с JavaFX для визуализации движения точек в 3D-пространстве, и сначала мне нужно настроить координатную сетку для визуального ориентира. К сожалению, мне не удалось найти пример кода для сетки, как на этом рисунке:

это изображение

Кто-нибудь знает, какой самый практичный способ создать что-то подобное?


person András Gajdács    schedule 17.08.2018    source источник


Ответы (1)


Уже есть несколько решений.

Библиотека FXyz3D имеет CubeWorld class, который дает вам точную сетку ссылок.

Кубический мир

Его очень легко использовать. Просто импортируйте зависимость 'org.fxyz3d:fxyz3d:0.3.0' из JCenter и используйте ее:

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 узлов).

Существуют и другие решения, в которых используется меньшее количество узлов. Например, для этого образца, который также связан с Leap Motion, я использовал TriangleMesh.

Прыжок V2

Это простое решение, всего с двумя сетками. Однако вместо квадратов вы видите треугольники.

Итак, давайте попробуем избавиться от треугольников. Для этого я буду использовать PolygonMesh, как и в этом другом вопросе, основанном на проекте 3DViewer, который доступен в репозитории OpenJFX, уже содержит 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);

Четырехсторонняя сетка

Общее количество узлов этого образца равно 14.

Конечно, его можно улучшить, добавив метки и многие другие функции.

person José Pereda    schedule 17.08.2018