aboutsummaryrefslogtreecommitdiff
path: root/platform/gl/gl.c
diff options
context:
space:
mode:
authoriamcheeseman <[email protected]>2026-05-24 17:00:30 -0400
committeriamcheeseman <[email protected]>2026-05-24 17:00:30 -0400
commit09d34f7b62f0bbf49b8c946b23c29cc51e7d67f9 (patch)
tree7e59d734447e923a6c4b0661c489d2aea65480d9 /platform/gl/gl.c
parent39a02a3bf2fe923d595e4f8df693a524c47291d5 (diff)
move `teensy/platform` -> `platform/`
Diffstat (limited to 'platform/gl/gl.c')
-rw-r--r--platform/gl/gl.c309
1 files changed, 309 insertions, 0 deletions
diff --git a/platform/gl/gl.c b/platform/gl/gl.c
new file mode 100644
index 0000000..3e454d2
--- /dev/null
+++ b/platform/gl/gl.c
@@ -0,0 +1,309 @@
+// 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 <glad/glad.h>
+#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;
+} Gl_Platform;
+
+typedef unsigned int GLuint;
+typedef unsigned int GLenum;
+
+Gl_Platform p;
+
+GLuint glCreateShader(GLenum type);
+void glCompileShader(GLuint shader);
+void glShaderSource(GLuint shader, int n, const char **src, void *ptr);
+void glGetShaderiv(GLuint shader, GLenum what, int *res);
+void glGetShaderInfoLog(GLuint shader, size_t len, void *ptr, char *txt);
+void glGenVertexArrays(int n, GLuint *vaos);
+void glGenBuffers(int n, GLuint *bos);
+void glBindVertexArray(GLuint vao);
+void glBindBuffer(GLenum type, GLuint bo);
+void glBufferData(GLenum type, size_t size, void *data, GLenum draw);
+void glVertexAttribPointer();
+void glEnableVertexAttribArray(int n);
+GLuint glCreateProgram(void);
+void glAttachShader(GLuint prog, GLuint shader);
+void glLinkProgram(GLuint prog);
+void glDeleteShader(GLuint shader);
+void glGetProgramiv(GLuint prog, GLenum type, int *res);
+void glGetProgramInfoLog(GLuint prog, size_t len, void *ptr, char *txt);
+int glGetUniformLocation(GLuint prog, const char *name);
+void glDeleteProgram(GLuint prog);
+void glDeleteBuffers(int n, GLuint *bos);
+void glDeleteVertexArrays(int n, GLuint *vaos);
+void glGenerateMipmap(GLenum type);
+void glUseProgram(GLuint prog);
+void glUniform1i(int loc, int val);
+
+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);
+
+ 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(x / scalex, 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;
+}