#+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_depth: bool) { mask := u32(gl.COLOR_BUFFER_BIT) if clear_depth { gl.ClearDepth(1.0) mask |= gl.DEPTH_BUFFER_BIT } gl.Clear(mask) } 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 Mesh_OpenGL :: struct { vbo, vao, ebo: u32, } Shader_OpenGL :: struct { handle: u32, } Shader_Program_OpenGL :: struct { handle: u32, } Texture_OpenGL :: struct { handle: u32, } opengl_create_mesh :: proc(renderer: ^Renderer, 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) gl.VertexAttribPointer(0, 3, gl.FLOAT, gl.FALSE, 8 * size_of(f32), uintptr(0)) gl.EnableVertexAttribArray(0) gl.VertexAttribPointer(1, 3, gl.FLOAT, gl.FALSE, 8 * size_of(f32), uintptr(3 * size_of(f32))) gl.EnableVertexAttribArray(1) gl.VertexAttribPointer(2, 2, gl.FLOAT, gl.FALSE, 8 * size_of(f32), uintptr(6 * size_of(f32))) gl.EnableVertexAttribArray(2) } 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_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_load_texture :: proc(renderer: ^Renderer, t: ^Texture, pixels: rawptr) -> bool { handle: u32 gl.GenTextures(1, &handle) gl.BindTexture(gl.TEXTURE_2D, handle) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) type := gl.UNSIGNED_BYTE switch t.depth { case 8: type = gl.UNSIGNED_BYTE case 16: type = gl.UNSIGNED_SHORT case 32: type = gl.UNSIGNED_INT } format := gl.RGBA switch t.channels { case 1: format = gl.RED case 2: format = gl.RG case 3: format = gl.RGB case 4: format = gl.RGBA } internal := gl.RGBA8 if t.depth == 8 { switch t.channels { case 1: internal = gl.R8 case 2: internal = gl.RG8 case 3: internal = gl.RGB8 case 4: internal = gl.RGBA8 } } else if t.depth == 16 { switch t.channels { case 1: internal = gl.R16 case 2: internal = gl.RG16 case 3: internal = gl.RGB16 case 4: internal = gl.RGBA16 } } // bytes.reverse(bytes.buffer_to_bytes(img.pixels)) gl.TexImage2D( target = gl.TEXTURE_2D, level = 0, internalformat = i32(internal), width = i32(t.width), height = i32(t.height), border = 0, format = u32(format), type = u32(type), pixels = pixels, // pixels = &pixels[0] // pixels = &bytes.reverse(bytes.buffer_to_bytes(&img.pixels))[0] ) 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_enable_depth_testing :: proc(renderer: ^Renderer, enable: bool) { if enable { gl.Enable(gl.DEPTH_TEST) } else { gl.Disable(gl.DEPTH_TEST) } } }