import { useGLTF } from "@react-three/drei"; import { useThree } from "@react-three/fiber"; import { useCallback, type ReactNode } from "react"; import type { WebGLRenderer } from "three"; import { KTX2Loader, type GLTFLoader } from "three-stdlib"; import { resolveContentUrl } from "../content/contentManager"; import { configureRunewakerTerrainLoader } from "./runewakerTerrainMaterial"; import { configureRunewakerSurfaceDetailLoader } from "./runewakerSurfaceDetailMaterial"; import { configureRunewakerWaterLoader } from "./runewakerWaterMaterial"; import { configureWowTerrainLoader } from "./wowTerrainMaterial"; export const KTX2_TRANSCODER_PATH = `${import.meta.env.BASE_URL}basis/`; const ktx2Loaders = new WeakMap(); function ktx2LoaderFor(renderer: WebGLRenderer): KTX2Loader { const cached = ktx2Loaders.get(renderer); if (cached) return cached; const loader = new KTX2Loader(); loader.setTranscoderPath(KTX2_TRANSCODER_PATH); loader.detectSupport(renderer); ktx2Loaders.set(renderer, loader); return loader; } /** * Marks the part of a Canvas that shares its GLTF decoder. KTX2Loader instances * intentionally live with their weakly referenced renderer: disposing one from * React cleanup can abort a GLB that is still decoding or cached by useGLTF. */ export function GameGltfLoaderLifecycle({ children }: { readonly children: ReactNode }) { return children; } /** * Loads shipped world and creature GLBs with Meshopt and KTX2 support. * Keeping one KTX2Loader per renderer also keeps Three from creating a worker * pool for every mounted model. */ export function useGameGLTF(url: string) { const resolvedUrl = resolveContentUrl(url); const renderer = useThree((state) => state.gl); const extendLoader = useCallback((loader: GLTFLoader) => { loader.setKTX2Loader(ktx2LoaderFor(renderer)); configureRunewakerTerrainLoader(loader); configureRunewakerSurfaceDetailLoader(loader); configureRunewakerWaterLoader(loader); configureWowTerrainLoader(loader); }, [renderer]); return useGLTF(resolvedUrl, false, true, extendLoader); } export function useGameGLTFs(urls: readonly string[]) { const resolvedUrls = urls.map(resolveContentUrl); const renderer = useThree((state) => state.gl); const extendLoader = useCallback((loader: GLTFLoader) => { loader.setKTX2Loader(ktx2LoaderFor(renderer)); configureRunewakerTerrainLoader(loader); configureRunewakerSurfaceDetailLoader(loader); configureRunewakerWaterLoader(loader); configureWowTerrainLoader(loader); }, [renderer]); return useGLTF(resolvedUrls, false, true, extendLoader); }