Skip to main content

veloren_voxygen/render/pipelines/
mod.rs

1pub mod blit;
2pub mod bloom;
3pub mod clouds;
4pub mod debug;
5pub mod figure;
6pub mod fluid;
7pub mod lod_object;
8pub mod lod_terrain;
9pub mod particle;
10pub mod postprocess;
11pub mod rain_occlusion;
12pub mod rope;
13pub mod shadow;
14pub mod skybox;
15pub mod sprite;
16pub mod terrain;
17pub mod trail;
18pub mod ui;
19
20use super::{Consts, Renderer, Texture};
21use crate::scene::camera::CameraMode;
22use bytemuck::{Pod, Zeroable};
23use common::{resources::TimeOfDay, terrain::BlockKind, util::srgb_to_linear};
24use std::marker::PhantomData;
25use vek::*;
26
27pub use self::{figure::FigureSpriteAtlasData, terrain::TerrainAtlasData};
28
29// TODO: auto insert these into shaders
30pub const MAX_POINT_LIGHT_COUNT: usize = 20;
31pub const MAX_FIGURE_SHADOW_COUNT: usize = 24;
32pub const MAX_DIRECTED_LIGHT_COUNT: usize = 6;
33
34#[repr(C)]
35#[derive(Copy, Clone, Debug, Zeroable, Pod)]
36pub struct Globals {
37    /// Transformation from world coordinate space (with focus_off as the
38    /// origin) to the camera space
39    view_mat: [[f32; 4]; 4],
40    proj_mat: [[f32; 4]; 4],
41    /// proj_mat * view_mat
42    all_mat: [[f32; 4]; 4],
43    /// Offset of the camera from the focus position
44    cam_pos: [f32; 4],
45    /// Integer portion of the focus position in world coordinates
46    focus_off: [f32; 4],
47    /// Fractions portion of the focus position
48    focus_pos: [f32; 4],
49    /// NOTE: view_distance.x is the horizontal view distance, view_distance.y
50    /// is the LOD detail, view_distance.z is the
51    /// minimum height over any land chunk (i.e. the sea level), and
52    /// view_distance.w is the maximum height over this minimum height.
53    ///
54    /// TODO: Fix whatever alignment issue requires these uniforms to be
55    /// aligned.
56    view_distance: [f32; 4],
57    time_of_day: [f32; 4], // TODO: Make this f64.
58    /// Direction of sunlight.
59    sun_dir: [f32; 4],
60    /// Direction of moonlight.
61    moon_dir: [f32; 4],
62    tick: [f32; 4],
63    /// x, y represent the resolution of the screen;
64    /// w, z represent the near and far planes of the shadow map.
65    screen_res: [f32; 4],
66    light_shadow_count: [u32; 4],
67    shadow_proj_factors: [f32; 4],
68    medium: [u32; 4],
69    select_pos: [i32; 4],
70    gamma_exposure: [f32; 4],
71    last_lightning: [f32; 4],
72    wind_vel: [f32; 2],
73    ambiance: f32,
74    cam_mode: u32,
75    sprite_render_distance: f32,
76    player_ori: f32,
77    screen_fade: f32,
78    globals_dummy: f32,
79}
80/// Make sure Globals is 16-byte-aligned.
81const _: () = assert!(core::mem::size_of::<Globals>().is_multiple_of(16));
82
83#[repr(C)]
84#[derive(Copy, Clone, Debug, Zeroable, Pod)]
85pub struct Light {
86    pub pos: [f32; 4],
87    pub col: [f32; 4],
88    pub dir: [f32; 4],
89}
90
91#[repr(C)]
92#[derive(Copy, Clone, Debug, Zeroable, Pod)]
93pub struct Shadow {
94    pos_radius: [f32; 4],
95}
96
97pub const TIME_OVERFLOW: f64 = 300000.0;
98
99impl Globals {
100    /// Create global consts from the provided parameters.
101    #[expect(clippy::too_many_arguments)]
102    pub fn new(
103        view_mat: Mat4<f32>,
104        proj_mat: Mat4<f32>,
105        cam_pos: Vec3<f32>,
106        focus_pos: Vec3<f32>,
107        view_distance: f32,
108        tgt_detail: f32,
109        map_bounds: Vec2<f32>,
110        time_of_day: f64,
111        tick: f64,
112        client_tick: f64,
113        screen_res: Vec2<u16>,
114        shadow_planes: Vec2<f32>,
115        light_count: usize,
116        shadow_count: usize,
117        directed_light_count: usize,
118        medium: BlockKind,
119        select_pos: Option<Vec3<i32>>,
120        gamma: f32,
121        exposure: f32,
122        last_lightning: (Vec3<f32>, f64),
123        wind_vel: Vec2<f32>,
124        ambiance: f32,
125        cam_mode: CameraMode,
126        sprite_render_distance: f32,
127        player_ori: f32,
128        screen_fade: f32,
129    ) -> Self {
130        Self {
131            view_mat: view_mat.into_col_arrays(),
132            proj_mat: proj_mat.into_col_arrays(),
133            all_mat: (proj_mat * view_mat).into_col_arrays(),
134            cam_pos: Vec4::from(cam_pos).into_array(),
135            focus_off: Vec4::from(focus_pos).map(|e: f32| e.trunc()).into_array(),
136            focus_pos: Vec4::from(focus_pos).map(|e: f32| e.fract()).into_array(),
137            view_distance: [view_distance, tgt_detail, map_bounds.x, map_bounds.y],
138            time_of_day: [
139                (time_of_day % (3600.0 * 24.0)) as f32,
140                // TODO: Find a better way than just pure repetition. A solution like
141                // the one applied to `tick` could work, but it would be used in hot
142                // shader_code. So we might not want to use that method there.
143                //
144                // Repeats every 1000 ingame days. This increases by dt * (1 / 3600)
145                // per tick on defualt server settings. So those per tick changes can't
146                // really be fully represented at a value above `50.0`.
147                (time_of_day / (3600.0 * 24.0) % 1000.0) as f32,
148                0.0,
149                0.0,
150            ],
151            sun_dir: Vec4::from_direction(TimeOfDay::new(time_of_day).get_sun_dir()).into_array(),
152            moon_dir: Vec4::from_direction(TimeOfDay::new(time_of_day).get_moon_dir()).into_array(),
153            tick: [
154                (tick % TIME_OVERFLOW) as f32,
155                (tick / TIME_OVERFLOW).floor() as f32,
156                client_tick as f32,
157                0.0,
158            ],
159            // Provide the shadow map far plane as well.
160            screen_res: [
161                screen_res.x as f32,
162                screen_res.y as f32,
163                shadow_planes.x,
164                shadow_planes.y,
165            ],
166            // TODO: why do we accept values greater than the max?
167            light_shadow_count: [
168                usize::min(light_count, MAX_POINT_LIGHT_COUNT) as u32,
169                usize::min(shadow_count, MAX_FIGURE_SHADOW_COUNT) as u32,
170                usize::min(directed_light_count, MAX_DIRECTED_LIGHT_COUNT) as u32,
171                0,
172            ],
173            shadow_proj_factors: [
174                shadow_planes.y / (shadow_planes.y - shadow_planes.x),
175                shadow_planes.y * shadow_planes.x / (shadow_planes.y - shadow_planes.x),
176                0.0,
177                0.0,
178            ],
179            medium: [if medium.is_liquid() {
180                1
181            } else if medium.is_filled() {
182                2
183            } else {
184                0
185            }; 4],
186            select_pos: select_pos
187                .map(|sp| Vec4::from(sp) + Vec4::unit_w())
188                .unwrap_or_else(Vec4::zero)
189                .into_array(),
190            gamma_exposure: [gamma, exposure, 0.0, 0.0],
191            last_lightning: last_lightning
192                .0
193                .with_w((last_lightning.1 % TIME_OVERFLOW) as f32)
194                .into_array(),
195            wind_vel: wind_vel.into_array(),
196            ambiance: ambiance.clamped(0.0, 1.0),
197            cam_mode: cam_mode as u32,
198            sprite_render_distance,
199            player_ori,
200            screen_fade: screen_fade.clamp(0.0, 1.0),
201            globals_dummy: 0.0,
202        }
203    }
204}
205
206impl Default for Globals {
207    fn default() -> Self {
208        Self::new(
209            Mat4::identity(),
210            Mat4::identity(),
211            Vec3::zero(),
212            Vec3::zero(),
213            0.0,
214            100.0,
215            Vec2::new(140.0, 2048.0),
216            0.0,
217            0.0,
218            0.0,
219            Vec2::new(800, 500),
220            Vec2::new(1.0, 25.0),
221            0,
222            0,
223            0,
224            BlockKind::Air,
225            None,
226            1.0,
227            1.0,
228            (Vec3::zero(), -1000.0),
229            Vec2::zero(),
230            1.0,
231            CameraMode::ThirdPerson,
232            250.0,
233            0.0,
234            1.0,
235        )
236    }
237}
238
239impl Light {
240    pub fn new(pos: Vec3<f32>, col: Rgb<f32>, strength: f32) -> Self {
241        let linearized_col = srgb_to_linear(col);
242
243        Self {
244            pos: Vec4::from(pos).into_array(),
245            col: (Rgba::new(linearized_col.r, linearized_col.g, linearized_col.b, 0.0) * strength)
246                .into_array(),
247            dir: [0.0, 0.0, 0.0, 10.0],
248        }
249    }
250
251    pub fn with_dir(mut self, dir: Vec3<f32>, fov: f32) -> Self {
252        self.dir = dir.normalized().with_w(fov).into_array();
253        self
254    }
255
256    pub fn get_pos(&self) -> Vec3<f32> { Vec3::new(self.pos[0], self.pos[1], self.pos[2]) }
257
258    #[must_use]
259    pub fn with_strength(mut self, strength: f32) -> Self {
260        self.col = (Vec4::<f32>::from(self.col) * strength).into_array();
261        self
262    }
263}
264
265impl Default for Light {
266    fn default() -> Self { Self::new(Vec3::zero(), Rgb::zero(), 0.0) }
267}
268
269impl Shadow {
270    pub fn new(pos: Vec3<f32>, radius: f32) -> Self {
271        Self {
272            pos_radius: [pos.x, pos.y, pos.z, radius],
273        }
274    }
275
276    pub fn get_pos(&self) -> Vec3<f32> {
277        Vec3::new(self.pos_radius[0], self.pos_radius[1], self.pos_radius[2])
278    }
279}
280
281impl Default for Shadow {
282    fn default() -> Self { Self::new(Vec3::zero(), 0.0) }
283}
284
285// Global scene data spread across several arrays.
286pub struct GlobalModel {
287    // TODO: enforce that these are the lengths in the shaders??
288    pub globals: Consts<Globals>,
289    pub lights: Consts<Light>,
290    pub shadows: Consts<Shadow>,
291    pub shadow_mats: shadow::BoundLocals,
292    pub rain_occlusion_mats: rain_occlusion::BoundLocals,
293    pub point_light_matrices: Box<[shadow::PointLightMatrix; 126]>,
294}
295
296pub struct GlobalsBindGroup {
297    pub(super) bind_group: wgpu::BindGroup,
298}
299
300pub struct ShadowTexturesBindGroup {
301    pub(super) bind_group: wgpu::BindGroup,
302}
303
304pub struct GlobalsLayouts {
305    pub globals: wgpu::BindGroupLayout,
306    pub figure_sprite_atlas_layout: VoxelAtlasLayout<FigureSpriteAtlasData>,
307    pub terrain_atlas_layout: VoxelAtlasLayout<TerrainAtlasData>,
308    pub shadow_textures: wgpu::BindGroupLayout,
309}
310
311/// A type representing a set of textures that have the same atlas layout and
312/// pertain to a greedy voxel structure.
313pub struct AtlasTextures<Locals, S: AtlasData>
314where
315    [(); S::TEXTURES]:,
316{
317    pub(super) bind_group: wgpu::BindGroup,
318    pub textures: [Texture; S::TEXTURES],
319    phantom: std::marker::PhantomData<Locals>,
320}
321
322pub struct VoxelAtlasLayout<S: AtlasData>(wgpu::BindGroupLayout, PhantomData<S>);
323
324impl<S: AtlasData> VoxelAtlasLayout<S> {
325    pub fn new(device: &wgpu::Device) -> Self {
326        let layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
327            label: None,
328            entries: &S::layout(),
329        });
330
331        Self(layout, PhantomData)
332    }
333
334    pub fn layout(&self) -> &wgpu::BindGroupLayout { &self.0 }
335}
336
337/// A trait implemented by texture atlas groups.
338///
339/// Terrain, figures, sprites, etc. all use texture atlases but have different
340/// requirements, such as that layers provided by each atlas. This trait
341/// abstracts over these cases.
342pub trait AtlasData {
343    /// The number of texture channels that this atlas has.
344    const TEXTURES: usize;
345    /// Abstracts over a slice into the texture data, as returned by
346    /// [`AtlasData::slice_mut`].
347    type SliceMut<'a>: Iterator
348    where
349        Self: 'a;
350
351    /// Return blank atlas data upon which texels can be applied.
352    fn blank_with_size(sz: Vec2<u16>) -> Self;
353
354    /// Return an array of texture formats and data for each texture layer in
355    /// the atlas.
356    fn as_texture_data(&self) -> [(wgpu::TextureFormat, &[u8]); Self::TEXTURES];
357
358    /// Return a layout entry that corresponds to the texture layers in the
359    /// atlas.
360    fn layout() -> Vec<wgpu::BindGroupLayoutEntry>;
361
362    /// Take a sub-slice of the texture data for each layer in the atlas.
363    fn slice_mut(&mut self, range: std::ops::Range<usize>) -> Self::SliceMut<'_>;
364
365    /// Create textures on the GPU corresponding to the layers in the atlas.
366    fn create_textures(
367        &self,
368        renderer: &mut Renderer,
369        atlas_size: Vec2<u16>,
370    ) -> [Texture; Self::TEXTURES] {
371        self.as_texture_data().map(|(fmt, data)| {
372            let texture_info = wgpu::TextureDescriptor {
373                label: None,
374                size: wgpu::Extent3d {
375                    width: u32::from(atlas_size.x),
376                    height: u32::from(atlas_size.y),
377                    depth_or_array_layers: 1,
378                },
379                mip_level_count: 1,
380                sample_count: 1,
381                dimension: wgpu::TextureDimension::D2,
382                format: fmt,
383                usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
384                view_formats: &[],
385            };
386
387            let sampler_info = wgpu::SamplerDescriptor {
388                label: None,
389                address_mode_u: wgpu::AddressMode::ClampToEdge,
390                address_mode_v: wgpu::AddressMode::ClampToEdge,
391                address_mode_w: wgpu::AddressMode::ClampToEdge,
392                mag_filter: wgpu::FilterMode::Linear,
393                min_filter: wgpu::FilterMode::Linear,
394                mipmap_filter: wgpu::FilterMode::Nearest,
395                border_color: Some(wgpu::SamplerBorderColor::TransparentBlack),
396                ..Default::default()
397            };
398
399            let view_info = wgpu::TextureViewDescriptor {
400                label: None,
401                format: Some(fmt),
402                dimension: Some(wgpu::TextureViewDimension::D2),
403                usage: None,
404                aspect: wgpu::TextureAspect::All,
405                base_mip_level: 0,
406                mip_level_count: None,
407                base_array_layer: 0,
408                array_layer_count: None,
409            };
410
411            renderer.create_texture_with_data_raw(&texture_info, &view_info, &sampler_info, data)
412        })
413    }
414}
415
416impl GlobalsLayouts {
417    pub fn base_globals_layout() -> Vec<wgpu::BindGroupLayoutEntry> {
418        vec![
419            // Global uniform
420            wgpu::BindGroupLayoutEntry {
421                binding: 0,
422                visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
423                ty: wgpu::BindingType::Buffer {
424                    ty: wgpu::BufferBindingType::Uniform,
425                    has_dynamic_offset: false,
426                    min_binding_size: None,
427                },
428                count: None,
429            },
430            // Noise tex
431            wgpu::BindGroupLayoutEntry {
432                binding: 1,
433                visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
434                ty: wgpu::BindingType::Texture {
435                    sample_type: wgpu::TextureSampleType::Float { filterable: true },
436                    view_dimension: wgpu::TextureViewDimension::D2,
437                    multisampled: false,
438                },
439                count: None,
440            },
441            wgpu::BindGroupLayoutEntry {
442                binding: 2,
443                visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
444                ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
445                count: None,
446            },
447            // Light uniform
448            wgpu::BindGroupLayoutEntry {
449                binding: 3,
450                visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
451                ty: wgpu::BindingType::Buffer {
452                    ty: wgpu::BufferBindingType::Uniform,
453                    has_dynamic_offset: false,
454                    min_binding_size: None,
455                },
456                count: None,
457            },
458            // Shadow uniform
459            wgpu::BindGroupLayoutEntry {
460                binding: 4,
461                visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
462                ty: wgpu::BindingType::Buffer {
463                    ty: wgpu::BufferBindingType::Uniform,
464                    has_dynamic_offset: false,
465                    min_binding_size: None,
466                },
467                count: None,
468            },
469            // Alt texture
470            wgpu::BindGroupLayoutEntry {
471                binding: 5,
472                visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
473                ty: wgpu::BindingType::Texture {
474                    sample_type: wgpu::TextureSampleType::Float { filterable: true },
475                    view_dimension: wgpu::TextureViewDimension::D2,
476                    multisampled: false,
477                },
478                count: None,
479            },
480            wgpu::BindGroupLayoutEntry {
481                binding: 6,
482                visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
483                ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
484                count: None,
485            },
486            // Horizon texture
487            wgpu::BindGroupLayoutEntry {
488                binding: 7,
489                visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
490                ty: wgpu::BindingType::Texture {
491                    sample_type: wgpu::TextureSampleType::Float { filterable: true },
492                    view_dimension: wgpu::TextureViewDimension::D2,
493                    multisampled: false,
494                },
495                count: None,
496            },
497            wgpu::BindGroupLayoutEntry {
498                binding: 8,
499                visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
500                ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
501                count: None,
502            },
503            // light shadows (ie shadows from a light?)
504            wgpu::BindGroupLayoutEntry {
505                binding: 9,
506                visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
507                // TODO: is this relevant?
508                ty: wgpu::BindingType::Buffer {
509                    ty: wgpu::BufferBindingType::Uniform,
510                    has_dynamic_offset: false,
511                    min_binding_size: None,
512                },
513                count: None,
514            },
515            // lod map (t_map)
516            wgpu::BindGroupLayoutEntry {
517                binding: 10,
518                visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
519                ty: wgpu::BindingType::Texture {
520                    sample_type: wgpu::TextureSampleType::Float { filterable: true },
521                    view_dimension: wgpu::TextureViewDimension::D2,
522                    multisampled: false,
523                },
524                count: None,
525            },
526            wgpu::BindGroupLayoutEntry {
527                binding: 11,
528                visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
529                ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
530                count: None,
531            },
532            // clouds t_weather
533            wgpu::BindGroupLayoutEntry {
534                binding: 12,
535                visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
536                ty: wgpu::BindingType::Texture {
537                    sample_type: wgpu::TextureSampleType::Float { filterable: true },
538                    view_dimension: wgpu::TextureViewDimension::D2,
539                    multisampled: false,
540                },
541                count: None,
542            },
543            wgpu::BindGroupLayoutEntry {
544                binding: 13,
545                visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
546                ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
547                count: None,
548            },
549            // rain occlusion
550            wgpu::BindGroupLayoutEntry {
551                binding: 14,
552                visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
553                ty: wgpu::BindingType::Buffer {
554                    ty: wgpu::BufferBindingType::Uniform,
555                    has_dynamic_offset: false,
556                    min_binding_size: None,
557                },
558                count: None,
559            },
560        ]
561    }
562
563    pub fn new(device: &wgpu::Device) -> Self {
564        let globals = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
565            label: Some("Globals layout"),
566            entries: &Self::base_globals_layout(),
567        });
568
569        let shadow_textures = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
570            label: None,
571            entries: &[
572                // point shadow_maps
573                wgpu::BindGroupLayoutEntry {
574                    binding: 0,
575                    visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
576                    ty: wgpu::BindingType::Texture {
577                        sample_type: wgpu::TextureSampleType::Depth,
578                        view_dimension: wgpu::TextureViewDimension::Cube,
579                        multisampled: false,
580                    },
581                    count: None,
582                },
583                wgpu::BindGroupLayoutEntry {
584                    binding: 1,
585                    visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
586                    ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Comparison),
587                    count: None,
588                },
589                // directed shadow maps
590                wgpu::BindGroupLayoutEntry {
591                    binding: 2,
592                    visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
593                    ty: wgpu::BindingType::Texture {
594                        sample_type: wgpu::TextureSampleType::Depth,
595                        view_dimension: wgpu::TextureViewDimension::D2,
596                        multisampled: false,
597                    },
598                    count: None,
599                },
600                wgpu::BindGroupLayoutEntry {
601                    binding: 3,
602                    visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
603                    ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Comparison),
604                    count: None,
605                },
606                // Rain occlusion maps
607                wgpu::BindGroupLayoutEntry {
608                    binding: 4,
609                    visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
610                    ty: wgpu::BindingType::Texture {
611                        sample_type: wgpu::TextureSampleType::Depth,
612                        view_dimension: wgpu::TextureViewDimension::D2,
613                        multisampled: false,
614                    },
615                    count: None,
616                },
617                wgpu::BindGroupLayoutEntry {
618                    binding: 5,
619                    visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
620                    ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Comparison),
621                    count: None,
622                },
623            ],
624        });
625
626        Self {
627            globals,
628            figure_sprite_atlas_layout: VoxelAtlasLayout::new(device),
629            terrain_atlas_layout: VoxelAtlasLayout::new(device),
630            shadow_textures,
631        }
632    }
633
634    // Note: this allocation serves the purpose of not having to duplicate code
635    pub fn bind_base_globals<'a>(
636        global_model: &'a GlobalModel,
637        lod_data: &'a lod_terrain::LodData,
638        noise: &'a Texture,
639    ) -> Vec<wgpu::BindGroupEntry<'a>> {
640        vec![
641            // Global uniform
642            wgpu::BindGroupEntry {
643                binding: 0,
644                resource: global_model.globals.buf().as_entire_binding(),
645            },
646            // Noise tex
647            wgpu::BindGroupEntry {
648                binding: 1,
649                resource: wgpu::BindingResource::TextureView(&noise.view),
650            },
651            wgpu::BindGroupEntry {
652                binding: 2,
653                resource: wgpu::BindingResource::Sampler(&noise.sampler),
654            },
655            // Light uniform
656            wgpu::BindGroupEntry {
657                binding: 3,
658                resource: global_model.lights.buf().as_entire_binding(),
659            },
660            // Shadow uniform
661            wgpu::BindGroupEntry {
662                binding: 4,
663                resource: global_model.shadows.buf().as_entire_binding(),
664            },
665            // Alt texture
666            wgpu::BindGroupEntry {
667                binding: 5,
668                resource: wgpu::BindingResource::TextureView(&lod_data.alt.view),
669            },
670            wgpu::BindGroupEntry {
671                binding: 6,
672                resource: wgpu::BindingResource::Sampler(&lod_data.alt.sampler),
673            },
674            // Horizon texture
675            wgpu::BindGroupEntry {
676                binding: 7,
677                resource: wgpu::BindingResource::TextureView(&lod_data.horizon.view),
678            },
679            wgpu::BindGroupEntry {
680                binding: 8,
681                resource: wgpu::BindingResource::Sampler(&lod_data.horizon.sampler),
682            },
683            // light shadows
684            wgpu::BindGroupEntry {
685                binding: 9,
686                resource: global_model.shadow_mats.buf().as_entire_binding(),
687            },
688            // lod map (t_map)
689            wgpu::BindGroupEntry {
690                binding: 10,
691                resource: wgpu::BindingResource::TextureView(&lod_data.map.view),
692            },
693            wgpu::BindGroupEntry {
694                binding: 11,
695                resource: wgpu::BindingResource::Sampler(&lod_data.map.sampler),
696            },
697            wgpu::BindGroupEntry {
698                binding: 12,
699                resource: wgpu::BindingResource::TextureView(&lod_data.weather.view),
700            },
701            wgpu::BindGroupEntry {
702                binding: 13,
703                resource: wgpu::BindingResource::Sampler(&lod_data.weather.sampler),
704            },
705            // rain occlusion
706            wgpu::BindGroupEntry {
707                binding: 14,
708                resource: global_model.rain_occlusion_mats.buf().as_entire_binding(),
709            },
710        ]
711    }
712
713    pub fn bind(
714        &self,
715        device: &wgpu::Device,
716        global_model: &GlobalModel,
717        lod_data: &lod_terrain::LodData,
718        noise: &Texture,
719    ) -> GlobalsBindGroup {
720        let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
721            label: None,
722            layout: &self.globals,
723            entries: &Self::bind_base_globals(global_model, lod_data, noise),
724        });
725
726        GlobalsBindGroup { bind_group }
727    }
728
729    pub fn bind_shadow_textures(
730        &self,
731        device: &wgpu::Device,
732        point_shadow_map: &Texture,
733        directed_shadow_map: &Texture,
734        rain_occlusion_map: &Texture,
735    ) -> ShadowTexturesBindGroup {
736        let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
737            label: None,
738            layout: &self.shadow_textures,
739            entries: &[
740                wgpu::BindGroupEntry {
741                    binding: 0,
742                    resource: wgpu::BindingResource::TextureView(&point_shadow_map.view),
743                },
744                wgpu::BindGroupEntry {
745                    binding: 1,
746                    resource: wgpu::BindingResource::Sampler(&point_shadow_map.sampler),
747                },
748                wgpu::BindGroupEntry {
749                    binding: 2,
750                    resource: wgpu::BindingResource::TextureView(&directed_shadow_map.view),
751                },
752                wgpu::BindGroupEntry {
753                    binding: 3,
754                    resource: wgpu::BindingResource::Sampler(&directed_shadow_map.sampler),
755                },
756                wgpu::BindGroupEntry {
757                    binding: 4,
758                    resource: wgpu::BindingResource::TextureView(&rain_occlusion_map.view),
759                },
760                wgpu::BindGroupEntry {
761                    binding: 5,
762                    resource: wgpu::BindingResource::Sampler(&rain_occlusion_map.sampler),
763                },
764            ],
765        });
766
767        ShadowTexturesBindGroup { bind_group }
768    }
769
770    pub fn bind_atlas_textures<Locals, S: AtlasData>(
771        &self,
772        device: &wgpu::Device,
773        layout: &VoxelAtlasLayout<S>,
774        textures: [Texture; S::TEXTURES],
775    ) -> AtlasTextures<Locals, S> {
776        let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
777            label: None,
778            layout: layout.layout(),
779            entries: &textures
780                .iter()
781                .enumerate()
782                .flat_map(|(i, tex)| {
783                    [
784                        wgpu::BindGroupEntry {
785                            binding: i as u32 * 2,
786                            resource: wgpu::BindingResource::TextureView(&tex.view),
787                        },
788                        wgpu::BindGroupEntry {
789                            binding: i as u32 * 2 + 1,
790                            resource: wgpu::BindingResource::Sampler(&tex.sampler),
791                        },
792                    ]
793                })
794                .collect::<Vec<_>>(),
795        });
796
797        AtlasTextures {
798            textures,
799            bind_group,
800            phantom: std::marker::PhantomData,
801        }
802    }
803}