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 globals_dummy: [f32; 2],
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 ) -> 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 (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 screen_res: [
160 screen_res.x as f32,
161 screen_res.y as f32,
162 shadow_planes.x,
163 shadow_planes.y,
164 ],
165 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
282pub struct GlobalModel {
284 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
308pub 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
334pub trait AtlasData {
340 const TEXTURES: usize;
342 type SliceMut<'a>: Iterator
345 where
346 Self: 'a;
347
348 fn blank_with_size(sz: Vec2<u16>) -> Self;
350
351 fn as_texture_data(&self) -> [(wgpu::TextureFormat, &[u8]); Self::TEXTURES];
354
355 fn layout() -> Vec<wgpu::BindGroupLayoutEntry>;
358
359 fn slice_mut(&mut self, range: std::ops::Range<usize>) -> Self::SliceMut<'_>;
361
362 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 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 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 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 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 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 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 wgpu::BindGroupLayoutEntry {
502 binding: 9,
503 visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
504 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 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 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 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 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 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 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 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 wgpu::BindGroupEntry {
640 binding: 0,
641 resource: global_model.globals.buf().as_entire_binding(),
642 },
643 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 wgpu::BindGroupEntry {
654 binding: 3,
655 resource: global_model.lights.buf().as_entire_binding(),
656 },
657 wgpu::BindGroupEntry {
659 binding: 4,
660 resource: global_model.shadows.buf().as_entire_binding(),
661 },
662 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 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 wgpu::BindGroupEntry {
682 binding: 9,
683 resource: global_model.shadow_mats.buf().as_entire_binding(),
684 },
685 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 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}