763 lines
39 KiB
C++
763 lines
39 KiB
C++
// Lot STM1 (STM1-SPEC-17 ; pack 1.4.0 ; STM1-DIAG-03 A1, C1, E1 : 1.4.1 ; STM1-DIAG-04 R1, STM1-SPEC-19 gazon : 1.4.2).
|
|
// Voir LPMakeCityGroundCommandlet.h.
|
|
|
|
#include "LPMakeCityGroundCommandlet.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/MaterialExpressionAbs.h"
|
|
#include "Materials/MaterialExpressionAdd.h"
|
|
#include "Materials/MaterialExpressionAppendVector.h"
|
|
#include "Materials/MaterialExpressionComponentMask.h"
|
|
#include "Materials/MaterialExpressionConstant.h"
|
|
#include "Materials/MaterialExpressionConstant2Vector.h"
|
|
#include "Materials/MaterialExpressionConstant3Vector.h"
|
|
#include "Materials/MaterialExpressionDivide.h"
|
|
#include "Materials/MaterialExpressionDotProduct.h"
|
|
#include "Materials/MaterialExpressionIf.h"
|
|
#include "Materials/MaterialExpressionLinearInterpolate.h"
|
|
#include "Materials/MaterialExpressionLocalPosition.h"
|
|
#include "Materials/MaterialExpressionMaterialFunctionCall.h"
|
|
#include "Materials/MaterialExpressionMultiply.h"
|
|
#include "Materials/MaterialExpressionOneMinus.h"
|
|
#include "Materials/MaterialExpressionPixelDepth.h"
|
|
#include "Materials/MaterialExpressionPower.h"
|
|
#include "Materials/MaterialExpressionSaturate.h"
|
|
#include "Materials/MaterialExpressionScalarParameter.h"
|
|
#include "Materials/MaterialExpressionSubtract.h"
|
|
#include "Materials/MaterialExpressionTextureCoordinate.h"
|
|
#include "Materials/MaterialExpressionTextureSample.h"
|
|
#include "Materials/MaterialExpressionUtils.h"
|
|
#include "Materials/MaterialExpressionVectorParameter.h"
|
|
#include "Materials/MaterialExpressionVertexColor.h"
|
|
#include "Materials/MaterialExpressionVertexNormalWS.h"
|
|
#include "Materials/MaterialExpressionWorldPosition.h"
|
|
#include "Materials/MaterialFunction.h"
|
|
#include "Materials/MaterialInstance.h"
|
|
#include "Materials/MaterialInstanceConstant.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(LogLPMakeCityGround, Log, All);
|
|
|
|
namespace LPMakeCityGround
|
|
{
|
|
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);
|
|
}
|
|
|
|
/** Graphe de materiau : creation d'expressions placees en colonnes, branchement par les entrees. */
|
|
struct FGraph
|
|
{
|
|
UMaterial* Material = nullptr;
|
|
int32 Y = 0;
|
|
|
|
template <class T>
|
|
T* Add(int32 Column)
|
|
{
|
|
Y += 60;
|
|
return Cast<T>(UMaterialEditingLibrary::CreateMaterialExpression(Material, T::StaticClass(), -300 * Column, Y));
|
|
}
|
|
UMaterialExpression* Scalar(const TCHAR* Name, float Value)
|
|
{
|
|
UMaterialExpressionScalarParameter* P = Add<UMaterialExpressionScalarParameter>(8);
|
|
P->ParameterName = Name;
|
|
P->DefaultValue = Value;
|
|
return P;
|
|
}
|
|
UMaterialExpression* Const(float Value)
|
|
{
|
|
UMaterialExpressionConstant* C = Add<UMaterialExpressionConstant>(8);
|
|
C->R = Value;
|
|
return C;
|
|
}
|
|
UMaterialExpression* Vector(const TCHAR* Name, const FLinearColor& Value)
|
|
{
|
|
UMaterialExpressionVectorParameter* P = Add<UMaterialExpressionVectorParameter>(8);
|
|
P->ParameterName = Name;
|
|
P->DefaultValue = Value;
|
|
return P;
|
|
}
|
|
UMaterialExpression* Const3(const FLinearColor& Value)
|
|
{
|
|
UMaterialExpressionConstant3Vector* C = Add<UMaterialExpressionConstant3Vector>(8);
|
|
C->Constant = Value;
|
|
return C;
|
|
}
|
|
UMaterialExpression* Mask(UMaterialExpression* In, bool R, bool G, bool B, int32 Output = 0)
|
|
{
|
|
UMaterialExpressionComponentMask* M = Add<UMaterialExpressionComponentMask>(7);
|
|
M->Input.Connect(Output, In);
|
|
M->R = R; M->G = G; M->B = B; M->A = false;
|
|
return M;
|
|
}
|
|
template <class T>
|
|
UMaterialExpression* Binary(UMaterialExpression* A, UMaterialExpression* B, int32 Column = 5)
|
|
{
|
|
T* E = Add<T>(Column);
|
|
E->A.Connect(0, A);
|
|
E->B.Connect(0, B);
|
|
return E;
|
|
}
|
|
UMaterialExpression* Lerp(UMaterialExpression* A, UMaterialExpression* B, UMaterialExpression* Alpha, int32 OutA = 0, int32 OutB = 0)
|
|
{
|
|
UMaterialExpressionLinearInterpolate* L = Add<UMaterialExpressionLinearInterpolate>(3);
|
|
L->A.Connect(OutA, A);
|
|
L->B.Connect(OutB, B);
|
|
L->Alpha.Connect(0, Alpha);
|
|
return L;
|
|
}
|
|
/** If : A > B -> Greater, sinon -> Other (egalite comprise). */
|
|
UMaterialExpression* IfGreater(UMaterialExpression* A, UMaterialExpression* B, UMaterialExpression* Greater, UMaterialExpression* Other, int32 OutGreater = 0, int32 OutOther = 0)
|
|
{
|
|
UMaterialExpressionIf* I = Add<UMaterialExpressionIf>(2);
|
|
I->A.Connect(0, A);
|
|
I->B.Connect(0, B);
|
|
I->AGreaterThanB.Connect(OutGreater, Greater);
|
|
I->AEqualsB.Connect(OutOther, Other);
|
|
I->ALessThanB.Connect(OutOther, Other);
|
|
return I;
|
|
}
|
|
UMaterialExpressionTextureSample* Sample(UTexture* Texture, UMaterialExpression* Uv)
|
|
{
|
|
UMaterialExpressionTextureSample* S = Add<UMaterialExpressionTextureSample>(4);
|
|
S->Texture = Texture;
|
|
S->SamplerType = MaterialExpressionUtils::GetSamplerTypeForTexture(Texture);
|
|
if (Uv) S->Coordinates.Connect(0, Uv);
|
|
return S;
|
|
}
|
|
};
|
|
|
|
// Sorties d'un echantillon de texture : 0 RGB, 1 R, 2 G, 3 B, 4 A.
|
|
constexpr int32 OutRGB = 0, OutR = 1, OutG = 2;
|
|
}
|
|
|
|
ULPMakeCityGroundCommandlet::ULPMakeCityGroundCommandlet()
|
|
{
|
|
IsClient = false;
|
|
IsServer = false;
|
|
IsEditor = true;
|
|
LogToConsole = true;
|
|
}
|
|
|
|
int32 ULPMakeCityGroundCommandlet::Main(const FString& Params)
|
|
{
|
|
using namespace LPMakeCityGround;
|
|
|
|
TArray<FString> Tokens, Switches;
|
|
TMap<FString, FString> ParamsMap;
|
|
ParseCommandLine(*Params, Tokens, Switches, ParamsMap);
|
|
const FString Root = ParamsMap.FindRef(TEXT("Root"));
|
|
const FString City = ParamsMap.FindRef(TEXT("City"));
|
|
const FString SpecPath = ParamsMap.FindRef(TEXT("Spec"));
|
|
const FString ReportPath = ParamsMap.FindRef(TEXT("Report"));
|
|
const int32 ColorMax = ParamsMap.Contains(TEXT("ColorMax")) ? FCString::Atoi(*ParamsMap[TEXT("ColorMax")]) : 2048;
|
|
const int32 DetailMax = ParamsMap.Contains(TEXT("DetailMax")) ? FCString::Atoi(*ParamsMap[TEXT("DetailMax")]) : 1024;
|
|
const FString RoadTexturePath = ParamsMap.Contains(TEXT("RoadTexture")) ? ParamsMap[TEXT("RoadTexture")] : FString(TEXT("/Game/Heavy/New2/Buildings/Mats/road.road"));
|
|
const FString PavementMaterialPath = ParamsMap.Contains(TEXT("PavementMaterial")) ? ParamsMap[TEXT("PavementMaterial")]
|
|
: FString(TEXT("/Game/Heavy/New2/Buildings/Mats/PavementMat.PavementMat"));
|
|
|
|
TArray<FString> Report;
|
|
auto Line = [&Report](const FString& Text)
|
|
{
|
|
UE_LOG(LogLPMakeCityGround, 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;
|
|
};
|
|
|
|
TArray<FString> RootParts;
|
|
Root.ParseIntoArray(RootParts, TEXT("/"));
|
|
if (City.IsEmpty() || SpecPath.IsEmpty() || RootParts.Num() != 1 || !RootParts[0].StartsWith(TEXT("lpm_city_")))
|
|
{
|
|
Line(TEXT("usage : -Root=/lpm_city_<v> -City=<Ville> -Spec=<fichier> [-ColorMax=2048] [-DetailMax=1024] [-RoadTexture=<objet>] [-PavementMaterial=<objet>] [-SquareOwnFar] [-Report=<f.md>]"));
|
|
return Finish(2);
|
|
}
|
|
Line(FString::Printf(TEXT("# LPMakeCityGround %s (%s) ; couleur %d, normales et ORM %d"), *Root, *City, ColorMax, DetailMax));
|
|
|
|
// Fichier de description : Cle=chemin.
|
|
TArray<FString> SpecLines;
|
|
if (!FFileHelper::LoadFileToStringArray(SpecLines, *SpecPath))
|
|
{
|
|
Line(FString::Printf(TEXT("ECHEC : lecture de %s ; aucun asset ecrit"), *SpecPath));
|
|
return Finish(1);
|
|
}
|
|
TMap<FString, FString> Spec;
|
|
for (const FString& Raw : SpecLines)
|
|
{
|
|
FString Key, Value;
|
|
if (Raw.StartsWith(TEXT("#")) || !Raw.Split(TEXT("="), &Key, &Value)) continue;
|
|
Spec.Add(Key.TrimStartAndEnd(), Value.TrimStartAndEnd());
|
|
}
|
|
TArray<FString> FlipGreen;
|
|
Spec.FindRef(TEXT("FlipGreen")).ParseIntoArray(FlipGreen, TEXT(","));
|
|
|
|
const FString MaterialsPath = Root / TEXT("Materials");
|
|
const FString GroundPackageName = FString::Printf(TEXT("%s/StreetMap/Ground/%s"), *Root, *City);
|
|
const FString GroundMaterialPackage = MaterialsPath / TEXT("M_LP_CityGround");
|
|
const FString SidewalkMaterialPackage = MaterialsPath / TEXT("M_LP_CitySidewalk");
|
|
const FString SquareMaterialPackage = MaterialsPath / TEXT("M_LP_CitySquare");
|
|
const FString GreenMaterialPackage = MaterialsPath / TEXT("M_LP_CityGreen");
|
|
// 1.5.1 (STM1-SPEC-21 § 2 a) : pierre de taille triplanaire des monuments sans UV (StoneBC, StoneORM dans -Spec).
|
|
const FString LandmarkStonePackage = MaterialsPath / TEXT("M_LP_LandmarkStone");
|
|
const bool bStone = Spec.Contains(TEXT("StoneBC"));
|
|
// 1.5.1 : paves propres aux places (PlaceBC, PlaceN, PlaceORM ; sinon les paves des routes) et dalles (SlabBC, SlabN, SlabORM :
|
|
// M_LP_CitySlab, couche 3 de LPBuildCitySidewalks).
|
|
const bool bPlace = Spec.Contains(TEXT("PlaceBC"));
|
|
const bool bSlab = Spec.Contains(TEXT("SlabBC"));
|
|
const FString SlabMaterialPackage = MaterialsPath / TEXT("M_LP_CitySlab");
|
|
// 1.5.2 (STM1-DIAG-06 § 3 ter) : SandTint=R,G,B -> M_LP_CitySand (stabilise des allees : beton teinte sable).
|
|
const bool bSand = Spec.Contains(TEXT("SandTint"));
|
|
const FString SandMaterialPackage = MaterialsPath / TEXT("M_LP_CitySand");
|
|
for (const FString& P : { GroundPackageName, GroundMaterialPackage, SidewalkMaterialPackage, SquareMaterialPackage, GreenMaterialPackage, LandmarkStonePackage, SlabMaterialPackage, SandMaterialPackage })
|
|
{
|
|
if (FPackageName::DoesPackageExist(P))
|
|
{
|
|
Line(FString::Printf(TEXT("ECHEC : %s existe deja ; aucun asset ecrit"), *P));
|
|
return Finish(1);
|
|
}
|
|
}
|
|
UTexture* RoadTexture = LoadObject<UTexture>(nullptr, *RoadTexturePath);
|
|
UMaterialInterface* PavementMaterial = LoadObject<UMaterialInterface>(nullptr, *PavementMaterialPath);
|
|
if (!RoadTexture || !PavementMaterial)
|
|
{
|
|
Line(FString::Printf(TEXT("ECHEC : texture road (%d) ou materiau du plan de pave (%d) introuvable ; aucun asset ecrit"), RoadTexture ? 1 : 0, PavementMaterial ? 1 : 0));
|
|
return Finish(1);
|
|
}
|
|
|
|
// Moyenne lineaire d'une texture, lue dans sa source (BGRA8 seulement) ; Out garde sa valeur par defaut sinon.
|
|
auto AverageColor = [&Line](UTexture* Texture, const TCHAR* Label, FLinearColor& Out)
|
|
{
|
|
TArray64<uint8> Mip;
|
|
const ETextureSourceFormat Format = Texture->Source.GetFormat();
|
|
if (Format == TSF_BGRA8 && Texture->Source.GetMipData(Mip, 0) && Mip.Num() >= 4)
|
|
{
|
|
double Sum[3] = { 0.0, 0.0, 0.0 };
|
|
const int64 Count = Mip.Num() / 4;
|
|
for (int64 I = 0; I < Count; ++I)
|
|
{
|
|
const FColor C(Mip[I * 4 + 2], Mip[I * 4 + 1], Mip[I * 4 + 0], 255);
|
|
const FLinearColor L = Texture->SRGB ? FLinearColor(C) : C.ReinterpretAsLinear();
|
|
Sum[0] += L.R; Sum[1] += L.G; Sum[2] += L.B;
|
|
}
|
|
Out = FLinearColor(float(Sum[0] / Count), float(Sum[1] / Count), float(Sum[2] / Count));
|
|
Line(FString::Printf(TEXT("texture %s (%s) : %d x %d, sRGB %d ; moyenne lineaire %.3f, %.3f, %.3f (couleur lointaine)"), Label, *Texture->GetPathName(),
|
|
Texture->Source.GetSizeX(), Texture->Source.GetSizeY(), Texture->SRGB ? 1 : 0, Out.R, Out.G, Out.B));
|
|
}
|
|
else
|
|
{
|
|
Line(FString::Printf(TEXT("texture %s : format source %d non lu ; couleur lointaine par defaut %.2f, %.2f, %.2f"), Label, int32(Format), Out.R, Out.G, Out.B));
|
|
}
|
|
};
|
|
|
|
// 1.4.1 (C1) : au loin, couleur unie egale a la moyenne de la texture road du jeu (plus d'etirement par segment).
|
|
FLinearColor FarColor(0.8f, 0.78f, 0.74f);
|
|
AverageColor(RoadTexture, TEXT("road"), FarColor);
|
|
|
|
// 1.4.2 (R1) : au loin, trottoirs et pierre prennent la couleur moyenne du plan de pave de MapMarkers (premiere texture
|
|
// de l'instance, sinon la premiere texture utilisee), pour se confondre avec lui sans tramage.
|
|
// Le plan (instance de Bu_Base : base = MapDiffuse x Color ; Metal, Roughness, Specular) : au loin, memes reglages.
|
|
FLinearColor PavementFar(0.6f, 0.58f, 0.56f);
|
|
FLinearColor PavementTint(1.f, 1.f, 1.f);
|
|
float PavementMetal = 0.f, PavementRough = 0.8f, PavementSpecular = 0.f;
|
|
{
|
|
UTexture* PavementTexture = nullptr;
|
|
if (const UMaterialInstance* Instance = Cast<UMaterialInstance>(PavementMaterial))
|
|
{
|
|
for (const FTextureParameterValue& P : Instance->TextureParameterValues)
|
|
{
|
|
Line(FString::Printf(TEXT("plan de pave : parametre texture %s = %s"), *P.ParameterInfo.Name.ToString(), *GetPathNameSafe(P.ParameterValue)));
|
|
PavementTexture = PavementTexture ? PavementTexture : P.ParameterValue.Get();
|
|
}
|
|
for (const FVectorParameterValue& P : Instance->VectorParameterValues)
|
|
{
|
|
Line(FString::Printf(TEXT("plan de pave : parametre vecteur %s = %.3f, %.3f, %.3f, %.3f"), *P.ParameterInfo.Name.ToString(),
|
|
P.ParameterValue.R, P.ParameterValue.G, P.ParameterValue.B, P.ParameterValue.A));
|
|
if (P.ParameterInfo.Name == TEXT("Color")) PavementTint = P.ParameterValue;
|
|
}
|
|
for (const FScalarParameterValue& P : Instance->ScalarParameterValues)
|
|
{
|
|
Line(FString::Printf(TEXT("plan de pave : parametre scalaire %s = %.3f"), *P.ParameterInfo.Name.ToString(), P.ParameterValue));
|
|
if (P.ParameterInfo.Name == TEXT("Metal")) PavementMetal = P.ParameterValue;
|
|
if (P.ParameterInfo.Name == TEXT("Roughness")) PavementRough = P.ParameterValue;
|
|
if (P.ParameterInfo.Name == TEXT("Specular")) PavementSpecular = P.ParameterValue;
|
|
}
|
|
}
|
|
if (!PavementTexture)
|
|
{
|
|
TArray<UTexture*> Used;
|
|
PavementMaterial->GetUsedTextures(Used, EMaterialQualityLevel::High);
|
|
PavementTexture = Used.IsEmpty() ? nullptr : Used[0];
|
|
}
|
|
if (PavementTexture)
|
|
{
|
|
AverageColor(PavementTexture, TEXT("du plan de pave"), PavementFar);
|
|
PavementFar = PavementFar * PavementTint;
|
|
PavementFar.A = 1.f;
|
|
Line(FString::Printf(TEXT("plan de pave : couleur lointaine (moyenne x Color) %.3f, %.3f, %.3f ; metal %.2f, rugosite %.2f, specular %.2f"),
|
|
PavementFar.R, PavementFar.G, PavementFar.B, PavementMetal, PavementRough, PavementSpecular));
|
|
}
|
|
else
|
|
{
|
|
Line(FString::Printf(TEXT("plan de pave : aucune texture lue ; couleur lointaine par defaut %.2f, %.2f, %.2f"), PavementFar.R, PavementFar.G, PavementFar.B));
|
|
}
|
|
}
|
|
|
|
// Textures (1.4.1 : celles deja importees sont reprises telles quelles).
|
|
TArray<FString> Keys = { TEXT("AsphaltBC"), TEXT("AsphaltN"), TEXT("AsphaltORM"), TEXT("CobbleBC"), TEXT("CobbleN"), TEXT("CobbleORM"),
|
|
TEXT("ConcreteBC"), TEXT("ConcreteN"), TEXT("ConcreteORM") };
|
|
// 1.4.2 (SPEC-19 § 4) : gazon facultatif, les trois cles ensemble.
|
|
const bool bGrass = Spec.Contains(TEXT("GrassBC"));
|
|
if (bGrass)
|
|
{
|
|
Keys.Append({ TEXT("GrassBC"), TEXT("GrassN"), TEXT("GrassORM") });
|
|
}
|
|
if (bStone)
|
|
{
|
|
Keys.Append({ TEXT("StoneBC"), TEXT("StoneORM") });
|
|
}
|
|
if (bPlace)
|
|
{
|
|
Keys.Append({ TEXT("PlaceBC"), TEXT("PlaceN"), TEXT("PlaceORM") });
|
|
}
|
|
if (bSlab)
|
|
{
|
|
Keys.Append({ TEXT("SlabBC"), TEXT("SlabN"), TEXT("SlabORM") });
|
|
}
|
|
TMap<FString, UTexture2D*> Textures;
|
|
for (const FString& Key : Keys)
|
|
{
|
|
const FString File = Spec.FindRef(Key);
|
|
// 1.5.1 : Cle=@AutreCle reprend une texture deja lue (repli de la pierre sur le beton, sans second import).
|
|
if (File.StartsWith(TEXT("@")))
|
|
{
|
|
UTexture2D* const* Same = Textures.Find(File.RightChop(1));
|
|
if (!Same)
|
|
{
|
|
Line(FString::Printf(TEXT("ECHEC : %s renvoie a %s, absente ou lue apres ; arret"), *Key, *File));
|
|
return Finish(1);
|
|
}
|
|
Textures.Add(Key, *Same);
|
|
Line(FString::Printf(TEXT("texture %s = %s (%s)"), *Key, *File, *(*Same)->GetPathName()));
|
|
continue;
|
|
}
|
|
if (File.IsEmpty() || !IFileManager::Get().FileExists(*File))
|
|
{
|
|
Line(FString::Printf(TEXT("ECHEC : %s absent ou introuvable (%s) ; arret"), *Key, *File));
|
|
return Finish(1);
|
|
}
|
|
const FString Name = FString::Printf(TEXT("T_LP_%s"), *Key);
|
|
const FString PackageName = MaterialsPath / TEXT("Textures") / Name;
|
|
if (FPackageName::DoesPackageExist(PackageName))
|
|
{
|
|
UTexture2D* Existing = LoadObject<UTexture2D>(nullptr, *FString::Printf(TEXT("%s.%s"), *PackageName, *Name));
|
|
if (!Existing)
|
|
{
|
|
Line(FString::Printf(TEXT("ECHEC : %s illisible ; arret"), *PackageName));
|
|
return Finish(1);
|
|
}
|
|
Textures.Add(Key, Existing);
|
|
Line(FString::Printf(TEXT("texture %s reprise (deja importee)"), *PackageName));
|
|
continue;
|
|
}
|
|
UTextureFactory* Factory = NewObject<UTextureFactory>(GetTransientPackage());
|
|
Factory->AddToRoot();
|
|
Factory->SuppressImportOverwriteDialog();
|
|
UPackage* Package = CreatePackage(*PackageName);
|
|
bool bCanceled = false;
|
|
UTexture2D* Texture = Cast<UTexture2D>(Factory->ImportObject(UTexture2D::StaticClass(), Package, *Name, RF_Public | RF_Standalone, File, nullptr, bCanceled));
|
|
Factory->RemoveFromRoot();
|
|
if (!Texture)
|
|
{
|
|
Line(FString::Printf(TEXT("ECHEC : import de %s ; arret"), *File));
|
|
return Finish(1);
|
|
}
|
|
const FString& K = Key;
|
|
const bool bNormal = K.EndsWith(TEXT("N"));
|
|
const bool bOrm = K.EndsWith(TEXT("ORM"));
|
|
if (bNormal)
|
|
{
|
|
Texture->CompressionSettings = TC_Normalmap;
|
|
Texture->SRGB = false;
|
|
Texture->LODGroup = TEXTUREGROUP_WorldNormalMap;
|
|
Texture->bFlipGreenChannel = FlipGreen.Contains(K);
|
|
Texture->MaxTextureSize = DetailMax;
|
|
}
|
|
else if (bOrm)
|
|
{
|
|
Texture->CompressionSettings = TC_Masks;
|
|
Texture->SRGB = false;
|
|
Texture->MaxTextureSize = DetailMax;
|
|
}
|
|
else
|
|
{
|
|
Texture->CompressionSettings = TC_Default;
|
|
Texture->SRGB = true;
|
|
Texture->MaxTextureSize = ColorMax;
|
|
}
|
|
Texture->PostEditChange();
|
|
FAssetRegistryModule::AssetCreated(Texture);
|
|
if (!Save(Package, Texture))
|
|
{
|
|
Line(FString::Printf(TEXT("ECHEC : sauvegarde de %s ; arret"), *PackageName));
|
|
return Finish(1);
|
|
}
|
|
Textures.Add(K, Texture);
|
|
Line(FString::Printf(TEXT("texture %s <- %s : source %d x %d ; plafond %d ; %s%s"), *PackageName, *FPaths::GetCleanFilename(File),
|
|
Texture->Source.GetSizeX(), Texture->Source.GetSizeY(), Texture->MaxTextureSize,
|
|
bNormal ? TEXT("normale") : bOrm ? TEXT("ORM lineaire") : TEXT("couleur sRGB"), Texture->bFlipGreenChannel ? TEXT(" (vert retourne)") : TEXT("")));
|
|
}
|
|
|
|
// 1.4.2 (SPEC-19 § 4) : gazon des parcs et du vert. Couleur lointaine = moyenne de sa texture.
|
|
FLinearColor GrassFar(0.f, 0.1f, 0.f);
|
|
if (bGrass)
|
|
{
|
|
AverageColor(Textures[TEXT("GrassBC")], TEXT("GrassBC"), GrassFar);
|
|
}
|
|
struct FSurface { UMaterialExpression* Base; UMaterialExpression* Emissive; UMaterialExpression* Normal; UMaterialExpression* Rough; UMaterialExpression* AO; };
|
|
// Gazon sur la position locale XY : deux echantillons (2 m, et 7,3 m tourne de 37 degres) melanges, teinte (GrassTint),
|
|
// fondu vers GrassFarColor ; emission = base x GrassEmissive, comme les parcs du jeu (base et emission 0 ; 0,1 ; 0).
|
|
auto GrassSurface = [&](FGraph& G, UMaterialExpression* XY, UMaterialExpression* Fade, UMaterialExpression* Up) -> FSurface
|
|
{
|
|
UMaterialExpression* Uv1 = G.Binary<UMaterialExpressionDivide>(XY, G.Scalar(TEXT("GrassTileCm"), 200.f), 6);
|
|
UMaterialExpression* XY2 = G.Binary<UMaterialExpressionDivide>(XY, G.Scalar(TEXT("GrassTile2Cm"), 730.f), 6);
|
|
const float C = FMath::Cos(FMath::DegreesToRadians(37.f)), S = FMath::Sin(FMath::DegreesToRadians(37.f));
|
|
UMaterialExpressionConstant2Vector* RotU = G.Add<UMaterialExpressionConstant2Vector>(8); RotU->R = C; RotU->G = S;
|
|
UMaterialExpressionConstant2Vector* RotV = G.Add<UMaterialExpressionConstant2Vector>(8); RotV->R = -S; RotV->G = C;
|
|
UMaterialExpression* Uv2 = G.Binary<UMaterialExpressionAppendVector>(G.Binary<UMaterialExpressionDotProduct>(XY2, RotU, 6), G.Binary<UMaterialExpressionDotProduct>(XY2, RotV, 6), 6);
|
|
UMaterialExpression* B1 = G.Sample(Textures[TEXT("GrassBC")], Uv1);
|
|
UMaterialExpression* B2 = G.Sample(Textures[TEXT("GrassBC")], Uv2);
|
|
UMaterialExpression* N = G.Sample(Textures[TEXT("GrassN")], Uv1);
|
|
UMaterialExpression* ORM = G.Sample(Textures[TEXT("GrassORM")], Uv1);
|
|
UMaterialExpression* Near = G.Binary<UMaterialExpressionMultiply>(G.Lerp(B1, B2, G.Const(0.5f)), G.Vector(TEXT("GrassTint"), FLinearColor(1.f, 1.f, 1.f)), 4);
|
|
UMaterialExpression* Base = G.Lerp(Near, G.Vector(TEXT("GrassFarColor"), GrassFar), Fade);
|
|
return FSurface{ Base, G.Binary<UMaterialExpressionMultiply>(Base, G.Scalar(TEXT("GrassEmissive"), 1.f), 3), G.Lerp(N, Up, Fade),
|
|
G.Lerp(ORM, G.Const(0.8f), Fade, OutG), G.Lerp(ORM, G.Const(1.f), Fade, OutR) };
|
|
};
|
|
|
|
// M_LP_CityGround.
|
|
UPackage* GroundMaterialPkg = CreatePackage(*GroundMaterialPackage);
|
|
UMaterial* Ground = NewObject<UMaterial>(GroundMaterialPkg, TEXT("M_LP_CityGround"), RF_Public | RF_Standalone);
|
|
Ground->BlendMode = BLEND_Masked;
|
|
{
|
|
FGraph G{ Ground };
|
|
UMaterialExpression* VC = G.Add<UMaterialExpressionVertexColor>(8);
|
|
UMaterialExpression* R = G.Mask(VC, true, false, false);
|
|
UMaterialExpression* Gc = G.Mask(VC, false, true, false);
|
|
UMaterialExpression* XY = G.Mask(G.Add<UMaterialExpressionLocalPosition>(8), true, true, false);
|
|
UMaterialExpression* Uv0 = G.Add<UMaterialExpressionTextureCoordinate>(8);
|
|
UMaterialExpression* U0 = G.Mask(Uv0, true, false, false);
|
|
|
|
UMaterialExpression* UvA1 = G.Binary<UMaterialExpressionDivide>(XY, G.Scalar(TEXT("AsphaltTileCm"), 160.f), 6);
|
|
// Second echantillon : echelle 5,3 m et rotation de 37 degres (u' = x cos + y sin, v' = -x sin + y cos).
|
|
UMaterialExpression* XY2 = G.Binary<UMaterialExpressionDivide>(XY, G.Scalar(TEXT("AsphaltTile2Cm"), 530.f), 6);
|
|
const float C = FMath::Cos(FMath::DegreesToRadians(37.f)), S = FMath::Sin(FMath::DegreesToRadians(37.f));
|
|
UMaterialExpressionConstant2Vector* RotU = G.Add<UMaterialExpressionConstant2Vector>(8); RotU->R = C; RotU->G = S;
|
|
UMaterialExpressionConstant2Vector* RotV = G.Add<UMaterialExpressionConstant2Vector>(8); RotV->R = -S; RotV->G = C;
|
|
UMaterialExpression* UvA2 = G.Binary<UMaterialExpressionAppendVector>(G.Binary<UMaterialExpressionDotProduct>(XY2, RotU, 6), G.Binary<UMaterialExpressionDotProduct>(XY2, RotV, 6), 6);
|
|
UMaterialExpression* UvC = G.Binary<UMaterialExpressionDivide>(XY, G.Scalar(TEXT("CobbleTileCm"), 200.f), 6);
|
|
UMaterialExpression* UvM = G.Binary<UMaterialExpressionDivide>(XY, G.Scalar(TEXT("MacroTileCm"), 4000.f), 6);
|
|
|
|
UMaterialExpression* A1 = G.Sample(Textures[TEXT("AsphaltBC")], UvA1);
|
|
UMaterialExpression* A2 = G.Sample(Textures[TEXT("AsphaltBC")], UvA2);
|
|
UMaterialExpression* AN = G.Sample(Textures[TEXT("AsphaltN")], UvA1);
|
|
UMaterialExpression* AORM = G.Sample(Textures[TEXT("AsphaltORM")], UvA1);
|
|
UMaterialExpression* CBC = G.Sample(Textures[TEXT("CobbleBC")], UvC);
|
|
UMaterialExpression* CN = G.Sample(Textures[TEXT("CobbleN")], UvC);
|
|
UMaterialExpression* CORM = G.Sample(Textures[TEXT("CobbleORM")], UvC);
|
|
UMaterialExpression* Macro = G.Sample(Textures[TEXT("ConcreteBC")], UvM);
|
|
|
|
// Asphalte : melange des deux echantillons, puis macro-variation (1 + (luminance - 0,5) x 2 x MacroStrength).
|
|
UMaterialExpression* Asphalt = G.Lerp(A1, A2, G.Const(0.5f));
|
|
UMaterialExpression* Luma = G.Binary<UMaterialExpressionDotProduct>(Macro, G.Const3(FLinearColor(0.3f, 0.59f, 0.11f)), 5);
|
|
UMaterialExpression* MacroMul = G.Binary<UMaterialExpressionAdd>(G.Const(1.f), G.Binary<UMaterialExpressionMultiply>(
|
|
G.Binary<UMaterialExpressionSubtract>(Luma, G.Const(0.5f)), G.Binary<UMaterialExpressionMultiply>(G.Const(2.f), G.Scalar(TEXT("MacroStrength"), 0.08f))));
|
|
Asphalt = G.Binary<UMaterialExpressionMultiply>(Asphalt, MacroMul, 4);
|
|
|
|
// Paves si U > 1,5.
|
|
UMaterialExpression* Paved = G.IfGreater(U0, G.Const(1.5f), G.Const(1.f), G.Const(0.f));
|
|
UMaterialExpression* NearBC = G.Lerp(Asphalt, CBC, Paved);
|
|
UMaterialExpression* NearN = G.Lerp(AN, CN, Paved);
|
|
UMaterialExpression* NearRough = G.Lerp(AORM, CORM, Paved, OutG, OutG);
|
|
UMaterialExpression* NearAO = G.Lerp(AORM, CORM, Paved, OutR, OutR);
|
|
|
|
// Fondu avec la distance : 0 pres, 1 loin (texture road du jeu, comme StreetMapMasked).
|
|
UMaterialExpression* FadeNear = G.Scalar(TEXT("FadeNearCm"), 6000.f);
|
|
UMaterialExpression* Fade = G.Add<UMaterialExpressionSaturate>(3);
|
|
Cast<UMaterialExpressionSaturate>(Fade)->Input.Connect(0, G.Binary<UMaterialExpressionDivide>(
|
|
G.Binary<UMaterialExpressionSubtract>(G.Add<UMaterialExpressionPixelDepth>(8), FadeNear),
|
|
G.Binary<UMaterialExpressionSubtract>(G.Scalar(TEXT("FadeFarCm"), 20000.f), FadeNear)));
|
|
|
|
UMaterialExpression* Park = G.Const3(FLinearColor(0.f, 0.1f, 0.f));
|
|
UMaterialExpression* Up = G.Const3(FLinearColor(0.f, 0.f, 1.f));
|
|
UMaterialExpression* Far = G.Vector(TEXT("FarColor"), FarColor);
|
|
UMaterialExpression* RoadBase = G.Lerp(NearBC, Far, Fade);
|
|
UMaterialExpression* RoadEmissive = G.Binary<UMaterialExpressionMultiply>(Far, Fade, 3);
|
|
UMaterialExpression* RoadNormal = G.Lerp(NearN, Up, Fade);
|
|
UMaterialExpression* RoadRough = G.Lerp(NearRough, G.Const(0.75f), Fade);
|
|
UMaterialExpression* RoadAO = G.Lerp(NearAO, G.Const(1.f), Fade);
|
|
UMaterialExpression* Threshold = G.Const(0.9f);
|
|
|
|
UMaterialExpression* Opacity = G.IfGreater(R, Gc, G.Const(1.f), G.Const(0.f));
|
|
// R < V (parcs) : visible aussi.
|
|
Cast<UMaterialExpressionIf>(Opacity)->ALessThanB.Connect(0, G.Const(1.f));
|
|
|
|
// Parcs : vert uni, ou gazon en 1.4.2.
|
|
const FSurface ParkSurface = bGrass ? GrassSurface(G, XY, Fade, Up) : FSurface{ Park, Park, Up, G.Const(0.75f), G.Const(1.f) };
|
|
const bool bLinked =
|
|
UMaterialEditingLibrary::ConnectMaterialProperty(G.IfGreater(R, Threshold, RoadBase, ParkSurface.Base), TEXT(""), MP_BaseColor)
|
|
&& UMaterialEditingLibrary::ConnectMaterialProperty(G.IfGreater(R, Threshold, RoadEmissive, ParkSurface.Emissive), TEXT(""), MP_EmissiveColor)
|
|
&& UMaterialEditingLibrary::ConnectMaterialProperty(G.IfGreater(R, Threshold, RoadNormal, ParkSurface.Normal), TEXT(""), MP_Normal)
|
|
&& UMaterialEditingLibrary::ConnectMaterialProperty(G.IfGreater(R, Threshold, RoadRough, ParkSurface.Rough), TEXT(""), MP_Roughness)
|
|
&& UMaterialEditingLibrary::ConnectMaterialProperty(G.IfGreater(R, Threshold, RoadAO, ParkSurface.AO), TEXT(""), MP_AmbientOcclusion)
|
|
&& UMaterialEditingLibrary::ConnectMaterialProperty(G.Const(0.15f), TEXT(""), MP_Specular)
|
|
&& UMaterialEditingLibrary::ConnectMaterialProperty(Opacity, TEXT(""), MP_OpacityMask);
|
|
if (!bLinked)
|
|
{
|
|
Line(TEXT("ECHEC : branchement des sorties de M_LP_CityGround ; arret"));
|
|
return Finish(1);
|
|
}
|
|
Line(FString::Printf(TEXT("M_LP_CityGround : %d expressions"), Ground->GetExpressions().Num()));
|
|
}
|
|
UMaterialEditingLibrary::RecompileMaterial(Ground);
|
|
FAssetRegistryModule::AssetCreated(Ground);
|
|
if (!Save(GroundMaterialPkg, Ground))
|
|
{
|
|
Line(TEXT("ECHEC : sauvegarde de M_LP_CityGround ; arret"));
|
|
return Finish(1);
|
|
}
|
|
|
|
// Materiaux plats du sol pieton (1.4.1, A1 et E1) : texture sur la position locale XY, teinte (Tint) et reflet (Specular)
|
|
// reglables.
|
|
// A1 : le beton Megascans est un gris froid presque uni (moyenne 131, 131, 134) ; en jeu, a cote du pave clair et chaud du
|
|
// jeu, il paraissait bleu-gris. Teinte chaude et reflet du ciel reduit.
|
|
// 1.4.2 (DIAG-04 R1) : opaques ; l'effacement en tramage (DitherTemporalAA) scintillait en survol sous TAA. Avec la
|
|
// distance, le rendu va vers celui du plan de pave (FarColor = moyenne de sa texture x Color ; Metal, Roughness et Specular
|
|
// du plan), la normale vers le haut.
|
|
auto MakeFlat = [&](const FString& PackageName, const TCHAR* Name, const TCHAR* Prefix, float TileCm, const FLinearColor& Tint, float Specular, const FLinearColor& FarBase) -> UMaterial*
|
|
{
|
|
UPackage* Pkg = CreatePackage(*PackageName);
|
|
UMaterial* Mat = NewObject<UMaterial>(Pkg, Name, RF_Public | RF_Standalone);
|
|
FGraph G{ Mat };
|
|
UMaterialExpression* XY = G.Mask(G.Add<UMaterialExpressionLocalPosition>(8), true, true, false);
|
|
UMaterialExpression* Uv = G.Binary<UMaterialExpressionDivide>(XY, G.Scalar(TEXT("TileCm"), TileCm), 6);
|
|
UMaterialExpression* BC = G.Sample(Textures[FString(Prefix) + TEXT("BC")], Uv);
|
|
UMaterialExpression* N = G.Sample(Textures[FString(Prefix) + TEXT("N")], Uv);
|
|
UMaterialExpression* ORM = G.Sample(Textures[FString(Prefix) + TEXT("ORM")], Uv);
|
|
UMaterialExpression* Base = G.Binary<UMaterialExpressionMultiply>(BC, G.Vector(TEXT("Tint"), Tint), 3);
|
|
UMaterialExpression* FadeNear = G.Scalar(TEXT("FadeNearCm"), 6000.f);
|
|
UMaterialExpressionSaturate* Fade = G.Add<UMaterialExpressionSaturate>(3);
|
|
Fade->Input.Connect(0, G.Binary<UMaterialExpressionDivide>(
|
|
G.Binary<UMaterialExpressionSubtract>(G.Add<UMaterialExpressionPixelDepth>(8), FadeNear),
|
|
G.Binary<UMaterialExpressionSubtract>(G.Scalar(TEXT("FadeFarCm"), 20000.f), FadeNear)));
|
|
UMaterialExpression* Up = G.Const3(FLinearColor(0.f, 0.f, 1.f));
|
|
const bool bLinked = UMaterialEditingLibrary::ConnectMaterialProperty(G.Lerp(Base, G.Vector(TEXT("FarColor"), FarBase), Fade), TEXT(""), MP_BaseColor)
|
|
&& UMaterialEditingLibrary::ConnectMaterialProperty(G.Lerp(N, Up, Fade), TEXT(""), MP_Normal)
|
|
&& UMaterialEditingLibrary::ConnectMaterialProperty(G.Lerp(ORM, G.Scalar(TEXT("FarRoughness"), PavementRough), Fade, OutG), TEXT(""), MP_Roughness)
|
|
&& UMaterialEditingLibrary::ConnectMaterialProperty(G.Lerp(ORM, G.Const(1.f), Fade, OutR), TEXT(""), MP_AmbientOcclusion)
|
|
&& UMaterialEditingLibrary::ConnectMaterialProperty(G.Lerp(G.Const(0.f), G.Scalar(TEXT("FarMetallic"), PavementMetal), Fade), TEXT(""), MP_Metallic)
|
|
&& UMaterialEditingLibrary::ConnectMaterialProperty(G.Lerp(G.Scalar(TEXT("Specular"), Specular), G.Scalar(TEXT("FarSpecular"), PavementSpecular), Fade), TEXT(""), MP_Specular);
|
|
if (!bLinked)
|
|
{
|
|
return nullptr;
|
|
}
|
|
UMaterialEditingLibrary::RecompileMaterial(Mat);
|
|
FAssetRegistryModule::AssetCreated(Mat);
|
|
if (!Save(Pkg, Mat))
|
|
{
|
|
return nullptr;
|
|
}
|
|
Line(FString::Printf(TEXT("%s : opaque, %d expressions ; texture %s tous les %.0f cm ; teinte %.2f, %.2f, %.2f ; specular %.2f ; au loin %.3f, %.3f, %.3f"), Name,
|
|
Mat->GetExpressions().Num(), Prefix, TileCm, Tint.R, Tint.G, Tint.B, Specular, FarBase.R, FarBase.G, FarBase.B));
|
|
return Mat;
|
|
};
|
|
// Trottoirs : beton, teinte vers le pave du jeu (rendu mesure 157, 151, 149). Pierre (places, parvis, rues pietonnes) : paves.
|
|
// 1.5.1 (STM1-SPEC-21 § 1 P2) : -SquareOwnFar : au loin, la pierre prend la moyenne de sa propre texture x Tint, et non celle du
|
|
// plan de pave du jeu ; les places restent visibles de haut.
|
|
// Avec Place*, teinte neutre et taille reelle de la texture (PlaceTileCm) ; sans, comme en 1.4.1.
|
|
const TCHAR* SquarePrefix = bPlace ? TEXT("Place") : TEXT("Cobble");
|
|
const float SquareTileCm = bPlace ? (Spec.Contains(TEXT("PlaceTileCm")) ? FCString::Atof(*Spec[TEXT("PlaceTileCm")]) : 100.f) : 200.f;
|
|
// 1.5.2 : PlaceTint (scalaire, 1 par defaut) eclaircit le granit des places (moyenne 0,13 en 1.5.1).
|
|
const float PlaceTint = Spec.Contains(TEXT("PlaceTint")) ? FCString::Atof(*Spec[TEXT("PlaceTint")]) : 1.f;
|
|
const FLinearColor SquareTint = bPlace ? FLinearColor(PlaceTint, PlaceTint, PlaceTint) : FLinearColor(1.2f, 1.12f, 1.0f);
|
|
const bool bOwnFar = Switches.Contains(TEXT("SquareOwnFar"));
|
|
// Couleur lointaine propre (-SquareOwnFar) : moyenne de la texture x teinte.
|
|
auto OwnFar = [&](const TCHAR* Prefix, const FLinearColor& Tint) -> FLinearColor
|
|
{
|
|
FLinearColor Mean = PavementFar;
|
|
AverageColor(Textures[FString(Prefix) + TEXT("BC")], *(FString(Prefix) + TEXT("BC")), Mean);
|
|
FLinearColor Far = Mean * Tint;
|
|
Far.A = 1.f;
|
|
return Far;
|
|
};
|
|
const FLinearColor SquareFar = bOwnFar ? OwnFar(SquarePrefix, SquareTint) : PavementFar;
|
|
UMaterial* Sidewalk = MakeFlat(SidewalkMaterialPackage, TEXT("M_LP_CitySidewalk"), TEXT("Concrete"), 200.f, FLinearColor(1.45f, 1.32f, 1.15f), 0.1f, PavementFar);
|
|
UMaterial* Square = Sidewalk ? MakeFlat(SquareMaterialPackage, TEXT("M_LP_CitySquare"), SquarePrefix, SquareTileCm, SquareTint, 0.1f, SquareFar) : nullptr;
|
|
if (Square && bSlab)
|
|
{
|
|
const float SlabTileCm = Spec.Contains(TEXT("SlabTileCm")) ? FCString::Atof(*Spec[TEXT("SlabTileCm")]) : 200.f;
|
|
// 1.5.1 : teinte neutre ; 1.6.1 (STM1-DIAG-07 c) : SlabTint (scalaire) eclaircit les dalles (moyenne 0,23 ; 0,17 ; 0,10).
|
|
const float SlabTintK = Spec.Contains(TEXT("SlabTint")) ? FCString::Atof(*Spec[TEXT("SlabTint")]) : 1.f;
|
|
const FLinearColor SlabTint(SlabTintK, SlabTintK, SlabTintK);
|
|
if (!MakeFlat(SlabMaterialPackage, TEXT("M_LP_CitySlab"), TEXT("Slab"), SlabTileCm, SlabTint, 0.1f, bOwnFar ? OwnFar(TEXT("Slab"), SlabTint) : PavementFar))
|
|
{
|
|
Line(TEXT("ECHEC : creation de M_LP_CitySlab ; arret"));
|
|
return Finish(1);
|
|
}
|
|
}
|
|
if (Square && bSand)
|
|
{
|
|
TArray<FString> Rgb;
|
|
Spec[TEXT("SandTint")].ParseIntoArray(Rgb, TEXT(","));
|
|
if (Rgb.Num() != 3)
|
|
{
|
|
Line(TEXT("ECHEC : SandTint=R,G,B attendu ; arret"));
|
|
return Finish(1);
|
|
}
|
|
const FLinearColor SandTint(FCString::Atof(*Rgb[0]), FCString::Atof(*Rgb[1]), FCString::Atof(*Rgb[2]));
|
|
if (!MakeFlat(SandMaterialPackage, TEXT("M_LP_CitySand"), TEXT("Concrete"), 200.f, SandTint, 0.1f, bOwnFar ? OwnFar(TEXT("Concrete"), SandTint) : PavementFar))
|
|
{
|
|
Line(TEXT("ECHEC : creation de M_LP_CitySand ; arret"));
|
|
return Finish(1);
|
|
}
|
|
}
|
|
if (!Sidewalk || !Square)
|
|
{
|
|
Line(TEXT("ECHEC : creation des materiaux des trottoirs ou de la pierre ; arret"));
|
|
return Finish(1);
|
|
}
|
|
|
|
// Vert (jardins, pelouses) : meme rendu que les parcs de la carte (StreetMapMasked : base et emission 0 ; 0,1 ; 0) ;
|
|
// 1.4.2 : meme gazon que les parcs de M_LP_CityGround.
|
|
UPackage* GreenPkg = CreatePackage(*GreenMaterialPackage);
|
|
UMaterial* GreenMat = NewObject<UMaterial>(GreenPkg, TEXT("M_LP_CityGreen"), RF_Public | RF_Standalone);
|
|
{
|
|
FGraph G{ GreenMat };
|
|
FSurface Surface;
|
|
if (bGrass)
|
|
{
|
|
UMaterialExpression* XY = G.Mask(G.Add<UMaterialExpressionLocalPosition>(8), true, true, false);
|
|
UMaterialExpression* FadeNear = G.Scalar(TEXT("FadeNearCm"), 6000.f);
|
|
UMaterialExpressionSaturate* Fade = G.Add<UMaterialExpressionSaturate>(3);
|
|
Fade->Input.Connect(0, G.Binary<UMaterialExpressionDivide>(
|
|
G.Binary<UMaterialExpressionSubtract>(G.Add<UMaterialExpressionPixelDepth>(8), FadeNear),
|
|
G.Binary<UMaterialExpressionSubtract>(G.Scalar(TEXT("FadeFarCm"), 20000.f), FadeNear)));
|
|
Surface = GrassSurface(G, XY, Fade, G.Const3(FLinearColor(0.f, 0.f, 1.f)));
|
|
}
|
|
else
|
|
{
|
|
UMaterialExpression* Color = G.Vector(TEXT("Color"), FLinearColor(0.f, 0.1f, 0.f));
|
|
Surface = FSurface{ Color, Color, nullptr, G.Const(0.75f), nullptr };
|
|
}
|
|
const bool bLinked = UMaterialEditingLibrary::ConnectMaterialProperty(Surface.Base, TEXT(""), MP_BaseColor)
|
|
&& UMaterialEditingLibrary::ConnectMaterialProperty(Surface.Emissive, TEXT(""), MP_EmissiveColor)
|
|
&& (!Surface.Normal || UMaterialEditingLibrary::ConnectMaterialProperty(Surface.Normal, TEXT(""), MP_Normal))
|
|
&& UMaterialEditingLibrary::ConnectMaterialProperty(Surface.Rough, TEXT(""), MP_Roughness)
|
|
&& (!Surface.AO || UMaterialEditingLibrary::ConnectMaterialProperty(Surface.AO, TEXT(""), MP_AmbientOcclusion))
|
|
&& UMaterialEditingLibrary::ConnectMaterialProperty(G.Const(0.15f), TEXT(""), MP_Specular);
|
|
if (!bLinked)
|
|
{
|
|
Line(TEXT("ECHEC : branchement de M_LP_CityGreen ; arret"));
|
|
return Finish(1);
|
|
}
|
|
}
|
|
UMaterialEditingLibrary::RecompileMaterial(GreenMat);
|
|
FAssetRegistryModule::AssetCreated(GreenMat);
|
|
if (!Save(GreenPkg, GreenMat))
|
|
{
|
|
Line(TEXT("ECHEC : sauvegarde de M_LP_CityGreen ; arret"));
|
|
return Finish(1);
|
|
}
|
|
Line(bGrass ? FString::Printf(TEXT("M_LP_CityGreen : gazon (au loin %.3f, %.3f, %.3f) ; parcs de M_LP_CityGround en gazon"), GrassFar.R, GrassFar.G, GrassFar.B)
|
|
: FString(TEXT("M_LP_CityGreen : vert des parcs (base et emission 0 ; 0,1 ; 0)")));
|
|
|
|
// 1.5.1 (STM1-SPEC-21 § 2 a) : M_LP_LandmarkStone, opaque, pierre en projection triplanaire sur la position monde (aucune UV) :
|
|
// trois echantillons (plans YZ, XZ, XY) ponderes par |normale|^StoneSharpness ; teinte StoneTint ; pas de normale (vue de haut).
|
|
if (bStone)
|
|
{
|
|
const float StoneTileCm = Spec.Contains(TEXT("StoneTileCm")) ? FCString::Atof(*Spec[TEXT("StoneTileCm")]) : 300.f;
|
|
UPackage* StonePkg = CreatePackage(*LandmarkStonePackage);
|
|
UMaterial* StoneMat = NewObject<UMaterial>(StonePkg, TEXT("M_LP_LandmarkStone"), RF_Public | RF_Standalone);
|
|
FGraph G{ StoneMat };
|
|
UMaterialExpression* Pos = G.Binary<UMaterialExpressionDivide>(G.Add<UMaterialExpressionWorldPosition>(8), G.Scalar(TEXT("StoneTileCm"), StoneTileCm), 7);
|
|
UMaterialExpression* UvX = G.Mask(Pos, false, true, true);
|
|
UMaterialExpression* UvY = G.Binary<UMaterialExpressionAppendVector>(G.Mask(Pos, true, false, false), G.Mask(Pos, false, false, true), 6);
|
|
UMaterialExpression* UvZ = G.Mask(Pos, true, true, false);
|
|
UMaterialExpressionAbs* AbsN = G.Add<UMaterialExpressionAbs>(7);
|
|
AbsN->Input.Connect(0, G.Add<UMaterialExpressionVertexNormalWS>(8));
|
|
UMaterialExpressionPower* Pow = G.Add<UMaterialExpressionPower>(6);
|
|
Pow->Base.Connect(0, AbsN);
|
|
Pow->Exponent.Connect(0, G.Scalar(TEXT("StoneSharpness"), 4.f));
|
|
UMaterialExpression* Wx = G.Mask(Pow, true, false, false);
|
|
UMaterialExpression* Wy = G.Mask(Pow, false, true, false);
|
|
UMaterialExpression* Wz = G.Mask(Pow, false, false, true);
|
|
UMaterialExpression* Sum = G.Binary<UMaterialExpressionAdd>(G.Binary<UMaterialExpressionAdd>(Wx, Wy), G.Binary<UMaterialExpressionAdd>(Wz, G.Const(0.0001f)));
|
|
// Une sortie d'echantillon (OutRGB, OutR, OutG) x poids.
|
|
auto Weighted = [&G](UMaterialExpression* SampleExpr, int32 Output, UMaterialExpression* Weight) -> UMaterialExpression*
|
|
{
|
|
UMaterialExpressionMultiply* M = G.Add<UMaterialExpressionMultiply>(4);
|
|
M->A.Connect(Output, SampleExpr);
|
|
M->B.Connect(0, Weight);
|
|
return M;
|
|
};
|
|
auto Blend = [&](const TCHAR* Key, int32 Output) -> UMaterialExpression*
|
|
{
|
|
UTexture2D* Texture = Textures[Key];
|
|
UMaterialExpression* Sx = Weighted(G.Sample(Texture, UvX), Output, Wx);
|
|
UMaterialExpression* Sy = Weighted(G.Sample(Texture, UvY), Output, Wy);
|
|
UMaterialExpression* Sz = Weighted(G.Sample(Texture, UvZ), Output, Wz);
|
|
return G.Binary<UMaterialExpressionDivide>(G.Binary<UMaterialExpressionAdd>(G.Binary<UMaterialExpressionAdd>(Sx, Sy, 3), Sz, 3), Sum, 3);
|
|
};
|
|
const FLinearColor StoneTint(0.95f, 0.9f, 0.8f);
|
|
UMaterialExpression* Base = G.Binary<UMaterialExpressionMultiply>(Blend(TEXT("StoneBC"), OutRGB), G.Vector(TEXT("StoneTint"), StoneTint), 2);
|
|
const bool bLinked = UMaterialEditingLibrary::ConnectMaterialProperty(Base, TEXT(""), MP_BaseColor)
|
|
&& UMaterialEditingLibrary::ConnectMaterialProperty(Blend(TEXT("StoneORM"), OutG), TEXT(""), MP_Roughness)
|
|
&& UMaterialEditingLibrary::ConnectMaterialProperty(Blend(TEXT("StoneORM"), OutR), TEXT(""), MP_AmbientOcclusion)
|
|
&& UMaterialEditingLibrary::ConnectMaterialProperty(G.Const(0.3f), TEXT(""), MP_Specular);
|
|
if (!bLinked)
|
|
{
|
|
Line(TEXT("ECHEC : branchement de M_LP_LandmarkStone ; arret"));
|
|
return Finish(1);
|
|
}
|
|
UMaterialEditingLibrary::RecompileMaterial(StoneMat);
|
|
FAssetRegistryModule::AssetCreated(StoneMat);
|
|
if (!Save(StonePkg, StoneMat))
|
|
{
|
|
Line(TEXT("ECHEC : sauvegarde de M_LP_LandmarkStone ; arret"));
|
|
return Finish(1);
|
|
}
|
|
Line(FString::Printf(TEXT("M_LP_LandmarkStone : opaque, triplanaire, %d expressions ; texture %s tous les %.0f cm ; teinte %.2f, %.2f, %.2f"),
|
|
StoneMat->GetExpressions().Num(), *Textures[TEXT("StoneBC")]->GetPathName(), StoneTileCm, StoneTint.R, StoneTint.G, StoneTint.B));
|
|
}
|
|
|
|
// Instance de ville, chargee par le jeu (ResolveCityPath de /Game/Heavy/StreetMap/Ground/<Ville>).
|
|
UPackage* GroundPkg = CreatePackage(*GroundPackageName);
|
|
UMaterialInstanceConstant* Instance = NewObject<UMaterialInstanceConstant>(GroundPkg, *City, RF_Public | RF_Standalone);
|
|
Instance->SetParentEditorOnly(Ground);
|
|
Instance->PostEditChange();
|
|
FAssetRegistryModule::AssetCreated(Instance);
|
|
if (!Save(GroundPkg, Instance))
|
|
{
|
|
Line(FString::Printf(TEXT("ECHEC : sauvegarde de %s"), *GroundPackageName));
|
|
return Finish(1);
|
|
}
|
|
Line(FString::Printf(TEXT("instance %s -> %s"), *GroundPackageName, *Ground->GetPathName()));
|
|
return Finish(0);
|
|
}
|