#include #include #include #include #include #include #include #define STB_IMAGE_IMPLEMENTATION #include #if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) /* include winapi */ #include #elif defined(__APPLE__) /* mac includes */ #elif defined(__linux) || defined(__linux__) || defined(linux) #include #include #endif #include #include #include #include #include #include #include #include #include #include #define DEFAULT_NUM_PROCS 8 Instance* GLOBAL_PLATFORM = NULL; #define DEFAULT_CAMERA { .pos = {3, 0, 0}, .dir = {1, 1, 1}, } static Camera default_camera = DEFAULT_CAMERA; input_callback_t* callbacks[128]; usize callbacks_len; i32 nproc(void) { return get_nprocs(); } void delay( uint64_t us ) { #if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) Sleep( ms ); #else struct timespec ts = { .tv_sec = (i64)us / 1000000, .tv_nsec = ((i64)us * 1000) % 1000000000 }; struct timespec rem = {0,0}; int err = 0; while((err = clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, &rem))) { switch (err) { case EINTR: WARN("clock_nanosleep didn't sleep full duration: interrupted."); break; case EINVAL: WARN("clock_nanosleep didn't sleep full duration: invalid argument."); break; case ENOTSUP: WARN("clock_nanosleep didn't sleep full duration: unsupported clock."); break; default: WARN("clock_nanosleep didn't sleep full duration!"); break; } ts = rem; rem = (struct timespec){0,0}; } #endif } i32 cmp_int(const void* a, const void* b) { const i32* x = a; const i32* y = b; return *x - *y; } /* Creates the window, initializes IO, Rendering, Fonts and engine-specific * resources. */ Instance* engine_init(const char* windowtitle, i32 windowWidth, i32 windowHeight, const u32 flags, const usize initial_memory) { INFO("Engine version " ENGINE_VERSION); #if defined(__linux) || defined(__linux__) || defined(linux) { pid_t pid = getpid(); INFO("Starting with pid %lu", pid); } #endif ivec2 windowsize = {windowWidth, windowHeight}; Instance* p = (Instance*)calloc(1, sizeof(Instance)); Window* w = (Window*)calloc(1, sizeof(Window)); /* initialize resources */ Resources* resources = calloc(1, sizeof(Resources)); // TODO: Initialize them :) w = Window_new(&Platform_GLFW, windowtitle, WINDOW_FRAMEWORK_GLFW, WINDOW_RENDERER_OPENGL, (ivec2){windowsize[0], windowsize[1]}, flags); p->window = w; p->quit = false; p->resources = resources; p->frame = 0; #ifdef _DEBUG engine_fps_max(p, 30); #else engine_fps_max(p, 300); #endif p->mem = memory_new(initial_memory); p->cam = &default_camera; glm_ortho_default(45.f, p->cam->per); // TODO: Add global bindings INFO("Available cores: %d", nproc()); GLOBAL_PLATFORM = p; #ifdef DAW_BUILD_HOTRELOAD #define State(name) \ if (!State_reload(STATE_##name, p->window->bindings, p->window->bindings_len)) { \ ERROR("Failed to reload shared object file for state %s", #name); \ }; #include #undef State #endif return p; } // TODO: MOVE TO RENDERING static f32 default_quad[8] = { -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, }; static f32 default_quad_uv[8] = { 0.f, 0.f, 1.f, 0.f, 1.f, 1.f, 0.f, 1.f, }; static u8 default_quad_ibo[6] = { 0, 1, 2, 2, 3, 0, }; // TODO: CREATE DEFAULT SHADERBUFF #define COUNT(a) sizeof(a) / sizeof(a[0]) static ShaderBuffer shaderbuf[3] = { SHADERBUFFER_NEW(f32, COUNT(default_quad), 2, default_quad, ShaderBuffer_AccessFrequency_static | ShaderBuffer_AccessType_draw | ShaderBuffer_Type_vertexPosition), SHADERBUFFER_NEW(f32, COUNT(default_quad_uv), 2, default_quad_uv, ShaderBuffer_AccessFrequency_static | ShaderBuffer_AccessType_draw), SHADERBUFFER_NEW(u8, COUNT(default_quad_ibo), 3, default_quad_ibo, ShaderBuffer_AccessFrequency_static | ShaderBuffer_AccessType_draw | ShaderBuffer_Type_vertexIndex), }; #undef COUNT static const char* default_quad_shader_vertex_src = "#version 330 core\n" "\n" "layout(location = 0) in vec2 vertexPosition_modelspace;\n" "layout(location = 1) in vec2 vertexUV;\n" "\n" "out vec2 UV;\n" "\n" "void main() {\n" " gl_Position = vec4(vertexPosition_modelspace, 0, 1);\n" "\n" " UV = vertexUV;\n" "}"; static const char* default_quad_shader_fragment_src = "#version 330 core\n" "\n" "// Ouput data\n" "in vec2 UV;\n" "out vec3 color;\n" "\n" "uniform sampler2D textureSampler;\n" "\n" "void main() {\n" " color = texture(textureSampler, UV).rgb;\n" "}"; i32 engine_run(Instance* p, StateType initial_state, void* state_arg) { // TODO: MOVE TO INIT Shader default_quad_shaders[] = { compile_shader(default_quad_shader_vertex_src, Shader_Vertex), compile_shader(default_quad_shader_fragment_src, Shader_Fragment), (Shader){0}, }; default_quad_shaders[2] = compose_shader(default_quad_shaders, 2); RenderObject default_quad_renderobject; if (p == NULL) { ERROR("Platform is uninitialized.\n"); INFO("initialize with `engine_init`"); return -1; } memory* mem = p->mem; StateType state = initial_state; Window* w = p->window; Camera default_renderbuffer_camera = default_camera; { u64 state_init_time = get_time(); State_init(state, p->window, mem, state_arg); if (!w->render_targets) { // Create only 1 additional framebuffer, in addition to the default // one. This is used to render a texture that is represented as a quad // on the default framebuffer. ivec2 wsz; window_get_size(&wsz); u32 t[] = { BUFFERPARAMETER_SET_PARAMETER(BUFFERPARAMETER_SET_TYPE(0, BufferType_texture), BUFFERPARAMETER_TEXTURE_2D | BUFFERPARAMETER_FMT_RGB8), // The depth buffer could also be a texture like so: BUFFERPARAMETER_SET_PARAMETER( BUFFERPARAMETER_SET_TYPE(0, BufferType_texture), BUFFERPARAMETER_TEXTURE_2D | BUFFERPARAMETER_FMT_DEPTH32 ), //BUFFERPARAMETER_SET_PARAMETER(BUFFERPARAMETER_SET_TYPE(0, BufferType_render), BUFFERPARAMETER_RENDERBUFFER_DEPTH | BUFFERPARAMETER_FMT_DEPTH32F), }; FramebufferParameters p[] = { {.num_attached_buffers = sizeof(t) / sizeof(t[0]), .dimensions = {wsz[0], wsz[1], 0}}, }; window_init_renderstack(w, 1, sizeof(t) / sizeof(t[0]), p, t); //w->render_targets->cam[0] = &default_renderbuffer_camera; } u32 first_texture = 0; for (usize i = 0; i < w->render_targets->buffer_len; i++) { if (BUFFERPARAMETER_GET_TYPE(w->render_targets->buffer_parameters[i]) == BufferType_texture) { first_texture = w->render_targets->buffer[i]; break; } } default_quad_renderobject = RenderObject_new( // Shader &default_quad_shaders[2], // Texture first_texture, // Vertices shaderbuf, sizeof(shaderbuf) / sizeof(ShaderBuffer) ); INFO("Initializing state \"%s\" took %.1fns", StateTypeStr[state], (get_time() - state_init_time)); } u64 frame_end = get_time(); // Update ticks u64 ticks = 0; StateType (*update_func)(Window *restrict, void*, f64) = State_updateFunc(state); u64 last_fps_measurement = frame_end; u64 last_fps_ticks = 0; /* The target frametime measured in μs */ const u32 fps_cap = p->fps_target > 0 ? 1000000 / p->fps_target : 0; /* Main loop */ do { /* frame_start is μs since engine was initialized */ const u64 frame_start = get_time(); /* dt measured in μs */ const u64 dt = frame_start - frame_end; /* Delta is relative to FPS cap */ //const f64 delta = (f64)dt / (f64)fps_cap; /* Events */ Platform_GLFW.window_poll(); i_flush_bindings(dt, callbacks_len, callbacks, mem->data); /* Update */ StateType next_state; next_state = update_func(p->window, (void*)(mem->data), dt); if (next_state != STATE_null) { if (next_state == STATE_quit) break; window_reset_drawing(); void* retval = State_free(state, mem); memory_clear(mem); i_ctx_reset(); // Reset camera to default camera p->cam = &default_camera; free(w->render_targets); state = next_state; update_func = State_updateFunc(state); { u64 state_init_time = get_time(); State_init(state, p->window, mem, retval); if (!w->render_targets) { // Create only 1 additional framebuffer, in addition to the default // one. This is used to render a texture that is represented as a quad // on the default framebuffer. FramebufferParameters fb_params[] = { {.num_attached_buffers = 1, {200,200}}, }; u32 t[] = { BUFFERPARAMETER_SET_PARAMETER(BUFFERPARAMETER_SET_TYPE(0, BufferType_texture), BUFFERPARAMETER_TEXTURE_2D) }; window_init_renderstack(w, 1, 1, fb_params, t); //w->render_targets->cam[0] = &default_camera; } u32 first_texture = 0; for (usize i = 0; i < w->render_targets->buffer_len; i++) { if (BUFFERPARAMETER_GET_TYPE(w->render_targets->buffer_parameters[i]) == BufferType_texture) { first_texture = w->render_targets->buffer[i]; break; } } default_quad_renderobject = RenderObject_new( // Shader &default_quad_shaders[2], // Texture first_texture, /*TODO*/ // Vertices shaderbuf, sizeof(shaderbuf) / sizeof(ShaderBuffer) ); INFO("Initializing state \"%s\" took %.1fns", StateTypeStr[state], (get_time() - state_init_time)); } } else { /* Render */ const u64 rendertime_begin = get_time(); render_begin(p->window); render_present(p->window, &default_quad_renderobject); const u64 rendertime_dt = get_time() - rendertime_begin; /* Regulate FPS */ frame_end = get_time(); const i64 fps_diff = fps_cap - (i64)(frame_end - frame_start); if (fps_diff > 0) { delay((u64)fps_diff); } frame_end = frame_start; /* Print stats */ const u64 dt_measurement = frame_end - last_fps_measurement; /* only make measurements once a second */ if (dt_measurement > 1000000) { printf(" FPS: %.1f" "\tticks: %lu" "\tframetime: %.3fms" "\trendertime: %.3fms" "\n" , ((f64)(ticks - last_fps_ticks)) / ((f64)dt_measurement / 1000000.0) , ticks , (double)dt / 1000.0 , (double)rendertime_dt / 1000.0 ); last_fps_measurement = frame_end; last_fps_ticks = ticks; } } ticks++; } while( !Platform_GLFW.window_should_close(p->window) && state != STATE_quit ); return 0; } void engine_stop(Instance* p) { if (p == NULL) return; //{ /* Deallocate resources */ // struct Resources* r = (struct Resources*)p->data; // if (r != NULL) { // /* Destroy textures */ // for (usize i = 0; i < r->textures_len; i++) { // if (r->textures[i] != NULL) { // r->textures[i] = NULL; // } // } // free(r->textures); // /* Destroy Fonts */ // } //} Platform_GLFW.window_destroy(p->window); free(p->resources); } /* Set the maximum framerate */ void engine_fps_max(Instance* p, u16 cap) { LOG("Setting max fps to %llu", cap); p->fps_target = cap; } usize f_get_sz(FILE* f) { if (f == NULL) { ERROR("File was null!"); return 0; } const isize pos = ftell(f); if (pos == -1) { ERROR("Failed to determine file size (%d): %s", errno, strerror(errno)); return 0; } const i32 err = fseek(f, 0, SEEK_END); if (err != 0) { if (err == ESPIPE) { ERROR("File is a pipe"); } else { ERROR("Failed to determine file size!"); } return 0; } const isize size = ftell(f); if (size == -1) { ERROR("Failed to determine file size (%d): %s", errno, strerror(errno)); return 0; } // Reset the position to the position prior to calling f_get_sz fseek(f, pos, SEEK_SET); return (usize)size; }