#include #include #include #include #include "glad.c" #include "fswatcher/fswatcher.h" #include "fswatcher/fswatcher.cpp" #include "core.h" #if defined(_WIN32) #define BUILD_GAME() system("build_game.bat") #else #define BUILD_GAME() system("sh build_game.sh") #endif // GAME LIBRARY FUNCTIONS typedef void (*GameHandleEvent)(GameState*, GameEventType); typedef void (*GameSetup)(GameState*); typedef void (*GameUpdateAndRender)(GameState*); struct GameLayer { void *lib; GameHandleEvent handle_event; GameSetup setup; GameUpdateAndRender update_and_render; }; internal GameLayer game_layer; bool load_game_layer() { // @hack: recompile game here before load object call BUILD_GAME(); SDL_UnloadObject(game_layer.lib); game_layer.lib = SDL_LoadObject("build/libgame.so"); if (game_layer.lib == NULL) { return false; } game_layer.handle_event = (GameHandleEvent)SDL_LoadFunction(game_layer.lib, "game_handle_event"); game_layer.setup = (GameSetup)SDL_LoadFunction(game_layer.lib, "game_setup"); game_layer.update_and_render = (GameUpdateAndRender)SDL_LoadFunction(game_layer.lib, "game_update_and_render"); return true; } bool game_file_update(fswatcher_event_handler *handler, fswatcher_event_type evtype, const char *src, const char *dst) { return load_game_layer(); } //bool game_shader_update(fswatcher_event_handler *handler, // fswatcher_event_type evtype, // const char *src, // const char *dst) { //} u32 gl_shader_program(char *vs, char *fs) { //============== // Vertex Shader u32 handle_vs = glCreateShader(GL_VERTEX_SHADER); glShaderSource(handle_vs, 1, &vs, NULL); glCompileShader(handle_vs); i32 vertex_compiled; glGetShaderiv(handle_vs, GL_COMPILE_STATUS, &vertex_compiled); if (vertex_compiled != GL_TRUE) { i32 log_length = 0; char message[1024]; glGetShaderInfoLog(handle_vs, 1024, &log_length, message); fprintf(stderr, "===Error compiling vertex shader===\n"); fprintf(stderr, "%s\n", message); } //================ // Fragment Shader u32 handle_fs = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(handle_fs, 1, &fs, NULL); glCompileShader(handle_fs); i32 fragment_compiled; glGetShaderiv(handle_fs, GL_COMPILE_STATUS, &fragment_compiled); if (fragment_compiled != GL_TRUE) { i32 log_length = 0; char message[1024]; glGetShaderInfoLog(handle_fs, 1024, &log_length, message); fprintf(stderr, "===Error compiling fragment shader===\n"); fprintf(stderr, "%s\n", message); } //=============== // Shader Program u32 shader_program = glCreateProgram(); glAttachShader(shader_program, handle_vs); glAttachShader(shader_program, handle_fs); glLinkProgram(shader_program); GLint program_compiled; glGetProgramiv(shader_program, GL_LINK_STATUS, &program_compiled); if (program_compiled != GL_TRUE) { i32 log_length = 0; char message[1024]; glGetShaderInfoLog(shader_program, 1024, &log_length, message); fprintf(stderr, "===Error linking shader program===\n"); fprintf(stderr, "%s\n", message); } glDeleteShader(handle_vs); glDeleteShader(handle_fs); return shader_program; } u32 gl_shader_program_from_path(const char *vspath, const char *fspath) { size_t read_count; char *vs = (char *)SDL_LoadFile(vspath, &read_count); if (vs == NULL) { const char *error_msg = SDL_GetError(); fprintf(stderr, "Error loading file %s: %s\n", vspath, error_msg); } read_count = 0; char *fs = (char *)SDL_LoadFile(fspath, &read_count); if (fs == NULL) { const char *error_msg = SDL_GetError(); fprintf(stderr, "Error loading file %s: %s\n", fspath, error_msg); } u32 shader_program = gl_shader_program(vs, fs); SDL_free(vs); SDL_free(fs); return shader_program; } int main(int argc, char *argv[]) { i32 raw_width = 1920; i32 raw_height = 1080; i32 dpi_width; i32 dpi_height; r32 scale_width; r32 scale_height; i32 width; i32 height; GameState state = {}; if (SDL_Init(SDL_INIT_VIDEO) != 0) { printf("Error initialising SDL2: %s\n", SDL_GetError()); return 0; }; SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); // initialise window with opengl flag SDL_Window *window = SDL_CreateWindow("SDL Test", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, raw_width, raw_height, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE|SDL_WINDOW_ALLOW_HIGHDPI); // create an opengl context SDL_GLContext context = SDL_GL_CreateContext(window); if (!context) { printf("OpenGL context creation failed: %s\n", SDL_GetError()); return -1; } // calculate correct window size due to scaling SDL_GL_GetDrawableSize(window, &dpi_width, &dpi_height); scale_width = (r32)raw_width/(r32)dpi_width; scale_height = (r32)raw_height/(r32)dpi_height; state.width = raw_width * scale_width; state.height = raw_height * scale_height; SDL_SetWindowSize(window, state.width, state.height); // load glad if (!gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress)) { printf("Failed to initialize Glad\n"); return 1; } SDL_GL_SetSwapInterval(1); u32 shader_program = gl_shader_program_from_path("./shaders/quad.vs.glsl", "./shaders/quad.fs.glsl"); state.triangle_sp = shader_program; glUseProgram(state.triangle_sp); // @func: gl_setup_triangle { GLfloat vertices[9] = { -0.5f, -0.5f, 0.0f, 0.5f, -0.5f, 0.0f, 0.0f, 0.5f, 0.0f }; GLuint VBO, VAO; glGenVertexArrays(1, &VAO); glGenBuffers(1, &VBO); glBindVertexArray(VAO); glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0); glEnableVertexAttribArray(0); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); state.triangle_vao = VAO; state.triangle_vbo = VBO; } glUseProgram(0); // add file watcher fswatcher_t game_watcher = fswatcher_create(FSWATCHER_CREATE_DEFAULT, FSWATCHER_EVENT_MODIFY, "source/game", NULL); fswatcher_event_handler game_file_update_handler; game_file_update_handler.callback = game_file_update; // load game layer if (!load_game_layer()) { return -1; } game_layer.setup(&state); for(;;) { SDL_Event ev; while(SDL_PollEvent(&ev)) { if (ev.type == SDL_QUIT) { return 0; } if (ev.type == SDL_WINDOWEVENT) { if (ev.window.event == SDL_WINDOWEVENT_RESIZED) { state.width = ev.window.data1; state.height = ev.window.data2; game_layer.handle_event(&state, GAME_EVENT_RESIZE); } } if (ev.type == SDL_KEYDOWN) { if (ev.key.keysym.sym == SDLK_SPACE) { } } } fswatcher_poll(game_watcher, &game_file_update_handler, NULL); // opengl rendering code here game_layer.update_and_render(&state); SDL_GL_SwapWindow(window); } // filewatcher fswatcher_destroy(game_watcher); // opengl free calls glDeleteVertexArrays(1, &state.triangle_vao); glDeleteBuffers(1, &state.triangle_vbo); glDeleteProgram(state.triangle_sp); // sdl free calls SDL_GL_DeleteContext(context); SDL_DestroyWindow(window); SDL_Quit(); return 0; }