1use {
2 crate::{
3 assets::{self, Asset, Model, ModelDesc, TypeModel},
4 config::config::FerrumConfig,
5 },
6 std::{
7 collections::HashMap,
8 marker::PhantomData,
9 sync::{
10 Arc,
11 atomic::{AtomicUsize, Ordering},
12 mpsc::{self, Receiver, Sender},
13 },
14 },
15 wgpu::{BindGroupLayout, Device, Queue},
16};
17
18pub struct Ingot<T> {
21 pub id: usize,
22 _marker: PhantomData<T>,
23}
24
25pub(crate) enum Bead<T> {
28 Burning,
29 Molten(T),
30 #[allow(dead_code)]
31 Ash,
32}
33
34pub struct ModelStore {
37 static_models: HashMap<usize, Bead<Model>>,
38 light_models: HashMap<usize, Bead<Model>>,
39 next_id: AtomicUsize,
40 sender: Sender<(usize, Model)>,
41 receiver: Receiver<(usize, Model)>,
42}
43
44impl ModelStore {
45 pub(crate) fn new() -> Self {
46 let (sender, receiver) = mpsc::channel::<(usize, Model)>();
47 Self {
48 static_models: HashMap::new(),
49 light_models: HashMap::new(),
50 next_id: AtomicUsize::new(0),
51 sender,
52 receiver,
53 }
54 }
55
56 pub(crate) fn spawn(
62 &mut self,
63 device: &Arc<Device>,
64 queue: &Arc<Queue>,
65 layout: &Arc<BindGroupLayout>,
66 model_desc: ModelDesc,
67 config: &FerrumConfig,
68 ) -> Ingot<Model> {
69 let id: usize = self.next_id.fetch_add(1, Ordering::SeqCst);
70
71 match model_desc.kind {
72 TypeModel::StaticObj => self.static_models.insert(id, Bead::Burning),
73 TypeModel::PointOfLight => self.light_models.insert(id, Bead::Burning),
74 };
75
76 let device: Arc<Device> = Arc::clone(device);
77 let queue: Arc<Queue> = Arc::clone(queue);
78 let layout: Arc<BindGroupLayout> = Arc::clone(layout);
79 let sender: Sender<(usize, Model)> = self.sender.clone();
80 let instances: Vec<assets::Instance> = model_desc.instances;
81 let kind: TypeModel = model_desc.kind;
82 let file_name: String = model_desc.file_name.to_string();
83 let asset: Asset = config.asset.clone();
84
85 #[cfg(not(target_arch = "wasm32"))]
86 std::thread::spawn(move || {
87 let result: Result<Model, anyhow::Error> = pollster::block_on(assets::load_model(
88 &asset, &file_name, &device, &queue, &layout, instances, kind,
89 ));
90 match result {
91 Ok(model) => {
92 let _ = sender.send((id, model));
93 }
94 Err(e) => log::error!("Failed to load model '{file_name}': {e:?}"),
95 }
96 });
97
98 #[cfg(target_arch = "wasm32")]
99 wasm_bindgen_futures::spawn_local(async move {
100 let result: Result<Model, anyhow::Error> = assets::load_model(
101 &asset, &file_name, &device, &queue, &layout, instances, kind,
102 )
103 .await;
104 match result {
105 Ok(model) => {
106 let _ = sender.send((id, model));
107 }
108 Err(e) => log::error!("Failed to load model '{file_name}': {e:?}"),
109 }
110 });
111
112 Ingot {
113 id,
114 _marker: PhantomData,
115 }
116 }
117
118 pub(crate) fn collect_loaded(&mut self) {
120 while let Ok((id, model)) = self.receiver.try_recv() {
121 match model.type_model {
122 TypeModel::StaticObj => self.static_models.insert(id, Bead::Molten(model)),
123 TypeModel::PointOfLight => self.light_models.insert(id, Bead::Molten(model)),
124 };
125 }
126 }
127
128 pub(crate) fn static_loaded(&self) -> impl Iterator<Item = &Model> {
129 self.static_models.values().filter_map(|bead| match bead {
130 Bead::Molten(model) => Some(model),
131 _ => None,
132 })
133 }
134
135 pub(crate) fn light_loaded(&self) -> impl Iterator<Item = &Model> {
136 self.light_models.values().filter_map(|bead| match bead {
137 Bead::Molten(model) => Some(model),
138 _ => None,
139 })
140 }
141
142 pub(crate) fn light_model_mut(&mut self, id: &usize) -> Option<&mut Model> {
143 match self.light_models.get_mut(id) {
144 Some(Bead::Molten(model)) => Some(model),
145 _ => None,
146 }
147 }
148}