Bonjour
Je développe un moteur 3D depuis quelques temps, j'ai implémenté les animations et tout fonctionne correctement mais je suis confronté maintenant à un problème. Tout d'abord voici un résumé de comment ça fonctionne jusqu'à présent :
Je crée un modèle skinné et animé sous un logiciel 3D, je l'exporte dans un format perso, j'exporte également les animations dans un autre format et je charge le tout dans mon moteur. Ensuite je crée mon modèle avec son skin (tableau qui associe à chaque vertex la somme pondérée des bones auquel il est lié) ainsi que son squelette.
Pour assurer la bonne transformation des vertex durant l'animation, j'utilise pour chaque modèle skinné 2 squelettes : un qui est toujours à la même position, celle d'origine, et l'autre qui va bouger conformément à l'animation. Je parcours ensuite la hiérarchie de ces 2 squelettes à chaque frame et je calcule pour chaque bone une matrice qui sera égale à la matrice de passage du squelette d'origine vers le squelette courant. J'obtiens ainsi un tableau contenant autant de matrices qu'il y a de bones dans mon squelette, chaque matrice décrivant la transformation subie pour passer de la position d'origine vers la position courante, et donc la déformation que devra subir le vertex qui l'utilisera pour suivre le mouvement de son bone. J'envoie ensuite le tout au vertex shader qui multiplie alors la matrice de transformation initiale (projection * modelview) par cette matrice. Au moment de l'animation, les déformations sont correctes donc je n'ai pas d'erreur à ce niveau là.
Maintenant, j'aimerais aller un peu plus loin en permettant au moteur de pouvoir lier un mesh à un bone, ce que font tous les moteurs 3D, mais le problème c'est que jusqu'à présent les 2 squelettes de mon mesh n'étaient pas liés à la scène car j'ai constaté que ça provoquait des problèmes dont je n'ai pas réussi à identifier correctement la cause (je ne suis pas très matheux à la base, j'ai déjà beaucoup galéré au niveau des matrices mais il y a encore des trucs qui m'échappent). J'ai donc imaginé plusieurs solutions qui s'apparentent plutôt à du bidouillage mais je vais d'abord essayer de décrire brièvement l'architecture de cette partie de l'application :
J'ai une classe entité qui dérive d'une classe de node et qui contient un pointeur vers les 2 squelettes (eux aussi de type node). Au moment du rendu, l'entité calcule sa matrice de transformation en fonction de sa matrice locale et de la matrice world de son parent. Pour le moment je n'ai pas eu besoin de lier les 2 squelettes à l'entité car je ne m'en sert que pour calculer les transformations locales subies par les différents bones du squelette, et j'envoie ensuite au shader la matrice world de l'entité ainsi que les matrices de déformation de ses différents bones. Le shader va donc composer ces matrices pour calculer la matrice de transformation finale (en pondérant évidemment les transformations pour les vertex influencés par plusieurs bones), ce qui fait que la méthode consistant à ne pas lier les squelettes au graphe de scène marche parfaitement... jusqu'au moment où je décider de lier d'autres entités à certains bones de mon squelette.
Etant donné qu'il n'est pas lié au graphe de scène, le bone parent de l'entité que je viens de linker ne contiendra que la transformation du bone par rapport au root du squelette, mais pas les informations sur les transformations de la caméra, ce qui fait qu'il restera toujours au même endroit par rapport à celle-ci.
Deux solutions possibles se présentent donc :
. Soit je link les deux squelettes à mon entité, mais ça me pose alors un problème pour calculer la matrice de passage des bones d'origines vers les bones courants. En effet, si on nomme M0 la matrice world de l'entité et M1...Mn les matrices de tous les bones du squelette d'origine et M1'...Mn' les matrices des bones courants, ça veut dire que pour un bone k, j'aurais sa matrice d'origine de cette forme :
Mg = M1 * ... * Mk
et sa matrice courante de cette forme :
Mc = M1' * ... * Mk'
et donc la matrice de passage vaudra :
Mp = Mc * Mg^-1 = M1' * ... * Mk' * Mk^-1 * ... * M1^-1.
Par contre si je link les 2 squelettes à l'entité, j'aurais la matrice de passage d'un bone k de la forme suivante :
Ap = M0 * M1' * ... * Mk' * Mk^-1 * ... * M1^-1 * M0^-1 = M0 * Mp * M0^-1
Or, arrêtez moi si je dis une bétise mais normalement pour tout M0 (sauf pour l'identité évidemment), M0 * Mp * M0^-1 =/= Mp, ce qui est d'ailleurs confirmé par les déformations complètement foireuses que subi mon modèle au moment de l'animation si j'utilise cette méthode.
C'est la raison pour laquelle j'ai imaginé une autre solution un peu crade qui consiste simplement à stocker dans chaque entité linkée à un bone un pointeur vers l'entité "maitre", c'est à dire celle qui contient le bone auquel elle est linkée. Au moment du rendu je multiplie la matrice world de l'entité linkée par la matrice world de l'entité maitre de cette manière : WorldBoneLinké = WorldEntitéMaitre * WorldBoneLinké.
Le problème c'est que ça ne marche toujours pas , au moment du rendu j'ai mon animation qui est jouée correctement, les déformations du meshes sont bonnes mais en revanche les objets liés se mettent n'importe où (je précise quand même qu'ils suivent bien l'animation, mais ils sont dans un autre repère que l'os auquel ils sont liés).
Même si c'est une bidouille et qu'il faudrait sûrement que j'implémente un algo moins crade par la suite, cette méthode me semble pourtant correcte non
Sinon j'ai imaginé une 3eme solution mais elle me parait encore plus pourrie que la 2eme, elle consisterait à laisser les deux squelettes non liés à l'entité et à utiliser un 3eme squelette lié à l'entité auxquelles seraient liées les entités filles, mais du coup ça veut dire qu'il faudra faire 3 update de squelette pour chaque entité au lieu de deux actuellement, sachant que ça utilise des inversions et des multiplications de matrices qui ne sont pas particulièrement rapide à calculer (d'autant plus que le modèle que j'utilise pour les test a 53 bones, ce qui est plutôt standard dans le jeu vidéo). En fait j'ai simplement imaginé cette méthode tout à l'heure et je n'ai pas encore bien réfléchi à toutes les conséquences, si ça se trouve je risque d'être confronté à d'autres difficultés ou bidouilles que je n'ose imaginer.
Voilà, merci pour votre patience pour ceux qui ont lu jusqu'au bout, j'aurais bien aimé être plus concis et plus clair mais c'est pas évident d'expliquer tout ça en peu de mot. Si l'un d'entre vous a déjà travaillé sur un moteur 3D et qu'il connait les techniques qui sont utilisées dans ce genre de cas, car à mon avis il doit exister une solution plus simple et élégante que mes algos, je serais ravi de discuter avec lui