Thinking Different




Tutorial 13 - Direct Input



원문 : http://www.rastertek.com/dx11tut13.html




이 튜토리얼은 DirectX 11의 Direct input을 다룹니다. 역시나 소스코드는 이전 튜토리얼에서 이어집니다.


Direct input은 DirectX API가 제공하는 고속의 입력 함수의 모음입니다. Direct input은 DirectX 11에서도 그 내용이 바뀌지 않았으며 현재 버전은 8입니다. 하지만 Direct input은 애초에 잘 만들어져 있기 때문에 더 이상 업데이트를 할 필요가 없나 봅니다(Direct Sound도 비슷합니다). Direct input은 일반적인 윈도우의 입력 시스템의 비해 믿을 수 없을 정도로 빠릅니다. 입력에 대한 빠른 응답시간을 요구하는 어플리케이션이라면 Direct input을 사용해야 합니다.


이 튜토리얼에서는 키보드와 마우스 장치에 대한 Direct input의 구현을 다룰 것입니다. 또한 TextClass를 사용하여 현재 마우스 포인터의 위치를 표시하게 할 것입니다. 이전 튜토리얼에 InputClass가 있기 때문에 전에 사용했던 기본 입력 대신 Direct input을 사용하도록 바꿀 것입니다.




Inputclass.h


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
#pragma once
 
class InputClass
{
public:
    InputClass();
    InputClass(const InputClass&);
    ~InputClass();
 
    bool Initialize(HINSTANCE, HWND, intint);
    void Shutdown();
    bool Frame();
 
    bool IsEscapePressed();
    void GetMouseLocation(int&int&);
 
private:
    bool ReadKeyboard();
    bool ReadMouse();
    void ProcessInput();
 
private:
    IDirectInput8* m_directInput = nullptr;
    IDirectInputDevice8* m_keyboard = nullptr;
    IDirectInputDevice8* m_mouse = nullptr;
 
    unsigned char m_keyboardState[256= { 0, };
    DIMOUSESTATE m_mouseState;
 
    int m_screenWidth = 0;
    int m_screenHeight = 0;
    int m_mouseX = 0;
    int m_mouseY = 0;
};
cs



Inputclass.cpp


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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
#include "stdafx.h"
#include "inputclass.h"
 
 
InputClass::InputClass()
{
}
 
 
InputClass::InputClass(const InputClass& other)
{
}
 
 
InputClass::~InputClass()
{
}
 
 
bool InputClass::Initialize(HINSTANCE hinstance, HWND hwnd, int screenWidth, int screenHeight)
{
    // 마우스 커서의 위치 지정에 사용될 화면 크기를 설정합니다.
    m_screenWidth = screenWidth;
    m_screenHeight = screenHeight;
 
    // Direct Input 인터페이스를 초기화 합니다.
    HRESULT result = DirectInput8Create(hinstance, DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&m_directInput, NULL);
    if (FAILED(result))
    {
        return false;
    }
 
    // 키보드의 Direct Input 인터페이스를 생성합니다
    result = m_directInput->CreateDevice(GUID_SysKeyboard, &m_keyboard, NULL);
    if (FAILED(result))
    {
        return false;
    }
 
    // 데이터 형식을 설정하십시오. 이 경우 키보드이므로 사전 정의 된 데이터 형식을 사용할 수 있습니다.
    result = m_keyboard->SetDataFormat(&c_dfDIKeyboard);
    if (FAILED(result))
    {
        return false;
    }
 
    // 다른 프로그램과 공유하지 않도록 키보드의 협조 수준을 설정합니다
    result = m_keyboard->SetCooperativeLevel(hwnd, DISCL_FOREGROUND | DISCL_EXCLUSIVE);
    if (FAILED(result))
    {
        return false;
    }
 
    // 키보드를 할당받는다
    result = m_keyboard->Acquire();
    if (FAILED(result))
    {
        return false;
    }
 
    // 마우스 Direct Input 인터페이스를 생성합니다.
    result = m_directInput->CreateDevice(GUID_SysMouse, &m_mouse, NULL);
    if (FAILED(result))
    {
        return false;
    }
 
    // 미리 정의 된 마우스 데이터 형식을 사용하여 마우스의 데이터 형식을 설정합니다.
    result = m_mouse->SetDataFormat(&c_dfDIMouse);
    if (FAILED(result))
    {
        return false;
    }
 
    // 다른 프로그램과 공유 할 수 있도록 마우스의 협력 수준을 설정합니다.
    result = m_mouse->SetCooperativeLevel(hwnd, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE);
    if (FAILED(result))
    {
        return false;
    }
 
    // 마우스를 할당받는다
    result = m_mouse->Acquire();
    if (FAILED(result))
    {
        return false;
    }
 
    return true;
}
 
 
void InputClass::Shutdown()
{
    // 마우스를 반환합니다.
    if (m_mouse)
    {
        m_mouse->Unacquire();
        m_mouse->Release();
        m_mouse = 0;
    }
 
    // 키보드를 반환합니다.
    if (m_keyboard)
    {
        m_keyboard->Unacquire();
        m_keyboard->Release();
        m_keyboard = 0;
    }
 
    // m_directInput 객체를 반환합니다.
    if (m_directInput)
    {
        m_directInput->Release();
        m_directInput = 0;
    }
}
 
 
bool InputClass::Frame()
{
    // 키보드의 현재 상태를 읽는다.
    if (!ReadKeyboard())
    {
        return false;
    }
 
    // 마우스의 현재 상태를 읽는다.
    if (!ReadMouse())
    {
        return false;
    }
 
    // 키보드와 마우스의 변경상태를 처리합니다.
    ProcessInput();
 
    return true;
}
 
 
bool InputClass::ReadKeyboard()
{
    // 키보드 디바이스를 얻는다.
    HRESULT result = m_keyboard->GetDeviceState(sizeof(m_keyboardState), (LPVOID)&m_keyboardState);
    if (FAILED(result))
    {
        // 키보드가 포커스를 잃었거나 획득되지 않은 경우 컨트롤을 다시 가져 온다
        if ((result == DIERR_INPUTLOST) || (result == DIERR_NOTACQUIRED))
        {
            m_keyboard->Acquire();
        }
        else
        {
            return false;
        }
    }
 
    return true;
}
 
 
bool InputClass::ReadMouse()
{
    // 마우스 디바이스를 얻는다.
    HRESULT result = m_mouse->GetDeviceState(sizeof(DIMOUSESTATE), (LPVOID)&m_mouseState);
    if (FAILED(result))
    {
        // 마우스가 포커스를 잃었거나 획득되지 않은 경우 컨트롤을 다시 가져 온다
        if ((result == DIERR_INPUTLOST) || (result == DIERR_NOTACQUIRED))
        {
            m_mouse->Acquire();
        }
        else
        {
            return false;
        }
    }
 
    return true;
}
 
 
void InputClass::ProcessInput()
{
    // 프레임 동안 마우스 위치의 변경을 기반으로 마우스 커서의 위치를 ​​업데이트 합니다.
    m_mouseX += m_mouseState.lX;
    m_mouseY += m_mouseState.lY;
 
    // 마우스 위치가 화면 너비 또는 높이를 초과하지 않는지 확인한다.
    if (m_mouseX < 0) { m_mouseX = 0; }
    if (m_mouseY < 0) { m_mouseY = 0; }
 
    if (m_mouseX > m_screenWidth) { m_mouseX = m_screenWidth; }
    if (m_mouseY > m_screenHeight) { m_mouseY = m_screenHeight; }
}
 
 
bool InputClass::IsEscapePressed()
{
    // escape 키가 현재 눌려지고 있는지 bit값을 계산하여 확인한다.
    if (m_keyboardState[DIK_ESCAPE] & 0x80)
    {
        return true;
    }
 
    return false;
}
 
 
void InputClass::GetMouseLocation(int& mouseX, int& mouseY)
{
    mouseX = m_mouseX;
    mouseY = m_mouseY;
}
cs






기존에 사용하던 윈도우 입력 시스템을 없애고 Direct input을 사용하기 때문에 바뀐 함수들을 위주로 설명하겠습니다. 기존의 Systemclass 헤더파일은 변함이 없으므로 생략하도록 하겠습니다.


Systemclass.cpp


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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
#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 객체 생성. 이 클래스는 추후 사용자의 키보드 입력 처리에 사용됩니다.
    m_Input = new InputClass;
    if (!m_Input)
    {
        return false;
    }
 
    // m_Input 객체 초기화
    if(!m_Input->Initialize(m_hinstance, m_hwnd, screenWidth, screenHeight))
    {
        MessageBox(m_hwnd, L"Could not initialize the input object.", L"Error", MB_OK);
        return false;
    }
 
    // m_Graphics 객체 생성.  그래픽 랜더링을 처리하기 위한 객체입니다.
    m_Graphics = new GraphicsClass;
    if (!m_Graphics)
    {
        return false;
    }
 
    // m_Graphics 객체 초기화.
    return m_Graphics->Initialize(screenWidth, screenHeight, m_hwnd);
}
 
 
void SystemClass::Shutdown()
{
    // m_Graphics 객체 반환
    if (m_Graphics)
    {
        m_Graphics->Shutdown();
        delete m_Graphics;
        m_Graphics = 0;
    }
 
    // m_Input 객체 반환
    if (m_Input)
    {
        m_Input->Shutdown();
        delete m_Input;
        m_Input = 0;
    }
 
    // Window 종료 처리
    ShutdownWindows();
}
 
 
void SystemClass::Run()
{
    // 메시지 구조체 생성 및 초기화
    MSG msg;
    ZeroMemory(&msg, sizeof(MSG));
 
    // 사용자로부터 종료 메시지를 받을때까지 메시지루프를 돕니다
    while (true)
    {
        // 윈도우 메시지를 처리합니다
        if (PeekMessage(&msg, NULL00, PM_REMOVE))
        {
            // 종료 메시지를 받을 경우 메시지 루프를 탈출합니다
            if (msg.message == WM_QUIT)
                break;
 
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        else
        {
            // 그 외에는 Frame 함수를 처리합니다.
            if (!Frame())
            {
                MessageBox(m_hwnd, L"Frame Processing Failed", L"Error", MB_OK);
                break;
            }
        }
        
        // 사용자가 ESC키를 눌렀는지 확인 후 종료 처리함
        if(m_Input->IsEscapePressed() == true)
        {
            break;
        }
    }
}
 
 
bool SystemClass::Frame()
{
    int mouseX = 0, mouseY = 0;
 
    // 입력 프레임 처리를 수행합니다
    if(!m_Input->Frame())
    {
        return false;
    }
 
    // 입력 객체에서 마우스 위치를 가져옵니다
    m_Input->GetMouseLocation(mouseX, mouseY);
 
    // 그래픽 객체의 Frame을 처리합니다
    if(!m_Graphics->Frame(mouseX, mouseY))
    {
        return false;
    }
 
    return m_Graphics->Render();
}
 
 
LRESULT CALLBACK SystemClass::MessageHandler(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam)
{
    return DefWindowProc(hwnd, umsg, wparam, lparam);
}
 
 
void SystemClass::InitializeWindows(int& screenWidth, int& screenHeight)
{
    // 외부 포인터를 이 객체로 지정합니다
    ApplicationHandle = this;
 
    // 이 프로그램의 인스턴스를 가져옵니다
    m_hinstance = GetModuleHandle(NULL);
 
    // 프로그램 이름을 지정합니다
    m_applicationName = L"Dx11Demo_13";
 
    // windows 클래스를 아래와 같이 설정합니다.
    WNDCLASSEX wc;
    wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
    wc.lpfnWndProc = WndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = m_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 = m_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, 0sizeof(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;
    }
 
    // 윈도우를 생성하고 핸들을 구합니다.
    m_hwnd = CreateWindowEx(WS_EX_APPWINDOW, m_applicationName, m_applicationName,
        WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_POPUP,
        posX, posY, screenWidth, screenHeight, NULLNULL, m_hinstance, NULL);
 
    // 윈도우를 화면에 표시하고 포커스를 지정합니다
    ShowWindow(m_hwnd, SW_SHOW);
    SetForegroundWindow(m_hwnd);
    SetFocus(m_hwnd);
}
 
 
void SystemClass::ShutdownWindows()
{
    // 풀스크린 모드였다면 디스플레이 설정을 초기화합니다.
    if (FULL_SCREEN)
    {
        ChangeDisplaySettings(NULL0);
    }
 
    // 창을 제거합니다
    DestroyWindow(m_hwnd);
    m_hwnd = NULL;
 
    // 프로그램 인스턴스를 제거합니다
    UnregisterClass(m_applicationName, m_hinstance);
    m_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);
    }
    }
}
cs



Graphicsclass.h


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
#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 CameraClass;
class TextClass;
 
class GraphicsClass
{
public:
    GraphicsClass();
    GraphicsClass(const GraphicsClass&);
    ~GraphicsClass();
 
    bool Initialize(intint, HWND);
    void Shutdown();
    bool Frame(intint);
    bool Render();
 
private:
    D3DClass* m_Direct3D = nullptr;
    CameraClass* m_Camera = nullptr;
    TextClass* m_Text = nullptr;
};
cs



Graphicsclass.cpp


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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
#include "stdafx.h"
#include "d3dclass.h"
#include "cameraclass.h"
#include "textclass.h"
#include "graphicsclass.h"
 
 
GraphicsClass::GraphicsClass()
{
}
 
 
GraphicsClass::GraphicsClass(const GraphicsClass& other)
{
}
 
 
GraphicsClass::~GraphicsClass()
{
}
 
 
bool GraphicsClass::Initialize(int screenWidth, int screenHeight, HWND hwnd)
{
    // Direct3D 객체 생성
    m_Direct3D = new D3DClass;
    if(!m_Direct3D)
    {
        return false;
    }
 
    // Direct3D 객체 초기화
    if(!m_Direct3D->Initialize(screenWidth, screenHeight, VSYNC_ENABLED, hwnd, FULL_SCREEN, SCREEN_DEPTH, SCREEN_NEAR))
    {
        MessageBox(hwnd, L"Could not initialize Direct3D.", L"Error", MB_OK);
        return false;
    }
 
    // m_Camera 객체 생성
    m_Camera = new CameraClass;
    if (!m_Camera)
    {
        return false;
    }
 
    // 카메라 포지션 설정
    XMMATRIX baseViewMatrix;
    m_Camera->SetPosition(0.0f, 0.0f, -1.0f);
    m_Camera->Render();
    m_Camera->GetViewMatrix(baseViewMatrix);
 
    // m_Text 객체 생성
    m_Text = new TextClass;
    if(!m_Text)
    {
        return false;
    }
 
    // m_Text 객체 초기화
    if (!m_Text->Initialize(m_Direct3D->GetDevice(), m_Direct3D->GetDeviceContext(), hwnd, screenWidth, screenHeight,
 baseViewMatrix))
    {
        MessageBox(hwnd, L"Could not initialize the text object.", L"Error", MB_OK);
        return false;
    }
 
    return true;
}
 
 
void GraphicsClass::Shutdown()
{
    // m_Text 객체 반환
    if (m_Text)
    {
        m_Text->Shutdown();
        delete m_Text;
        m_Text = 0;
    }
 
    // m_Camera 객체 반환
    if (m_Camera)
    {
        delete m_Camera;
        m_Camera = 0;
    }
 
    // Direct3D 객체 반환
    if (m_Direct3D)
    {
        m_Direct3D->Shutdown();
        delete m_Direct3D;
        m_Direct3D = 0;
    }
}
 
 
bool GraphicsClass::Frame(int mouseX, int mouseY)
{
    // 마우스 위치 설정
    if(!m_Text->SetMousePosition(mouseX, mouseY, m_Direct3D->GetDeviceContext()))
    {
        return false;
    }
 
    // 카메라 위치 설정
    m_Camera->SetPosition(0.0f, 0.0f, -10.0f);
 
    return true;
}
 
 
bool GraphicsClass::Render()
{
    // 씬을 그리기 위해 버퍼를 지웁니다
    m_Direct3D->BeginScene(0.0f, 0.0f, 0.0f, 1.0f);
 
    // 카메라의 위치에 따라 뷰 행렬을 생성합니다
    m_Camera->Render();
 
    // 카메라 및 d3d 객체에서 월드, 뷰 및 투영 행렬을 가져옵니다
    XMMATRIX worldMatrix, viewMatrix, projectionMatrix, orthoMatrix;
    
    m_Camera->GetViewMatrix(viewMatrix);
    m_Direct3D->GetWorldMatrix(worldMatrix);
    m_Direct3D->GetProjectionMatrix(projectionMatrix);
    m_Direct3D->GetOrthoMatrix(orthoMatrix);
 
    // 모든 2D 렌더링을 시작하려면 Z 버퍼를 끕니다.
    m_Direct3D->TurnZBufferOff();
 
    // 텍스트를 렌더링하기 전에 알파 블렌딩을 켭니다
    m_Direct3D->TurnOnAlphaBlending();
 
    // 텍스트 문자열을 렌더링 합니다
    if(!m_Text->Render(m_Direct3D->GetDeviceContext(), worldMatrix, orthoMatrix))
    {
        return false;
    }
 
    // 텍스트를 렌더링 한 후 알파 블렌딩을 해제합니다
    m_Direct3D->TurnOffAlphaBlending();
 
    // 모든 2D 렌더링이 완료되었으므로 Z 버퍼를 다시 켜십시오.
    m_Direct3D->TurnZBufferOn();
 
    // 버퍼의 내용을 화면에 출력합니다
    m_Direct3D->EndScene();
 
    return true;
}
cs





TextClass.h


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
37
38
39
40
41
42
43
44
45
46
47
#pragma once
 
class FontClass;
class FontShaderClass;
 
class TextClass : public AlignedAllocationPolicy<16>
{
private:
    struct SentenceType
    {
        ID3D11Buffer *vertexBuffer, *indexBuffer;
        int vertexCount, indexCount, maxLength;
        float red, green, blue;
    };
 
    struct VertexType
    {
        XMFLOAT3 position;
        XMFLOAT2 texture;
    };
 
public:
    TextClass();
    TextClass(const TextClass&);
    ~TextClass();
 
    bool Initialize(ID3D11Device*, ID3D11DeviceContext*, HWND, intint, XMMATRIX);
    void Shutdown();
    bool Render(ID3D11DeviceContext*, XMMATRIX, XMMATRIX);
 
    bool SetMousePosition(intint, ID3D11DeviceContext*);
 
private:
    bool InitializeSentence(SentenceType**int, ID3D11Device*);
    bool UpdateSentence(SentenceType*char*intintfloatfloatfloat, ID3D11DeviceContext*);
    void ReleaseSentence(SentenceType**);
    bool RenderSentence(ID3D11DeviceContext*, SentenceType*, XMMATRIX, XMMATRIX);
 
private:
    FontClass* m_Font = nullptr;
    FontShaderClass* m_FontShader = nullptr;
    int m_screenWidth = 0;
    int m_screenHeight = 0;
    XMMATRIX m_baseViewMatrix;
    SentenceType* m_sentence1;
    SentenceType* m_sentence2;
};
cs



TextClass.cpp


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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
#include "stdafx.h"
#include "FontClass.h"
#include "FontShaderClass.h"
#include "TextClass.h"
 
 
TextClass::TextClass()
{
}
 
TextClass::TextClass(const TextClass& other)
{
}
 
TextClass::~TextClass()
{
}
 
bool TextClass::Initialize(ID3D11Device* device, ID3D11DeviceContext* deviceContext, HWND hwnd, int screenWidth,
 int screenHeight, XMMATRIX baseViewMatrix)
{
    // 화면 너비와 높이를 저장합니다.
    m_screenWidth = screenWidth;
    m_screenHeight = screenHeight;
 
    // 기본 뷰 매트릭스를 저장합니다.
    m_baseViewMatrix = baseViewMatrix;
 
    // 폰트 객체를 생성합니다.
    m_Font = new FontClass;
    if (!m_Font)
    {
        return false;
    }
 
    // 폰트 객체를 초기화 합니다.
    bool result = m_Font->Initialize(device, "../Dx11Demo_13/data/fontdata.txt", L"../Dx11Demo_13/data/font.dds");
    if (!result)
    {
        MessageBox(hwnd, L"Could not initialize the font object.", L"Error", MB_OK);
        return false;
    }
 
    // 폰트 쉐이더 객체를 생성합니다.
    m_FontShader = new FontShaderClass;
    if (!m_FontShader)
    {
        return false;
    }
 
    // 폰트 쉐이더 객체를 초기화 합니다.
    result = m_FontShader->Initialize(device, hwnd);
    if (!result)
    {
        MessageBox(hwnd, L"Could not initialize the font shader object.", L"Error", MB_OK);
        return false;
    }
 
    // 첫 번째 문장을 초기화합니다.
    result = InitializeSentence(&m_sentence1, 16, device);
    if (!result)
    {
        return false;
    }
 
    // 문장 정점 버퍼를 새 문자열 정보로 업데이트합니다.
    result = UpdateSentence(m_sentence1, "Hello"1001001.0f, 1.0f, 1.0f, deviceContext);
    if (!result)
    {
        return false;
    }
 
    // 첫 번째 문장을 초기화합니다.
    result = InitializeSentence(&m_sentence2, 16, device);
    if (!result)
    {
        return false;
    }
 
    // 문장 정점 버퍼를 새 문자열 정보로 업데이트합니다.
    result = UpdateSentence(m_sentence2, "Goodbye"1002001.0f, 1.0f, 0.0f, deviceContext);
    if (!result)
    {
        return false;
    }
 
    return true;
}
 
 
void TextClass::Shutdown()
{
    // 첫번째 문장을 반환합니다.
    ReleaseSentence(&m_sentence1);
 
    // 두번째 문장을 반환합니다.
    ReleaseSentence(&m_sentence2);
 
    // 폰트 쉐이더 객체를 반환합니다.
    if (m_FontShader)
    {
        m_FontShader->Shutdown();
        delete m_FontShader;
        m_FontShader = 0;
    }
 
    // 폰트 객체를 반환합니다.
    if (m_Font)
    {
        m_Font->Shutdown();
        delete m_Font;
        m_Font = 0;
    }
}
 
 
bool TextClass::Render(ID3D11DeviceContext* deviceContext, XMMATRIX worldMatrix, XMMATRIX orthoMatrix)
{
    // 첫번째 문장을 그립니다.
    if (!RenderSentence(deviceContext, m_sentence1, worldMatrix, orthoMatrix))
    {
        return false;
    }
 
    // 두번째 문장을 그립니다.
    return RenderSentence(deviceContext, m_sentence2, worldMatrix, orthoMatrix);
}
 
 
bool TextClass::InitializeSentence(SentenceType** sentence, int maxLength, ID3D11Device* device)
{
    // 새로운 문장 개체를 만듭니다.
    *sentence = new SentenceType;
    if (!*sentence)
    {
        return false;
    }
 
    // 문장 버퍼를 null로 초기화합니다.
    (*sentence)->vertexBuffer = 0;
    (*sentence)->indexBuffer = 0;
 
    // 문장의 최대 길이를 설정합니다.
    (*sentence)->maxLength = maxLength;
 
    // 정점 배열의 정점 수를 설정합니다.
    (*sentence)->vertexCount = 6 * maxLength;
 
    // 인덱스 배열의 인덱스 수를 설정합니다.
    (*sentence)->indexCount = (*sentence)->vertexCount;
 
    // 정점 배열을 만듭니다.
    VertexType* vertices = new VertexType[(*sentence)->vertexCount];
    if (!vertices)
    {
        return false;
    }
 
    // 인덱스 배열을 만듭니다.
    unsigned long* indices = new unsigned long[(*sentence)->indexCount];
    if (!indices)
    {
        return false;
    }
 
    // 처음에는 정점 배열을 0으로 초기화합니다.
    memset(vertices, 0, (sizeof(VertexType) * (*sentence)->vertexCount));
 
    // 인덱스 배열을 초기화합니다.
    for (int i = 0; i<(*sentence)->indexCount; i++)
    {
        indices[i] = i;
    }
 
    // 동적 인 정점 버퍼의 설명을 설정한다.
    D3D11_BUFFER_DESC vertexBufferDesc;
    vertexBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
    vertexBufferDesc.ByteWidth = sizeof(VertexType) * (*sentence)->vertexCount;
    vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
    vertexBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    vertexBufferDesc.MiscFlags = 0;
    vertexBufferDesc.StructureByteStride = 0;
 
    // subresource 구조에 정점 데이터에 대한 포인터를 제공합니다.
    D3D11_SUBRESOURCE_DATA vertexData;
    vertexData.pSysMem = vertices;
    vertexData.SysMemPitch = 0;
    vertexData.SysMemSlicePitch = 0;
 
    // 정점 버퍼를 만든다.
    if (FAILED(device->CreateBuffer(&vertexBufferDesc, &vertexData, &(*sentence)->vertexBuffer)))
    {
        return false;
    }
 
    // 정적 인덱스 버퍼의 설명을 설정합니다.
    D3D11_BUFFER_DESC indexBufferDesc;
    
    indexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
    indexBufferDesc.ByteWidth = sizeof(unsigned long* (*sentence)->indexCount;
    indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
    indexBufferDesc.CPUAccessFlags = 0;
    indexBufferDesc.MiscFlags = 0;
    indexBufferDesc.StructureByteStride = 0;
 
    // 하위 리소스 구조에 인덱스 데이터에 대한 포인터를 제공합니다.
    D3D11_SUBRESOURCE_DATA indexData;
    indexData.pSysMem = indices;
    indexData.SysMemPitch = 0;
    indexData.SysMemSlicePitch = 0;
 
    // 인덱스 버퍼를 만듭니다.
    if (FAILED(device->CreateBuffer(&indexBufferDesc, &indexData, &(*sentence)->indexBuffer)))
    {
        return false;
    }
 
    // 더 이상 필요하지 않은 정점 배열을 해제합니다.
    delete[] vertices;
    vertices = 0;
 
    // 더 이상 필요하지 않은 인덱스 배열을 해제합니다.
    delete[] indices;
    indices = 0;
 
    return true;
}
 
 
bool TextClass::UpdateSentence(SentenceType* sentence, char* text, int positionX, int positionY, float red, float green,
 float blue, ID3D11DeviceContext* deviceContext)
{
    // 문장의 색을 저장한다.
    sentence->red = red;
    sentence->green = green;
    sentence->blue = blue;
 
    // 가능한 버퍼 오버 플로우를 확인합니다.
    if ((int)strlen(text) > sentence->maxLength)
    {
        return false;
    }
 
    // 정점 배열을 만듭니다.
    VertexType* vertices = new VertexType[sentence->vertexCount];
    if (!vertices)
    {
        return false;
    }
 
    // 처음에는 정점 배열을 0으로 초기화합니다.
    memset(vertices, 0, (sizeof(VertexType) * sentence->vertexCount));
 
    // 그리기를 시작할 화면에서 X 및 Y 픽셀 위치를 계산합니다.
    float drawX = (float)(((m_screenWidth / 2* -1+ positionX);
    float drawY = (float)((m_screenHeight / 2- positionY);
 
    // 폰트 클래스를 사용하여 문장 텍스트와 문장 그리기 위치에서 정점 배열을 만듭니다.
    m_Font->BuildVertexArray((void*)vertices, text, drawX, drawY);
 
    // 버텍스 버퍼를 쓸 수 있도록 잠급니다.
    D3D11_MAPPED_SUBRESOURCE mappedResource;
    if (FAILED(deviceContext->Map(sentence->vertexBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0&mappedResource)))
    {
        return false;
    }
 
    // 정점 버퍼의 데이터를 가리키는 포인터를 얻는다.
    VertexType* verticesPtr = (VertexType*)mappedResource.pData;
 
    // 데이터를 정점 버퍼에 복사합니다.
    memcpy(verticesPtr, (void*)vertices, (sizeof(VertexType) * sentence->vertexCount));
 
    // 정점 버퍼의 잠금을 해제합니다.
    deviceContext->Unmap(sentence->vertexBuffer, 0);
 
    // 더 이상 필요하지 않은 정점 배열을 해제합니다.
    delete[] vertices;
    vertices = 0;
 
    return true;
}
 
 
void TextClass::ReleaseSentence(SentenceType** sentence)
{
    if (*sentence)
    {
        // 문장 버텍스 버퍼를 해제합니다.
        if ((*sentence)->vertexBuffer)
        {
            (*sentence)->vertexBuffer->Release();
            (*sentence)->vertexBuffer = 0;
        }
 
        // 문장 인덱스 버퍼를 해제합니다.
        if ((*sentence)->indexBuffer)
        {
            (*sentence)->indexBuffer->Release();
            (*sentence)->indexBuffer = 0;
        }
 
        // 문장을 해제합니다.
        delete *sentence;
        *sentence = 0;
    }
}
 
 
bool TextClass::RenderSentence(ID3D11DeviceContext* deviceContext, SentenceType* sentence, XMMATRIX worldMatrix,
 XMMATRIX orthoMatrix)
{
    // 정점 버퍼 간격 및 오프셋을 설정합니다.
    unsigned int stride = sizeof(VertexType);
    unsigned int offset = 0;
 
    // 렌더링 할 수 있도록 입력 어셈블러에서 정점 버퍼를 활성으로 설정합니다.
    deviceContext->IASetVertexBuffers(01&sentence->vertexBuffer, &stride, &offset);
 
    // 렌더링 할 수 있도록 입력 어셈블러에서 인덱스 버퍼를 활성으로 설정합니다.
    deviceContext->IASetIndexBuffer(sentence->indexBuffer, DXGI_FORMAT_R32_UINT, 0);
 
    // 이 정점 버퍼에서 렌더링 되어야 하는 프리미티브 유형을 설정합니다.이 경우에는 삼각형입니다.
    deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
 
    // 입력 된 문장 색상으로 픽셀 색상 벡터를 만듭니다.
    XMFLOAT4 pixelColor = XMFLOAT4(sentence->red, sentence->green, sentence->blue, 1.0f);
 
    // 폰트 셰이더를 사용하여 텍스트를 렌더링합니다.
    if (!m_FontShader->Render(deviceContext, sentence->indexCount, worldMatrix, m_baseViewMatrix, orthoMatrix, 
m_Font->GetTexture(), pixelColor))
    {
        false;
    }
 
    return true;
}
 
bool TextClass::SetMousePosition(int mouseX, int mouseY, ID3D11DeviceContext* deviceContext)
{
    // mouseX 정수를 문자열 형식으로 변환합니다.
    char tempString[16= { 0, };
    _itoa_s(mouseX, tempString, 10);
 
    // mouseX 문자열을 설정합니다.
    char mouseString[16= { 0, };
    strcpy_s(mouseString, "Mouse X: ");
    strcat_s(mouseString, tempString);
 
    // 문장 정점 버퍼를 새 문자열 정보로 업데이트합니다.
    if(!UpdateSentence(m_sentence1, mouseString, 20201.0f, 1.0f, 1.0f, deviceContext))
    {
        return false;
    }
 
    // mouseY 정수를 문자열 형식으로 변환합니다.
    _itoa_s(mouseY, tempString, 10);
 
    // mouseY 문자열을 설정합니다.
    strcpy_s(mouseString, "Mouse Y: ");
    strcat_s(mouseString, tempString);
 
    // 문장 정점 버퍼를 새 문자열 정보로 업데이트합니다.
    if(!UpdateSentence(m_sentence2, mouseString, 20401.0f, 1.0f, 1.0f, deviceContext))
    {
        return false;
    }
 
    return true;
}
cs



출력 화면




마치면서


보신 것처럼 DirectX 11에서 direct input을 설정하는 것은 정말 간단하지만 입력 장치에 고속의 접근을 가능하게 해 줍니다.



연습문제


1. 프로그램을 컴파일하고 실행해 보십시오. 마우스를 화면 내에서 이동하고 위치를 표시하는 글자가 바뀌는 것을 확인해 보십시오.


2. 2D 렌더링 튜토리얼의 내용을 활용하여 마우스 이동이 반영되는 마우스 포인터를 만들어 보십시오.


3. 키보드 버퍼를 읽어 화면에 어떤 글자가 타이핑되는지 표시하는 함수를 만들어 보십시오.



소스코드


소스코드 : Dx11Demo_13.zip