Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | 1x 19x 19x 19x 18x 17x 17x 17x 17x 17x 17x 17x 17x 17x 19x | /**
* useDistanceCulling - Distance-based culling hook for HTML overlays
*
* Optimizes performance by culling distant HTML overlays that are
* beyond the camera's effective view distance. Helps maintain 60fps
* by reducing unnecessary DOM rendering.
*
* @module hooks/useDistanceCulling
* @category Performance
* @korean 거리컬링훅
*/
import { useThree } from "@react-three/fiber";
import { useMemo } from "react";
/**
* Distance culling hook options
*/
export interface DistanceCullingOptions {
/**
* Maximum distance in meters before culling
* @default 20
* @korean 최대거리
*/
readonly cullDistance?: number;
/**
* Whether culling is enabled
* @default true
* @korean 컬링활성화
*/
readonly enabled?: boolean;
}
/**
* useDistanceCulling hook
*
* Calculates whether an overlay should be rendered based on
* distance from camera. Returns false if object is beyond cull distance.
*
* @param position - World position of the overlay [x, y, z]
* @param options - Culling configuration options
* @returns boolean - true if should render, false if should cull
*
* @example
* ```tsx
* const isVisible = useDistanceCulling([5, 0, 0], { cullDistance: 20 });
* if (!isVisible) return null;
* ```
*
* @performance
* - Uses useMemo to prevent recalculation on every frame
* - Distance calculation only runs when camera or position changes
* - Manually calculates squared distance to avoid expensive sqrt operation
* - Reduces DOM rendering for distant overlays
*
* @note Camera position dependency
* This hook tracks individual camera position components (x, y, z) in the dependency array.
* While this causes recalculation on camera movement, it's necessary for accurate culling.
* The useMemo still prevents redundant calculations within the same frame. For games with
* very frequent camera updates, consider implementing a threshold-based approach (only update
* when camera moves more than a certain distance) or debouncing at the component level.
*
* @korean 거리컬링훅사용
*/
export const useDistanceCulling = (
position: readonly [number, number, number] | [number, number, number],
options: DistanceCullingOptions = {},
): boolean => {
const { cullDistance = 20, enabled = true } = options;
const camera = useThree((state) => state.camera);
const isVisible = useMemo(() => {
// If culling disabled, always render
if (!enabled) return true;
// Calculate distance from camera to overlay position
// Extract camera position components to avoid dependency issues
const camX = camera.position.x;
const camY = camera.position.y;
const camZ = camera.position.z;
// Calculate squared distance manually to avoid expensive sqrt operation
const dx = position[0] - camX;
const dy = position[1] - camY;
const dz = position[2] - camZ;
const distanceSquared = dx * dx + dy * dy + dz * dz;
const cullDistanceSquared = cullDistance * cullDistance;
// Return true if within cull distance, false otherwise
return distanceSquared <= cullDistanceSquared;
}, [camera.position.x, camera.position.y, camera.position.z, position, cullDistance, enabled]);
return isVisible;
};
// Note: useDistanceCullingWithThreshold was removed because it didn't actually implement
// hysteresis and was identical to useDistanceCulling. True hysteresis requires useState
// to track previous visibility state and apply different show/hide thresholds.
// TODO: Implement proper hysteresis in the future if needed.
export default useDistanceCulling;
|