How to create such shape using javaFx TriangleMesh?
Asked Answered
D

1

7

I need to create this shape. I understand how to create simple shapes such as a cube, but I don’t understand at all how to create such a shape. How to get the right points for these arrays? Please, help

    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;

`

I need to create this shape

Deianira answered 15/4, 2020 at 14:35 Comment(0)
T
12

There are several ways you can construct a 3D shape like the one you have posted.

3D Modeling

Probably the easiest way is by using a 3D editor, like 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.

JCSG

Without dealing with point, vertices and faces, but from a Java approach, another option is to use JCSG: you can just create two cubes, and do a boolean operation to get the desired shape (subtract a 20x20x60 cube from a 60x20x100 cube). There is also a way to convert the CSG object into a TriangleMesh.

FXyz3D

From a pure JavaFX perspective, you can also use the FXyz3D library and its 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, which precisely is a 2D constrained Delaunay triangulation library.

Since you need a flat surface on XY, I'll rewrite your list of points into:

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

and then the shape can be generated with:

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

(note the rotation will put the flat surface from plane XY into plane XZ as in your drawing)

TriangulatedMesh

You can now inspect the generated mesh, and you will see all the triangles generated:

Line and Fill Meshes

so you can use this information to "fill" your points, texture and faces arrays, and find out how it works.

TriangleMesh

Finally, starting from the scratch, but based on the above triangulation, these are the required arrays:

Vertices

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

vertices

Texture coordinates

These can be generated, for instance, based on a 2D surface with dimension 1x1, so vertices coordinates can be mapped easily with this expression: {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

Faces

We'll add the indices of 3 vertices and 3 texture coordinate for each triangular face.

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

For instance, for the top surface, there are four triangles defined, and the first one (0) has vertices (1, 2, 4), the second one (1) has vertices (4, 2, 3), and so on:

Faces

In this case, the coordinate textures have the same indices as the vertices (but this could be different). Note the vertex winding or anti-clockwise rotation.

EDIT

If you don't use normals, it is convenient to add face smoothing groups: each group contains the faces indices that belong to a same flat surface. For instance, the first four indices belong to the top surface.

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

And this is the whole code to create your custom MeshView node:

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;
    }
Turner answered 15/4, 2020 at 21:54 Comment(3)
Impressive and comprehensive answer.Mongolian
Thank you very much for the explanation!Deianira
@VarvaraDvortsova, you are welcome. Make sure you mark this answer as accepted if it works for you, as that will help others as well.Nashom

© 2022 - 2024 — McMap. All rights reserved.