diff options
author | omniscient <17525998+omnisci3nce@users.noreply.github.com> | 2024-08-10 14:06:15 +1000 |
---|---|---|
committer | omniscient <17525998+omnisci3nce@users.noreply.github.com> | 2024-08-10 14:06:15 +1000 |
commit | 9cb4bfacc69b2a95ce8e9250afc33fb04d1ba548 (patch) | |
tree | fbe75da188ac83b34153ac79c367b9d57e0d5ff9 /src/render | |
parent | 071a635e63536e50abfad7d5aeca1208dba58025 (diff) |
remove old code
Diffstat (limited to 'src/render')
-rw-r--r-- | src/render/archive/backends/backend_test.c (renamed from src/render/backends/backend_test.c) | 0 | ||||
-rw-r--r-- | src/render/archive/backends/metal/README.md (renamed from src/render/backends/metal/README.md) | 0 | ||||
-rw-r--r-- | src/render/archive/backends/metal/backend_metal.h (renamed from src/render/backends/metal/backend_metal.h) | 0 | ||||
-rw-r--r-- | src/render/archive/backends/metal/backend_metal.m (renamed from src/render/backends/metal/backend_metal.m) | 0 | ||||
-rw-r--r-- | src/render/archive/backends/opengl/backend_opengl.c (renamed from src/render/backends/opengl/backend_opengl.c) | 0 | ||||
-rw-r--r-- | src/render/archive/backends/opengl/backend_opengl.h (renamed from src/render/backends/opengl/backend_opengl.h) | 0 | ||||
-rw-r--r-- | src/render/archive/backends/vulkan/README.md (renamed from src/render/backends/vulkan/README.md) | 0 | ||||
-rw-r--r-- | src/render/archive/backends/vulkan/backend_vulkan.c (renamed from src/render/backends/vulkan/backend_vulkan.c) | 0 | ||||
-rw-r--r-- | src/render/archive/backends/vulkan/backend_vulkan.h (renamed from src/render/backends/vulkan/backend_vulkan.h) | 0 | ||||
-rw-r--r-- | src/render/builtin_materials.h | 153 | ||||
-rw-r--r-- | src/render/immdraw.c | 28 | ||||
-rw-r--r-- | src/render/immdraw.h | 27 | ||||
-rw-r--r-- | src/render/immediate.c | 46 | ||||
-rw-r--r-- | src/render/pbr.c | 216 | ||||
-rw-r--r-- | src/render/pbr.h | 61 | ||||
-rw-r--r-- | src/render/ral.c | 97 | ||||
-rw-r--r-- | src/render/ral_types.h | 102 | ||||
-rw-r--r-- | src/render/render.c | 357 | ||||
-rw-r--r-- | src/render/render.h | 146 | ||||
-rw-r--r-- | src/render/render_types.h | 143 | ||||
-rw-r--r-- | src/render/renderpasses.c | 140 | ||||
-rw-r--r-- | src/render/renderpasses.h | 56 | ||||
-rw-r--r-- | src/render/shader_layouts.h | 70 | ||||
-rw-r--r-- | src/render/shadows.c | 211 | ||||
-rw-r--r-- | src/render/shadows.h | 48 | ||||
-rw-r--r-- | src/render/skybox.c | 167 | ||||
-rw-r--r-- | src/render/skybox.h | 41 |
27 files changed, 1515 insertions, 594 deletions
diff --git a/src/render/backends/backend_test.c b/src/render/archive/backends/backend_test.c index 6347e27..6347e27 100644 --- a/src/render/backends/backend_test.c +++ b/src/render/archive/backends/backend_test.c diff --git a/src/render/backends/metal/README.md b/src/render/archive/backends/metal/README.md index f87f5c1..f87f5c1 100644 --- a/src/render/backends/metal/README.md +++ b/src/render/archive/backends/metal/README.md diff --git a/src/render/backends/metal/backend_metal.h b/src/render/archive/backends/metal/backend_metal.h index 9561bb6..9561bb6 100644 --- a/src/render/backends/metal/backend_metal.h +++ b/src/render/archive/backends/metal/backend_metal.h diff --git a/src/render/backends/metal/backend_metal.m b/src/render/archive/backends/metal/backend_metal.m index 4787755..4787755 100644 --- a/src/render/backends/metal/backend_metal.m +++ b/src/render/archive/backends/metal/backend_metal.m diff --git a/src/render/backends/opengl/backend_opengl.c b/src/render/archive/backends/opengl/backend_opengl.c index 43105e2..43105e2 100644 --- a/src/render/backends/opengl/backend_opengl.c +++ b/src/render/archive/backends/opengl/backend_opengl.c diff --git a/src/render/backends/opengl/backend_opengl.h b/src/render/archive/backends/opengl/backend_opengl.h index 73a19ed..73a19ed 100644 --- a/src/render/backends/opengl/backend_opengl.h +++ b/src/render/archive/backends/opengl/backend_opengl.h diff --git a/src/render/backends/vulkan/README.md b/src/render/archive/backends/vulkan/README.md index 220ed64..220ed64 100644 --- a/src/render/backends/vulkan/README.md +++ b/src/render/archive/backends/vulkan/README.md diff --git a/src/render/backends/vulkan/backend_vulkan.c b/src/render/archive/backends/vulkan/backend_vulkan.c index 8801230..8801230 100644 --- a/src/render/backends/vulkan/backend_vulkan.c +++ b/src/render/archive/backends/vulkan/backend_vulkan.c diff --git a/src/render/backends/vulkan/backend_vulkan.h b/src/render/archive/backends/vulkan/backend_vulkan.h index 6ca0bb5..6ca0bb5 100644 --- a/src/render/backends/vulkan/backend_vulkan.h +++ b/src/render/archive/backends/vulkan/backend_vulkan.h diff --git a/src/render/builtin_materials.h b/src/render/builtin_materials.h deleted file mode 100644 index b15e62e..0000000 --- a/src/render/builtin_materials.h +++ /dev/null @@ -1,153 +0,0 @@ -/** - * @file builtin_materials.h - * @author your name (you@domain.com) - * @brief - * @version 0.1 - * @date 2024-06-15 - * - * @copyright Copyright (c) 2024 - * - */ -#pragma once - -#include <assert.h> -#include "defines.h" -#include "ral_types.h" - -// Currently supported materials -// - Blinn Phong (textured) -// - PBR (params) -// - PBR (textured) - -// Thoughts -// -------- -// -// A material and a shader are inextricably linked. The input data for a shader needs the material. -// However, a shader may require more than just a material? - -// --- Common uniform blocks - -/* In glsl code we call it 'MVP_Matrices' */ -typedef struct mvp_matrix_uniforms { - mat4 model; - mat4 view; - mat4 projection; -} mvp_matrix_uniforms; - -// --- PBR (params) - -typedef struct pbr_params_material_uniforms { - vec3 albedo; - f32 metallic; - f32 roughness; - f32 ao; - f32 pad[2]; -} pbr_params_material_uniforms; - -typedef struct pbr_point_light { - vec3 pos; - f32 pad; - vec3 color; - f32 pad2; -} pbr_point_light; - -typedef struct pbr_params_light_uniforms { - pbr_point_light pointLights[4]; - vec4 viewPos; -} pbr_params_light_uniforms; - -typedef struct pbr_params_bindgroup { - mvp_matrix_uniforms mvp_matrices; - pbr_params_material_uniforms material; - pbr_params_light_uniforms lights; -} pbr_params_bindgroup; - -static shader_data_layout pbr_params_shader_layout(void* data) { - pbr_params_bindgroup* d = (pbr_params_bindgroup*)data; - bool has_data = data != NULL; - - shader_binding b1 = { .label = "MVP_Matrices", - .type = SHADER_BINDING_BYTES, - .stores_data = has_data, - .data = { .bytes = { .size = sizeof(mvp_matrix_uniforms) } } }; - - shader_binding b2 = { .label = "PBR_Params", - .type = SHADER_BINDING_BYTES, - .stores_data = has_data, - .data = { .bytes = { .size = sizeof(pbr_params_material_uniforms) } } }; - - shader_binding b3 = { .label = "Scene_Lights", - .type = SHADER_BINDING_BYTES, - .stores_data = has_data, - .data = { .bytes = { .size = sizeof(pbr_params_light_uniforms) } } }; - - if (has_data) { - // printf("Size %d \n", b3.data.bytes.size); - b1.data.bytes.data = &d->mvp_matrices; - b2.data.bytes.data = &d->material; - /* d->lights.viewPos = vec3(0, 1, 0); */ - b3.data.bytes.data = &d->lights; - // print_vec3(d->lights.viewPos); - } - - return (shader_data_layout){ .name = "pbr_params", .bindings = { b1, b2, b3 }, .bindings_count = 3 - - }; -} - -static void* shader_layout_get_binding(shader_data_layout* layout, u32 nth_binding) { - assert(nth_binding < layout->bindings_count); - return &layout->bindings[nth_binding].data; -} - -typedef struct pbr_textures { - texture_handle albedo_tex; - texture_handle metal_roughness_tex; - texture_handle ao_tex; - texture_handle normal_tex; -} pbr_textures; - -typedef struct pbr_textured_bindgroup { - mvp_matrix_uniforms mvp_matrices; - pbr_params_light_uniforms lights; - pbr_textures textures; -} pbr_textured_bindgroup; - -static shader_data_layout pbr_textured_shader_layout(void* data) { - pbr_textured_bindgroup* d = (pbr_textured_bindgroup*)data; - bool has_data = data != NULL; - - shader_binding b1 = { .label = "MVP_Matrices", - .type = SHADER_BINDING_BYTES, - .stores_data = has_data, - .data = { .bytes = { .size = sizeof(mvp_matrix_uniforms) } } }; - - shader_binding b2 = { .label = "Scene_Lights", - .type = SHADER_BINDING_BYTES, - .stores_data = has_data, - .data = { .bytes = { .size = sizeof(pbr_params_light_uniforms) } } }; - - shader_binding b3 = { .label = "albedoMap", - .type = SHADER_BINDING_TEXTURE, - .stores_data = has_data }; - shader_binding b4 = { .label = "metallicRoughnessMap", - .type = SHADER_BINDING_TEXTURE, - .stores_data = has_data }; - shader_binding b5 = { .label = "aoMap", .type = SHADER_BINDING_TEXTURE, .stores_data = has_data }; - shader_binding b6 = { .label = "normalMap", - .type = SHADER_BINDING_TEXTURE, - .stores_data = has_data }; - - if (has_data) { - b1.data.bytes.data = &d->mvp_matrices; - b2.data.bytes.data = &d->lights; - b3.data.texture.handle = d->textures.albedo_tex; - b4.data.texture.handle = d->textures.metal_roughness_tex; - b5.data.texture.handle = d->textures.ao_tex; - b6.data.texture.handle = d->textures.normal_tex; - } - - return (shader_data_layout){ .name = "pbr_params", - .bindings = { b1, b2, b3, b4, b5, b6 }, - .bindings_count = 6 }; -} diff --git a/src/render/immdraw.c b/src/render/immdraw.c new file mode 100644 index 0000000..c711b0c --- /dev/null +++ b/src/render/immdraw.c @@ -0,0 +1,28 @@ +#include "immdraw.h" +#include "log.h" +#include "primitives.h" +#include "ral_common.h" +#include "ral_impl.h" +#include "ral_types.h" +#include "render.h" +#include "shader_layouts.h" + +void Immdraw_Init(Immdraw_Storage* storage) { + INFO("Immediate drawing initialisation"); + // meshes + // Geometry sphere_geo = Geo_CreateUVsphere(1.0, 8, 8); + // storage->sphere = Mesh_Create(&sphere_geo, false); + + // pipeline / material + ShaderDataLayout camera_data = Binding_Camera_GetLayout(NULL); + GraphicsPipelineDesc pipeline_desc = { + .debug_name = "Immediate Draw Pipeline", + .data_layouts = { camera_data }, + .data_layouts_count = 1, + + }; + // storage->colour_pipeline = GPU_GraphicsPipeline_Create(pipeline_desc, + // GPU_GetDefaultRenderpass()); +} + +void Immdraw_Sphere(Transform tf, f32 size, Vec4 colour, bool wireframe) {}
\ No newline at end of file diff --git a/src/render/immdraw.h b/src/render/immdraw.h new file mode 100644 index 0000000..0d58375 --- /dev/null +++ b/src/render/immdraw.h @@ -0,0 +1,27 @@ +/** + * @brief Immediate-mode drawing APIs + */ + +#pragma once +#include "defines.h" +#include "maths_types.h" +#include "ral_impl.h" +#include "render_types.h" + +typedef struct Immdraw_Storage { + Mesh plane; + Mesh cube; + Mesh sphere; + GPU_Pipeline* colour_pipeline; +} Immdraw_Storage; + +// --- Public API + +PUB void Immdraw_Init(Immdraw_Storage* storage); +PUB void Immdraw_Shutdown(Immdraw_Storage* storage); + +// These functions cause a pipeline switch and so aren't optimised for performance +PUB void Immdraw_Plane(Transform tf, Vec4 colour, bool wireframe); +PUB void Immdraw_Cuboid(Transform tf, Vec4 colour, bool wireframe); +PUB void Immdraw_Sphere(Transform tf, f32 size, Vec4 colour, bool wireframe); +PUB void Immdraw_TransformGizmo(Transform tf, f32 size); diff --git a/src/render/immediate.c b/src/render/immediate.c deleted file mode 100644 index 63a62b8..0000000 --- a/src/render/immediate.c +++ /dev/null @@ -1,46 +0,0 @@ -#include "immediate.h" -#include "glad/glad.h" -#include "maths.h" -#include "primitives.h" -#include "ral_types.h" -#include "render.h" -#include "render_types.h" - -typedef struct immdraw_system { - // primitive meshes (get reused for each draw call) - mesh plane; - mesh cube; - mesh sphere; - // command lists - -} immdraw_system; - -bool immdraw_system_init(immdraw_system* state) { - geometry_data plane_geometry = geo_create_plane(f32x2(1, 1)); - state->plane = mesh_create(&plane_geometry, true); - - geometry_data cube_geometry = geo_create_cuboid(f32x3(1, 1, 1)); - state->cube = mesh_create(&cube_geometry, true); - - geometry_data sphere_geometry = geo_create_uvsphere(1.0, 48, 48); - state->sphere = mesh_create(&sphere_geometry, true); - - return true; -} - -void immdraw_plane(vec3 pos, quat rotation, f32 u_scale, f32 v_scale, vec4 colour) {} - -void immdraw_system_render(immdraw_system* state) {} - -// void imm_draw_sphere(vec3 pos, f32 radius, vec4 colour) { -// // Create the vertices -// geometry_data geometry = geo_create_uvsphere(radius, 16, 16); -// geo_set_vertex_colours(&geometry, colour); - -// // Upload to GPU -// mat4 model = mat4_translation(pos); - -// // Set pipeline - -// // Draw -// }
\ No newline at end of file diff --git a/src/render/pbr.c b/src/render/pbr.c new file mode 100644 index 0000000..1a7b4de --- /dev/null +++ b/src/render/pbr.c @@ -0,0 +1,216 @@ +#include "pbr.h" +#include "camera.h" +#include "core.h" +#include "file.h" +#include "log.h" +#include "maths.h" +#include "mem.h" +#include "ral_common.h" +#include "ral_impl.h" +#include "ral_types.h" +#include "render.h" +#include "render_types.h" +#include "shader_layouts.h" + +void PBR_Init(PBR_Storage* storage) { + INFO("PBR shaders init"); + storage->pbr_pass = PBR_RPassCreate(); + storage->pbr_pipeline = PBR_PipelineCreate(storage->pbr_pass); +} + +GPU_Renderpass* PBR_RPassCreate() { + GPU_RenderpassDesc desc = { .default_framebuffer = true }; + return GPU_Renderpass_Create(desc); +} + +GPU_Pipeline* PBR_PipelineCreate(GPU_Renderpass* rpass) { + arena scratch = arena_create(malloc(1024 * 1024), 1024 * 1024); + + const char* vert_path = "assets/shaders/pbr_textured.vert"; + const char* frag_path = "assets/shaders/pbr_textured.frag"; + // Str8 vert_path = str8("assets/shaders/pbr_textured.vert"); + // Str8 frag_path = str8("assets/shaders/pbr_textured.frag"); + // str8_opt vertex_shader = str8_from_file(&scratch, vert_path); + // str8_opt fragment_shader = str8_from_file(&scratch, frag_path); + // if (!vertex_shader.has_value || !fragment_shader.has_value) { + // ERROR_EXIT("Failed to load shaders from disk") + // } + char* vert_shader = string_from_file(vert_path); + char* frag_shader = string_from_file(frag_path); + + ShaderDataLayout camera_data = Binding_Camera_GetLayout(NULL); + ShaderDataLayout model_data = Binding_Model_GetLayout(NULL); + ShaderDataLayout material_data = PBRMaterial_GetLayout(NULL); + ShaderDataLayout lights_data = Binding_Lights_GetLayout(NULL); + + GraphicsPipelineDesc desc = { + .debug_name = "PBR Pipeline", + .vertex_desc = static_3d_vertex_description(), + .data_layouts = {camera_data,model_data,material_data, lights_data }, + .data_layouts_count = 4, + .vs = { .debug_name = "PBR (textured) Vertex Shader", + .filepath = str8(vert_path), + // .code = vertex_shader.contents + .code = vert_shader + }, + .fs = { .debug_name = "PBR (textured) Fragment Shader", + .filepath = str8(frag_path), + .code = frag_shader + // .code = fragment_shader.contents, + }, + .depth_test = true, + .wireframe = false, + }; + return GPU_GraphicsPipeline_Create(desc, rpass); +} + +void PBR_Execute(PBR_Storage* storage, Camera camera, TextureHandle shadowmap_tex, + RenderEnt* entities, size_t entity_count) { + // 1. set up our pipeline + // 2. upload constant data (camera, lights) + // 3. draw each entity + // - upload material data -> in the future we will sort & batch by material + // - upload model transform + // - emit draw call + + GPU_CmdEncoder* enc = GPU_GetDefaultEncoder(); + GPU_CmdEncoder_BeginRender(enc, storage->pbr_pass); + GPU_EncodeBindPipeline(enc, storage->pbr_pipeline); + + // Feed shader data + Mat4 view, proj; + u32x2 dimensions = GPU_Swapchain_GetDimensions(); + Camera_ViewProj(&camera, (f32)dimensions.x, (f32)dimensions.y, &view, &proj); + Binding_Camera camera_data = { .view = view, + .projection = proj, + .viewPos = vec4(camera.position.x, camera.position.y, + camera.position.z, 1.0) }; + GPU_EncodeBindShaderData(enc, 0, Binding_Camera_GetLayout(&camera_data)); + + Vec3 light_color = vec3(300.0, 300.0, 300.0); + Binding_Lights + lights_data = { .pointLights = { + // FIXME: add lights to our RenderScene structure. for now these are + // hardcoded + (pbr_point_light){ .pos = vec3(0.0, 6.0, 6.0), .color = light_color }, + (pbr_point_light){ .pos = vec3(-10, 10, 10), .color = light_color }, + (pbr_point_light){ .pos = vec3(10, -10, 10), .color = light_color }, + (pbr_point_light){ .pos = vec3(-10, -10, 10), .color = light_color }, + } }; + GPU_EncodeBindShaderData(enc, 3, Binding_Lights_GetLayout(&lights_data)); + + // TODO: Add shadowmap texture to uniforms + Mesh_pool* mesh_pool = Render_GetMeshPool(); + Material_pool* material_pool = Render_GetMaterialPool(); + + for (size_t ent_i = 0; ent_i < entity_count; ent_i++) { + RenderEnt renderable = entities[ent_i]; + Mesh* mesh = Mesh_pool_get(mesh_pool, renderable.mesh); + Material* mat = Material_pool_get(material_pool, renderable.material); + + // upload material data + PBRMaterialUniforms material_data = { .mat = *mat }; + GPU_EncodeBindShaderData(enc, 2, PBRMaterial_GetLayout(&material_data)); + + // upload model transform + Binding_Model model_data = { .model = renderable.affine }; + GPU_EncodeBindShaderData(enc, 1, Binding_Model_GetLayout(&model_data)); + + // set buffers + GPU_EncodeSetVertexBuffer(enc, mesh->vertex_buffer); + GPU_EncodeSetIndexBuffer(enc, mesh->index_buffer); + // draw + GPU_EncodeDrawIndexed(enc, mesh->geometry.index_count); + } + + GPU_CmdEncoder_EndRender(enc); +} + +void PBRMaterial_BindData(ShaderDataLayout* layout, const void* data) { + PBRMaterialUniforms* d = (PBRMaterialUniforms*)data; + CASSERT(data); + CASSERT(layout->binding_count == 5); + + TextureHandle white1x1 = Render_GetWhiteTexture(); + if (d->mat.albedo_map.raw != INVALID_TEX_HANDLE.raw) { + layout->bindings[0].data.texture.handle = d->mat.albedo_map; + } else { + layout->bindings[0].data.texture.handle = white1x1; + } + // TODO .. the rest +} + +ShaderDataLayout PBRMaterial_GetLayout(void* data) { + PBRMaterialUniforms* d = (PBRMaterialUniforms*)data; + bool has_data = data != NULL; + + ShaderBinding b1 = { + .label = "albedoMap", + .kind = BINDING_TEXTURE, + }; + ShaderBinding b2 = { + .label = "metallicRoughnessMap", + .kind = BINDING_TEXTURE, + }; + ShaderBinding b3 = { + .label = "aoMap", + .kind = BINDING_TEXTURE, + }; + ShaderBinding b4 = { + .label = "normalMap", + .kind = BINDING_TEXTURE, + }; + ShaderBinding b5 = { .label = "PBR_Params", + .kind = BINDING_BYTES, + .data.bytes.size = sizeof(PBR_Params) }; + + if (has_data) { + TextureHandle white1x1 = Render_GetWhiteTexture(); + if (d->mat.albedo_map.raw != INVALID_TEX_HANDLE.raw) { + b1.data.texture.handle = d->mat.albedo_map; + } else { + b1.data.texture.handle = white1x1; + } + + if (d->mat.metallic_roughness_map.raw != INVALID_TEX_HANDLE.raw) { + b2.data.texture.handle = d->mat.metallic_roughness_map; + } else { + b2.data.texture.handle = white1x1; + } + + if (d->mat.ambient_occlusion_map.raw != INVALID_TEX_HANDLE.raw) { + b3.data.texture.handle = d->mat.ambient_occlusion_map; + } else { + b3.data.texture.handle = white1x1; + } + + if (d->mat.normal_map.raw != INVALID_TEX_HANDLE.raw) { + b4.data.texture.handle = d->mat.normal_map; + } else { + b4.data.texture.handle = white1x1; + } + + arena* frame = Render_GetFrameArena(); + PBR_Params* params = arena_alloc(frame, sizeof(PBR_Params)); + params->albedo = d->mat.base_colour; + params->metallic = d->mat.metallic; + params->roughness = d->mat.roughness; + params->ambient_occlusion = d->mat.ambient_occlusion; + b5.data.bytes.data = params; + } + + return (ShaderDataLayout){ .bindings = { b1, b2, b3, b4, b5 }, .binding_count = 5 }; +} + +Material PBRMaterialDefault() { + return (Material){ .name = "Standard Material", + .kind = MAT_PBR, + .base_colour = vec3(1.0, 1.0, 1.0), + .metallic = 0.0, + .roughness = 0.5, + .ambient_occlusion = 0.0, + .albedo_map = INVALID_TEX_HANDLE, + .metallic_roughness_map = INVALID_TEX_HANDLE, + .normal_map = INVALID_TEX_HANDLE, + .ambient_occlusion_map = INVALID_TEX_HANDLE }; +} diff --git a/src/render/pbr.h b/src/render/pbr.h new file mode 100644 index 0000000..cbdc577 --- /dev/null +++ b/src/render/pbr.h @@ -0,0 +1,61 @@ +/** + * @file pbr.h + * @brief PBR render pass + */ + +#pragma once +#include "backend_opengl.h" +#include "camera.h" +#include "defines.h" +#include "maths_types.h" +#include "ral_types.h" +#include "render_types.h" + +// --- Public API +typedef struct PBR_Storage { + GPU_Renderpass* pbr_pass; + GPU_Pipeline* pbr_pipeline; + +} PBR_Storage; // Stores all necessary data and handles + +typedef struct PBRMaterialUniforms { + Material mat; +} PBRMaterialUniforms; + +PUB void PBR_Init(PBR_Storage* storage); + +// NOTE: For simplicity's sake we will render this pass directly to the default framebuffer +PUB void PBR_Run(PBR_Storage* storage + // light data + // camera + // geometry + // materials +); + +typedef struct PBR_Params { + Vec3 albedo; + f32 metallic; + f32 roughness; + f32 ambient_occlusion; +} PBR_Params; + +typedef struct PBR_Textures { + TextureHandle albedo_map; + TextureHandle normal_map; + bool metal_roughness_combined; + TextureHandle metallic_map; + TextureHandle roughness_map; + TextureHandle ao_map; +} PBR_Textures; + +PUB Material PBRMaterialDefault(); +PUB ShaderDataLayout PBRMaterial_GetLayout(void* data); + +// --- Internal + +GPU_Renderpass* PBR_RPassCreate(); + +GPU_Pipeline* PBR_PipelineCreate(GPU_Renderpass* rpass); + +void PBR_Execute(PBR_Storage* storage, Camera camera, TextureHandle shadowmap_tex, + RenderEnt* entities, size_t entity_count); diff --git a/src/render/ral.c b/src/render/ral.c deleted file mode 100644 index 9ca99ce..0000000 --- a/src/render/ral.c +++ /dev/null @@ -1,97 +0,0 @@ -#include "ral.h" -#include "file.h" -#include "log.h" -#include "mem.h" -#include "str.h" - -#if defined(CEL_REND_BACKEND_VULKAN) -#include "backend_vulkan.h" -#elif defined(CEL_REND_BACKEND_METAL) -#include "backend_metal.h" -#elif defined(CEL_REND_BACKEND_OPENGL) -#include "backend_opengl.h" -#endif - -size_t vertex_attrib_size(vertex_attrib_type attr) { - switch (attr) { - case ATTR_F32: - case ATTR_U32: - case ATTR_I32: - return 4; - case ATTR_F32x2: - case ATTR_U32x2: - case ATTR_I32x2: - return 8; - case ATTR_F32x3: - case ATTR_U32x3: - case ATTR_I32x3: - return 12; - case ATTR_F32x4: - case ATTR_U32x4: - case ATTR_I32x4: - return 16; - break; - } -} - -void vertex_desc_add(vertex_description* builder, const char* name, vertex_attrib_type type) { - u32 i = builder->attributes_count; - - size_t size = vertex_attrib_size(type); - builder->attributes[i] = type; - builder->stride += size; - builder->attr_names[i] = name; - - builder->attributes_count++; -} - -vertex_description static_3d_vertex_description() { - vertex_description builder = { .debug_label = "Standard static 3d vertex format" }; - vertex_desc_add(&builder, "inPosition", ATTR_F32x3); - vertex_desc_add(&builder, "inNormal", ATTR_F32x3); - vertex_desc_add(&builder, "inTexCoords", ATTR_F32x2); - builder.use_full_vertex_size = true; - return builder; -} - -void backend_pools_init(arena* a, gpu_backend_pools* backend_pools) { - pipeline_layout_pool pipeline_layout_pool = - pipeline_layout_pool_create(a, MAX_PIPELINES, sizeof(gpu_pipeline_layout)); - backend_pools->pipeline_layouts = pipeline_layout_pool; - pipeline_pool pipeline_pool = pipeline_pool_create(a, MAX_PIPELINES, sizeof(gpu_pipeline)); - backend_pools->pipelines = pipeline_pool; - renderpass_pool rpass_pool = renderpass_pool_create(a, MAX_RENDERPASSES, sizeof(gpu_renderpass)); - backend_pools->renderpasses = rpass_pool; - - // context.gpu_pools; -} - -void resource_pools_init(arena* a, struct resource_pools* res_pools) { - buffer_pool buf_pool = buffer_pool_create(a, MAX_BUFFERS, sizeof(gpu_buffer)); - res_pools->buffers = buf_pool; - texture_pool tex_pool = texture_pool_create(a, MAX_TEXTURES, sizeof(gpu_texture)); - res_pools->textures = tex_pool; - - // context.resource_pools = res_pools; -} - -void print_shader_binding(shader_binding b) { - printf("Binding name: %s type %s vis %d stores data %d\n", b.label, - shader_binding_type_name[b.type], b.vis, b.stores_data); -} - -shader_desc shader_quick_load(const char* filepath) { - arena a = arena_create(malloc(1024 * 1024), 1024 * 1024); - str8 path = str8_cstr_view(filepath); - str8_opt shader = str8_from_file(&a, path); - if (!shader.has_value) { - ERROR_EXIT("Failed to load shaders from disk"); - } - - return (shader_desc){ - .debug_name = filepath, - .code = shader.contents, - .filepath = path, - .is_spirv = true, - }; -} diff --git a/src/render/ral_types.h b/src/render/ral_types.h deleted file mode 100644 index 19f6ea3..0000000 --- a/src/render/ral_types.h +++ /dev/null @@ -1,102 +0,0 @@ -/** - * @file ral_types.h - * @author your name (you@domain.com) - * @brief Struct and enum definitions for RAL - * @version 0.1 - * @date 2024-04-27 - * - * @copyright Copyright (c) 2024 - * - */ -#pragma once - -#include "darray.h" -#include "defines.h" -#include "maths_types.h" - -#define MAX_VERTEX_ATTRIBUTES 16 - -/* #ifndef RENDERER_TYPED_HANDLES */ -CORE_DEFINE_HANDLE(buffer_handle); -CORE_DEFINE_HANDLE(texture_handle); -CORE_DEFINE_HANDLE(sampler_handle); -CORE_DEFINE_HANDLE(shader_handle); -CORE_DEFINE_HANDLE(pipeline_layout_handle); -CORE_DEFINE_HANDLE(pipeline_handle); -CORE_DEFINE_HANDLE(renderpass_handle); -#define ABSENT_MODEL_HANDLE 999999999 - -// --- Shaders & Bindings - -typedef enum shader_visibility { - VISIBILITY_VERTEX = 1 << 0, - VISIBILITY_FRAGMENT = 1 << 1, - VISIBILITY_COMPUTE = 1 << 2, -} shader_visibility; - -/** @brief Describes the kind of binding a `shader_binding` is for. This changes how we create - * backing data for it. */ -typedef enum shader_binding_type { - /** - * @brief Binds a buffer to a shader - * @note Vulkan: Becomes a Storage Buffer - */ - SHADER_BINDING_BUFFER, - SHADER_BINDING_BUFFER_ARRAY, - SHADER_BINDING_TEXTURE, - SHADER_BINDING_TEXTURE_ARRAY, - SHADER_BINDING_SAMPLER, - /** - * @brief Binds raw data to a shader - * @note Vulkan: Becomes a Uniform Buffer - */ - SHADER_BINDING_BYTES, - // TODO: Acceleration Structure - SHADER_BINDING_COUNT -} shader_binding_type; - -static const char* shader_binding_type_name[] = { "BUFFER", "BUFFER ARRAY", "TEXTURE", - "TEXTURE ARRAY", "SAMPLER", "BYTES", - "COUNT" }; - -// pub trait ShaderBindable: Clone + Copy { -// fn bind_to(&self, context: &mut PipelineContext, index: u32); -// } - -typedef struct shader_binding { - const char* label; - shader_binding_type type; - shader_visibility vis; - bool stores_data; /** @brief if this is true then the shader binding has references to live data, - if false then its just being used to describe a layout and .data - should be zeroed */ - union { - struct { - buffer_handle handle; - } buffer; - struct { - void* data; - size_t size; - } bytes; - struct { - texture_handle handle; - } texture; - } data; /** @brief can store any kind of data that we can bind to a shader / descriptor set */ -} shader_binding; - -void print_shader_binding(shader_binding b); - -/** @brief A list of bindings that describe what data a shader / pipeline expects - @note This roughly correlates to a descriptor set layout in Vulkan -*/ - -// ? How to tie together materials and shaders - -// Three registers -// 1. low level graphics api calls "ral" -// 2. higher level render calls -// 3. simplified immediate mode API - -// 3 - you don't need to know how the renderer works at all -// 2 - you need to know how the overall renderer is designed -// 1 - you need to understand graphics API specifics diff --git a/src/render/render.c b/src/render/render.c new file mode 100644 index 0000000..697a79e --- /dev/null +++ b/src/render/render.c @@ -0,0 +1,357 @@ +/** + * @brief + */ + +#include "render.h" +#include <assert.h> +#include <glfw3.h> +#include <stdio.h> +#include "camera.h" +#include "core.h" +#include "grid.h" +#include "immdraw.h" +#include "log.h" +#include "maths.h" +#include "maths_types.h" +#include "mem.h" +#include "pbr.h" +#include "ral_common.h" +#include "ral_impl.h" +#include "ral_types.h" +#include "render_types.h" +#include "shadows.h" +#include "terrain.h" + +#define STB_IMAGE_IMPLEMENTATION +#include <stb_image.h> + +#define FRAME_ARENA_SIZE MB(1) +#define POOL_SIZE_BYTES \ + MB(10) // we will reserve 10 megabytes up front to store resource, mesh, and material pools +#define MAX_MESHES 1024 +#define MAX_MATERIALS 256 + +extern Core g_core; + +struct Renderer { + struct GLFWwindow* window; + RendererConfig config; + GPU_Device device; + GPU_Swapchain swapchain; + GPU_Renderpass* default_renderpass; + bool frame_aborted; + RenderMode render_mode; + RenderScene scene; + PBR_Storage* pbr; + Shadow_Storage* shadows; + Terrain_Storage* terrain; + Grid_Storage* grid; + Immdraw_Storage* immediate; + // Text_Storage* text; + ResourcePools* resource_pools; + Mesh_pool mesh_pool; + Material_pool material_pool; + arena frame_arena; + TextureHandle white_1x1; +}; + +Renderer* get_renderer() { return g_core.renderer; } + +bool Renderer_Init(RendererConfig config, Renderer* ren, GLFWwindow** out_window, + GLFWwindow* optional_window) { + INFO("Renderer init"); + ren->render_mode = RENDER_MODE_DEFAULT; + + ren->frame_arena = arena_create(malloc(FRAME_ARENA_SIZE), FRAME_ARENA_SIZE); + + // init resource pools + DEBUG("Initialise GPU resource pools"); + arena pool_arena = arena_create(malloc(POOL_SIZE_BYTES), POOL_SIZE_BYTES); + ren->resource_pools = arena_alloc(&pool_arena, sizeof(struct ResourcePools)); + ResourcePools_Init(&pool_arena, ren->resource_pools); + ren->mesh_pool = Mesh_pool_create(&pool_arena, MAX_MESHES, sizeof(Mesh)); + ren->material_pool = Material_pool_create(&pool_arena, MAX_MATERIALS, sizeof(Material)); + + // GLFW window creation + GLFWwindow* window; + if (optional_window != NULL) { + INFO("GLFWwindow pointer was provided!!!! Skipping generic glfw init.."); + window = optional_window; + } else { + INFO("No GLFWwindow provided - creating one"); + // NOTE: all platforms use GLFW at the moment but thats subject to change + glfwInit(); + +#if defined(CEL_REND_BACKEND_OPENGL) + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); +#elif defined(CEL_REND_BACKEND_VULKAN) + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); +#endif + + window = glfwCreateWindow(config.scr_width, config.scr_height, config.window_name, NULL, NULL); + INFO("Window created"); + if (window == NULL) { + ERROR("Failed to create GLFW window\n"); + glfwTerminate(); + return false; + } + } + + // #if defined(CEL_REND_BACKEND_OPENGL) + // glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); + // glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); + // glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + // glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); + // #elif defined(CEL_REND_BACKEND_VULKAN) + // glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + // #endif + + ren->window = window; + *out_window = window; + + glfwMakeContextCurrent(ren->window); + + // FIXME + // DEBUG("Set up GLFW window callbacks"); + glfwSetWindowSizeCallback(window, Render_WindowSizeChanged); + + // set the RAL backend up + if (!GPU_Backend_Init(config.window_name, window, ren->resource_pools)) { + return false; + } + + GPU_Device_Create(&ren->device); + GPU_Swapchain_Create(&ren->swapchain); + + // set up default scene + Camera default_cam = + Camera_Create(vec3(0.0, 2.0, 4.0), vec3_normalise(vec3(0.0, -2.0, -4.0)), VEC3_Y, 45.0); + SetCamera(default_cam); + DirectionalLight default_light = { /* TODO */ }; + SetMainLight(default_light); + + // create our renderpasses + ren->shadows = malloc(sizeof(Shadow_Storage)); + Shadow_Init(ren->shadows, 1024, 1024); + + ren->pbr = calloc(1, sizeof(PBR_Storage)); + PBR_Init(ren->pbr); + + ren->terrain = calloc(1, sizeof(Terrain_Storage)); + Terrain_Init(ren->terrain); + + ren->grid = calloc(1, sizeof(Grid_Storage)); + Grid_Init(ren->grid); + + ren->immediate = calloc(1, sizeof(Immdraw_Storage)); + Immdraw_Init(ren->immediate); + + // load default textures + ren->white_1x1 = TextureLoadFromFile("assets/textures/white1x1.png"); + // TODO: black_1x1 + + return true; +} + +void Renderer_Shutdown(Renderer* ren) { + free(ren->shadows); + DEBUG("Freed Shadows storage"); + free(ren->pbr); + DEBUG("Freed PBR storage"); + free(ren->terrain); + DEBUG("Freed Terrain storage"); + arena_free_storage(&ren->frame_arena); + DEBUG("Freed frame allocator buffer"); +} +size_t Renderer_GetMemReqs() { return sizeof(Renderer); } + +void Render_WindowSizeChanged(GLFWwindow* window, i32 new_width, i32 new_height) { + (void)window; + INFO("Window size changed callback"); + // Renderer* ren = Core_GetRenderer(&g_core); + GPU_Swapchain_Resize(new_width, new_height); +} + +void Render_FrameBegin(Renderer* ren) { + arena_free_all(&ren->frame_arena); + ren->frame_aborted = false; + if (!GPU_Backend_BeginFrame()) { + ren->frame_aborted = true; + WARN("Frame aborted"); + return; + } +} +void Render_FrameEnd(Renderer* ren) { + if (ren->frame_aborted) { + return; + } + + GPU_CmdEncoder* enc = GPU_GetDefaultEncoder(); + + GPU_Backend_EndFrame(); +} +void Render_RenderEntities(RenderEnt* entities, size_t entity_count) { + Renderer* ren = get_renderer(); + RenderScene scene = ren->scene; + + // FUTURE: Depth pre-pass + + Shadow_Storage* shadow_storage = Render_GetShadowStorage(); + shadow_storage->enabled = false; + TextureHandle sun_shadowmap = + shadow_storage->enabled ? Shadow_GetShadowMapTexture(shadow_storage) : INVALID_TEX_HANDLE; + + PBR_Execute(ren->pbr, scene.camera, sun_shadowmap, entities, entity_count); +} + +TextureData TextureDataLoad(const char* path, bool invert_y) { + TRACE("Load texture %s", path); + + // load the file data + int width, height, num_channels; + stbi_set_flip_vertically_on_load(invert_y); + +#pragma GCC diagnostic ignored "-Wpointer-sign" + char* data = stbi_load(path, &width, &height, &num_channels, 0); + if (data) { + DEBUG("loaded texture: %s", path); + } else { + WARN("failed to load texture"); + } + + // printf("width: %d height: %d num channels: %d\n", width, height, num_channels); + + unsigned int channel_type; + GPU_TextureFormat format; + if (num_channels == 4) { + channel_type = GL_RGBA; + format = TEXTURE_FORMAT_8_8_8_8_RGBA_UNORM; + } else { + channel_type = GL_RGB; + format = TEXTURE_FORMAT_8_8_8_RGB_UNORM; + } + TextureDesc desc = { + .extents = { width, height }, + .format = format, + .num_channels = num_channels, + .tex_type = TEXTURE_TYPE_2D, + }; + + return (TextureData){ .description = desc, .image_data = data }; +} + +TextureHandle TextureLoadFromFile(const char* path) { + TextureData tex_data = TextureDataLoad(path, false); + TextureHandle h = GPU_TextureCreate(tex_data.description, true, tex_data.image_data); + return h; +} + +Mesh Mesh_Create(Geometry* geometry, bool free_on_upload) { + Mesh m = { 0 }; + + // Create and upload vertex buffer + size_t vert_bytes = geometry->vertices->len * sizeof(Vertex); + INFO("Creating vertex buffer with size %d (%d x %d)", vert_bytes, geometry->vertices->len, + sizeof(Vertex)); + m.vertex_buffer = + GPU_BufferCreate(vert_bytes, BUFFER_VERTEX, BUFFER_FLAG_GPU, geometry->vertices->data); + + // Create and upload index buffer + if (geometry->has_indices) { + size_t index_bytes = geometry->indices->len * sizeof(u32); + INFO("Creating index buffer with size %d (len: %d)", index_bytes, geometry->indices->len); + m.index_buffer = + GPU_BufferCreate(index_bytes, BUFFER_INDEX, BUFFER_FLAG_GPU, geometry->indices->data); + } + + m.is_uploaded = true; + m.geometry = *geometry; // clone geometry data and store on Mesh struct + if (free_on_upload) { + Geometry_Destroy(geometry); + } + return m; +} + +void Geometry_Destroy(Geometry* geometry) { + if (geometry->indices) { + u32_darray_free(geometry->indices); + } + if (geometry->vertices) { + Vertex_darray_free(geometry->vertices); + } +} +PUB MeshHandle Mesh_Insert(Mesh* mesh) { return Mesh_pool_insert(Render_GetMeshPool(), mesh); } +PUB MaterialHandle Material_Insert(Material* material) { + return Material_pool_insert(Render_GetMaterialPool(), material); +} +Mesh* Mesh_Get(MeshHandle handle) { + return Mesh_pool_get(Render_GetMeshPool(), handle); +} + +size_t ModelExtractRenderEnts(RenderEnt_darray* entities, ModelHandle model_handle, Mat4 affine, + RenderEntityFlags flags) { + Model* model = MODEL_GET(model_handle); + for (u32 i = 0; i < model->mesh_count; i++) { + Mesh* m = Mesh_pool_get(Render_GetMeshPool(), model->meshes[i]); + RenderEnt data = { .mesh = model->meshes[i], + .material = m->material, + .affine = affine, + // .bounding_box + .flags = flags }; + RenderEnt_darray_push(entities, data); + } + return model->mesh_count; // how many RenderEnts we pushed +} + +void SetCamera(Camera camera) { g_core.renderer->scene.camera = camera; } +void SetMainLight(DirectionalLight light) { g_core.renderer->scene.sun = light; } + +arena* GetRenderFrameArena(Renderer* r) { return &r->frame_arena; } + +RenderScene* Render_GetScene() { + Renderer* ren = Core_GetRenderer(&g_core); + return &ren->scene; +} + +Shadow_Storage* Render_GetShadowStorage() { + Renderer* ren = Core_GetRenderer(&g_core); + return ren->shadows; +} + +Terrain_Storage* Render_GetTerrainStorage() { + Renderer* ren = Core_GetRenderer(&g_core); + return ren->terrain; +} + +Grid_Storage* Render_GetGridStorage() { + Renderer* ren = Core_GetRenderer(&g_core); + return ren->grid; +} + +TextureHandle Render_GetWhiteTexture() { + Renderer* ren = Core_GetRenderer(&g_core); + return ren->white_1x1; +} + +/** @return an arena allocator that gets cleared at the beginning of every render frame */ +arena* Render_GetFrameArena() { + Renderer* ren = Core_GetRenderer(&g_core); + return &ren->frame_arena; +} + +Mesh_pool* Render_GetMeshPool() { + Renderer* ren = Core_GetRenderer(&g_core); + return &ren->mesh_pool; +} +Material_pool* Render_GetMaterialPool() { + Renderer* ren = Core_GetRenderer(&g_core); + return &ren->material_pool; +} + +void Render_SetRenderMode(RenderMode mode) { + Renderer* ren = Core_GetRenderer(&g_core); + ren->render_mode = mode; +} diff --git a/src/render/render.h b/src/render/render.h new file mode 100644 index 0000000..0aee51c --- /dev/null +++ b/src/render/render.h @@ -0,0 +1,146 @@ +/** + * @brief + */ + +#pragma once +#include "defines.h" +#include "grid.h" +#include "maths_types.h" +#include "ral_types.h" +#include "render_types.h" +#include "shadows.h" +#include "camera.h" + +typedef struct Renderer Renderer; +typedef struct GLFWwindow GLFWwindow; +typedef struct RendererConfig { + const char* window_name; + u32 scr_width, scr_height; + Vec3 clear_colour; +} RendererConfig; + +typedef struct RenderFlags { + bool wireframe; +} RenderFlags; + +typedef struct RenderCtx { + Mat4 view; + Mat4 projection; +} RenderCtx; + +/** @brief Holds globally bound data for rendering a scene. Typically held by the renderer. + * Whenever you call draw functions you can think of this as an implicit parameter. */ +typedef struct RenderScene { + Camera camera; + DirectionalLight sun; +} RenderScene; + +PUB void SetCamera(Camera camera); +PUB void SetMainLight(DirectionalLight light); + +// #define MESH_GET(h) (Mesh_pool_get(g_core.renderer->meshes, h)) +// #define MATERIAL_GET(h) (Material_pool_get(g_core.renderer->material, h)) + +// --- Lifecycle + +PUB bool Renderer_Init(RendererConfig config, Renderer* renderer, GLFWwindow** out_window, + GLFWwindow* optional_window); +PUB void Renderer_Shutdown(Renderer* renderer); +PUB size_t Renderer_GetMemReqs(); +void Render_WindowSizeChanged(GLFWwindow* window, i32 new_width, i32 new_height); + +// internal init functions +void DefaultPipelinesInit(Renderer* renderer); + +// NOTE: All of these functions grab the Renderer instance off the global Core +PUB void Render_FrameBegin(Renderer* renderer); +PUB void Render_FrameEnd(Renderer* renderer); + +/** @brief */ +PUB void Render_RenderEntities(RenderEnt* entities, size_t entity_count); + +// TODO: Render_FrameDraw(); - this will + +// --- Resources + +PUB TextureData TextureDataLoad(const char* path, bool invert_y); +PUB void TextureUpload(TextureHandle handle, size_t n_bytes, const void* data); +PUB TextureHandle TextureLoadFromFile(const char* path); +PUB ModelHandle ModelLoad(const char* debug_name, const char* filepath); + +// --- Rendering Data + +PUB Mesh Mesh_Create(Geometry* geometry, bool free_on_upload); +PUB void Mesh_Delete(Mesh* mesh); +Mesh* Mesh_Get(MeshHandle handle); +void Geometry_Destroy(Geometry* geometry); +MeshHandle Mesh_Insert(Mesh* mesh); +MaterialHandle Material_Insert(Material* material); + +/** @brief gets render entities from a model and pushes them into a dynamic array for rendering */ +size_t ModelExtractRenderEnts(RenderEnt_darray* entities, ModelHandle model_handle, Mat4 affine, RenderEntityFlags flags); + +// --- Drawing + +// NOTE: These functions use the globally bound camera in RenderScene +PUB void DrawMesh(Mesh* mesh, Material* material, Mat4 model); + +/** @brief the renderer does some internal bookkeeping for terrain so we use the terrain + stored on the Renderer rather than accept it as a parameter */ +PUB void Render_DrawTerrain(); + +// --- Getters (not in love with this but I'm finding keeping Renderer internals private to be okay) +arena* GetRenderFrameArena(Renderer* r); + +typedef struct RenderScene RenderScene; +typedef struct Shadow_Storage Shadow_Storage; +typedef struct Terrain_Storage Terrain_Storage; + +RenderScene* Render_GetScene(); +Shadow_Storage* Render_GetShadowStorage(); +Terrain_Storage* Render_GetTerrainStorage(); +Grid_Storage* Render_GetGridStorage(); +TextureHandle Render_GetWhiteTexture(); +arena* Render_GetFrameArena(); +Mesh_pool* Render_GetMeshPool(); +Material_pool* Render_GetMaterialPool(); + +// --- Setters +void Render_SetRenderMode(RenderMode mode); + +// ------------------------------------------------- + +// Frame lifecycle on CPU + +// 1. extract +// 2. culling +// 3. render +// 4. dispatch (combined with render for now) + +// typedef struct Cull_Result { +// u64 n_visible_objects; +// u64 n_culled_objects; +// u32* visible_ent_indices; // allocated on frame arena +// size_t index_count; +// } Cull_Result; + +// // everything that can be in the world, knows how to extract rendering data +// typedef void (*ExtractRenderData)(void* world_data); + +// typedef struct Renderer Renderer; + +// /** @brief Produces a smaller set of only those meshes visible in the camera frustum on the CPU */ +// Cull_Result Frame_Cull(Renderer* ren, RenderEnt* entities, size_t entity_count, Camera* camera); + +// Cull_Result Frame_Cull(Renderer* ren, RenderEnt* entities, size_t entity_count, Camera* camera) { +// // TODO: u32 chunk_count = Tpool_GetNumWorkers(); + +// arena* frame_arena = GetRenderFrameArena(ren); + +// Cull_Result result = { 0 }; +// result.visible_ent_indices = arena_alloc( +// frame_arena, sizeof(u32) * entity_count); // make space for if all ents are visible + +// assert((result.n_visible_objects + result.n_culled_objects == entity_count)); +// return result; +// } diff --git a/src/render/render_types.h b/src/render/render_types.h new file mode 100644 index 0000000..5fdca8a --- /dev/null +++ b/src/render/render_types.h @@ -0,0 +1,143 @@ +/** + * @file render_types.h + * @brief + */ + +#pragma once +#include "defines.h" +#include "maths_types.h" +#include "mem.h" +#include "ral_types.h" + +// --- Handles +CORE_DEFINE_HANDLE(ModelHandle); +CORE_DEFINE_HANDLE(MaterialHandle); +CORE_DEFINE_HANDLE(MeshHandle); +#define INVALID_MODEL_HANDLE ((ModelHandle){ .raw = 9999991 }) +#define INVALID_MATERIAL_HANDLE ((MaterialHandle){ .raw = 9999992 }) +#define INVALID_MESH_HANDLE ((MeshHandle){ .raw = 9999993 }) + +typedef enum RenderMode { + RENDER_MODE_DEFAULT, + RENDER_MODE_WIREFRAME, + RENDER_MODE_WIREFRAME_ON_LIT, + RENDER_MODE_COUNT +} RenderMode; + +typedef struct Geometry { + VertexFormat format; + Vertex_darray* vertices; + u32_darray* indices; + bool has_indices; + size_t index_count; +} Geometry; + +typedef struct u32_opt { + u32 value; + bool has_value; +} u32_opt; + +typedef struct Mesh { + BufferHandle vertex_buffer; + BufferHandle index_buffer; + Geometry geometry; // NULL means it has been freed CPU-side + // i32 material_index; // -1 => no material + MaterialHandle material; + bool is_uploaded; // has the data been uploaded to the GPU +} Mesh; +#ifndef TYPED_MESH_CONTAINERS +KITC_DECL_TYPED_ARRAY(Mesh) +TYPED_POOL(Mesh, Mesh) +#define TYPED_MESH_CONTAINERS +#endif + +typedef struct TextureData { + TextureDesc description; + void* image_data; +} TextureData; + +// --- Supported materials +typedef enum MaterialKind { + MAT_BLINN_PHONG, // NOTE: we're dropping support for this + MAT_PBR, // uses textures for PBR properties + MAT_PBR_PARAMS, // uses float values to represent a surface uniformly + MAT_COUNT +} MaterialKind; +static const char* material_kind_names[] = { "Blinn Phong", "PBR (Textures)", "PBR (Params)", + "Count (This should be an error)" }; + +/** + * @brief + * @note based on https://google.github.io/filament/Filament.html#materialsystem/standardmodel + */ +typedef struct Material { + char name[64]; + MaterialKind kind; // at the moment all materials are PBR materials + Vec3 base_colour; // linear RGB {0,0,0} to {1,1,1} + f32 metallic; + f32 roughness; + f32 ambient_occlusion; + TextureHandle albedo_map; + TextureHandle normal_map; + TextureHandle metallic_roughness_map; + TextureHandle ambient_occlusion_map; +} Material; + +#ifndef TYPED_MATERIAL_CONTAINERS +KITC_DECL_TYPED_ARRAY(Material) +TYPED_POOL(Material, Material) +#define TYPED_MATERIAL_CONTAINERS +#endif + +/** @brief Convenient wrapper around a number of meshes each with a material */ +typedef struct Model { + Str8 name; + MeshHandle* meshes; + size_t mesh_count; + MaterialHandle* materials; + size_t material_count; +} Model; +#ifndef TYPED_MODEL_ARRAY +KITC_DECL_TYPED_ARRAY(Model) +#define TYPED_MODEL_ARRAY +#endif + +// TODO: function to create a model from a single mesh (like when using primitives) + +// --- Lights +typedef struct PointLight { + Vec3 position; + f32 constant, linear, quadratic; + Vec3 ambient; + Vec3 diffuse; + Vec3 specular; +} PointLight; + +typedef struct DirectionalLight { + Vec3 direction; + Vec3 ambient; + Vec3 diffuse; + Vec3 specular; +} DirectionalLight; + +// --- + +typedef enum RenderEntityFlag { + REND_ENT_CASTS_SHADOWS = 1 << 0, + REND_ENT_VISIBLE = 1 << 1, +} RenderEntityFlag; +typedef u32 RenderEntityFlags; + +/** @brief A renderable 'thing' */ +typedef struct RenderEnt { + MeshHandle mesh; + MaterialHandle material; + Mat4 affine; // In the future this should be updated by the transform graph + Bbox_3D bounding_box; + RenderEntityFlags flags; +} RenderEnt; + +#ifndef TYPED_RENDERENT_ARRAY +KITC_DECL_TYPED_ARRAY(RenderEnt) +#define TYPED_RENDERENT_ARRAY +#endif
\ No newline at end of file diff --git a/src/render/renderpasses.c b/src/render/renderpasses.c deleted file mode 100644 index b93d487..0000000 --- a/src/render/renderpasses.c +++ /dev/null @@ -1,140 +0,0 @@ -/** - * @file renderpasses.c - * @author your name (you@domain.com) - * @brief - * @version 0.1 - * @date 2024-06-22 - * - * @copyright Copyright (c) 2024 - * - */ - -#include "renderpasses.h" -#include "file.h" -#include "log.h" -#include "maths_types.h" -#include "ral.h" -#include "ral_types.h" - -#define SHADOW_WIDTH 1000 -#define SHADOW_HEIGHT 1000 - -shader_data_layout debug_quad_layout(void* data) { - debug_quad_uniform* d = data; - bool has_data = data != NULL; - - shader_binding b1 = { .label = "depthMap", - .type = SHADER_BINDING_TEXTURE, - .stores_data = has_data }; - if (has_data) { - b1.data.texture.handle = d->depthMap; - } - return ( - shader_data_layout){ .name = "debug quad uniforms", .bindings = { b1 }, .bindings_count = 1 }; -} - -gpu_pipeline* debug_quad_pipeline_create() { - gpu_renderpass_desc rpass_desc = { .default_framebuffer = true }; - gpu_renderpass* rpass = gpu_renderpass_create(&rpass_desc); - shader_data shader_layout = { .data = NULL, .shader_data_get_layout = debug_quad_layout }; - struct graphics_pipeline_desc desc = { .debug_name = "Shadow maps debug quad", - .vertex_desc = static_3d_vertex_description(), - .data_layouts = { shader_layout }, - .data_layouts_count = 1, - .vs = shader_quick_load("assets/shaders/debug_quad.vert"), - .fs = shader_quick_load("assets/shaders/debug_quad.frag"), - .renderpass = rpass, - .wireframe = false }; - - return gpu_graphics_pipeline_create(desc); -} - -void ren_shadowmaps_init(ren_shadowmaps* storage) { - storage->rpass = shadowmaps_renderpass_create(); - storage->static_pipeline = shadowmaps_pipeline_create(storage->rpass); - storage->debug_quad = debug_quad_pipeline_create(); - storage->depth_tex = storage->rpass->description.depth_stencil; -} - -gpu_renderpass* shadowmaps_renderpass_create() { - // Create depthmap texture - u32x2 extents = u32x2(SHADOW_WIDTH, SHADOW_HEIGHT); - texture_desc depthmap_desc = { .extents = extents, - .format = CEL_TEXTURE_FORMAT_DEPTH_DEFAULT, - .tex_type = CEL_TEXTURE_TYPE_2D }; - texture_handle depthmap = gpu_texture_create(depthmap_desc, false, NULL); - - gpu_renderpass_desc shadows_desc = { .default_framebuffer = false, - .has_color_target = false, - .has_depth_stencil = true, - .depth_stencil = depthmap }; - return gpu_renderpass_create(&shadows_desc); -} - -// == shader bindings - -shader_data_layout model_uniform_layout(void* data) { - bool has_data = data != NULL; - - shader_binding b1 = { .label = "Model", - .type = SHADER_BINDING_BYTES, - .stores_data = has_data, - .data = { .bytes.size = sizeof(model_uniform) } }; - if (has_data) { - b1.data.bytes.data = data; - } - return (shader_data_layout){ .name = "model_uniform", .bindings = { b1 }, .bindings_count = 1 }; -} -shader_data_layout lightspace_uniform_layout(void* data) { - bool has_data = data != NULL; - - shader_binding b1 = { .label = "LightSpace", - .type = SHADER_BINDING_BYTES, - .stores_data = has_data, - .data = { .bytes.size = sizeof(lightspace_tf_uniform) } }; - if (has_data) { - b1.data.bytes.data = data; - } - return (shader_data_layout){ .name = "lightspace_tf_uniform", - .bindings = { b1 }, - .bindings_count = 1 }; -} - -// ================== - -gpu_pipeline* shadowmaps_pipeline_create(gpu_renderpass* rpass) { - arena scratch = arena_create(malloc(1024 * 1024), 1024 * 1024); - - str8 vert_path = str8lit("assets/shaders/shadows.vert"); - str8 frag_path = str8lit("assets/shaders/shadows.frag"); - str8_opt vertex_shader = str8_from_file(&scratch, vert_path); - str8_opt fragment_shader = str8_from_file(&scratch, frag_path); - if (!vertex_shader.has_value || !fragment_shader.has_value) { - ERROR_EXIT("Failed to load shaders from disk"); - } - - // We'll have two data layouts. 1. for the light-space transform, and 2. for the model matrix - shader_data model_uniform = { .data = NULL, .shader_data_get_layout = &model_uniform_layout }; - shader_data lightspace_uniform = { .data = NULL, - .shader_data_get_layout = &lightspace_uniform_layout }; - - struct graphics_pipeline_desc desc = { .debug_name = "Shadowmap drawing pipeline", - .vertex_desc = static_3d_vertex_description(), - .data_layouts = { model_uniform, lightspace_uniform }, - .data_layouts_count = 2, - .vs = { .debug_name = "Shadows Vert shader", - .filepath = vert_path, - .code = vertex_shader.contents, - .is_spirv = true }, - .fs = { .debug_name = "Shadows Frag shader", - .filepath = frag_path, - .code = fragment_shader.contents, - .is_spirv = true }, - .renderpass = rpass }; - - arena_free_storage(&scratch); - return gpu_graphics_pipeline_create(desc); -} - -void renderpass_shadowmap_execute(gpu_renderpass* pass, render_entity* entities, - size_t entity_count) {} diff --git a/src/render/renderpasses.h b/src/render/renderpasses.h deleted file mode 100644 index 1ceea6c..0000000 --- a/src/render/renderpasses.h +++ /dev/null @@ -1,56 +0,0 @@ -/** - * @file renderpasses.h - * @author your name (you@domain.com) - * @brief Built-in renderpasses to the engine - * @version 0.1 - * @date 2024-04-28 - * - * @copyright Copyright (c) 2024 - * - */ -#pragma once -#include "ral.h" -#include "ral_types.h" -#include "render_types.h" - -// Shadowmap pass -// Blinn-phong pass -// Unlit pass -// Debug visualisations pass - -// Don't need to pass in *anything*. -gpu_renderpass* renderpass_blinn_phong_create(); -void renderpass_blinn_phong_execute(gpu_renderpass* pass, render_entity* entities, - size_t entity_count); - -typedef struct ren_shadowmaps { - u32 width; - u32 height; - gpu_renderpass* rpass; - gpu_pipeline* static_pipeline; - gpu_pipeline* debug_quad; - texture_handle depth_tex; -} ren_shadowmaps; - -typedef struct model_uniform { - mat4 model; -} model_uniform; -typedef struct lightspace_tf_uniform { - mat4 lightSpaceMatrix; -} lightspace_tf_uniform; - -typedef struct debug_quad_uniform { - texture_handle depthMap; -} debug_quad_uniform; - -shader_data_layout model_uniform_layout(void* data); -shader_data_layout lightspace_uniform_layout(void* data); -shader_data_layout debug_quad_layout(void* data); - -void ren_shadowmaps_init(ren_shadowmaps* storage); - -gpu_renderpass* shadowmaps_renderpass_create(); -gpu_pipeline* shadowmaps_pipeline_create(gpu_renderpass* rpass); - -void renderpass_shadowmap_execute(gpu_renderpass* pass, render_entity* entities, - size_t entity_count); diff --git a/src/render/shader_layouts.h b/src/render/shader_layouts.h new file mode 100644 index 0000000..09cf129 --- /dev/null +++ b/src/render/shader_layouts.h @@ -0,0 +1,70 @@ +#pragma once +#include "maths_types.h" +#include "ral_types.h" + +/** @brief shader layout for camera matrices */ +typedef struct Binding_Camera { + Mat4 view; + Mat4 projection; + Vec4 viewPos; +} Binding_Camera; + +typedef struct Binding_Model { + Mat4 model; +} Binding_Model; + +/** @brief data that is handy to have in any shader */ +typedef struct Binding_Globals { +} Binding_Globals; + +typedef struct pbr_point_light { + Vec3 pos; + f32 pad; + Vec3 color; + f32 pad2; +} pbr_point_light; + +typedef struct Binding_Lights { + pbr_point_light pointLights[4]; +} Binding_Lights; + +static ShaderDataLayout Binding_Camera_GetLayout(void* data) { + Binding_Camera* d = data; + bool has_data = data != NULL; + + ShaderBinding b1 = { .label = "Camera", + .kind = BINDING_BYTES, + .data.bytes = { .size = sizeof(Binding_Camera) } }; + if (has_data) { + b1.data.bytes.data = d; + } + return (ShaderDataLayout){ .bindings = { b1 }, .binding_count = 1 }; +} + +static ShaderDataLayout Binding_Model_GetLayout(void* data) { + Binding_Model* d = data; + bool has_data = data != NULL; + + ShaderBinding b1 = { .label = "Model", + .kind = BINDING_BYTES, + .vis = VISIBILITY_VERTEX, + .data.bytes = { .size = sizeof(Binding_Model) } }; + if (has_data) { + b1.data.bytes.data = d; + } + return (ShaderDataLayout){ .bindings = { b1 }, .binding_count = 1 }; +} + +static ShaderDataLayout Binding_Lights_GetLayout(void* data) { + Binding_Lights* d = data; + bool has_data = data != NULL; + + ShaderBinding b1 = { .label = "Lights", + .kind = BINDING_BYTES, + .vis = VISIBILITY_FRAGMENT, + .data.bytes = { .size = sizeof(Binding_Lights) } }; + if (has_data) { + b1.data.bytes.data = d; + } + return (ShaderDataLayout){ .bindings = { b1 }, .binding_count = 1 }; +}
\ No newline at end of file diff --git a/src/render/shadows.c b/src/render/shadows.c new file mode 100644 index 0000000..18a5f7a --- /dev/null +++ b/src/render/shadows.c @@ -0,0 +1,211 @@ +#include "shadows.h" +#include <string.h> +#include "file.h" +#include "glad/glad.h" +#include "log.h" +#include "maths.h" +#include "maths_types.h" +#include "primitives.h" +#include "ral_common.h" +#include "ral_impl.h" +#include "ral_types.h" +#include "render.h" +#include "render_types.h" +#include "str.h" + +ShaderDataLayout ShadowUniforms_GetLayout(void* data) { + ShadowUniforms* d = (ShadowUniforms*)data; + bool has_data = data != NULL; + + ShaderBinding b1 = { + .label = "ShadowUniforms", + .kind = BINDING_BYTES, + .vis = VISIBILITY_VERTEX, + .data = { .bytes = { .size = sizeof(ShadowUniforms) } } + // TODO: split this into two bindings so we can update model matrix independently + }; + + if (has_data) { + b1.data.bytes.data = data; + } + + return (ShaderDataLayout){ .binding_count = 1, .bindings = { b1 } }; +} + +ShaderDataLayout ShadowDebugQuad_GetLayout(void* data) { + TextureHandle* handle = data; + bool has_data = data != NULL; + + ShaderBinding b1 = { + .label = "depthMap", + .kind = BINDING_TEXTURE, + .vis = VISIBILITY_FRAGMENT, + }; + + if (has_data) { + b1.data.texture.handle = *handle; + } + + return (ShaderDataLayout){ .binding_count = 1, .bindings = { b1 } }; +} + +void Shadow_Init(Shadow_Storage* storage, u32 shadowmap_width, u32 shadowmap_height) { + memset(storage, 0, sizeof(Shadow_Storage)); + arena scratch = arena_create(malloc(1024 * 1024), 1024 * 1024); + + TextureDesc depthmap_desc = { .extents = u32x2(shadowmap_width, shadowmap_height), + .format = TEXTURE_FORMAT_DEPTH_DEFAULT, + .tex_type = TEXTURE_TYPE_2D }; + DEBUG("Creating depth map texture for shadows"); + TextureHandle depthmap = GPU_TextureCreate(depthmap_desc, false, NULL); + storage->depth_texture = depthmap; + + // -- shadowmap drawing pass + GPU_RenderpassDesc rpass_desc = { .default_framebuffer = false, + .has_color_target = false, + .has_depth_stencil = true, + .depth_stencil = depthmap }; + + storage->shadowmap_pass = GPU_Renderpass_Create(rpass_desc); + + WARN("About to laod shaders"); + WARN("Shader paths: %s %s", "assets/shaders/shadows.vert", "assets/shaders/shadows.frag"); + Str8 vert_path = str8("assets/shaders/shadows.vert"); + Str8 frag_path = str8("assets/shaders/shadows.frag"); + str8_opt vertex_shader = str8_from_file(&scratch, vert_path); + str8_opt fragment_shader = str8_from_file(&scratch, frag_path); + if (!vertex_shader.has_value || !fragment_shader.has_value) { + ERROR_EXIT("Failed to load shaders from disk"); + } + + ShaderDataLayout uniforms = ShadowUniforms_GetLayout(NULL); + + GraphicsPipelineDesc pipeline_desc = { + .debug_name = "Shadows Pipeline", + .vertex_desc = static_3d_vertex_description(), + .data_layouts = { uniforms }, + .data_layouts_count = 1, + .vs = { .debug_name = "Shadows Vert shader", + .filepath = vert_path, + .code = vertex_shader.contents, + .is_spirv = false }, + .fs = { .debug_name = "Shadows Frag shader", + .filepath = frag_path, + .code = fragment_shader.contents, + .is_spirv = false }, + }; + storage->shadowmap_pipeline = GPU_GraphicsPipeline_Create(pipeline_desc, storage->shadowmap_pass); + + // -- debug quad pipeline + GPU_RenderpassDesc debug_pass_desc = { .default_framebuffer = true }; + storage->debugquad_pass = GPU_Renderpass_Create(debug_pass_desc); + + vert_path = str8("assets/shaders/debug_quad.vert"); + frag_path = str8("assets/shaders/debug_quad.frag"); + vertex_shader = str8_from_file(&scratch, vert_path); + fragment_shader = str8_from_file(&scratch, frag_path); + if (!vertex_shader.has_value || !fragment_shader.has_value) { + ERROR_EXIT("Failed to load shaders from disk"); + } + + ShaderDataLayout debugquad_uniforms = ShadowDebugQuad_GetLayout(NULL); + + GraphicsPipelineDesc debugquad_pipeline_desc = { + .debug_name = "Shadows debug quad Pipeline", + .vertex_desc = static_3d_vertex_description(), + .data_layouts = { debugquad_uniforms }, + .data_layouts_count = 1, + .vs = { .debug_name = "depth debug quad vert shader", + .filepath = vert_path, + .code = vertex_shader.contents, + .is_spirv = false }, + .fs = { .debug_name = "depth debug quad frag shader", + .filepath = frag_path, + .code = fragment_shader.contents, + .is_spirv = false }, + }; + storage->debugquad_pipeline = + GPU_GraphicsPipeline_Create(debugquad_pipeline_desc, storage->debugquad_pass); + + Geometry quad_geo = Geo_CreatePlane(f32x2(1, 1)); + // HACK: Swap vertices to make it face us + Vertex top0 = quad_geo.vertices->data[0]; + quad_geo.vertices->data[0] = quad_geo.vertices->data[2]; + quad_geo.vertices->data[2] = top0; + Vertex top1 = quad_geo.vertices->data[1]; + quad_geo.vertices->data[1] = quad_geo.vertices->data[3]; + quad_geo.vertices->data[3] = top1; + storage->quad = Mesh_Create(&quad_geo, false); + + arena_free_storage(&scratch); +} + +void Shadow_Run(RenderEnt* entities, size_t entity_count) { + Shadow_Storage* shadow_storage = Render_GetShadowStorage(); + + // calculations + RenderScene* render_scene = Render_GetScene(); + f32 near_plane = 1.0, far_plane = 10.0; + // -- Not sure about how we want to handle lights + Vec3 light_position = { 1, 4, -1 }; + // -- + Mat4 light_projection = mat4_orthographic(-10.0, 10.0, -10.0, 10.0, near_plane, far_plane); + Mat4 light_view = mat4_look_at(light_position, VEC3_ZERO, VEC3_Y); + Mat4 light_space_matrix = mat4_mult(light_view, light_projection); + + Shadow_ShadowmapExecute(shadow_storage, light_space_matrix, entities, entity_count); +} + +void Shadow_DrawDebugQuad() { + Shadow_Storage* shadow_storage = Render_GetShadowStorage(); + + GPU_CmdEncoder* enc = GPU_GetDefaultEncoder(); + GPU_CmdEncoder_BeginRender(enc, shadow_storage->debugquad_pass); + + GPU_EncodeBindPipeline(enc, shadow_storage->debugquad_pipeline); + ShaderDataLayout quad_data = ShadowDebugQuad_GetLayout(&shadow_storage->depth_texture); + GPU_EncodeBindShaderData(enc, 0, quad_data); + GPU_EncodeSetVertexBuffer(enc, shadow_storage->quad.vertex_buffer); + GPU_EncodeSetIndexBuffer(enc, shadow_storage->quad.index_buffer); + GPU_EncodeDrawIndexed(enc, shadow_storage->quad.geometry.indices->len); + + GPU_CmdEncoder_EndRender(enc); +} + +void Shadow_ShadowmapExecute(Shadow_Storage* storage, Mat4 light_space_transform, + RenderEnt* entities, size_t entity_count) { + GPU_CmdEncoder shadow_encoder = GPU_CmdEncoder_Create(); + + GPU_CmdEncoder_BeginRender(&shadow_encoder, storage->shadowmap_pass); + // DEBUG("Begin shadowmap renderpass"); + + // FIXME: shouldnt be gl specific + glClear(GL_DEPTH_BUFFER_BIT); + + GPU_EncodeBindPipeline(&shadow_encoder, storage->shadowmap_pipeline); + + ShadowUniforms uniforms = { + .light_space = light_space_transform, + .model = mat4_ident() // this will be overwritten for each Model + }; + ShaderDataLayout shader_data = ShadowUniforms_GetLayout(&uniforms); + + for (size_t ent_i = 0; ent_i < entity_count; ent_i++) { + RenderEnt renderable = entities[ent_i]; + if (renderable.flags && REND_ENT_CASTS_SHADOWS) { + // Model* model = MODEL_GET(renderable.model); + + uniforms.model = renderable.affine; // update the model transform + + Mesh* mesh = Mesh_pool_get(Render_GetMeshPool(), renderable.mesh); + GPU_EncodeBindShaderData(&shadow_encoder, 0, shader_data); + GPU_EncodeSetVertexBuffer(&shadow_encoder, mesh->vertex_buffer); + GPU_EncodeSetIndexBuffer(&shadow_encoder, mesh->index_buffer); + GPU_EncodeDrawIndexed(&shadow_encoder, mesh->geometry.indices->len); + } + } + + GPU_CmdEncoder_EndRender(&shadow_encoder); // end renderpass +} + +TextureHandle Shadow_GetShadowMapTexture(Shadow_Storage* storage) { return storage->depth_texture; } diff --git a/src/render/shadows.h b/src/render/shadows.h new file mode 100644 index 0000000..0482d10 --- /dev/null +++ b/src/render/shadows.h @@ -0,0 +1,48 @@ +/** + * @brief Functions for adding shadows to scene rendering. + */ + +#pragma once +#include "defines.h" +#include "ral_impl.h" +#include "ral_types.h" +#include "render_types.h" + +typedef struct Shadow_Storage { + bool enabled; + GPU_Renderpass* shadowmap_pass; + GPU_Pipeline* shadowmap_pipeline; + TextureHandle depth_texture; + bool debug_quad_enabled; + Mesh quad; + GPU_Renderpass* debugquad_pass; + GPU_Pipeline* debugquad_pipeline; + // TODO: Some statistics tracking +} Shadow_Storage; + +typedef struct ShadowUniforms { + Mat4 light_space; + Mat4 model; +} ShadowUniforms; + +typedef struct Camera Camera; +typedef struct Mat4 Mat4; + +// --- Public API +PUB void Shadow_Init(Shadow_Storage* storage, u32 shadowmap_width, u32 shadowmap_height); + +/** @brief Run shadow map generation for given entities, and store in a texture. + * @note Uses active directional light for now */ +PUB void Shadow_Run(RenderEnt* entities, size_t entity_count); + +PUB void Shadow_DrawDebugQuad(); + +/** @brief Get the shadow texture generated from shadowmap pass */ +PUB TextureHandle Shadow_GetShadowMapTexture(Shadow_Storage* storage); + +// --- Internal +GPU_Renderpass* Shadow_RPassCreate(); // Creates the render pass +GPU_Pipeline* Shadow_PipelineCreate(GPU_Renderpass* rpass); // Creates the pipeline +void Shadow_ShadowmapExecute(Shadow_Storage* storage, Mat4 light_space_transform, + RenderEnt* entities, size_t entity_count); +void Shadow_RenderDebugQuad(); diff --git a/src/render/skybox.c b/src/render/skybox.c new file mode 100644 index 0000000..cc5797f --- /dev/null +++ b/src/render/skybox.c @@ -0,0 +1,167 @@ +#include "skybox.h" +#include <assert.h> +#include "file.h" +#include "glad/glad.h" +#include "log.h" +#include "maths.h" +#include "primitives.h" +#include "ral_common.h" +#include "ral_impl.h" +#include "ral_types.h" +#include "render.h" +#include "render_types.h" +#include "shader_layouts.h" + +float skyboxVertices[] = { + // positions + -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, + 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, + + -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, + -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, + + 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, + + -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, + + -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, + + -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, + 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f +}; + +static const char* faces[6] = { "assets/demo/skybox/right.jpg", "assets/demo/skybox/left.jpg", + "assets/demo/skybox/top.jpg", "assets/demo/skybox/bottom.jpg", + "assets/demo/skybox/front.jpg", "assets/demo/skybox/back.jpg" }; + +Skybox Skybox_Create(const char** face_paths, int n) { + INFO("Creating a skybox"); + assert(n == 6); // ! we're only supporting a full cubemap for now + + // -- cube verts + Geometry geom = { .format = VERTEX_POS_ONLY, // doesnt matter + .has_indices = false, + .indices = NULL, + .vertices = Vertex_darray_new(36) }; + for (u32 i = 0; i < (36 * 3); i += 3) { + Vertex_darray_push( + geom.vertices, + (Vertex){ .pos_only = { .position = vec3(skyboxVertices[i], skyboxVertices[i + 1], + skyboxVertices[i + 2]) } }); + } + Mesh cube = Mesh_Create(&geom, false); + + // -- cubemap texture + TextureHandle handle; + GPU_Texture* tex = GPU_TextureAlloc(&handle); + glBindTexture(GL_TEXTURE_CUBE_MAP, tex->id); + + int width, height, nrChannels; + // unsigned char *data; + for (unsigned int i = 0; i < n; i++) { + TextureData data = TextureDataLoad( + face_paths[i], + false); // stbi_load(textures_faces[i].c_str(), &width, &height, &nrChannels, 0); + assert(data.description.format == TEXTURE_FORMAT_8_8_8_RGB_UNORM); + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, data.description.extents.x, + data.description.extents.y, 0, GL_RGB, GL_UNSIGNED_BYTE, data.image_data); + } + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + + // shader pipeline + GPU_RenderpassDesc rpass_desc = { + .default_framebuffer = true, + }; + GPU_Renderpass* pass = GPU_Renderpass_Create(rpass_desc); + + arena scratch = arena_create(malloc(1024 * 1024), 1024 * 1024); + + Str8 vert_path = str8("assets/shaders/skybox.vert"); + Str8 frag_path = str8("assets/shaders/skybox.frag"); + str8_opt vertex_shader = str8_from_file(&scratch, vert_path); + str8_opt fragment_shader = str8_from_file(&scratch, frag_path); + if (!vertex_shader.has_value || !fragment_shader.has_value) { + ERROR_EXIT("Failed to load shaders from disk") + } + + // VertexDescription pos_only = { .debug_label = "Position only verts" }; + // VertexDesc_AddAttr(&pos_only, "inPos", ATTR_F32x3); + // pos_only.use_full_vertex_size = true; + + ShaderDataLayout camera_data = Binding_Camera_GetLayout(NULL); + ShaderDataLayout shader_data = Skybox_GetLayout(NULL); + + VertexDescription builder = { .debug_label = "pos only" }; + VertexDesc_AddAttr(&builder, "inPosition", ATTR_F32x3); + builder.use_full_vertex_size = true; + + GraphicsPipelineDesc pipeline_desc = { + .debug_name = "Skybox pipeline", + .vertex_desc = builder, + .data_layouts = { shader_data, camera_data }, + .data_layouts_count = 2, + .vs = { .debug_name = "Skybox Vertex Shader", + .filepath = vert_path, + .code = vertex_shader.contents }, + .fs = { .debug_name = "Skybox Fragment Shader", + .filepath = frag_path, + .code = fragment_shader.contents }, + .wireframe = false, + .depth_test = true, + }; + + GPU_Pipeline* pipeline = GPU_GraphicsPipeline_Create(pipeline_desc, pass); + + return (Skybox){ .cube = cube, .texture = handle, .pipeline = pipeline }; +} + +Skybox Skybox_Default() { return Skybox_Create(faces, 6); } + +void Skybox_Draw(Skybox* skybox, Camera camera) { + GPU_CmdEncoder* enc = GPU_GetDefaultEncoder(); + glDepthFunc(GL_LEQUAL); + GPU_CmdEncoder_BeginRender(enc, skybox->pipeline->renderpass); + GPU_EncodeBindPipeline(enc, skybox->pipeline); + GPU_EncodeSetDefaults(enc); + + // Shader data + + Mat4 view, proj; + u32x2 dimensions = GPU_Swapchain_GetDimensions(); + Camera_ViewProj(&camera, dimensions.x, dimensions.y, &view, &proj); + Mat4 new = mat4_ident(); + new.data[0] = view.data[0]; + new.data[1] = view.data[1]; + new.data[2] = view.data[2]; + new.data[4] = view.data[4]; + new.data[5] = view.data[5]; + new.data[6] = view.data[6]; + new.data[8] = view.data[8]; + new.data[9] = view.data[9]; + new.data[10] = view.data[10]; + + Binding_Camera camera_data = { .view = new, + .projection = proj, + .viewPos = vec4(camera.position.x, camera.position.y, + camera.position.z, 1.0) }; + GPU_EncodeBindShaderData(enc, 0, Binding_Camera_GetLayout(&camera_data)); + + SkyboxUniforms uniforms = { .cubemap = skybox->texture }; + ShaderDataLayout skybox_data = Skybox_GetLayout(&uniforms); + GPU_EncodeBindShaderData(enc, 0, skybox_data); + + GPU_EncodeSetVertexBuffer(enc, skybox->cube.vertex_buffer); + GPU_EncodeSetIndexBuffer(enc, skybox->cube.index_buffer); + + GPU_EncodeDraw(enc, 36); + + GPU_CmdEncoder_EndRender(enc); + glDepthFunc(GL_LESS); +} diff --git a/src/render/skybox.h b/src/render/skybox.h new file mode 100644 index 0000000..c2ef3a2 --- /dev/null +++ b/src/render/skybox.h @@ -0,0 +1,41 @@ +/** + * @brief + */ + +#pragma once +#include "camera.h" +#include "defines.h" +#include "ral_impl.h" +#include "render_types.h" + +typedef struct Skybox { + Mesh cube; + TextureHandle texture; + GPU_Pipeline* pipeline; // "shader" +} Skybox; + +PUB Skybox Skybox_Create(const char** face_paths, int n); // should always pass n = 6 for now + +PUB void Skybox_Draw(Skybox* skybox, Camera camera); + +typedef struct SkyboxUniforms { + TextureHandle cubemap; +} SkyboxUniforms; + +static ShaderDataLayout Skybox_GetLayout(void* data) { + SkyboxUniforms* d = (SkyboxUniforms*)data; // cold cast + bool has_data = data != NULL; + + ShaderBinding b1 = { + .label = "cubeMap", + .vis = VISIBILITY_FRAGMENT, + .kind = BINDING_TEXTURE, + }; + + if (has_data) { + b1.data.texture.handle = d->cubemap; + } + return (ShaderDataLayout){ .bindings = { b1 }, .binding_count = 1 }; +} + +Skybox Skybox_Default();
\ No newline at end of file |