How to calculate the difference between 2 quaternions?

Started by
4 comments, last by lucky6969b 8 years, 3 months ago

I need to convert some local basis with respect to the world space
back into the world space, say the world space is y-up,
and the local space basis is oriented arbitrarily in the world space.
I want the local space to align with the world space.
And let's say it is a skeleton system. For each bone, there is an offset
matrix which is relative to the world space.
So that I need to get the difference between this canonical world space
and this arbitrarily oriented local basis together, so that they can be compared,
and I want all local space to be y-up.
I want to get the difference between the quaternion of the offset matrix of the bone
and quaternion of the world basis.
How do I go about calculating this difference?

Advertisement
You want: q2 = q1 * delta_q
<=>
delta_q = inverse(q1)*q2
<=> (assumption you have only unit quaternions)
delta_q = conjugation(q1)*q2

That's it, create the conjuagation and multiply it with the other quaterion to get the difference. From the difference you can extract the angle and rotation vector if needed.

Hello,
Just one off-topic question.
In most of the samples found on the net about loading .x files,
I notice that the bone offset matrices are stored in the meshcontainer data structure.
If the mesh is rigged and it is a continuous mesh, some frames may or may not have a meshcontainer.
Then I don't have access to the offset matrix. I wonder why the offset matrix should be placed in
the frame data structure instead?

>Then I don't have access to the offset matrix. I wonder why the offset matrix should be placed in
>the frame data structure instead?

Skeletal animation works on bone basis, that is, the mesh must be in bone space. E.g. if you want to animate an eye with a eye bone, the eye center must be at position 0,0,0 in the space of the eye-bone. When you look at the mesh, the eyes are most likely far away from the center position, therefor the offset matrix moves the eye from its original position in the mesh (mesh/model space) to 0,0,0 (bone space).

Now you could use this to animate two different mesh with the same animation data, even if the eyes of each mesh are at different position due to each mesh having its own offset matrix for the eye. The offset matrix ensures, that the mesh parts are at the right position, so that the animation data can be used independently of the mesh.

Thought, in general different meshes will have different bone structures and animation data, but this is the basic idea why the offset matrix is part of the mesh instead of the animation data.

In here, as the offset matrix is not attached to the frame, and as it is a continuous mesh.

Is it still possible to find its offset matrix?


struct FRAME : public D3DXFRAME
{
    D3DXMATRIX matCombined;
};

struct MESHCONTAINER : public D3DXMESHCONTAINER
{
    ~MESHCONTAINER()
    {
    }
    LPDIRECT3DTEXTURE9*  ppTextures;       // array of textures, entries are NULL if no texture specified    

    // SkinMesh info             
    LPD3DXMESH                pOrigMesh;
    LPD3DXATTRIBUTERANGE    pAttributeTable;
    DWORD                    dwNumAttributeGroups;
    DWORD                    dwNumInfl;
    DWORD                    dwNumBones;
    LPD3DXBUFFER            pBoneCombinationBuf;
    D3DXMATRIX**            ppBoneMatrixPtrs;
    D3DXMATRIX*                pBoneOffsetMatrices;

    // for software skinning
    D3DXMATRIX*                pBoneMatrices;
    DWORD                    dwNumPaletteEntries;

    bool                    bUseSoftwareVP;
    DWORD                    dwAttributeSW;     // used to denote the split between SW and HW if necessary for non-indexed skinning
};
 
 
 
void RenderMesh(const CMesh& mesh, const Shot& shot, float time, float scale) {
    
    FRAME* frame = (FRAME*)mesh.GetFrameRoot();

    stdext::hash_map<FRAME, Joint*, FrameHasher> boneMap = mesh.m_BoneMap;

    D3DXMATRIX iden;
    // sets to identity first, so that it has a reference frame
    d3d::m_pDevice->SetTransform(D3DTS_WORLD, D3DXMatrixIdentity(&iden));
    

    // local or world?
    shot.skeleton->Evaluate(shot.motion, time, zero_vector, identity_quaternion,
        &Joint::world_position);
    

    //UpdateFrame(frame, &iden);

    for (auto& b : boneMap)
    {        
        if (b.second && b.second->parent)
        {
            // world rotation
            const Vector& parent_position = b.second->parent->world_position;
            const Vector& position = b.second->world_position;



            if (position != parent_position)
            {
                double d, x_rot, y_rot;

                const Vector& p1 = parent_position;
                const Vector& p2 = position;

                Vector v = p2 - p1;
                v.polar(d, x_rot, y_rot);

                // transform        
                D3DXMATRIX matFinal, matRot, RotY, RotX;                

                D3DXMatrixRotationY(&RotY, y_rot);
                D3DXMatrixRotationX(&RotX, x_rot);

                // it is world matrix for this bone, it will be copied once it is in the canconical space
                matRot = RotY * RotX;

                //////////////////////////////////////////////////////////
                // move the source skeleton into canconial space
                // hence normalize the bone                

                // q? = qinit RG?1 q RG

                            

                // local rotation;
                D3DXQUATERNION qInit;

                // global rotation inversed
                D3DXQUATERNION RGInv;

                // align to cananical global frame, say y-up
                D3DXQUATERNION q;

                // global rotation
                D3DXQUATERNION RG;

                FRAME* f = (FRAME*)D3DXFrameFind(frame, b.first.Name);


                D3DXQuaternionRotationMatrix(&qInit, &f->TransformationMatrix);

                D3DXQuaternionRotationMatrix(&RG, &f->matCombined);

                D3DXQuaternionInverse(&RGInv, &RG);

                // say the cananical global frame is y-up
                // we get the difference Quaternion(1,0,0,0) [No Rotation]
                // around Y axis

                D3DXQUATERNION cancanicalQ;

                D3DXVECTOR3 cancanicalV(0, 1, 0);

                D3DXQuaternionRotationAxis(&cancanicalQ, &cancanicalV, 0);

                // find the difference between the basis of the local frame and this quaternion            

                //////////////////////////////////////////////////                
                // move the bvh into the canconical space
                MESHCONTAINER *meshcontainer = (MESHCONTAINER*)f->pMeshContainer;

                const D3DXMATRIX* offsetMat = meshcontainer->pBoneOffsetMatrices;

                D3DXQUATERNION offsetQuat;

                D3DXQuaternionRotationMatrix(&offsetQuat, offsetMat);


                // q2 = q1 * delta_q
                // where q2 is canconical and q1 is the offset matrix and the delta_q is the adjustment

                // delta_q = conjugation(q1)*q2

                D3DXQUATERNION delta_q;

                D3DXQUATERNION conjQ;

                // if q2 = canconical quat and q1 = offset
                //q2 = q1 * delta_q
                D3DXQuaternionConjugate(&conjQ, &offsetQuat);

                delta_q = conjQ * cancanicalQ;


                q = qInit * RGInv * delta_q * RG;
                

                //D3DXMATRIX matRot;
                //D3DXMatrixIdentity(&matRot);

                //D3DXMatrixRotationQuaternion(&matRot, &finalQuat);

                // todo: normalize the bvh

 
                D3DXMATRIX matTrans;
                D3DXMATRIX matScale;
                // check
                D3DXMATRIX matRot2;
                D3DXMatrixIdentity(&matTrans);
                D3DXMatrixIdentity(&matScale);
                D3DXMatrixIdentity(&matRot2);
                const D3DXMATRIX& TransformationMatrix = f->TransformationMatrix;

                D3DXVECTOR3 pos, scale;
                D3DXQUATERNION quat;
                D3DXMatrixDecompose(&scale, &quat, &pos, &TransformationMatrix);


                D3DXMatrixTranslation(&matTrans, pos.x, pos.y, pos.z);
                D3DXMatrixScaling(&matScale, scale.x, scale.y, scale.z);
                D3DXMatrixRotationQuaternion(&matRot2, &quat);

                D3DXMATRIX finalMat = matScale * matRot * matTrans;

                // if this is the case, I have to scale the mesh
                // otherwise, scale the bvh
                f->TransformationMatrix = finalMat;
            }
        }
        

        
    }

    

    UpdateFrame(frame, &iden);

    // render all frames
    RenderFrame(frame);
}

Hello Ashaman73,

Do you know why the target mesh is still not retargeted?

Thanks

Jack

void RenderMesh(const CMesh& mesh, const Shot& shot, float time, float scale) {
    
    FRAME* frame = (FRAME*)mesh.GetFrameRoot();

    stdext::hash_map<FRAME, Joint*, FrameHasher> boneMap = mesh.m_BoneMap;

    D3DXMATRIX iden;
    // sets to identity first, so that it has a reference frame
    d3d::m_pDevice->SetTransform(D3DTS_WORLD, D3DXMatrixIdentity(&iden));
    

    // local or world?
    shot.skeleton->Evaluate(shot.motion, time, zero_vector, identity_quaternion,
        &Joint::world_position);
    

    FRAME* f = (FRAME*)D3DXFrameFind(frame, "Body01");
    MESHCONTAINER *meshcontainer = (MESHCONTAINER*)f->pMeshContainer;
    const D3DXMATRIX* offsetMat = meshcontainer->pBoneOffsetMatrices;
    D3DXQUATERNION offsetQuat;
    D3DXQuaternionRotationMatrix(&offsetQuat, offsetMat);

    D3DXQUATERNION cancanicalQ;
    D3DXMATRIX cancanialM;

    D3DXVECTOR3 cancanicalV(0, 1, 0);

    D3DXQuaternionRotationAxis(&cancanicalQ, &cancanicalV, 0);

    D3DXMatrixRotationQuaternion(&cancanialM, &cancanicalQ);



    // Should I just change the offset matrix to make it y-up by applying the difference
    //meshcontainer->pBoneOffsetMatrices = &cancanialM;


    //////////////////////////////////////////////////////////
    // move the source skeleton into canconial space
    // hence normalize the bone            

    // Get The Scale of the character and get the scale of the bvh
    float heightOfCharacter = mesh.GetHeight();
    float heightOfBVH = 137.0f;

    if (heightOfCharacter <= 0.0f) {
        return;
    }

    float scaleOfCharacter = heightOfBVH / heightOfCharacter;

    


    for (auto& b : boneMap)
    {        
        if (b.second && b.second->parent)
        {
            // world rotation
            const Vector& parent_position = b.second->parent->world_position;
            const Vector& position = b.second->world_position;



            if (position != parent_position)
            {
                double d, x_rot, y_rot;

                const Vector& p1 = parent_position;
                const Vector& p2 = position;

                Vector v = p2 - p1;
                v.polar(d, x_rot, y_rot);

                // transform        
                D3DXMATRIX matRot, RotY, RotX;                

                D3DXMatrixRotationY(&RotY, y_rot);
                D3DXMatrixRotationX(&RotX, x_rot);

                // bone's world matrix according to the bvh
                matRot = RotY * RotX;

                // find the t-pose or a-pose of the character
                FRAME* fBone = (FRAME*)D3DXFrameFind(frame, b.first.Name);                

                // say the cananical global frame is y-up
                // we get the difference Quaternion(1,0,0,0) [No Rotation]
                // around Y axis                
                
                // q? = qinit RG?1 q RG                            

                // local rotation;
                D3DXQUATERNION qInit;

                // global rotation inversed
                D3DXQUATERNION RGInv;

                // align to cananical global frame, say y-up
                //D3DXQUATERNION q;

                // global rotation
                D3DXQUATERNION RG;
                
                D3DXMATRIX matRotInv;

                D3DXMatrixInverse(&matRotInv, NULL, &matRot);

                // mesh's local transform
                D3DXQuaternionRotationMatrix(&qInit, &matRotInv);

                // mesh's global transform
                D3DXQuaternionRotationMatrix(&RG, &matRot);


                // mesh's global transform inversed
                D3DXQuaternionInverse(&RGInv, &RG);

                

                D3DXQUATERNION conjQ;

                // mesh's offset matrix (only one for time being) conjugate
                D3DXQuaternionConjugate(&conjQ, &offsetQuat);

                // conjugate of offset Quaternion * canconical Q == the factor to multiply for differences
                D3DXQUATERNION delta_q = conjQ * cancanicalQ;

                // the difference between the orientation of the local bone and the delta_q
                D3DXQUATERNION q = qInit * delta_q;

                // now the orientation of the local bone in y-up space (
                // the offset matrix is in canconcial identity state, before affected by bvh
                D3DXQUATERNION finalQ = qInit * RGInv * q * RG;            
                

                // the matrix
                D3DXMatrixRotationQuaternion(&matRot, &finalQ);        
                
                // assuming the bvh is y-up
 
                D3DXMATRIX matTrans;
                D3DXMATRIX matScale;                
                
                D3DXMatrixIdentity(&matTrans);
                D3DXMatrixIdentity(&matScale);

                
 
                const D3DXMATRIX& TransformationMatrix = fBone->TransformationMatrix;

                D3DXVECTOR3 pos, vscale;
                D3DXQUATERNION quat;
                D3DXMatrixDecompose(&vscale, &quat, &pos, &TransformationMatrix);

                D3DXVECTOR3 posScaled = pos;

                



                D3DXMatrixTranslation(&matTrans, posScaled.x, posScaled.y, posScaled.z);                

                // okay
                D3DXMatrixScaling(&matScale, vscale.x, vscale.y, vscale.z);

                // offset now is identity, the bone relies just on this matrix
                D3DXMATRIX finalMat = matRot *  matTrans;
                
                if (strcmp(b.first.Name,"Bip001_L_UpperArm")==0 ||
                    strcmp(b.first.Name, "Bip001_R_UpperArm") == 0 ||
                    strcmp(b.first.Name, "Bip001_L_Forearm") == 0 ||
                    strcmp(b.first.Name, "Bip001_R_Forearm") == 0) { // ||
                    /*strcmp(b.first.Name, "Bip001_Spine")== 0 ||
                    strcmp(b.first.Name, "Bip001_Spine1") == 0 ||
                    strcmp(b.first.Name, "Bip001_Spine2") == 0 ||
                    strcmp(b.first.Name, "Bip001_Spine3") == 0 ||
                    strcmp(b.first.Name, "Bip001_L_Thigh") == 0) {*/
                    // now the skeleton is aligned to the bvh, copy the rotation matrix over
                    fBone->TransformationMatrix = finalMat;
                }
 
            }

        }  


        
    }

    UpdateFrame(frame, &iden);

    // render all frames
    RenderFrame(frame);
}

This topic is closed to new replies.

Advertisement