Letterbox, maintain render-resolution's aspect ratio
This commit is contained in:
@@ -121,7 +121,7 @@ get_camera_projection_matrix :: proc(renderer: ^Renderer, camera: ^Camera) -> (l
|
||||
case Camera_Perspective: {
|
||||
projection_matrix *= linalg.matrix4_perspective(
|
||||
linalg.to_radians(f32(t.fov_degrees)),
|
||||
get_aspect_ratio(renderer),
|
||||
get_aspect_ratio(renderer, .Render),
|
||||
camera.near,
|
||||
camera.far,
|
||||
flip_z_axis = true,
|
||||
@@ -129,7 +129,7 @@ get_camera_projection_matrix :: proc(renderer: ^Renderer, camera: ^Camera) -> (l
|
||||
}
|
||||
case Camera_Orthographic: {
|
||||
half_h := t.height / 2
|
||||
half_w := half_h * get_aspect_ratio(renderer)
|
||||
half_w := half_h * get_aspect_ratio(renderer, .Render)
|
||||
|
||||
projection_matrix *= linalg.matrix_ortho3d(
|
||||
-half_w, half_w,
|
||||
|
||||
11
mesh.odin
11
mesh.odin
@@ -161,6 +161,8 @@ create_mesh_from_primitive :: proc(renderer: ^Renderer, primitive_mesh_type: Pri
|
||||
|
||||
vertices := make([dynamic]f32, 0)
|
||||
indices := make([dynamic]u32, 0)
|
||||
defer delete(vertices)
|
||||
defer delete(indices)
|
||||
|
||||
for y in 0..=Y_SEGMENTS {
|
||||
y_segment := f32(y) / f32(Y_SEGMENTS)
|
||||
@@ -233,4 +235,13 @@ create_mesh_from_primitive :: proc(renderer: ^Renderer, primitive_mesh_type: Pri
|
||||
}
|
||||
|
||||
return {}, false
|
||||
}
|
||||
|
||||
delete_mesh :: proc(renderer: ^Renderer, mesh: ^Mesh) {
|
||||
assert(renderer != nil)
|
||||
assert(mesh != nil)
|
||||
|
||||
when RENDER_BACKEND_OPENGL {
|
||||
opengl_delete_mesh(renderer, mesh)
|
||||
}
|
||||
}
|
||||
@@ -243,10 +243,12 @@ delete_pass :: proc(pass: ^Pass) {
|
||||
}
|
||||
|
||||
@(private) delete_scene_pass :: proc(pass: ^Scene_Pass) {
|
||||
assert(pass.draw_commands != nil)
|
||||
delete(pass.draw_commands)
|
||||
}
|
||||
|
||||
@(private) delete_post_processing_pass :: proc(pass: ^Post_Processing_Pass) {
|
||||
assert(pass.post_processing_nodes != nil)
|
||||
delete(pass.post_processing_nodes)
|
||||
}
|
||||
|
||||
@@ -294,9 +296,10 @@ execute_pass :: proc(renderer: ^Renderer, pass: ^Pass, view_matrix, projection_m
|
||||
should_clear_depth := should_write_depth
|
||||
|
||||
should_clear_color := true
|
||||
should_scissor :: false
|
||||
set_clear_color(renderer, RGBA_Color { 0, 0, 0, 0 })
|
||||
|
||||
clear_screen(renderer, should_clear_color, should_clear_depth)
|
||||
clear_screen(renderer, should_clear_color, should_clear_depth, should_scissor)
|
||||
|
||||
apply_depth(renderer, should_test_depth, should_write_depth)
|
||||
|
||||
apply_blend_mode(renderer, t.blend_mode)
|
||||
|
||||
@@ -4,7 +4,7 @@ import "core:mem"
|
||||
import "core:log"
|
||||
import "core:fmt"
|
||||
|
||||
PIPELINE_MAX_PASSES :: 8
|
||||
PIPELINE_MAX_PASSES :: 16
|
||||
|
||||
Pipeline :: struct {
|
||||
passes: [PIPELINE_MAX_PASSES]^Pass,
|
||||
@@ -40,10 +40,11 @@ add_pass_to_pipeline :: proc(renderer: ^Renderer, pass: ^Pass) -> bool {
|
||||
|
||||
pipeline := &renderer.pipeline
|
||||
|
||||
if pipeline.amount_of_passes == PIPELINE_MAX_PASSES {
|
||||
log.warnf("Failed to add scene-pass '%v' to renderer's pipeline; hit max capacity (%v).", pass.name, PIPELINE_MAX_PASSES)
|
||||
return false
|
||||
}
|
||||
assert(pipeline.amount_of_passes < PIPELINE_MAX_PASSES)
|
||||
// if pipeline.amount_of_passes == PIPELINE_MAX_PASSES {
|
||||
// log.warnf("Failed to add scene-pass '%v' to renderer's pipeline; hit max capacity (%v).", pass.name, PIPELINE_MAX_PASSES)
|
||||
// return false
|
||||
// }
|
||||
|
||||
pipeline.passes[pipeline.amount_of_passes] = pass
|
||||
pipeline.amount_of_passes += 1
|
||||
|
||||
122
renderer.odin
122
renderer.odin
@@ -12,7 +12,10 @@ RENDER_BACKEND_DIRECTX11 :: #config(RENDER_BACKEND_DIRECTX11, false)
|
||||
RENDER_BACKEND_METAL :: #config(RENDER_BACKEND_METAL, false)
|
||||
|
||||
Renderer :: struct {
|
||||
render_resolution: Resolution,
|
||||
viewport: Viewport,
|
||||
backbuffer: Backbuffer,
|
||||
|
||||
surface_ptr: rawptr,
|
||||
vsync: bool,
|
||||
backend: rawptr,
|
||||
@@ -37,19 +40,44 @@ Polygon_Mode :: enum {
|
||||
Point,
|
||||
}
|
||||
|
||||
Resolution :: struct {
|
||||
width, height: u16,
|
||||
}
|
||||
|
||||
Backbuffer :: struct {
|
||||
resolution: Resolution,
|
||||
}
|
||||
|
||||
Viewport :: struct {
|
||||
x, y, width, height: u16,
|
||||
x, y: u16,
|
||||
res: Resolution,
|
||||
}
|
||||
|
||||
get_aspect_ratio :: proc(renderer: ^Renderer) -> f32 {
|
||||
Aspect_Ratio_Type :: enum { // TODO: SS - Rename?
|
||||
Render,
|
||||
Backbuffer,
|
||||
Viewport,
|
||||
}
|
||||
get_aspect_ratio :: proc(renderer: ^Renderer, type: Aspect_Ratio_Type) -> f32 {
|
||||
assert(renderer != nil)
|
||||
|
||||
res: Resolution
|
||||
switch type {
|
||||
case .Render: res = renderer.render_resolution
|
||||
case .Backbuffer: res = renderer.backbuffer.resolution
|
||||
case .Viewport: res = renderer.viewport.res
|
||||
}
|
||||
|
||||
viewport := &renderer.viewport
|
||||
return f32(viewport.width) / f32(viewport.height)
|
||||
return f32(res.width) / f32(res.height)
|
||||
}
|
||||
|
||||
create :: proc(surface_ptr: rawptr) -> (^Renderer, bool) {
|
||||
create :: proc(render_resolution: Resolution, surface_ptr: rawptr) -> (^Renderer, bool) {
|
||||
if render_resolution.width == 0 || render_resolution.height == 0 {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
renderer := new(Renderer)
|
||||
renderer.render_resolution = render_resolution
|
||||
renderer.surface_ptr = surface_ptr
|
||||
|
||||
when RENDER_BACKEND_OPENGL {
|
||||
@@ -119,21 +147,37 @@ create :: proc(surface_ptr: rawptr) -> (^Renderer, bool) {
|
||||
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)
|
||||
set_backbuffer_resolution :: proc(renderer: ^Renderer, res: Resolution) {
|
||||
assert(renderer != nil)
|
||||
renderer.backbuffer.resolution = res
|
||||
}
|
||||
|
||||
renderer.viewport = {
|
||||
x = x,
|
||||
y = y,
|
||||
width = width,
|
||||
height = height,
|
||||
}
|
||||
set_viewport :: proc {
|
||||
set_viewport_with_viewport,
|
||||
set_viewport_with_x_y_w_h,
|
||||
}
|
||||
|
||||
set_viewport_with_viewport :: proc(renderer: ^Renderer, viewport: Viewport) {
|
||||
assert(renderer != nil)
|
||||
|
||||
// log.infof("Setting viewport to %v:%v, %vx%v.", viewport.x, viewport.y, viewport.res.width, viewport.res.height)
|
||||
|
||||
renderer.viewport = viewport
|
||||
|
||||
when RENDER_BACKEND_OPENGL {
|
||||
opengl_viewport_changed(renderer)
|
||||
}
|
||||
}
|
||||
|
||||
set_viewport_with_x_y_w_h :: proc(renderer: ^Renderer, x, y, width, height: u16) {
|
||||
assert(renderer != nil)
|
||||
|
||||
set_viewport_with_viewport(renderer, Viewport {
|
||||
x, y,
|
||||
{ width, height }
|
||||
})
|
||||
}
|
||||
|
||||
set_vsync :: proc(renderer: ^Renderer, on: bool) {
|
||||
assert(renderer != nil)
|
||||
|
||||
@@ -158,9 +202,9 @@ set_vsync :: proc(renderer: ^Renderer, on: bool) {
|
||||
}
|
||||
}
|
||||
|
||||
@(private) clear_screen :: proc(renderer: ^Renderer, clear_color: bool, clear_depth: bool) {
|
||||
@(private) clear_screen :: proc(renderer: ^Renderer, clear_color, clear_depth, scissor: bool) {
|
||||
when RENDER_BACKEND_OPENGL {
|
||||
opengl_clear_screen(renderer, clear_color, clear_depth)
|
||||
opengl_clear_screen(renderer, clear_color, clear_depth, scissor)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,25 +217,51 @@ render_frame :: proc(renderer: ^Renderer, texture_to_present: ^Texture, clear_co
|
||||
camera_view_matrix, _ := get_camera_view_matrix(renderer.active_camera)
|
||||
camera_projection_matrix, _ := get_camera_projection_matrix(renderer, renderer.active_camera)
|
||||
|
||||
set_clear_color(renderer, clear_color)
|
||||
clear_screen(renderer, true, true)
|
||||
|
||||
for i in 0 ..< renderer.pipeline.amount_of_passes {
|
||||
execute_pass(renderer, renderer.pipeline.passes[i], camera_view_matrix, camera_projection_matrix)
|
||||
}
|
||||
|
||||
apply_polygon_mode(renderer, .Fill)
|
||||
|
||||
|
||||
if texture_to_present != nil { // Present.
|
||||
// Bind to the screen.
|
||||
bind_render_target(renderer, nil)
|
||||
|
||||
|
||||
// Disable depth
|
||||
apply_depth(renderer, false, false)
|
||||
|
||||
compute_letterboxed_viewport :: proc(backbuffer: Resolution, render: Resolution) -> Viewport {
|
||||
bb_w, bb_h := expand_values(backbuffer)
|
||||
r_w, r_h := expand_values(render)
|
||||
|
||||
bb_aspect := f32(bb_w) / f32(bb_h)
|
||||
r_aspect := f32(r_w) / f32(r_h)
|
||||
|
||||
if bb_aspect > r_aspect {
|
||||
// Pillarbox.
|
||||
h := bb_h
|
||||
w := u16(f32(h) * r_aspect)
|
||||
x := (bb_w - w) / 2
|
||||
|
||||
return { x, 0, { w, h } }
|
||||
} else {
|
||||
// Letterbox.
|
||||
w := bb_w
|
||||
h := u16(f32(w) / r_aspect)
|
||||
y := (bb_h - h) / 2
|
||||
|
||||
return { 0, y, { w, h } }
|
||||
}
|
||||
}
|
||||
|
||||
// Clear
|
||||
set_viewport(renderer, 0, 0, renderer.backbuffer.resolution.width, renderer.backbuffer.resolution.height)
|
||||
set_clear_color(renderer, RGB_Color{ 0, 0, 0 })
|
||||
clear_screen(renderer, true, false, false)
|
||||
|
||||
set_viewport(renderer, compute_letterboxed_viewport(renderer.backbuffer.resolution, renderer.render_resolution))
|
||||
set_clear_color(renderer, clear_color)
|
||||
clear_screen(renderer, true, true)
|
||||
clear_screen(renderer, true, true, true)
|
||||
|
||||
// Create a temporary Material.
|
||||
mat := Material {
|
||||
@@ -230,8 +300,6 @@ execute_post_processing_node :: proc(renderer: ^Renderer, node: ^Post_Processing
|
||||
defer bind_render_target(renderer, nil)
|
||||
|
||||
apply_depth(renderer, false, false)
|
||||
set_clear_color(renderer, RGBA_Color { 0, 0, 0, 0 })
|
||||
clear_screen(renderer, true, false)
|
||||
|
||||
// fmt.printfln("TODO: SS - Execute post-processing node '%v' (VS: '%v', FS: '%v').", "NAME", node.program.vertex_shader.path, node.program.fragment_shader.path)
|
||||
|
||||
@@ -252,13 +320,13 @@ execute_post_processing_node :: proc(renderer: ^Renderer, node: ^Post_Processing
|
||||
}
|
||||
|
||||
destroy :: proc(renderer: ^Renderer) {
|
||||
delete_mesh(renderer, &renderer.default_cube_mesh)
|
||||
delete_mesh(renderer, &renderer.default_quad_mesh)
|
||||
delete_mesh(renderer, &renderer.default_sphere_mesh)
|
||||
|
||||
when RENDER_BACKEND_OPENGL {
|
||||
opengl_destroy(renderer)
|
||||
}
|
||||
|
||||
// TODO: SS - Destroy 'default_cube_mesh'
|
||||
// TODO: SS - Destroy 'default_quad_mesh'
|
||||
// TODO: SS - Destroy 'default_sphere_mesh'
|
||||
|
||||
assert(renderer != nil)
|
||||
free(renderer)
|
||||
|
||||
@@ -90,21 +90,23 @@ when RENDER_BACKEND_OPENGL {
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
opengl_viewport_changed :: proc(renderer: ^Renderer) {
|
||||
@(private) opengl_apply_renderer_viewport :: proc(renderer: ^Renderer) {
|
||||
gl.Viewport(
|
||||
i32(renderer.viewport.x), i32(renderer.viewport.y),
|
||||
i32(renderer.viewport.width), i32(renderer.viewport.height)
|
||||
i32(renderer.viewport.res.width), i32(renderer.viewport.res.height)
|
||||
)
|
||||
}
|
||||
|
||||
opengl_viewport_changed :: proc(renderer: ^Renderer) {
|
||||
opengl_apply_renderer_viewport(renderer)
|
||||
}
|
||||
|
||||
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
|
||||
@@ -121,7 +123,17 @@ when RENDER_BACKEND_OPENGL {
|
||||
gl.ClearColor(r, g, b, a)
|
||||
}
|
||||
|
||||
opengl_clear_screen :: proc(renderer: ^Renderer, clear_color: bool, clear_depth: bool) {
|
||||
opengl_clear_screen :: proc(renderer: ^Renderer, clear_color, clear_depth, scissor: bool) {
|
||||
if scissor {
|
||||
gl.Enable(gl.SCISSOR_TEST)
|
||||
gl.Scissor(
|
||||
i32(renderer.viewport.x),
|
||||
i32(renderer.viewport.y),
|
||||
i32(renderer.viewport.res.width),
|
||||
i32(renderer.viewport.res.height)
|
||||
)
|
||||
}
|
||||
|
||||
flags := u32(0)
|
||||
if clear_color {
|
||||
flags |= gl.COLOR_BUFFER_BIT
|
||||
@@ -130,6 +142,10 @@ when RENDER_BACKEND_OPENGL {
|
||||
flags |= gl.DEPTH_BUFFER_BIT
|
||||
}
|
||||
gl.Clear(flags)
|
||||
|
||||
if scissor {
|
||||
gl.Disable(gl.SCISSOR_TEST)
|
||||
}
|
||||
}
|
||||
|
||||
opengl_swap_buffers :: proc(renderer: ^Renderer) {
|
||||
@@ -208,6 +224,18 @@ when RENDER_BACKEND_OPENGL {
|
||||
return m, true
|
||||
}
|
||||
|
||||
opengl_delete_mesh :: proc(renderer: ^Renderer, mesh: ^Mesh) {
|
||||
if mesh == nil { return }
|
||||
|
||||
gl.DeleteBuffers(1, &mesh.backend.vbo)
|
||||
gl.DeleteBuffers(1, &mesh.backend.ebo)
|
||||
gl.DeleteVertexArrays(1, &mesh.backend.vao)
|
||||
|
||||
mesh.backend.vbo = 0
|
||||
mesh.backend.ebo = 0
|
||||
mesh.backend.vao = 0
|
||||
}
|
||||
|
||||
opengl_create_shader :: proc(renderer: ^Renderer, type: Shader_Type, path: string, data: []u8) -> (Shader_OpenGL, bool) {
|
||||
handle: u32
|
||||
|
||||
@@ -544,12 +572,7 @@ when RENDER_BACKEND_OPENGL {
|
||||
if rt == nil {
|
||||
gl.BindFramebuffer(gl.FRAMEBUFFER, 0)
|
||||
|
||||
gl.Viewport(
|
||||
0,
|
||||
0,
|
||||
i32(renderer.viewport.width),
|
||||
i32(renderer.viewport.height),
|
||||
)
|
||||
opengl_apply_renderer_viewport(renderer)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user