commit c4077d84754b08872a16fde5d4e962aad6268f73
parent fbf146edf989a236edea2b95bf4eacc75e9cd106
Author: amin <dev@aminmesbah.com>
Date: Wed, 17 Apr 2019 02:37:24 +0000
Port to Windows
Compiles as long as `..\include\glfw\` exists. We'll fix that.
FossilOrigin-Name: 92f335710a8f24153c294eadc3926a8af65a1a0d0aec144de9b872a3853d1b88
Diffstat:
4 files changed, 324 insertions(+), 1 deletion(-)
diff --git a/build.bat b/build.bat
@@ -0,0 +1,12 @@
+@echo off
+
+set CFLAGS=-std=c99 -nostdlib -D_CRT_SECURE_NO_WARNINGS -Ilib -I../include/glfw/include -Wall -Wextra -Wshadow -Wswitch-enum -Wno-unused-parameter -Wno-unused-function -Wno-missing-braces
+set LDFLAGS=-luser32 -lgdi32 -lwinmm -lopengl32 -lshell32
+set RELCFLAGS=-O2 -Os
+
+IF NOT EXIST ".\out" mkdir ".\out"
+IF NOT EXIST ".\out\release" mkdir ".\out\release"
+clang %CFLAGS% %RELCFLAGS%^
+ src\platform_windows.c^
+ ..\include\glfw\lib-vc2015\glfw3.lib^
+ -o .\out\release\a-game.exe %LDFLAGS%
diff --git a/src/platform_windows.c b/src/platform_windows.c
@@ -0,0 +1,294 @@
+#include <time.h>
+
+#include <glad/glad.h>
+#include <GLFW/glfw3.h>
+
+// LOL, Windef.h
+#undef near
+#undef far
+
+#include "game.c"
+#include "platform.h"
+#include "platform_windows.h"
+
+int main(void)
+{
+ glfwSetErrorCallback(error_callback);
+
+ if (!glfwInit())
+ {
+ fprintf(stderr, "GLFW initialization failed\n");
+ return -1;
+ }
+
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
+ glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
+ glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
+
+ GLFWwindow* window = glfwCreateWindow(PLATFORM_SCR_WIDTH, PLATFORM_SCR_HEIGHT, "A Game", NULL, NULL);
+ if (!window)
+ {
+ fprintf(stderr, "GLFW window creation failed\n");
+ glfwTerminate();
+ return -1;
+ }
+ glfwMakeContextCurrent(window);
+ glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
+ glfwSetKeyCallback(window, key_callback);
+
+ if (!gladLoadGLLoader((GLADloadproc) glfwGetProcAddress))
+ {
+ fprintf(stderr, "glad initialization failed\n");
+ glfwDestroyWindow(window);
+ glfwTerminate();
+ return -1;
+ }
+
+#ifdef USE_TEST_SEED
+ srand((uint32_t)0);
+#else
+ srand((uint32_t)time(NULL));
+#endif
+
+ struct GameState game_state = {0};
+ struct GameMemory game_memory = {
+ .game_state = &game_state,
+ .platform = {
+ &windows_read_entire_file,
+ },
+ };
+
+ struct GameInput game_input = {0};
+ glfwSetWindowUserPointer(window, &game_input);
+ game_init(&game_memory, (v2u) {PLATFORM_SCR_WIDTH, PLATFORM_SCR_HEIGHT});
+
+#ifdef PLATFORM_HOTLOAD_GAME_CODE
+ struct GameCode game_code = load_game_code(PLATFORM_GAME_LIB_PATH);
+ game_code.game_load_opengl_symbols();
+#endif
+
+ // NOTE(amin): lag should always be very small, so it's fine to use a float
+ // to store it. We will not run out of precision.
+ uint64_t previous_wall_clock_time = glfwGetTimerValue();
+ uint64_t timer_frequency = glfwGetTimerFrequency();
+
+ while (!glfwWindowShouldClose(window))
+ {
+ uint64_t current_wall_clock_time = glfwGetTimerValue();
+
+ {
+ uint64_t previous_frame_duration = current_wall_clock_time - previous_wall_clock_time;
+ previous_wall_clock_time = current_wall_clock_time;
+ f32 dt = (float)previous_frame_duration / (float)timer_frequency;
+ // TODO: Debug periodic dropped frame on linux when fullscreen
+ //printf("ms per frame: %f\n", dt * 1000.0f);
+ game_input.dt = dt;
+ }
+
+ int32_t framebuffer_width = PLATFORM_SCR_WIDTH;
+ int32_t framebuffer_height = PLATFORM_SCR_HEIGHT;
+ glfwGetFramebufferSize(window, &framebuffer_width, &framebuffer_height);
+ assert(framebuffer_width >= 0);
+ assert(framebuffer_height >= 0);
+ v2u framebuffer = {framebuffer_width, framebuffer_height};
+
+#ifdef PLATFORM_HOTLOAD_GAME_CODE
+ time_t last_write_time = file_get_modified_time(PLATFORM_GAME_LIB_PATH);
+ bool game_code_has_changed = last_write_time && (last_write_time != game_code.last_write_time);
+ if (game_code_has_changed)
+ {
+ unload_game_code(&game_code);
+ game_code = load_game_code(PLATFORM_GAME_LIB_PATH);
+ game_code.game_load_opengl_symbols();
+ if (!game_code.is_valid)
+ {
+ // TODO: fall back to backup?
+ }
+ }
+ game_code.game_update_and_render(&game_memory, &game_input, framebuffer);
+#else
+ game_update_and_render(&game_memory, &game_input, framebuffer);
+#endif // PLATFORM_HOTLOAD_GAME_CODE
+
+ // TODO: Do something sane when vsync is disabled
+ glfwSwapBuffers(window);
+ glfwPollEvents();
+ }
+
+ game_cleanup(&game_memory);
+
+ glfwDestroyWindow(window);
+ glfwTerminate();
+ return 0;
+}
+
+internal void error_callback(int error, const char* description)
+{
+ fprintf(stderr, "Error: %s\n", description);
+}
+
+internal void framebuffer_size_callback(GLFWwindow* window, int width, int height)
+{
+ glViewport(0, 0, width, height);
+}
+
+internal void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
+{
+ struct GameInput *game_input = (struct GameInput*)glfwGetWindowUserPointer(window);
+
+ enum InputKeyAction game_input_key_action = 0;
+ switch (action)
+ {
+ case GLFW_PRESS:
+ game_input_key_action = KEY_PRESS;
+ break;
+ case GLFW_RELEASE:
+ game_input_key_action = KEY_RELEASE;
+ break;
+ case GLFW_REPEAT:
+ game_input_key_action = KEY_REPEAT;
+ break;
+ default:
+ exit(1);
+ break;
+ }
+
+ enum InputKeyAction* game_key = NULL;
+ switch (key)
+ {
+ case GLFW_KEY_LEFT:
+ game_key = &(game_input->key_left);
+ break;
+ case GLFW_KEY_RIGHT:
+ game_key = &(game_input->key_right);
+ break;
+ case GLFW_KEY_UP:
+ game_key = &(game_input->key_up);
+ break;
+ case GLFW_KEY_DOWN:
+ game_key = &(game_input->key_down);
+ break;
+ default:
+ break;
+ }
+
+ if (game_key)
+ {
+ *game_key = game_input_key_action;
+ }
+}
+
+//internal time_t file_get_modified_time(char *file_path)
+//{
+// time_t mtime = 0;
+// struct stat file_status = {0};
+// if (stat(file_path, &file_status) == 0)
+// {
+// //printf("File: %s last modified: %s\n", file_path, ctime(&file_status.st_mtime));
+// mtime = file_status.st_mtime;
+// }
+// else
+// {
+// fprintf(stderr, "ERROR: Failed to stat file: %s\n", file_path);
+// }
+// return mtime;
+//}
+
+internal PLATFORM_READ_ENTIRE_FILE(windows_read_entire_file)
+{
+ FILE *handle = fopen(file_path, "rb");
+ char *buffer = NULL;
+
+ if (handle)
+ {
+ // get file size
+ fseek(handle, 0, SEEK_END);
+ u32 num_bytes_in_file = ftell(handle);
+ rewind(handle);
+
+ // TODO: replace malloc with own allocator so I stop having nightmares
+ buffer = (char*) malloc(sizeof(char) * (num_bytes_in_file + 1) );
+
+ u32 bytes_read = fread(buffer, sizeof(char), num_bytes_in_file, handle);
+ // IMPORTANT! fread() doesn't add the '\0'
+ buffer[num_bytes_in_file] = '\0';
+
+ if (num_bytes_in_file != bytes_read)
+ {
+ free(buffer);
+ buffer = NULL;
+ }
+
+ fclose(handle);
+ }
+ else
+ {
+ printf("Error: Couldn't open file at path: %s", file_path);
+ // TODO: handle errors here in a better way
+ exit(1);
+ }
+
+ return buffer;
+}
+
+#ifdef PLATFORM_HOTLOAD_GAME_CODE
+internal void unload_game_code(struct GameCode *game_code)
+{
+ if (!game_code)
+ {
+ fprintf(stderr, "ERROR: Invalid pointer *game_code\n");
+ return;
+ }
+
+ if (game_code->game_code_library)
+ {
+ dlclose(game_code->game_code_library);
+ game_code->game_code_library = NULL;
+ }
+ game_code->is_valid = false;
+ game_code->game_load_opengl_symbols = NULL;
+ game_code->game_update_and_render = NULL;
+}
+
+
+// TODO: Add backup dll in case loading fails
+internal struct GameCode load_game_code(char *source_lib_path)
+{
+ struct GameCode game_code = {0};
+
+ game_code.last_write_time = file_get_modified_time(source_lib_path);
+ if (game_code.last_write_time)
+ {
+ game_code.game_code_library = dlopen(source_lib_path, RTLD_LAZY);
+ if (game_code.game_code_library)
+ {
+ // NOTE: The C standard (as of C99) distinguishes function pointers
+ // from object pointers (`void *` is an object pointer). Technically it
+ // is undefined behavior to cast between these two pointer types. In
+ // practice, it works on most modern platforms.
+ //
+ // In this case, we are protected by POSIX, which specifies that
+ // function and data pointers must be the same size. We will only ever
+ // be using dlsym on POSIX-compliant platforms.
+ game_code.game_load_opengl_symbols = (game_load_opengl_symbols_func *) dlsym(game_code.game_code_library, "game_load_opengl_symbols");
+ game_code.game_update_and_render = (game_update_and_render_func *) dlsym(game_code.game_code_library, "game_update_and_render");
+ game_code.is_valid = (game_code.game_load_opengl_symbols && game_code.game_update_and_render);
+ }
+ }
+
+ if (!game_code.is_valid)
+ {
+ fprintf(stderr, "ERROR: Game code is not valid: %s\n", dlerror());
+ }
+
+ return game_code;
+}
+#endif // PLATFORM_HOTLOAD_GAME_CODE
+
+#ifdef GLAD_DEBUG
+internal void pre_gl_callback(const char *func_name, void *func_ptr, int len_args, ...)
+{
+ printf("Calling: %s (%d arguments)\n", func_name, len_args);
+}
+#endif
diff --git a/src/platform_windows.h b/src/platform_windows.h
@@ -0,0 +1,18 @@
+#define PLATFORM_SCR_WIDTH 600u
+#define PLATFORM_SCR_HEIGHT 600u
+
+internal void error_callback(int error, const char* description);
+internal void framebuffer_size_callback(GLFWwindow* window, int width, int height);
+internal void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods);
+internal PLATFORM_READ_ENTIRE_FILE(windows_read_entire_file);
+
+#ifdef PLATFORM_HOTLOAD_GAME_CODE
+#define PLATFORM_GAME_LIB_PATH "./out/release/game.so"
+internal time_t file_get_modified_time(char *file_path);
+internal void unload_game_code(struct GameCode *game_code);
+internal struct GameCode load_game_code(char *source_lib_path);
+#endif
+
+#ifdef GLAD_DEBUG
+internal void pre_gl_callback(const char *func_name, void *func_ptr, int len_args, ...);
+#endif
diff --git a/src/shader.h b/src/shader.h
@@ -3,7 +3,6 @@ struct Shader
u32 program;
};
-internal char *read_file(char *file_path);
internal struct Shader shader_compile(const GLchar *vertex_shader_source, const GLchar *fragment_shader_source);
internal void shader_use(struct Shader *s);
internal void shader_setb(struct Shader *s, char *name, bool value);