use rinecs::{Read, Write, Entities, Resources, Entity};
use rin::graphics::{self, NodeRef, CameraExt};
use crate::{
Bundle as MutinyBundle, Builder,
components::{Ty, Name, Visible},
transformation::{Transformation, RenderPlane, Viewport, Camera},
};
#[cfg(any(feature="gl", feature="gles", feature="webgl"))]
use crate::renderer;
use rin::math::*;
use rin::events::{self, PropertyT, StreamT, Stream, RangedParameterMut};
use color::*;
use crate::scene::CreationContext;
use crate::Scene;
pub struct Settings{
pub name: String,
pub size: Vec2,
pub transformation: Transformation,
pub buffer_size: Vec2<u32>,
pub msaa_samples: u32,
pub water_parameters: Parameters,
}
pub struct Bundle;
impl MutinyBundle for Bundle{
fn register(world: &mut Scene){
world.register_component::<ParametersSend>();
}
fn setup(self, world: &mut Scene){
world.add_system_with_name(water_updater, "water update");
#[cfg(any(feature="gl", feature="gles", feature="webgl"))]
world.add_creation_system_with_name(renderer::water::upload_gpu_resources, "water create gpu resources");
}
}
pub struct Water;
impl<'a, C: CreationContext> Builder<'a, C> for Water{
type Settings = Settings;
fn new(world: &'a mut C, settings: Settings) -> Water{
let geometry = graphics::plane_texcoords_no_indices(settings.size, 1, 1);
let plane_normal = settings.transformation.z_axis();
let water_height = plane_normal.dot(&settings.transformation.global_position().to_vec());
let geometry = world.add_mesh(geometry);
let ratio = settings.buffer_size.x as f32 / settings.buffer_size.y as f32;
let camera = graphics::Camera::new(pnt3(1., 0., 0.), Point::origin(), plane_normal, Deg(60.), ratio);
let viewport = Rect{
pos: pnt2!(0),
width: settings.buffer_size.x as i32,
height: settings.buffer_size.y as i32
};
let parameters_send = settings.to_send();
let water = world.new_entity()
.add(Name(settings.name.clone()))
.add(settings.transformation)
.add(Ty::Model)
.add(geometry)
.add(Visible::new(true))
.add_slice(vec![
RenderPlane::new(&camera, viewport, Some(vec4!(plane_normal, -water_height)), 0.),
RenderPlane::new(&camera, viewport, Some(vec4!(-plane_normal, water_height)), 0.),
])
.add(parameters_send)
.build();
let material = Material{
parameters: settings.water_parameters,
entity: water,
msaa_samples: settings.msaa_samples,
};
let material = world
.register_material(&format!("WaterMaterial_{}", settings.name), material);
world.add_component_to(&water, material);
Water{}
}
}
fn water_updater(entities: Entities, resources: Resources){
let viewport = resources.get::<Viewport>().unwrap();
let viewport: Rect<f32> = convert(**viewport);
let camera = resources.get::<Camera>().unwrap();
let iter = entities.iter_for::<(
Read<Transformation>,
Write<ParametersSend>,
Write<RenderPlane>)>();
for (transformation, water_parameters, renderplanes) in iter {
let up_axis = transformation.z_axis();
if let [reflection, refraction] = renderplanes {
let ratio = viewport.aspect_ratio();
let float_viewport: Rect<f32> = convert(*reflection.camera_matrices().viewport());
let viewport_changed = (ratio - float_viewport.aspect_ratio()).abs() > 1.0e-6;
let bias_changed = refraction.bias() != *water_parameters.refraction_clip_bias ||
reflection.bias() != *water_parameters.reflection_clip_bias;
reflection.reset_has_changed();
refraction.reset_has_changed();
if transformation.has_changed() {
let water_height = up_axis.dot(&transformation.global_position().to_vec());
reflection.set_clip_plane(vec4!(up_axis, -water_height));
refraction.set_clip_plane(vec4!(-up_axis, water_height));
}
if camera.has_changed() || viewport_changed || bias_changed {
trace!("Updating water maps {} {} {}", camera.has_changed(), viewport_changed, bias_changed);
let camera = resources.get::<Camera>().unwrap();
let viewport = if viewport_changed {
let height = water_parameters.buffer_size.y as f32;
let width = height * ratio;
water_parameters.buffer_size = vec2(width as u32, water_parameters.buffer_size.y);
Rect{pos: pnt2!(0), width: width as i32, height:height as i32}
}else{
*reflection.camera_matrices().viewport()
};
let target = camera.target();
let camera_position = camera.global_position();
let (near, far) = camera.near_far_clip();
if let Some(fov) = camera.fov(){
let camera = graphics::Camera::new_with_frustum(
camera_position,
*target,
up_axis,
fov,
ratio,
near,
far);
refraction.update_camera_viewport_bias(
&camera,
viewport,
*water_parameters.refraction_clip_bias);
}else{
unimplemented!()
}
let plane_normal = refraction.clip_plane().unwrap().xyz();
let plane_position = transformation.global_position();
let camera_plane_distance = (camera_position - plane_position).dot(&plane_normal);
let camera_reflection = camera_position - plane_normal * camera_plane_distance * 2.;
let target_plane_distance = (target - plane_position).dot(&plane_normal);
let target_reflection = target - plane_normal * target_plane_distance * 2.;
if let Some(fov) = camera.fov(){
let camera = graphics::Camera::new_with_frustum(
camera_reflection,
target_reflection,
up_axis,
fov,
ratio,
near,
far);
reflection.update_camera_viewport_bias(
&camera,
viewport,
*water_parameters.reflection_clip_bias);
}else{
unimplemented!()
}
}
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "gui", derive(ParameterGroup))]
pub struct WaterColor{
pub r: RangedParameterMut<'static, f32>,
pub g: RangedParameterMut<'static, f32>,
pub b: RangedParameterMut<'static, f32>,
}
#[derive(UniformsLocationCache2,Clone, Component, Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "gui", derive(ParameterGroup))]
pub struct Parameters{
pub frequency: RangedParameterMut<'static, f32>,
pub wave_strength: RangedParameterMut<'static, f32>,
#[not_uniform]
pub speed: RangedParameterMut<'static, f32>,
pub reflective_factor: RangedParameterMut<'static, f32>,
pub normal_scale: RangedParameterMut<'static, f32>,
pub max_visible_depth: RangedParameterMut<'static, f32>,
#[not_uniform]
pub reflection_clip_bias: RangedParameterMut<'static, f32>,
#[not_uniform]
pub refraction_clip_bias: RangedParameterMut<'static, f32>,
pub water_density: RangedParameterMut<'static, f32>,
#[not_uniform]
#[cfg_attr(feature = "gui", gui(name="water color"))]
pub color: WaterColor,
pub color_mix: RangedParameterMut<'static, f32>,
pub highlights_factor: RangedParameterMut<'static, f32>,
pub highlights_power: RangedParameterMut<'static, f32>,
#[not_uniform]
pub receives_shadows: events::Property<'static, bool>,
#[not_uniform]
pub receives_global_illumination: events::Property<'static, bool>,
#[cfg_attr(feature = "gui", not_parameter)]
pub water_color: events::Property<'static, Rgba<f32>>,
#[cfg_attr(feature = "gui", not_parameter)]
pub time: f32,
}
#[derive(Component, Debug)]
#[debug_as_string]
pub struct ParametersSend{
pub frequency: events::Parameter<'static, f32>,
pub wave_strength: events::Parameter<'static, f32>,
pub speed: events::Parameter<'static, f32>,
pub reflective_factor: events::Parameter<'static, f32>,
pub normal_scale: events::Parameter<'static, f32>,
pub max_visible_depth: events::Parameter<'static, f32>,
pub reflection_clip_bias: events::Parameter<'static, f32>,
pub refraction_clip_bias: events::Parameter<'static, f32>,
pub water_density: events::Parameter<'static, f32>,
pub water_color: events::Parameter<'static, Rgba<f32>>,
pub color_mix: events::Parameter<'static, f32>,
pub highlights_factor: events::Parameter<'static, f32>,
pub highlights_power: events::Parameter<'static, f32>,
pub receives_shadows: events::Parameter<'static, bool>,
pub receives_global_illumination: events::Parameter<'static, bool>,
pub msaa_samples: u32,
pub buffer_size: Vec2<u32>,
}
impl Parameters{
pub fn new() -> Parameters {
let color = WaterColor{
r: RangedParameterMut::new(0., 0. .. 1.),
g: RangedParameterMut::new(0., 0. .. 1.),
b: RangedParameterMut::new(0., 0. .. 1.),
};
let water_color = color.r.clone().into_property()
.zip(color.g.clone().into_property())
.zip(color.b.clone().into_property())
.flatten()
.map(|(r,g,b)| rgba!(r,g,b,1.));
Parameters{
frequency: RangedParameterMut::new_log_scale(4., 0.1 .. 10.),
wave_strength: RangedParameterMut::new(0.015, 0. .. 0.1),
reflective_factor: RangedParameterMut::new_log_scale(1., 0.01 .. 10.),
speed: RangedParameterMut::new_log_scale(0.02, 0. .. 3.),
normal_scale: RangedParameterMut::new(0.2, 0. .. 1.),
max_visible_depth: RangedParameterMut::new(0.15, 0. .. 10.),
reflection_clip_bias: RangedParameterMut::new(0.03, -1. .. 1.),
refraction_clip_bias: RangedParameterMut::new(0.03, -1. .. 1.),
water_density: RangedParameterMut::new_log_scale(5., 0. .. 25.),
color_mix: RangedParameterMut::new(0.15, 0. .. 1.),
highlights_factor: RangedParameterMut::new(1., 0. .. 1.),
highlights_power: RangedParameterMut::new(1., 0. .. 5.),
receives_global_illumination: events::Property::new(true),
receives_shadows: events::Property::new(true),
water_color,
color,
time: 0.,
}
}
pub fn parameters_changed(&self) -> Stream<'static, ()>{
self.frequency.clone().into_property().stream().map(|_| ())
.merge(self.wave_strength.clone().into_property().stream().map(|_| ()))
.merge(self.speed.clone().into_property().stream().map(|_| ()))
.merge(self.reflective_factor.clone().into_property().stream().map(|_| ()))
.merge(self.normal_scale.clone().into_property().stream().map(|_| ()))
.merge(self.max_visible_depth.clone().into_property().stream().map(|_| ()))
.merge(self.reflection_clip_bias.clone().into_property().stream().map(|_| ()))
.merge(self.refraction_clip_bias.clone().into_property().stream().map(|_| ()))
.merge(self.water_density.clone().into_property().stream().map(|_| ()))
.merge(self.water_color.clone().stream().map(|_| ()))
.merge(self.color_mix.clone().into_property().stream().map(|_| ()))
.merge(self.highlights_factor.clone().into_property().stream().map(|_| ()))
.merge(self.highlights_power.clone().into_property().stream().map(|_| ()))
}
}
pub struct Material{
pub parameters: Parameters,
pub entity: Entity,
pub msaa_samples: u32,
}
impl Settings{
fn to_send(&self) -> ParametersSend{
let parameters = self.water_parameters.clone();
ParametersSend{
frequency: parameters.frequency.to_parameter(),
wave_strength: parameters.wave_strength.to_parameter(),
reflective_factor: parameters.reflective_factor.to_parameter(),
speed: parameters.speed.to_parameter(),
normal_scale: parameters.normal_scale.to_parameter(),
max_visible_depth: parameters.max_visible_depth.to_parameter(),
reflection_clip_bias: parameters.reflection_clip_bias.to_parameter(),
refraction_clip_bias: parameters.refraction_clip_bias.to_parameter(),
water_density: parameters.water_density.to_parameter(),
color_mix: parameters.color_mix.to_parameter(),
highlights_factor: parameters.highlights_factor.to_parameter(),
highlights_power: parameters.highlights_power.to_parameter(),
water_color: parameters.water_color.to_parameter(),
receives_shadows: parameters.receives_shadows.to_parameter(),
receives_global_illumination: parameters.receives_global_illumination.to_parameter(),
msaa_samples: self.msaa_samples,
buffer_size: self.buffer_size,
}
}
}
impl super::Material for Material{
fn parameters(&mut self) -> Vec<super::Parameter>{
vec![
]
}
fn parameter(&mut self, _name: &str) -> Option<super::Parameter>{
None
}
fn changed(&self) -> bool{
false
}
fn reset_changed(&mut self){
}
}