diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b727799..a1033b6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -29,7 +29,7 @@ add_executable( ${SOURCE_FILES} ) -target_include_directories(${current_target} PUBLIC ${CMAKE_SOURCE_DIR}/external) +target_include_directories(${current_target} PUBLIC . ${CMAKE_SOURCE_DIR}/external) if(MSVC) target_link_libraries(${current_target} PUBLIC opengl32.lib) diff --git a/src/main.cpp b/src/main.cpp index a9082c8..1b0f00a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -14,19 +14,20 @@ auto main() -> int }); auto renderer = oglsCreateOpenGL({ - .nativeWindowHandle = window->GetNativeHandle(), + .nativeWindowHandle = GetNativeHandle(window), .versionMajor = 4, .versionMinor = 6 }); - while (!window->ShouldClose()) + while (!ShouldClose(window)) { - window->PumpMessages(); + PumpMessages(window); - renderer->ClearBuffers(); - renderer->DrawScene(); - renderer->SwapBuffers(); + ClearBuffers(renderer); + DrawScene(renderer); + Present(renderer); } + Destroy(renderer); return 0; } diff --git a/src/opengl.h b/src/opengl.h index f5b53ad..3d39a07 100644 --- a/src/opengl.h +++ b/src/opengl.h @@ -4,7 +4,6 @@ namespace ogl_starter { -class Window; struct OpenGLCreateParams { @@ -13,16 +12,60 @@ struct OpenGLCreateParams int versionMinor; }; +template +concept IsOpenGL = requires(T t) { + ClearBuffers(t); + Present(t); + DrawScene(t); +}; + class OpenGL { public: - virtual ~OpenGL() = default; - virtual void ClearBuffers() = 0; - virtual void SwapBuffers() = 0; + template + OpenGL(T&& t) + : self{std::make_unique>(std::move(t))} + { + } - virtual void DrawScene() = 0; + ~OpenGL() = default; + OpenGL(OpenGL&&) = default; + OpenGL& operator=(OpenGL&&) = default; + OpenGL(const OpenGL&) = delete; + OpenGL operator=(const OpenGL&) = delete; + + friend void ClearBuffers(const OpenGL& opengl) { opengl.self->ClearBuffers_(); } + friend void Present(const OpenGL& opengl) { opengl.self->Present_(); } + friend void DrawScene(const OpenGL& opengl) { opengl.self->DrawScene_(); } + friend void Destroy(const OpenGL& opengl) { opengl.self->Destroy_(); } + +private: + struct concept_t + { + virtual ~concept_t() = default; + + virtual void ClearBuffers_() const = 0; + virtual void Present_() const = 0; + virtual void DrawScene_() const = 0; + virtual void Destroy_() const = 0; + }; + + template + struct model_t final : concept_t + { + model_t(T&& data) : m_data(std::move(data)) {} + + virtual void ClearBuffers_() const override { ClearBuffers(m_data); } + virtual void Present_() const override { Present(m_data); } + virtual void DrawScene_() const override { DrawScene(m_data); } + virtual void Destroy_() const override { Destroy(m_data); } + + T m_data; + }; + + std::unique_ptr self; }; } // namespace ogl_starter -std::unique_ptr oglsCreateOpenGL(ogl_starter::OpenGLCreateParams params); \ No newline at end of file +ogl_starter::OpenGL oglsCreateOpenGL(ogl_starter::OpenGLCreateParams params); diff --git a/src/platform/null_opengl.cpp b/src/platform/null_opengl.cpp index d787e20..dbe8be8 100644 --- a/src/platform/null_opengl.cpp +++ b/src/platform/null_opengl.cpp @@ -1,26 +1,29 @@ -#include "../opengl.h" +#include "opengl.h" namespace ogl_starter { -class NullOpenGLImpl final : public OpenGL +struct NullOpenGLImpl final { -public: - NullOpenGLImpl() = default; - ~NullOpenGLImpl() override = default; - NullOpenGLImpl(const NullOpenGLImpl&) = delete; - NullOpenGLImpl& operator=(const NullOpenGLImpl&) = delete; - - NullOpenGLImpl(NullOpenGLImpl&&) = default; - NullOpenGLImpl& operator=(NullOpenGLImpl&&) = default; - - void ClearBuffers() override {} - void SwapBuffers() override {} - void DrawScene() override {} }; + +void ClearBuffers(const NullOpenGLImpl&) +{ +} + +void Present(const NullOpenGLImpl&) +{ +} + +void DrawScene(const NullOpenGLImpl&) +{ +} + +void Destroy(const NullOpenGLImpl&) +{ +} } // namespace ogl_starter -std::unique_ptr oglsCreateOpenGL(ogl_starter::OpenGLCreateParams) +ogl_starter::OpenGL oglsCreateOpenGL(ogl_starter::OpenGLCreateParams) { - auto result = std::make_unique(); - return result; + return ogl_starter::NullOpenGLImpl{}; } diff --git a/src/platform/null_window.cpp b/src/platform/null_window.cpp index 20de6ea..c78cd20 100644 --- a/src/platform/null_window.cpp +++ b/src/platform/null_window.cpp @@ -1,29 +1,29 @@ -#include "../window.h" +#include "window.h" namespace ogl_starter { -class NullWindowImpl final : public Window +struct NullWindowImpl final { -public: - NullWindowImpl() = default; - ~NullWindowImpl() override = default; - - NullWindowImpl(NullWindowImpl&&) = delete; - NullWindowImpl& operator=(NullWindowImpl&&) = delete; - NullWindowImpl(const NullWindowImpl&) = delete; - NullWindowImpl& operator=(const NullWindowImpl&) = delete; - - void PumpMessages() override {} - bool ShouldClose() const override { return false; } - void* GetNativeHandle() const override { return nullptr; } - }; -} // namespace starter_window - -std::unique_ptr oglsCreateWindow(ogl_starter::WindowCreateParams) +void PumpMessages(NullWindowImpl&) { - auto result = std::make_unique(); - return result; +} + +bool ShouldClose(const NullWindowImpl&) +{ + return false; +} + +void* GetNativeHandle(const NullWindowImpl&) +{ + return nullptr; +} + +} // namespace ogl_starter + +ogl_starter::Window oglsCreateWindow(ogl_starter::WindowCreateParams) +{ + return ogl_starter::NullWindowImpl{}; } diff --git a/src/platform/win32_opengl.cpp b/src/platform/win32_opengl.cpp index d5fb1c5..e6f52da 100644 --- a/src/platform/win32_opengl.cpp +++ b/src/platform/win32_opengl.cpp @@ -1,10 +1,13 @@ -#include "../opengl.h" -#include "../window.h" +#include "opengl.h" #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif +#ifndef NOMINMAX +#define NOMINMAX +#endif + #include #include #include "GL/glext.h" @@ -18,11 +21,10 @@ static T LoadGLProc(const char* functionName) } #define VALIDATEGLPROC(proc) \ - if (!proc) \ - { \ - MessageBox(NULL, "Failed to load " #proc, "Fatal Error", MB_ICONERROR); \ - return false; \ - } +if (!proc) \ +{ \ + MessageBox(NULL, "Failed to load " #proc, "Fatal Error", MB_ICONERROR); \ +} static PFNWGLGETEXTENSIONSSTRINGEXTPROC wglGetExtensionsStringEXT; static PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB; @@ -87,203 +89,7 @@ static void LoadGLFunctions() glBufferData = LoadGLProc("glBufferData"); } -class Win32OpenGLImpl final : public OpenGL -{ -public: - Win32OpenGLImpl(); - ~Win32OpenGLImpl() override; - Win32OpenGLImpl(const Win32OpenGLImpl&) = delete; - Win32OpenGLImpl& operator=(const Win32OpenGLImpl&) = delete; - - Win32OpenGLImpl(Win32OpenGLImpl&&) = default; - Win32OpenGLImpl& operator=(Win32OpenGLImpl&&) = default; - - bool init(OpenGLCreateParams params); - - void ClearBuffers() override; - void SwapBuffers() override; - void DrawScene() override; - -private: - void initTriangleResources(); - - HDC m_deviceContext; - HGLRC m_renderingContext; - HWND m_windowHandle; - - //TEMP STUFF - GLuint VAO; - GLuint shaderProgram; -}; - -Win32OpenGLImpl::Win32OpenGLImpl() - : m_deviceContext(nullptr) - , m_renderingContext(nullptr) - , m_windowHandle(nullptr) - , VAO(0) - , shaderProgram(0) -{ -} - -Win32OpenGLImpl::~Win32OpenGLImpl() -{ - wglMakeCurrent(nullptr, nullptr); - wglDeleteContext(m_renderingContext); -} - -bool Win32OpenGLImpl::init(OpenGLCreateParams params) -{ - m_windowHandle = static_cast(params.nativeWindowHandle); - HINSTANCE hInstance = GetModuleHandle(NULL); - - HWND dummyWindow = [hInstance]() -> HWND - { - WNDCLASSEX wcex; - ZeroMemory(&wcex, sizeof(wcex)); - wcex.cbSize = sizeof(wcex); - wcex.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; - wcex.lpfnWndProc = DefWindowProc; - wcex.hInstance = hInstance; - wcex.hCursor = LoadCursor(NULL, IDC_ARROW); - wcex.lpszClassName = "DummyWNDClass"; - - if (RegisterClassEx(&wcex) == 0) - { - MessageBox(NULL, "registerClass() failed.", "Fatal Error", MB_ICONERROR); - return false; - } - - return CreateWindow( - wcex.lpszClassName, "Dummy Window", - WS_OVERLAPPEDWINDOW, - 0, 0, - 1, 1, - nullptr, nullptr, - hInstance, nullptr); - - }(); - - HDC dummyDC = GetDC(dummyWindow); - - PIXELFORMATDESCRIPTOR dummyPFD = {}; - dummyPFD.nSize = sizeof(dummyPFD); - dummyPFD.nVersion = 1; - dummyPFD.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; - dummyPFD.iPixelType = PFD_TYPE_RGBA; - dummyPFD.cColorBits = 32; - dummyPFD.cAlphaBits = 8; - dummyPFD.cDepthBits = 24; - dummyPFD.iLayerType = PFD_MAIN_PLANE; - - const int dummyPFDID = ChoosePixelFormat(dummyDC, &dummyPFD); - if (dummyPFDID == 0) - { - MessageBox(nullptr, "ChoosePixelFormat() failed.", "Fatal Error", MB_ICONERROR); - return false; - } - - if (SetPixelFormat(dummyDC, dummyPFDID, &dummyPFD) == false) - { - MessageBox(nullptr, "SetPixelFormat() failed.", "Fatal Error", MB_ICONERROR); - return false; - } - - HGLRC dummyRC = wglCreateContext(dummyDC); - - if (dummyRC == 0) - { - MessageBox(nullptr, "wglCreateContext() failed.", "Fatal Error", MB_ICONERROR); - return false; - } - - if (wglMakeCurrent(dummyDC, dummyRC) == false) - { - MessageBox(nullptr, "wglMakeCurrent() failed.", "Fatal Error", MB_ICONERROR); - return false; - } - - wglChoosePixelFormatARB = LoadGLProc("wglChoosePixelFormatARB"); - VALIDATEGLPROC(wglChoosePixelFormatARB); - - wglCreateContextAttribsARB = LoadGLProc("wglCreateContextAttribsARB"); - VALIDATEGLPROC(wglCreateContextAttribsARB); - - m_deviceContext = GetDC(m_windowHandle); - - const int pixelAttribs[] = { - WGL_DRAW_TO_WINDOW_ARB, GL_TRUE, - WGL_SUPPORT_OPENGL_ARB, GL_TRUE, - WGL_DOUBLE_BUFFER_ARB, GL_TRUE, - WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB, - WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB, - WGL_COLOR_BITS_ARB, 32, - WGL_ALPHA_BITS_ARB, 8, - WGL_DEPTH_BITS_ARB, 24, - WGL_STENCIL_BITS_ARB, 8, - WGL_SAMPLE_BUFFERS_ARB, GL_TRUE, - WGL_SAMPLES_ARB, 4, - 0}; - - int pixelFormatID; - UINT numFormats; - const bool status = wglChoosePixelFormatARB(m_deviceContext, pixelAttribs, NULL, 1, &pixelFormatID, &numFormats); - - if (status == false || numFormats == 0) - { - MessageBox(nullptr, "wglChoosePixelFormatARB() failed.", "Fatal Error", MB_ICONERROR); - return false; - } - - PIXELFORMATDESCRIPTOR PFD; - DescribePixelFormat(m_deviceContext, pixelFormatID, sizeof(PFD), &PFD); - SetPixelFormat(m_deviceContext, pixelFormatID, &PFD); - - const int major_min = 4, minor_min = 6; - const int contextAttribs[] = { - WGL_CONTEXT_MAJOR_VERSION_ARB, major_min, - WGL_CONTEXT_MINOR_VERSION_ARB, minor_min, - WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB, - WGL_CONTEXT_LAYER_PLANE_ARB, 0, - WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_DEBUG_BIT_ARB, - 0}; - - m_renderingContext = wglCreateContextAttribsARB(m_deviceContext, 0, contextAttribs); - if (m_renderingContext == nullptr) - { - MessageBox(nullptr, "wglCreateContextAttribsARB() failed.", "Fatal Error", MB_ICONERROR); - return false; - } - - wglMakeCurrent(nullptr, nullptr); - wglDeleteContext(dummyRC); - ReleaseDC(dummyWindow, dummyDC); - DestroyWindow(dummyWindow); - - if (!wglMakeCurrent(m_deviceContext, m_renderingContext)) - { - MessageBox(nullptr, "wglMakeCurrent() failed.", "Fatal Error", MB_ICONERROR); - return false; - } - - LoadGLFunctions(); - - initTriangleResources(); - - return true; -} - -void Win32OpenGLImpl::ClearBuffers() -{ - glClearColor(0.129f, 0.586f, 0.949f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT); -} - -void Win32OpenGLImpl::SwapBuffers() -{ - ::SwapBuffers(m_deviceContext); -} - -void Win32OpenGLImpl::initTriangleResources() +static std::pair InitTriangleResources() { float vertices[] = { 0.5f, 0.5f, 0.0f, @@ -297,6 +103,7 @@ void Win32OpenGLImpl::initTriangleResources() 1, 2, 3 }; + GLuint VAO; glGenVertexArrays(1, &VAO); glBindVertexArray(VAO); @@ -344,8 +151,8 @@ void Win32OpenGLImpl::initTriangleResources() "{\n" " FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n" "}\0"; - - + + glShaderSource(FS, 1, &fs_source, nullptr); glCompileShader(FS); glGetShaderiv(FS, GL_COMPILE_STATUS, &success); @@ -356,7 +163,7 @@ void Win32OpenGLImpl::initTriangleResources() printf("Fragment Shader Compilation Failed\n %s\n", infoLog); } - shaderProgram = glCreateProgram(); + GLuint shaderProgram = glCreateProgram(); glAttachShader(shaderProgram, VS); glAttachShader(shaderProgram, FS); glLinkProgram(shaderProgram); @@ -372,22 +179,179 @@ void Win32OpenGLImpl::initTriangleResources() glDeleteShader(VS); glDeleteShader(FS); + return {VAO, shaderProgram}; + } + +struct Win32OpenGLImpl final +{ + HDC m_deviceContext; + HGLRC m_renderingContext; + HWND m_windowHandle; + + GLuint VAO; + GLuint shaderProgram; +}; + +void ClearBuffers(const Win32OpenGLImpl&) +{ + glClearColor(0.129f, 0.586f, 0.949f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); } -void Win32OpenGLImpl::DrawScene() +void Present(const Win32OpenGLImpl& opengl) +{ + SwapBuffers(opengl.m_deviceContext); +} + +void DrawScene(const Win32OpenGLImpl&) { glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr); } +void Destroy(const Win32OpenGLImpl& opengl) +{ + glDeleteShader(opengl.shaderProgram); + wglMakeCurrent(nullptr, nullptr); + wglDeleteContext(opengl.m_renderingContext); +} } // namespace ogl_starter -std::unique_ptr oglsCreateOpenGL(ogl_starter::OpenGLCreateParams params) +ogl_starter::OpenGL oglsCreateOpenGL(ogl_starter::OpenGLCreateParams params) { - auto result = std::make_unique(); - if (!result->init(params)) + auto hWnd = static_cast(params.nativeWindowHandle); + auto hInstance = GetModuleHandle(NULL); + + HWND dummyWindow = [hInstance]() -> HWND { - result = nullptr; + WNDCLASSEX wcex; + ZeroMemory(&wcex, sizeof(wcex)); + wcex.cbSize = sizeof(wcex); + wcex.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; + wcex.lpfnWndProc = DefWindowProc; + wcex.hInstance = hInstance; + wcex.hCursor = LoadCursor(NULL, IDC_ARROW); + wcex.lpszClassName = "DummyWNDClass"; + + if (RegisterClassEx(&wcex) == 0) + { + MessageBox(NULL, "registerClass() failed.", "Fatal Error", MB_ICONERROR); + return nullptr; + } + + return CreateWindow( + wcex.lpszClassName, "Dummy Window", + WS_OVERLAPPEDWINDOW, + 0, 0, + 1, 1, + nullptr, nullptr, + hInstance, nullptr); + }(); + + HDC dummyDC = GetDC(dummyWindow); + + PIXELFORMATDESCRIPTOR dummyPFD = {}; + dummyPFD.nSize = sizeof(dummyPFD); + dummyPFD.nVersion = 1; + dummyPFD.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; + dummyPFD.iPixelType = PFD_TYPE_RGBA; + dummyPFD.cColorBits = 32; + dummyPFD.cAlphaBits = 8; + dummyPFD.cDepthBits = 24; + dummyPFD.iLayerType = PFD_MAIN_PLANE; + + const int dummyPFDID = ChoosePixelFormat(dummyDC, &dummyPFD); + if (dummyPFDID == 0) + { + MessageBox(nullptr, "ChoosePixelFormat() failed.", "Fatal Error", MB_ICONERROR); } - return result; -} + if (SetPixelFormat(dummyDC, dummyPFDID, &dummyPFD) == false) + { + MessageBox(nullptr, "SetPixelFormat() failed.", "Fatal Error", MB_ICONERROR); + } + + HGLRC dummyRC = wglCreateContext(dummyDC); + + if (dummyRC == 0) + { + MessageBox(nullptr, "wglCreateContext() failed.", "Fatal Error", MB_ICONERROR); + } + + if (wglMakeCurrent(dummyDC, dummyRC) == false) + { + MessageBox(nullptr, "wglMakeCurrent() failed.", "Fatal Error", MB_ICONERROR); + } + + wglChoosePixelFormatARB = LoadGLProc("wglChoosePixelFormatARB"); + VALIDATEGLPROC(wglChoosePixelFormatARB); + + wglCreateContextAttribsARB = LoadGLProc("wglCreateContextAttribsARB"); + VALIDATEGLPROC(wglCreateContextAttribsARB); + + auto deviceContext = GetDC(hWnd); + + const int pixelAttribs[] = { + WGL_DRAW_TO_WINDOW_ARB, GL_TRUE, + WGL_SUPPORT_OPENGL_ARB, GL_TRUE, + WGL_DOUBLE_BUFFER_ARB, GL_TRUE, + WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB, + WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB, + WGL_COLOR_BITS_ARB, 32, + WGL_ALPHA_BITS_ARB, 8, + WGL_DEPTH_BITS_ARB, 24, + WGL_STENCIL_BITS_ARB, 8, + WGL_SAMPLE_BUFFERS_ARB, GL_TRUE, + WGL_SAMPLES_ARB, 4, + 0}; + + int pixelFormatID; + UINT numFormats; + const bool status = wglChoosePixelFormatARB(deviceContext, pixelAttribs, NULL, 1, &pixelFormatID, &numFormats); + + if (status == false || numFormats == 0) + { + MessageBox(nullptr, "wglChoosePixelFormatARB() failed.", "Fatal Error", MB_ICONERROR); + } + + PIXELFORMATDESCRIPTOR PFD; + DescribePixelFormat(deviceContext, pixelFormatID, sizeof(PFD), &PFD); + SetPixelFormat(deviceContext, pixelFormatID, &PFD); + + const int major_min = 4, minor_min = 6; + const int contextAttribs[] = { + WGL_CONTEXT_MAJOR_VERSION_ARB, major_min, + WGL_CONTEXT_MINOR_VERSION_ARB, minor_min, + WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB, + WGL_CONTEXT_LAYER_PLANE_ARB, 0, + WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_DEBUG_BIT_ARB, + 0}; + + auto renderingContext = wglCreateContextAttribsARB(deviceContext, 0, contextAttribs); + if (renderingContext == nullptr) + { + MessageBox(nullptr, "wglCreateContextAttribsARB() failed.", "Fatal Error", MB_ICONERROR); + } + + wglMakeCurrent(nullptr, nullptr); + wglDeleteContext(dummyRC); + ReleaseDC(dummyWindow, dummyDC); + DestroyWindow(dummyWindow); + + if (!wglMakeCurrent(deviceContext, renderingContext)) + { + MessageBox(nullptr, "wglMakeCurrent() failed.", "Fatal Error", MB_ICONERROR); + } + + ogl_starter::LoadGLFunctions(); + auto[VAO, shaderProgram] = ogl_starter::InitTriangleResources(); + + auto result = ogl_starter::Win32OpenGLImpl( + deviceContext, + renderingContext, + hWnd, + VAO, + shaderProgram + ); + + return std::move(result); +} \ No newline at end of file diff --git a/src/platform/win32_window.cpp b/src/platform/win32_window.cpp index 8a94cf6..c76655c 100644 --- a/src/platform/win32_window.cpp +++ b/src/platform/win32_window.cpp @@ -1,4 +1,4 @@ -#include "../window.h" +#include "window.h" #ifndef NOMINMAX #define NOMINMAX @@ -23,56 +23,72 @@ static LRESULT CALLBACK WindowProc(HWND window, UINT message, WPARAM wParam, LPA default: return DefWindowProc(window, message, wParam, lParam); } - return 0; } namespace ogl_starter { -class Win32WindowImpl final : public Window +struct Win32WindowImpl final { -public: - Win32WindowImpl(); - - Win32WindowImpl(const Win32WindowImpl&) = delete; - Win32WindowImpl& operator=(const Win32WindowImpl&) = delete; - - bool init(WindowCreateParams params); - void PumpMessages() override; - bool ShouldClose() const override; - void* GetNativeHandle() const override; - - HINSTANCE hInstance; - HWND hWnd; + HINSTANCE m_hInstance; + HWND m_hWnd; bool m_close; }; -Win32WindowImpl::Win32WindowImpl() - : hInstance(GetModuleHandle(NULL)) - , hWnd(nullptr) - , m_close(false) +void PumpMessages(Win32WindowImpl& window) { + MSG message = {}; + if (GetMessage(&message, NULL, 0, 0) != 0) + { + TranslateMessage(&message); + DispatchMessage(&message); + } + else + { + // GetMessage returned WM_QUIT + window.m_close = true; + } } -bool Win32WindowImpl::init(WindowCreateParams params) +bool ShouldClose(const Win32WindowImpl& window) { + return window.m_close; +} + +void* GetNativeHandle(const Win32WindowImpl& window) +{ + return static_cast(window.m_hWnd); +} +} // namespace ogl_starter + +ogl_starter::Window oglsCreateWindow(ogl_starter::WindowCreateParams params) +{ + const char className[] = "Win32WindowImpl"; + HINSTANCE hInstance = GetModuleHandle(NULL); + ogl_starter::Win32WindowImpl result = { + .m_hInstance = nullptr, + .m_hWnd = nullptr, + .m_close = true}; + WNDCLASSEX wc = {}; + wc.cbSize = sizeof(WNDCLASSEX); wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; wc.lpfnWndProc = WindowProc; wc.hInstance = hInstance; + wc.lpszClassName = className; wc.hCursor = LoadCursor(NULL, IDC_ARROW); - wc.lpszClassName = "WindowClass"; - if (RegisterClassEx(&wc) == NULL) + if (RegisterClassEx(&wc) == 0) { - MessageBox(nullptr, "Call to RegisterClass failed", NULL, MB_OK); - return false; + MessageBox(nullptr, "Call to RegisterClass failed", "Fatal Error", MB_OK); + return result; } - hWnd = CreateWindow( - wc.lpszClassName, + HWND window = CreateWindowEx( + 0, + className, params.name, WS_OVERLAPPEDWINDOW, params.x, params.y, params.width, params.height, @@ -81,49 +97,17 @@ bool Win32WindowImpl::init(WindowCreateParams params) hInstance, nullptr); - if (hWnd == nullptr) + if (window == nullptr) { - MessageBox(nullptr, "Call to CreateWindow failed", nullptr, MB_OK); - return false; + MessageBox(nullptr, "Call to CreateWindow failed", "Fatal Error", MB_OK); + return result; } - ShowWindow(hWnd, SW_SHOW); - return true; -} + ShowWindow(window, SW_SHOW); -void Win32WindowImpl::PumpMessages() -{ - MSG msg = {}; - while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) - { - if (msg.message == WM_QUIT) - { - m_close = true; - } - - TranslateMessage(&msg); - DispatchMessage(&msg); - } -} - -bool Win32WindowImpl::ShouldClose() const -{ - return m_close; -} - -void* Win32WindowImpl::GetNativeHandle() const -{ - return static_cast(hWnd); -} -} // namespace ogl_starter - -std::unique_ptr oglsCreateWindow(ogl_starter::WindowCreateParams params) -{ - auto result = std::make_unique(); - if (result->init(params) == false) - { - result = nullptr; - } + result.m_hInstance = hInstance; + result.m_hWnd = window; + result.m_close = false; return result; } \ No newline at end of file diff --git a/src/window.h b/src/window.h index 3b71575..7a818d5 100644 --- a/src/window.h +++ b/src/window.h @@ -14,21 +14,56 @@ struct WindowCreateParams const char* name; }; +template +concept IsWindow = requires(T t) { + PumpMessages(t); + ShouldClose(t); + GetNativeHandle(t); +}; + class Window { public: - virtual ~Window() = default; - virtual void PumpMessages() = 0; - virtual bool ShouldClose() const = 0; - virtual void* GetNativeHandle() const = 0; - - template< typename T > - T NativeHandle() const + template + Window(T&& t) + : self{std::make_unique>(std::move(t))} { - return static_cast(GetNativeHandle()); } + + ~Window() = default; + Window(Window&&) = default; + Window& operator=(Window&&) = default; + Window(const Window&) = delete; + Window operator=(const Window&) = delete; + + friend void PumpMessages(Window& window) { window.self->PumpMessages_(); } + friend bool ShouldClose(const Window& window) { return window.self->ShouldClose_(); } + friend void* GetNativeHandle(const Window& window) { return window.self->GetNativeHandle_(); } + +private: + struct concept_t + { + virtual ~concept_t() = default; + virtual void PumpMessages_() = 0; + virtual bool ShouldClose_() const = 0; + virtual void* GetNativeHandle_() const = 0; + }; + + template + struct model_t final : concept_t + { + model_t(T&& data) : m_data(std::move(data)) {} + + virtual void PumpMessages_() override { PumpMessages(m_data); } + virtual bool ShouldClose_() const override { return ShouldClose(m_data); } + virtual void* GetNativeHandle_() const override { return GetNativeHandle(m_data); } + + T m_data; + }; + + std::unique_ptr self; }; -} +} // namespace starter_window -std::unique_ptr oglsCreateWindow(ogl_starter::WindowCreateParams params); +ogl_starter::Window oglsCreateWindow(ogl_starter::WindowCreateParams params);