Files
renderer/renderer_backend_opengl_windows.odin
2025-11-25 19:15:18 +01:00

322 lines
10 KiB
Odin

#+private
package renderer
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) {
opengl_clear_screen(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) {
gl.Clear(gl.COLOR_BUFFER_BIT)
}
opengl_swap_buffers :: proc(renderer: ^Renderer) {
win.SwapBuffers(backend.hdc)
opengl_clear_screen(renderer)
}
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) {
gl.UseProgram(material.shader_program.backend.handle)
gl.ActiveTexture(gl.TEXTURE0)
gl.BindTexture(gl.TEXTURE_2D, material.texture.backend.handle)
}
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) {
}
}