Files
renderer/renderer_backend_opengl_windows.odin

679 lines
22 KiB
Odin

#+private
package renderer
import "core:log"
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)
win.wglSwapIntervalEXT = transmute(win.SwapIntervalEXTType)win.wglGetProcAddress("wglSwapIntervalEXT")
if win.wglSwapIntervalEXT == nil {
opengl_destroy(renderer)
return false
}
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_vsync :: proc(renderer: ^Renderer, on: bool) -> bool {
if win.wglSwapIntervalEXT == nil {
fmt.printfln("'wglSwapIntervalEXT' is nil.")
return false
}
// Kommer inte in hit.
win.wglSwapIntervalEXT(on ? 1 : 0)
return true
}
opengl_set_clear_color :: proc(renderer: ^Renderer, color: Color) {
r, g, b, a: f32 = 1.0, 1.0, 1.0, 1.0
switch &c in color {
case RGB_Color: r, g, b = expand_values(color_to_f32(c))
case RGBA_Color: r, g, b, a = expand_values(color_to_f32(c))
}
gl.ClearColor(r, g, b, a)
}
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, path: string, 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))
str := string(transmute(cstring)(&info_log))
b := transmute([]u8)str
b = b[:len(b) - 1]
log.errorf("OPENGL: Failed to compile shader '%v' of type %v. Log: '%v'.", path, type, string(b))
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))
str := string(transmute(cstring)(&info_log))
b := transmute([]u8)str
b = b[:len(b) - 1]
log.errorf("OPENGL: Failed to create shader-program. Log: '%v'.", string(b))
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_bind_textures_in_material :: proc(material: ^Material) {
assert(material != nil)
for t, i in material.textures[:material.texture_count] {
assert(t != nil)
gl.ActiveTexture(gl.TEXTURE0 + u32(i))
gl.BindTexture(gl.TEXTURE_2D, t.backend.handle)
loc := gl.GetUniformLocation(material.shader_program.backend.handle, fmt.ctprintf("texture%d", i))
gl.Uniform1i(loc, i32(i))
}
}
opengl_activate_shader_program :: proc(program: ^Shader_Program) {
gl.UseProgram(program.backend.handle)
}
opengl_activate_material :: proc(material: ^Material, model_matrix, view_matrix, projection_matrix: linalg.Matrix4x4f32) {
opengl_activate_bind_textures_in_material(material)
// TODO: SS - Move uniforms up one step to the renderer-agnostic part of the codebase.
model_matrix_loc := gl.GetUniformLocation(material.shader_program.backend.handle, "in_model_matrix")
if model_matrix_loc >= 0 {
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")
if view_matrix_loc >= 0 {
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")
if projection_matrix_loc >= 0 {
projection_matrix_as_f32_array := transmute([16]f32)(projection_matrix)
gl.UniformMatrix4fv(projection_matrix_loc, 1, false, &projection_matrix_as_f32_array[0])
}
uv_scale_loc := gl.GetUniformLocation(material.shader_program.backend.handle, "in_uv_scale")
if uv_scale_loc >= 0 {
gl.Uniform2fv(uv_scale_loc, 1, &material.uv_scale[0])
}
}
opengl_activate_fullscreen_material :: proc(material: ^Material) { // TODO: SS - Maybe remove.
assert(material != nil)
opengl_activate_bind_textures_in_material(material)
gl.Disable(gl.DEPTH_TEST)
gl.DepthMask(gl.FALSE)
}
opengl_deactivate_fullscreen_material :: proc() { // TODO: SS - Maybe remove.
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)
status := gl.CheckFramebufferStatus(gl.FRAMEBUFFER)
assert(status == gl.FRAMEBUFFER_COMPLETE)
texture: ^Texture = nil
if len(rt.color_textures) > 0 {
texture = rt.color_textures[0]
}
else {
texture = rt.depth_texture
}
assert(texture != nil)
gl.Viewport(
0,
0,
i32(texture.width),
i32(texture.height),
)
}
opengl_create_render_target :: proc(renderer: ^Renderer, rt: ^Render_Target) -> bool {
fb: u32
gl.GenFramebuffers(1, &fb)
gl.BindFramebuffer(gl.FRAMEBUFFER, fb)
for texture, i in rt.color_textures {
gl.FramebufferTexture2D(
gl.FRAMEBUFFER,
gl.COLOR_ATTACHMENT0 + u32(i),
gl.TEXTURE_2D,
texture.backend.handle,
0
)
}
attachments: [MAX_COLOR_TEXTURES_IN_RENDER_TARGET]u32
attachment_count := i32(0)
for i in 0..<len(rt.color_textures) {
attachments[i] = gl.COLOR_ATTACHMENT0 + u32(i)
attachment_count += 1
}
gl.DrawBuffers(attachment_count, &attachments[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
}
opengl_destroy_render_target :: proc(renderer: ^Renderer, render_target: ^Render_Target) {
gl.DeleteFramebuffers(1, &render_target.backend.handle)
}
opengl_set_shader_uniform_texture :: proc(program: ^Shader_Program, uniform: Uniform_Texture) -> bool {
opengl_activate_shader_program(program)
i := uniform.index
gl.ActiveTexture(gl.TEXTURE0 + u32(i))
gl.BindTexture(gl.TEXTURE_2D, uniform.value.backend.handle)
loc := gl.GetUniformLocation(program.backend.handle, fmt.ctprintf("texture%d", i))
if loc < 0 {
fmt.printfln("texture Loc: %v", loc)
return false
}
gl.Uniform1i(loc, i32(i))
return true
}
opengl_set_shader_uniform_float :: proc(program: ^Shader_Program, uniform: Uniform_Float) -> bool {
opengl_activate_shader_program(program)
loc := gl.GetUniformLocation(program.backend.handle, fmt.ctprintf("%v", uniform.name))
if loc < 0 {
fmt.printfln("float Loc: %v", loc)
return false
}
gl.Uniform1f(loc, uniform.value^)
return true
}
// TODO: SS - This procedure and _float ^^ are very similar and will do pretty much the same thing but with calls to different gl.Uniform-- procedures. Make it generic instead then switch on the type?
opengl_set_shader_uniform_matrix4f32 :: proc(program: ^Shader_Program, uniform: Uniform_Matrix4f32) -> bool {
opengl_activate_shader_program(program)
loc := gl.GetUniformLocation(program.backend.handle, fmt.ctprintf("%v", uniform.name))
if loc < 0 {
fmt.printfln("matrixf32 loc: %v", loc)
return false
}
data := transmute([16]f32)(uniform.value^)
gl.UniformMatrix4fv(loc, 1, gl.FALSE, &data[0])
return true
}
opengl_set_shader_uniform_vector3 :: proc(program: ^Shader_Program, uniform: Uniform_Vector3) -> bool {
opengl_activate_shader_program(program)
loc := gl.GetUniformLocation(program.backend.handle, fmt.ctprintf("%v", uniform.name))
if loc < 0 {
fmt.printfln("vector3 Loc: %v", loc)
return false
}
gl.Uniform3fv(loc, 1, &uniform.value[0])
return true
}
opengl_apply_polygon_mode :: proc(renderer: ^Renderer, mode: Polygon_Mode) {
assert(renderer != nil)
m: u32 = gl.FILL
switch mode {
case .Fill: m = gl.FILL
case .Line: m = gl.LINE
case .Point: m = gl.POINT
}
gl.PolygonMode(gl.FRONT_AND_BACK, m)
}
}