furmint_runtime/
scenes.rs1use furmint_registry::ComponentRegistry;
6use furmint_resources::ResourceResult;
7use furmint_resources::assets::Handle;
8use furmint_resources::loader::ErasedAssetLoader;
9use log::debug;
10use ron::Value;
11use serde::Deserialize;
12use specs::{
13 Component, DenseVecStorage, Entities, LazyUpdate, Read, ReadExpect, System, WriteExpect,
14};
15use std::any::Any;
16use std::collections::HashMap;
17
18#[derive(Deserialize, Debug, Clone, Component)]
20pub struct Scene {
21 pub name: String,
23
24 pub entities: Vec<EntityDescriptor>,
26}
27
28#[derive(Deserialize, Debug, Clone)]
30pub struct EntityDescriptor {
31 pub id: String,
33
34 pub components: Vec<ComponentDescriptor>,
36}
37
38#[derive(Deserialize, Debug, Clone)]
40pub struct ComponentDescriptor {
41 pub id: String,
43
44 pub config: Value,
46}
47
48#[derive(Debug, Default)]
50pub struct ActiveScene {
51 pub name: Option<String>,
53
54 pub loaded: bool,
56
57 pub entities: Vec<specs::Entity>,
59}
60
61#[derive(Debug)]
63pub enum SceneCommand {
64 SwitchTo(String),
66 Reload,
68 Unload,
70}
71
72#[derive(Default, Debug)]
74pub struct SceneCommands {
75 queue: Vec<SceneCommand>,
76}
77
78#[derive(Debug, Default)]
80pub struct SceneLibrary {
81 pub scenes: HashMap<String, Handle<Scene>>,
83}
84
85impl SceneCommands {
86 pub fn switch_to(&mut self, name: impl Into<String>) {
88 self.queue.push(SceneCommand::SwitchTo(name.into()));
89 }
90
91 pub fn reload(&mut self) {
93 self.queue.push(SceneCommand::Reload);
94 }
95
96 pub fn unload(&mut self) {
98 self.queue.push(SceneCommand::Unload);
99 }
100
101 #[allow(dead_code)]
102 pub(crate) fn drain(&mut self) -> impl Iterator<Item = SceneCommand> + '_ {
103 self.queue.drain(..)
104 }
105
106 pub fn is_empty(&self) -> bool {
108 self.queue.is_empty()
109 }
110}
111
112impl ActiveScene {
113 pub fn set(&mut self, name: impl Into<String>) {
115 self.name = Some(name.into());
116 self.loaded = false;
117 }
118
119 pub fn clear(&mut self) {
121 debug!("unloading scene {:?}", self.name);
122 self.name = None;
123 self.loaded = false;
124 self.entities.clear();
125 }
126
127 pub fn is_loaded(&self) -> bool {
129 self.loaded
130 }
131}
132
133#[derive(Debug)]
134pub(crate) struct SceneLoader;
135
136impl ErasedAssetLoader for SceneLoader {
137 fn load_erased(
138 &self,
139 reader: &mut dyn std::io::Read,
140 ) -> ResourceResult<Box<dyn Any + Send + Sync>> {
141 let scene: Scene = ron::de::from_reader(reader).unwrap();
142 Ok(Box::new(scene))
143 }
144}
145
146pub(crate) struct SceneSystem;
148
149impl<'a> System<'a> for SceneSystem {
150 type SystemData = (
151 Entities<'a>,
152 WriteExpect<'a, ActiveScene>,
153 ReadExpect<'a, ComponentRegistry>,
154 Read<'a, LazyUpdate>,
155 ReadExpect<'a, SceneLibrary>,
156 );
157
158 fn run(
159 &mut self,
160 (entities, mut active_scene, component_registry, lazy, library): Self::SystemData,
161 ) {
162 if active_scene.loaded {
163 return;
164 }
165
166 let Some(active_name) = active_scene.name.as_deref() else {
167 return;
168 };
169
170 let scene_handle = library
171 .scenes
172 .get(active_name)
173 .unwrap_or_else(|| panic!("scene {active_name} not found"));
174
175 let scene = scene_handle.read();
176
177 debug!("loading scene '{}'", scene.name);
178
179 for ent_def in &scene.entities {
180 let entity = entities.create();
181
182 debug!("creating scene entity '{}'", ent_def.id);
183
184 for component in &ent_def.components {
185 debug!(
186 "loading component '{}' with parameters {:?}",
187 component.id, component.config
188 );
189
190 let factory = component_registry
191 .get(&component.id)
192 .unwrap_or_else(|| panic!("component factory '{}' not found", component.id));
193
194 factory.insert_lazy(&lazy, entity, &component.config);
195 }
196
197 active_scene.entities.push(entity);
198 }
199
200 active_scene.loaded = true;
201 }
202}