Initial commit

This commit is contained in:
2024-10-11 22:21:32 +02:00
commit 1bdd01e9b2
38 changed files with 11363 additions and 0 deletions

View File

@@ -0,0 +1,14 @@
Directional_Light :: struct {
color_and_intensity : Vector4;
direction : Vector4;
view_position : Vector3;
}
Directional_Light_Buffer_Data :: struct {
color_and_intensity : Vector4;
direction : Vector4;
//light_matrix: Matrix4;
}

1503
renderer/dx11_renderer.jai Normal file

File diff suppressed because it is too large Load Diff

105
renderer/font.jai Normal file
View File

@@ -0,0 +1,105 @@
Font_Handle :: #type, distinct u32;
Glyph :: struct {
ax: float; // advance.x
ay: float; // advance.y
bw: float; // bitmap.width;
bh: float; // bitmap.rows;
bl: float; // bitmap_left;
bt: float; // bitmap_top;
tx: float; // x offset of glyph in texture coordinates
}
Point :: struct {
x: float;
y: float;
s: float;
t: float;
color: Vector4;
};
Font :: struct {
glyphs: [..] Glyph;
face: FT_Face;
pixel_size: u32;
atlas_width: u32;
atlas_height: u32;
texture: Texture_Handle;
}
ft : FT_Library;
init_freetype :: () {
if FT_Init_FreeType(*ft) {
log("Couldn't init Freetype\n");
}
}
create_font :: (renderer: *Renderer, path: string, pixel_size: u32) -> Font_Handle {
font: Font;
font.pixel_size = pixel_size;
array_resize(*font.glyphs, 128);
if FT_New_Face(ft, path.data, 0, *font.face) {
log("Couldn't create new face\n");
assert(false);
} else {
FT_Set_Pixel_Sizes(font.face, 0, pixel_size);
g := font.face.glyph;
w : u32 = 0;
h : u32 = 0;
index := 0;
for 32..128-1 {
if FT_Load_Char(font.face, xx it, FT_LOAD_RENDER) {
continue;
}
w += g.bitmap.width + 1;
h = max(h, g.bitmap.rows);
}
atlas_width := w;
bytes : [..] u8;
bytes.allocator = temp;
array_resize(*bytes, w * h);
texture := create_texture(renderer, bytes.data, w, h, 1, generate_mips=false, format=.R8_UNORM);
font.atlas_width = w;
font.atlas_height = h;
font.texture = texture;
x : u32 = 0;
pixel_width := 1.0 / cast(float)atlas_width;
for 32..128-1 {
if FT_Load_Char(font.face, xx it, FT_LOAD_RENDER) continue;
// @Speed: We should put this buffer data into an array and upload everything once after this loop
update_texture_region(renderer, texture, x, 0, g.bitmap.width, g.bitmap.rows, 1, g.bitmap.buffer);
font.glyphs[it].ax = cast(float)(g.advance.x >> 6);
font.glyphs[it].ay = cast(float)(g.advance.y >> 6);
font.glyphs[it].bw = cast(float)g.bitmap.width;
font.glyphs[it].bh = cast(float)g.bitmap.rows;
font.glyphs[it].bl = cast(float)g.bitmap_left;
font.glyphs[it].bt = cast(float)g.bitmap_top;
font.glyphs[it].tx = cast(float)x / cast(float)w;
x += g.bitmap.width + 1;
}
array_add(*renderer.fonts, font);
return xx renderer.fonts.count;
}
return 0;
}
#import "freetype-2.12.1";

465
renderer/material.jai Normal file
View File

@@ -0,0 +1,465 @@
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);
}
}
}
}
}
}

191
renderer/mesh.jai Normal file
View File

@@ -0,0 +1,191 @@
Skin_Vertex :: struct {
bone_index: [4] float;
bone_weight: [4] float;
}
Mesh_Vertex_Data_Type :: enum {
NONE;
POSITION;
NORMAL;
TEXCOORD;
TANGENT;
COLOR;
BITANGENT;
BONE_INDICES;
BONE_WEIGHTS;
}
Mesh_Handle :: #type, distinct u32;
Mesh :: struct {
name : string;
positions : [..] Vector3;
normals : [..] Vector3;
texcoords : [..] Vector2;
tangents : [..] Vector3;
bitangents : [..] Vector3;
colors : [..] Vector4;
skin_data : [..] Skin_Vertex;
bone_indices: [..] s32;
bone_matrices: [..] Matrix4;
num_bones: s32;
indices : [..] u32;
ib : Buffer_Handle;
vbs : Table(u32, Buffer_Handle);
//vb : Buffer_Handle;
}
get_mesh_vb :: (mesh: *Mesh, pipeline_handle: Pipeline_State_Handle = 0) -> Buffer_Handle {
handle := ifx pipeline_handle == 0 then renderer.current_state.last_set_pipeline else pipeline_handle;
pipeline_state := *renderer.pipeline_states[handle-1];
return get_mesh_vb(mesh, pipeline_state.mesh_data_types);
}
get_mesh_vb :: (mesh: *Mesh, input: [] Mesh_Vertex_Data_Type) -> Buffer_Handle {
hash : u32 = 0;
nums : [8] u32 : .[13, 61, 84, 86, 65, 10000, 100000, 126];
for input, i: input {
hash += nums[i] * cast(u32)input;
}
if !table_contains(*mesh.vbs, hash) {
final_vertices : [..] float;
final_vertices.allocator = temp;
stride : u32 = 0;
for input: input {
if input == {
case .POSITION; stride += 3;
case .NORMAL; stride += 3;
case .TEXCOORD; stride += 2;
case .COLOR; stride += 4;
case .TANGENT; stride += 3;
case .BITANGENT; stride += 3;
case .BONE_INDICES; stride += 4;
case .BONE_WEIGHTS; stride += 4;
}
}
for 0..mesh.positions.count-1 {
for input: input {
if input == {
case .POSITION; {
array_add(*final_vertices, mesh.positions[it].x);
array_add(*final_vertices, mesh.positions[it].y);
array_add(*final_vertices, mesh.positions[it].z);
}
case .NORMAL; {
if mesh.normals.count > 0 {
array_add(*final_vertices, mesh.normals[it].x);
array_add(*final_vertices, mesh.normals[it].y);
array_add(*final_vertices, mesh.normals[it].z);
} else {
array_add(*final_vertices, 0.0);
array_add(*final_vertices, 0.0);
array_add(*final_vertices, 0.0);
log_error("Mesh didn't have required normals\n");
}
}
case .TEXCOORD; {
if mesh.texcoords.count > 0 {
array_add(*final_vertices, mesh.texcoords[it].x);
array_add(*final_vertices, mesh.texcoords[it].y);
} else {
array_add(*final_vertices, 0.0);
array_add(*final_vertices, 0.0);
log_error("Mesh didn't have required texcoords\n");
}
}
case .COLOR; {
if mesh.colors.count > 0 {
array_add(*final_vertices, mesh.colors[it].x);
array_add(*final_vertices, mesh.colors[it].y);
array_add(*final_vertices, mesh.colors[it].z);
array_add(*final_vertices, mesh.colors[it].w);
} else {
array_add(*final_vertices, 0.0);
array_add(*final_vertices, 0.0);
array_add(*final_vertices, 0.0);
array_add(*final_vertices, 0.0);
log_error("Mesh didn't have required colors\n");
}
}
case .TANGENT; {
if mesh.tangents.count > 0 {
array_add(*final_vertices, mesh.tangents[it].x);
array_add(*final_vertices, mesh.tangents[it].y);
array_add(*final_vertices, mesh.tangents[it].z);
} else {
array_add(*final_vertices, 0.0);
array_add(*final_vertices, 0.0);
array_add(*final_vertices, 0.0);
log_error("Mesh didn't have required tangents\n");
}
}
case .BITANGENT; {
if mesh.bitangents.count > 0 {
array_add(*final_vertices, mesh.bitangents[it].x);
array_add(*final_vertices, mesh.bitangents[it].y);
array_add(*final_vertices, mesh.bitangents[it].z);
} else {
array_add(*final_vertices, 0.0);
array_add(*final_vertices, 0.0);
array_add(*final_vertices, 0.0);
log_error("Mesh didn't have required bitangents\n");
}
}
case .BONE_INDICES; {
if mesh.skin_data.count > 0 {
for index: 0..3 {
array_add(*final_vertices, mesh.skin_data[it].bone_index[index]);
}
} else {
array_add(*final_vertices, 0.0);
array_add(*final_vertices, 0.0);
array_add(*final_vertices, 0.0);
array_add(*final_vertices, 0.0);
log_error("Mesh didn't have required bone indices\n");
}
}
case .BONE_WEIGHTS; {
if mesh.skin_data.count > 0 {
for index: 0..3 {
array_add(*final_vertices, mesh.skin_data[it].bone_weight[index]);
}
} else {
array_add(*final_vertices, 0.0);
array_add(*final_vertices, 0.0);
array_add(*final_vertices, 0.0);
array_add(*final_vertices, 0.0);
log_error("Mesh didn't have required bone weights\n");
}
}
}
}
}
vb_size := size_of(float)*stride*mesh.positions.count;
vb := create_vertex_buffer(renderer, final_vertices.data, xx vb_size, stride=size_of(float)*stride);
table_add(*mesh.vbs, hash, vb);
}
value, success := table_find(*mesh.vbs, hash);
return value;
}
delete_mesh :: (handle: Mesh_Handle) {
mesh := parray_get(*renderer.meshes, handle);
for mesh.vbs {
destroy_buffer(renderer, it);
}
deinit(*mesh.vbs);
parray_remove(*renderer.meshes, handle);
}

668
renderer/model.jai Normal file
View File

@@ -0,0 +1,668 @@
Node_Handle :: #type, distinct u32;
Material_Handle :: #type, distinct u32;
MAX_BONES :: 128;
MAX_WEIGHTS :: 4;
Node :: struct {
name : string;
path : string;
transform : Transform;
meshes : [..] Mesh_Handle;
material_defaults : [..] Model_Material;
materials_old : [..] Material_Old;
num_bones: s32;
parent : Node_Handle;
children : [..] Node_Handle;
// Instance variables
materials : [..] *Material;
has_sampled_animation: bool;
}
Node_Animation :: struct {
framerate : float64;
num_frames : s32;
rotation : [..] Quaternion;
position : [..] Vector3;
scale : [..] Vector3;
constant : bool;
const_rotation : Quaternion;
const_position : Vector3;
const_scale : Vector3;
is_set: bool;
}
Animation :: struct {
name: string;
duration: float64;
num_frames: s32;
framerate: float64;
nodes : [..] Node_Animation;
}
Model_Material :: struct {
base_color : Vector4;
textures : struct {
base_color: Texture_Handle;
normal: Texture_Handle;
}
}
Model :: struct {
name : string;
path : string;
nodes : [..] Node;
animations : [..] Animation;
materials : [..] Model_Material;
}
get_first_mesh_from_model :: (model: Model) -> Mesh_Handle, bool {
for model.nodes {
for m: it.meshes {
return m, true;
}
}
return 0, false;
}
get_mesh_by_name :: (model: Model, name: string) -> Mesh_Handle, bool {
for model.nodes {
for m: it.meshes {
mesh := parray_get(*renderer.meshes, m);
if mesh.name == name {
return m, true;
}
}
}
return 0, false;
}
Coloru16 :: struct {
r: u16;
g: u16;
b: u16;
a: u16;
}
parse_fbx_node :: (model: *Model, fbx_node: *ufbx_node) {
node : Node;
node.name = copy_string(to_string(fbx_node.name.data));
if fbx_node.parent != null {
node.parent = cast(Node_Handle) fbx_node.parent.typed_id + 1; // UFBX ids are 0-indexed, but 0 means none in our API
}
t := create_identity_transform();
transform := ufbx_matrix_to_transform(*fbx_node.node_to_parent);
t.position.x = cast(float)transform.translation.x;
t.position.y = cast(float)transform.translation.y;
t.position.z = cast(float)transform.translation.z;
t.scale.x = xx transform.scale.x;
t.scale.y = xx transform.scale.y;
t.scale.z = xx transform.scale.z;
t.orientation.x = cast(float)transform.rotation.x;
t.orientation.y = cast(float)transform.rotation.y;
t.orientation.z = cast(float)transform.rotation.z;
t.orientation.w = cast(float)transform.rotation.w;
update_matrix(*t);
//t.model_matrix = make_matrix(world_position, world_orientation, world_scale);
t.dirty = true;
node.transform = t;
Mesh_Vertex :: struct {
position: Vector3;
normal : Vector3;
uv : Vector2;
};
if fbx_node.mesh != null {
fbx_mesh := fbx_node.mesh;
max_parts : size_t = 0;
max_triangles : size_t = 0;
for pi: 0..cast(s32)fbx_mesh.materials.count-1 {
mesh_mat := fbx_mesh.materials.data[pi];
if mesh_mat.num_triangles == 0 continue;
max_parts += 1;
max_triangles = max(max_triangles, mesh_mat.num_triangles);
}
num_tri_indices := fbx_mesh.max_face_triangles * 3;
tri_indices : [..] u32;
vertices : [..] Mesh_Vertex;
indices : [..] u32;
skin_vertices : [..] Skin_Vertex;
mesh_skin_vertices : [..] Skin_Vertex;
tri_indices.allocator = temp;
vertices.allocator = temp;
indices.allocator = temp;
skin_vertices.allocator = temp;
mesh_skin_vertices.allocator = temp;
indices.allocator = temp;
array_resize(*tri_indices, xx num_tri_indices);
array_resize(*vertices, xx max_triangles * 3);
array_resize(*indices, xx max_triangles * 3);
array_resize(*skin_vertices, xx max_triangles * 3);
array_resize(*mesh_skin_vertices, xx max_triangles * 3);
num_bones : s32 = 0;
skin : *ufbx_skin_deformer = null;
skinned : bool;
bone_indices: [..] s32;
bone_matrices: [..] Matrix4;
bone_indices.allocator = temp;
bone_matrices.allocator = temp;
if fbx_mesh.skin_deformers.count > 0 && fbx_mesh.skin_deformers.data[0].clusters.count > 0 {
skinned = true;
skin = fbx_mesh.skin_deformers.data[0];
for ci: 0..skin.clusters.count-1 {
cluster := skin.clusters.data[ci];
if num_bones < MAX_BONES {
array_add(*bone_indices, cast(s32)cluster.bone_node.typed_id);
array_add(*bone_matrices, ufbx_to_mat4(cluster.geometry_to_bone));
num_bones += 1;
}
}
for vi: 0..fbx_mesh.num_vertices-1 {
num_weights := 0;
total_weight := 0.0;
weights : [4] float;
clusters : [4] float;
vertex_weights := skin.vertices.data[vi];
for wi: 0..vertex_weights.num_weights-1 {
if num_weights >= 4 break;
weight := skin.weights.data[vertex_weights.weight_begin + wi];
if weight.cluster_index < MAX_BONES {
total_weight += cast(float)weight.weight;
clusters[num_weights] = cast(float)weight.cluster_index;
weights[num_weights] = cast(float)weight.weight;
num_weights += 1;
}
}
if total_weight > 0.0 {
skin_vert := *mesh_skin_vertices[vi];
quantized_sum : float = 0;
for i: 0..3 {
quantized_weight := weights[i] / total_weight;
quantized_sum += quantized_weight;
skin_vert.bone_index[i] = clusters[i];
skin_vert.bone_weight[i] = quantized_weight;
}
skin_vert.bone_weight[0] += 1.0 - quantized_sum;
}
}
}
node.num_bones = num_bones;
for pi: 0..fbx_mesh.materials.count-1 {
mesh : Mesh;
mesh.name = copy_string(to_string(fbx_mesh.name.data));
mesh.num_bones = num_bones;
array_resize(*mesh.bone_indices, bone_indices.count);
array_resize(*mesh.bone_matrices, bone_matrices.count);
memcpy(mesh.bone_indices.data, bone_indices.data, size_of(s32) * bone_indices.count);
memcpy(mesh.bone_matrices.data, bone_matrices.data, size_of(Matrix4) * bone_matrices.count);
mesh_mat := fbx_mesh.materials.data[pi];
if mesh_mat.num_triangles == 0 continue;
num_indices : size_t = 0;
for fi: 0..mesh_mat.num_faces-1 {
face := fbx_mesh.faces.data[mesh_mat.face_indices.data[fi]];
num_tris := ufbx_catch_triangulate_face(null, tri_indices.data, num_tri_indices, fbx_mesh, face);
default_uv : ufbx_vec2;
for vi: 0..num_tris * 3 - 1 {
ix := tri_indices.data[num_tris * 3 - 1 -vi];
vert := *vertices.data[num_indices];
pos := ufbx_catch_get_vertex_vec3(null, *fbx_mesh.vertex_position, ix);
normal := ufbx_catch_get_vertex_vec3(null, *fbx_mesh.vertex_normal, ix);
uv : ufbx_vec2;
if fbx_mesh.vertex_uv.exists {
uv = ufbx_catch_get_vertex_vec2(null, *fbx_mesh.vertex_uv, ix);
}
vert.position.x = xx pos.x;
vert.position.y = xx pos.y;
vert.position.z = xx pos.z;
vert.normal.x = xx normal.x;
vert.normal.y = xx normal.y;
vert.normal.z = xx normal.z;
vert.normal = normalize(vert.normal);
vert.uv.x = xx uv.x;
vert.uv.y = xx uv.y;
if skin {
skin_vertices[num_indices] = mesh_skin_vertices[fbx_mesh.vertex_indices.data[ix]];
}
num_indices += 1;
}
}
num_streams : u64 = 1;
streams : [2] ufbx_vertex_stream;
streams[0].data = vertices.data;
streams[0].vertex_size = size_of(Mesh_Vertex);
if skin {
num_streams = 2;
streams[1].data = skin_vertices.data;
streams[1].vertex_size = size_of(Skin_Vertex);
}
error : ufbx_error;
num_vertices := ufbx_generate_indices(streams.data, num_streams, indices.data, num_indices, null, *error);
if error.type != .UFBX_ERROR_NONE {
log_error("Failed to generate index buffer\n");
}
array_reserve(*mesh.indices, xx num_indices);
for 0..num_indices-1 {
array_add(*mesh.indices, cast(u32)indices[it]);
}
array_reserve(*mesh.positions, xx num_vertices);
array_reserve(*mesh.normals, xx num_vertices);
array_reserve(*mesh.texcoords, xx num_vertices);
if skin {
array_reserve(*mesh.skin_data, xx num_vertices);
}
for 0..num_vertices-1 {
v := vertices[it];
array_add(*mesh.positions, v.position);
array_add(*mesh.normals, v.normal);
array_add(*mesh.texcoords, v.uv);
if skin {
array_add(*mesh.skin_data, skin_vertices[it]);
}
}
// Generate tangents + bitangents
if mesh.texcoords.count > 0 {
array_resize(*mesh.tangents, mesh.positions.count);
array_resize(*mesh.bitangents, mesh.positions.count);
i : u64 = 0;
while i < num_indices - 1 {
defer i += 3;
i0 := mesh.indices[i+0];
i1 := mesh.indices[i+1];
i2 := mesh.indices[i+2];
pos0 := mesh.positions[i0];
pos1 := mesh.positions[i1];
pos2 := mesh.positions[i2];
uv0 := mesh.texcoords[i0];
uv1 := mesh.texcoords[i1];
uv2 := mesh.texcoords[i2];
edge1 := pos1 - pos0;
edge2 := pos2 - pos0;
delta_uv1 := uv1 - uv0;
delta_uv2 := uv2 - uv0;
f := 1.0 / (delta_uv1.x * delta_uv2.y - delta_uv2.x * delta_uv1.y);
tangent : Vector3 = ---;
bitangent : Vector3 = ---;
tangent.x = f * (delta_uv2.y * edge1.x - delta_uv1.y * edge2.x);
tangent.y = f * (delta_uv2.y * edge1.y - delta_uv1.y * edge2.y);
tangent.z = f * (delta_uv2.y * edge1.z - delta_uv1.y * edge2.z);
bitangent.x = f * (-delta_uv2.x * edge1.x + delta_uv1.x * edge2.x);
bitangent.y = f * (-delta_uv2.x * edge1.y + delta_uv1.x * edge2.y);
bitangent.z = f * (-delta_uv2.x * edge1.z + delta_uv1.x * edge2.z);
mesh.tangents[i0] = tangent;
mesh.tangents[i1] = tangent;
mesh.tangents[i2] = tangent;
mesh.bitangents[i0] = bitangent;
mesh.bitangents[i1] = bitangent;
mesh.bitangents[i2] = bitangent;
}
}
ib_size := size_of(u32)*mesh.indices.count;
mesh.ib = create_index_buffer(renderer, mesh.indices.data, xx ib_size);
//material : Base_Material;
//if mesh_mat.material != null {
// for 0..mesh_mat.material.props.props.count-1 {
// prop := mesh_mat.material.props.props.data[it];
// prop_name := to_string(prop.name.data);
// if prop_name == "DiffuseColor" {
// material.base_color.x = xx prop.value_vec3.x;
// material.base_color.y = xx prop.value_vec3.y;
// material.base_color.z = xx prop.value_vec3.z;
// material.base_color.w = 1.0;
// }
// }
//}
array_add(*node.meshes, parray_add(*renderer.meshes, mesh));
array_add(*node.material_defaults, model.materials[mesh_mat.material.typed_id]); // @Incomplete
}
}
array_reserve(*node.children, xx fbx_node.children.count);
for 0..cast(s32)fbx_node.children.count-1 {
array_add(*node.children, cast(Node_Handle)fbx_node.children.data[it].typed_id + 1);
}
array_add(*model.nodes, node);
}
ufbx_to_v3 :: (ufbx_v3: ufbx_vec3) -> Vector3 {
v : Vector3 = ---;
v.x = cast(float)ufbx_v3.x;
v.y = cast(float)ufbx_v3.y;
v.z = cast(float)ufbx_v3.z;
return v;
}
ufbx_to_quat :: (ufbx_q: ufbx_quat) -> Quaternion {
q : Quaternion = ---;
q.x = cast(float)ufbx_q.x;
q.y = cast(float)ufbx_q.y;
q.z = cast(float)ufbx_q.z;
q.w = cast(float)ufbx_q.w;
return q;
}
ufbx_to_mat4 :: (m: ufbx_matrix) -> Matrix4 {
result : Matrix4;
result._11 = cast(float)m.m00;
result._12 = cast(float)m.m01;
result._13 = cast(float)m.m02;
result._14 = cast(float)m.m03;
result._21 = cast(float)m.m10;
result._22 = cast(float)m.m11;
result._23 = cast(float)m.m12;
result._24 = cast(float)m.m13;
result._31 = cast(float)m.m20;
result._32 = cast(float)m.m21;
result._33 = cast(float)m.m22;
result._34 = cast(float)m.m23;
result._41 = 0.0;
result._42 = 0.0;
result._43 = 0.0;
result._44 = 1.0;
return result;
}
v3_equal :: (v1: Vector3, v2: Vector3) -> bool {
return abs(v1.x - v2.x) < 0.0001 && abs(v1.y - v2.y) < 0.0001 && abs(v1.z - v2.z) < 0.0001;
}
quat_equal :: (q1: Quaternion, q2: Quaternion) -> bool {
return abs(q1.x - q2.x) < 0.0001 && abs(q1.y - q2.y) < 0.0001 && abs(q1.z - q2.z) < 0.0001 && abs(q1.w - q2.w) < 0.0001;
}
parse_node_anim :: (animation: *Animation, node_anim: *Node_Animation, stack: *ufbx_anim_stack, node: *ufbx_node) {
array_resize(*node_anim.rotation, animation.num_frames);
array_resize(*node_anim.position, animation.num_frames);
array_resize(*node_anim.scale, animation.num_frames);
const_rotation := true;
const_position := true;
const_scale := true;
if animation.num_frames == 0 {
node_anim.const_rotation = .{0,0,0,1};
node_anim.const_position = .{0,0,0};
node_anim.const_scale = .{1,1,1};
} else {
// Sample the node's transform evenly for the whole animation stack duration
for i: 0..animation.num_frames-1 {
time := stack.time_begin + cast(float64)i / animation.framerate;
transform := ufbx_evaluate_transform(*stack.anim, node, time);
node_anim.rotation[i] = ufbx_to_quat(transform.rotation);
node_anim.position[i] = ufbx_to_v3(transform.translation);
node_anim.scale[i] = ufbx_to_v3(transform.scale);
if i > 0 {
// Negated quaternions are equivalent, but interpolating between ones of different
// polarity takes a the longer path, so flip the quaternion if necessary.
if dot(node_anim.rotation[i], node_anim.rotation[i - 1]) < 0.0 {
node_anim.rotation[i].x = -node_anim.rotation[i].x;
node_anim.rotation[i].y = -node_anim.rotation[i].y;
node_anim.rotation[i].z = -node_anim.rotation[i].z;
node_anim.rotation[i].w = -node_anim.rotation[i].w;
}
// Keep track of which channels are constant for the whole animation as an optimization
if !quat_equal(node_anim.rotation[i - 1], node_anim.rotation[i]) const_rotation = false;
if !v3_equal(node_anim.position[i - 1], node_anim.position[i]) const_position = false;
if !v3_equal(node_anim.scale[i - 1], node_anim.scale[i]) const_scale = false;
}
}
if const_rotation { node_anim.const_rotation = node_anim.rotation[0]; array_free(node_anim.rotation); node_anim.rotation.count = 0; }
if const_position { node_anim.const_position = node_anim.position[0]; array_free(node_anim.position); node_anim.position.count = 0; }
if const_scale { node_anim.const_scale = node_anim.scale[0]; array_free(node_anim.scale); node_anim.scale.count = 0; }
}
node_anim.is_set = true;
}
parse_anim_stack :: (animation: *Animation, stack: *ufbx_anim_stack, scene: *ufbx_scene, model: *Model) {
animation.name = copy_string(to_string(stack.name.data));
animation.framerate = 24.0;
animation.duration = cast(float)(stack.time_end - stack.time_begin);
animation.num_frames = cast(s32)(animation.duration * animation.framerate);
animation.framerate = cast(float)(animation.num_frames - 1) / animation.duration;
array_resize(*animation.nodes, xx model.nodes.count);
if scene.nodes.count != xx model.nodes.count {
for 0..scene.nodes.count-1 {
scene_node := scene.nodes.data[it];
for j: 0..model.nodes.count-1 {
model_node := model.nodes[j];
if to_string(scene_node.name.data) == model_node.name {
animation.nodes[j] = .{};
parse_node_anim(animation, *animation.nodes[j], stack, scene_node);
break;
}
}
}
for 0..model.nodes.count-1 {
if !animation.nodes[it].is_set {
animation.nodes[it].const_position = model.nodes[it].transform.position;
animation.nodes[it].const_rotation = model.nodes[it].transform.orientation;
animation.nodes[it].const_scale = model.nodes[it].transform.scale;
animation.nodes[it].is_set = true;
}
}
} else {
for 0..scene.nodes.count-1 {
parse_node_anim(animation, *animation.nodes[it], stack, scene.nodes.data[it]);
}
}
}
load_fbx_animations_into_existing_model :: (path: string, model: *Model) {
// Load the file as normal, but only loop through the animations
opts : ufbx_load_opts = .{};
opts.load_external_files = true;
opts.evaluate_skinning = true;
opts.target_axes.right = .POSITIVE_X;
opts.target_axes.up = .POSITIVE_Y;
opts.target_axes.front = .NEGATIVE_Z;
opts.target_unit_meters = 1.0;
//opts.space_conversion = ufbx_space_conversion.UFBX_SPACE_CONVERSION_ADJUST_TRANSFORMS;
error : ufbx_error;
scene := ufbx_load_file(to_temp_c_string(path), *opts, *error);
if scene == null {
log_error("FBX '%' could not be loaded\n", path);
}
// The rig and nodes should be the same as the specified model
//if cast(s32)scene.nodes.count != model.nodes.count {
// log_error("Node count for '%' (%) not the same as original model (%)\n", path, scene.nodes.count, model.nodes.count);
// ufbx_free_scene(scene);
// return;
//}
for 0..scene.anim_stacks.count-1 {
animation : Animation;
parse_anim_stack(*animation, scene.anim_stacks.data[it], scene, model);
array_add(*model.animations, animation);
}
ufbx_free_scene(scene);
}
get_model_by_path :: (path: string) -> *Model {
for * renderer.model_lib {
if it.path == path {
return it;
}
}
return null;
}
load_fbx_texture :: (map: ufbx_material_map, format: Format) -> Texture_Handle {
if map.texture != null && map.texture.content.size > 0 {
return load_texture_from_data(renderer, map.texture.content.data, map.texture.content.size, format=format);
}
return 0;
}
load_fbx :: (path: string) -> *Model, bool {
opts : ufbx_load_opts = .{};
opts.load_external_files = true;
opts.evaluate_skinning = true;
opts.target_axes.right = .POSITIVE_X;
opts.target_axes.up = .POSITIVE_Y;
opts.target_axes.front = .NEGATIVE_Z;
opts.target_unit_meters = 1.0;
//opts.space_conversion = ufbx_space_conversion.UFBX_SPACE_CONVERSION_ADJUST_TRANSFORMS;
error : ufbx_error;
scene := ufbx_load_file(to_temp_c_string(path), *opts, *error);
if scene == null {
log_error("FBX '%' could not be loaded", path);
return null, false;
}
model, locator := find_and_occupy_empty_slot(*renderer.model_lib);
model.path = copy_string(path);
model.name = copy_string(path);
// Materials
for i: 0..scene.materials.count - 1 {
mat := scene.materials.data[i];
model_material : Model_Material;
model_material.textures.base_color = load_fbx_texture(mat.pbr.base_color, format=.R8G8B8A8_UNORM_SRGB);
model_material.textures.normal = load_fbx_texture(mat.pbr.normal_map, format=.R8G8B8A8_UNORM);
for 0..mat.props.props.count-1 {
prop := mat.props.props.data[it];
prop_name := to_string(prop.name.data,, allocator=temp);
if prop_name == "DiffuseColor" {
model_material.base_color.x = xx prop.value_vec3.x;
model_material.base_color.y = xx prop.value_vec3.y;
model_material.base_color.z = xx prop.value_vec3.z;
model_material.base_color.w = 1.0;
}
}
//mat.pbr.base_factor;
//create_texture :: (using renderer: *Renderer, data: *void, width: u32, height: u32, channels: u32, path: string = "", generate_mips: bool = true) -> Texture_Handle {
//Material &dst = materials[i + 1];
//dst.base_factor.value.x = 1.0f;
//setup_texture(dst.base_factor, mat->pbr.base_factor);
//setup_texture(dst.base_color, mat->pbr.base_color);
//setup_texture(dst.roughness, mat->pbr.roughness);
//setup_texture(dst.metallic, mat->pbr.metalness);
//setup_texture(dst.emission_factor, mat->pbr.emission_factor);
//setup_texture(dst.emission_color, mat->pbr.emission_color);
//dst.base_color.image.srgb = true;
//dst.emission_color.image.srgb = true;
array_add(*model.materials, model_material);
}
array_reserve(*model.nodes, xx scene.nodes.count);
zero_handle : Node_Handle = 0;
for i: 0..scene.nodes.count-1 {
node := scene.nodes.data[i];
parse_fbx_node(model, node);
}
// Load animations
array_resize(*model.animations, xx scene.anim_stacks.count);
for 0..model.animations.count-1 {
parse_anim_stack(*model.animations[it], scene.anim_stacks.data[it], scene, model);
}
ufbx_free_scene(scene);
return model, false;
}
get_or_load_model :: (path: string) -> *Model {
for * renderer.model_lib {
if it.path == path {
return it;
}
}
return load_fbx(path);
}
#import "ufbx";

140
renderer/render_graph.jai Normal file
View File

@@ -0,0 +1,140 @@
Render_Pass_Handle :: #type, distinct u32;
Pass_Buffer_Info :: struct {
shader_type: Shader_Type;
slot: u32;
buffer: Buffer_Handle;
}
Render_Graph :: struct {
temp_sampler: Sampler_Handle;
render_passes: PArray(Render_Pass, Render_Pass_Handle);
}
new_render_graph :: () -> *Render_Graph {
graph := New(Render_Graph);
return graph;
}
execute_render_graph :: (graph: *Render_Graph) {
for graph.render_passes {
execute_render_pass(it);
}
}
push_material_pass_properties :: (render_pass: Render_Pass, mat_pass: Material_Pass_Old) {
for mat_pass.properties {
if it.parameter.type == {
case .BUFFER;
push_cmd_set_constant_buffer(renderer, it.parameter.slot, it.buffer, it.parameter.shader);
case .SAMPLER;
push_cmd_set_sampler(renderer, it.parameter.slot, it.sampler);
case .TEXTURE;
{
is_texture_input := false;
input_index := 0;
if it.parameter.mapping == {
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;
}
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, it.parameter.slot, owning_pass.depth_stencil);
} else {
push_cmd_set_texture(renderer, it.parameter.slot, owning_pass.render_targets[input.rt_index]);
}
} else {
if it.texture > 0 {
push_cmd_set_texture(renderer, it.parameter.slot, it.texture);
}
}
}
}
}
}
setup_pass_inputs :: (render_pass: Render_Pass) {
for input: render_pass.inputs {
owning_pass := parray_get(*renderer.render_graph.render_passes, input.pass_handle);
if input.rt_index == DEPTH_STENCIL_SLOT {
push_cmd_set_texture(renderer, xx it_index, owning_pass.depth_stencil);
} else {
push_cmd_set_texture(renderer, xx it_index, owning_pass.render_targets[input.rt_index]);
}
}
}
set_render_pass_clear_color :: (rp: Render_Pass_Handle, input_index: s32, color: Color) {
pass := parray_get(*renderer.render_graph.render_passes, rp);
pass.clear_colors[input_index] = color;
}
execute_render_pass :: (render_pass: Render_Pass) { // @Incomplete: Add command buffer as parameter
if render_pass.render_targets.count == 0 {
if render_pass.has_depth_stencil {
push_cmd_set_render_targets(renderer, depth_stencil_enabled=render_pass.has_depth_stencil, depth_stencil_buffer=render_pass.depth_stencil);
push_cmd_clear_depth_stencil(renderer, render_pass.depth_stencil, 1.0);
width := ifx render_pass.width == SWAPCHAIN_SIZE then renderer.render_target_width else render_pass.width;
height := ifx render_pass.height == SWAPCHAIN_SIZE then renderer.render_target_height else render_pass.height;
push_cmd_set_viewport(renderer, width, height);
} else {
assert(render_pass.uses_backbuffer);
push_cmd_set_backbuffer(renderer);
color: Vector4;
color.x = 0.0;
color.y = 0.0;
color.z = 0.0;
color.w = 1.0;
push_cmd_clear_backbuffer(renderer, color);
push_cmd_set_viewport(renderer, xx renderer.render_target_width, xx renderer.render_target_height);
}
} else {
width := render_pass.width;
height := render_pass.height;
if render_pass.width == SWAPCHAIN_SIZE {
width = renderer.render_target_width;
}
if render_pass.height == SWAPCHAIN_SIZE {
height = renderer.render_target_height;
}
push_cmd_set_render_targets(renderer, ..render_pass.render_targets, render_pass.has_depth_stencil, render_pass.depth_stencil);
push_cmd_set_viewport(renderer, width, height);
for render_pass.render_targets {
push_cmd_clear_render_target(renderer, it, render_pass.clear_colors[it_index]);
}
if render_pass.has_depth_stencil {
push_cmd_clear_depth_stencil(renderer, render_pass.depth_stencil, 1.0);
}
}
// @NOCHECKIN Setup inputs!
setup_pass_inputs(render_pass);
render_pass.callback(render_pass);
}
#load "render_pass.jai";

97
renderer/render_pass.jai Normal file
View File

@@ -0,0 +1,97 @@
DEPTH_STENCIL_SLOT :: 42;
// The slots used by the render pass render targets
// Slots 10-17 are reserved for the passes
Render_Target_Shader_Slots :: enum {
SLOT0 :: 10;
SLOT1 :: 11;
SLOT2 :: 12;
SLOT3 :: 13;
SLOT4 :: 14;
SLOT5 :: 15;
SLOT6 :: 16;
SLOT7 :: 17;
}
Render_Target_Info :: struct {
format: Format;
width: u32;
height: u32;
clear_color: Color;
}
Depth_Stencil_Info :: struct {
enabled: bool;
format: Format;
width: u32;
height: u32;
}
Render_Pass_Input_Info :: struct {
pass_handle: Render_Pass_Handle;
rt_index: u32; // the index into the render pass
}
Render_Pass :: struct {
name: string;
inputs: [..] Render_Pass_Input_Info;
uses_backbuffer: bool;
render_targets: [..] Render_Target_Handle;
clear_colors: [..] Color;
depth_stencil: Depth_Stencil_Buffer_Handle;
has_depth_stencil: bool;
width: u32;
height: u32;
callback: (pass: Render_Pass);
}
create_render_pass :: (graph: *Render_Graph, name: string, callback: (Render_Pass), render_target_infos: [] Render_Target_Info = .[], depth_stencil_info : Depth_Stencil_Info = .{}, uses_backbuffer: bool = false) -> Render_Pass_Handle {
render_pass : Render_Pass;
render_pass.name = copy_string(name);
render_pass.uses_backbuffer = uses_backbuffer;
render_pass.callback = callback;
if depth_stencil_info.enabled {
render_pass.has_depth_stencil = true;
render_pass.depth_stencil = create_depth_stencil_buffer(depth_stencil_info.width, depth_stencil_info.height, 0);
render_pass.width = depth_stencil_info.width;
render_pass.height = depth_stencil_info.height;
}
array_reserve(*render_pass.render_targets, render_target_infos.count);
array_reserve(*render_pass.clear_colors, render_target_infos.count);
for render_target_infos {
array_add(*render_pass.render_targets, create_render_target(it.width, it.height, it.format));
array_add(*render_pass.clear_colors, it.clear_color);
render_pass.width = it.width;
render_pass.height = it.height;
}
return parray_add(*graph.render_passes, render_pass);
}
add_render_pass_input :: (graph: *Render_Graph, to_pass: Render_Pass_Handle, from_pass: Render_Pass_Handle, rt_index: u32) {
info : Render_Pass_Input_Info;
info.pass_handle = from_pass;
info.rt_index = rt_index;
pass := parray_get(*graph.render_passes, to_pass);
array_add(*pass.inputs, info);
}
get_texture_from_pass :: (name: string) -> Texture_Handle {
for renderer.render_graph.render_passes {
if it.name == name {
rt := parray_get(renderer.render_targets, it.render_targets[0]);
return rt.texture;
}
}
return xx 0;
}

1630
renderer/renderer.jai Normal file

File diff suppressed because it is too large Load Diff

9
renderer/vertex.jai Normal file
View File

@@ -0,0 +1,9 @@
Textured_Vert :: struct {
position: Vector2;
texcoord: Vector2;
}
Textured_Vert_3D :: struct {
position: Vector3;
texcoord: Vector2;
}