veloren_voxygen/render/renderer/
rain_occlusion_map.rs

1use crate::{render::pipelines::rain_occlusion, scene::terrain::RAIN_OCCLUSION_CHUNKS};
2
3use super::super::{RenderError, ShadowMapMode, texture::Texture};
4use common::{terrain::TerrainChunkSize, vol::RectVolSize};
5use vek::*;
6
7/// A type that holds rain occlusion map data.  Since rain occlusion mapping may
8/// not be supported on all platforms, we try to keep it separate.
9pub struct RainOcclusionMapRenderer {
10    pub depth: Texture,
11
12    pub terrain_pipeline: rain_occlusion::RainOcclusionPipeline,
13    pub figure_pipeline: rain_occlusion::RainOcclusionFigurePipeline,
14}
15
16pub enum RainOcclusionMap {
17    Enabled(RainOcclusionMapRenderer),
18    /// Dummy texture
19    Disabled(Texture),
20}
21
22impl RainOcclusionMap {
23    pub fn new(
24        device: &wgpu::Device,
25        queue: &wgpu::Queue,
26        directed: Option<rain_occlusion::RainOcclusionPipeline>,
27        figure: Option<rain_occlusion::RainOcclusionFigurePipeline>,
28        view: Option<Texture>,
29    ) -> Self {
30        if let (Some(terrain_pipeline), Some(figure_pipeline), Some(depth)) =
31            (directed, figure, view)
32        {
33            Self::Enabled(RainOcclusionMapRenderer {
34                depth,
35                terrain_pipeline,
36                figure_pipeline,
37            })
38        } else {
39            Self::Disabled(Self::create_dummy_tex(device, queue))
40        }
41    }
42
43    fn create_dummy_tex(device: &wgpu::Device, queue: &wgpu::Queue) -> Texture {
44        let tex = {
45            let tex = wgpu::TextureDescriptor {
46                label: None,
47                size: wgpu::Extent3d {
48                    width: 4,
49                    height: 4,
50                    depth_or_array_layers: 1,
51                },
52                mip_level_count: 1,
53                sample_count: 1,
54                dimension: wgpu::TextureDimension::D2,
55                format: wgpu::TextureFormat::Depth24Plus,
56                usage: wgpu::TextureUsages::TEXTURE_BINDING
57                    | wgpu::TextureUsages::RENDER_ATTACHMENT,
58                view_formats: &[],
59            };
60
61            let view = wgpu::TextureViewDescriptor {
62                label: None,
63                format: Some(wgpu::TextureFormat::Depth24Plus),
64                dimension: Some(wgpu::TextureViewDimension::D2),
65                aspect: wgpu::TextureAspect::DepthOnly,
66                base_mip_level: 0,
67                mip_level_count: None,
68                base_array_layer: 0,
69                array_layer_count: None,
70            };
71
72            let sampler_info = wgpu::SamplerDescriptor {
73                label: None,
74                address_mode_u: wgpu::AddressMode::ClampToEdge,
75                address_mode_v: wgpu::AddressMode::ClampToEdge,
76                address_mode_w: wgpu::AddressMode::ClampToEdge,
77                mag_filter: wgpu::FilterMode::Linear,
78                min_filter: wgpu::FilterMode::Linear,
79                mipmap_filter: wgpu::FilterMode::Nearest,
80                compare: Some(wgpu::CompareFunction::LessEqual),
81                ..Default::default()
82            };
83
84            Texture::new_raw(device, &tex, &view, &sampler_info)
85        };
86
87        // Clear to 1.0
88        let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
89            label: Some("Dummy rain occlusion tex clearing encoder"),
90        });
91
92        encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
93            label: Some("Clear dummy rain occlusion texture"),
94            color_attachments: &[],
95            depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
96                view: &tex.view,
97                depth_ops: Some(wgpu::Operations {
98                    load: wgpu::LoadOp::Clear(1.0),
99                    store: wgpu::StoreOp::Store,
100                }),
101                stencil_ops: None,
102            }),
103            timestamp_writes: None,
104            occlusion_query_set: None,
105        });
106
107        queue.submit(std::iter::once(encoder.finish()));
108
109        tex
110    }
111
112    /// Create texture and view for rain ocllusion maps.
113    /// Returns (point, directed)
114    pub(super) fn create_view(
115        device: &wgpu::Device,
116        mode: &ShadowMapMode,
117        max_texture_size: u32,
118    ) -> Result<Texture, RenderError> {
119        // (Attempt to) apply resolution factor to rain occlusion map resolution.
120        let resolution_factor = mode.resolution.clamped(0.25, 4.0);
121
122        let size =
123            (RAIN_OCCLUSION_CHUNKS as f32).sqrt().ceil() as u32 * TerrainChunkSize::RECT_SIZE * 2;
124
125        // Limit to max texture size, rather than erroring.
126        let size = size.map(|e| {
127            let size = e as f32 * resolution_factor;
128            // NOTE: We know 0 <= e since we clamped the resolution factor to be between
129            // 0.25 and 4.0.
130            if size <= max_texture_size as f32 {
131                size as u32
132            } else {
133                max_texture_size
134            }
135        });
136
137        let levels = 1;
138        // Limit to max texture size rather than erroring.
139        let two_size = size.map(|e| {
140            u32::checked_next_power_of_two(e)
141                .filter(|&e| e <= max_texture_size)
142                .unwrap_or(max_texture_size)
143        });
144        let min_size = size.reduce_min();
145        let max_size = size.reduce_max();
146        let _min_two_size = two_size.reduce_min();
147        let _max_two_size = two_size.reduce_max();
148        // For rotated shadow maps, the maximum size of a pixel along any axis is the
149        // size of a diagonal along that axis.
150        let diag_size = size.map(f64::from).magnitude();
151        let diag_cross_size = f64::from(min_size) / f64::from(max_size) * diag_size;
152        let (diag_size, _diag_cross_size) =
153            if 0.0 < diag_size && diag_size <= f64::from(max_texture_size) {
154                // NOTE: diag_cross_size must be non-negative, since it is the ratio of a
155                // non-negative and a positive number (if max_size were zero,
156                // diag_size would be 0 too).  And it must be <= diag_size,
157                // since min_size <= max_size.  Therefore, if diag_size fits in a
158                // u16, so does diag_cross_size.
159                (diag_size as u32, diag_cross_size as u32)
160            } else {
161                // Limit to max texture resolution rather than error.
162                (max_texture_size, max_texture_size)
163            };
164        let diag_two_size = u32::checked_next_power_of_two(diag_size)
165            .filter(|&e| e <= max_texture_size)
166            // Limit to max texture resolution rather than error.
167            .unwrap_or(max_texture_size)
168            // Make sure we don't try to create a zero sized texture (divided by 4 below)
169            .max(4);
170
171        let rain_occlusion_tex = wgpu::TextureDescriptor {
172            label: None,
173            size: wgpu::Extent3d {
174                width: diag_two_size,
175                height: diag_two_size,
176                depth_or_array_layers: 1,
177            },
178            mip_level_count: levels,
179            sample_count: 1,
180            dimension: wgpu::TextureDimension::D2,
181            format: wgpu::TextureFormat::Depth24Plus,
182            usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::RENDER_ATTACHMENT,
183            view_formats: &[],
184        };
185
186        let rain_occlusion_view = wgpu::TextureViewDescriptor {
187            label: None,
188            format: Some(wgpu::TextureFormat::Depth24Plus),
189            dimension: Some(wgpu::TextureViewDimension::D2),
190            aspect: wgpu::TextureAspect::DepthOnly,
191            base_mip_level: 0,
192            mip_level_count: None,
193            base_array_layer: 0,
194            array_layer_count: None,
195        };
196
197        let sampler_info = wgpu::SamplerDescriptor {
198            label: None,
199            address_mode_u: wgpu::AddressMode::ClampToEdge,
200            address_mode_v: wgpu::AddressMode::ClampToEdge,
201            address_mode_w: wgpu::AddressMode::ClampToEdge,
202            mag_filter: wgpu::FilterMode::Linear,
203            min_filter: wgpu::FilterMode::Linear,
204            mipmap_filter: wgpu::FilterMode::Nearest,
205            compare: Some(wgpu::CompareFunction::LessEqual),
206            ..Default::default()
207        };
208
209        let rain_occlusion_tex = Texture::new_raw(
210            device,
211            &rain_occlusion_tex,
212            &rain_occlusion_view,
213            &sampler_info,
214        );
215
216        Ok(rain_occlusion_tex)
217    }
218
219    pub fn texture(&self) -> &Texture {
220        match self {
221            Self::Enabled(renderer) => &renderer.depth,
222            Self::Disabled(dummy) => dummy,
223        }
224    }
225}