#+private package renderer import "core:math/linalg" import "core:bytes" import "core:slice" import "core:image" import "core:strings" import "core:fmt" import gl "vendor:OpenGL" import win "core:sys/windows" when RENDER_BACKEND_OPENGL { Backend_OpenGL_Windows :: struct { hwnd: win.HWND, hdc: win.HDC, gl_context: win.HGLRC, } backend: Backend_OpenGL_Windows OPENGL_MAJOR :: 3 OPENGL_MINOR :: 3 opengl_init :: proc(renderer: ^Renderer) -> bool { renderer.backend = &backend backend.hwnd = win.HWND(renderer.surface_ptr) backend.hdc = win.GetDC(backend.hwnd) pfd := win.PIXELFORMATDESCRIPTOR { nSize = size_of(win.PIXELFORMATDESCRIPTOR), nVersion = 1, dwFlags = win.PFD_DRAW_TO_WINDOW | win.PFD_SUPPORT_OPENGL | win.PFD_DOUBLEBUFFER, iPixelType = win.PFD_TYPE_RGBA, cColorBits = 32, cDepthBits = 24, cStencilBits = 8, iLayerType = win.PFD_MAIN_PLANE, } format_index := win.ChoosePixelFormat(backend.hdc, &pfd) assert(format_index != 0, "ChoosePixelFormat failed") set_pixel_format_ok := win.SetPixelFormat(backend.hdc, format_index, &pfd) assert(set_pixel_format_ok == win.TRUE, "SetPixelFormat failed") temp_context := win.wglCreateContext(backend.hdc) assert(temp_context != nil, "Failed to create temp OpenGL context") win.wglMakeCurrent(backend.hdc, temp_context) win.wglCreateContextAttribsARB = transmute(win.CreateContextAttribsARBType)win.wglGetProcAddress("wglCreateContextAttribsARB") assert(win.wglCreateContextAttribsARB != nil, "wglCreateContextAttribsARB not available") attribs := [?]i32 { win.WGL_CONTEXT_MAJOR_VERSION_ARB, OPENGL_MAJOR, win.WGL_CONTEXT_MINOR_VERSION_ARB, OPENGL_MINOR, win.WGL_CONTEXT_PROFILE_MASK_ARB, win.WGL_CONTEXT_CORE_PROFILE_BIT_ARB, 0 } backend.gl_context = win.wglCreateContextAttribsARB(backend.hdc, nil, raw_data(&attribs)) assert(backend.gl_context != nil, fmt.tprintf("Failed to create OpenGL %v.%v core context", OPENGL_MAJOR, OPENGL_MINOR)) win.wglMakeCurrent(nil, nil) win.wglDeleteContext(temp_context) win.wglMakeCurrent(backend.hdc, backend.gl_context) gl.load_up_to(3, 3, proc(p: rawptr, name: cstring) { addr := win.wglGetProcAddress(name) if addr == nil { dll := win.LoadLibraryW(win.utf8_to_wstring("opengl32.dll")) addr = win.GetProcAddress(dll, name) } (^rawptr)(p)^ = addr }) opengl_swap_buffers(renderer) return true } opengl_viewport_changed :: proc(renderer: ^Renderer) { gl.Viewport( i32(renderer.viewport.x), i32(renderer.viewport.y), i32(renderer.viewport.width), i32(renderer.viewport.height) ) } opengl_set_clear_color :: proc(renderer: ^Renderer, color: RGB_Color) { gl.ClearColor( f32(color.r) / f32(max(u8)), f32(color.g) / f32(max(u8)), f32(color.b) / f32(max(u8)), 1.0 ) } opengl_clear_screen :: proc(renderer: ^Renderer, clear_color: bool, clear_depth: bool) { flags := u32(0) if clear_color { flags |= gl.COLOR_BUFFER_BIT } if clear_depth { flags |= gl.DEPTH_BUFFER_BIT } gl.Clear(flags) } opengl_swap_buffers :: proc(renderer: ^Renderer) { win.SwapBuffers(backend.hdc) } opengl_destroy :: proc(renderer: ^Renderer) { win.wglMakeCurrent(nil, nil) win.wglDeleteContext(backend.gl_context) win.ReleaseDC(backend.hwnd, backend.hdc) backend = {} } Mesh_Backend :: Mesh_OpenGL Shader_Backend :: Shader_OpenGL Shader_Program_Backend :: Shader_Program_OpenGL Texture_Backend :: Texture_OpenGL Render_Target_Backend :: Render_Target_OpenGL Mesh_OpenGL :: struct { vbo, vao, ebo: u32, } Shader_OpenGL :: struct { handle: u32, } Shader_Program_OpenGL :: struct { handle: u32, } Texture_OpenGL :: struct { handle: u32, } Render_Target_OpenGL :: struct { handle: u32, } opengl_create_mesh :: proc(renderer: ^Renderer, layout: []Mesh_Layout, vertices: []f32, indices: []u32) -> (Mesh_OpenGL, bool) { // fmt.printfln("OPENGL: Creating mesh from vertices: %v.", vertices) m: Mesh_OpenGL gl.GenVertexArrays(1, &m.vao) gl.GenBuffers(1, &m.vbo) gl.GenBuffers(1, &m.ebo) gl.BindVertexArray(m.vao) { gl.BindBuffer(gl.ARRAY_BUFFER, m.vbo) gl.BufferData(gl.ARRAY_BUFFER, len(vertices) * size_of(f32), raw_data(vertices), gl.STATIC_DRAW) gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, m.ebo) gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, len(indices) * size_of(u32), raw_data(indices), gl.STATIC_DRAW) layout_total_stride := u32(0) for l in layout { layout_total_stride += u32(l.amount) * l.type_size; } offset := u32(0) for l, i in layout { // NOTE: SS - Hardcoded to 'gl.FLOAT' gl.VertexAttribPointer(u32(i), i32(l.amount), gl.FLOAT, gl.FALSE, i32(layout_total_stride), uintptr(offset)) gl.EnableVertexAttribArray(u32(i)) offset += u32(l.amount) * l.type_size } } gl.BindVertexArray(0) gl.BindBuffer(gl.ARRAY_BUFFER, 0) return m, true } opengl_create_shader :: proc(renderer: ^Renderer, type: Shader_Type, data: []u8) -> (Shader_OpenGL, bool) { handle: u32 switch type { case .Vertex: { handle = gl.CreateShader(gl.VERTEX_SHADER) } case .Fragment: { handle = gl.CreateShader(gl.FRAGMENT_SHADER) } } assert(handle != 0) src := strings.clone_to_cstring(transmute(string)data, context.allocator) defer delete(src) gl.ShaderSource(handle, 1, &src, nil) gl.CompileShader(handle) success: i32 info_log: [512]u8 gl.GetShaderiv(handle, gl.COMPILE_STATUS, &success) if success != 1 { gl.GetShaderInfoLog(handle, 512, nil, raw_data(&info_log)) fmt.printfln("OPENGL: Failed to compile shader of type %v. Log: '%v'.", type, info_log) return {}, false } s: Shader_OpenGL s.handle = handle return s, true } opengl_delete_shader :: proc(renderer: ^Renderer, shader: ^Shader) { gl.DeleteShader(shader.backend.handle) } opengl_create_shader_program :: proc(renderer: ^Renderer, vertex_shader, fragment_shader: ^Shader) -> (Shader_Program_OpenGL, bool) { handle := gl.CreateProgram() gl.AttachShader(handle, vertex_shader.backend.handle) gl.AttachShader(handle, fragment_shader.backend.handle) gl.LinkProgram(handle) success: i32 info_log: [512]u8 gl.GetProgramiv(handle, gl.LINK_STATUS, &success) if success != 1 { gl.GetProgramInfoLog(handle, 512, nil, raw_data(&info_log)) fmt.printfln("OPENGL: Failed to create shader-program. Log: '%v'.", info_log) return {}, false } s: Shader_Program_OpenGL s.handle = handle return s, true } opengl_delete_shader_program :: proc(renderer: ^Renderer, shader_program: ^Shader_Program) { gl.DeleteProgram(shader_program.backend.handle) } opengl_activate_material :: proc(material: ^Material, model_matrix, view_matrix, projection_matrix: linalg.Matrix4x4f32) { gl.UseProgram(material.shader_program.backend.handle) gl.ActiveTexture(gl.TEXTURE0) gl.BindTexture(gl.TEXTURE_2D, material.texture.backend.handle) model_matrix_loc := gl.GetUniformLocation(material.shader_program.backend.handle, "in_model_matrix") model_matrix_as_f32_array := transmute([16]f32)(model_matrix) gl.UniformMatrix4fv(model_matrix_loc, 1, false, &model_matrix_as_f32_array[0]) view_matrix_loc := gl.GetUniformLocation(material.shader_program.backend.handle, "in_view_matrix") view_matrix_as_f32_array := transmute([16]f32)(view_matrix) gl.UniformMatrix4fv(view_matrix_loc, 1, false, &view_matrix_as_f32_array[0]) projection_matrix_loc := gl.GetUniformLocation(material.shader_program.backend.handle, "in_projection_matrix") projection_matrix_as_f32_array := transmute([16]f32)(projection_matrix) gl.UniformMatrix4fv(projection_matrix_loc, 1, false, &projection_matrix_as_f32_array[0]) } opengl_activate_fullscreen_material :: proc(material: ^Material) { assert(material != nil) gl.UseProgram(material.shader_program.backend.handle) gl.ActiveTexture(gl.TEXTURE0) gl.BindTexture(gl.TEXTURE_2D, material.texture.backend.handle) loc := gl.GetUniformLocation(material.shader_program.backend.handle, "texture0") // eller "uTex" if loc != -1 { gl.Uniform1i(loc, 0) } gl.Disable(gl.DEPTH_TEST) gl.DepthMask(gl.FALSE) } opengl_deactivate_fullscreen_material :: proc() { gl.DepthMask(gl.TRUE) gl.Enable(gl.DEPTH_TEST) } opengl_draw_mesh :: proc(mesh: ^Mesh) { gl.BindVertexArray(mesh.backend.vao) assert(mesh.amount_of_indices < u32(max(i32))) gl.DrawElements(gl.TRIANGLES, i32(mesh.amount_of_indices), gl.UNSIGNED_INT, nil) } opengl_texture_format_from_texture :: proc(t: ^Texture) -> (u32, u32, u32) { internal, format, type: u32 switch t.format { case .R8: { internal = gl.R8 format = gl.RED type = gl.UNSIGNED_BYTE } case .R16F: { internal = gl.R16F format = gl.RED type = gl.HALF_FLOAT } case .R32F: { internal = gl.R32F format = gl.RED type = gl.FLOAT } case .RGBA8: { internal = gl.RGBA8 if t.channels == 3 { format = gl.RGB } else { format = gl.RGBA } type = gl.UNSIGNED_BYTE } case .RGBA16F: { internal = gl.RGBA16F format = gl.RGBA type = gl.HALF_FLOAT } case .R11G11B10F: { internal = gl.R11F_G11F_B10F format = gl.RGB type = gl.UNSIGNED_INT_10F_11F_11F_REV } case .Depth16: { internal = gl.DEPTH_COMPONENT16 format = gl.DEPTH_COMPONENT type = gl.UNSIGNED_SHORT } case .Depth24: { internal = gl.DEPTH_COMPONENT24 format = gl.DEPTH_COMPONENT type = gl.UNSIGNED_INT } case .Depth32F: { internal = gl.DEPTH_COMPONENT32F format = gl.DEPTH_COMPONENT type = gl.FLOAT } case .Depth24_Stencil8: { internal = gl.DEPTH24_STENCIL8 format = gl.DEPTH_STENCIL type = gl.UNSIGNED_INT_24_8 } case .Depth32F_Stencil8: { internal = gl.DEPTH32F_STENCIL8 format = gl.DEPTH_STENCIL type = gl.FLOAT_32_UNSIGNED_INT_24_8_REV } } return internal, format, type } opengl_load_texture :: proc(renderer: ^Renderer, t: ^Texture, pixels: rawptr) -> bool { handle: u32 gl.GenTextures(1, &handle) gl.BindTexture(gl.TEXTURE_2D, handle) switch t.filter_mode { case .Nearest: gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST) case .Linear: gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) case .Nearest_MipMap: gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST_MIPMAP_NEAREST) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST) case .Linear_MipMap: gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) } switch t.wrap_mode { case .Clamp: gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP) case .Repeat: gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT) case .Mirror: gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.MIRRORED_REPEAT) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.MIRRORED_REPEAT) } internal_format, format, type := opengl_texture_format_from_texture(t) gl.TexImage2D( gl.TEXTURE_2D, 0, i32(internal_format), i32(t.width), i32(t.height), 0, u32(format), u32(type), pixels, ) if !t.is_depth { gl.GenerateMipmap(gl.TEXTURE_2D) } t.backend = Texture_OpenGL { handle = handle } return true } opengl_delete_texture :: proc(renderer: ^Renderer, t: ^Texture) { gl.DeleteTextures(1, &t.backend.handle) } opengl_apply_depth :: proc(renderer: ^Renderer, test_depth, write_depth: bool) { if test_depth { gl.Enable(gl.DEPTH_TEST) } else { gl.Disable(gl.DEPTH_TEST) } gl.DepthMask(write_depth) } @(private="file") OPENGL_BLEND_FACTOR_U32_TABLE :: [Blend_Factor]u32 { .Zero = gl.ZERO, .One = gl.ONE, // Color. .Src_Color = gl.SRC_COLOR, .One_Minus_Src_Color = gl.ONE_MINUS_SRC_COLOR, .Dst_Color = gl.DST_COLOR, .One_Minus_Dst_Color = gl.ONE_MINUS_DST_COLOR, // Alpha. .Src_Alpha = gl.SRC_ALPHA, .One_Minus_Src_Alpha = gl.ONE_MINUS_SRC_ALPHA, .Dst_Alpha = gl.DST_ALPHA, .One_Minus_Dst_Alpha = gl.ONE_MINUS_DST_ALPHA, } opengl_set_blending :: proc(renderer: ^Renderer, enable: bool, factors: Blend_Factors) { if enable { gl.Enable(gl.BLEND) } else { gl.Disable(gl.BLEND) // Not sure that I want to return here.. Pretty nice that the blendfunc gets reset correctly even if we're disabling blending. } table := OPENGL_BLEND_FACTOR_U32_TABLE gl.BlendFunc( sfactor = table[factors.source], dfactor = table[factors.destination], ) } opengl_bind_render_target :: proc(renderer: ^Renderer, rt: ^Render_Target) { if rt == nil { gl.BindFramebuffer(gl.FRAMEBUFFER, 0) gl.Viewport( 0, 0, i32(renderer.viewport.width), i32(renderer.viewport.height), ) return } gl.BindFramebuffer(gl.FRAMEBUFFER, rt.backend.handle) gl.Viewport( 0, 0, i32(rt.color_texture.width), i32(rt.color_texture.height), ) } opengl_create_render_target :: proc(renderer: ^Renderer, rt: ^Render_Target) -> bool { fb: u32 gl.GenFramebuffers(1, &fb) gl.BindFramebuffer(gl.FRAMEBUFFER, fb) gl.FramebufferTexture2D( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, rt.color_texture.backend.handle, 0 ) if rt.depth_texture != nil { gl.FramebufferTexture2D( gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, rt.depth_texture.backend.handle, 0 ) } if gl.CheckFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE { fmt.printfln("Framebuffer status: %v", gl.CheckFramebufferStatus(gl.FRAMEBUFFER)) return false } rt.backend = Render_Target_OpenGL { handle = fb } return true } }