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,
95 pub refraction_strength: f32,
97 pub visible: bool,
99}
100
101impl Default for WaterSurface {
102 fn default() -> Self {
103 Self {
104 asset_id: AssetId::default(),
105 centre: [0.0, 0.0, 0.0],
106 extent: [10.0, 10.0],
107 subdivisions: 64,
108 waves: vec![WaterWave::default()],
109 deep_colour: [0.02, 0.05, 0.15],
110 shallow_colour: [0.20, 0.50, 0.55],
111 depth_falloff_metres: 4.0,
112 foam_width_metres: 0.30,
113 foam_intensity: 0.8,
114 fresnel_power: 5.0,
115 roughness: 0.05,
116 refraction_strength: 0.15,
117 visible: true,
118 }
119 }
120}
121
122#[cfg(test)]
123mod tests {
124 use super::*;
125
126 #[test]
127 fn a_blank_wave_travels_along_positive_x() {
128 let w = WaterWave::default();
129 assert_eq!(w.amplitude, 0.15);
130 assert_eq!(w.wavelength, 4.0);
131 assert_eq!(w.speed, 1.0);
132 assert_eq!(w.direction, [1.0, 0.0]);
133 assert_eq!(w.steepness, 0.4);
134 }
135
136 #[test]
137 fn a_blank_surface_already_has_one_wave_so_it_is_not_a_flat_plane() {
138 let s = WaterSurface::default();
139 assert_eq!(s.waves.len(), 1);
140 assert_eq!(s.waves[0].amplitude, WaterWave::default().amplitude);
141 assert_eq!(s.extent, [10.0, 10.0]);
142 assert_eq!(s.subdivisions, 64);
143 assert!(s.deep_colour[2] > s.deep_colour[0]);
146 assert!(s.shallow_colour[1] > s.deep_colour[1]);
147 assert_eq!(s.depth_falloff_metres, 4.0);
148 assert_eq!(s.foam_width_metres, 0.3);
149 assert_eq!(s.foam_intensity, 0.8);
150 assert_eq!(s.fresnel_power, 5.0);
151 assert_eq!(s.roughness, 0.05);
152 assert_eq!(s.refraction_strength, 0.15);
153 assert!(s.visible);
154 }
155
156 #[test]
157 fn a_multi_wave_surface_parses_and_round_trips_through_postcard() {
158 let s: WaterSurface = serde_json::from_str(
159 r#"{"centre":[0,0.2,-5],"extent":[40,25],"subdivisions":128,
160 "waves":[{"amplitude":0.4,"wavelength":12,"direction":[0.7,0.7]},
161 {"amplitude":0.05,"wavelength":1.5,"speed":2.5,"steepness":0.1}],
162 "deep_colour":[0,0.02,0.1],"shallow_colour":[0.1,0.4,0.45],
163 "depth_falloff_metres":8,"foam_width_metres":0.6,"foam_intensity":1.2,
164 "fresnel_power":4,"roughness":0.02,"refraction_strength":0.3,
165 "visible":false}"#,
166 )
167 .unwrap();
168 assert_eq!(s.waves.len(), 2);
169 assert_eq!(s.waves[0].speed, 1.0);
171 assert_eq!(s.waves[0].steepness, 0.4);
172 assert_eq!(s.waves[1].direction, [1.0, 0.0]);
173 assert!(!s.visible);
174
175 let bytes = postcard::to_allocvec(&s).unwrap();
176 let back: WaterSurface = postcard::from_bytes(&bytes).unwrap();
177 assert_eq!(back.centre, [0.0, 0.2, -5.0]);
178 assert_eq!(back.extent, [40.0, 25.0]);
179 assert_eq!(back.subdivisions, 128);
180 assert_eq!(back.waves[1].speed, 2.5);
181 assert_eq!(back.depth_falloff_metres, 8.0);
182 assert_eq!(back.foam_intensity, 1.2);
183 assert_eq!(back.refraction_strength, 0.3);
184 assert_eq!(back.asset_id, AssetId::default());
185 }
186}