#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 f32; typedef double f64; #include "math.h" #define WIN_WIDTH 1280.0f #define WIN_HEIGHT 1024.0f #define internal static /** * todo: * - Add a stack allocator, have many cases where it would be useful * - create a safe(r) way to use arrays with an array struct having a length field * - will be useful for 2d arrays as well as we will have a len and height * - start having error codes to help with error detection in case something went wrong in functions * Font rendering todos: * - Look into generating a font atlas and then generating quads from the * bitmap as needed * - SDF font rendering * */ internal i64 GlobalPerfCountFrequency; u8 PlatformDebugFileRead(const char *FileName, void *FileBuffer, u32 MaxSize) { // @todo: refactor function, check for closehandle error codes HANDLE FileHandle = CreateFileA(FileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (FileHandle != INVALID_HANDLE_VALUE) { DWORD BytesRead; 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 PlatformCopyMemory(void *NewMem, void *OldMem, size_t CopySize) { CopyMemory(NewMem, OldMem, CopySize); } void PlatformZeroMemory(void *Ptr, size_t Size) { ZeroMemory(Ptr, Size); } 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 f32 Win32GetSecondsElapsed(LARGE_INTEGER Start, LARGE_INTEGER End) { f32 Result = (f32)(End.QuadPart - Start.QuadPart)/(f32)GlobalPerfCountFrequency; return(Result); } #include "amr_memory.c" #include "gl_graphics.cpp" //#include "amr_camera.c" #include "game_main.h" #include "amr_fonts.h" 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; f32 TargetSecondsPerFrame = 1.0f / (f32)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; amr_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 *)amr_ArenaAlloc(&State.Memory.Arena, max_fsz); if (PlatformDebugFileRead("./code/shaders/text.vs.glsl", vs_file, max_fsz) != 1) { printf("ERROR: failed to open text vertex shader"); return -1; } u8 *fs_file = (u8 *)amr_ArenaAlloc(&State.Memory.Arena, max_fsz); if (PlatformDebugFileRead("./code/shaders/text.fs.glsl", fs_file, max_fsz) != 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); amr_ArenaFreeAll(&State.Memory.Arena); // =================== FONT RENDERING =================== // --------- Init Font ----------------- // input: // - filepath, memory buffer, memory buffer size // - maybe a font_object reference that I need u32 font_fz = MB(2); u8 *ArialBuffer = (u8 *)amr_ArenaAlloc(&State.Memory.Arena, font_fz); amr_font_state ArialFontState = {0}; amr_InitFont(&(ArialFontState.font_info), "c:/windows/fonts/arial.ttf", ArialBuffer, font_fz); ///////////////////////////////////// PREPARE TEXT INFO /////////////////// amr_LoadFontMetrics(&ArialFontState, 32.0f); // ========================= GLYPH AND FONT INFO STORAGE ================ ArialFontState.charset_start_index = LATIN_BASIC_START_INDEX; ArialFontState.charset_end_index = LATIN_BASIC_END_INDEX; amr_LoadGlyphsFromFont(&State.Memory.Arena, &ArialFontState); // ========================= NAIVE FONT BITMAP GENERATION =============== // Load text into bitmaps char *text = "Hello this is a\npiece of test text \nor and more on a newline."; u8 text_sz = 61; // @TODO: Add support for multiple text loading // this currently only supports 1 text at a time // the struct amr_font_state_debug only has field for 1 font // that can be fixed by adding an array object of a fixed size // that user can initalize and will then support adding fonts to // @TODO: look into refactoring this function, only did a basic pass amr_MakeFontBitmapNaive(&State, &ArialFontState, text, text_sz); // ---------- prepare bitmap texture and buffer objects ----------- ////////////////////////////////// PREPARE TEXT FOR RENDERING //////////// /// @resume: continue abstracting font rendering stuff into functions from here glPixelStorei(GL_UNPACK_ALIGNMENT, 1); f32 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 = ArialFontState.FontBitmapWidth; _tex.height = ArialFontState.FontBitmapHeight; _tex.data = ArialFontState.FontBitmap; 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; // ////////////////////// DEFINE TEXT POSITION AND SCALE ////////////////// // ====================== TEXT POSITIONING AND SCALING ====== Vec2 text_dims = {((f32)ArialFontState.FontBitmapWidth)/2.0f, ((f32)ArialFontState.FontBitmapHeight)/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, ArialFontState.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(); f32 WorkSecondsElapsed = Win32GetSecondsElapsed(LastCounter, WorkCounter); f32 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 f32 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 f32 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 f32 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 f32 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 =============== // ////////////////// RENDER TEXT BITMAP ///////////////////////////// { 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 f32 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; }