Animations au format X3D dans VREng
Transcription
Animations au format X3D dans VREng
Extension X3D pour Vreng Pascal Chambon Enst 2007 Sommaire I. Introduction.............................................................................................................................3 II. Mise en place de l'environnement de travail.........................................................................3 A. Difficultés plus ou moins prévisibles d'installation.........................................................3 B. Difficultés spécifiques d'installation.................................................................................4 1. Conflits de version entre Vreng et Ubit : .......................................................................4 2. Conflits de compatibilité avec Linux..............................................................................4 3. Bonne surprise au niveau d’Eclipse................................................................................4 C. Integration de mes fichiers sources dans vreng.................................................................5 D. Choix des librairies relatives a xml et x3d.........................................................................5 III. Developpement du module x3d............................................................................................6 A. Traduction des données xml..............................................................................................6 B. Rendu de l'arbre X3d........................................................................................................6 C. Animation...........................................................................................................................7 IV. Fonctionnalités supportées..................................................................................................8 A. Format du fichier et parsage : ...........................................................................................8 B. Maillages : .........................................................................................................................8 C. Matériaux : ........................................................................................................................9 D. Animations : ......................................................................................................................9 E. Gestion des collisions.......................................................................................................10 F. Interactions avec l'utilisateur............................................................................................10 V. Rapide Manuel du module X3d...........................................................................................10 A. Classes ajoutées au programme :.....................................................................................10 B. Format de la balise X3D dans le fichier VRE :................................................................11 C. Syntaxe des fonctionnalités supportées (format X3d) :...................................................11 VI. Conclusion : .......................................................................................................................11 I.Introduction Le moteur de réalité virtuelle Vreng possédait déjà avant mon projet le support de plusieurs formats de fichiers 3D (obj, 3ds, lwo...), en plus des primitives définies en interne (avec les fonctions de dessin de polygones de la classe Draw, ou les humanoïdes des classes Mech, Android...). Cependant, les animations étaient la majorité du temps codées manuellement : les instructions définissant les mouvements des objets (pour les balles, la mer...) se retrouvaient inscrites "en dur" dans le code source du moteur, de même que bien souvent les données polygonales des modèles destinés à être animés. C'est pour faciliter l'ajout de surfaces complexes et d'animations dans les mondes virtuels, pour séparer dans Vreng le code- source des données 3D qu'il manipule, que j'ai donc travaillé à l'implantation partielle du format X3D dans Vreng. Basé sur XML, ce format est facilement éditable, et il permet donc de transférer aisément dans le rendu opengl de Vreng tout un ensemble de données : primitives (cones, spheres, cubes....), surfaces polygonales, propriétés des matériaux, textures, et animations (animations sur les matériaux, sur les transformations géométriques...). II.Mise en place de l'environnement de travail La plus grande gageure du projet aura sans nul doute été la mise en place de l'environnement de travail. Afin de pouvoir travailler chez moi, et de tirer partie des environnements de développement intégrés très pratiques comme Eclipse, j'avais en effet installé Ubuntu Linux sur mon portable. Mais cela ne s'est pas fait sans mal. Α. Difficultés plus ou moins prévisibles d'installation Je n'avais jamais utilisé Linux auparavant, donc il m'a fallu un certain temps pour comprendre son fonctionnement (en particulier le système de compilation, et la gestion, au demeurant très sympathique, des librairies partagées), et installer et configurer correctement les différentes dépendances nécessaires à Vreng (librairies de manipulation d'images, librairies et pilotes Opengl, bonnes versions des autotools...). Fonctionnalité intéressante de Linux : si on met à jour un peu trop précipitamment les librairies fondamentales de Linux (libc .so etc.), avec le gestionnaire de paquet d’Ubuntu, il peu arriver que le système se désinstalle proprement et entièrement, pour le plus grand désespoir de l’utilisateur néophyte qui n’a plus qu’à tout réinstaller depuis zéro. Β.Difficultés spécifiques d'installation 1.Conflits de version entre Vreng et Ubit : etant donné que les deux logiciels sont en constante évolution, j'ai connu des erreurs dues au fait que certains champs contenus dans l'ancienne version d'Ubit, et mis en commentaire dans la nouvelle car obsolètes, étaient toujours requis par la dernière version de Vreng, d'où une incapacité à compiler. Finalement, je me suis contenté de la version précédente d'Ubit (5.5.0), et de la version de Vreng téléchargée par SVN le 22 mai 2007. Par la suite, je n'ai plus fait de mise à jour de ces sources par SVN, car j'ai dû modifier le code source des fichiers sources centraux de Vreng pour mener mes tests correctement sur des fichiers locaux de mon ordinateur. Par défaut Vreng allait en effet systématiquement chercher les mondes virtuels ainsi que les autres fichiers sur le serveur Internet, et il m'a fallu préserver l'intégrité du cache entre deux lancements, et rediriger certaines opérations de fichier vers celui- ci, afin de pouvoir modifier à ma guise les fichiers vre et les fichiers x3d utilisés pour mes tests. 2.Conflits de compatibilité avec Linux Une fois le code finalement compilé avec succès, se sont posés les problèmes de compatibilité avec Linux lors de l’exécution. D'un côté à cause d'incompatibilités au niveau des dépendances, dont les origines est restée du domaine du bogue indéterminé : l'affichage Opengl refusait de fonctionner correctement en mode logiciel (MESA), mais par chance il m'a été possible de me procurer des pilotes Opengl propriétaires pour ma carte graphique Ati, ce qui a réglé ces problèmes. A cause d'incompatibilités au niveau du fonctionnement de vreng ensuite : de nombreuses fonctions de bas niveau utilisées avaient d'importants effets de bord, et en particulier il arrivait qu'une chaîne de caractères "statique" passée en paramètre soit modifiée par un fonction, d'où un crash pour cause d'accès illégal à une zone mémoire statique. Ces crashs n'avaient pas lieu sous Unix, soit parce que la gestion des zones mémoire était différente, soit parce que les violations d'accès n'avaient pas été repérées, mais de tels problèmes ont continué à se poser à chaque fois que je tentais de passer du monde "RendezVous" à un autre monde à travers une Gate, et de façon très difficilement débuggable (le débuggeur GDB luimême crashait lors des tentatives de debuggage), ce qui fait que j'ai dû effectuer tous mes tests dans un monde "RendezVous" modifié par mes soins, en copie locale dans mon cache. A ce jour Vreng continue de crasher quand on tente un autre monde que "RendezVous.vre", même si on charge cet autre monde directement au lancelment de Vreng et non auprès le passage par une Gate. Et au vu des erreurs rencontrées il s'agit très probablement de problème lors des libérations de mémoire, à cause de free() effectués sur des pointeurs dont la cible a déjà été libérée ailleurs par exemple. 3.Bonne surprise au niveau d’Eclipse Le module C++ pour Eclipse s’est révélé apte a gérer très correctement les makefiles du projet, et à intégrer assez correctement dans son interface graphique GDB, il a donc été d’un grand secours pour gérer simultanément un grand nombre de fichiers sources en même temps et suivre l’exécution du programme. Les fonctionnalités de recherche et d’auto- complétion d’Eclipse sont souvent hasardeuses ou trop lentes pour être utilisables, mais le reste de cet environnement a largement montré son utilité pour s’immerger dans un programme assez volumineux. Χ.Integration de mes fichiers sources dans vreng Pour pouvoir implémenter mon chargeur X3D, j'ai ajouté dans le projet une classe X3DOBJECT (fichier source et fichier en- tête) dans src/wo, qui doit gérer le lien avec le parseur des fichiers *.vre et l'interface graphique Ubit, et une classe X3D (fichier source et en- tête) qui s'occupe de charger et afficher les modèles x3d. L'ajout de ces fichiers dans les fichiers de configuration de la compilation (automake.am etc.) n’a pas posé de difficultés, par contre l’inclusion des headers Vreng nécessaires à l’intégration de mes fichiers dans le fonctionnement de Vreng a posé des problèmes. En effet, la plupart des en- têtes vreng utilisent des symboles (macros, fonctions) définis totalement ailleurs, et ce sont les fichiers sources (.c, .cpp) qui doivent effectuer les diverses inclusions d'en- têtes nécessaires, et ce dans le bon ordre. Il a donc fallu que je retrouve, à l'aide de la documentation Doxygen, quelles étaient les dépendances implicites entre les fichiers en- tête, pour que mes fichiers sources personnels puissent se compiler. La familiarisation avec la structure du programme a elle aussi été un peu difficile, à cause des origine non orientées- objet du moteur et de l'utilisation de fonctions à effet de bord : utilisation massive de "char*" au lieu de la classe "string", émulation d'un l'héritage entre classes grâce au passage en paramètre de pointeurs vers fonctions etc. ∆.Choix des librairies relatives a xml et x3d Mon approche première était d'intégrer dans le moteur Vreng un chargeur X3D déjà existant, comme X3Dtoolkit ou LibX3d. Malheureusement, il s'est avéré que ces librairies étaient totalement incompilables, la moindre inclusion d'en- tête donnant lieu à des dizaines d'erreurs de syntaxe. J'ai donc finalement décidé de coder manuellement le traitement des balises X3D. Il me fallait quand même pour cela un parseur Xml, afin d'éviter de devoir moi- même mettre les fichiers textes xml sous forme d'arbres syntaxiques exploitables en c++. Mais les principales librairies existantes (Libxml déjà utilisé dans Vreng, Xerces) se sont révélées bien trop complexes à appréhender pour l'usage que je voulais en faire, et j'ai finalement trouvé mon bonheur avec la mini- librairie Xmlparser (de Franck Vanden Berghen), constituée uniquement de deux fichiers sources, qui m'a permis de charger et d'explorer les fichiers X3D de façon très simple et sans contraintes sur la validité du document parsé (par rapport au DTD X3D). III.Developpement du module x3d Une fois l'intégration de la mini- librairie xml et de l'objet "X3Dobject" dans le moteur vreng réussis (ce qui a posé quelques problèmes, car j'avais oublié quelques appels de fonctions d'initialisation cruciales dans le constructeur du X3dobject, ce qui empêchait ce dernier d'être intégré convenablement dans l'interface graphique Ubit), a commencé la partie réellement productive, c'est à dire - mettre les données de l'arbre x3d sous une forme exploitable par le moteur de rendu - rendre ces données avec les fonctions Opengl - ajouter les fonctions chargées d'animer les objets, c'est à dire de modifier leurs caractéristiques spatiales et surfaciques en fonction du temps. Α.Traduction des données xml L'arbre Xml sous sa forme originelle ne se prêtait pas à un rendu Opengl, car les diverses propriétés d'un même objet (position, couleur, type...) étaient répartis dans plusieurs balises, à des niveaux très différents de l'arbre, et dans des formats non directement exploitables (les vecteurs d'entier étant encore sous forme de chaînes de caractères). C'est pourquoi j'ai crée un nouveau type d'arbre, formé de nœuds X3dShape représentant chacun un objet avec ses attributs, ou bien une transformation qui s'appliquera à l'ensemble des objets qui lui sont subordonnés. La construction de cet arbre a lieu grâce à un unique parcours récursif de l'arbre Xml. Les plus importants travaux pour cette conversion ont consisté en la création des fonctions de parsage de nombres. A cause du format assez spécial des tableaux de vecteurs dans X3d, des structures de données particulières que j'utilisais (Vector de flottants, String...), et des dangers de la fonction strtok(), il était en effet trop complexe et risqué d'utiliser les fonctions de parsage déjà existantes. J'ai donc codé une série de fonctions pouvant mettre sous forme de tableaux multidimensionnels les chaînes de caractères des attributs x3d, et gérant les flottants, les entiers et la vérification des dimensions de ces tableaux. Avec de surcroît une certaine souplesse syntaxique, pour parser aussi bien que possible quelque soit le nombre d'espaces, de virgules et de sauts de lignes dans la chaîne de caractères. La création des primitives a elle aussi été un peu problématique, car je souhaitais initialement les représenter par des objets "Solid" de vreng, devant être créés en insérant une chaine "<solid shape=..../>" dans le constructeur du Solid. Mais cela posait de nombreux problèmes à cause du fonctionnement des objets « Solid » avec le reste du moteur ; pour garder le contrôle je me suis donc au final reporté sur une gestion personnalisée des primitives, en utilisant les fonctions de dessin de la classe Draw ou d’autres codées manuellement, en fonction des besoins (en particulier, pour compenser le fait que la « box » de la classe Draw affichait mystérieusement toutes ses faces à l’envers quand je l’utilisais). Β. Rendu de l'arbre X3d En ce qui concerne le rendu des objets x3d, il était hors de question de se contenter à nouveau d'une fonction parcourant récursivement l'arbre, car pour une telle fonction, appelée plusieurs dizaines de fois par secondes et sur des arbres potentiellement très grands, la dégradation des performance et les risques de débordement de la pile étaient rédhibitoires. Je me suis donc occupé de la dérécursivation du parcours de l'arbre, ce qui posait surtout des problèmes au niveau du suivi de la hiérarchie des objets : selon que l'on passe d'un noeud à un noeud parent, enfant ou frère, la gestion de la pile des attributs et de la pile des matrices de transformation était différente. Cependant, l'ajout d'informations de localisation dans les noeuds de l'arbre X3d a permis d'implémenter le système itératif sans problèmes majeurs. Au niveau de l'affichage proprement dit, le fait qu'Opengl soit une machine à états posait quelques problèmes, au niveau des attributs d'une part (il fallait sans cesse activer et désactiver des drapeaux lors de l'arrivée dans la fonction de rendu, pour avoir le contexte souhaité), et au niveau des transformations d'autre part (la manipulation des piles de matrices de transformation par le reste du programme empêchait par exemple de forcer l'affichage d’un objet au centre du monde avec un glLoadIdentity()). Un cas typique fut la malédiction du "GL_COLOR_MATERIAL" : les différentes couleurs de matériau (ambiante, diffuse, spéculaire...) étaient initialisées à leurs valeurs "neutres" avant le rendu de mon arbre, le modèle ne comportait qu'une seule forme à afficher, et pourtant l'aspect de cette forme était systématiquement faux : si je mettais son unique attribut "diffuseColor" à vert, la forme était était jaune, si je le mettais à bleu elle était rose....après bien des essais, j'ai remarqué que cela dépendait des appels précédents à glColor3f, malgré le fait que le drapeau GL_COLOR_MATERIAL était systématiquement à « false » au moment de l'affichage des facettes (donc la couleur actuelle n'aurait pas dû avoir la moindre influence sur l'aspect de la surface). En fin de compte, je me suis rendu compte que l’appel à glEnable(GL_COLOR_MATERIAL), au lieu d’utiliser directement la valeur de la couleur lors de l‘affichage des surfaces, se contentait de faire recopier systématiquement le buffer « couleur » dans le buffer « matériau ambiant et matériau diffus » (si tels avaient été les paramètres passés à glColorMaterial()). Donc au moment d’arriver à l’affichage de mon model, le buffer « couleur ambiante », au lieu d’être neutre, contenait quand même une valeur erronée, due à des appels à glEnable(GL_COLOR_MATERIAL) ayant eu lieu entre- temps, lors du passage dans des nœuds de l’arbre ne contenant pas d’objet « surface ». Et cette couleur ambiante (ici rouge), malgré la présence de lumière, se mélangeait fortement avec la couleur diffuse : rouge + vert = jaune… Il convient donc de se rappeler que les propriétés des objets X3d se propagent dans leur hiérarchie, et qu’en conséquence il faut penser à bien redéfinir les champs (couleurs, transparence) qui pourraient gêner le bon aspect des surfaces, dans les objets fils. Χ.Animation L’animation a principalement consisté en la création, facile mais longue, de toutes les structures et énumérations aptes à stocker les données de l’animation, et en la mise au point des diverses initialisations. J’ai fait des recherches sur les interpolations de quaternions pour les rotations, auxquelles j’ai finalement préféré une simple interpolation linéaire afin de garder le contrôle sur le mécanisme (les formules des quaternions laissant peu de liberté tant qu’on n’a pas bien compris leur fonctionnement). IV. Fonctionnalités supportées La classe X3d supporte les principales caractéristiques du format X3D. Par manque de temps, un certain nombre de primitives (objets extrusions…), balises (instanciations, groupes…), et attributs contenus dans le standard X3D ne sont pas pris en compte, cependant il sera toujours possible de faire assez simplement évoluer le parseur et la boucle de rendu des images pour tenir compte des fonctionnalités supplémentaires désirées. Α.Format du fichier et parsage : Le parseur de la classe X3dest semi- validant. A partir du moment où il est bien formé, un fichier x3d sera accepté même s'il possède des balises non reconnues, et les attributs manquants ou invalides ne seront détectés que s'ils appartiennent aux fonctionnalités utilisées par l'afficheur. En particulier, le parseur des attributs du type "vecteurs de flottants" est très souple, et tentera de récupérer des vecteurs valides même si le nombre d'espaces, de virgules ou de sauts de lignes dans la chaîne de caractères n'est pas standard. Mis à part le fait que les balises subordonnées les unes aux autres doivent bien s'inclure dans l'ordre correspondant, il n'existe pas de contraintes sur l'ordre des balises, en particulier les balises Interpolator, TimeSensor et Route peuvent se situer à n'importe quel niveau du fichier, la mise en place finale des mécanismes d'animation aura lieu sans problème, car la résolution des liens entre objets nommés se fait seulement après le parcours complet du fichier. Β.Maillages : - primitives cone, sphere, box et cylinder. - maillages personnalisés à base de polygones (IndexedFaceSet) Χ.Matériaux : - support des couleurs diffuse, émissive et spéculaire - support des coefficients de couleur ambiante, brillance et transparence (couleur ambiante = coefficient de couleur ambiante * couleur diffuse) - support des textures pour les maillages "IndexedFaceSet" (qui ont alors besoin de coordonnées de texture) - support des couleurs, par face ou par sommet, pour les maillages "IndexedFaceSet" Priorité des matériaux : si une texture est spécifiée, elle écrase les autres matériaux. Sinon, les matériaux standard s’appliquent, sauf la couleur diffuse qui est remplacée (et donc avec elle la couleur ambiante), si elle est absente, par la couleur de la face ou du sommet (spécifiée avec glColor3f() par exemple). ∆.Animations : - support des animations sur les trois types de transformation (l'animation sur la taille est d'ailleurs un rajout par rapport à la norme X3d) - support des animations sur tous les paramètres de matériau, excepté les images et coordonnées des texture, ainsi que les couleurs par sommet ou face dans les IndexedFaceSet. Ces animations se font pour l’heure par interpolation linéaire, mais il est tout à fait possible de modifier le petit bout de code où a lieu le calcul des nouveaux paramètres des objets, pour réaliser une rotation par interpolation de quaternions, un déplacement sur une courbe de Bézier... Par souci d’efficacité les maillages complexes sont stockés dans des callLists Opengl, et il n’est donc pas possible de les modifier par morphing au niveau de leur forme ou de leurs couleurs par face/sommet (ce qui de toute façon seraient des animations un peu trop complexes pour être codées à la main, donc on perdrait un des intérêts du format Xml). Ces animations peuvent être en boucle ou non, et on peut facilement régler leur vitesse. Elles reposent en effet sur un ensemble d’objets « chronomètres », qui envoient leurs signal vers un certain nombre d’objets « interpolateurs », qui eux- mêmes envoient les données mises à jour vers un ou plusieurs champs de l’arbre X3d. Ainsi au final des paramètres de temps, de transformation spatiale, et de matériau peuvent être partagés par plusieurs objets X3d. A noter que les valeurs spécifiées initialement dans les champs « attributs » des balises X3d, si ces champs sont par ailleurs animés, seront écrasés par les premières valeurs des interpolateurs concernés, lors de l’initialisation de ces interpolateurs. Ε.Gestion des collisions Pour avoir la gestion des collisions, il suffit d'inclure dans la balise X3dObject du fichier *.vre les dimensions voulues pour la boite englobante : Attribut « dim="dimx dimy dimz" » En l’absence de cet attribut, les dimensions de la bounding- box de l’objet X3dObject sont nulles, donc elle est inefficace. Φ.Interactions avec l'utilisateur Plusieurs actions sont possibles sur un objet x3d, qui est par défaut animé au lancement du programme : - lecture (remet en animation l’objet, depuis sa position actuelle) - pause (garde l'animation à sa position courante, aucun effet si l'objet est déjà inanimé) - arrêt (remet l'animation à zéro et la stoppe) V.Rapide Manuel du module X3d Α.Classes ajoutées au programme : X3dObject : C'est la classe qui fait l'interface entre les modèles X3D et le reste de Vreng : elle s'occupe du parsing des attributs spécifiques de la balise vreng "x3dobject", et du lien entre les boutons d'Ubit et leurs actions sur l'objet X3d qu'elle contient. X3d : ce type s'occupe de toutes les opérations concernant le modèle X3d lui- même : - il parse le fichier X3det le transcrit dans un format exploitable - il gère l'animation des paramètres du modèle. - il s'occupe d'afficher à l'écran le modèle qu'il stocke Et X3d utilise lui même plusieurs classes pour gérer le modèle : - VectorTools, biliothèque d'outils pour parser des chaines de caractères et les convertir en flottants contenus dans des objets vector. - X3dShape, pour stocker sous forme d'arbre les données de transformation, de matériau et de maillage initialement contenues dans le fichier au format x3d. - TimeSensor et Interpolator, objets qui s'occupent de la mesure du temps relatif et de calcul des valeurs interpolées pour l'animation. Β.Format de la balise X3D dans le fichier VRE : <x3dobject url="/vre/***/***.x3d" dim="dimx dimy dimz"> <x3dobject/> Il est possible d’inclure des solides à l’intérieur de cette balise. Χ.Syntaxe des fonctionnalités supportées (format X3d) : Le plus simple est de s’inspirer du fichier exemple « tutorial.x3d » joint, qui retrace l’ensemble des balises et attributs connus par le chargeur de Vreng. Le principe est simple : donner la structure hiérarchique des différentes surfaces du modèle, avec leurs matériaux, créer un ensemble de chronomètres et d’interpolateurs, et relier les champs de ces différents objets avec les balises « ROUTE ». Sachant que ces différentes opérations peuvent être écrites dans un ordre quelconque, et que le chargeur Vreng affichera autant que possible les origines des erreurs qu’il rencontre dans le parsage et l’affichage du modèle. Les routes relient des balises nommées, et ces balises peuvent être : des TimesSensors, des Interpolators, des Shape, des Transform, et des Material. Les autres balises ne reconnaissent pas l’attribut DEF (nom de balise). VI.Conclusion : Par delà les quelques nouveautés que mon module a ajouté à Vreng (support d’un format de fichier 3D simple et convivial, avec des possibilités assez larges d’animation des transformations et des matériaux), ce projet a été l’occasion pour moi de découvrir Linux, le C++ avancé (STL, templates) sous Eclipse, et Opengl, ainsi que de m’entraîner à l’intégration de nouveaux modules dans un projet de large ampleur.