jameslfc19
jameslfc19

Reputation: 1134

How to make the ends of Cylinder transparent in JavaFX

I'm currently trying to make the ends of a Cylinder completely transparent whilst keeping the sides a Material. I'm unsure how to achieve this. This thread mentions it but all the links are broken

I think I need to use a clipping plane? Although I don't know where to start with that.

Here's what I'm currently using to just simply set a translucent material!

        Cylinder line = new Cylinder(getRadius()/4, height);
        lineMaterial = new PhongMaterial();
        lineMaterial.setDiffuseColor(new Color(1,1,1,0.5));
        lineMaterial.diffuseMapProperty();
        line.setMaterial(lineMaterial);

Upvotes: 3

Views: 330

Answers (1)

José Pereda
José Pereda

Reputation: 45476

One possible way to get transparency is by using a png as the diffuse image, with some transparent pixels.

While this works, if you apply over a built-in JavaFX Cylinder, you won't get the expected result, because the Cylinder applies the same image to both the vertical tube and the two cap faces. So this won't work for your case.

There could be an option to remove the caps from the cylinder mesh and get just a tube, but unfortunately it doesn't export its triangle mesh.

So far, the best option is to create directly the mesh of a tube, and for that we can reuse the mesh of the Cylinder by checking the open source code at the (new) OpenJDK/JFX GitHub repository.

The following method creates a TriangleMesh of a Tube of height h, radius r, with div divisions (producing 2 * div triangles). It is the exact same code as the one in Cylinder, but without the caps points, texture coordinate and faces arrays.

private static TriangleMesh createMesh(int div, float h, float r) {

    final int nPoints = div * 2;
    final int tcCount = (div + 1) * 2;
    final int faceCount = div * 2;

    float textureDelta = 1.f / 256;

    float dA = 1.f / div;
    h *= .5f;

    float points[] = new float[nPoints * 3];
    float tPoints[] = new float[tcCount * 2];
    int faces[] = new int[faceCount * 6];
    int smoothing[] = new int[faceCount];

    int pPos = 0, tPos = 0;

    for (int i = 0; i < div; ++i) {
        double a = dA * i * 2 * Math.PI;
        points[pPos + 0] = (float) (Math.sin(a) * r);
        points[pPos + 1] = h;
        points[pPos + 2] = (float) (Math.cos(a) * r);
        tPoints[tPos + 0] = 1 - dA * i;
        tPoints[tPos + 1] = 1 - textureDelta;
        pPos += 3; tPos += 2;
    }

    // top edge
    tPoints[tPos + 0] = 0;
    tPoints[tPos + 1] = 1 - textureDelta;
    tPos += 2;

    for (int i = 0; i < div; ++i) {
        double a = dA * i * 2 * Math.PI;
        points[pPos + 0] = (float) (Math.sin(a) * r);
        points[pPos + 1] = -h;
        points[pPos + 2] = (float) (Math.cos(a) * r);
        tPoints[tPos + 0] = 1 - dA * i;
        tPoints[tPos + 1] = textureDelta;
        pPos += 3; tPos += 2;
    }

    // bottom edge
    tPoints[tPos + 0] = 0;
    tPoints[tPos + 1] = textureDelta;
    tPos += 2;

    int fIndex = 0;

    // build body faces
    for (int p0 = 0; p0 < div; ++p0) {
        int p1 = p0 + 1;
        int p2 = p0 + div;
        int p3 = p1 + div;

        // add p0, p1, p2
        faces[fIndex+0] = p0;
        faces[fIndex+1] = p0;
        faces[fIndex+2] = p2;
        faces[fIndex+3] = p2 + 1;
        faces[fIndex+4] = p1 == div ? 0 : p1;
        faces[fIndex+5] = p1;
        fIndex += 6;

        // add p3, p2, p1
        faces[fIndex+0] = p3 % div == 0 ? p3 - div : p3;
        faces[fIndex+1] = p3 + 1;
        faces[fIndex+2] = p1 == div ? 0 : p1;
        faces[fIndex+3] = p1;
        faces[fIndex+4] = p2;
        faces[fIndex+5] = p2 + 1;
        fIndex += 6;

    }

    for (int i = 0; i < div * 2; ++i) {
        smoothing[i] = 1;
    }

    TriangleMesh m = new TriangleMesh();
    m.getPoints().setAll(points);
    m.getTexCoords().setAll(tPoints);
    m.getFaces().setAll(faces);
    m.getFaceSmoothingGroups().setAll(smoothing);
    return m;
}

Now you will need to create a MeshView so you can add it to your scene:

private static MeshView createTube(int div, float h, float r) {
    MeshView meshView = new MeshView(createMesh(div, h, r));
//        meshView.setDrawMode(DrawMode.LINE);
    meshView.setCullFace(CullFace.NONE);
    PhongMaterial material = new PhongMaterial(Color.RED);
    meshView.setMaterial(material);
    return meshView;
}

Create and add one to your scene:

MeshView tube = createTube(64, 5f, 1.6f);
Scene scene = new Scene(new Group(tube), 600, 600, true, SceneAntialiasing.BALANCED);

And you will get your tube:

Tube

You can also apply a diffuse image as texture:

material.setDiffuseMap(new Image(getClass.getResourceAsStream("440px-JavaFX_Logo.png")));

Texture

Upvotes: 3

Related Questions