Update example to use type erased polymorphism
This commit is contained in:
@@ -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)
|
||||||
|
|||||||
13
src/main.cpp
13
src/main.cpp
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
55
src/opengl.h
55
src/opengl.h
@@ -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);
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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{};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 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<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);
|
||||||
}
|
}
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
55
src/window.h
55
src/window.h
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user