Compteur :
Java 3D - Chapitre 4 : Transformations géométriques

Retour table des matières Java 3D

 

En programmation 3D en général, les transformations géométriques quelconques se calculent à l'aide de matrices. La librairie Java 3D fournit la classe Transform3D qui permet de réaliser n'importe quel type de transformations géométriques, de la plus simple à la plus complexe. Il faut savoir que les transformations géométriques sont toujours associées à un nœud de type TransformGroup dans l'arborescence d'une scène 3D.
La classe Transform3D permet également de définir des transformations simples sans avoir à utiliser explicitement les matrices : les translations, les rotations et les homothéties. Les homothéties sont aussi appelées transformations d'échelle (scale en anglais).

Dans ce chapitre nous allons étudier les transformations simples que nous venons d’énumérer ainsi que des exemples de transformations multiples (combinaison de plusieurs transformations simples) : les rotations multiples et les combinaisons d’une translation et d'une rotation.

 

 

1. La classe Transform3D

 

Les transformations que l’on peut construire à partir de la classe Transform3D sont toujours relatives au centre du repère 3D. Ceci est un point fondamental, il ne faut jamais l’oublier sinon les mauvaises surprises seront nombreuses surtout en ce qui concerne les transformations multiples.

 

public Transform3D()
Construit une transformation identité, c’est à dire une transformation qui n’a aucun effet sur l’objet pour lequel elle est appliquée

 

public final void get(Matrix3f m)
Récupère la composante rotation de la transformation. Cette composante est stockée dans la matrice m.

public final void get(Vector3f trans)
Récupère la composante translation de la transformation. Cette composante est stockée dans le vecteur trans.

public final getScale(Vector3d scale)
Récupère les composantes d'échelle (homothétie uniforme ou pas) de cette transformation et les stocke dans le vecteur scale.

public final void mul(Transform3D t1)
Remplace la transformation courante par cette transformation courante que multiplie la transformation t1 (this = this * t1). t1 est d'abord appliquée suivie de la transformation courante.

public final void mul(Transform3D t1, Transform3D t2)
Remplace la transformation courante par le produit des transformations t1 et t2 (this = t1 * t2). t2 est d'abord appliquée suivie par t1.

public void rotX(double angle)
Modifie la transformation courante en une rotation directe (sens inverse des aiguilles d'une montre) d'angle angle radians autour de l'axe X de repère 3D. Les autres composantes de la transformation courante (par exemple une translation) sont supprimées, c'est-à-dire que chaque appel à rotX() annule la transformation courante à laquelle est éventuellement soumis un objet.

public void rotY(double angle)
Modifie la transformation courante en une rotation directe (sens inverse des aiguilles d'une montre) d'angle angle radians autour de l'axe Y de repère 3D. Les autres composantes de la transformation courante (par exemple une translation) sont supprimées, c'est-à-dire que chaque appel à rotY() annule la transformation courante à laquelle est éventuellement soumis un objet.

public void rotZ(double angle)
Modifie la transformation courante en une rotation directe (sens inverse des aiguilles d'une montre) d'angle angle radians autour de l'axe Z de repère 3D. Les autres composantes de la transformation courante (par exemple une translation) sont supprimées, c'est-à-dire que chaque appel à rotZ() annule la transformation courante à laquelle est éventuellement soumis un objet.
On se reportera au chapitre consacré aux bases de la construction d'une application Java 3D pour plus d'informations sur l'orientation du repère 3D.

public final void set(AxisAngle4f a1)
Supprime la transformation courante et la remplace par une rotation autour de l’axe a1 centré sur l’origine du repère 3D.

public final void set(Vector3f trans)
Supprime la transformation courante et la remplace par une translation dont les caractéristiques sont définies par le vecteur trans.

public final void setIdentity()
Supprime la transformation courante en la remplaçant par une matrice identité.

public final void setRotation(AxisAngle a1)
Remplace la composante rotation de la transformation courante en une rotation autour de l'axe a1. Les autres composantes éventuellement existantes de la transformation courante ne sont pas modifiées.

public final void setScale(Vector3d scale)
Applique un facteur d'échelle (homothétie) à la transformation courante. Les composantes de cette homothétie sont stockées dans le vecteur scale. Les autres composantes de la transformation courante ne sont pas modifiées. Si la transformation courante possède déjà un facteur d'échelle, il est supprimé et remplacé par le nouveau facteur entré par la méthode setScale().

public final void setTranslation(Vector3f trans)
Remplace la composante translation de la transformation courante en une translation dont les composantes sont stockées dans le vecteur trans. Les autres composantes éventuellement existantes de la transformation courante ne sont pas modifiées.

Attention : différences entre les transformations rotX(), rotY(), rotZ(), set(), setRotation() et setTranslation()

Les méthodes rotX(), rotY(), rotZ() et set() annulent la transformation courante avant de s'appliquer.
La méthode setTranslation() ne remplace que la composante translation de la transformation courante avant de s’appliquer, les autres composantes de la transformation ne sont pas modifiées.
La méthode setRotation() ne remplace que la composante rotation de la transformation courante avant de s'appliquer, les autres composantes de la transformation ne sont pas modifiées.

 

2. Transformations simples

 

Nous désignons par transformation simple les transformations uniques (ou élémentaires) de type translation, rotation ou homothétie.

 

La méthode de la classe Transform3D qui permet de réaliser des translations dans le repère 3D est setTranslation(Vector3f translation).

 

Voici un exemple où nous décrivons les étapes à suivre pour soumettre un objet de type ColorCube à une transformation de type translation dont le vecteur est v = (0.5, 0.5, 0)

  1. Création de la transformation 3D de type translation

Transform3D translation = new Transform3D();
translation.setTranslation(new Vector3f(0.5f, 0.5f, 0f));

 

  1. Association de la translation au noeud de type TransformGroup

TransformGroup transformGroup = new TransformGroup(translation);

 

  1. On ajoute l'objet que l'on veut transformer (ColorCube) au nœud TransformGroup

ColorCube cube = new ColorCube( 0.3f);
transformGroup.addChild(cube);

Translation (0.5, 0.5, 0) d'un objet ColorCube

 

Le fichier Translation.java présente le code source complet de cet exemple :

// Etape 1 :
// Importation des packages Java 2
import java.applet.Applet;
import java.awt.*;

// Etape 2 :
// Importation des packages Java 3D
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.universe.*;
import com.sun.j3d.utils.geometry.*;
import javax.media.j3d.*;
import javax.vecmath.*;

public class Translation extends Applet {

  public Translation() {
    this.setLayout(new BorderLayout());

    // Etape 3 :
    // Creation du Canvas 3D
    Canvas3D canvas3D = new Canvas3D(SimpleUniverse.getPreferredConfiguration());
    this.add(canvas3D, BorderLayout.CENTER);

    // Etape 4 :
    // Creation d'un objet SimpleUniverse
    SimpleUniverse simpleU = new SimpleUniverse(canvas3D);

    // Etape 5 :
    // Positionnement du point d'observation pour avoir une vue correcte de la
    // scene 3D
    simpleU.getViewingPlatform().setNominalViewingTransform();

    // Etape 6 :
    // Creation de la scene 3D qui contient tous les objets 3D que l'on veut
    // visualiser
    BranchGroup scene = createSceneGraph();

    // Etape 7 :
    // Compilation de la scene 3D
    scene.compile();

    // Etape 8:
    // Attachement de la scene 3D a l'objet SimpleUniverse
    simpleU.addBranchGraph(scene);
  }

  /**
   * Creation de la scene 3D
   * @return scene 3D
   */
  public BranchGroup createSceneGraph() {
    // Creation de l'objet racine (parent) qui contiendra tous les autres
    // noeuds
    BranchGroup parent = new BranchGroup();

    // Creation de la transformation (translation)
    Transform3D translation = new Transform3D();
    translation.setTranslation(new Vector3f(0.5f, 0.5f, 0f));

    // Creation du groupe qui va contenir la transformation
    TransformGroup transformGroup = new TransformGroup(translation);

    // Creation de l'objet pour lequel on va appliquer cette transformation
    // (translation)
    ColorCube cube = new ColorCube(0.3f);

    // Le cube est fils du TransformGroup qui contient la translation :
    // on l'ajoute donc a l'objet TransformGroup
    transformGroup.addChild(cube);

    // L'objet TransformGroup est le fils de l'objet racine (BranchGroup)
    parent.addChild(transformGroup);

    return parent;
  }

  /**
   * Etape 9 :
   * Methode main() nous permettant d'utiliser cette classe comme une applet
   * ou une application.
   * @param args
   */
  public static void main(String[] args) {
    Frame frame = new MainFrame(new Translation(), 256, 256);
  }
}

Exécutez l'applet Translation.html pour voir le résultat.

 

Nous allons étudier deux types de rotations : les rotations autour d’un axe X, Y ou Z du repère 3D et les rotations autour d’un axe quelconque centré en (0, 0, 0). Dans ces deux cas de figures, les rotations sont toujours relatives au centre du repère 3D.

 

Rotations autour d'un axe X, Y ou Z du repère 3D

Voici un exemple ou nous décrivons les étapes à suivre pour soumettre un objet de type ColorCube à une transformation de type rotation de 30 degrés autour de l’axe Y du repère 3D.

  1. Création de la transformation 3D de type rotation

Il faut utiliser la méthode rotY() pour effectuer la rotation autour de l’axe Y. Nous rappelons que les méthodes rotX(), rotY() et rotZ() annulent la transformation courante à laquelle l’objet était éventuellement soumis avant de lui appliquer la rotation. Enfin, il ne faut surtout pas oublier de convertir les 30 degrés en radians.

Transform3D rotation = new Transform3D();
rotation.rotY( 30f * Math.PI/180f);

 

  1. Association de la rotation au noeud de type TransformGroup

TransformGroup transformGroup = new TransformGroup(rotation);

 

  1. On ajoute l'objet que l'on veut transformer (ColorCube) au nœud TransformGroup

ColorCube cube = new ColorCube( 0.3f);
transformGroup.addChild(cube);

 

Remarque : rotations autour des axes X ou Z

Cet exemple est bien sûr parfaitement transposable au cas des rotations autour de l’axe X ou Z du repère 3D. Il suffit pour cela de remplacer la méthode rotY() par rotX() ou rotZ().

 

Rotation d'un objet ColorCube autour de l’axe Y

 

Le fichier Rotation.java présente le code source complet de cet exemple. Nous en avons extrait le listing de la méthode createSceneGraph().

/**
 * Creation de la scene 3D
 * @return scene 3D
 */
public BranchGroup createSceneGraph() {
  // Creation de l'objet racine (parent) qui contiendra tous les autres
  // noeuds
  BranchGroup parent = new BranchGroup();

  // Creation de la transformation (rotation de 30 degres autour de l'axe X)
  Transform3D rotation = new Transform3D();
  rotation.rotY(30f * Math.PI/180f);

  // Creation du groupe qui va contenir la transformation (rotation)
  TransformGroup transformGroup = new TransformGroup(rotation);

  // Creation de l'objet pour lequel on va appliquer cette transformation
  // (rotation)
  ColorCube cube = new ColorCube(0.3f);

  // Le cube est fils de l'objet transformGroup qui contient la rotation :
  // on l'ajoute donc a l'objet TransformGroup
  transformGroup.addChild(cube);

  // L'objet transformGroup est le fils de l'objet racine (BranchGroup)
  parent.addChild(transformGroup);

  return parent;
}

Exécutez l'applet Rotation.html pour voir le résultat.

 

Rotations autour d'un axe quelconque centré en (0, 0, 0)

A présent nous allons appliquer à notre objet ColorCube une rotation de 45 degrés et dont l’axe est orienté selon le vecteur v = (1, 1, 0). Cette rotation est centrée sur l’origine du repère 3D.

  1. Création de la transformation 3D

Il faut tout d’abord créer l’axe de rotation qui est un objet de type AxisAngle4f. Ensuite nous utilisons la méthode set() de la classe Transform3D pour créer une rotation autour de cet axe. Nous rappelons que la méthode set() annule la transformation courante à laquelle l’objet était éventuellement soumis avant de lui appliquer la rotation. Enfin, il ne faut surtout pas oublier de convertir les 45 degrés en radians.

Transform3D rotation = new Transform3D();
AxisAngle4f axis = new AxisAngle4f(1, 1, 0, (float)( 45f * Math.PI/180f));
rotation.set(axis);

 

  1. Association de la rotation au noeud de type TransformGroup

TransformGroup transformGroup = new TransformGroup(rotation);

 

  1. On ajoute l'objet que l'on veut transformer (ColorCube) au nœud TransformGroup

ColorCube cube = new ColorCube( 0.3f);
transformGroup.addChild(cube);

 

Rotation d'un objet ColorCube autour d’un axe quelconque

 

Le fichier Rotation2.java présente le code source complet de cet exemple. Nous en avons extrait le listing de la méthode createSceneGraph() :

 

/**
 * Creation de la scene 3D
 * @return scene 3D
 */
public BranchGroup createSceneGraph() {
  // Creation de l'objet racine (parent) qui contiendra tous les autres
  // noeuds
  BranchGroup parent = new BranchGroup();

  // Creation de la transformation (rotation de 45 degres autour de
  // l'axe (1, 1, 0)
  Transform3D rotation = new Transform3D();
  AxisAngle4f axis = new AxisAngle4f(1, 1, 0, (float)(45f * Math.PI/180f));
  rotation.set(axis);

  // Creation du groupe qui va contenir la transformation (rotation)
  TransformGroup transformGroup = new TransformGroup(rotation);

  // Creation de l'objet pour lequel on va appliquer cette transformation
  // (rotation)
  ColorCube cube = new ColorCube(0.3f);

  // Le cube est fils du TransformGroup qui contient la rotation :
  // on l'ajoute donc a l'objet TransformGroup
  transformGroup.addChild(cube);

  // L'objet TransformGroup est le fils de l'objet racine (BranchGroup)
  parent.addChild(transformGroup);

  return parent;
}

Exécutez l'applet Rotation2.html pour voir le résultat.

 

Il existe deux sortes d’homothéties (ou transformations d’échelle) :
les homothéties uniformes, c’est à dire celles qui appliquent le même facteur d’échelle pour les trois dimensions X, Y et Z de l’objet concerné, et les homothéties non uniformes pour lesquelles le facteur d’échelle n’est pas identique pour les trois dimensions.

 

Homothéties uniformes

Nous allons expliquer en détails la procédure à suivre pour appliquer à un objet 3D de type ColorCube une homothétie uniforme qui va réduire la taille de l’objet.

  1. Création de la transformation 3D de type homothétie uniforme

Une homothétie uniforme peut se construire à partir de la méthode setScale() de la classe Transform3D.

Transform3D homothetie = new Transform3D();
homothetie.setScale( 0.2f);

 

  1. Association de la rotation au noeud de type TransformGroup

TransformGroup transformGroup = new TransformGroup( homothetie);

 

  1. On ajoute l'objet que l'on veut transformer (ColorCube) au nœud TransformGroup

ColorCube cube = new ColorCube( 0.3f);
transformGroup.addChild(cube);

 

Homothétie uniforme sur un objet ColorCube

 

Le fichier Homothetie.java présente le code source complet de cet exemple. Nous en avons extrait le listing de la méthode createSceneGraph() :

 

/**
 * Creation de la scene 3D
 * @return scene 3D
 */
public BranchGroup createSceneGraph() {
  // Creation de l'objet racine (parent) qui contiendra tous les autres
  // noeuds
  BranchGroup parent = new BranchGroup();

  // Creation de la transformation (homothetie)
  Transform3D homothetie = new Transform3D();
  homothetie.setScale(0.2f);

  // Creation du groupe qui va contenir la transformation
  TransformGroup transformGroup = new TransformGroup(homothetie);

  // Creation de l'objet pour lequel on va appliquer cette transformation
  // (homothetie)
  ColorCube cube = new ColorCube(0.3f);

  // Le cube est fils du TransformGroup qui contient l'homothetie :
  // on l'ajoute donc a l'objet TransformGroup
  transformGroup.addChild(cube);

  // L'objet TransformGroup est le fils de l'objet racine (BranchGroup)
  parent.addChild(transformGroup);

  return parent;
}

Exécutez l'applet Homothetie.html pour voir le résultat.

 

Homothéties non uniformes

Pour créer des homothéties non uniformes, il suffit d’utiliser un vecteur comme paramètre de la méthode setScale() :

homothetie.setScale(new Vector3d(0.2, 1.5, 1.5));

Cette homothétie appliquera à l’objet un facteur de réduction d’échelle de 0.2 dans la dimension X et un facteur d’agrandissement d’échelle de 1.5 dans les dimensions Y et Z.

 

Homothétie non uniforme sur un objet ColorCube

 

3. Transformations multiples

 

Les transformations multiples sont constituées de plusieurs transformations simples ou élémentaires qui sont effectuées les unes à la suite des autres sur le même objet.
Nous étudierons le cas des transformations multiples qui ne comportent que deux transformations élémentaires consécutives.
Il existe deux approches permettant de réaliser des transformations successives :
La première consiste à combiner les transformations à l’aide de la méthode mul() et à n’utiliser qu’un seul groupe de type TransformGroup.
La seconde consiste à utiliser deux groupes de transformation TransformGroup contenant chacun une unique transformation.
Nous aborderons ces deux approches différentes dans les paragraphes qui suivent.

 

Pour illustrer ce paragraphe, nous allons utiliser un objet 3D de type ColorCube auquel nous allons d’abord faire subir une rotation de 45 degrés autour de l’axe X du repère puis nous allons ensuite lui appliquer une deuxième rotation de 45 degrés autour de l’axe Y du repère.
Nous allons construire un objet de type TransformGroup qui est constitué des deux rotations successives. Ces deux rotations vont être combinées (on dit aussi multipliées) à l’aide de la méthode mul().

  1. Création de la transformation 3D correspondant à la première rotation

Il s’agit de la rotation de 45 degrés autour de l’axe X du repère 3D

Transform3D rotationX = new Transform3D();
rotationX.rotX( 45f * Math.PI/180f);

 

  1. Création de la transformation 3D correspondant à la deuxième rotation

Il s’agit de la rotation de 45 degrés autour de l’axe Y du repère 3D

Transform3D transform = new Transform3D();
transform.rotY( 45f * Math.PI/180f);

 

  1. Combinaison (ou multiplication) des deux rotations

La rotation autour de l’axe X est effectuée en premier.

transform.mul(rotationX);

 

Attention : ordre d’exécution des rotations

L’ordre dans lequel on applique les rotations à notre objet 3D est primordial. En effet, les rotations comme les transformations en général ne sont pas commutatives. Une rotation autour de X suivie d’une autre autour de Y ne donne absolument pas le même résultat qu’une rotation autour de Y suivie d’une autre autour de X.
En effet, deux rotations successives se calculent en mathématiques à l’aide du produit des matrices correspondant à chaque rotation, or le produit matriciel n’est pas commutatif.

A ce stade, l’objet transform de type Transform3D est donc la combinaison (ou multiplication) des deux rotations successives.

Remarque :

Il n’est pas possible de combiner les deux rotations successives en appliquant la méthode rotX() suivie de la méthode rotY() car nous avons vu que ces méthodes suppriment totalement la transformation en cours. Autrement dit, rotX() suivie de rotY() reviendrait à n’effectuer que la rotation rotY().

 

  1. Création du groupe (TransformGroup) qui va contenir la transformation globale

TransformGroup transformGroup = new TransformGroup(transform);

 

  1. On ajoute l'objet que l'on veut transformer (ColorCube) au nœud TransformGroup

ColorCube cube = new ColorCube( 0.3f);
transformGroup.addChild(cube);

 

Rotations multiples sur un objet ColorCube

 

Le fichier RotationsMultiples.java présente le code source complet de cet exemple. Nous en avons extrait le listing de la méthode createSceneGraph() :

 

/**
 * Creation de la scene 3D
 * @return scene 3D
 */
  public BranchGroup createSceneGraph() {
  // Creation de l'objet racine (parent) qui contiendra tous les autres
  // noeuds
  BranchGroup parent = new BranchGroup();

  // Creation de la premiere rotation de 45 degres autour de l'axe X
  Transform3D rotationX = new Transform3D();
  rotationX.rotX(45f * Math.PI/180f);

  // Creation de la deuxieme rotation de 45 degres autour de l'axe Y
  Transform3D transform = new Transform3D();
  transform.rotY(45f * Math.PI/180f);

  // Cumul des deux rotations (rotation de 45 degres autour de l'axe X
  // d'abord puis rotation de 45 degres autour de l'axe Y ensuite)
  transform.mul(rotationX);

  // Creation du groupe qui va contenir la transformation globale
  TransformGroup transformGroup = new TransformGroup(transform);

  // Creation de l'objet pour lequel on va appliquer cette transformation
  // (double rotation)
  ColorCube cube = new ColorCube(0.3f);

  // Le cube est fils du TransformGroup qui contient la transformation :
  // on l'ajoute donc a l'objet TransformGroup
  transformGroup.addChild(cube);

  // L'objet TransformGroup est le fils de l'objet racine (BranchGroup)
  parent.addChild(transformGroup);

  return parent;
}

Exécutez l'applet RotationsMultiples.html pour voir le résultat.

 

Nous allons cette fois-ci appliquer consécutivement deux transformations différentes à notre objet ColorCube : une rotation suivie d’une translation. Tout comme l’exemple précédent, nous n’allons utiliser qu’un seul un objet de type TransformGroup qui est constitué des deux transformations successives. Ces deux transformations vont être combinées (on dit aussi multipliées) à l’aide de la méthode mul().
La première transformation est une rotation de 30 degrés autour de l’axe Z du repère 3D et la seconde une translation dont les caractéristiques sont définies par le vecteur v = (0.5, 0.5, 0)

 

  1. Création de la transformation 3D correspondant à la rotation

Il s’agit d’une rotation de 30 degrés autour de l’axe Z du repère 3D

Transform3D rotation = new Transform3D();
rotation.rotZ( 30f * Math.PI/180f);

 

  1. Création de la transformation 3D correspondant à la translation

Il s’agit d’une translation de vecteur v = (0.5, 0.5, 0) 

Transform3D transform = new Transform3D();
transform.setTranslation(new Vector3f(0.5f, 0.5f, 0f));

 

  1. Combinaison (ou multiplication) des deux transformations

La rotation autour de l’axe Z est effectuée en premier. Le résultat serait tout autre si la translation était effectuée en premier.

transform.mul(rotation);

A ce stade, l’objet transform de type Transform3D est donc la combinaison (ou multiplication) des deux transformations successives.

 

  1. Création du groupe (TransformGroup) qui va contenir la transformation globale

TransformGroup transformGroup = new TransformGroup(transform);

 

  1. On ajoute l'objet que l'on veut transformer (ColorCube) au nœud TransformGroup

ColorCube cube = new ColorCube( 0.2f);
transformGroup.addChild(cube);

 

Rotation suivie d’une translation sur un objet ColorCube

 

Le fichier RotationTranslation.java présente le code source complet de cet exemple. Nous en avons extrait le listing de la méthode createSceneGraph() :

 

/**
 * Creation de la scene 3D
 * @return scene 3D
 */
public BranchGroup createSceneGraph() {
  // Creation de l'objet racine (parent) qui contiendra tous les autres
  // noeuds
  BranchGroup parent = new BranchGroup();

  // Creation de la premiere transformation (rotation de 30 degres
  // autour de Z)
  Transform3D rotation = new Transform3D();
  rotation.rotZ(30f * Math.PI/180f);

  // Creation de la deuxieme transformation (translation)
  Transform3D transform = new Transform3D();
  transform.setTranslation(new Vector3f(0.5f, 0.5f, 0f));

  // Cumul des deux rotations (rotation d'abord, translation ensuite)
  transform.mul(rotation);

  // Creation du groupe qui va contenir la transformation globale :
  // rotation + translation
  TransformGroup transformGroup = new TransformGroup(transform);

  // Creation de l'objet pour lequel on va appliquer cette transformation
  // globale
  ColorCube cube = new ColorCube(0.2f);

  // Le cube est fils de l'objet transformGroup qui contient la
  // transformation globale : on l'ajoute donc a l'objet transformGroup
  transformGroup.addChild(cube);

  // L'objet transformGroup est le fils de l'objet racine (BranchGroup)
  parent.addChild(transformGroup);

  return parent;
}

Exécutez l'applet RotationTranslation.html pour voir le résultat.

 

Nous allons maintenant appliquer la même transformation globale que dans l’exemple précédent mais eu utilisant cette fois ci deux objets de type TransformGroup contenant chacun une unique transformation. Pour rappel, l’exemple précédent faisait appel à un seul objet TransformGroup contenant la combinaison des deux transformations.

 

  1. Création de la transformation 3D correspondant à la rotation de 30 degrés autour de l’axe Z ainsi que de l’objet TransformGroup associé.

Transform3D rotation = new Transform3D();
rotation.rotZ( 30f * Math.PI/180f);
TransformGroup rotationGroup = new TransformGroup(rotation);

 

  1. Création de la transformation 3D correspondant à la translation de vecteur v = (0.5, 0.5, 0) ainsi que de l’objet TransformGroup associé.

Transform3D translation = new Transform3D();
translation.setTranslation(new Vector3f(0.5f, 0.5f, 0f));
TransformGroup translationGroup = new TransformGroup(translation);

 

  1. Le cube est fils du premier groupe rotationGroup puisque l’on veut appliquer la rotation en premier

ColorCube cube = new ColorCube( 0.2f);
rotationGroup.addChild(cube);

 

  1. La translation est effectuée à la suite de la rotation, le groupe rotationGroup doit donc être fils du groupe translationGroup

translationGroup.addChild(rotationGroup);

 

A ce stade, l’objet translationGroup de type Transform3D est donc la combinaison (ou multiplication) des deux transformations successives.

Le fichier RotationTranslation2.java présente le code source complet de cet exemple. Nous en avons extrait le listing de la méthode createSceneGraph() :

 

/**
 * Creation de la scene 3D
 * @return scene 3D
 */
public BranchGroup createSceneGraph() {
  // Creation de l'objet racine (parent) qui contiendra tous les autres
  // noeuds
  BranchGroup parent = new BranchGroup();

  // Creation de la premiere transformation (rotation de 30 degres
  // autour de Z)
  Transform3D rotation = new Transform3D();
  rotation.rotZ(30f * Math.PI/180f);
  TransformGroup rotationGroup = new TransformGroup(rotation);

  // Creation de la deuxieme transformation (translation)
  Transform3D translation = new Transform3D();
  translation.setTranslation(new Vector3f(0.5f, 0.5f, 0f));
  TransformGroup translationGroup = new TransformGroup(translation);

  // Creation de l'objet pour lequel on va appliquer cette transformation
  // (rotation)
  ColorCube cube = new ColorCube(0.2f);

  // Le cube est fils du rotationGroup qui contient la rotation puisque
  // l'on veut effectuer la rotation en premier
  rotationGroup.addChild(cube);

  // L'objet rotationGroup est fils de translationGroup puisque l'on fait
  // la translation a la suite de la rotation
  translationGroup.addChild(rotationGroup);

  // L'objet translationGroup est le fils de l'objet racine (BranchGroup)
  parent.addChild(translationGroup);

  return parent;
}

Exécutez l'applet RotationTranslation2.html pour voir le résultat.

 

Voici pour comparaison, l’arborescence des scènes 3D correspondant à la transformation multiple rotation + translation avec un seul puis deux objets de type TransformGroup :

Arborescences comparées

 

Pour terminer ce chapitre, nous allons étudier un troisième moyen de réaliser la transformation multiple étudiée dans les deux paragraphes précédents.
Nous avons expliqué que le résultat serait complètement différent si la translation était d’abord appliquée suivie de la rotation ensuite. Toutefois, nous pouvons effectuer la rotation en dernier à condition que celle-ci doit relative à un axe parallèle à Z et passant par le centre de l’objet ColorCube.
La difficulté du problème réside dans le fait que la rotation n’est pas relative au centre de repère 3D, elle s’effectue par rapport a un axe qui ne passe pas par le centre (0, 0, 0). En conséquence nous ne pouvons utiliser ni rotZ() ni setRotation() car elles sont toutes deux relatives au centre du repère (comme toutes les transformations de la classe Transform3D).
Une méthode consisterait à calculer la matrice de la rotation mais la solution est loin d’être immédiate car cela nécessiterait un certains nombre de calculs mathématiques. Nous allons utiliser une toute autre approche beaucoup plus élégante sans calculs matriciels et n’utilisant que des méthodes de la classe Transform3D.
Il est à noter que cet exemple de rotation non centrée en (0, 0, 0) est généralisable à d’autres transformations autres que les rotations. Il est également généralisable à d’autres cas de figures où la première transformation est une transformation quelconque.

 

  1. Création de la première transformation 3D (translation de vecteur (0.5, 0.5, 0) dans le cas qui nous préoccupe) et de son objet TransformGroup qui lui est associé.

Transform3D translation = new Transform3D();
translation.setTranslation(new Vector3f(0.5f, 0.5f, 0f));
TransformGroup transformGroup = new TransformGroup(translation);

 

  1. Création de la seconde transformation 3D (rotation de 45 degrés autour de axe parallèle à Z mais passant par le centre du cube).

- On récupère la transformation courante (ici il s’agit de la translation effectuée à l’étape 1) :

Transform3D currentTransform = new Transform3D();
transformGroup.getTransform(currentTransform);

- On sauvegarde les composantes de la translation associée à cette transformation dans un vecteur :

Vector3f translationComponent = new Vector3f();
currentTransform.get(translationComponent);

- On recentre notre cube à l’origine du repère 3D, il faut pour cela annuler les composantes de la translation associée à la transformation courante :

currentTransform.setTranslation(new Vector3f(0, 0, 0));

- On applique maintenant la rotation de 30 degrés autour de l’axe Z à notre cube qui est maintenant au centre du repère 3D :

Transform3D rotation = new Transform3D();
rotation.rotZ( 30f * Math.PI/180f);

- On ajoute cette rotation à la suite de la transformation courante :

currentTransform.mul(rotation, currentTransform);

- On repositionne ensuite le cube à sa position d’origine en réintroduisant les composantes de la translation que nous avions supprimée :

currentTransform.setTranslation(translationComponent);

A ce state, la transformation courante est strictement équivalente à celle de notre exemple précédent.

 

  1. On applique cette transformation globale à l’objet transformGroup

transformGroup.setTransform(currentTransform);

 

  1. Enfin, on peut construire notre cube et le positionner en tant que fils de l’objet transformGroup qui contient la transformation globale

ColorCube cube = new ColorCube( 0.2f);
transformGroup.addChild(cube);

Le fichier TranslationRotation.java présente le code source complet de cet exemple. Nous en avons extrait le listing de la méthode createSceneGraph() :

 

/**
 * Creation de la scene 3D
 * @return scene 3D
 */
public BranchGroup createSceneGraph() {
  // Creation de l'objet racine (parent) qui contiendra tous les autres
  // noeuds
  BranchGroup parent = new BranchGroup();

  // Creation de la premiere transformation (translation)
  Transform3D translation = new Transform3D();
  translation.setTranslation(new Vector3f(0.5f, 0.5f, 0f));
  TransformGroup transformGroup = new TransformGroup(translation);

  /**
   * Creation de la seconde transformation (rotation de 45 degres autour
   * d'un axe parallele a Z mais passant par le centre du cube)
   */
  // On recupere la transformation globale courante
  // (ici une simple translation)
  Transform3D currentTransform = new Transform3D();
  transformGroup.getTransform(currentTransform);

  // On sauvegarde les composantes de la translation associee a cette
  // transformation dans un vecteur
  Vector3f translationComponent = new Vector3f();
  currentTransform.get(translationComponent);

  // On annule les composantes de la translation associee a cette
  // transformation globale : cela revient a recentrer notre cube
  // au centre du repere 3D
  currentTransform.setTranslation(new Vector3f(0, 0, 0));

  // On applique la rotation de 30 degres autour de l'axe Z a notre cube
  // qui est maintenant au centre du repere 3D
  Transform3D rotation = new Transform3D();
  rotation.rotZ(30f * Math.PI/180f);

  // Cette rotation est appliquee apres la transformation courante
  currentTransform.mul(rotation, currentTransform);

  // On repositionne ensuite le cube a sa position d'origine en
  // reintroduisant les composantes de la translation que nous avions
  // supprimée
  currentTransform.setTranslation(translationComponent);

  // On applique la transformation globale a l'objet transformGroup
  transformGroup.setTransform(currentTransform);

  // Creation de l'objet pour lequel on va appliquer cette transformation
  // (translation + rotation)
  ColorCube cube = new ColorCube(0.2f);

  // Le cube est fils du transformGroup qui contient la transformation
  // globale
  transformGroup.addChild(cube);

  // L'objet transformGroup est le fils de l'objet racine (BranchGroup)
  parent.addChild(transformGroup);

  return parent;
}

Exécutez l'applet TranslationRotation.html pour voir le résultat.

 

Retour haut de page

Chapitre 03 : Objets 3D << >> Chapitre 05 : Apparence