#![allow(clippy::too_many_arguments)]
use crate::input::{Input, KeyCode, MouseButton};
use glam::{Vec2, Vec3, Vec4};
use std::collections::HashMap;
#[derive(Clone, Copy, Debug)]
pub enum UniformValue {
F32(f32),
I32(i32),
U32(u32),
Vec2(Vec2),
Vec3(Vec3),
Vec4(Vec4),
}
impl UniformValue {
pub fn wgsl_type(&self) -> &'static str {
match self {
UniformValue::F32(_) => "f32",
UniformValue::I32(_) => "i32",
UniformValue::U32(_) => "u32",
UniformValue::Vec2(_) => "vec2<f32>",
UniformValue::Vec3(_) => "vec3<f32>",
UniformValue::Vec4(_) => "vec4<f32>",
}
}
pub fn byte_size(&self) -> usize {
match self {
UniformValue::F32(_) => 4,
UniformValue::I32(_) => 4,
UniformValue::U32(_) => 4,
UniformValue::Vec2(_) => 8,
UniformValue::Vec3(_) => 12, UniformValue::Vec4(_) => 16,
}
}
pub fn write_bytes(&self, buf: &mut Vec<u8>) {
match self {
UniformValue::F32(v) => buf.extend_from_slice(&v.to_le_bytes()),
UniformValue::I32(v) => buf.extend_from_slice(&v.to_le_bytes()),
UniformValue::U32(v) => buf.extend_from_slice(&v.to_le_bytes()),
UniformValue::Vec2(v) => {
buf.extend_from_slice(&v.x.to_le_bytes());
buf.extend_from_slice(&v.y.to_le_bytes());
}
UniformValue::Vec3(v) => {
buf.extend_from_slice(&v.x.to_le_bytes());
buf.extend_from_slice(&v.y.to_le_bytes());
buf.extend_from_slice(&v.z.to_le_bytes());
}
UniformValue::Vec4(v) => {
buf.extend_from_slice(&v.x.to_le_bytes());
buf.extend_from_slice(&v.y.to_le_bytes());
buf.extend_from_slice(&v.z.to_le_bytes());
buf.extend_from_slice(&v.w.to_le_bytes());
}
}
}
}
impl From<f32> for UniformValue {
fn from(v: f32) -> Self {
UniformValue::F32(v)
}
}
impl From<i32> for UniformValue {
fn from(v: i32) -> Self {
UniformValue::I32(v)
}
}
impl From<u32> for UniformValue {
fn from(v: u32) -> Self {
UniformValue::U32(v)
}
}
impl From<Vec2> for UniformValue {
fn from(v: Vec2) -> Self {
UniformValue::Vec2(v)
}
}
impl From<Vec3> for UniformValue {
fn from(v: Vec3) -> Self {
UniformValue::Vec3(v)
}
}
impl From<Vec4> for UniformValue {
fn from(v: Vec4) -> Self {
UniformValue::Vec4(v)
}
}
#[derive(Clone, Debug, Default)]
pub struct CustomUniforms {
values: Vec<(String, UniformValue)>,
indices: HashMap<String, usize>,
}
impl CustomUniforms {
pub fn new() -> Self {
Self::default()
}
pub fn set<V: Into<UniformValue>>(&mut self, name: &str, value: V) {
let value = value.into();
if let Some(&idx) = self.indices.get(name) {
self.values[idx].1 = value;
} else {
let idx = self.values.len();
self.values.push((name.to_string(), value));
self.indices.insert(name.to_string(), idx);
}
}
pub fn get(&self, name: &str) -> Option<&UniformValue> {
self.indices.get(name).map(|&idx| &self.values[idx].1)
}
pub fn is_empty(&self) -> bool {
self.values.is_empty()
}
pub fn len(&self) -> usize {
self.values.len()
}
pub fn iter(&self) -> impl Iterator<Item = (&str, &UniformValue)> {
self.values.iter().map(|(n, v)| (n.as_str(), v))
}
pub(crate) fn to_wgsl_fields(&self) -> String {
self.values
.iter()
.map(|(name, value)| format!(" {}: {},", name, value.wgsl_type()))
.collect::<Vec<_>>()
.join("\n")
}
pub(crate) fn to_bytes(&self) -> Vec<u8> {
let mut buf = Vec::new();
for (_, value) in &self.values {
let align = match value {
UniformValue::Vec4(_) | UniformValue::Vec3(_) => 16,
UniformValue::Vec2(_) => 8,
_ => 4,
};
while buf.len() % align != 0 {
buf.push(0);
}
value.write_bytes(&mut buf);
}
buf
}
pub(crate) fn byte_size(&self) -> usize {
let bytes = self.to_bytes();
(bytes.len() + 15) & !15
}
}
pub struct UpdateContext<'a> {
pub(crate) uniforms: &'a mut CustomUniforms,
pub input: &'a Input,
pub(crate) time: f32,
pub(crate) delta_time: f32,
pub(crate) bounds: f32,
pub(crate) aspect_ratio: f32,
pub(crate) grid_opacity: &'a mut Option<f32>,
pub(crate) readback_requested: &'a mut bool,
pub(crate) readback_data: Option<&'a [u8]>,
}
impl<'a> UpdateContext<'a> {
pub(crate) fn new(
uniforms: &'a mut CustomUniforms,
input: &'a Input,
time: f32,
delta_time: f32,
bounds: f32,
aspect_ratio: f32,
grid_opacity: &'a mut Option<f32>,
readback_requested: &'a mut bool,
readback_data: Option<&'a [u8]>,
) -> Self {
Self {
uniforms,
input,
time,
delta_time,
bounds,
aspect_ratio,
grid_opacity,
readback_requested,
readback_data,
}
}
pub fn time(&self) -> f32 {
self.time
}
pub fn delta_time(&self) -> f32 {
self.delta_time
}
pub fn mouse_ndc(&self) -> Vec2 {
self.input.mouse_ndc()
}
pub fn mouse_world_pos(&self) -> Vec3 {
let ndc = self.input.mouse_ndc();
Vec3::new(
ndc.x * self.bounds * self.aspect_ratio,
ndc.y * self.bounds,
0.0,
)
}
pub fn bounds(&self) -> f32 {
self.bounds
}
pub fn aspect_ratio(&self) -> f32 {
self.aspect_ratio
}
pub fn mouse_pressed(&self) -> bool {
self.input.mouse_held(MouseButton::Left)
}
pub fn key_pressed(&self, key: KeyCode) -> bool {
self.input.key_pressed(key)
}
pub fn key_held(&self, key: KeyCode) -> bool {
self.input.key_held(key)
}
pub fn set<V: Into<UniformValue>>(&mut self, name: &str, value: V) {
self.uniforms.set(name, value);
}
pub fn get(&self, name: &str) -> Option<&UniformValue> {
self.uniforms.get(name)
}
pub fn set_grid_opacity(&mut self, opacity: f32) {
*self.grid_opacity = Some(opacity.clamp(0.0, 1.0));
}
pub fn request_readback(&mut self) {
*self.readback_requested = true;
}
pub fn particles_raw(&self) -> Option<&[u8]> {
self.readback_data
}
pub fn with_particles<T, F>(&self, f: F) -> Option<T>
where
F: FnOnce(&[u8]) -> T,
{
self.readback_data.map(f)
}
}