fix(streaming): no false gate on digs, no see-through at tile handoffs; density volume off

- Dig near the pawn: a same-tile collision recook withdrew readiness although the old body was
  still installed, so the gate held a standing pawn (957 of 1,931 frames over 8 near digs).
  A tile now stays ready during a recook while collision is still wanted and its existing body is
  valid. After: 0 gate frames, 0 falling frames over 2,604 frames. Dig directly under the pawn
  still falls (~127 cm, max ~502 cm/s). The reported "accelerating toward the ground" was not
  reproduced; the false hold is the measured part.
- See-through: the retire logic only waited for replacements that overlapped the old tile in
  volume and missed desired neighbours touching at a face/edge/corner. Handoff now includes
  contact. New surface-witness gap audit (24 surface points per tile, skirts excluded, catches an
  injected early hide). 6000 cm/s walk, 75 s: 3 gaps / 51 frames / longest 603 ms -> 0.
  Dig session: 0 -> 0. Overlap audit and edit ghost stay at 0.
- bEnableDensityVolume defaults to false; when off: no volume, no fill thread, uploads or tick
  work, no volume-dependent proof bypass; terrain MIDs get VolP5.z = 0 (enable off), other VolP*
  black, VolTex* null, so M_Test1's volume nodes return their neutral result.

Gate: deep gate-on passes (79.9 m, 3 holds, -0.101 cm), gate-off penetrates (-5.1 cm).

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-09-29 04:53:57 +02:00
1 parent 0bcd67a016
commit ce6812dbeb
7 files changed
+1111 -76

No files matched your search

@@ -1065,6 +1065,7 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
// Émis en DOUBLE FACE (deux orientations) pour s'afficher quel que soit le côté caméra / le
// matériau. Une arête de surface posée sur une face de frontière a sa coordonnée d'axe EXACTE
// (l'interpolation MC garde fixe l'axe de la face) → comparaison flottante exacte fiable.
MeshData.NumSurfaceVertices = MeshData.Vertices.Num();
if (bGenerateSkirts && (GroundTris.Num() + CapTris.Num()) > 0)
{
const float ExtentCm = (float)(CellsPerAxis * Step) * VOXEL_SIZE;
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -305,7 +305,7 @@ public:
// Master switch. OFF = no volume built, no fill tasks, no GPU cost (terrain unlit by orbs).
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Lighting")
bool bEnableDensityVolume = true;
bool bEnableDensityVolume = false;
// Per-axis resolution of EACH clip level (cells). Memory per level ≈ Res³ bytes (R8). 128 →
// ~2 MB/level; 192 → ~7 MB; 256 → ~16 MB. Higher = crisper near shadows + bigger startup fill.
+4
View File
@@ -370,6 +370,9 @@ struct FVoxelMeshData
TArray<FVector> Normals; // Normale lissée (gradient de densité)
TArray<FColor> Colors; // Masques matériau F6 (R=palette biome dominant, G=pente,
// B=poids de fondu de bordure, A=palette biome voisin)
// Number of vertices on the actual density isosurface. LOD seam skirts are appended after
// this prefix and must not be used as witnesses for terrain visibility audits.
int32 NumSurfaceVertices = 0;
// F17 — classe de surface par TRIANGLE, portée par l'ORDRE du buffer d'indices : les
// NumCeilingTriangles DERNIERS triangles de Triangles sont la classe "plafond sky-cap"
@@ -384,6 +387,7 @@ struct FVoxelMeshData
UVs.Empty();
Normals.Empty();
Colors.Empty();
NumSurfaceVertices = 0;
NumCeilingTriangles = 0;
}
+14 -6
View File
@@ -152,6 +152,10 @@ struct FChunkResult
// Exact pre-edit level-0 lattice for the bounded local-player dig cache. Sampled on the worker
// with the tile's normal generator context and moved through the result queue.
FVoxelSharedDensityGrid PreEditDensityGrid;
// Sparse points on the prior surface, used only by the explicit rendered-coverage audit.
// Captured from the mesher output and kept out of normal streaming results.
bool bCaptureSurfaceWitnesses = false;
TArray<FVector> SurfaceWitnesses;
};
/** One worker-produced mesh identity emitted only by the explicit parity harness. */
@@ -223,17 +227,16 @@ public:
UPROPERTY()
UVoxelAtmosphereManager* AtmosphereManager;
/** Player-centred density CLIPMAP streamed onto the GPU for the mini-sun raymarched shadow
* system (forward rendering). Created in BeginPlay when Settings->bEnableDensityVolume is on.
* Filled on worker threads (re-evaluating GetDensityAt), recentred toroidally as the player
* moves, refilled locally on carve. See UVoxelDensityVolume. */
/** Optional player-centred density CLIPMAP for mini-sun raymarched shadows. It is absent when
* Settings->bEnableDensityVolume is false, so no fill thread or texture uploads run. */
UPROPERTY()
UVoxelDensityVolume* DensityVolume;
/** Shared Material Instance Dynamics that bind the density-volume textures + per-frame shadow params
* (clipmap transform + nearest orb) onto the terrain material(s). Keyed by BASE material so every
* tile of a given base shares ONE MID (no batching cost). Created lazily in ApplyMeshToTile,
* refreshed each Tick by UpdateTerrainMaterialParams. */
* tile of a given base shares ONE MID (no batching cost). Created lazily in ApplyMeshToTile;
* refreshed each Tick only while DensityVolume exists. When disabled, each MID receives neutral
* VolP* values and null VolTex* bindings at creation. */
UPROPERTY()
TMap<TObjectPtr<UMaterialInterface>, TObjectPtr<UMaterialInstanceDynamic>> TerrainMIDs;
@@ -541,10 +544,15 @@ private:
/** Apply the current TVP0..4 + level-0 volume texture to one MID (also used on MID creation). */
void SetVolumeParamsOnMID(UMaterialInstanceDynamic* MID) const;
/** Put all terrain MIDs into the material's neutral, volume-disabled state. */
void SetDensityVolumeMaterialParamsDisabled();
/** Explicitly opt-in headless streaming validation. This is a command-line-only harness path;
* normal gameplay never moves the pawn here. It supplies a repeatable moving session and lets
* -nullrhi request a graceful EndPlay so the clean latency summary is flushed. */
void ConfigureHeadlessStreamingTest();
void AdvanceHeadlessDigMotionTest(FVector& InOutPlayerPosition, APawn* Pawn);
void RecordHeadlessDigMotionFrame(APawn* Pawn, const FVector& PlayerPosition, float DeltaTime);
void AdvanceHeadlessStreamingTest(FVector& InOutPlayerPosition,
FVector& InOutPlayerHeading, APawn* PlayerPawn);
void AdvanceHeadlessCollisionGateStressTest(FVector& InOutPlayerPosition,