Skip to content
Open
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
133 changes: 82 additions & 51 deletions src/AudEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,21 @@
#include "DebugUtilities.h"
#include "Vector3.h"

namespace
{
// Debug render toggle names
constexpr const char* AUDIO_ATTENUATION_SPHERE_DEBUG_OPTION = "Audio Attenuation Sphere";
constexpr const char* AUDIO_EMITTER_DIRECTION_DEBUG_OPTION = "Audio Emitter Direction";
}

AudEmitter::AudEmitter( IRoot* lockobj ) :
AudGameObjResource( lockobj ),
m_normalizeAttenuationScaling( false ),
m_minNormalizedValue( 30.f ), // This default comes from the perspective that normalizing is most commonly used for bounding sphere radii of ships
m_maxNormalizedValue( 9000.f), // ^
m_minNormalizedScalingFactor( 0.4f ),
m_maxNormalizedScalingFactor( 3.5f ),
m_debugColor(0, 0, 0, 0),
m_debugColor( DebugUtilities::GenerateDebugColor( 0.4f, 0.9f ) ),
m_simulationColor(0xff00ff00), // Green
m_visualizationRadius(0.f),
m_listenerDistanceScaleFactor(0.003f),
Expand All @@ -31,7 +38,7 @@ AudEmitter::AudEmitter( AkGameObjectID gameObjID, IRoot* lockobj ) :
m_maxNormalizedValue( 9000.f), // ^
m_minNormalizedScalingFactor( 0.4f ),
m_maxNormalizedScalingFactor( 3.5f ),
m_debugColor(0, 0, 0, 0),
m_debugColor( DebugUtilities::GenerateDebugColor( 0.4f, 0.9f ) ),
m_simulationColor(0xff00ff00), // Green
m_visualizationRadius(0.f),
m_listenerDistanceScaleFactor(0.003f),
Expand Down Expand Up @@ -68,7 +75,7 @@ void AudEmitter::SetPrefix( const std::wstring& prefix )
int AudEmitter::SetPosition( const Vector3& front, const Vector3& top, const Vector3& pos )
{
m_hasReceivedPosition = true;
return SetPositionHelper( front, top, pos );
return SetPlacementFromParent( front, top, pos );
}

unsigned int AudEmitter::SendEvent( const std::wstring& name, bool bypassPrefix )
Expand Down Expand Up @@ -115,67 +122,91 @@ void AudEmitter::SetVisibility( bool isVisible )
m_isVisible = isVisible;
}

// Debug
void AudEmitter::GetDebugOptions( Tr2DebugRendererOptions& options )
{
options.insert( "Audio Attenuation Sphere" );
options.insert( AUDIO_ATTENUATION_SPHERE_DEBUG_OPTION );
options.insert( AUDIO_EMITTER_DIRECTION_DEBUG_OPTION );
}

void AudEmitter::RenderDebugInfo( ITr2DebugRenderer2& renderer )
{
if ( g_audioManager != nullptr && g_audioManager->GetState() == AudioState::Enabled )
if ( m_culled || g_audioManager == nullptr || g_audioManager->GetState() != AudioState::Enabled )
{
return;
}

RenderDebugBoundingSphere( renderer );
RenderDebugDirection( renderer );
RenderDebugName( renderer );
}

void AudEmitter::RenderDebugBoundingSphere( ITr2DebugRenderer2& renderer )
{
if ( !g_debugDisplayAllEmitters && !renderer.HasOption( GetRawRoot(), AUDIO_ATTENUATION_SPHERE_DEBUG_OPTION ) )

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This double negation isn't very easy to read. Maybe simplify it into something like:

Suggested change
if ( !g_debugDisplayAllEmitters && !renderer.HasOption( GetRawRoot(), AUDIO_ATTENUATION_SPHERE_DEBUG_OPTION ) )
if ( !( g_debugDisplayAllEmitters ||
renderer.HasOption( GetRawRoot(), AUDIO_ATTENUATION_SPHERE_DEBUG_OPTION ) ) )
return;

{
if ( !g_debugDisplayAllEmitters )
{
if ( !renderer.HasOption( GetRawRoot(), "Audio Attenuation Sphere" ) )
{
return;
}
}

uint32_t debugSphereSegments = static_cast<uint32_t>(8.f + m_visualizationRadius / 5000.f);
debugSphereSegments = (debugSphereSegments < 25) ? debugSphereSegments : 25;

if( m_visualizationRadius > 0.f )
{
float scaledRadius = m_visualizationRadius * m_scalingFactor;
renderer.DrawSphere( this, m_position, scaledRadius, debugSphereSegments, ITr2DebugRenderer2::Wireframe, Tr2DebugColor( m_simulationColor ) );
}

if ( !m_culled )
{
if ( Dot(m_debugColor, m_debugColor) == 0 )
{
float minRange = 0.4f;
float maxRange = 0.9f;
m_debugColor = DebugUtilities::GenerateDebugColor( minRange, maxRange );
}

const float emitterRange = AK::SoundEngine::Query::GetMaxRadius( m_ID );
renderer.DrawSphere( this, m_position, emitterRange, debugSphereSegments, ITr2DebugRenderer2::Wireframe, Tr2DebugColor( m_debugColor ) );

DrawClickableRadius(renderer);

std::string debugName = m_name + "(" + std::to_string(m_ID) + ")";
renderer.DrawText(TRI_DBG_FONT_SMALL, m_position, m_debugColor, debugName.c_str());
}
return;
}

constexpr float BASE_SEGMENTS = 8.f;
constexpr float RADIUS_PER_SEGMENT = 5000.f;
constexpr uint32_t MAX_SEGMENTS = 25;
uint32_t debugSphereSegments = static_cast<uint32_t>( BASE_SEGMENTS + m_visualizationRadius / RADIUS_PER_SEGMENT );
debugSphereSegments = std::min( debugSphereSegments, MAX_SEGMENTS );

if( m_visualizationRadius > 0.f )
{
float scaledRadius = m_visualizationRadius * m_scalingFactor;
renderer.DrawSphere( this, m_position, scaledRadius, debugSphereSegments, ITr2DebugRenderer2::Wireframe, Tr2DebugColor( m_simulationColor ) );
}

float emitterRange = std::max( m_visualizationRadius, AK::SoundEngine::Query::GetMaxRadius( m_ID ) );
renderer.DrawSphere( this, m_position, emitterRange, debugSphereSegments, ITr2DebugRenderer2::Wireframe, Tr2DebugColor( m_debugColor ) );
}

void AudEmitter::DrawClickableRadius(ITr2DebugRenderer2& renderer)
void AudEmitter::RenderDebugDirection( ITr2DebugRenderer2& renderer )
{
// In order to scale the clickable radius we need to know the distance to the camera. This is not so
// easy to get from our library so we use the listener position because the listener follows the camera.
AudListenerPtr listener = g_audioManager->GetListener();
Vector3 listenerPos = listener->GetPosition();
float distanceToListener = Length( m_position - listenerPos );
float scaleFactor = distanceToListener * m_listenerDistanceScaleFactor;
if ( !renderer.HasOption( GetRawRoot(), AUDIO_EMITTER_DIRECTION_DEBUG_OPTION ) )
{
return;
}

float textWidth = m_name.length() * m_debugFontCharWidth * scaleFactor;
float radius = textWidth * m_radiusToTextWidthRatio;
constexpr float ARROW_LENGTH_PER_DISTANCE = 0.025f;
constexpr float ARROW_MIN_LENGTH = 25.0f;
constexpr float ARROW_MAX_LENGTH = 250.0f;
constexpr float ARROW_RADIUS_PER_LENGTH = 0.035f;
constexpr float ARROW_MIN_RADIUS = 1.5f;
constexpr float ARROW_POINTER_LENGTH = 0.22f;
constexpr uint32_t ARROW_SEGMENTS = 12;

AudListenerPtr listener = g_audioManager->GetListener();
const float distanceToListener = Length( m_position - listener->GetPosition() );
const float arrowLength = std::min( std::max( distanceToListener * ARROW_LENGTH_PER_DISTANCE, ARROW_MIN_LENGTH ), ARROW_MAX_LENGTH );
const float arrowRadius = std::max( arrowLength * ARROW_RADIUS_PER_LENGTH, ARROW_MIN_RADIUS );
Vector3 direction = Normalize( m_effectiveOrientation.front );

renderer.DrawArrow(
this,
m_position,
m_position + direction * arrowLength,
arrowRadius,
ARROW_POINTER_LENGTH,
ARROW_SEGMENTS,
ITr2DebugRenderer2::Solid,
Tr2DebugColor( m_debugColor )
);
}

void AudEmitter::RenderDebugName( ITr2DebugRenderer2& renderer )
{
if ( !g_debugDisplayAllEmitters &&
!renderer.HasOption( GetRawRoot(), AUDIO_ATTENUATION_SPHERE_DEBUG_OPTION ) &&
!renderer.HasOption( GetRawRoot(), AUDIO_EMITTER_DIRECTION_DEBUG_OPTION ) )
{
return;
}

// Draw barely visible transparent sphere
renderer.DrawSphere( this, m_position, radius, 8, ITr2DebugRenderer2::Solid, Tr2DebugColor( 0x08000000 ) );
std::string debugName = m_name + "(" + std::to_string(m_ID) + ")";
renderer.DrawText(TRI_DBG_FONT_SMALL, m_position, m_debugColor, debugName.c_str());
}

void AudEmitter::Mute()
Expand Down
7 changes: 3 additions & 4 deletions src/AudEmitter.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,9 @@ BLUE_CLASS( AudEmitter ) :
virtual void RenderDebugInfo( ITr2DebugRenderer2& renderer ) override;
protected:
AudEmitter( AkGameObjectID gameObjID, IRoot* lockobj = NULL );
/**
* @brief Draw a transparent sphere that makes this clickable in our editor. Scales based on distance to camera.
*/
void DrawClickableRadius(ITr2DebugRenderer2& renderer);
void RenderDebugBoundingSphere( ITr2DebugRenderer2& renderer );
void RenderDebugDirection( ITr2DebugRenderer2& renderer );
void RenderDebugName( ITr2DebugRenderer2& renderer );
// Properties used for normalizing attenuation scaling for this audio emitter.
bool m_normalizeAttenuationScaling;
float m_minNormalizedValue;
Expand Down
7 changes: 5 additions & 2 deletions src/AudEmitter_Blue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ const Be::ClassInfo* AudEmitter::ExposeToBlue()
MAP_INTERFACE( ITr2AudEmitter )
MAP_INTERFACE( ITr2DebugRenderable )

MAP_ATTRIBUTE( "rotation", m_authoredRotation, "Authored local audio rotation used by placement providers.\n:jessica-group: Audio Placement", Be::READWRITE | Be::PERSIST | Be::NOTIFY )
MAP_PROPERTY_READONLY( "front", GetFront, "Effective front vector sent to Wwise.\n:jessica-hidden: True" )
MAP_PROPERTY_READONLY( "top", GetTop, "Effective top vector sent to Wwise.\n:jessica-hidden: True" )
MAP_ATTRIBUTE( "normalizeAttenuationScaling", m_normalizeAttenuationScaling, "Determines whether attenuation scaling for this audio emitter should be normalized\n:jessica-group: Attenuation Normalization", Be::READWRITE | Be::PERSIST )
MAP_ATTRIBUTE( "minNormalizedValue", m_minNormalizedValue, "The minimum number to use for attenuation normalization.\n:jessica-group: Attenuation Normalization", Be::READWRITE | Be::PERSIST )
MAP_ATTRIBUTE( "maxNormalizedValue", m_maxNormalizedValue, "The maximum number to use for attenuation normalization.\n:jessica-group: Attenuation Normalization", Be::READWRITE | Be::PERSIST )
Expand Down Expand Up @@ -49,12 +52,12 @@ const Be::ClassInfo* AudEmitter::ExposeToBlue()
)
MAP_METHOD_AND_WRAP
(
"SetPosition",
"SetPlacement",
SetPosition,
"Update orientation and position of an audio emitter.\n"
":param front: Vector3 representing the orientation of the object.\n"
":param top: Vector3 representing the up vector of the object\n"
":param position: Vector3 representing the world position .\n"
)
EXPOSURE_CHAINTO( AudGameObjResource )
}
}
101 changes: 84 additions & 17 deletions src/AudGameObjResource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,19 @@ static AkGameObjectID GenerateEntityID()
return s_currentID++;
}

AudGameObjResource::Orientation::Orientation( const Vector3& front_, const Vector3& top_ ) :
front( front_ ),
top( top_ )
{
}

AudGameObjResource::AudGameObjResource( IRoot* lockobj ) : PARENTLOCK( m_parameters ),
m_eventPrefix(L""),
m_scalingFactor( 1.0 ),
m_position( WWISE_INIT_POSITION ),
m_parentOrientation( Vector3( 0, 0, 1 ), Vector3( 0, 1, 0 ) ),
m_effectiveOrientation( Vector3( 0, 0, 1 ), Vector3( 0, 1, 0 ) ),
m_authoredRotation( 0.0f, 0.0f, 0.0f, 1.0f ),
m_mutex( "AudGameObjResource", "m_mutex" ),
m_gameObjRegistered( false ),
m_culled( true ),
Expand Down Expand Up @@ -58,6 +67,9 @@ AudGameObjResource::AudGameObjResource( AkGameObjectID gameObjID, IRoot* lockobj
m_eventPrefix(L""),
m_scalingFactor( 1.0 ),
m_position( WWISE_INIT_POSITION ),
m_parentOrientation( Vector3( 0, 0, 1 ), Vector3( 0, 1, 0 ) ),
m_effectiveOrientation( Vector3( 0, 0, 1 ), Vector3( 0, 1, 0 ) ),
m_authoredRotation( 0.0f, 0.0f, 0.0f, 1.0f ),
m_mutex( "AudGameObjResource", "m_mutex" ),
m_gameObjRegistered( false ),
m_culled( true ),
Expand All @@ -72,6 +84,7 @@ AudGameObjResource::AudGameObjResource( AkGameObjectID gameObjID, IRoot* lockobj
m_additionalCullingWeight( 0.0f ),
m_cumulativeWeight( 0.0f ),
m_maxAttenuationRadiusSq( 0.0f ),
m_hasReceivedPosition(false),
m_waitingOneShotInRange( std::pair( std::chrono::steady_clock::now(), L"" ) ),
m_eventName(L"")
{
Expand Down Expand Up @@ -364,34 +377,72 @@ bool AudGameObjResource::SetAttenuationScalingFactor( float value )
return false;
}

int AudGameObjResource::SetPositionHelper( const Vector3& front, const Vector3& top, const Vector3& position )
int AudGameObjResource::SetPlacementFromParent( const Vector3& front, const Vector3& top, const Vector3& position )
{
m_parentOrientation = Orientation( front, top );
const Orientation effectiveOrientation = GetEffectiveOrientation();
return ApplyEffectivePlacement( effectiveOrientation.front, effectiveOrientation.top, position );
}

AudGameObjResource::Orientation AudGameObjResource::Orthonormalize( const Vector3& front, const Vector3& top )
{
const Vector3 correctFront = Normalize( front );
const Vector3 correctUp = Normalize( Cross( Cross( correctFront, Normalize( top ) ), correctFront ) );
return Orientation( correctFront, correctUp );
}

int AudGameObjResource::ApplyEffectivePlacement( const Vector3& front, const Vector3& top, const Vector3& position )
{
const Orientation corrected = Orthonormalize( front, top );

m_position = position;
m_effectiveOrientation = corrected;

if( g_audioManager != nullptr && g_audioManager->GetState() == AudioState::Enabled )
if( g_audioManager != nullptr && g_audioManager->GetState() == AudioState::Enabled && m_gameObjRegistered )
{
if( m_gameObjRegistered )
{
AkSoundPosition tmp;
Vector3 correctFront = Normalize( front );
Vector3 correctUp = Normalize( top );
correctUp = Normalize( Cross( Cross( correctFront, correctUp ), correctFront ) );
tmp.Set( MakeAkVector(position), MakeAkVector(correctFront), MakeAkVector(correctUp) );
AkSoundPosition tmp;
tmp.Set( MakeAkVector( position ), MakeAkVector( corrected.front ), MakeAkVector( corrected.top ) );

// all vectors come in RH, but WWISE is LH, so convert
AkSoundPosition soundPosLH;
RH2LH::convertEmitter( &soundPosLH, &tmp);
// all vectors come in RH, but WWISE is LH, so convert
AkSoundPosition soundPosLH;
RH2LH::convertEmitter( &soundPosLH, &tmp );

AKRESULT result = AK::SoundEngine::SetPosition( m_ID, soundPosLH );
}
AK::SoundEngine::SetPosition( m_ID, soundPosLH );
}
return AK_Success;
}

bool AudGameObjResource::HasAuthoredRotation() const
{
return m_authoredRotation != IdentityQuaternion();
}

AudGameObjResource::Orientation AudGameObjResource::GetEffectiveOrientation() const
{
// XMVector3Rotate requires a unit quaternion; ignore identity and degenerate
// (e.g. zero-length) authored rotations so placement never becomes NaN.
if ( !HasAuthoredRotation() || LengthSq( m_authoredRotation ) <= 0.0f )
{
return m_parentOrientation;
}

const Quaternion rotation = Normalize( m_authoredRotation );
return Orientation(
Vector3( XMVector3Rotate( m_parentOrientation.front, rotation ) ),
Vector3( XMVector3Rotate( m_parentOrientation.top, rotation ) )
);
}

void AudGameObjResource::RefreshPlacementFromRotation()
{
const Orientation effectiveOrientation = GetEffectiveOrientation();
ApplyEffectivePlacement( effectiveOrientation.front, effectiveOrientation.top, m_position );
}

bool AudGameObjResource::Initialize()
{
RegisterWwiseObject();
SetPositionHelper( Vector3( 1,0,0 ), Vector3( 0,1,0 ), m_position );
SetPlacementFromParent( Vector3( 0, 0, 1 ), Vector3( 0, 1, 0 ), m_position );

if ( !m_eventName.empty() )
{
Expand All @@ -418,6 +469,12 @@ void AudGameObjResource::OnListModified( long event, ssize_t key, ssize_t key2,

bool AudGameObjResource::OnModified( Be::Var* value )
{
if ( ( Be::Var* )&m_authoredRotation == value )
{
RefreshPlacementFromRotation();
return true;
}

if ( IsMatch( value, m_eventName ) )
{
StopAll();
Expand Down Expand Up @@ -581,8 +638,8 @@ void AudGameObjResource::Wake()
return;
}

RegisterWwiseObject();
SetPositionHelper( Vector3( 1,0,0 ), Vector3( 0,1,0 ), m_position );
RegisterWwiseObject();
ApplyEffectivePlacement( m_effectiveOrientation.front, m_effectiveOrientation.top, m_position );
m_culled = false;
if ( m_waitingOneShotInRange.second != L"" && m_listenerInRange )
{
Expand Down Expand Up @@ -1002,6 +1059,16 @@ Vector3 AudGameObjResource::GetPosition() const
return m_position;
}

Vector3 AudGameObjResource::GetFront() const
{
return m_effectiveOrientation.front;
}

Vector3 AudGameObjResource::GetTop() const
{
return m_effectiveOrientation.top;
}

std::wstring AudGameObjResource::GetEventName()
{
return m_eventName;
Expand Down
Loading