Added camera, use MVP.
This commit is contained in:
62
camera.odin
Normal file
62
camera.odin
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
package renderer
|
||||||
|
|
||||||
|
import "core:math/linalg"
|
||||||
|
|
||||||
|
Camera :: struct {
|
||||||
|
position: [3]f32,
|
||||||
|
forward, up: [3]f32,
|
||||||
|
|
||||||
|
fov: f32,
|
||||||
|
near, far: f32,
|
||||||
|
|
||||||
|
yaw, pitch: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
create_camera :: proc(renderer: ^Renderer, position: [3]f32, forward, up: [3]f32, fov, near, far: f32) -> Camera {
|
||||||
|
c: Camera
|
||||||
|
c.position = position
|
||||||
|
c.forward = forward
|
||||||
|
c.up = up
|
||||||
|
c.fov = fov
|
||||||
|
c.near = near
|
||||||
|
c.far = far
|
||||||
|
|
||||||
|
assert(c.forward != {})
|
||||||
|
assert(c.up != {})
|
||||||
|
assert(fov > 0)
|
||||||
|
assert(near > 0)
|
||||||
|
assert(far > 0)
|
||||||
|
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
get_camera_view_matrix :: proc(camera: ^Camera) -> (linalg.Matrix4f32, bool) {
|
||||||
|
if camera == nil {
|
||||||
|
return {}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
center := camera.position + camera.forward
|
||||||
|
view_matrix := linalg.identity(linalg.Matrix4x4f32)
|
||||||
|
view_matrix *= linalg.matrix4_look_at_f32(camera.position, center, camera.up)
|
||||||
|
|
||||||
|
return view_matrix, true
|
||||||
|
}
|
||||||
|
|
||||||
|
get_camera_projection_matrix :: proc(renderer: ^Renderer, camera: ^Camera) -> (linalg.Matrix4f32, bool) {
|
||||||
|
if camera == nil {
|
||||||
|
return {}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
projection_matrix := linalg.identity(linalg.Matrix4x4f32)
|
||||||
|
projection_matrix *= linalg.matrix4_perspective(camera.fov, get_aspect_ratio(renderer), camera.near, camera.far)
|
||||||
|
|
||||||
|
return projection_matrix, true
|
||||||
|
}
|
||||||
|
|
||||||
|
set_active_camera :: proc(renderer: ^Renderer, camera: ^Camera) {
|
||||||
|
renderer.active_camera = camera
|
||||||
|
}
|
||||||
|
|
||||||
|
get_active_camera :: proc(renderer: ^Renderer) -> ^Camera {
|
||||||
|
return renderer.active_camera
|
||||||
|
}
|
||||||
@@ -8,6 +8,9 @@ Pass :: struct {
|
|||||||
name: string,
|
name: string,
|
||||||
clear_color: RGB_Color,
|
clear_color: RGB_Color,
|
||||||
|
|
||||||
|
should_test_depth: bool,
|
||||||
|
should_clear_depth: bool,
|
||||||
|
|
||||||
draw_commands: [MAX_DRAW_COMMANDS_PER_PASS]Draw_Command,
|
draw_commands: [MAX_DRAW_COMMANDS_PER_PASS]Draw_Command,
|
||||||
draw_command_count: u32,
|
draw_command_count: u32,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,12 +15,21 @@ Renderer :: struct {
|
|||||||
backend: rawptr,
|
backend: rawptr,
|
||||||
|
|
||||||
pipeline: Pipeline,
|
pipeline: Pipeline,
|
||||||
|
|
||||||
|
active_camera: ^Camera,
|
||||||
}
|
}
|
||||||
|
|
||||||
Viewport :: struct {
|
Viewport :: struct {
|
||||||
x, y, width, height: u16,
|
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
|
RGB_Color :: [3]u8
|
||||||
RGBA_Color :: [4]u8
|
RGBA_Color :: [4]u8
|
||||||
|
|
||||||
@@ -46,7 +55,7 @@ create :: proc(surface_ptr: rawptr) -> (^Renderer, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
set_viewport :: proc(renderer: ^Renderer, x, y, width, height: u16) {
|
set_viewport :: proc(renderer: ^Renderer, x, y, width, height: u16) {
|
||||||
fmt.printfln("Setting viewport to %v:%v, %vx%v.", x, y, width, height)
|
log.infof("Setting viewport to %v:%v, %vx%v.", x, y, width, height)
|
||||||
|
|
||||||
renderer.viewport = {
|
renderer.viewport = {
|
||||||
x = x,
|
x = x,
|
||||||
@@ -66,39 +75,50 @@ 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.
|
@(private="file") clear_screen :: proc(renderer: ^Renderer, clear_depth: bool) {
|
||||||
when RENDER_BACKEND_OPENGL {
|
when RENDER_BACKEND_OPENGL {
|
||||||
opengl_clear_screen(renderer)
|
opengl_clear_screen(renderer, clear_depth)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render_frame :: proc(renderer: ^Renderer) {
|
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
|
pipeline := &renderer.pipeline
|
||||||
for i in 0 ..< pipeline.amount_of_passes {
|
for i in 0 ..< pipeline.amount_of_passes {
|
||||||
pass := pipeline.passes[i]
|
pass := pipeline.passes[i]
|
||||||
|
|
||||||
set_clear_color(renderer, pass.clear_color)
|
set_clear_color(renderer, pass.clear_color)
|
||||||
clear_screen(renderer)
|
clear_screen(renderer, pass.should_clear_depth)
|
||||||
|
enable_depth_testing(renderer, pass.should_test_depth)
|
||||||
|
|
||||||
for i in 0..<pass.draw_command_count {
|
for i in 0..<pass.draw_command_count {
|
||||||
command := &pass.draw_commands[i]
|
command := &pass.draw_commands[i]
|
||||||
|
|
||||||
transform := linalg.identity(linalg.Matrix4x4f32)
|
model_matrix := linalg.identity(linalg.Matrix4x4f32)
|
||||||
|
|
||||||
scale := linalg.matrix4_scale(command.scale)
|
scale := linalg.matrix4_scale(command.scale)
|
||||||
rotation := linalg.matrix4_rotate(0, [3]f32 { 0, 0, 1 })
|
rotation := linalg.matrix4_rotate(linalg.to_radians(f32(0.0)), [3]f32 { 1, 0, 0 })
|
||||||
translation := linalg.matrix4_translate(command.position)
|
translation := linalg.matrix4_translate(command.position)
|
||||||
|
|
||||||
// Scale.
|
// Scale.
|
||||||
transform *= scale
|
model_matrix *= scale
|
||||||
|
|
||||||
// Rotate.
|
// Rotate.
|
||||||
transform *= rotation
|
model_matrix *= rotation
|
||||||
|
|
||||||
// Translate.
|
// Translate.
|
||||||
transform *= translation
|
model_matrix *= translation
|
||||||
|
|
||||||
activate_material(&command.material, transform)
|
activate_material(&command.material, model_matrix, view_matrix, projection_matrix)
|
||||||
|
|
||||||
draw_mesh(&command.mesh)
|
draw_mesh(&command.mesh)
|
||||||
}
|
}
|
||||||
@@ -112,6 +132,8 @@ render_frame :: proc(renderer: ^Renderer) {
|
|||||||
when RENDER_BACKEND_OPENGL {
|
when RENDER_BACKEND_OPENGL {
|
||||||
opengl_swap_buffers(renderer)
|
opengl_swap_buffers(renderer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clear_screen(renderer, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy :: proc(renderer: ^Renderer) {
|
destroy :: proc(renderer: ^Renderer) {
|
||||||
@@ -123,11 +145,11 @@ destroy :: proc(renderer: ^Renderer) {
|
|||||||
free(renderer)
|
free(renderer)
|
||||||
}
|
}
|
||||||
|
|
||||||
@(private) activate_material :: proc(material: ^Material, transform: linalg.Matrix4x4f32) {
|
@(private) activate_material :: proc(material: ^Material, model_matrix, view_matrix, projection_matrix: linalg.Matrix4x4f32) {
|
||||||
assert(material != nil)
|
assert(material != nil)
|
||||||
|
|
||||||
when RENDER_BACKEND_OPENGL {
|
when RENDER_BACKEND_OPENGL {
|
||||||
opengl_activate_material(material, transform)
|
opengl_activate_material(material, model_matrix, view_matrix, projection_matrix)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,4 +159,10 @@ destroy :: proc(renderer: ^Renderer) {
|
|||||||
when RENDER_BACKEND_OPENGL {
|
when RENDER_BACKEND_OPENGL {
|
||||||
opengl_draw_mesh(mesh)
|
opengl_draw_mesh(mesh)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private) enable_depth_testing :: proc(renderer: ^Renderer, enable: bool) {
|
||||||
|
when RENDER_BACKEND_OPENGL {
|
||||||
|
opengl_enable_depth_testing(renderer, true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -84,8 +84,6 @@ when RENDER_BACKEND_OPENGL {
|
|||||||
|
|
||||||
|
|
||||||
opengl_viewport_changed :: proc(renderer: ^Renderer) {
|
opengl_viewport_changed :: proc(renderer: ^Renderer) {
|
||||||
opengl_clear_screen(renderer)
|
|
||||||
|
|
||||||
gl.Viewport(
|
gl.Viewport(
|
||||||
i32(renderer.viewport.x),
|
i32(renderer.viewport.x),
|
||||||
i32(renderer.viewport.y),
|
i32(renderer.viewport.y),
|
||||||
@@ -103,13 +101,18 @@ when RENDER_BACKEND_OPENGL {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
opengl_clear_screen :: proc(renderer: ^Renderer) {
|
opengl_clear_screen :: proc(renderer: ^Renderer, clear_depth: bool) {
|
||||||
gl.Clear(gl.COLOR_BUFFER_BIT)
|
mask := u32(gl.COLOR_BUFFER_BIT)
|
||||||
|
if clear_depth {
|
||||||
|
gl.ClearDepth(1.0)
|
||||||
|
mask |= gl.DEPTH_BUFFER_BIT
|
||||||
|
}
|
||||||
|
|
||||||
|
gl.Clear(mask)
|
||||||
}
|
}
|
||||||
|
|
||||||
opengl_swap_buffers :: proc(renderer: ^Renderer) {
|
opengl_swap_buffers :: proc(renderer: ^Renderer) {
|
||||||
win.SwapBuffers(backend.hdc)
|
win.SwapBuffers(backend.hdc)
|
||||||
opengl_clear_screen(renderer)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
opengl_destroy :: proc(renderer: ^Renderer) {
|
opengl_destroy :: proc(renderer: ^Renderer) {
|
||||||
@@ -239,15 +242,23 @@ when RENDER_BACKEND_OPENGL {
|
|||||||
gl.DeleteProgram(shader_program.backend.handle)
|
gl.DeleteProgram(shader_program.backend.handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
opengl_activate_material :: proc(material: ^Material, transform: linalg.Matrix4x4f32) {
|
opengl_activate_material :: proc(material: ^Material, model_matrix, view_matrix, projection_matrix: linalg.Matrix4x4f32) {
|
||||||
gl.UseProgram(material.shader_program.backend.handle)
|
gl.UseProgram(material.shader_program.backend.handle)
|
||||||
|
|
||||||
gl.ActiveTexture(gl.TEXTURE0)
|
gl.ActiveTexture(gl.TEXTURE0)
|
||||||
gl.BindTexture(gl.TEXTURE_2D, material.texture.backend.handle)
|
gl.BindTexture(gl.TEXTURE_2D, material.texture.backend.handle)
|
||||||
|
|
||||||
transform_loc := gl.GetUniformLocation(material.shader_program.backend.handle, "in_transform")
|
model_matrix_loc := gl.GetUniformLocation(material.shader_program.backend.handle, "in_model_matrix")
|
||||||
transform_as_f32_array := transmute([16]f32)(transform)
|
model_matrix_as_f32_array := transmute([16]f32)(model_matrix)
|
||||||
gl.UniformMatrix4fv(transform_loc, 1, false, &transform_as_f32_array[0])
|
gl.UniformMatrix4fv(model_matrix_loc, 1, false, &model_matrix_as_f32_array[0])
|
||||||
|
|
||||||
|
view_matrix_loc := gl.GetUniformLocation(material.shader_program.backend.handle, "in_view_matrix")
|
||||||
|
view_matrix_as_f32_array := transmute([16]f32)(view_matrix)
|
||||||
|
gl.UniformMatrix4fv(view_matrix_loc, 1, false, &view_matrix_as_f32_array[0])
|
||||||
|
|
||||||
|
projection_matrix_loc := gl.GetUniformLocation(material.shader_program.backend.handle, "in_projection_matrix")
|
||||||
|
projection_matrix_as_f32_array := transmute([16]f32)(projection_matrix)
|
||||||
|
gl.UniformMatrix4fv(projection_matrix_loc, 1, false, &projection_matrix_as_f32_array[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
opengl_draw_mesh :: proc(mesh: ^Mesh) {
|
opengl_draw_mesh :: proc(mesh: ^Mesh) {
|
||||||
@@ -322,6 +333,15 @@ when RENDER_BACKEND_OPENGL {
|
|||||||
}
|
}
|
||||||
|
|
||||||
opengl_delete_texture :: proc(renderer: ^Renderer, t: ^Texture) {
|
opengl_delete_texture :: proc(renderer: ^Renderer, t: ^Texture) {
|
||||||
|
gl.DeleteTextures(1, &t.backend.handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
opengl_enable_depth_testing :: proc(renderer: ^Renderer, enable: bool) {
|
||||||
|
if enable {
|
||||||
|
gl.Enable(gl.DEPTH_TEST)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
gl.Disable(gl.DEPTH_TEST)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user