use rinecs::{Component, Entities, EntitiesCreationExt, Entity, Read, Resources, Write, system};
use rin_graphics::{self as graphics, NodeRef, CameraExt};
use crate::{Builder, CreationProxy, components::{Ty, Name, Visible}, transformation::{RenderPlane, Viewport, Camera}};
#[cfg(feature="gl_forward_renderer")]
use crate::renderer;
use rin_math::{Pnt3, ToPnt, pnt2, Rect, ToVec, vec4, convert, vec2, Vec2};
use rin_events::{self as events, StreamExt, Stream, RangedPropertyMut, ParamsDefault};
use color::*;
use crate::scene::CreationContext;
use mutiny_derive::Material;
use rin_material::{
self as material,
Parameter,
texture::{self, TextureSampler, TextureCreationFlags}
};
use std::cell::UnsafeCell;
use std::sync::mpsc;
use serde_derive::{Serialize, Deserialize};
#[cfg(gui)]
use rin_gui::{ParameterGroup, EnumParam};
use rin_graphics::Node;
pub struct Settings{
pub name: String,
pub size: Vec2,
pub transformation: Node,
pub buffer_size: Vec2<u32>,
pub msaa_samples: u32,
pub reflect_translucent: bool,
pub reflect_debug: bool,
pub water_parameters: Parameters,
pub priority: Option<i32>,
pub post_fragment_material: bool,
}
pub struct Water<'a>{
settings: Settings,
world: CreationProxy<'a>,
}
impl<'a> Builder<'a> for Water<'a>{
type Settings = Settings;
type IdType = WaterMaterial;
type Depends = ();
fn register(world: &mut rinecs::World){
let flags = TextureCreationFlags::MIPMAPS;
let map = include_bytes!("textures/water_dudvmap.png");
let img = graphics::image::load_from_memory(map).unwrap();
world.register_named_image("mutiny::water::dudv_map", &img, flags).unwrap();
let map = include_bytes!("textures/water_normalmap.png");
let img = graphics::image::load_from_memory(map).unwrap();
world.register_named_image("mutiny::water::normal_map", &img, flags).unwrap();
let sampler = texture::Sampler{
wrap: texture::Wrap::Repeat,
minfilter: texture::Filter::LinearMipNearest,
magfilter: texture::Filter::Linear,
};
world.register_named_sampler("mutiny::water::sampler", sampler);
world.add_system(water_updater);
#[cfg(feature = "gl_forward_renderer")]
world.add_creation_system(renderer::water::upload_gpu_resources);
}
fn new(world: CreationProxy<'a>, settings: Settings) -> Water<'a> {
Water{settings, world}
}
}
impl<'a> Water<'a> {
pub fn build(self) -> Entity {
let settings = self.settings;
let mut world = self.world;
let geometry = graphics::plane_texcoords(settings.size, 1, 1);
let eye = settings.transformation.global_x_axis();
let plane_normal = settings.transformation.global_z_axis();
let water_height = plane_normal.dot(&settings.transformation.global_position().to_vec());
let geometry = world.register_mesh(geometry);
let ratio = settings.buffer_size.x as f32 / settings.buffer_size.y as f32;
let camera = graphics::Camera::new(
eye.to_pnt(),
Pnt3::origin(),
plane_normal,
Deg(60.),
ratio
).unwrap();
let viewport = Rect{
pos: pnt2!(0),
width: settings.buffer_size.x as i32,
height: settings.buffer_size.y as i32
};
let (parameters_send, buffers_size_recv) = settings.to_send();
parameters_send.buffer_size_sender.send(parameters_send.buffer_size).unwrap();
let mut reflection = RenderPlane::new(&camera, viewport, Some(vec4!(plane_normal, -water_height)), 0.);
let mut refraction = RenderPlane::new(&camera, viewport, Some(vec4!(-plane_normal, water_height)), 0.);
reflection.set_draw_translucent(settings.reflect_translucent);
refraction.set_draw_translucent(settings.reflect_translucent);
reflection.set_draw_debug(settings.reflect_debug);
refraction.set_draw_debug(settings.reflect_debug);
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![reflection, refraction])
.add(parameters_send)
.build();
let dudv_map = world.find_named_texture("mutiny::water::dudv_map").unwrap();
let normal_map = world.find_named_texture("mutiny::water::normal_map").unwrap();
let sampler = world.find_named_sampler("mutiny::water::sampler").unwrap();
let dudv_map = TextureSampler{
texture: dudv_map,
sampler: Some(sampler),
};
let normal_map = TextureSampler{
texture: normal_map,
sampler: Some(sampler),
};
let mut trigger_first_frame = events::Property::new(());
let parameters_changed = settings.water_parameters.parameters_changed()
.merge(trigger_first_frame.clone().stream())
.try_iter();
let lighting_parameters_changed = settings.water_parameters.lighting_parameters_changed()
.merge(trigger_first_frame.clone().stream())
.try_iter();
let shader_precision_changed = settings.water_parameters.shader_precision.clone().map(|_| ())
.try_iter();
trigger_first_frame.set(());
let material = WaterMaterial{
parameters: settings.water_parameters,
entity: water,
msaa_samples: Parameter::new(settings.msaa_samples),
dudv_map: Parameter::new(dudv_map),
normal_map: Parameter::new(normal_map),
parameters_changed: UnsafeCell::new(parameters_changed),
lighting_parameters_changed: UnsafeCell::new(lighting_parameters_changed),
shader_precision_changed: UnsafeCell::new(shader_precision_changed),
buffers_changed: buffers_size_recv,
priority: settings.priority,
};
#[cfg(feature="gl_forward_renderer")]
let material = if settings.post_fragment_material{
let material = crate::renderer::PostFragmentMaterialBuilder::new(material).build();
world.register_material(&format!("WaterMaterial_{}", settings.name), material)
}else{
world.register_material(&format!("WaterMaterial_{}", settings.name), material)
};
#[cfg(not(feature="gl_forward_renderer"))]
let material = world.register_material(&format!("WaterMaterial_{}", settings.name), material);
world.add_component_to(&water, material);
water
}
}
#[system(name = "water updater")]
#[needs("Viewport", "Camera")]
#[updates("ParametersSend", "RenderPlane")]
#[reads("Node")]
fn water_updater(mut 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_mut::<(
Read<Node>,
Write<ParametersSend>,
Write<RenderPlane>)>();
for (transformation, water_parameters, mut renderplanes) in iter {
let up_axis = transformation.global_z_axis();
if let [reflection, refraction] = &mut *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.get()
|| reflection.bias() != *water_parameters.reflection_clip_bias.get();
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 {
log::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);
water_parameters.buffer_size_sender.send(water_parameters.buffer_size).unwrap();
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(){
if let Ok(camera) = graphics::camera::Builder::new(ratio)
.position(camera_position)
.look_at(*target)
.up_axis(up_axis)
.near_far(near, far)
.fov(fov)
.build()
{
refraction.update_camera_viewport_bias(
&camera,
viewport,
*water_parameters.refraction_clip_bias.get()
);
}
}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(){
if let Ok(camera) = graphics::camera::Builder::new(ratio)
.position(camera_reflection)
.look_at(target_reflection)
.up_axis(up_axis)
.near_far(near, far)
.fov(fov)
.build()
{
reflection.update_camera_viewport_bias(
&camera,
viewport,
*water_parameters.reflection_clip_bias.get()
);
}
}else{
unimplemented!()
}
}
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize, ParamsDefault)]
#[cfg_attr(gui, derive(ParameterGroup))]
pub struct WaterColor{
#[param(default = 0., range_log = 0. .. 1.)]
pub r: RangedPropertyMut<'static, f32>,
#[param(default = 0., range_log = 0. .. 1.)]
pub g: RangedPropertyMut<'static, f32>,
#[param(default = 0., range_log = 0. .. 1.)]
pub b: RangedPropertyMut<'static, f32>,
}
impl WaterColor{
fn to_rgb_param(&self) -> events::Property<'static, Rgb<f32>>{
self.r.clone().into_property()
.zip(self.g.clone().into_property())
.zip(self.b.clone().into_property())
.flatten()
.map(|(r,g,b)| rgb!(r,g,b))
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize)]
#[cfg_attr(gui, derive(EnumParam))]
pub enum ShaderPrecision{
Low,
Medium,
High,
}
impl ShaderPrecision{
pub fn to_material_shader_precision(self) -> material::ShaderPrecision{
match self{
ShaderPrecision::Low => material::ShaderPrecision::Low,
ShaderPrecision::Medium => material::ShaderPrecision::Medium,
ShaderPrecision::High => material::ShaderPrecision::High,
}
}
}
impl Default for ShaderPrecision{
fn default() -> ShaderPrecision{
ShaderPrecision::High
}
}
#[derive(Clone, Component, Debug, Serialize, Deserialize, ParamsDefault)]
#[cfg_attr(gui, derive(ParameterGroup))]
pub struct Parameters{
#[param(default = 4., range_log = 0.1 .. 10.)]
pub frequency: RangedPropertyMut<'static, f32>,
#[param(default = 0.015, range = 0. .. 0.1)]
pub wave_strength: RangedPropertyMut<'static, f32>,
#[param(default = 0.02, range_log = 0. .. 3.)]
pub speed: RangedPropertyMut<'static, f32>,
#[param(default = 1., range_log = 0.01 .. 10.)]
pub reflective_factor: RangedPropertyMut<'static, f32>,
#[param(default = 0.2, range = 0. .. 1.)]
pub normal_scale: RangedPropertyMut<'static, f32>,
#[param(default = 0.1, range_log = 0. .. 3.,)]
pub max_visible_depth: RangedPropertyMut<'static, f32>,
#[param(default = 0.03, range = -1. .. 1.)]
pub reflection_clip_bias: RangedPropertyMut<'static, f32>,
#[param(default = 0.03, range = -1. .. 1.)]
pub refraction_clip_bias: RangedPropertyMut<'static, f32>,
#[param(default = 25., range_log = 0. .. 100.)]
pub water_density: RangedPropertyMut<'static, f32>,
#[cfg_attr(gui, gui(name="color tint"))]
pub color_tint: WaterColor,
#[cfg_attr(gui, gui(name="color coefficient"))]
pub color_coefficient: WaterColor,
#[param(default = 0.0, range = 0. .. 1.)]
pub refraction_color_mix: RangedPropertyMut<'static, f32>,
#[param(default = 0.0, range = 0. .. 1.)]
pub reflection_color_mix: RangedPropertyMut<'static, f32>,
#[param(default = 1., range = 0. .. 5.)]
pub highlights_factor: RangedPropertyMut<'static, f32>,
#[param(default = 1., range = 0. .. 5.)]
pub highlights_power: RangedPropertyMut<'static, f32>,
#[param(default = true)]
pub receives_shadows: events::Property<'static, bool>,
#[param(default = true)]
pub receives_global_illumination: events::Property<'static, bool>,
#[cfg_attr(gui, gui(not_parameter))]
#[param(with = color_tint.to_rgb_param())]
pub water_color_tint: events::Property<'static, Rgb<f32>>,
#[cfg_attr(gui, gui(not_parameter))]
#[param(with = color_coefficient.to_rgb_param())]
pub water_color_coefficient: events::Property<'static, Rgb<f32>>,
pub shader_precision: events::Property<'static, ShaderPrecision>,
}
impl Parameters{
pub fn new() -> Parameters {
Parameters::default()
}
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_tint.clone().stream().map(|_| ()))
.merge(self.water_color_coefficient.clone().stream().map(|_| ()))
.merge(self.refraction_color_mix.clone().into_property().stream().map(|_| ()))
.merge(self.reflection_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 fn lighting_parameters_changed(&self) -> Stream<'static, ()>{
self.receives_shadows.clone().stream().map(|_| ())
.merge(self.receives_global_illumination.clone().stream().map(|_| ()))
}
}
#[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_tint: events::Parameter<'static, Rgb<f32>>,
pub water_color_coefficient: events::Parameter<'static, Rgb<f32>>,
pub refraction_color_mix: events::Parameter<'static, f32>,
pub reflection_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_sender: mpsc::Sender<Vec2<u32>>,
pub buffer_size: Vec2<u32>,
}
#[derive(Material)]
pub struct WaterMaterial{
#[material(skip)]
pub parameters: Parameters,
#[material(skip)]
entity: Entity,
#[material(not_data)]
pub msaa_samples: Parameter<u32>,
pub dudv_map: Parameter<TextureSampler>,
pub normal_map: Parameter<TextureSampler>,
#[material(skip)]
parameters_changed: UnsafeCell<events::TryIter<'static, ()>>,
#[material(skip)]
lighting_parameters_changed: UnsafeCell<events::TryIter<'static, ()>>,
#[material(skip)]
shader_precision_changed: UnsafeCell<events::TryIter<'static, ()>>,
#[material(skip)]
buffers_changed: mpsc::Receiver<Vec2<u32>>,
#[material(skip)]
priority: Option<i32>,
}
impl WaterMaterial{
pub fn parameters_mut(&mut self) -> &mut Parameters{
&mut self.parameters
}
pub fn parameters_changed(&self) -> bool{
unsafe{ (*self.parameters_changed.get()).by_ref().last().is_some() }
}
pub fn lighting_parameters_changed(&self) -> bool{
unsafe{ (*self.lighting_parameters_changed.get()).by_ref().last().is_some() }
}
pub fn shader_precision_changed(&self) -> bool{
unsafe{ (*self.shader_precision_changed.get()).by_ref().last().is_some() }
}
pub fn buffers_changed(&self) -> bool{
self.buffers_changed.try_iter().last().is_some()
}
pub fn entity(&self) -> Entity{
self.entity
}
pub fn priority(&self) -> Option<i32>{
self.priority
}
}
impl Settings{
fn to_send(&self) -> (ParametersSend, mpsc::Receiver<Vec2<u32>>){
let parameters = self.water_parameters.clone();
let (buffer_size_tx, buffer_size_rx) = mpsc::channel();
let parameters = 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(),
refraction_color_mix: parameters.refraction_color_mix.to_parameter(),
reflection_color_mix: parameters.reflection_color_mix.to_parameter(),
highlights_factor: parameters.highlights_factor.to_parameter(),
highlights_power: parameters.highlights_power.to_parameter(),
water_color_tint: parameters.water_color_tint.to_parameter(),
water_color_coefficient: parameters.water_color_coefficient.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_sender: buffer_size_tx,
buffer_size: self.buffer_size,
};
(parameters, buffer_size_rx)
}
}