Skip to main content

nightshade_api/
prefab.rs

1//! Runtime prefabs: capture a configured entity and its children once, then
2//! stamp copies of it anywhere. A prefab is a self-contained snapshot of a
3//! subtree with its meshes and textures embedded, so it survives a
4//! [`save_prefab`] round trip and instances independently of the original.
5
6use nightshade::ecs::scene::commands::{save_scene_binary_to_bytes, spawn_scene, subtree_to_scene};
7use nightshade::ecs::scene::{
8    Scene, SceneError, apply_scene_settings, capture_scene_settings, embed_referenced_meshes,
9    embed_referenced_textures, load_scene_binary_from_bytes,
10};
11use nightshade::prelude::{Entity, Vec3, World, tracing};
12
13/// A reusable template captured from a live entity subtree. Build one with
14/// [`make_prefab`] and place copies with [`spawn_prefab`].
15#[derive(Clone)]
16pub struct Prefab {
17    scene: Scene,
18}
19
20/// Captures `root` and everything parented under it into a prefab, embedding the
21/// meshes and textures it references so the template is self-contained.
22pub fn make_prefab(world: &mut World, root: Entity) -> Prefab {
23    let mut scene = subtree_to_scene(world, root, "prefab");
24    embed_referenced_meshes(world, &mut scene);
25    embed_referenced_textures(world, &mut scene);
26    Prefab { scene }
27}
28
29/// Stamps a fresh copy of `prefab` into the world with its root moved to
30/// `position`, returning the root entities of the new instance. Scene-level
31/// render settings are preserved across the spawn, which the underlying scene
32/// load would otherwise overwrite.
33pub fn spawn_prefab(world: &mut World, prefab: &Prefab, position: Vec3) -> Vec<Entity> {
34    let settings = capture_scene_settings(world);
35    let atmosphere = world
36        .res::<nightshade::render::config::RenderSettings>()
37        .atmosphere;
38    let roots = match spawn_scene(world, &prefab.scene, None) {
39        Ok(result) => result.root_entities,
40        Err(error) => {
41            tracing::error!("Failed to spawn prefab: {error}");
42            Vec::new()
43        }
44    };
45    apply_scene_settings(world, &settings);
46    world
47        .res_mut::<nightshade::render::config::RenderSettings>()
48        .atmosphere = atmosphere;
49    for &root in &roots {
50        crate::placement::set_position(world, root, position);
51    }
52    roots
53}
54
55/// Captures `root` as a prefab and stores it under `name` so a later
56/// [`spawn_prefab_named_command`] can stamp copies without holding the prefab
57/// value, which is what the data and script command surface needs.
58pub(crate) fn register_prefab_command(world: &mut World, name: &str, root: Entity) -> bool {
59    let prefab = make_prefab(world, root);
60    world
61        .res_mut::<nightshade::ecs::asset_state::AssetState>()
62        .scene_prefabs
63        .insert(name.to_string(), prefab.scene);
64    true
65}
66
67/// Stamps a copy of the prefab registered under `name`, or nothing when no
68/// prefab is registered there.
69pub(crate) fn spawn_prefab_named_command(
70    world: &mut World,
71    name: &str,
72    position: Vec3,
73) -> Vec<Entity> {
74    let Some(scene) = world
75        .res::<nightshade::ecs::asset_state::AssetState>()
76        .scene_prefabs
77        .get(name)
78        .cloned()
79    else {
80        return Vec::new();
81    };
82    spawn_prefab(world, &Prefab { scene }, position)
83}
84
85/// Serializes a prefab to self-contained bytes for storing on disk.
86pub fn save_prefab(prefab: &Prefab) -> Result<Vec<u8>, SceneError> {
87    let mut scene = prefab.scene.clone();
88    save_scene_binary_to_bytes(&mut scene)
89}
90
91/// Loads a prefab from bytes written by [`save_prefab`].
92pub fn load_prefab(bytes: &[u8]) -> Result<Prefab, SceneError> {
93    let scene = load_scene_binary_from_bytes(bytes)?;
94    Ok(Prefab { scene })
95}