Manipulation d'une boite en 3D
Mots clés : 3D - Open GL - Java - JOGL - WaspMote - RXTX - Xbee Pro - Libelium
Par Amadou Ada DIENE et Erwann KASSIS,
encadrés par M. Ahcène BOUNCEUR.
Janvier 2013

Introduction

Ce tutorial a été rédigé par Amadou Ada DIENE et Erwann KASSIS, étudiants de 3ième année de Licence en Ingénierie Informatique à l'Université de Bretagne Occidentale, à la suite d'un projet de Janvier 2013, encadré par M. Ahcène BOUNCEUR. Il porte sur la réalisation d'une boite en 3D ainsi qu'à la façon de pouvoir l'animer, notamment avec l'accéléromètre d'un contrôleur sans fil.

Alors si vous avez déjà voulu faire de l'OpenGL en Java, ou utiliser une carte WaspMote de Libelium sans trouver d'explications sur le sujet, ce tutoriel est fait pour vous! Et même si ce dernier ne couvre pas tous les aspects souhaités par le lecteur, il constitue cependant une bonne introduction dans les domaines cités ci-dessus.

Matériel et logiciels requis

Pour suivre ce tutoriel, vous aurez besoin des éléments ci-dessus:

Cartes XBee Pro et WaspMote

Lorsque le gateway sera relié à votre ordinateur, les deux parties du système pourront communiquer et permettre de le traitement des données émises par la carte WaspMote.

EDIs Eclipse et WaspMote

Ces deux logiciels n'ont à priori pas besoin d'être installés, il suffit juste de décompresser les archives téléchargées et lancer les applications. Pour plus d'informations, référez vous à la référence des éditeurs.

APIs JOGL 2.0 et RXTX

Faites attention car il y a plusieurs liens de téléchargements selon l'architecture et/ou le système d'exploitation. Choisissez le bon et reportez vous ici pour leur configuration sur Eclipse.

Configurations nécessaires

Les configurations suivantes sont obligatoires pour que tout fonctionne correctement:

Pilotes sur Windows

Sur Windows, les cartes ne sont reconnus qu'après avoir installé leurs pilotes normalement disponibles dans le dossier "drivers" de l'archive WaspMote que vous avez téléchargé. Pour un autre OS ou plus d'informations, référez vous à la référence des éditeurs.

APIs sur Eclipse

Sur Eclipse, les APIs ne sont utilisables que si elles sont installées parmis les bibliothèques utilisateurs. Ces dernières configurations sont expliquées par les étapes suivantes:

  1. Tout d'abord ouvrez la boite de dialogue Window/Preferences et développez
    l'arborescence Java/Build Path/User Libraries
  2. Cliquez ensuite sur New pour ajouter une nouvelle librairie à nommer en veillant à ce que
    la case System librairy ne soit pas cochée.
  3. Selectionnez la nouvelle librairie créée puis cliquez sur Add External JARs, cela permet
    d'ajouter les fichiers JARs de l'API.
  4. Eventuellement il peut être nécessaire d'indiquer les repertoires contenant les librairies natives (.dll sur Windows). Pour cela, cliquez développez l'arborescence du fichier JAR en question et
    double-cliquez sur Native library location.
  5. Enfin pour pouvoir utiliser une API installée dans un projet accessible via l'explorateur de projet,
    faites un clic droit sur le projet puis selectionner Build Path/Add Libraries ...,
    choisissez User libraries puis l'API souhaitée et c'est bon.
  6. Exemple de configuration, Windows 7 - 64 bits

Réalisation de la scène en 3D

Dans cette partie, nous pourrons enfin commencer le projet. Les codes présentés évoluent au fur et à mesure que l'on avance dans les fonctionnalités. Et c'est parti!

Interface graphique

Tout d'abord il nous faut un support pour afficher notre production. Ce sera essentiellement la classe Main qui comme illustré hérite de frame.

import java.awt.EventQueue;
import java.awt.Frame;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.media.opengl.awt.GLCanvas;

@SuppressWarnings("serial")
public class Main extends Frame{

        // Composant awt, pour le rendu OpenGL (JPanel)
        private GLCanvas canvas;                                                              

        public Main(){
                // Appel du constructeur parent
                super();                                                                                      
                // Spécifie la taille de la fenêtre
                setSize(500,500);
                // Renseigne le titre de la fenetre
                setTitle("Manipulation d'une boite en 3D");                  
                // Permet de centrer la fenetre à l'ouverture
                setLocationRelativeTo(null);                                          
                // Initialisation du canvas
                canvas = new GLCanvas();                                                      

                // Ajout du canvas au frame
                add(canvas);                                                                          
                
                // Adaptater permettant libérer les ressources lors de la fermeture de la fenêtre
                addWindowListener(new WindowAdapter() {                                                                                                                              
                        @Override
                        public void windowClosing(WindowEvent e) {
                                // Fermeture de l'application
                                System.exit(0);
                        }
                });
                
                // Affichage de la fenêtre
                setVisible(true);                                                                    
        }

        public static void main(String[] args) {
                // Les méthodes AWT doivent être invoquées depuis l'EDT d'AWT
                EventQueue.invokeLater(new Runnable() {
                        @Override
                        public void run() {
                                new Main();
                        }
                });  
        }
}

Dessin d'un cube

Mais si nous nous arrêtons là, nous n'aurons qu'une fenêtre de fond noire. Pour dessiner, nous allons maintenant faire de l'OpenGL en faisant appel aux fontionnalités de JOGL.
Pour cela nous allons modifier la classe Main et développer deux nouvelles classes, Renderer qui va se charger de gérer notre environnement OpenGL et une classe Cube qui va simplifier le dessin d'un cube.

import javax.media.opengl.GL2;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.glu.GLU;

public class Renderer implements GLEventListener {

        // Utilitaire donnant accès aux commandes bas niveau d'OpenGL
        private GLU glu;
        // Angle courant de rotation sur l'axe X
        public float alphaX=0f;        
        // Angle courant de rotation sur l'axe Y
        public float alphaY=0f;        
        // Angle courant de rotation sur l'axe Z
        public float alphaZ=0f;        

        
        /**
        * Appelée immédiatemment lors de l'initialisation du context graphique OpenGl (canvas).
        * N'est appelée qu'une seule fois.
        */
        @Override
        public void init(GLAutoDrawable drawable) {
                // Récupération du contexte en GL2
                GL2 gl = drawable.getGL().getGL2();                                                  
                // Initialisation de l'utilitaire
                glu = new GLU();                                                                      
                // Remplissage du contexte avec du NOIR
                gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);                                              
                // Configuration la profondeur au maximum
                gl.glClearDepth(1.0f);                                                                                        
                // Autorisation de faire un rendu avec une perspective
                gl.glEnable(GL2.GL_DEPTH_TEST);                                                              
                // Restriction de l'affichage aux éléments(Z<=Max)
                gl.glDepthFunc(GL2.GL_LEQUAL);                                                                
                // Correction pour la meilleure perspective
                gl.glHint(GL2.GL_PERSPECTIVE_CORRECTION_HINT, GL2.GL_NICEST);        
                // Joli mélange de couleur, et lissage des textures
                gl.glShadeModel(GL2.GL_SMOOTH);                                                              
        }
                
                
        /**
        * Appelée par l'animateur pour executer le rendu graphique.
        */
        @Override
        public void display(GLAutoDrawable drawable) {
                // Récupération du contexte en GL2
                GL2 gl = drawable.getGL().getGL2();                                                  
                // Réinitialisation de la scène (effacement des tampons)
                gl.glClear(GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT);        
                // Reinitialisation de la matrice courante
                gl.glLoadIdentity();                                                                                  

                /* (Alt aux translations) Placement de la caméra au point (4,0,12)
                Direction vers l'origine de la scène (0,0,0)
                Inclinaison nulle car la vue suit l'axe vertical (y) */
                glu.gluLookAt(        4f, 0f, 12f,
                                                0f, 0f, 0f,  
                                                0f, 1f, 0f  
                );

                // Rotation de la matrice courante de l'angle alpha autour de l'axe x (1,0,0)
                gl.glRotatef(alphaX,        
                                        1f, 0f, 0f
                );

                // Rotation de la matrice courante de l'angle alpha autour de l'axe x (0,1,0)
                gl.glRotatef(alphaY,
                                        0f, 1f, 0f
                );

                // Rotation de la matrice courante de l'angle alpha autour de l'axe x (0,0,1)
                gl.glRotatef(alphaZ,
                                        0f, 0f, 1f 
                );

                // Tous les dessins utltérieurs subiront la transformation : Dessin d'un cube
                new Cube(2.0f, 0, 0, 0).draw(gl);                                                            
        }

        
        /**
        * Appelée avant la destruction du context OpenGL. Permet de liberer les ressources telles que les buffers.
        */
        @Override
        public void dispose(GLAutoDrawable arg0) {
                // Non utilisée ici mais sa déclaration est obligatoire de par l'interface
        }

        /**
        * Appelée lorsque la fenetre d'affichage est redimensionnée,
        * Mais aussi lorsque la fenetre est affichée pour la première fois
        */
        @Override
        public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
                // Récupération du contexte en GL2
                GL2 gl = drawable.getGL().getGL2();

                // Alternative à la division par zéro
                if (height == 0) height = 1;                                                                          

                // Configuration de la zone d'affichage OpenGL
                gl.glViewport(0, 0, width, height);                                                          

                // Spécification d'une matrice de projection en perspective
                // Passage en mode définition de la PROJECTION
                gl.glMatrixMode(GL2.GL_PROJECTION);  
                // Reinitialisation de la matrice courante
                gl.glLoadIdentity();
                // Angle d'observation en degrés, echelle entre largeur et hauteur, intervalle de projection
                glu.gluPerspective(45.0, (float)width/height, 0.1, 100.0);

                // Selection de la transformation point de vue
                gl.glMatrixMode(GL2.GL_MODELVIEW);
                // Reinitialisation de la matrice courante
                gl.glLoadIdentity();
        }

}
import javax.media.opengl.GL2;

public class Cube {

        // Moitié de la longueur d'un coté, simplifie les calculs
        private float tailleSur2;
        // Centre du Cube dans le repere global, simplifie la translation
        private int [] position;

        public Cube(float taille, int x, int y, int z){
        tailleSur2 = taille;
        position = new int [3];
        position[0] = x;
        position[1] = y;
        position[2] = z;
        }

        public void draw(GL2 gl){

                // Dessin de 6 quadrilateres : {4 vertex = 1 quadrilatère}
                gl.glBegin(GL2.GL_QUADS);                                    

                // le quadrilatere de devant et de derriere, blanc
                gl.glColor3f(1f, 1f, 1f);                            
                gl.glVertex3f(position[0]-tailleSur2, position[1]-tailleSur2, position[2]+tailleSur2);
                gl.glVertex3f(position[0]+tailleSur2, position[1]-tailleSur2, position[2]+tailleSur2);
                gl.glVertex3f(position[0]+tailleSur2, position[1]+tailleSur2, position[2]+tailleSur2);
                gl.glVertex3f(position[0]-tailleSur2, position[1]+tailleSur2, position[2]+tailleSur2);

                gl.glVertex3f(position[0]-tailleSur2, position[1]-tailleSur2, position[2]-tailleSur2);
                gl.glVertex3f(position[0]+tailleSur2, position[1]-tailleSur2, position[2]-tailleSur2);
                gl.glVertex3f(position[0]+tailleSur2, position[1]+tailleSur2, position[2]-tailleSur2);
                gl.glVertex3f(position[0]-tailleSur2, position[1]+tailleSur2, position[2]-tailleSur2);

                // le quadrilatere de gauche et de droite, gris
                gl.glColor3f(0.5f, 0.5f, 0.5f);      
                gl.glVertex3f(position[0]-tailleSur2, position[1]-tailleSur2, position[2]+tailleSur2);
                gl.glVertex3f(position[0]-tailleSur2, position[1]-tailleSur2, position[2]-tailleSur2);
                gl.glVertex3f(position[0]-tailleSur2, position[1]+tailleSur2, position[2]-tailleSur2);
                gl.glVertex3f(position[0]-tailleSur2, position[1]+tailleSur2, position[2]+tailleSur2);

                gl.glVertex3f(position[0]+tailleSur2, position[1]-tailleSur2, position[2]+tailleSur2);
                gl.glVertex3f(position[0]+tailleSur2, position[1]-tailleSur2, position[2]-tailleSur2);
                gl.glVertex3f(position[0]+tailleSur2, position[1]+tailleSur2, position[2]-tailleSur2);
                gl.glVertex3f(position[0]+tailleSur2, position[1]+tailleSur2, position[2]+tailleSur2);

                // le quadrilatere du haut et du bas, bleu
                gl.glColor3f(0f, 0f, 1f);                            
                gl.glVertex3f(position[0]-tailleSur2, position[1]+tailleSur2, position[2]+tailleSur2);
                gl.glVertex3f(position[0]-tailleSur2, position[1]+tailleSur2, position[2]-tailleSur2);
                gl.glVertex3f(position[0]+tailleSur2, position[1]+tailleSur2, position[2]-tailleSur2);
                gl.glVertex3f(position[0]+tailleSur2, position[1]+tailleSur2, position[2]+tailleSur2);

                gl.glVertex3f(position[0]-tailleSur2, position[1]-tailleSur2, position[2]+tailleSur2);
                gl.glVertex3f(position[0]-tailleSur2, position[1]-tailleSur2, position[2]-tailleSur2);
                gl.glVertex3f(position[0]+tailleSur2, position[1]-tailleSur2, position[2]-tailleSur2);
                gl.glVertex3f(position[0]+tailleSur2, position[1]-tailleSur2, position[2]+tailleSur2);

                gl.glEnd();

        }

}
import java.awt.EventQueue;
import java.awt.Frame;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.media.opengl.awt.GLCanvas;
import com.jogamp.opengl.util.FPSAnimator;

@SuppressWarnings("serial")
public class Main extends Frame{
        // Composant awt, pour le rendu OpenGL (JPanel)
        private GLCanvas canvas;                      
        
        // Gestionnaire du FPS, permet de ménager le CPU
        private final FPSAnimator animator;    

        public Main(){
                // Appel du constructeur parent
                super();
                // Spécifie la taille de la fenêtre
                setSize(500,500);
                // Renseigne le titre de la fenetre
                setTitle("Manipulation d'une boite en 3D");
                // Permet de centrer la fenetre à l'ouverture
                setLocationRelativeTo(null);                          

                // Initialisation du manager d'événements OpenGL
                Renderer renderer = new Renderer();                  

                // Initialisation du canvas
                canvas = new GLCanvas();      
                // Spécification d'un manager d'événements OpenGL
                canvas.addGLEventListener(renderer);          

                // Ajout du canvas au frame
                add(canvas);                                                          

                // Initialisation de l'animateur avec ici 50 FPS
                animator  = new FPSAnimator(canvas, 50);      

                // Adaptater permettant libérer les ressources lors de la fermeture de la fenêtre
                addWindowListener(new WindowAdapter() {              

                @Override
                public void windowClosing(WindowEvent e) {
                        // Arret de l'animateur
                        animator.stop();
                        // Fermeture de l'application
                                System.exit(0);                            
                        }
                });

                // Démarrage de l'animateur
                animator.start();
                // Affichage de la fenêtre
                setVisible(true);                                                    
        }

        public static void main(String[] args) {
                // Les méthodes AWT doivent être invoquées depuis l'EDT d'AWT
                EventQueue.invokeLater(new Runnable() {              
                        @Override
                        public void run() {
                                new Main();
                        }
                });  
        }

}

L’assemblage de ces classes se résume ainsi, une fenêtre du Main initialise son canvas et le lie à un Renderer qui dans sa méthode display se charge de dessiner un Cube qui s’affiche à l’ecran.
Cependant pour le faire bouger, rendez vous à la partie suivante.

Gestion de l'animation du cube

Dessiner un cube en 3D c'est super, mais l'animer c'est encore mieux. C'est ce que nous allons voir avec les touches claviers avant d'utiliser une mannete (je vous rassure, ce n'est pas la dualshock).

Touches clavier

Pour animer notre cube à l'écran, bonne nouvelle, car nous n'aurons pas besoin de créer une nouvelle classe... ou si. En fait cette classe sera interne à la classe Main vu qu'elle va se charger de gérer les évenement claviers. Ainsi en appuyant sur une touche prédéfinie, nous allons modifier les valeurs des angles de rotations enregistrées par le Renderer. Notre classe Main devient alors :

import java.awt.EventQueue;
import java.awt.Frame;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.media.opengl.awt.GLCanvas;

import com.jogamp.opengl.util.FPSAnimator;

@SuppressWarnings("serial")
public class Main extends Frame{
        // Composant awt, pour le rendu OpenGL (JPanel)
        private GLCanvas canvas;                      
        
        // Gestionnaire du FPS, permet de ménager le CPU
        private final FPSAnimator animator;    

        
        // Classe interne chargée des évenements claviers
        private static final class MyKeyListener extends KeyAdapter {

                private final Main $this;
                private final Renderer renderer;
                private boolean isUpPressed;
                private boolean isDownPressed;
                private boolean isLeftPressed;
                private boolean isrightPressed;

                private MyKeyListener(Main m, Renderer r) {
                        $this = m;
                        renderer = r;
                }

                private void doThat(){
                        if(isDownPressed)
                                renderer.alphaX += 0.8;
                        if(isUpPressed)
                                renderer.alphaX -= 0.8;
                        if(isLeftPressed)
                                renderer.alphaY -= 0.8;
                        if(isrightPressed)
                                renderer.alphaY += 0.8;
                }

                public void keyPressed(KeyEvent e) {
                        switch (e.getKeyCode()) {
                                case KeyEvent.VK_ESCAPE:
                                        $this.animator.stop();
                                        System.exit(0);
                                        break;
                                case KeyEvent.VK_UP:
                                        isUpPressed = true;
                                        break;
                                case KeyEvent.VK_DOWN:
                                        isDownPressed = true;
                                        break;
                                case KeyEvent.VK_RIGHT:
                                        isrightPressed = true;
                                        break;
                                case KeyEvent.VK_LEFT:
                                        isLeftPressed = true;
                                        break;
                        }
                        doThat();
                }

                public void keyReleased(KeyEvent e) {
                        switch (e.getKeyCode()) {
                                case KeyEvent.VK_UP:
                                        isUpPressed = false;
                                        break;
                                case KeyEvent.VK_DOWN:
                                        isDownPressed = false;
                                        break;
                                case KeyEvent.VK_RIGHT:
                                        isrightPressed = false;
                                        break;
                                case KeyEvent.VK_LEFT:
                                        isLeftPressed = false;
                                        break;
                        }
                }
        }
        
        
        public Main(){
                // Appel du constructeur parent
                super();
                // Spécifie la taille de la fenêtre
                setSize(500,500);
                // Renseigne le titre de la fenetre
                setTitle("Manipulation d'une boite en 3D");
                // Permet de centrer la fenetre à l'ouverture
                setLocationRelativeTo(null);                          

                // Initialisation du manager d'événements OpenGL
                Renderer renderer = new Renderer();                  

                // Initialisation du canvas
                canvas = new GLCanvas();      
                // Spécification d'un manager d'événements OpenGL
                canvas.addGLEventListener(renderer);          

                // Ajout du canvas au frame
                add(canvas);                                                          

                // Initialisation de l'animateur avec ici 50 FPS
                animator  = new FPSAnimator(canvas, 50);      

                // Adaptater permettant libérer les ressources lors de la fermeture de la fenêtre
                addWindowListener(new WindowAdapter() {              

                @Override
                public void windowClosing(WindowEvent e) {
                        // Arret de l'animateur
                        animator.stop();
                        // Fermeture de l'application
                                System.exit(0);                            
                        }
                });
                
                // Liaison de la fenêtre et du canvas avec le manager d'evenement clavier
                addKeyListener(new MyKeyListener(this, renderer));    
        canvas.addKeyListener(new MyKeyListener(this, renderer));
                
                // Masque les menus et bordures de la fenetre
                setUndecorated(true);
                // Permet d'être en mode plein écran*/
        setExtendedState(Frame.MAXIMIZED_BOTH);        

                // Démarrage de l'animateur
                animator.start();
                // Affichage de la fenêtre
                setVisible(true);                                                    
        }

        public static void main(String[] args) {
                // Les méthodes AWT doivent être invoquées depuis l'EDT d'AWT
                EventQueue.invokeLater(new Runnable() {              
                        @Override
                        public void run() {
                                new Main();
                        }
                });  
        }

}

Accéléromètre

Maintenant que nous sommes certains que notre cube peut s'animer, essayons de le contrôler à distance.
La carte Waspmote dispose d'un accéléromètre dont nous allons récupérer les valeurs à chaque instant. Pour se faire, grace à l'IDE WaspMote preparons notre carte selon nos besoins avec le programme suivant:

Pour plus d'informations relatives à l'utilisation des cartes Waspmote, c'est aussi ici.
Nous pouvons ensuite vérifier la transmission:

Récupérer les valeurs c'est bien, mais notre objectif est de les traiter dans notre application. D'où la nécessité d'une autre classe chargée de recevoir les valeurs transmises par la carte.
Elle sera nommée Accelerometer et devra aussi pouvoir modifier les valeurs angulaires de rotation enregistrées par le Renderer. Sur ce rentrons dans le vif du sujet sans plus tarder.

import gnu.io.CommPortIdentifier;
import gnu.io.SerialPort;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Enumeration;
import java.util.StringTokenizer;

public class Accelerometer implements SerialPortEventListener {

        // Indique le port de reception des données
        private SerialPort serialPort;        
        // Gestionnaire OpenGL
        private Renderer renderer;
        // Permet de lire les reponses
        private BufferedReader input;          
        // Sert à stocker la réponse
        private String s = "" ;                        
        // Coordonées par rapport à l'axe X
        private int x = 0 ;                            
        // Coordonées par rapport à l'axe Y
        private int y = 0 ;                            
        // Coordonées par rapport à l'axe Z
        private int z = 0 ;

        public Accelerometer(Renderer r) {
                renderer = r;
                // Sert à recupérer une référence sur le port à écouter
                CommPortIdentifier portId = null;            
                @SuppressWarnings("rawtypes")
                Enumeration portEnum = CommPortIdentifier.getPortIdentifiers();

                // Recherche du port nommé "..." à indiquer
                while (portEnum.hasMoreElements()) {  
                        CommPortIdentifier currPortId = (CommPortIdentifier) portEnum.nextElement();

                        if (currPortId.getName().equals("COM3")) {
                                // Si trouvé, enregistrement de la référence puis sortie de boucle
                                portId = currPortId;
                                break;
                        }
                }

                // Tentative de liaison entre le port d'écoute et l'application
                try {
                        serialPort = (SerialPort) portId.open(this.getClass().getName(), 2000);
                        serialPort.setSerialPortParams(38400,
                        SerialPort.DATABITS_8,
                        SerialPort.STOPBITS_1,
                        SerialPort.PARITY_NONE);

                        input = new BufferedReader(new InputStreamReader(serialPort.getInputStream()));
                        serialPort.notifyOnDataAvailable(true);
                        serialPort.addEventListener(this);
                }
                catch (Exception e) {
                        System.err.println(e.toString());
                }
        }

        // Appelée lors de à la terminaison de l'application
        public void close() {
                if (serialPort != null) {
                        serialPort.removeEventListener();
                        serialPort.close();
                }
        }

        // Appelée à chaque reception
        @Override
        public void serialEvent(SerialPortEvent event) {
                
                // Tentative de lecture
                if (event.getEventType() == SerialPortEvent.DATA_AVAILABLE) {
                        try {
                                s = input.readLine() ;
                                if( s.equals("3A") ){
                                        s = input.readLine() ;
                                        StringTokenizer st = new StringTokenizer(s) ;
                                        x = Integer.parseInt(st.nextToken()) ;
                                        y = Integer.parseInt(st.nextToken()) ;
                                        z = Integer.parseInt(st.nextToken()) ;

                                        int lisse = 40;

                                        // Elimination des variations insignifiantes
                                        x -= x%lisse;
                                        y -= y%lisse;
                                        z -= z%lisse;

                                        
// Calcul des correspondances trigonométriques selon les valeurs reçues
double Axr = x >= 0 ? ( z<0 && (x*x/1000000f)>(y*y/1000000f) ? 180 - Math.asin(x/1000.0f) * 180/Math.PI
                                                                                                                        : Math.asin(x/1000.0f) * 180/Math.PI )
                                        : ( z<0 && (x*x/1000000f)>(y*y/1000000f) ? - 180 - Math.asin(x/1000.0f) * 180/Math.PI
                                                                                                                        : Math.asin(x/1000.0f) * 180/Math.PI );

double Ayr = y >= 0 ? ( z<0 && (x*x/1000000f)<(y*y/1000000f) ? -Math.acos(y/1000.0f) * 180/Math.PI
                                                                                                                        : Math.acos(y/1000.0f) * 180/Math.PI )
                                        : ( z<0 && (x*x/1000000f)<(y*y/1000000f) ? -Math.acos(y/1000.0f) * 180/Math.PI
                                                                                                                        : Math.acos(y/1000.0f) * 180/Math.PI );

                                                                                                                                                                
                                        // Modification des valeurs angulaires du Renderer
                                        if(renderer != null){
                                                renderer.alphaX = (float)Axr;
                                                renderer.alphaZ = (float)Ayr;
                                        }
                                }
                                else
                                        s = input.readLine();
                        }
                        catch (Exception e) {}      
                }
        }
}

Encore un détail et c'est bon, la classe Accelerometer est fonctionnelle mais nous ne l'avons pas encore liée au reste de l'application. En fait c'est simple, il suffit juste de rajouter cette ligne dans le Main.

//Dans Dans le constructeur de la classe Main à la fin
                
                // Liaison de la fenêtre et du canvas avec le manager d'evenement clavier
                // ....
                
                // Démarrage de l'accéléromètre
                acc = new Accelerometer(renderer);
                
                // Masque les menus et bordures de la fenetre
                // .....

Voilà c'est fini, cependant rien ne vous empêche d'aller plus loin. Votre imagination est votre seule limite. Afin de mieux se rendre compte des mouvements effectués, il serait interressant de fabriquer
une petite boite où insérer la carte Waspmote. Cela protége la batterie et permet d'éviter aussi le contact direct avec les mains.

Annexes

Pour aller plus loin ...

La réalisation de ce cube n’est qu’une petite partie des possibilités réalisables avec la 3D. Une adaptation pour rubik’s cube pourrait être une des possibilités de suite à cette application. Différents évènements peuvent être implémentés sur les différents axes des cubes afin de pouvoir les déplacer entre eux pour résoudre ce casse tête. L’algorithme de résolution du rubik’s cube peut être également développé pour résoudre le problème automatiquement et mettre une intelligence artificielle pour déplacer le groupe de cube afin de le reconstruire. Cela peut aboutir à un jeu où l’objectif serait de reconstituer le rubik’s cube avec une aide si le joueur est bloqué.

Il serait possible d’améliorer les manipulations de l’utilisateur sur le cube. L’utilisateur aurait la possibilité de modifier le cube en sélectionnant une face afin d’effectuer des transformations comme un étirement et obtenir un pavé. La possibilité d’agrandir l’objet, d’ajouter des objets pour créer différentes formes ou de réaliser des coupes.

Références

Ecole internationale "Réseaux de Capteurs, Mesures et Environnement"

Another Tutorial on JOGL 2.0

Les tutoriels Nehe

Les mathématiques de l'Infographie