Programmation JavaFX/Formes 3D
Une scène en 3D est composée de formes 3D, de source lumineuses et d'une caméra définissant le point de vue de la scène.
Coordonnées et orientation des axes 3D.
Les coordonnées d'un point ou d'une forme est un ensemble de 3 valeurs de type codice_1, nommées X, Y et Z.
L'orientation par défaut des axes est le suivant :
Classe de base.
La classe codice_2 est la classe de base des formes 3D.
Elle définit les méthodes communes à toutes les formes, permettant de les placer/déplacer et de les orienter.
La position initiale à la création d'une forme 3D est (0,0,0).
La classe codice_2 a 4 implémentations concrètes :
Les sous-classes présentées ci-dessous ont des caractéristiques communes :
Sphère (classe Sphere).
Les paramètres définissant une sphère sont les suivants (même ordre que les constructeurs de la classe) :
Exemple.
Sphere sphere = new Sphere(100);
Cylindre (classe Cylinder).
Les paramètres définissant un cylindre sont les suivants (même ordre que les constructeurs de la classe) :
L'orientation initiale du cylindre suit l'axe Y ; les faces circulaires sont donc perpendiculaire à l'axe Y.
Exemple.
Cylinder cyl = new Cylinder(40, 220);
cyl.setTranslateY(220);
Boîte (classe Box).
Les paramètres définissant une boîte sont les suivants (même ordre que les constructeurs de la classe) :
Exemple.
Box box = new Box(100, 200, 100);
box.setTranslateX(250);
box.setTranslateY(220);
Forme triangulée.
Une forme triangulée est définie avec un objet de class codice_10.
Le seul constructeur à appeler n'a aucun paramètres.
Il faut ensuite remplir les listes de points, de faces triangulaires et les points de texture.
Méthodes de la classe codice_10:
Exemple.
TriangleMesh pyramid_mesh = new TriangleMesh();
// Pas de texture dans cet exemple, mais un point est nécessaire => (0,0)
pyramid_mesh.getTexCoords().addAll(0, 0); // --> T[]
float h = 150; // Height
float s = 300; // Side
pyramid_mesh.getPoints().addAll( // --> P[]
// X Y Z
 0, 0, 0, // Point 0 - Top
 0, h, -s/2, // Point 1 - Front
 -s/2, h, 0, // Point 2 - Left
 0, h, s/2, // Point 3 - Back
 s/2, h, 0 ); // Point 4 - Right
pyramid_mesh.getFaces().addAll( // --> F[] = index in P[],T[]
// P,T P,T P,T
 0,2, 2,4, 1,3, // Front left face
 0,2, 1,4, 4,3, // Front right face
 0,2, 4,4, 3,3, // Back right face
 0,2, 3,4, 2,3, // Back left face
 3,2, 1,3, 2,0, // Bottom rear face
 3,2, 4,1, 1,4 ); // Bottom front face
// Regrouper les deux derniers triangles constituant la base carrée de la pyramide
pyramid_mesh.getFaceSmoothingGroups().addAll(0,1,2,3,4,4);
Il faut ensuite encapsuler la triangulation dans une forme :
MeshView pyramid = new MeshView(pyramid_mesh);
pyramid.setDrawMode(DrawMode.FILL);
pyramid.setTranslateY(-150-h);
Sources de lumière.
Par défaut, la scène n'a aucune source lumineuse. Le rendu serait donc de la couleur de fond de la scène.
Une source lumineuse a une couleur filtrant celle des objets, une intensité, et une position.
JavaFX a deux types de sources lumineuses :
Il faut également définir pour chaque source lumineuse, la liste des objets éclairés par celle-ci.
Exemple.
Node[] allnodes = { sphere, cyl, pyramid, box };
Color light_color = Color.LIGHTGREY;
AmbientLight light = new AmbientLight(light_color);
light.setTranslateX(-180);
light.setTranslateY(-90);
light.setTranslateZ(-120);
light.getScope().addAll(allnodes);
PointLight light2 = new PointLight(light_color);
light2.setTranslateX(+180);
light2.setTranslateY(+190);
light2.setTranslateZ(+180);
light2.getScope().addAll(allnodes);
Caméra.
La caméra (classe codice_35) représente le point de vue d'affichage de la scène : position et orientation.
Pour une représentation en 3D, la sous-classe codice_36 est utilisée.
// Rotation et relocalisation de la caméra
Rotate rotate_x = new Rotate(0, Rotate.X_AXIS);
Rotate rotate_y = new Rotate(0, Rotate.Y_AXIS);
Translate translate = new Translate(0, 0, -1000);
PerspectiveCamera camera = new PerspectiveCamera(true);
camera.setNearClip(0.1); // Afficher pour une distance entre 0.1
camera.setFarClip(10000.0); // ... et 10000
camera.getTransforms().addAll(rotate_x, rotate_y, translate);
Construction de la scène.
La scène est construite en spécifiant le nœud racine regroupant les éléments de la scène.
Exemple.
Il ne manque que la scène constituée des éléments vus précédemment.
Group root = new Group(allnodes);
root.getChildren().addAll(light, light2);
Scene scene = new Scene(root, 600, 600, true); // true to use depth buffer
scene.setFill(Color.BLACK); // Couleur de fond par défaut
scene.setCamera(camera);
// Affichage de la scène dans la fenêtre de l'application
stage.setTitle("3D JavaFX");
stage.setScene(scene);
stage.show();
Code complet.
package org.wikibooks.fr.javafx;
import javafx.application.*;
import javafx.scene.*;
import javafx.scene.effect.*;
import javafx.scene.paint.*;
import javafx.scene.shape.*;
import javafx.scene.transform.*;
import javafx.stage.*;
public class ExempleFormes3D extends Application
 // Transformations pour la caméra
 private final Rotate rotate_x = new Rotate(0, Rotate.X_AXIS);
 private final Rotate rotate_y = new Rotate(0, Rotate.Y_AXIS);
 private final Translate translate = new Translate(0, 0, -1000);
 @Override
 public void start(Stage stage)
 // Coordonnées de base
 int dx = 0, dy = 0, dz = 0;
 // Sphère
 Sphere sphere = new Sphere(100);
 sphere.setTranslateX(dx);
 sphere.setTranslateY(dy);
 sphere.setTranslateZ(dz);
 sphere.setCursor(Cursor.OPEN_HAND);
 // Boîte
 Box box = new Box(100, 200, 100);
 box.setTranslateX(dx + 250);
 box.setTranslateY(dy + 220);
 box.setTranslateZ(dz);
 box.setCursor(Cursor.OPEN_HAND);
 // Cylindre
 Cylinder cyl = new Cylinder(40, 220);
 cyl.setTranslateX(dx);
 cyl.setTranslateY(dy + 220);
 cyl.setTranslateZ(dz);
 cyl.setCursor(Cursor.OPEN_HAND);
 // Pyramide à base carrée : par triangulation
 TriangleMesh pyramid_mesh = new TriangleMesh(); // VertexFormat.POINT_TEXCOORD => P,T
 pyramid_mesh.getTexCoords().addAll(0, 0); // --> T[]
 pyramid_mesh.getTexCoords().addAll(1.0F, 0); // --> T[]
 pyramid_mesh.getTexCoords().addAll(0.515F, 0.02F); // --> T[]
 pyramid_mesh.getTexCoords().addAll(1.0F, 1.0F); // --> T[]
 pyramid_mesh.getTexCoords().addAll(0, 1.0F); // --> T[]
 float h = 150; // Height
 float s = 300; // Side
 pyramid_mesh.getPoints().addAll( // --> P[]
 // X Y Z
 0, 0, 0, // Point 0 - Top
 0, h, -s/2, // Point 1 - Front
 -s/2, h, 0, // Point 2 - Left
 0, h, s/2, // Point 3 - Back
 s/2, h, 0 ); // Point 4 - Right
 pyramid_mesh.getFaces().addAll( // --> F[] = index in P[],T[]
 // P,T P,T P,T
 0,2, 2,4, 1,3, // Front left face
 0,2, 1,4, 4,3, // Front right face
 0,2, 4,4, 3,3, // Back right face
 0,2, 3,4, 2,3, // Back left face
 3,2, 1,3, 2,0, // Bottom rear face
 3,2, 4,1, 1,4 ); // Bottom front face
 pyramid_mesh.getFaceSmoothingGroups().addAll(0,1,2,3,4,4);
 MeshView pyramid = new MeshView(pyramid_mesh);
 pyramid.setDrawMode(DrawMode.FILL);
 pyramid.setTranslateX(dx);
 pyramid.setTranslateY(dy-150-h);
 pyramid.setTranslateZ(dz);
 pyramid.setEffect(new Shadow());
 // Tous les éléments à éclairer
 Node[] allnodes = { sphere, cyl, pyramid, box };
 // Sources lumineuses
 Color light_color = Color.LIGHTGREY;
 AmbientLight light = new AmbientLight(light_color);
 light.setTranslateX(dx - 180);
 light.setTranslateY(dy - 90);
 light.setTranslateZ(dz - 120);
 light.getScope().addAll(allnodes);
 PointLight light2 = new PointLight(light_color);
 light2.setTranslateX(dx + 180);
 light2.setTranslateY(dy + 190);
 light2.setTranslateZ(dz + 180);
 light2.getScope().addAll(allnodes);
 // Nœud racine regroupant tous les éléments
 Group root = new Group(allnodes);
 root.getChildren().addAll(light, light2); // et les sources lumineuses
 Scene scene = new Scene(root, 600, 600, true); // true to use depth buffer
 scene.setFill(Color.BLACK);
 // Point de vue de la scène
 PerspectiveCamera camera = new PerspectiveCamera(true);
 camera.setNearClip(0.1);
 camera.setFarClip(10000.0);
 camera.getTransforms().addAll(rotate_x, rotate_y, translate);
 scene.setCamera(camera);
 stage.setTitle("3D JavaFX");
 stage.setScene(scene);
 stage.show();
 public static void main(String[] args)
 Application.launch(args);
L'apparence des objets sera améliorée dans le chapitre suivant.