1pub mod anim;
16pub mod camera;
17pub mod format;
18pub mod math;
19pub mod model;
20pub mod morph;
21pub mod movement;
22pub mod rig;
23pub mod wire;
24
25use std::collections::HashMap;
26use std::sync::{Arc, LazyLock, RwLock};
27
28pub use anim::{BonePose, Clip, Pose};
29pub use camera::{CameraFrame, CameraOut};
30pub use math::{Mat4, Vec3};
31pub use model::{Model, Quad};
32pub use movement::TravelFrame;
33pub use rig::Rig;
34pub use wire::{Anchor, BoneOp, Capture, Frame, Mesh, Mode, Ops};
35
36#[derive(Default)]
37struct Store {
38 models: HashMap<String, Arc<Model>>,
39 clips: HashMap<(String, String), Arc<Clip>>,
41}
42
43static STORE: LazyLock<RwLock<Store>> = LazyLock::new(Default::default);
44
45fn namespace(id: &str) -> &str {
46 id.split_once(':').map_or("minecraft", |(ns, _)| ns)
47}
48
49pub fn register_model(id: &str, data: &[u8]) -> Result<(), String> {
51 let file: format::ModelFile = yog_data::read_bytes(data).map_err(|e| format!("model {id}: {e:?}"))?;
52 let model = Model::new(&file, namespace(id)).map_err(|e| format!("model {id}: {e}"))?;
53 STORE.write().unwrap_or_else(|e| e.into_inner()).models.insert(id.to_owned(), Arc::new(model));
54 Ok(())
55}
56
57pub fn register_anims(id: &str, data: &[u8]) -> Result<(), String> {
59 let file: format::AnimFile = yog_data::read_bytes(data).map_err(|e| format!("animations {id}: {e:?}"))?;
60 let clips = anim::Clips::new(&file).map_err(|e| format!("animations {id}: {e}"))?;
61 let mut store = STORE.write().unwrap_or_else(|e| e.into_inner());
62 for c in clips.0 {
63 store.clips.insert((id.to_owned(), c.name.clone()), Arc::new(c));
64 }
65 Ok(())
66}
67
68pub fn model(id: &str) -> Option<Arc<Model>> {
70 STORE.read().unwrap_or_else(|e| e.into_inner()).models.get(id).cloned()
71}
72
73pub fn clip(id: &str, name: &str) -> Option<Arc<Clip>> {
75 STORE.read().unwrap_or_else(|e| e.into_inner()).clips.get(&(id.to_owned(), name.to_owned())).cloned()
76}