b7f5343a73
Editor tool for the LA and 3D departments: which assets each level uses, which are used nowhere, and export of a chosen set. Three modules. AssetUsageAuditCore holds the whole analysis and links no UI and no editor-only asset pipeline, so it stays runnable from a commandlet; AssetUsageAuditEditor holds the Slate panel and everything that needs UnrealEd or AssetTools; AssetUsageAuditTests holds 152 specs. Load-bearing decisions, each of which produces a wrong answer if undone: - Dependency queries are always Package + NoRequirements, never Hard. The map-to-external-actor edges the OFPA gatherer emits carry Game|Build without Hard, so a Hard query drops all 16117 external actor packages in this project. There is deliberately no Hard constant in the code. - Crossing into another map is allowed only from a level or its external actor package. Without that rule WP_Main reported 18136 assets, of which 9994 belonged to L_MainLevel, reached through the GameMode. - The verdict has five states, never a bool. The registry cannot see FMOD events, DataTable rows or string-built paths; those are Unknown, and the tool never proposes a deletion. - Copying .uasset files does not preserve references - they are stored as full package paths. Only the Migrate layout produces something Unreal can open; the others write a manifest so the graph can be rebuilt. Co-Authored-By: Claude Code <noreply@anthropic.com>
104 lines
4.0 KiB
C++
104 lines
4.0 KiB
C++
// NextGenium 2026. Asset Usage Audit.
|
|
|
|
#pragma once
|
|
|
|
#include "CoreMinimal.h"
|
|
#include "AssetDependencyClosure.h"
|
|
#include "AssetUsageAuditTypes.h"
|
|
#include "AssetUsageExporter.h"
|
|
|
|
class IAssetRegistry;
|
|
|
|
/**
|
|
* Decides where each exported file goes.
|
|
*
|
|
* Deliberately separate from FAssetUsageExporter. The exporter's job is to copy a package to a
|
|
* path and nothing else - it has no idea what a preset or a dependency is, and keeping it that way
|
|
* is why its tests can run against invented package names. Everything that needs the Asset
|
|
* Registry, the class hierarchy or the dependency closure lives here instead, and hands the
|
|
* exporter a finished list of "these packages, into this subfolder".
|
|
*/
|
|
namespace AssetExportLayout
|
|
{
|
|
/**
|
|
* A set of packages destined for one subfolder of the export target.
|
|
*
|
|
* An alias, not a second struct: this namespace exists to produce exactly what the exporter
|
|
* consumes, and two identical types would mean a conversion loop whose only job is to prove
|
|
* they stayed identical.
|
|
*/
|
|
using FGroup = FAssetUsageExporter::FExportGroup;
|
|
|
|
struct FOptions
|
|
{
|
|
EExportLayout Layout = EExportLayout::Flat;
|
|
|
|
/**
|
|
* Sort each asset's dependencies into per-type subfolders: Texture/, Material/, and so on.
|
|
*
|
|
* Only meaningful with FolderPerAsset - there is no "each asset's dependencies" to sort in
|
|
* the other layouts. BuildGroups ignores it rather than inventing a meaning.
|
|
*
|
|
* The seed itself stays at the root of its own folder. It is the subject of that folder;
|
|
* filing it under StaticMesh/ next to its own dependencies would bury it.
|
|
*/
|
|
bool bGroupDependenciesByType = false;
|
|
};
|
|
|
|
struct ASSETUSAGEAUDITCORE_API FStats
|
|
{
|
|
int32 GroupCount = 0;
|
|
|
|
/** Distinct packages across every group. */
|
|
int32 DistinctPackages = 0;
|
|
|
|
/**
|
|
* Total placements, counting a shared dependency once per folder it lands in.
|
|
*
|
|
* This, not DistinctPackages, is the number of files that will be written. Under
|
|
* FolderPerAsset the two differ sharply and the user has to be told which one they are
|
|
* looking at before the copy starts.
|
|
*/
|
|
int32 FilePlacements = 0;
|
|
|
|
/** Folder names that collided and were given a numeric suffix. */
|
|
int32 RenamedFolders = 0;
|
|
|
|
FString Summarise() const;
|
|
};
|
|
|
|
/**
|
|
* Build the export groups.
|
|
*
|
|
* @param SeedClosures For FolderPerAsset, one entry per ticked asset from GatherPerSeed. For
|
|
* the other layouts only the union of Packages is used, so a single
|
|
* closure covering everything is enough.
|
|
*/
|
|
ASSETUSAGEAUDITCORE_API TArray<FGroup> BuildGroups(
|
|
IAssetRegistry& Registry,
|
|
const TArray<AssetDependencyClosure::FSeedClosure>& SeedClosures,
|
|
const FOptions& Options,
|
|
FStats& OutStats);
|
|
|
|
/**
|
|
* Map every class the type presets cover, and their subclasses, to a folder name.
|
|
*
|
|
* Folder names are the preset names verbatim - Texture, Material, StaticMesh - rather than a
|
|
* second set of invented labels. The Types filter in the panel already shows those words, so
|
|
* the folders a user gets match the words they filtered by.
|
|
*
|
|
* Built once per export: expanding the class hierarchy costs a registry call per preset.
|
|
*/
|
|
ASSETUSAGEAUDITCORE_API TMap<FTopLevelAssetPath, FString> BuildTypeFolderMap(IAssetRegistry& Registry);
|
|
|
|
/**
|
|
* Folder name for one package, using a map from BuildTypeFolderMap.
|
|
* Returns "Other" for anything no preset covers, never an empty string - an empty name would
|
|
* silently put the file in the parent folder and look like the grouping had failed.
|
|
*/
|
|
ASSETUSAGEAUDITCORE_API FString TypeFolderForPackage(
|
|
IAssetRegistry& Registry,
|
|
FName PackageName,
|
|
const TMap<FTopLevelAssetPath, FString>& FolderByClass);
|
|
}
|