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                usage: None,
66                aspect: wgpu::TextureAspect::DepthOnly,
67                base_mip_level: 0,
68                mip_level_count: None,
69                base_array_layer: 0,
70                array_layer_count: None,
71            };
72
73            let sampler_info = wgpu::SamplerDescriptor {
74                label: None,
75                address_mode_u: wgpu::AddressMode::ClampToEdge,
76                address_mode_v: wgpu::AddressMode::ClampToEdge,
77                address_mode_w: wgpu::AddressMode::ClampToEdge,
78                mag_filter: wgpu::FilterMode::Linear,
79                min_filter: wgpu::FilterMode::Linear,
80                mipmap_filter: wgpu::FilterMode::Nearest,
81                compare: Some(wgpu::CompareFunction::LessEqual),
82                ..Default::default()
83            };
84
85            Texture::new_raw(device, &tex, &view, &sampler_info)
86        };
87
88        // Clear to 1.0
89        let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
90            label: Some("Dummy rain occlusion tex clearing encoder"),
91        });
92
93        encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
94            label: Some("Clear dummy rain occlusion texture"),
95            color_attachments: &[],
96            depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
97                view: &tex.view,
98                depth_ops: Some(wgpu::Operations {
99                    load: wgpu::LoadOp::Clear(1.0),
100                    store: wgpu::StoreOp::Store,
101                }),
102                stencil_ops: None,
103            }),
104            timestamp_writes: None,
105            occlusion_query_set: None,
106        });
107
108        queue.submit(std::iter::once(encoder.finish()));
109
110        tex
111    }
112
113    /// Create texture and view for rain ocllusion maps.
114    /// Returns (point, directed)
115    pub(super) fn create_view(
116        device: &wgpu::Device,
117        mode: &ShadowMapMode,
118        max_texture_size: u32,
119    ) -> Result<Texture, RenderError> {
120        // (Attempt to) apply resolution factor to rain occlusion map resolution.
121        let resolution_factor = mode.resolution.clamped(0.25, 4.0);
122
123        let size =
124            (RAIN_OCCLUSION_CHUNKS as f32).sqrt().ceil() as u32 * TerrainChunkSize::RECT_SIZE * 2;
125
126        // Limit to max texture size, rather than erroring.
127        let size = size.map(|e| {
128            let size = e as f32 * resolution_factor;
129            // NOTE: We know 0 <= e since we clamped the resolution factor to be between
130            // 0.25 and 4.0.
131            if size <= max_texture_size as f32 {
132                size as u32
133            } else {
134                max_texture_size
135            }
136        });
137
138        let levels = 1;
139        // Limit to max texture size rather than erroring.
140        let two_size = size.map(|e| {
141            u32::checked_next_power_of_two(e)
142                .filter(|&e| e <= max_texture_size)
143                .unwrap_or(max_texture_size)
144        });
145        let min_size = size.reduce_min();
146        let max_size = size.reduce_max();
147        let _min_two_size = two_size.reduce_min();
148        let _max_two_size = two_size.reduce_max();
149        // For rotated shadow maps, the maximum size of a pixel along any axis is the
150        // size of a diagonal along that axis.
151        let diag_size = size.map(f64::from).magnitude();
152        let diag_cross_size = f64::from(min_size) / f64::from(max_size) * diag_size;
153        let (diag_size, _diag_cross_size) =
154            if 0.0 < diag_size && diag_size <= f64::from(max_texture_size) {
155                // NOTE: diag_cross_size must be non-negative, since it is the ratio of a
156                // non-negative and a positive number (if max_size were zero,
157                // diag_size would be 0 too).  And it must be <= diag_size,
158                // since min_size <= max_size.  Therefore, if diag_size fits in a
159                // u16, so does diag_cross_size.
160                (diag_size as u32, diag_cross_size as u32)
161            } else {
162                // Limit to max texture resolution rather than error.
163                (max_texture_size, max_texture_size)
164            };
165        let diag_two_size = u32::checked_next_power_of_two(diag_size)
166            .filter(|&e| e <= max_texture_size)
167            // Limit to max texture resolution rather than error.
168            .unwrap_or(max_texture_size)
169            // Make sure we don't try to create a zero sized texture (divided by 4 below)
170            .max(4);
171
172        let rain_occlusion_tex = wgpu::TextureDescriptor {
173            label: None,
174            size: wgpu::Extent3d {
175                width: diag_two_size,
176                height: diag_two_size,
177                depth_or_array_layers: 1,
178            },
179            mip_level_count: levels,
180            sample_count: 1,
181            dimension: wgpu::TextureDimension::D2,
182            format: wgpu::TextureFormat::Depth24Plus,
183            usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::RENDER_ATTACHMENT,
184            view_formats: &[],
185        };
186
187        let rain_occlusion_view = wgpu::TextureViewDescriptor {
188            label: None,
189            format: Some(wgpu::TextureFormat::Depth24Plus),
190            dimension: Some(wgpu::TextureViewDimension::D2),
191            usage: None,
192            aspect: wgpu::TextureAspect::DepthOnly,
193            base_mip_level: 0,
194            mip_level_count: None,
195            base_array_layer: 0,
196            array_layer_count: None,
197        };
198
199        let sampler_info = wgpu::SamplerDescriptor {
200            label: None,
201            address_mode_u: wgpu::AddressMode::ClampToEdge,
202            address_mode_v: wgpu::AddressMode::ClampToEdge,
203            address_mode_w: wgpu::AddressMode::ClampToEdge,
204            mag_filter: wgpu::FilterMode::Linear,
205            min_filter: wgpu::FilterMode::Linear,
206            mipmap_filter: wgpu::FilterMode::Nearest,
207            compare: Some(wgpu::CompareFunction::LessEqual),
208            ..Default::default()
209        };
210
211        let rain_occlusion_tex = Texture::new_raw(
212            device,
213            &rain_occlusion_tex,
214            &rain_occlusion_view,
215            &sampler_info,
216        );
217
218        Ok(rain_occlusion_tex)
219    }
220
221    pub fn texture(&self) -> &Texture {
222        match self {
223            Self::Enabled(renderer) => &renderer.depth,
224            Self::Disabled(dummy) => dummy,
225        }
226    }
227}