Skip to content

Commit 6f9822d

Browse files
authored
Merge pull request #1669 from GhisBntly/enh-1658_mesh-build-callbacks
[enhancement#1658] mesh, material and tile loading callbacks
2 parents d5a1aae + 52bd20d commit 6f9822d

16 files changed

+513
-24
lines changed

CHANGES.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
### ? - ?
44

5+
##### Additions :tada:
6+
7+
- Added the interface `ICesium3DTilesetLifecycleEventReceiver`: when an implementation is registered on a tileset (with `ACesium3DTileset::SetLifecycleEventReceiver`), its functions will be called at various points in a tile's lifecycle, like when a mesh component is created, when a material is instanced, when the tile changes visibility, when it is unloaded, etc.
8+
59
##### Fixes :wrench:
610

711
- Fixed a problem where multi-selecting `UCesiumGlobeAnchorComponent` could cause the selected components to teleport to 0 degrees longitude and 0 degrees latitude. Now, the geospatial position and orientation fields are hidden while multi-selecting.

Source/CesiumRuntime/Private/Cesium3DTileset.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright 2020-2024 CesiumGS, Inc. and Contributors
22

33
#include "Cesium3DTileset.h"
4+
45
#include "Async/Async.h"
56
#include "Camera/CameraTypes.h"
67
#include "Camera/PlayerCameraManager.h"
@@ -9,6 +10,7 @@
910
#include "Cesium3DTilesSelection/TilesetLoadFailureDetails.h"
1011
#include "Cesium3DTilesSelection/TilesetOptions.h"
1112
#include "Cesium3DTilesSelection/TilesetSharedAssetSystem.h"
13+
#include "Cesium3DTilesetLifecycleEventReceiver.h"
1214
#include "Cesium3DTilesetLoadFailureDetails.h"
1315
#include "Cesium3DTilesetRoot.h"
1416
#include "CesiumActors.h"
@@ -48,12 +50,14 @@
4850
#include "StereoRendering.h"
4951
#include "UnrealPrepareRendererResources.h"
5052
#include "VecMath.h"
53+
5154
#include <glm/gtc/matrix_inverse.hpp>
5255
#include <memory>
5356
#include <spdlog/spdlog.h>
5457

5558
#ifdef CESIUM_DEBUG_TILE_STATES
5659
#include "HAL/PlatformFileManager.h"
60+
5761
#include <Cesium3DTilesSelection/DebugTileStateDatabase.h>
5862
#endif
5963

@@ -2315,3 +2319,16 @@ void ACesium3DTileset::RuntimeSettingsChanged(
23152319
}
23162320
}
23172321
#endif
2322+
2323+
ICesium3DTilesetLifecycleEventReceiver*
2324+
ACesium3DTileset::GetLifecycleEventReceiver() {
2325+
return Cast<ICesium3DTilesetLifecycleEventReceiver>(
2326+
this->_pLifecycleEventReceiver);
2327+
}
2328+
2329+
void ACesium3DTileset::SetLifecycleEventReceiver(UObject* InEventReceiver) {
2330+
if (UKismetSystemLibrary::DoesImplementInterface(
2331+
InEventReceiver,
2332+
UCesium3DTilesetLifecycleEventReceiver::StaticClass()))
2333+
this->_pLifecycleEventReceiver = InEventReceiver;
2334+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Copyright 2020-2025 CesiumGS, Inc. and Contributors
2+
3+
#include "Cesium3DTilesetLifecycleEventReceiver.h"
4+
5+
#include "Materials/MaterialInstanceDynamic.h"
6+
7+
UMaterialInstanceDynamic*
8+
ICesium3DTilesetLifecycleEventReceiver::CreateMaterial(
9+
ICesiumLoadedTilePrimitive& TilePrimitive,
10+
UMaterialInterface* DefaultBaseMaterial,
11+
const FName& Name) {
12+
// Default implementation: just create a new instance
13+
return UMaterialInstanceDynamic::Create(DefaultBaseMaterial, nullptr, Name);
14+
}
15+
16+
void ICesium3DTilesetLifecycleEventReceiver::CustomizeMaterial(
17+
ICesiumLoadedTilePrimitive& TilePrimitive,
18+
UMaterialInstanceDynamic& Material,
19+
const UCesiumMaterialUserData* CesiumData,
20+
const CesiumGltf::Material& GltfMaterial) {}
21+
22+
void ICesium3DTilesetLifecycleEventReceiver::OnTileMeshPrimitiveLoaded(
23+
ICesiumLoadedTilePrimitive& TilePrimitive) {}
24+
25+
void ICesium3DTilesetLifecycleEventReceiver::OnTileLoaded(
26+
ICesiumLoadedTile& Tile) {}
27+
28+
void ICesium3DTilesetLifecycleEventReceiver::OnTileVisibilityChanged(
29+
ICesiumLoadedTile& Tile,
30+
bool bVisible) {}
31+
32+
void ICesium3DTilesetLifecycleEventReceiver::OnTileUnloading(
33+
ICesiumLoadedTile& Tile) {}

Source/CesiumRuntime/Private/CesiumGltfComponent.cpp

Lines changed: 95 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
// Copyright 2020-2024 CesiumGS, Inc. and Contributors
22

33
#include "CesiumGltfComponent.h"
4+
45
#include "Async/Async.h"
6+
#include "Cesium3DTilesetLifecycleEventReceiver.h"
57
#include "CesiumCommon.h"
68
#include "CesiumEncodedMetadataUtility.h"
79
#include "CesiumFeatureIdSet.h"
@@ -1551,6 +1553,8 @@ static void loadPrimitive(
15511553
positionBuffer.Init(numVertices, false);
15521554

15531555
{
1556+
// Note: scaling from glTF vertices to Unreal's must match
1557+
// UCesiumGltfComponent::GetGltfToUnrealLocalVertexPositionScaleFactor
15541558
if (duplicateVertices) {
15551559
TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::CopyDuplicatedPositions)
15561560
for (uint32 i = 0; i < numVertices; ++i) {
@@ -2471,7 +2475,6 @@ static void SetGltfParameterValues(
24712475
CesiumGltf::Model& model,
24722476
LoadedPrimitiveResult& loadResult,
24732477
const CesiumGltf::Material& material,
2474-
const CesiumGltf::MaterialPBRMetallicRoughness& pbr,
24752478
UMaterialInstanceDynamic* pMaterial,
24762479
EMaterialParameterAssociation association,
24772480
int32 index) {
@@ -2483,7 +2486,9 @@ static void SetGltfParameterValues(
24832486
index),
24842487
static_cast<float>(textureCoordinateSet.second));
24852488
}
2486-
2489+
const CesiumGltf::MaterialPBRMetallicRoughness& pbr =
2490+
material.pbrMetallicRoughness ? material.pbrMetallicRoughness.value()
2491+
: defaultPbrMetallicRoughness;
24872492
if (pbr.baseColorFactor.size() > 3) {
24882493
pMaterial->SetVectorParameterValueByInfo(
24892494
FMaterialParameterInfo("baseColorFactor", association, index),
@@ -3138,11 +3143,6 @@ static void loadPrimitiveGameThreadPart(
31383143
const CesiumGltf::Material& material =
31393144
loadResult.materialIndex != -1 ? model.materials[loadResult.materialIndex]
31403145
: defaultMaterial;
3141-
3142-
const CesiumGltf::MaterialPBRMetallicRoughness& pbr =
3143-
material.pbrMetallicRoughness ? material.pbrMetallicRoughness.value()
3144-
: defaultPbrMetallicRoughness;
3145-
31463146
const FName ImportedSlotName(
31473147
*(TEXT("CesiumMaterial") + FString::FromInt(nextMaterialId++)));
31483148

@@ -3168,13 +3168,22 @@ static void loadPrimitiveGameThreadPart(
31683168
}
31693169
#endif
31703170

3171-
UMaterialInstanceDynamic* pMaterialForGltfPrimitive;
3171+
// Move this right now: CreateMaterial may need them!
3172+
// "Safe" even though loadResult is still used later, because the methods used
3173+
// during material setup (SetGltfParameterValues, etc.) below do not use these
3174+
// members.
3175+
primData.Features = std::move(loadResult.Features);
3176+
primData.Metadata = std::move(loadResult.Metadata);
3177+
3178+
UMaterialInstanceDynamic* pMaterialForGltfPrimitive = nullptr;
3179+
ICesium3DTilesetLifecycleEventReceiver* pLifecycleEventReceiver =
3180+
pTilesetActor->GetLifecycleEventReceiver();
31723181
{
31733182
TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::SetupMaterial)
3183+
ensure(pUserDesignatedMaterial);
31743184

31753185
UMaterialInstanceDynamic* pUserDesignatedMaterialAsDynamic =
31763186
Cast<UMaterialInstanceDynamic>(pUserDesignatedMaterial);
3177-
31783187
// If the user-designated material is a UMaterialInstanceDynamic, Create()
31793188
// will reject it as a valid instance parent. Defer to its non-dynamic
31803189
// parent instead.
@@ -3183,18 +3192,33 @@ static void loadPrimitiveGameThreadPart(
31833192
? pUserDesignatedMaterialAsDynamic->Parent.Get()
31843193
: pUserDesignatedMaterial;
31853194

3186-
pMaterialForGltfPrimitive = UMaterialInstanceDynamic::Create(
3187-
pBaseMaterial,
3188-
nullptr,
3189-
ImportedSlotName);
3195+
if (pLifecycleEventReceiver) {
3196+
// Possibility to override the material for this primitive
3197+
pMaterialForGltfPrimitive = pLifecycleEventReceiver->CreateMaterial(
3198+
*pCesiumPrimitive,
3199+
pBaseMaterial,
3200+
ImportedSlotName);
3201+
check(pMaterialForGltfPrimitive);
3202+
// pMaterialForGltfPrimitive created above may not have used the
3203+
// suggested pBaseMaterial passed as input
3204+
pBaseMaterial = pMaterialForGltfPrimitive->Parent.Get();
3205+
// may have changed but we don't need it from now on:
3206+
pUserDesignatedMaterialAsDynamic = nullptr;
3207+
} else {
3208+
// Same as ICesium3DTilesetLifecycleEventReceiver::CreateMaterial's
3209+
// default implementation
3210+
pMaterialForGltfPrimitive = UMaterialInstanceDynamic::Create(
3211+
pBaseMaterial,
3212+
nullptr,
3213+
ImportedSlotName);
3214+
}
31903215

31913216
pMaterialForGltfPrimitive->SetFlags(
31923217
RF_Transient | RF_DuplicateTransient | RF_TextExportTransient);
31933218
SetGltfParameterValues(
31943219
model,
31953220
loadResult,
31963221
material,
3197-
pbr,
31983222
pMaterialForGltfPrimitive,
31993223
EMaterialParameterAssociation::GlobalParameter,
32003224
INDEX_NONE);
@@ -3257,7 +3281,6 @@ static void loadPrimitiveGameThreadPart(
32573281
model,
32583282
loadResult,
32593283
material,
3260-
pbr,
32613284
pMaterialForGltfPrimitive,
32623285
EMaterialParameterAssociation::LayerParameter,
32633286
0);
@@ -3350,10 +3373,16 @@ static void loadPrimitiveGameThreadPart(
33503373
it.FontPage);
33513374
}
33523375
}
3353-
}
33543376

3355-
primData.Features = std::move(loadResult.Features);
3356-
primData.Metadata = std::move(loadResult.Metadata);
3377+
// Extra material customizations
3378+
if (pLifecycleEventReceiver) {
3379+
pLifecycleEventReceiver->CustomizeMaterial(
3380+
*pCesiumPrimitive,
3381+
*pMaterialForGltfPrimitive,
3382+
pCesiumData,
3383+
material);
3384+
}
3385+
}
33573386

33583387
primData.EncodedFeatures = std::move(loadResult.EncodedFeatures);
33593388
primData.EncodedMetadata = std::move(loadResult.EncodedMetadata);
@@ -3426,6 +3455,11 @@ static void loadPrimitiveGameThreadPart(
34263455
TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::RegisterComponent)
34273456
pMesh->RegisterComponent();
34283457
}
3458+
3459+
// Call the observer callback (if any) once all is done
3460+
if (pLifecycleEventReceiver) {
3461+
pLifecycleEventReceiver->OnTileMeshPrimitiveLoaded(*pCesiumPrimitive);
3462+
}
34293463
}
34303464

34313465
/*static*/ CesiumAsync::Future<UCesiumGltfComponent::CreateOffGameThreadResult>
@@ -3464,6 +3498,7 @@ UCesiumGltfComponent::CreateOffGameThread(
34643498
// }
34653499

34663500
UCesiumGltfComponent* Gltf = NewObject<UCesiumGltfComponent>(pTilesetActor);
3501+
Gltf->pTile = &tile;
34673502
Gltf->SetMobility(pTilesetActor->GetRootComponent()->Mobility);
34683503
Gltf->SetFlags(RF_Transient | RF_DuplicateTransient | RF_TextExportTransient);
34693504

@@ -3510,11 +3545,24 @@ UCesiumGltfComponent::CreateOffGameThread(
35103545
}
35113546
}
35123547

3548+
if (ICesium3DTilesetLifecycleEventReceiver* Receiver =
3549+
pTilesetActor->GetLifecycleEventReceiver()) {
3550+
Receiver->OnTileLoaded(*Gltf);
3551+
}
3552+
35133553
Gltf->SetVisibility(false, true);
35143554
Gltf->SetCollisionEnabled(ECollisionEnabled::NoCollision);
35153555
return Gltf;
35163556
}
35173557

3558+
void UCesiumGltfComponent::OnVisibilityChanged() {
3559+
USceneComponent::OnVisibilityChanged();
3560+
ICesium3DTilesetLifecycleEventReceiver* pLifecycleEventReceiver =
3561+
GetTilesetActor().GetLifecycleEventReceiver();
3562+
if (pLifecycleEventReceiver)
3563+
pLifecycleEventReceiver->OnTileVisibilityChanged(*this, GetVisibleFlag());
3564+
}
3565+
35183566
UCesiumGltfComponent::UCesiumGltfComponent() : USceneComponent() {
35193567
// Structure to hold one-time initialization
35203568
struct FConstructorStatics {
@@ -3545,6 +3593,35 @@ UCesiumGltfComponent::UCesiumGltfComponent() : USceneComponent() {
35453593
PrimaryComponentTick.bCanEverTick = false;
35463594
}
35473595

3596+
const CesiumGltf::Model* UCesiumGltfComponent::GetGltfModel() const {
3597+
if (pTile) {
3598+
if (auto RenderContent = pTile->getContent().getRenderContent())
3599+
return &RenderContent->getModel();
3600+
}
3601+
return nullptr;
3602+
}
3603+
3604+
const FCesiumModelMetadata& UCesiumGltfComponent::GetModelMetadata() const {
3605+
return Metadata;
3606+
}
3607+
3608+
const Cesium3DTilesSelection::TileID& UCesiumGltfComponent::GetTileID() const {
3609+
return pTile->getTileID();
3610+
}
3611+
3612+
ACesium3DTileset& UCesiumGltfComponent::GetTilesetActor() {
3613+
return *Cast<ACesium3DTileset>(GetOuter());
3614+
}
3615+
3616+
FVector
3617+
UCesiumGltfComponent::GetGltfToUnrealLocalVertexPositionScaleFactor() const {
3618+
// Note: replicates logic from (static) loadPrimitive
3619+
return FVector(
3620+
CesiumPrimitiveData::positionScaleFactor,
3621+
-CesiumPrimitiveData::positionScaleFactor,
3622+
CesiumPrimitiveData::positionScaleFactor);
3623+
}
3624+
35483625
void UCesiumGltfComponent::UpdateTransformFromCesium(
35493626
const glm::dmat4& cesiumToUnrealTransform) {
35503627
for (USceneComponent* pSceneComponent : this->GetAttachChildren()) {

Source/CesiumRuntime/Private/CesiumGltfComponent.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@
55
#include "Cesium3DTilesSelection/Tile.h"
66
#include "Cesium3DTileset.h"
77
#include "CesiumEncodedMetadataUtility.h"
8+
#include "CesiumLoadedTile.h"
89
#include "CesiumModelMetadata.h"
910
#include "Components/PrimitiveComponent.h"
1011
#include "Components/SceneComponent.h"
1112
#include "CoreMinimal.h"
1213
#include "CustomDepthParameters.h"
1314
#include "EncodedFeaturesMetadata.h"
1415
#include "Interfaces/IHttpRequest.h"
16+
#include "Templates/Function.h"
1517
#include <CesiumAsync/SharedFuture.h>
1618
#include <glm/mat4x4.hpp>
1719
#include <memory>
@@ -56,7 +58,7 @@ struct FRasterOverlayTile {
5658
};
5759

5860
UCLASS()
59-
class UCesiumGltfComponent : public USceneComponent {
61+
class UCesiumGltfComponent : public USceneComponent, public ICesiumLoadedTile {
6062
GENERATED_BODY()
6163

6264
public:
@@ -104,6 +106,8 @@ class UCesiumGltfComponent : public USceneComponent {
104106
UPROPERTY(EditAnywhere, Category = "Rendering")
105107
FCustomDepthParameters CustomDepthParameters{};
106108

109+
const Cesium3DTilesSelection::Tile* pTile = nullptr;
110+
107111
FCesiumModelMetadata Metadata{};
108112
EncodedFeaturesMetadata::EncodedModelMetadata EncodedMetadata{};
109113

@@ -131,6 +135,14 @@ class UCesiumGltfComponent : public USceneComponent {
131135
virtual void SetCollisionEnabled(ECollisionEnabled::Type NewType);
132136

133137
virtual void BeginDestroy() override;
138+
virtual void OnVisibilityChanged() override;
139+
140+
// from ICesiumLoadedTile
141+
const CesiumGltf::Model* GetGltfModel() const override;
142+
const FCesiumModelMetadata& GetModelMetadata() const override;
143+
const Cesium3DTilesSelection::TileID& GetTileID() const override;
144+
ACesium3DTileset& GetTilesetActor() override;
145+
FVector GetGltfToUnrealLocalVertexPositionScaleFactor() const override;
134146

135147
void UpdateFade(float fadePercentage, bool fadingIn);
136148

0 commit comments

Comments
 (0)