mirror of
https://github.com/storytold/storyteller-bevy.git
synced 2026-10-18 07:35:19 +00:00
Storyteller.ai API integration (#49)
This PR integrates asset retrieval from the Storyteller.ai back-end API, and begins implementing long-term scene saving and storage. ### Media retrieval Instead of fetching raw asset URLs, going forward, media files from remote sources (e.g., in URL parameters) should be specified with a path like the following: ``` remote://<token>.<filetype> ``` where * `token` is a media token (e.g., "m_70b77neq9jsgmy5v31mekzchcbd223") * `filetype` is a file extension appropriate for the type of file being fetched (e.g., "gltf") The `remote` protocol will be recognized by a [custom asset reader](https://github.com/storytold/storyteller-bevy/blob/70726db111f6ccafd47feb055c5546540fef1786/studio/src/remote/reader.rs) which will retrieve the file by calling a Storyteller API endpoint. ### Scene storage Additionally, this PR begins laying the groundwork for storing/retrieving Bevy scenes to/from the backend instead of solely in-memory. This work is still incomplete -- as of this PR, a button has been added to the web UI allowing the user to download the scene file locally for testing/debugging purposes. ### Test URL Media retrieval from the backend can be tested by [visiting this URL](https://deploy-preview-49--storyteller-studio.netlify.app/?mode=viewer&mixamo=remote://m_70b77neq9jsgmy5v31mekzchcbd223.gltf) in Chrome with the `--disable-web-security` flag (to bypass CORS errors)
This commit is contained in:
@@ -10,6 +10,13 @@ impl Plugin for PersistancePlugin {
|
||||
EditorTabName::Other("Persistance".to_string()),
|
||||
DebugPersistanceTab::default(),
|
||||
);
|
||||
|
||||
#[cfg(feature = "wasm")]
|
||||
app.add_systems(Update, wasm::forward_save_scene_events)
|
||||
.add_systems(
|
||||
OnEnter(SaveState::Idle),
|
||||
wasm::on_scene_saved.map(bevy::utils::warn),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,3 +50,64 @@ impl EditorTab for DebugPersistanceTab {
|
||||
"Persistance".into()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "wasm")]
|
||||
mod wasm {
|
||||
use bevy::{prelude::*, window::PrimaryWindow};
|
||||
use space_editor::prelude::{EditorEvent, EditorPrefabPath, PrefabMemoryCache};
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
use crate::wasm::{self, StaticBuffer, WasmMessageBuffer};
|
||||
|
||||
static SAVE_SCENE: WasmMessageBuffer<()> = WasmMessageBuffer::new();
|
||||
|
||||
#[wasm_bindgen(js_name = saveScene)]
|
||||
pub fn save_scene() {
|
||||
SAVE_SCENE.write_message(());
|
||||
}
|
||||
|
||||
pub(super) fn forward_save_scene_events(mut ew_editor_events: EventWriter<EditorEvent>) {
|
||||
if SAVE_SCENE.take_message().is_some() {
|
||||
ew_editor_events.send(EditorEvent::Save(EditorPrefabPath::MemoryCahce));
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen(typescript_custom_section)]
|
||||
const SCENE_SAVED_EVENT: &str = r#"
|
||||
export interface SceneSavedEvent extends CustomEvent {
|
||||
type: "scene-saved";
|
||||
detail: string;
|
||||
}
|
||||
|
||||
declare global {
|
||||
export interface GlobalEventHandlersEventMap {
|
||||
"scene-saved": SceneSavedEvent;
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
pub(super) fn on_scene_saved(
|
||||
r_scene_cache: Res<PrefabMemoryCache>,
|
||||
r_type_registry: Res<AppTypeRegistry>,
|
||||
ra_scenes: Res<Assets<DynamicScene>>,
|
||||
q_window: Query<&Window, With<PrimaryWindow>>,
|
||||
) -> Result<(), String> {
|
||||
let handle = r_scene_cache
|
||||
.scene
|
||||
.as_ref()
|
||||
.ok_or_else(|| "Scene cache is empty!".to_string())?;
|
||||
|
||||
let serialized = match ra_scenes.get(handle) {
|
||||
Some(scene) => scene
|
||||
.serialize_ron(&r_type_registry)
|
||||
.map_err(|err| format!("{err}")),
|
||||
None => Err("No scene found for cached handle!".into()),
|
||||
}?;
|
||||
|
||||
let Ok(window) = q_window.get_single() else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
wasm::dispatch_to_js(window, "scene-saved", &serialized)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user