#ifdef BUILD_RELEASE #include "all.c" #else #include "common.h" #endif typedef struct keystate { int w; int a; int s; int d; int up; int left; int down; int right; int q; int e; int enter; int space; int esc; int shift; int f1; } keystate_t; #ifdef BUILD_DEBUG typedef struct debug { time_t mod_time; const char* lib_name; void* lib_ptr; } debug_t; void (*engine_reload)(memory_t*); void (*engine_debug)(float, input_t, uint64_t, uint64_t, uint64_t); void (*engine_startup)(memory_t*); void (*engine_shutdown)(void); void (*engine_update)(float, input_t, bool, bool*); void (*engine_render)(void); void (*engine_audio_callback)(void* userdata, uint8_t* stream, int len); #endif int main(int argc, char* argv[]) { (void)argc; (void)argv; { int ret; ret = SDL_InitSubSystem(SDL_INIT_VIDEO); if (ret != 0) { fprintf(stderr, "Error initializing SDL Video subsystem: %s\n", SDL_GetError()); exit(EXIT_FAILURE); } ret = SDL_InitSubSystem(SDL_INIT_AUDIO); if (ret != 0) { fprintf(stderr, "Error initializing SDL Audio subsystem: %s\n", SDL_GetError()); exit(EXIT_FAILURE); } } SDL_Window* window = NULL; SDL_GLContext* context = NULL; { window = SDL_CreateWindow( WINDOW_TITLE, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_OPENGL); if (window == NULL) { fprintf(stderr, "Error creating SDL window: %s\n", SDL_GetError()); exit(EXIT_FAILURE); } SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); context = SDL_GL_CreateContext(window); if (context == NULL) { fprintf(stderr, "Error creating SDL GL context: %s\n", SDL_GetError()); exit(EXIT_FAILURE); } SDL_GL_SetSwapInterval(0); gladLoadGL((GLADloadfunc)SDL_GL_GetProcAddress); SDL_SetRelativeMouseMode(SDL_TRUE); SDL_ShowCursor(SDL_DISABLE); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } memory_t memory; { memory.engine.begin = calloc(ENGINE_MEMORY_SIZE, 1); memory.engine.end = memory.engine.begin + ENGINE_MEMORY_SIZE; memory.engine.current = memory.engine.begin; if (memory.engine.begin == NULL) { fprintf(stderr, "Error requesting %lu bytes for engine memory\n", ENGINE_MEMORY_SIZE); exit(EXIT_FAILURE); } memory.game.begin = calloc(GAME_MEMORY_SIZE, 1); memory.game.end = memory.game.begin + GAME_MEMORY_SIZE; memory.game.current = memory.game.begin; if (memory.game.begin == NULL) { fprintf(stderr, "Error requesting %lu bytes for game memory\n", GAME_MEMORY_SIZE); exit(EXIT_FAILURE); } memory.data1.begin = calloc(DATA1_MEMORY_SIZE, 1); memory.data1.end = memory.data1.begin + DATA1_MEMORY_SIZE; memory.data1.current = memory.data1.begin; if (memory.data1.begin == NULL) { fprintf(stderr, "Error requesting %lu bytes for data1 memory\n", DATA1_MEMORY_SIZE); exit(EXIT_FAILURE); } memory.data2.begin = calloc(DATA2_MEMORY_SIZE, 1); memory.data2.end = memory.data2.begin + DATA2_MEMORY_SIZE; memory.data2.current = memory.data2.begin; if (memory.data2.begin == NULL) { fprintf(stderr, "Error requesting %lu bytes for data2 memory\n", DATA2_MEMORY_SIZE); exit(EXIT_FAILURE); } memory.scratch.begin = calloc(SCRATCH_MEMORY_SIZE, 1); memory.scratch.end = memory.scratch.begin + SCRATCH_MEMORY_SIZE; memory.scratch.current = memory.scratch.begin ; if (memory.scratch.begin == NULL) { fprintf(stderr, "Error requesting %lu bytes for scratch memory\n", SCRATCH_MEMORY_SIZE); exit(EXIT_FAILURE); } } #ifdef BUILD_DEBUG debug_t debug = {0}; { const char* platform_str = SDL_GetPlatform(); if (strcmp(platform_str, "Windows") == 0) { debug.lib_name = ".\\libgame.dll"; } else { debug.lib_name = "./libgame.so"; } struct stat attrib; stat(debug.lib_name, &attrib); debug.mod_time = attrib.st_mtime; debug.lib_ptr = SDL_LoadObject(debug.lib_name); if (debug.lib_ptr == NULL) { fprintf(stderr, "Error loading game library: %s\n", SDL_GetError()); exit(EXIT_FAILURE); } engine_startup = SDL_LoadFunction(debug.lib_ptr, "engine_startup"); if (engine_startup == NULL) { fprintf(stderr, "Error loading startup function from game lib: %s\n", SDL_GetError()); exit(EXIT_FAILURE); } engine_shutdown = SDL_LoadFunction(debug.lib_ptr, "engine_shutdown"); if (engine_shutdown == NULL) { fprintf(stderr, "Error loading shutdown function from game lib: %s\n", SDL_GetError()); exit(EXIT_FAILURE); } engine_update = SDL_LoadFunction(debug.lib_ptr, "engine_update"); if (engine_update == NULL) { fprintf(stderr, "Error loading update function pointer from game lib: %s\n", SDL_GetError()); exit(EXIT_FAILURE); } engine_render = SDL_LoadFunction(debug.lib_ptr, "engine_render"); if (engine_render == NULL) { fprintf(stderr, "Error loading render function pointer from game lib: %s\n", SDL_GetError()); exit(EXIT_FAILURE); } engine_audio_callback = SDL_LoadFunction(debug.lib_ptr, "engine_audio_callback"); if (engine_audio_callback == NULL) { fprintf(stderr, "Error loading audio function pointer from game lib: %s\n", SDL_GetError()); exit(EXIT_FAILURE); } engine_debug = SDL_LoadFunction(debug.lib_ptr, "engine_debug"); if (engine_debug == NULL) { fprintf(stderr, "Error loading debug function pointer from game lib: %s\n", SDL_GetError()); exit(EXIT_FAILURE); } engine_reload = SDL_LoadFunction(debug.lib_ptr, "engine_reload"); if (engine_reload == NULL) { fprintf(stderr, "Error loading reload function pointer from game lib: %s\n", SDL_GetError()); exit(EXIT_FAILURE); } memory.debug.begin = calloc(DEBUG_MEMORY_SIZE, 1); memory.debug.end = memory.debug.begin + DEBUG_MEMORY_SIZE; memory.debug.current = memory.debug.begin ; if (memory.debug.begin == NULL) { fprintf(stderr, "Error requesting %lu bytes for debug memory\n", DEBUG_MEMORY_SIZE); exit(EXIT_FAILURE); } } #endif // Set up audio SDL_AudioDeviceID audio_device; SDL_AudioSpec audio_specs = {0}; { audio_specs.freq = 8000; audio_specs.format = AUDIO_F32; audio_specs.channels = 2; audio_specs.samples = 256; audio_specs.callback = engine_audio_callback; audio_specs.userdata = &memory; SDL_AudioSpec have; audio_device = SDL_OpenAudioDevice(NULL, 0, &audio_specs, &have, 0); // Un-pause SDL_PauseAudioDevice(audio_device, 0); } engine_startup(&memory); bool window_focus = true; bool is_running = true; keystate_t keystate_current = {0}; keystate_t keystate_previous = {0}; uint64_t previous_time = SDL_GetTicks64(); while (is_running) { float dt; uint16_t frame_time; { uint64_t current_time = SDL_GetTicks64(); frame_time = current_time - previous_time; if (frame_time < TARGET_FRAME_TIME) { uint64_t sleep_time = TARGET_FRAME_TIME - frame_time; SDL_Delay(sleep_time); current_time = SDL_GetTicks64(); frame_time = current_time - previous_time; } previous_time = current_time; dt = frame_time / 1000.0f; } bool quit_requested = false; { SDL_Event e; while (SDL_PollEvent(&e)) { if (e.type == SDL_QUIT) { quit_requested = true; } else if (e.type == SDL_WINDOWEVENT) { if (e.window.event == SDL_WINDOWEVENT_FOCUS_LOST) { window_focus = false; } else if (e.window.event == SDL_WINDOWEVENT_FOCUS_GAINED) { window_focus = true; } } else if (e.type == SDL_KEYDOWN || e.type == SDL_KEYUP) { switch (e.key.keysym.sym) { case SDLK_w: { keystate_current.w = e.type; } break; case SDLK_a: { keystate_current.a = e.type; } break; case SDLK_s: { keystate_current.s = e.type; } break; case SDLK_d: { keystate_current.d = e.type; } break; case SDLK_UP: { keystate_current.up = e.type; } break; case SDLK_LEFT: { keystate_current.left = e.type; } break; case SDLK_DOWN: { keystate_current.down = e.type; } break; case SDLK_RIGHT: { keystate_current.right = e.type; } break; case SDLK_e: { keystate_current.e = e.type; } break; case SDLK_q: { keystate_current.q = e.type; } break; case SDLK_RETURN: { keystate_current.enter = e.type; } break; case SDLK_SPACE: { keystate_current.space= e.type; } break; case SDLK_ESCAPE: { keystate_current.esc = e.type; } break; case SDLK_LSHIFT: { keystate_current.shift = e.type; } break; case SDLK_F1: { keystate_current.f1= e.type; } break; } } } } input_t input = {0}; SDL_GetRelativeMouseState(&input.mouse_x_delta, &input.mouse_y_delta); // Continuous inputs if (keystate_current.w == SDL_KEYDOWN) { input.forward = true; } if (keystate_current.a == SDL_KEYDOWN) { input.strafe_left = true; } if (keystate_current.s == SDL_KEYDOWN) { input.backward = true; } if (keystate_current.d == SDL_KEYDOWN) { input.strafe_right = true; } // Debounced inputs if (keystate_current.up == SDL_KEYDOWN) { if (keystate_current.up != keystate_previous.up) { input.up = true; } } if (keystate_current.down == SDL_KEYDOWN) { if (keystate_current.down != keystate_previous.down) { input.down = true; } } if (keystate_current.left == SDL_KEYDOWN) { if (keystate_current.left != keystate_previous.left) { input.left = true; } } if (keystate_current.left == SDL_KEYDOWN) { if (keystate_current.left != keystate_previous.left) { input.right = true; } } if (keystate_current.e == SDL_KEYDOWN) { if (keystate_current.e != keystate_previous.e) { input.interact = true; } } if (keystate_current.enter == SDL_KEYDOWN) { if (keystate_current.enter != keystate_previous.enter) { input.select = true; } } if (keystate_current.esc == SDL_KEYDOWN) { if (keystate_current.esc != keystate_previous.esc) { input.pause = true; } } #ifdef BUILD_RELEASE engine_update(dt, input, window_focus, &quit_requested); engine_render(); #else // Continuous inputs if (keystate_current.q == SDL_KEYDOWN) { input.debug.down = true; } if (keystate_current.e == SDL_KEYDOWN) { input.debug.up = true; } if (keystate_current.shift == SDL_KEYDOWN) { input.debug.speed = true; } // Debounced inputs if (keystate_current.f1 == SDL_KEYDOWN) { if (keystate_current.f1 != keystate_previous.f1) { input.debug.enable = true; } } { struct stat attrib; stat(debug.lib_name, &attrib); time_t mod_time = attrib.st_mtime; if (mod_time != debug.mod_time) { SDL_CloseAudioDevice(audio_device); SDL_UnloadObject(debug.lib_ptr); debug.lib_ptr = NULL; engine_debug= NULL; engine_update = NULL; engine_render = NULL; engine_audio_callback = NULL; engine_shutdown = NULL; // Failure here is likely the first few times while it attempts to load a library // that has not finished compiling, so we let it fail and try again next time debug.lib_ptr = SDL_LoadObject(debug.lib_name); if (debug.lib_ptr) { debug.mod_time = mod_time; engine_update = SDL_LoadFunction(debug.lib_ptr, "engine_update"); engine_render = SDL_LoadFunction(debug.lib_ptr, "engine_render"); engine_shutdown = SDL_LoadFunction(debug.lib_ptr, "engine_shutdown"); engine_debug = SDL_LoadFunction(debug.lib_ptr, "engine_debug"); engine_reload = SDL_LoadFunction(debug.lib_ptr, "engine_reload"); engine_audio_callback = SDL_LoadFunction(debug.lib_ptr, "engine_audio_callback"); if (engine_audio_callback) { // Need to re-initialize the audio device so it gets the updated callback pointer audio_specs.callback = engine_audio_callback; SDL_AudioSpec have; audio_device = SDL_OpenAudioDevice(NULL, 0, &audio_specs, &have, 0); SDL_PauseAudioDevice(audio_device, 0); } } } } if (engine_update && engine_render && engine_debug && engine_reload) { engine_reload(&memory); uint64_t update_time; { uint64_t pre_time = SDL_GetTicks64(); engine_update(dt, input, window_focus, &quit_requested); uint64_t post_time = SDL_GetTicks64(); update_time = post_time - pre_time; } uint16_t render_time; { uint64_t pre_time = SDL_GetTicks64(); engine_render(); uint64_t post_time = SDL_GetTicks64(); render_time = post_time - pre_time; } engine_debug(dt, input, update_time, render_time, frame_time); } #endif keystate_previous = keystate_current; is_running = !quit_requested; SDL_GL_SwapWindow(window); } engine_shutdown(); #ifdef BUILD_DEBUG SDL_UnloadObject(debug.lib_ptr); #endif SDL_QuitSubSystem(SDL_INIT_AUDIO); SDL_QuitSubSystem(SDL_INIT_VIDEO); SDL_Quit(); return EXIT_SUCCESS; }