summaryrefslogtreecommitdiff
path: root/code/win32_main.cpp
diff options
context:
space:
mode:
authortalha <talha@talhaamir.xyz>2023-08-30 11:04:00 +0500
committertalha <talha@talhaamir.xyz>2023-08-30 11:04:00 +0500
commitd980dcd2b66e4879989ce18291d044d5a4ffc902 (patch)
tree4f5fbd30d5152ffd21783eb02976bbaed6e0dd21 /code/win32_main.cpp
setting up font-rendering repo
Diffstat (limited to 'code/win32_main.cpp')
-rw-r--r--code/win32_main.cpp639
1 files changed, 639 insertions, 0 deletions
diff --git a/code/win32_main.cpp b/code/win32_main.cpp
new file mode 100644
index 0000000..4ea173d
--- /dev/null
+++ b/code/win32_main.cpp
@@ -0,0 +1,639 @@
+#include <windows.h>
+#include <direct.h>
+#include <glad/glad.h>
+#include <GLFW/glfw3.h>
+
+#define STB_TRUETYPE_IMPLEMENTATION
+#include "stb_truetype.h"
+#include <math.h>
+#include <stdio.h>
+#include <stdint.h>
+
+typedef uint8_t u8;
+typedef uint16_t u16;
+typedef uint32_t u32;
+typedef uint64_t u64;
+
+typedef int8_t i8;
+typedef int16_t i16;
+typedef int32_t i32;
+typedef int64_t i64;
+
+typedef u8 b8;
+typedef u16 b16;
+typedef u32 b32;
+
+typedef float r32;
+typedef double r64;
+
+#include "math.h"
+
+#define WIN_WIDTH 1280.0f
+#define WIN_HEIGHT 1024.0f
+#define internal static
+
+/**
+ * Text rendering todos:
+ * - Look into font-packing, what is that? why is it needed?
+ * - Look into generating a single bitmap and then extracting characters from the
+ * bitmap as needed
+ * - Same thing as what I do now, but not using all the bitmaps
+ * - SDF font rendering
+ * */
+internal i64 GlobalPerfCountFrequency;
+
+i8 PlatformDebugReadFile(char *FileName, void *FileBuffer, u32 MaxSize, LPDWORD BytesRead)
+{
+ // @todo: refactor function, check for closehandle error codes
+ HANDLE FileHandle = CreateFileA(FileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (FileHandle != INVALID_HANDLE_VALUE)
+ {
+ if (ReadFile(FileHandle, FileBuffer, MaxSize, BytesRead, NULL) != 0)
+ {
+ CloseHandle(FileHandle);
+ return 1;
+ }
+ else
+ {
+ // @todo: logging
+ CloseHandle(FileHandle);
+ return -2;
+ }
+ }
+ else
+ {
+ // @todo: logging
+ return -1;
+ }
+}
+
+void framebuffer_size_callback(GLFWwindow *window, i32 width, i32 height)
+{
+ glViewport(0, 0, width, height);
+}
+
+inline LARGE_INTEGER
+Win32GetWallClock(void)
+{
+ LARGE_INTEGER Result;
+ QueryPerformanceCounter(&Result);
+
+ return Result;
+}
+
+inline r32
+Win32GetSecondsElapsed(LARGE_INTEGER Start, LARGE_INTEGER End)
+{
+ r32 Result = (r32)(End.QuadPart - Start.QuadPart)/(r32)GlobalPerfCountFrequency;
+ return(Result);
+}
+
+#ifndef DEFAULT_ALIGNMENT
+#define DEFAULT_ALIGNMENT (2*sizeof(void *))
+#endif
+
+typedef struct DebugArena {
+ u8 *Buffer;
+ size_t Size;
+ size_t CurrOffset;
+ size_t PrevOffset;
+} DebugArena;
+
+bool IsPowerOfTwo(uintptr_t x)
+{
+ return (x & (x-1)) == 0;
+}
+
+uintptr_t AlignForward(uintptr_t ptr, size_t align)
+{
+ uintptr_t p, a, modulo;
+
+ assert(IsPowerOfTwo(align));
+
+ p = ptr;
+ a = (uintptr_t)align;
+ modulo = p & (a-1);
+
+ if (modulo != 0)
+ {
+ p += a - modulo;
+ }
+ return p;
+}
+
+void *ArenaAllocAlign(DebugArena *Alloc, size_t Size, size_t Align)
+{
+ uintptr_t CurrOffset = (uintptr_t)Alloc->Buffer + (uintptr_t)Alloc->CurrOffset;
+ uintptr_t AlignedOffset = AlignForward(CurrOffset, Align);
+ AlignedOffset -= (uintptr_t)(Alloc->Buffer);
+
+ if (AlignedOffset + Size < Alloc->Size)
+ {
+ void *Ptr = &Alloc->Buffer[AlignedOffset];
+ Alloc->PrevOffset = AlignedOffset;
+ Alloc->CurrOffset = AlignedOffset + Size;
+
+ ZeroMemory(Ptr, Size);
+ return Ptr;
+ }
+ return NULL;
+}
+
+void ArenaInit(DebugArena *Alloc, void* BackingBuffer, size_t Size)
+{
+ Alloc->Buffer = (u8 *)BackingBuffer;
+ Alloc->Size = Size;
+ Alloc->CurrOffset = 0;
+ Alloc->PrevOffset = 0;
+}
+
+void *ArenaAlloc(DebugArena *Alloc, size_t Size)
+{
+ return ArenaAllocAlign(Alloc, Size, DEFAULT_ALIGNMENT);
+}
+
+void ArenaFree(DebugArena *Alloc, void *Ptr)
+{
+ // do nothing
+}
+
+void *ArenaResizeAlign(DebugArena *Alloc, void *OldMem, size_t OldSize,
+ size_t NewSize, size_t Align)
+{
+ assert(IsPowerOfTwo(Align));
+ if (OldMem == NULL || OldSize == 0)
+ {
+ return ArenaAllocAlign(Alloc, NewSize, Align);
+ }
+ else if (Alloc->Buffer < OldMem && OldMem < Alloc->Buffer + Alloc->Size)
+ {
+ // check if old_memory falls on prev_offset
+ if (Alloc->Buffer + Alloc->PrevOffset == OldMem)
+ {
+ // re-use prev_offset and resize from there
+ size_t _CurrOffset = Alloc->CurrOffset;
+ Alloc->CurrOffset = Alloc->PrevOffset + NewSize;
+ if (NewSize > OldSize)
+ {
+ ZeroMemory(&Alloc->Buffer[_CurrOffset], NewSize - OldSize);
+ }
+ return OldMem;
+ }
+ else
+ {
+ // generate new memory
+ // will have some fragmentation
+ void *NewMem = ArenaAllocAlign(Alloc, NewSize, Align);
+ size_t CopySize = OldSize < NewSize ? OldSize : NewSize;
+
+ // copy old memory to new memory location
+ CopyMemory(NewMem, OldMem, CopySize);
+ return NewMem;
+ }
+ }
+ else
+ {
+ assert(0 && "Memory is out of bounds of the buffer in this arena");
+ return NULL;
+ }
+}
+
+void *ArenaResize(DebugArena *Alloc, void *OldMem, size_t OldSize, size_t NewSize)
+{
+ return ArenaResizeAlign(Alloc, OldMem, OldSize, NewSize, DEFAULT_ALIGNMENT);
+}
+
+void ArenaFreeAll(DebugArena *Alloc)
+{
+ Alloc->CurrOffset = 0;
+ Alloc->PrevOffset = 0;
+}
+
+#include "game_main.h"
+#include "gl_graphics.cpp"
+
+typedef struct glyph_info {
+ u32 charset_start_index;
+ u32 charset_end_index;
+} glyph_info;
+
+u32 GetGlyphIdFromCodepoint(glyph_info glyph, u32 *GlyphIndexArray, u32 Codepoint)
+{
+ u32 glyph_id = *(GlyphIndexArray + (Codepoint - glyph.charset_start_index));
+ return glyph_id;
+}
+
+int main()
+{
+ // ==================== PROGRAM STATE INITIALISATION =====================
+ LARGE_INTEGER PerfCountFrequencyResult;
+ QueryPerformanceFrequency(&PerfCountFrequencyResult);
+ GlobalPerfCountFrequency = PerfCountFrequencyResult.QuadPart;
+
+ // Note(talha): Set Windows scheduler granularity to 1ms
+ // so that our sleep can be more granular
+ UINT DesiredSchedulerMS = 1;
+ b8 SleepIsGranular = (timeBeginPeriod(DesiredSchedulerMS) == TIMERR_NOERROR);
+
+ // TODO(talha): how to use a library to query this
+ i32 MonitorRefreshHz = 60;
+ i32 GameUpdateHz = MonitorRefreshHz;
+ r32 TargetSecondsPerFrame = 1.0f / (r32)GameUpdateHz;
+
+ GameState State = {0};
+ State.Input.LastMouseX = WIN_WIDTH/2.0f;
+ State.Input.LastMouseY = WIN_HEIGHT/2.0f;
+ State.WinDimsPx.x = WIN_WIDTH;
+ State.WinDimsPx.y = WIN_HEIGHT;
+
+ // ================= GLFW WINDOW INITIALISATION ==========================
+ glfwInit();
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
+ glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
+
+ GLFWwindow *Window = glfwCreateWindow((i32)State.WinDimsPx.x, (i32)State.WinDimsPx.y, "LearnOpenGL", NULL, NULL);
+ if (Window == NULL)
+ {
+ // todo(talha): add error logging for failed to create glfw window
+ glfwTerminate();
+ printf("ERROR: Failed to create a glfw window\n");
+ return -1;
+ }
+
+ glfwMakeContextCurrent(Window);
+
+ if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
+ {
+ // todo(talha): error logging - failed to initialize glad
+ printf("ERROR: Failed to initialise glad\n");
+ return -1;
+ }
+
+ glViewport(0, 0, (GLsizei)State.WinDimsPx.x, (GLsizei)State.WinDimsPx.y);
+ glfwSetFramebufferSizeCallback(Window, framebuffer_size_callback);
+ //glfwSetInputMode(Window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
+ glfwSetCursorPos(Window, State.Input.LastMouseX, State.Input.LastMouseY);
+
+ LARGE_INTEGER LastCounter = Win32GetWallClock();
+ u64 LastCycleCount = __rdtsc();
+
+ size_t PermanentStorageSize = MB((u64)500);
+ void *PermanentStorage = VirtualAlloc(NULL, PermanentStorageSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
+ if (PermanentStorage == NULL)
+ {
+ printf("ERROR: Failed to allocate game memory\n");
+ return -1;
+ }
+ State.Memory.PermanentStorage = PermanentStorage;
+ State.Memory.PermanentStorageSize = PermanentStorageSize;
+
+ ArenaInit(&State.Memory.Arena, State.Memory.PermanentStorage, State.Memory.PermanentStorageSize);
+
+ // ======================= SHADER PROGRAM LOADING ========
+ DWORD BytesRead = 0;
+ u32 max_fsz = MB((u64)5);
+ u8 *vs_file = (u8 *)ArenaAlloc(&State.Memory.Arena, max_fsz);
+ if (PlatformDebugReadFile("./code/shaders/text.vs.glsl", vs_file, max_fsz, &BytesRead) != 1)
+ {
+ printf("ERROR: failed to open text vertex shader");
+ return -1;
+ }
+
+ u8 *fs_file = (u8 *)ArenaAlloc(&State.Memory.Arena, max_fsz);
+ if (PlatformDebugReadFile("./code/shaders/text.fs.glsl", fs_file, max_fsz, &BytesRead) != 1)
+ {
+ printf("ERROR: failed to open text vertex shader");
+ return -1;
+ }
+
+ u32 vs = CreateVertexShader((char *)vs_file);
+ u32 fs = CreateFragmentShader((char *)fs_file);
+ u32 sp = CreateShaderProgram(vs, fs);
+
+ ArenaFreeAll(&State.Memory.Arena);
+
+ // =================== FONT RENDERING ===================
+ u32 font_fz = MB(2);
+ u8 *ArialBuffer = (u8 *)ArenaAlloc(&State.Memory.Arena, font_fz);
+
+ // @todo: change to PlatformDebugFileRead
+ fread(ArialBuffer, 1, font_fz, fopen("c:/windows/fonts/arial.ttf", "rb"));
+
+ stbtt_fontinfo font_info;
+ if (!stbtt_InitFont(&font_info, ArialBuffer, 0))
+ {
+ printf("ERROR: failed to read arial font");
+ assert(1 == 0);
+ }
+
+ // get a SF (scale factor)
+ r32 SF = stbtt_ScaleForPixelHeight(&font_info, 32.0f);
+
+ // get vertical metrics
+ i32 f_ascent, f_descent, f_line_gap;
+ stbtt_GetFontVMetrics(&font_info, &f_ascent, &f_descent, &f_line_gap);
+
+ // ========================= GLYPH AND FONT INFO STORAGE ================
+ glyph_info latin_basic = {32, 126};
+ u32 glyph_count = latin_basic.charset_end_index - latin_basic.charset_start_index;
+ u32 *GlyphIndexArray = (u32 *)ArenaAlloc(&State.Memory.Arena, sizeof(glyph_count) * glyph_count);
+ u32 *glyph_array_iter = GlyphIndexArray;
+ for (u32 i = 0; i < glyph_count; i++)
+ {
+ *glyph_array_iter = stbtt_FindGlyphIndex(&font_info, latin_basic.charset_start_index + i);
+ glyph_array_iter++;
+ }
+
+ const char *text = "Hello this is a\npiece of test text \nor and more on a newline.";
+ u8 char_sz = 61;
+
+ u32 fd_sz = sizeof(debug_font_details)*char_sz;
+
+ debug_font_details *fd_arr = (debug_font_details *)ArenaAlloc(&State.Memory.Arena, fd_sz);
+ debug_font_details *_fi = fd_arr;
+
+ r32 lx = 0.0f, max_lx = 0.0f;
+ i32 baseline = (i32)roundf(f_ascent * SF);
+ // convert str to font
+ for (i32 i = 0; i < char_sz; ++i)
+ {
+ _fi->lx = lx;
+ _fi->baseline = baseline;
+
+ if (text[i] == '\n')
+ {
+ // if new line, move baseline down
+ baseline += (i32)roundf((f_ascent - f_descent + f_line_gap) * SF);
+ lx = 0;
+ ++_fi;
+ continue;
+ }
+
+ stbtt_GetGlyphHMetrics(&font_info, GetGlyphIdFromCodepoint(latin_basic, GlyphIndexArray, text[i]), &_fi->advance, &_fi->lsb);
+ stbtt_GetGlyphBitmapBox(&font_info, GetGlyphIdFromCodepoint(latin_basic, GlyphIndexArray, text[i]), SF, SF, &_fi->x0, &_fi->y0, &_fi->x1, &_fi->y1);
+
+ _fi->ly = (r32)(_fi->baseline + _fi->y0);
+ lx += (_fi->advance * SF);
+
+ i32 kern;
+ if (i < char_sz - 1 && text[i+1] != '\n')
+ {
+ kern = stbtt_GetGlyphKernAdvance(&font_info, GetGlyphIdFromCodepoint(latin_basic, GlyphIndexArray, text[i]), GetGlyphIdFromCodepoint(latin_basic, GlyphIndexArray, text[i]));
+ lx += roundf(kern * SF);
+ }
+ if (lx > max_lx)
+ {
+ max_lx = lx;
+ }
+ ++_fi;
+ }
+
+ // make all the glyphs into bitmaps.
+ _fi = fd_arr;
+ u32 bitmap_width_from_text_info = (u32)max_lx;
+ u32 bitmap_height_from_text_info = (u32)(baseline - roundf(SF * f_descent));
+
+ i32 bmp_sz = sizeof(u8)*bitmap_width_from_text_info*bitmap_height_from_text_info;
+ u8 *Bitmap = (u8 *)ArenaAlloc(&State.Memory.Arena, bmp_sz);
+
+ _fi = fd_arr;
+ for (int i=0; i<char_sz; i++)
+ {
+ if (text[i] == '\n') {
+ ++_fi;
+ continue;
+ }
+ _fi->byte_offset = (i32)(_fi->lx + roundf(_fi->lsb * SF) + (_fi->ly * bitmap_width_from_text_info)) ;
+ // store image data in bitmap
+ stbtt_MakeGlyphBitmap(&font_info, Bitmap + _fi->byte_offset, _fi->x1 - _fi->x0, _fi->y1 - _fi->y0, bitmap_width_from_text_info, SF, SF, GetGlyphIdFromCodepoint(latin_basic, GlyphIndexArray, text[i]));
+ _fi++;
+ }
+
+ // ---------- prepare bitmap texture and buffer objects -----------
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+
+ r32 bmp_vertices[] = {
+ // positions // texture coords
+ 1.0f, 1.0f, 0.0f, 1.0f, 1.0f,// top right
+ 1.0f, -1.0f, 0.0f, 1.0f, 0.0f,// bottom right
+ -1.0f, -1.0f, 0.0f, 0.0f, 0.0f,// bottom left
+ -1.0f, 1.0f, 0.0f, 0.0f, 1.0f // top left
+ };
+ u32 bmp_vsz = sizeof(bmp_vertices);
+
+ u32 bmp_indices[] = {
+ 0, 1, 3,
+ 1, 2, 3
+ };
+ u32 bmp_isz = sizeof(bmp_indices);
+
+ BufferO bo = CreateRectangleTextured(bmp_vertices, bmp_vsz, bmp_indices, bmp_isz, ATTR_TEX);
+ Texture2D _tex = {0};
+ {
+ _tex.width = bitmap_width_from_text_info;
+ _tex.height = bitmap_height_from_text_info;
+ _tex.data = Bitmap;
+
+ glBindVertexArray(bo.VAO);
+ glGenTextures(1, &bo.TexO);
+ glBindTexture(GL_TEXTURE_2D, bo.TexO);
+ // defining texture wrapping options
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, _tex.width, _tex.height, 0, GL_RED, GL_UNSIGNED_BYTE, _tex.data);
+ glBindVertexArray(0);
+ }
+
+ // =============== CAMERA SETUP ==================
+
+ State.Camera.Pos = {0};
+ State.Camera.Front = {0};
+ State.Camera.Front.z = -1.0f;
+ State.Camera.Up = {0};
+ State.Camera.Up.y = 1.0f;
+ State.Camera.MoveSpeed = 0.1f;
+ State.Input.Sensitivity = 0.1f;
+
+ // ======= SCREEN POSITIONING AND OBJECT SCALING SETUP ========
+
+ // recalculated each time resolution changes
+ // store in state
+ Vec2 pixel_span;
+ pixel_span.x = State.WinDimsPx.x*2.0f/State.WinDimsPx.y;
+ pixel_span.y = State.WinDimsPx.y*2.0f/State.WinDimsPx.x;
+
+ Vec2 pixel_ratio;
+ pixel_ratio.x = pixel_span.x/State.WinDimsPx.x;
+ pixel_ratio.y = pixel_span.y/State.WinDimsPx.y;
+
+ State.px_ratio = pixel_ratio;
+
+ Vec2 win_dims;
+ win_dims = State.px_ratio * State.WinDimsPx;
+
+ // calculate screen mid point in view pixels and world position
+ // @note: calculated each time player moves
+ Vec2 ScreenMidCoords = {State.Camera.Pos.x, State.Camera.Pos.y};
+ Vec2 ScreenMidPx = State.WinDimsPx/2.0f;
+ // calculate corner points of the field:
+ // tl -> top left
+ // br -> bot right
+ State.ScreenCoords.tl = ScreenMidCoords - ScreenMidPx;
+ State.ScreenCoords.br = ScreenMidCoords + ScreenMidPx;
+
+ // ====================== TEXT POSITIONING AND SCALING ======
+ Vec2 text_dims = {((r32)bitmap_width_from_text_info)/2.0f, ((r32)bitmap_height_from_text_info)/2.0f};
+ Vec2 text_scaled_dims = State.px_ratio * text_dims;
+ Vec2 text_scaled_mid = text_scaled_dims/2.0f;
+
+ Vec2 text_pos = {0.0f, 0.0f};
+ Vec2 text_offset = {text_dims.x/2.0f, (r32)fd_arr->baseline};
+ text_pos = text_offset + text_pos;
+ Vec2 text_scaled_pos = State.px_ratio*(State.ScreenCoords.tl + text_pos) + text_scaled_mid;
+
+
+ // ===================== TEXTURE BINDING ===================
+ glUseProgram(sp);
+ glBindVertexArray(bo.VAO);
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, bo.TexO);
+ LoadUniformInt(sp, "tex", 0);
+ LoadUniformVec3(sp, "TextColor", InitVec3(1.0f, 1.0f, 1.0f));
+
+ while (!glfwWindowShouldClose(Window)) {
+ LARGE_INTEGER WorkCounter = Win32GetWallClock();
+ r32 WorkSecondsElapsed = Win32GetSecondsElapsed(LastCounter, WorkCounter);
+
+ r32 SecondsElapsedForFrame = WorkSecondsElapsed;
+ // @note: this caps the framerate
+ // also prevents, for now, incorrect speeds based on timings
+ // @todo: fix framerate capping and speed timings being affected by framerate
+ if(SecondsElapsedForFrame < TargetSecondsPerFrame){
+ while(SecondsElapsedForFrame < TargetSecondsPerFrame)
+ {
+ if (SleepIsGranular)
+ {
+ DWORD SleepMS = (DWORD)(1000.0f * (TargetSecondsPerFrame - SecondsElapsedForFrame));
+ if (SleepMS > 0)
+ {
+ Sleep(SleepMS);
+ }
+ }
+ SecondsElapsedForFrame = Win32GetSecondsElapsed(LastCounter,
+ Win32GetWallClock());
+ }
+ }
+ else
+ {
+ // TODO(talha): Missed frame rate
+ // TODO(talha): Logging
+ }
+
+ glfwPollEvents();
+ // HandleInputs
+ if (glfwGetKey(Window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
+ {
+ glfwSetWindowShouldClose(Window, 1);
+ }
+ if (glfwGetKey(Window, GLFW_KEY_W) == GLFW_PRESS)
+ {
+ State.Camera.Pos = State.Camera.Pos + (State.Camera.Front * State.Camera.MoveSpeed);
+ }
+ if (glfwGetKey(Window, GLFW_KEY_S) == GLFW_PRESS)
+ {
+ State.Camera.Pos = State.Camera.Pos + ((State.Camera.Front * State.Camera.MoveSpeed) * -1.0f);
+ }
+ if (glfwGetKey(Window, GLFW_KEY_A) == GLFW_PRESS)
+ {
+ Vec3 HorizontalVec = (UnitVec3(CrossProductVec3(State.Camera.Front, State.Camera.Up)) * State.Camera.MoveSpeed);
+ State.Camera.Pos = (State.Camera.Pos + (HorizontalVec * -1.0f));
+ }
+ if (glfwGetKey(Window, GLFW_KEY_D) == GLFW_PRESS)
+ {
+ Vec3 HorizontalVec = (UnitVec3(CrossProductVec3(State.Camera.Front, State.Camera.Up)) * State.Camera.MoveSpeed);
+ State.Camera.Pos = State.Camera.Pos + HorizontalVec;
+ }
+
+ Mat4 LookAt = CreateLookAtMat4(State.Camera.Pos, State.Camera.Pos + State.Camera.Front, State.Camera.Up);
+ Mat4 View = LookAt;
+ // projection matrix
+ // far value is the max z-index value. Will be useful for layering textures
+ Mat4 Projection = CreateOrthographicWithRatio(WIN_WIDTH, WIN_HEIGHT, -5.0f, 5.0f);
+
+ const r32 view[16] = {View.x0, View.x1, View.x2, View.x3,
+ View.y0, View.y1, View.y2, View.y3,
+ View.z0, View.z1, View.z2, View.z3,
+ View.w0, View.w1, View.w2, View.w3};
+ const r32 projection[16] = {Projection.x0, Projection.x1, Projection.x2, Projection.x3,
+ Projection.y0, Projection.y1, Projection.y2, Projection.y3,
+ Projection.z0, Projection.z1, Projection.z2, Projection.z3,
+ Projection.w0, Projection.w1, Projection.w2, Projection.w3};
+ LoadUniformMat4(sp, "View", view);
+ LoadUniformMat4(sp, "Projection", projection);
+
+ glEnable(GL_DEPTH_TEST);
+ // glEnable(GL_BLEND);
+ // glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+
+ // ============== DRAW WOOD CONTAINER ===============
+ Mat4 Model = IdentityMat();
+
+#if 0
+ Mat4 Scale1 = CreateScaleMat(InitVec4(container_dims.x, container_dims.y, 1.0f, 0));
+ Model = Mul_Mat4Mat4(Scale1, Model);
+ Mat4 Tx1 = CreateTranslationMat(InitVec4(container_pos.x, container_pos.y, -1.0f, 0));
+ Model = Mul_Mat4Mat4(Tx1, Model);
+
+ const r32 container_model[16] = {Model.x0, Model.x1, Model.x2, Model.x3,
+ Model.y0, Model.y1, Model.y2, Model.y3,
+ Model.z0, Model.z1, Model.z2, Model.z3,
+ Model.w0, Model.w1, Model.w2, Model.w3};
+ LoadUniformMat4(sp, "Model", container_model);
+ DrawRectangleTextured(bo.VAO, Textures[0]);
+
+ // ============ DRAW FIELD ======================
+ Model = IdentityMat();
+
+ Mat4 Scale2 = CreateScaleMat(InitVec4(field_dims.x, field_dims.y, 1.0f, 0));
+ Mat4 Tx = CreateTranslationMat(InitVec4(0, 0, -2.0f, 0));
+ Model = Mul_Mat4Mat4(Scale2, Model);
+ Model = Mul_Mat4Mat4(Tx, Model);
+
+ const r32 field_model[16] = {Model.x0, Model.x1, Model.x2, Model.x3,
+ Model.y0, Model.y1, Model.y2, Model.y3,
+ Model.z0, Model.z1, Model.z2, Model.z3,
+ Model.w0, Model.w1, Model.w2, Model.w3};
+ LoadUniformMat4(sp, "Model", field_model);
+ DrawRectangleTextured(bo.VAO, Textures[1]);
+#endif
+
+ // =============== DRAW TEXT BITMAP RENDERING ===============
+ {
+ Model = IdentityMat();
+ Mat4 Scale = CreateScaleMat(InitVec4(text_scaled_dims.x, text_scaled_dims.y, 1, 0));
+ Model = Mul_Mat4Mat4(Scale, Model);
+ Mat4 Tx = CreateTranslationMat(InitVec4(text_scaled_pos.x, text_scaled_pos.y, 0, 0));
+ Model = Mul_Mat4Mat4(Tx, Model);
+
+ const r32 text_model[16] = {Model.x0, Model.x1, Model.x2, Model.x3,
+ Model.y0, Model.y1, Model.y2, Model.y3,
+ Model.z0, Model.z1, Model.z2, Model.z3,
+ Model.w0, Model.w1, Model.w2, Model.w3};
+ LoadUniformMat4(sp, "Model", text_model);
+ DrawRectangleTextured(bo.VAO, bo.TexO);
+ }
+
+
+ glfwSwapBuffers(Window);
+ // TODO(talha): should these be cleared
+ LastCounter = Win32GetWallClock();
+ LastCycleCount = __rdtsc();
+ };
+
+ VirtualFree(PermanentStorage, 0, MEM_RELEASE);
+ glfwTerminate();
+ return 0;
+}