1521 lines
50 KiB
C++
1521 lines
50 KiB
C++
/*******************************************************************************
|
|
The content of this file includes portions of the proprietary AUDIOKINETIC Wwise
|
|
Technology released in source code form as part of the game integration package.
|
|
The content of this file may not be used without valid licenses to the
|
|
AUDIOKINETIC Wwise Technology.
|
|
Note that the use of the game engine is subject to the Unreal(R) Engine End User
|
|
License Agreement at https://www.unrealengine.com/en-US/eula/unreal
|
|
|
|
License Usage
|
|
|
|
Licensees holding valid licenses to the AUDIOKINETIC Wwise Technology may use
|
|
this file in accordance with the end user license agreement provided with the
|
|
software or, alternatively, in accordance with the terms contained
|
|
in a written agreement between you and Audiokinetic Inc.
|
|
Copyright (c) 2023 Audiokinetic Inc.
|
|
*******************************************************************************/
|
|
|
|
#include "AkSettings.h"
|
|
|
|
#include "AkAcousticTexture.h"
|
|
#include "AkAuxBus.h"
|
|
#include "AkAudioDevice.h"
|
|
#include "AkAudioEvent.h"
|
|
#include "AkAudioModule.h"
|
|
#include "AkSettingsPerUser.h"
|
|
#include "AkUnrealHelper.h"
|
|
#include "AssetRegistry/AssetRegistryModule.h"
|
|
#include "AssetRegistry/AssetData.h"
|
|
#include "Framework/Docking/TabManager.h"
|
|
#include "Framework/Notifications/NotificationManager.h"
|
|
#include "StringMatchAlgos/Array2D.h"
|
|
#include "StringMatchAlgos/StringMatching.h"
|
|
#include "UObject/UnrealType.h"
|
|
#include "Widgets/Notifications/SNotificationList.h"
|
|
|
|
#if WITH_EDITOR
|
|
#include "AkAudioStyle.h"
|
|
#include "AssetTools/Public/AssetToolsModule.h"
|
|
#if UE_5_0_OR_LATER
|
|
#include "HAL/PlatformFileManager.h"
|
|
#else
|
|
#include "HAL/PlatformFilemanager.h"
|
|
#endif
|
|
#include "Misc/ConfigCacheIni.h"
|
|
#include "Misc/FileHelper.h"
|
|
#include "Misc/MessageDialog.h"
|
|
#include "Platforms/AkUEPlatform.h"
|
|
#include "Settings/ProjectPackagingSettings.h"
|
|
#include "SettingsEditor/Public/ISettingsEditorModule.h"
|
|
#include "ISourceControlModule.h"
|
|
#include "SourceControlHelpers.h"
|
|
#include "AkUnrealEditorHelper.h"
|
|
|
|
#if AK_SUPPORT_WAAPI
|
|
#include "AkWaapiClient.h"
|
|
#include "AkWaapiUtils.h"
|
|
#include "Async/Async.h"
|
|
|
|
bool WAAPIGetTextureParams(FGuid textureID, FAkAcousticTextureParams& params)
|
|
{
|
|
auto waapiClient = FAkWaapiClient::Get();
|
|
if (waapiClient != nullptr)
|
|
{
|
|
/* Construct the relevant WAAPI json fields. */
|
|
TArray<TSharedPtr<FJsonValue>> fromID;
|
|
fromID.Add(MakeShareable(new FJsonValueString(textureID.ToString(EGuidFormats::DigitsWithHyphensInBraces))));
|
|
|
|
TSharedRef<FJsonObject> getArgsJson = FAkWaapiClient::CreateWAAPIGetArgumentJson(FAkWaapiClient::WAAPIGetFromOption::ID, fromID);
|
|
|
|
TSharedRef<FJsonObject> options = MakeShareable(new FJsonObject());
|
|
TArray<TSharedPtr<FJsonValue>> StructJsonArray;
|
|
StructJsonArray.Add(MakeShareable(new FJsonValueString("id")));
|
|
TArray<FString> absorptionStrings{ "@AbsorptionLow", "@AbsorptionMidLow", "@AbsorptionMidHigh", "@AbsorptionHigh" };
|
|
for (int i = 0; i < absorptionStrings.Num(); ++i)
|
|
StructJsonArray.Add(MakeShareable(new FJsonValueString(absorptionStrings[i])));
|
|
|
|
options->SetArrayField(FAkWaapiClient::WAAPIStrings::RETURN, StructJsonArray);
|
|
|
|
TSharedPtr<FJsonObject> outJsonResult;
|
|
if (waapiClient->Call(ak::wwise::core::object::get, getArgsJson, options, outJsonResult, 500, false))
|
|
{
|
|
/* Get absorption values from WAAPI return json. */
|
|
TArray<TSharedPtr<FJsonValue>> returnJson = outJsonResult->GetArrayField(FAkWaapiClient::WAAPIStrings::RETURN);
|
|
if (returnJson.Num() > 0)
|
|
{
|
|
auto jsonObj = returnJson[0]->AsObject();
|
|
if (jsonObj != nullptr)
|
|
{
|
|
TSharedPtr<FJsonObject> absorptionObject = nullptr;
|
|
for (int i = 0; i < absorptionStrings.Num(); ++i)
|
|
{
|
|
params.AbsorptionValues[i] = (float)(jsonObj->GetNumberField(absorptionStrings[i])) / 100.0f;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool WAAPIGetObjectColorIndex(FGuid textureID, int& index)
|
|
{
|
|
auto waapiClient = FAkWaapiClient::Get();
|
|
if (waapiClient != nullptr)
|
|
{
|
|
/* Construct the relevant WAAPI json fields. */
|
|
TArray<TSharedPtr<FJsonValue>> fromID;
|
|
fromID.Add(MakeShareable(new FJsonValueString(textureID.ToString(EGuidFormats::DigitsWithHyphensInBraces))));
|
|
TSharedRef<FJsonObject> getArgsJson = FAkWaapiClient::CreateWAAPIGetArgumentJson(FAkWaapiClient::WAAPIGetFromOption::ID, fromID);
|
|
|
|
TSharedRef<FJsonObject> options = MakeShareable(new FJsonObject());
|
|
TArray<TSharedPtr<FJsonValue>> StructJsonArray;
|
|
StructJsonArray.Add(MakeShareable(new FJsonValueString("id")));
|
|
StructJsonArray.Add(MakeShareable(new FJsonValueString("@Color")));
|
|
|
|
options->SetArrayField(FAkWaapiClient::WAAPIStrings::RETURN, StructJsonArray);
|
|
|
|
TSharedPtr<FJsonObject> outJsonResult;
|
|
if (waapiClient->Call(ak::wwise::core::object::get, getArgsJson, options, outJsonResult, 500, false))
|
|
{
|
|
/* Get absorption values from WAAPI return json. */
|
|
TArray<TSharedPtr<FJsonValue>> returnJson = outJsonResult->GetArrayField(FAkWaapiClient::WAAPIStrings::RETURN);
|
|
if (returnJson.Num() > 0)
|
|
{
|
|
auto jsonObj = returnJson[0]->AsObject();
|
|
if (jsonObj != nullptr)
|
|
{
|
|
index = (int)(jsonObj->GetNumberField("@Color"));
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool WAAPIGetObjectOverrideColor(FGuid textureID)
|
|
{
|
|
auto waapiClient = FAkWaapiClient::Get();
|
|
if (waapiClient != nullptr)
|
|
{
|
|
/* Construct the relevant WAAPI json fields. */
|
|
TArray<TSharedPtr<FJsonValue>> fromID;
|
|
fromID.Add(MakeShareable(new FJsonValueString(textureID.ToString(EGuidFormats::DigitsWithHyphensInBraces))));
|
|
TSharedRef<FJsonObject> getArgsJson = FAkWaapiClient::CreateWAAPIGetArgumentJson(FAkWaapiClient::WAAPIGetFromOption::ID, fromID);
|
|
|
|
TSharedRef<FJsonObject> options = MakeShareable(new FJsonObject());
|
|
TArray<TSharedPtr<FJsonValue>> StructJsonArray;
|
|
StructJsonArray.Add(MakeShareable(new FJsonValueString("id")));
|
|
StructJsonArray.Add(MakeShareable(new FJsonValueString("@OverrideColor")));
|
|
|
|
options->SetArrayField(FAkWaapiClient::WAAPIStrings::RETURN, StructJsonArray);
|
|
|
|
TSharedPtr<FJsonObject> outJsonResult;
|
|
if (waapiClient->Call(ak::wwise::core::object::get, getArgsJson, options, outJsonResult, 500, false))
|
|
{
|
|
/* Get absorption values from WAAPI return json. */
|
|
TArray<TSharedPtr<FJsonValue>> returnJson = outJsonResult->GetArrayField(FAkWaapiClient::WAAPIStrings::RETURN);
|
|
if (returnJson.Num() > 0)
|
|
{
|
|
auto jsonObj = returnJson[0]->AsObject();
|
|
if (jsonObj != nullptr)
|
|
{
|
|
return jsonObj->GetBoolField("@OverrideColor");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
#endif // AK_SUPPORT_WAAPI
|
|
#endif // WITH_EDITOR
|
|
|
|
#define LOCTEXT_NAMESPACE "AkSettings"
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// UAkSettings
|
|
|
|
namespace AkSettings_Helper
|
|
{
|
|
#if WITH_EDITOR
|
|
void MigrateMultiCoreRendering(bool EnableMultiCoreRendering, const FString& PlatformName)
|
|
{
|
|
FString SettingsClassName = FString::Format(TEXT("Ak{0}InitializationSettings"), { *PlatformName });
|
|
#if UE_5_1_OR_LATER
|
|
auto* SettingsClass = UClass::TryFindTypeSlow<UClass>(*SettingsClassName);
|
|
#else
|
|
auto* SettingsClass = FindObject<UClass>(ANY_PACKAGE, *SettingsClassName);
|
|
#endif
|
|
if (!SettingsClass)
|
|
{
|
|
return;
|
|
}
|
|
|
|
auto* MigrationFunction = SettingsClass->FindFunctionByName(TEXT("MigrateMultiCoreRendering"));
|
|
auto* Settings = SettingsClass->GetDefaultObject();
|
|
if (!MigrationFunction || !Settings)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Settings->ProcessEvent(MigrationFunction, &EnableMultiCoreRendering);
|
|
|
|
AkUnrealEditorHelper::SaveConfigFile(Settings);
|
|
}
|
|
#endif
|
|
|
|
void MatchAcousticTextureNamesToPhysMaterialNames(
|
|
const TArray<FAssetData>& PhysicalMaterials,
|
|
const TArray<FAssetData>& AcousticTextures,
|
|
TArray<int32>& assignments)
|
|
{
|
|
uint32 NumPhysMat = (uint32)PhysicalMaterials.Num();
|
|
uint32 NumAcousticTex = (uint32)AcousticTextures.Num();
|
|
|
|
// Create a scores matrix
|
|
Array2D<float> scores(NumPhysMat, NumAcousticTex, 0);
|
|
|
|
for (uint32 i = 0; i < NumPhysMat; ++i)
|
|
{
|
|
TArray<bool> perfectObjectMatches;
|
|
perfectObjectMatches.Init(false, NumAcousticTex);
|
|
|
|
if (PhysicalMaterials[i].GetAsset())
|
|
{
|
|
FString physMaterialName = PhysicalMaterials[i].GetAsset()->GetName();
|
|
|
|
if (physMaterialName.Len() == 0)
|
|
continue;
|
|
|
|
for (uint32 j = 0; j < NumAcousticTex; ++j)
|
|
{
|
|
// Skip objects for which we already found a perfect match
|
|
if (perfectObjectMatches[j] == true)
|
|
continue;
|
|
|
|
if (AcousticTextures[j].GetAsset())
|
|
{
|
|
FString acousticTextureName = AcousticTextures[j].GetAsset()->GetName();
|
|
|
|
if (acousticTextureName.Len() == 0)
|
|
continue;
|
|
|
|
// Calculate longest common substring length
|
|
float lcs = LCS::GetLCSScore(
|
|
physMaterialName.ToLower(),
|
|
acousticTextureName.ToLower());
|
|
|
|
scores(i, j) = lcs;
|
|
|
|
if (FMath::IsNearlyEqual(lcs, 1.f))
|
|
{
|
|
assignments[i] = j;
|
|
perfectObjectMatches[j] = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (uint32 i = 0; i < NumPhysMat; ++i)
|
|
{
|
|
if (assignments[i] == -1)
|
|
{
|
|
float bestScore = 0.f;
|
|
int32 matchedIdx = -1;
|
|
for (uint32 j = 0; j < NumAcousticTex; ++j)
|
|
{
|
|
if (scores(i, j) > bestScore)
|
|
{
|
|
bestScore = scores(i, j);
|
|
matchedIdx = j;
|
|
}
|
|
}
|
|
if (bestScore >= 0.2f)
|
|
assignments[i] = matchedIdx;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
FString UAkSettings::DefaultSoundDataFolder = TEXT("WwiseAudio");
|
|
|
|
#if WITH_EDITOR
|
|
float UAkSettings::MinimumDecayKeyDistance = 0.01f;
|
|
#endif
|
|
|
|
UAkSettings::UAkSettings(const FObjectInitializer& ObjectInitializer)
|
|
: Super(ObjectInitializer)
|
|
{
|
|
WwiseSoundDataFolder.Path = DefaultSoundDataFolder;
|
|
|
|
GlobalDecayAbsorption = 0.5f;
|
|
|
|
#if WITH_EDITOR
|
|
AssetRegistryModule = &FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
|
|
|
|
//register to asset modification delegates
|
|
auto& AssetRegistry = AssetRegistryModule->Get();
|
|
AssetRegistry.OnAssetAdded().AddUObject(this, &UAkSettings::OnAssetAdded);
|
|
AssetRegistry.OnAssetRemoved().AddUObject(this, &UAkSettings::OnAssetRemoved);
|
|
VisualizeRoomsAndPortals = false;
|
|
bShowReverbInfo = true;
|
|
#endif // WITH_EDITOR
|
|
}
|
|
|
|
UAkSettings::~UAkSettings()
|
|
{
|
|
#if WITH_EDITOR
|
|
#if AK_SUPPORT_WAAPI
|
|
FAkWaapiClient* waapiClient = FAkWaapiClient::Get();
|
|
if (waapiClient != nullptr)
|
|
{
|
|
if (WaapiProjectLoadedHandle.IsValid())
|
|
{
|
|
waapiClient->OnProjectLoaded.Remove(WaapiProjectLoadedHandle);
|
|
WaapiProjectLoadedHandle.Reset();
|
|
}
|
|
if (WaapiConnectionLostHandle.IsValid())
|
|
{
|
|
waapiClient->OnConnectionLost.Remove(WaapiConnectionLostHandle);
|
|
WaapiConnectionLostHandle.Reset();
|
|
}
|
|
ClearWaapiTextureCallbacks();
|
|
}
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
ECollisionChannel UAkSettings::ConvertFitToGeomCollisionChannel(EAkCollisionChannel CollisionChannel)
|
|
{
|
|
if (CollisionChannel != EAkCollisionChannel::EAKCC_UseIntegrationSettingsDefault)
|
|
return (ECollisionChannel)CollisionChannel;
|
|
|
|
const UAkSettings* AkSettings = GetDefault<UAkSettings>();
|
|
|
|
if (AkSettings)
|
|
return AkSettings->DefaultFitToGeometryCollisionChannel;
|
|
|
|
return ECollisionChannel::ECC_WorldStatic;
|
|
}
|
|
|
|
ECollisionChannel UAkSettings::ConvertOcclusionCollisionChannel(EAkCollisionChannel CollisionChannel)
|
|
{
|
|
if (CollisionChannel != EAkCollisionChannel::EAKCC_UseIntegrationSettingsDefault)
|
|
return (ECollisionChannel)CollisionChannel;
|
|
|
|
const UAkSettings* AkSettings = GetDefault<UAkSettings>();
|
|
|
|
if (AkSettings)
|
|
return AkSettings->DefaultOcclusionCollisionChannel;
|
|
|
|
return ECollisionChannel::ECC_WorldStatic;
|
|
}
|
|
|
|
void UAkSettings::PostInitProperties()
|
|
{
|
|
Super::PostInitProperties();
|
|
|
|
#if WITH_EDITOR
|
|
UAkSettingsPerUser* AkSettingsPerUser = GetMutableDefault<UAkSettingsPerUser>();
|
|
|
|
if (AkSettingsPerUser)
|
|
{
|
|
bool didChanges = false;
|
|
|
|
if (!WwiseWindowsInstallationPath_DEPRECATED.Path.IsEmpty())
|
|
{
|
|
AkSettingsPerUser->WwiseWindowsInstallationPath = WwiseWindowsInstallationPath_DEPRECATED;
|
|
WwiseWindowsInstallationPath_DEPRECATED.Path.Reset();
|
|
didChanges = true;
|
|
}
|
|
|
|
if (!WwiseMacInstallationPath_DEPRECATED.FilePath.IsEmpty())
|
|
{
|
|
AkSettingsPerUser->WwiseMacInstallationPath = WwiseMacInstallationPath_DEPRECATED;
|
|
WwiseMacInstallationPath_DEPRECATED.FilePath.Reset();
|
|
didChanges = true;
|
|
}
|
|
|
|
if (bAutoConnectToWAAPI_DEPRECATED)
|
|
{
|
|
AkSettingsPerUser->bAutoConnectToWAAPI = true;
|
|
bAutoConnectToWAAPI_DEPRECATED = false;
|
|
didChanges = true;
|
|
}
|
|
|
|
if (didChanges)
|
|
{
|
|
AkUnrealEditorHelper::SaveConfigFile(this);
|
|
AkSettingsPerUser->SaveConfig();
|
|
}
|
|
}
|
|
|
|
if (!MigratedEnableMultiCoreRendering)
|
|
{
|
|
MigratedEnableMultiCoreRendering = true;
|
|
|
|
for (const auto& PlatformName : AkUnrealPlatformHelper::GetAllSupportedWwisePlatforms())
|
|
{
|
|
AkSettings_Helper::MigrateMultiCoreRendering(bEnableMultiCoreRendering_DEPRECATED, *PlatformName);
|
|
}
|
|
}
|
|
|
|
PreviousDecayAuxBusMap = EnvironmentDecayAuxBusMap;
|
|
#endif // WITH_EDITOR
|
|
}
|
|
|
|
#if WITH_EDITOR
|
|
void UAkSettings::PreEditChange(FProperty* PropertyAboutToChange)
|
|
{
|
|
PreviousWwiseProjectPath = WwiseProjectPath.FilePath;
|
|
PreviousWwiseGeneratedSoundBankFolder = GeneratedSoundBanksFolder.Path;
|
|
}
|
|
|
|
bool UAkSettings::UpdateGeneratedSoundBanksPath(FString Path)
|
|
{
|
|
PreviousWwiseGeneratedSoundBankFolder = GeneratedSoundBanksFolder.Path;
|
|
GeneratedSoundBanksFolder.Path = Path;
|
|
return UpdateGeneratedSoundBanksPath();
|
|
}
|
|
|
|
bool UAkSettings::GeneratedSoundBanksPathExists() const
|
|
{
|
|
return FPaths::DirectoryExists(AkUnrealHelper::GetSoundBankDirectory());
|
|
}
|
|
|
|
bool UAkSettings::AreSoundBanksGenerated() const
|
|
{
|
|
return FPaths::FileExists(FPaths::Combine(AkUnrealHelper::GetSoundBankDirectory(), TEXT("ProjectInfo.json")));
|
|
}
|
|
|
|
void UAkSettings::RefreshAcousticTextureParams() const
|
|
{
|
|
for (auto const& texture : AcousticTextureParamsMap)
|
|
{
|
|
OnTextureParamsChanged.Broadcast(texture.Key);
|
|
}
|
|
}
|
|
|
|
bool UAkSettings::UpdateGeneratedSoundBanksPath()
|
|
{
|
|
bool bPathChanged = AkUnrealEditorHelper::SanitizeFolderPathAndMakeRelativeToContentDir(
|
|
GeneratedSoundBanksFolder.Path, PreviousWwiseGeneratedSoundBankFolder,
|
|
FText::FromString("Please enter a valid directory path"));
|
|
|
|
if (bPathChanged)
|
|
{
|
|
OnGeneratedSoundBanksPathChanged.Broadcast();
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAkAudio, Log, TEXT("AkSettings: The given GeneratedSoundBanks folder was the same as the previous one."));
|
|
}
|
|
return bPathChanged;
|
|
}
|
|
|
|
void UAkSettings::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
|
|
{
|
|
const FName PropertyName = (PropertyChangedEvent.Property != nullptr) ? PropertyChangedEvent.Property->GetFName() : NAME_None;
|
|
const FName MemberPropertyName = (PropertyChangedEvent.MemberProperty != nullptr) ? PropertyChangedEvent.MemberProperty->GetFName() : NAME_None;
|
|
ISettingsEditorModule& SettingsEditorModule = FModuleManager::GetModuleChecked<ISettingsEditorModule>("SettingsEditor");
|
|
|
|
if ( PropertyName == GET_MEMBER_NAME_CHECKED(UAkSettings, MaxSimultaneousReverbVolumes))
|
|
{
|
|
MaxSimultaneousReverbVolumes = FMath::Clamp<uint8>( MaxSimultaneousReverbVolumes, 0, AK_MAX_AUX_PER_OBJ );
|
|
FAkAudioDevice* AkAudioDevice = FAkAudioDevice::Get();
|
|
if( AkAudioDevice )
|
|
{
|
|
AkAudioDevice->SetMaxAuxBus(MaxSimultaneousReverbVolumes);
|
|
}
|
|
}
|
|
else if (PropertyName == GET_MEMBER_NAME_CHECKED(UAkSettings, AudioRouting))
|
|
{
|
|
OnAudioRoutingUpdate();
|
|
}
|
|
|
|
else if (MemberPropertyName == GET_MEMBER_NAME_CHECKED(UAkSettings, WwiseProjectPath))
|
|
{
|
|
SanitizeProjectPath(WwiseProjectPath.FilePath, PreviousWwiseProjectPath, FText::FromString("Please enter a valid Wwise project"));
|
|
}
|
|
else if (MemberPropertyName == GET_MEMBER_NAME_CHECKED(UAkSettings, AkGeometryMap))
|
|
{
|
|
if (PropertyName == GET_MEMBER_NAME_CHECKED(FAkGeometrySurfacePropertiesToMap, AcousticTexture))
|
|
{
|
|
for (auto& elem : AkGeometryMap)
|
|
{
|
|
PhysicalMaterialAcousticTextureMap[elem.Key.LoadSynchronous()] = elem.Value.AcousticTexture.LoadSynchronous();
|
|
}
|
|
}
|
|
else if (PropertyName == GET_MEMBER_NAME_CHECKED(FAkGeometrySurfacePropertiesToMap, OcclusionValue))
|
|
{
|
|
for (auto& elem : AkGeometryMap)
|
|
{
|
|
PhysicalMaterialOcclusionMap[elem.Key.LoadSynchronous()] = elem.Value.OcclusionValue;
|
|
}
|
|
}
|
|
}
|
|
else if (MemberPropertyName == GET_MEMBER_NAME_CHECKED(UAkSettings, VisualizeRoomsAndPortals))
|
|
{
|
|
OnShowRoomsPortalsChanged.Broadcast();
|
|
}
|
|
else if (MemberPropertyName == GET_MEMBER_NAME_CHECKED(UAkSettings, bShowReverbInfo))
|
|
{
|
|
OnShowReverbInfoChanged.Broadcast();
|
|
}
|
|
else if (MemberPropertyName == GET_MEMBER_NAME_CHECKED(UAkSettings, EnvironmentDecayAuxBusMap))
|
|
{
|
|
if (PropertyChangedEvent.ChangeType != EPropertyChangeType::Interactive)
|
|
{
|
|
DecayAuxBusMapChanged();
|
|
OnAuxBusAssignmentMapChanged.Broadcast();
|
|
}
|
|
}
|
|
else if (MemberPropertyName == GET_MEMBER_NAME_CHECKED(UAkSettings, GlobalDecayAbsorption))
|
|
{
|
|
OnAuxBusAssignmentMapChanged.Broadcast();
|
|
}
|
|
else if (MemberPropertyName == GET_MEMBER_NAME_CHECKED(UAkSettings, DefaultReverbAuxBus))
|
|
{
|
|
OnAuxBusAssignmentMapChanged.Broadcast();
|
|
}
|
|
else if (MemberPropertyName == GET_MEMBER_NAME_CHECKED(UAkSettings, HFDampingName)
|
|
|| MemberPropertyName == GET_MEMBER_NAME_CHECKED(UAkSettings, DecayEstimateName)
|
|
|| MemberPropertyName == GET_MEMBER_NAME_CHECKED(UAkSettings, TimeToFirstReflectionName)
|
|
|| MemberPropertyName == GET_MEMBER_NAME_CHECKED(UAkSettings, HFDampingRTPC)
|
|
|| MemberPropertyName == GET_MEMBER_NAME_CHECKED(UAkSettings, DecayEstimateRTPC)
|
|
|| MemberPropertyName == GET_MEMBER_NAME_CHECKED(UAkSettings, TimeToFirstReflectionRTPC))
|
|
{
|
|
OnReverbRTPCChanged.Broadcast();
|
|
}
|
|
else if (MemberPropertyName == GET_MEMBER_NAME_CHECKED(UAkSettings, WwiseStagingDirectory))
|
|
{
|
|
FAkAudioModule::AkAudioModuleInstance->UpdateWwiseResourceLoaderSettings();
|
|
}
|
|
else if (MemberPropertyName == GET_MEMBER_NAME_CHECKED(UAkSettings, GeneratedSoundBanksFolder))
|
|
{
|
|
UpdateGeneratedSoundBanksPath();
|
|
}
|
|
|
|
Super::PostEditChangeProperty(PropertyChangedEvent);
|
|
}
|
|
|
|
void UAkSettings::ToggleVisualizeRoomsAndPortals()
|
|
{
|
|
VisualizeRoomsAndPortals = !VisualizeRoomsAndPortals;
|
|
OnShowRoomsPortalsChanged.Broadcast();
|
|
}
|
|
|
|
void UAkSettings::ToggleShowReverbInfo()
|
|
{
|
|
bShowReverbInfo = !bShowReverbInfo;
|
|
OnShowReverbInfoChanged.Broadcast();
|
|
}
|
|
|
|
void UAkSettings::FillAkGeometryMap(
|
|
const TArray<FAssetData>& PhysicalMaterialAssets,
|
|
const TArray<FAssetData>& AcousticTextureAssets)
|
|
{
|
|
TArray<int32> assignments;
|
|
assignments.Init(-1, PhysicalMaterialAssets.Num());
|
|
|
|
AkSettings_Helper::MatchAcousticTextureNamesToPhysMaterialNames(PhysicalMaterialAssets, AcousticTextureAssets, assignments);
|
|
|
|
for (int i = 0; i < PhysicalMaterialAssets.Num(); i++)
|
|
{
|
|
auto physicalMaterial = Cast<UPhysicalMaterial>(PhysicalMaterialAssets[i].GetAsset());
|
|
if (!PhysicalMaterialAcousticTextureMap.Contains(physicalMaterial))
|
|
{
|
|
if (assignments[i] != -1)
|
|
{
|
|
int32 acousticTextureIdx = assignments[i];
|
|
auto acousticTexture = Cast<UAkAcousticTexture>(AcousticTextureAssets[acousticTextureIdx].GetAsset());
|
|
PhysicalMaterialAcousticTextureMap.Add(physicalMaterial, acousticTexture);
|
|
}
|
|
else
|
|
{
|
|
PhysicalMaterialAcousticTextureMap.Add(physicalMaterial);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (assignments[i] != -1)
|
|
{
|
|
if (!PhysicalMaterialAcousticTextureMap[physicalMaterial])
|
|
{
|
|
int32 acousticTextureIdx = assignments[i];
|
|
auto acousticTexture = Cast<UAkAcousticTexture>(AcousticTextureAssets[acousticTextureIdx].GetAsset());
|
|
PhysicalMaterialAcousticTextureMap[physicalMaterial] = acousticTexture;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!PhysicalMaterialOcclusionMap.Contains(physicalMaterial))
|
|
PhysicalMaterialOcclusionMap.Add(physicalMaterial, 1.f);
|
|
}
|
|
|
|
UpdateAkGeometryMap();
|
|
}
|
|
|
|
void UAkSettings::UpdateAkGeometryMap()
|
|
{
|
|
decltype(AkGeometryMap) UpdatedGeometryMap;
|
|
for (const auto& AcousticTextureTuple : PhysicalMaterialAcousticTextureMap)
|
|
{
|
|
const auto* PhysicalMaterial(AcousticTextureTuple.Key);
|
|
const auto* AcousticTexture(AcousticTextureTuple.Value);
|
|
const auto* OcclusionPtr = PhysicalMaterialOcclusionMap.Find(PhysicalMaterial);
|
|
if (UNLIKELY(!OcclusionPtr))
|
|
{
|
|
UE_LOG(LogAkAudio, Warning, TEXT("UpdateAkGeometryMap: Could not find Occlusion of Physical Material %s with Acoustic Texture %s"),
|
|
PhysicalMaterial ? *PhysicalMaterial->GetFName().ToString() : TEXT("[nullptr]"),
|
|
AcousticTexture ? *AcousticTexture->GetFName().ToString() : TEXT("[nullptr]"));
|
|
continue;
|
|
}
|
|
|
|
FAkGeometrySurfacePropertiesToMap SurfaceProperties;
|
|
SurfaceProperties.AcousticTexture = AcousticTexture;
|
|
SurfaceProperties.OcclusionValue = *OcclusionPtr;
|
|
UpdatedGeometryMap.Emplace(PhysicalMaterial, MoveTemp(SurfaceProperties));
|
|
}
|
|
|
|
UpdatedGeometryMap.KeySort([](const auto& Lhs, const auto& Rhs) {
|
|
if (UNLIKELY(!Lhs.IsValid() || !Rhs.IsValid()))
|
|
{
|
|
return !Lhs.IsValid();
|
|
}
|
|
return Lhs->GetPathName().Compare(Rhs->GetPathName()) < 0;
|
|
});
|
|
|
|
if (!UpdatedGeometryMap.OrderIndependentCompareEqual(AkGeometryMap))
|
|
{
|
|
UE_LOG(LogAkAudio, Verbose, TEXT("UpdateAkGeometryMap: Updating changed AkGeometryMap"));
|
|
AkGeometryMap = UpdatedGeometryMap;
|
|
AkUnrealEditorHelper::SaveConfigFile(this);
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAkAudio, VeryVerbose, TEXT("UpdateAkGeometryMap: AkGeometryMap is unchanged. Skip updating."));
|
|
}
|
|
}
|
|
|
|
void UAkSettings::InitAkGeometryMap()
|
|
{
|
|
PhysicalMaterialAcousticTextureMap.Empty();
|
|
PhysicalMaterialOcclusionMap.Empty();
|
|
|
|
// copy everything from the ini file
|
|
for (auto& elem : AkGeometryMap)
|
|
{
|
|
auto physMat = elem.Key.LoadSynchronous();
|
|
auto surfaceProps = elem.Value;
|
|
PhysicalMaterialAcousticTextureMap.Add(physMat, surfaceProps.AcousticTexture.LoadSynchronous());
|
|
PhysicalMaterialOcclusionMap.Add(physMat, surfaceProps.OcclusionValue);
|
|
}
|
|
bAkGeometryMapInitialized = true;
|
|
|
|
// Obtain the 2 list of children we want to match
|
|
TArray<FAssetData> PhysicalMaterials, AcousticTextures;
|
|
#if UE_5_1_OR_LATER
|
|
AssetRegistryModule->Get().GetAssetsByClass(UPhysicalMaterial::StaticClass()->GetClassPathName(), PhysicalMaterials);
|
|
AssetRegistryModule->Get().GetAssetsByClass(UAkAcousticTexture::StaticClass()->GetClassPathName(), AcousticTextures);
|
|
#else
|
|
AssetRegistryModule->Get().GetAssetsByClass(UPhysicalMaterial::StaticClass()->GetFName(), PhysicalMaterials);
|
|
AssetRegistryModule->Get().GetAssetsByClass(UAkAcousticTexture::StaticClass()->GetFName(), AcousticTextures);
|
|
#endif
|
|
|
|
FillAkGeometryMap(PhysicalMaterials, AcousticTextures);
|
|
}
|
|
|
|
void UAkSettings::ClearAkRoomDecayAuxBusMap()
|
|
{
|
|
EnvironmentDecayAuxBusMap.Empty();
|
|
PreviousDecayAuxBusMap = EnvironmentDecayAuxBusMap;
|
|
}
|
|
|
|
void UAkSettings::InsertDecayKeyValue(const float& decayKey)
|
|
{
|
|
if (decayKey < 0.0f)
|
|
{
|
|
UE_LOG(LogAkAudio, Warning, TEXT("AkSettings: Reverb Assignment Map: Decay key values must be positive."));
|
|
return;
|
|
}
|
|
// Refuse key value if it is too close to an existing key value (within MinimumDecayKeyDistance).
|
|
TArray<float> decayKeys;
|
|
EnvironmentDecayAuxBusMap.GetKeys(decayKeys);
|
|
for (int i = 0; i < decayKeys.Num(); ++i)
|
|
{
|
|
if (FMath::Abs(decayKeys[i] - decayKey) < MinimumDecayKeyDistance)
|
|
{
|
|
UE_LOG(LogAkAudio, Warning, TEXT("AkSettings: Reverb Assignment Map: New decay key too close to existing key. Must be +- %f from any existing key."), MinimumDecayKeyDistance);
|
|
return;
|
|
}
|
|
// Keys are reverse sorted. If the new key value is larger enough than the current index, we have found a valid insert space.
|
|
// (There will be no other keys larger than the current index to check against).
|
|
if (decayKey - decayKeys[i] > MinimumDecayKeyDistance)
|
|
break;
|
|
}
|
|
EnvironmentDecayAuxBusMap.Add(decayKey, nullptr);
|
|
SortDecayKeys();
|
|
}
|
|
|
|
void UAkSettings::SetAcousticTextureParams(const FGuid& textureID, const FAkAcousticTextureParams& params)
|
|
{
|
|
if (AcousticTextureParamsMap.Contains(textureID))
|
|
AcousticTextureParamsMap[textureID] = params;
|
|
else
|
|
AcousticTextureParamsMap.Add(textureID, params);
|
|
|
|
#if AK_SUPPORT_WAAPI
|
|
RegisterWaapiTextureCallback(textureID);
|
|
#endif
|
|
}
|
|
|
|
void UAkSettings::ClearTextureParamsMap()
|
|
{
|
|
AcousticTextureParamsMap.Empty();
|
|
|
|
#if AK_SUPPORT_WAAPI
|
|
ClearWaapiTextureCallbacks();
|
|
#endif
|
|
}
|
|
|
|
#if AK_SUPPORT_WAAPI
|
|
void UAkSettings::WaapiProjectLoaded()
|
|
{
|
|
TArray<FGuid> keys;
|
|
AcousticTextureParamsMap.GetKeys(keys);
|
|
for (auto key : keys)
|
|
{
|
|
UpdateTextureParams(key);
|
|
UpdateTextureColor(key);
|
|
RegisterWaapiTextureCallback(key);
|
|
}
|
|
}
|
|
|
|
void UAkSettings::WaapiDisconnected()
|
|
{
|
|
ClearWaapiTextureCallbacks();
|
|
}
|
|
|
|
void UAkSettings::RegisterWaapiTextureCallback(const FGuid& textureID)
|
|
{
|
|
FAkWaapiClient* waapiClient = FAkWaapiClient::Get();
|
|
if (waapiClient != nullptr && waapiClient->IsConnected())
|
|
{
|
|
auto absorptionCallback = WampEventCallback::CreateLambda([this](uint64_t id, TSharedPtr<FJsonObject> jsonObject)
|
|
{
|
|
const TSharedPtr<FJsonObject> itemObj = jsonObject->GetObjectField(WwiseWaapiHelper::OBJECT);
|
|
if (itemObj != nullptr)
|
|
{
|
|
const FString itemIdString = itemObj->GetStringField(WwiseWaapiHelper::ID);
|
|
FGuid itemID = FGuid::NewGuid();
|
|
FGuid::ParseExact(itemIdString, EGuidFormats::DigitsWithHyphensInBraces, itemID);
|
|
if (AcousticTextureParamsMap.Find(itemID) != nullptr)
|
|
{
|
|
AsyncTask(ENamedThreads::GameThread, [this, itemID]
|
|
{
|
|
UpdateTextureParams(itemID);
|
|
});
|
|
}
|
|
}
|
|
});
|
|
|
|
|
|
TSharedRef<FJsonObject> options = MakeShareable(new FJsonObject());
|
|
options->SetStringField(WwiseWaapiHelper::OBJECT, textureID.ToString(EGuidFormats::DigitsWithHyphensInBraces));
|
|
|
|
TArray<FString> absorptionStrings{ "AbsorptionLow", "AbsorptionMidLow", "AbsorptionMidHigh", "AbsorptionHigh" };
|
|
TSharedPtr<FJsonObject> jsonResult;
|
|
TSharedPtr<FJsonObject> unsubscribeResult;
|
|
bool unsubscribeNeeded = WaapiTextureSubscriptions.Find(textureID) != nullptr;
|
|
TArray<uint64> subscriptionIDs{ 0,0,0,0 };
|
|
for (int i = 0; i < absorptionStrings.Num(); ++i)
|
|
{
|
|
options->SetStringField(WwiseWaapiHelper::PROPERTY, absorptionStrings[i]);
|
|
if (unsubscribeNeeded)
|
|
{
|
|
waapiClient->Unsubscribe(WaapiTextureSubscriptions[textureID][i], unsubscribeResult);
|
|
}
|
|
if (!waapiClient->Subscribe(ak::wwise::core::object::propertyChanged, options, absorptionCallback, subscriptionIDs[i], jsonResult))
|
|
{
|
|
UE_LOG(LogAkAudio, Warning, TEXT("AkSettings: WAAPI: Acoustic texture propertyChanged subscription failed."));
|
|
}
|
|
}
|
|
WaapiTextureSubscriptions.Add(textureID, subscriptionIDs);
|
|
|
|
|
|
auto colorCallback = WampEventCallback::CreateLambda([this](uint64_t id, TSharedPtr<FJsonObject> jsonObject)
|
|
{
|
|
const TSharedPtr<FJsonObject> itemObj = jsonObject->GetObjectField(WwiseWaapiHelper::OBJECT);
|
|
if (itemObj != nullptr)
|
|
{
|
|
const FString itemIdString = itemObj->GetStringField(WwiseWaapiHelper::ID);
|
|
FGuid itemID = FGuid::NewGuid();
|
|
FGuid::ParseExact(itemIdString, EGuidFormats::DigitsWithHyphensInBraces, itemID);
|
|
if (AcousticTextureParamsMap.Find(itemID) != nullptr)
|
|
{
|
|
AsyncTask(ENamedThreads::GameThread, [this, itemID]
|
|
{
|
|
UpdateTextureColor(itemID);
|
|
});
|
|
}
|
|
}
|
|
});
|
|
|
|
options = MakeShareable(new FJsonObject());
|
|
options->SetStringField(WwiseWaapiHelper::OBJECT, textureID.ToString(EGuidFormats::DigitsWithHyphensInBraces));
|
|
unsubscribeNeeded = WaapiTextureColorSubscriptions.Find(textureID) != nullptr;
|
|
uint64 subscriptionID = 0;
|
|
options->SetStringField(WwiseWaapiHelper::PROPERTY, "Color");
|
|
if (unsubscribeNeeded)
|
|
{
|
|
waapiClient->Unsubscribe(WaapiTextureColorSubscriptions[textureID], unsubscribeResult);
|
|
}
|
|
if (!waapiClient->Subscribe(ak::wwise::core::object::propertyChanged, options, colorCallback, subscriptionID, jsonResult))
|
|
{
|
|
UE_LOG(LogAkAudio, Warning, TEXT("AkSettings: WAAPI: Acoustic texture Color propertyChanged subscription failed."));
|
|
}
|
|
WaapiTextureColorSubscriptions.Add(textureID, subscriptionID);
|
|
|
|
unsubscribeNeeded = WaapiTextureColorOverrideSubscriptions.Find(textureID) != nullptr;
|
|
subscriptionID = 0;
|
|
options->SetStringField(WwiseWaapiHelper::PROPERTY, "OverrideColor");
|
|
if (unsubscribeNeeded)
|
|
{
|
|
waapiClient->Unsubscribe(WaapiTextureColorOverrideSubscriptions[textureID], unsubscribeResult);
|
|
}
|
|
if (!waapiClient->Subscribe(ak::wwise::core::object::propertyChanged, options, colorCallback, subscriptionID, jsonResult))
|
|
{
|
|
UE_LOG(LogAkAudio, Warning, TEXT("AkSettings: WAAPI: Acoustic texture OverrideColor propertyChanged subscription failed."));
|
|
}
|
|
WaapiTextureColorOverrideSubscriptions.Add(textureID, subscriptionID);
|
|
}
|
|
}
|
|
|
|
void UAkSettings::UnregisterWaapiTextureCallback(const FGuid& textureID)
|
|
{
|
|
FAkWaapiClient* waapiClient = FAkWaapiClient::Get();
|
|
if (waapiClient != nullptr && waapiClient->IsConnected())
|
|
{
|
|
if (WaapiTextureSubscriptions.Find(textureID) != nullptr)
|
|
{
|
|
TSharedPtr<FJsonObject> unsubscribeResult;
|
|
for (int i = 0; i < WaapiTextureSubscriptions[textureID].Num(); ++i)
|
|
waapiClient->Unsubscribe(WaapiTextureSubscriptions[textureID][i], unsubscribeResult);
|
|
WaapiTextureSubscriptions.Remove(textureID);
|
|
}
|
|
if (WaapiTextureColorSubscriptions.Find(textureID) != nullptr)
|
|
{
|
|
TSharedPtr<FJsonObject> unsubscribeResult;
|
|
waapiClient->Unsubscribe(WaapiTextureColorSubscriptions[textureID], unsubscribeResult);
|
|
WaapiTextureColorSubscriptions.Remove(textureID);
|
|
}
|
|
if (WaapiTextureColorOverrideSubscriptions.Find(textureID) != nullptr)
|
|
{
|
|
TSharedPtr<FJsonObject> unsubscribeResult;
|
|
waapiClient->Unsubscribe(WaapiTextureColorOverrideSubscriptions[textureID], unsubscribeResult);
|
|
WaapiTextureColorOverrideSubscriptions.Remove(textureID);
|
|
}
|
|
}
|
|
}
|
|
|
|
void UAkSettings::ClearWaapiTextureCallbacks()
|
|
{
|
|
FAkWaapiClient* waapiClient = FAkWaapiClient::Get();
|
|
if (waapiClient != nullptr && waapiClient->IsConnected())
|
|
{
|
|
for (auto it = WaapiTextureSubscriptions.CreateIterator(); it; ++it)
|
|
{
|
|
TSharedPtr<FJsonObject> unsubscribeResult;
|
|
for (int i = 0; i < it.Value().Num(); ++i)
|
|
waapiClient->Unsubscribe(it.Value()[i], unsubscribeResult);
|
|
}
|
|
for (auto it = WaapiTextureColorSubscriptions.CreateIterator(); it; ++it)
|
|
{
|
|
TSharedPtr<FJsonObject> unsubscribeResult;
|
|
waapiClient->Unsubscribe(it.Value(), unsubscribeResult);
|
|
}
|
|
for (auto it = WaapiTextureColorOverrideSubscriptions.CreateIterator(); it; ++it)
|
|
{
|
|
TSharedPtr<FJsonObject> unsubscribeResult;
|
|
waapiClient->Unsubscribe(it.Value(), unsubscribeResult);
|
|
}
|
|
WaapiTextureSubscriptions.Empty();
|
|
WaapiTextureColorSubscriptions.Empty();
|
|
WaapiTextureColorOverrideSubscriptions.Empty();
|
|
}
|
|
}
|
|
|
|
void UAkSettings::UpdateTextureParams(const FGuid& textureID)
|
|
{
|
|
WAAPIGetTextureParams(textureID, AcousticTextureParamsMap[textureID]);
|
|
OnTextureParamsChanged.Broadcast(textureID);
|
|
}
|
|
|
|
void UAkSettings::UpdateTextureColor(const FGuid& textureID)
|
|
{
|
|
if (!WAAPIGetObjectOverrideColor(textureID))
|
|
{
|
|
SetTextureColor(textureID, -1);
|
|
return;
|
|
}
|
|
|
|
int colorIndex = 0;
|
|
if (WAAPIGetObjectColorIndex(textureID, colorIndex))
|
|
{
|
|
SetTextureColor(textureID, colorIndex);
|
|
}
|
|
}
|
|
|
|
void UAkSettings::SetTextureColor(FGuid textureID, int colorIndex)
|
|
{
|
|
TArray<FAssetData> AcousticTextures;
|
|
#if UE_5_1_OR_LATER
|
|
AssetRegistryModule->Get().GetAssetsByClass(UAkAcousticTexture::StaticClass()->GetClassPathName(), AcousticTextures);
|
|
#else
|
|
AssetRegistryModule->Get().GetAssetsByClass(UAkAcousticTexture::StaticClass()->GetFName(), AcousticTextures);
|
|
#endif
|
|
|
|
FLinearColor color = FAkAudioStyle::GetWwiseObjectColor(colorIndex);
|
|
for (FAssetData& textureAsset : AcousticTextures)
|
|
{
|
|
if (UAkAcousticTexture* texture = Cast<UAkAcousticTexture>(textureAsset.GetAsset()))
|
|
{
|
|
if (texture->AcousticTextureInfo.WwiseGuid == textureID && texture->EditColor != color)
|
|
{
|
|
texture->Modify();
|
|
texture->EditColor = color;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif // AK_SUPPORT_WAAPI
|
|
|
|
void UAkSettings::DecayAuxBusMapChanged()
|
|
{
|
|
// If a key has been moved beyond its neighbours, restrict it between neighbouring key values
|
|
|
|
// Removal - nothing to restrict
|
|
if (PreviousDecayAuxBusMap.Num() > EnvironmentDecayAuxBusMap.Num())
|
|
{
|
|
PreviousDecayAuxBusMap = EnvironmentDecayAuxBusMap;
|
|
return;
|
|
}
|
|
// Addition - when the insert button is used, restrictions will already be handled.
|
|
// If the stock Unreal '+' button is used, a 0-nullptr entry will be added at the end of the map. In this case,
|
|
// remove it and insert it using the InsertDecayKeyValue so it can be properly restricted and placed.
|
|
if (PreviousDecayAuxBusMap.Num() < EnvironmentDecayAuxBusMap.Num())
|
|
{
|
|
if (EnvironmentDecayAuxBusMap.Num() > 1)
|
|
{
|
|
TArray<float> keys;
|
|
EnvironmentDecayAuxBusMap.GetKeys(keys);
|
|
if (keys.Last() == 0.0f && EnvironmentDecayAuxBusMap[0.0f] == nullptr)
|
|
{
|
|
EnvironmentDecayAuxBusMap.Remove(0.0f);
|
|
InsertDecayKeyValue(0.0f);
|
|
}
|
|
}
|
|
PreviousDecayAuxBusMap = EnvironmentDecayAuxBusMap;
|
|
return;
|
|
}
|
|
|
|
// Find key that has changed
|
|
int changedKeyIndex = -1;
|
|
const int numKeys = PreviousDecayAuxBusMap.Num();
|
|
TArray<float> previousKeys;
|
|
TArray<float> newKeys;
|
|
PreviousDecayAuxBusMap.GetKeys(previousKeys);
|
|
EnvironmentDecayAuxBusMap.GetKeys(newKeys);
|
|
for (int i = 0; i < numKeys; ++i)
|
|
{
|
|
if (!FMath::IsNearlyEqual(previousKeys[i], newKeys[i], 1.0e-06F)) // Floating point property values have a tendancy to gradually wander in UE.
|
|
{
|
|
changedKeyIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
// If no key values have changed, an aux bus has been changed. Nothing to restrict.
|
|
if (changedKeyIndex == -1)
|
|
return;
|
|
// check key value
|
|
float newKeyValue = newKeys[changedKeyIndex];
|
|
float restrictedKeyValue = newKeyValue;
|
|
FString restrictionInfoString;
|
|
// Keys are sorted in reverse order such that they are ordered vertically from smallest to largest, in the UI.
|
|
// So, check newKeyValue <= newKeys[changedKeyIndex + 1] and newKeyValue >= newKeys[changedKeyIndex - 1].
|
|
const bool changedKeyIsSmallest = changedKeyIndex == numKeys - 1;
|
|
float lowerLimit = changedKeyIsSmallest ? 0.0f : newKeys[changedKeyIndex + 1];
|
|
if (newKeyValue <= lowerLimit)
|
|
{
|
|
restrictedKeyValue = changedKeyIsSmallest ? 0.0f : lowerLimit + MinimumDecayKeyDistance;
|
|
restrictionInfoString = FString("Decay key value limited by next lowest value.");
|
|
}
|
|
else if (changedKeyIndex > 0 && newKeyValue >= newKeys[changedKeyIndex - 1])
|
|
{
|
|
restrictedKeyValue = newKeys[changedKeyIndex - 1] - MinimumDecayKeyDistance;
|
|
restrictionInfoString = FString("Decay key value limited by next highest value.");
|
|
}
|
|
// If key value needs to be restricted, remove and replace the entry in the map.
|
|
if (restrictedKeyValue != newKeyValue)
|
|
{
|
|
FAkAudioStyle::DisplayEditorMessage(FText::FromString(restrictionInfoString));
|
|
TSoftObjectPtr<UAkAuxBus> auxBusToMove = EnvironmentDecayAuxBusMap[newKeyValue];
|
|
EnvironmentDecayAuxBusMap.Remove(newKeyValue);
|
|
EnvironmentDecayAuxBusMap.Add(restrictedKeyValue, auxBusToMove);
|
|
SortDecayKeys();
|
|
}
|
|
else // No restriction to apply, but still keep track of the new key values.
|
|
{
|
|
PreviousDecayAuxBusMap = EnvironmentDecayAuxBusMap;
|
|
}
|
|
}
|
|
|
|
void UAkSettings::SortDecayKeys()
|
|
{
|
|
// high to low decay ('large' to 'small' environment structure)
|
|
EnvironmentDecayAuxBusMap.KeySort([](const float& left, const float& right) {return left > right; });
|
|
PreviousDecayAuxBusMap = EnvironmentDecayAuxBusMap;
|
|
}
|
|
|
|
void UAkSettings::OnAssetAdded(const FAssetData& NewAssetData)
|
|
{
|
|
if (!bAkGeometryMapInitialized)
|
|
return;
|
|
|
|
#if UE_5_1_OR_LATER
|
|
if (NewAssetData.AssetClassPath == UPhysicalMaterial::StaticClass()->GetClassPathName())
|
|
#else
|
|
if (NewAssetData.AssetClass == UPhysicalMaterial::StaticClass()->GetFName())
|
|
#endif
|
|
{
|
|
if (auto physicalMaterial = Cast<UPhysicalMaterial>(NewAssetData.GetAsset()))
|
|
{
|
|
TArray<FAssetData> PhysicalMaterials, AcousticTextures;
|
|
PhysicalMaterials.Add(NewAssetData);
|
|
#if UE_5_1_OR_LATER
|
|
AssetRegistryModule->Get().GetAssetsByClass(UAkAcousticTexture::StaticClass()->GetClassPathName(), AcousticTextures);
|
|
#else
|
|
AssetRegistryModule->Get().GetAssetsByClass(UAkAcousticTexture::StaticClass()->GetFName(), AcousticTextures);
|
|
#endif
|
|
|
|
FillAkGeometryMap(PhysicalMaterials, AcousticTextures);
|
|
}
|
|
}
|
|
#if UE_5_1_OR_LATER
|
|
else if (NewAssetData.AssetClassPath == UAkAcousticTexture::StaticClass()->GetClassPathName())
|
|
#else
|
|
else if (NewAssetData.AssetClass == UAkAcousticTexture::StaticClass()->GetFName())
|
|
#endif
|
|
{
|
|
if (auto acousticTexture = Cast<UAkAcousticTexture>(NewAssetData.GetAsset()))
|
|
{
|
|
TArray<FAssetData> PhysicalMaterials, AcousticTextures;
|
|
#if UE_5_1_OR_LATER
|
|
AssetRegistryModule->Get().GetAssetsByClass(UPhysicalMaterial::StaticClass()->GetClassPathName(), PhysicalMaterials);
|
|
#else
|
|
AssetRegistryModule->Get().GetAssetsByClass(UPhysicalMaterial::StaticClass()->GetFName(), PhysicalMaterials);
|
|
#endif
|
|
AcousticTextures.Add(NewAssetData);
|
|
|
|
FillAkGeometryMap(PhysicalMaterials, AcousticTextures);
|
|
FAkAcousticTextureParams params;
|
|
bool paramsExist = AcousticTextureParamsMap.Contains(acousticTexture->AcousticTextureInfo.WwiseGuid);
|
|
if (paramsExist)
|
|
{
|
|
params = *AcousticTextureParamsMap.Find(acousticTexture->AcousticTextureInfo.WwiseGuid);
|
|
params.shortID = acousticTexture->AcousticTextureInfo.WwiseShortId;
|
|
}
|
|
#if AK_SUPPORT_WAAPI
|
|
bool paramsSet = WAAPIGetTextureParams(acousticTexture->AcousticTextureInfo.WwiseGuid, params);
|
|
if (paramsSet && !paramsExist)
|
|
AcousticTextureParamsMap.Add(acousticTexture->AcousticTextureInfo.WwiseGuid, params);
|
|
RegisterWaapiTextureCallback(acousticTexture->AcousticTextureInfo.WwiseGuid);
|
|
int colorIndex = -1;
|
|
if (WAAPIGetObjectColorIndex(acousticTexture->AcousticTextureInfo.WwiseGuid, colorIndex))
|
|
{
|
|
acousticTexture->EditColor = FAkAudioStyle::GetWwiseObjectColor(colorIndex);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
void UAkSettings::OnAssetRemoved(const struct FAssetData& AssetData)
|
|
{
|
|
#if UE_5_1_OR_LATER
|
|
if (AssetData.AssetClassPath == UPhysicalMaterial::StaticClass()->GetClassPathName())
|
|
#else
|
|
if (AssetData.AssetClass == UPhysicalMaterial::StaticClass()->GetFName())
|
|
#endif
|
|
{
|
|
if (auto physicalMaterial = Cast<UPhysicalMaterial>(AssetData.GetAsset()))
|
|
{
|
|
PhysicalMaterialAcousticTextureMap.Remove(physicalMaterial);
|
|
PhysicalMaterialOcclusionMap.Remove(physicalMaterial);
|
|
UpdateAkGeometryMap();
|
|
}
|
|
}
|
|
#if UE_5_1_OR_LATER
|
|
else if(AssetData.AssetClassPath == UAkAcousticTexture::StaticClass()->GetClassPathName())
|
|
#else
|
|
else if(AssetData.AssetClass == UAkAcousticTexture::StaticClass()->GetFName())
|
|
#endif
|
|
{
|
|
if(auto acousticTexture = Cast<UAkAcousticTexture>(AssetData.GetAsset()))
|
|
{
|
|
AcousticTextureParamsMap.Remove(acousticTexture->AcousticTextureInfo.WwiseGuid);
|
|
#if AK_SUPPORT_WAAPI
|
|
UnregisterWaapiTextureCallback(acousticTexture->AcousticTextureInfo.WwiseGuid);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
#if AK_SUPPORT_WAAPI
|
|
void UAkSettings::InitWaapiSync()
|
|
{
|
|
FAkWaapiClient* waapiClient = FAkWaapiClient::Get();
|
|
if (waapiClient != nullptr)
|
|
{
|
|
if (waapiClient->IsProjectLoaded())
|
|
WaapiProjectLoaded();
|
|
WaapiProjectLoadedHandle = waapiClient->OnProjectLoaded.AddLambda([this]()
|
|
{
|
|
WaapiProjectLoaded();
|
|
});
|
|
WaapiConnectionLostHandle = waapiClient->OnConnectionLost.AddLambda([this]()
|
|
{
|
|
WaapiDisconnected();
|
|
});
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void UAkSettings::EnsurePluginContentIsInAlwaysCook() const
|
|
{
|
|
UProjectPackagingSettings* PackagingSettings = GetMutableDefault<UProjectPackagingSettings>();
|
|
|
|
bool packageSettingsNeedUpdate = false;
|
|
|
|
TArray<FString> PathsToCheck = { TEXT("/Wwise/WwiseTree"), TEXT("/Wwise/WwiseTypes") };
|
|
|
|
for (auto pathToCheck : PathsToCheck)
|
|
{
|
|
if (!PackagingSettings->DirectoriesToAlwaysCook.ContainsByPredicate([pathToCheck](FDirectoryPath PathInArray) { return PathInArray.Path == pathToCheck; }))
|
|
{
|
|
FDirectoryPath newPath;
|
|
newPath.Path = pathToCheck;
|
|
|
|
PackagingSettings->DirectoriesToAlwaysCook.Add(newPath);
|
|
packageSettingsNeedUpdate = true;
|
|
}
|
|
}
|
|
|
|
if (packageSettingsNeedUpdate)
|
|
{
|
|
AkUnrealEditorHelper::SaveConfigFile(PackagingSettings);
|
|
}
|
|
}
|
|
|
|
void UAkSettings::RemoveSoundDataFromAlwaysCook(const FString& SoundDataPath)
|
|
{
|
|
bool changed = false;
|
|
|
|
UProjectPackagingSettings* PackagingSettings = GetMutableDefault<UProjectPackagingSettings>();
|
|
|
|
for (int32 i = PackagingSettings->DirectoriesToAlwaysCook.Num() - 1; i >= 0; --i)
|
|
{
|
|
if (PackagingSettings->DirectoriesToAlwaysCook[i].Path == SoundDataPath)
|
|
{
|
|
PackagingSettings->DirectoriesToAlwaysCook.RemoveAt(i);
|
|
changed = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (changed)
|
|
{
|
|
AkUnrealEditorHelper::SaveConfigFile(PackagingSettings);
|
|
}
|
|
}
|
|
|
|
void UAkSettings::SanitizeProjectPath(FString& Path, const FString& PreviousPath, const FText& DialogMessage)
|
|
{
|
|
AkUnrealHelper::TrimPath(Path);
|
|
|
|
FString TempPath = IFileManager::Get().ConvertToAbsolutePathForExternalAppForWrite(*Path);
|
|
|
|
FText FailReason;
|
|
if (!FPaths::ValidatePath(TempPath, &FailReason))
|
|
{
|
|
if (EAppReturnType::Ok == FMessageDialog::Open(EAppMsgType::Ok, FailReason))
|
|
{
|
|
Path = PreviousPath;
|
|
return;
|
|
}
|
|
}
|
|
|
|
auto ProjectDirectory = AkUnrealHelper::GetProjectDirectory();
|
|
if (!FPaths::FileExists(TempPath))
|
|
{
|
|
// Path might be a valid one (relative to game) entered manually. Check that.
|
|
TempPath = FPaths::ConvertRelativePathToFull(ProjectDirectory, Path);
|
|
|
|
if (!FPaths::FileExists(TempPath))
|
|
{
|
|
if (EAppReturnType::Ok == FMessageDialog::Open(EAppMsgType::Ok, DialogMessage))
|
|
{
|
|
Path = PreviousPath;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Make the path relative to the game dir
|
|
FPaths::MakePathRelativeTo(TempPath, *ProjectDirectory);
|
|
Path = TempPath;
|
|
|
|
if (Path != PreviousPath)
|
|
{
|
|
#if UE_4_26_OR_LATER
|
|
auto WwiseBrowserTab = FGlobalTabmanager::Get()->TryInvokeTab(FName("WwiseBrowser"));
|
|
#else
|
|
TSharedRef<SDockTab> WwiseBrowserTab = FGlobalTabmanager::Get()->InvokeTab(FName("WwiseBrowser"));
|
|
#endif
|
|
bRequestRefresh = true;
|
|
}
|
|
}
|
|
|
|
void UAkSettings::OnAudioRoutingUpdate()
|
|
{
|
|
// Calculate what is expected
|
|
bool bExpectedCustom = false;
|
|
bool bExpectedSeparate = false;
|
|
bool bExpectedUsingAudioMixer = false;
|
|
bool bExpectedAudioModuleOverride = true;
|
|
bool bExpectedWwiseSoundEngineEnabled = true;
|
|
bool bExpectedWwiseAudioLinkEnabled = false;
|
|
bool bExpectedAkAudioMixerEnabled = false;
|
|
FString ExpectedAudioDeviceModuleName;
|
|
FString ExpectedAudioMixerModuleName;
|
|
switch (AudioRouting)
|
|
{
|
|
case EAkUnrealAudioRouting::Custom:
|
|
UE_LOG(LogAkAudio, VeryVerbose, TEXT("OnAudioRoutingUpdate: Setting for Custom"));
|
|
bExpectedCustom = true;
|
|
break;
|
|
|
|
case EAkUnrealAudioRouting::Separate:
|
|
UE_LOG(LogAkAudio, VeryVerbose, TEXT("OnAudioRoutingUpdate: Setting for Separate"));
|
|
bExpectedSeparate = true;
|
|
bExpectedUsingAudioMixer = true;
|
|
bExpectedAudioModuleOverride = false;
|
|
break;
|
|
|
|
case EAkUnrealAudioRouting::EnableWwiseOnly:
|
|
UE_LOG(LogAkAudio, VeryVerbose, TEXT("OnAudioRoutingUpdate: Setting for DisableUnreal"));
|
|
bExpectedUsingAudioMixer = false;
|
|
ExpectedAudioDeviceModuleName = TEXT("");
|
|
ExpectedAudioMixerModuleName = TEXT("");
|
|
break;
|
|
|
|
case EAkUnrealAudioRouting::EnableUnrealOnly:
|
|
UE_LOG(LogAkAudio, VeryVerbose, TEXT("OnAudioRoutingUpdate: Setting for DisableWwise"));
|
|
bExpectedSeparate = true;
|
|
bExpectedUsingAudioMixer = true;
|
|
bExpectedAudioModuleOverride = false;
|
|
bExpectedWwiseSoundEngineEnabled = false;
|
|
break;
|
|
|
|
case EAkUnrealAudioRouting::AudioMixer:
|
|
UE_LOG(LogAkAudio, VeryVerbose, TEXT("OnAudioRoutingUpdate: Setting for AudioMixer"));
|
|
bExpectedUsingAudioMixer = true;
|
|
bExpectedAkAudioMixerEnabled = true;
|
|
ExpectedAudioDeviceModuleName = TEXT("AkAudioMixer");
|
|
ExpectedAudioMixerModuleName = TEXT("AkAudioMixer");
|
|
break;
|
|
|
|
case EAkUnrealAudioRouting::AudioLink:
|
|
UE_LOG(LogAkAudio, VeryVerbose, TEXT("OnAudioRoutingUpdate: Setting for AudioLink"));
|
|
bExpectedSeparate = true;
|
|
bExpectedUsingAudioMixer = true;
|
|
bExpectedWwiseAudioLinkEnabled = true;
|
|
bExpectedAudioModuleOverride = false;
|
|
break;
|
|
|
|
default:
|
|
UE_LOG(LogAkAudio, Warning, TEXT("OnAudioRoutingUpdate: Unknown AudioRouting"));
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Actually update the files
|
|
//
|
|
|
|
UE_LOG(LogAkAudio, Verbose, TEXT("OnAudioRoutingUpdate: Updating system settings."));
|
|
|
|
{
|
|
bWwiseSoundEngineEnabled = bExpectedWwiseSoundEngineEnabled;
|
|
UE_LOG(LogAkAudio, Log, TEXT("OnAudioRoutingUpdate: Wwise SoundEngine Enabled: %s"), bExpectedWwiseSoundEngineEnabled ? TEXT("true") : TEXT("false"));
|
|
|
|
bWwiseAudioLinkEnabled = bExpectedWwiseAudioLinkEnabled;
|
|
UE_LOG(LogAkAudio, Log, TEXT("OnAudioRoutingUpdate: Wwise AudioLink Enabled: %s"), bExpectedWwiseAudioLinkEnabled ? TEXT("true") : TEXT("false"));
|
|
|
|
bAkAudioMixerEnabled = bExpectedAkAudioMixerEnabled;
|
|
UE_LOG(LogAkAudio, Log, TEXT("OnAudioRoutingUpdate: Wwise AudioMixer Enabled: %s"), bExpectedAkAudioMixerEnabled ? TEXT("true") : TEXT("false"));
|
|
#if UE_5_0_OR_LATER
|
|
TryUpdateDefaultConfigFile();
|
|
#else
|
|
UpdateDefaultConfigFile();
|
|
#endif
|
|
}
|
|
|
|
TArray<FString> IniPlatformNames;
|
|
|
|
#if UE_5_0_OR_LATER
|
|
for (const auto& PlatformInfo : FDataDrivenPlatformInfoRegistry::GetAllPlatformInfos())
|
|
{
|
|
if (!PlatformInfo.Value.bIsFakePlatform)
|
|
{
|
|
IniPlatformNames.Add(PlatformInfo.Value.IniPlatformName.ToString());
|
|
}
|
|
}
|
|
#else
|
|
for (const auto& Platform : GetTargetPlatformManagerRef().GetTargetPlatforms())
|
|
{
|
|
IniPlatformNames.Add(Platform->IniPlatformName());
|
|
}
|
|
#endif
|
|
for (const auto& IniPlatformName : IniPlatformNames)
|
|
{
|
|
const auto RelativePlatformEnginePath = FString::Printf(TEXT("%s/%sEngine.ini"), *IniPlatformName, *IniPlatformName);
|
|
auto PlatformEnginePath = FString::Printf(TEXT("%s%s"), *FPaths::SourceConfigDir(), *RelativePlatformEnginePath);
|
|
|
|
#if UE_5_1_OR_LATER
|
|
PlatformEnginePath = FConfigCacheIni::NormalizeConfigIniPath(PlatformEnginePath);
|
|
#else
|
|
FPaths::RemoveDuplicateSlashes(PlatformEnginePath);
|
|
PlatformEnginePath = FPaths::CreateStandardFilename(PlatformEnginePath);
|
|
#endif
|
|
|
|
const FString FullPlatformEnginePath = FPaths::ConvertRelativePathToFull(PlatformEnginePath);
|
|
|
|
if (FPlatformFileManager::Get().GetPlatformFile().FileExists(*FullPlatformEnginePath))
|
|
{
|
|
FText ErrorMessage;
|
|
|
|
if (ISourceControlModule::Get().IsEnabled())
|
|
{
|
|
if (SourceControlHelpers::CheckoutOrMarkForAdd(FullPlatformEnginePath, FText::FromString(FullPlatformEnginePath), NULL, ErrorMessage))
|
|
{
|
|
ErrorMessage = FText();
|
|
}
|
|
}
|
|
else if (!FPlatformFileManager::Get().GetPlatformFile().SetReadOnly(*FullPlatformEnginePath, false))
|
|
{
|
|
ErrorMessage = FText::Format(LOCTEXT("FailedToMakeWritable", "Could not make {0} writable."), FText::FromString(FullPlatformEnginePath));
|
|
}
|
|
|
|
if (!ErrorMessage.IsEmpty())
|
|
{
|
|
FNotificationInfo Info(ErrorMessage);
|
|
Info.ExpireDuration = 3.0;
|
|
|
|
FSlateNotificationManager::Get().AddNotification(Info);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (bExpectedUsingAudioMixer)
|
|
{
|
|
UE_LOG(LogAkAudio, Log, TEXT("%s: Removing UseAudioMixer override"), *RelativePlatformEnginePath);
|
|
GConfig->RemoveKey(TEXT("Audio"), TEXT("UseAudioMixer"), PlatformEnginePath);
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAkAudio, Log, TEXT("%s: Updating UseAudioMixer to: %s"), *RelativePlatformEnginePath, bExpectedUsingAudioMixer ? TEXT("true") : TEXT("false"));
|
|
GConfig->SetBool(TEXT("Audio"), TEXT("UseAudioMixer"), bExpectedUsingAudioMixer, PlatformEnginePath);
|
|
}
|
|
|
|
if (bExpectedAudioModuleOverride)
|
|
{
|
|
UE_LOG(LogAkAudio, Log, TEXT("%s: Updating AudioDeviceModuleName: %s"), *RelativePlatformEnginePath, ExpectedAudioDeviceModuleName.IsEmpty() ? TEXT("[empty]") : *ExpectedAudioDeviceModuleName);
|
|
UE_LOG(LogAkAudio, Log, TEXT("%s: Updating AudioMixerModuleName: %s"), *RelativePlatformEnginePath, ExpectedAudioMixerModuleName.IsEmpty() ? TEXT("[empty]") : *ExpectedAudioMixerModuleName);
|
|
GConfig->SetString(TEXT("Audio"), TEXT("AudioDeviceModuleName"), *ExpectedAudioDeviceModuleName, PlatformEnginePath);
|
|
GConfig->SetString(TEXT("Audio"), TEXT("AudioMixerModuleName"), *ExpectedAudioMixerModuleName, PlatformEnginePath);
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAkAudio, Log, TEXT("%s: Removing AudioDeviceModuleName override"), *RelativePlatformEnginePath);
|
|
UE_LOG(LogAkAudio, Log, TEXT("%s: Removing AudioMixerModuleName override"), *RelativePlatformEnginePath);
|
|
GConfig->RemoveKey(TEXT("Audio"), TEXT("AudioDeviceModuleName"), PlatformEnginePath);
|
|
GConfig->RemoveKey(TEXT("Audio"), TEXT("AudioMixerModuleName"), PlatformEnginePath);
|
|
}
|
|
|
|
GConfig->Flush(false, PlatformEnginePath);
|
|
}
|
|
}
|
|
|
|
void UAkSettings::RemoveSoundDataFromAlwaysStageAsUFS(const FString& SoundDataPath)
|
|
{
|
|
bool changed = false;
|
|
|
|
UProjectPackagingSettings* PackagingSettings = GetMutableDefault<UProjectPackagingSettings>();
|
|
|
|
for (int32 i = PackagingSettings->DirectoriesToAlwaysStageAsUFS.Num() - 1; i >= 0; --i)
|
|
{
|
|
if (PackagingSettings->DirectoriesToAlwaysStageAsUFS[i].Path == SoundDataPath)
|
|
{
|
|
PackagingSettings->DirectoriesToAlwaysStageAsUFS.RemoveAt(i);
|
|
changed = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (changed)
|
|
{
|
|
AkUnrealEditorHelper::SaveConfigFile(PackagingSettings);
|
|
}
|
|
}
|
|
|
|
#endif // WITH_EDITOR
|
|
|
|
const FAkAcousticTextureParams* UAkSettings::GetTextureParams(const uint32& shortID) const
|
|
{
|
|
for (auto it = AcousticTextureParamsMap.CreateConstIterator(); it; ++it)
|
|
{
|
|
if (it.Value().shortID == shortID)
|
|
return AcousticTextureParamsMap.Find(it.Key());
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
bool UAkSettings::ReverbRTPCsInUse() const
|
|
{
|
|
return DecayRTPCInUse() || DampingRTPCInUse() || PredelayRTPCInUse();
|
|
}
|
|
|
|
bool UAkSettings::DecayRTPCInUse() const
|
|
{
|
|
const bool validPath = !DecayEstimateRTPC.ToSoftObjectPath().ToString().IsEmpty();
|
|
return validPath || !DecayEstimateName.IsEmpty();
|
|
}
|
|
|
|
bool UAkSettings::DampingRTPCInUse() const
|
|
{
|
|
const bool validPath = !HFDampingRTPC.ToSoftObjectPath().ToString().IsEmpty();
|
|
return validPath || !HFDampingName.IsEmpty();
|
|
}
|
|
|
|
bool UAkSettings::PredelayRTPCInUse() const
|
|
{
|
|
const bool validPath = !TimeToFirstReflectionRTPC.ToSoftObjectPath().ToString().IsEmpty();
|
|
return validPath || !TimeToFirstReflectionName.IsEmpty();
|
|
}
|
|
|
|
bool UAkSettings::GetAssociatedAcousticTexture(const UPhysicalMaterial* physMaterial, UAkAcousticTexture*& acousticTexture) const
|
|
{
|
|
TSoftObjectPtr<class UPhysicalMaterial> physMatPtr(physMaterial);
|
|
auto props = AkGeometryMap.Find(physMatPtr);
|
|
|
|
if (!props)
|
|
return false;
|
|
|
|
TSoftObjectPtr<class UAkAcousticTexture> texturePtr = props->AcousticTexture;
|
|
acousticTexture = texturePtr.LoadSynchronous();
|
|
return true;
|
|
}
|
|
|
|
bool UAkSettings::GetAssociatedOcclusionValue(const UPhysicalMaterial* physMaterial, float& occlusionValue) const
|
|
{
|
|
TSoftObjectPtr<class UPhysicalMaterial> physMatPtr(physMaterial);
|
|
auto props = AkGeometryMap.Find(physMatPtr);
|
|
|
|
if (!props)
|
|
return false;
|
|
|
|
occlusionValue = props->OcclusionValue;
|
|
return true;
|
|
}
|
|
|
|
void UAkSettings::GetAuxBusForDecayValue(float decay, UAkAuxBus*& auxBus)
|
|
{
|
|
TArray<float> decayKeys;
|
|
EnvironmentDecayAuxBusMap.GetKeys(decayKeys);
|
|
decayKeys.Sort();
|
|
int numKeys = decayKeys.Num();
|
|
if (numKeys > 0)
|
|
{
|
|
int i = numKeys - 1;
|
|
if (decay > decayKeys[i])
|
|
{
|
|
auxBus = DefaultReverbAuxBus.LoadSynchronous();
|
|
return;
|
|
}
|
|
while (i > 0 && decay <= decayKeys[i - 1])
|
|
{
|
|
--i;
|
|
}
|
|
TSoftObjectPtr<UAkAuxBus> auxBusPtr = EnvironmentDecayAuxBusMap[decayKeys[i]];
|
|
auxBus = auxBusPtr.LoadSynchronous();
|
|
}
|
|
else
|
|
{
|
|
auxBus = DefaultReverbAuxBus.LoadSynchronous();
|
|
}
|
|
}
|
|
|
|
void UAkSettings::GetAudioInputEvent(UAkAudioEvent*& OutInputEvent)
|
|
{
|
|
OutInputEvent = AudioInputEvent.LoadSynchronous();
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE
|