use crate::core::*;
use crate::renderer::*;
use std::sync::Arc;
const VERTICES_PER_SIDE: usize = 33;
pub const MAX_WAVE_COUNT: usize = 4;
#[derive(Clone, Copy, Debug)]
pub struct WaveParameters {
pub wavelength: f32,
pub amplitude: f32,
pub speed: f32,
pub steepness: f32,
pub direction: Vec2,
}
impl Default for WaveParameters {
fn default() -> Self {
Self {
wavelength: 1.0,
amplitude: 0.0,
speed: 1.0,
steepness: 0.0,
direction: vec2(1.0, 0.0),
}
}
}
pub struct Water<M: Material> {
patches: Vec<WaterPatch>,
vertex_distance: f32,
material: M,
}
impl<M: Material> Water<M> {
pub fn new(
context: &Context,
material: M,
height: f32,
center: Vec2,
side_length: f32,
vertex_distance: f32,
parameters: impl IntoIterator<Item = WaveParameters> + Clone,
) -> Self {
let patch_size = vertex_distance * (VERTICES_PER_SIDE - 1) as f32;
let patches_per_side = ((side_length / patch_size).ceil() as u32).max(1);
let half_side_length = 0.5 * patches_per_side as f32 * patch_size;
let index_buffer = Self::indices(context);
let position_buffer = Self::positions(context, vertex_distance);
let mut patches = Vec::new();
for ix in 0..patches_per_side {
for iy in 0..patches_per_side {
let offset = vec2(
(ix as f32) * patch_size - half_side_length,
(iy as f32) * patch_size - half_side_length,
);
patches.push(WaterPatch::new(
context,
offset,
vec2(patch_size, patch_size),
position_buffer.clone(),
index_buffer.clone(),
));
}
}
let mut s = Self {
patches,
vertex_distance,
material,
};
s.set_parameters(parameters);
s.set_center(center);
s.set_height(height);
s
}
pub fn set_center(&mut self, center: Vec2) {
let x = (center.x / self.vertex_distance).floor() * self.vertex_distance;
let z = (center.y / self.vertex_distance).floor() * self.vertex_distance;
self.patches.iter_mut().for_each(|p| {
p.center.x = x;
p.center.z = z;
});
}
pub fn set_height(&mut self, height: f32) {
self.patches.iter_mut().for_each(|p| p.center.y = height);
}
pub fn set_parameters(&mut self, parameters: impl IntoIterator<Item = WaveParameters> + Clone) {
if parameters.clone().into_iter().count() > MAX_WAVE_COUNT {
panic!("Water only supports {} number of waves.", MAX_WAVE_COUNT);
}
let mut ps = [WaveParameters::default(); MAX_WAVE_COUNT];
parameters
.into_iter()
.enumerate()
.for_each(|(i, p)| ps[i] = p);
self.patches.iter_mut().for_each(|p| p.parameters = ps);
}
pub fn animate(&mut self, time: f32) {
self.patches.iter_mut().for_each(|m| m.animate(time));
}
fn indices(context: &Context) -> Arc<ElementBuffer<u32>> {
let mut indices: Vec<u32> = Vec::new();
let stride = VERTICES_PER_SIDE as u32;
let max = stride - 1;
for r in 0..max {
for c in 0..max {
indices.push(r + c * stride);
indices.push(r + 1 + c * stride);
indices.push(r + (c + 1) * stride);
indices.push(r + (c + 1) * stride);
indices.push(r + 1 + c * stride);
indices.push(r + 1 + (c + 1) * stride);
}
}
Arc::new(ElementBuffer::new_with_data(context, &indices))
}
fn positions(context: &Context, vertex_distance: f32) -> Arc<VertexBuffer<Vec3>> {
let mut data = vec![vec3(0.0, 0.0, 0.0); VERTICES_PER_SIDE * VERTICES_PER_SIDE];
for r in 0..VERTICES_PER_SIDE {
for c in 0..VERTICES_PER_SIDE {
let vertex_id = r * VERTICES_PER_SIDE + c;
let x = r as f32 * vertex_distance;
let z = c as f32 * vertex_distance;
data[vertex_id] = vec3(x, 0.0, z);
}
}
Arc::new(VertexBuffer::new_with_data(context, &data))
}
}
impl<'a, M: Material> IntoIterator for &'a Water<M> {
type Item = Gm<&'a dyn Geometry, &'a M>;
type IntoIter = std::vec::IntoIter<Gm<&'a dyn Geometry, &'a M>>;
fn into_iter(self) -> Self::IntoIter {
self.patches
.iter()
.map(|m| Gm::new(m as &dyn Geometry, &self.material))
.collect::<Vec<_>>()
.into_iter()
}
}
struct WaterPatch {
context: Context,
time: f32,
center: Vec3,
parameters: [WaveParameters; MAX_WAVE_COUNT],
offset: Vec2,
size: Vec2,
position_buffer: Arc<VertexBuffer<Vec3>>,
index_buffer: Arc<ElementBuffer<u32>>,
}
impl WaterPatch {
pub fn new(
context: &Context,
offset: Vec2,
size: Vec2,
position_buffer: Arc<VertexBuffer<Vec3>>,
index_buffer: Arc<ElementBuffer<u32>>,
) -> Self {
Self {
context: context.clone(),
time: 0.0,
center: vec3(0.0, 0.0, 0.0),
parameters: [WaveParameters::default(); MAX_WAVE_COUNT],
offset,
size,
position_buffer,
index_buffer,
}
}
}
impl Geometry for WaterPatch {
fn draw(&self, viewer: &dyn Viewer, program: &Program, render_states: RenderStates) {
program.use_uniform(
"offset",
self.center + vec3(self.offset.x, 0.0, self.offset.y),
);
program.use_uniform("viewProjection", viewer.projection() * viewer.view());
program.use_uniform("time", self.time * 0.001);
program.use_uniform_array(
"waveParameters",
&self
.parameters
.iter()
.map(|p| vec4(p.wavelength, p.amplitude, p.steepness, p.speed))
.collect::<Vec<_>>(),
);
program.use_uniform_array(
"directions",
&self
.parameters
.iter()
.map(|p| p.direction)
.collect::<Vec<_>>(),
);
program.use_vertex_attribute("position", &self.position_buffer);
program.draw_elements(render_states, viewer.viewport(), &self.index_buffer);
}
fn vertex_shader_source(&self) -> String {
include_str!("shaders/water.vert").to_owned()
}
fn id(&self) -> GeometryId {
GeometryId::WaterPatch
}
fn render_with_material(
&self,
material: &dyn Material,
viewer: &dyn Viewer,
lights: &[&dyn Light],
) {
render_with_material(&self.context, viewer, &self, material, lights)
}
fn render_with_effect(
&self,
material: &dyn Effect,
viewer: &dyn Viewer,
lights: &[&dyn Light],
color_texture: Option<ColorTexture>,
depth_texture: Option<DepthTexture>,
) {
render_with_effect(
&self.context,
viewer,
self,
material,
lights,
color_texture,
depth_texture,
)
}
fn aabb(&self) -> AxisAlignedBoundingBox {
let m = self
.parameters
.map(|p| p.amplitude)
.into_iter()
.reduce(f32::max)
.unwrap();
AxisAlignedBoundingBox::new_with_positions(&[
self.center + vec3(self.offset.x, -m, self.offset.y),
self.center + vec3(self.offset.x + self.size.x, m, self.offset.y + self.size.y),
])
}
fn animate(&mut self, time: f32) {
self.time = time;
}
}