diff --git a/src/components/three/ModelViewer.tsx b/src/components/three/ModelViewer.tsx index 2e4bde1..a46f032 100644 --- a/src/components/three/ModelViewer.tsx +++ b/src/components/three/ModelViewer.tsx @@ -260,19 +260,26 @@ function SceneModels() { ); } -/** Sphere marker at a 3D point — scaled per-frame to keep constant screen size */ +/** Sphere marker at a 3D point — scaled per-frame to keep constant screen size. + * Compensates for any cumulative parent world scale so it stays the same screen + * size whether mounted in world space or inside a transformed model group. */ function PointMarker({ position, color = '#e8a838' }: { position: [number, number, number]; color?: string }) { const ref = useRef(null); const { camera, size } = useThree(); + const tmpVec = useMemo(() => new THREE.Vector3(), []); + const worldPos = useMemo(() => new THREE.Vector3(), []); useFrame(() => { if (!ref.current) return; - const dist = camera.position.distanceTo(ref.current.position); + ref.current.getWorldPosition(worldPos); + const dist = camera.position.distanceTo(worldPos); const perspCam = camera as THREE.PerspectiveCamera; const fov = (perspCam.fov ?? 50) * (Math.PI / 180); const worldPerPixel = (2 * dist * Math.tan(fov / 2)) / size.height; - // Target: ~6 px radius (radius = 1 in base geometry → scale = 6*wpp) - const s = Math.max(0.0005, 6 * worldPerPixel); - ref.current.scale.setScalar(s); + // Target: ~6 px radius (radius = 1 in base geometry → scale = 6*wpp). + const targetWorld = Math.max(0.0005, 6 * worldPerPixel); + // Divide by parent world scale so the final world size matches `targetWorld`. + const parentScale = ref.current.parent ? ref.current.parent.getWorldScale(tmpVec).x || 1 : 1; + ref.current.scale.setScalar(targetWorld / parentScale); }); return (