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