use crate::component::Component;
use crate::entity::Entity;
use crate::world::World;
pub type ExtractFn = Box<dyn Fn(&World, Entity, &mut [f32]) -> bool + Send + Sync>;
pub trait ExtractToFloats: Component {
const STRIDE: usize;
fn extract(&self, buf: &mut [f32]);
}
#[allow(dead_code)]
pub struct ChannelRegistration {
pub name: String,
pub stride: usize,
pub extract_fn: ExtractFn,
}
pub struct RenderChannelRegistry {
pub channels: Vec<ChannelRegistration>,
}
impl RenderChannelRegistry {
pub fn new() -> Self {
Self {
channels: Vec::new(),
}
}
pub fn register<T: ExtractToFloats>(&mut self, name: impl Into<String>) {
let name = name.into();
if self.channels.iter().any(|c| c.name == name) {
panic!("render channel '{}' already registered", name);
}
let extract_fn =
Box::new(move |world: &World, entity: Entity, buf: &mut [f32]| {
match world.get::<T>(entity) {
Some(comp) => {
comp.extract(buf);
true
}
None => {
buf.fill(0.0);
false
}
}
});
self.channels.push(ChannelRegistration {
name,
stride: T::STRIDE,
extract_fn,
});
}
pub fn len(&self) -> usize {
self.channels.len()
}
pub fn is_empty(&self) -> bool {
self.channels.is_empty()
}
}
impl Default for RenderChannelRegistry {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::render::Transform;
#[derive(Debug)]
struct WearState {
wear: f32,
heat: f32,
tint: [f32; 3],
}
impl Component for WearState {}
impl ExtractToFloats for WearState {
const STRIDE: usize = 5;
fn extract(&self, buf: &mut [f32]) {
buf[0] = self.wear;
buf[1] = self.heat;
buf[2] = self.tint[0];
buf[3] = self.tint[1];
buf[4] = self.tint[2];
}
}
#[test]
fn empty_registry() {
let registry = RenderChannelRegistry::new();
assert!(registry.is_empty());
assert_eq!(registry.len(), 0);
}
#[test]
fn register_channel() {
let mut registry = RenderChannelRegistry::new();
registry.register::<WearState>("wear");
assert_eq!(registry.len(), 1);
assert_eq!(registry.channels[0].name, "wear");
assert_eq!(registry.channels[0].stride, 5);
}
#[test]
#[should_panic(expected = "already registered")]
fn duplicate_channel_panics() {
let mut registry = RenderChannelRegistry::new();
registry.register::<WearState>("wear");
registry.register::<WearState>("wear");
}
#[test]
fn extract_fn_returns_data_when_present() {
let mut world = World::new();
let entity = world.spawn((WearState {
wear: 0.75,
heat: 1.25,
tint: [0.1, 0.2, 0.3],
},));
let mut registry = RenderChannelRegistry::new();
registry.register::<WearState>("wear");
let channel = ®istry.channels[0];
let mut buf = vec![0.0f32; channel.stride];
let found = (channel.extract_fn)(&world, entity, &mut buf);
assert!(found);
assert!((buf[0] - 0.75).abs() < f32::EPSILON);
assert!((buf[1] - 1.25).abs() < f32::EPSILON);
assert!((buf[2] - 0.1).abs() < f32::EPSILON);
assert!((buf[3] - 0.2).abs() < f32::EPSILON);
assert!((buf[4] - 0.3).abs() < f32::EPSILON);
}
#[test]
fn extract_fn_zeroes_when_absent() {
let mut world = World::new();
let entity = world.spawn((Transform::identity(),));
let mut registry = RenderChannelRegistry::new();
registry.register::<WearState>("wear");
let channel = ®istry.channels[0];
let mut buf = vec![1.0f32; channel.stride]; let found = (channel.extract_fn)(&world, entity, &mut buf);
assert!(!found);
assert!(buf.iter().all(|&v| v == 0.0));
}
}