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