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
29pub 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 view_mat: [[f32; 4]; 4],
40 proj_mat: [[f32; 4]; 4],
41 all_mat: [[f32; 4]; 4],
43 cam_pos: [f32; 4],
45 focus_off: [f32; 4],
47 focus_pos: [f32; 4],
49 view_distance: [f32; 4],
57 time_of_day: [f32; 4], sun_dir: [f32; 4],
60 moon_dir: [f32; 4],
62 tick: [f32; 4],
63 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 screen_fade: f32,
78 globals_dummy: f32,
79}
80const _: () = 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 #[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 screen_fade: f32,
129 ) -> Self {
130 Self {
131 view_mat: view_mat.into_col_arrays(),
132 proj_mat: proj_mat.into_col_arrays(),
133 all_mat: (proj_mat * view_mat).into_col_arrays(),
134 cam_pos: Vec4::from(cam_pos).into_array(),
135 focus_off: Vec4::from(focus_pos).map(|e: f32| e.trunc()).into_array(),
136 focus_pos: Vec4::from(focus_pos).map(|e: f32| e.fract()).into_array(),
137 view_distance: [view_distance, tgt_detail, map_bounds.x, map_bounds.y],
138 time_of_day: [
139 (time_of_day % (3600.0 * 24.0)) as f32,
140 (time_of_day / (3600.0 * 24.0) % 1000.0) as f32,
148 0.0,
149 0.0,
150 ],
151 sun_dir: Vec4::from_direction(TimeOfDay::new(time_of_day).get_sun_dir()).into_array(),
152 moon_dir: Vec4::from_direction(TimeOfDay::new(time_of_day).get_moon_dir()).into_array(),
153 tick: [
154 (tick % TIME_OVERFLOW) as f32,
155 (tick / TIME_OVERFLOW).floor() as f32,
156 client_tick as f32,
157 0.0,
158 ],
159 screen_res: [
161 screen_res.x as f32,
162 screen_res.y as f32,
163 shadow_planes.x,
164 shadow_planes.y,
165 ],
166 light_shadow_count: [
168 usize::min(light_count, MAX_POINT_LIGHT_COUNT) as u32,
169 usize::min(shadow_count, MAX_FIGURE_SHADOW_COUNT) as u32,
170 usize::min(directed_light_count, MAX_DIRECTED_LIGHT_COUNT) as u32,
171 0,
172 ],
173 shadow_proj_factors: [
174 shadow_planes.y / (shadow_planes.y - shadow_planes.x),
175 shadow_planes.y * shadow_planes.x / (shadow_planes.y - shadow_planes.x),
176 0.0,
177 0.0,
178 ],
179 medium: [if medium.is_liquid() {
180 1
181 } else if medium.is_filled() {
182 2
183 } else {
184 0
185 }; 4],
186 select_pos: select_pos
187 .map(|sp| Vec4::from(sp) + Vec4::unit_w())
188 .unwrap_or_else(Vec4::zero)
189 .into_array(),
190 gamma_exposure: [gamma, exposure, 0.0, 0.0],
191 last_lightning: last_lightning
192 .0
193 .with_w((last_lightning.1 % TIME_OVERFLOW) as f32)
194 .into_array(),
195 wind_vel: wind_vel.into_array(),
196 ambiance: ambiance.clamped(0.0, 1.0),
197 cam_mode: cam_mode as u32,
198 sprite_render_distance,
199 player_ori,
200 screen_fade: screen_fade.clamp(0.0, 1.0),
201 globals_dummy: 0.0,
202 }
203 }
204}
205
206impl Default for Globals {
207 fn default() -> Self {
208 Self::new(
209 Mat4::identity(),
210 Mat4::identity(),
211 Vec3::zero(),
212 Vec3::zero(),
213 0.0,
214 100.0,
215 Vec2::new(140.0, 2048.0),
216 0.0,
217 0.0,
218 0.0,
219 Vec2::new(800, 500),
220 Vec2::new(1.0, 25.0),
221 0,
222 0,
223 0,
224 BlockKind::Air,
225 None,
226 1.0,
227 1.0,
228 (Vec3::zero(), -1000.0),
229 Vec2::zero(),
230 1.0,
231 CameraMode::ThirdPerson,
232 250.0,
233 0.0,
234 1.0,
235 )
236 }
237}
238
239impl Light {
240 pub fn new(pos: Vec3<f32>, col: Rgb<f32>, strength: f32) -> Self {
241 let linearized_col = srgb_to_linear(col);
242
243 Self {
244 pos: Vec4::from(pos).into_array(),
245 col: (Rgba::new(linearized_col.r, linearized_col.g, linearized_col.b, 0.0) * strength)
246 .into_array(),
247 dir: [0.0, 0.0, 0.0, 10.0],
248 }
249 }
250
251 pub fn with_dir(mut self, dir: Vec3<f32>, fov: f32) -> Self {
252 self.dir = dir.normalized().with_w(fov).into_array();
253 self
254 }
255
256 pub fn get_pos(&self) -> Vec3<f32> { Vec3::new(self.pos[0], self.pos[1], self.pos[2]) }
257
258 #[must_use]
259 pub fn with_strength(mut self, strength: f32) -> Self {
260 self.col = (Vec4::<f32>::from(self.col) * strength).into_array();
261 self
262 }
263}
264
265impl Default for Light {
266 fn default() -> Self { Self::new(Vec3::zero(), Rgb::zero(), 0.0) }
267}
268
269impl Shadow {
270 pub fn new(pos: Vec3<f32>, radius: f32) -> Self {
271 Self {
272 pos_radius: [pos.x, pos.y, pos.z, radius],
273 }
274 }
275
276 pub fn get_pos(&self) -> Vec3<f32> {
277 Vec3::new(self.pos_radius[0], self.pos_radius[1], self.pos_radius[2])
278 }
279}
280
281impl Default for Shadow {
282 fn default() -> Self { Self::new(Vec3::zero(), 0.0) }
283}
284
285pub struct GlobalModel {
287 pub globals: Consts<Globals>,
289 pub lights: Consts<Light>,
290 pub shadows: Consts<Shadow>,
291 pub shadow_mats: shadow::BoundLocals,
292 pub rain_occlusion_mats: rain_occlusion::BoundLocals,
293 pub point_light_matrices: Box<[shadow::PointLightMatrix; 126]>,
294}
295
296pub struct GlobalsBindGroup {
297 pub(super) bind_group: wgpu::BindGroup,
298}
299
300pub struct ShadowTexturesBindGroup {
301 pub(super) bind_group: wgpu::BindGroup,
302}
303
304pub struct GlobalsLayouts {
305 pub globals: wgpu::BindGroupLayout,
306 pub figure_sprite_atlas_layout: VoxelAtlasLayout<FigureSpriteAtlasData>,
307 pub terrain_atlas_layout: VoxelAtlasLayout<TerrainAtlasData>,
308 pub shadow_textures: wgpu::BindGroupLayout,
309}
310
311pub struct AtlasTextures<Locals, S: AtlasData>
314where
315 [(); S::TEXTURES]:,
316{
317 pub(super) bind_group: wgpu::BindGroup,
318 pub textures: [Texture; S::TEXTURES],
319 phantom: std::marker::PhantomData<Locals>,
320}
321
322pub struct VoxelAtlasLayout<S: AtlasData>(wgpu::BindGroupLayout, PhantomData<S>);
323
324impl<S: AtlasData> VoxelAtlasLayout<S> {
325 pub fn new(device: &wgpu::Device) -> Self {
326 let layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
327 label: None,
328 entries: &S::layout(),
329 });
330
331 Self(layout, PhantomData)
332 }
333
334 pub fn layout(&self) -> &wgpu::BindGroupLayout { &self.0 }
335}
336
337pub trait AtlasData {
343 const TEXTURES: usize;
345 type SliceMut<'a>: Iterator
348 where
349 Self: 'a;
350
351 fn blank_with_size(sz: Vec2<u16>) -> Self;
353
354 fn as_texture_data(&self) -> [(wgpu::TextureFormat, &[u8]); Self::TEXTURES];
357
358 fn layout() -> Vec<wgpu::BindGroupLayoutEntry>;
361
362 fn slice_mut(&mut self, range: std::ops::Range<usize>) -> Self::SliceMut<'_>;
364
365 fn create_textures(
367 &self,
368 renderer: &mut Renderer,
369 atlas_size: Vec2<u16>,
370 ) -> [Texture; Self::TEXTURES] {
371 self.as_texture_data().map(|(fmt, data)| {
372 let texture_info = wgpu::TextureDescriptor {
373 label: None,
374 size: wgpu::Extent3d {
375 width: u32::from(atlas_size.x),
376 height: u32::from(atlas_size.y),
377 depth_or_array_layers: 1,
378 },
379 mip_level_count: 1,
380 sample_count: 1,
381 dimension: wgpu::TextureDimension::D2,
382 format: fmt,
383 usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
384 view_formats: &[],
385 };
386
387 let sampler_info = wgpu::SamplerDescriptor {
388 label: None,
389 address_mode_u: wgpu::AddressMode::ClampToEdge,
390 address_mode_v: wgpu::AddressMode::ClampToEdge,
391 address_mode_w: wgpu::AddressMode::ClampToEdge,
392 mag_filter: wgpu::FilterMode::Linear,
393 min_filter: wgpu::FilterMode::Linear,
394 mipmap_filter: wgpu::FilterMode::Nearest,
395 border_color: Some(wgpu::SamplerBorderColor::TransparentBlack),
396 ..Default::default()
397 };
398
399 let view_info = wgpu::TextureViewDescriptor {
400 label: None,
401 format: Some(fmt),
402 dimension: Some(wgpu::TextureViewDimension::D2),
403 usage: None,
404 aspect: wgpu::TextureAspect::All,
405 base_mip_level: 0,
406 mip_level_count: None,
407 base_array_layer: 0,
408 array_layer_count: None,
409 };
410
411 renderer.create_texture_with_data_raw(&texture_info, &view_info, &sampler_info, data)
412 })
413 }
414}
415
416impl GlobalsLayouts {
417 pub fn base_globals_layout() -> Vec<wgpu::BindGroupLayoutEntry> {
418 vec![
419 wgpu::BindGroupLayoutEntry {
421 binding: 0,
422 visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
423 ty: wgpu::BindingType::Buffer {
424 ty: wgpu::BufferBindingType::Uniform,
425 has_dynamic_offset: false,
426 min_binding_size: None,
427 },
428 count: None,
429 },
430 wgpu::BindGroupLayoutEntry {
432 binding: 1,
433 visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
434 ty: wgpu::BindingType::Texture {
435 sample_type: wgpu::TextureSampleType::Float { filterable: true },
436 view_dimension: wgpu::TextureViewDimension::D2,
437 multisampled: false,
438 },
439 count: None,
440 },
441 wgpu::BindGroupLayoutEntry {
442 binding: 2,
443 visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
444 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
445 count: None,
446 },
447 wgpu::BindGroupLayoutEntry {
449 binding: 3,
450 visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
451 ty: wgpu::BindingType::Buffer {
452 ty: wgpu::BufferBindingType::Uniform,
453 has_dynamic_offset: false,
454 min_binding_size: None,
455 },
456 count: None,
457 },
458 wgpu::BindGroupLayoutEntry {
460 binding: 4,
461 visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
462 ty: wgpu::BindingType::Buffer {
463 ty: wgpu::BufferBindingType::Uniform,
464 has_dynamic_offset: false,
465 min_binding_size: None,
466 },
467 count: None,
468 },
469 wgpu::BindGroupLayoutEntry {
471 binding: 5,
472 visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
473 ty: wgpu::BindingType::Texture {
474 sample_type: wgpu::TextureSampleType::Float { filterable: true },
475 view_dimension: wgpu::TextureViewDimension::D2,
476 multisampled: false,
477 },
478 count: None,
479 },
480 wgpu::BindGroupLayoutEntry {
481 binding: 6,
482 visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
483 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
484 count: None,
485 },
486 wgpu::BindGroupLayoutEntry {
488 binding: 7,
489 visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
490 ty: wgpu::BindingType::Texture {
491 sample_type: wgpu::TextureSampleType::Float { filterable: true },
492 view_dimension: wgpu::TextureViewDimension::D2,
493 multisampled: false,
494 },
495 count: None,
496 },
497 wgpu::BindGroupLayoutEntry {
498 binding: 8,
499 visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
500 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
501 count: None,
502 },
503 wgpu::BindGroupLayoutEntry {
505 binding: 9,
506 visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
507 ty: wgpu::BindingType::Buffer {
509 ty: wgpu::BufferBindingType::Uniform,
510 has_dynamic_offset: false,
511 min_binding_size: None,
512 },
513 count: None,
514 },
515 wgpu::BindGroupLayoutEntry {
517 binding: 10,
518 visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
519 ty: wgpu::BindingType::Texture {
520 sample_type: wgpu::TextureSampleType::Float { filterable: true },
521 view_dimension: wgpu::TextureViewDimension::D2,
522 multisampled: false,
523 },
524 count: None,
525 },
526 wgpu::BindGroupLayoutEntry {
527 binding: 11,
528 visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
529 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
530 count: None,
531 },
532 wgpu::BindGroupLayoutEntry {
534 binding: 12,
535 visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
536 ty: wgpu::BindingType::Texture {
537 sample_type: wgpu::TextureSampleType::Float { filterable: true },
538 view_dimension: wgpu::TextureViewDimension::D2,
539 multisampled: false,
540 },
541 count: None,
542 },
543 wgpu::BindGroupLayoutEntry {
544 binding: 13,
545 visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
546 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
547 count: None,
548 },
549 wgpu::BindGroupLayoutEntry {
551 binding: 14,
552 visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
553 ty: wgpu::BindingType::Buffer {
554 ty: wgpu::BufferBindingType::Uniform,
555 has_dynamic_offset: false,
556 min_binding_size: None,
557 },
558 count: None,
559 },
560 ]
561 }
562
563 pub fn new(device: &wgpu::Device) -> Self {
564 let globals = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
565 label: Some("Globals layout"),
566 entries: &Self::base_globals_layout(),
567 });
568
569 let shadow_textures = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
570 label: None,
571 entries: &[
572 wgpu::BindGroupLayoutEntry {
574 binding: 0,
575 visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
576 ty: wgpu::BindingType::Texture {
577 sample_type: wgpu::TextureSampleType::Depth,
578 view_dimension: wgpu::TextureViewDimension::Cube,
579 multisampled: false,
580 },
581 count: None,
582 },
583 wgpu::BindGroupLayoutEntry {
584 binding: 1,
585 visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
586 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Comparison),
587 count: None,
588 },
589 wgpu::BindGroupLayoutEntry {
591 binding: 2,
592 visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
593 ty: wgpu::BindingType::Texture {
594 sample_type: wgpu::TextureSampleType::Depth,
595 view_dimension: wgpu::TextureViewDimension::D2,
596 multisampled: false,
597 },
598 count: None,
599 },
600 wgpu::BindGroupLayoutEntry {
601 binding: 3,
602 visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
603 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Comparison),
604 count: None,
605 },
606 wgpu::BindGroupLayoutEntry {
608 binding: 4,
609 visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
610 ty: wgpu::BindingType::Texture {
611 sample_type: wgpu::TextureSampleType::Depth,
612 view_dimension: wgpu::TextureViewDimension::D2,
613 multisampled: false,
614 },
615 count: None,
616 },
617 wgpu::BindGroupLayoutEntry {
618 binding: 5,
619 visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
620 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Comparison),
621 count: None,
622 },
623 ],
624 });
625
626 Self {
627 globals,
628 figure_sprite_atlas_layout: VoxelAtlasLayout::new(device),
629 terrain_atlas_layout: VoxelAtlasLayout::new(device),
630 shadow_textures,
631 }
632 }
633
634 pub fn bind_base_globals<'a>(
636 global_model: &'a GlobalModel,
637 lod_data: &'a lod_terrain::LodData,
638 noise: &'a Texture,
639 ) -> Vec<wgpu::BindGroupEntry<'a>> {
640 vec![
641 wgpu::BindGroupEntry {
643 binding: 0,
644 resource: global_model.globals.buf().as_entire_binding(),
645 },
646 wgpu::BindGroupEntry {
648 binding: 1,
649 resource: wgpu::BindingResource::TextureView(&noise.view),
650 },
651 wgpu::BindGroupEntry {
652 binding: 2,
653 resource: wgpu::BindingResource::Sampler(&noise.sampler),
654 },
655 wgpu::BindGroupEntry {
657 binding: 3,
658 resource: global_model.lights.buf().as_entire_binding(),
659 },
660 wgpu::BindGroupEntry {
662 binding: 4,
663 resource: global_model.shadows.buf().as_entire_binding(),
664 },
665 wgpu::BindGroupEntry {
667 binding: 5,
668 resource: wgpu::BindingResource::TextureView(&lod_data.alt.view),
669 },
670 wgpu::BindGroupEntry {
671 binding: 6,
672 resource: wgpu::BindingResource::Sampler(&lod_data.alt.sampler),
673 },
674 wgpu::BindGroupEntry {
676 binding: 7,
677 resource: wgpu::BindingResource::TextureView(&lod_data.horizon.view),
678 },
679 wgpu::BindGroupEntry {
680 binding: 8,
681 resource: wgpu::BindingResource::Sampler(&lod_data.horizon.sampler),
682 },
683 wgpu::BindGroupEntry {
685 binding: 9,
686 resource: global_model.shadow_mats.buf().as_entire_binding(),
687 },
688 wgpu::BindGroupEntry {
690 binding: 10,
691 resource: wgpu::BindingResource::TextureView(&lod_data.map.view),
692 },
693 wgpu::BindGroupEntry {
694 binding: 11,
695 resource: wgpu::BindingResource::Sampler(&lod_data.map.sampler),
696 },
697 wgpu::BindGroupEntry {
698 binding: 12,
699 resource: wgpu::BindingResource::TextureView(&lod_data.weather.view),
700 },
701 wgpu::BindGroupEntry {
702 binding: 13,
703 resource: wgpu::BindingResource::Sampler(&lod_data.weather.sampler),
704 },
705 wgpu::BindGroupEntry {
707 binding: 14,
708 resource: global_model.rain_occlusion_mats.buf().as_entire_binding(),
709 },
710 ]
711 }
712
713 pub fn bind(
714 &self,
715 device: &wgpu::Device,
716 global_model: &GlobalModel,
717 lod_data: &lod_terrain::LodData,
718 noise: &Texture,
719 ) -> GlobalsBindGroup {
720 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
721 label: None,
722 layout: &self.globals,
723 entries: &Self::bind_base_globals(global_model, lod_data, noise),
724 });
725
726 GlobalsBindGroup { bind_group }
727 }
728
729 pub fn bind_shadow_textures(
730 &self,
731 device: &wgpu::Device,
732 point_shadow_map: &Texture,
733 directed_shadow_map: &Texture,
734 rain_occlusion_map: &Texture,
735 ) -> ShadowTexturesBindGroup {
736 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
737 label: None,
738 layout: &self.shadow_textures,
739 entries: &[
740 wgpu::BindGroupEntry {
741 binding: 0,
742 resource: wgpu::BindingResource::TextureView(&point_shadow_map.view),
743 },
744 wgpu::BindGroupEntry {
745 binding: 1,
746 resource: wgpu::BindingResource::Sampler(&point_shadow_map.sampler),
747 },
748 wgpu::BindGroupEntry {
749 binding: 2,
750 resource: wgpu::BindingResource::TextureView(&directed_shadow_map.view),
751 },
752 wgpu::BindGroupEntry {
753 binding: 3,
754 resource: wgpu::BindingResource::Sampler(&directed_shadow_map.sampler),
755 },
756 wgpu::BindGroupEntry {
757 binding: 4,
758 resource: wgpu::BindingResource::TextureView(&rain_occlusion_map.view),
759 },
760 wgpu::BindGroupEntry {
761 binding: 5,
762 resource: wgpu::BindingResource::Sampler(&rain_occlusion_map.sampler),
763 },
764 ],
765 });
766
767 ShadowTexturesBindGroup { bind_group }
768 }
769
770 pub fn bind_atlas_textures<Locals, S: AtlasData>(
771 &self,
772 device: &wgpu::Device,
773 layout: &VoxelAtlasLayout<S>,
774 textures: [Texture; S::TEXTURES],
775 ) -> AtlasTextures<Locals, S> {
776 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
777 label: None,
778 layout: layout.layout(),
779 entries: &textures
780 .iter()
781 .enumerate()
782 .flat_map(|(i, tex)| {
783 [
784 wgpu::BindGroupEntry {
785 binding: i as u32 * 2,
786 resource: wgpu::BindingResource::TextureView(&tex.view),
787 },
788 wgpu::BindGroupEntry {
789 binding: i as u32 * 2 + 1,
790 resource: wgpu::BindingResource::Sampler(&tex.sampler),
791 },
792 ]
793 })
794 .collect::<Vec<_>>(),
795 });
796
797 AtlasTextures {
798 textures,
799 bind_group,
800 phantom: std::marker::PhantomData,
801 }
802 }
803}