// Required for OpenGL rendering backend #define GLAD_GL_IMPLEMENTATION #include #undef GLAD_GL_IMPLEMENTATION // TODO: import vulkan thingymajig once I get around to it. // TODO: move OpenGL initialization code at some point #undef GLFW_INCLUDE_NONE #include #include #include static void window_resize_callback(GLFWwindow *restrict window, int width, int height); static void framebuffer_resize_callback(GLFWwindow *restrict window, int width, int height); static void glfw_err_callback(int code, const char* description); static void render_init_opengl(Window *restrict w, const u32 flags); void window_size_glfw(GLFWwindow *restrict w, ivec2 *restrict dst); Window* window_init_glfw(const char *restrict windowtitle, ivec2 windowsize, const u32 flags) { Window* ret = NULL; GLFWwindow* window = NULL; glfwSetErrorCallback(&glfw_err_callback); INFO_("initializing glfw..."); if (glfwInit() == GLFW_FALSE) { const char *desc; int code = glfwGetError(&desc); ERROR("failed to initialize glfw [%d]: %s\n", code, *desc); exit(EXIT_FAILURE); } else printf("ok\n"); INFO_("initializing window..."); //glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); if (!(flags & DAW_WINDOW_RESIZEABLE)) { glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); } glfwWindowHint(GLFW_DECORATED, GLFW_FALSE); glfwWindowHint(GLFW_FLOATING, GLFW_TRUE); glfwWindowHint(GLFW_SAMPLES, 0); // Disable anti aliasing // Use a modern opengl version glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6); // Lean and mean glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); #ifdef __APPLE__ // To make MacOS happy; should not be needed glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); #endif /* "On Wayland specifically, you need to swap the buffers * once of a window for it to become visible." */ { GLFWmonitor* mon = NULL; if (flags & DAW_WINDOW_FULLSCREEN) mon = glfwGetPrimaryMonitor(); window = glfwCreateWindow(windowsize[0], windowsize[1], windowtitle, mon, NULL); } if (window == NULL) { ERROR("Failed to create GLFW window!\n"); const char *desc; int code = glfwGetError(&desc); ERROR("failed to initialize glfw window [%d]: %s\n", code, desc); exit(EXIT_FAILURE); } else printf("ok\n"); // Setup callbacks glfwSetFramebufferSizeCallback(window, framebuffer_resize_callback); glfwSetWindowSizeCallback(window, window_resize_callback); glfwSetKeyCallback(window, (GLFWkeyfun)key_callback); // Create the window datastructure ret = (Window*)calloc(1, sizeof(Window)); ret->framework = WINDOW_FRAMEWORK_GLFW; ret->renderer = WINDOW_RENDERER_NONE; ret->window = window; /* Last parameter is used for the renderer */ ret->context = NULL; { ivec2 wsize; vec2 wscaling; glfwGetWindowContentScale(window, &wscaling[0], &wscaling[1]); glfwGetWindowSize(window, &wsize[0], &wsize[1]); ret->windowsize[0] = (i32)((f32)wsize[0] * wscaling[0]); ret->windowsize[1] = (i32)((f32)wsize[1] * wscaling[1]); INFO("WINDOW CONTENT SCALING: %.2f x %.2f", wscaling[0], wscaling[1]); INFO("WINDOW SIZE: %d x %d -> %d x %d", wsize[0], wsize[1], ret->windowsize[0], ret->windowsize[1]); } // TODO: set this to `ret` once all the garbage is moved to `struct Window` glfwSetWindowUserPointer(window, (void*)ret); render_init_opengl(ret, flags); return ret; } void window_destroy_glfw(Window *restrict w) { glfwDestroyWindow(w->window); w->window = NULL; // If we ever do multi-window support, we need to make sure this is the last // window before terminating glfwTerminate(); switch(w->renderer) { case WINDOW_RENDERER_OPENGL: // Missing unloader function in glad MX library free(w->context); w->context = NULL; break; default: ERROR("Destroying unknown renderer type."); } } void window_resize_glfw(Window *restrict window, int width, int height) { window_resize_callback(window->window, width, height); framebuffer_resize_callback(window->window, width, height); } bool window_should_close_glfw(Window *restrict window) { return glfwWindowShouldClose(window->window); } void window_poll_glfw(void) { glfwPollEvents(); } // Helper function implementations static void window_resize_callback(GLFWwindow* window, int width, int height) { (void)width; (void)height; Window* w = glfwGetWindowUserPointer(window); glfwSwapBuffers(window); if (w != NULL) { const GladGLContext* gl = w->context; gl->Finish(); } } static void framebuffer_resize_callback(GLFWwindow* window, int width, int height) { (void)width; (void)height; Window* w = glfwGetWindowUserPointer(window); if (w != NULL) { const GladGLContext* gl = w->context; //TODO: Move the camera to window->renderer //Camera* c = w->cam; gl->Viewport(0,0, width, height); w->windowsize[0] = width; w->windowsize[1] = height; //r_reset_camera(c); } } static void glfw_err_callback(int code, const char* description) { ERROR("glfw [%d]: %s\n", code, description); // Terminate? exit(EXIT_FAILURE); } static void render_init_opengl(Window *restrict w, const u32 flags) { if (w->renderer != WINDOW_RENDERER_NONE || w->context != NULL) { ERROR("Window already initialized with a renderer!"); return; } // This is GLFW specific glfwMakeContextCurrent(w->window); GladGLContext* ctx = (GladGLContext*)malloc(sizeof(GladGLContext)); if (!ctx) { ERROR("Failed to allocate memory for context"); } int version = gladLoadGLContext(ctx, glfwGetProcAddress); INFO("Loaded OpenGL %d.%d", GLAD_VERSION_MAJOR(version), GLAD_VERSION_MINOR(version)); if (ctx == NULL) { ERROR("Failed to create glad context"); exit(EXIT_FAILURE); } if (flags & DAW_WINDOW_VSYNC) { glfwSwapInterval(1); } else { glfwSwapInterval(0); } ctx->Viewport(0, 0, w->windowsize[0], w->windowsize[1]); #ifdef _DEBUG ctx->ClearColor((float)0x10 / 255.f, (float)0x0a / 255.f, (float)0x33 / 255.f, 0.f); #else ctx->ClearColor(0x0, 0x0, 0x0, 0.f); #endif // Make sure faces closest to the camera are drawn on-top of faces that are // further away ctx->Enable(GL_DEPTH_TEST); ctx->DepthFunc(GL_LESS); w->context = ctx; w->renderer = WINDOW_RENDERER_OPENGL; } void window_size_glfw(GLFWwindow *restrict w, ivec2 *restrict dst) { ivec2 wsize; vec2 wscaling; glfwGetWindowContentScale(w, &wscaling[0], &wscaling[1]); glfwGetWindowSize(w, &wsize[0], &wsize[1]); *dst[0] = (i32)((f32)wsize[0] * wscaling[0]); *dst[1] = (i32)((f32)wsize[1] * wscaling[1]); }