Files
tts-workshop/packages/tabletop/src/surfaces/WorldSurfaceView.tsx
T
hypercross b312bf4f1f feat(tabletop): author rotation and tilt angles in degrees
Convert bgm rotation and tilt values to radians only at the three.js
boundary, so the format's angles are authored in degrees.
2026-08-10 00:11:13 +08:00

61 lines
1.8 KiB
TypeScript

/**
* `WorldSurfaceView` — mount a surface in world space.
*
* Renders a world mount node (a `kind: table` surface and its child surfaces)
* at its anchor. Parts on the surface are placed via `PartPlacement` from the
* derived render state. A disabled surface isn't part of the mount tree, so
* it's never rendered.
*/
import type { Package } from '@tts/bgm';
import { useRenderState, placementKey } from '../state.js';
import { PartPlacement } from '../placement.js';
import type { MountNode } from '../mount.js';
import { MM_TO_WORLD, DEG_TO_RAD } from '../part.js';
import { SurfaceBounds } from './SurfaceBounds.js';
import { SurfacePlane } from './SurfacePlane.js';
export function WorldSurfaceView({
pkg,
node,
showSurface = false,
}: {
pkg: Package;
node: MountNode;
showSurface?: boolean;
}) {
return <SurfaceNode pkg={pkg} node={node} showSurface={showSurface} />;
}
/** Render a mount node at its anchor, placing parts and recursing into children. */
export function SurfaceNode({
pkg,
node,
showSurface = false,
}: {
pkg: Package;
node: MountNode;
showSurface?: boolean;
}) {
const placements = useRenderState(pkg);
const own = placements.filter((p) => p.surface === node.id);
return (
<group
position={[node.x * MM_TO_WORLD, 0, node.y * MM_TO_WORLD]}
rotation={[0, node.rotation * DEG_TO_RAD, 0]}
>
{showSurface && (
<>
<SurfacePlane surface={node.surface} />
<SurfaceBounds surface={node.surface} />
</>
)}
{own.map((p) => (
<PartPlacement key={placementKey(p)} pkg={pkg} placement={p} />
))}
{node.children.map((child) => (
<SurfaceNode key={child.id} pkg={pkg} node={child} showSurface={showSurface} />
))}
</group>
);
}