3ds 파일을 읽어들이는 것은 쉽고, 재미있는 일이다. 오토데스크(Autodesk)에서 공식적으로 파일 포맷을 공개하지 않았기 때문에 비공식적으로 바이너리를 분석할 수 밖에 없지만, 이것은 큰 문제는 아니다. 이미 3ds 바이너리 포맷을 잘 분석한 문서가 존재하며, 간혹 이 문서에 나타나지 않는 데이터 조각들이 있긴 하지만 그다지 필요한 정보가 아니기 때문이다.
3ds 파일 포맷은, 데이터 조각들의 모음으로 구성되어 있고, 문서에서는 보통 이들을 chunk라고 표현한다(여기서는 그냥 데이터 조각이라고 부르기로 한다). 데이터 조각은 2 바이트 id와, 데이터 조각 자체의 크기를 나타내는 4 바이트 정수형을 합친, 6 바이트 헤더를 가진다. 데이터 조각들은 헤더 외에 데이터 종류에 따라 추가적인 데이터를 가질 수 있다. 데이터 조각들은 그 자체 데이터를 가질 수도 있고, 밑의 부분 집합으로 가지는 데이터 조각들의 데이터를 연속적으로 가질 수도 있다.
3ds 파일을 읽어들이는 로더를 구현한다면, 가장 먼저 이 데이터 조각를 표현할 구조체를 정의해야 한다. 그 다음, 3ds 파일에서 원하는 정보를 읽어들인뒤 렌더링을 수행할 클라이언트가 사용할 구조체 정보를 정의해야 하다. 이것을 모델이라고 하자. 즉, 로더의 작업 순서는 다음과 같다.
1. 데이터 조각의 헤더를 읽고 필요한 정보인지 그렇지 않을 정보인지 판단한다.
2. 필요한 정보라면, 추가적으로 데이터 조각의 하위 데이터를 읽어서 모델에 저장한다. 필요하지 않은 정보라면 4 바이트로 저장된, 데이터 조각의 크기만큼 건너 뛴다.
3. 위 과정을 파일의 끝을 만날 때까지 반복적으로 진행한다.
4. 클라이언트는 로더에 의해 저장된 모델을 읽어들여, 원하는대로 데이터를 렌더링한다.
여기서, 로더의 작업을 처리하는 구성은 다음과 같은 2가지로 생각해볼 수 있다. 먼저, 가장 단순한 방법으로는 데이터 조각의 헤더를 읽고, 그 id 값을 switch 문을 사용하여 case 문으로 쪼갠 뒤 적절한 처리를 할 수 있다. 그게 아니라면 팩토리 메서드와 같은 방법을 사용하여, switch - case 문을 대체할 수 있다. 어느 쪽을 선택하든 그것은 자유다. 그러나, switch - case 방식을 사용한다면 코드를 단순하게 구성할 수 있지만, 가공해야 하는 정보가 많아질수록 하나의 함수가 기하급수적으로 커지는 단점이 있다. 팩토리 메서드와 같은 기법을 사용한다면 함수의 크기를 줄일 수 있고 종속성 문제를 떨어뜨릴 수 있지만, 사용하지 않는 개체나 POD 타입의 데이터 조각이라도 모두 공통적인 처리 함수를 지정해야 한다.
가장 좋은 방법은, 함수자와 적절한 index - mapping 메커니즘을 구현하는 것인데, 처리해야 하는 데이터 조각이 몇개 되지 않는다면 swtich - case 문을 사용했을 때와 비교해 이득이 거의 없다. 그러나, 복잡한 데이터 조각을 처리해야 한다면, 함수자(functor)를 통해 처리 함수를 등록하고, 이 함수를 해당 파일에 분산시켜 종속성 문제를 떨어뜨릴 수 있기 때문에, 구현 정도가 커질수록 훨씬 나은 방법이다. 함수자에 익숙해야 하고, 필요한 함수자가 없다면(표준 C++ 함수자로 부족하다면) 직접 구현해야 하는 부담은 있지만, 할 수 있다면 가장 나은 방법이다. 여튼, 필요에 따라 적당한 방법을 선택하면 된다.
로더를 구성하는데 필요한 함수들을 살펴보면, 데이터 조각의 헤더를 읽어들이는 함수, 그리고 데이터 조각의 크기만큼 건너 뛰는 함수, 읽어들일 크기를 지정하면 그 크기만큼 데이터를 읽어들이는 함수 따위이다. 이들을 ChunkHelpers와 같은 클래스나 이름공간(namespace)에 모아두면 좋을 것이다. 그리고, 어떤 방법을 사용하든 이들 함수를 이용해 클라이언트가 사용할 모델에 원하는 데이터를 넣으면 된다. 어떤 데이터가 필요한지는 각 상황에 따라 다르다. 아래의 참고 사이트의 3ds 로더 구현을 참조하라.
3ds 파일 포맷을 분석한 문서로는, 다음 문서가 비공식적인 표준 같다.
이 문서에는 3ds material 블럭에 관한 세부적인 항목들은 나와 있지 않은데, 다음 사이트를 참조하면 된다. 이 사이트는 각 3D 모델링 툴의 파일 포맷들에 대해 잘 소개하고 있다 : http://www.martinreddy.net/gfx/3d/MLI.spec
한글로 된 정보를 찾고 있다면, 다음 사이트를 참고하라. 정보의 세세함은 부족하지만, 충분히 도움이 될만하다 : http://misofruit.co.kr/seojewoo/programming/opengl/opengl-9.htm
DirectX를 사용한다면 이펙트 프레임웍에서 3ds 파일을 바로 읽어들일 수 있다. 또, 각종 모델링 툴에서 각 포맷에 대한 플러그인들을 제공하기 때문에 굳이 3ds 바이너리를 직접 읽기 위해 로더와 같은 것을 작성해야 할 일은 드물 것이다. 그러나, OpenGL의 경우 이펙트 프레임웍과 같은 것을 제공하지 않기 때문에, 사용자의 클라이언트 코드에서 이들 파일을 직접 사용하고자 한다면 이런 수고를 직접 해줘야 한다. 직접적인 필요에 의해서가 아니더라도, 손수 파일을 읽어들이면서 해당 요소들이 어떻게 사용되는가를 파악해본다면, 3D 그래픽스에 대한 이해를 훨씬 더 넓힐 수 있을 것이다.
다음은 버텍스, 페이스 정보만 읽어들여 와이어프레임 형태로만 렌더링한 것이다.
Reference
http://www.spacesimulator.net/tut4_3dsloader.html
3ds 파일 포맷은, 데이터 조각들의 모음으로 구성되어 있고, 문서에서는 보통 이들을 chunk라고 표현한다(여기서는 그냥 데이터 조각이라고 부르기로 한다). 데이터 조각은 2 바이트 id와, 데이터 조각 자체의 크기를 나타내는 4 바이트 정수형을 합친, 6 바이트 헤더를 가진다. 데이터 조각들은 헤더 외에 데이터 종류에 따라 추가적인 데이터를 가질 수 있다. 데이터 조각들은 그 자체 데이터를 가질 수도 있고, 밑의 부분 집합으로 가지는 데이터 조각들의 데이터를 연속적으로 가질 수도 있다.
3ds 파일을 읽어들이는 로더를 구현한다면, 가장 먼저 이 데이터 조각를 표현할 구조체를 정의해야 한다. 그 다음, 3ds 파일에서 원하는 정보를 읽어들인뒤 렌더링을 수행할 클라이언트가 사용할 구조체 정보를 정의해야 하다. 이것을 모델이라고 하자. 즉, 로더의 작업 순서는 다음과 같다.
1. 데이터 조각의 헤더를 읽고 필요한 정보인지 그렇지 않을 정보인지 판단한다.
2. 필요한 정보라면, 추가적으로 데이터 조각의 하위 데이터를 읽어서 모델에 저장한다. 필요하지 않은 정보라면 4 바이트로 저장된, 데이터 조각의 크기만큼 건너 뛴다.
3. 위 과정을 파일의 끝을 만날 때까지 반복적으로 진행한다.
4. 클라이언트는 로더에 의해 저장된 모델을 읽어들여, 원하는대로 데이터를 렌더링한다.
여기서, 로더의 작업을 처리하는 구성은 다음과 같은 2가지로 생각해볼 수 있다. 먼저, 가장 단순한 방법으로는 데이터 조각의 헤더를 읽고, 그 id 값을 switch 문을 사용하여 case 문으로 쪼갠 뒤 적절한 처리를 할 수 있다. 그게 아니라면 팩토리 메서드와 같은 방법을 사용하여, switch - case 문을 대체할 수 있다. 어느 쪽을 선택하든 그것은 자유다. 그러나, switch - case 방식을 사용한다면 코드를 단순하게 구성할 수 있지만, 가공해야 하는 정보가 많아질수록 하나의 함수가 기하급수적으로 커지는 단점이 있다. 팩토리 메서드와 같은 기법을 사용한다면 함수의 크기를 줄일 수 있고 종속성 문제를 떨어뜨릴 수 있지만, 사용하지 않는 개체나 POD 타입의 데이터 조각이라도 모두 공통적인 처리 함수를 지정해야 한다.
가장 좋은 방법은, 함수자와 적절한 index - mapping 메커니즘을 구현하는 것인데, 처리해야 하는 데이터 조각이 몇개 되지 않는다면 swtich - case 문을 사용했을 때와 비교해 이득이 거의 없다. 그러나, 복잡한 데이터 조각을 처리해야 한다면, 함수자(functor)를 통해 처리 함수를 등록하고, 이 함수를 해당 파일에 분산시켜 종속성 문제를 떨어뜨릴 수 있기 때문에, 구현 정도가 커질수록 훨씬 나은 방법이다. 함수자에 익숙해야 하고, 필요한 함수자가 없다면(표준 C++ 함수자로 부족하다면) 직접 구현해야 하는 부담은 있지만, 할 수 있다면 가장 나은 방법이다. 여튼, 필요에 따라 적당한 방법을 선택하면 된다.
로더를 구성하는데 필요한 함수들을 살펴보면, 데이터 조각의 헤더를 읽어들이는 함수, 그리고 데이터 조각의 크기만큼 건너 뛰는 함수, 읽어들일 크기를 지정하면 그 크기만큼 데이터를 읽어들이는 함수 따위이다. 이들을 ChunkHelpers와 같은 클래스나 이름공간(namespace)에 모아두면 좋을 것이다. 그리고, 어떤 방법을 사용하든 이들 함수를 이용해 클라이언트가 사용할 모델에 원하는 데이터를 넣으면 된다. 어떤 데이터가 필요한지는 각 상황에 따라 다르다. 아래의 참고 사이트의 3ds 로더 구현을 참조하라.
3ds 파일 포맷을 분석한 문서로는, 다음 문서가 비공식적인 표준 같다.
이 문서에는 3ds material 블럭에 관한 세부적인 항목들은 나와 있지 않은데, 다음 사이트를 참조하면 된다. 이 사이트는 각 3D 모델링 툴의 파일 포맷들에 대해 잘 소개하고 있다 : http://www.martinreddy.net/gfx/3d/MLI.spec
한글로 된 정보를 찾고 있다면, 다음 사이트를 참고하라. 정보의 세세함은 부족하지만, 충분히 도움이 될만하다 : http://misofruit.co.kr/seojewoo/programming/opengl/opengl-9.htm
DirectX를 사용한다면 이펙트 프레임웍에서 3ds 파일을 바로 읽어들일 수 있다. 또, 각종 모델링 툴에서 각 포맷에 대한 플러그인들을 제공하기 때문에 굳이 3ds 바이너리를 직접 읽기 위해 로더와 같은 것을 작성해야 할 일은 드물 것이다. 그러나, OpenGL의 경우 이펙트 프레임웍과 같은 것을 제공하지 않기 때문에, 사용자의 클라이언트 코드에서 이들 파일을 직접 사용하고자 한다면 이런 수고를 직접 해줘야 한다. 직접적인 필요에 의해서가 아니더라도, 손수 파일을 읽어들이면서 해당 요소들이 어떻게 사용되는가를 파악해본다면, 3D 그래픽스에 대한 이해를 훨씬 더 넓힐 수 있을 것이다.
다음은 버텍스, 페이스 정보만 읽어들여 와이어프레임 형태로만 렌더링한 것이다.
Reference
http://www.spacesimulator.net/tut4_3dsloader.html