Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Unreal API for terrain height querying #1421

Open
wants to merge 71 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 67 commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
58d9852
First pass for terrain query test
csciguy8 May 7, 2024
c3318dd
Update cesium-native
csciguy8 May 7, 2024
35c0f8d
Get rid of unnecessary tileset. Hook up ::getHeightsAtCoordinates call
csciguy8 May 8, 2024
945fe93
Add ability for test to wait for terrain queries to finish
csciguy8 May 8, 2024
a67a6c4
Let verify function have access to creation context
csciguy8 May 9, 2024
467ebf3
Make separate pass to visualize results
csciguy8 May 9, 2024
2f268a8
Place cube at test query position to verify result
csciguy8 May 9, 2024
9db7b69
Expand test to query 25 times in a grid, rather than once
csciguy8 May 10, 2024
28c88dc
Update cesium-native
csciguy8 May 16, 2024
2dcf78b
updates from forEachPrimitiveInScene changes
csciguy8 May 16, 2024
08efed9
Update to new ::getHeightsAtCoordinates return type
csciguy8 May 28, 2024
322f00c
Merge branch 'main' into terrain-query-test
csciguy8 May 28, 2024
41ac3ce
Merge branch 'main' into terrain-query-test
csciguy8 May 31, 2024
a53b8bf
Update cesium-native
csciguy8 May 31, 2024
17487ad
Update to new height query return struct. Don't show test cube if no …
csciguy8 May 31, 2024
d8c6417
Update cesium-native
csciguy8 Jun 13, 2024
4324a53
Modify load query test to support warnings
csciguy8 Jun 20, 2024
45c3b7f
Fix bug where results were slightly below terrain
csciguy8 Jun 21, 2024
1928e5a
Warning fix
csciguy8 Jun 21, 2024
77693e1
Update cesium-native
csciguy8 Jul 8, 2024
9e5fa97
Merge branch 'main' into terrain-query-test
csciguy8 Jul 8, 2024
5707715
Modify model loading to support non-engine models too
csciguy8 Jul 10, 2024
5c654e0
update test location to denver hills
csciguy8 Jul 23, 2024
f733b12
update cesium-native
csciguy8 Jul 24, 2024
6d6dcf6
Merge branch 'main' into terrain-query-test
csciguy8 Jul 29, 2024
a9a13bc
Update cesium-native
csciguy8 Jul 29, 2024
985ad2a
Update cesium-native
csciguy8 Jul 29, 2024
e41e9a5
Add multiple queries test (stub)
csciguy8 Jul 29, 2024
3127b95
WIP for multiple query test
csciguy8 Jul 29, 2024
3b87c71
Changes to let multiple query test successfully run
csciguy8 Jul 31, 2024
96c5473
update cesium native, minor tweaks to test
csciguy8 Jul 31, 2024
0dc2ae1
update cesium-native
csciguy8 Aug 1, 2024
3709b8f
Merge branch 'main' into terrain-query-test
csciguy8 Aug 1, 2024
0a7b446
Update cesium-native
csciguy8 Aug 1, 2024
5cc80e1
Merge remote-tracking branch 'origin/main' into terrain-query-test
kring Aug 27, 2024
66edcdf
Update cesium-native, performance measurement tweaks.
kring Aug 27, 2024
ef2c2f3
Update cesium-native again.
kring Aug 27, 2024
ebbb105
Update cesium-native.
kring Aug 28, 2024
2a2e916
Merge remote-tracking branch 'origin/main' into terrain-query-test
kring Sep 10, 2024
48dd310
Update cesium-native.
kring Sep 12, 2024
1435d3b
Update cesium-native.
kring Sep 16, 2024
52aba21
Update cesium-native.
kring Sep 16, 2024
6e52599
Check for failed height samples.
kring Sep 16, 2024
7caa4f5
Update cesium-native.
kring Sep 16, 2024
d7447cf
Basic support for height queries via Blueprints.
kring Sep 17, 2024
a3c8158
Cleaner height sampling function.
kring Sep 17, 2024
d5b4559
Sample heights from Unreal C++ too.
kring Sep 17, 2024
abe6308
Formatting.
kring Sep 17, 2024
c0d7325
Add missing Category.
kring Sep 17, 2024
9bcd807
Add missing header.
kring Sep 17, 2024
396b709
Fix compiler errors.
kring Sep 17, 2024
0f8f9f9
Merge remote-tracking branch 'origin/main' into terrain-query-test
kring Sep 17, 2024
6852eae
Another missing header, only on some platforms.
kring Sep 17, 2024
3aed881
Doc, parameter names.
kring Sep 18, 2024
a37e455
Use Unreal height query API in tests.
kring Sep 18, 2024
87a6fdd
Formatting.
kring Sep 18, 2024
5770b74
Renames, add height query against Google perf test.
kring Sep 19, 2024
cf032b3
Improve names and organization of perf tests.
kring Sep 19, 2024
60f16ac
Add tests for SampleHeightMostDetailed.
kring Sep 19, 2024
06f20d5
Update CHANGES.md.
kring Sep 19, 2024
898002a
Fix non-Editor compilation.
kring Sep 19, 2024
87deacf
Another missing header, I guess.
kring Sep 19, 2024
49ec369
Disable distance field generation in test project.
kring Sep 20, 2024
dfc393f
Debug message to see if timing out test is caused by slow tick.
kring Sep 22, 2024
460a559
Attempt to tick during test.
kring Sep 23, 2024
13c51b9
Make sure the Editor ticks during tests.
kring Sep 23, 2024
934ce11
Allow ticks in the other tests, too.
kring Sep 23, 2024
78d279d
Doc tweaks + formatting
j9liu Sep 26, 2024
07d8954
HeightSampled -> SampleSuccess, update cesium-native.
kring Sep 27, 2024
ea7e110
Return valid result even when tileset fails to load.
kring Sep 27, 2024
5ec5ea4
Better handle tilesets not created yet, and add a test for it.
kring Sep 27, 2024
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 @@

### Next version (not released yet)

##### Additions :tada:

- Added `SampleHeightMostDetailed` function to `Cesium3DTileset`, which asynchronously queries the height of a tileset at a list of positions. It is callable from both C++ and Blueprints.

##### Fixes :wrench:

- Drastically reduced tile mesh memory usage in UE 5.3 and 5.4 by working around a bug that causes those engine versions to add more texture coordinate sets than necessary.
Expand Down
73 changes: 73 additions & 0 deletions Source/CesiumRuntime/Private/Cesium3DTileset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,79 @@ void ACesium3DTileset::SetMobility(EComponentMobility::Type NewMobility) {
}
}

void ACesium3DTileset::SampleHeightMostDetailed(
const TArray<FVector>& LongitudeLatitudeHeightArray,
FCesiumSampleHeightMostDetailedCallback OnHeightsSampled) {
CesiumAsync::Future<Cesium3DTilesSelection::Tileset*> futureNativeTileset =
this->GetTileset()
? getAsyncSystem().createResolvedFuture(this->GetTileset())
: getAsyncSystem().runInMainThread([this]() {
return IsValid(this) ? this->GetTileset() : nullptr;
});

std::vector<CesiumGeospatial::Cartographic> positions;
positions.reserve(LongitudeLatitudeHeightArray.Num());

for (const FVector& position : LongitudeLatitudeHeightArray) {
positions.emplace_back(CesiumGeospatial::Cartographic::fromDegrees(
position.X,
position.Y,
position.Z));
}

std::move(futureNativeTileset)
.thenImmediately([positions = std::move(positions)](
Cesium3DTilesSelection::Tileset* pTileset) {
if (pTileset) {
return pTileset->sampleHeightMostDetailed(positions);
} else {
return getAsyncSystem().createResolvedFuture(
Cesium3DTilesSelection::SampleHeightResult{
{},
{},
{"Could not sample heights from tileset because it has not been created."}});
}
})
.thenInMainThread(
[this, OnHeightsSampled = std::move(OnHeightsSampled)](
j9liu marked this conversation as resolved.
Show resolved Hide resolved
Cesium3DTilesSelection::SampleHeightResult&& result) {
if (!IsValid(this))
return;

check(result.positions.size() == result.heightSampled.size());

// This should do nothing, but will prevent undefined behavior if
// the array sizes are unexpectedly different.
result.heightSampled.resize(result.positions.size(), false);

TArray<FCesiumSampleHeightResult> ue;
ue.Reserve(result.positions.size());

for (size_t i = 0; i < result.positions.size(); ++i) {
const CesiumGeospatial::Cartographic& position =
result.positions[i];

FCesiumSampleHeightResult uePosition;
uePosition.LongitudeLatitudeHeight = FVector(
j9liu marked this conversation as resolved.
Show resolved Hide resolved
CesiumUtility::Math::radiansToDegrees(position.longitude),
CesiumUtility::Math::radiansToDegrees(position.latitude),
position.height);
uePosition.HeightSampled = result.heightSampled[i];

ue.Emplace(std::move(uePosition));
}

TArray<FString> warnings;
warnings.Reserve(result.warnings.size());

for (const std::string& warning : result.warnings) {
warnings.Emplace(UTF8_TO_TCHAR(warning.c_str()));
}

OnHeightsSampled.ExecuteIfBound(this, ue, warnings);
});
}

void ACesium3DTileset::SetGeoreference(
TSoftObjectPtr<ACesiumGeoreference> NewGeoreference) {
this->Georeference = NewGeoreference;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#pragma once

#include "HAL/Platform.h"
#include <gsl/span>

enum class ECesiumMetadataType : uint8;
Expand Down
4 changes: 2 additions & 2 deletions Source/CesiumRuntime/Private/CesiumMetadataPrimitive.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// Copyright 2020-2024 CesiumGS, Inc. and Contributors

PRAGMA_DISABLE_DEPRECATION_WARNINGS

#include "CesiumMetadataPrimitive.h"
#include "CesiumGltf/Model.h"

PRAGMA_DISABLE_DEPRECATION_WARNINGS

FCesiumMetadataPrimitive::FCesiumMetadataPrimitive(
const FCesiumPrimitiveFeatures& PrimitiveFeatures,
const FCesiumPrimitiveMetadata& PrimitiveMetadata,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright 2020-2024 CesiumGS, Inc. and Contributors
#include "CesiumSampleHeightMostDetailedAsyncAction.h"
#include "Cesium3DTileset.h"
#include "CesiumRuntime.h"

/*static*/ UCesiumSampleHeightMostDetailedAsyncAction*
UCesiumSampleHeightMostDetailedAsyncAction::SampleHeightMostDetailed(
ACesium3DTileset* Tileset,
const TArray<FVector>& LongitudeLatitudeHeightArray) {
UCesiumSampleHeightMostDetailedAsyncAction* pAsyncAction =
NewObject<UCesiumSampleHeightMostDetailedAsyncAction>();
pAsyncAction->_pTileset = Tileset;
pAsyncAction->_longitudeLatitudeHeightArray = LongitudeLatitudeHeightArray;

return pAsyncAction;
}

void UCesiumSampleHeightMostDetailedAsyncAction::Activate() {
this->RegisterWithGameInstance(this->_pTileset);

this->_pTileset->SampleHeightMostDetailed(
this->_longitudeLatitudeHeightArray,
FCesiumSampleHeightMostDetailedCallback::CreateUObject(
this,
&UCesiumSampleHeightMostDetailedAsyncAction::RaiseOnHeightsSampled));
}

void UCesiumSampleHeightMostDetailedAsyncAction::RaiseOnHeightsSampled(
ACesium3DTileset* Tileset,
const TArray<FCesiumSampleHeightResult>& Result,
const TArray<FString>& Warnings) {
this->OnHeightsSampled.Broadcast(Result, Warnings);
this->SetReadyToDestroy();
}
61 changes: 38 additions & 23 deletions Source/CesiumRuntime/Private/Tests/CesiumLoadTestCore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,13 @@ struct LoadTestContext {

LoadTestContext gLoadTestContext;

DEFINE_LATENT_AUTOMATION_COMMAND_THREE_PARAMETER(
DEFINE_LATENT_AUTOMATION_COMMAND_FOUR_PARAMETER(
TimeLoadingCommand,
FString,
loggingName,
SceneGenerationContext&,
creationContext,
SceneGenerationContext&,
playContext,
TestPass&,
pass);
Expand Down Expand Up @@ -75,31 +77,43 @@ bool TimeLoadingCommand::Update() {
bool tilesetsloaded = playContext.areTilesetsDoneLoading();
bool timedOut = pass.elapsedTime >= testTimeout;

if (tilesetsloaded || timedOut) {
pass.endMark = timeMark;
UE_LOG(LogCesium, Display, TEXT("-- Load end mark -- %s"), *loggingName);
if (timedOut) {
UE_LOG(
LogCesium,
Error,
TEXT("TIMED OUT: Loading stopped after %.2f seconds"),
pass.elapsedTime);
// Command is done
pass.testInProgress = false;
return true;
}

if (timedOut) {
UE_LOG(
LogCesium,
Error,
TEXT("TIMED OUT: Loading stopped after %.2f seconds"),
pass.elapsedTime);
} else {
if (tilesetsloaded) {
// Run verify step as part of timing
// This is useful for running additional logic after a load, or if the step
// exists in the pass solely, timing very specific functionality (like
// terrain queries)
bool verifyComplete = true;
if (pass.verifyStep)
verifyComplete =
pass.verifyStep(creationContext, playContext, pass.optionalParameter);

if (verifyComplete) {
pass.endMark = FPlatformTime::Seconds();
UE_LOG(LogCesium, Display, TEXT("-- Load end mark -- %s"), *loggingName);

pass.elapsedTime = pass.endMark - pass.startMark;
UE_LOG(
LogCesium,
Display,
TEXT("Tileset load completed in %.2f seconds"),
TEXT("Pass completed in %.2f seconds"),
pass.elapsedTime);
}

if (pass.verifyStep)
pass.verifyStep(playContext, pass.optionalParameter);
pass.testInProgress = false;

pass.testInProgress = false;

// Command is done
return true;
// Command is done
return true;
}
}

// Let world tick, we'll come back to this command
Expand Down Expand Up @@ -163,8 +177,6 @@ bool TestCleanupCommand::Update() {
else
defaultReportStep(context.testPasses);

// Turn on the editor tileset updates so we can see what we loaded
// gLoadTestContext.creationContext.setSuspendUpdate(false);
return true;
}

Expand Down Expand Up @@ -246,8 +258,11 @@ bool RunLoadTest(
// Do our timing capture
FString loggingName = testName + "-" + pass.name;

ADD_LATENT_AUTOMATION_COMMAND(
TimeLoadingCommand(loggingName, context.playContext, pass));
ADD_LATENT_AUTOMATION_COMMAND(TimeLoadingCommand(
loggingName,
context.creationContext,
context.playContext,
pass));

ADD_LATENT_AUTOMATION_COMMAND(FWaitLatentCommand(1.0f));

Expand Down
9 changes: 6 additions & 3 deletions Source/CesiumRuntime/Private/Tests/CesiumLoadTestCore.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,14 @@ namespace Cesium {
struct TestPass {
typedef swl::variant<int, float> TestingParameter;
typedef std::function<void(SceneGenerationContext&, TestingParameter)>
PassCallback;
SetupCallback;
typedef std::function<
bool(SceneGenerationContext&, SceneGenerationContext&, TestingParameter)>
VerifyCallback;

FString name;
PassCallback setupStep;
PassCallback verifyStep;
SetupCallback setupStep;
VerifyCallback verifyStep;
TestingParameter optionalParameter;

bool testInProgress = false;
Expand Down
35 changes: 35 additions & 0 deletions Source/CesiumRuntime/Private/Tests/CesiumTestHelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
#include "CesiumGeoreference.h"
#include "Engine/Engine.h"

#if WITH_EDITOR
#include "Editor/EditorPerformanceSettings.h"
#endif

namespace CesiumTestHelpers {

UWorld* getGlobalWorldContext() {
Expand Down Expand Up @@ -63,6 +67,37 @@ FName getUniqueTag(UActorComponent* pComponent) {
return FName(FString::Printf(TEXT("%lld"), pComponent));
}

#if WITH_EDITOR
namespace {
size_t timesAllowingEditorTick = 0;
bool originalEditorTickState = true;
} // namespace
#endif

void pushAllowTickInEditor() {
#if WITH_EDITOR
if (timesAllowingEditorTick == 0) {
UEditorPerformanceSettings* pSettings =
GetMutableDefault<UEditorPerformanceSettings>();
originalEditorTickState = pSettings->bThrottleCPUWhenNotForeground;
pSettings->bThrottleCPUWhenNotForeground = false;
}

++timesAllowingEditorTick;
#endif
}

void popAllowTickInEditor() {
#if WITH_EDITOR
--timesAllowingEditorTick;
if (timesAllowingEditorTick == 0) {
UEditorPerformanceSettings* pSettings =
GetMutableDefault<UEditorPerformanceSettings>();
pSettings->bThrottleCPUWhenNotForeground = originalEditorTickState;
}
#endif
}

void trackForPlay(AActor* pEditorActor) {
pEditorActor->Tags.Add(getUniqueTag(pEditorActor));
}
Expand Down
19 changes: 18 additions & 1 deletion Source/CesiumRuntime/Private/Tests/CesiumTestHelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,10 @@ void waitForImpl(
done.Execute();
} else {
pWorld->GetTimerManager().SetTimerForNextTick(
[done, pWorld, condition, timerHandle]() mutable {
[done,
pWorld,
condition = std::forward<T>(condition),
timerHandle]() mutable {
waitForImpl<T>(done, pWorld, std::move(condition), timerHandle);
});
}
Expand Down Expand Up @@ -135,6 +138,20 @@ FName getUniqueTag(AActor* pActor);
/// <returns>The unique tag.</returns>
FName getUniqueTag(UActorComponent* pComponent);

/// <summary>
/// By default, UE 5.3+ don't tick in a headless Editor, which is often used to
/// run tests. Call this at the start of a test that requires ticking to
/// override this default. Call popAllowTickInEditor after the test to restore
/// the default.
/// </summary>
void pushAllowTickInEditor();

/// <summary>
/// Call this after a test that needs working ticking to restore the default
/// state.
/// </summary>
void popAllowTickInEditor();

#if WITH_EDITOR

/// <summary>
Expand Down
Loading