1#![allow(missing_docs)] use crate::renderer::resources::RendererResources;
26use crate::{
27 core::{
28 algebra::{Matrix4, Point3, Vector2, Vector3, Vector4},
29 arrayvec::ArrayVec,
30 color::{self, Color},
31 err_once,
32 log::Log,
33 math::{frustum::Frustum, Matrix4Ext, Rect},
34 pool::Handle,
35 sstorage::ImmutableString,
36 },
37 graph::SceneGraph,
38 graphics::{
39 error::FrameworkError,
40 framebuffer::{GpuFrameBuffer, ResourceBindGroup, ResourceBinding},
41 gpu_program::{
42 SamplerFallback, ShaderProperty, ShaderPropertyKind, ShaderResourceDefinition,
43 ShaderResourceKind,
44 },
45 gpu_texture::GpuTexture,
46 server::GraphicsServer,
47 uniform::{ByteStorage, StaticUniformBuffer, UniformBuffer},
48 ElementRange,
49 },
50 material::{self, shader::ShaderDefinition, Material, MaterialPropertyRef, MaterialResource},
51 renderer::{
52 cache::{
53 geometry::GeometryCache,
54 shader::ShaderCache,
55 texture::TextureCache,
56 uniform::{UniformBlockLocation, UniformMemoryAllocator},
57 DynamicSurfaceCache, TimeToLive,
58 },
59 observer::ObserverPosition,
60 RenderPassStatistics,
61 },
62 resource::texture::TextureResource,
63 scene::{
64 collider::BitMask,
65 graph::Graph,
66 light::{
67 directional::{CsmOptions, DirectionalLight},
68 point::PointLight,
69 spot::SpotLight,
70 BaseLight,
71 },
72 mesh::{
73 buffer::{TriangleBufferRefMut, VertexAttributeDescriptor, VertexBufferRefMut},
74 surface::SurfaceResource,
75 RenderPath,
76 },
77 node::{Node, NodeTrait, RdcControlFlow},
78 probe::ReflectionProbe,
79 },
80};
81use fxhash::{FxBuildHasher, FxHashMap, FxHasher};
82use fyrox_graph::SceneGraphNode;
83use fyrox_resource::manager::ResourceManager;
84use std::{
85 fmt::{Debug, Formatter},
86 hash::{Hash, Hasher},
87};
88
89pub struct RenderContext<'a> {
92 pub render_mask: BitMask,
95 pub elapsed_time: f32,
99 pub observer_position: &'a ObserverPosition,
100 pub frustum: Option<&'a Frustum>,
103 pub storage: &'a mut dyn RenderDataBundleStorageTrait,
106 pub graph: &'a Graph,
109 pub render_pass_name: &'a ImmutableString,
111 pub dynamic_surface_cache: &'a mut DynamicSurfaceCache,
112}
113
114impl RenderContext<'_> {
115 pub fn calculate_sorting_index(&self, global_position: Vector3<f32>) -> u64 {
119 const RANGE_CENTER: u64 = u64::MAX / 2;
120 const GRANULARITY: f32 = 1000.0;
121
122 let view_matrix = &self.observer_position.view_matrix;
123 let world_space_point = Point3::from(global_position);
124 let view_space_point = view_matrix.transform_point(&world_space_point);
125
126 RANGE_CENTER.saturating_add_signed((view_space_point.z * GRANULARITY) as i64)
127 }
128}
129
130#[allow(missing_docs)] pub struct BundleRenderContext<'a> {
132 pub texture_cache: &'a mut TextureCache,
133 pub render_pass_name: &'a ImmutableString,
134 pub frame_buffer: &'a GpuFrameBuffer,
135 pub viewport: Rect<i32>,
136 pub uniform_memory_allocator: &'a mut UniformMemoryAllocator,
137 pub resource_manager: &'a ResourceManager,
138
139 pub use_pom: bool,
141 pub light_position: &'a Vector3<f32>,
142 pub ambient_light: Color,
143 pub scene_depth: Option<&'a GpuTexture>,
146 pub renderer_resources: &'a RendererResources,
147}
148
149pub struct SurfaceInstanceData {
151 pub world_transform: Matrix4<f32>,
153 pub bone_matrices: Vec<Matrix4<f32>>,
155 pub blend_shapes_weights: Vec<f32>,
157 pub element_range: ElementRange,
160 pub node_handle: Handle<Node>,
162}
163
164impl Default for SurfaceInstanceData {
165 fn default() -> Self {
166 Self {
167 world_transform: Matrix4::identity(),
168 bone_matrices: Default::default(),
169 blend_shapes_weights: Default::default(),
170 element_range: Default::default(),
171 node_handle: Default::default(),
172 }
173 }
174}
175
176pub struct RenderDataBundle {
182 pub data: SurfaceResource,
184 pub time_to_live: TimeToLive,
187 pub instances: Vec<SurfaceInstanceData>,
189 pub material: MaterialResource,
191 pub render_path: RenderPath,
193 sort_index: u64,
196}
197
198impl Debug for RenderDataBundle {
199 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
200 write!(
201 f,
202 "Bundle {}: {} instances",
203 self.data.key(),
204 self.instances.len()
205 )
206 }
207}
208
209pub struct InstanceUniformData {
212 pub instance_block: UniformBlockLocation,
214 pub bone_matrices_block: Option<UniformBlockLocation>,
216}
217
218pub struct BundleUniformData {
221 pub material_property_group_blocks: Vec<(usize, UniformBlockLocation)>,
223 pub light_data_block: UniformBlockLocation,
225 pub instance_blocks: Vec<InstanceUniformData>,
227}
228
229pub struct GlobalUniformData {
230 pub camera_block: UniformBlockLocation,
232 pub lights_block: UniformBlockLocation,
234 pub graphics_settings_block: UniformBlockLocation,
236}
237
238pub fn write_with_material<T, C, G>(
239 shader_property_group: &[ShaderProperty],
240 material_property_group: &C,
241 getter: G,
242 buf: &mut UniformBuffer<T>,
243) where
244 T: ByteStorage,
245 G: for<'a> Fn(&'a C, &ImmutableString) -> Option<MaterialPropertyRef<'a>>,
246{
247 for shader_property in shader_property_group {
250 let material_property = getter(material_property_group, &shader_property.name);
251
252 macro_rules! push_value {
253 ($variant:ident, $shader_value:ident) => {
254 if let Some(property) = material_property {
255 if let MaterialPropertyRef::$variant(material_value) = property {
256 buf.push(material_value);
257 } else {
258 buf.push($shader_value);
259 Log::err(format!(
260 "Unable to use material property {} because of mismatching types.\
261 Expected {:?} got {:?}. Fallback to shader default value.",
262 shader_property.name, shader_property, property
263 ));
264 }
265 } else {
266 buf.push($shader_value);
267 }
268 };
269 }
270
271 macro_rules! push_slice {
272 ($variant:ident, $shader_value:ident, $max_size:ident) => {
273 if let Some(property) = material_property {
274 if let MaterialPropertyRef::$variant(material_value) = property {
275 buf.push_slice_with_max_size(material_value, *$max_size);
276 } else {
277 buf.push_slice_with_max_size($shader_value, *$max_size);
278 Log::err(format!(
279 "Unable to use material property {} because of mismatching types.\
280 Expected {:?} got {:?}. Fallback to shader default value.",
281 shader_property.name, shader_property, property
282 ))
283 }
284 } else {
285 buf.push_slice_with_max_size($shader_value, *$max_size);
286 }
287 };
288 }
289
290 use ShaderPropertyKind::*;
291 match &shader_property.kind {
292 Float { value } => push_value!(Float, value),
293 FloatArray { value, max_len } => push_slice!(FloatArray, value, max_len),
294 Int { value } => push_value!(Int, value),
295 IntArray { value, max_len } => push_slice!(IntArray, value, max_len),
296 UInt { value } => push_value!(UInt, value),
297 UIntArray { value, max_len } => push_slice!(UIntArray, value, max_len),
298 Vector2 { value } => push_value!(Vector2, value),
299 Vector2Array { value, max_len } => push_slice!(Vector2Array, value, max_len),
300 Vector3 { value } => push_value!(Vector3, value),
301 Vector3Array { value, max_len } => push_slice!(Vector3Array, value, max_len),
302 Vector4 { value: default } => push_value!(Vector4, default),
303 Vector4Array { value, max_len } => push_slice!(Vector4Array, value, max_len),
304 Matrix2 { value: default } => push_value!(Matrix2, default),
305 Matrix2Array { value, max_len } => push_slice!(Matrix2Array, value, max_len),
306 Matrix3 { value: default } => push_value!(Matrix3, default),
307 Matrix3Array { value, max_len } => push_slice!(Matrix3Array, value, max_len),
308 Matrix4 { value: default } => push_value!(Matrix4, default),
309 Matrix4Array { value, max_len } => push_slice!(Matrix4Array, value, max_len),
310 Bool { value } => push_value!(Bool, value),
311 Color { r, g, b, a } => {
312 let value = &color::Color::from_rgba(*r, *g, *b, *a);
313 push_value!(Color, value)
314 }
315 };
316 }
317}
318
319pub fn write_shader_values<T: ByteStorage>(
320 shader_property_group: &[ShaderProperty],
321 buf: &mut UniformBuffer<T>,
322) {
323 for property in shader_property_group {
324 use ShaderPropertyKind::*;
325 match &property.kind {
326 Float { value } => buf.push(value),
327 FloatArray { value, max_len } => buf.push_slice_with_max_size(value, *max_len),
328 Int { value } => buf.push(value),
329 IntArray { value, max_len } => buf.push_slice_with_max_size(value, *max_len),
330 UInt { value } => buf.push(value),
331 UIntArray { value, max_len } => buf.push_slice_with_max_size(value, *max_len),
332 Vector2 { value } => buf.push(value),
333 Vector2Array { value, max_len } => buf.push_slice_with_max_size(value, *max_len),
334 Vector3 { value } => buf.push(value),
335 Vector3Array { value, max_len } => buf.push_slice_with_max_size(value, *max_len),
336 Vector4 { value: default } => buf.push(default),
337 Vector4Array { value, max_len } => buf.push_slice_with_max_size(value, *max_len),
338 Matrix2 { value: default } => buf.push(default),
339 Matrix2Array { value, max_len } => buf.push_slice_with_max_size(value, *max_len),
340 Matrix3 { value: default } => buf.push(default),
341 Matrix3Array { value, max_len } => buf.push_slice_with_max_size(value, *max_len),
342 Matrix4 { value: default } => buf.push(default),
343 Matrix4Array { value, max_len } => buf.push_slice_with_max_size(value, *max_len),
344 Bool { value } => buf.push(value),
345 Color { r, g, b, a } => buf.push(&color::Color::from_rgba(*r, *g, *b, *a)),
346 };
347 }
348}
349
350pub fn make_texture_binding(
351 server: &dyn GraphicsServer,
352 material: &Material,
353 resource_definition: &ShaderResourceDefinition,
354 renderer_resources: &RendererResources,
355 fallback: SamplerFallback,
356 resource_manager: &ResourceManager,
357 texture_cache: &mut TextureCache,
358) -> ResourceBinding {
359 let fallback = renderer_resources.sampler_fallback(fallback);
360 let fallback = (fallback, &renderer_resources.linear_wrap_sampler);
361
362 let texture_sampler_pair =
363 if let Some(binding) = material.binding_ref(resource_definition.name.clone()) {
364 if let material::MaterialResourceBinding::Texture(binding) = binding {
365 binding
366 .value
367 .as_ref()
368 .and_then(|t| {
369 texture_cache
370 .get(server, resource_manager, t)
371 .map(|t| (&t.gpu_texture, &t.gpu_sampler))
372 })
373 .unwrap_or(fallback)
374 } else {
375 Log::err(format!(
376 "Unable to use texture binding {}, types mismatch! Expected \
377 {:?} got {:?}",
378 resource_definition.name, resource_definition.kind, binding
379 ));
380
381 fallback
382 }
383 } else {
384 fallback
385 };
386
387 ResourceBinding::texture(
388 texture_sampler_pair.0,
389 texture_sampler_pair.1,
390 resource_definition.binding,
391 )
392}
393
394impl RenderDataBundle {
395 pub fn write_uniforms(
397 &self,
398 view_projection_matrix: &Matrix4<f32>,
399 render_context: &mut BundleRenderContext,
400 ) -> Option<BundleUniformData> {
401 let mut material_state = self.material.state();
402 let material = material_state.data()?;
403
404 let mut material_property_group_blocks = Vec::new();
406 let shader_state = material.shader().state();
407 let shader = shader_state.data_ref()?;
408 for resource_definition in shader.definition.resources.iter() {
409 if resource_definition.is_built_in() {
411 continue;
412 }
413
414 let ShaderResourceKind::PropertyGroup(ref shader_property_group) =
415 resource_definition.kind
416 else {
417 continue;
418 };
419
420 let mut buf = StaticUniformBuffer::<16384>::new();
421
422 if let Some(material_property_group) =
423 material.property_group_ref(resource_definition.name.clone())
424 {
425 write_with_material(
426 shader_property_group,
427 material_property_group,
428 |c, n| c.property_ref(n.clone()).map(|p| p.as_ref()),
429 &mut buf,
430 );
431 } else {
432 write_shader_values(shader_property_group, &mut buf)
436 }
437
438 if buf.is_empty() {
439 continue;
442 }
443
444 material_property_group_blocks.push((
445 resource_definition.binding,
446 render_context.uniform_memory_allocator.allocate(buf),
447 ))
448 }
449
450 let light_data = StaticUniformBuffer::<256>::new()
451 .with(render_context.light_position)
452 .with(&render_context.ambient_light.as_frgba());
453 let light_data_block = render_context.uniform_memory_allocator.allocate(light_data);
454
455 let mut instance_blocks = Vec::with_capacity(self.instances.len());
457 for instance in self.instances.iter() {
458 let mut packed_blend_shape_weights =
459 [Vector4::<f32>::default(); ShaderDefinition::MAX_BLEND_SHAPE_WEIGHT_GROUPS];
460
461 for (i, blend_shape_weight) in instance.blend_shapes_weights.iter().enumerate() {
462 let n = i / 4;
463 let c = i % 4;
464 packed_blend_shape_weights[n][c] = *blend_shape_weight;
465 }
466
467 let instance_buffer = StaticUniformBuffer::<1024>::new()
468 .with(&instance.world_transform)
469 .with(&(view_projection_matrix * instance.world_transform))
470 .with(&(instance.blend_shapes_weights.len() as i32))
471 .with(&(!instance.bone_matrices.is_empty()))
472 .with_slice_with_max_size(
473 &packed_blend_shape_weights,
474 ShaderDefinition::MAX_BLEND_SHAPE_WEIGHT_GROUPS,
475 );
476
477 let mut instance_uniform_data = InstanceUniformData {
478 instance_block: render_context
479 .uniform_memory_allocator
480 .allocate(instance_buffer),
481 bone_matrices_block: None,
482 };
483
484 if !instance.bone_matrices.is_empty() {
485 const INIT: Matrix4<f32> = Matrix4::new(
486 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
487 );
488 let mut matrices = [INIT; ShaderDefinition::MAX_BONE_MATRICES];
489 const SIZE: usize = ShaderDefinition::MAX_BONE_MATRICES * size_of::<Matrix4<f32>>();
490 matrices[0..instance.bone_matrices.len()].copy_from_slice(&instance.bone_matrices);
491
492 let bone_matrices_block = render_context
493 .uniform_memory_allocator
494 .allocate(StaticUniformBuffer::<SIZE>::new().with(&matrices));
495 instance_uniform_data.bone_matrices_block = Some(bone_matrices_block);
496 }
497
498 instance_blocks.push(instance_uniform_data);
499 }
500
501 Some(BundleUniformData {
502 material_property_group_blocks,
503 light_data_block,
504 instance_blocks,
505 })
506 }
507
508 pub fn render_to_frame_buffer<F>(
510 &self,
511 server: &dyn GraphicsServer,
512 geometry_cache: &mut GeometryCache,
513 shader_cache: &mut ShaderCache,
514 instance_filter: &mut F,
515 render_context: &mut BundleRenderContext,
516 bundle_uniform_data: BundleUniformData,
517 global_uniform_data: &GlobalUniformData,
518 ) -> Result<RenderPassStatistics, FrameworkError>
519 where
520 F: FnMut(&SurfaceInstanceData) -> bool,
521 {
522 let mut stats = RenderPassStatistics::default();
523
524 let mut material_state = self.material.state();
525
526 let Some(material) = material_state.data() else {
527 err_once!(
528 self.data.key() as usize,
529 "Unable to use material {}, because it is in invalid state \
530 (failed to load or still loading)!",
531 material_state.kind()
532 );
533 return Ok(stats);
534 };
535
536 let geometry = match geometry_cache.get(server, &self.data, self.time_to_live) {
537 Ok(geometry) => geometry,
538 Err(err) => {
539 err_once!(
540 self.data.key() as usize,
541 "Unable to get geometry for rendering! Reason: {err:?}"
542 );
543 return Ok(stats);
544 }
545 };
546
547 let Some(shader_set) = shader_cache.get(server, material.shader()) else {
548 err_once!(
549 self.data.key() as usize,
550 "Unable to get a compiled shader set for material {:?}!",
551 material.shader().resource_uuid()
552 );
553 return Ok(stats);
554 };
555
556 let Some(render_pass) = shader_set
557 .render_passes
558 .get(render_context.render_pass_name)
559 else {
560 let shader_state = material.shader().state();
561 if let Some(shader_data) = shader_state.data_ref() {
562 if !shader_data
563 .definition
564 .disabled_passes
565 .iter()
566 .any(|pass_name| pass_name.as_str() == render_context.render_pass_name.as_str())
567 {
568 err_once!(
569 self.data.key() as usize,
570 "There's no render pass {} in {:?} shader! \
571 If it is not needed, add it to disabled passes.",
572 render_context.render_pass_name,
573 shader_data.definition.name,
574 );
575 }
576 }
577 return Ok(stats);
578 };
579
580 let mut material_bindings = ArrayVec::<ResourceBinding, 32>::new();
581 let shader_state = material.shader().state();
582 let shader = shader_state
583 .data_ref()
584 .ok_or_else(|| FrameworkError::Custom("Invalid shader!".to_string()))?;
585 for resource_definition in shader.definition.resources.iter() {
586 let name = resource_definition.name.as_str();
587
588 match name {
589 "fyrox_sceneDepth" => {
590 material_bindings.push(ResourceBinding::texture(
591 if let Some(scene_depth) = render_context.scene_depth.as_ref() {
592 scene_depth
593 } else {
594 &render_context.renderer_resources.black_dummy
595 },
596 &render_context.renderer_resources.nearest_clamp_sampler,
597 resource_definition.binding,
598 ));
599 }
600 "fyrox_cameraData" => {
601 material_bindings.push(
602 render_context.uniform_memory_allocator.block_to_binding(
603 global_uniform_data.camera_block,
604 resource_definition.binding,
605 ),
606 );
607 }
608 "fyrox_lightData" => {
609 material_bindings.push(
610 render_context.uniform_memory_allocator.block_to_binding(
611 bundle_uniform_data.light_data_block,
612 resource_definition.binding,
613 ),
614 );
615 }
616 "fyrox_graphicsSettings" => {
617 material_bindings.push(
618 render_context.uniform_memory_allocator.block_to_binding(
619 global_uniform_data.graphics_settings_block,
620 resource_definition.binding,
621 ),
622 );
623 }
624 "fyrox_lightsBlock" => {
625 material_bindings.push(
626 render_context.uniform_memory_allocator.block_to_binding(
627 global_uniform_data.lights_block,
628 resource_definition.binding,
629 ),
630 );
631 }
632 _ => match resource_definition.kind {
633 ShaderResourceKind::Texture { fallback, .. } => {
634 material_bindings.push(make_texture_binding(
635 server,
636 material,
637 resource_definition,
638 render_context.renderer_resources,
639 fallback,
640 render_context.resource_manager,
641 render_context.texture_cache,
642 ));
643 }
644 ShaderResourceKind::PropertyGroup(_) => {
645 if let Some((_, block_location)) = bundle_uniform_data
647 .material_property_group_blocks
648 .iter()
649 .find(|(binding, _)| *binding == resource_definition.binding)
650 {
651 material_bindings.push(
652 render_context
653 .uniform_memory_allocator
654 .block_to_binding(*block_location, resource_definition.binding),
655 );
656 }
657 }
658 },
659 }
660 }
661
662 for (instance, uniform_data) in self
663 .instances
664 .iter()
665 .zip(bundle_uniform_data.instance_blocks)
666 {
667 if !instance_filter(instance) {
668 continue;
669 }
670 let mut instance_bindings = ArrayVec::<ResourceBinding, 32>::new();
671
672 for resource_definition in shader.definition.resources.iter() {
673 let name = resource_definition.name.as_str();
674 match name {
675 "fyrox_instanceData" => {
676 instance_bindings.push(
677 render_context.uniform_memory_allocator.block_to_binding(
678 uniform_data.instance_block,
679 resource_definition.binding,
680 ),
681 );
682 }
683 "fyrox_boneMatrices" => {
684 match uniform_data.bone_matrices_block {
685 Some(block) => {
686 instance_bindings.push(
687 render_context
688 .uniform_memory_allocator
689 .block_to_binding(block, resource_definition.binding),
690 );
691 }
692 None => {
693 instance_bindings.push(ResourceBinding::Buffer {
696 buffer: render_context
697 .renderer_resources
698 .bone_matrices_stub_uniform_buffer
699 .clone(),
700 binding: resource_definition.binding,
701 data_usage: Default::default(),
702 });
703 }
704 }
705 }
706 _ => (),
707 };
708 }
709
710 stats += render_context.frame_buffer.draw(
711 geometry,
712 render_context.viewport,
713 &render_pass.program,
714 &render_pass.draw_params,
715 &[
716 ResourceBindGroup {
717 bindings: &material_bindings,
718 },
719 ResourceBindGroup {
720 bindings: &instance_bindings,
721 },
722 ],
723 instance.element_range,
724 )?;
725 }
726
727 Ok(stats)
728 }
729}
730
731pub trait RenderDataBundleStorageTrait {
733 fn push_triangles(
754 &mut self,
755 dynamic_surface_cache: &mut DynamicSurfaceCache,
756 layout: &[VertexAttributeDescriptor],
757 material: &MaterialResource,
758 render_path: RenderPath,
759 sort_index: u64,
760 node_handle: Handle<Node>,
761 func: &mut dyn FnMut(VertexBufferRefMut, TriangleBufferRefMut),
762 );
763
764 fn push(
769 &mut self,
770 data: &SurfaceResource,
771 material: &MaterialResource,
772 render_path: RenderPath,
773 sort_index: u64,
774 instance_data: SurfaceInstanceData,
775 );
776}
777
778pub enum LightSourceKind {
779 Spot {
780 full_cone_angle: f32,
781 hotspot_cone_angle: f32,
782 distance: f32,
783 shadow_bias: f32,
784 cookie_texture: Option<TextureResource>,
785 },
786 Point {
787 radius: f32,
788 shadow_bias: f32,
789 },
790 Directional {
791 csm_options: CsmOptions,
792 },
793 Unknown,
794}
795
796#[allow(missing_docs)] pub struct LightData<const N: usize = 16> {
798 pub count: usize,
799 pub color_radius: [Vector4<f32>; N],
800 pub position: [Vector3<f32>; N],
801 pub direction: [Vector3<f32>; N],
802 pub parameters: [Vector2<f32>; N],
803}
804
805impl<const N: usize> Default for LightData<N> {
806 fn default() -> Self {
807 Self {
808 count: 0,
809 color_radius: [Default::default(); N],
810 position: [Default::default(); N],
811 direction: [Default::default(); N],
812 parameters: [Default::default(); N],
813 }
814 }
815}
816
817pub struct LightSource {
818 pub handle: Handle<Node>,
819 pub global_transform: Matrix4<f32>,
820 pub kind: LightSourceKind,
821 pub position: Vector3<f32>,
822 pub up_vector: Vector3<f32>,
823 pub side_vector: Vector3<f32>,
824 pub look_vector: Vector3<f32>,
825 pub cast_shadows: bool,
826 pub local_scale: Vector3<f32>,
827 pub color: Color,
828 pub intensity: f32,
829 pub scatter_enabled: bool,
830 pub scatter: Vector3<f32>,
831}
832
833pub struct RenderDataBundleStorage {
836 bundle_map: FxHashMap<u64, usize>,
837 pub observer_position: ObserverPosition,
839 pub bundles: Vec<RenderDataBundle>,
841 pub light_sources: Vec<LightSource>,
842 pub environment_map: Option<TextureResource>,
843}
844
845pub struct RenderDataBundleStorageOptions {
846 pub collect_lights: bool,
847}
848
849impl Default for RenderDataBundleStorageOptions {
850 fn default() -> Self {
851 Self {
852 collect_lights: true,
853 }
854 }
855}
856
857impl RenderDataBundleStorage {
858 pub fn new_empty(observer_position: ObserverPosition) -> Self {
859 Self {
860 bundle_map: Default::default(),
861 observer_position,
862 bundles: Default::default(),
863 light_sources: Default::default(),
864 environment_map: None,
865 }
866 }
867
868 pub fn from_graph(
872 graph: &Graph,
873 render_mask: BitMask,
874 elapsed_time: f32,
875 observer_position: &ObserverPosition,
876 render_pass_name: ImmutableString,
877 options: RenderDataBundleStorageOptions,
878 dynamic_surface_cache: &mut DynamicSurfaceCache,
879 ) -> Self {
880 let capacity = graph.node_count() as usize;
882 let mut storage = Self {
883 bundle_map: FxHashMap::with_capacity_and_hasher(capacity, FxBuildHasher::default()),
884 observer_position: observer_position.clone(),
885 bundles: Vec::with_capacity(capacity),
886 light_sources: Default::default(),
887 environment_map: None,
888 };
889
890 let frustum = Frustum::from_view_projection_matrix(
891 observer_position.projection_matrix * observer_position.view_matrix,
892 )
893 .unwrap_or_default();
894
895 let mut lod_filter = vec![true; graph.capacity() as usize];
896 for (node_handle, node) in graph.pair_iter() {
897 if let Some(lod_group) = node.lod_group() {
898 for level in lod_group.levels.iter() {
899 for &object in level.objects.iter() {
900 if let Ok(object_ref) = graph.try_get_node(object) {
901 let distance = observer_position
902 .translation
903 .metric_distance(&object_ref.global_position());
904 let z_range = observer_position.z_far - observer_position.z_near;
905 let normalized_distance =
906 (distance - observer_position.z_near) / z_range;
907 let visible = normalized_distance >= level.begin()
908 && normalized_distance <= level.end();
909 lod_filter[object.index() as usize] = visible;
910 }
911 }
912 }
913 }
914
915 if let Some(reflection_probe) = node.component_ref::<ReflectionProbe>() {
916 if (reflection_probe as &dyn NodeTrait)
917 .world_bounding_box()
918 .is_contains_point(observer_position.translation)
919 {
920 storage.environment_map = Some(reflection_probe.render_target().clone());
921 }
922 }
923
924 if options.collect_lights {
925 if let Some(base_light) = node.component_ref::<BaseLight>() {
926 if frustum.is_intersects_aabb(&node.world_bounding_box())
927 && base_light.global_visibility()
928 && base_light.is_globally_enabled()
929 {
930 let kind = if let Some(spot_light) = node.cast::<SpotLight>() {
931 LightSourceKind::Spot {
932 full_cone_angle: spot_light.full_cone_angle(),
933 hotspot_cone_angle: spot_light.hotspot_cone_angle(),
934 distance: spot_light.distance(),
935 shadow_bias: spot_light.shadow_bias(),
936 cookie_texture: spot_light.cookie_texture(),
937 }
938 } else if let Some(point_light) = node.cast::<PointLight>() {
939 LightSourceKind::Point {
940 radius: point_light.radius(),
941 shadow_bias: point_light.shadow_bias(),
942 }
943 } else if let Some(directional_light) = node.cast::<DirectionalLight>() {
944 LightSourceKind::Directional {
945 csm_options: (*directional_light.csm_options).clone(),
946 }
947 } else {
948 LightSourceKind::Unknown
949 };
950
951 let source = LightSource {
952 handle: node_handle,
953 global_transform: base_light.global_transform(),
954 kind,
955 position: base_light.global_position(),
956 up_vector: base_light.up_vector(),
957 side_vector: base_light.side_vector(),
958 look_vector: base_light.look_vector(),
959 cast_shadows: base_light.cast_shadows(),
960 local_scale: **base_light.local_transform().scale(),
961 color: base_light.color(),
962 intensity: base_light.intensity(),
963 scatter_enabled: base_light.is_scatter_enabled(),
964 scatter: base_light.scatter(),
965 };
966
967 storage.light_sources.push(source);
968 }
969 }
970 }
971 }
972
973 let mut ctx = RenderContext {
974 render_mask,
975 elapsed_time,
976 observer_position,
977 frustum: Some(&frustum),
978 storage: &mut storage,
979 graph,
980 render_pass_name: &render_pass_name,
981 dynamic_surface_cache,
982 };
983
984 #[inline(always)]
985 fn iterate_recursive(
986 node_handle: Handle<Node>,
987 graph: &Graph,
988 lod_filter: &[bool],
989 ctx: &mut RenderContext,
990 ) {
991 if lod_filter[node_handle.index() as usize] {
992 let node = graph.node(node_handle);
993 if let RdcControlFlow::Continue = node.collect_render_data(ctx) {
994 for child in node.children() {
995 iterate_recursive(*child, graph, lod_filter, ctx);
996 }
997 }
998 }
999 }
1000
1001 iterate_recursive(graph.root(), graph, &lod_filter, &mut ctx);
1002
1003 storage.sort();
1004
1005 storage
1006 }
1007
1008 pub fn sort(&mut self) {
1010 self.bundles.sort_unstable_by_key(|b| b.sort_index);
1011 }
1012
1013 pub fn write_global_uniform_blocks(
1014 &self,
1015 render_context: &mut BundleRenderContext,
1016 ) -> GlobalUniformData {
1017 let mut light_data = LightData::<{ ShaderDefinition::MAX_LIGHTS }>::default();
1018
1019 for (i, light) in self
1020 .light_sources
1021 .iter()
1022 .enumerate()
1023 .take(ShaderDefinition::MAX_LIGHTS)
1024 {
1025 let color = light.color.as_frgb();
1026
1027 light_data.color_radius[i] = Vector4::new(color.x, color.y, color.z, 0.0);
1028 light_data.position[i] = light.position;
1029 light_data.direction[i] = light.up_vector;
1030
1031 match light.kind {
1032 LightSourceKind::Spot {
1033 full_cone_angle,
1034 hotspot_cone_angle,
1035 distance,
1036 ..
1037 } => {
1038 light_data.color_radius[i].w = distance;
1039 light_data.parameters[i].x = (hotspot_cone_angle * 0.5).cos();
1040 light_data.parameters[i].y = (full_cone_angle * 0.5).cos();
1041 }
1042 LightSourceKind::Point { radius, .. } => {
1043 light_data.color_radius[i].w = radius;
1044 light_data.parameters[i].x = std::f32::consts::PI.cos();
1045 light_data.parameters[i].y = std::f32::consts::PI.cos();
1046 }
1047 LightSourceKind::Directional { .. } => {
1048 light_data.color_radius[i].w = f32::INFINITY;
1049 light_data.parameters[i].x = std::f32::consts::PI.cos();
1050 light_data.parameters[i].y = std::f32::consts::PI.cos();
1051 }
1052 LightSourceKind::Unknown => {}
1053 }
1054
1055 light_data.count += 1;
1056 }
1057
1058 let lights_data = StaticUniformBuffer::<2048>::new()
1059 .with(&(light_data.count as i32))
1060 .with(&light_data.color_radius)
1061 .with(&light_data.parameters)
1062 .with(&light_data.position)
1063 .with(&light_data.direction);
1064 let lights_block = render_context
1065 .uniform_memory_allocator
1066 .allocate(lights_data);
1067
1068 let inv_view = self
1070 .observer_position
1071 .view_matrix
1072 .try_inverse()
1073 .unwrap_or_default();
1074 let view_projection =
1075 self.observer_position.projection_matrix * self.observer_position.view_matrix;
1076 let camera_up = inv_view.up();
1077 let camera_side = inv_view.side();
1078 let camera_uniforms = StaticUniformBuffer::<512>::new()
1079 .with(&view_projection)
1080 .with(&self.observer_position.translation)
1081 .with(&camera_up)
1082 .with(&camera_side)
1083 .with(&self.observer_position.z_near)
1084 .with(&self.observer_position.z_far)
1085 .with(&(self.observer_position.z_far - self.observer_position.z_near));
1086 let camera_block = render_context
1087 .uniform_memory_allocator
1088 .allocate(camera_uniforms);
1089
1090 let graphics_settings = StaticUniformBuffer::<256>::new().with(&render_context.use_pom);
1091 let graphics_settings_block = render_context
1092 .uniform_memory_allocator
1093 .allocate(graphics_settings);
1094
1095 GlobalUniformData {
1096 camera_block,
1097 lights_block,
1098 graphics_settings_block,
1099 }
1100 }
1101
1102 pub fn render_to_frame_buffer<BundleFilter, InstanceFilter>(
1104 &self,
1105 server: &dyn GraphicsServer,
1106 geometry_cache: &mut GeometryCache,
1107 shader_cache: &mut ShaderCache,
1108 mut bundle_filter: BundleFilter,
1109 mut instance_filter: InstanceFilter,
1110 mut render_context: BundleRenderContext,
1111 ) -> Result<RenderPassStatistics, FrameworkError>
1112 where
1113 BundleFilter: FnMut(&RenderDataBundle) -> bool,
1114 InstanceFilter: FnMut(&SurfaceInstanceData) -> bool,
1115 {
1116 let global_uniforms = self.write_global_uniform_blocks(&mut render_context);
1117
1118 let view_projection =
1119 self.observer_position.projection_matrix * self.observer_position.view_matrix;
1120 let mut bundle_uniform_data_set = Vec::with_capacity(self.bundles.len());
1121 for bundle in self.bundles.iter() {
1122 if !bundle_filter(bundle) {
1123 continue;
1124 }
1125 bundle_uniform_data_set
1126 .push(bundle.write_uniforms(&view_projection, &mut render_context));
1127 }
1128 render_context.uniform_memory_allocator.upload(server)?;
1129
1130 let mut stats = RenderPassStatistics::default();
1131 for (bundle, bundle_uniform_data) in self
1132 .bundles
1133 .iter()
1134 .filter(|bundle| bundle_filter(bundle))
1135 .zip(bundle_uniform_data_set)
1136 {
1137 if let Some(bundle_uniform_data) = bundle_uniform_data {
1138 stats += bundle.render_to_frame_buffer(
1139 server,
1140 geometry_cache,
1141 shader_cache,
1142 &mut instance_filter,
1143 &mut render_context,
1144 bundle_uniform_data,
1145 &global_uniforms,
1146 )?
1147 }
1148 }
1149 Ok(stats)
1150 }
1151}
1152
1153impl RenderDataBundleStorageTrait for RenderDataBundleStorage {
1154 fn push_triangles(
1175 &mut self,
1176 dynamic_surface_cache: &mut DynamicSurfaceCache,
1177 layout: &[VertexAttributeDescriptor],
1178 material: &MaterialResource,
1179 render_path: RenderPath,
1180 sort_index: u64,
1181 node_handle: Handle<Node>,
1182 func: &mut dyn FnMut(VertexBufferRefMut, TriangleBufferRefMut),
1183 ) {
1184 let mut hasher = FxHasher::default();
1185 hasher.write_u64(material.key());
1186 layout.hash(&mut hasher);
1187 hasher.write_u64(sort_index);
1188 hasher.write_u32(render_path as u32);
1189 let key = hasher.finish();
1190
1191 let bundle = if let Some(&bundle_index) = self.bundle_map.get(&key) {
1192 self.bundles.get_mut(bundle_index).unwrap()
1193 } else {
1194 self.bundle_map.insert(key, self.bundles.len());
1195 self.bundles.push(RenderDataBundle {
1196 data: dynamic_surface_cache.get_or_create(key, layout),
1197 sort_index,
1198 instances: vec![
1199 SurfaceInstanceData {
1201 node_handle,
1202 ..Default::default()
1203 },
1204 ],
1205 material: material.clone(),
1206 render_path,
1207 time_to_live: Default::default(),
1208 });
1209 self.bundles.last_mut().unwrap()
1210 };
1211
1212 let mut data = bundle.data.data_ref();
1213 let data = &mut *data;
1214
1215 let vertex_buffer = data.vertex_buffer.modify();
1216 let triangle_buffer = data.geometry_buffer.modify();
1217
1218 func(vertex_buffer, triangle_buffer);
1219 }
1220
1221 fn push(
1225 &mut self,
1226 data: &SurfaceResource,
1227 material: &MaterialResource,
1228 render_path: RenderPath,
1229 sort_index: u64,
1230 instance_data: SurfaceInstanceData,
1231 ) {
1232 let mut hasher = FxHasher::default();
1233 hasher.write_u64(material.key());
1234 hasher.write_u64(data.key());
1235 hasher.write_u32(render_path as u32);
1236 let key = hasher.finish();
1237
1238 let bundle = if let Some(&bundle_index) = self.bundle_map.get(&key) {
1239 self.bundles.get_mut(bundle_index).unwrap()
1240 } else {
1241 self.bundle_map.insert(key, self.bundles.len());
1242 self.bundles.push(RenderDataBundle {
1243 data: data.clone(),
1244 sort_index,
1245 instances: Default::default(),
1246 material: material.clone(),
1247 render_path,
1248 time_to_live: Default::default(),
1249 });
1250 self.bundles.last_mut().unwrap()
1251 };
1252
1253 bundle.instances.push(instance_data)
1254 }
1255}
1256
1257#[cfg(test)]
1258mod test {
1259 use crate::renderer::bundle::{RenderContext, RenderDataBundleStorage};
1260 use crate::renderer::observer::ObserverPosition;
1261 use fyrox_core::algebra::{Matrix4, Vector3};
1262
1263 #[test]
1265 fn test_calculate_sorting_index() {
1266 let observer_position = ObserverPosition {
1267 translation: Default::default(),
1268 z_near: 0.0,
1269 z_far: 0.0,
1270 view_matrix: Matrix4::identity(),
1271 projection_matrix: Matrix4::identity(),
1272 view_projection_matrix: Matrix4::identity(),
1273 };
1274
1275 let render_context = RenderContext {
1276 render_mask: Default::default(),
1277 elapsed_time: 0.0,
1278 observer_position: &observer_position.clone(),
1279 frustum: None,
1280 storage: &mut RenderDataBundleStorage::new_empty(observer_position),
1281 graph: &Default::default(),
1282 render_pass_name: &Default::default(),
1283 dynamic_surface_cache: &mut Default::default(),
1284 };
1285
1286 let center = u64::MAX / 2;
1287
1288 assert_eq!(
1289 render_context.calculate_sorting_index(Vector3::repeat(0.0)),
1290 center
1291 );
1292
1293 assert_eq!(
1294 render_context.calculate_sorting_index(Vector3::new(0.0, 0.0, 1.0)),
1295 center + 1000
1296 );
1297
1298 assert_eq!(
1299 render_context.calculate_sorting_index(Vector3::new(0.0, 0.0, 2.0)),
1300 center + 2000
1301 );
1302
1303 assert_eq!(
1304 render_context.calculate_sorting_index(Vector3::new(0.0, 0.0, -3.0)),
1305 center - 3000
1306 );
1307 }
1308}