|
(2) https://youtu.be/DnoyhQ6OhMY (3) https://youtu.be/iIxwMVEHDpk
프레임워크(framework)란? 코드를 작성하기 위해 제공되는 프로그래밍 틀을 말한다. 오늘날 대부분의 프로그래밍은 프레임워크 안에서 프로그래밍을 한다고 해도 과언이 아니다. 예를 들면 닷넷 프레임워크, Win32 API 프로그래밍 구조, Direct3D DXUT등 수 많은 프레임워크가 존재한다. |
|
잘 만들어진 프레임워크는 팀 단위의 코드를 쉽게 통합할 수 있게 하므로 업그레이드와 디버깅이 원활하게 된다.
21.1 Direct3D Framework
20강까지 매번 Win32 API 구조를 일일이 코딩하면서 Direct3D를 설정하였다.
하지만 이와 같이 반복되는 코딩은 번거로울 뿐이다.
그래서 Direct3D에서는 Sample Browser에서 Win32 API와 Direct3D Framework를 자동으로 생성해 주는 EmptyProject라는
항목이 있다.
[그림 21-1]
이 프레임워크는 Direct3D에서 제공되는 여러 유틸리티 클래스를 쉽게 사용할 수 있도록 해주며 이를 통해 코드의
노고를 줄일 수 있다.
또한 이 유틸리티가 상당히 잘 되어 있어서 이를 상속하여 사용하거나 그대로 사용하는 경우가 많고 상용 게임에서도
이 유틸리티가 많이 애용되고 있다.
1) Direct3D Framework 생성과 파일
위 [그림 21-1]에서 Install Projects를 통해 Direct3D Framework를 생성하면 아래와 같은 내용으로 생성된다.
[그림 21-2]
Visual Studio 2010을 통해 솔루션 파일(xxx_2010.sln)을 로딩하면 아래와 같은 폴더를 볼 수 있다.
[그림 21-3]
DXUT 폴더에는 Direct3D 유틸리티 클래스들이 있으며 하단에 보면 EmptyProject.cpp 가 있다.
DXUT에는 다음과 같은 내용의 클래스가 있다.
파일명 | 간략한 설명 |
DXUT.cpp | Direct3D Framework 관련 대부분의 전역함수와 콜백함수들이 있다. |
DXUTcamera.cpp | 카메라 클래스가 있어서 툴과 게임에서 쉽게 카메라를 사용할 수 있게 해준다. 특히 CD3DArcBall 클래스는 툴을 만들 때 유용한 클래스이다. |
DXUTenum.cpp | Direct3D의 어뎁터와 디바이스와 모드에 대한 설정 함수가 있다. |
DXUTgui.cpp | Direct3D 다이얼로그, 리소스 관리자, 컨트롤(버튼, Static Text등)을 구현한 클래스가 있다. |
DXUTmisc.cpp | 타이머, 화면등에 관한 함수와 클래스가 있다. |
DXUTres.cpp | Direct3D에서 제공되는 리소스에 대한 아이디가 있다. |
DXUTsettingsdlg.cpp | Direct3D를 셋업하기 위한 Dialog GUI 클래스가 있다. |
SDKmesh.cpp | 메쉬를 다루기 위한 클래스가 있다. |
SDKmisc.cpp | 메쉬에 사용되는 텍스쳐와 텍스트 출력등에 관한 클래스가 있다. |
[표 21-1]
EmptyProject.cpp 파일의 내용 게임 로직을 실행하기 위한 윈도우 생성부터 윈도우 종료까지 대부분의 코드가
전부 되어 있다.
이 프레임워크를 분석하거나 읽을 때는 [표 21-1]의 내용을 분석하기 보다는 WinMain()이 있는 EmptyProject.cpp
파일의 내용을 먼저 보는 것이 핵심이다.
2) Direct3D framework 구조
이 구조를 이해하기 위해서는 게임 구조를 아는 것이 중요하다.
[그림 21-4] 게임 구조
[그림 21-4]의 게임 구조에 따라 초기화, 데이터 갱신, 렌더링, 해제등이 콜백함수로 호출된다.
콜백함수의 실체는 함수포인터로써 [그림 21-4]와 같은 게임 구조가 Direct3D 프레임워크 안에서 반복실행
되면서 적절한 때에 호출된다.
EmptyProject 파일에 있는 주요 콜백함수를 살펴보면 다음과 같다.
함수 | 설명 |
OnD3D9CreateDevice() | 이미 기본 설정은 되어 있지만 특정한 d3d device에 대한 설정과 백버퍼에 대한 설정을 하기 위한 함수이다. 또한 카메라와 프로젝션에 대한 설정도 한다. 여기는 d3d device에 한 번만 설정하면 되는 코드가 나열된다. |
OnD3D9ResetDevice() | device가 소실되었을 경우 device에 연관된 객체를 다시 셋업하기 위한 함수이다. |
OnFrameMove() | 데이터 갱신 함수이다. |
OnD3D9FrameRender() | 렌더링 함수이다. |
MsgProc() | 응용프로그램의 윈도우 메시지를 처리하는 함수이다. WndProc()와 같은 역할을 한다. |
OnD3D9LostDevice() | device에 연관된 객체를 해제하기 위한 함수로써 OnD3D9ResetDevice()에서 생성된 객체를 해제하는 함수이다. |
OnD3D9DestroyDevice() | OnD3D9CreateDevice()에서 생성된 객체를 해제하는 함수이다. |
wWinMain() | 윈도우 생성 및 콜백 함수의 셋업과 게임 로직을 반복 실행하는 게임프로그래밍의 전체 구조에 해당하는 코드가 있다. |
[표 21-2]
3) WinMain() 함수 살펴보기
WinMain() 함수를 보면 크기 셋업과 생성 및 무한루프 그리고 해제를 볼 수 있다.
셋업은 전부 콜백함수를 설정하는 것이며 생성은 윈도우와 Direct3D의 생성에 해당이 된다.
무한루프는 크게 데이터 갱신과 렌더링 나뉘며 해제는 Direct3D의 리소스와 Direct3D 그리고 윈도우 해제가
해당된다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | INT WINAPI wWinMain( HINSTANCE, HINSTANCE, LPWSTR, int ) { // Enable run-time memory check for debug builds. #if defined(DEBUG) | defined(_DEBUG) _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); #endif
// Set the callback functions DXUTSetCallbackD3D9DeviceAcceptable( IsD3D9DeviceAcceptable ); DXUTSetCallbackD3D9DeviceCreated( OnD3D9CreateDevice ); DXUTSetCallbackD3D9DeviceReset( OnD3D9ResetDevice ); DXUTSetCallbackD3D9FrameRender( OnD3D9FrameRender ); DXUTSetCallbackD3D9DeviceLost( OnD3D9LostDevice ); DXUTSetCallbackD3D9DeviceDestroyed( OnD3D9DestroyDevice ); DXUTSetCallbackDeviceChanging( ModifyDeviceSettings ); DXUTSetCallbackMsgProc( MsgProc ); DXUTSetCallbackFrameMove( OnFrameMove );
// TODO: Perform any application-level initialization here
// Initialize DXUT and create the desired Win32 window and Direct3D device for the application DXUTInit( true, true ); // Parse the command line and show msgboxes DXUTSetHotkeyHandling( true, true, true ); // handle the default hotkeys DXUTSetCursorSettings( true, true ); // Show the cursor and clip it when in full screen DXUTCreateWindow( L"EmptyProject" ); DXUTCreateDevice( true, 640, 480 );
// Start the render loop DXUTMainLoop();
// TODO: Perform any application-level cleanup here
return DXUTGetExitCode(); } |
[소스 21-1]
9 ~ 17행까지는 콜백함수의 셋업이고 이 부분이 MFC에서 툴 만들 때도 그대로 옮겨가게 됩니다.
23 ~ 28행까지는 윈도우 생성과 Direct3D의 생성입니다.
여기 과정에서 OnD3D9CreateDevice()가 호출되고 OnD3D9ResetDevice()가 호출됩니다.
호출을 확인하려면 이들 함수에 MessageBox()를 넣어 확인을 해보면 됩니다.
31행에서 OnFrameMove()와 OnD3D9FrameRender()를 반복적으로 호출합니다.
4) DXUT 셋업 함수
WinMain()에 사용된 DXUT 셋업 함수는 콜백함수를 셋업하는 함수가 대부분이다.
함수명 | 설명 |
DXUTSetCallbackDeviceCreated() | 윈도우 초기화와 Direct3D device가 생성되었을 때 호출되는 함수를 설정한다. |
DXUTSetCallbackDeviceReset() | ALT+ Tap키 또는 윈도우 크기 변환 등으로 화면 전환이 발생하여 device가 소실되었을 경우에 복원 또는 다시 재생성을 위해서 호출하는 함수이다. 특히 device를 통해 D3DPOOL_DEFAULT로 생성한 리소스는 device에서 다시 설정 및 생성해야 하므로 여기에는 재설정 코드가 오게 된다. |
DXUTSetCallbackDeviceLost() | IDirect3DDevice9::Reset()이 호출되기 전에 호출되며 Direct3D device가 소실상태에 들어가면 호출되는 콜백함수를 설정한다. |
DXUTSetCallbackDeviceDestroyed() | device를 재생성 하거나 응용프로그램이 종료할 때 호출되며 모든 device resource를 해제하게 된다. |
[표 21-3] d3d device 관련함수
함수명 | 설명 |
DXUTSetCallbackFrameMove() | 프레임 갱신 시작 할 때 호출되며 데이터 갱신에 관련된 콜백함수를 설정하게 된다. |
DXUTSetCallbackFrameRender() | 화면에 출력하는 콜백함수를 설정한다. |
[표 21-4] frame 관련함수
함수명 | 설명 |
DXUTSetCallbackMsgProc() | 윈도우 메시지가 발생될 때 호출되는 콜백함수를 설정한다. |
DXUTSetCallbackKeyboard() | 키보드와 관련된 메시지가 발생될 때 호출되는 콜백함수를 설정한다. |
DXUTSetCallbackMouse() | 마우스와 관련된 메시지가 발생될 때 호출되는 콜백함수를 설정한다. |
[표 21-4] 메시지 관련함수
5) 콜백함수 호출 순서
- 윈도우 생성부터 종료 전까지
IsDeviceAcceptable ➡ ModifyDeviceSettings ➡ OnCreateDevice ➡ xxOnResetDevice
➡ OnFrameMove ➡ OnFrameRender ➡ OnFrameMove ➡ OnFrameRender ➡ 반복
- 윈도우가 종료될 때
OnLostDevice ➡ OnDestroyDevice
- Device가 소실될 때 또는 윈도우 크기가 변경될 때
OnLostDevice ➡ xxOnResetDevice
21.2 DXUT 함수와 기능
DXUT 함수와 기능은 directx_sdk.chm의 Programming Guide에서 DXUT 항목을 보면 자세히 알 수 있다.
[그림 21-5]
1) 윈도우와 Direc3D 생성 및 초기화
Direct3D도 윈도우를 기반으로 하므로 윈도우 핸들이 있어야 한다. Win32 API를 이용한 윈도우의 생성은
상당히 많은 내용을 포함하고 있지만 아래의 DX 함수 호출만으로 간단한 윈도우가 생성된다.
DXUTInit( true, true ); // Parse the command line and show msgboxes DXUTSetHotkeyHandling( true, true, true ); // handle the default hotkeys DXUTSetCursorSettings( true, true ); // Show the cursor and clip it when in full screen DXUTCreateWindow( L"EmptyProject" ); |
- DXUTInit()
HRESULT WINAPI DXUTInit( bool bParseCommandLine = true, bool bShowMsgBoxxxOnError = true, __in_opt WCHAR* strExtraCommandLineParams = NULL, bool bThreadSafeDXUT = false ); |
bParseCommandLine은 DXUT 콘솔명령어를 사용할 수 있는 매개변수이다.
bShowMsgBoxxxOnError는 에러 발생시 DXUT가 Error MessageBox를 출력하도록 할 것인지에 대한 것이며
기본 값은 true이다.
strExtraCommandLineParams은 DXUT 추가 콘솔명령을 사용할 수 있게 할 것인가에 대한 매개변수이다.
기본 값은 NULL이다.
bThreadSafeDXUT는 DXUT의 상태를 조사하거나 수정할 때 사용되는 매개변수이다
기본 값은 false이다.
- DXUTSetHotkeyHandling()
화면전환과 응용프로그램의 종료등을 단축키로 할 수 있도록 할 것인지를 설정함수이다.
void DXUTSetHotkeyHandling( bool bAltEnterToToggleFullscreen, bool bEscapeToQuit, bool bPauseToToggleTimePause ) |
bAltEnterToToggleFullscreen은 ALT+ Enter키를 허용할 것인가를 설정하는 매개변수로 기본 값은 true이다.
bEscapeToQuit은 escape 키 입력시 응용프로그램 종료하게 할 것인지에 대한 매개변수로 기본 값은 true이다.
bPauseToToggleTimePause은 타이머를 중지할 수 있도록 할 것인가에 대한 매개변수로 기본 값은 true이다.
테스트 외에 실전에서는 DXUTSetHotkeyHandling(true, false, false)로 설정한다.
Hot Key | 역할 |
ALT+ENTER | 전체 화면과 윈도우 화면의 전환 |
ESC | 응용프로그램의 종료 |
F3 | HAL과 Reference device의 전환 |
F8 | Wireframe과 Solidframe 전환 |
Pause Break | 응용프로그램 일시 정지 |
[표 21-5] DXUT 단축키
- DXUTSetCursorSettings()
전체 화면에서 커서의 사용을 설정하는 함수이다.
HRESULT DXUTSetCursorSettings( bool bShowCursorWhenFullScreen, bool bClipCursorWhenFullScreen ) |
bShowCursorWhenFullScreen은 전체화면에 커서를 나타낼 것인지를 설정하는 매개변수이다.
bClipCursorWhenFullScreen은 커서가 전체화면을 벗어난 만큼 잘려서 출력되도록 하는 매개변수이다.
- DXUTCreateWindow()
윈도우를 생성하는 함수이다.
HRESULT DXUTCreateWindow( CONST const WCHAR * strWindowTitle, HINSTANCE hInstance, HICON hIcon, HMENU hMenu, INT x, INT y ) |
기본 값은 DXUT.h에 아래와 같인 설정되어 있다.
HRESULT WINAPI DXUTCreateWindow( const WCHAR* strWindowTitle = L"Direct3D Window", HINSTANCE hInstance = NULL, HICON hIcon = NULL, HMENU hMenu = NULL, int x = CW_USEDEFAULT, int y = CW_USEDEFAULT ); |
- DXUTCreateDevice()
Direct3D 9를 생성하는 함수이다.
HRESULT DXUTCreateDevice( bool bWindowed, INT nSuggestedWidth, INT nSuggestedHeight ) |
bWindowed는 윈도우 모드 설정 매개변수로 false이면 응용프로그램이 실행될 때 전체화면으로 시작된다.
nSuggestedWidth, nSuggestedHeight는 백버퍼의 가로세로 길이로써 이 길이만큼 렌더링되는 클라이언트
영역이 결정된다.
2) 게임 루프
게임 루프는 기본적으로 데이터 갱신, 렌더링, 윈도우 메시지 처리로 나눌 수 있으며
DXUTMainLoop()가 이와 같은 역할을 한다.
이 함수는 내부적으로 아래의 3개의 함수를 실행한다.
DXUTSetCallbackMsgProc( MsgProc ); DXUTSetCallbackFrameRender( OnFrameRender ); DXUTSetCallbackFrameMove( OnFrameMove ); |
21.3 윈도우 모드와 전체 화면 모드의 전환
Windowed Mode는 주로 디버깅 하기가 용이하며 Full Screen Mode는 실제 게임을 실행할 경우에 사용 한다.
실제 게임 제작에서는 이 두 모드를 번갈아 전환하면서 프로그래밍을 하며 Mode 전환 함수를 아래와 같이
DXUT에서 제공된다.
HRESULT DXUTToggleFullscreen() |
DXUTToggleFullscreen()함수를 호출하면 내부적으로 다음가 같이 차례대로 호출한 후에 다시 OnFrameMove()와
OnFrameRender()를 반복하여 렌더링을 한다.
ModifyDeviceSettings() ➡ OnD3D9LostDevice() ➡ OnD3D9ResetDevice() ➡ Toggle 실행 ➡ OnD3D9FrameMove()
➡ OnD3D9FrameRender() ➡ ... 반복
( 과제 )
1) Direct3D Framework 환경 안에서 Teapot를 출력하는 프로그램을 작성해 보자.
단, 소스는 9강의 [소스 9-1]을 프레임워크로 옮기도록 하며 카메라만 아래와 같이 설정한다.
g_vEye.x = 0.0f;
g_vEye.y = 1.0f;
g_vEye.z = -10.0f;