feat(web): render bgm setups as a 3D table
Wire the tabletop library into the setup detail route: seed the store from the setup, resolve the surface mount tree, and render world and HUD surfaces. Add serializedToPackage to convert the vite-emitted package.
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
import { Suspense, useMemo } from 'react';
|
||||
import type { SerializedPackage, Setup } from '@tts/bgm';
|
||||
import {
|
||||
SetupLoader,
|
||||
WorldSurfaceView,
|
||||
HudSurfaceView,
|
||||
resolveMountTree,
|
||||
serializedToPackage,
|
||||
useTabletopStore,
|
||||
} from '@tts/tabletop';
|
||||
import Scene from '../viewers/Scene';
|
||||
|
||||
/**
|
||||
* Render a bgm setup as an interactive 3D table.
|
||||
*
|
||||
* Seeds the tabletop store from the setup, resolves the surface mount tree,
|
||||
* and renders each enabled surface — world surfaces in world space, HUD
|
||||
* surfaces as an overlay — with their parts placed on routes. The store is
|
||||
* seeded on every render so a setup change re-seeds it.
|
||||
*/
|
||||
export default function TabletopScene({
|
||||
pkg,
|
||||
setup,
|
||||
}: {
|
||||
pkg: SerializedPackage;
|
||||
setup: Setup;
|
||||
}) {
|
||||
const packageData = useMemo(() => serializedToPackage(pkg), [pkg]);
|
||||
const surfaces = useTabletopStore((s) => s.surfaces);
|
||||
|
||||
const tree = useMemo(
|
||||
() => resolveMountTree(packageData.surfaces, new Set(Object.keys(surfaces))),
|
||||
[packageData, surfaces],
|
||||
);
|
||||
|
||||
return (
|
||||
<Scene>
|
||||
<SetupLoader pkg={packageData} setup={setup} />
|
||||
<Suspense fallback={null}>
|
||||
{tree.world.map((node) => (
|
||||
<WorldSurfaceView key={node.id} pkg={packageData} node={node} />
|
||||
))}
|
||||
{tree.hud.map((node) => (
|
||||
<HudSurfaceView key={node.id} pkg={packageData} node={node} />
|
||||
))}
|
||||
</Suspense>
|
||||
</Scene>
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useParams } from 'react-router-dom';
|
||||
import Breadcrumbs from '../components/Breadcrumbs';
|
||||
import PackageMissing from '../components/PackageMissing';
|
||||
import TabletopScene from '../components/tabletop/TabletopScene';
|
||||
import { findPackage } from './bgm';
|
||||
|
||||
/** Detail view for a single setup within a package. */
|
||||
@@ -30,7 +31,12 @@ export default function SetupPage() {
|
||||
<h1 className="text-2xl font-semibold">
|
||||
{found.type}#{found.id}
|
||||
</h1>
|
||||
<p className="mt-1 text-sm text-zinc-400">
|
||||
{Object.keys(found.setup).length} path
|
||||
{Object.keys(found.setup).length === 1 ? '' : 's'}
|
||||
</p>
|
||||
</div>
|
||||
<TabletopScene pkg={pkg} setup={found} />
|
||||
<pre className="overflow-x-auto rounded-lg border border-zinc-800 bg-zinc-900 p-3 text-xs text-zinc-400">
|
||||
{JSON.stringify(found.setup, null, 2)}
|
||||
</pre>
|
||||
|
||||
Reference in New Issue
Block a user