From 6e700900742372a5a8ea14d9b29a9a61a98f21e1 Mon Sep 17 00:00:00 2001 From: Jage9 Date: Sat, 21 Feb 2026 19:43:28 -0500 Subject: [PATCH] Fix directional facing orientation and make rear attenuation range-based --- client/src/audio/spatial.ts | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/client/src/audio/spatial.ts b/client/src/audio/spatial.ts index 87170dc..ce60d30 100644 --- a/client/src/audio/spatial.ts +++ b/client/src/audio/spatial.ts @@ -35,11 +35,28 @@ export function resolveSpatialMix(options: SpatialMixOptions): SpatialMixResult } const distance = Math.hypot(dx, dy); - if (distance > range) { + let effectiveRange = range; + if (options.directional?.enabled) { + const coneDeg = Math.max(1, Math.min(359, options.directional.coneDeg ?? 120)); + const rearGain = Math.max(0, Math.min(1, options.directional.rearGain ?? 0.5)); + const facingDeg = normalizeDegrees(options.directional.facingDeg); + // `dx/dy` are listener-relative source coords in current callers, so invert to get source->listener bearing. + const bearingDeg = bearingFromSourceToListener(-dx, -dy); + const diff = angularDifferenceDeg(facingDeg, bearingDeg); + const halfCone = coneDeg / 2; + if (diff > halfCone) { + const span = Math.max(1, 180 - halfCone); + const t = Math.max(0, Math.min(1, (diff - halfCone) / span)); + const directionalFactor = 1 - t * (1 - rearGain); + effectiveRange = Math.max(0.01, range * directionalFactor); + } + } + + if (distance > effectiveRange) { return null; } - const volumeRatio = Math.max(0, 1 - distance / range); + const volumeRatio = Math.max(0, 1 - distance / effectiveRange); let gain = baseGain * Math.pow(volumeRatio, 2); const clampedX = Math.max(-range, Math.min(range, dx)); let pan = Math.sin((clampedX / range) * (Math.PI / 2)); @@ -51,21 +68,6 @@ export function resolveSpatialMix(options: SpatialMixOptions): SpatialMixResult } } - if (options.directional?.enabled) { - const coneDeg = Math.max(1, Math.min(359, options.directional.coneDeg ?? 120)); - const rearGain = Math.max(0, Math.min(1, options.directional.rearGain ?? 0.5)); - const facingDeg = normalizeDegrees(options.directional.facingDeg); - const bearingDeg = bearingFromSourceToListener(dx, dy); - const diff = angularDifferenceDeg(facingDeg, bearingDeg); - const halfCone = coneDeg / 2; - if (diff > halfCone) { - const span = Math.max(1, 180 - halfCone); - const t = Math.max(0, Math.min(1, (diff - halfCone) / span)); - const directionalGain = 1 - t * (1 - rearGain); - gain *= directionalGain; - } - } - return { distance, gain, pan }; }