Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
8b92701
[enhancement#1658] mesh, material and tile loading callbacks
GhisBntly May 8, 2025
6025fa9
Account for most of the feedback received to this day
GhisBntly May 13, 2025
51d7b21
Fix missing condition on GetLifecycleEventReceiver() pointer
GhisBntly May 19, 2025
e30313e
Reworked/simplified CreateMaterial/CustomizeMaterial
GhisBntly May 19, 2025
dd2bf54
revert adding CPU access option from Tilesets
GhisBntly May 28, 2025
e15b06a
Merge branch 'main' into enh-1658_mesh-build-callbacks
GhisBntly May 28, 2025
65efbd2
Remove CPU access option mention from CHANGES.md
GhisBntly May 28, 2025
40676e5
Merge remote-tracking branch 'cesium/main' into enh-1658_mesh-build-c…
GhisBntly Jun 11, 2025
5d357f5
AdvViz: add GetVertexPositionScaleFactor to UCesiumLoadedTile to make…
GhisBntly Jun 11, 2025
380fa13
CesiumMetadataValueAccess => FCesiumMetadataValueAccess
GhisBntly Jun 17, 2025
335d612
missing fwd decl
GhisBntly Jun 17, 2025
d58decc
Hide implementation detail
GhisBntly Jun 17, 2025
e4336ac
Revert addition of tile render-readiness concept
GhisBntly Jul 22, 2025
cb8cade
Merge remote-tracking branch 'origin/main' into enh-1658_mesh-build-c…
GhisBntly Jul 22, 2025
c33861f
Update CHANGES.md
GhisBntly Jul 22, 2025
8113a0a
Merge remote-tracking branch 'origin/main' into enh-1658_mesh-build-c…
GhisBntly Aug 14, 2025
f047b24
_lifecycleEventReceive => _pLifecycleEventReceive, make it visible to GC
GhisBntly Aug 14, 2025
1293e31
Methods are no longer pure virtual, which is more user friendly
GhisBntly Aug 14, 2025
e16a061
language
GhisBntly Aug 14, 2025
1adf464
Revert "CesiumMetadataValueAccess => FCesiumMetadataValueAccess"
GhisBntly Aug 14, 2025
f2fd3a0
Semantics and comments' rewrite by K.Ring
GhisBntly Aug 14, 2025
67f880c
require that CreateMaterial returns non-null
GhisBntly Aug 14, 2025
ceb0511
remove the need for GlTFmaterialPBR from CustomizeMaterial
GhisBntly Aug 14, 2025
17c87f8
bring back GetGltfModel, probably lost during a merge...
GhisBntly Aug 14, 2025
7c8b222
FindTexCoordIndexForGltfAttribute => FindTextureCoordinateIndexForGlt…
GhisBntly Aug 14, 2025
ebd52b1
doc comments in CesiumLoadedTile.h
GhisBntly Aug 14, 2025
2430526
Merge remote-tracking branch 'origin/main' into enh-1658_mesh-build-c…
kring Oct 16, 2025
42f31ec
Move changelog entry to correct version.
kring Oct 16, 2025
ca34f86
clang-format
kring Oct 16, 2025
7e19b17
Doc tweaks.
kring Oct 16, 2025
a08335e
Doc tweaks.
kring Oct 16, 2025
2daba76
clang-format.
kring Oct 16, 2025
f8293a5
More doc tweaks.
kring Oct 16, 2025
ff67aa4
Avoid auto.
kring Oct 16, 2025
c8b3006
Move implementation out of header.
kring Oct 16, 2025
cf29ac1
clang-format
kring Oct 16, 2025
4bb28fe
Only provide const pointers to tiles.
kring Oct 16, 2025
52bd20d
Minor code tweaks.
kring Oct 16, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

### ? - ?

##### Additions :tada:

- 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.

##### Fixes :wrench:

- 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.
Expand Down
17 changes: 17 additions & 0 deletions Source/CesiumRuntime/Private/Cesium3DTileset.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright 2020-2024 CesiumGS, Inc. and Contributors

#include "Cesium3DTileset.h"

#include "Async/Async.h"
#include "Camera/CameraTypes.h"
#include "Camera/PlayerCameraManager.h"
Expand All @@ -9,6 +10,7 @@
#include "Cesium3DTilesSelection/TilesetLoadFailureDetails.h"
#include "Cesium3DTilesSelection/TilesetOptions.h"
#include "Cesium3DTilesSelection/TilesetSharedAssetSystem.h"
#include "Cesium3DTilesetLifecycleEventReceiver.h"
#include "Cesium3DTilesetLoadFailureDetails.h"
#include "Cesium3DTilesetRoot.h"
#include "CesiumActors.h"
Expand Down Expand Up @@ -48,12 +50,14 @@
#include "StereoRendering.h"
#include "UnrealPrepareRendererResources.h"
#include "VecMath.h"

#include <glm/gtc/matrix_inverse.hpp>
#include <memory>
#include <spdlog/spdlog.h>

#ifdef CESIUM_DEBUG_TILE_STATES
#include "HAL/PlatformFileManager.h"

#include <Cesium3DTilesSelection/DebugTileStateDatabase.h>
#endif

Expand Down Expand Up @@ -2315,3 +2319,16 @@ void ACesium3DTileset::RuntimeSettingsChanged(
}
}
#endif

ICesium3DTilesetLifecycleEventReceiver*
ACesium3DTileset::GetLifecycleEventReceiver() {
return Cast<ICesium3DTilesetLifecycleEventReceiver>(
this->_pLifecycleEventReceiver);
}

void ACesium3DTileset::SetLifecycleEventReceiver(UObject* InEventReceiver) {
if (UKismetSystemLibrary::DoesImplementInterface(
InEventReceiver,
UCesium3DTilesetLifecycleEventReceiver::StaticClass()))
this->_pLifecycleEventReceiver = InEventReceiver;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright 2020-2025 CesiumGS, Inc. and Contributors

#include "Cesium3DTilesetLifecycleEventReceiver.h"

#include "Materials/MaterialInstanceDynamic.h"

UMaterialInstanceDynamic*
ICesium3DTilesetLifecycleEventReceiver::CreateMaterial(
ICesiumLoadedTilePrimitive& TilePrimitive,
UMaterialInterface* DefaultBaseMaterial,
const FName& Name) {
// Default implementation: just create a new instance
return UMaterialInstanceDynamic::Create(DefaultBaseMaterial, nullptr, Name);
}

void ICesium3DTilesetLifecycleEventReceiver::CustomizeMaterial(
ICesiumLoadedTilePrimitive& TilePrimitive,
UMaterialInstanceDynamic& Material,
const UCesiumMaterialUserData* CesiumData,
const CesiumGltf::Material& GltfMaterial) {}

void ICesium3DTilesetLifecycleEventReceiver::OnTileMeshPrimitiveLoaded(
ICesiumLoadedTilePrimitive& TilePrimitive) {}

void ICesium3DTilesetLifecycleEventReceiver::OnTileLoaded(
ICesiumLoadedTile& Tile) {}

void ICesium3DTilesetLifecycleEventReceiver::OnTileVisibilityChanged(
ICesiumLoadedTile& Tile,
bool bVisible) {}

void ICesium3DTilesetLifecycleEventReceiver::OnTileUnloading(
ICesiumLoadedTile& Tile) {}
113 changes: 95 additions & 18 deletions Source/CesiumRuntime/Private/CesiumGltfComponent.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// Copyright 2020-2024 CesiumGS, Inc. and Contributors

#include "CesiumGltfComponent.h"

#include "Async/Async.h"
#include "Cesium3DTilesetLifecycleEventReceiver.h"
#include "CesiumCommon.h"
#include "CesiumEncodedMetadataUtility.h"
#include "CesiumFeatureIdSet.h"
Expand Down Expand Up @@ -1551,6 +1553,8 @@ static void loadPrimitive(
positionBuffer.Init(numVertices, false);

{
// Note: scaling from glTF vertices to Unreal's must match
// UCesiumGltfComponent::GetGltfToUnrealLocalVertexPositionScaleFactor
if (duplicateVertices) {
TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::CopyDuplicatedPositions)
for (uint32 i = 0; i < numVertices; ++i) {
Expand Down Expand Up @@ -2471,7 +2475,6 @@ static void SetGltfParameterValues(
CesiumGltf::Model& model,
LoadedPrimitiveResult& loadResult,
const CesiumGltf::Material& material,
const CesiumGltf::MaterialPBRMetallicRoughness& pbr,
UMaterialInstanceDynamic* pMaterial,
EMaterialParameterAssociation association,
int32 index) {
Expand All @@ -2483,7 +2486,9 @@ static void SetGltfParameterValues(
index),
static_cast<float>(textureCoordinateSet.second));
}

const CesiumGltf::MaterialPBRMetallicRoughness& pbr =
material.pbrMetallicRoughness ? material.pbrMetallicRoughness.value()
: defaultPbrMetallicRoughness;
if (pbr.baseColorFactor.size() > 3) {
pMaterial->SetVectorParameterValueByInfo(
FMaterialParameterInfo("baseColorFactor", association, index),
Expand Down Expand Up @@ -3138,11 +3143,6 @@ static void loadPrimitiveGameThreadPart(
const CesiumGltf::Material& material =
loadResult.materialIndex != -1 ? model.materials[loadResult.materialIndex]
: defaultMaterial;

const CesiumGltf::MaterialPBRMetallicRoughness& pbr =
material.pbrMetallicRoughness ? material.pbrMetallicRoughness.value()
: defaultPbrMetallicRoughness;

const FName ImportedSlotName(
*(TEXT("CesiumMaterial") + FString::FromInt(nextMaterialId++)));

Expand All @@ -3168,13 +3168,22 @@ static void loadPrimitiveGameThreadPart(
}
#endif

UMaterialInstanceDynamic* pMaterialForGltfPrimitive;
// Move this right now: CreateMaterial may need them!
// "Safe" even though loadResult is still used later, because the methods used
// during material setup (SetGltfParameterValues, etc.) below do not use these
// members.
primData.Features = std::move(loadResult.Features);
primData.Metadata = std::move(loadResult.Metadata);

UMaterialInstanceDynamic* pMaterialForGltfPrimitive = nullptr;
ICesium3DTilesetLifecycleEventReceiver* pLifecycleEventReceiver =
pTilesetActor->GetLifecycleEventReceiver();
{
TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::SetupMaterial)
ensure(pUserDesignatedMaterial);

UMaterialInstanceDynamic* pUserDesignatedMaterialAsDynamic =
Cast<UMaterialInstanceDynamic>(pUserDesignatedMaterial);

// If the user-designated material is a UMaterialInstanceDynamic, Create()
// will reject it as a valid instance parent. Defer to its non-dynamic
// parent instead.
Expand All @@ -3183,18 +3192,33 @@ static void loadPrimitiveGameThreadPart(
? pUserDesignatedMaterialAsDynamic->Parent.Get()
: pUserDesignatedMaterial;

pMaterialForGltfPrimitive = UMaterialInstanceDynamic::Create(
pBaseMaterial,
nullptr,
ImportedSlotName);
if (pLifecycleEventReceiver) {
// Possibility to override the material for this primitive
pMaterialForGltfPrimitive = pLifecycleEventReceiver->CreateMaterial(
*pCesiumPrimitive,
pBaseMaterial,
ImportedSlotName);
check(pMaterialForGltfPrimitive);
// pMaterialForGltfPrimitive created above may not have used the
// suggested pBaseMaterial passed as input
pBaseMaterial = pMaterialForGltfPrimitive->Parent.Get();
// may have changed but we don't need it from now on:
pUserDesignatedMaterialAsDynamic = nullptr;
} else {
// Same as ICesium3DTilesetLifecycleEventReceiver::CreateMaterial's
// default implementation
pMaterialForGltfPrimitive = UMaterialInstanceDynamic::Create(
pBaseMaterial,
nullptr,
ImportedSlotName);
}

pMaterialForGltfPrimitive->SetFlags(
RF_Transient | RF_DuplicateTransient | RF_TextExportTransient);
SetGltfParameterValues(
model,
loadResult,
material,
pbr,
pMaterialForGltfPrimitive,
EMaterialParameterAssociation::GlobalParameter,
INDEX_NONE);
Expand Down Expand Up @@ -3257,7 +3281,6 @@ static void loadPrimitiveGameThreadPart(
model,
loadResult,
material,
pbr,
pMaterialForGltfPrimitive,
EMaterialParameterAssociation::LayerParameter,
0);
Expand Down Expand Up @@ -3350,10 +3373,16 @@ static void loadPrimitiveGameThreadPart(
it.FontPage);
}
}
}

primData.Features = std::move(loadResult.Features);
primData.Metadata = std::move(loadResult.Metadata);
// Extra material customizations
if (pLifecycleEventReceiver) {
pLifecycleEventReceiver->CustomizeMaterial(
*pCesiumPrimitive,
*pMaterialForGltfPrimitive,
pCesiumData,
material);
}
}

primData.EncodedFeatures = std::move(loadResult.EncodedFeatures);
primData.EncodedMetadata = std::move(loadResult.EncodedMetadata);
Expand Down Expand Up @@ -3426,6 +3455,11 @@ static void loadPrimitiveGameThreadPart(
TRACE_CPUPROFILER_EVENT_SCOPE(Cesium::RegisterComponent)
pMesh->RegisterComponent();
}

// Call the observer callback (if any) once all is done
if (pLifecycleEventReceiver) {
pLifecycleEventReceiver->OnTileMeshPrimitiveLoaded(*pCesiumPrimitive);
}
}

/*static*/ CesiumAsync::Future<UCesiumGltfComponent::CreateOffGameThreadResult>
Expand Down Expand Up @@ -3464,6 +3498,7 @@ UCesiumGltfComponent::CreateOffGameThread(
// }

UCesiumGltfComponent* Gltf = NewObject<UCesiumGltfComponent>(pTilesetActor);
Gltf->pTile = &tile;
Gltf->SetMobility(pTilesetActor->GetRootComponent()->Mobility);
Gltf->SetFlags(RF_Transient | RF_DuplicateTransient | RF_TextExportTransient);

Expand Down Expand Up @@ -3510,11 +3545,24 @@ UCesiumGltfComponent::CreateOffGameThread(
}
}

if (ICesium3DTilesetLifecycleEventReceiver* Receiver =
pTilesetActor->GetLifecycleEventReceiver()) {
Receiver->OnTileLoaded(*Gltf);
}

Gltf->SetVisibility(false, true);
Gltf->SetCollisionEnabled(ECollisionEnabled::NoCollision);
return Gltf;
}

void UCesiumGltfComponent::OnVisibilityChanged() {
USceneComponent::OnVisibilityChanged();
ICesium3DTilesetLifecycleEventReceiver* pLifecycleEventReceiver =
GetTilesetActor().GetLifecycleEventReceiver();
if (pLifecycleEventReceiver)
pLifecycleEventReceiver->OnTileVisibilityChanged(*this, GetVisibleFlag());
}

UCesiumGltfComponent::UCesiumGltfComponent() : USceneComponent() {
// Structure to hold one-time initialization
struct FConstructorStatics {
Expand Down Expand Up @@ -3545,6 +3593,35 @@ UCesiumGltfComponent::UCesiumGltfComponent() : USceneComponent() {
PrimaryComponentTick.bCanEverTick = false;
}

const CesiumGltf::Model* UCesiumGltfComponent::GetGltfModel() const {
if (pTile) {
if (auto RenderContent = pTile->getContent().getRenderContent())
return &RenderContent->getModel();
}
return nullptr;
}

const FCesiumModelMetadata& UCesiumGltfComponent::GetModelMetadata() const {
return Metadata;
}

const Cesium3DTilesSelection::TileID& UCesiumGltfComponent::GetTileID() const {
return pTile->getTileID();
}

ACesium3DTileset& UCesiumGltfComponent::GetTilesetActor() {
return *Cast<ACesium3DTileset>(GetOuter());
}

FVector
UCesiumGltfComponent::GetGltfToUnrealLocalVertexPositionScaleFactor() const {
// Note: replicates logic from (static) loadPrimitive
return FVector(
CesiumPrimitiveData::positionScaleFactor,
-CesiumPrimitiveData::positionScaleFactor,
CesiumPrimitiveData::positionScaleFactor);
}

void UCesiumGltfComponent::UpdateTransformFromCesium(
const glm::dmat4& cesiumToUnrealTransform) {
for (USceneComponent* pSceneComponent : this->GetAttachChildren()) {
Expand Down
14 changes: 13 additions & 1 deletion Source/CesiumRuntime/Private/CesiumGltfComponent.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@
#include "Cesium3DTilesSelection/Tile.h"
#include "Cesium3DTileset.h"
#include "CesiumEncodedMetadataUtility.h"
#include "CesiumLoadedTile.h"
#include "CesiumModelMetadata.h"
#include "Components/PrimitiveComponent.h"
#include "Components/SceneComponent.h"
#include "CoreMinimal.h"
#include "CustomDepthParameters.h"
#include "EncodedFeaturesMetadata.h"
#include "Interfaces/IHttpRequest.h"
#include "Templates/Function.h"
#include <CesiumAsync/SharedFuture.h>
#include <glm/mat4x4.hpp>
#include <memory>
Expand Down Expand Up @@ -56,7 +58,7 @@ struct FRasterOverlayTile {
};

UCLASS()
class UCesiumGltfComponent : public USceneComponent {
class UCesiumGltfComponent : public USceneComponent, public ICesiumLoadedTile {
GENERATED_BODY()

public:
Expand Down Expand Up @@ -104,6 +106,8 @@ class UCesiumGltfComponent : public USceneComponent {
UPROPERTY(EditAnywhere, Category = "Rendering")
FCustomDepthParameters CustomDepthParameters{};

const Cesium3DTilesSelection::Tile* pTile = nullptr;

FCesiumModelMetadata Metadata{};
EncodedFeaturesMetadata::EncodedModelMetadata EncodedMetadata{};

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

virtual void BeginDestroy() override;
virtual void OnVisibilityChanged() override;

// from ICesiumLoadedTile
const CesiumGltf::Model* GetGltfModel() const override;
const FCesiumModelMetadata& GetModelMetadata() const override;
const Cesium3DTilesSelection::TileID& GetTileID() const override;
ACesium3DTileset& GetTilesetActor() override;
FVector GetGltfToUnrealLocalVertexPositionScaleFactor() const override;

void UpdateFade(float fadePercentage, bool fadingIn);

Expand Down
Loading