use super::{Model, ModelData, ModelDataGroup};
use crate::internal::UpdateMessage;
use cgmath::{Euler, Rad, Vector3};
use parking_lot::RwLock;
use std::sync::{
atomic::{AtomicU64, Ordering},
mpsc::Sender,
Arc,
};
static ID: AtomicU64 = AtomicU64::new(1);
pub struct ModelHandle {
id: u64,
message_handle: Sender<UpdateMessage>,
data: Arc<RwLock<ModelData>>,
}
impl ModelHandle {
pub fn position(&self) -> Vector3<f32> {
self.read(|d| d.position)
}
pub fn rotation(&self) -> Euler<Rad<f32>> {
self.read(|d| d.rotation)
}
pub fn scale(&self) -> f32 {
self.read(|d| d.scale)
}
pub fn read<T>(&self, cb: impl FnOnce(&ModelData) -> T) -> T {
let data = self.data.read();
cb(&data)
}
pub fn modify<T>(&self, cb: impl FnOnce(&mut ModelData) -> T) -> T {
let mut data = self.data.write();
cb(&mut data)
}
}
impl Clone for ModelHandle {
fn clone(&self) -> Self {
let new_id = ID.fetch_add(1, Ordering::Relaxed);
let message_handle = self.message_handle.clone();
let data = self.data.read();
let data = Arc::new(RwLock::new(ModelData {
position: data.position,
rotation: data.rotation,
scale: data.scale,
groups: data.groups.clone(),
}));
let _ = self.message_handle.send(UpdateMessage::NewModel {
old_id: self.id,
new_id,
data: data.clone(),
});
ModelHandle {
id: new_id,
message_handle,
data,
}
}
}
impl Drop for ModelHandle {
fn drop(&mut self) {
let _ = self
.message_handle
.send(UpdateMessage::ModelDropped(self.id));
}
}
pub struct ModelRef {
pub model: Arc<Model>,
pub data: Arc<RwLock<ModelData>>,
}
impl ModelRef {
pub fn new(
model: Arc<Model>,
message_handle: Sender<UpdateMessage>,
mut data: ModelData,
) -> (u64, ModelRef, ModelHandle) {
let id = ID.fetch_add(1, Ordering::Relaxed);
let groups = (0..model.groups.len())
.map(|_| ModelDataGroup::default())
.collect();
data.groups = groups;
let data = Arc::new(RwLock::new(data));
(
id,
ModelRef {
model,
data: data.clone(),
},
ModelHandle {
id,
data,
message_handle,
},
)
}
pub fn with_new_data(&self, data: Arc<RwLock<ModelData>>) -> Self {
ModelRef {
model: self.model.clone(),
data,
}
}
}