posted by Junction 2020. 9. 8. 17:51

쌀 옥수수 50

마토마 30

대왕호박 500

'어나더 에덴' 카테고리의 다른 글

파르지팔 궁전(서브퀘스트) - 애도의 꽃  (0) 2020.09.07
posted by Junction 2020. 9. 7. 06:47

파르지팔 궁전

1. 복도

 

아크툴 행상

2. 빨강하양분홍

3. 5장

4. 코린다 장미

'어나더 에덴' 카테고리의 다른 글

어나더에덴 - 라크니바 서브퀘스트  (0) 2020.09.08
posted by Junction 2017. 5. 31. 16:58

 

인테리어 목공 가벽.

 

 

 

가벽을 시공할 자리에 우선 먹을 놓는다.

먹선에 맞춰  위 아래 각목을 설치한다.

450mm간격으로 상을 세운다.

 

 

레이저 레벨기를 이용하여 수직 수평을 잡아가며

상이 흔들리지 않도록 mdf와 각목을 이용해 고정한다.

( 이때 mdf는 딱 맞출 필요 없이 고정용도로만 사용하고

튀어나온 부분은 톱이나 기타 공구를 통해 잘라내면 된다. )

 

 

석고 보드를 박아준다( 422타카 사용 )

( 중간에 비어있는 곳은 콘솔박스를 만들기 위한 용도. )

 

 

 

해당 표시가 되어있는 벽면과 바닥은 굴곡이 있을수 있기 때문에

특별한 방법을 이용하여 시공한다.

 

채워야 하는 사이즈에서 가장 넓은 사이즈를 측정

측정후 그 사이즈 만큼 재단.

 

 굴곡진 벽에 맞춰 채워야 하는 부분.

 석고보드 1

석고보드 2  

 

적당한 사이즈 ( 예 : 100mm ) 정도를

채워야 하는 부분에서 석고보드1 쪽으로 띄워서

재단된 석고보드를 630(실타카)을 이용해 살짝 고정시켜준다.

 

mdf나 각목등 이용해 띄운만큼의 사이즈로 가이드를 만들어 굴곡진 벽에 대고 선을 긋는다.

 

재단된 석고보드에 그어진 선대로 재단후 붙이면 굴곡진부분을 깔끔하게 시공할수 있다.

바닥도 미장을 하면서 수평을 맞춰도 어느정도 굴곡이 생길수 있기에

위와 같은 방법을 이용하면 좋다.

 

수평 수직이 잘 맞다면 그냥 시공하면 좋으나 대부분이 굴곡이 있다고 한다.

 

 

 

 

 

'잡동사니' 카테고리의 다른 글

인테리어 목공- 천장  (0) 2017.05.31
사인 코사인 탄젠트  (0) 2012.09.06
.hack 이미지  (0) 2012.08.16
posted by Junction 2017. 5. 31. 07:56

 

지정된 수치에 먹을 놓기 위해 레이저 레벨기를 이용해 허리먹을 놓고

허리먹에서 길이를 재어 천정먹을 놓는다.

 

(석고보드, mdf합판 등의 두께와 석고보드에 페인트 칠을 할때는

두장을 붙여야 하기 때문에

먹을 놓기 전에 마지막 마감재를

생각해서 먹을 놓는다. .)

 

 

 

먹을 놓은후

먹선에 맞춰 다루끼를 댄다.

ct64를 이용해 st핀으로 콘크리트 벽에 박는다

(벽이 목재일경우 dt핀을 이용해 고정)

 

 

천장에 상을 그냥 설치하면 휘기 때문에 상을 고정시켜줄

트러스를 설치한다.

 

 

각목을 이용하여 상을 설치한다

이때 각목의 길이는 완벽하게 맞추기 보다는 살짝 짧게 자른후

한쪽을 dt핀으로 박고 핀의 길이를 이용해 끝부분을 맞춘다.

상의 간격은 300mm혹은 450mm 간격으로 설치한다.

 

 

 

상이 다 설치되면 그 위에 석고 보드나 mdf합판을 422타카를 이용하여 고정시키고 마무리 한다.

 

 

 

 

설치 전에 등을 달수 있는 전기 선의 위치를 생각해서 전기선을 뺄수 있도록 공간을 만들어 주고

등을 고정시킬수 있게 안쪽에 각목을 이용하여 보강대를 만들어 놓는다.

 

 

*동일한 수량을 이용해 깔끔한 간격으로 시공하는 방법

(석고보드 한장의 길이 가로 900, 세로 1800)

천장의 길이가 2400(가로) 1800(세로)이라고 했을경우 석고보드 3장 필요 

두장 붙이고 하나를 600으로 재단해서 붙이는것보다

해당 계산을 통해 재단하는것이 깔끔하다

2400(천장가로길이)/900(석고보드) = 2 나머지 600

( 900(석고보드)+600(위에 계산식에 나머지 길이) )/2 = 750

750사이즈로 좌우측 끝을 붙여주고 가운데 원판 한장을 붙여주면 

 

두장을 붙이고 한장 재단해서 붙이는것보다

깔끔해 보이고 좋다. (타일을 붙일때도 같은 방법을 이용한다.)

'잡동사니' 카테고리의 다른 글

인테리어 목공 - 가벽.  (0) 2017.05.31
사인 코사인 탄젠트  (0) 2012.09.06
.hack 이미지  (0) 2012.08.16
posted by Junction 2017. 5. 30. 11:19

PillarsOfEternity_Data_09.24_2_.7z

 

덮어 씌우기

 

posted by Junction 2017. 5. 13. 00:41

Pillars Of Eternity 한글 패치.rar

 

덮어 씌우기.

'Data' 카테고리의 다른 글

razer-deathadder  (0) 2014.01.26
갓 이터 버스트( GEB ) - Drama & Original Soundtrack  (0) 2012.11.10
tap control  (0) 2012.11.04
도트 이미지 - setgalefreeus  (0) 2012.10.25
pushpush  (0) 2012.10.25
posted by Junction 2014. 1. 26. 10:57
razer-deathadder

드라이버

'Data' 카테고리의 다른 글

pillars of eternity 한글 파일  (0) 2017.05.13
갓 이터 버스트( GEB ) - Drama & Original Soundtrack  (0) 2012.11.10
tap control  (0) 2012.11.04
도트 이미지 - setgalefreeus  (0) 2012.10.25
pushpush  (0) 2012.10.25
posted by Junction 2013. 10. 20. 15:57

먼저 이건 학교에서 C/C++ 갖고 간단한 코딩하는 공대생들을 위한 정보다.

웹하드 뒤져가면서 불법으로 2010 2008 이런버전 쓸 필요 없이, MS 홈페이지에서 정품 등록하고 쓰자.

단, MFC 등 일부 라이브러리가 빠져있다. 한마디로 실무용으로 내놓은게 아님.

뭐가 더 빠진지는 나도모르고 MFC가 없다는건 안다.

C/C++ 입문/중급용으론 충분히 사용가능.

 

 

http://search.naver.com/search.naver?sm=tab_hty.top&where=nexearch&ie=utf8&query=visual+studio&x=0&y=0

1. 위 링크에 방문한다.

1-1. 네이버 검색 최상단에 뜨는 MS 홈페이지 접속

(MS홈페이지 직접링크 걸었더니 먼 프록시어쩌구하면서 안됨 ㅡㅡ 네이버경유하면되는데 이거뭐임?)

 

2. 우측상단 다운로드 메뉴에 들어간다.

아래로 내리다보면 Visual Studio Express 2012가 있다.

난독증있는 공대생들을 위해 스샷찍어줌

 

 

 

 

3. 아이고 난 윈도우 8이니 2번째걸 받겠어! 하고 발기하지말고 3번째에 있는 for Windows Desktop을 받는다.

(사실 다른버전은 안써봄)

 

4. 설치한다.

 

5. 메일주소 등록하고 인증키 발급 받는다.

 

6. 끝

 

여기서 퍼왔음. http://nontoxice.tistory.com/40

posted by Junction 2013. 6. 5. 23:53

 

 

 

 

 

 

posted by Junction 2013. 5. 4. 09:51

ASE ParsingDirectX이론

2009/11/18 16:08

복사http://blog.naver.com/cor2738/150074382752

전용뷰어 보기

원본 : http://3dapi.com/bs27_ani/

출처 : http://3dapi.com/?id=0

◈Animation◈


3D Game은 정점을 통해서 화면의 장면을 연출합니다. 정점들은 사람이 하나하나 손으로
만들 수도 있지만 이런 방법은 분명히 한계가 있고, 대부분 3D 툴이라는 것을 통해서
그래픽을 담당하는 디자이너들을 통해서 만들어 집니다.
게임에서 사용되는 대표적인 3D 툴이 Max와 Maya입니다. 이들 툴은 원래 게임을 위해서
만든 툴이 아니라 애니메이션을 위해서 만든 툴입니다. 애니메이션과 게임은 둘 다 3D를
사용하지만 목적이 분명히 다릅니다. 간단히 말한다면 애니메이션은 3D를 화면의
Pixel이라는 공간에 집중하는 반면 게임은 Pixel 공간뿐만 아니라 시간까지 대상으로
삼고 있습니다.
애니메이션은 며칠이 걸려도 장면을 연출하는 픽셀을 만들면 그만 이지만 게임은 플레이
속도 또한 고려를 해야 합니다.
게임 프로그래머는 Max나 Maya 같은 3D 그래픽 툴로 만든 데이터 속에서 게임에서 필요한
데이터만을 추출(Exporting)해야 하는데 이를 담당하는 프로그램을 익스포터(Exporter)라
합니다. 익스포터는 독립적으로 실행되지 않고, 3D 툴에서 일부 기능을 위임 받아
실행되는데 이렇게 메인 프로그램 안에서 메인 프로그램의 내용을 가지고 독립적으로
실행하는 프로그램을 플러그인(Plug-in)이라 합니다.
프로그래머는 익스포터를 플러그인 형태로 만들게 되는데 익스포터 만드는 일이 그리 쉬운
일이 아닙니다. 그것은 익스포터를 만들기 위한 3D에 대한 기초지식은 물론이고, 3D 그래픽
툴의 SDK에 대한 지식을 요구하고 있어서 현업에서도 경력자들이 주로 이것을 만들고
게임 프로그램의 전문적인 영역으로 자리잡고 있습니다.
간단한 애니메이션 정도까지 처리되는 익스포터를 만드는데 처음 익스포터를 만드는 사람들은
대략 4~6개월 정도 기간이 소요가 됩니다. 또한 익스포터를 만들면서 뷰어 프로그램을 만들게
되는데 뷰어는 추출된 데이터와 툴을 비교하기 위해서 사용하고, 일부 핵심 코드를 게임
클라이언트의 오브젝트(Object) 렌더링(Rendering)에 사용하기 위해 만들어 집니다.
즉 익스포터를 만드는 것은 뷰어 + 클라이언트 오브젝트 렌더링 코드까지 만드는 작업이라
할 수 있습니다.
어디서부터 익스포터를 시작해야 할지 망막하게 마련인데 다행히도 대부분의 그래픽 툴은
자체적으로 플러그인에 대한 SDK와 예제들을 함께 제공해 주고 있습니다. 이것은 툴에서
모든 것을 제공하는 것이 한계가 있어서 일부 중요한 기능들은 툴에서 가지고 있고, 나머지
기능들은 플러그인을 통해서 서비스하고, 툴을 사용하는 사람들이 각자의 요구사항에 필요한
기능을 플러그인으로 만들어서 전체 프로그램을 유연하게 사용할 수 있게 하기 위해서 입니다.
플러그인은 일종의 DLL(Dynamic Linking Library)입니다. DLL도 하나의 실행 프로그램이라
할 수 있습니다. 맥스(3D Max)의 경우 시작할 때 플러그인 폴더에 있는 모든 것을 로딩하기
때문에 맥스가 실행되는 동안 플러그인을 바꿀 수가 없습니다. (Maya는 툴이 실행되고 있어도
플러그인 교체가 가능하다고 합니다.) 따라서 맥스에 대한 플러그인을 바꾸려면 매번 맥스를
닫고, 새로운 플러그인을 복사해야 합니다.
그런데 이것이 워낙 불편해서 맥스와 플러그인 중간에 교체를 도와주는 플러그인을 두어
맥스를 닫지 않고, 실행하는 도중에 플러그인을 교체할 수 있도록 플러그인을 감싼 플러그인을
만들게 되었습니다. 이러한 플러그인을 Wrapper 플러그인이라고 하는데, 래퍼 플러그인은
맥스가 처음 실행될 때 같이 로딩 되고 맥스가 래퍼 플러그인을 호출하면 래퍼 플러그인이
대상 플러그인을 다시 호출하는 방식입니다.
툴에서 3D데이터를 추출하는 방법이 플러그인뿐만 아니라 스크립트를 이용하는 방법도
있습니다. 스크립트를 사용하는 방법은 소규모 팀 작업에서는 탁월한 효과를 보이고
있습니다. 무엇보다도 맥스의 스크립트는 번잡하지 않습니다. 또한 그래픽 디자이너들도
쉽게 사용할 수 있도록 스크립트가 구성되어 있어서 프로그래머가 작성하지 않더라도
원하는 데이터를 만들어 낼 수 있습니다.
플러그인을 사용하게 되는 경우라면 그래픽과 프로그램 개발팀이 완전히 분리되어 있는
시스템이거나 상업 목적을 위해서 사용할 때입니다. 의사소통이 잘 되고, 자신들이 만드는
게임의 오브젝트 데이터가 간단한 구조이면 플러그인보다는 스크립트가 작업의 효율 면에서
훨씬 좋은데 스크립트를 사용하는 방법은 다른 강좌에서 진행하도록 하겠습니다.
캐릭터 애니메이션의 시작은 Max에서 제공하는 ASE(AsciiExport)에 대한 파싱(Parsing)입니다.
파싱은 구문 분석으로 ASE 파일의 내용이 3D에 적용되는 의미를 해석하는 것입니다.
그런데 ASE를 파싱하는 이유는 ASE는 맥스 SDK의 플러그인 예제로 있을 뿐만 아니라 텍스트
형태로 구성되어 있어서 독자적인 플러그인과 애니메이션 개발에 충분히 연습할 수 있는 자료
구조로 구성되어 있기 때문입니다.
즉 ASE는 일종의 애니메이션과 플러그인의 기초과정이라 할 수 있기에 이 장에서는 ASE를
파싱하면서 캐릭터 애니메이션을 구현하도록 하겠습니다.



1 ASE Parsing


1.1 간단한 텍스트 파일 입출력


맥스의 Create Tab에서 Standard Primitives에 Teapot 모델을 만들 수 있는 메뉴가 있습니다.
이를 통해서 주전자를 만들고 파일 메뉴의 “Export...”를 선택한 후, 파일 형식에서
ASCII Scene Export(*.ASE)를 선택한 다음, 파일 이름을 저장하면 다음과 같은 창이 보이고
아래와 같이 체크를 한 다음 OK 버튼을 누르면 ASE 파일이 생성됩니다.



LcAs00_teapot.ase


이 파일을 열어 보면 머리가 아플 정도로 상당히 내용이 많습니다. 위의 익스포트 창에서
애니메이션과 관련된 데이터까지 포함 한다면 파일의 내용과 크기가 엄청 더 커집니다.
이렇게 내용이 많다고 너무 겁먹을 필요는 없습니다. 이것들은 단계적으로 하나씩
처리하면서 일정한 형식을 통하면 여러분들의 자료구조에 눈 깜짝할 순간에 올라오게
되어있습니다.
이처럼 거대한 이러한 파일을 처음부터 분석하지 않고 단순한 형태를 가지고 시작하는
것이 중요합니다. 먼저 몸 풀기부터 시작하죠.
ASE파싱을 하기 위해서 파일 입출력에 대한 C언어의 텍스트모드와 바이너리 모드에 대한
함수들을 기억해야 합니다. 또한 텍스트 단위로 읽어 들일 때 라인단위로 읽고 이를 해석하기
위해 fgets() 함수와 sscanf() 함수를 활용할 것입니다.
다음은 간단한 파일 입출력 예제입니다.
이 예제는 텍스트로 되어 있는 ASE를 라인단위로 읽어와서 문장의 앞과 끝의 공백, 탭, 라인 변경
등의 Escape 문자를 제거한 후 확인해서 다시 텍스트 파일로 출력하고 있습니다.

LcAs01_Text_Read.zip



1.2 구문과 문장해석(데이터 파싱)


앞의 예제는 텍스 파일의 연습이었고, 이제부터 본격적으로 ASE를 해석해볼 차례입니다.
LcAs02_Text_Parse.zip 파일의 압축을 풀고 Model폴더를 보면 ASE파일이 하나 있습니다.
이 파일은 이전의 파일보다 많은 정보를 담고 있는데 각각의 라인을 훑어 보면
ASE는 {Keyword, Values}의 형식으로 되어 있음을 직관적으로 알 수 있습니다.
또한 Values는 없는 것도 있고, 하나만 있는 것도 있고 복수의 형태로 구성되어 있는 것도
있음을 볼 수 있고 이 모든 값들은 전적으로 Keyword에 따라 달라짐을 알 수 있습니다.
이것은 Keyword에 대해서 문장을 해석해야 함을 간접적으로 이야기 하고 있고 각 문장에
따라 코딩을 해야 함을 의미합니다.
문장의 끝에 '{' 기호가 있는 경우에는 자료구조 블록의 시작을 알리고 블록의 끝에서는 '}'으로
지정하고 있음을 볼 수 있습니다.
이렇게 중 괄호로 표현된 부분은 자료구조를 선언하고 나서 while문으로 처리해야만 합니다.
그래서 이런 부분들이 ASE파싱에서 가장 난이도가 있는 부분이기도 합니다.
Values에서 유의해 볼 것은 큰따옴표(" ")로 구성되어 있는 값이 있다는 것도 잊지 않아야
합니다. 이렇게 큰 따옴표로 묶인 부분은 파일 이름이나 맥스에서 작업한 지오메트리의
오브젝트 이름과 같은 문자열 데이터로써 공백이 포함되는 경우도 있으므로 단순하게
sscanf()로 읽게 되면 낭패를 볼 수 있습니다. 따라서 큰 따옴표 처리를 위한 함수를
만들어야 합니다.
전체 코드는 다음을 참고 하기 바랍니다.

LcAs02_Text_Parse.zip



1.3 자료구조


어느 정도 코드를 갖추었으면 자료구조를 만들어야 합니다. ASE 파일의 자료구조는 크게
Scene, Material(재질), Geometry Object(GEOMOBJECT)로 구성되어 있습니다. Scene 자료구조는
애니메이션의 프레임과 시간이 저장되어 있습니다.
Material 자료구조에는 조명에 필요한 재질, 정점 좌표에 적용이 되는 텍스처, 라이팅 등등의
정보들이 저장되어 있습니다.
가장 중요한 것은 GEOMOBJECT 자료구조 입니다. 이 자료구조에는 맥스에서 작업할 때 지정된
자신의 노드 이름과 링크가 설정이 되었을 때 부모 노드의 이름이 있고, 장면을 구성하기 위한
월드 행렬, 정점의 인덱스, 정점의 위치, 법선, 텍스처 좌표 등에 해당하는 메쉬 정보 그리고,
애니메이션에 관련된 프레임에 따라 위치, 이동, 크기변환에 대한 정보, 머티리얼 인덱스 등이
저장되어 있습니다.
다음의 예제는 ASE 파일의 내용 중에서 GEOMOBJECT 자료구조를 간단하게 자료구조를 해석하는
연습을 위한 예제입니다.

LcAs03_struct.zip



1.4 뷰어와 피라미드


콘솔 모드에서 할 수 있는 작업은 거의 다 끝이 났습니다. 이제부터는 화면에 장면을 연출
하면서 코드를 보강 해야 합니다.
먼저 준비해야 할 것은 ASE 모듈을 테스트 할 수 있는 드라이버(Driver)가 필요합니다.
드라이버는 키보드, 카메라, 그리고 오브젝트의 크기를 눈으로 확인할 수 있는
그리드(Grid)가 보이는 프로그램이어야 합니다.

LcAs04_Viewer_basic.zip


콘솔 모드에서 연습한 ASE 파서 클래스를 이 프로그램에 적용 시켜 렌더링을 연습해 봅시다.
ASE를 해석할 때 주의할 점은 정점의 위치를 읽는 경우 Max의 Y 축과 Z 축은 DirectX의 Z 축과
Y 축이 됩니다. 따라서 다음과 같이 y와 z를 읽어야 합니다.

	INT	nIdx=0;
	FLOAT	x=0.F, y=0.F, z=0.F;
	sscanf(sLine, "%*s %d %f %f %f", &nIdx, &x, &y, &z);

	m_pGeo[nGeoIdx].pLstVtx[nIdx].x = x;
	m_pGeo[nGeoIdx].pLstVtx[nIdx].y = z;
	m_pGeo[nGeoIdx].pLstVtx[nIdx].z = y;


MAX는 오른손 좌표계를 사용하고 DirectX는 왼손 좌표계를 사용합니다. 삼각형을 그리는 순서가
MAX는 시계 반대방향(CCW: Count Clock Wise)로 그리고 DirectX는 시계 방향(CW: Clock Wise)로
그립니다. 따라서 삼각형을 그리는 순서를 다음과 같이 바꾸어 줍니다.

	INT	nIdx=0;
	INT	a=0, b=0, c=0;

	sscanf(sLine, "%*s  %d:  %s  %d  %s  %d  %s  %d"
		, &nIdx
		, sTmp1, &a
	, sTmp1, &b
		, sTmp1, &c);
		
	m_pGeo[nGeoIdx].pLstFce[nIdx].a = a;
		m_pGeo[nGeoIdx].pLstFce[nIdx].b = c;
		m_pGeo[nGeoIdx].pLstFce[nIdx].c = b;


다음의 예제는 위의 내용을 바탕으로 “Pyramid.ASE” 파일을 렌더링 하는 예제입니다.

LcAs05_Viewer_pyramid.zip





1.5 추상화


ASE를 해석하는 코드는 애니메이션과 오브젝트에 대한 처리가 정교해질수록 코드가 점점
커지게 되어 있습니다. 따라서 이런 부분을 라이브러리로 만들어 놓고 사용한다면 ASE
코드를 만드는 개발자나 이 코드를 게임에 적용하는 메인 프로그래머에게 적용에 대해서
번잡하지 않게 만들어 작업의 효율을 높일 수 있습니다.
만약 다음과 같이 ILcMdl 클래스를 최상위 클래스를 정의해 놓고 CLcAse 클래스가 이를
상속해서 내부 코드를 만든다면 쉽게 라이브러리로 전환 할 수 있습니다.

	struct ILcMdl
	{
		virtual ~ILcMdl(){};
		virtual INT	Create(void* pDev, void* sFile)=0;
		virtual void	Destroy()=0;

		virtual	INT	FrameMove()=0;
		virtual void	Render()=0;
	};

	INT LcAse_Create(char* sCmd
			 , ILcMdl** pData	// Output data
			 , void* pDev		// Device
			 , void* sName	= NULL	// Model File Name
			 , void* pOriginal=NULL	// Original ILcMdl Pointer for Clone Creating
			 , void* p4=NULL	// Not Use
			 , void* p5=NULL	// Not Use
					 );

	class CLcAse : public ILcMdl
	…


이것을 코드로 구현을 했다면 드라이버 프로그램의 CMain에서 객체를 다음과 같이 선언하고
객체의 생성은 CMain 클래스의 Init()함수에서 LcAse_Create() 함수로 생성하도록 합니다.

	class CMain : public CD3DApplication
	{
	public:
		…
		ILcMdl*		m_pMdl;
		…

	HRESULT CMain::Init()
		…
		if( FAILED( LcAse_Create(NULL, &m_pMdl, m_pd3dDevice, "Model/Pyramid.ASE")))
			return -1;
	…


객체는 CMain::Destroy() 함수에서 소멸하고, 렌더링은 CMain::Render() 함수에서 다음과
같이 처리합니다.

	HRESULT CMain::Render()
		…
		if( FAILED( m_pd3dDevice->BeginScene() ) )
			return -1;
		…
		SAFE_RENDER(	m_pMdl		);
		…
		m_pd3dDevice->EndScene();


	HRESULT CMain::Destroy()
		…
		SAFE_DELETE(	m_pMdl	);
		…


간단하게 최상위 클래스로 만들고 이를 구현하는 방법을 살펴보았습니다. 나머지 부분은
다음 예제를 참고 하기 바랍니다.

LcAs06_Object.zip





1.6 Geometries


캐릭터, 건물 등 게임에서 사용하는 오브젝트들은 대부분 여러 지오메트리로 구성되어
있습니다.특히 애니메이션 오브젝트들은 하나의 메쉬로 구성되어 있지는 않습니다. 이것은
오브젝트를 개별적으로 만들어서 하나로 합치기 때문입니다.

ASE는 Geometries의 숫자를 기록하지 않습니다. 따라서 다음과 같이 while 루프를 이용해서
지오메트리의 개수를 찾아야 합니다. 주의 해야 할 것은 지오메트리의 숫자를 파악했다면
파일 포인터를 반드시 처음으로 이동시켜야 합니다.

	while(!feof(fp))
	{
		…
		if(0 == _strnicmp(sLine, "*GEOMOBJECT {", strlen("*GEOMOBJECT {") ))
			++m_nGeo;
		…
	}
	…
	fseek(fp, 0, SEEK_SET);


지오메트리의 숫자를 파악하고 하고 나서 다음과 같이 지오메트리에 대한 메모리를 할당한
다음 다음과 같이 이전 코드를 변형해서 사용하면 됩니다.

	//지오메트리 생성
	m_pGeo = new AseGeo[m_nGeo];
	INT	nGeoIdx = -1;
	AseGeo*	pGeo	= NULL;
		
	while(!feof(fp))
	…
		if(0 == _strnicmp(sLine, "*GEOMOBJECT {", strlen("*GEOMOBJECT {") ))
		{
			++nGeoIdx;
			pGeo = &m_pGeo[nGeoIdx];
	…
			pGeo->…


다음 예제는 실제 게임에서 적용될 수 있는 캐릭터를 화면에 출력하는 예제입니다. 이 캐릭터는
강체(Rigid body) 형태로 구성되어 있습니다.

단일 지오메트리 - LcAs11_Geometry.zip

다중 지오메트리 - LcAs12_Geometries.zip





1.7 텍스처


애니메이션이 없는 오브젝트의 마지막 단계로 텍스처 적용이 남아 있습니다. ASE 파일 구조
안에 텍스처에 대한 정보는 머티리얼(Material: 재질) 안에 저장되어 있습니다. Max의
머티리얼은 광원에 대한 정보와 정점에서 사용하는 텍스처에 대한 파일 정보가 저장되어
있습니다.

머티리얼은 여러 하위 머티리얼을 가질 수 있습니다. 이것은 지오메트리가 하나의 메쉬로
구성되어 있지만 각각의 정점은 다른 머티리얼을 참고 할 수 있도록 Max가 구성되어 있기
때문입니다. 이에 대한 예는 각각 다른 머티리얼을 가진 두 이상의 오브젝트를 하나로
합칠 때 발생합니다. 따라서 프로그래머는 이에 대해서도 처리 해야 하지만 이 강좌에서는
이것을 다루기에 내용이 많으므로 모든 오브젝트는 하나의 머티리얼만 적용한다 가정 하고
생략하겠습니다.

UV좌표는 화면의 왼쪽 상단이 (0,0), 오른쪽 하단이 (1,1) 좌표입니다. ST 좌표는 화면의
왼쪽 하단이 (0,0), 오른쪽 상단이(1,1) 인 수학에서 사용하는 2차원 좌표계로 구성되어
있는 좌표계입니다.

DirectX는 UV 좌표를 사용하지만 Max ST 좌표를 사용합니다. 따라서 Max의 텍스처 좌표는
DirectX에 맞게 변경이 되야 하는데 Y에 대한 값을 다음과 같이 변경하면 됩니다.

	Y = 1.0f - Y


또한 Max는 텍스처 좌표를 UVW 3차원 좌표를 사용합니다. 사용할 경우 앞의 두 개만
해석해서 사용하면 됩니다. 구체적인 코드는 다음과 같습니다.

	INT	nIdx=0;
	FLOAT	u=0.F, v=0.F, w=0.F;
	sscanf(sLine, "%*s %d %f %f %f", &nIdx, &u, &v, &w);
	pGeo->pLstTvtx[nIdx].u = u;
	pGeo->pLstTvtx[nIdx].v = 1.0f - v;
	pGeo->pLstTvtx[nIdx].w = w;


Max는 정점 버퍼와 텍스처에 대한 Tvertex 버퍼를 개별적으로 가지고 있습니다. 여러 텍스처
좌표는 하나의 정점을 공유할 수 있으므로 항상 정점의 위치 좌표 수는 텍스처의 좌표 보다
같거나 작습니다.
이러한 이유로 삼각형을 구성하는 인덱스 또한 따로 가지고 있습니다. 위치에 대한
인덱스(Face)와 텍스처 좌표에 대한 인덱스(T-Face)의 내용은 꼭 같지만은 않게 되어
있습니다.
따라서 T-Face에 대한 내용을 읽어와야 합니다. T-Face도 정점의 인덱스와 마찬가지로 다음
코드와 같이 b와 c를 교환해야 합니다.

	INT	nIdx=0;
	INT	a=0, b=0, c=0;
	sscanf(sLine, "%*s %d %d %d %d", &nIdx, &a, &b, &c);
	pGeo->pLstTfce[nIdx].a = a;
	pGeo->pLstTfce[nIdx].b = c;
	pGeo->pLstTfce[nIdx].c = b;


앞서 Max는 여러 텍스처 좌표가 하나의 정점 위치를 공유할 수 있다고 했습니다. 따라서
정점 위치를 가지고 렌더링의 정점을 정하는 것이 아니라 텍스처 좌표를 가지고 정점을
구성해야 합니다. 즉, 최종 정점의 숫자는 텍스처 좌표 숫자와 일치 시키고, 삼각형의
인덱스 구성도 T-Face를 가지고 합니다.

	// 쉬운 UV먼저 설정
	for(int j=0; j< pGeo->nTvtx; ++j)
	{
		pVtxR[j].u = pGeo->pTvtx[j].u;
		pVtxR[j].v = pGeo->pTvtx[j].v;
	}

	for(int n=0; n< pGeo->nFce; ++n)
	{
		INT nT = 0; 
		INT nV = 0; 					
		
		nT = pGeo->pTFce[n].a;		// T-face U V 인덱스를 가져온다.		
		nV = pGeo->pFce[n].a;		// Vertex 버퍼에서 정점의 위치를 가져온다.
		pVtxR[nT].p = pGeo->pVtx[nV].p;	

		nT = pGeo->pTFce[n].b;
		nV = pGeo->pFce[n].b;
		pVtxR[nT].p = pGeo->pVtx[nV].p;	
		
		nT = pGeo->pTFce[n].c;
		nV = pGeo->pFce[n].c;
		pVtxR[nT].p = pGeo->pVtx[nV].p;
	}


다음은 ASE 파일의 텍스처 해석을 적용한 예제입니다.

LcAs13_Texture.zip





2 Rigid body Animation


애니메이션은 여러 분류가 있는데 그 중에서 지오메트리를 구성하고 있는 정점들 사이의
간격이 변하지 않는 강체(Rigid body) 애니메이션과 피부처럼 접히거나 늘어나는
스키닝(Skinning) 애니메이션으로 구분할 수 있습니다.

강체 애니메이션은 지오메트리의 모든 정점들에 대해서 같은 값을 가지는 행렬이 적용
됩니다. 스키닝은 지오메트리를 같이 구성하지만 행렬의 값은 개별적으로 설정할 수
있습니다. 과거에는 스키닝을 지원하는 그래픽 카드가 워낙 비싸서 게임에서는 강체
애니메이션을 주로 사용했습니다. 지금은 가격이 많이 내려 캐릭터에 대한 애니메이션은
대부분 스키닝을 이용합니다.

강체 애니메이션은 관절에서 폴리곤이 갈라지는 부분만 뺀다면 스키닝보다 구현과
속도 면에서 우수 합니다. 따라서 기계 팔과 움직임은 강체 애니메이션을 적용한다면
오히려 속도 향상의 이득이 있습니다.
이 장은 애니메이션 기초 과정이고 강체 애니메이션을 잘 이해한다면 스키닝을 배우는데
그리 큰 노력이 들지 않으므로 스키닝 대신 강체 애니메이션을 설명하겠습니다.





2.1 변환 행렬(TM: Transform Matrix)


애니메이션을 하려면 오브젝트를 구성하는 모든 객체들은 각자 자기의 모델 좌표계에 대한
행렬 값을 가지고 있어야 합니다. ASE파일의 *NODE_TM{} 안에 오브젝트의 월드 행렬에
대한 정보가 있습니다.
만약 다음과 같은 값으로 NODE_TM이 구성되어 있는 경우를 해석해 봅시다.

	*TM_ROW0 -0.1225	-0.3713		0.9204
	*TM_ROW1 -0.4945	-0.7812		-0.3810
	*TM_ROW2 0.8605		-0.5018		-0.0879
	*TM_ROW3 -0.0443	-0.2532		10.2929


이것은 DirectX의 D3DXMATRIX 행렬로 변환을 할 때 다음과 같이 TM_ROW1 부분과 TM_ROW2
부분을 교환해줍니다.
-0.1225 -0.3713 0.9204 0.8605 -0.5018 -0.0879 -0.4945 -0.7812 -0.3810 -0.0443 -0.2532 10.2929
다음으로 1 번째, 2 번째, 3 번째 열에서 2번째와, 3번째 열을 교환해 줍니다.

	-0.1225		0.9204		-0.3713
	0.8605		-0.087		-0.50189
	-0.4945		-0.381		-0.78120
	-0.0443		10.292		-0.25329


마지막으로 4열의 값을 0, 0, 0, 1 의 값으로 채우면 이 지오메트리의 월드 행렬을
얻게 됩니다.

	-0.1225		0.9204		-0.3713		0
	0.8605		-0.087		-0.50189	0
	-0.4945		-0.381		-0.78120	0
	-0.0443		10.292		-0.25329	1


이렇게 월드 행렬을 해석하고 나서 모델 좌표계의 지역 행렬 값을 구합니다. 지역 행렬은
자신의 월드 행렬 = 자신의 지역 행렬 * 부모의 월드 행렬을 통해 만들어짐으로 자신의
지역행렬은 다음과 같이 구합니다.

	자신의 지역 행렬 = (자신의 월드 행렬) * (부모의 월드 행렬의 역 행렬)


이것을 프로그램으로 구현하면 다음과 같습니다.

	D3DXMATRIX mtPrn = pGeoPrn->TmInf.mtW;
	D3DXMATRIX mtPrnI;
	D3DXMATRIX mtL;
	D3DXMatrixInverse(&mtPrnI, NULL, &mtPrn);
				
	pGeo->TmInf.mtL = pGeo->TmInf.mtW * mtPrnI;


ASE 파일의 지오메트리는 자료구조의 숲(Forest) 구조로 구성되어 있습니다. 따라서 파일에서
순차적으로 읽은 지오메트리의 순서는 곧 숲을 구성하는 나무(Tree) 자료 구조와 일치하므로
for 루프를 통해서 순서대로 지역 행렬을 만들면 됩니다.

만약 부모가 없다면 부모의 월드 행렬의 역 행렬 값은 4X4 항등 행렬로 설정해서 지역 행렬을
구하면 됩니다.

	// TmLocal = TmWorld * TmWorld(parent)-1

	for(i=0; i< m_nGeo; ++i)
	{
		AseGeo*	pGeoCur = &m_pGeo[i];
		AseGeo*	pGeoPrn = NULL;

		// Find Parent
		for(j=0; j< m_nGeo; ++j)
		{
			AseGeo*	pGeoT = &m_pGeo[j];

			if(0==_stricmp(pGeoCur->sNodePrn, pGeoT->sNodeCur))
			{
				pGeoPrn		= pGeoT;
				pGeoCur->pGeoPrn= pGeoT;
				break;
			}
		}

		if(pGeoPrn)
		{
			D3DXMATRIX mtPrn = pGeoPrn->TmInf.mtW;
			D3DXMATRIX mtPrnI;
			D3DXMATRIX mtL;
			D3DXMatrixInverse(&mtPrnI, NULL, &mtPrn);
			
			pGeoCur->TmInf.mtL = pGeoCur->TmInf.mtW * mtPrnI;
		}
		else
		{
			pGeoCur->TmInf.mtL = pGeoCur->TmInf.mtW;
		}
	}


이것으로 변환 행렬을 마치겠습니다. 좀 더 자세한 코드는 다음 예제를 참고 하기 바랍니다.

LcAs21_Tm.zip

LcAs22_TmLocal.zip





2.2 애니메이션


지역 행렬을 구했다면 애니메이션을 할 수 있습니다. 애니메이션을 위해서 ASE의 “SCENE”
부분을 해석해야 하는데 이 부분은 다음 예제를 참고 하기 바랍니다.

LcAs23_Scene.zip


강체의 운동은 크게 이동과 회전입니다. 이동 부분의 해석은 어려움이 없으나 회전 부분은
주의를 해야 합니다. 회전을 ASE 파일은 사원수(Quaternion) 을 이용합니다.
따라서 회전에서 사원수를 행렬로 변환해서 가지고 있거나 아니면 회전에 대한 값을 요구할
경우 매번 행렬을 만들어야 하는데 ASE 파일의 사원수는 누적 값이 아닌 이전 프레임과
상대적인 값으로 가지고 있습니다.
따라서 행렬을 만들 경우 사원수 누적 값을 만들어야 하는데 이렇게 하려면 행렬로 가지고
있는 것이 사용하기가 편리합니다. 사원수를 해석하는 방법은 다음과 같습니다.

	D3DXQUATERNION	q1;

	INT		nTrck;
	FLOAT	x=0.F, y=0.F, z=0.F, w=0.F;
	sscanf(sLine, "%*s %d %f %f %f %f", &nTrck, &x, &y, &z, &w);

	nTrck /= m_AseScene.nFrmT;

	q1.x = sinf(w/2.0f) * x;
	q1.z = sinf(w/2.0f) * y;
	q1.y = sinf(w/2.0f) * z;
	q1.w = cosf(w/2.0f);

	INT iSize = pGeo->vRot.size();

	if(0==iSize)
	{
		AseTrack	trck(nTrck, q1.x, q1.y, q1.z, q1.w);
		pGeo->vRot.push_back(trck);
	}
	else
	{
		D3DXQUATERNION	q2;
		D3DXQUATERNION	q3;

		AseTrack* ptrck = &pGeo->vRot[iSize-1];

		q2.x = ptrck->x;
		q2.y = ptrck->y;
		q2.z = ptrck->z;
		q2.w = ptrck->w;

		D3DXQuaternionMultiply(&q3, &q2, &q1);

		AseTrack	trck(nTrck, q3.x, q3.y, q3.z, q3.w);
		pGeo->vRot.push_back( trck );
	}


STL의 벡터를 사용하면 위와 같이 사원수 값을 누적시켜 저장 할 수 있습니다. 이렇게
누적된 사원수를 이용해서 다음과 같이 사원수의 선형 보간(Linear Interpolation)을
통해서 캐릭터의 애니메이션에 관한 회전 행렬을 만들어 내는데 선형 보간은 다음과
같은 형태의 공식을 이용합니다.

	Vout = (1 - w) * V0 + w * V1


선형 보간을 위해서 두 프레임 사이의 w 값을 계산합니다.

	w = (현재 프레임 - 현재 프레임보다 작거나 같은 프레임) /
	    (현재 프레임보다 작거나 같은 프레임 - 다음 프레임)


이것을 코드로 표현하면 다음과 같습니다.

	FLOAT	w = (nFrame - pGeo->vRot[i].nF)/(pGeo->vRot[i+1].nF - pGeo->vRot[i].nF);


DirectX의 사원수는 곱하기 연산자 ‘+’연산자, ‘*’연산자에 대한 다중 정의가 되어
있어서 다음과 같은 공식을 이용할 수 있습니다.

	q = q1 + w * (q2 - q1);


만약 함수를 사용하고 싶다면 다음과 같은 방법으로도 됩니다.

	D3DXQuaternionSlerp(& q, &q1, &q2, w);


이렇게 두 프레임 사이의 사원수를 얻었다면 마지막으로 행렬을 만듭니다. 행렬은 다음과
같은 함수를 통해서 구합니다.

	D3DXMatrixRotationQuaternion(&mtA, &q);


사원수를 이용해서 회전에 대한 행렬을 만들었다면 이동(Transverse)에 대한 행렬을 만들
차례입니다. 이동도 마찬가지로 보간을 이용하는데 앞서 사원수처럼 두 프레임 사이의 w 값을
구하고 나서 선형 보간을 한 다음 마지막에 앞서 구한 회전 행렬의 _41, _42, _43 에 위치를
설정합니다.

	FLOAT	w = (nFrame - pGeo->vTrs[i].nF)/(pGeo->vTrs[i+1].nF - pGeo->vTrs[i].nF);
	p = p1  + w * (p2-p1);
	mtA._41 = p.x;	mtA._42 = p.y;	mtA._43 = p.z;


전체 코드는 다음 예제를 참고 하기 바랍니다.

LcAs24_Track.zip


마지막 단계로 정점을 모델 좌표계로 바꾸는 일이 남아 있습니다. 월드좌표계의 정점 좌표 =
지역좌표계의 위치 * 월드 행렬 이므로 지역좌표계에서의 위치좌표는 다음과 같이 구해집니다.

	지역좌표계에서의 정점 위치 = (ASE에서 구한 정점 위치) * (지오메트리 월드 행렬의 역 행렬)


이것을 모든 지오메트리에 적용해야 하는데 다음과 같은 코드로 구현합니다.

	// v(local) = v(world)*TmWorld-1
	for(i=0; i< m_nGeo; ++i)
	{
		AseGeo*	pGeoCur = &m_pGeo[i];

		D3DXMATRIX mtWI;
		D3DXMatrixInverse(&mtWI, NULL, &pGeoCur->TmInf.mtW);

		INT	iNvtx = pGeoCur->iNumVtx;
		for(j=0; j< iNvtx; ++j)
		{
			D3DXVECTOR3 vcI = *((D3DXVECTOR3*)&(pGeoCur->pLstVtx[j]));
			D3DXVECTOR3 vcO;

			D3DXVec3TransformCoord(&vcO, &vcI, &mtWI);
			*((D3DXVECTOR3*)&(pGeoCur->pLstVtx[j])) = vcO;
		}
	}


다음 예제는 강체 애니메이션을 구현한 코드 입니다.

LcAs25_Animation1.zip

LcAs25_Animation2.zip


ASE 파일의 애니메이션에 대한 중요 부부의 설명은 모두 끝이 났습니다. 좀 더 확실한
학습은 스스로가 직접 하나하나 구현해 보는 것이 가장 좋습니다. 중요한 내용을 몇
가지 정리한다면 다음과 같습니다.

  • 위치를 x, z, y로 해석한다.
  • 위치에 대한 삼각형 인데스는 a, c, b 이다.
  • 텍스처 좌표는 u, 1.0f - v 이다.
  • 텍스처 좌표 인덱스는 a, c, b 이다.
  • 위치는 텍스처 좌표보다 같거나 작다.
  • 텍스처 좌표 인덱스와 위치 인덱스 수는 같다.
  • 정점을 구성하려면 k 번째 텍스처 좌표 인덱스에서 텍스처 버퍼 번호를 얻어 uv 좌표를 얻고,
    k 번째 정점 위치 인덱스에서 위치 버퍼 번호를 얻어 정점 위치를 얻는다.
  • TM은 2번째와 3번째 행을 교환한 다음, 2번째 열과 3번째 열 또한 교환한다.
  • ASE는 월드 행렬로 구성되어 있어서 자신의 지역 행렬 = (자신의 월드 행렬) * (부모의 월드 행렬의 역 행렬)로 구한다.
  • 지역좌표계 정점 위치 = (ASE에서 구한 위치) * (지오메트리 월드 행렬 역 행렬) 이다.
  • 애니메이션의 회전 값은 사원수로 저장되어 있으며 누적 값이 아닌 이전 프레임과 상대적인 값이다.
  • 회전은 사원수 보간을 이용한다.

    [출처] ASE Parsing|작성자 그라파