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                usage: None,
390                aspect: wgpu::TextureAspect::All,
391                base_mip_level: 0,
392                mip_level_count: None,
393                base_array_layer: 0,
394                array_layer_count: None,
395            };
396
397            renderer.create_texture_with_data_raw(&texture_info, &view_info, &sampler_info, data)
398        })
399    }
400}
401
402impl GlobalsLayouts {
403    pub fn base_globals_layout() -> Vec<wgpu::BindGroupLayoutEntry> {
404        vec![
405            // Global uniform
406            wgpu::BindGroupLayoutEntry {
407                binding: 0,
408                visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
409                ty: wgpu::BindingType::Buffer {
410                    ty: wgpu::BufferBindingType::Uniform,
411                    has_dynamic_offset: false,
412                    min_binding_size: None,
413                },
414                count: None,
415            },
416            // Noise tex
417            wgpu::BindGroupLayoutEntry {
418                binding: 1,
419                visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
420                ty: wgpu::BindingType::Texture {
421                    sample_type: wgpu::TextureSampleType::Float { filterable: true },
422                    view_dimension: wgpu::TextureViewDimension::D2,
423                    multisampled: false,
424                },
425                count: None,
426            },
427            wgpu::BindGroupLayoutEntry {
428                binding: 2,
429                visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
430                ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
431                count: None,
432            },
433            // Light uniform
434            wgpu::BindGroupLayoutEntry {
435                binding: 3,
436                visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
437                ty: wgpu::BindingType::Buffer {
438                    ty: wgpu::BufferBindingType::Uniform,
439                    has_dynamic_offset: false,
440                    min_binding_size: None,
441                },
442                count: None,
443            },
444            // Shadow uniform
445            wgpu::BindGroupLayoutEntry {
446                binding: 4,
447                visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
448                ty: wgpu::BindingType::Buffer {
449                    ty: wgpu::BufferBindingType::Uniform,
450                    has_dynamic_offset: false,
451                    min_binding_size: None,
452                },
453                count: None,
454            },
455            // Alt texture
456            wgpu::BindGroupLayoutEntry {
457                binding: 5,
458                visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
459                ty: wgpu::BindingType::Texture {
460                    sample_type: wgpu::TextureSampleType::Float { filterable: true },
461                    view_dimension: wgpu::TextureViewDimension::D2,
462                    multisampled: false,
463                },
464                count: None,
465            },
466            wgpu::BindGroupLayoutEntry {
467                binding: 6,
468                visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
469                ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
470                count: None,
471            },
472            // Horizon texture
473            wgpu::BindGroupLayoutEntry {
474                binding: 7,
475                visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
476                ty: wgpu::BindingType::Texture {
477                    sample_type: wgpu::TextureSampleType::Float { filterable: true },
478                    view_dimension: wgpu::TextureViewDimension::D2,
479                    multisampled: false,
480                },
481                count: None,
482            },
483            wgpu::BindGroupLayoutEntry {
484                binding: 8,
485                visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
486                ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
487                count: None,
488            },
489            // light shadows (ie shadows from a light?)
490            wgpu::BindGroupLayoutEntry {
491                binding: 9,
492                visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
493                // TODO: is this relevant?
494                ty: wgpu::BindingType::Buffer {
495                    ty: wgpu::BufferBindingType::Uniform,
496                    has_dynamic_offset: false,
497                    min_binding_size: None,
498                },
499                count: None,
500            },
501            // lod map (t_map)
502            wgpu::BindGroupLayoutEntry {
503                binding: 10,
504                visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
505                ty: wgpu::BindingType::Texture {
506                    sample_type: wgpu::TextureSampleType::Float { filterable: true },
507                    view_dimension: wgpu::TextureViewDimension::D2,
508                    multisampled: false,
509                },
510                count: None,
511            },
512            wgpu::BindGroupLayoutEntry {
513                binding: 11,
514                visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
515                ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
516                count: None,
517            },
518            // clouds t_weather
519            wgpu::BindGroupLayoutEntry {
520                binding: 12,
521                visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
522                ty: wgpu::BindingType::Texture {
523                    sample_type: wgpu::TextureSampleType::Float { filterable: true },
524                    view_dimension: wgpu::TextureViewDimension::D2,
525                    multisampled: false,
526                },
527                count: None,
528            },
529            wgpu::BindGroupLayoutEntry {
530                binding: 13,
531                visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
532                ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
533                count: None,
534            },
535            // rain occlusion
536            wgpu::BindGroupLayoutEntry {
537                binding: 14,
538                visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
539                ty: wgpu::BindingType::Buffer {
540                    ty: wgpu::BufferBindingType::Uniform,
541                    has_dynamic_offset: false,
542                    min_binding_size: None,
543                },
544                count: None,
545            },
546        ]
547    }
548
549    pub fn new(device: &wgpu::Device) -> Self {
550        let globals = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
551            label: Some("Globals layout"),
552            entries: &Self::base_globals_layout(),
553        });
554
555        let shadow_textures = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
556            label: None,
557            entries: &[
558                // point shadow_maps
559                wgpu::BindGroupLayoutEntry {
560                    binding: 0,
561                    visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
562                    ty: wgpu::BindingType::Texture {
563                        sample_type: wgpu::TextureSampleType::Depth,
564                        view_dimension: wgpu::TextureViewDimension::Cube,
565                        multisampled: false,
566                    },
567                    count: None,
568                },
569                wgpu::BindGroupLayoutEntry {
570                    binding: 1,
571                    visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
572                    ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Comparison),
573                    count: None,
574                },
575                // directed shadow maps
576                wgpu::BindGroupLayoutEntry {
577                    binding: 2,
578                    visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
579                    ty: wgpu::BindingType::Texture {
580                        sample_type: wgpu::TextureSampleType::Depth,
581                        view_dimension: wgpu::TextureViewDimension::D2,
582                        multisampled: false,
583                    },
584                    count: None,
585                },
586                wgpu::BindGroupLayoutEntry {
587                    binding: 3,
588                    visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
589                    ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Comparison),
590                    count: None,
591                },
592                // Rain occlusion maps
593                wgpu::BindGroupLayoutEntry {
594                    binding: 4,
595                    visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
596                    ty: wgpu::BindingType::Texture {
597                        sample_type: wgpu::TextureSampleType::Depth,
598                        view_dimension: wgpu::TextureViewDimension::D2,
599                        multisampled: false,
600                    },
601                    count: None,
602                },
603                wgpu::BindGroupLayoutEntry {
604                    binding: 5,
605                    visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
606                    ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Comparison),
607                    count: None,
608                },
609            ],
610        });
611
612        Self {
613            globals,
614            figure_sprite_atlas_layout: VoxelAtlasLayout::new(device),
615            terrain_atlas_layout: VoxelAtlasLayout::new(device),
616            shadow_textures,
617        }
618    }
619
620    // Note: this allocation serves the purpose of not having to duplicate code
621    pub fn bind_base_globals<'a>(
622        global_model: &'a GlobalModel,
623        lod_data: &'a lod_terrain::LodData,
624        noise: &'a Texture,
625    ) -> Vec<wgpu::BindGroupEntry<'a>> {
626        vec![
627            // Global uniform
628            wgpu::BindGroupEntry {
629                binding: 0,
630                resource: global_model.globals.buf().as_entire_binding(),
631            },
632            // Noise tex
633            wgpu::BindGroupEntry {
634                binding: 1,
635                resource: wgpu::BindingResource::TextureView(&noise.view),
636            },
637            wgpu::BindGroupEntry {
638                binding: 2,
639                resource: wgpu::BindingResource::Sampler(&noise.sampler),
640            },
641            // Light uniform
642            wgpu::BindGroupEntry {
643                binding: 3,
644                resource: global_model.lights.buf().as_entire_binding(),
645            },
646            // Shadow uniform
647            wgpu::BindGroupEntry {
648                binding: 4,
649                resource: global_model.shadows.buf().as_entire_binding(),
650            },
651            // Alt texture
652            wgpu::BindGroupEntry {
653                binding: 5,
654                resource: wgpu::BindingResource::TextureView(&lod_data.alt.view),
655            },
656            wgpu::BindGroupEntry {
657                binding: 6,
658                resource: wgpu::BindingResource::Sampler(&lod_data.alt.sampler),
659            },
660            // Horizon texture
661            wgpu::BindGroupEntry {
662                binding: 7,
663                resource: wgpu::BindingResource::TextureView(&lod_data.horizon.view),
664            },
665            wgpu::BindGroupEntry {
666                binding: 8,
667                resource: wgpu::BindingResource::Sampler(&lod_data.horizon.sampler),
668            },
669            // light shadows
670            wgpu::BindGroupEntry {
671                binding: 9,
672                resource: global_model.shadow_mats.buf().as_entire_binding(),
673            },
674            // lod map (t_map)
675            wgpu::BindGroupEntry {
676                binding: 10,
677                resource: wgpu::BindingResource::TextureView(&lod_data.map.view),
678            },
679            wgpu::BindGroupEntry {
680                binding: 11,
681                resource: wgpu::BindingResource::Sampler(&lod_data.map.sampler),
682            },
683            wgpu::BindGroupEntry {
684                binding: 12,
685                resource: wgpu::BindingResource::TextureView(&lod_data.weather.view),
686            },
687            wgpu::BindGroupEntry {
688                binding: 13,
689                resource: wgpu::BindingResource::Sampler(&lod_data.weather.sampler),
690            },
691            // rain occlusion
692            wgpu::BindGroupEntry {
693                binding: 14,
694                resource: global_model.rain_occlusion_mats.buf().as_entire_binding(),
695            },
696        ]
697    }
698
699    pub fn bind(
700        &self,
701        device: &wgpu::Device,
702        global_model: &GlobalModel,
703        lod_data: &lod_terrain::LodData,
704        noise: &Texture,
705    ) -> GlobalsBindGroup {
706        let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
707            label: None,
708            layout: &self.globals,
709            entries: &Self::bind_base_globals(global_model, lod_data, noise),
710        });
711
712        GlobalsBindGroup { bind_group }
713    }
714
715    pub fn bind_shadow_textures(
716        &self,
717        device: &wgpu::Device,
718        point_shadow_map: &Texture,
719        directed_shadow_map: &Texture,
720        rain_occlusion_map: &Texture,
721    ) -> ShadowTexturesBindGroup {
722        let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
723            label: None,
724            layout: &self.shadow_textures,
725            entries: &[
726                wgpu::BindGroupEntry {
727                    binding: 0,
728                    resource: wgpu::BindingResource::TextureView(&point_shadow_map.view),
729                },
730                wgpu::BindGroupEntry {
731                    binding: 1,
732                    resource: wgpu::BindingResource::Sampler(&point_shadow_map.sampler),
733                },
734                wgpu::BindGroupEntry {
735                    binding: 2,
736                    resource: wgpu::BindingResource::TextureView(&directed_shadow_map.view),
737                },
738                wgpu::BindGroupEntry {
739                    binding: 3,
740                    resource: wgpu::BindingResource::Sampler(&directed_shadow_map.sampler),
741                },
742                wgpu::BindGroupEntry {
743                    binding: 4,
744                    resource: wgpu::BindingResource::TextureView(&rain_occlusion_map.view),
745                },
746                wgpu::BindGroupEntry {
747                    binding: 5,
748                    resource: wgpu::BindingResource::Sampler(&rain_occlusion_map.sampler),
749                },
750            ],
751        });
752
753        ShadowTexturesBindGroup { bind_group }
754    }
755
756    pub fn bind_atlas_textures<Locals, S: AtlasData>(
757        &self,
758        device: &wgpu::Device,
759        layout: &VoxelAtlasLayout<S>,
760        textures: [Texture; S::TEXTURES],
761    ) -> AtlasTextures<Locals, S> {
762        let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
763            label: None,
764            layout: layout.layout(),
765            entries: &textures
766                .iter()
767                .enumerate()
768                .flat_map(|(i, tex)| {
769                    [
770                        wgpu::BindGroupEntry {
771                            binding: i as u32 * 2,
772                            resource: wgpu::BindingResource::TextureView(&tex.view),
773                        },
774                        wgpu::BindGroupEntry {
775                            binding: i as u32 * 2 + 1,
776                            resource: wgpu::BindingResource::Sampler(&tex.sampler),
777                        },
778                    ]
779                })
780                .collect::<Vec<_>>(),
781        });
782
783        AtlasTextures {
784            textures,
785            bind_group,
786            phantom: std::marker::PhantomData,
787        }
788    }
789}