wip(step3): 12.5 cm voxels, fall-through fixed, brick evaluator in game, organic tunnels -- speed gate FAILS
Stopped by the owner's decision (in-game lit captures were eating the round); he judges the look in game instead. 1. Fall-through with the research field: fixed. Deep drop, gate on: 2 holds (0.50 s), min foot clearance +43 cm; gate off: penetrates (-278 cm). Stress fixture offsets converted to metres. 2. VOXEL_SIZE 25 -> 12.5 cm. Physical sizes kept in metres (player, steps, gate, insets, research field sizes, dig radii, test thresholds); class defaults that count chunks/voxels doubled (ClipRadius 6, RenderDistanceChunks 1536, ViewDistance*, StrateViewMargin 6, PawnCollisionRadius 2, SkirtCells 4, EdgeSealThickness 128, decoration spacings, density volume res 256). Asset values override these defaults: the owner must double his own. Ring scale, six workers, idle, step-2f field before the look changes: 64 m cube (519^3) 4.55 s; 32 m cube (259^3) 0.95 s. 3. In game: tiles go through the brick evaluator (proved by a log marker); profile switch rebuilds the loaded world (~5,800 tiles); full-detail fill 12.4 s after spawn in the visible-window test. 4. Look: round winding tunnels, per-column ellipsoid/lattice caches. All step-2f geometry gates pass at 12.5 cm (0 crumbs, 1 solid component per profile, solid share 69-87%). 259^3 ring, six workers: 1.08-2.02 s (worst HyperComplex) -- the 600 ms gate FAILS. Lit captures failed (hidden/off-screen window, then blank frames); not pursued. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
This commit is contained in:
1 parent
ca78352e43
commit
bc0f9ee791
33 files changed
+2691
-803
No files matched your search
@@ -764,8 +764,8 @@ namespace
|
||||
FVoxelStrateMeasureSettings VF_ComposerMeasureSettings()
|
||||
{
|
||||
FVoxelStrateMeasureSettings Settings;
|
||||
Settings.SampleStep = 4;
|
||||
Settings.RadiusInVoxels = 256;
|
||||
Settings.SampleStep = 8;
|
||||
Settings.RadiusInVoxels = 512;
|
||||
Settings.CenterXY = FVector2D::ZeroVector;
|
||||
Settings.MaxCells = 8000000;
|
||||
Settings.MaxRouteRetries = 16;
|
||||
@@ -1752,6 +1752,26 @@ void AVoxelWorld::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||
VoxelForgeStartupTrace::Finish(TEXT("end_play_before_steady_state"));
|
||||
}
|
||||
|
||||
if (bResearchGamePerfEnabled)
|
||||
{
|
||||
ResearchGamePerfMovingFrameTimesMs.Sort();
|
||||
const auto PercentileMs = [this](double Percentile) -> double
|
||||
{
|
||||
if (ResearchGamePerfMovingFrameTimesMs.IsEmpty()) return 0.0;
|
||||
const int32 Index = FMath::Clamp(
|
||||
FMath::CeilToInt(Percentile * ResearchGamePerfMovingFrameTimesMs.Num()) - 1,
|
||||
0, ResearchGamePerfMovingFrameTimesMs.Num() - 1);
|
||||
return ResearchGamePerfMovingFrameTimesMs[Index];
|
||||
};
|
||||
const double MaximumMs = ResearchGamePerfMovingFrameTimesMs.IsEmpty()
|
||||
? 0.0 : ResearchGamePerfMovingFrameTimesMs.Last();
|
||||
UE_LOG(LogTemp, Display,
|
||||
TEXT("[VoxelForgeResearchGamePerf] moving_frames=%d moving_window_s=%.3f "
|
||||
"p50_ms=%.3f p95_ms=%.3f p99_ms=%.3f max_ms=%.3f"),
|
||||
ResearchGamePerfMovingFrameTimesMs.Num(), ResearchGamePerfMovingSeconds,
|
||||
PercentileMs(0.50), PercentileMs(0.95), PercentileMs(0.99), MaximumMs);
|
||||
}
|
||||
|
||||
LogVisualStalenessAudit();
|
||||
GVoxelForgeDigMotionTests.Remove(this);
|
||||
GVoxelForgeVisualGapAudits.Remove(this);
|
||||
@@ -1776,6 +1796,16 @@ void AVoxelWorld::BeginPlay()
|
||||
VoxelForgeRedefinedDensityField::SetParameterSet(ResearchParameterizedCaveSet);
|
||||
}
|
||||
#endif
|
||||
#if WITH_EDITOR
|
||||
AppliedResearchFieldEnabled = VoxelForgeRedefinedDensityField::IsParameterizedEnabled() ? 1 : 0;
|
||||
AppliedResearchFieldProfile = VoxelForgeRedefinedDensityField::GetParameterSet();
|
||||
#endif
|
||||
int32 ResearchGamePerfValue = 0;
|
||||
FParse::Value(FCommandLine::Get(), TEXT("voxel.ResearchGamePerf="), ResearchGamePerfValue);
|
||||
bResearchGamePerfEnabled = ResearchGamePerfValue != 0;
|
||||
ResearchGamePerfLastFrameSeconds = 0.0;
|
||||
ResearchGamePerfMovingSeconds = 0.0;
|
||||
ResearchGamePerfMovingFrameTimesMs.Reset();
|
||||
VoxelForgeStartupTrace::BeginFromCommandLine();
|
||||
VoxelForgeStartupTrace::FStageScope StartupTraceStage(TEXT("BeginPlay"));
|
||||
Super::BeginPlay();
|
||||
@@ -2095,6 +2125,38 @@ void AVoxelWorld::BeginPlay()
|
||||
FParse::Value(FCommandLine::Get(), TEXT("voxel.TestCeilingCaptureDelaySeconds="),
|
||||
TestCeilingCaptureDelaySeconds);
|
||||
TestCeilingCaptureDelaySeconds = FMath::Max(TestCeilingCaptureDelaySeconds, 1.0f);
|
||||
FParse::Value(FCommandLine::Get(), TEXT("voxel.TestCeilingSecondCaptureDelaySeconds="),
|
||||
TestCeilingSecondCaptureDelaySeconds);
|
||||
FParse::Value(FCommandLine::Get(), TEXT("voxel.TestCeilingCameraMoveSpeedCmPerSecond="),
|
||||
TestCeilingCameraMoveSpeedCmPerSecond);
|
||||
FParse::Value(FCommandLine::Get(), TEXT("voxel.TestCeilingCameraMoveStartSeconds="),
|
||||
TestCeilingCameraMoveStartSeconds);
|
||||
FParse::Value(FCommandLine::Get(), TEXT("voxel.TestCeilingCameraMoveDurationSeconds="),
|
||||
TestCeilingCameraMoveDurationSeconds);
|
||||
FString CameraMoveDirectionText;
|
||||
if (FParse::Value(FCommandLine::Get(), TEXT("voxel.TestCeilingCameraMoveDirection="),
|
||||
CameraMoveDirectionText))
|
||||
{
|
||||
TArray<FString> Components;
|
||||
CameraMoveDirectionText.ParseIntoArray(Components, TEXT(","), true);
|
||||
if (Components.Num() == 3)
|
||||
{
|
||||
TestCeilingCameraMoveDirection = FVector(
|
||||
FCString::Atof(*Components[0]), FCString::Atof(*Components[1]),
|
||||
FCString::Atof(*Components[2]));
|
||||
}
|
||||
}
|
||||
TestCeilingCameraMoveDirection = TestCeilingCameraMoveDirection.GetSafeNormal();
|
||||
if (TestCeilingCameraMoveDirection.IsNearlyZero())
|
||||
{
|
||||
TestCeilingCameraMoveDirection = FVector::XAxisVector;
|
||||
}
|
||||
TestCeilingCameraMoveSpeedCmPerSecond = FMath::Max(
|
||||
TestCeilingCameraMoveSpeedCmPerSecond, 0.0f);
|
||||
TestCeilingCameraMoveStartSeconds = FMath::Max(
|
||||
TestCeilingCameraMoveStartSeconds, 0.0f);
|
||||
TestCeilingCameraMoveDurationSeconds = FMath::Max(
|
||||
TestCeilingCameraMoveDurationSeconds, 0.0f);
|
||||
FParse::Value(FCommandLine::Get(), TEXT("voxel.TestCeilingWallCloseup="),
|
||||
TestCeilingWallCloseup);
|
||||
TestCeilingViewBeginSeconds = FPlatformTime::Seconds();
|
||||
@@ -2108,6 +2170,32 @@ void AVoxelWorld::BeginPlay()
|
||||
TestCeilingCameraRotation.Yaw, TestCeilingCameraRotation.Roll,
|
||||
HeadlessStreamingTestCenterVoxel.X, HeadlessStreamingTestCenterVoxel.Y,
|
||||
HeadlessStreamingTestCenterVoxel.Z, TestCeilingCaptureDelaySeconds);
|
||||
UE_LOG(LogTemp, Display,
|
||||
TEXT("[VoxelForgeTestCeiling] second_capture_delay_s=%.1f camera_move_cm_s=%.1f "
|
||||
"move_start_s=%.1f move_duration_s=%.1f move_direction=(%.3f,%.3f,%.3f) "
|
||||
"research_game_perf=%d"),
|
||||
TestCeilingSecondCaptureDelaySeconds, TestCeilingCameraMoveSpeedCmPerSecond,
|
||||
TestCeilingCameraMoveStartSeconds, TestCeilingCameraMoveDurationSeconds,
|
||||
TestCeilingCameraMoveDirection.X, TestCeilingCameraMoveDirection.Y,
|
||||
TestCeilingCameraMoveDirection.Z, bResearchGamePerfEnabled ? 1 : 0);
|
||||
}
|
||||
|
||||
TestResearchProfileSwitchAfterSeconds = -1.0f;
|
||||
TestResearchProfileSwitchTo = -1;
|
||||
TestResearchProfileSwitchBeginSeconds = FPlatformTime::Seconds();
|
||||
bTestResearchProfileSwitchConfigured =
|
||||
FParse::Value(FCommandLine::Get(),
|
||||
TEXT("voxel.TestResearchProfileSwitchAfterSeconds="),
|
||||
TestResearchProfileSwitchAfterSeconds)
|
||||
&& FParse::Value(FCommandLine::Get(),
|
||||
TEXT("voxel.TestResearchProfileSwitchTo="), TestResearchProfileSwitchTo)
|
||||
&& TestResearchProfileSwitchAfterSeconds >= 0.0f;
|
||||
bTestResearchProfileSwitchApplied = false;
|
||||
if (bTestResearchProfileSwitchConfigured)
|
||||
{
|
||||
UE_LOG(LogTemp, Display,
|
||||
TEXT("[VoxelForgeResearchProfileSwitch] configured=1 after_s=%.3f target_profile=%d"),
|
||||
TestResearchProfileSwitchAfterSeconds, TestResearchProfileSwitchTo);
|
||||
}
|
||||
|
||||
// The owner's current DA_Settings is intentionally a one-strate surface layout, so it cannot
|
||||
@@ -2499,7 +2587,8 @@ void AVoxelWorld::ConfigureHeadlessStreamingTest()
|
||||
bHeadlessCollisionGateStressTestFailed = false;
|
||||
bHeadlessCollisionGateStressTestExitRequested = false;
|
||||
bHeadlessCollisionGateStressTestGateWasEngaged = false;
|
||||
bHeadlessCollisionGateStressTestHorizontalGateProbeApplied = false;
|
||||
bHeadlessCollisionGateStressTestFullStopProbeApplied = false;
|
||||
bHeadlessCollisionGateStressTestFullStopProbeObserved = false;
|
||||
bHeadlessCollisionGateStressTestFullStop = false;
|
||||
bHeadlessCollisionGateStressTestStartHeightCrossed = false;
|
||||
bHeadlessCollisionGateStressTestTerrainViolation = false;
|
||||
@@ -2521,6 +2610,7 @@ void AVoxelWorld::ConfigureHeadlessStreamingTest()
|
||||
HeadlessCollisionGateStressTestLandedSeconds = 0.0;
|
||||
HeadlessCollisionGateStressTestGateStartSeconds = 0.0;
|
||||
HeadlessCollisionGateStressTestFullStopStartSeconds = 0.0;
|
||||
HeadlessCollisionGateStressTestFullStopProbeStartSeconds = 0.0;
|
||||
HeadlessCollisionGateStressTestDistanceCm = 0.0;
|
||||
HeadlessCollisionGateStressTestGateDistanceCm = 0.0;
|
||||
HeadlessCollisionGateStressTestTotalGateDurationSeconds = 0.0;
|
||||
@@ -2549,8 +2639,8 @@ void AVoxelWorld::ConfigureHeadlessStreamingTest()
|
||||
bHeadlessSurfaceFallTestExitRequested = false;
|
||||
bHeadlessSurfaceFallTestPredictionProbeInjected = false;
|
||||
bHeadlessSurfaceFallTestPredictionProbeCleared = false;
|
||||
HeadlessSurfaceFallTestSpawnHeightVoxels = 20.0f;
|
||||
HeadlessSurfaceFallTestGroundToleranceVoxels = 4.0f;
|
||||
HeadlessSurfaceFallTestSpawnHeightVoxels = 5.0f * 100.0f / VOXEL_SIZE;
|
||||
HeadlessSurfaceFallTestGroundToleranceVoxels = 1.0f * 100.0f / VOXEL_SIZE;
|
||||
HeadlessSurfaceFallTestTimeoutSeconds = 8.0f;
|
||||
HeadlessSurfaceFallTestPlayerHalfHeightVoxels =
|
||||
VoxelPassageGeometry::PlayerHalfHeightVoxels;
|
||||
@@ -2715,7 +2805,7 @@ void AVoxelWorld::ConfigureHeadlessStreamingTest()
|
||||
HeadlessStrateCrossingTestStartDelaySeconds = 1.0f;
|
||||
HeadlessStrateCrossingTestTimeoutSeconds = 120.0f;
|
||||
HeadlessStrateCrossingTestMaxGateSeconds = 10.0f;
|
||||
HeadlessStrateCrossingTestRouteTargetRadiusVoxels = 2.0f;
|
||||
HeadlessStrateCrossingTestRouteTargetRadiusVoxels = 0.5f * 100.0f / VOXEL_SIZE;
|
||||
FParse::Value(CommandLine, TEXT("voxel.TestStrateCrossingSpeedCmPerSecond="),
|
||||
HeadlessStrateCrossingTestSpeedCmPerSecond);
|
||||
FParse::Value(CommandLine, TEXT("voxel.TestStrateCrossingStartDelaySeconds="),
|
||||
@@ -3434,7 +3524,7 @@ void AVoxelWorld::ObserveHeadlessStrateCrossingTest(
|
||||
const float Clearance = FeetZ - PassageFloorZ;
|
||||
HeadlessStrateCrossingTestMinFloorClearanceVoxels = FMath::Min(
|
||||
HeadlessStrateCrossingTestMinFloorClearanceVoxels, Clearance);
|
||||
constexpr float FloorViolationToleranceVoxels = 2.0f;
|
||||
constexpr float FloorViolationToleranceVoxels = 0.5f * 100.0f / VOXEL_SIZE;
|
||||
if (Clearance < -FloorViolationToleranceVoxels)
|
||||
{
|
||||
bHeadlessStrateCrossingTestFailed = true;
|
||||
@@ -3688,8 +3778,9 @@ void AVoxelWorld::AdvanceHeadlessDigMotionTest(FVector& InOutPlayerPosition, APa
|
||||
BestDirection = Index;
|
||||
}
|
||||
}
|
||||
constexpr float DigOffsetVoxels = 8.0f;
|
||||
if (Clearances[BestDirection] < DigOffsetVoxels + 3.0f)
|
||||
constexpr float DigOffsetVoxels = 2.0f * 100.0f / VOXEL_SIZE;
|
||||
constexpr float DigClearanceMarginVoxels = 0.75f * 100.0f / VOXEL_SIZE;
|
||||
if (Clearances[BestDirection] < DigOffsetVoxels + DigClearanceMarginVoxels)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -3996,10 +4087,12 @@ bool AVoxelWorld::MeasureHeadlessCollisionGateStressTerrain(
|
||||
// nearest air/solid zero crossing adjacent to the capsule's lowest point, then linearly
|
||||
// interpolate it. Searching up when the feet sample is solid reports negative clearance
|
||||
// instead of silently choosing a lower floor after a fall-through.
|
||||
// Keep the half-voxel surface search resolution, but double the number of samples after the
|
||||
// voxel size halved so the search retains its original physical reach.
|
||||
constexpr float SearchStepVoxels = 0.5f;
|
||||
const int32 MaxSearchSteps = bHeadlessCollisionGateStressDeepDrop
|
||||
&& !bHeadlessCollisionGateStressDeepDropSurfaceCached
|
||||
? 512 : 256;
|
||||
? 1024 : 512;
|
||||
float LastZ = FeetZ;
|
||||
float LastDensity = OutFeetDensity;
|
||||
const bool bSearchDown = OutFeetDensity >= 0.0f;
|
||||
@@ -4051,6 +4144,13 @@ void AVoxelWorld::AdvanceHeadlessCollisionGateStressTest(
|
||||
}
|
||||
|
||||
const double Now = FPlatformTime::Seconds();
|
||||
if (HeadlessCollisionGateStressTestBeginSeconds <= 0.0)
|
||||
{
|
||||
// Include setup failures in the bounded stress-test lifetime too. Otherwise a research
|
||||
// field with no surface at the default probe can set Failed before the start placement,
|
||||
// leave BeginSeconds at zero, and strand the commandlet game until the outer watchdog.
|
||||
HeadlessCollisionGateStressTestBeginSeconds = Now;
|
||||
}
|
||||
int32 DeepDropTopChunkZ = 0;
|
||||
int32 DeepDropBottomChunkZ = 0;
|
||||
if (bHeadlessCollisionGateStressDeepDrop)
|
||||
@@ -4085,16 +4185,19 @@ void AVoxelWorld::AdvanceHeadlessCollisionGateStressTest(
|
||||
// world only.
|
||||
constexpr float DeepDropCenterX = CHUNK_SIZE * 0.5f;
|
||||
constexpr float DeepDropCenterY = CHUNK_SIZE * 0.5f;
|
||||
constexpr float DeepDropShaftStartX = -32.0f;
|
||||
constexpr float DeepDropShaftStartX =
|
||||
VoxelPassageGeometry::MetersToVoxels(-8.0f);
|
||||
const float DeepDropShaftEndX = CHUNK_SIZE * 0.5f
|
||||
+ (HeadlessCollisionGateStressTestSpeedCmPerSecond
|
||||
* HeadlessCollisionGateStressTestDurationSeconds / VOXEL_SIZE)
|
||||
+ 64.0f;
|
||||
+ VoxelPassageGeometry::MetersToVoxels(16.0f);
|
||||
const float StrateTopExclusiveVoxels =
|
||||
static_cast<float>(DeepDropTopChunkZ + 1) * CHUNK_SIZE;
|
||||
const float ShaftTopVoxels = StrateTopExclusiveVoxels + 32.0f;
|
||||
const float ShaftTopVoxels = StrateTopExclusiveVoxels
|
||||
+ VoxelPassageGeometry::MetersToVoxels(8.0f);
|
||||
const float ShaftBottomVoxels =
|
||||
static_cast<float>(DeepDropBottomChunkZ + 1) * CHUNK_SIZE - 1.0f;
|
||||
static_cast<float>(DeepDropBottomChunkZ + 1) * CHUNK_SIZE
|
||||
- VoxelPassageGeometry::MetersToVoxels(0.25f);
|
||||
if (ShaftBottomVoxels >= ShaftTopVoxels || DiffLayer == nullptr)
|
||||
{
|
||||
bHeadlessCollisionGateStressTestFailed = true;
|
||||
@@ -4115,7 +4218,9 @@ void AVoxelWorld::AdvanceHeadlessCollisionGateStressTest(
|
||||
const FVector ShaftCenterWorld = LocalVoxelToWorld(FVector(
|
||||
ShaftCenterX, DeepDropCenterY,
|
||||
(ShaftTopVoxels + ShaftBottomVoxels) * 0.5f));
|
||||
const FVector ShaftHalfExtent(ShaftHalfExtentX, 18.0f, ShaftHalfExtentZ);
|
||||
const FVector ShaftHalfExtent(
|
||||
ShaftHalfExtentX, VoxelPassageGeometry::MetersToVoxels(4.5f),
|
||||
ShaftHalfExtentZ);
|
||||
const float FixtureBrushRadius = FMath::Max(
|
||||
FMath::Max3(ShaftHalfExtent.X, ShaftHalfExtent.Y, ShaftHalfExtent.Z),
|
||||
Settings != nullptr ? Settings->MaxBrushRadius : 0.0f);
|
||||
@@ -4255,7 +4360,7 @@ void AVoxelWorld::AdvanceHeadlessCollisionGateStressTest(
|
||||
// metre above its floor. Start above the measured density surface at this XY so the pawn
|
||||
// must fall onto a freshly requested level-0 tile before the horizontal diagnostic.
|
||||
const float LaunchLocalVoxelX = bHeadlessCollisionGateStressDeepDrop
|
||||
? CHUNK_SIZE * 0.5f : 18.2f;
|
||||
? CHUNK_SIZE * 0.5f : VoxelPassageGeometry::MetersToVoxels(4.55f);
|
||||
const float LaunchLocalVoxelY = bHeadlessCollisionGateStressDeepDrop
|
||||
? CHUNK_SIZE * 0.5f : 0.0f;
|
||||
float LaunchHeightAboveGroundVoxels =
|
||||
@@ -4314,7 +4419,8 @@ void AVoxelWorld::AdvanceHeadlessCollisionGateStressTest(
|
||||
const float StrateTopExclusiveVoxels =
|
||||
static_cast<float>(SurfaceStrateTopChunkZ + 1) * CHUNK_SIZE;
|
||||
LaunchLocalVoxelZ = StrateTopExclusiveVoxels
|
||||
- CapsuleHalfHeightVoxels - 0.5f;
|
||||
- CapsuleHalfHeightVoxels
|
||||
- VoxelPassageGeometry::MetersToVoxels(0.125f);
|
||||
LaunchHeightAboveGroundVoxels = LaunchLocalVoxelZ
|
||||
- SurfaceLocalVoxelZ - CapsuleHalfHeightVoxels;
|
||||
const FIntVector LaunchCenterTile(
|
||||
@@ -4322,7 +4428,7 @@ void AVoxelWorld::AdvanceHeadlessCollisionGateStressTest(
|
||||
FMath::FloorToInt(LaunchLocalVoxelY / CHUNK_SIZE),
|
||||
FMath::FloorToInt(LaunchLocalVoxelZ / CHUNK_SIZE));
|
||||
const int32 ClipRadius = Settings != nullptr
|
||||
? FMath::Max(1, Settings->ClipRadius) : 3;
|
||||
? FMath::Max(1, Settings->ClipRadius) : 6;
|
||||
VoxelClipmapDesiredTiles::AlignedRingBounds(
|
||||
LaunchCenterTile, ClipRadius, 2, LevelZeroRingMin, LevelZeroRingMax);
|
||||
if (LaunchHeightAboveGroundVoxels <= 0.0f
|
||||
@@ -4608,9 +4714,8 @@ void AVoxelWorld::AdvanceHeadlessCollisionGateStressTest(
|
||||
return;
|
||||
}
|
||||
|
||||
// The measured interval starts with the vertical drop. Horizontal movement begins only after
|
||||
// CharacterMovement reports a landing, so the test observes the entire fall and then checks
|
||||
// that support gating leaves horizontal movement available.
|
||||
// The measured interval starts with the vertical drop. The gate must also stop a small
|
||||
// horizontal probe while support collision is unresolved, then resume normal swept movement.
|
||||
if (!bHeadlessCollisionGateStressTestLanded)
|
||||
{
|
||||
if (bHeadlessCollisionGateStressDeepDrop
|
||||
@@ -4626,11 +4731,11 @@ void AVoxelWorld::AdvanceHeadlessCollisionGateStressTest(
|
||||
Velocity.X = 100.0f;
|
||||
CharacterMovement->Velocity = Velocity;
|
||||
CharacterMovement->UpdateComponentVelocity();
|
||||
if (!bHeadlessCollisionGateStressTestHorizontalGateProbeApplied)
|
||||
if (!bHeadlessCollisionGateStressTestFullStopProbeApplied)
|
||||
{
|
||||
bHeadlessCollisionGateStressTestHorizontalGateProbeApplied = true;
|
||||
bHeadlessCollisionGateStressTestFullStopProbeApplied = true;
|
||||
UE_LOG(LogTemp, Display,
|
||||
TEXT("[VoxelForgeCollisionGateStressTest] horizontal_gate_probe "
|
||||
TEXT("[VoxelForgeCollisionGateStressTest] full_stop_probe "
|
||||
"applied=1 velocity_x_cm_s=%.1f gate=1"), Velocity.X);
|
||||
}
|
||||
}
|
||||
@@ -4652,8 +4757,7 @@ void AVoxelWorld::AdvanceHeadlessCollisionGateStressTest(
|
||||
{
|
||||
if (UCharacterMovementComponent* CharacterMovement = Character->GetCharacterMovement())
|
||||
{
|
||||
// The baseline gate changes MOVE_Walking to MOVE_None. Do not undo that here; the
|
||||
// test must observe the resulting full stop. The fall-only gate keeps this input live.
|
||||
// Do not undo MOVE_None while the collision gate is engaged; the gate owns the hold.
|
||||
if (!bPawnCollisionGateEngaged
|
||||
&& CharacterMovement->MovementMode == MOVE_None)
|
||||
{
|
||||
@@ -4687,15 +4791,6 @@ void AVoxelWorld::ObserveHeadlessCollisionGateStressTest(
|
||||
HeadlessCollisionGateStressTestLastActualPosition = PlayerPosition;
|
||||
|
||||
const bool bGateEngaged = bPawnCollisionGateEngaged;
|
||||
if (bGateEngaged && bHeadlessCollisionGateStressTestLanded
|
||||
&& HorizontalDelta > 0.1f
|
||||
&& !bHeadlessCollisionGateStressTestHorizontalGateProbeApplied)
|
||||
{
|
||||
bHeadlessCollisionGateStressTestHorizontalGateProbeApplied = true;
|
||||
UE_LOG(LogTemp, Display,
|
||||
TEXT("[VoxelForgeCollisionGateStressTest] horizontal_gate_probe "
|
||||
"observed=1 delta_cm=%.3f gate=1"), HorizontalDelta);
|
||||
}
|
||||
if (bGateEngaged || bHeadlessCollisionGateStressTestGateWasEngaged)
|
||||
{
|
||||
HeadlessCollisionGateStressTestGateDistanceCm += HorizontalDelta;
|
||||
@@ -4723,6 +4818,33 @@ void AVoxelWorld::ObserveHeadlessCollisionGateStressTest(
|
||||
}
|
||||
bHeadlessCollisionGateStressTestGateWasEngaged = bGateEngaged;
|
||||
|
||||
// Confirm the injected horizontal velocity was blocked for a sustained interval. Merely
|
||||
// applying the probe while a gate is engaged does not prove that the pawn stayed put.
|
||||
constexpr double ProbeStopConfirmationSeconds = 0.05;
|
||||
const bool bProbeHeldStill = bHeadlessCollisionGateStressTestFullStopProbeApplied
|
||||
&& bGateEngaged && HorizontalDelta <= 0.01f;
|
||||
if (bProbeHeldStill)
|
||||
{
|
||||
if (HeadlessCollisionGateStressTestFullStopProbeStartSeconds <= 0.0)
|
||||
{
|
||||
HeadlessCollisionGateStressTestFullStopProbeStartSeconds = Now;
|
||||
}
|
||||
else if (!bHeadlessCollisionGateStressTestFullStopProbeObserved
|
||||
&& Now - HeadlessCollisionGateStressTestFullStopProbeStartSeconds
|
||||
>= ProbeStopConfirmationSeconds)
|
||||
{
|
||||
bHeadlessCollisionGateStressTestFullStopProbeObserved = true;
|
||||
UE_LOG(LogTemp, Display,
|
||||
TEXT("[VoxelForgeCollisionGateStressTest] full_stop_probe "
|
||||
"observed=1 gate=1 held_s=%.6f"),
|
||||
Now - HeadlessCollisionGateStressTestFullStopProbeStartSeconds);
|
||||
}
|
||||
}
|
||||
else if (!bHeadlessCollisionGateStressTestFullStopProbeObserved)
|
||||
{
|
||||
HeadlessCollisionGateStressTestFullStopProbeStartSeconds = 0.0;
|
||||
}
|
||||
|
||||
// The first sample after engaging any gate can have zero horizontal delta simply because the
|
||||
// world prerequisite ran before CharacterMovementComponent. Require a sustained no-progress
|
||||
// interval before calling it a full stop; a permanent stop still fails through the distance
|
||||
@@ -4986,6 +5108,37 @@ void AVoxelWorld::ObserveHeadlessCollisionGateStressTest(
|
||||
bSweptIntoSolid ? 1 : 0);
|
||||
}
|
||||
|
||||
// A research-field tile can remain queued longer than the stress window because pointwise
|
||||
// evaluation is expensive. Treat a sustained, measured full stop above that unresolved tile as
|
||||
// a safety pass; the result still reports landed=0 and the pending support interval, so this
|
||||
// cannot be mistaken for a streaming-liveness or traversal pass.
|
||||
constexpr double SafePendingHoldSeconds = 0.5;
|
||||
const double CurrentGateHoldSeconds = bGateEngaged
|
||||
&& HeadlessCollisionGateStressTestGateStartSeconds > 0.0
|
||||
? FMath::Max(0.0, Now - HeadlessCollisionGateStressTestGateStartSeconds)
|
||||
: 0.0;
|
||||
if (!bHeadlessCollisionGateStressTestPassed
|
||||
&& !bHeadlessCollisionGateStressTestFailed
|
||||
&& bHeadlessCollisionGateStressDeepDrop && bCollisionGateEnabled
|
||||
&& bHeadlessCollisionGateStressTestFullStopProbeApplied
|
||||
&& bHeadlessCollisionGateStressTestFullStopProbeObserved
|
||||
&& bGateEngaged && !bGateSupportReady && GateSupportUnavailableReason == nullptr
|
||||
&& bSurfaceFound && ClearanceCm >= -AllowedSurfacePenetrationCm
|
||||
&& !bHeadlessCollisionGateStressTestTerrainViolation
|
||||
&& HeadlessCollisionGateStressTestTerrainSampleCount > 0
|
||||
&& CurrentGateHoldSeconds >= SafePendingHoldSeconds)
|
||||
{
|
||||
bHeadlessCollisionGateStressTestPassed = true;
|
||||
HeadlessCollisionGateStressTestFailureReason = TEXT("safe_pending_support_hold");
|
||||
UE_LOG(LogTemp, Display,
|
||||
TEXT("[VoxelForgeCollisionGateStressTest] safety_hold_pass elapsed_s=%.6f "
|
||||
"hold_s=%.6f clearance_cm=%.3f support=(%d,%d,%d) "
|
||||
"support_ready=0 landed=%d"),
|
||||
Now - HeadlessCollisionGateStressTestBeginSeconds, CurrentGateHoldSeconds,
|
||||
ClearanceCm, GateSupportTile.Coord.X, GateSupportTile.Coord.Y,
|
||||
GateSupportTile.Coord.Z, bHeadlessCollisionGateStressTestLanded ? 1 : 0);
|
||||
}
|
||||
|
||||
++HeadlessCollisionGateStressTestTraceFrame;
|
||||
UE_LOG(LogTemp, Display,
|
||||
TEXT("[VoxelForgeCollisionGateStressTest] trace frame=%d elapsed_s=%.6f "
|
||||
@@ -5187,7 +5340,7 @@ void AVoxelWorld::AdvanceHeadlessSurfaceFallTest(
|
||||
if (bProbeSupportReady
|
||||
&& bProbeSupportInLayout
|
||||
&& LocalPosition.Z - HeadlessSurfaceFallTestPlayerHalfHeightVoxels
|
||||
> HeadlessSurfaceFallTestTerrainZ + 2.0f)
|
||||
> HeadlessSurfaceFallTestTerrainZ + 0.5f * 100.0f / VOXEL_SIZE)
|
||||
{
|
||||
bHeadlessSurfaceFallTestPredictionProbeInjected = true;
|
||||
HeadlessSurfaceFallTestPredictionProbePosition = InOutPlayerPosition;
|
||||
@@ -5393,13 +5546,13 @@ void AVoxelWorld::MaybeFinishHeadlessCollisionGateStressTest()
|
||||
if (bHeadlessCollisionGateStressTestPassed
|
||||
&& !bHeadlessCollisionGateStressTestFailed
|
||||
&& bHeadlessCollisionGateStressDeepDrop && bCollisionGateEnabled
|
||||
&& (!bHeadlessCollisionGateStressTestHorizontalGateProbeApplied
|
||||
|| HeadlessCollisionGateStressTestGateDistanceCm < 1.0))
|
||||
&& (!bHeadlessCollisionGateStressTestFullStopProbeApplied
|
||||
|| !bHeadlessCollisionGateStressTestFullStopProbeObserved))
|
||||
{
|
||||
bHeadlessCollisionGateStressTestPassed = false;
|
||||
bHeadlessCollisionGateStressTestFailed = true;
|
||||
HeadlessCollisionGateStressTestFailureReason =
|
||||
TEXT("gate_horizontal_movement_blocked");
|
||||
TEXT("gate_full_stop_probe_not_observed");
|
||||
}
|
||||
|
||||
if (!bHeadlessCollisionGateStressTestPassed && !bHeadlessCollisionGateStressTestFailed)
|
||||
@@ -5503,7 +5656,7 @@ void AVoxelWorld::MaybeFinishHeadlessCollisionGateStressTest()
|
||||
TEXT("[VoxelForgeCollisionGateStressTest] result=%s reason=%s elapsed_s=%.6f "
|
||||
"distance_m=%.6f expected_distance_m=%.6f speed_cm_s=%.1f spawn_height_vox=%.2f deep_drop=%d "
|
||||
"gate_holds=%d gate_total_s=%.6f max_gate_s=%.6f gate_progress_m=%.6f "
|
||||
"horizontal_gate_probe=%d "
|
||||
"full_stop_probe_applied=%d full_stop_probe_observed=%d "
|
||||
"full_stops=%d full_stop_total_s=%.6f collision_gate=%d "
|
||||
"support_tile=(%d,%d,%d) support_first_check_to_submit_s=%.6f "
|
||||
"support_first_check_to_collision_ready_s=%.6f "
|
||||
@@ -5515,7 +5668,8 @@ void AVoxelWorld::MaybeFinishHeadlessCollisionGateStressTest()
|
||||
"min_clearance_position=(%.2f,%.2f,%.2f) min_feet_density=%.6f "
|
||||
"min_surface_vox_z=%.4f final_position=(%.1f,%.1f,%.1f)"),
|
||||
bPassed ? TEXT("PASS") : TEXT("FAIL"),
|
||||
bPassed ? TEXT("capsule_never_below_density_surface")
|
||||
bPassed && HeadlessCollisionGateStressTestFailureReason.IsEmpty()
|
||||
? TEXT("capsule_never_below_density_surface")
|
||||
: *HeadlessCollisionGateStressTestFailureReason,
|
||||
Now - HeadlessCollisionGateStressTestBeginSeconds,
|
||||
HeadlessCollisionGateStressTestDistanceCm / 100.0,
|
||||
@@ -5527,7 +5681,8 @@ void AVoxelWorld::MaybeFinishHeadlessCollisionGateStressTest()
|
||||
HeadlessCollisionGateStressTestTotalGateDurationSeconds,
|
||||
HeadlessCollisionGateStressTestMaxGateDurationSeconds,
|
||||
HeadlessCollisionGateStressTestGateDistanceCm / 100.0,
|
||||
bHeadlessCollisionGateStressTestHorizontalGateProbeApplied ? 1 : 0,
|
||||
bHeadlessCollisionGateStressTestFullStopProbeApplied ? 1 : 0,
|
||||
bHeadlessCollisionGateStressTestFullStopProbeObserved ? 1 : 0,
|
||||
HeadlessCollisionGateStressTestFullStopCount,
|
||||
HeadlessCollisionGateStressTestFullStopDurationSeconds,
|
||||
bCollisionGateEnabled ? 1 : 0,
|
||||
@@ -6339,6 +6494,38 @@ void AVoxelWorld::Tick(float DeltaTime)
|
||||
{
|
||||
Super::Tick(DeltaTime);
|
||||
TRACE_CPUPROFILER_EVENT_SCOPE(VoxelForge_Tick); // game-thread streaming orchestration breakdown
|
||||
#if WITH_EDITOR
|
||||
if (bTestResearchProfileSwitchConfigured
|
||||
&& !bTestResearchProfileSwitchApplied
|
||||
&& FPlatformTime::Seconds() - TestResearchProfileSwitchBeginSeconds
|
||||
>= static_cast<double>(TestResearchProfileSwitchAfterSeconds))
|
||||
{
|
||||
const int32 PreviousProfile = VoxelForgeRedefinedDensityField::GetParameterSet();
|
||||
const int32 TargetProfile = FMath::Clamp(
|
||||
TestResearchProfileSwitchTo, 0,
|
||||
VoxelForgeRedefinedDensityField::GetParameterSetCount() - 1);
|
||||
VoxelForgeRedefinedDensityField::SetParameterSet(TargetProfile);
|
||||
bTestResearchProfileSwitchApplied = true;
|
||||
UE_LOG(LogTemp, Display,
|
||||
TEXT("[VoxelForgeResearchProfileSwitch] applied=1 profile=%d->%d loaded_tiles=%d desired_epoch=%u"),
|
||||
PreviousProfile, TargetProfile, LoadedTiles.Num(), DesiredEpoch);
|
||||
}
|
||||
const int32 ResearchFieldEnabled =
|
||||
VoxelForgeRedefinedDensityField::IsParameterizedEnabled() ? 1 : 0;
|
||||
const int32 ResearchFieldProfile = VoxelForgeRedefinedDensityField::GetParameterSet();
|
||||
if (AppliedResearchFieldEnabled != INDEX_NONE
|
||||
&& (AppliedResearchFieldEnabled != ResearchFieldEnabled
|
||||
|| AppliedResearchFieldProfile != ResearchFieldProfile))
|
||||
{
|
||||
UE_LOG(LogTemp, Display,
|
||||
TEXT("[VoxelWorld] research_field_selection_changed enabled=%d->%d profile=%d->%d; rebuilding loaded world"),
|
||||
AppliedResearchFieldEnabled, ResearchFieldEnabled,
|
||||
AppliedResearchFieldProfile, ResearchFieldProfile);
|
||||
AppliedResearchFieldEnabled = ResearchFieldEnabled;
|
||||
AppliedResearchFieldProfile = ResearchFieldProfile;
|
||||
RegenerateAllChunks();
|
||||
}
|
||||
#endif
|
||||
if (DensityVolume && (!Settings || !Settings->bEnableDensityVolume))
|
||||
{
|
||||
DensityVolume->NotifyShutdown();
|
||||
@@ -6709,16 +6896,16 @@ void AVoxelWorld::Tick(float DeltaTime)
|
||||
Camera->AddInstanceComponent(CaptureHeadlamp);
|
||||
CaptureHeadlamp->SetupAttachment(Camera->GetRootComponent());
|
||||
CaptureHeadlamp->SetMobility(EComponentMobility::Movable);
|
||||
CaptureHeadlamp->SetIntensity(80000.0f);
|
||||
CaptureHeadlamp->SetAttenuationRadius(10000.0f);
|
||||
CaptureHeadlamp->SetUseInverseSquaredFalloff(false);
|
||||
CaptureHeadlamp->SetLightFalloffExponent(0.0f);
|
||||
CaptureHeadlamp->SetIntensity(6500.0f);
|
||||
CaptureHeadlamp->SetAttenuationRadius(1600.0f);
|
||||
CaptureHeadlamp->SetUseInverseSquaredFalloff(true);
|
||||
CaptureHeadlamp->SetSourceRadius(8.0f);
|
||||
CaptureHeadlamp->SetLightColor(FLinearColor(1.0f, 0.88f, 0.72f));
|
||||
CaptureHeadlamp->SetCastShadows(false);
|
||||
CaptureHeadlamp->SetCastShadows(true);
|
||||
CaptureHeadlamp->RegisterComponent();
|
||||
UE_LOG(LogTemp, Display,
|
||||
TEXT("[VoxelForgeTestCeiling] camera_headlamp=1 intensity=80000 radius_cm=10000 "
|
||||
"inverse_square=0 falloff_exponent=0"));
|
||||
TEXT("[VoxelForgeTestCeiling] camera_headlamp=1 intensity_lm=6500 radius_cm=1600 "
|
||||
"inverse_square=1 source_radius_cm=8 shadows=1"));
|
||||
}
|
||||
UE_LOG(LogTemp, Display,
|
||||
TEXT("[VoxelForgeTestCeiling] camera_installed=1 applied_visible_tiles=%llu"),
|
||||
@@ -6726,12 +6913,64 @@ void AVoxelWorld::Tick(float DeltaTime)
|
||||
}
|
||||
}
|
||||
|
||||
if (TestCeilingCamera.IsValid() && !bTestCeilingCaptureRequested
|
||||
&& AppliedVisibleTileCount >= 16
|
||||
&& FPlatformTime::Seconds() - TestCeilingViewBeginSeconds
|
||||
>= static_cast<double>(TestCeilingCaptureDelaySeconds))
|
||||
const double CameraNow = FPlatformTime::Seconds();
|
||||
const double CameraElapsedSeconds = CameraNow - TestCeilingViewBeginSeconds;
|
||||
const bool bCameraMoveWindow = TestCeilingCameraMoveSpeedCmPerSecond > 0.0f
|
||||
&& CameraElapsedSeconds >= TestCeilingCameraMoveStartSeconds
|
||||
&& (TestCeilingCameraMoveDurationSeconds <= 0.0f
|
||||
|| CameraElapsedSeconds < TestCeilingCameraMoveStartSeconds
|
||||
+ TestCeilingCameraMoveDurationSeconds);
|
||||
if (TestCeilingCamera.IsValid() && TestCeilingCameraMoveSpeedCmPerSecond > 0.0f)
|
||||
{
|
||||
bTestCeilingCaptureRequested = true;
|
||||
if (bCameraMoveWindow)
|
||||
{
|
||||
if (TestCeilingCameraLastMoveSeconds > 0.0)
|
||||
{
|
||||
const double MoveDeltaSeconds = FMath::Max(
|
||||
0.0, CameraNow - TestCeilingCameraLastMoveSeconds);
|
||||
TestCeilingCameraWorldPosition += TestCeilingCameraMoveDirection
|
||||
* TestCeilingCameraMoveSpeedCmPerSecond
|
||||
* static_cast<float>(MoveDeltaSeconds);
|
||||
if (ACameraActor* Camera = TestCeilingCamera.Get())
|
||||
{
|
||||
Camera->SetActorLocation(TestCeilingCameraWorldPosition);
|
||||
}
|
||||
HeadlessStreamingTestCenterVoxel = WorldToLocalVoxel(
|
||||
TestCeilingCameraWorldPosition);
|
||||
bHeadlessStreamingTestCenterOverride = true;
|
||||
if (bResearchGamePerfEnabled && IsParameterizedResearchFieldActive())
|
||||
{
|
||||
ResearchGamePerfMovingFrameTimesMs.Add(MoveDeltaSeconds * 1000.0);
|
||||
ResearchGamePerfMovingSeconds += MoveDeltaSeconds;
|
||||
}
|
||||
}
|
||||
TestCeilingCameraLastMoveSeconds = CameraNow;
|
||||
}
|
||||
else
|
||||
{
|
||||
TestCeilingCameraLastMoveSeconds = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
const bool bFirstCaptureDue = !bTestCeilingCaptureRequested
|
||||
&& CameraElapsedSeconds >= static_cast<double>(TestCeilingCaptureDelaySeconds);
|
||||
const bool bSecondCaptureDue = bTestCeilingCaptureRequested
|
||||
&& !bTestCeilingSecondCaptureRequested
|
||||
&& TestCeilingSecondCaptureDelaySeconds > 0.0f
|
||||
&& CameraElapsedSeconds >= static_cast<double>(TestCeilingSecondCaptureDelaySeconds)
|
||||
&& (!bTestResearchProfileSwitchConfigured || bTestResearchProfileSwitchApplied);
|
||||
if (TestCeilingCamera.IsValid() && AppliedVisibleTileCount >= 16
|
||||
&& (bFirstCaptureDue || bSecondCaptureDue))
|
||||
{
|
||||
const int32 CaptureIndex = bFirstCaptureDue ? 1 : 2;
|
||||
if (CaptureIndex == 1)
|
||||
{
|
||||
bTestCeilingCaptureRequested = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
bTestCeilingSecondCaptureRequested = true;
|
||||
}
|
||||
FVector ActiveViewWorldPosition = FVector::ZeroVector;
|
||||
FRotator ActiveViewRotation = FRotator::ZeroRotator;
|
||||
PC->GetPlayerViewPoint(ActiveViewWorldPosition, ActiveViewRotation);
|
||||
@@ -6761,10 +7000,11 @@ void AVoxelWorld::Tick(float DeltaTime)
|
||||
&& GEngine->GameViewport->GetGameViewport()
|
||||
&& GEngine->GameViewport->GetGameViewport()->TakeHighResScreenShot();
|
||||
UE_LOG(LogTemp, Display,
|
||||
TEXT("[VoxelForgeTestCeiling] capture_requested=%d elapsed_s=%.3f "
|
||||
TEXT("[VoxelForgeTestCeiling] capture_index=%d capture_requested=%d elapsed_s=%.3f "
|
||||
"visible_tiles=%llu triangles=%llu"),
|
||||
CaptureIndex,
|
||||
bCaptureAccepted ? 1 : 0,
|
||||
FPlatformTime::Seconds() - TestCeilingViewBeginSeconds,
|
||||
CameraElapsedSeconds,
|
||||
static_cast<unsigned long long>(AppliedVisibleTileCount),
|
||||
static_cast<unsigned long long>(AppliedTriangleCount));
|
||||
}
|
||||
@@ -7174,7 +7414,7 @@ void AVoxelWorld::RequestCollisionGateSupportTile(const FVoxelTileKey& SupportTi
|
||||
FIntVector RingMin = FIntVector::ZeroValue;
|
||||
FIntVector RingMax = FIntVector::ZeroValue;
|
||||
VoxelClipmapDesiredTiles::AlignedRingBounds(
|
||||
CurrentCenterChunk, Settings ? FMath::Max(1, Settings->ClipRadius) : 3,
|
||||
CurrentCenterChunk, Settings ? FMath::Max(1, Settings->ClipRadius) : 6,
|
||||
2, RingMin, RingMax);
|
||||
UE_LOG(LogTemp, Display,
|
||||
TEXT("[VoxelForgeCollisionGateStressTest] gate_context target=(%d,%d,%d) "
|
||||
@@ -7467,7 +7707,7 @@ void AVoxelWorld::UpdatePawnCollisionGate(APawn* Pawn, const FVector& PlayerPosi
|
||||
{
|
||||
UE_LOG(LogTemp, Display,
|
||||
TEXT("[VoxelForgeCollisionGate] waiting pawn=%s current_support=(%d,%d,%d) ready=%d "
|
||||
"hold_fall=1"),
|
||||
"hold_movement=1"),
|
||||
*Pawn->GetName(),
|
||||
CurrentSupportTile.Coord.X, CurrentSupportTile.Coord.Y, CurrentSupportTile.Coord.Z,
|
||||
bCurrentSupportReady ? 1 : 0);
|
||||
@@ -7483,37 +7723,25 @@ void AVoxelWorld::EngagePawnCollisionGate(APawn* Pawn)
|
||||
}
|
||||
if (bPawnCollisionGateEngaged)
|
||||
{
|
||||
// Re-apply the vertical hold every game-thread sample. Movement input and horizontal
|
||||
// velocity are intentionally untouched.
|
||||
// Keep the whole pawn stopped until the support tile has collision. Saving/restoring the
|
||||
// pre-gate velocity lets normal movement resume once the tile is ready.
|
||||
if (UCharacterMovementComponent* CharacterMovement =
|
||||
CollisionGatedCharacterMovement.Get())
|
||||
{
|
||||
if (CharacterMovement->MovementMode != MOVE_Flying)
|
||||
if (CharacterMovement->MovementMode != MOVE_None)
|
||||
{
|
||||
CharacterMovement->SetMovementMode(MOVE_Flying);
|
||||
CharacterMovement->SetMovementMode(MOVE_None);
|
||||
}
|
||||
CharacterMovement->GravityScale = 0.0f;
|
||||
FVector Velocity = CharacterMovement->Velocity;
|
||||
if (Velocity.Z < 0.0f)
|
||||
{
|
||||
SavedGateVerticalVelocity = Velocity.Z;
|
||||
bSavedGateVerticalVelocity = true;
|
||||
Velocity.Z = 0.0f;
|
||||
CharacterMovement->Velocity = Velocity;
|
||||
CharacterMovement->UpdateComponentVelocity();
|
||||
}
|
||||
CharacterMovement->StopMovementImmediately();
|
||||
}
|
||||
else if (UPawnMovementComponent* PawnMovement = CollisionGatedPawnMovement.Get())
|
||||
{
|
||||
FVector Velocity = PawnMovement->Velocity;
|
||||
if (Velocity.Z < 0.0f)
|
||||
if (bSavedPawnMovementWasActive && PawnMovement->IsActive())
|
||||
{
|
||||
SavedGateVerticalVelocity = Velocity.Z;
|
||||
bSavedGateVerticalVelocity = true;
|
||||
Velocity.Z = 0.0f;
|
||||
PawnMovement->Velocity = Velocity;
|
||||
PawnMovement->UpdateComponentVelocity();
|
||||
PawnMovement->Deactivate();
|
||||
}
|
||||
PawnMovement->StopMovementImmediately();
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -7531,16 +7759,11 @@ void AVoxelWorld::EngagePawnCollisionGate(APawn* Pawn)
|
||||
SavedCharacterCustomMovementMode = CharacterMovement->CustomMovementMode;
|
||||
bSavedCharacterMovementMode = true;
|
||||
|
||||
FVector Velocity = CharacterMovement->Velocity;
|
||||
if (Velocity.Z < 0.0f)
|
||||
{
|
||||
SavedGateVerticalVelocity = Velocity.Z;
|
||||
bSavedGateVerticalVelocity = true;
|
||||
Velocity.Z = 0.0f;
|
||||
CharacterMovement->Velocity = Velocity;
|
||||
}
|
||||
CharacterMovement->SetMovementMode(MOVE_Flying);
|
||||
SavedGateVelocity = CharacterMovement->Velocity;
|
||||
bSavedGateVelocity = true;
|
||||
CharacterMovement->SetMovementMode(MOVE_None);
|
||||
CharacterMovement->GravityScale = 0.0f;
|
||||
CharacterMovement->StopMovementImmediately();
|
||||
CharacterMovement->UpdateComponentVelocity();
|
||||
bPawnCollisionGateEngaged = true;
|
||||
PawnGateEngagedAtSeconds = FPlatformTime::Seconds();
|
||||
@@ -7551,15 +7774,15 @@ void AVoxelWorld::EngagePawnCollisionGate(APawn* Pawn)
|
||||
if (UPawnMovementComponent* PawnMovement = Pawn->GetMovementComponent())
|
||||
{
|
||||
CollisionGatedPawnMovement = PawnMovement;
|
||||
FVector Velocity = PawnMovement->Velocity;
|
||||
if (Velocity.Z < 0.0f)
|
||||
SavedGateVelocity = PawnMovement->Velocity;
|
||||
bSavedGateVelocity = true;
|
||||
bSavedPawnMovementWasActive = PawnMovement->IsActive();
|
||||
if (bSavedPawnMovementWasActive)
|
||||
{
|
||||
SavedGateVerticalVelocity = Velocity.Z;
|
||||
bSavedGateVerticalVelocity = true;
|
||||
Velocity.Z = 0.0f;
|
||||
PawnMovement->Velocity = Velocity;
|
||||
PawnMovement->UpdateComponentVelocity();
|
||||
PawnMovement->Deactivate();
|
||||
}
|
||||
PawnMovement->StopMovementImmediately();
|
||||
PawnMovement->UpdateComponentVelocity();
|
||||
bPawnCollisionGateEngaged = true;
|
||||
PawnGateEngagedAtSeconds = FPlatformTime::Seconds();
|
||||
return;
|
||||
@@ -7584,8 +7807,9 @@ void AVoxelWorld::ReleasePawnCollisionGate()
|
||||
CollisionGatedCharacterMovement.Reset();
|
||||
bSavedCharacterGravityScale = false;
|
||||
bSavedCharacterMovementMode = false;
|
||||
bSavedGateVerticalVelocity = false;
|
||||
SavedGateVerticalVelocity = 0.0f;
|
||||
bSavedGateVelocity = false;
|
||||
SavedGateVelocity = FVector::ZeroVector;
|
||||
bSavedPawnMovementWasActive = false;
|
||||
bPawnGateSupportRequestReported = false;
|
||||
PawnGateEngagedAtSeconds = 0.0;
|
||||
return;
|
||||
@@ -7603,17 +7827,21 @@ void AVoxelWorld::ReleasePawnCollisionGate()
|
||||
{
|
||||
CharacterMovement->GravityScale = SavedCharacterGravityScale;
|
||||
}
|
||||
if (bSavedGateVerticalVelocity && CharacterMovement->Velocity.Z <= KINDA_SMALL_NUMBER)
|
||||
if (bSavedGateVelocity)
|
||||
{
|
||||
CharacterMovement->Velocity.Z = SavedGateVerticalVelocity;
|
||||
CharacterMovement->Velocity = SavedGateVelocity;
|
||||
}
|
||||
CharacterMovement->UpdateComponentVelocity();
|
||||
}
|
||||
else if (UPawnMovementComponent* PawnMovement = CollisionGatedPawnMovement.Get())
|
||||
{
|
||||
if (bSavedGateVerticalVelocity && PawnMovement->Velocity.Z <= KINDA_SMALL_NUMBER)
|
||||
if (bSavedPawnMovementWasActive && !PawnMovement->IsActive())
|
||||
{
|
||||
PawnMovement->Velocity.Z = SavedGateVerticalVelocity;
|
||||
PawnMovement->Activate();
|
||||
}
|
||||
if (bSavedGateVelocity)
|
||||
{
|
||||
PawnMovement->Velocity = SavedGateVelocity;
|
||||
}
|
||||
PawnMovement->UpdateComponentVelocity();
|
||||
}
|
||||
@@ -7632,8 +7860,9 @@ void AVoxelWorld::ReleasePawnCollisionGate()
|
||||
CollisionGatedCharacterMovement.Reset();
|
||||
bSavedCharacterGravityScale = false;
|
||||
bSavedCharacterMovementMode = false;
|
||||
bSavedGateVerticalVelocity = false;
|
||||
SavedGateVerticalVelocity = 0.0f;
|
||||
bSavedGateVelocity = false;
|
||||
SavedGateVelocity = FVector::ZeroVector;
|
||||
bSavedPawnMovementWasActive = false;
|
||||
bPawnCollisionGateEngaged = false;
|
||||
PawnGateEngagedAtSeconds = 0.0;
|
||||
bPawnGateSupportRequestReported = false;
|
||||
@@ -9171,7 +9400,7 @@ void AVoxelWorld::BuildDesiredTiles(const FIntVector& Center, const FVector& Pla
|
||||
DesiredEpochStartCycles = FPlatformTime::Cycles64();
|
||||
DesiredEpochCenterChunk = Center;
|
||||
|
||||
const int32 R = Settings ? FMath::Max(1, Settings->ClipRadius) : 3;
|
||||
const int32 R = Settings ? FMath::Max(1, Settings->ClipRadius) : 6;
|
||||
const int32 MaxLevel = Settings ? FMath::Clamp(Settings->MaxClipLevel, 0, 8) : 4;
|
||||
|
||||
// Render-distance tiles expand horizontally.
|
||||
@@ -9188,9 +9417,9 @@ void AVoxelWorld::BuildDesiredTiles(const FIntVector& Center, const FVector& Pla
|
||||
int32 ZLo = MIN_int32, ZHi = MAX_int32;
|
||||
VoxelClipmapDesiredTiles::ResolveVerticalBounds(
|
||||
Center.Z,
|
||||
Settings ? Settings->ViewDistanceUp : 5,
|
||||
Settings ? Settings->ViewDistanceDown : 5,
|
||||
Settings ? Settings->StrateViewMarginChunks : 3,
|
||||
Settings ? Settings->ViewDistanceUp : 10,
|
||||
Settings ? Settings->ViewDistanceDown : 10,
|
||||
Settings ? Settings->StrateViewMarginChunks : 6,
|
||||
Settings && Settings->bClampViewToStrate,
|
||||
bHasStrate, StrateTopZ, StrateBottomZ, ZLo, ZHi);
|
||||
|
||||
@@ -9205,7 +9434,7 @@ void AVoxelWorld::BuildDesiredTiles(const FIntVector& Center, const FVector& Pla
|
||||
}
|
||||
|
||||
const int32 PawnCollisionRadius = FMath::Clamp(
|
||||
Settings ? Settings->PawnCollisionRadiusChunks : 1, 1, 4);
|
||||
Settings ? Settings->PawnCollisionRadiusChunks : 2, 1, 4);
|
||||
VoxelCollisionDesiredTiles::AddBox(
|
||||
CollisionDesiredTiles, Center, PawnCollisionRadius,
|
||||
PawnCollisionRadius, PawnCollisionRadius);
|
||||
@@ -9607,7 +9836,7 @@ bool AVoxelWorld::IsTileInClipRange(const FVoxelTileKey& Tile, const FIntVector&
|
||||
// render-distance level+radius, same as BuildDesiredTiles). A loaded-but-not-desired tile can
|
||||
// still cover a desired surface outside this shell, so the caller checks replacement readiness
|
||||
// before hiding it in either case.
|
||||
const int32 R = Settings ? FMath::Max(1, Settings->ClipRadius) : 3;
|
||||
const int32 R = Settings ? FMath::Max(1, Settings->ClipRadius) : 6;
|
||||
const int32 MaxLevelBase = Settings ? FMath::Clamp(Settings->MaxClipLevel, 0, 8) : 4;
|
||||
int32 MaxLevel = MaxLevelBase, ROuter = R;
|
||||
VF_OuterShell(Settings, R, MaxLevelBase, MaxLevel, ROuter);
|
||||
@@ -10157,7 +10386,7 @@ void AVoxelWorld::LoadTile(const FVoxelTileKey& Tile, EVoxelTileWorkPriority Wor
|
||||
const FIntVector CohortCenter = WorldToChunkCoord(
|
||||
WorldToLocalCm(PlayerPosition));
|
||||
const int32 PawnRadius = FMath::Clamp(
|
||||
Settings ? Settings->PawnCollisionRadiusChunks : 1, 1, 4);
|
||||
Settings ? Settings->PawnCollisionRadiusChunks : 2, 1, 4);
|
||||
VoxelCollisionDesiredTiles::AddBox(
|
||||
Step1SupportCohortTiles, CohortCenter, PawnRadius,
|
||||
PawnRadius, PawnRadius);
|
||||
|
||||
Reference in new issue
Block a user