// The desktop platform. Uses GLFW for an interface with the OS. The code // quality here is meh. Things could be organized more into functions, but as // for now, it works fine, and isn't worth improving immediately. #include "glad/glad.h" #define GLFW_NO_INCLUDE #include "GLFW/glfw3.h" #include "teensy.h" #include "teensy_common.h" #include "teensy_platform.h" static char vert[] = "#version 330 core\n" "layout (location = 0) in vec2 v_pos;\n" "layout (location = 1) in vec2 v_uv;\n" "\n" "out vec2 f_uv;\n" "\n" "void main() {\n" " gl_Position = vec4(v_pos, 0.0, 1.0);\n" " f_uv = v_uv;\n" "}\n" ; static char frag[] = "#version 330 core\n" "\n" "in vec2 f_uv;\n" "\n" "out vec4 color;\n" "\n" "uniform sampler2D tex;\n" "\n" "void main() {\n" " color = texture2D(tex, f_uv);\n" "}\n" ; typedef struct { GLFWwindow *win; GLuint vao; GLuint vbo; GLuint program; GLint uniform_tex_loc; GLuint screen_handle; uint32_t typed; double scroll; } Desktop_Platform; Desktop_Platform p; static void character_callback(GLFWwindow *window, unsigned int cp) { (void)window; p.typed = cp; } static void scroll_callback(GLFWwindow* window, double x, double y) { (void)window; (void)x; p.scroll = y; } static GLuint compile_shader(GLenum type, const char *src) { GLuint shader = glCreateShader(type); glShaderSource(shader, 1, &src, NULL); glCompileShader(shader); int ok; glGetShaderiv(shader, GL_COMPILE_STATUS, &ok); if (!ok) { char msg[1024]; glGetShaderInfoLog(shader, 1024, NULL, msg); ty_log_fatal(TY_PLATFORM_ERR, "failed to compile shader: %s", msg); } return shader; } void ty_platform_init(ty_Ctx *ctx) { ty_log_info("initializing GLFW..."); if (glfwInit() < 0) ty_log_fatal(TY_PLATFORM_ERR, "could not init GLFW"); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); p.win = glfwCreateWindow( ctx->hints.scr_width * 3, ctx->hints.scr_height * 3, ctx->hints.game_title, NULL, NULL ); if (!p.win) ty_log_fatal(TY_PLATFORM_ERR, "could not init GLFW window"); glfwMakeContextCurrent(p.win); ty_log_info("initializing GLAD..."); if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) ty_log_fatal(TY_PLATFORM_ERR, "could not load GLAD"); glfwSetCharCallback(p.win, character_callback); glfwSetScrollCallback(p.win, scroll_callback); ty_Vec2 vertices[] = { ty_vec2(-1, -1), ty_vec2(0, 1), ty_vec2( 1, -1), ty_vec2(1, 1), ty_vec2( 1, 1), ty_vec2(1, 0), ty_vec2(-1, -1), ty_vec2(0, 1), ty_vec2( 1, 1), ty_vec2(1, 0), ty_vec2(-1, 1), ty_vec2(0, 0), }; glGenTextures(1, &p.screen_handle); glBindTexture(GL_TEXTURE_2D, p.screen_handle); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glGenVertexArrays(1, &p.vao); glGenBuffers(1, &p.vbo); glBindVertexArray(p.vao); glBindBuffer(GL_ARRAY_BUFFER, p.vbo); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); glVertexAttribPointer( 0, 2, GL_FLOAT, GL_FALSE, sizeof(ty_Vec2) * 2, (void*)0 ); glEnableVertexAttribArray(0); glVertexAttribPointer( 1, 2, GL_FLOAT, GL_FALSE, sizeof(ty_Vec2) * 2, (void*)sizeof(ty_Vec2) ); glEnableVertexAttribArray(1); p.program = glCreateProgram(); GLuint vert_sh = compile_shader(GL_VERTEX_SHADER, vert); GLuint frag_sh = compile_shader(GL_FRAGMENT_SHADER, frag); glAttachShader(p.program, vert_sh); glAttachShader(p.program, frag_sh); glLinkProgram(p.program); glDeleteShader(vert_sh); glDeleteShader(frag_sh); int ok; glGetProgramiv(p.program, GL_LINK_STATUS, &ok); if (!ok) { char msg[1024]; glGetProgramInfoLog(p.program, 1024, NULL, msg); ty_log_fatal(TY_PLATFORM_ERR, "failed to link shaders: %s", msg); } p.uniform_tex_loc = glGetUniformLocation(p.program, "texture"); } void ty_platform_deinit(void) { ty_log_info("deinitializing OpenGL..."); glDeleteProgram(p.program); glDeleteBuffers(1, &p.vbo); glDeleteVertexArrays(1, &p.vao); glDeleteTextures(1, &p.screen_handle); ty_log_info("deinitializing GLFW..."); glfwDestroyWindow(p.win); glfwTerminate(); } void ty_platform_frame(ty_Image img) { p.scroll = 0; glfwPollEvents(); int win_width, win_height; glfwGetWindowSize(p.win, &win_width, &win_height); glViewport(0, 0, win_width, win_height); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, p.screen_handle); glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, img.width, img.height, 0, GL_RGB, GL_UNSIGNED_BYTE, img.data ); glGenerateMipmap(GL_TEXTURE_2D); glClearColor(0, 0, 0, 1); glClear(GL_COLOR_BUFFER_BIT); glUseProgram(p.program); glUniform1i(p.uniform_tex_loc, 0); glBindVertexArray(p.vao); glDrawArrays(GL_TRIANGLES, 0, 6); glfwSwapBuffers(p.win); if (glfwGetKey(p.win, GLFW_KEY_ESCAPE) == GLFW_PRESS) { glfwSetWindowShouldClose(p.win, true); } } bool ty_platform_os_wants_quit(void) { return glfwWindowShouldClose(p.win); } double ty_platform_get_time(void) { return glfwGetTime(); } ty_Vec2i ty_platform_get_mouse(void) { double x, y; glfwGetCursorPos(p.win, &x, &y); int win_width, win_height; glfwGetWindowSize(p.win, &win_width, &win_height); double scalex = win_width / ctx.hints.scr_width; double scaley = win_height / ctx.hints.scr_height; return ty_vec2i((int)(x / scalex), (int)(y / scaley)); } bool ty_platform_is_button_down(ty_Button btn) { int res = -1; switch (btn) { case TY_BTN_LEFT_UP: res = glfwGetKey(p.win, GLFW_KEY_W); break; case TY_BTN_LEFT_DOWN: res = glfwGetKey(p.win, GLFW_KEY_S); break; case TY_BTN_LEFT_LEFT: res = glfwGetKey(p.win, GLFW_KEY_A); break; case TY_BTN_LEFT_RIGHT: res = glfwGetKey(p.win, GLFW_KEY_D); break; case TY_BTN_RIGHT_UP: res = glfwGetKey(p.win, GLFW_KEY_I); break; case TY_BTN_RIGHT_DOWN: res = glfwGetKey(p.win, GLFW_KEY_K); break; case TY_BTN_RIGHT_LEFT: res = glfwGetKey(p.win, GLFW_KEY_J); break; case TY_BTN_RIGHT_RIGHT: res = glfwGetKey(p.win, GLFW_KEY_L); break; case TY_BTN_ACTION_1: res = glfwGetKey(p.win, GLFW_KEY_Q); break; case TY_BTN_ACTION_2: res = glfwGetKey(p.win, GLFW_KEY_E); break; case TY_BTN_ACTION_3: res = glfwGetKey(p.win, GLFW_KEY_U); break; case TY_BTN_ACTION_4: res = glfwGetKey(p.win, GLFW_KEY_O); break; case TY_BTN_DB_CTRL: res = glfwGetKey(p.win, GLFW_KEY_LEFT_CONTROL); break; case TY_BTN_DB_SHIFT: res = glfwGetKey(p.win, GLFW_KEY_LEFT_SHIFT); break; case TY_BTN_DB_BKSPACE: res = glfwGetKey(p.win, GLFW_KEY_BACKSPACE); break; case TY_BTN_DB_LMB: res = glfwGetMouseButton(p.win, GLFW_MOUSE_BUTTON_LEFT); break; case TY_BTN_DB_RMB: res = glfwGetMouseButton(p.win, GLFW_MOUSE_BUTTON_RIGHT); break; case TY_BTN_DB_MMB: res = glfwGetMouseButton(p.win, GLFW_MOUSE_BUTTON_MIDDLE); break; } return res == GLFW_PRESS; } bool ty_platform_get_typed_char(uint32_t *c) { assert(c); *c = p.typed; if (p.typed == 0) return false; p.typed = 0; return true; } int ty_platform_get_scroll(void) { return (int)p.scroll; }