¿Cómo aplico la animación esquelética desde un archivo .x (Direct X)?

8

Usando el formato .x para exportar un modelo desde Blender, puedo cargar una malla, armadura y animación. No tengo problemas para generar la malla y ver modelos en el juego. Además, tengo animaciones y la armadura cargada correctamente en las estructuras de datos apropiadas.

Mi problema es aplicar correctamente la animación a los modelos. Tengo el marco para aplicar los modelos y el código para seleccionar animaciones y recorrer cuadros.

Por lo que entiendo, la AnimationKeys dentro de las AnimationSetfuentes proporciona las transformaciones para transformar la pose de enlace a la pose en el marco animado. Como pequeño ejemplo:

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

Entonces, para aplicar el marco 2, tomaría la posición, la rotación y la escala del marco 2, crearía una matriz de transformación (llamada Transform_A) a partir de ellos y aplicaría esa matriz a los vértices controlados por Armature_001_Bonesus pesos. Así que me metería TransformAen mi sombreador y transformaría el vértice. Algo como:

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

Donde bfs_BoneIndicesy bfs_BoneWeightsson valores específicos del vértice actual.

Al cargar en los vértices de malla, los transformo por el rootTransformy el meshTransform. Esto garantiza que estén orientados y escalados correctamente para ver la pose de enlace.

El problema es que cuando creo esa matriz de transformación (usando la posición, rotación y escala de la animación), no transforma adecuadamente el vértice. Es probable que haya más que solo usar los datos de animación. También intenté aplicar las jerarquías de transformación ósea, aún sin dados. Básicamente termino con algunos modelos retorcidos. También se debe tener en cuenta que estoy trabajando en openGL, por lo que se debe considerar cualquier transposición de matriz que deba aplicarse.

¿Qué datos necesito y cómo los combino para aplicar animaciones .x a los modelos?

He hecho algunas maquetas de cómo se ve esto, en caso de que sea útil.

Primero, solo quería probar la traducción, esta es una cabeza flotante, cómo se ve en Blender:

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

Y cómo se ve en el juego (no importa los colores):

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

Luego, solo para la rotación, la animación era la cabeza girando 360 grados alrededor del eje vertical. Así es como se ve en el juego:

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

Tenga en cuenta que no debe haber inclinación, solo rotación como un tiovivo.

Actualizar

Tengo la parte de traducción de la animación funcionando. Pero se siente un poco hacky y no veo cómo aplicarlo a la rotación.

La traducción funciona siguiendo estos pasos:

  1. Tome la posición del cuadro de animación e intercambie los valores yyz
  2. Traslade la matriz de transformación por la posición alterada
  3. Transponer la matriz de transformación
  4. Aplicar la matriz de transformación a los vértices.

Así es como puede funcionar, pero ¿cómo se supone que funciona en general para la posición, la escala y la rotación?

MichaelHouse
fuente
2
¿Transformó el vértice con la matriz de postura de unión inversa antes de aplicar el desollado?
r2d2rigo
Tengo, tiene un efecto, pero no es el deseado. Parece afectar la representación visual del modelo (invierte todas las normales, parece), pero no tiene ningún efecto en el movimiento / rotación / escala.
MichaelHouse
¿Podría publicar una captura de pantalla que ilustre el problema?
r2d2rigo
@ r2d2rigo He actualizado con gifs animados.
MichaelHouse
1
Aquí estoy haciendo una lluvia de ideas, pero si el orden vectorial (xyz, zxy) difiere entre .xy opengl, obtendrás algunos efectos divertidos. El hecho de que la transposición ayude significa que utiliza el vector de columna (o fila) para la posición, en lugar de lo contrario. en.wikipedia.org/wiki/… tiene una descripción de rotaciones para 2D. Sin embargo, no sé cómo modificar un cuaternión. O la diferencia entre los formatos .xy openGL
Daniel Carlsson,

Respuestas:

3

También se debe tener en cuenta que estoy trabajando en openGL, por lo que se debe considerar cualquier transposición de matriz que deba aplicarse.

Tal vez esa sea la clave, ¿ha intentado negar algunas coordenadas Z para convertir las coordenadas de DirectX para zurdos en OpenGL para diestros?

Puede negar todas las Z antes de aplicar sus transformaciones, y volver a negar cuando haya terminado, ver si la salida mejora.

EDITAR

Intercambiar las coordenadas Y y Z es otra forma de cambiar las manos. Para que tenga que adaptar sus transformaciones, consulte este documento para obtener más detalles .

El hecho de que tenga que transponer su matriz también significa que va de mayor a mayor de columna o al revés. Esto debería ser menos complicado de manejar, probablemente solo tendrá que encontrar el "lugar" adecuado para hacerlo en su tubería. Solo estoy adivinando, pero hacerlo en cuaterniones podría simplemente significar cambiarlas a matrices, transponer y volver a cuaterniones, si su lib matemática le ofrece esta opción.

Laurent Couvidou
fuente
Es como decirme lo que ya sé aquí ...
MichaelHouse
1
Bueno, no mencionaste la imparcialidad en tu pregunta, y creo que es al menos parte de tu problema. No puedo suponer que sabes o no que DirectX y OpenGL siguen diferentes convenciones en este punto (esto es en realidad un poco más complejo ). Tenga en cuenta que esto podría ayudar a otras personas que lean esta pregunta a verla mencionada, lo cual es una especie de punto de todo este sitio web.
Laurent Couvidou
Muy bien, tienes razón. Solo insinué el hecho de que sabía sobre las conversiones que debían ocurrir cuando mencioné el uso de OpenGL y las transposiciones que debían ocurrir.
MichaelHouse
2

De acuerdo, dediqué un poco más de tiempo a esto y lo hice funcionar para varios huesos, y considerablemente menos como un pirateo (aunque todavía un poco). El sistema que tenía en su lugar originalmente era casi correcto. Sin embargo, los principales problemas fueron:

  1. Blender guarda Quaternions en formato W, X, Y, Z. I estaba cargando ellos X, Y, Z, W .
  2. Mi código para convertir un Quaternion en una matriz era incorrecto. En realidad, todavía no estoy seguro de por qué esto es incorrecto, pero sé que el siguiente código funciona, como parte de mi clase 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;
    }

Aparte de eso, es solo:

finalMatrix = boneMatrix * skinMatrix * invertedBindMatrix;

Tenga en cuenta que boneMatrixdebería usar las matrices animadas para cada hueso en lugar de las matrices de unión. También debería obtener todas sus matrices principales de la siguiente manera:

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

Y funciona. Deja un comentario si necesitas una aclaración.

MichaelHouse
fuente
Escribí información más general sobre cómo hacer que esto funcione en mi blog: byte56.com/2012/12/the-blender-connection.html
MichaelHouse