#include "d3dUtility.h"
// Direct3D 초기화 메서드
bool d3d::InitD3D(
HINSTANCE hInstance,
int width, int height,
bool windowed,
D3DDEVTYPE deviceType,
IDirect3DDevice9** device)
{
// 메인 애플리케이션 생성
WNDCLASS wc;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = (WNDPROC)d3d::WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(0, IDI_APPLICATION);
wc.hCursor = LoadCursor(0, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = 0;
wc.lpszClassName = "Direct3D9App";
if( !RegisterClass(&wc) )
{
::MessageBox(0, "RegisterClass() - FAILED", 0, 0);
return false;
}
HWND hwnd = 0;
hwnd = ::CreateWindow("Direct3D9App", "Direct3D9App",
WS_EX_TOPMOST,
0, 0, width, height,
0 /*parent hwnd*/, 0 /* menu */, hInstance, 0 /*extra*/);
if( !hwnd )
{
::MessageBox(0, "CreateWindow() - FAILED", 0, 0);
return false;
}
::ShowWindow(hwnd, SW_SHOW);
::UpdateWindow(hwnd);
//
// Direct3D 초기화하기
//
HRESULT hr = 0;
// 1. IDirect3D9 인터페이스를 얻는다.
// Direct3DCreate9의 단일인자에는 항상 D3D_SDK_VERSION을 전달해야 한다.
// 애플리케이션이 올바른 헤더 파일로 만들어졌음을 확인하기 위한 것으로 함수 실패시 null 포인터 리턴.
// IDirect3D9 객체는 장치 검증과 IDirect3DDevice9 객체 생성의 두 가지 용도로 이용됨.
// 장치 검증이란 기능, 디스플레이 모드, 포맷 등과 같은 시스템의 그래픽 장치가 제공하는 특성 정보를 얻는 과정
IDirect3D9* d3d9 = 0;
d3d9 = Direct3DCreate9(D3D_SDK_VERSION);
if( !d3d9 )
{
::MessageBox(0, "Direct3DCreate9() - FAILED", 0, 0);
return false;
}
// 2. 하드웨어 버텍스 프로세싱 확인하기
// 기본 디스플레이 어댑터와 대응되는 IDirect3DDevice9 객체를 생성할 때는 반드시 원하는 버텍스 프로세싱
// 모드를 지정해야 한다. 이때, 모든 카드가 하드웨어 버텍스 프로세싱을 이용하는지 보장할 수 없으므로
// 지원 여부를 확인해야 한다.
// 어댑터의 특성에 따라 D3DCAPS9 인스턴스를 초기화한다.
D3DCAPS9 caps;
// 첫번째 인자 : 특성을 얻고자 하는 물리 디스플레이 어댑터 지정.
// 두번째 인자 : 이용할 장치 타입을 지정 (하드웨어 장치(D3DDEVTYPE_HAL), 소프트웨어 장치(D3DDEVTYPE_REF))
// 세번째 인자 : 초기화된 특성 구조체를 리턴.
d3d9->GetDeviceCaps(D3DADAPTER_DEFAULT, deviceType, &caps);
// 이후에 IDirect3DDevice9 객체를 만들 때 버텍스 프로세싱의 타입을 지정하기 위해 저장할 곳 : vp
int vp = 0;
// 하드웨어 버텍스 프로세싱을 이용할 수 있는지 확인
if( caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT )
// 지원될 경우 vp에 하드웨어 버텍스 프로세싱을 저장
vp = D3DCREATE_HARDWARE_VERTEXPROCESSING;
else
// 지원 안 될 경우 vp에 소프트웨어 버텍스 프로세싱을 저장
vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
// 3. D3DPRESENT_PARAMETERS 구조체 채우기
// D3DPRESENT_PARAMETERS 구조체를 만들고자 하는 IDirect3DDevice9 객체의 성격을 결정하는데 사용
D3DPRESENT_PARAMETERS d3dpp;
// 픽셀 단위의 후면 버퍼 너비
d3dpp.BackBufferWidth = width;
// 픽셀 단위의 후면 버퍼 높이
d3dpp.BackBufferHeight = height;
// 후면 버퍼의 픽셀 포맷 (D3DFMT_A8R8G8B8은 32-비트 픽셀 포맷)
d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8;
// 이용할 후면 버퍼의 수. 보통 1을 지정하고 한개의 후면 버퍼를 이용한다.
d3dpp.BackBufferCount = 1;
// 후면 버퍼에 이용할 멀티 샘플링의 타입 (SDK 참조)
d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE;
// 멀티 샘플링의 레빌 (SDK 참조)
d3dpp.MultiSampleQuality = 0;
// 플리핑 체인의 버퍼가 교환되는 방법을 지정하는 D3DSWAPEFFECT 열거형 멤버.
// D3DSWAPEFFECT_DISCARD가 가장 효과적이다.
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
// 서비스와 연결된 윈도우 핸들. 드로잉 대상이 될 애플리케이션 윈도우를 지정한다.
d3dpp.hDeviceWindow = hwnd;
// 윈도우 모드로 실행중일 때는 true, 전체화면 모드로 실행중일 때는 false를 지정
d3dpp.Windowed = false;
// Direct3D가 자동으로 깊이/스텐실 버퍼를 만들고 관리하길 원한다면 true
d3dpp.EnableAutoDepthStencil = true;
// 깊이/스텐실 버퍼의 포맷 (24버티 깊이 버퍼와 스텐실 버퍼로 8 비트 예약 : D3DFMT_D24S8)
d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;
// 몇 가지 부가적인 특성들. 0(플래그 없음), SDK 참조
d3dpp.Flags = 0;
// 재생율을 지정 (디폴트 : D3DPRESENT_RATE_DEFAULT)
d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
// D3DPRESENT 집합의 멤버. 전체 간격 리스트는 SDK 참조
// 즉시 시연 (D3DPRESENT_INTERVAL_IMMEDIATE)
// 적절한 시연 간격을 Direct3D가 결정 (D3DPRESENT_INTERVAL_DEFAULT)
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
// 4. IDirect3DDevice9 인터페이스 만들기
// 첫번째 인자 : 만들어질 IDirect3DDevice9 객체와 대응될 물리 디스플레이 어댑터 지정
// 두번째 인자 : 이용할 장치 타입 (D3DDEVTYPE_HAL or D3DDEVTYPE_REF)
// 세번째 인자 : 장치와 연결될 윈도우 핸들
// 네번째 인자 : 지원하는 버텍스 프로세싱
// 다섯번째 인자 : 장치 특성의 일부를 정의하는 초기화된 D3DPRESENT_PARAMETERS 인스턴스를 지정
// 여섯번째 인자 : 생성된 장치를 리턴
hr = d3d9->CreateDevice(
D3DADAPTER_DEFAULT, // primary adapter
deviceType, // device type
hwnd, // window associated with device
vp, // vertex processing
&d3dpp, // present parameters
device); // return created device
if( FAILED(hr) )
{
// try again using a 16-bit depth buffer
d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
hr = d3d9->CreateDevice(
D3DADAPTER_DEFAULT,
deviceType,
hwnd,
vp,
&d3dpp,
device);
if( FAILED(hr) )
{
d3d9->Release(); // done with d3d9 object
::MessageBox(0, "CreateDevice() - FAILED", 0, 0);
return false;
}
}
d3d9->Release(); // done with d3d9 object
return true;
}
// Display 함수가 될 함수의 포인터를 받아서, 메시지 루프 함수가 유휴 상태에서 디스플레이 함수를 호출.
int d3d::EnterMsgLoop(bool (*ptr_display)(float timeDelta))
{
MSG msg;
::ZeroMemory(&msg, sizeof(MSG));
static float lastTime = (float)timeGetTime();
while(msg.message != WM_QUIT)
{
if(::PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
{
::TranslateMessage(&msg);
::DispatchMessageA(&msg);
}
else
{
float currTime=(float)timeGetTime();
float timeDelta = (currTime-lastTime)*0.001f;
ptr_display(timeDelta);
lastTime = currTime;
}
}
return msg.wParam;
}