From fde02a1dc76c7756fd5b80965a357fbf0302005c Mon Sep 17 00:00:00 2001 From: Katajisto Date: Sun, 5 Oct 2025 21:49:09 +0300 Subject: [PATCH] work on refactoring the rendering pipeline --- game/game.jai | 24 ++++----- src/editor/level_editor.jai | 8 +-- src/main.jai | 31 ++++++------ src/{ => rendering}/arbtri.jai | 0 src/rendering/backend.jai | 51 +++++++++++++++++++ src/rendering/backend_sokol.jai | 84 +++++++++++++++++++++++++++++++ src/{ => rendering}/camera.jai | 0 src/rendering/core.jai | 17 +++++++ src/rendering/groundplane.jai | 24 --------- src/{ => rendering}/meshgen.jai | 0 src/{ => rendering}/pipelines.jai | 0 src/rendering/rendering.jai | 15 ++++++ src/rendering/sky.jai | 23 ++++++++- src/rendering/tasks.jai | 83 ++++++++++++++++++++++++++++++ src/trile.jai | 2 - 15 files changed, 300 insertions(+), 62 deletions(-) rename src/{ => rendering}/arbtri.jai (100%) create mode 100644 src/rendering/backend.jai create mode 100644 src/rendering/backend_sokol.jai rename src/{ => rendering}/camera.jai (100%) create mode 100644 src/rendering/core.jai rename src/{ => rendering}/meshgen.jai (100%) rename src/{ => rendering}/pipelines.jai (100%) create mode 100644 src/rendering/tasks.jai diff --git a/game/game.jai b/game/game.jai index eb4902f..92993d4 100644 --- a/game/game.jai +++ b/game/game.jai @@ -6,7 +6,7 @@ rotation : float = 0.0; cam : Camera = .{ far = 2000.0, near = 1.0, - target = .{0.0, 0.0, 0.0}, + target = .{0.0, 4.0, 0.0}, position = .{3.0, 5.0, 3.0} }; @@ -22,19 +22,19 @@ game_init :: () { world.ground[501][500] = .GRASS; } +game_ui :: () { + +} + game_tick :: () { + } game_draw :: () { - if !is_in_reflection_pass then update_image_from_ground(*world, *gPipelines.plane.bind.images[1]); - if is_in_reflection_pass { - cam.position.y *= -1; - cam.target.y *= -1; - } - draw_sky(*cam); - if !is_in_reflection_pass then draw_ground_plane(*cam, *world.conf); - if is_in_reflection_pass { - cam.position.y *= -1; - cam.target.y *= -1; - } + camtask := Rendering_Task_Set_Camera.{type = .SET_CAMERA, camera = cam}; + add_rendering_task(camtask); + skytask := Rendering_Task_Sky.{type = .SKY, worldConfig = *world.conf}; + add_rendering_task(skytask); + groundtask := Rendering_Task_Ground.{type = .GROUND, world = *world}; + add_rendering_task(groundtask); } diff --git a/src/editor/level_editor.jai b/src/editor/level_editor.jai index 5c07710..8296b42 100644 --- a/src/editor/level_editor.jai +++ b/src/editor/level_editor.jai @@ -270,13 +270,9 @@ handle_tool_click :: (x: int, y: int, z: int) { case .TRILES; if editor_current_trile != null then add_trile(editor_current_trile.name, cast(float)x, cast(float)y, cast(float)z); case .GROUND; - - - ray := get_mouse_ray(*get_level_editor_camera()); hit, point := ray_plane_collision_point(ray, 0, 100); - print("ground point: %\n"); - if hit { + if hit { world.ground[floor(point.y).(int) + 500][floor(point.x).(int) + 500] = groundType; } } @@ -384,9 +380,7 @@ draw_world_triles :: (cam: *Camera, world: *World) { draw_level_editor :: () { cam := get_level_editor_camera(); - if !is_in_reflection_pass then update_image_from_ground(*world, *gPipelines.plane.bind.images[1]); draw_sky(*get_level_editor_camera(), *world.conf); - if !is_in_reflection_pass then draw_ground_plane(*get_level_editor_camera(), *world.conf); draw_world_triles(*cam, *world); } diff --git a/src/main.jai b/src/main.jai index b41779b..9b7dd09 100644 --- a/src/main.jai +++ b/src/main.jai @@ -17,12 +17,9 @@ stbi :: #import "stb_image"; #load "input/hotkeys.jai"; #load "ui/ui.jai"; #load "editor/editor.jai"; -#load "pipelines.jai"; #load "time.jai"; -#load "arbtri.jai"; #load "events.jai"; #load "load.jai"; -#load "camera.jai"; #load "ray.jai"; #load "world.jai"; @@ -96,7 +93,6 @@ init :: () { } init_after_asset_pack :: () { - add_font_from_pack("./resources/DroidSerif-Regular.ttf"); ui_init_font_fields(*state.font_default); @@ -148,20 +144,23 @@ frame :: () { tick_ui(); - sg_begin_pass(*(sg_pass.{ action = state.pass_action_clear, attachments = gPipelines.plane.attachments})); - is_in_reflection_pass = true; - draw_editor(); + // This populates our render task queue. if !in_editor_view then game_draw(); - is_in_reflection_pass = false; - sg_end_pass(); - sg_begin_pass(*(sg_pass.{ action = state.pass_action_clear, swapchain = cast,force(sg_swapchain) sglue_swapchain() })); - draw_editor(); - if !in_editor_view then game_draw(); - ui_clear_mouse_occluders(); - ui_pass(); - sg_end_pass(); - sg_commit(); + render(); + // sg_begin_pass(*(sg_pass.{ action = state.pass_action_clear, attachments = gPipelines.plane.attachments})); + // is_in_reflection_pass = true; + // draw_editor(); + // is_in_reflection_pass = false; + // sg_end_pass(); + + // sg_begin_pass(*(sg_pass.{ action = state.pass_action_clear, swapchain = cast,force(sg_swapchain) sglue_swapchain() })); + // draw_editor(); + // if !in_editor_view then game_draw(); + // ui_clear_mouse_occluders(); + // ui_pass(); + // sg_end_pass(); + // sg_commit(); input_per_frame_event_and_flag_update(); #if MEM_DEBUG { diff --git a/src/arbtri.jai b/src/rendering/arbtri.jai similarity index 100% rename from src/arbtri.jai rename to src/rendering/arbtri.jai diff --git a/src/rendering/backend.jai b/src/rendering/backend.jai new file mode 100644 index 0000000..f1a6dfe --- /dev/null +++ b/src/rendering/backend.jai @@ -0,0 +1,51 @@ +#scope_export + +Render_Command_Buckets :: struct { + setup : [..]*Render_Command; + shadow : [..]*Render_Command; + reflection : [..]*Render_Command; + main : [..]*Render_Command; + ui : [..]*Render_Command; +} + +render_command_buckets : Render_Command_Buckets; + +Render_Command_Type :: enum { + INVALID; + DRAW_SKY; + SET_CAMERA; + DRAW_GROUND; + GENERATE_GROUND; +} + +Render_Command :: struct { + type: Render_Command_Type = .INVALID; +} + +Render_Command_Sky :: struct { + #as using c : Render_Command; + worldConfig : *World_Config; +} + +Render_Command_Draw_Ground :: struct { + #as using c : Render_Command; + worldConfig : *World_Config; +} + +Render_Command_Generate_Ground_Texture :: struct { + #as using c : Render_Command; + world : *World; +} + +Render_Command_Set_Camera :: struct { + #as using c : Render_Command; + camera: Camera; +} + +process_command_buckets :: () { + backend_process_command_buckets(); +} + +#scope_file +#load "backend_sokol.jai"; + diff --git a/src/rendering/backend_sokol.jai b/src/rendering/backend_sokol.jai new file mode 100644 index 0000000..debee8e --- /dev/null +++ b/src/rendering/backend_sokol.jai @@ -0,0 +1,84 @@ +camera: Camera; + +backend_handle_command :: (cmd: *Render_Command) { + if cmd.type == { + case .DRAW_SKY; + sky_command := cast(*Render_Command_Sky)cmd; + backend_draw_sky(sky_command.worldConfig); + case .SET_CAMERA; + camera_command := cast(*Render_Command_Set_Camera)cmd; + camera = camera_command.camera; + case .GENERATE_GROUND; + gen_command := cast(*Render_Command_Generate_Ground_Texture)cmd; + update_image_from_ground(gen_command.world, *gPipelines.plane.bind.images[1]); + case .DRAW_GROUND; + ground_command := cast(*Render_Command_Draw_Ground)cmd; + backend_draw_ground(ground_command.worldConfig); + + } +} + +backend_draw_sky :: (wc: *World_Config) { + mvp := create_viewproj(*camera); + vs_params : Sky_Vs_Params; + + world_conf : Sky_World_Config; + + world_config_to_shader_type(wc, *world_conf); + + vs_params.mvp = mvp.floats; + sg_apply_pipeline(gPipelines.sky.pipeline); + sg_apply_bindings(*gPipelines.sky.bind); + sg_apply_uniforms(UB_sky_vs_params, *(sg_range.{ ptr = *vs_params, size = size_of(type_of(vs_params)) })); + sg_apply_uniforms(UB_sky_world_config, *(sg_range.{ptr = *world_conf, size = size_of(type_of(world_conf))})); + sg_draw(0, 36, 1); +} + +backend_draw_ground :: (wc: *World_Config) { + mvp := create_viewproj(*camera); + vs_params : Plane_Vs_Params; + world_conf : Plane_World_Config; + plane_data : Plane_Data; + w, h := get_window_size(); + plane_data.screen_w = w; + plane_data.screen_h = h; + + world_config_to_shader_type(wc, *world_conf); + + vs_params.mvp = mvp.floats; + sg_apply_pipeline(gPipelines.plane.pipeline); + sg_apply_bindings(*gPipelines.plane.bind); + sg_apply_uniforms(UB_plane_vs_params, *(sg_range.{ ptr = *vs_params, size = size_of(type_of(vs_params)) })); + sg_apply_uniforms(UB_plane_world_config, *(sg_range.{ptr = *world_conf, size = size_of(type_of(world_conf))})); + sg_apply_uniforms(UB_plane_data, *(sg_range.{ptr = *plane_data, size = size_of(type_of(plane_data))})); + sg_draw(0, 6, 1); +} + +backend_process_command_buckets :: () { + // 1. Set up textures and buffers. + for render_command_buckets.setup { + backend_handle_command(it); + } + + // 2. Reflection pass + sg_begin_pass(*(sg_pass.{ action = state.pass_action_clear, attachments = gPipelines.plane.attachments})); + for render_command_buckets.reflection { + backend_handle_command(it); + } + sg_end_pass(); + + // 3. Main pass + sg_begin_pass(*(sg_pass.{ action = state.pass_action_clear, swapchain = cast,force(sg_swapchain) sglue_swapchain() })); + for render_command_buckets.main { + backend_handle_command(it); + } + sg_end_pass(); + + sg_commit(); + + array_reset_keeping_memory(*render_command_buckets.setup); + array_reset_keeping_memory(*render_command_buckets.shadow); + array_reset_keeping_memory(*render_command_buckets.reflection); + array_reset_keeping_memory(*render_command_buckets.main); + array_reset_keeping_memory(*render_command_buckets.ui); +} diff --git a/src/camera.jai b/src/rendering/camera.jai similarity index 100% rename from src/camera.jai rename to src/rendering/camera.jai diff --git a/src/rendering/core.jai b/src/rendering/core.jai new file mode 100644 index 0000000..7eebff2 --- /dev/null +++ b/src/rendering/core.jai @@ -0,0 +1,17 @@ +#scope_export + +#load "backend.jai"; + +// Core rendering function, runs several passes. +render :: () { + tasks_to_commands(); + process_command_buckets(); + // // @ToDo: Consider a depth pre-pass here? Research required. + // shadow_pass(); + // reflection_pass(); + // forward_pass(); + // // @ToDo: Post-processing. +} + +#scope_file + diff --git a/src/rendering/groundplane.jai b/src/rendering/groundplane.jai index 46b0b71..8b13789 100644 --- a/src/rendering/groundplane.jai +++ b/src/rendering/groundplane.jai @@ -1,25 +1 @@ -draw_ground_plane :: (cam: *Camera, worldConfig: *World_Config = null) { - mvp := create_viewproj(cam); - vs_params : Plane_Vs_Params; - world_conf : Plane_World_Config; - plane_data : Plane_Data; - - w, h := get_window_size(); - plane_data.screen_w = w; - plane_data.screen_h = h; - plane_data.is_reflection_pass = xx ifx is_in_reflection_pass then 1 else 0; - - wc : *World_Config = ifx worldConfig == null then *(World_Config.{}) else worldConfig; - - world_config_to_shader_type(wc, *world_conf); - - vs_params.mvp = mvp.floats; - sg_apply_pipeline(gPipelines.plane.pipeline); - sg_apply_bindings(*gPipelines.plane.bind); - sg_apply_uniforms(UB_plane_vs_params, *(sg_range.{ ptr = *vs_params, size = size_of(type_of(vs_params)) })); - sg_apply_uniforms(UB_plane_world_config, *(sg_range.{ptr = *world_conf, size = size_of(type_of(world_conf))})); - sg_apply_uniforms(UB_plane_data, *(sg_range.{ptr = *plane_data, size = size_of(type_of(plane_data))})); - - sg_draw(0, 6, 1); -} diff --git a/src/meshgen.jai b/src/rendering/meshgen.jai similarity index 100% rename from src/meshgen.jai rename to src/rendering/meshgen.jai diff --git a/src/pipelines.jai b/src/rendering/pipelines.jai similarity index 100% rename from src/pipelines.jai rename to src/rendering/pipelines.jai diff --git a/src/rendering/rendering.jai b/src/rendering/rendering.jai index 2614ec1..83d1b68 100644 --- a/src/rendering/rendering.jai +++ b/src/rendering/rendering.jai @@ -1,5 +1,20 @@ +/* + + The rendering pipeline. + + @ToDo: Write something smart about our rendering + architecture. + +*/ + #load "groundplane.jai"; +#load "tasks.jai"; #load "sky.jai"; +#load "core.jai"; +#load "camera.jai"; +#load "pipelines.jai"; +#load "meshgen.jai"; +#load "arbtri.jai"; #scope_file diff --git a/src/rendering/sky.jai b/src/rendering/sky.jai index dfd77c6..75a4003 100644 --- a/src/rendering/sky.jai +++ b/src/rendering/sky.jai @@ -13,5 +13,26 @@ draw_sky :: (cam: *Camera, worldConfig: *World_Config = null) { sg_apply_bindings(*gPipelines.sky.bind); sg_apply_uniforms(UB_sky_vs_params, *(sg_range.{ ptr = *vs_params, size = size_of(type_of(vs_params)) })); sg_apply_uniforms(UB_sky_world_config, *(sg_range.{ptr = *world_conf, size = size_of(type_of(world_conf))})); - sg_draw(0, 36, 1); + sg_draw(0, 36, 1); +} + +sun_shadowmap_viewproj :: (cam: *Camera, conf: *World_Config) -> Matrix4 { + up: Vector3 = .{0, 1, 0}; + targetToPos := conf.sunPosition; + A := normalize(targetToPos); + B := normalize(cross(up, A)); + C := cross(A, B); + + sunCameraPosition := cam.target + 20*A; + + view := Matrix4.{ + B.x, C.x, A.x, 0, + B.y, C.y, A.y, 0, + B.z, C.z, A.z, 0, + -dot(B, sunCameraPosition), -dot(C, sunCameraPosition), -dot(A, sunCameraPosition), 1 + }; + + top := 10.0; right := 10.0; + proj := orthographic_projection_matrix(-right, right, -top, top, 5, 100, true); + return view * proj; } diff --git a/src/rendering/tasks.jai b/src/rendering/tasks.jai new file mode 100644 index 0000000..8f4fd2d --- /dev/null +++ b/src/rendering/tasks.jai @@ -0,0 +1,83 @@ +#scope_export + +Rendering_Task_Type :: enum { + INVALID; + GROUND; + SKY; + SET_CAMERA; + TRILE; // not implemented yet + SPRITE; // not implemented yet + PARTICLES; // not implemented yet +}; + +Rendering_Task :: struct { + type : Rendering_Task_Type = .INVALID; +}; + +Rendering_Task_World :: struct { + #as using t : Rendering_Task; + world : *World; +} + +Rendering_Task_Sky :: struct { + #as using t : Rendering_Task; + t.type = .SKY; + worldConfig : *World_Config; +} + +Rendering_Task_Ground :: struct { + #as using t : Rendering_Task; + t.type = .GROUND; + world : *World; +} + +Rendering_Task_Set_Camera :: struct { + #as using t : Rendering_Task; + camera : Camera; +} + +rendering_tasklist_clear :: () { + array_reset_keeping_memory(*rendering_tasklist); +} + +add_rendering_task :: (task: $T) { + task_ptr := New(T,, temp); + task_ptr.* = task; + array_add(*rendering_tasklist, task_ptr); +} + +rendering_tasklist : [..]*Rendering_Task; + +tasks_to_commands :: () { + for rendering_tasklist { + if it.type == { + case .SKY; + command := New(Render_Command_Sky,, temp); + command.type = .DRAW_SKY; + command.worldConfig = (cast(*Rendering_Task_Sky)it).worldConfig; + array_add(*render_command_buckets.reflection, command); + array_add(*render_command_buckets.main, command); + case .GROUND; + commandDrawGround := New(Render_Command_Draw_Ground,, temp); + commandDrawGround.type = .DRAW_GROUND; + commandDrawGround.worldConfig = *(cast(*Rendering_Task_Ground)it).world.conf; + commandGenGround := New(Render_Command_Generate_Ground_Texture,, temp); + commandGenGround.type = .GENERATE_GROUND; + commandGenGround.world = (cast(*Rendering_Task_Ground)it).world; + array_add(*render_command_buckets.setup, commandGenGround); + array_add(*render_command_buckets.main, commandDrawGround); + case .SET_CAMERA; + command := New(Render_Command_Set_Camera,, temp); + command.type = .SET_CAMERA; + command.camera = (cast(*Rendering_Task_Set_Camera)it).camera; + array_add(*render_command_buckets.main, command); + commandReflected := New(Render_Command_Set_Camera,, temp); + commandReflected.type = .SET_CAMERA; + commandReflected.camera = (cast(*Rendering_Task_Set_Camera)it).camera; + commandReflected.camera.target *= Vector3.{1, -1, 1}; + commandReflected.camera.position *= Vector3.{1, -1, 1}; + array_add(*render_command_buckets.reflection, commandReflected); + } + } + rendering_tasklist_clear(); +} diff --git a/src/trile.jai b/src/trile.jai index de35b2a..c0c529f 100644 --- a/src/trile.jai +++ b/src/trile.jai @@ -1,5 +1,3 @@ -#load "meshgen.jai"; - #scope_file trile_gfx_table : Table(string, Trile_GFX);