khora_data/flow/
render.rs1use khora_core::{
24 asset::Material,
25 math::{Mat4, Vec3},
26 renderer::{
27 api::scene::{GpuMaterial, GpuMesh},
28 light::LightType,
29 },
30 Runtime,
31};
32
33use crate::ecs::{Camera, GlobalTransform, HandleComponent, Light, SemanticDomain, World};
34use crate::flow::{Flow, Selection};
35use crate::register_flow;
36use crate::render::{ExtractedLight, ExtractedMesh, ExtractedView, RenderWorld};
37use khora_core::ecs::entity::EntityId;
38use khora_core::interpolation::{SharedTransformInterpolation, TransformInterpolation};
39
40#[derive(Default)]
43pub struct RenderFlow;
44
45impl Flow for RenderFlow {
46 type View = RenderWorld;
47
48 const DOMAIN: SemanticDomain = SemanticDomain::Render;
49 const NAME: &'static str = "render";
50
51 fn cache_key(&self, world: &World, runtime: &Runtime) -> Option<u64> {
57 Some(crate::flow::combine_cache_key([
58 world.instance_id(),
59 world.domain_epoch(SemanticDomain::Render),
60 world.domain_epoch(SemanticDomain::Spatial),
61 crate::render::editor_override_fingerprint(runtime),
62 render_interpolation_alpha(runtime).to_bits() as u64,
67 ]))
68 }
69
70 fn project(&self, world: &World, _sel: &Selection, runtime: &Runtime) -> Self::View {
71 let mut rw = RenderWorld::new();
72 let alpha = render_interpolation_alpha(runtime);
75 let interp = runtime.resources.get::<SharedTransformInterpolation>();
78 let interp_guard = interp.as_ref().and_then(|shared| shared.read().ok());
79 extract_meshes(world, &mut rw, alpha, interp_guard.as_deref());
80 extract_lights(world, &mut rw);
81 extract_views(world, &mut rw);
82
83 if rw.views.is_empty() {
87 if let Some(view) = crate::render::primary_view(world, runtime) {
88 rw.views.push(view);
89 }
90 }
91 rw
92 }
93}
94
95register_flow!(RenderFlow);
96
97fn render_interpolation_alpha(runtime: &Runtime) -> f32 {
102 runtime
103 .resources
104 .get::<khora_core::time::SharedTime>()
105 .and_then(|shared| shared.read().ok().map(|t| t.interpolation_alpha))
106 .unwrap_or(0.0)
107}
108
109fn extract_meshes(
110 world: &World,
111 render_world: &mut RenderWorld,
112 alpha: f32,
113 interpolation: Option<&TransformInterpolation>,
114) {
115 let query = world.query::<(EntityId, &GlobalTransform, &HandleComponent<GpuMesh>)>();
116 for (entity_id, transform, gpu_mesh_handle) in query {
117 let material = world
121 .get::<HandleComponent<Box<dyn Material>>>(entity_id)
122 .map(|m| m.handle.clone());
123 let gpu_material = world
124 .get::<HandleComponent<GpuMaterial>>(entity_id)
125 .map(|h| h.handle.clone());
126
127 let transform = match interpolation.and_then(|store| store.previous(entity_id)) {
133 Some(prev) => prev.interpolate(&transform.0, alpha),
134 None => transform.0,
135 };
136
137 render_world.meshes.push(ExtractedMesh {
138 transform,
139 cpu_mesh_uuid: gpu_mesh_handle.uuid,
140 gpu_mesh: gpu_mesh_handle.handle.clone(),
141 material,
142 gpu_material,
143 });
144 }
145}
146
147fn extract_lights(world: &World, render_world: &mut RenderWorld) {
148 let light_query = world.query::<(&Light, &GlobalTransform)>();
149 for (light_comp, global_transform) in light_query {
150 if !light_comp.enabled {
151 continue;
152 }
153
154 let position = global_transform.0.translation();
155 let direction = match &light_comp.light_type {
156 LightType::Directional(dir_light) => {
157 global_transform.0.rotation() * dir_light.direction
158 }
159 LightType::Spot(spot_light) => global_transform.0.rotation() * spot_light.direction,
160 LightType::Point(_) => Vec3::ZERO,
161 };
162
163 render_world.lights.push(ExtractedLight {
164 light_type: light_comp.light_type,
165 position,
166 direction,
167 shadow_view_proj: Mat4::IDENTITY,
168 shadow_atlas_index: None,
169 });
170 }
171}
172
173fn extract_views(world: &World, render_world: &mut RenderWorld) {
174 let camera_query = world.query::<(&Camera, &GlobalTransform)>();
175 for (camera, global_transform) in camera_query {
176 if !camera.is_active {
177 continue;
178 }
179
180 let position = global_transform.0.translation();
181 let rotation = global_transform.0.rotation();
182
183 let rotation_matrix = Mat4::from_quat(rotation.inverse());
184 let translation_matrix = Mat4::from_translation(-position);
185 let view_matrix = rotation_matrix * translation_matrix;
186 let proj_matrix = camera.projection_matrix();
187 let view_proj = proj_matrix * view_matrix;
188
189 render_world.views.push(ExtractedView {
190 view_proj,
191 position,
192 });
193 }
194}