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:
Danny McGee
2024-02-23 21:05:32 -05:00
committed by GitHub
parent fffcb38ec6
commit 6ca2a992e5
10 changed files with 770 additions and 27 deletions
+32 -1
View File
@@ -5,6 +5,7 @@ import {
type EntityMultiSpawnEvent,
type EntitySelectEvent,
type EntitySpawnEvent,
type SceneSavedEvent,
type SceneStateEvent,
TransformSpace,
TransformType,
@@ -150,6 +151,22 @@ export class AppElement extends LitElement {
this.#updateHierarchy();
}
@on("window:scene-saved")
onSceneSaved({ detail: scene }: SceneSavedEvent): void {
const fileBlob = new Blob([scene], { type: "text/ron" });
const url = URL.createObjectURL(fileBlob);
const anchor = document.createElement("a");
anchor.href = url;
anchor.download = "scene.scn.ron";
anchor.click();
setTimeout(() => {
URL.revokeObjectURL(url);
anchor.remove();
});
}
toggleHelp(): void {
this.showHelp = !this.showHelp;
}
@@ -211,6 +228,7 @@ export class AppElement extends LitElement {
class="app-root"
${ref(this.#canvasProvider as any)}
>
<!-- Scene view -->
${this.bvhPath ? html`
<sts-bvh-viewer
bvhPath=${this.bvhPath}
@@ -232,11 +250,20 @@ export class AppElement extends LitElement {
></sts-viewer>
`}
<!-- Actions toolbar -->
<sts-toolbar class="v-start h-start">
<sts-toolbar-group>
${this.mode === StudioMode.Editor ? html`
<sts-button
icon="floppy-disk"
@click=${() => studio.saveScene()}
>
Save
</sts-button>
` : nothing}
<sts-button
class=${classMap({ active: this.showHelp })}
icon="circle-info"
icon="question"
@click=${this.toggleHelp}
>
Help
@@ -245,6 +272,7 @@ export class AppElement extends LitElement {
</sts-toolbar>
${this.mode === StudioMode.Editor ? html`
<!-- Transform toolbar -->
<sts-transform-toolbar
class="v-start h-center"
.type=${this.transformType}
@@ -253,6 +281,7 @@ export class AppElement extends LitElement {
@space-change=${this.onTransformSpaceChange}
></sts-transform-toolbar>
<!-- Hierarchy and inspector -->
<div class="hierarchy v-start h-end">
${this.sceneObjects.map(obj => html`
<sts-tree class="scene-object"
@@ -264,10 +293,12 @@ export class AppElement extends LitElement {
</div>
` : nothing}
<!-- Animation timeline -->
${this.mode === StudioMode.Editor ? html`
<sts-anim-editor class="timeline v-end h-start h-before-hierarchy"></sts-anim-editor>
` : nothing}
<!-- Dialogs and modals -->
${this.showHelp ? html`
<sts-help class="help v-after-toolbar h-start"></sts-help>
` : nothing}