three_d/renderer/object/
water.rs

1use crate::core::*;
2use crate::renderer::*;
3use std::sync::Arc;
4
5const VERTICES_PER_SIDE: usize = 33;
6/// The maximum number of waves.
7pub const MAX_WAVE_COUNT: usize = 4;
8
9///
10/// A set of parameters that defines one wave of the water surface.
11///
12#[derive(Clone, Copy, Debug)]
13pub struct WaveParameters {
14    /// The distance between each top of the wave.
15    pub wavelength: f32,
16    /// The distance from the top or bottom of the wave to the average water height.
17    pub amplitude: f32,
18    /// The speed at which the waves move.
19    pub speed: f32,
20    /// The steepness of the wave, which specifies how pointy the wave top will be.
21    pub steepness: f32,
22    /// The direction of the wave.
23    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
38///
39/// A water geometry with an applied material.
40///
41pub struct Water<M: Material> {
42    patches: Vec<WaterPatch>,
43    vertex_distance: f32,
44    material: M,
45}
46impl<M: Material> Water<M> {
47    ///
48    /// Constructs a new [Water] object with the given material and at the given height.
49    ///
50    /// The `center` is the center of the visualized water surface, which can be updated using [Self::set_center] to simualte an infinite water surface.
51    /// The `side_length` is the length of one side of the visualized water surface.
52    /// The `vertex_distance` is the distance between vertices.
53    ///
54    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    ///
97    /// Set the center of the water.
98    /// To be able to move the water with the camera, thereby simulating infinite water.
99    ///
100    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    ///
110    /// Set the average height of the water.
111    ///
112    pub fn set_height(&mut self, height: f32) {
113        self.patches.iter_mut().for_each(|p| p.center.y = height);
114    }
115
116    ///
117    /// Set the currently used [WaveParameters].
118    ///
119    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    ///
132    /// For updating the animation. The time parameter should be some continious time, for example the time since start.
133    ///
134    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}