Load remote BVH from URL param

This commit is contained in:
Danny McGee
2024-01-18 23:05:16 -05:00
parent 5cee4482ef
commit 37015b1e98
11 changed files with 492 additions and 109 deletions
+1
View File
@@ -1,2 +1,3 @@
export * from "./lib/bvh-viewer.element";
export * from "./lib/canvas-provider.element";
export * from "./lib/viewer.element";
+34
View File
@@ -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);
}
}
+72
View File
@@ -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>
`;
}
+20 -82
View File
@@ -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);
}
}