Skip to main content

bevy_pbr/transmission/
texture.rs

1use bevy_core_pipeline::core_3d::{AlphaMask3d, Opaque3d, Transparent3d};
2use bevy_ecs::{
3    component::Component,
4    entity::Entity,
5    system::{Commands, Query, Res, ResMut},
6};
7use bevy_image::ToExtents;
8use bevy_platform::collections::HashMap;
9use bevy_render::{
10    camera::ExtractedCamera,
11    render_phase::{ViewBinnedRenderPhases, ViewSortedRenderPhases},
12    render_resource::{
13        FilterMode, Sampler, SamplerDescriptor, Texture, TextureDescriptor, TextureDimension,
14        TextureUsages, TextureView,
15    },
16    renderer::RenderDevice,
17    texture::TextureCache,
18    view::ExtractedView,
19};
20
21use crate::{ScreenSpaceTransmission, Transmissive3d};
22
23#[derive(Component)]
24pub struct ViewTransmissionTexture {
25    pub texture: Texture,
26    pub view: TextureView,
27    pub sampler: Sampler,
28}
29
30pub fn prepare_core_3d_transmission_textures(
31    mut commands: Commands,
32    mut texture_cache: ResMut<TextureCache>,
33    render_device: Res<RenderDevice>,
34    opaque_3d_phases: Res<ViewBinnedRenderPhases<Opaque3d>>,
35    alpha_mask_3d_phases: Res<ViewBinnedRenderPhases<AlphaMask3d>>,
36    transmissive_3d_phases: Res<ViewSortedRenderPhases<Transmissive3d>>,
37    transparent_3d_phases: Res<ViewSortedRenderPhases<Transparent3d>>,
38    views_3d: Query<(
39        Entity,
40        &ExtractedCamera,
41        &ScreenSpaceTransmission,
42        &ExtractedView,
43    )>,
44) {
45    let mut textures = <HashMap<_, _>>::default();
46    for (entity, camera, transmission, view) in &views_3d {
47        if !opaque_3d_phases.contains_key(&view.retained_view_entity)
48            || !alpha_mask_3d_phases.contains_key(&view.retained_view_entity)
49            || !transparent_3d_phases.contains_key(&view.retained_view_entity)
50        {
51            continue;
52        };
53
54        let Some(transmissive_3d_phase) = transmissive_3d_phases.get(&view.retained_view_entity)
55        else {
56            continue;
57        };
58
59        let Some(physical_target_size) = camera.physical_target_size else {
60            continue;
61        };
62
63        // Don't prepare a transmission texture if the number of steps is set to 0
64        if transmission.steps == 0 {
65            continue;
66        }
67
68        // Don't prepare a transmission texture if there are no transmissive items to render
69        if transmissive_3d_phase.items.is_empty() {
70            continue;
71        }
72
73        let cached_texture = textures
74            .entry(camera.target.clone())
75            .or_insert_with(|| {
76                let usage = TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST;
77
78                let descriptor = TextureDescriptor {
79                    label: Some("view_transmission_texture"),
80                    // The size of the transmission texture
81                    size: physical_target_size.to_extents(),
82                    mip_level_count: 1,
83                    sample_count: 1, // No need for MSAA, as we'll only copy the main texture here
84                    dimension: TextureDimension::D2,
85                    format: view.target_format,
86                    usage,
87                    view_formats: &[],
88                };
89
90                texture_cache.get(&render_device, descriptor)
91            })
92            .clone();
93
94        let sampler = render_device.create_sampler(&SamplerDescriptor {
95            label: Some("view_transmission_sampler"),
96            mag_filter: FilterMode::Linear,
97            min_filter: FilterMode::Linear,
98            ..Default::default()
99        });
100
101        commands.entity(entity).insert(ViewTransmissionTexture {
102            texture: cached_texture.texture,
103            view: cached_texture.default_view,
104            sampler,
105        });
106    }
107}