downloading

Transcription

downloading
Tutoriel AReVi/hLib 2
Ronan BILLON
lundi 15 janvier 2007
Table des matières
1. Introduction ........................................................................................................................................................2
2. Théorie et principe de la hLib...........................................................................................................................2
2.1. Classe de données ....................................................................................................................................3
2.2. Classe d’affichage ....................................................................................................................................3
2.3. Classe d’animation...................................................................................................................................4
3. Utilisation et pratique à partir des exemples ...................................................................................................5
3.1. testBody : création manuelle d’un squelette............................................................................................6
3.2. testSkin ....................................................................................................................................................7
3.3. testLoad....................................................................................................................................................8
Bibliographie...........................................................................................................................................................9
1. Introduction
ARéVi est une bibliothèque constituant la base commune à la majorité des travaux menés au LISYC/CERV
http://www.cerv.fr/. La thématique de celle-ci repose sur l’activation de multiples entités autonomes en interaction dans des environnements tridimensionnels interactifs (simulation biologique, circulation routière, outils de
formation...). Pour avoir de plus amples informations, un chapitre y est consacré dans « Le traité de la réalité
virtuelle » [HCJ06] . Le code source est à la disposition de la communauté, selon la licence LGPL, depuis le site
http://www.enib.fr/~harrouet/ ou encore sur SourceForge (http://sourceforge.net/projects/arevi/)
Cette documentation ne contient pas les instructions pour l’installation, mais vous pourrez retrouver toutes les
instructions pour compiler la bibliothèque dans le fichier README. Les exemples fournis avec cette documentation
se compile avec la suite de commande traditionnelle sous *nix :
Exemple 1. Compilation et exécution des exemples
$ ./configure
======== Building dependencies...
...
$ make
$ ./myexecutable
Note : Pour que le script de configuration s’exécute correctement, il est indispensable
que l’exécutable arevi-config soit dans votre PATH. Consultez la documentation situé dans
MY_PATH_TO_AREVI/AReVi/Vrac/areviGuide.pdf.
hLib (Humanoid Library) est une bibliothèque qui ajoute les fonctionnalités nécessaires pour créer des personnages animés à base de squelette. Ces personnages ne sont pas obligatoirement des humanoïdes. En théorie
hLib peut être compilée sur toutes les plateformes où ARéVi est disponible. Les objectifs de la hLib sont les
suivants :
•
Création et importation de squelettes (depuis les types de fichier suivant : SMD (Half Life 2), MD5 (Doom 3),
BVH (Biovision)) ;
•
Importation d’animation directe à partir de keyframes (même type de fichier) ;
•
Skinning animation de la peau en suivant le squelette ;
•
Animation par cinématique inverse.
•
Animation en temps réel de plusieurs individus.
Le but de ce document est de présenter les concepts de base nécessaires à l’utilisation de cette bibliothèque.
Il s’adresse à un public avertit, connaissant déjà ARéVi et ayant des notions de mathématiques géométriques
3D (matrices, quaternions, ...). Pour avoir des informations sur les classes présentes, une documentation a été
générée automatiquement avec Doxygen. Vous la trouverez dans le ballot. Pour plus de détails sur les différents
mécanismes déployés ici, nous invitions le lecteur à consulter la bibliographie. La plupart des algorithmes utilisés
sont issus de publications scientifiques.
Pour utiliser cette bibliothèque, veuillez ajouter à votre compilateur le lien vers AReViHLib2.(so,dll).
2
Tutoriel AReVi/hLib 2
2. Théorie et principe de la hLib
Ce chapitre présente la structure élémentaire composant tout humanoïde : le squelette. Le corps humain est
composé de plus de 200 os, reliés entre eux par les muscles et les ligaments. Les degrés de liberté des articulations
du squelette (genou, épaule, . . . ) sont contraints, ainsi tous les mouvements ne sont pas possibles. Mais, Le
squelette humain permet un ensemble de mouvements très complexes. La reproduction de ceux-ci sur ordinateur
est difficiles. Il est commun en animation de synthèse de n’utiliser qu’un nombre restreint d’articulations afin de
simplifier les calculs.
2.1. Classe de données
Avant de voir le squelette en lui-même, nous allons voir la structure élémentaire le composant. Il s’agit
du Joint, c’est la jonction entre deux os du squelette. Dans la hLib, le choix a été fait de le faire dérivé de
la classe Base3D. en effet, chaque Joint est composé d’une position et d’une orientation. Le squelette sera
alors une simple collection de joints reliés entre eux, on pourra le représenter sous forme d’arbre. Les liens
entre joints se font toujours dans le sens : "un fils connaît son père" et non "un père connaît ses fils" comme
c’était le cas précédemment dans la première version de la hLib. Les enfants sont attachés (méthode de Base3D :
attachTo(parent)) à leur père, ce qui facilite la manipulation. Supposons par exemple que l’utilisateur souhaite faire bouger le bras. Pour cela il lui suffit de manipuler l’articulation de l’épaule et les enfants (coude,
poignet, doigts. . . ) seront automatiquement mis à jour.
La manipulation des joints peut se faire indifféremment dans le repère global (très rare), le repère de
l’objet (exemple : getPosition() de la classe Base3D), le repère du parent (setLocalTranslation() et
setLocalRotation()) et le repère local du joint courant. Avant toute manipulation assurez-vous de savoir
quelle est votre référence.
Avertissement
Il y a deux différences notables avec la première version :
1. la disparition d’une position de repos et du delta. En effet, précédemment, il était
nécessaire de préciser une position et orientation de repos du joint dans le repère
du parent et d’effectuer toutes les manipulations en faisant varier un delta. Cette
particularité n’étant pas indispensable, elle a été supprimée par soucis de simplicité ;
2. la disparition des contraintes articulaires. Devant la difficulté à implémenter, puis
à exprimer et utiliser correctement les contraintes, elles ont été simplement supprimées. La représentation par quaternion (3 degrées de liberté) n’est absolument pas faite pour être contrainte, car il existe une infinité de combinaison de
rotations simples pour atteindre une orientation donnée. Cette possibilité sera
re-insérée lorsqu’une représentation adéquate sera trouvée.
2.2. Classe d’affichage
L’affichage des humanoïdes, de cette version, est géré par la classe BodyShape qui hérite d’Shape3D. Cette
classe génère automatiquement une représentation symbolique du squelette avec des sphères pour les joints et des
lignes 3D pour les os. L’affichage du squelette est mis à jour automatiquement dès qu’un joint est déplacé. Il est
important de préciser que le squelette du BodyShape est une simple référence, toute modification effectuée sera
automatiquement retranscrite à l’affichage. Pour toute manipulation sur les joints (à partir de setPosition() et
setOrientation() de la classe Base3D), le repère de référence est celui de l’objet 3D et non le repère global
du monde.
3
Tutoriel AReVi/hLib 2
Pour embellir le squelette, une peau, se déformant en suivant ses mouvements, peut être ajoutée. La classe
Skin héritant de Surface3D gère cela parfaitement. Pour un même BodyShape, plusieurs peaux peuvent exister,
cela permet un découpage de la structure du personnage, pour par exemple changer seulement sa tête. Le principe
de la déformation s’appuie sur l’orientation et la position des joints, ainsi qu’une pondération. Ce qui signifie particulièrement que deux squelettes ayant une forme identique peuvent avoir des orientations de joints différentes,
et donc un comportement de la peau différent.
Figure 1. Deux squelettes identiques avec une orientation des joints différente
L’image précédente montre l’utilisation des programmes de test $ ./testMorphet $ ./testLoad. Avec le
premier, il y a une adaptation de l’orientation des joints en fonction de la peau alors que le second montre une
application brute et directe de la skin sur le squelette.
2.3. Classe d’animation
L’animation se découpe en deux parties majeures : La cinématique directe et la cinématique inverse.
L’animation directe est une technique courante qui se base sur une décomposition de l’animation en positions clés. Ces images clés sont ensuite relues pour re-générer l’animation. La classe Keyframe dérive de la
classe Skeleton, une position clé correspond à un squelette avec une pose bien définie. La seule variable supplémentaire à y ajouter est le temps que doit durer cette pose. Ces images clés sont regroupées dans un conteneur
KeyframeAnimation. Il est très rare quelles soient manipulées directement. Une animation décrit de manière
4
Tutoriel AReVi/hLib 2
ordonnée les différentes postures à appliquer à un squelette pour faire un mouvement. La durée du mouvement
est calculée automatiquement au moment d’une insertion ou d’une suppression d’image clé.
La cinématique inverse permet de trouver une position et une orientation de chaque joints du squelette pour
satisfaire une condition de position d’un joint particulier. Par exemple, je souhaite que la main d’un humanoïde
atteigne la porte, l’algorithme va calculer les orientations de chacun des joints de la chaîne jusqu’à la racine.
L’algorithme utilisé par la hLib est le Cyclic Coordinate Descent (CCD). En plusieurs étapes, l’algorithme modifie
l’orientation de chaque joints pour tenter d’atteindre la position voulue.
3. Utilisation et pratique à partir des exemples
Avant de commencer, voici un diagramme UML synthétique présentant l’organisation des classes fournient
par la hLib.
Figure 2. Synthèse du diagramme de classe
Pour présenter l’utilisation de la hLib, nous utiliserons les exemples fournis dans le ballot. Dans l’ordre :
•
testBody : création manuelle d’un squelette ;
•
testInteractiveBody : création manuelle d’un squelette et manipulation interactive du squelette à partir de cinématique inverse ;
•
testSkin : création manuelle d’un squelette simple, génération d’une peau à partir d’une sphère ;
•
testLoad : importation de plusieurs fichiers contenant le squelette, la peau et l’animation ;
•
testMorph : importation de plusieurs fichiers contenant le squelette, la peau et l’animation avec une adaptation
du squelette pour une déformation adaptée de la peau ;
5
Tutoriel AReVi/hLib 2
•
tesLoadInteractive : importation d’un fichier contenant le squelette et la peau, l’humanoïde pourra être manipulé
par cinématique inverse.
3.1. testBody : création manuelle d’un squelette
Ce test permet de créer un squelette qui aura la forme d’une fourche. Un joint sera modifié périodiquement
par une activité.
Exemple 2. testBody.cpp
...
ArRef<Skeleton> skeleton = Skeleton::NEW();
ArRef<Joint> joint = Joint::NEW();
joint->setName("root");
joint->setLocalTranslation(Vector3d(-0.5, 0.0, 0.0));
skeleton->addJoint(joint);
joint = Joint::NEW();
joint->setName("joint1");
joint->setParent(skeleton->findJoint("root"));
joint->setLocalTranslation(Vector3d(1.0, 0.0, 0.0));
joint->setLocalRotation(Quaterniond(1.6,0.0, 1.0, 0.0));
skeleton->addJoint(joint)
...
La création d’un squelette vide se déroule de la même façon que tout objet ARéVi avec la commande
Skeleton::NEW(). La suite correspond à l’ajout des joints dans le squelette. Les étapes à respecter dans l’ordre :
1. instancier avec Joint::NEW() ;
2. nommer le joint avec joint->setName("root"). Cette étape est très importante car plusieurs vérification
sont faites (asssert) que ce soit lors de l’insertion, du clonage de squelette, de l’application de keyframe.
3. attribuer un parent avec joint->setParent(skeleton->findJoint("root")). Cette étape doit
précéder toute modification locale du joint. En effet, si celui-ci n’a pas de parent comme référent, il ne
pourra pas s’orienter correctement.
4. placer et orienter avec les méthodes de Base3D ou celle du type joint->setLocalTranslation().
La suite est simple, il suffit de passer le squelette à un BodyShape pour qu’il s’affiche en 3D.
Exemple 3. Passage du squelette à un BodyShape
ArRef<BodyShape> bodyShape = BodyShape::NEW();
bodyShape->setSkeleton(skeleton);
body = Body::NEW();
body->setShape(bodyShape);
Lors de la création des joints, la référence d’un a été sauvegardé. Celle-ci servira durant l’activité pour faire
tourner un morceau d’un angle de PI/4.
6
Tutoriel AReVi/hLib 2
Exemple 4. testBody.cpp
...
body->setPosition(1,0,0);
scene->addObject(body);
...
bool TestBody::event(ArRef<Activity> /*act*/, double /*dt*/)
{
joint_test->pitch(M_PI_4);
return true;
}
...
Le cas suivant s’appuyant fortement sur cet exemple, il ne sera pas détaillé. La différence notable est
l’instanciation du Body avec la commande : body = InteractiveBody::NEW(). J’encourage fortement la
lecture de la documentation générée par Doxygen pour l’utilisation des chaînes pour la résolution de la cinématique inverse. La méthode setAutoIk() permet d’activer la résolution. La méthode setIkChain() permet de
définir la propagation de la résolution depuis le joint sélectionné jusqu’à la racine.
La résolution de la cinématique inverse se base sur l’algorithme CCD. Celui-ci est implémenté dans la classe
IkCcd, elle contient une variable statique définissant le nombre de fois que la chaîne entière doit être estimé. Et,
une méthode statique solve(goal, end, parent) qui modifiera la chaîne depuis le joint end jusqu’au joint
parent pour atteindre la position fournit par goal. Bien que ce paramètre soit une Base3D, seul la position est
utilisée.
3.2. testSkin
Cet exemple vise à réaliser une peau en forme de sphère qui sera déformée en suivant les différents mouvements des joints du squelette en étoile sous-jacent.
Exemple 5. testSkin.cpp
...
ArRef<Skeleton> skeleton = Skeleton::NEW();
ArRef<Joint> joint = Joint::NEW();
joint->setName("root");
skeleton->addJoint(joint);
...
ArRef<BodyShape> bodyShape = BodyShape::NEW();
bodyShape->setSkeleton(skeleton);
body = Body::NEW();
body->setShape(bodyShape);
...
La première étape est la création du squelette avec des joints disposés en étoile autour d’un noeud racine.
Puis d’appliquer ce squelette à une BodyShape. Ensuite vient la génération de la peau, pour le cas présent et d’un
point de vue didactique, elle est générée à partir d’une sphère. Toute Skin est un objet Surface3D avec deux
informations fondamentales pour chaque vertex : une liste de joints à suivre et une pondération pour chaque joints
de la liste.
7
Tutoriel AReVi/hLib 2
Exemple 6. testSkin.cpp
...
ArRef<Skin> skin = Skin::NEW();
ArRef<Sphere3D> sphere = Sphere3D::NEW();
sphere->setRadius(1.0);
generate(skin, sphere, skeleton);
bodyShape->addSkin(skin);
Comme indiqué dans la documentation générée par Doxygen, il faut impérativement utiliser la méthode
setRestPose() après avoir ajouté toutes les peaux au corps, sinon, il risque d’y avoir des déformations
« étranges ». Le reste du code permet de créer six cube de manipulation qui sont attachés aux joints grâce à la
méthode attachTo(), voici un exemple d’intérêt pour la classe Joint d’hériter de Base3D. En manipulant les
cubes, on remarque bien la déformation de la peau.
3.3. testLoad
Cette utilisation sera la plus commune de la hLib. Ce programme prend en argument un ou deux fichiers et
affiche ce qu’il contient. Il peut s’agir :
•
d’un squelette simple ;
•
d’un squelette avec une peau (type *.md5mesh et *.smd) ;
•
d’un squelette avec une animation (type *.bvh, *.md5anim et *.smd) ;
Plusieurs combinaisons sont possibles, tant que l’ordre et le nom des joints est respecté. Dans ce programme
la première étape est d’utiliser les méthodes static de la classe HLibLoader. Puis en suivant les mêmes étapes
que la version précédente, récupération des données après avoir chargé les fichiers avec : LoaderData data
= HLibLoader::loadFile(str);. LoaderData contient trois champs : body, animation et failed qui
sont suffisamment explicite. Le champs animation peut avoir la valeur Animation::nullRef() si aucune information n’a été trouvée dans le fichier.
Exemple 7. testLoad.cpp
...
StlString str = ArSystem::getCommandLine()[1];
cerr << "Loading " << str << " ...\t";
LoaderData data = HLibLoader::loadFile(str);
if(data.failed) {
cerr << "FAILED" << endl;
} else {
cerr << "done" << endl;
}
body = data.body;
scene->addObject(body);
...
L’animation s’effectue simplement en appliquant une pose au corps. Cette pose est déterminée en fonction
du temps courant, les étapes sont les suivantes :
1. fmod(act->getTime(),animation->getDuration()) : on calcule le temps courant modulo la durée de l’animation
;
8
Tutoriel AReVi/hLib 2
2. animation->evaluate(dt) : l’animation retourne une pose (Keyframe) en fonction des positions clés ;
3. body->applyPose() : qui est l’application de cette pose par recopie des données de la Keyframe.
Le dernier exemple testMorph ne sera pas détaillé, car une seule ligne a été ajoutée. Il s’agit de l’adaptation
du Body pour que les orientations de départ de l’animation correspondent aux orientations de départ de la peau.
Si cette adaptation n’est pas faite, la peau devient « étrange », voir les images Figure 1. Cette déformation ne
se produira pas si les joints de l’animation sont bien en correspondance avec les joints du squelette associé à la
peau, ce qui arrive souvent lorsque toute la création est faite d’un coup, mais plus rarement lorsque l’on récupère
des animations venant d’ailleurs (exemple fichier *.bvh appliqué avec un bonhomme *.md5). La commande est
simple : body->setBaseframe(animation->evaluate(0),false). Le premier paramètre est la première
pose de l’animation et le second paramètre est un booléen permettant à l’algorithme de déformer le squelette.
Cette option n’est à utiliser que si les squelettes sont très proches sinon, l’« étrange » reviendra.
Bibliographie
[HCJ06] Fabrice Harrouet, Eric Cazeaux, et Thomas Jourdan, Arévi, chapitre du volume « Outils et modèles
informatiques des environnements virtuels » du traité de la réalité virtuelle, P. Fuchs, G. Moreau, et 72
auteurs, 4 volumes, Les Presses de l’Ecole des Mines de Paris, www.esnmp.fr/Presses, mars 2006.
9