Files
2025-05-18 13:04:45 +08:00

264 lines
7.3 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "MetaHumanCharacterAssetObserver.h"
#include "Algo/Transform.h"
#include "AssetRegistry/AssetData.h"
#include "AssetRegistry/AssetRegistryModule.h"
#include "DirectoryWatcherModule.h"
#include "IDirectoryWatcher.h"
#include "MetaHumanWardrobeItem.h"
#include "Misc/PackageName.h"
namespace UE::MetaHuman::Private
{
IDirectoryWatcher* GetDirectoryWatcherIfLoaded()
{
static const FName DirectoryWatcherModuleName = FName("DirectoryWatcher");
if (FModuleManager::Get().IsModuleLoaded(DirectoryWatcherModuleName))
{
return FModuleManager::GetModuleChecked<FDirectoryWatcherModule>(DirectoryWatcherModuleName).Get();
}
return nullptr;
}
FARCompiledFilter GetAssetFilter(FName InPackagePath, const TSet<TSubclassOf<UObject>>& InClassesToFilter)
{
FARCompiledFilter Filter;
Filter.PackagePaths.Add(InPackagePath);
for (const TSubclassOf<UObject>& Class : InClassesToFilter)
{
Filter.ClassPaths.Add(FTopLevelAssetPath(Class));
}
return Filter;
}
TArray<FAssetData> GetAssetsInDirectory(const FName& InDirectory, const TSet<TSubclassOf<UObject>>& InClassesToFilter)
{
IAssetRegistry& AssetRegistry = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry")).Get();
TArray<FAssetData> Assets;
AssetRegistry.GetAssets(GetAssetFilter(InDirectory, InClassesToFilter), Assets);
return Assets;
}
TArray<FAssetData> GetWardrobeItemsInDirectory(const FName& InDirectory, const TSet<TSubclassOf<UObject>>& InClassesToFilter)
{
TSet<FTopLevelAssetPath> ClassAssetPathsToFilter;
Algo::Transform(InClassesToFilter, ClassAssetPathsToFilter, [](TSubclassOf<UObject> Class) { return FTopLevelAssetPath(Class); });
IAssetRegistry& AssetRegistry = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry")).Get();
TArray<FAssetData> Assets;
AssetRegistry.GetAssets(GetAssetFilter(InDirectory, { UMetaHumanWardrobeItem::StaticClass() }), Assets);
for (int32 AssetIndex = Assets.Num() - 1; AssetIndex >= 0; AssetIndex--)
{
const FAssetData& Asset = Assets[AssetIndex];
const UMetaHumanWardrobeItem* WardrobeItem = Cast<UMetaHumanWardrobeItem>(Asset.GetAsset());
if (WardrobeItem)
{
FAssetData PrincipalAsset;
if (AssetRegistry.TryGetAssetByObjectPath(WardrobeItem->PrincipalAsset.ToSoftObjectPath(), PrincipalAsset) == UE::AssetRegistry::EExists::Exists
&& ClassAssetPathsToFilter.Contains(PrincipalAsset.AssetClassPath))
{
// This asset matches; keep it
continue;
}
}
// This asset doesn't match; remove it
Assets.RemoveAtSwap(AssetIndex, 1, EAllowShrinking::No);
}
return Assets;
}
}
FMetaHumanCharacterAssetObserver& FMetaHumanCharacterAssetObserver::Get()
{
static FMetaHumanCharacterAssetObserver Instance;
return Instance;
}
FMetaHumanCharacterAssetObserver::~FMetaHumanCharacterAssetObserver()
{
StopObserving();
}
bool FMetaHumanCharacterAssetObserver::IsDirectoryObserved(const FName& InDir) const
{
return ObserverData.Contains(InDir);
}
bool FMetaHumanCharacterAssetObserver::StartObserving(const FName& InDir)
{
if (ObserverData.Contains(InDir))
{
return false;
}
FString LongPackageName;
if (!FPackageName::TryConvertLongPackageNameToFilename(InDir.ToString(), LongPackageName))
{
return false;
}
if (IDirectoryWatcher* DirectoryWatcher = UE::MetaHuman::Private::GetDirectoryWatcherIfLoaded())
{
FDelegateHandle DirWatcherHandle;
const FString AbsDir = FPaths::ConvertRelativePathToFull(LongPackageName);
DirectoryWatcher->RegisterDirectoryChangedCallback_Handle(
AbsDir,
IDirectoryWatcher::FDirectoryChanged::CreateRaw(this, &FMetaHumanCharacterAssetObserver::OnDirectoryChanged, InDir),
DirWatcherHandle);
FObserverData& Data = ObserverData.Add(InDir);
Data.DirWatcherHandle = MoveTemp(DirWatcherHandle);
Data.AbsDir = AbsDir;
return true;
}
return false;
}
bool FMetaHumanCharacterAssetObserver::StopObserving(const FName& InDir)
{
if (const FObserverData* FoundData = ObserverData.Find(InDir))
{
if (IDirectoryWatcher* DirectoryWatcher = UE::MetaHuman::Private::GetDirectoryWatcherIfLoaded())
{
DirectoryWatcher->UnregisterDirectoryChangedCallback_Handle(FoundData->AbsDir, FoundData->DirWatcherHandle);
ObserverData.Remove(InDir);
return true;
}
}
return false;
}
void FMetaHumanCharacterAssetObserver::StopObserving()
{
if (IDirectoryWatcher* DirectoryWatcher = UE::MetaHuman::Private::GetDirectoryWatcherIfLoaded())
{
TArray<FName> Directories;
ObserverData.GenerateKeyArray(Directories);
for (const FName& Dir : Directories)
{
StopObserving(Dir);
}
}
}
FDelegateHandle FMetaHumanCharacterAssetObserver::SubscribeToObserver(const FName& InDir, const FOnObservedDirectoryChanged& InCallback)
{
if (FObserverData* FoundData = ObserverData.Find(InDir))
{
return FoundData->Callback.Add(InCallback);
}
return FDelegateHandle();
}
bool FMetaHumanCharacterAssetObserver::UnsubscribeFromObserver(const FName& InDir, const FOnObservedDirectoryChanged& InCallback)
{
if (FObserverData* FoundData = ObserverData.Find(InDir))
{
if (FoundData->Callback.Remove(InCallback.GetHandle()))
{
return true;
}
}
return false;
}
bool FMetaHumanCharacterAssetObserver::UnsubscribeFromObserver(const FName& InDir, const FDelegateHandle& InHandle)
{
if (FObserverData* FoundData = ObserverData.Find(InDir))
{
return FoundData->Callback.Remove(InHandle);
}
return false;
}
bool FMetaHumanCharacterAssetObserver::GetAssets(
const FName& InDir,
const TSet<TSubclassOf<UObject>>& InClassesToFilter,
TArray<FAssetData>& OutAssets)
{
if (const FObserverData* FoundData = ObserverData.Find(InDir))
{
OutAssets = UE::MetaHuman::Private::GetAssetsInDirectory(InDir, InClassesToFilter);
return true;
}
return false;
}
bool FMetaHumanCharacterAssetObserver::GetWardrobeAssets(
const FName& InDir,
const TSet<TSubclassOf<UObject>>& InClassesToFilter,
TArray<FAssetData>& OutAssets)
{
if (const FObserverData* FoundData = ObserverData.Find(InDir))
{
OutAssets = UE::MetaHuman::Private::GetWardrobeItemsInDirectory(InDir, InClassesToFilter);
return true;
}
return false;
}
void FMetaHumanCharacterAssetObserver::OnDirectoryChanged(const TArray<FFileChangeData>& InChanges, const FName InDir)
{
if (const FObserverData* FoundObserverData = ObserverData.Find(InDir))
{
FMetaHumanObserverChanges Result;
Result.Dir = InDir;
for (FFileChangeData FileChange : InChanges)
{
FString LongPackageName;
if (FPackageName::TryConvertFilenameToLongPackageName(FileChange.Filename, LongPackageName))
{
LongPackageName += FString::Printf(TEXT(".%s"), *FPaths::GetBaseFilename(FileChange.Filename));
TSoftObjectPtr<UObject> Asset = TSoftObjectPtr<UObject>(FSoftObjectPath(FTopLevelAssetPath(LongPackageName)));
if (FileChange.Action == FFileChangeData::FCA_Added)
{
Result.Changes.FindOrAdd(FMetaHumanObserverChanges::EChangeType::Added).Add(Asset);
}
else if (FileChange.Action == FFileChangeData::FCA_Removed)
{
Result.Changes.FindOrAdd(FMetaHumanObserverChanges::EChangeType::Removed).Add(Asset);
}
else if (FileChange.Action == FFileChangeData::FCA_Modified)
{
Result.Changes.FindOrAdd(FMetaHumanObserverChanges::EChangeType::Modified).Add(Asset);
}
}
}
if (FoundObserverData->Callback.IsBound())
{
FoundObserverData->Callback.Broadcast(Result);
}
}
}