use std::sync::Arc;
use std::sync::Mutex;
use std::sync::atomic::{AtomicU64, Ordering};
use glam::Vec3;
use crate::scene::bounds::Aabb;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct GeometryId(pub u64);
pub fn next_geometry_id() -> GeometryId {
static COUNTER: AtomicU64 = AtomicU64::new(1);
GeometryId(COUNTER.fetch_add(1, Ordering::Relaxed))
}
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct MeshVertex {
pub position: Vec3,
pub normal: Vec3,
}
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct ScenePoint {
pub position: Vec3,
pub color: [f32; 4],
}
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct LineSegment {
pub start: Vec3,
pub end: Vec3,
pub color: [f32; 4],
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct MeshData {
pub vertices: Vec<MeshVertex>,
pub indices: Option<Vec<u32>>,
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct PointData {
pub points: Vec<ScenePoint>,
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct LineData {
pub segments: Vec<LineSegment>,
}
pub trait GeometryData: Send + Sync + 'static {
fn compute_bounds(&self) -> Aabb;
}
impl GeometryData for MeshData {
fn compute_bounds(&self) -> Aabb {
Aabb::from_points(self.vertices.iter().map(|v| v.position))
}
}
impl GeometryData for PointData {
fn compute_bounds(&self) -> Aabb {
Aabb::from_points(self.points.iter().map(|p| p.position))
}
}
impl GeometryData for LineData {
fn compute_bounds(&self) -> Aabb {
let mut bb = Aabb::EMPTY;
for seg in &self.segments {
bb.expand(seg.start);
bb.expand(seg.end);
}
bb
}
}
struct Inner<T> {
data: Arc<T>,
bounds: Aabb,
}
struct Store<T> {
id: GeometryId,
rev: AtomicU64,
inner: Mutex<Inner<T>>,
}
#[derive(Clone)]
pub struct GeometryHandle<T> {
store: Arc<Store<T>>,
}
impl<T: GeometryData> GeometryHandle<T> {
pub fn new(data: T) -> Self {
let bounds = data.compute_bounds();
Self {
store: Arc::new(Store {
id: next_geometry_id(),
rev: AtomicU64::new(1),
inner: Mutex::new(Inner {
data: Arc::new(data),
bounds,
}),
}),
}
}
pub fn set(&self, data: T) {
let bounds = data.compute_bounds();
{
let mut inner = self.store.inner.lock().unwrap();
inner.data = Arc::new(data);
inner.bounds = bounds;
}
self.store.rev.fetch_add(1, Ordering::Release);
}
pub fn id(&self) -> GeometryId {
self.store.id
}
pub fn revision(&self) -> u64 {
self.store.rev.load(Ordering::Acquire)
}
pub fn bounds(&self) -> Aabb {
self.store.inner.lock().unwrap().bounds
}
pub fn snapshot(&self) -> (Arc<T>, u64) {
let inner = self.store.inner.lock().unwrap();
let rev = self.store.rev.load(Ordering::Acquire);
(Arc::clone(&inner.data), rev)
}
}
impl<T: GeometryData> std::fmt::Debug for GeometryHandle<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("GeometryHandle")
.field("id", &self.id().0)
.field("revision", &self.revision())
.field("bounds", &self.bounds())
.finish()
}
}
pub type MeshHandle = GeometryHandle<MeshData>;
pub type PointsHandle = GeometryHandle<PointData>;
pub type LinesHandle = GeometryHandle<LineData>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ids_are_unique() {
let a = PointsHandle::new(PointData::default());
let b = PointsHandle::new(PointData::default());
assert_ne!(a.id(), b.id());
}
#[test]
fn set_advances_revision_and_recomputes_bounds() {
let h = PointsHandle::new(PointData::default());
assert_eq!(h.revision(), 1);
assert!(!h.bounds().is_valid());
h.set(PointData {
points: vec![
ScenePoint {
position: Vec3::ZERO,
color: [1.0; 4],
},
ScenePoint {
position: Vec3::new(2.0, 4.0, 6.0),
color: [1.0; 4],
},
],
});
assert_eq!(h.revision(), 2);
let bb = h.bounds();
assert_eq!(bb.min, Vec3::ZERO);
assert_eq!(bb.max, Vec3::new(2.0, 4.0, 6.0));
}
#[test]
fn snapshot_tracks_current_revision() {
let h = MeshHandle::new(MeshData::default());
let (_d0, r0) = h.snapshot();
assert_eq!(r0, 1);
h.set(MeshData {
vertices: vec![MeshVertex {
position: Vec3::ZERO,
normal: Vec3::Y,
}],
indices: None,
});
let (d1, r1) = h.snapshot();
assert_eq!(r1, 2);
assert_eq!(d1.vertices.len(), 1);
}
#[test]
fn clone_shares_store() {
let h = LinesHandle::new(LineData::default());
let c = h.clone();
assert_eq!(h.id(), c.id());
c.set(LineData {
segments: vec![LineSegment {
start: Vec3::ZERO,
end: Vec3::ONE,
color: [1.0; 4],
}],
});
assert_eq!(h.revision(), 2);
assert!(h.bounds().is_valid());
}
}