466 lines
16 KiB
Plaintext
466 lines
16 KiB
Plaintext
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 := *engine.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 = engine.renderer.engine_buffers.camera; // @Incomplete
|
|
}
|
|
case .MATERIAL; {
|
|
param.buffer_info.buffer = material_buffer;
|
|
}
|
|
case .DIRECTIONAL_LIGHT; {
|
|
//param.buffer_info.buffer = engine.renderer.engine_buffers.directional_light; // @Incomplete
|
|
}
|
|
case .POINT_LIGHTS; {
|
|
//param.buffer_info.buffer = engine.renderer.engine_buffers.point_lights; // @Incomplete
|
|
}
|
|
case .BONE_MATRICES; {
|
|
param.buffer_info.buffer = bone_buffer;
|
|
}
|
|
case .CUSTOM; {
|
|
if engine.renderer.callbacks.get_custom_material_parameter_mapping != null {
|
|
success, mapping_info := engine.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 = engine.renderer.default_samplers.repeat;
|
|
}
|
|
case .CLAMP_SAMPLER; {
|
|
param.sampler = engine.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 engine.renderer.callbacks.get_custom_material_parameter_mapping != null {
|
|
success, mapping_info := engine.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(*engine.renderer.render_graph.render_passes, engine.input.pass_handle);
|
|
|
|
if engine.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[engine.input.rt_index]);
|
|
}
|
|
} else {
|
|
if param.texture > 0 {
|
|
push_cmd_set_texture(renderer, param.slot, param.texture);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|