veloren_voxygen/render/pipelines/
mod.rs

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