import React, { useEffect, useRef } from "react" import * as SPLAT from "gsplat" type GsplatStatus = | "idle" | "loading" | "loaded" | "failed" export function Gsplat({ url, width, height, className = "" }: { url: string width?: number height?: number className?: string }) { const canvasRef = useRef(null) const sceneRef = useRef() const cameraRef = useRef() const controlsRef = useRef() const rendererRef = useRef() const frameIdRef = useRef(0) const statusRef = useRef("idle") function animate() { const renderer = rendererRef.current const scene = sceneRef.current const camera = cameraRef.current const controls = controlsRef.current if (!scene || !renderer || !camera || !controls) { return } controls.update() renderer.render(scene, camera) frameIdRef.current = requestAnimationFrame(animate) } async function loadScene() { const canvas = canvasRef.current if (!canvas) { return } const status = statusRef.current if (!status || status === "loaded" || status === "loading" || status === "failed") { console.log(`Gsplat: a scene is already loading or loaded: skipping..`) return } statusRef.current = "loading" try { const renderer = rendererRef.current = new SPLAT.WebGLRenderer(canvas) let fileUrl = url.trim() let fileExt = fileUrl.toLowerCase().split(".").pop() || "splat" const isVideo = fileExt === "splatv" if (isVideo) { console.log("Gsplat: loading video splat..") renderer.addProgram(new SPLAT.VideoRenderProgram(renderer)) const scene = sceneRef.current = new SPLAT.Scene() const camera = cameraRef.current = new SPLAT.Camera() const controls = controlsRef.current = new SPLAT.OrbitControls(camera, renderer.canvas) await SPLAT.SplatvLoader.LoadAsync(url, scene, camera, (progress) => { console.log(`${Math.round(progress * 100)}%`) }) controls.setCameraTarget(camera.position.add(camera.forward.multiply(5))) } else { console.log("Gsplat: loading static splat..") const scene = sceneRef.current = new SPLAT.Scene() const camera = cameraRef.current = new SPLAT.Camera() const controls = controlsRef.current = new SPLAT.OrbitControls(camera, renderer.canvas) await SPLAT.Loader.LoadAsync(url, scene, (progress) => { console.log(`${Math.round(progress * 100)}%`) }) } console.log("Gsplat: finished loading! rendering..") statusRef.current = "loaded" } catch (err) { console.error(`Gsplat: failed to load the content`) statusRef.current = "failed" return } animate() }; useEffect(() => { if (!canvasRef.current) { return } loadScene() return () => { cancelAnimationFrame(frameIdRef?.current || 0) } }, []) // responsive width and height useEffect(() => { const canvas = canvasRef.current const renderer = rendererRef.current if (!canvas || !renderer) { return } // renderer.setSize(canvas.clientWidth, canvas.clientHeight) renderer.setSize( width || canvas.clientWidth, height || canvas.clientHeight ) }, [width, height]) return (
); }