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