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.
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.
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
Hi, I am using cesium ion’s sample height most detailed and it consistently gives height or elevation values below the tile surface for cesium 3d tiles using cesium geo reference asset with valid asset id and token. I am using the following structure in level blueprint but always getting a height value below the tile surface although georeference is centered at 0,0,0 (x,y,z) unreal engine co-ordinate
Keep in mind that the height returned by SampleHeightMostDetailed is relative to the WGS84 ellipsoid, which is not always aligned with the terrain’s surface. You can always convert this WGS84-relative height to something else, like its equivalent in Mean Sea Level (MSL)
We currently don’t have any way of calculating MSL height in the plugin, but there are some posts in the forum that give suggestions, like so: