From d980dcd2b66e4879989ce18291d044d5a4ffc902 Mon Sep 17 00:00:00 2001 From: talha 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 +#include +#include +#include + +#define STB_TRUETYPE_IMPLEMENTATION +#include "stb_truetype.h" +#include +#include +#include + +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; ibyte_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