public static float Evaluate(ref VolumeFalloffEngineData falloff, ref OrientedBBox obb, float3 positionWS, float distanceWS)
        {
            float3x3 obbFrame   = new float3x3(obb.right, obb.up, math.cross(obb.up, obb.right));
            float3   obbExtents = new float3(obb.extentX, obb.extentY, obb.extentZ);

            float3 samplePositionBS  = math.mul(obbFrame, positionWS - obb.center);
            float3 samplePositionBCS = samplePositionBS / obbExtents;

            bool isInsideVolume = math.max(math.abs(samplePositionBCS.x), math.max(math.abs(samplePositionBCS.y), math.abs(samplePositionBCS.z))) < 1.0f;

            if (!isInsideVolume)
            {
                return(0.0f);
            }

            float3 samplePositionBNDC = samplePositionBCS * 0.5f + 0.5f;

            float volumeWeight = VolumeFalloff.ComputeVolumeFalloffWeight(
                samplePositionBNDC,
                distanceWS,
                falloff.rcpFaceFadePos,
                falloff.rcpFaceFadeNeg,
                falloff.rcpDistFadeLen,
                falloff.endTimesRcpDistFadeLen
                );

            volumeWeight *= falloff.weight;
            return(volumeWeight);
        }
        private void ComputeEngineData(ref List <AmbientAudioVolume> instances)
        {
            for (int i = 0, iLen = instances.Count; i < iLen; ++i)
            {
                AmbientAudioVolume ambientAudioVolume = instances[i];
                VolumeFalloff      volumeFalloff      = ambientAudioVolume.volumeFalloff;

                VolumeFalloff.ComputeOrientedBBoxFromFalloffAndTransform(out VolumeFalloff.OrientedBBox obb, ref volumeFalloff, ambientAudioVolume.transform);
                bounds[i]      = obb;
                falloffData[i] = volumeFalloff.ConvertToEngineData();
                audioData[i]   = ambientAudioVolume.ConvertToEngineData();
            }
        }
        private void ComputeAudioVolumeAtPositionAndDirectionFromEngineData(float3 targetPositionWS, float3 targetTangentDirectionWS, float3 targetForwardDirectionWS)
        {
            for (int i = 0; i < audioVolumeCount; ++i)
            {
                audioVolume[i] = float2.zero;
            }

            for (int i = 0; i < count; ++i)
            {
                VolumeFalloff.OrientedBBox obb = bounds[i];
                AmbientAudioVolume.AmbientAudioVolumeEngineData audioEngineData   = audioData[i];
                VolumeFalloff.VolumeFalloffEngineData           falloffEngineData = falloffData[i];

                float distanceWS = math.length(obb.center - targetPositionWS);
                float weight     = VolumeFalloff.Evaluate(ref falloffEngineData, ref obb, targetPositionWS, distanceWS);
                if (weight < 1e-5f)
                {
                    continue;
                }

                // TODO: Figure out how to modulate left and right ear independantly.
                float3 audioPositionOffsetWS = audioEngineData.audioPositionWS - targetPositionWS;
                float  spatialAttenuation    = 1.0f / math.max(1.0f, math.dot(audioPositionOffsetWS, audioPositionOffsetWS));

                float audioVolumeIsotropic = audioEngineData.audioVolume * weight * spatialAttenuation;

                float3 targetToAudioDirectionWS = math.normalize(audioPositionOffsetWS);

                // Debug.Log("spatialAttenuation = " + spatialAttenuation);

                // Perform simple wrap lighting model for ear direction attenuation.
                // TODO: Research realistic attenuation models.
                float2 earWeights = new float2(
                    math.saturate(math.dot(targetTangentDirectionWS, targetToAudioDirectionWS) * -0.5f + 0.5f),
                    math.saturate(math.dot(targetTangentDirectionWS, targetToAudioDirectionWS) * 0.5f + 0.5f)
                    );

                // Add behind head attenuation to help differentiate between things in front and things behind the camera.
                earWeights *= (1.0f - math.max(0.0f, math.dot(-targetForwardDirectionWS, targetToAudioDirectionWS))) * 0.5f + 0.5f;

                audioVolume[audioEngineData.audioIndex] = earWeights * audioVolumeIsotropic + audioVolume[audioEngineData.audioIndex];
            }
        }
        private static float ComputeVolumeFalloffWeight(
            float3 samplePositionBoxNDC,
            float distanceWS,
            float3 rcpPosFaceFade,
            float3 rcpNegFaceFade,
            float rcpDistFadeLen,
            float endTimesRcpDistFadeLen
            )
        {
            // We have to account for handedness.
            samplePositionBoxNDC.z = 1 - samplePositionBoxNDC.z;

            float3 posF = VolumeFalloff.Remap10(samplePositionBoxNDC, rcpPosFaceFade, rcpPosFaceFade);
            float3 negF = VolumeFalloff.Remap01(samplePositionBoxNDC, rcpNegFaceFade, 0);
            float  fade = posF.x * posF.y * posF.z * negF.x * negF.y * negF.z;
            float  dstF = 1.0f;//VolumeFalloff.Remap10(distanceWS, rcpDistFadeLen, endTimesRcpDistFadeLen);

            return(fade * dstF);
        }
        public static void ComputeOrientedBBoxFromFalloffAndTransform(out OrientedBBox obb, ref VolumeFalloff falloff, Transform t)
        {
            Matrix4x4 m = falloff.isTransformScaleUsed
                ? (t.localToWorldMatrix * Matrix4x4.TRS(falloff.center, Quaternion.identity, falloff.size))
                : Matrix4x4.TRS(t.position + (t.rotation * falloff.center), t.rotation, falloff.size);

            obb = new OrientedBBox(m);
        }