Comment appliquer une animation squelette à partir d'un fichier .x (Direct X)?

8

En utilisant le format .x pour exporter un modèle depuis Blender, je peux charger un maillage, une armature et une animation. Je n'ai aucun problème à générer le maillage et à visualiser les modèles en jeu. De plus, j'ai des animations et l'armature correctement chargées dans les structures de données appropriées.

Mon problème est d'appliquer correctement l'animation aux modèles. J'ai le cadre pour appliquer les modèles et le code pour sélectionner les animations et parcourir les cadres.

D'après ce que je comprends, le AnimationKeys à l'intérieur du AnimationSetfournit les transformations pour transformer la pose de reliure en pose dans le cadre animé. Comme petit exemple:

Animation {
  {Armature_001_Bone}
  AnimationKey { 
    2; //Position
    121; //number of frames
    0;3;     0.000000, 0.000000, 0.000000;;,
    1;3;     0.000000, 0.000000, 0.005524;;,
    2;3;     0.000000, 0.000000, 0.022217;;,
    ...
  }
  AnimationKey { 
    0; //Quaternion Rotation 
    121;
    0;4;   -0.707107, 0.707107, 0.000000, 0.000000;;,
    1;4;   -0.697332, 0.697332, 0.015710, 0.015710;;,
    2;4;   -0.684805, 0.684805, 0.035442, 0.035442;;,
    ...
  }
  AnimationKey { 
    1; //Scale
    121;
    0;3;     1.000000, 1.000000, 1.000000;;,
    1;3;     1.000000, 1.000000, 1.000000;;,
    2;3;     1.000000, 1.000000, 1.000000;;,
    ...
  }
}

Donc, pour appliquer l'image 2, je prendrais la position, la rotation et l'échelle de l'image 2, créerais une matrice de transformation (appelez-la Transform_A) à partir d'eux et appliquerais cette matrice aux sommets contrôlés par Armature_001_Boneleurs poids. Donc je fourrerais TransformAdans mon shader et transformerais le sommet. Quelque chose comme:

vertexPos = vertexPos * bones[ int(bfs_BoneIndices.x) ] * bfs_BoneWeights.x;

bfs_BoneIndiceset bfs_BoneWeightssont des valeurs spécifiques au sommet actuel.

Lors du chargement dans les sommets du maillage, je les transforme par le rootTransformet le meshTransform. Cela garantit qu'ils sont orientés et mis à l'échelle correctement pour afficher la pose de liaison.

Le problème est que lorsque je crée cette matrice de transformation (en utilisant la position, la rotation et l'échelle de l'animation), elle ne transforme pas correctement le sommet. Il y a probablement plus que l'utilisation des données d'animation. J'ai également essayé d'appliquer les hiérarchies de transformation osseuse, toujours pas de dés. En gros, je me retrouve avec des modèles tordus. Il convient également de noter que je travaille dans openGL, donc toute transposition de matrice qui pourrait devoir être appliquée doit être prise en compte.

De quelles données ai-je besoin et comment les combiner pour appliquer des animations .x aux modèles?

J'ai fait quelques maquettes de ce à quoi cela ressemble, au cas où cela serait utile.

Je voulais d'abord tester la traduction, c'est une tête flottante, à quoi ça ressemble dans Blender:

http://i.stack.imgur.com/NAc4B.gif

Et à quoi cela ressemble dans le jeu (ne vous occupez pas des couleurs):

http://i.stack.imgur.com/nj2du.gif

Ensuite, pour la rotation, l'animation était la tête tournant à 360 degrés autour de l'axe vertical. Voici à quoi cela ressemble en jeu:

http://i.stack.imgur.com/gVyUW.gif

Notez qu'il ne doit pas y avoir d'inclinaison, seulement une rotation comme un manège.

Mise à jour

J'ai la partie traduction de l'animation qui fonctionne. Mais ça fait un peu hacky et je ne vois pas comment l'appliquer à la rotation.

La traduction fonctionne en suivant ces étapes:

  1. Prenez la position du cadre d'animation et échangez les valeurs yetz
  2. Traduire la matrice de transformation par la position modifiée
  3. Transposer la matrice de transformation
  4. Appliquer la matrice de transformation aux sommets

Voilà comment cela peut fonctionner, mais comment est-il censé fonctionner généralement pour la position, l'échelle et la rotation?

MichaelHouse
la source
2
Avez-vous transformé le sommet avec la matrice de pose de liaison inverse avant d'appliquer le skinning?
r2d2rigo
J'ai, cela a un effet, mais ce n'est pas souhaité. Il semble affecter la représentation visuelle du modèle (inverser toutes les normales, ressemble), mais n'a aucun effet sur le mouvement / rotation / échelle.
MichaelHouse
Pourriez-vous publier une capture d'écran illustrant le problème?
r2d2rigo
@ r2d2rigo J'ai mis à jour avec des gifs animés.
MichaelHouse
1
Je fais surtout du brainstorming ici, mais si l'ordre des vecteurs (xyz, zxy) diffère entre .x et opengl, vous obtiendrez des effets amusants. Le fait que la transposition aide signifie qu'elle utilise un vecteur de colonne (ou ligne) pour la position, et non l'inverse. en.wikipedia.org/wiki/… a une description des rotations pour 2D. Je ne sais pas trop comment modifier un quaternion. Ou la différence entre les formats .x et openGL
Daniel Carlsson

Réponses:

3

Il convient également de noter que je travaille dans openGL, donc toute transposition de matrice qui pourrait devoir être appliquée doit être prise en compte.

C'est peut-être la clé, avez-vous essayé d'annuler certaines coordonnées Z pour convertir des coordonnées gauches DirectX en coordonnées droitières OpenGL?

Vous pouvez annuler tous les Z avant d'appliquer vos transformations et annuler à nouveau lorsque vous avez terminé, voyez si la sortie s'améliore.

ÉDITER

L'échange des coordonnées Y et Z est une autre façon de changer la neutralité. Vous devez donc adapter vos transformations, consultez ce document pour plus de détails .

Le fait que vous deviez transposer votre matrice signifie également que vous passiez de la ligne principale à la colonne principale ou vice versa. Cela devrait être moins compliqué à gérer, vous n'aurez probablement qu'à trouver le bon "endroit" pour le faire dans votre pipeline. Je suis juste en train de deviner, mais le faire sur des quaternions pourrait simplement signifier les changer en matrices, transposer et revenir en quaternions, si votre bibliothèque Math vous offre cette option.

Laurent Couvidou
la source
Genre de me dire ce que je sais déjà ici ...
MichaelHouse
1
Eh bien, vous n'avez pas mentionné la neutralité dans votre question, et je pense que c'est au moins une partie de votre problème. Je ne peux pas supposer que vous faites ou ne savez pas que DirectX et OpenGL suivent des conventions différentes sur ce point (c'est en fait un peu plus complexe ). Notez que cela pourrait aider d' autres personnes à lire cette question à la voir mentionnée, ce qui est un peu le but de tout ce site Web.
Laurent Couvidou
Assez juste, vous avez raison. J'ai seulement fait allusion au fait que je connaissais les conversions qui devaient avoir lieu lorsque j'ai mentionné l'utilisation d'OpenGL et les transpositions qui devaient se produire.
MichaelHouse
2

OK, j'ai mis un peu plus de temps pour cela et je l'ai fait fonctionner pour plusieurs os, et considérablement moins comme un hack (bien que toujours un peu). Le système que j'avais en place à l'origine était presque correct. Cependant, les principaux problèmes étaient:

  1. Blender enregistre les Quaternions au format W, X, Y, Z. Je chargeais les X, Y, Z, W .
  2. Mon code pour convertir un Quaternion en matrice était incorrect. En fait, je ne sais toujours pas pourquoi cela est incorrect, mais je sais que le code suivant fonctionne, dans le cadre de ma classe Quaternion:

    public Matrix4f toMatrix() {
        Matrix4f tmpMatrix = new Matrix4f();
        if (this.length() > 0)
            this.normalise();
    
        float xx = this.x * this.x;
        float xy = this.x * this.y;
        float xz = this.x * this.z;
        float wx = this.x * this.w;
    
        float yy = this.y * this.y;
        float yz = this.y * this.z;
        float wy = this.y * this.w;
    
        float zz = this.z * this.z;
        float wz = this.z * this.w;
        //note the "* -1"s
        //I needed those to make the same matrices Blender was expecting
        tmpMatrix.m00 = (1.0f - 2.0f * (yy + zz)) * -1;
        tmpMatrix.m01 = (       2.0f * (xy - wz)) * -1;
        tmpMatrix.m02 =        2.0f * (xz + wy);
        tmpMatrix.m03 = 0;
    
        tmpMatrix.m10 =        2.0f * (xy + wz);
        tmpMatrix.m11 = 1.0f - 2.0f * (xx + zz);
        tmpMatrix.m12 = (       2.0f * (yz - wx)) * -1;
        tmpMatrix.m13 = 0;
    
        tmpMatrix.m20 =        2.0f * (xz - wy);
        tmpMatrix.m21 =        2.0f * (yz + wx);
        tmpMatrix.m22 = (1.0f - 2.0f * (xx + yy)) * -1;
        tmpMatrix.m23 = 0;
        return tmpMatrix;
    }

A part ça, c'est juste:

finalMatrix = boneMatrix * skinMatrix * invertedBindMatrix;

Notez que le boneMatrixdoit utiliser les matrices animées pour chaque os au lieu des matrices de liaison. Il devrait également obtenir toutes ses matrices parent comme ceci:

private Matrix4f getBoneMatrix() {
        if (parent == null) {
            return Matrix4f.mul(Matrix4f.mul(parentArmature.getBindMatrix(), parentArmature.getArmatureRootMatrix(), null), boneMatrix, null);
        } else {
            return Matrix4f.mul(parent.getBoneMatrix(), boneMatrix, null);
        }
    }

Et il fonctionne. Laissez un commentaire si vous avez besoin d'éclaircissements.

MichaelHouse
la source
J'ai écrit des informations plus générales sur le fonctionnement de ce blog sur mon blog: byte56.com/2012/12/the-blender-connection.html
MichaelHouse