package renderer import "core:fmt" 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, 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. } Post_Processing_Node :: struct { input: []^Texture, output: ^Render_Target, program: ^Shader_Program, } Draw_Command :: struct { renderer: ^Renderer, // Needed for sorting. mesh: Mesh, material: Material, position: [3]f32, rotation: [3]f32, scale: [3]f32, } 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. } create_scene_pass :: proc( name: string, blend_mode: Blend_Mode, sort_mode: Sort_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, 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 }