// The OpenGL 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 #include #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" ; struct gl_platform { GLFWwindow *win; GLuint vao; GLuint vbo; GLuint program; GLint uniform_tex_loc; GLuint screen_handle; }; struct gl_platform p; 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(struct 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 init GLAD"); struct 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(struct ty_vec2) * 2, (void*)0 ); glEnableVertexAttribArray(0); glVertexAttribPointer( 1, 2, GL_FLOAT, GL_FALSE, sizeof(struct ty_vec2) * 2, (void*)sizeof(struct 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(struct ty_image img) { 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(); } struct ty_vec2i ty_platform_get_mouse(void) { double x, y; glfwGetCursorPos(p.win, &x, &y); return ty_vec2i(x, y); } static int button_to_glfw_key(enum ty_button btn) { switch (btn) { case TY_BTN_LEFT_UP: return GLFW_KEY_W; case TY_BTN_LEFT_DOWN: return GLFW_KEY_S; case TY_BTN_LEFT_LEFT: return GLFW_KEY_A; case TY_BTN_LEFT_RIGHT: return GLFW_KEY_D; case TY_BTN_RIGHT_UP: return GLFW_KEY_I; case TY_BTN_RIGHT_DOWN: return GLFW_KEY_K; case TY_BTN_RIGHT_LEFT: return GLFW_KEY_J; case TY_BTN_RIGHT_RIGHT: return GLFW_KEY_L; case TY_BTN_ACTION_1: return GLFW_KEY_Q; case TY_BTN_ACTION_2: return GLFW_KEY_E; case TY_BTN_ACTION_3: return GLFW_KEY_U; case TY_BTN_ACTION_4: return GLFW_KEY_O; case TY_BTN_DB_CTRL: return GLFW_KEY_LEFT_CONTROL; case TY_BTN_DB_SHIFT: return GLFW_KEY_LEFT_SHIFT; } return 0; } bool ty_platform_is_button_down(enum ty_button btn) { return glfwGetKey(p.win, button_to_glfw_key(btn)) == GLFW_PRESS; }