Update example to use type erased polymorphism

This commit is contained in:
McMassiveNZ
2023-07-03 22:51:08 +02:00
parent dd43799890
commit e2a1f86ac8
8 changed files with 370 additions and 340 deletions

View File

@@ -29,7 +29,7 @@ add_executable(
${SOURCE_FILES} ${SOURCE_FILES}
) )
target_include_directories(${current_target} PUBLIC ${CMAKE_SOURCE_DIR}/external) target_include_directories(${current_target} PUBLIC . ${CMAKE_SOURCE_DIR}/external)
if(MSVC) if(MSVC)
target_link_libraries(${current_target} PUBLIC opengl32.lib) target_link_libraries(${current_target} PUBLIC opengl32.lib)

View File

@@ -14,19 +14,20 @@ auto main() -> int
}); });
auto renderer = oglsCreateOpenGL({ auto renderer = oglsCreateOpenGL({
.nativeWindowHandle = window->GetNativeHandle(), .nativeWindowHandle = GetNativeHandle(window),
.versionMajor = 4, .versionMajor = 4,
.versionMinor = 6 .versionMinor = 6
}); });
while (!window->ShouldClose()) while (!ShouldClose(window))
{ {
window->PumpMessages(); PumpMessages(window);
renderer->ClearBuffers(); ClearBuffers(renderer);
renderer->DrawScene(); DrawScene(renderer);
renderer->SwapBuffers(); Present(renderer);
} }
Destroy(renderer);
return 0; return 0;
} }

View File

@@ -4,7 +4,6 @@
namespace ogl_starter namespace ogl_starter
{ {
class Window;
struct OpenGLCreateParams struct OpenGLCreateParams
{ {
@@ -13,16 +12,60 @@ struct OpenGLCreateParams
int versionMinor; int versionMinor;
}; };
template <typename T>
concept IsOpenGL = requires(T t) {
ClearBuffers(t);
Present(t);
DrawScene(t);
};
class OpenGL class OpenGL
{ {
public: public:
virtual ~OpenGL() = default; template <IsOpenGL T>
virtual void ClearBuffers() = 0; OpenGL(T&& t)
virtual void SwapBuffers() = 0; : self{std::make_unique<model_t<T>>(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 <typename T>
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<concept_t> self;
}; };
} // namespace ogl_starter } // namespace ogl_starter
std::unique_ptr<ogl_starter::OpenGL> oglsCreateOpenGL(ogl_starter::OpenGLCreateParams params); ogl_starter::OpenGL oglsCreateOpenGL(ogl_starter::OpenGLCreateParams params);

View File

@@ -1,26 +1,29 @@
#include "../opengl.h" #include "opengl.h"
namespace ogl_starter 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 } // namespace ogl_starter
std::unique_ptr<ogl_starter::OpenGL> oglsCreateOpenGL(ogl_starter::OpenGLCreateParams) ogl_starter::OpenGL oglsCreateOpenGL(ogl_starter::OpenGLCreateParams)
{ {
auto result = std::make_unique<ogl_starter::NullOpenGLImpl>(); return ogl_starter::NullOpenGLImpl{};
return result;
} }

View File

@@ -1,29 +1,29 @@
#include "../window.h" #include "window.h"
namespace ogl_starter 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 void PumpMessages(NullWindowImpl&)
std::unique_ptr<ogl_starter::Window> oglsCreateWindow(ogl_starter::WindowCreateParams)
{ {
auto result = std::make_unique<ogl_starter::NullWindowImpl>(); }
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{};
} }

View File

@@ -1,10 +1,13 @@
#include "../opengl.h" #include "opengl.h"
#include "../window.h"
#ifndef WIN32_LEAN_AND_MEAN #ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#endif #endif
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <Windows.h> #include <Windows.h>
#include <GL/GL.h> #include <GL/GL.h>
#include "GL/glext.h" #include "GL/glext.h"
@@ -18,11 +21,10 @@ static T LoadGLProc(const char* functionName)
} }
#define VALIDATEGLPROC(proc) \ #define VALIDATEGLPROC(proc) \
if (!proc) \ if (!proc) \
{ \ { \
MessageBox(NULL, "Failed to load " #proc, "Fatal Error", MB_ICONERROR); \ MessageBox(NULL, "Failed to load " #proc, "Fatal Error", MB_ICONERROR); \
return false; \ }
}
static PFNWGLGETEXTENSIONSSTRINGEXTPROC wglGetExtensionsStringEXT; static PFNWGLGETEXTENSIONSSTRINGEXTPROC wglGetExtensionsStringEXT;
static PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB; static PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB;
@@ -87,203 +89,7 @@ static void LoadGLFunctions()
glBufferData = LoadGLProc<PFNGLBUFFERDATAPROC>("glBufferData"); glBufferData = LoadGLProc<PFNGLBUFFERDATAPROC>("glBufferData");
} }
class Win32OpenGLImpl final : public OpenGL static std::pair<GLuint, GLuint> InitTriangleResources()
{
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<HWND>(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<PFNWGLCHOOSEPIXELFORMATARBPROC>("wglChoosePixelFormatARB");
VALIDATEGLPROC(wglChoosePixelFormatARB);
wglCreateContextAttribsARB = LoadGLProc<PFNWGLCREATECONTEXTATTRIBSARBPROC>("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()
{ {
float vertices[] = { float vertices[] = {
0.5f, 0.5f, 0.0f, 0.5f, 0.5f, 0.0f,
@@ -297,6 +103,7 @@ void Win32OpenGLImpl::initTriangleResources()
1, 2, 3 1, 2, 3
}; };
GLuint VAO;
glGenVertexArrays(1, &VAO); glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO); glBindVertexArray(VAO);
@@ -356,7 +163,7 @@ void Win32OpenGLImpl::initTriangleResources()
printf("Fragment Shader Compilation Failed\n %s\n", infoLog); printf("Fragment Shader Compilation Failed\n %s\n", infoLog);
} }
shaderProgram = glCreateProgram(); GLuint shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, VS); glAttachShader(shaderProgram, VS);
glAttachShader(shaderProgram, FS); glAttachShader(shaderProgram, FS);
glLinkProgram(shaderProgram); glLinkProgram(shaderProgram);
@@ -372,22 +179,179 @@ void Win32OpenGLImpl::initTriangleResources()
glDeleteShader(VS); glDeleteShader(VS);
glDeleteShader(FS); 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); 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 } // namespace ogl_starter
std::unique_ptr<ogl_starter::OpenGL> oglsCreateOpenGL(ogl_starter::OpenGLCreateParams params) ogl_starter::OpenGL oglsCreateOpenGL(ogl_starter::OpenGLCreateParams params)
{ {
auto result = std::make_unique<ogl_starter::Win32OpenGLImpl>(); auto hWnd = static_cast<HWND>(params.nativeWindowHandle);
if (!result->init(params)) 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 result; 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);
}
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<PFNWGLCHOOSEPIXELFORMATARBPROC>("wglChoosePixelFormatARB");
VALIDATEGLPROC(wglChoosePixelFormatARB);
wglCreateContextAttribsARB = LoadGLProc<PFNWGLCREATECONTEXTATTRIBSARBPROC>("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);
} }

View File

@@ -1,4 +1,4 @@
#include "../window.h" #include "window.h"
#ifndef NOMINMAX #ifndef NOMINMAX
#define NOMINMAX #define NOMINMAX
@@ -23,56 +23,72 @@ static LRESULT CALLBACK WindowProc(HWND window, UINT message, WPARAM wParam, LPA
default: default:
return DefWindowProc(window, message, wParam, lParam); return DefWindowProc(window, message, wParam, lParam);
} }
return 0; return 0;
} }
namespace ogl_starter namespace ogl_starter
{ {
class Win32WindowImpl final : public Window struct Win32WindowImpl final
{ {
public: HINSTANCE m_hInstance;
Win32WindowImpl(); HWND m_hWnd;
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;
bool m_close; bool m_close;
}; };
Win32WindowImpl::Win32WindowImpl() void PumpMessages(Win32WindowImpl& window)
: hInstance(GetModuleHandle(NULL))
, hWnd(nullptr)
, m_close(false)
{ {
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<void*>(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 = {}; WNDCLASSEX wc = {};
wc.cbSize = sizeof(WNDCLASSEX); wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
wc.lpfnWndProc = WindowProc; wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance; wc.hInstance = hInstance;
wc.lpszClassName = className;
wc.hCursor = LoadCursor(NULL, IDC_ARROW); 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); MessageBox(nullptr, "Call to RegisterClass failed", "Fatal Error", MB_OK);
return false; return result;
} }
hWnd = CreateWindow( HWND window = CreateWindowEx(
wc.lpszClassName, 0,
className,
params.name, params.name,
WS_OVERLAPPEDWINDOW, WS_OVERLAPPEDWINDOW,
params.x, params.y, params.width, params.height, params.x, params.y, params.width, params.height,
@@ -81,49 +97,17 @@ bool Win32WindowImpl::init(WindowCreateParams params)
hInstance, hInstance,
nullptr); nullptr);
if (hWnd == nullptr) if (window == nullptr)
{ {
MessageBox(nullptr, "Call to CreateWindow failed", nullptr, MB_OK); MessageBox(nullptr, "Call to CreateWindow failed", "Fatal Error", MB_OK);
return false; return result;
} }
ShowWindow(hWnd, SW_SHOW); ShowWindow(window, SW_SHOW);
return true;
}
void Win32WindowImpl::PumpMessages() result.m_hInstance = hInstance;
{ result.m_hWnd = window;
MSG msg = {}; result.m_close = false;
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<void*>(hWnd);
}
} // namespace ogl_starter
std::unique_ptr<ogl_starter::Window> oglsCreateWindow(ogl_starter::WindowCreateParams params)
{
auto result = std::make_unique<ogl_starter::Win32WindowImpl>();
if (result->init(params) == false)
{
result = nullptr;
}
return result; return result;
} }

View File

@@ -14,21 +14,56 @@ struct WindowCreateParams
const char* name; const char* name;
}; };
template <typename T>
concept IsWindow = requires(T t) {
PumpMessages(t);
ShouldClose(t);
GetNativeHandle(t);
};
class Window class Window
{ {
public: public:
virtual ~Window() = default; template <IsWindow T>
virtual void PumpMessages() = 0; Window(T&& t)
virtual bool ShouldClose() const = 0; : self{std::make_unique<model_t<T>>(std::move(t))}
virtual void* GetNativeHandle() const = 0;
template< typename T >
T NativeHandle() const
{ {
return static_cast<T>(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 <typename T>
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<concept_t> self;
}; };
} } // namespace starter_window
std::unique_ptr<ogl_starter::Window> oglsCreateWindow(ogl_starter::WindowCreateParams params); ogl_starter::Window oglsCreateWindow(ogl_starter::WindowCreateParams params);