steel_core/chunk/light/sky_propagation/
context.rs1use super::{
2 ChunkPos, LightCacheLayout, LightLayer, LightLayerEdit, LightSectionReadCache,
3 PackedLightPropagationQueues, SectionPos, SkyLightPropagationContextError,
4};
5
6pub struct SkyLightPropagationContext<'a, 'sections, 'light> {
8 pub(super) layout: LightCacheLayout,
9 pub(super) sections: &'a LightSectionReadCache<'sections>,
10 pub(super) light: &'a mut LightLayerEdit<'light>,
11 pub(super) queues: &'a mut PackedLightPropagationQueues,
12 pub(super) missing_section_checked: Vec<bool>,
13}
14
15impl<'a, 'sections, 'light> SkyLightPropagationContext<'a, 'sections, 'light> {
16 pub fn new(
18 sections: &'a LightSectionReadCache<'sections>,
19 light: &'a mut LightLayerEdit<'light>,
20 queues: &'a mut PackedLightPropagationQueues,
21 ) -> Result<Self, SkyLightPropagationContextError> {
22 if light.layer() != LightLayer::Sky {
23 return Err(SkyLightPropagationContextError::WrongLayer {
24 layer: light.layer(),
25 });
26 }
27
28 if sections.layout() != light.layout() {
29 return Err(SkyLightPropagationContextError::layout_mismatch(
30 sections.layout(),
31 light.layout(),
32 ));
33 }
34
35 let layout = light.layout();
36 let section_count = layout.range().section_count();
37
38 Ok(Self {
39 layout,
40 sections,
41 light,
42 queues,
43 missing_section_checked: vec![false; section_count],
44 })
45 }
46
47 pub fn handle_unlit_empty_section_changes(&mut self, chunk_pos: ChunkPos) {
49 self.initialize_chunk_sections(chunk_pos, true);
50 self.deinit_and_lazy_init_empty_sections(chunk_pos, true);
51 }
52
53 pub fn handle_loaded_empty_section_changes(&mut self, chunk_pos: ChunkPos) {
55 self.initialize_chunk_sections(chunk_pos, false);
56 self.deinit_and_lazy_init_empty_sections(chunk_pos, false);
57 }
58
59 fn initialize_chunk_sections(&mut self, chunk_pos: ChunkPos, unlit: bool) {
60 for section_y in (self.layout.range().min_chunk_section_y()
61 ..self.layout.range().max_chunk_section_y_exclusive())
62 .rev()
63 {
64 let section_pos = SectionPos::new(chunk_pos.0.x, section_y, chunk_pos.0.y);
65 if !self.section_is_non_empty(section_pos) {
66 continue;
67 }
68
69 for offset_z in -1..=1 {
70 for offset_x in -1..=1 {
71 let extrude = (offset_x | offset_z) != 0 || !unlit;
72 for offset_y in (-1..=1).rev() {
73 self.init_light_section(
74 SectionPos::new(
75 chunk_pos.0.x + offset_x,
76 section_y + offset_y,
77 chunk_pos.0.y + offset_z,
78 ),
79 extrude,
80 false,
81 );
82 }
83 }
84 }
85 }
86 }
87
88 pub(super) fn deinit_and_lazy_init_empty_sections(&mut self, chunk_pos: ChunkPos, unlit: bool) {
89 for offset_z in -1..=1 {
90 for offset_x in -1..=1 {
91 let target_chunk =
92 ChunkPos::new(chunk_pos.0.x + offset_x, chunk_pos.0.y + offset_z);
93
94 for section_y in (self.layout.range().min_section_y()
95 ..self.layout.range().max_section_y_exclusive())
96 .rev()
97 {
98 let section_pos =
99 SectionPos::new(target_chunk.0.x, section_y, target_chunk.0.y);
100 match self.section_neighborhood_all_empty_if_known(target_chunk, section_y) {
101 Some(true) => {
102 self.light.set_section_missing(section_pos);
103 }
104 Some(false) => {
105 self.init_light_section(
106 section_pos,
107 (offset_x | offset_z) != 0 || !unlit,
108 false,
109 );
110 }
111 None => {
112 if !self.section_neighborhood_all_empty(target_chunk, section_y) {
113 self.init_light_section(
114 section_pos,
115 (offset_x | offset_z) != 0 || !unlit,
116 false,
117 );
118 }
119 }
120 }
121 }
122 }
123 }
124 }
125
126 fn section_neighborhood_all_empty(&self, chunk_pos: ChunkPos, section_y: i32) -> bool {
127 for offset_y in -1..=1 {
128 let neighbor_y = section_y + offset_y;
129 if neighbor_y < self.layout.range().min_chunk_section_y()
130 || neighbor_y >= self.layout.range().max_chunk_section_y_exclusive()
131 {
132 continue;
133 }
134
135 for offset_z in -1..=1 {
136 for offset_x in -1..=1 {
137 let section_pos = SectionPos::new(
138 chunk_pos.0.x + offset_x,
139 neighbor_y,
140 chunk_pos.0.y + offset_z,
141 );
142 if let Some(empty) = self.sections.section_empty(section_pos) {
143 if !empty {
144 return false;
145 }
146 } else if let Some(empty) = self.light.section_empty(section_pos) {
147 if !empty {
148 return false;
149 }
150 } else if self.sections.has_non_empty_section(section_pos) {
151 return false;
152 }
153 }
154 }
155 }
156
157 true
158 }
159
160 fn section_neighborhood_all_empty_if_known(
161 &self,
162 chunk_pos: ChunkPos,
163 section_y: i32,
164 ) -> Option<bool> {
165 for offset_y in -1..=1 {
166 let neighbor_y = section_y + offset_y;
167 if neighbor_y < self.layout.range().min_chunk_section_y()
168 || neighbor_y >= self.layout.range().max_chunk_section_y_exclusive()
169 {
170 continue;
171 }
172
173 for offset_z in -1..=1 {
174 for offset_x in -1..=1 {
175 let section_pos = SectionPos::new(
176 chunk_pos.0.x + offset_x,
177 neighbor_y,
178 chunk_pos.0.y + offset_z,
179 );
180 let empty = self.sections.section_empty(section_pos)?;
181 if !empty {
182 return Some(false);
183 }
184 }
185 }
186 }
187
188 Some(true)
189 }
190
191 pub fn reset_center_chunk_sections(&mut self) {
193 self.light
194 .reset_chunk_sections_to_missing(self.layout.center_chunk());
195 }
196}