353 lines
9.2 KiB
Odin
353 lines
9.2 KiB
Odin
package renderer
|
|
|
|
import "core:math/linalg"
|
|
import "core:fmt"
|
|
import "core:log"
|
|
|
|
MAX_DRAW_COMMANDS_CAPACITY :: 4096
|
|
MAX_POST_PROCESS_NODES_PER_PASS :: 8
|
|
|
|
Pass :: struct {
|
|
name: string,
|
|
type: Pass_Type,
|
|
}
|
|
|
|
Pass_Type :: union {
|
|
Scene_Pass,
|
|
Post_Processing_Pass,
|
|
}
|
|
|
|
Scene_Pass :: struct {
|
|
blend_mode: Blend_Mode,
|
|
sort_mode: Sort_Mode,
|
|
cull_mode: Cull_Mode,
|
|
|
|
draw_commands: [dynamic]Draw_Command, // Capacity is 'MAX_DRAW_COMMANDS_CAPACITY'.
|
|
|
|
output_rt: ^Render_Target, // Commands draw to this render-target.
|
|
}
|
|
|
|
Post_Processing_Pass :: struct {
|
|
post_processing_nodes: [dynamic]Post_Processing_Node, // These nodes are executed after all commands have been drawn onto the render-target.
|
|
}
|
|
|
|
Uniform :: union {
|
|
Uniform_Texture,
|
|
|
|
Uniform_Float,
|
|
Uniform_Float_Pointer,
|
|
|
|
Uniform_Matrix4f32,
|
|
Uniform_Matrix4f32_Pointer,
|
|
|
|
Uniform_Vector3,
|
|
Uniform_Vector3_Pointer,
|
|
|
|
Uniform_Vector4,
|
|
Uniform_Vector4_Pointer,
|
|
|
|
Uniform_Color,
|
|
Uniform_Color_Pointer,
|
|
}
|
|
|
|
Uniform_Texture :: struct {
|
|
index: u8,
|
|
value: ^Texture,
|
|
}
|
|
|
|
Uniform_Float :: struct {
|
|
name: string,
|
|
value: f32,
|
|
}
|
|
Uniform_Float_Pointer :: struct {
|
|
name: string,
|
|
value: ^f32,
|
|
}
|
|
|
|
Uniform_Matrix4f32 :: struct {
|
|
name: string,
|
|
value: linalg.Matrix4f32,
|
|
}
|
|
Uniform_Matrix4f32_Pointer :: struct {
|
|
name: string,
|
|
value: ^linalg.Matrix4f32,
|
|
}
|
|
|
|
Uniform_Vector3 :: struct {
|
|
name: string,
|
|
value: [3]f32,
|
|
}
|
|
Uniform_Vector3_Pointer :: struct {
|
|
name: string,
|
|
value: ^[3]f32,
|
|
}
|
|
|
|
Uniform_Vector4 :: struct {
|
|
name: string,
|
|
value: [4]f32,
|
|
}
|
|
Uniform_Vector4_Pointer :: struct {
|
|
name: string,
|
|
value: ^[4]f32,
|
|
}
|
|
|
|
Uniform_Color :: struct {
|
|
name: string,
|
|
value: Color,
|
|
}
|
|
Uniform_Color_Pointer :: struct {
|
|
name: string,
|
|
value: ^Color,
|
|
}
|
|
|
|
Post_Processing_Node :: struct {
|
|
uniforms: []Uniform,
|
|
output: ^Render_Target,
|
|
|
|
program: ^Shader_Program,
|
|
}
|
|
|
|
// TODO: SS - Create a pool of 'Draw_Command's and reuse them.
|
|
|
|
MAX_UNIFORMS_PER_DRAW_COMMAND :: 8
|
|
|
|
Draw_Command :: struct {
|
|
renderer: ^Renderer, // Needed for sorting.
|
|
|
|
mesh: Mesh,
|
|
material: Material,
|
|
uniforms: [MAX_UNIFORMS_PER_DRAW_COMMAND]Uniform,
|
|
uniform_count: u8,
|
|
|
|
position: [3]f32,
|
|
rotation: [3]f32,
|
|
scale: [3]f32,
|
|
}
|
|
|
|
create_draw_command :: proc(renderer: ^Renderer, mesh: Mesh, material: Material, uniforms: []Uniform, position, rotation, scale: [3]f32, loc := #caller_location) -> Draw_Command {
|
|
dc: Draw_Command
|
|
|
|
dc.renderer = renderer
|
|
dc.mesh = mesh
|
|
dc.material = material
|
|
dc.position = position
|
|
dc.rotation = rotation
|
|
dc.scale = scale
|
|
|
|
for u in uniforms {
|
|
if dc.uniform_count >= MAX_UNIFORMS_PER_DRAW_COMMAND {
|
|
log.warnf("Hit max capacity (%v) of uniforms per draw command! %v", MAX_UNIFORMS_PER_DRAW_COMMAND, loc)
|
|
break
|
|
}
|
|
|
|
dc.uniforms[dc.uniform_count] = u
|
|
dc.uniform_count += 1
|
|
}
|
|
|
|
return dc
|
|
}
|
|
|
|
Blend_Mode :: enum {
|
|
None,
|
|
Alpha,
|
|
Additive,
|
|
Multiply,
|
|
}
|
|
|
|
Blend_Factor :: enum {
|
|
Zero,
|
|
One,
|
|
Src_Color,
|
|
One_Minus_Src_Color,
|
|
Dst_Color,
|
|
One_Minus_Dst_Color,
|
|
Src_Alpha,
|
|
One_Minus_Src_Alpha,
|
|
Dst_Alpha,
|
|
One_Minus_Dst_Alpha,
|
|
// ..
|
|
}
|
|
|
|
Blend_Factors :: struct {
|
|
source, destination: Blend_Factor
|
|
}
|
|
|
|
@(private) BLEND_FACTOR_TABLE :: [Blend_Mode]Blend_Factors {
|
|
.None = { .One, .Zero },
|
|
.Alpha = { .Src_Alpha, .One_Minus_Src_Alpha },
|
|
.Additive = { .Src_Alpha, .One },
|
|
.Multiply = { .Dst_Color, .Zero },
|
|
}
|
|
|
|
Sort_Mode :: enum {
|
|
None, // Draws the commands in the order they're placed in the 'draw_commmands' array.
|
|
Back_To_Front, // Sorts the commands in the 'draw_commmands' array (from back to front) before drawing them.
|
|
Front_To_Back, // Sorts the commands in the 'draw_commmands' array (from front to back) before drawing them.
|
|
}
|
|
|
|
Cull_Mode :: enum {
|
|
None,
|
|
Back,
|
|
Front,
|
|
Front_Back,
|
|
}
|
|
|
|
create_scene_pass :: proc(
|
|
name: string,
|
|
blend_mode: Blend_Mode,
|
|
sort_mode: Sort_Mode,
|
|
cull_mode: Cull_Mode,
|
|
output_rt: ^Render_Target,
|
|
) -> Pass
|
|
{
|
|
assert(len(name) > 0)
|
|
|
|
p: Pass
|
|
p.name = name
|
|
p.type = Scene_Pass {
|
|
blend_mode = blend_mode,
|
|
sort_mode = sort_mode,
|
|
cull_mode = cull_mode,
|
|
|
|
draw_commands = make([dynamic]Draw_Command, 0, MAX_DRAW_COMMANDS_CAPACITY),
|
|
|
|
output_rt = output_rt,
|
|
}
|
|
|
|
return p
|
|
}
|
|
|
|
create_post_processing_pass :: proc(name: string, post_processing_nodes: []Post_Processing_Node) -> Pass {
|
|
assert(len(name) > 0)
|
|
|
|
p: Pass
|
|
p.name = name
|
|
|
|
ppp := Post_Processing_Pass {
|
|
post_processing_nodes = make([dynamic]Post_Processing_Node, 0, MAX_POST_PROCESS_NODES_PER_PASS)
|
|
}
|
|
|
|
append(&ppp.post_processing_nodes, ..post_processing_nodes)
|
|
|
|
p.type = ppp
|
|
return p
|
|
}
|
|
|
|
delete_pass :: proc(pass: ^Pass) {
|
|
assert(pass != nil)
|
|
|
|
switch &t in &pass.type {
|
|
case Scene_Pass: delete_scene_pass(&t)
|
|
case Post_Processing_Pass: delete_post_processing_pass(&t)
|
|
}
|
|
}
|
|
|
|
@(private) delete_scene_pass :: proc(pass: ^Scene_Pass) {
|
|
delete(pass.draw_commands)
|
|
}
|
|
|
|
@(private) delete_post_processing_pass :: proc(pass: ^Post_Processing_Pass) {
|
|
delete(pass.post_processing_nodes)
|
|
}
|
|
|
|
add_command_to_pass :: proc(renderer: ^Renderer, pass: ^Pass, command: Draw_Command) -> bool {
|
|
assert(renderer != nil)
|
|
assert(pass != nil)
|
|
|
|
switch &t in &pass.type {
|
|
case Post_Processing_Pass: {
|
|
fmt.printfln("Can't add commands to a post-processing pass.")
|
|
return false
|
|
}
|
|
case Scene_Pass: {
|
|
if t.output_rt == nil {
|
|
fmt.printfln("Pass '%v' does not have a output render-target so you're not allowed to add commands to it.", pass.name)
|
|
return false
|
|
}
|
|
|
|
cmd := command
|
|
cmd.renderer = renderer
|
|
n, err := append(&t.draw_commands, cmd)
|
|
assert(err == .None)
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
execute_pass :: proc(renderer: ^Renderer, pass: ^Pass, view_matrix, projection_matrix: linalg.Matrix4x4f32) { // TODO: SS - Move to 'pass.odin'.
|
|
// fmt.printfln("Executing pass '%v'.", pass.name)
|
|
|
|
assert(renderer != nil)
|
|
assert(pass != nil)
|
|
|
|
switch &t in &pass.type {
|
|
case Scene_Pass: {
|
|
apply_polygon_mode(renderer, renderer.polygon_mode)
|
|
|
|
assert(t.output_rt != nil)
|
|
bind_render_target(renderer, t.output_rt)
|
|
defer bind_render_target(renderer, nil)
|
|
|
|
should_write_depth := t.output_rt.depth_texture != nil
|
|
should_test_depth := should_write_depth
|
|
should_clear_depth := should_write_depth
|
|
|
|
should_clear_color := true
|
|
set_clear_color(renderer, RGBA_Color { 0, 0, 0, 0 })
|
|
|
|
clear_screen(renderer, should_clear_color, should_clear_depth)
|
|
apply_depth(renderer, should_test_depth, should_write_depth)
|
|
|
|
apply_blend_mode(renderer, t.blend_mode)
|
|
defer apply_blend_mode(renderer, .None)
|
|
|
|
apply_cull_mode(renderer, t.cull_mode)
|
|
defer apply_cull_mode(renderer, .None)
|
|
|
|
sort_draw_commands(renderer, &t)
|
|
|
|
for &dc in &t.draw_commands { // TODO: SS - Don't think we need the address of the draw-commands.
|
|
model_matrix := linalg.identity(linalg.Matrix4x4f32)
|
|
|
|
// Translate.
|
|
translation := linalg.matrix4_translate(dc.position)
|
|
|
|
// Rotate.
|
|
rot_x := linalg.matrix4_rotate(linalg.to_radians(dc.rotation.x), [3]f32 { 1, 0, 0 })
|
|
rot_y := linalg.matrix4_rotate(linalg.to_radians(dc.rotation.y), [3]f32 { 0, 1, 0 })
|
|
rot_z := linalg.matrix4_rotate(linalg.to_radians(dc.rotation.z), [3]f32 { 0, 0, 1 })
|
|
rotation := rot_z * rot_y * rot_x
|
|
|
|
// Scale.
|
|
scale := linalg.matrix4_scale(dc.scale)
|
|
|
|
model_matrix *= translation * rotation * scale
|
|
|
|
// Apply uniforms.
|
|
activate_material(&dc.material, model_matrix, view_matrix, projection_matrix)
|
|
if dc.uniform_count > 0 {
|
|
set_shader_uniforms(dc.material.shader_program, dc.uniforms[:dc.uniform_count])
|
|
}
|
|
|
|
// Draw the mesh.
|
|
draw_mesh(&dc.mesh)
|
|
}
|
|
|
|
// Clear the pass' draw-commands.
|
|
clear(&t.draw_commands)
|
|
|
|
// TODO: SS - "Deactivate" the pass?
|
|
|
|
apply_polygon_mode(renderer, .Fill)
|
|
}
|
|
case Post_Processing_Pass: {
|
|
// Execute the post-processing nodes.
|
|
for &pp in &t.post_processing_nodes {
|
|
execute_post_processing_node(renderer, &pp, view_matrix, projection_matrix)
|
|
}
|
|
|
|
// TODO: SS - "Deactivate" the pass?
|
|
}
|
|
}
|
|
} |