UNPKG

@playcanvas/blocks

Version:

High level abstract 3D primitives for React

85 lines 4.5 kB
"use client"; import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime"; import { Application } from "@playcanvas/react"; import { useApp, useSplat } from "@playcanvas/react/hooks"; import { GSplat } from "@playcanvas/react/components"; import { Suspense, useCallback, useEffect, useRef, useState } from "react"; import { AssetViewerProvider, useAssetViewer, useTimeline } from "./splat-viewer-context"; import { TooltipProvider } from "../components/ui/tooltip"; import { cn } from "../lib/utils"; import { SmartCamera } from "./smart-camera"; import { HelpDialog } from "./help-dialog"; import { FILLMODE_NONE } from "playcanvas"; import { RESOLUTION_AUTO } from "playcanvas"; import { useSubscribe } from "./hooks/use-subscribe"; const identity = (a) => a; function SplatComponent({ src, variant = "neutral", onAssetProgress }) { const isObject = typeof src === 'object'; const { asset, error, subscribe } = useSplat(isObject ? "vfs://force-use-sogs-parser.json" : src, isObject ? { data: src, options: { mapUrl: identity } } : {}); const { isInteracting } = useAssetViewer(); const { isPlaying } = useTimeline(); const app = useApp(); // unload the asset when the component is unmounted useEffect(() => { return () => asset?.unload(); }, [asset]); useEffect(() => { return subscribe(({ progress }) => onAssetProgress?.(progress)); }, [subscribe, onAssetProgress]); // Hide the cursor when the timeline is playing and the user is not interacting useEffect(() => { if (app.graphicsDevice.canvas) { app.graphicsDevice.canvas.style.cursor = isPlaying && !isInteracting ? 'none' : 'grab'; } }, [isInteracting, app, isPlaying]); // Update cursor on mouse down/up useEffect(() => { const canvas = app.graphicsDevice.canvas; if (!canvas) return; const onMouseDown = () => { canvas.style.cursor = 'grabbing'; }; const onMouseUp = () => { canvas.style.cursor = isPlaying && !isInteracting ? 'none' : 'grab'; }; canvas.addEventListener('mousedown', onMouseDown); canvas.addEventListener('mouseup', onMouseUp); canvas.addEventListener('mouseleave', onMouseUp); return () => { canvas.removeEventListener('mousedown', onMouseDown); canvas.removeEventListener('mouseup', onMouseUp); canvas.removeEventListener('mouseleave', onMouseUp); }; }, [app, isPlaying, isInteracting]); if (error) throw new Error(error); if (!asset) return null; return (_jsxs(_Fragment, { children: [ // type === 'animation' ? <AnimationCamera fov={30} track={track} /> : // type === 'orbit' ? <InteractiveCamera fov={30} /> : // <InteractiveCamera fov={30} type={type} /> _jsx(SmartCamera, { fov: 30, variant: variant }), _jsx(GSplat, { asset: asset })] })); } function PosterComponent({ poster }) { return (_jsx("img", { src: poster, alt: "poster" })); } /** * The SplatViewer is a component that displays a Gaussian Splat. */ export function SplatViewer({ src, variant = "neutral", poster, mode = 'orbit', defaultMode = 'orbit', onTypeChange, className, children }) { const { subscribe, notify } = useSubscribe(); const onAssetProgress = useCallback((progress) => notify(progress), [src, notify]); const isControlled = !mode; const containerRef = useRef(null); const [uncontrolledMode, setUncontrolledMode] = useState(defaultMode); const setCameraMode = useCallback((mode) => { if (!isControlled) setUncontrolledMode(mode); onTypeChange?.(mode); }, [isControlled, onTypeChange]); const currentMode = isControlled ? mode : uncontrolledMode; return (_jsx("div", { ref: containerRef, className: cn("relative overflow-hidden", className), children: _jsxs(AssetViewerProvider, { targetRef: containerRef, src: src, mode: currentMode, setMode: setCameraMode, subscribe: subscribe, children: [_jsxs(Suspense, { fallback: _jsx(PosterComponent, { poster: poster }), children: [_jsx(Application, { fillMode: FILLMODE_NONE, resolutionMode: RESOLUTION_AUTO, autoRender: false, children: _jsx(SplatComponent, { src: src, onAssetProgress: onAssetProgress, variant: variant }) }), _jsx(TooltipProvider, { children: children })] }), _jsx(HelpDialog, {})] }) })); } //# sourceMappingURL=splat-viewer.js.map