3D MAX로 만든 데이터를 DirectX로 그리기 위해서는 여러가지 과정이 필요하다.

그중 이번에는 ASE Animation 데이터를 읽어보자.

애니메이션의 종류는 여러가지인데 그 중 지금하는건 키프레임 방식의 애니메이션이다.


정점부터 애니메이션까지 모든 정보를 가진 상태를 기준으로 진행된다.(파싱이 완료된 상황)


위의 사진과 같이 업데이트를 돌게된다.

nKeyFrame은 현재 프레임을 말한다. (ex: 시작 프레임이 4 , 마지막 프레임이 20이라면 4 ~ 20 사이의 값)

pMatParent는 부모 행렬을 가리킨다. (단 Root는 부모 행렬이 없다.)


Scale -> Rotate -> Translate (크기 , 회전 , 이동)의 과정중 현재 회전 , 이동만 처리되어있다.


1) CalcLocalR을 통해 Rotate행렬을 구한다.

2) CalcLocalT를 통해 Translate 행렬을 구한다.

3) 부모가 없다면 Rotate * Translate 행렬을 곱해 자신의 로컬 행렬을 구하고

부모가 있다면 Rotate * Translate 한 행렬에 부모 행렬을 곱해준다.

4)그다음 노드(자신)가 소유한 자식들에게 현재 가지고 있는 월드 매트릭스(행렬)를 보내준다.


먼저 위의 사진과 같이 업데이트를 하기 위해서는 시간이 일단 흘러가야한다.

시간을 어떻게 증가시키는지 확인해보자.


main.cpp에서 업데이트를 호출할땐 Root에서 nKeyFrame을 구하고 있다. 이함수를 통해 시간이 늘어나는것을 확인하고 있다.


다음 사진은 시간을 구하는 함수와 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함수를 보자.

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행렬이 모두 구해진다.

이제 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에서 처리되고 있다)

이와같은 흐름을 통해 애니메이션을 재생할 수 있다.

+ Recent posts