I. Assimp▲
Dans les scènes vues jusqu’ici, nous avons utilisé notre petit conteneur de nombreuses façons. Mais avec le temps, nos meilleurs amis peuvent devenir un peu ennuyeux. Dans les applications graphiques, on trouvera nombre de modèles complexes et intéressants, bien plus agréables à regarder qu’un simple conteneur. Cependant, contrairement à un conteneur, on ne peut pas définir manuellement tous les sommets, les normales et les coordonnées de textures de formes complexes comme des maisons, des véhicules ou des formes humaines. En réalité, on importera ces modèles dans l’application, lesquels seront conçus par des artistes 3D avec des outils comme Blender, 3DS Max ou Maya.
Ces outils, appelés « logiciels de modélisation 3D » permettent la création de formes complexes et l’application de textures, procédé nommé uv-mapping. Ces logiciels génèrent automatiquement toutes les coordonnées des sommets, les normales et les coordonnées de textures lors de leur exportation dans un fichier créé selon un format spécifié. De cette façon, les modeleurs disposent de nombreux outils pour créer des modèles de grande qualité, sans avoir trop à se soucier des détails techniques, qui sont cachés dans le fichier exporté du modèle. Nous, en qualité de développeurs, nous devons nous soucier de ces aspects techniques.
Notre travail consistera à analyser ces modèles exportés pour en extraire les informations utiles, de façon à les présenter sous une forme compréhensible par OpenGL. Un problème courant est lié à l’existence de dizaines de formats différents pour exporter les données d’un modèle. Des formats de modèles comme le .obj de Wavefront ne contiennent que les données, avec seulement quelques informations comme les couleurs et les textures (diffuses ou spéculaires), tandis que d’autres formats comme le format Collada sont très détaillés, contenant les modèles, les éclairages, beaucoup de types de matériaux, des données d’animation, des caméras, des informations sur la scène entière et bien plus encore. Le format .obj est considéré comme un format de modèle facile à analyser. On vous recommande de visiter la page wiki de Wavefront, au moins pour voir comment un tel format de fichier est structuré. Cela vous donnera un aperçu sur le sujet.
En tout état de cause, il existe tant de formats de fichiers différents que trouver une structure commune et générale se révèle impossible. Ainsi, si nous voulions importer un modèle à partir de ces formats, nous aurions à développer un module d’importation pour chacun des formats envisagés. Heureusement, une bibliothèque existe pour réaliser ce travail.
I-A. Une bibliothèque de chargement de modèle▲
Une bibliothèque très populaire pour importer des modèles s’appelle Assimp, pour Open Asset Import Library. Assimp peut importer des dizaines de formats de fichiers modèles (et peut aussi les exporter), en archivant les données du modèle dans des structures de données générales. Dès qu’Assimp charge le modèle, on peut retrouver toutes les données du modèle dans des structures de données propres à Assimp. Ces structures de données restant toujours les mêmes, quel que soit le format du fichier importé, cela nous permet de s’affranchir des formats utilisés pour les fichiers importés.
Assimp charge le modèle entier dans un objet Scene qui contient toutes les données du modèle importé. Assimp contient alors un ensemble de nœuds, chaque nœud contenant des indices pointant vers les données de l’objet Scene, un nœud pouvant avoir un nombre quelconque de nœuds fils. Un modèle (simplifié) de la structure des données d’Assimp est figuré ci-dessous :
- Toutes les données du modèle, comme les matériaux et les mailles, sont contenues dans l’objet Scene. Cet objet détient aussi une référence vers le nœud Root de la scène.
- Le nœud Root de la scène peut comprendre des nœuds fils (comme tout autre nœud) et peut contenir un ensemble d’indices qui pointent vers les données de maillage dans le tableau mMeshes de l’objet Scene. Le tableau mMeshes du nœud Root contient les objets Mesh réels, alors que les valeurs du tableau mMeshes d’un nœud ne sont que des indices pour le tableau des mailles de la scène.
- Un objet Mesh contient toutes les données nécessaires pour le rendu : positions des sommets, normales, coordonnées de textures, faces, ainsi que les matériaux de l’objet.
- Une maille contient plusieurs faces. Une face représente une primitive de rendu de l’objet (triangles, carrés, points). Une face contient les indices des sommets qui forment une primitive. Les sommets et les indices étant séparés, cela nous facilite le rendu si l’on utilise un tampon d’indices (voir le chapitre Hello Triangle).
- Et enfin, une maille contient aussi un objet Material qui contient plusieurs fonctions pour retrouver les propriétés de matériau d’un objet. On pense aux couleurs, aux textures (comme les textures diffuses ou spéculaires).
Pour commencer, nous voulons charger un modèle dans un objet Scene, retrouver récursivement les objets Mesh correspondants à partir de chacun des nœuds (nous chercherons récursivement chaque fils de chaque nœud) puis traiter chaque objet Mesh pour récupérer les données des sommets, ses indices et ses propriétés de matériau. Le résultat est ainsi un ensemble de données de maillage qui seront contenues dans un seul objet Model.
Maille (Mesh)
Pour modéliser des objets, les créateurs ne conçoivent pas une forme unique pour tout le modèle. Généralement, chaque modèle est constitué de plusieurs sous-ensembles. Chacune de ces composantes est appelée une maille. Pensez par exemple à un modèle d’humain : l’artiste modélisera la tête, les membres, les vêtements, les armes, tout cela séparément, et la combinaison de ces mailles formera le modèle complet. Une maille est la représentation minimale de ce qui est nécessaire pour afficher un objet avec OpenGL (sommets, indices, matériaux). Un modèle contient généralement plusieurs mailles.
Dans le chapitre suivant, nous créerons nos propres classes Model et Mesh pour charger et mémoriser les modèles importés en utilisant la structure que nous venons de décrire. Si nous voulons ensuite afficher un modèle, nous afficherons ce modèle maille par maille. Cependant, avant de commencer à importer des modèles, nous devons intégrer Assimp à nos projets.
I-B. Installer Assimp▲
Vous pouvez télécharger Assimp à partir de cette page et choisir la version qui vous convient. Lors de l’écriture de ces pages, la dernière version était 3.1.1. Nous vous conseillons de compiler ces bibliothèques vous-mêmes, car, en général, les versions précompilées ne fonctionnent pas. Consultez le troisième chapitre si vous avez oublié comment compiler une bibliothèque avec CMake.
Quelques questions peuvent se poser lors de l’installation d’Assimp, je les mentionne ci-dessous avec la solution si vous les rencontrez :
- CMake renvoie continuellement des erreurs à propos de bibliothèques DirectX manquantes, des messages comme celui-ci :
- La solution consiste à installer le SDK de DirectX si ce n’est pas déjà fait, que vous pouvez télécharger ici.
- Lors de l’installation du SDK de DirectX, le code d’erreur s1023 peut apparaître. Dans ce cas, il faudra désinstaller le(s) paquet(s) C++ Redistributable avant d’installer le SDK, comme indiqué ici.
- Lorsque la configuration est complète, vous pouvez générer un fichier solution, l’ouvrir et compiler les bibliothèques (soit une version release, soit une version debug, comme vous voudrez).
- La configuration par défaut installe Assimp comme une bibliothèque dynamique, ainsi il faudra ajouter le fichier assimp.dll à côté des binaires de votre application. Il vous suffira de copier cette DLL dans le même répertoire que votre exécutable.
- Après la compilation d’Assimp, les bibliothèques construites et le fichier DLL se trouveront dans le répertoire code/Debug ou code/Release.
- Vous pouvez simplement copier ces fichiers dans le répertoire de votre choix, et les intégrer à votre solution. N’oubliez pas d’inclure les fichiers d’en-tête, que vous trouverez dans le répertoire include du module Assimp.
En cas de souci, n’hésitez pas à poser une question sur le forum.
Si vous souhaitez qu’Assimp fonctionne en multithreading pour améliorer les performances, vous pouvez le compiler avec Boost, dont vous trouverez les instructions pour l’installation sur cette page.
Maintenant, vous devriez avoir compilé Assimp et l’avoir lié à votre application. Étape suivante : importer un truc sympa !
I-C. Remerciements▲
Ce tutoriel est une traduction réalisée par Jean-Michel Fray dont l’original a été écrit par Joey de Vries et qui est disponible sur le site Learn OpenGL.