├── Source ├── msvc-build.bat ├── msvc-build-crinkler.bat ├── mingw32-build.bat ├── mingw64-build.bat ├── 4kGL.h └── 4kGL.cpp ├── Documentation ├── License └── Changelog └── README.md /Source/msvc-build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | REM Build a 32 or 64 bit executable using MSVC 4 | 5 | cl /c /W4 /O1 /Os /GS- 4kGL.cpp 6 | 7 | link /NODEFAULTLIB /ENTRY:EntryPoint ^ 8 | /SUBSYSTEM:windows ^ 9 | 4kGL.obj gdi32.lib kernel32.lib opengl32.lib user32.lib winmm.lib 10 | 11 | del *.obj 12 | 13 | -------------------------------------------------------------------------------- /Documentation/License: -------------------------------------------------------------------------------- 1 | 2 | LICENSE 3 | 4 | Permission is hereby granted, free of charge, to anyone 5 | obtaining a copy of this document and accompanying files, 6 | to do whatever they want with them without any restriction, 7 | including, but not limited to, copying, modification and redistribution. 8 | 9 | NO WARRANTY OF ANY KIND IS PROVIDED. 10 | 11 | -------------------------------------------------------------------------------- /Source/msvc-build-crinkler.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | REM Build a 32 bit executable using MSVC and Crinkler 4 | 5 | cl /c /W4 /O1 /Os /GS- 4kGL.cpp 6 | 7 | crinkler /ENTRY:EntryPoint ^ 8 | /SUBSYSTEM:windows ^ 9 | /COMPMODE:SLOW ^ 10 | /HASHSIZE:100 ^ 11 | /UNSAFEIMPORT ^ 12 | /OUT:4kGL.exe ^ 13 | 4kGL.obj gdi32.lib kernel32.lib opengl32.lib user32.lib winmm.lib 14 | 15 | del *.obj 16 | 17 | -------------------------------------------------------------------------------- /Source/mingw32-build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | REM Build a 32 bit executable using MinGW32 4 | 5 | g++ 4kGL.cpp -O2 -s ^ 6 | -Wall -Wextra -Wpedantic -ansi -Wmissing-declarations ^ 7 | -nostdlib -e __Z10EntryPointv ^ 8 | -fno-exceptions -fomit-frame-pointer ^ 9 | -mwindows ^ 10 | -lgdi32 -lkernel32 -lopengl32 -luser32 -lwinmm ^ 11 | -o 4kGL 12 | 13 | -------------------------------------------------------------------------------- /Source/mingw64-build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | REM Build a 64 bit executable using MinGW64 4 | 5 | g++ 4kGL.cpp -O2 -s ^ 6 | -Wall -Wextra -Wpedantic -ansi -Wmissing-declarations ^ 7 | -nostdlib -e _Z10EntryPointv ^ 8 | -fno-exceptions -fomit-frame-pointer ^ 9 | -mwindows ^ 10 | -lgdi32 -lkernel32 -lopengl32 -luser32 -lwinmm ^ 11 | -o 4kGL 12 | 13 | -------------------------------------------------------------------------------- /Source/4kGL.h: -------------------------------------------------------------------------------- 1 | 2 | // Window properties: 3 | 4 | #define WINDOW_TITLE TEXT("4kGL") 5 | #define WINDOW_WIDTH 640 6 | #define WINDOW_HEIGHT 480 7 | 8 | 9 | // Minimum amount of milliseconds for each update cycle: 10 | 11 | #define STEP_RATE 13 12 | 13 | 14 | // Globals: 15 | 16 | HINSTANCE g_hInstance; 17 | HWND g_hWnd; 18 | 19 | HDC g_hDC; 20 | HGLRC g_hGLRC; 21 | 22 | 23 | // Function prototypes: 24 | 25 | LRESULT CALLBACK WndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); 26 | 27 | bool Initialize (); 28 | void Shutdown (UINT uExitCode); 29 | void Update (); 30 | void Loop (); 31 | void ResizeClientWindow (HWND hWnd, UINT uWidth, UINT uHeight); 32 | void EntryPoint (); 33 | 34 | -------------------------------------------------------------------------------- /Documentation/Changelog: -------------------------------------------------------------------------------- 1 | 2 | CHANGELOG 3 | 4 | * 2016/02/02: 5 | 6 | - Revised. Working on latest MinGW 5.3.0. 7 | 8 | * 2014/08/22: 9 | 10 | - Tested on Clang (LLVM SVN r215932). 11 | 12 | - Added -Wmissing-declarations. 13 | 14 | * 2014/07/21: 15 | 16 | - Revised. Working on MinGW 4.9.0 and MSVC 2014 CTP. 17 | No changes needed. 18 | 19 | * 2013/12/02: 20 | 21 | - Do not handle ESC to quit. 22 | 23 | * 2013/09/25: 24 | 25 | - Set warnings to the highest level on both MSVC and MinGW. 26 | 27 | - Include build scripts for MinGW32 and MinGW64. 28 | 29 | - Do robust error handling and properly release resources. 30 | Not doing it only saves about 150 bytes. 31 | 32 | - Always call ExitProcess() ourselves. 33 | 34 | * 2013/07/08: 35 | 36 | - Use adaptive sleeping. 37 | 38 | - Fixed GAME_STEP. Default to 13 (about 75 FPS). 39 | 40 | * 2013/07/04: 41 | 42 | - First version. 43 | 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ## About 3 | 4 | This is the OpenGL counterpart to [4k][], a small framework intended for 4k 5 | games or demos. It does the basics of creating a window, setting up an OpenGL 6 | context and running a game loop at a fixed rate. It provides the bare minimum 7 | but it's easy to extend. 8 | 9 | Using [Crinkler][] it compiles to a 1kb executable. 10 | 11 | See also [4kGL-Example][] for a simple demo and [4k][] for the same thing 12 | using GDI instead of OpenGL. 13 | 14 | ## Compiling 15 | 16 | There are batch files the [Source][] folder that compile the project 17 | using MSVC (with the default linker or [Crinkler][]) and MinGW. It has been 18 | tested using MSVC 2010/2012/2013/2014 CTP and MinGW 4.8.1+ on Windows 7 and 8 19 | (both x86 and x86-64). 20 | 21 | It's also possible to build it using other compilers, such as Clang (LLVM 3.6+) 22 | or to cross-compile (e.g. from Debian using MinGW), but this is not supported. 23 | 24 | ## Alternatives 25 | 26 | There is another 1k/4k framework done by [Iñigo Quilez][]. It's more mature 27 | and has been used in more productions than this one. The main difference between 28 | both is that iq's framework uses every known trick to keep the final size small. 29 | This one tries to be clean and simple without ugly hacks. 30 | 31 | ## Status 32 | 33 | This program is finished! 34 | 35 | 4kGL is feature-complete and has no known bugs. Unless issues are reported 36 | I plan no further development on it other than maintenance. 37 | 38 | ## License 39 | 40 | Like all my hobby projects, this is Free Software. See the [Documentation][] 41 | folder for more information. No warranty though. 42 | 43 | [4kGL-Example]: https://github.com/Beluki/4kGL-Example 44 | [4k]: https://github.com/Beluki/4k 45 | 46 | [Crinkler]: http://www.crinkler.net 47 | [Iñigo Quilez]: http://www.iquilezles.org/www/material/isystem1k4k/isystem1k4k.htm 48 | 49 | [Documentation]: Documentation 50 | [Source]: Source 51 | 52 | -------------------------------------------------------------------------------- /Source/4kGL.cpp: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | 4kGL. 4 | A minimal starting point for 4k OpenGL demo/game programming. 5 | */ 6 | 7 | 8 | #define STRICT 9 | #define WIN32_LEAN_AND_MEAN 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include "4kGL.h" 16 | 17 | 18 | #if defined(_MSC_VER) 19 | extern "C" int _fltused = 0; 20 | #endif 21 | 22 | 23 | // Message handler: 24 | 25 | LRESULT CALLBACK 26 | WndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { 27 | switch (uMsg) { 28 | case WM_CLOSE: 29 | PostQuitMessage(0); 30 | return 0; 31 | default: 32 | return DefWindowProc(hWnd, uMsg, wParam, lParam); 33 | } 34 | } 35 | 36 | 37 | // Initialization and shutdown: 38 | 39 | bool 40 | Initialize () { 41 | 42 | // no WinMain so get the module handle: 43 | g_hInstance = GetModuleHandle(NULL); 44 | if (g_hInstance == NULL) 45 | return false; 46 | 47 | // register the window class: 48 | WNDCLASS wc; 49 | 50 | wc.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW; 51 | wc.lpfnWndProc = WndProc; 52 | wc.cbClsExtra = 0; 53 | wc.cbWndExtra = 0; 54 | wc.hInstance = g_hInstance; 55 | wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); 56 | wc.hCursor = LoadCursor(NULL, IDC_ARROW); 57 | wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1); 58 | wc.lpszMenuName = NULL; 59 | wc.lpszClassName = WINDOW_TITLE; 60 | 61 | if (RegisterClass(&wc) == 0) 62 | return false; 63 | 64 | // create the window: 65 | g_hWnd = CreateWindow( 66 | WINDOW_TITLE, // class name 67 | WINDOW_TITLE, // title 68 | WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, // style 69 | CW_USEDEFAULT, CW_USEDEFAULT, // position 70 | CW_USEDEFAULT, CW_USEDEFAULT, // size 71 | NULL, // no parent 72 | NULL, // no menu 73 | g_hInstance, // instance 74 | NULL // no special 75 | ); 76 | 77 | if (g_hWnd == NULL) 78 | return false; 79 | 80 | // setup OpenGL: 81 | g_hDC = GetDC(g_hWnd); 82 | if (g_hDC == NULL) 83 | return false; 84 | 85 | PIXELFORMATDESCRIPTOR pfd = { 86 | sizeof(PIXELFORMATDESCRIPTOR), 87 | 1, // version number 88 | PFD_DRAW_TO_WINDOW | // support window 89 | PFD_SUPPORT_OPENGL | // support OpenGL 90 | PFD_DOUBLEBUFFER, // double buffered 91 | PFD_TYPE_RGBA, // RGBA type 92 | 32, // 32-bit color depth 93 | 0, 0, 0, 0, 0, 0, // color bits ignored 94 | 0, // no alpha buffer 95 | 0, // shift bit ignored 96 | 0, // no accumulation buffer 97 | 0, 0, 0, 0, // accumulation bits ignored 98 | 32, // 32-bit z buffer 99 | 0, // no stencil buffer 100 | 0, // no auxiliary buffer 101 | PFD_MAIN_PLANE, // main layer 102 | 0, // reserved 103 | 0, 0, 0, // layer masks ignored 104 | }; 105 | 106 | int pixelFormat = ChoosePixelFormat(g_hDC, &pfd); 107 | if (pixelFormat == 0) 108 | return false; 109 | 110 | if (SetPixelFormat(g_hDC, pixelFormat, &pfd) == FALSE) 111 | return false; 112 | 113 | g_hGLRC = wglCreateContext(g_hDC); 114 | if (g_hGLRC == NULL) 115 | return false; 116 | 117 | if (wglMakeCurrent(g_hDC, g_hGLRC) == FALSE) 118 | return false; 119 | 120 | return true; 121 | } 122 | 123 | 124 | void 125 | Shutdown (UINT uExitCode) { 126 | 127 | // release OpenGL context: 128 | if (g_hGLRC != NULL) { 129 | wglMakeCurrent(NULL, NULL); 130 | wglDeleteContext(g_hGLRC); 131 | } 132 | 133 | if (g_hDC != NULL) 134 | ReleaseDC(g_hWnd, g_hDC); 135 | 136 | // destroy the window and unregister the class: 137 | if (g_hWnd != NULL) 138 | DestroyWindow(g_hWnd); 139 | 140 | WNDCLASS wc; 141 | if (GetClassInfo(g_hInstance, WINDOW_TITLE, &wc) != 0) 142 | UnregisterClass(WINDOW_TITLE, g_hInstance); 143 | 144 | // without WinMainCRTStartup() we must exit the process ourselves: 145 | ExitProcess(uExitCode); 146 | } 147 | 148 | 149 | // Update step and main loop: 150 | 151 | void 152 | Update () { 153 | 154 | // just paint the whole back buffer in blue: 155 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 156 | glClearColor(0.392f, 0.584f, 0.929f, 0.0f); 157 | 158 | } 159 | 160 | 161 | void 162 | Loop () { 163 | MSG msg; 164 | bool done = false; 165 | 166 | // this game loop is far from ideal since rendering/logic are tied 167 | // and timeGetTime() and Sleep() are not precise, but it is 168 | // small and works well enough for simple 4k games/demos 169 | 170 | // for a better game loop see: 171 | // 172 | 173 | while (!done) { 174 | DWORD dwStart = timeGetTime(); 175 | 176 | // poll windows events: 177 | while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) != 0) { 178 | if (msg.message == WM_QUIT) 179 | done = true; 180 | TranslateMessage(&msg); 181 | DispatchMessage(&msg); 182 | } 183 | 184 | // update and swap buffers: 185 | Update(); 186 | SwapBuffers(g_hDC); 187 | 188 | // sleep until next step: 189 | DWORD dwDelta = timeGetTime() - dwStart; 190 | if (dwDelta < STEP_RATE) { 191 | Sleep(STEP_RATE - dwDelta); 192 | } 193 | } 194 | } 195 | 196 | 197 | // A helper to resize the window with respect to the client area: 198 | 199 | void 200 | ResizeClientWindow (HWND hWnd, UINT uWidth, UINT uHeight) { 201 | RECT rcClient, rcWindow; 202 | 203 | GetClientRect(hWnd, &rcClient); 204 | GetWindowRect(hWnd, &rcWindow); 205 | 206 | MoveWindow(hWnd, 207 | rcWindow.left, 208 | rcWindow.top, 209 | uWidth + (rcWindow.right - rcWindow.left) - rcClient.right, 210 | uHeight + (rcWindow.bottom - rcWindow.top) - rcClient.bottom, 211 | FALSE); 212 | } 213 | 214 | 215 | // Entry point: 216 | 217 | void 218 | EntryPoint () { 219 | if (!Initialize()) { 220 | MessageBox(NULL, "Initialization failed.", "Error", MB_OK | MB_ICONERROR); 221 | Shutdown(1); 222 | } 223 | 224 | ResizeClientWindow(g_hWnd, WINDOW_WIDTH, WINDOW_HEIGHT); 225 | ShowWindow(g_hWnd, SW_SHOW); 226 | Loop(); 227 | Shutdown(0); 228 | } 229 | 230 | --------------------------------------------------------------------------------