From 828c4e8870991e29eb2793c805e8694c6a7ee872 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=27=C3=A9lectron=20rare?= <108685187+electron-rare@users.noreply.github.com> Date: Thu, 9 Jul 2026 14:04:57 +0200 Subject: [PATCH] fix: orient stop panels toward the road MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Each stop's group faced world +Z regardless of the road's local tangent, so IDENTITÉS and GREETINGS showed mirrored text and the GREETINGS plane read as crossing the road. Compute yaw once per stop via atan2 from the anchor to pointAt(stop.t) so the panel's +Z always points at the road it belongs to. Lamp yaw is world-space absolute, so subtract the group's new rotation to keep its facing unchanged. --- src/scene/stops/StopPanel.tsx | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) 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 */} +
) }