import { useEffect, useRef, useState } from "react"; import type { LoadedModelAsset } from "../assets/gltf-model-import"; import type { LoadedImageAsset } from "../assets/image-assets"; import type { ProjectAssetRecord } from "../assets/project-assets"; import type { FirstPersonTelemetry } from "../runtime-three/navigation-controller"; import { RuntimeHost } from "../runtime-three/runtime-host"; import type { RuntimeInteractionPrompt } from "../runtime-three/runtime-interaction-system"; import type { RuntimeNavigationMode, RuntimeSceneDefinition } from "../runtime-three/runtime-scene-build"; import { createWorldBackgroundStyle } from "../shared-ui/world-background-style"; interface RunnerCanvasProps { runtimeScene: RuntimeSceneDefinition; projectAssets: Record; loadedModelAssets: Record; loadedImageAssets: Record; navigationMode: RuntimeNavigationMode; onRuntimeMessageChange(message: string | null): void; onFirstPersonTelemetryChange(telemetry: FirstPersonTelemetry | null): void; onInteractionPromptChange(prompt: RuntimeInteractionPrompt | null): void; } export function RunnerCanvas({ runtimeScene, projectAssets, loadedModelAssets, loadedImageAssets, navigationMode, onRuntimeMessageChange, onFirstPersonTelemetryChange, onInteractionPromptChange }: RunnerCanvasProps) { const containerRef = useRef(null); const hostRef = useRef(null); const [runnerMessage, setRunnerMessage] = useState(null); const [interactionPrompt, setInteractionPrompt] = useState(null); useEffect(() => { const container = containerRef.current; if (container === null) { return; } const testCanvas = document.createElement("canvas"); const hasWebGl = testCanvas.getContext("webgl2") !== null || testCanvas.getContext("webgl") !== null || testCanvas.getContext("experimental-webgl") !== null; try { const runtimeHost = new RuntimeHost({ enableRendering: hasWebGl }); hostRef.current = runtimeHost; runtimeHost.mount(container); runtimeHost.setRuntimeMessageHandler(onRuntimeMessageChange); runtimeHost.setFirstPersonTelemetryHandler(onFirstPersonTelemetryChange); runtimeHost.setInteractionPromptHandler((prompt) => { setInteractionPrompt(prompt); onInteractionPromptChange(prompt); }); setRunnerMessage( hasWebGl ? null : "WebGL is unavailable in this browser environment. The runner shell is visible, but runtime rendering is disabled." ); return () => { onInteractionPromptChange(null); runtimeHost.dispose(); hostRef.current = null; }; } catch (error) { const message = error instanceof Error ? error.message : "Runner initialization failed."; setRunnerMessage(`Runner initialization failed: ${message}`); onInteractionPromptChange(null); return; } }, [onFirstPersonTelemetryChange, onInteractionPromptChange, onRuntimeMessageChange]); useEffect(() => { hostRef.current?.updateAssets(projectAssets, loadedModelAssets, loadedImageAssets); }, [projectAssets, loadedModelAssets, loadedImageAssets]); useEffect(() => { hostRef.current?.loadScene(runtimeScene); }, [runtimeScene]); useEffect(() => { hostRef.current?.setNavigationMode(navigationMode); }, [navigationMode]); return (
{navigationMode === "firstPerson" ? ); }