From d980dcd2b66e4879989ce18291d044d5a4ffc902 Mon Sep 17 00:00:00 2001
From: talha <talha@talhaamir.xyz>
Date: Wed, 30 Aug 2023 11:04:00 +0500
Subject: setting up font-rendering repo

---
 code/win32_main.cpp | 639 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 639 insertions(+)
 create mode 100644 code/win32_main.cpp

(limited to 'code/win32_main.cpp')

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;
+}
-- 
cgit v1.2.3