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