Files
coven/renderer/material.jai
2024-10-11 22:21:32 +02:00

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 := *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);
}
}
}
}
}
}