From 28ca7625b8f042cb85dc5d46072e0391f6638c85 Mon Sep 17 00:00:00 2001 From: samstalhandske Date: Tue, 25 Nov 2025 04:14:07 +0100 Subject: [PATCH] Successfully rendered a quad! --- material.odin | 6 + mesh.odin | 40 +++++++ pass.odin | 23 ++++ renderer.odin | 35 +++++- renderer_backend_opengl_windows.odin | 158 +++++++++++++++++++++++++-- shader.odin | 81 ++++++++++++++ 6 files changed, 334 insertions(+), 9 deletions(-) create mode 100644 material.odin create mode 100644 mesh.odin create mode 100644 shader.odin diff --git a/material.odin b/material.odin new file mode 100644 index 0000000..d1d905b --- /dev/null +++ b/material.odin @@ -0,0 +1,6 @@ +package renderer + +Material :: struct { + shader_program: Shader_Program, + // uniforms, textures, etc. +} \ No newline at end of file diff --git a/mesh.odin b/mesh.odin new file mode 100644 index 0000000..dcb64ce --- /dev/null +++ b/mesh.odin @@ -0,0 +1,40 @@ +package renderer + +Mesh :: struct { + amount_of_indices: u32, + backend: Mesh_Backend, +} + +Mesh_Handle :: distinct u32 +Buffer_Handle :: distinct u32 + +Vertex :: distinct [3]f32 +Index :: distinct u32 + + +create_mesh :: proc(renderer: ^Renderer, vertices: []Vertex, indices: []Index) -> (Mesh, bool) { // TODO: SS - Should probably return a Mesh_Handle. + mesh: Mesh + + if len(vertices) == 0 { + return {}, false + } + if len(indices) == 0 { + return {}, false + } + + m: Mesh + m.amount_of_indices = u32(len(indices)) + + when RENDER_BACKEND_OPENGL { + opengl_mesh, ok := opengl_create_mesh(renderer, vertices, indices) + if !ok { + return {}, false + } + m.backend = opengl_mesh + } + else { + #assert(false) + } + + return m, true +} \ No newline at end of file diff --git a/pass.odin b/pass.odin index d9b817a..51221ca 100644 --- a/pass.odin +++ b/pass.odin @@ -1,6 +1,29 @@ package renderer +import "core:fmt" + +MAX_DRAW_COMMANDS_PER_PASS :: 4096 + Pass :: struct { name: string, clear_color: RGB_Color, + + draw_commands: [MAX_DRAW_COMMANDS_PER_PASS]Draw_Command, + draw_command_count: u32, +} + +Draw_Command :: struct { + mesh: Mesh, + material: Material, +} + +add_command_to_pass :: proc(pass: ^Pass, command: Draw_Command) -> bool { + if pass.draw_command_count >= len(pass.draw_commands) { + return false + } + + pass.draw_commands[pass.draw_command_count] = command + pass.draw_command_count += 1 + + return true } \ No newline at end of file diff --git a/renderer.odin b/renderer.odin index 19346fa..06d0cc2 100644 --- a/renderer.odin +++ b/renderer.odin @@ -65,17 +65,32 @@ set_viewport :: proc(renderer: ^Renderer, x, y, width, height: u16) { } } +@(private="file") clear_screen :: proc(renderer: ^Renderer) { // TODO: SS - Add options here to say if you want to clear depth or not, for example. + when RENDER_BACKEND_OPENGL { + opengl_clear_screen(renderer) + } +} + render_frame :: proc(renderer: ^Renderer) { pipeline := &renderer.pipeline for i in 0 ..< pipeline.amount_of_passes { pass := pipeline.passes[i] - // TODO: SS - "Activate" the pass. set_clear_color(renderer, pass.clear_color) + clear_screen(renderer) - // TODO: SS - Loop over the pass' render-commands. + for i in 0.. bool { renderer.backend = &backend @@ -37,11 +42,28 @@ when RENDER_BACKEND_OPENGL { set_pixel_format_ok := win.SetPixelFormat(backend.hdc, format_index, &pfd) assert(set_pixel_format_ok == win.TRUE, "SetPixelFormat failed") - backend.gl_context = win.wglCreateContext(backend.hdc) - assert(backend.gl_context != nil, "Failed to create OpenGL context") + 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) - // fmt.printfln("GL Context: %v", backend.gl_context) gl.load_up_to(3, 3, proc(p: rawptr, name: cstring) { addr := win.wglGetProcAddress(name) @@ -52,14 +74,13 @@ when RENDER_BACKEND_OPENGL { (^rawptr)(p)^ = addr }) - opengl_swap_buffers(renderer) - return true } + opengl_viewport_changed :: proc(renderer: ^Renderer) { - gl.Clear(gl.COLOR_BUFFER_BIT) + opengl_clear_screen(renderer) gl.Viewport( i32(renderer.viewport.x), @@ -77,10 +98,14 @@ when RENDER_BACKEND_OPENGL { 1.0 ) } + + opengl_clear_screen :: proc(renderer: ^Renderer) { + gl.Clear(gl.COLOR_BUFFER_BIT) + } opengl_swap_buffers :: proc(renderer: ^Renderer) { - gl.Clear(gl.COLOR_BUFFER_BIT) win.SwapBuffers(backend.hdc) + opengl_clear_screen(renderer) } opengl_destroy :: proc(renderer: ^Renderer) { @@ -90,4 +115,123 @@ when RENDER_BACKEND_OPENGL { backend = {} } + + Mesh_Backend :: Mesh_OpenGL + Shader_Backend :: Shader_OpenGL + Shader_Program_Backend :: Shader_Program_OpenGL + + Mesh_OpenGL :: struct { + vbo, vao, ebo: u32, + } + + Shader_OpenGL :: struct { + handle: u32, + } + + Shader_Program_OpenGL :: struct { + handle: u32, + } + + opengl_create_mesh :: proc(renderer: ^Renderer, vertices: []Vertex, indices: []Index) -> (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(Vertex), raw_data(&vertices[0]), gl.STATIC_DRAW) + + gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, m.ebo) + gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, len(indices) * size_of(Index), raw_data(indices), gl.STATIC_DRAW) + + gl.VertexAttribPointer(0, 3, gl.FLOAT, gl.FALSE, 3 * size_of(f32), 0) + gl.EnableVertexAttribArray(0) + + } + 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) + } + + 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) + gl.BindVertexArray(0) + } } \ No newline at end of file diff --git a/shader.odin b/shader.odin new file mode 100644 index 0000000..b60526b --- /dev/null +++ b/shader.odin @@ -0,0 +1,81 @@ +package renderer + +import os "core:os/os2" + +Shader :: struct { + type: Shader_Type, + backend: Shader_Backend, +} + +Shader_Program :: struct { + vertex_shader, fragment_shader: Shader, + backend: Shader_Program_Backend, +} + +Shader_Type :: enum { + Vertex, + Fragment, + // .. +} + +create_shader :: proc(renderer: ^Renderer, type: Shader_Type, path: string) -> (Shader, bool) { + bytes, error := os.read_entire_file_from_path(path, context.allocator) + if error != nil { + return {}, false + } + defer delete(bytes) + + s: Shader + s.type = type + + when RENDER_BACKEND_OPENGL { + shader_opengl, ok := opengl_create_shader(renderer, type, bytes) + if !ok { + return {}, false + } + s.backend = shader_opengl + } + else { + #assert(false) + } + + return s, true +} + +delete_shader :: proc(renderer: ^Renderer, shader: ^Shader) { + assert(shader != nil) + + when RENDER_BACKEND_OPENGL { + opengl_delete_shader(renderer, shader) + } +} + +create_shader_program :: proc(renderer: ^Renderer, vertex_shader, fragment_shader: ^Shader) -> (Shader_Program, bool) { + assert(vertex_shader != nil) + assert(fragment_shader != nil) + + p: Shader_Program + p.vertex_shader = vertex_shader^ + p.fragment_shader = fragment_shader^ + + when RENDER_BACKEND_OPENGL { + program_opengl, ok := opengl_create_shader_program(renderer, vertex_shader, fragment_shader) + if !ok { + return {}, false + } + p.backend = program_opengl + } + else { + #assert(false) + } + + return p, true +} + +delete_shader_program :: proc(renderer: ^Renderer, shader_program: ^Shader_Program) { + assert(shader_program != nil) + + when RENDER_BACKEND_OPENGL { + opengl_delete_shader_program(renderer, shader_program) + } +} \ No newline at end of file