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: {
|
case Camera_Perspective: {
|
||||||
projection_matrix *= linalg.matrix4_perspective(
|
projection_matrix *= linalg.matrix4_perspective(
|
||||||
linalg.to_radians(f32(t.fov_degrees)),
|
linalg.to_radians(f32(t.fov_degrees)),
|
||||||
get_aspect_ratio(renderer),
|
get_aspect_ratio(renderer, .Render),
|
||||||
camera.near,
|
camera.near,
|
||||||
camera.far,
|
camera.far,
|
||||||
flip_z_axis = true,
|
flip_z_axis = true,
|
||||||
@@ -129,7 +129,7 @@ get_camera_projection_matrix :: proc(renderer: ^Renderer, camera: ^Camera) -> (l
|
|||||||
}
|
}
|
||||||
case Camera_Orthographic: {
|
case Camera_Orthographic: {
|
||||||
half_h := t.height / 2
|
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(
|
projection_matrix *= linalg.matrix_ortho3d(
|
||||||
-half_w, half_w,
|
-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)
|
vertices := make([dynamic]f32, 0)
|
||||||
indices := make([dynamic]u32, 0)
|
indices := make([dynamic]u32, 0)
|
||||||
|
defer delete(vertices)
|
||||||
|
defer delete(indices)
|
||||||
|
|
||||||
for y in 0..=Y_SEGMENTS {
|
for y in 0..=Y_SEGMENTS {
|
||||||
y_segment := f32(y) / f32(Y_SEGMENTS)
|
y_segment := f32(y) / f32(Y_SEGMENTS)
|
||||||
@@ -234,3 +236,12 @@ create_mesh_from_primitive :: proc(renderer: ^Renderer, primitive_mesh_type: Pri
|
|||||||
|
|
||||||
return {}, false
|
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) {
|
@(private) delete_scene_pass :: proc(pass: ^Scene_Pass) {
|
||||||
|
assert(pass.draw_commands != nil)
|
||||||
delete(pass.draw_commands)
|
delete(pass.draw_commands)
|
||||||
}
|
}
|
||||||
|
|
||||||
@(private) delete_post_processing_pass :: proc(pass: ^Post_Processing_Pass) {
|
@(private) delete_post_processing_pass :: proc(pass: ^Post_Processing_Pass) {
|
||||||
|
assert(pass.post_processing_nodes != nil)
|
||||||
delete(pass.post_processing_nodes)
|
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_depth := should_write_depth
|
||||||
|
|
||||||
should_clear_color := true
|
should_clear_color := true
|
||||||
|
should_scissor :: false
|
||||||
set_clear_color(renderer, RGBA_Color { 0, 0, 0, 0 })
|
set_clear_color(renderer, RGBA_Color { 0, 0, 0, 0 })
|
||||||
|
clear_screen(renderer, should_clear_color, should_clear_depth, should_scissor)
|
||||||
|
|
||||||
clear_screen(renderer, should_clear_color, should_clear_depth)
|
|
||||||
apply_depth(renderer, should_test_depth, should_write_depth)
|
apply_depth(renderer, should_test_depth, should_write_depth)
|
||||||
|
|
||||||
apply_blend_mode(renderer, t.blend_mode)
|
apply_blend_mode(renderer, t.blend_mode)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import "core:mem"
|
|||||||
import "core:log"
|
import "core:log"
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
|
|
||||||
PIPELINE_MAX_PASSES :: 8
|
PIPELINE_MAX_PASSES :: 16
|
||||||
|
|
||||||
Pipeline :: struct {
|
Pipeline :: struct {
|
||||||
passes: [PIPELINE_MAX_PASSES]^Pass,
|
passes: [PIPELINE_MAX_PASSES]^Pass,
|
||||||
@@ -40,10 +40,11 @@ add_pass_to_pipeline :: proc(renderer: ^Renderer, pass: ^Pass) -> bool {
|
|||||||
|
|
||||||
pipeline := &renderer.pipeline
|
pipeline := &renderer.pipeline
|
||||||
|
|
||||||
if pipeline.amount_of_passes == PIPELINE_MAX_PASSES {
|
assert(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)
|
// if pipeline.amount_of_passes == PIPELINE_MAX_PASSES {
|
||||||
return false
|
// 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.passes[pipeline.amount_of_passes] = pass
|
||||||
pipeline.amount_of_passes += 1
|
pipeline.amount_of_passes += 1
|
||||||
|
|||||||
120
renderer.odin
120
renderer.odin
@@ -12,7 +12,10 @@ RENDER_BACKEND_DIRECTX11 :: #config(RENDER_BACKEND_DIRECTX11, false)
|
|||||||
RENDER_BACKEND_METAL :: #config(RENDER_BACKEND_METAL, false)
|
RENDER_BACKEND_METAL :: #config(RENDER_BACKEND_METAL, false)
|
||||||
|
|
||||||
Renderer :: struct {
|
Renderer :: struct {
|
||||||
|
render_resolution: Resolution,
|
||||||
viewport: Viewport,
|
viewport: Viewport,
|
||||||
|
backbuffer: Backbuffer,
|
||||||
|
|
||||||
surface_ptr: rawptr,
|
surface_ptr: rawptr,
|
||||||
vsync: bool,
|
vsync: bool,
|
||||||
backend: rawptr,
|
backend: rawptr,
|
||||||
@@ -37,19 +40,44 @@ Polygon_Mode :: enum {
|
|||||||
Point,
|
Point,
|
||||||
}
|
}
|
||||||
|
|
||||||
Viewport :: struct {
|
Resolution :: struct {
|
||||||
x, y, width, height: u16,
|
width, height: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
get_aspect_ratio :: proc(renderer: ^Renderer) -> f32 {
|
Backbuffer :: struct {
|
||||||
|
resolution: Resolution,
|
||||||
|
}
|
||||||
|
|
||||||
|
Viewport :: struct {
|
||||||
|
x, y: u16,
|
||||||
|
res: Resolution,
|
||||||
|
}
|
||||||
|
|
||||||
|
Aspect_Ratio_Type :: enum { // TODO: SS - Rename?
|
||||||
|
Render,
|
||||||
|
Backbuffer,
|
||||||
|
Viewport,
|
||||||
|
}
|
||||||
|
get_aspect_ratio :: proc(renderer: ^Renderer, type: Aspect_Ratio_Type) -> f32 {
|
||||||
assert(renderer != nil)
|
assert(renderer != nil)
|
||||||
|
|
||||||
viewport := &renderer.viewport
|
res: Resolution
|
||||||
return f32(viewport.width) / f32(viewport.height)
|
switch type {
|
||||||
|
case .Render: res = renderer.render_resolution
|
||||||
|
case .Backbuffer: res = renderer.backbuffer.resolution
|
||||||
|
case .Viewport: res = renderer.viewport.res
|
||||||
|
}
|
||||||
|
|
||||||
|
return f32(res.width) / f32(res.height)
|
||||||
|
}
|
||||||
|
|
||||||
|
create :: proc(render_resolution: Resolution, surface_ptr: rawptr) -> (^Renderer, bool) {
|
||||||
|
if render_resolution.width == 0 || render_resolution.height == 0 {
|
||||||
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
create :: proc(surface_ptr: rawptr) -> (^Renderer, bool) {
|
|
||||||
renderer := new(Renderer)
|
renderer := new(Renderer)
|
||||||
|
renderer.render_resolution = render_resolution
|
||||||
renderer.surface_ptr = surface_ptr
|
renderer.surface_ptr = surface_ptr
|
||||||
|
|
||||||
when RENDER_BACKEND_OPENGL {
|
when RENDER_BACKEND_OPENGL {
|
||||||
@@ -119,21 +147,37 @@ create :: proc(surface_ptr: rawptr) -> (^Renderer, bool) {
|
|||||||
return renderer, true
|
return renderer, true
|
||||||
}
|
}
|
||||||
|
|
||||||
set_viewport :: proc(renderer: ^Renderer, x, y, width, height: u16) {
|
set_backbuffer_resolution :: proc(renderer: ^Renderer, res: Resolution) {
|
||||||
log.infof("Setting viewport to %v:%v, %vx%v.", x, y, width, height)
|
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 {
|
when RENDER_BACKEND_OPENGL {
|
||||||
opengl_viewport_changed(renderer)
|
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) {
|
set_vsync :: proc(renderer: ^Renderer, on: bool) {
|
||||||
assert(renderer != nil)
|
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 {
|
when RENDER_BACKEND_OPENGL {
|
||||||
opengl_clear_screen(renderer, clear_color, clear_depth)
|
opengl_clear_screen(renderer, clear_color, clear_depth, scissor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,9 +217,6 @@ render_frame :: proc(renderer: ^Renderer, texture_to_present: ^Texture, clear_co
|
|||||||
camera_view_matrix, _ := get_camera_view_matrix(renderer.active_camera)
|
camera_view_matrix, _ := get_camera_view_matrix(renderer.active_camera)
|
||||||
camera_projection_matrix, _ := get_camera_projection_matrix(renderer, 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 {
|
for i in 0 ..< renderer.pipeline.amount_of_passes {
|
||||||
execute_pass(renderer, renderer.pipeline.passes[i], camera_view_matrix, camera_projection_matrix)
|
execute_pass(renderer, renderer.pipeline.passes[i], camera_view_matrix, camera_projection_matrix)
|
||||||
}
|
}
|
||||||
@@ -189,9 +230,38 @@ render_frame :: proc(renderer: ^Renderer, texture_to_present: ^Texture, clear_co
|
|||||||
// Disable depth
|
// Disable depth
|
||||||
apply_depth(renderer, false, false)
|
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
|
// 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)
|
set_clear_color(renderer, clear_color)
|
||||||
clear_screen(renderer, true, true)
|
clear_screen(renderer, true, true, true)
|
||||||
|
|
||||||
// Create a temporary Material.
|
// Create a temporary Material.
|
||||||
mat := Material {
|
mat := Material {
|
||||||
@@ -230,8 +300,6 @@ execute_post_processing_node :: proc(renderer: ^Renderer, node: ^Post_Processing
|
|||||||
defer bind_render_target(renderer, nil)
|
defer bind_render_target(renderer, nil)
|
||||||
|
|
||||||
apply_depth(renderer, false, false)
|
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)
|
// 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,14 +320,14 @@ execute_post_processing_node :: proc(renderer: ^Renderer, node: ^Post_Processing
|
|||||||
}
|
}
|
||||||
|
|
||||||
destroy :: proc(renderer: ^Renderer) {
|
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 {
|
when RENDER_BACKEND_OPENGL {
|
||||||
opengl_destroy(renderer)
|
opengl_destroy(renderer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: SS - Destroy 'default_cube_mesh'
|
|
||||||
// TODO: SS - Destroy 'default_quad_mesh'
|
|
||||||
// TODO: SS - Destroy 'default_sphere_mesh'
|
|
||||||
|
|
||||||
assert(renderer != nil)
|
assert(renderer != nil)
|
||||||
free(renderer)
|
free(renderer)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,21 +90,23 @@ when RENDER_BACKEND_OPENGL {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(private) opengl_apply_renderer_viewport :: proc(renderer: ^Renderer) {
|
||||||
opengl_viewport_changed :: proc(renderer: ^Renderer) {
|
|
||||||
gl.Viewport(
|
gl.Viewport(
|
||||||
i32(renderer.viewport.x), i32(renderer.viewport.y),
|
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 {
|
opengl_set_vsync :: proc(renderer: ^Renderer, on: bool) -> bool {
|
||||||
if win.wglSwapIntervalEXT == nil {
|
if win.wglSwapIntervalEXT == nil {
|
||||||
fmt.printfln("'wglSwapIntervalEXT' is nil.")
|
fmt.printfln("'wglSwapIntervalEXT' is nil.")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Kommer inte in hit.
|
|
||||||
win.wglSwapIntervalEXT(on ? 1 : 0)
|
win.wglSwapIntervalEXT(on ? 1 : 0)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
@@ -121,7 +123,17 @@ when RENDER_BACKEND_OPENGL {
|
|||||||
gl.ClearColor(r, g, b, a)
|
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)
|
flags := u32(0)
|
||||||
if clear_color {
|
if clear_color {
|
||||||
flags |= gl.COLOR_BUFFER_BIT
|
flags |= gl.COLOR_BUFFER_BIT
|
||||||
@@ -130,6 +142,10 @@ when RENDER_BACKEND_OPENGL {
|
|||||||
flags |= gl.DEPTH_BUFFER_BIT
|
flags |= gl.DEPTH_BUFFER_BIT
|
||||||
}
|
}
|
||||||
gl.Clear(flags)
|
gl.Clear(flags)
|
||||||
|
|
||||||
|
if scissor {
|
||||||
|
gl.Disable(gl.SCISSOR_TEST)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
opengl_swap_buffers :: proc(renderer: ^Renderer) {
|
opengl_swap_buffers :: proc(renderer: ^Renderer) {
|
||||||
@@ -208,6 +224,18 @@ when RENDER_BACKEND_OPENGL {
|
|||||||
return m, true
|
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) {
|
opengl_create_shader :: proc(renderer: ^Renderer, type: Shader_Type, path: string, data: []u8) -> (Shader_OpenGL, bool) {
|
||||||
handle: u32
|
handle: u32
|
||||||
|
|
||||||
@@ -544,12 +572,7 @@ when RENDER_BACKEND_OPENGL {
|
|||||||
if rt == nil {
|
if rt == nil {
|
||||||
gl.BindFramebuffer(gl.FRAMEBUFFER, 0)
|
gl.BindFramebuffer(gl.FRAMEBUFFER, 0)
|
||||||
|
|
||||||
gl.Viewport(
|
opengl_apply_renderer_viewport(renderer)
|
||||||
0,
|
|
||||||
0,
|
|
||||||
i32(renderer.viewport.width),
|
|
||||||
i32(renderer.viewport.height),
|
|
||||||
)
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user