Successfully rendered a quad!

This commit is contained in:
2025-11-25 04:14:07 +01:00
parent 3c3df9796a
commit 28ca7625b8
6 changed files with 334 additions and 9 deletions

6
material.odin Normal file
View File

@@ -0,0 +1,6 @@
package renderer
Material :: struct {
shader_program: Shader_Program,
// uniforms, textures, etc.
}

40
mesh.odin Normal file
View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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..<pass.draw_command_count {
command := &pass.draw_commands[i]
activate_material(&command.material)
draw_mesh(&command.mesh)
}
// TODO: SS - "Deactivate" the pass.
// Clear the pass' draw-commands.
pass.draw_command_count = 0
}
when RENDER_BACKEND_OPENGL {
@@ -90,4 +105,20 @@ destroy :: proc(renderer: ^Renderer) {
assert(renderer != nil)
free(renderer)
}
@(private) activate_material :: proc(material: ^Material) {
assert(material != nil)
when RENDER_BACKEND_OPENGL {
opengl_activate_material(material)
}
}
@(private) draw_mesh :: proc(mesh: ^Mesh) {
assert(mesh != nil)
when RENDER_BACKEND_OPENGL {
opengl_draw_mesh(mesh)
}
}

View File

@@ -1,5 +1,7 @@
#+private
package renderer
import "core:strings"
import "core:fmt"
import gl "vendor:OpenGL"
import win "core:sys/windows"
@@ -13,6 +15,9 @@ when RENDER_BACKEND_OPENGL {
}
backend: Backend_OpenGL_Windows
OPENGL_MAJOR :: 3
OPENGL_MINOR :: 3
opengl_init :: proc(renderer: ^Renderer) -> 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)
}
}

81
shader.odin Normal file
View File

@@ -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)
}
}