Refactor Studio entry-points (#54)

This PR heavily refactors the entry-point functions in
`studio/src/lib.rs` to simplify and consolidate. Additionally, the
frontend application should now be configurable as expected with any
valid combination of the following URL parameters:

* `objectId` : A pre-bundled library object to load (e.g., "couch.gltf")
* `bvh` : Any resolvable path to a BVH animation with a MocapNET
skeleton (e.g., "mocap/shuffle.bvh")
* `mixamo` : Any resolvable path to a glTF animation with the Mixamo
skeleton (e.g., "mocap/hip-hop-dancing.gltf")
* `sceneImport` : Any resolvable path to an arbitrary glTF scene
(untested, but should theoretically work)
* `scene` : Any resolvable path to a previously saved Storyteller Studio
scene in `*.scn.ron` format
* `skybox` : The ID of a pre-bundled skybox asset. Valid options are:
  - "gum_trees_4k"
  - "kloofenfal_28d_misty_4k"
  - "meadow_4k"
  - "promenade_de_vidy_4k"
  - "scythian_tombs_4k"
  - "test_scene" (this is the default if not specified)
* `mode` : One of "studio" or "viewer" (the default if not specified)

"Validity" rules are as follows:

* `objectId`, `bvh`, `mixamo`, `sceneImport` and `scene` are mutually
exclusive
* `skybox` is not supported for `scene` (the skybox should be saved and
loaded as part of the scene description, though this is not currently
working)
* Both `mode` options are valid for all other parameters
This commit is contained in:
Danny McGee
2024-02-27 12:22:00 -05:00
committed by GitHub
parent d51eec52c3
commit c788bcf6b7
21 changed files with 564 additions and 554 deletions
+1 -4
View File
@@ -1,8 +1,5 @@
export * from "./lib/bvh-viewer.element";
export * from "./lib/canvas-provider.element";
export * from "./lib/editor.element";
export * from "./lib/mixamo-viewer.element";
export * from "./lib/scene-hierarchy.element";
export * from "./lib/studio.element";
export * from "./lib/transform-toolbar.element";
export * from "./lib/url-params";
export * from "./lib/viewer.element";
-34
View File
@@ -1,34 +0,0 @@
import * as studio from "@storyteller/studio";
import { ViewportSize } from "@storyteller/studio";
import { customElement, property } from "lit/decorators.js";
import { StudioBaseElement } from "./studio-base.element";
import { PropertyValues } from "lit";
@customElement("sts-bvh-viewer")
export class BvhViewerElement extends StudioBaseElement {
@property() bvhPath?: string;
@property() skyboxId?: string;
onStart(viewportSize: ViewportSize): void {
if (!this.bvhPath) return;
if (!this.skyboxId) return;
studio.startBvhViewer(
this.bvhPath,
this.skyboxId,
`#${this._canvasProvider!.canvasId}`,
"Storyteller Studio",
viewportSize,
);
}
onChanges(changes: PropertyValues<this>): void {
if (changes.has("bvhPath") && !!this.bvhPath) {
// TODO
}
if (changes.has("skyboxId") && !!this.skyboxId)
studio.setSkybox(this.skyboxId);
}
}
-28
View File
@@ -1,28 +0,0 @@
import * as studio from "@storyteller/studio";
import { ViewportSize } from "@storyteller/studio";
import { PropertyValues } from "lit";
import { customElement, property } from "lit/decorators.js";
import { StudioBaseElement } from "./studio-base.element";
@customElement("sts-editor")
export class EditorElement extends StudioBaseElement {
@property() scenePath?: string;
onStart(viewportSize: ViewportSize): void {
if (!this.scenePath) return;
studio.startEditor(
this.scenePath,
`#${this._canvasProvider!.canvasId}`,
"Storyteller Studio",
viewportSize,
);
}
onChanges(changes: PropertyValues<this>): void {
if (changes.has("scenePath") && !!this.scenePath) {
// TODO
}
}
}
@@ -1,34 +0,0 @@
import * as studio from "@storyteller/studio";
import { ViewportSize } from "@storyteller/studio";
import { PropertyValues } from "lit";
import { customElement, property } from "lit/decorators.js";
import { StudioBaseElement } from "./studio-base.element";
@customElement("sts-mixamo-viewer")
export class MixamoViewerElement extends StudioBaseElement {
@property() mixamoPath?: string;
@property() skyboxId?: string;
onStart(viewportSize: ViewportSize): void {
if (!this.mixamoPath) return;
if (!this.skyboxId) return;
studio.startMixamoViewer(
this.mixamoPath,
this.skyboxId,
`#${this._canvasProvider!.canvasId}`,
"Storyteller Studio",
viewportSize,
);
}
onChanges(changes: PropertyValues<this>): void {
if (changes.has("mixamoPath") && !!this.mixamoPath) {
// TODO
}
if (changes.has("skyboxId") && !!this.skyboxId)
studio.setSkybox(this.skyboxId);
}
}
@@ -1,16 +1,28 @@
import { inject } from "@storyteller/framework";
import * as studio from "@storyteller/studio";
import { ViewportSize } from "@storyteller/studio";
import { LitElement, PropertyValues, html, unsafeCSS } from "lit";
import {
type InitOptions,
type SkyboxId,
StudioMode,
ViewportSize,
} from "@storyteller/studio";
import { LitElement, type PropertyValues, html, unsafeCSS } from "lit";
import { customElement, property } from "lit/decorators.js";
import { CANVAS_PROVIDER, type CanvasProvider } from "./canvas-provider.element";
import styles from "./studio-base.element.scss?inline";
import styles from "./studio.element.scss?inline";
export abstract class StudioBaseElement extends LitElement {
@customElement("sts-studio")
export class StudioElement extends LitElement {
static override styles = unsafeCSS(styles);
get canvas() { return this.#canvas; }
@property() objectId?: string;
@property() bvhPath?: string;
@property() mixamoPath?: string;
@property() sceneImport?: string;
@property() storytellerScene?: string;
@property() skyboxId?: SkyboxId;
@property({ type: Number }) mode?: StudioMode;
@inject(CANVAS_PROVIDER)
_canvasProvider?: CanvasProvider;
@@ -19,9 +31,6 @@ export abstract class StudioBaseElement extends LitElement {
#resizeObserver?: ResizeObserver;
#frameRequest?: number;
abstract onStart(viewportSize: ViewportSize): void;
abstract onChanges?(changes: PropertyValues<this>): void;
override connectedCallback(): void {
super.connectedCallback();
@@ -53,7 +62,7 @@ export abstract class StudioBaseElement extends LitElement {
if (!studio.isInitialized()) {
this.#start();
} else {
this.onChanges?.(changes);
// TODO
}
}
@@ -75,10 +84,36 @@ export abstract class StudioBaseElement extends LitElement {
#start(): void {
if (!this.#canvas) return;
if (this.mode == null) return;
const { width, height } = this.getBoundingClientRect();
const options: InitOptions = {
mode: this.mode,
canvasSelector: `#${this._canvasProvider?.canvasId}`,
canvasAlt: "Storyteller Studio",
viewportSize: new ViewportSize(width, height),
};
try {
this.onStart(new ViewportSize(width, height));
if (
this.skyboxId
&& (this.objectId || this.bvhPath || this.mixamoPath || this.sceneImport)
) {
options.skyboxId = this.skyboxId;
if (this.objectId) {
studio.initFromLibrary(this.objectId, options);
} else if (this.bvhPath) {
studio.initFromBvh(this.bvhPath, options);
} else if (this.mixamoPath) {
studio.initFromMixamo(this.mixamoPath, options);
} else if (this.sceneImport) {
studio.initFromSceneImport(this.sceneImport, options);
}
}
else if (this.storytellerScene) {
studio.initFromStorytellerScene(this.storytellerScene, options);
}
} catch (err) {
if (!(err as Error).message?.startsWith("Using exceptions for control flow"))
throw err;
+9 -4
View File
@@ -1,4 +1,4 @@
import { StudioMode } from "@storyteller/studio";
import { type SkyboxId, StudioMode } from "@storyteller/studio";
import { match } from "@storyteller/utility";
export class StudioURLParams {
@@ -7,9 +7,14 @@ export class StudioURLParams {
return this.#_nativeParams ??= new URLSearchParams(document.location.search);
}
get bvh() { return this.#nativeParams.get("bvh"); }
get mixamo() { return this.#nativeParams.get("mixamo"); }
get scene() { return this.#nativeParams.get("scene"); }
get objectId() { return this.#nativeParams.get("objectId") ?? undefined; }
get bvh() { return this.#nativeParams.get("bvh") ?? undefined; }
get mixamo() { return this.#nativeParams.get("mixamo") ?? undefined; }
get sceneImport() { return this.#nativeParams.get("sceneImport") ?? undefined; }
get scene() { return this.#nativeParams.get("scene") ?? undefined; }
get skybox() {
return this.#nativeParams.get("skybox") as SkyboxId ?? undefined;
}
get mode(): StudioMode {
return match(this.#nativeParams.get("mode"), {
"studio": () => StudioMode.Editor,
-42
View File
@@ -1,42 +0,0 @@
import * as studio from "@storyteller/studio";
import { StudioMode, ViewportSize } from "@storyteller/studio";
import { PropertyValues } from "lit";
import { customElement, property } from "lit/decorators.js";
import { StudioBaseElement } from "./studio-base.element";
@customElement("sts-viewer")
export class ViewerElement extends StudioBaseElement {
@property() objectId?: string;
@property() skyboxId?: string;
@property({ type: Number }) mode?: StudioMode;
onStart(viewportSize: ViewportSize): void {
if (!this.objectId) return;
if (!this.skyboxId) return;
if (this.mode == null) return;
studio.start(
this.objectId,
this.skyboxId,
`#${this._canvasProvider!.canvasId}`,
"Storyteller Studio",
viewportSize,
this.mode,
);
}
onChanges(changes: PropertyValues<this>): void {
if (changes.has("objectId") && !!this.objectId)
studio.loadObject(this.objectId);
if (changes.has("skyboxId") && !!this.skyboxId)
studio.setSkybox(this.skyboxId);
}
}
declare global {
export interface HTMLElementTagNameMap {
"sts-viewer": ViewerElement;
}
}