Skip to content

Instantly share code, notes, and snippets.

@davepkennedy
Created May 5, 2018 07:56
Show Gist options
  • Save davepkennedy/8979254ef8fc8b86965dcb38e60e3ce2 to your computer and use it in GitHub Desktop.
Save davepkennedy/8979254ef8fc8b86965dcb38e60e3ce2 to your computer and use it in GitHub Desktop.
Basic code for GL4 on Window
#pragma once
#include <Windows.h>
#define DECLARE_APP(WinType) \
int CALLBACK wWinMain( \
HINSTANCE hInst, \
HINSTANCE hPrevInst, \
LPWSTR lpCmdLine, \
int nCmdShow \
) { \
Application<WinType> app; \
return app.Run(); \
}
template <typename WINDOW>
class Application {
private:
WINDOW* window;
void Setup()
{
WINDOW dummy;
dummy.Register();
dummy.Create();
if (dummy.CanUpgrade()) {
window = new WINDOW(&dummy);
}
else {
window = new WINDOW;
}
window->Register();
window->Create();
window->ShowWindow(SW_SHOW);
window->Update();
}
void Loop()
{
MSG msg;
ZeroMemory(&msg, sizeof(MSG));
while (true) {
if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT) {
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
if (window->MakeCurrent()) {
window->Tick();
window->Render();
window->SwapBuffers();
}
}
}
void Teardown()
{
window->Shutdown();
delete window;
}
public:
int Run()
{
Setup();
Loop();
Teardown();
return 0;
}
};
#include "GLWindow.h"
#pragma comment (lib, "opengl32")
GLWindow::GLWindow()
: _lastTick(GetTickCount64())
, _createdVersion(1)
{
observe(WM_CREATE, [this](WPARAM, LPARAM) {
_deviceContext = GetDC(this->Handle());
this->SetupContext(_deviceContext);
_glContext = wglCreateContext(_deviceContext);
MakeCurrent();
LoadPrototypes();
Init();
});
SetupCommon();
}
int GLWindow::ChoosePixelFormat(const HDC hdc, const int multisamples, const bool stereo3D) {
FLOAT fAttributes[] = { 0, 0 };
int iAttributes[] = {
WGL_SAMPLE_BUFFERS_ARB, ((multisamples > 1) ? 1 : 0),
WGL_SAMPLES_ARB, multisamples,
WGL_DOUBLE_BUFFER_ARB, TRUE,
WGL_STENCIL_BITS_ARB, 8,
WGL_DEPTH_BITS_ARB, 24,
WGL_RED_BITS_ARB, 8,
WGL_BLUE_BITS_ARB, 8,
WGL_GREEN_BITS_ARB, 8,
WGL_ALPHA_BITS_ARB, 8,
WGL_STEREO_ARB, (stereo3D ? TRUE : FALSE),
0, 0
};
int pixelFormat;
UINT numFormats;
if (!wglChoosePixelFormatARB(hdc, iAttributes, fAttributes, 1, &pixelFormat, &numFormats)) {
return -1;
}
return pixelFormat;
}
GLWindow::GLWindow(const GLWindow* window)
: _lastTick(GetTickCount64())
, _createdVersion(window->Version())
{
observe(WM_CREATE, [this, window](WPARAM, LPARAM) {
LoadPrototypes();
_deviceContext = GetDC(this->Handle());
int pixelFormat = this->ChoosePixelFormat(_deviceContext, 0, false);
PIXELFORMATDESCRIPTOR pfd;
ZeroMemory(&pfd, sizeof(PIXELFORMATDESCRIPTOR));
DescribePixelFormat(_deviceContext, pixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &pfd);
if (!SetPixelFormat(_deviceContext, pixelFormat, &pfd)) {
ShowLastError();
}
const int attribs[] =
{
//WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
//WGL_CONTEXT_MINOR_VERSION_ARB, 0,
WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_DEBUG_BIT_ARB,
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
0, 0
};
_glContext = wglCreateContextAttribsARB(_deviceContext, nullptr, attribs);
MakeCurrent();
Init();
});
SetupCommon();
}
void GLWindow::Destroy()
{
wglDeleteContext(_glContext);
ReleaseDC(Handle(), _deviceContext);
}
void GLWindow::SetupCommon()
{
observe(WM_CLOSE, [](WPARAM, LPARAM) {PostQuitMessage(0); });
observe(WM_SIZE, [this](WPARAM wParam, LPARAM lParam) {
int width = LOWORD(lParam);
int height = HIWORD(lParam);
Resized(width, height);
});
}
GLWindow::~GLWindow()
{
wglDeleteContext(_glContext);
}
int GLWindow::Version() const
{
const char* version = (char*)glGetString(GL_VERSION);
int version_max, version_min;
sscanf_s(version, "%d.%d", &version_max, &version_min);
return version_max;
}
void GLWindow::SetupContext(HDC hDC) {
PIXELFORMATDESCRIPTOR pfd;
ZeroMemory(&pfd, sizeof(PIXELFORMATDESCRIPTOR));
pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
pfd.nSize = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW |
PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 32;
pfd.cDepthBits = 16;
pfd.iLayerType = PFD_MAIN_PLANE;
int format = ::ChoosePixelFormat(hDC, &pfd);
SetPixelFormat(hDC, format, &pfd);
}
BOOL GLWindow::MakeCurrent()
{
return ::wglMakeCurrent(_deviceContext, _glContext);
}
void GLWindow::Resized(int width, int height)
{
MakeCurrent();
glViewport(0, 0, width, height);
}
void GLWindow::SwapBuffers()
{
::SwapBuffers(_deviceContext);
}
void GLWindow::Init()
{
}
void GLWindow::Shutdown()
{
}
void GLWindow::Render()
{
}
void GLWindow::Tick()
{
ULONGLONG thisTick = GetTickCount64();
TimeElapsed(thisTick - _lastTick);
_lastTick = thisTick;
}
void GLWindow::LoadDll() {
_gldll = LoadLibrary(TEXT("opengl32.dll"));
}
void GLWindow::UnloadDll() {
FreeLibrary(_gldll);
}
LPVOID GLWindow::LoadFunction(const char* name) {
LPVOID ret = wglGetProcAddress(name);
if (!ret) {
ret = GetProcAddress(_gldll, name);
}
return ret;
}
void GLWindow::LoadPrototypes() {
LoadDll();
wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)LoadFunction("wglChoosePixelFormatARB");
wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)LoadFunction("wglCreateContextAttribsARB");
glGetString = (PFNGLGETSTRINGPROC)LoadFunction("glGetString");
glViewport = (PFNGLVIEWPORTPROC)LoadFunction("glViewport");
glClearBufferfv = (PFNGLCLEARBUFFERFVPROC)LoadFunction("glClearBufferfv");
glEnable = (PFNGLENABLEPROC)LoadFunction("glEnable");
glDepthFunc = (PFNGLDEPTHFUNCPROC)LoadFunction("glDepthFunc");
glGenBuffers = (PFNGLGENBUFFERSPROC)LoadFunction("glGenBuffers");
glBindBuffer = (PFNGLBINDBUFFERPROC)LoadFunction("glBindBuffer");
glBufferData = (PFNGLBUFFERDATAPROC)LoadFunction("glBufferData");
glGenVertexArrays = (PFNGLGENVERTEXARRAYSPROC)LoadFunction("glGenVertexArrays");
glBindVertexArray = (PFNGLBINDVERTEXARRAYPROC)LoadFunction("glBindVertexArray");
glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC)LoadFunction("glEnableVertexAttribArray");
glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC)LoadFunction("glVertexAttribPointer");
glCreateShader = (PFNGLCREATESHADERPROC)LoadFunction("glCreateShader");
glShaderSource = (PFNGLSHADERSOURCEPROC)LoadFunction("glShaderSource");
glCompileShader = (PFNGLCOMPILESHADERPROC)LoadFunction("glCompileShader");
glCreateProgram = (PFNGLCREATEPROGRAMPROC)LoadFunction("glCreateProgram");
glAttachShader = (PFNGLATTACHSHADERPROC)LoadFunction("glAttachShader");
glLinkProgram = (PFNGLLINKPROGRAMPROC)LoadFunction("glLinkProgram");
glClear = (PFNGLCLEARPROC)LoadFunction("glClear");
glUseProgram = (PFNGLUSEPROGRAMPROC)LoadFunction("glUseProgram");
glDrawArrays = (PFNGLDRAWARRAYSPROC)LoadFunction("glDrawArrays");
UnloadDll();
}
#pragma once
#include "Window.h"
#include "GL\glcorearb.h"
#include "GL\glext.h"
#include "GL\wglext.h"
class GLWindow :
public Window
{
private:
int _createdVersion;
HDC _deviceContext;
HGLRC _glContext;
ULONGLONG _lastTick;
HMODULE _gldll;
private:
void LoadDll();
void UnloadDll();
LPVOID LoadFunction(const char* name);
void SetupCommon();
protected:
void SetupContext(HDC hDC);
int ChoosePixelFormat(const HDC hdc, const int multisamples, const bool stereo3D);
void LoadPrototypes();
virtual void TimeElapsed(ULONGLONG elapsedTime) {}
void PrintShaderLog(GLuint shader) const;
void PrintShaderInfoLog(GLuint shader) const;
public:
GLWindow();
GLWindow(const GLWindow* window);
virtual ~GLWindow();
void Destroy();
BOOL MakeCurrent();
void Resized(int width, int height);
void Tick();
virtual void Init();
virtual void Shutdown();
virtual void Render();
void SwapBuffers();
inline BOOL CanUpgrade() const {
return (Version() > _createdVersion);
}
UINT ClassStyle() { return Window::ClassStyle() | CS_OWNDC; }
LPCTSTR ClassName() { return TEXT("GLWindow"); }
int Version() const;
protected:
PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB;
PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB;
PFNGLGETSTRINGPROC glGetString;
PFNGLVIEWPORTPROC glViewport;
PFNGLCLEARBUFFERFVPROC glClearBufferfv;
PFNGLENABLEPROC glEnable;
PFNGLDEPTHFUNCPROC glDepthFunc;
PFNGLGENBUFFERSPROC glGenBuffers;
PFNGLBINDBUFFERPROC glBindBuffer;
PFNGLBUFFERDATAPROC glBufferData;
PFNGLGENVERTEXARRAYSPROC glGenVertexArrays;
PFNGLBINDVERTEXARRAYPROC glBindVertexArray;
PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray;
PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer;
PFNGLCREATESHADERPROC glCreateShader;
PFNGLSHADERSOURCEPROC glShaderSource;
PFNGLCOMPILESHADERPROC glCompileShader;
PFNGLCREATEPROGRAMPROC glCreateProgram;
PFNGLATTACHSHADERPROC glAttachShader;
PFNGLLINKPROGRAMPROC glLinkProgram;
PFNGLCLEARPROC glClear;
PFNGLUSEPROGRAMPROC glUseProgram;
PFNGLDRAWARRAYSPROC glDrawArrays;
};
#pragma once
#include <map>
#include <functional>
template <typename MSG, typename ... ARGS>
class Observable {
private:
std::map<MSG, std::function<void(ARGS...)>> callbacks;
public:
void observe(MSG msg, std::function<void(ARGS...)> callback) {
callbacks[msg] = callback;
}
void invoke(MSG msg, ARGS... args) {
if (callbacks.find(msg) != callbacks.end()) {
callbacks[msg](std::forward<ARGS>(args)...);
}
}
};
#include "Window.h"
LRESULT CALLBACK Window::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
Window* window = nullptr;
if (msg == WM_CREATE) {
LPCREATESTRUCT lpCreateStruct = (LPCREATESTRUCT)lParam;
window = (Window*)lpCreateStruct->lpCreateParams;
window->_window = hWnd;
SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG)window);
}
else {
window = (Window*)GetWindowLongPtr(hWnd, GWLP_USERDATA);
}
if (window) {
window->invoke(msg, wParam, lParam);
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
Window::Window()
: _window(nullptr)
{
}
Window::~Window()
{
Close();
Destroy();
}
ATOM Window::Register()
{
WNDCLASSEX wcx;
::ZeroMemory(&wcx, sizeof(WNDCLASSEX));
wcx.cbSize = sizeof(WNDCLASSEX);
wcx.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcx.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcx.hIcon = LoadIcon(nullptr, IDI_APPLICATION);
wcx.hIconSm = wcx.hIcon;
wcx.hInstance = GetModuleHandle(nullptr);
wcx.lpfnWndProc = WndProc;
wcx.lpszClassName = ClassName();
wcx.lpszMenuName = nullptr;
wcx.style = ClassStyle();
return RegisterClassEx(&wcx);
}
void Window::Create()
{
CreateWindow(ClassName(), WindowName(), WindowStyle(),
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
nullptr, nullptr, GetModuleHandle(nullptr), this);
observe(WM_CLOSE, [](WPARAM, LPARAM) {PostQuitMessage(0); });
}
void Window::Update()
{
UpdateWindow(_window);
}
void Window::ShowWindow(int nCmdShow)
{
::ShowWindow(_window, nCmdShow);
}
void Window::Close()
{
if (_window) {
CloseWindow(_window);
}
}
void Window::Destroy()
{
if (_window) {
DestroyWindow(_window);
_window = nullptr;
}
}
SIZE Window::Size() const
{
RECT rect;
SIZE size = { 0, 0 };
if (GetWindowRect(_window, &rect)) {
size.cx = rect.right - rect.left;
size.cy = rect.bottom - rect.top;
}
return size;
}
void Window::ShowLastError() const
{
DWORD err = GetLastError();
// Translate ErrorCode to String.
LPTSTR error = nullptr;
if (::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
nullptr,
err,
0, (LPTSTR)&error,
0,
nullptr) == 0)
{
// Failed in translating.
}
// Display message.
MessageBox(nullptr, error, TEXT("Error"), MB_OK | MB_ICONWARNING);
// Free the buffer.
if (error)
{
::LocalFree(error);
}
}
#pragma once
#include <Windows.h>
#include "Observable.h"
class Window :
public Observable <UINT, WPARAM, LPARAM>
{
private:
HWND _window;
private:
static LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
protected:
inline HWND Handle() const { return _window; }
void ShowLastError() const;
public:
Window();
virtual ~Window();
ATOM Register();
void Create();
void Update();
void ShowWindow(int nCmdShow);
void Close();
virtual void Destroy();
SIZE Size() const;
virtual LPCTSTR ClassName() { return TEXT("BasicWindow"); }
virtual LPCTSTR WindowName() { return ClassName(); }
virtual UINT ClassStyle() { return CS_HREDRAW | CS_VREDRAW; }
virtual UINT WindowStyle() { return WS_OVERLAPPEDWINDOW; }
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment