concinnity_core/components/
water_surface.rs1use crate::ecs::asset_id::AssetId;
4use alloc::vec;
5use alloc::vec::Vec;
6
7pub const MAX_WATER_WAVES: usize = 4;
10
11#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
18#[serde(default)]
19pub struct WaterWave {
20 pub amplitude: f32,
22 pub wavelength: f32,
24 pub speed: f32,
26 pub direction: [f32; 2],
28 pub steepness: f32,
31}
32
33impl Default for WaterWave {
34 fn default() -> Self {
35 Self {
36 amplitude: 0.15,
37 wavelength: 4.0,
38 speed: 1.0,
39 direction: [1.0, 0.0],
40 steepness: 0.4,
41 }
42 }
43}
44
45#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
65#[serde(default)]
66pub struct WaterSurface {
67 #[serde(skip)]
69 pub asset_id: AssetId,
70 pub centre: [f32; 3],
72 pub extent: [f32; 2],
74 pub subdivisions: u32,
77 pub waves: Vec<WaterWave>,
80 pub deep_colour: [f32; 3],
82 pub shallow_colour: [f32; 3],
84 pub depth_falloff_metres: f32,
86 pub foam_width_metres: f32,
88 pub foam_intensity: f32,
90 pub fresnel_power: f32,
93 pub roughness: f32,
97 pub refraction_strength: f32,
99 pub visible: bool,
101}
102
103impl Default for WaterSurface {
104 fn default() -> Self {
105 Self {
106 asset_id: AssetId::default(),
107 centre: [0.0, 0.0, 0.0],
108 extent: [10.0, 10.0],
109 subdivisions: 64,
110 waves: vec![WaterWave::default()],
111 deep_colour: [0.02, 0.05, 0.15],
112 shallow_colour: [0.20, 0.50, 0.55],
113 depth_falloff_metres: 4.0,
114 foam_width_metres: 0.30,
115 foam_intensity: 0.8,
116 fresnel_power: 5.0,
117 roughness: 0.05,
118 refraction_strength: 0.15,
119 visible: true,
120 }
121 }
122}
123
124#[cfg(test)]
125mod tests {
126 use super::*;
127
128 #[test]
129 fn a_blank_wave_travels_along_positive_x() {
130 let w = WaterWave::default();
131 assert_eq!(w.amplitude, 0.15);
132 assert_eq!(w.wavelength, 4.0);
133 assert_eq!(w.speed, 1.0);
134 assert_eq!(w.direction, [1.0, 0.0]);
135 assert_eq!(w.steepness, 0.4);
136 }
137
138 #[test]
139 fn a_blank_surface_already_has_one_wave_so_it_is_not_a_flat_plane() {
140 let s = WaterSurface::default();
141 assert_eq!(s.waves.len(), 1);
142 assert_eq!(s.waves[0].amplitude, WaterWave::default().amplitude);
143 assert_eq!(s.extent, [10.0, 10.0]);
144 assert_eq!(s.subdivisions, 64);
145 assert!(s.deep_colour[2] > s.deep_colour[0]);
148 assert!(s.shallow_colour[1] > s.deep_colour[1]);
149 assert_eq!(s.depth_falloff_metres, 4.0);
150 assert_eq!(s.foam_width_metres, 0.3);
151 assert_eq!(s.foam_intensity, 0.8);
152 assert_eq!(s.fresnel_power, 5.0);
153 assert_eq!(s.roughness, 0.05);
154 assert_eq!(s.refraction_strength, 0.15);
155 assert!(s.visible);
156 }
157
158 #[test]
159 fn a_multi_wave_surface_parses_and_round_trips_through_postcard() {
160 let s: WaterSurface = serde_json::from_str(
161 r#"{"centre":[0,0.2,-5],"extent":[40,25],"subdivisions":128,
162 "waves":[{"amplitude":0.4,"wavelength":12,"direction":[0.7,0.7]},
163 {"amplitude":0.05,"wavelength":1.5,"speed":2.5,"steepness":0.1}],
164 "deep_colour":[0,0.02,0.1],"shallow_colour":[0.1,0.4,0.45],
165 "depth_falloff_metres":8,"foam_width_metres":0.6,"foam_intensity":1.2,
166 "fresnel_power":4,"roughness":0.02,"refraction_strength":0.3,
167 "visible":false}"#,
168 )
169 .unwrap();
170 assert_eq!(s.waves.len(), 2);
171 assert_eq!(s.waves[0].speed, 1.0);
173 assert_eq!(s.waves[0].steepness, 0.4);
174 assert_eq!(s.waves[1].direction, [1.0, 0.0]);
175 assert!(!s.visible);
176
177 let bytes = postcard::to_allocvec(&s).unwrap();
178 let back: WaterSurface = postcard::from_bytes(&bytes).unwrap();
179 assert_eq!(back.centre, [0.0, 0.2, -5.0]);
180 assert_eq!(back.extent, [40.0, 25.0]);
181 assert_eq!(back.subdivisions, 128);
182 assert_eq!(back.waves[1].speed, 2.5);
183 assert_eq!(back.depth_falloff_metres, 8.0);
184 assert_eq!(back.foam_intensity, 1.2);
185 assert_eq!(back.refraction_strength, 0.3);
186 assert_eq!(back.asset_id, AssetId::default());
187 }
188}