I
I
inf1k2019-08-01 11:08:26
3D
inf1k, 2019-08-01 11:08:26

Import SMD animation, how to get the right quaternion?

I set up import / export of .smd files in a 3D editor. I managed to import the reference file and get the correct coordinates of the bones in the global world. Code snippet:

fscanf(pFile, "%i %f %f %f %f %f %f",
    &iBoneIndex, &vecBonePos.x, &vecBonePos.z, &vecBonePos.y,
    &vAngles.x, &vAngles.z, &vAngles.y);
        
  vecBonePos.z = -vecBonePos.z;
  vAngles.z = -vAngles.z;

  vAngles = vec3(DEGREES(vAngles.x), DEGREES(vAngles.y), DEGREES(vAngles.z));
  vec3 vBonePos = vec3(vecBonePos.x, vecBonePos.y, vecBonePos.z);
  matrix mX, mY, mZ;
  mX.setAngles(vec3(vAngles.x, 0, 0));
  mY.setAngles(vec3(0, vAngles.y, 0));
  mZ.setAngles(vec3(0, 0, vAngles.z));
  matrix mRot; // local matrix
  mRot = mY * mZ * mX; //YZX rotation order
  mRot.setPos(vBonePos);
  aLocalMatrices.add(mRot);
        
  if (GetBoneAt(i)->m_iParent == -1)
    aGlobalMatrices[i] = mRot;
  else
    aGlobalMatrices[i] = aGlobalMatrices[GetBoneAt(i)->m_iParent] * mRot;
  //set position
  GetBoneAt(i)->m_vPos = Vector(aGlobalMatrices[i].getPos().x, aGlobalMatrices[i].getPos().y, aGlobalMatrices[i].getPos().z);

The .smd gives the local position of the bone relative to the parent bone, as well as the Euler angles to create a local rotation matrix.
y-axis and z-axis are swapped, so this is the order of multiplication of rotation matrices.
My animation goes like this:
matrix m = getBoneMatrix(aBones[i], g_pAnimationEditor->m_flAnimPos );  // тут происходит интерполяция
    vec3 vRootOffset = getRootOffset(g_pAnimationEditor->m_flAnimPos); // root bone offset 
    if (aBones[i]->m_iParent != -1)
    {
      vec3 vPos = g_pAnimationEditor->m_aRefPoses[i]; //bone pos in global space
      vec3 vParentPos = vec3(aBones[aBones[i]->m_iParent]->m_vPos.x, aBones[aBones[i]->m_iParent]->m_vPos.y, aBones[aBones[i]->m_iParent]->m_vPos.z); //parent bone pos in global space
      vec3 vLocalPos = g_pAnimationEditor->m_aRefPoses[i] - g_pAnimationEditor->m_aRefParentPoses[i]; // local bone pos in parent space
      matrix mParent = m_aCurrentState[aBones[i]->m_iParent]; // parent rotation matrix
      mParent.setPos(vec3()); //remove pos
      m = mParent * m;
      vPos = m * vPos;
      vec3 vNewBonePos = vParentPos + mParent * vLocalPos; // new  bone location 
            aBones[i]->m_vPos = Vector(vNewBonePos.x, vNewBonePos.y, vNewBonePos.z); //adding root offset here because it resends data to gpu

      m.setPos(vNewBonePos - vPos); //offset
    }
    m.setPos(m.getPos() + vRootOffset); //add root offset

And when rendering, the final position of the bone is calculated as follows:
m = m_aCurrentState[i];
    glVertex3fv(m * m_aRefPoses[i]); // RefPos -> initial bone pos in global space

The problem is that I can't get the right quaternion from the Euler angles. All I need for my animation is a reference bone pos and a quaternion to rotate to local space.
PS
Editing a frame is done as follows:
vec3 vFrontAxis = (vEnd - vStart).getNormalized();
  matrix mBoneMatrix = m_aCurrentState[pSelectedBone->m_iParent];
  mBoneMatrix.setPos(vec3());
  mBoneMatrix.invert();
  vFrontAxis = mBoneMatrix * vFrontAxis;
  int iCurFrame = m_iCurrentFrame;
  m_aFrames[iCurFrame].Get(pSelectedBone)->m_quat = m_aFrames[iCurFrame].Get(pSelectedBone)->m_quat * quat().fromAxisAngle(vFrontAxis, flDeltaX); // flDeltaX = mouse.x - lastmouse.x

Answer the question

In order to leave comments, you need to log in

1 answer(s)
A
Alexander Skusnov, 2019-08-01
@AlexSku

XMQuaternionRotationRollPitchYaw function

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question