mirror of
https://github.com/storytold/storyteller-bevy.git
synced 2026-10-18 07:35:19 +00:00
Load remote BVH from URL param (#30)
This PR replaces the hard-coded BVH animation with a URL parameter, and adds a specialized BVH viewer web component and entry-point function for previewing the specified animation on our generic mannequin.
This commit is contained in:
@@ -1,2 +1,3 @@
|
||||
export * from "./lib/bvh-viewer.element";
|
||||
export * from "./lib/canvas-provider.element";
|
||||
export * from "./lib/viewer.element";
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import { inject } from "@storyteller/framework";
|
||||
import * as studio from "@storyteller/studio";
|
||||
import { ViewportSize } from "@storyteller/studio";
|
||||
import { LitElement, PropertyValues, html, unsafeCSS } from "lit";
|
||||
|
||||
import { CANVAS_PROVIDER, type CanvasProvider } from "./canvas-provider.element";
|
||||
|
||||
import styles from "./studio-base.element.scss?inline";
|
||||
|
||||
export abstract class StudioBaseElement extends LitElement {
|
||||
static override styles = unsafeCSS(styles);
|
||||
|
||||
@inject(CANVAS_PROVIDER)
|
||||
_canvasProvider?: CanvasProvider;
|
||||
|
||||
#canvas?: HTMLCanvasElement;
|
||||
#resizeObserver?: ResizeObserver;
|
||||
|
||||
abstract onStart(viewportSize: ViewportSize): void;
|
||||
abstract onChanges?(changes: PropertyValues<this>): void;
|
||||
|
||||
override connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
|
||||
this.#resizeObserver = new ResizeObserver(this.#onResize.bind(this));
|
||||
this.#resizeObserver.observe(this);
|
||||
|
||||
if (!this._canvasProvider)
|
||||
throw new Error(`No provider found for token: ${CANVAS_PROVIDER}`);
|
||||
|
||||
if ((this.#canvas = this._canvasProvider.takeCanvas()))
|
||||
this.appendChild(this.#canvas);
|
||||
}
|
||||
|
||||
override disconnectedCallback(): void {
|
||||
if (this.#canvas)
|
||||
this._canvasProvider?.returnCanvas(this.removeChild(this.#canvas));
|
||||
|
||||
this.#resizeObserver?.disconnect();
|
||||
|
||||
super.disconnectedCallback();
|
||||
}
|
||||
|
||||
protected override updated(changes: PropertyValues<this>): void {
|
||||
if (!studio.isInitialized()) {
|
||||
this.#start();
|
||||
} else {
|
||||
this.onChanges?.(changes);
|
||||
}
|
||||
}
|
||||
|
||||
#onResize(): void {
|
||||
const { width, height } = this.getBoundingClientRect();
|
||||
studio.resize(new ViewportSize(width, height));
|
||||
}
|
||||
|
||||
#start(): void {
|
||||
if (!this.#canvas) return;
|
||||
|
||||
const { width, height } = this.getBoundingClientRect();
|
||||
try {
|
||||
this.onStart(new ViewportSize(width, height));
|
||||
} catch (err) {
|
||||
if (!(err as Error).message?.startsWith("Using exceptions for control flow"))
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
protected override render = () => html`
|
||||
<slot name="canvas"></slot>
|
||||
`;
|
||||
}
|
||||
@@ -1,99 +1,37 @@
|
||||
import * as studio from "@storyteller/studio";
|
||||
import { StudioMode, ViewportSize } from "@storyteller/studio";
|
||||
import { inject } from "@storyteller/framework";
|
||||
import { LitElement, PropertyValues, html, unsafeCSS } from "lit";
|
||||
import { PropertyValues } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
import { CANVAS_PROVIDER, type CanvasProvider } from "./canvas-provider.element";
|
||||
|
||||
import styles from "./viewer.element.scss?inline";
|
||||
import { StudioBaseElement } from "./studio-base.element";
|
||||
|
||||
@customElement("sts-viewer")
|
||||
export class ViewerElement extends LitElement {
|
||||
static override styles = unsafeCSS(styles);
|
||||
|
||||
// TODO: `<sts-viewer />` and `<sts-studio />` should be separate element
|
||||
// types with different inputs. `<sts-viewer />` should take a BVH
|
||||
// animation and load a simple mannequin asset playing the given
|
||||
// animation in a loop, while `<sts-studio />` should take a scene
|
||||
// asset handle and load it with full editing capabilities.
|
||||
//
|
||||
// Both elements should share most of the core functionality here,
|
||||
// which could easily be extracted to a base class for inheritance by
|
||||
// making `#startViewer` an abstract method.
|
||||
export class ViewerElement extends StudioBaseElement {
|
||||
@property() objectId?: string;
|
||||
@property() skyboxId?: string;
|
||||
@property({ type: Number }) mode?: StudioMode;
|
||||
|
||||
@inject(CANVAS_PROVIDER)
|
||||
_canvasProvider?: CanvasProvider;
|
||||
|
||||
#canvas?: HTMLCanvasElement;
|
||||
#resizeObserver?: ResizeObserver;
|
||||
|
||||
override connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
|
||||
this.#resizeObserver = new ResizeObserver(this.#onResize.bind(this));
|
||||
this.#resizeObserver.observe(this);
|
||||
|
||||
if (!this._canvasProvider)
|
||||
throw new Error(`No provider found for token: ${CANVAS_PROVIDER}`);
|
||||
|
||||
if ((this.#canvas = this._canvasProvider.takeCanvas()))
|
||||
this.appendChild(this.#canvas);
|
||||
}
|
||||
|
||||
override disconnectedCallback(): void {
|
||||
if (this.#canvas)
|
||||
this._canvasProvider?.returnCanvas(this.removeChild(this.#canvas));
|
||||
|
||||
this.#resizeObserver?.disconnect();
|
||||
|
||||
super.disconnectedCallback();
|
||||
}
|
||||
|
||||
protected override updated(changes: PropertyValues<this>): void {
|
||||
if (!studio.isInitialized()) {
|
||||
this.#startViewer();
|
||||
} else {
|
||||
if (changes.has("objectId") && !!this.objectId)
|
||||
studio.loadObject(this.objectId);
|
||||
|
||||
if (changes.has("skyboxId") && !!this.skyboxId)
|
||||
studio.setSkybox(this.skyboxId);
|
||||
}
|
||||
}
|
||||
|
||||
protected override render = () => html`
|
||||
<slot name="canvas"></slot>
|
||||
`;
|
||||
|
||||
#onResize(): void {
|
||||
const { width, height } = this.getBoundingClientRect();
|
||||
studio.resize(new ViewportSize(width, height));
|
||||
}
|
||||
|
||||
#startViewer(): void {
|
||||
if (!this.#canvas) return;
|
||||
onStart(viewportSize: ViewportSize): void {
|
||||
if (!this.objectId) return;
|
||||
if (!this.skyboxId) return;
|
||||
if (this.mode == null) return;
|
||||
|
||||
const { width, height } = this.getBoundingClientRect();
|
||||
try {
|
||||
studio.start(
|
||||
this.objectId,
|
||||
this.skyboxId,
|
||||
`#${this._canvasProvider!.canvasId}`,
|
||||
"Storyteller Studio",
|
||||
new ViewportSize(width, height),
|
||||
this.mode,
|
||||
);
|
||||
} catch (err) {
|
||||
if (!(err as Error).message?.startsWith("Using exceptions for control flow"))
|
||||
throw err;
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user