fixed issue
This commit is contained in:
@ -0,0 +1,146 @@
|
||||
// Copyright (c), Firelight Technologies Pty, Ltd. 2012-2023.
|
||||
|
||||
#include "AssetTypeActions_FMODEvent.h"
|
||||
#include "AssetTypeActions_Base.h"
|
||||
#include "FMODEventEditor.h"
|
||||
#include "FMODEvent.h"
|
||||
#include "FMODUtils.h"
|
||||
#include "FMODStudioModule.h"
|
||||
#include "FMODStudioEditorModule.h"
|
||||
#include "UnrealEd/Public/Editor.h"
|
||||
#include "Framework/MultiBox/MultiBoxBuilder.h"
|
||||
#include "EditorStyle/Public/EditorStyleSet.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "AssetTypeActions"
|
||||
|
||||
FAssetTypeActions_FMODEvent::FAssetTypeActions_FMODEvent()
|
||||
: CurrentPreviewEventInstance(nullptr)
|
||||
{
|
||||
BeginPIEDelegateHandle = FEditorDelegates::BeginPIE.AddRaw(this, &FAssetTypeActions_FMODEvent::HandleBeginPIE);
|
||||
IFMODStudioEditorModule::Get().BanksReloadedEvent().AddRaw(this, &FAssetTypeActions_FMODEvent::HandleBanksReloaded);
|
||||
}
|
||||
|
||||
FAssetTypeActions_FMODEvent::~FAssetTypeActions_FMODEvent()
|
||||
{
|
||||
if (GIsRunning)
|
||||
{
|
||||
FEditorDelegates::BeginPIE.Remove(BeginPIEDelegateHandle);
|
||||
IFMODStudioEditorModule::Get().BanksReloadedEvent().RemoveAll(this);
|
||||
IFMODStudioModule::Get().StopAuditioningInstance();
|
||||
}
|
||||
}
|
||||
|
||||
UClass *FAssetTypeActions_FMODEvent::GetSupportedClass() const
|
||||
{
|
||||
return UFMODEvent::StaticClass();
|
||||
}
|
||||
|
||||
void FAssetTypeActions_FMODEvent::GetActions(const TArray<UObject *> &InObjects, FMenuBuilder &MenuBuilder)
|
||||
{
|
||||
auto Events = GetTypedWeakObjectPtrs<UFMODEvent>(InObjects);
|
||||
|
||||
MenuBuilder.AddMenuEntry(LOCTEXT("FMODEvent_Play", "Play"), LOCTEXT("FMODEvent_PlayTooltip", "Plays the selected FMOD event."),
|
||||
FSlateIcon(FAppStyle::GetAppStyleSetName(), "MediaAsset.AssetActions.Play"),
|
||||
FUIAction(FExecuteAction::CreateSP(this, &FAssetTypeActions_FMODEvent::ExecutePlay, Events),
|
||||
FCanExecuteAction::CreateSP(this, &FAssetTypeActions_FMODEvent::CanExecutePlayCommand, Events)));
|
||||
|
||||
MenuBuilder.AddMenuEntry(LOCTEXT("FMODEvent_Stop", "Stop"), LOCTEXT("FMODEvent_StopTooltip", "Stops the currently playing FMOD event."),
|
||||
FSlateIcon(FAppStyle::GetAppStyleSetName(), "MediaAsset.AssetActions.Stop"),
|
||||
FUIAction(FExecuteAction::CreateSP(this, &FAssetTypeActions_FMODEvent::ExecuteStop, Events), FCanExecuteAction()));
|
||||
}
|
||||
|
||||
void FAssetTypeActions_FMODEvent::OpenAssetEditor(const TArray<UObject *> &InObjects, TSharedPtr<IToolkitHost> EditWithinLevelEditor)
|
||||
{
|
||||
EToolkitMode::Type Mode = EditWithinLevelEditor.IsValid() ? EToolkitMode::WorldCentric : EToolkitMode::Standalone;
|
||||
|
||||
for (auto ObjIt = InObjects.CreateConstIterator(); ObjIt; ++ObjIt)
|
||||
{
|
||||
auto Event = Cast<UFMODEvent>(*ObjIt);
|
||||
if (IsValid(Event))
|
||||
{
|
||||
TSharedRef<FFMODEventEditor> NewFMODEventEditor(new FFMODEventEditor());
|
||||
NewFMODEventEditor->InitFMODEventEditor(Mode, EditWithinLevelEditor, Event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool FAssetTypeActions_FMODEvent::CanExecutePlayCommand(TArray<TWeakObjectPtr<UFMODEvent>> Objects) const
|
||||
{
|
||||
return Objects.Num() == 1;
|
||||
}
|
||||
|
||||
bool FAssetTypeActions_FMODEvent::AssetsActivatedOverride(const TArray<UObject *> &InObjects, EAssetTypeActivationMethod::Type ActivationType)
|
||||
{
|
||||
if (ActivationType == EAssetTypeActivationMethod::Previewed)
|
||||
{
|
||||
for (auto ObjIt = InObjects.CreateConstIterator(); ObjIt; ++ObjIt)
|
||||
{
|
||||
UFMODEvent *Event = Cast<UFMODEvent>(*ObjIt);
|
||||
if (IsValid(Event))
|
||||
{
|
||||
// Only play the first valid event
|
||||
PlayEvent(Event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void FAssetTypeActions_FMODEvent::ExecuteEdit(TArray<TWeakObjectPtr<UFMODEvent>> Objects)
|
||||
{
|
||||
for (auto ObjIt = Objects.CreateConstIterator(); ObjIt; ++ObjIt)
|
||||
{
|
||||
auto Object = (*ObjIt).Get();
|
||||
if (IsValid(Object))
|
||||
{
|
||||
GEditor->GetEditorSubsystem<UAssetEditorSubsystem>()->OpenEditorForAsset(Object);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FAssetTypeActions_FMODEvent::ExecutePlay(TArray<TWeakObjectPtr<UFMODEvent>> Objects)
|
||||
{
|
||||
for (auto ObjIt = Objects.CreateConstIterator(); ObjIt; ++ObjIt)
|
||||
{
|
||||
UFMODEvent *Event = (*ObjIt).Get();
|
||||
if (IsValid(Event))
|
||||
{
|
||||
// Only play the first valid event
|
||||
PlayEvent(Event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FAssetTypeActions_FMODEvent::ExecuteStop(TArray<TWeakObjectPtr<UFMODEvent>> Objects)
|
||||
{
|
||||
IFMODStudioModule::Get().StopAuditioningInstance();
|
||||
}
|
||||
|
||||
void FAssetTypeActions_FMODEvent::PlayEvent(UFMODEvent *Event)
|
||||
{
|
||||
if (IsValid(Event))
|
||||
{
|
||||
CurrentPreviewEventInstance = IFMODStudioModule::Get().CreateAuditioningInstance(Event);
|
||||
if (CurrentPreviewEventInstance != nullptr)
|
||||
{
|
||||
CurrentPreviewEventInstance->start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FAssetTypeActions_FMODEvent::HandleBeginPIE(bool bSimulating)
|
||||
{
|
||||
// Studio module will handle its own auditioning, just clear the handle
|
||||
CurrentPreviewEventInstance = nullptr;
|
||||
}
|
||||
|
||||
void FAssetTypeActions_FMODEvent::HandleBanksReloaded()
|
||||
{
|
||||
// Studio module will handle its own auditioning, just clear the handle
|
||||
CurrentPreviewEventInstance = nullptr;
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
@ -0,0 +1,56 @@
|
||||
// Copyright (c), Firelight Technologies Pty, Ltd. 2012-2023.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "AssetTypeActions_Base.h"
|
||||
|
||||
namespace FMOD
|
||||
{
|
||||
namespace Studio
|
||||
{
|
||||
class EventInstance;
|
||||
}
|
||||
}
|
||||
|
||||
class UFMODEvent;
|
||||
|
||||
class FAssetTypeActions_FMODEvent : public FAssetTypeActions_Base
|
||||
{
|
||||
public:
|
||||
FAssetTypeActions_FMODEvent();
|
||||
~FAssetTypeActions_FMODEvent();
|
||||
|
||||
// IAssetTypeActions Implementation
|
||||
virtual FText GetName() const override { return NSLOCTEXT("AssetTypeActions", "AssetTypeActions_FMODEvent", "FMOD Event"); }
|
||||
virtual FColor GetTypeColor() const override { return FColor(0, 175, 255); }
|
||||
virtual UClass *GetSupportedClass() const override;
|
||||
virtual bool HasActions(const TArray<UObject *> &InObjects) const override { return true; }
|
||||
virtual void GetActions(const TArray<UObject *> &InObjects, FMenuBuilder &MenuBuilder) override;
|
||||
virtual bool AssetsActivatedOverride(const TArray<UObject *> &InObjects, EAssetTypeActivationMethod::Type ActivationType) override;
|
||||
virtual void OpenAssetEditor(
|
||||
const TArray<UObject *> &InObjects, TSharedPtr<class IToolkitHost> EditWithinLevelEditor = TSharedPtr<IToolkitHost>()) override;
|
||||
virtual bool CanFilter() override { return false; }
|
||||
virtual uint32 GetCategories() override { return EAssetTypeCategories::Sounds; }
|
||||
|
||||
private:
|
||||
/** Returns true if only one event is selected to play */
|
||||
bool CanExecutePlayCommand(TArray<TWeakObjectPtr<UFMODEvent>> Objects) const;
|
||||
|
||||
/** Handler for when Edit is selected */
|
||||
void ExecuteEdit(TArray<TWeakObjectPtr<UFMODEvent>> Objects);
|
||||
|
||||
/** Handler for when Play is selected */
|
||||
void ExecutePlay(TArray<TWeakObjectPtr<UFMODEvent>> Objects);
|
||||
|
||||
/** Handler for when Stop is selected */
|
||||
void ExecuteStop(TArray<TWeakObjectPtr<UFMODEvent>> Objects);
|
||||
|
||||
/** Plays the event */
|
||||
void PlayEvent(UFMODEvent *Event);
|
||||
|
||||
void HandleBeginPIE(bool bSimulating);
|
||||
void HandleBanksReloaded();
|
||||
|
||||
FMOD::Studio::EventInstance *CurrentPreviewEventInstance;
|
||||
FDelegateHandle BeginPIEDelegateHandle;
|
||||
};
|
@ -0,0 +1,67 @@
|
||||
// Copyright (c), Firelight Technologies Pty, Ltd. 2012-2023.
|
||||
|
||||
#include "FMODAmbientSoundActorFactory.h"
|
||||
#include "FMODStudioEditorPrivatePCH.h"
|
||||
#include "FMODAmbientSound.h"
|
||||
#include "FMODEvent.h"
|
||||
#include "AssetRegistry/Public/AssetData.h"
|
||||
#include "Editor/EditorEngine.h"
|
||||
|
||||
UFMODAmbientSoundActorFactory::UFMODAmbientSoundActorFactory(const FObjectInitializer &ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
{
|
||||
DisplayName = NSLOCTEXT("FMOD", "FMODAmbientSoundDisplayName", "FMOD Ambient Sound");
|
||||
NewActorClass = AFMODAmbientSound::StaticClass();
|
||||
}
|
||||
|
||||
bool UFMODAmbientSoundActorFactory::CanCreateActorFrom(const FAssetData &AssetData, FText &OutErrorMsg)
|
||||
{
|
||||
//We allow creating AAmbientSounds without an existing sound asset
|
||||
if (UActorFactory::CanCreateActorFrom(AssetData, OutErrorMsg))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (AssetData.IsValid() && !AssetData.GetClass()->IsChildOf(UFMODEvent::StaticClass()))
|
||||
{
|
||||
OutErrorMsg = NSLOCTEXT("FMOD", "CanCreateActorFrom_NoFMODEventAsset", "A valid FMOD Event asset must be specified.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void UFMODAmbientSoundActorFactory::PostSpawnActor(UObject *Asset, AActor *NewActor)
|
||||
{
|
||||
UFMODEvent *Event = Cast<UFMODEvent>(Asset);
|
||||
|
||||
if (Event != NULL)
|
||||
{
|
||||
AFMODAmbientSound *NewSound = CastChecked<AFMODAmbientSound>(NewActor);
|
||||
FActorLabelUtilities::SetActorLabelUnique(NewSound, Event->GetName());
|
||||
NewSound->AudioComponent->Event = Event;
|
||||
}
|
||||
}
|
||||
|
||||
UObject *UFMODAmbientSoundActorFactory::GetAssetFromActorInstance(AActor *Instance)
|
||||
{
|
||||
check(Instance->IsA(NewActorClass));
|
||||
AFMODAmbientSound *SoundActor = CastChecked<AFMODAmbientSound>(Instance);
|
||||
|
||||
check(SoundActor->AudioComponent);
|
||||
return SoundActor->AudioComponent->Event;
|
||||
}
|
||||
|
||||
void UFMODAmbientSoundActorFactory::PostCreateBlueprint(UObject *Asset, AActor *CDO)
|
||||
{
|
||||
if (Asset != NULL && CDO != NULL)
|
||||
{
|
||||
UFMODEvent *Event = Cast<UFMODEvent>(Asset);
|
||||
|
||||
if (Event != NULL)
|
||||
{
|
||||
AFMODAmbientSound *NewSound = CastChecked<AFMODAmbientSound>(CDO);
|
||||
NewSound->AudioComponent->Event = Event;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
// Copyright (c), Firelight Technologies Pty, Ltd. 2012-2023.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ComponentAssetBroker.h"
|
||||
#include "FMODEvent.h"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// FFMODAssetBroker
|
||||
|
||||
class FFMODAssetBroker : public IComponentAssetBroker
|
||||
{
|
||||
public:
|
||||
UClass *GetSupportedAssetClass() override { return UFMODEvent::StaticClass(); }
|
||||
|
||||
virtual bool AssignAssetToComponent(UActorComponent *InComponent, UObject *InAsset) override
|
||||
{
|
||||
if (UFMODAudioComponent *AudioComp = Cast<UFMODAudioComponent>(InComponent))
|
||||
{
|
||||
UFMODEvent *Event = Cast<UFMODEvent>(InAsset);
|
||||
|
||||
if ((Event != NULL) || (InAsset == NULL))
|
||||
{
|
||||
AudioComp->Event = Event;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual UObject *GetAssetFromComponent(UActorComponent *InComponent) override
|
||||
{
|
||||
if (UFMODAudioComponent *AudioComp = Cast<UFMODAudioComponent>(InComponent))
|
||||
{
|
||||
return AudioComp->Event;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
};
|
@ -0,0 +1,655 @@
|
||||
#include "FMODAssetBuilder.h"
|
||||
|
||||
#include "AssetRegistry/AssetRegistryModule.h"
|
||||
#include "FMODAssetLookup.h"
|
||||
#include "FMODAssetTable.h"
|
||||
#include "FMODBank.h"
|
||||
#include "FMODBankLookup.h"
|
||||
#include "FMODBus.h"
|
||||
#include "FMODEvent.h"
|
||||
#include "FMODSettings.h"
|
||||
#include "FMODSnapshot.h"
|
||||
#include "FMODSnapshotReverb.h"
|
||||
#include "FMODPort.h"
|
||||
#include "FMODUtils.h"
|
||||
#include "FMODVCA.h"
|
||||
#include "FileHelpers.h"
|
||||
#include "ObjectTools.h"
|
||||
#include "SourceControlHelpers.h"
|
||||
#include "HAL/FileManager.h"
|
||||
#include "Misc/MessageDialog.h"
|
||||
|
||||
#include "fmod_studio.hpp"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FMODAssetBuilder"
|
||||
|
||||
FFMODAssetBuilder::~FFMODAssetBuilder()
|
||||
{
|
||||
if (StudioSystem)
|
||||
{
|
||||
StudioSystem->release();
|
||||
}
|
||||
}
|
||||
|
||||
void FFMODAssetBuilder::Create()
|
||||
{
|
||||
verifyfmod(FMOD::Studio::System::create(&StudioSystem));
|
||||
FMOD::System *lowLevelSystem = nullptr;
|
||||
verifyfmod(StudioSystem->getCoreSystem(&lowLevelSystem));
|
||||
verifyfmod(lowLevelSystem->setOutput(FMOD_OUTPUTTYPE_NOSOUND_NRT));
|
||||
verifyfmod(StudioSystem->initialize(1, FMOD_STUDIO_INIT_ALLOW_MISSING_PLUGINS | FMOD_STUDIO_INIT_SYNCHRONOUS_UPDATE, FMOD_INIT_MIX_FROM_UPDATE,
|
||||
nullptr));
|
||||
}
|
||||
|
||||
void FFMODAssetBuilder::ProcessBanks()
|
||||
{
|
||||
TArray<UObject*> AssetsToSave;
|
||||
TArray<UObject*> AssetsToDelete;
|
||||
const UFMODSettings& Settings = *GetDefault<UFMODSettings>();
|
||||
FString PackagePath = Settings.GetFullContentPath() / FFMODAssetTable::PrivateDataPath();
|
||||
BuildBankLookup(FFMODAssetTable::BankLookupName(), PackagePath, Settings, AssetsToSave);
|
||||
BuildAssets(Settings, FFMODAssetTable::AssetLookupName(), PackagePath, AssetsToSave, AssetsToDelete);
|
||||
SaveAssets(AssetsToSave);
|
||||
DeleteAssets(AssetsToDelete);
|
||||
}
|
||||
|
||||
FString FFMODAssetBuilder::GetMasterStringsBankPath()
|
||||
{
|
||||
return BankLookup ? BankLookup->MasterStringsBankPath : FString();
|
||||
}
|
||||
|
||||
void FFMODAssetBuilder::BuildAssets(const UFMODSettings& InSettings, const FString &AssetLookupName, const FString &AssetLookupPath,
|
||||
TArray<UObject*>& AssetsToSave, TArray<UObject*>& AssetsToDelete)
|
||||
{
|
||||
if (!BankLookup->MasterStringsBankPath.IsEmpty())
|
||||
{
|
||||
FString StringPath = InSettings.GetFullBankPath() / BankLookup->MasterStringsBankPath;
|
||||
|
||||
UE_LOG(LogFMOD, Log, TEXT("Loading strings bank: %s"), *StringPath);
|
||||
|
||||
FMOD::Studio::Bank *StudioStringBank;
|
||||
FMOD_RESULT StringResult = StudioSystem->loadBankFile(TCHAR_TO_UTF8(*StringPath), FMOD_STUDIO_LOAD_BANK_NORMAL, &StudioStringBank);
|
||||
|
||||
if (StringResult == FMOD_OK)
|
||||
{
|
||||
TArray<char> RawBuffer;
|
||||
RawBuffer.SetNum(256); // Initial capacity
|
||||
|
||||
int Count = 0;
|
||||
verifyfmod(StudioStringBank->getStringCount(&Count));
|
||||
|
||||
// Enumerate all of the names in the strings bank and gather the information required to create the UE4 assets for each object
|
||||
TArray<AssetCreateInfo> AssetCreateInfos;
|
||||
AssetCreateInfos.Reserve(Count);
|
||||
|
||||
for (int StringIdx = 0; StringIdx < Count; ++StringIdx)
|
||||
{
|
||||
FMOD::Studio::ID Guid = { 0 };
|
||||
|
||||
while (true)
|
||||
{
|
||||
int ActualSize = 0;
|
||||
FMOD_RESULT Result = StudioStringBank->getStringInfo(StringIdx, &Guid, RawBuffer.GetData(), RawBuffer.Num(), &ActualSize);
|
||||
|
||||
if (Result == FMOD_ERR_TRUNCATED)
|
||||
{
|
||||
RawBuffer.SetNum(ActualSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
verifyfmod(Result);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
FString AssetName(UTF8_TO_TCHAR(RawBuffer.GetData()));
|
||||
FGuid AssetGuid = FMODUtils::ConvertGuid(Guid);
|
||||
|
||||
if (!AssetName.IsEmpty())
|
||||
{
|
||||
AssetCreateInfo CreateInfo = {};
|
||||
|
||||
if (MakeAssetCreateInfo(AssetGuid, AssetName, &CreateInfo))
|
||||
{
|
||||
AssetCreateInfos.Add(CreateInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
verifyfmod(StudioStringBank->unload());
|
||||
verifyfmod(StudioSystem->update());
|
||||
|
||||
// Load or create asset lookup
|
||||
FString AssetLookupPackageName = AssetLookupPath + AssetLookupName;
|
||||
UPackage *AssetLookupPackage = CreatePackage(*AssetLookupPackageName);
|
||||
AssetLookupPackage->FullyLoad();
|
||||
|
||||
bool bAssetLookupCreated = false;
|
||||
bool bAssetLookupModified = false;
|
||||
|
||||
UDataTable *AssetLookup = FindObject<UDataTable>(AssetLookupPackage, *AssetLookupName, true);
|
||||
|
||||
if (!AssetLookup)
|
||||
{
|
||||
AssetLookup = NewObject<UDataTable>(AssetLookupPackage, *AssetLookupName, RF_Public | RF_Standalone | RF_MarkAsRootSet);
|
||||
AssetLookup->RowStruct = FFMODAssetLookupRow::StaticStruct();
|
||||
bAssetLookupCreated = true;
|
||||
}
|
||||
|
||||
// Create a list of existing assets in the lookup - we'll use this to delete stale assets
|
||||
TMap<FName, FFMODAssetLookupRow> StaleAssets{};
|
||||
AssetLookup->ForeachRow<FFMODAssetLookupRow>(FString(), [&StaleAssets](const FName& Key, const FFMODAssetLookupRow& Value) {
|
||||
StaleAssets.Add(Key, Value);
|
||||
});
|
||||
|
||||
for (const AssetCreateInfo &CreateInfo : AssetCreateInfos)
|
||||
{
|
||||
UFMODAsset *Asset = CreateAsset(CreateInfo, AssetsToSave);
|
||||
|
||||
if (Asset)
|
||||
{
|
||||
UPackage *AssetPackage = Asset->GetPackage();
|
||||
FString AssetPackageName = AssetPackage->GetPathName();
|
||||
FString AssetName = Asset->GetPathName(AssetPackage);
|
||||
FName LookupRowName = FName(*CreateInfo.StudioPath);
|
||||
FFMODAssetLookupRow* LookupRow = AssetLookup->FindRow<FFMODAssetLookupRow>(LookupRowName, FString(), false);
|
||||
|
||||
if (LookupRow)
|
||||
{
|
||||
if (LookupRow->PackageName != AssetPackageName || LookupRow->AssetName != AssetName)
|
||||
{
|
||||
LookupRow->PackageName = AssetPackageName;
|
||||
LookupRow->AssetName = AssetName;
|
||||
bAssetLookupModified = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FFMODAssetLookupRow NewRow{};
|
||||
NewRow.PackageName = AssetPackageName;
|
||||
NewRow.AssetName = AssetName;
|
||||
AssetLookup->AddRow(LookupRowName, NewRow);
|
||||
bAssetLookupModified = true;
|
||||
}
|
||||
|
||||
StaleAssets.Remove(LookupRowName);
|
||||
}
|
||||
}
|
||||
|
||||
// Delete stale assets
|
||||
if (StaleAssets.Num() > 0)
|
||||
{
|
||||
for (auto& Entry : StaleAssets)
|
||||
{
|
||||
UPackage *Package = CreatePackage(*Entry.Value.PackageName);
|
||||
Package->FullyLoad();
|
||||
UFMODAsset *Asset = Package ? FindObject<UFMODAsset>(Package, *Entry.Value.AssetName) : nullptr;
|
||||
|
||||
if (Asset)
|
||||
{
|
||||
UE_LOG(LogFMOD, Log, TEXT("Deleting stale asset %s/%s."), *Entry.Value.PackageName, *Entry.Value.AssetName);
|
||||
AssetsToDelete.Add(Asset);
|
||||
}
|
||||
|
||||
AssetLookup->RemoveRow(Entry.Key);
|
||||
}
|
||||
|
||||
bAssetLookupModified = true;
|
||||
}
|
||||
|
||||
if (bAssetLookupCreated || bAssetLookupModified)
|
||||
{
|
||||
AssetsToSave.Add(AssetLookup);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogFMOD, Warning, TEXT("Failed to load strings bank: %s"), *StringPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const FString BankExtensions[] = { TEXT(".assets"), TEXT(".streams"), TEXT(".bank")};
|
||||
|
||||
void FFMODAssetBuilder::BuildBankLookup(const FString &AssetName, const FString &PackagePath, const UFMODSettings &InSettings,
|
||||
TArray<UObject*>& AssetsToSave)
|
||||
{
|
||||
FString PackageName = PackagePath + AssetName;
|
||||
UPackage *Package = CreatePackage(*PackageName);
|
||||
Package->FullyLoad();
|
||||
|
||||
bool bCreated = false;
|
||||
bool bModified = false;
|
||||
|
||||
BankLookup = FindObject<UFMODBankLookup>(Package, *AssetName, true);
|
||||
|
||||
if (!BankLookup)
|
||||
{
|
||||
BankLookup = NewObject<UFMODBankLookup>(Package, *AssetName, RF_Public | RF_Standalone | RF_MarkAsRootSet);
|
||||
BankLookup->DataTable = NewObject<UDataTable>(BankLookup, "DataTable", RF_NoFlags);
|
||||
BankLookup->DataTable->RowStruct = FFMODLocalizedBankTable::StaticStruct();
|
||||
bCreated = true;
|
||||
}
|
||||
|
||||
// Get a list of all bank GUIDs already in the lookup - this will be used to remove stale GUIDs after processing
|
||||
// the current banks on disk.
|
||||
TArray<FName> StaleBanks(BankLookup->DataTable->GetRowNames());
|
||||
|
||||
// Process all banks on disk
|
||||
TArray<FString> BankPaths;
|
||||
FString SearchDir = InSettings.GetFullBankPath();
|
||||
IFileManager::Get().FindFilesRecursive(BankPaths, *SearchDir, TEXT("*.bank"), true, false, false);
|
||||
|
||||
if (BankPaths.Num() <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TMap<FString, FString> BankGuids;
|
||||
|
||||
for (FString BankPath : BankPaths)
|
||||
{
|
||||
FMOD::Studio::Bank* Bank;
|
||||
FMOD_RESULT result = StudioSystem->loadBankFile(TCHAR_TO_UTF8(*BankPath), FMOD_STUDIO_LOAD_BANK_NORMAL, &Bank);
|
||||
FMOD_GUID BankID;
|
||||
|
||||
if (result == FMOD_OK)
|
||||
{
|
||||
result = Bank->getID(&BankID);
|
||||
Bank->unload();
|
||||
}
|
||||
|
||||
if (result == FMOD_OK)
|
||||
{
|
||||
FString GUID = FMODUtils::ConvertGuid(BankID).ToString(EGuidFormats::DigitsWithHyphensInBraces);
|
||||
|
||||
FString* otherBankPath = BankGuids.Find(GUID);
|
||||
if (otherBankPath != nullptr)
|
||||
{
|
||||
bool foundLocale = false;
|
||||
for (const FFMODProjectLocale& Locale : InSettings.Locales)
|
||||
{
|
||||
if (Locale.bDefault && BankPath.EndsWith(FString("_") + Locale.LocaleCode + FString(".bank")))
|
||||
{
|
||||
foundLocale = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!foundLocale)
|
||||
{
|
||||
UE_LOG(LogFMOD, Warning, TEXT("Ignoring bank %s as another bank with the same GUID is already being used.\n"
|
||||
"Bank %s does not match any locales in the FMOD Studio plugin settings."), *BankPath, *BankPath);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
BankGuids.Add(GUID, BankPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogFMOD, Error, TEXT("Failed to add bank %s to lookup."), *BankPath);
|
||||
}
|
||||
}
|
||||
|
||||
for (TPair<FString,FString> GUIDPath : BankGuids)
|
||||
{
|
||||
FName OuterRowName(*GUIDPath.Key);
|
||||
FFMODLocalizedBankTable* Row = BankLookup->DataTable->FindRow<FFMODLocalizedBankTable>(OuterRowName, nullptr, false);
|
||||
|
||||
if (Row)
|
||||
{
|
||||
StaleBanks.RemoveSingle(OuterRowName);
|
||||
}
|
||||
else
|
||||
{
|
||||
FFMODLocalizedBankTable NewRow{};
|
||||
NewRow.Banks = NewObject<UDataTable>(BankLookup->DataTable, *GUIDPath.Key, RF_NoFlags);
|
||||
NewRow.Banks->RowStruct = FFMODLocalizedBankRow::StaticStruct();
|
||||
BankLookup->DataTable->AddRow(OuterRowName, NewRow);
|
||||
Row = BankLookup->DataTable->FindRow<FFMODLocalizedBankTable>(OuterRowName, nullptr, false);
|
||||
bModified = true;
|
||||
}
|
||||
|
||||
FString CurFilename = FPaths::GetCleanFilename(GUIDPath.Value);
|
||||
FString FilenamePart = CurFilename;
|
||||
|
||||
for (const FString& extension : BankExtensions)
|
||||
{
|
||||
FilenamePart.ReplaceInline(*extension, TEXT(""));
|
||||
}
|
||||
|
||||
FString InnerRowName("<NON-LOCALIZED>");
|
||||
for (const FFMODProjectLocale& Locale : InSettings.Locales)
|
||||
{
|
||||
if (FilenamePart.EndsWith(FString("_") + Locale.LocaleCode))
|
||||
{
|
||||
InnerRowName = Locale.LocaleCode;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
FFMODLocalizedBankRow* InnerRow = Row->Banks->FindRow<FFMODLocalizedBankRow>(FName(*InnerRowName), nullptr, false);
|
||||
FString RelativeBankPath = GUIDPath.Value.RightChop(InSettings.GetFullBankPath().Len() + 1);
|
||||
|
||||
if (InnerRow)
|
||||
{
|
||||
if (InnerRow->Path != RelativeBankPath)
|
||||
{
|
||||
InnerRow->Path = RelativeBankPath;
|
||||
bModified = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FFMODLocalizedBankRow NewRow{};
|
||||
NewRow.Path = RelativeBankPath;
|
||||
Row->Banks->AddRow(FName(*InnerRowName), NewRow);
|
||||
bModified = true;
|
||||
}
|
||||
|
||||
if (CurFilename == InSettings.GetMasterBankFilename() && BankLookup->MasterBankPath != RelativeBankPath)
|
||||
{
|
||||
BankLookup->MasterBankPath = RelativeBankPath;
|
||||
bModified = true;
|
||||
}
|
||||
else if (CurFilename == InSettings.GetMasterStringsBankFilename() && BankLookup->MasterStringsBankPath != RelativeBankPath)
|
||||
{
|
||||
BankLookup->MasterStringsBankPath = RelativeBankPath;
|
||||
bModified = true;
|
||||
}
|
||||
else if (CurFilename == InSettings.GetMasterAssetsBankFilename() && BankLookup->MasterAssetsBankPath != RelativeBankPath)
|
||||
{
|
||||
BankLookup->MasterAssetsBankPath = RelativeBankPath;
|
||||
bModified = true;
|
||||
}
|
||||
}
|
||||
|
||||
StudioSystem->flushCommands();
|
||||
|
||||
// Remove stale banks from lookup
|
||||
if (StaleBanks.Num() > 0)
|
||||
{
|
||||
for (const auto& RowName : StaleBanks)
|
||||
{
|
||||
BankLookup->DataTable->RemoveRow(RowName);
|
||||
}
|
||||
|
||||
bModified = true;
|
||||
}
|
||||
|
||||
if (bCreated)
|
||||
{
|
||||
FAssetRegistryModule::AssetCreated(BankLookup);
|
||||
}
|
||||
|
||||
if (bCreated || bModified)
|
||||
{
|
||||
AssetsToSave.Add(BankLookup);
|
||||
}
|
||||
}
|
||||
|
||||
FString FFMODAssetBuilder::GetAssetClassName(UClass* AssetClass)
|
||||
{
|
||||
FString ClassName("");
|
||||
|
||||
if (AssetClass == UFMODEvent::StaticClass())
|
||||
{
|
||||
ClassName = TEXT("Events");
|
||||
}
|
||||
else if (AssetClass == UFMODSnapshot::StaticClass())
|
||||
{
|
||||
ClassName = TEXT("Snapshots");
|
||||
}
|
||||
else if (AssetClass == UFMODBank::StaticClass())
|
||||
{
|
||||
ClassName = TEXT("Banks");
|
||||
}
|
||||
else if (AssetClass == UFMODBus::StaticClass())
|
||||
{
|
||||
ClassName = TEXT("Buses");
|
||||
}
|
||||
else if (AssetClass == UFMODVCA::StaticClass())
|
||||
{
|
||||
ClassName = TEXT("VCAs");
|
||||
}
|
||||
else if (AssetClass == UFMODSnapshotReverb::StaticClass())
|
||||
{
|
||||
ClassName = TEXT("Reverbs");
|
||||
}
|
||||
else if (AssetClass == UFMODPort::StaticClass())
|
||||
{
|
||||
ClassName = TEXT("Ports");
|
||||
}
|
||||
return ClassName;
|
||||
}
|
||||
|
||||
bool FFMODAssetBuilder::MakeAssetCreateInfo(const FGuid &AssetGuid, const FString &StudioPath, AssetCreateInfo *CreateInfo)
|
||||
{
|
||||
CreateInfo->StudioPath = StudioPath;
|
||||
CreateInfo->Guid = AssetGuid;
|
||||
|
||||
FString AssetType;
|
||||
FString AssetPath;
|
||||
StudioPath.Split(TEXT(":"), &AssetType, &AssetPath, ESearchCase::CaseSensitive, ESearchDir::FromStart);
|
||||
|
||||
if (AssetType.Equals(TEXT("event")))
|
||||
{
|
||||
CreateInfo->Class = UFMODEvent::StaticClass();
|
||||
}
|
||||
else if (AssetType.Equals(TEXT("snapshot")))
|
||||
{
|
||||
CreateInfo->Class = UFMODSnapshot::StaticClass();
|
||||
}
|
||||
else if (AssetType.Equals(TEXT("bank")))
|
||||
{
|
||||
CreateInfo->Class = UFMODBank::StaticClass();
|
||||
}
|
||||
else if (AssetType.Equals(TEXT("bus")))
|
||||
{
|
||||
CreateInfo->Class = UFMODBus::StaticClass();
|
||||
}
|
||||
else if (AssetType.Equals(TEXT("vca")))
|
||||
{
|
||||
CreateInfo->Class = UFMODVCA::StaticClass();
|
||||
}
|
||||
else if (AssetType.Equals(TEXT("port")))
|
||||
{
|
||||
CreateInfo->Class = UFMODPort::StaticClass();
|
||||
}
|
||||
else if (AssetType.Equals(TEXT("parameter")))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogFMOD, Warning, TEXT("Unknown asset type: %s"), *AssetType);
|
||||
CreateInfo->Class = UFMODAsset::StaticClass();
|
||||
}
|
||||
|
||||
AssetPath.Split(TEXT("/"), &(CreateInfo->Path), &(CreateInfo->AssetName), ESearchCase::CaseSensitive, ESearchDir::FromEnd);
|
||||
|
||||
if (CreateInfo->AssetName.IsEmpty() || CreateInfo->AssetName.Contains(TEXT(".strings")))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
UFMODAsset *FFMODAssetBuilder::CreateAsset(const AssetCreateInfo& CreateInfo, TArray<UObject*>& AssetsToSave)
|
||||
{
|
||||
FString SanitizedAssetName;
|
||||
FText OutReason;
|
||||
|
||||
if (FName::IsValidXName(CreateInfo.AssetName, INVALID_OBJECTNAME_CHARACTERS, &OutReason))
|
||||
{
|
||||
SanitizedAssetName = CreateInfo.AssetName;
|
||||
}
|
||||
else
|
||||
{
|
||||
SanitizedAssetName = ObjectTools::SanitizeObjectName(CreateInfo.AssetName);
|
||||
UE_LOG(LogFMOD, Log, TEXT("'%s' cannot be used as a UE4 asset name. %s. Using '%s' instead."), *CreateInfo.AssetName,
|
||||
*OutReason.ToString(), *SanitizedAssetName);
|
||||
}
|
||||
|
||||
const UFMODSettings &Settings = *GetDefault<UFMODSettings>();
|
||||
FString Folder = Settings.GetFullContentPath() / GetAssetClassName(CreateInfo.Class) + CreateInfo.Path;
|
||||
FString PackagePath = FString::Printf(TEXT("%s/%s"), *Folder, *SanitizedAssetName);
|
||||
FString SanitizedPackagePath;
|
||||
|
||||
if (FName::IsValidXName(PackagePath, INVALID_LONGPACKAGE_CHARACTERS, &OutReason))
|
||||
{
|
||||
SanitizedPackagePath = PackagePath;
|
||||
}
|
||||
else
|
||||
{
|
||||
SanitizedPackagePath = ObjectTools::SanitizeInvalidChars(PackagePath, INVALID_OBJECTPATH_CHARACTERS);
|
||||
UE_LOG(LogFMOD, Log, TEXT("'%s' cannot be used as a UE4 asset path. %s. Using '%s' instead."), *PackagePath, *OutReason.ToString(),
|
||||
*SanitizedPackagePath);
|
||||
}
|
||||
|
||||
UPackage *Package = CreatePackage(*SanitizedPackagePath);
|
||||
Package->FullyLoad();
|
||||
|
||||
UFMODAsset *Asset = FindObject<UFMODAsset>(Package, *SanitizedAssetName);
|
||||
bool bCreated = false;
|
||||
bool bModified = false;
|
||||
|
||||
if (Asset && Asset->GetClass() == CreateInfo.Class)
|
||||
{
|
||||
if (Asset->AssetGuid != CreateInfo.Guid)
|
||||
{
|
||||
UE_LOG(LogFMOD, Log, TEXT("Updating asset: %s"), *SanitizedPackagePath);
|
||||
Asset->AssetGuid = CreateInfo.Guid;
|
||||
bModified = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogFMOD, Log, TEXT("Adding asset: %s"), *SanitizedPackagePath);
|
||||
Asset = NewObject<UFMODAsset>(Package, CreateInfo.Class, FName(*SanitizedAssetName), RF_Standalone | RF_Public | RF_MarkAsRootSet);
|
||||
Asset->AssetGuid = CreateInfo.Guid;
|
||||
bCreated = true;
|
||||
}
|
||||
|
||||
if (bCreated)
|
||||
{
|
||||
FAssetRegistryModule::AssetCreated(Asset);
|
||||
}
|
||||
|
||||
if (bCreated || bModified)
|
||||
{
|
||||
AssetsToSave.Add(Asset);
|
||||
}
|
||||
|
||||
if (!IsValid(Asset))
|
||||
{
|
||||
UE_LOG(LogFMOD, Error, TEXT("Failed to construct asset: %s"), *SanitizedPackagePath);
|
||||
}
|
||||
|
||||
if (CreateInfo.Class == UFMODSnapshot::StaticClass())
|
||||
{
|
||||
FString OldPrefix = Settings.ContentBrowserPrefix + GetAssetClassName(Asset->GetClass());
|
||||
FString NewPrefix = Settings.ContentBrowserPrefix + GetAssetClassName(UFMODSnapshotReverb::StaticClass());
|
||||
UObject *Outer = Asset->GetOuter() ? Asset->GetOuter() : Asset;
|
||||
FString ReverbPackagePath = Outer->GetPathName().Replace(*OldPrefix, *NewPrefix);
|
||||
|
||||
UPackage *ReverbPackage = CreatePackage(*ReverbPackagePath);
|
||||
ReverbPackage->FullyLoad();
|
||||
|
||||
UFMODSnapshotReverb *AssetReverb = FindObject<UFMODSnapshotReverb>(ReverbPackage, *SanitizedAssetName, true);
|
||||
bCreated = false;
|
||||
bModified = false;
|
||||
|
||||
if (AssetReverb)
|
||||
{
|
||||
if (AssetReverb->AssetGuid != CreateInfo.Guid)
|
||||
{
|
||||
UE_LOG(LogFMOD, Log, TEXT("Updating snapshot reverb asset: %s"), *ReverbPackagePath);
|
||||
AssetReverb->AssetGuid = CreateInfo.Guid;
|
||||
bModified = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogFMOD, Log, TEXT("Constructing snapshot reverb asset: %s"), *ReverbPackagePath);
|
||||
AssetReverb = NewObject<UFMODSnapshotReverb>(ReverbPackage, UFMODSnapshotReverb::StaticClass(), FName(*SanitizedAssetName),
|
||||
RF_Standalone | RF_Public | RF_MarkAsRootSet);
|
||||
AssetReverb->AssetGuid = CreateInfo.Guid;
|
||||
bCreated = true;
|
||||
}
|
||||
|
||||
if (bCreated)
|
||||
{
|
||||
FAssetRegistryModule::AssetCreated(AssetReverb);
|
||||
}
|
||||
|
||||
if (bCreated || bModified)
|
||||
{
|
||||
AssetsToSave.Add(AssetReverb);
|
||||
}
|
||||
|
||||
if (!IsValid(AssetReverb))
|
||||
{
|
||||
UE_LOG(LogFMOD, Error, TEXT("Failed to construct snapshot reverb asset: %s"), *ReverbPackagePath);
|
||||
}
|
||||
}
|
||||
|
||||
return Asset;
|
||||
}
|
||||
|
||||
void FFMODAssetBuilder::SaveAssets(TArray<UObject*>& AssetsToSave)
|
||||
{
|
||||
if (AssetsToSave.Num() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TArray<UPackage *> PackagesToSave;
|
||||
|
||||
for (auto& Asset : AssetsToSave)
|
||||
{
|
||||
UPackage* Package = Asset->GetPackage();
|
||||
|
||||
if (Package)
|
||||
{
|
||||
Package->MarkPackageDirty();
|
||||
PackagesToSave.Add(Package);
|
||||
}
|
||||
}
|
||||
|
||||
UEditorLoadingAndSavingUtils::SavePackages(PackagesToSave, true);
|
||||
}
|
||||
|
||||
void FFMODAssetBuilder::DeleteAssets(TArray<UObject*>& AssetsToDelete)
|
||||
{
|
||||
if (AssetsToDelete.Num() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TArray<UObject*> ObjectsToDelete;
|
||||
|
||||
for (auto& Asset : AssetsToDelete)
|
||||
{
|
||||
ObjectsToDelete.Add(Asset);
|
||||
|
||||
if (Asset->GetClass() == UFMODSnapshot::StaticClass())
|
||||
{
|
||||
// Also delete the reverb asset
|
||||
const UFMODSettings &Settings = *GetDefault<UFMODSettings>();
|
||||
FString OldPrefix = Settings.ContentBrowserPrefix + GetAssetClassName(Asset->GetClass());
|
||||
FString NewPrefix = Settings.ContentBrowserPrefix + GetAssetClassName(UFMODSnapshotReverb::StaticClass());
|
||||
FString ReverbName = Asset->GetPathName().Replace(*OldPrefix, *NewPrefix);
|
||||
UObject *Reverb = StaticFindObject(UFMODSnapshotReverb::StaticClass(), nullptr, *ReverbName);
|
||||
|
||||
if (Reverb)
|
||||
{
|
||||
ObjectsToDelete.Add(Reverb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use ObjectTools to delete assets - ObjectTools::DeleteObjects handles confirmation, source control, and making read only files writables
|
||||
ObjectTools::DeleteObjects(ObjectsToDelete, !IsRunningCommandlet());
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
@ -0,0 +1,115 @@
|
||||
// Copyright (c), Firelight Technologies Pty, Ltd. 2012-2023.
|
||||
|
||||
#include "FMODAudioComponentDetails.h"
|
||||
#include "Subsystems/AssetEditorSubsystem.h"
|
||||
#include "FMODAmbientSound.h"
|
||||
#include "FMODStudioModule.h"
|
||||
#include "FMODEvent.h"
|
||||
#include "fmod_studio.hpp"
|
||||
#include "UnrealEd/Public/Editor.h"
|
||||
#include "Widgets/Input/SButton.h"
|
||||
#include "PropertyEditor/Public/DetailLayoutBuilder.h"
|
||||
#include "PropertyEditor/Public/DetailCategoryBuilder.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FMODStudio"
|
||||
|
||||
TSharedRef<IDetailCustomization> FFMODAudioComponentDetails::MakeInstance()
|
||||
{
|
||||
return MakeShareable(new FFMODAudioComponentDetails);
|
||||
}
|
||||
|
||||
void FFMODAudioComponentDetails::CustomizeDetails(IDetailLayoutBuilder &DetailBuilder)
|
||||
{
|
||||
const TArray<TWeakObjectPtr<UObject>> &SelectedObjects = DetailBuilder.GetSelectedObjects();
|
||||
|
||||
for (int32 ObjectIndex = 0; !AudioComponent.IsValid() && ObjectIndex < SelectedObjects.Num(); ++ObjectIndex)
|
||||
{
|
||||
const TWeakObjectPtr<UObject> &CurrentObject = SelectedObjects[ObjectIndex];
|
||||
if (CurrentObject.Get()->GetClass()->IsChildOf(UFMODAudioComponent::StaticClass()))
|
||||
{
|
||||
AudioComponent = Cast<UFMODAudioComponent>(CurrentObject.Get());
|
||||
}
|
||||
else
|
||||
{
|
||||
AudioComponent = Cast<AActor>(CurrentObject.Get())->FindComponentByClass<UFMODAudioComponent>();
|
||||
}
|
||||
}
|
||||
|
||||
DetailBuilder.EditCategory(TEXT("FMODAudio"))
|
||||
.AddCustomRow(
|
||||
FText::GetEmpty())[SNew(SVerticalBox) +
|
||||
SVerticalBox::Slot()
|
||||
.Padding(0, 2.0f, 0, 0)
|
||||
.FillHeight(1.0f)
|
||||
.VAlign(
|
||||
VAlign_Center)[SNew(SHorizontalBox) +
|
||||
SHorizontalBox::Slot()
|
||||
.AutoWidth()
|
||||
.Padding(2.0f, 0.0f)
|
||||
.VAlign(VAlign_Center)
|
||||
.HAlign(HAlign_Left)[SNew(SButton)
|
||||
.VAlign(VAlign_Center)
|
||||
.OnClicked(this, &FFMODAudioComponentDetails::OnEditSoundClicked)
|
||||
.Text(LOCTEXT("View Details", "Details"))] +
|
||||
SHorizontalBox::Slot()
|
||||
.AutoWidth()
|
||||
.Padding(2.0f, 0.0f)
|
||||
.VAlign(VAlign_Center)
|
||||
.HAlign(HAlign_Left)[SNew(SButton)
|
||||
.VAlign(VAlign_Center)
|
||||
.OnClicked(this, &FFMODAudioComponentDetails::OnPlaySoundClicked)
|
||||
.Text(LOCTEXT("Play FMOD Event", "Play"))] +
|
||||
SHorizontalBox::Slot()
|
||||
.AutoWidth()
|
||||
.Padding(2.0f, 0.0f)
|
||||
.VAlign(VAlign_Center)
|
||||
.HAlign(HAlign_Left)[SNew(SButton)
|
||||
.VAlign(VAlign_Center)
|
||||
.OnClicked(this, &FFMODAudioComponentDetails::OnStopSoundClicked)
|
||||
.Text(LOCTEXT("Stop FMOD Event", "Stop"))]]];
|
||||
}
|
||||
|
||||
FReply FFMODAudioComponentDetails::OnEditSoundClicked()
|
||||
{
|
||||
if (AudioComponent.IsValid())
|
||||
{
|
||||
UFMODEvent *Event = AudioComponent.Get()->Event;
|
||||
if (Event)
|
||||
{
|
||||
GEditor->GetEditorSubsystem<UAssetEditorSubsystem>()->OpenEditorForAsset(Event);
|
||||
}
|
||||
}
|
||||
|
||||
return FReply::Handled();
|
||||
}
|
||||
|
||||
FReply FFMODAudioComponentDetails::OnPlaySoundClicked()
|
||||
{
|
||||
if (AudioComponent.IsValid())
|
||||
{
|
||||
UFMODEvent *Event = AudioComponent.Get()->Event;
|
||||
if (IsValid(Event))
|
||||
{
|
||||
FMOD::Studio::EventInstance *Instance = IFMODStudioModule::Get().CreateAuditioningInstance(Event);
|
||||
if (Instance)
|
||||
{
|
||||
for (auto param : AudioComponent->ParameterCache)
|
||||
{
|
||||
Instance->setParameterByName(TCHAR_TO_UTF8(*param.Key.ToString()), param.Value);
|
||||
}
|
||||
Instance->start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return FReply::Handled();
|
||||
}
|
||||
|
||||
FReply FFMODAudioComponentDetails::OnStopSoundClicked()
|
||||
{
|
||||
IFMODStudioModule::Get().StopAuditioningInstance();
|
||||
|
||||
return FReply::Handled();
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
@ -0,0 +1,24 @@
|
||||
// Copyright (c), Firelight Technologies Pty, Ltd. 2012-2023.
|
||||
|
||||
#pragma once
|
||||
|
||||
//#include "PropertyEditing.h"
|
||||
#include "PropertyCustomizationHelpers.h"
|
||||
#include "PropertyEditor/Public/IDetailCustomization.h"
|
||||
|
||||
class FFMODAudioComponentDetails : public IDetailCustomization
|
||||
{
|
||||
public:
|
||||
/** Makes a new instance of this detail layout class for a specific detail view requesting it */
|
||||
static TSharedRef<IDetailCustomization> MakeInstance();
|
||||
|
||||
private:
|
||||
/** IDetailCustomization interface */
|
||||
virtual void CustomizeDetails(IDetailLayoutBuilder &DetailBuilder) override;
|
||||
|
||||
FReply OnEditSoundClicked();
|
||||
FReply OnPlaySoundClicked();
|
||||
FReply OnStopSoundClicked();
|
||||
|
||||
TWeakObjectPtr<class UFMODAudioComponent> AudioComponent;
|
||||
};
|
@ -0,0 +1,54 @@
|
||||
// Copyright (c), Firelight Technologies Pty, Ltd. 2012-2023.
|
||||
|
||||
#include "FMODAudioComponentVisualizer.h"
|
||||
#include "FMODAudioComponent.h"
|
||||
#include "FMODUtils.h"
|
||||
#include "FMODEvent.h"
|
||||
#include "fmod_studio.hpp"
|
||||
#include "Engine/Public/SceneView.h"
|
||||
#include "Engine/Public/SceneManagement.h"
|
||||
|
||||
void FFMODAudioComponentVisualizer::DrawVisualization(const UActorComponent *Component, const FSceneView *View, FPrimitiveDrawInterface *PDI)
|
||||
{
|
||||
if (View->Family->EngineShowFlags.AudioRadius)
|
||||
{
|
||||
const UFMODAudioComponent *AudioComp = Cast<const UFMODAudioComponent>(Component);
|
||||
if (IsValid(AudioComp) && AudioComp->Event)
|
||||
{
|
||||
FMOD::Studio::EventDescription *EventDesc =
|
||||
IFMODStudioModule::Get().GetEventDescription(AudioComp->Event, EFMODSystemContext::Auditioning);
|
||||
if (EventDesc != nullptr)
|
||||
{
|
||||
bool bIs3D = false;
|
||||
EventDesc->is3D(&bIs3D);
|
||||
if (bIs3D)
|
||||
{
|
||||
const FColor AudioOuterRadiusColor(255, 153, 0);
|
||||
const FColor AudioInnerRadiusColor(216, 130, 0);
|
||||
|
||||
const FTransform &Transform = AudioComp->GetComponentTransform();
|
||||
|
||||
float MinDistance = 0.0f;
|
||||
float MaxDistance = 0.0f;
|
||||
if (AudioComp->AttenuationDetails.bOverrideAttenuation)
|
||||
{
|
||||
MinDistance = AudioComp->AttenuationDetails.MinimumDistance;
|
||||
MaxDistance = AudioComp->AttenuationDetails.MaximumDistance;
|
||||
}
|
||||
else
|
||||
{
|
||||
EventDesc->getMinMaxDistance(&MinDistance, &MaxDistance);
|
||||
}
|
||||
MinDistance = FMODUtils::DistanceToUEScale(MinDistance);
|
||||
MaxDistance = FMODUtils::DistanceToUEScale(MaxDistance);
|
||||
|
||||
DrawWireSphereAutoSides(PDI, Transform.GetTranslation(), AudioOuterRadiusColor, MinDistance, SDPG_World);
|
||||
if (MaxDistance != MinDistance)
|
||||
{
|
||||
DrawWireSphereAutoSides(PDI, Transform.GetTranslation(), AudioInnerRadiusColor, MaxDistance, SDPG_World);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
// Copyright (c), Firelight Technologies Pty, Ltd. 2012-2023.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ComponentVisualizer.h"
|
||||
|
||||
class FFMODAudioComponentVisualizer : public FComponentVisualizer
|
||||
{
|
||||
public:
|
||||
// Begin FComponentVisualizer interface
|
||||
virtual void DrawVisualization(const UActorComponent *Component, const FSceneView *View, FPrimitiveDrawInterface *PDI) override;
|
||||
// End FComponentVisualizer interface
|
||||
};
|
@ -0,0 +1,96 @@
|
||||
// Copyright (c), Firelight Technologies Pty, Ltd. 2012-2023.
|
||||
|
||||
#include "FMODBankUpdateNotifier.h"
|
||||
#include "FMODSettings.h"
|
||||
#include "HAL/FileManager.h"
|
||||
|
||||
#include "FMODStudioEditorPrivatePCH.h"
|
||||
|
||||
FFMODBankUpdateNotifier::FFMODBankUpdateNotifier()
|
||||
: bUpdateEnabled(true)
|
||||
, NextRefreshTime(FDateTime::MinValue())
|
||||
, FileTime(FDateTime::MinValue())
|
||||
, Countdown(0.0f)
|
||||
{
|
||||
}
|
||||
|
||||
void FFMODBankUpdateNotifier::SetFilePath(const FString &InPath)
|
||||
{
|
||||
FilePath = InPath;
|
||||
NextRefreshTime = FDateTime::MinValue();
|
||||
FileTime = MostRecentFileTime();
|
||||
}
|
||||
|
||||
void FFMODBankUpdateNotifier::Update(float DeltaTime)
|
||||
{
|
||||
if (bUpdateEnabled)
|
||||
{
|
||||
FDateTime CurTime = FDateTime::UtcNow();
|
||||
|
||||
if (CurTime >= NextRefreshTime)
|
||||
{
|
||||
Refresh();
|
||||
NextRefreshTime = CurTime + FTimespan(0, 0, 1);
|
||||
}
|
||||
|
||||
if (Countdown > 0.0f)
|
||||
{
|
||||
Countdown -= DeltaTime;
|
||||
|
||||
if (Countdown <= 0.0f)
|
||||
{
|
||||
BanksUpdatedEvent.Broadcast();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FFMODBankUpdateNotifier::EnableUpdate(bool bEnable)
|
||||
{
|
||||
bUpdateEnabled = bEnable;
|
||||
|
||||
if (bEnable)
|
||||
{
|
||||
// Refreshing right after update is enabled is not desirable
|
||||
NextRefreshTime = FDateTime::UtcNow() + FTimespan(0, 0, 1);
|
||||
|
||||
// Cancel any pending countdown
|
||||
Countdown = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
void FFMODBankUpdateNotifier::Refresh()
|
||||
{
|
||||
if (!FilePath.IsEmpty())
|
||||
{
|
||||
FDateTime NewFileTime = MostRecentFileTime();
|
||||
|
||||
if (NewFileTime != FileTime)
|
||||
{
|
||||
const UFMODSettings &Settings = *GetDefault<UFMODSettings>();
|
||||
Countdown = (float)Settings.ReloadBanksDelay;
|
||||
FileTime = NewFileTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FDateTime FFMODBankUpdateNotifier::MostRecentFileTime()
|
||||
{
|
||||
// Get the most recent modified timestamp of all the bank files in the directory we are watching.
|
||||
FDateTime MostRecent = FDateTime::MinValue();
|
||||
|
||||
TArray<FString> BankPaths;
|
||||
IFileManager::Get().FindFilesRecursive(BankPaths, *FilePath, TEXT("*.bank"), true, false, false);
|
||||
|
||||
for (const auto& Path : BankPaths)
|
||||
{
|
||||
FDateTime ModifiedTime = IFileManager::Get().GetTimeStamp(*Path);
|
||||
|
||||
if (ModifiedTime > MostRecent)
|
||||
{
|
||||
MostRecent = ModifiedTime;
|
||||
}
|
||||
}
|
||||
|
||||
return MostRecent;
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
// Copyright (c), Firelight Technologies Pty, Ltd. 2012-2023.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Containers/UnrealString.h"
|
||||
#include "Misc/DateTime.h"
|
||||
#include "Delegates/Delegate.h"
|
||||
|
||||
class FFMODBankUpdateNotifier
|
||||
{
|
||||
public:
|
||||
FFMODBankUpdateNotifier();
|
||||
|
||||
void SetFilePath(const FString &InPath);
|
||||
void Update(float DeltaTime);
|
||||
|
||||
void EnableUpdate(bool bEnable);
|
||||
|
||||
FSimpleMulticastDelegate BanksUpdatedEvent;
|
||||
|
||||
private:
|
||||
void Refresh();
|
||||
FDateTime MostRecentFileTime();
|
||||
|
||||
bool bUpdateEnabled;
|
||||
FString FilePath;
|
||||
FDateTime NextRefreshTime;
|
||||
FDateTime FileTime;
|
||||
float Countdown;
|
||||
};
|
@ -0,0 +1,189 @@
|
||||
// Copyright (c), Firelight Technologies Pty, Ltd. 2012-2023.
|
||||
|
||||
#include "FMODEventEditor.h"
|
||||
#include "FMODEvent.h"
|
||||
#include "FMODStudioEditorModule.h"
|
||||
#include "FMODStudioModule.h"
|
||||
#include "FMODUtils.h"
|
||||
#include "SFMODEventEditorPanel.h"
|
||||
#include "Widgets/Docking/SDockTab.h"
|
||||
#include "fmod_studio.hpp"
|
||||
#include "UnrealEd/Public/Editor.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FMODEventEditor"
|
||||
|
||||
DEFINE_LOG_CATEGORY_STATIC(LogFMODEventEditor, Log, All);
|
||||
|
||||
const FName FFMODEventEditor::EventEditorTabId(TEXT("FFMODEventEditor_EventView"));
|
||||
const FName FFMODEventEditor::FMODEventEditorAppIdentifier(TEXT("FMODEventEditorApp"));
|
||||
|
||||
void FFMODEventEditor::RegisterTabSpawners(const TSharedRef<class FTabManager> &NewTabManager)
|
||||
{
|
||||
WorkspaceMenuCategory = NewTabManager->AddLocalWorkspaceMenuCategory(LOCTEXT("WorkspaceMenu_FMODEventEditor", "FMOD Event Editor"));
|
||||
auto WorkspaceMenuCategoryRef = WorkspaceMenuCategory.ToSharedRef();
|
||||
|
||||
FAssetEditorToolkit::RegisterTabSpawners(NewTabManager);
|
||||
|
||||
NewTabManager->RegisterTabSpawner(EventEditorTabId, FOnSpawnTab::CreateSP(this, &FFMODEventEditor::SpawnTab_EventEditor))
|
||||
.SetDisplayName(LOCTEXT("EventTab", "FMOD Event"))
|
||||
.SetGroup(WorkspaceMenuCategoryRef);
|
||||
}
|
||||
|
||||
void FFMODEventEditor::UnregisterTabSpawners(const TSharedRef<class FTabManager> &NewTabManager)
|
||||
{
|
||||
FAssetEditorToolkit::UnregisterTabSpawners(NewTabManager);
|
||||
|
||||
NewTabManager->UnregisterTabSpawner(EventEditorTabId);
|
||||
}
|
||||
|
||||
FFMODEventEditor::FFMODEventEditor()
|
||||
: CurrentPreviewEventInstance(nullptr)
|
||||
, EditedEvent(nullptr)
|
||||
{
|
||||
IFMODStudioEditorModule::Get().BanksReloadedEvent().AddRaw(this, &FFMODEventEditor::HandleBanksReloaded);
|
||||
BeginPIEDelegateHandle = FEditorDelegates::BeginPIE.AddRaw(this, &FFMODEventEditor::HandleBeginPIE);
|
||||
}
|
||||
|
||||
FFMODEventEditor::~FFMODEventEditor()
|
||||
{
|
||||
IFMODStudioEditorModule::Get().BanksReloadedEvent().RemoveAll(this);
|
||||
FEditorDelegates::BeginPIE.Remove(BeginPIEDelegateHandle);
|
||||
|
||||
CurrentPreviewEventInstance = nullptr;
|
||||
}
|
||||
|
||||
UFMODEvent *FFMODEventEditor::GetEditedEvent() const
|
||||
{
|
||||
return EditedEvent;
|
||||
}
|
||||
|
||||
FMOD::Studio::EventDescription *FFMODEventEditor::GetEventDescription() const
|
||||
{
|
||||
return IFMODStudioModule::Get().GetEventDescription(EditedEvent, EFMODSystemContext::Auditioning);
|
||||
}
|
||||
|
||||
void FFMODEventEditor::PlayEvent()
|
||||
{
|
||||
CurrentPreviewEventInstance = IFMODStudioModule::Get().CreateAuditioningInstance(EditedEvent);
|
||||
if (CurrentPreviewEventInstance != nullptr)
|
||||
{
|
||||
TArray<float> values;
|
||||
TArray<FMOD_STUDIO_PARAMETER_ID> ids;
|
||||
|
||||
ParameterValues.GenerateKeyArray(ids);
|
||||
ParameterValues.GenerateValueArray(values);
|
||||
|
||||
CurrentPreviewEventInstance->setParametersByIDs(ids.GetData(), values.GetData(), ParameterValues.Num());
|
||||
|
||||
CurrentPreviewEventInstance->start();
|
||||
}
|
||||
}
|
||||
|
||||
void FFMODEventEditor::PauseEvent()
|
||||
{
|
||||
if (CurrentPreviewEventInstance != nullptr)
|
||||
{
|
||||
bool bIsPaused = false;
|
||||
CurrentPreviewEventInstance->getPaused(&bIsPaused);
|
||||
CurrentPreviewEventInstance->setPaused(!bIsPaused);
|
||||
}
|
||||
}
|
||||
|
||||
void FFMODEventEditor::StopEvent()
|
||||
{
|
||||
IFMODStudioModule::Get().StopAuditioningInstance();
|
||||
}
|
||||
|
||||
void FFMODEventEditor::SetParameterValue(FMOD_STUDIO_PARAMETER_ID ParameterId, float Value)
|
||||
{
|
||||
ParameterValues[ParameterId] = Value;
|
||||
|
||||
if (CurrentPreviewEventInstance != nullptr)
|
||||
{
|
||||
CurrentPreviewEventInstance->setParameterByID(ParameterId, Value);
|
||||
}
|
||||
}
|
||||
|
||||
void FFMODEventEditor::AddParameter(FMOD_STUDIO_PARAMETER_ID ParameterId, float Value)
|
||||
{
|
||||
ParameterValues.Add(ParameterId, Value);
|
||||
}
|
||||
|
||||
float FFMODEventEditor::GetParameterValue(FMOD_STUDIO_PARAMETER_ID Id)
|
||||
{
|
||||
return ParameterValues[Id];
|
||||
}
|
||||
|
||||
void FFMODEventEditor::InitFMODEventEditor(const EToolkitMode::Type Mode, const TSharedPtr<class IToolkitHost> &InitToolkitHost, UFMODEvent *Event)
|
||||
{
|
||||
if (IsValid(Event))
|
||||
{
|
||||
EditedEvent = Event;
|
||||
|
||||
TSharedRef<FTabManager::FLayout> StandaloneDefaultLayout =
|
||||
FTabManager::NewLayout("Standalone_FMODEventEditor_Layout")
|
||||
->AddArea(FTabManager::NewPrimaryArea()
|
||||
->SetOrientation(Orient_Vertical)
|
||||
->Split(FTabManager::NewStack()->AddTab(EventEditorTabId, ETabState::OpenedTab)->SetHideTabWell(true)));
|
||||
|
||||
const bool bCreateDefaultStandaloneMenu = true;
|
||||
const bool bCreateDefaultToolbar = false;
|
||||
FAssetEditorToolkit::InitAssetEditor(Mode, InitToolkitHost, FFMODEventEditor::FMODEventEditorAppIdentifier, StandaloneDefaultLayout,
|
||||
bCreateDefaultStandaloneMenu, bCreateDefaultToolbar, Event);
|
||||
}
|
||||
}
|
||||
|
||||
FName FFMODEventEditor::GetToolkitFName() const
|
||||
{
|
||||
return FName("FMODEventEditor");
|
||||
}
|
||||
|
||||
FText FFMODEventEditor::GetBaseToolkitName() const
|
||||
{
|
||||
return LOCTEXT("ToolkitName", "FMOD Event Editor");
|
||||
}
|
||||
|
||||
FString FFMODEventEditor::GetWorldCentricTabPrefix() const
|
||||
{
|
||||
return LOCTEXT("WorldCentricTabPrefix", "FMOD Event ").ToString();
|
||||
}
|
||||
|
||||
FLinearColor FFMODEventEditor::GetWorldCentricTabColorScale() const
|
||||
{
|
||||
return FLinearColor(0.0f, 0.0f, 0.5f, 0.5f);
|
||||
}
|
||||
|
||||
void FFMODEventEditor::CreateInternalWidgets()
|
||||
{
|
||||
FMODEventEditorPanel = SNew(SFMODEventEditorPanel).FMODEventEditor(SharedThis(this));
|
||||
}
|
||||
|
||||
TSharedRef<SDockTab> FFMODEventEditor::SpawnTab_EventEditor(const FSpawnTabArgs &Args)
|
||||
{
|
||||
check(Args.GetTabId().TabType == EventEditorTabId);
|
||||
|
||||
CreateInternalWidgets();
|
||||
|
||||
return SAssignNew(OwnerTab, SDockTab)
|
||||
.Label(LOCTEXT("EventEditorTitle", "FMOD Event"))
|
||||
.TabColorScale(GetTabColorScale())[FMODEventEditorPanel.ToSharedRef()];
|
||||
}
|
||||
|
||||
void FFMODEventEditor::HandleBanksReloaded()
|
||||
{
|
||||
CurrentPreviewEventInstance = nullptr;
|
||||
|
||||
CreateInternalWidgets();
|
||||
|
||||
if (OwnerTab.IsValid())
|
||||
{
|
||||
OwnerTab->SetContent(FMODEventEditorPanel.ToSharedRef());
|
||||
}
|
||||
}
|
||||
|
||||
void FFMODEventEditor::HandleBeginPIE(bool bSimulating)
|
||||
{
|
||||
CurrentPreviewEventInstance = nullptr;
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
@ -0,0 +1,88 @@
|
||||
// Copyright (c), Firelight Technologies Pty, Ltd. 2012-2023.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Toolkits/AssetEditorToolkit.h"
|
||||
#include "fmod_studio_common.h"
|
||||
|
||||
namespace FMOD
|
||||
{
|
||||
namespace Studio
|
||||
{
|
||||
class EventDescription;
|
||||
class EventInstance;
|
||||
}
|
||||
}
|
||||
|
||||
static bool operator==(const FMOD_STUDIO_PARAMETER_ID &a, const FMOD_STUDIO_PARAMETER_ID &b)
|
||||
{
|
||||
return (a.data1 == b.data1 && a.data2 == b.data2);
|
||||
}
|
||||
FORCEINLINE uint32 GetTypeHash(const FMOD_STUDIO_PARAMETER_ID& id)
|
||||
{
|
||||
return FCrc::MemCrc_DEPRECATED(&id, sizeof(FMOD_STUDIO_PARAMETER_ID));
|
||||
}
|
||||
|
||||
class FFMODEventEditor : public FAssetEditorToolkit
|
||||
{
|
||||
public:
|
||||
virtual void RegisterTabSpawners(const TSharedRef<class FTabManager> &NewTabManager) override;
|
||||
virtual void UnregisterTabSpawners(const TSharedRef<class FTabManager> &NewTabManager) override;
|
||||
|
||||
/**
|
||||
* Edits the specified event
|
||||
*
|
||||
* @param Mode Asset editing mode for this editor (standalone or world-centric)
|
||||
* @param InitToolkitHost When Mode is WorldCentric, this is the level editor instance to spawn this editor within
|
||||
* @param Event The event to edit
|
||||
*/
|
||||
void InitFMODEventEditor(const EToolkitMode::Type Mode, const TSharedPtr<class IToolkitHost> &InitToolkitHost, class UFMODEvent *Event);
|
||||
|
||||
/** Constructor */
|
||||
FFMODEventEditor();
|
||||
|
||||
/** Destructor */
|
||||
virtual ~FFMODEventEditor();
|
||||
|
||||
UFMODEvent *GetEditedEvent() const;
|
||||
FMOD::Studio::EventDescription *GetEventDescription() const;
|
||||
void PlayEvent();
|
||||
void PauseEvent();
|
||||
void StopEvent();
|
||||
float GetParameterValue(FMOD_STUDIO_PARAMETER_ID Id);
|
||||
void SetParameterValue(FMOD_STUDIO_PARAMETER_ID ParameterId, float Value);
|
||||
void AddParameter(FMOD_STUDIO_PARAMETER_ID ParameterId, float Value);
|
||||
|
||||
/** IToolkit interface */
|
||||
virtual FName GetToolkitFName() const override;
|
||||
virtual FText GetBaseToolkitName() const override;
|
||||
virtual FString GetWorldCentricTabPrefix() const override;
|
||||
virtual FLinearColor GetWorldCentricTabColorScale() const override;
|
||||
|
||||
private:
|
||||
TMap<FMOD_STUDIO_PARAMETER_ID, float> ParameterValues;
|
||||
FMOD::Studio::EventInstance *CurrentPreviewEventInstance;
|
||||
|
||||
void HandlePreBanksReloaded();
|
||||
void HandleBanksReloaded();
|
||||
void HandleBeginPIE(bool bSimulating);
|
||||
|
||||
/** Creates all internal widgets for the tabs to point at */
|
||||
void CreateInternalWidgets();
|
||||
|
||||
/** Spawns the tab with the FMOD event inside */
|
||||
TSharedRef<SDockTab> SpawnTab_EventEditor(const FSpawnTabArgs &Args);
|
||||
|
||||
TSharedPtr<class SFMODEventEditorPanel> FMODEventEditorPanel;
|
||||
TSharedPtr<SDockTab> OwnerTab;
|
||||
|
||||
/** The tab id for the event editor tab */
|
||||
static const FName EventEditorTabId;
|
||||
|
||||
/** FMOD event editor app identifier string */
|
||||
static const FName FMODEventEditorAppIdentifier;
|
||||
|
||||
class UFMODEvent *EditedEvent;
|
||||
|
||||
FDelegateHandle BeginPIEDelegateHandle;
|
||||
};
|
@ -0,0 +1,70 @@
|
||||
// Copyright (c), Firelight Technologies Pty, Ltd.
|
||||
|
||||
#include "FMODGenerateAssetsCommandlet.h"
|
||||
|
||||
#include "FMODSettings.h"
|
||||
#include "AssetRegistry/AssetRegistryModule.h"
|
||||
#include "Editor.h"
|
||||
#include "Editor/UnrealEd/Public/FileHelpers.h"
|
||||
#include "HAL/PlatformFileManager.h"
|
||||
#include "../Classes/FMODAssetBuilder.h"
|
||||
|
||||
DEFINE_LOG_CATEGORY_STATIC(LogFMODCommandlet, Log, All);
|
||||
|
||||
static constexpr auto RebuildSwitch = TEXT("rebuild");
|
||||
|
||||
UFMODGenerateAssetsCommandlet::UFMODGenerateAssetsCommandlet(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
{
|
||||
}
|
||||
|
||||
int32 UFMODGenerateAssetsCommandlet::Main(const FString& CommandLine)
|
||||
{
|
||||
int32 returnCode = 0;
|
||||
|
||||
#if WITH_EDITOR
|
||||
|
||||
FAssetRegistryModule& assetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(AssetRegistryConstants::ModuleName);
|
||||
IAssetRegistry& AssetRegistry = assetRegistryModule.Get();
|
||||
|
||||
const UFMODSettings& Settings = *GetDefault<UFMODSettings>();
|
||||
|
||||
TArray<FString> Tokens, Switches;
|
||||
TMap<FString, FString> Params;
|
||||
ParseCommandLine(*CommandLine, Tokens, Switches, Params);
|
||||
|
||||
// Rebuild switch
|
||||
if (Switches.Contains(RebuildSwitch))
|
||||
{
|
||||
FString FolderToDelete;
|
||||
IPlatformFile& FileManager = FPlatformFileManager::Get().GetPlatformFile();
|
||||
for (FString folder : Settings.GeneratedFolders)
|
||||
{
|
||||
FolderToDelete = FPaths::ProjectContentDir() + Settings.ContentBrowserPrefix + folder;
|
||||
bool removed = FileManager.DeleteDirectoryRecursively(*FolderToDelete);
|
||||
if (!removed)
|
||||
{
|
||||
UE_LOG(LogFMODCommandlet, Warning, TEXT("Unable to delete '%s'."), *FolderToDelete);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure AssetRegistry is up to date
|
||||
TArray<FString> InPaths;
|
||||
InPaths.Add(Settings.GetFullContentPath());
|
||||
AssetRegistry.ScanPathsSynchronous(InPaths);
|
||||
while (AssetRegistry.IsLoadingAssets())
|
||||
{
|
||||
AssetRegistry.Tick(1.0f);
|
||||
}
|
||||
|
||||
FFMODAssetBuilder assetBuilder;
|
||||
if (!IsEngineExitRequested())
|
||||
{
|
||||
assetBuilder.Create();
|
||||
assetBuilder.ProcessBanks();
|
||||
}
|
||||
#endif
|
||||
|
||||
return returnCode;
|
||||
}
|
@ -0,0 +1,237 @@
|
||||
// Copyright (c), Firelight Technologies Pty, Ltd. 2023.
|
||||
|
||||
#include "FMODSettingsCustomization.h"
|
||||
|
||||
#include "DetailCategoryBuilder.h"
|
||||
#include "DetailLayoutBuilder.h"
|
||||
#include "DetailWidgetRow.h"
|
||||
#include "FMODSettings.h"
|
||||
#include "IDetailPropertyRow.h"
|
||||
#include "Settings/ProjectPackagingSettings.h"
|
||||
#include "Styling/SlateColor.h"
|
||||
#include "Widgets/DeclarativeSyntaxSupport.h"
|
||||
#include "Widgets/SWidget.h"
|
||||
#include "Widgets/SCompoundWidget.h"
|
||||
#include "Widgets/SBoxPanel.h"
|
||||
#include "Widgets/Layout/SBorder.h"
|
||||
#include "Widgets/Layout/SWidgetSwitcher.h"
|
||||
#include "Widgets/Images/SImage.h"
|
||||
#include "Widgets/Text/STextBlock.h"
|
||||
#include "Widgets/Input/SButton.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FMODSettings"
|
||||
|
||||
class SSettingsMessage : public SCompoundWidget
|
||||
{
|
||||
SLATE_BEGIN_ARGS(SSettingsMessage)
|
||||
{}
|
||||
|
||||
// Called when the Setup button is clicked
|
||||
SLATE_EVENT(FSimpleDelegate, OnSetupClicked)
|
||||
|
||||
SLATE_END_ARGS()
|
||||
|
||||
public:
|
||||
void Construct(const FArguments& InArgs)
|
||||
{
|
||||
TSharedRef<SWidget> SettingsOkayWidget = MakeRow(
|
||||
"SettingsEditor.GoodIcon",
|
||||
LOCTEXT("SettingsOkayText", "FMOD Settings are valid, run the Validate FMOD command to perform additional checking."),
|
||||
FText()
|
||||
);
|
||||
|
||||
TSharedRef<SWidget> NoContentDirWidget = MakeRow(
|
||||
"SettingsEditor.WarningIcon",
|
||||
LOCTEXT("NoContentDirText", "Bank Output Directory directory has not been set."),
|
||||
FText()
|
||||
);
|
||||
|
||||
TSharedRef<SWidget> PackagingSettingsBadWidget = MakeRow(
|
||||
"SettingsEditor.WarningIcon",
|
||||
LOCTEXT("PackagingSettingsBadText",
|
||||
"The packaging settings for copying the FMOD bank files to staging are not correct. It is recommended that:\n"
|
||||
" - The bank output directory for the Desktop platform (or the forced platform if set) is added to the \"Additional Non-Asset Directories To Copy\" list.\n"
|
||||
" - That no other directory containing FMOD banks or assets is added to either the \"Additional Non-Asset Directories To Copy\" list "
|
||||
"or the \"Additional Non-Asset Directories to Package\" list.\n"
|
||||
" - The Generated Assets are added to the \"Additional Asset Directories to Cook\" list."
|
||||
),
|
||||
LOCTEXT("FixPackagingSettings", "Fix")
|
||||
);
|
||||
|
||||
ChildSlot
|
||||
[
|
||||
SNew(SBorder)
|
||||
.BorderBackgroundColor(this, &SSettingsMessage::GetBorderColor)
|
||||
.BorderImage(FAppStyle::GetBrush("ToolPanel.LightGroupBorder"))
|
||||
.Padding(8.0f)
|
||||
[
|
||||
SNew(SWidgetSwitcher)
|
||||
.WidgetIndex(this, &SSettingsMessage::GetSetupStateAsInt)
|
||||
|
||||
+ SWidgetSwitcher::Slot()
|
||||
[
|
||||
SettingsOkayWidget
|
||||
]
|
||||
|
||||
+ SWidgetSwitcher::Slot()
|
||||
[
|
||||
NoContentDirWidget
|
||||
]
|
||||
|
||||
+ SWidgetSwitcher::Slot()
|
||||
[
|
||||
PackagingSettingsBadWidget
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
UpdateState();
|
||||
}
|
||||
|
||||
void Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime)
|
||||
{
|
||||
UpdateState();
|
||||
}
|
||||
|
||||
private:
|
||||
FSlateColor GetBorderColor() const
|
||||
{
|
||||
if (SettingsState == UFMODSettings::Okay)
|
||||
{
|
||||
return FLinearColor::Green;
|
||||
}
|
||||
else
|
||||
{
|
||||
return FLinearColor(0.8f, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
TSharedRef<SWidget> MakeRow(FName IconName, FText Message, FText ButtonMessage)
|
||||
{
|
||||
TSharedRef<SHorizontalBox> Result = SNew(SHorizontalBox)
|
||||
|
||||
// Status icon
|
||||
+ SHorizontalBox::Slot().AutoWidth().VAlign(VAlign_Center)[SNew(SImage).Image(FAppStyle::GetBrush(IconName))]
|
||||
|
||||
// Notice
|
||||
+ SHorizontalBox::Slot()
|
||||
.FillWidth(1.0f)
|
||||
.Padding(16.0f, 0.0f)
|
||||
.VAlign(VAlign_Center)[
|
||||
SNew(STextBlock)
|
||||
.ColorAndOpacity(FLinearColor::White)
|
||||
.ShadowColorAndOpacity(FLinearColor::Black)
|
||||
.ShadowOffset(FVector2D::UnitVector)
|
||||
.AutoWrapText(true)
|
||||
.Text(Message)
|
||||
];
|
||||
|
||||
if (!ButtonMessage.IsEmpty())
|
||||
{
|
||||
Result->AddSlot()
|
||||
.AutoWidth()
|
||||
.VAlign(VAlign_Center)
|
||||
[
|
||||
SNew(SButton)
|
||||
.OnClicked(this, &SSettingsMessage::OnButtonPressed)
|
||||
.Text(ButtonMessage)
|
||||
];
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
FReply OnButtonPressed()
|
||||
{
|
||||
const UFMODSettings& Settings = *GetDefault<UFMODSettings>();
|
||||
UProjectPackagingSettings* PackagingSettings = Cast<UProjectPackagingSettings>(UProjectPackagingSettings::StaticClass()->GetDefaultObject());
|
||||
|
||||
if (SettingsState == UFMODSettings::PackagingSettingsBad)
|
||||
{
|
||||
// Remove any bad entries
|
||||
for (int i = 0; i < PackagingSettings->DirectoriesToAlwaysStageAsNonUFS.Num();)
|
||||
{
|
||||
if (PackagingSettings->DirectoriesToAlwaysStageAsNonUFS[i].Path.StartsWith(Settings.BankOutputDirectory.Path))
|
||||
{
|
||||
PackagingSettings->DirectoriesToAlwaysStageAsNonUFS.RemoveAt(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < PackagingSettings->DirectoriesToAlwaysStageAsUFS.Num();)
|
||||
{
|
||||
if (PackagingSettings->DirectoriesToAlwaysStageAsUFS[i].Path.StartsWith(Settings.BankOutputDirectory.Path))
|
||||
{
|
||||
PackagingSettings->DirectoriesToAlwaysStageAsUFS.RemoveAt(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < PackagingSettings->DirectoriesToAlwaysCook.Num();)
|
||||
{
|
||||
if (PackagingSettings->DirectoriesToAlwaysCook[i].Path.StartsWith(Settings.GetFullContentPath()))
|
||||
{
|
||||
PackagingSettings->DirectoriesToAlwaysCook.RemoveAt(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
// Add correct entry
|
||||
FDirectoryPath BankPath;
|
||||
BankPath.Path = Settings.GetDesktopBankPath();
|
||||
PackagingSettings->DirectoriesToAlwaysStageAsNonUFS.Add(BankPath);
|
||||
FDirectoryPath generatedFolder;
|
||||
for (FString folder : Settings.GeneratedFolders)
|
||||
{
|
||||
generatedFolder.Path = Settings.GetFullContentPath() / folder;
|
||||
PackagingSettings->DirectoriesToAlwaysCook.Add(generatedFolder);
|
||||
}
|
||||
|
||||
PackagingSettings->TryUpdateDefaultConfigFile();
|
||||
}
|
||||
|
||||
UpdateState();
|
||||
return FReply::Handled();
|
||||
}
|
||||
|
||||
int32 GetSetupStateAsInt() const
|
||||
{
|
||||
return (int32)SettingsState;
|
||||
}
|
||||
|
||||
void UpdateState()
|
||||
{
|
||||
const UFMODSettings& Settings = *GetDefault<UFMODSettings>();
|
||||
SettingsState = Settings.Check();
|
||||
}
|
||||
|
||||
private:
|
||||
UFMODSettings::EProblem SettingsState;
|
||||
};
|
||||
|
||||
TSharedRef<IDetailCustomization> FFMODSettingsCustomization::MakeInstance()
|
||||
{
|
||||
return MakeShareable(new FFMODSettingsCustomization);
|
||||
}
|
||||
|
||||
FFMODSettingsCustomization::FFMODSettingsCustomization()
|
||||
{
|
||||
}
|
||||
|
||||
void FFMODSettingsCustomization::CustomizeDetails(IDetailLayoutBuilder &DetailLayout)
|
||||
{
|
||||
IDetailCategoryBuilder &PackagingCategory = DetailLayout.EditCategory(TEXT("Notice"), FText::GetEmpty(), ECategoryPriority::Important);
|
||||
TSharedRef<SSettingsMessage> PlatformSetupMessage = SNew(SSettingsMessage);
|
||||
PackagingCategory.AddCustomRow(LOCTEXT("Warning", "Warning"), false).WholeRowWidget[PlatformSetupMessage];
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
@ -0,0 +1,25 @@
|
||||
// Copyright (c), Firelight Technologies Pty, Ltd. 2023.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Misc/Attribute.h"
|
||||
#include "Input/Reply.h"
|
||||
#include "IDetailCustomization.h"
|
||||
#include "PropertyHandle.h"
|
||||
|
||||
class IDetailLayoutBuilder;
|
||||
|
||||
class FFMODSettingsCustomization : public IDetailCustomization
|
||||
{
|
||||
public:
|
||||
// Makes a new instance of this detail layout class for a specific detail view requesting it
|
||||
static TSharedRef<IDetailCustomization> MakeInstance();
|
||||
|
||||
// IDetailCustomization interface
|
||||
virtual void CustomizeDetails(IDetailLayoutBuilder& DetailLayout) override;
|
||||
// End of IDetailCustomization interface
|
||||
|
||||
private:
|
||||
FFMODSettingsCustomization();
|
||||
};
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,19 @@
|
||||
// Copyright (c), Firelight Technologies Pty, Ltd. 2012-2023.
|
||||
#pragma once
|
||||
|
||||
#include "Runtime/Engine/Classes/Components/SceneComponent.h"
|
||||
#include "Runtime/Engine/Classes/Camera/CameraComponent.h"
|
||||
#include "Runtime/Engine/Classes/Curves/IntegralCurve.h"
|
||||
#include "Runtime/Engine/Classes/Curves/NameCurve.h"
|
||||
#include "Runtime/Engine/Classes/Curves/RichCurve.h"
|
||||
#include "Runtime/MovieScene/Public/MovieScene.h"
|
||||
#include "Runtime/MovieScene/Public/KeyParams.h"
|
||||
|
||||
#include "Editor/Sequencer/Public/ISectionLayoutBuilder.h"
|
||||
#include "Editor/Sequencer/Public/ISequencerSection.h"
|
||||
#include "Editor/Sequencer/Public/MovieSceneTrackEditor.h"
|
||||
#include "Runtime/MovieScene/Public/MovieSceneCommonHelpers.h"
|
||||
#include "Runtime/MovieScene/Public/MovieSceneSection.h"
|
||||
#include "Editor/UnrealEd/Public/ScopedTransaction.h"
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogFMOD, Log, All);
|
@ -0,0 +1,53 @@
|
||||
// Copyright (c), Firelight Technologies Pty, Ltd. 2012-2023.
|
||||
|
||||
#include "FMODStudioStyle.h"
|
||||
#include "Styling/SlateStyleRegistry.h"
|
||||
#include "EditorStyle/Public/Interfaces/IEditorStyleModule.h"
|
||||
#include "Modules/ModuleManager.h"
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// FFMODStudioStyle
|
||||
|
||||
TSharedPtr<FSlateStyleSet> FFMODStudioStyle::StyleInstance = NULL;
|
||||
|
||||
void FFMODStudioStyle::Initialize()
|
||||
{
|
||||
if (!StyleInstance.IsValid())
|
||||
{
|
||||
StyleInstance = Create();
|
||||
FSlateStyleRegistry::RegisterSlateStyle(*StyleInstance);
|
||||
}
|
||||
}
|
||||
|
||||
#define IMAGE_BRUSH(RelativePath, ...) FSlateImageBrush(Style->RootToContentDir(RelativePath, TEXT(".png")), __VA_ARGS__)
|
||||
|
||||
TSharedRef<FSlateStyleSet> FFMODStudioStyle::Create()
|
||||
{
|
||||
TSharedRef<FSlateStyleSet> Style = MakeShared<FSlateStyleSet>(TEXT("FMODStudioStyle"));
|
||||
|
||||
Style->SetContentRoot(FPaths::EngineContentDir() / TEXT("Editor/Slate"));
|
||||
|
||||
const FVector2D Icon16x16(16.0f, 16.0f);
|
||||
const FVector2D Icon64x64(64.0f, 64.0f);
|
||||
|
||||
Style->Set("ClassIcon.FMODAmbientSound", new IMAGE_BRUSH(TEXT("Icons/AssetIcons/AmbientSound_16x"), Icon16x16));
|
||||
Style->Set("ClassThumbnail.FMODAmbientSound", new IMAGE_BRUSH(TEXT("Icons/AssetIcons/AmbientSound_64x"), Icon64x64));
|
||||
Style->Set("ClassIcon.FMODAudioComponent", new IMAGE_BRUSH(TEXT("Icons/ActorIcons/SoundActor_16x"), Icon16x16));
|
||||
Style->Set("ClassIcon.FMODAsset", new IMAGE_BRUSH(TEXT("Icons/ActorIcons/SoundActor_16x"), Icon16x16));
|
||||
|
||||
return Style;
|
||||
}
|
||||
|
||||
#undef IMAGE_BRUSH
|
||||
|
||||
void FFMODStudioStyle::Shutdown()
|
||||
{
|
||||
ensureMsgf(StyleInstance.IsValid(), TEXT("%S called, but StyleInstance wasn't initialized"), __FUNCTION__);
|
||||
FSlateStyleRegistry::UnRegisterSlateStyle(*StyleInstance);
|
||||
ensure(StyleInstance.IsUnique());
|
||||
StyleInstance.Reset();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
@ -0,0 +1,24 @@
|
||||
// Copyright (c), Firelight Technologies Pty, Ltd. 2012-2023.
|
||||
|
||||
#pragma once
|
||||
#include "SlateCore/Public/Styling/SlateStyle.h"
|
||||
#include "EditorStyle/Public/EditorStyleSet.h"
|
||||
|
||||
class FFMODStudioStyle
|
||||
{
|
||||
public:
|
||||
static void Initialize();
|
||||
|
||||
static FName GetStyleSetName();
|
||||
|
||||
static void Shutdown();
|
||||
|
||||
private:
|
||||
static TSharedRef<class FSlateStyleSet> Create();
|
||||
|
||||
private:
|
||||
static TSharedPtr<class FSlateStyleSet> StyleInstance;
|
||||
|
||||
private:
|
||||
FFMODStudioStyle() {}
|
||||
};
|
@ -0,0 +1,285 @@
|
||||
// Copyright (c), Firelight Technologies Pty, Ltd. 2012-2023.
|
||||
|
||||
#include "SFMODEventEditorPanel.h"
|
||||
#include "FMODStudioModule.h"
|
||||
#include "FMODUtils.h"
|
||||
#include "Input/Reply.h"
|
||||
#include "Widgets/Input/SNumericEntryBox.h"
|
||||
#include "Widgets/Layout/SExpandableArea.h"
|
||||
#include "EditorStyle/Public/EditorStyleSet.h"
|
||||
#include "Widgets/Input/SButton.h"
|
||||
#include "Widgets/Layout/SScrollBox.h"
|
||||
#include "fmod_studio.hpp"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FMODEventEditor"
|
||||
|
||||
SFMODEventEditorPanel::~SFMODEventEditorPanel()
|
||||
{
|
||||
}
|
||||
|
||||
void SFMODEventEditorPanel::Construct(const FArguments &InArgs)
|
||||
{
|
||||
FMODEventEditorPtr = InArgs._FMODEventEditor;
|
||||
|
||||
FMOD::Studio::EventDescription *EventDescription = FMODEventEditorPtr.Pin()->GetEventDescription();
|
||||
|
||||
TSharedRef<SBorder> ToolbarBorder = ConstructToolbar(EventDescription);
|
||||
TSharedRef<SExpandableArea> InfoArea = ConstructInfo(EventDescription);
|
||||
TSharedRef<SExpandableArea> ParametersArea = ConstructParameters(EventDescription);
|
||||
TSharedRef<SExpandableArea> UserPropertiesArea = ConstructUserProperties(EventDescription);
|
||||
|
||||
TSharedRef<SVerticalBox> ChildWidget = SNew(SVerticalBox) + SVerticalBox::Slot().AutoHeight().Padding(0.0f, 3.0f)[InfoArea] +
|
||||
SVerticalBox::Slot().AutoHeight().Padding(0.0f, 3.0f)[ParametersArea] +
|
||||
SVerticalBox::Slot().AutoHeight().Padding(0.0f, 3.0f)[UserPropertiesArea];
|
||||
|
||||
ChildSlot[SNew(SVerticalBox) + SVerticalBox::Slot().AutoHeight().Padding(0.0f, 3.0f)[ToolbarBorder] +
|
||||
SVerticalBox::Slot().FillHeight(
|
||||
1.0f)[SNew(SScrollBox) +
|
||||
SScrollBox::Slot().Padding(0.0f)[SNew(SVerticalBox) + SVerticalBox::Slot().AutoHeight().Padding(0.0f)[ChildWidget]]]];
|
||||
}
|
||||
|
||||
TSharedRef<SBorder> SFMODEventEditorPanel::ConstructToolbar(FMOD::Studio::EventDescription *EventDescription)
|
||||
{
|
||||
float MinDistance = 0.0f;
|
||||
float MaxDistance = 0.0f;
|
||||
int32 EventLengthMS = 0;
|
||||
bool bIsOneshot = false, bIsStream = false, bIs3D = false;
|
||||
if (EventDescription != nullptr)
|
||||
{
|
||||
EventDescription->getMinMaxDistance(&MinDistance, &MaxDistance);
|
||||
EventDescription->getLength(&EventLengthMS);
|
||||
EventDescription->isOneshot(&bIsOneshot);
|
||||
EventDescription->isStream(&bIsStream);
|
||||
EventDescription->is3D(&bIs3D);
|
||||
}
|
||||
|
||||
const FTimespan EventLength = FTimespan::FromMilliseconds((double)EventLengthMS);
|
||||
const FString EventLengthString =
|
||||
EventLength.GetHours() <= 0 ? EventLength.ToString(TEXT("%m:%s.%f")) : EventLength.ToString(TEXT("%h:%m:%s.%f"));
|
||||
|
||||
const FText RadiusText =
|
||||
FText::Format(LOCTEXT("RadiusFormat", "Distance Attenuation: {0}m to {1}m"), FText::AsNumber(MinDistance), FText::AsNumber(MaxDistance));
|
||||
const FText LengthText = FText::Format(LOCTEXT("LengthFormat", "Length: {0}"), FText::FromString(EventLengthString));
|
||||
|
||||
FText EventInfoText;
|
||||
if (bIs3D && bIsOneshot)
|
||||
{
|
||||
EventInfoText = FText::Format(LOCTEXT("RadiusLengthFormat", "{0} - {1}"), RadiusText, LengthText);
|
||||
}
|
||||
else if (!bIs3D && bIsOneshot)
|
||||
{
|
||||
EventInfoText = LengthText;
|
||||
}
|
||||
else if (bIs3D && !bIsOneshot)
|
||||
{
|
||||
EventInfoText = RadiusText;
|
||||
}
|
||||
|
||||
return SNew(SBorder)
|
||||
.BorderImage(FAppStyle::Get().GetBrush("ToolPanel.GroupBorder"))
|
||||
.Padding(6.0f)
|
||||
.Content()[SNew(SHorizontalBox) +
|
||||
SHorizontalBox::Slot()
|
||||
.AutoWidth()
|
||||
.Padding(0.0f, 0.0f, 2.0f, 0.0f)
|
||||
.VAlign(VAlign_Center)
|
||||
.HAlign(HAlign_Left)[SNew(SButton)
|
||||
.VAlign(VAlign_Center)
|
||||
.Text(LOCTEXT("Play", "Play"))
|
||||
.ContentPadding(4)
|
||||
.OnClicked(this, &SFMODEventEditorPanel::OnClickedPlay)] +
|
||||
SHorizontalBox::Slot()
|
||||
.AutoWidth()
|
||||
.Padding(2.0f, 0.0f)
|
||||
.VAlign(VAlign_Center)
|
||||
.HAlign(HAlign_Left)
|
||||
[SNew(SButton).Text(LOCTEXT("Pause", "Pause")).ContentPadding(4).OnClicked(this, &SFMODEventEditorPanel::OnClickedPause)] +
|
||||
SHorizontalBox::Slot()
|
||||
.AutoWidth()
|
||||
.Padding(2.0f, 0.0f)
|
||||
.VAlign(VAlign_Center)
|
||||
.HAlign(HAlign_Left)[SNew(SButton)
|
||||
.VAlign(VAlign_Center)
|
||||
.Text(LOCTEXT("Stop", "Stop"))
|
||||
.ContentPadding(4)
|
||||
.OnClicked(this, &SFMODEventEditorPanel::OnClickedStop)] +
|
||||
SHorizontalBox::Slot()
|
||||
.FillWidth(1.0f)
|
||||
.Padding(2.0f, 0.0f)
|
||||
.VAlign(VAlign_Center)
|
||||
.HAlign(HAlign_Right)[SNew(STextBlock).Text(EventInfoText)]];
|
||||
}
|
||||
|
||||
void AddTextField(TSharedRef<SVerticalBox> &InfoBox, const TCHAR *Name, const FText &Value)
|
||||
{
|
||||
InfoBox->AddSlot().Padding(
|
||||
4.0f, 3.0f)[SNew(SHorizontalBox) + SHorizontalBox::Slot().FillWidth(0.3f)[SNew(STextBlock).Text(FText::FromString(Name))] +
|
||||
SHorizontalBox::Slot()[SNew(SEditableText).Text(Value).IsReadOnly(true)]];
|
||||
}
|
||||
|
||||
void AddBoolField(TSharedRef<SVerticalBox> &InfoBox, const TCHAR *Name, bool bValue)
|
||||
{
|
||||
AddTextField(InfoBox, Name, bValue ? LOCTEXT("True", "True") : LOCTEXT("False", "False"));
|
||||
}
|
||||
|
||||
void AddFloatField(TSharedRef<SVerticalBox> &InfoBox, const TCHAR *Name, float Value)
|
||||
{
|
||||
AddTextField(InfoBox, Name, FText::AsNumber(Value));
|
||||
}
|
||||
|
||||
TSharedRef<SExpandableArea> MakeBox(TSharedRef<SVerticalBox> &InfoBox, const FText &Value)
|
||||
{
|
||||
return SNew(SExpandableArea)
|
||||
.AreaTitle(Value)
|
||||
.InitiallyCollapsed(false)
|
||||
.BodyContent()[SNew(SBorder).BorderImage(FCoreStyle::Get().GetBrush("NoBorder")).Padding(4.0f).Content()[InfoBox]];
|
||||
}
|
||||
|
||||
TSharedRef<SExpandableArea> SFMODEventEditorPanel::ConstructInfo(FMOD::Studio::EventDescription *EventDescription)
|
||||
{
|
||||
TSharedRef<SVerticalBox> InfoBox = SNew(SVerticalBox);
|
||||
|
||||
if (EventDescription != nullptr)
|
||||
{
|
||||
FString EventPath = FMODUtils::GetPath(EventDescription);
|
||||
FGuid Guid = FMODUtils::GetID(EventDescription);
|
||||
|
||||
int Length = 0.0f;
|
||||
float MinDist = 0.0f;
|
||||
float MaxDist = 0.0f;
|
||||
EventDescription->getLength(&Length);
|
||||
EventDescription->getMinMaxDistance(&MinDist, &MaxDist);
|
||||
|
||||
bool bOneShot = false;
|
||||
bool bStream = false;
|
||||
bool b3D = false;
|
||||
EventDescription->isOneshot(&bOneShot);
|
||||
EventDescription->isStream(&bStream);
|
||||
EventDescription->is3D(&b3D);
|
||||
|
||||
AddTextField(InfoBox, TEXT("Path"), FText::FromString(EventPath));
|
||||
AddTextField(InfoBox, TEXT("Guid"), FText::FromString(Guid.ToString(EGuidFormats::DigitsWithHyphensInBraces)));
|
||||
AddBoolField(InfoBox, TEXT("OneShot"), bOneShot);
|
||||
AddBoolField(InfoBox, TEXT("Streaming"), bStream);
|
||||
AddBoolField(InfoBox, TEXT("3D"), b3D);
|
||||
|
||||
AddFloatField(InfoBox, TEXT("Length"), static_cast<float>(Length));
|
||||
if (b3D)
|
||||
{
|
||||
AddFloatField(InfoBox, TEXT("Min Dist"), MinDist);
|
||||
AddFloatField(InfoBox, TEXT("Max Dist"), MaxDist);
|
||||
}
|
||||
}
|
||||
|
||||
return MakeBox(InfoBox, LOCTEXT("EventInfo", "Event Info"));
|
||||
}
|
||||
|
||||
TSharedRef<SExpandableArea> SFMODEventEditorPanel::ConstructParameters(FMOD::Studio::EventDescription *EventDescription)
|
||||
{
|
||||
auto EventEditor = FMODEventEditorPtr.Pin();
|
||||
TSharedRef<SVerticalBox> ParametersBox = SNew(SVerticalBox);
|
||||
|
||||
FNumberFormattingOptions Options;
|
||||
Options.MinimumFractionalDigits = 1;
|
||||
|
||||
if (EventDescription != nullptr)
|
||||
{
|
||||
int32 ParameterCount;
|
||||
EventDescription->getParameterDescriptionCount(&ParameterCount);
|
||||
for (int32 ParamIdx = 0; ParamIdx < ParameterCount; ParamIdx++)
|
||||
{
|
||||
FMOD_STUDIO_PARAMETER_DESCRIPTION Parameter;
|
||||
EventDescription->getParameterDescriptionByIndex(ParamIdx, &Parameter);
|
||||
|
||||
EventEditor->AddParameter(Parameter.id, Parameter.minimum);
|
||||
|
||||
const FString ParameterName = Parameter.type == FMOD_STUDIO_PARAMETER_GAME_CONTROLLED ? FString(UTF8_TO_TCHAR(Parameter.name)) :
|
||||
FMODUtils::ParameterTypeToString(Parameter.type);
|
||||
const FText ToolTipText = FText::Format(LOCTEXT("ParameterTooltipFormat", "{0} (Min Value: {1} - Max Value: {2})"),
|
||||
FText::FromString(ParameterName), FText::AsNumber(Parameter.minimum, &Options), FText::AsNumber(Parameter.maximum, &Options));
|
||||
|
||||
ParametersBox->AddSlot().Padding(4.0f,
|
||||
2.0f)[SNew(SHorizontalBox).ToolTipText(ToolTipText) +
|
||||
SHorizontalBox::Slot().FillWidth(0.3f)[SNew(STextBlock).Text(FText::FromString(ParameterName))] +
|
||||
SHorizontalBox::Slot().MaxWidth(200.0f)[SNew(SNumericEntryBox<float>)
|
||||
.Value(this, &SFMODEventEditorPanel::GetParameterValue, Parameter.id)
|
||||
.OnValueChanged(this, &SFMODEventEditorPanel::OnParameterValueChanged, Parameter.id)
|
||||
.AllowSpin(true)
|
||||
.MinValue(Parameter.minimum)
|
||||
.MaxValue(Parameter.maximum)
|
||||
.MinSliderValue(Parameter.minimum)
|
||||
.MaxSliderValue(Parameter.maximum)
|
||||
.Delta(0.01f)]];
|
||||
}
|
||||
}
|
||||
|
||||
return MakeBox(ParametersBox, LOCTEXT("EventParameters", "Event Parameters"));
|
||||
}
|
||||
|
||||
TSharedRef<SExpandableArea> SFMODEventEditorPanel::ConstructUserProperties(FMOD::Studio::EventDescription *EventDescription)
|
||||
{
|
||||
TSharedRef<SVerticalBox> UserPropertiesBox = SNew(SVerticalBox);
|
||||
|
||||
if (EventDescription != nullptr)
|
||||
{
|
||||
int32 UserPropertyCount;
|
||||
EventDescription->getUserPropertyCount(&UserPropertyCount);
|
||||
for (int32 PropertyIdx = 0; PropertyIdx < UserPropertyCount; PropertyIdx++)
|
||||
{
|
||||
FMOD_STUDIO_USER_PROPERTY UserProperty;
|
||||
EventDescription->getUserPropertyByIndex(PropertyIdx, &UserProperty);
|
||||
|
||||
FText PropertyText;
|
||||
switch (UserProperty.type)
|
||||
{
|
||||
case FMOD_STUDIO_USER_PROPERTY_TYPE_INTEGER:
|
||||
PropertyText = FText::AsNumber(UserProperty.intvalue);
|
||||
break;
|
||||
case FMOD_STUDIO_USER_PROPERTY_TYPE_BOOLEAN:
|
||||
PropertyText = UserProperty.boolvalue ? LOCTEXT("True", "True") : LOCTEXT("False", "False");
|
||||
break;
|
||||
case FMOD_STUDIO_USER_PROPERTY_TYPE_FLOAT:
|
||||
PropertyText = FText::AsNumber(UserProperty.floatvalue);
|
||||
break;
|
||||
case FMOD_STUDIO_USER_PROPERTY_TYPE_STRING:
|
||||
PropertyText = FText::FromString(UTF8_TO_TCHAR(UserProperty.stringvalue));
|
||||
break;
|
||||
}
|
||||
|
||||
FString UserName(UTF8_TO_TCHAR(UserProperty.name));
|
||||
AddTextField(UserPropertiesBox, *UserName, PropertyText);
|
||||
}
|
||||
}
|
||||
|
||||
return MakeBox(UserPropertiesBox, LOCTEXT("EventUserProperties", "Event User Properties"));
|
||||
}
|
||||
|
||||
FReply SFMODEventEditorPanel::OnClickedPlay()
|
||||
{
|
||||
FMODEventEditorPtr.Pin()->PlayEvent();
|
||||
return FReply::Handled();
|
||||
}
|
||||
|
||||
FReply SFMODEventEditorPanel::OnClickedStop()
|
||||
{
|
||||
FMODEventEditorPtr.Pin()->StopEvent();
|
||||
return FReply::Handled();
|
||||
}
|
||||
|
||||
FReply SFMODEventEditorPanel::OnClickedPause()
|
||||
{
|
||||
FMODEventEditorPtr.Pin()->PauseEvent();
|
||||
return FReply::Handled();
|
||||
}
|
||||
|
||||
void SFMODEventEditorPanel::OnParameterValueChanged(float NewValue, FMOD_STUDIO_PARAMETER_ID ParameterId)
|
||||
{
|
||||
FMODEventEditorPtr.Pin()->SetParameterValue(ParameterId, NewValue);
|
||||
}
|
||||
|
||||
TOptional<float> SFMODEventEditorPanel::GetParameterValue(FMOD_STUDIO_PARAMETER_ID ParameterId) const
|
||||
{
|
||||
return FMODEventEditorPtr.Pin()->GetParameterValue(ParameterId);
|
||||
}
|
||||
|
||||
#undef LOC_NAMESPACE
|
@ -0,0 +1,42 @@
|
||||
// Copyright (c), Firelight Technologies Pty, Ltd. 2012-2023.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "FMODEventEditor.h"
|
||||
|
||||
namespace FMOD
|
||||
{
|
||||
namespace Studio
|
||||
{
|
||||
class EventDescription;
|
||||
}
|
||||
}
|
||||
|
||||
class SFMODEventEditorPanel : public SCompoundWidget
|
||||
{
|
||||
public:
|
||||
SLATE_BEGIN_ARGS(SFMODEventEditorPanel) {}
|
||||
SLATE_ARGUMENT(TWeakPtr<FFMODEventEditor>, FMODEventEditor)
|
||||
SLATE_END_ARGS()
|
||||
|
||||
~SFMODEventEditorPanel();
|
||||
|
||||
/** SCompoundWidget interface */
|
||||
void Construct(const FArguments &InArgs);
|
||||
|
||||
private:
|
||||
TSharedRef<SBorder> ConstructToolbar(FMOD::Studio::EventDescription *EventDescription);
|
||||
TSharedRef<SExpandableArea> ConstructInfo(FMOD::Studio::EventDescription *EventDescription);
|
||||
TSharedRef<SExpandableArea> ConstructParameters(FMOD::Studio::EventDescription *EventDescription);
|
||||
TSharedRef<SExpandableArea> ConstructUserProperties(FMOD::Studio::EventDescription *EventDescription);
|
||||
|
||||
/** Editor that owns this panel */
|
||||
TWeakPtr<FFMODEventEditor> FMODEventEditorPtr;
|
||||
|
||||
FReply OnClickedPlay();
|
||||
FReply OnClickedStop();
|
||||
FReply OnClickedPause();
|
||||
|
||||
TOptional<float> GetParameterValue(FMOD_STUDIO_PARAMETER_ID ParameterId) const;
|
||||
void OnParameterValueChanged(float NewValue, FMOD_STUDIO_PARAMETER_ID ParameterId);
|
||||
};
|
@ -0,0 +1,185 @@
|
||||
// Copyright (c), Firelight Technologies Pty, Ltd. 2012-2023.
|
||||
|
||||
#include "Sequencer/FMODChannelEditors.h"
|
||||
#include "ISequencerChannelInterface.h"
|
||||
#include "Widgets/DeclarativeSyntaxSupport.h"
|
||||
#include "Widgets/SCompoundWidget.h"
|
||||
#include "MovieSceneTimeHelpers.h"
|
||||
#include "MovieSceneToolHelpers.h"
|
||||
#include "ScopedTransaction.h"
|
||||
#include "EditorWidgets/Public/SEnumCombo.h"
|
||||
#include "EditorStyleSet.h"
|
||||
#include "Channels/MovieSceneChannelTraits.h"
|
||||
|
||||
class SFMODEventControlKeyEditor : public SCompoundWidget
|
||||
{
|
||||
public:
|
||||
SLATE_BEGIN_ARGS(SFMODEventControlKeyEditor) {}
|
||||
SLATE_END_ARGS();
|
||||
|
||||
void Construct(const FArguments &InArgs, TMovieSceneChannelHandle<FFMODEventControlChannel> InChannelHandle,
|
||||
TWeakObjectPtr<UMovieSceneSection> InWeakSection, TWeakPtr<ISequencer> InWeakSequencer, UEnum *InEnum)
|
||||
{
|
||||
ChannelHandle = InChannelHandle;
|
||||
WeakSection = InWeakSection;
|
||||
WeakSequencer = InWeakSequencer;
|
||||
|
||||
ChildSlot[MovieSceneToolHelpers::MakeEnumComboBox(InEnum,
|
||||
TAttribute<int32>::Create(TAttribute<int32>::FGetter::CreateSP(this, &SFMODEventControlKeyEditor::OnGetCurrentValueAsInt)),
|
||||
SEnumComboBox::FOnEnumSelectionChanged::CreateSP(this, &SFMODEventControlKeyEditor::OnChangeKey))];
|
||||
}
|
||||
|
||||
private:
|
||||
int32 OnGetCurrentValueAsInt() const
|
||||
{
|
||||
using namespace UE::MovieScene;
|
||||
|
||||
FFMODEventControlChannel *Channel = ChannelHandle.Get();
|
||||
ISequencer *Sequencer = WeakSequencer.Pin().Get();
|
||||
UMovieSceneSection *OwningSection = WeakSection.Get();
|
||||
uint8 Result = 0;
|
||||
|
||||
if (Channel && Sequencer && OwningSection)
|
||||
{
|
||||
const FFrameTime CurrentTime = UE::MovieScene::ClampToDiscreteRange(Sequencer->GetLocalTime().Time, OwningSection->GetRange());
|
||||
EvaluateChannel(Channel, CurrentTime, Result);
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
void SetValue(uint8 InValue)
|
||||
{
|
||||
using namespace UE::MovieScene;
|
||||
|
||||
UMovieSceneSection *OwningSection = WeakSection.Get();
|
||||
if (!OwningSection)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
OwningSection->SetFlags(RF_Transactional);
|
||||
|
||||
FFMODEventControlChannel *Channel = ChannelHandle.Get();
|
||||
ISequencer *Sequencer = WeakSequencer.Pin().Get();
|
||||
|
||||
if (!OwningSection->TryModify() || !Channel || !Sequencer)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const FFrameNumber CurrentTime = Sequencer->GetLocalTime().Time.FloorToFrame();
|
||||
const bool bAutoSetTrackDefaults = Sequencer->GetAutoSetTrackDefaults();
|
||||
|
||||
EMovieSceneKeyInterpolation Interpolation = Sequencer->GetKeyInterpolation();
|
||||
|
||||
TArray<FKeyHandle> KeysAtCurrentTime;
|
||||
Channel->GetKeys(TRange<FFrameNumber>(CurrentTime), nullptr, &KeysAtCurrentTime);
|
||||
|
||||
if (KeysAtCurrentTime.Num() > 0)
|
||||
{
|
||||
AssignValue(Channel, KeysAtCurrentTime[0], InValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
const bool bHasAnyKeys = Channel->GetNumKeys() != 0;
|
||||
|
||||
if (bHasAnyKeys || bAutoSetTrackDefaults == false)
|
||||
{
|
||||
// When auto setting track defaults are disabled, add a key even when it's empty so that the changed
|
||||
// value is saved and is propagated to the property.
|
||||
AddKeyToChannel(Channel, CurrentTime, InValue, Interpolation);
|
||||
}
|
||||
|
||||
if (bHasAnyKeys)
|
||||
{
|
||||
TRange<FFrameNumber> KeyRange = TRange<FFrameNumber>(CurrentTime);
|
||||
TRange<FFrameNumber> SectionRange = OwningSection->GetRange();
|
||||
|
||||
if (!SectionRange.Contains(KeyRange))
|
||||
{
|
||||
OwningSection->SetRange(TRange<FFrameNumber>::Hull(KeyRange, SectionRange));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Always update the default value when auto-set default values is enabled so that the last changes
|
||||
// are always saved to the track.
|
||||
if (bAutoSetTrackDefaults)
|
||||
{
|
||||
SetChannelDefault(Channel, InValue);
|
||||
}
|
||||
}
|
||||
|
||||
void OnChangeKey(int32 Selection, ESelectInfo::Type SelectionType)
|
||||
{
|
||||
FScopedTransaction Transaction(FText::FromString("Set FMOD Event Control Key Value"));
|
||||
SetValue(Selection);
|
||||
if (ISequencer *Sequencer = WeakSequencer.Pin().Get())
|
||||
{
|
||||
Sequencer->NotifyMovieSceneDataChanged(EMovieSceneDataChangeType::TrackValueChangedRefreshImmediately);
|
||||
}
|
||||
}
|
||||
|
||||
TMovieSceneChannelHandle<FFMODEventControlChannel> ChannelHandle;
|
||||
TWeakObjectPtr<UMovieSceneSection> WeakSection;
|
||||
TWeakPtr<ISequencer> WeakSequencer;
|
||||
};
|
||||
|
||||
bool CanCreateKeyEditor(const FFMODEventControlChannel *Channel)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
TSharedRef<SWidget> CreateKeyEditor(const TMovieSceneChannelHandle<FFMODEventControlChannel> &Channel, UMovieSceneSection *Section,
|
||||
const FGuid &InObjectBindingID, TWeakPtr<FTrackInstancePropertyBindings> PropertyBindings, TWeakPtr<ISequencer> InSequencer)
|
||||
{
|
||||
const FFMODEventControlChannel *RawChannel = Channel.Get();
|
||||
|
||||
if (!RawChannel)
|
||||
{
|
||||
return SNullWidget::NullWidget;
|
||||
}
|
||||
|
||||
UEnum *Enum = RawChannel->GetEnum();
|
||||
return SNew(SFMODEventControlKeyEditor, Channel, Section, InSequencer, Enum);
|
||||
}
|
||||
|
||||
void DrawKeys(FFMODEventControlChannel *Channel, TArrayView<const FKeyHandle> InKeyHandles, const UMovieSceneSection* InOwner, TArrayView<FKeyDrawParams> OutKeyDrawParams)
|
||||
{
|
||||
static const FName KeyLeftBrushName("Sequencer.KeyLeft");
|
||||
static const FName KeyRightBrushName("Sequencer.KeyRight");
|
||||
static const FName KeyDiamondBrushName("Sequencer.KeyDiamond");
|
||||
|
||||
const FSlateBrush *LeftKeyBrush = FAppStyle::GetBrush(KeyLeftBrushName);
|
||||
const FSlateBrush *RightKeyBrush = FAppStyle::GetBrush(KeyRightBrushName);
|
||||
const FSlateBrush *DiamondBrush = FAppStyle::GetBrush(KeyDiamondBrushName);
|
||||
|
||||
TMovieSceneChannelData<uint8> ChannelData = Channel->GetData();
|
||||
|
||||
for (int32 Index = 0; Index < InKeyHandles.Num(); ++Index)
|
||||
{
|
||||
FKeyHandle Handle = InKeyHandles[Index];
|
||||
|
||||
FKeyDrawParams Params;
|
||||
Params.BorderBrush = Params.FillBrush = DiamondBrush;
|
||||
|
||||
const int32 KeyIndex = ChannelData.GetIndex(Handle);
|
||||
if (KeyIndex != INDEX_NONE)
|
||||
{
|
||||
const EFMODEventControlKey Value = (EFMODEventControlKey)ChannelData.GetValues()[KeyIndex];
|
||||
if (Value == EFMODEventControlKey::Play)
|
||||
{
|
||||
Params.BorderBrush = Params.FillBrush = LeftKeyBrush;
|
||||
Params.FillOffset = FVector2D(-1.0f, 1.0f);
|
||||
}
|
||||
else if (Value == EFMODEventControlKey::Stop)
|
||||
{
|
||||
Params.BorderBrush = Params.FillBrush = RightKeyBrush;
|
||||
Params.FillOffset = FVector2D(1.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
OutKeyDrawParams[Index] = Params;
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "MVVM/Views/KeyDrawParams.h"
|
||||
#include "Channels/MovieSceneChannelHandle.h"
|
||||
#include "Sequencer/FMODEventControlSection.h"
|
||||
|
||||
/** Key editor overrides */
|
||||
bool CanCreateKeyEditor(const FFMODEventControlChannel* Channel);
|
||||
|
||||
TSharedRef<SWidget> CreateKeyEditor(const TMovieSceneChannelHandle<FFMODEventControlChannel>& Channel, UMovieSceneSection* Section,
|
||||
const FGuid& InObjectBindingID, TWeakPtr<FTrackInstancePropertyBindings> PropertyBindings, TWeakPtr<ISequencer> InSequencer);
|
||||
|
||||
/** Key drawing overrides */
|
||||
void DrawKeys(FFMODEventControlChannel *Channel, TArrayView<const FKeyHandle> InKeyHandles, const UMovieSceneSection* InOwner, TArrayView<FKeyDrawParams> OutKeyDrawParams);
|
@ -0,0 +1,194 @@
|
||||
// Copyright (c), Firelight Technologies Pty, Ltd. 2012-2023.
|
||||
|
||||
#include "FMODEventControlTrackEditor.h"
|
||||
#include "Rendering/DrawElements.h"
|
||||
#include "Framework/MultiBox/MultiBoxBuilder.h"
|
||||
#include "Curves/IntegralCurve.h"
|
||||
#include "SequencerSectionPainter.h"
|
||||
#include "EditorStyleSet.h"
|
||||
#include "Editor/UnrealEdEngine.h"
|
||||
#include "Sequencer/FMODEventControlSection.h"
|
||||
#include "Sequencer/FMODEventControlTrack.h"
|
||||
#include "ISectionLayoutBuilder.h"
|
||||
#include "FMODAmbientSound.h"
|
||||
#include "CommonMovieSceneTools.h"
|
||||
#include "Channels/MovieSceneChannelProxy.h"
|
||||
#include "Channels/MovieSceneChannelEditorData.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FFMODEventControlTrackEditor"
|
||||
|
||||
FFMODEventControlSection::FFMODEventControlSection(UMovieSceneSection &InSection, TSharedRef<ISequencer> InOwningSequencer)
|
||||
: Section(InSection)
|
||||
, OwningSequencerPtr(InOwningSequencer)
|
||||
{
|
||||
}
|
||||
|
||||
UMovieSceneSection *FFMODEventControlSection::GetSectionObject()
|
||||
{
|
||||
return &Section;
|
||||
}
|
||||
|
||||
float FFMODEventControlSection::GetSectionHeight() const
|
||||
{
|
||||
static const float SectionHeight = 20.f;
|
||||
return SectionHeight;
|
||||
}
|
||||
|
||||
int32 FFMODEventControlSection::OnPaintSection(FSequencerSectionPainter &InPainter) const
|
||||
{
|
||||
TSharedPtr<ISequencer> OwningSequencer = OwningSequencerPtr.Pin();
|
||||
|
||||
if (!OwningSequencer.IsValid())
|
||||
{
|
||||
return InPainter.LayerId + 1;
|
||||
}
|
||||
|
||||
const ESlateDrawEffect DrawEffects = InPainter.bParentEnabled ? ESlateDrawEffect::None : ESlateDrawEffect::DisabledEffect;
|
||||
const FTimeToPixel &TimeToPixelConverter = InPainter.GetTimeConverter();
|
||||
|
||||
FLinearColor TrackColor;
|
||||
|
||||
// TODO: Set / clip stop time based on event length
|
||||
UFMODEventControlSection *ControlSection = Cast<UFMODEventControlSection>(&Section);
|
||||
if (IsValid(ControlSection))
|
||||
{
|
||||
UFMODEventControlTrack *ParentTrack = Cast<UFMODEventControlTrack>(ControlSection->GetOuter());
|
||||
if (IsValid(ParentTrack))
|
||||
{
|
||||
TrackColor = ParentTrack->GetColorTint();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: This should only draw the visible ranges.
|
||||
TArray<TRange<float>> DrawRanges;
|
||||
TOptional<float> CurrentRangeStart;
|
||||
|
||||
if (ControlSection != nullptr)
|
||||
{
|
||||
TMovieSceneChannelData<const uint8> ChannelData = ControlSection->ControlKeys.GetData();
|
||||
TArrayView<const FFrameNumber> Times = ChannelData.GetTimes();
|
||||
TArrayView<const uint8> Values = ChannelData.GetValues();
|
||||
|
||||
for (int32 Index = 0; Index < Times.Num(); ++Index)
|
||||
{
|
||||
const double Time = Times[Index] / TimeToPixelConverter.GetTickResolution();
|
||||
const EFMODEventControlKey Value = (EFMODEventControlKey)Values[Index];
|
||||
|
||||
if (Value == EFMODEventControlKey::Play)
|
||||
{
|
||||
if (CurrentRangeStart.IsSet() == false)
|
||||
{
|
||||
CurrentRangeStart = Time;
|
||||
}
|
||||
}
|
||||
if (Value == EFMODEventControlKey::Stop)
|
||||
{
|
||||
if (CurrentRangeStart.IsSet())
|
||||
{
|
||||
DrawRanges.Add(TRange<float>(CurrentRangeStart.GetValue(), Time));
|
||||
CurrentRangeStart.Reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (CurrentRangeStart.IsSet())
|
||||
{
|
||||
DrawRanges.Add(TRange<float>(CurrentRangeStart.GetValue(), OwningSequencer->GetViewRange().GetUpperBoundValue()));
|
||||
}
|
||||
|
||||
for (const TRange<float> &DrawRange : DrawRanges)
|
||||
{
|
||||
float XOffset = TimeToPixelConverter.SecondsToPixel(DrawRange.GetLowerBoundValue());
|
||||
float XSize = TimeToPixelConverter.SecondsToPixel(DrawRange.GetUpperBoundValue()) - XOffset;
|
||||
FSlateDrawElement::MakeBox(InPainter.DrawElements, InPainter.LayerId,
|
||||
InPainter.SectionGeometry.ToPaintGeometry(
|
||||
FVector2D(XOffset, (InPainter.SectionGeometry.GetLocalSize().Y - SequencerSectionConstants::KeySize.Y) / 2),
|
||||
FVector2D(XSize, SequencerSectionConstants::KeySize.Y)),
|
||||
FAppStyle::GetBrush("Sequencer.Section.Background"), DrawEffects);
|
||||
FSlateDrawElement::MakeBox(InPainter.DrawElements, InPainter.LayerId,
|
||||
InPainter.SectionGeometry.ToPaintGeometry(
|
||||
FVector2D(XOffset, (InPainter.SectionGeometry.GetLocalSize().Y - SequencerSectionConstants::KeySize.Y) / 2),
|
||||
FVector2D(XSize, SequencerSectionConstants::KeySize.Y)),
|
||||
FAppStyle::GetBrush("Sequencer.Section.BackgroundTint"), DrawEffects, TrackColor);
|
||||
}
|
||||
|
||||
return InPainter.LayerId + 1;
|
||||
}
|
||||
|
||||
FFMODEventControlTrackEditor::FFMODEventControlTrackEditor(TSharedRef<ISequencer> InSequencer)
|
||||
: FMovieSceneTrackEditor(InSequencer)
|
||||
{
|
||||
}
|
||||
|
||||
TSharedRef<ISequencerTrackEditor> FFMODEventControlTrackEditor::CreateTrackEditor(TSharedRef<ISequencer> InSequencer)
|
||||
{
|
||||
return MakeShareable(new FFMODEventControlTrackEditor(InSequencer));
|
||||
}
|
||||
|
||||
bool FFMODEventControlTrackEditor::SupportsType(TSubclassOf<UMovieSceneTrack> Type) const
|
||||
{
|
||||
return Type == UFMODEventControlTrack::StaticClass();
|
||||
}
|
||||
|
||||
TSharedRef<ISequencerSection> FFMODEventControlTrackEditor::MakeSectionInterface(
|
||||
UMovieSceneSection &SectionObject, UMovieSceneTrack &Track, FGuid ObjectBinding)
|
||||
{
|
||||
check(SupportsType(SectionObject.GetOuter()->GetClass()));
|
||||
const TSharedPtr<ISequencer> OwningSequencer = GetSequencer();
|
||||
return MakeShareable(new FFMODEventControlSection(SectionObject, OwningSequencer.ToSharedRef()));
|
||||
}
|
||||
|
||||
void FFMODEventControlTrackEditor::BuildObjectBindingTrackMenu(FMenuBuilder &MenuBuilder, const TArray<FGuid> &ObjectBindings, const UClass *ObjectClass)
|
||||
{
|
||||
if (ObjectClass->IsChildOf(AFMODAmbientSound::StaticClass()) || ObjectClass->IsChildOf(UFMODAudioComponent::StaticClass()))
|
||||
{
|
||||
const TSharedPtr<ISequencer> ParentSequencer = GetSequencer();
|
||||
|
||||
MenuBuilder.AddMenuEntry(LOCTEXT("AddFMODEventControlTrack", "FMOD Event Control Track"),
|
||||
LOCTEXT("FMODEventControlTooltip", "Adds a track for controlling FMOD event."), FSlateIcon(),
|
||||
FUIAction(FExecuteAction::CreateSP(this, &FFMODEventControlTrackEditor::AddControlKey, ObjectBindings)));
|
||||
}
|
||||
}
|
||||
|
||||
void FFMODEventControlTrackEditor::AddControlKey(TArray<FGuid> ObjectGuids)
|
||||
{
|
||||
TSharedPtr<ISequencer> SequencerPtr = GetSequencer();
|
||||
for (FGuid ObjectGuid : ObjectGuids)
|
||||
{
|
||||
UObject *Object = SequencerPtr.IsValid() ? SequencerPtr->FindSpawnedObjectOrTemplate(ObjectGuid) : nullptr;
|
||||
|
||||
if (Object)
|
||||
{
|
||||
AnimatablePropertyChanged(FOnKeyProperty::CreateRaw(this, &FFMODEventControlTrackEditor::AddKeyInternal, Object));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FKeyPropertyResult FFMODEventControlTrackEditor::AddKeyInternal(FFrameNumber KeyTime, UObject *Object)
|
||||
{
|
||||
FKeyPropertyResult KeyPropertyResult;
|
||||
|
||||
FFindOrCreateHandleResult HandleResult = FindOrCreateHandleToObject(Object);
|
||||
FGuid ObjectHandle = HandleResult.Handle;
|
||||
KeyPropertyResult.bHandleCreated |= HandleResult.bWasCreated;
|
||||
|
||||
if (ObjectHandle.IsValid())
|
||||
{
|
||||
FFindOrCreateTrackResult TrackResult = FindOrCreateTrackForObject(ObjectHandle, UFMODEventControlTrack::StaticClass());
|
||||
UMovieSceneTrack *Track = TrackResult.Track;
|
||||
KeyPropertyResult.bTrackCreated |= TrackResult.bWasCreated;
|
||||
|
||||
if (KeyPropertyResult.bTrackCreated && ensure(Track))
|
||||
{
|
||||
UFMODEventControlTrack *EventTrack = Cast<UFMODEventControlTrack>(Track);
|
||||
EventTrack->AddNewSection(KeyTime);
|
||||
EventTrack->SetDisplayName(LOCTEXT("TrackName", "FMOD Event"));
|
||||
KeyPropertyResult.bTrackModified = true;
|
||||
}
|
||||
}
|
||||
|
||||
return KeyPropertyResult;
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
@ -0,0 +1,59 @@
|
||||
// Copyright (c), Firelight Technologies Pty, Ltd. 2012-2023.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Misc/Guid.h"
|
||||
#include "Templates/SubclassOf.h"
|
||||
#include "Curves/KeyHandle.h"
|
||||
#include "ISequencer.h"
|
||||
#include "MovieSceneTrack.h"
|
||||
#include "ISequencerSection.h"
|
||||
#include "ISequencerTrackEditor.h"
|
||||
#include "MovieSceneTrackEditor.h"
|
||||
|
||||
class FMenuBuilder;
|
||||
class FSequencerSectionPainter;
|
||||
|
||||
/** FMOD Event control track */
|
||||
class FFMODEventControlTrackEditor : public FMovieSceneTrackEditor
|
||||
{
|
||||
public:
|
||||
FFMODEventControlTrackEditor(TSharedRef<ISequencer> InSequencer);
|
||||
|
||||
static TSharedRef<ISequencerTrackEditor> CreateTrackEditor(TSharedRef<ISequencer> OwningSequencer);
|
||||
|
||||
void AddControlKey(TArray<FGuid> ObjectGuids);
|
||||
|
||||
// Begin ISequencerTrackEditor interface
|
||||
virtual void BuildObjectBindingTrackMenu(FMenuBuilder &MenuBuilder, const TArray<FGuid> &ObjectBindings, const UClass *ObjectClass) override;
|
||||
virtual TSharedRef<ISequencerSection> MakeSectionInterface(
|
||||
UMovieSceneSection &SectionObject, UMovieSceneTrack &Track, FGuid ObjectBinding) override;
|
||||
virtual bool SupportsType(TSubclassOf<UMovieSceneTrack> Type) const override;
|
||||
// End ISequencerTrackEditor interface
|
||||
|
||||
private:
|
||||
/** Delegate for AnimatablePropertyChanged in AddKey. */
|
||||
virtual FKeyPropertyResult AddKeyInternal(FFrameNumber KeyTime, UObject *Object);
|
||||
};
|
||||
|
||||
/** Class for event control sections. */
|
||||
class FFMODEventControlSection : public ISequencerSection, public TSharedFromThis<FFMODEventControlSection>
|
||||
{
|
||||
public:
|
||||
FFMODEventControlSection(UMovieSceneSection &InSection, TSharedRef<ISequencer> InOwningSequencer);
|
||||
|
||||
// Begin ISequencerSection interface
|
||||
virtual UMovieSceneSection *GetSectionObject() override;
|
||||
virtual float GetSectionHeight() const override;
|
||||
virtual int32 OnPaintSection(FSequencerSectionPainter &InPainter) const override;
|
||||
virtual bool SectionIsResizable() const override { return false; }
|
||||
// End ISequencerSection interface
|
||||
|
||||
private:
|
||||
/** The section we are visualizing. */
|
||||
UMovieSceneSection &Section;
|
||||
|
||||
/** The sequencer that owns this section */
|
||||
TWeakPtr<ISequencer> OwningSequencerPtr;
|
||||
};
|
@ -0,0 +1,156 @@
|
||||
// Copyright (c), Firelight Technologies Pty, Ltd. 2012-2023.
|
||||
|
||||
#include "FMODEventParameterTrackEditor.h"
|
||||
#include "FMODAmbientSound.h"
|
||||
#include "FMODEvent.h"
|
||||
#include "FMODStudioModule.h"
|
||||
#include "Framework/MultiBox/MultiBoxBuilder.h"
|
||||
#include "Sequencer/FMODEventParameterTrack.h"
|
||||
#include "Sections/MovieSceneParameterSection.h"
|
||||
#include "Sequencer/FMODParameterSection.h"
|
||||
#include "SequencerUtilities.h"
|
||||
#include "fmod_studio.hpp"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FMODEeventParameterTrackEditor"
|
||||
|
||||
FName FFMODEventParameterTrackEditor::TrackName("FMODEventParameter");
|
||||
|
||||
FFMODEventParameterTrackEditor::FFMODEventParameterTrackEditor(TSharedRef<ISequencer> InSequencer)
|
||||
: FMovieSceneTrackEditor(InSequencer)
|
||||
{
|
||||
}
|
||||
|
||||
TSharedRef<ISequencerTrackEditor> FFMODEventParameterTrackEditor::CreateTrackEditor(TSharedRef<ISequencer> OwningSequencer)
|
||||
{
|
||||
return MakeShareable(new FFMODEventParameterTrackEditor(OwningSequencer));
|
||||
}
|
||||
|
||||
TSharedRef<ISequencerSection> FFMODEventParameterTrackEditor::MakeSectionInterface(
|
||||
UMovieSceneSection &SectionObject, UMovieSceneTrack &Track, FGuid ObjectBinding)
|
||||
{
|
||||
UMovieSceneParameterSection *ParameterSection = Cast<UMovieSceneParameterSection>(&SectionObject);
|
||||
checkf(ParameterSection != nullptr, TEXT("Unsupported section type."));
|
||||
return MakeShareable(new FFMODParameterSection(*ParameterSection));
|
||||
}
|
||||
|
||||
TSharedPtr<SWidget> FFMODEventParameterTrackEditor::BuildOutlinerEditWidget(
|
||||
const FGuid &ObjectBinding, UMovieSceneTrack *Track, const FBuildEditWidgetParams &Params)
|
||||
{
|
||||
UFMODEventParameterTrack *EventParameterTrack = Cast<UFMODEventParameterTrack>(Track);
|
||||
|
||||
// Create a container edit box
|
||||
return FSequencerUtilities::MakeAddButton(LOCTEXT("ParameterText", "Parameter"),
|
||||
FOnGetContent::CreateSP(this, &FFMODEventParameterTrackEditor::OnGetAddParameterMenuContent, ObjectBinding, EventParameterTrack),
|
||||
Params.NodeIsHovered,
|
||||
GetSequencer());
|
||||
}
|
||||
|
||||
void FFMODEventParameterTrackEditor::BuildObjectBindingTrackMenu(FMenuBuilder &MenuBuilder, const TArray<FGuid> &ObjectBindings, const UClass *ObjectClass)
|
||||
{
|
||||
if (ObjectClass->IsChildOf(AFMODAmbientSound::StaticClass()) || ObjectClass->IsChildOf(UFMODAudioComponent::StaticClass()))
|
||||
{
|
||||
const TSharedPtr<ISequencer> ParentSequencer = GetSequencer();
|
||||
|
||||
MenuBuilder.AddMenuEntry(LOCTEXT("AddFMODParameterTrack", "FMOD Event Parameter Track"),
|
||||
LOCTEXT("AddFMODParameterTrackTooltip", "Adds a track for controlling FMOD event parameter values."), FSlateIcon(),
|
||||
FUIAction(FExecuteAction::CreateSP(this, &FFMODEventParameterTrackEditor::AddEventParameterTrack, ObjectBindings[0]),
|
||||
FCanExecuteAction::CreateSP(this, &FFMODEventParameterTrackEditor::CanAddEventParameterTrack, ObjectBindings[0])));
|
||||
}
|
||||
}
|
||||
|
||||
bool FFMODEventParameterTrackEditor::SupportsType(TSubclassOf<UMovieSceneTrack> Type) const
|
||||
{
|
||||
return Type == UFMODEventParameterTrack::StaticClass();
|
||||
}
|
||||
|
||||
TSharedRef<SWidget> FFMODEventParameterTrackEditor::OnGetAddParameterMenuContent(FGuid ObjectBinding, UFMODEventParameterTrack *EventParameterTrack)
|
||||
{
|
||||
TSharedPtr<ISequencer> SequencerPtr = GetSequencer();
|
||||
AFMODAmbientSound *Sound = SequencerPtr.IsValid() ? Cast<AFMODAmbientSound>(SequencerPtr->FindSpawnedObjectOrTemplate(ObjectBinding)) : nullptr;
|
||||
UFMODAudioComponent *AudioComponent;
|
||||
|
||||
if (IsValid(Sound))
|
||||
{
|
||||
AudioComponent = Sound->AudioComponent;
|
||||
}
|
||||
else
|
||||
{
|
||||
AudioComponent = SequencerPtr.IsValid() ? Cast<UFMODAudioComponent>(SequencerPtr->FindSpawnedObjectOrTemplate(ObjectBinding)) : nullptr;
|
||||
}
|
||||
return BuildParameterMenu(ObjectBinding, EventParameterTrack, AudioComponent);
|
||||
}
|
||||
|
||||
TSharedRef<SWidget> FFMODEventParameterTrackEditor::BuildParameterMenu(
|
||||
FGuid ObjectBinding, UFMODEventParameterTrack *EventParameterTrack, UFMODAudioComponent *AudioComponent)
|
||||
{
|
||||
FMenuBuilder AddParameterMenuBuilder(true, nullptr);
|
||||
|
||||
if (IsValid(AudioComponent) && AudioComponent->Event)
|
||||
{
|
||||
TArray<FParameterNameAndAction> ParameterNamesAndActions;
|
||||
TArray<FMOD_STUDIO_PARAMETER_DESCRIPTION> ParameterDescriptions;
|
||||
AudioComponent->Event->GetParameterDescriptions(ParameterDescriptions);
|
||||
|
||||
for (FMOD_STUDIO_PARAMETER_DESCRIPTION &ParameterDescription : ParameterDescriptions)
|
||||
{
|
||||
FName ParameterName(ParameterDescription.name);
|
||||
FExecuteAction InitAction =
|
||||
FExecuteAction::CreateSP(this, &FFMODEventParameterTrackEditor::AddParameter, ObjectBinding, EventParameterTrack, ParameterName);
|
||||
FUIAction AddParameterMenuAction(InitAction);
|
||||
FParameterNameAndAction NameAndAction(ParameterName, AddParameterMenuAction);
|
||||
ParameterNamesAndActions.Add(NameAndAction);
|
||||
}
|
||||
|
||||
// Sort and generate menu.
|
||||
ParameterNamesAndActions.Sort();
|
||||
for (FParameterNameAndAction NameAndAction : ParameterNamesAndActions)
|
||||
{
|
||||
AddParameterMenuBuilder.AddMenuEntry(FText::FromName(NameAndAction.ParameterName), FText(), FSlateIcon(), NameAndAction.Action);
|
||||
}
|
||||
}
|
||||
return AddParameterMenuBuilder.MakeWidget();
|
||||
}
|
||||
|
||||
bool FFMODEventParameterTrackEditor::CanAddEventParameterTrack(FGuid ObjectBinding)
|
||||
{
|
||||
return GetSequencer()->GetFocusedMovieSceneSequence()->GetMovieScene()->FindTrack(
|
||||
UFMODEventParameterTrack::StaticClass(), ObjectBinding, TrackName) == nullptr;
|
||||
}
|
||||
|
||||
void FFMODEventParameterTrackEditor::AddEventParameterTrack(FGuid ObjectBinding)
|
||||
{
|
||||
FindOrCreateTrackForObject(ObjectBinding, UFMODEventParameterTrack::StaticClass(), TrackName, true);
|
||||
GetSequencer()->NotifyMovieSceneDataChanged(EMovieSceneDataChangeType::MovieSceneStructureItemAdded);
|
||||
}
|
||||
|
||||
void FFMODEventParameterTrackEditor::AddParameter(FGuid ObjectBinding, UFMODEventParameterTrack *EventParameterTrack, FName ParameterName)
|
||||
{
|
||||
UMovieSceneSequence *MovieSceneSequence = GetMovieSceneSequence();
|
||||
FFrameNumber KeyTime = GetTimeForKey();
|
||||
|
||||
for (TWeakObjectPtr<> Object : GetSequencer()->FindObjectsInCurrentSequence(ObjectBinding))
|
||||
{
|
||||
AFMODAmbientSound *Sound = Cast<AFMODAmbientSound>(Object.Get());
|
||||
UFMODAudioComponent *AudioComponent = nullptr;
|
||||
|
||||
if (IsValid(Sound))
|
||||
{
|
||||
AudioComponent = Sound->AudioComponent;
|
||||
}
|
||||
else
|
||||
{
|
||||
AudioComponent = Cast<UFMODAudioComponent>(Object.Get());
|
||||
}
|
||||
|
||||
if (IsValid(AudioComponent))
|
||||
{
|
||||
float Value = AudioComponent->GetParameter(ParameterName);
|
||||
const FScopedTransaction Transaction(LOCTEXT("AddEventParameter", "Add event parameter"));
|
||||
EventParameterTrack->Modify();
|
||||
EventParameterTrack->AddParameterKey(ParameterName, KeyTime, Value);
|
||||
}
|
||||
}
|
||||
GetSequencer()->NotifyMovieSceneDataChanged(EMovieSceneDataChangeType::MovieSceneStructureItemAdded);
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
@ -0,0 +1,77 @@
|
||||
// Taken from ParticleParameterTrackEditor
|
||||
|
||||
#pragma once
|
||||
#include "CoreMinimal.h"
|
||||
#include "Misc/Guid.h"
|
||||
#include "Templates/SubclassOf.h"
|
||||
#include "Widgets/SWidget.h"
|
||||
#include "ISequencer.h"
|
||||
#include "MovieSceneTrack.h"
|
||||
#include "ISequencerSection.h"
|
||||
#include "Framework/Commands/UIAction.h"
|
||||
#include "ISequencerTrackEditor.h"
|
||||
#include "MovieSceneTrackEditor.h"
|
||||
|
||||
class FMenuBuilder;
|
||||
class UFMODEventParameterTrack;
|
||||
class UFMODAudioComponent;
|
||||
struct FMOD_STUDIO_PARAMETER_DESCRIPTION;
|
||||
|
||||
/**
|
||||
* Track editor for material parameters.
|
||||
*/
|
||||
class FFMODEventParameterTrackEditor : public FMovieSceneTrackEditor
|
||||
{
|
||||
public:
|
||||
/** Constructor. */
|
||||
FFMODEventParameterTrackEditor(TSharedRef<ISequencer> InSequencer);
|
||||
|
||||
/** Virtual destructor. */
|
||||
virtual ~FFMODEventParameterTrackEditor() {}
|
||||
|
||||
/**
|
||||
* Creates an instance of this class. Called by a sequencer.
|
||||
*
|
||||
* @param OwningSequencer The sequencer instance to be used by this tool.
|
||||
* @return The new instance of this class.
|
||||
*/
|
||||
static TSharedRef<ISequencerTrackEditor> CreateTrackEditor(TSharedRef<ISequencer> OwningSequencer);
|
||||
|
||||
// ISequencerTrackEditor interface
|
||||
|
||||
virtual TSharedPtr<SWidget> BuildOutlinerEditWidget(
|
||||
const FGuid &ObjectBinding, UMovieSceneTrack *Track, const FBuildEditWidgetParams &Params) override;
|
||||
virtual TSharedRef<ISequencerSection> MakeSectionInterface(
|
||||
UMovieSceneSection &SectionObject, UMovieSceneTrack &Track, FGuid ObjectBinding) override;
|
||||
virtual bool SupportsType(TSubclassOf<UMovieSceneTrack> Type) const override;
|
||||
|
||||
private:
|
||||
static FName TrackName;
|
||||
|
||||
// Struct used for building the parameter menu.
|
||||
struct FParameterNameAndAction
|
||||
{
|
||||
FName ParameterName;
|
||||
FUIAction Action;
|
||||
|
||||
FParameterNameAndAction(FName InParameterName, FUIAction InAction)
|
||||
{
|
||||
ParameterName = InParameterName;
|
||||
Action = InAction;
|
||||
}
|
||||
|
||||
bool operator<(FParameterNameAndAction const &Other) const { return ParameterName.FastLess(Other.ParameterName); }
|
||||
};
|
||||
|
||||
void BuildObjectBindingTrackMenu(FMenuBuilder &MenuBuilder, const TArray<FGuid> &ObjectBindings, const UClass *ObjectClass);
|
||||
|
||||
/** Provides the contents of the add parameter menu. */
|
||||
TSharedRef<SWidget> OnGetAddParameterMenuContent(FGuid ObjectBinding, UFMODEventParameterTrack *EventParameterTrack);
|
||||
TSharedRef<SWidget> BuildParameterMenu(FGuid ObjectBinding, UFMODEventParameterTrack *EventParameterTrack, UFMODAudioComponent *AudioComponent);
|
||||
|
||||
bool CanAddEventParameterTrack(FGuid ObjectBinding);
|
||||
void AddEventParameterTrack(FGuid ObjectBinding);
|
||||
|
||||
/** Adds an event parameter and initial key to a track. */
|
||||
void AddParameter(FGuid ObjectBinding, UFMODEventParameterTrack *EventParameterTrack, FName ParameterName);
|
||||
};
|
@ -0,0 +1,25 @@
|
||||
// Copyright (c), Firelight Technologies Pty, Ltd. 2012-2023.
|
||||
|
||||
#include "FMODParameterSection.h"
|
||||
#include "ISectionLayoutBuilder.h"
|
||||
#include "ScopedTransaction.h"
|
||||
#include "Sections/MovieSceneParameterSection.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FMODParameterSection"
|
||||
|
||||
bool FFMODParameterSection::RequestDeleteKeyArea(const TArray<FName> &KeyAreaNamePath)
|
||||
{
|
||||
if (KeyAreaNamePath.Num() == 1)
|
||||
{
|
||||
const FScopedTransaction Transaction(LOCTEXT("DeleteEventParameter", "Delete event parameter"));
|
||||
UMovieSceneParameterSection *ParameterSection = Cast<UMovieSceneParameterSection>(WeakSection.Get());
|
||||
if (ParameterSection->TryModify())
|
||||
{
|
||||
return ParameterSection->RemoveScalarParameter(KeyAreaNamePath[0]);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
@ -0,0 +1,21 @@
|
||||
// Copyright (c), Firelight Technologies Pty, Ltd. 2012-2023.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ISequencerSection.h"
|
||||
|
||||
class UMovieSceneSection;
|
||||
|
||||
/** A movie scene section for Event parameters. */
|
||||
class FFMODParameterSection : public FSequencerSection
|
||||
{
|
||||
public:
|
||||
FFMODParameterSection(UMovieSceneSection &InSectionObject)
|
||||
: FSequencerSection(InSectionObject)
|
||||
{
|
||||
}
|
||||
|
||||
// Begin ISequencerSection interface
|
||||
virtual bool RequestDeleteKeyArea(const TArray<FName> &KeyAreaNamePath) override;
|
||||
// End ISequencerSection interface
|
||||
};
|
Reference in New Issue
Block a user