From b3a3f3557c461ee0f7214f18f30ad16771505439 Mon Sep 17 00:00:00 2001 From: Tom Boullay Date: Thu, 14 May 2026 00:15:41 +0200 Subject: [PATCH] fix: add disposal on unmount in TerrainModel --- src/components/three/world/TerrainModel.tsx | 73 +++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 src/components/three/world/TerrainModel.tsx diff --git a/src/components/three/world/TerrainModel.tsx b/src/components/three/world/TerrainModel.tsx new file mode 100644 index 0000000..62d10c3 --- /dev/null +++ b/src/components/three/world/TerrainModel.tsx @@ -0,0 +1,73 @@ +import { useEffect, useMemo, useRef } from "react"; +import * as THREE from "three"; +import { useGLTF } from "@react-three/drei"; +import type { Vector3Tuple } from "@/types/three/three"; +import { disposeObject3D } from "@/utils/three/dispose"; + +const TERRAIN_MODEL_PATH = "/models/terrain/model.gltf"; +const TERRAIN_DEFAULT_POSITION: Vector3Tuple = [0, 0, 0]; + +interface TerrainModelProps { + position?: Vector3Tuple; + rotation?: Vector3Tuple; + scale?: Vector3Tuple; + receiveShadow?: boolean; + visible?: boolean; + groupRef?: React.RefObject; + onLoaded?: () => void; +} + +function applyTerrainMaterialSettings( + scene: THREE.Object3D, + receiveShadow: boolean, +): void { + scene.traverse((child) => { + if (child instanceof THREE.Mesh) { + child.receiveShadow = receiveShadow; + } + }); +} + +export function TerrainModel({ + position = TERRAIN_DEFAULT_POSITION, + rotation = [0, 0, 0], + scale = [1, 1, 1], + receiveShadow = true, + visible = true, + groupRef, + onLoaded, +}: TerrainModelProps): React.JSX.Element { + const internalRef = useRef(null); + const ref = groupRef ?? internalRef; + const { scene } = useGLTF(TERRAIN_MODEL_PATH); + + const terrainModel = useMemo(() => { + const model = scene.clone(true); + applyTerrainMaterialSettings(model, receiveShadow); + return model; + }, [scene, receiveShadow]); + + useEffect(() => { + return () => { + disposeObject3D(terrainModel); + }; + }, [terrainModel]); + + useEffect(() => { + onLoaded?.(); + }, [onLoaded]); + + return ( + + + + ); +} + +useGLTF.preload(TERRAIN_MODEL_PATH);