Set Up Guide - Sweej Propagation with Wwise
This guide is aimed at programmers and technical sound designers who are getting SweejTech Propagation set up to work with Wwise.
How to integrate
To get SweejTech GPU-powered obstruction working in your project, you will need to do two things:
- Update the raytracing manager with the listener position. We currently only support one main listener to calculate obstruction with.
- Register/Unregister the positions you would like obstruction calculated for with the raytracing manager. This is done via
USceneComponents.
From there you will be able to access obstruction on a per component basis (GetObstructionAmount) or query all obstruction values for all registered positions in the raytracing manager (GetAllObstructionValues).
You can register, unregister, and query obstruction values in both C++ and Blueprints.
Params such as SourceRadius, ObstructionValueSmoothingTime, NumFirstFrameObstructionRays and so on can be added to the component via asset user data.
TL;DR - API Cheatsheet
USweejPropagationRaytraceSubsystem
static FSweejObstructionRayTraceManager* FSweejObstructionRayTraceManager* GetRayTraceManager(const TObjectPtr<UWorld> InWorld);
FSweejObstructionRayTraceManager
void SetListenerPositionOverride(const FVector& InListenerPosition)FSweejPropagationID AddPropagationComponent(USceneComponent& InPropagationComponent);void RemovePropagationComponent(const USceneComponent& InPropagationComponent);FSweejPropagationID GetPropagationID(const USceneComponent& InComponent) const;float GetObstructionAmount(const USceneComponent& InComponent) const;float GetObstructionAmount(const FSweejPropagationID InPropagationID) const;TArray<FSweejObstructionData> GetAllObstructionValues() const;
USweejPropagationBlueprintFunctionLibrary
// Returns all obstruction values from the SweejTech GPU obstruction system
UFUNCTION(BlueprintCallable, Category = "SweejTech|Propagation|Obstruction", meta = (WorldContext = "WorldContextObject", DisplayName = "Get All Obstruction Values"))
static const TArray<FSweejObstructionData> GetAllObstructionValues(const UObject* WorldContextObject);
// Returns the obstruction value for a specific PropagationID from the SweejTech GPU obstruction system
UFUNCTION(BlueprintCallable, Category = "SweejTech|Propagation|Obstruction", meta = (WorldContext = "WorldContextObject", DisplayName = "Get Obstruction Value For Propagation ID"))
static float GetObstructionValueForPropagationID(const UObject* WorldContextObject, int32 PropagationID);
// Returns the obstruction value for a specific SceneComponent registered with the SweejTech GPU obstruction system
UFUNCTION(BlueprintCallable, Category = "SweejTech|Propagation|Obstruction", meta = (WorldContext = "WorldContextObject", DisplayName = "Get Obstruction Value For Scene Component"))
static float GetObstructionValueForSceneComponent(const UObject* WorldContextObject, const USceneComponent* SceneComponent);
// Returns the obstruction value for a specific AudioComponent registered with the SweejTech GPU obstruction system
UFUNCTION(BlueprintCallable, Category = "SweejTech|Propagation|Obstruction", meta = (WorldContext = "WorldContextObject", DisplayName = "Get Obstruction Value For Audio Component"))
static float GetObstructionValueForAudioComponent(const UObject* WorldContextObject, const UAudioComponent* AudioComponent);
// Returns the PropagationID for a specific SceneComponent registered with the SweejTech GPU obstruction system
UFUNCTION(BlueprintCallable, Category = "SweejTech|Propagation|PropagationID", meta = (WorldContext = "WorldContextObject", DisplayName = "Get Propagation ID For Scene Component"))
static int32 GetPropagationIDFromSceneComponent(const UObject* WorldContextObject, const USceneComponent* SceneComponent);
// Returns the PropagationID for a specific AudioComponent registered with the SweejTech GPU obstruction system
UFUNCTION(BlueprintCallable, Category = "SweejTech|Propagation|PropagationID", meta = (WorldContext = "WorldContextObject", DisplayName = "Get Propagation ID For Audio Component"))
static int32 GetPropagationIDFromAudioComponent(const UObject* WorldContextObject, const UAudioComponent* AudioComponent);
// Register a USceneComponent with SweejTech's GPU Obstruction system
// Only required for non-UE Audio objects (Unreal Audio objects are registered automatically)
// Returns a Propagation ID that can be used to retrieve obstruction values
//
// WARNING! Registered scene components must be unregistered when finished with
UFUNCTION(BlueprintCallable, Category = "SweejTech|Propagation|Obstruction", meta = (WorldContext = "WorldContextObject", DisplayName = "Register Scene Component With GPU Obstruction"))
static int32 RegisterSceneComponentWithGPUObstruction(const UObject* WorldContextObject, USceneComponent* SceneComponent);
// Unregister a USceneComponent from SweejTech's GPU Obstruction system
// Only required for non-UE Audio objects (Unreal Audio objects are unregistered automatically)
UFUNCTION(BlueprintCallable, Category = "SweejTech|Propagation|Obstruction", meta = (WorldContext = "WorldContextObject", DisplayName = "Unregister Scene Component From GPU Obstruction"))
static void UnregisterSceneComponentFromGPUObstruction(const UObject* WorldContextObject, const USceneComponent* SceneComponent);How do I access the raytracing manager in code?
FSweejObstructionRayTraceManager is the raytracing manager. It is owned by USweejPropagationRaytraceSubsystem, which is a UWorldSubsystem.
To access from another system, there is a static helper function inside USweejPropagationRaytraceSubsystem:
USweejPropagationRaytraceSubsystem:
static FSweejObstructionRayTraceManager* FSweejObstructionRayTraceManager* GetRayTraceManager(const TObjectPtr<UWorld> InWorld);
Example:
FSweejObstructionRayTraceManager* UMyWorldSubsystem::GetRayTraceManager() const
{
return USweejPropagationRaytraceSubsystem::GetRayTraceManager(GetWorld());`
}How do I update the listener position in the Raytracing Manager?
Call this every Tick, or whenever the Listener position updates with your new Listener Position.
FSweejObstructionRayTraceManager:
void SetListenerPositionOverride(const FVector& InListenerPosition)
Example:
Assuming the listener is on the main camera:
const TObjectPtr<APlayerCameraManager> CameraManager = UGameplayStatics::GetPlayerCameraManager(GetWorld(), 0 /*PlayerIndex*/);
if (CameraManager == nullptr)
{
return;
}
const UAkComponentSet& DefaultListeners = AkAudioDevice->GetDefaultListeners();
if (DefaultListeners.IsEmpty())
{
return;
}
const FVector MainCameraPosition = CameraManager->GetTransform().GetLocation();
TWeakObjectPtr<UAkComponent> MainListener;
float MinDistToCameraSq = TNumericLimits<float>::Max();
// Get the closest default AkListener to the main camera
for (const TWeakObjectPtr<UAkComponent> Listener : DefaultListeners)
{
if (!Listener.IsValid())
{
continue;
}
const float ListenerDistToCamera = FVector::DistSquared(Listener.Pin()->GetPosition(), MainCameraPosition);
if (ListenerDistToCamera <= MinDistToCameraSq)
{
MainListener = Listener;
MinDistToCameraSq = ListenerDistToCamera;
}
}
if (!MainListener.IsValid())
{
return;
}
// Update the sweej raytracing manager with the updated listener position
RaytracingManager->SetListenerPositionOverride(MainListener.Pin()->GetPosition());How do I register/unregister positions with the Raytracing Manager?
You will need a component that is/derives from USceneComponent at the sound position you are wishing to calculate obstruction values for.
Note: UAkGameObject derives from USceneComponent if you are using the Wwise UE integration.
FSweejObstructionRayTraceManager:
FSweejPropagationID AddPropagationComponent(USceneComponent& InPropagationComponent);
FSweejPropagationIDreturned from AddPropagationComponent is the internal ID that the raytracing manager uses to identify the position.
void RemovePropagationComponent(const USceneComponent& InPropagationComponent);
- Make sure to unregister your positions when destroyed, or if you want to reduce the number of positions that obstruction is calculated for.
FSweejPropagationID GetPropagationID(const USceneComponent& InComponent) const;
- Query the propagation ID for a scene component. Will return
INVALID_SWEEJ_PROPAGATION_IDif none are found.
Example:
Component example:
class UMyAudioComponent : public UAkGameObjectRegistering:
void UMyAudioComponent::OnRegister()
{
Super::OnRegister();
if (FSweejObstructionRayTraceManager* RaytraceManager = USweejPropagationRaytraceSubsystem::GetRayTraceManager(GetWorld()))
{
PropagationID = RaytraceManager->AddPropagationComponent(*this);
}
}Unregistering:
void UMyAudioComponent::OnUnregister()
{
if (FSweejObstructionRayTraceManager* RaytraceManager = USweejPropagationRaytraceSubsystem::GetRayTraceManager(GetWorld()))
{
RaytraceManager->RemovePropagationComponent(*this);
PropagationID = INVALID_SWEEJ_PROPAGATION_ID;
}
Super::OnUnregister();
}How do I get my obstruction values out of the Raytracing Manager?
From C++:
FSweejObstructionRayTraceManager:
float GetObstructionAmount(const USceneComponent& InComponent) const;float GetObstructionAmount(const FSweejPropagationID InPropagationID) const;
- Gets the obstruction value for a single component.
TArray<FSweejObstructionData> GetAllObstructionValues() const;
- Gets all obstruction values for all registered components.
- Returns an array of
FSweejObstructionData, a struct which holds the propagation ID and obstruction value.
UPROPERTY(BlueprintReadOnly)
int32 PropagationID{ 0 }; // Note, this is the same as FSweejPropagationID
UPROPERTY(BlueprintReadOnly)
float ObstructionAmount{ 0.0f };Example:
Subsystem handling both single point and multipoint game objects in Wwise.
if (FSweejPropagationData* FoundPropagationData = GameObjectIDToSweejPropagationDataMap.Find(GameObject->GetAkGameObjectID()))
{
if (FoundPropagationData->PropagationIDs.IsEmpty())
{
continue;
}
// Single GameObject
if (FoundPropagationData->PropagationIDs.Num() == 1)
{
const float ObstructionAmount = RaytracingManager->GetObstructionAmount(FoundPropagationData->PropagationIDs[0]);
WwiseSoundEngine->SetObjectObstructionAndOcclusion(GameObject->GetAkGameObjectID(), MainListener.Pin()->GetAkGameObjectID(), ObstructionAmount, 0.0f /*OcclusionLevel*/);
}
// Multiplepoint GameObject
else
{
TArray<AkObstructionOcclusionValues> ObstructionOcclusionValues;
ObstructionOcclusionValues.Reserve(FoundPropagationData->PropagationIDs.Num());
for (const FSweejPropagationID& PropagationID : FoundPropagationData->PropagationIDs)
{
ObstructionOcclusionValues.Emplace(RaytracingManager->GetObstructionAmount(PropagationID), 0.0f /*OcclusionLevel*/);
}
WwiseSoundEngine->SetMultipleObstructionAndOcclusion(GameObject->GetAkGameObjectID(), MainListener.Pin()->GetAkGameObjectID(), ObstructionOcclusionValues.GetData(), ObstructionOcclusionValues.Num());
}
}How do I edit parameters like Source Radius, first frame obstruction settings, etc?
These are stored in USweejPropagationSoundDefinition and can be created, edited and hooked up to components in the editor via AssetUserData.
- In the Content Browser, right click and search for Sweej Propagation Sound Definition. This asset lets you set default values for various parameters, and you can create multiple of these assets.
- To add this to a component that has obstruction in the editor, find where the component lives on a Blueprint, and in the details panel find the AssetUserData section. Add a new entry, and in the dropdown and select Sweej Propagation Asset User Data. You can expand this to show the parameter data, assign a loudness definition file to set the defaults, and override specific params.
void AddAssetUserData(UAssetUserData* InUserData) overrideinUActorComponentalso allows you to add this asset user data to aUSceneComponentin C++.
Full custom propagation subsystem example:
SweejWwisePropagationSubsystem.h
// Copyright 2026, SweejTech Ltd. All Rights Reserved.
#pragma once
#include "AkGameObject.h"
#include "Engine/World.h"
#include "Subsystems/WorldSubsystem.h"
#include "SweejObstructionRayTraceManager.h"
#include "SweejWwisePropagationSubsystem.generated.h"
class FSweejObstructionRayTraceManager;
class FSubsystemCollectionBase;
class USweejWwiseGameObjectComponent;
UCLASS()
class SWEEJWWISEPROPAGATION_API USweejWwisePropagationSubsystem : public UTickableWorldSubsystem
{
GENERATED_BODY()
public:
static TObjectPtr<USweejWwisePropagationSubsystem> Get(const TObjectPtr<UWorld> InWorld);
USweejWwisePropagationSubsystem() = default;
virtual bool ShouldCreateSubsystem(UObject* Outer) const override { return true; }
virtual bool IsTickableInEditor() const override { return true; }
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
virtual void Deinitialize() override;
virtual void Tick(float DeltaTime) override;
virtual TStatId GetStatId() const override;
private:
void OnGameObjectAddedHandler(USweejWwiseGameObjectComponent& InGameObject);
void OnGameObjectRemovedHandler(const USweejWwiseGameObjectComponent& InGameObject);
FSweejObstructionRayTraceManager* GetRayTraceManager() const;
struct FSweejPropagationWwiseData
{
AkGameObjectID WwiseGameobjectID{ AK_INVALID_GAME_OBJECT };
float ObstructionValue{ 0.0f };
};
struct FSweejPropagationData
{
TArray<FSweejPropagationID> PropagationIDs;
};
TMap<AkGameObjectID, FSweejPropagationData> GameObjectIDToSweejPropagationDataMap;
FDelegateHandle OnGameObjectAddedHandle;
FDelegateHandle OnGameObjectRemovedHandle;
};SweejWwisePropagationSubsystem.cpp
// Copyright 2026, SweejTech Ltd. All Rights Reserved.
#include "SweejWwisePropagationSubsystem.h"
#include "AkAudioDevice.h"
#include "AkComponent.h"
#include "Delegates/Delegate.h"
#include "Delegates/DelegateCombinations.h"
#include "Kismet/GameplayStatics.h"
#include "Subsystems/SubsystemCollection.h"
#include "SweejObstructionRayTraceManager.h"
#include "SweejPropagationRaytraceSubsystem.h"
#include "SweejWwiseGameObjectComponent.h"
#include "SweejWwiseGameObjectSubsystem.h"
#include "Wwise/API/WwiseSoundEngineAPI.h"
TObjectPtr<USweejWwisePropagationSubsystem> USweejWwisePropagationSubsystem::Get(const TObjectPtr<UWorld> InWorld)
{
if (InWorld)
{
return InWorld->GetSubsystem<USweejWwisePropagationSubsystem>();
}
return nullptr;
}
void USweejWwisePropagationSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
Super::Initialize(Collection);
if (TObjectPtr<USweejWwiseGameObjectSubsystem> SweejWwiseGameObjectSubsystem = USweejWwiseGameObjectSubsystem::Get(GetWorld()))
{
OnGameObjectAddedHandle = SweejWwiseGameObjectSubsystem->OnGameObjectAdded.AddUObject(this, &USweejWwisePropagationSubsystem::OnGameObjectAddedHandler);
OnGameObjectRemovedHandle = SweejWwiseGameObjectSubsystem->OnGameObjectRemoved.AddUObject(this, &USweejWwisePropagationSubsystem::OnGameObjectRemovedHandler);
}
}
void USweejWwisePropagationSubsystem::Deinitialize()
{
if (TObjectPtr<USweejWwiseGameObjectSubsystem> SweejWwiseGameObjectSubsystem = USweejWwiseGameObjectSubsystem::Get(GetWorld()))
{
SweejWwiseGameObjectSubsystem->OnGameObjectAdded.Remove(OnGameObjectAddedHandle);
SweejWwiseGameObjectSubsystem->OnGameObjectRemoved.Remove(OnGameObjectRemovedHandle);
}
Super::Deinitialize();
}
void USweejWwisePropagationSubsystem::Tick(float DeltaTime)
{
FSweejObstructionRayTraceManager* RaytracingManager = GetRayTraceManager();
if (RaytracingManager == nullptr)
{
return;
}
IWwiseSoundEngineAPI* WwiseSoundEngine = IWwiseSoundEngineAPI::Get();
if (WwiseSoundEngine == nullptr)
{
return;
}
FAkAudioDevice* AkAudioDevice = FAkAudioDevice::Get();
if (AkAudioDevice == nullptr)
{
return;
}
// We are assuming everything is using a default listener on the player camera
const TObjectPtr<APlayerCameraManager> CameraManager = UGameplayStatics::GetPlayerCameraManager(GetWorld(), 0 /*PlayerIndex*/);
if (CameraManager == nullptr)
{
return;
}
const UAkComponentSet& DefaultListeners = AkAudioDevice->GetDefaultListeners();
if (DefaultListeners.IsEmpty())
{
return;
}
const FVector MainCameraPosition = CameraManager->GetTransform().GetLocation();
TWeakObjectPtr<UAkComponent> MainListener;
float MinDistToCameraSq = TNumericLimits<float>::Max();
for (const TWeakObjectPtr<UAkComponent> Listener : DefaultListeners)
{
if (!Listener.IsValid())
{
continue;
}
const float ListenerDistToCamera = FVector::DistSquared(Listener.Pin()->GetPosition(), MainCameraPosition);
if (ListenerDistToCamera <= MinDistToCameraSq)
{
MainListener = Listener;
MinDistToCameraSq = ListenerDistToCamera;
}
}
if (!MainListener.IsValid())
{
return;
}
RaytracingManager->SetListenerPositionOverride(MainListener.Pin()->GetPosition());
if (TObjectPtr<USweejWwiseGameObjectSubsystem> SweejWwiseGameObjectSubsystem = USweejWwiseGameObjectSubsystem::Get(GetWorld()))
{
const TArray<TWeakObjectPtr<USweejWwiseGameObjectComponent>>& GameObjects = SweejWwiseGameObjectSubsystem->GetGameObjects();
for (const TWeakObjectPtr<USweejWwiseGameObjectComponent>& GameObject : GameObjects)
{
if (FSweejPropagationData* FoundPropagationData = GameObjectIDToSweejPropagationDataMap.Find(GameObject->GetAkGameObjectID()))
{
if (FoundPropagationData->PropagationIDs.IsEmpty())
{
continue;
}
// Single GameObject
if (FoundPropagationData->PropagationIDs.Num() == 1)
{
const float ObstructionAmount = RaytracingManager->GetObstructionAmount(FoundPropagationData->PropagationIDs[0]);
WwiseSoundEngine->SetObjectObstructionAndOcclusion(GameObject->GetAkGameObjectID(), MainListener.Pin()->GetAkGameObjectID(), ObstructionAmount, 0.0f /*OcclusionLevel*/);
}
// Multiplepoint GameObject
else
{
TArray<AkObstructionOcclusionValues> ObstructionOcclusionValues;
ObstructionOcclusionValues.Reserve(FoundPropagationData->PropagationIDs.Num());
for (const FSweejPropagationID& PropagationID : FoundPropagationData->PropagationIDs)
{
ObstructionOcclusionValues.Emplace(RaytracingManager->GetObstructionAmount(PropagationID), 0.0f /*OcclusionLevel*/);
}
WwiseSoundEngine->SetMultipleObstructionAndOcclusion(GameObject->GetAkGameObjectID(), MainListener.Pin()->GetAkGameObjectID(), ObstructionOcclusionValues.GetData(), ObstructionOcclusionValues.Num());
}
}
}
}
}
TStatId USweejWwisePropagationSubsystem::GetStatId() const
{
RETURN_QUICK_DECLARE_CYCLE_STAT(USweejWwisePropagationSubsystem, STATGROUP_Tickables);
}
void USweejWwisePropagationSubsystem::OnGameObjectAddedHandler(USweejWwiseGameObjectComponent& InGameObject)
{
if (FSweejObstructionRayTraceManager* RaytracingManager = GetRayTraceManager())
{
const FSweejPropagationID PropagationID = RaytracingManager->AddPropagationComponent(InGameObject);
FSweejPropagationData PropagationData;
PropagationData.PropagationIDs = { PropagationID };
GameObjectIDToSweejPropagationDataMap.Emplace(InGameObject.GetAkGameObjectID(), PropagationData);
}
}
void USweejWwisePropagationSubsystem::OnGameObjectRemovedHandler(const USweejWwiseGameObjectComponent& InGameObject)
{
if (FSweejObstructionRayTraceManager* RaytracingManager = GetRayTraceManager())
{
RaytracingManager->RemovePropagationComponent(InGameObject);
GameObjectIDToSweejPropagationDataMap.Remove(InGameObject.GetAkGameObjectID());
}
}
FSweejObstructionRayTraceManager* USweejWwisePropagationSubsystem::GetRayTraceManager() const
{
return USweejPropagationRaytraceSubsystem::GetRayTraceManager(GetWorld());
}