From c0b763c0ba978c6ec9035c074e6a800786587e16 Mon Sep 17 00:00:00 2001 From: phevosccp <160875214+phevosccp@users.noreply.github.com> Date: Wed, 4 Feb 2026 16:26:54 +0000 Subject: [PATCH 01/48] empty initialize AudGeometry --- CMakeLists.txt | 2 ++ src/AudGeometry.cpp | 0 src/AudGeometry.h | 31 +++++++++++++++++++++++++++++++ 3 files changed, 33 insertions(+) create mode 100644 src/AudGeometry.cpp create mode 100644 src/AudGeometry.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 13fd8fb..a61d227 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -70,6 +70,7 @@ ccp_add_library(CarbonAudio SHARED src/AudGameObjResource.cpp src/audio2.cpp src/AudGameObjResource_Blue.cpp + src/AudGeometry.cpp src/stdafx.cpp src/AudListener.cpp src/AudioCurveSetDriver.cpp @@ -111,6 +112,7 @@ target_sources(CarbonAudio PRIVATE src/AudStaticDataRepository.h src/AudUIPlayer.h src/AudMusicPlayer.h + src/AudGeometry.h src/autoversion.h src/DebugUtilities.h src/LogBridge.h diff --git a/src/AudGeometry.cpp b/src/AudGeometry.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/AudGeometry.h b/src/AudGeometry.h new file mode 100644 index 0000000..e6b583d --- /dev/null +++ b/src/AudGeometry.h @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////// +// +// Creator: Phevos Rinis +// Creation Date: Jan 2026 +// Copyright (c) 2026 CCP Games +// + +#pragma once + + +#include +#include + + +struct Vector3; + + +BLUE_CLASS(AudGeometry) : + public ITr2AudGeometry + +{ +public: + AudGeometry(IRoot* lockobj = NULL); + virtual ~AudGeometry(); + + EXPOSE_TO_BLUE(); + + +}; + + From 364fbb6c21c15711718d7a61f9539900abe3cbaf Mon Sep 17 00:00:00 2001 From: phevosccp <160875214+phevosccp@users.noreply.github.com> Date: Wed, 4 Feb 2026 16:30:24 +0000 Subject: [PATCH 02/48] initialize the AKSpatialAudioSDK --- src/AudListener.cpp | 9 +++++++-- src/AudManager.cpp | 26 ++++++++++++++++++++++++++ src/AudManager.h | 2 ++ src/stdafx.h | 2 ++ 4 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/AudListener.cpp b/src/AudListener.cpp index bb7936e..bd513e5 100644 --- a/src/AudListener.cpp +++ b/src/AudListener.cpp @@ -13,8 +13,9 @@ AudListener::AudListener( IRoot* lockobj ) : AudGameObjResource( LISTENER_GAME_O AudListener::~AudListener() { - AK::SoundEngine::RemoveDefaultListener(m_ID); - AK::SoundEngine::UnregisterGameObj(m_ID); + AK::SpatialAudio::UnregisterListener( m_ID ); + AK::SoundEngine::RemoveDefaultListener( m_ID ); + AK::SoundEngine::UnregisterGameObj( m_ID ); } void AudListener::RegisterWwiseObject() @@ -25,6 +26,10 @@ void AudListener::RegisterWwiseObject() { AK::SoundEngine::RegisterGameObj(m_ID, m_name.c_str()); AK::SoundEngine::AddDefaultListener(m_ID); + + // Register listener for occlusion/diffraction processing + AK::SpatialAudio::RegisterListener( m_ID ); + m_gameObjRegistered = true; } } diff --git a/src/AudManager.cpp b/src/AudManager.cpp index 3063aff..4f45212 100644 --- a/src/AudManager.cpp +++ b/src/AudManager.cpp @@ -151,6 +151,12 @@ bool AudManager::Init() return false; } + if( !InitSpatialAudioGeometry() ) + { + CCP_LOGERR( "Failed to initialize audio : Spatial Audio Geometry" ); + return false; + } + #ifndef AK_OPTIMIZED if( !InitCommunication() ) { @@ -392,6 +398,26 @@ bool AudManager::InitMusic() return true; } +bool AudManager::InitSpatialAudioGeometry() +{ + AkSpatialAudioInitSettings spatialSettings; + + // Configure geometry-based audio processing + // spatialSettings.uMaxSoundPropagationDepth = 8; + // spatialSettings.uNumberOfPrimaryRays = 100; + // spatialSettings.uMaxDiffractionOrder = 8; + // spatialSettings.bEnableGeometricDiffractionAndTransmission = true; + + if( AK::SpatialAudio::Init( spatialSettings ) != AK_Success ) + { + CCP_LOGERR( "Failed to initialize Wwise Spatial Audio for geometry processing" ); + return false; + } + + CCP_LOG_CH( s_ch, "Wwise Spatial Audio initialized for geometry-based occlusion/diffraction" ); + return true; +} + bool AudManager::SetGlobalRTPC( const std::wstring& rtpcName, float value ) { if( g_audioInitialized && !g_shuttingDown) diff --git a/src/AudManager.h b/src/AudManager.h index cc6b663..4b4d1b0 100644 --- a/src/AudManager.h +++ b/src/AudManager.h @@ -183,6 +183,8 @@ BLUE_CLASS( AudManager ) : bool InitMusic(); // Initializes Wwise's sound engine. bool InitSound(); + // Initializes Wwise's Spatial Audio for geometry-based occlusion and diffraction. + bool InitSpatialAudioGeometry(); // Tick handler void Process(); // Registers audio2 for the tick handler. diff --git a/src/stdafx.h b/src/stdafx.h index 59fdcba..2b3484d 100644 --- a/src/stdafx.h +++ b/src/stdafx.h @@ -42,6 +42,8 @@ #include #include +#include + #ifndef AK_OPTIMIZED #include #endif From dcfd31455fe588d5a606b29c1af3d2cade12ace3 Mon Sep 17 00:00:00 2001 From: phevosccp <160875214+phevosccp@users.noreply.github.com> Date: Wed, 4 Feb 2026 17:08:31 +0000 Subject: [PATCH 03/48] add blue file for audGeometry --- CMakeLists.txt | 1 + src/AudGeometry_Blue.cpp | 10 ++++++++++ 2 files changed, 11 insertions(+) create mode 100644 src/AudGeometry_Blue.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index a61d227..465e8a9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -71,6 +71,7 @@ ccp_add_library(CarbonAudio SHARED src/audio2.cpp src/AudGameObjResource_Blue.cpp src/AudGeometry.cpp + src/AudGeometry_Blue.cpp src/stdafx.cpp src/AudListener.cpp src/AudioCurveSetDriver.cpp diff --git a/src/AudGeometry_Blue.cpp b/src/AudGeometry_Blue.cpp new file mode 100644 index 0000000..0b7186a --- /dev/null +++ b/src/AudGeometry_Blue.cpp @@ -0,0 +1,10 @@ +#include "stdafx.h" +#include "AudGeometry.h" + +BLUE_DEFINE_ABSTRACT( AudGeometry ); + +const Be::ClassInfo* AudGeometry::ExposeToBlue() +{ + EXPOSURE_BEGIN( AudGeometry, "Audio geometry for Wwise Spatial Audio occlusion/diffraction" ) + EXPOSURE_END() +} From 73e2c708b30fc917603176b407ba8ba172e3d0a4 Mon Sep 17 00:00:00 2001 From: phevosccp <160875214+phevosccp@users.noreply.github.com> Date: Fri, 6 Feb 2026 10:54:03 +0000 Subject: [PATCH 04/48] add conversion and blue file --- src/AudGeometry.cpp | 138 +++++++++++++++++++++++++++++++++++++++ src/AudGeometry.h | 13 +++- src/AudGeometry_Blue.cpp | 4 +- 3 files changed, 152 insertions(+), 3 deletions(-) diff --git a/src/AudGeometry.cpp b/src/AudGeometry.cpp index e69de29..4775a34 100644 --- a/src/AudGeometry.cpp +++ b/src/AudGeometry.cpp @@ -0,0 +1,138 @@ +#include "stdafx.h" +#include "AudGeometry.h" +#include "Vector3.h" +#include "AudManager.h" + +static CcpLogChannel_t s_ch = CCP_LOG_DEFINE_CHANNEL( "AudGeometry" ); + +namespace +{ + // Convert Vector3 array to AkVertex array + // Applies RH to LH coordinate conversion (negate Z) + std::vector ConvertVertices( const std::vector& vertices ) + { + std::vector akVertices( vertices.size() ); + for( size_t i = 0; i < vertices.size(); ++i ) + { + const Vector3& v = vertices[i]; + // RH to LH: negate Z + akVertices[i] = AkVertex( v.x, v.y, -v.z ); + } + return akVertices; + } + + // Convert index array to AkTriangle array + // Every 3 indices form one triangle + // Winding order is reversed due to coordinate system change + std::vector ConvertTriangles( const std::vector& indices ) + { + size_t numTriangles = indices.size() / 3; + std::vector akTriangles( numTriangles ); + for( size_t i = 0; i < numTriangles; ++i ) + { + // Reverse winding order (0,1,2 -> 0,2,1) for RH to LH conversion + akTriangles[i] = AkTriangle( + static_cast( indices[i * 3 + 0] ), + static_cast( indices[i * 3 + 2] ), + static_cast( indices[i * 3 + 1] ), + AK_INVALID_SURFACE + ); + } + return akTriangles; + } + + // Convert Matrix to AkTransform for geometry instance placement + // Applies RH to LH coordinate conversion + AkTransform ConvertTransform( const Matrix& matrix ) + { + AkTransform transform; + + // Extract position from matrix + AkVector position; + position.X = matrix._41; + position.Y = matrix._42; + position.Z = -matrix._43; + + // Extract forward + AkVector orientationFront; + orientationFront.X = matrix._31; + orientationFront.Y = matrix._32; + orientationFront.Z = -matrix._33; + + AkVector orientationTop; + orientationTop.X = matrix._21; + orientationTop.Y = matrix._22; + orientationTop.Z = -matrix._23; + + transform.SetPosition( position ); + transform.SetOrientation( orientationFront, orientationTop ); + return transform; + } +} + +AudGeometry::AudGeometry( IRoot* lockobj ) {} + +AudGeometry::~AudGeometry() {} + +void AudGeometry::SetGeometry( + uint64_t geometryId, + const Tr2AudGeometryData& geometryData, + const Matrix& worldTransform ) +{ + if( geometryData.m_vertices.empty() || geometryData.m_indices.empty() ) + { + CCP_LOGWARN_CH( s_ch, "SetGeometry called with empty geometry data for ID %llu", geometryId ); + return; + } + + // Convert vertices and triangles to Wwise format + std::vector akVertices = ConvertVertices( geometryData.m_vertices ); + std::vector akTriangles = ConvertTriangles( geometryData.m_indices ); + + // Set up geometry parameters + AkGeometryParams params; + params.Vertices = akVertices.data(); + params.NumVertices = static_cast( akVertices.size() ); + params.Triangles = akTriangles.data(); + params.NumTriangles = static_cast( akTriangles.size() ); + params.Surfaces = nullptr; + params.NumSurfaces = 0; + params.EnableDiffraction = true; + params.EnableDiffractionOnBoundaryEdges = true; + + // Register the geometry set with Wwise + AKRESULT result = AK::SpatialAudio::SetGeometry( geometryId, params ); + if( result != AK_Success ) + { + CCP_LOGERR_CH( s_ch, "Failed to set geometry for ID %llu, error: %d", geometryId, result ); + return; + } + + CCP_LOG_CH( s_ch, "Registered geometry ID %llu with %zu vertices, %zu triangles", + geometryId, akVertices.size(), akTriangles.size() ); + + // TODO: Create geometry instance to place it in the world + // AkGeometryInstanceParams instanceParams; + // instanceParams.GeometrySetID = geometryId; + // instanceParams.PositionAndOrientation = ConvertTransform( worldTransform ); + // instanceParams.RoomID = AK::SpatialAudio::kOutdoorRoomID; + // AK::SpatialAudio::SetGeometryInstance( geometryId, instanceParams ); +} + +void AudGeometry::SetGeometryTransform( uint64_t geometryId, const Matrix& worldTransform ) +{ + // TODO: Update existing geometry instance's position/orientation + // AkGeometryInstanceParams instanceParams; + // instanceParams.GeometrySetID = geometryId; + // instanceParams.PositionAndOrientation = ConvertTransform( worldTransform ); + // instanceParams.RoomID = AK::SpatialAudio::kOutdoorRoomID; + // AK::SpatialAudio::SetGeometryInstance( geometryId, instanceParams ); +} + +void AudGeometry::RemoveGeometry( uint64_t geometryId ) +{ + // TODO: Remove instance first, then geometry set + // AK::SpatialAudio::RemoveGeometryInstance( geometryId ); + // AK::SpatialAudio::RemoveGeometry( geometryId ); +} + diff --git a/src/AudGeometry.h b/src/AudGeometry.h index e6b583d..07356f4 100644 --- a/src/AudGeometry.h +++ b/src/AudGeometry.h @@ -25,7 +25,16 @@ BLUE_CLASS(AudGeometry) : EXPOSE_TO_BLUE(); - -}; + // ITr2AudGeometry interface + + void SetGeometry( + uint64_t geometryId, + const Tr2AudGeometryData& geometryData, + const Matrix& worldTransform) override; + + void SetGeometryTransform(uint64_t geometryId, const Matrix& worldTransform) override; + void RemoveGeometry(uint64_t geometryId) override; +}; +TYPEDEF_BLUECLASS( AudGeometry ); diff --git a/src/AudGeometry_Blue.cpp b/src/AudGeometry_Blue.cpp index 0b7186a..4eb19a1 100644 --- a/src/AudGeometry_Blue.cpp +++ b/src/AudGeometry_Blue.cpp @@ -1,10 +1,12 @@ #include "stdafx.h" #include "AudGeometry.h" -BLUE_DEFINE_ABSTRACT( AudGeometry ); +BLUE_DEFINE( AudGeometry ); +BLUE_DEFINE_INTERFACE( ITr2AudGeometry ); const Be::ClassInfo* AudGeometry::ExposeToBlue() { EXPOSURE_BEGIN( AudGeometry, "Audio geometry for Wwise Spatial Audio occlusion/diffraction" ) + MAP_INTERFACE( ITr2AudGeometry ) EXPOSURE_END() } From 51c23c3ddbf0a900ee67fcf0862dcb3ec418710c Mon Sep 17 00:00:00 2001 From: phevosccp <160875214+phevosccp@users.noreply.github.com> Date: Fri, 6 Feb 2026 13:24:31 +0000 Subject: [PATCH 05/48] minor fix for surfaces --- src/AudGeometry.cpp | 38 ++++++++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/src/AudGeometry.cpp b/src/AudGeometry.cpp index 4775a34..48d3717 100644 --- a/src/AudGeometry.cpp +++ b/src/AudGeometry.cpp @@ -35,7 +35,7 @@ namespace static_cast( indices[i * 3 + 0] ), static_cast( indices[i * 3 + 2] ), static_cast( indices[i * 3 + 1] ), - AK_INVALID_SURFACE + 0 // default surface index ); } return akTriangles; @@ -85,31 +85,57 @@ void AudGeometry::SetGeometry( return; } + if( geometryData.m_indices.size() % 3 != 0 ) + { + CCP_LOGERR_CH( s_ch, "SetGeometry: index count %zu is not a multiple of 3 for ID %llu", + geometryData.m_indices.size(), geometryId ); + return; + } + + // Validate that all triangle indices are within vertex bounds + for( size_t i = 0; i < geometryData.m_indices.size(); ++i ) + { + if( geometryData.m_indices[i] >= geometryData.m_vertices.size() ) + { + CCP_LOGERR_CH( s_ch, "SetGeometry: index[%zu] = %u is out of bounds (vertex count: %zu) for ID %llu", + i, geometryData.m_indices[i], geometryData.m_vertices.size(), geometryId ); + return; + } + } + // Convert vertices and triangles to Wwise format std::vector akVertices = ConvertVertices( geometryData.m_vertices ); std::vector akTriangles = ConvertTriangles( geometryData.m_indices ); + // Default acoustic surface - all triangles share the same surface properties + AkAcousticSurface surface; + surface.strName = "default"; + surface.textureID = AK_INVALID_UNIQUE_ID; + surface.transmissionLoss = 1.0f; + // Set up geometry parameters AkGeometryParams params; params.Vertices = akVertices.data(); params.NumVertices = static_cast( akVertices.size() ); params.Triangles = akTriangles.data(); params.NumTriangles = static_cast( akTriangles.size() ); - params.Surfaces = nullptr; - params.NumSurfaces = 0; + params.Surfaces = &surface; + params.NumSurfaces = 1; params.EnableDiffraction = true; params.EnableDiffractionOnBoundaryEdges = true; + CCP_LOG_CH( s_ch, "SetGeometry ID %llu: %u vertices, %u triangles", + geometryId, params.NumVertices, params.NumTriangles ); + // Register the geometry set with Wwise AKRESULT result = AK::SpatialAudio::SetGeometry( geometryId, params ); if( result != AK_Success ) { - CCP_LOGERR_CH( s_ch, "Failed to set geometry for ID %llu, error: %d", geometryId, result ); + CCP_LOGERR_CH( s_ch, "Failed to set geometry for ID %llu, AKRESULT: %d", geometryId, result ); return; } - CCP_LOG_CH( s_ch, "Registered geometry ID %llu with %zu vertices, %zu triangles", - geometryId, akVertices.size(), akTriangles.size() ); + CCP_LOG_CH( s_ch, "Registered geometry ID %llu successfully", geometryId ); // TODO: Create geometry instance to place it in the world // AkGeometryInstanceParams instanceParams; From 878f037348a9aa0b59eccf9a08b92661db1f0847 Mon Sep 17 00:00:00 2001 From: phevosccp <160875214+phevosccp@users.noreply.github.com> Date: Mon, 9 Feb 2026 10:00:01 +0000 Subject: [PATCH 06/48] uncomment and finalize RemoveGeometry, SetGeometryTransform methods --- src/AudGeometry.cpp | 42 +++++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/src/AudGeometry.cpp b/src/AudGeometry.cpp index 48d3717..529569f 100644 --- a/src/AudGeometry.cpp +++ b/src/AudGeometry.cpp @@ -137,28 +137,40 @@ void AudGeometry::SetGeometry( CCP_LOG_CH( s_ch, "Registered geometry ID %llu successfully", geometryId ); - // TODO: Create geometry instance to place it in the world - // AkGeometryInstanceParams instanceParams; - // instanceParams.GeometrySetID = geometryId; - // instanceParams.PositionAndOrientation = ConvertTransform( worldTransform ); - // instanceParams.RoomID = AK::SpatialAudio::kOutdoorRoomID; - // AK::SpatialAudio::SetGeometryInstance( geometryId, instanceParams ); + // Create geometry instance to place it in the world + AkGeometryInstanceParams instanceParams; + instanceParams.GeometrySetID = geometryId; + instanceParams.PositionAndOrientation = ConvertTransform( worldTransform ); + + AKRESULT instanceResult = AK::SpatialAudio::SetGeometryInstance( geometryId, instanceParams ); + if( instanceResult != AK_Success ) + { + CCP_LOGERR_CH( s_ch, "Failed to set geometry instance for ID %llu, AKRESULT: %d", geometryId, instanceResult ); + return; + } + + CCP_LOG_CH( s_ch, "Placed geometry instance ID %llu in world", geometryId ); } void AudGeometry::SetGeometryTransform( uint64_t geometryId, const Matrix& worldTransform ) { - // TODO: Update existing geometry instance's position/orientation - // AkGeometryInstanceParams instanceParams; - // instanceParams.GeometrySetID = geometryId; - // instanceParams.PositionAndOrientation = ConvertTransform( worldTransform ); - // instanceParams.RoomID = AK::SpatialAudio::kOutdoorRoomID; - // AK::SpatialAudio::SetGeometryInstance( geometryId, instanceParams ); + AkGeometryInstanceParams instanceParams; + instanceParams.GeometrySetID = geometryId; + instanceParams.PositionAndOrientation = ConvertTransform( worldTransform ); + + AKRESULT result = AK::SpatialAudio::SetGeometryInstance( geometryId, instanceParams ); + if( result != AK_Success ) + { + CCP_LOGERR_CH( s_ch, "Failed to update geometry instance transform for ID %llu, AKRESULT: %d", geometryId, result ); + } } void AudGeometry::RemoveGeometry( uint64_t geometryId ) { - // TODO: Remove instance first, then geometry set - // AK::SpatialAudio::RemoveGeometryInstance( geometryId ); - // AK::SpatialAudio::RemoveGeometry( geometryId ); + // Remove instance first, then geometry set + AK::SpatialAudio::RemoveGeometryInstance( geometryId ); + AK::SpatialAudio::RemoveGeometry( geometryId ); + + CCP_LOG_CH( s_ch, "Removed geometry and instance for ID %llu", geometryId ); } From c8cf4c60631f1360450740297b45f93b9fc0b8b6 Mon Sep 17 00:00:00 2001 From: phevosccp <160875214+phevosccp@users.noreply.github.com> Date: Mon, 9 Feb 2026 15:24:20 +0000 Subject: [PATCH 07/48] fix for IDs per mesh because of the use of pointers, no overlapping --- src/AudGeometry.cpp | 164 ++++++++++++++++++++++++++++---------------- src/AudGeometry.h | 20 +++++- 2 files changed, 123 insertions(+), 61 deletions(-) diff --git a/src/AudGeometry.cpp b/src/AudGeometry.cpp index 529569f..8147144 100644 --- a/src/AudGeometry.cpp +++ b/src/AudGeometry.cpp @@ -47,17 +47,17 @@ namespace { AkTransform transform; - // Extract position from matrix + // Extract position from matrix AkVector position; position.X = matrix._41; position.Y = matrix._42; - position.Z = -matrix._43; + position.Z = -matrix._43; - // Extract forward + // Extract forward AkVector orientationFront; orientationFront.X = matrix._31; orientationFront.Y = matrix._32; - orientationFront.Z = -matrix._33; + orientationFront.Z = -matrix._33; AkVector orientationTop; orientationTop.X = matrix._21; @@ -70,25 +70,37 @@ namespace } } -AudGeometry::AudGeometry( IRoot* lockobj ) {} +AudGeometry::AudGeometry( IRoot* lockobj ) + : m_mutex( "AudGeometry", "m_mutex" ) +{} -AudGeometry::~AudGeometry() {} +AudGeometry::~AudGeometry() +{ + for( const auto& pair : m_geometrySetRefCounts ) + { + AK::SpatialAudio::RemoveGeometry( pair.first ); + CCP_LOGWARN_CH( s_ch, "Cleanup: removed orphaned geometry set %llu with %u remaining refs", + pair.first, pair.second ); + } + m_geometrySetRefCounts.clear(); +} void AudGeometry::SetGeometry( - uint64_t geometryId, + uint64_t geometrySetId, + uint64_t instanceId, const Tr2AudGeometryData& geometryData, const Matrix& worldTransform ) { if( geometryData.m_vertices.empty() || geometryData.m_indices.empty() ) { - CCP_LOGWARN_CH( s_ch, "SetGeometry called with empty geometry data for ID %llu", geometryId ); + CCP_LOGWARN_CH( s_ch, "SetGeometry called with empty geometry data for instance %llu", instanceId ); return; } if( geometryData.m_indices.size() % 3 != 0 ) { - CCP_LOGERR_CH( s_ch, "SetGeometry: index count %zu is not a multiple of 3 for ID %llu", - geometryData.m_indices.size(), geometryId ); + CCP_LOGERR_CH( s_ch, "SetGeometry: index count %zu is not a multiple of 3 for instance %llu", + geometryData.m_indices.size(), instanceId ); return; } @@ -97,80 +109,116 @@ void AudGeometry::SetGeometry( { if( geometryData.m_indices[i] >= geometryData.m_vertices.size() ) { - CCP_LOGERR_CH( s_ch, "SetGeometry: index[%zu] = %u is out of bounds (vertex count: %zu) for ID %llu", - i, geometryData.m_indices[i], geometryData.m_vertices.size(), geometryId ); + CCP_LOGERR_CH( s_ch, "SetGeometry: index[%zu] = %u is out of bounds (vertex count: %zu) for instance %llu", + i, geometryData.m_indices[i], geometryData.m_vertices.size(), instanceId ); return; } } - // Convert vertices and triangles to Wwise format - std::vector akVertices = ConvertVertices( geometryData.m_vertices ); - std::vector akTriangles = ConvertTriangles( geometryData.m_indices ); - - // Default acoustic surface - all triangles share the same surface properties - AkAcousticSurface surface; - surface.strName = "default"; - surface.textureID = AK_INVALID_UNIQUE_ID; - surface.transmissionLoss = 1.0f; - - // Set up geometry parameters - AkGeometryParams params; - params.Vertices = akVertices.data(); - params.NumVertices = static_cast( akVertices.size() ); - params.Triangles = akTriangles.data(); - params.NumTriangles = static_cast( akTriangles.size() ); - params.Surfaces = &surface; - params.NumSurfaces = 1; - params.EnableDiffraction = true; - params.EnableDiffractionOnBoundaryEdges = true; - - CCP_LOG_CH( s_ch, "SetGeometry ID %llu: %u vertices, %u triangles", - geometryId, params.NumVertices, params.NumTriangles ); - - // Register the geometry set with Wwise - AKRESULT result = AK::SpatialAudio::SetGeometry( geometryId, params ); - if( result != AK_Success ) + CcpAutoMutex lock( m_mutex ); + + // Only register the geometry set with Wwise if this is the first instance using it + auto it = m_geometrySetRefCounts.find( geometrySetId ); + if( it == m_geometrySetRefCounts.end() ) { - CCP_LOGERR_CH( s_ch, "Failed to set geometry for ID %llu, AKRESULT: %d", geometryId, result ); - return; - } + // Convert vertices and triangles to Wwise format + std::vector akVertices = ConvertVertices( geometryData.m_vertices ); + std::vector akTriangles = ConvertTriangles( geometryData.m_indices ); + + // Default acoustic surface - all triangles share the same surface properties + AkAcousticSurface surface; + surface.strName = "default"; + surface.textureID = AK_INVALID_UNIQUE_ID; + surface.transmissionLoss = 1.0f; + + // Set up geometry parameters + AkGeometryParams params; + params.Vertices = akVertices.data(); + params.NumVertices = static_cast( akVertices.size() ); + params.Triangles = akTriangles.data(); + params.NumTriangles = static_cast( akTriangles.size() ); + params.Surfaces = &surface; + params.NumSurfaces = 1; + params.EnableDiffraction = true; + params.EnableDiffractionOnBoundaryEdges = true; + + CCP_LOG_CH( s_ch, "Registering geometry set %llu: %u vertices, %u triangles", + geometrySetId, params.NumVertices, params.NumTriangles ); + + AKRESULT result = AK::SpatialAudio::SetGeometry( geometrySetId, params ); + if( result != AK_Success ) + { + CCP_LOGERR_CH( s_ch, "Failed to set geometry for set %llu, AKRESULT: %d", geometrySetId, result ); + return; + } - CCP_LOG_CH( s_ch, "Registered geometry ID %llu successfully", geometryId ); + m_geometrySetRefCounts[geometrySetId] = 1; + } + else + { + it->second++; + CCP_LOG_CH( s_ch, "Geometry set %llu ref count incremented to %u", geometrySetId, it->second ); + } - // Create geometry instance to place it in the world + // Create a geometry instance for this specific object AkGeometryInstanceParams instanceParams; - instanceParams.GeometrySetID = geometryId; + instanceParams.GeometrySetID = geometrySetId; instanceParams.PositionAndOrientation = ConvertTransform( worldTransform ); - AKRESULT instanceResult = AK::SpatialAudio::SetGeometryInstance( geometryId, instanceParams ); + AKRESULT instanceResult = AK::SpatialAudio::SetGeometryInstance( instanceId, instanceParams ); if( instanceResult != AK_Success ) { - CCP_LOGERR_CH( s_ch, "Failed to set geometry instance for ID %llu, AKRESULT: %d", geometryId, instanceResult ); + CCP_LOGERR_CH( s_ch, "Failed to set geometry instance %llu (set %llu), AKRESULT: %d", + instanceId, geometrySetId, instanceResult ); return; } - CCP_LOG_CH( s_ch, "Placed geometry instance ID %llu in world", geometryId ); + CCP_LOG_CH( s_ch, "Placed geometry instance %llu referencing set %llu", instanceId, geometrySetId ); } -void AudGeometry::SetGeometryTransform( uint64_t geometryId, const Matrix& worldTransform ) +void AudGeometry::SetGeometryTransform( + uint64_t geometrySetId, + uint64_t instanceId, + const Matrix& worldTransform ) { AkGeometryInstanceParams instanceParams; - instanceParams.GeometrySetID = geometryId; + instanceParams.GeometrySetID = geometrySetId; instanceParams.PositionAndOrientation = ConvertTransform( worldTransform ); - AKRESULT result = AK::SpatialAudio::SetGeometryInstance( geometryId, instanceParams ); + AKRESULT result = AK::SpatialAudio::SetGeometryInstance( instanceId, instanceParams ); if( result != AK_Success ) { - CCP_LOGERR_CH( s_ch, "Failed to update geometry instance transform for ID %llu, AKRESULT: %d", geometryId, result ); + CCP_LOGERR_CH( s_ch, "Failed to update geometry instance transform for instance %llu (set %llu), AKRESULT: %d", + instanceId, geometrySetId, result ); } } -void AudGeometry::RemoveGeometry( uint64_t geometryId ) +void AudGeometry::RemoveGeometry( + uint64_t geometrySetId, + uint64_t instanceId ) { - // Remove instance first, then geometry set - AK::SpatialAudio::RemoveGeometryInstance( geometryId ); - AK::SpatialAudio::RemoveGeometry( geometryId ); + AK::SpatialAudio::RemoveGeometryInstance( instanceId ); + CCP_LOG_CH( s_ch, "Removed geometry instance %llu", instanceId ); - CCP_LOG_CH( s_ch, "Removed geometry and instance for ID %llu", geometryId ); -} + CcpAutoMutex lock( m_mutex ); + auto it = m_geometrySetRefCounts.find( geometrySetId ); + if( it != m_geometrySetRefCounts.end() ) + { + it->second--; + if( it->second == 0 ) + { + AK::SpatialAudio::RemoveGeometry( geometrySetId ); + m_geometrySetRefCounts.erase( it ); + CCP_LOG_CH( s_ch, "Removed geometry set %llu (last instance removed)", geometrySetId ); + } + else + { + CCP_LOG_CH( s_ch, "Geometry set %llu ref count decremented to %u", geometrySetId, it->second ); + } + } + else + { + CCP_LOGWARN_CH( s_ch, "RemoveGeometry called for unknown geometry set %llu", geometrySetId ); + } +} diff --git a/src/AudGeometry.h b/src/AudGeometry.h index 07356f4..f3be341 100644 --- a/src/AudGeometry.h +++ b/src/AudGeometry.h @@ -11,6 +11,7 @@ #include #include +#include struct Vector3; @@ -29,12 +30,25 @@ BLUE_CLASS(AudGeometry) : // ITr2AudGeometry interface void SetGeometry( - uint64_t geometryId, + uint64_t geometrySetId, + uint64_t instanceId, const Tr2AudGeometryData& geometryData, const Matrix& worldTransform) override; - void SetGeometryTransform(uint64_t geometryId, const Matrix& worldTransform) override; - void RemoveGeometry(uint64_t geometryId) override; + void SetGeometryTransform( + uint64_t geometrySetId, + uint64_t instanceId, + const Matrix& worldTransform) override; + + void RemoveGeometry( + uint64_t geometrySetId, + uint64_t instanceId) override; + +private: + // Tracks how many active instances reference each geometry set + std::unordered_map m_geometrySetRefCounts; + + CcpMutex m_mutex; }; TYPEDEF_BLUECLASS( AudGeometry ); From 93864a7f8a57086322a7cd3abd35a5b29eb7fa09 Mon Sep 17 00:00:00 2001 From: phevosccp <160875214+phevosccp@users.noreply.github.com> Date: Tue, 10 Feb 2026 15:05:46 +0000 Subject: [PATCH 08/48] fix for geometry instance indexing --- src/AudGeometry.cpp | 68 ++++++++++++++++++++++++++++++--------------- src/AudGeometry.h | 7 +++-- 2 files changed, 49 insertions(+), 26 deletions(-) diff --git a/src/AudGeometry.cpp b/src/AudGeometry.cpp index 8147144..45462ad 100644 --- a/src/AudGeometry.cpp +++ b/src/AudGeometry.cpp @@ -2,13 +2,14 @@ #include "AudGeometry.h" #include "Vector3.h" #include "AudManager.h" +#include static CcpLogChannel_t s_ch = CCP_LOG_DEFINE_CHANNEL( "AudGeometry" ); namespace { // Convert Vector3 array to AkVertex array - // Applies RH to LH coordinate conversion (negate Z) + // Applies RH to LH coordinate conversion std::vector ConvertVertices( const std::vector& vertices ) { std::vector akVertices( vertices.size() ); @@ -30,12 +31,12 @@ namespace std::vector akTriangles( numTriangles ); for( size_t i = 0; i < numTriangles; ++i ) { - // Reverse winding order (0,1,2 -> 0,2,1) for RH to LH conversion + akTriangles[i] = AkTriangle( static_cast( indices[i * 3 + 0] ), static_cast( indices[i * 3 + 2] ), static_cast( indices[i * 3 + 1] ), - 0 // default surface index + 0 ); } return akTriangles; @@ -70,19 +71,16 @@ namespace } } +std::unordered_map AudGeometry::s_geometrySetRefCounts; +CcpMutex AudGeometry::s_mutex( "AudGeometry", "s_mutex" ); + AudGeometry::AudGeometry( IRoot* lockobj ) - : m_mutex( "AudGeometry", "m_mutex" ) {} AudGeometry::~AudGeometry() { - for( const auto& pair : m_geometrySetRefCounts ) - { - AK::SpatialAudio::RemoveGeometry( pair.first ); - CCP_LOGWARN_CH( s_ch, "Cleanup: removed orphaned geometry set %llu with %u remaining refs", - pair.first, pair.second ); - } - m_geometrySetRefCounts.clear(); + // Ref counts are static/shared - individual instances must not clear global state. + // Geometry sets are cleaned up via RemoveGeometry() as each space object is destroyed. } void AudGeometry::SetGeometry( @@ -104,7 +102,25 @@ void AudGeometry::SetGeometry( return; } - // Validate that all triangle indices are within vertex bounds + // Wwise uses AkVertIdx (uint16) and AkTriIdx (uint16), max 65535 each + constexpr size_t kMaxVertices = std::numeric_limits::max(); + constexpr size_t kMaxTriangles = std::numeric_limits::max(); + + if( geometryData.m_vertices.size() > kMaxVertices ) + { + CCP_LOGERR_CH( s_ch, "SetGeometry: vertex count %zu exceeds Wwise max %zu (AkVertIdx is uint16) for geometry set %llu, skipping", + geometryData.m_vertices.size(), kMaxVertices, geometrySetId ); + return; + } + + size_t numTriangles = geometryData.m_indices.size() / 3; + if( numTriangles > kMaxTriangles ) + { + CCP_LOGERR_CH( s_ch, "SetGeometry: triangle count %zu exceeds Wwise max %zu (AkTriIdx is uint16) for geometry set %llu, skipping", + numTriangles, kMaxTriangles, geometrySetId ); + return; + } + for( size_t i = 0; i < geometryData.m_indices.size(); ++i ) { if( geometryData.m_indices[i] >= geometryData.m_vertices.size() ) @@ -115,17 +131,16 @@ void AudGeometry::SetGeometry( } } - CcpAutoMutex lock( m_mutex ); + CcpAutoMutex lock( s_mutex ); // Only register the geometry set with Wwise if this is the first instance using it - auto it = m_geometrySetRefCounts.find( geometrySetId ); - if( it == m_geometrySetRefCounts.end() ) + auto it = s_geometrySetRefCounts.find( geometrySetId ); + if( it == s_geometrySetRefCounts.end() ) { - // Convert vertices and triangles to Wwise format std::vector akVertices = ConvertVertices( geometryData.m_vertices ); std::vector akTriangles = ConvertTriangles( geometryData.m_indices ); - // Default acoustic surface - all triangles share the same surface properties + // Default acoustic surface AkAcousticSurface surface; surface.strName = "default"; surface.textureID = AK_INVALID_UNIQUE_ID; @@ -152,7 +167,7 @@ void AudGeometry::SetGeometry( return; } - m_geometrySetRefCounts[geometrySetId] = 1; + s_geometrySetRefCounts[geometrySetId] = 1; } else { @@ -160,7 +175,6 @@ void AudGeometry::SetGeometry( CCP_LOG_CH( s_ch, "Geometry set %llu ref count incremented to %u", geometrySetId, it->second ); } - // Create a geometry instance for this specific object AkGeometryInstanceParams instanceParams; instanceParams.GeometrySetID = geometrySetId; instanceParams.PositionAndOrientation = ConvertTransform( worldTransform ); @@ -181,6 +195,14 @@ void AudGeometry::SetGeometryTransform( uint64_t instanceId, const Matrix& worldTransform ) { + { + CcpAutoMutex lock( s_mutex ); + if( s_geometrySetRefCounts.find( geometrySetId ) == s_geometrySetRefCounts.end() ) + { + return; + } + } + AkGeometryInstanceParams instanceParams; instanceParams.GeometrySetID = geometrySetId; instanceParams.PositionAndOrientation = ConvertTransform( worldTransform ); @@ -200,16 +222,16 @@ void AudGeometry::RemoveGeometry( AK::SpatialAudio::RemoveGeometryInstance( instanceId ); CCP_LOG_CH( s_ch, "Removed geometry instance %llu", instanceId ); - CcpAutoMutex lock( m_mutex ); + CcpAutoMutex lock( s_mutex ); - auto it = m_geometrySetRefCounts.find( geometrySetId ); - if( it != m_geometrySetRefCounts.end() ) + auto it = s_geometrySetRefCounts.find( geometrySetId ); + if( it != s_geometrySetRefCounts.end() ) { it->second--; if( it->second == 0 ) { AK::SpatialAudio::RemoveGeometry( geometrySetId ); - m_geometrySetRefCounts.erase( it ); + s_geometrySetRefCounts.erase( it ); CCP_LOG_CH( s_ch, "Removed geometry set %llu (last instance removed)", geometrySetId ); } else diff --git a/src/AudGeometry.h b/src/AudGeometry.h index f3be341..2a21c39 100644 --- a/src/AudGeometry.h +++ b/src/AudGeometry.h @@ -46,9 +46,10 @@ BLUE_CLASS(AudGeometry) : private: // Tracks how many active instances reference each geometry set - std::unordered_map m_geometrySetRefCounts; - - CcpMutex m_mutex; + // Static: shared across all AudGeometry instances because multiple EveSpaceObject2s + // each own their own AudGeometry but share the same Wwise geometry sets by ID + static std::unordered_map s_geometrySetRefCounts; + static CcpMutex s_mutex; }; TYPEDEF_BLUECLASS( AudGeometry ); From a73565894d1496dc435f7a7c906ed294115b733b Mon Sep 17 00:00:00 2001 From: phevosccp <160875214+phevosccp@users.noreply.github.com> Date: Wed, 11 Feb 2026 11:17:03 +0000 Subject: [PATCH 09/48] add check for degenerate triangles --- src/AudGeometry.cpp | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/src/AudGeometry.cpp b/src/AudGeometry.cpp index 45462ad..114e08f 100644 --- a/src/AudGeometry.cpp +++ b/src/AudGeometry.cpp @@ -42,6 +42,23 @@ namespace return akTriangles; } + // Check if a triangle is degenerate + bool IsTriangleDegenerate( const AkVertex& a, const AkVertex& b, const AkVertex& c ) + { + constexpr float kEpsilonSq = 1e-12f; + + // Edge vectors + float e1x = b.X - a.X, e1y = b.Y - a.Y, e1z = b.Z - a.Z; + float e2x = c.X - a.X, e2y = c.Y - a.Y, e2z = c.Z - a.Z; + + // Cross product + float cx = e1y * e2z - e1z * e2y; + float cy = e1z * e2x - e1x * e2z; + float cz = e1x * e2y - e1y * e2x; + + return ( cx * cx + cy * cy + cz * cz ) <= kEpsilonSq; + } + // Convert Matrix to AkTransform for geometry instance placement // Applies RH to LH coordinate conversion AkTransform ConvertTransform( const Matrix& matrix ) @@ -138,7 +155,21 @@ void AudGeometry::SetGeometry( if( it == s_geometrySetRefCounts.end() ) { std::vector akVertices = ConvertVertices( geometryData.m_vertices ); - std::vector akTriangles = ConvertTriangles( geometryData.m_indices ); + std::vector allTriangles = ConvertTriangles( geometryData.m_indices ); + + // Skip degenerate triangles + std::vector akTriangles; + akTriangles.reserve( allTriangles.size() ); + size_t degenerateCount = 0; + for( const AkTriangle& tri : allTriangles ) + { + if( IsTriangleDegenerate( akVertices[tri.point0], akVertices[tri.point1], akVertices[tri.point2] ) ) + { + ++degenerateCount; + continue; + } + akTriangles.push_back( tri ); + } // Default acoustic surface AkAcousticSurface surface; From 9c69ddfadb67c511bf0d28daca83cb34034794a7 Mon Sep 17 00:00:00 2001 From: phevosccp <160875214+phevosccp@users.noreply.github.com> Date: Thu, 12 Feb 2026 14:09:45 +0000 Subject: [PATCH 10/48] disable EnableDiffractionOnBoundaryEdges because its causing wwise to not register complex geometries above a certain vertex count. plus some clean up of debug code --- src/AudGeometry.cpp | 57 ++++++--------------------------------------- 1 file changed, 7 insertions(+), 50 deletions(-) diff --git a/src/AudGeometry.cpp b/src/AudGeometry.cpp index 114e08f..a3e02dd 100644 --- a/src/AudGeometry.cpp +++ b/src/AudGeometry.cpp @@ -8,30 +8,23 @@ static CcpLogChannel_t s_ch = CCP_LOG_DEFINE_CHANNEL( "AudGeometry" ); namespace { - // Convert Vector3 array to AkVertex array - // Applies RH to LH coordinate conversion std::vector ConvertVertices( const std::vector& vertices ) { std::vector akVertices( vertices.size() ); for( size_t i = 0; i < vertices.size(); ++i ) { const Vector3& v = vertices[i]; - // RH to LH: negate Z akVertices[i] = AkVertex( v.x, v.y, -v.z ); } return akVertices; } - // Convert index array to AkTriangle array - // Every 3 indices form one triangle - // Winding order is reversed due to coordinate system change std::vector ConvertTriangles( const std::vector& indices ) { size_t numTriangles = indices.size() / 3; std::vector akTriangles( numTriangles ); for( size_t i = 0; i < numTriangles; ++i ) { - akTriangles[i] = AkTriangle( static_cast( indices[i * 3 + 0] ), static_cast( indices[i * 3 + 2] ), @@ -42,16 +35,13 @@ namespace return akTriangles; } - // Check if a triangle is degenerate bool IsTriangleDegenerate( const AkVertex& a, const AkVertex& b, const AkVertex& c ) { constexpr float kEpsilonSq = 1e-12f; - // Edge vectors float e1x = b.X - a.X, e1y = b.Y - a.Y, e1z = b.Z - a.Z; float e2x = c.X - a.X, e2y = c.Y - a.Y, e2z = c.Z - a.Z; - // Cross product float cx = e1y * e2z - e1z * e2y; float cy = e1z * e2x - e1x * e2z; float cz = e1x * e2y - e1y * e2x; @@ -59,19 +49,15 @@ namespace return ( cx * cx + cy * cy + cz * cz ) <= kEpsilonSq; } - // Convert Matrix to AkTransform for geometry instance placement - // Applies RH to LH coordinate conversion AkTransform ConvertTransform( const Matrix& matrix ) { AkTransform transform; - // Extract position from matrix AkVector position; position.X = matrix._41; position.Y = matrix._42; position.Z = -matrix._43; - // Extract forward AkVector orientationFront; orientationFront.X = matrix._31; orientationFront.Y = matrix._32; @@ -95,10 +81,7 @@ AudGeometry::AudGeometry( IRoot* lockobj ) {} AudGeometry::~AudGeometry() -{ - // Ref counts are static/shared - individual instances must not clear global state. - // Geometry sets are cleaned up via RemoveGeometry() as each space object is destroyed. -} +{} void AudGeometry::SetGeometry( uint64_t geometrySetId, @@ -108,7 +91,6 @@ void AudGeometry::SetGeometry( { if( geometryData.m_vertices.empty() || geometryData.m_indices.empty() ) { - CCP_LOGWARN_CH( s_ch, "SetGeometry called with empty geometry data for instance %llu", instanceId ); return; } @@ -119,13 +101,12 @@ void AudGeometry::SetGeometry( return; } - // Wwise uses AkVertIdx (uint16) and AkTriIdx (uint16), max 65535 each constexpr size_t kMaxVertices = std::numeric_limits::max(); constexpr size_t kMaxTriangles = std::numeric_limits::max(); if( geometryData.m_vertices.size() > kMaxVertices ) { - CCP_LOGERR_CH( s_ch, "SetGeometry: vertex count %zu exceeds Wwise max %zu (AkVertIdx is uint16) for geometry set %llu, skipping", + CCP_LOGERR_CH( s_ch, "SetGeometry: vertex count %zu exceeds max %zu for geometry set %llu, skipping", geometryData.m_vertices.size(), kMaxVertices, geometrySetId ); return; } @@ -133,7 +114,7 @@ void AudGeometry::SetGeometry( size_t numTriangles = geometryData.m_indices.size() / 3; if( numTriangles > kMaxTriangles ) { - CCP_LOGERR_CH( s_ch, "SetGeometry: triangle count %zu exceeds Wwise max %zu (AkTriIdx is uint16) for geometry set %llu, skipping", + CCP_LOGERR_CH( s_ch, "SetGeometry: triangle count %zu exceeds max %zu for geometry set %llu, skipping", numTriangles, kMaxTriangles, geometrySetId ); return; } @@ -142,7 +123,7 @@ void AudGeometry::SetGeometry( { if( geometryData.m_indices[i] >= geometryData.m_vertices.size() ) { - CCP_LOGERR_CH( s_ch, "SetGeometry: index[%zu] = %u is out of bounds (vertex count: %zu) for instance %llu", + CCP_LOGERR_CH( s_ch, "SetGeometry: index[%zu] = %u out of bounds (vertex count: %zu) for instance %llu", i, geometryData.m_indices[i], geometryData.m_vertices.size(), instanceId ); return; } @@ -150,34 +131,27 @@ void AudGeometry::SetGeometry( CcpAutoMutex lock( s_mutex ); - // Only register the geometry set with Wwise if this is the first instance using it auto it = s_geometrySetRefCounts.find( geometrySetId ); if( it == s_geometrySetRefCounts.end() ) { std::vector akVertices = ConvertVertices( geometryData.m_vertices ); std::vector allTriangles = ConvertTriangles( geometryData.m_indices ); - // Skip degenerate triangles std::vector akTriangles; akTriangles.reserve( allTriangles.size() ); - size_t degenerateCount = 0; for( const AkTriangle& tri : allTriangles ) { - if( IsTriangleDegenerate( akVertices[tri.point0], akVertices[tri.point1], akVertices[tri.point2] ) ) + if( !IsTriangleDegenerate( akVertices[tri.point0], akVertices[tri.point1], akVertices[tri.point2] ) ) { - ++degenerateCount; - continue; + akTriangles.push_back( tri ); } - akTriangles.push_back( tri ); } - // Default acoustic surface AkAcousticSurface surface; surface.strName = "default"; surface.textureID = AK_INVALID_UNIQUE_ID; surface.transmissionLoss = 1.0f; - // Set up geometry parameters AkGeometryParams params; params.Vertices = akVertices.data(); params.NumVertices = static_cast( akVertices.size() ); @@ -186,10 +160,7 @@ void AudGeometry::SetGeometry( params.Surfaces = &surface; params.NumSurfaces = 1; params.EnableDiffraction = true; - params.EnableDiffractionOnBoundaryEdges = true; - - CCP_LOG_CH( s_ch, "Registering geometry set %llu: %u vertices, %u triangles", - geometrySetId, params.NumVertices, params.NumTriangles ); + params.EnableDiffractionOnBoundaryEdges = false; AKRESULT result = AK::SpatialAudio::SetGeometry( geometrySetId, params ); if( result != AK_Success ) @@ -203,7 +174,6 @@ void AudGeometry::SetGeometry( else { it->second++; - CCP_LOG_CH( s_ch, "Geometry set %llu ref count incremented to %u", geometrySetId, it->second ); } AkGeometryInstanceParams instanceParams; @@ -215,10 +185,7 @@ void AudGeometry::SetGeometry( { CCP_LOGERR_CH( s_ch, "Failed to set geometry instance %llu (set %llu), AKRESULT: %d", instanceId, geometrySetId, instanceResult ); - return; } - - CCP_LOG_CH( s_ch, "Placed geometry instance %llu referencing set %llu", instanceId, geometrySetId ); } void AudGeometry::SetGeometryTransform( @@ -251,7 +218,6 @@ void AudGeometry::RemoveGeometry( uint64_t instanceId ) { AK::SpatialAudio::RemoveGeometryInstance( instanceId ); - CCP_LOG_CH( s_ch, "Removed geometry instance %llu", instanceId ); CcpAutoMutex lock( s_mutex ); @@ -263,15 +229,6 @@ void AudGeometry::RemoveGeometry( { AK::SpatialAudio::RemoveGeometry( geometrySetId ); s_geometrySetRefCounts.erase( it ); - CCP_LOG_CH( s_ch, "Removed geometry set %llu (last instance removed)", geometrySetId ); - } - else - { - CCP_LOG_CH( s_ch, "Geometry set %llu ref count decremented to %u", geometrySetId, it->second ); } } - else - { - CCP_LOGWARN_CH( s_ch, "RemoveGeometry called for unknown geometry set %llu", geometrySetId ); - } } From ecbac378d797771a2ab72d6d29a8852f305b8e33 Mon Sep 17 00:00:00 2001 From: phevosccp <160875214+phevosccp@users.noreply.github.com> Date: Wed, 18 Feb 2026 12:18:00 +0000 Subject: [PATCH 11/48] AudGeometry transform conversion by validating orientation vectors and sending extracted world-matrix scale to Wwise geometry instances. --- src/AudGeometry.cpp | 146 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 127 insertions(+), 19 deletions(-) diff --git a/src/AudGeometry.cpp b/src/AudGeometry.cpp index a3e02dd..ab92d1a 100644 --- a/src/AudGeometry.cpp +++ b/src/AudGeometry.cpp @@ -2,12 +2,82 @@ #include "AudGeometry.h" #include "Vector3.h" #include "AudManager.h" +#include #include static CcpLogChannel_t s_ch = CCP_LOG_DEFINE_CHANNEL( "AudGeometry" ); namespace { + AkVector MakeAkVector( float x, float y, float z ) + { + AkVector v; + v.X = x; + v.Y = y; + v.Z = z; + return v; + } + + bool IsFiniteAkVector( const AkVector& v ) + { + return std::isfinite( v.X ) && std::isfinite( v.Y ) && std::isfinite( v.Z ); + } + + float Dot( const AkVector& a, const AkVector& b ) + { + return a.X * b.X + a.Y * b.Y + a.Z * b.Z; + } + + float LengthSq( const AkVector& v ) + { + return Dot( v, v ); + } + + AkVector Cross( const AkVector& a, const AkVector& b ) + { + return MakeAkVector( + a.Y * b.Z - a.Z * b.Y, + a.Z * b.X - a.X * b.Z, + a.X * b.Y - a.Y * b.X + ); + } + + bool NormalizeSafe( AkVector& v ) + { + const float lenSq = LengthSq( v ); + if( lenSq <= 1e-10f || !std::isfinite( lenSq ) ) + { + return false; + } + + const float invLen = 1.0f / std::sqrt( lenSq ); + v.X *= invLen; + v.Y *= invLen; + v.Z *= invLen; + return IsFiniteAkVector( v ); + } + + float AxisScaleOrDefault( float x, float y, float z ) + { + const float lenSq = x * x + y * y + z * z; + if( lenSq <= 1e-10f || !std::isfinite( lenSq ) ) + { + return 1.0f; + } + + const float len = std::sqrt( lenSq ); + return std::isfinite( len ) ? len : 1.0f; + } + + AkVector ExtractScale( const Matrix& matrix ) + { + return MakeAkVector( + AxisScaleOrDefault( matrix._11, matrix._12, matrix._13 ), + AxisScaleOrDefault( matrix._21, matrix._22, matrix._23 ), + AxisScaleOrDefault( matrix._31, matrix._32, matrix._33 ) + ); + } + std::vector ConvertVertices( const std::vector& vertices ) { std::vector akVertices( vertices.size() ); @@ -49,28 +119,60 @@ namespace return ( cx * cx + cy * cy + cz * cz ) <= kEpsilonSq; } - AkTransform ConvertTransform( const Matrix& matrix ) + void ConvertTransform( const Matrix& matrix, AkTransform& transformOut ) { - AkTransform transform; + AkVector position = MakeAkVector( matrix._41, matrix._42, -matrix._43 ); + if( !IsFiniteAkVector( position ) ) + { + position = MakeAkVector( 0.0f, 0.0f, 0.0f ); + } + + AkVector front = MakeAkVector( matrix._31, matrix._32, -matrix._33 ); + AkVector up = MakeAkVector( matrix._21, matrix._22, -matrix._23 ); + + if( !IsFiniteAkVector( front ) || !IsFiniteAkVector( up ) ) + { + front = MakeAkVector( 0.0f, 0.0f, 1.0f ); + up = MakeAkVector( 0.0f, 1.0f, 0.0f ); + } + + if( !NormalizeSafe( front ) ) + { + front = MakeAkVector( 0.0f, 0.0f, 1.0f ); + } + + const float upDotFront = Dot( up, front ); + up.X -= front.X * upDotFront; + up.Y -= front.Y * upDotFront; + up.Z -= front.Z * upDotFront; - AkVector position; - position.X = matrix._41; - position.Y = matrix._42; - position.Z = -matrix._43; + if( !NormalizeSafe( up ) ) + { + AkVector reference = ( std::fabs( front.Y ) < 0.99f ) ? + MakeAkVector( 0.0f, 1.0f, 0.0f ) : + MakeAkVector( 1.0f, 0.0f, 0.0f ); + + AkVector right = Cross( reference, front ); + if( !NormalizeSafe( right ) ) + { + reference = MakeAkVector( 0.0f, 0.0f, 1.0f ); + right = Cross( reference, front ); + } - AkVector orientationFront; - orientationFront.X = matrix._31; - orientationFront.Y = matrix._32; - orientationFront.Z = -matrix._33; + if( !NormalizeSafe( right ) ) + { + right = MakeAkVector( 1.0f, 0.0f, 0.0f ); + } - AkVector orientationTop; - orientationTop.X = matrix._21; - orientationTop.Y = matrix._22; - orientationTop.Z = -matrix._23; + up = Cross( front, right ); + if( !NormalizeSafe( up ) ) + { + up = MakeAkVector( 0.0f, 1.0f, 0.0f ); + } + } - transform.SetPosition( position ); - transform.SetOrientation( orientationFront, orientationTop ); - return transform; + transformOut.SetPosition( position ); + transformOut.SetOrientation( front, up ); } } @@ -178,7 +280,10 @@ void AudGeometry::SetGeometry( AkGeometryInstanceParams instanceParams; instanceParams.GeometrySetID = geometrySetId; - instanceParams.PositionAndOrientation = ConvertTransform( worldTransform ); + AkTransform transform; + ConvertTransform( worldTransform, transform ); + instanceParams.PositionAndOrientation = transform; + instanceParams.Scale = ExtractScale( worldTransform ); AKRESULT instanceResult = AK::SpatialAudio::SetGeometryInstance( instanceId, instanceParams ); if( instanceResult != AK_Success ) @@ -203,7 +308,10 @@ void AudGeometry::SetGeometryTransform( AkGeometryInstanceParams instanceParams; instanceParams.GeometrySetID = geometrySetId; - instanceParams.PositionAndOrientation = ConvertTransform( worldTransform ); + AkTransform transform; + ConvertTransform( worldTransform, transform ); + instanceParams.PositionAndOrientation = transform; + instanceParams.Scale = ExtractScale( worldTransform ); AKRESULT result = AK::SpatialAudio::SetGeometryInstance( instanceId, instanceParams ); if( result != AK_Success ) From 90b494d8e4c402351b10b3fae1c7039539be021d Mon Sep 17 00:00:00 2001 From: phevosccp <160875214+phevosccp@users.noreply.github.com> Date: Fri, 20 Feb 2026 09:59:10 +0000 Subject: [PATCH 12/48] lower transmittion param to 0.7 --- src/AudGeometry.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AudGeometry.cpp b/src/AudGeometry.cpp index ab92d1a..d87ac8b 100644 --- a/src/AudGeometry.cpp +++ b/src/AudGeometry.cpp @@ -252,7 +252,7 @@ void AudGeometry::SetGeometry( AkAcousticSurface surface; surface.strName = "default"; surface.textureID = AK_INVALID_UNIQUE_ID; - surface.transmissionLoss = 1.0f; + surface.transmissionLoss = 0.7f; AkGeometryParams params; params.Vertices = akVertices.data(); From c50f1a929c74a5e826de6ee84ce5b259d77bec10 Mon Sep 17 00:00:00 2001 From: phevosccp <160875214+phevosccp@users.noreply.github.com> Date: Tue, 24 Feb 2026 09:50:20 +0000 Subject: [PATCH 13/48] Simplify conversion methods and move them to Utilities --- src/AudGeometry.cpp | 136 ++------------------------------------------ src/AudManager.cpp | 17 ++++-- src/Utilities.h | 45 ++++++++++++++- 3 files changed, 62 insertions(+), 136 deletions(-) diff --git a/src/AudGeometry.cpp b/src/AudGeometry.cpp index d87ac8b..fa85014 100644 --- a/src/AudGeometry.cpp +++ b/src/AudGeometry.cpp @@ -1,5 +1,6 @@ #include "stdafx.h" #include "AudGeometry.h" +#include "Utilities.h" #include "Vector3.h" #include "AudManager.h" #include @@ -9,75 +10,6 @@ static CcpLogChannel_t s_ch = CCP_LOG_DEFINE_CHANNEL( "AudGeometry" ); namespace { - AkVector MakeAkVector( float x, float y, float z ) - { - AkVector v; - v.X = x; - v.Y = y; - v.Z = z; - return v; - } - - bool IsFiniteAkVector( const AkVector& v ) - { - return std::isfinite( v.X ) && std::isfinite( v.Y ) && std::isfinite( v.Z ); - } - - float Dot( const AkVector& a, const AkVector& b ) - { - return a.X * b.X + a.Y * b.Y + a.Z * b.Z; - } - - float LengthSq( const AkVector& v ) - { - return Dot( v, v ); - } - - AkVector Cross( const AkVector& a, const AkVector& b ) - { - return MakeAkVector( - a.Y * b.Z - a.Z * b.Y, - a.Z * b.X - a.X * b.Z, - a.X * b.Y - a.Y * b.X - ); - } - - bool NormalizeSafe( AkVector& v ) - { - const float lenSq = LengthSq( v ); - if( lenSq <= 1e-10f || !std::isfinite( lenSq ) ) - { - return false; - } - - const float invLen = 1.0f / std::sqrt( lenSq ); - v.X *= invLen; - v.Y *= invLen; - v.Z *= invLen; - return IsFiniteAkVector( v ); - } - - float AxisScaleOrDefault( float x, float y, float z ) - { - const float lenSq = x * x + y * y + z * z; - if( lenSq <= 1e-10f || !std::isfinite( lenSq ) ) - { - return 1.0f; - } - - const float len = std::sqrt( lenSq ); - return std::isfinite( len ) ? len : 1.0f; - } - - AkVector ExtractScale( const Matrix& matrix ) - { - return MakeAkVector( - AxisScaleOrDefault( matrix._11, matrix._12, matrix._13 ), - AxisScaleOrDefault( matrix._21, matrix._22, matrix._23 ), - AxisScaleOrDefault( matrix._31, matrix._32, matrix._33 ) - ); - } - std::vector ConvertVertices( const std::vector& vertices ) { std::vector akVertices( vertices.size() ); @@ -97,8 +29,8 @@ namespace { akTriangles[i] = AkTriangle( static_cast( indices[i * 3 + 0] ), - static_cast( indices[i * 3 + 2] ), static_cast( indices[i * 3 + 1] ), + static_cast( indices[i * 3 + 2] ), 0 ); } @@ -118,62 +50,6 @@ namespace return ( cx * cx + cy * cy + cz * cz ) <= kEpsilonSq; } - - void ConvertTransform( const Matrix& matrix, AkTransform& transformOut ) - { - AkVector position = MakeAkVector( matrix._41, matrix._42, -matrix._43 ); - if( !IsFiniteAkVector( position ) ) - { - position = MakeAkVector( 0.0f, 0.0f, 0.0f ); - } - - AkVector front = MakeAkVector( matrix._31, matrix._32, -matrix._33 ); - AkVector up = MakeAkVector( matrix._21, matrix._22, -matrix._23 ); - - if( !IsFiniteAkVector( front ) || !IsFiniteAkVector( up ) ) - { - front = MakeAkVector( 0.0f, 0.0f, 1.0f ); - up = MakeAkVector( 0.0f, 1.0f, 0.0f ); - } - - if( !NormalizeSafe( front ) ) - { - front = MakeAkVector( 0.0f, 0.0f, 1.0f ); - } - - const float upDotFront = Dot( up, front ); - up.X -= front.X * upDotFront; - up.Y -= front.Y * upDotFront; - up.Z -= front.Z * upDotFront; - - if( !NormalizeSafe( up ) ) - { - AkVector reference = ( std::fabs( front.Y ) < 0.99f ) ? - MakeAkVector( 0.0f, 1.0f, 0.0f ) : - MakeAkVector( 1.0f, 0.0f, 0.0f ); - - AkVector right = Cross( reference, front ); - if( !NormalizeSafe( right ) ) - { - reference = MakeAkVector( 0.0f, 0.0f, 1.0f ); - right = Cross( reference, front ); - } - - if( !NormalizeSafe( right ) ) - { - right = MakeAkVector( 1.0f, 0.0f, 0.0f ); - } - - up = Cross( front, right ); - if( !NormalizeSafe( up ) ) - { - up = MakeAkVector( 0.0f, 1.0f, 0.0f ); - } - } - - transformOut.SetPosition( position ); - transformOut.SetOrientation( front, up ); - } } std::unordered_map AudGeometry::s_geometrySetRefCounts; @@ -281,9 +157,9 @@ void AudGeometry::SetGeometry( AkGeometryInstanceParams instanceParams; instanceParams.GeometrySetID = geometrySetId; AkTransform transform; - ConvertTransform( worldTransform, transform ); + RH2LH::convertTransform( worldTransform, transform ); instanceParams.PositionAndOrientation = transform; - instanceParams.Scale = ExtractScale( worldTransform ); + instanceParams.Scale = RH2LH::extractScale( worldTransform ); AKRESULT instanceResult = AK::SpatialAudio::SetGeometryInstance( instanceId, instanceParams ); if( instanceResult != AK_Success ) @@ -309,9 +185,9 @@ void AudGeometry::SetGeometryTransform( AkGeometryInstanceParams instanceParams; instanceParams.GeometrySetID = geometrySetId; AkTransform transform; - ConvertTransform( worldTransform, transform ); + RH2LH::convertTransform( worldTransform, transform ); instanceParams.PositionAndOrientation = transform; - instanceParams.Scale = ExtractScale( worldTransform ); + instanceParams.Scale = RH2LH::extractScale( worldTransform ); AKRESULT result = AK::SpatialAudio::SetGeometryInstance( instanceId, instanceParams ); if( result != AK_Success ) diff --git a/src/AudManager.cpp b/src/AudManager.cpp index 4f45212..58cb585 100644 --- a/src/AudManager.cpp +++ b/src/AudManager.cpp @@ -402,11 +402,18 @@ bool AudManager::InitSpatialAudioGeometry() { AkSpatialAudioInitSettings spatialSettings; - // Configure geometry-based audio processing - // spatialSettings.uMaxSoundPropagationDepth = 8; - // spatialSettings.uNumberOfPrimaryRays = 100; - // spatialSettings.uMaxDiffractionOrder = 8; - // spatialSettings.bEnableGeometricDiffractionAndTransmission = true; + spatialSettings.uNumberOfPrimaryRays = 8; + spatialSettings.fCPULimitPercentage = 0.0f; // disabled — fixed ray count is more predictable at low counts + spatialSettings.uLoadBalancingSpread = 8; // spread path validation over 8 frames + + spatialSettings.fMovementThreshold = 1.0f; + spatialSettings.uMaxDiffractionOrder = 1; + + spatialSettings.uMaxReflectionOrder = 0; + spatialSettings.uDiffractionOnReflectionsOrder = 0; + spatialSettings.fMaxPathLength = 1000.0f; + + spatialSettings.bEnableGeometricDiffractionAndTransmission = true; if( AK::SpatialAudio::Init( spatialSettings ) != AK_Success ) { diff --git a/src/Utilities.h b/src/Utilities.h index a066d6e..e344284 100644 --- a/src/Utilities.h +++ b/src/Utilities.h @@ -10,6 +10,8 @@ #ifndef _AUD_UTILITIES_H_ #define _AUD_UTILITIES_H_ +#include + // Inherit this privatly to inhibit copying class NoCopy { @@ -44,13 +46,54 @@ class RH2LH AkVector front = emitterLH->OrientationFront(); AkVector top = emitterLH->OrientationTop(); AkVector64 pos = emitterLH->Position(); - + pos.Z *= -1.f; front.Z *= -1.f; top.Z *= -1.f; emitterLH->Set(pos, front, top); } + + static void convertTransform( const Matrix& matrix, AkTransform& out ) + { + constexpr float kEpsilon = 1e-10f; + + Ak3DVector32 position( matrix._41, matrix._42, -matrix._43 ); + if( !position.IsFinite() ) + position = Ak3DVector32( 0.0f, 0.0f, 0.0f ); + + Ak3DVector32 front( matrix._31, matrix._32, -matrix._33 ); + if( !front.IsFinite() || front.LengthSquared() <= kEpsilon ) + front = Ak3DVector32( 0.0f, 0.0f, 1.0f ); + else + front.Normalize(); + + Ak3DVector32 up( matrix._21, matrix._22, -matrix._23 ); + if( !up.IsFinite() || up.LengthSquared() <= kEpsilon ) + up = Ak3DVector32( 0.0f, 1.0f, 0.0f ); + else + up.Normalize(); + + out.SetPosition( position ); + out.SetOrientation( front, up ); + } + + static AkVector extractScale( const Matrix& matrix ) + { + auto axisLength = []( float x, float y, float z ) -> float + { + const Ak3DVector32 axis( x, y, z ); + if( !axis.IsFinite() || axis.LengthSquared() <= 1e-10f ) + return 1.0f; + return axis.Length(); + }; + + AkVector scale; + scale.X = axisLength( matrix._11, matrix._12, matrix._13 ); + scale.Y = axisLength( matrix._21, matrix._22, matrix._23 ); + scale.Z = axisLength( matrix._31, matrix._32, matrix._33 ); + return scale; + } }; class StringUtils From a36b9d9d7b1f023cb12c0bdbec007f30ab3e0610 Mon Sep 17 00:00:00 2001 From: phevosccp <160875214+phevosccp@users.noreply.github.com> Date: Tue, 24 Feb 2026 12:50:33 +0000 Subject: [PATCH 14/48] Implement obstruction and occlusion in a new class and querry diffraction paths --- src/AudManager.cpp | 17 +++----- src/AudManager.h | 2 + src/AudObstruction.cpp | 89 ++++++++++++++++++++++++++++++++++++++++++ src/AudObstruction.h | 48 +++++++++++++++++++++++ 4 files changed, 145 insertions(+), 11 deletions(-) create mode 100644 src/AudObstruction.cpp create mode 100644 src/AudObstruction.h diff --git a/src/AudManager.cpp b/src/AudManager.cpp index 58cb585..fdebed1 100644 --- a/src/AudManager.cpp +++ b/src/AudManager.cpp @@ -77,12 +77,14 @@ AudManager::AudManager( IRoot* lockobj ) : { // Initialize sound prioritization system m_soundPrioritization = new SoundPrioritization(); + m_obstruction = new AudObstruction(); } AudManager::~AudManager() { // Clean up sound prioritization system delete m_soundPrioritization; + delete m_obstruction; if( g_audioInitialized ) { @@ -99,6 +101,10 @@ void AudManager::Process() m_soundPrioritization->CullAudio(); } + // Smooth obstruction values using Wwise's computed diffraction/transmission paths + m_obstruction->Update( LISTENER_GAME_OBJ_ID, + m_soundPrioritization->GetPrioritizedAudioObjects() ); + // Process bank requests, events, positions, RTPC, etc. AK::SoundEngine::RenderAudio(); @@ -402,17 +408,6 @@ bool AudManager::InitSpatialAudioGeometry() { AkSpatialAudioInitSettings spatialSettings; - spatialSettings.uNumberOfPrimaryRays = 8; - spatialSettings.fCPULimitPercentage = 0.0f; // disabled — fixed ray count is more predictable at low counts - spatialSettings.uLoadBalancingSpread = 8; // spread path validation over 8 frames - - spatialSettings.fMovementThreshold = 1.0f; - spatialSettings.uMaxDiffractionOrder = 1; - - spatialSettings.uMaxReflectionOrder = 0; - spatialSettings.uDiffractionOnReflectionsOrder = 0; - spatialSettings.fMaxPathLength = 1000.0f; - spatialSettings.bEnableGeometricDiffractionAndTransmission = true; if( AK::SpatialAudio::Init( spatialSettings ) != AK_Success ) diff --git a/src/AudManager.h b/src/AudManager.h index 4b4d1b0..2c014ef 100644 --- a/src/AudManager.h +++ b/src/AudManager.h @@ -13,6 +13,7 @@ #include "Audio2.h" #include "AudSettings.h" #include "AudListener.h" +#include "AudObstruction.h" #include "SoundPrioritization.h" #include "CCPFilePackageLowLevelIO.h" #include @@ -226,6 +227,7 @@ BLUE_CLASS( AudManager ) : CcpMutex m_moniteredParametersMapMutex; SoundPrioritization* m_soundPrioritization; + AudObstruction* m_obstruction; // A boolean for the state of the profiler capture bool m_isProfilerCapturing; diff --git a/src/AudObstruction.cpp b/src/AudObstruction.cpp new file mode 100644 index 0000000..6ed58a1 --- /dev/null +++ b/src/AudObstruction.cpp @@ -0,0 +1,89 @@ +#include "stdafx.h" +#include "AudObstruction.h" +#include "Audio2.h" +#include + +AudObstruction::AudObstruction() + : m_lastUpdateTime( std::chrono::steady_clock::now() ) +{ +} + +void AudObstruction::Update( + AkGameObjectID listenerID, + const std::vector& gameObjects ) +{ + if( !g_audioInitialized ) + return; + + // Delta time + auto now = std::chrono::steady_clock::now(); + float dt = std::chrono::duration( now - m_lastUpdateTime ).count(); + m_lastUpdateTime = now; + dt = std::min( dt, 0.1f ); // Clamp to avoid huge jumps after pauses + + // Exponential smoothing factor: 1 - e^(-rate * dt) + float alpha = 1.0f - std::exp( -kSmoothingRate * dt ); + + for( IPrioritizedObject* obj : gameObjects ) + { + AkGameObjectID emitterID = obj->GetID(); + + if( emitterID == listenerID ) + continue; + + if( obj->IsCulled() ) + { + m_smoothed.erase( emitterID ); + continue; + } + + // Query the diffraction/transmission paths that Wwise already computed. + AkDiffractionPathInfo paths[8]; + AkUInt32 numPaths = 8; + AkVector64 listenerPos, emitterPos; + + AKRESULT result = AK::SpatialAudio::QueryDiffractionPaths( + emitterID, 0, listenerPos, emitterPos, paths, numPaths ); + + float targetObstruction = 0.0f; + float targetOcclusion = 0.0f; + + if( result == AK_Success && numPaths > 0 ) + { + // Find the best (least-obstructed) values across all paths. + float bestDiffraction = 1.0f; + float bestTransmission = 1.0f; + + for( AkUInt32 i = 0; i < numPaths; ++i ) + { + if( paths[i].diffraction < bestDiffraction ) + bestDiffraction = paths[i].diffraction; + if( paths[i].transmissionLoss < bestTransmission ) + bestTransmission = paths[i].transmissionLoss; + } + + targetObstruction = bestDiffraction; + targetOcclusion = bestTransmission; + } + + // Smooth + auto it = m_smoothed.find( emitterID ); + if( it == m_smoothed.end() ) + { + // First frame for this emitter -- snap to target (no pop on spawn) + m_smoothed[emitterID] = { targetObstruction, targetOcclusion }; + } + else + { + it->second.obstruction += ( targetObstruction - it->second.obstruction ) * alpha; + it->second.occlusion += ( targetOcclusion - it->second.occlusion ) * alpha; + } + + SmoothedValues& sv = m_smoothed[emitterID]; + float obstruction = std::max( 0.0f, std::min( 1.0f, sv.obstruction ) ); + float occlusion = std::max( 0.0f, std::min( 1.0f, sv.occlusion ) ); + + AK::SoundEngine::SetObjectObstructionAndOcclusion( + emitterID, listenerID, obstruction, occlusion ); + } +} diff --git a/src/AudObstruction.h b/src/AudObstruction.h new file mode 100644 index 0000000..ca37ccd --- /dev/null +++ b/src/AudObstruction.h @@ -0,0 +1,48 @@ +//////////////////////////////////////////////////////////// +// +// Creator: Phevos Rinis +// Creation Date: Feb 2026 +// Copyright (c) 2026 CCP Games +// +// Smooth obstruction and occlusion using Wwise's own spatial +// audio raycasting. +// +// Each frame, queries QueryDiffractionPaths() to read the +// transmission/diffraction values that Wwise already computed +// from registered geometry, then exponentially smooths them +// over time and feeds the result to SetObjectObstructionAndOcclusion(). +// +// Obstruction (direct path only) is driven by diffraction. +// Occlusion (direct + reverb) is driven by transmissionLoss. +// The Wwise project curves decide how each one affects sound. +// + +#pragma once + +#include +#include +#include +#include "IPrioritizedObject.h" + +class AudObstruction +{ +public: + AudObstruction(); + + // Call once per frame from AudManager::Process(), after CullAudio() and before RenderAudio(). + void Update( AkGameObjectID listenerID, + const std::vector& gameObjects ); + +private: + struct SmoothedValues + { + float obstruction = 0.0f; + float occlusion = 0.0f; + }; + + std::unordered_map m_smoothed; + std::chrono::steady_clock::time_point m_lastUpdateTime; + + // Smoothing rate. Higher = faster convergence. 2.0 gives ~1 second transitions. + static constexpr float kSmoothingRate = 2.0f; +}; From c24eb7557c8e2a179a3f0e844d0850165768f080 Mon Sep 17 00:00:00 2001 From: phevosccp <160875214+phevosccp@users.noreply.github.com> Date: Tue, 24 Feb 2026 13:12:35 +0000 Subject: [PATCH 15/48] change interpolation from expo to linear also reduce the time which diffraction querries are updated for performance --- src/AudObstruction.cpp | 152 +++++++++++++++++++++++++++++------------ src/AudObstruction.h | 42 ++++++++++-- 2 files changed, 146 insertions(+), 48 deletions(-) diff --git a/src/AudObstruction.cpp b/src/AudObstruction.cpp index 6ed58a1..bcda18d 100644 --- a/src/AudObstruction.cpp +++ b/src/AudObstruction.cpp @@ -1,7 +1,8 @@ #include "stdafx.h" #include "AudObstruction.h" #include "Audio2.h" -#include +#include +#include AudObstruction::AudObstruction() : m_lastUpdateTime( std::chrono::steady_clock::now() ) @@ -20,9 +21,7 @@ void AudObstruction::Update( float dt = std::chrono::duration( now - m_lastUpdateTime ).count(); m_lastUpdateTime = now; dt = std::min( dt, 0.1f ); // Clamp to avoid huge jumps after pauses - - // Exponential smoothing factor: 1 - e^(-rate * dt) - float alpha = 1.0f - std::exp( -kSmoothingRate * dt ); + m_time += dt; for( IPrioritizedObject* obj : gameObjects ) { @@ -33,57 +32,126 @@ void AudObstruction::Update( if( obj->IsCulled() ) { - m_smoothed.erase( emitterID ); + m_emitters.erase( emitterID ); continue; } - // Query the diffraction/transmission paths that Wwise already computed. - AkDiffractionPathInfo paths[8]; - AkUInt32 numPaths = 8; - AkVector64 listenerPos, emitterPos; - - AKRESULT result = AK::SpatialAudio::QueryDiffractionPaths( - emitterID, 0, listenerPos, emitterPos, paths, numPaths ); + EmitterState& state = m_emitters[emitterID]; - float targetObstruction = 0.0f; - float targetOcclusion = 0.0f; - - if( result == AK_Success && numPaths > 0 ) + // First time seeing this emitter: query immediately and snap to result. + if( !state.initialized ) { - // Find the best (least-obstructed) values across all paths. - float bestDiffraction = 1.0f; - float bestTransmission = 1.0f; - - for( AkUInt32 i = 0; i < numPaths; ++i ) + QueryEmitter( emitterID, state ); + state.obstruction = state.targetObstruction; + state.occlusion = state.targetOcclusion; + // Stagger future queries so emitters don't all query on the same frame. + state.nextQueryTime = m_time + m_refreshInterval * ( (float)std::rand() / RAND_MAX ); + state.initialized = true; + } + else + { + // Throttled query: only re-query when the refresh interval has elapsed. + if( m_time >= state.nextQueryTime ) { - if( paths[i].diffraction < bestDiffraction ) - bestDiffraction = paths[i].diffraction; - if( paths[i].transmissionLoss < bestTransmission ) - bestTransmission = paths[i].transmissionLoss; + QueryEmitter( emitterID, state ); + state.nextQueryTime = m_time + m_refreshInterval; } - targetObstruction = bestDiffraction; - targetOcclusion = bestTransmission; - } + // Linear fade every frame regardless of query rate. + float fadeStep = m_fadeRate * dt; - // Smooth - auto it = m_smoothed.find( emitterID ); - if( it == m_smoothed.end() ) - { - // First frame for this emitter -- snap to target (no pop on spawn) - m_smoothed[emitterID] = { targetObstruction, targetOcclusion }; - } - else - { - it->second.obstruction += ( targetObstruction - it->second.obstruction ) * alpha; - it->second.occlusion += ( targetOcclusion - it->second.occlusion ) * alpha; + if( state.obstruction < state.targetObstruction ) + state.obstruction = std::min( state.obstruction + fadeStep, state.targetObstruction ); + else if( state.obstruction > state.targetObstruction ) + state.obstruction = std::max( state.obstruction - fadeStep, state.targetObstruction ); + + if( state.occlusion < state.targetOcclusion ) + state.occlusion = std::min( state.occlusion + fadeStep, state.targetOcclusion ); + else if( state.occlusion > state.targetOcclusion ) + state.occlusion = std::max( state.occlusion - fadeStep, state.targetOcclusion ); } - SmoothedValues& sv = m_smoothed[emitterID]; - float obstruction = std::max( 0.0f, std::min( 1.0f, sv.obstruction ) ); - float occlusion = std::max( 0.0f, std::min( 1.0f, sv.occlusion ) ); + float obstruction = std::max( 0.0f, std::min( 1.0f, state.obstruction ) ); + float occlusion = std::max( 0.0f, std::min( 1.0f, state.occlusion ) ); AK::SoundEngine::SetObjectObstructionAndOcclusion( emitterID, listenerID, obstruction, occlusion ); } + + // Periodically clean up emitters that are no longer in the active set. + if( ++m_cleanupCounter >= kCleanupEveryNFrames ) + { + m_cleanupCounter = 0; + CleanupStaleEmitters( gameObjects ); + } +} + +void AudObstruction::QueryEmitter( AkGameObjectID emitterID, EmitterState& state ) +{ + AkDiffractionPathInfo paths[8]; + AkUInt32 numPaths = 8; + AkVector64 listenerPos, emitterPos; + + AKRESULT result = AK::SpatialAudio::QueryDiffractionPaths( + emitterID, 0, listenerPos, emitterPos, paths, numPaths ); + + state.targetObstruction = 0.0f; + state.targetOcclusion = 0.0f; + + if( result == AK_Success && numPaths > 0 ) + { + float bestDiffraction = 1.0f; + float bestTransmission = 1.0f; + + for( AkUInt32 i = 0; i < numPaths; ++i ) + { + if( paths[i].diffraction < bestDiffraction ) + bestDiffraction = paths[i].diffraction; + if( paths[i].transmissionLoss < bestTransmission ) + bestTransmission = paths[i].transmissionLoss; + } + + state.targetObstruction = bestDiffraction; + state.targetOcclusion = bestTransmission; + } +} + +void AudObstruction::CleanupStaleEmitters( + const std::vector& gameObjects ) +{ + // Build a set of currently active emitter IDs. + std::unordered_set activeIDs; + activeIDs.reserve( gameObjects.size() ); + for( IPrioritizedObject* obj : gameObjects ) + activeIDs.insert( obj->GetID() ); + + // Remove any tracked emitters that are no longer in the active set. + auto it = m_emitters.begin(); + while( it != m_emitters.end() ) + { + if( activeIDs.find( it->first ) == activeIDs.end() ) + it = m_emitters.erase( it ); + else + ++it; + } +} + +void AudObstruction::SetRefreshInterval( float seconds ) +{ + m_refreshInterval = std::max( 0.0f, seconds ); +} + +float AudObstruction::GetRefreshInterval() const +{ + return m_refreshInterval; +} + +void AudObstruction::SetFadeRate( float unitsPerSecond ) +{ + m_fadeRate = std::max( 0.0f, unitsPerSecond ); +} + +float AudObstruction::GetFadeRate() const +{ + return m_fadeRate; } diff --git a/src/AudObstruction.h b/src/AudObstruction.h index ca37ccd..fe2b4cf 100644 --- a/src/AudObstruction.h +++ b/src/AudObstruction.h @@ -7,15 +7,19 @@ // Smooth obstruction and occlusion using Wwise's own spatial // audio raycasting. // -// Each frame, queries QueryDiffractionPaths() to read the +// Periodically queries QueryDiffractionPaths() to read the // transmission/diffraction values that Wwise already computed -// from registered geometry, then exponentially smooths them +// from registered geometry, then linearly fades toward them // over time and feeds the result to SetObjectObstructionAndOcclusion(). // // Obstruction (direct path only) is driven by diffraction. // Occlusion (direct + reverb) is driven by transmissionLoss. // The Wwise project curves decide how each one affects sound. // +// Queries are throttled to a configurable refresh interval +// (default 0.2s) and staggered across emitters to avoid +// CPU spikes from all emitters querying on the same frame. +// #pragma once @@ -33,16 +37,42 @@ class AudObstruction void Update( AkGameObjectID listenerID, const std::vector& gameObjects ); + void SetRefreshInterval( float seconds ); + float GetRefreshInterval() const; + + void SetFadeRate( float unitsPerSecond ); + float GetFadeRate() const; + private: - struct SmoothedValues + struct EmitterState { float obstruction = 0.0f; float occlusion = 0.0f; + float targetObstruction = 0.0f; + float targetOcclusion = 0.0f; + float nextQueryTime = 0.0f; // Staggered per-emitter query time + bool initialized = false; }; - std::unordered_map m_smoothed; + std::unordered_map m_emitters; + + // Accumulated time since start, used for scheduling queries. + float m_time = 0.0f; std::chrono::steady_clock::time_point m_lastUpdateTime; - // Smoothing rate. Higher = faster convergence. 2.0 gives ~1 second transitions. - static constexpr float kSmoothingRate = 2.0f; + // How often to re-query Wwise per emitter (seconds). Default 0.2s = 5 queries/sec. + float m_refreshInterval = 0.2f; + + // Linear fade rate in units per second. 2.0 = full 0-to-1 transition in 0.5 seconds. + float m_fadeRate = 2.0f; + + // Query Wwise for the current diffraction/transmission values for an emitter. + void QueryEmitter( AkGameObjectID emitterID, EmitterState& state ); + + // Remove entries for emitters that are no longer in the active set. + void CleanupStaleEmitters( const std::vector& gameObjects ); + + // Counter to throttle cleanup (not every frame). + int m_cleanupCounter = 0; + static constexpr int kCleanupEveryNFrames = 120; }; From 1e488756f0f6a0b653004da517504cdeaf393bfd Mon Sep 17 00:00:00 2001 From: phevosccp <160875214+phevosccp@users.noreply.github.com> Date: Tue, 24 Feb 2026 13:21:06 +0000 Subject: [PATCH 16/48] optimise init settings for spatial audio --- src/AudManager.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/AudManager.cpp b/src/AudManager.cpp index fdebed1..b890ee0 100644 --- a/src/AudManager.cpp +++ b/src/AudManager.cpp @@ -408,7 +408,17 @@ bool AudManager::InitSpatialAudioGeometry() { AkSpatialAudioInitSettings spatialSettings; + // We use geometry only for transmission detection (line-of-sight raycasting). + // Reflections, diffraction edges, rooms, and portals are not used. + // These settings minimize CPU cost while keeping transmission queries working. spatialSettings.bEnableGeometricDiffractionAndTransmission = true; + spatialSettings.bCalcEmitterVirtualPosition = false; + spatialSettings.uNumberOfPrimaryRays = 8; + spatialSettings.uMaxReflectionOrder = 0; + spatialSettings.uMaxDiffractionOrder = 1; + spatialSettings.uDiffractionOnReflectionsOrder = 0; + spatialSettings.uMaxEmitterRoomAuxSends = 0; + spatialSettings.uMaxSoundPropagationDepth = 1; if( AK::SpatialAudio::Init( spatialSettings ) != AK_Success ) { @@ -416,7 +426,7 @@ bool AudManager::InitSpatialAudioGeometry() return false; } - CCP_LOG_CH( s_ch, "Wwise Spatial Audio initialized for geometry-based occlusion/diffraction" ); + CCP_LOG_CH( s_ch, "Wwise Spatial Audio initialized for geometry-based transmission" ); return true; } From 5a27c674a9818513f826202192c10bdcbb63f744 Mon Sep 17 00:00:00 2001 From: phevosccp <160875214+phevosccp@users.noreply.github.com> Date: Tue, 24 Feb 2026 14:03:12 +0000 Subject: [PATCH 17/48] add cmake include for AudObstruction --- CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 465e8a9..991905d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,6 +49,7 @@ ccp_add_library(CarbonAudio SHARED src/AudActionLog.cpp src/AudActionLog_Blue.cpp src/AudManager.cpp + src/AudObstruction.cpp src/AudConstants.cpp src/AudManager_Blue.cpp src/AudEmitter.cpp @@ -107,6 +108,7 @@ target_sources(CarbonAudio PRIVATE src/AudioInputMgr.h src/AudListener.h src/AudManager.h + src/AudObstruction.h src/AudParameter.h src/AudPosition.h src/AudSettings.h From 0efb8444a075dd8556f8f025063a9ea45c8d23da Mon Sep 17 00:00:00 2001 From: phevosccp <160875214+phevosccp@users.noreply.github.com> Date: Tue, 24 Feb 2026 14:05:48 +0000 Subject: [PATCH 18/48] minor change --- src/AudObstruction.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/AudObstruction.cpp b/src/AudObstruction.cpp index bcda18d..ae958be 100644 --- a/src/AudObstruction.cpp +++ b/src/AudObstruction.cpp @@ -38,7 +38,7 @@ void AudObstruction::Update( EmitterState& state = m_emitters[emitterID]; - // First time seeing this emitter: query immediately and snap to result. + // First time seeing this emitter: query and snap to result. if( !state.initialized ) { QueryEmitter( emitterID, state ); @@ -50,14 +50,12 @@ void AudObstruction::Update( } else { - // Throttled query: only re-query when the refresh interval has elapsed. if( m_time >= state.nextQueryTime ) { QueryEmitter( emitterID, state ); state.nextQueryTime = m_time + m_refreshInterval; } - // Linear fade every frame regardless of query rate. float fadeStep = m_fadeRate * dt; if( state.obstruction < state.targetObstruction ) @@ -125,7 +123,6 @@ void AudObstruction::CleanupStaleEmitters( for( IPrioritizedObject* obj : gameObjects ) activeIDs.insert( obj->GetID() ); - // Remove any tracked emitters that are no longer in the active set. auto it = m_emitters.begin(); while( it != m_emitters.end() ) { From 9c3ff3cc600dc5c883372319398a6890c7cbd043 Mon Sep 17 00:00:00 2001 From: phevosccp <160875214+phevosccp@users.noreply.github.com> Date: Wed, 25 Feb 2026 16:59:26 +0000 Subject: [PATCH 19/48] Orthogonalize because Wwise rejects transforms where dot(front, up)^2 >= 0.1 --- src/Utilities.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/Utilities.h b/src/Utilities.h index e344284..9efa42d 100644 --- a/src/Utilities.h +++ b/src/Utilities.h @@ -74,6 +74,19 @@ class RH2LH else up.Normalize(); + // Orthogonalize: Wwise rejects transforms where dot(front, up)^2 >= 0.1 + Ak3DVector32 right = up.Cross( front ); + if( right.LengthSquared() <= kEpsilon ) + { + up = ( std::fabs( front.Y ) < 0.9f ) + ? Ak3DVector32( 0.0f, 1.0f, 0.0f ) + : Ak3DVector32( 0.0f, 0.0f, 1.0f ); + right = up.Cross( front ); + } + right.Normalize(); + up = front.Cross( right ); + up.Normalize(); + out.SetPosition( position ); out.SetOrientation( front, up ); } From 7c0ce488e3fa16617314482d189a1cea6e7a830d Mon Sep 17 00:00:00 2001 From: phevosccp <160875214+phevosccp@users.noreply.github.com> Date: Thu, 26 Feb 2026 11:16:39 +0000 Subject: [PATCH 20/48] remove degenerate triangle checks , fix for transform --- src/AudGeometry.cpp | 61 +-------------------------------------------- src/Utilities.h | 3 +-- 2 files changed, 2 insertions(+), 62 deletions(-) diff --git a/src/AudGeometry.cpp b/src/AudGeometry.cpp index fa85014..74ba7e1 100644 --- a/src/AudGeometry.cpp +++ b/src/AudGeometry.cpp @@ -37,19 +37,6 @@ namespace return akTriangles; } - bool IsTriangleDegenerate( const AkVertex& a, const AkVertex& b, const AkVertex& c ) - { - constexpr float kEpsilonSq = 1e-12f; - - float e1x = b.X - a.X, e1y = b.Y - a.Y, e1z = b.Z - a.Z; - float e2x = c.X - a.X, e2y = c.Y - a.Y, e2z = c.Z - a.Z; - - float cx = e1y * e2z - e1z * e2y; - float cy = e1z * e2x - e1x * e2z; - float cz = e1x * e2y - e1y * e2x; - - return ( cx * cx + cy * cy + cz * cz ) <= kEpsilonSq; - } } std::unordered_map AudGeometry::s_geometrySetRefCounts; @@ -71,59 +58,13 @@ void AudGeometry::SetGeometry( { return; } - - if( geometryData.m_indices.size() % 3 != 0 ) - { - CCP_LOGERR_CH( s_ch, "SetGeometry: index count %zu is not a multiple of 3 for instance %llu", - geometryData.m_indices.size(), instanceId ); - return; - } - - constexpr size_t kMaxVertices = std::numeric_limits::max(); - constexpr size_t kMaxTriangles = std::numeric_limits::max(); - - if( geometryData.m_vertices.size() > kMaxVertices ) - { - CCP_LOGERR_CH( s_ch, "SetGeometry: vertex count %zu exceeds max %zu for geometry set %llu, skipping", - geometryData.m_vertices.size(), kMaxVertices, geometrySetId ); - return; - } - - size_t numTriangles = geometryData.m_indices.size() / 3; - if( numTriangles > kMaxTriangles ) - { - CCP_LOGERR_CH( s_ch, "SetGeometry: triangle count %zu exceeds max %zu for geometry set %llu, skipping", - numTriangles, kMaxTriangles, geometrySetId ); - return; - } - - for( size_t i = 0; i < geometryData.m_indices.size(); ++i ) - { - if( geometryData.m_indices[i] >= geometryData.m_vertices.size() ) - { - CCP_LOGERR_CH( s_ch, "SetGeometry: index[%zu] = %u out of bounds (vertex count: %zu) for instance %llu", - i, geometryData.m_indices[i], geometryData.m_vertices.size(), instanceId ); - return; - } - } - CcpAutoMutex lock( s_mutex ); auto it = s_geometrySetRefCounts.find( geometrySetId ); if( it == s_geometrySetRefCounts.end() ) { std::vector akVertices = ConvertVertices( geometryData.m_vertices ); - std::vector allTriangles = ConvertTriangles( geometryData.m_indices ); - - std::vector akTriangles; - akTriangles.reserve( allTriangles.size() ); - for( const AkTriangle& tri : allTriangles ) - { - if( !IsTriangleDegenerate( akVertices[tri.point0], akVertices[tri.point1], akVertices[tri.point2] ) ) - { - akTriangles.push_back( tri ); - } - } + std::vector akTriangles = ConvertTriangles( geometryData.m_indices ); AkAcousticSurface surface; surface.strName = "default"; diff --git a/src/Utilities.h b/src/Utilities.h index 9efa42d..4c88bdc 100644 --- a/src/Utilities.h +++ b/src/Utilities.h @@ -62,7 +62,7 @@ class RH2LH if( !position.IsFinite() ) position = Ak3DVector32( 0.0f, 0.0f, 0.0f ); - Ak3DVector32 front( matrix._31, matrix._32, -matrix._33 ); + Ak3DVector32 front( -matrix._31, -matrix._32, matrix._33 ); if( !front.IsFinite() || front.LengthSquared() <= kEpsilon ) front = Ak3DVector32( 0.0f, 0.0f, 1.0f ); else @@ -74,7 +74,6 @@ class RH2LH else up.Normalize(); - // Orthogonalize: Wwise rejects transforms where dot(front, up)^2 >= 0.1 Ak3DVector32 right = up.Cross( front ); if( right.LengthSquared() <= kEpsilon ) { From 6ba8016ae4945e6b01b032aaac097a5c6fa8dfe2 Mon Sep 17 00:00:00 2001 From: phevosccp <160875214+phevosccp@users.noreply.github.com> Date: Thu, 26 Feb 2026 13:31:39 +0000 Subject: [PATCH 21/48] minor docs change --- src/AudGeometry.h | 3 --- src/AudObstruction.h | 27 ++++----------------------- 2 files changed, 4 insertions(+), 26 deletions(-) diff --git a/src/AudGeometry.h b/src/AudGeometry.h index 2a21c39..fdd2be7 100644 --- a/src/AudGeometry.h +++ b/src/AudGeometry.h @@ -46,9 +46,6 @@ BLUE_CLASS(AudGeometry) : private: // Tracks how many active instances reference each geometry set - // Static: shared across all AudGeometry instances because multiple EveSpaceObject2s - // each own their own AudGeometry but share the same Wwise geometry sets by ID - static std::unordered_map s_geometrySetRefCounts; static CcpMutex s_mutex; }; diff --git a/src/AudObstruction.h b/src/AudObstruction.h index fe2b4cf..251c153 100644 --- a/src/AudObstruction.h +++ b/src/AudObstruction.h @@ -4,22 +4,9 @@ // Creation Date: Feb 2026 // Copyright (c) 2026 CCP Games // -// Smooth obstruction and occlusion using Wwise's own spatial -// audio raycasting. -// -// Periodically queries QueryDiffractionPaths() to read the -// transmission/diffraction values that Wwise already computed -// from registered geometry, then linearly fades toward them +// Queries QueryDiffractionPaths() to read the +// transmission/diffraction values from registered geometry, then linearly fades toward them // over time and feeds the result to SetObjectObstructionAndOcclusion(). -// -// Obstruction (direct path only) is driven by diffraction. -// Occlusion (direct + reverb) is driven by transmissionLoss. -// The Wwise project curves decide how each one affects sound. -// -// Queries are throttled to a configurable refresh interval -// (default 0.2s) and staggered across emitters to avoid -// CPU spikes from all emitters querying on the same frame. -// #pragma once @@ -33,7 +20,6 @@ class AudObstruction public: AudObstruction(); - // Call once per frame from AudManager::Process(), after CullAudio() and before RenderAudio(). void Update( AkGameObjectID listenerID, const std::vector& gameObjects ); @@ -50,29 +36,24 @@ class AudObstruction float occlusion = 0.0f; float targetObstruction = 0.0f; float targetOcclusion = 0.0f; - float nextQueryTime = 0.0f; // Staggered per-emitter query time + float nextQueryTime = 0.0f; bool initialized = false; }; std::unordered_map m_emitters; - // Accumulated time since start, used for scheduling queries. float m_time = 0.0f; std::chrono::steady_clock::time_point m_lastUpdateTime; - // How often to re-query Wwise per emitter (seconds). Default 0.2s = 5 queries/sec. float m_refreshInterval = 0.2f; - // Linear fade rate in units per second. 2.0 = full 0-to-1 transition in 0.5 seconds. + // Linear fade rate in units per second float m_fadeRate = 2.0f; - // Query Wwise for the current diffraction/transmission values for an emitter. void QueryEmitter( AkGameObjectID emitterID, EmitterState& state ); - // Remove entries for emitters that are no longer in the active set. void CleanupStaleEmitters( const std::vector& gameObjects ); - // Counter to throttle cleanup (not every frame). int m_cleanupCounter = 0; static constexpr int kCleanupEveryNFrames = 120; }; From e26cb8ffd6021e6529cd44088a0d940660520834 Mon Sep 17 00:00:00 2001 From: phevosccp <160875214+phevosccp@users.noreply.github.com> Date: Thu, 26 Feb 2026 13:34:14 +0000 Subject: [PATCH 22/48] add back declaration accidentaly removed --- src/AudGeometry.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/AudGeometry.h b/src/AudGeometry.h index fdd2be7..26e1524 100644 --- a/src/AudGeometry.h +++ b/src/AudGeometry.h @@ -46,6 +46,7 @@ BLUE_CLASS(AudGeometry) : private: // Tracks how many active instances reference each geometry set + static std::unordered_map s_geometrySetRefCounts; static CcpMutex s_mutex; }; From c1be78dba750a709a579d661a1a61b17e63be461 Mon Sep 17 00:00:00 2001 From: phevosccp <160875214+phevosccp@users.noreply.github.com> Date: Fri, 27 Feb 2026 11:39:44 +0000 Subject: [PATCH 23/48] add doxygen --- src/AudGeometry.cpp | 2 -- src/AudGeometry.h | 43 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/src/AudGeometry.cpp b/src/AudGeometry.cpp index 74ba7e1..8c6e2e0 100644 --- a/src/AudGeometry.cpp +++ b/src/AudGeometry.cpp @@ -3,8 +3,6 @@ #include "Utilities.h" #include "Vector3.h" #include "AudManager.h" -#include -#include static CcpLogChannel_t s_ch = CCP_LOG_DEFINE_CHANNEL( "AudGeometry" ); diff --git a/src/AudGeometry.h b/src/AudGeometry.h index 26e1524..b3bc604 100644 --- a/src/AudGeometry.h +++ b/src/AudGeometry.h @@ -13,9 +13,18 @@ #include -struct Vector3; - +/** + * @brief Manages Wwise Spatial Audio geometry sets and geometry instances for + * geometric reflection and diffraction processing. + * + * AudGeometry implements the ITr2AudGeometry interface to submit geometry to + * AK::SpatialAudio. A geometry set is a logical set of vertices, triangles, + * and acoustic surfaces (see AkGeometryParams). A geometry instance is a unique + * placement of a geometry set in the world with a specified transform — position, + * orientation, and scale (see AkGeometryInstanceParams). + * + */ BLUE_CLASS(AudGeometry) : public ITr2AudGeometry @@ -29,24 +38,52 @@ BLUE_CLASS(AudGeometry) : // ITr2AudGeometry interface + /** + * @brief Registers a ref-counted geometry set and places a geometry + * instance in the world via AK::SpatialAudio::SetGeometryInstance. + * + * @param geometrySetId Shared geometry set identifier. + * @param instanceId Unique geometry instance identifier. + * @param geometryData Triangle mesh. + * @param worldTransform World position, orientation and scale. + * + */ void SetGeometry( uint64_t geometrySetId, uint64_t instanceId, const Tr2AudGeometryData& geometryData, const Matrix& worldTransform) override; + /** + * @brief Updates the world transform of an existing geometry instance + * + * @param geometrySetId Geometry set the instance references. + * @param instanceId Geometry instance to update. + * @param worldTransform New world-space position, orientation and scale. + * + */ void SetGeometryTransform( uint64_t geometrySetId, uint64_t instanceId, const Matrix& worldTransform) override; + /** + * @brief Removes a geometry instance and releases the geometry set when + * its reference count reaches zero. + * + * @param geometrySetId Geometry set to dereference. + * @param instanceId Geometry instance to remove. + * + */ void RemoveGeometry( uint64_t geometrySetId, uint64_t instanceId) override; private: - // Tracks how many active instances reference each geometry set + /// Tracks how many active instances reference each geometry set. static std::unordered_map s_geometrySetRefCounts; + + /// Mutex protecting s_geometrySetRefCounts and Wwise geometry operations. static CcpMutex s_mutex; }; From d85e32bf48e4f2939b2e24ac7bd7c7a250e4d258 Mon Sep 17 00:00:00 2001 From: phevosccp <160875214+phevosccp@users.noreply.github.com> Date: Fri, 27 Feb 2026 11:59:28 +0000 Subject: [PATCH 24/48] Rename AudObstruction to AudObstructionOcclusion and add doxygen --- src/AudManager.cpp | 8 +- src/AudManager.h | 4 +- src/AudObstruction.h | 59 --------- ...uction.cpp => AudObstructionOcclusion.cpp} | 26 ++-- src/AudObstructionOcclusion.h | 116 ++++++++++++++++++ 5 files changed, 134 insertions(+), 79 deletions(-) delete mode 100644 src/AudObstruction.h rename src/{AudObstruction.cpp => AudObstructionOcclusion.cpp} (83%) create mode 100644 src/AudObstructionOcclusion.h diff --git a/src/AudManager.cpp b/src/AudManager.cpp index b890ee0..af311b3 100644 --- a/src/AudManager.cpp +++ b/src/AudManager.cpp @@ -77,14 +77,14 @@ AudManager::AudManager( IRoot* lockobj ) : { // Initialize sound prioritization system m_soundPrioritization = new SoundPrioritization(); - m_obstruction = new AudObstruction(); + m_obstructionOcclusion = new AudObstructionOcclusion(); } AudManager::~AudManager() { // Clean up sound prioritization system delete m_soundPrioritization; - delete m_obstruction; + delete m_obstructionOcclusion; if( g_audioInitialized ) { @@ -102,7 +102,7 @@ void AudManager::Process() } // Smooth obstruction values using Wwise's computed diffraction/transmission paths - m_obstruction->Update( LISTENER_GAME_OBJ_ID, + m_obstructionOcclusion->Update( LISTENER_GAME_OBJ_ID, m_soundPrioritization->GetPrioritizedAudioObjects() ); // Process bank requests, events, positions, RTPC, etc. @@ -1326,4 +1326,4 @@ void AudManager::ResourceMonitorCallback( const AkResourceMonitorDataSummary* da CCP_STATS_SET( totalVoices, dataSummary->totalVoices ); CCP_STATS_SET( nbActiveEvents, dataSummary->nbActiveEvents ); } -#endif \ No newline at end of file +#endif diff --git a/src/AudManager.h b/src/AudManager.h index 2c014ef..427f9d1 100644 --- a/src/AudManager.h +++ b/src/AudManager.h @@ -13,7 +13,7 @@ #include "Audio2.h" #include "AudSettings.h" #include "AudListener.h" -#include "AudObstruction.h" +#include "AudObstructionOcclusion.h" #include "SoundPrioritization.h" #include "CCPFilePackageLowLevelIO.h" #include @@ -227,7 +227,7 @@ BLUE_CLASS( AudManager ) : CcpMutex m_moniteredParametersMapMutex; SoundPrioritization* m_soundPrioritization; - AudObstruction* m_obstruction; + AudObstructionOcclusion* m_obstructionOcclusion; // A boolean for the state of the profiler capture bool m_isProfilerCapturing; diff --git a/src/AudObstruction.h b/src/AudObstruction.h deleted file mode 100644 index 251c153..0000000 --- a/src/AudObstruction.h +++ /dev/null @@ -1,59 +0,0 @@ -//////////////////////////////////////////////////////////// -// -// Creator: Phevos Rinis -// Creation Date: Feb 2026 -// Copyright (c) 2026 CCP Games -// -// Queries QueryDiffractionPaths() to read the -// transmission/diffraction values from registered geometry, then linearly fades toward them -// over time and feeds the result to SetObjectObstructionAndOcclusion(). - -#pragma once - -#include -#include -#include -#include "IPrioritizedObject.h" - -class AudObstruction -{ -public: - AudObstruction(); - - void Update( AkGameObjectID listenerID, - const std::vector& gameObjects ); - - void SetRefreshInterval( float seconds ); - float GetRefreshInterval() const; - - void SetFadeRate( float unitsPerSecond ); - float GetFadeRate() const; - -private: - struct EmitterState - { - float obstruction = 0.0f; - float occlusion = 0.0f; - float targetObstruction = 0.0f; - float targetOcclusion = 0.0f; - float nextQueryTime = 0.0f; - bool initialized = false; - }; - - std::unordered_map m_emitters; - - float m_time = 0.0f; - std::chrono::steady_clock::time_point m_lastUpdateTime; - - float m_refreshInterval = 0.2f; - - // Linear fade rate in units per second - float m_fadeRate = 2.0f; - - void QueryEmitter( AkGameObjectID emitterID, EmitterState& state ); - - void CleanupStaleEmitters( const std::vector& gameObjects ); - - int m_cleanupCounter = 0; - static constexpr int kCleanupEveryNFrames = 120; -}; diff --git a/src/AudObstruction.cpp b/src/AudObstructionOcclusion.cpp similarity index 83% rename from src/AudObstruction.cpp rename to src/AudObstructionOcclusion.cpp index ae958be..a22c223 100644 --- a/src/AudObstruction.cpp +++ b/src/AudObstructionOcclusion.cpp @@ -1,26 +1,26 @@ #include "stdafx.h" -#include "AudObstruction.h" +#include "AudObstructionOcclusion.h" #include "Audio2.h" #include #include -AudObstruction::AudObstruction() +AudObstructionOcclusion::AudObstructionOcclusion() : m_lastUpdateTime( std::chrono::steady_clock::now() ) { } -void AudObstruction::Update( +void AudObstructionOcclusion::Update( AkGameObjectID listenerID, const std::vector& gameObjects ) { if( !g_audioInitialized ) return; - // Delta time auto now = std::chrono::steady_clock::now(); float dt = std::chrono::duration( now - m_lastUpdateTime ).count(); + m_lastUpdateTime = now; - dt = std::min( dt, 0.1f ); // Clamp to avoid huge jumps after pauses + dt = std::min( dt, 0.1f ); m_time += dt; for( IPrioritizedObject* obj : gameObjects ) @@ -38,12 +38,12 @@ void AudObstruction::Update( EmitterState& state = m_emitters[emitterID]; - // First time seeing this emitter: query and snap to result. if( !state.initialized ) { QueryEmitter( emitterID, state ); state.obstruction = state.targetObstruction; state.occlusion = state.targetOcclusion; + // Stagger future queries so emitters don't all query on the same frame. state.nextQueryTime = m_time + m_refreshInterval * ( (float)std::rand() / RAND_MAX ); state.initialized = true; @@ -76,7 +76,6 @@ void AudObstruction::Update( emitterID, listenerID, obstruction, occlusion ); } - // Periodically clean up emitters that are no longer in the active set. if( ++m_cleanupCounter >= kCleanupEveryNFrames ) { m_cleanupCounter = 0; @@ -84,7 +83,7 @@ void AudObstruction::Update( } } -void AudObstruction::QueryEmitter( AkGameObjectID emitterID, EmitterState& state ) +void AudObstructionOcclusion::QueryEmitter( AkGameObjectID emitterID, EmitterState& state ) { AkDiffractionPathInfo paths[8]; AkUInt32 numPaths = 8; @@ -114,10 +113,9 @@ void AudObstruction::QueryEmitter( AkGameObjectID emitterID, EmitterState& state } } -void AudObstruction::CleanupStaleEmitters( +void AudObstructionOcclusion::CleanupStaleEmitters( const std::vector& gameObjects ) { - // Build a set of currently active emitter IDs. std::unordered_set activeIDs; activeIDs.reserve( gameObjects.size() ); for( IPrioritizedObject* obj : gameObjects ) @@ -133,22 +131,22 @@ void AudObstruction::CleanupStaleEmitters( } } -void AudObstruction::SetRefreshInterval( float seconds ) +void AudObstructionOcclusion::SetRefreshInterval( float seconds ) { m_refreshInterval = std::max( 0.0f, seconds ); } -float AudObstruction::GetRefreshInterval() const +float AudObstructionOcclusion::GetRefreshInterval() const { return m_refreshInterval; } -void AudObstruction::SetFadeRate( float unitsPerSecond ) +void AudObstructionOcclusion::SetFadeRate( float unitsPerSecond ) { m_fadeRate = std::max( 0.0f, unitsPerSecond ); } -float AudObstruction::GetFadeRate() const +float AudObstructionOcclusion::GetFadeRate() const { return m_fadeRate; } diff --git a/src/AudObstructionOcclusion.h b/src/AudObstructionOcclusion.h new file mode 100644 index 0000000..1f6b57b --- /dev/null +++ b/src/AudObstructionOcclusion.h @@ -0,0 +1,116 @@ +//////////////////////////////////////////////////////////// +// +// Creator: Phevos Rinis +// Creation Date: Feb 2026 +// Copyright (c) 2026 CCP Games +// + +#pragma once + +#include +#include +#include +#include "IPrioritizedObject.h" + +/** + * @brief Tracks and applies per-emitter obstruction/occlusion values. + * + * This class queries Wwise Spatial Audio diffraction paths, derives target + * obstruction/occlusion values, smooths them over time, and writes the + * smoothed values back to Wwise each tick. + */ +class AudObstructionOcclusion +{ +public: + /** + * @brief Constructor and init timing state. + */ + AudObstructionOcclusion(); + + /** + * @brief Updates obstruction/occlusion for the current active emitter set. + * + * @param listenerID Game object ID of the active listener. + * @param gameObjects Active prioritized audio objects to evaluate. + */ + void Update( AkGameObjectID listenerID, + const std::vector& gameObjects ); + + /** + * @brief Sets how often each emitter is re-queried from Spatial Audio. + * + * @param seconds Query interval in seconds. + */ + void SetRefreshInterval( float seconds ); + + /** + * @brief Gets the per-emitter Spatial Audio query interval in seconds. + * + * @return Current refresh interval in seconds. + */ + float GetRefreshInterval() const; + + /** + * @brief Sets linear smoothing speed toward target values. + * + * @param unitsPerSecond Fade speed in units per second. + */ + void SetFadeRate( float unitsPerSecond ); + + /** + * @brief Gets linear smoothing speed toward target values. + * + * @return Current fade rate in units per second. + */ + float GetFadeRate() const; + +private: + /** + * @brief Cached runtime state for one emitter. + */ + struct EmitterState + { + float obstruction = 0.0f; + float occlusion = 0.0f; + float targetObstruction = 0.0f; + float targetOcclusion = 0.0f; + float nextQueryTime = 0.0f; + bool initialized = false; + }; + + /// Active emitter state indexed by game object ID. + std::unordered_map m_emitters; + + /// local time accumulator in seconds. + float m_time = 0.0f; + + /// Timestamp from previous update. + std::chrono::steady_clock::time_point m_lastUpdateTime; + + /// Per-emitter query cadence in seconds. + float m_refreshInterval = 0.2f; + + /// Linear fade rate in second. + float m_fadeRate = 2.0f; + + /** + * @brief Queries Wwise Spatial Audio and updates emitter target values. + * + * @param emitterID Emitter game object ID. + * @param state Mutable state to update with query results. + */ + void QueryEmitter( AkGameObjectID emitterID, EmitterState& state ); + + /** + * @brief Removes cached emitter states not present in the active set. + * + * @param gameObjects Active prioritized audio objects. + */ + void CleanupStaleEmitters( const std::vector& gameObjects ); + + /// Frame counter used to schedule periodic stale-state cleanup. + int m_cleanupCounter = 0; + + /// Number of update frames between stale-state cleanup passes. + static constexpr int kCleanupEveryNFrames = 120; +}; From 1c2d4c02fb95cda8ba40db3abf3293404dd888ab Mon Sep 17 00:00:00 2001 From: phevosccp <160875214+phevosccp@users.noreply.github.com> Date: Fri, 27 Feb 2026 12:50:24 +0000 Subject: [PATCH 25/48] change class name in cmakelists too --- CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 991905d..32eaaa3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,7 +49,7 @@ ccp_add_library(CarbonAudio SHARED src/AudActionLog.cpp src/AudActionLog_Blue.cpp src/AudManager.cpp - src/AudObstruction.cpp + src/AudObstructionOcclusion.cpp src/AudConstants.cpp src/AudManager_Blue.cpp src/AudEmitter.cpp @@ -108,7 +108,7 @@ target_sources(CarbonAudio PRIVATE src/AudioInputMgr.h src/AudListener.h src/AudManager.h - src/AudObstruction.h + src/AudObstructionOcclusion.h src/AudParameter.h src/AudPosition.h src/AudSettings.h @@ -380,4 +380,4 @@ if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) set_tests_properties(${PYTHON_TEST} PROPERTIES ENVIRONMENT "PYTHONPATH=$:${CMAKE_CURRENT_SOURCE_DIR}/python;BUILDFLAVOR=$>") endif() endforeach() -endif() \ No newline at end of file +endif() From 5cd754f22c61f473041d77488934a59edca5e147 Mon Sep 17 00:00:00 2001 From: phevosccp <160875214+phevosccp@users.noreply.github.com> Date: Mon, 2 Mar 2026 09:59:06 +0000 Subject: [PATCH 26/48] add const for IPrioritizedObject --- src/AudObstructionOcclusion.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AudObstructionOcclusion.cpp b/src/AudObstructionOcclusion.cpp index a22c223..41b22ab 100644 --- a/src/AudObstructionOcclusion.cpp +++ b/src/AudObstructionOcclusion.cpp @@ -23,7 +23,7 @@ void AudObstructionOcclusion::Update( dt = std::min( dt, 0.1f ); m_time += dt; - for( IPrioritizedObject* obj : gameObjects ) + for( const IPrioritizedObject* obj : gameObjects ) { AkGameObjectID emitterID = obj->GetID(); From f55b03c08c369d184c77fdc99834d602d559305f Mon Sep 17 00:00:00 2001 From: phevosccp <160875214+phevosccp@users.noreply.github.com> Date: Fri, 6 Mar 2026 09:48:46 +0000 Subject: [PATCH 27/48] various fixes for transform and geoemtry settings --- src/AudGeometry.cpp | 2 +- src/AudListener.cpp | 11 ++++++-- src/AudManager.cpp | 6 +++-- src/AudObstructionOcclusion.cpp | 45 ++++++++++++++++++++++++++------- src/Utilities.h | 5 ++-- 5 files changed, 52 insertions(+), 17 deletions(-) diff --git a/src/AudGeometry.cpp b/src/AudGeometry.cpp index 8c6e2e0..8bb654c 100644 --- a/src/AudGeometry.cpp +++ b/src/AudGeometry.cpp @@ -67,7 +67,7 @@ void AudGeometry::SetGeometry( AkAcousticSurface surface; surface.strName = "default"; surface.textureID = AK_INVALID_UNIQUE_ID; - surface.transmissionLoss = 0.7f; + surface.transmissionLoss = 1.0f; AkGeometryParams params; params.Vertices = akVertices.data(); diff --git a/src/AudListener.cpp b/src/AudListener.cpp index bd513e5..5b3d165 100644 --- a/src/AudListener.cpp +++ b/src/AudListener.cpp @@ -51,7 +51,14 @@ int AudListener::SetPositionHelper( const Vector3& front, const Vector3& top, co Vector3 correctFront = Normalize( front ); Vector3 correctUp = Normalize( top ); correctUp = Normalize( Cross( Cross( correctFront, correctUp ), correctFront ) ); - tmp.Set( MakeAkVector(position), MakeAkVector(correctFront), MakeAkVector(correctUp) ); + + + Vector3 correctedPosition = position; + correctedPosition.y *= -1.f; + correctFront.y *= -1.f; + correctUp.y *= -1.f; + + tmp.Set( MakeAkVector( correctedPosition ), MakeAkVector( correctFront ), MakeAkVector( correctUp ) ); // all vectors come in RH, but WWISE is LH, so convert AkSoundPosition soundPosLH; @@ -61,4 +68,4 @@ int AudListener::SetPositionHelper( const Vector3& front, const Vector3& top, co } } return AK_Success; -} \ No newline at end of file +} diff --git a/src/AudManager.cpp b/src/AudManager.cpp index af311b3..bda6026 100644 --- a/src/AudManager.cpp +++ b/src/AudManager.cpp @@ -331,6 +331,7 @@ bool AudManager::InitSound() AK::SoundEngine::GetDefaultInitSettings( initSettings ); AK::SoundEngine::GetDefaultPlatformInitSettings( platformInitSettings ); initSettings.uCommandQueueSize = 512000; + initSettings.eFloorPlane = AkFloorPlane_XZ; #ifndef AK_OPTIMIZED initSettings.fnProfilerPopTimer = AkPlatformProfilerPopTimer; initSettings.fnProfilerPushTimer = AkPlatformProfilerPushTimer; @@ -413,9 +414,10 @@ bool AudManager::InitSpatialAudioGeometry() // These settings minimize CPU cost while keeping transmission queries working. spatialSettings.bEnableGeometricDiffractionAndTransmission = true; spatialSettings.bCalcEmitterVirtualPosition = false; - spatialSettings.uNumberOfPrimaryRays = 8; + spatialSettings.uNumberOfPrimaryRays = 2; spatialSettings.uMaxReflectionOrder = 0; - spatialSettings.uMaxDiffractionOrder = 1; + // Keep diffraction enabled but cap to single-edge for lower CPU cost. + spatialSettings.uMaxDiffractionOrder = 4; spatialSettings.uDiffractionOnReflectionsOrder = 0; spatialSettings.uMaxEmitterRoomAuxSends = 0; spatialSettings.uMaxSoundPropagationDepth = 1; diff --git a/src/AudObstructionOcclusion.cpp b/src/AudObstructionOcclusion.cpp index 41b22ab..6a7d269 100644 --- a/src/AudObstructionOcclusion.cpp +++ b/src/AudObstructionOcclusion.cpp @@ -85,8 +85,8 @@ void AudObstructionOcclusion::Update( void AudObstructionOcclusion::QueryEmitter( AkGameObjectID emitterID, EmitterState& state ) { - AkDiffractionPathInfo paths[8]; - AkUInt32 numPaths = 8; + AkDiffractionPathInfo paths[4]; + AkUInt32 numPaths = 4; AkVector64 listenerPos, emitterPos; AKRESULT result = AK::SpatialAudio::QueryDiffractionPaths( @@ -98,18 +98,45 @@ void AudObstructionOcclusion::QueryEmitter( AkGameObjectID emitterID, EmitterSta if( result == AK_Success && numPaths > 0 ) { float bestDiffraction = 1.0f; - float bestTransmission = 1.0f; + float bestReportedDiffraction = 1.0f; + float strongestTransmission = 0.0f; + bool hasDiffractionPath = false; + bool hasReportedDiffraction = false; + bool hasBlockedDirectPath = false; for( AkUInt32 i = 0; i < numPaths; ++i ) { - if( paths[i].diffraction < bestDiffraction ) - bestDiffraction = paths[i].diffraction; - if( paths[i].transmissionLoss < bestTransmission ) - bestTransmission = paths[i].transmissionLoss; + float diffraction = std::max( 0.0f, std::min( 1.0f, paths[i].diffraction ) ); + float transmission = std::max( 0.0f, std::min( 1.0f, paths[i].transmissionLoss ) ); + strongestTransmission = std::max( strongestTransmission, transmission ); + if( diffraction > 0.0f ) + { + hasReportedDiffraction = true; + bestReportedDiffraction = std::min( bestReportedDiffraction, diffraction ); + } + + // A path with at least one node represents diffraction around geometry edges. + // This is the gradual value the demo exposes while moving behind/away from a wall. + if( paths[i].nodeCount > 0 ) + { + hasDiffractionPath = true; + bestDiffraction = std::min( bestDiffraction, diffraction ); + } + else if( transmission > 0.0f ) + { + // No edge nodes and non-zero transmission indicates a blocked direct path. + hasBlockedDirectPath = true; + } } - state.targetObstruction = bestDiffraction; - state.targetOcclusion = bestTransmission; + if( hasDiffractionPath ) + state.targetObstruction = bestDiffraction; + else if( hasReportedDiffraction ) + state.targetObstruction = bestReportedDiffraction; + else + state.targetObstruction = hasBlockedDirectPath ? 1.0f : 0.0f; + + state.targetOcclusion = strongestTransmission; } } diff --git a/src/Utilities.h b/src/Utilities.h index 4c88bdc..0fa0d43 100644 --- a/src/Utilities.h +++ b/src/Utilities.h @@ -33,10 +33,9 @@ class RH2LH AkVector top = listenerLH->OrientationTop(); AkVector64 pos = listenerLH->Position(); - front.X *= -1.f; - front.Y *= -1.f; + pos.Z *= -1.f; + front.Z *= -1.f; top.Z *= -1.f; - pos.Z *= -1.f; listenerLH->Set(pos, front, top); } From f075c28f921e4fd69427694276deb2cf3c92709e Mon Sep 17 00:00:00 2001 From: phevosccp <160875214+phevosccp@users.noreply.github.com> Date: Mon, 9 Mar 2026 16:18:16 +0000 Subject: [PATCH 28/48] simplify transform --- src/AudListener.cpp | 9 +-------- src/AudManager.cpp | 1 - src/Utilities.h | 4 ++++ 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/AudListener.cpp b/src/AudListener.cpp index 5b3d165..4726c77 100644 --- a/src/AudListener.cpp +++ b/src/AudListener.cpp @@ -51,14 +51,7 @@ int AudListener::SetPositionHelper( const Vector3& front, const Vector3& top, co Vector3 correctFront = Normalize( front ); Vector3 correctUp = Normalize( top ); correctUp = Normalize( Cross( Cross( correctFront, correctUp ), correctFront ) ); - - - Vector3 correctedPosition = position; - correctedPosition.y *= -1.f; - correctFront.y *= -1.f; - correctUp.y *= -1.f; - - tmp.Set( MakeAkVector( correctedPosition ), MakeAkVector( correctFront ), MakeAkVector( correctUp ) ); + tmp.Set( MakeAkVector( position ), MakeAkVector( correctFront ), MakeAkVector( correctUp ) ); // all vectors come in RH, but WWISE is LH, so convert AkSoundPosition soundPosLH; diff --git a/src/AudManager.cpp b/src/AudManager.cpp index bda6026..0a839c6 100644 --- a/src/AudManager.cpp +++ b/src/AudManager.cpp @@ -331,7 +331,6 @@ bool AudManager::InitSound() AK::SoundEngine::GetDefaultInitSettings( initSettings ); AK::SoundEngine::GetDefaultPlatformInitSettings( platformInitSettings ); initSettings.uCommandQueueSize = 512000; - initSettings.eFloorPlane = AkFloorPlane_XZ; #ifndef AK_OPTIMIZED initSettings.fnProfilerPopTimer = AkPlatformProfilerPopTimer; initSettings.fnProfilerPushTimer = AkPlatformProfilerPushTimer; diff --git a/src/Utilities.h b/src/Utilities.h index 0fa0d43..6d2dbeb 100644 --- a/src/Utilities.h +++ b/src/Utilities.h @@ -33,6 +33,10 @@ class RH2LH AkVector top = listenerLH->OrientationTop(); AkVector64 pos = listenerLH->Position(); + front.X *= -1.f; + front.Y *= -1.f; + front.Z *= -1.f; + pos.Z *= -1.f; front.Z *= -1.f; top.Z *= -1.f; From 8ad6dbcbb75f79f7a91fc99837c4378e1caf6176 Mon Sep 17 00:00:00 2001 From: phevosccp <160875214+phevosccp@users.noreply.github.com> Date: Tue, 10 Mar 2026 11:01:29 +0000 Subject: [PATCH 29/48] settings improvement --- src/AudManager.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/AudManager.cpp b/src/AudManager.cpp index 0a839c6..b59fbad 100644 --- a/src/AudManager.cpp +++ b/src/AudManager.cpp @@ -413,13 +413,16 @@ bool AudManager::InitSpatialAudioGeometry() // These settings minimize CPU cost while keeping transmission queries working. spatialSettings.bEnableGeometricDiffractionAndTransmission = true; spatialSettings.bCalcEmitterVirtualPosition = false; - spatialSettings.uNumberOfPrimaryRays = 2; + spatialSettings.uNumberOfPrimaryRays = 0; spatialSettings.uMaxReflectionOrder = 0; // Keep diffraction enabled but cap to single-edge for lower CPU cost. spatialSettings.uMaxDiffractionOrder = 4; spatialSettings.uDiffractionOnReflectionsOrder = 0; spatialSettings.uMaxEmitterRoomAuxSends = 0; spatialSettings.uMaxSoundPropagationDepth = 1; + spatialSettings.fMovementThreshold = 100; + spatialSettings.fCPULimitPercentage = 20; + if( AK::SpatialAudio::Init( spatialSettings ) != AK_Success ) { From a9061dd7eaeca9aab1a0966a8b9e7d7eadb7f667 Mon Sep 17 00:00:00 2001 From: phevosccp <160875214+phevosccp@users.noreply.github.com> Date: Wed, 11 Mar 2026 11:51:39 +0000 Subject: [PATCH 30/48] switch between HQ spatial mode and basic occlusion --- src/AudGeometry.cpp | 2 +- src/AudManager.cpp | 46 ++++++++++++++++++++++-------- src/AudManager.h | 4 +++ src/AudObstructionOcclusion.cpp | 50 +++++---------------------------- src/AudSettings.h | 8 ++++++ src/AudSettings_Blue.cpp | 7 +++++ 6 files changed, 61 insertions(+), 56 deletions(-) diff --git a/src/AudGeometry.cpp b/src/AudGeometry.cpp index 8bb654c..b69eaf6 100644 --- a/src/AudGeometry.cpp +++ b/src/AudGeometry.cpp @@ -76,7 +76,7 @@ void AudGeometry::SetGeometry( params.NumTriangles = static_cast( akTriangles.size() ); params.Surfaces = &surface; params.NumSurfaces = 1; - params.EnableDiffraction = true; + params.EnableDiffraction = ( g_audioManager && g_audioManager->GetOcclusionMode() == AudOcclusionMode::HQ ); params.EnableDiffractionOnBoundaryEdges = false; AKRESULT result = AK::SpatialAudio::SetGeometry( geometrySetId, params ); diff --git a/src/AudManager.cpp b/src/AudManager.cpp index b59fbad..b4a1910 100644 --- a/src/AudManager.cpp +++ b/src/AudManager.cpp @@ -101,9 +101,13 @@ void AudManager::Process() m_soundPrioritization->CullAudio(); } - // Smooth obstruction values using Wwise's computed diffraction/transmission paths - m_obstructionOcclusion->Update( LISTENER_GAME_OBJ_ID, - m_soundPrioritization->GetPrioritizedAudioObjects() ); + // In Basic mode, manually query 1 ray per emitter and apply occlusion. + // In HQ mode, Wwise Spatial Audio handles diffraction + transmission automatically. + if( m_occlusionMode == AudOcclusionMode::Basic ) + { + m_obstructionOcclusion->Update( LISTENER_GAME_OBJ_ID, + m_soundPrioritization->GetPrioritizedAudioObjects() ); + } // Process bank requests, events, positions, RTPC, etc. AK::SoundEngine::RenderAudio(); @@ -408,21 +412,32 @@ bool AudManager::InitSpatialAudioGeometry() { AkSpatialAudioInitSettings spatialSettings; - // We use geometry only for transmission detection (line-of-sight raycasting). - // Reflections, diffraction edges, rooms, and portals are not used. - // These settings minimize CPU cost while keeping transmission queries working. + // Both modes need geometric transmission enabled for line-of-sight checks. spatialSettings.bEnableGeometricDiffractionAndTransmission = true; - spatialSettings.bCalcEmitterVirtualPosition = false; - spatialSettings.uNumberOfPrimaryRays = 0; + spatialSettings.uMaxReflectionOrder = 0; - // Keep diffraction enabled but cap to single-edge for lower CPU cost. - spatialSettings.uMaxDiffractionOrder = 4; spatialSettings.uDiffractionOnReflectionsOrder = 0; spatialSettings.uMaxEmitterRoomAuxSends = 0; spatialSettings.uMaxSoundPropagationDepth = 1; spatialSettings.fMovementThreshold = 100; - spatialSettings.fCPULimitPercentage = 20; + if( m_occlusionMode == AudOcclusionMode::HQ ) + { + // HQ: Wwise Spatial Audio handles diffraction + transmission automatically. + spatialSettings.uMaxDiffractionOrder = 4; + spatialSettings.bCalcEmitterVirtualPosition = true; + spatialSettings.fCPULimitPercentage = 20; + } + else + { + // Basic: No diffraction, only transmission/line-of-sight raycasting. + // AudObstructionOcclusion queries 1 ray per emitter and feeds the result + // into AK::SoundEngine::SetObjectObstructionAndOcclusion(). + spatialSettings.uNumberOfPrimaryRays = 1; + spatialSettings.uMaxDiffractionOrder = 0; + spatialSettings.bCalcEmitterVirtualPosition = false; + spatialSettings.fCPULimitPercentage = 10; + } if( AK::SpatialAudio::Init( spatialSettings ) != AK_Success ) { @@ -430,7 +445,8 @@ bool AudManager::InitSpatialAudioGeometry() return false; } - CCP_LOG_CH( s_ch, "Wwise Spatial Audio initialized for geometry-based transmission" ); + const char* modeName = ( m_occlusionMode == AudOcclusionMode::HQ ) ? "HQ (diffraction + transmission)" : "Basic (transmission only)"; + CCP_LOG_CH( s_ch, "Wwise Spatial Audio initialized in %s mode", modeName ); return true; } @@ -466,6 +482,11 @@ bool AudManager::SetState( const std::wstring& stateGroup, const std::wstring& s // Description: // Signals whether Carbon Audio supports spatial audio features on this operating system. //----------------------------------------------------- +AudOcclusionMode AudManager::GetOcclusionMode() const +{ + return m_occlusionMode; +} + const bool AudManager::SpatialAudioIsSupported() { return s_systemSupportsSpatialAudio; @@ -474,6 +495,7 @@ const bool AudManager::SpatialAudioIsSupported() void AudManager::UpdateSettings( AudSettings* settings ) { m_settings = settings; + m_occlusionMode = static_cast( m_settings->m_occlusionMode ); } void AudManager::LoadBank( const std::wstring& name ) diff --git a/src/AudManager.h b/src/AudManager.h index 427f9d1..380a1c1 100644 --- a/src/AudManager.h +++ b/src/AudManager.h @@ -115,6 +115,8 @@ BLUE_CLASS( AudManager ) : bool SetGlobalRTPC( const std::wstring& rtpcName, float value ); // Set a global state in Wwise. bool SetState( const std::wstring& stateGroup, const std::wstring& stateName ); + // Returns the active occlusion mode (HQ or Basic). + AudOcclusionMode GetOcclusionMode() const; // Can be called to see if the current platform supports spatial audio. const bool SpatialAudioIsSupported(); // Stop all currently playing sounds on all game objects. @@ -213,6 +215,8 @@ BLUE_CLASS( AudManager ) : bool m_asyncOpen; // Signals whether Carbon Audio's spatial audio features are enabled. If the user currently doesn't have an active spatial audio endpoint then output will still be in stereo. bool m_spatialAudioEnabled; + // Controls whether to use HQ (full diffraction) or Basic (1-ray occlusion) mode. + AudOcclusionMode m_occlusionMode = AudOcclusionMode::HQ; mutable bool m_audioCullingEnabled; std::map m_soundBankInfoMap; diff --git a/src/AudObstructionOcclusion.cpp b/src/AudObstructionOcclusion.cpp index 6a7d269..3ee8b56 100644 --- a/src/AudObstructionOcclusion.cpp +++ b/src/AudObstructionOcclusion.cpp @@ -85,58 +85,22 @@ void AudObstructionOcclusion::Update( void AudObstructionOcclusion::QueryEmitter( AkGameObjectID emitterID, EmitterState& state ) { - AkDiffractionPathInfo paths[4]; - AkUInt32 numPaths = 4; + // Query a single direct path (1 ray) to check line-of-sight. + // With uMaxDiffractionOrder = 0 and EnableDiffraction = false, + // Wwise only returns the direct path with transmission loss if blocked. + AkDiffractionPathInfo path; + AkUInt32 numPaths = 1; AkVector64 listenerPos, emitterPos; AKRESULT result = AK::SpatialAudio::QueryDiffractionPaths( - emitterID, 0, listenerPos, emitterPos, paths, numPaths ); + emitterID, 0, listenerPos, emitterPos, &path, numPaths ); state.targetObstruction = 0.0f; state.targetOcclusion = 0.0f; if( result == AK_Success && numPaths > 0 ) { - float bestDiffraction = 1.0f; - float bestReportedDiffraction = 1.0f; - float strongestTransmission = 0.0f; - bool hasDiffractionPath = false; - bool hasReportedDiffraction = false; - bool hasBlockedDirectPath = false; - - for( AkUInt32 i = 0; i < numPaths; ++i ) - { - float diffraction = std::max( 0.0f, std::min( 1.0f, paths[i].diffraction ) ); - float transmission = std::max( 0.0f, std::min( 1.0f, paths[i].transmissionLoss ) ); - strongestTransmission = std::max( strongestTransmission, transmission ); - if( diffraction > 0.0f ) - { - hasReportedDiffraction = true; - bestReportedDiffraction = std::min( bestReportedDiffraction, diffraction ); - } - - // A path with at least one node represents diffraction around geometry edges. - // This is the gradual value the demo exposes while moving behind/away from a wall. - if( paths[i].nodeCount > 0 ) - { - hasDiffractionPath = true; - bestDiffraction = std::min( bestDiffraction, diffraction ); - } - else if( transmission > 0.0f ) - { - // No edge nodes and non-zero transmission indicates a blocked direct path. - hasBlockedDirectPath = true; - } - } - - if( hasDiffractionPath ) - state.targetObstruction = bestDiffraction; - else if( hasReportedDiffraction ) - state.targetObstruction = bestReportedDiffraction; - else - state.targetObstruction = hasBlockedDirectPath ? 1.0f : 0.0f; - - state.targetOcclusion = strongestTransmission; + state.targetOcclusion = std::max( 0.0f, std::min( 1.0f, path.transmissionLoss ) ); } } diff --git a/src/AudSettings.h b/src/AudSettings.h index 3e8fc29..196d841 100644 --- a/src/AudSettings.h +++ b/src/AudSettings.h @@ -9,11 +9,19 @@ #include "Audio2.h" +enum class AudOcclusionMode : int +{ + Basic = 0, // Manual 1-ray line-of-sight check, transmission only + HQ = 1 // Wwise Spatial Audio handles diffraction + transmission +}; + BLUE_CLASS( AudSettings ) : public IRoot { public: EXPOSE_TO_BLUE(); + int m_occlusionMode = static_cast( AudOcclusionMode::Basic ); + #if _WIN32 bool m_spatialAudioEnabled = true; std::wstring m_audioSrcPath = L"Media"; diff --git a/src/AudSettings_Blue.cpp b/src/AudSettings_Blue.cpp index 4f2233b..f00222c 100644 --- a/src/AudSettings_Blue.cpp +++ b/src/AudSettings_Blue.cpp @@ -53,5 +53,12 @@ const Be::ClassInfo* AudSettings::ExposeToBlue() Be::READWRITE ) MAP_ATTRIBUTE("essentialPath", m_essentialPath, "The path for essential media.", Be::READWRITE ) + MAP_ATTRIBUTE( + "occlusionMode", + m_occlusionMode, + "Controls spatial audio occlusion quality. 0 = Basic (1-ray transmission only, lower CPU), " + "1 = HQ (full diffraction + transmission via Wwise Spatial Audio). Must be set before Enable().", + Be::READWRITE + ) EXPOSURE_END() } From 0e664d1e16b20350e5710d78a7cd683be70a0e11 Mon Sep 17 00:00:00 2001 From: phevosccp <160875214+phevosccp@users.noreply.github.com> Date: Wed, 11 Mar 2026 14:26:54 +0000 Subject: [PATCH 31/48] 1. Adds a global occlusion OFF mode 2. Exposes global transmittion loss and the interpolated fade in/out timer value --- src/AudGeometry.cpp | 15 +++++++++++++-- src/AudManager.cpp | 20 +++++++++++++++++--- src/AudManager.h | 12 ++++++++++++ src/AudManager_Blue.cpp | 5 +++++ src/AudObstructionOcclusion.cpp | 14 ++++++++++++-- src/AudObstructionOcclusion.h | 17 +++++++++++++++++ src/AudSettings.h | 1 + src/AudSettings_Blue.cpp | 5 +++-- 8 files changed, 80 insertions(+), 9 deletions(-) diff --git a/src/AudGeometry.cpp b/src/AudGeometry.cpp index b69eaf6..c786c22 100644 --- a/src/AudGeometry.cpp +++ b/src/AudGeometry.cpp @@ -56,6 +56,13 @@ void AudGeometry::SetGeometry( { return; } + + // Off mode: No occlusion, don't register any geometry with Wwise. + if( !g_audioManager || g_audioManager->GetOcclusionMode() == AudOcclusionMode::Off ) + { + return; + } + CcpAutoMutex lock( s_mutex ); auto it = s_geometrySetRefCounts.find( geometrySetId ); @@ -67,7 +74,11 @@ void AudGeometry::SetGeometry( AkAcousticSurface surface; surface.strName = "default"; surface.textureID = AK_INVALID_UNIQUE_ID; - surface.transmissionLoss = 1.0f; + // HQ: global transmission loss handled by Wwise Spatial Audio. + // Basic: epsilon so Wwise detects blocking but doesn't audibly attenuate; + // AudObstructionOcclusion applies the actual occlusion via SetObjectObstructionAndOcclusion. + bool isHQ = ( g_audioManager->GetOcclusionMode() == AudOcclusionMode::HQ ); + surface.transmissionLoss = isHQ ? g_audioManager->GetGlobalTransmissionLoss() : 0.001f; AkGeometryParams params; params.Vertices = akVertices.data(); @@ -76,7 +87,7 @@ void AudGeometry::SetGeometry( params.NumTriangles = static_cast( akTriangles.size() ); params.Surfaces = &surface; params.NumSurfaces = 1; - params.EnableDiffraction = ( g_audioManager && g_audioManager->GetOcclusionMode() == AudOcclusionMode::HQ ); + params.EnableDiffraction = ( g_audioManager->GetOcclusionMode() == AudOcclusionMode::HQ ); params.EnableDiffractionOnBoundaryEdges = false; AKRESULT result = AK::SpatialAudio::SetGeometry( geometrySetId, params ); diff --git a/src/AudManager.cpp b/src/AudManager.cpp index b4a1910..05b6ae6 100644 --- a/src/AudManager.cpp +++ b/src/AudManager.cpp @@ -161,10 +161,13 @@ bool AudManager::Init() return false; } - if( !InitSpatialAudioGeometry() ) + if( m_occlusionMode != AudOcclusionMode::Off ) { - CCP_LOGERR( "Failed to initialize audio : Spatial Audio Geometry" ); - return false; + if( !InitSpatialAudioGeometry() ) + { + CCP_LOGERR( "Failed to initialize audio : Spatial Audio Geometry" ); + return false; + } } #ifndef AK_OPTIMIZED @@ -487,6 +490,17 @@ AudOcclusionMode AudManager::GetOcclusionMode() const return m_occlusionMode; } +float AudManager::GetGlobalTransmissionLoss() const +{ + return m_globalTransmissionLoss; +} + +void AudManager::SetGlobalTransmissionLoss( float value ) +{ + m_globalTransmissionLoss = std::max( 0.0f, std::min( 1.0f, value ) ); + m_obstructionOcclusion->SetOcclusionValue( m_globalTransmissionLoss ); +} + const bool AudManager::SpatialAudioIsSupported() { return s_systemSupportsSpatialAudio; diff --git a/src/AudManager.h b/src/AudManager.h index 380a1c1..7878d7d 100644 --- a/src/AudManager.h +++ b/src/AudManager.h @@ -117,6 +117,10 @@ BLUE_CLASS( AudManager ) : bool SetState( const std::wstring& stateGroup, const std::wstring& stateName ); // Returns the active occlusion mode (HQ or Basic). AudOcclusionMode GetOcclusionMode() const; + // Returns the global transmission loss [0.0-1.0]. In HQ mode controls geometry transmission loss, in Basic controls the occlusion level. + float GetGlobalTransmissionLoss() const; + // Sets the global transmission loss [0.0-1.0]. + void SetGlobalTransmissionLoss( float value ); // Can be called to see if the current platform supports spatial audio. const bool SpatialAudioIsSupported(); // Stop all currently playing sounds on all game objects. @@ -217,6 +221,8 @@ BLUE_CLASS( AudManager ) : bool m_spatialAudioEnabled; // Controls whether to use HQ (full diffraction) or Basic (1-ray occlusion) mode. AudOcclusionMode m_occlusionMode = AudOcclusionMode::HQ; + // Global transmission loss [0.0-1.0]: HQ = geometry transmissionLoss, Basic = fixed occlusion level. + float m_globalTransmissionLoss = 0.7f; mutable bool m_audioCullingEnabled; std::map m_soundBankInfoMap; @@ -289,6 +295,12 @@ BLUE_CLASS( AudManager ) : DELEGATE_SETTER( float, SetWeightMultiplier ) DELEGATE_SETTER( int, SetMaxAwakeGameObjects ) + // Occlusion delegates (forwarded to m_obstructionOcclusion) + float GetOcclusionFadeRate() const { return m_obstructionOcclusion->GetFadeRate(); } + void SetOcclusionFadeRate( float value ) { m_obstructionOcclusion->SetFadeRate( value ); } + float GetOcclusionRefreshInterval() const { return m_obstructionOcclusion->GetRefreshInterval(); } + void SetOcclusionRefreshInterval( float value ) { m_obstructionOcclusion->SetRefreshInterval( value ); } + // Undefine macros to prevent affecting code outside the AudManager class #undef DELEGATE_GETTER diff --git a/src/AudManager_Blue.cpp b/src/AudManager_Blue.cpp index eba1973..f06970a 100644 --- a/src/AudManager_Blue.cpp +++ b/src/AudManager_Blue.cpp @@ -22,6 +22,11 @@ const Be::ClassInfo* AudManager::ExposeToBlue() MAP_PROPERTY( "waitingOneShotWeight", GetWaitingOneShotWeight, SetWaitingOneShotWeight, "The weight applied to a game object if there is a one shot sound waiting to play.") MAP_PROPERTY( "visibleWeight", GetVisibleWeight, SetVisibleWeight, "The weight applied to a game object if it is visible to the listener.") MAP_PROPERTY( "playing2DWeight", GetPlaying2DWeight, SetPlaying2DWeight, "The weight applied to a game object if it is currently playing a 2D sound.") + + // Spatial audio occlusion settings + MAP_PROPERTY( "globalTransmissionLoss", GetGlobalTransmissionLoss, SetGlobalTransmissionLoss, "Global transmission loss [0.0-1.0]. In HQ mode sets geometry transmission loss. In Basic mode sets the fixed occlusion level when blocked.") + MAP_PROPERTY( "occlusionFadeRate", GetOcclusionFadeRate, SetOcclusionFadeRate, "Linear fade speed (units/sec) for occlusion transitions in Basic mode. Higher = faster fade.") + MAP_PROPERTY( "occlusionRefreshInterval", GetOcclusionRefreshInterval, SetOcclusionRefreshInterval, "How often each emitter is re-queried for occlusion state in Basic mode (seconds).") MAP_METHOD_AND_WRAP ( diff --git a/src/AudObstructionOcclusion.cpp b/src/AudObstructionOcclusion.cpp index 3ee8b56..1a1b57b 100644 --- a/src/AudObstructionOcclusion.cpp +++ b/src/AudObstructionOcclusion.cpp @@ -98,9 +98,9 @@ void AudObstructionOcclusion::QueryEmitter( AkGameObjectID emitterID, EmitterSta state.targetObstruction = 0.0f; state.targetOcclusion = 0.0f; - if( result == AK_Success && numPaths > 0 ) + if( result == AK_Success && numPaths > 0 && path.transmissionLoss > 0.0f ) { - state.targetOcclusion = std::max( 0.0f, std::min( 1.0f, path.transmissionLoss ) ); + state.targetOcclusion = m_occlusionValue; } } @@ -141,3 +141,13 @@ float AudObstructionOcclusion::GetFadeRate() const { return m_fadeRate; } + +void AudObstructionOcclusion::SetOcclusionValue( float value ) +{ + m_occlusionValue = std::max( 0.0f, std::min( 1.0f, value ) ); +} + +float AudObstructionOcclusion::GetOcclusionValue() const +{ + return m_occlusionValue; +} diff --git a/src/AudObstructionOcclusion.h b/src/AudObstructionOcclusion.h index 1f6b57b..f1f9cce 100644 --- a/src/AudObstructionOcclusion.h +++ b/src/AudObstructionOcclusion.h @@ -64,6 +64,20 @@ class AudObstructionOcclusion */ float GetFadeRate() const; + /** + * @brief Sets the fixed occlusion value applied when an emitter is blocked. + * + * @param value Occlusion level [0.0-1.0]. + */ + void SetOcclusionValue( float value ); + + /** + * @brief Gets the fixed occlusion value applied when an emitter is blocked. + * + * @return Current occlusion value. + */ + float GetOcclusionValue() const; + private: /** * @brief Cached runtime state for one emitter. @@ -93,6 +107,9 @@ class AudObstructionOcclusion /// Linear fade rate in second. float m_fadeRate = 2.0f; + /// Fixed occlusion value applied when emitter is blocked. + float m_occlusionValue = 0.7f; + /** * @brief Queries Wwise Spatial Audio and updates emitter target values. * diff --git a/src/AudSettings.h b/src/AudSettings.h index 196d841..16e5680 100644 --- a/src/AudSettings.h +++ b/src/AudSettings.h @@ -11,6 +11,7 @@ enum class AudOcclusionMode : int { + Off = -1, // Occlusion disabled, no geometry registered, spatial audio geometry not initialized Basic = 0, // Manual 1-ray line-of-sight check, transmission only HQ = 1 // Wwise Spatial Audio handles diffraction + transmission }; diff --git a/src/AudSettings_Blue.cpp b/src/AudSettings_Blue.cpp index f00222c..5bd1172 100644 --- a/src/AudSettings_Blue.cpp +++ b/src/AudSettings_Blue.cpp @@ -56,8 +56,9 @@ const Be::ClassInfo* AudSettings::ExposeToBlue() MAP_ATTRIBUTE( "occlusionMode", m_occlusionMode, - "Controls spatial audio occlusion quality. 0 = Basic (1-ray transmission only, lower CPU), " - "1 = HQ (full diffraction + transmission via Wwise Spatial Audio). Must be set before Enable().", + "Controls spatial audio occlusion quality. -1 = Off (no geometry, no occlusion), " + "0 = Basic (No diffraction raycasting only occlusion and interpolated fade in/out. Lower CPU cost), " + "1 = HQ (full diffraction + transmission ray casting. Higer CPU cost).", Be::READWRITE ) EXPOSURE_END() From fc0d74f69249ea824135ac7ab2a189d48fc7ea13 Mon Sep 17 00:00:00 2001 From: phevosccp <160875214+phevosccp@users.noreply.github.com> Date: Wed, 11 Mar 2026 14:46:09 +0000 Subject: [PATCH 32/48] doxygen update with modes --- src/AudGeometry.cpp | 5 +---- src/AudGeometry.h | 11 ++++++++--- src/AudObstructionOcclusion.h | 15 +++++++++++---- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/AudGeometry.cpp b/src/AudGeometry.cpp index c786c22..fe060e5 100644 --- a/src/AudGeometry.cpp +++ b/src/AudGeometry.cpp @@ -57,7 +57,6 @@ void AudGeometry::SetGeometry( return; } - // Off mode: No occlusion, don't register any geometry with Wwise. if( !g_audioManager || g_audioManager->GetOcclusionMode() == AudOcclusionMode::Off ) { return; @@ -74,9 +73,7 @@ void AudGeometry::SetGeometry( AkAcousticSurface surface; surface.strName = "default"; surface.textureID = AK_INVALID_UNIQUE_ID; - // HQ: global transmission loss handled by Wwise Spatial Audio. - // Basic: epsilon so Wwise detects blocking but doesn't audibly attenuate; - // AudObstructionOcclusion applies the actual occlusion via SetObjectObstructionAndOcclusion. + bool isHQ = ( g_audioManager->GetOcclusionMode() == AudOcclusionMode::HQ ); surface.transmissionLoss = isHQ ? g_audioManager->GetGlobalTransmissionLoss() : 0.001f; diff --git a/src/AudGeometry.h b/src/AudGeometry.h index b3bc604..bee3f81 100644 --- a/src/AudGeometry.h +++ b/src/AudGeometry.h @@ -15,15 +15,20 @@ /** - * @brief Manages Wwise Spatial Audio geometry sets and geometry instances for - * geometric reflection and diffraction processing. + * @brief Manages Wwise Spatial Audio geometry sets and geometry instances. * - * AudGeometry implements the ITr2AudGeometry interface to submit geometry to + * Implements the ITr2AudGeometry interface to submit geometry to * AK::SpatialAudio. A geometry set is a logical set of vertices, triangles, * and acoustic surfaces (see AkGeometryParams). A geometry instance is a unique * placement of a geometry set in the world with a specified transform — position, * orientation, and scale (see AkGeometryInstanceParams). * + * Behaviour depends on the @c AudOcclusionMode: + * - **Off**: geometry registration is skipped entirely. + * - **Basic**: geometry registered with @c EnableDiffraction=false and a near-zero + * @c transmissionLoss so Wwise can detect blocking for AudObstructionOcclusion. + * - **HQ**: geometry registered with @c EnableDiffraction=true and + * @c transmissionLoss set to @c globalTransmissionLoss replacing basic occlusion. */ BLUE_CLASS(AudGeometry) : public ITr2AudGeometry diff --git a/src/AudObstructionOcclusion.h b/src/AudObstructionOcclusion.h index f1f9cce..d7851e2 100644 --- a/src/AudObstructionOcclusion.h +++ b/src/AudObstructionOcclusion.h @@ -13,11 +13,18 @@ #include "IPrioritizedObject.h" /** - * @brief Tracks and applies per-emitter obstruction/occlusion values. + * @brief Lightweight per-emitter occlusion for Basic mode only. * - * This class queries Wwise Spatial Audio diffraction paths, derives target - * obstruction/occlusion values, smooths them over time, and writes the - * smoothed values back to Wwise each tick. + * Used exclusively when @c AudOcclusionMode::Basic is active. + * Casts a single ray per emitter via Wwise Spatial Audio's + * @c QueryDiffractionPaths (with diffraction disabled) to detect + * line-of-sight blocking, then feeds a fixed occlusion value into + * @c AK::SoundEngine::SetObjectObstructionAndOcclusion(). + * A linear interpolation fade smooths transitions between blocked + * and clear states. + * + * Not used in HQ mode (Wwise Spatial Audio handles diffraction and + * transmission directly) or Off mode (no occlusion processing). */ class AudObstructionOcclusion { From bc932415087af73aa662bf687bb3ad102abb28a5 Mon Sep 17 00:00:00 2001 From: phevosccp <160875214+phevosccp@users.noreply.github.com> Date: Wed, 11 Mar 2026 14:50:24 +0000 Subject: [PATCH 33/48] set occlusion by default off --- src/AudSettings.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AudSettings.h b/src/AudSettings.h index 16e5680..cbde2a1 100644 --- a/src/AudSettings.h +++ b/src/AudSettings.h @@ -21,7 +21,7 @@ BLUE_CLASS( AudSettings ) : public IRoot public: EXPOSE_TO_BLUE(); - int m_occlusionMode = static_cast( AudOcclusionMode::Basic ); + int m_occlusionMode = static_cast( AudOcclusionMode::Off ); #if _WIN32 bool m_spatialAudioEnabled = true; From 0a446dafcd65555db54953610c5409d6ed77a0bb Mon Sep 17 00:00:00 2001 From: phevosccp <160875214+phevosccp@users.noreply.github.com> Date: Wed, 11 Mar 2026 14:54:52 +0000 Subject: [PATCH 34/48] update safeguard --- src/AudManager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/AudManager.cpp b/src/AudManager.cpp index 05b6ae6..9e783a3 100644 --- a/src/AudManager.cpp +++ b/src/AudManager.cpp @@ -448,8 +448,8 @@ bool AudManager::InitSpatialAudioGeometry() return false; } - const char* modeName = ( m_occlusionMode == AudOcclusionMode::HQ ) ? "HQ (diffraction + transmission)" : "Basic (transmission only)"; - CCP_LOG_CH( s_ch, "Wwise Spatial Audio initialized in %s mode", modeName ); + const char* modeName = ( m_occlusionMode == AudOcclusionMode::HQ ) ? "HQ (diffraction + transmission)" : "Basic (occlusion only)"; + CCP_LOG_CH( s_ch, "Wwise Spatial Audio Geometry initialized in %s mode", modeName ); return true; } From 1026a22636cc32fe360b723ee837040934472a08 Mon Sep 17 00:00:00 2001 From: phevosccp <160875214+phevosccp@users.noreply.github.com> Date: Wed, 11 Mar 2026 15:37:02 +0000 Subject: [PATCH 35/48] fix listener conversion --- src/Utilities.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Utilities.h b/src/Utilities.h index 6d2dbeb..573c8ec 100644 --- a/src/Utilities.h +++ b/src/Utilities.h @@ -35,10 +35,8 @@ class RH2LH front.X *= -1.f; front.Y *= -1.f; - front.Z *= -1.f; pos.Z *= -1.f; - front.Z *= -1.f; top.Z *= -1.f; listenerLH->Set(pos, front, top); From f1b8a0627b83947cd4cde2543b00169d3f803577 Mon Sep 17 00:00:00 2001 From: phevosccp <160875214+phevosccp@users.noreply.github.com> Date: Wed, 11 Mar 2026 15:42:31 +0000 Subject: [PATCH 36/48] make occ mode initialization default to off in AudManager --- src/AudManager.cpp | 1 + src/AudManager.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/AudManager.cpp b/src/AudManager.cpp index 9e783a3..1114d7b 100644 --- a/src/AudManager.cpp +++ b/src/AudManager.cpp @@ -70,6 +70,7 @@ AudManager::AudManager( IRoot* lockobj ) : m_asyncOpen( true ), m_log(), m_spatialAudioEnabled( true ), + m_occlusionMode( AudOcclusionMode::Off ), m_moniteredParametersMapMutex( "AudManager", "m_monitoredParametersMapMutex" ), m_soundBankMutex( "AudManager", "m_soundBankMutex" ), m_isProfilerCapturing( false ), diff --git a/src/AudManager.h b/src/AudManager.h index 7878d7d..c03a1e1 100644 --- a/src/AudManager.h +++ b/src/AudManager.h @@ -220,7 +220,7 @@ BLUE_CLASS( AudManager ) : // Signals whether Carbon Audio's spatial audio features are enabled. If the user currently doesn't have an active spatial audio endpoint then output will still be in stereo. bool m_spatialAudioEnabled; // Controls whether to use HQ (full diffraction) or Basic (1-ray occlusion) mode. - AudOcclusionMode m_occlusionMode = AudOcclusionMode::HQ; + AudOcclusionMode m_occlusionMode; // Global transmission loss [0.0-1.0]: HQ = geometry transmissionLoss, Basic = fixed occlusion level. float m_globalTransmissionLoss = 0.7f; mutable bool m_audioCullingEnabled; From 67d587f6df5b38e25e7f282963c3b386bb29426a Mon Sep 17 00:00:00 2001 From: phevosccp <160875214+phevosccp@users.noreply.github.com> Date: Fri, 13 Mar 2026 14:05:13 +0000 Subject: [PATCH 37/48] apply transmittion directly in basic mode --- src/AudGameObjResource.cpp | 2 +- src/AudGeometry.cpp | 3 +-- src/AudObstructionOcclusion.cpp | 3 --- src/AudObstructionOcclusion.h | 2 +- src/AudSettings.h | 2 +- 5 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/AudGameObjResource.cpp b/src/AudGameObjResource.cpp index 47eae7c..27cdfa7 100644 --- a/src/AudGameObjResource.cpp +++ b/src/AudGameObjResource.cpp @@ -581,7 +581,7 @@ void AudGameObjResource::Wake() return; } - RegisterWwiseObject(); + RegisterWwiseObject(); SetPositionHelper( Vector3( 1,0,0 ), Vector3( 0,1,0 ), m_position ); m_culled = false; if ( m_waitingOneShotInRange.second != L"" && m_listenerInRange ) diff --git a/src/AudGeometry.cpp b/src/AudGeometry.cpp index fe060e5..e230741 100644 --- a/src/AudGeometry.cpp +++ b/src/AudGeometry.cpp @@ -74,8 +74,7 @@ void AudGeometry::SetGeometry( surface.strName = "default"; surface.textureID = AK_INVALID_UNIQUE_ID; - bool isHQ = ( g_audioManager->GetOcclusionMode() == AudOcclusionMode::HQ ); - surface.transmissionLoss = isHQ ? g_audioManager->GetGlobalTransmissionLoss() : 0.001f; + surface.transmissionLoss = g_audioManager->GetGlobalTransmissionLoss(); AkGeometryParams params; params.Vertices = akVertices.data(); diff --git a/src/AudObstructionOcclusion.cpp b/src/AudObstructionOcclusion.cpp index 1a1b57b..842e5d0 100644 --- a/src/AudObstructionOcclusion.cpp +++ b/src/AudObstructionOcclusion.cpp @@ -85,9 +85,6 @@ void AudObstructionOcclusion::Update( void AudObstructionOcclusion::QueryEmitter( AkGameObjectID emitterID, EmitterState& state ) { - // Query a single direct path (1 ray) to check line-of-sight. - // With uMaxDiffractionOrder = 0 and EnableDiffraction = false, - // Wwise only returns the direct path with transmission loss if blocked. AkDiffractionPathInfo path; AkUInt32 numPaths = 1; AkVector64 listenerPos, emitterPos; diff --git a/src/AudObstructionOcclusion.h b/src/AudObstructionOcclusion.h index d7851e2..b553c13 100644 --- a/src/AudObstructionOcclusion.h +++ b/src/AudObstructionOcclusion.h @@ -92,7 +92,7 @@ class AudObstructionOcclusion struct EmitterState { float obstruction = 0.0f; - float occlusion = 0.0f; + float occlusion = 0.0f; float targetObstruction = 0.0f; float targetOcclusion = 0.0f; float nextQueryTime = 0.0f; diff --git a/src/AudSettings.h b/src/AudSettings.h index cbde2a1..16e5680 100644 --- a/src/AudSettings.h +++ b/src/AudSettings.h @@ -21,7 +21,7 @@ BLUE_CLASS( AudSettings ) : public IRoot public: EXPOSE_TO_BLUE(); - int m_occlusionMode = static_cast( AudOcclusionMode::Off ); + int m_occlusionMode = static_cast( AudOcclusionMode::Basic ); #if _WIN32 bool m_spatialAudioEnabled = true; From 4333c64c62d110aadc532f5d88c791050fed4eba Mon Sep 17 00:00:00 2001 From: phevosccp <160875214+phevosccp@users.noreply.github.com> Date: Fri, 13 Mar 2026 15:13:57 +0000 Subject: [PATCH 38/48] limit settings for basic occlusion even further --- src/AudGameObjResource.cpp | 2 +- src/AudManager.cpp | 3 ++- src/AudObstructionOcclusion.h | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/AudGameObjResource.cpp b/src/AudGameObjResource.cpp index 27cdfa7..7ae4b50 100644 --- a/src/AudGameObjResource.cpp +++ b/src/AudGameObjResource.cpp @@ -398,7 +398,7 @@ bool AudGameObjResource::Initialize() RegisterWwiseObject(); SetPositionHelper( Vector3( 1,0,0 ), Vector3( 0,1,0 ), m_position ); - if ( !m_eventName.empty() ) + if ( !m_eventName.empty() ) { PostEvent( m_eventName ); } diff --git a/src/AudManager.cpp b/src/AudManager.cpp index 1114d7b..5313bd1 100644 --- a/src/AudManager.cpp +++ b/src/AudManager.cpp @@ -440,7 +440,8 @@ bool AudManager::InitSpatialAudioGeometry() spatialSettings.uNumberOfPrimaryRays = 1; spatialSettings.uMaxDiffractionOrder = 0; spatialSettings.bCalcEmitterVirtualPosition = false; - spatialSettings.fCPULimitPercentage = 10; + spatialSettings.fCPULimitPercentage = 5; + spatialSettings.uLoadBalancingSpread = 2; } if( AK::SpatialAudio::Init( spatialSettings ) != AK_Success ) diff --git a/src/AudObstructionOcclusion.h b/src/AudObstructionOcclusion.h index b553c13..11d0087 100644 --- a/src/AudObstructionOcclusion.h +++ b/src/AudObstructionOcclusion.h @@ -111,8 +111,8 @@ class AudObstructionOcclusion /// Per-emitter query cadence in seconds. float m_refreshInterval = 0.2f; - /// Linear fade rate in second. - float m_fadeRate = 2.0f; + /// Linear fade rate in units per second. + float m_fadeRate = 1.0f; /// Fixed occlusion value applied when emitter is blocked. float m_occlusionValue = 0.7f; From 14818c586784721b7baa375742306f61b94edb78 Mon Sep 17 00:00:00 2001 From: phevosccp <160875214+phevosccp@users.noreply.github.com> Date: Fri, 13 Mar 2026 16:47:12 +0000 Subject: [PATCH 39/48] remove basic mode and keep only On/Off --- src/AudGeometry.cpp | 2 +- src/AudGeometry.h | 9 +- src/AudManager.cpp | 38 ++------ src/AudManager.h | 16 +--- src/AudManager_Blue.cpp | 4 +- src/AudObstructionOcclusion.cpp | 150 -------------------------------- src/AudObstructionOcclusion.h | 140 ----------------------------- src/AudSettings.h | 7 +- src/AudSettings_Blue.cpp | 5 +- 9 files changed, 19 insertions(+), 352 deletions(-) delete mode 100644 src/AudObstructionOcclusion.cpp delete mode 100644 src/AudObstructionOcclusion.h diff --git a/src/AudGeometry.cpp b/src/AudGeometry.cpp index e230741..de3db80 100644 --- a/src/AudGeometry.cpp +++ b/src/AudGeometry.cpp @@ -83,7 +83,7 @@ void AudGeometry::SetGeometry( params.NumTriangles = static_cast( akTriangles.size() ); params.Surfaces = &surface; params.NumSurfaces = 1; - params.EnableDiffraction = ( g_audioManager->GetOcclusionMode() == AudOcclusionMode::HQ ); + params.EnableDiffraction = true; params.EnableDiffractionOnBoundaryEdges = false; AKRESULT result = AK::SpatialAudio::SetGeometry( geometrySetId, params ); diff --git a/src/AudGeometry.h b/src/AudGeometry.h index bee3f81..02fb0a1 100644 --- a/src/AudGeometry.h +++ b/src/AudGeometry.h @@ -23,12 +23,9 @@ * placement of a geometry set in the world with a specified transform — position, * orientation, and scale (see AkGeometryInstanceParams). * - * Behaviour depends on the @c AudOcclusionMode: - * - **Off**: geometry registration is skipped entirely. - * - **Basic**: geometry registered with @c EnableDiffraction=false and a near-zero - * @c transmissionLoss so Wwise can detect blocking for AudObstructionOcclusion. - * - **HQ**: geometry registered with @c EnableDiffraction=true and - * @c transmissionLoss set to @c globalTransmissionLoss replacing basic occlusion. + * When @c AudOcclusionMode::On, geometry is registered with diffraction and + * transmission enabled. When @c AudOcclusionMode::Off, geometry registration + * is skipped entirely. */ BLUE_CLASS(AudGeometry) : public ITr2AudGeometry diff --git a/src/AudManager.cpp b/src/AudManager.cpp index 5313bd1..fd9f9e2 100644 --- a/src/AudManager.cpp +++ b/src/AudManager.cpp @@ -78,14 +78,12 @@ AudManager::AudManager( IRoot* lockobj ) : { // Initialize sound prioritization system m_soundPrioritization = new SoundPrioritization(); - m_obstructionOcclusion = new AudObstructionOcclusion(); } AudManager::~AudManager() { // Clean up sound prioritization system delete m_soundPrioritization; - delete m_obstructionOcclusion; if( g_audioInitialized ) { @@ -102,14 +100,6 @@ void AudManager::Process() m_soundPrioritization->CullAudio(); } - // In Basic mode, manually query 1 ray per emitter and apply occlusion. - // In HQ mode, Wwise Spatial Audio handles diffraction + transmission automatically. - if( m_occlusionMode == AudOcclusionMode::Basic ) - { - m_obstructionOcclusion->Update( LISTENER_GAME_OBJ_ID, - m_soundPrioritization->GetPrioritizedAudioObjects() ); - } - // Process bank requests, events, positions, RTPC, etc. AK::SoundEngine::RenderAudio(); @@ -162,7 +152,7 @@ bool AudManager::Init() return false; } - if( m_occlusionMode != AudOcclusionMode::Off ) + if( m_occlusionMode == AudOcclusionMode::On ) { if( !InitSpatialAudioGeometry() ) { @@ -416,7 +406,6 @@ bool AudManager::InitSpatialAudioGeometry() { AkSpatialAudioInitSettings spatialSettings; - // Both modes need geometric transmission enabled for line-of-sight checks. spatialSettings.bEnableGeometricDiffractionAndTransmission = true; spatialSettings.uMaxReflectionOrder = 0; @@ -425,24 +414,9 @@ bool AudManager::InitSpatialAudioGeometry() spatialSettings.uMaxSoundPropagationDepth = 1; spatialSettings.fMovementThreshold = 100; - if( m_occlusionMode == AudOcclusionMode::HQ ) - { - // HQ: Wwise Spatial Audio handles diffraction + transmission automatically. - spatialSettings.uMaxDiffractionOrder = 4; - spatialSettings.bCalcEmitterVirtualPosition = true; - spatialSettings.fCPULimitPercentage = 20; - } - else - { - // Basic: No diffraction, only transmission/line-of-sight raycasting. - // AudObstructionOcclusion queries 1 ray per emitter and feeds the result - // into AK::SoundEngine::SetObjectObstructionAndOcclusion(). - spatialSettings.uNumberOfPrimaryRays = 1; - spatialSettings.uMaxDiffractionOrder = 0; - spatialSettings.bCalcEmitterVirtualPosition = false; - spatialSettings.fCPULimitPercentage = 5; - spatialSettings.uLoadBalancingSpread = 2; - } + spatialSettings.uMaxDiffractionOrder = 4; + spatialSettings.bCalcEmitterVirtualPosition = true; + spatialSettings.fCPULimitPercentage = 20; if( AK::SpatialAudio::Init( spatialSettings ) != AK_Success ) { @@ -450,8 +424,7 @@ bool AudManager::InitSpatialAudioGeometry() return false; } - const char* modeName = ( m_occlusionMode == AudOcclusionMode::HQ ) ? "HQ (diffraction + transmission)" : "Basic (occlusion only)"; - CCP_LOG_CH( s_ch, "Wwise Spatial Audio Geometry initialized in %s mode", modeName ); + CCP_LOG_CH( s_ch, "Wwise Spatial Audio Geometry initialized" ); return true; } @@ -500,7 +473,6 @@ float AudManager::GetGlobalTransmissionLoss() const void AudManager::SetGlobalTransmissionLoss( float value ) { m_globalTransmissionLoss = std::max( 0.0f, std::min( 1.0f, value ) ); - m_obstructionOcclusion->SetOcclusionValue( m_globalTransmissionLoss ); } const bool AudManager::SpatialAudioIsSupported() diff --git a/src/AudManager.h b/src/AudManager.h index c03a1e1..86af5f0 100644 --- a/src/AudManager.h +++ b/src/AudManager.h @@ -13,7 +13,6 @@ #include "Audio2.h" #include "AudSettings.h" #include "AudListener.h" -#include "AudObstructionOcclusion.h" #include "SoundPrioritization.h" #include "CCPFilePackageLowLevelIO.h" #include @@ -115,9 +114,9 @@ BLUE_CLASS( AudManager ) : bool SetGlobalRTPC( const std::wstring& rtpcName, float value ); // Set a global state in Wwise. bool SetState( const std::wstring& stateGroup, const std::wstring& stateName ); - // Returns the active occlusion mode (HQ or Basic). + // Returns the active occlusion mode (On or Off). AudOcclusionMode GetOcclusionMode() const; - // Returns the global transmission loss [0.0-1.0]. In HQ mode controls geometry transmission loss, in Basic controls the occlusion level. + // Returns the global transmission loss [0.0-1.0] used for geometry surfaces. float GetGlobalTransmissionLoss() const; // Sets the global transmission loss [0.0-1.0]. void SetGlobalTransmissionLoss( float value ); @@ -219,9 +218,9 @@ BLUE_CLASS( AudManager ) : bool m_asyncOpen; // Signals whether Carbon Audio's spatial audio features are enabled. If the user currently doesn't have an active spatial audio endpoint then output will still be in stereo. bool m_spatialAudioEnabled; - // Controls whether to use HQ (full diffraction) or Basic (1-ray occlusion) mode. + // Controls whether occlusion is On or Off. AudOcclusionMode m_occlusionMode; - // Global transmission loss [0.0-1.0]: HQ = geometry transmissionLoss, Basic = fixed occlusion level. + // Global transmission loss [0.0-1.0] applied to geometry surfaces. float m_globalTransmissionLoss = 0.7f; mutable bool m_audioCullingEnabled; @@ -237,7 +236,6 @@ BLUE_CLASS( AudManager ) : CcpMutex m_moniteredParametersMapMutex; SoundPrioritization* m_soundPrioritization; - AudObstructionOcclusion* m_obstructionOcclusion; // A boolean for the state of the profiler capture bool m_isProfilerCapturing; @@ -295,12 +293,6 @@ BLUE_CLASS( AudManager ) : DELEGATE_SETTER( float, SetWeightMultiplier ) DELEGATE_SETTER( int, SetMaxAwakeGameObjects ) - // Occlusion delegates (forwarded to m_obstructionOcclusion) - float GetOcclusionFadeRate() const { return m_obstructionOcclusion->GetFadeRate(); } - void SetOcclusionFadeRate( float value ) { m_obstructionOcclusion->SetFadeRate( value ); } - float GetOcclusionRefreshInterval() const { return m_obstructionOcclusion->GetRefreshInterval(); } - void SetOcclusionRefreshInterval( float value ) { m_obstructionOcclusion->SetRefreshInterval( value ); } - // Undefine macros to prevent affecting code outside the AudManager class #undef DELEGATE_GETTER diff --git a/src/AudManager_Blue.cpp b/src/AudManager_Blue.cpp index f06970a..d2cd14d 100644 --- a/src/AudManager_Blue.cpp +++ b/src/AudManager_Blue.cpp @@ -24,9 +24,7 @@ const Be::ClassInfo* AudManager::ExposeToBlue() MAP_PROPERTY( "playing2DWeight", GetPlaying2DWeight, SetPlaying2DWeight, "The weight applied to a game object if it is currently playing a 2D sound.") // Spatial audio occlusion settings - MAP_PROPERTY( "globalTransmissionLoss", GetGlobalTransmissionLoss, SetGlobalTransmissionLoss, "Global transmission loss [0.0-1.0]. In HQ mode sets geometry transmission loss. In Basic mode sets the fixed occlusion level when blocked.") - MAP_PROPERTY( "occlusionFadeRate", GetOcclusionFadeRate, SetOcclusionFadeRate, "Linear fade speed (units/sec) for occlusion transitions in Basic mode. Higher = faster fade.") - MAP_PROPERTY( "occlusionRefreshInterval", GetOcclusionRefreshInterval, SetOcclusionRefreshInterval, "How often each emitter is re-queried for occlusion state in Basic mode (seconds).") + MAP_PROPERTY( "globalTransmissionLoss", GetGlobalTransmissionLoss, SetGlobalTransmissionLoss, "Global transmission loss [0.0-1.0] applied to geometry surfaces when occlusion is enabled.") MAP_METHOD_AND_WRAP ( diff --git a/src/AudObstructionOcclusion.cpp b/src/AudObstructionOcclusion.cpp deleted file mode 100644 index 842e5d0..0000000 --- a/src/AudObstructionOcclusion.cpp +++ /dev/null @@ -1,150 +0,0 @@ -#include "stdafx.h" -#include "AudObstructionOcclusion.h" -#include "Audio2.h" -#include -#include - -AudObstructionOcclusion::AudObstructionOcclusion() - : m_lastUpdateTime( std::chrono::steady_clock::now() ) -{ -} - -void AudObstructionOcclusion::Update( - AkGameObjectID listenerID, - const std::vector& gameObjects ) -{ - if( !g_audioInitialized ) - return; - - auto now = std::chrono::steady_clock::now(); - float dt = std::chrono::duration( now - m_lastUpdateTime ).count(); - - m_lastUpdateTime = now; - dt = std::min( dt, 0.1f ); - m_time += dt; - - for( const IPrioritizedObject* obj : gameObjects ) - { - AkGameObjectID emitterID = obj->GetID(); - - if( emitterID == listenerID ) - continue; - - if( obj->IsCulled() ) - { - m_emitters.erase( emitterID ); - continue; - } - - EmitterState& state = m_emitters[emitterID]; - - if( !state.initialized ) - { - QueryEmitter( emitterID, state ); - state.obstruction = state.targetObstruction; - state.occlusion = state.targetOcclusion; - - // Stagger future queries so emitters don't all query on the same frame. - state.nextQueryTime = m_time + m_refreshInterval * ( (float)std::rand() / RAND_MAX ); - state.initialized = true; - } - else - { - if( m_time >= state.nextQueryTime ) - { - QueryEmitter( emitterID, state ); - state.nextQueryTime = m_time + m_refreshInterval; - } - - float fadeStep = m_fadeRate * dt; - - if( state.obstruction < state.targetObstruction ) - state.obstruction = std::min( state.obstruction + fadeStep, state.targetObstruction ); - else if( state.obstruction > state.targetObstruction ) - state.obstruction = std::max( state.obstruction - fadeStep, state.targetObstruction ); - - if( state.occlusion < state.targetOcclusion ) - state.occlusion = std::min( state.occlusion + fadeStep, state.targetOcclusion ); - else if( state.occlusion > state.targetOcclusion ) - state.occlusion = std::max( state.occlusion - fadeStep, state.targetOcclusion ); - } - - float obstruction = std::max( 0.0f, std::min( 1.0f, state.obstruction ) ); - float occlusion = std::max( 0.0f, std::min( 1.0f, state.occlusion ) ); - - AK::SoundEngine::SetObjectObstructionAndOcclusion( - emitterID, listenerID, obstruction, occlusion ); - } - - if( ++m_cleanupCounter >= kCleanupEveryNFrames ) - { - m_cleanupCounter = 0; - CleanupStaleEmitters( gameObjects ); - } -} - -void AudObstructionOcclusion::QueryEmitter( AkGameObjectID emitterID, EmitterState& state ) -{ - AkDiffractionPathInfo path; - AkUInt32 numPaths = 1; - AkVector64 listenerPos, emitterPos; - - AKRESULT result = AK::SpatialAudio::QueryDiffractionPaths( - emitterID, 0, listenerPos, emitterPos, &path, numPaths ); - - state.targetObstruction = 0.0f; - state.targetOcclusion = 0.0f; - - if( result == AK_Success && numPaths > 0 && path.transmissionLoss > 0.0f ) - { - state.targetOcclusion = m_occlusionValue; - } -} - -void AudObstructionOcclusion::CleanupStaleEmitters( - const std::vector& gameObjects ) -{ - std::unordered_set activeIDs; - activeIDs.reserve( gameObjects.size() ); - for( IPrioritizedObject* obj : gameObjects ) - activeIDs.insert( obj->GetID() ); - - auto it = m_emitters.begin(); - while( it != m_emitters.end() ) - { - if( activeIDs.find( it->first ) == activeIDs.end() ) - it = m_emitters.erase( it ); - else - ++it; - } -} - -void AudObstructionOcclusion::SetRefreshInterval( float seconds ) -{ - m_refreshInterval = std::max( 0.0f, seconds ); -} - -float AudObstructionOcclusion::GetRefreshInterval() const -{ - return m_refreshInterval; -} - -void AudObstructionOcclusion::SetFadeRate( float unitsPerSecond ) -{ - m_fadeRate = std::max( 0.0f, unitsPerSecond ); -} - -float AudObstructionOcclusion::GetFadeRate() const -{ - return m_fadeRate; -} - -void AudObstructionOcclusion::SetOcclusionValue( float value ) -{ - m_occlusionValue = std::max( 0.0f, std::min( 1.0f, value ) ); -} - -float AudObstructionOcclusion::GetOcclusionValue() const -{ - return m_occlusionValue; -} diff --git a/src/AudObstructionOcclusion.h b/src/AudObstructionOcclusion.h deleted file mode 100644 index 11d0087..0000000 --- a/src/AudObstructionOcclusion.h +++ /dev/null @@ -1,140 +0,0 @@ -//////////////////////////////////////////////////////////// -// -// Creator: Phevos Rinis -// Creation Date: Feb 2026 -// Copyright (c) 2026 CCP Games -// - -#pragma once - -#include -#include -#include -#include "IPrioritizedObject.h" - -/** - * @brief Lightweight per-emitter occlusion for Basic mode only. - * - * Used exclusively when @c AudOcclusionMode::Basic is active. - * Casts a single ray per emitter via Wwise Spatial Audio's - * @c QueryDiffractionPaths (with diffraction disabled) to detect - * line-of-sight blocking, then feeds a fixed occlusion value into - * @c AK::SoundEngine::SetObjectObstructionAndOcclusion(). - * A linear interpolation fade smooths transitions between blocked - * and clear states. - * - * Not used in HQ mode (Wwise Spatial Audio handles diffraction and - * transmission directly) or Off mode (no occlusion processing). - */ -class AudObstructionOcclusion -{ -public: - /** - * @brief Constructor and init timing state. - */ - AudObstructionOcclusion(); - - /** - * @brief Updates obstruction/occlusion for the current active emitter set. - * - * @param listenerID Game object ID of the active listener. - * @param gameObjects Active prioritized audio objects to evaluate. - */ - void Update( AkGameObjectID listenerID, - const std::vector& gameObjects ); - - /** - * @brief Sets how often each emitter is re-queried from Spatial Audio. - * - * @param seconds Query interval in seconds. - */ - void SetRefreshInterval( float seconds ); - - /** - * @brief Gets the per-emitter Spatial Audio query interval in seconds. - * - * @return Current refresh interval in seconds. - */ - float GetRefreshInterval() const; - - /** - * @brief Sets linear smoothing speed toward target values. - * - * @param unitsPerSecond Fade speed in units per second. - */ - void SetFadeRate( float unitsPerSecond ); - - /** - * @brief Gets linear smoothing speed toward target values. - * - * @return Current fade rate in units per second. - */ - float GetFadeRate() const; - - /** - * @brief Sets the fixed occlusion value applied when an emitter is blocked. - * - * @param value Occlusion level [0.0-1.0]. - */ - void SetOcclusionValue( float value ); - - /** - * @brief Gets the fixed occlusion value applied when an emitter is blocked. - * - * @return Current occlusion value. - */ - float GetOcclusionValue() const; - -private: - /** - * @brief Cached runtime state for one emitter. - */ - struct EmitterState - { - float obstruction = 0.0f; - float occlusion = 0.0f; - float targetObstruction = 0.0f; - float targetOcclusion = 0.0f; - float nextQueryTime = 0.0f; - bool initialized = false; - }; - - /// Active emitter state indexed by game object ID. - std::unordered_map m_emitters; - - /// local time accumulator in seconds. - float m_time = 0.0f; - - /// Timestamp from previous update. - std::chrono::steady_clock::time_point m_lastUpdateTime; - - /// Per-emitter query cadence in seconds. - float m_refreshInterval = 0.2f; - - /// Linear fade rate in units per second. - float m_fadeRate = 1.0f; - - /// Fixed occlusion value applied when emitter is blocked. - float m_occlusionValue = 0.7f; - - /** - * @brief Queries Wwise Spatial Audio and updates emitter target values. - * - * @param emitterID Emitter game object ID. - * @param state Mutable state to update with query results. - */ - void QueryEmitter( AkGameObjectID emitterID, EmitterState& state ); - - /** - * @brief Removes cached emitter states not present in the active set. - * - * @param gameObjects Active prioritized audio objects. - */ - void CleanupStaleEmitters( const std::vector& gameObjects ); - - /// Frame counter used to schedule periodic stale-state cleanup. - int m_cleanupCounter = 0; - - /// Number of update frames between stale-state cleanup passes. - static constexpr int kCleanupEveryNFrames = 120; -}; diff --git a/src/AudSettings.h b/src/AudSettings.h index 16e5680..c6e38e8 100644 --- a/src/AudSettings.h +++ b/src/AudSettings.h @@ -11,9 +11,8 @@ enum class AudOcclusionMode : int { - Off = -1, // Occlusion disabled, no geometry registered, spatial audio geometry not initialized - Basic = 0, // Manual 1-ray line-of-sight check, transmission only - HQ = 1 // Wwise Spatial Audio handles diffraction + transmission + Off = 0, // Occlusion disabled, no geometry registered, spatial audio geometry not initialized + On = 1 // Wwise Spatial Audio handles diffraction + transmission }; BLUE_CLASS( AudSettings ) : public IRoot @@ -21,7 +20,7 @@ BLUE_CLASS( AudSettings ) : public IRoot public: EXPOSE_TO_BLUE(); - int m_occlusionMode = static_cast( AudOcclusionMode::Basic ); + int m_occlusionMode = static_cast( AudOcclusionMode::Off ); #if _WIN32 bool m_spatialAudioEnabled = true; diff --git a/src/AudSettings_Blue.cpp b/src/AudSettings_Blue.cpp index 5bd1172..ef25659 100644 --- a/src/AudSettings_Blue.cpp +++ b/src/AudSettings_Blue.cpp @@ -56,9 +56,8 @@ const Be::ClassInfo* AudSettings::ExposeToBlue() MAP_ATTRIBUTE( "occlusionMode", m_occlusionMode, - "Controls spatial audio occlusion quality. -1 = Off (no geometry, no occlusion), " - "0 = Basic (No diffraction raycasting only occlusion and interpolated fade in/out. Lower CPU cost), " - "1 = HQ (full diffraction + transmission ray casting. Higer CPU cost).", + "Controls spatial audio occlusion. 0 = Off (no geometry, no occlusion), " + "1 = On ( Wwise Spatial Audio Diffraction + transmission ).", Be::READWRITE ) EXPOSURE_END() From c264dd56b07adafba2452550bc3ea5c136545c63 Mon Sep 17 00:00:00 2001 From: phevosccp <160875214+phevosccp@users.noreply.github.com> Date: Fri, 13 Mar 2026 17:10:15 +0000 Subject: [PATCH 40/48] Full exposure of spatial audio occlusion settings --- src/AudManager.cpp | 38 +++---- src/AudManager.h | 57 +++++++++- src/AudManager_Blue.cpp | 15 ++- src/AudSettings.h | 8 -- src/AudSettings_Blue.cpp | 7 -- src/SpatialAudioSettings.cpp | 58 ++++++++++ src/SpatialAudioSettings.h | 202 +++++++++++++++++++++++++++++++++++ 7 files changed, 342 insertions(+), 43 deletions(-) create mode 100644 src/SpatialAudioSettings.cpp create mode 100644 src/SpatialAudioSettings.h diff --git a/src/AudManager.cpp b/src/AudManager.cpp index fd9f9e2..8328d8f 100644 --- a/src/AudManager.cpp +++ b/src/AudManager.cpp @@ -70,7 +70,6 @@ AudManager::AudManager( IRoot* lockobj ) : m_asyncOpen( true ), m_log(), m_spatialAudioEnabled( true ), - m_occlusionMode( AudOcclusionMode::Off ), m_moniteredParametersMapMutex( "AudManager", "m_monitoredParametersMapMutex" ), m_soundBankMutex( "AudManager", "m_soundBankMutex" ), m_isProfilerCapturing( false ), @@ -78,12 +77,14 @@ AudManager::AudManager( IRoot* lockobj ) : { // Initialize sound prioritization system m_soundPrioritization = new SoundPrioritization(); + m_spatialAudioSettings = new SpatialAudioSettings(); } AudManager::~AudManager() { // Clean up sound prioritization system delete m_soundPrioritization; + delete m_spatialAudioSettings; if( g_audioInitialized ) { @@ -152,7 +153,7 @@ bool AudManager::Init() return false; } - if( m_occlusionMode == AudOcclusionMode::On ) + if( m_spatialAudioSettings->GetOcclusionMode() == AudOcclusionMode::On ) { if( !InitSpatialAudioGeometry() ) { @@ -406,17 +407,18 @@ bool AudManager::InitSpatialAudioGeometry() { AkSpatialAudioInitSettings spatialSettings; - spatialSettings.bEnableGeometricDiffractionAndTransmission = true; - - spatialSettings.uMaxReflectionOrder = 0; - spatialSettings.uDiffractionOnReflectionsOrder = 0; - spatialSettings.uMaxEmitterRoomAuxSends = 0; - spatialSettings.uMaxSoundPropagationDepth = 1; - spatialSettings.fMovementThreshold = 100; - - spatialSettings.uMaxDiffractionOrder = 4; - spatialSettings.bCalcEmitterVirtualPosition = true; - spatialSettings.fCPULimitPercentage = 20; + spatialSettings.uMaxSoundPropagationDepth = m_spatialAudioSettings->GetMaxSoundPropagationDepth(); + spatialSettings.fMovementThreshold = m_spatialAudioSettings->GetMovementThreshold(); + spatialSettings.uNumberOfPrimaryRays = m_spatialAudioSettings->GetNumberOfPrimaryRays(); + spatialSettings.uMaxReflectionOrder = m_spatialAudioSettings->GetMaxReflectionOrder(); + spatialSettings.uMaxDiffractionOrder = m_spatialAudioSettings->GetMaxDiffractionOrder(); + spatialSettings.uMaxEmitterRoomAuxSends = m_spatialAudioSettings->GetMaxEmitterRoomAuxSends(); + spatialSettings.uDiffractionOnReflectionsOrder = m_spatialAudioSettings->GetDiffractionOnReflectionsOrder(); + spatialSettings.fMaxPathLength = m_spatialAudioSettings->GetMaxPathLength(); + spatialSettings.fCPULimitPercentage = m_spatialAudioSettings->GetCPULimitPercentage(); + spatialSettings.uLoadBalancingSpread = m_spatialAudioSettings->GetLoadBalancingSpread(); + spatialSettings.bEnableGeometricDiffractionAndTransmission = m_spatialAudioSettings->GetEnableDiffractionAndTransmission(); + spatialSettings.bCalcEmitterVirtualPosition = m_spatialAudioSettings->GetCalcEmitterVirtualPosition(); if( AK::SpatialAudio::Init( spatialSettings ) != AK_Success ) { @@ -456,15 +458,6 @@ bool AudManager::SetState( const std::wstring& stateGroup, const std::wstring& s return false; } -//----------------------------------------------------- -// Description: -// Signals whether Carbon Audio supports spatial audio features on this operating system. -//----------------------------------------------------- -AudOcclusionMode AudManager::GetOcclusionMode() const -{ - return m_occlusionMode; -} - float AudManager::GetGlobalTransmissionLoss() const { return m_globalTransmissionLoss; @@ -483,7 +476,6 @@ const bool AudManager::SpatialAudioIsSupported() void AudManager::UpdateSettings( AudSettings* settings ) { m_settings = settings; - m_occlusionMode = static_cast( m_settings->m_occlusionMode ); } void AudManager::LoadBank( const std::wstring& name ) diff --git a/src/AudManager.h b/src/AudManager.h index 86af5f0..040d1dc 100644 --- a/src/AudManager.h +++ b/src/AudManager.h @@ -14,6 +14,7 @@ #include "AudSettings.h" #include "AudListener.h" #include "SoundPrioritization.h" +#include "SpatialAudioSettings.h" #include "CCPFilePackageLowLevelIO.h" #include @@ -114,8 +115,6 @@ BLUE_CLASS( AudManager ) : bool SetGlobalRTPC( const std::wstring& rtpcName, float value ); // Set a global state in Wwise. bool SetState( const std::wstring& stateGroup, const std::wstring& stateName ); - // Returns the active occlusion mode (On or Off). - AudOcclusionMode GetOcclusionMode() const; // Returns the global transmission loss [0.0-1.0] used for geometry surfaces. float GetGlobalTransmissionLoss() const; // Sets the global transmission loss [0.0-1.0]. @@ -218,8 +217,6 @@ BLUE_CLASS( AudManager ) : bool m_asyncOpen; // Signals whether Carbon Audio's spatial audio features are enabled. If the user currently doesn't have an active spatial audio endpoint then output will still be in stereo. bool m_spatialAudioEnabled; - // Controls whether occlusion is On or Off. - AudOcclusionMode m_occlusionMode; // Global transmission loss [0.0-1.0] applied to geometry surfaces. float m_globalTransmissionLoss = 0.7f; mutable bool m_audioCullingEnabled; @@ -236,6 +233,7 @@ BLUE_CLASS( AudManager ) : CcpMutex m_moniteredParametersMapMutex; SoundPrioritization* m_soundPrioritization; + SpatialAudioSettings* m_spatialAudioSettings; // A boolean for the state of the profiler capture bool m_isProfilerCapturing; @@ -297,6 +295,57 @@ BLUE_CLASS( AudManager ) : #undef DELEGATE_GETTER #undef DELEGATE_SETTER + + //----------------------------------------------------- + // Description: + // Delegate macros to forward getter/setter calls to + // the SpatialAudioSettings instance. + //----------------------------------------------------- + +#define DELEGATE_SA_GETTER( ReturnType, MethodName ) \ + ReturnType MethodName() const \ + { \ + return m_spatialAudioSettings->MethodName(); \ + } + +#define DELEGATE_SA_SETTER( ParamType, MethodName ) \ + void MethodName( ParamType value ) \ + { \ + m_spatialAudioSettings->MethodName( value ); \ + } + + // Getters + DELEGATE_SA_GETTER( AudOcclusionMode, GetOcclusionMode ) + DELEGATE_SA_GETTER( int, GetMaxSoundPropagationDepth ) + DELEGATE_SA_GETTER( float, GetMovementThreshold ) + DELEGATE_SA_GETTER( int, GetNumberOfPrimaryRays ) + DELEGATE_SA_GETTER( int, GetMaxReflectionOrder ) + DELEGATE_SA_GETTER( int, GetMaxDiffractionOrder ) + DELEGATE_SA_GETTER( int, GetMaxEmitterRoomAuxSends ) + DELEGATE_SA_GETTER( int, GetDiffractionOnReflectionsOrder ) + DELEGATE_SA_GETTER( float, GetMaxPathLength ) + DELEGATE_SA_GETTER( float, GetCPULimitPercentage ) + DELEGATE_SA_GETTER( int, GetLoadBalancingSpread ) + DELEGATE_SA_GETTER( bool, GetEnableDiffractionAndTransmission ) + DELEGATE_SA_GETTER( bool, GetCalcEmitterVirtualPosition ) + + // Setters + DELEGATE_SA_SETTER( AudOcclusionMode, SetOcclusionMode ) + DELEGATE_SA_SETTER( int, SetMaxSoundPropagationDepth ) + DELEGATE_SA_SETTER( float, SetMovementThreshold ) + DELEGATE_SA_SETTER( int, SetNumberOfPrimaryRays ) + DELEGATE_SA_SETTER( int, SetMaxReflectionOrder ) + DELEGATE_SA_SETTER( int, SetMaxDiffractionOrder ) + DELEGATE_SA_SETTER( int, SetMaxEmitterRoomAuxSends ) + DELEGATE_SA_SETTER( int, SetDiffractionOnReflectionsOrder ) + DELEGATE_SA_SETTER( float, SetMaxPathLength ) + DELEGATE_SA_SETTER( float, SetCPULimitPercentage ) + DELEGATE_SA_SETTER( int, SetLoadBalancingSpread ) + DELEGATE_SA_SETTER( bool, SetEnableDiffractionAndTransmission ) + DELEGATE_SA_SETTER( bool, SetCalcEmitterVirtualPosition ) + +#undef DELEGATE_SA_GETTER +#undef DELEGATE_SA_SETTER }; TYPEDEF_BLUECLASS( AudManager ); diff --git a/src/AudManager_Blue.cpp b/src/AudManager_Blue.cpp index d2cd14d..ff37dc8 100644 --- a/src/AudManager_Blue.cpp +++ b/src/AudManager_Blue.cpp @@ -23,8 +23,21 @@ const Be::ClassInfo* AudManager::ExposeToBlue() MAP_PROPERTY( "visibleWeight", GetVisibleWeight, SetVisibleWeight, "The weight applied to a game object if it is visible to the listener.") MAP_PROPERTY( "playing2DWeight", GetPlaying2DWeight, SetPlaying2DWeight, "The weight applied to a game object if it is currently playing a 2D sound.") - // Spatial audio occlusion settings + // Spatial audio settings (applied at init when occlusion mode is On) + MAP_PROPERTY( "occlusionMode", GetOcclusionMode, SetOcclusionMode, "Controls spatial audio occlusion. 0 = Off (no geometry, no occlusion), 1 = On (Wwise Spatial Audio diffraction + transmission).") MAP_PROPERTY( "globalTransmissionLoss", GetGlobalTransmissionLoss, SetGlobalTransmissionLoss, "Global transmission loss [0.0-1.0] applied to geometry surfaces when occlusion is enabled.") + MAP_PROPERTY( "maxSoundPropagationDepth", GetMaxSoundPropagationDepth, SetMaxSoundPropagationDepth, "Maximum number of portals that sound can propagate through.") + MAP_PROPERTY( "movementThreshold", GetMovementThreshold, SetMovementThreshold, "Distance an emitter or listener must move to trigger a re-validation of reflections/diffraction.") + MAP_PROPERTY( "numberOfPrimaryRays", GetNumberOfPrimaryRays, SetNumberOfPrimaryRays, "Number of primary rays used in the ray tracing engine. More rays = better quality but higher CPU.") + MAP_PROPERTY( "maxReflectionOrder", GetMaxReflectionOrder, SetMaxReflectionOrder, "Maximum reflection order [1-4] - number of bounces in a reflection path.") + MAP_PROPERTY( "maxDiffractionOrder", GetMaxDiffractionOrder, SetMaxDiffractionOrder, "Maximum diffraction order [1-8] - number of bends in a diffraction path. Set to 0 to disable diffraction.") + MAP_PROPERTY( "maxEmitterRoomAuxSends", GetMaxEmitterRoomAuxSends, SetMaxEmitterRoomAuxSends, "Maximum number of game-defined auxiliary sends from a single emitter. Set to 0 to disable the limit.") + MAP_PROPERTY( "diffractionOnReflectionsOrder", GetDiffractionOnReflectionsOrder, SetDiffractionOnReflectionsOrder, "Maximum diffraction points at each end of a reflection path. Set to 0 to disable diffraction on reflections.") + MAP_PROPERTY( "maxPathLength", GetMaxPathLength, SetMaxPathLength, "Maximum total length of a path composed of segments. Higher values compute longer paths but increase CPU cost.") + MAP_PROPERTY( "cpuLimitPercentage", GetCPULimitPercentage, SetCPULimitPercentage, "Targeted computation time for ray tracing as a percentage [0-100] of the audio frame. 0 = no limit.") + MAP_PROPERTY( "loadBalancingSpread", GetLoadBalancingSpread, SetLoadBalancingSpread, "Spread path computation over N frames [1..]. 1 = no load balancing.") + MAP_PROPERTY( "enableDiffractionAndTransmission", GetEnableDiffractionAndTransmission, SetEnableDiffractionAndTransmission, "Enable geometric diffraction and transmission path computation.") + MAP_PROPERTY( "calcEmitterVirtualPosition", GetCalcEmitterVirtualPosition, SetCalcEmitterVirtualPosition, "Calculate virtual position for emitters diffracted through portals or around geometry.") MAP_METHOD_AND_WRAP ( diff --git a/src/AudSettings.h b/src/AudSettings.h index c6e38e8..3e8fc29 100644 --- a/src/AudSettings.h +++ b/src/AudSettings.h @@ -9,19 +9,11 @@ #include "Audio2.h" -enum class AudOcclusionMode : int -{ - Off = 0, // Occlusion disabled, no geometry registered, spatial audio geometry not initialized - On = 1 // Wwise Spatial Audio handles diffraction + transmission -}; - BLUE_CLASS( AudSettings ) : public IRoot { public: EXPOSE_TO_BLUE(); - int m_occlusionMode = static_cast( AudOcclusionMode::Off ); - #if _WIN32 bool m_spatialAudioEnabled = true; std::wstring m_audioSrcPath = L"Media"; diff --git a/src/AudSettings_Blue.cpp b/src/AudSettings_Blue.cpp index ef25659..4f2233b 100644 --- a/src/AudSettings_Blue.cpp +++ b/src/AudSettings_Blue.cpp @@ -53,12 +53,5 @@ const Be::ClassInfo* AudSettings::ExposeToBlue() Be::READWRITE ) MAP_ATTRIBUTE("essentialPath", m_essentialPath, "The path for essential media.", Be::READWRITE ) - MAP_ATTRIBUTE( - "occlusionMode", - m_occlusionMode, - "Controls spatial audio occlusion. 0 = Off (no geometry, no occlusion), " - "1 = On ( Wwise Spatial Audio Diffraction + transmission ).", - Be::READWRITE - ) EXPOSURE_END() } diff --git a/src/SpatialAudioSettings.cpp b/src/SpatialAudioSettings.cpp new file mode 100644 index 0000000..f83040d --- /dev/null +++ b/src/SpatialAudioSettings.cpp @@ -0,0 +1,58 @@ +#include "stdafx.h" +#include "SpatialAudioSettings.h" + +SpatialAudioSettings::SpatialAudioSettings() + : m_occlusionMode( AudOcclusionMode::On ) + , m_maxSoundPropagationDepth( 1 ) + , m_movementThreshold( 100.0f ) + , m_numberOfPrimaryRays( 35 ) + , m_maxReflectionOrder( 0 ) + , m_maxDiffractionOrder( 4 ) + , m_maxEmitterRoomAuxSends( 0 ) + , m_diffractionOnReflectionsOrder( 0 ) + , m_maxPathLength( 1000.0f ) + , m_cpuLimitPercentage( 20.0f ) + , m_loadBalancingSpread( 1 ) + , m_enableDiffractionAndTransmission( true ) + , m_calcEmitterVirtualPosition( true ) +{ +} + +AudOcclusionMode SpatialAudioSettings::GetOcclusionMode() const { return m_occlusionMode; } +void SpatialAudioSettings::SetOcclusionMode( AudOcclusionMode value ) { m_occlusionMode = value; } + +int SpatialAudioSettings::GetMaxSoundPropagationDepth() const { return m_maxSoundPropagationDepth; } +void SpatialAudioSettings::SetMaxSoundPropagationDepth( int value ) { m_maxSoundPropagationDepth = value; } + +float SpatialAudioSettings::GetMovementThreshold() const { return m_movementThreshold; } +void SpatialAudioSettings::SetMovementThreshold( float value ) { m_movementThreshold = value; } + +int SpatialAudioSettings::GetNumberOfPrimaryRays() const { return m_numberOfPrimaryRays; } +void SpatialAudioSettings::SetNumberOfPrimaryRays( int value ) { m_numberOfPrimaryRays = value; } + +int SpatialAudioSettings::GetMaxReflectionOrder() const { return m_maxReflectionOrder; } +void SpatialAudioSettings::SetMaxReflectionOrder( int value ) { m_maxReflectionOrder = value; } + +int SpatialAudioSettings::GetMaxDiffractionOrder() const { return m_maxDiffractionOrder; } +void SpatialAudioSettings::SetMaxDiffractionOrder( int value ) { m_maxDiffractionOrder = value; } + +int SpatialAudioSettings::GetMaxEmitterRoomAuxSends() const { return m_maxEmitterRoomAuxSends; } +void SpatialAudioSettings::SetMaxEmitterRoomAuxSends( int value ) { m_maxEmitterRoomAuxSends = value; } + +int SpatialAudioSettings::GetDiffractionOnReflectionsOrder() const { return m_diffractionOnReflectionsOrder; } +void SpatialAudioSettings::SetDiffractionOnReflectionsOrder( int value ) { m_diffractionOnReflectionsOrder = value; } + +float SpatialAudioSettings::GetMaxPathLength() const { return m_maxPathLength; } +void SpatialAudioSettings::SetMaxPathLength( float value ) { m_maxPathLength = value; } + +float SpatialAudioSettings::GetCPULimitPercentage() const { return m_cpuLimitPercentage; } +void SpatialAudioSettings::SetCPULimitPercentage( float value ) { m_cpuLimitPercentage = value; } + +int SpatialAudioSettings::GetLoadBalancingSpread() const { return m_loadBalancingSpread; } +void SpatialAudioSettings::SetLoadBalancingSpread( int value ) { m_loadBalancingSpread = value; } + +bool SpatialAudioSettings::GetEnableDiffractionAndTransmission() const { return m_enableDiffractionAndTransmission; } +void SpatialAudioSettings::SetEnableDiffractionAndTransmission( bool value ) { m_enableDiffractionAndTransmission = value; } + +bool SpatialAudioSettings::GetCalcEmitterVirtualPosition() const { return m_calcEmitterVirtualPosition; } +void SpatialAudioSettings::SetCalcEmitterVirtualPosition( bool value ) { m_calcEmitterVirtualPosition = value; } diff --git a/src/SpatialAudioSettings.h b/src/SpatialAudioSettings.h new file mode 100644 index 0000000..fcac01c --- /dev/null +++ b/src/SpatialAudioSettings.h @@ -0,0 +1,202 @@ +//////////////////////////////////////////////////////////// +// +// Creator: Phevos Rinis +// Creation Date: Mar 2026 +// Copyright (c) 2026 CCP Games +// + +#pragma once + +enum class AudOcclusionMode : int +{ + Off = 0, // Occlusion disabled, no geometry registered, spatial audio geometry not initialized + On = 1 // Wwise Spatial Audio handles diffraction + transmission +}; + +/** + * @brief Configuration for Wwise Spatial Audio initialization. + * + * Holds all parameters that map to AkSpatialAudioInitSettings. + * Values are set before AudManager::Enable() and consumed by + * InitSpatialAudioGeometry(). + */ +class SpatialAudioSettings +{ +public: + SpatialAudioSettings(); + + /** + * @brief Controls whether spatial audio occlusion is On or Off. + * + * When On, Wwise Spatial Audio handles diffraction and transmission. + * When Off, no geometry is registered and spatial audio geometry is + * not initialized. + */ + AudOcclusionMode GetOcclusionMode() const; + void SetOcclusionMode( AudOcclusionMode value ); + + /** + * @brief Maximum number of portals that sound can propagate through. + * + * Must be less than or equal to AK_MAX_SOUND_PROPAGATION_DEPTH. + */ + int GetMaxSoundPropagationDepth() const; + void SetMaxSoundPropagationDepth( int value ); + + /** + * @brief Amount that an emitter or listener has to move to trigger a + * re-validation of reflections/diffraction. + * + * Larger values reduce CPU load at the cost of reduced accuracy. + * The ray tracing itself is not affected by this value — rays are + * cast each time a Spatial Audio update is executed. + */ + float GetMovementThreshold() const; + void SetMovementThreshold( float value ); + + /** + * @brief Number of primary rays used in the ray tracing engine. + * + * A larger number of rays increases the chances of finding reflection + * and diffraction paths, but results in higher CPU usage. When CPU + * limit is active (see @c cpuLimitPercentage), this setting represents + * the maximum allowed number of primary rays. + */ + int GetNumberOfPrimaryRays() const; + void SetNumberOfPrimaryRays( int value ); + + /** + * @brief Maximum reflection order [1, 4] — the number of 'bounces' + * in a reflection path. + * + * A high reflection order renders more details at the expense of + * higher CPU usage. + */ + int GetMaxReflectionOrder() const; + void SetMaxReflectionOrder( int value ); + + /** + * @brief Maximum diffraction order [1, 8] — the number of 'bends' + * in a diffraction path. + * + * A high diffraction order accommodates more complex geometry at the + * expense of higher CPU usage. Set to 0 to disable diffraction on + * all geometry. Diffraction must be enabled on the geometry itself + * (see AkGeometryParams). + * + * This limits the recursion depth of diffraction rays cast from the + * listener and the depth of the diffraction search between emitter + * and listener. To optimize CPU, set it to the maximum number of + * edges you expect geometry to traverse (e.g. 2 for a single box). + * + * A search starts from the listener; when the maximum order is + * exceeded, remaining geometry between the path's end and the emitter + * is ignored, causing the diffraction coefficient to be underestimated. + */ + int GetMaxDiffractionOrder() const; + void SetMaxDiffractionOrder( int value ); + + /** + * @brief Maximum number of game-defined auxiliary sends that can + * originate from a single emitter. + * + * An emitter can send to its own room and to all adjacent rooms if + * the emitter and listener are in the same room. If a limit is set, + * the most prominent sends are kept based on spread to the adjacent + * portal from the emitter's perspective. Set to 1 to only allow + * emitters to send directly to their current room. Set to 0 to + * disable the limit. + */ + int GetMaxEmitterRoomAuxSends() const; + void SetMaxEmitterRoomAuxSends( int value ); + + /** + * @brief Maximum number of diffraction points at each end of a + * reflection path. + * + * Diffraction on reflections allows reflections to fade in and out + * smoothly as the listener or emitter moves in and out of a + * reflection's shadow zone. When greater than zero, diffraction rays + * are sent from the listener to search for reflections around corners. + * Set to 0 to disable diffraction on reflections. Set to 2 or greater + * to allow reflections to propagate through portals without being + * cut off. + */ + int GetDiffractionOnReflectionsOrder() const; + void SetDiffractionOnReflectionsOrder( int value ); + + /** + * @brief Maximum total length of a path composed of a sequence of + * segments (rays). + * + * High values compute longer paths but increase CPU cost. Each + * individual sound is also affected by its maximum attenuation + * distance specified in the Wwise Authoring tool. Reflection or + * diffraction paths will never exceed a sound's maximum attenuation + * distance (unless the furthest point is above the audibility + * threshold, in which case attenuation is considered infinite). + */ + float GetMaxPathLength() const; + void SetMaxPathLength( float value ); + + /** + * @brief Targeted computation time allocated for the ray tracing + * engine, as a percentage [0, 100] of the current audio frame. + * + * The ray tracing engine dynamically adapts the number of primary + * rays to target the specified computation time. The computed number + * of primary rays will never exceed @c numberOfPrimaryRays. + * A value of 0 indicates no target has been set — the number of + * primary rays is then fixed at @c numberOfPrimaryRays. + */ + float GetCPULimitPercentage() const; + void SetCPULimitPercentage( float value ); + + /** + * @brief Spread path computation over N frames [1, ..]. + * + * When set to 1, no load balancing is done. Values greater than 1 + * spread the computation of paths across that many frames. + */ + int GetLoadBalancingSpread() const; + void SetLoadBalancingSpread( int value ); + + /** + * @brief Enable computation of geometric diffraction and transmission + * paths for all sources that have "Enable Diffraction and + * Transmission" checked in the Wwise Positioning tab. + * + * This flag enables sound paths around (diffraction) and through + * (transmission) geometry. Setting to false implies geometry is only + * used for reflection calculation. If false but a sound has diffraction + * enabled in Wwise, the sound will diffract through portals but pass + * through geometry as if it were not there. + */ + bool GetEnableDiffractionAndTransmission() const; + void SetEnableDiffractionAndTransmission( bool value ); + + /** + * @brief Calculate virtual positions for emitters diffracted through + * a portal or around geometry. + * + * The apparent or virtual position is calculated by Wwise Spatial + * Audio and passed on to the sound engine. + */ + bool GetCalcEmitterVirtualPosition() const; + void SetCalcEmitterVirtualPosition( bool value ); + +private: + AudOcclusionMode m_occlusionMode; + int m_maxSoundPropagationDepth; + float m_movementThreshold; + int m_numberOfPrimaryRays; + int m_maxReflectionOrder; + int m_maxDiffractionOrder; + int m_maxEmitterRoomAuxSends; + int m_diffractionOnReflectionsOrder; + float m_maxPathLength; + float m_cpuLimitPercentage; + int m_loadBalancingSpread; + bool m_enableDiffractionAndTransmission; + bool m_calcEmitterVirtualPosition; +}; From 550acf08c152e03d5d3906b38ea00429f4e3d801 Mon Sep 17 00:00:00 2001 From: phevosccp <160875214+phevosccp@users.noreply.github.com> Date: Fri, 13 Mar 2026 19:18:16 +0000 Subject: [PATCH 41/48] fix cmake issues --- CMakeLists.txt | 6 +++--- src/AudManager.cpp | 15 +++++++++++++++ src/AudManager.h | 8 ++++++-- src/AudManager_Blue.cpp | 2 +- 4 files changed, 25 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 32eaaa3..3977f5c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,7 +49,6 @@ ccp_add_library(CarbonAudio SHARED src/AudActionLog.cpp src/AudActionLog_Blue.cpp src/AudManager.cpp - src/AudObstructionOcclusion.cpp src/AudConstants.cpp src/AudManager_Blue.cpp src/AudEmitter.cpp @@ -73,6 +72,7 @@ ccp_add_library(CarbonAudio SHARED src/AudGameObjResource_Blue.cpp src/AudGeometry.cpp src/AudGeometry_Blue.cpp + src/SpatialAudioSettings.cpp src/stdafx.cpp src/AudListener.cpp src/AudioCurveSetDriver.cpp @@ -108,7 +108,6 @@ target_sources(CarbonAudio PRIVATE src/AudioInputMgr.h src/AudListener.h src/AudManager.h - src/AudObstructionOcclusion.h src/AudParameter.h src/AudPosition.h src/AudSettings.h @@ -116,6 +115,7 @@ target_sources(CarbonAudio PRIVATE src/AudUIPlayer.h src/AudMusicPlayer.h src/AudGeometry.h + src/SpatialAudioSettings.h src/autoversion.h src/DebugUtilities.h src/LogBridge.h @@ -352,7 +352,7 @@ if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) # Filter out command line arguments and only keep actual test names # This is an unfortunate necessity because MacOS was not properly handling test discovery and taking all arguments as tests. - string(REPLACE "\n" ";" PYTHON_TESTS_RAW ${PYTHON_TESTS_STR}) + string(REPLACE "\n" ";" PYTHON_TESTS_RAW "${PYTHON_TESTS_STR}") set(PYTHON_TESTS "") foreach(LINE ${PYTHON_TESTS_RAW}) string(STRIP "${LINE}" LINE_STRIPPED) diff --git a/src/AudManager.cpp b/src/AudManager.cpp index 8328d8f..c182883 100644 --- a/src/AudManager.cpp +++ b/src/AudManager.cpp @@ -458,6 +458,21 @@ bool AudManager::SetState( const std::wstring& stateGroup, const std::wstring& s return false; } +int AudManager::GetOcclusionModeInt() const +{ + return static_cast( m_spatialAudioSettings->GetOcclusionMode() ); +} + +void AudManager::SetOcclusionModeInt( int value ) +{ + m_spatialAudioSettings->SetOcclusionMode( static_cast( value ) ); +} + +AudOcclusionMode AudManager::GetOcclusionMode() const +{ + return m_spatialAudioSettings->GetOcclusionMode(); +} + float AudManager::GetGlobalTransmissionLoss() const { return m_globalTransmissionLoss; diff --git a/src/AudManager.h b/src/AudManager.h index 040d1dc..4cbd620 100644 --- a/src/AudManager.h +++ b/src/AudManager.h @@ -115,6 +115,12 @@ BLUE_CLASS( AudManager ) : bool SetGlobalRTPC( const std::wstring& rtpcName, float value ); // Set a global state in Wwise. bool SetState( const std::wstring& stateGroup, const std::wstring& stateName ); + // Returns the occlusion mode as an integer for Blue property exposure. + int GetOcclusionModeInt() const; + // Sets the occlusion mode from an integer value. + void SetOcclusionModeInt( int value ); + // Returns the occlusion mode enum. + AudOcclusionMode GetOcclusionMode() const; // Returns the global transmission loss [0.0-1.0] used for geometry surfaces. float GetGlobalTransmissionLoss() const; // Sets the global transmission loss [0.0-1.0]. @@ -315,7 +321,6 @@ BLUE_CLASS( AudManager ) : } // Getters - DELEGATE_SA_GETTER( AudOcclusionMode, GetOcclusionMode ) DELEGATE_SA_GETTER( int, GetMaxSoundPropagationDepth ) DELEGATE_SA_GETTER( float, GetMovementThreshold ) DELEGATE_SA_GETTER( int, GetNumberOfPrimaryRays ) @@ -330,7 +335,6 @@ BLUE_CLASS( AudManager ) : DELEGATE_SA_GETTER( bool, GetCalcEmitterVirtualPosition ) // Setters - DELEGATE_SA_SETTER( AudOcclusionMode, SetOcclusionMode ) DELEGATE_SA_SETTER( int, SetMaxSoundPropagationDepth ) DELEGATE_SA_SETTER( float, SetMovementThreshold ) DELEGATE_SA_SETTER( int, SetNumberOfPrimaryRays ) diff --git a/src/AudManager_Blue.cpp b/src/AudManager_Blue.cpp index ff37dc8..907ec03 100644 --- a/src/AudManager_Blue.cpp +++ b/src/AudManager_Blue.cpp @@ -24,7 +24,7 @@ const Be::ClassInfo* AudManager::ExposeToBlue() MAP_PROPERTY( "playing2DWeight", GetPlaying2DWeight, SetPlaying2DWeight, "The weight applied to a game object if it is currently playing a 2D sound.") // Spatial audio settings (applied at init when occlusion mode is On) - MAP_PROPERTY( "occlusionMode", GetOcclusionMode, SetOcclusionMode, "Controls spatial audio occlusion. 0 = Off (no geometry, no occlusion), 1 = On (Wwise Spatial Audio diffraction + transmission).") + MAP_PROPERTY( "occlusionMode", GetOcclusionModeInt, SetOcclusionModeInt, "Controls spatial audio occlusion. 0 = Off (no geometry, no occlusion), 1 = On (Wwise Spatial Audio diffraction + transmission).") MAP_PROPERTY( "globalTransmissionLoss", GetGlobalTransmissionLoss, SetGlobalTransmissionLoss, "Global transmission loss [0.0-1.0] applied to geometry surfaces when occlusion is enabled.") MAP_PROPERTY( "maxSoundPropagationDepth", GetMaxSoundPropagationDepth, SetMaxSoundPropagationDepth, "Maximum number of portals that sound can propagate through.") MAP_PROPERTY( "movementThreshold", GetMovementThreshold, SetMovementThreshold, "Distance an emitter or listener must move to trigger a re-validation of reflections/diffraction.") From 0d07ebfe7b4ca4f99a0223c1555bffcf15ed98ab Mon Sep 17 00:00:00 2001 From: phevosccp <160875214+phevosccp@users.noreply.github.com> Date: Mon, 16 Mar 2026 09:32:37 +0000 Subject: [PATCH 42/48] refactor AudGeometry --- src/AudGeometry.cpp | 74 +++++++++++++++++++++------------------------ src/AudGeometry.h | 9 +++--- 2 files changed, 40 insertions(+), 43 deletions(-) diff --git a/src/AudGeometry.cpp b/src/AudGeometry.cpp index de3db80..185af40 100644 --- a/src/AudGeometry.cpp +++ b/src/AudGeometry.cpp @@ -14,7 +14,7 @@ namespace for( size_t i = 0; i < vertices.size(); ++i ) { const Vector3& v = vertices[i]; - akVertices[i] = AkVertex( v.x, v.y, -v.z ); + akVertices[i] = AkVertex( static_cast( v.x ), static_cast( v.y ), static_cast( -v.z ) ); } return akVertices; } @@ -37,15 +37,24 @@ namespace } -std::unordered_map AudGeometry::s_geometrySetRefCounts; -CcpMutex AudGeometry::s_mutex( "AudGeometry", "s_mutex" ); - AudGeometry::AudGeometry( IRoot* lockobj ) {} AudGeometry::~AudGeometry() {} +AkGeometryInstanceParams AudGeometry::MakeInstanceParams( + uint64_t geometrySetId, const Matrix& worldTransform ) +{ + AkGeometryInstanceParams params; + params.GeometrySetID = geometrySetId; + AkTransform transform; + RH2LH::convertTransform( worldTransform, transform ); + params.PositionAndOrientation = transform; + params.Scale = RH2LH::extractScale( worldTransform ); + return params; +} + void AudGeometry::SetGeometry( uint64_t geometrySetId, uint64_t instanceId, @@ -62,8 +71,6 @@ void AudGeometry::SetGeometry( return; } - CcpAutoMutex lock( s_mutex ); - auto it = s_geometrySetRefCounts.find( geometrySetId ); if( it == s_geometrySetRefCounts.end() ) { @@ -73,7 +80,6 @@ void AudGeometry::SetGeometry( AkAcousticSurface surface; surface.strName = "default"; surface.textureID = AK_INVALID_UNIQUE_ID; - surface.transmissionLoss = g_audioManager->GetGlobalTransmissionLoss(); AkGeometryParams params; @@ -100,18 +106,12 @@ void AudGeometry::SetGeometry( it->second++; } - AkGeometryInstanceParams instanceParams; - instanceParams.GeometrySetID = geometrySetId; - AkTransform transform; - RH2LH::convertTransform( worldTransform, transform ); - instanceParams.PositionAndOrientation = transform; - instanceParams.Scale = RH2LH::extractScale( worldTransform ); - - AKRESULT instanceResult = AK::SpatialAudio::SetGeometryInstance( instanceId, instanceParams ); - if( instanceResult != AK_Success ) + AKRESULT result = AK::SpatialAudio::SetGeometryInstance( + instanceId, MakeInstanceParams( geometrySetId, worldTransform ) ); + if( result != AK_Success ) { CCP_LOGERR_CH( s_ch, "Failed to set geometry instance %llu (set %llu), AKRESULT: %d", - instanceId, geometrySetId, instanceResult ); + instanceId, geometrySetId, result ); } } @@ -120,22 +120,18 @@ void AudGeometry::SetGeometryTransform( uint64_t instanceId, const Matrix& worldTransform ) { + if( !g_audioManager || g_audioManager->GetOcclusionMode() == AudOcclusionMode::Off ) { - CcpAutoMutex lock( s_mutex ); - if( s_geometrySetRefCounts.find( geometrySetId ) == s_geometrySetRefCounts.end() ) - { - return; - } + return; } - AkGeometryInstanceParams instanceParams; - instanceParams.GeometrySetID = geometrySetId; - AkTransform transform; - RH2LH::convertTransform( worldTransform, transform ); - instanceParams.PositionAndOrientation = transform; - instanceParams.Scale = RH2LH::extractScale( worldTransform ); + if( s_geometrySetRefCounts.find( geometrySetId ) == s_geometrySetRefCounts.end() ) + { + return; + } - AKRESULT result = AK::SpatialAudio::SetGeometryInstance( instanceId, instanceParams ); + AKRESULT result = AK::SpatialAudio::SetGeometryInstance( + instanceId, MakeInstanceParams( geometrySetId, worldTransform ) ); if( result != AK_Success ) { CCP_LOGERR_CH( s_ch, "Failed to update geometry instance transform for instance %llu (set %llu), AKRESULT: %d", @@ -147,18 +143,18 @@ void AudGeometry::RemoveGeometry( uint64_t geometrySetId, uint64_t instanceId ) { - AK::SpatialAudio::RemoveGeometryInstance( instanceId ); + auto it = s_geometrySetRefCounts.find( geometrySetId ); + if( it == s_geometrySetRefCounts.end() ) + { + return; + } - CcpAutoMutex lock( s_mutex ); + AK::SpatialAudio::RemoveGeometryInstance( instanceId ); - auto it = s_geometrySetRefCounts.find( geometrySetId ); - if( it != s_geometrySetRefCounts.end() ) + it->second--; + if( it->second == 0 ) { - it->second--; - if( it->second == 0 ) - { - AK::SpatialAudio::RemoveGeometry( geometrySetId ); - s_geometrySetRefCounts.erase( it ); - } + AK::SpatialAudio::RemoveGeometry( geometrySetId ); + s_geometrySetRefCounts.erase( it ); } } diff --git a/src/AudGeometry.h b/src/AudGeometry.h index 02fb0a1..918104e 100644 --- a/src/AudGeometry.h +++ b/src/AudGeometry.h @@ -82,11 +82,12 @@ BLUE_CLASS(AudGeometry) : uint64_t instanceId) override; private: - /// Tracks how many active instances reference each geometry set. - static std::unordered_map s_geometrySetRefCounts; + /// Builds AkGeometryInstanceParams from a geometry set ID and world transform. + static AkGeometryInstanceParams MakeInstanceParams( + uint64_t geometrySetId, const Matrix& worldTransform ); - /// Mutex protecting s_geometrySetRefCounts and Wwise geometry operations. - static CcpMutex s_mutex; + /// Tracks how many active instances reference each geometry set. + inline static std::unordered_map s_geometrySetRefCounts; }; TYPEDEF_BLUECLASS( AudGeometry ); From a59cde81c7a8260d29ea87b9f458ec185c565e5a Mon Sep 17 00:00:00 2001 From: phevosccp <160875214+phevosccp@users.noreply.github.com> Date: Mon, 16 Mar 2026 10:02:47 +0000 Subject: [PATCH 43/48] also expose EnableDiffraction and EnableDiffractionOnBoundaryEdges plus some minor changes --- .gitignore | 1 + src/AudGameObjResource.cpp | 4 ++-- src/AudGeometry.cpp | 8 ++++---- src/AudGeometry.h | 4 ++-- src/AudManager.cpp | 6 +++--- src/AudManager.h | 7 ++++++- src/SpatialAudioSettings.cpp | 14 +++++++++++--- src/SpatialAudioSettings.h | 31 +++++++++++++++++++++++++++---- 8 files changed, 56 insertions(+), 19 deletions(-) diff --git a/.gitignore b/.gitignore index 1175a3b..35b2b92 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ out* # Wwise .backup* .cache* +build* *.akd *.prof *.validationcache diff --git a/src/AudGameObjResource.cpp b/src/AudGameObjResource.cpp index 7ae4b50..dc4e2a9 100644 --- a/src/AudGameObjResource.cpp +++ b/src/AudGameObjResource.cpp @@ -398,7 +398,7 @@ bool AudGameObjResource::Initialize() RegisterWwiseObject(); SetPositionHelper( Vector3( 1,0,0 ), Vector3( 0,1,0 ), m_position ); - if ( !m_eventName.empty() ) + if ( !m_eventName.empty() ) { PostEvent( m_eventName ); } @@ -581,7 +581,7 @@ void AudGameObjResource::Wake() return; } - RegisterWwiseObject(); + RegisterWwiseObject(); SetPositionHelper( Vector3( 1,0,0 ), Vector3( 0,1,0 ), m_position ); m_culled = false; if ( m_waitingOneShotInRange.second != L"" && m_listenerInRange ) diff --git a/src/AudGeometry.cpp b/src/AudGeometry.cpp index 185af40..4455b4c 100644 --- a/src/AudGeometry.cpp +++ b/src/AudGeometry.cpp @@ -66,7 +66,7 @@ void AudGeometry::SetGeometry( return; } - if( !g_audioManager || g_audioManager->GetOcclusionMode() == AudOcclusionMode::Off ) + if( !g_audioManager || g_audioManager->GetOcclusionMode() == AudOcclusion::Off ) { return; } @@ -89,8 +89,8 @@ void AudGeometry::SetGeometry( params.NumTriangles = static_cast( akTriangles.size() ); params.Surfaces = &surface; params.NumSurfaces = 1; - params.EnableDiffraction = true; - params.EnableDiffractionOnBoundaryEdges = false; + params.EnableDiffraction = g_audioManager->GetEnableDiffraction(); + params.EnableDiffractionOnBoundaryEdges = g_audioManager->GetEnableDiffractionOnBoundaryEdges(); AKRESULT result = AK::SpatialAudio::SetGeometry( geometrySetId, params ); if( result != AK_Success ) @@ -120,7 +120,7 @@ void AudGeometry::SetGeometryTransform( uint64_t instanceId, const Matrix& worldTransform ) { - if( !g_audioManager || g_audioManager->GetOcclusionMode() == AudOcclusionMode::Off ) + if( !g_audioManager || g_audioManager->GetOcclusionMode() == AudOcclusion::Off ) { return; } diff --git a/src/AudGeometry.h b/src/AudGeometry.h index 918104e..401bb69 100644 --- a/src/AudGeometry.h +++ b/src/AudGeometry.h @@ -23,8 +23,8 @@ * placement of a geometry set in the world with a specified transform — position, * orientation, and scale (see AkGeometryInstanceParams). * - * When @c AudOcclusionMode::On, geometry is registered with diffraction and - * transmission enabled. When @c AudOcclusionMode::Off, geometry registration + * When @c AudOcclusion::On, geometry is registered with diffraction and + * transmission enabled. When @c AudOcclusion::Off, geometry registration * is skipped entirely. */ BLUE_CLASS(AudGeometry) : diff --git a/src/AudManager.cpp b/src/AudManager.cpp index c182883..7fb1085 100644 --- a/src/AudManager.cpp +++ b/src/AudManager.cpp @@ -153,7 +153,7 @@ bool AudManager::Init() return false; } - if( m_spatialAudioSettings->GetOcclusionMode() == AudOcclusionMode::On ) + if( m_spatialAudioSettings->GetOcclusionMode() == AudOcclusion::On ) { if( !InitSpatialAudioGeometry() ) { @@ -465,10 +465,10 @@ int AudManager::GetOcclusionModeInt() const void AudManager::SetOcclusionModeInt( int value ) { - m_spatialAudioSettings->SetOcclusionMode( static_cast( value ) ); + m_spatialAudioSettings->SetOcclusionMode( static_cast( value ) ); } -AudOcclusionMode AudManager::GetOcclusionMode() const +AudOcclusion AudManager::GetOcclusionMode() const { return m_spatialAudioSettings->GetOcclusionMode(); } diff --git a/src/AudManager.h b/src/AudManager.h index 4cbd620..5b4073d 100644 --- a/src/AudManager.h +++ b/src/AudManager.h @@ -120,7 +120,7 @@ BLUE_CLASS( AudManager ) : // Sets the occlusion mode from an integer value. void SetOcclusionModeInt( int value ); // Returns the occlusion mode enum. - AudOcclusionMode GetOcclusionMode() const; + AudOcclusion GetOcclusionMode() const; // Returns the global transmission loss [0.0-1.0] used for geometry surfaces. float GetGlobalTransmissionLoss() const; // Sets the global transmission loss [0.0-1.0]. @@ -302,6 +302,7 @@ BLUE_CLASS( AudManager ) : #undef DELEGATE_GETTER #undef DELEGATE_SETTER +public: //----------------------------------------------------- // Description: // Delegate macros to forward getter/setter calls to @@ -333,6 +334,8 @@ BLUE_CLASS( AudManager ) : DELEGATE_SA_GETTER( int, GetLoadBalancingSpread ) DELEGATE_SA_GETTER( bool, GetEnableDiffractionAndTransmission ) DELEGATE_SA_GETTER( bool, GetCalcEmitterVirtualPosition ) + DELEGATE_SA_GETTER( bool, GetEnableDiffraction ) + DELEGATE_SA_GETTER( bool, GetEnableDiffractionOnBoundaryEdges ) // Setters DELEGATE_SA_SETTER( int, SetMaxSoundPropagationDepth ) @@ -347,6 +350,8 @@ BLUE_CLASS( AudManager ) : DELEGATE_SA_SETTER( int, SetLoadBalancingSpread ) DELEGATE_SA_SETTER( bool, SetEnableDiffractionAndTransmission ) DELEGATE_SA_SETTER( bool, SetCalcEmitterVirtualPosition ) + DELEGATE_SA_SETTER( bool, SetEnableDiffraction ) + DELEGATE_SA_SETTER( bool, SetEnableDiffractionOnBoundaryEdges ) #undef DELEGATE_SA_GETTER #undef DELEGATE_SA_SETTER diff --git a/src/SpatialAudioSettings.cpp b/src/SpatialAudioSettings.cpp index f83040d..6249ce0 100644 --- a/src/SpatialAudioSettings.cpp +++ b/src/SpatialAudioSettings.cpp @@ -2,7 +2,7 @@ #include "SpatialAudioSettings.h" SpatialAudioSettings::SpatialAudioSettings() - : m_occlusionMode( AudOcclusionMode::On ) + : m_occlusionMode( AudOcclusion::On ) , m_maxSoundPropagationDepth( 1 ) , m_movementThreshold( 100.0f ) , m_numberOfPrimaryRays( 35 ) @@ -15,11 +15,13 @@ SpatialAudioSettings::SpatialAudioSettings() , m_loadBalancingSpread( 1 ) , m_enableDiffractionAndTransmission( true ) , m_calcEmitterVirtualPosition( true ) + , m_enableDiffraction( true ) + , m_enableDiffractionOnBoundaryEdges( true ) { } -AudOcclusionMode SpatialAudioSettings::GetOcclusionMode() const { return m_occlusionMode; } -void SpatialAudioSettings::SetOcclusionMode( AudOcclusionMode value ) { m_occlusionMode = value; } +AudOcclusion SpatialAudioSettings::GetOcclusionMode() const { return m_occlusionMode; } +void SpatialAudioSettings::SetOcclusionMode( AudOcclusion value ) { m_occlusionMode = value; } int SpatialAudioSettings::GetMaxSoundPropagationDepth() const { return m_maxSoundPropagationDepth; } void SpatialAudioSettings::SetMaxSoundPropagationDepth( int value ) { m_maxSoundPropagationDepth = value; } @@ -56,3 +58,9 @@ void SpatialAudioSettings::SetEnableDiffractionAndTransmission( bool value ) { m bool SpatialAudioSettings::GetCalcEmitterVirtualPosition() const { return m_calcEmitterVirtualPosition; } void SpatialAudioSettings::SetCalcEmitterVirtualPosition( bool value ) { m_calcEmitterVirtualPosition = value; } + +bool SpatialAudioSettings::GetEnableDiffraction() const { return m_enableDiffraction; } +void SpatialAudioSettings::SetEnableDiffraction( bool value ) { m_enableDiffraction = value; } + +bool SpatialAudioSettings::GetEnableDiffractionOnBoundaryEdges() const { return m_enableDiffractionOnBoundaryEdges; } +void SpatialAudioSettings::SetEnableDiffractionOnBoundaryEdges( bool value ) { m_enableDiffractionOnBoundaryEdges = value; } diff --git a/src/SpatialAudioSettings.h b/src/SpatialAudioSettings.h index fcac01c..5362d42 100644 --- a/src/SpatialAudioSettings.h +++ b/src/SpatialAudioSettings.h @@ -7,7 +7,7 @@ #pragma once -enum class AudOcclusionMode : int +enum class AudOcclusion : int { Off = 0, // Occlusion disabled, no geometry registered, spatial audio geometry not initialized On = 1 // Wwise Spatial Audio handles diffraction + transmission @@ -32,8 +32,8 @@ class SpatialAudioSettings * When Off, no geometry is registered and spatial audio geometry is * not initialized. */ - AudOcclusionMode GetOcclusionMode() const; - void SetOcclusionMode( AudOcclusionMode value ); + AudOcclusion GetOcclusionMode() const; + void SetOcclusionMode( AudOcclusion value ); /** * @brief Maximum number of portals that sound can propagate through. @@ -185,8 +185,29 @@ class SpatialAudioSettings bool GetCalcEmitterVirtualPosition() const; void SetCalcEmitterVirtualPosition( bool value ); + /** + * @brief Enable diffraction on geometry edges. + * + * When enabled, sound can bend around edges of geometry. This adds + * realism but increases CPU usage. Diffraction must also be enabled + * globally via @c EnableDiffractionAndTransmission. + */ + bool GetEnableDiffraction() const; + void SetEnableDiffraction( bool value ); + + /** + * @brief Enable diffraction on boundary edges. + * + * Boundary edges are at the edges of the geometry mesh (not shared + * by two triangles). Enabling this generates diffraction edges for + * all boundary edges, which is more expensive but useful for + * incomplete meshes. + */ + bool GetEnableDiffractionOnBoundaryEdges() const; + void SetEnableDiffractionOnBoundaryEdges( bool value ); + private: - AudOcclusionMode m_occlusionMode; + AudOcclusion m_occlusionMode; int m_maxSoundPropagationDepth; float m_movementThreshold; int m_numberOfPrimaryRays; @@ -199,4 +220,6 @@ class SpatialAudioSettings int m_loadBalancingSpread; bool m_enableDiffractionAndTransmission; bool m_calcEmitterVirtualPosition; + bool m_enableDiffraction; + bool m_enableDiffractionOnBoundaryEdges; }; From accd75d9d4cd0048d9af7da01958539c21d3cec1 Mon Sep 17 00:00:00 2001 From: phevosccp <160875214+phevosccp@users.noreply.github.com> Date: Mon, 16 Mar 2026 10:56:45 +0000 Subject: [PATCH 44/48] update wrapper doxygen and remove reduntant parameter --- src/AudGeometry.h | 21 ++-- src/AudManager.cpp | 1 - src/AudManager.h | 2 - src/AudManager_Blue.cpp | 1 - src/SpatialAudioSettings.cpp | 4 - src/SpatialAudioSettings.h | 197 ++++++++++++++++------------------- 6 files changed, 96 insertions(+), 130 deletions(-) diff --git a/src/AudGeometry.h b/src/AudGeometry.h index 401bb69..22aeb5e 100644 --- a/src/AudGeometry.h +++ b/src/AudGeometry.h @@ -15,17 +15,13 @@ /** - * @brief Manages Wwise Spatial Audio geometry sets and geometry instances. + * @brief Registers meshes from Trinity as Spatial Audio geometry sets and manages their lifecycle. * - * Implements the ITr2AudGeometry interface to submit geometry to - * AK::SpatialAudio. A geometry set is a logical set of vertices, triangles, - * and acoustic surfaces (see AkGeometryParams). A geometry instance is a unique - * placement of a geometry set in the world with a specified transform — position, - * orientation, and scale (see AkGeometryInstanceParams). - * - * When @c AudOcclusion::On, geometry is registered with diffraction and - * transmission enabled. When @c AudOcclusion::Off, geometry registration - * is skipped entirely. + * Implements the ITr2AudGeometry interface to register meshes to + * AK::SpatialAudio as geeometry sets. A geometry set is a set of vertices, triangles, + * and acoustic surfaces (see AkGeometryParams). Each geometry instance represents a unique + * placement of a geometry set in the world with a transform — position, + * orientation, and scale. */ BLUE_CLASS(AudGeometry) : public ITr2AudGeometry @@ -41,12 +37,11 @@ BLUE_CLASS(AudGeometry) : // ITr2AudGeometry interface /** - * @brief Registers a ref-counted geometry set and places a geometry - * instance in the world via AK::SpatialAudio::SetGeometryInstance. + * @brief Registers a geometry set instance and places it in the world. * * @param geometrySetId Shared geometry set identifier. * @param instanceId Unique geometry instance identifier. - * @param geometryData Triangle mesh. + * @param geometryData Triangle mesh data. * @param worldTransform World position, orientation and scale. * */ diff --git a/src/AudManager.cpp b/src/AudManager.cpp index 7fb1085..9ef8a80 100644 --- a/src/AudManager.cpp +++ b/src/AudManager.cpp @@ -407,7 +407,6 @@ bool AudManager::InitSpatialAudioGeometry() { AkSpatialAudioInitSettings spatialSettings; - spatialSettings.uMaxSoundPropagationDepth = m_spatialAudioSettings->GetMaxSoundPropagationDepth(); spatialSettings.fMovementThreshold = m_spatialAudioSettings->GetMovementThreshold(); spatialSettings.uNumberOfPrimaryRays = m_spatialAudioSettings->GetNumberOfPrimaryRays(); spatialSettings.uMaxReflectionOrder = m_spatialAudioSettings->GetMaxReflectionOrder(); diff --git a/src/AudManager.h b/src/AudManager.h index 5b4073d..96ed1dc 100644 --- a/src/AudManager.h +++ b/src/AudManager.h @@ -322,7 +322,6 @@ BLUE_CLASS( AudManager ) : } // Getters - DELEGATE_SA_GETTER( int, GetMaxSoundPropagationDepth ) DELEGATE_SA_GETTER( float, GetMovementThreshold ) DELEGATE_SA_GETTER( int, GetNumberOfPrimaryRays ) DELEGATE_SA_GETTER( int, GetMaxReflectionOrder ) @@ -338,7 +337,6 @@ BLUE_CLASS( AudManager ) : DELEGATE_SA_GETTER( bool, GetEnableDiffractionOnBoundaryEdges ) // Setters - DELEGATE_SA_SETTER( int, SetMaxSoundPropagationDepth ) DELEGATE_SA_SETTER( float, SetMovementThreshold ) DELEGATE_SA_SETTER( int, SetNumberOfPrimaryRays ) DELEGATE_SA_SETTER( int, SetMaxReflectionOrder ) diff --git a/src/AudManager_Blue.cpp b/src/AudManager_Blue.cpp index 907ec03..69591c0 100644 --- a/src/AudManager_Blue.cpp +++ b/src/AudManager_Blue.cpp @@ -26,7 +26,6 @@ const Be::ClassInfo* AudManager::ExposeToBlue() // Spatial audio settings (applied at init when occlusion mode is On) MAP_PROPERTY( "occlusionMode", GetOcclusionModeInt, SetOcclusionModeInt, "Controls spatial audio occlusion. 0 = Off (no geometry, no occlusion), 1 = On (Wwise Spatial Audio diffraction + transmission).") MAP_PROPERTY( "globalTransmissionLoss", GetGlobalTransmissionLoss, SetGlobalTransmissionLoss, "Global transmission loss [0.0-1.0] applied to geometry surfaces when occlusion is enabled.") - MAP_PROPERTY( "maxSoundPropagationDepth", GetMaxSoundPropagationDepth, SetMaxSoundPropagationDepth, "Maximum number of portals that sound can propagate through.") MAP_PROPERTY( "movementThreshold", GetMovementThreshold, SetMovementThreshold, "Distance an emitter or listener must move to trigger a re-validation of reflections/diffraction.") MAP_PROPERTY( "numberOfPrimaryRays", GetNumberOfPrimaryRays, SetNumberOfPrimaryRays, "Number of primary rays used in the ray tracing engine. More rays = better quality but higher CPU.") MAP_PROPERTY( "maxReflectionOrder", GetMaxReflectionOrder, SetMaxReflectionOrder, "Maximum reflection order [1-4] - number of bounces in a reflection path.") diff --git a/src/SpatialAudioSettings.cpp b/src/SpatialAudioSettings.cpp index 6249ce0..dc1141b 100644 --- a/src/SpatialAudioSettings.cpp +++ b/src/SpatialAudioSettings.cpp @@ -3,7 +3,6 @@ SpatialAudioSettings::SpatialAudioSettings() : m_occlusionMode( AudOcclusion::On ) - , m_maxSoundPropagationDepth( 1 ) , m_movementThreshold( 100.0f ) , m_numberOfPrimaryRays( 35 ) , m_maxReflectionOrder( 0 ) @@ -23,9 +22,6 @@ SpatialAudioSettings::SpatialAudioSettings() AudOcclusion SpatialAudioSettings::GetOcclusionMode() const { return m_occlusionMode; } void SpatialAudioSettings::SetOcclusionMode( AudOcclusion value ) { m_occlusionMode = value; } -int SpatialAudioSettings::GetMaxSoundPropagationDepth() const { return m_maxSoundPropagationDepth; } -void SpatialAudioSettings::SetMaxSoundPropagationDepth( int value ) { m_maxSoundPropagationDepth = value; } - float SpatialAudioSettings::GetMovementThreshold() const { return m_movementThreshold; } void SpatialAudioSettings::SetMovementThreshold( float value ) { m_movementThreshold = value; } diff --git a/src/SpatialAudioSettings.h b/src/SpatialAudioSettings.h index 5362d42..cb5de34 100644 --- a/src/SpatialAudioSettings.h +++ b/src/SpatialAudioSettings.h @@ -14,11 +14,8 @@ enum class AudOcclusion : int }; /** - * @brief Configuration for Wwise Spatial Audio initialization. + * @brief A wrapper with configuration settings for Wwise Spatial Audio initialization. * - * Holds all parameters that map to AkSpatialAudioInitSettings. - * Values are set before AudManager::Enable() and consumed by - * InitSpatialAudioGeometry(). */ class SpatialAudioSettings { @@ -27,188 +24,170 @@ class SpatialAudioSettings /** * @brief Controls whether spatial audio occlusion is On or Off. - * - * When On, Wwise Spatial Audio handles diffraction and transmission. - * When Off, no geometry is registered and spatial audio geometry is - * not initialized. */ AudOcclusion GetOcclusionMode() const; void SetOcclusionMode( AudOcclusion value ); /** - * @brief Maximum number of portals that sound can propagate through. + * @brief Amount that an emitter or listener has to move to trigger a validation of reflections/diffraction. * - * Must be less than or equal to AK_MAX_SOUND_PROPAGATION_DEPTH. - */ - int GetMaxSoundPropagationDepth() const; - void SetMaxSoundPropagationDepth( int value ); - - /** - * @brief Amount that an emitter or listener has to move to trigger a - * re-validation of reflections/diffraction. - * - * Larger values reduce CPU load at the cost of reduced accuracy. - * The ray tracing itself is not affected by this value — rays are - * cast each time a Spatial Audio update is executed. + * Larger values can reduce the CPU load at the cost of reduced accuracy. + * Note that the ray tracing itself is not affected by this value. + * Rays are cast each time a Spatial Audio update is executed. */ float GetMovementThreshold() const; void SetMovementThreshold( float value ); /** - * @brief Number of primary rays used in the ray tracing engine. + * @brief The number of primary rays used in the ray tracing engine. * - * A larger number of rays increases the chances of finding reflection - * and diffraction paths, but results in higher CPU usage. When CPU - * limit is active (see @c cpuLimitPercentage), this setting represents - * the maximum allowed number of primary rays. + * A larger number of rays will increase the chances of finding reflection and diffraction paths, + * but will result in higher CPU usage. When CPU limit is active + * (see @c fCPULimitPercentage), this setting represents the maximum + * allowed number of primary rays. */ int GetNumberOfPrimaryRays() const; void SetNumberOfPrimaryRays( int value ); /** - * @brief Maximum reflection order [1, 4] — the number of 'bounces' - * in a reflection path. + * @brief Maximum reflection order [1, 4] - the number of 'bounces' in a reflection path. * - * A high reflection order renders more details at the expense of - * higher CPU usage. + * A high reflection order renders more details at the expense of higher CPU usage. */ int GetMaxReflectionOrder() const; void SetMaxReflectionOrder( int value ); /** - * @brief Maximum diffraction order [1, 8] — the number of 'bends' - * in a diffraction path. - * - * A high diffraction order accommodates more complex geometry at the - * expense of higher CPU usage. Set to 0 to disable diffraction on - * all geometry. Diffraction must be enabled on the geometry itself - * (see AkGeometryParams). - * - * This limits the recursion depth of diffraction rays cast from the - * listener and the depth of the diffraction search between emitter - * and listener. To optimize CPU, set it to the maximum number of - * edges you expect geometry to traverse (e.g. 2 for a single box). - * - * A search starts from the listener; when the maximum order is - * exceeded, remaining geometry between the path's end and the emitter - * is ignored, causing the diffraction coefficient to be underestimated. + * @brief Maximum diffraction order [1, 8] - the number of 'bends' in a diffraction path. + * + * A high diffraction order accommodates more complex geometry at the expense of higher CPU usage. + * Diffraction must be enabled on the geometry to find diffraction paths + * (refer to @c AkGeometryParams). Set to 0 to disable diffraction on all geometry. + * This parameter limits the recursion depth of diffraction rays cast from the listener + * to scan the environment, and also the depth of the diffraction search to find paths + * between emitter and listener. + * To optimize CPU usage, set it to the maximum number of edges you expect the obstructing + * geometry to traverse. For example, if box-shaped geometry is used exclusively, and only + * a single box is expected between an emitter and the listener, limiting @c uMaxDiffractionOrder + * to 2 may be sufficient. + * A diffraction path search starts from the listener, so when the maximum diffraction order + * is exceeded, the remaining geometry between the end of the path and the emitter is ignored. + * In such case, where the search is terminated before reaching the emitter, the diffraction + * coefficient will be underestimated. It is calculated from a partial path, ignoring any + * remaining geometry. */ int GetMaxDiffractionOrder() const; void SetMaxDiffractionOrder( int value ); /** - * @brief Maximum number of game-defined auxiliary sends that can - * originate from a single emitter. + * @brief The maximum number of game-defined auxiliary sends that can originate from a single emitter. * - * An emitter can send to its own room and to all adjacent rooms if - * the emitter and listener are in the same room. If a limit is set, - * the most prominent sends are kept based on spread to the adjacent - * portal from the emitter's perspective. Set to 1 to only allow - * emitters to send directly to their current room. Set to 0 to - * disable the limit. + * An emitter can send to its own room, and to all adjacent rooms if the emitter and listener + * are in the same room. If a limit is set, the most prominent sends are kept, based on spread + * to the adjacent portal from the emitter's perspective. + * Set to 1 to only allow emitters to send directly to their current room, and to the room + * a listener is transitioning to if inside a portal. Set to 0 to disable the limit. */ int GetMaxEmitterRoomAuxSends() const; void SetMaxEmitterRoomAuxSends( int value ); /** - * @brief Maximum number of diffraction points at each end of a - * reflection path. - * - * Diffraction on reflections allows reflections to fade in and out - * smoothly as the listener or emitter moves in and out of a - * reflection's shadow zone. When greater than zero, diffraction rays - * are sent from the listener to search for reflections around corners. - * Set to 0 to disable diffraction on reflections. Set to 2 or greater - * to allow reflections to propagate through portals without being - * cut off. + * @brief The maximum possible number of diffraction points at each end of a reflection path. + * + * Diffraction on reflection allows reflections to fade in and out smoothly as the listener + * or emitter moves in and out of the reflection's shadow zone. + * When greater than zero, diffraction rays are sent from the listener to search for reflections + * around one or more corners from the listener. + * Diffraction must be enabled on the geometry to find diffracted reflections + * (refer to @c AkGeometryParams). Set to 0 to disable diffraction on reflections. + * To allow reflections to propagate through portals without being cut off, + * set @c uDiffractionOnReflectionsOrder to 2 or greater. */ int GetDiffractionOnReflectionsOrder() const; void SetDiffractionOnReflectionsOrder( int value ); /** - * @brief Maximum total length of a path composed of a sequence of - * segments (rays). - * - * High values compute longer paths but increase CPU cost. Each - * individual sound is also affected by its maximum attenuation - * distance specified in the Wwise Authoring tool. Reflection or - * diffraction paths will never exceed a sound's maximum attenuation - * distance (unless the furthest point is above the audibility - * threshold, in which case attenuation is considered infinite). + * @brief The total length of a path composed of a sequence of segments (or rays) cannot exceed + * the defined maximum path length. + * + * High values compute longer paths but increase the CPU cost. + * Each individual sound is also affected by its maximum attenuation distance, specified + * in the Authoring tool. Reflection or diffraction paths, calculated inside Spatial Audio, + * will never exceed a sound's maximum attenuation distance. + * Note, however, that attenuation is considered infinite if the furthest point is above + * the audibility threshold. */ float GetMaxPathLength() const; void SetMaxPathLength( float value ); /** - * @brief Targeted computation time allocated for the ray tracing - * engine, as a percentage [0, 100] of the current audio frame. - * - * The ray tracing engine dynamically adapts the number of primary - * rays to target the specified computation time. The computed number - * of primary rays will never exceed @c numberOfPrimaryRays. - * A value of 0 indicates no target has been set — the number of - * primary rays is then fixed at @c numberOfPrimaryRays. + * @brief Defines the targeted computation time allocated for the ray tracing engine. + * + * Defined as a percentage [0, 100] of the current audio frame. + * The ray tracing engine dynamically adapts the number of primary rays to target + * the specified computation time value. In all circumstances, the computed number + * of primary rays cannot exceed the number of primary rays specified by + * @c uNumberOfPrimaryRays. + * A value of 0 indicates no target has been set. In this case, the number of primary + * rays is fixed and is set by @c uNumberOfPrimaryRays. */ float GetCPULimitPercentage() const; void SetCPULimitPercentage( float value ); /** - * @brief Spread path computation over N frames [1, ..]. + * @brief Spread the computation of paths on uLoadBalancingSpread frames [1, ..]. * - * When set to 1, no load balancing is done. Values greater than 1 - * spread the computation of paths across that many frames. + * When uLoadBalancingSpread is set to 1, no load balancing is done. + * Values greater than 1 indicate the computation of paths will be spread + * on this number of frames. */ int GetLoadBalancingSpread() const; void SetLoadBalancingSpread( int value ); /** - * @brief Enable computation of geometric diffraction and transmission - * paths for all sources that have "Enable Diffraction and - * Transmission" checked in the Wwise Positioning tab. - * - * This flag enables sound paths around (diffraction) and through - * (transmission) geometry. Setting to false implies geometry is only - * used for reflection calculation. If false but a sound has diffraction - * enabled in Wwise, the sound will diffract through portals but pass - * through geometry as if it were not there. + * @brief Enable computation of geometric diffraction and transmission paths for all sources + * that have the "Enable Diffraction and Transmission" box checked in the Positioning + * tab of the Wwise Property Editor. + * + * This flag enables sound paths around (diffraction) and through (transmission) geometry + * (see @c AK::SpatialAudio::SetGeometry). + * Setting @c bEnableGeometricDiffractionAndTransmission to false implies that geometry + * is only to be used for reflection calculation. + * Diffraction edges must be enabled on geometry for diffraction calculation + * (see @c AkGeometryParams). + * If @c bEnableGeometricDiffractionAndTransmission is false but a sound has + * "Enable Diffraction and Transmission" selected in the Positioning tab of the authoring tool, + * the sound will diffract through portals but will pass through geometry as if it is not there. + * One would typically disable this setting in the case that the game intends to perform its own + * obstruction calculation, but geometry is still passed to spatial audio for reflection calculation. */ bool GetEnableDiffractionAndTransmission() const; void SetEnableDiffractionAndTransmission( bool value ); /** - * @brief Calculate virtual positions for emitters diffracted through - * a portal or around geometry. - * - * The apparent or virtual position is calculated by Wwise Spatial - * Audio and passed on to the sound engine. + * @brief An emitter that is diffracted through a portal or around geometry will have its + * apparent or virtual position calculated by Wwise Spatial Audio and passed on to + * the sound engine. */ bool GetCalcEmitterVirtualPosition() const; void SetCalcEmitterVirtualPosition( bool value ); /** - * @brief Enable diffraction on geometry edges. - * - * When enabled, sound can bend around edges of geometry. This adds - * realism but increases CPU usage. Diffraction must also be enabled - * globally via @c EnableDiffractionAndTransmission. + * @brief Switch to enable or disable geometric diffraction for this Geometry. */ bool GetEnableDiffraction() const; void SetEnableDiffraction( bool value ); /** - * @brief Enable diffraction on boundary edges. + * @brief Switch to enable or disable geometric diffraction on boundary edges for this Geometry. * - * Boundary edges are at the edges of the geometry mesh (not shared - * by two triangles). Enabling this generates diffraction edges for - * all boundary edges, which is more expensive but useful for - * incomplete meshes. + * Boundary edges are edges that are connected to only one triangle. */ bool GetEnableDiffractionOnBoundaryEdges() const; void SetEnableDiffractionOnBoundaryEdges( bool value ); private: AudOcclusion m_occlusionMode; - int m_maxSoundPropagationDepth; float m_movementThreshold; int m_numberOfPrimaryRays; int m_maxReflectionOrder; From 9097f3984bcf78cb7141ffc6b1d86ca09459dcf4 Mon Sep 17 00:00:00 2001 From: phevosccp <160875214+phevosccp@users.noreply.github.com> Date: Mon, 16 Mar 2026 10:59:00 +0000 Subject: [PATCH 45/48] make occlusion off by default --- src/SpatialAudioSettings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SpatialAudioSettings.cpp b/src/SpatialAudioSettings.cpp index dc1141b..318b400 100644 --- a/src/SpatialAudioSettings.cpp +++ b/src/SpatialAudioSettings.cpp @@ -2,7 +2,7 @@ #include "SpatialAudioSettings.h" SpatialAudioSettings::SpatialAudioSettings() - : m_occlusionMode( AudOcclusion::On ) + : m_occlusionMode( AudOcclusion::Off ) , m_movementThreshold( 100.0f ) , m_numberOfPrimaryRays( 35 ) , m_maxReflectionOrder( 0 ) From 7d99ddb095b9ca8d23969f70c6e2f3510cb834c8 Mon Sep 17 00:00:00 2001 From: phevosccp <160875214+phevosccp@users.noreply.github.com> Date: Mon, 16 Mar 2026 11:13:21 +0000 Subject: [PATCH 46/48] make a method that populates all spatial settings to debloat the poor AudManager --- src/AudGameObjResource.cpp | 2 +- src/AudManager.cpp | 18 ++++-------------- src/AudManager.h | 2 +- src/SpatialAudioSettings.cpp | 16 ++++++++++++++++ src/SpatialAudioSettings.h | 7 +++++++ 5 files changed, 29 insertions(+), 16 deletions(-) diff --git a/src/AudGameObjResource.cpp b/src/AudGameObjResource.cpp index dc4e2a9..47eae7c 100644 --- a/src/AudGameObjResource.cpp +++ b/src/AudGameObjResource.cpp @@ -581,7 +581,7 @@ void AudGameObjResource::Wake() return; } - RegisterWwiseObject(); + RegisterWwiseObject(); SetPositionHelper( Vector3( 1,0,0 ), Vector3( 0,1,0 ), m_position ); m_culled = false; if ( m_waitingOneShotInRange.second != L"" && m_listenerInRange ) diff --git a/src/AudManager.cpp b/src/AudManager.cpp index 9ef8a80..7558d77 100644 --- a/src/AudManager.cpp +++ b/src/AudManager.cpp @@ -73,7 +73,8 @@ AudManager::AudManager( IRoot* lockobj ) : m_moniteredParametersMapMutex( "AudManager", "m_monitoredParametersMapMutex" ), m_soundBankMutex( "AudManager", "m_soundBankMutex" ), m_isProfilerCapturing( false ), - m_audioCullingEnabled( true ) + m_audioCullingEnabled( true ), + m_globalTransmissionLoss( 0.7f ) { // Initialize sound prioritization system m_soundPrioritization = new SoundPrioritization(); @@ -157,7 +158,7 @@ bool AudManager::Init() { if( !InitSpatialAudioGeometry() ) { - CCP_LOGERR( "Failed to initialize audio : Spatial Audio Geometry" ); + CCP_LOGERR( "Failed to initialize Spatial Audio Geometry" ); return false; } } @@ -406,18 +407,7 @@ bool AudManager::InitMusic() bool AudManager::InitSpatialAudioGeometry() { AkSpatialAudioInitSettings spatialSettings; - - spatialSettings.fMovementThreshold = m_spatialAudioSettings->GetMovementThreshold(); - spatialSettings.uNumberOfPrimaryRays = m_spatialAudioSettings->GetNumberOfPrimaryRays(); - spatialSettings.uMaxReflectionOrder = m_spatialAudioSettings->GetMaxReflectionOrder(); - spatialSettings.uMaxDiffractionOrder = m_spatialAudioSettings->GetMaxDiffractionOrder(); - spatialSettings.uMaxEmitterRoomAuxSends = m_spatialAudioSettings->GetMaxEmitterRoomAuxSends(); - spatialSettings.uDiffractionOnReflectionsOrder = m_spatialAudioSettings->GetDiffractionOnReflectionsOrder(); - spatialSettings.fMaxPathLength = m_spatialAudioSettings->GetMaxPathLength(); - spatialSettings.fCPULimitPercentage = m_spatialAudioSettings->GetCPULimitPercentage(); - spatialSettings.uLoadBalancingSpread = m_spatialAudioSettings->GetLoadBalancingSpread(); - spatialSettings.bEnableGeometricDiffractionAndTransmission = m_spatialAudioSettings->GetEnableDiffractionAndTransmission(); - spatialSettings.bCalcEmitterVirtualPosition = m_spatialAudioSettings->GetCalcEmitterVirtualPosition(); + m_spatialAudioSettings->PopulateInitSettings( spatialSettings ); if( AK::SpatialAudio::Init( spatialSettings ) != AK_Success ) { diff --git a/src/AudManager.h b/src/AudManager.h index 96ed1dc..1d71c15 100644 --- a/src/AudManager.h +++ b/src/AudManager.h @@ -224,7 +224,7 @@ BLUE_CLASS( AudManager ) : // Signals whether Carbon Audio's spatial audio features are enabled. If the user currently doesn't have an active spatial audio endpoint then output will still be in stereo. bool m_spatialAudioEnabled; // Global transmission loss [0.0-1.0] applied to geometry surfaces. - float m_globalTransmissionLoss = 0.7f; + float m_globalTransmissionLoss; mutable bool m_audioCullingEnabled; std::map m_soundBankInfoMap; diff --git a/src/SpatialAudioSettings.cpp b/src/SpatialAudioSettings.cpp index 318b400..3698461 100644 --- a/src/SpatialAudioSettings.cpp +++ b/src/SpatialAudioSettings.cpp @@ -1,5 +1,6 @@ #include "stdafx.h" #include "SpatialAudioSettings.h" +#include SpatialAudioSettings::SpatialAudioSettings() : m_occlusionMode( AudOcclusion::Off ) @@ -60,3 +61,18 @@ void SpatialAudioSettings::SetEnableDiffraction( bool value ) { m_enableDiffract bool SpatialAudioSettings::GetEnableDiffractionOnBoundaryEdges() const { return m_enableDiffractionOnBoundaryEdges; } void SpatialAudioSettings::SetEnableDiffractionOnBoundaryEdges( bool value ) { m_enableDiffractionOnBoundaryEdges = value; } + +void SpatialAudioSettings::PopulateInitSettings( AkSpatialAudioInitSettings& out ) const +{ + out.fMovementThreshold = m_movementThreshold; + out.uNumberOfPrimaryRays = m_numberOfPrimaryRays; + out.uMaxReflectionOrder = m_maxReflectionOrder; + out.uMaxDiffractionOrder = m_maxDiffractionOrder; + out.uMaxEmitterRoomAuxSends = m_maxEmitterRoomAuxSends; + out.uDiffractionOnReflectionsOrder = m_diffractionOnReflectionsOrder; + out.fMaxPathLength = m_maxPathLength; + out.fCPULimitPercentage = m_cpuLimitPercentage; + out.uLoadBalancingSpread = m_loadBalancingSpread; + out.bEnableGeometricDiffractionAndTransmission = m_enableDiffractionAndTransmission; + out.bCalcEmitterVirtualPosition = m_calcEmitterVirtualPosition; +} diff --git a/src/SpatialAudioSettings.h b/src/SpatialAudioSettings.h index cb5de34..8d52600 100644 --- a/src/SpatialAudioSettings.h +++ b/src/SpatialAudioSettings.h @@ -7,6 +7,8 @@ #pragma once +struct AkSpatialAudioInitSettings; + enum class AudOcclusion : int { Off = 0, // Occlusion disabled, no geometry registered, spatial audio geometry not initialized @@ -186,6 +188,11 @@ class SpatialAudioSettings bool GetEnableDiffractionOnBoundaryEdges() const; void SetEnableDiffractionOnBoundaryEdges( bool value ); + /** + * @brief Populates an AkSpatialAudioInitSettings struct from the current settings. + */ + void PopulateInitSettings( AkSpatialAudioInitSettings& out ) const; + private: AudOcclusion m_occlusionMode; float m_movementThreshold; From b5ef3c645f83f37e745bf3105e915c8d9b51a5f5 Mon Sep 17 00:00:00 2001 From: phevosccp <160875214+phevosccp@users.noreply.github.com> Date: Mon, 16 Mar 2026 15:49:52 +0000 Subject: [PATCH 47/48] - Replace AudOcclusion enum with a bool spatial audio geometry toggle on SpatialAudioSettings - Add runtime enable/disable of spatial audio geometry with geometry cleanup - More minor fixes all around --- src/AudGeometry.cpp | 24 ++++++++++++-- src/AudGeometry.h | 3 ++ src/AudManager.cpp | 62 +++++++++++++++++++++++++----------- src/AudManager.h | 20 +++++------- src/AudManager_Blue.cpp | 10 +++--- src/SpatialAudioSettings.cpp | 10 ++++-- src/SpatialAudioSettings.h | 26 +++++++-------- 7 files changed, 101 insertions(+), 54 deletions(-) diff --git a/src/AudGeometry.cpp b/src/AudGeometry.cpp index 4455b4c..e73f74c 100644 --- a/src/AudGeometry.cpp +++ b/src/AudGeometry.cpp @@ -43,6 +43,24 @@ AudGeometry::AudGeometry( IRoot* lockobj ) AudGeometry::~AudGeometry() {} +void AudGeometry::ClearAllGeometry() +{ + if( s_geometrySetRefCounts.empty() ) + { + return; + } + + if( AK::SoundEngine::IsInitialized() ) + { + for( const auto& geometrySetEntry : s_geometrySetRefCounts ) + { + AK::SpatialAudio::RemoveGeometry( geometrySetEntry.first ); + } + } + + s_geometrySetRefCounts.clear(); +} + AkGeometryInstanceParams AudGeometry::MakeInstanceParams( uint64_t geometrySetId, const Matrix& worldTransform ) { @@ -66,7 +84,7 @@ void AudGeometry::SetGeometry( return; } - if( !g_audioManager || g_audioManager->GetOcclusionMode() == AudOcclusion::Off ) + if( !g_audioManager || !g_audioManager->GetSpatialAudioGeometryEnabled() ) { return; } @@ -80,7 +98,7 @@ void AudGeometry::SetGeometry( AkAcousticSurface surface; surface.strName = "default"; surface.textureID = AK_INVALID_UNIQUE_ID; - surface.transmissionLoss = g_audioManager->GetGlobalTransmissionLoss(); + surface.transmissionLoss = g_audioManager->GetTransmissionLoss(); AkGeometryParams params; params.Vertices = akVertices.data(); @@ -120,7 +138,7 @@ void AudGeometry::SetGeometryTransform( uint64_t instanceId, const Matrix& worldTransform ) { - if( !g_audioManager || g_audioManager->GetOcclusionMode() == AudOcclusion::Off ) + if( !g_audioManager || !g_audioManager->GetSpatialAudioGeometryEnabled() ) { return; } diff --git a/src/AudGeometry.h b/src/AudGeometry.h index 22aeb5e..c6159ff 100644 --- a/src/AudGeometry.h +++ b/src/AudGeometry.h @@ -76,6 +76,9 @@ BLUE_CLASS(AudGeometry) : uint64_t geometrySetId, uint64_t instanceId) override; + /// Removes all registered geometry sets from the Wwise engine. + static void ClearAllGeometry(); + private: /// Builds AkGeometryInstanceParams from a geometry set ID and world transform. static AkGeometryInstanceParams MakeInstanceParams( diff --git a/src/AudManager.cpp b/src/AudManager.cpp index 7558d77..ec07baa 100644 --- a/src/AudManager.cpp +++ b/src/AudManager.cpp @@ -31,6 +31,7 @@ #include "AudActionLog.h" #include "AudEmitter.h" +#include "AudGeometry.h" #include "AudSettings.h" #include "AudStaticDataRepository.h" #include "LogBridge.h" @@ -70,11 +71,11 @@ AudManager::AudManager( IRoot* lockobj ) : m_asyncOpen( true ), m_log(), m_spatialAudioEnabled( true ), + m_spatialAudioGeometryInitialized( false ), m_moniteredParametersMapMutex( "AudManager", "m_monitoredParametersMapMutex" ), m_soundBankMutex( "AudManager", "m_soundBankMutex" ), m_isProfilerCapturing( false ), - m_audioCullingEnabled( true ), - m_globalTransmissionLoss( 0.7f ) + m_audioCullingEnabled( true ) { // Initialize sound prioritization system m_soundPrioritization = new SoundPrioritization(); @@ -130,6 +131,8 @@ AkBankID AudManager::ComputeWwiseHashForSoundBank( const std::wstring& soundBank bool AudManager::Init() { + m_spatialAudioGeometryInitialized = false; + if( g_staticDataRepository == nullptr || !g_staticDataRepository->IsInitialized() ) { CCP_LOGERR( "The static data repository in audio2 has not been generated and needs to exist for audio2 " @@ -154,7 +157,7 @@ bool AudManager::Init() return false; } - if( m_spatialAudioSettings->GetOcclusionMode() == AudOcclusion::On ) + if( m_spatialAudioSettings->GetSpatialAudioGeometryEnabled() ) { if( !InitSpatialAudioGeometry() ) { @@ -204,6 +207,7 @@ void AudManager::Terminate() // Terminate the Memory Manager AK::MemoryMgr::Term(); + m_spatialAudioGeometryInitialized = false; g_audioInitialized = false; } @@ -406,6 +410,11 @@ bool AudManager::InitMusic() bool AudManager::InitSpatialAudioGeometry() { + if( m_spatialAudioGeometryInitialized ) + { + return true; + } + AkSpatialAudioInitSettings spatialSettings; m_spatialAudioSettings->PopulateInitSettings( spatialSettings ); @@ -415,6 +424,7 @@ bool AudManager::InitSpatialAudioGeometry() return false; } + m_spatialAudioGeometryInitialized = true; CCP_LOG_CH( s_ch, "Wwise Spatial Audio Geometry initialized" ); return true; } @@ -447,29 +457,42 @@ bool AudManager::SetState( const std::wstring& stateGroup, const std::wstring& s return false; } -int AudManager::GetOcclusionModeInt() const +bool AudManager::GetSpatialAudioGeometryEnabled() const { - return static_cast( m_spatialAudioSettings->GetOcclusionMode() ); + return m_spatialAudioSettings->GetSpatialAudioGeometryEnabled(); } -void AudManager::SetOcclusionModeInt( int value ) +void AudManager::SetSpatialAudioGeometryEnabled( bool enabled ) { - m_spatialAudioSettings->SetOcclusionMode( static_cast( value ) ); -} + const bool wasEnabled = GetSpatialAudioGeometryEnabled(); + if( wasEnabled == enabled ) + { + return; + } -AudOcclusion AudManager::GetOcclusionMode() const -{ - return m_spatialAudioSettings->GetOcclusionMode(); -} + if( !g_audioInitialized ) + { + m_spatialAudioSettings->SetSpatialAudioGeometryEnabled( enabled ); + return; + } -float AudManager::GetGlobalTransmissionLoss() const -{ - return m_globalTransmissionLoss; -} + if( !enabled ) + { + m_spatialAudioSettings->SetSpatialAudioGeometryEnabled( false ); + AudGeometry::ClearAllGeometry(); + CCP_LOG_CH( s_ch, "Spatial audio geometry disabled." ); + } + else + { + if( !InitSpatialAudioGeometry() ) + { + CCP_LOGERR_CH( s_ch, "Spatial audio geometry failed to initialize." ); + return; + } -void AudManager::SetGlobalTransmissionLoss( float value ) -{ - m_globalTransmissionLoss = std::max( 0.0f, std::min( 1.0f, value ) ); + m_spatialAudioSettings->SetSpatialAudioGeometryEnabled( true ); + CCP_LOG_CH( s_ch, "Spatial audio geometry enabled." ); + } } const bool AudManager::SpatialAudioIsSupported() @@ -726,6 +749,7 @@ void AudManager::Disable() #ifndef AK_OPTIMIZED AK::SoundEngine::UnregisterResourceMonitorCallback(ResourceMonitorCallback); #endif + AudGeometry::ClearAllGeometry(); Terminate(); g_audioEnabled = false; diff --git a/src/AudManager.h b/src/AudManager.h index 1d71c15..b722f70 100644 --- a/src/AudManager.h +++ b/src/AudManager.h @@ -115,16 +115,10 @@ BLUE_CLASS( AudManager ) : bool SetGlobalRTPC( const std::wstring& rtpcName, float value ); // Set a global state in Wwise. bool SetState( const std::wstring& stateGroup, const std::wstring& stateName ); - // Returns the occlusion mode as an integer for Blue property exposure. - int GetOcclusionModeInt() const; - // Sets the occlusion mode from an integer value. - void SetOcclusionModeInt( int value ); - // Returns the occlusion mode enum. - AudOcclusion GetOcclusionMode() const; - // Returns the global transmission loss [0.0-1.0] used for geometry surfaces. - float GetGlobalTransmissionLoss() const; - // Sets the global transmission loss [0.0-1.0]. - void SetGlobalTransmissionLoss( float value ); + // Returns whether spatial audio geometry is enabled. + bool GetSpatialAudioGeometryEnabled() const; + // Enables or disables spatial audio geometry. + void SetSpatialAudioGeometryEnabled( bool enabled ); // Can be called to see if the current platform supports spatial audio. const bool SpatialAudioIsSupported(); // Stop all currently playing sounds on all game objects. @@ -223,8 +217,8 @@ BLUE_CLASS( AudManager ) : bool m_asyncOpen; // Signals whether Carbon Audio's spatial audio features are enabled. If the user currently doesn't have an active spatial audio endpoint then output will still be in stereo. bool m_spatialAudioEnabled; - // Global transmission loss [0.0-1.0] applied to geometry surfaces. - float m_globalTransmissionLoss; + // Tracks whether Wwise Spatial Audio geometry has been initialized in the current audio-engine lifetime. + bool m_spatialAudioGeometryInitialized; mutable bool m_audioCullingEnabled; std::map m_soundBankInfoMap; @@ -333,6 +327,7 @@ BLUE_CLASS( AudManager ) : DELEGATE_SA_GETTER( int, GetLoadBalancingSpread ) DELEGATE_SA_GETTER( bool, GetEnableDiffractionAndTransmission ) DELEGATE_SA_GETTER( bool, GetCalcEmitterVirtualPosition ) + DELEGATE_SA_GETTER( float, GetTransmissionLoss ) DELEGATE_SA_GETTER( bool, GetEnableDiffraction ) DELEGATE_SA_GETTER( bool, GetEnableDiffractionOnBoundaryEdges ) @@ -348,6 +343,7 @@ BLUE_CLASS( AudManager ) : DELEGATE_SA_SETTER( int, SetLoadBalancingSpread ) DELEGATE_SA_SETTER( bool, SetEnableDiffractionAndTransmission ) DELEGATE_SA_SETTER( bool, SetCalcEmitterVirtualPosition ) + DELEGATE_SA_SETTER( float, SetTransmissionLoss ) DELEGATE_SA_SETTER( bool, SetEnableDiffraction ) DELEGATE_SA_SETTER( bool, SetEnableDiffractionOnBoundaryEdges ) diff --git a/src/AudManager_Blue.cpp b/src/AudManager_Blue.cpp index 69591c0..dc1b5c8 100644 --- a/src/AudManager_Blue.cpp +++ b/src/AudManager_Blue.cpp @@ -23,9 +23,8 @@ const Be::ClassInfo* AudManager::ExposeToBlue() MAP_PROPERTY( "visibleWeight", GetVisibleWeight, SetVisibleWeight, "The weight applied to a game object if it is visible to the listener.") MAP_PROPERTY( "playing2DWeight", GetPlaying2DWeight, SetPlaying2DWeight, "The weight applied to a game object if it is currently playing a 2D sound.") - // Spatial audio settings (applied at init when occlusion mode is On) - MAP_PROPERTY( "occlusionMode", GetOcclusionModeInt, SetOcclusionModeInt, "Controls spatial audio occlusion. 0 = Off (no geometry, no occlusion), 1 = On (Wwise Spatial Audio diffraction + transmission).") - MAP_PROPERTY( "globalTransmissionLoss", GetGlobalTransmissionLoss, SetGlobalTransmissionLoss, "Global transmission loss [0.0-1.0] applied to geometry surfaces when occlusion is enabled.") + // Spatial audio geometry settings + MAP_PROPERTY( "spatialAudioGeometryEnabled", GetSpatialAudioGeometryEnabled, SetSpatialAudioGeometryEnabled, "Enable or disable spatial audio geometry.") MAP_PROPERTY( "movementThreshold", GetMovementThreshold, SetMovementThreshold, "Distance an emitter or listener must move to trigger a re-validation of reflections/diffraction.") MAP_PROPERTY( "numberOfPrimaryRays", GetNumberOfPrimaryRays, SetNumberOfPrimaryRays, "Number of primary rays used in the ray tracing engine. More rays = better quality but higher CPU.") MAP_PROPERTY( "maxReflectionOrder", GetMaxReflectionOrder, SetMaxReflectionOrder, "Maximum reflection order [1-4] - number of bounces in a reflection path.") @@ -37,6 +36,9 @@ const Be::ClassInfo* AudManager::ExposeToBlue() MAP_PROPERTY( "loadBalancingSpread", GetLoadBalancingSpread, SetLoadBalancingSpread, "Spread path computation over N frames [1..]. 1 = no load balancing.") MAP_PROPERTY( "enableDiffractionAndTransmission", GetEnableDiffractionAndTransmission, SetEnableDiffractionAndTransmission, "Enable geometric diffraction and transmission path computation.") MAP_PROPERTY( "calcEmitterVirtualPosition", GetCalcEmitterVirtualPosition, SetCalcEmitterVirtualPosition, "Calculate virtual position for emitters diffracted through portals or around geometry.") + MAP_PROPERTY( "transmissionLoss", GetTransmissionLoss, SetTransmissionLoss, "Per-mesh setting: transmission loss [0.0-1.0] applied to geometry surfaces when meshes are registered.") + MAP_PROPERTY( "enableDiffraction", GetEnableDiffraction, SetEnableDiffraction, "Per-mesh setting: enable or disable geometric diffraction on mesh geometry.") + MAP_PROPERTY( "enableDiffractionOnBoundaryEdges", GetEnableDiffractionOnBoundaryEdges, SetEnableDiffractionOnBoundaryEdges, "Per-mesh setting: switch to enable or disable geometric diffraction on boundary edges for this mesh.") MAP_METHOD_AND_WRAP ( @@ -217,4 +219,4 @@ const Be::ClassInfo* AudManager::ExposeToBlue() ":return: True if the profiler is capturing, False otherwise." ) EXPOSURE_END() -} \ No newline at end of file +} diff --git a/src/SpatialAudioSettings.cpp b/src/SpatialAudioSettings.cpp index 3698461..d1332ff 100644 --- a/src/SpatialAudioSettings.cpp +++ b/src/SpatialAudioSettings.cpp @@ -3,7 +3,7 @@ #include SpatialAudioSettings::SpatialAudioSettings() - : m_occlusionMode( AudOcclusion::Off ) + : m_spatialAudioGeometryEnabled( false ) , m_movementThreshold( 100.0f ) , m_numberOfPrimaryRays( 35 ) , m_maxReflectionOrder( 0 ) @@ -15,13 +15,14 @@ SpatialAudioSettings::SpatialAudioSettings() , m_loadBalancingSpread( 1 ) , m_enableDiffractionAndTransmission( true ) , m_calcEmitterVirtualPosition( true ) + , m_transmissionLoss( 0.7f ) , m_enableDiffraction( true ) , m_enableDiffractionOnBoundaryEdges( true ) { } -AudOcclusion SpatialAudioSettings::GetOcclusionMode() const { return m_occlusionMode; } -void SpatialAudioSettings::SetOcclusionMode( AudOcclusion value ) { m_occlusionMode = value; } +bool SpatialAudioSettings::GetSpatialAudioGeometryEnabled() const { return m_spatialAudioGeometryEnabled; } +void SpatialAudioSettings::SetSpatialAudioGeometryEnabled( bool value ) { m_spatialAudioGeometryEnabled = value; } float SpatialAudioSettings::GetMovementThreshold() const { return m_movementThreshold; } void SpatialAudioSettings::SetMovementThreshold( float value ) { m_movementThreshold = value; } @@ -56,6 +57,9 @@ void SpatialAudioSettings::SetEnableDiffractionAndTransmission( bool value ) { m bool SpatialAudioSettings::GetCalcEmitterVirtualPosition() const { return m_calcEmitterVirtualPosition; } void SpatialAudioSettings::SetCalcEmitterVirtualPosition( bool value ) { m_calcEmitterVirtualPosition = value; } +float SpatialAudioSettings::GetTransmissionLoss() const { return m_transmissionLoss; } +void SpatialAudioSettings::SetTransmissionLoss( float value ) { m_transmissionLoss = std::max( 0.0f, std::min( 1.0f, value ) ); } + bool SpatialAudioSettings::GetEnableDiffraction() const { return m_enableDiffraction; } void SpatialAudioSettings::SetEnableDiffraction( bool value ) { m_enableDiffraction = value; } diff --git a/src/SpatialAudioSettings.h b/src/SpatialAudioSettings.h index 8d52600..ab0d367 100644 --- a/src/SpatialAudioSettings.h +++ b/src/SpatialAudioSettings.h @@ -9,12 +9,6 @@ struct AkSpatialAudioInitSettings; -enum class AudOcclusion : int -{ - Off = 0, // Occlusion disabled, no geometry registered, spatial audio geometry not initialized - On = 1 // Wwise Spatial Audio handles diffraction + transmission -}; - /** * @brief A wrapper with configuration settings for Wwise Spatial Audio initialization. * @@ -25,10 +19,10 @@ class SpatialAudioSettings SpatialAudioSettings(); /** - * @brief Controls whether spatial audio occlusion is On or Off. + * @brief Controls whether geometry based spatial audio processing is enabled. */ - AudOcclusion GetOcclusionMode() const; - void SetOcclusionMode( AudOcclusion value ); + bool GetSpatialAudioGeometryEnabled() const; + void SetSpatialAudioGeometryEnabled( bool value ); /** * @brief Amount that an emitter or listener has to move to trigger a validation of reflections/diffraction. @@ -174,6 +168,12 @@ class SpatialAudioSettings bool GetCalcEmitterVirtualPosition() const; void SetCalcEmitterVirtualPosition( bool value ); + /** + * @brief Transmission loss [0.0-1.0] applied to geometry surfaces when meshes are registered. + */ + float GetTransmissionLoss() const; + void SetTransmissionLoss( float value ); + /** * @brief Switch to enable or disable geometric diffraction for this Geometry. */ @@ -181,9 +181,8 @@ class SpatialAudioSettings void SetEnableDiffraction( bool value ); /** - * @brief Switch to enable or disable geometric diffraction on boundary edges for this Geometry. - * - * Boundary edges are edges that are connected to only one triangle. + * @brief Switch to enable or disable geometric diffraction on boundary edges for this mesh. + * Boundary edges are edges that are connected to only one triangle. */ bool GetEnableDiffractionOnBoundaryEdges() const; void SetEnableDiffractionOnBoundaryEdges( bool value ); @@ -194,7 +193,7 @@ class SpatialAudioSettings void PopulateInitSettings( AkSpatialAudioInitSettings& out ) const; private: - AudOcclusion m_occlusionMode; + bool m_spatialAudioGeometryEnabled; float m_movementThreshold; int m_numberOfPrimaryRays; int m_maxReflectionOrder; @@ -206,6 +205,7 @@ class SpatialAudioSettings int m_loadBalancingSpread; bool m_enableDiffractionAndTransmission; bool m_calcEmitterVirtualPosition; + float m_transmissionLoss; bool m_enableDiffraction; bool m_enableDiffractionOnBoundaryEdges; }; From 419ce415432fe7416a8400fff6e7537fb9555214 Mon Sep 17 00:00:00 2001 From: phevosccp Date: Tue, 30 Jun 2026 15:54:16 +0000 Subject: [PATCH 48/48] registering the spatial audio listener should be behind the corresponding flag --- src/AudListener.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/AudListener.cpp b/src/AudListener.cpp index 8102b68..bbe8697 100644 --- a/src/AudListener.cpp +++ b/src/AudListener.cpp @@ -3,6 +3,7 @@ #include "stdafx.h" #include "AudListener.h" +#include "AudManager.h" #include "Vector3.h" #include "Utilities.h" @@ -15,7 +16,10 @@ AudListener::AudListener( IRoot* lockobj ) : AudGameObjResource( LISTENER_GAME_O AudListener::~AudListener() { - AK::SpatialAudio::UnregisterListener( m_ID ); + if( g_audioManager != nullptr && g_audioManager->GetSpatialAudioGeometryEnabled() ) + { + AK::SpatialAudio::UnregisterListener( m_ID ); + } AK::SoundEngine::RemoveDefaultListener( m_ID ); AK::SoundEngine::UnregisterGameObj( m_ID ); } @@ -30,7 +34,10 @@ void AudListener::RegisterWwiseObject() AK::SoundEngine::AddDefaultListener(m_ID); // Register listener for occlusion/diffraction processing - AK::SpatialAudio::RegisterListener( m_ID ); + if( g_audioManager != nullptr && g_audioManager->GetSpatialAudioGeometryEnabled() ) + { + AK::SpatialAudio::RegisterListener( m_ID ); + } m_gameObjRegistered = true; }