如何使用 javaFx TriangleMesh 创建这样的形状?

How to create such shape using javaFx TriangleMesh?

我需要创建这个形状。 我知道如何创建简单的形状,例如立方体,但我完全不知道如何创建这样的形状。如何为这些数组获取正确的点?求助

    TriangleMesh mesh = new TriangleMesh();

    mesh.getPoints().addAll(
            0, 0, 0,//P1
            0,0,100,//P2
            0,20,100,//P3
            60,20,100,//P4
            60,0,100,//P5
            60,20,60,//P6
            60,0,60,//P7
            40,0,60,//P8
            40,20,60,//P9
            40,20,0,//P10
            40,0,0,//P11
            0,20,0//P12
    );

    mesh.getTexCoords().addAll(
     //which points should be here?
    );

    mesh.getFaces().addAll(
   // which points should be here?
               );
    return mesh;

`

有多种方法可以像您发布的那样构建 3D 形状。

3D 建模

可能最简单的方法是使用 3D 编辑器,例如 Blender (open source), and then exporting the model to an .OBJ file. In this OBJ file you will get a list of vertices, textures and faces. However, the format is not readable directly, so you can't just feed it into a JavaFX MeshView. However, there are importers of this format, that will create a TriangleMesh, like this one

南航

无需处理点、顶点和面,但从 Java 方法,另一种选择是使用 JCSG:您可以只创建两个立方体,并进行布尔运算以获得所需的形状(从 60x20x100 的立方体中减去 20x20x60 的立方体)。还有一种方法可以将 CSG 对象转换为 TriangleMesh

FXyz3D

从纯粹的 JavaFX 角度来看,您还可以使用 FXyz3D 库及其 TriangulatedMesh. Is is based on a flat surface with a list of 3D points ({x, y, 0}) that define its perimeter, that is triangulated and extruded to a given height. Internally it uses Poly2Tri,也就是 a 2D constrained Delaunay triangulation library.

由于您需要 XY 上的平面,我会将您的点列表重写为:

private final List<Point3D> points = new ArrayList<>(Arrays.asList(
            new Point3D(0,   0, 0),
            new Point3D(0, 100, 0), new Point3D(60, 100, 0),
            new Point3D(60, 60, 0), new Point3D(40, 60,  0),
            new Point3D(40,  0, 0), new Point3D( 0,  0,  0)));

然后可以生成形状:

TriangulatedMesh customShape = new TriangulatedMesh(points, 20);
customShape.setLevel(0);
customShape.setCullFace(CullFace.NONE);
customShape.getTransforms().addAll(new Rotate(-90, Rotate.X_AXIS));

(注意旋转会将平面从 XY 平面放入 XZ 平面,如您的绘图所示)

您现在可以检查生成的网格,您将看到生成的所有三角形:

因此您可以将此信息用于 "fill" 您的点、纹理和面数组,并了解其工作原理。

三角形网格

最后,从头开始,但根据上面的三角剖分,这些是所需的数组:

顶点

float[] vertices = {
         0.0,  0.0,   0.0,  // 0
         0.0,  0.0, 100.0,  // 1
        60.0,  0.0, 100.0,
        60.0,  0.0,  60.0,
        40.0,  0.0,  60.0,
        40.0,  0.0,   0.0,
         0.0, 20.0,   0.0,
         0.0, 20.0, 100.0,
        60.0, 20.0, 100.0,
        60.0, 20.0,  60.0,
        40.0, 20.0,  60.0,
        40.0, 20.0,   0.0};   // 11

纹理坐标

这些可以生成,例如,基于尺寸为 1x1 的 2D 表面,因此可以使用此表达式轻松映射顶点坐标:{x / (MaxX-MinX), y /(MaxY-MinY)}

float[] texture = {
        0.00, 0.00,        // 0
        0.00, 1.00,        // 1
        1.00, 1.00,
        1.00, 0.60,
        0.67, 0.60,
        0.67, 0.00,
        0.00, 0.00,
        0.00, 1.00,
        1.00, 1.00,
        1.00, 0.60,
        0.67, 0.60,
        0.67, 0.00};        // 11

面孔

我们将为每个三角形面添加 3 个顶点的索引和 3 个纹理坐标。

int[] faces = {
         1,  1,  2,  2,  4,  4,      // 0
         4,  4,  2,  2,  3,  3,      // 1
         1,  1,  4,  4,  0,  0,      // 2
         0,  0,  4,  4,  5,  5,
         7,  7, 10, 10,  8,  8,
        10, 10,  9,  9,  8,  8,
         7,  7,  6,  6, 10, 10,
         6,  6, 11, 11, 10, 10,
         0,  0,  1,  1,  7,  7,
         0,  0,  7,  7,  6,  6,
         1,  1,  2,  2,  8,  8,
         1,  1,  8,  8,  7,  7,
         2,  2,  3,  3,  9,  9,
         2,  2,  9,  9,  8,  8,
         3,  3,  4,  4, 10, 10,
         3,  3, 10, 10,  9,  9,
         4,  4,  5,  5, 11, 11,
         4,  4, 11, 11, 10, 10,
         5,  5,  0,  0,  6,  6,
         5,  5,  6,  6, 11, 11};     // 19

例如,对于顶面,定义了四个三角形,第一个(0)的顶点为(1,2,4),第二个(1)的顶点为(4,2,3) ), 等等:

在这种情况下,坐标纹理与顶点具有相同的索引(但这可能不同)。注意顶点缠绕或逆时针旋转。

编辑

如果不使用法线,添加面平滑组会很方便:每个组包含属于同一平面的面索引。比如前四个指标属于顶面。

int[] smooth = {0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7};

这是创建自定义 MeshView 节点的全部代码:

private final float[] vertices = {
             0.0f,  0.0f,   0.0f,  // 0
             0.0f,  0.0f, 100.0f,  // 1
            60.0f,  0.0f, 100.0f,
            60.0f,  0.0f,  60.0f,
            40.0f,  0.0f,  60.0f,
            40.0f,  0.0f,   0.0f,
             0.0f, 20.0f,   0.0f,
             0.0f, 20.0f, 100.0f,
            60.0f, 20.0f, 100.0f,
            60.0f, 20.0f,  60.0f,
            40.0f, 20.0f,  60.0f,
            40.0f, 20.0f,   0.0f};   // 11

    private final float[] texture = {
            0.00f, 0.00f,        // 0
            0.00f, 1.00f,        // 1
            1.00f, 1.00f,
            1.00f, 0.60f,
            0.67f, 0.60f,
            0.67f, 0.00f,
            0.00f, 0.00f,
            0.00f, 1.00f,
            1.00f, 1.00f,
            1.00f, 0.60f,
            0.67f, 0.60f,
            0.67f, 0.00f};        // 11

    private final int[] faces = {
            1,  1,  2,  2,  4,  4,      // 0
            4,  4,  2,  2,  3,  3,      // 1
            1,  1,  4,  4,  0,  0,      // 2
            0,  0,  4,  4,  5,  5,
            7,  7, 10, 10,  8,  8,
            10, 10,  9,  9,  8,  8,
            7,  7,  6,  6, 10, 10,
            6,  6, 11, 11, 10, 10,
            0,  0,  1,  1,  7,  7,
            0,  0,  7,  7,  6,  6,
            1,  1,  2,  2,  8,  8,
            1,  1,  8,  8,  7,  7,
            2,  2,  3,  3,  9,  9,
            2,  2,  9,  9,  8,  8,
            3,  3,  4,  4, 10, 10,
            3,  3, 10, 10,  9,  9,
            4,  4,  5,  5, 11, 11,
            4,  4, 11, 11, 10, 10,
            5,  5,  0,  0,  6,  6,
            5,  5,  6,  6, 11, 11};     // 19

    private final int[] smooth = {
            0, 0, 0, 0,   // top surface
            1, 1, 1, 1,   // bottom surface
            2, 2,
            3, 3,
            4, 4,
            5, 5,
            6, 6,
            7, 7};

    public MeshView getMeshView() {
        TriangleMesh mesh = new TriangleMesh();
        mesh.getPoints().addAll(vertices);
        mesh.getTexCoords().addAll(texture);
        mesh.getFaces().addAll(faces);
        mesh.getFaceSmoothingGroups().addAll(smooth);

        MeshView meshView = new MeshView(mesh);
        meshView.setMaterial(new PhongMaterial(Color.FIREBRICK));
        meshView.setCullFace(CullFace.NONE);
        return meshView;
    }