DirectX11

3ds Max SDK Export(Object Animation) -1-

치킨CEO 2019. 10. 31. 18:27
  3ds Max(이하 Max)는 컴퓨터 그래픽스를 위한 디자인 툴이다. 이 툴로 3D 모델링, 애니메이션을 만들 수 있고, 그 결과물을 확인할 수 있다. 3ds Max SDK는 이 디자인 툴의 플러그인을 만들 수 있는 개발자 도구다. 이 개발자 도구를 통해 Max에서 만들어진 모델링과 애니메이션 데이터를 원하는 부분만 뽑아서 개발자만의 데이터 형식으로 파일을 출력할 수 있다.
 

 

1. INode
  Max에서는 모든 오브젝트가 INode를 부모로 가지고 있다. 마테리얼, 메쉬, 카메라, 라이트 등의 오브젝트를 해당되는 INode를 통해 접근할 수 있다. 접근하는 방식은 함수를 통해 해당 오브젝트 타입의 주소값을 얻는 방식이기 때문에, 상속 보다는 멤버로 가지고 있다고 생각하는 것이 이해하기가 더 쉽다.
  INode는 Max에서 작업한 것에 따라서 자식 INode를 가지고 있다. 트리구조로 되어있으며, 자식의 수는 여러 개를 가질 수 있다. 자식 INode는 부모에 영향을 받기 때문에 부모 자식의 관계는 상당히 중요하다.
 
2. 오브젝트의 위치
  Max에서의 오브젝트 위치는 복잡하게 되어있다. 부모에 따라서 영향을 받는다는 점과 애니메이션이 있기 때문에 시간에 따라 위치가 바뀐다는 점이다. 먼저 애니메이션이 없는 상태에서 오브젝트의 위치가 어떻게 되는지 보도록 하자.
  
Vertex = Local Vertex * ObjectTM

 

 
  하나의 오브젝트의 정점의 위치는 지역 정점(Local Vertex)에 ObjectTM이라고 하는 행렬을 곱한다. 그러면 Max에서 보이는 해당 오브젝트의 정점 위치가 나온다. 이 ObjectTM은 다음과 같은 행렬로 이루어져 있다.
 
ObjectTM = OffsetTM * NodeTM
NodeTM = LocalNodeTM * ParentNodeTM
 
  OffsetTM은 나중에 설명하도록 하고 NodeTM부터 보도록 하자. NodeTM은 Max에서의 World Matrix라고 생각하면 된다. World Matrix에 대해서는 3D Game의 기본이기 때문에 따로 설명은 하지 않겠다. 어쨌든 NodeTM은 WorldMatrix다. 이 NodeTM을 정점에 곱하면 월드 정점으로 바뀌게 된다.
  그러면 NodeTM은 왜 LocalNodeTM과 ParentNode으로 분리되는 것일까? 여기에 대한 해답은 INode의 부모 자식 관계에 있다. 이 다음에 나오는 표를 보도록 하자.

 

 

  해당 데이터는 Object Animation을 테스트 하기 위해 쓴 터렛에서 뽑은 데이터 중 일부이다. Parent는 부모가 없는 INode이고Child는 Parent의 자식 INode이다.

 

  Parent한테 부모가 없음에도 불구하고 ParentNodeTM이 단위 행렬이 아니다. 이것은 Max상에서 부모가 없더라고 무조건 부모가 하나씩 잡혀있기 때문이다. 실제로 Parent의 NodeTM을 구하면 ParentNodeTM의 데이터는 무시된다.
  그 증거로 Child의 ParentNodeTM이 Parent의 LocalNodeTM과 값이 같다. 실제로 Parent의 ParentNodeTM이 Parent의 NodeTM에 영향이 있었다면, 이러한 값이 나오지 않았을 것이다. 정리하자면, 두 오브젝트의 월드 정점 위치는 다음과 같이 나오게 된다.
 
Parent Vertex = Local Pos * NodeTM
   = Local Pos * LocalNodeTM
 
Child Vertex  = Local Pos * NodeTM
   = Local Pos * LocalNodeTM * ParentNodeTM
 
  이 정리를 보고 한 가지 중요한 사실을 눈치챈 사람이 있을 것이다. 이 중요한 사실이란, Child의 월드 좌표는 Parent의 월드 좌표로부터 얼마만큼 이동한 위치라는 점이다. 즉, 부모가 있는 INode의 월드 좌표는 부모의 월드 좌표에 영향을 받는다. 이 말은 각 INode의 로컬 원점은 다음과 같은 그림이 나오게 될 것이다.
 
 
  이 원점은 사실 로컬 원점이라고 부르지 않는다. 이 원점은 뼈 좌표계의 원점이라고 부른다. 로컬 좌표계가 아닌 뼈 좌표계인 이유는 OffsetTM이 있기 때문이다.
 
 
  위의 그림에서 왼쪽을 보면 팔이 원점에 이상하게 붙어있는 것을 볼 수 있다. OffsetTM이 없으면 이렇게 붙을 수도 있고 오른쪽 처럼 정상적으로 붙을 수도 있다. 이 팔 오브젝트가 어떻게 디자인이 됐는지 따라 다르다. 원점이 팔 끝을 기준으로 할 수도 있고, 팔 중앙일 수도 있다. 이러한 부분을 보정하여 오른쪽처럼 붙게 하는 행렬이 OffsetTM이다. 다시 정리하자면, 팔이 정확한 위치에 옮겨지도록 원점을 옮기는 행렬이 OffsetTM인 것이다. 그러면 위에서 정리한 월드 정점 위치는 다음과 같이 수정되어야 할 것이다.
 
 
Parent Vertex = Local Pos * OffsetTM * NodeTM
   = Local Pos * OffsetTM * LocalNodeTM
 
Child Vertex  = Local Pos * OffsetTM * NodeTM
   = Local Pos * OffsetTM * LocalNodeTM * ParentNodeTM
 
  이제 그림에서 보여줬던 원점이 왜 로컬 원점이 아닌지 알 수 있을 것이다. OffsetTM이 적용된 정점의 원점이기 때문에 로컬 원점이라 할 수 없다. 대신 이름이 붙은 것이 뼈 좌표계의 원점인 것이다. 뼈라고 부르는 이유는 뼈대와 같은 역할을 해주기 때문이라고 이해하면 된다.
 
3. 오브젝트 애니메이션
  이 상태로 데이터를 뽑게 되면 정점(혹은 오브젝트)를 정확한 위치에 옮기는 것은 가능해졌지만, 현재 상태로는 애니메이션이 불가능하다. 시간에 따라서 정점이 변환되는 작업을 처리하지 않았기 때문이다. 그러면 어떻게 해야 그 작업이 가능해질까?
  사실 Max에서 ObjectTM이나 NodeTM을 얻을때 쓰는 함수는 인자값을 필요로 한다. 그 인자값이 바로 시간이다. 이 시간의 단위는 Tick 단위인데, 일반적으로 1Frame 당 160Tick이 된다. 이것 또한 모델러가 어떻게 작업했는지에 따라 Frame당 Tick이 몇인지 달라진다.
  넘겨주는 인자값에 따라서 애니메이션 작업이 되었다는 전제 하에 ObjectTM과 NodeTM이 달라진다. 이걸 달리 이야기하면, 매 프레임마다 본 좌표계에 있는 정점들을 해당 프레임의 NodeTM을 곱해주면 애니메이션이 적용되는 것이다.
 
Vertex = Local * ObjectTM(Tick)
Vertex = Local * OffsetTM * NodeTM(Tick)
 
  그러면 Max에서 정점과 매 프레임에 해당되는 ObjectTM을 데이터로 뽑거나, OffsetTM이 적용된 본 좌표계의 정점과 매 프레임에 해당되는 NodeTM(Tick)을 데이터로 뽑으면 DirectX에서 사용 가능할 것이다. Texture 처리는 되지 않았지만 적어도 색 하나를 통일시키면 오브젝트들이 똑같은 애니메이션을 하게 될 것이다.
  이렇게 사용하면 일단 애니메이션은 가능해진다. 다만, 이렇게 쓴다면 여러 문제가 발생하는데 이거는 다음 포스팅때 다루도록 하겠다.