wip(assembler): round S9 -- collision look-ahead: ground ring ready on arrival up to 40 m/s and 50 m/s falls

voxel.StreamingCollisionLookahead (default on) predicts the support tile 350 ms
ahead along full 3D velocity and prioritises its 9-tile collision ring. Owner
plan, BuildHost actor: 40 m/s arrival-ready 25/74 -> 74/74 (CVar-off control
15/74); 6/12/25 m/s stay 100%; -Z 50 m/s 93/93. Spawn unchanged (~7.7 s).
Critical LOD0 path at 40 m/s: generation 67 ms p50, RMC body update 38 ms p50.
LOD8 cost counted (native horizon 48 ms of 60), optimisation deferred.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
This commit is contained in:
Fr0zkaandClaude Opus 5.5 committed 2026-10-19 10:56:54 +02:00
1 parent 8440adc742
commit e7586a1256
7 files changed
+507 -70

No files matched your search

+186 -65
View File
@@ -197,6 +197,12 @@ namespace
GVoxelForgeStreamingDynamicNearReserve,
TEXT("Keep the authored near-worker reserve while moving or before the walkable ring is ready; default 1 releases idle slots after that gate, 0 restores the legacy fixed reserve."));
int32 GVoxelForgeStreamingCollisionLookahead = 1;
FAutoConsoleVariableRef CVarVoxelForgeStreamingCollisionLookahead(
TEXT("voxel.StreamingCollisionLookahead"),
GVoxelForgeStreamingCollisionLookahead,
TEXT("Request up to two collision rings ahead along player velocity; 0 disables predictive ring admission."));
int32 GVoxelForgeStreamingLegacyPendingResultCap = 0;
FAutoConsoleVariableRef CVarVoxelForgeStreamingLegacyPendingResultCap(
TEXT("voxel.StreamingLegacyPendingResultCap"),
@@ -2056,6 +2062,7 @@ void AVoxelWorld::BeginPlay()
int32 CommandLineSampleStacks = GVoxelForgeSampleStacks;
int32 CommandLineCollisionGate = GVoxelForgeCollisionGate;
int32 CommandLineCoreRegionConcurrentAdmission = GVoxelForgeCoreRegionConcurrentAdmission;
int32 CommandLineStreamingCollisionLookahead = GVoxelForgeStreamingCollisionLookahead;
int32 CommandLineStreamingResultBacklogCap = GVoxelForgeStreamingResultBacklogCap;
int32 CommandLineStep1GenerationCap = GVoxelForgeStep1GenerationCap;
int32 CommandLineStep1ExitAfterFrames = GVoxelForgeStep1ExitAfterFrames;
@@ -2134,6 +2141,12 @@ void AVoxelWorld::BeginPlay()
GVoxelForgeStreamingDynamicNearReserve);
FParse::Value(FCommandLine::Get(), TEXT("voxel.StreamingLegacyPendingResultCap="),
GVoxelForgeStreamingLegacyPendingResultCap);
if (FParse::Value(FCommandLine::Get(), TEXT("voxel.StreamingCollisionLookahead="),
CommandLineStreamingCollisionLookahead))
{
GVoxelForgeStreamingCollisionLookahead =
CommandLineStreamingCollisionLookahead != 0 ? 1 : 0;
}
if (FParse::Value(FCommandLine::Get(), TEXT("voxel.CoreRegionConcurrentAdmission="),
CommandLineCoreRegionConcurrentAdmission))
{
@@ -6896,8 +6909,8 @@ void AVoxelWorld::Tick(float DeltaTime)
AdvanceHeadlessCollisionGateStressTest(PlayerLastPos, PlayerHeading, PlayerPawn);
AdvanceHeadlessDigMotionTest(PlayerLastPos, PlayerPawn);
PeakObservedPawnSpeedCmPerSecond = FMath::Max(
PeakObservedPawnSpeedCmPerSecond, PlayerHeading.Size2D());
StartupTraceSpeedCmPerSecond = PlayerHeading.Size2D();
PeakObservedPawnSpeedCmPerSecond, PlayerHeading.Size());
StartupTraceSpeedCmPerSecond = PlayerHeading.Size();
UpdatePawnCollisionGate(PlayerPawn, PlayerLastPos, DeltaTime);
RecordHeadlessDigMotionFrame(PlayerPawn, PlayerLastPos, DeltaTime);
ObserveHeadlessStrateCrossingTest(PlayerLastPos, PlayerPawn);
@@ -10253,6 +10266,52 @@ void AVoxelWorld::BuildDesiredTransitionIndex()
}
}
FIntVector AVoxelWorld::GetStreamingHeadingStep(const FVector& PlayerHeading,
const APawn* PlayerPawn) const
{
FVector Heading = GetActorTransform().InverseTransformVectorNoScale(PlayerHeading);
if (Heading.IsNearlyZero() && IsValid(PlayerPawn))
{
Heading = GetActorTransform().InverseTransformVectorNoScale(
PlayerPawn->GetActorForwardVector());
}
const float AbsX = FMath::Abs(Heading.X);
const float AbsY = FMath::Abs(Heading.Y);
const float AbsZ = FMath::Abs(Heading.Z);
if (AbsX <= KINDA_SMALL_NUMBER && AbsY <= KINDA_SMALL_NUMBER
&& AbsZ <= KINDA_SMALL_NUMBER)
{
return FIntVector::ZeroValue;
}
if (AbsX >= AbsY && AbsX >= AbsZ)
{
return FIntVector(Heading.X >= 0.0f ? 1 : -1, 0, 0);
}
if (AbsY >= AbsZ)
{
return FIntVector(0, Heading.Y >= 0.0f ? 1 : -1, 0);
}
return FIntVector(0, 0, Heading.Z >= 0.0f ? 1 : -1);
}
int32 AVoxelWorld::GetStreamingCollisionLookaheadTiles(
const FVector& PlayerHeading, const FIntVector& HeadingStep) const
{
if (GVoxelForgeStreamingCollisionLookahead == 0
|| HeadingStep == FIntVector::ZeroValue || PlayerHeading.Size() < 50.0f)
{
return 0;
}
// Aim 350 ms ahead, round up to a whole level-0 tile, and cap the lead at two tiles.
// Viser 350 ms d'avance, arrondir au tile L0 superieur et limiter l'avance a deux tiles.
const float TileWidthCm = static_cast<float>(CHUNK_SIZE) * GetActiveVoxelSizeCm();
const int32 RequestedTiles = FMath::CeilToInt(
PlayerHeading.Size() * 0.35f / FMath::Max(TileWidthCm, 1.0f));
return FMath::Clamp(RequestedTiles, 1, 2);
}
void AVoxelWorld::BuildCoreDesiredTiles(const FIntVector& Center, const FVector& PlayerPosition,
APawn* PlayerPawn, const FVector& PlayerHeading,
TArray<FVoxelTileKey>& OutLeavers)
@@ -10279,6 +10338,12 @@ void AVoxelWorld::BuildCoreDesiredTiles(const FIntVector& Center, const FVector&
{CameraVoxels.X, CameraVoxels.Y, CameraVoxels.Z});
}
const FIntVector LookaheadHeadingStep = GetStreamingHeadingStep(PlayerHeading, PlayerPawn);
const int32 LookaheadTiles = GetStreamingCollisionLookaheadTiles(
PlayerHeading, LookaheadHeadingStep);
int32 LookaheadCollisionTilesAdded = 0;
int32 LookaheadDesiredTilesMissing = 0;
TArray<FVoxelTileKey> CandidateKeys;
TSet<FVoxelTileKey> CandidateSet;
TSet<FVoxelTileKey> BaseVisualTiles;
@@ -10357,6 +10422,49 @@ void AVoxelWorld::BuildCoreDesiredTiles(const FIntVector& Center, const FVector&
CollisionAnchorTiles.Add(Key);
}
const FVoxelTileKey PlayerTile(GetTileCoordAtLocalCm(WorldToLocalCm(PlayerPosition)), 0);
FVoxelTileKey OccupiedTile = PlayerTile;
FVoxelTileKey SupportTile;
if (GetPlayerSupportTile(PlayerPawn, PlayerPosition, SupportTile))
{
OccupiedTile = SupportTile;
}
// Reserve the future 3x3 collision rings inside the existing LOD0 clipmap.
// Reserver les prochains anneaux 3x3 dans le clipmap LOD0 deja present.
for (int32 Ahead = 1; Ahead <= LookaheadTiles; ++Ahead)
{
const FIntVector AheadCenter = OccupiedTile.Coord + LookaheadHeadingStep * Ahead;
for (int32 Dy = -1; Dy <= 1; ++Dy)
for (int32 Dx = -1; Dx <= 1; ++Dx)
{
const FVoxelTileKey Key(AheadCenter + FIntVector(Dx, Dy, 0), 0);
if (!CandidateSet.Contains(Key))
{
++LookaheadDesiredTilesMissing;
continue;
}
if (!CollisionDesiredTiles.Contains(Key))
{
CollisionDesiredTiles.Add(Key);
++LookaheadCollisionTilesAdded;
}
if (!BaseVisualTiles.Contains(Key)) CollisionAnchorTiles.Add(Key);
}
}
if (LookaheadTiles > 0 && VoxelForgeStartupTrace::IsActive())
{
VoxelForgeStartupTrace::RecordEvent(TEXT("collision_lookahead"), FString::Printf(
TEXT("\"center\":[%d,%d,%d],\"walkable_center\":[%d,%d,%d],"
"\"heading_step\":[%d,%d,%d],\"speed_cm_s\":%s,"
"\"lookahead_tiles\":%d,\"collision_tiles_added\":%d,\"desired_tiles_missing\":%d"),
Center.X, Center.Y, Center.Z, OccupiedTile.Coord.X, OccupiedTile.Coord.Y,
OccupiedTile.Coord.Z,
LookaheadHeadingStep.X, LookaheadHeadingStep.Y, LookaheadHeadingStep.Z,
*FString::SanitizeFloat(PlayerHeading.Size()), LookaheadTiles,
LookaheadCollisionTilesAdded, LookaheadDesiredTilesMissing));
}
for (const FVoxelTileKey& Key : CollisionAnchorTiles)
{
if (!BaseVisualTiles.Contains(Key) && !FullVisualAnchorTiles.Contains(Key))
@@ -10391,13 +10499,6 @@ void AVoxelWorld::BuildCoreDesiredTiles(const FIntVector& Center, const FVector&
}
CancelObsoleteTileWork();
const FVoxelTileKey PlayerTile(GetTileCoordAtLocalCm(WorldToLocalCm(PlayerPosition)), 0);
FVoxelTileKey OccupiedTile = PlayerTile;
FVoxelTileKey SupportTile;
if (GetPlayerSupportTile(PlayerPawn, PlayerPosition, SupportTile))
{
OccupiedTile = SupportTile;
}
auto AddCriticalTile = [this](const FVoxelTileKey& Key)
{
if (IsDesired(Key) && !CriticalDesiredTiles.Contains(Key))
@@ -10415,22 +10516,7 @@ void AVoxelWorld::BuildCoreDesiredTiles(const FIntVector& Center, const FVector&
}
AddCriticalTile(OccupiedTile);
FVector Heading = GetActorTransform().InverseTransformVectorNoScale(PlayerHeading);
if (Heading.IsNearlyZero() && IsValid(PlayerPawn))
{
Heading = GetActorTransform().InverseTransformVectorNoScale(
PlayerPawn->GetActorForwardVector());
}
FIntVector HeadingStep = FIntVector::ZeroValue;
const float AbsX = FMath::Abs(Heading.X);
const float AbsY = FMath::Abs(Heading.Y);
const float AbsZ = FMath::Abs(Heading.Z);
if (AbsX > KINDA_SMALL_NUMBER || AbsY > KINDA_SMALL_NUMBER || AbsZ > KINDA_SMALL_NUMBER)
{
if (AbsX >= AbsY && AbsX >= AbsZ) HeadingStep.X = Heading.X >= 0.0f ? 1 : -1;
else if (AbsY >= AbsZ) HeadingStep.Y = Heading.Y >= 0.0f ? 1 : -1;
else HeadingStep.Z = Heading.Z >= 0.0f ? 1 : -1;
}
const FIntVector HeadingStep = LookaheadHeadingStep;
if (HeadingStep != FIntVector::ZeroValue)
{
FVoxelTileKey NextTile = OccupiedTile;
@@ -10443,9 +10529,19 @@ void AVoxelWorld::BuildCoreDesiredTiles(const FIntVector& Center, const FVector&
for (int32 Dy = -1; Dy <= 1; ++Dy)
for (int32 Dx = -1; Dx <= 1; ++Dx)
{
const FVoxelTileKey WalkableKey(PlayerTile.Coord + FIntVector(Dx, Dy, 0), 0);
const FVoxelTileKey WalkableKey(OccupiedTile.Coord + FIntVector(Dx, Dy, 0), 0);
if (CollisionDesiredTiles.Contains(WalkableKey)) AddCriticalTile(WalkableKey);
}
for (int32 Ahead = 1; Ahead <= LookaheadTiles; ++Ahead)
{
const FIntVector AheadCenter = OccupiedTile.Coord + HeadingStep * Ahead;
for (int32 Dy = -1; Dy <= 1; ++Dy)
for (int32 Dx = -1; Dx <= 1; ++Dx)
{
const FVoxelTileKey WalkableKey(AheadCenter + FIntVector(Dx, Dy, 0), 0);
if (CollisionDesiredTiles.Contains(WalkableKey)) AddCriticalTile(WalkableKey);
}
}
for (int32 Index = CriticalDesiredTiles.Num() - 1; Index >= 0; --Index)
{
const int32 ExistingIndex = DesiredSorted.Find(CriticalDesiredTiles[Index]);
@@ -10710,6 +10806,11 @@ void AVoxelWorld::BuildDesiredTiles(const FIntVector& Center, const FVector& Pla
++DesiredEpoch;
DesiredEpochStartCycles = FPlatformTime::Cycles64();
DesiredEpochCenterChunk = Center;
const FIntVector LookaheadHeadingStep = GetStreamingHeadingStep(PlayerHeading, PlayerPawn);
const int32 LookaheadTiles = GetStreamingCollisionLookaheadTiles(
PlayerHeading, LookaheadHeadingStep);
int32 LookaheadCollisionTilesAdded = 0;
int32 LookaheadDesiredTilesMissing = 0;
const int32 R = Settings ? FMath::Max(1, Settings->ClipRadius) : 6;
const int32 MaxLevel = Settings ? FMath::Clamp(Settings->MaxClipLevel, 0, 8) : 4;
@@ -10791,6 +10892,49 @@ void AVoxelWorld::BuildDesiredTiles(const FIntVector& Center, const FVector& Pla
CollisionDesiredTiles.Add(Key);
}
const FVoxelTileKey PlayerTile(
WorldToChunkCoord(WorldToLocalCm(PlayerPosition)), 0);
FVoxelTileKey OccupiedTile = PlayerTile;
FVoxelTileKey SupportTile;
if (GetPlayerSupportTile(PlayerPawn, PlayerPosition, SupportTile))
{
OccupiedTile = SupportTile;
}
// Reserve predictive collision inside the current clipmap so those tiles cook before arrival.
// Reserver la collision predictive dans le clipmap courant pour cuire avant l'arrivee.
for (int32 Ahead = 1; Ahead <= LookaheadTiles; ++Ahead)
{
const FIntVector AheadCenter = OccupiedTile.Coord + LookaheadHeadingStep * Ahead;
for (int32 Dy = -1; Dy <= 1; ++Dy)
for (int32 Dx = -1; Dx <= 1; ++Dx)
{
const FVoxelTileKey Key(AheadCenter + FIntVector(Dx, Dy, 0), 0);
if (!IsDesired(Key))
{
++LookaheadDesiredTilesMissing;
continue;
}
if (!CollisionDesiredTiles.Contains(Key))
{
CollisionDesiredTiles.Add(Key);
++LookaheadCollisionTilesAdded;
}
}
}
if (LookaheadTiles > 0 && VoxelForgeStartupTrace::IsActive())
{
VoxelForgeStartupTrace::RecordEvent(TEXT("collision_lookahead"), FString::Printf(
TEXT("\"center\":[%d,%d,%d],\"walkable_center\":[%d,%d,%d],"
"\"heading_step\":[%d,%d,%d],\"speed_cm_s\":%s,"
"\"lookahead_tiles\":%d,\"collision_tiles_added\":%d,\"desired_tiles_missing\":%d"),
Center.X, Center.Y, Center.Z, OccupiedTile.Coord.X, OccupiedTile.Coord.Y,
OccupiedTile.Coord.Z,
LookaheadHeadingStep.X, LookaheadHeadingStep.Y, LookaheadHeadingStep.Z,
*FString::SanitizeFloat(PlayerHeading.Size()), LookaheadTiles,
LookaheadCollisionTilesAdded, LookaheadDesiredTilesMissing));
}
if (GVoxelForgeProfileTileGeneration != 0)
{
int32 RenderLOD0Count = 0;
@@ -10860,15 +11004,6 @@ void AVoxelWorld::BuildDesiredTiles(const FIntVector& Center, const FVector& Pla
// one that can catch a falling pawn — followed by the adjacent tile in the dominant movement
// direction. Keep the actor-centre tile as a fallback for capsule/offset edge cases. Only keys
// in the current desired set are promoted, so this never expands the streaming footprint.
const FVoxelTileKey PlayerTile(
WorldToChunkCoord(WorldToLocalCm(PlayerPosition)), 0);
FVoxelTileKey OccupiedTile = PlayerTile;
FVoxelTileKey SupportTile;
if (GetPlayerSupportTile(PlayerPawn, PlayerPosition, SupportTile))
{
OccupiedTile = SupportTile;
}
auto AddCriticalTile = [this](const FVoxelTileKey& Key)
{
if (IsDesired(Key) && !CriticalDesiredTiles.Contains(Key))
@@ -10889,31 +11024,7 @@ void AVoxelWorld::BuildDesiredTiles(const FIntVector& Center, const FVector& Pla
}
AddCriticalTile(OccupiedTile);
FVector Heading = PlayerHeading;
if (Heading.IsNearlyZero() && IsValid(PlayerPawn))
{
Heading = PlayerPawn->GetActorForwardVector();
}
Heading = GetActorTransform().InverseTransformVectorNoScale(Heading);
const float AbsX = FMath::Abs(Heading.X);
const float AbsY = FMath::Abs(Heading.Y);
const float AbsZ = FMath::Abs(Heading.Z);
FIntVector HeadingStep = FIntVector::ZeroValue;
if (AbsX > KINDA_SMALL_NUMBER || AbsY > KINDA_SMALL_NUMBER || AbsZ > KINDA_SMALL_NUMBER)
{
if (AbsX >= AbsY && AbsX >= AbsZ)
{
HeadingStep.X = Heading.X >= 0.0f ? 1 : -1;
}
else if (AbsY >= AbsZ)
{
HeadingStep.Y = Heading.Y >= 0.0f ? 1 : -1;
}
else
{
HeadingStep.Z = Heading.Z >= 0.0f ? 1 : -1;
}
}
const FIntVector HeadingStep = LookaheadHeadingStep;
if (HeadingStep != FIntVector::ZeroValue)
{
FVoxelTileKey NextTile = OccupiedTile;
@@ -10926,9 +11037,19 @@ void AVoxelWorld::BuildDesiredTiles(const FIntVector& Center, const FVector& Pla
for (int32 Dy = -1; Dy <= 1; ++Dy)
for (int32 Dx = -1; Dx <= 1; ++Dx)
{
const FVoxelTileKey WalkableKey(PlayerTile.Coord + FIntVector(Dx, Dy, 0), 0);
const FVoxelTileKey WalkableKey(OccupiedTile.Coord + FIntVector(Dx, Dy, 0), 0);
if (CollisionDesiredTiles.Contains(WalkableKey)) AddCriticalTile(WalkableKey);
}
for (int32 Ahead = 1; Ahead <= LookaheadTiles; ++Ahead)
{
const FIntVector AheadCenter = OccupiedTile.Coord + HeadingStep * Ahead;
for (int32 Dy = -1; Dy <= 1; ++Dy)
for (int32 Dx = -1; Dx <= 1; ++Dx)
{
const FVoxelTileKey WalkableKey(AheadCenter + FIntVector(Dx, Dy, 0), 0);
if (CollisionDesiredTiles.Contains(WalkableKey)) AddCriticalTile(WalkableKey);
}
}
// Insert in reverse so CriticalDesiredTiles[0] remains the occupied floor, [1] the heading
// tile, and all ordinary distance-sorted work follows both.
@@ -11378,7 +11499,7 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
? LastStreamingSchedulerSampleSeconds : StreamingUpdateStartSeconds;
const double IntervalSeconds = FMath::Max(0.0, Now - IntervalStart);
const double BusySeconds = FPlatformTime::ToSeconds64(WorkerCyclesDelta);
const double SpeedCmPerSecond = PlayerHeading.Size2D();
const double SpeedCmPerSecond = PlayerHeading.Size();
VoxelForgeStartupTrace::RecordEvent(TEXT("scheduler_skip"), FString::Printf(
TEXT("\"frame\":%llu,\"reason\":\"%s\",\"desired_count\":%d,"
"\"desired_missing\":%d,\"desired_missing_by_lod\":%s,"
@@ -11589,7 +11710,7 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
bGateCenterChanged ? WalkableCenter.Y - PreviousGateCenter.Y : 0,
bGateCenterChanged ? WalkableCenter.Z - PreviousGateCenter.Z : 0,
bGateCenterChanged ? 1 : 0, bWalkableRingReadyForReserve ? 1 : 0,
WalkableRingReadyCount, *FString::SanitizeFloat(PlayerHeading.Size2D()), DesiredEpoch));
WalkableRingReadyCount, *FString::SanitizeFloat(PlayerHeading.Size()), DesiredEpoch));
bHasLastWalkableGateSample = true;
LastWalkableGateCenter = WalkableCenter;
bLastWalkableGateReady = bWalkableRingReadyForReserve;
@@ -11600,7 +11721,7 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
RecordSchedulerSkip(TEXT("core_region_compile_pending"));
return;
}
const bool bPawnMoving = PlayerHeading.Size2D() >= 50.0f;
const bool bPawnMoving = PlayerHeading.Size() >= 50.0f;
EffectiveNearTaskReserve = GVoxelForgeStreamingDynamicNearReserve == 0
? AuthoredNearTaskReserve
: ((bPawnMoving || !bWalkableRingReadyForReserve) ? AuthoredNearTaskReserve : 0);
@@ -12219,7 +12340,7 @@ void AVoxelWorld::UpdateChunksAroundPosition(const FVector& CenterPosition, APaw
CenterChunk.X, CenterChunk.Y, CenterChunk.Z,
*FString::SanitizeFloat(LocalPawn.X), *FString::SanitizeFloat(LocalPawn.Y),
*FString::SanitizeFloat(LocalPawn.Z),
*FString::SanitizeFloat(PlayerHeading.Size2D()),
*FString::SanitizeFloat(PlayerHeading.Size()),
bWalkableRingReadyForReserve ? 1 : 0,
*DesiredCounts, *ResidentCounts, *PendingCounts,
GetActiveGenerationTaskCount(), GetGenerationAdmissionCount(), MaxTasks, NearTaskReserve,
+4
View File
@@ -702,6 +702,10 @@ private:
void BuildCoreDesiredTiles(const FIntVector& Center, const FVector& PlayerPosition,
APawn* PlayerPawn, const FVector& PlayerHeading,
TArray<FVoxelTileKey>& OutLeavers);
FIntVector GetStreamingHeadingStep(const FVector& PlayerHeading,
const APawn* PlayerPawn) const;
int32 GetStreamingCollisionLookaheadTiles(const FVector& PlayerHeading,
const FIntVector& HeadingStep) const;
void StartPendingCoreRegionCompile();
void LoadCoreTile(const FVoxelTileKey& Tile, EVoxelTileWorkPriority WorkPriority);
void EnqueueStreamingResult(FChunkResult&& Result);
@@ -17490,6 +17490,24 @@ bool RunCoreWorldActorIntegrationTest(const FExploreArguments& Arguments, FStrin
float WalkSpeedCmPerSecond = 600.0f;
FParse::Value(FCommandLine::Get(), TEXT("voxel.TestMoveSpeedCmPerSecond="),
WalkSpeedCmPerSecond);
FString WalkDirectionText(TEXT("0,1,0"));
const FString DirectionKey(TEXT("voxel.TestMoveDirection="));
const FString CommandLineText(FCommandLine::Get());
const int32 DirectionKeyIndex = CommandLineText.Find(
DirectionKey, ESearchCase::IgnoreCase, ESearchDir::FromStart);
if (DirectionKeyIndex != INDEX_NONE)
{
WalkDirectionText = CommandLineText.Mid(DirectionKeyIndex + DirectionKey.Len());
const int32 DirectionEnd = WalkDirectionText.Find(
TEXT(" "), ESearchCase::CaseSensitive, ESearchDir::FromStart);
if (DirectionEnd != INDEX_NONE)
{
WalkDirectionText.LeftInline(DirectionEnd, EAllowShrinking::No);
}
WalkDirectionText.TrimStartAndEndInline();
WalkDirectionText.RemoveFromStart(TEXT("\""));
WalkDirectionText.RemoveFromEnd(TEXT("\""));
}
TArray<double> SpawnTickMilliseconds;
TArray<double> WalkTickMilliseconds;
TArray<TPair<double, double>> TickSamples;
@@ -17569,11 +17587,11 @@ bool RunCoreWorldActorIntegrationTest(const FExploreArguments& Arguments, FStrin
return false;
}
UE_LOG(LogTemp, Display,
TEXT("[VoxelForgeCoreWorldStreamTrace] pass complete=1 recipe=%s voxel_cm=%.3f walk_speed_cm_s=%.0f walk_direction=+Y"),
TEXT("[VoxelForgeCoreWorldStreamTrace] pass complete=1 recipe=%s voxel_cm=%.3f walk_speed_cm_s=%.0f walk_direction=%s"),
Arguments.CoreHybridMesherVersion == 2 ? TEXT("WorldAssemblerHybridV2")
: (Arguments.CoreHybridMesherVersion == 1 ? TEXT("WorldAssemblerHybridV1")
: TEXT("MarchingCubesV1")),
Arguments.CoreVoxelSizeCm, WalkSpeedCmPerSecond);
Arguments.CoreVoxelSizeCm, WalkSpeedCmPerSecond, *WalkDirectionText);
return true;
}