Skip to main content

furmint_runtime/
scenes.rs

1//! Scenes are compositions of entities/components and scripts the user sees on their screen.
2//! A scene has a name, a list of scripts it uses (doesn't actually own them, retrieves from
3//! resource manager) and a list of entities (same as with scripts)
4
5use 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/// Scene asset
19#[derive(Deserialize, Debug, Clone, Component)]
20pub struct Scene {
21    /// Scene internal name.
22    pub name: String,
23
24    /// Entity descriptors in this scene.
25    pub entities: Vec<EntityDescriptor>,
26}
27
28/// Descriptor of an entity in a scene file
29#[derive(Deserialize, Debug, Clone)]
30pub struct EntityDescriptor {
31    /// Entity internal id.
32    pub id: String,
33
34    /// Components attached to this entity.
35    pub components: Vec<ComponentDescriptor>,
36}
37
38/// Descriptor of a component in a scene file
39#[derive(Deserialize, Debug, Clone)]
40pub struct ComponentDescriptor {
41    /// Component registry id.
42    pub id: String,
43
44    /// Component configuration.
45    pub config: Value,
46}
47
48/// Runtime state for the currently active scene
49#[derive(Debug, Default)]
50pub struct ActiveScene {
51    /// Active scene name
52    pub name: Option<String>,
53
54    /// Whether the active scene has been loaded
55    pub loaded: bool,
56
57    /// Entities owned by the active scene
58    pub entities: Vec<specs::Entity>,
59}
60
61/// Scene commands
62#[derive(Debug)]
63pub enum SceneCommand {
64    /// Switch to a scene
65    SwitchTo(String),
66    /// Reload scene
67    Reload,
68    /// Unload scene
69    Unload,
70}
71
72/// Scene commands
73#[derive(Default, Debug)]
74pub struct SceneCommands {
75    queue: Vec<SceneCommand>,
76}
77
78/// Scene library, handles keyed by name
79#[derive(Debug, Default)]
80pub struct SceneLibrary {
81    /// Scenes themselves
82    pub scenes: HashMap<String, Handle<Scene>>,
83}
84
85impl SceneCommands {
86    /// Switch to another scene
87    pub fn switch_to(&mut self, name: impl Into<String>) {
88        self.queue.push(SceneCommand::SwitchTo(name.into()));
89    }
90
91    /// Reload current scene
92    pub fn reload(&mut self) {
93        self.queue.push(SceneCommand::Reload);
94    }
95
96    /// Unload current scne
97    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    /// Is the scene commands queue empty
107    pub fn is_empty(&self) -> bool {
108        self.queue.is_empty()
109    }
110}
111
112impl ActiveScene {
113    /// Set the current scene
114    pub fn set(&mut self, name: impl Into<String>) {
115        self.name = Some(name.into());
116        self.loaded = false;
117    }
118
119    /// Unload current scene
120    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    /// Is the current scene loaded?
128    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
146/// Scene loading system
147pub(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}