XML Exporter: 애니메이션 보간

애니메이션  프레임이 많을수록 저장 용량과 메모리 용량이 늘어난다.
애니메이션 프레임 사이의 애니메이션을 선형 보간 하여 애니메이션이 부드럽게 보이도록 한다.

위치는 선형 보간하고 회전은 행렬을 쿼트니언으로 변환 --> 쿼트니언 선형 보간 --> 쿼트니언을 행렬로 변화한다.

< 선형 보간 >
행렬에서 _41, _42, _43의 값은 위치를 나타낸다.
선형 보간 공식: v' = (1 - t) * v1 + t * v2

< 회전 선형 보간 >
D3DXQuaternionSlerp를 이용한다.D3DXQuaternionSlerp  : 구면선형 보간을 사용해, 2 개의 쿼터니온간을 보간 한다.

D3DXQuaternionRotationMatrix : 회전 행렬로부터 쿼터니온을 생성 한다.
행렬을 보간하는 코드는 아래와 같다.

void MatrixLerp(D3DXMATRIX* pOut, const D3DXMATRIX* m1, const D3DXMATRIX* m2, float t)

{

#if  0
    //애니메이션 간격이 좁을 때

    *pOut = (1 - t) * (*m1) + t * (*m2);

#else

    //애니메이션 간격이 넓을 때

    D3DXQUATERNION    q, q1, q2;

    D3DXVECTOR3    v, v1, v2;

 

    D3DXQuaternionRotationMatrix(&q1, m1);

    D3DXQuaternionRotationMatrix(&q2, m2);

 

    v1 = D3DXVECTOR3(m1->_41, m1->_42, m1->_43);

    v2 = D3DXVECTOR3(m2->_41, m2->_42, m2->_43);

 

    // 위치에 대한 선형 보간

    v = (1 - t) * v1 + t * v2;

 

    // 회전의 선형 보간

    D3DXQuaternionSlerp(&q, &q1, &q2, t);

 

    // 회전을 행렬로 전환

    D3DXMatrixRotationQuaternion(pOut, &q);

 

    // 위치 적용

    pOut->_41 = v.x;

    pOut->_42 = v.y;

    pOut->_43 = v.z;

#endif

}

애니메이션 간격 이 좁은 경우는 *pOut = (1 - t) * (*m1) + t * (*m2)  행렬 자체의 선형 보간으로도 가능하다.

이유:
회전하는 두사원수의 각도가 작으면 sinθ의 값은 θ의 값과 비슷해져서 회전 사원수의 보간을 간단하게 하면 다음과 같다.
 
Result = 1/sinθ( sinθ( 1 - t )qi + sin(θt)qf

        = 1/θ ( θ( 1 - t )qi + (θt)qf

        = (1-t)qi + tqf

< 보간 값 셋팅 >

프레임과 프레임 사이의 가중치를 구하기 위해 float값의 프레임에서 정수형의 프레임을 구한다.

float GameTime::GetWeightTime()

{

    return var.fWeight;

}

 

void GameTime::Update()

{

    var.elapsedTick = GetTickCount() - var.startTick;

    var.time = (float)var.elapsedTick / 1000;   

 

    int stride = 1000 / var.fps;

    var.nFrame = var.elapsedTick / stride;

    var.fWeight = (float)var.elapsedTick / (float)stride - var.nFrame;

    var.nFrame %= var.endFrame;

}

행렬 팔레트에 넣기 전에 행렬을 보간한다. EShaderBone::OnFrameMove()에도 보간하는 코드를 추가한다.

void EShaderBoneMesh::OnFrameMove()

{

    if( this->m_nAniSize > 0 )

    {

        int nFrame = GameTime::GetFrame();   

        float fWt = GameTime::GetWeightTime();

        D3DXMatrixIdentity( &m_matWorld );

        MatrixLerp(&m_matWorld, &m_aniArray[nFrame], &m_aniArray[nFrame + 1 ], fWt );

    }

    else

        D3DXMatrixIdentity(&m_matWorld);

 

    if( m_id >= 0 )

        m_matBlend[m_id] = m_matWorld;

}

프로젝트: viewer_interpolation.zip