1// SPDX-License-Identifier: GPL-2.0-only
2// Copyright (c) 2025-present FERS Contributors (see AUTHORS.md).
4import { useMemo } from 'react';
5import { useFrame, useThree } from '@react-three/fiber';
6import * as THREE from 'three';
8interface DynamicScaleOptions {
10 * The base multiplier for the scale.
15 * The distance at which the object appears at its "natural" size (scale = baseScale).
18 referenceDistance?: number;
22 * A hook that dynamically scales a Three.js object based on its distance from the camera.
23 * This ensures the object maintains a relatively constant screen size (billboard-like scaling)
24 * while preserving its 3D orientation.
26 * @param objectRef A React ref pointing to the Three.js Object3D (Group/Mesh).
27 * @param options Configuration options for scaling behavior.
29export function useDynamicScale(
30 objectRef: React.RefObject<THREE.Object3D | null>,
31 options: DynamicScaleOptions = {}
33 const { baseScale = 1.0, referenceDistance = 50 } = options;
34 const { camera } = useThree();
36 // Reusable vector to prevent garbage collection churn in the render loop
37 const worldPos = useMemo(() => new THREE.Vector3(), []);
40 if (!objectRef.current) return;
42 // Get absolute world position to calculate accurate distance to camera
43 objectRef.current.getWorldPosition(worldPos);
44 const distance = camera.position.distanceTo(worldPos);
46 // Calculate scale factor: (Distance / RefDist) * Base
47 // Clamp minimum to prevent mathematical singularities or invisibility
48 const scale = Math.max(
50 (distance / referenceDistance) * baseScale
53 objectRef.current.scale.set(scale, scale, scale);