MAX_BUFFER_MAPPINGS :: 4; MAX_PARAMETERS :: 16; MAX_PROPERTY_REFERENCES :: 4; Pass_Reference :: struct { pass_index: s64; parameter_index: s64; offset: u32; // Only for buffers } Material_Property :: struct { name: string; type: Shader_Property_Type; union { float_val : float; float2_val : Vector2; float3_val : Vector3; float4_val : Vector4; mat3_val : Matrix3; mat4_val : Matrix4; int_val : int; bool_val : bool; texture_val : Texture_Handle; sampler_val : Sampler_Handle; } references : [MAX_PROPERTY_REFERENCES] Pass_Reference; num_references: s64; } Material_Pass_Parameter :: struct { slot: u32; type: Shader_Parameter_Type; shader: Shader_Type; mapping: Shader_Parameter_Mapping; mapping_str: string; union { buffer_info : struct { buffer: Buffer_Handle; size: u32; data: *void; dirty: bool; } sampler: Sampler_Handle; texture: Texture_Handle; } } Material_Pass :: struct { pass: Render_Pass_Handle; pipeline: Pipeline_State_Handle; parameters: [MAX_PARAMETERS] Material_Pass_Parameter; num_parameters: s64; } Material :: struct { name: string; properties: [..] Material_Property; passes: [..] Material_Pass; allocator: Allocator; pool: Flat_Pool; // For removing the material later _locator: Bucket_Locator; } Material_Pass_Info :: struct { pass: Render_Pass_Handle; pipeline: Pipeline_State_Handle; } materials: Bucket_Array(Material, 32); delete_material :: (mat: *Material) { for pass: mat.passes { for pi: 0..pass.num_parameters-1 { param := pass.parameters[pi]; if param.type != .BUFFER continue; if param.buffer_info.data != null && param.mapping == .NONE && param.buffer_info.buffer != 0 { // I guess if there is a mapping, the data ptr is null as well, but checking for both, if this changes at a later point destroy_buffer(renderer, param.buffer_info.buffer); } param.buffer_info.buffer = 0; } } fini(*mat.pool); bucket_array_remove(*materials, mat._locator); } create_material :: (name: string) -> *Material { p, locator := find_and_occupy_empty_slot(*materials); p.pool = .{}; p.allocator.proc = flat_pool_allocator_proc; p.allocator.data = *p.pool; p.name = copy_string(name,, p.allocator); p.passes.allocator = p.allocator; p.properties.allocator = p.allocator; p._locator = locator; return p; } // Create the material, but reuse the material's arrays? //copy_material :: (material: *Material) -> *Material { // new_material := create_material(material.name); // // new_context := context; // new_context.allocator = new_material.allocator; // // push_context new_context { // array_reserve(*new_material.passes, material.passes.count); // array_reserve(*new_material.properties, material.properties.count); // // for pass: material.passes { // new_pass := pass; // for pi: 0..new_pass.num_parameters-1 { // new_pass.parameters[pi].mapping_str = copy_string(pass.parameters[pi].mapping_str); // } // array_add(*new_material.passes, new_pass); // } // // for prop: material.properties { // new_prop := prop; // new_prop.name = copy_string(prop.name); // array_add(*new_material.properties, new_prop); // } // } // // return new_material; //} create_material_for_passes :: (name: string, material_pass_infos: [] Material_Pass_Info) -> *Material { add_properties_from_shader :: (mat: *Material, pass_index: s64, shader: Shader) { pass := *mat.passes[pass_index]; for *param: shader.info.parameters { pass_param : Material_Pass_Parameter; pass_param.type = param.type; pass_param.slot = param.slot; pass_param.mapping = param.mapping; pass_param.mapping_str = copy_string(param.mapping_str); // @Incomplete: MEMORY pass_param.shader = param.shader; pass_param.buffer_info.size = param.size; pass_param.buffer_info.data = alloc(param.size); memset(pass_param.buffer_info.data, 0, param.size); if pass_param.type == { case .BUFFER; { if param.mapping == .NONE { // Only allocate if the parameter doesn't have a mapping // allocate the buffer param.mapping = .NONE; pass_param.buffer_info.buffer = create_constant_buffer(renderer, pass_param.buffer_info.data, pass_param.buffer_info.size, mappable=true); } } case .TEXTURE; { // @Incomplete: Set default texture } case .SAMPLER; { // @Incomplete: Set default texture } } pass.parameters[pass.num_parameters] = pass_param; pass.num_parameters += 1; if param.mapping == .NONE { for pi: 0..param.num_properties-1 { param_prop := param.properties[pi]; prop : *Material_Property; // Check if property exists for *existing: mat.properties { if existing.name == param_prop.name && existing.type == param_prop.type { // We found one! prop = existing; break; } } if prop == null { array_add(*mat.properties, .{}); prop = *mat.properties[mat.properties.count-1]; prop.name = param_prop.name; prop.type = param_prop.type; } // Add the data about the pass buffer, texture, etc here reference : Pass_Reference; reference.pass_index = pass_index; reference.parameter_index = pass.num_parameters - 1; reference.offset = xx param_prop.buffer_offset; prop.references[prop.num_references] = reference; prop.num_references += 1; } } } } mat := create_material(name); new_context := context; new_context.allocator = mat.allocator; push_context new_context { for info: material_pass_infos { pipeline_state := *renderer.pipeline_states[info.pipeline - 1]; vs := get_shader(pipeline_state.vs); ps := get_shader(pipeline_state.ps); pass : Material_Pass; pass.pipeline = info.pipeline; pass.pass = info.pass; array_add(*mat.passes, pass); add_properties_from_shader(mat, mat.passes.count-1, vs); add_properties_from_shader(mat, mat.passes.count-1, ps); } } return mat; } set_material_property :: (material: *Material, name: string, val: float) { found := false; for *prop: material.properties { if prop.name == name && prop.type == .FLOAT { prop.float_val = val; for 0..prop.num_references-1 { ref := prop.references[it]; pass := *material.passes[ref.pass_index]; param := *pass.parameters[ref.parameter_index]; memcpy(*param.buffer_info.data[ref.offset], *val, size_of(float)); param.buffer_info.dirty = true; } found = true; break; } } if !found { log_error("MATERIAL: Tried setting float property '%' on material '%' but the property wasn't found\n", name, material.name); } } set_material_property :: (material: *Material, name: string, val: Vector2) { found := false; for *prop: material.properties { if prop.name == name && prop.type == .FLOAT2 { prop.float2_val = val; for 0..prop.num_references-1 { ref := prop.references[it]; pass := *material.passes[ref.pass_index]; param := *pass.parameters[ref.parameter_index]; memcpy(*param.buffer_info.data[ref.offset], *val, size_of(Vector2)); param.buffer_info.dirty = true; } found = true; break; } } if !found { log_error("MATERIAL: Tried setting Vector2 property '%' on material '%' but the property wasn't found\n", name, material.name); } } set_material_property :: (material: *Material, name: string, val: Vector3) { found := false; for *prop: material.properties { if prop.name == name && prop.type == .FLOAT3 { prop.float3_val = val; for 0..prop.num_references-1 { ref := prop.references[it]; pass := *material.passes[ref.pass_index]; param := *pass.parameters[ref.parameter_index]; memcpy(*param.buffer_info.data[ref.offset], *val, size_of(Vector3)); param.buffer_info.dirty = true; } found = true; break; } } if !found { log_error("MATERIAL: Tried setting Vector3 property '%' on material '%' but the property wasn't found\n", name, material.name); } } set_material_property :: (material: *Material, name: string, val: Vector4) { found := false; for *prop: material.properties { if prop.name == name && prop.type == .FLOAT4 { prop.float4_val = val; for 0..prop.num_references-1 { ref := prop.references[it]; pass := *material.passes[ref.pass_index]; param := *pass.parameters[ref.parameter_index]; memcpy(*param.buffer_info.data[ref.offset], *val, size_of(Vector4)); param.buffer_info.dirty = true; } found = true; break; } } if !found { log_error("MATERIAL: Tried setting Vector4 property '%' on material '%' but the property wasn't found\n", name, material.name); } } set_material_property :: (material: *Material, name: string, val: Texture_Handle) { found := false; for *prop: material.properties { if prop.name == name && prop.type == .TEXTURE { prop.texture_val = val; for 0..prop.num_references-1 { ref := prop.references[it]; pass := *material.passes[ref.pass_index]; param := *pass.parameters[ref.parameter_index]; param.texture = val; } found = true; break; } } if !found { log_error("MATERIAL: Tried setting texture property '%' on material '%' but the property wasn't found\n", name, material.name); } } set_material_pass_parameters :: (material: *Material, pass_index: s64, render_pass: Render_Pass, transform_buffer: Buffer_Handle, material_buffer: Buffer_Handle, bone_buffer: Buffer_Handle, defaults: Model_Material) { mat_pass := material.passes[pass_index]; for 0..mat_pass.num_parameters-1 { param := mat_pass.parameters[it]; if param.buffer_info.dirty { param.buffer_info.dirty = false; upload_data_to_buffer(renderer, param.buffer_info.buffer, param.buffer_info.data, param.buffer_info.size); } if param.type == { case .BUFFER; { if param.mapping == { case .TIME; { //param.buffer_info.buffer = engine.time_buffer; // @Incomplete } case .MODEL_MATRIX; { param.buffer_info.buffer = transform_buffer; } case .CAMERA_DATA; { //param.buffer_info.buffer = renderer.engine_buffers.camera; // @Incomplete } case .MATERIAL; { param.buffer_info.buffer = material_buffer; } case .DIRECTIONAL_LIGHT; { //param.buffer_info.buffer = renderer.engine_buffers.directional_light; // @Incomplete } case .POINT_LIGHTS; { //param.buffer_info.buffer = renderer.engine_buffers.point_lights; // @Incomplete } case .BONE_MATRICES; { param.buffer_info.buffer = bone_buffer; } case .CUSTOM; { if renderer.callbacks.get_custom_material_parameter_mapping != null { success, mapping_info := renderer.callbacks.get_custom_material_parameter_mapping(param.mapping_str); if success { param.buffer_info.buffer = mapping_info.buffer; } } } } if param.buffer_info.buffer > 0 { push_cmd_set_constant_buffer(renderer, param.slot, param.buffer_info.buffer, param.shader); } } case .SAMPLER; if param.mapping == { case .REPEAT_SAMPLER; { param.sampler = renderer.default_samplers.repeat; } case .CLAMP_SAMPLER; { param.sampler = renderer.default_samplers.clamp; } } if param.sampler != 0 { push_cmd_set_sampler(renderer, param.slot, param.sampler); } case .TEXTURE; { is_texture_input := false; input_index := 0; if param.mapping == { case .BASE_COLOR_TEXTURE; { param.texture = defaults.textures.base_color; } case .NORMAL_MAP; { param.texture = defaults.textures.normal; } case .SHADER_ATTACHMENT0; { is_texture_input = true; input_index = 0; } case .SHADER_ATTACHMENT1; { is_texture_input = true; input_index = 1; } case .SHADER_ATTACHMENT2; { is_texture_input = true; input_index = 2; } case .SHADER_ATTACHMENT3; { is_texture_input = true; input_index = 3; } case .CUSTOM; { if renderer.callbacks.get_custom_material_parameter_mapping != null { success, mapping_info := renderer.callbacks.get_custom_material_parameter_mapping(param.mapping_str); if success { param.texture = mapping_info.texture; } } } } if is_texture_input { input := render_pass.inputs[input_index]; owning_pass := parray_get(*renderer.render_graph.render_passes, input.pass_handle); if input.rt_index == DEPTH_STENCIL_SLOT { push_cmd_set_texture(renderer, param.slot, owning_pass.depth_stencil); } else { push_cmd_set_texture(renderer, param.slot, owning_pass.render_targets[input.rt_index]); } } else { if param.texture > 0 { push_cmd_set_texture(renderer, param.slot, param.texture); } } } } } }