diff options
Diffstat (limited to 'platform/desktop/desktop.c')
| -rw-r--r-- | platform/desktop/desktop.c | 285 |
1 files changed, 285 insertions, 0 deletions
diff --git a/platform/desktop/desktop.c b/platform/desktop/desktop.c new file mode 100644 index 0000000..9574c9c --- /dev/null +++ b/platform/desktop/desktop.c @@ -0,0 +1,285 @@ +// 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; +} Desktop_Platform; + +Desktop_Platform p; + +static +void character_callback(GLFWwindow *window, unsigned int cp) +{ + (void)window; + p.typed = cp; +} + +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); + + 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) +{ + 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; +} |
