프레임워크 작업은 네 가지 항목으로 시작됩니다. 진입 점을 처리하는 WinMain 함수가 있습니다. 또한 WinMain 함수 내에서 호출 될 전체 응용 프로그램을 캡슐화 하는 시스템 클래스가 있습니다. 시스템 클래스 안에는 사용자 입력을 처리하기 위한 입력 클래스와 DirectX 글래픽 코드를 처리하기 위한 그래픽 클래스가 있습니다.
전방선언 사용의 이점과 빌드 시간 감축을 위해 미리 컴파일된 헤더(stdafx.h)의 이점을 사용하기 위해 미리 사용될 헤더 파일을 따로 "DxDefine.h"으로 작성해서 만들었습니다.
stdafx.h
// stdafx.h : 자주 사용하지만 자주 변경되지는 않는
// 표준 시스템 포함 파일 또는 프로젝트 관련 포함 파일이
// 들어 있는 포함 파일입니다.
//
#pragma once
#include "targetver.h"
#define WIN32_LEAN_AND_MEAN // 거의 사용되지 않는 내용은 Windows 헤더에서 제외합니다.
// Windows 헤더 파일:
#include <windows.h>
// C 런타임 헤더 파일입니다.
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <tchar.h>
// TODO: 프로그램에 필요한 추가 헤더는 여기에서 참조합니다.
#include "DxDefine.h"
#incdlue "DxDefine.h" 를 추가합니다
DxDefine.h
#pragma once
아직 DxDefine.h 이 헤더 파일에 아무런 내용이 없지만, DirectX sdk를 사용하기 위한 헤더 및 라이브러리 파일 코드를 넣을 것입니다.
다음은 main.cpp 파일의 WinMain 함수를 살펴 보겠습니다.
WinMain
// Dx11Demo_02.cpp: 응용 프로그램의 진입점을 정의합니다.
//
#include "stdafx.h"
#include "SystemClass.h"
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
// System 객체 생성
SystemClass* System = new SystemClass;
if (!System)
{
return -1;
}
// System 객체 초기화 및 실행
if (System->Initialize())
{
System->Run();
}
// System 객체 종료 및 메모리 반환
System->Shutdown();
delete System;
System = nullptr;
return 0;
WinMain함수는 매우 간단하게 유지했습니다. 시스템 클래스 생성, 초기화, 실행, 종료, 반환 순서로 구동됩니다. 따라서 우리는 매우 단순하게 유지하고 전체 애플리케이션을 시스템 클래스 내에 캡슐화 했습니다.
시스템 클래스 헤더파일을 살펴보겠습니다.
SystemCalss
#pragma once
class InputClass;
class GraphicsClass;
class SystemClass
{
public:
SystemClass();
SystemClass(const SystemClass&);
~SystemClass();
bool Initialize();
void Shutdown();
void Run();
LRESULT CALLBACK MessageHandler(HWND, UINT, WPARAM, LPARAM);
private:
bool Frame();
void InitializeWindows(int&, int&);
void ShutdownWindows();
private:
LPCWSTR _applicationName;
HINSTANCE _hinstance;
HWND _hwnd;
InputClass* _input = nullptr;
GraphicsClass* _graphics = nullptr;
};
static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
static SystemClass* ApplicationHandle = 0;
클래스의 정의는 매우 간단합니다. 여기에 정의 된 WinMain에서 호출 된 Initialize, Shitdown 및 Run 함수가 표시됩니다. 또한 이러한 함수 내에서 호출되는 일부 개인 함수가 있습니다. 우리는 클래스에 MessageHandler함수를 두어 응용 프로그램이 실행되는 동안 응용 프로그램에 전송 될 Windows 시스템 메시지를 처리합니다. 마지막으로 우리는 그패칙과 입력을 처리 할 두 객체에 대한 포인터가 될 m_Input과 m_Graphics라는 클래스 변수를 가지고 있습니다. 또한 전방 선언을 통해 빠른 컴파일을 작성할 수 있도록 하였습니다.
간단히 설명하자만 SystemCalss 는 WinAPI 윈도우 프로그래밍을 클래스화 한 것입니다
#include "stdafx.h"
#include "inputclass.h"
#include "graphicsclass.h"
#include "systemclass.h"
SystemClass::SystemClass()
{
}
SystemClass::SystemClass(const SystemClass& other)
{
}
SystemClass::~SystemClass()
{
}
bool SystemClass::Initialize()
{
// 윈도우 창 가로, 세로 넓이 변수 초기화
int screenWidth = 0;
int screenHeight = 0;
// 윈도우 생성 초기화
InitializeWindows(screenWidth, screenHeight);
// m_Input 객체 생성. 이 클래스는 추후 사용자의 키보드 입력 처리에 사용됩니다.
_input = new InputClass;
if(!_input)
{
return false;
}
// m_Input 객체 초기화
_input->Initialize();
// m_Graphics 객체 생성. 그래픽 랜더링을 처리하기 위한 객체입니다.
_graphics = new GraphicsClass;
if(!_graphics)
{
return false;
}
// m_Graphics 객체 초기화.
return _graphics->Initialize(screenWidth, screenHeight, _hwnd);
}
void SystemClass::Shutdown()
{
// m_Graphics 객체 반환
if(_graphics)
{
_graphics->Shutdown();
delete _graphics;
_graphics = 0;
}
// m_Input 객체 반환
if(_input)
{
delete _input;
_input = 0;
}
// Window 종료 처리
ShutdownWindows();
}
void SystemClass::Run()
{
// 메시지 구조체 생성 및 초기화
MSG msg;
ZeroMemory(&msg, sizeof(MSG));
// 사용자로부터 종료 메시지를 받을때까지 메시지루프를 돕니다
while(true)
{
// 윈도우 메시지를 처리합니다
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
// 종료 메시지를 받을 경우 메시지 루프를 탈출합니다
if (msg.message == WM_QUIT)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
// 그 외에는 Frame 함수를 처리합니다.
if (!Frame())
break;
}
}
}
bool SystemClass::Frame()
{
// ESC 키 감지 및 종료 여부를 처리합니다
if(_input->IsKeyDown(VK_ESCAPE))
{
return false;
}
// 그래픽 객체의 Frame을 처리합니다
return _graphics->Frame();
}
LRESULT CALLBACK SystemClass::MessageHandler(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam)
{
switch(umsg)
{
// 키보드가 눌러졌는가 처리
case WM_KEYDOWN:
{
// 키 눌림 flag를 m_Input 객체에 처리하도록 합니다
_input->KeyDown((unsigned int)wparam);
return 0;
}
// 키보드가 떨어졌는가 처리
case WM_KEYUP:
{
// 키 해제 flag를 m_Input 객체에 처리하도록 합니다.
_input->KeyUp((unsigned int)wparam);
return 0;
}
// 그 외의 모든 메시지들은 기본 메시지 처리로 넘깁니다.
default:
{
return DefWindowProc(hwnd, umsg, wparam, lparam);
}
}
}
void SystemClass::InitializeWindows(int& screenWidth, int& screenHeight)
{
// 외부 포인터를 이 객체로 지정합니다
ApplicationHandle = this;
// 이 프로그램의 인스턴스를 가져옵니다
_hinstance = GetModuleHandle(NULL);
// 프로그램 이름을 지정합니다
_applicationName = L"Dx11Demo_02";
// windows 클래스를 아래와 같이 설정합니다.
WNDCLASSEX wc;
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = _hinstance;
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
wc.hIconSm = wc.hIcon;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
wc.lpszMenuName = NULL;
wc.lpszClassName = _applicationName;
wc.cbSize = sizeof(WNDCLASSEX);
// windows class를 등록합니다
RegisterClassEx(&wc);
// 모니터 화면의 해상도를 읽어옵니다
screenWidth = GetSystemMetrics(SM_CXSCREEN);
screenHeight = GetSystemMetrics(SM_CYSCREEN);
int posX = 0;
int posY = 0;
// FULL_SCREEN 변수 값에 따라 화면을 설정합니다.
if(FULL_SCREEN)
{
// 풀스크린 모드로 지정했다면 모니터 화면 해상도를 데스크톱 해상도로 지정하고 색상을 32bit로 지정합니다.
DEVMODE dmScreenSettings;
memset(&dmScreenSettings, 0, sizeof(dmScreenSettings));
dmScreenSettings.dmSize = sizeof(dmScreenSettings);
dmScreenSettings.dmPelsWidth = (unsigned long)screenWidth;
dmScreenSettings.dmPelsHeight = (unsigned long)screenHeight;
dmScreenSettings.dmBitsPerPel = 32;
dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
// 풀스크린으로 디스플레이 설정을 변경합니다.
ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN);
}
else
{
// 윈도우 모드의 경우 800 * 600 크기를 지정합니다.
screenWidth = 800;
screenHeight = 600;
// 윈도우 창을 가로, 세로의 정 가운데 오도록 합니다.
posX = (GetSystemMetrics(SM_CXSCREEN) - screenWidth) / 2;
posY = (GetSystemMetrics(SM_CYSCREEN) - screenHeight) / 2;
}
// 윈도우를 생성하고 핸들을 구합니다.
_hwnd = CreateWindowEx(WS_EX_APPWINDOW, _applicationName, _applicationName,
WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_POPUP,
posX, posY, screenWidth, screenHeight, NULL, NULL, _hinstance, NULL);
// 윈도우를 화면에 표시하고 포커스를 지정합니다
ShowWindow(_hwnd, SW_SHOW);
SetForegroundWindow(_hwnd);
SetFocus(_hwnd);
}
void SystemClass::ShutdownWindows()
{
// 풀스크린 모드였다면 디스플레이 설정을 초기화합니다.
if(FULL_SCREEN)
{
ChangeDisplaySettings(NULL, 0);
}
// 창을 제거합니다
DestroyWindow(_hwnd);
_hwnd = NULL;
// 프로그램 인스턴스를 제거합니다
UnregisterClass(_applicationName, _hinstance);
_hinstance = NULL;
// 외부포인터 참조를 초기화합니다
ApplicationHandle = NULL;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT umessage, WPARAM wparam, LPARAM lparam)
{
switch(umessage)
{
// 윈도우 종료를 확인합니다
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
}
// 윈도우가 닫히는지 확인합니다
case WM_CLOSE:
{
PostQuitMessage(0);
return 0;
}
// 그 외의 모든 메시지들은 시스템 클래스의 메시지 처리로 넘깁니다.
default:
{
return ApplicationHandle->MessageHandler(hwnd, umessage, wparam, lparam);
}
}
}
튜토이렁ㄹ을 간결하게 유지하기 위해 DirectInput Tutorial을 사용할 때 까지 당분간 윈도우 입력을 사용하도록 하겠습니다. 입력 클래스는 키보드의 사용자 입력을 처리합니다. 이 클래스는 SystemCalss::MessageHandler 함수로부터 입력을 받습니다. 입력 객체는 키보드 배열에 각 키의 상태를 저장합니다. 질의를 받으면 특정 키가 눌려 졌는지를 호출 함수에 알려줍니다. 다음은 InputClass 입니다.
InputClass
#pragma once
class InputClass
{
public:
InputClass();
InputClass(const InputClass&);
~InputClass();
void Initialize();
void KeyDown(unsigned int);
void KeyUp(unsigned int);
bool IsKeyDown(unsigned int);
private:
bool _keys[256];
};
#include "stdafx.h"
#include "inputclass.h"
InputClass::InputClass()
{
}
InputClass::InputClass(const InputClass& other)
{
}
InputClass::~InputClass()
{
}
void InputClass::Initialize()
{
// 키 배열을 초기화합니다
for(int i=0; i<256; ++i)
{
_keys[i] = false;
}
}
void InputClass::KeyDown(unsigned int input)
{
// 키가 눌렸다면 해당 키값을 true로 저장합니다
_keys[input] = true;
}
void InputClass::KeyUp(unsigned int input)
{
// 키가 해제되었다면 해당 키값을 false로 저장합니다
_keys[input] = false;
}
bool InputClass::IsKeyDown(unsigned int key)
{
// 현재 키값이 눌려졌는지 아닌지 상태를 반환합니다
return _keys[key];
}
그래픽 클래스는 시스템 클래스에 의해 생성되는 다른 객체입니다. 이 응용 프로그램의 모든 그래픽 기능은 이 클래스에 캡슐화 됩니다. 또한 이 파일의 헤더를 저넻화면 또는 창 보드와 같이 변경할 수 있는 모든 그래픽 관련 전역 설정에 사용합니다. 현재 이 클래스는 비어있지만, 나중에 튜토이렁레는 모든 그래픽 객체가 포합됩니다.
GraphicClass
#pragma once
/////////////
// GLOBALS //
/////////////
const bool FULL_SCREEN = false;
const bool VSYNC_ENABLED = true;
const float SCREEN_DEPTH = 1000.0f;
const float SCREEN_NEAR = 0.1f;
class D3DClass;
class GraphicsClass
{
public:
GraphicsClass();
GraphicsClass(const GraphicsClass&);
~GraphicsClass();
bool Initialize(int, int, HWND);
void Shutdown();
bool Frame();
private:
bool Render();
private:
D3DClass* _direct3D = nullptr;
};
#include "stdafx.h"
#include "d3dclass.h"
#include "graphicsclass.h"
GraphicsClass::GraphicsClass()
{
}
GraphicsClass::GraphicsClass(const GraphicsClass& other)
{
}
GraphicsClass::~GraphicsClass()
{
}
bool GraphicsClass::Initialize(int screenWidth, int screenHeight, HWND hwnd)
{
// Direct3D 객체 생성
_direct3D = (D3DClass*) SAFE_ALIGNED_NEW(sizeof(D3DClass), 16);
if (!_direct3D)
{
return false;
}
// Direct3D 객체 초기화
if (!_direct3D->Initialize(screenWidth, screenHeight, VSYNC_ENABLED, hwnd, FULL_SCREEN, SCREEN_DEPTH, SCREEN_NEAR))
{
MessageBox(hwnd, L"Could not initialze Direct3D", L"Error", MB_OK);
return false;
}
return true;
}
void GraphicsClass::Shutdown()
{
// Direct3D 객체 반환
if (_direct3D)
{
_direct3D->Shutdown();
SAFE_ALIGNED_DELETE(_direct3D);
_direct3D = 0;
}
}
bool GraphicsClass::Frame()
{
// 그래픽 랜더링 처리
return Render();
}
bool GraphicsClass::Render()
{
// 씬을 그리기 위해 버퍼를 지웁니다.
_direct3D->BeginScene(0.5f, 0.5f, 0.5f, 1.0f);
// 버퍼의 내용을 화면에 출력합니다.
_direct3D->EndScene();
return true;
}
출력화면
'DirectX' 카테고리의 다른 글
5. 조명 (0) | 2022.11.21 |
---|---|
4. 텍스쳐 (0) | 2022.11.11 |
3. 버퍼, 쉐이더 및 HLSL (0) | 2022.10.10 |
번외) Warning - warning C4316 에러 문제 (0) | 2022.10.03 |
2. DirectX 초기화 (0) | 2022.10.03 |