LifePlay-Revival/Source/LifePlayEditorTools/LPBindLandmarkTexturesCommandlet.cpp

211 lines
7.9 KiB
C++
Raw Normal View History

// Lot STM1 (STM1-SPEC-11, M3 ; 1.4.3, STM1-SPEC-19 A1). Voir LPBindLandmarkTexturesCommandlet.h.
#include "LPBindLandmarkTexturesCommandlet.h"
#include "AssetRegistry/AssetRegistryModule.h"
#include "Engine/Texture2D.h"
#include "Factories/TextureFactory.h"
#include "HAL/FileManager.h"
#include "MaterialEditingLibrary.h"
#include "Materials/Material.h"
#include "Materials/MaterialExpressionTextureBase.h"
#include "Materials/MaterialExpressionTextureSample.h"
#include "Misc/FileHelper.h"
#include "Misc/PackageName.h"
#include "Misc/Paths.h"
#include "UObject/Package.h"
#include "UObject/SavePackage.h"
DEFINE_LOG_CATEGORY_STATIC(LogLPBindLandmarkTextures, Log, All);
namespace LPBindLandmarkTextures
{
/** 1.4.3 : aussi /lpm_city_<v>/Houses/<Nom> (arbres poses en HISM avec les maisons). */
bool IsAllowedFolder(const FString& Folder)
{
if (Folder.StartsWith(TEXT("/Game/Heavy/StreetMap/Landmarks/")))
{
return true;
}
TArray<FString> Parts;
Folder.ParseIntoArray(Parts, TEXT("/"));
return Parts.Num() == 3 && Parts[0].StartsWith(TEXT("lpm_city_")) && (Parts[1] == TEXT("Landmarks") || Parts[1] == TEXT("Houses"));
}
bool Save(UPackage* Package, UObject* Asset)
{
FSavePackageArgs Args;
Args.TopLevelFlags = RF_Public | RF_Standalone;
return UPackage::SavePackage(Package, Asset, *FPackageName::LongPackageNameToFilename(Package->GetName(), FPackageName::GetAssetPackageExtension()), Args);
}
}
ULPBindLandmarkTexturesCommandlet::ULPBindLandmarkTexturesCommandlet()
{
IsClient = false;
IsServer = false;
IsEditor = true;
LogToConsole = true;
}
int32 ULPBindLandmarkTexturesCommandlet::Main(const FString& Params)
{
using namespace LPBindLandmarkTextures;
TArray<FString> Tokens, Switches;
TMap<FString, FString> ParamsMap;
ParseCommandLine(*Params, Tokens, Switches, ParamsMap);
const FString Folder = ParamsMap.FindRef(TEXT("Folder"));
const FString TexDir = ParamsMap.FindRef(TEXT("TexDir"));
const FString ReportPath = ParamsMap.FindRef(TEXT("Report"));
const int32 MaxTexture = ParamsMap.Contains(TEXT("MaxTexture")) ? FCString::Atoi(*ParamsMap[TEXT("MaxTexture")]) : 2048;
// 1.4.3 : -Masked=<materiau>,... : feuillage ; opacite = alpha de la texture, materiau Masked et double face.
TArray<FString> MaskedNames;
ParamsMap.FindRef(TEXT("Masked")).ParseIntoArray(MaskedNames, TEXT(","));
TMap<FString, FString> Explicit;
{
TArray<FString> Pairs;
ParamsMap.FindRef(TEXT("Map")).ParseIntoArray(Pairs, TEXT(";"));
for (const FString& Pair : Pairs)
{
FString Key, Value;
if (Pair.Split(TEXT("="), &Key, &Value)) Explicit.Add(Key, Value);
}
}
TArray<FString> Report;
auto Line = [&Report](const FString& Text)
{
UE_LOG(LogLPBindLandmarkTextures, Display, TEXT("%s"), *Text);
Report.Add(Text);
};
auto Finish = [&](int32 Code)
{
Line(FString::Printf(TEXT("CODE %d"), Code));
if (!ReportPath.IsEmpty())
{
FFileHelper::SaveStringArrayToFile(Report, *ReportPath, FFileHelper::EEncodingOptions::ForceUTF8WithoutBOM);
}
return Code;
};
if (Folder.IsEmpty() || TexDir.IsEmpty())
{
Line(TEXT("usage : -Folder=</lpm_city_<v>/(Landmarks|Houses)/<Nom>> -TexDir=<dossier> [-Map=<materiau>=<fichier>;...] [-Masked=<materiau>,...] [-MaxTexture=2048] [-Report=<f.md>]"));
return Finish(2);
}
Line(FString::Printf(TEXT("# LPBindLandmarkTextures %s <- %s"), *Folder, *TexDir));
if (!IsAllowedFolder(Folder))
{
Line(TEXT("ECHEC : dossier hors de /lpm_city_*/(Landmarks|Houses)/<Nom> et de /Game/Heavy/StreetMap/Landmarks/ ; rien modifie"));
return Finish(1);
}
TArray<FAssetData> Assets;
IAssetRegistry& Registry = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry")).Get();
Registry.ScanPathsSynchronous({ Folder }, /*bForceRescan=*/true);
Registry.GetAssetsByPath(FName(*Folder), Assets, false);
int32 Bound = 0, Skipped = 0, Missing = 0;
for (const FAssetData& Data : Assets)
{
if (Data.AssetClassPath != UMaterial::StaticClass()->GetClassPathName()) continue;
UMaterial* Material = Cast<UMaterial>(Data.GetAsset());
if (!Material) continue;
// Une couleur de base deja issue d'une texture est gardee ; une couleur constante (mise par l'importeur FBX faute de
// texture reliee) est remplacee par la texture diffuse.
const UMaterialExpression* Existing = Material->GetEditorOnlyData() ? Material->GetEditorOnlyData()->BaseColor.Expression : nullptr;
if (Existing && Existing->IsA<UMaterialExpressionTextureBase>())
{
++Skipped;
Line(FString::Printf(TEXT("%s : couleur de base deja issue d'une texture (%s), rien fait"), *Material->GetName(), *Existing->GetClass()->GetName()));
continue;
}
const FString Name = Material->GetName();
TArray<FString> Candidates;
if (const FString* File = Explicit.Find(Name)) Candidates.Add(*File);
Candidates.Add(FString::Printf(TEXT("T_%s_D.png"), *Name));
Candidates.Add(Name + TEXT(".png"));
FString Found;
for (const FString& C : Candidates)
{
const FString Path = FPaths::Combine(TexDir, C);
if (IFileManager::Get().FileExists(*Path)) { Found = Path; break; }
}
if (Found.IsEmpty())
{
++Missing;
Line(FString::Printf(TEXT("%s : aucune texture trouvee (%s)"), *Name, *FString::Join(Candidates, TEXT(", "))));
continue;
}
const FString TexName = FString::Printf(TEXT("T_%s_D"), *Name);
const FString TexPackageName = Folder / TexName;
UTexture2D* Texture = nullptr;
if (FPackageName::DoesPackageExist(TexPackageName))
{
Texture = LoadObject<UTexture2D>(nullptr, *FString::Printf(TEXT("%s.%s"), *TexPackageName, *TexName));
}
if (!Texture)
{
UTextureFactory* Factory = NewObject<UTextureFactory>(GetTransientPackage());
Factory->AddToRoot();
Factory->SuppressImportOverwriteDialog();
UPackage* TexPackage = CreatePackage(*TexPackageName);
bool bCanceled = false;
Texture = Cast<UTexture2D>(Factory->ImportObject(UTexture2D::StaticClass(), TexPackage, *TexName, RF_Public | RF_Standalone, Found, nullptr, bCanceled));
Factory->RemoveFromRoot();
if (!Texture)
{
Line(FString::Printf(TEXT("ECHEC : import de %s"), *Found));
return Finish(1);
}
Texture->MaxTextureSize = MaxTexture;
Texture->PostEditChange();
FAssetRegistryModule::AssetCreated(Texture);
if (!Save(TexPackage, Texture))
{
Line(FString::Printf(TEXT("ECHEC : sauvegarde de %s"), *TexPackageName));
return Finish(1);
}
}
UMaterialExpressionTextureSample* Sample = Cast<UMaterialExpressionTextureSample>(
UMaterialEditingLibrary::CreateMaterialExpression(Material, UMaterialExpressionTextureSample::StaticClass(), -400, 0));
if (!Sample)
{
Line(FString::Printf(TEXT("ECHEC : expression de texture pour %s"), *Name));
return Finish(1);
}
Sample->Texture = Texture;
Sample->SamplerType = SAMPLERTYPE_Color;
if (!UMaterialEditingLibrary::ConnectMaterialProperty(Sample, TEXT("RGB"), MP_BaseColor))
{
Line(FString::Printf(TEXT("ECHEC : branchement de la couleur de base de %s"), *Name));
return Finish(1);
}
const bool bMasked = MaskedNames.Contains(Name);
if (bMasked)
{
Material->BlendMode = BLEND_Masked;
Material->TwoSided = true;
if (!UMaterialEditingLibrary::ConnectMaterialProperty(Sample, TEXT("A"), MP_OpacityMask))
{
Line(FString::Printf(TEXT("ECHEC : branchement de l'opacite de %s"), *Name));
return Finish(1);
}
}
UMaterialEditingLibrary::RecompileMaterial(Material);
if (!Save(Material->GetPackage(), Material))
{
Line(FString::Printf(TEXT("ECHEC : sauvegarde de %s"), *Material->GetPathName()));
return Finish(1);
}
++Bound;
Line(FString::Printf(TEXT("%s : %s -> %s (source %d x %d, plafond %d) ; remplace %s%s"), *Name, *FPaths::GetCleanFilename(Found), *TexPackageName,
Texture->Source.GetSizeX(), Texture->Source.GetSizeY(), MaxTexture, Existing ? *Existing->GetClass()->GetName() : TEXT("rien"),
bMasked ? TEXT(" ; Masked, double face, opacite = alpha") : TEXT("")));
}
Line(FString::Printf(TEXT("materiaux branches %d ; deja branches %d ; sans texture %d"), Bound, Skipped, Missing));
return Finish(0);
}