1use std::fmt;
2
3use serde::{Deserialize, Serialize};
4use vek::{Lerp, Vec2, Vec3};
5
6use crate::{grid::Grid, terrain::TerrainChunkSize, vol::RectVolSize};
7
8#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default)]
10pub struct Weather {
11 pub cloud: f32,
13 pub rain: f32,
15 pub wind: Vec2<f32>,
17}
18
19impl Weather {
20 pub fn new(cloud: f32, rain: f32, wind: Vec2<f32>) -> Self { Self { cloud, rain, wind } }
21
22 pub fn get_kind(&self) -> WeatherKind {
23 if self.wind.magnitude_squared() >= 24.5f32.powi(2) {
25 WeatherKind::Storm
26 } else if (0.1..=1.0).contains(&self.rain) {
27 WeatherKind::Rain
28 } else if (0.2..=1.0).contains(&self.cloud) {
29 WeatherKind::Cloudy
30 } else {
31 WeatherKind::Clear
32 }
33 }
34
35 pub fn lerp_unclamped(&self, to: &Self, t: f32) -> Self {
36 Self {
37 cloud: f32::lerp_unclamped(self.cloud, to.cloud, t),
38 rain: f32::lerp_unclamped(self.rain, to.rain, t),
39 wind: Vec2::<f32>::lerp_unclamped(self.wind, to.wind, t),
40 }
41 }
42
43 pub fn rain_vel(&self) -> Vec3<f32> {
45 const FALL_RATE: f32 = 30.0;
46 self.wind.with_z(-FALL_RATE)
47 }
48
49 pub fn wind_vel(&self) -> Vec2<f32> { self.wind }
51}
52
53#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
54pub enum WeatherKind {
55 Clear,
56 Cloudy,
57 Rain,
58 Storm,
59}
60
61impl fmt::Display for WeatherKind {
62 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63 match self {
64 WeatherKind::Clear => write!(f, "Clear"),
65 WeatherKind::Cloudy => write!(f, "Cloudy"),
66 WeatherKind::Rain => write!(f, "Rain"),
67 WeatherKind::Storm => write!(f, "Storm"),
68 }
69 }
70}
71
72pub const CHUNKS_PER_CELL: u32 = 16;
75
76pub const CELL_SIZE: u32 = CHUNKS_PER_CELL * TerrainChunkSize::RECT_SIZE.x;
77
78#[derive(Debug, Clone)]
79pub struct WeatherGrid {
80 weather: Grid<Weather>,
81}
82
83#[derive(Default, Debug, Clone, Copy, Serialize, Deserialize)]
85pub struct CompressedWeather {
86 cloud: u8,
87 rain: u8,
88}
89
90impl CompressedWeather {
91 pub fn lerp_unclamped(&self, to: &CompressedWeather, t: f32) -> Weather {
92 Weather {
93 cloud: f32::lerp_unclamped(self.cloud as f32, to.cloud as f32, t) / 255.0,
94 rain: f32::lerp_unclamped(self.rain as f32, to.rain as f32, t) / 255.0,
95 wind: Vec2::zero(),
96 }
97 }
98}
99
100impl From<Weather> for CompressedWeather {
101 fn from(weather: Weather) -> Self {
102 Self {
103 cloud: (weather.cloud * 255.0).round() as u8,
104 rain: (weather.rain * 255.0).round() as u8,
105 }
106 }
107}
108
109impl From<CompressedWeather> for Weather {
110 fn from(weather: CompressedWeather) -> Self {
111 Self {
112 cloud: weather.cloud as f32 / 255.0,
113 rain: weather.rain as f32 / 255.0,
114 wind: Vec2::zero(),
115 }
116 }
117}
118
119#[derive(Debug, Clone, Serialize, Deserialize)]
120pub struct SharedWeatherGrid {
121 weather: Grid<CompressedWeather>,
122}
123
124impl From<&WeatherGrid> for SharedWeatherGrid {
125 fn from(value: &WeatherGrid) -> Self {
126 Self {
127 weather: Grid::from_raw(
128 value.weather.size(),
129 value
130 .weather
131 .raw()
132 .iter()
133 .copied()
134 .map(CompressedWeather::from)
135 .collect::<Vec<_>>(),
136 ),
137 }
138 }
139}
140
141impl From<&SharedWeatherGrid> for WeatherGrid {
142 fn from(value: &SharedWeatherGrid) -> Self {
143 Self {
144 weather: Grid::from_raw(
145 value.weather.size(),
146 value
147 .weather
148 .raw()
149 .iter()
150 .copied()
151 .map(Weather::from)
152 .collect::<Vec<_>>(),
153 ),
154 }
155 }
156}
157
158impl SharedWeatherGrid {
159 pub fn new(size: Vec2<u32>) -> Self {
160 size.map(|e| debug_assert!(i32::try_from(e).is_ok()));
161 Self {
162 weather: Grid::new(size.as_(), CompressedWeather::default()),
163 }
164 }
165
166 pub fn iter(&self) -> impl Iterator<Item = (Vec2<i32>, &CompressedWeather)> {
167 self.weather.iter()
168 }
169
170 pub fn iter_mut(&mut self) -> impl Iterator<Item = (Vec2<i32>, &mut CompressedWeather)> {
171 self.weather.iter_mut()
172 }
173
174 pub fn size(&self) -> Vec2<u32> { self.weather.size().as_() }
175}
176
177fn to_cell_pos(wpos: Vec2<f32>) -> Vec2<f32> { wpos / CELL_SIZE as f32 - 0.5 }
180
181const LOCALITY: [Vec2<i32>; 9] = [
183 Vec2::new(0, 0),
184 Vec2::new(0, 1),
185 Vec2::new(1, 0),
186 Vec2::new(0, -1),
187 Vec2::new(-1, 0),
188 Vec2::new(1, 1),
189 Vec2::new(1, -1),
190 Vec2::new(-1, 1),
191 Vec2::new(-1, -1),
192];
193
194impl WeatherGrid {
195 pub fn new(size: Vec2<u32>) -> Self {
196 size.map(|e| debug_assert!(i32::try_from(e).is_ok()));
197 Self {
198 weather: Grid::new(size.as_(), Weather::default()),
199 }
200 }
201
202 pub fn iter(&self) -> impl Iterator<Item = (Vec2<i32>, &Weather)> { self.weather.iter() }
203
204 pub fn iter_mut(&mut self) -> impl Iterator<Item = (Vec2<i32>, &mut Weather)> {
205 self.weather.iter_mut()
206 }
207
208 pub fn size(&self) -> Vec2<u32> { self.weather.size().as_() }
209
210 pub fn get(&self, cell_pos: Vec2<u32>) -> Weather {
211 self.weather
212 .get(cell_pos.as_())
213 .copied()
214 .unwrap_or_default()
215 }
216
217 pub fn get_interpolated(&self, wpos: Vec2<f32>) -> Weather {
220 let cell_pos = to_cell_pos(wpos);
221 let rpos = cell_pos.map(|e| e.fract() + (1.0 - e.signum()) / 2.0);
222 let cell_pos = cell_pos.map(|e| e.floor());
223
224 let cpos = cell_pos.as_::<i32>();
225 Weather::lerp_unclamped(
226 &Weather::lerp_unclamped(
227 self.weather.get(cpos).unwrap_or(&Weather::default()),
228 self.weather
229 .get(cpos + Vec2::unit_x())
230 .unwrap_or(&Weather::default()),
231 rpos.x,
232 ),
233 &Weather::lerp_unclamped(
234 self.weather
235 .get(cpos + Vec2::unit_y())
236 .unwrap_or(&Weather::default()),
237 self.weather
238 .get(cpos + Vec2::one())
239 .unwrap_or(&Weather::default()),
240 rpos.x,
241 ),
242 rpos.y,
243 )
244 }
245
246 pub fn get_max_near(&self, wpos: Vec2<f32>) -> Weather {
248 let cell_pos: Vec2<i32> = to_cell_pos(wpos).as_();
249 LOCALITY
250 .iter()
251 .map(|l| {
252 self.weather
253 .get(cell_pos + l)
254 .cloned()
255 .unwrap_or_default()
256 })
257 .reduce(|a, b| Weather {
258 cloud: a.cloud.max(b.cloud),
259 rain: a.rain.max(b.rain),
260 wind: a.wind.map2(b.wind, |a, b| a.max(b)),
261 })
262 .unwrap()
264 }
265}