import { useEffect, useMemo, useState, useRef } from "react"; import { useGLTF } from "@react-three/drei"; import * as THREE from "three"; import { useOctreeGraphNode } from "@/hooks/useOctreeGraphNode"; import { loadMapSceneData } from "@/utils/loadMapSceneData"; import type { OctreeReadyHandler } from "@/types/3d"; import type { MapNode } from "@/types/editor"; interface GameMapProps { onOctreeReady: OctreeReadyHandler; } export function GameMap({ onOctreeReady }: GameMapProps): React.JSX.Element { const [mapNodes, setMapNodes] = useState([]); const [isLoading, setIsLoading] = useState(true); const groupRef = useRef(null); useOctreeGraphNode(groupRef, onOctreeReady); useEffect(() => { const loadMap = async () => { try { const sceneData = await loadMapSceneData(); if (!sceneData) { console.warn("map.json not found"); setIsLoading(false); return; } setMapNodes( sceneData.mapNodes.filter((node) => sceneData.models.has(node.name)), ); } catch (error) { console.error("Error loading map:", error); } finally { setIsLoading(false); } }; loadMap(); }, []); if (isLoading) { return <>; } return ( {mapNodes.map((node, index) => ( ))} ); } function ModelInstance({ node }: { node: MapNode }): React.JSX.Element { const modelPath = `/models/${node.name}/model.gltf`; const groupRef = useRef(null); const { scene } = useGLTF(modelPath); const sceneInstance = useMemo(() => scene.clone(true), [scene]); const { position, rotation, scale } = node; useEffect(() => { if (groupRef.current) { groupRef.current.position.set(...position); groupRef.current.rotation.set(...rotation); groupRef.current.scale.set(...scale); } }, [position, rotation, scale]); return ( ); }