본문 바로가기

DirectX/3D

ASE 파일을 이용한 Bone Animation (1)

ASE 파일을 이용한 Bone Animation (1)
조회 319 | 공감 | 스크랩 1

게임 프로그래밍 | 2004-08-24 15:06:40  

[이글은 게임프로그래머에게 배우는 게임개발 테크닉에 기고했던 글의 일부입니다. 이 글의 저작권은 뭐시기 뭐시기 나부랑 나부랑 하니깐 불펌은 왠만하면 자제해 주시기 바랍니다.]
 
 
ASE파일을 이용한 Bone Animation
 
(이 글에 실린 모든 그래픽 데이터는 다른 분들에게 저작권이 있으며, 이것은 인터넷과 통신망 등에서 임의로 발췌해 사용한 것입니다. 사전에 연락을 드린 것에 대해 많은 양해를 부탁드립니다.)
 
3D프로그래밍을 시작하면서 보통 가장 먼저 하는 것이 Ase파일을 이용한 모델 출력일 것이다. Ase포맷은 3D Studio max의 export포맷중 하나로 확장된 ascii 포맷이란 의미이다. , 일반적인 모델 데이터뿐 아니라, 애니메이션 등의 데이터까지도 모두 포함된 구조이면서, Text파일 (아주 중요하다)이다.
Text파일인 것이 중요 하느냐? 하면, 당연히 이것은 사용자가 포맷을 분석하기 매우 용이하단 의미이기 때문이다. 3D는 일반적인 2D이미지처럼 바이너리화 필요가 없는 좌표의 집합이므로, 텍스트포맷으로 출력이 용이하다. 그래서 공부할 보통 텍스트로 Raw,Obj,Ase포맷 등을 많이 사용한다. 대부분의 Text포맷들이 애니메이션을 지원하지 않는 관계로, Animation까지 공부해보고자 하는 학생들에게 Ase포맷은 매우 귀중한 포맷이다.
 
 


 <그림 0> 이것이 국내에서 가장 많이 쓰이는 3D그래픽 3ds max 이다.
 
1. Ase 포맷
여기서는 일반적으로 많이 알려진 open-gl을 사용한 방법이 아니고 D3D 8.0을 Rendering API로 사용할 것이다. Ase포맷은 3Ds max의 좌표계(오른손 좌표계)를 사용하고 있다. 그렇기 때문에 여기서는 Max 좌표계로 저장된 포맷을 왼손 좌표계로 바꾸는 방법도 함께 설명하겠다. ASE포맷은 3D Max의 거의 모든 데이터를 출력해 주므로, 실제 게임에서는 사용되지 않는 데이터도 다수 존재한다. 여기서는 게임에 필요한 데이터만을 표시하고자, 사용되는 데이터는 밑줄을 쳐서 표시했다.
 
먼저 ASE포맷이 어떻게 생겼는지 알아보자. 텍스트에디터로 미리 준비된 ASE파일을 열어보면 다음과 같은 헤더가 있을 것이다.
 
-----------------------------------------------------------
*3DSMAX_ASCIIEXPORT   200
*COMMENT "AsciiExport Version  2.00 - Wed Nov 24 20:00:20 1999"
*SCENE {
             *SCENE_FILENAME ""
             *SCENE_FIRSTFRAME 0
             *SCENE_LASTFRAME 100
             *SCENE_FRAMESPEED 30
             *SCENE_TICKSPERFRAME 160
             *SCENE_BACKGROUND_STATIC 0.0000000 0.0000000            0.0000000
             *SCENE_AMBIENT_STATIC 0.0431400         0.0431400            0.0431400
}
----------------------------------------------------
여기에는 ASE포맷의 버전과 애니메이션에 대한 정보, 그리고 배경의 색값이 들어있다. 하지만, 여기에 있는 데이터중 데이터는 *SCENE_LASTFRAME , *SCENE_FRAMESPEED , *SCENE_TICKSPERFRAME  가지이다.
*SCENE_LASTFRAME 애니메이션의 마지막 프레임을 뜻한다. , 100이면 100프레임 짜리 애니메이션이란 뜻이다.
*SCENE_FRAMESPEED 1초당 프레임을 애니메이션 시키는지를 나타낸다. 위에는 30으로 나와있으므로, 1초당 30프레임의 속도로 애니메이션 된다.
*SCENE_TICKSPERFRAME 프레임당 Tick이 지나가는지를 알려준다. 애니메이션은 Tick단위로 저장된다. Tick이 160이면 1초를 플레이시키려면 160 * SCENE_FRAMESPEED Tick을 플레이시켜야 한다.
 
위의 헤더다음에 나오는 것이 재질(Material)이다. 재질에 대한 것은 대부분 알고 있으리라 생각하고, 간단하게 설명하도록 하겠다.
D3D에서 재질 사용되는 것은 open-GL이나 3D Max보다 훨씬 적게 사용된다. D3D에서 사용되는 수치는 Ambient, Diffuse, Specular 값이다. (이것은 각각 주변광, 분산광, 반사광의 값을 나타낸다.)
-----------------------------------
*MATERIAL_LIST {
             *MATERIAL_COUNT 1
             *MATERIAL 0 {
                           *MATERIAL_NAME "Material #25"
                           *MATERIAL_CLASS "Standard"
                           *MATERIAL_AMBIENT 0.0000         0.0000    0.0000
                           *MATERIAL_DIFFUSE 1.0000          1.0000    1.0000
                           *MATERIAL_SPECULAR 1.0000      1.0000    1.0000
                           *MATERIAL_SHINE 0.2500
                           *MATERIAL_SHINESTRENGTH 0.0500
                           *MATERIAL_TRANSPARENCY 0.0000
                           *MATERIAL_WIRESIZE 1.0000
                           *MATERIAL_SHADING Phong
                           *MATERIAL_XP_FALLOFF 0.0000
                           *MATERIAL_SELFILLUM 0.0000
                           *MATERIAL_FALLOFF In
                           *MATERIAL_XP_TYPE Filter
                           *MAP_DIFFUSE {
                                        *MAP_NAME "Map #1"
                                        *MAP_CLASS "Bitmap"
                                        *MAP_SUBNO 1
                                        *MAP_AMOUNT 1.0000
                                        *BITMAP "E:1.tga"
                                        *MAP_TYPE Screen
                                        *UVW_U_OFFSET 0.0000
                                        *UVW_V_OFFSET 0.0000
                                        *UVW_U_TILING 1.0000
                                        *UVW_V_TILING 1.0000
                                        *UVW_ANGLE 0.0000
                                        *UVW_BLUR 1.0000
                                        *UVW_BLUR_OFFSET 0.0000
                                        *UVW_NOUSE_AMT 1.0000
                                        *UVW_NOISE_SIZE 1.0000
                                        *UVW_NOISE_LEVEL 1
                                        *UVW_NOISE_PHASE 0.0000
                                        *BITMAP_FILTER Pyramidal
                           }
             }
}
---------------------
재질중 가장 중요한 부분이 바로 Texture인데, 3D Max에서는 몇가지 방법으로 Multi-Texture를 지원한다. 하지만, 여러 단계의 텍스춰를 입힐 없으므로, 대부분 Diffuse맵만을 읽거나 Alpha값을 사용한 Opacity 맵만을 읽어서 사용한다. 여기서는 가지 맵만을 읽어서 사용하기로 한다.
Texutre에 대한 자료는 매우 많지만, 실제로 사용되는 것은 Texutre 파일의 이름뿐이고, 나머지는 게임 내에 적용되지 않는다.
 
순서대로 읽어들이면 다음에 나오는 것은 메시(mesh)들이다. 메시에 대한 정확한 개념은 여타 다른 3D엔진에 대한 서적에 맡기고 여기서는 간단하게 메시들이 모여서 모델을 이룬다고 알고 있으면 된다. 대부분이 애니메이션이 되는 단위, 관절부를 중심으로 나눠서 만들곤 하는데, 요즘은 Skinning등의 골격체 애니메이션(Bone animation)을 사용하므로, 관절단위로 메시를 나누지 않고 있다. 여기서는 애니메이션의 기초단계인 메시단위의 애니메이션을 알아보는 것이므로, 메시를 읽어들여야 한다.
처음 나오는 것은 NODE_NAME으로 메시의 이름이다. 메시의 이름은 매우 중요한데, 이것은 나중에 메시의 계층구조를 만들 필요하기 때문이다.
다음에 읽는 것은 *INHERIT_POS 등을 뛰어넘고 TM_ROW 들이다.
이것은 3*3행렬로 메시의 회전 확대축소를 저장한 행렬이다. 실제로 게임 내에서는 4*4 행렬만을 사용하므로, 이것을 4*4 행렬로 변환해서 읽어야 한다. 하지만, 여기서도 골치 아픈 것이 있는데, 행렬이 오른손 좌표계로 만들어진 것이란 것이다. D3D의 왼손좌표계로 좌표계를 바꿔줘야한다.
, 오른손 좌표계를 왼손좌표계를 바꿀 때는 y좌표와 z좌표를 서로 뒤집으므로,
{ 11, 13, 12 }
{ 31, 33, 32 }
{ 21, 23, 22 }
{ 41, 43, 42 }
순서로 읽어들여야 한다.
 
---------------------
*GEOMOBJECT {
             *NODE_NAME "a"
             *NODE_TM {
                           *NODE_NAME "a"
                           *INHERIT_POS 0 0 0
                           *INHERIT_ROT 0 0 0
                           *INHERIT_SCL 0 0 0
                           *TM_ROW0 1.0000   0.0000      0.0000
                           *TM_ROW1 0.0000   1.0000      0.0000
                           *TM_ROW2 0.0000   0.0000      1.0000
                           *TM_ROW3 -0.9464 1.2543      -0.0950
                           *TM_POS -0.9464    1.2543      -0.0950
                           *TM_ROTAXIS 0.0000            0.0000     0.0000
                           *TM_ROTANGLE 0.0000
                           *TM_SCALE 1.0000  1.0000      1.0000
                           *TM_SCALEAXIS 0.0000        0.0000     0.0000
                           *TM_SCALEAXISANG 0.0000
             }
-------------------------------------------------
다음에 읽어들일 것은 바로 메시의 정점 (Face) 대한 정보이다. 정점정보는 단순하게 정점의 좌표값만을 저장하기 때문에 매우 읽어들이기 쉽다. , y와 z값이 서로 바뀌어야 한다는 것을 제외하고는 말이다. 그렇지만 Face정보는 틀리다.
먼저 면은 점의 정점으로 이루어져 있다. 면의 점에는 각각의 Texture를 매핑할 있는 u, v좌표가 저장된다. 일반적인 모델링일 경우는 정점의 숫자와 u,v좌표의 숫자가 같지만, 대부분의 게임에서의 경우는 u,v매핑을 일일이 수작업으로 지정하기 때문에 면마다 u,v좌표가 틀려진다. 이때는 어쩔 수없이 정점의 개수를 늘려주거나, 아니면 면에 직접 저장하는 수밖에 없다. 여기서는 면에 저장하는 방법을 택했다. 또한 Ase는 면마다 Texture를 다르게 입힐 있는데, *MESH_MTLID를 참조하여 매핑하는 것이다.
CVERTEX라 함은 정점 색상(Vertex Color)을 지정할 사용된다. 하지만, 정점 색상은 게임에서 사용되는 경우가 드물기 때문에 여기서는 사용하지 않기로 한다.
*MESH_NORMALS는 면마다 법선(Normal)값을 저장하고 있다. 물론 셰이딩(Shading) 위해 면에 속한 정점에도 법선값이 저장된다.
이러한 정보들을 읽을 한가지 유의해야 점은 CCW(Counter ClockWise)로 작업을 하게 된다면, 면에서 정점을 읽는 순서를 바꿔주어야 한다는 것이다. ASE는 CW(ClockWise) 저장되어 있기 때문에 D3D에서 기본으로 사용되는 CCW로 작업하려면 물론 이것을 바꿔서 읽어주어야 한다. , 0, 1, 2 순서가 아니고, 0, 2, 1 순서로 읽어주면 된다.
 
             *MESH {
                           *TIMEVALUE 0
                           *MESH_NUMVERTEX 24
                           *MESH_NUMFACES 20
                           *MESH_VERTEX_LIST {
                                        *MESH_VERTEX    0            -1.1347    1.7350      -0.0950
---------- 이하 생략 -----------------
 
                           }
                           *MESH_FACE_LIST {
*MESH_FACE    0:    A:    0 B:    9 C:    8 AB:    0 BC:    1 CA:    1          *MESH_SMOOTHING 1           *MESH_MTLID 0
------------------ 이하 생략 --------------------------
                           }
                           *MESH_NUMTVERTEX 48
                           *MESH_TVERTLIST {
                                        *MESH_TVERT 0     0.9811     0.1357     0.0000
----------------- 이하 생략 ---------------------------
                           }
                           *MESH_NUMTVFACES 20
                           *MESH_TFACELIST {
                                        *MESH_TFACE 0     0            9            8
----------------- 이하 생략 ---------------------------
                           }
                           *MESH_NUMCVERTEX 1
                           *MESH_CVERTLIST {
                                        *MESH_VERTCOL 0 1.0000     1.0000     1.0000
                           }
                           *MESH_NUMCVFACES 20
                           *MESH_CFACELIST {
                                        *MESH_CFACE 0    0            0            0
----------------- 이하 생략 ---------------------------
                           }
                           *MESH_NORMALS {
                                        *MESH_FACENORMAL 0        0.0000     0.9239      0.3827
                                                     *MESH_VERTEXNORMAL 0    0.0000      0.9906     0.1368
                                                     *MESH_VERTEXNORMAL 9    0.0000      0.7972     0.6037
                                                     *MESH_VERTEXNORMAL 8    0.0000      0.9906     -0.1368
----------------- 이하 생략 ---------------------------
                           }
             }
             *PROP_MOTIONBLUR 0
             *PROP_CASTSHADOW 1
             *PROP_RECVSHADOW 1
             *MATERIAL_REF 0
}
 
여기까지 읽었으면 필요한 정보는 *MATERIAL_REF 밖에 없다. 정보는 메시가 어떤 재질을 참고해서 찍어야 하는 것인가를 나타낸다. 메시에 여러 개의 재질이 사용되는 경우, ASE에서는 SubMaterial이라 하여, 재질자체가 여러 개의 재질을 가질 있도록 되어있다. *MATERIAL_REF는 서브재질을 가지고 있는 어미재질의 번호(ID)이고, 면이 가지고 있는 재질 ID는 사실은 SubMaterial의 ID이다.

 

출처 : http://blog.empas.com/deadfish0/3238873


'DirectX > 3D' 카테고리의 다른 글

Directx의 파이프라인을 따라 구현  (1) 2010.07.19
ASE 관련 서적 ASE 에니메이션 오브젝트 등등  (0) 2010.07.19
픽킹~ 수학 이론  (0) 2010.07.16
지형 렌더링 LOD  (0) 2010.07.08
쿼드트리(Quad Tree)  (0) 2010.07.06