Skip to content

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:

  1. Update the raytracing manager with the listener position. We currently only support one main listener to calculate obstruction with.
  2. 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);

  • FSweejPropagationID returned 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_ID if none are found.

Example:

Component example:

class UMyAudioComponent : public UAkGameObject

Registering:

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) override in UActorComponent also allows you to add this asset user data to a USceneComponent in 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());
}