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) _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.DeleteBuffers(1, &ebo) 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) }