Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,12 @@ ccp_add_library(CarbonAudio SHARED
src/AudGeometry.cpp
src/AudGeometry_Blue.cpp
src/AudObstructionOcclusion.cpp
src/AudPortal.cpp
src/AudPortal_Blue.cpp
src/AudRoom.cpp
src/AudRoom_Blue.cpp
src/AudRoomManager.cpp
src/AudUnitBox.cpp
src/SpatialAudioSettings.cpp
src/audio2.cpp
src/AudGameObjResource_Blue.cpp
Expand Down Expand Up @@ -89,8 +92,10 @@ target_sources(CarbonAudio PRIVATE
src/AudMusicPlayer.h
src/AudGeometry.h
src/AudObstructionOcclusion.h
src/AudPortal.h
src/AudRoom.h
src/AudRoomManager.h
src/AudUnitBox.h
src/SpatialAudioSettings.h
src/autoversion.h
src/DebugUtilities.h
Expand Down
109 changes: 109 additions & 0 deletions src/AudPortal.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
////////////////////////////////////////////////////////////
//
// Creator: Phevos Rinis
// Creation Date: Oct 2026
// Copyright (c) 2026 Fenris Creations
//

#include "stdafx.h"
#include "AudPortal.h"

#include "AudManager.h"
#include "AudRoomManager.h"

AudPortal::AudPortal( IRoot* lockobj ) :
m_portalID( AllocateGameObjectID() ),
m_open( true ),
m_adjacentRoomBleed( 1.0f ),
m_obstruction( 0.0f ),
m_occlusion( 0.0f ),
m_obstructionOcclusionTransition( false ),
m_enabled( true ),
m_shapeEnabled( true ),
m_warnedDegenerate( false ),
m_sentToWwise( false ),
m_registeredWithManager( false ),
m_frontRoomID( AudRoomManager::OUTDOOR_ROOM_ID ),
m_backRoomID( AudRoomManager::OUTDOOR_ROOM_ID ),
m_foundFrontRoomID( AudRoomManager::OUTDOOR_ROOM_ID ),
m_foundBackRoomID( AudRoomManager::OUTDOOR_ROOM_ID ),
m_foundTicks( 0 ),
m_obstructionOcclusionSent( false ),
m_sentObstruction( 0.0f ),
m_sentOcclusion( 0.0f ),
m_sentTransition( false ),
m_setPortalFailureLogged( false ),
m_obstructionOcclusionFailureLogged( false )
{
EnsureRegistered();
}

AudPortal::~AudPortal()
{
if( g_audioManager != nullptr && m_registeredWithManager )
{
g_audioManager->GetRoomManager().UnregisterPortal( this );
}
}

bool AudPortal::EnsureRegistered()
{
if( m_registeredWithManager )
{
return true;
}

if( g_audioManager == nullptr )
{
return false;
}

g_audioManager->GetRoomManager().RegisterPortal( this );
m_registeredWithManager = true;
return true;
}

void AudPortal::SetTransform( const Matrix& unitBoxToWorld )
{
if( EnsureRegistered() )
{
g_audioManager->GetRoomManager().SetPortalTransform( *this, unitBoxToWorld );
}
else
{
ApplyTransform( unitBoxToWorld );
}
}

void AudPortal::ApplyTransform( const Matrix& unitBoxToWorld )
{
if( !m_box.SetTransform( unitBoxToWorld ) && !m_warnedDegenerate )
{
CCP_LOGWARN( "Portal '%s' (%llu) has a box with a zero-length axis; it is not sent to Wwise until it is scaled.", m_name.c_str(), m_portalID );
m_warnedDegenerate = true;
}
}

void AudPortal::SetEnabled( bool enabled )
{
m_shapeEnabled = enabled;
EnsureRegistered();
}

void AudPortal::Remove()
{
if( EnsureRegistered() )
{
g_audioManager->GetRoomManager().RemovePortalShape( *this );
}
else
{
m_box.ClearTransform();
}
}

bool AudPortal::OnModified( Be::Var* value )
{
EnsureRegistered();
return true;
}
176 changes: 176 additions & 0 deletions src/AudPortal.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
////////////////////////////////////////////////////////////
//
// Creator: Phevos Rinis
// Creation Date: Oct 2026
// Copyright (c) 2026 Fenris Creations
//

#pragma once

#include <ITr2BoxVolume.h>

#include "AudUnitBox.h"

#include <AK/SoundEngine/Common/AkTypes.h>
#include <AK/SpatialAudio/Common/AkSpatialAudioTypes.h>

#include <string>

/**
* @brief A Wwise Spatial Audio portal, placed as a unit box through ITr2BoxVolume, with the front room on the box's local -Z side.
*/
BLUE_CLASS( AudPortal ) :
public ITr2BoxVolume,
public INotify
{
public:
AudPortal( IRoot* lockobj = NULL );
virtual ~AudPortal();

EXPOSE_TO_BLUE();

/**
* @brief Stores the portal's box. The portal is sent to Wwise on the next update.
*
* @param unitBoxToWorld Maps the unit box to world space.
*/
void SetTransform( const Matrix& unitBoxToWorld ) override;

/**
* @brief Called when the volume object is enabled or disabled.
*
* @param enabled Whether the volume object is enabled.
*/
void SetEnabled( bool enabled ) override;

/**
* @brief Clears the box and removes the portal from Wwise.
*/
void Remove() override;

/**
* @brief Called by Blue when an attribute changes.
*
* @param value The attribute that changed.
*
* @return Always true.
*/
bool OnModified( Be::Var* value ) override;

/**
* @brief Portal ID, from the same ID space as game objects.
*/
AkUInt64 GetPortalID() const { return m_portalID; }

/**
* @brief Name shown in the Wwise profiler.
*/
const std::string& GetName() const { return m_name; }

/**
* @brief Whether sound passes through the portal, e.g. an open door.
*/
bool IsOpen() const { return m_open; }

/**
* @brief Scales how much reverb of the room on the other side is heard through the portal.
*/
float GetAdjacentRoomBleed() const { return m_adjacentRoomBleed; }

/**
* @brief Obstruction of the direct sound through the portal.
*/
float GetObstruction() const { return m_obstruction; }

/**
* @brief Occlusion of all sound through the portal, e.g. a door that is partly closed.
*/
float GetOcclusion() const { return m_occlusion; }

/**
* @brief Whether obstruction and occlusion fade over the depth of the portal.
*/
bool GetObstructionOcclusionTransition() const { return m_obstructionOcclusionTransition; }

/**
* @brief True when the portal and its volume object are both enabled.
*/
bool IsEnabled() const { return m_enabled && m_shapeEnabled; }

/**
* @brief Box of the portal, set through ITr2BoxVolume.
*/
const AudUnitBox& GetBox() const { return m_box; }

/**
* @brief Whether the portal is in Wwise.
*/
bool IsRegistered() const { return m_sentToWwise; }

/**
* @brief Room on the front side, as sent to Wwise.
*/
AkUInt64 GetFrontRoomID() const { return m_frontRoomID; }

/**
* @brief Room on the back side, as sent to Wwise.
*/
AkUInt64 GetBackRoomID() const { return m_backRoomID; }

private:
friend class AudRoomManager;

/**
* @brief Registers with the room manager once it exists.
*
* @return True when the portal is registered.
*/
bool EnsureRegistered();

/**
* @brief Stores the box. Called by AudRoomManager under its lock.
*
* @param unitBoxToWorld Maps the unit box to world space.
*/
void ApplyTransform( const Matrix& unitBoxToWorld );

AkUInt64 m_portalID;

std::string m_name;
bool m_open;
float m_adjacentRoomBleed;
float m_obstruction;
float m_occlusion;
bool m_obstructionOcclusionTransition;
/// Enabled attribute of the portal.
bool m_enabled;
/// Set through ITr2BoxVolume::SetEnabled.
bool m_shapeEnabled;

AudUnitBox m_box;
bool m_warnedDegenerate;
bool m_sentToWwise;
bool m_registeredWithManager;

/// Last parameters sent to Wwise.
AkPortalParams m_sentPortalParams;
std::string m_sentName;
AkUInt64 m_frontRoomID;
AkUInt64 m_backRoomID;
/// Rooms found at the faces on the last update.
AkUInt64 m_foundFrontRoomID;
AkUInt64 m_foundBackRoomID;
/// Updates in a row the same rooms were found.
int m_foundTicks;
/// Obstruction and occlusion last sent to Wwise.
bool m_obstructionOcclusionSent;
float m_sentObstruction;
float m_sentOcclusion;
bool m_sentTransition;
/// Set after a failed SetPortal is logged, so it is not logged again every update.
bool m_setPortalFailureLogged;
/// Set after a failed SetPortalObstructionAndOcclusion is logged, so it is not logged again every update.
bool m_obstructionOcclusionFailureLogged;
};

TYPEDEF_BLUECLASS( AudPortal );
32 changes: 32 additions & 0 deletions src/AudPortal_Blue.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
////////////////////////////////////////////////////////////
//
// Creator: Phevos Rinis
// Creation Date: Oct 2026
// Copyright (c) 2026 Fenris Creations
//

#include "stdafx.h"
#include "AudPortal.h"

BLUE_DEFINE( AudPortal );

const Be::ClassInfo* AudPortal::ExposeToBlue()
{
EXPOSURE_BEGIN( AudPortal, "A Wwise Spatial Audio portal, placed as a unit box through ITr2BoxVolume" )
MAP_INTERFACE( ITr2BoxVolume )
MAP_INTERFACE( INotify )

MAP_ATTRIBUTE( "name", m_name, "Name of the portal, shown in the Wwise profiler.", Be::READWRITE | Be::PERSIST | Be::NOTIFY )
MAP_ATTRIBUTE( "open", m_open, "Whether sound passes through the portal, e.g. an open door.\n:jessica-group: Acoustics", Be::READWRITE | Be::PERSIST | Be::NOTIFY )
MAP_ATTRIBUTE( "adjacentRoomBleed", m_adjacentRoomBleed, "Scales how much reverb of the room on the other side is heard through the portal.\n:jessica-group: Acoustics", Be::READWRITE | Be::PERSIST | Be::NOTIFY )
MAP_ATTRIBUTE( "obstruction", m_obstruction, "Obstruction [0, 1] of the direct sound through the portal.\n:jessica-group: Acoustics", Be::READWRITE | Be::PERSIST | Be::NOTIFY )
MAP_ATTRIBUTE( "occlusion", m_occlusion, "Occlusion [0, 1] of all sound through the portal, e.g. a door that is partly closed.\n:jessica-group: Acoustics", Be::READWRITE | Be::PERSIST | Be::NOTIFY )
MAP_ATTRIBUTE( "obstructionOcclusionTransition", m_obstructionOcclusionTransition, "Fade obstruction and occlusion over the depth of the portal.\n:jessica-group: Acoustics", Be::READWRITE | Be::PERSIST | Be::NOTIFY )
MAP_ATTRIBUTE( "enabled", m_enabled, "Whether the portal is active. It is only sent to Wwise when its volume object is enabled too.", Be::READWRITE | Be::PERSIST | Be::NOTIFY )

MAP_ATTRIBUTE( "portalID", m_portalID, "Wwise portal identifier.\n:jessica-hidden: True", Be::READ )
MAP_ATTRIBUTE( "isRegistered", m_sentToWwise, "Whether the portal is in Wwise.\n:jessica-hidden: True", Be::READ )
MAP_ATTRIBUTE( "frontRoomID", m_frontRoomID, "Room on the front side of the portal in Wwise.\n:jessica-hidden: True", Be::READ )
MAP_ATTRIBUTE( "backRoomID", m_backRoomID, "Room on the back side of the portal in Wwise.\n:jessica-hidden: True", Be::READ )
EXPOSURE_END()
}
Loading