-
Notifications
You must be signed in to change notification settings - Fork 4
/
app.cpp
339 lines (274 loc) · 13.1 KB
/
app.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
#include "pch.h"
#include "core/app.h"
#include "core/clock.h"
#include "core/input.h"
#include "core/log.h"
#include "core/window.h"
#include "scene/renderer.h"
#include "scene/ui.h"
using namespace scene;
namespace core {
static bool app_exit = false;
// singleton instance accessor
Application& Application::GetInstance() {
// since c++11, this will be thread-safe, there's no need for manual locking
static Application instance;
return instance;
}
// check if the OpenGL context is active
bool Application::IsContextActive() { return GetInstance().opengl_context_active; }
#pragma warning(push)
#pragma warning(disable : 4100)
// OpenGL debug message callback
static void GLAPIENTRY OnErrorMessage(GLenum source, GLenum type, GLuint id,
GLenum severity, GLsizei length, const GLchar* message, const void* userParam) {
if (severity == GL_DEBUG_SEVERITY_NOTIFICATION) {
return; // silently ignore notifications
}
std::string err_code;
GLenum err;
while ((err = glGetError()) != GL_NO_ERROR) {
switch (err) {
case GL_INVALID_ENUM: err_code = "invalid enumeration"; break;
case GL_INVALID_VALUE: err_code = "invalid parameter value"; break;
case GL_INVALID_OPERATION: err_code = "invalid operation"; break;
case GL_INVALID_FRAMEBUFFER_OPERATION: err_code = "invalid framebuffer"; break;
case GL_OUT_OF_MEMORY: err_code = "cannot allocate memory"; break;
case GL_CONTEXT_LOST: err_code = "OpenGL context lost"; break;
case GL_STACK_OVERFLOW: err_code = "stack overflow"; break;
case GL_STACK_UNDERFLOW: err_code = "stack underflow"; break;
default: err_code = "unknown error"; break;
}
CORE_ERROR("Internal error detected! {0:x}: {1}", err, err_code);
}
std::string err_source, err_type, err_level;
switch (source) {
case GL_DEBUG_SOURCE_API: err_source = "OpenGL API calls"; break;
case GL_DEBUG_SOURCE_WINDOW_SYSTEM: err_source = "Windows API calls"; break;
case GL_DEBUG_SOURCE_SHADER_COMPILER: err_source = "shader compiler"; break;
case GL_DEBUG_SOURCE_THIRD_PARTY: err_source = "third party"; break;
case GL_DEBUG_SOURCE_APPLICATION: err_source = "main application"; break;
case GL_DEBUG_SOURCE_OTHER: err_source = "other"; break;
default: err_source = "???"; break;
}
switch (type) {
case GL_DEBUG_TYPE_ERROR: err_type = "error"; break;
case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: err_type = "deprecated behavior"; break;
case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: err_type = "undefined behavior"; break;
case GL_DEBUG_TYPE_PORTABILITY: err_type = "portability"; break;
case GL_DEBUG_TYPE_PERFORMANCE: err_type = "performance"; break;
case GL_DEBUG_TYPE_OTHER: err_type = "other"; break;
default: err_type = "???"; break;
}
switch (severity) {
case GL_DEBUG_SEVERITY_HIGH: err_level = "high"; break;
case GL_DEBUG_SEVERITY_MEDIUM: err_level = "medium"; break;
case GL_DEBUG_SEVERITY_LOW: err_level = "low"; break;
default: err_level = "???"; break;
}
CORE_ERROR("Debug message callback has been triggered!");
CORE_ERROR("Error source: {0}", err_source);
CORE_ERROR("Error type: {0}", err_type);
CORE_ERROR("Error severity: {0}", err_level);
CORE_TRACE("Error message: {0}", message);
// if the breakpoint is triggered, check the "Stack Frame" dropdown list above
// to find out exactly which source file and line number has caused the error.
// please also read the detailede error code and message in the console, FAQ:
// https://www.khronos.org/opengl/wiki/OpenGL_Error
#ifdef _DEBUG
if (err_level == "High") {
__debugbreak(); // this is the MSVC intrinsic
}
#endif
}
#pragma warning(pop)
// -------------------------------------------------------------------------
// freeglut event callbacks
// -------------------------------------------------------------------------
#pragma warning(push)
#pragma warning(disable : 4100)
void Application::Idle() {}
void Application::Display() {
Renderer::DrawScene();
}
void Application::Entry(int state) {
if (state == GLUT_ENTERED) {
CORE_INFO("Cursor enters window");
}
else if (state == GLUT_LEFT) {
CORE_INFO("Cursor leaves window");
}
}
void Application::Keyboard(unsigned char key, int x, int y) {
// when the windows api is on top, yield input control to the operating system
if (Window::layer == Layer::Win32) {
return;
}
Input::SetKeyState(key, true);
// functional keys have the highest priority (application/window level)
if (Input::IsKeyPressed(VK_ESCAPE)) {
app_exit = Window::ConfirmExit(); // the user has requested to exit?
Input::SetKeyState(VK_ESCAPE, false); // exit is cancelled, release esc key
return;
}
if (Input::IsKeyPressed(VK_RETURN)) {
Window::ToggleImGui();
return;
}
// gameplay control keys have lower priority (scene/layer level)
if (Window::layer == Layer::ImGui) {
// current layer is ImGui, dispatch input control to ImGui
ImGui_ImplGLUT_KeyboardFunc(key, x, y);
// disable our own scene input control, we don't want objects to
// move around the scene when the ImGui menu is on the top layer
Input::SetKeyState(key, false);
}
}
void Application::KeyboardUp(unsigned char key, int x, int y) {
// when the windows api is on top, yield input control to the operating system
if (Window::layer == Layer::Win32) {
return;
}
Input::SetKeyState(key, false);
if (Window::layer == Layer::ImGui) {
ImGui_ImplGLUT_KeyboardUpFunc(key, x, y);
}
}
void Application::Reshape(int width, int height) {
// in this demo, we simply lock window position, size and aspect ratio
Window::Reshape();
// ImGui will decide how to respond to reshape by itself
ImGui_ImplGLUT_ReshapeFunc(Window::width, Window::height);
}
void Application::Motion(int x, int y) {
// this callback responds to mouse drag-and-move events, which is only used
// when the current layer is ImGui (for moving, resizing & docking widgets)
if (Window::layer == Layer::ImGui) {
ImGui_ImplGLUT_MotionFunc(x, y);
}
}
void Application::PassiveMotion(int x, int y) {
if (Window::layer == Layer::Scene) {
Input::SetMouseMove(x, y);
}
else if (Window::layer == Layer::ImGui) {
ImGui_ImplGLUT_MotionFunc(x, y);
}
}
void Application::Mouse(int button, int state, int x, int y) {
if (Window::layer == Layer::ImGui) {
ImGui_ImplGLUT_MouseFunc(button, state, x, y);
}
else if (Window::layer == Layer::Scene) {
if (Input::IsScrollWheelDown(button, state)) {
Input::SetMouseZoom(+1);
}
else if (Input::IsScrollWheelUp(button, state)) {
Input::SetMouseZoom(-1);
}
}
}
void Application::Special(int key, int x, int y) {
// this callback responds to special keys pressing events (f1, f2, numpads, direction keys)
// note that this is only invoked every few frames, not each frame, so the update here
// won't be smooth, you should place your continuous updates in the idle and display
// callback to avoid jerkiness, this callback should only be reserved for setting flags.
// also be aware that the keyboard callback in freeglut uses `unsigned char` for key types,
// while the special callback uses `int` instead, so there are potential conflicts if you
// use both callbacks for registering input key states due to unsafe type conversion.
// for example, key "d", NumPad 4 and the left arrow key all have a value of 100 when
// converted to `uint8_t`, if you press "d" to move right, the left arrow key will be
// activated at the same time so the movements would cancel each other.
}
void Application::SpecialUp(int key, int x, int y) {
// this callback responds to special keys releasing events (f1, f2, numpads, direction keys)
// take notice of discrete updates and potential conflicts as in the special callback above
}
#pragma warning(pop)
// -------------------------------------------------------------------------
// core event functions
// -------------------------------------------------------------------------
void Application::Init() {
gl_vendor = gl_renderer = gl_version = glsl_version = "";
gl_texsize = gl_texsize_3d = gl_texsize_cubemap = gl_max_texture_units = 0;
opengl_context_active = false;
std::cout << ".........\n" << std::endl;
Log::Init();
CORE_INFO("Initializing console logger ...");
CORE_INFO("Initializing application window ...");
Window::Init();
// create the main freeglut window
glutInitDisplayMode(Window::display_mode);
glutInitWindowSize(Window::width, Window::height);
glutInitWindowPosition(Window::pos_x, Window::pos_y);
Window::id = glutCreateWindow((Window::title).c_str());
if (Window::id <= 0) {
CORE_ERROR("Failed to create a window...");
exit(EXIT_FAILURE);
}
// convert the window name from char* to wchar_t* in order to call the Win32 API
// this step is necessary because we are compiling with Unicode rather than ANSI
const char* win_char = (Window::title).c_str();
const size_t n_char = strlen(win_char) + 1;
std::wstring win_wchar = std::wstring(n_char, L'#');
mbstowcs(&win_wchar[0], win_char, n_char);
LPCWSTR win_ptr = (LPCWSTR)(wchar_t*)win_wchar.c_str();
// set custom style for the freeglut window
// https://docs.microsoft.com/en-us/windows/win32/winmsg/window-styles
HWND win_handle = WIN32::FindWindow(NULL, win_ptr); // find the handle to the glut window
LONG style = GetWindowLong(win_handle, GWL_STYLE); // find the current window style
style = style ^ WS_SYSMENU; // disable maximize/minimize/close button on title bar
SetWindowLong(win_handle, GWL_STYLE, style); // apply the new window style
// loading OpenGL function pointers
if (glewInit() != GLEW_OK) {
CORE_ERROR("Failed to initialize GLEW...");
exit(EXIT_FAILURE);
}
CORE_INFO("Initializing input control system ...");
Input::Init();
Input::HideCursor();
CORE_INFO("Initializing ImGui backends ...");
ui::Init();
CORE_INFO("Starting application debug session ...");
// register the debug message callback
glEnable(GL_DEBUG_OUTPUT);
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
glDebugMessageCallback(OnErrorMessage, nullptr);
glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_TRUE);
// register freeglut event callbacks
glutIdleFunc(Idle);
glutDisplayFunc(Display);
glutEntryFunc(Entry);
glutKeyboardFunc(Keyboard);
glutKeyboardUpFunc(KeyboardUp);
glutMouseFunc(Mouse);
glutReshapeFunc(Reshape);
glutMotionFunc(Motion);
glutPassiveMotionFunc(PassiveMotion);
glutSpecialFunc(Special);
glutSpecialUpFunc(SpecialUp);
// opengl context is now active
opengl_context_active = true;
std::cout << std::endl;
}
void Application::Start() {
Clock::Reset();
Renderer::Attach("Welcome Screen");
}
void Application::PostEventUpdate() {
if (app_exit) {
GetInstance().Clear();
exit(EXIT_SUCCESS);
}
Clock::Update();
Renderer::DrawImGui();
}
void Application::Clear() {
CORE_TRACE("Application running time: {0:.2f} seconds", Clock::time);
CORE_TRACE("Shutting down application ...");
ui::Clear();
Renderer::Detach();
Clock::Reset();
Window::Clear();
}
}