Files
renderer/renderer.odin

169 lines
4.7 KiB
Odin

package renderer
import "core:math/linalg"
import "core:fmt"
import "core:log"
RENDER_BACKEND_OPENGL :: #config(RENDER_BACKEND_OPENGL, false)
RENDER_BACKEND_VULKAN :: #config(RENDER_BACKEND_VULKAN, false)
RENDER_BACKEND_DIRECTX11 :: #config(RENDER_BACKEND_DIRECTX11, false)
RENDER_BACKEND_METAL :: #config(RENDER_BACKEND_METAL, false)
Renderer :: struct {
viewport: Viewport,
surface_ptr: rawptr,
backend: rawptr,
pipeline: Pipeline,
active_camera: ^Camera,
}
Viewport :: struct {
x, y, width, height: u16,
}
get_aspect_ratio :: proc(renderer: ^Renderer) -> f32 {
assert(renderer != nil)
viewport := &renderer.viewport
return f32(viewport.width) / f32(viewport.height)
}
RGB_Color :: [3]u8
RGBA_Color :: [4]u8
create :: proc(surface_ptr: rawptr) -> (^Renderer, bool) {
renderer := new(Renderer)
renderer.surface_ptr = surface_ptr
when RENDER_BACKEND_OPENGL {
if !opengl_init(renderer) {
fmt.printfln("Failed to initialize OpenGL.")
destroy(renderer)
return nil, false
}
}
else {
destroy(renderer)
fmt.printfln("Unhandled backend or no backend selected.")
return nil, false
}
return renderer, true
}
set_viewport :: proc(renderer: ^Renderer, x, y, width, height: u16) {
log.infof("Setting viewport to %v:%v, %vx%v.", x, y, width, height)
renderer.viewport = {
x = x,
y = y,
width = width,
height = height,
}
when RENDER_BACKEND_OPENGL {
opengl_viewport_changed(renderer)
}
}
@(private="file") set_clear_color :: proc(renderer: ^Renderer, color: RGB_Color) {
when RENDER_BACKEND_OPENGL {
opengl_set_clear_color(renderer, color)
}
}
@(private="file") clear_screen :: proc(renderer: ^Renderer, clear_depth: bool) {
when RENDER_BACKEND_OPENGL {
opengl_clear_screen(renderer, clear_depth)
}
}
render_frame :: proc(renderer: ^Renderer) {
if renderer.active_camera == nil {
fmt.printfln("No active camera!")
return
}
view_matrix, view_matrix_ok := get_camera_view_matrix(renderer.active_camera)
assert(view_matrix_ok)
projection_matrix, projection_matrix_ok := get_camera_projection_matrix(renderer, renderer.active_camera)
assert(projection_matrix_ok)
pipeline := &renderer.pipeline
for i in 0 ..< pipeline.amount_of_passes {
pass := pipeline.passes[i]
set_clear_color(renderer, pass.clear_color)
clear_screen(renderer, pass.should_clear_depth)
enable_depth_testing(renderer, pass.should_test_depth)
for i in 0..<pass.draw_command_count {
command := &pass.draw_commands[i]
model_matrix := linalg.identity(linalg.Matrix4x4f32)
// Translate.
translation := linalg.matrix4_translate(command.position)
// Rotate.
rot_x := linalg.matrix4_rotate(linalg.to_radians(command.rotation.x), [3]f32 { 1, 0, 0 })
rot_y := linalg.matrix4_rotate(linalg.to_radians(command.rotation.y), [3]f32 { 0, 1, 0 })
rot_z := linalg.matrix4_rotate(linalg.to_radians(command.rotation.z), [3]f32 { 0, 0, 1 })
rotation := rot_z * rot_y * rot_x
// Scale.
scale := linalg.matrix4_scale(command.scale)
model_matrix *= translation * rotation * scale
activate_material(&command.material, model_matrix, view_matrix, projection_matrix)
draw_mesh(&command.mesh)
}
// TODO: SS - "Deactivate" the pass.
// Clear the pass' draw-commands.
pass.draw_command_count = 0
}
when RENDER_BACKEND_OPENGL {
opengl_swap_buffers(renderer)
}
clear_screen(renderer, true)
}
destroy :: proc(renderer: ^Renderer) {
when RENDER_BACKEND_OPENGL {
opengl_destroy(renderer)
}
assert(renderer != nil)
free(renderer)
}
@(private) activate_material :: proc(material: ^Material, model_matrix, view_matrix, projection_matrix: linalg.Matrix4x4f32) {
assert(material != nil)
when RENDER_BACKEND_OPENGL {
opengl_activate_material(material, model_matrix, view_matrix, projection_matrix)
}
}
@(private) draw_mesh :: proc(mesh: ^Mesh) {
assert(mesh != nil)
when RENDER_BACKEND_OPENGL {
opengl_draw_mesh(mesh)
}
}
@(private) enable_depth_testing :: proc(renderer: ^Renderer, enable: bool) {
when RENDER_BACKEND_OPENGL {
opengl_enable_depth_testing(renderer, true)
}
}