Coloring individual triangles in a triangle mesh on javafx

The way coloring works in JavaFX 3D meshes is by the material you assign them. For one mesh there’s one material, and it’s not possible to assing different materials to different triangles of the same mesh.

So if you want to avoid textures, I’m afraid the only way is grouping triangles with the same color in the same mesh, and creating so many meshes as colors.

On the contrary, with textures is relatively easy…, since you have just one mesh, one material and one image with all the coloring.

I’ve made an example of an icosahedron, built a triangle mesh for it and added a single texture to color all the faces.

For that, we need:

  • the 3D coordinates of the 12 vertex,
  • the 2D normalized coordinates of the uv mapping for the textures.
  • and the 20 faces. Each face is defined by 6 indices p0, t0, p1, t1, p3, t3, where p0, p1, p2 and p3 are indices into the points array, and t0, t1, t2 and t3 are indices into the texCoords array.

    public class IcosahedronMesh extends MeshView {

    public IcosahedronMesh(){
        setMesh(createCube());
    }
    private TriangleMesh createCube() {
        TriangleMesh m = new TriangleMesh();
    
        // POINTS
        m.getPoints().addAll(
            0f, 0f, -0.951057f, 
            0f, 0f, 0.951057f, 
            -0.850651f, 0f, -0.425325f, 
            0.850651f, 0f, 0.425325f, 
            0.688191f, -0.5f, -0.425325f, 
            0.688191f, 0.5f, -0.425325f, 
            -0.688191f, -0.5f, 0.425325f, 
            -0.688191f, 0.5f, 0.425325f, 
            -0.262866f, -0.809017f, -0.425325f, 
            -0.262866f, 0.809017f, -0.425325f, 
            0.262866f, -0.809017f, 0.425325f, 
            0.262866f, 0.809017f, 0.425325f
        );
    
        // TEXTURES
        m.getTexCoords().addAll(
                0.181818f, 0f, 
                0.363636f, 0f, 
                0.545455f, 0f, 
                0.727273f, 0f, 
                0.909091f, 0f,
                0.0909091f, 0.333333f,
                0.272727f, 0.333333f, 
                0.454545f, 0.333333f, 
                0.636364f, 0.333333f, 
                0.818182f, 0.333333f, 
                1f, 0.333333f, 
                0f, 0.666667f, 
                0.181818f, 0.666667f, 
                0.363636f, 0.666667f, 
                0.545455f, 0.666667f, 
                0.727273f, 0.666667f, 
                0.909091f, 0.666667f, 
                0.0909091f, 1f, 
                0.272727f, 1f, 
                0.454545f, 1f, 
                0.636364f, 1f, 
                0.818182f, 1f
        );
    
        // FACES
        m.getFaces().addAll(
                1, 6, 11, 5, 7, 0, 
                1, 12, 7, 11, 6, 5, 
                1, 7, 6, 6, 10, 1, 
                1, 13, 10, 12, 3, 6, 
                1, 8, 3, 7, 11, 2,
                4, 14, 8, 13, 0, 7, 
                5, 9, 4, 8, 0, 3, 
                9, 15, 5, 14, 0, 8, 
                2, 10, 9, 9, 0, 4, 
                8, 16, 2, 15, 0, 9,
                11, 5, 9, 6, 7, 12,
                7, 11, 2, 12, 6, 17, 
                6, 6, 8, 7, 10, 13, 
                10, 12, 4, 13, 3, 18, 
                3, 7, 5, 8, 11, 14,
                4, 13, 10, 14, 8, 19, 
                5, 8, 3, 9, 4, 15, 
                9, 14, 11, 15, 5, 20, 
                2, 9, 7, 10, 9, 16, 
                8, 15, 6, 16, 2, 21
        );
        return m;
    }
    

    }

Now we need an image with the coloring for each face, based on the net of an icosahedron, like this:

Net of an icosahedron

(Image found here)

Note that the mapping is done from the (0,0) to (1,1) normalized coordinates to the image (left,top) to (right, bottom) pixels.

Let’s finally create the scene, load the mesh and add the texture to its material:

@Override
public void start(Stage primaryStage) throws Exception {
    Group sceneRoot = new Group();
    Scene scene = new Scene(sceneRoot, 600, 600, true, SceneAntialiasing.BALANCED);
    scene.setFill(Color.BLACK);
    PerspectiveCamera camera = new PerspectiveCamera(true);
    camera.setNearClip(0.1);
    camera.setFarClip(10000.0);
    camera.setTranslateZ(-4);
    scene.setCamera(camera);

    IcosahedronMesh mesh = new IcosahedronMesh();
    mesh.setCullFace(CullFace.FRONT);
    PhongMaterial mat = new PhongMaterial();
    mat.setDiffuseMap(new Image(getClass().getResourceAsStream("icosah_net.png")));
    mesh.setMaterial(mat);
    Rotate rotateY = new Rotate(0, 0, 0, 0, Rotate.Y_AXIS);
    mesh.getTransforms().addAll(new Rotate(30,Rotate.X_AXIS),rotateY);

    sceneRoot.getChildren().addAll(mesh, new AmbientLight(Color.WHITE));

    primaryStage.setTitle("JavaFX 3D - Icosahedron");
    primaryStage.setScene(scene);
    primaryStage.show();        
}

This is how it looks like:

Icosahedron

EDIT

Now, if you think about how the texture is applied, you could simplify the image up to several squares with the palette of colors you need:

Palette of colors

And the texture coordinates can be really simplified:

m.getTexCoords().addAll(
        0.1f, 0.5f, // 0 red
        0.3f, 0.5f, // 1 green
        0.5f, 0.5f, // 2 blue
        0.7f, 0.5f, // 3 yellow
        0.9f, 0.5f  // 4 orange
);

Finally, we have to map those points in our faces. Following the same pattern as the net image:

m.getFaces().addAll(
        1, 0, 11, 0, 7, 0, 
        1, 4, 7, 4, 6, 4, 
        1, 4, 6, 4, 10, 4, 
        1, 2, 10, 2, 3, 2, 
        1, 2, 3, 2, 11, 2,                
        4, 3, 8, 3, 0, 3, 
        5, 3, 4, 3, 0, 3, 
        9, 1, 5, 1, 0, 1, 
        2, 1, 9, 1, 0, 1, 
        8, 0, 2, 0, 0, 0, 

        11, 3, 9, 3, 7, 3,
        7, 1, 2, 1, 6, 1, 
        6, 1, 8, 1, 10, 1, 
        10, 0, 4, 0, 3, 0, 
        3, 0, 5, 0, 11, 0,

        4, 4, 10, 4, 8, 4, 
        5, 4, 3, 4, 4, 4, 
        9, 2, 11, 2, 5, 2, 
        2, 2, 7, 2, 9, 2, 
        8, 3, 6, 3, 2, 3
);

Now we’ll have a very neat icosahedron, since we get rid of the borders and bad resolution of the image:

Improved icosahedron

This can be extended to any triangle mesh, or use any algorithm to refine the triangles.

Leave a Comment