From 3d1d31538d30a7f161f9f2b6d5e075ec69d3b860 Mon Sep 17 00:00:00 2001 From: iamcheeseman <[hidden email]> Date: Tue, 3 Feb 2026 22:25:00 -0500 Subject: ditch raylib (icky, and for loser BEGINNERS) --- src/fw/opengl.odin | 363 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 363 insertions(+) create mode 100644 src/fw/opengl.odin (limited to 'src/fw/opengl.odin') diff --git a/src/fw/opengl.odin b/src/fw/opengl.odin new file mode 100644 index 0000000..ab2bb81 --- /dev/null +++ b/src/fw/opengl.odin @@ -0,0 +1,363 @@ +package fw + +import "base:runtime" + +import "core:log" +import "core:fmt" +import "core:math/linalg" +import "core:os" + +import "vendor:glfw" +import gl "vendor:OpenGL" + +@(private) +enum_conv: map[All_Enums]u32 + +vert_shader_src := string(#load("shader.vert")) +frag_shader_src := string(#load("shader.frag")) + +vao: u32 +vbo: u32 +ebo: u32 + +framebuf: struct { + handle: u32, + color: Texture, // so we can reuse pre-existing draw procs + depth: u32, +} +shader_program: u32 + +projection_loc: i32 +view_loc: i32 +tex0_loc: i32 + +// @(private) +// _gl_debug_message :: proc "c" ( +// source: u32, +// type: u32, +// id: u32, +// severity: u32, +// length: i32, +// message: cstring, +// userParam: rawptr, +// ) { +// context = runtime.default_context() +// switch severity { +// case gl.DEBUG_SEVERITY_HIGH: fmt.print("[HIGH]") +// case gl.DEBUG_SEVERITY_MEDIUM: fmt.print("[MEDIUM]") +// case gl.DEBUG_SEVERITY_LOW: fmt.print("[LOW]") +// case gl.DEBUG_SEVERITY_NOTIFICATION: fmt.print("[NOTIF]") +// } +// fmt.printfln(" OpenGL %v: %v", id, message) +// } + +@(private) +_init_vertex_buffer_and_array :: proc(vao: u32, vbo: u32, ebo: u32) { + gl.BindVertexArray(vao) + + gl.BindBuffer(gl.ARRAY_BUFFER, vbo) + gl.BufferData(gl.ARRAY_BUFFER, 0, nil, gl.DYNAMIC_DRAW) + + gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, ebo) + gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, 0, nil, gl.DYNAMIC_DRAW) + + stride := i32(size_of(Vertex)) + + gl.EnableVertexAttribArray(0) + gl.VertexAttribPointer(0, 3, gl.FLOAT, false, stride, offset_of(Vertex, pos)) + + gl.EnableVertexAttribArray(1) + gl.VertexAttribPointer(1, 2, gl.FLOAT, false, stride, offset_of(Vertex, uv)) + + gl.EnableVertexAttribArray(2) + gl.VertexAttribPointer( + 2, + 4, + gl.FLOAT, + false, + stride, + offset_of(Vertex, color), + ) +} + +@(private) +_gl_load_shader :: proc(src: string, type: u32) -> u32 { + shader := gl.CreateShader(type) + csrc := cstring(raw_data(src)) + src_len := i32(len(src)) + gl.ShaderSource(shader, 1, &csrc, &src_len) + gl.CompileShader(shader) + + status: i32 + gl.GetShaderiv(shader, gl.COMPILE_STATUS, &status) + if status == 0 { + gl.DeleteShader(shader) + return 0 + } + + return shader +} + +@(private) +_gl_load_shader_program :: proc(vert_src: string, frag_src: string) -> u32 { + vert_shader := _gl_load_shader(vert_src, gl.VERTEX_SHADER) + defer gl.DeleteShader(vert_shader) + frag_shader := _gl_load_shader(frag_src, gl.FRAGMENT_SHADER) + defer gl.DeleteShader(frag_shader) + + program := gl.CreateProgram() + gl.AttachShader(program, vert_shader) + gl.AttachShader(program, frag_shader) + gl.LinkProgram(program) + + status: i32 + gl.GetProgramiv(program, gl.LINK_STATUS, &status) + if status == 0 { + gl.DeleteProgram(program) + return 0 + } + + return program +} + +gl_init :: proc() { + glfw.MakeContextCurrent(window) + + gl.load_up_to(3, 3, glfw.gl_set_proc_address) + + gl.Enable(gl.DEPTH_TEST) + gl.DepthFunc(gl.LEQUAL) + + // gl.Enable(gl.BLEND); + // gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); + + renderer.vt.start_frame = gl_start_frame + renderer.vt.end_frame = gl_end_frame + renderer.vt.draw = gl_draw + renderer.vt.clear = gl_clear + renderer.vt.init_texture = gl_init_texture + renderer.vt.destroy_texture = gl_destroy_texture + renderer.vt.init_screen = gl_init_screen + + enum_conv[Vertex_Mode.None] = gl.TRIANGLES + enum_conv[Vertex_Mode.Triangles] = gl.TRIANGLES + enum_conv[Vertex_Mode.Triangle_Fan] = gl.TRIANGLE_FAN + enum_conv[Vertex_Mode.Triangle_Strip] = gl.TRIANGLE_STRIP + enum_conv[Vertex_Mode.Lines] = gl.LINES + enum_conv[Vertex_Mode.Line_Strip] = gl.LINE_STRIP + enum_conv[Vertex_Mode.Line_Loop] = gl.LINE_LOOP + enum_conv[Vertex_Mode.Points] = gl.POINTS + + gl.PixelStorei(gl.UNPACK_ALIGNMENT, 1); + + gl.GenBuffers(1, &vbo) + gl.GenBuffers(1, &ebo) + gl.GenVertexArrays(1, &vao) + _init_vertex_buffer_and_array(vao, vbo, ebo) + + shader_program = _gl_load_shader_program(vert_shader_src, frag_shader_src) + projection_loc = gl.GetUniformLocation(shader_program, "projection") + view_loc = gl.GetUniformLocation(shader_program, "view") + tex0_loc = gl.GetUniformLocation(shader_program, "tex0") +} + +gl_deinit :: proc() { + delete(enum_conv) + + destroy_texture(framebuf.color) + gl.DeleteRenderbuffers(1, &framebuf.depth) + gl.DeleteFramebuffers(1, &framebuf.handle) + + gl.DeleteProgram(shader_program) + + gl.DeleteBuffers(1, &vbo) + gl.DeleteVertexArrays(1, &vao) +} + +gl_start_frame :: proc() { + gl.BindFramebuffer(gl.FRAMEBUFFER, framebuf.handle) + gl.Viewport(0, 0, framebuf.color.size.x, framebuf.color.size.y) + renderer.projection = linalg.matrix_ortho3d( + f32(0), f32(framebuf.color.size.x), + f32(framebuf.color.size.y), 0, + -1, 1, + ) +} + +gl_end_frame :: proc() { + gl.BindFramebuffer(gl.FRAMEBUFFER, 0) + + w_width, w_height := glfw.GetWindowSize(window) + + gl.Viewport(0, 0, w_width, w_height) + + renderer.projection = linalg.matrix_ortho3d( + f32(0), f32(w_width), + 0, f32(w_height), + -1, 1, + ) + + draw_rect({0, 0}, {f32(w_width), f32(w_height)}, tex = framebuf.color) + flush_batch(&renderer.draw_call) +} + +gl_draw :: proc(draw_call: Draw_Call) { + draw_call := draw_call + + gl.UseProgram(shader_program) + + gl.ActiveTexture(gl.TEXTURE0) + gl.BindTexture(gl.TEXTURE_2D, draw_call.tex) + + gl.Uniform1i(tex0_loc, 0) + gl.UniformMatrix4fv( + projection_loc, + 1, + false, + transmute([^]f32)(&draw_call.projection), + ) + gl.UniformMatrix4fv(view_loc, 1, false, transmute([^]f32)(&draw_call.view)) + + gl.BindBuffer(gl.ARRAY_BUFFER, vbo) + gl.BufferData( + gl.ARRAY_BUFFER, + size_of(Vertex) * len(draw_call.vertices), + raw_data(draw_call.vertices), + gl.DYNAMIC_DRAW, + ) + + gl.BindVertexArray(vao) + + if !draw_call.indexed { + gl.DrawArrays( + enum_conv[draw_call.vertex_mode], + 0, + i32(len(draw_call.vertices)), + ) + } else { + gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, ebo) + gl.BufferData( + gl.ELEMENT_ARRAY_BUFFER, + size_of(Index) * len(draw_call.indices), + raw_data(draw_call.indices), + gl.DYNAMIC_DRAW, + ) + + gl.DrawElements( + enum_conv[draw_call.vertex_mode], + i32(len(draw_call.indices)), + gl.UNSIGNED_SHORT, + nil, + ) + gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, 0) + } + + gl.BindBuffer(gl.ARRAY_BUFFER, 0) + gl.BindVertexArray(0) +} + +gl_clear :: proc(col: Color) { + gl.ClearColor(col.r, col.g, col.b, col.a) + gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT) +} + +gl_init_texture :: proc( + texture: ^Texture, + data: []u8, + channels: i32, +) { + tex: u32 + gl.GenTextures(1, &tex) + + format := gl.RGBA + switch channels { + case 3: format = gl.RGB + case 4: format = gl.RGBA + case: panic("Unsupported amount of channels. Must be either 3 or 4.") + } + + gl.BindTexture(gl.TEXTURE_2D, tex) + gl.TexImage2D( + gl.TEXTURE_2D, + 0, + i32(format), + texture.size.x, + texture.size.y, + 0, + u32(format), + gl.UNSIGNED_BYTE, + raw_data(data), + ) + + gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST) + gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST) + + gl.GenerateMipmap(gl.TEXTURE_2D) + + texture.handle = tex +} + +gl_destroy_texture :: proc(texture: Texture_Handle) { + texture := texture + gl.DeleteTextures(1, &texture) +} + +gl_init_screen :: proc(screen_size: Vec2i) { + assert(screen_size.x * screen_size.y > 0) + + gl.GenFramebuffers(1, &framebuf.handle) + gl.BindFramebuffer(gl.FRAMEBUFFER, framebuf.handle) + + color_tex: u32 + gl.GenTextures(1, &color_tex) + gl.BindTexture(gl.TEXTURE_2D, color_tex) + + gl.TexImage2D( + gl.TEXTURE_2D, + 0, + gl.RGB, + screen_size.x, + screen_size.y, + 0, + gl.RGB, + gl.UNSIGNED_BYTE, + nil, + ) + gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST) + gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST) + + gl.FramebufferTexture2D( + gl.FRAMEBUFFER, + gl.COLOR_ATTACHMENT0, + gl.TEXTURE_2D, + color_tex, + 0, + ) + + framebuf.color = Texture { + handle = color_tex, + size = screen_size, + } + + gl.GenRenderbuffers(1, &framebuf.depth) + gl.BindRenderbuffer(gl.RENDERBUFFER, framebuf.depth) + gl.RenderbufferStorage( + gl.RENDERBUFFER, + gl.DEPTH_COMPONENT32F, + screen_size.x, + screen_size.y, + ); + gl.FramebufferRenderbuffer( + gl.FRAMEBUFFER, + gl.DEPTH_ATTACHMENT, + gl.RENDERBUFFER, + framebuf.depth, + ); + + if gl.CheckFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE { + log.fatal("[OPENGL] Framebuffer could not be completed") + os.exit(1) + } + + gl.BindFramebuffer(gl.FRAMEBUFFER, 0) +} -- cgit v1.3-2-g0d8e