Skip to content

Send emitter positions to destiny for line of sight occlusion - #22

Open
phevosccp wants to merge 14 commits into
mainfrom
sightline-query
Open

phevosccp wants to merge 14 commits into
mainfrom
sightline-query

Conversation

@phevosccp

@phevosccp phevosccp commented Sep 24, 2026 •

Copy link
Copy Markdown
Member

Summary

This change optimizes performance since the physics engine and CarbonAudio are now directly talking to each other with an interface. This refactor also helps resolve bugs with NPCs and one-shot sounds occasionally not being occluded since after this, each emitter carries its own occlusion state.

  • Adds IEveObstructionQuery, implemented by destiny's ballpark and set from script as
    audioManager.obstructionQuery. CarbonAudio sends the listener and emitter positions and gets
    back a blocked flag per emitter. SetEmitterLineOfSightBlockage still works when no query is set.
  • The pass runs in the audio tick: an emitter that just started a voice is checked every tick so its
    occlusion reaches Wwise before the first buffer, everything else audible is rechecked every 0.2s.
    Unpositioned, 2D and out of range emitters are skipped.
  • Emitters are only tracked while occluded or fading, and values snap rather than fade when nothing
    is playing on them.
  • GetLastSightlineResults exposes the last pass to debug tools.
  • Fixes GetMaxAttenuationRadius not squaring the scaling factor, so range tests were wrong for any
    scaled emitter.
  • GetListener now goes through the prioritizer instead of scanning every game object, which was too
    slow to call from the tick.

Linked issue

https://fenriscreations.atlassian.net/browse/PLAT-12113

Testing

Tested in Frontier/Windows

Rewrote some of the occlusion unit tests and reduced them from 11 to 5.

…renamed on any change

- the includer provides blue, same as IEveBallpark.h, since destiny sets the vector override before any blue header
- rename the interface on any method change instead of append-only so a stale copy fails the cast
…ck occluded ones

- skip out of range and 2D emitters when collecting candidates, refresh pass went from 13-18ms to under 1ms
- collect candidates under the prioritization lock into scratch vectors so a warm pass allocates nothing
- drop entries that settle at clear so Update only walks occluded or fading emitters
- onset flag only set when no voice is live and cleared when the last voice ends
- GetListener reads the prioritizer's cached listener instead of scanning every object
- keep the last sightline results per emitter and expose GetLastSightlineVerdicts to blue and python
- result array is a plain bool array kept between ticks
scaled emitters were cut off at r*sqrt(k) instead of r*k, so the sightline pass never decided occlusion for them in that band and they played unoccluded behind geometry. culling range weight used the same test so it was off too.
- remove IsTracked
- EmitterState::SetTargets is the one place targets get set
- remove the snap param from SetObstructionOcclusion and SetEmitterLineOfSightBlockage, back to main signatures
- rename GetLastSightlineVerdicts to GetLastSightlineResults in c++, blue and python
- rename AtRestClear to IsClear
- shorter comments in plain words
@phevosccp
phevosccp marked this pull request as ready for review September 24, 2026 11:05
Comment thread include/IEveObstructionQuery.h Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the idea is for destiny to implement this interface, it may be better to have this header in destiny, and for audio to depend on destiny (and source this file from it). It would make the link between these two components more explicit. See carbonengine/trinity#91 for a similar trinity change

Comment thread src/AudManager.h
// Current, mid-fade occlusion value for an emitter. 0.0 if the emitter is clear or not tracked.
float GetEmitterOcclusion( AkGameObjectID emitterID ) const;
// Results of the last sightline pass, emitter id to blocked. See AudObstructionOcclusion::GetLastSightlineResults.
std::map<AkGameObjectID, bool> GetLastSightlineResults() const;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe return const std::map<AkGameObjectID, bool>& to avoid copying the map?

float GetEmitterOcclusion(AkGameObjectID emitterID) const;

/// Results of the last sightline pass, emitter id to blocked. Emitters that were not checked are missing.
std::map<AkGameObjectID, bool> GetLastSightlineResults() const;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to the other place, return const ...&?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants