diff --git a/src/scene/stops/StopPanel.tsx b/src/scene/stops/StopPanel.tsx index 28d921f..b246d9f 100644 --- a/src/scene/stops/StopPanel.tsx +++ b/src/scene/stops/StopPanel.tsx @@ -30,6 +30,8 @@ function hash(id: string): number { } // face the lamp's curved arm back toward the road, plus a per-stop wobble +// (absolute world yaw — the caller compensates for the group's own facing +// rotation so the arm keeps its world-space orientation) function lampYaw(stop: Stop): number { const tan = tangentAt(stop.t) const n = new Vector3(-tan.z, 0, tan.x) @@ -43,12 +45,20 @@ export function StopPanel({ stop, children, width = 340 }: { children: ReactNode width?: number }) { - const pos = useMemo(() => stopAnchor(stop), [stop]) + // orient the group so its +Z (the sign/panel's readable face) points at + // the road point where the bus stops, instead of always facing world +Z — + // otherwise, depending on which side of the curve the stop sits on, the + // panel can show its back to the arriving bus. + const { pos, rotationY } = useMemo(() => { + const p = stopAnchor(stop) + const road = pointAt(stop.t) + return { pos: p, rotationY: Math.atan2(road.x - p.x, road.z - p.z) } + }, [stop]) const active = useRide((s) => s.stop?.id === stop.id) const { scene: signScene } = useGLTF('/models/sign-highway-wide.glb') const { scene: lampScene } = useGLTF('/models/light-curved.glb') return ( - + {/* bus-stop sign: billboard-on-posts (native board faces its local -X; rotate 90° so the face reads along +Z toward the road) */} @@ -58,8 +68,10 @@ export function StopPanel({ stop, children, width = 340 }: {
{children}
- {/* street lamp beside the stop */} - + {/* street lamp beside the stop — compensate for the group's own yaw so + the arm keeps facing the road in world space, unaffected by the + panel's new facing rotation */} +
) }