Changes
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
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]);
|
||||
}
|
||||
Reference in New Issue
Block a user