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 globals_dummy: [f32; 3],
78}
79const _: () = 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 #[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 (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 screen_res: [
158 screen_res.x as f32,
159 screen_res.y as f32,
160 shadow_planes.x,
161 shadow_planes.y,
162 ],
163 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
278pub struct GlobalModel {
280 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
304pub 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
330pub trait AtlasData {
336 const TEXTURES: usize;
338 type SliceMut<'a>: Iterator
341 where
342 Self: 'a;
343
344 fn blank_with_size(sz: Vec2<u16>) -> Self;
346
347 fn as_texture_data(&self) -> [(wgpu::TextureFormat, &[u8]); Self::TEXTURES];
350
351 fn layout() -> Vec<wgpu::BindGroupLayoutEntry>;
354
355 fn slice_mut(&mut self, range: std::ops::Range<usize>) -> Self::SliceMut<'_>;
357
358 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 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 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 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 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 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 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 wgpu::BindGroupLayoutEntry {
498 binding: 9,
499 visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
500 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 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 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 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 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 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 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 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 wgpu::BindGroupEntry {
636 binding: 0,
637 resource: global_model.globals.buf().as_entire_binding(),
638 },
639 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 wgpu::BindGroupEntry {
650 binding: 3,
651 resource: global_model.lights.buf().as_entire_binding(),
652 },
653 wgpu::BindGroupEntry {
655 binding: 4,
656 resource: global_model.shadows.buf().as_entire_binding(),
657 },
658 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 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 wgpu::BindGroupEntry {
678 binding: 9,
679 resource: global_model.shadow_mats.buf().as_entire_binding(),
680 },
681 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 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}