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