Files
SteelXR/src/components/three/XRPlaneOverlay.tsx
T
gpt-engineer-app[bot] 414317889c Changes
2026-02-28 01:27:17 +00:00

96 lines
2.9 KiB
TypeScript

import { useMemo } from 'react';
import { useXRPlanes, XRPlaneModel } from '@react-three/xr';
import { XRSpace } from '@react-three/xr';
import * as THREE from 'three';
interface XRPlaneOverlayProps {
showPlanes: boolean;
highlightNearest?: THREE.Vector3 | null;
}
/**
* Renders semi-transparent overlays on all detected WebXR planes.
* Walls are tinted blue, floors/ceilings green, others grey.
*/
export function XRPlaneOverlay({ showPlanes, highlightNearest }: XRPlaneOverlayProps) {
const allPlanes = useXRPlanes();
if (!showPlanes || allPlanes.length === 0) return null;
return (
<>
{allPlanes.map((plane, i) => {
const label = plane.semanticLabel ?? '';
const isHorizontal = plane.orientation === 'horizontal';
const isVertical = plane.orientation === 'vertical';
// Color by type
let color = '#64748b'; // default grey
if (label === 'floor' || label === 'table' || (isHorizontal && label !== 'ceiling')) {
color = '#22c55e'; // green for floor/table/horizontal
} else if (label === 'wall' || isVertical) {
color = '#3b82f6'; // blue for walls
} else if (label === 'ceiling') {
color = '#a855f7'; // purple for ceiling
}
return (
<XRSpace key={i} space={plane.planeSpace}>
<XRPlaneModel plane={plane}>
<meshBasicMaterial
color={color}
transparent
opacity={0.15}
side={THREE.DoubleSide}
depthWrite={false}
/>
</XRPlaneModel>
</XRSpace>
);
})}
</>
);
}
/**
* Hook to find the nearest detected plane to a given point.
* Returns the plane's center position and orientation quaternion.
*/
export function useNearestPlane(
targetPoint: THREE.Vector3 | null,
maxDistance = 0.5
): { position: THREE.Vector3; quaternion: THREE.Quaternion; plane: XRPlane } | null {
const allPlanes = useXRPlanes();
return useMemo(() => {
if (!targetPoint || allPlanes.length === 0) return null;
let nearest: { position: THREE.Vector3; quaternion: THREE.Quaternion; plane: XRPlane } | null = null;
let minDist = maxDistance;
for (const plane of allPlanes) {
// Use the plane's polygon center as approximation
const polygon = plane.polygon;
if (!polygon || polygon.length === 0) continue;
const center = new THREE.Vector3();
for (const pt of polygon) {
center.add(new THREE.Vector3(pt.x, pt.y, pt.z));
}
center.divideScalar(polygon.length);
const dist = center.distanceTo(targetPoint);
if (dist < minDist) {
minDist = dist;
nearest = {
position: center.clone(),
quaternion: new THREE.Quaternion(),
plane,
};
}
}
return nearest;
}, [targetPoint, allPlanes, maxDistance]);
}