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.

Documents pareils