use raw_window_handle::{HasWindowHandle, HasDisplayHandle};
use crate::config::FluidConfig;
use crate::renderer::Renderer;
use crate::fluid::FluidSim;
use crate::input::{InputManager, random_color};
pub use crate::fluid::hsv_to_rgb;
pub struct RenderContext<'a> {
pub device: &'a wgpu::Device,
pub queue: &'a wgpu::Queue,
pub surface_format: wgpu::TextureFormat,
pub width: u32,
pub height: u32,
}
pub struct FluidEngine<'w> {
pub input: InputManager,
pub(crate) renderer: Renderer<'w>,
pub(crate) fluid: FluidSim,
pub(crate) config: FluidConfig,
color_timer: f32,
}
impl<'w> FluidEngine<'w> {
pub async fn new<W>(window: W, width: u32, height: u32, config: &FluidConfig) -> Self
where
W: HasWindowHandle + HasDisplayHandle + Send + Sync + 'w,
{
let renderer = Renderer::new(window, width, height, config).await;
let fluid = FluidSim::new(&renderer, config);
let mut input = InputManager::new();
input.burst(10); Self { input, renderer, fluid, config: config.clone(), color_timer: 0.0 }
}
pub fn config(&self) -> &FluidConfig { &self.config }
pub fn config_mut(&mut self) -> &mut FluidConfig { &mut self.config }
pub fn set_config(&mut self, config: FluidConfig) {
let rebuild =
config.sim_resolution != self.config.sim_resolution ||
config.dye_resolution != self.config.dye_resolution ||
config.bloom_resolution != self.config.bloom_resolution ||
config.bloom_iterations != self.config.bloom_iterations ||
config.sunrays_resolution != self.config.sunrays_resolution;
self.config = config;
if rebuild { self.fluid.resize(&self.renderer, &self.config); }
}
pub fn set_density_dissipation(&mut self, v: f32) { self.config.density_dissipation = v; }
pub fn set_velocity_dissipation(&mut self, v: f32) { self.config.velocity_dissipation = v; }
pub fn set_curl(&mut self, v: f32) { self.config.curl = v; }
pub fn set_splat_radius(&mut self, v: f32) { self.config.splat_radius = v.max(0.001); }
pub fn set_splat_force(&mut self, v: f32) { self.config.splat_force = v; }
pub fn set_pressure(&mut self, v: f32) { self.config.pressure = v.clamp(0.0, 1.0); }
pub fn set_shading(&mut self, v: bool) { self.config.shading = v; }
pub fn set_bloom(&mut self, v: bool) { self.config.bloom = v; }
pub fn set_bloom_intensity(&mut self, v: f32) { self.config.bloom_intensity = v.max(0.0); }
pub fn set_bloom_threshold(&mut self, v: f32) { self.config.bloom_threshold = v.clamp(0.0, 1.0); }
pub fn set_sunrays(&mut self, v: bool) { self.config.sunrays = v; }
pub fn set_sunrays_weight(&mut self, v: f32) { self.config.sunrays_weight = v.max(0.0); }
pub fn set_background_color(&mut self, r: f32, g: f32, b: f32) { self.config.back_color = [r, g, b]; }
pub fn set_background_alpha(&mut self, a: f32) { self.config.back_alpha = a.clamp(0.0, 1.0); }
pub fn set_paused(&mut self, v: bool) { self.config.paused = v; }
pub fn toggle_paused(&mut self) -> bool { self.config.paused = !self.config.paused; self.config.paused }
pub fn set_bloom_color_tint(&mut self, r: f32, g: f32, b: f32) {
self.config.bloom_color_tint = [r.max(0.0), g.max(0.0), b.max(0.0)];
}
pub fn set_bloom_hdr_power(&mut self, v: f32) {
self.config.bloom_hdr_power = v.max(0.1);
}
pub fn set_tone_map_exposure(&mut self, v: f32) {
self.config.tone_map_exposure = v.max(0.0);
}
pub fn set_sunrays_exposure(&mut self, v: f32) {
self.config.sunrays_exposure = v.max(0.0);
}
pub fn set_sunrays_decay(&mut self, v: f32) {
self.config.sunrays_decay = v.clamp(0.1, 0.999);
}
pub fn reset_to_js_defaults(&mut self) {
self.set_config(FluidConfig::js_defaults());
}
pub fn is_paused(&self) -> bool { self.config.paused }
pub fn size(&self) -> (u32, u32) { (self.renderer.width, self.renderer.height) }
pub fn device(&self) -> &wgpu::Device { &self.renderer.device }
pub fn queue(&self) -> &wgpu::Queue { &self.renderer.queue }
pub fn surface_format(&self) -> wgpu::TextureFormat { self.renderer.surface_config.format }
pub fn render_context(&self) -> RenderContext<'_> {
RenderContext {
device: &self.renderer.device,
queue: &self.renderer.queue,
surface_format: self.renderer.surface_config.format,
width: self.renderer.width,
height: self.renderer.height,
}
}
pub fn update(&mut self, dt: f32) {
if self.config.paused { return; }
self.color_timer += dt * self.config.color_update_speed;
if self.color_timer >= 1.0 {
self.color_timer = 0.0;
for p in self.input.points.iter_mut() {
if p.active { p.color = random_color(); }
}
}
let splats = std::mem::take(&mut self.input.splat_queue);
let bursts = std::mem::take(&mut self.input.burst_queue);
for count in bursts {
self.fluid.multiple_splats(&self.renderer, count, &self.config);
}
for s in &splats {
self.fluid.splat(&self.renderer, s.x, s.y, s.dx, s.dy, s.color, &self.config);
}
let aspect = self.renderer.width as f32 / self.renderer.height as f32;
let force = self.config.splat_force;
for p in self.input.points.iter_mut() {
if p.active && p.moved {
let dx = (p.x - p.prev_x) * aspect * force;
let dy = (p.y - p.prev_y) * force;
self.fluid.splat(&self.renderer, p.x, p.y, dx, dy, p.color, &self.config);
p.moved = false;
}
}
self.fluid.step(&self.renderer, dt, &self.config);
}
pub fn render(&self) -> Result<(), wgpu::SurfaceError> {
self.renderer.render(
&self.fluid,
self.config.shading,
self.config.bloom,
self.config.sunrays,
[
self.config.back_color[0],
self.config.back_color[1],
self.config.back_color[2],
self.config.back_alpha,
],
self.config.bloom_color_tint,
self.config.bloom_hdr_power,
self.config.tone_map_exposure,
|_, _| {},
)
}
pub fn render_with_ui<F>(&self, draw_ui: F) -> Result<(), wgpu::SurfaceError>
where F: FnOnce(&mut wgpu::CommandEncoder, &wgpu::TextureView) {
self.renderer.render(
&self.fluid,
self.config.shading,
self.config.bloom,
self.config.sunrays,
[
self.config.back_color[0],
self.config.back_color[1],
self.config.back_color[2],
self.config.back_alpha,
],
self.config.bloom_color_tint,
self.config.bloom_hdr_power,
self.config.tone_map_exposure,
draw_ui,
)
}
pub fn render_with_ui_split<F>(&self, draw_ui: F) -> Result<(), wgpu::SurfaceError>
where F: FnOnce(&mut wgpu::CommandEncoder, &wgpu::TextureView, &wgpu::Device, &wgpu::Queue) {
let device = &self.renderer.device;
let queue = &self.renderer.queue;
self.renderer.render(
&self.fluid,
self.config.shading,
self.config.bloom,
self.config.sunrays,
[
self.config.back_color[0],
self.config.back_color[1],
self.config.back_color[2],
self.config.back_alpha,
],
self.config.bloom_color_tint,
self.config.bloom_hdr_power,
self.config.tone_map_exposure,
move |enc, view| draw_ui(enc, view, device, queue),
)
}
pub fn resize(&mut self, width: u32, height: u32) {
self.renderer.resize(width, height);
self.fluid.resize(&self.renderer, &self.config);
}
}