LifePlay-Revival/Source/LifePlayEditorTools/LPFixZeroScaleActorsCommandlet.cpp

224 lines
7 KiB
C++
Raw Normal View History

// Portage UE 5.5 - voir LPFixZeroScaleActorsCommandlet.h
#include "LPFixZeroScaleActorsCommandlet.h"
#include "AssetRegistry/AssetRegistryModule.h"
#include "Components/SceneComponent.h"
#include "Components/PrimitiveComponent.h"
#include "Components/StaticMeshComponent.h"
#include "Engine/StaticMesh.h"
#include "Engine/World.h"
#include "GameFramework/Actor.h"
#include "Misc/PackageName.h"
#include "UObject/Package.h"
#include "UObject/SavePackage.h"
#include "UObject/UObjectGlobals.h"
DEFINE_LOG_CATEGORY_STATIC(LogLPFixZeroScale, Log, All);
namespace
{
/** Seuil du moteur : FMath::IsNearlyZero sur chaque composante avant creation du body. */
constexpr double ZeroScaleTolerance = UE_KINDA_SMALL_NUMBER;
/** Decrit le maillage porte par un composant, pour pouvoir juger de la valeur a remettre. */
FString DescribeMesh(const USceneComponent* Component)
{
const UStaticMeshComponent* MeshComponent = Cast<UStaticMeshComponent>(Component);
if (!MeshComponent || !MeshComponent->GetStaticMesh())
{
return TEXT("(pas de maillage statique)");
}
const UStaticMesh* Mesh = MeshComponent->GetStaticMesh();
const FBoxSphereBounds Bounds = Mesh->GetBounds();
return FString::Printf(TEXT("%s demi-dimensions locales (X=%.2f Y=%.2f Z=%.2f)"),
*Mesh->GetPathName(),
Bounds.BoxExtent.X, Bounds.BoxExtent.Y, Bounds.BoxExtent.Z);
}
/**
* Visibilite et collision : ce sont elles qui dictent la valeur de remplacement.
* Un bloqueur invisible tolere une epaisseur quelconque ; un mur visible non.
*/
FString DescribeRendering(const USceneComponent* Component)
{
const UPrimitiveComponent* Primitive = Cast<UPrimitiveComponent>(Component);
if (!Primitive)
{
return TEXT("(pas un composant primitif)");
}
const UEnum* CollisionEnum = StaticEnum<ECollisionEnabled::Type>();
const ECollisionEnabled::Type Collision = Primitive->GetCollisionEnabled();
return FString::Printf(TEXT("visible=%s cacheEnJeu=%s collision=%s profil=%s"),
Primitive->IsVisible() ? TEXT("oui") : TEXT("NON"),
Primitive->bHiddenInGame ? TEXT("oui") : TEXT("non"),
CollisionEnum ? *CollisionEnum->GetNameStringByValue(static_cast<int64>(Collision)) : TEXT("?"),
*Primitive->GetCollisionProfileName().ToString());
}
}
ULPFixZeroScaleActorsCommandlet::ULPFixZeroScaleActorsCommandlet()
{
IsClient = false;
IsServer = false;
IsEditor = true;
LogToConsole = true;
}
int32 ULPFixZeroScaleActorsCommandlet::Main(const FString& Params)
{
TArray<FString> Tokens;
TArray<FString> Switches;
TMap<FString, FString> ParamsMap;
ParseCommandLine(*Params, Tokens, Switches, ParamsMap);
const bool bFix = Switches.Contains(TEXT("Fix"));
double Replacement = 1.0;
if (const FString* ScaleText = ParamsMap.Find(TEXT("Scale")))
{
Replacement = FCString::Atod(**ScaleText);
}
// Garde-fou volontaire : PowerShell coupe « -Scale=0.1 » en deux arguments si on ne le
// guillemette pas, et la commandlet recevrait alors 0 sans rien dire. Voir l'en-tete.
if (bFix && FMath::IsNearlyZero(Replacement, ZeroScaleTolerance))
{
UE_LOG(LogLPFixZeroScale, Error,
TEXT("-Scale lu a %f : valeur nulle ou argument coupe par le shell. Guillemettez \"-Scale=0.1\"."),
Replacement);
return 1;
}
UE_LOG(LogLPFixZeroScale, Warning, TEXT("=== Composantes d'echelle nulles (R-06)%s ==="),
bFix ? *FString::Printf(TEXT(" [CORRECTION -> %.4f]"), Replacement) : TEXT(" [RAPPORT SEUL]"));
FAssetRegistryModule& AssetRegistryModule =
FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
AssetRegistryModule.Get().SearchAllAssets(true);
TArray<FAssetData> MapAssets;
AssetRegistryModule.Get().GetAssetsByClass(UWorld::StaticClass()->GetClassPathName(), MapAssets, true);
int32 ComponentsFound = 0;
int32 ComponentsFixed = 0;
int32 MapsModified = 0;
int32 SaveFailures = 0;
for (const FAssetData& AssetData : MapAssets)
{
const FString PackageName = AssetData.PackageName.ToString();
if (!PackageName.StartsWith(TEXT("/Game/")))
{
continue;
}
UPackage* Package = LoadPackage(nullptr, *PackageName, LOAD_None);
UWorld* World = Package ? UWorld::FindWorldInPackage(Package) : nullptr;
if (!World || !World->PersistentLevel)
{
UE_LOG(LogLPFixZeroScale, Warning, TEXT(" %s : monde illisible, ignore."), *PackageName);
continue;
}
int32 FixedHere = 0;
for (AActor* Actor : World->PersistentLevel->Actors)
{
if (!Actor)
{
continue;
}
TArray<USceneComponent*> Components;
Actor->GetComponents<USceneComponent>(Components);
for (USceneComponent* Component : Components)
{
if (!Component)
{
continue;
}
FVector Scale = Component->GetRelativeScale3D();
const bool bZeroX = FMath::IsNearlyZero(Scale.X, ZeroScaleTolerance);
const bool bZeroY = FMath::IsNearlyZero(Scale.Y, ZeroScaleTolerance);
const bool bZeroZ = FMath::IsNearlyZero(Scale.Z, ZeroScaleTolerance);
if (!bZeroX && !bZeroY && !bZeroZ)
{
continue;
}
++ComponentsFound;
UE_LOG(LogLPFixZeroScale, Warning, TEXT(" %s : %s.%s"),
*PackageName, *Actor->GetName(), *Component->GetName());
UE_LOG(LogLPFixZeroScale, Warning, TEXT(" echelle : X=%.4f Y=%.4f Z=%.4f"),
Scale.X, Scale.Y, Scale.Z);
UE_LOG(LogLPFixZeroScale, Warning, TEXT(" maillage : %s"), *DescribeMesh(Component));
UE_LOG(LogLPFixZeroScale, Warning, TEXT(" rendu : %s"), *DescribeRendering(Component));
if (!bFix)
{
continue;
}
if (bZeroX) { Scale.X = Replacement; }
if (bZeroY) { Scale.Y = Replacement; }
if (bZeroZ) { Scale.Z = Replacement; }
Actor->Modify();
Component->Modify();
Component->SetRelativeScale3D(Scale);
Component->MarkRenderStateDirty();
// Recalcule le corps physique avec la nouvelle echelle.
Component->RecreatePhysicsState();
UE_LOG(LogLPFixZeroScale, Warning, TEXT(" -> X=%.4f Y=%.4f Z=%.4f"),
Scale.X, Scale.Y, Scale.Z);
++ComponentsFixed;
++FixedHere;
}
}
if (FixedHere > 0)
{
++MapsModified;
FString Filename;
if (!FPackageName::TryConvertLongPackageNameToFilename(
PackageName, Filename, FPackageName::GetMapPackageExtension()))
{
++SaveFailures;
UE_LOG(LogLPFixZeroScale, Error, TEXT(" chemin introuvable pour %s"), *PackageName);
continue;
}
Package->MarkPackageDirty();
FSavePackageArgs SaveArgs;
SaveArgs.TopLevelFlags = RF_Public | RF_Standalone;
SaveArgs.SaveFlags = SAVE_None;
SaveArgs.bWarnOfLongFilename = true;
SaveArgs.Error = GWarn;
if (UPackage::SavePackage(Package, World, *Filename, SaveArgs))
{
UE_LOG(LogLPFixZeroScale, Warning, TEXT(" -> %s sauvegarde (%d composant(s))."), *PackageName, FixedHere);
}
else
{
++SaveFailures;
UE_LOG(LogLPFixZeroScale, Error, TEXT(" -> ECHEC de sauvegarde de %s"), *PackageName);
}
}
}
UE_LOG(LogLPFixZeroScale, Warning,
TEXT("=== Termine : %d composant(s) a echelle nulle, %d corrige(s) dans %d carte(s), %d echec(s) de sauvegarde. ==="),
ComponentsFound, ComponentsFixed, MapsModified, SaveFailures);
return SaveFailures > 0 ? 1 : 0;
}