Ejemplo n.º 1
0
    private void _ComputeSoundSpatialization(Vector3 listenerPosition, float distanceToListener, PathSound pathSound)
    {
        if(pathSound.firstPortal != null)
        {
            Vector3 firstPortalPosition = pathSound.firstPortal.Center;

            Vector3 finalPosition;
            float finalDistance;
            float distanceToPortalSqr = pathSound.firstPortal.BoundingBox.SqrDistance(listenerPosition);
            if(distanceToPortalSqr >= InterpDistance * InterpDistance)
            {
                finalPosition = firstPortalPosition;
                finalDistance = pathSound.firstDistance;
            }
            else
            {
                float distanceToPortal = Mathf.Sqrt(distanceToPortalSqr);
                float alpha = Mathf.Clamp01(distanceToPortal / InterpDistance);
                Vector3 secondPortalPosition = pathSound.secondPortal ? pathSound.secondPortal.Center : transform.position;
                finalPosition = Vector3.Lerp(secondPortalPosition, firstPortalPosition, alpha);
                float secondPortalDistance = pathSound.secondPortal ? pathSound.secondDistance : distanceToListener;
                finalDistance = Mathf.Lerp(secondPortalDistance, pathSound.firstDistance, alpha);
            }

            pathSound.position = finalPosition;
            pathSound.distance = finalDistance;
        }
        else
        {
            // We're either in the same room, or something has gone horribly wrong. Either way, this is the best fallback.
            pathSound.position = transform.position;
            pathSound.distance = distanceToListener;
        }
    }
Ejemplo n.º 2
0
    void Update()
    {
        if(playing && Cue != null && cachedMember.Sectors.Count > 0 && SECTR_AudioSystem.Initialized)
        {
            Transform listener = SECTR_AudioSystem.Listener;
            Vector3 listenerPosition = listener.position;
            Vector3 sourcePosition = transform.position;
            directDistanceToListener = Vector3.Distance(sourcePosition, listenerPosition);
            bool occludable = Cue.SourceCue.Spatialization == SECTR_AudioCue.Spatializations.Occludable3D;
            int numActiveSounds = activeSounds.Count;

            if(played && !Loop && !Cue.SourceCue.Loops && numActiveSounds == 0)
            {
                Stop(false);
                return;
            }

            // Only perform full propagation logic on sounds within the range of the sound.
            if(directDistanceToListener <= Cue.SourceCue.MaxDistance)
            {
                // First, find the shortest path from the source position to the listener position
                PathSound currentSound = null;
                SECTR_Graph.FindShortestPath(ref path, listenerPosition, transform.position, 0);
                int pathSize = path.Count;
                if(pathSize > 0)
                {
                    // Compute the current frame's propogated sound position based on the first and second
                    // portals in the path.
                    SECTR_Portal firstPortal = path[0].Portal;
                    SECTR_Portal secondPortal = pathSize > 1 ? path[1].Portal : null;
                    bool newSound = false;

                    // Now we need to update the sound based on the current position, but we need to
                    // determine if we should update an existing instance or create a new one.
                    // We can re-use an instance if it is on the same path as the current path.
                    for(int soundIndex = 0; soundIndex < numActiveSounds; ++soundIndex)
                    {
                        // "Same path" just means that the current and next portals are identical.
                        PathSound activeSound = activeSounds[soundIndex];
                        if(firstPortal == activeSound.firstPortal || firstPortal == activeSound.secondPortal || secondPortal == activeSound.firstPortal)
                        {
                            currentSound = activeSound;
                            break;
                        }
                    }

                    if(currentSound == null)
                    {
                        currentSound = new PathSound();
                        newSound = true;
                    }

                    currentSound.firstPortal = firstPortal;
                    currentSound.secondPortal = secondPortal;
                    currentSound.occluded = false;
                    currentSound.firstDistance = 0;
                    currentSound.secondDistance = 0;
                    currentSound.distance = 0;

                    // Compute path distance and occlusion together to avoid excessive walks
                    SECTR_AudioSystem.OcclusionModes occlusionFlags = occludable ? SECTR_AudioSystem.System.OcclusionFlags : 0;
                    bool graphOccludable = (occlusionFlags & SECTR_AudioSystem.OcclusionModes.Graph) != 0;

                    if(pathSize == 1 && path[0].Portal == null)
                    {
                        currentSound.firstDistance = directDistanceToListener;
                        currentSound.secondDistance = directDistanceToListener;
                    }
                    else
                    {
                        for(int pathIndex = 0; pathIndex < pathSize; ++pathIndex)
                        {
                            SECTR_Portal portal = path[pathIndex].Portal;
                            SECTR_Portal nextPortal = pathIndex < pathSize - 1 ? path[pathIndex + 1].Portal : null;
                            Vector3 portalPosition = portal.Center;

                            if(pathIndex == 0)
                            {
                                currentSound.firstDistance += Vector3.Distance(portalPosition, listenerPosition);
                            }
                            else if(pathIndex == 1 && portal)
                            {
                                currentSound.secondDistance += Vector3.Distance(portalPosition, listenerPosition);
                            }

                            float portalDistance = 0f;
                            if(portal && nextPortal)
                            {
                                portalDistance = Vector3.Distance(portalPosition, nextPortal.Center);
                            }
                            else
                            {
                                portalDistance = Vector3.Distance(portalPosition, sourcePosition);
                            }

                            currentSound.firstDistance += portalDistance;
                            if(pathIndex >= 1)
                            {
                                currentSound.secondDistance += portalDistance;
                            }

                            if(portal &&
                               graphOccludable && !currentSound.occluded &&
                               (portal.Flags & SECTR_Portal.PortalFlags.Closed) != 0)
                            {
                                currentSound.occluded = true;
                            }
                        }
                    }

                    // Use the default occlusion routine for all other types of occlusion.
                    occlusionFlags &= ~SECTR_AudioSystem.OcclusionModes.Graph;
                    if(!currentSound.occluded && occlusionFlags != 0)
                    {
                        currentSound.occluded = SECTR_AudioSystem.IsOccluded(sourcePosition, occlusionFlags);
                    }

                    _ComputeSoundSpatialization(listenerPosition, directDistanceToListener, currentSound);

                    // If we couldn't find a match with an existing instance, create a new one.
                    if(!currentSound.instance)
                    {
                        if(activeSounds.Count > 0)
                        {
                            currentSound.instance = SECTR_AudioSystem.Clone(activeSounds[0].instance, currentSound.position);
                        }
                        else
                        {
                            currentSound.instance = SECTR_AudioSystem.Play(Cue, currentSound.position, Loop);
                        }
                        if(currentSound.instance)
                        {
                            currentSound.instance.ForceInfinite();
                            if(newSound)
                            {
                                activeSounds.Add(currentSound);
                                ++numActiveSounds;
                            }
                        }
                        else
                        {
                            if(!newSound)
                            {
                                activeSounds.Remove(currentSound);
                            }
                            currentSound = null;
                        }
                    }
                    else
                    {
                        currentSound.instance.Position = currentSound.position;
                    }

                    if(currentSound != null)
                    {
                        // mark as played for use in one shot limiting
                        currentSound.lastListenerPosition = listenerPosition;
                        played = true;
                    }
                }

                // Update volume and other properties of all active sound instances.
                {
                    // To ensure a clean cross fade as players move around, we need to
                    // compute a weight for each sound.
                    int soundIndex = 0;
                    float residualWeight = 1;
                    const float invFalloffDistance = (1f/2f);
                    for(soundIndex = 0; soundIndex < numActiveSounds; ++soundIndex)
                    {
                        PathSound activeSound = activeSounds[soundIndex];
                        if(activeSound != currentSound)
                        {
                            _ComputeSoundSpatialization(listenerPosition, directDistanceToListener, activeSound);
                            activeSound.weight = activeSound.instance ? (1f - Mathf.Clamp01(Vector3.Distance(activeSound.lastListenerPosition, listenerPosition) * invFalloffDistance)) : 0f;
                            residualWeight -= activeSound.weight;
                        }
                    }

                    // If there is a current sound and there is any weight left over,
                    // give the current sound (i.e. the newest sound) the residual weight.
                    // This gives priority to pre-existing sounds so that they don't pop out.
                    if(currentSound != null)
                    {
                        currentSound.weight = Mathf.Max(0, residualWeight);
                    }

                    // Now update all sounds in the group.
                    soundIndex = 0;
                    float minDistance = Cue.SourceCue.MinDistance;
                    float maxDistance = Cue.SourceCue.MaxDistance;
                    float invDistanceDelta = 1f / (maxDistance - minDistance);
                    while(soundIndex < numActiveSounds)
                    {
                        // Sounds with active instances get their attenuation, pitch, and occlusion set.
                        PathSound activeSound = activeSounds[soundIndex];
                        if(activeSound.instance)
                        {
                            activeSound.instance.Position = activeSound.position;

                            // Attenuation code is duplicated from SECTR_AudioSystem, but I don't want to pay extra
                            // function cal overhead so...
                            float attenuation = 1;
                            switch(Cue.SourceCue.Falloff)
                            {
                            case SECTR_AudioCue.FalloffTypes.Linear:
                                attenuation = 1 - Mathf.Clamp01((activeSound.distance - minDistance) * invDistanceDelta);
                                break;
                            case SECTR_AudioCue.FalloffTypes.Logrithmic:
                                attenuation = Mathf.Clamp01(1 / Mathf.Max(activeSound.distance - minDistance - 1, 0.001f));
                                break;
                            }
                            // Note that this actually sets the UserVolume, so HDR sounds are still mixed
                            // internally to the AudioSystem.
                            activeSound.instance.Volume = attenuation * activeSound.weight * volume;
                            activeSound.instance.Pitch = pitch;

                            // Set occlusion values that were computed above.
                            if(occludable)
                            {
                                activeSound.instance.ForceOcclusion(activeSound.occluded);
                            }
                        }

                        // Sounds with no weight left get removed from the list.
                        // But not the current sound, we might need that.
                        if(activeSound.weight <= 0f || !activeSound.instance)
                        {
                            // Hard stop because we should be inaudible at this point.
                            activeSound.instance.Stop(true);
                            activeSounds.RemoveAt(soundIndex);
                            --numActiveSounds;
                        }
                        else
                        {
                            ++soundIndex;
                        }
                    }
                }
            }
            else // if we're out of range, shut everything down.
            {
                for(int activeIndex = 0; activeIndex < numActiveSounds; ++activeIndex)
                {
                    PathSound activeSound = activeSounds[activeIndex];
                    if(activeSound != null)
                    {
                        activeSound.instance.Stop(false);
                    }
                }
                activeSounds.Clear();
            }
        }
    }