3D MAX로 만든 데이터를 DirectX로 그리기 위해서는 여러가지 과정이 필요하다.
그중 이번에는 ASE Animation 데이터를 읽어보자.
애니메이션의 종류는 여러가지인데 그 중 지금하는건 키프레임 방식의 애니메이션이다.
정점부터 애니메이션까지 모든 정보를 가진 상태를 기준으로 진행된다.(파싱이 완료된 상황)
![](https://t1.daumcdn.net/cfile/tistory/22086A38591430F206)
위의 사진과 같이 업데이트를 돌게된다.
nKeyFrame은 현재 프레임을 말한다. (ex: 시작 프레임이 4 , 마지막 프레임이 20이라면 4 ~ 20 사이의 값)
pMatParent는 부모 행렬을 가리킨다. (단 Root는 부모 행렬이 없다.)
Scale -> Rotate -> Translate (크기 , 회전 , 이동)의 과정중 현재 회전 , 이동만 처리되어있다.
1) CalcLocalR을 통해 Rotate행렬을 구한다.
2) CalcLocalT를 통해 Translate 행렬을 구한다.
3) 부모가 없다면 Rotate * Translate 행렬을 곱해 자신의 로컬 행렬을 구하고
부모가 있다면 Rotate * Translate 한 행렬에 부모 행렬을 곱해준다.
4)그다음 노드(자신)가 소유한 자식들에게 현재 가지고 있는 월드 매트릭스(행렬)를 보내준다.
먼저 위의 사진과 같이 업데이트를 하기 위해서는 시간이 일단 흘러가야한다.
시간을 어떻게 증가시키는지 확인해보자.
![](https://t1.daumcdn.net/cfile/tistory/223A0F3E5914321316)
main.cpp에서 업데이트를 호출할땐 Root에서 nKeyFrame을 구하고 있다. 이함수를 통해 시간이 늘어나는것을 확인하고 있다.
![](https://t1.daumcdn.net/cfile/tistory/211CC5455914329C11)
![](https://t1.daumcdn.net/cfile/tistory/211718455914329C35)
다음 사진은 시간을 구하는 함수와 ASE파일에서 SCENE부분이다. 내용을 먼저 보자.
ASE파일에서 FIRSTFRAME은 4이다. 즉 0 ~ 4 사이의 프레임에 대한 정보가 없다.
LASTFRAME이 20이기 때문에 지금 이 애니메이션 4 ~ 20프레임 까지의 애니메이션 정보밖에 없다.
4보다 작은 프레임 , 20보다 큰 프레임에 대한 정보가 없다는 점에 주의해야 한다.
이제 GetKeyFrame함수를 보자.
GetTickCount는 컴퓨터(윈도우)가 켜진뒤 값이 계속 증가하는데 이 값을 얻어오는 함수이다.
1.First에 첫 프레임값 (4)과 틱에 따른 프레임 증가값 (160)을 곱해서 담아준다.
2.Last에 마지막 프레임 값(16)과 틱에 따른 프레임 증가값 (160)을 곱해서 담아준다.
3.GetTickCount를 하게되면 일단 컴퓨터로부터 어떤 값이 넘어오게 된다.
이 값을 (최대프레임 - 최소프레임) 차를 이용해 나머지 연산을 하게되면 프레임 차이인 16보다 작은 값이 나오게 된다.
4.하지만 지금 애니메이션 정보가 4 ~ 20 까지 있기 때문에 최소값인 4를 더해준다.
이렇게 최소값을 더해주게 되면 4 ~ 20 사이의 값이 저 함수를 통해 나오게 된다.
GetTickCount를 하게되면 컴퓨터가 켜져있는 이상 값이 계속 증가하기 때문에 시간이 증가하게 된다.
단 이함수는 행동이 이어지는것 뿐이지 처음부터 애니메이션을 하지는 않기 때문에
애니메이션이 처음부터 나오게 하길 원한다면 기준이 될 시간값 (여기서는 GetTickCount)을 대체할 다른 변수가 필요하다.
이함수를 통해서 시간이 지남에 따라 프레임이 증가하는 효과를 낼수 있다.
이제 시간은 흘러가게 됬으니 Rotate함수와 Translate함수를 보자.
![](https://t1.daumcdn.net/cfile/tistory/231FD74F5914347502)
Rotate 행렬을 구하는 함수를 먼저 보자.
1) matR이 초기화가 안되있기 때문에 항등행렬(Identity)로 초기화 해준다.
m_vecRotTrack은 ASE파일에서 읽어온 ROT_TRACK 데이터를 담아둔 곳이다.
2) 만약 아무 정보도 없다면 자신의 좌표인 LocalTM으로 초기화 해준다.
3)프레임이 RotTrack에 맨처음에 담긴 n보다 작거나 같다면 Rotation행렬을 RotTrack[0]에 담긴 쿼터니언 값만큼 회전한뒤 함수를 끝낸다.
-> 0 ~ 4 프레임까지의 정보가 없기 때문에 nKeyFrame이 4보다 작은 값이 들어오면 최소프레임인 4의 데이터를 넣어준다.
4)프레임이 RotTrack에 맨마지막에 담긴 n보다 크거나 같다면 RotTrack[Size - 1]에 담긴 쿼터니언 값만큼 회전한뒤 함수를 끝내버린다.
-> 20프레임 까지의 데이터밖에 없기 때문에 nKeyFrame이 20보다 큰값이 들어온다면 마지막 값이 20의 데이터를 넣어준다.
5) 만약 여기까지 왔는데도 아무 조건에 해당하지 않는다면 이제 RotTrack에 담긴 모든값들을 확인한다
-> 이 과정은 현재 프레임이 어디인지를 찾는 과정이다.
6) 이렇게 해서 현재 프레임을 찾은뒤 t를 구하게 된다 (t는 이전 프레임 , 다음 프레임 에서 어느정도만큼 움직였는지 확인하는 거다)
EX : 1프레임일때 X좌표가 0, 2프레임일때 X좌표가 10, 지금 X좌표가 5라면 현재 내 t값은 0.5다. (0 ~ 10 에서 5는 절반만큼 왔으니까
비율로 나타낸다면 0 ~ 1 의 절반인 0.5로 나타내 진것)
7) 이런식으로 이전 프레임 , 다음 프레임 사이에서 내가 얼마나 움직였는지 t값을 구하고 그값만큼 쿼터니언 함수를 이용해서 행렬을 이동한다.
->쿼터니언을 이용해서 회전하지 않으면 짐벌락이라는 문제가 생길수 있기 때문에 쿼터니언을 이용한다.
이단계를 마무리 하면 Rotation행렬이 모두 구해진다.
![](https://t1.daumcdn.net/cfile/tistory/211B2B4559143C0812)
이제 Translate행렬을 구해보자.
1) 먼저 MatT를 항등행렬(Identity)로 초기화 해준다.
2) 만약 POS_TRACK의 값이 없다면 자신의 Local좌표 (LocalTM의 4행 1열 , 4행 2열, 4행 3열의값 코드기준시 -1씩)를 넣어준다.
3) 현재 프레임이 맨처음 프레임보다 적거나 같다면 x,y,z를 맨처음 프레임 애니메이션 값으로 초기화한다.
->0 ~ 4프레임 까지의 정보가 없기 때문에 nKeyFrame이 최소프레임보다 적게 들어온 상황을 위한 예외처리
4) 현재 프레임이 맨 마지막 프레임보다 크거나 같다면 x,y,z를 마지막 프레임 애니메이션 값으로 초기화한다.
->20프레임 이후의 정보가 없기때문에 nKeyFrame이 20프레임보다 크게 들어온 상황을 위한 예외처리
5) 두 예외상황이 모두 아니라면 이제 현재 프레임이 어디에 있는지 벡터를 모두 돌면서 찾는다.
벡터를 모두 돌아서 현재 프레임이 어느 프레임 사이에 있는지 찾아냈다면
6) 이전 프레임 , 다음 프레임 사이에서 어느정도에 위치하고 있는지 T값을 찾는다.
7) 그다음 출발점 [nPrevIndex]에서 T값만큼 쿼터니언 회전을 해서 위치를 찾아낸다.
그뒤 그위치를 적용시킨다.
이렇게 R , T를 적용시킨뒤 부모 행렬이 만약 존재한다면 부모행렬을 곱해주면 된다. (main의 cpp에서 처리되고 있다)
이와같은 흐름을 통해 애니메이션을 재생할 수 있다.