Issue
I have a triangle mesh object in JAVAFX and would like to either
- color individual triangles of the triangle mesh
or
- color individual vertices of each triangle and have the triangle be colored according to an interpolation of the colors of each vertex (say for instance with a Gouraud shading).
The particular triangle mesh object is an icosphere with millions of faces (that is why I am using a triangle mesh: I need speed).
I have NOT used texture coordinates since I have been unable to find a clear explanation to it using JAVAFX, plus I am hoping that there is a easier way.
Solution
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:
(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:
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:
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:
This can be extended to any triangle mesh, or use any algorithm to refine the triangles.
Answered By - José Pereda
Answer Checked By - Marie Seifert (JavaFixing Admin)