Come posso applicare l'animazione scheletrica da un file .x (Direct X)?


8

Usando il formato .x per esportare un modello da Blender, posso caricare una mesh, un'armatura e un'animazione. Non ho problemi a generare la mesh e a visualizzare i modelli nel gioco. Inoltre, ho animazioni e armature correttamente caricate in strutture dati appropriate.

Il mio problema è applicare correttamente l'animazione ai modelli. Ho il framework per applicare i modelli e il codice per selezionare le animazioni e passare da un frame all'altro.

Da quello che ho capito, la AnimationKeys all'interno AnimationSetfornisce le trasformazioni per trasformare la posa del legame in posa nella cornice animata. Come piccolo esempio:

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;;,
    ...
  }
}

Quindi, per applicare il fotogramma 2, prenderei la posizione, la rotazione e la scala dal fotogramma 2, creerei una matrice di trasformazione (chiamala Transform_A) da loro e applicherei quella matrice dai vertici controllati dai Armature_001_Boneloro pesi. Quindi aggiungerei il TransformAmio shader e trasformerei il vertice. Qualcosa di simile a:

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

Dove bfs_BoneIndicese bfs_BoneWeightssono valori specifici dell'attuale vertice.

Quando carico i vertici della mesh, li trasformo con il rootTransforme il meshTransform. Ciò garantisce che siano orientati e ridimensionati correttamente per visualizzare la posa del legame.

Il problema è quando creo quella matrice di trasformazione (usando la posizione, la rotazione e la scala dall'animazione), non trasforma correttamente il vertice. Probabilmente c'è di più oltre al semplice utilizzo dei dati di animazione. Ho anche provato ad applicare le gerarchie di trasformazione ossea, ancora nessun dado. Fondamentalmente finisco con alcuni modelli contorti. Va anche notato che sto lavorando in openGL, quindi è necessario considerare qualsiasi trasposizione di matrici che potrebbe essere necessario applicare.

Di quali dati ho bisogno e come posso combinarli per applicare animazioni .x ai modelli?

Ho fatto alcuni modelli di come appare, nel caso sia utile.

Per prima cosa volevo solo testare la traduzione, questa è una testa bobbing, come appare in Blender:

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

E come appare nel gioco (non importa i colori):

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

Quindi, solo per la rotazione, l'animazione era la testa che ruotava di 360 gradi attorno all'asse verticale. Ecco come appare nel gioco:

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

Nota, non dovrebbe esserci inclinazione, solo rotazione come una giostra.

Aggiornare

Ho la parte di traduzione dell'animazione funzionante. Ma sembra un po 'confuso e non vedo come applicarlo alla rotazione.

La traduzione funziona seguendo questi passaggi:

  1. Prendi la posizione dal riquadro dell'animazione e scambia i valori yez
  2. Traduci la matrice di trasformazione in base alla posizione modificata
  3. Trasponi la matrice di trasformazione
  4. Applica la matrice di trasformazione ai vertici

Quindi è così che può funzionare, ma come dovrebbe funzionare generalmente per posizione, scala e rotazione?


2
Hai trasformato il vertice con la matrice di posa del legame inverso prima di applicare lo skinning?
r2d2rigo,

Ho, ha un effetto, ma non è desiderato. Sembra influenzare la rappresentazione visiva del modello (invertendo tutte le normali, sembra), ma non ha alcun effetto sul movimento / rotazione / scala.
MichaelHouse

Potresti pubblicare uno screenshot che illustra il problema?
r2d2rigo,

@ r2d2rigo ho aggiornato con gif animate.
MichaelHouse

1
Qui sto principalmente facendo brainstorming, ma se l'ordine dei vettori (xyz, zxy) differisce tra .x e opengl, otterresti degli effetti divertenti. Il fatto che il recepimento sia utile significa che utilizza il vettore di colonna (o riga) per la posizione, anziché viceversa. en.wikipedia.org/wiki/… ha una descrizione delle rotazioni per il 2D. Tuttavia, non so come modificare un quaternione. O la differenza tra i formati .xe openGL
Daniel Carlsson,

Risposte:


3

Va anche notato che sto lavorando in openGL, quindi è necessario considerare qualsiasi trasposizione di matrici che potrebbe essere necessario applicare.

Forse questa è la chiave, hai provato a negare alcune coordinate Z in giro per convertire da coordinate per mancini DirectX in destrimane per OpenGL?

Potresti annullare tutte le Z prima di applicare le tue trasformazioni e, al termine, ri-negare, vedi se l'output migliora.

MODIFICARE

Scambiare le coordinate Y e Z è un altro modo per cambiare la mano. Quindi devi adattare le tue trasformazioni, controlla questo documento per i dettagli .

Il fatto che devi trasporre la tua matrice significa anche che passi dalla riga maggiore alla colonna maggiore o viceversa. Dovrebbe essere meno complicato da gestire, probabilmente dovrai solo trovare il "posto" giusto per farlo nella tua pipeline. Sto solo indovinando, ma farlo sui quaternioni potrebbe significare semplicemente cambiarli in matrici, trasporre e tornare ai quaternioni, se la tua libreria Math offre questa opzione.


Tipo di dirmi quello che già so qui ...
MichaelHouse

1
Bene, non hai menzionato la mano nella tua domanda, e credo che sia almeno una parte del tuo problema. Non posso presumere che tu lo sappia o non sappia che DirectX e OpenGL stanno seguendo convenzioni diverse su questo punto (in realtà è un po 'più complesso ). Nota che questo potrebbe aiutare altre persone a leggere questa domanda per vederla menzionata, il che è una specie del punto dell'intero sito web.
Laurent Couvidou,

Abbastanza giusto, hai ragione. Ho solo accennato al fatto che ero a conoscenza delle conversioni che dovevano aver luogo quando ho menzionato l'utilizzo di OpenGL e le trasposizioni che dovevano avvenire.
MichaelHouse

2

OK, ho dedicato un po 'più di tempo a questo e l'ho fatto funzionare per più ossa, e notevolmente meno simile a un hack (anche se ancora un po'). Il sistema che avevo installato inizialmente era quasi corretto. Tuttavia, i problemi principali erano:

  1. Blender salva i Quaternioni nei formati W, X, Y, Z. Stavo caricando loro X, Y, Z, W .
  2. Il mio codice per convertire un Quaternion in una matrice non era corretto. In realtà non sono ancora sicuro del perché questo non sia corretto, ma so che il seguente codice funziona, come parte della mia 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 parte questo, è solo:

finalMatrix = boneMatrix * skinMatrix * invertedBindMatrix;

Si noti che boneMatrixdovrebbero utilizzare le matrici animate per ciascun osso anziché le matrici di legame. Dovrebbe anche ottenere tutte le matrici principali in questo modo:

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);
        }
    }

E funziona Lascia un commento se hai bisogno di chiarimenti.


Ho scritto alcune informazioni più generali su come farlo funzionare nel mio blog: byte56.com/2012/12/the-blender-connection.html
MichaelHouse
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.