SampleHeightMostDetailed causes crash when called in BeginPlay()

I have an actor in my game right now that will place a static mesh in the world at a coordinate location and at cesium terrain height. I am using SampleHeightMostDetailed to get the placement height, which is called during BeginPlay for the spawner. The issue I am having is this causes an access violation exception. The call stack says it crashes inside of CreditSystem::createCredit but the plugin doesn’t come with debug symbols.

Here is my call stack:

|>|UnrealEditor-CesiumRuntime.dll!CesiumUtility::CreditSystem::createCredit(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > &&,bool)|C++|
|---|---|---|
| |UnrealEditor-CesiumRuntime.dll!std::_Func_impl_no_alloc<class <lambda_027f1ced2456d505fee4b8218291e8a5>,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &>::_Target_type(void)|C++|
| |UnrealEditor-CesiumRuntime.dll!CesiumRasterOverlays::QuadtreeRasterOverlayTileProvider::~QuadtreeRasterOverlayTileProvider(void)|C++|
| |UnrealEditor-CesiumRuntime.dll!CesiumRasterOverlays::BingMapsRasterOverlay::createTileProvider(class CesiumAsync::AsyncSystem const &,class std::shared_ptr<class CesiumAsync::IAssetAccessor> const &,class std::shared_ptr<class CesiumUtility::CreditSystem> const &,class std::shared_ptr<class CesiumRasterOverlays::IPrepareRasterOverlayRendererResources> const &,class std::shared_ptr<class spdlog::logger> const &,class CesiumUtility::IntrusivePointer<class CesiumRasterOverlays::RasterOverlay const >)|C++|
| |UnrealEditor-CesiumRuntime.dll!CesiumRasterOverlays::IonRasterOverlay::createTileProvider(struct CesiumRasterOverlays::IonRasterOverlay::ExternalAssetEndpoint const &,class CesiumAsync::AsyncSystem const &,class std::shared_ptr<class CesiumAsync::IAssetAccessor> const &,class std::shared_ptr<class CesiumUtility::CreditSystem> const &,class std::shared_ptr<class CesiumRasterOverlays::IPrepareRasterOverlayRendererResources> const &,class std::shared_ptr<class spdlog::logger> const &,class CesiumUtility::IntrusivePointer<class CesiumRasterOverlays::RasterOverlay const >)|C++|
| |UnrealEditor-CesiumRuntime.dll!CesiumRasterOverlays::IonRasterOverlay::createTileProvider(class CesiumAsync::AsyncSystem const &,class std::shared_ptr<class CesiumAsync::IAssetAccessor> const &,class std::shared_ptr<class CesiumUtility::CreditSystem> const &,class std::shared_ptr<class CesiumRasterOverlays::IPrepareRasterOverlayRendererResources> const &,class std::shared_ptr<class spdlog::logger> const &,class CesiumUtility::IntrusivePointer<class CesiumRasterOverlays::RasterOverlay const >)|C++|
| |UnrealEditor-CesiumRuntime.dll!Cesium3DTilesSelection::RasterOverlayCollection::add(class CesiumUtility::IntrusivePointer<class CesiumRasterOverlays::RasterOverlay> const &)|C++|
| |UnrealEditor-CesiumRuntime.dll!UCesiumRasterOverlay::AddToTileset() Line 103|C++|
| |UnrealEditor-CesiumRuntime.dll!ACesium3DTileset::LoadTileset() Line 1300|C++|
| |UnrealEditor-CesiumRuntime.dll!ACesium3DTileset::SampleHeightMostDetailed(const TArray<UE::Math::TVector<double>,TSizedDefaultAllocator<32>> & LongitudeLatitudeHeightArray, TDelegate<void __cdecl(ACesium3DTileset *,TArray<FCesiumSampleHeightResult,TSizedDefaultAllocator<32>> const &,TArray<FString,TSizedDefaultAllocator<32>> const &),FDefaultDelegateUserPolicy> OnHeightsSampled) Line 132|C++|

This issue seems to be related but was resolved

Hi @Timski_HII,

A few questions to help get to the bottom of this:

  • What version of Cesium for Unreal are you using?
  • Are you creating the Cesium3DTileset and its raster overlay at runtime? Or does it exist in the Outliner in the Editor, too?
  • If it’s the latter, does your Outliner also have a CesiumCreditSystemBP?
  • When does this happen? Play-in-Editor? In a built game? When running on a headless server?

Hi Kevin,

I am using Cesium for Unreal v2.8.0 downloaded through the Epic Games Launcher. I am running into this issue in the PIE. So far this issue has been with the Tileset and all Cesium actors in the outliner. Once this actor is working it will be used on a level that generates everything at runtime.

To better isolate the problem I have created a new empty level and added Cesium CWT + Bing Maps Aerial to it though the Cesium menu. CesiumCameraManager and CesiumCreditSystemBP as also added from this.

Then I add my spawner with one object to spawn at runtime.

When I press play I get the exception I mentioned above.

Here is the spawner class I wrote:
Spawner.h

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "StaticObstacle.h"
#include <CesiumGeoreference.h>
#include <Cesium3DTileset.h>
#include "StaticObstacleSpawner.generated.h"

USTRUCT(BlueprintType)
struct FStaticObstacleContainer
{
	GENERATED_BODY()

	/* Placement */
	UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Placement")
	FVector LongitudeLatitudeHeight;

	/* Mesh Settings*/
	UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Mesh")
	UStaticMesh* Mesh;

	UPROPERTY()
	TWeakObjectPtr<AStaticObstacle> Obstacle;
};

UCLASS()
class ODYSSEY_VW_API AStaticObstacleSpawner : public AActor
{
	GENERATED_BODY()

public:
	// Sets default values for this actor's properties
	AStaticObstacleSpawner();

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:
	// Called every frame
	virtual void Tick(float DeltaTime) override;

	TWeakObjectPtr<ACesiumGeoreference> Georeference;
	TWeakObjectPtr<ACesium3DTileset> Tileset;

	UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Obstacles")
	TArray<FStaticObstacleContainer> Obstacles;

	UFUNCTION(BlueprintCallable)
	void AddObstacle(double Longitude, double Latitude, UStaticMesh* Mesh);
};

Spawner.cpp

#include "Actors/Objects/StaticObstacleSpawner.h"
#include <Utility/GeoTools.h>
#include <Kismet/GameplayStatics.h>

// Sets default values
AStaticObstacleSpawner::AStaticObstacleSpawner()
{
	// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = false;

	Georeference = ACesiumGeoreference::GetDefaultGeoreference(GetWorld());

}

// Called when the game starts or when spawned
void AStaticObstacleSpawner::BeginPlay()
{
	Super::BeginPlay();
	UWorld* World = GetWorld();
	TArray<FVector> Locations;

	for (FStaticObstacleContainer& Obstacle : Obstacles)
	{
		Locations.Add(Obstacle.LongitudeLatitudeHeight);
		Obstacle.Obstacle = World->SpawnActor<AStaticObstacle>();
		Obstacle.Obstacle->SetStaticMesh(Obstacle.Mesh);
		Obstacle.Obstacle->SetCesiumLocation(Obstacle.LongitudeLatitudeHeight);
	}

	FCesiumSampleHeightMostDetailedCallback CesiumSampleHeightMostDetailedCallback;
	CesiumSampleHeightMostDetailedCallback.BindLambda(
		[this](ACesium3DTileset* Tileset, const TArray<FCesiumSampleHeightResult>& Results, const TArray<FString>& Strings) mutable
		{
			Tileset;
			Strings;

			if (this->Obstacles.Num() != Results.Num())
			{
				return;
			}

			auto LocationIter = Results.CreateConstIterator();
			for (auto ObstacleIter = this->Obstacles.CreateIterator(); ObstacleIter; ++ObstacleIter)
			{
				if (LocationIter->SampleSuccess)
				{
					FVector LongitudeLatitudeHeight = LocationIter->LongitudeLatitudeHeight;
					LongitudeLatitudeHeight.Z += UGeoTools::ElipsoidGeoidDifference(LongitudeLatitudeHeight.X, LongitudeLatitudeHeight.Y);
					ObstacleIter->Obstacle->SetCesiumLocation(LongitudeLatitudeHeight);
				}
				LocationIter++;
			}
		}
	);

	if (!IsValid(Tileset.Get()))
	{
		Tileset = Cast<ACesium3DTileset>(UGameplayStatics::GetActorOfClass(World, ACesium3DTileset::StaticClass()));
	}

	Tileset->SampleHeightMostDetailed(Locations, CesiumSampleHeightMostDetailedCallback);
}

// Called every frame
void AStaticObstacleSpawner::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);
}

void AStaticObstacleSpawner::AddObstacle(double Longitude, double Latitude, UStaticMesh* Mesh)
{
}

I’m having the same issue.
Unreal 5.4.4, Windows 11, tried with Cesium 2.8, 2.9 and 2.10

Calling the blueprint function “SampleHeightMostDetailed” from “Event BeginPlay” crashes Unreal (and can render both the project and the blueprint unopenable).

Probably happens because the tile isn’t fully loaded yet.
If I bind the event to “On Tileset Loaded” everything goes smoothly, and I don’t get any crashes.

Blueprint snippet that currently works for me

I figured moving it to the OnTilesetLoad delegate would fix this issue. The Sample Height function call is async though so it should handle its components not being loaded yet

@Timski_HII I’m guessing you mean v2.9.0, since SampleHeightMostDetailed wasn’t available in v2.8.0?

I think I see the problem. I wrote an issue describing it:

I mention a workaround at the end, which I’m pretty sure should work. If you try it out, let me know.