test(visual): a shadow-gap measurement, and the spawn height cleared

Tools/shadow_gap.py measures the vertical pixel gap between an object's lowest pixels and the
top of its shadow, as a fraction of the object's height, and calls it contact or floating. It
classifies a shadow by per-channel scaling - a shadow darkens every channel by the same
proportion, an object does not - which separates the two where an absolute delta cannot. Four
unit tests cover contact, floating, an empty box and the border median.

On the real spawn view it returns 'unknown': the crop contains two surfaces (pale wall behind,
dark road below) and one global background cannot classify both. Recorded as the next step, not
presented as working.

The spawn height is measured and cleared: tracing down from 30 m, the topmost surface under the
player is Z 366 and the capsule's feet are at 369 - 3 cm, the capsule skin. If the character
still reads as floating, the gap is between the rendered pavement and its collision surface, not
the spawn Z, and that is what to measure next.
This commit is contained in:
zwitschi committed 2026-11-09 17:07:23 +01:00
1 parent dde451fd69
commit f1d6283384
5 files changed
+230 -1

No files matched your search

@@ -382,6 +382,23 @@ void AGigLineCharacter::LogFootProbes()
static const ECollisionChannel Channels[] = {ECC_Visibility, ECC_WorldStatic, ECC_Pawn};
const float CapsuleHalf = GetCapsuleComponent()
? GetCapsuleComponent()->GetScaledCapsuleHalfHeight() : 0.f;
// Trace from well ABOVE the head down: the first hit is the topmost surface, i.e. the
// pavement the player can see. Tracing up from the feet finds whatever is below them,
// which is how "ground 366" got reported while the pavement on screen was higher.
if (UWorld* WorldTop = GetWorld())
{
FHitResult Top;
FCollisionQueryParams QTop(TEXT("GigLineTop"), false, this);
const FVector From = GetActorLocation() + FVector(0.f, 0.f, 3000.f);
const bool bTop = WorldTop->LineTraceSingleByChannel(Top, From,
From - FVector(0.f, 0.f, 6000.f), ECC_WorldStatic, QTop);
UE_LOG(LogTemp, Warning,
TEXT("GigLine top surface under player: hit=%d Z=%.0f actor=%s (actor Z %.0f)"),
bTop ? 1 : 0, bTop ? Top.ImpactPoint.Z : -9999.f,
bTop && Top.GetActor() ? *Top.GetActor()->GetName() : TEXT("none"),
GetActorLocation().Z);
}
const FVector Feet = GetActorLocation() - FVector(0.f, 0.f, CapsuleHalf);
for (int32 Index = 0; Index < 3; ++Index)
{