I. Avant-propos▲
I-A. Pourquoi cette série d'articles ?▲
Beaucoup d'entre nous jouent aux jeux vidéo, c'est un fait, mais beaucoup aussi souhaiteraient aller plus loin en prenant le rôle de créateur. Nombreux sont ceux qui souhaitent se lancer dans le développement de jeux ou d'applications 3D. Cependant la tâche est loin d'être simple, car il s'agit d'un des domaines les plus complexes de la programmation. Il existe de nombreux sites traitant le sujet avec plus ou moins de réussite, mais les ressources pour nous, francophones, sont bien plus rares. Il est difficile de trouver des articles sur l'apprentissage de l'OpenGL en français et il d'autant plus dur d'en trouver utilisant le langage Java.
Le niveau de difficulté des articles sera progressif. Chaque article traitera d'un point précis (ou d'un ensemble de points liés). J'essaierai de suivre un ordre logique dans les thèmes abordés. C'est pourquoi il est conseillé de lire les articles dans l'ordre de parution si vous découvrez ou si vous débutez avec OpenGL. Il est donc normal que lors des premiers articles, les résultats ne soient pas à la hauteur de ce que vous pourriez espérer.
Non, vous ne développerez pas un clone de Doom3 cette semaine :), cependant ce jeu est l'exemple parfait pour montrer ce qui est réalisable avec de l'OpenGL et de bonnes connaissances de maths, mais bon on peut toujours rêver ;).
À propos des maths, ce sera justement le moment pour vous de ressortir vos cours. J'espère que les mathématiques ne vous font pas mal au cœur, car si vous souhaitez développer des jeux ayant un minimum de qualité, vous retrouverez beaucoup de formules, de calculs, de géométrie spatiale…
Une connaissance relativement correcte du Java est tout de même indispensable pour suivre correctement les articles.
I-B. OpenGL▲
Pour ceux qui ne se sont pas arrêtés à mes dernières lignes, OpenGL est une API de rendu graphique, utilisée pour faire de la 2D et de la 3D. Ce qui rend son utilisation intéressante est qu'elle se sert de la carte graphique pour effectuer ses calculs. D'autre part, il s'agit d'une API disponible sur de nombreux systèmes, ce qui en fait donc un choix parfait pour le développement d'application portable.
Pour une description plus détaillée, reportez-vous à la FAQ OpenGL ou encore à mon autre article sur les API 3D.
I-C. Pourquoi Java ?▲
Malgré tous les préjugés qui l'entourent, Java est une solution tout à fait viable pour le développement d'applications et de jeux 3D. D'une part, grâce au Java et à des IDE des Eclipse, on obtient une très grande productivité de développement. D'autre part, et je pense que c'est le plus important, l'utilisation de Java et d'OpenGL permet de développer des applications 3D multiplateformes (sans nécessiter de recompilation).
Cependant, OpenGL reste de l'OpenGL, il est donc possible d'utiliser les connaissances acquises dans ces articles avec n'importe quel langage permettant d'utiliser OpenGL, avec très peu, voire aucune modification de code. Les principales différences se situent au niveau de la gestion de l'affichage (fenêtrage) et du passage de donnée (textures, VBO…). En Java, on utilisera principalement les NIO avec leurs différentes classes de Buffer.
De plus j'ai aussi dû faire un choix au niveau du binding OpenGL utilisé (https://info-rital.developpez.com/tutoriel/java/api/3d/). Ayant le choix entre LWJGL et le JSR-231 (anciennement JOGL), j'ai finalement opté pour le second. Non pas que j'aie une préférence particulière ou même que l'un soit plus performant que l'autre, mais que je devais faire un choix, je me suis basé sur des critères pas forcément très représentatifs. Si j'ai choisi le JSR-231, c'est parce qu'il s'agit du binding développé par Sun lui-même. Généralement les gens font plus confiance dans ce qui est officiel. De plus, dans la prochaine version de Java (au doux nom code de Mustang), de nombreuses possibilités d'interaction entre Swing et OpenGL vous seront offertes en utilisant le JSR-231.
Quoi qu'il en soit LWJGL est simple à utiliser, et puis comme je l'ai dit plus haut, pour 99% du code, il sera le même que vous utilisiez LWJGL ou le JSR-231. Il vous suffira de regarder quelques codes source pour effectuer les modifications adéquates. Il se peut même que je consacre un article à ça.
II. Installation▲
Avant de pouvoir commencer à développer, il va falloir installer le nécessaire. Je me passerai de l'installation de Java, bien que je vous conseille très fortement d'installer la version 1.5 du JDK de Sun. Faites un « java -version » dans une invite de commande ou un terminal pour connaître la version de Java installée sur votre ordinateur (si Java il y a, mais cela va de soi)
Il y a plusieurs façons d'utiliser le JSR-231. Contrairement à la plupart des extensions Java, il est complètement déconseillé d'installer directement dans votre répertoire d'installation de Java, les fichiers du JSR-231. Cela risquerait de poser des problèmes en cas de conflits entre versions. Les API Java ne s'entendent pas très bien quand deux versions différentes sont confrontées (jalousie, tout ça…), notamment quand une application chargée par JavaWebStart propose une version différente que celle installée sur votre système.
II-A. Téléchargement▲
Il va d'abord vous falloir récupérer les fichiers du JSR-231. Pour cela, rendez-vous sur le site https://jogl.dev.java.net/. Vous avez maintenant plusieurs choix. Soit vous téléchargez la version courante du JSR-231, soit une « nightly build ». Il s'agit d'une version fraîchement compilée, non officielle. Mais les différences sont relativement minimes. Elles le seront plus d'ici quelques mois. De plus, étant donné que le JSR-231 n'est pas encore sorti en version « stable », je vous conseille de mettre à jour plus ou moins régulièrement votre version.
Il y a au moins deux fichiers à récupérer : jogl.jar et jogl-natives-xxx-yyy.jar où xxx n'est pas le nom d'un film, mais votre système d'exploitation et yyy votre architecture matérielle.
Vous pouvez aussi récupérer les sources et la documentation de l'API, mais c'est facultatif.
II-B. Installation manuelle▲
Nous allons installer ces fichiers directement dans le répertoire de votre programme (ou dans un répertoire à part).
Commencez par créer un répertoire (appelons-le « lib ») dans lequel nous plaçons jogl.jar et nous dézippons jogl-natives-xxx-yyy.jar. Ce dernier contient la partie native, propre à chaque plateforme, alors que jogl.jar n'est que l'interface (au sens programmation), qui fait le pont entre l'application Java et les binaires natifs.
Pour lancez votre application Java, vous devrez utiliser la commande suivante :
java -classpath lib/jogl.jar:. -Djava.library.path=lib/ VotreAppli « lib/ » et « VotreAppli » sont bien sûr à remplacer par le bon chemin vers vos fichiers du JSR-231 et par le nom de votre classe d'entré de votre application Java.
II-C. JavaWebStart▲
Pour utiliser le JSR-231 dans vos applications distribuées par JavaWebStart, il suffit de placer dans le descripteur JNLP, dans la section <resources> la ligne suivante :
<extension name=« jogl » href=« http://download.java.net/media/jogl/builds/archive/jsr-231-webstart-current/jogl.jnlp » />
II-D. Eclipse▲
Pour utiliser le JSR-231 dans votre projet avec Eclipse, allez dans le Java Build Path, ajoutez en tant que Jar externe, jogl.jar. Puis allez dans les propriétés de ce dernier, puis Native Library et joignez le second jar (non décompressé cette fois-ci).
II-E. Note pour Windows▲
Sous Windows, il peut y avoir des conflits entre DirectDraw et OpenGL. C'est pourquoi il est conseillé de lancer votre application en ajoutant : -Dsun.java2d.noddraw=true
Sous JavaWebStart, il faut ajouter, dans la section resources :
<property name=« sun.java2d.noddraw » value=« true »/>
Il existe aussi un bug concernant les drivers ATI (plutôt flou), mais si jamais vous remarquez des fuites de mémoires, ajoutez la propriété « jogl.GLContext.nofree ».
III. Fonctionnement d'OpenGL▲
OpenGL dispose de nombreuses fonctionnalités pour effectuer un grand nombre d'actions comme la gestion de :
- la caméra ;
- la rotation 3D des objets ;
- le remplissage des faces ;
- les textures ;
- la lumière.
Et bien d'autres choses encore…
Mais nous ne ferons pas de 3D pour le moment, car de nombreuses connaissances préalables vous seront nécessaires.
III-A. Tout est une question de contexte…▲
Nous avons vu que OpenGL permet de faire des choses merveilleuses. Cependant, pour pouvoir gérer tout cela, il nous faut une surface devinable et un « contexte » dans lequel OpenGL puisse pleinement s'épanouir. C'est là qu'intervient Java.
Il existe plusieurs types de contextes, je me contenterai du plus courant, qui est celui que l'on utilise dans l'énorme majorité des cas.
Il va nous falloir créer une fenêtre (Frame) et un composant de type GLCanvas pour pouvoir dessiner dessus.
III-B. GLEventListener▲
Pour réagir avec OpenGL, il va falloir implémenter la classe GLEventListener. Il s'agit d'un mécanisme de callback qui va permettre d'utiliser OpenGL avec notre surface de dessin.
Cette interface dispose de quatre méthodes qui sont :
- init() ;
- display() ;
- reshape() ;
- displayChanged().
init() : cette méthode est appelée une fois. Elle contiendra toute la partie initialisation. display() : cette méthode sera appelée en boucle. Elle contiendra la partie affichage. reshape() : cette méthode est appelée lors du redimensionnement de la fenêtre. Elle permettra de réajuster certaines valeurs pour ne pas avoir un affichage difforme.
III-C. Y a de l'animation▲
Pour pouvoir appeler en boucle la méthode display(), qui se chargera de créer la nouvelle image qui sera affichée sur l'écran, nous pourrions utiliser une boucle (for, while…).
Cependant les développeurs du JSR-231 ont pensé à nous (ils sont gentils :)) et nous ont développé la classe Animator qui se chargera d'appeler la méthode display(). Elle crée un thread à part dans lequel sont effectués les appels à display(). De plus elle se charge de faire une petite pause après chaque redessinage de l'image pour permettre au CPU de souffler un peu et de s'occuper des autres threads voir même des autres programmes. Il est cependant possible de supprimer cette pause avec la méthode setRunAsFastAsPossible(true);
Cette classe dispose de deux méthodes principales : start(); et stop(); que nous utiliserons pour lancer et arrêter l'animation.
Il existe une autre classe, qui hérite de Animator. Il s'agit de FPSAnimator. Cette classe permet de fixer un FPS (frames-per-second, le nombre d'images par seconde) pour éviter d'utiliser tout le CPU. Mais bon il ne s'agit que d'une estimation que l'on donne et la valeur ne sera pas garantie.
III-D. En avant le code▲
Voici le code de cet article. Nous le réutiliserons et nous l'élargirons au fur et à mesure dans les articles qui suivront.
Le code est volontairement très commenté, même sur des parties qui peuvent sembler très simples pour certains. Le code source constitue une partie de l'article, il est important que chaque ligne soit bien comprise.
package
developpez.opengl;
import
java.awt.*;
import
java.awt.event.*;
import
javax.media.opengl.*;
import
com.sun.opengl.util.*;
/**
* Article1.java
* author: InfoRital
*
* Code source du premier article.
* Article1 implémente
{@link
GLEventListener
}
pour obtenir le mécanisme de callback
*
*/
public
class
Article1 implements
GLEventListener {
public
static
void
main
(
String[] args) {
/**
* Création d'une fenêtre
* Nous utilisons la classe Frame de AWT
* plutôt que la classe JFrame de Swing
* pour une question de performance
*/
Frame frame =
new
Frame
(
"Article1"
);
/**
* Création du Canvas OpenGL
* pour pouvoir dessiner dessus
*/
GLCanvas canvas =
new
GLCanvas
(
);
/**
* Nous attachons ensuite le méchanisme de callback
* à notre surface dessinable
*/
canvas.addGLEventListener
(
new
Article1
(
));
/**
* Nous attachons maintenant notre
* surface dessinable à notre fenêtre
*/
frame.add
(
canvas);
/**
* Création de l'Animator
* comme expliqué dans l'article
*/
final
Animator animator =
new
Animator
(
canvas);
/**
* Le code qui suit permet de gérer la fermeture
* de la fenêtre par l'utilisateur
*/
frame.addWindowListener
(
new
WindowAdapter
(
) {
public
void
windowClosing
(
WindowEvent e) {
/**
* Nous créons un nouveau thread autre
* que la queue d'événements AWT
* pour être d'arrêter l'Animator avant
* de quitter complètement
*/
new
Thread
(
new
Runnable
(
) {
public
void
run
(
) {
animator.stop
(
);
System.exit
(
0
);
}
}
).start
(
);
}
}
);
/**
* Nous donnons une taille à la fenêtre
* et nous l'affichons
*/
frame.setSize
(
300
, 300
);
frame.setVisible
(
true
);
/**
* Nous démarrons l'Animator qui
* va se charger de faire des appels
* à la méthode display();
*/
animator.start
(
);
}
/**
* init() sera appelée une fois au début de l'animation
* C'est dans cette méthode que nous nous chargerons de toutes
* les opérations d'initialisation
*/
public
void
init
(
GLAutoDrawable drawable) {
/**
* GLEventListener renvoie un contexte (drawable)
* que nous allons utiliser pour instancier un objet
* de type GL, qui nous permettra d'utiliser
* les fonctions OpenGL, bien que dans cet article
* cela reste encore inutile
*/
GL gl =
drawable.getGL
(
);
/**
* Cette fonction permet de désactiver
* la synchronisation verticale, indépendamment
* de la plateforme utilisée
*/
gl.setSwapInterval
(
1
);
}
/**
* reshape() sera appelée si la fenêtre d'affichage est redimensionnée
*/
public
void
reshape
(
GLAutoDrawable drawable, int
x, int
y, int
width,
int
height) {
GL gl =
drawable.getGL
(
);
}
/**
* display() sera appelée en boucle tout au long de l'application
* par la classe Animator. C'est dans cette fonction qu'on fera
* tout ce qui doit être affiché
*/
public
void
display
(
GLAutoDrawable drawable) {
GL gl =
drawable.getGL
(
);
}
/**
* displayChanged() est appelée si le mode d'affichage par exemple
* est modifié. Cependant nous n'implémenterons pas cette méthode.
*/
public
void
displayChanged
(
GLAutoDrawable drawable, boolean
modeChanged,
boolean
deviceChanged) {
}
}
IV. Conclusion▲
Nous avons vu dans cet article comment créer une fenêtre dans laquelle il est possible de dessiner avec OpenGL.
Dans le prochain article, nous commencerons à utiliser les fonctions OpenGL pour initialiser notre scène.
IV-A. Démo JavaWebStart▲
Le résultat de chaque article sera présenté sous forme de démonstration JavaWebStart.
Il vous suffit de cliquer sur le lien suivant pour lancer la démo (bien que celle-ci soit vraiment très basique) :
Démo JWS
Il vous faut bien sûr avoir Java (1.4 au minimum) d'installé.