Skip to content

Commit 5537c1e

Browse files
authored
Merge pull request #1565 from CesiumGS/instance-feature-rendering
Instance feature rendering
2 parents 633b037 + a63409c commit 5537c1e

File tree

8 files changed

+167
-20
lines changed

8 files changed

+167
-20
lines changed

CHANGES.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@
88
- Renamed `FCesiumFeatureIdAttribute::GetFeatureIDForVertex` to `FCesiumFeatureIdAttribute::GetFeatureID`.
99
- Renamed `FCesiumFeatureIdAttribute::GetVertexCount` to `FCesiumFeatureIdAttribute::GetCount`.
1010

11+
##### Additions :tada:
12+
13+
- Added support for metadata querying and styling on instanced models.
14+
1115
##### Fixes :wrench:
1216

1317
- Fixed a bug in `CesiumSubLevelSwitcherComponent` that could prevent all sub-levels from loading if a single sub-level failed to load.
4.79 KB
Binary file not shown.
Binary file not shown.

Source/CesiumRuntime/Private/CesiumEncodedFeaturesMetadata.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,20 @@ FString getNameForFeatureIDSet(
5252
}
5353
}
5454

55+
if (type == ECesiumFeatureIdSetType::Instance) {
56+
FCesiumFeatureIdAttribute attribute =
57+
UCesiumFeatureIdSetBlueprintLibrary::GetAsFeatureIDAttribute(
58+
featureIDSet);
59+
ECesiumFeatureIdAttributeStatus status =
60+
UCesiumFeatureIdAttributeBlueprintLibrary::GetFeatureIDAttributeStatus(
61+
attribute);
62+
if (status == ECesiumFeatureIdAttributeStatus::Valid) {
63+
std::string generatedName = "_FEATURE_INSTANCE_ID_" +
64+
std::to_string(attribute.getAttributeIndex());
65+
return FString(generatedName.c_str());
66+
}
67+
}
68+
5569
if (type == ECesiumFeatureIdSetType::Texture) {
5670
std::string generatedName =
5771
"_FEATURE_ID_TEXTURE_" + std::to_string(FeatureIdTextureCounter);
@@ -63,6 +77,10 @@ FString getNameForFeatureIDSet(
6377
return FString("_IMPLICIT_FEATURE_ID");
6478
}
6579

80+
if (type == ECesiumFeatureIdSetType::InstanceImplicit) {
81+
return FString("_IMPLICIT_FEATURE_INSTANCE_ID");
82+
}
83+
6684
// If for some reason an empty / invalid feature ID set was constructed,
6785
// return an empty name.
6886
return FString();

Source/CesiumRuntime/Private/CesiumFeaturesMetadataComponent.cpp

Lines changed: 70 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
#include "Materials/MaterialExpressionFunctionOutput.h"
2727
#include "Materials/MaterialExpressionIf.h"
2828
#include "Materials/MaterialExpressionMaterialFunctionCall.h"
29+
#include "Materials/MaterialExpressionPerInstanceCustomData.h"
30+
#include "Materials/MaterialExpressionRound.h"
2931
#include "Materials/MaterialExpressionScalarParameter.h"
3032
#include "Materials/MaterialExpressionSetMaterialAttributes.h"
3133
#include "Materials/MaterialExpressionTextureCoordinate.h"
@@ -268,9 +270,15 @@ void AutoFillPropertyTextureDescriptions(
268270
void AutoFillFeatureIdSetDescriptions(
269271
TArray<FCesiumFeatureIdSetDescription>& Descriptions,
270272
const FCesiumPrimitiveFeatures& Features,
273+
const FCesiumPrimitiveFeatures* InstanceFeatures,
271274
const TArray<FCesiumPropertyTable>& PropertyTables) {
272-
const TArray<FCesiumFeatureIdSet> featureIDSets =
275+
TArray<FCesiumFeatureIdSet> featureIDSets =
273276
UCesiumPrimitiveFeaturesBlueprintLibrary::GetFeatureIDSets(Features);
277+
if (InstanceFeatures) {
278+
featureIDSets.Append(
279+
UCesiumPrimitiveFeaturesBlueprintLibrary::GetFeatureIDSets(
280+
*InstanceFeatures));
281+
}
274282
int32 featureIDTextureCounter = 0;
275283

276284
for (const FCesiumFeatureIdSet& featureIDSet : featureIDSets) {
@@ -385,9 +393,16 @@ void UCesiumFeaturesMetadataComponent::AutoFill() {
385393
const TArray<FCesiumPropertyTable>& propertyTables =
386394
UCesiumModelMetadataBlueprintLibrary::GetPropertyTables(
387395
modelMetadata);
396+
const FCesiumPrimitiveFeatures* pInstanceFeatures = nullptr;
397+
const auto* pInstancedComponent =
398+
Cast<UCesiumGltfInstancedComponent>(pChildComponent);
399+
if (pInstancedComponent) {
400+
pInstanceFeatures = pInstancedComponent->pInstanceFeatures.Get();
401+
}
388402
AutoFillFeatureIdSetDescriptions(
389403
this->FeatureIdSets,
390404
primitiveFeatures,
405+
pInstanceFeatures,
391406
propertyTables);
392407

393408
const FCesiumPrimitiveMetadata& primitiveMetadata = primData.Metadata;
@@ -438,6 +453,7 @@ struct MaterialFunctionLibrary {
438453
UMaterialFunction* TransformTexCoords = nullptr;
439454
UMaterialFunction* GetFeatureIdsFromAttribute = nullptr;
440455
UMaterialFunction* GetFeatureIdsFromTexture = nullptr;
456+
UMaterialFunction* GetFeatureIdsFromInstance = nullptr;
441457

442458
MaterialFunctionLibrary()
443459
: SelectTexCoords(LoadMaterialFunction(
@@ -447,13 +463,16 @@ struct MaterialFunctionLibrary {
447463
GetFeatureIdsFromAttribute(LoadMaterialFunction(
448464
"/CesiumForUnreal/Materials/MaterialFunctions/CesiumGetFeatureIdsFromAttribute.CesiumGetFeatureIdsFromAttribute")),
449465
GetFeatureIdsFromTexture(LoadMaterialFunction(
450-
"/CesiumForUnreal/Materials/MaterialFunctions/CesiumGetFeatureIdsFromTexture.CesiumGetFeatureIdsFromTexture")) {
466+
"/CesiumForUnreal/Materials/MaterialFunctions/CesiumGetFeatureIdsFromTexture.CesiumGetFeatureIdsFromTexture")),
467+
GetFeatureIdsFromInstance(LoadMaterialFunction(
468+
"/CesiumForUnreal/Materials/MaterialFunctions/CesiumGetFeatureIdsFromInstance.CesiumGetFeatureIdsFromInstance")) {
451469
}
452470

453471
bool isValid() {
454472
return SelectTexCoords != nullptr &&
455473
GetFeatureIdsFromAttribute != nullptr &&
456-
GetFeatureIdsFromTexture != nullptr;
474+
GetFeatureIdsFromTexture != nullptr &&
475+
GetFeatureIdsFromInstance != nullptr;
457476
}
458477
};
459478
} // namespace
@@ -468,7 +487,8 @@ static void ClassifyNodes(
468487
FunctionLibrary.GetFeatureIdsFromAttribute;
469488
const UMaterialFunction* GetFeatureIdsFromTextureFunction =
470489
FunctionLibrary.GetFeatureIdsFromTexture;
471-
490+
const UMaterialFunction* GetFeatureIdsFromInstanceFunction =
491+
FunctionLibrary.GetFeatureIdsFromInstance;
472492
for (const TObjectPtr<UMaterialExpression>& Node :
473493
Layer->GetExpressionCollection().Expressions) {
474494
// Check if this node is marked as autogenerated.
@@ -507,7 +527,8 @@ static void ClassifyNodes(
507527

508528
const FName& name = FunctionCallNode->MaterialFunction->GetFName();
509529
if (name == GetFeatureIdsFromAttributeFunction->GetFName() ||
510-
name == GetFeatureIdsFromTextureFunction->GetFName()) {
530+
name == GetFeatureIdsFromTextureFunction->GetFName() ||
531+
name == GetFeatureIdsFromInstanceFunction->GetFName()) {
511532
Classification.GetFeatureIdNodes.Add(FunctionCallNode);
512533
}
513534
} else {
@@ -742,15 +763,17 @@ static void RemapUserConnections(
742763
for (UMaterialExpressionMaterialFunctionCall* GetFeatureIdNode :
743764
Classification.GetFeatureIdNodes) {
744765
const auto Inputs = GetFeatureIdNode->FunctionInputs;
745-
const auto Parameter =
746-
Cast<UMaterialExpressionParameter>(Inputs[0].Input.Expression);
747-
FString ParameterName = Parameter->ParameterName.ToString();
766+
if (!Inputs.IsEmpty()) {
767+
const auto Parameter =
768+
Cast<UMaterialExpressionParameter>(Inputs[0].Input.Expression);
769+
FString ParameterName = Parameter->ParameterName.ToString();
748770

749-
FString Key = GetFeatureIdNode->GetDescription() + ParameterName;
750-
TArray<FExpressionInput*>* pConnections = ConnectionOutputRemap.Find(Key);
751-
if (pConnections) {
752-
for (FExpressionInput* pConnection : *pConnections) {
753-
pConnection->Connect(0, GetFeatureIdNode);
771+
FString Key = GetFeatureIdNode->GetDescription() + ParameterName;
772+
TArray<FExpressionInput*>* pConnections = ConnectionOutputRemap.Find(Key);
773+
if (pConnections) {
774+
for (FExpressionInput* pConnection : *pConnections) {
775+
pConnection->Connect(0, GetFeatureIdNode);
776+
}
754777
}
755778
}
756779
}
@@ -1803,7 +1826,7 @@ void GenerateNodesForPropertyTable(
18031826
UMaterialFunctionMaterialLayer* TargetMaterialLayer,
18041827
int32& NodeX,
18051828
int32& NodeY,
1806-
UMaterialExpressionMaterialFunctionCall* GetFeatureIdCall) {
1829+
UMaterialExpression* GetFeatureExpression) {
18071830
int32 BeginSectionX = NodeX;
18081831
// This value is used by parameters on the left side of the
18091832
// "GetPropertyValues" function...
@@ -1843,7 +1866,7 @@ void GenerateNodesForPropertyTable(
18431866

18441867
FCustomInput& FeatureIDInput = GetPropertyValuesFunction->Inputs[0];
18451868
FeatureIDInput.InputName = FName("FeatureID");
1846-
FeatureIDInput.Input.Expression = GetFeatureIdCall;
1869+
FeatureIDInput.Input.Expression = GetFeatureExpression;
18471870

18481871
GetPropertyValuesFunction->AdditionalOutputs.Reserve(
18491872
PropertyTable.Properties.Num());
@@ -2327,6 +2350,27 @@ void GenerateNodesForPropertyTexture(
23272350
NodeY = FMath::Max(PropertyDataSectionY, PropertyTransformsSectionY) + Incr;
23282351
}
23292352

2353+
UMaterialExpression* GenerateInstanceNodes(
2354+
TArray<UMaterialExpression*>& AutoGeneratedNodes,
2355+
UMaterialFunctionMaterialLayer* TargetMaterialLayer,
2356+
UMaterialFunction* GetFeatureIdsFromInstanceFunction,
2357+
int32& NodeX,
2358+
int32& NodeY) {
2359+
UMaterialExpressionMaterialFunctionCall* GetFeatureIds =
2360+
NewObject<UMaterialExpressionMaterialFunctionCall>(TargetMaterialLayer);
2361+
GetFeatureIds->MaterialFunction = GetFeatureIdsFromInstanceFunction;
2362+
GetFeatureIds->MaterialExpressionEditorX = NodeX;
2363+
GetFeatureIds->MaterialExpressionEditorY = NodeY;
2364+
2365+
GetFeatureIdsFromInstanceFunction->GetInputsAndOutputs(
2366+
GetFeatureIds->FunctionInputs,
2367+
GetFeatureIds->FunctionOutputs);
2368+
2369+
NodeX += 2 * Incr;
2370+
AutoGeneratedNodes.Add(GetFeatureIds);
2371+
return GetFeatureIds;
2372+
}
2373+
23302374
void GenerateMaterialNodes(
23312375
UCesiumFeaturesMetadataComponent* pComponent,
23322376
TArray<UMaterialExpression*>& AutoGeneratedNodes,
@@ -2348,17 +2392,25 @@ void GenerateMaterialNodes(
23482392
}
23492393

23502394
UMaterialExpressionMaterialFunctionCall* GetFeatureIdCall = nullptr;
2395+
UMaterialExpression* LastNode = nullptr;
23512396
if (featureIdSet.Type == ECesiumFeatureIdSetType::Texture) {
2352-
GetFeatureIdCall = GenerateNodesForFeatureIdTexture(
2397+
LastNode = GenerateNodesForFeatureIdTexture(
23532398
featureIdSet,
23542399
AutoGeneratedNodes,
23552400
pComponent->TargetMaterialLayer,
23562401
FunctionLibrary,
23572402
NodeX,
23582403
NodeY);
2404+
} else if (featureIdSet.Type == ECesiumFeatureIdSetType::Instance) {
2405+
LastNode = GenerateInstanceNodes(
2406+
AutoGeneratedNodes,
2407+
pComponent->TargetMaterialLayer,
2408+
FunctionLibrary.GetFeatureIdsFromInstance,
2409+
NodeX,
2410+
NodeY);
23592411
} else {
23602412
// Handle implicit feature IDs the same as feature ID attributes
2361-
GetFeatureIdCall = GenerateNodesForFeatureIdAttribute(
2413+
LastNode = GenerateNodesForFeatureIdAttribute(
23622414
featureIdSet,
23632415
AutoGeneratedNodes,
23642416
pComponent->TargetMaterialLayer,
@@ -2367,7 +2419,6 @@ void GenerateMaterialNodes(
23672419
NodeY);
23682420
}
23692421

2370-
UMaterialExpression* LastNode = GetFeatureIdCall;
23712422
int32 BeginSectionY = NodeY;
23722423

23732424
if (!featureIdSet.PropertyTableName.IsEmpty()) {
@@ -2386,7 +2437,7 @@ void GenerateMaterialNodes(
23862437
pComponent->TargetMaterialLayer,
23872438
NodeX,
23882439
NodeY,
2389-
GetFeatureIdCall);
2440+
LastNode);
23902441
GeneratedPropertyTableNames.Add(pPropertyTable->Name);
23912442
}
23922443
}

Source/CesiumRuntime/Private/CesiumGltfComponent.cpp

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2889,6 +2889,67 @@ static void SetMetadataParameterValues_DEPRECATED(
28892889
PRAGMA_ENABLE_DEPRECATION_WARNINGS
28902890
#pragma endregion
28912891

2892+
namespace {
2893+
void addInstanceFeatureIds(
2894+
UCesiumGltfInstancedComponent* pInstancedComponent,
2895+
const FCesiumFeaturesMetadataDescription& featuresDescription) {
2896+
const TSharedPtr<FCesiumPrimitiveFeatures>& pInstanceFeatures =
2897+
pInstancedComponent->pInstanceFeatures;
2898+
if (!pInstanceFeatures) {
2899+
return;
2900+
}
2901+
const TArray<FCesiumFeatureIdSet>& allFeatureIdSets =
2902+
UCesiumPrimitiveFeaturesBlueprintLibrary::GetFeatureIDSets(
2903+
*pInstanceFeatures);
2904+
2905+
const TArray<FCesiumFeatureIdSetDescription>& featureIDSetDescriptions =
2906+
featuresDescription.Features.FeatureIdSets;
2907+
2908+
int32_t featureIdTextureCounter = 0;
2909+
2910+
TArray<int32> activeFeatureIdSets;
2911+
2912+
for (int32 i = 0; i < allFeatureIdSets.Num(); ++i) {
2913+
FString name = CesiumEncodedFeaturesMetadata::getNameForFeatureIDSet(
2914+
allFeatureIdSets[i],
2915+
featureIdTextureCounter);
2916+
2917+
const FCesiumFeatureIdSetDescription* pDescription =
2918+
featureIDSetDescriptions.FindByPredicate(
2919+
[&name](
2920+
const FCesiumFeatureIdSetDescription& existingFeatureIDSet) {
2921+
return existingFeatureIDSet.Name == name;
2922+
});
2923+
2924+
if (pDescription) {
2925+
activeFeatureIdSets.Emplace(i);
2926+
}
2927+
}
2928+
2929+
int32 featureSetCount = activeFeatureIdSets.Num();
2930+
if (featureSetCount == 0) {
2931+
return;
2932+
}
2933+
pInstancedComponent->SetNumCustomDataFloats(featureSetCount);
2934+
int32 numInstances = pInstancedComponent->GetInstanceCount();
2935+
pInstancedComponent->PerInstanceSMCustomData.SetNum(
2936+
featureSetCount * numInstances);
2937+
for (int32 j = 0; j < featureSetCount; ++j) {
2938+
int64_t setIndex = activeFeatureIdSets[j];
2939+
2940+
for (int32 i = 0; i < numInstances; ++i) {
2941+
int64 featureId =
2942+
UCesiumPrimitiveFeaturesBlueprintLibrary::GetFeatureIDFromInstance(
2943+
*pInstanceFeatures,
2944+
i,
2945+
setIndex);
2946+
pInstancedComponent
2947+
->SetCustomDataValue(i, j, static_cast<float>(featureId), true);
2948+
}
2949+
}
2950+
}
2951+
} // namespace
2952+
28922953
static void loadPrimitiveGameThreadPart(
28932954
CesiumGltf::Model& model,
28942955
UCesiumGltfComponent* pGltf,
@@ -2932,6 +2993,14 @@ static void loadPrimitiveGameThreadPart(
29322993
pInstancedComponent->AddInstance(transform, false);
29332994
}
29342995
pInstancedComponent->pInstanceFeatures = pInstanceFeatures;
2996+
2997+
const std::optional<FCesiumFeaturesMetadataDescription>&
2998+
maybeFeaturesDescription =
2999+
pTilesetActor->getFeaturesMetadataDescription();
3000+
if (maybeFeaturesDescription) {
3001+
addInstanceFeatureIds(pInstancedComponent, *maybeFeaturesDescription);
3002+
}
3003+
29353004
pCesiumPrimitive = pInstancedComponent;
29363005
} else {
29373006
auto* pComponent =

Source/CesiumRuntime/Public/Cesium3DTileset.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1114,6 +1114,11 @@ class CESIUMRUNTIME_API ACesium3DTileset : public AActor {
11141114
return this->_pTileset.Get();
11151115
}
11161116

1117+
const std::optional<FCesiumFeaturesMetadataDescription>&
1118+
getFeaturesMetadataDescription() const {
1119+
return this->_featuresMetadataDescription;
1120+
}
1121+
11171122
// AActor overrides (some or most of them should be protected)
11181123
virtual bool ShouldTickIfViewportsOnly() const override;
11191124
virtual void Tick(float DeltaTime) override;

0 commit comments

Comments
 (0)