1use crate::core::*;
2use crate::renderer::*;
3use std::sync::Arc;
4
5const VERTICES_PER_SIDE: usize = 33;
6pub const MAX_WAVE_COUNT: usize = 4;
8
9#[derive(Clone, Copy, Debug)]
13pub struct WaveParameters {
14 pub wavelength: f32,
16 pub amplitude: f32,
18 pub speed: f32,
20 pub steepness: f32,
22 pub direction: Vec2,
24}
25
26impl Default for WaveParameters {
27 fn default() -> Self {
28 Self {
29 wavelength: 1.0,
30 amplitude: 0.0,
31 speed: 1.0,
32 steepness: 0.0,
33 direction: vec2(1.0, 0.0),
34 }
35 }
36}
37
38pub struct Water<M: Material> {
42 patches: Vec<WaterPatch>,
43 vertex_distance: f32,
44 material: M,
45}
46impl<M: Material> Water<M> {
47 pub fn new(
55 context: &Context,
56 material: M,
57 height: f32,
58 center: Vec2,
59 side_length: f32,
60 vertex_distance: f32,
61 parameters: impl IntoIterator<Item = WaveParameters> + Clone,
62 ) -> Self {
63 let patch_size = vertex_distance * (VERTICES_PER_SIDE - 1) as f32;
64 let patches_per_side = ((side_length / patch_size).ceil() as u32).max(1);
65 let half_side_length = 0.5 * patches_per_side as f32 * patch_size;
66 let index_buffer = Self::indices(context);
67 let position_buffer = Self::positions(context, vertex_distance);
68 let mut patches = Vec::new();
69 for ix in 0..patches_per_side {
70 for iy in 0..patches_per_side {
71 let offset = vec2(
72 (ix as f32) * patch_size - half_side_length,
73 (iy as f32) * patch_size - half_side_length,
74 );
75 patches.push(WaterPatch::new(
76 context,
77 offset,
78 vec2(patch_size, patch_size),
79 position_buffer.clone(),
80 index_buffer.clone(),
81 ));
82 }
83 }
84
85 let mut s = Self {
86 patches,
87 vertex_distance,
88 material,
89 };
90 s.set_parameters(parameters);
91 s.set_center(center);
92 s.set_height(height);
93 s
94 }
95
96 pub fn set_center(&mut self, center: Vec2) {
101 let x = (center.x / self.vertex_distance).floor() * self.vertex_distance;
102 let z = (center.y / self.vertex_distance).floor() * self.vertex_distance;
103 self.patches.iter_mut().for_each(|p| {
104 p.center.x = x;
105 p.center.z = z;
106 });
107 }
108
109 pub fn set_height(&mut self, height: f32) {
113 self.patches.iter_mut().for_each(|p| p.center.y = height);
114 }
115
116 pub fn set_parameters(&mut self, parameters: impl IntoIterator<Item = WaveParameters> + Clone) {
120 if parameters.clone().into_iter().count() > MAX_WAVE_COUNT {
121 panic!("Water only supports {} number of waves.", MAX_WAVE_COUNT);
122 }
123 let mut ps = [WaveParameters::default(); MAX_WAVE_COUNT];
124 parameters
125 .into_iter()
126 .enumerate()
127 .for_each(|(i, p)| ps[i] = p);
128 self.patches.iter_mut().for_each(|p| p.parameters = ps);
129 }
130
131 pub fn animate(&mut self, time: f32) {
135 self.patches.iter_mut().for_each(|m| m.animate(time));
136 }
137
138 fn indices(context: &Context) -> Arc<ElementBuffer<u32>> {
139 let mut indices: Vec<u32> = Vec::new();
140 let stride = VERTICES_PER_SIDE as u32;
141 let max = stride - 1;
142 for r in 0..max {
143 for c in 0..max {
144 indices.push(r + c * stride);
145 indices.push(r + 1 + c * stride);
146 indices.push(r + (c + 1) * stride);
147 indices.push(r + (c + 1) * stride);
148 indices.push(r + 1 + c * stride);
149 indices.push(r + 1 + (c + 1) * stride);
150 }
151 }
152 Arc::new(ElementBuffer::new_with_data(context, &indices))
153 }
154
155 fn positions(context: &Context, vertex_distance: f32) -> Arc<VertexBuffer<Vec3>> {
156 let mut data = vec![vec3(0.0, 0.0, 0.0); VERTICES_PER_SIDE * VERTICES_PER_SIDE];
157 for r in 0..VERTICES_PER_SIDE {
158 for c in 0..VERTICES_PER_SIDE {
159 let vertex_id = r * VERTICES_PER_SIDE + c;
160 let x = r as f32 * vertex_distance;
161 let z = c as f32 * vertex_distance;
162 data[vertex_id] = vec3(x, 0.0, z);
163 }
164 }
165 Arc::new(VertexBuffer::new_with_data(context, &data))
166 }
167}
168
169impl<'a, M: Material> IntoIterator for &'a Water<M> {
170 type Item = Gm<&'a dyn Geometry, &'a M>;
171 type IntoIter = std::vec::IntoIter<Gm<&'a dyn Geometry, &'a M>>;
172
173 fn into_iter(self) -> Self::IntoIter {
174 self.patches
175 .iter()
176 .map(|m| Gm::new(m as &dyn Geometry, &self.material))
177 .collect::<Vec<_>>()
178 .into_iter()
179 }
180}
181
182struct WaterPatch {
183 context: Context,
184 time: f32,
185 center: Vec3,
186 parameters: [WaveParameters; MAX_WAVE_COUNT],
187 offset: Vec2,
188 size: Vec2,
189 position_buffer: Arc<VertexBuffer<Vec3>>,
190 index_buffer: Arc<ElementBuffer<u32>>,
191}
192
193impl WaterPatch {
194 pub fn new(
195 context: &Context,
196 offset: Vec2,
197 size: Vec2,
198 position_buffer: Arc<VertexBuffer<Vec3>>,
199 index_buffer: Arc<ElementBuffer<u32>>,
200 ) -> Self {
201 Self {
202 context: context.clone(),
203 time: 0.0,
204 center: vec3(0.0, 0.0, 0.0),
205 parameters: [WaveParameters::default(); MAX_WAVE_COUNT],
206 offset,
207 size,
208 position_buffer,
209 index_buffer,
210 }
211 }
212}
213
214impl Geometry for WaterPatch {
215 fn draw(&self, viewer: &dyn Viewer, program: &Program, render_states: RenderStates) {
216 program.use_uniform(
217 "offset",
218 self.center + vec3(self.offset.x, 0.0, self.offset.y),
219 );
220 program.use_uniform("viewProjection", viewer.projection() * viewer.view());
221 program.use_uniform("time", self.time * 0.001);
222 program.use_uniform_array(
223 "waveParameters",
224 &self
225 .parameters
226 .iter()
227 .map(|p| vec4(p.wavelength, p.amplitude, p.steepness, p.speed))
228 .collect::<Vec<_>>(),
229 );
230 program.use_uniform_array(
231 "directions",
232 &self
233 .parameters
234 .iter()
235 .map(|p| p.direction)
236 .collect::<Vec<_>>(),
237 );
238
239 program.use_vertex_attribute("position", &self.position_buffer);
240 program.draw_elements(render_states, viewer.viewport(), &self.index_buffer);
241 }
242
243 fn vertex_shader_source(&self) -> String {
244 include_str!("shaders/water.vert").to_owned()
245 }
246
247 fn id(&self) -> GeometryId {
248 GeometryId::WaterPatch
249 }
250
251 fn render_with_material(
252 &self,
253 material: &dyn Material,
254 viewer: &dyn Viewer,
255 lights: &[&dyn Light],
256 ) {
257 render_with_material(&self.context, viewer, &self, material, lights)
258 }
259
260 fn render_with_effect(
261 &self,
262 material: &dyn Effect,
263 viewer: &dyn Viewer,
264 lights: &[&dyn Light],
265 color_texture: Option<ColorTexture>,
266 depth_texture: Option<DepthTexture>,
267 ) {
268 render_with_effect(
269 &self.context,
270 viewer,
271 self,
272 material,
273 lights,
274 color_texture,
275 depth_texture,
276 )
277 }
278
279 fn aabb(&self) -> AxisAlignedBoundingBox {
280 let m = self
281 .parameters
282 .map(|p| p.amplitude)
283 .into_iter()
284 .reduce(f32::max)
285 .unwrap();
286 AxisAlignedBoundingBox::new_with_positions(&[
287 self.center + vec3(self.offset.x, -m, self.offset.y),
288 self.center + vec3(self.offset.x + self.size.x, m, self.offset.y + self.size.y),
289 ])
290 }
291
292 fn animate(&mut self, time: f32) {
293 self.time = time;
294 }
295}