Skip to content

Commit

Permalink
Create and use GenerateMaterialUtility
Browse files Browse the repository at this point in the history
  • Loading branch information
j9liu committed Mar 5, 2025
1 parent 423c2bf commit 8bbfccd
Show file tree
Hide file tree
Showing 3 changed files with 305 additions and 172 deletions.
230 changes: 58 additions & 172 deletions Source/CesiumRuntime/Private/CesiumFeaturesMetadataComponent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@

#include "CesiumFeaturesMetadataComponent.h"
#include "Cesium3DTileset.h"
#include "EncodedMetadataConversions.h"
#include "CesiumGltfComponent.h"
#include "CesiumGltfPrimitiveComponent.h"
#include "CesiumModelMetadata.h"
#include "CesiumRuntime.h"
#include "EncodedFeaturesMetadata.h"
#include "EncodedMetadataConversions.h"
#include "GenerateMaterialUtility.h"
#include "UnrealMetadataConversions.h"

#if WITH_EDITOR
Expand Down Expand Up @@ -42,8 +43,7 @@
extern UNREALED_API class UEditorEngine* GEditor;

using namespace EncodedFeaturesMetadata;

static const FString AutogeneratedMessage = "AUTOGENERATED DO NOT EDIT";
using namespace GenerateMaterialUtility;

namespace {
void AutoFillPropertyTableDescriptions(
Expand Down Expand Up @@ -423,19 +423,7 @@ void UCesiumFeaturesMetadataComponent::AutoFill() {
Super::PostEditChange();
}

template <typename ObjClass>
static FORCEINLINE ObjClass* LoadObjFromPath(const FName& Path) {
if (Path == NAME_None)
return nullptr;

return Cast<ObjClass>(
StaticLoadObject(ObjClass::StaticClass(), nullptr, *Path.ToString()));
}

static FORCEINLINE UMaterialFunction* LoadMaterialFunction(const FName& Path) {
if (Path == NAME_None)
return nullptr;

return LoadObjFromPath<UMaterialFunction>(Path);
}

Expand Down Expand Up @@ -472,27 +460,31 @@ struct MaterialFunctionLibrary {
"/CesiumForUnreal/Materials/MaterialFunctions/CesiumGetFeatureIdsFromInstance.CesiumGetFeatureIdsFromInstance")) {
}

bool isValid() {
return SelectTexCoords != nullptr &&
bool isValid() const {
return SelectTexCoords != nullptr && TransformTexCoords != nullptr &&
GetFeatureIdsFromAttribute != nullptr &&
GetFeatureIdsFromTexture != nullptr &&
GetFeatureIdsFromInstance != nullptr;
}
};
} // namespace

// Separate nodes into auto-generated and user-added. Collect the property
// result nodes.
static void ClassifyNodes(
UMaterialFunctionMaterialLayer* Layer,
MaterialNodeClassification& Classification,
/**
* Classifies nodes into categories under MaterialNodeClassification, such that
* they can be handled separately.
*/
static MaterialNodeClassification ClassifyNodes(
const UMaterialFunctionMaterialLayer* Layer,
const MaterialFunctionLibrary& FunctionLibrary) {
const UMaterialFunction* GetFeatureIdsFromAttributeFunction =
FunctionLibrary.GetFeatureIdsFromAttribute;
const UMaterialFunction* GetFeatureIdsFromTextureFunction =
FunctionLibrary.GetFeatureIdsFromTexture;
const UMaterialFunction* GetFeatureIdsFromInstanceFunction =
FunctionLibrary.GetFeatureIdsFromInstance;

MaterialNodeClassification Classification;

for (const TObjectPtr<UMaterialExpression>& Node :
Layer->GetExpressionCollection().Expressions) {
// Check if this node is marked as autogenerated.
Expand Down Expand Up @@ -539,6 +531,8 @@ static void ClassifyNodes(
Classification.UserAddedNodes.Add(Node);
}
}

return Classification;
}

static void ClearAutoGeneratedNodes(
Expand All @@ -547,8 +541,8 @@ static void ClearAutoGeneratedNodes(
TMap<FString, TArray<FExpressionInput*>>& ConnectionOutputRemap,
const MaterialFunctionLibrary& FunctionLibrary) {

MaterialNodeClassification Classification;
ClassifyNodes(Layer, Classification, FunctionLibrary);
MaterialNodeClassification Classification =
ClassifyNodes(Layer, FunctionLibrary);

// Determine which user-added connections to remap when regenerating the
// feature ID retrieval nodes.
Expand Down Expand Up @@ -761,8 +755,8 @@ static void RemapUserConnections(
TMap<FString, TArray<FExpressionInput*>>& ConnectionOutputRemap,
const MaterialFunctionLibrary& FunctionLibrary) {

MaterialNodeClassification Classification;
ClassifyNodes(Layer, Classification, FunctionLibrary);
MaterialNodeClassification Classification =
ClassifyNodes(Layer, FunctionLibrary);

for (UMaterialExpressionMaterialFunctionCall* GetFeatureIdNode :
Classification.GetFeatureIdNodes) {
Expand Down Expand Up @@ -894,53 +888,7 @@ static void RemapUserConnections(
}
}

// Increment constant that is used to space out the autogenerated nodes.
static const int32 Incr = 200;

namespace {
/**
* Computes a scalar for spacing out material nodes. The actual computation is
* rather arbitrary, but this prevents clumping when properties have extremely
* long names.
*/
float GetNameLengthScalar(const FName& Name) {
return FMath::Max(static_cast<float>(Name.GetStringLength()) / 24, 1.0f);
}

float GetNameLengthScalar(const FString& Name) {
return FMath::Max(static_cast<float>(Name.Len()) / 24, 1.0f);
}

ECustomMaterialOutputType
GetOutputTypeForEncodedType(ECesiumEncodedMetadataType Type) {
switch (Type) {
case ECesiumEncodedMetadataType::Vec2:
return ECustomMaterialOutputType::CMOT_Float2;
case ECesiumEncodedMetadataType::Vec3:
return ECustomMaterialOutputType::CMOT_Float3;
case ECesiumEncodedMetadataType::Vec4:
return ECustomMaterialOutputType::CMOT_Float4;
case ECesiumEncodedMetadataType::Scalar:
default:
return ECustomMaterialOutputType::CMOT_Float1;
};
}

FString GetSwizzleForEncodedType(ECesiumEncodedMetadataType Type) {
switch (Type) {
case ECesiumEncodedMetadataType::Scalar:
return ".r";
case ECesiumEncodedMetadataType::Vec2:
return ".rg";
case ECesiumEncodedMetadataType::Vec3:
return ".rgb";
case ECesiumEncodedMetadataType::Vec4:
return ".rgba";
default:
return FString();
};
}

/**
* @brief Generates code for assembling metadata values from a scalar property
* texture property.
Expand Down Expand Up @@ -1440,48 +1388,6 @@ void GenerateNodesForNullFeatureId(
AutoGeneratedNodes.Add(IfStatement);
}

/**
* @brief Generates a parameter node corresponding to the given encoded metadata
* type.
*/
UMaterialExpressionParameter* GenerateParameterNodeWithGivenType(
const ECesiumEncodedMetadataType Type,
const FString& Name,
TArray<UMaterialExpression*>& AutoGeneratedNodes,
UMaterialFunctionMaterialLayer* TargetMaterialLayer,
int32 NodeX,
int32 NodeY) {
UMaterialExpressionParameter* Parameter = nullptr;
int32 NodeHeight = 0;

if (Type == ECesiumEncodedMetadataType::Scalar) {
UMaterialExpressionScalarParameter* ScalarParameter =
NewObject<UMaterialExpressionScalarParameter>(TargetMaterialLayer);
ScalarParameter->DefaultValue = 0.0f;
Parameter = ScalarParameter;
}

if (Type == ECesiumEncodedMetadataType::Vec2 ||
Type == ECesiumEncodedMetadataType::Vec3 ||
Type == ECesiumEncodedMetadataType::Vec4) {
UMaterialExpressionVectorParameter* VectorParameter =
NewObject<UMaterialExpressionVectorParameter>(TargetMaterialLayer);
VectorParameter->DefaultValue = FLinearColor(0.0f, 0.0f, 0.0f, 0.0f);
Parameter = VectorParameter;
}

if (!Parameter) {
return nullptr;
}

Parameter->ParameterName = FName(Name);
Parameter->MaterialExpressionEditorX = NodeX;
Parameter->MaterialExpressionEditorY = NodeY;
AutoGeneratedNodes.Add(Parameter);

return Parameter;
}

/**
* @brief Generates the nodes necessary to apply property transforms to a
* metadata property.
Expand Down Expand Up @@ -1556,21 +1462,20 @@ void GenerateNodesForMetadataPropertyTransforms(

if (PropertyDetails.bHasScale) {
NodeY += Incr;
UMaterialExpressionParameter* Parameter =
GenerateParameterNodeWithGivenType(
Type,
FullPropertyName + MaterialPropertyScaleSuffix,
AutoGeneratedNodes,
TargetMaterialLayer,
BeginSectionX,
NodeY);
UMaterialExpressionParameter* Parameter = GenerateParameterNode(
TargetMaterialLayer,
Type,
FullPropertyName + MaterialPropertyScaleSuffix,
BeginSectionX,
NodeY);
AutoGeneratedNodes.Add(Parameter);

FString ScaleName = "Scale";

FCustomInput& DefaultInput =
FCustomInput& ScaleInput =
ApplyTransformsFunction->Inputs.Emplace_GetRef();
DefaultInput.InputName = FName(ScaleName);
DefaultInput.Input.Expression = Parameter;
ScaleInput.InputName = FName(ScaleName);
ScaleInput.Input.Expression = Parameter;

TransformCode += " * " + ScaleName;

Expand All @@ -1581,21 +1486,20 @@ void GenerateNodesForMetadataPropertyTransforms(

if (PropertyDetails.bHasOffset) {
NodeY += Incr;
UMaterialExpressionParameter* Parameter =
GenerateParameterNodeWithGivenType(
Type,
FullPropertyName + MaterialPropertyOffsetSuffix,
AutoGeneratedNodes,
TargetMaterialLayer,
BeginSectionX,
NodeY);
UMaterialExpressionParameter* Parameter = GenerateParameterNode(
TargetMaterialLayer,
Type,
FullPropertyName + MaterialPropertyOffsetSuffix,
BeginSectionX,
NodeY);
AutoGeneratedNodes.Add(Parameter);

FString OffsetName = "Offset";

FCustomInput& DefaultInput =
FCustomInput& OffsetInput =
ApplyTransformsFunction->Inputs.Emplace_GetRef();
DefaultInput.InputName = FName(OffsetName);
DefaultInput.Input.Expression = Parameter;
OffsetInput.InputName = FName(OffsetName);
OffsetInput.Input.Expression = Parameter;

TransformCode += " + " + OffsetName;

Expand All @@ -1620,14 +1524,13 @@ void GenerateNodesForMetadataPropertyTransforms(

if (PropertyDetails.bHasNoDataValue) {
NodeY += Incr;
UMaterialExpressionParameter* Parameter =
GenerateParameterNodeWithGivenType(
Type,
FullPropertyName + MaterialPropertyNoDataSuffix,
AutoGeneratedNodes,
TargetMaterialLayer,
BeginSectionX,
NodeY);
UMaterialExpressionParameter* Parameter = GenerateParameterNode(
TargetMaterialLayer,
Type,
FullPropertyName + MaterialPropertyNoDataSuffix,
BeginSectionX,
NodeY);
AutoGeneratedNodes.Add(Parameter);

int32 NameLength = Incr * GetNameLengthScalar(Parameter->ParameterName);

Expand Down Expand Up @@ -1670,14 +1573,13 @@ void GenerateNodesForMetadataPropertyTransforms(

if (PropertyDetails.bHasDefaultValue) {
NodeY += 0.75 * Incr;
UMaterialExpressionParameter* Parameter =
GenerateParameterNodeWithGivenType(
Type,
FullPropertyName + MaterialPropertyDefaultValueSuffix,
AutoGeneratedNodes,
TargetMaterialLayer,
BeginSectionX,
NodeY);
UMaterialExpressionParameter* Parameter = GenerateParameterNode(
TargetMaterialLayer,
Type,
FullPropertyName + MaterialPropertyDefaultValueSuffix,
BeginSectionX,
NodeY);
AutoGeneratedNodes.Add(Parameter);

int32 NameLength = Incr * GetNameLengthScalar(Parameter->ParameterName);

Expand Down Expand Up @@ -2629,10 +2531,10 @@ void UCesiumFeaturesMetadataComponent::GenerateMaterial() {
return;
}

FString MaterialName =
const FString MaterialName =
"ML_" + pTileset->GetFName().ToString() + "_FeaturesMetadata";
FString PackageBaseName = "/Game/";
FString PackageName = PackageBaseName + MaterialName;
const FString PackageBaseName = "/Game/";
const FString PackageName = PackageBaseName + MaterialName;

MaterialFunctionLibrary FunctionLibrary = MaterialFunctionLibrary();
if (!FunctionLibrary.isValid()) {
Expand Down Expand Up @@ -2661,22 +2563,7 @@ void UCesiumFeaturesMetadataComponent::GenerateMaterial() {
GEditor->GetEditorSubsystem<UAssetEditorSubsystem>()
->CloseAllEditorsForAsset(this->TargetMaterialLayer);
} else {
UPackage* Package = CreatePackage(*PackageName);

// Create an Unreal material layer
UMaterialFunctionMaterialLayerFactory* MaterialFactory =
NewObject<UMaterialFunctionMaterialLayerFactory>();
this->TargetMaterialLayer =
(UMaterialFunctionMaterialLayer*)MaterialFactory->FactoryCreateNew(
UMaterialFunctionMaterialLayer::StaticClass(),
Package,
*MaterialName,
RF_Public | RF_Standalone | RF_Transactional,
NULL,
GWarn);
FAssetRegistryModule::AssetCreated(this->TargetMaterialLayer);
Package->FullyLoad();
Package->SetDirtyFlag(true);
this->TargetMaterialLayer = CreateMaterialLayer(PackageName, MaterialName);
}

this->TargetMaterialLayer->PreEditChange(NULL);
Expand Down Expand Up @@ -2708,7 +2595,6 @@ void UCesiumFeaturesMetadataComponent::GenerateMaterial() {
for (UMaterialExpression* AutoGeneratedNode : AutoGeneratedNodes) {
// Mark as auto-generated. If the material is regenerated, we will look
// for this exact description to determine whether it was autogenerated.

AutoGeneratedNode->Desc = AutogeneratedMessage;

this->TargetMaterialLayer->GetExpressionCollection().AddExpression(
Expand Down
Loading

0 comments on commit 8bbfccd

Please sign in to comment.