Scene storage backend integration (#51)

This PR adds Storyteller.ai API integration for saving and loading
Storyteller Studio scenes to/from the backend.

### Known issues
* The skybox is not currently stored in the serialized scene format
This commit is contained in:
Danny McGee
2024-02-23 22:04:06 -05:00
committed by GitHub
parent 6ca2a992e5
commit 7b06d48c5d
14 changed files with 244 additions and 62 deletions
+56 -8
View File
@@ -13,7 +13,10 @@ use bevy::{
scene::InstanceId,
};
use space_editor::{prelude::PrefabMarker, space_prefab::component::SceneAutoChild};
use space_editor::{
prelude::{EditorEvent, EditorPrefabPath, PrefabMarker},
space_prefab::component::SceneAutoChild,
};
#[cfg(feature = "wasm")]
use wasm_bindgen::prelude::wasm_bindgen;
use wgpu::{TextureDescriptor, TextureUsages};
@@ -37,6 +40,7 @@ impl Plugin for ScenePlugin {
app.insert_resource(ClearColor(Color::hsl(0.0, 0.0, 0.25)))
.insert_resource(Msaa::Off)
.add_state::<SceneState>()
.add_event::<LoadLibraryObjectEvent>()
.add_event::<LoadSceneEvent>()
.add_systems(
Startup,
@@ -45,7 +49,10 @@ impl Plugin for ScenePlugin {
initialiaze_headless.run_if(is_headless),
),
)
.add_systems(Update, init_scene_loading.in_set(SceneSpawnerSystem))
.add_systems(
Update,
(init_object_loading, init_scene_loading).in_set(SceneSpawnerSystem),
)
.add_systems(
OnEnter(SceneState::Active),
(
@@ -78,7 +85,7 @@ impl Plugin for ScenePlugin {
app.add_systems(
Update,
(
wasm::dispatch_scene_request,
wasm::dispatch_object_load_request,
wasm::notify_state_changes
.map(bevy::utils::error)
.run_if(resource_changed::<State<SceneState>>()),
@@ -172,6 +179,33 @@ fn auto_add_handle<T: Asset>(
}
}
/// Request the application to load a `gltf`/`glb` scene from the "bundled"
/// asset library in `assets/gltf/`. Expects a filename including the extension.
///
/// ```ignore
/// fn main() {
/// App::new()
/// .add_plugins((
/// DefaultPlugins,
/// CommonPlugins,
/// ))
/// .add_systems(Startup, load_mannequin)
/// }
///
/// fn load_mannequin(mut ew_load_object: EventWriter<LoadLibraryObjectEvent>) {
/// ew_load_object.send(LoadLibraryObjectEvent("Mannequin.gltf".into()));
/// }
/// ```
#[derive(Event, Deref)]
pub struct LoadLibraryObjectEvent(pub String);
/// Request the application to load a previously saved `*.scn.ron` dynamic scene
/// file.
///
/// Expects a full, resolvable path to the file (e.g. `scenes/my-scene.scn.ron`
/// for a local file saved under `studio/assets/scenes`, or
/// `remote://<media_token>.scn.ron` for a scene saved to the Storyteller.ai
/// backend).
#[derive(Event, Deref)]
pub struct LoadSceneEvent(pub String);
@@ -270,9 +304,9 @@ fn initialiaze_headless(
));
}
fn init_scene_loading(mut cmd: Commands, mut er: EventReader<LoadSceneEvent>) {
fn init_object_loading(mut cmd: Commands, mut er: EventReader<LoadLibraryObjectEvent>) {
if let Some(id) = er.read().last() {
info!("Loading scene: {}", id.0);
info!("Loading library asset: {}", id.0);
cmd.spawn((
SpatialBundle::default(),
space_editor::prelude::GltfPrefab {
@@ -285,6 +319,18 @@ fn init_scene_loading(mut cmd: Commands, mut er: EventReader<LoadSceneEvent>) {
}
}
fn init_scene_loading(
mut er_load_scene: EventReader<LoadSceneEvent>,
mut ew_editor: EventWriter<EditorEvent>,
) {
if let Some(scene_path) = er_load_scene.read().last() {
info!("Loading scene: {}", scene_path.0);
ew_editor.send(EditorEvent::Load(EditorPrefabPath::File(
scene_path.0.clone(),
)));
}
}
#[derive(Component)]
pub struct NoMeshElement;
@@ -442,7 +488,7 @@ mod wasm {
use bevy::{prelude::*, window::PrimaryWindow};
use wasm_bindgen::prelude::*;
use super::{LoadSceneEvent, SceneState};
use super::{LoadLibraryObjectEvent, SceneState};
use crate::wasm::{self, StaticBuffer, WasmMessageBuffer};
static OBJECT_LOAD_REQUEST: WasmMessageBuffer<String> = WasmMessageBuffer::new();
@@ -454,9 +500,11 @@ mod wasm {
// FIXME: Is it an object or a scene? Inconsistent terminology is
// unnecessarily confusing
pub(super) fn dispatch_scene_request(mut ew_load_scene: EventWriter<LoadSceneEvent>) {
pub(super) fn dispatch_object_load_request(
mut ew_load_scene: EventWriter<LoadLibraryObjectEvent>,
) {
if let Some(id) = OBJECT_LOAD_REQUEST.take_message() {
ew_load_scene.send(LoadSceneEvent(id));
ew_load_scene.send(LoadLibraryObjectEvent(id));
}
}