Skip to main content

bevy_pbr/transmission/
phase.rs

1use core::ops::Range;
2
3use bevy_camera::{Camera, Camera3d};
4use bevy_core_pipeline::core_3d::TransparentSortingInfo3d;
5use bevy_ecs::{
6    entity::{Entity, EntityHash},
7    query::With,
8    system::{Local, Query, ResMut},
9};
10use bevy_material::{descriptor::CachedRenderPipelineId, labels::DrawFunctionId};
11use bevy_math::FloatOrd;
12use bevy_platform::collections::HashSet;
13use bevy_render::{
14    render_phase::{
15        CachedRenderPipelinePhaseItem, PhaseItem, PhaseItemExtraIndex, SortedPhaseItem,
16        ViewSortedRenderPhases,
17    },
18    sync_world::MainEntity,
19    view::{ExtractedView, RetainedViewEntity},
20    Extract,
21};
22use indexmap::IndexMap;
23
24pub struct Transmissive3d {
25    pub sorting_info: TransparentSortingInfo3d,
26    pub distance: f32,
27    pub pipeline: CachedRenderPipelineId,
28    pub entity: (Entity, MainEntity),
29    pub draw_function: DrawFunctionId,
30    pub batch_range: Range<u32>,
31    pub extra_index: PhaseItemExtraIndex,
32    /// Whether the mesh in question is indexed (uses an index buffer in
33    /// addition to its vertex buffer).
34    pub indexed: bool,
35}
36
37impl PhaseItem for Transmissive3d {
38    /// For now, automatic batching is disabled for transmissive items because their rendering is
39    /// split into multiple steps depending on [`crate::ScreenSpaceTransmission::steps`],
40    /// which the batching system doesn't currently know about.
41    ///
42    /// Having batching enabled would cause the same item to be drawn multiple times across different
43    /// steps, whenever the batching range crossed a step boundary.
44    ///
45    /// Eventually, we could add support for this by having the batching system break up the batch ranges
46    /// using the same logic as the transmissive pass, but for now it's simpler to just disable batching.
47    const AUTOMATIC_BATCHING: bool = false;
48
49    #[inline]
50    fn entity(&self) -> Entity {
51        self.entity.0
52    }
53
54    #[inline]
55    fn main_entity(&self) -> MainEntity {
56        self.entity.1
57    }
58
59    #[inline]
60    fn draw_function(&self) -> DrawFunctionId {
61        self.draw_function
62    }
63
64    #[inline]
65    fn batch_range(&self) -> &Range<u32> {
66        &self.batch_range
67    }
68
69    #[inline]
70    fn batch_range_mut(&mut self) -> &mut Range<u32> {
71        &mut self.batch_range
72    }
73
74    #[inline]
75    fn extra_index(&self) -> PhaseItemExtraIndex {
76        self.extra_index.clone()
77    }
78
79    #[inline]
80    fn batch_range_and_extra_index_mut(&mut self) -> (&mut Range<u32>, &mut PhaseItemExtraIndex) {
81        (&mut self.batch_range, &mut self.extra_index)
82    }
83}
84
85impl SortedPhaseItem for Transmissive3d {
86    // NOTE: Values increase towards the camera. Back-to-front ordering for transmissive means we need an ascending sort.
87    type SortKey = FloatOrd;
88
89    #[inline]
90    fn sort_key(&self) -> Self::SortKey {
91        FloatOrd(self.distance)
92    }
93
94    #[inline]
95    fn sort(items: &mut IndexMap<(Entity, MainEntity), Transmissive3d, EntityHash>) {
96        items.sort_by_key(|_, item| item.sort_key());
97    }
98
99    fn recalculate_sort_keys(
100        items: &mut IndexMap<(Entity, MainEntity), Self, EntityHash>,
101        view: &ExtractedView,
102    ) {
103        // Determine the distance to the view for each phase item.
104        let rangefinder = view.rangefinder3d();
105        for item in items.values_mut() {
106            item.distance = item.sorting_info.sort_distance(&rangefinder);
107        }
108    }
109
110    #[inline]
111    fn indexed(&self) -> bool {
112        self.indexed
113    }
114}
115
116impl CachedRenderPipelinePhaseItem for Transmissive3d {
117    #[inline]
118    fn cached_pipeline(&self) -> CachedRenderPipelineId {
119        self.pipeline
120    }
121}
122
123pub fn extract_transmissive_camera_phases(
124    mut transmissive_3d_phases: ResMut<ViewSortedRenderPhases<Transmissive3d>>,
125    cameras: Extract<Query<(Entity, &Camera), With<Camera3d>>>,
126    mut live_entities: Local<HashSet<RetainedViewEntity>>,
127) {
128    live_entities.clear();
129
130    for (main_entity, camera) in &cameras {
131        if !camera.is_active {
132            continue;
133        }
134
135        // This is the main camera, so use the first subview index (0).
136        let retained_view_entity = RetainedViewEntity::new(main_entity.into(), None, 0);
137
138        transmissive_3d_phases.prepare_for_new_frame(retained_view_entity);
139        live_entities.insert(retained_view_entity);
140    }
141
142    transmissive_3d_phases.retain(|view_entity, _| live_entities.contains(view_entity));
143}