XML Exporter: Transform

앞장에서는 각 노드마다 transform이 없었기 때문에, 메쉬들이 화면 중앙에 모여서 렌더링 되었을 것이다.
그리고 맥스 좌표계는 Z-Up의 오른속 좌표계이고,  Viewer는 Y-Up인 왼손 좌표계이기 때문에 주전자나 메쉬들이 이상한 방향으로 나왔을 것이다.

제대로 출력된 그림부터 보자.

맥스와 똑 같은 모양으로 나올 것이다.

설명은 다음의 순서로 진행한다.

1. Export 클래스의 바뀐 메서드 이름   ( 맥스 플러그인 )
2. Transform 추가   ( 맥스 플러그인 )
3. 맥스좌표계에서 다이렉트X 좌표계로 변환   ( 맥스 플러그인 )
4. XmlMesh에 로컬 행렬 추가 (뷰어)
5. ENode에서 월드 행렬 계산 (뷰어)

1. Export 클래스의 바뀐 메서드 이름

Create~~~ 이름이 흔하고 다른 이름과 구분이 안되서 Export시 불리는 함수라는 것을 쉽게 알아 볼수 있도록 Write~~~ 접두어로 바꾸었다.

CreateXMLObject() --->  WriteXMLObject()
CreateBaseMesh() --->   WriteBaseMesh()
WriteXmlNode 추가:   단지 Xml 노드만 생성하던 CreateXmlNode() 대신 Export::WriteXmlNode() 메서드를 이용해서 Xml 노드 생성은 물론 Transform 행렬을 저장하도록 하엿다.   

2. Transform 추가

우리에게 필요한건 INode의 로컬 행렬이다.  INode로 월드행렬은 직접 얻을수 있지만 로컬행렬은 부모노드의 역행렬과 월드행렬을 이용해서 구한다.

  Matrix3 matParent = pParent->GetObjTMAfterWSM( 0 )
  matLocal = matWorld * Inverse( matParent );

루트 노드일 경우 월드와 로컬의 행렬이 같다.

    INode* pParent = pNode->GetParentNode();

 

    Matrix3 matLocal;

    Matrix3 matWorld = pNode->GetObjTMAfterWSM( 0 );

 

    if( pParent == NULL )

    {

        matLocal = matWorld;

    }

    else

    {

        Matrix3 matParent = pParent->GetObjTMAfterWSM( 0 );

        matLocal = matWorld * Inverse( matParent );

    }

로컬행렬을 XML로 저장하면 Transform 노드를 생성후 LocalMatrix 노드에 저장된다.

<Transform>
       <LocalMatrix>r1(1, 0, 0, 0),r2(0, 1, 0, 0),r3(0, 0, 1, 0),r4(30, 0, 17, 1)</LocalMatrix>
</Transform>

3. 맥스좌표계에서 다이렉트X 좌표계로 변환

맥스 좌표계에서 다이렉트X Y-Up의 왼손 좌표계로 바꿀려면 Transform 변환과 정점의 좌표변환이 필요하다.

Transform 변환 : y, z를 바꾸기 위해, 행렬의 2와 3행을 바꾸고 2와 3열을 서로 바꾼다.
                     RightToLeft()메서드로 바꾼다.

void Export::RightToLeft(Matrix3* pDst, Matrix3* pSrc )

{

    Point3    v1, v2;

 

    v1 = pSrc->GetRow(0);  v2.x = v1.x;  v2.y = v1.z;  v2.z = v1.y;  pDst->SetRow( 0, v2 );

    v1 = pSrc->GetRow(2);  v2.x = v1.x;  v2.y = v1.z;  v2.z = v1.y;  pDst->SetRow( 1, v2 );

    v1 = pSrc->GetRow(1);  v2.x = v1.x;  v2.y = v1.z;  v2.z = v1.y;  pDst->SetRow( 2, v2 );

    v1 = pSrc->GetRow(3);  v2.x = v1.x;  v2.y = v1.z;  v2.z = v1.y;  pDst->SetRow( 3, v2 );

}

정점 좌표 변환 :

정점과 인덱스도 y와 z를 바꾼다. Export::WriteBaseMesh()의 코드이다.

        Point3 v = pMesh->verts[i];

        if( GetLeftHand() )

        {

            v.y = pMesh->verts[i].z;

            v.z = pMesh->verts[i].y;

        }

 

        ...........

 

        if( GetLeftHand() )

        {

            index.b = pMesh->faces[i].v[2];

            index.c = pMesh->faces[i].v[1];

        }

위에 보이는 GetLeftHand()는 익스포트시 "Left-handed Cartesian Coordinates" 체크 박스가 셋팅 되어 있으면 왼손 좌표계로 익스포트 된다.

4. XmlMesh에 로컬 행렬 추가

XmlMesh는 XML 파일을 파싱하는 클래스이다.
파싱한 로컬행렬 값 저장을 위해  D3DXMATRIXA16  m_matLocal 멤버변수를 추가한다.

m_bUseData 멤버 변수는 파싱후 해당 노드의 데이터를 엔진용 데이터로 바꿀것인지 결정하는 멤버이다.
"Transform" 노드나 mesh의 자식노드는 파싱후 필요없는 노드로 m_bUseData = false로 셋팅하면 ENode 생성시 해당하는 XmlMesh 노드는 변환하지 않는다.

5. ENode에서 월드 행렬 계산

m_matLocal은 변하지 않는 값이고 m_matWorld는 애니메이션이나 상황에 변하는 값이다.
로컬 행렬과 부모노드를  곱해서 월드 행렬을 구한다.

void ENode::OnCalcWorldMatrix()

{

    if( m_pParent == NULL )

    {

        m_matWorld = m_matLocal;

    }

    else

    {

        m_matWorld = m_matLocal * m_pParent->m_matWorld;

    }

}

프로젝트 :  maxProject_transform.zip                 viewer_transform.zip