fix: orient stop panels toward the road

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.
This commit is contained in:
L'électron rare
2026-07-09 14:04:57 +02:00
parent 031a102dac
commit 828c4e8870
+16 -4
View File
@@ -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 (
<group position={pos}>
<group position={pos} rotation={[0, rotationY, 0]}>
{/* bus-stop sign: billboard-on-posts (native board faces its local
-X; rotate 90° so the face reads along +Z toward the road) */}
<Clone object={signScene} scale={SIGN_SCALE} rotation={[0, Math.PI / 2, 0]} />
@@ -58,8 +68,10 @@ export function StopPanel({ stop, children, width = 340 }: {
<Html transform position={[0, 2.6, 0.4]} distanceFactor={10} style={{ width }}>
<div className={`panel${active ? ' panel--active' : ''}`}>{children}</div>
</Html>
{/* street lamp beside the stop */}
<Clone object={lampScene} scale={LAMP_SCALE} position={[5.2, 0, -1]} rotation={[0, lampYaw(stop), 0]} />
{/* 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 */}
<Clone object={lampScene} scale={LAMP_SCALE} position={[5.2, 0, -1]} rotation={[0, lampYaw(stop) - rotationY, 0]} />
</group>
)
}