Скелетная анимация: интерполяция между матрицами преобразования (коллада) - PullRequest
1 голос
/ 25 сентября 2019

Добрый день.Я хочу реализовать скелетную анимацию.Я создал простой пример в Blender и экспортировал его через collada.

anim будет

Поскольку это все для меня ново, поэтому я использовал collada1.4.1 спецификация и воссоздание сетки по ключевым кадрам.

ключевой кадр_0

ключевой кадр_1

ключевой кадр_2

Затем я попытался интерполировать между матрицами преобразования, чтобы получить полную анимацию.

    Vector32BITF* vertices = (Vector32BITF*)calloc(CubeDAE::verticesCount, sizeof(Vector32BITF));
    SceUShort16* indices = (SceUShort16*)calloc(CubeDAE::indicesCount, sizeof(SceUShort16));
    for (unsigned int i = 0; i < CubeDAE::indicesCount; i++) { indices[i] = CubeDAE::indices[i][0]; }

    float                   timer = 2.1f;

    ScePspFMatrix4          bones_transform[3];
    int                     keyframeA_i = 0;
    int                     keyframeB_i = 0;
    float                   lerpFact = 0.0f;

    while(!done){
        fps->PreUpdate();

        keyframeA_i = ((int)timer) % CubeDAE::keyframesCount;
        keyframeB_i = (((int)timer) + 1) % CubeDAE::keyframesCount;
        lerpFact = pspFpuFrac(timer);
        // keyframeA_i = 0;
        // keyframeB_i = 1;
        // lerpFact = 0.8;

        for (unsigned int bone_i = 0; bone_i < CubeDAE::bonesCount; bone_i++) {

            ScePspFMatrix4* local_matrixA = &CubeDAE::bones[bone_i][keyframeA_i];
            ScePspFMatrix4* local_matrixB = &CubeDAE::bones[bone_i][keyframeB_i];

            ScePspFVector3 positionA;
            ScePspFVector3 positionB;
            Mathf::Matrix::GetPosition(&positionA, local_matrixA);
            Mathf::Matrix::GetPosition(&positionB, local_matrixB);

            ScePspFMatrix3 rotationA;
            ScePspFMatrix3 rotationB;
            Mathf::Matrix::GetRotation(&rotationA, local_matrixA);
            Mathf::Matrix::GetRotation(&rotationB, local_matrixB);

            ScePspFVector4 q_rotationA;
            ScePspFVector4 q_rotationB;
            Mathf::Matrix::ConvertToQuaternion(&q_rotationA, &rotationA);
            Mathf::Matrix::ConvertToQuaternion(&q_rotationB, &rotationB);

            ScePspFVector3 translation;
            ScePspFVector4 quaternion; 
            Mathf::Vector::Lerp(&translation, &positionA, &positionB, lerpFact);
            Mathf::Quaternion::Slerp(&quaternion, &q_rotationA, &q_rotationB, lerpFact);

            ScePspFMatrix4 offsetMatrix;
            Mathf::Matrix::fromRotationTranslation(&offsetMatrix, &quaternion, &translation);

            if (bone_i == 0) {
                std::memcpy(&bones_transform[bone_i], &offsetMatrix, sizeof(ScePspFMatrix4));
            } else if (bone_i == 1) {
                Mathf::Matrix::Multiply(&bones_transform[bone_i], &offsetMatrix, &bones_transform[bone_i - 1]);
            } else if (bone_i == 2) {
                Mathf::Matrix::Multiply(&bones_transform[bone_i], &offsetMatrix, &bones_transform[bone_i - 2]);
            }
        }

        unsigned int v_i = 0;
        for (unsigned int vertices_i = 0; vertices_i < CubeDAE::verticesCount; vertices_i++) {

            vertices[vertices_i].color = (0xff<<24)|((int)(pspFpuAbs(pspFpuCos(vertices_i)) * 255.0f) << 16)|((int)(pspFpuAbs(pspFpuSin(vertices_i)) * 255.0f) << 8)|((int)(pspFpuAbs(vertices_i) * 255.0f));

            vertices[vertices_i].x = 0;
            vertices[vertices_i].y = 0;
            vertices[vertices_i].z = 0;

            for (unsigned int vcount_i = 0; vcount_i < CubeDAE::vcount[vertices_i]; vcount_i++, v_i += 2) {

                /*
                SUM += ((v * BSM) * IBMi * JMi) *JM

                • n: number of joints that influence vertex v
                • BSM: bind shape matrix
                • IBMi: inverse bind matrix of joint i
                • JMi: joint matrix of joint i
                • JW: joint weight/influence of joint i on vertex v
                */

                // (v * BSM)
                //ScePspFVector3 out = {0,0,0};
                //Mathf::Vector::Transform(&out, &CubeDAE::shapeMatrix, &CubeDAE::vertices[vertices_i]);

                // IBMi * JMi
                ScePspFMatrix4 skinning_matrix = {{1,0,0,0}, {0,1,0,0}, {0,0,1,0}, {0,0,0,1}};
                Mathf::Matrix::Multiply(&skinning_matrix, &CubeDAE::poses[CubeDAE::v[v_i]], &bones_transform[CubeDAE::v[v_i]]);

                // ((v * BSM) * IBMi * JMi)
                ScePspFVector3 skin = {0,0,0};
                Mathf::Vector::Transform(&skin, &skinning_matrix, &CubeDAE::vertices[vertices_i]);

                // ((v * BSM) * IBMi * JMi) *JM
                Mathf::Vector::Scale(&skin, CubeDAE::weights[CubeDAE::v[v_i + 1]]);

                vertices[vertices_i].x += skin.x;
                vertices[vertices_i].y += skin.y;
                vertices[vertices_i].z += skin.z;
            }
        }
    // TODO: render here
    }

Но я где-то допустил ошибку, поэтому получил неправильную анимацию.

anim got

Где я был не прав?

PS: приложение написано под платформу PSP, но смысл ничем не отличается от того, что делается везде.Полный простой пример вы можете найти здесь .

1 Ответ

1 голос
/ 27 сентября 2019

После долгого поиска проблемы я наконец нашел ее.

Основная ошибка была в функции Mathf :: Matrix :: fromRotationTranslation ()

было

        void fromRotationTranslation(ScePspFMatrix4* result, const ScePspFVector4* quaternion, const ScePspFVector3* translation)
        {
            // Quaternion math
            float x = quaternion->x, y = quaternion->y, z = quaternion->z, w = quaternion->w,
                x2 = x + x,
                y2 = y + y,
                z2 = z + z,

                xx = x * x2,
                xy = x * y2,
                xz = x * z2,
                yy = y * y2,
                yz = y * z2,
                zz = z * z2,
                wx = w * x2,
                wy = w * y2,
                wz = w * z2;

            result->x.x = 1 - (yy + zz);
            result->x.y = xy + wz;
            result->x.z = xz - wy;
            result->x.w = 0;
            result->y.x = xy - wz;
            result->y.y = 1 - (xx + zz);
            result->y.z = yz + wx;
            result->y.w = 0;
            result->z.x = xz + wy;
            result->z.y = yz - wx;
            result->z.z = 1 - (xx + yy);
            result->z.w = 0;
            result->w.x = translation->x;
            result->w.y = translation->y;
            result->w.z = translation->z;
            result->w.w = 1;
        }

у меня сейчас

        void fromRotationTranslation(ScePspFMatrix4* result, const ScePspFVector4* quaternion, const ScePspFVector3* translation)
        {
            // Quaternion math
            float x = quaternion->x, y = quaternion->y, z = quaternion->z, w = quaternion->w,
                xx = x * x,
                xy = x * y,
                xz = x * z,
                xw = x * w,

                yy = y * y,
                yz = y * z,
                yw = y * w,

                zz = z * z,
                zw = z * w;

            result->x.x  = 1 - 2 * ( yy + zz );
            result->x.y  =     2 * ( xy - zw );
            result->x.z  =     2 * ( xz + yw );

            result->y.x  =     2 * ( xy + zw );
            result->y.y  = 1 - 2 * ( xx + zz );
            result->y.z  =     2 * ( yz - xw );

            result->z.x  =     2 * ( xz - yw );
            result->z.y  =     2 * ( yz + xw );
            result->z.z  = 1 - 2 * ( xx + yy );

            result->x.w  = translation->x;
            result->y.w  = translation->y;
            result->z.w  = translation->z;

            result->w.x = result->w.y = result->w.z = 0;
            result->w.w = 1;
        }

anim done

PS: очень помог этот FAQ

...